@sesamespace/hivemind 0.10.0 → 0.11.1

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 (52) hide show
  1. package/.pnpmrc.json +1 -0
  2. package/AUTO-DEBUG-DESIGN.md +267 -0
  3. package/AUTOMATIC-MEMORY-MANAGEMENT.md +109 -0
  4. package/DASHBOARD-PLAN.md +206 -0
  5. package/MEMORY-ENHANCEMENT-PLAN.md +211 -0
  6. package/TOOL-USE-DESIGN.md +173 -0
  7. package/dist/{chunk-FBQBBAPZ.js → chunk-4C6B2AMB.js} +2 -2
  8. package/dist/{chunk-FK6WYXRM.js → chunk-4YXOQGQC.js} +2 -2
  9. package/dist/{chunk-IXBIAX76.js → chunk-K6KL2VD6.js} +2 -2
  10. package/dist/{chunk-IJRAVHQC.js → chunk-LWJCKTQP.js} +51 -11
  11. package/dist/chunk-LWJCKTQP.js.map +1 -0
  12. package/dist/{chunk-BHCDOHSK.js → chunk-LYL5GG2F.js} +3 -3
  13. package/dist/{chunk-M3A2WRXM.js → chunk-OB6OXLPC.js} +430 -2
  14. package/dist/chunk-OB6OXLPC.js.map +1 -0
  15. package/dist/{chunk-DPLCEMEC.js → chunk-ZA4NWNS6.js} +2 -2
  16. package/dist/commands/fleet.js +3 -3
  17. package/dist/commands/init.js +3 -3
  18. package/dist/commands/service.js +1 -1
  19. package/dist/commands/start.js +3 -3
  20. package/dist/commands/watchdog.js +3 -3
  21. package/dist/dashboard.html +100 -60
  22. package/dist/index.js +2 -2
  23. package/dist/main.js +7 -7
  24. package/dist/start.js +1 -1
  25. package/docs/TOOL-PARITY-PLAN.md +191 -0
  26. package/package.json +23 -24
  27. package/src/memory/dashboard-integration.ts +295 -0
  28. package/src/memory/index.ts +187 -0
  29. package/src/memory/performance-test.ts +208 -0
  30. package/src/memory/processors/agent-sync.ts +312 -0
  31. package/src/memory/processors/command-learner.ts +298 -0
  32. package/src/memory/processors/memory-api-client.ts +105 -0
  33. package/src/memory/processors/message-flow-integration.ts +168 -0
  34. package/src/memory/processors/research-digester.ts +204 -0
  35. package/test-caitlin-access.md +11 -0
  36. package/dist/chunk-IJRAVHQC.js.map +0 -1
  37. package/dist/chunk-M3A2WRXM.js.map +0 -1
  38. package/install.sh +0 -162
  39. package/packages/memory/Cargo.lock +0 -6480
  40. package/packages/memory/Cargo.toml +0 -21
  41. package/packages/memory/src/src/context.rs +0 -179
  42. package/packages/memory/src/src/embeddings.rs +0 -51
  43. package/packages/memory/src/src/main.rs +0 -887
  44. package/packages/memory/src/src/promotion.rs +0 -808
  45. package/packages/memory/src/src/scoring.rs +0 -142
  46. package/packages/memory/src/src/store.rs +0 -460
  47. package/packages/memory/src/src/tasks.rs +0 -321
  48. /package/dist/{chunk-FBQBBAPZ.js.map → chunk-4C6B2AMB.js.map} +0 -0
  49. /package/dist/{chunk-FK6WYXRM.js.map → chunk-4YXOQGQC.js.map} +0 -0
  50. /package/dist/{chunk-IXBIAX76.js.map → chunk-K6KL2VD6.js.map} +0 -0
  51. /package/dist/{chunk-BHCDOHSK.js.map → chunk-LYL5GG2F.js.map} +0 -0
  52. /package/dist/{chunk-DPLCEMEC.js.map → chunk-ZA4NWNS6.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startPipeline
3
- } from "./chunk-M3A2WRXM.js";
3
+ } from "./chunk-OB6OXLPC.js";
4
4
 
5
5
  // packages/cli/src/commands/start.ts
6
6
  import { resolve } from "path";
@@ -66,4 +66,4 @@ Options:
66
66
  export {
67
67
  runStartCommand
68
68
  };
69
- //# sourceMappingURL=chunk-DPLCEMEC.js.map
69
+ //# sourceMappingURL=chunk-ZA4NWNS6.js.map
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  runFleetCommand
3
- } from "../chunk-FBQBBAPZ.js";
4
- import "../chunk-IXBIAX76.js";
5
- import "../chunk-M3A2WRXM.js";
3
+ } from "../chunk-4C6B2AMB.js";
4
+ import "../chunk-K6KL2VD6.js";
5
+ import "../chunk-OB6OXLPC.js";
6
6
  import "../chunk-DGUM43GV.js";
