helixmind 0.2.1 → 0.2.3

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.

Potentially problematic release.


This version of helixmind might be problematic. Click here for more details.

Files changed (53) hide show
  1. package/README.md +31 -0
  2. package/dist/cli/agent/autonomous.d.ts +14 -0
  3. package/dist/cli/agent/autonomous.d.ts.map +1 -1
  4. package/dist/cli/agent/autonomous.js +22 -0
  5. package/dist/cli/agent/autonomous.js.map +1 -1
  6. package/dist/cli/agent/monitor/alerter.d.ts +29 -0
  7. package/dist/cli/agent/monitor/alerter.d.ts.map +1 -0
  8. package/dist/cli/agent/monitor/alerter.js +80 -0
  9. package/dist/cli/agent/monitor/alerter.js.map +1 -0
  10. package/dist/cli/agent/monitor/baseline.d.ts +14 -0
  11. package/dist/cli/agent/monitor/baseline.d.ts.map +1 -0
  12. package/dist/cli/agent/monitor/baseline.js +157 -0
  13. package/dist/cli/agent/monitor/baseline.js.map +1 -0
  14. package/dist/cli/agent/monitor/prompts.d.ts +9 -0
  15. package/dist/cli/agent/monitor/prompts.d.ts.map +1 -0
  16. package/dist/cli/agent/monitor/prompts.js +103 -0
  17. package/dist/cli/agent/monitor/prompts.js.map +1 -0
  18. package/dist/cli/agent/monitor/responder.d.ts +12 -0
  19. package/dist/cli/agent/monitor/responder.d.ts.map +1 -0
  20. package/dist/cli/agent/monitor/responder.js +59 -0
  21. package/dist/cli/agent/monitor/responder.js.map +1 -0
  22. package/dist/cli/agent/monitor/scanner.d.ts +18 -0
  23. package/dist/cli/agent/monitor/scanner.d.ts.map +1 -0
  24. package/dist/cli/agent/monitor/scanner.js +81 -0
  25. package/dist/cli/agent/monitor/scanner.js.map +1 -0
  26. package/dist/cli/agent/monitor/types.d.ts +119 -0
  27. package/dist/cli/agent/monitor/types.d.ts.map +1 -0
  28. package/dist/cli/agent/monitor/types.js +5 -0
  29. package/dist/cli/agent/monitor/types.js.map +1 -0
  30. package/dist/cli/agent/monitor/watcher.d.ts +4 -0
  31. package/dist/cli/agent/monitor/watcher.d.ts.map +1 -0
  32. package/dist/cli/agent/monitor/watcher.js +214 -0
  33. package/dist/cli/agent/monitor/watcher.js.map +1 -0
  34. package/dist/cli/brain/control-protocol.d.ts +47 -1
  35. package/dist/cli/brain/control-protocol.d.ts.map +1 -1
  36. package/dist/cli/brain/control-protocol.js.map +1 -1
  37. package/dist/cli/brain/generator.d.ts +14 -0
  38. package/dist/cli/brain/generator.d.ts.map +1 -1
  39. package/dist/cli/brain/generator.js +40 -0
  40. package/dist/cli/brain/generator.js.map +1 -1
  41. package/dist/cli/checkpoints/browser.d.ts +3 -4
  42. package/dist/cli/checkpoints/browser.d.ts.map +1 -1
  43. package/dist/cli/checkpoints/browser.js +162 -135
  44. package/dist/cli/checkpoints/browser.js.map +1 -1
  45. package/dist/cli/checkpoints/store.js +1 -1
  46. package/dist/cli/checkpoints/store.js.map +1 -1
  47. package/dist/cli/commands/chat.d.ts.map +1 -1
  48. package/dist/cli/commands/chat.js +248 -2
  49. package/dist/cli/commands/chat.js.map +1 -1
  50. package/dist/cli/context/trimmer.d.ts.map +1 -1
  51. package/dist/cli/context/trimmer.js +31 -6
  52. package/dist/cli/context/trimmer.js.map +1 -1
  53. package/package.json +1 -1
