@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,3 +1,56 @@
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, readFileSync as readFileSync2, writeFileSync } from "node:fs";
20
+ import { join as join3 } from "node:path";
21
+ import { tmpdir } from "node:os";
22
+ function getIndexMarkerDir() {
23
+ return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join3(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 join3(getIndexMarkerDir(), `${markerKey}.json`);
28
+ }
29
+ function hasFreshIndexMarker(markerPath) {
30
+ if (!existsSync2(markerPath))
31
+ return false;
32
+ try {
33
+ const raw = JSON.parse(readFileSync2(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
+ mkdirSync(getIndexMarkerDir(), { recursive: true });
44
+ writeFileSync(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/utils/stdin.js
2
55
  function readStdin() {
3
56
  return new Promise((resolve, reject) => {
@@ -43,15 +96,13 @@ function loadConfig() {
43
96
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
44
97
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
45
98
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
99
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
46
100
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join(home, ".deeplake", "memory")
47
101
  };
48
102
  }
49
103
 
50
104
  // dist/src/deeplake-api.js
51
105
  import { randomUUID } from "node:crypto";
52
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
53
- import { join as join3 } from "node:path";
54
- import { tmpdir } from "node:os";
55
106
 
56
107
  // dist/src/utils/debug.js
57
108
  import { appendFileSync } from "node:fs";
@@ -73,8 +124,33 @@ function sqlStr(value) {
73
124
  function sqlLike(value) {
74
125
  return sqlStr(value).replace(/%/g, "\\%").replace(/_/g, "\\_");
75
126
  }
127
+ function sqlIdent(name) {
128
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
129
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
130
+ }
131
+ return name;
132
+ }
133
+
134
+ // dist/src/embeddings/columns.js
135
+ var SUMMARY_EMBEDDING_COL = "summary_embedding";
136
+ var MESSAGE_EMBEDDING_COL = "message_embedding";
137
+
138
+ // dist/src/utils/client-header.js
139
+ var DEEPLAKE_CLIENT_HEADER = "X-Deeplake-Client";
140
+ function deeplakeClientValue() {
141
+ return "hivemind";
142
+ }
143
+ function deeplakeClientHeader() {
144
+ return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
145
+ }
76
146
 
77
147
  // dist/src/deeplake-api.js
148
+ var indexMarkerStorePromise = null;
149
+ function getIndexMarkerStore() {
150
+ if (!indexMarkerStorePromise)
151
+ indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
152
+ return indexMarkerStorePromise;
153
+ }
78
154
  var log2 = (msg) => log("sdk", msg);
79
155
  function summarizeSql(sql, maxLen = 220) {
80
156
  const compact = sql.replace(/\s+/g, " ").trim();
@@ -94,7 +170,6 @@ var MAX_RETRIES = 3;
94
170
  var BASE_DELAY_MS = 500;
95
171
  var MAX_CONCURRENCY = 5;
96
172
  var QUERY_TIMEOUT_MS = Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
97
- var INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
98
173
  function sleep(ms) {
99
174
  return new Promise((resolve) => setTimeout(resolve, ms));
100
175
  }
@@ -114,9 +189,6 @@ function isTransientHtml403(text) {
114
189
  const body = text.toLowerCase();
115
190
  return body.includes("<html") || body.includes("403 forbidden") || body.includes("cloudflare") || body.includes("nginx");
116
191
  }
117
- function getIndexMarkerDir() {
118
- return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join3(tmpdir(), "hivemind-deeplake-indexes");
119
- }
120
192
  var Semaphore = class {
121
193
  max;
122
194
  waiting = [];
@@ -185,7 +257,8 @@ var DeeplakeApi = class {
185
257
  headers: {
186
258
  Authorization: `Bearer ${this.token}`,
187
259
  "Content-Type": "application/json",
188
- "X-Activeloop-Org-Id": this.orgId
260
+ "X-Activeloop-Org-Id": this.orgId,
261
+ ...deeplakeClientHeader()
189
262
  },
190
263
  signal,
191
264
  body: JSON.stringify({ query: sql })
@@ -212,7 +285,8 @@ var DeeplakeApi = class {
212
285
  }
213
286
  const text = await resp.text().catch(() => "");
214
287
  const retryable403 = isSessionInsertQuery(sql) && (resp.status === 401 || resp.status === 403 && (text.length === 0 || isTransientHtml403(text)));
215
- if (attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
288
+ const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
289
+ if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
216
290
  const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
217
291
  log2(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
218
292
  await sleep(delay);
@@ -246,7 +320,7 @@ var DeeplakeApi = class {
246
320
  const lud = row.lastUpdateDate ?? ts;
247
321
  const exists = await this.query(`SELECT path FROM "${this.tableName}" WHERE path = '${sqlStr(row.path)}' LIMIT 1`);
248
322
  if (exists.length > 0) {
249
- let setClauses = `summary = E'${sqlStr(row.contentText)}', mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
323
+ let setClauses = `summary = E'${sqlStr(row.contentText)}', ${SUMMARY_EMBEDDING_COL} = NULL, mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
250
324
  if (row.project !== void 0)
251
325
  setClauses += `, project = '${sqlStr(row.project)}'`;
252
326
  if (row.description !== void 0)
@@ -254,8 +328,8 @@ var DeeplakeApi = class {
254
328
  await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(row.path)}'`);
255
329
  } else {
256
330
  const id = randomUUID();
257
- let cols = "id, path, filename, summary, mime_type, size_bytes, creation_date, last_update_date";
258
- let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
331
+ let cols = `id, path, filename, summary, ${SUMMARY_EMBEDDING_COL}, mime_type, size_bytes, creation_date, last_update_date`;
332
+ let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', NULL, '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
259
333
  if (row.project !== void 0) {
260
334
  cols += ", project";
261
335
  vals += `, '${sqlStr(row.project)}'`;
@@ -280,48 +354,83 @@ var DeeplakeApi = class {
280
354
  buildLookupIndexName(table, suffix) {
281
355
  return `idx_${table}_${suffix}`.replace(/[^a-zA-Z0-9_]/g, "_");
282
356
  }
283
- getLookupIndexMarkerPath(table, suffix) {
284
- const markerKey = [
285
- this.workspaceId,
286
- this.orgId,
287
- table,
288
- suffix
289
- ].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
290
- return join3(getIndexMarkerDir(), `${markerKey}.json`);
291
- }
292
- hasFreshLookupIndexMarker(table, suffix) {
293
- const markerPath = this.getLookupIndexMarkerPath(table, suffix);
294
- if (!existsSync2(markerPath))
295
- return false;
296
- try {
297
- const raw = JSON.parse(readFileSync2(markerPath, "utf-8"));
298
- const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
299
- if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
300
- return false;
301
- return true;
302
- } catch {
303
- return false;
304
- }
305
- }
306
- markLookupIndexReady(table, suffix) {
307
- mkdirSync(getIndexMarkerDir(), { recursive: true });
308
- writeFileSync(this.getLookupIndexMarkerPath(table, suffix), JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
309
- }
310
357
  async ensureLookupIndex(table, suffix, columnsSql) {
311
- if (this.hasFreshLookupIndexMarker(table, suffix))
358
+ const markers = await getIndexMarkerStore();
359
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, suffix);
360
+ if (markers.hasFreshIndexMarker(markerPath))
312
361
  return;
313
362
  const indexName = this.buildLookupIndexName(table, suffix);
314
363
  try {
315
364
  await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ${columnsSql}`);
316
- this.markLookupIndexReady(table, suffix);
365
+ markers.writeIndexMarker(markerPath);
317
366
  } catch (e) {
318
367
  if (isDuplicateIndexError(e)) {
319
- this.markLookupIndexReady(table, suffix);
368
+ markers.writeIndexMarker(markerPath);
320
369
  return;
321
370
  }
322
371
  log2(`index "${indexName}" skipped: ${e.message}`);
323
372
  }
324
373
  }
374
+ /**
375
+ * Ensure a vector column exists on the given table.
376
+ *
377
+ * The previous implementation always issued `ALTER TABLE ADD COLUMN IF NOT
378
+ * EXISTS …` on every SessionStart. On a long-running workspace that's
379
+ * already migrated, every call returns 500 "Column already exists" — noisy
380
+ * in the log and a wasted round-trip. Worse, the very first call after the
381
+ * column is genuinely added triggers Deeplake's post-ALTER `vector::at`
382
+ * window (~30s) during which subsequent INSERTs fail; minimising the
383
+ * number of ALTER calls minimises exposure to that window.
384
+ *
385
+ * New flow:
386
+ * 1. Check the local marker file (mirrors ensureLookupIndex). If fresh,
387
+ * return — zero network calls.
388
+ * 2. SELECT 1 FROM information_schema.columns WHERE table_name = T AND
389
+ * column_name = C. Read-only, idempotent, can't tickle the post-ALTER
390
+ * bug. If the column is present → mark + return.
391
+ * 3. Only if step 2 says the column is missing, fall back to ALTER ADD
392
+ * COLUMN IF NOT EXISTS. Mark on success, also mark if Deeplake reports
393
+ * "already exists" (race: another client added it between our SELECT
394
+ * and ALTER).
395
+ *
396
+ * Marker uses the same dir / TTL as ensureLookupIndex so both schema
397
+ * caches share an opt-out (HIVEMIND_INDEX_MARKER_DIR) and a TTL knob.
398
+ */
399
+ async ensureEmbeddingColumn(table, column) {
400
+ await this.ensureColumn(table, column, "FLOAT4[]");
401
+ }
402
+ /**
403
+ * Generic marker-gated column migration. Same SELECT-then-ALTER flow as
404
+ * ensureEmbeddingColumn, parameterized by SQL type so it can patch up any
405
+ * column that was added to the schema after the table was originally
406
+ * created. Used today for `summary_embedding`, `message_embedding`, and
407
+ * the `agent` column (added 2026-04-11) — the latter has no fallback if
408
+ * a user upgraded over a pre-2026-04-11 table, so every INSERT fails
409
+ * with `column "agent" does not exist`.
410
+ */
411
+ async ensureColumn(table, column, sqlType) {
412
+ const markers = await getIndexMarkerStore();
413
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, `col_${column}`);
414
+ if (markers.hasFreshIndexMarker(markerPath))
415
+ return;
416
+ 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`;
417
+ const rows = await this.query(colCheck);
418
+ if (rows.length > 0) {
419
+ markers.writeIndexMarker(markerPath);
420
+ return;
421
+ }
422
+ try {
423
+ await this.query(`ALTER TABLE "${table}" ADD COLUMN ${column} ${sqlType}`);
424
+ } catch (e) {
425
+ const msg = e instanceof Error ? e.message : String(e);
426
+ if (!/already exists/i.test(msg))
427
+ throw e;
428
+ const recheck = await this.query(colCheck);
429
+ if (recheck.length === 0)
430
+ throw e;
431
+ }
432
+ markers.writeIndexMarker(markerPath);
433
+ }
325
434
  /** List all tables in the workspace (with retry). */
326
435
  async listTables(forceRefresh = false) {
327
436
  if (!forceRefresh && this._tablesCache)
@@ -337,7 +446,8 @@ var DeeplakeApi = class {
337
446
  const resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables`, {
338
447
  headers: {
339
448
  Authorization: `Bearer ${this.token}`,
340
- "X-Activeloop-Org-Id": this.orgId
449
+ "X-Activeloop-Org-Id": this.orgId,
450
+ ...deeplakeClientHeader()
341
451
  }
342
452
  });
343
453
  if (resp.ok) {
@@ -362,29 +472,84 @@ var DeeplakeApi = class {
362
472
  }
363
473
  return { tables: [], cacheable: false };
364
474
  }
475
+ /**
476
+ * Run a `CREATE TABLE` with an extra outer retry budget. The base
477
+ * `query()` already retries 3 times on fetch errors (~3.5s total), but a
478
+ * failed CREATE is permanent corruption — every subsequent SELECT against
479
+ * the missing table fails. Wrapping in an outer loop with longer backoff
480
+ * (2s, 5s, then 10s) gives us ~17s of reach across transient network
481
+ * blips before giving up. Failures still propagate; getApi() resets its
482
+ * cache on init failure (openclaw plugin) so the next call retries the
483
+ * whole init flow.
484
+ */
485
+ async createTableWithRetry(sql, label) {
486
+ const OUTER_BACKOFFS_MS = [2e3, 5e3, 1e4];
487
+ let lastErr = null;
488
+ for (let attempt = 0; attempt <= OUTER_BACKOFFS_MS.length; attempt++) {
489
+ try {
490
+ await this.query(sql);
491
+ return;
492
+ } catch (err) {
493
+ lastErr = err;
494
+ const msg = err instanceof Error ? err.message : String(err);
495
+ log2(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
496
+ if (attempt < OUTER_BACKOFFS_MS.length) {
497
+ await sleep(OUTER_BACKOFFS_MS[attempt]);
498
+ }
499
+ }
500
+ }
501
+ throw lastErr;
502
+ }
365
503
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
366
504
  async ensureTable(name) {
367
- const tbl = name ?? this.tableName;
505
+ const tbl = sqlIdent(name ?? this.tableName);
368
506
  const tables = await this.listTables();
369
507
  if (!tables.includes(tbl)) {
370
508
  log2(`table "${tbl}" not found, creating`);
371
- 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`);
509
+ 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);
372
510
  log2(`table "${tbl}" created`);
373
511
  if (!tables.includes(tbl))
374
512
  this._tablesCache = [...tables, tbl];
375
513
  }
514
+ await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
515
+ await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
376
516
  }
377
517
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
378
518
  async ensureSessionsTable(name) {
519
+ const safe = sqlIdent(name);
520
+ const tables = await this.listTables();
521
+ if (!tables.includes(safe)) {
522
+ log2(`table "${safe}" not found, creating`);
523
+ 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);
524
+ log2(`table "${safe}" created`);
525
+ if (!tables.includes(safe))
526
+ this._tablesCache = [...tables, safe];
527
+ }
528
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
529
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
530
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
531
+ }
532
+ /**
533
+ * Create the skills table.
534
+ *
535
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
536
+ * MERGE rather than UPDATE-ing in place, so the full version history is
537
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
538
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
539
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
540
+ * worker.
541
+ */
542
+ async ensureSkillsTable(name) {
543
+ const safe = sqlIdent(name);
379
544
  const tables = await this.listTables();
380
- if (!tables.includes(name)) {
381
- log2(`table "${name}" not found, creating`);
382
- 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`);
383
- log2(`table "${name}" created`);
384
- if (!tables.includes(name))
385
- this._tablesCache = [...tables, name];
545
+ if (!tables.includes(safe)) {
546
+ log2(`table "${safe}" not found, creating`);
547
+ 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);
548
+ log2(`table "${safe}" created`);
549
+ if (!tables.includes(safe))
550
+ this._tablesCache = [...tables, safe];
386
551
  }
387
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
552
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
388
553
  }
389
554
  };
390
555
 
@@ -552,24 +717,25 @@ function normalizeContent(path, raw) {
552
717
  return raw;
553
718
  }
554
719
  if (Array.isArray(obj.turns)) {
555
- const header = [];
556
- if (obj.date_time)
557
- header.push(`date: ${obj.date_time}`);
558
- if (obj.speakers) {
559
- const s = obj.speakers;
560
- const names = [s.speaker_a, s.speaker_b].filter(Boolean).join(", ");
561
- if (names)
562
- header.push(`speakers: ${names}`);
563
- }
720
+ const dateHeader = obj.date_time ? `(${String(obj.date_time)}) ` : "";
564
721
  const lines = obj.turns.map((t) => {
565
722
  const sp = String(t?.speaker ?? t?.name ?? "?").trim();
566
723
  const tx = String(t?.text ?? t?.content ?? "").replace(/\s+/g, " ").trim();
567
724
  const tag = t?.dia_id ? `[${t.dia_id}] ` : "";
568
- return `${tag}${sp}: ${tx}`;
725
+ return `${dateHeader}${tag}${sp}: ${tx}`;
569
726
  });
570
- const out2 = [...header, ...lines].join("\n");
727
+ const out2 = lines.join("\n");
571
728
  return out2.trim() ? out2 : raw;
572
729
  }
730
+ if (obj.turn && typeof obj.turn === "object" && !Array.isArray(obj.turn)) {
731
+ const t = obj.turn;
732
+ const sp = String(t.speaker ?? t.name ?? "?").trim();
733
+ const tx = String(t.text ?? t.content ?? "").replace(/\s+/g, " ").trim();
734
+ const tag = t.dia_id ? `[${String(t.dia_id)}] ` : "";
735
+ const dateHeader = obj.date_time ? `(${String(obj.date_time)}) ` : "";
736
+ const line = `${dateHeader}${tag}${sp}: ${tx}`;
737
+ return line.trim() ? line : raw;
738
+ }
573
739
  const stripRecalled = (t) => {
574
740
  const i = t.indexOf("<recalled-memories>");
575
741
  if (i === -1)
@@ -612,8 +778,38 @@ function buildPathCondition(targetPath) {
612
778
  return `(path = '${sqlStr(clean)}' OR path LIKE '${sqlLike(clean)}/%' ESCAPE '\\')`;
613
779
  }
614
780
  async function searchDeeplakeTables(api, memoryTable, sessionsTable, opts) {
615
- const { pathFilter, contentScanOnly, likeOp, escapedPattern, prefilterPattern, prefilterPatterns, multiWordPatterns } = opts;
781
+ const { pathFilter, contentScanOnly, likeOp, escapedPattern, prefilterPattern, prefilterPatterns, queryEmbedding, multiWordPatterns } = opts;
616
782
  const limit = opts.limit ?? 100;
783
+ if (queryEmbedding && queryEmbedding.length > 0) {
784
+ const vecLit = serializeFloat4Array(queryEmbedding);
785
+ const semanticLimit = Math.min(limit, Number(process.env.HIVEMIND_SEMANTIC_LIMIT ?? "20"));
786
+ const lexicalLimit = Math.min(limit, Number(process.env.HIVEMIND_HYBRID_LEXICAL_LIMIT ?? "20"));
787
+ const filterPatternsForLex = contentScanOnly ? prefilterPatterns && prefilterPatterns.length > 0 ? prefilterPatterns : prefilterPattern ? [prefilterPattern] : [] : [escapedPattern];
788
+ const memLexFilter = buildContentFilter("summary::text", likeOp, filterPatternsForLex);
789
+ const sessLexFilter = buildContentFilter("message::text", likeOp, filterPatternsForLex);
790
+ const memLexQuery = memLexFilter ? `SELECT path, summary::text AS content, 0 AS source_order, '' AS creation_date, 1.0 AS score FROM "${memoryTable}" WHERE 1=1${pathFilter}${memLexFilter} LIMIT ${lexicalLimit}` : null;
791
+ const sessLexQuery = sessLexFilter ? `SELECT path, message::text AS content, 1 AS source_order, COALESCE(creation_date::text, '') AS creation_date, 1.0 AS score FROM "${sessionsTable}" WHERE 1=1${pathFilter}${sessLexFilter} LIMIT ${lexicalLimit}` : null;
792
+ const memSemQuery = `SELECT path, summary::text AS content, 0 AS source_order, '' AS creation_date, (summary_embedding <#> ${vecLit}) AS score FROM "${memoryTable}" WHERE ARRAY_LENGTH(summary_embedding, 1) > 0${pathFilter} ORDER BY score DESC LIMIT ${semanticLimit}`;
793
+ const sessSemQuery = `SELECT path, message::text AS content, 1 AS source_order, COALESCE(creation_date::text, '') AS creation_date, (message_embedding <#> ${vecLit}) AS score FROM "${sessionsTable}" WHERE ARRAY_LENGTH(message_embedding, 1) > 0${pathFilter} ORDER BY score DESC LIMIT ${semanticLimit}`;
794
+ const parts = [memSemQuery, sessSemQuery];
795
+ if (memLexQuery)
796
+ parts.push(memLexQuery);
797
+ if (sessLexQuery)
798
+ parts.push(sessLexQuery);
799
+ const unionSql = parts.map((q) => `(${q})`).join(" UNION ALL ");
800
+ const outerLimit = semanticLimit + lexicalLimit;
801
+ const rows2 = await api.query(`SELECT path, content, source_order, creation_date, score FROM (` + unionSql + `) AS combined ORDER BY score DESC LIMIT ${outerLimit}`);
802
+ const seen = /* @__PURE__ */ new Set();
803
+ const unique = [];
804
+ for (const row of rows2) {
805
+ const p = String(row["path"]);
806
+ if (seen.has(p))
807
+ continue;
808
+ seen.add(p);
809
+ unique.push({ path: p, content: String(row["content"] ?? "") });
810
+ }
811
+ return unique;
812
+ }
617
813
  const filterPatterns = contentScanOnly ? prefilterPatterns && prefilterPatterns.length > 0 ? prefilterPatterns : prefilterPattern ? [prefilterPattern] : [] : multiWordPatterns && multiWordPatterns.length > 1 ? multiWordPatterns : [escapedPattern];
618
814
  const memFilter = buildContentFilter("summary::text", likeOp, filterPatterns);
619
815
  const sessFilter = buildContentFilter("message::text", likeOp, filterPatterns);
@@ -625,6 +821,15 @@ async function searchDeeplakeTables(api, memoryTable, sessionsTable, opts) {
625
821
  content: String(row["content"] ?? "")
626
822
  }));
627
823
  }
824
+ function serializeFloat4Array(vec) {
825
+ const parts = [];
826
+ for (const v of vec) {
827
+ if (!Number.isFinite(v))
828
+ return "NULL";
829
+ parts.push(String(v));
830
+ }
831
+ return `ARRAY[${parts.join(",")}]::float4[]`;
832
+ }
628
833
  function buildPathFilter(targetPath) {
629
834
  const condition = buildPathCondition(targetPath);
630
835
  return condition ? ` AND ${condition}` : "";
@@ -707,7 +912,7 @@ function buildGrepSearchOptions(params, targetPath) {
707
912
  return {
708
913
  pathFilter: buildPathFilter(targetPath),
709
914
  contentScanOnly: hasRegexMeta,
710
- likeOp: params.ignoreCase ? "ILIKE" : "LIKE",
915
+ likeOp: process.env.HIVEMIND_GREP_LIKE === "case-sensitive" ? "LIKE" : "ILIKE",
711
916
  escapedPattern: sqlLike(params.pattern),
712
917
  prefilterPattern: literalPrefilter ? sqlLike(literalPrefilter) : void 0,
713
918
  prefilterPatterns: alternationPrefilters?.map((literal) => sqlLike(literal)),
@@ -762,11 +967,28 @@ function refineGrepMatches(rows, params, forceMultiFilePrefix) {
762
967
  }
763
968
  return output;
764
969
  }
765
- async function grepBothTables(api, memoryTable, sessionsTable, params, targetPath) {
766
- const rows = await searchDeeplakeTables(api, memoryTable, sessionsTable, buildGrepSearchOptions(params, targetPath));
970
+ async function grepBothTables(api, memoryTable, sessionsTable, params, targetPath, queryEmbedding) {
971
+ const rows = await searchDeeplakeTables(api, memoryTable, sessionsTable, {
972
+ ...buildGrepSearchOptions(params, targetPath),
973
+ queryEmbedding
974
+ });
767
975
  const seen = /* @__PURE__ */ new Set();
768
976
  const unique = rows.filter((r) => seen.has(r.path) ? false : (seen.add(r.path), true));
769
977
  const normalized = unique.map((r) => ({ path: r.path, content: normalizeContent(r.path, r.content) }));
978
+ if (queryEmbedding && queryEmbedding.length > 0) {
979
+ const emitAllLines = process.env.HIVEMIND_SEMANTIC_EMIT_ALL !== "false";
980
+ if (emitAllLines) {
981
+ const lines = [];
982
+ for (const r of normalized) {
983
+ for (const line of r.content.split("\n")) {
984
+ const trimmed = line.trim();
985
+ if (trimmed)
986
+ lines.push(`${r.path}:${line}`);
987
+ }
988
+ }
989
+ return lines;
990
+ }
991
+ }
770
992
  return refineGrepMatches(normalized, params);
771
993
  }
772
994
 
@@ -810,7 +1032,298 @@ function capOutputForClaude(output, options = {}) {
810
1032
  return keptLines.join("\n") + footer;
811
1033
  }
812
1034
 
1035
+ // dist/src/embeddings/client.js
1036
+ import { connect } from "node:net";
1037
+ import { spawn } from "node:child_process";
1038
+ import { openSync, closeSync, writeSync, unlinkSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
1039
+ import { homedir as homedir3 } from "node:os";
1040
+ import { join as join4 } from "node:path";
1041
+
1042
+ // dist/src/embeddings/protocol.js
1043
+ var DEFAULT_SOCKET_DIR = "/tmp";
1044
+ var DEFAULT_IDLE_TIMEOUT_MS = 10 * 60 * 1e3;
1045
+ var DEFAULT_CLIENT_TIMEOUT_MS = 2e3;
1046
+ function socketPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
1047
+ return `${dir}/hivemind-embed-${uid}.sock`;
1048
+ }
1049
+ function pidPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
1050
+ return `${dir}/hivemind-embed-${uid}.pid`;
1051
+ }
1052
+
1053
+ // dist/src/embeddings/client.js
1054
+ var SHARED_DAEMON_PATH = join4(homedir3(), ".hivemind", "embed-deps", "embed-daemon.js");
1055
+ var log3 = (m) => log("embed-client", m);
1056
+ function getUid() {
1057
+ const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
1058
+ return uid !== void 0 ? String(uid) : process.env.USER ?? "default";
1059
+ }
1060
+ var EmbedClient = class {
1061
+ socketPath;
1062
+ pidPath;
1063
+ timeoutMs;
1064
+ daemonEntry;
1065
+ autoSpawn;
1066
+ spawnWaitMs;
1067
+ nextId = 0;
1068
+ constructor(opts = {}) {
1069
+ const uid = getUid();
1070
+ const dir = opts.socketDir ?? "/tmp";
1071
+ this.socketPath = socketPathFor(uid, dir);
1072
+ this.pidPath = pidPathFor(uid, dir);
1073
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_CLIENT_TIMEOUT_MS;
1074
+ this.daemonEntry = opts.daemonEntry ?? process.env.HIVEMIND_EMBED_DAEMON ?? (existsSync3(SHARED_DAEMON_PATH) ? SHARED_DAEMON_PATH : void 0);
1075
+ this.autoSpawn = opts.autoSpawn ?? true;
1076
+ this.spawnWaitMs = opts.spawnWaitMs ?? 5e3;
1077
+ }
1078
+ /**
1079
+ * Returns an embedding vector, or null on timeout/failure. Hooks MUST treat
1080
+ * null as "skip embedding column" — never block the write path on us.
1081
+ *
1082
+ * Fire-and-forget spawn on miss: if the daemon isn't up, this call returns
1083
+ * null AND kicks off a background spawn. The next call finds a ready daemon.
1084
+ */
1085
+ async embed(text, kind = "document") {
1086
+ let sock;
1087
+ try {
1088
+ sock = await this.connectOnce();
1089
+ } catch {
1090
+ if (this.autoSpawn)
1091
+ this.trySpawnDaemon();
1092
+ return null;
1093
+ }
1094
+ try {
1095
+ const id = String(++this.nextId);
1096
+ const req = { op: "embed", id, kind, text };
1097
+ const resp = await this.sendAndWait(sock, req);
1098
+ if (resp.error || !("embedding" in resp) || !resp.embedding) {
1099
+ log3(`embed err: ${resp.error ?? "no embedding"}`);
1100
+ return null;
1101
+ }
1102
+ return resp.embedding;
1103
+ } catch (e) {
1104
+ const err = e instanceof Error ? e.message : String(e);
1105
+ log3(`embed failed: ${err}`);
1106
+ return null;
1107
+ } finally {
1108
+ try {
1109
+ sock.end();
1110
+ } catch {
1111
+ }
1112
+ }
1113
+ }
1114
+ /**
1115
+ * Wait up to spawnWaitMs for the daemon to accept connections, spawning if
1116
+ * necessary. Meant for SessionStart / long-running batches — not the hot path.
1117
+ */
1118
+ async warmup() {
1119
+ try {
1120
+ const s = await this.connectOnce();
1121
+ s.end();
1122
+ return true;
1123
+ } catch {
1124
+ if (!this.autoSpawn)
1125
+ return false;
1126
+ this.trySpawnDaemon();
1127
+ try {
1128
+ const s = await this.waitForSocket();
1129
+ s.end();
1130
+ return true;
1131
+ } catch {
1132
+ return false;
1133
+ }
1134
+ }
1135
+ }
1136
+ connectOnce() {
1137
+ return new Promise((resolve, reject) => {
1138
+ const sock = connect(this.socketPath);
1139
+ const to = setTimeout(() => {
1140
+ sock.destroy();
1141
+ reject(new Error("connect timeout"));
1142
+ }, this.timeoutMs);
1143
+ sock.once("connect", () => {
1144
+ clearTimeout(to);
1145
+ resolve(sock);
1146
+ });
1147
+ sock.once("error", (e) => {
1148
+ clearTimeout(to);
1149
+ reject(e);
1150
+ });
1151
+ });
1152
+ }
1153
+ trySpawnDaemon() {
1154
+ let fd;
1155
+ try {
1156
+ fd = openSync(this.pidPath, "wx", 384);
1157
+ writeSync(fd, String(process.pid));
1158
+ } catch (e) {
1159
+ if (this.isPidFileStale()) {
1160
+ try {
1161
+ unlinkSync(this.pidPath);
1162
+ } catch {
1163
+ }
1164
+ try {
1165
+ fd = openSync(this.pidPath, "wx", 384);
1166
+ writeSync(fd, String(process.pid));
1167
+ } catch {
1168
+ return;
1169
+ }
1170
+ } else {
1171
+ return;
1172
+ }
1173
+ }
1174
+ if (!this.daemonEntry || !existsSync3(this.daemonEntry)) {
1175
+ log3(`daemonEntry not configured or missing: ${this.daemonEntry}`);
1176
+ try {
1177
+ closeSync(fd);
1178
+ unlinkSync(this.pidPath);
1179
+ } catch {
1180
+ }
1181
+ return;
1182
+ }
1183
+ try {
1184
+ const child = spawn(process.execPath, [this.daemonEntry], {
1185
+ detached: true,
1186
+ stdio: "ignore",
1187
+ env: process.env
1188
+ });
1189
+ child.unref();
1190
+ log3(`spawned daemon pid=${child.pid}`);
1191
+ } finally {
1192
+ closeSync(fd);
1193
+ }
1194
+ }
1195
+ isPidFileStale() {
1196
+ try {
1197
+ const raw = readFileSync3(this.pidPath, "utf-8").trim();
1198
+ const pid = Number(raw);
1199
+ if (!pid || Number.isNaN(pid))
1200
+ return true;
1201
+ try {
1202
+ process.kill(pid, 0);
1203
+ return false;
1204
+ } catch {
1205
+ return true;
1206
+ }
1207
+ } catch {
1208
+ return true;
1209
+ }
1210
+ }
1211
+ async waitForSocket() {
1212
+ const deadline = Date.now() + this.spawnWaitMs;
1213
+ let delay = 30;
1214
+ while (Date.now() < deadline) {
1215
+ await sleep2(delay);
1216
+ delay = Math.min(delay * 1.5, 300);
1217
+ if (!existsSync3(this.socketPath))
1218
+ continue;
1219
+ try {
1220
+ return await this.connectOnce();
1221
+ } catch {
1222
+ }
1223
+ }
1224
+ throw new Error("daemon did not become ready within spawnWaitMs");
1225
+ }
1226
+ sendAndWait(sock, req) {
1227
+ return new Promise((resolve, reject) => {
1228
+ let buf = "";
1229
+ const to = setTimeout(() => {
1230
+ sock.destroy();
1231
+ reject(new Error("request timeout"));
1232
+ }, this.timeoutMs);
1233
+ sock.setEncoding("utf-8");
1234
+ sock.on("data", (chunk) => {
1235
+ buf += chunk;
1236
+ const nl = buf.indexOf("\n");
1237
+ if (nl === -1)
1238
+ return;
1239
+ const line = buf.slice(0, nl);
1240
+ clearTimeout(to);
1241
+ try {
1242
+ resolve(JSON.parse(line));
1243
+ } catch (e) {
1244
+ reject(e);
1245
+ }
1246
+ });
1247
+ sock.on("error", (e) => {
1248
+ clearTimeout(to);
1249
+ reject(e);
1250
+ });
1251
+ sock.on("end", () => {
1252
+ clearTimeout(to);
1253
+ reject(new Error("connection closed without response"));
1254
+ });
1255
+ sock.write(JSON.stringify(req) + "\n");
1256
+ });
1257
+ }
1258
+ };
1259
+ function sleep2(ms) {
1260
+ return new Promise((r) => setTimeout(r, ms));
1261
+ }
1262
+
1263
+ // dist/src/embeddings/disable.js
1264
+ import { createRequire } from "node:module";
1265
+ import { homedir as homedir4 } from "node:os";
1266
+ import { join as join5 } from "node:path";
1267
+ import { pathToFileURL } from "node:url";
1268
+ var cachedStatus = null;
1269
+ function defaultResolveTransformers() {
1270
+ try {
1271
+ createRequire(import.meta.url).resolve("@huggingface/transformers");
1272
+ return;
1273
+ } catch {
1274
+ }
1275
+ const sharedDir = join5(homedir4(), ".hivemind", "embed-deps");
1276
+ createRequire(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
1277
+ }
1278
+ var _resolve = defaultResolveTransformers;
1279
+ function detectStatus() {
1280
+ if (process.env.HIVEMIND_EMBEDDINGS === "false")
1281
+ return "env-disabled";
1282
+ try {
1283
+ _resolve();
1284
+ return "enabled";
1285
+ } catch {
1286
+ return "no-transformers";
1287
+ }
1288
+ }
1289
+ function embeddingsStatus() {
1290
+ if (cachedStatus !== null)
1291
+ return cachedStatus;
1292
+ cachedStatus = detectStatus();
1293
+ return cachedStatus;
1294
+ }
1295
+ function embeddingsDisabled() {
1296
+ return embeddingsStatus() !== "enabled";
1297
+ }
1298
+
813
1299
  // dist/src/hooks/grep-direct.js
1300
+ import { fileURLToPath } from "node:url";
1301
+ import { dirname, join as join6 } from "node:path";
1302
+ var SEMANTIC_ENABLED = process.env.HIVEMIND_SEMANTIC_SEARCH !== "false" && !embeddingsDisabled();
1303
+ var SEMANTIC_TIMEOUT_MS = Number(process.env.HIVEMIND_SEMANTIC_EMBED_TIMEOUT_MS ?? "500");
1304
+ function resolveDaemonPath() {
1305
+ return join6(dirname(fileURLToPath(import.meta.url)), "..", "embeddings", "embed-daemon.js");
1306
+ }
1307
+ var sharedEmbedClient = null;
1308
+ function getEmbedClient() {
1309
+ if (!sharedEmbedClient) {
1310
+ sharedEmbedClient = new EmbedClient({
1311
+ daemonEntry: resolveDaemonPath(),
1312
+ timeoutMs: SEMANTIC_TIMEOUT_MS
1313
+ });
1314
+ }
1315
+ return sharedEmbedClient;
1316
+ }
1317
+ function patternIsSemanticFriendly(pattern, fixedString) {
1318
+ if (!pattern || pattern.length < 2)
1319
+ return false;
1320
+ if (fixedString)
1321
+ return true;
1322
+ const meta = pattern.match(/[|()\[\]{}+?^$\\]/g);
1323
+ if (!meta)
1324
+ return true;
1325
+ return meta.length <= 1;
1326
+ }
814
1327
  function splitFirstPipelineStage(cmd) {
815
1328
  const input = cmd.trim();
816
1329
  let quote = null;
@@ -1128,15 +1641,23 @@ async function handleGrepDirect(api, table, sessionsTable, params) {
1128
1641
  invertMatch: params.invertMatch,
1129
1642
  fixedString: params.fixedString
1130
1643
  };
1131
- const output = await grepBothTables(api, table, sessionsTable, matchParams, params.targetPath);
1644
+ let queryEmbedding = null;
1645
+ if (SEMANTIC_ENABLED && patternIsSemanticFriendly(params.pattern, params.fixedString)) {
1646
+ try {
1647
+ queryEmbedding = await getEmbedClient().embed(params.pattern, "query");
1648
+ } catch {
1649
+ queryEmbedding = null;
1650
+ }
1651
+ }
1652
+ const output = await grepBothTables(api, table, sessionsTable, matchParams, params.targetPath, queryEmbedding);
1132
1653
  const joined = output.join("\n") || "(no matches)";
1133
1654
  return capOutputForClaude(joined, { kind: "grep" });
1134
1655
  }
1135
1656
 
1136
1657
  // dist/src/hooks/memory-path-utils.js
1137
- import { homedir as homedir3 } from "node:os";
1138
- import { join as join4 } from "node:path";
1139
- var MEMORY_PATH = join4(homedir3(), ".deeplake", "memory");
1658
+ import { homedir as homedir5 } from "node:os";
1659
+ import { join as join7 } from "node:path";
1660
+ var MEMORY_PATH = join7(homedir5(), ".deeplake", "memory");
1140
1661
  var TILDE_PATH = "~/.deeplake/memory";
1141
1662
  var HOME_VAR_PATH = "$HOME/.deeplake/memory";
1142
1663
  function touchesMemory(p) {
@@ -1147,7 +1668,7 @@ function rewritePaths(cmd) {
1147
1668
  }
1148
1669
 
1149
1670
  // dist/src/hooks/hermes/pre-tool-use.js
1150
- var log3 = (msg) => log("hermes-pre-tool-use", msg);
1671
+ var log4 = (msg) => log("hermes-pre-tool-use", msg);
1151
1672
  async function main() {
1152
1673
  const input = await readStdin();
1153
1674
  if (input.tool_name !== "terminal")
@@ -1164,7 +1685,7 @@ async function main() {
1164
1685
  return;
1165
1686
  const config = loadConfig();
1166
1687
  if (!config) {
1167
- log3("no config \u2014 falling through to Hermes");
1688
+ log4("no config \u2014 falling through to Hermes");
1168
1689
  return;
1169
1690
  }
1170
1691
  const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
@@ -1172,7 +1693,7 @@ async function main() {
1172
1693
  const result = await handleGrepDirect(api, config.tableName, config.sessionsTableName, grepParams);
1173
1694
  if (result === null)
1174
1695
  return;
1175
- log3(`intercepted ${command.slice(0, 80)} \u2192 ${result.length} chars from SQL fast-path`);
1696
+ log4(`intercepted ${command.slice(0, 80)} \u2192 ${result.length} chars from SQL fast-path`);
1176
1697
  const message = [
1177
1698
  result,
1178
1699
  "",
@@ -1181,10 +1702,10 @@ async function main() {
1181
1702
  process.stdout.write(JSON.stringify({ action: "block", message }));
1182
1703
  } catch (err) {
1183
1704
  const msg = err instanceof Error ? err.message : String(err);
1184
- log3(`fast-path failed, falling through: ${msg}`);
1705
+ log4(`fast-path failed, falling through: ${msg}`);
1185
1706
  }
1186
1707
  }
1187
1708
  main().catch((e) => {
1188
- log3(`fatal: ${e.message}`);
1709
+ log4(`fatal: ${e.message}`);
1189
1710
  process.exit(0);
1190
1711
  });