7
7
  export {
8
8
  runFleetCommand
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  runInitCommand
3
- } from "../chunk-FK6WYXRM.js";
4
- import "../chunk-IXBIAX76.js";
5
- import "../chunk-M3A2WRXM.js";
3
+ } from "../chunk-4YXOQGQC.js";
4
+ import "../chunk-K6KL2VD6.js";
5
+ import "../chunk-OB6OXLPC.js";
6
6
  import "../chunk-DGUM43GV.js";
7
7
  export {
8
8
  runInitCommand
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runServiceCommand
3
- } from "../chunk-IJRAVHQC.js";
3
+ } from "../chunk-LWJCKTQP.js";
4
4
  import "../chunk-DGUM43GV.js";
5
5
  export {
6
6
  runServiceCommand
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  runStartCommand
3
- } from "../chunk-DPLCEMEC.js";
4
- import "../chunk-IXBIAX76.js";
5
- import "../chunk-M3A2WRXM.js";
3
+ } from "../chunk-ZA4NWNS6.js";
4
+ import "../chunk-K6KL2VD6.js";
5
+ import "../chunk-OB6OXLPC.js";
6
6
  import "../chunk-DGUM43GV.js";
7
7
  export {
8
8
  runStartCommand
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  runWatchdogCommand
3
- } from "../chunk-BHCDOHSK.js";
4
- import "../chunk-IXBIAX76.js";
5
- import "../chunk-M3A2WRXM.js";
3
+ } from "../chunk-LYL5GG2F.js";
4
+ import "../chunk-K6KL2VD6.js";
5
+ import "../chunk-OB6OXLPC.js";
6
6
  import "../chunk-DGUM43GV.js";
