@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,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 existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
21
+ import { join as join4 } from "node:path";
22
+ import { tmpdir } from "node:os";
23
+ function getIndexMarkerDir() {
24
+ return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join4(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 join4(getIndexMarkerDir(), `${markerKey}.json`);
29
+ }
30
+ function hasFreshIndexMarker(markerPath) {
31
+ if (!existsSync2(markerPath))
32
+ return false;
33
+ try {
34
+ const raw = JSON.parse(readFileSync3(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
+ mkdirSync2(getIndexMarkerDir(), { recursive: true });
45
+ writeFileSync2(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/hooks/codex/session-start-setup.js
4
56
  import { fileURLToPath } from "node:url";
@@ -7,36 +59,48 @@ import { execSync as execSync2 } from "node:child_process";
7
59
  import { homedir as homedir4 } from "node:os";
8
60
 
9
61
  // dist/src/commands/auth.js
10
- import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
62
+ import { execSync } from "node:child_process";
63
+
64
+ // dist/src/utils/client-header.js
65
+ var DEEPLAKE_CLIENT_HEADER = "X-Deeplake-Client";
66
+ function deeplakeClientValue() {
67
+ return "hivemind";
68
+ }
69
+ function deeplakeClientHeader() {
70
+ return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
71
+ }
72
+
73
+ // dist/src/commands/auth-creds.js
74
+ import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
11
75
  import { join } from "node:path";
12
76
  import { homedir } from "node:os";
13
- import { execSync } from "node:child_process";
14
- var CONFIG_DIR = join(homedir(), ".deeplake");
15
- var CREDS_PATH = join(CONFIG_DIR, "credentials.json");
77
+ function configDir() {
78
+ return join(homedir(), ".deeplake");
79
+ }
80
+ function credsPath() {
81
+ return join(configDir(), "credentials.json");
82
+ }
16
83
  function loadCredentials() {
17
- if (!existsSync(CREDS_PATH))
18
- return null;
19
84
  try {
20
- return JSON.parse(readFileSync(CREDS_PATH, "utf-8"));
85
+ return JSON.parse(readFileSync(credsPath(), "utf-8"));
21
86
  } catch {
22
87
  return null;
23
88
  }
24
89
  }
25
90
  function saveCredentials(creds) {
26
- if (!existsSync(CONFIG_DIR))
27
- mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
28
- writeFileSync(CREDS_PATH, JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
91
+ mkdirSync(configDir(), { recursive: true, mode: 448 });
92
+ writeFileSync(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
29
93
  }
30
94
 
31
95
  // dist/src/config.js
32
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
96
+ import { readFileSync as readFileSync2, existsSync } from "node:fs";
33
97
  import { join as join2 } from "node:path";
34
98
  import { homedir as homedir2, userInfo } from "node:os";
35
99
  function loadConfig() {
36
100
  const home = homedir2();
37
101
  const credPath = join2(home, ".deeplake", "credentials.json");
38
102
  let creds = null;
39
- if (existsSync2(credPath)) {
103
+ if (existsSync(credPath)) {
40
104
  try {
41
105
  creds = JSON.parse(readFileSync2(credPath, "utf-8"));
42
106
  } catch {
@@ -56,15 +120,13 @@ function loadConfig() {
56
120
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
57
121
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
58
122
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
123
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
59
124
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join2(home, ".deeplake", "memory")
60
125
  };
61
126
  }
62
127
 
63
128
  // dist/src/deeplake-api.js
64
129
  import { randomUUID } from "node:crypto";
65
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
66
- import { join as join4 } from "node:path";
67
- import { tmpdir } from "node:os";
68
130
 
69
131
  // dist/src/utils/debug.js
70
132
  import { appendFileSync } from "node:fs";
@@ -86,8 +148,24 @@ function log(tag, msg) {
86
148
  function sqlStr(value) {
87
149
  return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
88
150
  }
151
+ function sqlIdent(name) {
152
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
153
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
154
+ }
155
+ return name;
156
+ }
157
+
158
+ // dist/src/embeddings/columns.js
159
+ var SUMMARY_EMBEDDING_COL = "summary_embedding";
160
+ var MESSAGE_EMBEDDING_COL = "message_embedding";
89
161
 
90
162
  // dist/src/deeplake-api.js
163
+ var indexMarkerStorePromise = null;
164
+ function getIndexMarkerStore() {
165
+ if (!indexMarkerStorePromise)
166
+ indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
167
+ return indexMarkerStorePromise;
168
+ }
91
169
  var log2 = (msg) => log("sdk", msg);
92
170
  function summarizeSql(sql, maxLen = 220) {
93
171
  const compact = sql.replace(/\s+/g, " ").trim();
@@ -107,7 +185,6 @@ var MAX_RETRIES = 3;
107
185
  var BASE_DELAY_MS = 500;
108
186
  var MAX_CONCURRENCY = 5;
109
187
  var QUERY_TIMEOUT_MS = Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
110
- var INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
111
188
  function sleep(ms) {
112
189
  return new Promise((resolve) => setTimeout(resolve, ms));
113
190
  }
@@ -127,9 +204,6 @@ function isTransientHtml403(text) {
127
204
  const body = text.toLowerCase();
128
205
  return body.includes("<html") || body.includes("403 forbidden") || body.includes("cloudflare") || body.includes("nginx");
129
206
  }
130
- function getIndexMarkerDir() {
131
- return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join4(tmpdir(), "hivemind-deeplake-indexes");
132
- }
133
207
  var Semaphore = class {
134
208
  max;
135
209
  waiting = [];
@@ -198,7 +272,8 @@ var DeeplakeApi = class {
198
272
  headers: {
199
273
  Authorization: `Bearer ${this.token}`,
200
274
  "Content-Type": "application/json",
201
- "X-Activeloop-Org-Id": this.orgId
275
+ "X-Activeloop-Org-Id": this.orgId,
276
+ ...deeplakeClientHeader()
202
277
  },
203
278
  signal,
204
279
  body: JSON.stringify({ query: sql })
@@ -225,7 +300,8 @@ var DeeplakeApi = class {
225
300
  }
226
301
  const text = await resp.text().catch(() => "");
227
302
  const retryable403 = isSessionInsertQuery(sql) && (resp.status === 401 || resp.status === 403 && (text.length === 0 || isTransientHtml403(text)));
228
- if (attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
303
+ const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
304
+ if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
229
305
  const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
230
306
  log2(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
231
307
  await sleep(delay);
@@ -259,7 +335,7 @@ var DeeplakeApi = class {
259
335
  const lud = row.lastUpdateDate ?? ts;
260
336
  const exists = await this.query(`SELECT path FROM "${this.tableName}" WHERE path = '${sqlStr(row.path)}' LIMIT 1`);
261
337
  if (exists.length > 0) {
262
- let setClauses = `summary = E'${sqlStr(row.contentText)}', mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
338
+ let setClauses = `summary = E'${sqlStr(row.contentText)}', ${SUMMARY_EMBEDDING_COL} = NULL, mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
263
339
  if (row.project !== void 0)
264
340
  setClauses += `, project = '${sqlStr(row.project)}'`;
265
341
  if (row.description !== void 0)
@@ -267,8 +343,8 @@ var DeeplakeApi = class {
267
343
  await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(row.path)}'`);
268
344
  } else {
269
345
  const id = randomUUID();
270
- let cols = "id, path, filename, summary, mime_type, size_bytes, creation_date, last_update_date";
271
- let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
346
+ let cols = `id, path, filename, summary, ${SUMMARY_EMBEDDING_COL}, mime_type, size_bytes, creation_date, last_update_date`;
347
+ let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', NULL, '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
272
348
  if (row.project !== void 0) {
273
349
  cols += ", project";
274
350
  vals += `, '${sqlStr(row.project)}'`;
@@ -293,48 +369,83 @@ var DeeplakeApi = class {
293
369
  buildLookupIndexName(table, suffix) {
294
370
  return `idx_${table}_${suffix}`.replace(/[^a-zA-Z0-9_]/g, "_");
295
371
  }
296
- getLookupIndexMarkerPath(table, suffix) {
297
- const markerKey = [
298
- this.workspaceId,
299
- this.orgId,
300
- table,
301
- suffix
302
- ].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
303
- return join4(getIndexMarkerDir(), `${markerKey}.json`);
304
- }
305
- hasFreshLookupIndexMarker(table, suffix) {
306
- const markerPath = this.getLookupIndexMarkerPath(table, suffix);
307
- if (!existsSync3(markerPath))
308
- return false;
309
- try {
310
- const raw = JSON.parse(readFileSync3(markerPath, "utf-8"));
311
- const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
312
- if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
313
- return false;
314
- return true;
315
- } catch {
316
- return false;
317
- }
318
- }
319
- markLookupIndexReady(table, suffix) {
320
- mkdirSync2(getIndexMarkerDir(), { recursive: true });
321
- writeFileSync2(this.getLookupIndexMarkerPath(table, suffix), JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
322
- }
323
372
  async ensureLookupIndex(table, suffix, columnsSql) {
324
- if (this.hasFreshLookupIndexMarker(table, suffix))
373
+ const markers = await getIndexMarkerStore();
374
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, suffix);
375
+ if (markers.hasFreshIndexMarker(markerPath))
325
376
  return;
326
377
  const indexName = this.buildLookupIndexName(table, suffix);
327
378
  try {
328
379
  await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ${columnsSql}`);
329
- this.markLookupIndexReady(table, suffix);
380
+ markers.writeIndexMarker(markerPath);
330
381
  } catch (e) {
331
382
  if (isDuplicateIndexError(e)) {
332
- this.markLookupIndexReady(table, suffix);
383
+ markers.writeIndexMarker(markerPath);
333
384
  return;
334
385
  }
335
386
  log2(`index "${indexName}" skipped: ${e.message}`);
336
387
  }
337
388
  }
389
+ /**
390
+ * Ensure a vector column exists on the given table.
391
+ *
392
+ * The previous implementation always issued `ALTER TABLE ADD COLUMN IF NOT
393
+ * EXISTS …` on every SessionStart. On a long-running workspace that's
394
+ * already migrated, every call returns 500 "Column already exists" — noisy
395
+ * in the log and a wasted round-trip. Worse, the very first call after the
396
+ * column is genuinely added triggers Deeplake's post-ALTER `vector::at`
397
+ * window (~30s) during which subsequent INSERTs fail; minimising the
398
+ * number of ALTER calls minimises exposure to that window.
399
+ *
400
+ * New flow:
401
+ * 1. Check the local marker file (mirrors ensureLookupIndex). If fresh,
402
+ * return — zero network calls.
403
+ * 2. SELECT 1 FROM information_schema.columns WHERE table_name = T AND
404
+ * column_name = C. Read-only, idempotent, can't tickle the post-ALTER
405
+ * bug. If the column is present → mark + return.
406
+ * 3. Only if step 2 says the column is missing, fall back to ALTER ADD
407
+ * COLUMN IF NOT EXISTS. Mark on success, also mark if Deeplake reports
408
+ * "already exists" (race: another client added it between our SELECT
409
+ * and ALTER).
410
+ *
411
+ * Marker uses the same dir / TTL as ensureLookupIndex so both schema
412
+ * caches share an opt-out (HIVEMIND_INDEX_MARKER_DIR) and a TTL knob.
413
+ */
414
+ async ensureEmbeddingColumn(table, column) {
415
+ await this.ensureColumn(table, column, "FLOAT4[]");
416
+ }
417
+ /**
418
+ * Generic marker-gated column migration. Same SELECT-then-ALTER flow as
419
+ * ensureEmbeddingColumn, parameterized by SQL type so it can patch up any
420
+ * column that was added to the schema after the table was originally
421
+ * created. Used today for `summary_embedding`, `message_embedding`, and
422
+ * the `agent` column (added 2026-04-11) — the latter has no fallback if
423
+ * a user upgraded over a pre-2026-04-11 table, so every INSERT fails
424
+ * with `column "agent" does not exist`.
425
+ */
426
+ async ensureColumn(table, column, sqlType) {
427
+ const markers = await getIndexMarkerStore();
428
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, `col_${column}`);
429
+ if (markers.hasFreshIndexMarker(markerPath))
430
+ return;
431
+ 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`;
432
+ const rows = await this.query(colCheck);
433
+ if (rows.length > 0) {
434
+ markers.writeIndexMarker(markerPath);
435
+ return;
436
+ }
437
+ try {
438
+ await this.query(`ALTER TABLE "${table}" ADD COLUMN ${column} ${sqlType}`);
439
+ } catch (e) {
440
+ const msg = e instanceof Error ? e.message : String(e);
441
+ if (!/already exists/i.test(msg))
442
+ throw e;
443
+ const recheck = await this.query(colCheck);
444
+ if (recheck.length === 0)
445
+ throw e;
446
+ }
447
+ markers.writeIndexMarker(markerPath);
448
+ }
338
449
  /** List all tables in the workspace (with retry). */
339
450
  async listTables(forceRefresh = false) {
340
451
  if (!forceRefresh && this._tablesCache)
@@ -350,7 +461,8 @@ var DeeplakeApi = class {
350
461
  const resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables`, {
351
462
  headers: {
352
463
  Authorization: `Bearer ${this.token}`,
353
- "X-Activeloop-Org-Id": this.orgId
464
+ "X-Activeloop-Org-Id": this.orgId,
465
+ ...deeplakeClientHeader()
354
466
  }
355
467
  });
356
468
  if (resp.ok) {
@@ -375,29 +487,84 @@ var DeeplakeApi = class {
375
487
  }
376
488
  return { tables: [], cacheable: false };
377
489
  }
490
+ /**
491
+ * Run a `CREATE TABLE` with an extra outer retry budget. The base
492
+ * `query()` already retries 3 times on fetch errors (~3.5s total), but a
493
+ * failed CREATE is permanent corruption — every subsequent SELECT against
494
+ * the missing table fails. Wrapping in an outer loop with longer backoff
495
+ * (2s, 5s, then 10s) gives us ~17s of reach across transient network
496
+ * blips before giving up. Failures still propagate; getApi() resets its
497
+ * cache on init failure (openclaw plugin) so the next call retries the
498
+ * whole init flow.
499
+ */
500
+ async createTableWithRetry(sql, label) {
501
+ const OUTER_BACKOFFS_MS = [2e3, 5e3, 1e4];
502
+ let lastErr = null;
503
+ for (let attempt = 0; attempt <= OUTER_BACKOFFS_MS.length; attempt++) {
504
+ try {
505
+ await this.query(sql);
506
+ return;
507
+ } catch (err) {
508
+ lastErr = err;
509
+ const msg = err instanceof Error ? err.message : String(err);
510
+ log2(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
511
+ if (attempt < OUTER_BACKOFFS_MS.length) {
512
+ await sleep(OUTER_BACKOFFS_MS[attempt]);
513
+ }
514
+ }
515
+ }
516
+ throw lastErr;
517
+ }
378
518
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
379
519
  async ensureTable(name) {
380
- const tbl = name ?? this.tableName;
520
+ const tbl = sqlIdent(name ?? this.tableName);
381
521
  const tables = await this.listTables();
382
522
  if (!tables.includes(tbl)) {
383
523
  log2(`table "${tbl}" not found, creating`);
384
- 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`);
524
+ 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);
385
525
  log2(`table "${tbl}" created`);
386
526
  if (!tables.includes(tbl))
387
527
  this._tablesCache = [...tables, tbl];
388
528
  }
529
+ await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
530
+ await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
389
531
  }
390
532
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
391
533
  async ensureSessionsTable(name) {
534
+ const safe = sqlIdent(name);
535
+ const tables = await this.listTables();
536
+ if (!tables.includes(safe)) {
537
+ log2(`table "${safe}" not found, creating`);
538
+ 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);
539
+ log2(`table "${safe}" created`);
540
+ if (!tables.includes(safe))
541
+ this._tablesCache = [...tables, safe];
542
+ }
543
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
544
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
545
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
546
+ }
547
+ /**
548
+ * Create the skills table.
549
+ *
550
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
551
+ * MERGE rather than UPDATE-ing in place, so the full version history is
552
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
553
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
554
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
555
+ * worker.
556
+ */
557
+ async ensureSkillsTable(name) {
558
+ const safe = sqlIdent(name);
392
559
  const tables = await this.listTables();
393
- if (!tables.includes(name)) {
394
- log2(`table "${name}" not found, creating`);
395
- 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`);
396
- log2(`table "${name}" created`);
397
- if (!tables.includes(name))
398
- this._tablesCache = [...tables, name];
560
+ if (!tables.includes(safe)) {
561
+ log2(`table "${safe}" not found, creating`);
562
+ 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);
563
+ log2(`table "${safe}" created`);
564
+ if (!tables.includes(safe))
565
+ this._tablesCache = [...tables, safe];
399
566
  }
400
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
567
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
401
568
  }
402
569
  };
403
570
 
@@ -6,17 +6,21 @@ import { fileURLToPath } from "node:url";
6
6
  import { dirname as dirname2, join as join4 } from "node:path";
7
7
 
8
8
  // dist/src/commands/auth.js
9
- import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
9
+ import { execSync } from "node:child_process";
10
+
11
+ // dist/src/commands/auth-creds.js
12
+ import { readFileSync, writeFileSync, mkdirSync, unlinkSync } from "node:fs";
10
13
  import { join } from "node:path";
11
14
  import { homedir } from "node:os";
12
- import { execSync } from "node:child_process";
13
- var CONFIG_DIR = join(homedir(), ".deeplake");
14
- var CREDS_PATH = join(CONFIG_DIR, "credentials.json");
15
+ function configDir() {
16
+ return join(homedir(), ".deeplake");
17
+ }
18
+ function credsPath() {
19
+ return join(configDir(), "credentials.json");
20
+ }
15
21
  function loadCredentials() {
16
- if (!existsSync(CREDS_PATH))
17
- return null;
18
22
  try {
19
- return JSON.parse(readFileSync(CREDS_PATH, "utf-8"));
23
+ return JSON.parse(readFileSync(credsPath(), "utf-8"));
20
24
  } catch {
21
25
  return null;
22
26
  }
@@ -97,13 +101,48 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
97
101
  // dist/src/hooks/codex/session-start.js
98
102
  var log2 = (msg) => log("codex-session-start", msg);
99
103
  var __bundleDir = dirname2(fileURLToPath(import.meta.url));
100
- var AUTH_CMD = join4(__bundleDir, "commands", "auth-login.js");
101
104
  var context = `DEEPLAKE MEMORY: Persistent memory at ~/.deeplake/memory/ shared across sessions, users, and agents.
102
105
 
103
- Structure: index.md (start here) \u2192 summaries/*.md \u2192 sessions/*.jsonl (last resort). Do NOT jump straight to JSONL.
104
- Search: grep -r "keyword" ~/.deeplake/memory/
105
- IMPORTANT: Only use bash commands (cat, ls, grep, echo, jq, head, tail, sed, awk, etc.) to interact with ~/.deeplake/memory/. Do NOT use python, python3, node, curl, or other interpreters \u2014 they are not available in the memory filesystem.
106
- Do NOT spawn subagents to read deeplake memory.`;
106
+ Deeplake memory has THREE tiers \u2014 pick the right one for the question:
107
+ 1. ~/.deeplake/memory/index.md \u2014 auto-generated index, top 50 most-recently-updated entries with Created + Last Updated + Project + Description columns. ~5 KB. **For "what's recent / who did X this week / since <date>" queries, START HERE** and trust the Last Updated column over any "Started:" line in summary bodies.
108
+ 2. ~/.deeplake/memory/summaries/ \u2014 condensed wiki summaries per session (~3 KB each). For keyword/topic recall, search these.
109
+ 3. ~/.deeplake/memory/sessions/ \u2014 raw full-dialogue JSONL (~5 KB each). FALLBACK only \u2014 use when summaries don't contain the exact quote/turn you need.
110
+
111
+ Search workflow:
112
+ - Time-based ("last week", "today", "since X"): cat ~/.deeplake/memory/index.md and read the most-recent rows.
113
+ - Keyword/topic recall: grep -r "keyword" ~/.deeplake/memory/summaries/ (the shell hook routes this through hybrid lexical+semantic search \u2014 synonyms match too). Then cat the top-matching summary.
114
+ - Raw transcript fallback only: grep -r "keyword" ~/.deeplake/memory/sessions/ (use sparingly \u2014 JSONL is verbose).
115
+
116
+ \u2705 grep -r "keyword" ~/.deeplake/memory/summaries/
117
+ \u274C grep without a summaries/ or sessions/ suffix \u2014 too noisy
118
+
119
+ IMPORTANT: Only use bash builtins (cat, ls, grep, echo, jq, head, tail, sed, awk, etc.) on ~/.deeplake/memory/. Do NOT use python, python3, node, curl, or other interpreters \u2014 they are not available in the memory filesystem.
120
+ Do NOT spawn subagents to read deeplake memory.
121
+
122
+ Organization management \u2014 each argument is SEPARATE (do NOT quote subcommands together):
123
+ - hivemind login \u2014 SSO login
124
+ - hivemind whoami \u2014 show current user/org
125
+ - hivemind org list \u2014 list organizations
126
+ - hivemind org switch <name-or-id> \u2014 switch organization
127
+ - hivemind workspaces \u2014 list workspaces
128
+ - hivemind workspace <id> \u2014 switch workspace
129
+ - hivemind invite <email> <ADMIN|WRITE|READ> \u2014 invite member (ALWAYS ask user which role before inviting)
130
+ - hivemind members \u2014 list members
131
+ - hivemind remove <user-id> \u2014 remove member
132
+
133
+ SKILLS (skilify) \u2014 mine + share reusable skills across the org:
134
+ - hivemind skilify \u2014 show scope/team/install + per-project state
135
+ - hivemind skilify pull \u2014 sync project skills from the org table
136
+ - hivemind skilify pull --user <email> \u2014 only that author's skills
137
+ - hivemind skilify pull --users a,b,c \u2014 multiple authors (CSV)
138
+ - hivemind skilify pull --all-users \u2014 explicit "no author filter"
139
+ - hivemind skilify pull --to project|global \u2014 install location
140
+ - hivemind skilify pull --dry-run \u2014 preview only
141
+ - hivemind skilify pull --force \u2014 overwrite local (creates .bak)
142
+ - hivemind skilify pull <skill-name> \u2014 pull only that skill (combines with --user)
143
+ - hivemind skilify scope <me|team|org> \u2014 sharing scope for new skills
144
+ - hivemind skilify install <project|global> \u2014 default install location
145
+ - hivemind skilify team add|remove|list <name> \u2014 manage team list`;
107
146
  async function main() {
108
147
  if (process.env.HIVEMIND_WIKI_WORKER === "1")
109
148
  return;
@@ -134,7 +173,7 @@ Hivemind v${current}`;
134
173
  }
135
174
  const additionalContext = creds?.token ? `${context}
136
175
  Logged in to Deeplake as org: ${creds.orgName ?? creds.orgId} (workspace: ${creds.workspaceId ?? "default"})${versionNotice}` : `${context}
137
- Not logged in to Deeplake. Run: node "${AUTH_CMD}" login${versionNotice}`;
176
+ Not logged in to Deeplake. Run: hivemind login${versionNotice}`;
138
177
  console.log(additionalContext);
139
178
  }
140
179
  main().catch((e) => {