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.
- package/README.md +1010 -999
- package/bin/morpheus.js +48 -48
- package/dist/channels/telegram.js +34 -29
- package/dist/cli/commands/start.js +41 -3
- package/dist/runtime/lifecycle.js +13 -0
- package/dist/runtime/memory/backfill-embeddings.js +12 -12
- package/dist/runtime/memory/sati/index.js +5 -5
- package/dist/runtime/memory/sati/repository.js +186 -186
- package/dist/runtime/memory/sati/system-prompts.js +52 -52
- package/dist/runtime/memory/session-embedding-worker.js +32 -32
- package/dist/runtime/memory/sqlite.js +151 -151
- package/dist/runtime/oracle.js +116 -116
- package/dist/runtime/tools/analytics-tools.js +12 -12
- package/dist/ui/index.html +13 -2
- package/dist/ui/manifest.webmanifest +1 -0
- package/dist/ui/pwa-192x192.png +0 -0
- package/dist/ui/pwa-512x512.png +0 -0
- package/dist/ui/pwa-maskable-192x192.png +0 -0
- package/dist/ui/pwa-maskable-512x512.png +0 -0
- package/dist/ui/registerSW.js +1 -0
- package/dist/ui/sw.js +1 -0
- package/dist/ui/vite.svg +31 -31
- package/dist/ui/workbox-26f462e7.js +1 -0
- package/package.json +84 -84
- package/dist/http/__tests__/status_api.test.js +0 -55
- package/dist/http/__tests__/status_with_server_api.test.js +0 -60
- package/dist/runtime/__tests__/agent.test.js +0 -95
- package/dist/runtime/__tests__/agent_memory_limit.test.js +0 -61
- package/dist/runtime/__tests__/agent_persistence.test.js +0 -154
- package/dist/runtime/__tests__/manual_santi_verify.js +0 -55
- package/dist/runtime/agent.js +0 -172
- package/dist/runtime/audio-agent.js +0 -55
- package/dist/runtime/santi/contracts.js +0 -1
- package/dist/runtime/santi/middleware.js +0 -61
- package/dist/runtime/santi/santi.js +0 -109
- package/dist/runtime/santi/store.js +0 -158
- 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
|
-
|
|
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.
|
|
31
|
-
|
|
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) {
|