chat-recall 0.6.8 → 0.6.9

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/watch.js CHANGED
@@ -5031,577 +5031,262 @@ var init_settings = __esm({
5031
5031
  }
5032
5032
  });
5033
5033
 
5034
- // ../engine/dist/core/home-approval.js
5035
- import { existsSync as existsSync3, readdirSync as readdirSync2, realpathSync } from "fs";
5036
- import { homedir as homedir2 } from "os";
5037
- import { join as join3, dirname as dirname3 } from "path";
5038
- function normalizeHomePath(p) {
5039
- const expanded = p.startsWith("~") ? join3(homedir2(), p.slice(1)) : p;
5040
- const trimmed = expanded.replace(/[/\\]+$/, "");
5041
- try {
5042
- return realpathSync(trimmed);
5043
- } catch {
5044
- return trimmed;
5045
- }
5034
+ // ../engine/dist/core/secret-redactor.js
5035
+ import { statSync as statSync3 } from "fs";
5036
+ function secretOf(rule, m) {
5037
+ const raw = rule.secret != null ? m[rule.secret] : m[0];
5038
+ if (raw == null)
5039
+ return null;
5040
+ if (rule.where && !rule.where(raw))
5041
+ return null;
5042
+ return raw;
5046
5043
  }
5047
- function legacyAutoSyncedHomes() {
5048
- const home = homedir2();
5049
- const out = [];
5050
- const add = (dir, marker) => {
5051
- if (!existsSync3(join3(dir, marker)))
5052
- return;
5053
- const n = normalizeHomePath(dir);
5054
- if (!out.includes(n))
5055
- out.push(n);
5056
- };
5057
- const bases = [
5058
- { base: join3(home, ".claude"), marker: "projects" },
5059
- { base: join3(home, ".gemini"), marker: "tmp" },
5060
- { base: join3(home, ".codex"), marker: "sessions" },
5061
- { base: join3(home, ".gemini", "antigravity-cli"), marker: "brain" },
5062
- { base: join3(home, ".local", "share", "opencode"), marker: "opencode.db" },
5063
- { base: join3(home, ".cursor"), marker: "chats" }
5064
- ];
5065
- for (const { base: base2, marker } of bases) {
5066
- add(base2, marker);
5067
- const parent = dirname3(base2);
5068
- const leaf = base2.slice(parent.length + 1);
5069
- try {
5070
- for (const e of readdirSync2(parent, { withFileTypes: true })) {
5071
- if (!e.isDirectory())
5072
- continue;
5073
- if (e.name === leaf || !e.name.startsWith(leaf + "-"))
5074
- continue;
5075
- if (e.name === ".claude-code")
5076
- continue;
5077
- add(join3(parent, e.name), marker);
5044
+ function looksLikeSecretToken(t) {
5045
+ if (t.length < 32)
5046
+ return false;
5047
+ if (/^[a-f0-9]+$/i.test(t))
5048
+ return true;
5049
+ return /[A-Z]/.test(t) && /[a-z]/.test(t) && /[0-9]/.test(t);
5050
+ }
5051
+ function findContextualSecrets(text) {
5052
+ if (!text)
5053
+ return [];
5054
+ const ranges = [];
5055
+ let m;
5056
+ SECRET_CONTEXT_RE.lastIndex = 0;
5057
+ while ((m = SECRET_CONTEXT_RE.exec(text)) !== null) {
5058
+ const winStart = m.index + m[0].length;
5059
+ const window = text.slice(winStart, winStart + 80);
5060
+ HIGH_ENTROPY_TOKEN_RE.lastIndex = 0;
5061
+ let tm;
5062
+ while ((tm = HIGH_ENTROPY_TOKEN_RE.exec(window)) !== null) {
5063
+ if (!looksLikeSecretToken(tm[0]))
5064
+ continue;
5065
+ if (/^[a-f0-9]+$/i.test(tm[0]) && /\b(?:commit|sha-?\d*|hash|digest|checksum|uuid|guid|etag|blob|ref)\b/i.test(window.slice(0, tm.index))) {
5066
+ continue;
5078
5067
  }
5079
- } catch {
5068
+ ranges.push({ start: winStart + tm.index, end: winStart + tm.index + tm[0].length });
5069
+ break;
5080
5070
  }
5081
5071
  }
5082
- return out;
5083
- }
5084
- function grandfatherLegacyHomes() {
5085
- let s;
5086
- try {
5087
- s = loadSettings();
5088
- } catch {
5089
- return { seeded: [] };
5090
- }
5091
- if (s.sources.homesGrandfathered)
5092
- return { seeded: [] };
5093
- const legacy = legacyAutoSyncedHomes();
5094
- const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
5095
- const seeded = [];
5096
- for (const p of legacy) {
5097
- if (!approved.has(p)) {
5098
- approved.add(p);
5099
- seeded.push(p);
5072
+ ranges.sort((a, b) => a.start - b.start);
5073
+ const out = [];
5074
+ for (const r of ranges) {
5075
+ const last = out[out.length - 1];
5076
+ if (last && r.start < last.end) {
5077
+ last.end = Math.max(last.end, r.end);
5078
+ continue;
5100
5079
  }
5080
+ out.push({ ...r });
5101
5081
  }
5102
- s.sources.approvedHomes = [...approved];
5103
- s.sources.homesGrandfathered = true;
5104
- try {
5105
- saveSettings(s);
5106
- } catch {
5107
- return { seeded: [] };
5108
- }
5109
- return { seeded };
5082
+ return out;
5110
5083
  }
5111
- function primaryHomes() {
5112
- const home = homedir2();
5113
- const env = process.env;
5114
- let s;
5084
+ function isEnvRedactionRequested() {
5085
+ const v = process.env.CHAT_RECALL_REDACT_INDEX;
5086
+ if (v === void 0)
5087
+ return null;
5088
+ return v === "1" || v === "true" || v === "yes";
5089
+ }
5090
+ function settingsView() {
5091
+ let mtimeMs = -1;
5115
5092
  try {
5116
- s = loadSettings().sources;
5093
+ mtimeMs = statSync3(settingsFilePath()).mtimeMs;
5117
5094
  } catch {
5118
- s = void 0;
5119
5095
  }
5120
- const opencodeDb = env.CHAT_RECALL_OPENCODE_DB || s?.opencodeDbPath || join3(home, ".local", "share", "opencode", "opencode.db");
5121
- const candidates2 = [
5122
- env.CHAT_RECALL_CLAUDE_HOME || s?.claudeHome || join3(home, ".claude"),
5123
- env.CHAT_RECALL_GEMINI_HOME || s?.geminiHome || join3(home, ".gemini"),
5124
- env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
5125
- env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
5126
- env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
5127
- dirOf(opencodeDb)
5128
- ];
5129
- return candidates2.map(normalizeHomePath);
5130
- }
5131
- function homeDecision(path) {
5132
- const p = normalizeHomePath(path);
5133
- if (primaryHomes().includes(p))
5134
- return "primary";
5135
- let s;
5096
+ if (cache && cache.mtimeMs === mtimeMs)
5097
+ return cache;
5098
+ let enabled = false;
5099
+ const extra = [];
5136
5100
  try {
5137
- s = loadSettings().sources;
5101
+ const s = loadSettings();
5102
+ enabled = !!s.privacy?.redactIndex;
5103
+ for (const r of s.privacy?.redactionRules ?? []) {
5104
+ try {
5105
+ extra.push({ label: r.label, pattern: new RegExp(r.pattern, "g") });
5106
+ } catch {
5107
+ }
5108
+ }
5138
5109
  } catch {
5139
- return "pending";
5140
5110
  }
5141
- if ((s.declinedHomes ?? []).map(normalizeHomePath).includes(p))
5142
- return "declined";
5143
- if ((s.approvedHomes ?? []).map(normalizeHomePath).includes(p))
5144
- return "approved";
5145
- if ((s.extraClaudeHomes ?? []).map(normalizeHomePath).includes(p))
5146
- return "approved";
5147
- return "pending";
5148
- }
5149
- function isHomeSynced(path) {
5150
- const d = homeDecision(path);
5151
- return d === "primary" || d === "approved";
5111
+ cache = { mtimeMs, enabled, rules: [...DEFAULT_REDACTION_RULES, ...extra] };
5112
+ return cache;
5152
5113
  }
5153
- function setDecision(path, decision) {
5154
- const p = normalizeHomePath(path);
5155
- let s;
5114
+ function compileServerRule(spec) {
5115
+ const name = spec.name || "(unnamed)";
5116
+ if (!spec.regex)
5117
+ return { name, reason: "empty pattern" };
5118
+ if (spec.regex.length > MAX_PATTERN_LEN)
5119
+ return { name, reason: `pattern longer than ${MAX_PATTERN_LEN} chars` };
5120
+ let re;
5156
5121
  try {
5157
- s = loadSettings();
5158
- } catch {
5159
- return false;
5122
+ const flags = "g" + (spec.flags || "").replace(/[^ims]/g, "");
5123
+ re = new RegExp(spec.regex, flags);
5124
+ } catch (e) {
5125
+ return { name, reason: `invalid regex: ${e instanceof Error ? e.message : e}` };
5160
5126
  }
5161
- const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
5162
- const declined = new Set((s.sources.declinedHomes ?? []).map(normalizeHomePath));
5163
- approved.delete(p);
5164
- declined.delete(p);
5165
- (decision === "approved" ? approved : declined).add(p);
5166
- s.sources.approvedHomes = [...approved];
5167
- s.sources.declinedHomes = [...declined];
5168
- try {
5169
- saveSettings(s);
5170
- } catch {
5171
- return false;
5127
+ if (re.test(""))
5128
+ return { name, reason: "matches the empty string (would redact everything)" };
5129
+ re.lastIndex = 0;
5130
+ if (re.test(CANARY))
5131
+ return { name, reason: "matches benign text \u2014 too broad" };
5132
+ re.lastIndex = 0;
5133
+ return { label: `${spec.source === "pack" ? "pack" : "tenant"}:${name}`, pattern: re };
5134
+ }
5135
+ function installServerRulePack(pack) {
5136
+ const rejected = [];
5137
+ const rules = [];
5138
+ for (const spec of pack.rules || []) {
5139
+ if (rules.length >= MAX_PACK_RULES) {
5140
+ rejected.push({ name: spec.name || "(unnamed)", reason: `pack exceeds ${MAX_PACK_RULES} rules` });
5141
+ continue;
5142
+ }
5143
+ const out = compileServerRule(spec);
5144
+ if ("reason" in out)
5145
+ rejected.push(out);
5146
+ else
5147
+ rules.push(out);
5172
5148
  }
5173
- return true;
5149
+ const version = pack.version || "";
5150
+ serverPack = { version, rules };
5151
+ return { version, accepted: rules.length, rejected };
5174
5152
  }
5175
- function approveHome(path) {
5176
- return setDecision(path, "approved");
5153
+ function serverRulePackVersion() {
5154
+ return serverPack ? serverPack.version || "unversioned" : null;
5177
5155
  }
5178
- function declineHome(path) {
5179
- return setDecision(path, "declined");
5156
+ function activeRules() {
5157
+ const base2 = settingsView().rules;
5158
+ return serverPack && serverPack.rules.length > 0 ? [...base2, ...serverPack.rules] : base2;
5180
5159
  }
5181
- var dirOf;
5182
- var init_home_approval = __esm({
5183
- "../engine/dist/core/home-approval.js"() {
5184
- "use strict";
5185
- init_settings();
5186
- dirOf = (filePath) => dirname3(filePath);
5187
- }
5188
- });
5189
-
5190
- // ../engine/dist/core/tool-paths.js
5191
- import { existsSync as existsSync4, readdirSync as readdirSync3 } from "fs";
5192
- import { homedir as homedir3 } from "os";
5193
- import { join as join4, dirname as dirname4, basename } from "path";
5194
- function claudeHomeDir() {
5195
- return process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome || join4(homedir3(), ".claude");
5160
+ function isRedactionEnabled() {
5161
+ const env = isEnvRedactionRequested();
5162
+ if (env !== null)
5163
+ return env;
5164
+ return settingsView().enabled;
5196
5165
  }
5197
- function claudeProjectDirs(opts = {}) {
5198
- const home = homedir3();
5199
- const dirs = [];
5200
- const addHome = (d) => {
5201
- const p = join4(d, "projects");
5202
- if (existsSync4(p) && !dirs.includes(p))
5203
- dirs.push(p);
5204
- };
5205
- addHome(claudeHomeDir());
5206
- const homeOverridden = !!(process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome);
5207
- if (!homeOverridden) {
5208
- try {
5209
- for (const entry of readdirSync3(home, { withFileTypes: true })) {
5210
- if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
5211
- addHome(join4(home, entry.name));
5212
- }
5213
- }
5214
- } catch {
5215
- }
5166
+ function redactSecrets(text, opts = {}) {
5167
+ if (!opts.force && !isRedactionEnabled())
5168
+ return text;
5169
+ if (!text)
5170
+ return text;
5171
+ const rules = opts.rules || activeRules();
5172
+ let out = text;
5173
+ for (const r of rules) {
5174
+ out = out.replace(r.pattern, (...args) => {
5175
+ const whole = args[0];
5176
+ const m = args.slice(0, args.length);
5177
+ const secret = secretOf(r, m);
5178
+ if (secret == null)
5179
+ return whole;
5180
+ if (opts.count)
5181
+ opts.count.redactions++;
5182
+ return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
5183
+ });
5216
5184
  }
5217
- if (process.env.CLAUDE_DIRS) {
5218
- for (const d of process.env.CLAUDE_DIRS.split(",")) {
5219
- const t = d.trim();
5220
- if (t)
5221
- addHome(t.startsWith("~/") ? join4(home, t.slice(2)) : t);
5222
- }
5185
+ const ctx = findContextualSecrets(out);
5186
+ for (let i = ctx.length - 1; i >= 0; i--) {
5187
+ const { start, end } = ctx[i];
5188
+ if (opts.count)
5189
+ opts.count.redactions++;
5190
+ out = out.slice(0, start) + "[REDACTED:secret-context]" + out.slice(end);
5223
5191
  }
5224
- if (dirs.length === 0)
5225
- dirs.push(join4(claudeHomeDir(), "projects"));
5226
- return applyDecisions(dirs, opts.includeExcluded);
5227
- }
5228
- function _setSourceExclusionFilter(fn) {
5229
- _sourceExclusionFilter = fn;
5230
- }
5231
- function geminiHomeDir() {
5232
- return process.env.CHAT_RECALL_GEMINI_HOME || sources().geminiHome || join4(homedir3(), ".gemini");
5233
- }
5234
- function opencodeDbPath() {
5235
- return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
5236
- }
5237
- function codexHomeDir() {
5238
- return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
5239
- }
5240
- function agyHomeDir() {
5241
- return process.env.CHAT_RECALL_AGY_HOME || sources().agyHome || join4(homedir3(), ".gemini", "antigravity-cli");
5192
+ return out;
5242
5193
  }
5243
- function cursorHomeDir() {
5244
- return process.env.CHAT_RECALL_CURSOR_HOME || sources().cursorHome || join4(homedir3(), ".cursor");
5194
+ function maskSecret(s) {
5195
+ if (!s)
5196
+ return "";
5197
+ if (s.length <= 4)
5198
+ return "*".repeat(s.length);
5199
+ return "*".repeat(Math.min(s.length - 4, 60)) + s.slice(-4);
5245
5200
  }
5246
- function cursorIdeHomeDir() {
5247
- return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join4(homedir3(), "Library", "Application Support", "Cursor") : join4(homedir3(), ".config", "Cursor"));
5201
+ function globalRegexFor(pattern) {
5202
+ const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
5203
+ const key2 = pattern.source + "\0" + flags;
5204
+ let re = globalRegexCache.get(key2);
5205
+ if (!re) {
5206
+ re = new RegExp(pattern.source, flags);
5207
+ globalRegexCache.set(key2, re);
5208
+ }
5209
+ return re;
5248
5210
  }
5249
- function siblingHomes(base2, overridden, suffix) {
5250
- const roots = [];
5251
- const add = (home) => {
5252
- const root = suffix ? join4(home, suffix) : home;
5253
- if (existsSync4(root) && !roots.includes(root))
5254
- roots.push(root);
5211
+ function makeLineResolver(text) {
5212
+ const starts = [];
5213
+ for (let i = text.indexOf("\n"); i !== -1; i = text.indexOf("\n", i + 1))
5214
+ starts.push(i);
5215
+ return (index) => {
5216
+ let lo = 0, hi = starts.length;
5217
+ while (lo < hi) {
5218
+ const mid = lo + hi >> 1;
5219
+ if (starts[mid] < index)
5220
+ lo = mid + 1;
5221
+ else
5222
+ hi = mid;
5223
+ }
5224
+ return lo + 1;
5255
5225
  };
5256
- add(base2);
5257
- if (!overridden) {
5258
- const parent = dirname4(base2);
5259
- const leaf = basename(base2);
5260
- try {
5261
- for (const entry of readdirSync3(parent, { withFileTypes: true })) {
5262
- if (!entry.isDirectory())
5263
- continue;
5264
- if (entry.name === leaf || !entry.name.startsWith(leaf + "-"))
5265
- continue;
5266
- add(join4(parent, entry.name));
5226
+ }
5227
+ function scanTextForFindings(text, opts = {}) {
5228
+ if (!text)
5229
+ return [];
5230
+ const rules = opts.rules || activeRules();
5231
+ const findings = [];
5232
+ const seenSpans = /* @__PURE__ */ new Set();
5233
+ const lineAt = makeLineResolver(text);
5234
+ for (const r of rules) {
5235
+ const re = globalRegexFor(r.pattern);
5236
+ re.lastIndex = 0;
5237
+ let m;
5238
+ while ((m = re.exec(text)) !== null) {
5239
+ const secret = secretOf(r, m);
5240
+ if (secret == null) {
5241
+ if (re.lastIndex === m.index)
5242
+ re.lastIndex++;
5243
+ continue;
5267
5244
  }
5268
- } catch {
5245
+ const at = m.index + (m[0].length - secret.length);
5246
+ const span = `${at}:${secret.length}`;
5247
+ if (!seenSpans.has(span)) {
5248
+ seenSpans.add(span);
5249
+ findings.push({ rule: r.label, line: lineAt(at), preview: maskSecret(secret) });
5250
+ }
5251
+ if (re.lastIndex === m.index)
5252
+ re.lastIndex++;
5269
5253
  }
5270
5254
  }
5271
- return roots;
5255
+ for (const { start, end } of findContextualSecrets(text)) {
5256
+ const line = lineAt(start);
5257
+ findings.push({ rule: "secret-context", line, preview: maskSecret(text.slice(start, end)) });
5258
+ }
5259
+ return findings;
5272
5260
  }
5273
- function applyDecisions(roots, includeExcluded) {
5274
- if (roots.length === 0)
5275
- return roots;
5276
- let kept = roots;
5277
- if (!includeExcluded) {
5278
- try {
5279
- grandfatherLegacyHomes();
5280
- const approved = kept.filter((r) => isHomeSynced(dirname4(r)));
5281
- if (approved.length > 0)
5282
- kept = approved;
5283
- } catch {
5284
- }
5261
+ function privacyView() {
5262
+ let mtimeMs = -1;
5263
+ try {
5264
+ mtimeMs = statSync3(settingsFilePath()).mtimeMs;
5265
+ } catch {
5285
5266
  }
5286
- if (!includeExcluded && _sourceExclusionFilter) {
5287
- const notExcluded = kept.filter((r) => !_sourceExclusionFilter(r));
5288
- if (notExcluded.length > 0)
5289
- kept = notExcluded;
5267
+ if (privacyCache && privacyCache.mtimeMs === mtimeMs)
5268
+ return privacyCache;
5269
+ let redactToolOutputs = false, redactFilePaths = false;
5270
+ try {
5271
+ const s = loadSettings();
5272
+ redactToolOutputs = !!s.privacy?.redactToolOutputs;
5273
+ redactFilePaths = !!s.privacy?.redactFilePaths;
5274
+ } catch {
5290
5275
  }
5291
- return kept;
5292
- }
5293
- function applyExclusions(roots, includeExcluded) {
5294
- return applyDecisions(roots, includeExcluded);
5295
- }
5296
- function codexSessionDirs(opts = {}) {
5297
- const overridden = !!(process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome);
5298
- return applyExclusions(siblingHomes(codexHomeDir(), overridden, "sessions"), opts.includeExcluded);
5299
- }
5300
- function agyBrainDirs(opts = {}) {
5301
- const overridden = !!(process.env.CHAT_RECALL_AGY_HOME || sources().agyHome);
5302
- return applyExclusions(siblingHomes(agyHomeDir(), overridden, "brain"), opts.includeExcluded);
5303
- }
5304
- function cursorChatDirs(opts = {}) {
5305
- const overridden = !!(process.env.CHAT_RECALL_CURSOR_HOME || sources().cursorHome);
5306
- return applyExclusions(siblingHomes(cursorHomeDir(), overridden, "chats"), opts.includeExcluded);
5307
- }
5308
- function cursorIdeWorkspaceDirs(opts = {}) {
5309
- const overridden = !!(process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome);
5310
- return applyExclusions(siblingHomes(cursorIdeHomeDir(), overridden, join4("User", "workspaceStorage")), opts.includeExcluded);
5276
+ privacyCache = { mtimeMs, redactToolOutputs, redactFilePaths };
5277
+ return privacyCache;
5311
5278
  }
5312
- function cursorIdeGlobalDbs(opts = {}) {
5313
- const overridden = !!(process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome);
5314
- const roots = applyExclusions(siblingHomes(cursorIdeHomeDir(), overridden, join4("User", "globalStorage")), opts.includeExcluded);
5315
- const dbs = [];
5316
- for (const r of roots) {
5317
- const p = join4(r, "state.vscdb");
5318
- if (existsSync4(p) && !dbs.includes(p))
5319
- dbs.push(p);
5320
- }
5321
- return dbs;
5279
+ function isToolOutputRedactionEnabled() {
5280
+ return privacyView().redactToolOutputs;
5322
5281
  }
5323
- function opencodeDbPaths(opts = {}) {
5324
- const primary = opencodeDbPath();
5325
- const overridden = !!(process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath);
5326
- const dbFile = basename(primary);
5327
- const dirs = siblingHomes(dirname4(primary), overridden, "");
5328
- const dbs = [];
5329
- for (const d of dirs) {
5330
- const p = join4(d, dbFile);
5331
- if (existsSync4(p) && !dbs.includes(p))
5332
- dbs.push(p);
5333
- }
5334
- if (dbs.length === 0)
5335
- dbs.push(primary);
5336
- return applyExclusions(dbs, opts.includeExcluded);
5282
+ function redactToolResultBody(toolName, body) {
5283
+ if (!body)
5284
+ return body;
5285
+ return `[redacted: ${toolName} output, ${body.length} chars]`;
5337
5286
  }
5338
- var sources, _sourceExclusionFilter;
5339
- var init_tool_paths = __esm({
5340
- "../engine/dist/core/tool-paths.js"() {
5341
- "use strict";
5342
- init_home_approval();
5343
- init_settings();
5344
- sources = loadSourceSettings;
5345
- _sourceExclusionFilter = null;
5346
- }
5347
- });
5348
-
5349
- // ../engine/dist/core/secret-redactor.js
5350
- import { statSync as statSync3 } from "fs";
5351
- function secretOf(rule, m) {
5352
- const raw = rule.secret != null ? m[rule.secret] : m[0];
5353
- if (raw == null)
5354
- return null;
5355
- if (rule.where && !rule.where(raw))
5356
- return null;
5357
- return raw;
5358
- }
5359
- function looksLikeSecretToken(t) {
5360
- if (t.length < 32)
5361
- return false;
5362
- if (/^[a-f0-9]+$/i.test(t))
5363
- return true;
5364
- return /[A-Z]/.test(t) && /[a-z]/.test(t) && /[0-9]/.test(t);
5365
- }
5366
- function findContextualSecrets(text) {
5367
- if (!text)
5368
- return [];
5369
- const ranges = [];
5370
- let m;
5371
- SECRET_CONTEXT_RE.lastIndex = 0;
5372
- while ((m = SECRET_CONTEXT_RE.exec(text)) !== null) {
5373
- const winStart = m.index + m[0].length;
5374
- const window = text.slice(winStart, winStart + 80);
5375
- HIGH_ENTROPY_TOKEN_RE.lastIndex = 0;
5376
- let tm;
5377
- while ((tm = HIGH_ENTROPY_TOKEN_RE.exec(window)) !== null) {
5378
- if (!looksLikeSecretToken(tm[0]))
5379
- continue;
5380
- if (/^[a-f0-9]+$/i.test(tm[0]) && /\b(?:commit|sha-?\d*|hash|digest|checksum|uuid|guid|etag|blob|ref)\b/i.test(window.slice(0, tm.index))) {
5381
- continue;
5382
- }
5383
- ranges.push({ start: winStart + tm.index, end: winStart + tm.index + tm[0].length });
5384
- break;
5385
- }
5386
- }
5387
- ranges.sort((a, b) => a.start - b.start);
5388
- const out = [];
5389
- for (const r of ranges) {
5390
- const last = out[out.length - 1];
5391
- if (last && r.start < last.end) {
5392
- last.end = Math.max(last.end, r.end);
5393
- continue;
5394
- }
5395
- out.push({ ...r });
5396
- }
5397
- return out;
5398
- }
5399
- function isEnvRedactionRequested() {
5400
- const v = process.env.CHAT_RECALL_REDACT_INDEX;
5401
- if (v === void 0)
5402
- return null;
5403
- return v === "1" || v === "true" || v === "yes";
5404
- }
5405
- function settingsView() {
5406
- let mtimeMs = -1;
5407
- try {
5408
- mtimeMs = statSync3(settingsFilePath()).mtimeMs;
5409
- } catch {
5410
- }
5411
- if (cache && cache.mtimeMs === mtimeMs)
5412
- return cache;
5413
- let enabled = false;
5414
- const extra = [];
5415
- try {
5416
- const s = loadSettings();
5417
- enabled = !!s.privacy?.redactIndex;
5418
- for (const r of s.privacy?.redactionRules ?? []) {
5419
- try {
5420
- extra.push({ label: r.label, pattern: new RegExp(r.pattern, "g") });
5421
- } catch {
5422
- }
5423
- }
5424
- } catch {
5425
- }
5426
- cache = { mtimeMs, enabled, rules: [...DEFAULT_REDACTION_RULES, ...extra] };
5427
- return cache;
5428
- }
5429
- function compileServerRule(spec) {
5430
- const name = spec.name || "(unnamed)";
5431
- if (!spec.regex)
5432
- return { name, reason: "empty pattern" };
5433
- if (spec.regex.length > MAX_PATTERN_LEN)
5434
- return { name, reason: `pattern longer than ${MAX_PATTERN_LEN} chars` };
5435
- let re;
5436
- try {
5437
- const flags = "g" + (spec.flags || "").replace(/[^ims]/g, "");
5438
- re = new RegExp(spec.regex, flags);
5439
- } catch (e) {
5440
- return { name, reason: `invalid regex: ${e instanceof Error ? e.message : e}` };
5441
- }
5442
- if (re.test(""))
5443
- return { name, reason: "matches the empty string (would redact everything)" };
5444
- re.lastIndex = 0;
5445
- if (re.test(CANARY))
5446
- return { name, reason: "matches benign text \u2014 too broad" };
5447
- re.lastIndex = 0;
5448
- return { label: `${spec.source === "pack" ? "pack" : "tenant"}:${name}`, pattern: re };
5449
- }
5450
- function installServerRulePack(pack) {
5451
- const rejected = [];
5452
- const rules = [];
5453
- for (const spec of pack.rules || []) {
5454
- if (rules.length >= MAX_PACK_RULES) {
5455
- rejected.push({ name: spec.name || "(unnamed)", reason: `pack exceeds ${MAX_PACK_RULES} rules` });
5456
- continue;
5457
- }
5458
- const out = compileServerRule(spec);
5459
- if ("reason" in out)
5460
- rejected.push(out);
5461
- else
5462
- rules.push(out);
5463
- }
5464
- const version = pack.version || "";
5465
- serverPack = { version, rules };
5466
- return { version, accepted: rules.length, rejected };
5467
- }
5468
- function serverRulePackVersion() {
5469
- return serverPack ? serverPack.version || "unversioned" : null;
5470
- }
5471
- function activeRules() {
5472
- const base2 = settingsView().rules;
5473
- return serverPack && serverPack.rules.length > 0 ? [...base2, ...serverPack.rules] : base2;
5474
- }
5475
- function isRedactionEnabled() {
5476
- const env = isEnvRedactionRequested();
5477
- if (env !== null)
5478
- return env;
5479
- return settingsView().enabled;
5480
- }
5481
- function redactSecrets(text, opts = {}) {
5482
- if (!opts.force && !isRedactionEnabled())
5483
- return text;
5484
- if (!text)
5485
- return text;
5486
- const rules = opts.rules || activeRules();
5487
- let out = text;
5488
- for (const r of rules) {
5489
- out = out.replace(r.pattern, (...args) => {
5490
- const whole = args[0];
5491
- const m = args.slice(0, args.length);
5492
- const secret = secretOf(r, m);
5493
- if (secret == null)
5494
- return whole;
5495
- if (opts.count)
5496
- opts.count.redactions++;
5497
- return whole.slice(0, whole.length - secret.length) + `[REDACTED:${r.label}]`;
5498
- });
5499
- }
5500
- const ctx = findContextualSecrets(out);
5501
- for (let i = ctx.length - 1; i >= 0; i--) {
5502
- const { start, end } = ctx[i];
5503
- if (opts.count)
5504
- opts.count.redactions++;
5505
- out = out.slice(0, start) + "[REDACTED:secret-context]" + out.slice(end);
5506
- }
5507
- return out;
5508
- }
5509
- function maskSecret(s) {
5510
- if (!s)
5511
- return "";
5512
- if (s.length <= 4)
5513
- return "*".repeat(s.length);
5514
- return "*".repeat(Math.min(s.length - 4, 60)) + s.slice(-4);
5515
- }
5516
- function globalRegexFor(pattern) {
5517
- const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
5518
- const key2 = pattern.source + "\0" + flags;
5519
- let re = globalRegexCache.get(key2);
5520
- if (!re) {
5521
- re = new RegExp(pattern.source, flags);
5522
- globalRegexCache.set(key2, re);
5523
- }
5524
- return re;
5525
- }
5526
- function makeLineResolver(text) {
5527
- const starts = [];
5528
- for (let i = text.indexOf("\n"); i !== -1; i = text.indexOf("\n", i + 1))
5529
- starts.push(i);
5530
- return (index) => {
5531
- let lo = 0, hi = starts.length;
5532
- while (lo < hi) {
5533
- const mid = lo + hi >> 1;
5534
- if (starts[mid] < index)
5535
- lo = mid + 1;
5536
- else
5537
- hi = mid;
5538
- }
5539
- return lo + 1;
5540
- };
5541
- }
5542
- function scanTextForFindings(text, opts = {}) {
5543
- if (!text)
5544
- return [];
5545
- const rules = opts.rules || activeRules();
5546
- const findings = [];
5547
- const seenSpans = /* @__PURE__ */ new Set();
5548
- const lineAt = makeLineResolver(text);
5549
- for (const r of rules) {
5550
- const re = globalRegexFor(r.pattern);
5551
- re.lastIndex = 0;
5552
- let m;
5553
- while ((m = re.exec(text)) !== null) {
5554
- const secret = secretOf(r, m);
5555
- if (secret == null) {
5556
- if (re.lastIndex === m.index)
5557
- re.lastIndex++;
5558
- continue;
5559
- }
5560
- const at = m.index + (m[0].length - secret.length);
5561
- const span = `${at}:${secret.length}`;
5562
- if (!seenSpans.has(span)) {
5563
- seenSpans.add(span);
5564
- findings.push({ rule: r.label, line: lineAt(at), preview: maskSecret(secret) });
5565
- }
5566
- if (re.lastIndex === m.index)
5567
- re.lastIndex++;
5568
- }
5569
- }
5570
- for (const { start, end } of findContextualSecrets(text)) {
5571
- const line = lineAt(start);
5572
- findings.push({ rule: "secret-context", line, preview: maskSecret(text.slice(start, end)) });
5573
- }
5574
- return findings;
5575
- }
5576
- function privacyView() {
5577
- let mtimeMs = -1;
5578
- try {
5579
- mtimeMs = statSync3(settingsFilePath()).mtimeMs;
5580
- } catch {
5581
- }
5582
- if (privacyCache && privacyCache.mtimeMs === mtimeMs)
5583
- return privacyCache;
5584
- let redactToolOutputs = false, redactFilePaths = false;
5585
- try {
5586
- const s = loadSettings();
5587
- redactToolOutputs = !!s.privacy?.redactToolOutputs;
5588
- redactFilePaths = !!s.privacy?.redactFilePaths;
5589
- } catch {
5590
- }
5591
- privacyCache = { mtimeMs, redactToolOutputs, redactFilePaths };
5592
- return privacyCache;
5593
- }
5594
- function isToolOutputRedactionEnabled() {
5595
- return privacyView().redactToolOutputs;
5596
- }
5597
- function redactToolResultBody(toolName, body) {
5598
- if (!body)
5599
- return body;
5600
- return `[redacted: ${toolName} output, ${body.length} chars]`;
5601
- }
5602
- var DEFAULT_REDACTION_RULES, SECRET_CONTEXT_RE, HIGH_ENTROPY_TOKEN_RE, cache, CANARY, MAX_PACK_RULES, MAX_PATTERN_LEN, serverPack, globalRegexCache, privacyCache;
5603
- var init_secret_redactor = __esm({
5604
- "../engine/dist/core/secret-redactor.js"() {
5287
+ var DEFAULT_REDACTION_RULES, SECRET_CONTEXT_RE, HIGH_ENTROPY_TOKEN_RE, cache, CANARY, MAX_PACK_RULES, MAX_PATTERN_LEN, serverPack, globalRegexCache, privacyCache;
5288
+ var init_secret_redactor = __esm({
5289
+ "../engine/dist/core/secret-redactor.js"() {
5605
5290
  "use strict";
5606
5291
  init_settings();
5607
5292
  DEFAULT_REDACTION_RULES = [
@@ -5897,18 +5582,333 @@ function chunkSession(entry, content, maxChunks = 80, maxTokensPerChunk = 500) {
5897
5582
  }
5898
5583
  }
5899
5584
  }
5900
- return chunks.slice(0, maxChunks);
5585
+ return chunks.slice(0, maxChunks);
5586
+ }
5587
+ var INJECTED_BANNERS;
5588
+ var init_chunker = __esm({
5589
+ "../engine/dist/parsers/chunker.js"() {
5590
+ "use strict";
5591
+ init_secret_redactor();
5592
+ INJECTED_BANNERS = [
5593
+ /MCP issues detected\. ?Run \/mcp list for status\.?/g,
5594
+ /Context low[^\n]*Run \/compact[^\n]*/g,
5595
+ /API Error:[^\n]{0,120}/g
5596
+ ];
5597
+ }
5598
+ });
5599
+
5600
+ // ../engine/dist/core/home-approval.js
5601
+ import { existsSync as existsSync3, readdirSync as readdirSync2, realpathSync } from "fs";
5602
+ import { homedir as homedir2 } from "os";
5603
+ import { join as join3, dirname as dirname3 } from "path";
5604
+ function normalizeHomePath(p) {
5605
+ const expanded = p.startsWith("~") ? join3(homedir2(), p.slice(1)) : p;
5606
+ const trimmed = expanded.replace(/[/\\]+$/, "");
5607
+ try {
5608
+ return realpathSync(trimmed);
5609
+ } catch {
5610
+ return trimmed;
5611
+ }
5612
+ }
5613
+ function legacyAutoSyncedHomes() {
5614
+ const home = homedir2();
5615
+ const out = [];
5616
+ const add = (dir, marker) => {
5617
+ if (!existsSync3(join3(dir, marker)))
5618
+ return;
5619
+ const n = normalizeHomePath(dir);
5620
+ if (!out.includes(n))
5621
+ out.push(n);
5622
+ };
5623
+ const bases = [
5624
+ { base: join3(home, ".claude"), marker: "projects" },
5625
+ { base: join3(home, ".gemini"), marker: "tmp" },
5626
+ { base: join3(home, ".codex"), marker: "sessions" },
5627
+ { base: join3(home, ".gemini", "antigravity-cli"), marker: "brain" },
5628
+ { base: join3(home, ".local", "share", "opencode"), marker: "opencode.db" },
5629
+ { base: join3(home, ".cursor"), marker: "chats" }
5630
+ ];
5631
+ for (const { base: base2, marker } of bases) {
5632
+ add(base2, marker);
5633
+ const parent = dirname3(base2);
5634
+ const leaf = base2.slice(parent.length + 1);
5635
+ try {
5636
+ for (const e of readdirSync2(parent, { withFileTypes: true })) {
5637
+ if (!e.isDirectory())
5638
+ continue;
5639
+ if (e.name === leaf || !e.name.startsWith(leaf + "-"))
5640
+ continue;
5641
+ if (e.name === ".claude-code")
5642
+ continue;
5643
+ add(join3(parent, e.name), marker);
5644
+ }
5645
+ } catch {
5646
+ }
5647
+ }
5648
+ return out;
5649
+ }
5650
+ function grandfatherLegacyHomes() {
5651
+ let s;
5652
+ try {
5653
+ s = loadSettings();
5654
+ } catch {
5655
+ return { seeded: [] };
5656
+ }
5657
+ if (s.sources.homesGrandfathered)
5658
+ return { seeded: [] };
5659
+ const legacy = legacyAutoSyncedHomes();
5660
+ const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
5661
+ const seeded = [];
5662
+ for (const p of legacy) {
5663
+ if (!approved.has(p)) {
5664
+ approved.add(p);
5665
+ seeded.push(p);
5666
+ }
5667
+ }
5668
+ s.sources.approvedHomes = [...approved];
5669
+ s.sources.homesGrandfathered = true;
5670
+ try {
5671
+ saveSettings(s);
5672
+ } catch {
5673
+ return { seeded: [] };
5674
+ }
5675
+ return { seeded };
5676
+ }
5677
+ function primaryHomes() {
5678
+ const home = homedir2();
5679
+ const env = process.env;
5680
+ let s;
5681
+ try {
5682
+ s = loadSettings().sources;
5683
+ } catch {
5684
+ s = void 0;
5685
+ }
5686
+ const opencodeDb = env.CHAT_RECALL_OPENCODE_DB || s?.opencodeDbPath || join3(home, ".local", "share", "opencode", "opencode.db");
5687
+ const candidates2 = [
5688
+ env.CHAT_RECALL_CLAUDE_HOME || s?.claudeHome || join3(home, ".claude"),
5689
+ env.CHAT_RECALL_GEMINI_HOME || s?.geminiHome || join3(home, ".gemini"),
5690
+ env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
5691
+ env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
5692
+ env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
5693
+ dirOf(opencodeDb)
5694
+ ];
5695
+ return candidates2.map(normalizeHomePath);
5696
+ }
5697
+ function homeDecision(path) {
5698
+ const p = normalizeHomePath(path);
5699
+ if (primaryHomes().includes(p))
5700
+ return "primary";
5701
+ let s;
5702
+ try {
5703
+ s = loadSettings().sources;
5704
+ } catch {
5705
+ return "pending";
5706
+ }
5707
+ if ((s.declinedHomes ?? []).map(normalizeHomePath).includes(p))
5708
+ return "declined";
5709
+ if ((s.approvedHomes ?? []).map(normalizeHomePath).includes(p))
5710
+ return "approved";
5711
+ if ((s.extraClaudeHomes ?? []).map(normalizeHomePath).includes(p))
5712
+ return "approved";
5713
+ return "pending";
5714
+ }
5715
+ function isHomeSynced(path) {
5716
+ const d = homeDecision(path);
5717
+ return d === "primary" || d === "approved";
5718
+ }
5719
+ function setDecision(path, decision) {
5720
+ const p = normalizeHomePath(path);
5721
+ let s;
5722
+ try {
5723
+ s = loadSettings();
5724
+ } catch {
5725
+ return false;
5726
+ }
5727
+ const approved = new Set((s.sources.approvedHomes ?? []).map(normalizeHomePath));
5728
+ const declined = new Set((s.sources.declinedHomes ?? []).map(normalizeHomePath));
5729
+ approved.delete(p);
5730
+ declined.delete(p);
5731
+ (decision === "approved" ? approved : declined).add(p);
5732
+ s.sources.approvedHomes = [...approved];
5733
+ s.sources.declinedHomes = [...declined];
5734
+ try {
5735
+ saveSettings(s);
5736
+ } catch {
5737
+ return false;
5738
+ }
5739
+ return true;
5740
+ }
5741
+ function approveHome(path) {
5742
+ return setDecision(path, "approved");
5743
+ }
5744
+ function declineHome(path) {
5745
+ return setDecision(path, "declined");
5746
+ }
5747
+ var dirOf;
5748
+ var init_home_approval = __esm({
5749
+ "../engine/dist/core/home-approval.js"() {
5750
+ "use strict";
5751
+ init_settings();
5752
+ dirOf = (filePath) => dirname3(filePath);
5753
+ }
5754
+ });
5755
+
5756
+ // ../engine/dist/core/tool-paths.js
5757
+ import { existsSync as existsSync4, readdirSync as readdirSync3 } from "fs";
5758
+ import { homedir as homedir3 } from "os";
5759
+ import { join as join4, dirname as dirname4, basename } from "path";
5760
+ function claudeHomeDir() {
5761
+ return process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome || join4(homedir3(), ".claude");
5762
+ }
5763
+ function claudeProjectDirs(opts = {}) {
5764
+ const home = homedir3();
5765
+ const dirs = [];
5766
+ const addHome = (d) => {
5767
+ const p = join4(d, "projects");
5768
+ if (existsSync4(p) && !dirs.includes(p))
5769
+ dirs.push(p);
5770
+ };
5771
+ addHome(claudeHomeDir());
5772
+ const homeOverridden = !!(process.env.CHAT_RECALL_CLAUDE_HOME || sources().claudeHome);
5773
+ if (!homeOverridden) {
5774
+ try {
5775
+ for (const entry of readdirSync3(home, { withFileTypes: true })) {
5776
+ if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
5777
+ addHome(join4(home, entry.name));
5778
+ }
5779
+ }
5780
+ } catch {
5781
+ }
5782
+ }
5783
+ if (process.env.CLAUDE_DIRS) {
5784
+ for (const d of process.env.CLAUDE_DIRS.split(",")) {
5785
+ const t = d.trim();
5786
+ if (t)
5787
+ addHome(t.startsWith("~/") ? join4(home, t.slice(2)) : t);
5788
+ }
5789
+ }
5790
+ if (dirs.length === 0)
5791
+ dirs.push(join4(claudeHomeDir(), "projects"));
5792
+ return applyDecisions(dirs, opts.includeExcluded);
5793
+ }
5794
+ function _setSourceExclusionFilter(fn) {
5795
+ _sourceExclusionFilter = fn;
5796
+ }
5797
+ function geminiHomeDir() {
5798
+ return process.env.CHAT_RECALL_GEMINI_HOME || sources().geminiHome || join4(homedir3(), ".gemini");
5799
+ }
5800
+ function opencodeDbPath() {
5801
+ return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
5802
+ }
5803
+ function codexHomeDir() {
5804
+ return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
5805
+ }
5806
+ function agyHomeDir() {
5807
+ return process.env.CHAT_RECALL_AGY_HOME || sources().agyHome || join4(homedir3(), ".gemini", "antigravity-cli");
5808
+ }
5809
+ function cursorHomeDir() {
5810
+ return process.env.CHAT_RECALL_CURSOR_HOME || sources().cursorHome || join4(homedir3(), ".cursor");
5811
+ }
5812
+ function cursorIdeHomeDir() {
5813
+ return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join4(homedir3(), "Library", "Application Support", "Cursor") : join4(homedir3(), ".config", "Cursor"));
5814
+ }
5815
+ function siblingHomes(base2, overridden, suffix) {
5816
+ const roots = [];
5817
+ const add = (home) => {
5818
+ const root = suffix ? join4(home, suffix) : home;
5819
+ if (existsSync4(root) && !roots.includes(root))
5820
+ roots.push(root);
5821
+ };
5822
+ add(base2);
5823
+ if (!overridden) {
5824
+ const parent = dirname4(base2);
5825
+ const leaf = basename(base2);
5826
+ try {
5827
+ for (const entry of readdirSync3(parent, { withFileTypes: true })) {
5828
+ if (!entry.isDirectory())
5829
+ continue;
5830
+ if (entry.name === leaf || !entry.name.startsWith(leaf + "-"))
5831
+ continue;
5832
+ add(join4(parent, entry.name));
5833
+ }
5834
+ } catch {
5835
+ }
5836
+ }
5837
+ return roots;
5838
+ }
5839
+ function applyDecisions(roots, includeExcluded) {
5840
+ if (roots.length === 0)
5841
+ return roots;
5842
+ let kept = roots;
5843
+ if (!includeExcluded) {
5844
+ try {
5845
+ grandfatherLegacyHomes();
5846
+ const approved = kept.filter((r) => isHomeSynced(dirname4(r)));
5847
+ if (approved.length > 0)
5848
+ kept = approved;
5849
+ } catch {
5850
+ }
5851
+ }
5852
+ if (!includeExcluded && _sourceExclusionFilter) {
5853
+ const notExcluded = kept.filter((r) => !_sourceExclusionFilter(r));
5854
+ if (notExcluded.length > 0)
5855
+ kept = notExcluded;
5856
+ }
5857
+ return kept;
5901
5858
  }
5902
- var INJECTED_BANNERS;
5903
- var init_chunker = __esm({
5904
- "../engine/dist/parsers/chunker.js"() {
5859
+ function applyExclusions(roots, includeExcluded) {
5860
+ return applyDecisions(roots, includeExcluded);
5861
+ }
5862
+ function codexSessionDirs(opts = {}) {
5863
+ const overridden = !!(process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome);
5864
+ return applyExclusions(siblingHomes(codexHomeDir(), overridden, "sessions"), opts.includeExcluded);
5865
+ }
5866
+ function agyBrainDirs(opts = {}) {
5867
+ const overridden = !!(process.env.CHAT_RECALL_AGY_HOME || sources().agyHome);
5868
+ return applyExclusions(siblingHomes(agyHomeDir(), overridden, "brain"), opts.includeExcluded);
5869
+ }
5870
+ function cursorChatDirs(opts = {}) {
5871
+ const overridden = !!(process.env.CHAT_RECALL_CURSOR_HOME || sources().cursorHome);
5872
+ return applyExclusions(siblingHomes(cursorHomeDir(), overridden, "chats"), opts.includeExcluded);
5873
+ }
5874
+ function cursorIdeWorkspaceDirs(opts = {}) {
5875
+ const overridden = !!(process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome);
5876
+ return applyExclusions(siblingHomes(cursorIdeHomeDir(), overridden, join4("User", "workspaceStorage")), opts.includeExcluded);
5877
+ }
5878
+ function cursorIdeGlobalDbs(opts = {}) {
5879
+ const overridden = !!(process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome);
5880
+ const roots = applyExclusions(siblingHomes(cursorIdeHomeDir(), overridden, join4("User", "globalStorage")), opts.includeExcluded);
5881
+ const dbs = [];
5882
+ for (const r of roots) {
5883
+ const p = join4(r, "state.vscdb");
5884
+ if (existsSync4(p) && !dbs.includes(p))
5885
+ dbs.push(p);
5886
+ }
5887
+ return dbs;
5888
+ }
5889
+ function opencodeDbPaths(opts = {}) {
5890
+ const primary = opencodeDbPath();
5891
+ const overridden = !!(process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath);
5892
+ const dbFile = basename(primary);
5893
+ const dirs = siblingHomes(dirname4(primary), overridden, "");
5894
+ const dbs = [];
5895
+ for (const d of dirs) {
5896
+ const p = join4(d, dbFile);
5897
+ if (existsSync4(p) && !dbs.includes(p))
5898
+ dbs.push(p);
5899
+ }
5900
+ if (dbs.length === 0)
5901
+ dbs.push(primary);
5902
+ return applyExclusions(dbs, opts.includeExcluded);
5903
+ }
5904
+ var sources, _sourceExclusionFilter;
5905
+ var init_tool_paths = __esm({
5906
+ "../engine/dist/core/tool-paths.js"() {
5905
5907
  "use strict";
5906
- init_secret_redactor();
5907
- INJECTED_BANNERS = [
5908
- /MCP issues detected\. ?Run \/mcp list for status\.?/g,
5909
- /Context low[^\n]*Run \/compact[^\n]*/g,
5910
- /API Error:[^\n]{0,120}/g
5911
- ];
5908
+ init_home_approval();
5909
+ init_settings();
5910
+ sources = loadSourceSettings;
5911
+ _sourceExclusionFilter = null;
5912
5912
  }
5913
5913
  });
5914
5914
 
@@ -7622,13 +7622,26 @@ function resolveSessionContentGroups(sessionId) {
7622
7622
  }
7623
7623
  function withSessionReadCache(fn) {
7624
7624
  readScopeDepth++;
7625
+ if (readScopeDepth === 1)
7626
+ derivedScope = /* @__PURE__ */ new Map();
7625
7627
  try {
7626
7628
  return fn();
7627
7629
  } finally {
7628
- if (--readScopeDepth === 0)
7630
+ if (--readScopeDepth === 0) {
7629
7631
  readScope = null;
7632
+ derivedScope = null;
7633
+ }
7630
7634
  }
7631
7635
  }
7636
+ function memoInSessionScope(key2, compute) {
7637
+ if (!derivedScope)
7638
+ return compute();
7639
+ if (derivedScope.has(key2))
7640
+ return derivedScope.get(key2);
7641
+ const value = compute();
7642
+ derivedScope.set(key2, value);
7643
+ return value;
7644
+ }
7632
7645
  function readSessionGroupText(group) {
7633
7646
  let key2 = "";
7634
7647
  if (readScopeDepth > 0) {
@@ -7740,7 +7753,7 @@ function findCodexSessionFileIn(root, sessionId) {
7740
7753
  }
7741
7754
  return scanDir(root);
7742
7755
  }
7743
- var scanScopeDepth, indexCache, readScope, readScopeDepth, geminiCache, MAX_EVENT_LINE_BYTES;
7756
+ var scanScopeDepth, indexCache, readScope, readScopeDepth, derivedScope, geminiCache, MAX_EVENT_LINE_BYTES;
7744
7757
  var init_live_session_scan = __esm({
7745
7758
  "../engine/dist/core/live-session-scan.js"() {
7746
7759
  "use strict";
@@ -7754,6 +7767,7 @@ var init_live_session_scan = __esm({
7754
7767
  indexCache = null;
7755
7768
  readScope = null;
7756
7769
  readScopeDepth = 0;
7770
+ derivedScope = null;
7757
7771
  geminiCache = null;
7758
7772
  MAX_EVENT_LINE_BYTES = 8 * 1024 * 1024;
7759
7773
  }
@@ -8031,6 +8045,28 @@ var init_session_git = __esm({
8031
8045
  }
8032
8046
  });
8033
8047
 
8048
+ // ../engine/dist/core/session-multi-tool.js
8049
+ function extractTurnsAny(sessionId, opts = {}) {
8050
+ const backend = getBackendForId(sessionId);
8051
+ if (!backend)
8052
+ return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
8053
+ return memoInSessionScope(`turns:${sessionId}:${opts.maxTurns ?? ""}:${opts.assistantMax ?? ""}`, () => backend.extractTurns(sessionId, opts));
8054
+ }
8055
+ function replaySessionAny(sessionId) {
8056
+ const backend = getBackendForId(sessionId);
8057
+ if (!backend)
8058
+ return { sessionId, found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
8059
+ return memoInSessionScope(`replay:${sessionId}`, () => backend.replay(sessionId));
8060
+ }
8061
+ var init_session_multi_tool = __esm({
8062
+ "../engine/dist/core/session-multi-tool.js"() {
8063
+ "use strict";
8064
+ init_tool_backend();
8065
+ init_live_session_scan();
8066
+ init_backends();
8067
+ }
8068
+ });
8069
+
8034
8070
  // ../engine/dist/core/session-outcome.js
8035
8071
  function extractDecisionsFromTurn(turn) {
8036
8072
  if (turn.kind !== "assistant_text" || !turn.text)
@@ -8127,7 +8163,7 @@ function computeOutcome(sessionId, opts = {}) {
8127
8163
  };
8128
8164
  if (!backend)
8129
8165
  return notFound;
8130
- const turnsRes = backend.extractTurns(sessionId, { assistantMax: 2e3 });
8166
+ const turnsRes = extractTurnsAny(sessionId, { assistantMax: 2e3 });
8131
8167
  if (!turnsRes.found)
8132
8168
  return notFound;
8133
8169
  const decisions = [];
@@ -8166,7 +8202,7 @@ function computeOutcome(sessionId, opts = {}) {
8166
8202
  totalLinesAdded = fast.totalLinesAdded;
8167
8203
  totalLinesRemoved = fast.totalLinesRemoved;
8168
8204
  } else {
8169
- const replay = backend.replay(sessionId);
8205
+ const replay = replaySessionAny(sessionId);
8170
8206
  filesChanged = replay.files.map((f) => f.file);
8171
8207
  totalLinesAdded = replay.totalLinesAdded;
8172
8208
  totalLinesRemoved = replay.totalLinesRemoved;
@@ -8205,6 +8241,7 @@ var init_session_outcome = __esm({
8205
8241
  init_session_sentiment();
8206
8242
  init_session_git();
8207
8243
  init_tool_backend();
8244
+ init_session_multi_tool();
8208
8245
  init_backends();
8209
8246
  DECISION_PATTERNS = [
8210
8247
  /\b(?:I'?ll|I will|I'?m going to|Going to|Let me|We'?ll|We will|We should|Let'?s|I'?m about to|I'?m going to)\b[^\n.!?]*[.!?]/i,
@@ -8674,6 +8711,7 @@ var UUID_RE, ClaudeBackend, claudeBackend;
8674
8711
  var init_claude2 = __esm({
8675
8712
  "../engine/dist/core/backends/claude.js"() {
8676
8713
  "use strict";
8714
+ init_live_session_scan();
8677
8715
  init_tool_paths();
8678
8716
  init_live_session_scan();
8679
8717
  init_session_outcome();
@@ -8936,6 +8974,9 @@ var init_claude2 = __esm({
8936
8974
  * splits show up alongside the parent.
8937
8975
  */
8938
8976
  readEvents(rawId) {
8977
+ return memoInSessionScope(`events:claude:${rawId}`, () => this.#readEventsUncached(rawId));
8978
+ }
8979
+ #readEventsUncached(rawId) {
8939
8980
  const groups = resolveSessionContentGroups(rawId);
8940
8981
  if (groups.length === 0)
8941
8982
  return [];
@@ -12287,6 +12328,76 @@ var init_backends = __esm({
12287
12328
  }
12288
12329
  });
12289
12330
 
12331
+ // ../engine/dist/core/collector-health.js
12332
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "node:fs";
12333
+ import { join as join17, dirname as dirname13 } from "node:path";
12334
+ function applySyncOutcome(prior, outcome, now = Date.now()) {
12335
+ const t = { lastOkAt: prior?.lastOkAt ?? null, failures: prior?.failures ?? 0 };
12336
+ if (prior?.lastError !== void 0)
12337
+ t.lastError = prior.lastError;
12338
+ if (outcome.ok) {
12339
+ t.lastOkAt = now;
12340
+ t.failures = 0;
12341
+ delete t.lastError;
12342
+ return t;
12343
+ }
12344
+ if ((outcome.accepted ?? 0) > 0)
12345
+ t.lastOkAt = now;
12346
+ t.failures += 1;
12347
+ t.lastError = (outcome.error ?? "unknown error").slice(0, 160);
12348
+ return t;
12349
+ }
12350
+ function collectorHealthPath() {
12351
+ return healthPath();
12352
+ }
12353
+ function readCollectorHealth() {
12354
+ try {
12355
+ const h = JSON.parse(readFileSync11(healthPath(), "utf-8"));
12356
+ return h && h.v === 1 && typeof h.updatedAt === "number" ? h : null;
12357
+ } catch {
12358
+ return null;
12359
+ }
12360
+ }
12361
+ function writeCollectorHealth(h) {
12362
+ try {
12363
+ const p = healthPath();
12364
+ mkdirSync4(dirname13(p), { recursive: true });
12365
+ writeFileSync3(p, JSON.stringify(h));
12366
+ } catch {
12367
+ }
12368
+ }
12369
+ function updateCollectorHealth(patch) {
12370
+ const prior = readCollectorHealth();
12371
+ writeCollectorHealth({
12372
+ ...prior,
12373
+ v: 1,
12374
+ updatedAt: Date.now(),
12375
+ startedAt: prior?.startedAt ?? Date.now(),
12376
+ restartsLastHour: prior?.restartsLastHour ?? 0,
12377
+ targets: prior?.targets ?? {},
12378
+ ...patch
12379
+ });
12380
+ }
12381
+ function reportWalkProgress(p) {
12382
+ const now = Date.now();
12383
+ const boundary = p.complete || p.done === 0;
12384
+ if (!boundary && now - lastProgressWrite < PROGRESS_WRITE_INTERVAL_MS)
12385
+ return;
12386
+ lastProgressWrite = now;
12387
+ updateCollectorHealth({ progress: p });
12388
+ }
12389
+ var healthPath, PROGRESS_WRITE_INTERVAL_MS, lastProgressWrite, STALE_AFTER_MS;
12390
+ var init_collector_health = __esm({
12391
+ "../engine/dist/core/collector-health.js"() {
12392
+ "use strict";
12393
+ init_paths();
12394
+ healthPath = () => join17(getDataDir(), "collector-health.json");
12395
+ PROGRESS_WRITE_INTERVAL_MS = 2e3;
12396
+ lastProgressWrite = 0;
12397
+ STALE_AFTER_MS = 30 * 6e4;
12398
+ }
12399
+ });
12400
+
12290
12401
  // ../engine/dist/core/credentials-env.js
12291
12402
  function envCredentials(env = process.env) {
12292
12403
  const token = (env.CHAT_RECALL_TOKEN || "").trim();
@@ -12308,7 +12419,7 @@ var init_credentials_env = __esm({
12308
12419
  // ../engine/dist/core/source-discovery.js
12309
12420
  import { createHash as createHash3 } from "crypto";
12310
12421
  import { existsSync as existsSync16, readdirSync as readdirSync12, statSync as statSync14 } from "fs";
12311
- import { join as join17 } from "path";
12422
+ import { join as join18 } from "path";
12312
12423
  function sourceId(path) {
12313
12424
  return "src_" + createHash3("sha256").update(path).digest("hex").slice(0, 12);
12314
12425
  }
@@ -12347,7 +12458,7 @@ function countTranscripts(root, tool) {
12347
12458
  return;
12348
12459
  }
12349
12460
  for (const e of entries) {
12350
- const full = join17(dir, e.name);
12461
+ const full = join18(dir, e.name);
12351
12462
  if (e.isDirectory()) {
12352
12463
  walk(full, depth + 1);
12353
12464
  continue;
@@ -12411,9 +12522,9 @@ var init_source_discovery = __esm({
12411
12522
 
12412
12523
  // ../engine/dist/core/home-discovery.js
12413
12524
  import { execFileSync as execFileSync2 } from "child_process";
12414
- import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync11, realpathSync as realpathSync2, statSync as statSync15 } from "fs";
12525
+ import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync12, realpathSync as realpathSync2, statSync as statSync15 } from "fs";
12415
12526
  import { homedir as homedir5, platform as osPlatform } from "os";
12416
- import { join as join18 } from "path";
12527
+ import { join as join19 } from "path";
12417
12528
  function candidateRoots(plat = osPlatform(), env = process.env) {
12418
12529
  const home = homedir5();
12419
12530
  const roots = [home];
@@ -12426,23 +12537,23 @@ function candidateRoots(plat = osPlatform(), env = process.env) {
12426
12537
  add(env.LOCALAPPDATA);
12427
12538
  add(env.USERPROFILE);
12428
12539
  } else if (plat === "darwin") {
12429
- add(join18(home, "Library", "Application Support"));
12540
+ add(join19(home, "Library", "Application Support"));
12430
12541
  add(env.XDG_CONFIG_HOME);
12431
12542
  } else {
12432
- add(env.XDG_CONFIG_HOME || join18(home, ".config"));
12433
- add(env.XDG_DATA_HOME || join18(home, ".local", "share"));
12543
+ add(env.XDG_CONFIG_HOME || join19(home, ".config"));
12544
+ add(env.XDG_DATA_HOME || join19(home, ".local", "share"));
12434
12545
  }
12435
12546
  return roots.filter((r) => existsSync17(r));
12436
12547
  }
12437
12548
  function identifyHome(dir) {
12438
- const has = (...parts) => existsSync17(join18(dir, ...parts));
12439
- if (has("projects") && dirHasTranscript(join18(dir, "projects"), (f) => f.endsWith(".jsonl"), 2))
12549
+ const has = (...parts) => existsSync17(join19(dir, ...parts));
12550
+ if (has("projects") && dirHasTranscript(join19(dir, "projects"), (f) => f.endsWith(".jsonl"), 2))
12440
12551
  return "claude";
12441
- if (has("sessions") && dirHasTranscript(join18(dir, "sessions"), (f) => f.startsWith("rollout-") && f.endsWith(".jsonl"), 4))
12552
+ if (has("sessions") && dirHasTranscript(join19(dir, "sessions"), (f) => f.startsWith("rollout-") && f.endsWith(".jsonl"), 4))
12442
12553
  return "codex";
12443
- if (has("brain") && dirHasTranscript(join18(dir, "brain"), (f) => f.endsWith(".jsonl"), 4))
12554
+ if (has("brain") && dirHasTranscript(join19(dir, "brain"), (f) => f.endsWith(".jsonl"), 4))
12444
12555
  return "agy";
12445
- if (has("chats") && dirHasTranscript(join18(dir, "chats"), (f) => f === "store.db", 3))
12556
+ if (has("chats") && dirHasTranscript(join19(dir, "chats"), (f) => f === "store.db", 3))
12446
12557
  return "cursor";
12447
12558
  if (has("opencode.db"))
12448
12559
  return "opencode";
@@ -12462,7 +12573,7 @@ function dirHasTranscript(root, match, depth) {
12462
12573
  continue;
12463
12574
  if (e.isFile() && match(e.name))
12464
12575
  return true;
12465
- if (e.isDirectory() && dirHasTranscript(join18(root, e.name), match, depth - 1))
12576
+ if (e.isDirectory() && dirHasTranscript(join19(root, e.name), match, depth - 1))
12466
12577
  return true;
12467
12578
  }
12468
12579
  return false;
@@ -12492,12 +12603,12 @@ function countSessions(dir, tool) {
12492
12603
  if (SKIP.has(e.name))
12493
12604
  continue;
12494
12605
  if (e.isDirectory())
12495
- walk(join18(d, e.name), depth - 1);
12606
+ walk(join19(d, e.name), depth - 1);
12496
12607
  else if (s.match(e.name))
12497
12608
  n++;
12498
12609
  }
12499
12610
  };
12500
- walk(join18(dir, s.sub), s.depth);
12611
+ walk(join19(dir, s.sub), s.depth);
12501
12612
  return n;
12502
12613
  }
12503
12614
  function declaredHomes(env = process.env, plat = osPlatform()) {
@@ -12531,11 +12642,11 @@ function declaredHomes(env = process.env, plat = osPlatform()) {
12531
12642
  return out;
12532
12643
  }
12533
12644
  const home = homedir5();
12534
- const rcs = [".bashrc", ".bash_profile", ".zshrc", ".zprofile", ".profile"].map((f) => join18(home, f)).concat([join18(home, ".config", "fish", "config.fish")]);
12645
+ const rcs = [".bashrc", ".bash_profile", ".zshrc", ".zprofile", ".profile"].map((f) => join19(home, f)).concat([join19(home, ".config", "fish", "config.fish")]);
12535
12646
  for (const rc of rcs) {
12536
12647
  let text;
12537
12648
  try {
12538
- text = readFileSync11(rc, "utf-8");
12649
+ text = readFileSync12(rc, "utf-8");
12539
12650
  } catch {
12540
12651
  continue;
12541
12652
  }
@@ -12546,7 +12657,7 @@ function declaredHomes(env = process.env, plat = osPlatform()) {
12546
12657
  add(d);
12547
12658
  }
12548
12659
  }
12549
- return out.map((p) => p.startsWith("~") ? join18(home, p.slice(1)) : p);
12660
+ return out.map((p) => p.startsWith("~") ? join19(home, p.slice(1)) : p);
12550
12661
  }
12551
12662
  function runningToolHomes(plat = osPlatform()) {
12552
12663
  const out = [];
@@ -12560,7 +12671,7 @@ function runningToolHomes(plat = osPlatform()) {
12560
12671
  for (const pid of pids) {
12561
12672
  let env;
12562
12673
  try {
12563
- env = readFileSync11(`/proc/${pid}/environ`, "utf-8");
12674
+ env = readFileSync12(`/proc/${pid}/environ`, "utf-8");
12564
12675
  } catch {
12565
12676
  continue;
12566
12677
  }
@@ -12640,7 +12751,7 @@ function walkForHomes(dir, depth, visit) {
12640
12751
  for (const e of entries) {
12641
12752
  if (!e.isDirectory() || SKIP.has(e.name))
12642
12753
  continue;
12643
- const child = join18(dir, e.name);
12754
+ const child = join19(dir, e.name);
12644
12755
  visit(child);
12645
12756
  walkForHomes(child, depth - 1, visit);
12646
12757
  }
@@ -12681,20 +12792,20 @@ var init_home_discovery = __esm({
12681
12792
 
12682
12793
  // ../engine/dist/core/detector-install.js
12683
12794
  import { createHash as createHash4 } from "crypto";
12684
- import { existsSync as existsSync18, mkdirSync as mkdirSync4, mkdtempSync, chmodSync as chmodSync2, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync3, readFileSync as readFileSync12, statSync as statSync16, copyFileSync as copyFileSync2 } from "fs";
12685
- import { join as join19 } from "path";
12795
+ import { existsSync as existsSync18, mkdirSync as mkdirSync5, mkdtempSync, chmodSync as chmodSync2, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync4, readFileSync as readFileSync13, statSync as statSync16, copyFileSync as copyFileSync2 } from "fs";
12796
+ import { join as join20 } from "path";
12686
12797
  function managedDetectorPath(name) {
12687
- return join19(getDetectorBinDir(), name + exeSuffix());
12798
+ return join20(getDetectorBinDir(), name + exeSuffix());
12688
12799
  }
12689
12800
  function receiptPath(name) {
12690
- return join19(getDetectorBinDir(), `${name}.receipt.json`);
12801
+ return join20(getDetectorBinDir(), `${name}.receipt.json`);
12691
12802
  }
12692
12803
  function sha256File(path) {
12693
- return createHash4("sha256").update(readFileSync12(path)).digest("hex");
12804
+ return createHash4("sha256").update(readFileSync13(path)).digest("hex");
12694
12805
  }
12695
12806
  function readReceipt(name) {
12696
12807
  try {
12697
- return JSON.parse(readFileSync12(receiptPath(name), "utf-8"));
12808
+ return JSON.parse(readFileSync13(receiptPath(name), "utf-8"));
12698
12809
  } catch {
12699
12810
  return null;
12700
12811
  }
@@ -12720,7 +12831,7 @@ function configuredDir() {
12720
12831
  function resolveDetector(name) {
12721
12832
  const dir = configuredDir();
12722
12833
  if (dir) {
12723
- const p2 = join19(dir, name + exeSuffix());
12834
+ const p2 = join20(dir, name + exeSuffix());
12724
12835
  if (existsSync18(p2))
12725
12836
  return { path: p2, version: "operator-supplied", source: "configured" };
12726
12837
  return { error: "not-installed" };
@@ -12785,9 +12896,9 @@ var init_detector_install = __esm({
12785
12896
 
12786
12897
  // ../engine/dist/core/secret-scanner.js
12787
12898
  import { spawnSync } from "child_process";
12788
- import { existsSync as existsSync19, unlinkSync, readFileSync as readFileSync13, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
12899
+ import { existsSync as existsSync19, unlinkSync, readFileSync as readFileSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
12789
12900
  import { tmpdir } from "os";
12790
- import { join as join20 } from "path";
12901
+ import { join as join21 } from "path";
12791
12902
  function mask(s) {
12792
12903
  if (!s)
12793
12904
  return "";
@@ -12815,7 +12926,7 @@ function whichDetector(bin) {
12815
12926
  return null;
12816
12927
  }
12817
12928
  function spawnScoped(binary, args, opts) {
12818
- const scratch = mkdtempSync2(join20(tmpdir(), "cr-scan-tmp-"));
12929
+ const scratch = mkdtempSync2(join21(tmpdir(), "cr-scan-tmp-"));
12819
12930
  try {
12820
12931
  return spawnSync(binary, args, {
12821
12932
  ...opts,
@@ -12846,7 +12957,7 @@ function runGitleaks(binary, filePath) {
12846
12957
  return [];
12847
12958
  let parsed = [];
12848
12959
  try {
12849
- parsed = JSON.parse(readFileSync13(reportPath, "utf-8")) || [];
12960
+ parsed = JSON.parse(readFileSync14(reportPath, "utf-8")) || [];
12850
12961
  } catch {
12851
12962
  }
12852
12963
  try {
@@ -12910,7 +13021,7 @@ function runGitleaksDir(binary, dir) {
12910
13021
  }
12911
13022
  let parsed = [];
12912
13023
  try {
12913
- parsed = JSON.parse(readFileSync13(reportPath, "utf-8")) || [];
13024
+ parsed = JSON.parse(readFileSync14(reportPath, "utf-8")) || [];
12914
13025
  } catch {
12915
13026
  }
12916
13027
  try {
@@ -13001,7 +13112,7 @@ function scanFileForSecrets(filePath, opts = {}) {
13001
13112
  out.push({ detector: "trufflehog", ...f });
13002
13113
  if (opts.tenantRules && opts.tenantRules.length > 0) {
13003
13114
  try {
13004
- const text = readFileSync13(filePath, "utf-8");
13115
+ const text = readFileSync14(filePath, "utf-8");
13005
13116
  out.push(...scanTenantRules(text, opts.tenantRules));
13006
13117
  } catch {
13007
13118
  }
@@ -13377,10 +13488,10 @@ var init_transcript = __esm({
13377
13488
  // ../engine/dist/core/project-resolver.js
13378
13489
  import { execSync } from "child_process";
13379
13490
  import { createHash as createHash5 } from "crypto";
13380
- import { existsSync as existsSync20, readFileSync as readFileSync14, realpathSync as realpathSync3, readdirSync as readdirSync14, statSync as statSync18 } from "fs";
13381
- import { basename as basename11, dirname as dirname13, isAbsolute, join as join21, resolve, sep } from "path";
13491
+ import { existsSync as existsSync20, readFileSync as readFileSync15, realpathSync as realpathSync3, readdirSync as readdirSync14, statSync as statSync18 } from "fs";
13492
+ import { basename as basename11, dirname as dirname14, isAbsolute, join as join22, resolve, sep } from "path";
13382
13493
  function getProjectsConfigPath() {
13383
- return join21(getDataDir(), "projects.json");
13494
+ return join22(getDataDir(), "projects.json");
13384
13495
  }
13385
13496
  function loadProjectsConfig() {
13386
13497
  if (cachedConfig && cachedConfig.mtime === Number.MAX_SAFE_INTEGER) {
@@ -13395,7 +13506,7 @@ function loadProjectsConfig() {
13395
13506
  if (cachedConfig && cachedConfig.mtime === mtime)
13396
13507
  return cachedConfig.cfg;
13397
13508
  try {
13398
- const raw = readFileSync14(path, "utf-8");
13509
+ const raw = readFileSync15(path, "utf-8");
13399
13510
  const cfg = JSON.parse(raw);
13400
13511
  cachedConfig = { mtime, cfg };
13401
13512
  return cfg;
@@ -13491,7 +13602,7 @@ function matchDeclaredProject(realPath, projects) {
13491
13602
  function matchChild(realPath, parentRoot, children) {
13492
13603
  const sorted = [...children].sort((a, b) => b.path.length - a.path.length);
13493
13604
  for (const c of sorted) {
13494
- const childRoot = safeRealpath(join21(parentRoot, c.path));
13605
+ const childRoot = safeRealpath(join22(parentRoot, c.path));
13495
13606
  if (isInside(realPath, childRoot))
13496
13607
  return c;
13497
13608
  }
@@ -13526,10 +13637,10 @@ function findGitToplevel(realPath) {
13526
13637
  let cur = realPath;
13527
13638
  let last = "";
13528
13639
  while (cur && cur !== last) {
13529
- if (existsSync20(join21(cur, ".git")))
13640
+ if (existsSync20(join22(cur, ".git")))
13530
13641
  return cur;
13531
13642
  last = cur;
13532
- cur = dirname13(cur);
13643
+ cur = dirname14(cur);
13533
13644
  }
13534
13645
  return null;
13535
13646
  }
@@ -13593,7 +13704,7 @@ function resolveAutoWorkspace(realPath, minRepos) {
13593
13704
  let last = "";
13594
13705
  let depth = 0;
13595
13706
  while (cur && cur !== last && depth < AUTO_WORKSPACE_MAX_DEPTH) {
13596
- if (!existsSync20(join21(cur, ".git"))) {
13707
+ if (!existsSync20(join22(cur, ".git"))) {
13597
13708
  const cached = workspaceCache.get(cur);
13598
13709
  let qualifies;
13599
13710
  if (cached === void 0) {
@@ -13608,7 +13719,7 @@ function resolveAutoWorkspace(realPath, minRepos) {
13608
13719
  }
13609
13720
  }
13610
13721
  last = cur;
13611
- cur = dirname13(cur);
13722
+ cur = dirname14(cur);
13612
13723
  depth++;
13613
13724
  }
13614
13725
  return null;
@@ -13624,12 +13735,12 @@ function countGitChildren(dir) {
13624
13735
  for (const name of entries) {
13625
13736
  if (name.startsWith("."))
13626
13737
  continue;
13627
- const child = join21(dir, name);
13738
+ const child = join22(dir, name);
13628
13739
  try {
13629
13740
  const st = statSync18(child);
13630
13741
  if (!st.isDirectory())
13631
13742
  continue;
13632
- if (existsSync20(join21(child, ".git")))
13743
+ if (existsSync20(join22(child, ".git")))
13633
13744
  count++;
13634
13745
  } catch {
13635
13746
  }
@@ -14443,7 +14554,7 @@ var init_session_source = __esm({
14443
14554
  // ../engine/dist/core/utils.js
14444
14555
  import { existsSync as existsSync21, readdirSync as readdirSync15 } from "fs";
14445
14556
  import { homedir as homedir6 } from "os";
14446
- import { join as join22 } from "path";
14557
+ import { join as join23 } from "path";
14447
14558
  function splitByHeaders(markdown) {
14448
14559
  const lines = markdown.split("\n");
14449
14560
  const sections = [];
@@ -14486,17 +14597,17 @@ function getClaudeDirs() {
14486
14597
  for (const d of process.env.CLAUDE_DIRS.split(",")) {
14487
14598
  const trimmed = d.trim();
14488
14599
  if (trimmed) {
14489
- add(trimmed.startsWith("~/") ? join22(home, trimmed.slice(2)) : trimmed);
14600
+ add(trimmed.startsWith("~/") ? join23(home, trimmed.slice(2)) : trimmed);
14490
14601
  }
14491
14602
  }
14492
14603
  }
14493
- add(join22(home, ".claude"));
14604
+ add(join23(home, ".claude"));
14494
14605
  try {
14495
14606
  const entries = readdirSync15(home, { withFileTypes: true });
14496
14607
  for (const entry of entries) {
14497
14608
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
14498
- const candidate = join22(home, entry.name);
14499
- if (existsSync21(join22(candidate, "projects"))) {
14609
+ const candidate = join23(home, entry.name);
14610
+ if (existsSync21(join23(candidate, "projects"))) {
14500
14611
  add(candidate);
14501
14612
  }
14502
14613
  }
@@ -14508,12 +14619,12 @@ function getClaudeDirs() {
14508
14619
  function discoverSubdirs(subdir) {
14509
14620
  const result = [];
14510
14621
  for (const claudeDir of getClaudeDirs()) {
14511
- const path = join22(claudeDir, subdir);
14622
+ const path = join23(claudeDir, subdir);
14512
14623
  if (existsSync21(path)) {
14513
14624
  result.push(path);
14514
14625
  }
14515
14626
  }
14516
- return result.length > 0 ? result : [join22(homedir6(), ".claude", subdir)];
14627
+ return result.length > 0 ? result : [join23(homedir6(), ".claude", subdir)];
14517
14628
  }
14518
14629
  var init_utils = __esm({
14519
14630
  "../engine/dist/core/utils.js"() {
@@ -14522,9 +14633,9 @@ var init_utils = __esm({
14522
14633
  });
14523
14634
 
14524
14635
  // ../engine/dist/parsers/plan-source.js
14525
- import { existsSync as existsSync22, readdirSync as readdirSync16, readFileSync as readFileSync15, statSync as statSync20 } from "fs";
14636
+ import { existsSync as existsSync22, readdirSync as readdirSync16, readFileSync as readFileSync16, statSync as statSync20 } from "fs";
14526
14637
  import { homedir as homedir7 } from "os";
14527
- import { join as join23, basename as basename12 } from "path";
14638
+ import { join as join24, basename as basename12 } from "path";
14528
14639
  function parseFrontmatter(content) {
14529
14640
  if (!content.startsWith("---"))
14530
14641
  return { body: content };
@@ -14619,12 +14730,12 @@ var init_plan_source = __esm({
14619
14730
  if (seenClaude.has(file))
14620
14731
  continue;
14621
14732
  seenClaude.add(file);
14622
- const filePath = join23(plansDir, file);
14733
+ const filePath = join24(plansDir, file);
14623
14734
  try {
14624
14735
  const stat3 = statSync20(filePath);
14625
14736
  const planName = basename12(file, ".md");
14626
14737
  const isAgentPlan = planName.includes("-agent-");
14627
- const content = readFileSync15(filePath, "utf-8");
14738
+ const content = readFileSync16(filePath, "utf-8");
14628
14739
  const fm = parseFrontmatter(content);
14629
14740
  const body = fm.body;
14630
14741
  const firstLine = body.split("\n").find((l) => l.trim())?.replace(/^#+\s*/, "").trim() || planName;
@@ -14663,10 +14774,10 @@ var init_plan_source = __esm({
14663
14774
  return;
14664
14775
  }
14665
14776
  for (const file of files) {
14666
- const filePath = join23(plansDir, file);
14777
+ const filePath = join24(plansDir, file);
14667
14778
  try {
14668
14779
  const stat3 = statSync20(filePath);
14669
- const content = readFileSync15(filePath, "utf-8");
14780
+ const content = readFileSync16(filePath, "utf-8");
14670
14781
  const firstLine = content.split("\n").find((l) => l.trim())?.replace(/^#+\s*/, "").trim() || file;
14671
14782
  const planName = `${opencodeBackend.idPrefix}plan_${basename12(file, ".md")}`;
14672
14783
  const projectPath = this.extractProjectPath(content);
@@ -14694,7 +14805,7 @@ var init_plan_source = __esm({
14694
14805
  for (const match of content.matchAll(fullPathPattern)) {
14695
14806
  let path = (match[1] || "").trim();
14696
14807
  if (path.startsWith("~/")) {
14697
- path = join23(homedir7(), path.slice(2));
14808
+ path = join24(homedir7(), path.slice(2));
14698
14809
  }
14699
14810
  for (const projectDir of knownProjects) {
14700
14811
  if (path.startsWith(projectDir)) {
@@ -14743,7 +14854,7 @@ var init_plan_source = __esm({
14743
14854
  async parse(item) {
14744
14855
  if (!existsSync22(item.filePath))
14745
14856
  return [];
14746
- const content = readFileSync15(item.filePath, "utf-8");
14857
+ const content = readFileSync16(item.filePath, "utf-8");
14747
14858
  const chunks = [];
14748
14859
  const { body } = parseFrontmatter(content);
14749
14860
  const sections = splitByHeaders(body);
@@ -14779,9 +14890,9 @@ var init_plan_source = __esm({
14779
14890
  const map = /* @__PURE__ */ new Map();
14780
14891
  const root = claudeBackend.projectsDir();
14781
14892
  for (const proj of safeReaddir(root)) {
14782
- const projPath = join23(root, proj);
14893
+ const projPath = join24(root, proj);
14783
14894
  for (const sessionEntry of safeReaddir(projPath)) {
14784
- const subDir = join23(projPath, sessionEntry, "subagents");
14895
+ const subDir = join24(projPath, sessionEntry, "subagents");
14785
14896
  if (!existsSync22(subDir))
14786
14897
  continue;
14787
14898
  for (const f of safeReaddir(subDir)) {
@@ -14856,7 +14967,7 @@ var init_plan_source = __esm({
14856
14967
  return links;
14857
14968
  }
14858
14969
  async *discoverAgyPlans() {
14859
- const brainDir = join23(agyBackend.homeDir(), "brain");
14970
+ const brainDir = join24(agyBackend.homeDir(), "brain");
14860
14971
  if (!existsSync22(brainDir))
14861
14972
  return;
14862
14973
  let sessionDirs;
@@ -14866,7 +14977,7 @@ var init_plan_source = __esm({
14866
14977
  return;
14867
14978
  }
14868
14979
  for (const rawId of sessionDirs) {
14869
- const sessionPath = join23(brainDir, rawId);
14980
+ const sessionPath = join24(brainDir, rawId);
14870
14981
  try {
14871
14982
  if (!statSync20(sessionPath).isDirectory())
14872
14983
  continue;
@@ -14880,10 +14991,10 @@ var init_plan_source = __esm({
14880
14991
  continue;
14881
14992
  }
14882
14993
  for (const file of files) {
14883
- const filePath = join23(sessionPath, file);
14994
+ const filePath = join24(sessionPath, file);
14884
14995
  try {
14885
14996
  const stat3 = statSync20(filePath);
14886
- const content = readFileSync15(filePath, "utf-8");
14997
+ const content = readFileSync16(filePath, "utf-8");
14887
14998
  const firstLine = content.split("\n").find((l) => l.trim())?.replace(/^#+\s*/, "").trim() || file;
14888
14999
  const sessionLoc = agyBackend.findSession(rawId);
14889
15000
  const projectPath = sessionLoc?.projectPath || this.extractProjectPath(content);
@@ -14912,8 +15023,8 @@ var init_plan_source = __esm({
14912
15023
  });
14913
15024
 
14914
15025
  // ../engine/dist/parsers/task-source.js
14915
- import { existsSync as existsSync23, readdirSync as readdirSync17, readFileSync as readFileSync16, statSync as statSync21 } from "fs";
14916
- import { join as join24 } from "path";
15026
+ import { existsSync as existsSync23, readdirSync as readdirSync17, readFileSync as readFileSync17, statSync as statSync21 } from "fs";
15027
+ import { join as join25 } from "path";
14917
15028
  var MAX_CHUNK_CHARS2, TaskSource;
14918
15029
  var init_task_source = __esm({
14919
15030
  "../engine/dist/parsers/task-source.js"() {
@@ -14951,7 +15062,7 @@ var init_task_source = __esm({
14951
15062
  return;
14952
15063
  }
14953
15064
  for (const file of files) {
14954
- const filePath = join24(todosDir, file);
15065
+ const filePath = join25(todosDir, file);
14955
15066
  let stat3;
14956
15067
  try {
14957
15068
  stat3 = statSync21(filePath);
@@ -14962,7 +15073,7 @@ var init_task_source = __esm({
14962
15073
  continue;
14963
15074
  let parsed;
14964
15075
  try {
14965
- const raw = readFileSync16(filePath, "utf-8");
15076
+ const raw = readFileSync17(filePath, "utf-8");
14966
15077
  parsed = JSON.parse(raw);
14967
15078
  if (!Array.isArray(parsed) || parsed.length === 0)
14968
15079
  continue;
@@ -15000,7 +15111,7 @@ var init_task_source = __esm({
15000
15111
  if (!entry.isDirectory())
15001
15112
  continue;
15002
15113
  const sessionUuid = entry.name;
15003
- const taskDir = join24(tasksDir, sessionUuid);
15114
+ const taskDir = join25(tasksDir, sessionUuid);
15004
15115
  try {
15005
15116
  const stat3 = statSync21(taskDir);
15006
15117
  const taskFiles = readdirSync17(taskDir).filter((f) => f.endsWith(".json"));
@@ -15075,7 +15186,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
15075
15186
  text,
15076
15187
  chunkType: "task_detail",
15077
15188
  projectPath: item.projectPath,
15078
- filePath: join24(taskDir, `${task.id}.json`),
15189
+ filePath: join25(taskDir, `${task.id}.json`),
15079
15190
  mtime: item.mtime
15080
15191
  });
15081
15192
  }
@@ -15100,7 +15211,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
15100
15211
  return [];
15101
15212
  let parsed;
15102
15213
  try {
15103
- parsed = JSON.parse(readFileSync16(item.filePath, "utf-8"));
15214
+ parsed = JSON.parse(readFileSync17(item.filePath, "utf-8"));
15104
15215
  } catch {
15105
15216
  return [];
15106
15217
  }
@@ -15132,7 +15243,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
15132
15243
  });
15133
15244
  for (const file of sorted) {
15134
15245
  try {
15135
- const content = readFileSync16(join24(taskDir, file), "utf-8");
15246
+ const content = readFileSync17(join25(taskDir, file), "utf-8");
15136
15247
  const task = JSON.parse(content);
15137
15248
  if (task.subject) {
15138
15249
  tasks.push(task);
@@ -15148,9 +15259,9 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
15148
15259
  });
15149
15260
 
15150
15261
  // ../engine/dist/parsers/claude-md-source.js
15151
- import { existsSync as existsSync24, readFileSync as readFileSync17, readdirSync as readdirSync18, statSync as statSync22 } from "fs";
15262
+ import { existsSync as existsSync24, readFileSync as readFileSync18, readdirSync as readdirSync18, statSync as statSync22 } from "fs";
15152
15263
  import { homedir as homedir8 } from "os";
15153
- import { join as join25, basename as basename13, dirname as dirname14 } from "path";
15264
+ import { join as join26, basename as basename13, dirname as dirname15 } from "path";
15154
15265
  import { createHash as createHash6 } from "crypto";
15155
15266
  function discoverProjectDirs(claudeDir) {
15156
15267
  const dirs = [];
@@ -15176,7 +15287,7 @@ function discoverProjectDirs(claudeDir) {
15176
15287
  }
15177
15288
  };
15178
15289
  if (claudeDir) {
15179
- scanProjectsDir(join25(claudeDir, "projects"));
15290
+ scanProjectsDir(join26(claudeDir, "projects"));
15180
15291
  return dirs;
15181
15292
  }
15182
15293
  const home = homedir8();
@@ -15184,7 +15295,7 @@ function discoverProjectDirs(claudeDir) {
15184
15295
  try {
15185
15296
  for (const entry of readdirSync18(home, { withFileTypes: true })) {
15186
15297
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
15187
- scanProjectsDir(join25(home, entry.name, "projects"));
15298
+ scanProjectsDir(join26(home, entry.name, "projects"));
15188
15299
  }
15189
15300
  }
15190
15301
  } catch {
@@ -15192,7 +15303,7 @@ function discoverProjectDirs(claudeDir) {
15192
15303
  const gemProjects = agyBackend.sharedProjectsJson();
15193
15304
  if (existsSync24(gemProjects)) {
15194
15305
  try {
15195
- const data = JSON.parse(readFileSync17(gemProjects, "utf-8"));
15306
+ const data = JSON.parse(readFileSync18(gemProjects, "utf-8"));
15196
15307
  for (const path of Object.keys(data.projects || {}))
15197
15308
  addProject(path);
15198
15309
  } catch {
@@ -15248,7 +15359,7 @@ var init_claude_md_source = __esm({
15248
15359
  const seen = /* @__PURE__ */ new Set();
15249
15360
  const seenKey = (path, tool) => `${tool}:${path}`;
15250
15361
  for (const note of NOTE_FILES) {
15251
- const p = join25(homedir8(), note.filename);
15362
+ const p = join26(homedir8(), note.filename);
15252
15363
  const key2 = seenKey(p, note.tool);
15253
15364
  if (existsSync24(p) && !seen.has(key2)) {
15254
15365
  seen.add(key2);
@@ -15260,7 +15371,7 @@ var init_claude_md_source = __esm({
15260
15371
  const projectDirs = discoverProjectDirs(this.claudeDir);
15261
15372
  for (const projectDir of projectDirs) {
15262
15373
  for (const note of NOTE_FILES) {
15263
- const p = join25(projectDir, note.filename);
15374
+ const p = join26(projectDir, note.filename);
15264
15375
  const key2 = seenKey(p, note.tool);
15265
15376
  if (existsSync24(p) && !seen.has(key2)) {
15266
15377
  seen.add(key2);
@@ -15274,7 +15385,7 @@ var init_claude_md_source = __esm({
15274
15385
  if (!existsSync24(searchDir))
15275
15386
  continue;
15276
15387
  for (const note of NOTE_FILES) {
15277
- const directPath = join25(searchDir, note.filename);
15388
+ const directPath = join26(searchDir, note.filename);
15278
15389
  const key2 = seenKey(directPath, note.tool);
15279
15390
  if (existsSync24(directPath) && !seen.has(key2)) {
15280
15391
  seen.add(key2);
@@ -15289,7 +15400,7 @@ var init_claude_md_source = __esm({
15289
15400
  if (!entry.isDirectory())
15290
15401
  continue;
15291
15402
  for (const note of NOTE_FILES) {
15292
- const p = join25(searchDir, entry.name, note.filename);
15403
+ const p = join26(searchDir, entry.name, note.filename);
15293
15404
  const key2 = seenKey(p, note.tool);
15294
15405
  if (existsSync24(p) && !seen.has(key2)) {
15295
15406
  seen.add(key2);
@@ -15307,7 +15418,7 @@ var init_claude_md_source = __esm({
15307
15418
  async parse(item) {
15308
15419
  if (!existsSync24(item.filePath))
15309
15420
  return [];
15310
- const content = readFileSync17(item.filePath, "utf-8");
15421
+ const content = readFileSync18(item.filePath, "utf-8");
15311
15422
  const chunks = [];
15312
15423
  const sections = splitByHeaders(content);
15313
15424
  for (let i = 0; i < sections.length; i++) {
@@ -15346,10 +15457,10 @@ var init_claude_md_source = __esm({
15346
15457
  fileToItem(filePath, filename, tool) {
15347
15458
  try {
15348
15459
  const stat3 = statSync22(filePath);
15349
- const projectDir = dirname14(filePath);
15460
+ const projectDir = dirname15(filePath);
15350
15461
  const projectName = basename13(projectDir);
15351
15462
  const projectPath = projectDir;
15352
- const content = readFileSync17(filePath, "utf-8");
15463
+ const content = readFileSync18(filePath, "utf-8");
15353
15464
  const firstLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#"))?.trim()?.slice(0, 200) || `${filename} for ${projectName}`;
15354
15465
  const id = tool === "claude" ? projectName : `${projectName}_${tool}`;
15355
15466
  return {
@@ -15374,9 +15485,9 @@ var init_claude_md_source = __esm({
15374
15485
  });
15375
15486
 
15376
15487
  // ../engine/dist/parsers/agent-memory-source.js
15377
- import { existsSync as existsSync25, readFileSync as readFileSync18, readdirSync as readdirSync19, statSync as statSync23 } from "fs";
15488
+ import { existsSync as existsSync25, readFileSync as readFileSync19, readdirSync as readdirSync19, statSync as statSync23 } from "fs";
15378
15489
  import { homedir as homedir9 } from "os";
15379
- import { join as join26, basename as basename14 } from "path";
15490
+ import { join as join27, basename as basename14 } from "path";
15380
15491
  function parseFrontmatter2(content) {
15381
15492
  const fm = {};
15382
15493
  let body = content;
@@ -15418,7 +15529,7 @@ function discoverMemoryDirs() {
15418
15529
  try {
15419
15530
  for (const entry of readdirSync19(home, { withFileTypes: true })) {
15420
15531
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
15421
- claudeRoots.push(join26(home, entry.name, "projects"));
15532
+ claudeRoots.push(join27(home, entry.name, "projects"));
15422
15533
  }
15423
15534
  }
15424
15535
  } catch {
@@ -15430,7 +15541,7 @@ function discoverMemoryDirs() {
15430
15541
  for (const entry of readdirSync19(projectsDir, { withFileTypes: true })) {
15431
15542
  if (!entry.isDirectory() || entry.name.startsWith("."))
15432
15543
  continue;
15433
- add(join26(projectsDir, entry.name, "memory"), entry.name);
15544
+ add(join27(projectsDir, entry.name, "memory"), entry.name);
15434
15545
  }
15435
15546
  } catch {
15436
15547
  }
@@ -15459,7 +15570,7 @@ var init_agent_memory_source = __esm({
15459
15570
  for (const entry of readdirSync19(memoryDir, { withFileTypes: true })) {
15460
15571
  if (!entry.isFile() || !entry.name.endsWith(".md"))
15461
15572
  continue;
15462
- const filePath = join26(memoryDir, entry.name);
15573
+ const filePath = join27(memoryDir, entry.name);
15463
15574
  if (seen.has(filePath))
15464
15575
  continue;
15465
15576
  seen.add(filePath);
@@ -15475,7 +15586,7 @@ var init_agent_memory_source = __esm({
15475
15586
  async parse(item) {
15476
15587
  if (!existsSync25(item.filePath))
15477
15588
  return [];
15478
- const content = readFileSync18(item.filePath, "utf-8");
15589
+ const content = readFileSync19(item.filePath, "utf-8");
15479
15590
  const { body } = parseFrontmatter2(content);
15480
15591
  const chunks = [];
15481
15592
  const sections = splitByHeaders(body);
@@ -15528,7 +15639,7 @@ var init_agent_memory_source = __esm({
15528
15639
  fileToItem(filePath, filename, projectPath, _projectHash) {
15529
15640
  try {
15530
15641
  const stat3 = statSync23(filePath);
15531
- const content = readFileSync18(filePath, "utf-8");
15642
+ const content = readFileSync19(filePath, "utf-8");
15532
15643
  const { fm } = parseFrontmatter2(content);
15533
15644
  const stem = filename.replace(/\.md$/, "");
15534
15645
  const idBase = fm.name || stem;
@@ -15563,7 +15674,7 @@ import { existsSync as existsSync26, readdirSync as readdirSync20, statSync as s
15563
15674
  import { createReadStream as createReadStream3 } from "fs";
15564
15675
  import { createInterface as createInterface2 } from "readline";
15565
15676
  import { homedir as homedir10 } from "os";
15566
- import { join as join27 } from "path";
15677
+ import { join as join28 } from "path";
15567
15678
  var MAX_CHUNK_CHARS5, HistorySource;
15568
15679
  var init_history_source = __esm({
15569
15680
  "../engine/dist/parsers/history-source.js"() {
@@ -15588,12 +15699,12 @@ var init_history_source = __esm({
15588
15699
  try {
15589
15700
  for (const entry of readdirSync20(home, { withFileTypes: true })) {
15590
15701
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
15591
- tryAdd(join27(home, entry.name, "history.jsonl"));
15702
+ tryAdd(join28(home, entry.name, "history.jsonl"));
15592
15703
  }
15593
15704
  }
15594
15705
  } catch {
15595
15706
  }
15596
- this.historyPaths = paths.length > 0 ? paths : [join27(home, ".claude", "history.jsonl")];
15707
+ this.historyPaths = paths.length > 0 ? paths : [join28(home, ".claude", "history.jsonl")];
15597
15708
  }
15598
15709
  }
15599
15710
  async *discover() {
@@ -15743,9 +15854,9 @@ var init_history_source = __esm({
15743
15854
  });
15744
15855
 
15745
15856
  // ../engine/dist/parsers/paste-source.js
15746
- import { existsSync as existsSync27, readdirSync as readdirSync21, readFileSync as readFileSync19, statSync as statSync25 } from "fs";
15857
+ import { existsSync as existsSync27, readdirSync as readdirSync21, readFileSync as readFileSync20, statSync as statSync25 } from "fs";
15747
15858
  import { homedir as homedir11 } from "os";
15748
- import { join as join28, basename as basename15 } from "path";
15859
+ import { join as join29, basename as basename15 } from "path";
15749
15860
  var MAX_CHUNK_CHARS6, MAX_FILE_SIZE, PasteSource;
15750
15861
  var init_paste_source = __esm({
15751
15862
  "../engine/dist/parsers/paste-source.js"() {
@@ -15771,7 +15882,7 @@ var init_paste_source = __esm({
15771
15882
  try {
15772
15883
  for (const entry of readdirSync21(home, { withFileTypes: true })) {
15773
15884
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
15774
- tryAdd(join28(home, entry.name, "paste-cache"));
15885
+ tryAdd(join29(home, entry.name, "paste-cache"));
15775
15886
  }
15776
15887
  }
15777
15888
  } catch {
@@ -15791,7 +15902,7 @@ var init_paste_source = __esm({
15791
15902
  if (seen.has(file))
15792
15903
  continue;
15793
15904
  seen.add(file);
15794
- const filePath = join28(cacheDir, file);
15905
+ const filePath = join29(cacheDir, file);
15795
15906
  try {
15796
15907
  const stat3 = statSync25(filePath);
15797
15908
  if (stat3.size > MAX_FILE_SIZE)
@@ -15799,7 +15910,7 @@ var init_paste_source = __esm({
15799
15910
  if (stat3.size < 10)
15800
15911
  continue;
15801
15912
  const hash = basename15(file, ".txt");
15802
- const content = readFileSync19(filePath, "utf-8");
15913
+ const content = readFileSync20(filePath, "utf-8");
15803
15914
  const firstLine = content.split("\n").find((l) => l.trim().length > 5)?.trim()?.slice(0, 100) || `Paste ${hash.slice(0, 8)}`;
15804
15915
  yield {
15805
15916
  id: hash,
@@ -15820,7 +15931,7 @@ var init_paste_source = __esm({
15820
15931
  async parse(item) {
15821
15932
  if (!existsSync27(item.filePath))
15822
15933
  return [];
15823
- const content = readFileSync19(item.filePath, "utf-8");
15934
+ const content = readFileSync20(item.filePath, "utf-8");
15824
15935
  if (content.trim().length < 20)
15825
15936
  return [];
15826
15937
  return [{
@@ -15843,8 +15954,8 @@ var init_paste_source = __esm({
15843
15954
  });
15844
15955
 
15845
15956
  // ../engine/dist/parsers/antigravity-brain-source.js
15846
- import { existsSync as existsSync28, readdirSync as readdirSync22, readFileSync as readFileSync20, statSync as statSync26 } from "fs";
15847
- import { join as join29 } from "path";
15957
+ import { existsSync as existsSync28, readdirSync as readdirSync22, readFileSync as readFileSync21, statSync as statSync26 } from "fs";
15958
+ import { join as join30 } from "path";
15848
15959
  var MAX_CHUNK_CHARS7, AntigravityBrainSource;
15849
15960
  var init_antigravity_brain_source = __esm({
15850
15961
  "../engine/dist/parsers/antigravity-brain-source.js"() {
@@ -15868,22 +15979,22 @@ var init_antigravity_brain_source = __esm({
15868
15979
  for (const entry of sessions) {
15869
15980
  if (!entry.isDirectory())
15870
15981
  continue;
15871
- const sessionDir = join29(this.brainDir, entry.name);
15982
+ const sessionDir = join30(this.brainDir, entry.name);
15872
15983
  const files = readdirSync22(sessionDir).filter((f) => f.endsWith(".md") && !f.includes(".resolved") && !f.includes(".metadata"));
15873
15984
  if (files.length === 0)
15874
15985
  continue;
15875
15986
  for (const file of files) {
15876
- const filePath = join29(sessionDir, file);
15987
+ const filePath = join30(sessionDir, file);
15877
15988
  try {
15878
15989
  const stat3 = statSync26(filePath);
15879
- const content = readFileSync20(filePath, "utf-8");
15990
+ const content = readFileSync21(filePath, "utf-8");
15880
15991
  const firstLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#"))?.trim() || "";
15881
15992
  const heading = content.split("\n").find((l) => l.startsWith("#"))?.replace(/^#+\s*/, "").trim() || file;
15882
15993
  const metaPath = `${filePath}.metadata.json`;
15883
15994
  let metaSummary = "";
15884
15995
  if (existsSync28(metaPath)) {
15885
15996
  try {
15886
- const meta = JSON.parse(readFileSync20(metaPath, "utf-8"));
15997
+ const meta = JSON.parse(readFileSync21(metaPath, "utf-8"));
15887
15998
  metaSummary = meta.summary || "";
15888
15999
  } catch {
15889
16000
  }
@@ -15914,7 +16025,7 @@ var init_antigravity_brain_source = __esm({
15914
16025
  async parse(item) {
15915
16026
  if (!existsSync28(item.filePath))
15916
16027
  return [];
15917
- const content = readFileSync20(item.filePath, "utf-8");
16028
+ const content = readFileSync21(item.filePath, "utf-8");
15918
16029
  const chunks = [];
15919
16030
  const sections = splitByHeaders(content);
15920
16031
  for (let i = 0; i < sections.length; i++) {
@@ -16257,8 +16368,8 @@ var init_opencode_todo_source = __esm({
16257
16368
  });
16258
16369
 
16259
16370
  // ../engine/dist/parsers/codex-session-source.js
16260
- import { existsSync as existsSync31, readdirSync as readdirSync23, readFileSync as readFileSync21, statSync as statSync27 } from "fs";
16261
- import { join as join30, basename as basename16 } from "path";
16371
+ import { existsSync as existsSync31, readdirSync as readdirSync23, readFileSync as readFileSync22, statSync as statSync27 } from "fs";
16372
+ import { join as join31, basename as basename16 } from "path";
16262
16373
  function getCodexSessionsDir() {
16263
16374
  return codexBackend.sessionsDir();
16264
16375
  }
@@ -16285,7 +16396,7 @@ function extractReasoningText(event) {
16285
16396
  return "";
16286
16397
  }
16287
16398
  function readJsonlLines(filePath) {
16288
- const text = readFileSync21(filePath, "utf-8");
16399
+ const text = readFileSync22(filePath, "utf-8");
16289
16400
  const lines = text.split("\n");
16290
16401
  const events = [];
16291
16402
  for (const line of lines) {
@@ -16320,7 +16431,7 @@ var init_codex_session_source = __esm({
16320
16431
  for (const filePath of jsonlFiles) {
16321
16432
  try {
16322
16433
  const stat3 = statSync27(filePath);
16323
- const lines = readFileSync21(filePath, "utf-8").split("\n");
16434
+ const lines = readFileSync22(filePath, "utf-8").split("\n");
16324
16435
  const firstLine = lines.find((l) => l.trim());
16325
16436
  if (!firstLine)
16326
16437
  continue;
@@ -16479,7 +16590,7 @@ var init_codex_session_source = __esm({
16479
16590
  if (!existsSync31(dir))
16480
16591
  return results;
16481
16592
  for (const entry of readdirSync23(dir, { withFileTypes: true })) {
16482
- const fullPath = join30(dir, entry.name);
16593
+ const fullPath = join31(dir, entry.name);
16483
16594
  if (entry.isDirectory()) {
16484
16595
  results.push(...this.collectJsonlFiles(fullPath));
16485
16596
  } else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(".jsonl")) {
@@ -16493,8 +16604,8 @@ var init_codex_session_source = __esm({
16493
16604
  });
16494
16605
 
16495
16606
  // ../engine/dist/parsers/diary-source.js
16496
- import { existsSync as existsSync32, mkdirSync as mkdirSync5, readdirSync as readdirSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync4, statSync as statSync28 } from "fs";
16497
- import { join as join31, basename as basename17 } from "path";
16607
+ import { existsSync as existsSync32, mkdirSync as mkdirSync6, readdirSync as readdirSync24, readFileSync as readFileSync23, writeFileSync as writeFileSync5, statSync as statSync28 } from "fs";
16608
+ import { join as join32, basename as basename17 } from "path";
16498
16609
  var MAX_CHUNK_CHARS10, DiarySource;
16499
16610
  var init_diary_source = __esm({
16500
16611
  "../engine/dist/parsers/diary-source.js"() {
@@ -16513,13 +16624,13 @@ var init_diary_source = __esm({
16513
16624
  return;
16514
16625
  const agents = readdirSync24(this.diaryDir, { withFileTypes: true }).filter((d) => d.isDirectory());
16515
16626
  for (const agentDir of agents) {
16516
- const agentPath2 = join31(this.diaryDir, agentDir.name);
16627
+ const agentPath2 = join32(this.diaryDir, agentDir.name);
16517
16628
  const files = readdirSync24(agentPath2).filter((f) => f.endsWith(".json"));
16518
16629
  for (const file of files) {
16519
- const filePath = join31(agentPath2, file);
16630
+ const filePath = join32(agentPath2, file);
16520
16631
  try {
16521
16632
  const stat3 = statSync28(filePath);
16522
- const raw = readFileSync22(filePath, "utf-8");
16633
+ const raw = readFileSync23(filePath, "utf-8");
16523
16634
  const entry = JSON.parse(raw);
16524
16635
  const entryId = basename17(file, ".json");
16525
16636
  const title = `[${entry.agent}] ${entry.topic}: ${entry.content.slice(0, 80)}`;
@@ -16546,7 +16657,7 @@ var init_diary_source = __esm({
16546
16657
  async parse(item) {
16547
16658
  if (!existsSync32(item.filePath))
16548
16659
  return [];
16549
- const raw = readFileSync22(item.filePath, "utf-8");
16660
+ const raw = readFileSync23(item.filePath, "utf-8");
16550
16661
  const entry = JSON.parse(raw);
16551
16662
  if (!entry.content || entry.content.trim().length < 10)
16552
16663
  return [];
@@ -16583,18 +16694,18 @@ var init_diary_source = __esm({
16583
16694
  */
16584
16695
  static write(entry, diaryDir) {
16585
16696
  const dir = diaryDir || getDiaryDir();
16586
- const agentDir = join31(dir, entry.agent.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
16697
+ const agentDir = join32(dir, entry.agent.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
16587
16698
  if (!existsSync32(agentDir)) {
16588
- mkdirSync5(agentDir, { recursive: true });
16699
+ mkdirSync6(agentDir, { recursive: true });
16589
16700
  }
16590
16701
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
16591
16702
  const entryId = `diary_${entry.agent.toLowerCase()}_${ts}`;
16592
- const filePath = join31(agentDir, `${entryId}.json`);
16703
+ const filePath = join32(agentDir, `${entryId}.json`);
16593
16704
  const full = {
16594
16705
  ...entry,
16595
16706
  timestamp: entry.timestamp || (/* @__PURE__ */ new Date()).toISOString()
16596
16707
  };
16597
- writeFileSync4(filePath, JSON.stringify(full, null, 2), "utf-8");
16708
+ writeFileSync5(filePath, JSON.stringify(full, null, 2), "utf-8");
16598
16709
  return entryId;
16599
16710
  }
16600
16711
  /**
@@ -16602,14 +16713,14 @@ var init_diary_source = __esm({
16602
16713
  */
16603
16714
  static read(agentName, lastN = 10, diaryDir) {
16604
16715
  const dir = diaryDir || getDiaryDir();
16605
- const agentDir = join31(dir, agentName.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
16716
+ const agentDir = join32(dir, agentName.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
16606
16717
  if (!existsSync32(agentDir))
16607
16718
  return [];
16608
16719
  const files = readdirSync24(agentDir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, lastN);
16609
16720
  const entries = [];
16610
16721
  for (const file of files) {
16611
16722
  try {
16612
- const raw = readFileSync22(join31(agentDir, file), "utf-8");
16723
+ const raw = readFileSync23(join32(agentDir, file), "utf-8");
16613
16724
  entries.push(JSON.parse(raw));
16614
16725
  } catch {
16615
16726
  continue;
@@ -16654,9 +16765,9 @@ var init_redact_inline = __esm({
16654
16765
  });
16655
16766
 
16656
16767
  // ../engine/dist/parsers/skills-source.js
16657
- import { existsSync as existsSync33, readdirSync as readdirSync25, readFileSync as readFileSync23, statSync as statSync29 } from "fs";
16768
+ import { existsSync as existsSync33, readdirSync as readdirSync25, readFileSync as readFileSync24, statSync as statSync29 } from "fs";
16658
16769
  import { homedir as homedir12 } from "os";
16659
- import { join as join32 } from "path";
16770
+ import { join as join33 } from "path";
16660
16771
  function rebuildBody(content) {
16661
16772
  const { text, redacted } = redactInlineSecrets(content);
16662
16773
  const out = {};
@@ -16674,25 +16785,25 @@ function resolveRoots() {
16674
16785
  const home = homedir12();
16675
16786
  const all = [];
16676
16787
  if (isSourceEnabled("shared", "skills")) {
16677
- all.push({ path: join32(home, ".agents", "skills"), tool: "shared" });
16788
+ all.push({ path: join33(home, ".agents", "skills"), tool: "shared" });
16678
16789
  }
16679
16790
  if (isSourceEnabled("claude", "skills")) {
16680
16791
  all.push({ path: claudeBackend.skillsDir(), tool: "claude" });
16681
16792
  }
16682
16793
  if (isSourceEnabled("agy", "skills")) {
16683
- all.push({ path: join32(agyBackend.homeDir(), "skills"), tool: "agy" });
16794
+ all.push({ path: join33(agyBackend.homeDir(), "skills"), tool: "agy" });
16684
16795
  all.push({ path: agyBackend.sharedSkillsDir(), tool: "agy" });
16685
16796
  }
16686
16797
  if (isSourceEnabled("opencode", "skills")) {
16687
- all.push({ path: join32(home, ".config", "opencode", "skills"), tool: "opencode" }, { path: join32(home, ".config", "opencode", "skill"), tool: "opencode" }, { path: join32(home, ".opencode", "skill"), tool: "opencode" }, { path: join32(home, ".opencode", "skills"), tool: "opencode" });
16798
+ all.push({ path: join33(home, ".config", "opencode", "skills"), tool: "opencode" }, { path: join33(home, ".config", "opencode", "skill"), tool: "opencode" }, { path: join33(home, ".opencode", "skill"), tool: "opencode" }, { path: join33(home, ".opencode", "skills"), tool: "opencode" });
16688
16799
  }
16689
16800
  if (isSourceEnabled("codex", "skills")) {
16690
16801
  all.push({ path: codexBackend.skillsDir(), tool: "codex", skipChildren: [".system"] });
16691
16802
  all.push({ path: codexBackend.skillsSystemDir(), tool: "codex", readonly: true });
16692
16803
  }
16693
16804
  if (isSourceEnabled("cursor", "skills")) {
16694
- all.push({ path: join32(cursorHomeDir(), "skills"), tool: "cursor" });
16695
- all.push({ path: join32(cursorHomeDir(), "skills-cursor"), tool: "cursor", readonly: true });
16805
+ all.push({ path: join33(cursorHomeDir(), "skills"), tool: "cursor" });
16806
+ all.push({ path: join33(cursorHomeDir(), "skills-cursor"), tool: "cursor", readonly: true });
16696
16807
  }
16697
16808
  return all.filter((r) => existsSync33(r.path));
16698
16809
  }
@@ -16737,7 +16848,7 @@ var init_skills_source = __esm({
16737
16848
  const codexPluginsDir = codexBackend.pluginsDir();
16738
16849
  if (existsSync33(codexPluginsDir)) {
16739
16850
  for (const pluginName of readdirSync25(codexPluginsDir)) {
16740
- const skillsDir = join32(codexPluginsDir, pluginName, "skills");
16851
+ const skillsDir = join33(codexPluginsDir, pluginName, "skills");
16741
16852
  if (!existsSync33(skillsDir))
16742
16853
  continue;
16743
16854
  yield* this.fromSkillRoot({ path: skillsDir, tool: "codex" }, pluginName);
@@ -16754,7 +16865,7 @@ var init_skills_source = __esm({
16754
16865
  for (const name of entries) {
16755
16866
  if (root.skipChildren?.includes(name))
16756
16867
  continue;
16757
- const skillDir = join32(root.path, name);
16868
+ const skillDir = join33(root.path, name);
16758
16869
  let st;
16759
16870
  try {
16760
16871
  st = statSync29(skillDir);
@@ -16763,12 +16874,12 @@ var init_skills_source = __esm({
16763
16874
  }
16764
16875
  if (!st.isDirectory())
16765
16876
  continue;
16766
- const skillMd = join32(skillDir, "SKILL.md");
16877
+ const skillMd = join33(skillDir, "SKILL.md");
16767
16878
  if (!existsSync33(skillMd))
16768
16879
  continue;
16769
16880
  let content;
16770
16881
  try {
16771
- content = readFileSync23(skillMd, "utf-8");
16882
+ content = readFileSync24(skillMd, "utf-8");
16772
16883
  } catch {
16773
16884
  continue;
16774
16885
  }
@@ -16782,7 +16893,7 @@ var init_skills_source = __esm({
16782
16893
  const subdirs = {};
16783
16894
  try {
16784
16895
  for (const sub of readdirSync25(skillDir)) {
16785
- const subPath = join32(skillDir, sub);
16896
+ const subPath = join33(skillDir, sub);
16786
16897
  try {
16787
16898
  const ss = statSync29(subPath);
16788
16899
  if (ss.isDirectory()) {
@@ -16831,7 +16942,7 @@ var init_skills_source = __esm({
16831
16942
  async parse(item) {
16832
16943
  if (!existsSync33(item.filePath))
16833
16944
  return [];
16834
- const content = readFileSync23(item.filePath, "utf-8");
16945
+ const content = readFileSync24(item.filePath, "utf-8");
16835
16946
  if (!content.trim())
16836
16947
  return [];
16837
16948
  return [{
@@ -16854,14 +16965,14 @@ var init_skills_source = __esm({
16854
16965
  });
16855
16966
 
16856
16967
  // ../engine/dist/parsers/mcps-source.js
16857
- import { existsSync as existsSync34, readdirSync as readdirSync26, readFileSync as readFileSync24, statSync as statSync30 } from "fs";
16968
+ import { existsSync as existsSync34, readdirSync as readdirSync26, readFileSync as readFileSync25, statSync as statSync30 } from "fs";
16858
16969
  import { homedir as homedir13 } from "os";
16859
- import { join as join33 } from "path";
16970
+ import { join as join34 } from "path";
16860
16971
  function readJson(path) {
16861
16972
  if (!existsSync34(path))
16862
16973
  return null;
16863
16974
  try {
16864
- const raw = readFileSync24(path, "utf-8");
16975
+ const raw = readFileSync25(path, "utf-8");
16865
16976
  const stripped = path.endsWith(".jsonc") ? raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1").replace(/,(\s*[}\]])/g, "$1") : raw;
16866
16977
  return JSON.parse(stripped);
16867
16978
  } catch {
@@ -16922,23 +17033,23 @@ var init_mcps_source = __esm({
16922
17033
  if (!isSourceEnabled("common", "mcps"))
16923
17034
  return;
16924
17035
  const home = homedir13();
16925
- yield* this.fromObject(readJson(join33(home, ".mcp.json"))?.mcpServers, {
17036
+ yield* this.fromObject(readJson(join34(home, ".mcp.json"))?.mcpServers, {
16926
17037
  tool: "claude",
16927
- filePath: join33(home, ".mcp.json"),
17038
+ filePath: join34(home, ".mcp.json"),
16928
17039
  scope: "global"
16929
17040
  });
16930
- yield* this.fromObject(readJson(join33(home, ".claude.json"))?.mcpServers, {
17041
+ yield* this.fromObject(readJson(join34(home, ".claude.json"))?.mcpServers, {
16931
17042
  tool: "claude",
16932
- filePath: join33(home, ".claude.json"),
17043
+ filePath: join34(home, ".claude.json"),
16933
17044
  scope: "user"
16934
17045
  });
16935
17046
  for (const path of [
16936
- join33(home, ".config", "opencode", "opencode.json"),
16937
- join33(home, ".config", "opencode", "opencode.jsonc"),
16938
- join33(home, ".config", "opencode", "config.json"),
16939
- join33(home, ".opencode", "opencode.json"),
16940
- join33(home, ".opencode", "opencode.jsonc"),
16941
- join33(home, ".opencode", "config.json")
17047
+ join34(home, ".config", "opencode", "opencode.json"),
17048
+ join34(home, ".config", "opencode", "opencode.jsonc"),
17049
+ join34(home, ".config", "opencode", "config.json"),
17050
+ join34(home, ".opencode", "opencode.json"),
17051
+ join34(home, ".opencode", "opencode.jsonc"),
17052
+ join34(home, ".opencode", "config.json")
16942
17053
  ]) {
16943
17054
  yield* this.fromObject(readJson(path)?.mcp, {
16944
17055
  tool: "opencode",
@@ -16953,13 +17064,13 @@ var init_mcps_source = __esm({
16953
17064
  filePath: geminiSettings,
16954
17065
  scope: "user"
16955
17066
  });
16956
- const cursorMcp = join33(cursorHomeDir(), "mcp.json");
17067
+ const cursorMcp = join34(cursorHomeDir(), "mcp.json");
16957
17068
  yield* this.fromObject(readJson(cursorMcp)?.mcpServers, {
16958
17069
  tool: "cursor",
16959
17070
  filePath: cursorMcp,
16960
17071
  scope: "user"
16961
17072
  });
16962
- const agyMcp = join33(home, ".gemini", "config", "mcp_config.json");
17073
+ const agyMcp = join34(home, ".gemini", "config", "mcp_config.json");
16963
17074
  yield* this.fromObject(readJson(agyMcp)?.mcpServers, {
16964
17075
  tool: "agy",
16965
17076
  filePath: agyMcp,
@@ -16969,7 +17080,7 @@ var init_mcps_source = __esm({
16969
17080
  const pluginsDir = codexBackend.pluginsDir();
16970
17081
  if (existsSync34(pluginsDir)) {
16971
17082
  for (const plugin of readdirSync26(pluginsDir)) {
16972
- const mcpPath = join33(pluginsDir, plugin, ".mcp.json");
17083
+ const mcpPath = join34(pluginsDir, plugin, ".mcp.json");
16973
17084
  yield* this.fromObject(readJson(mcpPath)?.mcpServers, {
16974
17085
  tool: "codex",
16975
17086
  filePath: mcpPath,
@@ -17023,7 +17134,7 @@ var init_mcps_source = __esm({
17023
17134
  return;
17024
17135
  let content;
17025
17136
  try {
17026
- content = readFileSync24(path, "utf-8");
17137
+ content = readFileSync25(path, "utf-8");
17027
17138
  } catch {
17028
17139
  return;
17029
17140
  }
@@ -17192,8 +17303,8 @@ var init_toolkit_format = __esm({
17192
17303
  });
17193
17304
 
17194
17305
  // ../engine/dist/parsers/slash-commands-source.js
17195
- import { existsSync as existsSync35, readdirSync as readdirSync27, readFileSync as readFileSync25, statSync as statSync31 } from "fs";
17196
- import { join as join34, basename as basename18 } from "path";
17306
+ import { existsSync as existsSync35, readdirSync as readdirSync27, readFileSync as readFileSync26, statSync as statSync31 } from "fs";
17307
+ import { join as join35, basename as basename18 } from "path";
17197
17308
  function rebuildBody2(content) {
17198
17309
  const { text, redacted } = redactInlineSecrets(content);
17199
17310
  const out = {};
@@ -17251,7 +17362,7 @@ var init_slash_commands_source = __esm({
17251
17362
  if (!entry.isDirectory() || entry.name.startsWith("."))
17252
17363
  continue;
17253
17364
  const projectPath = resolveProjectDirName(entry.name);
17254
- const cmdDir = join34(projectPath, ".claude", "commands");
17365
+ const cmdDir = join35(projectPath, ".claude", "commands");
17255
17366
  if (existsSync35(cmdDir))
17256
17367
  roots.push({ path: cmdDir, tool: "claude", scope: "project", projectPath, format: "md" });
17257
17368
  }
@@ -17260,7 +17371,7 @@ var init_slash_commands_source = __esm({
17260
17371
  }
17261
17372
  }
17262
17373
  if (isSourceEnabled("agy", "commands")) {
17263
- roots.push({ path: join34(agyBackend.homeDir(), "commands"), tool: "agy", scope: "user", projectPath: "", format: "toml" });
17374
+ roots.push({ path: join35(agyBackend.homeDir(), "commands"), tool: "agy", scope: "user", projectPath: "", format: "toml" });
17264
17375
  roots.push({ path: agyBackend.sharedCommandsDir(), tool: "agy", scope: "user", projectPath: "", format: "toml" });
17265
17376
  }
17266
17377
  if (isSourceEnabled("opencode", "commands")) {
@@ -17270,7 +17381,7 @@ var init_slash_commands_source = __esm({
17270
17381
  roots.push({ path: codexBackend.promptsDir(), tool: "codex", scope: "user", projectPath: "", format: "md" });
17271
17382
  }
17272
17383
  if (isSourceEnabled("cursor", "commands")) {
17273
- roots.push({ path: join34(cursorHomeDir(), "commands"), tool: "cursor", scope: "user", projectPath: "", format: "md" });
17384
+ roots.push({ path: join35(cursorHomeDir(), "commands"), tool: "cursor", scope: "user", projectPath: "", format: "md" });
17274
17385
  }
17275
17386
  return roots;
17276
17387
  }
@@ -17284,10 +17395,10 @@ var init_slash_commands_source = __esm({
17284
17395
  continue;
17285
17396
  }
17286
17397
  for (const file of files) {
17287
- const filePath = join34(root.path, file);
17398
+ const filePath = join35(root.path, file);
17288
17399
  try {
17289
17400
  const stat3 = statSync31(filePath);
17290
- const content = readFileSync25(filePath, "utf-8");
17401
+ const content = readFileSync26(filePath, "utf-8");
17291
17402
  const parsed = parseCommand(content, basename18(file, ext), root.format);
17292
17403
  yield {
17293
17404
  id: `${root.tool}_cmd_${root.scope}_${parsed.name}`,
@@ -17316,7 +17427,7 @@ var init_slash_commands_source = __esm({
17316
17427
  async parse(item) {
17317
17428
  if (!existsSync35(item.filePath))
17318
17429
  return [];
17319
- const content = readFileSync25(item.filePath, "utf-8");
17430
+ const content = readFileSync26(item.filePath, "utf-8");
17320
17431
  if (!content.trim())
17321
17432
  return [];
17322
17433
  return [{
@@ -17339,8 +17450,8 @@ var init_slash_commands_source = __esm({
17339
17450
  });
17340
17451
 
17341
17452
  // ../engine/dist/parsers/subagents-source.js
17342
- import { existsSync as existsSync36, readdirSync as readdirSync28, readFileSync as readFileSync26, statSync as statSync32 } from "fs";
17343
- import { join as join35, basename as basename19 } from "path";
17453
+ import { existsSync as existsSync36, readdirSync as readdirSync28, readFileSync as readFileSync27, statSync as statSync32 } from "fs";
17454
+ import { join as join36, basename as basename19 } from "path";
17344
17455
  function rebuildBody3(content) {
17345
17456
  const { text, redacted } = redactInlineSecrets(content);
17346
17457
  const out = {};
@@ -17400,7 +17511,7 @@ var init_subagents_source = __esm({
17400
17511
  if (!entry.isDirectory() || entry.name.startsWith("."))
17401
17512
  continue;
17402
17513
  const projectPath = resolveProjectDirName(entry.name);
17403
- const agentDir = join35(projectPath, ".claude", "agents");
17514
+ const agentDir = join36(projectPath, ".claude", "agents");
17404
17515
  if (existsSync36(agentDir))
17405
17516
  roots.push({ path: agentDir, tool: "claude", scope: "project", projectPath, format: "md" });
17406
17517
  }
@@ -17409,7 +17520,7 @@ var init_subagents_source = __esm({
17409
17520
  }
17410
17521
  }
17411
17522
  if (isSourceEnabled("agy", "agents")) {
17412
- roots.push({ path: join35(agyBackend.homeDir(), "agents"), tool: "agy", scope: "user", projectPath: "", format: "md" });
17523
+ roots.push({ path: join36(agyBackend.homeDir(), "agents"), tool: "agy", scope: "user", projectPath: "", format: "md" });
17413
17524
  roots.push({ path: agyBackend.sharedAgentsDir(), tool: "agy", scope: "user", projectPath: "", format: "md" });
17414
17525
  }
17415
17526
  if (isSourceEnabled("opencode", "agents")) {
@@ -17419,7 +17530,7 @@ var init_subagents_source = __esm({
17419
17530
  roots.push({ path: codexBackend.agentsDir(), tool: "codex", scope: "user", projectPath: "", format: "toml" });
17420
17531
  }
17421
17532
  if (isSourceEnabled("cursor", "agents")) {
17422
- roots.push({ path: join35(cursorHomeDir(), "agents"), tool: "cursor", scope: "user", projectPath: "", format: "md" });
17533
+ roots.push({ path: join36(cursorHomeDir(), "agents"), tool: "cursor", scope: "user", projectPath: "", format: "md" });
17423
17534
  }
17424
17535
  return roots;
17425
17536
  }
@@ -17433,10 +17544,10 @@ var init_subagents_source = __esm({
17433
17544
  continue;
17434
17545
  }
17435
17546
  for (const file of files) {
17436
- const filePath = join35(root.path, file);
17547
+ const filePath = join36(root.path, file);
17437
17548
  try {
17438
17549
  const stat3 = statSync32(filePath);
17439
- const content = readFileSync26(filePath, "utf-8");
17550
+ const content = readFileSync27(filePath, "utf-8");
17440
17551
  const parsed = parseAgent(content, basename19(file, ext), root.format);
17441
17552
  yield {
17442
17553
  id: `${root.tool}_agent_${root.scope}_${parsed.name}`,
@@ -17466,7 +17577,7 @@ var init_subagents_source = __esm({
17466
17577
  async parse(item) {
17467
17578
  if (!existsSync36(item.filePath))
17468
17579
  return [];
17469
- const content = readFileSync26(item.filePath, "utf-8");
17580
+ const content = readFileSync27(item.filePath, "utf-8");
17470
17581
  if (!content.trim())
17471
17582
  return [];
17472
17583
  return [{
@@ -17489,13 +17600,13 @@ var init_subagents_source = __esm({
17489
17600
  });
17490
17601
 
17491
17602
  // ../engine/dist/parsers/hooks-source.js
17492
- import { existsSync as existsSync37, readFileSync as readFileSync27, readdirSync as readdirSync29, statSync as statSync33 } from "fs";
17493
- import { join as join36 } from "path";
17603
+ import { existsSync as existsSync37, readFileSync as readFileSync28, readdirSync as readdirSync29, statSync as statSync33 } from "fs";
17604
+ import { join as join37 } from "path";
17494
17605
  function readJson2(path) {
17495
17606
  if (!existsSync37(path))
17496
17607
  return null;
17497
17608
  try {
17498
- return JSON.parse(readFileSync27(path, "utf-8"));
17609
+ return JSON.parse(readFileSync28(path, "utf-8"));
17499
17610
  } catch {
17500
17611
  return null;
17501
17612
  }
@@ -17520,7 +17631,7 @@ var init_hooks_source = __esm({
17520
17631
  if (!entry.isDirectory() || entry.name.startsWith("."))
17521
17632
  continue;
17522
17633
  const projectPath = resolveProjectDirName(entry.name);
17523
- const settingsPath2 = join36(projectPath, ".claude", "settings.json");
17634
+ const settingsPath2 = join37(projectPath, ".claude", "settings.json");
17524
17635
  if (existsSync37(settingsPath2)) {
17525
17636
  yield* this.fromSettings(settingsPath2, "project", projectPath);
17526
17637
  }
@@ -17612,14 +17723,14 @@ var init_hooks_source = __esm({
17612
17723
  });
17613
17724
 
17614
17725
  // ../engine/dist/parsers/plugins-source.js
17615
- import { existsSync as existsSync38, readFileSync as readFileSync28, readdirSync as readdirSync30, statSync as statSync34 } from "fs";
17726
+ import { existsSync as existsSync38, readFileSync as readFileSync29, readdirSync as readdirSync30, statSync as statSync34 } from "fs";
17616
17727
  import { homedir as homedir14 } from "os";
17617
- import { join as join37 } from "path";
17728
+ import { join as join38 } from "path";
17618
17729
  function readJson3(path) {
17619
17730
  if (!existsSync38(path))
17620
17731
  return null;
17621
17732
  try {
17622
- return JSON.parse(readFileSync28(path, "utf-8"));
17733
+ return JSON.parse(readFileSync29(path, "utf-8"));
17623
17734
  } catch {
17624
17735
  return null;
17625
17736
  }
@@ -17679,7 +17790,7 @@ var init_plugins_source = __esm({
17679
17790
  entries = [];
17680
17791
  }
17681
17792
  for (const name of entries) {
17682
- const dir = join37(gemRoot, name);
17793
+ const dir = join38(gemRoot, name);
17683
17794
  let st;
17684
17795
  try {
17685
17796
  st = statSync34(dir);
@@ -17688,7 +17799,7 @@ var init_plugins_source = __esm({
17688
17799
  }
17689
17800
  if (!st.isDirectory())
17690
17801
  continue;
17691
- const manifestPath = join37(dir, "gemini-extension.json");
17802
+ const manifestPath = join38(dir, "gemini-extension.json");
17692
17803
  const manifest = readJson3(manifestPath);
17693
17804
  if (!manifest || typeof manifest !== "object")
17694
17805
  continue;
@@ -17727,7 +17838,7 @@ var init_plugins_source = __esm({
17727
17838
  entries = [];
17728
17839
  }
17729
17840
  for (const name of entries) {
17730
- const pluginDir = join37(codexPluginsRoot, name);
17841
+ const pluginDir = join38(codexPluginsRoot, name);
17731
17842
  let st;
17732
17843
  try {
17733
17844
  st = statSync34(pluginDir);
@@ -17736,11 +17847,11 @@ var init_plugins_source = __esm({
17736
17847
  }
17737
17848
  if (!st.isDirectory())
17738
17849
  continue;
17739
- const appJson = readJson3(join37(pluginDir, ".codex-plugin", ".app.json"));
17740
- const pluginJson = readJson3(join37(pluginDir, ".codex-plugin", "plugin.json"));
17850
+ const appJson = readJson3(join38(pluginDir, ".codex-plugin", ".app.json"));
17851
+ const pluginJson = readJson3(join38(pluginDir, ".codex-plugin", "plugin.json"));
17741
17852
  const manifest = pluginJson || appJson || {};
17742
17853
  let mcpServers = [];
17743
- const mcpJson = readJson3(join37(pluginDir, ".mcp.json"));
17854
+ const mcpJson = readJson3(join38(pluginDir, ".mcp.json"));
17744
17855
  if (mcpJson?.mcpServers)
17745
17856
  mcpServers = Object.keys(mcpJson.mcpServers);
17746
17857
  yield {
@@ -17748,7 +17859,7 @@ var init_plugins_source = __esm({
17748
17859
  sourceType: "plugin",
17749
17860
  title: manifest.name || name,
17750
17861
  projectPath: "",
17751
- filePath: join37(pluginDir, ".codex-plugin"),
17862
+ filePath: join38(pluginDir, ".codex-plugin"),
17752
17863
  mtime: st.mtimeMs,
17753
17864
  contentPreview: (manifest.description || "").slice(0, 300),
17754
17865
  extra: {
@@ -17908,8 +18019,8 @@ __export(sync_ledger_exports, {
17908
18019
  syncMode: () => syncMode,
17909
18020
  unackedCursorAttemptCount: () => unackedCursorAttemptCount
17910
18021
  });
17911
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync5, renameSync as renameSync4, mkdirSync as mkdirSync6 } from "node:fs";
17912
- import { join as join38, dirname as dirname15 } from "node:path";
18022
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync6, renameSync as renameSync4, mkdirSync as mkdirSync7 } from "node:fs";
18023
+ import { join as join39, dirname as dirname16 } from "node:path";
17913
18024
  function toRow(entry) {
17914
18025
  if (entry === void 0) return void 0;
17915
18026
  return typeof entry === "number" ? { m: entry, v: 0 } : entry;
@@ -17920,7 +18031,7 @@ function ledgerFilePath() {
17920
18031
  function load() {
17921
18032
  if (cache3) return cache3;
17922
18033
  try {
17923
- const parsed = JSON.parse(readFileSync29(ledgerPath(), "utf-8"));
18034
+ const parsed = JSON.parse(readFileSync30(ledgerPath(), "utf-8"));
17924
18035
  cache3 = parsed && typeof parsed === "object" ? parsed : {};
17925
18036
  } catch {
17926
18037
  cache3 = {};
@@ -17929,9 +18040,9 @@ function load() {
17929
18040
  }
17930
18041
  function persistNow(data) {
17931
18042
  const path = ledgerPath();
17932
- mkdirSync6(dirname15(path), { recursive: true });
18043
+ mkdirSync7(dirname16(path), { recursive: true });
17933
18044
  const tmp = `${path}.tmp`;
17934
- writeFileSync5(tmp, JSON.stringify(data));
18045
+ writeFileSync6(tmp, JSON.stringify(data));
17935
18046
  renameSync4(tmp, path);
17936
18047
  pendingWrite = false;
17937
18048
  }
@@ -17966,10 +18077,10 @@ function pruneLedgerTargets(configuredServers) {
17966
18077
  try {
17967
18078
  let prior = {};
17968
18079
  try {
17969
- prior = JSON.parse(readFileSync29(side, "utf-8"));
18080
+ prior = JSON.parse(readFileSync30(side, "utf-8"));
17970
18081
  } catch {
17971
18082
  }
17972
- writeFileSync5(side, JSON.stringify({ ...prior, ...orphans }));
18083
+ writeFileSync6(side, JSON.stringify({ ...prior, ...orphans }));
17973
18084
  } catch {
17974
18085
  for (const n of names) data[n] = orphans[n];
17975
18086
  return 0;
@@ -17990,7 +18101,7 @@ function flushLedger() {
17990
18101
  function loadItemVersionsAll() {
17991
18102
  if (itemVersionsCache) return itemVersionsCache;
17992
18103
  try {
17993
- const parsed = JSON.parse(readFileSync29(itemVersionsPath(), "utf-8"));
18104
+ const parsed = JSON.parse(readFileSync30(itemVersionsPath(), "utf-8"));
17994
18105
  itemVersionsCache = parsed && typeof parsed === "object" ? parsed : {};
17995
18106
  } catch {
17996
18107
  itemVersionsCache = {};
@@ -18004,9 +18115,9 @@ function saveItemVersions(server, versions) {
18004
18115
  const all = loadItemVersionsAll();
18005
18116
  all[server] = { ...all[server] || {}, ...versions };
18006
18117
  const path = itemVersionsPath();
18007
- mkdirSync6(dirname15(path), { recursive: true });
18118
+ mkdirSync7(dirname16(path), { recursive: true });
18008
18119
  const tmp = `${path}.tmp`;
18009
- writeFileSync5(tmp, JSON.stringify(all));
18120
+ writeFileSync6(tmp, JSON.stringify(all));
18010
18121
  renameSync4(tmp, path);
18011
18122
  }
18012
18123
  function getSyncedMtimes(server) {
@@ -18084,7 +18195,7 @@ function markFieldCoverage(server, field, rows) {
18084
18195
  function loadReconcile() {
18085
18196
  if (reconcileCache) return reconcileCache;
18086
18197
  try {
18087
- const parsed = JSON.parse(readFileSync29(reconcilePath(), "utf-8"));
18198
+ const parsed = JSON.parse(readFileSync30(reconcilePath(), "utf-8"));
18088
18199
  reconcileCache = parsed && typeof parsed === "object" ? parsed : {};
18089
18200
  } catch {
18090
18201
  reconcileCache = {};
@@ -18093,9 +18204,9 @@ function loadReconcile() {
18093
18204
  }
18094
18205
  function persistReconcile(data) {
18095
18206
  const path = reconcilePath();
18096
- mkdirSync6(dirname15(path), { recursive: true });
18207
+ mkdirSync7(dirname16(path), { recursive: true });
18097
18208
  const tmp = `${path}.tmp`;
18098
- writeFileSync5(tmp, JSON.stringify(data));
18209
+ writeFileSync6(tmp, JSON.stringify(data));
18099
18210
  renameSync4(tmp, path);
18100
18211
  }
18101
18212
  function stateFor(server) {
@@ -18153,15 +18264,15 @@ var init_sync_ledger = __esm({
18153
18264
  "use strict";
18154
18265
  init_paths();
18155
18266
  init_extractor_version();
18156
- ledgerPath = () => join38(getDataDir(), "sync-ledger.json");
18267
+ ledgerPath = () => join39(getDataDir(), "sync-ledger.json");
18157
18268
  cache3 = null;
18158
18269
  LEDGER_FLUSH_MS = 2e3;
18159
18270
  pendingWrite = false;
18160
18271
  flushTimer = null;
18161
- itemVersionsPath = () => join38(getDataDir(), "item-versions.json");
18272
+ itemVersionsPath = () => join39(getDataDir(), "item-versions.json");
18162
18273
  itemVersionsCache = null;
18163
18274
  unackedCursorAttempts = 0;
18164
- reconcilePath = () => join38(getDataDir(), "sync-field-reconcile.json");
18275
+ reconcilePath = () => join39(getDataDir(), "sync-field-reconcile.json");
18165
18276
  reconcileCache = null;
18166
18277
  }
18167
18278
  });
@@ -18192,11 +18303,11 @@ var init_sync_fields = __esm({
18192
18303
  });
18193
18304
 
18194
18305
  // ../engine/dist/core/index-lock.js
18195
- import { existsSync as existsSync39, mkdirSync as mkdirSync7, openSync as openSync7, writeFileSync as writeFileSync6, readFileSync as readFileSync30, rmSync as rmSync4, statSync as statSync35, closeSync as closeSync7 } from "fs";
18196
- import { dirname as dirname16, join as join39 } from "path";
18306
+ import { existsSync as existsSync39, mkdirSync as mkdirSync8, openSync as openSync7, writeFileSync as writeFileSync7, readFileSync as readFileSync31, rmSync as rmSync4, statSync as statSync35, closeSync as closeSync7 } from "fs";
18307
+ import { dirname as dirname17, join as join40 } from "path";
18197
18308
  function acquireIndexLock(opts) {
18198
18309
  const lockPath = lockFilePath();
18199
- mkdirSync7(dirname16(lockPath), { recursive: true });
18310
+ mkdirSync8(dirname17(lockPath), { recursive: true });
18200
18311
  if (existsSync39(lockPath)) {
18201
18312
  if (!stealStaleLock(lockPath, opts.staleAfterMs ?? 10 * 6e4)) {
18202
18313
  return null;
@@ -18212,7 +18323,7 @@ function acquireIndexLock(opts) {
18212
18323
  }
18213
18324
  const holder = { pid: process.pid, kind: opts.kind, startedAt: Date.now() };
18214
18325
  try {
18215
- writeFileSync6(fd, JSON.stringify(holder));
18326
+ writeFileSync7(fd, JSON.stringify(holder));
18216
18327
  } finally {
18217
18328
  closeSync7(fd);
18218
18329
  }
@@ -18224,7 +18335,7 @@ function acquireIndexLock(opts) {
18224
18335
  return;
18225
18336
  released = true;
18226
18337
  try {
18227
- const current = JSON.parse(readFileSync30(lockPath, "utf-8"));
18338
+ const current = JSON.parse(readFileSync31(lockPath, "utf-8"));
18228
18339
  if (current.pid !== holder.pid || current.startedAt !== holder.startedAt)
18229
18340
  return;
18230
18341
  } catch {
@@ -18238,7 +18349,7 @@ function acquireIndexLock(opts) {
18238
18349
  };
18239
18350
  }
18240
18351
  function lockFilePath() {
18241
- return join39(getIndexDir(), ".compact.lock");
18352
+ return join40(getIndexDir(), ".compact.lock");
18242
18353
  }
18243
18354
  function stealStaleLock(lockPath, staleAfterMs) {
18244
18355
  try {
@@ -18246,7 +18357,7 @@ function stealStaleLock(lockPath, staleAfterMs) {
18246
18357
  const age = Date.now() - s.mtimeMs;
18247
18358
  let payload;
18248
18359
  try {
18249
- payload = JSON.parse(readFileSync30(lockPath, "utf-8"));
18360
+ payload = JSON.parse(readFileSync31(lockPath, "utf-8"));
18250
18361
  } catch {
18251
18362
  rmSync4(lockPath, { force: true });
18252
18363
  return true;
@@ -18284,7 +18395,7 @@ var init_index_lock = __esm({
18284
18395
  });
18285
18396
 
18286
18397
  // src/http.ts
18287
- import { readFileSync as readFileSync31 } from "node:fs";
18398
+ import { readFileSync as readFileSync32 } from "node:fs";
18288
18399
  function clientHeaders() {
18289
18400
  return {
18290
18401
  "x-chat-recall-cli": CLI_VERSION,
@@ -18302,10 +18413,10 @@ var init_http = __esm({
18302
18413
  "use strict";
18303
18414
  CLI_VERSION = "0.0.0";
18304
18415
  try {
18305
- CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18416
+ CLI_VERSION = JSON.parse(readFileSync32(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18306
18417
  } catch {
18307
18418
  try {
18308
- if (true) CLI_VERSION = "0.6.8";
18419
+ if (true) CLI_VERSION = "0.6.9";
18309
18420
  } catch {
18310
18421
  }
18311
18422
  }
@@ -18330,27 +18441,6 @@ var init_redact_deep = __esm({
18330
18441
  }
18331
18442
  });
18332
18443
 
18333
- // ../engine/dist/core/session-multi-tool.js
18334
- function extractTurnsAny(sessionId, opts = {}) {
18335
- const backend = getBackendForId(sessionId);
18336
- if (!backend)
18337
- return { sessionId, found: false, turns: [], startMs: 0, endMs: 0 };
18338
- return backend.extractTurns(sessionId, opts);
18339
- }
18340
- function replaySessionAny(sessionId) {
18341
- const backend = getBackendForId(sessionId);
18342
- if (!backend)
18343
- return { sessionId, found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
18344
- return backend.replay(sessionId);
18345
- }
18346
- var init_session_multi_tool = __esm({
18347
- "../engine/dist/core/session-multi-tool.js"() {
18348
- "use strict";
18349
- init_tool_backend();
18350
- init_backends();
18351
- }
18352
- });
18353
-
18354
18444
  // src/derived.ts
18355
18445
  function collectDerivedRows(ref, mtime, count, maxRowBytes) {
18356
18446
  const id = ref.prefixedId;
@@ -18821,7 +18911,7 @@ var init_telemetry = __esm({
18821
18911
  init_sync_client();
18822
18912
  cliVersion = () => {
18823
18913
  try {
18824
- return true ? "0.6.8" : "dev";
18914
+ return true ? "0.6.9" : "dev";
18825
18915
  } catch {
18826
18916
  return "dev";
18827
18917
  }
@@ -18831,60 +18921,6 @@ var init_telemetry = __esm({
18831
18921
  }
18832
18922
  });
18833
18923
 
18834
- // ../engine/dist/core/collector-health.js
18835
- import { readFileSync as readFileSync32, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "node:fs";
18836
- import { join as join40, dirname as dirname17 } from "node:path";
18837
- function collectorHealthPath() {
18838
- return healthPath();
18839
- }
18840
- function readCollectorHealth() {
18841
- try {
18842
- const h = JSON.parse(readFileSync32(healthPath(), "utf-8"));
18843
- return h && h.v === 1 && typeof h.updatedAt === "number" ? h : null;
18844
- } catch {
18845
- return null;
18846
- }
18847
- }
18848
- function writeCollectorHealth(h) {
18849
- try {
18850
- const p = healthPath();
18851
- mkdirSync8(dirname17(p), { recursive: true });
18852
- writeFileSync7(p, JSON.stringify(h));
18853
- } catch {
18854
- }
18855
- }
18856
- function updateCollectorHealth(patch) {
18857
- const prior = readCollectorHealth();
18858
- writeCollectorHealth({
18859
- ...prior,
18860
- v: 1,
18861
- updatedAt: Date.now(),
18862
- startedAt: prior?.startedAt ?? Date.now(),
18863
- restartsLastHour: prior?.restartsLastHour ?? 0,
18864
- targets: prior?.targets ?? {},
18865
- ...patch
18866
- });
18867
- }
18868
- function reportWalkProgress(p) {
18869
- const now = Date.now();
18870
- const boundary = p.complete || p.done === 0;
18871
- if (!boundary && now - lastProgressWrite < PROGRESS_WRITE_INTERVAL_MS)
18872
- return;
18873
- lastProgressWrite = now;
18874
- updateCollectorHealth({ progress: p });
18875
- }
18876
- var healthPath, PROGRESS_WRITE_INTERVAL_MS, lastProgressWrite, STALE_AFTER_MS;
18877
- var init_collector_health = __esm({
18878
- "../engine/dist/core/collector-health.js"() {
18879
- "use strict";
18880
- init_paths();
18881
- healthPath = () => join40(getDataDir(), "collector-health.json");
18882
- PROGRESS_WRITE_INTERVAL_MS = 2e3;
18883
- lastProgressWrite = 0;
18884
- STALE_AFTER_MS = 30 * 6e4;
18885
- }
18886
- });
18887
-
18888
18924
  // src/server-probe.ts
18889
18925
  var server_probe_exports = {};
18890
18926
  __export(server_probe_exports, {
@@ -19571,10 +19607,11 @@ async function syncSessions(opts = {}) {
19571
19607
  };
19572
19608
  continue;
19573
19609
  }
19610
+ acceptedUploads.set(cred.serverUrl, 0);
19574
19611
  try {
19575
19612
  const r = await syncToTarget(cred, opts);
19576
19613
  noteTargetSuccess(cred.serverUrl);
19577
- perTarget[cred.serverUrl] = { ok: true };
19614
+ perTarget[cred.serverUrl] = { ok: true, accepted: acceptedUploads.get(cred.serverUrl) ?? 0 };
19578
19615
  agg = agg ? {
19579
19616
  uploaded: agg.uploaded + r.uploaded,
19580
19617
  skipped: agg.skipped + r.skipped,
@@ -19591,7 +19628,7 @@ async function syncSessions(opts = {}) {
19591
19628
  } catch (e) {
19592
19629
  const msg = e instanceof Error ? e.message : String(e);
19593
19630
  errors.push(`${cred.serverUrl}: ${msg}`);
19594
- perTarget[cred.serverUrl] = { ok: false, error: msg };
19631
+ perTarget[cred.serverUrl] = { ok: false, error: msg, accepted: acceptedUploads.get(cred.serverUrl) ?? 0 };
19595
19632
  const next = noteTargetFailure(cred.serverUrl, msg);
19596
19633
  const notice = breakerNotice(cred.serverUrl, next);
19597
19634
  if (notice) console.error(`[sync] ${notice}`);
@@ -19830,7 +19867,7 @@ async function syncToTargetInScope(cred, opts = {}) {
19830
19867
  return [];
19831
19868
  }
19832
19869
  });
19833
- const MAX_BATCH_BYTES = 4 * 1024 * 1024;
19870
+ const MAX_BATCH_BYTES = Number(process.env.CHAT_RECALL_MAX_BATCH_BYTES) || (isLocalHost(cred.serverUrl) ? 4 * 1024 * 1024 : 1024 * 1024);
19834
19871
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
19835
19872
  const explicitThrottle = typeof opts.throttleMs === "number" && opts.throttleMs > 0;
19836
19873
  const throttleMs = explicitThrottle ? opts.throttleMs : 0;
@@ -19865,6 +19902,7 @@ async function syncToTargetInScope(cred, opts = {}) {
19865
19902
  signal: ac.signal
19866
19903
  });
19867
19904
  if (res.ok) {
19905
+ acceptedUploads.set(cred.serverUrl, (acceptedUploads.get(cred.serverUrl) ?? 0) + 1);
19868
19906
  const body2 = await res.json().catch(() => ({}));
19869
19907
  if (typeof body2.telemetry === "boolean") {
19870
19908
  setTelemetryEligible(base2, body2.telemetry);
@@ -19915,7 +19953,8 @@ async function syncToTargetInScope(cred, opts = {}) {
19915
19953
  if (attempt >= RETRY_DELAYS_MS.length) throw new Error(`sync failed after ${attempt + 1} attempts: HTTP ${res.status} ${text}`);
19916
19954
  const retryAfterS = Number(res.headers.get("retry-after"));
19917
19955
  const waitMs = res.status === 429 && retryAfterS > 0 ? retryAfterS * 1e3 : RETRY_DELAYS_MS[attempt];
19918
- console.error(`[sync] HTTP ${res.status} from ${base2} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
19956
+ const why = res.status === 524 ? ` \u2014 the proxy cut the request at its time limit; the batch is too big for one request` : "";
19957
+ console.error(`[sync] HTTP ${res.status} from ${base2}${why} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
19919
19958
  await sleep(waitMs);
19920
19959
  continue;
19921
19960
  } catch (err) {
@@ -20774,7 +20813,7 @@ async function syncIncremental(opts = {}) {
20774
20813
  lock.release();
20775
20814
  }
20776
20815
  }
20777
- var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, SYNC_RULES_REFUSAL, hashPath, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, FULL_BUILD_TAIL_BYTES, BATCH_SCAN_MAX_BYTES;
20816
+ var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, SYNC_RULES_REFUSAL, hashPath, acceptedUploads, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, FULL_BUILD_TAIL_BYTES, BATCH_SCAN_MAX_BYTES;
20778
20817
  var init_sync_client = __esm({
20779
20818
  "src/sync-client.ts"() {
20780
20819
  "use strict";
@@ -20819,7 +20858,7 @@ var init_sync_client = __esm({
20819
20858
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
20820
20859
  } catch {
20821
20860
  try {
20822
- if (true) ownVersion = "0.6.8";
20861
+ if (true) ownVersion = "0.6.9";
20823
20862
  } catch {
20824
20863
  }
20825
20864
  }
@@ -20838,6 +20877,7 @@ var init_sync_client = __esm({
20838
20877
  EMPTY_SYNC_CONFIG = { excludeTools: [], excludeProjects: [], excludeSources: [], approveSources: [] };
20839
20878
  SYNC_RULES_REFUSAL = /* @__PURE__ */ Symbol.for("chat-recall.syncRulesRefusal");
20840
20879
  hashPath = (p) => p ? "p_" + createHash8("sha256").update(p).digest("hex").slice(0, 12) : "";
20880
+ acceptedUploads = /* @__PURE__ */ new Map();
20841
20881
  MAX_DERIVED_ROW_BYTES = 2 * 1024 * 1024;
20842
20882
  isLocalHost = isPrivateHost;
20843
20883
  CAPS_TTL_MS = 60 * 6e4;
@@ -23582,6 +23622,7 @@ var init_service_installer = __esm({
23582
23622
  // auto-indexer/indexer.ts
23583
23623
  init_backends();
23584
23624
  init_paths();
23625
+ init_collector_health();
23585
23626
  init_settings();
23586
23627
  init_sync_client();
23587
23628
  init_http();
@@ -23903,7 +23944,7 @@ try {
23903
23944
  ownVersion2 = JSON.parse(readFileSync39(join48(dir, "../package.json"), "utf-8")).version || ownVersion2;
23904
23945
  } catch {
23905
23946
  try {
23906
- if (true) ownVersion2 = "0.6.8";
23947
+ if (true) ownVersion2 = "0.6.9";
23907
23948
  } catch {
23908
23949
  }
23909
23950
  }
@@ -24251,7 +24292,7 @@ async function runCollectorMigration(opts) {
24251
24292
  init_sync_ledger();
24252
24293
  init_collector_health();
24253
24294
  import { existsSync as existsSync49, readFileSync as readFileSync45, statSync as statSync44 } from "fs";
24254
- var CLI_VERSION3 = true ? "0.6.8" : "0.0.0";
24295
+ var CLI_VERSION3 = true ? "0.6.9" : "0.0.0";
24255
24296
  var DEBOUNCE_MS = 5e3;
24256
24297
  var CLAUDE_DIR = claudeBackend.projectsDir();
24257
24298
  var PLANS_DIR = claudeBackend.plansDir();
@@ -24288,7 +24329,7 @@ async function shipToServer(trigger) {
24288
24329
  );
24289
24330
  }
24290
24331
  for (const [server, outcome] of Object.entries(result.perTarget ?? {})) {
24291
- noteSyncOutcome(server, outcome.ok, outcome.error);
24332
+ noteSyncOutcome(server, outcome.ok, outcome.error, outcome.accepted ?? 0);
24292
24333
  }
24293
24334
  } catch (err) {
24294
24335
  const msg = err instanceof Error ? err.message : String(err);
@@ -24556,6 +24597,9 @@ setTimeout(() => {
24556
24597
  setInterval(() => {
24557
24598
  ticks.request("housekeeping", TICK_PRIORITY.housekeeping, shadowPruneTick);
24558
24599
  }, SHADOW_PRUNE_MS).unref();
24600
+ function _targetHealth() {
24601
+ return targetHealth;
24602
+ }
24559
24603
  function syncTargetUrls() {
24560
24604
  try {
24561
24605
  return loadAllCredentials().map((c) => c.serverUrl);
@@ -24563,17 +24607,8 @@ function syncTargetUrls() {
24563
24607
  return [];
24564
24608
  }
24565
24609
  }
24566
- function noteSyncOutcome(server, ok, err) {
24567
- const t = targetHealth[server] ?? { lastOkAt: null, failures: 0 };
24568
- if (ok) {
24569
- t.lastOkAt = Date.now();
24570
- t.failures = 0;
24571
- delete t.lastError;
24572
- } else {
24573
- t.failures += 1;
24574
- t.lastError = (err ?? "unknown error").slice(0, 160);
24575
- }
24576
- targetHealth[server] = t;
24610
+ function noteSyncOutcome(server, ok, err, accepted = 0) {
24611
+ targetHealth[server] = applySyncOutcome(targetHealth[server], { ok, accepted, error: err });
24577
24612
  publishHealth();
24578
24613
  }
24579
24614
  publishHealth();
@@ -24834,3 +24869,7 @@ process.on("exit", () => {
24834
24869
  } catch {
24835
24870
  }
24836
24871
  });
24872
+ export {
24873
+ _targetHealth,
24874
+ noteSyncOutcome
24875
+ };