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
@@ -95,37 +95,37 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
95
95
  */
96
96
  ensureTable() {
97
97
  try {
98
- this.db.exec(`
99
- CREATE TABLE IF NOT EXISTS messages (
100
- id INTEGER PRIMARY KEY AUTOINCREMENT,
101
- session_id TEXT NOT NULL,
102
- type TEXT NOT NULL,
103
- content TEXT NOT NULL,
104
- created_at INTEGER NOT NULL,
105
- input_tokens INTEGER,
106
- output_tokens INTEGER,
107
- total_tokens INTEGER,
108
- cache_read_tokens INTEGER,
109
- provider TEXT,
110
- model TEXT
111
- );
112
-
113
- CREATE INDEX IF NOT EXISTS idx_messages_session_id
114
- ON messages(session_id);
115
-
116
- CREATE TABLE IF NOT EXISTS sessions (
117
- id TEXT PRIMARY KEY,
118
- title TEXT,
119
- status TEXT CHECK (
120
- status IN ('active', 'paused', 'archived', 'deleted')
121
- ) NOT NULL DEFAULT 'paused',
122
- started_at INTEGER NOT NULL,
123
- ended_at INTEGER,
124
- archived_at INTEGER,
125
- deleted_at INTEGER,
126
- embedding_status TEXT CHECK (embedding_status IN ('none', 'pending', 'embedded', 'failed')) NOT NULL DEFAULT 'none'
127
- );
128
-
98
+ this.db.exec(`
99
+ CREATE TABLE IF NOT EXISTS messages (
100
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
101
+ session_id TEXT NOT NULL,
102
+ type TEXT NOT NULL,
103
+ content TEXT NOT NULL,
104
+ created_at INTEGER NOT NULL,
105
+ input_tokens INTEGER,
106
+ output_tokens INTEGER,
107
+ total_tokens INTEGER,
108
+ cache_read_tokens INTEGER,
109
+ provider TEXT,
110
+ model TEXT
111
+ );
112
+
113
+ CREATE INDEX IF NOT EXISTS idx_messages_session_id
114
+ ON messages(session_id);
115
+
116
+ CREATE TABLE IF NOT EXISTS sessions (
117
+ id TEXT PRIMARY KEY,
118
+ title TEXT,
119
+ status TEXT CHECK (
120
+ status IN ('active', 'paused', 'archived', 'deleted')
121
+ ) NOT NULL DEFAULT 'paused',
122
+ started_at INTEGER NOT NULL,
123
+ ended_at INTEGER,
124
+ archived_at INTEGER,
125
+ deleted_at INTEGER,
126
+ embedding_status TEXT CHECK (embedding_status IN ('none', 'pending', 'embedded', 'failed')) NOT NULL DEFAULT 'none'
127
+ );
128
+
129
129
  `);
130
130
  this.migrateTable();
131
131
  }
@@ -174,10 +174,10 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
174
174
  async getMessages() {
175
175
  try {
176
176
  // Fetch new columns
177
- const stmt = this.db.prepare(`SELECT type, content, input_tokens, output_tokens, total_tokens, cache_read_tokens, provider, model
178
- FROM messages
179
- WHERE session_id = ?
180
- ORDER BY id DESC
177
+ const stmt = this.db.prepare(`SELECT type, content, input_tokens, output_tokens, total_tokens, cache_read_tokens, provider, model
178
+ FROM messages
179
+ WHERE session_id = ?
180
+ ORDER BY id DESC
181
181
  LIMIT ?`);
182
182
  const rows = stmt.all(this.sessionId, this.limit);
183
183
  return rows.map((row) => {
@@ -341,21 +341,21 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
341
341
  */
342
342
  async setSessionTitleIfNeeded() {
343
343
  // Verificar se a sessão já tem título
344
- const session = this.db.prepare(`
345
- SELECT title FROM sessions
346
- WHERE id = ?
344
+ const session = this.db.prepare(`
345
+ SELECT title FROM sessions
346
+ WHERE id = ?
347
347
  `).get(this.sessionId);
348
348
  if (session && session.title) {
349
349
  // A sessão já tem título, não precisa fazer nada
350
350
  return;
351
351
  }
352
352
  // Obter a mensagem mais antiga do tipo "human" da sessão
353
- const oldestHumanMessage = this.db.prepare(`
354
- SELECT content
355
- FROM messages
356
- WHERE session_id = ? AND type = 'human'
357
- ORDER BY created_at ASC
358
- LIMIT 1
353
+ const oldestHumanMessage = this.db.prepare(`
354
+ SELECT content
355
+ FROM messages
356
+ WHERE session_id = ? AND type = 'human'
357
+ ORDER BY created_at ASC
358
+ LIMIT 1
359
359
  `).get(this.sessionId);
360
360
  if (oldestHumanMessage) {
361
361
  // Pegar os primeiros 50 caracteres como título
@@ -412,16 +412,16 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
412
412
  */
413
413
  async getUsageStatsByProviderAndModel() {
414
414
  try {
415
- const stmt = this.db.prepare(`SELECT
416
- provider,
417
- COALESCE(model, 'unknown') as model,
418
- SUM(input_tokens) as totalInputTokens,
419
- SUM(output_tokens) as totalOutputTokens,
420
- SUM(total_tokens) as totalTokens,
421
- COUNT(*) as messageCount
422
- FROM messages
423
- WHERE provider IS NOT NULL
424
- GROUP BY provider, COALESCE(model, 'unknown')
415
+ const stmt = this.db.prepare(`SELECT
416
+ provider,
417
+ COALESCE(model, 'unknown') as model,
418
+ SUM(input_tokens) as totalInputTokens,
419
+ SUM(output_tokens) as totalOutputTokens,
420
+ SUM(total_tokens) as totalTokens,
421
+ COUNT(*) as messageCount
422
+ FROM messages
423
+ WHERE provider IS NOT NULL
424
+ GROUP BY provider, COALESCE(model, 'unknown')
425
425
  ORDER BY provider, model`);
426
426
  const rows = stmt.all();
427
427
  return rows.map((row) => ({
@@ -485,26 +485,26 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
485
485
  // Transação para garantir consistência
486
486
  const tx = this.db.transaction(() => {
487
487
  // Pegar a sessão atualmente ativa
488
- const activeSession = this.db.prepare(`
489
- SELECT id FROM sessions
490
- WHERE status = 'active'
488
+ const activeSession = this.db.prepare(`
489
+ SELECT id FROM sessions
490
+ WHERE status = 'active'
491
491
  `).get();
492
492
  // Se houver uma sessão ativa, mudar seu status para 'paused'
493
493
  if (activeSession) {
494
- this.db.prepare(`
495
- UPDATE sessions
496
- SET status = 'paused'
497
- WHERE id = ?
494
+ this.db.prepare(`
495
+ UPDATE sessions
496
+ SET status = 'paused'
497
+ WHERE id = ?
498
498
  `).run(activeSession.id);
499
499
  }
500
500
  // Criar uma nova sessão ativa
501
501
  const newId = randomUUID();
502
- this.db.prepare(`
503
- INSERT INTO sessions (
504
- id,
505
- started_at,
506
- status
507
- ) VALUES (?, ?, 'active')
502
+ this.db.prepare(`
503
+ INSERT INTO sessions (
504
+ id,
505
+ started_at,
506
+ status
507
+ ) VALUES (?, ?, 'active')
508
508
  `).run(newId, now);
509
509
  // Atualizar o ID da sessão atual desta instância
510
510
  this.sessionId = newId;
@@ -521,9 +521,9 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
521
521
  */
522
522
  async archiveSession(sessionId) {
523
523
  // Validar sessão existe e está em active ou paused
524
- const session = this.db.prepare(`
525
- SELECT id, status FROM sessions
526
- WHERE id = ?
524
+ const session = this.db.prepare(`
525
+ SELECT id, status FROM sessions
526
+ WHERE id = ?
527
527
  `).get(sessionId);
528
528
  if (!session) {
529
529
  throw new Error(`Sessão com ID ${sessionId} não encontrada.`);
@@ -535,20 +535,20 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
535
535
  // Transação para garantir consistência
536
536
  const tx = this.db.transaction(() => {
537
537
  // Marcar sessão como: status = 'archived', ended_at = now, archived_at = now, embedding_status = 'pending'
538
- this.db.prepare(`
539
- UPDATE sessions
540
- SET status = 'archived',
541
- ended_at = ?,
542
- archived_at = ?,
543
- embedding_status = 'pending'
544
- WHERE id = ?
538
+ this.db.prepare(`
539
+ UPDATE sessions
540
+ SET status = 'archived',
541
+ ended_at = ?,
542
+ archived_at = ?,
543
+ embedding_status = 'pending'
544
+ WHERE id = ?
545
545
  `).run(now, now, sessionId);
546
546
  // Exportar mensagens → texto
547
- const messages = this.db.prepare(`
548
- SELECT type, content
549
- FROM messages
550
- WHERE session_id = ?
551
- ORDER BY created_at ASC
547
+ const messages = this.db.prepare(`
548
+ SELECT type, content
549
+ FROM messages
550
+ WHERE session_id = ?
551
+ ORDER BY created_at ASC
552
552
  `).all(sessionId);
553
553
  if (messages.length > 0) {
554
554
  const sessionText = messages
@@ -558,22 +558,22 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
558
558
  if (this.dbSati) {
559
559
  const chunks = this.chunkText(sessionText);
560
560
  for (let i = 0; i < chunks.length; i++) {
561
- this.dbSati.prepare(`
562
- INSERT INTO session_chunks (
563
- id,
564
- session_id,
565
- chunk_index,
566
- content,
567
- created_at
568
- ) VALUES (?, ?, ?, ?, ?)
561
+ this.dbSati.prepare(`
562
+ INSERT INTO session_chunks (
563
+ id,
564
+ session_id,
565
+ chunk_index,
566
+ content,
567
+ created_at
568
+ ) VALUES (?, ?, ?, ?, ?)
569
569
  `).run(randomUUID(), sessionId, i, chunks[i], now);
570
570
  }
571
571
  this.display.log(`🧩 ${chunks.length} chunks criados para sessão ${sessionId}`, { source: 'Sati' });
572
572
  }
573
573
  // Remover mensagens da sessão após criar os chunks
574
- this.db.prepare(`
575
- DELETE FROM messages
576
- WHERE session_id = ?
574
+ this.db.prepare(`
575
+ DELETE FROM messages
576
+ WHERE session_id = ?
577
577
  `).run(sessionId);
578
578
  }
579
579
  });
@@ -587,9 +587,9 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
587
587
  */
588
588
  async deleteSession(sessionId) {
589
589
  // Validar sessão existe
590
- const session = this.db.prepare(`
591
- SELECT id, status FROM sessions
592
- WHERE id = ?
590
+ const session = this.db.prepare(`
591
+ SELECT id, status FROM sessions
592
+ WHERE id = ?
593
593
  `).get(sessionId);
594
594
  if (!session) {
595
595
  throw new Error(`Sessão com ID ${sessionId} não encontrada.`);
@@ -602,33 +602,33 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
602
602
  // Transação: deletar mensagens da sessão, marcar sessão como: status = 'deleted', deleted_at = now
603
603
  const tx = this.db.transaction(() => {
604
604
  // Deletar mensagens da sessão
605
- this.db.prepare(`
606
- DELETE FROM messages
607
- WHERE session_id = ?
605
+ this.db.prepare(`
606
+ DELETE FROM messages
607
+ WHERE session_id = ?
608
608
  `).run(sessionId);
609
609
  // Marcar sessão como: status = 'deleted', deleted_at = now
610
- this.db.prepare(`
611
- UPDATE sessions
612
- SET status = 'deleted',
613
- deleted_at = ?
614
- WHERE id = ?
610
+ this.db.prepare(`
611
+ UPDATE sessions
612
+ SET status = 'deleted',
613
+ deleted_at = ?
614
+ WHERE id = ?
615
615
  `).run(now, sessionId);
616
616
  });
617
617
  tx(); // Executar a transação
618
618
  // Se a sessão era active, verificar se há outra para ativar
619
619
  if (session.status === 'active') {
620
- const nextSession = this.db.prepare(`
621
- SELECT id FROM sessions
622
- WHERE status = 'paused'
623
- ORDER BY started_at DESC
624
- LIMIT 1
620
+ const nextSession = this.db.prepare(`
621
+ SELECT id FROM sessions
622
+ WHERE status = 'paused'
623
+ ORDER BY started_at DESC
624
+ LIMIT 1
625
625
  `).get();
626
626
  if (nextSession) {
627
627
  // Promover a próxima sessão a ativa
628
- this.db.prepare(`
629
- UPDATE sessions
630
- SET status = 'active'
631
- WHERE id = ?
628
+ this.db.prepare(`
629
+ UPDATE sessions
630
+ SET status = 'active'
631
+ WHERE id = ?
632
632
  `).run(nextSession.id);
633
633
  }
634
634
  else {
@@ -644,9 +644,9 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
644
644
  */
645
645
  async renameSession(sessionId, title) {
646
646
  // Validar sessão existe e status ∈ (paused, active)
647
- const session = this.db.prepare(`
648
- SELECT id, status FROM sessions
649
- WHERE id = ?
647
+ const session = this.db.prepare(`
648
+ SELECT id, status FROM sessions
649
+ WHERE id = ?
650
650
  `).get(sessionId);
651
651
  if (!session) {
652
652
  throw new Error(`Sessão com ID ${sessionId} não encontrada.`);
@@ -657,10 +657,10 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
657
657
  // Transação para garantir consistência
658
658
  const tx = this.db.transaction(() => {
659
659
  // Atualizar o título da sessão
660
- this.db.prepare(`
661
- UPDATE sessions
662
- SET title = ?
663
- WHERE id = ?
660
+ this.db.prepare(`
661
+ UPDATE sessions
662
+ SET title = ?
663
+ WHERE id = ?
664
664
  `).run(title, sessionId);
665
665
  });
666
666
  tx(); // Executar a transação
@@ -673,9 +673,9 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
673
673
  */
674
674
  async switchSession(targetSessionId) {
675
675
  // Validar sessão alvo: existe e status ∈ (paused, active)
676
- const targetSession = this.db.prepare(`
677
- SELECT id, status FROM sessions
678
- WHERE id = ?
676
+ const targetSession = this.db.prepare(`
677
+ SELECT id, status FROM sessions
678
+ WHERE id = ?
679
679
  `).get(targetSessionId);
680
680
  if (!targetSession) {
681
681
  throw new Error(`Sessão alvo com ID ${targetSessionId} não encontrada.`);
@@ -690,23 +690,23 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
690
690
  // Transação: sessão atual active → paused, sessão alvo → active
691
691
  const tx = this.db.transaction(() => {
692
692
  // Pegar a sessão atualmente ativa
693
- const currentActiveSession = this.db.prepare(`
694
- SELECT id FROM sessions
695
- WHERE status = 'active'
693
+ const currentActiveSession = this.db.prepare(`
694
+ SELECT id FROM sessions
695
+ WHERE status = 'active'
696
696
  `).get();
697
697
  // Se houver uma sessão ativa, mudar seu status para 'paused'
698
698
  if (currentActiveSession) {
699
- this.db.prepare(`
700
- UPDATE sessions
701
- SET status = 'paused'
702
- WHERE id = ?
699
+ this.db.prepare(`
700
+ UPDATE sessions
701
+ SET status = 'paused'
702
+ WHERE id = ?
703
703
  `).run(currentActiveSession.id);
704
704
  }
705
705
  // Mudar o status da sessão alvo para 'active'
706
- this.db.prepare(`
707
- UPDATE sessions
708
- SET status = 'active'
709
- WHERE id = ?
706
+ this.db.prepare(`
707
+ UPDATE sessions
708
+ SET status = 'active'
709
+ WHERE id = ?
710
710
  `).run(targetSessionId);
711
711
  });
712
712
  tx(); // Executar a transação
@@ -718,9 +718,9 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
718
718
  */
719
719
  async getCurrentSessionOrCreate() {
720
720
  // Buscar sessão com status = 'active'
721
- const activeSession = this.db.prepare(`
722
- SELECT id FROM sessions
723
- WHERE status = 'active'
721
+ const activeSession = this.db.prepare(`
722
+ SELECT id FROM sessions
723
+ WHERE status = 'active'
724
724
  `).get();
725
725
  if (activeSession) {
726
726
  // Se existir, retornar seu id
@@ -734,21 +734,21 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
734
734
  }
735
735
  async createFreshSession() {
736
736
  // Validar que não existe sessão 'active'
737
- const activeSession = this.db.prepare(`
738
- SELECT id FROM sessions
739
- WHERE status = 'active'
737
+ const activeSession = this.db.prepare(`
738
+ SELECT id FROM sessions
739
+ WHERE status = 'active'
740
740
  `).get();
741
741
  if (activeSession) {
742
742
  throw new Error('Já existe uma sessão ativa. Não é possível criar uma nova sessão ativa.');
743
743
  }
744
744
  const now = Date.now();
745
745
  const newId = randomUUID();
746
- this.db.prepare(`
747
- INSERT INTO sessions (
748
- id,
749
- started_at,
750
- status
751
- ) VALUES (?, ?, 'active')
746
+ this.db.prepare(`
747
+ INSERT INTO sessions (
748
+ id,
749
+ started_at,
750
+ status
751
+ ) VALUES (?, ?, 'active')
752
752
  `).run(newId, now);
753
753
  return newId;
754
754
  }
@@ -782,11 +782,11 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
782
782
  * Returns an array of session objects containing id, title, status, and started_at.
783
783
  */
784
784
  async listSessions() {
785
- const sessions = this.db.prepare(`
786
- SELECT id, title, status, started_at
787
- FROM sessions
788
- WHERE status IN ('active', 'paused')
789
- ORDER BY started_at DESC
785
+ const sessions = this.db.prepare(`
786
+ SELECT id, title, status, started_at
787
+ FROM sessions
788
+ WHERE status IN ('active', 'paused')
789
+ ORDER BY started_at DESC
790
790
  `).all();
791
791
  return sessions;
792
792
  }