@deeplake/hivemind 0.7.21 → 0.7.22

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.
@@ -110,7 +110,7 @@ function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
110
110
  // dist/src/hooks/hermes/spawn-wiki-worker.js
111
111
  import { spawn, execSync } from "node:child_process";
112
112
  import { fileURLToPath } from "node:url";
113
- import { dirname, join as join5 } from "node:path";
113
+ import { dirname as dirname2, join as join6 } from "node:path";
114
114
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "node:fs";
115
115
  import { homedir as homedir4, tmpdir } from "node:os";
116
116
 
@@ -132,9 +132,51 @@ function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
132
132
  };
133
133
  }
134
134
 
135
+ // dist/src/utils/version-check.js
136
+ import { readFileSync as readFileSync3 } from "node:fs";
137
+ import { dirname, join as join5 } from "node:path";
138
+ function getInstalledVersion(bundleDir, pluginManifestDir) {
139
+ try {
140
+ const pluginJson = join5(bundleDir, "..", pluginManifestDir, "plugin.json");
141
+ const plugin = JSON.parse(readFileSync3(pluginJson, "utf-8"));
142
+ if (plugin.version)
143
+ return plugin.version;
144
+ } catch {
145
+ }
146
+ try {
147
+ const stamp = readFileSync3(join5(bundleDir, "..", ".hivemind_version"), "utf-8").trim();
148
+ if (stamp)
149
+ return stamp;
150
+ } catch {
151
+ }
152
+ const HIVEMIND_PKG_NAMES = /* @__PURE__ */ new Set([
153
+ "hivemind",
154
+ "hivemind-codex",
155
+ "@deeplake/hivemind",
156
+ "@deeplake/hivemind-codex",
157
+ "@activeloop/hivemind",
158
+ "@activeloop/hivemind-codex"
159
+ ]);
160
+ let dir = bundleDir;
161
+ for (let i = 0; i < 5; i++) {
162
+ const candidate = join5(dir, "package.json");
163
+ try {
164
+ const pkg = JSON.parse(readFileSync3(candidate, "utf-8"));
165
+ if (HIVEMIND_PKG_NAMES.has(pkg.name) && pkg.version)
166
+ return pkg.version;
167
+ } catch {
168
+ }
169
+ const parent = dirname(dir);
170
+ if (parent === dir)
171
+ break;
172
+ dir = parent;
173
+ }
174
+ return null;
175
+ }
176
+
135
177
  // dist/src/hooks/hermes/spawn-wiki-worker.js
136
178
  var HOME = homedir4();
137
- var wikiLogger = makeWikiLogger(join5(HOME, ".hermes", "hooks"));
179
+ var wikiLogger = makeWikiLogger(join6(HOME, ".hermes", "hooks"));
138
180
  var WIKI_LOG = wikiLogger.path;
