@deeplake/hivemind 0.7.78 → 0.7.80

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.
@@ -6,18 +6,18 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Cloud-backed persistent shared memory for AI agents powered by Deeplake",
9
- "version": "0.7.78"
9
+ "version": "0.7.80"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "hivemind",
14
14
  "description": "Persistent shared memory powered by Deeplake — captures all session activity and provides cross-session, cross-agent memory search",
15
- "version": "0.7.78",
15
+ "version": "0.7.80",
16
16
  "source": {
17
17
  "source": "git-subdir",
18
18
  "url": "https://github.com/activeloopai/hivemind.git",
19
19
  "path": "claude-code",
20
- "sha": "64e787b7d16918e72a53395fd79edc36836a2bd2"
20
+ "sha": "491ff3fe01c8ffe102dd4ed88dc67b645870c8af"
21
21
  },
22
22
  "homepage": "https://github.com/activeloopai/hivemind"
23
23
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hivemind",
3
3
  "description": "Cloud-backed persistent memory powered by Deeplake — read, write, and share memory across Claude Code sessions and agents",
4
- "version": "0.7.78",
4
+ "version": "0.7.80",
5
5
  "author": {
6
6
  "name": "Activeloop",
7
7
  "url": "https://deeplake.ai"
package/bundle/cli.js CHANGED
@@ -3622,6 +3622,7 @@ var WIKI_WORKER_DIR = join10(PI_AGENT_DIR, "hivemind");
3622
3622
  var WIKI_WORKER_PATH = join10(WIKI_WORKER_DIR, "wiki-worker.js");
3623
3623
  var SKILLIFY_WORKER_PATH = join10(WIKI_WORKER_DIR, "skillify-worker.js");
3624
3624
  var AUTOPULL_WORKER_PATH = join10(WIKI_WORKER_DIR, "autopull-worker.js");
3625
+ var SKILLOPT_WORKER_PATH = join10(WIKI_WORKER_DIR, "skillopt-worker.js");
3625
3626
  var HIVEMIND_BLOCK_START = "<!-- BEGIN hivemind-memory -->";
3626
3627
  var HIVEMIND_BLOCK_END = "<!-- END hivemind-memory -->";
3627
3628
  var HIVEMIND_BLOCK_BODY = `${HIVEMIND_BLOCK_START}
@@ -3716,6 +3717,11 @@ function installPi() {
3716
3717
  ensureDir(WIKI_WORKER_DIR);
3717
3718
  copyFileSync2(srcAutopullWorker, AUTOPULL_WORKER_PATH);
3718
3719
  }
3720
+ const srcSkilloptWorker = join10(pkgRoot(), "pi", "bundle", "skillopt-worker.js");
3721
+ if (existsSync9(srcSkilloptWorker)) {
3722
+ ensureDir(WIKI_WORKER_DIR);
3723
+ copyFileSync2(srcSkilloptWorker, SKILLOPT_WORKER_PATH);
3724
+ }
3719
3725
  ensureDir(VERSION_DIR);
3720
3726
  writeVersionStamp(VERSION_DIR, getVersion());
3721
3727
  log(` pi AGENTS.md updated -> ${AGENTS_MD}`);
@@ -3729,6 +3735,9 @@ function installPi() {
3729
3735
  if (existsSync9(AUTOPULL_WORKER_PATH)) {
3730
3736
  log(` pi autopull-worker installed -> ${AUTOPULL_WORKER_PATH}`);
3731
3737
  }
3738
+ if (existsSync9(SKILLOPT_WORKER_PATH)) {
3739
+ log(` pi skillopt-worker installed -> ${SKILLOPT_WORKER_PATH}`);
3740
+ }
3732
3741
  }
3733
3742
  function uninstallPi() {
3734
3743
  if (existsSync9(LEGACY_SKILL_DIR)) {
@@ -367,38 +367,38 @@ function readQueue() {
367
367
  return { queue: [] };
368
368
  }
369
369
  }
370
- function _isQueuePathInsideHome(path, home) {
371
- const r = resolve(path);
370
+ function _isQueuePathInsideHome(path2, home) {
371
+ const r = resolve(path2);
372
372
  const h = resolve(home);
373
373
  return r.startsWith(h + "/") || r === h;
374
374
  }
375
375
  function writeQueue(q) {
376
- const path = queuePath();
376
+ const path2 = queuePath();
377
377
  const home = resolve(homedir3());
378
- if (!_isQueuePathInsideHome(path, home)) {
379
- throw new Error(`notifications-queue write blocked: ${path} is outside ${home}`);
378
+ if (!_isQueuePathInsideHome(path2, home)) {
379
+ throw new Error(`notifications-queue write blocked: ${path2} is outside ${home}`);
380
380
  }
381
381
  mkdirSync(join3(home, ".deeplake"), { recursive: true, mode: 448 });
382
- const tmp = `${path}.${process.pid}.tmp`;
382
+ const tmp = `${path2}.${process.pid}.tmp`;
383
383
  writeFileSync(tmp, JSON.stringify(q, null, 2), { mode: 384 });
384
- renameSync(tmp, path);
384
+ renameSync(tmp, path2);
385
385
  }
386
386
  async function withQueueLock(fn) {
387
- const path = lockPath();
387
+ const path2 = lockPath();
388
388
  mkdirSync(join3(homedir3(), ".deeplake"), { recursive: true, mode: 448 });
389
389
  let fd = null;
390
390
  for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
391
391
  try {
392
- fd = openSync(path, "wx", 384);
392
+ fd = openSync(path2, "wx", 384);
393
393
  break;
394
394
  } catch (e) {
395
395
  const code = e.code;
396
396
  if (code !== "EEXIST")
397
397
  throw e;
398
398
  try {
399
- const age = Date.now() - statSync(path).mtimeMs;
399
+ const age = Date.now() - statSync(path2).mtimeMs;
400
400
  if (age > LOCK_STALE_MS) {
401
- unlinkSync(path);
401
+ unlinkSync(path2);
402
402
  continue;
403
403
  }
404
404
  } catch {
@@ -419,7 +419,7 @@ async function withQueueLock(fn) {
419
419
  } catch {
420
420
  }
421
421
  try {
422
- unlinkSync(path);
422
+ unlinkSync(path2);
423
423
  } catch {
424
424
  }
425
425
  }
@@ -695,9 +695,9 @@ var DeeplakeApi = class {
695
695
  }
696
696
  }
697
697
  /** Update specific columns on a row by path. */
698
- async updateColumns(path, columns) {
698
+ async updateColumns(path2, columns) {
699
699
  const setClauses = Object.entries(columns).map(([col, val]) => typeof val === "number" ? `${col} = ${val}` : `${col} = '${sqlStr(String(val))}'`).join(", ");
700
- await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(path)}'`);
700
+ await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(path2)}'`);
701
701
  }
