monomind 2.5.4 → 2.5.5

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 (71) hide show
  1. package/README.md +10 -3
  2. package/package.json +1 -1
  3. package/packages/@monomind/cli/.claude/commands/mastermind/topology.md +121 -0
  4. package/packages/@monomind/cli/.claude/commands/mastermind.md +121 -102
  5. package/packages/@monomind/cli/.claude/helpers/control-start.cjs +22 -2
  6. package/packages/@monomind/cli/.claude/helpers/control-stop.cjs +95 -0
  7. package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +6 -0
  8. package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +13 -5
  9. package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +33 -10
  10. package/packages/@monomind/cli/.claude/helpers/utils/system-pressure.cjs +46 -0
  11. package/packages/@monomind/cli/.claude/settings.json +448 -0
  12. package/packages/@monomind/cli/README.md +10 -3
  13. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +1 -1
  14. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +1 -1
  15. package/packages/@monomind/cli/dist/src/commands/cleanup.js +53 -1
  16. package/packages/@monomind/cli/dist/src/commands/config.js +1 -1
  17. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +1 -1
  18. package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -1
  19. package/packages/@monomind/cli/dist/src/commands/index.js +4 -0
  20. package/packages/@monomind/cli/dist/src/commands/init-wizard.js +3 -3
  21. package/packages/@monomind/cli/dist/src/commands/init.js +2 -2
  22. package/packages/@monomind/cli/dist/src/commands/mcp.js +2 -2
  23. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +8 -7
  24. package/packages/@monomind/cli/dist/src/commands/memory-list.js +2 -2
  25. package/packages/@monomind/cli/dist/src/commands/memory.d.ts +1 -1
  26. package/packages/@monomind/cli/dist/src/commands/memory.js +4 -4
  27. package/packages/@monomind/cli/dist/src/commands/neural-core.js +1 -1
  28. package/packages/@monomind/cli/dist/src/commands/performance.js +8 -9
  29. package/packages/@monomind/cli/dist/src/commands/status.js +2 -2
  30. package/packages/@monomind/cli/dist/src/commands/swarm.js +1 -1
  31. package/packages/@monomind/cli/dist/src/index.js +1 -1
  32. package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +2 -2
  33. package/packages/@monomind/cli/dist/src/init/executor.js +13 -6
  34. package/packages/@monomind/cli/dist/src/init/helpers-generator.js +66 -3
  35. package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.d.ts +1 -1
  36. package/packages/@monomind/cli/dist/src/init/shared-instructions-generator.js +1 -1
  37. package/packages/@monomind/cli/dist/src/init/statusline-generator.d.ts +1 -1
  38. package/packages/@monomind/cli/dist/src/init/statusline-generator.js +2 -2
  39. package/packages/@monomind/cli/dist/src/init/types.d.ts +1 -1
  40. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.d.ts +1 -1
  41. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +3 -3
  42. package/packages/@monomind/cli/dist/src/mcp-client.d.ts +13 -51
  43. package/packages/@monomind/cli/dist/src/mcp-client.js +79 -134
  44. package/packages/@monomind/cli/dist/src/mcp-server.js +4 -3
  45. package/packages/@monomind/cli/dist/src/mcp-tools/browser-tools.js +45 -19
  46. package/packages/@monomind/cli/dist/src/mcp-tools/guidance-tools.js +1 -1
  47. package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +2 -2
  48. package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +2 -2
  49. package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +1 -1
  50. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +2 -2
  51. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +22 -9
  52. package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.d.ts +1 -1
  53. package/packages/@monomind/cli/dist/src/mcp-tools/neural-tools.js +3 -3
  54. package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +1 -1
  55. package/packages/@monomind/cli/dist/src/memory/embedding-operations.js +2 -2
  56. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +53 -26
  57. package/packages/@monomind/cli/dist/src/memory/intelligence.js +2 -2
  58. package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -3
  59. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +24 -10
  60. package/packages/@monomind/cli/dist/src/memory/memory-crud.js +2 -2
  61. package/packages/@monomind/cli/dist/src/memory/memory-initializer.d.ts +2 -2
  62. package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +6 -6
  63. package/packages/@monomind/cli/dist/src/memory/memory-read.js +4 -4
  64. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +12 -6
  65. package/packages/@monomind/cli/dist/src/orgrt/mailbox.d.ts +1 -0
  66. package/packages/@monomind/cli/dist/src/orgrt/mailbox.js +4 -0
  67. package/packages/@monomind/cli/dist/src/orgrt/policy.js +1 -1
  68. package/packages/@monomind/cli/dist/src/orgrt/server.js +25 -0
  69. package/packages/@monomind/cli/dist/src/ui/dashboard.html +327 -611
  70. package/packages/@monomind/cli/dist/src/ui/server.mjs +165 -22
  71. package/packages/@monomind/cli/package.json +1 -1
