@deeplake/hivemind 0.7.31 → 0.7.32

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.
@@ -53,19 +53,19 @@ var init_index_marker_store = __esm({
53
53
  });
54
54
 
55
55
  // dist/src/hooks/codex/stop.js
56
- import { readFileSync as readFileSync8, existsSync as existsSync9 } from "node:fs";
56
+ import { readFileSync as readFileSync10, existsSync as existsSync10 } from "node:fs";
57
57
  import { fileURLToPath as fileURLToPath3 } from "node:url";
58
- import { dirname as dirname4, join as join15 } from "node:path";
58
+ import { dirname as dirname5, join as join17 } from "node:path";
59
59
 
60
60
  // dist/src/utils/stdin.js
61
61
  function readStdin() {
62
- return new Promise((resolve, reject) => {
62
+ return new Promise((resolve2, reject) => {
63
63
  let data = "";
64
64
  process.stdin.setEncoding("utf-8");
65
65
  process.stdin.on("data", (chunk) => data += chunk);
66
66
  process.stdin.on("end", () => {
67
67
  try {
68
- resolve(JSON.parse(data));
68
+ resolve2(JSON.parse(data));
69
69
  } catch (err) {
70
70
  reject(new Error(`Failed to parse hook input: ${err}`));
71
71
  }
@@ -181,7 +181,7 @@ function getQueryTimeoutMs() {
181
181
  return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
182
182
  }
183
183
  function sleep(ms) {
184
- return new Promise((resolve) => setTimeout(resolve, ms));
184
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
185
185
  }
186
186
  function isTimeoutError(error) {
187
187
  const name = error instanceof Error ? error.name.toLowerCase() : "";
@@ -211,7 +211,7 @@ var Semaphore = class {
211
211
  this.active++;
212
212
  return;
213
213
  }
214
- await new Promise((resolve) => this.waiting.push(resolve));
214
+ await new Promise((resolve2) => this.waiting.push(resolve2));
215
215
  }
216
216
  release() {
217
217
  this.active--;
@@ -1175,9 +1175,9 @@ function buildSessionPath(config, sessionId) {
1175
1175
  // dist/src/embeddings/client.js
1176
1176
  import { connect } from "node:net";
1177
1177
  import { spawn as spawn3 } from "node:child_process";
1178
- import { openSync as openSync3, closeSync as closeSync3, writeSync as writeSync3, unlinkSync as unlinkSync3, existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
1179
- import { homedir as homedir10 } from "node:os";
1180
- import { join as join13 } from "node:path";
1178
+ import { openSync as openSync4, closeSync as closeSync4, writeSync as writeSync3, unlinkSync as unlinkSync4, existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
1179
+ import { homedir as homedir13 } from "node:os";
1180
+ import { join as join16 } from "node:path";
1181
1181
 
1182
1182
  // dist/src/embeddings/protocol.js
1183
1183
  var DEFAULT_SOCKET_DIR = "/tmp";
@@ -1190,13 +1190,234 @@ function pidPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
1190
1190
  return `${dir}/hivemind-embed-${uid}.pid`;
1191
1191
  }
1192
1192
 
1193
+ // dist/src/notifications/queue.js
1194
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, renameSync as renameSync4, mkdirSync as mkdirSync8, openSync as openSync3, closeSync as closeSync3, unlinkSync as unlinkSync3, statSync } from "node:fs";
1195
+ import { join as join13, resolve } from "node:path";
1196
+ import { homedir as homedir10 } from "node:os";
1197
+ import { setTimeout as sleep2 } from "node:timers/promises";
1198
+ var log3 = (msg) => log("notifications-queue", msg);
1199
+ var LOCK_RETRY_MAX = 50;
1200
+ var LOCK_RETRY_BASE_MS = 5;
1201
+ var LOCK_STALE_MS = 5e3;
1202
+ function queuePath() {
1203
+ return join13(homedir10(), ".deeplake", "notifications-queue.json");
1204
+ }
1205
+ function lockPath3() {
1206
+ return `${queuePath()}.lock`;
1207
+ }
1208
+ function readQueue() {
1209
+ try {
1210
+ const raw = readFileSync7(queuePath(), "utf-8");
1211
+ const parsed = JSON.parse(raw);
1212
+ if (!parsed || !Array.isArray(parsed.queue)) {
1213
+ log3(`queue malformed \u2192 treating as empty`);
1214
+ return { queue: [] };
1215
+ }
1216
+ return { queue: parsed.queue };
1217
+ } catch {
1218
+ return { queue: [] };
1219
+ }
1220
+ }
1221
+ function _isQueuePathInsideHome(path, home) {
1222
+ const r = resolve(path);
1223
+ const h = resolve(home);
1224
+ return r.startsWith(h + "/") || r === h;
1225
+ }
1226
+ function writeQueue(q) {
1227
+ const path = queuePath();
1228
+ const home = resolve(homedir10());
1229
+ if (!_isQueuePathInsideHome(path, home)) {
1230
+ throw new Error(`notifications-queue write blocked: ${path} is outside ${home}`);
1231
+ }
1232
+ mkdirSync8(join13(home, ".deeplake"), { recursive: true, mode: 448 });
1233
+ const tmp = `${path}.${process.pid}.tmp`;
1234
+ writeFileSync7(tmp, JSON.stringify(q, null, 2), { mode: 384 });
1235
+ renameSync4(tmp, path);
1236
+ }
1237
+ async function withQueueLock(fn) {
1238
+ const path = lockPath3();
1239
+ mkdirSync8(join13(homedir10(), ".deeplake"), { recursive: true, mode: 448 });
1240
+ let fd = null;
1241
+ for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
1242
+ try {
1243
+ fd = openSync3(path, "wx", 384);
1244
+ break;
1245
+ } catch (e) {
1246
+ const code = e.code;
1247
+ if (code !== "EEXIST")
1248
+ throw e;
1249
+ try {
1250
+ const age = Date.now() - statSync(path).mtimeMs;
1251
+ if (age > LOCK_STALE_MS) {
1252
+ unlinkSync3(path);
1253
+ continue;
1254
+ }
1255
+ } catch {
1256
+ }
1257
+ const delay = LOCK_RETRY_BASE_MS * (attempt + 1);
1258
+ await sleep2(delay);
1259
+ }
1260
+ }
1261
+ if (fd === null) {
1262
+ log3(`lock acquisition gave up after ${LOCK_RETRY_MAX} attempts \u2014 proceeding unlocked (last-writer-wins)`);
1263
+ return fn();
1264
+ }
1265
+ try {
1266
+ return fn();
1267
+ } finally {
1268
+ try {
1269
+ closeSync3(fd);
1270
+ } catch {
1271
+ }
1272
+ try {
1273
+ unlinkSync3(path);
1274
+ } catch {
1275
+ }
1276
+ }
1277
+ }
1278
+ function sameDedupKey(a, b) {
1279
+ if (a.id !== b.id)
1280
+ return false;
1281
+ return JSON.stringify(a.dedupKey) === JSON.stringify(b.dedupKey);
1282
+ }
1283
+ async function enqueueNotification(n) {
1284
+ await withQueueLock(() => {
1285
+ const q = readQueue();
1286
+ if (q.queue.some((existing) => sameDedupKey(existing, n))) {
1287
+ return;
1288
+ }
1289
+ q.queue.push(n);
1290
+ writeQueue(q);
1291
+ });
1292
+ }
1293
+
1294
+ // dist/src/embeddings/disable.js
1295
+ import { createRequire as createRequire2 } from "node:module";
1296
+ import { homedir as homedir12 } from "node:os";
1297
+ import { join as join15 } from "node:path";
1298
+ import { pathToFileURL } from "node:url";
1299
+
1300
+ // dist/src/user-config.js
1301
+ import { existsSync as existsSync8, mkdirSync as mkdirSync9, readFileSync as readFileSync8, renameSync as renameSync5, writeFileSync as writeFileSync8 } from "node:fs";
1302
+ import { homedir as homedir11 } from "node:os";
1303
+ import { dirname as dirname4, join as join14 } from "node:path";
1304
+ var _configPath = () => process.env.HIVEMIND_CONFIG_PATH ?? join14(homedir11(), ".deeplake", "config.json");
1305
+ var _cache = null;
1306
+ var _migrated = false;
1307
+ function readUserConfig() {
1308
+ if (_cache !== null)
1309
+ return _cache;
1310
+ const path = _configPath();
1311
+ if (!existsSync8(path)) {
1312
+ _cache = {};
1313
+ return _cache;
1314
+ }
1315
+ try {
1316
+ const raw = readFileSync8(path, "utf-8");
1317
+ const parsed = JSON.parse(raw);
1318
+ _cache = isPlainObject(parsed) ? parsed : {};
1319
+ } catch {
1320
+ _cache = {};
1321
+ }
1322
+ return _cache;
1323
+ }
1324
+ function writeUserConfig(patch) {
1325
+ const current = readUserConfig();
1326
+ const merged = deepMerge(current, patch);
1327
+ const path = _configPath();
1328
+ const dir = dirname4(path);
1329
+ if (!existsSync8(dir))
1330
+ mkdirSync9(dir, { recursive: true });
1331
+ const tmp = `${path}.tmp.${process.pid}`;
1332
+ writeFileSync8(tmp, JSON.stringify(merged, null, 2) + "\n", "utf-8");
1333
+ renameSync5(tmp, path);
1334
+ _cache = merged;
1335
+ return merged;
1336
+ }
1337
+ function getEmbeddingsEnabled() {
1338
+ const cfg = readUserConfig();
1339
+ if (cfg.embeddings && typeof cfg.embeddings.enabled === "boolean") {
1340
+ return cfg.embeddings.enabled;
1341
+ }
1342
+ if (_migrated) {
1343
+ return migrationValueFromEnv();
1344
+ }
1345
+ _migrated = true;
1346
+ const enabled = migrationValueFromEnv();
1347
+ try {
1348
+ writeUserConfig({ embeddings: { enabled } });
1349
+ } catch {
1350
+ _cache = { ...cfg ?? {}, embeddings: { ...cfg?.embeddings ?? {}, enabled } };
1351
+ }
1352
+ return enabled;
1353
+ }
1354
+ function migrationValueFromEnv() {
1355
+ const raw = process.env.HIVEMIND_EMBEDDINGS;
1356
+ if (raw === void 0)
1357
+ return false;
1358
+ if (raw === "false")
1359
+ return false;
1360
+ return true;
1361
+ }
1362
+ function isPlainObject(value) {
1363
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1364
+ }
1365
+ function deepMerge(base, patch) {
1366
+ const out = { ...base };
1367
+ for (const key of Object.keys(patch)) {
1368
+ const patchVal = patch[key];
1369
+ const baseVal = base[key];
1370
+ if (isPlainObject(patchVal) && isPlainObject(baseVal)) {
1371
+ out[key] = { ...baseVal, ...patchVal };
1372
+ } else if (patchVal !== void 0) {
1373
+ out[key] = patchVal;
1374
+ }
1375
+ }
1376
+ return out;
1377
+ }
1378
+
1379
+ // dist/src/embeddings/disable.js
1380
+ var cachedStatus = null;
1381
+ function defaultResolveTransformers() {
1382
+ const sharedDir = join15(homedir12(), ".hivemind", "embed-deps");
1383
+ try {
1384
+ createRequire2(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
1385
+ return;
1386
+ } catch {
1387
+ }
1388
+ createRequire2(import.meta.url).resolve("@huggingface/transformers");
1389
+ }
1390
+ var _resolve = defaultResolveTransformers;
1391
+ var _readEnabled = getEmbeddingsEnabled;
1392
+ function detectStatus() {
1393
+ if (!_readEnabled())
1394
+ return "user-disabled";
1395
+ try {
1396
+ _resolve();
1397
+ return "enabled";
1398
+ } catch {
1399
+ return "no-transformers";
1400
+ }
1401
+ }
1402
+ function embeddingsStatus() {
1403
+ if (cachedStatus !== null)
1404
+ return cachedStatus;
1405
+ cachedStatus = detectStatus();
1406
+ return cachedStatus;
1407
+ }
1408
+ function embeddingsDisabled() {
1409
+ return embeddingsStatus() !== "enabled";
1410
+ }
1411
+
1193
1412
  // dist/src/embeddings/client.js
1194
- var SHARED_DAEMON_PATH = join13(homedir10(), ".hivemind", "embed-deps", "embed-daemon.js");
1195
- var log3 = (m) => log("embed-client", m);
1413
+ var SHARED_DAEMON_PATH = join16(homedir13(), ".hivemind", "embed-deps", "embed-daemon.js");
1414
+ var log4 = (m) => log("embed-client", m);
1196
1415
  function getUid() {
1197
1416
  const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
1198
1417
  return uid !== void 0 ? String(uid) : process.env.USER ?? "default";
1199
1418
  }
1419
+ var _signalledMissingDeps = false;
1420
+ var _recycledStuckDaemon = false;
1200
1421
  var EmbedClient = class {
1201
1422
  socketPath;
1202
1423
  pidPath;
@@ -1205,13 +1426,14 @@ var EmbedClient = class {
1205
1426
  autoSpawn;
1206
1427
  spawnWaitMs;
1207
1428
  nextId = 0;
1429
+ helloVerified = false;
1208
1430
  constructor(opts = {}) {
1209
1431
  const uid = getUid();
1210
1432
  const dir = opts.socketDir ?? "/tmp";
1211
1433
  this.socketPath = socketPathFor(uid, dir);
1212
1434
  this.pidPath = pidPathFor(uid, dir);
1213
1435
  this.timeoutMs = opts.timeoutMs ?? DEFAULT_CLIENT_TIMEOUT_MS;
1214
- this.daemonEntry = opts.daemonEntry ?? process.env.HIVEMIND_EMBED_DAEMON ?? (existsSync8(SHARED_DAEMON_PATH) ? SHARED_DAEMON_PATH : void 0);
1436
+ this.daemonEntry = opts.daemonEntry ?? process.env.HIVEMIND_EMBED_DAEMON ?? (existsSync9(SHARED_DAEMON_PATH) ? SHARED_DAEMON_PATH : void 0);
1215
1437
  this.autoSpawn = opts.autoSpawn ?? true;
1216
1438
  this.spawnWaitMs = opts.spawnWaitMs ?? 5e3;
1217
1439
  }
@@ -1221,8 +1443,33 @@ var EmbedClient = class {
1221
1443
  *
1222
1444
  * Fire-and-forget spawn on miss: if the daemon isn't up, this call returns
1223
1445
  * null AND kicks off a background spawn. The next call finds a ready daemon.
1446
+ *
1447
+ * Stuck-daemon recycle: if the daemon returns a transformers-missing
1448
+ * error (typical after a marketplace upgrade left an older daemon process
1449
+ * alive but with no node_modules accessible from its bundle path), we
1450
+ * SIGTERM it and clear its sock/pid so the very next call spawns a fresh
1451
+ * daemon from the current bundle. Without this, the stuck daemon would
1452
+ * keep poisoning every session until its 10-minute idle-out fires.
1224
1453
  */
1225
1454
  async embed(text, kind = "document") {
1455
+ const v = await this.embedAttempt(text, kind);
1456
+ if (v !== "recycled")
1457
+ return v;
1458
+ if (!this.autoSpawn)
1459
+ return null;
1460
+ this.trySpawnDaemon();
1461
+ await this.waitForDaemonReady();
1462
+ const retry = await this.embedAttempt(text, kind);
1463
+ return retry === "recycled" ? null : retry;
1464
+ }
1465
+ /**
1466
+ * One round-trip: connect → verify → embed. Returns:
1467
+ * - number[] : embedding vector (happy path)
1468
+ * - null : timeout / daemon error / transformers-missing
1469
+ * - "recycled": verifyDaemonOnce killed the daemon mid-call;
1470
+ * caller should respawn and retry once.
1471
+ */
1472
+ async embedAttempt(text, kind) {
1226
1473
  let sock;
1227
1474
  try {
1228
1475
  sock = await this.connectOnce();
@@ -1232,17 +1479,25 @@ var EmbedClient = class {
1232
1479
  return null;
1233
1480
  }
1234
1481
  try {
1482
+ const recycled = await this.verifyDaemonOnce(sock);
1483
+ if (recycled) {
1484
+ return "recycled";
1485
+ }
1235
1486
  const id = String(++this.nextId);
1236
1487
  const req = { op: "embed", id, kind, text };
1237
1488
  const resp = await this.sendAndWait(sock, req);
1238
1489
  if (resp.error || !("embedding" in resp) || !resp.embedding) {
1239
- log3(`embed err: ${resp.error ?? "no embedding"}`);
1490
+ const err = resp.error ?? "no embedding";
1491
+ log4(`embed err: ${err}`);
1492
+ if (isTransformersMissingError(err)) {
1493
+ this.handleTransformersMissing(err);
1494
+ }
1240
1495
  return null;
1241
1496
  }
1242
1497
  return resp.embedding;
1243
1498
  } catch (e) {
1244
1499
  const err = e instanceof Error ? e.message : String(e);
1245
- log3(`embed failed: ${err}`);
1500
+ log4(`embed failed: ${err}`);
1246
1501
  return null;
1247
1502
  } finally {
1248
1503
  try {
@@ -1251,6 +1506,139 @@ var EmbedClient = class {
1251
1506
  }
1252
1507
  }
1253
1508
  }
1509
+ /**
1510
+ * Poll for the sock file to come back after `trySpawnDaemon` — used by
1511
+ * the recycle retry path. Best-effort: caps at `spawnWaitMs` and
1512
+ * returns regardless so the retry attempt can run.
1513
+ */
1514
+ async waitForDaemonReady() {
1515
+ const deadline = Date.now() + this.spawnWaitMs;
1516
+ while (Date.now() < deadline) {
1517
+ if (existsSync9(this.socketPath))
1518
+ return;
1519
+ await new Promise((r) => setTimeout(r, 50));
1520
+ }
1521
+ }
1522
+ /**
1523
+ * Send a `hello` on first successful connect per EmbedClient instance.
1524
+ * If the daemon answers with a path that doesn't match our configured
1525
+ * daemonEntry — typical after a marketplace upgrade replaced the bundle
1526
+ * — SIGTERM the daemon + clear sock/pid so the next call spawns from the
1527
+ * current bundle.
1528
+ *
1529
+ * `helloVerified` is set ONLY after we've seen a compatible response,
1530
+ * so a transient probe failure or a recycle-triggering mismatch leaves
1531
+ * the flag false; the next reconnect re-runs verification against
1532
+ * whatever daemon is then live (typically the fresh spawn).
1533
+ */
1534
+ async verifyDaemonOnce(sock) {
1535
+ if (this.helloVerified)
1536
+ return false;
1537
+ if (!this.daemonEntry) {
1538
+ this.helloVerified = true;
1539
+ return false;
1540
+ }
1541
+ const id = String(++this.nextId);
1542
+ const req = { op: "hello", id };
1543
+ let resp;
1544
+ try {
1545
+ resp = await this.sendAndWait(sock, req);
1546
+ } catch (e) {
1547
+ log4(`hello probe failed (inconclusive, will retry next connect): ${e instanceof Error ? e.message : String(e)}`);
1548
+ return false;
1549
+ }
1550
+ const hello = resp;
1551
+ if (_recycledStuckDaemon) {
1552
+ return false;
1553
+ }
1554
+ if (!hello.daemonPath) {
1555
+ _recycledStuckDaemon = true;
1556
+ log4(`daemon does not implement hello (older protocol); recycling`);
1557
+ this.recycleDaemon(hello.pid);
1558
+ return true;
1559
+ }
1560
+ if (hello.daemonPath !== this.daemonEntry && !existsSync9(hello.daemonPath)) {
1561
+ _recycledStuckDaemon = true;
1562
+ log4(`daemon path no longer on disk \u2014 running=${hello.daemonPath} (gone) expected=${this.daemonEntry}; recycling`);
1563
+ this.recycleDaemon(hello.pid);
1564
+ return true;
1565
+ }
1566
+ this.helloVerified = true;
1567
+ return false;
1568
+ }
1569
+ /**
1570
+ * On a transformers-missing error from the daemon, SIGTERM the stuck
1571
+ * daemon (the bundle daemon that can't find its deps) and clear
1572
+ * sock/pid so the next call spawns fresh. Also enqueue a one-time
1573
+ * notification telling the user to run `hivemind embeddings install`
1574
+ * — but only when the user has opted in. Suppressed when
1575
+ * embeddingsStatus() === "user-disabled" so we don't nag users who
1576
+ * explicitly chose to turn embeddings off.
1577
+ */
1578
+ handleTransformersMissing(detail) {
1579
+ if (!_recycledStuckDaemon) {
1580
+ _recycledStuckDaemon = true;
1581
+ this.recycleDaemon(null);
1582
+ }
1583
+ if (_signalledMissingDeps)
1584
+ return;
1585
+ _signalledMissingDeps = true;
1586
+ let status;
1587
+ try {
1588
+ status = embeddingsStatus();
1589
+ } catch {
1590
+ status = "enabled";
1591
+ }
1592
+ if (status === "user-disabled")
1593
+ return;
1594
+ enqueueNotification({
1595
+ id: "embed-deps-missing",
1596
+ severity: "warn",
1597
+ title: "Hivemind embeddings disabled \u2014 deps missing",
1598
+ body: `Semantic memory search is off because @huggingface/transformers is not installed where the daemon can find it. Run \`hivemind embeddings install\` to enable.`,
1599
+ dedupKey: { reason: "transformers-missing", detail: detail.slice(0, 200) }
1600
+ }).catch((e) => {
1601
+ log4(`enqueue embed-deps-missing failed: ${e instanceof Error ? e.message : String(e)}`);
1602
+ });
1603
+ }
1604
+ /**
1605
+ * Best-effort SIGTERM + sock/pid cleanup. Tolerant of every missing-file
1606
+ * combination and dead-PID cases.
1607
+ *
1608
+ * Identity check: gate the SIGTERM on the daemon's socket file still
1609
+ * existing. We know the daemon was alive moments ago (we either just
1610
+ * got a hello response or the caller saw a transformers-missing error
1611
+ * the daemon emitted), but if the socket file is gone by the time we
1612
+ * try to kill, the daemon process is also gone and the PID we
1613
+ * captured may already have been recycled by the OS to an unrelated
1614
+ * user process. Mirrors the gate added to `killEmbedDaemon` in the
1615
+ * CLI — same failure mode, rarer trigger.
1616
+ */
1617
+ recycleDaemon(reportedPid) {
1618
+ let pid = reportedPid;
1619
+ if (pid === null) {
1620
+ try {
1621
+ pid = Number.parseInt(readFileSync9(this.pidPath, "utf-8").trim(), 10);
1622
+ } catch {
1623
+ }
1624
+ }
1625
+ if (Number.isFinite(pid) && pid !== null && pid > 0 && existsSync9(this.socketPath)) {
1626
+ try {
1627
+ process.kill(pid, "SIGTERM");
1628
+ } catch {
1629
+ }
1630
+ } else if (pid !== null) {
1631
+ log4(`recycle: socket gone, skipping SIGTERM on possibly-stale pid ${pid}`);
1632
+ }
1633
+ try {
1634
+ unlinkSync4(this.socketPath);
1635
+ } catch {
1636
+ }
1637
+ try {
1638
+ unlinkSync4(this.pidPath);
1639
+ } catch {
1640
+ }
1641
+ }
1254
1642
  /**
1255
1643
  * Wait up to spawnWaitMs for the daemon to accept connections, spawning if
1256
1644
  * necessary. Meant for SessionStart / long-running batches — not the hot path.
@@ -1274,7 +1662,7 @@ var EmbedClient = class {
1274
1662
  }
1275
1663
  }
1276
1664
  connectOnce() {
1277
- return new Promise((resolve, reject) => {
1665
+ return new Promise((resolve2, reject) => {
1278
1666
  const sock = connect(this.socketPath);
1279
1667
  const to = setTimeout(() => {
1280
1668
  sock.destroy();
@@ -1282,7 +1670,7 @@ var EmbedClient = class {
1282
1670
  }, this.timeoutMs);
1283
1671
  sock.once("connect", () => {
1284
1672
  clearTimeout(to);
1285
- resolve(sock);
1673
+ resolve2(sock);
1286
1674
  });
1287
1675
  sock.once("error", (e) => {
1288
1676
  clearTimeout(to);
@@ -1293,16 +1681,16 @@ var EmbedClient = class {
1293
1681
  trySpawnDaemon() {
1294
1682
  let fd;
1295
1683
  try {
1296
- fd = openSync3(this.pidPath, "wx", 384);
1684
+ fd = openSync4(this.pidPath, "wx", 384);
1297
1685
  writeSync3(fd, String(process.pid));
1298
1686
  } catch (e) {
1299
1687
  if (this.isPidFileStale()) {
1300
1688
  try {
1301
- unlinkSync3(this.pidPath);
1689
+ unlinkSync4(this.pidPath);
1302
1690
  } catch {
1303
1691
  }
1304
1692
  try {
1305
- fd = openSync3(this.pidPath, "wx", 384);
1693
+ fd = openSync4(this.pidPath, "wx", 384);
1306
1694
  writeSync3(fd, String(process.pid));
1307
1695
  } catch {
1308
1696
  return;
@@ -1311,11 +1699,11 @@ var EmbedClient = class {
1311
1699
  return;
1312
1700
  }
1313
1701
  }
1314
- if (!this.daemonEntry || !existsSync8(this.daemonEntry)) {
1315
- log3(`daemonEntry not configured or missing: ${this.daemonEntry}`);
1702
+ if (!this.daemonEntry || !existsSync9(this.daemonEntry)) {
1703
+ log4(`daemonEntry not configured or missing: ${this.daemonEntry}`);
1316
1704
  try {
1317
- closeSync3(fd);
1318
- unlinkSync3(this.pidPath);
1705
+ closeSync4(fd);
1706
+ unlinkSync4(this.pidPath);
1319
1707
  } catch {
1320
1708
  }
1321
1709
  return;
@@ -1327,14 +1715,14 @@ var EmbedClient = class {
1327
1715
  env: process.env
1328
1716
  });
1329
1717
  child.unref();
1330
- log3(`spawned daemon pid=${child.pid}`);
1718
+ log4(`spawned daemon pid=${child.pid}`);
1331
1719
  } finally {
1332
- closeSync3(fd);
1720
+ closeSync4(fd);
1333
1721
  }
1334
1722
  }
1335
1723
  isPidFileStale() {
1336
1724
  try {
1337
- const raw = readFileSync7(this.pidPath, "utf-8").trim();
1725
+ const raw = readFileSync9(this.pidPath, "utf-8").trim();
1338
1726
  const pid = Number(raw);
1339
1727
  if (!pid || Number.isNaN(pid))
1340
1728
  return true;
@@ -1352,9 +1740,9 @@ var EmbedClient = class {
1352
1740
  const deadline = Date.now() + this.spawnWaitMs;
1353
1741
  let delay = 30;
1354
1742
  while (Date.now() < deadline) {
1355
- await sleep2(delay);
1743
+ await sleep3(delay);
1356
1744
  delay = Math.min(delay * 1.5, 300);
1357
- if (!existsSync8(this.socketPath))
1745
+ if (!existsSync9(this.socketPath))
1358
1746
  continue;
1359
1747
  try {
1360
1748
  return await this.connectOnce();
@@ -1364,7 +1752,7 @@ var EmbedClient = class {
1364
1752
  throw new Error("daemon did not become ready within spawnWaitMs");
1365
1753
  }
1366
1754
  sendAndWait(sock, req) {
1367
- return new Promise((resolve, reject) => {
1755
+ return new Promise((resolve2, reject) => {
1368
1756
  let buf = "";
1369
1757
  const to = setTimeout(() => {
1370
1758
  sock.destroy();
@@ -1379,7 +1767,7 @@ var EmbedClient = class {
1379
1767
  const line = buf.slice(0, nl);
1380
1768
  clearTimeout(to);
1381
1769
  try {
1382
- resolve(JSON.parse(line));
1770
+ resolve2(JSON.parse(line));
1383
1771
  } catch (e) {
1384
1772
  reject(e);
1385
1773
  }
@@ -1396,9 +1784,14 @@ var EmbedClient = class {
1396
1784
  });
1397
1785
  }
1398
1786
  };
1399
- function sleep2(ms) {
1787
+ function sleep3(ms) {
1400
1788
  return new Promise((r) => setTimeout(r, ms));
1401
1789
  }
1790
+ function isTransformersMissingError(err) {
1791
+ if (/hivemind embeddings install/i.test(err))
1792
+ return true;
1793
+ return /@huggingface\/transformers/i.test(err);
1794
+ }
1402
1795
 
1403
1796
  // dist/src/embeddings/sql.js
1404
1797
  function embeddingSqlLiteral(vec) {
@@ -1413,48 +1806,12 @@ function embeddingSqlLiteral(vec) {
1413
1806
  return `ARRAY[${parts.join(",")}]::float4[]`;
1414
1807
  }
1415
1808
 
1416
- // dist/src/embeddings/disable.js
1417
- import { createRequire as createRequire2 } from "node:module";
1418
- import { homedir as homedir11 } from "node:os";
1419
- import { join as join14 } from "node:path";
1420
- import { pathToFileURL } from "node:url";
1421
- var cachedStatus = null;
1422
- function defaultResolveTransformers() {
1423
- try {
1424
- createRequire2(import.meta.url).resolve("@huggingface/transformers");
1425
- return;
1426
- } catch {
1427
- }
1428
- const sharedDir = join14(homedir11(), ".hivemind", "embed-deps");
1429
- createRequire2(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
1430
- }
1431
- var _resolve = defaultResolveTransformers;
1432
- function detectStatus() {
1433
- if (process.env.HIVEMIND_EMBEDDINGS === "false")
1434
- return "env-disabled";
1435
- try {
1436
- _resolve();
1437
- return "enabled";
1438
- } catch {
1439
- return "no-transformers";
1440
- }
1441
- }
1442
- function embeddingsStatus() {
1443
- if (cachedStatus !== null)
1444
- return cachedStatus;
1445
- cachedStatus = detectStatus();
1446
- return cachedStatus;
1447
- }
1448
- function embeddingsDisabled() {
1449
- return embeddingsStatus() !== "enabled";
1450
- }
1451
-
1452
1809
  // dist/src/hooks/codex/stop.js
1453
- var log4 = (msg) => log("codex-stop", msg);
1810
+ var log5 = (msg) => log("codex-stop", msg);
1454
1811
  function resolveEmbedDaemonPath() {
1455
- return join15(dirname4(fileURLToPath3(import.meta.url)), "embeddings", "embed-daemon.js");
1812
+ return join17(dirname5(fileURLToPath3(import.meta.url)), "embeddings", "embed-daemon.js");
1456
1813
  }
1457
- var __bundleDir = dirname4(fileURLToPath3(import.meta.url));
1814
+ var __bundleDir = dirname5(fileURLToPath3(import.meta.url));
1458
1815
  var PLUGIN_VERSION = getInstalledVersion(__bundleDir, ".codex-plugin") ?? "";
1459
1816
  var CAPTURE = process.env.HIVEMIND_CAPTURE !== "false";
1460
1817
  async function main() {
@@ -1466,7 +1823,7 @@ async function main() {
1466
1823
  return;
1467
1824
  const config = loadConfig();
1468
1825
  if (!config) {
1469
- log4("no config");
1826
+ log5("no config");
1470
1827
  return;
1471
1828
  }
1472
1829
  if (CAPTURE) {
@@ -1478,8 +1835,8 @@ async function main() {
1478
1835
  if (input.transcript_path) {
1479
1836
  try {
1480
1837
  const transcriptPath = input.transcript_path;
1481
- if (existsSync9(transcriptPath)) {
1482
- const transcript = readFileSync8(transcriptPath, "utf-8");
1838
+ if (existsSync10(transcriptPath)) {
1839
+ const transcript = readFileSync10(transcriptPath, "utf-8");
1483
1840
  const lines = transcript.trim().split("\n").reverse();
1484
1841
  for (const line2 of lines) {
1485
1842
  try {
@@ -1496,10 +1853,10 @@ async function main() {
1496
1853
  }
1497
1854
  }
1498
1855
  if (lastAssistantMessage)
1499
- log4(`extracted assistant message from transcript (${lastAssistantMessage.length} chars)`);
1856
+ log5(`extracted assistant message from transcript (${lastAssistantMessage.length} chars)`);
1500
1857
  }
1501
1858
  } catch (e) {
1502
- log4(`transcript read failed: ${e.message}`);
1859
+ log5(`transcript read failed: ${e.message}`);
1503
1860
  }
1504
1861
  }
1505
1862
  const entry = {
@@ -1522,9 +1879,9 @@ async function main() {
1522
1879
  const embeddingSql = embeddingSqlLiteral(embedding);
1523
1880
  const insertSql = `INSERT INTO "${sessionsTable}" (id, path, filename, message, message_embedding, author, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(sessionPath)}', '${sqlStr(filename)}', '${jsonForSql}'::jsonb, ${embeddingSql}, '${sqlStr(config.userName)}', ${Buffer.byteLength(line, "utf-8")}, '${sqlStr(projectName)}', 'Stop', 'codex', '${sqlStr(PLUGIN_VERSION)}', '${ts}', '${ts}')`;
1524
1881
  await api.query(insertSql);
1525
- log4("stop event captured");
1882
+ log5("stop event captured");
1526
1883
  } catch (e) {
1527
- log4(`capture failed: ${e.message}`);
1884
+ log5(`capture failed: ${e.message}`);
1528
1885
  }
1529
1886
  }
1530
1887
  if (!CAPTURE)
@@ -1543,11 +1900,11 @@ async function main() {
1543
1900
  reason: "Stop"
1544
1901
  });
1545
1902
  } catch (e) {
1546
- log4(`spawn failed: ${e.message}`);
1903
+ log5(`spawn failed: ${e.message}`);
1547
1904
  try {
1548
1905
  releaseLock(sessionId);
1549
1906
  } catch (releaseErr) {
1550
- log4(`releaseLock after spawn failure also failed: ${releaseErr.message}`);
1907
+ log5(`releaseLock after spawn failure also failed: ${releaseErr.message}`);
1551
1908
  }
1552
1909
  throw e;
1553
1910
  }
@@ -1560,6 +1917,6 @@ async function main() {
1560
1917
  });
1561
1918
  }
1562
1919
  main().catch((e) => {
1563
- log4(`fatal: ${e.message}`);
1920
+ log5(`fatal: ${e.message}`);
1564
1921
  process.exit(0);
1565
1922
  });