139
181
  var WIKI_PROMPT_TEMPLATE = `You are building a personal wiki from a coding session. Your goal is to extract every piece of knowledge \u2014 entities, decisions, relationships, and facts \u2014 into a structured, searchable wiki entry.
140
182
 
@@ -196,9 +238,10 @@ function findHermesBin() {
196
238
  function spawnHermesWikiWorker(opts) {
197
239
  const { config, sessionId, cwd, bundleDir, reason } = opts;
198
240
  const projectName = cwd.split("/").pop() || "unknown";
199
- const tmpDir = join5(tmpdir(), `deeplake-wiki-${sessionId}-${Date.now()}`);
241
+ const tmpDir = join6(tmpdir(), `deeplake-wiki-${sessionId}-${Date.now()}`);
200
242
  mkdirSync3(tmpDir, { recursive: true });
201
- const configFile = join5(tmpDir, "config.json");
243
+ const pluginVersion = getInstalledVersion(bundleDir, ".claude-plugin") ?? "";
244
+ const configFile = join6(tmpDir, "config.json");
202
245
  writeFileSync2(configFile, JSON.stringify({
203
246
  apiUrl: config.apiUrl,
204
247
  token: config.token,
@@ -209,16 +252,17 @@ function spawnHermesWikiWorker(opts) {
209
252
  sessionId,
210
253
  userName: config.userName,
211
254
  project: projectName,
255
+ pluginVersion,
212
256
  tmpDir,
213
257
  hermesBin: findHermesBin(),
214
258
  hermesProvider: process.env.HIVEMIND_HERMES_PROVIDER ?? "openrouter",
215
259
  hermesModel: process.env.HIVEMIND_HERMES_MODEL ?? "anthropic/claude-haiku-4-5",
216
260
  wikiLog: WIKI_LOG,
217
- hooksDir: join5(HOME, ".hermes", "hooks"),
261
+ hooksDir: join6(HOME, ".hermes", "hooks"),
218
262
  promptTemplate: WIKI_PROMPT_TEMPLATE
219
263
  }));
220
264
  wikiLog(`${reason}: spawning summary worker for ${sessionId}`);
221
- const workerPath = join5(bundleDir, "wiki-worker.js");
265
+ const workerPath = join6(bundleDir, "wiki-worker.js");
222
266
  spawn("nohup", ["node", workerPath, configFile], {
223
267
  detached: true,
224
268
  stdio: ["ignore", "ignore", "ignore"]
@@ -226,13 +270,13 @@ function spawnHermesWikiWorker(opts) {
226
270
  wikiLog(`${reason}: spawned summary worker for ${sessionId}`);
227
271
  }
228
272
  function bundleDirFromImportMeta(importMetaUrl) {
229
- return dirname(fileURLToPath(importMetaUrl));
273
+ return dirname2(fileURLToPath(importMetaUrl));
230
274
  }
231
275
 
232
276
  // dist/src/skillify/spawn-skillify-worker.js
233
277
  import { spawn as spawn2 } from "node:child_process";
234
278
  import { fileURLToPath as fileURLToPath2 } from "node:url";
235
- import { dirname as dirname2, join as join7 } from "node:path";
279
+ import { dirname as dirname3, join as join8 } from "node:path";
236
280
  import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, appendFileSync as appendFileSync3, chmodSync } from "node:fs";
237
281
  import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
238
282
 
@@ -240,7 +284,7 @@ import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
240
284
  import { execFileSync } from "node:child_process";
241
285
  import { existsSync as existsSync3 } from "node:fs";
242
286
  import { homedir as homedir5 } from "node:os";
243
- import { join as join6 } from "node:path";
287
+ import { join as join7 } from "node:path";
244
288
  function findAgentBin(agent) {
245
289
  const which = (name) => {
246
290
  try {
@@ -255,24 +299,24 @@ function findAgentBin(agent) {
255
299
  };
256
300
  switch (agent) {
257
301
  case "claude_code":
258
- return which("claude") ?? join6(homedir5(), ".claude", "local", "claude");
302
+ return which("claude") ?? join7(homedir5(), ".claude", "local", "claude");
259
303
  case "codex":
260
304
  return which("codex") ?? "/usr/local/bin/codex";
261
305
  case "cursor":
262
306
  return which("cursor-agent") ?? "/usr/local/bin/cursor-agent";
263
307
  case "hermes":
264
- return which("hermes") ?? join6(homedir5(), ".local", "bin", "hermes");
308
+ return which("hermes") ?? join7(homedir5(), ".local", "bin", "hermes");
265
309
  case "pi":
266
- return which("pi") ?? join6(homedir5(), ".local", "bin", "pi");
310
+ return which("pi") ?? join7(homedir5(), ".local", "bin", "pi");
267
311
  }
268
312
  }
269
313
 
270
314
  // dist/src/skillify/spawn-skillify-worker.js
271
315
  var HOME2 = homedir6();
272
- var SKILLIFY_LOG = join7(HOME2, ".claude", "hooks", "skillify.log");
316
+ var SKILLIFY_LOG = join8(HOME2, ".claude", "hooks", "skillify.log");
273
317
  function skillifyLog(msg) {
274
318
  try {
275
- mkdirSync4(dirname2(SKILLIFY_LOG), { recursive: true });
319
+ mkdirSync4(dirname3(SKILLIFY_LOG), { recursive: true });
276
320
  appendFileSync3(SKILLIFY_LOG, `[${utcTimestamp()}] ${msg}
