@deeplake/hivemind 0.6.48 → 0.7.4

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.
Files changed (40) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +147 -20
  4. package/bundle/cli.js +552 -95
  5. package/codex/bundle/capture.js +509 -89
  6. package/codex/bundle/commands/auth-login.js +209 -66
  7. package/codex/bundle/embeddings/embed-daemon.js +243 -0
  8. package/codex/bundle/pre-tool-use.js +629 -104
  9. package/codex/bundle/session-start-setup.js +194 -57
  10. package/codex/bundle/session-start.js +25 -10
  11. package/codex/bundle/shell/deeplake-shell.js +679 -112
  12. package/codex/bundle/stop.js +476 -58
  13. package/codex/bundle/wiki-worker.js +312 -11
  14. package/cursor/bundle/capture.js +768 -57
  15. package/cursor/bundle/commands/auth-login.js +209 -66
  16. package/cursor/bundle/embeddings/embed-daemon.js +243 -0
  17. package/cursor/bundle/pre-tool-use.js +561 -70
  18. package/cursor/bundle/session-end.js +223 -2
  19. package/cursor/bundle/session-start.js +192 -54
  20. package/cursor/bundle/shell/deeplake-shell.js +679 -112
  21. package/cursor/bundle/wiki-worker.js +571 -0
  22. package/hermes/bundle/capture.js +771 -58
  23. package/hermes/bundle/commands/auth-login.js +209 -66
  24. package/hermes/bundle/embeddings/embed-daemon.js +243 -0
  25. package/hermes/bundle/pre-tool-use.js +560 -69
  26. package/hermes/bundle/session-end.js +224 -1
  27. package/hermes/bundle/session-start.js +195 -54
  28. package/hermes/bundle/shell/deeplake-shell.js +679 -112
  29. package/hermes/bundle/wiki-worker.js +572 -0
  30. package/mcp/bundle/server.js +253 -68
  31. package/openclaw/dist/chunks/auth-creds-AEKS6D3P.js +14 -0
  32. package/openclaw/dist/chunks/chunk-SRCBBT4H.js +37 -0
  33. package/openclaw/dist/chunks/config-G23NI5TV.js +33 -0
  34. package/openclaw/dist/chunks/index-marker-store-PGT5CW6T.js +33 -0
  35. package/openclaw/dist/chunks/setup-config-C35UK4LP.js +114 -0
  36. package/openclaw/dist/index.js +752 -702
  37. package/openclaw/openclaw.plugin.json +1 -1
  38. package/openclaw/package.json +1 -1
  39. package/package.json +2 -1
  40. package/pi/extension-source/hivemind.ts +473 -21
