@deeplake/hivemind 0.7.4 → 0.7.11

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 (35) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +97 -0
  4. package/bundle/cli.js +820 -20
  5. package/codex/bundle/capture.js +40 -10
  6. package/codex/bundle/commands/auth-login.js +84 -18
  7. package/codex/bundle/pre-tool-use.js +41 -11
  8. package/codex/bundle/session-start-setup.js +40 -10
  9. package/codex/bundle/session-start.js +27 -3
  10. package/codex/bundle/shell/deeplake-shell.js +41 -11
  11. package/codex/bundle/skilify-worker.js +907 -0
  12. package/codex/bundle/stop.js +373 -51
  13. package/cursor/bundle/capture.js +354 -13
  14. package/cursor/bundle/commands/auth-login.js +84 -18
  15. package/cursor/bundle/pre-tool-use.js +40 -10
  16. package/cursor/bundle/session-end.js +303 -6
  17. package/cursor/bundle/session-start.js +68 -14
  18. package/cursor/bundle/shell/deeplake-shell.js +41 -11
  19. package/cursor/bundle/skilify-worker.js +907 -0
  20. package/hermes/bundle/capture.js +354 -13
  21. package/hermes/bundle/commands/auth-login.js +84 -18
  22. package/hermes/bundle/pre-tool-use.js +40 -10
  23. package/hermes/bundle/session-end.js +305 -7
  24. package/hermes/bundle/session-start.js +68 -14
  25. package/hermes/bundle/shell/deeplake-shell.js +41 -11
  26. package/hermes/bundle/skilify-worker.js +907 -0
  27. package/mcp/bundle/server.js +41 -11
  28. package/openclaw/dist/chunks/{config-G23NI5TV.js → config-ZLH6JFJS.js} +1 -0
  29. package/openclaw/dist/index.js +185 -16
  30. package/openclaw/dist/skilify-worker.js +907 -0
  31. package/openclaw/openclaw.plugin.json +1 -1
  32. package/openclaw/package.json +2 -2
  33. package/openclaw/skills/SKILL.md +19 -0
  34. package/package.json +6 -1
  35. package/pi/extension-source/hivemind.ts +130 -1
@@ -23323,6 +23323,7 @@ function loadConfig() {
23323
23323
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
23324
23324
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
23325
23325
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
23326
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
23326
23327
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join2(home, ".deeplake", "memory")
23327
23328
  };
23328
23329
  }
@@ -23350,6 +23351,12 @@ function sqlStr(value) {
23350
23351
  function sqlLike(value) {
23351
23352
  return sqlStr(value).replace(/%/g, "\\%").replace(/_/g, "\\_");
23352
23353
  }
23354
+ function sqlIdent(name) {
23355
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
23356
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
23357
+ }
23358
+ return name;
23359
+ }
23353
23360
 
23354
23361
  // dist/src/embeddings/columns.js
23355
23362
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
@@ -23713,7 +23720,7 @@ var DeeplakeApi = class {
23713
23720
  }
23714
23721
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
23715
23722
  async ensureTable(name) {
23716
- const tbl = name ?? this.tableName;
23723
+ const tbl = sqlIdent(name ?? this.tableName);
23717
23724
  const tables = await this.listTables();
23718
23725
  if (!tables.includes(tbl)) {
23719
23726
  log2(`table "${tbl}" not found, creating`);
@@ -23727,17 +23734,40 @@ var DeeplakeApi = class {
23727
23734
  }
23728
23735
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
23729
23736
  async ensureSessionsTable(name) {
23737
+ const safe = sqlIdent(name);
23738
+ const tables = await this.listTables();
23739
+ if (!tables.includes(safe)) {
23740
+ log2(`table "${safe}" not found, creating`);
23741
+ 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);
23742
+ log2(`table "${safe}" created`);
23743
+ if (!tables.includes(safe))
23744
+ this._tablesCache = [...tables, safe];
23745
+ }
23746
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
23747
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
23748
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
23749
+ }
23750
+ /**
23751
+ * Create the skills table.
23752
+ *
23753
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
23754
+ * MERGE rather than UPDATE-ing in place, so the full version history is
23755
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
23756
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
23757
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
23758
+ * worker.
23759
+ */
23760
+ async ensureSkillsTable(name) {
23761
+ const safe = sqlIdent(name);
23730
23762
  const tables = await this.listTables();
23731
- if (!tables.includes(name)) {
23732
- log2(`table "${name}" not found, creating`);
23733
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${name}" (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`, name);
23734
- log2(`table "${name}" created`);
23735
- if (!tables.includes(name))
23736
- this._tablesCache = [...tables, name];
23737
- }
23738
- await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
23739
- await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
23740
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
23763
+ if (!tables.includes(safe)) {
23764
+ log2(`table "${safe}" not found, creating`);
23765
+ 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);
23766
+ log2(`table "${safe}" created`);
23767
+ if (!tables.includes(safe))
23768
+ this._tablesCache = [...tables, safe];
23769
+ }
23770
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
23741
23771
  }
