helixmind 0.6.1 → 0.6.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.
- package/dist/cli/agent/loop.d.ts.map +1 -1
- package/dist/cli/agent/loop.js +40 -3
- package/dist/cli/agent/loop.js.map +1 -1
- package/dist/cli/agent/tools/registry.d.ts +2 -0
- package/dist/cli/agent/tools/registry.d.ts.map +1 -1
- package/dist/cli/agent/tools/registry.js.map +1 -1
- package/dist/cli/agent/turn-directives.d.ts +12 -0
- package/dist/cli/agent/turn-directives.d.ts.map +1 -0
- package/dist/cli/agent/turn-directives.js +89 -0
- package/dist/cli/agent/turn-directives.js.map +1 -0
- package/dist/cli/brain/generator.d.ts.map +1 -1
- package/dist/cli/brain/generator.js +44 -1
- package/dist/cli/brain/generator.js.map +1 -1
- package/dist/cli/commands/chat.d.ts.map +1 -1
- package/dist/cli/commands/chat.js +392 -144
- package/dist/cli/commands/chat.js.map +1 -1
- package/dist/cli/jarvis/daemon.d.ts +4 -0
- package/dist/cli/jarvis/daemon.d.ts.map +1 -1
- package/dist/cli/jarvis/daemon.js +22 -0
- package/dist/cli/jarvis/daemon.js.map +1 -1
- package/dist/cli/jarvis/orchestrator.d.ts +13 -0
- package/dist/cli/jarvis/orchestrator.d.ts.map +1 -1
- package/dist/cli/jarvis/orchestrator.js +102 -26
- package/dist/cli/jarvis/orchestrator.js.map +1 -1
- package/dist/cli/jarvis/skill-builder.d.ts +26 -0
- package/dist/cli/jarvis/skill-builder.d.ts.map +1 -0
- package/dist/cli/jarvis/skill-builder.js +127 -0
- package/dist/cli/jarvis/skill-builder.js.map +1 -0
- package/dist/cli/ui/activity.d.ts.map +1 -1
- package/dist/cli/ui/activity.js +12 -1
- package/dist/cli/ui/activity.js.map +1 -1
- package/dist/cli/ui/command-suggest.d.ts.map +1 -1
- package/dist/cli/ui/command-suggest.js +2 -0
- package/dist/cli/ui/command-suggest.js.map +1 -1
- package/dist/cli/validation/policy.d.ts +13 -0
- package/dist/cli/validation/policy.d.ts.map +1 -0
- package/dist/cli/validation/policy.js +34 -0
- package/dist/cli/validation/policy.js.map +1 -0
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ import { showHelixMenu } from './helix-menu.js';
|
|
|
21
21
|
import { initializeTools } from '../agent/tools/registry.js';
|
|
22
22
|
import { runAgentLoop, AgentController, AgentAbortError } from '../agent/loop.js';
|
|
23
23
|
import { PermissionManager } from '../agent/permissions.js';
|
|
24
|
+
import { parseTurnDirectives } from '../agent/turn-directives.js';
|
|
24
25
|
import { UndoStack } from '../agent/undo.js';
|
|
25
26
|
import { renderStatusBar, getGitInfo } from '../ui/statusbar.js';
|
|
26
27
|
import { CheckpointStore } from '../checkpoints/store.js';
|
|
@@ -48,11 +49,13 @@ import { WorldModelManager } from '../jarvis/world-model.js';
|
|
|
48
49
|
import { AutonomyManager } from '../jarvis/autonomy.js';
|
|
49
50
|
import { NotificationManager } from '../jarvis/notifications.js';
|
|
50
51
|
import { SkillManager } from '../jarvis/skills.js';
|
|
52
|
+
import { SkillScorer } from '../jarvis/skill-scoring.js';
|
|
51
53
|
import { JarvisTelegramBot, parseTelegramCommand } from '../jarvis/telegram-bot.js';
|
|
52
54
|
import { LearningJournal } from '../jarvis/learning.js';
|
|
53
55
|
import { SentimentAnalyzer } from '../jarvis/sentiment.js';
|
|
54
56
|
import { acquireJarvisSlot, releaseJarvisSlot } from '../jarvis/instance-lock.js';
|
|
55
57
|
import { buildRuntimeContext } from '../jarvis/runtime-context.js';
|
|
58
|
+
import { buildSkillBuildSpecFromProposal, buildSkillBuildTask, detectSkillBuildFromFailure } from '../jarvis/skill-builder.js';
|
|
56
59
|
import { BrainInstanceManager } from '../brain/instance-manager.js';
|
|
57
60
|
import { BrowserController } from '../browser/controller.js';
|
|
58
61
|
import { VisionProcessor } from '../browser/vision.js';
|
|
@@ -60,6 +63,7 @@ import { classifyTask } from '../validation/classifier.js';
|
|
|
60
63
|
import { generateCriteria } from '../validation/criteria.js';
|
|
61
64
|
import { validationLoop } from '../validation/autofix.js';
|
|
62
65
|
import { createValidationProvider } from '../validation/model.js';
|
|
66
|
+
import { resolveValidationDecision } from '../validation/policy.js';
|
|
63
67
|
import { renderValidationSummary, renderValidationStart, renderClassification } from '../validation/reporter.js';
|
|
64
68
|
import { storeValidationResult, getValidationStats, renderValidationStats } from '../validation/stats.js';
|
|
65
69
|
import { isFeatureAvailable, getJarvisLimitsForPlan, getBrainLimitsForPlan, isLoggedIn } from '../auth/feature-gate.js';
|
|
@@ -80,6 +84,8 @@ const HELP_CATEGORIES = [
|
|
|
80
84
|
{ cmd: '/clear', label: '/clear', description: 'Clear conversation history' },
|
|
81
85
|
{ cmd: '/model', label: '/model', description: 'Switch LLM model' },
|
|
82
86
|
{ cmd: '/keys', label: '/keys', description: 'Manage API keys' },
|
|
87
|
+
{ cmd: '/fast [prompt]', label: '/fast', description: 'Low-latency turn (skip validation, no auto-swarm)' },
|
|
88
|
+
{ cmd: '/swarm [prompt]', label: '/swarm', description: 'Force swarm decomposition for one turn' },
|
|
83
89
|
{ cmd: '/yolo', label: '/yolo', description: 'Toggle YOLO mode' },
|
|
84
90
|
{ cmd: '/skip-permissions', label: '/skip-permissions', description: 'Toggle skip-permissions' },
|
|
85
91
|
{ cmd: '/plan', label: '/plan', description: 'Toggle plan mode (read-only → plan → execute)' },
|
|
@@ -225,10 +231,12 @@ const HELP_TEXT = `
|
|
|
225
231
|
${chalk.hex('#00d4ff').bold(' Chat & Interaction')}
|
|
226
232
|
${theme.primary('/help'.padEnd(22))} ${theme.dim('Show this help')}
|
|
227
233
|
${theme.primary('/clear'.padEnd(22))} ${theme.dim('Clear conversation history')}
|
|
228
|
-
${theme.primary('/model [name]'.padEnd(22))} ${theme.dim('Switch model (interactive or direct: /model gpt-4o)')}
|
|
229
|
-
${theme.primary('/keys'.padEnd(22))} ${theme.dim('Add/remove/update API keys')}
|
|
230
|
-
${theme.primary('/
|
|
231
|
-
${theme.primary('/
|
|
234
|
+
${theme.primary('/model [name]'.padEnd(22))} ${theme.dim('Switch model (interactive or direct: /model gpt-4o)')}
|
|
235
|
+
${theme.primary('/keys'.padEnd(22))} ${theme.dim('Add/remove/update API keys')}
|
|
236
|
+
${theme.primary('/fast [prompt]'.padEnd(22))} ${theme.dim('One fast turn: skip validation and auto-swarm')}
|
|
237
|
+
${theme.primary('/swarm [prompt]'.padEnd(22))} ${theme.dim('Force multi-agent swarm for one request')}
|
|
238
|
+
${theme.primary('/yolo [on|off]'.padEnd(22))} ${theme.dim('Toggle YOLO mode — auto-approve ALL operations')}
|
|
239
|
+
${theme.primary('/skip-permissions'.padEnd(22))} ${theme.dim('Toggle skip-permissions (auto-approve safe ops)')}
|
|
232
240
|
|
|
233
241
|
${chalk.hex('#00ff88').bold(' Spiral Memory')}
|
|
234
242
|
${theme.primary('/spiral'.padEnd(22))} ${theme.dim('Show spiral status (nodes per level)')}
|
|
@@ -407,15 +415,18 @@ export async function chatCommand(options) {
|
|
|
407
415
|
let jarvisAutonomy;
|
|
408
416
|
let jarvisNotifications;
|
|
409
417
|
let jarvisSkills;
|
|
418
|
+
let jarvisSkillScorer;
|
|
410
419
|
let jarvisSentiment;
|
|
411
420
|
let jarvisTelegramBot = null;
|
|
412
421
|
let jarvisLearning;
|
|
413
422
|
let jarvisScope;
|
|
423
|
+
let jarvisRuntimeReady = false;
|
|
424
|
+
let pendingJarvisQueueBinder = null;
|
|
414
425
|
let jarvisDaemonSession = null;
|
|
415
426
|
let jarvisPaused = false;
|
|
416
427
|
const resolveJarvisRoot = (scope) => scope === 'project' ? process.cwd() : join(homedir(), '.spiral-context');
|
|
417
|
-
// Browser controller
|
|
418
|
-
let browserController
|
|
428
|
+
// Browser controller stays lazy so plain chat startup avoids browser setup work.
|
|
429
|
+
let browserController;
|
|
419
430
|
let visionProcessor;
|
|
420
431
|
// Terminal + Screen (replaces BottomChrome with framed input area)
|
|
421
432
|
const terminal = new Terminal();
|
|
@@ -428,6 +439,16 @@ export async function chatCommand(options) {
|
|
|
428
439
|
const chrome = screen;
|
|
429
440
|
// Activity indicator (renders on chrome row 0 during agent work)
|
|
430
441
|
const activity = new ActivityIndicator(chrome);
|
|
442
|
+
const chromeRowCache = new Map();
|
|
443
|
+
function invalidateChromeRows() {
|
|
444
|
+
chromeRowCache.clear();
|
|
445
|
+
}
|
|
446
|
+
function setChromeRow(row, content, force = false) {
|
|
447
|
+
if (!force && chromeRowCache.get(row) === content)
|
|
448
|
+
return;
|
|
449
|
+
chromeRowCache.set(row, content);
|
|
450
|
+
chrome.setRow(row, content);
|
|
451
|
+
}
|
|
431
452
|
// Agent controller for pause/resume
|
|
432
453
|
const agentController = new AgentController();
|
|
433
454
|
let agentRunning = false;
|
|
@@ -557,28 +578,54 @@ export async function chatCommand(options) {
|
|
|
557
578
|
}
|
|
558
579
|
// Jarvis scope follows brain scope — init after brainScope detection
|
|
559
580
|
jarvisScope = brainScope;
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
+
function initializeJarvisRuntime(scope = jarvisScope) {
|
|
582
|
+
const root = resolveJarvisRoot(scope);
|
|
583
|
+
jarvisScope = scope;
|
|
584
|
+
jarvisQueue = new JarvisQueue(root);
|
|
585
|
+
jarvisIdentity = new JarvisIdentityManager(root);
|
|
586
|
+
jarvisProposals = new ProposalJournal(root);
|
|
587
|
+
jarvisScheduler = new JarvisScheduler(root);
|
|
588
|
+
jarvisTriggers = new TriggerManager(root);
|
|
589
|
+
jarvisWorldModel = new WorldModelManager(root);
|
|
590
|
+
jarvisAutonomy = new AutonomyManager(jarvisIdentity.getIdentity().autonomyLevel);
|
|
591
|
+
jarvisNotifications = new NotificationManager(root);
|
|
592
|
+
jarvisSkills = new SkillManager(root);
|
|
593
|
+
jarvisSkillScorer = new SkillScorer(root);
|
|
594
|
+
jarvisSkills.syncRegistry();
|
|
595
|
+
jarvisLearning = new LearningJournal(root);
|
|
596
|
+
jarvisSentiment = new SentimentAnalyzer(root, {
|
|
597
|
+
onShift: (from, to, frustrationLevel) => {
|
|
598
|
+
jarvisIdentity.recordEvent({ type: 'sentiment_shift', from, to, frustrationLevel });
|
|
599
|
+
import('../brain/generator.js').then(mod => {
|
|
600
|
+
if (mod.isBrainServerRunning?.()) {
|
|
601
|
+
mod.pushNeuronFired?.('yellow', '#ff6b6b', 'sentiment');
|
|
602
|
+
}
|
|
603
|
+
}).catch(() => { });
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
jarvisRuntimeReady = true;
|
|
607
|
+
pendingJarvisQueueBinder?.();
|
|
608
|
+
}
|
|
609
|
+
function ensureJarvisRuntime(scope = jarvisScope) {
|
|
610
|
+
if (jarvisRuntimeReady && jarvisScope === scope)
|
|
611
|
+
return;
|
|
612
|
+
initializeJarvisRuntime(scope);
|
|
613
|
+
}
|
|
614
|
+
function warmJarvisRuntime(scope = jarvisScope) {
|
|
615
|
+
if (jarvisRuntimeReady && jarvisScope === scope)
|
|
616
|
+
return;
|
|
617
|
+
setTimeout(() => {
|
|
618
|
+
try {
|
|
619
|
+
ensureJarvisRuntime(scope);
|
|
620
|
+
}
|
|
621
|
+
catch {
|
|
622
|
+
// Best effort: Jarvis can still initialize on first real use.
|
|
623
|
+
}
|
|
624
|
+
}, 0);
|
|
625
|
+
}
|
|
581
626
|
let spiralEngine = null;
|
|
627
|
+
let spiralInitPromise = null;
|
|
628
|
+
let spiralInitScope = null;
|
|
582
629
|
async function initSpiralEngine(scope) {
|
|
583
630
|
try {
|
|
584
631
|
const { SpiralEngine } = await import('../../spiral/engine.js');
|
|
@@ -593,8 +640,58 @@ export async function chatCommand(options) {
|
|
|
593
640
|
return null;
|
|
594
641
|
}
|
|
595
642
|
}
|
|
596
|
-
|
|
597
|
-
|
|
643
|
+
async function ensureSpiralEngine(scope = brainScope) {
|
|
644
|
+
if (!config.spiral.enabled)
|
|
645
|
+
return null;
|
|
646
|
+
if (spiralEngine && spiralInitScope === scope)
|
|
647
|
+
return spiralEngine;
|
|
648
|
+
if (spiralInitPromise && spiralInitScope === scope)
|
|
649
|
+
return spiralInitPromise;
|
|
650
|
+
spiralInitScope = scope;
|
|
651
|
+
spiralInitPromise = initSpiralEngine(scope).then(async (engine) => {
|
|
652
|
+
spiralEngine = engine;
|
|
653
|
+
spiralInitPromise = null;
|
|
654
|
+
if (engine && brainUrl) {
|
|
655
|
+
try {
|
|
656
|
+
const { startLiveBrain } = await import('../brain/generator.js');
|
|
657
|
+
await startLiveBrain(engine, project.name || 'HelixMind', scope);
|
|
658
|
+
}
|
|
659
|
+
catch {
|
|
660
|
+
// Dashboard sync is best effort.
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return engine;
|
|
664
|
+
}).catch((err) => {
|
|
665
|
+
spiralInitPromise = null;
|
|
666
|
+
spiralInitScope = null;
|
|
667
|
+
throw err;
|
|
668
|
+
});
|
|
669
|
+
return spiralInitPromise;
|
|
670
|
+
}
|
|
671
|
+
async function getSpiralEngine(scope = brainScope) {
|
|
672
|
+
if (!config.spiral.enabled)
|
|
673
|
+
return null;
|
|
674
|
+
if (spiralEngine && spiralInitScope === scope)
|
|
675
|
+
return spiralEngine;
|
|
676
|
+
const engine = await ensureSpiralEngine(scope);
|
|
677
|
+
spiralEngine = engine;
|
|
678
|
+
return engine;
|
|
679
|
+
}
|
|
680
|
+
function warmSpiralEngine(scope = brainScope) {
|
|
681
|
+
if (!config.spiral.enabled)
|
|
682
|
+
return;
|
|
683
|
+
void ensureSpiralEngine(scope).catch(() => { });
|
|
684
|
+
}
|
|
685
|
+
function resetSpiralEngine(scope) {
|
|
686
|
+
if (spiralEngine) {
|
|
687
|
+
try {
|
|
688
|
+
spiralEngine.close();
|
|
689
|
+
}
|
|
690
|
+
catch { /* best effort */ }
|
|
691
|
+
}
|
|
692
|
+
spiralEngine = null;
|
|
693
|
+
spiralInitPromise = null;
|
|
694
|
+
spiralInitScope = scope ?? null;
|
|
598
695
|
}
|
|
599
696
|
// Register current brain in manager (so limits track it)
|
|
600
697
|
if (brainManager) {
|
|
@@ -617,6 +714,7 @@ export async function chatCommand(options) {
|
|
|
617
714
|
}
|
|
618
715
|
// Build SkillContext for skill activation
|
|
619
716
|
function buildSkillContext() {
|
|
717
|
+
ensureJarvisRuntime();
|
|
620
718
|
return {
|
|
621
719
|
registerTool: (name, def, handler) => {
|
|
622
720
|
// Skills register tools into the skill manager's registry
|
|
@@ -626,10 +724,11 @@ export async function chatCommand(options) {
|
|
|
626
724
|
unregisterTool: (_name) => { },
|
|
627
725
|
addTask: (title, desc, priority) => jarvisQueue.addTask(title, desc, { priority }),
|
|
628
726
|
querySpiral: async (query) => {
|
|
629
|
-
|
|
727
|
+
const engine = spiralEngine ?? await ensureSpiralEngine(brainScope);
|
|
728
|
+
if (!engine)
|
|
630
729
|
return '';
|
|
631
730
|
try {
|
|
632
|
-
const results = await
|
|
731
|
+
const results = await engine.query(query, { maxResults: 10 });
|
|
633
732
|
return results.level_1.concat(results.level_2, results.level_3)
|
|
634
733
|
.map((n) => `[${n.type}] ${n.content}`)
|
|
635
734
|
.join('\n')
|
|
@@ -640,10 +739,11 @@ export async function chatCommand(options) {
|
|
|
640
739
|
}
|
|
641
740
|
},
|
|
642
741
|
storeInSpiral: async (content, type, tags) => {
|
|
643
|
-
|
|
742
|
+
const engine = spiralEngine ?? await ensureSpiralEngine(brainScope);
|
|
743
|
+
if (!engine)
|
|
644
744
|
return;
|
|
645
745
|
try {
|
|
646
|
-
await
|
|
746
|
+
await engine.add(content, { type, tags });
|
|
647
747
|
}
|
|
648
748
|
catch { }
|
|
649
749
|
},
|
|
@@ -655,8 +755,64 @@ export async function chatCommand(options) {
|
|
|
655
755
|
log: (msg) => { renderInfo(chalk.dim(`[skill] ${msg}`)); },
|
|
656
756
|
};
|
|
657
757
|
}
|
|
758
|
+
function findOpenSkillBuildTask(skillName) {
|
|
759
|
+
ensureJarvisRuntime();
|
|
760
|
+
return jarvisQueue.getAllTasks().find((task) => task.status !== 'completed' &&
|
|
761
|
+
task.status !== 'failed' &&
|
|
762
|
+
task.tags?.includes('skill_build') &&
|
|
763
|
+
task.tags?.includes(`skill:${skillName}`));
|
|
764
|
+
}
|
|
765
|
+
function enqueueProposalTask(proposal) {
|
|
766
|
+
ensureJarvisRuntime();
|
|
767
|
+
if (proposal.category === 'skill_creation' || proposal.category === 'skill_update') {
|
|
768
|
+
const spec = buildSkillBuildSpecFromProposal(proposal);
|
|
769
|
+
const existing = findOpenSkillBuildTask(spec.skillName);
|
|
770
|
+
if (existing)
|
|
771
|
+
return existing;
|
|
772
|
+
const buildTask = buildSkillBuildTask(spec);
|
|
773
|
+
return jarvisQueue.addTask(buildTask.title, buildTask.description, {
|
|
774
|
+
priority: buildTask.priority,
|
|
775
|
+
tags: buildTask.tags,
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
return jarvisQueue.addTask(proposal.title, proposal.description, {
|
|
779
|
+
priority: proposal.impact === 'high' ? 'high' : 'medium',
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
function queueSkillBuildFromFailure(task, failure) {
|
|
783
|
+
ensureJarvisRuntime();
|
|
784
|
+
const spec = detectSkillBuildFromFailure({
|
|
785
|
+
taskTitle: task.title,
|
|
786
|
+
taskDescription: task.description,
|
|
787
|
+
failure,
|
|
788
|
+
existingScores: jarvisSkillScorer.scoreSkills(task.description || task.title, 'skill_gap', jarvisSkills.listSkills()),
|
|
789
|
+
existingSkillNames: jarvisSkills.listSkills().map((skill) => skill.manifest.name),
|
|
790
|
+
});
|
|
791
|
+
if (!spec)
|
|
792
|
+
return null;
|
|
793
|
+
const existing = findOpenSkillBuildTask(spec.skillName);
|
|
794
|
+
if (existing)
|
|
795
|
+
return existing;
|
|
796
|
+
const buildTask = buildSkillBuildTask(spec);
|
|
797
|
+
return jarvisQueue.addTask(buildTask.title, buildTask.description, {
|
|
798
|
+
priority: buildTask.priority,
|
|
799
|
+
tags: buildTask.tags.concat('self_improvement'),
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
async function activateBuiltSkill(task, skillName) {
|
|
803
|
+
ensureJarvisRuntime();
|
|
804
|
+
jarvisSkills.syncRegistry();
|
|
805
|
+
const result = await jarvisSkills.activateSkill(skillName, buildSkillContext());
|
|
806
|
+
if (result.success) {
|
|
807
|
+
renderInfo(chalk.green(`Activated skill "${skillName}" after task #${task.id}.`));
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
renderInfo(chalk.yellow(`Built skill "${skillName}" but activation failed: ${result.error || 'unknown error'}`));
|
|
811
|
+
}
|
|
812
|
+
}
|
|
658
813
|
// Start Telegram bot if configured (called when daemon starts)
|
|
659
814
|
function startTelegramBot() {
|
|
815
|
+
ensureJarvisRuntime();
|
|
660
816
|
if (jarvisTelegramBot?.isRunning)
|
|
661
817
|
return;
|
|
662
818
|
const teleConfig = jarvisNotifications.getConfig().targets.find(t => t.channel === 'telegram');
|
|
@@ -699,9 +855,7 @@ export async function chatCommand(options) {
|
|
|
699
855
|
const proposal = jarvisProposals.approve(id);
|
|
700
856
|
if (proposal) {
|
|
701
857
|
jarvisIdentity.recordEvent({ type: 'proposal_approved', proposalId: id });
|
|
702
|
-
const task =
|
|
703
|
-
priority: proposal.impact === 'high' ? 'high' : 'medium',
|
|
704
|
-
});
|
|
858
|
+
const task = enqueueProposalTask(proposal);
|
|
705
859
|
proposal.convertedTaskId = task.id;
|
|
706
860
|
reply(`\u{1F7E2} Proposal #${id} approved \u2192 Task #${task.id} queued`);
|
|
707
861
|
}
|
|
@@ -760,7 +914,7 @@ export async function chatCommand(options) {
|
|
|
760
914
|
const proposal = jarvisProposals.approve(id);
|
|
761
915
|
if (proposal) {
|
|
762
916
|
jarvisIdentity.recordEvent({ type: 'proposal_approved', proposalId: id });
|
|
763
|
-
const task =
|
|
917
|
+
const task = enqueueProposalTask(proposal);
|
|
764
918
|
proposal.convertedTaskId = task.id;
|
|
765
919
|
jarvisTelegramBot?.send(`\u2705 Approved #${id} \u2192 Task #${task.id}`, { chatId });
|
|
766
920
|
}
|
|
@@ -777,6 +931,7 @@ export async function chatCommand(options) {
|
|
|
777
931
|
}
|
|
778
932
|
// Build ThinkingCallbacks for Jarvis AGI thinking loop
|
|
779
933
|
function buildThinkingCallbacks(bgSession) {
|
|
934
|
+
ensureJarvisRuntime();
|
|
780
935
|
return {
|
|
781
936
|
sendMessage: async (prompt) => {
|
|
782
937
|
bgSession.controller.reset();
|
|
@@ -789,10 +944,11 @@ export async function chatCommand(options) {
|
|
|
789
944
|
isAborted: () => bgSession.controller.isAborted,
|
|
790
945
|
isPaused: () => jarvisPaused,
|
|
791
946
|
querySpiral: async (query, maxTokens) => {
|
|
792
|
-
|
|
947
|
+
const engine = spiralEngine ?? await ensureSpiralEngine(brainScope);
|
|
948
|
+
if (!engine)
|
|
793
949
|
return '';
|
|
794
950
|
try {
|
|
795
|
-
const results = await
|
|
951
|
+
const results = await engine.query(query, { maxResults: maxTokens ?? 50 });
|
|
796
952
|
return results.level_1.concat(results.level_2, results.level_3)
|
|
797
953
|
.map((n) => `[${n.type}] ${n.content}`)
|
|
798
954
|
.join('\n')
|
|
@@ -803,15 +959,29 @@ export async function chatCommand(options) {
|
|
|
803
959
|
}
|
|
804
960
|
},
|
|
805
961
|
storeInSpiral: async (content, type, tags) => {
|
|
806
|
-
|
|
962
|
+
const engine = spiralEngine ?? await ensureSpiralEngine(brainScope);
|
|
963
|
+
if (!engine)
|
|
807
964
|
return;
|
|
808
965
|
try {
|
|
809
|
-
await
|
|
966
|
+
await engine.add(content, { type: type, tags });
|
|
810
967
|
}
|
|
811
968
|
catch { }
|
|
812
969
|
},
|
|
813
970
|
createProposal: (title, desc, rationale, opts) => {
|
|
814
|
-
|
|
971
|
+
const proposal = jarvisProposals.create(title, desc, rationale, opts);
|
|
972
|
+
if (proposal.category === 'skill_creation' &&
|
|
973
|
+
proposal.source.startsWith('thinking') &&
|
|
974
|
+
jarvisAutonomy.getLevel() >= 4) {
|
|
975
|
+
const approved = jarvisProposals.approve(proposal.id);
|
|
976
|
+
if (approved) {
|
|
977
|
+
jarvisIdentity.recordEvent({ type: 'proposal_approved', proposalId: approved.id });
|
|
978
|
+
const task = enqueueProposalTask(approved);
|
|
979
|
+
approved.convertedTaskId = task.id;
|
|
980
|
+
renderInfo(chalk.dim(`Auto-approved skill proposal #${approved.id} -> Task #${task.id}`));
|
|
981
|
+
return approved;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return proposal;
|
|
815
985
|
},
|
|
816
986
|
wouldLikelyBeDenied: (cat, files) => {
|
|
817
987
|
return jarvisProposals.wouldLikelyBeDenied(cat, files);
|
|
@@ -852,6 +1022,10 @@ export async function chatCommand(options) {
|
|
|
852
1022
|
topCategories,
|
|
853
1023
|
};
|
|
854
1024
|
},
|
|
1025
|
+
getSkillScores: (taskDesc) => {
|
|
1026
|
+
return jarvisSkillScorer.scoreSkills(taskDesc, 'skill_gap', jarvisSkills.listSkills())
|
|
1027
|
+
.map((score) => ({ skillName: score.skillName, totalScore: score.totalScore }));
|
|
1028
|
+
},
|
|
855
1029
|
};
|
|
856
1030
|
}
|
|
857
1031
|
// Create session start checkpoint
|
|
@@ -1210,7 +1384,8 @@ export async function chatCommand(options) {
|
|
|
1210
1384
|
bgSession.controller.reset();
|
|
1211
1385
|
const rth = { text: '' };
|
|
1212
1386
|
bgSession.buffer.onSummary = (t) => { rth.text = t; };
|
|
1213
|
-
|
|
1387
|
+
const activeSpiral = await getSpiralEngine(brainScope);
|
|
1388
|
+
await sendAgentMessage(prompt, bgSession.history, provider, project, activeSpiral, 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 }, undefined, undefined, undefined, undefined, null, undefined, jarvisLearning, undefined, jarvisSkills);
|
|
1214
1389
|
bgSession.buffer.onSummary = undefined;
|
|
1215
1390
|
return rth.text;
|
|
1216
1391
|
},
|
|
@@ -1306,6 +1481,7 @@ export async function chatCommand(options) {
|
|
|
1306
1481
|
return bugJournal.delete(id);
|
|
1307
1482
|
},
|
|
1308
1483
|
startJarvis: () => {
|
|
1484
|
+
ensureJarvisRuntime();
|
|
1309
1485
|
if (jarvisDaemonSession && jarvisDaemonSession.status === 'running')
|
|
1310
1486
|
return jarvisDaemonSession.id;
|
|
1311
1487
|
// Enforce cross-process Jarvis instance limit
|
|
@@ -1362,9 +1538,12 @@ export async function chatCommand(options) {
|
|
|
1362
1538
|
pushSessionUpdate(serializeSession(bgSession));
|
|
1363
1539
|
},
|
|
1364
1540
|
updateStatus: () => updateStatusBar(),
|
|
1365
|
-
storeInSpiral:
|
|
1541
|
+
storeInSpiral: config.spiral.enabled ? async (content, type, tags) => {
|
|
1542
|
+
const activeSpiral = await getSpiralEngine(brainScope);
|
|
1543
|
+
if (!activeSpiral)
|
|
1544
|
+
return;
|
|
1366
1545
|
try {
|
|
1367
|
-
await
|
|
1546
|
+
await activeSpiral.add(content, { type: type, tags });
|
|
1368
1547
|
}
|
|
1369
1548
|
catch { }
|
|
1370
1549
|
} : undefined,
|
|
@@ -1375,6 +1554,8 @@ export async function chatCommand(options) {
|
|
|
1375
1554
|
jarvisLearning.recordLearning(error, solution, category, context, ['jarvis_daemon']);
|
|
1376
1555
|
},
|
|
1377
1556
|
thinkingCallbacks: buildThinkingCallbacks(bgSession),
|
|
1557
|
+
queueSkillBuildTask: queueSkillBuildFromFailure,
|
|
1558
|
+
onSkillBuildComplete: activateBuiltSkill,
|
|
1378
1559
|
});
|
|
1379
1560
|
}
|
|
1380
1561
|
catch (err) {
|
|
@@ -1398,6 +1579,7 @@ export async function chatCommand(options) {
|
|
|
1398
1579
|
return true;
|
|
1399
1580
|
},
|
|
1400
1581
|
pauseJarvis: () => {
|
|
1582
|
+
ensureJarvisRuntime();
|
|
1401
1583
|
if (!jarvisDaemonSession || jarvisPaused)
|
|
1402
1584
|
return false;
|
|
1403
1585
|
jarvisPaused = true;
|
|
@@ -1405,6 +1587,7 @@ export async function chatCommand(options) {
|
|
|
1405
1587
|
return true;
|
|
1406
1588
|
},
|
|
1407
1589
|
resumeJarvis: () => {
|
|
1590
|
+
ensureJarvisRuntime();
|
|
1408
1591
|
if (!jarvisDaemonSession || !jarvisPaused)
|
|
1409
1592
|
return false;
|
|
1410
1593
|
jarvisPaused = false;
|
|
@@ -1412,24 +1595,36 @@ export async function chatCommand(options) {
|
|
|
1412
1595
|
return true;
|
|
1413
1596
|
},
|
|
1414
1597
|
addJarvisTask: (title, description, opts) => {
|
|
1598
|
+
ensureJarvisRuntime();
|
|
1415
1599
|
const task = jarvisQueue.addTask(title, description, opts);
|
|
1416
1600
|
return serializeJarvisTask(task);
|
|
1417
1601
|
},
|
|
1418
|
-
listJarvisTasks: () =>
|
|
1602
|
+
listJarvisTasks: () => {
|
|
1603
|
+
ensureJarvisRuntime();
|
|
1604
|
+
return jarvisQueue.getAllTasks().map(serializeJarvisTask);
|
|
1605
|
+
},
|
|
1419
1606
|
deleteJarvisTask: (taskId) => {
|
|
1607
|
+
ensureJarvisRuntime();
|
|
1420
1608
|
return jarvisQueue.removeTask(taskId);
|
|
1421
1609
|
},
|
|
1422
|
-
getJarvisStatus: () =>
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1610
|
+
getJarvisStatus: () => {
|
|
1611
|
+
ensureJarvisRuntime();
|
|
1612
|
+
return {
|
|
1613
|
+
...jarvisQueue.getStatus(),
|
|
1614
|
+
scope: jarvisScope === 'project' ? 'local' : 'global',
|
|
1615
|
+
jarvisName: jarvisIdentity.getIdentity().name,
|
|
1616
|
+
};
|
|
1617
|
+
},
|
|
1618
|
+
clearJarvisCompleted: () => {
|
|
1619
|
+
ensureJarvisRuntime();
|
|
1620
|
+
return jarvisQueue.clearCompleted();
|
|
1621
|
+
},
|
|
1428
1622
|
// Jarvis AGI handlers (stubs — wired when AGI modules are initialized)
|
|
1429
1623
|
listProposals: () => [],
|
|
1430
1624
|
approveProposal: () => false,
|
|
1431
1625
|
denyProposal: () => false,
|
|
1432
1626
|
setAutonomyLevel: (level) => {
|
|
1627
|
+
ensureJarvisRuntime();
|
|
1433
1628
|
if (level < 0 || level > 5)
|
|
1434
1629
|
return false;
|
|
1435
1630
|
jarvisAutonomy.setLevel(level);
|
|
@@ -1776,19 +1971,24 @@ export async function chatCommand(options) {
|
|
|
1776
1971
|
pushBugUpdated(bugInfo);
|
|
1777
1972
|
}
|
|
1778
1973
|
});
|
|
1779
|
-
// Wire Jarvis queue change events to brain server
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1974
|
+
// Wire Jarvis queue change events to brain server when Jarvis runtime exists.
|
|
1975
|
+
pendingJarvisQueueBinder = () => {
|
|
1976
|
+
if (!jarvisRuntimeReady)
|
|
1977
|
+
return;
|
|
1978
|
+
jarvisQueue.setOnChange((event, task) => {
|
|
1979
|
+
const info = serializeJarvisTask(task);
|
|
1980
|
+
if (event === 'task_created') {
|
|
1981
|
+
pushJarvisTaskCreated(info);
|
|
1982
|
+
}
|
|
1983
|
+
else if (event === 'task_removed') {
|
|
1984
|
+
pushJarvisTaskRemoved(task.id);
|
|
1985
|
+
}
|
|
1986
|
+
else {
|
|
1987
|
+
pushJarvisTaskUpdated(info);
|
|
1988
|
+
}
|
|
1989
|
+
});
|
|
1990
|
+
};
|
|
1991
|
+
pendingJarvisQueueBinder();
|
|
1792
1992
|
// Wire browser screenshots to brain server
|
|
1793
1993
|
pushScreenshotToBrainFn = (info) => {
|
|
1794
1994
|
pushBrowserScreenshot({
|
|
@@ -1888,6 +2088,34 @@ export async function chatCommand(options) {
|
|
|
1888
2088
|
hints.push(chalk.dim('esc to interrupt'));
|
|
1889
2089
|
return hints.join(chalk.dim(' \u00B7 '));
|
|
1890
2090
|
}
|
|
2091
|
+
function renderExecutionIntent(directives, validationDecision, swarmHeuristic, usingSwarm) {
|
|
2092
|
+
const parts = [];
|
|
2093
|
+
if (usingSwarm) {
|
|
2094
|
+
const reason = swarmHeuristic.reasons.slice(0, 2).join(', ');
|
|
2095
|
+
parts.push(chalk.hex('#ffd700')('swarm'));
|
|
2096
|
+
if (reason)
|
|
2097
|
+
parts.push(chalk.dim(reason));
|
|
2098
|
+
}
|
|
2099
|
+
else {
|
|
2100
|
+
parts.push(chalk.hex('#00d4ff')('single-agent'));
|
|
2101
|
+
if (directives.skipSwarm) {
|
|
2102
|
+
parts.push(chalk.dim('swarm skipped'));
|
|
2103
|
+
}
|
|
2104
|
+
else if (swarmHeuristic.shouldOrchestrate) {
|
|
2105
|
+
parts.push(chalk.dim('swarm available but not used'));
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
if (validationDecision.enabled) {
|
|
2109
|
+
parts.push(chalk.green('validation on'));
|
|
2110
|
+
}
|
|
2111
|
+
else {
|
|
2112
|
+
parts.push(chalk.yellow(`validation off (${validationDecision.reason})`));
|
|
2113
|
+
}
|
|
2114
|
+
if (directives.fastMode) {
|
|
2115
|
+
parts.push(chalk.hex('#ff6600')('fast lane'));
|
|
2116
|
+
}
|
|
2117
|
+
renderInfo(chalk.dim('Intent: ') + parts.join(chalk.dim(' · ')));
|
|
2118
|
+
}
|
|
1891
2119
|
/**
|
|
1892
2120
|
* Show the full prompt area using sticky bottom chrome (4 rows):
|
|
1893
2121
|
* ┌──────────────────────────────────────┐ ← chrome row 0 (N-3): top border / activity indicator
|
|
@@ -1912,9 +2140,9 @@ export async function chatCommand(options) {
|
|
|
1912
2140
|
}
|
|
1913
2141
|
// Now set chrome content and draw (screen must be active for these to render)
|
|
1914
2142
|
screen.drawFrameBottom();
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
2143
|
+
setChromeRow(1, hintLine, true);
|
|
2144
|
+
setChromeRow(2, ' ' + statusLine1, true);
|
|
2145
|
+
setChromeRow(3, ' ' + statusLine2, true);
|
|
1918
2146
|
isAtPrompt = true;
|
|
1919
2147
|
// Suppress stdout hook redraws while InputManager manages the cursor
|
|
1920
2148
|
screen.inputActive = true;
|
|
@@ -2008,6 +2236,7 @@ export async function chatCommand(options) {
|
|
|
2008
2236
|
// Update screen and activity on terminal resize
|
|
2009
2237
|
process.stdout.on('resize', () => {
|
|
2010
2238
|
closeSuggestionPanel(); // Close panel on resize (reopens on next keypress)
|
|
2239
|
+
invalidateChromeRows();
|
|
2011
2240
|
screen.handleResize();
|
|
2012
2241
|
activity.handleResize();
|
|
2013
2242
|
if (isAtPrompt) {
|
|
@@ -2039,9 +2268,11 @@ export async function chatCommand(options) {
|
|
|
2039
2268
|
isAtPrompt = active;
|
|
2040
2269
|
// Deactivate/activate bottom chrome so permission select menu renders cleanly
|
|
2041
2270
|
if (active) {
|
|
2271
|
+
invalidateChromeRows();
|
|
2042
2272
|
chrome.deactivate();
|
|
2043
2273
|
}
|
|
2044
2274
|
else {
|
|
2275
|
+
invalidateChromeRows();
|
|
2045
2276
|
chrome.activate();
|
|
2046
2277
|
}
|
|
2047
2278
|
});
|
|
@@ -2065,7 +2296,7 @@ export async function chatCommand(options) {
|
|
|
2065
2296
|
if (isAtPrompt) {
|
|
2066
2297
|
const hintLine = buildHintLine();
|
|
2067
2298
|
activity.setRestoreContent(hintLine);
|
|
2068
|
-
|
|
2299
|
+
invalidateChromeRows();
|
|
2069
2300
|
updateStatusBar();
|
|
2070
2301
|
}
|
|
2071
2302
|
});
|
|
@@ -2234,12 +2465,7 @@ export async function chatCommand(options) {
|
|
|
2234
2465
|
if (newScope === brainScope)
|
|
2235
2466
|
return;
|
|
2236
2467
|
try {
|
|
2237
|
-
|
|
2238
|
-
try {
|
|
2239
|
-
spiralEngine.close();
|
|
2240
|
-
}
|
|
2241
|
-
catch { /* best effort */ }
|
|
2242
|
-
}
|
|
2468
|
+
resetSpiralEngine();
|
|
2243
2469
|
brainScope = newScope;
|
|
2244
2470
|
if (newScope === 'project') {
|
|
2245
2471
|
const { mkdirSync, existsSync } = await import('node:fs');
|
|
@@ -2258,7 +2484,7 @@ export async function chatCommand(options) {
|
|
|
2258
2484
|
}
|
|
2259
2485
|
}
|
|
2260
2486
|
}
|
|
2261
|
-
spiralEngine = await
|
|
2487
|
+
spiralEngine = await ensureSpiralEngine(newScope);
|
|
2262
2488
|
const { exportBrainData } = await import('../brain/exporter.js');
|
|
2263
2489
|
const { startLiveBrain } = await import('../brain/generator.js');
|
|
2264
2490
|
await startLiveBrain(spiralEngine, project.name || 'HelixMind', newScope);
|
|
@@ -2583,6 +2809,7 @@ export async function chatCommand(options) {
|
|
|
2583
2809
|
function getJarvisContextForPrompt() {
|
|
2584
2810
|
if (!jarvisDaemonSession || jarvisDaemonSession.status !== 'running')
|
|
2585
2811
|
return null;
|
|
2812
|
+
ensureJarvisRuntime();
|
|
2586
2813
|
const sentimentGuidance = jarvisSentiment.getResponseGuidance();
|
|
2587
2814
|
const identityPrompt = jarvisIdentity.getIdentityPrompt(jarvisSkills.getSkillsPrompt() ?? undefined, sentimentGuidance || undefined);
|
|
2588
2815
|
const runtimeContext = buildRuntimeContext({
|
|
@@ -2623,9 +2850,9 @@ export async function chatCommand(options) {
|
|
|
2623
2850
|
line1 = `${tabBar} ${statusLine1}`;
|
|
2624
2851
|
}
|
|
2625
2852
|
// Refresh hint line on row 1 (prevents stale border/status from previous layout)
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2853
|
+
setChromeRow(1, buildHintLine());
|
|
2854
|
+
setChromeRow(2, ' ' + line1);
|
|
2855
|
+
setChromeRow(3, ' ' + (statusLine2 || ''));
|
|
2629
2856
|
}
|
|
2630
2857
|
/** Push session findings to brain visualization */
|
|
2631
2858
|
function pushFindingsToBrain(session) {
|
|
@@ -2688,6 +2915,9 @@ export async function chatCommand(options) {
|
|
|
2688
2915
|
// Write the collected startup banner into the scroll region.
|
|
2689
2916
|
// Content flows bottom-up within the region, sitting just above the input frame.
|
|
2690
2917
|
screen.writeOutput(startupBannerParts.join(''));
|
|
2918
|
+
// Warm optional heavy subsystems after the prompt is already visible.
|
|
2919
|
+
warmJarvisRuntime(jarvisScope);
|
|
2920
|
+
warmSpiralEngine(brainScope);
|
|
2691
2921
|
// Footer timer — redraws statusbar on chrome rows 2+3 during agent work.
|
|
2692
2922
|
// Skipped when:
|
|
2693
2923
|
// - user is at readline prompt (isAtPrompt) — prevents cursor-jumping
|
|
@@ -2701,6 +2931,7 @@ export async function chatCommand(options) {
|
|
|
2701
2931
|
footerTimer.unref();
|
|
2702
2932
|
/** Process a complete input (single line or assembled paste block) */
|
|
2703
2933
|
async function processInput(input, displayText) {
|
|
2934
|
+
const rawInput = input;
|
|
2704
2935
|
// Clear input frame FIRST — before any renderInfo/process.stdout.write.
|
|
2705
2936
|
// This ensures the cursor moves out of the input area so all output
|
|
2706
2937
|
// (slash command results, "daemon started", etc.) goes to the scroll region.
|
|
@@ -2711,12 +2942,14 @@ export async function chatCommand(options) {
|
|
|
2711
2942
|
promptHistory.add(input, process.cwd()).catch(() => { });
|
|
2712
2943
|
// Handle /feed directly here (needs access to inlineProgressActive flag)
|
|
2713
2944
|
if (input.startsWith('/feed')) {
|
|
2714
|
-
|
|
2945
|
+
const activeSpiral = spiralEngine ?? await ensureSpiralEngine(brainScope);
|
|
2946
|
+
if (activeSpiral) {
|
|
2947
|
+
spiralEngine = activeSpiral;
|
|
2715
2948
|
const feedPath = input.split(/\s+/)[1];
|
|
2716
2949
|
const rootDir = process.cwd();
|
|
2717
2950
|
renderInfo('\u{1F300} Feeding project...\n');
|
|
2718
2951
|
try {
|
|
2719
|
-
const result = await runFeedPipeline(rootDir,
|
|
2952
|
+
const result = await runFeedPipeline(rootDir, activeSpiral, {
|
|
2720
2953
|
targetPath: feedPath,
|
|
2721
2954
|
onProgress: wrappedFeedProgress,
|
|
2722
2955
|
});
|
|
@@ -2778,8 +3011,27 @@ export async function chatCommand(options) {
|
|
|
2778
3011
|
showPrompt();
|
|
2779
3012
|
return;
|
|
2780
3013
|
}
|
|
3014
|
+
const turnDirectives = parseTurnDirectives(rawInput);
|
|
3015
|
+
input = turnDirectives.input;
|
|
3016
|
+
const renderedInput = displayText ?? turnDirectives.displayInput;
|
|
3017
|
+
if (!input) {
|
|
3018
|
+
if (turnDirectives.fastMode) {
|
|
3019
|
+
renderInfo(chalk.dim('Usage: /fast <prompt>'));
|
|
3020
|
+
}
|
|
3021
|
+
else if (turnDirectives.forceSwarm) {
|
|
3022
|
+
renderInfo(chalk.dim('Usage: /swarm <prompt>'));
|
|
3023
|
+
}
|
|
3024
|
+
else if (turnDirectives.strippedFlags.length > 0) {
|
|
3025
|
+
renderInfo(chalk.dim('Execution flags were provided without a prompt.'));
|
|
3026
|
+
}
|
|
3027
|
+
showPrompt();
|
|
3028
|
+
return;
|
|
3029
|
+
}
|
|
2781
3030
|
// Handle slash commands
|
|
2782
|
-
if (input.startsWith('/')) {
|
|
3031
|
+
if (input.startsWith('/') && !turnDirectives.forceSwarm) {
|
|
3032
|
+
if (/^\/(?:jarvis|del)\b/i.test(input)) {
|
|
3033
|
+
ensureJarvisRuntime();
|
|
3034
|
+
}
|
|
2783
3035
|
const handled = await handleSlashCommand(input, messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, { input: sessionTokensInput, output: sessionTokensOutput }, sessionToolCalls, (newProvider) => {
|
|
2784
3036
|
provider = newProvider;
|
|
2785
3037
|
config = store.getAll();
|
|
@@ -2788,12 +3040,7 @@ export async function chatCommand(options) {
|
|
|
2788
3040
|
typeAheadBuffer.length = 0;
|
|
2789
3041
|
}, async (newScope) => {
|
|
2790
3042
|
// Switch brain scope
|
|
2791
|
-
|
|
2792
|
-
try {
|
|
2793
|
-
spiralEngine.close();
|
|
2794
|
-
}
|
|
2795
|
-
catch { /* best effort */ }
|
|
2796
|
-
}
|
|
3043
|
+
resetSpiralEngine();
|
|
2797
3044
|
brainScope = newScope;
|
|
2798
3045
|
// Create .helixmind/ dir if switching to project and it doesn't exist
|
|
2799
3046
|
if (newScope === 'project') {
|
|
@@ -2817,8 +3064,8 @@ export async function chatCommand(options) {
|
|
|
2817
3064
|
}
|
|
2818
3065
|
}
|
|
2819
3066
|
}
|
|
2820
|
-
spiralEngine = await
|
|
2821
|
-
}, brainScope, async (action, goal) => {
|
|
3067
|
+
spiralEngine = await ensureSpiralEngine(newScope);
|
|
3068
|
+
}, brainScope, (scope) => getSpiralEngine(scope ?? brainScope), async (action, goal) => {
|
|
2822
3069
|
if (action === 'stop') {
|
|
2823
3070
|
// Stop all background sessions + autonomous mode
|
|
2824
3071
|
const running = sessionMgr.running;
|
|
@@ -3075,23 +3322,7 @@ export async function chatCommand(options) {
|
|
|
3075
3322
|
sentiment: jarvisSentiment,
|
|
3076
3323
|
getScope: () => jarvisScope,
|
|
3077
3324
|
setScope: (scope) => {
|
|
3078
|
-
|
|
3079
|
-
const newRoot = resolveJarvisRoot(scope);
|
|
3080
|
-
jarvisQueue = new JarvisQueue(newRoot);
|
|
3081
|
-
jarvisIdentity = new JarvisIdentityManager(newRoot);
|
|
3082
|
-
jarvisProposals = new ProposalJournal(newRoot);
|
|
3083
|
-
jarvisScheduler = new JarvisScheduler(newRoot);
|
|
3084
|
-
jarvisTriggers = new TriggerManager(newRoot);
|
|
3085
|
-
jarvisWorldModel = new WorldModelManager(newRoot);
|
|
3086
|
-
jarvisNotifications = new NotificationManager(newRoot);
|
|
3087
|
-
jarvisSkills = new SkillManager(newRoot);
|
|
3088
|
-
jarvisSkills.syncRegistry();
|
|
3089
|
-
jarvisSentiment = new SentimentAnalyzer(newRoot, {
|
|
3090
|
-
onShift: (from, to, frustrationLevel) => {
|
|
3091
|
-
jarvisIdentity.recordEvent({ type: 'sentiment_shift', from, to, frustrationLevel });
|
|
3092
|
-
},
|
|
3093
|
-
});
|
|
3094
|
-
jarvisAutonomy = new AutonomyManager(jarvisIdentity.getIdentity().autonomyLevel);
|
|
3325
|
+
initializeJarvisRuntime(scope);
|
|
3095
3326
|
},
|
|
3096
3327
|
getSession: () => jarvisDaemonSession,
|
|
3097
3328
|
setSession: (s) => { jarvisDaemonSession = s; },
|
|
@@ -3106,6 +3337,7 @@ export async function chatCommand(options) {
|
|
|
3106
3337
|
startTelegramBot,
|
|
3107
3338
|
learning: jarvisLearning,
|
|
3108
3339
|
startDaemon: () => {
|
|
3340
|
+
ensureJarvisRuntime();
|
|
3109
3341
|
const jName = jarvisIdentity.getIdentity().name;
|
|
3110
3342
|
const bgSession = sessionMgr.create(jName, '\u{1F916}', agentHistory);
|
|
3111
3343
|
bgSession.start();
|
|
@@ -3149,7 +3381,8 @@ export async function chatCommand(options) {
|
|
|
3149
3381
|
bgSession.controller.reset();
|
|
3150
3382
|
const rth = { text: '' };
|
|
3151
3383
|
bgSession.buffer.onSummary = (t) => { rth.text = t; };
|
|
3152
|
-
|
|
3384
|
+
const activeSpiral = await getSpiralEngine(brainScope);
|
|
3385
|
+
await sendAgentMessage(prompt, bgSession.history, provider, project, activeSpiral, config, daemonPermissions, bgSession.undoStack, checkpointStore, bgSession.controller, new ActivityIndicator(), bgSession.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; }, undefined, { enabled: false, verbose: false, strict: false }, undefined, undefined, undefined, undefined, null, undefined, jarvisLearning, undefined, jarvisSkills);
|
|
3153
3386
|
bgSession.buffer.onSummary = undefined;
|
|
3154
3387
|
return rth.text;
|
|
3155
3388
|
},
|
|
@@ -3184,9 +3417,12 @@ export async function chatCommand(options) {
|
|
|
3184
3417
|
}
|
|
3185
3418
|
},
|
|
3186
3419
|
updateStatus: () => updateStatusBar(),
|
|
3187
|
-
storeInSpiral:
|
|
3420
|
+
storeInSpiral: config.spiral.enabled ? async (content, type, tags) => {
|
|
3421
|
+
const activeSpiral = await getSpiralEngine(brainScope);
|
|
3422
|
+
if (!activeSpiral)
|
|
3423
|
+
return;
|
|
3188
3424
|
try {
|
|
3189
|
-
await
|
|
3425
|
+
await activeSpiral.add(content, { type: type, tags });
|
|
3190
3426
|
}
|
|
3191
3427
|
catch { }
|
|
3192
3428
|
} : undefined,
|
|
@@ -3197,6 +3433,8 @@ export async function chatCommand(options) {
|
|
|
3197
3433
|
jarvisLearning.recordLearning(error, solution, category, context, ['jarvis_daemon']);
|
|
3198
3434
|
},
|
|
3199
3435
|
thinkingCallbacks: buildThinkingCallbacks(bgSession),
|
|
3436
|
+
queueSkillBuildTask: queueSkillBuildFromFailure,
|
|
3437
|
+
onSkillBuildComplete: activateBuiltSkill,
|
|
3200
3438
|
});
|
|
3201
3439
|
}
|
|
3202
3440
|
catch (err) {
|
|
@@ -3245,10 +3483,11 @@ export async function chatCommand(options) {
|
|
|
3245
3483
|
}
|
|
3246
3484
|
// Frame was already cleared at processInput entry — render user message
|
|
3247
3485
|
// Use display text (badge placeholders) if available, full text goes to model
|
|
3248
|
-
renderUserMessage(
|
|
3486
|
+
renderUserMessage(renderedInput);
|
|
3249
3487
|
// Track user message in session buffer
|
|
3250
3488
|
sessionBuffer.addUserMessage(input);
|
|
3251
3489
|
// Sentiment detection on every user message
|
|
3490
|
+
ensureJarvisRuntime();
|
|
3252
3491
|
const sentimentReading = jarvisSentiment.detectSentiment(input);
|
|
3253
3492
|
jarvisSentiment.recordReading(sentimentReading);
|
|
3254
3493
|
// Auto-detect bug reports from user messages
|
|
@@ -3401,10 +3640,16 @@ export async function chatCommand(options) {
|
|
|
3401
3640
|
}
|
|
3402
3641
|
else {
|
|
3403
3642
|
// ═══ NORMAL MODE ═══
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3643
|
+
if (config.spiral.enabled && !turnDirectives.fastMode) {
|
|
3644
|
+
spiralEngine = await ensureSpiralEngine(brainScope);
|
|
3645
|
+
}
|
|
3646
|
+
const effectiveValidation = resolveValidationDecision(input, { enabled: validationEnabled, verbose: validationVerbose, strict: validationStrict }, turnDirectives);
|
|
3647
|
+
const swarmHeuristic = swarmOrchestrator.analyze(input);
|
|
3648
|
+
const shouldUseSwarm = !activeSwarm &&
|
|
3649
|
+
!turnDirectives.skipSwarm &&
|
|
3650
|
+
(turnDirectives.forceSwarm || swarmHeuristic.shouldOrchestrate);
|
|
3651
|
+
renderExecutionIntent(turnDirectives, effectiveValidation, swarmHeuristic, shouldUseSwarm);
|
|
3652
|
+
if (shouldUseSwarm) {
|
|
3408
3653
|
const { pushSwarmCreated, pushSwarmUpdated, pushSwarmCompleted, pushWorkerStarted, pushWorkerCompleted, pushOutputLine: _pushOutputLine, pushSessionCreated: _pushSessionCreated } = await import('../brain/generator.js');
|
|
3409
3654
|
const { serializeSession: _serializeSession } = await import('../brain/control-protocol.js');
|
|
3410
3655
|
const swarm = new SwarmController(swarmOrchestrator, swarmExecutor, sessionMgr, {
|
|
@@ -3434,7 +3679,7 @@ export async function chatCommand(options) {
|
|
|
3434
3679
|
};
|
|
3435
3680
|
_pushSessionCreated(_serializeSession(session));
|
|
3436
3681
|
try {
|
|
3437
|
-
await sendAgentMessage(prompt, session.history, provider, project, spiralEngine, config, permissions, session.undoStack, checkpointStore, session.controller, bgActivity, session.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; roundToolCalls++; }, undefined,
|
|
3682
|
+
await sendAgentMessage(prompt, session.history, provider, project, spiralEngine, config, permissions, session.undoStack, checkpointStore, session.controller, bgActivity, session.buffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; roundToolCalls++; }, undefined, effectiveValidation, bugJournal, undefined, undefined, undefined, null, undefined, undefined, runtime);
|
|
3438
3683
|
}
|
|
3439
3684
|
finally {
|
|
3440
3685
|
await runtime.release();
|
|
@@ -3459,8 +3704,10 @@ export async function chatCommand(options) {
|
|
|
3459
3704
|
pushWorkerCompleted,
|
|
3460
3705
|
});
|
|
3461
3706
|
activeSwarm = swarm;
|
|
3462
|
-
|
|
3463
|
-
|
|
3707
|
+
const swarmReason = swarmHeuristic.reasons.slice(0, 2).join(', ');
|
|
3708
|
+
renderInfo(chalk.hex('#ffd700')('\u{1F41D} Swarm engaged') +
|
|
3709
|
+
(swarmReason ? chalk.dim(` — ${swarmReason}`) : ''));
|
|
3710
|
+
const summary = await swarm.run(input);
|
|
3464
3711
|
activeSwarm = null;
|
|
3465
3712
|
if (summary) {
|
|
3466
3713
|
// Swarm completed — render summary
|
|
@@ -3469,7 +3716,7 @@ export async function chatCommand(options) {
|
|
|
3469
3716
|
}
|
|
3470
3717
|
else {
|
|
3471
3718
|
// Swarm decided single task — fall through to normal agent
|
|
3472
|
-
await sendAgentMessage(input, agentHistory, provider, project, spiralEngine, config, permissions, undoStack, checkpointStore, agentController, activity, sessionBuffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; roundToolCalls++; }, () => { isAtPrompt = false; },
|
|
3719
|
+
await sendAgentMessage(input, agentHistory, provider, project, spiralEngine, config, permissions, undoStack, checkpointStore, agentController, activity, sessionBuffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; roundToolCalls++; }, () => { isAtPrompt = false; }, effectiveValidation, bugJournal, browserController, visionProcessor, pushScreenshotToBrainFn, getJarvisContextForPrompt(), (label, tool) => {
|
|
3473
3720
|
currentStepLabel = label;
|
|
3474
3721
|
const fileTools = new Set(['read_file', 'write_file', 'edit_file']);
|
|
3475
3722
|
currentStepFile = fileTools.has(tool) ? label.replace(/^(reading|writing|editing)\s+/, '') : '';
|
|
@@ -3488,7 +3735,7 @@ export async function chatCommand(options) {
|
|
|
3488
3735
|
// Muting is handled by activity.setMuteCallbacks (mute during LLM stream,
|
|
3489
3736
|
// unmute during tool execution so user can answer permission prompts).
|
|
3490
3737
|
isAtPrompt = false;
|
|
3491
|
-
},
|
|
3738
|
+
}, effectiveValidation, bugJournal, browserController, visionProcessor, pushScreenshotToBrainFn, getJarvisContextForPrompt(), (label, tool) => {
|
|
3492
3739
|
currentStepLabel = label;
|
|
3493
3740
|
const fileTools = new Set(['read_file', 'write_file', 'edit_file']);
|
|
3494
3741
|
currentStepFile = fileTools.has(tool) ? label.replace(/^(reading|writing|editing)\s+/, '') : '';
|
|
@@ -3503,22 +3750,7 @@ export async function chatCommand(options) {
|
|
|
3503
3750
|
while (typeAheadBuffer.length > 0 && !agentController.isAborted) {
|
|
3504
3751
|
const buffered = typeAheadBuffer.shift();
|
|
3505
3752
|
if (buffered.trim()) {
|
|
3506
|
-
|
|
3507
|
-
sessionBuffer.addUserMessage(buffered.trim());
|
|
3508
|
-
checkpointStore.createForChat(buffered.trim(), agentHistory.length);
|
|
3509
|
-
roundToolCalls = 0;
|
|
3510
|
-
currentStepLabel = '';
|
|
3511
|
-
currentStepFile = '';
|
|
3512
|
-
agentRunning = true;
|
|
3513
|
-
agentController.reset();
|
|
3514
|
-
updateStatusBar();
|
|
3515
|
-
await sendAgentMessage(buffered.trim(), agentHistory, provider, project, spiralEngine, config, permissions, undoStack, checkpointStore, agentController, activity, sessionBuffer, (inp, out) => { sessionTokensInput += inp; sessionTokensOutput += out; }, () => { sessionToolCalls++; roundToolCalls++; }, () => { showPrompt(); }, { enabled: validationEnabled, verbose: validationVerbose, strict: validationStrict }, bugJournal, browserController, visionProcessor, pushScreenshotToBrainFn, getJarvisContextForPrompt(), (label, tool) => {
|
|
3516
|
-
currentStepLabel = label;
|
|
3517
|
-
const fileTools = new Set(['read_file', 'write_file', 'edit_file']);
|
|
3518
|
-
currentStepFile = fileTools.has(tool) ? label.replace(/^(reading|writing|editing)\s+/, '') : '';
|
|
3519
|
-
});
|
|
3520
|
-
agentRunning = false;
|
|
3521
|
-
messages.push({ role: 'user', content: buffered.trim() });
|
|
3753
|
+
await processInput(buffered.trim());
|
|
3522
3754
|
}
|
|
3523
3755
|
}
|
|
3524
3756
|
showPrompt();
|
|
@@ -3712,7 +3944,7 @@ async function runBackgroundSession(session, prompt, provider, project, spiralEn
|
|
|
3712
3944
|
durationMs: session.elapsed,
|
|
3713
3945
|
};
|
|
3714
3946
|
}
|
|
3715
|
-
async function sendAgentMessage(input, agentHistory, provider, project, spiralEngine, config, permissions, undoStack, checkpointStore, controller, activity, sessionBuffer, onTokens, onToolCall, onAgentStart, validationOpts, bugJournal, browserController, visionProcessor, onBrowserScreenshot, jarvisContext, onStepInfo, learningJournal, runtime) {
|
|
3947
|
+
async function sendAgentMessage(input, agentHistory, provider, project, spiralEngine, config, permissions, undoStack, checkpointStore, controller, activity, sessionBuffer, onTokens, onToolCall, onAgentStart, validationOpts, bugJournal, browserController, visionProcessor, onBrowserScreenshot, jarvisContext, onStepInfo, learningJournal, runtime, skillManager) {
|
|
3716
3948
|
// User message was rendered by renderUserMessage() in the caller before entering here.
|
|
3717
3949
|
const executionRoot = runtime?.executionRoot ?? process.cwd();
|
|
3718
3950
|
// Intent Detection: Check if user wants to feed the codebase
|
|
@@ -3811,6 +4043,7 @@ async function sendAgentMessage(input, agentHistory, provider, project, spiralEn
|
|
|
3811
4043
|
visionProcessor,
|
|
3812
4044
|
onBrowserScreenshot: onBrowserScreenshot ?? undefined,
|
|
3813
4045
|
learningJournal,
|
|
4046
|
+
skillManager,
|
|
3814
4047
|
worktree: runtime?.worktree,
|
|
3815
4048
|
},
|
|
3816
4049
|
checkpointStore,
|
|
@@ -4008,9 +4241,15 @@ function showFullAutonomousWarning() {
|
|
|
4008
4241
|
process.stdout.write(d('\u2502 ') + d('ESC = stop agent if needed.') + d(' \u2502') + '\n');
|
|
4009
4242
|
process.stdout.write(d('\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F') + '\n\n');
|
|
4010
4243
|
}
|
|
4011
|
-
async function handleSlashCommand(input, messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx, onModeChange, chrome, planCtx, worktreeCtx) {
|
|
4244
|
+
async function handleSlashCommand(input, messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, resolveSpiralEngine, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx, onModeChange, chrome, planCtx, worktreeCtx) {
|
|
4012
4245
|
const parts = input.split(/\s+/);
|
|
4013
4246
|
let cmd = parts[0].toLowerCase();
|
|
4247
|
+
const ensureSpiralForScope = async (scope = currentBrainScope || 'global') => {
|
|
4248
|
+
if (!resolveSpiralEngine)
|
|
4249
|
+
return spiralEngine;
|
|
4250
|
+
spiralEngine = await resolveSpiralEngine(scope);
|
|
4251
|
+
return spiralEngine;
|
|
4252
|
+
};
|
|
4014
4253
|
// ─── Chrome-aware selectMenu ───────────────────────────
|
|
4015
4254
|
// Deactivates Screen during interactive menus to prevent
|
|
4016
4255
|
// stdout hook interference (menu stacking on Windows Terminal).
|
|
@@ -4112,7 +4351,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4112
4351
|
rl.resume();
|
|
4113
4352
|
if (helpIdx >= 0 && helpCmds[helpIdx]) {
|
|
4114
4353
|
// Execute the selected command
|
|
4115
|
-
return handleSlashCommand(helpCmds[helpIdx], messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx, onModeChange, chrome, planCtx, worktreeCtx);
|
|
4354
|
+
return handleSlashCommand(helpCmds[helpIdx], messages, agentHistory, config, spiralEngine, store, rl, permissions, undoStack, checkpointStore, sessionBuffer, sessionTokens, sessionToolCalls, onProviderSwitch, onBrainSwitch, currentBrainScope, resolveSpiralEngine, onAutonomous, onValidation, sessionManager, onRegisterBrainHandlers, onSubPrompt, bugJournal, jarvisCtx, onModeChange, chrome, planCtx, worktreeCtx);
|
|
4116
4355
|
}
|
|
4117
4356
|
break;
|
|
4118
4357
|
}
|
|
@@ -4211,6 +4450,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4211
4450
|
return 'drain'; // Sub-readline may leave phantom line events
|
|
4212
4451
|
}
|
|
4213
4452
|
case '/spiral':
|
|
4453
|
+
spiralEngine = await ensureSpiralForScope(currentBrainScope || 'global');
|
|
4214
4454
|
if (spiralEngine) {
|
|
4215
4455
|
try {
|
|
4216
4456
|
const status = spiralEngine.status();
|
|
@@ -4221,7 +4461,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4221
4461
|
}
|
|
4222
4462
|
}
|
|
4223
4463
|
else {
|
|
4224
|
-
renderInfo('Spiral engine disabled.');
|
|
4464
|
+
renderInfo(config.spiral.enabled ? 'Spiral engine not available.' : 'Spiral engine disabled.');
|
|
4225
4465
|
}
|
|
4226
4466
|
break;
|
|
4227
4467
|
case '/helix':
|
|
@@ -4242,6 +4482,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4242
4482
|
renderInfo(chalk.yellow(' Cannot use local brain here — using global'));
|
|
4243
4483
|
}
|
|
4244
4484
|
}
|
|
4485
|
+
spiralEngine = await ensureSpiralForScope('project');
|
|
4245
4486
|
// Auto-start brain visualization
|
|
4246
4487
|
if (spiralEngine) {
|
|
4247
4488
|
try {
|
|
@@ -4282,6 +4523,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4282
4523
|
}
|
|
4283
4524
|
catch { /* optional */ }
|
|
4284
4525
|
}
|
|
4526
|
+
spiralEngine = await ensureSpiralForScope('global');
|
|
4285
4527
|
// Auto-start brain visualization
|
|
4286
4528
|
if (spiralEngine) {
|
|
4287
4529
|
try {
|
|
@@ -4365,6 +4607,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4365
4607
|
}
|
|
4366
4608
|
// Open 3D visualization (for /brain or /brain view)
|
|
4367
4609
|
if (!brainArg || brainArg === 'view') {
|
|
4610
|
+
spiralEngine = await ensureSpiralForScope(currentBrainScope || 'global');
|
|
4368
4611
|
if (spiralEngine) {
|
|
4369
4612
|
try {
|
|
4370
4613
|
const { exportBrainData } = await import('../brain/exporter.js');
|
|
@@ -4401,6 +4644,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4401
4644
|
// Handled directly in chatCommand() for access to inlineProgressActive flag
|
|
4402
4645
|
break;
|
|
4403
4646
|
case '/context':
|
|
4647
|
+
spiralEngine = await ensureSpiralForScope(currentBrainScope || 'global');
|
|
4404
4648
|
if (spiralEngine) {
|
|
4405
4649
|
const status = spiralEngine.status();
|
|
4406
4650
|
renderInfo(`Context: ${status.total_nodes} spiral nodes, ${status.total_edges} edges`);
|
|
@@ -4423,6 +4667,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4423
4667
|
break;
|
|
4424
4668
|
}
|
|
4425
4669
|
case '/compact':
|
|
4670
|
+
spiralEngine = await ensureSpiralForScope(currentBrainScope || 'global');
|
|
4426
4671
|
if (spiralEngine) {
|
|
4427
4672
|
const result = spiralEngine.evolve();
|
|
4428
4673
|
renderInfo(`Evolution: ${result.promoted} promoted, ${result.demoted} demoted, ${result.summarized} summarized`);
|
|
@@ -4739,6 +4984,7 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
4739
4984
|
}
|
|
4740
4985
|
case '/export': {
|
|
4741
4986
|
const outputDir = parts[1] || process.cwd();
|
|
4987
|
+
spiralEngine = await ensureSpiralForScope(currentBrainScope || 'global');
|
|
4742
4988
|
if (spiralEngine) {
|
|
4743
4989
|
try {
|
|
4744
4990
|
const { exportToZip } = await import('../brain/archive.js');
|
|
@@ -5577,7 +5823,12 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
5577
5823
|
const port = getBrainPort();
|
|
5578
5824
|
process.stdout.write(` ${theme.success('\u{1F310} Already connected')} on port ${port}\n`);
|
|
5579
5825
|
}
|
|
5580
|
-
else
|
|
5826
|
+
else {
|
|
5827
|
+
spiralEngine = await ensureSpiralForScope(currentBrainScope || 'project');
|
|
5828
|
+
if (!spiralEngine) {
|
|
5829
|
+
renderError(config.spiral.enabled ? 'No spiral engine available.' : 'Spiral engine is disabled.');
|
|
5830
|
+
break;
|
|
5831
|
+
}
|
|
5581
5832
|
const { exportBrainData } = await import('../brain/exporter.js');
|
|
5582
5833
|
exportBrainData(spiralEngine, 'HelixMind Project', currentBrainScope || 'project');
|
|
5583
5834
|
const url = await startLiveBrain(spiralEngine, 'HelixMind Project', currentBrainScope || 'project');
|
|
@@ -5586,9 +5837,6 @@ async function handleSlashCommand(input, messages, agentHistory, config, spiralE
|
|
|
5586
5837
|
process.stdout.write(` ${theme.success('\u{1F310} Brain server started:')} ${url}\n`);
|
|
5587
5838
|
process.stdout.write(` ${theme.dim('Web dashboard can now connect.')}\n`);
|
|
5588
5839
|
}
|
|
5589
|
-
else {
|
|
5590
|
-
renderError('No spiral engine available. Run /helix first.');
|
|
5591
|
-
}
|
|
5592
5840
|
}
|
|
5593
5841
|
catch (err) {
|
|
5594
5842
|
renderError(`Failed to start brain server: ${err}`);
|