wolverine-ai 4.8.0 → 4.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/package.json +1 -1
- package/src/brain/brain.js +4 -0
- package/src/brain/tool-router.js +211 -0
- package/src/core/wolverine.js +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "Self-healing Node.js server framework powered by AI. Catches crashes, diagnoses errors, generates fixes, verifies, and restarts — automatically.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
package/src/brain/brain.js
CHANGED
|
@@ -145,6 +145,10 @@ const SEED_DOCS = [
|
|
|
145
145
|
text: "Server context scanner (wolverine --init): scans server/ directory on every startup to build .wolverine/server-context.json. Extracts routes (HTTP methods + paths from fastify/express), middleware stack, database type + tables, config structure, dependencies, env vars used (process.env.X patterns), and full file tree. Context summary auto-injected into agent's heal prompt so it knows the server's route map, DB schema, and dependencies without re-scanning. Manual scan: wolverine --init. Auto-scan: runs silently on every boot. The context is read-only — never modified by the agent.",
|
|
146
146
|
metadata: { topic: "server-context" },
|
|
147
147
|
},
|
|
148
|
+
{
|
|
149
|
+
text: "Tool router (src/brain/tool-router.js): maps error types to recommended tool chains. Injected into agent prompt automatically — agent sees 'TOOL ROUTE: diagnose with [X, Y], fix with [Z]. Strategy: ...' for every error it heals. 25+ error categories mapped: TypeError→read_file+edit_file, ENOENT→list_dir+write_file, ECONNREFUSED→check_network+check_port+inspect_cache, EMFILE→check_file_descriptors+disk_cleanup, ENOSPC→disk_cleanup, certificate→inspect_certificate, OOM→check_memory+check_event_loop, websocket���check_websocket, database→inspect_db+run_db_fix (with prereq: inspect_db FIRST), missing_module→audit_deps+verify_node_modules, env_missing→inspect_env+add_env_var. Lookup is O(1) by error type + regex fallback. No AI tokens used for routing.",
|
|
150
|
+
metadata: { topic: "tool-router" },
|
|
151
|
+
},
|
|
148
152
|
{
|
|
149
153
|
text: "Telemetry architecture: 4 files, ~250 lines total. heartbeat.js sends one HTTP POST every 60s (5s timeout, non-blocking). register.js auto-registers and caches key in memory + disk. queue.js appends to JSONL file only on failure, trims lazily. telemetry.js collects from subsystems using optional chaining (no crashes if subsystem missing). All secrets redacted before sending. Response bodies drained immediately (res.resume). No blocking, no delays, no busy waits.",
|
|
150
154
|
metadata: { topic: "telemetry-architecture" },
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Router — maps error types to recommended tool chains.
|
|
3
|
+
*
|
|
4
|
+
* Lightweight adjacency graph that tells the agent which tools to use
|
|
5
|
+
* for each error category. Injected into the agent prompt so it doesn't
|
|
6
|
+
* waste turns guessing.
|
|
7
|
+
*
|
|
8
|
+
* Structure per error type:
|
|
9
|
+
* diagnose: tools to understand the problem (read-only)
|
|
10
|
+
* fix: tools to apply the solution (write)
|
|
11
|
+
* hint: one-line strategy for the AI
|
|
12
|
+
* prereq: tools that must run BEFORE fix tools
|
|
13
|
+
*
|
|
14
|
+
* Lookup: O(1) by error classifier output or regex match on error message.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const TOOL_ROUTES = {
|
|
18
|
+
// ── Code Errors ──
|
|
19
|
+
"TypeError": {
|
|
20
|
+
diagnose: ["read_file", "grep_code"],
|
|
21
|
+
fix: ["edit_file"],
|
|
22
|
+
hint: "Read the file at the error line. Find the undefined/null variable. Add a guard or fix the assignment.",
|
|
23
|
+
},
|
|
24
|
+
"ReferenceError": {
|
|
25
|
+
diagnose: ["read_file", "grep_code"],
|
|
26
|
+
fix: ["edit_file"],
|
|
27
|
+
hint: "Variable is not defined. Check for typos, missing imports, or scope issues.",
|
|
28
|
+
},
|
|
29
|
+
"SyntaxError": {
|
|
30
|
+
diagnose: ["read_file"],
|
|
31
|
+
fix: ["edit_file"],
|
|
32
|
+
hint: "Parse error in source. Read the file and fix the syntax at the reported line.",
|
|
33
|
+
},
|
|
34
|
+
"RangeError": {
|
|
35
|
+
diagnose: ["read_file", "check_memory"],
|
|
36
|
+
fix: ["edit_file"],
|
|
37
|
+
hint: "Stack overflow or invalid array length. Check for infinite recursion or unbounded growth.",
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// ── Module/Dependency Errors ──
|
|
41
|
+
"missing_module": {
|
|
42
|
+
diagnose: ["audit_deps", "verify_node_modules"],
|
|
43
|
+
fix: ["bash_exec"],
|
|
44
|
+
hint: "Run npm install <module>. If in package.json but missing from disk, run npm ci.",
|
|
45
|
+
},
|
|
46
|
+
"missing_file": {
|
|
47
|
+
diagnose: ["read_file", "list_dir", "inspect_env"],
|
|
48
|
+
fix: ["write_file", "bash_exec"],
|
|
49
|
+
hint: "Check if the path is wrong (edit require) or if the file should exist (create it with expected content).",
|
|
50
|
+
},
|
|
51
|
+
"version_conflict": {
|
|
52
|
+
diagnose: ["audit_deps", "check_migration", "verify_node_modules"],
|
|
53
|
+
fix: ["bash_exec"],
|
|
54
|
+
hint: "Check peer deps and version compatibility. May need npm install package@version or full reinstall.",
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// ── File System Errors ──
|
|
58
|
+
"ENOENT": {
|
|
59
|
+
diagnose: ["list_dir", "inspect_env", "read_file"],
|
|
60
|
+
fix: ["write_file", "bash_exec"],
|
|
61
|
+
prereq: ["read_file"],
|
|
62
|
+
hint: "File or directory doesn't exist. Check if path is correct, create if missing.",
|
|
63
|
+
},
|
|
64
|
+
"EACCES": {
|
|
65
|
+
diagnose: ["bash_exec", "list_dir"],
|
|
66
|
+
fix: ["bash_exec"],
|
|
67
|
+
hint: "Permission denied. Run chmod to fix permissions on the target file/directory.",
|
|
68
|
+
},
|
|
69
|
+
"ENOSPC": {
|
|
70
|
+
diagnose: ["disk_cleanup", "check_memory"],
|
|
71
|
+
fix: ["disk_cleanup"],
|
|
72
|
+
hint: "Disk full. Run disk_cleanup with dry_run=false to clear old backups and caches.",
|
|
73
|
+
},
|
|
74
|
+
"EMFILE": {
|
|
75
|
+
diagnose: ["check_file_descriptors", "list_processes", "check_event_loop"],
|
|
76
|
+
fix: ["disk_cleanup", "restart_service"],
|
|
77
|
+
hint: "Too many open files. Check for FD leaks, clear caches, consider raising ulimit in system profile.",
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// ── Network Errors ──
|
|
81
|
+
"ECONNREFUSED": {
|
|
82
|
+
diagnose: ["check_network", "check_port", "inspect_cache", "inspect_env"],
|
|
83
|
+
fix: ["edit_file", "restart_service"],
|
|
84
|
+
hint: "Target service is down or wrong host/port. Check config, verify service is running.",
|
|
85
|
+
},
|
|
86
|
+
"ECONNRESET": {
|
|
87
|
+
diagnose: ["check_network", "check_logs"],
|
|
88
|
+
fix: ["edit_file"],
|
|
89
|
+
hint: "Connection reset by remote. Check for timeout settings, proxy config, or unstable network.",
|
|
90
|
+
},
|
|
91
|
+
"ETIMEDOUT": {
|
|
92
|
+
diagnose: ["check_network", "check_logs", "inspect_certificate"],
|
|
93
|
+
fix: ["edit_file"],
|
|
94
|
+
hint: "Connection timed out. Increase timeout, check DNS, verify endpoint is reachable.",
|
|
95
|
+
},
|
|
96
|
+
"EADDRINUSE": {
|
|
97
|
+
diagnose: ["check_port", "list_processes"],
|
|
98
|
+
fix: ["bash_exec"],
|
|
99
|
+
hint: "Port already in use. Find and kill the stale process holding the port.",
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
// ── Database Errors ──
|
|
103
|
+
"database": {
|
|
104
|
+
diagnose: ["inspect_db", "inspect_cache", "read_file"],
|
|
105
|
+
fix: ["run_db_fix", "edit_file"],
|
|
106
|
+
prereq: ["inspect_db"],
|
|
107
|
+
hint: "ALWAYS inspect_db before run_db_fix. Check table schema, query the affected data, then fix.",
|
|
108
|
+
},
|
|
109
|
+
"pool_exhaustion": {
|
|
110
|
+
diagnose: ["inspect_cache", "check_network", "check_logs"],
|
|
111
|
+
fix: ["edit_file", "restart_service"],
|
|
112
|
+
hint: "DB connection pool exhausted. Check for connection leaks, increase pool size, or restart.",
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// ── SSL/TLS Errors ──
|
|
116
|
+
"certificate": {
|
|
117
|
+
diagnose: ["inspect_certificate", "check_network", "inspect_env"],
|
|
118
|
+
fix: ["bash_exec", "edit_file", "add_env_var"],
|
|
119
|
+
hint: "Check cert expiry, SAN list, and chain. May need renewal, hostname fix, or CA bundle.",
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// ── Memory/Performance Errors ──
|
|
123
|
+
"OOM": {
|
|
124
|
+
diagnose: ["check_memory", "check_event_loop", "list_processes"],
|
|
125
|
+
fix: ["edit_file", "restart_service"],
|
|
126
|
+
hint: "Out of memory. Check for memory leaks (growing arrays, unclosed streams), reduce batch sizes.",
|
|
127
|
+
},
|
|
128
|
+
"event_loop_blocked": {
|
|
129
|
+
diagnose: ["check_event_loop", "check_logs", "check_memory"],
|
|
130
|
+
fix: ["edit_file"],
|
|
131
|
+
hint: "Synchronous operation blocking event loop. Replace readFileSync→readFile, execSync→exec, etc.",
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
// ── WebSocket Errors ──
|
|
135
|
+
"websocket": {
|
|
136
|
+
diagnose: ["check_websocket", "check_network", "check_port"],
|
|
137
|
+
fix: ["edit_file"],
|
|
138
|
+
hint: "Test WS handshake. Check upgrade headers, proxy config, and connection timeout settings.",
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
// ── Config/Environment Errors ──
|
|
142
|
+
"config": {
|
|
143
|
+
diagnose: ["read_file", "inspect_env", "list_dir"],
|
|
144
|
+
fix: ["write_file", "edit_file", "add_env_var"],
|
|
145
|
+
hint: "Check if config file exists and has expected structure. Check if required env vars are set.",
|
|
146
|
+
},
|
|
147
|
+
"env_missing": {
|
|
148
|
+
diagnose: ["inspect_env", "read_file"],
|
|
149
|
+
fix: ["add_env_var"],
|
|
150
|
+
hint: "Required environment variable not set. Add it to .env.local with the correct value.",
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Find tool recommendations for an error.
|
|
156
|
+
* @param {string} errorMessage — the raw error message
|
|
157
|
+
* @param {string} errorType — from error-parser classifyError() (optional)
|
|
158
|
+
* @returns {{ diagnose: string[], fix: string[], prereq?: string[], hint: string } | null}
|
|
159
|
+
*/
|
|
160
|
+
function route(errorMessage, errorType) {
|
|
161
|
+
const msg = (errorMessage || "").toLowerCase();
|
|
162
|
+
|
|
163
|
+
// 1. Try exact error type match
|
|
164
|
+
if (errorType && TOOL_ROUTES[errorType]) return TOOL_ROUTES[errorType];
|
|
165
|
+
|
|
166
|
+
// 2. Try error class match
|
|
167
|
+
if (/typeerror/i.test(msg)) return TOOL_ROUTES.TypeError;
|
|
168
|
+
if (/referenceerror/i.test(msg)) return TOOL_ROUTES.ReferenceError;
|
|
169
|
+
if (/syntaxerror|unexpected token/i.test(msg)) return TOOL_ROUTES.SyntaxError;
|
|
170
|
+
if (/rangeerror/i.test(msg)) return TOOL_ROUTES.RangeError;
|
|
171
|
+
|
|
172
|
+
// 3. Try errno/code match
|
|
173
|
+
if (/enoent|no such file/i.test(msg)) return TOOL_ROUTES.ENOENT;
|
|
174
|
+
if (/eacces|eperm/i.test(msg)) return TOOL_ROUTES.EACCES;
|
|
175
|
+
if (/enospc/i.test(msg)) return TOOL_ROUTES.ENOSPC;
|
|
176
|
+
if (/emfile|enfile/i.test(msg)) return TOOL_ROUTES.EMFILE;
|
|
177
|
+
if (/econnrefused/i.test(msg)) return TOOL_ROUTES.ECONNREFUSED;
|
|
178
|
+
if (/econnreset/i.test(msg)) return TOOL_ROUTES.ECONNRESET;
|
|
179
|
+
if (/etimedout/i.test(msg)) return TOOL_ROUTES.ETIMEDOUT;
|
|
180
|
+
if (/eaddrinuse/i.test(msg)) return TOOL_ROUTES.EADDRINUSE;
|
|
181
|
+
|
|
182
|
+
// 4. Try pattern match
|
|
183
|
+
if (/cannot find module/i.test(msg)) return /['"][./\\]/.test(msg) ? TOOL_ROUTES.missing_file : TOOL_ROUTES.missing_module;
|
|
184
|
+
if (/cert|ssl|tls|self.signed/i.test(msg)) return TOOL_ROUTES.certificate;
|
|
185
|
+
if (/pool.*exhaust|pool.*timeout|acquire.*timeout/i.test(msg)) return TOOL_ROUTES.pool_exhaustion;
|
|
186
|
+
if (/websocket|ws.*close|transport.*close/i.test(msg)) return TOOL_ROUTES.websocket;
|
|
187
|
+
if (/out of memory|heap.*limit|allocation.*failed/i.test(msg)) return TOOL_ROUTES.OOM;
|
|
188
|
+
if (/not.set|undefined.*env|missing.*env/i.test(msg)) return TOOL_ROUTES.env_missing;
|
|
189
|
+
if (/missing.*config|invalid.*json|invalid.*config/i.test(msg)) return TOOL_ROUTES.config;
|
|
190
|
+
if (/sqlite|postgres|mysql|mongo|sequelize|prisma|knex/i.test(msg)) return TOOL_ROUTES.database;
|
|
191
|
+
if (/peer dep|eresolve|version.*mismatch/i.test(msg)) return TOOL_ROUTES.version_conflict;
|
|
192
|
+
|
|
193
|
+
return null; // unknown — let the AI figure it out
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get a compact prompt injection for the agent.
|
|
198
|
+
* Returns a string like "TOOL ROUTE: diagnose with [check_port, list_processes], fix with [bash_exec]. Hint: ..."
|
|
199
|
+
*/
|
|
200
|
+
function getRoutePrompt(errorMessage, errorType) {
|
|
201
|
+
const r = route(errorMessage, errorType);
|
|
202
|
+
if (!r) return "";
|
|
203
|
+
const parts = [`TOOL ROUTE for this error:`];
|
|
204
|
+
parts.push(` Diagnose: ${r.diagnose.join(", ")}`);
|
|
205
|
+
if (r.prereq) parts.push(` Prerequisites: ${r.prereq.join(", ")} (run these FIRST)`);
|
|
206
|
+
parts.push(` Fix: ${r.fix.join(", ")}`);
|
|
207
|
+
parts.push(` Strategy: ${r.hint}`);
|
|
208
|
+
return parts.join("\n");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = { route, getRoutePrompt, TOOL_ROUTES };
|
package/src/core/wolverine.js
CHANGED
|
@@ -13,6 +13,7 @@ const { RateLimiter } = require("../security/rate-limiter");
|
|
|
13
13
|
const { detectInjection } = require("../security/injection-detector");
|
|
14
14
|
const { redact, hasSecrets } = require("../security/secret-redactor");
|
|
15
15
|
const { BackupManager } = require("../backup/backup-manager");
|
|
16
|
+
const { getRoutePrompt } = require("../brain/tool-router");
|
|
16
17
|
const { AgentEngine } = require("../agent/agent-engine");
|
|
17
18
|
const { ResearchAgent } = require("../agent/research-agent");
|
|
18
19
|
const { GoalLoop } = require("../agent/goal-loop");
|
|
@@ -215,6 +216,12 @@ async function _healImpl({ stderr, cwd, sandbox, notifier, rateLimiter, backupMa
|
|
|
215
216
|
const serverCtx = getServerContextSummary(cwd);
|
|
216
217
|
if (serverCtx) brainContext += serverCtx + "\n\n";
|
|
217
218
|
} catch {}
|
|
219
|
+
// Inject tool routing — tells agent exactly which tools to use for this error type
|
|
220
|
+
const toolRoute = getRoutePrompt(parsed.errorMessage, parsed.errorType);
|
|
221
|
+
if (toolRoute) {
|
|
222
|
+
brainContext += toolRoute + "\n\n";
|
|
223
|
+
console.log(chalk.gray(` 🗺️ Tool route: ${toolRoute.split("\n")[1]?.trim() || "matched"}`));
|
|
224
|
+
}
|
|
218
225
|
// Inject relevant skill context (claw-code: pre-enrich prompt with matched tools)
|
|
219
226
|
if (skills) {
|
|
220
227
|
const skillCtx = skills.buildContext(parsed.errorMessage);
|