morpheus-cli 0.8.8 → 0.9.0
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/runtime/__tests__/keymaker.test.js +5 -2
- package/dist/runtime/apoc.js +7 -7
- package/dist/{devkit/registry.js → runtime/devkit-instrument.js} +5 -29
- package/dist/runtime/keymaker.js +6 -5
- package/dist/runtime/memory/sati/service.js +1 -1
- package/dist/runtime/neo.js +1 -1
- package/dist/runtime/oracle.js +1 -1
- package/dist/runtime/smiths/delegator.js +2 -2
- package/dist/runtime/trinity.js +1 -1
- package/package.json +5 -1
- package/dist/devkit/adapters/shell.js +0 -80
- package/dist/devkit/index.js +0 -11
- package/dist/devkit/tools/browser.js +0 -504
- package/dist/devkit/tools/filesystem.js +0 -235
- package/dist/devkit/tools/git.js +0 -226
- package/dist/devkit/tools/network.js +0 -165
- package/dist/devkit/tools/packages.js +0 -73
- package/dist/devkit/tools/processes.js +0 -130
- package/dist/devkit/tools/shell.js +0 -106
- package/dist/devkit/tools/system.js +0 -132
- package/dist/devkit/types.js +0 -1
- package/dist/devkit/utils.js +0 -45
|
@@ -32,12 +32,15 @@ vi.mock('../display.js', () => ({
|
|
|
32
32
|
})),
|
|
33
33
|
},
|
|
34
34
|
}));
|
|
35
|
-
vi.mock('
|
|
35
|
+
vi.mock('morpheus-devkit', () => ({
|
|
36
36
|
buildDevKit: vi.fn(() => [
|
|
37
37
|
{ name: 'fs_read', description: 'Read file' },
|
|
38
38
|
{ name: 'shell_exec', description: 'Execute shell command' },
|
|
39
39
|
]),
|
|
40
40
|
}));
|
|
41
|
+
vi.mock('../devkit-instrument.js', () => ({
|
|
42
|
+
instrumentDevKitTools: vi.fn((tools) => tools),
|
|
43
|
+
}));
|
|
41
44
|
vi.mock('../tools/factory.js', () => ({
|
|
42
45
|
Construtor: {
|
|
43
46
|
create: vi.fn(() => Promise.resolve([
|
|
@@ -97,7 +100,7 @@ describe('Keymaker', () => {
|
|
|
97
100
|
describe('initialize()', () => {
|
|
98
101
|
it('should initialize agent with all tools', async () => {
|
|
99
102
|
const { ProviderFactory } = await import('../providers/factory.js');
|
|
100
|
-
const { buildDevKit } = await import('
|
|
103
|
+
const { buildDevKit } = await import('morpheus-devkit');
|
|
101
104
|
const { Construtor } = await import('../tools/factory.js');
|
|
102
105
|
const keymaker = new Keymaker('test-skill', '# Instructions');
|
|
103
106
|
await keymaker.initialize();
|
package/dist/runtime/apoc.js
CHANGED
|
@@ -3,7 +3,8 @@ import { ConfigManager } from "../config/manager.js";
|
|
|
3
3
|
import { ProviderFactory } from "./providers/factory.js";
|
|
4
4
|
import { ProviderError } from "./errors.js";
|
|
5
5
|
import { DisplayManager } from "./display.js";
|
|
6
|
-
import { buildDevKit } from "
|
|
6
|
+
import { buildDevKit } from "morpheus-devkit";
|
|
7
|
+
import { instrumentDevKitTools } from "./devkit-instrument.js";
|
|
7
8
|
import { extractRawUsage, persistAgentMessage, buildAgentResult, emitToolAuditEvents } from "./subagent-utils.js";
|
|
8
9
|
import { buildDelegationTool } from "./tools/delegation-utils.js";
|
|
9
10
|
/**
|
|
@@ -47,9 +48,9 @@ export class Apoc {
|
|
|
47
48
|
const devkit = ConfigManager.getInstance().getDevKitConfig();
|
|
48
49
|
const timeout_ms = devkit.timeout_ms || this.config.apoc?.timeout_ms || 30_000;
|
|
49
50
|
const personality = this.config.apoc?.personality || 'pragmatic_dev';
|
|
50
|
-
// Import
|
|
51
|
-
await import("
|
|
52
|
-
const
|
|
51
|
+
// Import morpheus-devkit to trigger side-effect tool registration
|
|
52
|
+
await import("morpheus-devkit");
|
|
53
|
+
const rawTools = buildDevKit({
|
|
53
54
|
working_dir: devkit.sandbox_dir || process.cwd(),
|
|
54
55
|
allowed_commands: devkit.allowed_shell_commands || [],
|
|
55
56
|
timeout_ms,
|
|
@@ -59,9 +60,8 @@ export class Apoc {
|
|
|
59
60
|
enable_shell: devkit.enable_shell,
|
|
60
61
|
enable_git: devkit.enable_git,
|
|
61
62
|
enable_network: devkit.enable_network,
|
|
62
|
-
getSessionId: () => Apoc.currentSessionId,
|
|
63
|
-
getAgent: () => 'apoc',
|
|
64
63
|
});
|
|
64
|
+
const tools = instrumentDevKitTools(rawTools, () => Apoc.currentSessionId, () => 'apoc');
|
|
65
65
|
this.display.log(`Apoc initialized with ${tools.length} DevKit tools (sandbox_dir: ${devkit.sandbox_dir}, personality: ${personality})`, { source: "Apoc" });
|
|
66
66
|
try {
|
|
67
67
|
this.agent = await ProviderFactory.createBare(apocConfig, tools);
|
|
@@ -259,7 +259,7 @@ ${context ? `CONTEXT FROM ORACLE:\n${context}` : ""}
|
|
|
259
259
|
try {
|
|
260
260
|
const inputCount = messages.length;
|
|
261
261
|
const startMs = Date.now();
|
|
262
|
-
const response = await this.agent.invoke({ messages });
|
|
262
|
+
const response = await this.agent.invoke({ messages }, { recursionLimit: 100 });
|
|
263
263
|
const durationMs = Date.now() - startMs;
|
|
264
264
|
const apocConfig = this.config.apoc || this.config.llm;
|
|
265
265
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
import { AuditRepository } from '
|
|
2
|
-
const factories = [];
|
|
3
|
-
export function registerToolFactory(factory, category = 'system') {
|
|
4
|
-
factories.push({ category, factory });
|
|
5
|
-
}
|
|
6
|
-
/** Categories that can be toggled off via DevKit config */
|
|
7
|
-
const TOGGLEABLE_CATEGORIES = {
|
|
8
|
-
filesystem: 'enable_filesystem',
|
|
9
|
-
shell: 'enable_shell',
|
|
10
|
-
git: 'enable_git',
|
|
11
|
-
network: 'enable_network',
|
|
12
|
-
};
|
|
1
|
+
import { AuditRepository } from './audit/repository.js';
|
|
13
2
|
/**
|
|
14
3
|
* Wraps a StructuredTool to record audit events on each invocation.
|
|
15
4
|
* The `getSessionId` getter is called at invocation time so it reflects
|
|
@@ -51,22 +40,9 @@ function instrumentTool(tool, getSessionId, getAgent) {
|
|
|
51
40
|
return tool;
|
|
52
41
|
}
|
|
53
42
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* and returns tools with the context captured in closure.
|
|
57
|
-
* Disabled categories are filtered out based on context flags.
|
|
58
|
-
* All tools are wrapped with audit instrumentation.
|
|
43
|
+
* Wraps all DevKit tools with audit instrumentation.
|
|
44
|
+
* Call this after buildDevKit() to add Morpheus audit tracking.
|
|
59
45
|
*/
|
|
60
|
-
export function
|
|
61
|
-
|
|
62
|
-
const getAgent = ctx.getAgent ?? (() => 'apoc');
|
|
63
|
-
return factories
|
|
64
|
-
.filter(({ category }) => {
|
|
65
|
-
const ctxKey = TOGGLEABLE_CATEGORIES[category];
|
|
66
|
-
if (!ctxKey)
|
|
67
|
-
return true; // non-toggleable categories always load
|
|
68
|
-
return ctx[ctxKey] !== false;
|
|
69
|
-
})
|
|
70
|
-
.flatMap(({ factory }) => factory(ctx))
|
|
71
|
-
.map(tool => instrumentTool(tool, getSessionId, getAgent));
|
|
46
|
+
export function instrumentDevKitTools(tools, getSessionId, getAgent) {
|
|
47
|
+
return tools.map(tool => instrumentTool(tool, getSessionId, getAgent));
|
|
72
48
|
}
|
package/dist/runtime/keymaker.js
CHANGED
|
@@ -3,7 +3,8 @@ import { ConfigManager } from "../config/manager.js";
|
|
|
3
3
|
import { ProviderFactory } from "./providers/factory.js";
|
|
4
4
|
import { ProviderError } from "./errors.js";
|
|
5
5
|
import { DisplayManager } from "./display.js";
|
|
6
|
-
import { buildDevKit } from "
|
|
6
|
+
import { buildDevKit } from "morpheus-devkit";
|
|
7
|
+
import { instrumentDevKitTools } from "./devkit-instrument.js";
|
|
7
8
|
import { Construtor } from "./tools/factory.js";
|
|
8
9
|
import { morpheusTools } from "./tools/index.js";
|
|
9
10
|
import { SkillRegistry } from "./skills/registry.js";
|
|
@@ -36,8 +37,8 @@ export class Keymaker {
|
|
|
36
37
|
// Build DevKit tools (filesystem, shell, git, browser, network, etc.)
|
|
37
38
|
const devkit = ConfigManager.getInstance().getDevKitConfig();
|
|
38
39
|
const timeout_ms = devkit.timeout_ms || 30_000;
|
|
39
|
-
await import("
|
|
40
|
-
const devKitTools = buildDevKit({
|
|
40
|
+
await import("morpheus-devkit");
|
|
41
|
+
const devKitTools = instrumentDevKitTools(buildDevKit({
|
|
41
42
|
working_dir: devkit.sandbox_dir || process.cwd(),
|
|
42
43
|
allowed_commands: devkit.allowed_shell_commands || [],
|
|
43
44
|
timeout_ms,
|
|
@@ -47,7 +48,7 @@ export class Keymaker {
|
|
|
47
48
|
enable_shell: devkit.enable_shell,
|
|
48
49
|
enable_git: devkit.enable_git,
|
|
49
50
|
enable_network: devkit.enable_network,
|
|
50
|
-
});
|
|
51
|
+
}), () => undefined, () => 'keymaker');
|
|
51
52
|
// Load MCP tools from configured servers
|
|
52
53
|
const mcpTools = await Construtor.create();
|
|
53
54
|
// Combine all tools
|
|
@@ -116,7 +117,7 @@ CRITICAL — NEVER FABRICATE DATA:
|
|
|
116
117
|
origin_user_id: taskContext?.origin_user_id,
|
|
117
118
|
};
|
|
118
119
|
const startMs = Date.now();
|
|
119
|
-
const response = await TaskRequestContext.run(invokeContext, () => this.agent.invoke({ messages }));
|
|
120
|
+
const response = await TaskRequestContext.run(invokeContext, () => this.agent.invoke({ messages }, { recursionLimit: 100 }));
|
|
120
121
|
const durationMs = Date.now() - startMs;
|
|
121
122
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
122
123
|
const content = typeof lastMessage.content === "string"
|
|
@@ -97,7 +97,7 @@ export class SatiService {
|
|
|
97
97
|
console.warn('[SatiService] Failed to persist input log:', e);
|
|
98
98
|
}
|
|
99
99
|
const satiStartMs = Date.now();
|
|
100
|
-
const response = await agent.invoke({ messages });
|
|
100
|
+
const response = await agent.invoke({ messages }, { recursionLimit: 100 });
|
|
101
101
|
const satiDurationMs = Date.now() - satiStartMs;
|
|
102
102
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
103
103
|
let content = lastMessage.content.toString();
|
package/dist/runtime/neo.js
CHANGED
|
@@ -137,7 +137,7 @@ ${context ? `Context:\n${context}` : ""}
|
|
|
137
137
|
};
|
|
138
138
|
const inputCount = messages.length;
|
|
139
139
|
const startMs = Date.now();
|
|
140
|
-
const response = await TaskRequestContext.run(invokeContext, () => this.agent.invoke({ messages }));
|
|
140
|
+
const response = await TaskRequestContext.run(invokeContext, () => this.agent.invoke({ messages }, { recursionLimit: 100 }));
|
|
141
141
|
const durationMs = Date.now() - startMs;
|
|
142
142
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
143
143
|
const content = typeof lastMessage.content === "string"
|
package/dist/runtime/oracle.js
CHANGED
|
@@ -395,7 +395,7 @@ Use it to inform your response and tool selection (if needed), but do not assume
|
|
|
395
395
|
let syncDelegationCount = 0;
|
|
396
396
|
const oracleStartMs = Date.now();
|
|
397
397
|
const response = await TaskRequestContext.run(invokeContext, async () => {
|
|
398
|
-
const agentResponse = await this.provider.invoke({ messages });
|
|
398
|
+
const agentResponse = await this.provider.invoke({ messages }, { recursionLimit: 100 });
|
|
399
399
|
contextDelegationAcks = TaskRequestContext.getDelegationAcks();
|
|
400
400
|
syncDelegationCount = TaskRequestContext.getSyncDelegationCount();
|
|
401
401
|
return agentResponse;
|
|
@@ -5,7 +5,7 @@ import { DisplayManager } from '../display.js';
|
|
|
5
5
|
import { SmithRegistry } from './registry.js';
|
|
6
6
|
import { ConfigManager } from '../../config/manager.js';
|
|
7
7
|
import { ProviderFactory } from '../providers/factory.js';
|
|
8
|
-
import { buildDevKit } from '
|
|
8
|
+
import { buildDevKit } from 'morpheus-devkit';
|
|
9
9
|
import { SQLiteChatMessageHistory } from '../memory/sqlite.js';
|
|
10
10
|
import { AuditRepository } from '../audit/repository.js';
|
|
11
11
|
import { TaskRequestContext } from '../tasks/context.js';
|
|
@@ -156,7 +156,7 @@ Respond in the same language as the task.`);
|
|
|
156
156
|
: task;
|
|
157
157
|
const messages = [systemMessage, new HumanMessage(userContent)];
|
|
158
158
|
const startMs = Date.now();
|
|
159
|
-
const response = await agent.invoke({ messages });
|
|
159
|
+
const response = await agent.invoke({ messages }, { recursionLimit: 100 });
|
|
160
160
|
const durationMs = Date.now() - startMs;
|
|
161
161
|
// Extract final response
|
|
162
162
|
const lastMessage = response.messages[response.messages.length - 1];
|
package/dist/runtime/trinity.js
CHANGED
|
@@ -238,7 +238,7 @@ ${context ? `CONTEXT FROM ORACLE:\n${context}` : ''}
|
|
|
238
238
|
const messages = [systemMessage, userMessage];
|
|
239
239
|
try {
|
|
240
240
|
const startMs = Date.now();
|
|
241
|
-
const response = await this.agent.invoke({ messages });
|
|
241
|
+
const response = await this.agent.invoke({ messages }, { recursionLimit: 100 });
|
|
242
242
|
const durationMs = Date.now() - startMs;
|
|
243
243
|
const lastMessage = response.messages[response.messages.length - 1];
|
|
244
244
|
const content = typeof lastMessage.content === 'string'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "morpheus-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Morpheus is a local AI agent for developers, running as a CLI daemon that connects to LLMs, local tools, and MCPs, enabling interaction via Terminal, Telegram, and Discord. Inspired by the character Morpheus from *The Matrix*, the project acts as an intelligent orchestrator, bridging the gap between the developer and complex systems.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"morpheus": "./bin/morpheus.js"
|
|
@@ -34,8 +34,10 @@
|
|
|
34
34
|
"@langchain/mcp-adapters": "^1.1.2",
|
|
35
35
|
"@langchain/ollama": "^1.2.1",
|
|
36
36
|
"@langchain/openai": "^1.2.3",
|
|
37
|
+
"@mozilla/readability": "^0.6.0",
|
|
37
38
|
"@openrouter/sdk": "^0.8.0",
|
|
38
39
|
"@types/better-sqlite3": "^7.6.13",
|
|
40
|
+
"@types/jsdom": "^28.0.0",
|
|
39
41
|
"@types/pg": "^8.16.0",
|
|
40
42
|
"@xenova/transformers": "^2.17.2",
|
|
41
43
|
"better-sqlite3": "^12.6.2",
|
|
@@ -54,10 +56,12 @@
|
|
|
54
56
|
"figlet": "^1.10.0",
|
|
55
57
|
"fs-extra": "^11.3.3",
|
|
56
58
|
"js-yaml": "^4.1.1",
|
|
59
|
+
"jsdom": "^28.1.0",
|
|
57
60
|
"langchain": "^1.2.16",
|
|
58
61
|
"mcp-remote": "^0.1.38",
|
|
59
62
|
"mongodb": "^6.21.0",
|
|
60
63
|
"mongoose": "^8.23.0",
|
|
64
|
+
"morpheus-devkit": "^1.0.0",
|
|
61
65
|
"multer": "^2.0.2",
|
|
62
66
|
"music-metadata": "^11.12.1",
|
|
63
67
|
"mysql2": "^3.17.4",
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { platform } from 'os';
|
|
2
|
-
export class ShellAdapter {
|
|
3
|
-
/**
|
|
4
|
-
* Factory: returns the appropriate adapter for the current OS.
|
|
5
|
-
* Uses direct imports (ESM-compatible, no require()).
|
|
6
|
-
*/
|
|
7
|
-
static create() {
|
|
8
|
-
switch (platform()) {
|
|
9
|
-
case 'win32': return new WindowsAdapter();
|
|
10
|
-
case 'darwin': return new MacAdapter();
|
|
11
|
-
default: return new LinuxAdapter();
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
// ─── Inline implementations (avoids ESM dynamic import issues) ────────────────
|
|
16
|
-
import { spawn } from 'child_process';
|
|
17
|
-
class WindowsAdapter extends ShellAdapter {
|
|
18
|
-
getShell() { return { shell: 'cmd.exe', flag: '/c' }; }
|
|
19
|
-
async run(command, args, options) {
|
|
20
|
-
return spawnCommand(command, args, { ...options, windowsHide: true, shell: true });
|
|
21
|
-
}
|
|
22
|
-
async which(binary) {
|
|
23
|
-
const result = await this.run('where', [binary], { cwd: process.cwd(), timeout_ms: 5000 });
|
|
24
|
-
if (result.exitCode !== 0)
|
|
25
|
-
return null;
|
|
26
|
-
const first = result.stdout.trim().split(/\r?\n/)[0];
|
|
27
|
-
return first || null;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
class LinuxAdapter extends ShellAdapter {
|
|
31
|
-
getShell() { return { shell: '/bin/bash', flag: '-c' }; }
|
|
32
|
-
async run(command, args, options) {
|
|
33
|
-
return spawnCommand(command, args, { ...options, shell: false });
|
|
34
|
-
}
|
|
35
|
-
async which(binary) {
|
|
36
|
-
const result = await this.run('which', [binary], { cwd: process.cwd(), timeout_ms: 5000 });
|
|
37
|
-
if (result.exitCode !== 0)
|
|
38
|
-
return null;
|
|
39
|
-
return result.stdout.trim() || null;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
class MacAdapter extends ShellAdapter {
|
|
43
|
-
getShell() { return { shell: '/bin/zsh', flag: '-c' }; }
|
|
44
|
-
async run(command, args, options) {
|
|
45
|
-
return spawnCommand(command, args, { ...options, shell: false });
|
|
46
|
-
}
|
|
47
|
-
async which(binary) {
|
|
48
|
-
const result = await this.run('which', [binary], { cwd: process.cwd(), timeout_ms: 5000 });
|
|
49
|
-
if (result.exitCode !== 0)
|
|
50
|
-
return null;
|
|
51
|
-
return result.stdout.trim() || null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function spawnCommand(command, args, options) {
|
|
55
|
-
return new Promise((resolve) => {
|
|
56
|
-
let stdout = '';
|
|
57
|
-
let stderr = '';
|
|
58
|
-
let timedOut = false;
|
|
59
|
-
const child = spawn(command, args, {
|
|
60
|
-
cwd: options.cwd,
|
|
61
|
-
env: { ...process.env, ...options.env },
|
|
62
|
-
shell: options.shell ?? false,
|
|
63
|
-
windowsHide: options.windowsHide ?? false,
|
|
64
|
-
});
|
|
65
|
-
const timer = setTimeout(() => {
|
|
66
|
-
timedOut = true;
|
|
67
|
-
child.kill('SIGKILL');
|
|
68
|
-
}, options.timeout_ms);
|
|
69
|
-
child.stdout?.on('data', (d) => { stdout += d.toString(); });
|
|
70
|
-
child.stderr?.on('data', (d) => { stderr += d.toString(); });
|
|
71
|
-
child.on('error', (err) => {
|
|
72
|
-
clearTimeout(timer);
|
|
73
|
-
resolve({ exitCode: 1, stdout, stderr: stderr + err.message, timedOut });
|
|
74
|
-
});
|
|
75
|
-
child.on('close', (code) => {
|
|
76
|
-
clearTimeout(timer);
|
|
77
|
-
resolve({ exitCode: code ?? 1, stdout, stderr, timedOut });
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
}
|
package/dist/devkit/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Register all DevKit tool factories
|
|
2
|
-
// Import order matters: each import triggers registerToolFactory() as a side effect
|
|
3
|
-
import './tools/filesystem.js';
|
|
4
|
-
import './tools/shell.js';
|
|
5
|
-
import './tools/processes.js';
|
|
6
|
-
import './tools/network.js';
|
|
7
|
-
import './tools/git.js';
|
|
8
|
-
import './tools/packages.js';
|
|
9
|
-
import './tools/system.js';
|
|
10
|
-
import './tools/browser.js';
|
|
11
|
-
export { buildDevKit } from './registry.js';
|