23742
23772
  };
23743
23773
 
@@ -25,6 +25,7 @@ function loadConfig() {
25
25
  apiUrl: creds?.apiUrl ?? "https://api.deeplake.ai",
26
26
  tableName: "memory",
27
27
  sessionsTableName: "sessions",
28
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
28
29
  memoryPath: join(home, ".deeplake", "memory")
29
30
  };
30
31
  }
@@ -90,6 +90,12 @@ function sqlStr(value) {
90
90
  function sqlLike(value) {
91
91
  return sqlStr(value).replace(/%/g, "\\%").replace(/_/g, "\\_");
92
92
  }
93
+ function sqlIdent(name) {
94
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
95
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
96
+ }
97
+ return name;
98
+ }
93
99
 
94
100
  // src/embeddings/columns.ts
95
101
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
@@ -450,7 +456,7 @@ var DeeplakeApi = class {
450
456
  }
451
457
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
452
458
  async ensureTable(name) {
453
- const tbl = name ?? this.tableName;
459
+ const tbl = sqlIdent(name ?? this.tableName);
454
460
  const tables = await this.listTables();
455
461
  if (!tables.includes(tbl)) {
456
462
  log2(`table "${tbl}" not found, creating`);
@@ -466,19 +472,44 @@ var DeeplakeApi = class {
466
472
  }
467
473
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
468
474
  async ensureSessionsTable(name) {
475
+ const safe = sqlIdent(name);
476
+ const tables = await this.listTables();
477
+ if (!tables.includes(safe)) {
478
+ log2(`table "${safe}" not found, creating`);
479
+ await this.createTableWithRetry(
480
+ `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`,
481
+ safe
482
+ );
483
+ log2(`table "${safe}" created`);
484
+ if (!tables.includes(safe)) this._tablesCache = [...tables, safe];
485
+ }
486
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
487
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
488
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
489
+ }
490
+ /**
491
+ * Create the skills table.
492
+ *
493
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
494
+ * MERGE rather than UPDATE-ing in place, so the full version history is
495
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
496
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
497
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
498
+ * worker.
499
+ */
500
+ async ensureSkillsTable(name) {
501
+ const safe = sqlIdent(name);
469
502
  const tables = await this.listTables();
470
- if (!tables.includes(name)) {
471
- log2(`table "${name}" not found, creating`);
503
+ if (!tables.includes(safe)) {
504
+ log2(`table "${safe}" not found, creating`);
472
505
  await this.createTableWithRetry(
473
- `CREATE TABLE IF NOT EXISTS "${name}" (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`,
474
- name
506
+ `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`,
507
+ safe
475
508
  );
476
- log2(`table "${name}" created`);
477
- if (!tables.includes(name)) this._tablesCache = [...tables, name];
509
+ log2(`table "${safe}" created`);
510
+ if (!tables.includes(safe)) this._tablesCache = [...tables, safe];
478
511
  }
479
- await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
480
- await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
481
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
512
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
482
513
  }
483
514
  };
484
515
 
@@ -987,6 +1018,19 @@ async function readVirtualPathContent(api2, memoryTable2, sessionsTable2, virtua
987
1018
  }
988
1019
 
989
1020
  // openclaw/src/index.ts
1021
+ import { fileURLToPath } from "node:url";
1022
+ import { join as joinPath, dirname as dirnamePath } from "node:path";
1023
+ import { homedir as homedir2, tmpdir } from "node:os";
1024
+ import {
1025
+ existsSync as fsExists,
1026
+ mkdirSync as fsMkdir,
1027
+ openSync as fsOpen,
1028
+ closeSync as fsClose,
1029
+ writeFileSync as fsWriteFile,
1030
+ constants as fsConstants
1031
+ } from "node:fs";
1032
+ import { createHash } from "node:crypto";
1033
+ import { createRequire } from "node:module";
990
1034
  function definePluginEntry(entry) {
991
1035
  return entry;
992
1036
  }
@@ -1000,7 +1044,7 @@ function loadCredsModule() {
1000
1044
  return credsModulePromise;
1001
1045
  }
1002
1046
  function loadConfigModule() {
1003
- if (!configModulePromise) configModulePromise = import("./chunks/config-G23NI5TV.js");
1047
+ if (!configModulePromise) configModulePromise = import("./chunks/config-ZLH6JFJS.js");
1004
1048
  return configModulePromise;
1005
1049
  }
1006
1050
  async function loadCredentials2() {
@@ -1016,6 +1060,8 @@ async function loadConfig() {
1016
1060
  const m = await loadConfigModule();
1017
1061
  return m.loadConfig();
1018
1062
  }
1063
+ var requireFromOpenclaw = createRequire(import.meta.url);
1064
+ var { spawn: realSpawn, execFileSync: realExecFileSync } = requireFromOpenclaw("node:child_process");
1019
1065
  var DEFAULT_API_URL2 = "https://api.deeplake.ai";
1020
1066
  var VERSION_URL = "https://clawhub.ai/api/v1/packages/hivemind";
1021
1067
  function extractLatestVersion(body) {
@@ -1026,7 +1072,7 @@ function extractLatestVersion(body) {
1026
1072
  return typeof v === "string" && v.length > 0 ? v : null;
1027
1073
  }
1028
1074
  function getInstalledVersion() {
1029
- return "0.7.4".length > 0 ? "0.7.4" : null;
1075
+ return "0.7.11".length > 0 ? "0.7.11" : null;
1030
1076
  }
1031
1077
  function isNewer(latest, current) {
1032
1078
  const parse = (v) => v.replace(/-.*$/, "").split(".").map(Number);
@@ -1123,9 +1169,112 @@ async function requestAuth() {
1123
1169
  var api = null;
1124
1170
  var sessionsTable = "sessions";
1125
1171
  var memoryTable = "memory";
1172
+ var skillsTable = "skills";
1126
1173
  var captureEnabled = true;
1127
1174
  var capturedCounts = /* @__PURE__ */ new Map();
1128
1175
  var fallbackSessionId = crypto.randomUUID();
1176
+ var __openclaw_filename = fileURLToPath(import.meta.url);
1177
+ var __openclaw_dirname = dirnamePath(__openclaw_filename);
1178
+ var OPENCLAW_SKILIFY_WORKER_PATH = joinPath(__openclaw_dirname, "skilify-worker.js");
1179
+ var OPENCLAW_SKILIFY_STATE_DIR = joinPath(homedir2(), ".deeplake", "state", "skilify");
1180
+ function deriveOpenclawProjectKey(channel) {
1181
+ const project = channel || "openclaw";
1182
+ const key = createHash("sha1").update(project).digest("hex").slice(0, 16);
1183
+ return { key, project };
1184
+ }
1185
+ function tryAcquireOpenclawSkilifyLock(projectKey) {
1186
+ try {
1187
+ fsMkdir(OPENCLAW_SKILIFY_STATE_DIR, { recursive: true });
1188
+ const lockPath = joinPath(OPENCLAW_SKILIFY_STATE_DIR, `${projectKey}.worker.lock`);
1189
+ const fd = fsOpen(lockPath, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY);
1190
+ fsClose(fd);
1191
+ return true;
1192
+ } catch {
1193
+ return false;
1194
+ }
1195
+ }
1196
+ function detectOpenclawGateAgent() {
1197
+ const candidates = [
1198
+ ["claude_code", "claude"],
1199
+ ["codex", "codex"],
1200
+ ["cursor", "cursor-agent"],
1201
+ ["hermes", "hermes"],
1202
+ ["pi", "pi"]
1203
+ ];
1204
+ for (const [agent, bin] of candidates) {
1205
+ try {
1206
+ realExecFileSync("which", [bin], { stdio: ["ignore", "pipe", "ignore"] });
1207
+ return agent;
1208
+ } catch {
1209
+ }
1210
+ }
1211
+ return null;
1212
+ }
1213
+ function spawnOpenclawSkilifyWorker(a) {
1214
+ if (!fsExists(OPENCLAW_SKILIFY_WORKER_PATH)) {
1215
+ a.loggerWarn?.(`skilify worker missing at ${OPENCLAW_SKILIFY_WORKER_PATH} \u2014 reinstall openclaw plugin`);
1216
+ return;
1217
+ }
1218
+ const gateAgent = detectOpenclawGateAgent();
1219
+ if (!gateAgent) {
1220
+ a.loggerWarn?.(`skilify spawn: no delegate gate CLI found on PATH (need one of: claude, codex, cursor-agent, hermes, pi). Mining skipped.`);
1221
+ return;
1222
+ }
1223
+ const { key: projectKey, project } = deriveOpenclawProjectKey(a.channel);
1224
+ if (!tryAcquireOpenclawSkilifyLock(projectKey)) {
1225
+ return;
1226
+ }
1227
+ const tmpDir = joinPath(tmpdir(), `deeplake-skilify-openclaw-${projectKey}-${Date.now()}`);
1228
+ try {
1229
+ fsMkdir(tmpDir, { recursive: true, mode: 448 });
1230
+ } catch (e) {
1231
+ a.loggerWarn?.(`skilify spawn: mkdir failed: ${e?.message ?? e}`);
1232
+ return;
1233
+ }
1234
+ const configPath = joinPath(tmpDir, "config.json");
1235
+ const config = {
1236
+ apiUrl: a.apiUrl,
1237
+ token: a.token,
1238
+ orgId: a.orgId,
1239
+ workspaceId: a.workspaceId,
1240
+ sessionsTable,
1241
+ skillsTable,
1242
+ userName: a.userName,
1243
+ cwd: homedir2(),
1244
+ // sentinel — only used by worker if install=project
1245
+ projectKey,
1246
+ project,
1247
+ agent: "openclaw",
1248
+ gateAgent,
1249
+ // delegate CLI for the worker's gate call (openclaw has no CLI of its own)
1250
+ scope: "me",
1251
+ team: [],
1252
+ install: "global",
1253
+ tmpDir,
1254
+ gateBin: null,
1255
+ // worker uses gateAgent to look up the binary itself
1256
+ cursorModel: void 0,
1257
+ hermesProvider: void 0,
1258
+ hermesModel: void 0,
1259
+ skilifyLog: joinPath(homedir2(), ".deeplake", "hivemind-openclaw-skilify.log"),
1260
+ currentSessionId: a.sessionId
1261
+ };
1262
+ try {
1263
+ fsWriteFile(configPath, JSON.stringify(config), { mode: 384 });
1264
+ } catch (e) {
1265
+ a.loggerWarn?.(`skilify spawn: config write failed: ${e?.message ?? e}`);
1266
+ return;
1267
+ }
1268
+ try {
1269
+ realSpawn(process.execPath, [OPENCLAW_SKILIFY_WORKER_PATH, configPath], {
1270
+ detached: true,
1271
+ stdio: "ignore",
1272
+ env: { ...process.env, HIVEMIND_SKILIFY_WORKER: "1", HIVEMIND_CAPTURE: "false" }
1273
+ }).unref();
1274
+ } catch (e) {
1275
+ a.loggerWarn?.(`skilify spawn: spawn failed: ${e?.message ?? e}`);
1276
+ }
1277
+ }
1129
1278
  function buildSessionPath(config, sessionId) {
1130
1279
  return `/sessions/${config.userName}/${config.userName}_${config.orgName}_${config.workspaceId}_${sessionId}.jsonl`;
1131
1280
  }
@@ -1200,6 +1349,7 @@ async function getApi() {
1200
1349
  }
1201
1350
  sessionsTable = config.sessionsTableName;
1202
1351
  memoryTable = config.tableName;
1352
+ skillsTable = config.skillsTableName;
1203
1353
  const candidate = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
1204
1354
  await candidate.ensureTable();
1205
1355
  await candidate.ensureSessionsTable(sessionsTable);
@@ -1333,17 +1483,22 @@ ${available}` };
1333
1483
  handler: async () => {
1334
1484
  const { ensureHivemindAllowlisted } = await loadSetupConfig();
1335
1485
  const result = ensureHivemindAllowlisted();
1486
+ const skilifyHint = `
1487
+
1488
+ Skill mining (skilify) runs in the background after each turn \u2014 your conversations get crystallised into reusable skills automatically. From your terminal:
1489
+ hivemind skilify status \u2014 see what's been mined
1490
+ hivemind skilify pull \u2014 fetch teammates' skills`;
1336
1491
  if (result.status === "already-set") {
1337
1492
  return { text: `\u2705 Hivemind tools are already enabled in your allowlist.
1338
1493
 
1339
- No changes needed \u2014 memory tools are available to the agent.` };
1494
+ No changes needed \u2014 memory tools are available to the agent.${skilifyHint}` };
1340
1495
  }
1341
1496
  if (result.status === "added") {
1342
1497
  return { text: `\u2705 Added "hivemind" to your tool allowlist.
1343
1498
 
1344
1499
  Openclaw will detect the config change and restart. On the next turn, the agent will have access to hivemind_search, hivemind_read, and hivemind_index.
1345
1500
 
1346
- Backup of previous config: ${result.backupPath}` };
1501
+ Backup of previous config: ${result.backupPath}${skilifyHint}` };
1347
1502
  }
1348
1503
  return { text: `\u26A0\uFE0F Could not update allowlist: ${result.error}
1349
1504
 
@@ -1606,7 +1761,7 @@ ${body.slice(0, 500)}`;
1606
1761
  }
1607
1762
  })();
1608
1763
  }
1609
- if ('---\nname: hivemind\ndescription: Global team and org memory powered by Activeloop. ALWAYS check BOTH built-in memory AND Hivemind memory when recalling information.\nallowed-tools: hivemind_search, hivemind_read, hivemind_index\n---\n\n# Hivemind Memory\n\nYou have TWO memory sources. ALWAYS check BOTH when the user asks you to recall, remember, or look up ANY information:\n\n1. **Your built-in memory** \u2014 personal per-project notes from the host agent\n2. **Hivemind global memory** \u2014 global memory shared across all sessions, users, and agents in the org, accessed via the tools below\n\n## Memory Structure\n\n```\n/index.md \u2190 START HERE \u2014 table of all sessions\n/summaries/\n <username>/\n <session-id>.md \u2190 AI-generated wiki summary per session\n/sessions/\n <username>/\n <user_org_ws_slug>.jsonl \u2190 raw session data\n```\n\n## How to Search\n\n1. **First**: call `hivemind_index()` \u2014 table of all sessions with dates, projects, descriptions\n2. **If you need details**: call `hivemind_read("/summaries/<username>/<session>.md")`\n3. **If you need raw data**: call `hivemind_read("/sessions/<username>/<file>.jsonl")`\n4. **Keyword search**: call `hivemind_search("keyword")` \u2014 substring search across both summaries and sessions, returns `path:line` hits\n\nDo NOT jump straight to reading raw JSONL files. Always start with `hivemind_index` and summaries.\n\n## Organization Management\n\n- `/hivemind_login` \u2014 sign in via device flow\n- `/hivemind_capture` \u2014 toggle capture on/off (off = no data sent)\n- `/hivemind_whoami` \u2014 show current org and workspace\n- `/hivemind_orgs` \u2014 list organizations\n- `/hivemind_switch_org <name-or-id>` \u2014 switch organization\n- `/hivemind_workspaces` \u2014 list workspaces\n- `/hivemind_switch_workspace <id>` \u2014 switch workspace\n- `/hivemind_version` \u2014 show installed version and check ClawHub for updates\n- `/hivemind_update` \u2014 shows how to install (ask the agent, or run `openclaw plugins update hivemind` in your terminal)\n- `/hivemind_autoupdate [on|off]` \u2014 toggle the agent-facing update nudge (on by default: when a newer version is available, the agent is prompted to install it via `exec` if you ask to update)\n\n## Limits\n\nDo NOT delegate to subagents when reading Hivemind memory. If a tool call returns empty after 2 attempts, skip it and move on. Report what you found rather than exhaustively retrying.\n\n## Getting Started\n\nAfter installing the plugin:\n1. Run `/hivemind_login` to authenticate\n2. Run `/hivemind_setup` to enable the memory tools in your openclaw allowlist (one-time, per install)\n3. Start using memory \u2014 ask questions, the agent automatically captures and searches\n\n## Sharing memory\n\nMultiple agents share memory when users are in the same Activeloop organization.\n'.length > 0) {
1764
+ if ('---\nname: hivemind\ndescription: Global team and org memory powered by Activeloop. ALWAYS check BOTH built-in memory AND Hivemind memory when recalling information.\nallowed-tools: hivemind_search, hivemind_read, hivemind_index\n---\n\n# Hivemind Memory\n\nYou have TWO memory sources. ALWAYS check BOTH when the user asks you to recall, remember, or look up ANY information:\n\n1. **Your built-in memory** \u2014 personal per-project notes from the host agent\n2. **Hivemind global memory** \u2014 global memory shared across all sessions, users, and agents in the org, accessed via the tools below\n\n## Memory Structure\n\n```\n/index.md \u2190 START HERE \u2014 table of all sessions\n/summaries/\n <username>/\n <session-id>.md \u2190 AI-generated wiki summary per session\n/sessions/\n <username>/\n <user_org_ws_slug>.jsonl \u2190 raw session data\n```\n\n## How to Search\n\n1. **First**: call `hivemind_index()` \u2014 table of all sessions with dates, projects, descriptions\n2. **If you need details**: call `hivemind_read("/summaries/<username>/<session>.md")`\n3. **If you need raw data**: call `hivemind_read("/sessions/<username>/<file>.jsonl")`\n4. **Keyword search**: call `hivemind_search("keyword")` \u2014 substring search across both summaries and sessions, returns `path:line` hits\n\nDo NOT jump straight to reading raw JSONL files. Always start with `hivemind_index` and summaries.\n\n## Organization Management\n\n- `/hivemind_login` \u2014 sign in via device flow\n- `/hivemind_capture` \u2014 toggle capture on/off (off = no data sent)\n- `/hivemind_whoami` \u2014 show current org and workspace\n- `/hivemind_orgs` \u2014 list organizations\n- `/hivemind_switch_org <name-or-id>` \u2014 switch organization\n- `/hivemind_workspaces` \u2014 list workspaces\n- `/hivemind_switch_workspace <id>` \u2014 switch workspace\n- `/hivemind_version` \u2014 show installed version and check ClawHub for updates\n- `/hivemind_update` \u2014 shows how to install (ask the agent, or run `openclaw plugins update hivemind` in your terminal)\n- `/hivemind_autoupdate [on|off]` \u2014 toggle the agent-facing update nudge (on by default: when a newer version is available, the agent is prompted to install it via `exec` if you ask to update)\n\n## Skill Management (skilify)\n\nHivemind also mines reusable Claude skills from agent sessions and stores them in a per-org Deeplake table. Openclaw itself doesn\'t run sessions to mine, but you can pull skills others have already mined for the user. These run in the user\'s terminal (the openclaw plugin does not register them as `/hivemind_*` commands):\n\n- `hivemind skilify` \u2014 show scope/team/install + per-project state\n- `hivemind skilify pull` \u2014 sync skills for the current project from the org table\n- `hivemind skilify pull --user <email>` \u2014 only that author\'s skills\n- `hivemind skilify pull --users a,b,c` \u2014 multiple authors (CSV)\n- `hivemind skilify pull --all-users` \u2014 explicit "no author filter"\n- `hivemind skilify pull --to project|global` \u2014 install location (`<cwd>/.claude/skills/` vs `~/.claude/skills/`)\n- `hivemind skilify pull --dry-run` \u2014 preview without touching disk\n- `hivemind skilify pull --force` \u2014 overwrite local (creates `.bak`)\n- `hivemind skilify pull <skill-name>` \u2014 pull only that one skill (combines with `--user`)\n- `hivemind skilify scope <me|team|org>` \u2014 set sharing scope for new skills\n- `hivemind skilify install <project|global>` \u2014 default install location\n- `hivemind skilify team add|remove|list <name>` \u2014 manage team list\n\nIf the user asks to "pull skills from X", "share skills with the team", or similar, suggest the matching `hivemind skilify` command. Run `hivemind skilify --help` for the full reference.\n\n## Limits\n\nDo NOT delegate to subagents when reading Hivemind memory. If a tool call returns empty after 2 attempts, skip it and move on. Report what you found rather than exhaustively retrying.\n\n## Getting Started\n\nAfter installing the plugin:\n1. Run `/hivemind_login` to authenticate\n2. Run `/hivemind_setup` to enable the memory tools in your openclaw allowlist (one-time, per install)\n3. Start using memory \u2014 ask questions, the agent automatically captures and searches\n\n## Sharing memory\n\nMultiple agents share memory when users are in the same Activeloop organization.\n'.length > 0) {
1610
1765
  const setupConfigPromise = loadSetupConfig();
1611
1766
  hook("before_prompt_build", async () => {
1612
1767
  const { detectAllowlistMissing } = await setupConfigPromise;
@@ -1618,7 +1773,7 @@ A newer Hivemind version is available: ${pendingUpdate.current} \u2192 ${pending
1618
1773
  </hivemind-update-available>
1619
1774
  ` : "";
1620
1775
  return {
1621
- prependSystemContext: allowlistNudge + updateNudge + '\n\n<hivemind-skill>\n---\nname: hivemind\ndescription: Global team and org memory powered by Activeloop. ALWAYS check BOTH built-in memory AND Hivemind memory when recalling information.\nallowed-tools: hivemind_search, hivemind_read, hivemind_index\n---\n\n# Hivemind Memory\n\nYou have TWO memory sources. ALWAYS check BOTH when the user asks you to recall, remember, or look up ANY information:\n\n1. **Your built-in memory** \u2014 personal per-project notes from the host agent\n2. **Hivemind global memory** \u2014 global memory shared across all sessions, users, and agents in the org, accessed via the tools below\n\n## Memory Structure\n\n```\n/index.md \u2190 START HERE \u2014 table of all sessions\n/summaries/\n <username>/\n <session-id>.md \u2190 AI-generated wiki summary per session\n/sessions/\n <username>/\n <user_org_ws_slug>.jsonl \u2190 raw session data\n```\n\n## How to Search\n\n1. **First**: call `hivemind_index()` \u2014 table of all sessions with dates, projects, descriptions\n2. **If you need details**: call `hivemind_read("/summaries/<username>/<session>.md")`\n3. **If you need raw data**: call `hivemind_read("/sessions/<username>/<file>.jsonl")`\n4. **Keyword search**: call `hivemind_search("keyword")` \u2014 substring search across both summaries and sessions, returns `path:line` hits\n\nDo NOT jump straight to reading raw JSONL files. Always start with `hivemind_index` and summaries.\n\n## Organization Management\n\n- `/hivemind_login` \u2014 sign in via device flow\n- `/hivemind_capture` \u2014 toggle capture on/off (off = no data sent)\n- `/hivemind_whoami` \u2014 show current org and workspace\n- `/hivemind_orgs` \u2014 list organizations\n- `/hivemind_switch_org <name-or-id>` \u2014 switch organization\n- `/hivemind_workspaces` \u2014 list workspaces\n- `/hivemind_switch_workspace <id>` \u2014 switch workspace\n- `/hivemind_version` \u2014 show installed version and check ClawHub for updates\n- `/hivemind_update` \u2014 shows how to install (ask the agent, or run `openclaw plugins update hivemind` in your terminal)\n- `/hivemind_autoupdate [on|off]` \u2014 toggle the agent-facing update nudge (on by default: when a newer version is available, the agent is prompted to install it via `exec` if you ask to update)\n\n## Limits\n\nDo NOT delegate to subagents when reading Hivemind memory. If a tool call returns empty after 2 attempts, skip it and move on. Report what you found rather than exhaustively retrying.\n\n## Getting Started\n\nAfter installing the plugin:\n1. Run `/hivemind_login` to authenticate\n2. Run `/hivemind_setup` to enable the memory tools in your openclaw allowlist (one-time, per install)\n3. Start using memory \u2014 ask questions, the agent automatically captures and searches\n\n## Sharing memory\n\nMultiple agents share memory when users are in the same Activeloop organization.\n\n</hivemind-skill>\n'
1776
+ prependSystemContext: allowlistNudge + updateNudge + '\n\n<hivemind-skill>\n---\nname: hivemind\ndescription: Global team and org memory powered by Activeloop. ALWAYS check BOTH built-in memory AND Hivemind memory when recalling information.\nallowed-tools: hivemind_search, hivemind_read, hivemind_index\n---\n\n# Hivemind Memory\n\nYou have TWO memory sources. ALWAYS check BOTH when the user asks you to recall, remember, or look up ANY information:\n\n1. **Your built-in memory** \u2014 personal per-project notes from the host agent\n2. **Hivemind global memory** \u2014 global memory shared across all sessions, users, and agents in the org, accessed via the tools below\n\n## Memory Structure\n\n```\n/index.md \u2190 START HERE \u2014 table of all sessions\n/summaries/\n <username>/\n <session-id>.md \u2190 AI-generated wiki summary per session\n/sessions/\n <username>/\n <user_org_ws_slug>.jsonl \u2190 raw session data\n```\n\n## How to Search\n\n1. **First**: call `hivemind_index()` \u2014 table of all sessions with dates, projects, descriptions\n2. **If you need details**: call `hivemind_read("/summaries/<username>/<session>.md")`\n3. **If you need raw data**: call `hivemind_read("/sessions/<username>/<file>.jsonl")`\n4. **Keyword search**: call `hivemind_search("keyword")` \u2014 substring search across both summaries and sessions, returns `path:line` hits\n\nDo NOT jump straight to reading raw JSONL files. Always start with `hivemind_index` and summaries.\n\n## Organization Management\n\n- `/hivemind_login` \u2014 sign in via device flow\n- `/hivemind_capture` \u2014 toggle capture on/off (off = no data sent)\n- `/hivemind_whoami` \u2014 show current org and workspace\n- `/hivemind_orgs` \u2014 list organizations\n- `/hivemind_switch_org <name-or-id>` \u2014 switch organization\n- `/hivemind_workspaces` \u2014 list workspaces\n- `/hivemind_switch_workspace <id>` \u2014 switch workspace\n- `/hivemind_version` \u2014 show installed version and check ClawHub for updates\n- `/hivemind_update` \u2014 shows how to install (ask the agent, or run `openclaw plugins update hivemind` in your terminal)\n- `/hivemind_autoupdate [on|off]` \u2014 toggle the agent-facing update nudge (on by default: when a newer version is available, the agent is prompted to install it via `exec` if you ask to update)\n\n## Skill Management (skilify)\n\nHivemind also mines reusable Claude skills from agent sessions and stores them in a per-org Deeplake table. Openclaw itself doesn\'t run sessions to mine, but you can pull skills others have already mined for the user. These run in the user\'s terminal (the openclaw plugin does not register them as `/hivemind_*` commands):\n\n- `hivemind skilify` \u2014 show scope/team/install + per-project state\n- `hivemind skilify pull` \u2014 sync skills for the current project from the org table\n- `hivemind skilify pull --user <email>` \u2014 only that author\'s skills\n- `hivemind skilify pull --users a,b,c` \u2014 multiple authors (CSV)\n- `hivemind skilify pull --all-users` \u2014 explicit "no author filter"\n- `hivemind skilify pull --to project|global` \u2014 install location (`<cwd>/.claude/skills/` vs `~/.claude/skills/`)\n- `hivemind skilify pull --dry-run` \u2014 preview without touching disk\n- `hivemind skilify pull --force` \u2014 overwrite local (creates `.bak`)\n- `hivemind skilify pull <skill-name>` \u2014 pull only that one skill (combines with `--user`)\n- `hivemind skilify scope <me|team|org>` \u2014 set sharing scope for new skills\n- `hivemind skilify install <project|global>` \u2014 default install location\n- `hivemind skilify team add|remove|list <name>` \u2014 manage team list\n\nIf the user asks to "pull skills from X", "share skills with the team", or similar, suggest the matching `hivemind skilify` command. Run `hivemind skilify --help` for the full reference.\n\n## Limits\n\nDo NOT delegate to subagents when reading Hivemind memory. If a tool call returns empty after 2 attempts, skip it and move on. Report what you found rather than exhaustively retrying.\n\n## Getting Started\n\nAfter installing the plugin:\n1. Run `/hivemind_login` to authenticate\n2. Run `/hivemind_setup` to enable the memory tools in your openclaw allowlist (one-time, per install)\n3. Start using memory \u2014 ask questions, the agent automatically captures and searches\n\n## Sharing memory\n\nMultiple agents share memory when users are in the same Activeloop organization.\n\n</hivemind-skill>\n'
1622
1777
  };
1623
1778
  });
1624
1779
  }
@@ -1736,6 +1891,20 @@ One brain for every agent on your team.
1736
1891
  }
1737
1892
  }
1738
1893
  logger.info?.(`Auto-captured ${newMessages.length} messages`);
1894
+ try {
1895
+ spawnOpenclawSkilifyWorker({
1896
+ apiUrl: cfg.apiUrl,
1897
+ token: cfg.token,
1898
+ orgId: cfg.orgId,
1899
+ workspaceId: cfg.workspaceId,
1900
+ userName: cfg.userName,
1901
+ channel: ev.channel || "openclaw",
1902
+ sessionId: sid,
1903
+ loggerWarn: (msg) => logger.error(`Skilify spawn: ${msg}`)
1904
+ });
1905
+ } catch (e) {
1906
+ logger.error(`Skilify spawn threw: ${e?.message ?? e}`);
1907
+ }
1739
1908
  } catch (err) {
1740
1909
  logger.error(`Auto-capture failed: ${err instanceof Error ? err.message : String(err)}`);
1741
1910
  }