morpheus-cli 0.9.13 → 0.9.22
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 +48 -17
- package/dist/channels/discord.js +93 -6
- package/dist/channels/telegram.js +109 -9
- package/dist/cli/commands/start.js +15 -0
- package/dist/config/manager.js +20 -1
- package/dist/config/paths.js +4 -0
- package/dist/config/schemas.js +15 -0
- package/dist/http/api.js +5 -1
- package/dist/http/middleware/auth.js +2 -1
- package/dist/http/routers/danger.js +4 -5
- package/dist/http/routers/display.js +33 -0
- package/dist/http/routers/link.js +2 -2
- package/dist/runtime/__tests__/telephonist-tts.test.js +84 -0
- package/dist/runtime/adapters/AuditRepositoryAdapter.js +6 -0
- package/dist/runtime/adapters/ChannelNotifierAdapter.js +9 -0
- package/dist/runtime/adapters/LangChainProviderAdapter.js +9 -0
- package/dist/runtime/adapters/SQLiteChatHistoryAdapter.js +15 -0
- package/dist/runtime/adapters/SQLiteTaskEnqueuerAdapter.js +6 -0
- package/dist/runtime/adapters/index.js +5 -0
- package/dist/runtime/audit/repository.js +6 -2
- package/dist/runtime/chronos/repository.js +2 -2
- package/dist/runtime/container.js +50 -0
- package/dist/runtime/display.js +17 -2
- package/dist/runtime/hot-reload.js +6 -9
- package/dist/runtime/memory/backfill-embeddings.js +2 -3
- package/dist/runtime/memory/sati/repository.js +3 -3
- package/dist/runtime/memory/sqlite.js +3 -3
- package/dist/runtime/memory/trinity-db.js +2 -2
- package/dist/runtime/ports/IAuditEmitter.js +1 -0
- package/dist/runtime/ports/IChatHistory.js +1 -0
- package/dist/runtime/ports/ILLMProviderFactory.js +1 -0
- package/dist/runtime/ports/INotifier.js +1 -0
- package/dist/runtime/ports/ITaskEnqueuer.js +1 -0
- package/dist/runtime/ports/index.js +1 -0
- package/dist/runtime/providers/factory.js +8 -52
- package/dist/runtime/providers/strategies.js +66 -0
- package/dist/runtime/setup/repository.js +2 -2
- package/dist/runtime/subagents/apoc.js +2 -2
- package/dist/runtime/subagents/link/link.js +2 -2
- package/dist/runtime/subagents/link/repository.js +2 -2
- package/dist/runtime/subagents/link/worker.js +3 -3
- package/dist/runtime/subagents/neo.js +2 -2
- package/dist/runtime/subagents/trinity/trinity.js +2 -2
- package/dist/runtime/tasks/repository.js +2 -2
- package/dist/runtime/telephonist.js +160 -0
- package/dist/runtime/tools/delegation-utils.js +5 -7
- package/dist/runtime/tools/morpheus-tools.js +6 -7
- package/dist/runtime/tools/smith-tool.js +5 -7
- package/dist/runtime/webhooks/repository.js +2 -2
- package/dist/types/config.js +6 -0
- package/dist/ui/assets/AuditDashboard-z3OBbJ8I.js +1 -0
- package/dist/ui/assets/{Chat-UVoDlqqM.js → Chat-aFz9FjrD.js} +7 -7
- package/dist/ui/assets/{Chronos-Dfs_pOsc.js → Chronos-MP_NCj2A.js} +1 -1
- package/dist/ui/assets/{ConfirmationModal-BBIjVef7.js → ConfirmationModal-B3gHIVKY.js} +1 -1
- package/dist/ui/assets/Dashboard-OyZXnj44.js +4120 -0
- package/dist/ui/assets/{DeleteConfirmationModal-Du85q5u2.js → DeleteConfirmationModal-D8QsQzwP.js} +1 -1
- package/dist/ui/assets/{Documents-DguILrI8.js → Documents-B8g_yv4f.js} +1 -1
- package/dist/ui/assets/{Logs-BDup2FET.js → Logs-BWufAtHa.js} +1 -1
- package/dist/ui/assets/{MCPManager-WBdh1rum.js → MCPManager-lLoGEyBy.js} +1 -1
- package/dist/ui/assets/ModelPricing-CuYIUwXt.js +1 -0
- package/dist/ui/assets/Notifications-nI--fmYx.js +1 -0
- package/dist/ui/assets/{Pagination-BHZKk42X.js → Pagination-D4ShqUKO.js} +1 -1
- package/dist/ui/assets/SatiMemories-DO3JDQBi.js +1 -0
- package/dist/ui/assets/{SelectInput-KVLsnfra.js → SelectInput-BPDcd3y7.js} +1 -1
- package/dist/ui/assets/SessionAudit-BWtJRkj1.js +9 -0
- package/dist/ui/assets/Settings-CblauAVd.js +49 -0
- package/dist/ui/assets/Skills-Dw6G5c8W.js +7 -0
- package/dist/ui/assets/Smiths-B6-CnRMv.js +1 -0
- package/dist/ui/assets/Switch-C7TxLq0E.js +1 -0
- package/dist/ui/assets/Tasks-DzUyw5z3.js +1 -0
- package/dist/ui/assets/{TrinityDatabases-DYHJunk7.js → TrinityDatabases-DCjdwnLH.js} +1 -1
- package/dist/ui/assets/{UsageStats-BpGXaHgW.js → UsageStats-VajzjndO.js} +1 -1
- package/dist/ui/assets/{WebhookManager-D2muhYy9.js → WebhookManager-BbfMCiy-.js} +2 -2
- package/dist/ui/assets/{agents-CgqJea9n.js → agents-CN_AKX_I.js} +1 -1
- package/dist/ui/assets/{audit-Dc3YW0-4.js → audit-M-5UGwoK.js} +1 -1
- package/dist/ui/assets/{chronos-CZvGhZQB.js → chronos-mZ0RIvh4.js} +1 -1
- package/dist/ui/assets/config-7LGRnJ26.js +1 -0
- package/dist/ui/assets/index-Bko2TlZY.css +1 -0
- package/dist/ui/assets/{index-Bta9YXEm.js → index-Db1XEN8v.js} +3 -3
- package/dist/ui/assets/{mcp-vIffcwd6.js → mcp-YiYC-9IH.js} +1 -1
- package/dist/ui/assets/{skills-wANsorUj.js → skills-dc6Xqqhb.js} +1 -1
- package/dist/ui/assets/{stats-xnlA4NwX.js → stats-BzqxCDuj.js} +1 -1
- package/dist/ui/assets/useCurrency-CEc5edm2.js +1 -0
- package/dist/ui/assets/vendor-icons-DE7PWdkN.js +1 -0
- package/dist/ui/assets/vendor-utils-BIYveU_1.js +39 -0
- package/dist/ui/index.html +4 -4
- package/dist/ui/sw.js +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/AuditDashboard-BVyKnpVm.js +0 -1
- package/dist/ui/assets/Dashboard-BdSQDB14.js +0 -1
- package/dist/ui/assets/ModelPricing-BQPw0r6z.js +0 -1
- package/dist/ui/assets/Notifications-BslO2Ect.js +0 -1
- package/dist/ui/assets/SatiMemories-DzaLaZ6M.js +0 -1
- package/dist/ui/assets/SessionAudit-CBDThjBi.js +0 -9
- package/dist/ui/assets/Settings-JPTCA7C7.js +0 -49
- package/dist/ui/assets/Skills-BnDg1HCb.js +0 -7
- package/dist/ui/assets/Smiths-DR6g_o3D.js +0 -1
- package/dist/ui/assets/Tasks-BuoNCvI-.js +0 -1
- package/dist/ui/assets/config-pKL8Y4V9.js +0 -1
- package/dist/ui/assets/index-Cjli-AD7.css +0 -1
- package/dist/ui/assets/vendor-icons-NHF9HNeN.js +0 -1
- package/dist/ui/assets/vendor-utils-D4NnWbOU.js +0 -39
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { createTtsTelephonist, TTS_MAX_CHARS } from '../telephonist.js';
|
|
3
|
+
// ─── createTtsTelephonist factory ─────────────────────────────────────────────
|
|
4
|
+
describe('createTtsTelephonist', () => {
|
|
5
|
+
it('returns an instance with synthesize() for openai provider', () => {
|
|
6
|
+
const telephonist = createTtsTelephonist({
|
|
7
|
+
enabled: true,
|
|
8
|
+
provider: 'openai',
|
|
9
|
+
model: 'tts-1',
|
|
10
|
+
voice: 'alloy',
|
|
11
|
+
});
|
|
12
|
+
expect(telephonist).toBeDefined();
|
|
13
|
+
expect(typeof telephonist.synthesize).toBe('function');
|
|
14
|
+
});
|
|
15
|
+
it('returns an instance with synthesize() for google provider', () => {
|
|
16
|
+
const telephonist = createTtsTelephonist({
|
|
17
|
+
enabled: true,
|
|
18
|
+
provider: 'google',
|
|
19
|
+
model: 'gemini-2.5-flash',
|
|
20
|
+
voice: 'Kore',
|
|
21
|
+
});
|
|
22
|
+
expect(telephonist).toBeDefined();
|
|
23
|
+
expect(typeof telephonist.synthesize).toBe('function');
|
|
24
|
+
});
|
|
25
|
+
it('throws for unsupported provider', () => {
|
|
26
|
+
expect(() => createTtsTelephonist({
|
|
27
|
+
enabled: true,
|
|
28
|
+
provider: 'ollama',
|
|
29
|
+
model: 'some-model',
|
|
30
|
+
voice: 'default',
|
|
31
|
+
})).toThrow(/Unsupported TTS provider/);
|
|
32
|
+
});
|
|
33
|
+
it('does not expose transcribe() meaningfully (throws)', async () => {
|
|
34
|
+
const telephonist = createTtsTelephonist({
|
|
35
|
+
enabled: true,
|
|
36
|
+
provider: 'openai',
|
|
37
|
+
model: 'tts-1',
|
|
38
|
+
voice: 'alloy',
|
|
39
|
+
});
|
|
40
|
+
await expect(telephonist.transcribe('', '', '')).rejects.toThrow();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
// ─── Text truncation ──────────────────────────────────────────────────────────
|
|
44
|
+
describe('TTS text truncation', () => {
|
|
45
|
+
it('TTS_MAX_CHARS constant is 4096', () => {
|
|
46
|
+
expect(TTS_MAX_CHARS).toBe(4096);
|
|
47
|
+
});
|
|
48
|
+
it('short text (under 4096 chars) passes through unchanged in OpenAI synthesize', async () => {
|
|
49
|
+
// We test the truncation logic indirectly by checking that the SDK call
|
|
50
|
+
// receives the correct (non-truncated) text. We mock the OpenAI client.
|
|
51
|
+
const mockCreate = vi.fn().mockResolvedValue({
|
|
52
|
+
arrayBuffer: async () => new ArrayBuffer(8),
|
|
53
|
+
});
|
|
54
|
+
vi.mock('openai', () => ({
|
|
55
|
+
default: vi.fn().mockImplementation(() => ({
|
|
56
|
+
audio: {
|
|
57
|
+
speech: { create: mockCreate },
|
|
58
|
+
},
|
|
59
|
+
})),
|
|
60
|
+
}));
|
|
61
|
+
vi.mock('fs-extra', () => ({
|
|
62
|
+
default: { writeFile: vi.fn().mockResolvedValue(undefined) },
|
|
63
|
+
}));
|
|
64
|
+
const telephonist = createTtsTelephonist({
|
|
65
|
+
enabled: true,
|
|
66
|
+
provider: 'openai',
|
|
67
|
+
model: 'tts-1',
|
|
68
|
+
voice: 'alloy',
|
|
69
|
+
});
|
|
70
|
+
const shortText = 'Hello world';
|
|
71
|
+
await telephonist.synthesize(shortText, 'fake-key').catch(() => { });
|
|
72
|
+
// Verify the mock was called with the short text (not truncated)
|
|
73
|
+
if (mockCreate.mock.calls.length > 0) {
|
|
74
|
+
expect(mockCreate.mock.calls[0][0].input).toBe(shortText);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
it('long text (over 4096 chars) is truncated to 4096', () => {
|
|
78
|
+
// Test the truncation logic directly by re-creating what truncateForTts does
|
|
79
|
+
const longText = 'a'.repeat(5000);
|
|
80
|
+
const truncated = longText.slice(0, 4096);
|
|
81
|
+
expect(truncated.length).toBe(4096);
|
|
82
|
+
expect(longText.length).toBeGreaterThan(4096);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ChannelRegistry } from '../../channels/registry.js';
|
|
2
|
+
export class ChannelNotifierAdapter {
|
|
3
|
+
async sendToUser(channel, userId, text) {
|
|
4
|
+
await ChannelRegistry.sendToUser(channel, userId, text);
|
|
5
|
+
}
|
|
6
|
+
async broadcast(text) {
|
|
7
|
+
await ChannelRegistry.broadcast(text);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ProviderFactory } from '../providers/factory.js';
|
|
2
|
+
export class LangChainProviderAdapter {
|
|
3
|
+
async createBare(config, tools = []) {
|
|
4
|
+
return ProviderFactory.createBare(config, tools);
|
|
5
|
+
}
|
|
6
|
+
async create(config, tools = []) {
|
|
7
|
+
return ProviderFactory.createBare(config, tools);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SQLiteChatMessageHistory } from '../memory/sqlite.js';
|
|
2
|
+
export class SQLiteChatHistoryAdapter {
|
|
3
|
+
async getMessages(sessionId) {
|
|
4
|
+
const history = new SQLiteChatMessageHistory({ sessionId });
|
|
5
|
+
return history.getMessages();
|
|
6
|
+
}
|
|
7
|
+
async addMessage(sessionId, message) {
|
|
8
|
+
const history = new SQLiteChatMessageHistory({ sessionId });
|
|
9
|
+
await history.addMessage(message);
|
|
10
|
+
}
|
|
11
|
+
async clear(sessionId) {
|
|
12
|
+
const history = new SQLiteChatMessageHistory({ sessionId });
|
|
13
|
+
await history.clear();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ChannelNotifierAdapter } from './ChannelNotifierAdapter.js';
|
|
2
|
+
export { SQLiteTaskEnqueuerAdapter } from './SQLiteTaskEnqueuerAdapter.js';
|
|
3
|
+
export { SQLiteChatHistoryAdapter } from './SQLiteChatHistoryAdapter.js';
|
|
4
|
+
export { LangChainProviderAdapter } from './LangChainProviderAdapter.js';
|
|
5
|
+
export { AuditRepositoryAdapter } from './AuditRepositoryAdapter.js';
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
4
|
import { randomUUID } from 'crypto';
|
|
6
5
|
import { DisplayManager } from '../display.js';
|
|
6
|
+
import { PATHS } from '../../config/paths.js';
|
|
7
7
|
export class AuditRepository {
|
|
8
8
|
static instance = null;
|
|
9
9
|
db;
|
|
10
10
|
constructor() {
|
|
11
|
-
const dbPath =
|
|
11
|
+
const dbPath = PATHS.shortMemoryDb;
|
|
12
12
|
fs.ensureDirSync(path.dirname(dbPath));
|
|
13
13
|
this.db = new Database(dbPath, { timeout: 5000 });
|
|
14
14
|
this.db.pragma('journal_mode = WAL');
|
|
@@ -59,6 +59,10 @@ export class AuditRepository {
|
|
|
59
59
|
DisplayManager.getInstance().log(`AuditRepository.insert failed: ${err?.message ?? String(err)}`, { source: 'Audit', level: 'error' });
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
countBySession(sessionId) {
|
|
63
|
+
const row = this.db.prepare(`SELECT COUNT(*) as n FROM audit_events WHERE session_id = ?`).get(sessionId);
|
|
64
|
+
return row?.n ?? 0;
|
|
65
|
+
}
|
|
62
66
|
getBySession(sessionId, opts) {
|
|
63
67
|
const limit = opts?.limit ?? 500;
|
|
64
68
|
const offset = opts?.offset ?? 0;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
4
|
import { randomUUID } from 'crypto';
|
|
6
5
|
import { ConfigManager } from '../../config/manager.js';
|
|
6
|
+
import { PATHS } from '../../config/paths.js';
|
|
7
7
|
export class ChronosError extends Error {
|
|
8
8
|
constructor(message) {
|
|
9
9
|
super(message);
|
|
@@ -14,7 +14,7 @@ export class ChronosRepository {
|
|
|
14
14
|
static instance = null;
|
|
15
15
|
db;
|
|
16
16
|
constructor() {
|
|
17
|
-
const dbPath =
|
|
17
|
+
const dbPath = PATHS.shortMemoryDb;
|
|
18
18
|
fs.ensureDirSync(path.dirname(dbPath));
|
|
19
19
|
this.db = new Database(dbPath, { timeout: 5000 });
|
|
20
20
|
this.db.pragma('journal_mode = WAL');
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ServiceContainer — Composition Root
|
|
3
|
+
*
|
|
4
|
+
* Simple typed registry for application services (ports).
|
|
5
|
+
* Configured once at startup in start.ts. No DI framework magic —
|
|
6
|
+
* just a Map with type-safe get/register.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* // Registration (start.ts):
|
|
10
|
+
* ServiceContainer.register('notifier', new ChannelNotifierAdapter());
|
|
11
|
+
*
|
|
12
|
+
* // Consumption:
|
|
13
|
+
* const notifier = ServiceContainer.get<INotifier>('notifier');
|
|
14
|
+
*/
|
|
15
|
+
export class ServiceContainer {
|
|
16
|
+
static services = new Map();
|
|
17
|
+
/** Register a service under a given key. Overwrites if already registered. */
|
|
18
|
+
static register(key, service) {
|
|
19
|
+
ServiceContainer.services.set(key, service);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Retrieve a registered service.
|
|
23
|
+
* @throws if the key is not registered.
|
|
24
|
+
*/
|
|
25
|
+
static get(key) {
|
|
26
|
+
const service = ServiceContainer.services.get(key);
|
|
27
|
+
if (service === undefined) {
|
|
28
|
+
throw new Error(`ServiceContainer: "${key}" is not registered. Did you call register() in start.ts?`);
|
|
29
|
+
}
|
|
30
|
+
return service;
|
|
31
|
+
}
|
|
32
|
+
/** Returns true if a service is registered under the given key. */
|
|
33
|
+
static has(key) {
|
|
34
|
+
return ServiceContainer.services.has(key);
|
|
35
|
+
}
|
|
36
|
+
/** Remove all registered services (useful in tests). */
|
|
37
|
+
static reset() {
|
|
38
|
+
ServiceContainer.services.clear();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Well-known service keys — use these constants to avoid typo bugs.
|
|
43
|
+
*/
|
|
44
|
+
export const SERVICE_KEYS = {
|
|
45
|
+
notifier: 'notifier',
|
|
46
|
+
taskEnqueuer: 'taskEnqueuer',
|
|
47
|
+
chatHistory: 'chatHistory',
|
|
48
|
+
providerFactory: 'providerFactory',
|
|
49
|
+
auditEmitter: 'auditEmitter',
|
|
50
|
+
};
|
package/dist/runtime/display.js
CHANGED
|
@@ -2,12 +2,14 @@ import ora from 'ora';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import winston from 'winston';
|
|
4
4
|
import DailyRotateFile from 'winston-daily-rotate-file';
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
5
6
|
import { LOGS_DIR } from '../config/paths.js';
|
|
6
|
-
export class DisplayManager {
|
|
7
|
+
export class DisplayManager extends EventEmitter {
|
|
7
8
|
static instance;
|
|
8
9
|
spinner;
|
|
9
10
|
logger;
|
|
10
11
|
constructor() {
|
|
12
|
+
super();
|
|
11
13
|
this.spinner = ora();
|
|
12
14
|
}
|
|
13
15
|
static getInstance() {
|
|
@@ -38,7 +40,9 @@ export class DisplayManager {
|
|
|
38
40
|
],
|
|
39
41
|
});
|
|
40
42
|
}
|
|
41
|
-
startSpinner(text) {
|
|
43
|
+
startSpinner(text, source) {
|
|
44
|
+
const defaultAgentKey = source ? source.toLowerCase() : 'oracle';
|
|
45
|
+
this.emit('activity_start', { agent: defaultAgentKey, message: text || 'processing...', timestamp: Date.now() });
|
|
42
46
|
if (this.spinner.isSpinning) {
|
|
43
47
|
if (text) {
|
|
44
48
|
this.spinner.text = text;
|
|
@@ -53,6 +57,7 @@ export class DisplayManager {
|
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
stopSpinner(success) {
|
|
60
|
+
this.emit('activity_end', { timestamp: Date.now(), success });
|
|
56
61
|
if (!this.spinner.isSpinning)
|
|
57
62
|
return;
|
|
58
63
|
if (success === true) {
|
|
@@ -134,6 +139,16 @@ export class DisplayManager {
|
|
|
134
139
|
// But existing log() Implementation didn't handle 'debug' specifically before I added it to type.
|
|
135
140
|
// I'll leave console behavior as is (prints everything).
|
|
136
141
|
console.log(`${prefix}${formattedMessage}`);
|
|
142
|
+
// Emit purely for visualization (ignoring debug if needed to keep stream light)
|
|
143
|
+
if (options?.level !== 'debug') {
|
|
144
|
+
this.emit('message', {
|
|
145
|
+
message,
|
|
146
|
+
source: options?.source || 'system',
|
|
147
|
+
level: options?.level || 'info',
|
|
148
|
+
timestamp: Date.now(),
|
|
149
|
+
meta: options?.meta
|
|
150
|
+
});
|
|
151
|
+
}
|
|
137
152
|
if (this.logger) {
|
|
138
153
|
try {
|
|
139
154
|
const level = this.mapLevel(options?.level);
|
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { ConfigManager } from '../config/manager.js';
|
|
9
9
|
import { DisplayManager } from './display.js';
|
|
10
|
-
import {
|
|
11
|
-
import { Neo } from './subagents/neo.js';
|
|
12
|
-
import { Trinity } from './subagents/trinity/trinity.js';
|
|
10
|
+
import { SubagentRegistry } from './subagents/registry.js';
|
|
13
11
|
let currentOracle = null;
|
|
14
12
|
/**
|
|
15
13
|
* Register the current Oracle instance for hot-reload.
|
|
@@ -44,12 +42,11 @@ export async function hotReloadConfig() {
|
|
|
44
42
|
reinitialized.push('Oracle');
|
|
45
43
|
display.log('Oracle reinitialized with new config', { source: 'HotReload', level: 'info' });
|
|
46
44
|
}
|
|
47
|
-
// 3.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
display.log('Subagent singletons reset (will reinitialize on next use)', { source: 'HotReload', level: 'info' });
|
|
45
|
+
// 3. Reload all registered subagents via SubagentRegistry
|
|
46
|
+
await SubagentRegistry.reloadAll();
|
|
47
|
+
const agentNames = SubagentRegistry.getAll().map(r => r.label);
|
|
48
|
+
reinitialized.push(...agentNames);
|
|
49
|
+
display.log(`Subagents reloaded: ${agentNames.join(', ')}`, { source: 'HotReload', level: 'info' });
|
|
53
50
|
return {
|
|
54
51
|
success: true,
|
|
55
52
|
reinitialized,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import { EmbeddingService } from './embedding.service.js';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
3
|
import loadVecExtension from './sqlite-vec.js';
|
|
6
|
-
|
|
4
|
+
import { PATHS } from '../../config/paths.js';
|
|
5
|
+
const db = new Database(PATHS.satiDb);
|
|
7
6
|
db.pragma('journal_mode = WAL');
|
|
8
7
|
// 🔥 ISSO AQUI É O QUE ESTÁ FALTANDO
|
|
9
8
|
loadVecExtension(db);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
2
|
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
5
4
|
import { randomUUID } from 'crypto';
|
|
6
5
|
import loadVecExtension from '../sqlite-vec.js';
|
|
7
6
|
import { DisplayManager } from '../../display.js';
|
|
8
7
|
import { ConfigManager } from '../../../config/manager.js';
|
|
8
|
+
import { PATHS } from '../../../config/paths.js';
|
|
9
9
|
const EMBEDDING_DIM = 384;
|
|
10
10
|
export class SatiRepository {
|
|
11
11
|
db = null;
|
|
@@ -14,7 +14,7 @@ export class SatiRepository {
|
|
|
14
14
|
display = DisplayManager.getInstance();
|
|
15
15
|
constructor(dbPath) {
|
|
16
16
|
this.dbPath =
|
|
17
|
-
dbPath ||
|
|
17
|
+
dbPath || PATHS.satiDb;
|
|
18
18
|
}
|
|
19
19
|
static getInstance(dbPath) {
|
|
20
20
|
if (!SatiRepository.instance) {
|
|
@@ -3,7 +3,7 @@ import { HumanMessage, AIMessage, SystemMessage, ToolMessage } from "@langchain/
|
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
4
|
import fs from "fs-extra";
|
|
5
5
|
import * as path from "path";
|
|
6
|
-
import {
|
|
6
|
+
import { PATHS } from "../../config/paths.js";
|
|
7
7
|
import { randomUUID } from 'crypto';
|
|
8
8
|
import { DisplayManager } from "../display.js";
|
|
9
9
|
export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
|
|
@@ -22,7 +22,7 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
|
|
|
22
22
|
this.sessionId = fields.sessionId && fields.sessionId !== '' ? fields.sessionId : '';
|
|
23
23
|
this.limit = fields.limit ? fields.limit : 20;
|
|
24
24
|
// Default path: ~/.morpheus/memory/short-memory.db
|
|
25
|
-
const dbPath = fields.databasePath ||
|
|
25
|
+
const dbPath = fields.databasePath || PATHS.shortMemoryDb;
|
|
26
26
|
// Ensure the directory exists
|
|
27
27
|
this.ensureDirectory(dbPath);
|
|
28
28
|
// Initialize database with retry logic for locked databases
|
|
@@ -874,7 +874,7 @@ export class SQLiteChatMessageHistory extends BaseListChatMessageHistory {
|
|
|
874
874
|
const sessionText = tx(); // Executar a transação
|
|
875
875
|
// Criar chunks no banco Sati — conexão aberta localmente e fechada ao fim
|
|
876
876
|
if (sessionText) {
|
|
877
|
-
const dbSatiPath =
|
|
877
|
+
const dbSatiPath = PATHS.satiDb;
|
|
878
878
|
this.ensureDirectory(dbSatiPath);
|
|
879
879
|
const dbSati = new Database(dbSatiPath, { timeout: 5000 });
|
|
880
880
|
dbSati.pragma('journal_mode = WAL');
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
4
|
import { encrypt, decrypt, canEncrypt } from '../trinity-crypto.js';
|
|
5
|
+
import { PATHS } from '../../config/paths.js';
|
|
6
6
|
function safeDecrypt(value) {
|
|
7
7
|
if (!value)
|
|
8
8
|
return null;
|
|
@@ -39,7 +39,7 @@ export class DatabaseRegistry {
|
|
|
39
39
|
static instance = null;
|
|
40
40
|
db;
|
|
41
41
|
constructor() {
|
|
42
|
-
const dbPath =
|
|
42
|
+
const dbPath = PATHS.trinityDb;
|
|
43
43
|
fs.ensureDirSync(path.dirname(dbPath));
|
|
44
44
|
this.db = new Database(dbPath, { timeout: 5000 });
|
|
45
45
|
this.db.pragma('journal_mode = WAL');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { ChatOpenAI } from "@langchain/openai";
|
|
2
|
-
import { ChatAnthropic } from "@langchain/anthropic";
|
|
3
|
-
import { ChatOllama } from "@langchain/ollama";
|
|
4
|
-
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
|
|
5
1
|
import { ProviderError } from "../errors.js";
|
|
6
2
|
import { createAgent, createMiddleware } from "langchain";
|
|
7
3
|
import { DisplayManager } from "../display.js";
|
|
8
|
-
import { getUsableApiKey } from "../trinity-crypto.js";
|
|
9
4
|
import { ConfigManager } from "../../config/manager.js";
|
|
10
5
|
import { TaskRequestContext } from "../tasks/context.js";
|
|
11
6
|
import { ChannelRegistry } from "../../channels/registry.js";
|
|
7
|
+
import { getStrategy, registerStrategy } from "./strategies.js";
|
|
12
8
|
/** Channels that should NOT receive verbose tool notifications */
|
|
13
9
|
const SILENT_CHANNELS = new Set(['api', 'ui']);
|
|
10
|
+
export { registerStrategy };
|
|
14
11
|
export class ProviderFactory {
|
|
15
12
|
static buildMonitoringMiddleware() {
|
|
16
13
|
const display = DisplayManager.getInstance();
|
|
@@ -41,44 +38,11 @@ export class ProviderFactory {
|
|
|
41
38
|
});
|
|
42
39
|
}
|
|
43
40
|
static buildModel(config) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return new ChatOpenAI({
|
|
48
|
-
modelName: config.model,
|
|
49
|
-
temperature: config.temperature,
|
|
50
|
-
apiKey: process.env.OPENAI_API_KEY || usableApiKey,
|
|
51
|
-
});
|
|
52
|
-
case 'anthropic':
|
|
53
|
-
return new ChatAnthropic({
|
|
54
|
-
modelName: config.model,
|
|
55
|
-
temperature: config.temperature,
|
|
56
|
-
apiKey: process.env.ANTHROPIC_API_KEY || usableApiKey,
|
|
57
|
-
});
|
|
58
|
-
case 'openrouter':
|
|
59
|
-
return new ChatOpenAI({
|
|
60
|
-
modelName: config.model,
|
|
61
|
-
temperature: config.temperature,
|
|
62
|
-
apiKey: process.env.OPENROUTER_API_KEY || usableApiKey,
|
|
63
|
-
configuration: {
|
|
64
|
-
baseURL: config.base_url || 'https://openrouter.ai/api/v1'
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
case 'ollama':
|
|
68
|
-
return new ChatOllama({
|
|
69
|
-
model: config.model,
|
|
70
|
-
temperature: config.temperature,
|
|
71
|
-
baseUrl: config.base_url || usableApiKey,
|
|
72
|
-
});
|
|
73
|
-
case 'gemini':
|
|
74
|
-
return new ChatGoogleGenerativeAI({
|
|
75
|
-
model: config.model,
|
|
76
|
-
temperature: config.temperature,
|
|
77
|
-
apiKey: process.env.GOOGLE_API_KEY || usableApiKey
|
|
78
|
-
});
|
|
79
|
-
default:
|
|
80
|
-
throw new Error(`Unsupported provider: ${config.provider}`);
|
|
41
|
+
const strategy = getStrategy(config.provider);
|
|
42
|
+
if (!strategy) {
|
|
43
|
+
throw new Error(`Unsupported provider: ${config.provider}`);
|
|
81
44
|
}
|
|
45
|
+
return strategy.build(config);
|
|
82
46
|
}
|
|
83
47
|
static handleProviderError(config, error) {
|
|
84
48
|
let suggestion = "Check your configuration and API keys.";
|
|
@@ -114,14 +78,6 @@ export class ProviderFactory {
|
|
|
114
78
|
ProviderFactory.handleProviderError(config, error);
|
|
115
79
|
}
|
|
116
80
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const model = ProviderFactory.buildModel(config);
|
|
120
|
-
const middleware = ProviderFactory.buildMonitoringMiddleware();
|
|
121
|
-
return createAgent({ model, tools, middleware: [middleware] });
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
ProviderFactory.handleProviderError(config, error);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
81
|
+
/** Alias for createBare — both methods are identical. */
|
|
82
|
+
static create = ProviderFactory.createBare;
|
|
127
83
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ChatOpenAI } from "@langchain/openai";
|
|
2
|
+
import { ChatAnthropic } from "@langchain/anthropic";
|
|
3
|
+
import { ChatOllama } from "@langchain/ollama";
|
|
4
|
+
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
|
|
5
|
+
import { getUsableApiKey } from "../trinity-crypto.js";
|
|
6
|
+
class OpenAIStrategy {
|
|
7
|
+
build(config) {
|
|
8
|
+
return new ChatOpenAI({
|
|
9
|
+
modelName: config.model,
|
|
10
|
+
temperature: config.temperature,
|
|
11
|
+
apiKey: process.env.OPENAI_API_KEY || getUsableApiKey(config.api_key),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class AnthropicStrategy {
|
|
16
|
+
build(config) {
|
|
17
|
+
return new ChatAnthropic({
|
|
18
|
+
modelName: config.model,
|
|
19
|
+
temperature: config.temperature,
|
|
20
|
+
apiKey: process.env.ANTHROPIC_API_KEY || getUsableApiKey(config.api_key),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
class OpenRouterStrategy {
|
|
25
|
+
build(config) {
|
|
26
|
+
return new ChatOpenAI({
|
|
27
|
+
modelName: config.model,
|
|
28
|
+
temperature: config.temperature,
|
|
29
|
+
apiKey: process.env.OPENROUTER_API_KEY || getUsableApiKey(config.api_key),
|
|
30
|
+
configuration: {
|
|
31
|
+
baseURL: config.base_url || 'https://openrouter.ai/api/v1'
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class OllamaStrategy {
|
|
37
|
+
build(config) {
|
|
38
|
+
return new ChatOllama({
|
|
39
|
+
model: config.model,
|
|
40
|
+
temperature: config.temperature,
|
|
41
|
+
baseUrl: config.base_url || getUsableApiKey(config.api_key),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
class GeminiStrategy {
|
|
46
|
+
build(config) {
|
|
47
|
+
return new ChatGoogleGenerativeAI({
|
|
48
|
+
model: config.model,
|
|
49
|
+
temperature: config.temperature,
|
|
50
|
+
apiKey: process.env.GOOGLE_API_KEY || getUsableApiKey(config.api_key),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const strategies = new Map([
|
|
55
|
+
['openai', new OpenAIStrategy()],
|
|
56
|
+
['anthropic', new AnthropicStrategy()],
|
|
57
|
+
['openrouter', new OpenRouterStrategy()],
|
|
58
|
+
['ollama', new OllamaStrategy()],
|
|
59
|
+
['gemini', new GeminiStrategy()],
|
|
60
|
+
]);
|
|
61
|
+
export function registerStrategy(provider, strategy) {
|
|
62
|
+
strategies.set(provider, strategy);
|
|
63
|
+
}
|
|
64
|
+
export function getStrategy(provider) {
|
|
65
|
+
return strategies.get(provider);
|
|
66
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
4
|
import { ConfigManager } from '../../config/manager.js';
|
|
5
|
+
import { PATHS } from '../../config/paths.js';
|
|
6
6
|
export class SetupRepository {
|
|
7
7
|
static instance = null;
|
|
8
8
|
db;
|
|
9
9
|
constructor(dbPath) {
|
|
10
|
-
const resolvedPath = dbPath ??
|
|
10
|
+
const resolvedPath = dbPath ?? PATHS.shortMemoryDb;
|
|
11
11
|
fs.ensureDirSync(path.dirname(resolvedPath));
|
|
12
12
|
this.db = new Database(resolvedPath, { timeout: 5000 });
|
|
13
13
|
this.db.pragma('journal_mode = WAL');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HumanMessage, SystemMessage, AIMessage } from "@langchain/core/messages";
|
|
2
2
|
import { ConfigManager } from "../../config/manager.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ServiceContainer, SERVICE_KEYS } from "../container.js";
|
|
4
4
|
import { ProviderError } from "../errors.js";
|
|
5
5
|
import { DisplayManager } from "../display.js";
|
|
6
6
|
import { buildDevKit } from "morpheus-devkit";
|
|
@@ -78,7 +78,7 @@ export class Apoc {
|
|
|
78
78
|
const tools = instrumentDevKitTools(rawTools, () => Apoc.currentSessionId, () => 'apoc');
|
|
79
79
|
this.display.log(`Apoc initialized with ${tools.length} DevKit tools (sandbox_dir: ${devkit.sandbox_dir}, personality: ${personality})`, { source: "Apoc" });
|
|
80
80
|
try {
|
|
81
|
-
this.agent = await
|
|
81
|
+
this.agent = await ServiceContainer.get(SERVICE_KEYS.providerFactory).createBare(apocConfig, tools);
|
|
82
82
|
}
|
|
83
83
|
catch (err) {
|
|
84
84
|
throw new ProviderError(apocConfig.provider, err, "Apoc subagent initialization failed");
|
|
@@ -4,7 +4,7 @@ import { DynamicStructuredTool } from "@langchain/core/tools";
|
|
|
4
4
|
import { ConfigManager } from '../../../config/manager.js';
|
|
5
5
|
import { LinkRepository } from './repository.js';
|
|
6
6
|
import { LinkSearch } from './search.js';
|
|
7
|
-
import {
|
|
7
|
+
import { ServiceContainer, SERVICE_KEYS } from '../../container.js';
|
|
8
8
|
import { ProviderError } from '../../errors.js';
|
|
9
9
|
import { DisplayManager } from '../../display.js';
|
|
10
10
|
import { TaskRequestContext } from '../../tasks/context.js';
|
|
@@ -268,7 +268,7 @@ export class Link {
|
|
|
268
268
|
}
|
|
269
269
|
this.display.log(`Link initialized with personality: ${personality}.`, { source: 'Link' });
|
|
270
270
|
try {
|
|
271
|
-
this.agent = await
|
|
271
|
+
this.agent = await ServiceContainer.get(SERVICE_KEYS.providerFactory).create(linkConfig, tools);
|
|
272
272
|
}
|
|
273
273
|
catch (err) {
|
|
274
274
|
throw new ProviderError(linkConfig.provider, err, 'Link subagent initialization failed');
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
4
|
import { randomUUID } from 'crypto';
|
|
6
5
|
import loadVecExtension from '../../memory/sqlite-vec.js';
|
|
7
6
|
import { DisplayManager } from '../../display.js';
|
|
7
|
+
import { PATHS } from '../../../config/paths.js';
|
|
8
8
|
// ─── Repository ──────────────────────────────────────────────────────────────
|
|
9
9
|
const EMBEDDING_DIM = 384;
|
|
10
10
|
export class LinkRepository {
|
|
@@ -13,7 +13,7 @@ export class LinkRepository {
|
|
|
13
13
|
dbPath;
|
|
14
14
|
display = DisplayManager.getInstance();
|
|
15
15
|
constructor(dbPath) {
|
|
16
|
-
this.dbPath = dbPath ||
|
|
16
|
+
this.dbPath = dbPath || PATHS.linkDb;
|
|
17
17
|
}
|
|
18
18
|
static getInstance(dbPath) {
|
|
19
19
|
if (!LinkRepository.instance) {
|