@deeplake/hivemind 0.7.34 → 0.7.36

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.
@@ -4,6 +4,10 @@ import {
4
4
  saveCredentials
5
5
  } from "./chunks/chunk-OSD5GJJ5.js";
6
6
 
7
+ // stub:node:child_process
8
+ var spawn = () => {
9
+ };
10
+
7
11
  // src/utils/client-header.ts
8
12
  var DEEPLAKE_CLIENT_HEADER = "X-Deeplake-Client";
9
13
  function deeplakeClientValue() {
@@ -102,7 +106,121 @@ function sqlIdent(name) {
102
106
 
103
107
  // src/embeddings/columns.ts
104
108
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
105
- var MESSAGE_EMBEDDING_COL = "message_embedding";
109
+
110
+ // src/deeplake-schema.ts
111
+ var MEMORY_COLUMNS = Object.freeze([
112
+ { name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
113
+ { name: "path", sql: "TEXT NOT NULL DEFAULT ''" },
114
+ { name: "filename", sql: "TEXT NOT NULL DEFAULT ''" },
115
+ { name: "summary", sql: "TEXT NOT NULL DEFAULT ''" },
116
+ { name: "summary_embedding", sql: "FLOAT4[]" },
117
+ { name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
118
+ { name: "mime_type", sql: "TEXT NOT NULL DEFAULT 'text/plain'" },
119
+ { name: "size_bytes", sql: "BIGINT NOT NULL DEFAULT 0" },
120
+ { name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
121
+ { name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
122
+ { name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
123
+ { name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" },
124
+ { name: "creation_date", sql: "TEXT NOT NULL DEFAULT ''" },
125
+ { name: "last_update_date", sql: "TEXT NOT NULL DEFAULT ''" }
126
+ ]);
127
+ var SESSIONS_COLUMNS = Object.freeze([
128
+ { name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
129
+ { name: "path", sql: "TEXT NOT NULL DEFAULT ''" },
130
+ { name: "filename", sql: "TEXT NOT NULL DEFAULT ''" },
131
+ { name: "message", sql: "JSONB" },
132
+ { name: "message_embedding", sql: "FLOAT4[]" },
133
+ { name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
134
+ { name: "mime_type", sql: "TEXT NOT NULL DEFAULT 'application/json'" },
135
+ { name: "size_bytes", sql: "BIGINT NOT NULL DEFAULT 0" },
136
+ { name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
137
+ { name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
138
+ { name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
139
+ { name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" },
140
+ { name: "creation_date", sql: "TEXT NOT NULL DEFAULT ''" },
141
+ { name: "last_update_date", sql: "TEXT NOT NULL DEFAULT ''" }
142
+ ]);
143
+ var SKILLS_COLUMNS = Object.freeze([
144
+ { name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
145
+ { name: "name", sql: "TEXT NOT NULL DEFAULT ''" },
146
+ { name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
147
+ { name: "project_key", sql: "TEXT NOT NULL DEFAULT ''" },
148
+ { name: "local_path", sql: "TEXT NOT NULL DEFAULT ''" },
149
+ { name: "install", sql: "TEXT NOT NULL DEFAULT 'project'" },
150
+ { name: "source_sessions", sql: "TEXT NOT NULL DEFAULT '[]'" },
151
+ { name: "source_agent", sql: "TEXT NOT NULL DEFAULT ''" },
152
+ { name: "scope", sql: "TEXT NOT NULL DEFAULT 'me'" },
153
+ { name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
154
+ { name: "contributors", sql: "TEXT NOT NULL DEFAULT '[]'" },
155
+ { name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
156
+ { name: "trigger_text", sql: "TEXT NOT NULL DEFAULT ''" },
157
+ { name: "body", sql: "TEXT NOT NULL DEFAULT ''" },
158
+ { name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
159
+ { name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
160
+ { name: "updated_at", sql: "TEXT NOT NULL DEFAULT ''" }
161
+ ]);
162
+ function validateSchema(label, cols) {
163
+ const seen = /* @__PURE__ */ new Set();
164
+ for (const col of cols) {
165
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(col.name)) {
166
+ throw new Error(`${label}: column name "${col.name}" is not a valid SQL identifier`);
167
+ }
168
+ if (seen.has(col.name)) {
169
+ throw new Error(`${label}: duplicate column "${col.name}"`);
170
+ }
171
+ seen.add(col.name);
172
+ const notNull = /\bNOT\s+NULL\b/i.test(col.sql);
173
+ const hasDefault = /\bDEFAULT\b/i.test(col.sql);
174
+ if (notNull && !hasDefault) {
175
+ throw new Error(
176
+ `${label}: column "${col.name}" is NOT NULL but has no DEFAULT \u2014 ALTER TABLE ADD COLUMN on a populated table would fail.`
177
+ );
178
+ }
179
+ }
180
+ }
181
+ validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
182
+ validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
183
+ validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
184
+ function buildCreateTableSql(tableName, cols) {
185
+ const safe = sqlIdent(tableName);
186
+ const colSql = cols.map((c) => `${c.name} ${c.sql}`).join(", ");
187
+ return `CREATE TABLE IF NOT EXISTS "${safe}" (${colSql}) USING deeplake`;
188
+ }
189
+ function buildIntrospectionSql(tableName, workspaceId) {
190
+ return `SELECT column_name FROM information_schema.columns WHERE table_name = '${sqlStr(tableName)}' AND table_schema = '${sqlStr(workspaceId)}'`;
191
+ }
192
+ async function healMissingColumns(args) {
193
+ const safeTable = sqlIdent(args.tableName);
194
+ const introspectSql = buildIntrospectionSql(args.tableName, args.workspaceId);
195
+ const rows = await args.query(introspectSql);
196
+ const existing = /* @__PURE__ */ new Set();
197
+ for (const row of rows) {
198
+ const v = row?.column_name;
199
+ if (typeof v === "string") existing.add(v.toLowerCase());
200
+ }
201
+ const missingCols = args.columns.filter((c) => !existing.has(c.name.toLowerCase()));
202
+ const missing = missingCols.map((c) => c.name);
203
+ if (missingCols.length === 0) return { missing, altered: [] };
204
+ const altered = [];
205
+ for (const col of missingCols) {
206
+ try {
207
+ await args.query(`ALTER TABLE "${safeTable}" ADD COLUMN ${col.name} ${col.sql}`);
208
+ altered.push(col.name);
209
+ args.log?.(`schema-heal: added "${args.tableName}"."${col.name}"`);
210
+ } catch (e) {
211
+ const msg = e instanceof Error ? e.message : String(e);
212
+ if (!/already exists/i.test(msg)) throw e;
213
+ const recheck = await args.query(introspectSql);
214
+ const present = recheck.some((r) => {
215
+ const v = r?.column_name;
216
+ return typeof v === "string" && v.toLowerCase() === col.name.toLowerCase();
217
+ });
218
+ if (!present) throw e;
219
+ args.log?.(`schema-heal: "${args.tableName}"."${col.name}" appeared via race, treating as success`);
220
+ }
221
+ }
222
+ return { missing, altered };
223
+ }
106
224
 
107
225
  // src/notifications/queue.ts
108
226
  import { readFileSync, writeFileSync, renameSync, mkdirSync, openSync, closeSync, unlinkSync, statSync } from "node:fs";
@@ -465,61 +583,33 @@ var DeeplakeApi = class {
465
583
  }
466
584
  }
467
585
  /**
468
- * Ensure a vector column exists on the given table.
586
+ * Heal any missing columns on a table so it matches one of the schema
587
+ * definitions in `deeplake-schema.ts`. One SELECT against
588
+ * `information_schema.columns` per call, then `ALTER TABLE ADD COLUMN`
589
+ * only the genuinely missing ones — never blanket, never `IF NOT
590
+ * EXISTS`.
469
591
  *
470
- * The previous implementation always issued `ALTER TABLE ADD COLUMN IF NOT
471
- * EXISTS …` on every SessionStart. On a long-running workspace that's
472
- * already migrated, every call returns 500 "Column already exists" — noisy
473
- * in the log and a wasted round-trip. Worse, the very first call after the
474
- * column is genuinely added triggers Deeplake's post-ALTER `vector::at`
475
- * window (~30s) during which subsequent INSERTs fail; minimising the
476
- * number of ALTER calls minimises exposure to that window.
477
- *
478
- * New flow:
479
- * 1. Check the local marker file (mirrors ensureLookupIndex). If fresh,
480
- * return zero network calls.
481
- * 2. SELECT 1 FROM information_schema.columns WHERE table_name = T AND
482
- * column_name = C. Read-only, idempotent, can't tickle the post-ALTER
483
- * bug. If the column is present → mark + return.
484
- * 3. Only if step 2 says the column is missing, fall back to ALTER ADD
485
- * COLUMN IF NOT EXISTS. Mark on success, also mark if Deeplake reports
486
- * "already exists" (race: another client added it between our SELECT
487
- * and ALTER).
488
- *
489
- * Marker uses the same dir / TTL as ensureLookupIndex so both schema
490
- * caches share an opt-out (HIVEMIND_INDEX_MARKER_DIR) and a TTL knob.
592
+ * History: an earlier path used a local marker file (`col_<name>` under
593
+ * the index-marker dir) to skip even the SELECT after the first
594
+ * confirmation, plus per-column ALTERs for `summary_embedding`,
595
+ * `message_embedding`, `agent`, `plugin_version`. The marker existed
596
+ * because Deeplake used to expose a ~30s post-ALTER bug where
597
+ * subsequent INSERTs failed, so we wanted to keep ALTER traffic to a
598
+ * minimum. The bug was re-verified on 2026-05-18 against
599
+ * `api.deeplake.ai` (`test_plugin` org) and no longer reproduces
600
+ * (71/71 INSERTs OK, first success 2ms after ALTER). The single SELECT
601
+ * + targeted ALTER pattern survives the marker removal because: each
602
+ * ALTER still costs ~800ms (so blanket sweeps are wasteful) and the
603
+ * diff produces clearer logs than "ALTER all with IF NOT EXISTS".
491
604
  */
492
- async ensureEmbeddingColumn(table, column) {
493
- await this.ensureColumn(table, column, "FLOAT4[]");
494
- }
495
- /**
496
- * Generic marker-gated column migration. Same SELECT-then-ALTER flow as
497
- * ensureEmbeddingColumn, parameterized by SQL type so it can patch up any
498
- * column that was added to the schema after the table was originally
499
- * created. Used today for `summary_embedding`, `message_embedding`, and
500
- * the `agent` column (added 2026-04-11) — the latter has no fallback if
501
- * a user upgraded over a pre-2026-04-11 table, so every INSERT fails
502
- * with `column "agent" does not exist`.
503
- */
504
- async ensureColumn(table, column, sqlType) {
505
- const markers = await getIndexMarkerStore();
506
- const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, `col_${column}`);
507
- if (markers.hasFreshIndexMarker(markerPath)) return;
508
- 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`;
509
- const rows = await this.query(colCheck);
510
- if (rows.length > 0) {
511
- markers.writeIndexMarker(markerPath);
512
- return;
513
- }
514
- try {
515
- await this.query(`ALTER TABLE "${table}" ADD COLUMN ${column} ${sqlType}`);
516
- } catch (e) {
517
- const msg = e instanceof Error ? e.message : String(e);
518
- if (!/already exists/i.test(msg)) throw e;
519
- const recheck = await this.query(colCheck);
520
- if (recheck.length === 0) throw e;
521
- }
522
- markers.writeIndexMarker(markerPath);
605
+ async healSchema(table, columns) {
606
+ await healMissingColumns({
607
+ query: (sql) => this.query(sql),
608
+ tableName: table,
609
+ workspaceId: this.workspaceId,
610
+ columns,
611
+ log: log3
612
+ });
523
613
  }
524
614
  /** List all tables in the workspace (with retry). */
525
615
  async listTables(forceRefresh = false) {
@@ -588,22 +678,20 @@ var DeeplakeApi = class {
588
678
  }
589
679
  throw lastErr;
590
680
  }
591
- /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
681
+ /** Create the memory table if it doesn't already exist. Heal missing columns on existing tables. */
592
682
  async ensureTable(name) {
683
+ if (!MEMORY_COLUMNS.some((c) => c.name === SUMMARY_EMBEDDING_COL)) {
684
+ throw new Error(`MEMORY_COLUMNS missing "${SUMMARY_EMBEDDING_COL}" (embeddings/columns.ts drift)`);
685
+ }
593
686
  const tbl = sqlIdent(name ?? this.tableName);
594
687
  const tables = await this.listTables();
595
688
  if (!tables.includes(tbl)) {
596
689
  log3(`table "${tbl}" not found, creating`);
597
- await this.createTableWithRetry(
598
- `CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`,
599
- tbl
600
- );
690
+ await this.createTableWithRetry(buildCreateTableSql(tbl, MEMORY_COLUMNS), tbl);
601
691
  log3(`table "${tbl}" created`);
602
692
  if (!tables.includes(tbl)) this._tablesCache = [...tables, tbl];
603
693
  }
604
- await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
605
- await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
606
- await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
694
+ await this.healSchema(tbl, MEMORY_COLUMNS);
607
695
  }
608
696
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
609
697
  async ensureSessionsTable(name) {
@@ -611,16 +699,11 @@ var DeeplakeApi = class {
611
699
  const tables = await this.listTables();
612
700
  if (!tables.includes(safe)) {
613
701
  log3(`table "${safe}" not found, creating`);
614
- await this.createTableWithRetry(
615
- `CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`,
616
- safe
617
- );
702
+ await this.createTableWithRetry(buildCreateTableSql(safe, SESSIONS_COLUMNS), safe);
618
703
  log3(`table "${safe}" created`);
619
704
  if (!tables.includes(safe)) this._tablesCache = [...tables, safe];
620
705
  }
621
- await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
622
- await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
623
- await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
706
+ await this.healSchema(safe, SESSIONS_COLUMNS);
624
707
  await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
625
708
  }
626
709
  /**
@@ -638,13 +721,11 @@ var DeeplakeApi = class {
638
721
  const tables = await this.listTables();
639
722
  if (!tables.includes(safe)) {
640
723
  log3(`table "${safe}" not found, creating`);
641
- await this.createTableWithRetry(
642
- `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`,
643
- safe
644
- );
724
+ await this.createTableWithRetry(buildCreateTableSql(safe, SKILLS_COLUMNS), safe);
645
725
  log3(`table "${safe}" created`);
646
726
  if (!tables.includes(safe)) this._tablesCache = [...tables, safe];
647
727
  }
728
+ await this.healSchema(safe, SKILLS_COLUMNS);
648
729
  await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
649
730
  }
650
731
  };
@@ -1153,10 +1234,232 @@ async function readVirtualPathContent(api2, memoryTable2, sessionsTable2, virtua
1153
1234
  return (await readVirtualPathContents(api2, memoryTable2, sessionsTable2, [virtualPath])).get(virtualPath) ?? null;
1154
1235
  }
1155
1236
 
1237
+ // src/embeddings/standalone-embed-client.ts
1238
+ import { connect } from "node:net";
1239
+ import {
1240
+ openSync as openSync2,
1241
+ closeSync as closeSync2,
1242
+ writeSync,
1243
+ unlinkSync as unlinkSync2,
1244
+ existsSync,
1245
+ readFileSync as readFileSync2
1246
+ } from "node:fs";
1247
+ import { homedir as homedir3 } from "node:os";
1248
+ import { join as join3 } from "node:path";
1249
+
1250
+ // src/embeddings/protocol.ts
1251
+ var DEFAULT_SOCKET_DIR = "/tmp";
1252
+ var DEFAULT_IDLE_TIMEOUT_MS = 10 * 60 * 1e3;
1253
+ var DEFAULT_CLIENT_TIMEOUT_MS = 2e3;
1254
+ function socketPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
1255
+ return `${dir}/hivemind-embed-${uid}.sock`;
1256
+ }
1257
+ function pidPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
1258
+ return `${dir}/hivemind-embed-${uid}.pid`;
1259
+ }
1260
+
1261
+ // src/embeddings/standalone-embed-client.ts
1262
+ var SHARED_DAEMON_PATH = join3(homedir3(), ".hivemind", "embed-deps", "embed-daemon.js");
1263
+ var _spawn = spawn;
1264
+ function getUid() {
1265
+ const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
1266
+ return uid !== void 0 ? String(uid) : "default";
1267
+ }
1268
+ function isPidAlive(pid) {
1269
+ if (!Number.isFinite(pid) || pid <= 0) return false;
1270
+ try {
1271
+ process.kill(pid, 0);
1272
+ return true;
1273
+ } catch {
1274
+ return false;
1275
+ }
1276
+ }
1277
+ function readPidFile(path) {
1278
+ let raw;
1279
+ try {
1280
+ raw = readFileSync2(path, "utf-8").trim();
1281
+ } catch {
1282
+ return null;
1283
+ }
1284
+ if (raw === "") return "empty";
1285
+ const pid = Number(raw);
1286
+ if (!pid || Number.isNaN(pid)) return null;
1287
+ return pid;
1288
+ }
1289
+ function connectOnce(socketPath, timeoutMs) {
1290
+ return new Promise((resolve2, reject) => {
1291
+ const sock = connect(socketPath);
1292
+ const to = setTimeout(() => {
1293
+ sock.destroy();
1294
+ reject(new Error("connect timeout"));
1295
+ }, timeoutMs);
1296
+ sock.once("connect", () => {
1297
+ clearTimeout(to);
1298
+ resolve2(sock);
1299
+ });
1300
+ sock.once("error", (e) => {
1301
+ clearTimeout(to);
1302
+ reject(e);
1303
+ });
1304
+ });
1305
+ }
1306
+ function sendAndWait(sock, req, timeoutMs) {
1307
+ return new Promise((resolve2, reject) => {
1308
+ let buf = "";
1309
+ const to = setTimeout(() => {
1310
+ sock.destroy();
1311
+ reject(new Error("request timeout"));
1312
+ }, timeoutMs);
1313
+ sock.setEncoding("utf-8");
1314
+ sock.on("data", (chunk) => {
1315
+ buf += chunk;
1316
+ const nl = buf.indexOf("\n");
1317
+ if (nl === -1) return;
1318
+ clearTimeout(to);
1319
+ try {
1320
+ resolve2(JSON.parse(buf.slice(0, nl)));
1321
+ } catch (e) {
1322
+ reject(e);
1323
+ }
1324
+ });
1325
+ sock.on("error", (e) => {
1326
+ clearTimeout(to);
1327
+ reject(e);
1328
+ });
1329
+ sock.on("end", () => {
1330
+ clearTimeout(to);
1331
+ reject(new Error("connection closed without response"));
1332
+ });
1333
+ sock.write(JSON.stringify(req) + "\n");
1334
+ });
1335
+ }
1336
+ function trySpawnDaemon(daemonEntry, pidPath) {
1337
+ let fd;
1338
+ try {
1339
+ fd = openSync2(pidPath, "wx", 384);
1340
+ writeSync(fd, String(process.pid));
1341
+ } catch {
1342
+ const existing = readPidFile(pidPath);
1343
+ if (existing === "empty") return false;
1344
+ if (existing !== null && isPidAlive(existing)) {
1345
+ return false;
1346
+ }
1347
+ try {
1348
+ unlinkSync2(pidPath);
1349
+ } catch {
1350
+ }
1351
+ try {
1352
+ fd = openSync2(pidPath, "wx", 384);
1353
+ writeSync(fd, String(process.pid));
1354
+ } catch {
1355
+ return false;
1356
+ }
1357
+ }
1358
+ try {
1359
+ const child = _spawn(process.execPath, [daemonEntry], {
1360
+ detached: true,
1361
+ stdio: "ignore"
1362
+ });
1363
+ child.unref();
1364
+ return true;
1365
+ } catch {
1366
+ try {
1367
+ unlinkSync2(pidPath);
1368
+ } catch {
1369
+ }
1370
+ return false;
1371
+ } finally {
1372
+ try {
1373
+ closeSync2(fd);
1374
+ } catch {
1375
+ }
1376
+ }
1377
+ }
1378
+ function maybeCleanupOwnPlaceholder(pidPath) {
1379
+ const existing = readPidFile(pidPath);
1380
+ if (existing === process.pid || existing === "empty") {
1381
+ try {
1382
+ unlinkSync2(pidPath);
1383
+ } catch {
1384
+ }
1385
+ }
1386
+ }
1387
+ async function waitForSocket(socketPath, deadline, connectTimeoutMs) {
1388
+ let delay = 30;
1389
+ while (Date.now() < deadline) {
1390
+ await new Promise((r) => setTimeout(r, delay));
1391
+ delay = Math.min(delay * 1.5, 300);
1392
+ if (!existsSync(socketPath)) continue;
1393
+ try {
1394
+ return await connectOnce(socketPath, connectTimeoutMs);
1395
+ } catch {
1396
+ }
1397
+ }
1398
+ return null;
1399
+ }
1400
+ async function tryEmbedStandalone(text, kind, opts = {}) {
1401
+ const socketDir = opts.socketDir ?? "/tmp";
1402
+ const daemonEntry = opts.daemonEntry ?? SHARED_DAEMON_PATH;
1403
+ const requestTimeoutMs = opts.requestTimeoutMs ?? DEFAULT_CLIENT_TIMEOUT_MS;
1404
+ const spawnWaitMs = opts.spawnWaitMs ?? 5e3;
1405
+ const uid = getUid();
1406
+ const socketPath = socketPathFor(uid, socketDir);
1407
+ const pidPath = pidPathFor(uid, socketDir);
1408
+ let sock = null;
1409
+ try {
1410
+ sock = await connectOnce(socketPath, requestTimeoutMs);
1411
+ } catch {
1412
+ }
1413
+ if (!sock) {
1414
+ if (!existsSync(daemonEntry)) {
1415
+ return null;
1416
+ }
1417
+ trySpawnDaemon(daemonEntry, pidPath);
1418
+ const deadline = Date.now() + spawnWaitMs;
1419
+ sock = await waitForSocket(socketPath, deadline, requestTimeoutMs);
1420
+ if (!sock) {
1421
+ maybeCleanupOwnPlaceholder(pidPath);
1422
+ return null;
1423
+ }
1424
+ }
1425
+ try {
1426
+ const req = { op: "embed", id: "1", kind, text };
1427
+ const resp = await sendAndWait(sock, req, requestTimeoutMs);
1428
+ if (resp.error || !resp.embedding || !Array.isArray(resp.embedding)) {
1429
+ return null;
1430
+ }
1431
+ for (const v of resp.embedding) {
1432
+ if (typeof v !== "number" || !Number.isFinite(v)) return null;
1433
+ }
1434
+ return resp.embedding;
1435
+ } catch {
1436
+ return null;
1437
+ } finally {
1438
+ try {
1439
+ sock.end();
1440
+ } catch {
1441
+ }
1442
+ }
1443
+ }
1444
+ function _setSpawnImpl(fn) {
1445
+ _spawn = fn ?? spawn;
1446
+ }
1447
+
1448
+ // src/embeddings/sql.ts
1449
+ function embeddingSqlLiteral(vec) {
1450
+ if (!vec || vec.length === 0) return "NULL";
1451
+ const parts = [];
1452
+ for (const v of vec) {
1453
+ if (!Number.isFinite(v)) return "NULL";
1454
+ parts.push(String(v));
1455
+ }
1456
+ return `ARRAY[${parts.join(",")}]::float4[]`;
1457
+ }
1458
+
1156
1459
  // openclaw/src/index.ts
1157
1460
  import { fileURLToPath } from "node:url";
1158
1461
  import { join as joinPath, dirname as dirnamePath } from "node:path";
1159
- import { homedir as homedir3, tmpdir } from "node:os";
1462
+ import { homedir as homedir4, tmpdir } from "node:os";
1160
1463
  import {
1161
1464
  existsSync as fsExists,
1162
1465
  mkdirSync as fsMkdir,
@@ -1202,6 +1505,7 @@ async function loadConfig() {
1202
1505
  }
1203
1506
  var requireFromOpenclaw = createRequire(import.meta.url);
1204
1507
  var { spawn: realSpawn, execFileSync: realExecFileSync } = requireFromOpenclaw("node:child_process");
1508
+ _setSpawnImpl(realSpawn);
1205
1509
  var inheritedEnv = process;
1206
1510
  function applyOpenclawTuning(pluginConfig) {
1207
1511
  const cfg = pluginConfig ?? {};
@@ -1239,7 +1543,7 @@ function extractLatestVersion(body) {
1239
1543
  return typeof v === "string" && v.length > 0 ? v : null;
1240
1544
  }
1241
1545
  function getInstalledVersion() {
1242
- return "0.7.34".length > 0 ? "0.7.34" : null;
1546
+ return "0.7.36".length > 0 ? "0.7.36" : null;
1243
1547
  }
1244
1548
  function isNewer(latest, current) {
1245
1549
  const parse = (v) => v.replace(/-.*$/, "").split(".").map(Number);
@@ -1346,8 +1650,8 @@ var skillifySpawnedFor = /* @__PURE__ */ new Set();
1346
1650
  var __openclaw_filename = fileURLToPath(import.meta.url);
1347
1651
  var __openclaw_dirname = dirnamePath(__openclaw_filename);
1348
1652
  var OPENCLAW_SKILLIFY_WORKER_PATH = joinPath(__openclaw_dirname, "skillify-worker.js");
1349
- var OPENCLAW_SKILLIFY_STATE_DIR = joinPath(homedir3(), ".deeplake", "state", "skillify");
1350
- var OPENCLAW_SKILLIFY_LEGACY_STATE_DIR = joinPath(homedir3(), ".deeplake", "state", "skilify");
1653
+ var OPENCLAW_SKILLIFY_STATE_DIR = joinPath(homedir4(), ".deeplake", "state", "skillify");
1654
+ var OPENCLAW_SKILLIFY_LEGACY_STATE_DIR = joinPath(homedir4(), ".deeplake", "state", "skilify");
1351
1655
  var openclawSkillifyMigrationAttempted = false;
1352
1656
  function migrateOpenclawSkillifyLegacyStateDir() {
1353
1657
  if (openclawSkillifyMigrationAttempted) return;
@@ -1463,7 +1767,7 @@ function spawnOpenclawSkillifyWorker(a) {
1463
1767
  sessionsTable,
1464
1768
  skillsTable,
1465
1769
  userName: a.userName,
1466
- cwd: homedir3(),
1770
+ cwd: homedir4(),
1467
1771
  // sentinel — only used by worker if install=project
1468
1772
  projectKey,
1469
1773
  project,
@@ -1479,7 +1783,7 @@ function spawnOpenclawSkillifyWorker(a) {
1479
1783
  cursorModel: void 0,
1480
1784
  hermesProvider: void 0,
1481
1785
  hermesModel: void 0,
1482
- skillifyLog: joinPath(homedir3(), ".deeplake", "hivemind-openclaw-skillify.log"),
1786
+ skillifyLog: joinPath(homedir4(), ".deeplake", "hivemind-openclaw-skillify.log"),
1483
1787
  currentSessionId: a.sessionId,
1484
1788
  // Pass the tuning dispatch through so the worker can repopulate its
1485
1789
  // own globalThis (each process has its own globalThis). The worker
@@ -2018,7 +2322,9 @@ One brain for every agent on your team.
2018
2322
  };
2019
2323
  const line = JSON.stringify(entry);
2020
2324
  const jsonForSql = line.replace(/'/g, "''");
2021
- const insertSql = `INSERT INTO "${sessionsTable}" (id, path, filename, message, author, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(sessionPath)}', '${sqlStr(filename)}', '${jsonForSql}'::jsonb, '${sqlStr(cfg.userName)}', ${Buffer.byteLength(line, "utf-8")}, '${sqlStr(projectName)}', '${sqlStr(msg.role)}', 'openclaw', '${sqlStr(getInstalledVersion() ?? "")}', '${ts}', '${ts}')`;
2325
+ const embedding = await tryEmbedStandalone(line, "document");
2326
+ const embeddingSql = embeddingSqlLiteral(embedding);
2327
+ const insertSql = `INSERT INTO "${sessionsTable}" (id, path, filename, message, message_embedding, author, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(sessionPath)}', '${sqlStr(filename)}', '${jsonForSql}'::jsonb, ${embeddingSql}, '${sqlStr(cfg.userName)}', ${Buffer.byteLength(line, "utf-8")}, '${sqlStr(projectName)}', '${sqlStr(msg.role)}', 'openclaw', '${sqlStr(getInstalledVersion() ?? "")}', '${ts}', '${ts}')`;
2022
2328
  try {
2023
2329
  await dl.query(insertSql);
2024
2330
  } catch (e) {