702
702
  // ── Convenience ─────────────────────────────────────────────────────────────
703
703
  /** Create a BM25 search index on a column. */
@@ -1403,13 +1403,13 @@ var _migrated = false;
1403
1403
  function readUserConfig() {
1404
1404
  if (_cache !== null)
1405
1405
  return _cache;
1406
- const path = _configPath();
1407
- if (!existsSync4(path)) {
1406
+ const path2 = _configPath();
1407
+ if (!existsSync4(path2)) {
1408
1408
  _cache = {};
1409
1409
  return _cache;
1410
1410
  }
1411
1411
  try {
1412
- const raw = readFileSync6(path, "utf-8");
1412
+ const raw = readFileSync6(path2, "utf-8");
1413
1413
  const parsed = JSON.parse(raw);
1414
1414
  _cache = isPlainObject(parsed) ? parsed : {};
1415
1415
  } catch {
@@ -1420,13 +1420,13 @@ function readUserConfig() {
1420
1420
  function writeUserConfig(patch) {
1421
1421
  const current = readUserConfig();
1422
1422
  const merged = deepMerge(current, patch);
1423
- const path = _configPath();
1424
- const dir = dirname(path);
1423
+ const path2 = _configPath();
1424
+ const dir = dirname(path2);
1425
1425
  if (!existsSync4(dir))
1426
1426
  mkdirSync4(dir, { recursive: true });
1427
- const tmp = `${path}.tmp.${process.pid}`;
1427
+ const tmp = `${path2}.tmp.${process.pid}`;
1428
1428
  writeFileSync4(tmp, JSON.stringify(merged, null, 2) + "\n", "utf-8");
1429
- renameSync2(tmp, path);
1429
+ renameSync2(tmp, path2);
1430
1430
  _cache = merged;
1431
1431
  return merged;
1432
1432
  }
@@ -1571,8 +1571,8 @@ function createSymlinkAtomic(target, link) {
1571
1571
  }
1572
1572
 
1573
1573
  // dist/src/hooks/codex/capture.js
1574
- import { fileURLToPath as fileURLToPath2 } from "node:url";
1575
- import { dirname as dirname5, join as join14 } from "node:path";
1574
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
1575
+ import { dirname as dirname7, join as join17 } from "node:path";
1576
1576
 
1577
1577
  // dist/src/hooks/summary-state.js
1578
1578
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, writeSync as writeSync2, mkdirSync as mkdirSync6, renameSync as renameSync4, existsSync as existsSync6, unlinkSync as unlinkSync4, openSync as openSync3, closeSync as closeSync3, statSync as statSync3 } from "node:fs";
@@ -1719,13 +1719,13 @@ import { homedir as homedir10, tmpdir as tmpdir2 } from "node:os";
1719
1719
  import { mkdirSync as mkdirSync7, appendFileSync as appendFileSync2 } from "node:fs";
1720
1720
  import { join as join11 } from "node:path";
1721
1721
  function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
1722
- const path = join11(hooksDir, filename);
1722
+ const path2 = join11(hooksDir, filename);
1723
1723
  return {
1724
- path,
1724
+ path: path2,
1725
1725
  log(msg) {
1726
1726
  try {
1727
1727
  mkdirSync7(hooksDir, { recursive: true });
1728
- appendFileSync2(path, `[${utcTimestamp()}] ${msg}
1728
+ appendFileSync2(path2, `[${utcTimestamp()}] ${msg}
1729
1729
  `);
1730
1730
  } catch {
1731
1731
  }
@@ -1778,10 +1778,10 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
1778
1778
  // dist/src/utils/spawn-detached.js
1779
1779
  import { spawn as nodeSpawn } from "node:child_process";
1780
1780
  function spawnDetachedNodeWorker(workerPath, args = [], deps = {}) {
1781
- const spawn2 = deps.spawn ?? nodeSpawn;
1781
+ const spawn3 = deps.spawn ?? nodeSpawn;
1782
1782
  const execPath = deps.execPath ?? process.execPath;
1783
1783
  try {
1784
- const child = spawn2(execPath, [workerPath, ...args], {
1784
+ const child = spawn3(execPath, [workerPath, ...args], {
1785
1785
  detached: true,
1786
1786
  stdio: ["ignore", "ignore", "ignore"],
1787
1787
  // Suppress the transient console window Windows would otherwise pop for
@@ -1892,12 +1892,144 @@ function bundleDirFromImportMeta(importMetaUrl) {
1892
1892
  return dirname4(fileURLToPath(importMetaUrl));
1893
1893
  }
1894
1894
 
1895
+ // dist/src/skillify/skillopt-trigger.js
1896
+ import { spawn as spawn2 } from "node:child_process";
1897
+ import fs from "node:fs";
1898
+ import path from "node:path";
1899
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
1900
+
1901
+ // dist/src/skillify/state-dir.js
1902
+ import { homedir as homedir11 } from "node:os";
1903
+ import { join as join14 } from "node:path";
1904
+ function getStateDir() {
1905
+ const override = process.env.HIVEMIND_STATE_DIR?.trim();
1906
+ return override && override.length > 0 ? override : join14(homedir11(), ".deeplake", "state", "skillify");
1907
+ }
1908
+
1909
+ // dist/src/skillify/manifest.js
1910
+ import { existsSync as existsSync8, lstatSync as lstatSync2, mkdirSync as mkdirSync9, readFileSync as readFileSync9, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync7 } from "node:fs";
1911
+ import { dirname as dirname6, join as join16 } from "node:path";
1912
+
1913
+ // dist/src/skillify/legacy-migration.js
1914
+ import { existsSync as existsSync7, renameSync as renameSync5 } from "node:fs";
1915
+ import { dirname as dirname5, join as join15 } from "node:path";
1916
+
1917
+ // dist/src/skillify/skillopt-env.js
1918
+ var SKILLOPT_ENV = {
1919
+ /** User-set kill switch: "1" disables the whole trigger. */
1920
+ DISABLED: "HIVEMIND_SKILLOPT_DISABLED",
1921
+ /** Recursion guard the trigger sets on the spawned worker so the worker can't re-arm. */
1922
+ WORKER: "HIVEMIND_SKILLOPT_WORKER",
1923
+ /** Worker inputs, handed trigger → worker via the child env. */
1924
+ SESSION: "HIVEMIND_SKILLOPT_SESSION",
1925
+ SKILL: "HIVEMIND_SKILLOPT_SKILL",
1926
+ REACTION: "HIVEMIND_SKILLOPT_REACTION",
1927
+ TOOL_USE_ID: "HIVEMIND_SKILLOPT_TOOL_USE_ID",
1928
+ /** Which agent's CLI runs the judge/proposer (claude_code/codex/hermes/cursor/pi). */
1929
+ AGENT: "HIVEMIND_SKILLOPT_AGENT",
1930
+ /** K-message judgment-window size override. */
1931
+ JUDGE_WINDOW: "HIVEMIND_SKILLOPT_JUDGE_WINDOW"
1932
+ };
1933
+
1934
+ // dist/src/skillify/skillopt-trigger.js
1935
+ var log5 = (m) => log("skillopt-trigger", m);
1936
+ function defaultHasCreds() {
1937
+ try {
1938
+ return Boolean(loadConfig()?.token);
1939
+ } catch {
1940
+ return false;
1941
+ }
1942
+ }
1943
+ var MAX_REACTION = 8e3;
1944
+ function pendingFile(sessionId) {
1945
+ const safe = sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 200);
1946
+ return path.join(getStateDir(), "skillopt", "pending", `${safe}.json`);
1947
+ }
1948
+ var fileStore = {
1949
+ load(sessionId) {
1950
+ try {
1951
+ return JSON.parse(fs.readFileSync(pendingFile(sessionId), "utf8"));
1952
+ } catch {
1953
+ return null;
1954
+ }
1955
+ },
1956
+ save(sessionId, p) {
1957
+ try {
1958
+ const f = pendingFile(sessionId);
1959
+ if (p === null) {
1960
+ try {
1961
+ fs.unlinkSync(f);
1962
+ } catch {
1963
+ }
1964
+ return;
1965
+ }
1966
+ fs.mkdirSync(path.dirname(f), { recursive: true });
1967
+ const tmp = `${f}.${process.pid}.tmp`;
1968
+ fs.writeFileSync(tmp, JSON.stringify(p));
1969
+ fs.renameSync(tmp, f);
1970
+ } catch {
1971
+ }
1972
+ }
1973
+ };
1974
+ function runEventTrigger(sessionId, reaction, opts = {}) {
1975
+ const deps = opts.deps ?? {};
1976
+ const env = deps.env ?? process.env;
1977
+ if (env[SKILLOPT_ENV.DISABLED] === "1")
1978
+ return { fired: false, reason: "disabled" };
1979
+ if (env[SKILLOPT_ENV.WORKER] === "1")
1980
+ return { fired: false, reason: "in-worker" };
1981
+ if (!sessionId)
1982
+ return { fired: false, reason: "no-skill" };
1983
+ const store = deps.store ?? fileStore;
1984
+ const p = store.load(sessionId);
1985
+ if (!p)
1986
+ return { fired: false, reason: "no-skill" };
1987
+ if (!(deps.canFire ?? defaultHasCreds)())
1988
+ return { fired: false, reason: "no-creds" };
1989
+ store.save(sessionId, p.budget - 1 <= 0 ? null : { ...p, budget: p.budget - 1 });
1990
+ (deps.spawnWorker ?? spawnWorker)(sessionId, p.skill, reaction ?? "", p.toolUseId, opts.agent);
1991
+ return { fired: true, reason: "spawned" };
1992
+ }
1993
+ function spawnWorker(sessionId, skill, reaction, toolUseId, agent) {
1994
+ try {
1995
+ const here = path.dirname(fileURLToPath2(import.meta.url));
1996
+ const entry = path.join(here, "skillopt-worker.js");
1997
+ const child = spawn2(process.execPath, [entry], {
1998
+ detached: true,
1999
+ stdio: "ignore",
2000
+ env: {
2001
+ ...process.env,
2002
+ [SKILLOPT_ENV.WORKER]: "1",
2003
+ [SKILLOPT_ENV.SESSION]: sessionId,
2004
+ [SKILLOPT_ENV.SKILL]: skill,
2005
+ [SKILLOPT_ENV.REACTION]: (reaction ?? "").slice(0, MAX_REACTION),
2006
+ ...toolUseId ? { [SKILLOPT_ENV.TOOL_USE_ID]: toolUseId } : {},
2007
+ ...agent ? { [SKILLOPT_ENV.AGENT]: agent } : {}
2008
+ }
2009
+ });
2010
+ child.unref();
2011
+ log5(`spawned skillopt worker for ${skill} in ${sessionId}${agent ? ` (agent=${agent})` : ""}`);
2012
+ } catch (e) {
2013
+ log5(`spawn failed (swallowed): ${e?.message ?? e}`);
2014
+ }
2015
+ }
2016
+
2017
+ // dist/src/hooks/shared/skillopt-hook.js
2018
+ function reactSkillOpt(sessionId, prompt, agent) {
2019
+ try {
2020
+ if (prompt === void 0 || prompt.trim() === "" || process.env.HIVEMIND_WIKI_WORKER === "1")
2021
+ return;
2022
+ runEventTrigger(sessionId, prompt, { agent });
2023
+ } catch {
2024
+ }
2025
+ }
2026
+
1895
2027
  // dist/src/hooks/codex/capture.js
1896
- var log5 = (msg) => log("codex-capture", msg);
2028
+ var log6 = (msg) => log("codex-capture", msg);
1897
2029
  function resolveEmbedDaemonPath() {
1898
- return join14(dirname5(fileURLToPath2(import.meta.url)), "embeddings", "embed-daemon.js");
2030
+ return join17(dirname7(fileURLToPath3(import.meta.url)), "embeddings", "embed-daemon.js");
1899
2031
  }
1900
- var __bundleDir = dirname5(fileURLToPath2(import.meta.url));
2032
+ var __bundleDir = dirname7(fileURLToPath3(import.meta.url));
1901
2033
  var PLUGIN_VERSION = getInstalledVersion(__bundleDir, ".codex-plugin") ?? "";
1902
2034
  if (!embeddingsDisabled()) {
1903
2035
  try {
@@ -1912,7 +2044,7 @@ async function main() {
1912
2044
  const input = await readStdin();
1913
2045
  const config = loadConfig();
1914
2046
  if (!config) {
1915
- log5("no config");
2047
+ log6("no config");
1916
2048
  return;
1917
2049
  }
1918
2050
  const sessionsTable = config.sessionsTableName;
@@ -1929,7 +2061,7 @@ async function main() {
1929
2061
  };
1930
2062
  let entry;
1931
2063
  if (input.hook_event_name === "UserPromptSubmit" && input.prompt !== void 0) {
1932
- log5(`user session=${input.session_id}`);
2064
+ log6(`user session=${input.session_id}`);
1933
2065
  entry = {
1934
2066
  id: crypto.randomUUID(),
1935
2067
  ...meta,
@@ -1937,7 +2069,7 @@ async function main() {
1937
2069
  content: input.prompt
1938
2070
  };
1939
2071
  } else if (input.hook_event_name === "PostToolUse" && input.tool_name !== void 0) {
1940
- log5(`tool=${input.tool_name} session=${input.session_id}`);
2072
+ log6(`tool=${input.tool_name} session=${input.session_id}`);
1941
2073
  entry = {
1942
2074
  id: crypto.randomUUID(),
1943
2075
  ...meta,
@@ -1948,12 +2080,12 @@ async function main() {
1948
2080
  tool_response: JSON.stringify(input.tool_response)
1949
2081
  };
1950
2082
  } else {
1951
- log5(`unknown event: ${input.hook_event_name}, skipping`);
2083
+ log6(`unknown event: ${input.hook_event_name}, skipping`);
1952
2084
  return;
1953
2085
  }
1954
2086
  const sessionPath = buildSessionPath(config, input.session_id);
1955
2087
  const line = JSON.stringify(entry);
1956
- log5(`writing to ${sessionPath}`);
2088
+ log6(`writing to ${sessionPath}`);
1957
2089
  const projectName = projectNameFromCwd(input.cwd);
1958
2090
  const filename = sessionPath.split("/").pop() ?? "";
1959
2091
  const jsonForSql = line.replace(/'/g, "''");
@@ -1964,14 +2096,15 @@ async function main() {
1964
2096
  await api.query(insertSql);
1965
2097
  } catch (e) {
1966
2098
  if (e.message?.includes("permission denied") || e.message?.includes("does not exist")) {
1967
- log5("table missing, creating and retrying");
2099
+ log6("table missing, creating and retrying");
1968
2100
  await api.ensureSessionsTable(sessionsTable);
1969
2101
  await api.query(insertSql);
1970
2102
  } else {
1971
2103
  throw e;
1972
2104
  }
1973
2105
  }
1974
- log5("capture ok");
2106
+ log6("capture ok");
2107
+ reactSkillOpt(input.session_id, input.prompt, "codex");
1975
2108
  maybeTriggerPeriodicSummary(input.session_id, input.cwd ?? "", config);
1976
2109
  }
1977
2110
  function maybeTriggerPeriodicSummary(sessionId, cwd, config) {
@@ -1983,7 +2116,7 @@ function maybeTriggerPeriodicSummary(sessionId, cwd, config) {
1983
2116
  if (!shouldTrigger(state, cfg))
1984
2117
  return;
1985
2118
  if (!tryAcquireLock(sessionId)) {
1986
- log5(`periodic trigger suppressed (lock held) session=${sessionId}`);
2119
+ log6(`periodic trigger suppressed (lock held) session=${sessionId}`);
1987
2120
  return;
1988
2121
  }
1989
2122
  wikiLog(`Periodic: threshold hit (total=${state.totalCount}, since=${state.totalCount - state.lastSummaryCount}, N=${cfg.everyNMessages}, hours=${cfg.everyHours})`);
@@ -1996,19 +2129,19 @@ function maybeTriggerPeriodicSummary(sessionId, cwd, config) {
1996
2129
  reason: "Periodic"
1997
2130
  });
1998
2131
  } catch (e) {
1999
- log5(`periodic spawn failed: ${e.message}`);
2132
+ log6(`periodic spawn failed: ${e.message}`);
2000
2133
  try {
2001
2134
  releaseLock(sessionId);
2002
2135
  } catch (releaseErr) {
2003
- log5(`releaseLock after periodic spawn failure also failed: ${releaseErr.message}`);
2136
+ log6(`releaseLock after periodic spawn failure also failed: ${releaseErr.message}`);
2004
2137
  }
2005
2138
  throw e;
2006
2139
  }
2007
2140
  } catch (e) {
2008
- log5(`periodic trigger error: ${e.message}`);
2141
+ log6(`periodic trigger error: ${e.message}`);
2009
2142
  }
2010
2143
  }
2011
2144
  main().catch((e) => {
2012
- log5(`fatal: ${e.message}`);
2145
+ log6(`fatal: ${e.message}`);
2013
2146
  process.exit(0);
2014
2147
  });