@@ -5,6 +5,7 @@ import os from 'os';
5
5
  import crypto from 'crypto';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { createRequire } from 'module';
8
+ import { spawn } from 'child_process';
8
9
  import { collectAll, getWatchPaths, collectProject, collectSessions, collectSwarm, collectSwarmHistory, appendSwarmHistory, collectSwarmEvents, getSwarmDataSize, cleanSwarmData, collectAgents, collectTokens, collectHooks, collectKnowledge, collectMetrics, collectMemory, collectMemoryFiles, collectSystem } from './collector.mjs';
9
10
  import { addSseClient, removeSseClient, broadcast, getSseClientCount, closeSseClients, addMmClient, removeMmClient, broadcastMm, getMmClientCount } from './sse-manager.mjs';
10
11
 
@@ -226,6 +227,23 @@ let _runDbInsertStmt = null; // prepared INSERT statement
226
227
 
227
228
  const _require = createRequire(import.meta.url);
228
229
 
230
+ /**
231
+ * Guard against a stale PID file outliving its process and the OS recycling
232
+ * that PID for an unrelated process — verify the live process actually looks
233
+ * like ours (node/npx running something under this project or monomind)
234
+ * before signaling it or reporting it as "running".
235
+ */
236
+ function looksLikeOurProcess(pid, dir) {
237
+ try {
238
+ const { execSync } = _require('child_process');
239
+ const cmd = execSync(`ps -p ${pid} -o command=`, { timeout: 2000, encoding: 'utf-8' }).trim();
240
+ const looksLikeNode = cmd.includes('node') || cmd.includes('npx') || cmd.includes('npm exec');
241
+ return looksLikeNode && (cmd.includes('monomind') || cmd.includes('monograph') || cmd.includes(dir));
242
+ } catch {
243
+ return false;
244
+ }
245
+ }
246
+
229
247
  async function _initRunDb(monoHome) {
230
248
  try {
231
249
  const initSqlJs = _require('sql.js');
@@ -410,6 +428,7 @@ function appendToFile(filePath, line) {
410
428
  // reach /main/.git, then up one more to /main/ for the monomind data root.
411
429
  // Falls back to the working directory if git isn't available.
412
430
  const _gitMonomindCache = new Map();
431
+ const _MAX_GIT_CACHE = 100;
413
432
  function _getGitMonomindDir(workDir) {
414
433
  if (!workDir) return null;
415
434
  if (_gitMonomindCache.has(workDir)) return _gitMonomindCache.get(workDir);
@@ -433,6 +452,10 @@ function _getGitMonomindDir(workDir) {
433
452
  }
434
453
  } catch {}
435
454
  if (!result) result = path.join(workDir, '.monomind'); // fallback
455
+ if (_gitMonomindCache.size >= _MAX_GIT_CACHE) {
456
+ const oldest = _gitMonomindCache.keys().next().value;
457
+ _gitMonomindCache.delete(oldest);
458
+ }
436
459
  _gitMonomindCache.set(workDir, result);
437
460
  return result;
438
461
  }
@@ -629,10 +652,15 @@ function bindServer(server, port) {
629
652
  * Falls back to cwd in session files, then to direct slug replacement.
630
653
  */
631
654
  const _slugPathCache = new Map();
655
+ const _MAX_SLUG_CACHE = 200;
632
656
 
633
657
  function resolveSlugToPath(slug, projDir) {
634
658
  if (_slugPathCache.has(slug)) return _slugPathCache.get(slug);
635
659
  const resolved = _resolveSlugToPathUncached(slug, projDir);
660
+ if (_slugPathCache.size >= _MAX_SLUG_CACHE) {
661
+ const oldest = _slugPathCache.keys().next().value;
662
+ _slugPathCache.delete(oldest);
663
+ }
636
664
  _slugPathCache.set(slug, resolved);
637
665
  return resolved;
638
666
  }
@@ -3186,13 +3214,17 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
3186
3214
  const qs = new URL(req.url, 'http://localhost').searchParams;
3187
3215
  const dir = qs.get('dir') || projectDir || process.cwd();
3188
3216
  const d = path.resolve(dir || process.cwd());
3189
- const pidPath = path.join(d, '.monomind', 'monograph.watch.pid');
3190
3217
  let running = false, pid = null;
3191
- try {
3192
- pid = parseInt(fs.readFileSync(pidPath, 'utf-8').trim(), 10);
3193
- process.kill(pid, 0);
3194
- running = true;
3195
- } catch {}
3218
+ for (const pidName of ['monograph.watch.pid', 'monograph-watch.pid']) {
3219
+ try {
3220
+ const pp = path.join(d, '.monomind', pidName);
3221
+ pid = parseInt(fs.readFileSync(pp, 'utf-8').trim(), 10);
3222
+ process.kill(pid, 0);
3223
+ if (!looksLikeOurProcess(pid, d)) continue;
3224
+ running = true;
3225
+ break;
3226
+ } catch {}
3227
+ }
3196
3228
  res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
3197
3229
  res.end(JSON.stringify({ running, pid }));
3198
3230
  } catch (err) {
@@ -3210,13 +3242,17 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
3210
3242
  const d = path.resolve(dir || process.cwd());
3211
3243
  const pidPath = path.join(d, '.monomind', 'monograph.watch.pid');
3212
3244
  let wasRunning = false;
3213
- try {
3214
- const pid = parseInt(fs.readFileSync(pidPath, 'utf-8').trim(), 10);
3215
- process.kill(pid, 0);
3216
- wasRunning = true;
3217
- process.kill(pid, 'SIGTERM');
3218
- try { fs.unlinkSync(pidPath); } catch {}
3219
- } catch {}
3245
+ for (const pidName of ['monograph.watch.pid', 'monograph-watch.pid']) {
3246
+ try {
3247
+ const pp = path.join(d, '.monomind', pidName);
3248
+ const pid = parseInt(fs.readFileSync(pp, 'utf-8').trim(), 10);
3249
+ process.kill(pid, 0);
3250
+ if (!looksLikeOurProcess(pid, d)) continue;
3251
+ wasRunning = true;
3252
+ process.kill(pid, 'SIGTERM');
3253
+ try { fs.unlinkSync(pp); } catch {}
3254
+ } catch {}
3255
+ }
3220
3256
 
3221
3257
  if (wasRunning) {
3222
3258
  res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
@@ -3237,6 +3273,28 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
3237
3273
  return;
3238
3274
  }
3239
3275
 
3276
+ // -------------------------------------------------- POST /api/shutdown
3277
+ if (req.method === 'POST' && url === '/api/shutdown') {
3278
+ res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
3279
+ res.end(JSON.stringify({ ok: true }));
3280
+ // Kill monograph watcher if running
3281
+ const d = projectDir || process.cwd();
3282
+ for (const pidName of ['monograph.watch.pid', 'monograph-watch.pid']) {
3283
+ try {
3284
+ const wp = path.join(d, '.monomind', pidName);
3285
+ if (fs.statSync(wp).size > 32) continue;
3286
+ const wpid = parseInt(fs.readFileSync(wp, 'utf-8').trim(), 10);
3287
+ if (!Number.isInteger(wpid) || wpid <= 0) { try { fs.unlinkSync(wp); } catch {} continue; }
3288
+ if (looksLikeOurProcess(wpid, d)) process.kill(wpid, 'SIGTERM');
3289
+ try { fs.unlinkSync(wp); } catch {}
3290
+ } catch {}
3291
+ }
3292
+ // Remove control.json so startup knows we're gone
3293
+ try { fs.unlinkSync(path.join(d, '.monomind', 'control.json')); } catch {}
3294
+ setTimeout(shutdown, 100);
3295
+ return;
3296
+ }
3297
+
3240
3298
  // -------------------------------------------------- POST /api/mcp/call
3241
3299
  if (req.method === 'POST' && url === '/api/mcp/call') {
3242
3300
  let body = '';
@@ -4559,6 +4617,43 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
4559
4617
  return;
4560
4618
  }
4561
4619
 
4620
+ // POST /api/org/:name/agent/:roleId/avatar — set (or clear) a custom avatar for one
4621
+ // role, stored inline as a data URL on the role itself so it travels with the org
4622
+ // config export/import. Body: { avatarDataUrl } — null/omitted avatarDataUrl clears
4623
+ // the custom avatar and reverts to the built-in library picture. `dir` (like every
4624
+ // other dir-accepting route in this file, POST included — see /goals above) comes
4625
+ // from the query string, not the body.
4626
+ if (req.method === 'POST' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/agent\/[a-z0-9][a-z0-9_-]{0,63}\/avatar$/i.test(url)) {
4627
+ let body = '';
4628
+ for await (const chunk of req) { body += chunk; if (body.length > 8388608) { req.destroy(); break; } }
4629
+ try {
4630
+ const parts = url.split('/');
4631
+ const orgName = decodeURIComponent(parts[3]);
4632
+ const roleId = decodeURIComponent(parts[5]);
4633
+ if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('{"ok":false,"error":"Invalid org name"}'); return; }
4634
+ if (roleId.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(roleId)) { res.writeHead(400); res.end('{"ok":false,"error":"Invalid role id"}'); return; }
4635
+ const parsed = JSON.parse(body);
4636
+ const avatarDataUrl = parsed.avatarDataUrl;
4637
+ if (avatarDataUrl != null && (typeof avatarDataUrl !== 'string' || avatarDataUrl.length > 2_000_000 || !/^data:image\/(png|jpe?g|webp|gif);base64,[A-Za-z0-9+/=]+$/.test(avatarDataUrl))) {
4638
+ res.writeHead(400); res.end('{"ok":false,"error":"Invalid or oversized avatarDataUrl"}'); return;
4639
+ }
4640
+ const _avatarQs = new URL(req.url, 'http://localhost').searchParams;
4641
+ const d = path.resolve(_avatarQs.get('dir') || projectDir || process.cwd());
4642
+ const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
4643
+ if (!fs.existsSync(orgFile)) { res.writeHead(404); res.end('{"ok":false,"error":"org not found"}'); return; }
4644
+ const config = JSON.parse(fs.readFileSync(orgFile, 'utf8'));
4645
+ const role = (config.roles || []).find(r => r.id === roleId);
4646
+ if (!role) { res.writeHead(404); res.end('{"ok":false,"error":"role not found"}'); return; }
4647
+ if (avatarDataUrl) role.avatar = avatarDataUrl; else delete role.avatar;
4648
+ const tmp = `${orgFile}.tmp`;
4649
+ fs.writeFileSync(tmp, JSON.stringify(config, null, 2), 'utf-8');
4650
+ fs.renameSync(tmp, orgFile);
4651
+ res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
4652
+ res.end(JSON.stringify({ ok: true }));
4653
+ } catch (e) { res.writeHead(500); res.end(JSON.stringify({ ok: false, error: String(e.message || e) })); }
4654
+ return;
4655
+ }
4656
+
4562
4657
  // GET /api/org/:name/search?q=<query> — fuzzy search across org data
4563
4658
  if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/search(\?.*)?$/i.test(url)) {
4564
4659
  try {
@@ -4967,9 +5062,11 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
4967
5062
  return;
4968
5063
  }
4969
5064
 
4970
- // GET /api/questions?dir=<projectDir> — list unanswered ask_human questions for
4971
- // every org in one project. Mirrors the existing -approvals.json sidecar convention,
4972
- // but reads this feature's .monomind/orgs/<org>/questions.json files (one per org dir).
5065
+ // GET /api/questions?dir=<projectDir> — list ask_human questions (pending and
5066
+ // answered) for every org in one project. Mirrors the existing -approvals.json
5067
+ // sidecar convention, but reads this feature's .monomind/orgs/<org>/questions.json
5068
+ // files (one per org dir). Each entry's `answer`/`answeredAt` fields (present once
5069
+ // answered, absent while pending) let the dashboard split them into tabs.
4973
5070
  if (req.method === 'GET' && url === '/api/questions') {
4974
5071
  try {
4975
5072
  const _qDir = new URL(req.url, 'http://localhost').searchParams.get('dir') || projectDir || process.cwd();
@@ -4982,7 +5079,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
4982
5079
  let data = { questions: [] };
4983
5080
  try { data = JSON.parse(fs.readFileSync(qFile, 'utf8')); } catch (_) {}
4984
5081
  for (const q of (data.questions || [])) {
4985
- if (q.answer === null || q.answer === undefined) out.push({ org: orgName, ...q });
5082
+ out.push({ org: orgName, ...q });
4986
5083
  }
4987
5084
  }
4988
5085
  }
@@ -5059,8 +5156,19 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
5059
5156
  const queuedEvent = { type: 'org:question-answered', org, role, questionId, ts: Date.now(), queued: true };
5060
5157
  appendToFile(path.join(projDir, 'data', 'mastermind-events.jsonl'), JSON.stringify(queuedEvent) + '\n').catch(() => {});
5061
5158
  broadcastMm(queuedEvent);
5159
+ // Auto-wake: a queued answer is worthless if nothing ever restarts the org to
5160
+ // drain it (unlike the direct-daemon path, this control server holds no OrgDaemon
5161
+ // instance to call autoWake() on). `org run` itself no-ops if the org's runtime.json
5162
+ // already shows a live pid, so this is safe to fire even if another process is
5163
+ // racing to start it.
5164
+ try {
5165
+ const child = spawn('npx', ['-y', 'monomind@latest', 'org', 'run', org], {
5166
+ cwd: projDir, detached: true, stdio: 'ignore',
5167
+ });
5168
+ child.unref();
5169
+ } catch (_) { /* best-effort — the answer is still queued on disk either way */ }
5062
5170
  res.writeHead(200, { 'Content-Type': 'application/json' });
5063
- res.end(JSON.stringify({ ok: true, queued: true }));
5171
+ res.end(JSON.stringify({ ok: true, queued: true, waking: true }));
5064
5172
  return;
5065
5173
  }
5066
5174
  const fwd = await fetch(`${hostUrl}/api/answer-question`, {
@@ -5496,8 +5604,10 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
5496
5604
  const _delOrgQs = new URL(req.url, 'http://localhost').searchParams;
5497
5605
  const orgsDir = path.join(path.resolve(_delOrgQs.get('dir') || projectDir || process.cwd()), '.monomind', 'orgs');
5498
5606
  const configFile = path.join(orgsDir, `${orgName}.json`);
5499
- if (!fs.existsSync(configFile)) { res.writeHead(404); res.end('{"error":"org not found"}'); return; }
5607
+ const v1ConfigFile = path.join(orgsDir, `${orgName}.v1.json`);
5608
+ if (!fs.existsSync(configFile) && !fs.existsSync(v1ConfigFile)) { res.writeHead(404); res.end('{"error":"org not found"}'); return; }
5500
5609
  // Remove all org-associated files (config + state + data)
5610
+ try { if (fs.existsSync(v1ConfigFile)) fs.unlinkSync(v1ConfigFile); } catch(_) {}
5501
5611
  const suffixes = ['', '-state', '-goals', '-routines', '-approvals', '-activity', '-issues', '-members', '-projects', '-workspaces', '-worktrees', '-environments', '-plugins', '-adapters', '-budgets', '-threads', '-secrets', '-join-requests', '-bootstrap', '-project-workspaces', '-approval-comments', '-skills'];
5502
5612
  for (const suf of suffixes) {
5503
5613
  const f = path.join(orgsDir, `${orgName}${suf}.json`);
@@ -6481,7 +6591,9 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
6481
6591
  try { _chPayload = JSON.parse(_chBody); } catch(_) {}
6482
6592
  const _chText = String(_chPayload.text || '').trim().slice(0, 4096);
6483
6593
  if (!_chText) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'text is required' })); return; }
