@deeplake/hivemind 0.6.48 → 0.7.9

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 (45) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +244 -20
  4. package/bundle/cli.js +1369 -112
  5. package/codex/bundle/capture.js +546 -96
  6. package/codex/bundle/commands/auth-login.js +290 -81
  7. package/codex/bundle/embeddings/embed-daemon.js +243 -0
  8. package/codex/bundle/pre-tool-use.js +666 -111
  9. package/codex/bundle/session-start-setup.js +231 -64
  10. package/codex/bundle/session-start.js +52 -13
  11. package/codex/bundle/shell/deeplake-shell.js +716 -119
  12. package/codex/bundle/skilify-worker.js +907 -0
  13. package/codex/bundle/stop.js +819 -79
  14. package/codex/bundle/wiki-worker.js +312 -11
  15. package/cursor/bundle/capture.js +1116 -64
  16. package/cursor/bundle/commands/auth-login.js +290 -81
  17. package/cursor/bundle/embeddings/embed-daemon.js +243 -0
  18. package/cursor/bundle/pre-tool-use.js +598 -77
  19. package/cursor/bundle/session-end.js +520 -2
  20. package/cursor/bundle/session-start.js +257 -65
  21. package/cursor/bundle/shell/deeplake-shell.js +716 -119
  22. package/cursor/bundle/skilify-worker.js +907 -0
  23. package/cursor/bundle/wiki-worker.js +571 -0
  24. package/hermes/bundle/capture.js +1119 -65
  25. package/hermes/bundle/commands/auth-login.js +290 -81
  26. package/hermes/bundle/embeddings/embed-daemon.js +243 -0
  27. package/hermes/bundle/pre-tool-use.js +597 -76
  28. package/hermes/bundle/session-end.js +522 -1
  29. package/hermes/bundle/session-start.js +260 -65
  30. package/hermes/bundle/shell/deeplake-shell.js +716 -119
  31. package/hermes/bundle/skilify-worker.js +907 -0
  32. package/hermes/bundle/wiki-worker.js +572 -0
  33. package/mcp/bundle/server.js +290 -75
  34. package/openclaw/dist/chunks/auth-creds-AEKS6D3P.js +14 -0
  35. package/openclaw/dist/chunks/chunk-SRCBBT4H.js +37 -0
  36. package/openclaw/dist/chunks/config-ZLH6JFJS.js +34 -0
  37. package/openclaw/dist/chunks/index-marker-store-PGT5CW6T.js +33 -0
  38. package/openclaw/dist/chunks/setup-config-C35UK4LP.js +114 -0
  39. package/openclaw/dist/index.js +929 -710
  40. package/openclaw/dist/skilify-worker.js +907 -0
  41. package/openclaw/openclaw.plugin.json +1 -1
  42. package/openclaw/package.json +1 -1
  43. package/openclaw/skills/SKILL.md +19 -0
  44. package/package.json +7 -1
  45. package/pi/extension-source/hivemind.ts +603 -22
