morpheus-cli 0.3.3 → 0.3.6

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 (37) hide show
  1. package/README.md +1010 -999
  2. package/bin/morpheus.js +48 -48
  3. package/dist/channels/telegram.js +34 -29
  4. package/dist/cli/commands/start.js +41 -3
  5. package/dist/runtime/lifecycle.js +13 -0
  6. package/dist/runtime/memory/backfill-embeddings.js +12 -12
  7. package/dist/runtime/memory/sati/index.js +5 -5
  8. package/dist/runtime/memory/sati/repository.js +186 -186
  9. package/dist/runtime/memory/sati/system-prompts.js +52 -52
  10. package/dist/runtime/memory/session-embedding-worker.js +32 -32
  11. package/dist/runtime/memory/sqlite.js +151 -151
  12. package/dist/runtime/oracle.js +116 -116
  13. package/dist/runtime/tools/analytics-tools.js +12 -12
  14. package/dist/ui/index.html +13 -2
  15. package/dist/ui/manifest.webmanifest +1 -0
  16. package/dist/ui/pwa-192x192.png +0 -0
  17. package/dist/ui/pwa-512x512.png +0 -0
  18. package/dist/ui/pwa-maskable-192x192.png +0 -0
  19. package/dist/ui/pwa-maskable-512x512.png +0 -0
  20. package/dist/ui/registerSW.js +1 -0
  21. package/dist/ui/sw.js +1 -0
  22. package/dist/ui/vite.svg +31 -31
  23. package/dist/ui/workbox-26f462e7.js +1 -0
  24. package/package.json +84 -84
  25. package/dist/http/__tests__/status_api.test.js +0 -55
  26. package/dist/http/__tests__/status_with_server_api.test.js +0 -60
  27. package/dist/runtime/__tests__/agent.test.js +0 -95
  28. package/dist/runtime/__tests__/agent_memory_limit.test.js +0 -61
  29. package/dist/runtime/__tests__/agent_persistence.test.js +0 -154
  30. package/dist/runtime/__tests__/manual_santi_verify.js +0 -55
  31. package/dist/runtime/agent.js +0 -172
  32. package/dist/runtime/audio-agent.js +0 -55
  33. package/dist/runtime/santi/contracts.js +0 -1
  34. package/dist/runtime/santi/middleware.js +0 -61
  35. package/dist/runtime/santi/santi.js +0 -109
  36. package/dist/runtime/santi/store.js +0 -158
  37. package/dist/runtime/tools/__tests__/factory.test.js +0 -42