@@ -80,6 +80,7 @@ const HELP_CATEGORIES = [
80
80
  { cmd: '/auto', label: '/auto', description: 'Autonomous mode' },
81
81
  { cmd: '/stop', label: '/stop', description: 'Stop autonomous mode' },
82
82
  { cmd: '/security', label: '/security', description: 'Run security audit (background)' },
83
+ { cmd: '/monitor', label: '/monitor', description: 'Continuous security monitoring' },
83
84
  { cmd: '/sessions', label: '/sessions', description: 'List all sessions & tabs' },
84
85
  { cmd: '/local', label: '/local', description: 'Local LLM setup (Ollama)' },
85
86
  ],
@@ -536,6 +537,95 @@ export async function chatCommand(options) {
536
537
  });
537
538
  }).catch(() => { });
538
539
  },
540
+ startMonitor: (mode) => {
541
+ const existingMonitor = sessionMgr.background.find(s => s.name.includes('Monitor') && s.status === 'running');
542
+ if (existingMonitor)
543
+ return existingMonitor.id;
544
+ const modeIcons = { passive: '\u{1F50D}', defensive: '\u{1F6E1}\uFE0F', active: '\u2694\uFE0F' };
545
+ const icon = modeIcons[mode] || '\u{1F6E1}\uFE0F';
546
+ const bgSession = sessionMgr.create(`${icon} Monitor`, icon, agentHistory);
547
+ bgSession.start();
548
+ wireSessionOutput(bgSession);
549
+ pushSessionCreated(serializeSession(bgSession));
550
+ // Start monitor in background (same as /monitor command)
551
+ (async () => {
552
+ try {
553
+ const { scanSystem } = await import('../agent/monitor/scanner.js');
554
+ const { buildBaseline } = await import('../agent/monitor/baseline.js');
555
+ const { runMonitorLoop } = await import('../agent/monitor/watcher.js');
556
+ const { pushMonitorStatus: pushStatus } = await import('../brain/generator.js');
557
+ const scanResult = await scanSystem({
558
+ sendMessage: async (prompt) => {
559
+ bgSession.controller.reset();
560
+ const rth = { text: '' };
561
+ const orig = bgSession.buffer.addAssistantSummary.bind(bgSession.buffer);
562
+ bgSession.buffer.addAssistantSummary = (t) => { rth.text = t; orig(t); };
563
+ await sendAgentMessage(prompt, bgSession.history, provider, project, spiralEngine, config, permissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false });
564
+ bgSession.buffer.addAssistantSummary = orig;
565
+ return rth.text;
566
+ },
567
+ isAborted: () => bgSession.controller.isAborted,
568
+ onThreat: () => { }, onDefense: () => { },
569
+ onScanComplete: (p) => bgSession.capture(`Scan: ${p}`),
570
+ onStatusUpdate: () => { }, updateStatus: () => { },
571
+ });
572
+ if (bgSession.controller.isAborted)
573
+ return;
574
+ const baseline = buildBaseline(scanResult);
575
+ if (bgSession.controller.isAborted)
576
+ return;
577
+ await runMonitorLoop({
578
+ sendMessage: async (prompt) => {
579
+ bgSession.controller.reset();
580
+ const rth = { text: '' };
581
+ const orig = bgSession.buffer.addAssistantSummary.bind(bgSession.buffer);
582
+ bgSession.buffer.addAssistantSummary = (t) => { rth.text = t; orig(t); };
583
+ await sendAgentMessage(prompt, bgSession.history, provider, project, spiralEngine, config, permissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false });
584
+ bgSession.buffer.addAssistantSummary = orig;
585
+ return rth.text;
586
+ },
587
+ isAborted: () => bgSession.controller.isAborted,
588
+ onThreat: (t) => bgSession.capture(`THREAT [${t.severity}]: ${t.title}`),
589
+ onDefense: (d) => bgSession.capture(`DEFENSE: ${d.action} \u2192 ${d.target}`),
590
+ onScanComplete: (p) => bgSession.capture(`Check: ${p}`),
591
+ onStatusUpdate: (state) => {
592
+ pushStatus({ mode: state.mode, uptime: state.uptime, threatCount: state.threats.length, defenseCount: state.defenses.length, lastScan: state.lastScan });
593
+ },
594
+ updateStatus: () => updateStatusBar(),
595
+ }, mode, baseline);
596
+ }
597
+ catch (err) {
598
+ if (!(err instanceof AgentAbortError))
599
+ bgSession.capture(`Monitor error: ${err}`);
600
+ }
601
+ sessionMgr.complete(bgSession.id, { text: 'Monitor ended', steps: [], errors: bgSession.controller.isAborted ? ['Stopped'] : [], durationMs: bgSession.elapsed });
602
+ pushSessionUpdate(serializeSession(bgSession));
603
+ })();
604
+ return bgSession.id;
605
+ },
606
+ stopMonitor: () => {
607
+ const monitorSession = sessionMgr.background.find(s => s.name.includes('Monitor') && s.status === 'running');
608
+ if (!monitorSession)
609
+ return false;
610
+ monitorSession.abort();
611
+ pushSessionUpdate(serializeSession(monitorSession));
612
+ return true;
613
+ },
614
+ handleMonitorCommand: (command, params) => {
615
+ if (command === 'stop_monitor') {
616
+ const ms = sessionMgr.background.find(s => s.name.includes('Monitor') && s.status === 'running');
617
+ if (ms) {
618
+ ms.abort();
619
+ pushSessionUpdate(serializeSession(ms));
620
+ }
621
+ }
622
+ // Other commands (set_mode, rescan, unblock_ip) can be extended here
623
+ },
624
+ handleApprovalResponse: (requestId, approved) => {
625
+ import('../agent/monitor/alerter.js').then(({ resolveApproval }) => {
626
+ resolveApproval(requestId, approved);
627
+ }).catch(() => { });
628
+ },
539
629
  getFindings: () => [...collectedFindings],