@@ -1,33 +1,99 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // dist/src/index-marker-store.js
12
+ var index_marker_store_exports = {};
13
+ __export(index_marker_store_exports, {
14
+ buildIndexMarkerPath: () => buildIndexMarkerPath,
15
+ getIndexMarkerDir: () => getIndexMarkerDir,
16
+ hasFreshIndexMarker: () => hasFreshIndexMarker,
17
+ writeIndexMarker: () => writeIndexMarker
18
+ });
19
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
20
+ import { join as join4 } from "node:path";
21
+ import { tmpdir } from "node:os";
22
+ function getIndexMarkerDir() {
23
+ return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join4(tmpdir(), "hivemind-deeplake-indexes");
24
+ }
25
+ function buildIndexMarkerPath(workspaceId, orgId, table, suffix) {
26
+ const markerKey = [workspaceId, orgId, table, suffix].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
27
+ return join4(getIndexMarkerDir(), `${markerKey}.json`);
28
+ }
29
+ function hasFreshIndexMarker(markerPath) {
30
+ if (!existsSync2(markerPath))
31
+ return false;
32
+ try {
33
+ const raw = JSON.parse(readFileSync3(markerPath, "utf-8"));
34
+ const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
35
+ if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
36
+ return false;
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+ function writeIndexMarker(markerPath) {
43
+ mkdirSync2(getIndexMarkerDir(), { recursive: true });
44
+ writeFileSync2(markerPath, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
45
+ }
46
+ var INDEX_MARKER_TTL_MS;
47
+ var init_index_marker_store = __esm({
48
+ "dist/src/index-marker-store.js"() {
49
+ "use strict";
50
+ INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
51
+ }
52
+ });
53
+
1
54
  // dist/src/hooks/hermes/session-start.js
2
55
  import { fileURLToPath } from "node:url";
3
- import { dirname as dirname2, join as join6 } from "node:path";
56
+ import { dirname as dirname2 } from "node:path";
4
57
 
5
58
  // dist/src/commands/auth.js
6
- import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
59
+ import { execSync } from "node:child_process";
60
+
61
+ // dist/src/utils/client-header.js
62
+ var DEEPLAKE_CLIENT_HEADER = "X-Deeplake-Client";
63
+ function deeplakeClientValue() {
64
+ return "hivemind";
65
+ }
66
+ function deeplakeClientHeader() {
67
+ return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
68
+ }
69
+
70
+ // dist/src/commands/auth-creds.js
71
+ import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
7
72
  import { join } from "node:path";
8
73
  import { homedir } from "node:os";
9
- import { execSync } from "node:child_process";
10
- var CONFIG_DIR = join(homedir(), ".deeplake");
11
- var CREDS_PATH = join(CONFIG_DIR, "credentials.json");
74
+ function configDir() {
75
+ return join(homedir(), ".deeplake");
76
+ }
77
+ function credsPath() {
78
+ return join(configDir(), "credentials.json");
79
+ }
12
80
  function loadCredentials() {
13
- if (!existsSync(CREDS_PATH))
14
- return null;
15
81
  try {
16
- return JSON.parse(readFileSync(CREDS_PATH, "utf-8"));
82
+ return JSON.parse(readFileSync(credsPath(), "utf-8"));
17
83
  } catch {
18
84
  return null;
19
85
  }
20
86
  }
21
87
 
22
88
  // dist/src/config.js
23
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
89
+ import { readFileSync as readFileSync2, existsSync } from "node:fs";
24
90
  import { join as join2 } from "node:path";
25
91
  import { homedir as homedir2, userInfo } from "node:os";
26
92
  function loadConfig() {
27
93
  const home = homedir2();
28
94
  const credPath = join2(home, ".deeplake", "credentials.json");
29
95
  let creds = null;
30
- if (existsSync2(credPath)) {
96
+ if (existsSync(credPath)) {
31
97
  try {
32
98
  creds = JSON.parse(readFileSync2(credPath, "utf-8"));
33
99
  } catch {
@@ -47,15 +113,13 @@ function loadConfig() {
47
113
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
48
114
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
49
115
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
116
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
50
117
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join2(home, ".deeplake", "memory")
51
118
  };
52
119
  }
53
120
 
54
121
  // dist/src/deeplake-api.js
55
122
  import { randomUUID } from "node:crypto";
56
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
57
- import { join as join4 } from "node:path";
58
- import { tmpdir } from "node:os";
59
123
 
60
124
  // dist/src/utils/debug.js
61
125
  import { appendFileSync } from "node:fs";
@@ -74,8 +138,24 @@ function log(tag, msg) {
74
138
  function sqlStr(value) {
75
139
  return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
76
140
  }
141
+ function sqlIdent(name) {
142
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
143
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
144
+ }
145
+ return name;
146
+ }
147
+
148
+ // dist/src/embeddings/columns.js
149
+ var SUMMARY_EMBEDDING_COL = "summary_embedding";
150
+ var MESSAGE_EMBEDDING_COL = "message_embedding";
77
151
 
78
152
  // dist/src/deeplake-api.js
153
+ var indexMarkerStorePromise = null;
154
+ function getIndexMarkerStore() {
155
+ if (!indexMarkerStorePromise)
156
+ indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
157
+ return indexMarkerStorePromise;
158
+ }
79
159
  var log2 = (msg) => log("sdk", msg);
80
160
  function summarizeSql(sql, maxLen = 220) {
81
161
  const compact = sql.replace(/\s+/g, " ").trim();
@@ -95,7 +175,6 @@ var MAX_RETRIES = 3;
95
175
  var BASE_DELAY_MS = 500;
96
176
  var MAX_CONCURRENCY = 5;
97
177
  var QUERY_TIMEOUT_MS = Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
98
- var INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
99
178
  function sleep(ms) {
100
179
  return new Promise((resolve) => setTimeout(resolve, ms));
101
180
  }
@@ -115,9 +194,6 @@ function isTransientHtml403(text) {
115
194
  const body = text.toLowerCase();
116
195
  return body.includes("<html") || body.includes("403 forbidden") || body.includes("cloudflare") || body.includes("nginx");
117
196
  }
118
- function getIndexMarkerDir() {
119
- return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join4(tmpdir(), "hivemind-deeplake-indexes");
120
- }
121
197
  var Semaphore = class {
122
198
  max;
123
199
  waiting = [];
@@ -186,7 +262,8 @@ var DeeplakeApi = class {
186
262
  headers: {
187
263
  Authorization: `Bearer ${this.token}`,
188
264
  "Content-Type": "application/json",
189
- "X-Activeloop-Org-Id": this.orgId
265
+ "X-Activeloop-Org-Id": this.orgId,
266
+ ...deeplakeClientHeader()
190
267
  },
191
268
  signal,
192
269
  body: JSON.stringify({ query: sql })
@@ -213,7 +290,8 @@ var DeeplakeApi = class {
213
290
  }
214
291
  const text = await resp.text().catch(() => "");
215
292
  const retryable403 = isSessionInsertQuery(sql) && (resp.status === 401 || resp.status === 403 && (text.length === 0 || isTransientHtml403(text)));
216
- if (attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
293
+ const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
294
+ if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
217
295
  const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
218
296
  log2(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
219
297
  await sleep(delay);
@@ -247,7 +325,7 @@ var DeeplakeApi = class {
247
325
  const lud = row.lastUpdateDate ?? ts;
248
326
  const exists = await this.query(`SELECT path FROM "${this.tableName}" WHERE path = '${sqlStr(row.path)}' LIMIT 1`);
249
327
  if (exists.length > 0) {
250
- let setClauses = `summary = E'${sqlStr(row.contentText)}', mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
328
+ let setClauses = `summary = E'${sqlStr(row.contentText)}', ${SUMMARY_EMBEDDING_COL} = NULL, mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
251
329
  if (row.project !== void 0)
252
330
  setClauses += `, project = '${sqlStr(row.project)}'`;
253
331
  if (row.description !== void 0)
@@ -255,8 +333,8 @@ var DeeplakeApi = class {
255
333
  await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(row.path)}'`);
256
334
  } else {
257
335
  const id = randomUUID();
258
- let cols = "id, path, filename, summary, mime_type, size_bytes, creation_date, last_update_date";
259
- let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
336
+ let cols = `id, path, filename, summary, ${SUMMARY_EMBEDDING_COL}, mime_type, size_bytes, creation_date, last_update_date`;
337
+ let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', NULL, '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
260
338
  if (row.project !== void 0) {
261
339
  cols += ", project";
262
340
  vals += `, '${sqlStr(row.project)}'`;
@@ -281,48 +359,83 @@ var DeeplakeApi = class {
281
359
  buildLookupIndexName(table, suffix) {
282
360
  return `idx_${table}_${suffix}`.replace(/[^a-zA-Z0-9_]/g, "_");
283
361
  }
284
- getLookupIndexMarkerPath(table, suffix) {
285
- const markerKey = [
286
- this.workspaceId,
287
- this.orgId,
288
- table,
289
- suffix
290
- ].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
291
- return join4(getIndexMarkerDir(), `${markerKey}.json`);
292
- }
293
- hasFreshLookupIndexMarker(table, suffix) {
294
- const markerPath = this.getLookupIndexMarkerPath(table, suffix);
295
- if (!existsSync3(markerPath))
296
- return false;
297
- try {
298
- const raw = JSON.parse(readFileSync3(markerPath, "utf-8"));
299
- const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
300
- if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
301
- return false;
302
- return true;
303
- } catch {
304
- return false;
305
- }
306
- }
307
- markLookupIndexReady(table, suffix) {
308
- mkdirSync2(getIndexMarkerDir(), { recursive: true });
309
- writeFileSync2(this.getLookupIndexMarkerPath(table, suffix), JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
310
- }
311
362
  async ensureLookupIndex(table, suffix, columnsSql) {
312
- if (this.hasFreshLookupIndexMarker(table, suffix))
363
+ const markers = await getIndexMarkerStore();
364
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, suffix);
365
+ if (markers.hasFreshIndexMarker(markerPath))
313
366
  return;
314
367
  const indexName = this.buildLookupIndexName(table, suffix);
315
368
  try {
316
369
  await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ${columnsSql}`);
317
- this.markLookupIndexReady(table, suffix);
370
+ markers.writeIndexMarker(markerPath);
318
371
  } catch (e) {
319
372
  if (isDuplicateIndexError(e)) {
320
- this.markLookupIndexReady(table, suffix);
373
+ markers.writeIndexMarker(markerPath);
321
374
  return;
322
375
  }
323
376
  log2(`index "${indexName}" skipped: ${e.message}`);
324
377
  }
325
378
  }
379
+ /**
380
+ * Ensure a vector column exists on the given table.
381
+ *
382
+ * The previous implementation always issued `ALTER TABLE ADD COLUMN IF NOT
383
+ * EXISTS …` on every SessionStart. On a long-running workspace that's
384
+ * already migrated, every call returns 500 "Column already exists" — noisy
385
+ * in the log and a wasted round-trip. Worse, the very first call after the
386
+ * column is genuinely added triggers Deeplake's post-ALTER `vector::at`
387
+ * window (~30s) during which subsequent INSERTs fail; minimising the
388
+ * number of ALTER calls minimises exposure to that window.
389
+ *
390
+ * New flow:
391
+ * 1. Check the local marker file (mirrors ensureLookupIndex). If fresh,
392
+ * return — zero network calls.
393
+ * 2. SELECT 1 FROM information_schema.columns WHERE table_name = T AND
394
+ * column_name = C. Read-only, idempotent, can't tickle the post-ALTER
395
+ * bug. If the column is present → mark + return.
396
+ * 3. Only if step 2 says the column is missing, fall back to ALTER ADD
397
+ * COLUMN IF NOT EXISTS. Mark on success, also mark if Deeplake reports
398
+ * "already exists" (race: another client added it between our SELECT
399
+ * and ALTER).
400
+ *
401
+ * Marker uses the same dir / TTL as ensureLookupIndex so both schema
402
+ * caches share an opt-out (HIVEMIND_INDEX_MARKER_DIR) and a TTL knob.
403
+ */
404
+ async ensureEmbeddingColumn(table, column) {
405
+ await this.ensureColumn(table, column, "FLOAT4[]");
406
+ }
407
+ /**
408
+ * Generic marker-gated column migration. Same SELECT-then-ALTER flow as
409
+ * ensureEmbeddingColumn, parameterized by SQL type so it can patch up any
410
+ * column that was added to the schema after the table was originally
411
+ * created. Used today for `summary_embedding`, `message_embedding`, and
412
+ * the `agent` column (added 2026-04-11) — the latter has no fallback if
413
+ * a user upgraded over a pre-2026-04-11 table, so every INSERT fails
414
+ * with `column "agent" does not exist`.
415
+ */
416
+ async ensureColumn(table, column, sqlType) {
417
+ const markers = await getIndexMarkerStore();
418
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, `col_${column}`);
419
+ if (markers.hasFreshIndexMarker(markerPath))
420
+ return;
421
+ 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`;
422
+ const rows = await this.query(colCheck);
423
+ if (rows.length > 0) {
424
+ markers.writeIndexMarker(markerPath);
425
+ return;
426
+ }
427
+ try {
428
+ await this.query(`ALTER TABLE "${table}" ADD COLUMN ${column} ${sqlType}`);
429
+ } catch (e) {
430
+ const msg = e instanceof Error ? e.message : String(e);
431
+ if (!/already exists/i.test(msg))
432
+ throw e;
433
+ const recheck = await this.query(colCheck);
434
+ if (recheck.length === 0)
435
+ throw e;
436
+ }
437
+ markers.writeIndexMarker(markerPath);
438
+ }
326
439
  /** List all tables in the workspace (with retry). */
327
440
  async listTables(forceRefresh = false) {
328
441
  if (!forceRefresh && this._tablesCache)
@@ -338,7 +451,8 @@ var DeeplakeApi = class {
338
451
  const resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables`, {
339
452
  headers: {
340
453
  Authorization: `Bearer ${this.token}`,
341
- "X-Activeloop-Org-Id": this.orgId
454
+ "X-Activeloop-Org-Id": this.orgId,
455
+ ...deeplakeClientHeader()
342
456
  }
343
457
  });
344
458
  if (resp.ok) {
@@ -363,29 +477,84 @@ var DeeplakeApi = class {
363
477
  }
364
478
  return { tables: [], cacheable: false };
365
479
  }
480
+ /**
481
+ * Run a `CREATE TABLE` with an extra outer retry budget. The base
482
+ * `query()` already retries 3 times on fetch errors (~3.5s total), but a
483
+ * failed CREATE is permanent corruption — every subsequent SELECT against
484
+ * the missing table fails. Wrapping in an outer loop with longer backoff
485
+ * (2s, 5s, then 10s) gives us ~17s of reach across transient network
486
+ * blips before giving up. Failures still propagate; getApi() resets its
487
+ * cache on init failure (openclaw plugin) so the next call retries the
488
+ * whole init flow.
489
+ */
490
+ async createTableWithRetry(sql, label) {
491
+ const OUTER_BACKOFFS_MS = [2e3, 5e3, 1e4];
492
+ let lastErr = null;
493
+ for (let attempt = 0; attempt <= OUTER_BACKOFFS_MS.length; attempt++) {
494
+ try {
495
+ await this.query(sql);
496
+ return;
497
+ } catch (err) {
498
+ lastErr = err;
499
+ const msg = err instanceof Error ? err.message : String(err);
500
+ log2(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
501
+ if (attempt < OUTER_BACKOFFS_MS.length) {
502
+ await sleep(OUTER_BACKOFFS_MS[attempt]);
503
+ }
504
+ }
505
+ }
506
+ throw lastErr;
507
+ }
366
508
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
367
509
  async ensureTable(name) {
368
- const tbl = name ?? this.tableName;
510
+ const tbl = sqlIdent(name ?? this.tableName);
369
511
  const tables = await this.listTables();
370
512
  if (!tables.includes(tbl)) {
371
513
  log2(`table "${tbl}" not found, creating`);
372
- 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`);
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);
373
515
  log2(`table "${tbl}" created`);
374
516
  if (!tables.includes(tbl))
375
517
  this._tablesCache = [...tables, tbl];
376
518
  }
519
+ await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
520
+ await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
377
521
  }
378
522
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
379
523
  async ensureSessionsTable(name) {
524
+ const safe = sqlIdent(name);
525
+ const tables = await this.listTables();
526
+ if (!tables.includes(safe)) {
527
+ 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
+ log2(`table "${safe}" created`);
530
+ if (!tables.includes(safe))
531
+ this._tablesCache = [...tables, safe];
532
+ }
533
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
534
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
535
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
536
+ }
537
+ /**
538
+ * Create the skills table.
539
+ *
540
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
541
+ * MERGE rather than UPDATE-ing in place, so the full version history is
542
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
543
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
544
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
545
+ * worker.
546
+ */
547
+ async ensureSkillsTable(name) {
548
+ const safe = sqlIdent(name);
380
549
  const tables = await this.listTables();
381
- if (!tables.includes(name)) {
382
- log2(`table "${name}" not found, creating`);
383
- 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`);
384
- log2(`table "${name}" created`);
385
- if (!tables.includes(name))
386
- this._tablesCache = [...tables, name];
550
+ if (!tables.includes(safe)) {
551
+ log2(`table "${safe}" not found, creating`);
552
+ await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
553
+ log2(`table "${safe}" created`);
554
+ if (!tables.includes(safe))
555
+ this._tablesCache = [...tables, safe];
387
556
  }
388
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
557
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
389
558
  }
390
559
  };
391
560
 
@@ -451,14 +620,38 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
451
620
  // dist/src/hooks/hermes/session-start.js
452
621
  var log3 = (msg) => log("hermes-session-start", msg);
453
622
  var __bundleDir = dirname2(fileURLToPath(import.meta.url));
454
- var AUTH_CMD = join6(__bundleDir, "commands", "auth-login.js");
455
623
  var context = `DEEPLAKE MEMORY: Persistent memory at ~/.deeplake/memory/ shared across sessions, users, and agents.
456
624
 
457
625
  Structure: index.md (start here) \u2192 summaries/*.md \u2192 sessions/*.jsonl (last resort). Do NOT jump straight to JSONL.
458
626
  Search: use \`grep\` (NOT \`rg\`/ripgrep). Example: grep -ri "keyword" ~/.deeplake/memory/
459
627
  You also have hivemind MCP tools registered: hivemind_search, hivemind_read, hivemind_index. Prefer these \u2014 one tool call returns ranked hits across all summaries and sessions in a single SQL query.
460
628
  IMPORTANT: Only use these bash builtins to interact with ~/.deeplake/memory/: cat, ls, grep, echo, jq, head, tail, sed, awk, wc, sort, find. Do NOT use rg/ripgrep, python, python3, node, curl, or other interpreters.
461
- Do NOT spawn subagents to read deeplake memory.`;
629
+ Do NOT spawn subagents to read deeplake memory.
630
+
631
+ Organization management \u2014 each argument is SEPARATE (do NOT quote subcommands together):
632
+ - hivemind login \u2014 SSO login
633
+ - hivemind whoami \u2014 show current user/org
634
+ - hivemind org list \u2014 list organizations
635
+ - hivemind org switch <name-or-id> \u2014 switch organization
636
+ - hivemind workspaces \u2014 list workspaces
637
+ - hivemind workspace <id> \u2014 switch workspace
638
+ - hivemind invite <email> <ADMIN|WRITE|READ> \u2014 invite member (ALWAYS ask user which role before inviting)
639
+ - hivemind members \u2014 list members
640
+ - hivemind remove <user-id> \u2014 remove member
641
+
642
+ SKILLS (skilify) \u2014 mine + share reusable skills across the org:
643
+ - hivemind skilify \u2014 show scope/team/install + per-project state
644
+ - hivemind skilify pull \u2014 sync project skills from the org table
645
+ - hivemind skilify pull --user <email> \u2014 only that author's skills
646
+ - hivemind skilify pull --users a,b,c \u2014 multiple authors (CSV)
647
+ - hivemind skilify pull --all-users \u2014 explicit "no author filter"
648
+ - hivemind skilify pull --to project|global \u2014 install location
649
+ - hivemind skilify pull --dry-run \u2014 preview only
650
+ - hivemind skilify pull --force \u2014 overwrite local (creates .bak)
651
+ - hivemind skilify pull <skill-name> \u2014 pull only that skill (combines with --user)
652
+ - hivemind skilify scope <me|team|org> \u2014 sharing scope for new skills
653
+ - hivemind skilify install <project|global> \u2014 default install location
654
+ - hivemind skilify team add|remove|list <name> \u2014 manage team list`;
462
655
  async function createPlaceholder(api, table, sessionId, cwd, userName, orgName, workspaceId) {
463
656
  const summaryPath = `/summaries/${userName}/${sessionId}.md`;
464
657
  const existing = await api.query(`SELECT path FROM "${table}" WHERE path = '${sqlStr(summaryPath)}' LIMIT 1`);
@@ -491,6 +684,8 @@ async function main() {
491
684
  const config = loadConfig();
492
685
  if (config) {
493
686
  const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
687
+ await api.ensureTable();
688
+ await api.ensureSessionsTable(config.sessionsTableName);
494
689
  await createPlaceholder(api, config.tableName, sessionId, cwd, config.userName, config.orgName, config.workspaceId);
495
690
  log3("placeholder created");
496
691
  }
@@ -505,7 +700,7 @@ async function main() {
505
700
  Hivemind v${current}`;
506
701
  const additional = creds?.token ? `${context}
507
702
  Logged in to Deeplake as org: ${creds.orgName ?? creds.orgId} (workspace: ${creds.workspaceId ?? "default"})${versionNotice}` : `${context}
508
- Not logged in to Deeplake. Run: node "${AUTH_CMD}" login${versionNotice}`;
703
+ Not logged in to Deeplake. Run: hivemind login${versionNotice}`;
509
704
  console.log(JSON.stringify({ context: additional }));
510
705
  }
511
706
  main().catch((e) => {