package/bundle/cli.js CHANGED
@@ -1,4 +1,56 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // dist/src/index-marker-store.js
13
+ var index_marker_store_exports = {};
14
+ __export(index_marker_store_exports, {
15
+ buildIndexMarkerPath: () => buildIndexMarkerPath,
16
+ getIndexMarkerDir: () => getIndexMarkerDir,
17
+ hasFreshIndexMarker: () => hasFreshIndexMarker,
18
+ writeIndexMarker: () => writeIndexMarker
19
+ });
20
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
21
+ import { join as join14 } from "node:path";
22
+ import { tmpdir } from "node:os";
23
+ function getIndexMarkerDir() {
24
+ return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join14(tmpdir(), "hivemind-deeplake-indexes");
25
+ }
26
+ function buildIndexMarkerPath(workspaceId, orgId, table, suffix) {
27
+ const markerKey = [workspaceId, orgId, table, suffix].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
28
+ return join14(getIndexMarkerDir(), `${markerKey}.json`);
29
+ }
30
+ function hasFreshIndexMarker(markerPath) {
31
+ if (!existsSync11(markerPath))
32
+ return false;
33
+ try {
34
+ const raw = JSON.parse(readFileSync8(markerPath, "utf-8"));
35
+ const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
36
+ if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
37
+ return false;
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+ function writeIndexMarker(markerPath) {
44
+ mkdirSync3(getIndexMarkerDir(), { recursive: true });
45
+ writeFileSync5(markerPath, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
46
+ }
47
+ var INDEX_MARKER_TTL_MS;
48
+ var init_index_marker_store = __esm({
49
+ "dist/src/index-marker-store.js"() {
50
+ "use strict";
51
+ INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
52
+ }
53
+ });
2
54
 
3
55
  // dist/src/cli/install-claude.js
4
56
  import { execFileSync } from "node:child_process";
@@ -10,6 +62,19 @@ import { homedir } from "node:os";
10
62
  import { fileURLToPath } from "node:url";
11
63
  var HOME = homedir();
12
64
  function pkgRoot() {
65
+ let dir = fileURLToPath(new URL(".", import.meta.url));
66
+ for (let i = 0; i < 8; i++) {
67
+ try {
68
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
69
+ if (pkg.name === "@deeplake/hivemind" || pkg.name === "hivemind")
70
+ return dir;
71
+ } catch {
72
+ }
73
+ const parent = dirname(dir);
74
+ if (parent === dir)
75
+ break;
76
+ dir = parent;
77
+ }
13
78
  return fileURLToPath(new URL("..", import.meta.url));
14
79
  }
15
80
  function ensureDir(path, mode = 493) {
@@ -187,7 +252,15 @@ function buildHooksJson() {
187
252
  }
188
253
  };
189
254
  }
190
- function isHivemindHookEntry(entry) {
255
+ var HIVEMIND_BUNDLE_FILES = [
256
+ "session-start.js",
257
+ "session-start-setup.js",
258
+ "capture.js",
259
+ "pre-tool-use.js",
260
+ "stop.js",
261
+ "wiki-worker.js"
262
+ ];
263
+ function isHivemindHookEntry(entry, pluginDir = PLUGIN_DIR) {
191
264
  if (!entry || typeof entry !== "object")
192
265
  return false;
193
266
  const e = entry;
@@ -196,9 +269,41 @@ function isHivemindHookEntry(entry) {
196
269
  if (!h || typeof h !== "object")
197
270
  return false;
198
271
  const cmd = h.command;
199
- return typeof cmd === "string" && cmd.includes(`${PLUGIN_DIR}/bundle/`);
272
+ if (typeof cmd !== "string")
273
+ return false;
274
+ if (cmd.includes(`${pluginDir}/bundle/`))
275
+ return true;
276
+ return HIVEMIND_BUNDLE_FILES.some((f) => cmd.includes(`/bundle/${f}`));
200
277
  });
201
278
  }
279
+ function isForeignHivemindHookEntry(entry, pluginDir = PLUGIN_DIR) {
280
+ if (!isHivemindHookEntry(entry, pluginDir))
281
+ return false;
282
+ const e = entry;
283
+ const hooks = Array.isArray(e.hooks) ? e.hooks : [];
284
+ return hooks.every((h) => {
285
+ if (!h || typeof h !== "object")
286
+ return false;
287
+ const cmd = h.command;
288
+ if (typeof cmd !== "string")
289
+ return false;
290
+ return !cmd.includes(`${pluginDir}/bundle/`);
291
+ });
292
+ }
293
+ function mergeHooks(existing, ours, pluginDir = PLUGIN_DIR) {
294
+ const existingHooks = existing.hooks && typeof existing.hooks === "object" ? existing.hooks : {};
295
+ const ourHooks = ours.hooks;
296
+ const merged = {};
297
+ for (const [event, entries] of Object.entries(existingHooks)) {
298
+ const surviving = (entries ?? []).filter((e) => !isHivemindHookEntry(e, pluginDir));
299
+ if (surviving.length)
300
+ merged[event] = surviving;
301
+ }
302
+ for (const [event, entries] of Object.entries(ourHooks)) {
303
+ merged[event] = [...merged[event] ?? [], ...entries ?? []];
304
+ }
305
+ return { ...existing, hooks: merged };
306
+ }
202
307
  function mergeHooksJson(ours) {
203
308
  let existing = {};
204
309
  try {
@@ -210,18 +315,30 @@ function mergeHooksJson(ours) {
210
315
  } catch {
211
316
  warn(` Codex ${HOOKS_PATH} unparseable \u2014 ignoring prior content`);
212
317
  }
318
+ reportForeignHivemindHooks(existing);
319
+ return mergeHooks(existing, ours);
320
+ }
321
+ function reportForeignHivemindHooks(existing) {
213
322
  const existingHooks = existing.hooks && typeof existing.hooks === "object" ? existing.hooks : {};
214
- const ourHooks = ours.hooks;
215
- const merged = {};
216
- for (const [event, entries] of Object.entries(existingHooks)) {
217
- const surviving = (entries ?? []).filter((e) => !isHivemindHookEntry(e));
218
- if (surviving.length)
219
- merged[event] = surviving;
220
- }
221
- for (const [event, entries] of Object.entries(ourHooks)) {
222
- merged[event] = [...merged[event] ?? [], ...entries ?? []];
323
+ const foreign = /* @__PURE__ */ new Set();
324
+ for (const entries of Object.values(existingHooks)) {
325
+ for (const e of entries ?? []) {
326
+ if (!isForeignHivemindHookEntry(e))
327
+ continue;
328
+ const hooks = Array.isArray(e.hooks) ? e.hooks : [];
329
+ for (const h of hooks) {
330
+ const cmd = h?.command;
331
+ if (typeof cmd === "string")
332
+ foreign.add(cmd);
333
+ }
334
+ }
223
335
  }
224
- return { ...existing, hooks: merged };
336
+ if (foreign.size === 0)
337
+ return;
338
+ warn(` Codex stripping ${foreign.size} hivemind hook(s) from a non-canonical path:`);
339
+ for (const cmd of foreign)
340
+ warn(` ${cmd}`);
341
+ warn(` (these were probably leftover from a local dev clone \u2014 re-add them manually if intentional)`);
225
342
  }
226
343
  function tryEnableCodexHooks() {
227
344
  try {
@@ -253,8 +370,28 @@ function installCodex() {
253
370
  }
254
371
  function uninstallCodex() {
255
372
  if (existsSync2(HOOKS_PATH)) {
256
- unlinkSync2(HOOKS_PATH);
257
- log(` Codex removed ${HOOKS_PATH}`);
373
+ let existing = {};
374
+ try {
375
+ const raw = JSON.parse(readFileSync3(HOOKS_PATH, "utf-8"));
376
+ if (raw && typeof raw === "object")
377
+ existing = raw;
378
+ } catch {
379
+ unlinkSync2(HOOKS_PATH);
380
+ log(` Codex removed unparseable ${HOOKS_PATH}`);
381
+ existing = {};
382
+ }
383
+ if (Object.keys(existing).length > 0) {
384
+ const stripped = mergeHooks(existing, { hooks: {} });
385
+ const survivingHooks = stripped.hooks ?? {};
386
+ const otherTopLevelKeys = Object.keys(stripped).filter((k) => k !== "hooks");
387
+ if (Object.keys(survivingHooks).length === 0 && otherTopLevelKeys.length === 0) {
388
+ unlinkSync2(HOOKS_PATH);
389
+ log(` Codex removed ${HOOKS_PATH}`);
390
+ } else {
391
+ writeJson(HOOKS_PATH, stripped);
392
+ log(` Codex stripped hivemind hooks from ${HOOKS_PATH}`);
393
+ }
394
+ }
258
395
  }
259
396
  if (existsSync2(SKILL_LINK)) {
260
397
  unlinkSync2(SKILL_LINK);
@@ -336,7 +473,7 @@ function isHivemindEntry(entry) {
336
473
  const cmd = entry.command;
337
474
  return typeof cmd === "string" && cmd.includes("/.cursor/hivemind/bundle/");
338
475
  }
339
- function mergeHooks(existing) {
476
+ function mergeHooks2(existing) {
340
477
  const root = existing ?? { version: 1, hooks: {} };
341
478
  if (!root.version)
342
479
  root.version = 1;
@@ -375,7 +512,7 @@ function installCursor() {
375
512
  ensureDir(PLUGIN_DIR3);
376
513
  copyDir(srcBundle, join5(PLUGIN_DIR3, "bundle"));
377
514
  const existing = readJson(HOOKS_PATH2);
378
- const merged = mergeHooks(existing);
515
+ const merged = mergeHooks2(existing);
379
516
  writeJson(HOOKS_PATH2, merged);
380
517
  writeVersionStamp(PLUGIN_DIR3, getVersion());
381
518
  log(` Cursor installed -> ${PLUGIN_DIR3}`);
@@ -3090,7 +3227,7 @@ function buildHooksBlock() {
3090
3227
  on_session_end: [buildHookEntry("session-end.js", 30)]
3091
3228
  };
3092
3229
  }
3093
- function mergeHooks2(existing) {
3230
+ function mergeHooks3(existing) {
3094
3231
  const merged = { ...existing ?? {} };
3095
3232
  const ours = buildHooksBlock();
3096
3233
  for (const [event, entries] of Object.entries(ours)) {
@@ -3151,7 +3288,7 @@ function installHermes() {
3151
3288
  command: "node",
3152
3289
  args: [MCP_SERVER_PATH]
3153
3290
  };
3154
- cfg.hooks = mergeHooks2(cfg.hooks);
3291
+ cfg.hooks = mergeHooks3(cfg.hooks);
3155
3292
  cfg.hooks_auto_accept = true;
3156
3293
  writeConfig(cfg);
3157
3294
  log(` Hermes config updated -> ${CONFIG_PATH} (mcp_servers + hooks + hooks_auto_accept)`);
@@ -3182,6 +3319,10 @@ function uninstallHermes() {
3182
3319
  delete cfg.hooks;
3183
3320
  touched = true;
3184
3321
  }
3322
+ if ("hooks_auto_accept" in cfg) {
3323
+ delete cfg.hooks_auto_accept;
3324
+ touched = true;
3325
+ }
3185
3326
  if (touched) {
3186
3327
  if (Object.keys(cfg).length === 0) {
3187
3328
  unlinkSync4(CONFIG_PATH);
@@ -3202,6 +3343,8 @@ var LEGACY_SKILL_DIR = join8(PI_AGENT_DIR, "skills", "hivemind-memory");
3202
3343
  var EXTENSIONS_DIR = join8(PI_AGENT_DIR, "extensions");
3203
3344
  var EXTENSION_PATH = join8(EXTENSIONS_DIR, "hivemind.ts");
3204
3345
  var VERSION_DIR = join8(PI_AGENT_DIR, ".hivemind");
3346
+ var WIKI_WORKER_DIR = join8(PI_AGENT_DIR, "hivemind");
3347
+ var WIKI_WORKER_PATH = join8(WIKI_WORKER_DIR, "wiki-worker.js");
3205
3348
  var HIVEMIND_BLOCK_START = "<!-- BEGIN hivemind-memory -->";
3206
3349
  var HIVEMIND_BLOCK_END = "<!-- END hivemind-memory -->";
3207
3350
  var HIVEMIND_BLOCK_BODY = `${HIVEMIND_BLOCK_START}
@@ -3281,10 +3424,18 @@ function installPi() {
3281
3424
  }
3282
3425
  ensureDir(EXTENSIONS_DIR);
3283
3426
  copyFileSync2(srcExtension, EXTENSION_PATH);
3427
+ const srcWorker = join8(pkgRoot(), "pi", "bundle", "wiki-worker.js");
3428
+ if (existsSync7(srcWorker)) {
3429
+ ensureDir(WIKI_WORKER_DIR);
3430
+ copyFileSync2(srcWorker, WIKI_WORKER_PATH);
3431
+ }
3284
3432
  ensureDir(VERSION_DIR);
3285
3433
  writeVersionStamp(VERSION_DIR, getVersion());
3286
3434
  log(` pi AGENTS.md updated -> ${AGENTS_MD}`);
3287
3435
  log(` pi extension installed -> ${EXTENSION_PATH}`);
3436
+ if (existsSync7(WIKI_WORKER_PATH)) {
3437
+ log(` pi wiki-worker installed -> ${WIKI_WORKER_PATH}`);
3438
+ }
3288
3439
  }
3289
3440
  function uninstallPi() {
3290
3441
  if (existsSync7(LEGACY_SKILL_DIR)) {
@@ -3295,6 +3446,10 @@ function uninstallPi() {
3295
3446
  rmSync3(EXTENSION_PATH, { force: true });
3296
3447
  log(` pi removed extension ${EXTENSION_PATH}`);
3297
3448
  }
3449
+ if (existsSync7(WIKI_WORKER_DIR)) {
3450
+ rmSync3(WIKI_WORKER_DIR, { recursive: true, force: true });
3451
+ log(` pi removed wiki-worker dir ${WIKI_WORKER_DIR}`);
3452
+ }
3298
3453
  if (existsSync7(AGENTS_MD)) {
3299
3454
  const prior = readFileSync5(AGENTS_MD, "utf-8");
3300
3455
  const stripped = stripHivemindBlock(prior);
@@ -3311,43 +3466,228 @@ function uninstallPi() {
3311
3466
  }
3312
3467
  }
3313
3468
 
3469
+ // dist/src/cli/embeddings.js
3470
+ import { copyFileSync as copyFileSync3, chmodSync, existsSync as existsSync8, lstatSync as lstatSync2, readdirSync, readlinkSync, rmSync as rmSync4, statSync, unlinkSync as unlinkSync5 } from "node:fs";
3471
+ import { execFileSync as execFileSync3 } from "node:child_process";
3472
+ import { join as join9 } from "node:path";
3473
+ var SHARED_DIR = join9(HOME, ".hivemind", "embed-deps");
3474
+ var SHARED_NODE_MODULES = join9(SHARED_DIR, "node_modules");
3475
+ var SHARED_DAEMON_PATH = join9(SHARED_DIR, "embed-daemon.js");
3476
+ var TRANSFORMERS_PKG = "@huggingface/transformers";
3477
+ var TRANSFORMERS_RANGE = "^3.0.0";
3478
+ function findHivemindInstalls(home = HOME) {
3479
+ const out = [];
3480
+ const fixed = [
3481
+ { id: "codex", pluginDir: join9(home, ".codex", "hivemind") },
3482
+ { id: "cursor", pluginDir: join9(home, ".cursor", "hivemind") },
3483
+ { id: "hermes", pluginDir: join9(home, ".hermes", "hivemind") }
3484
+ ];
3485
+ for (const inst of fixed) {
3486
+ if (existsSync8(join9(inst.pluginDir, "bundle")))
3487
+ out.push(inst);
3488
+ }
3489
+ const ccCache = join9(home, ".claude", "plugins", "cache", "hivemind", "hivemind");
3490
+ if (existsSync8(ccCache)) {
3491
+ let entries = [];
3492
+ try {
3493
+ entries = readdirSync(ccCache);
3494
+ } catch {
3495
+ }
3496
+ for (const ver of entries) {
3497
+ const dir = join9(ccCache, ver);
3498
+ try {
3499
+ if (!statSync(dir).isDirectory())
3500
+ continue;
3501
+ } catch {
3502
+ continue;
3503
+ }
3504
+ const candidates = [join9(dir, "bundle"), join9(dir, "claude-code", "bundle")];
3505
+ if (candidates.some((p) => existsSync8(p))) {
3506
+ out.push({ id: `claude (${ver})`, pluginDir: dir });
3507
+ }
3508
+ }
3509
+ }
3510
+ return out;
3511
+ }
3512
+ function isSharedDepsInstalled(sharedNodeModules = SHARED_NODE_MODULES) {
3513
+ return existsSync8(join9(sharedNodeModules, TRANSFORMERS_PKG));
3514
+ }
3515
+ function isSymlinkToSharedDeps(linkPath, sharedNodeModules) {
3516
+ if (!existsSync8(linkPath))
3517
+ return false;
3518
+ try {
3519
+ if (!lstatSync2(linkPath).isSymbolicLink())
3520
+ return false;
3521
+ return readlinkSync(linkPath) === sharedNodeModules;
3522
+ } catch {
3523
+ return false;
3524
+ }
3525
+ }
3526
+ function linkStateFor(install, sharedNodeModules = SHARED_NODE_MODULES) {
3527
+ const link = join9(install.pluginDir, "node_modules");
3528
+ if (!existsSync8(link) && !isSymbolicLink(link))
3529
+ return { kind: "no-node-modules" };
3530
+ try {
3531
+ if (lstatSync2(link).isSymbolicLink()) {
3532
+ const target = readlinkSync(link);
3533
+ return target === sharedNodeModules ? { kind: "linked-to-shared" } : { kind: "linked-elsewhere", target };
3534
+ }
3535
+ } catch {
3536
+ return { kind: "no-node-modules" };
3537
+ }
3538
+ return { kind: "owns-own-node-modules" };
3539
+ }
3540
+ function isSymbolicLink(path) {
3541
+ try {
3542
+ return lstatSync2(path).isSymbolicLink();
3543
+ } catch {
3544
+ return false;
3545
+ }
3546
+ }
3547
+ function ensureSharedDeps() {
3548
+ if (!isSharedDepsInstalled()) {
3549
+ log(` Embeddings installing ${TRANSFORMERS_PKG}@${TRANSFORMERS_RANGE} into ${SHARED_DIR}`);
3550
+ log(` (~600 MB; first install only \u2014 every agent will share this)`);
3551
+ ensureDir(SHARED_DIR);
3552
+ writeJson(join9(SHARED_DIR, "package.json"), {
3553
+ name: "hivemind-embed-deps",
3554
+ version: "1.0.0",
3555
+ private: true,
3556
+ dependencies: { [TRANSFORMERS_PKG]: TRANSFORMERS_RANGE }
3557
+ });
3558
+ execFileSync3("npm", ["install", "--omit=dev", "--no-package-lock", "--no-audit", "--no-fund"], {
3559
+ cwd: SHARED_DIR,
3560
+ stdio: "inherit"
3561
+ });
3562
+ } else {
3563
+ log(` Embeddings shared deps already present at ${SHARED_DIR}`);
3564
+ }
3565
+ ensureDir(SHARED_DIR);
3566
+ const src = join9(pkgRoot(), "embeddings", "embed-daemon.js");
3567
+ if (existsSync8(src)) {
3568
+ copyFileSync3(src, SHARED_DAEMON_PATH);
3569
+ chmodSync(SHARED_DAEMON_PATH, 493);
3570
+ } else {
3571
+ warn(` Embeddings standalone daemon bundle missing at ${src} (run 'npm run build' first)`);
3572
+ }
3573
+ }
3574
+ function linkAgent(install) {
3575
+ const link = join9(install.pluginDir, "node_modules");
3576
+ symlinkForce(SHARED_NODE_MODULES, link);
3577
+ log(` Embeddings linked ${install.id.padEnd(20)} -> shared deps`);
3578
+ }
3579
+ function enableEmbeddings() {
3580
+ ensureSharedDeps();
3581
+ const installs = findHivemindInstalls();
3582
+ if (installs.length === 0) {
3583
+ warn(" Embeddings no hivemind installs detected \u2014 run `hivemind install` first");
3584
+ warn(" (the shared deps are in place; subsequent agent installs will pick them up if you re-run `hivemind embeddings install`)");
3585
+ return;
3586
+ }
3587
+ for (const inst of installs)
3588
+ linkAgent(inst);
3589
+ log(` Embeddings enabled. Restart your agents to pick up.`);
3590
+ }
3591
+ function disableEmbeddings(opts) {
3592
+ const installs = findHivemindInstalls();
3593
+ for (const inst of installs) {
3594
+ const link = join9(inst.pluginDir, "node_modules");
3595
+ if (isSymlinkToSharedDeps(link, SHARED_NODE_MODULES)) {
3596
+ unlinkSync5(link);
3597
+ log(` Embeddings unlinked ${inst.id}`);
3598
+ }
3599
+ }
3600
+ if (opts?.prune && existsSync8(SHARED_DIR)) {
3601
+ rmSync4(SHARED_DIR, { recursive: true, force: true });
3602
+ log(` Embeddings pruned ${SHARED_DIR}`);
3603
+ }
3604
+ }
3605
+ function statusEmbeddings() {
3606
+ log(`Shared deps: ${SHARED_DIR}`);
3607
+ log(`Installed: ${isSharedDepsInstalled() ? "yes" : "no"}`);
3608
+ log(`Daemon: ${existsSync8(SHARED_DAEMON_PATH) ? SHARED_DAEMON_PATH : "(not present)"}`);
3609
+ log("");
3610
+ log(`Agent installs:`);
3611
+ const installs = findHivemindInstalls();
3612
+ if (installs.length === 0) {
3613
+ log(` (none detected)`);
3614
+ return;
3615
+ }
3616
+ for (const inst of installs) {
3617
+ const state = linkStateFor(inst);
3618
+ let label;
3619
+ switch (state.kind) {
3620
+ case "linked-to-shared":
3621
+ label = "\u2713 linked \u2192 shared";
3622
+ break;
3623
+ case "no-node-modules":
3624
+ label = "\u2717 not linked (embeddings disabled)";
3625
+ break;
3626
+ case "owns-own-node-modules":
3627
+ label = "\u25B3 has its own node_modules (not shared)";
3628
+ break;
3629
+ case "linked-elsewhere":
3630
+ label = `\u25B3 linked \u2192 ${state.target}`;
3631
+ break;
3632
+ }
3633
+ log(` ${inst.id.padEnd(20)} ${label}`);
3634
+ log(` ${" ".repeat(20)} ${inst.pluginDir}`);
3635
+ }
3636
+ }
3637
+
3314
3638
  // dist/src/cli/auth.js
3315
3639
  import { existsSync as existsSync9 } from "node:fs";
3316
- import { join as join10 } from "node:path";
3640
+ import { join as join11 } from "node:path";
3317
3641
 
3318
3642
  // dist/src/commands/auth.js
3319
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync2, unlinkSync as unlinkSync5 } from "node:fs";
3320
- import { join as join9 } from "node:path";
3321
- import { homedir as homedir2 } from "node:os";
3322
3643
  import { execSync } from "node:child_process";
3323
- var CONFIG_DIR = join9(homedir2(), ".deeplake");
3324
- var CREDS_PATH = join9(CONFIG_DIR, "credentials.json");
3325
- var DEFAULT_API_URL = "https://api.deeplake.ai";
3644
+
3645
+ // dist/src/utils/client-header.js
3646
+ var DEEPLAKE_CLIENT_HEADER = "X-Deeplake-Client";
3647
+ function deeplakeClientValue() {
3648
+ return "hivemind";
3649
+ }
3650
+ function deeplakeClientHeader() {
3651
+ return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
3652
+ }
3653
+
3654
+ // dist/src/commands/auth-creds.js
3655
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, unlinkSync as unlinkSync6 } from "node:fs";
3656
+ import { join as join10 } from "node:path";
3657
+ import { homedir as homedir2 } from "node:os";
3658
+ function configDir() {
3659
+ return join10(homedir2(), ".deeplake");
3660
+ }
3661
+ function credsPath() {
3662
+ return join10(configDir(), "credentials.json");
3663
+ }
3326
3664
  function loadCredentials() {
3327
- if (!existsSync8(CREDS_PATH))
3328
- return null;
3329
3665
  try {
3330
- return JSON.parse(readFileSync6(CREDS_PATH, "utf-8"));
3666
+ return JSON.parse(readFileSync6(credsPath(), "utf-8"));
3331
3667
  } catch {
3332
3668
  return null;
3333
3669
  }
3334
3670
  }
3335
3671
  function saveCredentials(creds) {
3336
- if (!existsSync8(CONFIG_DIR))
3337
- mkdirSync2(CONFIG_DIR, { recursive: true, mode: 448 });
3338
- writeFileSync4(CREDS_PATH, JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
3672
+ mkdirSync2(configDir(), { recursive: true, mode: 448 });
3673
+ writeFileSync4(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
3339
3674
  }
3340
3675
  function deleteCredentials() {
3341
- if (existsSync8(CREDS_PATH)) {
3342
- unlinkSync5(CREDS_PATH);
3676
+ try {
3677
+ unlinkSync6(credsPath());
3343
3678
  return true;
3679
+ } catch {
3680
+ return false;
3344
3681
  }
3345
- return false;
3346
3682
  }
3683
+
3684
+ // dist/src/commands/auth.js
3685
+ var DEFAULT_API_URL = "https://api.deeplake.ai";
3347
3686
  async function apiGet(path, token, apiUrl, orgId) {
3348
3687
  const headers = {
3349
3688
  Authorization: `Bearer ${token}`,
3350
- "Content-Type": "application/json"
3689
+ "Content-Type": "application/json",
3690
+ ...deeplakeClientHeader()
3351
3691
  };
3352
3692
  if (orgId)
3353
3693
  headers["X-Activeloop-Org-Id"] = orgId;
@@ -3359,7 +3699,8 @@ async function apiGet(path, token, apiUrl, orgId) {
3359
3699
  async function apiPost(path, body, token, apiUrl, orgId) {
3360
3700
  const headers = {
3361
3701
  Authorization: `Bearer ${token}`,
3362
- "Content-Type": "application/json"
3702
+ "Content-Type": "application/json",
3703
+ ...deeplakeClientHeader()
3363
3704
  };
3364
3705
  if (orgId)
3365
3706
  headers["X-Activeloop-Org-Id"] = orgId;
@@ -3371,7 +3712,8 @@ async function apiPost(path, body, token, apiUrl, orgId) {
3371
3712
  async function apiDelete(path, token, apiUrl, orgId) {
3372
3713
  const headers = {
3373
3714
  Authorization: `Bearer ${token}`,
3374
- "Content-Type": "application/json"
3715
+ "Content-Type": "application/json",
3716
+ ...deeplakeClientHeader()
3375
3717
  };
3376
3718
  if (orgId)
3377
3719
  headers["X-Activeloop-Org-Id"] = orgId;
@@ -3382,7 +3724,7 @@ async function apiDelete(path, token, apiUrl, orgId) {
3382
3724
  async function requestDeviceCode(apiUrl = DEFAULT_API_URL) {
3383
3725
  const resp = await fetch(`${apiUrl}/auth/device/code`, {
3384
3726
  method: "POST",
3385
- headers: { "Content-Type": "application/json" }
3727
+ headers: { "Content-Type": "application/json", ...deeplakeClientHeader() }
3386
3728
  });
3387
3729
  if (!resp.ok)
3388
3730
  throw new Error(`Device flow unavailable: HTTP ${resp.status}`);
@@ -3391,7 +3733,7 @@ async function requestDeviceCode(apiUrl = DEFAULT_API_URL) {
3391
3733
  async function pollForToken(deviceCode, apiUrl = DEFAULT_API_URL) {
3392
3734
  const resp = await fetch(`${apiUrl}/auth/device/token`, {
3393
3735
  method: "POST",
3394
- headers: { "Content-Type": "application/json" },
3736
+ headers: { "Content-Type": "application/json", ...deeplakeClientHeader() },
3395
3737
  body: JSON.stringify({ device_code: deviceCode })
3396
3738
  });
3397
3739
  if (resp.ok)
@@ -3517,9 +3859,9 @@ Using: ${orgName}
3517
3859
  }
3518
3860
 
3519
3861
  // dist/src/cli/auth.js
3520
- var CREDS_PATH2 = join10(HOME, ".deeplake", "credentials.json");
3862
+ var CREDS_PATH = join11(HOME, ".deeplake", "credentials.json");
3521
3863
  function isLoggedIn() {
3522
- return existsSync9(CREDS_PATH2) && loadCredentials() !== null;
3864
+ return existsSync9(CREDS_PATH) && loadCredentials() !== null;
3523
3865
  }
3524
3866
  async function ensureLoggedIn() {
3525
3867
  if (isLoggedIn())
@@ -3553,11 +3895,11 @@ async function maybeShowOrgChoice() {
3553
3895
 
3554
3896
  // dist/src/config.js
3555
3897
  import { readFileSync as readFileSync7, existsSync as existsSync10 } from "node:fs";
3556
- import { join as join11 } from "node:path";
3898
+ import { join as join12 } from "node:path";
3557
3899
  import { homedir as homedir3, userInfo } from "node:os";
3558
3900
  function loadConfig() {
3559
3901
  const home = homedir3();
3560
- const credPath = join11(home, ".deeplake", "credentials.json");
3902
+ const credPath = join12(home, ".deeplake", "credentials.json");
3561
3903
  let creds = null;
3562
3904
  if (existsSync10(credPath)) {
3563
3905
  try {
@@ -3579,22 +3921,19 @@ function loadConfig() {
3579
3921
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
3580
3922
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
3581
3923
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
3582
- memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join11(home, ".deeplake", "memory")
3924
+ memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join12(home, ".deeplake", "memory")
3583
3925
  };
3584
3926
  }
3585
3927
 
3586
3928
  // dist/src/deeplake-api.js
3587
3929
  import { randomUUID } from "node:crypto";
3588
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
3589
- import { join as join13 } from "node:path";
3590
- import { tmpdir } from "node:os";
3591
3930
 
3592
3931
  // dist/src/utils/debug.js
3593
3932
  import { appendFileSync } from "node:fs";
3594
- import { join as join12 } from "node:path";
3933
+ import { join as join13 } from "node:path";
3595
3934
  import { homedir as homedir4 } from "node:os";
3596
3935
  var DEBUG = process.env.HIVEMIND_DEBUG === "1";
3597
- var LOG = join12(homedir4(), ".deeplake", "hook-debug.log");
3936
+ var LOG = join13(homedir4(), ".deeplake", "hook-debug.log");
3598
3937
  function log2(tag, msg) {
3599
3938
  if (!DEBUG)
3600
3939
  return;
@@ -3607,7 +3946,17 @@ function sqlStr(value) {
3607
3946
  return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
3608
3947
  }
3609
3948
 
3949
+ // dist/src/embeddings/columns.js
3950
+ var SUMMARY_EMBEDDING_COL = "summary_embedding";
3951
+ var MESSAGE_EMBEDDING_COL = "message_embedding";
3952
+
3610
3953
  // dist/src/deeplake-api.js
3954
+ var indexMarkerStorePromise = null;
3955
+ function getIndexMarkerStore() {
3956
+ if (!indexMarkerStorePromise)
3957
+ indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
3958
+ return indexMarkerStorePromise;
3959
+ }
3611
3960
  var log3 = (msg) => log2("sdk", msg);
3612
3961
  function summarizeSql(sql, maxLen = 220) {
3613
3962
  const compact = sql.replace(/\s+/g, " ").trim();
@@ -3627,7 +3976,6 @@ var MAX_RETRIES = 3;
3627
3976
  var BASE_DELAY_MS = 500;
3628
3977
  var MAX_CONCURRENCY = 5;
3629
3978
  var QUERY_TIMEOUT_MS = Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
3630
- var INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
3631
3979
  function sleep(ms) {
3632
3980
  return new Promise((resolve) => setTimeout(resolve, ms));
3633
3981
  }
@@ -3647,9 +3995,6 @@ function isTransientHtml403(text) {
3647
3995
  const body = text.toLowerCase();
3648
3996
  return body.includes("<html") || body.includes("403 forbidden") || body.includes("cloudflare") || body.includes("nginx");
3649
3997
  }
3650
- function getIndexMarkerDir() {
3651
- return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join13(tmpdir(), "hivemind-deeplake-indexes");
3652
- }
3653
3998
  var Semaphore = class {
3654
3999
  max;
3655
4000
  waiting = [];
@@ -3718,7 +4063,8 @@ var DeeplakeApi = class {
3718
4063
  headers: {
3719
4064
  Authorization: `Bearer ${this.token}`,
3720
4065
  "Content-Type": "application/json",
3721
- "X-Activeloop-Org-Id": this.orgId
4066
+ "X-Activeloop-Org-Id": this.orgId,
4067
+ ...deeplakeClientHeader()
3722
4068
  },
3723
4069
  signal,
3724
4070
  body: JSON.stringify({ query: sql })
@@ -3745,7 +4091,8 @@ var DeeplakeApi = class {
3745
4091
  }
3746
4092
  const text = await resp.text().catch(() => "");
3747
4093
  const retryable403 = isSessionInsertQuery(sql) && (resp.status === 401 || resp.status === 403 && (text.length === 0 || isTransientHtml403(text)));
3748
- if (attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
4094
+ const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
4095
+ if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
3749
4096
  const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
3750
4097
  log3(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
3751
4098
  await sleep(delay);
@@ -3779,7 +4126,7 @@ var DeeplakeApi = class {
3779
4126
  const lud = row.lastUpdateDate ?? ts;
3780
4127
  const exists = await this.query(`SELECT path FROM "${this.tableName}" WHERE path = '${sqlStr(row.path)}' LIMIT 1`);
3781
4128
  if (exists.length > 0) {
3782
- let setClauses = `summary = E'${sqlStr(row.contentText)}', mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
4129
+ let setClauses = `summary = E'${sqlStr(row.contentText)}', ${SUMMARY_EMBEDDING_COL} = NULL, mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
3783
4130
  if (row.project !== void 0)
3784
4131
  setClauses += `, project = '${sqlStr(row.project)}'`;
3785
4132
  if (row.description !== void 0)
@@ -3787,8 +4134,8 @@ var DeeplakeApi = class {
3787
4134
  await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(row.path)}'`);
3788
4135
  } else {
3789
4136
  const id = randomUUID();
3790
- let cols = "id, path, filename, summary, mime_type, size_bytes, creation_date, last_update_date";
3791
- let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
4137
+ let cols = `id, path, filename, summary, ${SUMMARY_EMBEDDING_COL}, mime_type, size_bytes, creation_date, last_update_date`;
4138
+ let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', NULL, '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
3792
4139
  if (row.project !== void 0) {
3793
4140
  cols += ", project";
3794
4141
  vals += `, '${sqlStr(row.project)}'`;
@@ -3813,48 +4160,83 @@ var DeeplakeApi = class {
3813
4160
  buildLookupIndexName(table, suffix) {
3814
4161
  return `idx_${table}_${suffix}`.replace(/[^a-zA-Z0-9_]/g, "_");
3815
4162
  }
3816
- getLookupIndexMarkerPath(table, suffix) {
3817
- const markerKey = [
3818
- this.workspaceId,
3819
- this.orgId,
3820
- table,
3821
- suffix
3822
- ].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
3823
- return join13(getIndexMarkerDir(), `${markerKey}.json`);
3824
- }
3825
- hasFreshLookupIndexMarker(table, suffix) {
3826
- const markerPath = this.getLookupIndexMarkerPath(table, suffix);
3827
- if (!existsSync11(markerPath))
3828
- return false;
3829
- try {
3830
- const raw = JSON.parse(readFileSync8(markerPath, "utf-8"));
3831
- const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
3832
- if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
3833
- return false;
3834
- return true;
3835
- } catch {
3836
- return false;
3837
- }
3838
- }
3839
- markLookupIndexReady(table, suffix) {
3840
- mkdirSync3(getIndexMarkerDir(), { recursive: true });
3841
- writeFileSync5(this.getLookupIndexMarkerPath(table, suffix), JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
3842
- }
3843
4163
  async ensureLookupIndex(table, suffix, columnsSql) {
3844
- if (this.hasFreshLookupIndexMarker(table, suffix))
4164
+ const markers = await getIndexMarkerStore();
4165
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, suffix);
4166
+ if (markers.hasFreshIndexMarker(markerPath))
3845
4167
  return;
3846
4168
  const indexName = this.buildLookupIndexName(table, suffix);
3847
4169
  try {
3848
4170
  await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ${columnsSql}`);
3849
- this.markLookupIndexReady(table, suffix);
4171
+ markers.writeIndexMarker(markerPath);
3850
4172
  } catch (e) {
3851
4173
  if (isDuplicateIndexError(e)) {
3852
- this.markLookupIndexReady(table, suffix);
4174
+ markers.writeIndexMarker(markerPath);
3853
4175
  return;
3854
4176
  }
3855
4177
  log3(`index "${indexName}" skipped: ${e.message}`);
3856
4178
  }
3857
4179
  }
4180
+ /**
4181
+ * Ensure a vector column exists on the given table.
4182
+ *
4183
+ * The previous implementation always issued `ALTER TABLE ADD COLUMN IF NOT
4184
+ * EXISTS …` on every SessionStart. On a long-running workspace that's
4185
+ * already migrated, every call returns 500 "Column already exists" — noisy
4186
+ * in the log and a wasted round-trip. Worse, the very first call after the
4187
+ * column is genuinely added triggers Deeplake's post-ALTER `vector::at`
4188
+ * window (~30s) during which subsequent INSERTs fail; minimising the
4189
+ * number of ALTER calls minimises exposure to that window.
4190
+ *
4191
+ * New flow:
4192
+ * 1. Check the local marker file (mirrors ensureLookupIndex). If fresh,
4193
+ * return — zero network calls.
4194
+ * 2. SELECT 1 FROM information_schema.columns WHERE table_name = T AND
4195
+ * column_name = C. Read-only, idempotent, can't tickle the post-ALTER
4196
+ * bug. If the column is present → mark + return.
4197
+ * 3. Only if step 2 says the column is missing, fall back to ALTER ADD
4198
+ * COLUMN IF NOT EXISTS. Mark on success, also mark if Deeplake reports
4199
+ * "already exists" (race: another client added it between our SELECT
4200
+ * and ALTER).
4201
+ *
4202
+ * Marker uses the same dir / TTL as ensureLookupIndex so both schema
4203
+ * caches share an opt-out (HIVEMIND_INDEX_MARKER_DIR) and a TTL knob.
4204
+ */
4205
+ async ensureEmbeddingColumn(table, column) {
4206
+ await this.ensureColumn(table, column, "FLOAT4[]");
4207
+ }
4208
+ /**
4209
+ * Generic marker-gated column migration. Same SELECT-then-ALTER flow as
4210
+ * ensureEmbeddingColumn, parameterized by SQL type so it can patch up any
4211
+ * column that was added to the schema after the table was originally
4212
+ * created. Used today for `summary_embedding`, `message_embedding`, and
4213
+ * the `agent` column (added 2026-04-11) — the latter has no fallback if
4214
+ * a user upgraded over a pre-2026-04-11 table, so every INSERT fails
4215
+ * with `column "agent" does not exist`.
4216
+ */
4217
+ async ensureColumn(table, column, sqlType) {
4218
+ const markers = await getIndexMarkerStore();
4219
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, `col_${column}`);
4220
+ if (markers.hasFreshIndexMarker(markerPath))
4221
+ return;
4222
+ const colCheck = `SELECT 1 FROM information_schema.columns WHERE table_name = '${sqlStr(table)}' AND column_name = '${sqlStr(column)}' AND table_schema = '${sqlStr(this.workspaceId)}' LIMIT 1`;
4223
+ const rows = await this.query(colCheck);
4224
+ if (rows.length > 0) {
4225
+ markers.writeIndexMarker(markerPath);
4226
+ return;
4227
+ }
4228
+ try {
4229
+ await this.query(`ALTER TABLE "${table}" ADD COLUMN ${column} ${sqlType}`);
4230
+ } catch (e) {
4231
+ const msg = e instanceof Error ? e.message : String(e);
4232
+ if (!/already exists/i.test(msg))
4233
+ throw e;
4234
+ const recheck = await this.query(colCheck);
4235
+ if (recheck.length === 0)
4236
+ throw e;
4237
+ }
4238
+ markers.writeIndexMarker(markerPath);
4239
+ }
3858
4240
  /** List all tables in the workspace (with retry). */
3859
4241
  async listTables(forceRefresh = false) {
3860
4242
  if (!forceRefresh && this._tablesCache)
@@ -3870,7 +4252,8 @@ var DeeplakeApi = class {
3870
4252
  const resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables`, {
3871
4253
  headers: {
3872
4254
  Authorization: `Bearer ${this.token}`,
3873
- "X-Activeloop-Org-Id": this.orgId
4255
+ "X-Activeloop-Org-Id": this.orgId,
4256
+ ...deeplakeClientHeader()
3874
4257
  }
3875
4258
  });
3876
4259
  if (resp.ok) {
@@ -3895,28 +4278,60 @@ var DeeplakeApi = class {
3895
4278
  }
3896
4279
  return { tables: [], cacheable: false };
3897
4280
  }
4281
+ /**
4282
+ * Run a `CREATE TABLE` with an extra outer retry budget. The base
4283
+ * `query()` already retries 3 times on fetch errors (~3.5s total), but a
4284
+ * failed CREATE is permanent corruption — every subsequent SELECT against
4285
+ * the missing table fails. Wrapping in an outer loop with longer backoff
4286
+ * (2s, 5s, then 10s) gives us ~17s of reach across transient network
4287
+ * blips before giving up. Failures still propagate; getApi() resets its
4288
+ * cache on init failure (openclaw plugin) so the next call retries the
4289
+ * whole init flow.
4290
+ */
4291
+ async createTableWithRetry(sql, label) {
4292
+ const OUTER_BACKOFFS_MS = [2e3, 5e3, 1e4];
4293
+ let lastErr = null;
4294
+ for (let attempt = 0; attempt <= OUTER_BACKOFFS_MS.length; attempt++) {
4295
+ try {
4296
+ await this.query(sql);
4297
+ return;
4298
+ } catch (err) {
4299
+ lastErr = err;
4300
+ const msg = err instanceof Error ? err.message : String(err);
4301
+ log3(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
4302
+ if (attempt < OUTER_BACKOFFS_MS.length) {
4303
+ await sleep(OUTER_BACKOFFS_MS[attempt]);
4304
+ }
4305
+ }
4306
+ }
4307
+ throw lastErr;
4308
+ }
3898
4309
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
3899
4310
  async ensureTable(name) {
3900
4311
  const tbl = name ?? this.tableName;
3901
4312
  const tables = await this.listTables();
3902
4313
  if (!tables.includes(tbl)) {
3903
4314
  log3(`table "${tbl}" not found, creating`);
3904
- await this.query(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`);
4315
+ await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
3905
4316
  log3(`table "${tbl}" created`);
3906
4317
  if (!tables.includes(tbl))
3907
4318
  this._tablesCache = [...tables, tbl];
3908
4319
  }
4320
+ await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
4321
+ await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
3909
4322
  }
3910
4323
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
3911
4324
  async ensureSessionsTable(name) {
3912
4325
  const tables = await this.listTables();
3913
4326
  if (!tables.includes(name)) {
3914
4327
  log3(`table "${name}" not found, creating`);
3915
- await this.query(`CREATE TABLE IF NOT EXISTS "${name}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`);
4328
+ await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${name}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, name);
3916
4329
  log3(`table "${name}" created`);
3917
4330
  if (!tables.includes(name))
3918
4331
  this._tablesCache = [...tables, name];
3919
4332
  }
4333
+ await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
4334
+ await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
3920
4335
  await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
3921
4336
  }
3922
4337
  };
@@ -4229,10 +4644,14 @@ var USAGE = `
4229
4644
  hivemind \u2014 one brain for every agent on your team
4230
4645
 
4231
4646
  Usage:
4232
- hivemind install [--only <platforms>] [--skip-auth]
4647
+ hivemind install [--only <platforms>] [--skip-auth]
4233
4648
  Auto-detect assistants on this machine and install hivemind into each.
4234
4649
  --only takes a comma-separated list: ${allPlatformIds().join(",")}
4235
4650
 
4651
+ hivemind uninstall [--only <platforms>]
4652
+ Auto-detect installed assistants and remove hivemind from each.
4653
+ --only takes the same list to scope the removal.
4654
+
4236
4655
  hivemind claude install | uninstall
4237
4656
  hivemind codex install | uninstall
4238
4657
  hivemind claw install | uninstall
@@ -4244,6 +4663,18 @@ Usage:
4244
4663
  hivemind login Run device-flow login (open browser).
4245
4664
  hivemind status Show which assistants are wired up.
4246
4665
 
4666
+ Semantic search (embeddings):
4667
+ hivemind embeddings install Download @huggingface/transformers
4668
+ once (~600 MB) into a shared dir
4669
+ and symlink every detected agent
4670
+ plugin to it. Idempotent.
4671
+ hivemind embeddings uninstall [--prune] Remove the per-agent symlinks.
4672
+ --prune also deletes the shared dir.
4673
+ hivemind embeddings status Show shared-deps + per-agent state.
4674
+
4675
+ Add --with-embeddings to "hivemind install" (or "hivemind <agent> install")
4676
+ to run "embeddings install" automatically after installing the agent(s).
4677
+
4247
4678
  Account / org / workspace:
4248
4679
  hivemind whoami Show current user, org, workspace.
4249
4680
  hivemind logout Remove credentials.
@@ -4284,6 +4715,7 @@ function hasFlag(args, flag) {
4284
4715
  async function runInstallAll(args) {
4285
4716
  const only = parseOnly(args);
4286
4717
  const skipAuth = hasFlag(args, "--skip-auth");
4718
+ const withEmbeddings = hasFlag(args, "--with-embeddings");
4287
4719
  const targets = only ?? detectPlatforms().map((p) => p.id);
4288
4720
  if (targets.length === 0) {
4289
4721
  log("No supported assistants detected.");
@@ -4302,6 +4734,10 @@ async function runInstallAll(args) {
4302
4734
  }
4303
4735
  for (const id of targets)
4304
4736
  runSingleInstall(id);
4737
+ if (withEmbeddings) {
4738
+ log("");
4739
+ enableEmbeddings();
4740
+ }
4305
4741
  await maybeShowOrgChoice();
4306
4742
  log("");
4307
4743
  log("Done. Restart each assistant to activate hooks.");
@@ -4383,6 +4819,23 @@ async function main() {
4383
4819
  runStatus();
4384
4820
  return;
4385
4821
  }
4822
+ if (cmd === "embeddings") {
4823
+ const sub = args[1];
4824
+ if (sub === "install" || sub === "enable") {
4825
+ enableEmbeddings();
4826
+ return;
4827
+ }
4828
+ if (sub === "uninstall" || sub === "disable") {
4829
+ disableEmbeddings({ prune: hasFlag(args.slice(2), "--prune") });
4830
+ return;
4831
+ }
4832
+ if (sub === "status") {
4833
+ statusEmbeddings();
4834
+ return;
4835
+ }
4836
+ warn("Usage: hivemind embeddings install | uninstall [--prune] | status");
4837
+ process.exit(1);
4838
+ }
4386
4839
  if (AUTH_SUBCOMMANDS.has(cmd)) {
4387
4840
  await runAuthCommand(args);
4388
4841
  return;
@@ -4390,12 +4843,16 @@ async function main() {
4390
4843
  const platformCmds = ["claude", "codex", "claw", "cursor", "hermes", "pi"];
4391
4844
  if (platformCmds.includes(cmd)) {
4392
4845
  const sub = args[1];
4393
- if (sub === "install")
4846
+ if (sub === "install") {
4394
4847
  runSingleInstall(cmd);
4395
- else if (sub === "uninstall")
4848
+ if (hasFlag(args.slice(2), "--with-embeddings")) {
4849
+ log("");
4850
+ enableEmbeddings();
4851
+ }
4852
+ } else if (sub === "uninstall")
4396
4853
  runSingleUninstall(cmd);
4397
4854
  else {
4398
- warn(`Usage: hivemind ${cmd} install|uninstall`);
4855
+ warn(`Usage: hivemind ${cmd} install [--with-embeddings] | uninstall`);
4399
4856
  process.exit(1);
4400
4857
  }
4401
4858
  return;