7
7
  export {
8
8
  runWatchdogCommand
@@ -406,26 +406,36 @@ async function loadHealthView() {
406
406
  const tbody = document.getElementById('ctx-summary-body');
407
407
  const scoringEl = document.getElementById('scoring-config-section');
408
408
 
409
+ let stats = null;
410
+ let contexts = [];
411
+
412
+ // Fetch stats and contexts independently so partial data still shows
409
413
  try {
410
- const [statsRes, ctxRes] = await Promise.all([
411
- fetch(API + '/api/stats'),
412
- fetch(API + '/api/contexts'),
413
- ]);
414
-
415
- if (!statsRes.ok || !ctxRes.ok) {
416
- showNotification('Failed to load system stats', 'error');
417
- cards.innerHTML = '<p style="color:var(--red)">Failed to load stats (daemon may be offline)</p>';
418
- return;
414
+ const ctxRes = await fetch(API + '/api/contexts');
415
+ if (ctxRes.ok) {
416
+ const ctxData = await ctxRes.json();
417
+ contexts = ctxData.contexts || [];
419
418
  }
419
+ } catch {}
420
420
 
421
- const stats = await statsRes.json();
422
- const ctxData = await ctxRes.json();
423
- const contexts = ctxData.contexts || [];
421
+ try {
422
+ const statsRes = await fetch(API + '/api/stats');
423
+ if (statsRes.ok) stats = await statsRes.json();
424
+ } catch {}
424
425
 
425
- const totalEpisodes = Object.values(stats.total_episodes || {}).reduce((a, b) => a + b, 0);
426
+ if (!stats && contexts.length === 0) {
427
+ cards.innerHTML = '<p style="color:var(--red)">Cannot connect to memory daemon</p>';
428
+ showNotification('Health view failed: daemon may be offline', 'error');
429
+ return;
430
+ }
431
+
432
+ try {
426
433
  const reqCountRes = await fetch(API + '/api/requests?limit=1');
427
- const reqCount = reqCountRes.ok ? (await reqCountRes.json()).total || 0 : '?';
434
+ var reqCount = reqCountRes.ok ? (await reqCountRes.json()).total || 0 : '?';
435
+ } catch { var reqCount = '?'; }
428
436
 
437
+ if (stats) {
438
+ const totalEpisodes = Object.values(stats.total_episodes || {}).reduce((a, b) => a + b, 0);
429
439
  cards.innerHTML = `
430
440
  <div class="status-card"><div class="label">Daemon Status</div><div class="value" style="color:var(${daemonStatus === 'ok' ? '--green' : '--red'})">${daemonStatus === 'ok' ? 'Online' : daemonStatus}</div><div class="sub">v${esc(daemonVersion)}</div></div>
431
441
  <div class="status-card"><div class="label">Embedding Model</div><div class="value" style="font-size:14px">${esc(stats.embedding_model)}</div></div>
@@ -434,26 +444,37 @@ async function loadHealthView() {
434
444
  <div class="status-card"><div class="label">Access Records</div><div class="value">${stats.total_access_records}</div></div>
435
445
  <div class="status-card"><div class="label">Request Logs</div><div class="value">${reqCount}</div></div>
436
446
  `;
447
+ } else {
448
+ const totalEpisodes = contexts.reduce((a, c) => a + (c.episode_count || 0), 0);
449
+ cards.innerHTML = `
450
+ <div class="status-card"><div class="label">Daemon Status</div><div class="value" style="color:var(${daemonStatus === 'ok' ? '--green' : '--red'})">${daemonStatus === 'ok' ? 'Online' : daemonStatus}</div><div class="sub">v${esc(daemonVersion)}</div></div>
451
+ <div class="status-card"><div class="label">Stats</div><div class="value" style="font-size:14px;color:var(--yellow)">Unavailable</div><div class="sub">/stats endpoint not found — rebuild daemon</div></div>
452
+ <div class="status-card"><div class="label">Total Episodes</div><div class="value">${totalEpisodes}</div><div class="sub">${contexts.length} contexts</div></div>
453
+ <div class="status-card"><div class="label">Request Logs</div><div class="value">${reqCount}</div></div>
454
+ `;
455
+ }
437
456
 
438
- // Context summary table
439
- tbody.innerHTML = '';
440
- for (const c of contexts) {
441
- const epCount = stats.total_episodes?.[c.name] ?? c.episode_count ?? 0;
442
- const tr = document.createElement('tr');
443
- tr.innerHTML = `<td style="color:var(--accent)">${esc(c.name)}</td><td>${epCount}</td><td>-</td><td>${c.created_at ? new Date(c.created_at).toLocaleDateString() : '-'}</td>`;
444
- tbody.appendChild(tr);
445
- }
457
+ // Context summary table — always populate from contexts data
458
+ tbody.innerHTML = '';
459
+ for (const c of contexts) {
460
+ const epCount = stats ? (stats.total_episodes?.[c.name] ?? c.episode_count ?? 0) : (c.episode_count ?? 0);
461
+ const tr = document.createElement('tr');
462
+ tr.innerHTML = `<td style="color:var(--accent)">${esc(c.name)}</td><td>${epCount}</td><td>-</td><td>${c.created_at ? new Date(c.created_at).toLocaleDateString() : '-'}</td>`;
463
+ tbody.appendChild(tr);
464
+ }
446
465
 
447
- // Scoring section
448
- const defaultHL = stats.default_half_life_hours || 48;
466
+ // Scoring section
467
+ const defaultHL = (stats && stats.default_half_life_hours) || 48;
468
+ if (stats) {
449
469
  scoringEl.innerHTML = `
450
470
  <p style="color:var(--text2);font-size:13px;margin-bottom:12px">Default half-life: <strong>${defaultHL}h</strong>. Episodes lose ~50% relevance weight every ${defaultHL} hours.</p>
451
471
  ${renderDecayCurve(defaultHL)}
452
472
  `;
453
-
454
- } catch (err) {
455
- cards.innerHTML = '<p style="color:var(--red)">Cannot connect to memory daemon</p>';
456
- showNotification('Health view failed: ' + err.message, 'error');
473
+ } else {
474
+ scoringEl.innerHTML = `
475
+ <p style="color:var(--yellow);font-size:13px;margin-bottom:12px">Stats unavailable showing default half-life: <strong>${defaultHL}h</strong>.</p>
476
+ ${renderDecayCurve(defaultHL)}
477
+ `;
457
478
  }
458
479
  }
459
480
 
@@ -998,37 +1019,41 @@ async function loadPromotion() {
998
1019
 
999
1020
  async function loadThresholds() {
1000
1021
  const el = document.getElementById('promo-thresholds');
1022
+ let stats = null;
1023
+ let statsUnavailable = false;
1001
1024
  try {
1002
1025
  const res = await fetch(API + '/api/stats');
1003
- if (!res.ok) { el.innerHTML = '<p style="color:var(--red)">Failed to load stats</p>'; return; }
1004
- const stats = await res.json();
1005
- const t = stats.promotion_thresholds || { access: 5, cooccurrence: 3 };
1006
- el.innerHTML = `
1007
- <div class="status-grid" style="margin-bottom:24px">
1008
- <div class="status-card">
1009
- <div class="label">Access Threshold</div>
1010
- <div class="value">${t.access}</div>
1011
- <div class="sub">Episode must be accessed at least ${t.access} times</div>
1012
- </div>
1013
- <div class="status-card">
1014
- <div class="label">Co-occurrence Threshold</div>
1015
- <div class="value">${t.cooccurrence}</div>
1016
- <div class="sub">Episode must co-occur with others at least ${t.cooccurrence} times</div>
1017
- </div>
1026
+ if (res.ok) stats = await res.json();
1027
+ else statsUnavailable = true;
1028
+ } catch { statsUnavailable = true; }
1029
+
1030
+ const t = (stats && stats.promotion_thresholds) || { access: 5, cooccurrence: 3 };
1031
+ const warning = statsUnavailable ? '<p style="color:var(--yellow);font-size:12px;margin-bottom:12px;padding:8px;border:1px solid var(--yellow);border-radius:4px">/stats endpoint unavailable — showing default thresholds (access: 5, cooccurrence: 3). Rebuild daemon to enable.</p>' : '';
1032
+
1033
+ el.innerHTML = `
1034
+ ${warning}
1035
+ <div class="status-grid" style="margin-bottom:24px">
1036
+ <div class="status-card">
1037
+ <div class="label">Access Threshold</div>
1038
+ <div class="value">${t.access}</div>
1039
+ <div class="sub">Episode must be accessed at least ${t.access} times</div>
1018
1040
  </div>
1019
- <h3 style="margin-bottom:12px">How Promotion Works</h3>
1020
- <div style="color:var(--text2);font-size:13px;line-height:1.8">
1021
- <p><strong>L2 &rarr; L3 promotion</strong> happens when an episode meets <em>both</em> thresholds:</p>
1022
- <ol style="margin:8px 0 8px 20px">
1023
- <li>The episode has been accessed (retrieved in search results) at least <strong>${t.access} times</strong></li>
1024
- <li>The episode has co-occurred with other episodes at least <strong>${t.cooccurrence} times total</strong> (connection density)</li>
1025
- </ol>
1026
- <p>Once promoted, the episode's content is stored as long-term semantic knowledge (L3) and included in future system prompts for that context.</p>
1041
+ <div class="status-card">
1042
+ <div class="label">Co-occurrence Threshold</div>
1043
+ <div class="value">${t.cooccurrence}</div>
1044
+ <div class="sub">Episode must co-occur with others at least ${t.cooccurrence} times</div>
1027
1045
  </div>
1028
- `;
1029
- } catch (err) {
1030
- el.innerHTML = `<p style="color:var(--red)">Failed: ${esc(err.message)}</p>`;
1031
- }
1046
+ </div>
1047
+ <h3 style="margin-bottom:12px">How Promotion Works</h3>
1048
+ <div style="color:var(--text2);font-size:13px;line-height:1.8">
1049
+ <p><strong>L2 &rarr; L3 promotion</strong> happens when an episode meets <em>both</em> thresholds:</p>
1050
+ <ol style="margin:8px 0 8px 20px">
1051
+ <li>The episode has been accessed (retrieved in search results) at least <strong>${t.access} times</strong></li>
1052
+ <li>The episode has co-occurred with other episodes at least <strong>${t.cooccurrence} times total</strong> (connection density)</li>
1053
+ </ol>
1054
+ <p>Once promoted, the episode's content is stored as long-term semantic knowledge (L3) and included in future system prompts for that context.</p>
1055
+ </div>
1056
+ `;
1032
1057
  }
1033
1058
 
1034
1059
  async function loadCandidates() {
@@ -1036,7 +1061,14 @@ async function loadCandidates() {
1036
1061
  el.innerHTML = '<p style="color:var(--text2)">Loading candidates...</p>';
1037
1062
  try {
1038
1063
  const res = await fetch(API + '/api/access/top?limit=50');
1039
- if (!res.ok) { el.innerHTML = '<p style="color:var(--red)">Failed to load candidates</p>'; return; }
1064
+ if (!res.ok) {
1065
+ if (res.status === 404) {
1066
+ el.innerHTML = '<p style="color:var(--yellow);padding:12px;border:1px solid var(--yellow);border-radius:4px">/access/top endpoint not found (404). The running daemon binary may be outdated. Rebuild with: <code style="background:var(--bg);padding:2px 6px;border-radius:3px">cd packages/memory && cargo build --release</code> and restart the daemon.</p>';
1067
+ } else {
1068
+ el.innerHTML = `<p style="color:var(--red)">Failed to load candidates (HTTP ${res.status})</p>`;
1069
+ }
1070
+ return;
1071
+ }
1040
1072
  const data = await res.json();
1041
1073
  const records = data.records || [];
1042
1074
 
@@ -1086,6 +1118,7 @@ async function loadScoringTab() {
1086
1118
  // Fetch scoring for each context
1087
1119
  const scoringData = {};
1088
1120
  let defaultHL = 48;
1121
+ let scoringAvailable = true;
1089
1122
  for (const c of contexts) {
1090
1123
  try {
1091
1124
  const sRes = await fetch(API + '/api/contexts/' + encodeURIComponent(c.name) + '/scoring');
@@ -1093,23 +1126,30 @@ async function loadScoringTab() {
1093
1126
  const sd = await sRes.json();
1094
1127
  scoringData[c.name] = sd;
1095
1128
  defaultHL = sd.default_half_life_hours || 48;
1129
+ } else if (sRes.status === 404) {
1130
+ scoringAvailable = false;
1096
1131
  }
1097
1132
  } catch {}
1098
1133
  }
1099
1134
 
1100
- let html = `<p style="color:var(--text2);font-size:13px;margin-bottom:16px">Default half-life: <strong>${defaultHL}h</strong>. Settings reset when daemon restarts (in-memory only).</p>`;
1135
+ let html = '';
1136
+ if (!scoringAvailable) {
1137
+ html += '<p style="color:var(--yellow);font-size:12px;margin-bottom:12px;padding:8px;border:1px solid var(--yellow);border-radius:4px">/scoring endpoint not found — the running daemon may be outdated. Showing default values. Rebuild daemon to enable per-context scoring configuration.</p>';
1138
+ }
1139
+ html += `<p style="color:var(--text2);font-size:13px;margin-bottom:16px">Default half-life: <strong>${defaultHL}h</strong>. Settings reset when daemon restarts (in-memory only).</p>`;
1101
1140
 
1102
1141
  html += '<table class="data-table"><thead><tr><th>Context</th><th>Half-Life (hours)</th><th>Custom?</th><th>Decay Curve</th><th>Actions</th></tr></thead><tbody>';
1103
1142
  for (const c of contexts) {
1104
1143
  const sd = scoringData[c.name] || {};
1105
1144
  const hl = sd.half_life_hours || defaultHL;
1106
1145
  const isCustom = sd.is_default === false;
1146
+ const sourceLabel = !scoringAvailable ? '<span style="color:var(--text2)">Default (endpoint unavailable)</span>' : (isCustom ? '<span style="color:var(--yellow)">Yes</span>' : 'Default');
1107
1147
  html += `<tr>
1108
1148
  <td style="color:var(--accent)">${esc(c.name)}</td>
1109
- <td><input type="number" value="${hl}" min="1" max="8760" step="1" style="width:80px;background:var(--bg);border:1px solid var(--border);color:var(--text);padding:4px;border-radius:4px" id="hl-${esc(c.name)}" /></td>
1110
- <td>${isCustom ? '<span style="color:var(--yellow)">Yes</span>' : 'Default'}</td>
1149
+ <td><input type="number" value="${hl}" min="1" max="8760" step="1" style="width:80px;background:var(--bg);border:1px solid var(--border);color:var(--text);padding:4px;border-radius:4px" id="hl-${esc(c.name)}" ${!scoringAvailable ? 'disabled' : ''} /></td>
1150
+ <td>${sourceLabel}</td>
1111
1151
  <td>${renderDecayCurve(hl)}</td>
1112
- <td><button class="btn-sm" onclick="saveScoring('${esc(c.name)}')">Save</button></td>
1152
+ <td><button class="btn-sm" onclick="saveScoring('${esc(c.name)}')" ${!scoringAvailable ? 'disabled' : ''}>Save</button></td>
1113
1153
  </tr>`;
1114
1154
  }
1115
1155
  html += '</tbody></table>';
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  PrimaryMemorySync,
5
5
  Watchdog,
6
6
  WorkerMemorySync
7
- } from "./chunk-IXBIAX76.js";
7
+ } from "./chunk-K6KL2VD6.js";
8
8
  import {
9
9
  Agent,
10
10
  AutoDebugger,
@@ -34,7 +34,7 @@ import {
34
34
  setLogLevel,
35
35
  startPipeline,
36
36
  startWorker
37
- } from "./chunk-M3A2WRXM.js";
37
+ } from "./chunk-OB6OXLPC.js";
38
38
  import "./chunk-DGUM43GV.js";
39
39
  export {
40
40
  Agent,
package/dist/main.js CHANGED
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runInitCommand
4
- } from "./chunk-FK6WYXRM.js";
4
+ } from "./chunk-4YXOQGQC.js";
5
5
  import {
6
6
  runStartCommand
7
- } from "./chunk-DPLCEMEC.js";
7
+ } from "./chunk-ZA4NWNS6.js";
8
8
  import {
9
9
  runFleetCommand
10
- } from "./chunk-FBQBBAPZ.js";
10
+ } from "./chunk-4C6B2AMB.js";
11
11
  import {
12
12
  runServiceCommand
13
- } from "./chunk-IJRAVHQC.js";
13
+ } from "./chunk-LWJCKTQP.js";
14
14
  import {
15
15
  runUpgradeCommand
16
16
  } from "./chunk-ICSJNKI6.js";
17
17
  import {
18
18
  runWatchdogCommand
19
- } from "./chunk-BHCDOHSK.js";
20
- import "./chunk-IXBIAX76.js";
21
- import "./chunk-M3A2WRXM.js";
19
+ } from "./chunk-LYL5GG2F.js";
20
+ import "./chunk-K6KL2VD6.js";
21
+ import "./chunk-OB6OXLPC.js";
22
22
  import "./chunk-DGUM43GV.js";
23
23
 
24
24
  // packages/cli/src/commands/session.ts
package/dist/start.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  loadConfig,
4
4
  startPipeline,
5
5
  startWorker
6
- } from "./chunk-M3A2WRXM.js";
6
+ } from "./chunk-OB6OXLPC.js";
7
7
  import "./chunk-DGUM43GV.js";
