@deeplake/hivemind 0.7.4 → 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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +97 -0
- package/bundle/cli.js +820 -20
- package/codex/bundle/capture.js +40 -10
- package/codex/bundle/commands/auth-login.js +84 -18
- package/codex/bundle/pre-tool-use.js +41 -11
- package/codex/bundle/session-start-setup.js +40 -10
- package/codex/bundle/session-start.js +27 -3
- package/codex/bundle/shell/deeplake-shell.js +41 -11
- package/codex/bundle/skilify-worker.js +907 -0
- package/codex/bundle/stop.js +373 -51
- package/cursor/bundle/capture.js +354 -13
- package/cursor/bundle/commands/auth-login.js +84 -18
- package/cursor/bundle/pre-tool-use.js +40 -10
- package/cursor/bundle/session-end.js +303 -6
- package/cursor/bundle/session-start.js +68 -14
- package/cursor/bundle/shell/deeplake-shell.js +41 -11
- package/cursor/bundle/skilify-worker.js +907 -0
- package/hermes/bundle/capture.js +354 -13
- package/hermes/bundle/commands/auth-login.js +84 -18
- package/hermes/bundle/pre-tool-use.js +40 -10
- package/hermes/bundle/session-end.js +305 -7
- package/hermes/bundle/session-start.js +68 -14
- package/hermes/bundle/shell/deeplake-shell.js +41 -11
- package/hermes/bundle/skilify-worker.js +907 -0
- package/mcp/bundle/server.js +41 -11
- package/openclaw/dist/chunks/{config-G23NI5TV.js → config-ZLH6JFJS.js} +1 -0
- package/openclaw/dist/index.js +185 -16
- package/openclaw/dist/skilify-worker.js +907 -0
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/openclaw/skills/SKILL.md +19 -0
- package/package.json +6 -1
- package/pi/extension-source/hivemind.ts +130 -1
|
@@ -59,6 +59,7 @@ function loadConfig() {
|
|
|
59
59
|
apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
|
|
60
60
|
tableName: process.env.HIVEMIND_TABLE ?? "memory",
|
|
61
61
|
sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
|
|
62
|
+
skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
|
|
62
63
|
memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join2(home, ".deeplake", "memory")
|
|
63
64
|
};
|
|
64
65
|
}
|
|
@@ -228,6 +229,291 @@ function bundleDirFromImportMeta(importMetaUrl) {
|
|
|
228
229
|
return dirname(fileURLToPath(importMetaUrl));
|
|
229
230
|
}
|
|
230
231
|
|
|
232
|
+
// dist/src/skilify/spawn-skilify-worker.js
|
|
233
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
234
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
235
|
+
import { dirname as dirname2, join as join7 } from "node:path";
|
|
236
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, appendFileSync as appendFileSync3, chmodSync } from "node:fs";
|
|
237
|
+
import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
|
|
238
|
+
|
|
239
|
+
// dist/src/skilify/gate-runner.js
|
|
240
|
+
import { execFileSync } from "node:child_process";
|
|
241
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
242
|
+
import { homedir as homedir5 } from "node:os";
|
|
243
|
+
import { join as join6 } from "node:path";
|
|
244
|
+
function findAgentBin(agent) {
|
|
245
|
+
const which = (name) => {
|
|
246
|
+
try {
|
|
247
|
+
const out = execFileSync("which", [name], {
|
|
248
|
+
encoding: "utf-8",
|
|
249
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
250
|
+
});
|
|
251
|
+
return out.trim() || null;
|
|
252
|
+
} catch {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
switch (agent) {
|
|
257
|
+
case "claude_code":
|
|
258
|
+
return which("claude") ?? join6(homedir5(), ".claude", "local", "claude");
|
|
259
|
+
case "codex":
|
|
260
|
+
return which("codex") ?? "/usr/local/bin/codex";
|
|
261
|
+
case "cursor":
|
|
262
|
+
return which("cursor-agent") ?? "/usr/local/bin/cursor-agent";
|
|
263
|
+
case "hermes":
|
|
264
|
+
return which("hermes") ?? join6(homedir5(), ".local", "bin", "hermes");
|
|
265
|
+
case "pi":
|
|
266
|
+
return which("pi") ?? join6(homedir5(), ".local", "bin", "pi");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// dist/src/skilify/spawn-skilify-worker.js
|
|
271
|
+
var HOME2 = homedir6();
|
|
272
|
+
var SKILIFY_LOG = join7(HOME2, ".claude", "hooks", "skilify.log");
|
|
273
|
+
function skilifyLog(msg) {
|
|
274
|
+
try {
|
|
275
|
+
mkdirSync4(dirname2(SKILIFY_LOG), { recursive: true });
|
|
276
|
+
appendFileSync3(SKILIFY_LOG, `[${utcTimestamp()}] ${msg}
|
|
277
|
+
`);
|
|
278
|
+
} catch {
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function spawnSkilifyWorker(opts) {
|
|
282
|
+
const { config, cwd, projectKey, project, bundleDir, agent, scopeConfig, currentSessionId, reason } = opts;
|
|
283
|
+
const tmpDir = join7(tmpdir2(), `deeplake-skilify-${projectKey}-${Date.now()}`);
|
|
284
|
+
mkdirSync4(tmpDir, { recursive: true, mode: 448 });
|
|
285
|
+
const gateBin = findAgentBin(agent);
|
|
286
|
+
const configFile = join7(tmpDir, "config.json");
|
|
287
|
+
writeFileSync3(configFile, JSON.stringify({
|
|
288
|
+
apiUrl: config.apiUrl,
|
|
289
|
+
token: config.token,
|
|
290
|
+
orgId: config.orgId,
|
|
291
|
+
workspaceId: config.workspaceId,
|
|
292
|
+
sessionsTable: config.sessionsTableName,
|
|
293
|
+
skillsTable: config.skillsTableName,
|
|
294
|
+
userName: config.userName,
|
|
295
|
+
cwd,
|
|
296
|
+
projectKey,
|
|
297
|
+
project,
|
|
298
|
+
agent,
|
|
299
|
+
scope: scopeConfig.scope,
|
|
300
|
+
team: scopeConfig.team,
|
|
301
|
+
install: scopeConfig.install,
|
|
302
|
+
tmpDir,
|
|
303
|
+
gateBin,
|
|
304
|
+
cursorModel: process.env.HIVEMIND_CURSOR_MODEL,
|
|
305
|
+
hermesProvider: process.env.HIVEMIND_HERMES_PROVIDER,
|
|
306
|
+
hermesModel: process.env.HIVEMIND_HERMES_MODEL,
|
|
307
|
+
piProvider: process.env.HIVEMIND_PI_PROVIDER,
|
|
308
|
+
piModel: process.env.HIVEMIND_PI_MODEL,
|
|
309
|
+
skilifyLog: SKILIFY_LOG,
|
|
310
|
+
currentSessionId
|
|
311
|
+
}), { mode: 384 });
|
|
312
|
+
try {
|
|
313
|
+
chmodSync(configFile, 384);
|
|
314
|
+
} catch {
|
|
315
|
+
}
|
|
316
|
+
skilifyLog(`${reason}: spawning skilify worker for project=${project} key=${projectKey}`);
|
|
317
|
+
const workerPath = join7(bundleDir, "skilify-worker.js");
|
|
318
|
+
spawn2("nohup", ["node", workerPath, configFile], {
|
|
319
|
+
detached: true,
|
|
320
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
321
|
+
}).unref();
|
|
322
|
+
skilifyLog(`${reason}: spawned skilify worker for ${projectKey}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// dist/src/skilify/state.js
|
|
326
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, writeSync as writeSync2, mkdirSync as mkdirSync5, renameSync as renameSync2, existsSync as existsSync4, unlinkSync as unlinkSync2, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
|
|
327
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
328
|
+
import { homedir as homedir7 } from "node:os";
|
|
329
|
+
import { createHash } from "node:crypto";
|
|
330
|
+
import { join as join8, basename } from "node:path";
|
|
331
|
+
var dlog2 = (msg) => log("skilify-state", msg);
|
|
332
|
+
var STATE_DIR2 = join8(homedir7(), ".deeplake", "state", "skilify");
|
|
333
|
+
var YIELD_BUF2 = new Int32Array(new SharedArrayBuffer(4));
|
|
334
|
+
var TRIGGER_THRESHOLD = (() => {
|
|
335
|
+
const n = Number(process.env.HIVEMIND_SKILIFY_EVERY_N_TURNS ?? "");
|
|
336
|
+
return Number.isInteger(n) && n > 0 ? n : 20;
|
|
337
|
+
})();
|
|
338
|
+
function statePath(projectKey) {
|
|
339
|
+
return join8(STATE_DIR2, `${projectKey}.json`);
|
|
340
|
+
}
|
|
341
|
+
function lockPath2(projectKey) {
|
|
342
|
+
return join8(STATE_DIR2, `${projectKey}.lock`);
|
|
343
|
+
}
|
|
344
|
+
function deriveProjectKey(cwd) {
|
|
345
|
+
const project = basename(cwd) || "unknown";
|
|
346
|
+
let signature = null;
|
|
347
|
+
try {
|
|
348
|
+
signature = execSync2("git config --get remote.origin.url", {
|
|
349
|
+
cwd,
|
|
350
|
+
encoding: "utf-8",
|
|
351
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
352
|
+
}).trim() || null;
|
|
353
|
+
} catch {
|
|
354
|
+
}
|
|
355
|
+
const input = signature ?? cwd;
|
|
356
|
+
const key = createHash("sha1").update(input).digest("hex").slice(0, 16);
|
|
357
|
+
return { key, project };
|
|
358
|
+
}
|
|
359
|
+
function readState(projectKey) {
|
|
360
|
+
const p = statePath(projectKey);
|
|
361
|
+
if (!existsSync4(p))
|
|
362
|
+
return null;
|
|
363
|
+
try {
|
|
364
|
+
return JSON.parse(readFileSync3(p, "utf-8"));
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function writeState(projectKey, state) {
|
|
370
|
+
mkdirSync5(STATE_DIR2, { recursive: true });
|
|
371
|
+
const p = statePath(projectKey);
|
|
372
|
+
const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
|
|
373
|
+
writeFileSync4(tmp, JSON.stringify(state, null, 2));
|
|
374
|
+
renameSync2(tmp, p);
|
|
375
|
+
}
|
|
376
|
+
function withRmwLock(projectKey, fn) {
|
|
377
|
+
mkdirSync5(STATE_DIR2, { recursive: true });
|
|
378
|
+
const rmw = lockPath2(projectKey) + ".rmw";
|
|
379
|
+
const deadline = Date.now() + 2e3;
|
|
380
|
+
let fd = null;
|
|
381
|
+
while (fd === null) {
|
|
382
|
+
try {
|
|
383
|
+
fd = openSync2(rmw, "wx");
|
|
384
|
+
} catch (e) {
|
|
385
|
+
if (e.code !== "EEXIST")
|
|
386
|
+
throw e;
|
|
387
|
+
if (Date.now() > deadline) {
|
|
388
|
+
dlog2(`rmw lock deadline exceeded for ${projectKey}, reclaiming stale lock`);
|
|
389
|
+
try {
|
|
390
|
+
unlinkSync2(rmw);
|
|
391
|
+
} catch (unlinkErr) {
|
|
392
|
+
dlog2(`stale rmw lock unlink failed for ${projectKey}: ${unlinkErr.message}`);
|
|
393
|
+
}
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
Atomics.wait(YIELD_BUF2, 0, 0, 10);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
return fn();
|
|
401
|
+
} finally {
|
|
402
|
+
closeSync2(fd);
|
|
403
|
+
try {
|
|
404
|
+
unlinkSync2(rmw);
|
|
405
|
+
} catch (unlinkErr) {
|
|
406
|
+
dlog2(`rmw lock cleanup failed for ${projectKey}: ${unlinkErr.message}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function resetCounter(projectKey) {
|
|
411
|
+
withRmwLock(projectKey, () => {
|
|
412
|
+
const s = readState(projectKey);
|
|
413
|
+
if (!s)
|
|
414
|
+
return;
|
|
415
|
+
writeState(projectKey, { ...s, counter: 0, updatedAt: Date.now() });
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
function tryAcquireWorkerLock(projectKey, maxAgeMs = 10 * 60 * 1e3) {
|
|
419
|
+
mkdirSync5(STATE_DIR2, { recursive: true });
|
|
420
|
+
const p = lockPath2(projectKey);
|
|
421
|
+
if (existsSync4(p)) {
|
|
422
|
+
try {
|
|
423
|
+
const ageMs = Date.now() - parseInt(readFileSync3(p, "utf-8"), 10);
|
|
424
|
+
if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
|
|
425
|
+
return false;
|
|
426
|
+
} catch (readErr) {
|
|
427
|
+
dlog2(`worker lock unreadable for ${projectKey}, treating as stale: ${readErr.message}`);
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
unlinkSync2(p);
|
|
431
|
+
} catch (unlinkErr) {
|
|
432
|
+
dlog2(`could not unlink stale worker lock for ${projectKey}: ${unlinkErr.message}`);
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
try {
|
|
437
|
+
const fd = openSync2(p, "wx");
|
|
438
|
+
try {
|
|
439
|
+
writeSync2(fd, String(Date.now()));
|
|
440
|
+
} finally {
|
|
441
|
+
closeSync2(fd);
|
|
442
|
+
}
|
|
443
|
+
return true;
|
|
444
|
+
} catch {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function releaseWorkerLock(projectKey) {
|
|
449
|
+
const p = lockPath2(projectKey);
|
|
450
|
+
try {
|
|
451
|
+
unlinkSync2(p);
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// dist/src/skilify/scope-config.js
|
|
457
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
458
|
+
import { homedir as homedir8 } from "node:os";
|
|
459
|
+
import { join as join9 } from "node:path";
|
|
460
|
+
var STATE_DIR3 = join9(homedir8(), ".deeplake", "state", "skilify");
|
|
461
|
+
var CONFIG_PATH = join9(STATE_DIR3, "config.json");
|
|
462
|
+
var DEFAULT = { scope: "me", team: [], install: "project" };
|
|
463
|
+
function loadScopeConfig() {
|
|
464
|
+
if (!existsSync5(CONFIG_PATH))
|
|
465
|
+
return DEFAULT;
|
|
466
|
+
try {
|
|
467
|
+
const raw = JSON.parse(readFileSync4(CONFIG_PATH, "utf-8"));
|
|
468
|
+
const scope = raw.scope === "team" || raw.scope === "org" ? raw.scope : "me";
|
|
469
|
+
const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
|
|
470
|
+
const install = raw.install === "global" ? "global" : "project";
|
|
471
|
+
return { scope, team, install };
|
|
472
|
+
} catch {
|
|
473
|
+
return DEFAULT;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// dist/src/skilify/triggers.js
|
|
478
|
+
function forceSessionEndTrigger(opts) {
|
|
479
|
+
if (process.env.HIVEMIND_SKILIFY_WORKER === "1")
|
|
480
|
+
return;
|
|
481
|
+
if (!opts.cwd)
|
|
482
|
+
return;
|
|
483
|
+
try {
|
|
484
|
+
const { key: projectKey, project } = deriveProjectKey(opts.cwd);
|
|
485
|
+
if (!tryAcquireWorkerLock(projectKey)) {
|
|
486
|
+
skilifyLog(`SessionEnd: skilify worker already running for ${projectKey}, skipping`);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
if (readState(projectKey)) {
|
|
490
|
+
resetCounter(projectKey);
|
|
491
|
+
}
|
|
492
|
+
skilifyLog(`SessionEnd: spawning skilify worker for project=${project} agent=${opts.agent}`);
|
|
493
|
+
try {
|
|
494
|
+
spawnSkilifyWorker({
|
|
495
|
+
config: opts.config,
|
|
496
|
+
cwd: opts.cwd,
|
|
497
|
+
projectKey,
|
|
498
|
+
project,
|
|
499
|
+
bundleDir: opts.bundleDir,
|
|
500
|
+
agent: opts.agent,
|
|
501
|
+
scopeConfig: loadScopeConfig(),
|
|
502
|
+
currentSessionId: opts.sessionId,
|
|
503
|
+
reason: "SessionEnd"
|
|
504
|
+
});
|
|
505
|
+
} catch (e) {
|
|
506
|
+
skilifyLog(`SessionEnd spawn failed: ${e?.message ?? e}`);
|
|
507
|
+
try {
|
|
508
|
+
releaseWorkerLock(projectKey);
|
|
509
|
+
} catch {
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
} catch (e) {
|
|
513
|
+
skilifyLog(`SessionEnd trigger error: ${e?.message ?? e}`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
231
517
|
// dist/src/hooks/hermes/session-end.js
|
|
232
518
|
var log2 = (msg) => log("hermes-session-end", msg);
|
|
233
519
|
async function main() {
|
|
@@ -242,21 +528,33 @@ async function main() {
|
|
|
242
528
|
wikiLog(`SessionEnd: periodic worker already running for ${sessionId}, skipping final`);
|
|
243
529
|
return;
|
|
244
530
|
}
|
|
531
|
+
const config = loadConfig();
|
|
532
|
+
if (!config) {
|
|
533
|
+
wikiLog(`SessionEnd: no config, skipping summary`);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const cwd = input.cwd ?? process.cwd();
|
|
245
537
|
try {
|
|
246
|
-
const config = loadConfig();
|
|
247
|
-
if (!config) {
|
|
248
|
-
wikiLog(`SessionEnd: no config, skipping summary`);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
538
|
spawnHermesWikiWorker({
|
|
252
539
|
config,
|
|
253
540
|
sessionId,
|
|
254
|
-
cwd
|
|
541
|
+
cwd,
|
|
255
542
|
bundleDir: bundleDirFromImportMeta(import.meta.url),
|
|
256
543
|
reason: "SessionEnd"
|
|
257
544
|
});
|
|
258
545
|
} catch (e) {
|
|
259
|
-
wikiLog(`SessionEnd: spawn failed: ${e?.message ?? e}`);
|
|
546
|
+
wikiLog(`SessionEnd: wiki spawn failed: ${e?.message ?? e}`);
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
forceSessionEndTrigger({
|
|
550
|
+
config,
|
|
551
|
+
cwd,
|
|
552
|
+
bundleDir: bundleDirFromImportMeta(import.meta.url),
|
|
553
|
+
agent: "hermes",
|
|
554
|
+
sessionId
|
|
555
|
+
});
|
|
556
|
+
} catch (e) {
|
|
557
|
+
wikiLog(`SessionEnd: skilify trigger failed: ${e?.message ?? e}`);
|
|
260
558
|
}
|
|
261
559
|
}
|
|
262
560
|
main().catch((e) => {
|
|
@@ -53,7 +53,7 @@ var init_index_marker_store = __esm({
|
|
|
53
53
|
|
|
54
54
|
// dist/src/hooks/hermes/session-start.js
|
|
55
55
|
import { fileURLToPath } from "node:url";
|
|
56
|
-
import { dirname as dirname2
|
|
56
|
+
import { dirname as dirname2 } from "node:path";
|
|
57
57
|
|
|
58
58
|
// dist/src/commands/auth.js
|
|
59
59
|
import { execSync } from "node:child_process";
|
|
@@ -113,6 +113,7 @@ function loadConfig() {
|
|
|
113
113
|
apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
|
|
114
114
|
tableName: process.env.HIVEMIND_TABLE ?? "memory",
|
|
115
115
|
sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
|
|
116
|
+
skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
|
|
116
117
|
memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join2(home, ".deeplake", "memory")
|
|
117
118
|
};
|
|
118
119
|
}
|
|
@@ -137,6 +138,12 @@ function log(tag, msg) {
|
|
|
137
138
|
function sqlStr(value) {
|
|
138
139
|
return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
139
140
|
}
|
|
141
|
+
function sqlIdent(name) {
|
|
142
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
143
|
+
throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
|
|
144
|
+
}
|
|
145
|
+
return name;
|
|
146
|
+
}
|
|
140
147
|
|
|
141
148
|
// dist/src/embeddings/columns.js
|
|
142
149
|
var SUMMARY_EMBEDDING_COL = "summary_embedding";
|
|
@@ -500,7 +507,7 @@ var DeeplakeApi = class {
|
|
|
500
507
|
}
|
|
501
508
|
/** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
|
|
502
509
|
async ensureTable(name) {
|
|
503
|
-
const tbl = name ?? this.tableName;
|
|
510
|
+
const tbl = sqlIdent(name ?? this.tableName);
|
|
504
511
|
const tables = await this.listTables();
|
|
505
512
|
if (!tables.includes(tbl)) {
|
|
506
513
|
log2(`table "${tbl}" not found, creating`);
|
|
@@ -514,17 +521,40 @@ var DeeplakeApi = class {
|
|
|
514
521
|
}
|
|
515
522
|
/** Create the sessions table (uses JSONB for message since every row is a JSON event). */
|
|
516
523
|
async ensureSessionsTable(name) {
|
|
524
|
+
const safe = sqlIdent(name);
|
|
525
|
+
const tables = await this.listTables();
|
|
526
|
+
if (!tables.includes(safe)) {
|
|
527
|
+
log2(`table "${safe}" not found, creating`);
|
|
528
|
+
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
529
|
+
log2(`table "${safe}" created`);
|
|
530
|
+
if (!tables.includes(safe))
|
|
531
|
+
this._tablesCache = [...tables, safe];
|
|
532
|
+
}
|
|
533
|
+
await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
|
|
534
|
+
await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
535
|
+
await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Create the skills table.
|
|
539
|
+
*
|
|
540
|
+
* One row per skill version. Workers INSERT a fresh row on every KEEP /
|
|
541
|
+
* MERGE rather than UPDATE-ing in place, so the full version history is
|
|
542
|
+
* recoverable. Uniqueness in the *current* state is by (project_key, name)
|
|
543
|
+
* — newer rows shadow older ones at read time (ORDER BY version DESC).
|
|
544
|
+
* This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
|
|
545
|
+
* worker.
|
|
546
|
+
*/
|
|
547
|
+
async ensureSkillsTable(name) {
|
|
548
|
+
const safe = sqlIdent(name);
|
|
517
549
|
const tables = await this.listTables();
|
|
518
|
-
if (!tables.includes(
|
|
519
|
-
log2(`table "${
|
|
520
|
-
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${
|
|
521
|
-
log2(`table "${
|
|
522
|
-
if (!tables.includes(
|
|
523
|
-
this._tablesCache = [...tables,
|
|
550
|
+
if (!tables.includes(safe)) {
|
|
551
|
+
log2(`table "${safe}" not found, creating`);
|
|
552
|
+
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
553
|
+
log2(`table "${safe}" created`);
|
|
554
|
+
if (!tables.includes(safe))
|
|
555
|
+
this._tablesCache = [...tables, safe];
|
|
524
556
|
}
|
|
525
|
-
await this.
|
|
526
|
-
await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
527
|
-
await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
|
|
557
|
+
await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
|
|
528
558
|
}
|
|
529
559
|
};
|
|
530
560
|
|
|
@@ -590,14 +620,38 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
|
|
|
590
620
|
// dist/src/hooks/hermes/session-start.js
|
|
591
621
|
var log3 = (msg) => log("hermes-session-start", msg);
|
|
592
622
|
var __bundleDir = dirname2(fileURLToPath(import.meta.url));
|
|
593
|
-
var AUTH_CMD = join6(__bundleDir, "commands", "auth-login.js");
|
|
594
623
|
var context = `DEEPLAKE MEMORY: Persistent memory at ~/.deeplake/memory/ shared across sessions, users, and agents.
|
|
595
624
|
|
|
596
625
|
Structure: index.md (start here) \u2192 summaries/*.md \u2192 sessions/*.jsonl (last resort). Do NOT jump straight to JSONL.
|
|
597
626
|
Search: use \`grep\` (NOT \`rg\`/ripgrep). Example: grep -ri "keyword" ~/.deeplake/memory/
|
|
598
627
|
You also have hivemind MCP tools registered: hivemind_search, hivemind_read, hivemind_index. Prefer these \u2014 one tool call returns ranked hits across all summaries and sessions in a single SQL query.
|
|
599
628
|
IMPORTANT: Only use these bash builtins to interact with ~/.deeplake/memory/: cat, ls, grep, echo, jq, head, tail, sed, awk, wc, sort, find. Do NOT use rg/ripgrep, python, python3, node, curl, or other interpreters.
|
|
600
|
-
Do NOT spawn subagents to read deeplake memory
|
|
629
|
+
Do NOT spawn subagents to read deeplake memory.
|
|
630
|
+
|
|
631
|
+
Organization management \u2014 each argument is SEPARATE (do NOT quote subcommands together):
|
|
632
|
+
- hivemind login \u2014 SSO login
|
|
633
|
+
- hivemind whoami \u2014 show current user/org
|
|
634
|
+
- hivemind org list \u2014 list organizations
|
|
635
|
+
- hivemind org switch <name-or-id> \u2014 switch organization
|
|
636
|
+
- hivemind workspaces \u2014 list workspaces
|
|
637
|
+
- hivemind workspace <id> \u2014 switch workspace
|
|
638
|
+
- hivemind invite <email> <ADMIN|WRITE|READ> \u2014 invite member (ALWAYS ask user which role before inviting)
|
|
639
|
+
- hivemind members \u2014 list members
|
|
640
|
+
- hivemind remove <user-id> \u2014 remove member
|
|
641
|
+
|
|
642
|
+
SKILLS (skilify) \u2014 mine + share reusable skills across the org:
|
|
643
|
+
- hivemind skilify \u2014 show scope/team/install + per-project state
|
|
644
|
+
- hivemind skilify pull \u2014 sync project skills from the org table
|
|
645
|
+
- hivemind skilify pull --user <email> \u2014 only that author's skills
|
|
646
|
+
- hivemind skilify pull --users a,b,c \u2014 multiple authors (CSV)
|
|
647
|
+
- hivemind skilify pull --all-users \u2014 explicit "no author filter"
|
|
648
|
+
- hivemind skilify pull --to project|global \u2014 install location
|
|
649
|
+
- hivemind skilify pull --dry-run \u2014 preview only
|
|
650
|
+
- hivemind skilify pull --force \u2014 overwrite local (creates .bak)
|
|
651
|
+
- hivemind skilify pull <skill-name> \u2014 pull only that skill (combines with --user)
|
|
652
|
+
- hivemind skilify scope <me|team|org> \u2014 sharing scope for new skills
|
|
653
|
+
- hivemind skilify install <project|global> \u2014 default install location
|
|
654
|
+
- hivemind skilify team add|remove|list <name> \u2014 manage team list`;
|
|
601
655
|
async function createPlaceholder(api, table, sessionId, cwd, userName, orgName, workspaceId) {
|
|
602
656
|
const summaryPath = `/summaries/${userName}/${sessionId}.md`;
|
|
603
657
|
const existing = await api.query(`SELECT path FROM "${table}" WHERE path = '${sqlStr(summaryPath)}' LIMIT 1`);
|
|
@@ -646,7 +700,7 @@ async function main() {
|
|
|
646
700
|
Hivemind v${current}`;
|
|
647
701
|
const additional = creds?.token ? `${context}
|
|
648
702
|
Logged in to Deeplake as org: ${creds.orgName ?? creds.orgId} (workspace: ${creds.workspaceId ?? "default"})${versionNotice}` : `${context}
|
|
649
|
-
Not logged in to Deeplake. Run:
|
|
703
|
+
Not logged in to Deeplake. Run: hivemind login${versionNotice}`;
|
|
650
704
|
console.log(JSON.stringify({ context: additional }));
|
|
651
705
|
}
|
|
652
706
|
main().catch((e) => {
|
|
@@ -66791,6 +66791,7 @@ function loadConfig() {
|
|
|
66791
66791
|
apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
|
|
66792
66792
|
tableName: process.env.HIVEMIND_TABLE ?? "memory",
|
|
66793
66793
|
sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
|
|
66794
|
+
skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
|
|
66794
66795
|
memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join4(home, ".deeplake", "memory")
|
|
66795
66796
|
};
|
|
66796
66797
|
}
|
|
@@ -66818,6 +66819,12 @@ function sqlStr(value) {
|
|
|
66818
66819
|
function sqlLike(value) {
|
|
66819
66820
|
return sqlStr(value).replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
66820
66821
|
}
|
|
66822
|
+
function sqlIdent(name) {
|
|
66823
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
66824
|
+
throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
|
|
66825
|
+
}
|
|
66826
|
+
return name;
|
|
66827
|
+
}
|
|
66821
66828
|
|
|
66822
66829
|
// dist/src/embeddings/columns.js
|
|
66823
66830
|
var SUMMARY_EMBEDDING_COL = "summary_embedding";
|
|
@@ -67190,7 +67197,7 @@ var DeeplakeApi = class {
|
|
|
67190
67197
|
}
|
|
67191
67198
|
/** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
|
|
67192
67199
|
async ensureTable(name) {
|
|
67193
|
-
const tbl = name ?? this.tableName;
|
|
67200
|
+
const tbl = sqlIdent(name ?? this.tableName);
|
|
67194
67201
|
const tables = await this.listTables();
|
|
67195
67202
|
if (!tables.includes(tbl)) {
|
|
67196
67203
|
log2(`table "${tbl}" not found, creating`);
|
|
@@ -67204,17 +67211,40 @@ var DeeplakeApi = class {
|
|
|
67204
67211
|
}
|
|
67205
67212
|
/** Create the sessions table (uses JSONB for message since every row is a JSON event). */
|
|
67206
67213
|
async ensureSessionsTable(name) {
|
|
67214
|
+
const safe = sqlIdent(name);
|
|
67215
|
+
const tables = await this.listTables();
|
|
67216
|
+
if (!tables.includes(safe)) {
|
|
67217
|
+
log2(`table "${safe}" not found, creating`);
|
|
67218
|
+
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
67219
|
+
log2(`table "${safe}" created`);
|
|
67220
|
+
if (!tables.includes(safe))
|
|
67221
|
+
this._tablesCache = [...tables, safe];
|
|
67222
|
+
}
|
|
67223
|
+
await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
|
|
67224
|
+
await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
67225
|
+
await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
|
|
67226
|
+
}
|
|
67227
|
+
/**
|
|
67228
|
+
* Create the skills table.
|
|
67229
|
+
*
|
|
67230
|
+
* One row per skill version. Workers INSERT a fresh row on every KEEP /
|
|
67231
|
+
* MERGE rather than UPDATE-ing in place, so the full version history is
|
|
67232
|
+
* recoverable. Uniqueness in the *current* state is by (project_key, name)
|
|
67233
|
+
* — newer rows shadow older ones at read time (ORDER BY version DESC).
|
|
67234
|
+
* This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
|
|
67235
|
+
* worker.
|
|
67236
|
+
*/
|
|
67237
|
+
async ensureSkillsTable(name) {
|
|
67238
|
+
const safe = sqlIdent(name);
|
|
67207
67239
|
const tables = await this.listTables();
|
|
67208
|
-
if (!tables.includes(
|
|
67209
|
-
log2(`table "${
|
|
67210
|
-
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${
|
|
67211
|
-
log2(`table "${
|
|
67212
|
-
if (!tables.includes(
|
|
67213
|
-
this._tablesCache = [...tables,
|
|
67214
|
-
}
|
|
67215
|
-
await this.
|
|
67216
|
-
await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
67217
|
-
await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
|
|
67240
|
+
if (!tables.includes(safe)) {
|
|
67241
|
+
log2(`table "${safe}" not found, creating`);
|
|
67242
|
+
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);
|
|
67243
|
+
log2(`table "${safe}" created`);
|
|
67244
|
+
if (!tables.includes(safe))
|
|
67245
|
+
this._tablesCache = [...tables, safe];
|
|
67246
|
+
}
|
|
67247
|
+
await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
|
|
67218
67248
|
}
|
|
67219
67249
|
};
|
|
67220
67250
|
|