chat-recall 0.7.18 → 0.7.19

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
@@ -152,6 +152,8 @@ function declaredOrigin(value) {
152
152
  function userRecordOrigin(obj, text) {
153
153
  if (obj.isSidechain === true)
154
154
  return "subagent";
155
+ if (obj.isCompactSummary === true)
156
+ return "compact-summary";
155
157
  const declared = declaredOrigin(obj.origin);
156
158
  if (declared && declared !== "human")
157
159
  return declared;
@@ -4816,6 +4818,7 @@ function defaultSourcesEnabled() {
4816
4818
  commands: true
4817
4819
  },
4818
4820
  cursor: { sessions: true, skills: true, agents: true, commands: true },
4821
+ hermes: { sessions: true },
4819
4822
  shared: { skills: true },
4820
4823
  common: { mcps: true, agentMd: true }
4821
4824
  };
@@ -4892,6 +4895,7 @@ function mergeSources(base3, partial) {
4892
4895
  codex: { ...base3.enabled.codex, ...partial.enabled?.codex ?? {} },
4893
4896
  agy: { ...base3.enabled.agy, ...inherited, ...partial.enabled?.agy ?? {} },
4894
4897
  cursor: { ...base3.enabled.cursor, ...partial.enabled?.cursor ?? {} },
4898
+ hermes: { ...base3.enabled.hermes, ...partial.enabled?.hermes ?? {} },
4895
4899
  shared: { ...base3.enabled.shared, ...partial.enabled?.shared ?? {} },
4896
4900
  common: { ...base3.enabled.common, ...partial.enabled?.common ?? {} }
4897
4901
  };
@@ -4903,6 +4907,7 @@ function mergeSources(base3, partial) {
4903
4907
  agyHome: partial.agyHome ?? base3.agyHome,
4904
4908
  cursorHome: partial.cursorHome ?? base3.cursorHome,
4905
4909
  cursorIdeHome: partial.cursorIdeHome ?? base3.cursorIdeHome,
4910
+ hermesHome: partial.hermesHome ?? base3.hermesHome,
4906
4911
  extraClaudeHomes: partial.extraClaudeHomes ?? base3.extraClaudeHomes,
4907
4912
  approvedHomes: partial.approvedHomes ?? base3.approvedHomes,
4908
4913
  declinedHomes: partial.declinedHomes ?? base3.declinedHomes,
@@ -5200,6 +5205,13 @@ function grandfatherLegacyHomes() {
5200
5205
  }
5201
5206
  return { seeded };
5202
5207
  }
5208
+ function defaultHermesHome() {
5209
+ if (process.platform === "win32") {
5210
+ const local = process.env.LOCALAPPDATA?.trim() || join3(homedir2(), "AppData", "Local");
5211
+ return join3(local, "hermes");
5212
+ }
5213
+ return join3(homedir2(), ".hermes");
5214
+ }
5203
5215
  function primaryHomes() {
5204
5216
  const home = homedir2();
5205
5217
  const env = process.env;
@@ -5216,7 +5228,8 @@ function primaryHomes() {
5216
5228
  env.CHAT_RECALL_CODEX_HOME || s?.codexHome || join3(home, ".codex"),
5217
5229
  env.CHAT_RECALL_AGY_HOME || s?.agyHome || join3(home, ".gemini", "antigravity-cli"),
5218
5230
  env.CHAT_RECALL_CURSOR_HOME || s?.cursorHome || join3(home, ".cursor"),
5219
- dirOf(opencodeDb)
5231
+ dirOf(opencodeDb),
5232
+ env.CHAT_RECALL_HERMES_HOME || s?.hermesHome || env.HERMES_HOME?.trim() || defaultHermesHome()
5220
5233
  ];
5221
5234
  return candidates2.map(normalizeHomePath);
5222
5235
  }
@@ -5326,6 +5339,9 @@ function geminiHomeDir() {
5326
5339
  function opencodeDbPath() {
5327
5340
  return process.env.CHAT_RECALL_OPENCODE_DB || sources().opencodeDbPath || join4(homedir3(), ".local", "share", "opencode", "opencode.db");
5328
5341
  }
5342
+ function hermesHomeDir() {
5343
+ return process.env.CHAT_RECALL_HERMES_HOME || sources().hermesHome || process.env.HERMES_HOME?.trim() || defaultHermesHome();
5344
+ }
5329
5345
  function codexHomeDir() {
5330
5346
  return process.env.CHAT_RECALL_CODEX_HOME || sources().codexHome || join4(homedir3(), ".codex");
5331
5347
  }
@@ -5427,6 +5443,22 @@ function opencodeDbPaths(opts = {}) {
5427
5443
  dbs.push(primary);
5428
5444
  return applyExclusions(dbs, opts.includeExcluded);
5429
5445
  }
5446
+ function hermesDbPaths(opts = {}) {
5447
+ const root = hermesHomeDir();
5448
+ const dbs = [join4(root, "state.db")];
5449
+ try {
5450
+ const profiles = join4(root, "profiles");
5451
+ for (const entry of readdirSync3(profiles, { withFileTypes: true })) {
5452
+ if (!entry.isDirectory())
5453
+ continue;
5454
+ const p = join4(profiles, entry.name, "state.db");
5455
+ if (existsSync4(p))
5456
+ dbs.push(p);
5457
+ }
5458
+ } catch {
5459
+ }
5460
+ return applyExclusions(dbs, opts.includeExcluded);
5461
+ }
5430
5462
  var sources, _sourceExclusionFilter;
5431
5463
  var init_tool_paths = __esm({
5432
5464
  "../engine/dist/core/tool-paths.js"() {
@@ -6405,7 +6437,7 @@ function canonicalEventsToMessages(events) {
6405
6437
  switch (e.kind) {
6406
6438
  case "user":
6407
6439
  cur = null;
6408
- messages.push({ line: e.line, role: "user", content: e.text ?? "", timestamp: e.tsIso });
6440
+ messages.push({ line: e.line, role: "user", content: e.text ?? "", timestamp: e.tsIso, ...e.origin ? { origin: e.origin } : {} });
6409
6441
  break;
6410
6442
  case "assistant_text":
6411
6443
  cur = { line: e.line, role: "assistant", content: e.text ?? "", timestamp: e.tsIso };
@@ -8082,6 +8114,71 @@ function readTailFromOffset(path, offset, maxBytes = TAIL_READ_MAX_BYTES) {
8082
8114
  }
8083
8115
  }
8084
8116
  }
8117
+ function readSourcesFromOffset(paths, offset, maxBytes = TAIL_READ_MAX_BYTES) {
8118
+ let start = 0;
8119
+ let linesBefore = 0;
8120
+ for (let i = 0; i < paths.length; i++) {
8121
+ const path = paths[i];
8122
+ let size = 0;
8123
+ try {
8124
+ size = statSync6(path).size;
8125
+ } catch {
8126
+ size = 0;
8127
+ }
8128
+ const last = i === paths.length - 1;
8129
+ if (!last && offset >= start + size) {
8130
+ const lines = linesBeforeOffset(path, size);
8131
+ if (lines === void 0)
8132
+ return { text: "", newOffset: offset };
8133
+ linesBefore += lines + (size > 0 && !endsWithNewline(path, size) ? 1 : 0);
8134
+ start += size;
8135
+ continue;
8136
+ }
8137
+ const local = offset - start;
8138
+ const read = readTailFromOffset(path, local, maxBytes);
8139
+ const shift = (r) => ({
8140
+ ...r,
8141
+ newOffset: start + r.newOffset,
8142
+ ...r.baseLine !== void 0 ? { baseLine: linesBefore + r.baseLine } : {}
8143
+ });
8144
+ if (last || read.newOffset > local || local >= size)
8145
+ return shift(read);
8146
+ if (size - local > maxBytes)
8147
+ return { text: "", newOffset: start + size, skippedBytes: size - local };
8148
+ let fd = null;
8149
+ try {
8150
+ fd = openSync5(path, "r");
8151
+ const text = readAt(fd, local, size - local).toString("utf-8");
8152
+ return shift({ text, newOffset: size, baseLine: linesBeforeOffset(path, local) });
8153
+ } catch {
8154
+ return { text: "", newOffset: offset };
8155
+ } finally {
8156
+ if (fd !== null) {
8157
+ try {
8158
+ closeSync5(fd);
8159
+ } catch {
8160
+ }
8161
+ }
8162
+ }
8163
+ }
8164
+ return { text: "", newOffset: offset };
8165
+ }
8166
+ function endsWithNewline(path, size) {
8167
+ let fd = null;
8168
+ try {
8169
+ fd = openSync5(path, "r");
8170
+ return readAt(fd, size - 1, 1)[0] === 10;
8171
+ } catch {
8172
+ return true;
8173
+ } finally {
8174
+ if (fd !== null) {
8175
+ try {
8176
+ closeSync5(fd);
8177
+ } catch {
8178
+ }
8179
+ }
8180
+ }
8181
+ }
8085
8182
  function findNewline(fd, from, size) {
8086
8183
  const block = Buffer.allocUnsafe(SKIP_SCAN_BYTES);
8087
8184
  let pos = from;
@@ -8998,27 +9095,45 @@ var init_claude2 = __esm({
8998
9095
  /**
8999
9096
  * True when this session exists in MORE THAN ONE home.
9000
9097
  *
9001
- * Such a session cannot be tail-appended: `fileSize()` is the SUM across
9002
- * copies while a byte offset addresses ONE file, so the two disagree by
9003
- * construction and the "tail" is either empty or the wrong bytes. The caller
9004
- * uses this to force a FULL sync, which ships the unioned container and is
9005
- * correct regardless of how the halves are arranged.
9006
- *
9007
9098
  * Measured on ec05f266: ~/.claude 1715 records, ~/.claude-work 205 records,
9008
- * zero overlap. An offset-based append against a 5846662-byte "size" that no
9009
- * single file has could only ever ship nonsense.
9099
+ * zero overlap. `fileSize()` is the sum across the copies, and
9100
+ * `readFromOffset()` reads them joined end to end, primary first.
9010
9101
  */
9011
9102
  spansMultipleSources(prefixedId) {
9012
9103
  return findSessionFiles(this.toRawId(prefixedId)).length > 1;
9013
9104
  }
9105
+ /**
9106
+ * The copies before the last one, each with its size. Claude Code writes to
9107
+ * the home of the profile that runs the session, so the live copy can grow,
9108
+ * appear, vanish or merge back into the primary. Each of those moves bytes
9109
+ * under an offset into the joined copies; this key changes with all of them.
9110
+ */
9111
+ sourceLayout(prefixedId) {
9112
+ const copies = findSessionFiles(this.toRawId(prefixedId));
9113
+ if (copies.length < 2)
9114
+ return "";
9115
+ return copies.slice(0, -1).map((c) => {
9116
+ let size = -1;
9117
+ try {
9118
+ size = statSync7(c.path).size;
9119
+ } catch {
9120
+ }
9121
+ return `${c.path}:${size}`;
9122
+ }).join("|");
9123
+ }
9124
+ /**
9125
+ * Session 8de13d0a had 31 MB in ~/.claude and 2 MB in ~/.claude-work. Over
9126
+ * the 24 MB build ceiling a sync ships the transcript in chunks read here,
9127
+ * and this returned no text for a session in two homes. Each sync shipped
9128
+ * nothing, and the server stayed three hours behind.
9129
+ */
9014
9130
  async readFromOffset(prefixedId, offset, maxBytes) {
9015
- const rawId = this.toRawId(prefixedId);
9016
- const copies = findSessionFiles(rawId);
9131
+ const copies = findSessionFiles(this.toRawId(prefixedId));
9017
9132
  if (copies.length === 0)
9018
9133
  return { text: "", newOffset: offset };
9019
- if (copies.length > 1)
9020
- return { text: "", newOffset: offset };
9021
- return readTailFromOffset(copies[0].path, offset, maxBytes);
9134
+ if (copies.length === 1)
9135
+ return readTailFromOffset(copies[0].path, offset, maxBytes);
9136
+ return readSourcesFromOffset(copies.map((c) => c.path), offset, maxBytes);
9022
9137
  }
9023
9138
  };
9024
9139
  claudeBackend = new ClaudeBackend();
@@ -12077,6 +12192,473 @@ var init_cursor = __esm({
12077
12192
  }
12078
12193
  });
12079
12194
 
12195
+ // ../engine/dist/core/backends/hermes.js
12196
+ import { existsSync as existsSync16 } from "fs";
12197
+ function firstUserPrompt(db, sessionId) {
12198
+ try {
12199
+ const row = db.prepare(`
12200
+ SELECT substr(content, 1, 400) AS text FROM messages
12201
+ WHERE session_id = ? AND role = 'user' AND active = 1 AND _compressed_summary = 0
12202
+ AND (display_kind IS NULL OR display_kind = 'steer')
12203
+ AND length(trim(content)) > 0
12204
+ ORDER BY id LIMIT 1
12205
+ `).get(sessionId);
12206
+ return row?.text ? flatString(row.text.slice(0, 200)) : "";
12207
+ } catch {
12208
+ return "";
12209
+ }
12210
+ }
12211
+ function messageRowsToEvents(rows) {
12212
+ const events = [];
12213
+ let line = 0;
12214
+ for (const r of rows) {
12215
+ line++;
12216
+ if (r.active === 0)
12217
+ continue;
12218
+ if (r.display_kind === "failed_turn" || r.display_kind === "hidden")
12219
+ continue;
12220
+ const ts = Math.round((r.timestamp || 0) * 1e3);
12221
+ const tsIso = ts ? new Date(ts).toISOString() : void 0;
12222
+ const text = (r.content ?? "").trim();
12223
+ if (r._compressed_summary === 1) {
12224
+ if (text)
12225
+ events.push({ kind: "summary", ts, tsIso, line, text });
12226
+ continue;
12227
+ }
12228
+ if (r.role === "user") {
12229
+ if (!text)
12230
+ continue;
12231
+ events.push({
12232
+ kind: "user",
12233
+ ts,
12234
+ tsIso,
12235
+ line,
12236
+ text,
12237
+ ...r.display_kind === "process_complete" ? { origin: "task-notification" } : {}
12238
+ });
12239
+ continue;
12240
+ }
12241
+ if (r.role === "assistant") {
12242
+ if (text)
12243
+ events.push({ kind: "assistant_text", ts, tsIso, line, text });
12244
+ for (const call2 of parseToolCalls(r.tool_calls)) {
12245
+ const toolName = call2.function?.name ?? "";
12246
+ const toolInput = parseArguments(call2.function?.arguments);
12247
+ const command = toolName === "terminal" && typeof toolInput?.command === "string" ? toolInput.command : void 0;
12248
+ events.push({
12249
+ kind: "tool_use",
12250
+ ts,
12251
+ tsIso,
12252
+ line,
12253
+ toolName,
12254
+ toolUseId: call2.id || call2.call_id || `${r.id}:${toolName}`,
12255
+ toolInput,
12256
+ ...command ? { command } : {}
12257
+ });
12258
+ }
12259
+ continue;
12260
+ }
12261
+ if (r.role === "tool") {
12262
+ const body = r.content ?? "";
12263
+ const { exitCode, isError } = toolResultStatus(body);
12264
+ events.push({
12265
+ kind: "tool_result",
12266
+ ts,
12267
+ tsIso,
12268
+ line,
12269
+ toolUseId: r.tool_call_id ?? void 0,
12270
+ toolName: r.tool_name ?? void 0,
12271
+ resultBody: body,
12272
+ resultIsError: isError,
12273
+ ...exitCode !== void 0 ? { resultExitCode: exitCode } : {},
12274
+ resultBytes: Buffer.byteLength(body)
12275
+ });
12276
+ }
12277
+ }
12278
+ return events;
12279
+ }
12280
+ function parseToolCalls(json) {
12281
+ if (!json)
12282
+ return [];
12283
+ try {
12284
+ const calls = JSON.parse(json);
12285
+ return Array.isArray(calls) ? calls : [];
12286
+ } catch {
12287
+ return [];
12288
+ }
12289
+ }
12290
+ function parseArguments(args) {
12291
+ if (typeof args !== "string")
12292
+ return args ?? {};
12293
+ try {
12294
+ return JSON.parse(args);
12295
+ } catch {
12296
+ return { raw: args };
12297
+ }
12298
+ }
12299
+ function toolResultStatus(body) {
12300
+ if (!body.startsWith("{"))
12301
+ return { isError: false };
12302
+ try {
12303
+ const r = JSON.parse(body);
12304
+ const exitCode = typeof r.exit_code === "number" ? r.exit_code : void 0;
12305
+ const hasError = r.error !== void 0 && r.error !== null && r.error !== "";
12306
+ return { exitCode, isError: hasError || exitCode !== void 0 && exitCode !== 0 };
12307
+ } catch {
12308
+ return { isError: false };
12309
+ }
12310
+ }
12311
+ var PREFIX5, MESSAGE_COLUMNS, ARCHIVED_SESSION_COLUMNS, SESSION_MTIME_SQL, HermesBackend, hermesBackend;
12312
+ var init_hermes = __esm({
12313
+ "../engine/dist/core/backends/hermes.js"() {
12314
+ "use strict";
12315
+ init_sqlite_reader();
12316
+ init_tool_paths();
12317
+ init_session_outcome();
12318
+ init_session_git();
12319
+ init_generic_engine();
12320
+ init_flat_string();
12321
+ PREFIX5 = "hermes_";
12322
+ MESSAGE_COLUMNS = "id, role, content, tool_calls, tool_call_id, tool_name, timestamp, display_kind, active, _compressed_summary";
12323
+ ARCHIVED_SESSION_COLUMNS = [
12324
+ "id",
12325
+ "source",
12326
+ "title",
12327
+ "model",
12328
+ "cwd",
12329
+ "git_branch",
12330
+ "git_repo_root",
12331
+ "parent_session_id",
12332
+ "started_at",
12333
+ "ended_at",
12334
+ "end_reason",
12335
+ "message_count",
12336
+ "tool_call_count",
12337
+ "input_tokens",
12338
+ "output_tokens",
12339
+ "cache_read_tokens",
12340
+ "cache_write_tokens",
12341
+ "reasoning_tokens",
12342
+ "estimated_cost_usd",
12343
+ "actual_cost_usd"
12344
+ ];
12345
+ SESSION_MTIME_SQL = `MAX(
12346
+ s.started_at,
12347
+ COALESCE(s.ended_at, 0),
12348
+ COALESCE(s.last_activity_at, 0),
12349
+ COALESCE((SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = s.id), 0)
12350
+ )`;
12351
+ HermesBackend = class {
12352
+ id = "hermes";
12353
+ idPrefix = PREFIX5;
12354
+ displayName = "Hermes";
12355
+ homeDir() {
12356
+ return hermesHomeDir();
12357
+ }
12358
+ /** Every Hermes database on this machine, the root profile first. */
12359
+ dbPaths() {
12360
+ return hermesDbPaths();
12361
+ }
12362
+ isAvailable() {
12363
+ return this.dbPaths().some((p) => existsSync16(p));
12364
+ }
12365
+ /** The database that holds this session. A session id is unique within a
12366
+ * database, and each named profile has its own. */
12367
+ dbPathFor(rawId) {
12368
+ const cached = this._dbForSession.get(rawId);
12369
+ if (cached)
12370
+ return cached;
12371
+ for (const path of this.dbPaths()) {
12372
+ const db = openSqliteReadonly(path);
12373
+ if (!db)
12374
+ continue;
12375
+ try {
12376
+ if (db.prepare("SELECT 1 AS ok FROM sessions WHERE id = ?").get(rawId)) {
12377
+ this._dbForSession.set(rawId, path);
12378
+ return path;
12379
+ }
12380
+ } catch {
12381
+ } finally {
12382
+ db.close();
12383
+ }
12384
+ }
12385
+ return null;
12386
+ }
12387
+ _dbForSession = /* @__PURE__ */ new Map();
12388
+ /** Tests, and anything that adds a profile mid-process. */
12389
+ _clearDbRouting() {
12390
+ this._dbForSession.clear();
12391
+ }
12392
+ // ── ID handling ────────────────────────────────────────────────
12393
+ matchesId(id) {
12394
+ return id.startsWith(PREFIX5);
12395
+ }
12396
+ toRawId(id) {
12397
+ return id.startsWith(PREFIX5) ? id.slice(PREFIX5.length) : id;
12398
+ }
12399
+ toPrefixedId(rawId) {
12400
+ return rawId.startsWith(PREFIX5) ? rawId : PREFIX5 + rawId;
12401
+ }
12402
+ // ── Location ───────────────────────────────────────────────────
12403
+ findSession(id) {
12404
+ const rawId = this.toRawId(id);
12405
+ const path = this.dbPathFor(rawId);
12406
+ if (!path)
12407
+ return null;
12408
+ const db = openSqliteReadonly(path);
12409
+ if (!db)
12410
+ return null;
12411
+ try {
12412
+ const row = db.prepare(`
12413
+ SELECT s.cwd, s.git_repo_root, ${SESSION_MTIME_SQL} AS mtime
12414
+ FROM sessions s WHERE s.id = ?
12415
+ `).get(rawId);
12416
+ if (!row)
12417
+ return null;
12418
+ return {
12419
+ path,
12420
+ format: "sqlite",
12421
+ projectDir: "",
12422
+ projectPath: row.git_repo_root || row.cwd || "",
12423
+ mtime: Math.round((row.mtime || 0) * 1e3)
12424
+ };
12425
+ } catch {
12426
+ return null;
12427
+ } finally {
12428
+ db.close();
12429
+ }
12430
+ }
12431
+ listSessions(opts = {}) {
12432
+ const byId = /* @__PURE__ */ new Map();
12433
+ for (const dbPath of this.dbPaths()) {
12434
+ if (!existsSync16(dbPath))
12435
+ continue;
12436
+ for (const ref of this.listSessionsInDb(dbPath, opts)) {
12437
+ const prior = byId.get(ref.rawId);
12438
+ if (!prior || ref.mtime > prior.mtime)
12439
+ byId.set(ref.rawId, ref);
12440
+ }
12441
+ }
12442
+ const all = [...byId.values()].sort((a, b) => b.mtime - a.mtime);
12443
+ return opts.limit ? all.slice(0, opts.limit) : all;
12444
+ }
12445
+ listSessionsInDb(dbPath, opts) {
12446
+ const cutoffSec = (opts.sinceMs ?? 0) / 1e3;
12447
+ const filter = opts.projectFilter?.toLowerCase();
12448
+ const db = openSqliteReadonly(dbPath);
12449
+ if (!db)
12450
+ return [];
12451
+ try {
12452
+ const rows = db.prepare(`
12453
+ SELECT * FROM (
12454
+ SELECT s.id, s.cwd, s.git_repo_root, s.started_at, s.message_count,
12455
+ ${SESSION_MTIME_SQL} AS mtime
12456
+ FROM sessions s
12457
+ ) WHERE mtime >= ? ORDER BY mtime DESC
12458
+ `).all(cutoffSec);
12459
+ const out = [];
12460
+ for (const row of rows) {
12461
+ const projectPath = row.git_repo_root || row.cwd || "";
12462
+ if (filter && !projectPath.toLowerCase().includes(filter))
12463
+ continue;
12464
+ const mtime = Math.round(row.mtime * 1e3);
12465
+ this._dbForSession.set(row.id, dbPath);
12466
+ out.push({
12467
+ toolId: "hermes",
12468
+ rawId: row.id,
12469
+ prefixedId: this.toPrefixedId(row.id),
12470
+ projectPath,
12471
+ projectDir: "",
12472
+ fullPath: dbPath,
12473
+ created: new Date(Math.round(row.started_at * 1e3)).toISOString(),
12474
+ modified: new Date(mtime).toISOString(),
12475
+ mtime,
12476
+ firstPrompt: opts.previews === false ? "" : firstUserPrompt(db, row.id),
12477
+ messageCount: opts.previews === false ? 0 : row.message_count ?? 0
12478
+ });
12479
+ if (opts.limit && out.length >= opts.limit)
12480
+ break;
12481
+ }
12482
+ return out;
12483
+ } catch {
12484
+ return [];
12485
+ } finally {
12486
+ db.close();
12487
+ }
12488
+ }
12489
+ /** Hermes writes a title for each session (a model-written one, or the
12490
+ * first prompt for a one-shot run). */
12491
+ getNativeTitle(rawId) {
12492
+ const path = this.dbPathFor(this.toRawId(rawId));
12493
+ if (!path)
12494
+ return null;
12495
+ const db = openSqliteReadonly(path);
12496
+ if (!db)
12497
+ return null;
12498
+ try {
12499
+ const row = db.prepare("SELECT title FROM sessions WHERE id = ?").get(this.toRawId(rawId));
12500
+ const t = row?.title?.trim();
12501
+ return t ? t.slice(0, 200) : null;
12502
+ } catch {
12503
+ return null;
12504
+ } finally {
12505
+ db.close();
12506
+ }
12507
+ }
12508
+ // ── Generic-engine inputs ───────────────────────────────────────
12509
+ fileToolMap = {
12510
+ write_file: "write",
12511
+ patch: "edit",
12512
+ read_file: "read"
12513
+ };
12514
+ /**
12515
+ * `write_file` carries `{ path, content }`. `patch` carries
12516
+ * `{ mode: 'replace', path, old_string, new_string }`; its other mode,
12517
+ * `patch`, is a multi-file V4A patch with no single before and after.
12518
+ */
12519
+ extractEditDelta(toolName, input) {
12520
+ if (input == null || typeof input !== "object")
12521
+ return null;
12522
+ const inp = input;
12523
+ if (toolName === "write_file") {
12524
+ return { before: "", after: typeof inp.content === "string" ? inp.content : null };
12525
+ }
12526
+ if (toolName === "patch" && (inp.mode === void 0 || inp.mode === "replace")) {
12527
+ const before = typeof inp.old_string === "string" ? inp.old_string : null;
12528
+ const after = typeof inp.new_string === "string" ? inp.new_string : null;
12529
+ if (before === null && after === null)
12530
+ return null;
12531
+ return { before, after };
12532
+ }
12533
+ return null;
12534
+ }
12535
+ readEvents(rawId) {
12536
+ const id = this.toRawId(rawId);
12537
+ const path = this.dbPathFor(id);
12538
+ if (!path)
12539
+ return [];
12540
+ const db = openSqliteReadonly(path);
12541
+ if (!db)
12542
+ return [];
12543
+ try {
12544
+ const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(id);
12545
+ return messageRowsToEvents(rows);
12546
+ } catch {
12547
+ return [];
12548
+ } finally {
12549
+ db.close();
12550
+ }
12551
+ }
12552
+ /**
12553
+ * Events from the dump `exportRawSession` writes, for a session rebuilt from
12554
+ * the server's archive. The archive joins the lines of every dump it got, so
12555
+ * a row that changed (a rewind sets `active` to 0) is there more than once.
12556
+ * The last copy of each row id is the newest.
12557
+ */
12558
+ readEventsFromText(text) {
12559
+ const rows = /* @__PURE__ */ new Map();
12560
+ for (const line of text.split("\n")) {
12561
+ if (!line.trim())
12562
+ continue;
12563
+ try {
12564
+ const rec = JSON.parse(line);
12565
+ if (rec.kind === "message" && rec.row) {
12566
+ rows.delete(rec.row.id);
12567
+ rows.set(rec.row.id, rec.row);
12568
+ }
12569
+ } catch {
12570
+ }
12571
+ }
12572
+ return messageRowsToEvents([...rows.values()].sort((a, b) => a.id - b.id));
12573
+ }
12574
+ // ── Per-session operations — all delegate to the generic engine ─
12575
+ extractTurns(id, opts = {}) {
12576
+ return extractTurnsFromEvents(this.toPrefixedId(id), this.readEvents(id), opts);
12577
+ }
12578
+ liveScanEdits(id) {
12579
+ const located = this.findSession(id);
12580
+ if (!located) {
12581
+ return { found: false, projectPath: "", projectDir: "", edits: [], fileMtime: 0, tool: "hermes" };
12582
+ }
12583
+ return liveScanEditsFromEvents(this.readEvents(id), this.fileToolMap, {
12584
+ sessionId: this.toPrefixedId(id),
12585
+ tool: "hermes",
12586
+ projectPath: located.projectPath,
12587
+ projectDir: located.projectDir,
12588
+ fileMtime: located.mtime,
12589
+ found: true
12590
+ });
12591
+ }
12592
+ replay(id) {
12593
+ const located = this.findSession(id);
12594
+ if (!located) {
12595
+ return { sessionId: this.toPrefixedId(id), found: false, projectPath: "", files: [], totalLinesAdded: 0, totalLinesRemoved: 0 };
12596
+ }
12597
+ return replayFromEvents(this.toPrefixedId(id), this.readEvents(id), this.fileToolMap, this.extractEditDelta.bind(this), {
12598
+ projectPath: located.projectPath,
12599
+ found: true
12600
+ });
12601
+ }
12602
+ computeOutcome(id, opts) {
12603
+ return computeOutcome(this.toPrefixedId(id), opts);
12604
+ }
12605
+ /** File edits since `sinceMs` across the sessions active since then. */
12606
+ collectRecentEdits(opts) {
12607
+ const refs = this.listSessions({ sinceMs: opts.sinceMs, projectFilter: opts.projectFilter, previews: false });
12608
+ const edits = [];
12609
+ for (const ref of opts.limitSessions ? refs.slice(0, opts.limitSessions) : refs) {
12610
+ for (const e of this.liveScanEdits(ref.rawId).edits) {
12611
+ if (e.ts >= opts.sinceMs)
12612
+ edits.push(e);
12613
+ }
12614
+ }
12615
+ return edits.sort((a, b) => b.ts - a.ts);
12616
+ }
12617
+ getCommits(id, files, startMs, endMs, bufferMinutes) {
12618
+ return getSessionCommits(this.toPrefixedId(id), files, startMs, endMs, bufferMinutes);
12619
+ }
12620
+ /**
12621
+ * The session row and its messages as JSONL, one row per line in id order.
12622
+ * Hermes rewrites and compacts its database, so the archive keeps the rows
12623
+ * as they were synced.
12624
+ */
12625
+ exportRawSession(id) {
12626
+ const rawId = this.toRawId(id);
12627
+ const path = this.dbPathFor(rawId);
12628
+ if (!path)
12629
+ return null;
12630
+ const db = openSqliteReadonly(path);
12631
+ if (!db)
12632
+ return null;
12633
+ try {
12634
+ const have = new Set(db.prepare(`SELECT name FROM pragma_table_info('sessions')`).all().map((c) => c.name));
12635
+ const cols = ARCHIVED_SESSION_COLUMNS.filter((c) => have.has(c)).map((c) => `s.${c}`);
12636
+ const session = db.prepare(`
12637
+ SELECT ${[...cols, `${SESSION_MTIME_SQL} AS mtime`].join(", ")}
12638
+ FROM sessions s WHERE s.id = ?
12639
+ `).get(rawId);
12640
+ if (!session)
12641
+ return null;
12642
+ const rows = db.prepare(`SELECT ${MESSAGE_COLUMNS} FROM messages WHERE session_id = ? ORDER BY id`).all(rawId);
12643
+ const lines = [JSON.stringify({ kind: "session", row: session })];
12644
+ for (const r of rows)
12645
+ lines.push(JSON.stringify({ kind: "message", row: r }));
12646
+ return {
12647
+ tool: "hermes",
12648
+ mtime: Math.round(Number(session.mtime || 0) * 1e3),
12649
+ files: [{ name: `${rawId}.dump.jsonl`, bytes: Buffer.from(lines.join("\n") + "\n", "utf-8") }]
12650
+ };
12651
+ } catch {
12652
+ return null;
12653
+ } finally {
12654
+ db.close();
12655
+ }
12656
+ }
12657
+ };
12658
+ hermesBackend = new HermesBackend();
12659
+ }
12660
+ });
12661
+
12080
12662
  // ../engine/dist/core/backends/index.js
12081
12663
  var backends_exports = {};
12082
12664
  __export(backends_exports, {
@@ -12084,6 +12666,7 @@ __export(backends_exports, {
12084
12666
  ClaudeBackend: () => ClaudeBackend,
12085
12667
  CodexBackend: () => CodexBackend,
12086
12668
  CursorBackend: () => CursorBackend,
12669
+ HermesBackend: () => HermesBackend,
12087
12670
  OpencodeBackend: () => OpencodeBackend,
12088
12671
  agyBackend: () => agyBackend,
12089
12672
  bootstrapBackends: () => bootstrapBackends,
@@ -12092,6 +12675,7 @@ __export(backends_exports, {
12092
12675
  cursorBackend: () => cursorBackend,
12093
12676
  getBackend: () => getBackend,
12094
12677
  getBackendForId: () => getBackendForId,
12678
+ hermesBackend: () => hermesBackend,
12095
12679
  listAllBackends: () => listAllBackends,
12096
12680
  listAvailableBackends: () => listAvailableBackends,
12097
12681
  opencodeBackend: () => opencodeBackend,
@@ -12104,6 +12688,7 @@ function bootstrapBackends() {
12104
12688
  registerBackend(codexBackend);
12105
12689
  registerBackend(agyBackend);
12106
12690
  registerBackend(cursorBackend);
12691
+ registerBackend(hermesBackend);
12107
12692
  }
12108
12693
  var init_backends = __esm({
12109
12694
  "../engine/dist/core/backends/index.js"() {
@@ -12114,6 +12699,7 @@ var init_backends = __esm({
12114
12699
  init_codex2();
12115
12700
  init_agy();
12116
12701
  init_cursor();
12702
+ init_hermes();
12117
12703
  init_tool_backend();
12118
12704
  _setRegistryBootstrapper(bootstrapBackends);
12119
12705
  }
@@ -12630,7 +13216,7 @@ var init_secret_redactor = __esm({
12630
13216
 
12631
13217
  // ../engine/dist/core/source-discovery.js
12632
13218
  import { createHash as createHash3 } from "crypto";
12633
- import { existsSync as existsSync16, readdirSync as readdirSync12, statSync as statSync14 } from "fs";
13219
+ import { existsSync as existsSync17, readdirSync as readdirSync12, statSync as statSync14 } from "fs";
12634
13220
  import { join as join18 } from "path";
12635
13221
  function sourceId(path) {
12636
13222
  return "src_" + createHash3("sha256").update(path).digest("hex").slice(0, 12);
@@ -12689,7 +13275,7 @@ function countTranscripts(root, tool) {
12689
13275
  walk(root, 0);
12690
13276
  return { sessions, newestMtime };
12691
13277
  }
12692
- function countOpencodeSessions(dbPath) {
13278
+ function countDbSessions(dbPath) {
12693
13279
  try {
12694
13280
  const st = statSync14(dbPath);
12695
13281
  return { sessions: 0, newestMtime: st.mtimeMs };
@@ -12707,18 +13293,24 @@ function discoverSessionSources() {
12707
13293
  ];
12708
13294
  for (const [tool, dirs] of dirTools) {
12709
13295
  for (const [i, path] of dirs.entries()) {
12710
- if (!existsSync16(path))
13296
+ if (!existsSync17(path))
12711
13297
  continue;
12712
13298
  const { sessions, newestMtime } = countTranscripts(path, tool);
12713
13299
  out.push({ id: sourceId(path), tool, path, sessions, newestMtime, isPrimary: i === 0 });
12714
13300
  }
12715
13301
  }
12716
13302
  for (const [i, path] of opencodeDbPaths({ includeExcluded: true }).entries()) {
12717
- if (!existsSync16(path))
13303
+ if (!existsSync17(path))
12718
13304
  continue;
12719
- const { sessions, newestMtime } = countOpencodeSessions(path);
13305
+ const { sessions, newestMtime } = countDbSessions(path);
12720
13306
  out.push({ id: sourceId(path), tool: "opencode", path, sessions, newestMtime, isPrimary: i === 0 });
12721
13307
  }
13308
+ for (const [i, path] of hermesDbPaths({ includeExcluded: true }).entries()) {
13309
+ if (!existsSync17(path))
13310
+ continue;
13311
+ const { sessions, newestMtime } = countDbSessions(path);
13312
+ out.push({ id: sourceId(path), tool: "hermes", path, sessions, newestMtime, isPrimary: i === 0 });
13313
+ }
12722
13314
  return out;
12723
13315
  }
12724
13316
  var excludedIds, SOURCE_ID_RE, MAX_DEPTH;
@@ -12734,7 +13326,7 @@ var init_source_discovery = __esm({
12734
13326
 
12735
13327
  // ../engine/dist/core/home-discovery.js
12736
13328
  import { execFileSync as execFileSync2 } from "child_process";
12737
- import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync12, realpathSync as realpathSync2, statSync as statSync15 } from "fs";
13329
+ import { existsSync as existsSync18, readdirSync as readdirSync13, readFileSync as readFileSync12, realpathSync as realpathSync2, statSync as statSync15 } from "fs";
12738
13330
  import { homedir as homedir5, platform as osPlatform } from "os";
12739
13331
  import { join as join19 } from "path";
12740
13332
  function candidateRoots(plat = osPlatform(), env = process.env) {
@@ -12755,10 +13347,10 @@ function candidateRoots(plat = osPlatform(), env = process.env) {
12755
13347
  add(env.XDG_CONFIG_HOME || join19(home, ".config"));
12756
13348
  add(env.XDG_DATA_HOME || join19(home, ".local", "share"));
12757
13349
  }
12758
- return roots.filter((r) => existsSync17(r));
13350
+ return roots.filter((r) => existsSync18(r));
12759
13351
  }
12760
13352
  function identifyHome(dir) {
12761
- const has = (...parts) => existsSync17(join19(dir, ...parts));
13353
+ const has = (...parts) => existsSync18(join19(dir, ...parts));
12762
13354
  if (has("projects") && dirHasTranscript(join19(dir, "projects"), (f) => f.endsWith(".jsonl"), 2))
12763
13355
  return "claude";
12764
13356
  if (has("sessions") && dirHasTranscript(join19(dir, "sessions"), (f) => f.startsWith("rollout-") && f.endsWith(".jsonl"), 4))
@@ -12769,6 +13361,8 @@ function identifyHome(dir) {
12769
13361
  return "cursor";
12770
13362
  if (has("opencode.db"))
12771
13363
  return "opencode";
13364
+ if (has("state.db") && has("SOUL.md"))
13365
+ return "hermes";
12772
13366
  return null;
12773
13367
  }
12774
13368
  function dirHasTranscript(root, match, depth) {
@@ -12796,7 +13390,8 @@ function countSessions(dir, tool) {
12796
13390
  codex: { sub: "sessions", depth: 4, match: (f) => f.startsWith("rollout-") && f.endsWith(".jsonl") },
12797
13391
  agy: { sub: "brain", depth: 4, match: (f) => f.endsWith(".jsonl") },
12798
13392
  cursor: { sub: "chats", depth: 3, match: (f) => f === "store.db" },
12799
- opencode: { sub: "", depth: 0, match: () => false }
13393
+ opencode: { sub: "", depth: 0, match: () => false },
13394
+ hermes: { sub: "", depth: 0, match: () => false }
12800
13395
  };
12801
13396
  const s = spec[tool];
12802
13397
  if (!s.sub)
@@ -12839,6 +13434,8 @@ function declaredHomes(env = process.env, plat = osPlatform()) {
12839
13434
  add(env.CHAT_RECALL_AGY_HOME);
12840
13435
  add(env.CHAT_RECALL_CURSOR_HOME);
12841
13436
  add(env.CHAT_RECALL_CURSOR_IDE_HOME);
13437
+ add(env.CHAT_RECALL_HERMES_HOME);
13438
+ add(env.HERMES_HOME);
12842
13439
  for (const d of (env.CLAUDE_DIRS || "").split(","))
12843
13440
  add(d);
12844
13441
  if (plat === "win32") {
@@ -12938,6 +13535,15 @@ function discoverHomes(opts = {}) {
12938
13535
  if (prior && !(via === "declared" && prior.via !== "declared"))
12939
13536
  return;
12940
13537
  found.set(real, { tool, path: real, via, sessions: countSessions(real, tool) });
13538
+ if (tool === "hermes") {
13539
+ try {
13540
+ for (const e of readdirSync13(join19(real, "profiles"), { withFileTypes: true })) {
13541
+ if (e.isDirectory())
13542
+ consider(join19(real, "profiles", e.name), via);
13543
+ }
13544
+ } catch {
13545
+ }
13546
+ }
12941
13547
  };
12942
13548
  for (const d of declaredHomes(env, plat))
12943
13549
  consider(d, "declared");
@@ -13004,7 +13610,7 @@ var init_home_discovery = __esm({
13004
13610
 
13005
13611
  // ../engine/dist/core/detector-install.js
13006
13612
  import { createHash as createHash4 } from "crypto";
13007
- 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";
13613
+ import { existsSync as existsSync19, 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";
13008
13614
  import { join as join20 } from "path";
13009
13615
  function managedDetectorPath(name) {
13010
13616
  return join20(getDetectorBinDir(), name + exeSuffix());
@@ -13044,7 +13650,7 @@ function resolveDetector(name) {
13044
13650
  const dir = configuredDir();
13045
13651
  if (dir) {
13046
13652
  const p2 = join20(dir, name + exeSuffix());
13047
- if (existsSync18(p2))
13653
+ if (existsSync19(p2))
13048
13654
  return { path: p2, version: "operator-supplied", source: "configured" };
13049
13655
  return { error: "not-installed" };
13050
13656
  }
@@ -13052,7 +13658,7 @@ function resolveDetector(name) {
13052
13658
  if (!spec.assets[platformKey()])
13053
13659
  return { error: "unsupported-platform" };
13054
13660
  const p = managedDetectorPath(name);
13055
- if (!existsSync18(p))
13661
+ if (!existsSync19(p))
13056
13662
  return { error: "not-installed" };
13057
13663
  const receipt = readReceipt(name);
13058
13664
  if (!receipt)
@@ -13108,7 +13714,7 @@ var init_detector_install = __esm({
13108
13714
 
13109
13715
  // ../engine/dist/core/secret-scanner.js
13110
13716
  import { spawnSync } from "child_process";
13111
- import { existsSync as existsSync19, unlinkSync, readFileSync as readFileSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
13717
+ import { existsSync as existsSync20, unlinkSync, readFileSync as readFileSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
13112
13718
  import { tmpdir } from "os";
13113
13719
  import { join as join21 } from "path";
13114
13720
  function mask(s) {
@@ -13274,7 +13880,7 @@ function runTrufflehogDir(binary, dir, opts = {}) {
13274
13880
  return out;
13275
13881
  }
13276
13882
  function scanDirForSecrets(dir, opts = {}) {
13277
- if (!existsSync19(dir))
13883
+ if (!existsSync20(dir))
13278
13884
  return [];
13279
13885
  const gitleaks = whichDetector("gitleaks");
13280
13886
  const trufflehog = whichDetector("trufflehog");
@@ -13311,7 +13917,7 @@ function scanTenantRules(text, tenantRules) {
13311
13917
  return out;
13312
13918
  }
13313
13919
  function scanFileForSecrets(filePath, opts = {}) {
13314
- if (!existsSync19(filePath))
13920
+ if (!existsSync20(filePath))
13315
13921
  return [];
13316
13922
  const gitleaks = whichDetector("gitleaks");
13317
13923
  const trufflehog = whichDetector("trufflehog");
@@ -13428,10 +14034,10 @@ var init_types = __esm({
13428
14034
 
13429
14035
  // ../engine/dist/transcript/opencode.js
13430
14036
  async function parseOpenCodeSubagents(parentSessionId) {
13431
- const { existsSync: existsSync51 } = await import("fs");
14037
+ const { existsSync: existsSync52 } = await import("fs");
13432
14038
  const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
13433
14039
  const dbPath = opencodeBackend2.dbPath();
13434
- if (!existsSync51(dbPath))
14040
+ if (!existsSync52(dbPath))
13435
14041
  return [];
13436
14042
  const realParent = parentSessionId.replace("opencode_", "");
13437
14043
  const db = openSqliteReadonlyOrThrow(dbPath);
@@ -13472,10 +14078,10 @@ async function parseOpenCodeSubagents(parentSessionId) {
13472
14078
  }
13473
14079
  }
13474
14080
  async function parseOpenCodeTranscript(sessionId) {
13475
- const { existsSync: existsSync51 } = await import("fs");
14081
+ const { existsSync: existsSync52 } = await import("fs");
13476
14082
  const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
13477
14083
  const dbPath = opencodeBackend2.dbPath();
13478
- if (!existsSync51(dbPath))
14084
+ if (!existsSync52(dbPath))
13479
14085
  return [];
13480
14086
  const realId = sessionId.replace("opencode_", "");
13481
14087
  const db = openSqliteReadonlyOrThrow(dbPath);
@@ -13706,7 +14312,7 @@ var init_transcript = __esm({
13706
14312
  // ../engine/dist/core/project-resolver.js
13707
14313
  import { execSync } from "child_process";
13708
14314
  import { createHash as createHash5 } from "crypto";
13709
- import { existsSync as existsSync20, readFileSync as readFileSync15, realpathSync as realpathSync3, readdirSync as readdirSync14, statSync as statSync18 } from "fs";
14315
+ import { existsSync as existsSync21, readFileSync as readFileSync15, realpathSync as realpathSync3, readdirSync as readdirSync14, statSync as statSync18 } from "fs";
13710
14316
  import { basename as basename11, dirname as dirname14, isAbsolute, join as join22, resolve, sep } from "path";
13711
14317
  function getProjectsConfigPath() {
13712
14318
  return join22(getDataDir(), "projects.json");
@@ -13716,7 +14322,7 @@ function loadProjectsConfig() {
13716
14322
  return cachedConfig.cfg;
13717
14323
  }
13718
14324
  const path = getProjectsConfigPath();
13719
- if (!existsSync20(path)) {
14325
+ if (!existsSync21(path)) {
13720
14326
  cachedConfig = { mtime: 0, cfg: {} };
13721
14327
  return cachedConfig.cfg;
13722
14328
  }
@@ -13856,7 +14462,7 @@ function findGitToplevel(realPath) {
13856
14462
  let cur = realPath;
13857
14463
  let last = "";
13858
14464
  while (cur && cur !== last) {
13859
- if (existsSync20(join22(cur, ".git")))
14465
+ if (existsSync21(join22(cur, ".git")))
13860
14466
  return cur;
13861
14467
  last = cur;
13862
14468
  cur = dirname14(cur);
@@ -13923,7 +14529,7 @@ function resolveAutoWorkspace(realPath, minRepos) {
13923
14529
  let last = "";
13924
14530
  let depth = 0;
13925
14531
  while (cur && cur !== last && depth < AUTO_WORKSPACE_MAX_DEPTH) {
13926
- if (!existsSync20(join22(cur, ".git"))) {
14532
+ if (!existsSync21(join22(cur, ".git"))) {
13927
14533
  const cached = workspaceCache.get(cur);
13928
14534
  let qualifies;
13929
14535
  if (cached === void 0) {
@@ -13959,7 +14565,7 @@ function countGitChildren(dir) {
13959
14565
  const st = statSync18(child);
13960
14566
  if (!st.isDirectory())
13961
14567
  continue;
13962
- if (existsSync20(join22(child, ".git")))
14568
+ if (existsSync21(join22(child, ".git")))
13963
14569
  count++;
13964
14570
  } catch {
13965
14571
  }
@@ -14572,7 +15178,7 @@ var init_source_policy = __esm({
14572
15178
  init_project_path_match();
14573
15179
  init_settings();
14574
15180
  cache2 = null;
14575
- TOOLS = ["claude", "opencode", "codex", "agy", "cursor"];
15181
+ TOOLS = ["claude", "opencode", "codex", "agy", "cursor", "hermes"];
14576
15182
  }
14577
15183
  });
14578
15184
 
@@ -14667,7 +15273,7 @@ var init_source_registry = __esm({
14667
15273
  });
14668
15274
 
14669
15275
  // ../engine/dist/core/utils.js
14670
- import { existsSync as existsSync21, readdirSync as readdirSync15 } from "fs";
15276
+ import { existsSync as existsSync22, readdirSync as readdirSync15 } from "fs";
14671
15277
  import { homedir as homedir6 } from "os";
14672
15278
  import { join as join23 } from "path";
14673
15279
  function splitByHeaders(markdown) {
@@ -14703,7 +15309,7 @@ function getClaudeDirs() {
14703
15309
  const dirs = [];
14704
15310
  const seen = /* @__PURE__ */ new Set();
14705
15311
  const add = (dir) => {
14706
- if (!seen.has(dir) && existsSync21(dir)) {
15312
+ if (!seen.has(dir) && existsSync22(dir)) {
14707
15313
  seen.add(dir);
14708
15314
  dirs.push(dir);
14709
15315
  }
@@ -14722,7 +15328,7 @@ function getClaudeDirs() {
14722
15328
  for (const entry of entries) {
14723
15329
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
14724
15330
  const candidate = join23(home, entry.name);
14725
- if (existsSync21(join23(candidate, "projects"))) {
15331
+ if (existsSync22(join23(candidate, "projects"))) {
14726
15332
  add(candidate);
14727
15333
  }
14728
15334
  }
@@ -14735,7 +15341,7 @@ function discoverSubdirs(subdir) {
14735
15341
  const result = [];
14736
15342
  for (const claudeDir of getClaudeDirs()) {
14737
15343
  const path = join23(claudeDir, subdir);
14738
- if (existsSync21(path)) {
15344
+ if (existsSync22(path)) {
14739
15345
  result.push(path);
14740
15346
  }
14741
15347
  }
@@ -14748,7 +15354,7 @@ var init_utils = __esm({
14748
15354
  });
14749
15355
 
14750
15356
  // ../engine/dist/parsers/plan-source.js
14751
- import { existsSync as existsSync22, readdirSync as readdirSync16, readFileSync as readFileSync16, statSync as statSync20 } from "fs";
15357
+ import { existsSync as existsSync23, readdirSync as readdirSync16, readFileSync as readFileSync16, statSync as statSync20 } from "fs";
14752
15358
  import { homedir as homedir7 } from "os";
14753
15359
  import { join as join24, basename as basename12 } from "path";
14754
15360
  function parseFrontmatter(content) {
@@ -14806,7 +15412,7 @@ var init_plan_source = __esm({
14806
15412
  return this._knownProjectDirs;
14807
15413
  const projectsDir = claudeBackend.projectsDir();
14808
15414
  this._knownProjectDirs = [];
14809
- if (!existsSync22(projectsDir))
15415
+ if (!existsSync23(projectsDir))
14810
15416
  return this._knownProjectDirs;
14811
15417
  try {
14812
15418
  const entries = readdirSync16(projectsDir, { withFileTypes: true });
@@ -14814,7 +15420,7 @@ var init_plan_source = __esm({
14814
15420
  if (!entry.isDirectory() || entry.name.startsWith("."))
14815
15421
  continue;
14816
15422
  const projectPath = resolveProjectDirName(entry.name);
14817
- if (existsSync22(projectPath)) {
15423
+ if (existsSync23(projectPath)) {
14818
15424
  this._knownProjectDirs.push(projectPath);
14819
15425
  }
14820
15426
  }
@@ -14838,7 +15444,7 @@ var init_plan_source = __esm({
14838
15444
  async *discoverClaudePlans() {
14839
15445
  const seenClaude = /* @__PURE__ */ new Set();
14840
15446
  for (const plansDir of this.plansDirs) {
14841
- if (!existsSync22(plansDir))
15447
+ if (!existsSync23(plansDir))
14842
15448
  continue;
14843
15449
  const files = readdirSync16(plansDir).filter((f) => f.endsWith(".md"));
14844
15450
  for (const file of files) {
@@ -14880,7 +15486,7 @@ var init_plan_source = __esm({
14880
15486
  }
14881
15487
  async *discoverOpenCodePlans() {
14882
15488
  const plansDir = opencodeBackend.plansDir();
14883
- if (!existsSync22(plansDir))
15489
+ if (!existsSync23(plansDir))
14884
15490
  return;
14885
15491
  let files;
14886
15492
  try {
@@ -14967,7 +15573,7 @@ var init_plan_source = __esm({
14967
15573
  return "";
14968
15574
  }
14969
15575
  async parse(item) {
14970
- if (!existsSync22(item.filePath))
15576
+ if (!existsSync23(item.filePath))
14971
15577
  return [];
14972
15578
  const content = readFileSync16(item.filePath, "utf-8");
14973
15579
  const chunks = [];
@@ -15008,7 +15614,7 @@ var init_plan_source = __esm({
15008
15614
  const projPath = join24(root, proj);
15009
15615
  for (const sessionEntry of safeReaddir(projPath)) {
15010
15616
  const subDir = join24(projPath, sessionEntry, "subagents");
15011
- if (!existsSync22(subDir))
15617
+ if (!existsSync23(subDir))
15012
15618
  continue;
15013
15619
  for (const f of safeReaddir(subDir)) {
15014
15620
  const m = f.match(/^agent-([a-z0-9_-]+)\.jsonl$/i);
@@ -15083,7 +15689,7 @@ var init_plan_source = __esm({
15083
15689
  }
15084
15690
  async *discoverAgyPlans() {
15085
15691
  const brainDir = join24(agyBackend.homeDir(), "brain");
15086
- if (!existsSync22(brainDir))
15692
+ if (!existsSync23(brainDir))
15087
15693
  return;
15088
15694
  let sessionDirs;
15089
15695
  try {
@@ -15138,7 +15744,7 @@ var init_plan_source = __esm({
15138
15744
  });
15139
15745
 
15140
15746
  // ../engine/dist/parsers/task-source.js
15141
- import { existsSync as existsSync23, readdirSync as readdirSync17, readFileSync as readFileSync17, statSync as statSync21 } from "fs";
15747
+ import { existsSync as existsSync24, readdirSync as readdirSync17, readFileSync as readFileSync17, statSync as statSync21 } from "fs";
15142
15748
  import { join as join25 } from "path";
15143
15749
  var MAX_CHUNK_CHARS2, TaskSource;
15144
15750
  var init_task_source = __esm({
@@ -15168,7 +15774,7 @@ var init_task_source = __esm({
15168
15774
  }
15169
15775
  async *discoverClaudeTodos() {
15170
15776
  const todosDir = claudeBackend.todosDir();
15171
- if (!existsSync23(todosDir))
15777
+ if (!existsSync24(todosDir))
15172
15778
  return;
15173
15779
  let files;
15174
15780
  try {
@@ -15219,7 +15825,7 @@ var init_task_source = __esm({
15219
15825
  }
15220
15826
  }
15221
15827
  async *discoverInDir(tasksDir) {
15222
- if (!existsSync23(tasksDir))
15828
+ if (!existsSync24(tasksDir))
15223
15829
  return;
15224
15830
  const entries = readdirSync17(tasksDir, { withFileTypes: true });
15225
15831
  for (const entry of entries) {
@@ -15261,7 +15867,7 @@ var init_task_source = __esm({
15261
15867
  return this.parseAgentTodo(item);
15262
15868
  }
15263
15869
  const taskDir = item.filePath;
15264
- if (!existsSync23(taskDir))
15870
+ if (!existsSync24(taskDir))
15265
15871
  return [];
15266
15872
  const taskFiles = readdirSync17(taskDir).filter((f) => f.endsWith(".json"));
15267
15873
  const tasks = this.readTasks(taskDir, taskFiles);
@@ -15322,7 +15928,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
15322
15928
  }];
15323
15929
  }
15324
15930
  async parseAgentTodo(item) {
15325
- if (!existsSync23(item.filePath))
15931
+ if (!existsSync24(item.filePath))
15326
15932
  return [];
15327
15933
  let parsed;
15328
15934
  try {
@@ -15374,7 +15980,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
15374
15980
  });
15375
15981
 
15376
15982
  // ../engine/dist/parsers/claude-md-source.js
15377
- import { existsSync as existsSync24, readFileSync as readFileSync18, readdirSync as readdirSync18, statSync as statSync22 } from "fs";
15983
+ import { existsSync as existsSync25, readFileSync as readFileSync18, readdirSync as readdirSync18, statSync as statSync22 } from "fs";
15378
15984
  import { homedir as homedir8 } from "os";
15379
15985
  import { join as join26, basename as basename13, dirname as dirname15 } from "path";
15380
15986
  import { createHash as createHash6 } from "crypto";
@@ -15382,13 +15988,13 @@ function discoverProjectDirs(claudeDir) {
15382
15988
  const dirs = [];
15383
15989
  const seen = /* @__PURE__ */ new Set();
15384
15990
  const addProject = (path) => {
15385
- if (!seen.has(path) && existsSync24(path)) {
15991
+ if (!seen.has(path) && existsSync25(path)) {
15386
15992
  seen.add(path);
15387
15993
  dirs.push(path);
15388
15994
  }
15389
15995
  };
15390
15996
  const scanProjectsDir = (projectsDir) => {
15391
- if (!existsSync24(projectsDir))
15997
+ if (!existsSync25(projectsDir))
15392
15998
  return;
15393
15999
  try {
15394
16000
  const entries = readdirSync18(projectsDir, { withFileTypes: true });
@@ -15416,7 +16022,7 @@ function discoverProjectDirs(claudeDir) {
15416
16022
  } catch {
15417
16023
  }
15418
16024
  const gemProjects = agyBackend.sharedProjectsJson();
15419
- if (existsSync24(gemProjects)) {
16025
+ if (existsSync25(gemProjects)) {
15420
16026
  try {
15421
16027
  const data = JSON.parse(readFileSync18(gemProjects, "utf-8"));
15422
16028
  for (const path of Object.keys(data.projects || {}))
@@ -15425,7 +16031,7 @@ function discoverProjectDirs(claudeDir) {
15425
16031
  }
15426
16032
  }
15427
16033
  const ocDb = opencodeBackend.dbPath();
15428
- if (existsSync24(ocDb)) {
16034
+ if (existsSync25(ocDb)) {
15429
16035
  try {
15430
16036
  const db = openSqliteReadonlyOrThrow(ocDb);
15431
16037
  try {
@@ -15476,7 +16082,7 @@ var init_claude_md_source = __esm({
15476
16082
  for (const note of NOTE_FILES) {
15477
16083
  const p = join26(homedir8(), note.filename);
15478
16084
  const key2 = seenKey(p, note.tool);
15479
- if (existsSync24(p) && !seen.has(key2)) {
16085
+ if (existsSync25(p) && !seen.has(key2)) {
15480
16086
  seen.add(key2);
15481
16087
  const item = this.fileToItem(p, note.filename, note.tool);
15482
16088
  if (item)
@@ -15488,7 +16094,7 @@ var init_claude_md_source = __esm({
15488
16094
  for (const note of NOTE_FILES) {
15489
16095
  const p = join26(projectDir, note.filename);
15490
16096
  const key2 = seenKey(p, note.tool);
15491
- if (existsSync24(p) && !seen.has(key2)) {
16097
+ if (existsSync25(p) && !seen.has(key2)) {
15492
16098
  seen.add(key2);
15493
16099
  const item = this.fileToItem(p, note.filename, note.tool);
15494
16100
  if (item)
@@ -15497,12 +16103,12 @@ var init_claude_md_source = __esm({
15497
16103
  }
15498
16104
  }
15499
16105
  for (const searchDir of this.searchDirs) {
15500
- if (!existsSync24(searchDir))
16106
+ if (!existsSync25(searchDir))
15501
16107
  continue;
15502
16108
  for (const note of NOTE_FILES) {
15503
16109
  const directPath = join26(searchDir, note.filename);
15504
16110
  const key2 = seenKey(directPath, note.tool);
15505
- if (existsSync24(directPath) && !seen.has(key2)) {
16111
+ if (existsSync25(directPath) && !seen.has(key2)) {
15506
16112
  seen.add(key2);
15507
16113
  const item = this.fileToItem(directPath, note.filename, note.tool);
15508
16114
  if (item)
@@ -15517,7 +16123,7 @@ var init_claude_md_source = __esm({
15517
16123
  for (const note of NOTE_FILES) {
15518
16124
  const p = join26(searchDir, entry.name, note.filename);
15519
16125
  const key2 = seenKey(p, note.tool);
15520
- if (existsSync24(p) && !seen.has(key2)) {
16126
+ if (existsSync25(p) && !seen.has(key2)) {
15521
16127
  seen.add(key2);
15522
16128
  const item = this.fileToItem(p, note.filename, note.tool);
15523
16129
  if (item)
@@ -15531,7 +16137,7 @@ var init_claude_md_source = __esm({
15531
16137
  }
15532
16138
  }
15533
16139
  async parse(item) {
15534
- if (!existsSync24(item.filePath))
16140
+ if (!existsSync25(item.filePath))
15535
16141
  return [];
15536
16142
  const content = readFileSync18(item.filePath, "utf-8");
15537
16143
  const chunks = [];
@@ -15600,7 +16206,7 @@ var init_claude_md_source = __esm({
15600
16206
  });
15601
16207
 
15602
16208
  // ../engine/dist/parsers/agent-memory-source.js
15603
- import { existsSync as existsSync25, readFileSync as readFileSync19, readdirSync as readdirSync19, statSync as statSync23 } from "fs";
16209
+ import { existsSync as existsSync26, readFileSync as readFileSync19, readdirSync as readdirSync19, statSync as statSync23 } from "fs";
15604
16210
  import { homedir as homedir9 } from "os";
15605
16211
  import { join as join27, basename as basename14 } from "path";
15606
16212
  function parseFrontmatter2(content) {
@@ -15634,7 +16240,7 @@ function discoverMemoryDirs() {
15634
16240
  const out = [];
15635
16241
  const seen = /* @__PURE__ */ new Set();
15636
16242
  const add = (memoryDir, projectHash) => {
15637
- if (!seen.has(memoryDir) && existsSync25(memoryDir)) {
16243
+ if (!seen.has(memoryDir) && existsSync26(memoryDir)) {
15638
16244
  seen.add(memoryDir);
15639
16245
  out.push({ memoryDir, projectHash });
15640
16246
  }
@@ -15650,7 +16256,7 @@ function discoverMemoryDirs() {
15650
16256
  } catch {
15651
16257
  }
15652
16258
  for (const projectsDir of claudeRoots) {
15653
- if (!existsSync25(projectsDir))
16259
+ if (!existsSync26(projectsDir))
15654
16260
  continue;
15655
16261
  try {
15656
16262
  for (const entry of readdirSync19(projectsDir, { withFileTypes: true })) {
@@ -15699,7 +16305,7 @@ var init_agent_memory_source = __esm({
15699
16305
  }
15700
16306
  }
15701
16307
  async parse(item) {
15702
- if (!existsSync25(item.filePath))
16308
+ if (!existsSync26(item.filePath))
15703
16309
  return [];
15704
16310
  const content = readFileSync19(item.filePath, "utf-8");
15705
16311
  const { body } = parseFrontmatter2(content);
@@ -15785,7 +16391,7 @@ var init_agent_memory_source = __esm({
15785
16391
  });
15786
16392
 
15787
16393
  // ../engine/dist/parsers/history-source.js
15788
- import { existsSync as existsSync26, readdirSync as readdirSync20, statSync as statSync24 } from "fs";
16394
+ import { existsSync as existsSync27, readdirSync as readdirSync20, statSync as statSync24 } from "fs";
15789
16395
  import { createReadStream as createReadStream3 } from "fs";
15790
16396
  import { createInterface as createInterface2 } from "readline";
15791
16397
  import { homedir as homedir10 } from "os";
@@ -15807,7 +16413,7 @@ var init_history_source = __esm({
15807
16413
  const home = homedir10();
15808
16414
  const paths = [];
15809
16415
  const tryAdd = (p) => {
15810
- if (existsSync26(p) && !paths.includes(p))
16416
+ if (existsSync27(p) && !paths.includes(p))
15811
16417
  paths.push(p);
15812
16418
  };
15813
16419
  tryAdd(claudeBackend.historyFile());
@@ -15825,7 +16431,7 @@ var init_history_source = __esm({
15825
16431
  async *discover() {
15826
16432
  if (!isSourceEnabled("claude", "history"))
15827
16433
  return;
15828
- const historyPath = this.historyPaths.find((p) => existsSync26(p));
16434
+ const historyPath = this.historyPaths.find((p) => existsSync27(p));
15829
16435
  if (!historyPath)
15830
16436
  return;
15831
16437
  const stat3 = statSync24(historyPath);
@@ -15921,7 +16527,7 @@ var init_history_source = __esm({
15921
16527
  async groupByProject() {
15922
16528
  const groups = /* @__PURE__ */ new Map();
15923
16529
  for (const historyPath of this.historyPaths) {
15924
- if (!existsSync26(historyPath))
16530
+ if (!existsSync27(historyPath))
15925
16531
  continue;
15926
16532
  const rl = createInterface2({
15927
16533
  input: createReadStream3(historyPath),
@@ -15969,7 +16575,7 @@ var init_history_source = __esm({
15969
16575
  });
15970
16576
 
15971
16577
  // ../engine/dist/parsers/paste-source.js
15972
- import { existsSync as existsSync27, readdirSync as readdirSync21, readFileSync as readFileSync20, statSync as statSync25 } from "fs";
16578
+ import { existsSync as existsSync28, readdirSync as readdirSync21, readFileSync as readFileSync20, statSync as statSync25 } from "fs";
15973
16579
  import { homedir as homedir11 } from "os";
15974
16580
  import { join as join29, basename as basename15 } from "path";
15975
16581
  var MAX_CHUNK_CHARS6, MAX_FILE_SIZE, PasteSource;
@@ -15990,7 +16596,7 @@ var init_paste_source = __esm({
15990
16596
  const home = homedir11();
15991
16597
  const dirs = [];
15992
16598
  const tryAdd = (p) => {
15993
- if (existsSync27(p) && !dirs.includes(p))
16599
+ if (existsSync28(p) && !dirs.includes(p))
15994
16600
  dirs.push(p);
15995
16601
  };
15996
16602
  tryAdd(claudeBackend.pasteCacheDir());
@@ -16010,7 +16616,7 @@ var init_paste_source = __esm({
16010
16616
  return;
16011
16617
  const seen = /* @__PURE__ */ new Set();
16012
16618
  for (const cacheDir of this.cacheDirs) {
16013
- if (!existsSync27(cacheDir))
16619
+ if (!existsSync28(cacheDir))
16014
16620
  continue;
16015
16621
  const files = readdirSync21(cacheDir).filter((f) => f.endsWith(".txt"));
16016
16622
  for (const file of files) {
@@ -16044,7 +16650,7 @@ var init_paste_source = __esm({
16044
16650
  }
16045
16651
  }
16046
16652
  async parse(item) {
16047
- if (!existsSync27(item.filePath))
16653
+ if (!existsSync28(item.filePath))
16048
16654
  return [];
16049
16655
  const content = readFileSync20(item.filePath, "utf-8");
16050
16656
  if (content.trim().length < 20)
@@ -16069,7 +16675,7 @@ var init_paste_source = __esm({
16069
16675
  });
16070
16676
 
16071
16677
  // ../engine/dist/parsers/antigravity-brain-source.js
16072
- import { existsSync as existsSync28, readdirSync as readdirSync22, readFileSync as readFileSync21, statSync as statSync26 } from "fs";
16678
+ import { existsSync as existsSync29, readdirSync as readdirSync22, readFileSync as readFileSync21, statSync as statSync26 } from "fs";
16073
16679
  import { join as join30 } from "path";
16074
16680
  var MAX_CHUNK_CHARS7, AntigravityBrainSource;
16075
16681
  var init_antigravity_brain_source = __esm({
@@ -16088,7 +16694,7 @@ var init_antigravity_brain_source = __esm({
16088
16694
  async *discover() {
16089
16695
  if (!isSourceEnabled("agy", "brain"))
16090
16696
  return;
16091
- if (!existsSync28(this.brainDir))
16697
+ if (!existsSync29(this.brainDir))
16092
16698
  return;
16093
16699
  const sessions = readdirSync22(this.brainDir, { withFileTypes: true });
16094
16700
  for (const entry of sessions) {
@@ -16107,7 +16713,7 @@ var init_antigravity_brain_source = __esm({
16107
16713
  const heading = content.split("\n").find((l) => l.startsWith("#"))?.replace(/^#+\s*/, "").trim() || file;
16108
16714
  const metaPath = `${filePath}.metadata.json`;
16109
16715
  let metaSummary = "";
16110
- if (existsSync28(metaPath)) {
16716
+ if (existsSync29(metaPath)) {
16111
16717
  try {
16112
16718
  const meta = JSON.parse(readFileSync21(metaPath, "utf-8"));
16113
16719
  metaSummary = meta.summary || "";
@@ -16138,7 +16744,7 @@ var init_antigravity_brain_source = __esm({
16138
16744
  }
16139
16745
  }
16140
16746
  async parse(item) {
16141
- if (!existsSync28(item.filePath))
16747
+ if (!existsSync29(item.filePath))
16142
16748
  return [];
16143
16749
  const content = readFileSync21(item.filePath, "utf-8");
16144
16750
  const chunks = [];
@@ -16181,7 +16787,7 @@ var init_antigravity_brain_source = __esm({
16181
16787
  });
16182
16788
 
16183
16789
  // ../engine/dist/parsers/opencode-source.js
16184
- import { existsSync as existsSync29 } from "fs";
16790
+ import { existsSync as existsSync30 } from "fs";
16185
16791
  var MAX_CHUNK_CHARS8, OpenCodeSource;
16186
16792
  var init_opencode_source = __esm({
16187
16793
  "../engine/dist/parsers/opencode-source.js"() {
@@ -16199,7 +16805,7 @@ var init_opencode_source = __esm({
16199
16805
  async *discover() {
16200
16806
  if (!isSourceEnabled("opencode", "sessions"))
16201
16807
  return;
16202
- if (!existsSync29(this.dbPath))
16808
+ if (!existsSync30(this.dbPath))
16203
16809
  return;
16204
16810
  let db;
16205
16811
  try {
@@ -16297,7 +16903,7 @@ var init_opencode_source = __esm({
16297
16903
  }
16298
16904
  }
16299
16905
  async parse(item) {
16300
- if (!existsSync29(this.dbPath))
16906
+ if (!existsSync30(this.dbPath))
16301
16907
  return [];
16302
16908
  const sessionId = item.id.replace("opencode_", "");
16303
16909
  const db = openSqliteReadonlyOrThrow(this.dbPath);
@@ -16369,7 +16975,7 @@ var init_opencode_source = __esm({
16369
16975
  });
16370
16976
 
16371
16977
  // ../engine/dist/parsers/opencode-todo-source.js
16372
- import { existsSync as existsSync30 } from "fs";
16978
+ import { existsSync as existsSync31 } from "fs";
16373
16979
  var OpenCodeTodoSource;
16374
16980
  var init_opencode_todo_source = __esm({
16375
16981
  "../engine/dist/parsers/opencode-todo-source.js"() {
@@ -16386,7 +16992,7 @@ var init_opencode_todo_source = __esm({
16386
16992
  async *discover() {
16387
16993
  if (!isSourceEnabled("opencode", "todos"))
16388
16994
  return;
16389
- if (!existsSync30(this.dbPath))
16995
+ if (!existsSync31(this.dbPath))
16390
16996
  return;
16391
16997
  let db;
16392
16998
  try {
@@ -16435,7 +17041,7 @@ var init_opencode_todo_source = __esm({
16435
17041
  }
16436
17042
  }
16437
17043
  async parse(item) {
16438
- if (!existsSync30(this.dbPath))
17044
+ if (!existsSync31(this.dbPath))
16439
17045
  return [];
16440
17046
  const sessionId = item.extra?.sessionId || item.id.replace(`${opencodeBackend.idPrefix}todo_`, "");
16441
17047
  const db = openSqliteReadonlyOrThrow(this.dbPath);
@@ -16483,7 +17089,7 @@ var init_opencode_todo_source = __esm({
16483
17089
  });
16484
17090
 
16485
17091
  // ../engine/dist/parsers/codex-session-source.js
16486
- import { existsSync as existsSync31, readdirSync as readdirSync23, readFileSync as readFileSync22, statSync as statSync27 } from "fs";
17092
+ import { existsSync as existsSync32, readdirSync as readdirSync23, readFileSync as readFileSync22, statSync as statSync27 } from "fs";
16487
17093
  import { join as join31, basename as basename16 } from "path";
16488
17094
  function getCodexSessionsDir() {
16489
17095
  return codexBackend.sessionsDir();
@@ -16540,7 +17146,7 @@ var init_codex_session_source = __esm({
16540
17146
  async *discover() {
16541
17147
  if (!isSourceEnabled("codex", "sessions"))
16542
17148
  return;
16543
- if (!existsSync31(this.sessionsDir))
17149
+ if (!existsSync32(this.sessionsDir))
16544
17150
  return;
16545
17151
  const jsonlFiles = this.collectJsonlFiles(this.sessionsDir);
16546
17152
  for (const filePath of jsonlFiles) {
@@ -16599,7 +17205,7 @@ var init_codex_session_source = __esm({
16599
17205
  }
16600
17206
  }
16601
17207
  async parse(item) {
16602
- if (!existsSync31(item.filePath))
17208
+ if (!existsSync32(item.filePath))
16603
17209
  return [];
16604
17210
  const events = readJsonlLines(item.filePath);
16605
17211
  const chunks = [];
@@ -16702,7 +17308,7 @@ var init_codex_session_source = __esm({
16702
17308
  }
16703
17309
  collectJsonlFiles(dir) {
16704
17310
  const results = [];
16705
- if (!existsSync31(dir))
17311
+ if (!existsSync32(dir))
16706
17312
  return results;
16707
17313
  for (const entry of readdirSync23(dir, { withFileTypes: true })) {
16708
17314
  const fullPath = join31(dir, entry.name);
@@ -16719,7 +17325,7 @@ var init_codex_session_source = __esm({
16719
17325
  });
16720
17326
 
16721
17327
  // ../engine/dist/parsers/diary-source.js
16722
- import { existsSync as existsSync32, mkdirSync as mkdirSync6, readdirSync as readdirSync24, readFileSync as readFileSync23, writeFileSync as writeFileSync5, statSync as statSync28 } from "fs";
17328
+ import { existsSync as existsSync33, mkdirSync as mkdirSync6, readdirSync as readdirSync24, readFileSync as readFileSync23, writeFileSync as writeFileSync5, statSync as statSync28 } from "fs";
16723
17329
  import { join as join32, basename as basename17 } from "path";
16724
17330
  var MAX_CHUNK_CHARS10, DiarySource;
16725
17331
  var init_diary_source = __esm({
@@ -16735,7 +17341,7 @@ var init_diary_source = __esm({
16735
17341
  }
16736
17342
  /** Discover all diary entries across all agents. */
16737
17343
  async *discover() {
16738
- if (!existsSync32(this.diaryDir))
17344
+ if (!existsSync33(this.diaryDir))
16739
17345
  return;
16740
17346
  const agents = readdirSync24(this.diaryDir, { withFileTypes: true }).filter((d) => d.isDirectory());
16741
17347
  for (const agentDir of agents) {
@@ -16770,7 +17376,7 @@ var init_diary_source = __esm({
16770
17376
  }
16771
17377
  }
16772
17378
  async parse(item) {
16773
- if (!existsSync32(item.filePath))
17379
+ if (!existsSync33(item.filePath))
16774
17380
  return [];
16775
17381
  const raw = readFileSync23(item.filePath, "utf-8");
16776
17382
  const entry = JSON.parse(raw);
@@ -16810,7 +17416,7 @@ var init_diary_source = __esm({
16810
17416
  static write(entry, diaryDir) {
16811
17417
  const dir = diaryDir || getDiaryDir();
16812
17418
  const agentDir = join32(dir, entry.agent.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
16813
- if (!existsSync32(agentDir)) {
17419
+ if (!existsSync33(agentDir)) {
16814
17420
  mkdirSync6(agentDir, { recursive: true });
16815
17421
  }
16816
17422
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -16829,7 +17435,7 @@ var init_diary_source = __esm({
16829
17435
  static read(agentName, lastN = 10, diaryDir) {
16830
17436
  const dir = diaryDir || getDiaryDir();
16831
17437
  const agentDir = join32(dir, agentName.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
16832
- if (!existsSync32(agentDir))
17438
+ if (!existsSync33(agentDir))
16833
17439
  return [];
16834
17440
  const files = readdirSync24(agentDir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, lastN);
16835
17441
  const entries = [];
@@ -16880,7 +17486,7 @@ var init_redact_inline = __esm({
16880
17486
  });
16881
17487
 
16882
17488
  // ../engine/dist/parsers/skills-source.js
16883
- import { existsSync as existsSync33, readdirSync as readdirSync25, readFileSync as readFileSync24, statSync as statSync29 } from "fs";
17489
+ import { existsSync as existsSync34, readdirSync as readdirSync25, readFileSync as readFileSync24, statSync as statSync29 } from "fs";
16884
17490
  import { homedir as homedir12 } from "os";
16885
17491
  import { join as join33 } from "path";
16886
17492
  function rebuildBody(content) {
@@ -16920,7 +17526,7 @@ function resolveRoots() {
16920
17526
  all.push({ path: join33(cursorHomeDir(), "skills"), tool: "cursor" });
16921
17527
  all.push({ path: join33(cursorHomeDir(), "skills-cursor"), tool: "cursor", readonly: true });
16922
17528
  }
16923
- return all.filter((r) => existsSync33(r.path));
17529
+ return all.filter((r) => existsSync34(r.path));
16924
17530
  }
16925
17531
  function parseFrontmatter3(text) {
16926
17532
  const m = text.match(/^---\n([\s\S]*?)\n---/);
@@ -16962,7 +17568,7 @@ var init_skills_source = __esm({
16962
17568
  return;
16963
17569
  for (const { name: pluginName, dir } of codexBackend.installedPluginDirs()) {
16964
17570
  const skillsDir = join33(dir, "skills");
16965
- if (!existsSync33(skillsDir))
17571
+ if (!existsSync34(skillsDir))
16966
17572
  continue;
16967
17573
  yield* this.fromSkillRoot({ path: skillsDir, tool: "codex" }, pluginName);
16968
17574
  }
@@ -16987,7 +17593,7 @@ var init_skills_source = __esm({
16987
17593
  if (!st.isDirectory())
16988
17594
  continue;
16989
17595
  const skillMd = join33(skillDir, "SKILL.md");
16990
- if (!existsSync33(skillMd))
17596
+ if (!existsSync34(skillMd))
16991
17597
  continue;
16992
17598
  let content;
16993
17599
  try {
@@ -17052,7 +17658,7 @@ var init_skills_source = __esm({
17052
17658
  }
17053
17659
  }
17054
17660
  async parse(item) {
17055
- if (!existsSync33(item.filePath))
17661
+ if (!existsSync34(item.filePath))
17056
17662
  return [];
17057
17663
  const content = readFileSync24(item.filePath, "utf-8");
17058
17664
  if (!content.trim())
@@ -17077,11 +17683,11 @@ var init_skills_source = __esm({
17077
17683
  });
17078
17684
 
17079
17685
  // ../engine/dist/parsers/mcps-source.js
17080
- import { existsSync as existsSync34, readFileSync as readFileSync25, statSync as statSync30 } from "fs";
17686
+ import { existsSync as existsSync35, readFileSync as readFileSync25, statSync as statSync30 } from "fs";
17081
17687
  import { homedir as homedir13 } from "os";
17082
17688
  import { join as join34 } from "path";
17083
17689
  function readJson(path) {
17084
- if (!existsSync34(path))
17690
+ if (!existsSync35(path))
17085
17691
  return null;
17086
17692
  try {
17087
17693
  const raw = readFileSync25(path, "utf-8");
@@ -17244,7 +17850,7 @@ var init_mcps_source = __esm({
17244
17850
  }
17245
17851
  }
17246
17852
  async *fromCodexToml(path) {
17247
- if (!existsSync34(path))
17853
+ if (!existsSync35(path))
17248
17854
  return;
17249
17855
  let content;
17250
17856
  try {
@@ -17417,7 +18023,7 @@ var init_toolkit_format = __esm({
17417
18023
  });
17418
18024
 
17419
18025
  // ../engine/dist/parsers/slash-commands-source.js
17420
- import { existsSync as existsSync35, readdirSync as readdirSync26, readFileSync as readFileSync26, statSync as statSync31 } from "fs";
18026
+ import { existsSync as existsSync36, readdirSync as readdirSync26, readFileSync as readFileSync26, statSync as statSync31 } from "fs";
17421
18027
  import { join as join35, basename as basename18 } from "path";
17422
18028
  function rebuildBody2(content) {
17423
18029
  const { text, redacted } = redactInlineSecrets(content);
@@ -17470,14 +18076,14 @@ var init_slash_commands_source = __esm({
17470
18076
  if (isSourceEnabled("claude", "commands")) {
17471
18077
  roots.push({ path: claudeBackend.commandsDir(), tool: "claude", scope: "user", projectPath: "", format: "md" });
17472
18078
  const projectsRoot = claudeBackend.projectsDir();
17473
- if (existsSync35(projectsRoot)) {
18079
+ if (existsSync36(projectsRoot)) {
17474
18080
  try {
17475
18081
  for (const entry of readdirSync26(projectsRoot, { withFileTypes: true })) {
17476
18082
  if (!entry.isDirectory() || entry.name.startsWith("."))
17477
18083
  continue;
17478
18084
  const projectPath = resolveProjectDirName(entry.name);
17479
18085
  const cmdDir = join35(projectPath, ".claude", "commands");
17480
- if (existsSync35(cmdDir))
18086
+ if (existsSync36(cmdDir))
17481
18087
  roots.push({ path: cmdDir, tool: "claude", scope: "project", projectPath, format: "md" });
17482
18088
  }
17483
18089
  } catch {
@@ -17539,7 +18145,7 @@ var init_slash_commands_source = __esm({
17539
18145
  }
17540
18146
  }
17541
18147
  async parse(item) {
17542
- if (!existsSync35(item.filePath))
18148
+ if (!existsSync36(item.filePath))
17543
18149
  return [];
17544
18150
  const content = readFileSync26(item.filePath, "utf-8");
17545
18151
  if (!content.trim())
@@ -17564,7 +18170,7 @@ var init_slash_commands_source = __esm({
17564
18170
  });
17565
18171
 
17566
18172
  // ../engine/dist/parsers/subagents-source.js
17567
- import { existsSync as existsSync36, readdirSync as readdirSync27, readFileSync as readFileSync27, statSync as statSync32 } from "fs";
18173
+ import { existsSync as existsSync37, readdirSync as readdirSync27, readFileSync as readFileSync27, statSync as statSync32 } from "fs";
17568
18174
  import { join as join36, basename as basename19 } from "path";
17569
18175
  function rebuildBody3(content) {
17570
18176
  const { text, redacted } = redactInlineSecrets(content);
@@ -17619,14 +18225,14 @@ var init_subagents_source = __esm({
17619
18225
  if (isSourceEnabled("claude", "agents")) {
17620
18226
  roots.push({ path: claudeBackend.agentsDir(), tool: "claude", scope: "user", projectPath: "", format: "md" });
17621
18227
  const projectsRoot = claudeBackend.projectsDir();
17622
- if (existsSync36(projectsRoot)) {
18228
+ if (existsSync37(projectsRoot)) {
17623
18229
  try {
17624
18230
  for (const entry of readdirSync27(projectsRoot, { withFileTypes: true })) {
17625
18231
  if (!entry.isDirectory() || entry.name.startsWith("."))
17626
18232
  continue;
17627
18233
  const projectPath = resolveProjectDirName(entry.name);
17628
18234
  const agentDir = join36(projectPath, ".claude", "agents");
17629
- if (existsSync36(agentDir))
18235
+ if (existsSync37(agentDir))
17630
18236
  roots.push({ path: agentDir, tool: "claude", scope: "project", projectPath, format: "md" });
17631
18237
  }
17632
18238
  } catch {
@@ -17689,7 +18295,7 @@ var init_subagents_source = __esm({
17689
18295
  }
17690
18296
  }
17691
18297
  async parse(item) {
17692
- if (!existsSync36(item.filePath))
18298
+ if (!existsSync37(item.filePath))
17693
18299
  return [];
17694
18300
  const content = readFileSync27(item.filePath, "utf-8");
17695
18301
  if (!content.trim())
@@ -17714,10 +18320,10 @@ var init_subagents_source = __esm({
17714
18320
  });
17715
18321
 
17716
18322
  // ../engine/dist/parsers/hooks-source.js
17717
- import { existsSync as existsSync37, readFileSync as readFileSync28, readdirSync as readdirSync28, statSync as statSync33 } from "fs";
18323
+ import { existsSync as existsSync38, readFileSync as readFileSync28, readdirSync as readdirSync28, statSync as statSync33 } from "fs";
17718
18324
  import { join as join37 } from "path";
17719
18325
  function readJson2(path) {
17720
- if (!existsSync37(path))
18326
+ if (!existsSync38(path))
17721
18327
  return null;
17722
18328
  try {
17723
18329
  return JSON.parse(readFileSync28(path, "utf-8"));
@@ -17739,14 +18345,14 @@ var init_hooks_source = __esm({
17739
18345
  return;
17740
18346
  yield* this.fromSettings(claudeBackend.settingsFile(), "user", "");
17741
18347
  const projectsRoot = claudeBackend.projectsDir();
17742
- if (existsSync37(projectsRoot)) {
18348
+ if (existsSync38(projectsRoot)) {
17743
18349
  try {
17744
18350
  for (const entry of readdirSync28(projectsRoot, { withFileTypes: true })) {
17745
18351
  if (!entry.isDirectory() || entry.name.startsWith("."))
17746
18352
  continue;
17747
18353
  const projectPath = resolveProjectDirName(entry.name);
17748
18354
  const settingsPath2 = join37(projectPath, ".claude", "settings.json");
17749
- if (existsSync37(settingsPath2)) {
18355
+ if (existsSync38(settingsPath2)) {
17750
18356
  yield* this.fromSettings(settingsPath2, "project", projectPath);
17751
18357
  }
17752
18358
  }
@@ -17837,11 +18443,11 @@ var init_hooks_source = __esm({
17837
18443
  });
17838
18444
 
17839
18445
  // ../engine/dist/parsers/plugins-source.js
17840
- import { existsSync as existsSync38, readFileSync as readFileSync29, readdirSync as readdirSync29, statSync as statSync34 } from "fs";
18446
+ import { existsSync as existsSync39, readFileSync as readFileSync29, readdirSync as readdirSync29, statSync as statSync34 } from "fs";
17841
18447
  import { homedir as homedir14 } from "os";
17842
18448
  import { join as join38 } from "path";
17843
18449
  function readJson3(path) {
17844
- if (!existsSync38(path))
18450
+ if (!existsSync39(path))
17845
18451
  return null;
17846
18452
  try {
17847
18453
  return JSON.parse(readFileSync29(path, "utf-8"));
@@ -17896,7 +18502,7 @@ var init_plugins_source = __esm({
17896
18502
  }
17897
18503
  }
17898
18504
  const gemRoot = agyBackend.sharedExtensionsDir();
17899
- if (isSourceEnabled("agy", "extensions") && existsSync38(gemRoot)) {
18505
+ if (isSourceEnabled("agy", "extensions") && existsSync39(gemRoot)) {
17900
18506
  let entries;
17901
18507
  try {
17902
18508
  entries = readdirSync29(gemRoot);
@@ -18072,6 +18678,8 @@ function toolOfId(id) {
18072
18678
  return "codex";
18073
18679
  if (id.startsWith("cursor_"))
18074
18680
  return "cursor";
18681
+ if (id.startsWith("hermes_"))
18682
+ return "hermes";
18075
18683
  return "claude";
18076
18684
  }
18077
18685
  function extractorVersionForTool(tool) {
@@ -18372,6 +18980,8 @@ function markSynced(server, rows) {
18372
18980
  else if (prev?.F !== void 0) base3.F = prev.F;
18373
18981
  if (cursorFromCaller && r.chunkedHead) base3.k = 1;
18374
18982
  else if (prev?.k && !completesChunked && !(cursorFromCaller && r.full)) base3.k = 1;
18983
+ const layout = cursorFromCaller ? r.layout : prev?.L;
18984
+ if (layout) base3.L = layout;
18375
18985
  forServer[r.id] = base3;
18376
18986
  }
18377
18987
  persist(data);
@@ -18529,12 +19139,12 @@ var init_sync_fields = __esm({
18529
19139
  });
18530
19140
 
18531
19141
  // ../engine/dist/core/index-lock.js
18532
- import { existsSync as existsSync39, mkdirSync as mkdirSync8, openSync as openSync8, writeFileSync as writeFileSync7, readFileSync as readFileSync31, rmSync as rmSync4, statSync as statSync36, closeSync as closeSync8 } from "fs";
19142
+ import { existsSync as existsSync40, mkdirSync as mkdirSync8, openSync as openSync8, writeFileSync as writeFileSync7, readFileSync as readFileSync31, rmSync as rmSync4, statSync as statSync36, closeSync as closeSync8 } from "fs";
18533
19143
  import { dirname as dirname17, join as join40 } from "path";
18534
19144
  function acquireIndexLock(opts) {
18535
19145
  const lockPath = lockFilePath(opts.file);
18536
19146
  mkdirSync8(dirname17(lockPath), { recursive: true });
18537
- if (existsSync39(lockPath)) {
19147
+ if (existsSync40(lockPath)) {
18538
19148
  if (!stealStaleLock(lockPath, opts.staleAfterMs ?? 10 * 6e4)) {
18539
19149
  return null;
18540
19150
  }
@@ -18642,7 +19252,7 @@ var init_http = __esm({
18642
19252
  CLI_VERSION = JSON.parse(readFileSync32(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18643
19253
  } catch {
18644
19254
  try {
18645
- if (true) CLI_VERSION = "0.7.18";
19255
+ if (true) CLI_VERSION = "0.7.19";
18646
19256
  } catch {
18647
19257
  }
18648
19258
  }
@@ -18739,7 +19349,7 @@ var init_derived = __esm({
18739
19349
 
18740
19350
  // src/scan-pool.ts
18741
19351
  import { Worker } from "node:worker_threads";
18742
- import { existsSync as existsSync40 } from "node:fs";
19352
+ import { existsSync as existsSync41 } from "node:fs";
18743
19353
  import { fileURLToPath } from "node:url";
18744
19354
  import { cpus } from "node:os";
18745
19355
  function poolSize() {
@@ -18754,7 +19364,7 @@ function workerScript() {
18754
19364
  for (const rel of ["./scan-worker.js", "../dist/scan-worker.js"]) {
18755
19365
  try {
18756
19366
  const p = fileURLToPath(new URL(rel, import.meta.url));
18757
- if (existsSync40(p)) return p;
19367
+ if (existsSync41(p)) return p;
18758
19368
  } catch {
18759
19369
  }
18760
19370
  }
@@ -19136,7 +19746,7 @@ var init_telemetry = __esm({
19136
19746
  init_sync_client();
19137
19747
  cliVersion = () => {
19138
19748
  try {
19139
- return true ? "0.7.18" : "dev";
19749
+ return true ? "0.7.19" : "dev";
19140
19750
  } catch {
19141
19751
  return "dev";
19142
19752
  }
@@ -19516,7 +20126,7 @@ __export(auto_update_exports, {
19516
20126
  tarballUrl: () => tarballUrl
19517
20127
  });
19518
20128
  import { execSync as execSync2, execFileSync as execFileSync3 } from "node:child_process";
19519
- import { readFileSync as readFileSync35, existsSync as existsSync41 } from "node:fs";
20129
+ import { readFileSync as readFileSync35, existsSync as existsSync42 } from "node:fs";
19520
20130
  import { fileURLToPath as fileURLToPath2 } from "node:url";
19521
20131
  import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync6, readdirSync as readdirSync30, statSync as statSync38, mkdirSync as mkdirSync9 } from "node:fs";
19522
20132
  import { join as join42, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
@@ -19677,7 +20287,7 @@ function resolveNpm(loc) {
19677
20287
  }
19678
20288
  function realInstall(tgz) {
19679
20289
  const prefix = runningPrefix();
19680
- const npm = resolveNpm({ execPath: process.execPath, prefix, platform: process.platform, env: process.env, exists: existsSync41 });
20290
+ const npm = resolveNpm({ execPath: process.execPath, prefix, platform: process.platform, env: process.env, exists: existsSync42 });
19681
20291
  if (prefix) {
19682
20292
  npmInstall(npm, ["install", "-g", "--prefix", prefix, tgz]);
19683
20293
  return;
@@ -20435,6 +21045,11 @@ async function syncToTargetInScope(cred, opts = {}) {
20435
21045
  };
20436
21046
  const localSessionIds = /* @__PURE__ */ new Set();
20437
21047
  const shippedHash = /* @__PURE__ */ new Map();
21048
+ const shippedLayout = /* @__PURE__ */ new Map();
21049
+ const layoutOf = (id) => {
21050
+ const layout = shippedLayout.get(id);
21051
+ return layout ? { layout } : {};
21052
+ };
20438
21053
  const makeBatcher = (field, maxItems, flatten = false, onFlush, barrier) => {
20439
21054
  let batch = [];
20440
21055
  let batchBytes = 0;
@@ -20467,7 +21082,7 @@ async function syncToTargetInScope(cred, opts = {}) {
20467
21082
  markSynced(base3, rows.map((r) => {
20468
21083
  const off = typeof r.from_offset === "number" ? r.from_offset : 0;
20469
21084
  const hash = shippedHash.get(r.session_id);
20470
- return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true, full: true } : { id: r.session_id, mtime: r.mtime, hash };
21085
+ return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true, full: true, ...layoutOf(r.session_id) } : { id: r.session_id, mtime: r.mtime, hash };
20471
21086
  }));
20472
21087
  } catch {
20473
21088
  }
@@ -20510,8 +21125,9 @@ async function syncToTargetInScope(cred, opts = {}) {
20510
21125
  // size = new offset (file grew to here)
20511
21126
  acked: true,
20512
21127
  // server merged this tail
20513
- chunkFinal: c.final === true
21128
+ chunkFinal: c.final === true,
20514
21129
  // completes a chunked full sync
21130
+ ...layoutOf(c.session_id)
20515
21131
  })));
20516
21132
  } catch {
20517
21133
  }
@@ -20543,9 +21159,9 @@ async function syncToTargetInScope(cred, opts = {}) {
20543
21159
  const mtime = conv.mtime;
20544
21160
  try {
20545
21161
  if (staged || !guarded) {
20546
- markSynced(base3, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true, chunkedHead: true }]);
21162
+ markSynced(base3, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true, chunkedHead: true, ...layoutOf(id) }]);
20547
21163
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
20548
- markSynced(base3, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true, chunkedHead: true }]);
21164
+ markSynced(base3, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true, chunkedHead: true, ...layoutOf(id) }]);
20549
21165
  } else {
20550
21166
  markSynced(base3, [{ id, mtime }]);
20551
21167
  }
@@ -20640,8 +21256,8 @@ async function syncToTargetInScope(cred, opts = {}) {
20640
21256
  if (!ledger) return ref.mtime >= (opts.sinceMs ?? 0) ? "full" : "skip";
20641
21257
  const ack = ledger.get(ref.prefixedId);
20642
21258
  const backend = backendFor(ref);
20643
- const split = !!backend?.spansMultipleSources?.(ref.prefixedId);
20644
- const isAO = !!backend?.isAppendOnly?.() && !split;
21259
+ const sameLayout = (ack?.L ?? "") === (backend?.sourceLayout?.(ref.prefixedId) ?? "");
21260
+ const isAO = !!backend?.isAppendOnly?.() && sameLayout;
20645
21261
  const size = isAO ? backend?.fileSize?.(ref.prefixedId) ?? 0 : 0;
20646
21262
  return syncMode(ack, ref.mtime, size, extractorVersionForTool(ref.toolId), isAO);
20647
21263
  };
@@ -20688,6 +21304,9 @@ async function syncToTargetInScope(cred, opts = {}) {
20688
21304
  skipped++;
20689
21305
  continue;
20690
21306
  }
21307
+ const layout = backendFor(ref)?.sourceLayout?.(ref.prefixedId) ?? "";
21308
+ if (layout) shippedLayout.set(ref.prefixedId, layout);
21309
+ else shippedLayout.delete(ref.prefixedId);
20691
21310
  trace?.(`build (${mode}) ${ref.prefixedId} \xB7 ${(sessionFileBytes(ref) / 1048576).toFixed(1)}MB`);
20692
21311
  let promotedForFindings = false;
20693
21312
  if (mode === "append" && ledger) {
@@ -20733,7 +21352,7 @@ async function syncToTargetInScope(cred, opts = {}) {
20733
21352
  const off = getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId) ?? 0;
20734
21353
  const priorCursor = ackRow?.s ?? 0;
20735
21354
  const safeToAck = off > 0 && off <= priorCursor;
20736
- markSynced(base3, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
21355
+ markSynced(base3, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true, ...layoutOf(ref.prefixedId) } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
20737
21356
  } catch {
20738
21357
  }
20739
21358
  }
@@ -21488,7 +22107,7 @@ var init_sync_client = __esm({
21488
22107
  ownVersion = JSON.parse(readFileSync36(join43(dir, "../package.json"), "utf-8")).version || ownVersion;
21489
22108
  } catch {
21490
22109
  try {
21491
- if (true) ownVersion = "0.7.18";
22110
+ if (true) ownVersion = "0.7.19";
21492
22111
  } catch {
21493
22112
  }
21494
22113
  }
@@ -21666,7 +22285,7 @@ var init_artifact_codec = __esm({
21666
22285
  });
21667
22286
 
21668
22287
  // ../engine/dist/core/toolkit-sync.js
21669
- import { existsSync as existsSync42, mkdirSync as mkdirSync11, readFileSync as readFileSync38, writeFileSync as writeFileSync10, cpSync } from "fs";
22288
+ import { existsSync as existsSync43, mkdirSync as mkdirSync11, readFileSync as readFileSync38, writeFileSync as writeFileSync10, cpSync } from "fs";
21670
22289
  import { dirname as dirname20, join as join45, basename as basename22 } from "path";
21671
22290
  import { homedir as homedir17 } from "os";
21672
22291
  function rowFromMemoryItem(type, item) {
@@ -21700,11 +22319,11 @@ function skillsDirFor(tool) {
21700
22319
  }
21701
22320
  function copySkill(row, toTool) {
21702
22321
  const skillDir = row.extra.skillDir || dirname20(row.filePath);
21703
- if (!existsSync42(skillDir))
22322
+ if (!existsSync43(skillDir))
21704
22323
  return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
21705
22324
  const name = row.extra.skillName || basename22(skillDir);
21706
22325
  const targetDir = join45(skillsDirFor(toTool), name);
21707
- if (existsSync42(targetDir))
22326
+ if (existsSync43(targetDir))
21708
22327
  return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
21709
22328
  try {
21710
22329
  mkdirSync11(dirname20(targetDir), { recursive: true });
@@ -21736,7 +22355,7 @@ function readMcpEntry(tool, name) {
21736
22355
  return entry;
21737
22356
  }
21738
22357
  for (const t of tries) {
21739
- if (!existsSync42(t.path))
22358
+ if (!existsSync43(t.path))
21740
22359
  continue;
21741
22360
  try {
21742
22361
  const cfg = JSON.parse(readFileSync38(t.path, "utf-8"));
@@ -21749,7 +22368,7 @@ function readMcpEntry(tool, name) {
21749
22368
  return null;
21750
22369
  }
21751
22370
  function readCodexMcpEntry(path, target) {
21752
- if (!existsSync42(path))
22371
+ if (!existsSync43(path))
21753
22372
  return null;
21754
22373
  let content;
21755
22374
  try {
@@ -21803,7 +22422,7 @@ function readCodexMcpEntry(path, target) {
21803
22422
  }
21804
22423
  function writeCodexMcpEntry(path, name, entry) {
21805
22424
  let content = "";
21806
- if (existsSync42(path))
22425
+ if (existsSync43(path))
21807
22426
  content = readFileSync38(path, "utf-8");
21808
22427
  const header = `[mcp_servers.${name}]`;
21809
22428
  if (content.includes(header))
@@ -21861,7 +22480,7 @@ function writeMcpEntry(toTool, name, entry) {
21861
22480
  key2 = "mcp";
21862
22481
  }
21863
22482
  let cfg = {};
21864
- if (existsSync42(path)) {
22483
+ if (existsSync43(path)) {
21865
22484
  try {
21866
22485
  cfg = JSON.parse(readFileSync38(path, "utf-8"));
21867
22486
  } catch {
@@ -21925,7 +22544,7 @@ function copyCommandOrAgent(type, row, toTool) {
21925
22544
  return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
21926
22545
  }
21927
22546
  const out = emit(type, art, toTool);
21928
- if (existsSync42(out.path))
22547
+ if (existsSync43(out.path))
21929
22548
  return { ok: false, status: 409, error: `Already exists: ${out.path}` };
21930
22549
  try {
21931
22550
  mkdirSync11(dirname20(out.path), { recursive: true });
@@ -21945,7 +22564,7 @@ function copyInstructions(row, toTool) {
21945
22564
  return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
21946
22565
  }
21947
22566
  const out = emit("instructions", art, toTool, row.projectPath || void 0);
21948
- if (existsSync42(out.path))
22567
+ if (existsSync43(out.path))
21949
22568
  return { ok: false, status: 409, error: `Already exists: ${out.path}` };
21950
22569
  try {
21951
22570
  mkdirSync11(dirname20(out.path), { recursive: true });
@@ -22088,7 +22707,7 @@ var init_toolkit_sync = __esm({
22088
22707
  });
22089
22708
 
22090
22709
  // ../engine/dist/core/which.js
22091
- import { existsSync as existsSync44, statSync as statSync41, accessSync, constants } from "node:fs";
22710
+ import { existsSync as existsSync45, statSync as statSync41, accessSync, constants } from "node:fs";
22092
22711
  import { join as join47, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
22093
22712
  function executable(p) {
22094
22713
  try {
@@ -22129,7 +22748,7 @@ function resolveOnPath(name) {
22129
22748
  continue;
22130
22749
  for (const c of candidates(name)) {
22131
22750
  const full = join47(dir, c);
22132
- if (existsSync44(full) && executable(full))
22751
+ if (existsSync45(full) && executable(full))
22133
22752
  return full;
22134
22753
  }
22135
22754
  }
@@ -22155,7 +22774,7 @@ __export(toolkit_pull_exports, {
22155
22774
  platformRefusal: () => platformRefusal,
22156
22775
  portableCommand: () => portableCommand
22157
22776
  });
22158
- import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync42, writeFileSync as writeFileSync12 } from "node:fs";
22777
+ import { existsSync as existsSync46, mkdirSync as mkdirSync13, statSync as statSync42, writeFileSync as writeFileSync12 } from "node:fs";
22159
22778
  import { dirname as dirname22, join as join48, basename as basename23, isAbsolute as isAbsolute3 } from "node:path";
22160
22779
  function isExecutableFile(p) {
22161
22780
  try {
@@ -22328,7 +22947,7 @@ function executePull(rows, opts = {}) {
22328
22947
  for (const tool of tools) {
22329
22948
  const dir = join48(skillsDirFor(tool), name);
22330
22949
  const file = join48(dir, "SKILL.md");
22331
- if (existsSync45(file)) {
22950
+ if (existsSync46(file)) {
22332
22951
  outcomes.push({ type: "skill", name, tool, status: "present" });
22333
22952
  continue;
22334
22953
  }
@@ -22373,7 +22992,7 @@ function executePull(rows, opts = {}) {
22373
22992
  outcomes.push({ type, name, tool, status: "failed", reason: e instanceof Error ? e.message : "emit failed" });
22374
22993
  continue;
22375
22994
  }
22376
- if (existsSync45(out.path)) {
22995
+ if (existsSync46(out.path)) {
22377
22996
  outcomes.push({ type, name, tool, status: "present" });
22378
22997
  continue;
22379
22998
  }
@@ -22837,7 +23456,7 @@ var init_code_intel = __esm({
22837
23456
  });
22838
23457
 
22839
23458
  // ../engine/dist/core/companions.js
22840
- import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync46, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream as createWriteStream2, readFileSync as readFileSync43, writeFileSync as writeFileSync15 } from "node:fs";
23459
+ import { existsSync as existsSync49, mkdirSync as mkdirSync16, statSync as statSync46, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream as createWriteStream2, readFileSync as readFileSync43, writeFileSync as writeFileSync15 } from "node:fs";
22841
23460
  import { homedir as homedir19 } from "node:os";
22842
23461
  import { join as join52 } from "node:path";
22843
23462
  import { execSync as execSync3 } from "node:child_process";
@@ -22960,7 +23579,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
22960
23579
  renameSync5(tmpPath, CODEINDEX_BIN_PATH);
22961
23580
  } catch (err) {
22962
23581
  try {
22963
- if (existsSync48(tmpPath))
23582
+ if (existsSync49(tmpPath))
22964
23583
  unlinkSync4(tmpPath);
22965
23584
  } catch {
22966
23585
  }
@@ -24047,7 +24666,7 @@ __export(service_installer_exports, {
24047
24666
  uninstallService: () => uninstallService
24048
24667
  });
24049
24668
  import { execSync as execSync5 } from "child_process";
24050
- import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync45, rmSync as rmSync8, writeFileSync as writeFileSync16 } from "fs";
24669
+ import { existsSync as existsSync50, mkdirSync as mkdirSync17, readFileSync as readFileSync45, rmSync as rmSync8, writeFileSync as writeFileSync16 } from "fs";
24051
24670
  import { homedir as homedir20 } from "os";
24052
24671
  import { join as join54 } from "path";
24053
24672
  import { fileURLToPath as fileURLToPath5 } from "url";
@@ -24083,7 +24702,7 @@ function refreshServiceDefinition() {
24083
24702
  try {
24084
24703
  const { watchJs, node, logFile } = resolveDaemonPaths();
24085
24704
  const unitPath = join54(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
24086
- if (!existsSync49(unitPath)) return false;
24705
+ if (!existsSync50(unitPath)) return false;
24087
24706
  const current = readFileSync45(unitPath, "utf8");
24088
24707
  if (!current.includes(watchJs)) return false;
24089
24708
  const wanted = renderSystemdUnit(watchJs, node, logFile);
@@ -24251,7 +24870,7 @@ function uninstallService() {
24251
24870
  } catch {
24252
24871
  }
24253
24872
  const unitPath = join54(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
24254
- if (existsSync49(unitPath)) {
24873
+ if (existsSync50(unitPath)) {
24255
24874
  rmSync8(unitPath, { force: true });
24256
24875
  removed = true;
24257
24876
  }
@@ -24268,7 +24887,7 @@ function uninstallService() {
24268
24887
  } catch {
24269
24888
  }
24270
24889
  let removed = false;
24271
- if (existsSync49(plistPath)) {
24890
+ if (existsSync50(plistPath)) {
24272
24891
  rmSync8(plistPath, { force: true });
24273
24892
  removed = true;
24274
24893
  }
@@ -24326,12 +24945,12 @@ init_codex2();
24326
24945
  init_cursor();
24327
24946
  init_opencode();
24328
24947
  init_agy();
24329
- import { existsSync as existsSync46, readFileSync as readFileSync40, writeFileSync as writeFileSync13, appendFileSync, mkdirSync as mkdirSync14 } from "node:fs";
24948
+ import { existsSync as existsSync47, readFileSync as readFileSync40, writeFileSync as writeFileSync13, appendFileSync, mkdirSync as mkdirSync14 } from "node:fs";
24330
24949
  import { join as join49 } from "node:path";
24331
24950
 
24332
24951
  // src/project-tasks.ts
24333
24952
  init_http();
24334
- import { existsSync as existsSync43, readFileSync as readFileSync39, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, statSync as statSync40 } from "node:fs";
24953
+ import { existsSync as existsSync44, readFileSync as readFileSync39, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, statSync as statSync40 } from "node:fs";
24335
24954
  import { join as join46, dirname as dirname21 } from "node:path";
24336
24955
  import { homedir as homedir18 } from "node:os";
24337
24956
 
@@ -24439,7 +25058,7 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
24439
25058
  }
24440
25059
  for (const project of projects) {
24441
25060
  const file = join46(project, fk.filename);
24442
- if (!existsSync43(file)) continue;
25061
+ if (!existsSync44(file)) continue;
24443
25062
  let mtime;
24444
25063
  try {
24445
25064
  mtime = statSync40(file).mtimeMs;
@@ -24500,7 +25119,7 @@ function instructionTargets(rootPath, isGlobal) {
24500
25119
  const tools = installed.length ? installed : [INSTRUCTION_TOOLS[0]];
24501
25120
  if (isGlobal) return [...new Set(tools.map((t) => instructionsPath(t.id)))];
24502
25121
  if (!rootPath) return [];
24503
- const present = INSTRUCTION_FILENAMES.map((f) => join49(rootPath, f)).filter((p) => existsSync46(p));
25122
+ const present = INSTRUCTION_FILENAMES.map((f) => join49(rootPath, f)).filter((p) => existsSync47(p));
24504
25123
  if (present.length) return present;
24505
25124
  return [...new Set(tools.map((t) => join49(rootPath, instructionsFilename(t.id))))];
24506
25125
  }
@@ -24528,7 +25147,7 @@ function applyCodeRecommendation(intent) {
24528
25147
  mkdirSync14(dir, { recursive: true });
24529
25148
  } catch {
24530
25149
  }
24531
- const existing = existsSync46(file) ? readFileSync40(file, "utf8") : "";
25150
+ const existing = existsSync47(file) ? readFileSync40(file, "utf8") : "";
24532
25151
  if (existing.includes(text)) {
24533
25152
  skipped.push(file);
24534
25153
  continue;
@@ -24630,7 +25249,7 @@ try {
24630
25249
  ownVersion2 = JSON.parse(readFileSync40(join49(dir, "../package.json"), "utf-8")).version || ownVersion2;
24631
25250
  } catch {
24632
25251
  try {
24633
- if (true) ownVersion2 = "0.7.18";
25252
+ if (true) ownVersion2 = "0.7.19";
24634
25253
  } catch {
24635
25254
  }
24636
25255
  }
@@ -24971,8 +25590,8 @@ function rotateLogIfLarge(logPath, maxBytes = LOG_MAX_BYTES) {
24971
25590
 
24972
25591
  // src/collector-migrate.ts
24973
25592
  init_http();
24974
- import { existsSync as existsSync47 } from "node:fs";
24975
- function selectProjectsToReindex(projects, current, existsFn = existsSync47) {
25593
+ import { existsSync as existsSync48 } from "node:fs";
25594
+ function selectProjectsToReindex(projects, current, existsFn = existsSync48) {
24976
25595
  const reindex = [];
24977
25596
  const missing = [];
24978
25597
  for (const p of projects) {
@@ -25014,8 +25633,8 @@ async function runCollectorMigration(opts) {
25014
25633
  // auto-indexer/indexer.ts
25015
25634
  init_sync_ledger();
25016
25635
  init_collector_health();
25017
- import { existsSync as existsSync50, readFileSync as readFileSync46, statSync as statSync47 } from "fs";
25018
- var CLI_VERSION3 = true ? "0.7.18" : "0.0.0";
25636
+ import { existsSync as existsSync51, readFileSync as readFileSync46, statSync as statSync47 } from "fs";
25637
+ var CLI_VERSION3 = true ? "0.7.19" : "0.0.0";
25019
25638
  var DEBOUNCE_MS = 5e3;
25020
25639
  var PLANS_DIR = claudeBackend.plansDir();
25021
25640
  var TASKS_DIR = claudeBackend.tasksDir();
@@ -25167,6 +25786,16 @@ var opencodeWatcher = chokidar.watch(
25167
25786
  ...POLL(1e4)
25168
25787
  }
25169
25788
  );
25789
+ var HERMES_DB_PATHS = hermesBackend.dbPaths();
25790
+ var hermesWatcher = chokidar.watch(
25791
+ HERMES_DB_PATHS.flatMap((p) => [p, `${p}-wal`]),
25792
+ {
25793
+ persistent: true,
25794
+ ignoreInitial: true,
25795
+ awaitWriteFinish: { stabilityThreshold: 2500, pollInterval: 500 },
25796
+ ...POLL(1e4)
25797
+ }
25798
+ );
25170
25799
  var agentMemoryWatcher = chokidar.watch(CLAUDE_SESSION_ROOTS.map((root) => `${root}/*/memory/*.md`), {
25171
25800
  persistent: true,
25172
25801
  ignoreInitial: true,
@@ -25187,6 +25816,7 @@ var watchers = {
25187
25816
  diary: diaryWatcher,
25188
25817
  codex: codexWatcher,
25189
25818
  opencode: opencodeWatcher,
25819
+ hermes: hermesWatcher,
25190
25820
  agentMemory: agentMemoryWatcher,
25191
25821
  cleanup: cleanupWatcher
25192
25822
  };
@@ -25222,6 +25852,7 @@ daemonLog.info(` Watching history: ${HISTORY_PATH}`);
25222
25852
  daemonLog.info(` Watching diary: ${DIARY_DIR}`);
25223
25853
  daemonLog.info(` Watching codex: ${CODEX_SESSIONS_DIR}`);
25224
25854
  daemonLog.info(` Watching opencode: ${OPENCODE_DB_DIR}`);
25855
+ daemonLog.info(` Watching hermes: ${HERMES_DB_PATHS.join(", ")}`);
25225
25856
  daemonLog.info(` Watching agent memory: ${CLAUDE_SESSION_ROOTS.map((root) => `${root}/*/memory/`).join(", ")}`);
25226
25857
  daemonLog.info(` Resume-guard: ${CURRENT_RESUME_PATH}`);
25227
25858
  daemonLog.info(` Debounce: ${DEBOUNCE_MS}ms \xB7 ships via syncIncremental() to the configured server`);
@@ -25416,7 +26047,7 @@ function discoverWorkspaces() {
25416
26047
  const isExcluded = (p) => excluded.some((x) => p.includes(x));
25417
26048
  const candidates2 = [...newest.entries()].filter(([p]) => !isExcluded(p)).filter(([p]) => !isPersonalPath(p, includeProjects)).filter(([p]) => {
25418
26049
  try {
25419
- return existsSync50(p);
26050
+ return existsSync51(p);
25420
26051
  } catch {
25421
26052
  return false;
25422
26053
  }
@@ -25448,7 +26079,7 @@ function isCodeWorkspace(p) {
25448
26079
  }
25449
26080
  return WORKSPACE_MARKERS.some((m) => {
25450
26081
  try {
25451
- return existsSync50(join55(p, m));
26082
+ return existsSync51(join55(p, m));
25452
26083
  } catch {
25453
26084
  return false;
25454
26085
  }