8
8
 
9
9
  // packages/runtime/src/start.ts
@@ -0,0 +1,191 @@
1
+ # Hivemind Tool Parity Plan
2
+
3
+ *Goal: Bring Hivemind tool capabilities to parity with OpenClaw/Pi, then beyond.*
4
+
5
+ *Created: 2026-03-01 | Last updated: 2026-03-01*
6
+
7
+ ---
8
+
9
+ ## Context
10
+
11
+ OpenClaw uses [Pi](https://github.com/badlogic/pi-mono/) as its underlying coding agent (see [Armin's article](https://lucumr.pocoo.org/2026/1/31/pi/)). Pi provides 4 core tools: `read`, `write`, `edit`, `bash`. OpenClaw layers orchestration on top: messaging, scheduling, sub-agents, browser control, vision, TTS, device control, and more.
12
+
13
+ Hivemind is a **headless messaging agent** (not a TUI coding agent like Pi), but it needs equivalent capabilities. Hivemind's competitive edge is its **3-layer semantic memory** (L2 episodic + L3 semantic), which neither Pi nor OpenClaw have.
14
+
15
+ **Principle: Build, don't integrate.** Study Pi's source for inspiration, write our own implementations. No external dependency risk, tailored to our headless/messaging paradigm.
16
+
17
+ ---
18
+
19
+ ## What's Already Implemented ✅
20
+
21
+ ### Core Tools (shipped in v0.6.0-v0.7.2)
22
+ | Tool | Status | Notes |
23
+ |------|--------|-------|
24
+ | `shell` | ✅ Shipped | Workspace-scoped, configurable timeout |
25
+ | `read_file` | ✅ Shipped | With offset/limit for large files |
26
+ | `write_file` | ✅ Shipped | Auto-creates directories |
27
+ | `edit_file` | ✅ Shipped | Exact-match find-and-replace |
28
+ | `list_files` | ✅ Shipped | Directory listing |
29
+ | `web_search` | ✅ Shipped | Brave Search API |
30
+ | `web_fetch` | ✅ Shipped | URL → readable text |
31
+
32
+ ### Memory Tools (shipped in v0.7.0)
33
+ | Tool | Status | Notes |
34
+ |------|--------|-------|
35
+ | `memory_search` | ✅ Shipped | L2 semantic search per context |
36
+ | `memory_contexts` | ✅ Shipped | List all contexts |
37
+ | `memory_l3` | ✅ Shipped | View promoted knowledge |
38
+ | `memory_cross_search` | ✅ Shipped | Search across all contexts |
39
+
40
+ ### Infrastructure (shipped in v0.7.0)
41
+ | Feature | Status | Notes |
42
+ |---------|--------|-------|
43
+ | Agentic tool-use loop | ✅ Shipped | OpenAI-compatible function calling via OpenRouter, max 25 iterations |
44
+ | Tool registry | ✅ Shipped | Pluggable registration system |
45
+ | Session persistence | ✅ Shipped | JSONL per context, survives restarts |
46
+ | Lossless compaction | ✅ Shipped | Saves episodes to L2 before summarizing |
47
+ | MEMORY.md | ✅ Shipped | Global + per-context agent-managed memory files |
48
+ | Skills discovery | ✅ Shipped | Auto-scan workspace/skills/*/SKILL.md |
49
+ | Events system | ✅ Shipped | File-based immediate/one-shot/periodic scheduling |
50
+ | Token budget management | ✅ Shipped | Context limit + response reserve |
51
+ | Dashboard | ✅ Shipped | Request inspector, memory browser, context overview |
52
+ | Service install | ✅ Shipped | launchd services (agent + memory daemon + watchdog) |
53
+ | One-line installer | ✅ Shipped | `curl -sL api.sesame.space/api/v1/hivemind/install | bash -s -- <key>` |
54
+
55
+ ---
56
+
57
+ ## What's Not Implemented Yet
58
+
59
+ ### Tier 1 — Essential (Phase 1-2) ✅ COMPLETE
60
+
61
+ | # | Tool/Feature | Status | Tools Added |
62
+ |---|-------------|--------|------------|
63
+ | 1 | **Event/scheduling tools** | ✅ Shipped | `create_event`, `list_events`, `delete_event` |
64
+ | 2 | **Enhanced Sesame messaging** | ✅ Shipped | `send_message` |
65
+ | 3 | **Sub-agent spawning** | ✅ Shipped | `spawn_agent`, `list_agents`, `kill_agent` |
66
+ | 4 | **Web browsing** | ✅ Shipped | `browse` (extract/screenshot/click/type/evaluate) |
67
+
68
+ ### Tier 2 — Important (Phase 3) ✅ COMPLETE
69
+
70
+ | # | Tool/Feature | Status | Tools Added |
71
+ |---|-------------|--------|------------|
72
+ | 5 | **Image/vision analysis** | ✅ Shipped | `analyze_image` |
73
+ | 6 | **Git operations** | ✅ Shipped | `git_status`, `git_diff`, `git_commit`, `git_log`, `git_push` |
74
+ | 7 | **Cross-context messaging** | ⬚ Deferred | (existing cross-context search covers most cases) |
75
+
76
+ ### Tier 3 — System & Mac Capabilities ✅ COMPLETE
77
+
78
+ | # | Tool/Feature | Status | Tools Added |
79
+ |---|-------------|--------|------------|
80
+ | 8 | **System management** | ✅ Shipped | `system_info`, `process_list`, `process_kill`, `service_control`, `disk_usage`, `network_info` |
81
+ | 9 | **macOS automation** | ✅ Shipped | `run_applescript`, `notify`, `clipboard_read`, `clipboard_write`, `open_url`, `screenshot` |
82
+ | 10 | **Data handling** | ✅ Shipped | `sqlite_query`, `archive_create`, `archive_extract`, `pdf_extract` |
83
+ | 11 | **HTTP server/client** | ✅ Shipped | `http_serve`, `http_stop`, `http_request` |
84
+ | 12 | **File watching** | ✅ Shipped | `watch_start`, `watch_stop`, `watch_list` |
85
+
86
+ ### Tier 4 — Future (not yet implemented)
87
+
88
+ | # | Tool/Feature | What It Does | Notes |
89
+ |---|-------------|-------------|-------|
90
+ | 13 | **Skills/extensions system** | Formalized hot-reloadable skills with tool registration | Inspired by Pi's extension system |
91
+ | 14 | **TTS** | Text-to-speech output via API | ElevenLabs API |
92
+ | 15 | **SSH** | Remote machine access | Could use shell + ssh CLI |
93
+
94
+ ---
95
+
96
+ ## Implementation Plan
97
+
98
+ ### Phase 1: Event Tools + Enhanced Messaging
99
+ **Estimate: 1-2 coding sessions**
100
+
101
+ ```
102
+ packages/runtime/src/tools/
103
+ events.ts — NEW: create_event, list_events, delete_event
104
+ messaging.ts — NEW: send_message (to any Sesame channel/user)
105
+ ```
106
+
107
+ **Event tools:** Wrap the existing `EventsWatcher` infrastructure. Agent can create one-shot (reminders) and periodic (cron-like) events. Events fire as messages processed by the agent.
108
+
109
+ **Messaging tools:** Use existing Sesame SDK to send messages to arbitrary channels. Agent can proactively reach out, not just reply to incoming messages.
110
+
111
+ **Dependencies:** None new. Uses existing EventsWatcher + Sesame SDK.
112
+
113
+ ### Phase 2: Sub-agents + Web Browsing
114
+ **Estimate: 3-4 coding sessions**
115
+
116
+ ```
117
+ packages/runtime/src/tools/
118
+ spawn.ts — NEW: spawn_agent, list_agents, kill_agent
119
+ browser.ts — NEW: browse_url, browser_action
120
+ ```
121
+
122
+ **Sub-agents:** Fork a new agent process with `hivemind start --context <name> --task "<prompt>"`. Parent tracks child PIDs. Results reported back via Sesame message or shared file. Timeout and cleanup built in.
123
+
124
+ **Web browsing:** Headless Playwright for full web interaction. Agent can navigate, click, fill forms, extract content from JS-rendered pages. Single dependency (`playwright`), but it's the right tool for this — curl/fetch can't handle SPAs, authentication flows, or dynamic content.
125
+
126
+ **Why Playwright:** We considered avoiding it (heavy dep), but web browsing is a core capability for agents that need to "do anything a human can do on a computer." Pi/OpenClaw use it. The alternative (shell + curl) only handles static pages.
127
+
128
+ ### Phase 3: Vision + Git
129
+ **Estimate: 1-2 coding sessions**
130
+
131
+ ```
132
+ packages/runtime/src/tools/
133
+ vision.ts — NEW: analyze_image
134
+ git.ts — NEW: git_status, git_diff, git_commit, git_push
135
+ ```
136
+
137
+ **Vision:** Single HTTP call to OpenRouter with vision-capable model. Accept image URL or base64.
138
+
139
+ **Git:** Thin wrappers around git CLI with structured output. Reduces token usage vs raw `shell` git output. Safety: confirmation before push.
140
+
141
+ ### Phase 4: Skills/Extensions System
142
+ **Estimate: 2-3 coding sessions**
143
+
144
+ Formalize the existing skills discovery into a proper extension system:
145
+ - Skills can register new tools dynamically
146
+ - Hot-reload on file change (inspired by Pi's extension system)
147
+ - Agent can write and install its own skills
148
+ - Skills persist across restarts
149
+
150
+ ---
151
+
152
+ ## Architecture Notes
153
+
154
+ ### Tool Registration Pattern
155
+ All tools follow the existing registry pattern:
156
+ ```typescript
157
+ // Each tool file exports a register function
158
+ export function registerEventTools(registry: ToolRegistry, dataDir: string): void {
159
+ registry.register("create_event", description, schema, executor);
160
+ // ...
161
+ }
162
+ ```
163
+
164
+ ### Security Model
165
+ - **Shell:** Workspace-scoped, configurable timeout (existing)
166
+ - **Files:** Scoped to workspace (existing)
167
+ - **Messaging:** Authenticated via existing Sesame connection
168
+ - **Sub-agents:** Inherit parent permissions, isolated context
169
+ - **Events:** File-based, local only
170
+ - **Browser:** Sandboxed Playwright instance, no persistent state
171
+ - **Git:** Confirmation before push
172
+
173
+ ### What Hivemind Has That OpenClaw/Pi Don't
174
+ - **3-layer semantic memory** (L2 episodic + L3 semantic with automatic promotion)
175
+ - **Cross-context knowledge sharing** with isolation by default
176
+ - **Memory-augmented responses** (every LLM call enriched with relevant history)
177
+ - **Dashboard** for debugging LLM calls + memory state
178
+ - **Token budget management** with configurable limits
179
+
180
+ This isn't just parity — it's parity + memory, which is the competitive edge.
181
+
182
+ ---
183
+
184
+ ## Superseded Documents
185
+
186
+ - `TOOL-USE-DESIGN.md` (root) — Original tool architecture design. **Fully implemented** in v0.6.0-v0.7.2. Kept for historical reference.
187
+ - `DASHBOARD-PLAN.md` (root) — Dashboard design. **Fully implemented**. Kept for reference.
188
+
189
+ ---
190
+
191
+ *This is the authoritative plan for Hivemind tool development. Update this document as phases complete.*
package/package.json CHANGED
@@ -1,34 +1,33 @@
1
1
  {
2
2
  "name": "@sesamespace/hivemind",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Cognitive architecture for AI agents with multi-layered memory",
5
- "type": "module",
6
- "bin": {
7
- "hivemind": "dist/main.js"
5
+ "scripts": {
6
+ "build": "tsup",
7
+ "typecheck": "pnpm -r typecheck",
8
+ "lint": "pnpm -r lint",
9
+ "test": "pnpm -r test",
10
+ "prepublishOnly": "pnpm install --frozen-lockfile && tsup"
8
11
  },
9
- "files": [
10
- "dist/",
11
- "config/",
12
- "packages/memory/",
13
- "install.sh",
14
- "README.md"
15
- ],
16
12
  "engines": {
17
- "node": ">=20.0.0"
13
+ "node": ">=20.0.0",
14
+ "pnpm": ">=9.0.0"
18
15
  },
19
16
  "dependencies": {
20
- "ws": "^8.18.0"
17
+ "@sesamespace/sdk": "^0.1.6",
18
+ "smol-toml": "^1.6.0",
19
+ "axios": "^1.7.9",
20
+ "express": "^4.21.2"
21
+ },
22
+ "devDependencies": {
23
+ "tsup": "^8.5.1"
24
+ },
25
+ "bin": {
26
+ "hivemind": "dist/main.js"
21
27
  },
22
- "keywords": [
23
- "hivemind",
24
- "ai-agent",
25
- "memory",
26
- "sesame",
27
- "cognitive-architecture"
28
- ],
29
- "license": "UNLICENSED",
30
- "repository": {
31
- "type": "git",
32
- "url": "https://github.com/baileydavis2026/hivemind"
28
+ "pnpm": {
29
+ "onlyBuiltDependencies": [
30
+ "better-sqlite3"
31
+ ]
33
32
  }
34
33
  }