277
321
  `);
278
322
  } catch {
@@ -280,10 +324,10 @@ function skillifyLog(msg) {
280
324
  }
281
325
  function spawnSkillifyWorker(opts) {
282
326
  const { config, cwd, projectKey, project, bundleDir, agent, scopeConfig, currentSessionId, reason } = opts;
283
- const tmpDir = join7(tmpdir2(), `deeplake-skillify-${projectKey}-${Date.now()}`);
327
+ const tmpDir = join8(tmpdir2(), `deeplake-skillify-${projectKey}-${Date.now()}`);
284
328
  mkdirSync4(tmpDir, { recursive: true, mode: 448 });
285
329
  const gateBin = findAgentBin(agent);
286
- const configFile = join7(tmpDir, "config.json");
330
+ const configFile = join8(tmpDir, "config.json");
287
331
  writeFileSync3(configFile, JSON.stringify({
288
332
  apiUrl: config.apiUrl,
289
333
  token: config.token,
@@ -314,7 +358,7 @@ function spawnSkillifyWorker(opts) {
314
358
  } catch {
315
359
  }
316
360
  skillifyLog(`${reason}: spawning skillify worker for project=${project} key=${projectKey}`);
317
- const workerPath = join7(bundleDir, "skillify-worker.js");
361
+ const workerPath = join8(bundleDir, "skillify-worker.js");
318
362
  spawn2("nohup", ["node", workerPath, configFile], {
319
363
  detached: true,
320
364
  stdio: ["ignore", "ignore", "ignore"]
@@ -323,25 +367,25 @@ function spawnSkillifyWorker(opts) {
323
367
  }
324
368
 
325
369
  // dist/src/skillify/state.js
326
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, writeSync as writeSync2, mkdirSync as mkdirSync5, renameSync as renameSync3, existsSync as existsSync5, unlinkSync as unlinkSync2, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
370
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, writeSync as writeSync2, mkdirSync as mkdirSync5, renameSync as renameSync3, existsSync as existsSync5, unlinkSync as unlinkSync2, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
327
371
  import { execSync as execSync2 } from "node:child_process";
328
372
  import { homedir as homedir8 } from "node:os";
329
373
  import { createHash } from "node:crypto";
330
- import { join as join9, basename } from "node:path";
374
+ import { join as join10, basename } from "node:path";
331
375
 
332
376
  // dist/src/skillify/legacy-migration.js
333
377
  import { existsSync as existsSync4, renameSync as renameSync2 } from "node:fs";
334
378
  import { homedir as homedir7 } from "node:os";
335
- import { join as join8 } from "node:path";
379
+ import { join as join9 } from "node:path";
336
380
  var dlog2 = (msg) => log("skillify-migrate", msg);
337
381
  var attempted = false;
338
382
  function migrateLegacyStateDir() {
339
383
  if (attempted)
340
384
  return;
341
385
  attempted = true;
342
- const root = join8(homedir7(), ".deeplake", "state");
343
- const legacy = join8(root, "skilify");
344
- const current = join8(root, "skillify");
386
+ const root = join9(homedir7(), ".deeplake", "state");
387
+ const legacy = join9(root, "skilify");
388
+ const current = join9(root, "skillify");
345
389
  if (!existsSync4(legacy))
346
390
  return;
347
391
  if (existsSync4(current))
@@ -361,17 +405,17 @@ function migrateLegacyStateDir() {
361
405
 
362
406
  // dist/src/skillify/state.js
363
407
  var dlog3 = (msg) => log("skillify-state", msg);
364
- var STATE_DIR2 = join9(homedir8(), ".deeplake", "state", "skillify");
408
+ var STATE_DIR2 = join10(homedir8(), ".deeplake", "state", "skillify");
365
409
  var YIELD_BUF2 = new Int32Array(new SharedArrayBuffer(4));
366
410
  var TRIGGER_THRESHOLD = (() => {
367
411
  const n = Number(process.env.HIVEMIND_SKILLIFY_EVERY_N_TURNS ?? "");
368
412
  return Number.isInteger(n) && n > 0 ? n : 20;
369
413
  })();
370
414
  function statePath(projectKey) {
371
- return join9(STATE_DIR2, `${projectKey}.json`);
415
+ return join10(STATE_DIR2, `${projectKey}.json`);
372
416
  }
373
417
  function lockPath2(projectKey) {
374
- return join9(STATE_DIR2, `${projectKey}.lock`);
418
+ return join10(STATE_DIR2, `${projectKey}.lock`);
375
419
  }
376
420
  var DEFAULT_PORTS = {
377
421
  http: "80",
@@ -420,7 +464,7 @@ function readState(projectKey) {
420
464
  if (!existsSync5(p))
421
465
  return null;
422
466
  try {
423
- return JSON.parse(readFileSync3(p, "utf-8"));
467
+ return JSON.parse(readFileSync4(p, "utf-8"));
424
468
  } catch {
425
469
  return null;
426
470
  }
@@ -482,7 +526,7 @@ function tryAcquireWorkerLock(projectKey, maxAgeMs = 10 * 60 * 1e3) {
482
526
  const p = lockPath2(projectKey);
483
527
  if (existsSync5(p)) {
484
528
  try {
485
- const ageMs = Date.now() - parseInt(readFileSync3(p, "utf-8"), 10);
529
+ const ageMs = Date.now() - parseInt(readFileSync4(p, "utf-8"), 10);
486
530
  if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
487
531
  return false;
488
532
  } catch (readErr) {
@@ -516,18 +560,18 @@ function releaseWorkerLock(projectKey) {
516
560
  }
517
561
 
518
562
  // dist/src/skillify/scope-config.js
519
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
563
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
520
564
  import { homedir as homedir9 } from "node:os";
521
- import { join as join10 } from "node:path";
522
- var STATE_DIR3 = join10(homedir9(), ".deeplake", "state", "skillify");
523
- var CONFIG_PATH = join10(STATE_DIR3, "config.json");
565
+ import { join as join11 } from "node:path";
566
+ var STATE_DIR3 = join11(homedir9(), ".deeplake", "state", "skillify");
567
+ var CONFIG_PATH = join11(STATE_DIR3, "config.json");
524
568
  var DEFAULT = { scope: "me", team: [], install: "project" };
525
569
  function loadScopeConfig() {
526
570
  migrateLegacyStateDir();
527
571
  if (!existsSync6(CONFIG_PATH))
528
572
  return DEFAULT;
529
573
  try {
530
- const raw = JSON.parse(readFileSync4(CONFIG_PATH, "utf-8"));
574
+ const raw = JSON.parse(readFileSync5(CONFIG_PATH, "utf-8"));
531
575
  const scope = raw.scope === "team" || raw.scope === "org" ? raw.scope : "me";
532
576
  const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
533
577
  const install = raw.install === "global" ? "global" : "project";
@@ -511,13 +511,14 @@ var DeeplakeApi = class {
511
511
  const tables = await this.listTables();
512
512
  if (!tables.includes(tbl)) {
513
513
  log2(`table "${tbl}" not found, creating`);
514
- 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);
514
+ 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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
515
515
  log2(`table "${tbl}" created`);
516
516
  if (!tables.includes(tbl))
517
517
  this._tablesCache = [...tables, tbl];
518
518
  }
519
519
  await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
520
520
  await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
521
+ await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
521
522
  }
522
523
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
523
524
  async ensureSessionsTable(name) {
@@ -525,13 +526,14 @@ var DeeplakeApi = class {
525
526
  const tables = await this.listTables();
526
527
  if (!tables.includes(safe)) {
527
528
  log2(`table "${safe}" not found, creating`);
528
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (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`, safe);
529
+ await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
529
530
  log2(`table "${safe}" created`);