package/bin/morpheus.js CHANGED
@@ -1,49 +1,49 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- // Load .env file if it exists (Simple shim to avoid 'dotenv' dependency issues)
6
- const envPath = path.join(process.cwd(), '.env');
7
- if (fs.existsSync(envPath)) {
8
- try {
9
- const envConfig = fs.readFileSync(envPath, 'utf-8');
10
- envConfig.split('\n').forEach(line => {
11
- const trimmed = line.trim();
12
- if (!trimmed || trimmed.startsWith('#')) return;
13
-
14
- const match = trimmed.match(/^([^=]+)=(.*)$/);
15
- if (match) {
16
- const key = match[1].trim();
17
- let value = match[2].trim();
18
- // Remove quotes if present
19
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
20
- value = value.slice(1, -1);
21
- }
22
- // Don't overwrite existing env vars
23
- if (!process.env[key]) {
24
- process.env[key] = value;
25
- }
26
- }
27
- });
28
- } catch (err) {
29
- // Ignore .env errors
30
- }
31
- }
32
-
33
- // Suppress experimental warnings for JSON modules
34
- const originalEmit = process.emit;
35
- process.emit = function (name, data, ...args) {
36
- if (
37
- name === 'warning' &&
38
- typeof data === 'object' &&
39
- data.name === 'ExperimentalWarning' &&
40
- data.message.includes('Importing JSON modules')
41
- ) {
42
- return false;
43
- }
44
- return originalEmit.apply(process, [name, data, ...args]);
45
- };
46
-
47
- // Use dynamic import to ensure the warning suppression is active before the module graph loads
48
- const { cli } = await import('../dist/cli/index.js');
49
- cli();
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ // Load .env file if it exists (Simple shim to avoid 'dotenv' dependency issues)
6
+ const envPath = path.join(process.cwd(), '.env');
7
+ if (fs.existsSync(envPath)) {
8
+ try {
9
+ const envConfig = fs.readFileSync(envPath, 'utf-8');
10
+ envConfig.split('\n').forEach(line => {
11
+ const trimmed = line.trim();
12
+ if (!trimmed || trimmed.startsWith('#')) return;
13
+
14
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
15
+ if (match) {
16
+ const key = match[1].trim();
17
+ let value = match[2].trim();
18
+ // Remove quotes if present
19
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
20
+ value = value.slice(1, -1);
21
+ }
22
+ // Don't overwrite existing env vars
23
+ if (!process.env[key]) {
24
+ process.env[key] = value;
25
+ }
26
+ }
27
+ });
28
+ } catch (err) {
29
+ // Ignore .env errors
30
+ }
31
+ }
32
+
33
+ // Suppress experimental warnings for JSON modules
34
+ const originalEmit = process.emit;
35
+ process.emit = function (name, data, ...args) {
36
+ if (
37
+ name === 'warning' &&
38
+ typeof data === 'object' &&
39
+ data.name === 'ExperimentalWarning' &&
40
+ data.message.includes('Importing JSON modules')
41
+ ) {
42
+ return false;
43
+ }
44
+ return originalEmit.apply(process, [name, data, ...args]);
45
+ };
46
+
47
+ // Use dynamic import to ensure the warning suppression is active before the module graph loads
48
+ const { cli } = await import('../dist/cli/index.js');
49
+ cli();
@@ -22,17 +22,17 @@ export class TelegramAdapter {
22
22
  telephonist = null;
23
23
  telephonistProvider = null;
24
24
  history = new SQLiteChatMessageHistory({ sessionId: '' });
25
- HELP_MESSAGE = `/start - Show this welcome message and available commands
26
- /status - Check the status of the Morpheus agent
27
- /doctor - Diagnose environment and configuration issues
28
- /stats - Show token usage statistics
29
- /help - Show available commands
30
- /zaion - Show system configurations
31
- /sati <qnt> - Show specific memories
32
- /newsession - Archive current session and start fresh
33
- /sessions - List all sessions with titles and switch between them
34
- /restart - Restart the Morpheus agent
35
- /mcpreload - Reload MCP servers without restarting
25
+ HELP_MESSAGE = `/start - Show this welcome message and available commands
26
+ /status - Check the status of the Morpheus agent
27
+ /doctor - Diagnose environment and configuration issues
28
+ /stats - Show token usage statistics
29
+ /help - Show available commands
30
+ /zaion - Show system configurations
31
+ /sati <qnt> - Show specific memories
32
+ /newsession - Archive current session and start fresh
33
+ /sessions - List all sessions with titles and switch between them
34
+ /restart - Restart the Morpheus agent
35
+ /mcpreload - Reload MCP servers without restarting
36
36
  /mcp or /mcps - List registered MCP servers`;
37
37
  constructor(oracle) {
38
38
  this.oracle = oracle;
@@ -474,13 +474,13 @@ export class TelegramAdapter {
474
474
  }
475
475
  }
476
476
  async handleStartCommand(ctx, user) {
477
- const welcomeMessage = `
478
- Hello, @${user}! I am ${this.config.get().agent.name}, ${this.config.get().agent.personality}.
479
-
480
- I am your local AI operator/agent. Here are the commands you can use:
481
-
482
- ${this.HELP_MESSAGE}
483
-
477
+ const welcomeMessage = `
478
+ Hello, @${user}! I am ${this.config.get().agent.name}, ${this.config.get().agent.personality}.
479
+
480
+ I am your local AI operator/agent. Here are the commands you can use:
481
+
482
+ ${this.HELP_MESSAGE}
483
+
484
484
  How can I assist you today?`;
485
485
  await ctx.reply(welcomeMessage);
486
486
  }