540
630
  getBugs: () => bugJournal.getAllBugs().map(b => ({
541
631
  id: b.id,
@@ -639,6 +729,10 @@ export async function chatCommand(options) {
639
729
  });
640
730
  }).catch(() => { });
641
731
  },
732
+ startMonitor: () => '',
733
+ stopMonitor: () => false,
734
+ handleMonitorCommand: () => { },
735
+ handleApprovalResponse: () => { },
642
736
  getFindings: () => [...collectedFindings],
643
737
  getBugs: () => bugJournal.getAllBugs().map(b => ({
644
738
  id: b.id, description: b.description, file: b.file, line: b.line,
@@ -1013,6 +1107,11 @@ export async function chatCommand(options) {
1013
1107
  renderInfo(chalk.yellow(`${r.messagesRemoved} message(s) reverted`));
1014
1108
  if (r.filesReverted > 0)
1015
1109
  renderInfo(chalk.yellow(`${r.filesReverted} file(s) reverted`));
1110
+ // Restore user text into readline input
1111
+ if (browserResult.messageText) {
1112
+ rl.line = browserResult.messageText;
1113
+ rl.cursor = browserResult.messageText.length;
1114
+ }
1016
1115
  }
1017
1116
  }
1018
1117
  catch {
@@ -1238,6 +1337,108 @@ export async function chatCommand(options) {
1238
1337
  });
1239
1338
  return;
1240
1339
  }
1340
+ if (action === 'monitor') {
1341
+ // Monitor mode — runs as background session with continuous watch loop
1342
+ const monitorMode = goal || 'passive';
1343
+ const existingMonitor = sessionMgr.background.find(s => s.name.includes('Monitor') && s.status === 'running');
1344
+ if (existingMonitor) {
1345
+ renderInfo('Monitor already running. Use /stop to stop it first.');
1346
+ return;
1347
+ }
1348
+ const modeIcons = { passive: '\u{1F50D}', defensive: '\u{1F6E1}\uFE0F', active: '\u2694\uFE0F' };
1349
+ const icon = modeIcons[monitorMode] || '\u{1F6E1}\uFE0F';
1350
+ const bgSession = sessionMgr.create(`${icon} Monitor`, icon, agentHistory);
1351
+ bgSession.start();
1352
+ renderInfo(`${chalk.hex('#ff6600')(icon)} Monitor started (${monitorMode}) ${chalk.dim(`[session ${bgSession.id}]`)}`);
1353
+ updateStatusBar();
1354
+ // Run monitor loop in background — user keeps prompt
1355
+ (async () => {
1356
+ try {
1357
+ const { scanSystem } = await import('../agent/monitor/scanner.js');
1358
+ const { buildBaseline } = await import('../agent/monitor/baseline.js');
1359
+ const { runMonitorLoop } = await import('../agent/monitor/watcher.js');
1360
+ const { pushMonitorStatus } = await import('../brain/generator.js');
1361
+ // Phase 1: Full system scan
1362
+ bgSession.capture('Phase 1: Scanning system...');
1363
+ const scanResult = await scanSystem({
1364
+ sendMessage: async (prompt) => {
1365
+ bgSession.controller.reset();
1366
+ const resultTextHolder = { text: '' };
1367
+ const origAddSummary = bgSession.buffer.addAssistantSummary.bind(bgSession.buffer);
1368
+ bgSession.buffer.addAssistantSummary = (t) => {
1369
+ resultTextHolder.text = t;
1370
+ origAddSummary(t);
1371
+ };
1372
+ await sendAgentMessage(prompt, bgSession.history, provider, project, spiralEngine, config, permissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false });
1373
+ bgSession.buffer.addAssistantSummary = origAddSummary;
1374
+ return resultTextHolder.text;
1375
+ },
1376
+ isAborted: () => bgSession.controller.isAborted,
1377
+ onThreat: () => { },
1378
+ onDefense: () => { },
1379
+ onScanComplete: (phase) => bgSession.capture(`Scan: ${phase}`),
1380
+ onStatusUpdate: () => updateStatusBar(),
1381
+ updateStatus: () => updateStatusBar(),
1382
+ });
1383
+ if (bgSession.controller.isAborted)
1384
+ return;
1385
+ // Phase 2: Build baseline
1386
+ bgSession.capture('Phase 2: Building security baseline...');
1387
+ const baseline = buildBaseline(scanResult);
1388
+ bgSession.capture(`Baseline: ${baseline.processes.length} processes, ${baseline.ports.length} ports`);
1389
+ if (bgSession.controller.isAborted)
1390
+ return;
1391
+ // Phase 3: Continuous watching
1392
+ bgSession.capture(`Phase 3: Monitoring (${monitorMode} mode)...`);
1393
+ await runMonitorLoop({
1394
+ sendMessage: async (prompt) => {
1395
+ bgSession.controller.reset();
1396
+ const resultTextHolder = { text: '' };
1397
+ const origAddSummary = bgSession.buffer.addAssistantSummary.bind(bgSession.buffer);
1398
+ bgSession.buffer.addAssistantSummary = (t) => {
1399
+ resultTextHolder.text = t;
1400
+ origAddSummary(t);
1401
+ };
1402
+ await sendAgentMessage(prompt, bgSession.history, provider, project, spiralEngine, config, permissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false });
1403
+ bgSession.buffer.addAssistantSummary = origAddSummary;
1404
+ return resultTextHolder.text;
1405
+ },
1406
+ isAborted: () => bgSession.controller.isAborted,
1407
+ onThreat: (threat) => {
1408
+ bgSession.capture(`THREAT [${threat.severity}]: ${threat.title}`);
1409
+ },
1410
+ onDefense: (defense) => {
1411
+ bgSession.capture(`DEFENSE: ${defense.action} \u2192 ${defense.target}`);
1412
+ },
1413
+ onScanComplete: (phase) => bgSession.capture(`Check: ${phase}`),
1414
+ onStatusUpdate: (state) => {
1415
+ pushMonitorStatus({
1416
+ mode: state.mode,
1417
+ uptime: state.uptime,
1418
+ threatCount: state.threats.length,
1419
+ defenseCount: state.defenses.length,
1420
+ lastScan: state.lastScan,
1421
+ });
1422
+ updateStatusBar();
1423
+ },
1424
+ updateStatus: () => updateStatusBar(),
1425
+ }, monitorMode, baseline);
1426
+ }
1427
+ catch (err) {
1428
+ if (!(err instanceof AgentAbortError)) {
1429
+ bgSession.capture(`Monitor error: ${err}`);
1430
+ }
1431
+ }
1432
+ sessionMgr.complete(bgSession.id, {
1433
+ text: 'Monitor session ended',
1434
+ steps: [],
1435
+ errors: bgSession.controller.isAborted ? ['Stopped by user'] : [],
1436
+ durationMs: bgSession.elapsed,
1437
+ });
1438
+ updateStatusBar();
1439
+ })();
1440
+ return;
1441
+ }
1241
1442
  if (action === 'start') {
1242
1443
  // Check if autonomous is already running
1243
1444
  const existingAuto = sessionMgr.background.find(s => s.name.includes('Auto') && s.status === 'running');
@@ -1351,8 +1552,9 @@ export async function chatCommand(options) {
1351
1552
  showPrompt();
1352
1553
  return;
1353
1554
  }
1354
- // Render user message explicitly so it persists in the chat scroll history.
1355
- // Readline's prompt echo can be overwritten by activity indicator / agent output.
1555
+ // Clear the readline echo line (prompt + typed text) and replace with the
1556
+ // styled "You" label so the user doesn't see their input twice.
1557
+ process.stdout.write('\x1b[A\x1b[2K');
1356
1558
  renderUserMessage(input);
1357
1559
  // Track user message in session buffer
1358
1560
  sessionBuffer.addUserMessage(input);
@@ -2434,6 +2636,50 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
2434
2636
  await onAutonomous('security');
2435
2637
  }