530
531
  if (!tables.includes(safe))
531
532
  this._tablesCache = [...tables, safe];
532
533
  }
533
534
  await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
534
535
  await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
536
+ await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
535
537
  await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
536
538
  }
537
539
  /**
@@ -1315,7 +1317,7 @@ SKILLS (skillify) \u2014 mine + share reusable skills across the org:
1315
1317
  - hivemind skillify scope <me|team|org> \u2014 sharing scope for new skills
1316
1318
  - hivemind skillify install <project|global> \u2014 default install location
1317
1319
  - hivemind skillify team add|remove|list <name> \u2014 manage team list`;
1318
- async function createPlaceholder(api, table, sessionId, cwd, userName, orgName, workspaceId) {
1320
+ async function createPlaceholder(api, table, sessionId, cwd, userName, orgName, workspaceId, pluginVersion) {
1319
1321
  const summaryPath = `/summaries/${userName}/${sessionId}.md`;
1320
1322
  const existing = await api.query(`SELECT path FROM "${table}" WHERE path = '${sqlStr(summaryPath)}' LIMIT 1`);
1321
1323
  if (existing.length > 0)
@@ -1332,7 +1334,7 @@ async function createPlaceholder(api, table, sessionId, cwd, userName, orgName,
1332
1334
  ""
1333
1335
  ].join("\n");
1334
1336
  const filename = `${sessionId}.md`;
1335
- await api.query(`INSERT INTO "${table}" (id, path, filename, summary, author, mime_type, size_bytes, project, description, agent, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(summaryPath)}', '${sqlStr(filename)}', E'${sqlStr(content)}', '${sqlStr(userName)}', 'text/markdown', ${Buffer.byteLength(content, "utf-8")}, '${sqlStr(projectName)}', 'in progress', 'hermes', '${now}', '${now}')`);
1337
+ await api.query(`INSERT INTO "${table}" (id, path, filename, summary, author, mime_type, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(summaryPath)}', '${sqlStr(filename)}', E'${sqlStr(content)}', '${sqlStr(userName)}', 'text/markdown', ${Buffer.byteLength(content, "utf-8")}, '${sqlStr(projectName)}', 'in progress', 'hermes', '${sqlStr(pluginVersion)}', '${now}', '${now}')`);
1336
1338
  }
1337
1339
  async function main() {
1338
1340
  if (process.env.HIVEMIND_WIKI_WORKER === "1")
@@ -1343,6 +1345,8 @@ async function main() {
1343
1345
  const creds = loadCredentials();
1344
1346
  const captureEnabled = process.env.HIVEMIND_CAPTURE !== "false";
1345
1347
  await autoUpdate(creds, { agent: "hermes" });
1348
+ const current = getInstalledVersion(__bundleDir, ".claude-plugin");
1349
+ const pluginVersion = current ?? "";
1346
1350
  if (creds?.token && captureEnabled) {
1347
1351
  try {
1348
1352
  const config = loadConfig();
@@ -1350,7 +1354,7 @@ async function main() {
1350
1354
  const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
1351
1355
  await api.ensureTable();
1352
1356
  await api.ensureSessionsTable(config.sessionsTableName);
1353
- await createPlaceholder(api, config.tableName, sessionId, cwd, config.userName, config.orgName, config.workspaceId);
1357
+ await createPlaceholder(api, config.tableName, sessionId, cwd, config.userName, config.orgName, config.workspaceId, pluginVersion);
1354
1358
  log5("placeholder created");
1355
1359
  }
1356
1360
  } catch (e) {
@@ -1360,7 +1364,6 @@ async function main() {
1360
1364
  const pullResult = await autoPullSkills();
1361
1365
  log5(`autopull: pulled=${pullResult.pulled} skipped=${pullResult.skipped}`);
1362
1366
  let versionNotice = "";
1363
- const current = getInstalledVersion(__bundleDir, ".claude-plugin");
1364
1367
  if (current)
1365
1368
  versionNotice = `
1366
1369
  Hivemind v${current}`;
@@ -67201,13 +67201,14 @@ var DeeplakeApi = class {
67201
67201
  const tables = await this.listTables();
67202
67202
  if (!tables.includes(tbl)) {
67203
67203
  log2(`table "${tbl}" not found, creating`);
67204
- 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);
67204
+ 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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
67205
67205
  log2(`table "${tbl}" created`);
67206
67206
  if (!tables.includes(tbl))
67207
67207
  this._tablesCache = [...tables, tbl];
67208
67208
  }
67209
67209
  await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
67210
67210
  await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
67211
+ await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
67211
67212
  }
67212
67213
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
67213
67214
  async ensureSessionsTable(name) {
@@ -67215,13 +67216,14 @@ var DeeplakeApi = class {
67215
67216
  const tables = await this.listTables();
67216
67217
  if (!tables.includes(safe)) {
67217
67218
  log2(`table "${safe}" not found, creating`);
67218
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (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`, safe);
67219
+ await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
67219
67220
  log2(`table "${safe}" created`);
67220
67221
  if (!tables.includes(safe))
67221
67222
  this._tablesCache = [...tables, safe];
67222
67223
  }
67223
67224
  await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
67224
67225
  await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
67226
+ await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
67225
67227
  await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
67226
67228
  }
67227
67229
  /**
@@ -135,13 +135,16 @@ async function uploadSummary(query2, params) {
135
135
  const desc = extractDescription(text);
136
136
  const sizeBytes = Buffer.byteLength(text);
137
137
  const embSql = embeddingSqlLiteral(params.embedding ?? null);
138
+ const pluginVersion = params.pluginVersion;
138
139
  const existing = await query2(`SELECT path FROM "${tableName}" WHERE path = '${esc(vpath)}' LIMIT 1`);
139
140
  if (existing.length > 0) {
140
- const sql2 = `UPDATE "${tableName}" SET summary = E'${esc(text)}', summary_embedding = ${embSql}, size_bytes = ${sizeBytes}, description = E'${esc(desc)}', last_update_date = '${ts}' WHERE path = '${esc(vpath)}'`;
141
+ const pluginVersionSet = pluginVersion === void 0 ? "" : `plugin_version = '${esc(pluginVersion)}', `;
142
+ const sql2 = `UPDATE "${tableName}" SET summary = E'${esc(text)}', summary_embedding = ${embSql}, size_bytes = ${sizeBytes}, description = E'${esc(desc)}', ` + pluginVersionSet + `last_update_date = '${ts}' WHERE path = '${esc(vpath)}'`;
141
143
  await query2(sql2);
142
144
  return { path: "update", sql: sql2, descLength: desc.length, summaryLength: text.length };
143
145
  }
144
- const sql = `INSERT INTO "${tableName}" (id, path, filename, summary, summary_embedding, author, mime_type, size_bytes, project, description, agent, creation_date, last_update_date) VALUES ('${randomUUID()}', '${esc(vpath)}', '${esc(fname)}', E'${esc(text)}', ${embSql}, '${esc(userName)}', 'text/markdown', ${sizeBytes}, '${esc(project)}', E'${esc(desc)}', '${esc(agent)}', '${ts}', '${ts}')`;
146
+ const pluginVersionForInsert = pluginVersion ?? "";
147
+ const sql = `INSERT INTO "${tableName}" (id, path, filename, summary, summary_embedding, author, mime_type, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) VALUES ('${randomUUID()}', '${esc(vpath)}', '${esc(fname)}', E'${esc(text)}', ${embSql}, '${esc(userName)}', 'text/markdown', ${sizeBytes}, '${esc(project)}', E'${esc(desc)}', '${esc(agent)}', '${esc(pluginVersionForInsert)}', '${ts}', '${ts}')`;
145
148
  await query2(sql);
146
149
  return { path: "insert", sql, descLength: desc.length, summaryLength: text.length };
147
150
  }
@@ -544,7 +547,8 @@ async function main() {
544
547
  agent: "hermes",
545
548
  sessionId: cfg.sessionId,
546
549
  text,
547
- embedding
550
+ embedding,
551
+ pluginVersion: cfg.pluginVersion ?? ""
548
552
  });
549
553
  wlog(`uploaded ${vpath} (summary=${result.summaryLength}, desc=${result.descLength})`);
550
554
  try {
@@ -23724,13 +23724,14 @@ var DeeplakeApi = class {
23724
23724
  const tables = await this.listTables();
23725
23725
  if (!tables.includes(tbl)) {
23726
23726
  log2(`table "${tbl}" not found, creating`);
23727
- 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);
23727
+ 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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
23728
23728
  log2(`table "${tbl}" created`);
23729
23729
  if (!tables.includes(tbl))
23730
23730
  this._tablesCache = [...tables, tbl];
23731
23731
  }
23732
23732
  await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
23733
23733
  await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
23734
+ await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
23734
23735
  }
23735
23736
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
23736
23737
  async ensureSessionsTable(name) {
@@ -23738,13 +23739,14 @@ var DeeplakeApi = class {
23738
23739
  const tables = await this.listTables();
23739
23740
  if (!tables.includes(safe)) {
23740
23741
  log2(`table "${safe}" not found, creating`);
23741
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (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`, safe);
23742
+ await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
23742
23743
  log2(`table "${safe}" created`);
23743
23744
  if (!tables.includes(safe))
23744
23745
  this._tablesCache = [...tables, safe];
23745
23746
  }
23746
23747
  await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
23747
23748
  await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
23749
+ await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
23748
23750
  await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
23749
23751
  }
23750
23752
  /**
@@ -461,7 +461,7 @@ var DeeplakeApi = class {
461
461
  if (!tables.includes(tbl)) {
462
462
  log2(`table "${tbl}" not found, creating`);
463
463
  await this.createTableWithRetry(
464
- `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`,
464
+ `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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`,
465
465
  tbl
466
466
  );
467
467
  log2(`table "${tbl}" created`);
@@ -469,6 +469,7 @@ var DeeplakeApi = class {
469
469
  }
470
470
  await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
471
471
  await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
472
+ await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
472
473
  }
473
474
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
474
475
  async ensureSessionsTable(name) {
@@ -477,7 +478,7 @@ var DeeplakeApi = class {
477
478
  if (!tables.includes(safe)) {
478
479
  log2(`table "${safe}" not found, creating`);
479
480
  await this.createTableWithRetry(
480
- `CREATE TABLE IF NOT EXISTS "${safe}" (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`,
481
+ `CREATE TABLE IF NOT EXISTS "${safe}" (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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`,
481
482
  safe
482
483
  );
483
484
  log2(`table "${safe}" created`);
@@ -485,6 +486,7 @@ var DeeplakeApi = class {
485
486
  }
486
487
  await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
487
488
  await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
489
+ await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
488
490
  await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
489
491
  }
490
492
  /**
@@ -1071,7 +1073,7 @@ function extractLatestVersion(body) {
1071
1073
  return typeof v === "string" && v.length > 0 ? v : null;
1072
1074
  }
1073
1075
  function getInstalledVersion() {
1074
- return "0.7.21".length > 0 ? "0.7.21" : null;
1076
+ return "0.7.22".length > 0 ? "0.7.22" : null;
1075
1077
  }
1076
1078
  function isNewer(latest, current) {
1077
1079
  const parse = (v) => v.replace(/-.*$/, "").split(".").map(Number);
@@ -1879,7 +1881,7 @@ One brain for every agent on your team.
1879
1881
  };
1880
1882
  const line = JSON.stringify(entry);
1881
1883
  const jsonForSql = line.replace(/'/g, "''");
1882
- const insertSql = `INSERT INTO "${sessionsTable}" (id, path, filename, message, author, size_bytes, project, description, agent, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(sessionPath)}', '${sqlStr(filename)}', '${jsonForSql}'::jsonb, '${sqlStr(cfg.userName)}', ${Buffer.byteLength(line, "utf-8")}, '${sqlStr(projectName)}', '${sqlStr(msg.role)}', 'openclaw', '${ts}', '${ts}')`;
1884
+ const insertSql = `INSERT INTO "${sessionsTable}" (id, path, filename, message, author, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(sessionPath)}', '${sqlStr(filename)}', '${jsonForSql}'::jsonb, '${sqlStr(cfg.userName)}', ${Buffer.byteLength(line, "utf-8")}, '${sqlStr(projectName)}', '${sqlStr(msg.role)}', 'openclaw', '${sqlStr(getInstalledVersion() ?? "")}', '${ts}', '${ts}')`;
1883
1885
  try {
1884
1886
  await dl.query(insertSql);
1885
1887
  } catch (e) {
@@ -52,5 +52,5 @@
52
52
  }
53
53
  }
54
54
  },
55
- "version": "0.7.21"
55
+ "version": "0.7.22"
56
56
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hivemind",
3
- "version": "0.7.21",
3
+ "version": "0.7.22",
4
4
  "type": "module",
5
5
  "description": "Hivemind — cloud-backed persistent shared memory for AI agents, powered by DeepLake",
6
6
  "license": "Apache-2.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeplake/hivemind",
3
- "version": "0.7.21",
3
+ "version": "0.7.22",
4
4
  "description": "Cloud-backed persistent shared memory for AI agents powered by Deeplake",
5
5
  "type": "module",
6
6
  "repository": {
@@ -94,6 +94,20 @@ function loadCreds(): Creds | null {
94
94
  const MEMORY_TABLE = process.env.HIVEMIND_TABLE ?? "memory";
95
95
  const SESSIONS_TABLE = process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions";
96
96
 
97
+ // Read the hivemind version stamped by `hivemind pi install` into
98
+ // ~/.pi/agent/.hivemind/.hivemind_version. The installer writes this
99
+ // at install time (see src/cli/install-pi.ts), so by the time this
100
+ // extension loads the file should be present. Resolved once and reused
101
+ // — the version doesn't change for the lifetime of a pi process.
102
+ const PLUGIN_VERSION: string = (() => {
103
+ try {
104
+ const stamp = readFileSync(join(homedir(), ".pi", "agent", ".hivemind", ".hivemind_version"), "utf-8").trim();
105
+ return stamp || "";
106
+ } catch {
107
+ return "";
108
+ }
109
+ })();
110
+
97
111
  // ---------- SQL escape (matches src/utils/sql.ts) ------------------------------
98
112
 
99
113
  function sqlStr(value: string): string {
@@ -422,6 +436,7 @@ function spawnWikiWorker(
422
436
  sessionId,
423
437
  userName: creds.userName,
424
438
  project,
439
+ pluginVersion: PLUGIN_VERSION,
425
440
  tmpDir,
426
441
  piBin: findPiBin(),
427
442
  piProvider: process.env.HIVEMIND_PI_PROVIDER ?? "google",
@@ -626,9 +641,9 @@ async function writeSessionRow(
626
641
  const emb = await embed(line);
627
642
  logHm(`writeSessionRow: embed=${emb ? `dims=${emb.length}` : "null"}`);
628
643
  const insertSql =
629
- `INSERT INTO "${SESSIONS_TABLE}" (id, path, filename, message, message_embedding, author, size_bytes, project, description, agent, creation_date, last_update_date) ` +
644
+ `INSERT INTO "${SESSIONS_TABLE}" (id, path, filename, message, message_embedding, author, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) ` +
630
645
  `VALUES ('${crypto.randomUUID()}', '${sqlStr(sessionPath)}', '${sqlStr(filename)}', '${jsonForSql}'::jsonb, ${embedSqlLiteral(emb)}, '${sqlStr(creds.userName)}', ` +
631
- `${Buffer.byteLength(line, "utf-8")}, '${sqlStr(projectName)}', '${sqlStr(event)}', '${agent}', '${ts}', '${ts}')`;
646
+ `${Buffer.byteLength(line, "utf-8")}, '${sqlStr(projectName)}', '${sqlStr(event)}', '${agent}', '${sqlStr(PLUGIN_VERSION)}', '${ts}', '${ts}')`;
632
647
  let lastErr: any = null;
633
648
  for (let attempt = 0; attempt <= INSERT_RETRY_BACKOFFS_MS.length; attempt++) {
634
649
  try {