crispy-recall 0.2.1 → 0.3.0

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/stop-hook.js CHANGED
@@ -153,21 +153,21 @@ var require_file_uri_to_path = __commonJS({
153
153
  var rest = decodeURI(uri.substring(7));
154
154
  var firstSlash = rest.indexOf("/");
155
155
  var host = rest.substring(0, firstSlash);
156
- var path2 = rest.substring(firstSlash + 1);
156
+ var path = rest.substring(firstSlash + 1);
157
157
  if ("localhost" == host)
158
158
  host = "";
159
159
  if (host) {
160
160
  host = sep + sep + host;
161
161
  }
162
- path2 = path2.replace(/^(.+)\|/, "$1:");
162
+ path = path.replace(/^(.+)\|/, "$1:");
163
163
  if (sep == "\\") {
164
- path2 = path2.replace(/\//g, "\\");
164
+ path = path.replace(/\//g, "\\");
165
165
  }
166
- if (/^.+\:/.test(path2)) {
166
+ if (/^.+\:/.test(path)) {
167
167
  } else {
168
- path2 = sep + path2;
168
+ path = sep + path;
169
169
  }
170
- return host + path2;
170
+ return host + path;
171
171
  }
172
172
  }
173
173
  });
@@ -176,18 +176,18 @@ var require_file_uri_to_path = __commonJS({
176
176
  var require_bindings = __commonJS({
177
177
  "node_modules/bindings/bindings.js"(exports2, module2) {
178
178
  var fs3 = require("fs");
179
- var path2 = require("path");
179
+ var path = require("path");
180
180
  var fileURLToPath = require_file_uri_to_path();
181
- var join5 = path2.join;
182
- var dirname2 = path2.dirname;
183
- var exists = fs3.accessSync && function(path3) {
181
+ var join4 = path.join;
182
+ var dirname2 = path.dirname;
183
+ var exists = fs3.accessSync && function(path2) {
184
184
  try {
185
- fs3.accessSync(path3);
185
+ fs3.accessSync(path2);
186
186
  } catch (e) {
187
187
  return false;
188
188
  }
189
189
  return true;
190
- } || fs3.existsSync || path2.existsSync;
190
+ } || fs3.existsSync || path.existsSync;
191
191
  var defaults = {
192
192
  arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
193
193
  compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
@@ -233,13 +233,13 @@ var require_bindings = __commonJS({
233
233
  if (!opts.module_root) {
234
234
  opts.module_root = exports2.getRoot(exports2.getFileName());
235
235
  }
236
- if (path2.extname(opts.bindings) != ".node") {
236
+ if (path.extname(opts.bindings) != ".node") {
237
237
  opts.bindings += ".node";
238
238
  }
239
239
  var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
240
240
  var tries = [], i = 0, l = opts.try.length, n, b, err;
241
241
  for (; i < l; i++) {
242
- n = join5.apply(
242
+ n = join4.apply(
243
243
  null,
244
244
  opts.try[i].map(function(p) {
245
245
  return opts[p] || p;
@@ -300,7 +300,7 @@ var require_bindings = __commonJS({
300
300
  if (dir === ".") {
301
301
  dir = process.cwd();
302
302
  }
303
- if (exists(join5(dir, "package.json")) || exists(join5(dir, "node_modules"))) {
303
+ if (exists(join4(dir, "package.json")) || exists(join4(dir, "node_modules"))) {
304
304
  return dir;
305
305
  }
306
306
  if (prev === dir) {
@@ -309,7 +309,7 @@ var require_bindings = __commonJS({
309
309
  );
310
310
  }
311
311
  prev = dir;
312
- dir = join5(dir, "..");
312
+ dir = join4(dir, "..");
313
313
  }
314
314
  };
315
315
  }
@@ -477,7 +477,7 @@ var require_backup = __commonJS({
477
477
  "node_modules/better-sqlite3/lib/methods/backup.js"(exports2, module2) {
478
478
  "use strict";
479
479
  var fs3 = require("fs");
480
- var path2 = require("path");
480
+ var path = require("path");
481
481
  var { promisify } = require("util");
482
482
  var { cppdb } = require_util();
483
483
  var fsAccess = promisify(fs3.access);
@@ -501,7 +501,7 @@ var require_backup = __commonJS({
501
501
  throw new TypeError('The "attached" option cannot be an empty string');
502
502
  if (handler != null && typeof handler !== "function")
503
503
  throw new TypeError('Expected the "progress" option to be a function');
504
- await fsAccess(path2.dirname(filename)).catch(() => {
504
+ await fsAccess(path.dirname(filename)).catch(() => {
505
505
  throw new TypeError("Cannot save backup because the directory does not exist");
506
506
  });
507
507
  const isNewFile = await fsAccess(filename).then(() => false, () => true);
@@ -832,7 +832,7 @@ var require_database = __commonJS({
832
832
  "node_modules/better-sqlite3/lib/database.js"(exports2, module2) {
833
833
  "use strict";
834
834
  var fs3 = require("fs");
835
- var path2 = require("path");
835
+ var path = require("path");
836
836
  var util = require_util();
837
837
  var SqliteError = require_sqlite_error();
838
838
  var DEFAULT_ADDON;
@@ -879,7 +879,7 @@ var require_database = __commonJS({
879
879
  addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
880
880
  } else if (typeof nativeBinding === "string") {
881
881
  const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : require;
882
- addon = requireFunc(path2.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
882
+ addon = requireFunc(path.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
883
883
  } else {
884
884
  addon = nativeBinding;
885
885
  }
@@ -887,7 +887,7 @@ var require_database = __commonJS({
887
887
  addon.setErrorConstructor(SqliteError);
888
888
  addon.isInitialized = true;
889
889
  }
890
- if (!anonymous && !filename.startsWith("file:") && !fs3.existsSync(path2.dirname(filename))) {
890
+ if (!anonymous && !filename.startsWith("file:") && !fs3.existsSync(path.dirname(filename))) {
891
891
  throw new TypeError("Cannot open database because the directory does not exist");
892
892
  }
893
893
  Object.defineProperties(this, {
@@ -933,6 +933,7 @@ var init_quantize = __esm({
933
933
  // src/hooks/stop-hook.ts
934
934
  var stop_hook_exports = {};
935
935
  __export(stop_hook_exports, {
936
+ resolveIngestTarget: () => resolveIngestTarget,
936
937
  vendorForTranscript: () => vendorForTranscript
937
938
  });
938
939
  module.exports = __toCommonJS(stop_hook_exports);
@@ -986,6 +987,15 @@ Run \`recall doctor\` for details.`
986
987
  this.name = "BindingLoadError";
987
988
  }
988
989
  };
990
+ var MigrationPendingError = class extends Error {
991
+ constructor(dbPath2) {
992
+ super(
993
+ "recall: this database needs a one-time schema migration \u2014 run `recall install` to finish it. (Normal commands refuse to rewrite the index unattended.)"
994
+ );
995
+ this.dbPath = dbPath2;
996
+ this.name = "MigrationPendingError";
997
+ }
998
+ };
989
999
  function isBindingLoadError(e) {
990
1000
  if (!e || typeof e !== "object")
991
1001
  return false;
@@ -1000,7 +1010,7 @@ function isBindingLoadError(e) {
1000
1010
  }
1001
1011
  var db = null;
1002
1012
  var currentDbPath = null;
1003
- function getDb(dbPath2) {
1013
+ function getDb(dbPath2, opts) {
1004
1014
  if (db && currentDbPath === dbPath2)
1005
1015
  return db;
1006
1016
  if (db) {
@@ -1015,12 +1025,45 @@ function getDb(dbPath2) {
1015
1025
  raw.close();
1016
1026
  throw e;
1017
1027
  }
1018
- db = createAdapter(raw);
1028
+ const adapter = createAdapter(raw);
1029
+ if (isRetrievalMigrationPending(adapter)) {
1030
+ if (!opts?.allowPendingMigration) {
1031
+ raw.close();
1032
+ throw new MigrationPendingError(dbPath2);
1033
+ }
1034
+ db = adapter;
1035
+ currentDbPath = dbPath2;
1036
+ log({ source: "db", level: "info", summary: `DB: opened pending-migration DB at ${dbPath2} (installer mode)` });
1037
+ return db;
1038
+ }
1039
+ db = adapter;
1019
1040
  currentDbPath = dbPath2;
1020
1041
  ensureSchema(db);
1021
1042
  log({ source: "db", level: "info", summary: `DB: initialized at ${dbPath2}` });
1022
1043
  return db;
1023
1044
  }
1045
+ var RETRIEVAL_MIGRATION_KEY = "retrieval_class_migration";
1046
+ function isRetrievalMigrationPending(d) {
1047
+ try {
1048
+ const hasMessages = d.get(
1049
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'messages'`
1050
+ );
1051
+ if (!hasMessages)
1052
+ return false;
1053
+ const hasMeta = d.get(
1054
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'schema_meta'`
1055
+ );
1056
+ if (!hasMeta)
1057
+ return true;
1058
+ const row = d.get(
1059
+ `SELECT value FROM schema_meta WHERE key = ?`,
1060
+ [RETRIEVAL_MIGRATION_KEY]
1061
+ );
1062
+ return row?.value !== "complete";
1063
+ } catch {
1064
+ return true;
1065
+ }
1066
+ }
1024
1067
  function closeDb() {
1025
1068
  if (db) {
1026
1069
  db.close();
@@ -1131,16 +1174,18 @@ function cleanupWasmArtifacts(dbPath2) {
1131
1174
  } catch {
1132
1175
  }
1133
1176
  }
1134
- function ensureSchema(db3) {
1135
- db3.exec(`
1177
+ var RETRIEVAL_SCHEMA_DDL = {
1178
+ /** messages + provenance tables + indexes (no FTS objects). */
1179
+ tables: `
1136
1180
  CREATE TABLE IF NOT EXISTS messages (
1137
- message_id TEXT PRIMARY KEY,
1138
- session_id TEXT NOT NULL,
1139
- message_seq INTEGER NOT NULL,
1140
- message_text TEXT NOT NULL,
1141
- project_id TEXT,
1142
- created_at INTEGER NOT NULL,
1143
- message_role TEXT,
1181
+ message_id TEXT PRIMARY KEY,
1182
+ session_id TEXT NOT NULL,
1183
+ message_seq INTEGER NOT NULL,
1184
+ message_text TEXT NOT NULL,
1185
+ project_id TEXT,
1186
+ created_at INTEGER NOT NULL,
1187
+ message_role TEXT,
1188
+ retrieval_class TEXT NOT NULL DEFAULT 'hot',
1144
1189
  UNIQUE(session_id, message_id)
1145
1190
  );
1146
1191
 
@@ -1152,68 +1197,134 @@ function ensureSchema(db3) {
1152
1197
  -- embed drain and every Stop-hook catch-up. This index serves the ORDER BY so
1153
1198
  -- the planner walks it and early-terminates at LIMIT instead.
1154
1199
  CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at);
1155
- `);
1156
- db3.exec(`
1200
+
1201
+ -- session_provenance \u2014 durable per-session classification evidence:
1202
+ -- canonical id, vendor, root-vs-agent kind, parent thread, hook/agent
1203
+ -- metadata, and the transcript path \u2192 canonical id mapping that lets
1204
+ -- later T1/mtime scans resolve a child to ONE identity.
1205
+ CREATE TABLE IF NOT EXISTS session_provenance (
1206
+ session_id TEXT PRIMARY KEY,
1207
+ vendor TEXT NOT NULL,
1208
+ kind TEXT NOT NULL,
1209
+ parent_session_id TEXT,
1210
+ agent_depth INTEGER,
1211
+ agent_meta TEXT,
1212
+ transcript_path TEXT,
1213
+ updated_at INTEGER NOT NULL
1214
+ );
1215
+ CREATE INDEX IF NOT EXISTS idx_provenance_path ON session_provenance(transcript_path);
1216
+
1217
+ -- session_aliases \u2014 alternate identifiers (e.g. a hook agent_id that
1218
+ -- differs from the child rollout's session-meta UUID) \u2192 canonical id.
1219
+ CREATE TABLE IF NOT EXISTS session_aliases (
1220
+ alias_id TEXT PRIMARY KEY,
1221
+ session_id TEXT NOT NULL,
1222
+ source TEXT,
1223
+ created_at INTEGER NOT NULL
1224
+ );
1225
+
1226
+ CREATE TABLE IF NOT EXISTS schema_meta (
1227
+ key TEXT PRIMARY KEY,
1228
+ value TEXT NOT NULL
1229
+ );
1230
+ `,
1231
+ /** Filtered external-content view + FTS + four-state triggers + vocab. */
1232
+ fts: `
1233
+ -- Filtered external-content source: FTS5 rebuild/integrity-check read
1234
+ -- THIS view, so 'rebuild' repopulates only hot rows and the (rank-1)
1235
+ -- integrity check compares against the filtered corpus.
1236
+ CREATE VIEW IF NOT EXISTS searchable_messages AS
1237
+ SELECT rowid, message_text FROM messages WHERE retrieval_class = 'hot';
1238
+
1157
1239
  CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
1158
1240
  message_text,
1159
- content=messages,
1241
+ content=searchable_messages,
1160
1242
  content_rowid=rowid,
1161
1243
  tokenize='porter unicode61'
1162
1244
  );
1163
1245
 
1164
- CREATE TRIGGER IF NOT EXISTS messages_fts_ai AFTER INSERT ON messages BEGIN
1246
+ -- Four-state trigger behavior:
1247
+ -- insert hot \u2192 add | insert agent \u2192 no-op
1248
+ -- delete hot \u2192 delete | delete agent \u2192 no-op
1249
+ -- update hot\u2192hot \u2192 delete old + add new
1250
+ -- update hot\u2192agent \u2192 delete old only
1251
+ -- update agent\u2192hot \u2192 add new only
1252
+ -- update agent\u2192agent \u2192 no-op
1253
+ CREATE TRIGGER IF NOT EXISTS messages_fts_ai AFTER INSERT ON messages
1254
+ WHEN new.retrieval_class = 'hot'
1255
+ BEGIN
1165
1256
  INSERT INTO messages_fts(rowid, message_text) VALUES (new.rowid, new.message_text);
1166
1257
  END;
1167
1258
 
1168
- CREATE TRIGGER IF NOT EXISTS messages_fts_ad AFTER DELETE ON messages BEGIN
1259
+ CREATE TRIGGER IF NOT EXISTS messages_fts_ad AFTER DELETE ON messages
1260
+ WHEN old.retrieval_class = 'hot'
1261
+ BEGIN
1169
1262
  INSERT INTO messages_fts(messages_fts, rowid, message_text)
1170
1263
  VALUES ('delete', old.rowid, old.message_text);
1171
1264
  END;
1172
1265
 
1173
1266
  CREATE TRIGGER IF NOT EXISTS messages_fts_au AFTER UPDATE ON messages BEGIN
1174
1267
  INSERT INTO messages_fts(messages_fts, rowid, message_text)
1175
- VALUES ('delete', old.rowid, old.message_text);
1176
- INSERT INTO messages_fts(rowid, message_text) VALUES (new.rowid, new.message_text);
1268
+ SELECT 'delete', old.rowid, old.message_text WHERE old.retrieval_class = 'hot';
1269
+ INSERT INTO messages_fts(rowid, message_text)
1270
+ SELECT new.rowid, new.message_text WHERE new.retrieval_class = 'hot';
1177
1271
  END;
1178
- `);
1179
- db3.exec(`
1272
+
1180
1273
  CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts_vocab
1181
1274
  USING fts5vocab(messages_fts, 'row');
1182
- `);
1183
- db3.exec(`
1184
- CREATE VIRTUAL TABLE IF NOT EXISTS _stem USING fts5(
1185
- t, tokenize='porter unicode61'
1186
- );
1187
- CREATE VIRTUAL TABLE IF NOT EXISTS _stem_vocab
1188
- USING fts5vocab(_stem, 'row');
1189
- `);
1190
- db3.exec(`
1191
- CREATE TABLE IF NOT EXISTS message_vectors (
1192
- message_id TEXT PRIMARY KEY REFERENCES messages(message_id) ON DELETE CASCADE,
1193
- embedding_q8 BLOB NOT NULL,
1194
- norm REAL NOT NULL,
1195
- quant_scale REAL NOT NULL,
1196
- embed_version INTEGER NOT NULL DEFAULT 1
1197
- );
1198
- `);
1199
- const hasEmbedVersion = () => db3.all(`PRAGMA table_info(message_vectors)`).some((c) => c.name === "embed_version");
1200
- if (!hasEmbedVersion()) {
1275
+ `
1276
+ };
1277
+ function ensureSchema(db3) {
1278
+ db3.exec("BEGIN IMMEDIATE");
1279
+ try {
1280
+ db3.exec(RETRIEVAL_SCHEMA_DDL.tables);
1281
+ db3.exec(RETRIEVAL_SCHEMA_DDL.fts);
1282
+ db3.exec(`
1283
+ CREATE VIRTUAL TABLE IF NOT EXISTS _stem USING fts5(
1284
+ t, tokenize='porter unicode61'
1285
+ );
1286
+ CREATE VIRTUAL TABLE IF NOT EXISTS _stem_vocab
1287
+ USING fts5vocab(_stem, 'row');
1288
+ `);
1289
+ db3.exec(`
1290
+ CREATE TABLE IF NOT EXISTS message_vectors (
1291
+ message_id TEXT PRIMARY KEY REFERENCES messages(message_id) ON DELETE CASCADE,
1292
+ embedding_q8 BLOB NOT NULL,
1293
+ norm REAL NOT NULL,
1294
+ quant_scale REAL NOT NULL,
1295
+ embed_version INTEGER NOT NULL DEFAULT 1
1296
+ );
1297
+ `);
1298
+ const hasEmbedVersion = () => db3.all(`PRAGMA table_info(message_vectors)`).some((c) => c.name === "embed_version");
1299
+ if (!hasEmbedVersion()) {
1300
+ try {
1301
+ db3.exec(`ALTER TABLE message_vectors ADD COLUMN embed_version INTEGER NOT NULL DEFAULT 1`);
1302
+ } catch (e) {
1303
+ if (!hasEmbedVersion())
1304
+ throw e;
1305
+ }
1306
+ }
1307
+ db3.exec(`
1308
+ CREATE TABLE IF NOT EXISTS ingest_watermark (
1309
+ transcript_path TEXT PRIMARY KEY,
1310
+ last_mtime INTEGER NOT NULL,
1311
+ last_size INTEGER NOT NULL,
1312
+ vendor TEXT NOT NULL
1313
+ );
1314
+ CREATE INDEX IF NOT EXISTS idx_watermark_vendor ON ingest_watermark(vendor);
1315
+ `);
1316
+ db3.exec(`
1317
+ INSERT OR IGNORE INTO schema_meta(key, value)
1318
+ VALUES ('${RETRIEVAL_MIGRATION_KEY}', 'complete');
1319
+ `);
1320
+ db3.exec("COMMIT");
1321
+ } catch (e) {
1201
1322
  try {
1202
- db3.exec(`ALTER TABLE message_vectors ADD COLUMN embed_version INTEGER NOT NULL DEFAULT 1`);
1203
- } catch (e) {
1204
- if (!hasEmbedVersion())
1205
- throw e;
1323
+ db3.exec("ROLLBACK");
1324
+ } catch {
1206
1325
  }
1326
+ throw e;
1207
1327
  }
1208
- db3.exec(`
1209
- CREATE TABLE IF NOT EXISTS ingest_watermark (
1210
- transcript_path TEXT PRIMARY KEY,
1211
- last_mtime INTEGER NOT NULL,
1212
- last_size INTEGER NOT NULL,
1213
- vendor TEXT NOT NULL
1214
- );
1215
- CREATE INDEX IF NOT EXISTS idx_watermark_vendor ON ingest_watermark(vendor);
1216
- `);
1217
1328
  }
1218
1329
 
1219
1330
  // src/recall/message-store.ts
@@ -1236,7 +1347,7 @@ function ensureDir2() {
1236
1347
  }
1237
1348
  }
1238
1349
  function insertMessages(messages, opts) {
1239
- if (messages.length === 0 && !opts?.replaceSessionId)
1350
+ if (messages.length === 0 && !opts?.replaceSessionId && !opts?.provenance)
1240
1351
  return;
1241
1352
  ensureDir2();
1242
1353
  const d = db2();
@@ -1252,8 +1363,8 @@ function insertMessages(messages, opts) {
1252
1363
  }
1253
1364
  const stmt = d.prepare(
1254
1365
  `INSERT OR IGNORE INTO messages
1255
- (message_id, session_id, message_seq, message_text, project_id, created_at, message_role)
1256
- VALUES (?, ?, ?, ?, ?, ?, ?)`
1366
+ (message_id, session_id, message_seq, message_text, project_id, created_at, message_role, retrieval_class)
1367
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
1257
1368
  );
1258
1369
  for (const m of messages) {
1259
1370
  stmt.run([
@@ -1263,9 +1374,55 @@ function insertMessages(messages, opts) {
1263
1374
  m.message_text,
1264
1375
  m.project_id,
1265
1376
  m.created_at,
1266
- m.message_role
1377
+ m.message_role,
1378
+ m.retrieval_class ?? "hot"
1267
1379
  ]);
1268
1380
  }
1381
+ if (opts?.provenance) {
1382
+ const p = opts.provenance;
1383
+ d.run(
1384
+ `INSERT INTO session_provenance
1385
+ (session_id, vendor, kind, parent_session_id, agent_depth, agent_meta, transcript_path, updated_at)
1386
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1387
+ ON CONFLICT(session_id) DO UPDATE SET
1388
+ vendor = excluded.vendor,
1389
+ kind = excluded.kind,
1390
+ parent_session_id = COALESCE(excluded.parent_session_id, session_provenance.parent_session_id),
1391
+ agent_depth = COALESCE(excluded.agent_depth, session_provenance.agent_depth),
1392
+ agent_meta = COALESCE(excluded.agent_meta, session_provenance.agent_meta),
1393
+ transcript_path = COALESCE(excluded.transcript_path, session_provenance.transcript_path),
1394
+ updated_at = excluded.updated_at`,
1395
+ [
1396
+ p.sessionId,
1397
+ p.vendor,
1398
+ p.kind,
1399
+ p.parentSessionId,
1400
+ p.agentDepth,
1401
+ p.agentMeta ? JSON.stringify(p.agentMeta) : null,
1402
+ p.transcriptPath,
1403
+ Date.now()
1404
+ ]
1405
+ );
1406
+ if (p.kind === "agent") {
1407
+ d.run(
1408
+ `UPDATE messages SET retrieval_class = 'agent'
1409
+ WHERE session_id = ? AND retrieval_class != 'agent'`,
1410
+ [p.sessionId]
1411
+ );
1412
+ d.run(
1413
+ `DELETE FROM message_vectors WHERE message_id IN
1414
+ (SELECT message_id FROM messages WHERE session_id = ?)`,
1415
+ [p.sessionId]
1416
+ );
1417
+ }
1418
+ }
1419
+ for (const a of opts?.aliases ?? []) {
1420
+ d.run(
1421
+ `INSERT OR REPLACE INTO session_aliases (alias_id, session_id, source, created_at)
1422
+ VALUES (?, ?, ?, ?)`,
1423
+ [a.aliasId, a.sessionId, a.source, Date.now()]
1424
+ );
1425
+ }
1269
1426
  d.exec("COMMIT");
1270
1427
  } catch (e) {
1271
1428
  d.exec("ROLLBACK");
@@ -1273,6 +1430,260 @@ function insertMessages(messages, opts) {
1273
1430
  }
1274
1431
  }
1275
1432
 
1433
+ // src/recall/session-classifier.ts
1434
+ init_paths();
1435
+ init_log();
1436
+
1437
+ // src/adapters/codex/codex-jsonl-reader.ts
1438
+ var fs = __toESM(require("fs"));
1439
+ init_log();
1440
+ function parseCodexJsonlFile(filepath) {
1441
+ try {
1442
+ const content = fs.readFileSync(filepath, "utf-8");
1443
+ const lines = content.split("\n");
1444
+ const records = [];
1445
+ for (const line of lines) {
1446
+ const trimmed = line.trim();
1447
+ if (!trimmed)
1448
+ continue;
1449
+ try {
1450
+ const record = JSON.parse(trimmed);
1451
+ records.push(record);
1452
+ } catch (err) {
1453
+ log({ level: "warn", source: "codex-jsonl-reader", summary: `Skipping unparseable line: ${err.message}` });
1454
+ }
1455
+ }
1456
+ return records;
1457
+ } catch (error) {
1458
+ log({ level: "error", source: "codex-jsonl-reader", summary: `Failed to read ${filepath}: ${error instanceof Error ? error.message : String(error)}`, data: { filepath, error: String(error) } });
1459
+ return [];
1460
+ }
1461
+ }
1462
+ function extractCodexSessionMeta(filepath) {
1463
+ let fd = null;
1464
+ try {
1465
+ fd = fs.openSync(filepath, "r");
1466
+ const buffer = Buffer.alloc(8192);
1467
+ const bytesRead = fs.readSync(fd, buffer, 0, 8192, 0);
1468
+ if (bytesRead === 0)
1469
+ return null;
1470
+ const content = buffer.toString("utf-8", 0, bytesRead);
1471
+ const newlineIdx = content.indexOf("\n");
1472
+ const firstLine = newlineIdx >= 0 ? content.slice(0, newlineIdx) : content;
1473
+ const record = JSON.parse(firstLine.trim());
1474
+ if (record.type !== "session_meta")
1475
+ return null;
1476
+ const payload = record.payload;
1477
+ return {
1478
+ id: payload.id,
1479
+ cwd: payload.cwd,
1480
+ cli_version: payload.cli_version,
1481
+ git: payload.git,
1482
+ ...payload.source && typeof payload.source === "object" ? { source: payload.source } : {}
1483
+ };
1484
+ } catch {
1485
+ return null;
1486
+ } finally {
1487
+ if (fd !== null) {
1488
+ try {
1489
+ fs.closeSync(fd);
1490
+ } catch {
1491
+ }
1492
+ }
1493
+ }
1494
+ }
1495
+ var READ_BUFFER_SIZE = 64 * 1024;
1496
+ var TURN_CONTENT_CAP = 8 * 1024;
1497
+
1498
+ // src/recall/session-classifier.ts
1499
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1500
+ var ROLLOUT_UUID_RE = /rollout-.*-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
1501
+ function normalize(p) {
1502
+ return p.replace(/\\/g, "/");
1503
+ }
1504
+ function basenameNoExt(p) {
1505
+ return normalize(p).split("/").pop().replace(/\.jsonl$/i, "");
1506
+ }
1507
+ function lookupStoredProvenance(transcriptPath) {
1508
+ try {
1509
+ const row = getDb(dbPath()).get(
1510
+ `SELECT session_id, kind, parent_session_id FROM session_provenance
1511
+ WHERE transcript_path = ? LIMIT 1`,
1512
+ [normalize(transcriptPath)]
1513
+ );
1514
+ return row ?? null;
1515
+ } catch {
1516
+ return null;
1517
+ }
1518
+ }
1519
+ function claudeSubagentParent(normPath) {
1520
+ const segments = normPath.split("/");
1521
+ const idx = segments.lastIndexOf("subagents");
1522
+ if (idx <= 0)
1523
+ return null;
1524
+ const parent = segments[idx - 1];
1525
+ return parent || null;
1526
+ }
1527
+ function classifySession(args) {
1528
+ const { sessionId, transcriptPath, vendor, hook } = args;
1529
+ const normPath = normalize(transcriptPath);
1530
+ const base = basenameNoExt(transcriptPath);
1531
+ const stored = lookupStoredProvenance(transcriptPath);
1532
+ if (stored && !(stored.kind !== "agent" && hook?.isSubagent)) {
1533
+ return {
1534
+ kind: stored.kind === "agent" ? "agent" : "root",
1535
+ canonicalSessionId: stored.session_id,
1536
+ parentSessionId: stored.parent_session_id,
1537
+ agentDepth: null,
1538
+ agentMeta: null,
1539
+ aliases: aliasCandidates(stored.session_id, sessionId, hook),
1540
+ evidence: "stored"
1541
+ };
1542
+ }
1543
+ if (vendor === "claude")
1544
+ return classifyClaude(sessionId, normPath, base, hook);
1545
+ return classifyCodex(sessionId, transcriptPath, base, hook);
1546
+ }
1547
+ function aliasCandidates(canonical, proposed, hook) {
1548
+ const out = /* @__PURE__ */ new Set();
1549
+ if (hook?.agentId && hook.agentId !== canonical)
1550
+ out.add(hook.agentId);
1551
+ if (proposed && proposed !== canonical)
1552
+ out.add(proposed);
1553
+ return [...out];
1554
+ }
1555
+ function classifyClaude(sessionId, normPath, base, hook) {
1556
+ if (normPath.includes("/subagents/") && base.startsWith("agent-")) {
1557
+ return {
1558
+ kind: "agent",
1559
+ canonicalSessionId: base,
1560
+ parentSessionId: claudeSubagentParent(normPath) ?? hook?.payloadSessionId ?? null,
1561
+ agentDepth: null,
1562
+ agentMeta: hook?.agentId ? { hookAgentId: hook.agentId } : null,
1563
+ aliases: aliasCandidates(base, sessionId, hook),
1564
+ evidence: "claude-path"
1565
+ };
1566
+ }
1567
+ if (base.startsWith("agent-") || sessionId.startsWith("agent-")) {
1568
+ const canonical = base.startsWith("agent-") ? base : sessionId;
1569
+ return {
1570
+ kind: "agent",
1571
+ canonicalSessionId: canonical,
1572
+ parentSessionId: hook?.payloadSessionId ?? null,
1573
+ agentDepth: null,
1574
+ agentMeta: hook?.agentId ? { hookAgentId: hook.agentId } : null,
1575
+ aliases: aliasCandidates(canonical, sessionId, hook),
1576
+ evidence: "claude-name"
1577
+ };
1578
+ }
1579
+ if (hook?.isSubagent) {
1580
+ return {
1581
+ kind: "agent",
1582
+ canonicalSessionId: base || sessionId,
1583
+ parentSessionId: hook.payloadSessionId ?? null,
1584
+ agentDepth: null,
1585
+ agentMeta: hook.agentId ? { hookAgentId: hook.agentId } : null,
1586
+ aliases: aliasCandidates(base || sessionId, sessionId, hook),
1587
+ evidence: "hook"
1588
+ };
1589
+ }
1590
+ return {
1591
+ kind: "root",
1592
+ canonicalSessionId: sessionId,
1593
+ parentSessionId: null,
1594
+ agentDepth: null,
1595
+ agentMeta: null,
1596
+ aliases: [],
1597
+ evidence: "default-root"
1598
+ };
1599
+ }
1600
+ function classifyCodex(sessionId, transcriptPath, base, hook) {
1601
+ const meta = extractCodexSessionMeta(transcriptPath);
1602
+ const filenameUuid = ROLLOUT_UUID_RE.exec(normalize(transcriptPath))?.[1] ?? null;
1603
+ const canonical = meta?.id && typeof meta.id === "string" && meta.id.length > 0 ? meta.id : filenameUuid ?? (UUID_RE.test(sessionId) ? sessionId : null);
1604
+ const sub = parseSubagentSource(meta?.source, transcriptPath);
1605
+ if (sub.malformed) {
1606
+ log({
1607
+ source: "recall:classify",
1608
+ level: "warn",
1609
+ summary: `codex session_meta.source looks subagent-like but is malformed \u2014 leaving session hot (conservative): ${transcriptPath}`
1610
+ });
1611
+ }
1612
+ if (sub.isSubagent || hook?.isSubagent) {
1613
+ if (!canonical) {
1614
+ log({
1615
+ source: "recall:classify",
1616
+ level: "warn",
1617
+ summary: `codex subagent transcript has no derivable canonical id \u2014 skipping ingest (conservative): ${transcriptPath}`
1618
+ });
1619
+ return {
1620
+ kind: "agent",
1621
+ canonicalSessionId: base || sessionId,
1622
+ parentSessionId: sub.parentThreadId ?? hook?.payloadSessionId ?? null,
1623
+ agentDepth: sub.depth,
1624
+ agentMeta: sub.meta,
1625
+ aliases: [],
1626
+ evidence: sub.isSubagent ? "codex-meta" : "hook",
1627
+ unresolvable: true
1628
+ };
1629
+ }
1630
+ return {
1631
+ kind: "agent",
1632
+ canonicalSessionId: canonical,
1633
+ parentSessionId: sub.parentThreadId ?? hook?.payloadSessionId ?? null,
1634
+ agentDepth: sub.depth,
1635
+ agentMeta: mergeMeta(sub.meta, hook?.agentId),
1636
+ aliases: aliasCandidates(canonical, sessionId, hook),
1637
+ evidence: sub.isSubagent ? "codex-meta" : "hook"
1638
+ };
1639
+ }
1640
+ return {
1641
+ kind: "root",
1642
+ canonicalSessionId: canonical ?? sessionId,
1643
+ parentSessionId: null,
1644
+ agentDepth: null,
1645
+ agentMeta: null,
1646
+ aliases: canonical && canonical !== sessionId ? [sessionId] : [],
1647
+ evidence: "default-root"
1648
+ };
1649
+ }
1650
+ function mergeMeta(meta, hookAgentId) {
1651
+ if (!hookAgentId)
1652
+ return meta;
1653
+ return { ...meta ?? {}, hookAgentId };
1654
+ }
1655
+ function parseSubagentSource(source, _transcriptPath) {
1656
+ const none = { isSubagent: false, malformed: false, parentThreadId: null, depth: null, meta: null };
1657
+ if (!source || typeof source !== "object")
1658
+ return none;
1659
+ const subRaw = source.subagent;
1660
+ const typeTag = source.type;
1661
+ const looksSubagent = subRaw !== void 0 || typeTag === "subagent";
1662
+ if (!looksSubagent)
1663
+ return none;
1664
+ if (subRaw !== void 0 && (typeof subRaw !== "object" || subRaw === null)) {
1665
+ return { ...none, malformed: true };
1666
+ }
1667
+ const sub = subRaw ?? {};
1668
+ const spawnRaw = sub.thread_spawn ?? source.thread_spawn;
1669
+ const spawn2 = spawnRaw && typeof spawnRaw === "object" ? spawnRaw : {};
1670
+ const parentThreadId = typeof spawn2.parent_thread_id === "string" && spawn2.parent_thread_id ? spawn2.parent_thread_id : null;
1671
+ const depth = typeof spawn2.depth === "number" && Number.isFinite(spawn2.depth) ? spawn2.depth : null;
1672
+ const meta = {};
1673
+ for (const k of ["agent_path", "agent_type", "agent_name", "path", "type", "name"]) {
1674
+ const v = spawn2[k] ?? sub[k];
1675
+ if (typeof v === "string" || typeof v === "number")
1676
+ meta[k] = v;
1677
+ }
1678
+ return {
1679
+ isSubagent: true,
1680
+ malformed: false,
1681
+ parentThreadId,
1682
+ depth,
1683
+ meta: Object.keys(meta).length > 0 ? meta : null
1684
+ };
1685
+ }
1686
+
1276
1687
  // src/recall/message-ingest.ts
1277
1688
  init_paths();
1278
1689
 
@@ -1291,13 +1702,13 @@ function normalizePath(p) {
1291
1702
  init_log();
1292
1703
 
1293
1704
  // src/adapters/claude/jsonl-reader.ts
1294
- var fs = __toESM(require("fs"));
1705
+ var fs2 = __toESM(require("fs"));
1295
1706
  init_log();
1296
- var READ_BUFFER_SIZE = 64 * 1024;
1297
- var TURN_CONTENT_CAP = 8 * 1024;
1707
+ var READ_BUFFER_SIZE2 = 64 * 1024;
1708
+ var TURN_CONTENT_CAP2 = 8 * 1024;
1298
1709
  function parseJsonlFile(filepath) {
1299
1710
  try {
1300
- const content = fs.readFileSync(filepath, "utf-8");
1711
+ const content = fs2.readFileSync(filepath, "utf-8");
1301
1712
  const lines = content.split("\n");
1302
1713
  const entries = [];
1303
1714
  for (const line of lines) {
@@ -1486,37 +1897,6 @@ function adaptClaudeEntries(rawEntries) {
1486
1897
  return rawEntries.map(adaptClaudeEntry).filter((entry) => entry !== null);
1487
1898
  }
1488
1899
 
1489
- // src/adapters/codex/codex-jsonl-reader.ts
1490
- var fs2 = __toESM(require("fs"));
1491
- init_log();
1492
- var path = __toESM(require("path"));
1493
- var os = __toESM(require("os"));
1494
- var CODEX_SESSIONS_DIR = path.join(os.homedir(), ".codex", "sessions");
1495
- function parseCodexJsonlFile(filepath) {
1496
- try {
1497
- const content = fs2.readFileSync(filepath, "utf-8");
1498
- const lines = content.split("\n");
1499
- const records = [];
1500
- for (const line of lines) {
1501
- const trimmed = line.trim();
1502
- if (!trimmed)
1503
- continue;
1504
- try {
1505
- const record = JSON.parse(trimmed);
1506
- records.push(record);
1507
- } catch (err) {
1508
- log({ level: "warn", source: "codex-jsonl-reader", summary: `Skipping unparseable line: ${err.message}` });
1509
- }
1510
- }
1511
- return records;
1512
- } catch (error) {
1513
- log({ level: "error", source: "codex-jsonl-reader", summary: `Failed to read ${filepath}: ${error instanceof Error ? error.message : String(error)}`, data: { filepath, error: String(error) } });
1514
- return [];
1515
- }
1516
- }
1517
- var READ_BUFFER_SIZE2 = 64 * 1024;
1518
- var TURN_CONTENT_CAP2 = 8 * 1024;
1519
-
1520
1900
  // src/adapters/codex/codex-jsonl-adapter.ts
1521
1901
  function adaptCodexJsonlRecords(records, sessionId) {
1522
1902
  const outputIndex = /* @__PURE__ */ new Map();
@@ -2151,6 +2531,36 @@ function entriesCwd(entries) {
2151
2531
  return void 0;
2152
2532
  }
2153
2533
  async function ingestSessionMessages(sessionId, transcriptPath, vendor, options) {
2534
+ const classification = classifySession({
2535
+ sessionId,
2536
+ transcriptPath,
2537
+ vendor,
2538
+ ...options?.hook ? { hook: options.hook } : {}
2539
+ });
2540
+ if (classification.unresolvable) {
2541
+ return {
2542
+ sessionId,
2543
+ chunksCreated: 0,
2544
+ skipped: true,
2545
+ error: "subagent transcript has no derivable canonical identity \u2014 skipped (conservative)"
2546
+ };
2547
+ }
2548
+ const canonicalId = classification.canonicalSessionId;
2549
+ const retrievalClass = classification.kind === "agent" ? "agent" : "hot";
2550
+ const provenance = {
2551
+ sessionId: canonicalId,
2552
+ vendor,
2553
+ kind: classification.kind,
2554
+ parentSessionId: classification.parentSessionId,
2555
+ agentDepth: classification.agentDepth,
2556
+ agentMeta: classification.agentMeta,
2557
+ transcriptPath: transcriptPath.replace(/\\/g, "/")
2558
+ };
2559
+ const aliases = classification.aliases.map((aliasId) => ({
2560
+ aliasId,
2561
+ sessionId: canonicalId,
2562
+ source: options?.hook?.agentId === aliasId ? "hook-agent-id" : "ingest-request-id"
2563
+ }));
2154
2564
  let rawEntries;
2155
2565
  try {
2156
2566
  if (vendor === "claude") {
@@ -2158,18 +2568,25 @@ async function ingestSessionMessages(sessionId, transcriptPath, vendor, options)
2158
2568
  rawEntries = adaptClaudeEntries(raw);
2159
2569
  } else {
2160
2570
  const envelopes = parseCodexJsonlFile(transcriptPath);
2161
- rawEntries = adaptCodexJsonlRecords(envelopes, sessionId);
2571
+ rawEntries = adaptCodexJsonlRecords(envelopes, canonicalId);
2162
2572
  }
2163
2573
  } catch (err) {
2164
2574
  return {
2165
- sessionId,
2575
+ sessionId: canonicalId,
2166
2576
  chunksCreated: 0,
2167
2577
  skipped: false,
2168
- error: `Failed to load session: ${err instanceof Error ? err.message : String(err)}`
2578
+ error: `Failed to load session: ${err instanceof Error ? err.message : String(err)}`,
2579
+ retrievalClass
2169
2580
  };
2170
2581
  }
2171
2582
  if (rawEntries.length === 0) {
2172
- return { sessionId, chunksCreated: 0, skipped: true };
2583
+ if (options?.force) {
2584
+ try {
2585
+ insertMessages([], { replaceSessionId: canonicalId, provenance, aliases });
2586
+ } catch {
2587
+ }
2588
+ }
2589
+ return { sessionId: canonicalId, chunksCreated: 0, skipped: true, retrievalClass };
2173
2590
  }
2174
2591
  const rawProjectId = options?.projectId ?? entriesCwd(rawEntries);
2175
2592
  const projectId = rawProjectId ? normalizePath(rawProjectId) : null;
@@ -2186,31 +2603,44 @@ async function ingestSessionMessages(sessionId, transcriptPath, vendor, options)
2186
2603
  const createdAt = entry.timestamp ? new Date(entry.timestamp).getTime() : Date.now();
2187
2604
  records.push({
2188
2605
  message_id: entry.uuid,
2189
- session_id: sessionId,
2606
+ session_id: canonicalId,
2190
2607
  message_seq: i,
2191
2608
  message_text: text,
2192
2609
  project_id: projectId,
2193
2610
  created_at: createdAt,
2194
- message_role: entry.message?.role ?? entry.type ?? null
2611
+ message_role: entry.message?.role ?? entry.type ?? null,
2612
+ retrieval_class: retrievalClass
2195
2613
  });
2196
2614
  }
2197
2615
  if (records.length === 0) {
2198
- return { sessionId, chunksCreated: 0, skipped: true };
2616
+ if (options?.force) {
2617
+ try {
2618
+ insertMessages([], { replaceSessionId: canonicalId, provenance, aliases });
2619
+ } catch {
2620
+ }
2621
+ }
2622
+ return { sessionId: canonicalId, chunksCreated: 0, skipped: true, retrievalClass };
2199
2623
  }
2200
2624
  try {
2201
- insertMessages(records, options?.force ? { replaceSessionId: sessionId } : void 0);
2625
+ insertMessages(records, {
2626
+ ...options?.force ? { replaceSessionId: canonicalId } : {},
2627
+ provenance,
2628
+ aliases
2629
+ });
2202
2630
  } catch (err) {
2203
2631
  return {
2204
- sessionId,
2632
+ sessionId: canonicalId,
2205
2633
  chunksCreated: 0,
2206
2634
  skipped: false,
2207
- error: `DB insert failed: ${err instanceof Error ? err.message : String(err)}`
2635
+ error: `DB insert failed: ${err instanceof Error ? err.message : String(err)}`,
2636
+ retrievalClass
2208
2637
  };
2209
2638
  }
2210
2639
  return {
2211
- sessionId,
2640
+ sessionId: canonicalId,
2212
2641
  chunksCreated: records.length,
2213
- skipped: false
2642
+ skipped: false,
2643
+ retrievalClass
2214
2644
  };
2215
2645
  }
2216
2646
 
@@ -2219,9 +2649,35 @@ init_paths();
2219
2649
  var import_fs = require("fs");
2220
2650
  var import_child_process = require("child_process");
2221
2651
  var import_path = require("path");
2222
- function vendorForTranscript(transcriptPath) {
2652
+ function vendorForTranscript(transcriptPath, codexHome = process.env["CODEX_HOME"]) {
2223
2653
  const p = transcriptPath.replace(/\\/g, "/");
2224
- return p.includes("/.codex/") ? "codex" : "claude";
2654
+ const root = codexHome?.replace(/\\/g, "/").replace(/\/$/, "");
2655
+ const underCodexHome = root ? p === root || p.startsWith(`${root}/`) : false;
2656
+ return p.includes("/.codex/") || underCodexHome ? "codex" : "claude";
2657
+ }
2658
+ function resolveIngestTarget(payload) {
2659
+ if (payload.agent_transcript_path) {
2660
+ const transcriptPath = payload.agent_transcript_path;
2661
+ const base = transcriptPath.replace(/\\/g, "/").split("/").pop().replace(/\.jsonl$/i, "");
2662
+ if (!base)
2663
+ return null;
2664
+ return {
2665
+ sessionId: base,
2666
+ transcriptPath,
2667
+ hook: {
2668
+ ...payload.session_id ? { payloadSessionId: payload.session_id } : {},
2669
+ ...payload.agent_id ? { agentId: payload.agent_id } : {},
2670
+ isSubagent: true
2671
+ }
2672
+ };
2673
+ }
2674
+ if (!payload.session_id || !payload.transcript_path)
2675
+ return null;
2676
+ return {
2677
+ sessionId: payload.session_id,
2678
+ transcriptPath: payload.transcript_path,
2679
+ hook: { payloadSessionId: payload.session_id, isSubagent: false }
2680
+ };
2225
2681
  }
2226
2682
  function logStopHook(line) {
2227
2683
  try {
@@ -2240,40 +2696,47 @@ async function runStopHook() {
2240
2696
  } catch {
2241
2697
  process.exit(0);
2242
2698
  }
2243
- if (!payload?.session_id || !payload?.transcript_path)
2244
- process.exit(0);
2245
2699
  if (payload.stop_hook_active)
2246
2700
  process.exit(0);
2701
+ const target = resolveIngestTarget(payload);
2702
+ if (!target)
2703
+ process.exit(0);
2704
+ let ingestedClass;
2705
+ let canonicalId = target.sessionId;
2247
2706
  try {
2248
2707
  getDb(dbPath()).exec("PRAGMA busy_timeout = 5000;");
2249
- const vendor = vendorForTranscript(payload.transcript_path);
2708
+ const vendor = vendorForTranscript(target.transcriptPath);
2250
2709
  const result = await ingestSessionMessages(
2251
- payload.session_id,
2252
- payload.transcript_path,
2710
+ target.sessionId,
2711
+ target.transcriptPath,
2253
2712
  vendor,
2254
- { projectId: payload.cwd ?? void 0 }
2713
+ { projectId: payload.cwd ?? void 0, hook: target.hook }
2255
2714
  );
2715
+ ingestedClass = result?.retrievalClass;
2716
+ canonicalId = result?.sessionId ?? target.sessionId;
2256
2717
  if (result?.error) {
2257
2718
  logStopHook(
2258
- `${(/* @__PURE__ */ new Date()).toISOString()} ingest-failed sid=${payload.session_id} err=${result.error}
2719
+ `${(/* @__PURE__ */ new Date()).toISOString()} ingest-failed sid=${target.sessionId} err=${result.error}
2259
2720
  `
2260
2721
  );
2261
2722
  }
2262
2723
  } catch (e) {
2263
2724
  logStopHook(
2264
- `${(/* @__PURE__ */ new Date()).toISOString()} ingest-failed sid=${payload.session_id} err=${e.message}
2725
+ `${(/* @__PURE__ */ new Date()).toISOString()} ingest-failed sid=${target.sessionId} err=${e.message}
2265
2726
  `
2266
2727
  );
2267
2728
  }
2268
- (0, import_child_process.spawn)(
2269
- process.execPath,
2270
- [(0, import_path.join)(binDir(), "embed-pending.js"), payload.session_id],
2271
- {
2272
- detached: true,
2273
- stdio: "ignore",
2274
- windowsHide: true
2275
- }
2276
- ).unref();
2729
+ if (!target.hook.isSubagent && ingestedClass !== "agent") {
2730
+ (0, import_child_process.spawn)(
2731
+ process.execPath,
2732
+ [(0, import_path.join)(binDir(), "embed-pending.js"), canonicalId],
2733
+ {
2734
+ detached: true,
2735
+ stdio: "ignore",
2736
+ windowsHide: true
2737
+ }
2738
+ ).unref();
2739
+ }
2277
2740
  process.exit(0);
2278
2741
  }
2279
2742
  if (typeof require !== "undefined" && typeof module !== "undefined" && require.main === module) {
@@ -2281,5 +2744,6 @@ if (typeof require !== "undefined" && typeof module !== "undefined" && require.m
2281
2744
  }
2282
2745
  // Annotate the CommonJS export names for ESM import in node:
2283
2746
  0 && (module.exports = {
2747
+ resolveIngestTarget,
2284
2748
  vendorForTranscript
2285
2749
  });