@@ -578,24 +578,29 @@ How can I assist you today?`;
578
578
  }
579
579
  }
580
580
  async handleDefaultCommand(ctx, user, command) {
581
- const prompt = `O usuário enviou o comando: ${command},
582
- Não entendemos o comando
583
- temos os seguintes comandos disponíveis:
584
- ${this.HELP_MESSAGE}
585
- Identifique se ele talvez tenha errado o comando e pergunte se ele não quis executar outro comando.
581
+ const prompt = `O usuário enviou o comando: ${command},
582
+ Não entendemos o comando
583
+ temos os seguintes comandos disponíveis:
584
+ ${this.HELP_MESSAGE}
585
+ Identifique se ele talvez tenha errado o comando e pergunte se ele não quis executar outro comando.
586
586
  Só faça isso agora.`;
587
587
  let response = await this.oracle.chat(prompt);
588
588
  if (response) {
589
- await ctx.reply(response, { parse_mode: 'Markdown' });
589
+ try {
590
+ await ctx.reply(response, { parse_mode: 'Markdown' });
591
+ }
592
+ catch {
593
+ await ctx.reply(response);
594
+ }
590
595
  }
591
596
  // await ctx.reply(`Command not recognized. Type /help to see available commands.`);
592
597
  }
593
598
  async handleHelpCommand(ctx, user) {
594
- const helpMessage = `
595
- *Available Commands:*
596
-
597
- ${this.HELP_MESSAGE}
598
-
599
+ const helpMessage = `
600
+ *Available Commands:*
601
+
602
+ ${this.HELP_MESSAGE}
603
+
599
604
  How can I assist you today?`;
600
605
  await ctx.reply(helpMessage, { parse_mode: 'Markdown' });
601
606
  }
@@ -1,9 +1,10 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import fs from 'fs-extra';
4
+ import { confirm } from '@inquirer/prompts';
4
5
  import { scaffold } from '../../runtime/scaffold.js';
5
6
  import { DisplayManager } from '../../runtime/display.js';
6
- import { writePid, readPid, isProcessRunning, clearPid, checkStalePid } from '../../runtime/lifecycle.js';
7
+ import { writePid, readPid, isProcessRunning, clearPid, checkStalePid, killProcess } from '../../runtime/lifecycle.js';
7
8
  import { ConfigManager } from '../../config/manager.js';
8
9
  import { renderBanner } from '../utils/render.js';
9
10
  import { TelegramAdapter } from '../../channels/telegram.js';
@@ -18,6 +19,7 @@ export const startCommand = new Command('start')
18
19
  .option('--ui', 'Enable web UI', true)
19
20
  .option('--no-ui', 'Disable web UI')
20
21
  .option('-p, --port <number>', 'Port for web UI', '3333')
22
+ .option('-y, --yes', 'Automatically answer yes to prompts')
21
23
  .action(async (options) => {
22
24
  const display = DisplayManager.getInstance();
23
25
  try {
@@ -27,9 +29,45 @@ export const startCommand = new Command('start')
27
29
  await checkStalePid();
28
30
  const existingPid = await readPid();
29
31
  if (existingPid !== null && isProcessRunning(existingPid)) {
30
- display.log(chalk.red(`Morpheus is already running (PID: ${existingPid})`));
31
- process.exit(1);
32
+ display.log(chalk.yellow(`Morpheus is already running (PID: ${existingPid})`));
33
+ let shouldKill = options.yes;
34
+ if (!shouldKill) {
35
+ try {
36
+ shouldKill = await confirm({
37
+ message: 'Do you want to stop the running instance and start a new one?',
38
+ default: false,
39
+ });
40
+ }
41
+ catch (error) {
42
+ // User cancelled (Ctrl+C)
43
+ display.log(chalk.gray('\nCancelled'));
44
+ process.exit(1);
45
+ }
46
+ }
47
+ if (shouldKill) {
48
+ display.log(chalk.cyan(`Stopping existing process (PID: ${existingPid})...`));
49
+ const killed = killProcess(existingPid);
50
+ if (killed) {
51
+ display.log(chalk.green('✓ Process stopped successfully'));
52
+ await clearPid();
53
+ // Give a moment for the process to fully terminate
54
+ await new Promise(resolve => setTimeout(resolve, 1000));
55
+ }
56
+ else {
57
+ display.log(chalk.red('Failed to stop the process'));
58
+ await clearPid();
59
+ process.exit(1);
60
+ }
61
+ }
62
+ else {
63
+ display.log(chalk.gray('Use a different port or stop the running instance manually'));
64
+ process.exit(0);
65
+ }
32
66
  }
67
+ // Always remove any leftover PID file before writing the new one.
68
+ // Guards against PID reuse on Linux where a stale PID may coincidentally
69
+ // belong to an unrelated process, causing checkStalePid to keep it.
70
+ await clearPid();
33
71
  // Check config existence
34
72
  if (!await fs.pathExists(PATHS.config)) {
35
73
  display.log(chalk.yellow("Configuration not found."));
@@ -39,3 +39,16 @@ export async function checkStalePid() {
39
39
  }
40
40
  }
41
41
  }
42
+ export function killProcess(pid) {
43
+ try {
44
+ process.kill(pid, 'SIGTERM');
45
+ return true;
46
+ }
47
+ catch (e) {
48
+ if (e.code === 'ESRCH') {
49
+ // Process doesn't exist
50
+ return false;
51
+ }
52
+ throw e;
53
+ }
54
+ }
@@ -12,12 +12,12 @@ const BATCH_SIZE = 50;
12
12
  async function run() {
13
13
  console.log('🔎 Buscando memórias sem embedding vetorial...');
14
14
  while (true) {
15
- const rows = db.prepare(`
16
- SELECT m.rowid, m.summary, m.details
17
- FROM long_term_memory m
18
- LEFT JOIN memory_vec v ON m.rowid = v.rowid
19
- WHERE v.rowid IS NULL
20
- LIMIT ?
15
+ const rows = db.prepare(`
16
+ SELECT m.rowid, m.summary, m.details
17
+ FROM long_term_memory m
18
+ LEFT JOIN memory_vec v ON m.rowid = v.rowid
19
+ WHERE v.rowid IS NULL
20
+ LIMIT ?
21
21
  `).all(BATCH_SIZE);
22
22
  if (rows.length === 0) {
23
23
  console.log('✅ Todas as memórias já possuem embedding.');
@@ -30,13 +30,13 @@ async function run() {
30
30
  const vector = await embeddingService.generate(text);
31
31
  vectors.push({ rowid: row.rowid, vector });
32
32
  }
33
- const insertVec = db.prepare(`
34
- INSERT INTO memory_vec (embedding)
35
- VALUES (?)
33
+ const insertVec = db.prepare(`
34
+ INSERT INTO memory_vec (embedding)
35
+ VALUES (?)
36
36
  `);
37
- const insertMap = db.prepare(`
38
- INSERT INTO memory_embedding_map (memory_id, vec_rowid)
39
- VALUES (?, ?)
37
+ const insertMap = db.prepare(`
38
+ INSERT INTO memory_embedding_map (memory_id, vec_rowid)
39
+ VALUES (?, ?)
40
40
  `);
41
41
  const transaction = db.transaction((items) => {
42
42
  for (const item of items) {
@@ -28,11 +28,11 @@ export class SatiMemoryMiddleware {
28
28
  .map(m => `- [${m.category.toUpperCase()}] ${m.summary}`)
29
29
  .join('\n');
30
30
  display.log(`Retrieved ${result.relevant_memories.length} memories.`, { source: 'Sati' });
31
- return new AIMessage(`
32
- ### LONG-TERM MEMORY (SATI)
33
- The following information was retrieved from previous sessions. Use it if relevant:
34
-
35
- ${memoryContext}
31
+ return new AIMessage(`
32
+ ### LONG-TERM MEMORY (SATI)
33
+ The following information was retrieved from previous sessions. Use it if relevant:
34
+
35
+ ${memoryContext}
36
36
  `);
37
37
  }
38
38
  catch (error) {