wolverine-ai 3.0.1 → 3.1.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/agent/agent-engine.js +41 -73
- package/src/agent/sub-agents.js +30 -11
- package/src/brain/brain.js +26 -4
- package/src/core/runner.js +6 -3
- package/src/core/wolverine.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.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": {
|
|
@@ -351,79 +351,9 @@ class AgentEngine {
|
|
|
351
351
|
async run({ errorMessage, stackTrace, primaryFile, sourceCode, brainContext }) {
|
|
352
352
|
const model = getModel("reasoning");
|
|
353
353
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
## YOUR TOOLS
|
|
359
|
-
|
|
360
|
-
FILE TOOLS:
|
|
361
|
-
- read_file: Read any file (with optional offset/limit for large files)
|
|
362
|
-
- write_file: Write a complete file (creates parent dirs)
|
|
363
|
-
- edit_file: Surgical find-and-replace (preferred for small fixes)
|
|
364
|
-
- glob_files: Find files by pattern (e.g. "**/*.js", "server/**/*.json")
|
|
365
|
-
- grep_code: Search code with regex across the project
|
|
366
|
-
- list_dir: List directory contents (check structure, find misplaced files)
|
|
367
|
-
- move_file: Move or rename files (fix misplaced files)
|
|
368
|
-
|
|
369
|
-
SHELL TOOLS:
|
|
370
|
-
- bash_exec: Run any shell command (npm install, chmod, kill, etc.)
|
|
371
|
-
- git_log: View recent commits (what changed recently?)
|
|
372
|
-
- git_diff: View uncommitted changes
|
|
373
|
-
|
|
374
|
-
DATABASE TOOLS:
|
|
375
|
-
- inspect_db: List tables, show schema, or run SELECT on SQLite databases
|
|
376
|
-
- run_db_fix: Run UPDATE/DELETE/INSERT/ALTER on SQLite databases (backs up first)
|
|
377
|
-
|
|
378
|
-
DIAGNOSTICS:
|
|
379
|
-
- check_port: Check if a port is in use and by what process
|
|
380
|
-
- check_env: Check environment variables (values auto-redacted for security)
|
|
381
|
-
|
|
382
|
-
DEPENDENCY MANAGEMENT:
|
|
383
|
-
- audit_deps: Full health check (vulnerabilities, outdated, peer conflicts, unused). Use FIRST for dependency errors.
|
|
384
|
-
- check_migration: Check if a package has a known upgrade path (express→fastify, moment→dayjs, etc.)
|
|
385
|
-
|
|
386
|
-
RESEARCH:
|
|
387
|
-
- web_fetch: Fetch a URL (docs, npm packages, error solutions)
|
|
388
|
-
|
|
389
|
-
## DIAGNOSIS FLOWCHART — follow this order:
|
|
390
|
-
|
|
391
|
-
1. READ THE ERROR CAREFULLY — what type of problem is this?
|
|
392
|
-
2. If no file path: use glob_files, grep_code, list_dir to investigate
|
|
393
|
-
3. If file path: read_file to see the code, then investigate related files
|
|
394
|
-
|
|
395
|
-
## ERROR → FIX STRATEGY TABLE
|
|
396
|
-
|
|
397
|
-
| Error Pattern | Category | Diagnostic Steps | Fix |
|
|
398
|
-
|---|---|---|---|
|
|
399
|
-
| Cannot find module 'X' | DEPENDENCY | audit_deps first, check package.json | bash_exec: npm install X |
|
|
400
|
-
| Cannot find module './X' | IMPORT | glob_files to find real path | edit_file: fix require path |
|
|
401
|
-
| ENOENT: no such file | FILE MISSING | list_dir to check structure | write_file or move_file |
|
|
402
|
-
| EACCES/EPERM | PERMISSION | bash_exec: ls -la | bash_exec: chmod 755 |
|
|
403
|
-
| EADDRINUSE | PORT | check_port to find blocker | bash_exec: kill PID, or edit config |
|
|
404
|
-
| ECONNREFUSED | SERVICE DOWN | check if DB/service is running | bash_exec: start service |
|
|
405
|
-
| SyntaxError | CODE | read_file to see context | edit_file: fix syntax |
|
|
406
|
-
| TypeError/ReferenceError | CODE | read_file + grep_code | edit_file: fix logic |
|
|
407
|
-
| ER_NO_SUCH_TABLE | DATABASE | inspect_db: tables | run_db_fix: CREATE TABLE or bash_exec migration |
|
|
408
|
-
| SQLITE_ERROR/CONSTRAINT | DATABASE | inspect_db: schema + query | run_db_fix: UPDATE/ALTER |
|
|
409
|
-
| Invalid JSON | CONFIG | read_file the JSON | edit_file: fix JSON syntax |
|
|
410
|
-
| ENOMEM / heap out of memory | RESOURCE | check_env for NODE_OPTIONS | edit config or bash_exec: increase limit |
|
|
411
|
-
| Missing env variable | CONFIG | check_env | write_file .env or edit config |
|
|
412
|
-
| Wrong file location | STRUCTURE | list_dir + glob_files | move_file to correct location |
|
|
413
|
-
| Corrupted node_modules | DEPENDENCY | bash_exec: ls node_modules | bash_exec: rm -rf node_modules && npm install |
|
|
414
|
-
| Git conflict markers | CODE | grep_code: <<<<<<< | edit_file: resolve conflicts |
|
|
415
|
-
|
|
416
|
-
## RULES
|
|
417
|
-
|
|
418
|
-
1. INVESTIGATE FIRST — never guess. Read files, check directories, inspect databases before fixing.
|
|
419
|
-
2. Read files before modifying them. Check package.json before editing imports.
|
|
420
|
-
3. Make minimal, targeted changes — fix the root cause, not symptoms.
|
|
421
|
-
4. Use the right tool: bash_exec for operational fixes, edit_file for code, run_db_fix for data.
|
|
422
|
-
5. You can edit ANY file type: .js, .json, .sql, .yaml, .env, .toml, .sh, .dockerfile, etc.
|
|
423
|
-
6. If the error has no file path, USE YOUR TOOLS to find the problem (glob, grep, list_dir, inspect_db).
|
|
424
|
-
7. When done, call the "done" tool with a summary of what you found and fixed.
|
|
425
|
-
|
|
426
|
-
Project root: ${this.cwd}${primaryFile ? `\nPrimary crash file: ${primaryFile}` : ""}`;
|
|
354
|
+
// Dynamic system prompt: compact for simple errors (~400 tokens), full for complex (~1200 tokens)
|
|
355
|
+
const isSimple = /TypeError|ReferenceError|SyntaxError|Cannot find module|Cannot read prop/.test(errorMessage || "");
|
|
356
|
+
const systemPrompt = isSimple ? _simplePrompt(this.cwd, primaryFile) : _fullPrompt(this.cwd, primaryFile);
|
|
427
357
|
|
|
428
358
|
// Build user message — handle cases with and without a specific file
|
|
429
359
|
let userContent = `The server has an error:\n\n**Error:** ${errorMessage}\n\n**Stack Trace:**\n\`\`\`\n${stackTrace}\n\`\`\``;
|
|
@@ -1114,6 +1044,44 @@ Project root: ${this.cwd}${primaryFile ? `\nPrimary crash file: ${primaryFile}`
|
|
|
1114
1044
|
}
|
|
1115
1045
|
}
|
|
1116
1046
|
|
|
1047
|
+
// ── Dynamic System Prompts ──
|
|
1048
|
+
|
|
1049
|
+
/** Compact prompt for simple code errors (~400 tokens vs ~1200). Saves 50% on 70% of heals. */
|
|
1050
|
+
function _simplePrompt(cwd, primaryFile) {
|
|
1051
|
+
return `You are Wolverine, a Node.js server repair agent. Fix the error using minimal changes.
|
|
1052
|
+
|
|
1053
|
+
TOOLS: read_file, write_file, edit_file, glob_files, grep_code, bash_exec, done
|
|
1054
|
+
RULES: Read the file before editing. Use edit_file for targeted fixes. Call done when finished.
|
|
1055
|
+
${primaryFile ? `File: ${primaryFile}` : ""}
|
|
1056
|
+
Project: ${cwd}`;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/** Full prompt for complex/unknown errors — all 18 tools + strategy table. */
|
|
1060
|
+
function _fullPrompt(cwd, primaryFile) {
|
|
1061
|
+
return `You are Wolverine, an autonomous Node.js server repair agent. Diagnose and fix the error.
|
|
1062
|
+
|
|
1063
|
+
You are a full server doctor. Errors can be code bugs, missing deps, database problems, config issues, port conflicts, permissions, or corrupted state. Investigate the root cause before fixing.
|
|
1064
|
+
|
|
1065
|
+
TOOLS: read_file, write_file, edit_file, glob_files, grep_code, list_dir, move_file, bash_exec, git_log, git_diff, inspect_db, run_db_fix, check_port, check_env, audit_deps, check_migration, web_fetch, done
|
|
1066
|
+
|
|
1067
|
+
STRATEGY:
|
|
1068
|
+
- Cannot find module 'X' → bash_exec: npm install X
|
|
1069
|
+
- Cannot find module './X' → edit_file: fix require path
|
|
1070
|
+
- ENOENT → write_file or move_file
|
|
1071
|
+
- EADDRINUSE → check_port then bash_exec: kill
|
|
1072
|
+
- TypeError/ReferenceError → read_file then edit_file
|
|
1073
|
+
- Database error → inspect_db then run_db_fix
|
|
1074
|
+
- Missing env var → check_env
|
|
1075
|
+
|
|
1076
|
+
RULES:
|
|
1077
|
+
1. Investigate first — read files before modifying
|
|
1078
|
+
2. Minimal targeted changes — fix root cause not symptoms
|
|
1079
|
+
3. bash_exec for operational fixes, edit_file for code, run_db_fix for data
|
|
1080
|
+
4. Call done with summary when finished
|
|
1081
|
+
${primaryFile ? `\nFile: ${primaryFile}` : ""}
|
|
1082
|
+
Project: ${cwd}`;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1117
1085
|
// ── Zero-Cost Compaction Helpers (claw-code pattern) ──
|
|
1118
1086
|
|
|
1119
1087
|
/**
|
package/src/agent/sub-agents.js
CHANGED
|
@@ -33,19 +33,38 @@ const AGENT_TOOL_SETS = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
// Default model + budget per agent type
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
explore: { model: "classifier", maxTurns: 5
|
|
41
|
-
plan: { model: "classifier", maxTurns: 3
|
|
42
|
-
fix: { model: "coding", maxTurns: 5
|
|
43
|
-
verify: { model: "classifier", maxTurns: 3
|
|
44
|
-
research: { model: "classifier", maxTurns: 3
|
|
45
|
-
security: { model: "audit", maxTurns: 3
|
|
46
|
-
database: { model: "coding", maxTurns: 5
|
|
36
|
+
// Dynamic token budgets: scale with error complexity.
|
|
37
|
+
// Simple errors (TypeError) get tight budgets. Complex errors (database, multi-file) get more.
|
|
38
|
+
// Triage agents use cheap models (Haiku), fixer uses expensive (Sonnet/Opus).
|
|
39
|
+
const AGENT_CONFIGS_BASE = {
|
|
40
|
+
explore: { model: "classifier", maxTurns: 5 },
|
|
41
|
+
plan: { model: "classifier", maxTurns: 3 },
|
|
42
|
+
fix: { model: "coding", maxTurns: 5 },
|
|
43
|
+
verify: { model: "classifier", maxTurns: 3 },
|
|
44
|
+
research: { model: "classifier", maxTurns: 3 },
|
|
45
|
+
security: { model: "audit", maxTurns: 3 },
|
|
46
|
+
database: { model: "coding", maxTurns: 5 },
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
const AGENT_BUDGETS = {
|
|
50
|
+
simple: { explore: 8000, plan: 5000, fix: 25000, verify: 5000, research: 5000, security: 5000, database: 25000 },
|
|
51
|
+
moderate:{ explore: 15000, plan: 10000, fix: 50000, verify: 8000, research: 10000, security: 8000, database: 50000 },
|
|
52
|
+
complex: { explore: 25000, plan: 15000, fix: 80000, verify: 10000, research: 15000, security: 10000, database: 80000 },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function getAgentConfig(type, errorComplexity) {
|
|
56
|
+
const base = AGENT_CONFIGS_BASE[type] || { model: "classifier", maxTurns: 3 };
|
|
57
|
+
const tier = errorComplexity || "moderate";
|
|
58
|
+
const budget = AGENT_BUDGETS[tier] || AGENT_BUDGETS.moderate;
|
|
59
|
+
return { ...base, maxTokens: budget[type] || 15000 };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Backward compat
|
|
63
|
+
const AGENT_CONFIGS = {};
|
|
64
|
+
for (const type of Object.keys(AGENT_CONFIGS_BASE)) {
|
|
65
|
+
AGENT_CONFIGS[type] = { ...AGENT_CONFIGS_BASE[type], maxTokens: AGENT_BUDGETS.moderate[type] || 15000 };
|
|
66
|
+
}
|
|
67
|
+
|
|
49
68
|
// System prompts per agent type
|
|
50
69
|
const AGENT_PROMPTS = {
|
|
51
70
|
explore: "You are an Explorer agent. Your job is to investigate the codebase and find files relevant to the problem. Read files, search for patterns, check git history. Report what you found — do NOT make changes.",
|
package/src/brain/brain.js
CHANGED
|
@@ -257,6 +257,10 @@ const SEED_DOCS = [
|
|
|
257
257
|
text: "Token waste prevention: 3 layers. (1) Empty stderr guard — signal kills with no error output just restart, no AI ($0.00). (2) Loop guard — 3 failed heals on same error → stop and file bug report, no more AI calls. (3) Global rate limit — max 5 heals per 5 minutes regardless of error signature. Idle server burns exactly $0.00 in tokens.",
|
|
258
258
|
metadata: { topic: "token-protection" },
|
|
259
259
|
},
|
|
260
|
+
{
|
|
261
|
+
text: "Audit optimizations: (1) Brain namespace isolation — seed docs (20K tokens of wolverine self-knowledge) excluded from error healing searches. Only searched when query is about wolverine itself. Cuts context by 50%. (2) Dynamic system prompt — simple errors (TypeError/ReferenceError) get 400-token compact prompt with 7 tools. Complex errors get full 1200-token prompt with 18 tools + strategy table. Saves 50% on 70% of heals. (3) Stability timer race fix — backup ID captured in closure, prevents wrong backup promoted if new heal starts before 30min timer. (4) Dynamic sub-agent budgets — simple: explore 8K/fix 25K, moderate: 15K/50K, complex: 25K/80K. Saves 40% on simple fixes. (5) Function map hash check — skips re-embedding if unchanged (MD5 hash stored in .wolverine/brain/.fmap-hash). 10-20% faster startup.",
|
|
262
|
+
metadata: { topic: "audit-optimizations" },
|
|
263
|
+
},
|
|
260
264
|
{
|
|
261
265
|
text: "Agent efficiency (claw-code patterns): (1) Anthropic prompt caching — system prompt marked with cache_control:{type:'ephemeral'}, cached server-side across agent turns, 90% cheaper on repeat calls (12-16K saved tokens per heal). (2) Tool result truncation — capped at 4K chars before entering message history, prevents context blowup from large grep/file reads. (3) Zero-cost structural compaction — extracts signals (tools used, files touched, errors found, actions taken) from message history WITHOUT an LLM call. Costs $0.00 vs old method that burned tokens on a compacting model. Triggers when estimated tokens > 10K (text.length/4 approximation). Preserves last 4 messages verbatim. (2) Token estimation — text.length/4+1, fast approximation without tokenizer, ~10% accurate. Used for budget decisions before API calls. (3) Error-graceful tools — tool errors returned as [ERROR] prefixed results, not thrown. Model sees the error and decides how to proceed. (4) Pre/post tool hooks — shell commands in .wolverine/hooks.json, exit 0=allow, 2=deny. Enables audit logging and policy enforcement without hard-coding.",
|
|
262
266
|
metadata: { topic: "agent-efficiency" },
|
|
@@ -303,8 +307,18 @@ class Brain {
|
|
|
303
307
|
this.functionMap = scanProject(this.projectRoot);
|
|
304
308
|
console.log(chalk.gray(` 🧠 Found: ${this.functionMap.routes.length} routes, ${this.functionMap.functions.length} functions, ${this.functionMap.classes.length} classes`));
|
|
305
309
|
|
|
306
|
-
// 3. Embed function map (
|
|
307
|
-
|
|
310
|
+
// 3. Embed function map — only if changed (hash check saves 10-20% startup time)
|
|
311
|
+
const crypto = require("crypto");
|
|
312
|
+
const mapHash = crypto.createHash("md5").update(JSON.stringify(this.functionMap)).digest("hex");
|
|
313
|
+
const hashPath = path.join(this.projectRoot, ".wolverine", "brain", ".fmap-hash");
|
|
314
|
+
let lastHash = "";
|
|
315
|
+
try { lastHash = fs.readFileSync(hashPath, "utf-8").trim(); } catch {}
|
|
316
|
+
if (mapHash !== lastHash) {
|
|
317
|
+
await this._embedFunctionMap();
|
|
318
|
+
try { fs.writeFileSync(hashPath, mapHash, "utf-8"); } catch {}
|
|
319
|
+
} else {
|
|
320
|
+
console.log(chalk.gray(" 🧠 Function map unchanged — skipping re-embed"));
|
|
321
|
+
}
|
|
308
322
|
|
|
309
323
|
// 4. Save
|
|
310
324
|
this.store.save();
|
|
@@ -380,9 +394,17 @@ class Brain {
|
|
|
380
394
|
parts.push("## Server Function Map\n" + this.functionMap.summary);
|
|
381
395
|
}
|
|
382
396
|
|
|
383
|
-
//
|
|
397
|
+
// Search only operational namespaces — NOT docs (seed docs add 20K tokens of
|
|
398
|
+
// wolverine self-knowledge that's irrelevant to fixing a TypeError).
|
|
399
|
+
// Docs are only searched when user asks about wolverine itself.
|
|
400
|
+
const isAboutWolverine = /wolverine|heal|pipeline|agent|backup|brain|dashboard/i.test(errorMessage || "");
|
|
384
401
|
if (errorMessage) {
|
|
385
|
-
const
|
|
402
|
+
const searchNamespaces = isAboutWolverine ? undefined : undefined; // search all but filter below
|
|
403
|
+
const allMemories = await this.recall(errorMessage, { topK: 8, minScore: 0.3 });
|
|
404
|
+
// Filter: exclude seed docs unless query is about wolverine
|
|
405
|
+
const memories = isAboutWolverine
|
|
406
|
+
? allMemories.slice(0, 5)
|
|
407
|
+
: allMemories.filter(m => m.namespace !== "docs").slice(0, 5);
|
|
386
408
|
if (memories.length > 0) {
|
|
387
409
|
parts.push("\n## Relevant Context from Brain");
|
|
388
410
|
for (const mem of memories) {
|
package/src/core/runner.js
CHANGED
|
@@ -643,15 +643,18 @@ class WolverineRunner {
|
|
|
643
643
|
|
|
644
644
|
_startStabilityTimer() {
|
|
645
645
|
this._clearStabilityTimer();
|
|
646
|
+
// Capture backup ID in closure — prevents race where a new heal overwrites _lastBackupId
|
|
647
|
+
// before this timer fires, causing the wrong backup to be promoted.
|
|
648
|
+
const backupId = this._lastBackupId;
|
|
646
649
|
this._stabilityTimer = setTimeout(() => {
|
|
647
|
-
if (
|
|
648
|
-
this.backupManager.markStable(
|
|
650
|
+
if (backupId && this.running) {
|
|
651
|
+
this.backupManager.markStable(backupId);
|
|
649
652
|
this.retryCount = 0;
|
|
650
653
|
const healthStats = this.healthMonitor.getStats();
|
|
651
654
|
if (healthStats.totalChecks > 0) {
|
|
652
655
|
console.log(chalk.green(` 📊 Uptime: ${healthStats.uptimePercent}% (${healthStats.totalPasses}/${healthStats.totalChecks} checks passed)`));
|
|
653
656
|
}
|
|
654
|
-
this.logger.info(EVENT_TYPES.BACKUP_STABLE, `Backup ${
|
|
657
|
+
this.logger.info(EVENT_TYPES.BACKUP_STABLE, `Backup ${backupId} promoted to stable`, { backupId });
|
|
655
658
|
}
|
|
656
659
|
}, STABILITY_THRESHOLD_MS);
|
|
657
660
|
}
|
package/src/core/wolverine.js
CHANGED
|
@@ -260,7 +260,7 @@ async function _healImpl({ stderr, cwd, sandbox, notifier, rateLimiter, backupMa
|
|
|
260
260
|
let priorSummary = "";
|
|
261
261
|
if (priorAttempts && priorAttempts.length > 0) {
|
|
262
262
|
priorSummary = "\nPRIOR ATTEMPTS (do NOT repeat):\n" + priorAttempts.map(a =>
|
|
263
|
-
`- Attempt ${a.iteration} (${a.mode}): ${a.explanation?.slice(0,
|
|
263
|
+
`- Attempt ${a.iteration} (${a.mode}): ${a.explanation?.slice(0, 50)}`
|
|
264
264
|
).join("\n") + "\n";
|
|
265
265
|
}
|
|
266
266
|
|