2436
2638
  break;
2639
+ case '/monitor': {
2640
+ if (!onAutonomous)
2641
+ break;
2642
+ const { MONITOR_MODES, MONITOR_WARNINGS } = await import('../agent/autonomous.js');
2643
+ // Interactive setup: show banner + mode selection
2644
+ rl.pause();
2645
+ process.stdout.write('\n');
2646
+ const monitorMenuItems = [
2647
+ ...MONITOR_MODES.map(m => ({ label: chalk.hex('#ff6600').bold(m.label), description: m.description })),
2648
+ { label: chalk.dim('\u2715 Cancel'), description: 'Go back' },
2649
+ ];
2650
+ const modeIdx = await selectMenu(monitorMenuItems, {
2651
+ title: chalk.hex('#ff6600').bold('\u{1F6E1}\uFE0F MONITOR MODE'),
2652
+ cancelLabel: 'Cancel',
2653
+ });
2654
+ if (modeIdx < 0 || modeIdx >= MONITOR_MODES.length) {
2655
+ rl.resume();
2656
+ renderInfo('Monitor cancelled.');
2657
+ break;
2658
+ }
2659
+ const selectedMode = MONITOR_MODES[modeIdx].key;
2660
+ // Defensive/Active modes: show warning + confirm
2661
+ if (selectedMode !== 'passive') {
2662
+ const warnings = MONITOR_WARNINGS[selectedMode] || [];
2663
+ process.stdout.write('\n');
2664
+ process.stdout.write(chalk.yellow(` \u26A0\uFE0F ${selectedMode.charAt(0).toUpperCase() + selectedMode.slice(1)} mode will:\n`));
2665
+ for (const w of warnings) {
2666
+ process.stdout.write(chalk.dim(` \u2022 ${w}\n`));
2667
+ }
2668
+ process.stdout.write('\n');
2669
+ const confirmIdx = await selectMenu([
2670
+ { label: chalk.hex('#ff6600').bold('Yes, activate'), description: `Start ${selectedMode} monitor` },
2671
+ { label: 'No, go back', description: 'Cancel' },
2672
+ ], { title: chalk.yellow(`Activate ${selectedMode} monitor?`), cancelLabel: 'Cancel' });
2673
+ if (confirmIdx !== 0) {
2674
+ rl.resume();
2675
+ renderInfo('Monitor cancelled.');
2676
+ break;
2677
+ }
2678
+ }
2679
+ rl.resume();
2680
+ await onAutonomous('monitor', selectedMode);
2681
+ break;
2682
+ }
2437
2683
  case '/sessions':
2438
2684
  case '/session': {
2439
2685
  if (!sessionManager)