6484
- const _chRoot = projectDir || process.cwd();
6594
+ const _chQs = new URL(req.url, 'http://localhost').searchParams;
6595
+ const _chServerRoot = path.resolve(_chQs.get('dir') || projectDir || process.cwd());
6596
+ const _chRoot = _resolveOrgProjectDir(_chOrgName, _chServerRoot) || _chServerRoot;
6485
6597
  const _chMonoDir = _getGitMonomindDir(_chRoot) || path.join(_chRoot, '.monomind');
6486
6598
  const _chRunId = activeOrgRuns.get(_chOrgName) || _getActiveRunId(_chOrgName, _chRoot);
6487
6599
  const _chEvent = { type: 'user:message', org: _chOrgName, runId: _chRunId || null, text: _chText, ts: Date.now() };
@@ -6490,7 +6602,8 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
6490
6602
  const _chRunFile = path.join(_chMonoDir, 'orgs', _chOrgName, 'runs', `${_chRunId}.jsonl`);
6491
6603
  if (fs.existsSync(_chRunFile)) await appendToFile(_chRunFile, JSON.stringify(_chEvent) + '\n');
6492
6604
  }
6493
- // Write to mailbox file so boss agent can pick up on next cycle
6605
+ // Write to mailbox file so boss agent can pick up on next cycle (durable
6606
+ // record + offline fallback — the live-delivery attempt below is best-effort)
6494
6607
  const _chMailbox = path.join(_chRoot, '.monomind', 'orgs', `${_chOrgName}-threads.json`);
6495
6608
  try {
6496
6609
  let _chThreads = { messages: [] };
@@ -6506,8 +6619,36 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
6506
6619
  const _chLine = `data: ${JSON.stringify(_chEvent)}\n\n`;
6507
6620
  for (const _cl of _chFwdClients) { try { _cl.write(_chLine); } catch(_) { _chFwdClients.delete(_cl); } }
6508
6621
  }
6622
+ // Forward to the org's live process (if any) so the message actually lands in a
6623
+ // running role's mailbox — looked up via the same file-based broker registry
6624
+ // orgrt's cross-process delivery already uses (mirrors POST /api/questions/answer).
6625
+ let _chDelivered = false;
6626
+ try {
6627
+ const _chBrokerEntryPath = path.join(os.homedir(), '.monomind', 'orgrt-broker', `${_chOrgName}.json`);
6628
+ const _chBrokerEntry = JSON.parse(fs.readFileSync(_chBrokerEntryPath, 'utf8'));
6629
+ if (Date.now() - _chBrokerEntry.updatedAt < 90000 && _chBrokerEntry.url) {
6630
+ let _chTargetRole = _chPayload.role;
6631
+ if (!_chTargetRole) {
6632
+ try {
6633
+ const _chCfg = JSON.parse(fs.readFileSync(path.join(_chRoot, '.monomind', 'orgs', `${_chOrgName}.json`), 'utf8'));
6634
+ const _chRoles = Array.isArray(_chCfg.roles) ? _chCfg.roles : [];
6635
+ const _chBoss = _chRoles.find(r => r.type === 'boss' || r.reports_to === null) || _chRoles[0];
6636
+ _chTargetRole = _chBoss?.id;
6637
+ } catch(_) {}
6638
+ }
6639
+ if (_chTargetRole) {
6640
+ const _chFwd = await fetch(`${_chBrokerEntry.url}/api/human-message`, {
6641
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
6642
+ body: JSON.stringify({ org: _chOrgName, role: _chTargetRole, text: _chText }),
6643
+ signal: AbortSignal.timeout(5000),
6644
+ });
6645
+ const _chFwdData = await _chFwd.json().catch(() => ({}));
6646
+ _chDelivered = !!(_chFwd.ok && _chFwdData.ok);
6647
+ }
6648
+ }
6649
+ } catch(_) {}
6509
6650
  res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
6510
- res.end(JSON.stringify({ ok: true }));
6651
+ res.end(JSON.stringify({ ok: true, delivered: _chDelivered }));
6511
6652
  } catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
6512
6653
  return;
6513
6654
  }
@@ -6942,12 +7083,14 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
6942
7083
  currentPort = null;
6943
7084
  currentUrl = null;
6944
7085
  activeServer = null;
7086
+ process.exit(0);
6945
7087
  });
6946
7088
  });
6947
7089
  }
6948
7090
 
6949
7091
  process.once('SIGTERM', shutdown);
6950
7092
  process.once('SIGINT', shutdown);
7093
+ process.once('SIGHUP', shutdown);
6951
7094
 
6952
7095
  // ---------------------------------------------------------- Auto-open
6953
7096
  if (openBrowser) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "2.5.4",
3
+ "version": "2.5.5",
4
4
  "type": "module",
5
5
  "description": "CLI engine for Monomind \u2014 an open-source MCP server that extends Claude Code with a codebase knowledge graph (tree-sitter + SQLite), persistent memory, multi-agent task coordination, and session hooks. MIT licensed, fully local.",
6
6
  "main": "dist/src/index.js",