wolverine-ai 1.0.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.
Files changed (79) hide show
  1. package/PLATFORM.md +442 -0
  2. package/README.md +475 -0
  3. package/SERVER_BEST_PRACTICES.md +62 -0
  4. package/TELEMETRY.md +108 -0
  5. package/bin/wolverine.js +95 -0
  6. package/examples/01-basic-typo.js +31 -0
  7. package/examples/02-multi-file/routes/users.js +15 -0
  8. package/examples/02-multi-file/server.js +25 -0
  9. package/examples/03-syntax-error.js +23 -0
  10. package/examples/04-secret-leak.js +14 -0
  11. package/examples/05-expired-key.js +27 -0
  12. package/examples/06-json-config/config.json +13 -0
  13. package/examples/06-json-config/server.js +28 -0
  14. package/examples/07-rate-limit-loop.js +11 -0
  15. package/examples/08-sandbox-escape.js +20 -0
  16. package/examples/buggy-server.js +39 -0
  17. package/examples/demos/01-basic-typo/index.js +20 -0
  18. package/examples/demos/01-basic-typo/routes/api.js +13 -0
  19. package/examples/demos/01-basic-typo/routes/health.js +4 -0
  20. package/examples/demos/02-multi-file/index.js +24 -0
  21. package/examples/demos/02-multi-file/routes/api.js +13 -0
  22. package/examples/demos/02-multi-file/routes/health.js +4 -0
  23. package/examples/demos/03-syntax-error/index.js +18 -0
  24. package/examples/demos/04-secret-leak/index.js +16 -0
  25. package/examples/demos/05-expired-key/index.js +21 -0
  26. package/examples/demos/06-json-config/config.json +9 -0
  27. package/examples/demos/06-json-config/index.js +20 -0
  28. package/examples/demos/07-null-crash/index.js +16 -0
  29. package/examples/run-demo.js +110 -0
  30. package/package.json +67 -0
  31. package/server/config/settings.json +62 -0
  32. package/server/index.js +33 -0
  33. package/server/routes/api.js +12 -0
  34. package/server/routes/health.js +16 -0
  35. package/server/routes/time.js +12 -0
  36. package/src/agent/agent-engine.js +727 -0
  37. package/src/agent/goal-loop.js +140 -0
  38. package/src/agent/research-agent.js +120 -0
  39. package/src/agent/sub-agents.js +176 -0
  40. package/src/backup/backup-manager.js +321 -0
  41. package/src/brain/brain.js +315 -0
  42. package/src/brain/embedder.js +131 -0
  43. package/src/brain/function-map.js +263 -0
  44. package/src/brain/vector-store.js +267 -0
  45. package/src/core/ai-client.js +387 -0
  46. package/src/core/cluster-manager.js +144 -0
  47. package/src/core/config.js +89 -0
  48. package/src/core/error-parser.js +87 -0
  49. package/src/core/health-monitor.js +129 -0
  50. package/src/core/models.js +132 -0
  51. package/src/core/patcher.js +55 -0
  52. package/src/core/runner.js +464 -0
  53. package/src/core/system-info.js +141 -0
  54. package/src/core/verifier.js +146 -0
  55. package/src/core/wolverine.js +290 -0
  56. package/src/dashboard/server.js +1332 -0
  57. package/src/index.js +94 -0
  58. package/src/logger/event-logger.js +237 -0
  59. package/src/logger/pricing.js +96 -0
  60. package/src/logger/repair-history.js +109 -0
  61. package/src/logger/token-tracker.js +277 -0
  62. package/src/mcp/mcp-client.js +224 -0
  63. package/src/mcp/mcp-registry.js +228 -0
  64. package/src/mcp/mcp-security.js +152 -0
  65. package/src/monitor/perf-monitor.js +300 -0
  66. package/src/monitor/process-monitor.js +231 -0
  67. package/src/monitor/route-prober.js +191 -0
  68. package/src/notifications/notifier.js +227 -0
  69. package/src/platform/heartbeat.js +93 -0
  70. package/src/platform/queue.js +53 -0
  71. package/src/platform/register.js +64 -0
  72. package/src/platform/telemetry.js +76 -0
  73. package/src/security/admin-auth.js +150 -0
  74. package/src/security/injection-detector.js +174 -0
  75. package/src/security/rate-limiter.js +152 -0
  76. package/src/security/sandbox.js +128 -0
  77. package/src/security/secret-redactor.js +217 -0
  78. package/src/skills/skill-registry.js +129 -0
  79. package/src/skills/sql.js +375 -0
@@ -0,0 +1,140 @@
1
+ const chalk = require("chalk");
2
+ const { verifyFix } = require("../core/verifier");
3
+
4
+ /**
5
+ * Goal Loop — claw-code run_turn_loop pattern.
6
+ *
7
+ * Instead of: try fast → try agent → give up
8
+ * Now: set goal → attempt → verify → learn → retry with new context
9
+ *
10
+ * The loop:
11
+ * 1. Set goal (e.g. "server starts without error X")
12
+ * 2. Check brain for past attempts on this goal
13
+ * 3. Attempt a fix (strategy chosen by iteration)
14
+ * 4. Verify: did we meet the goal?
15
+ * 5. Yes → record success, exit
16
+ * 6. No → record failure, research if needed, loop with new context
17
+ *
18
+ * Each iteration gets smarter because failures feed into the next attempt's context.
19
+ */
20
+
21
+ class GoalLoop {
22
+ constructor(options = {}) {
23
+ this.maxIterations = options.maxIterations || 3;
24
+ this.researcher = options.researcher;
25
+ this.logger = options.logger;
26
+ this.onAttempt = options.onAttempt; // async (iteration, context) => { healed, explanation }
27
+ this.onVerify = options.onVerify; // async () => { verified, status }
28
+ this.goal = options.goal || "Fix the error";
29
+
30
+ this._attempts = [];
31
+ }
32
+
33
+ /**
34
+ * Run the goal loop. Returns the final result.
35
+ */
36
+ async run({ errorMessage, filePath, cwd }) {
37
+ console.log(chalk.magenta(`\n 🎯 Goal: ${this.goal}`));
38
+ console.log(chalk.gray(` Max iterations: ${this.maxIterations}\n`));
39
+
40
+ if (this.logger) {
41
+ this.logger.info("goal.start", `Goal: ${this.goal}`, { maxIterations: this.maxIterations, error: errorMessage?.slice(0, 100) });
42
+ }
43
+
44
+ for (let i = 0; i < this.maxIterations; i++) {
45
+ const iteration = i + 1;
46
+ console.log(chalk.blue(` ── Iteration ${iteration}/${this.maxIterations} ──`));
47
+
48
+ // Build context from past attempts
49
+ let context = "";
50
+ if (this.researcher) {
51
+ context = await this.researcher.buildFixContext(errorMessage);
52
+ }
53
+ if (this._attempts.length > 0) {
54
+ context += "\n\n## This Session's Attempts";
55
+ for (const a of this._attempts) {
56
+ context += `\nAttempt ${a.iteration}: ${a.success ? "SUCCESS" : "FAILED"} — ${a.explanation}`;
57
+ }
58
+ }
59
+
60
+ // Attempt the fix
61
+ let attempt;
62
+ try {
63
+ attempt = await this.onAttempt(iteration, context);
64
+ } catch (err) {
65
+ attempt = { healed: false, explanation: `Error: ${err.message}` };
66
+ }
67
+
68
+ this._attempts.push({
69
+ iteration,
70
+ success: attempt.healed,
71
+ explanation: attempt.explanation || "No explanation",
72
+ });
73
+
74
+ if (attempt.healed) {
75
+ // Goal met!
76
+ console.log(chalk.green(` ✅ Goal achieved on iteration ${iteration}`));
77
+
78
+ if (this.researcher) {
79
+ await this.researcher.recordAttempt({
80
+ errorMessage, filePath,
81
+ success: true,
82
+ explanation: attempt.explanation,
83
+ }).catch(() => {});
84
+ }
85
+
86
+ if (this.logger) {
87
+ this.logger.info("goal.success", `Goal achieved: ${this.goal}`, { iteration, explanation: attempt.explanation });
88
+ }
89
+
90
+ return {
91
+ success: true,
92
+ iteration,
93
+ explanation: attempt.explanation,
94
+ attempts: this._attempts,
95
+ ...attempt,
96
+ };
97
+ }
98
+
99
+ // Record failure
100
+ console.log(chalk.yellow(` ↩️ Iteration ${iteration} failed: ${attempt.explanation?.slice(0, 80)}`));
101
+
102
+ if (this.researcher) {
103
+ await this.researcher.recordAttempt({
104
+ errorMessage, filePath,
105
+ success: false,
106
+ explanation: attempt.explanation,
107
+ }).catch(() => {});
108
+
109
+ // Deep research after 2nd failure — bring in RESEARCH_MODEL
110
+ if (iteration >= 2) {
111
+ console.log(chalk.magenta(` 🔬 Triggering deep research after ${iteration} failures...`));
112
+ const research = await this.researcher.research(errorMessage, context);
113
+ if (research) {
114
+ console.log(chalk.gray(` 🔬 Research insight: ${research.slice(0, 100)}`));
115
+ }
116
+ }
117
+ }
118
+
119
+ if (this.logger) {
120
+ this.logger.warn("goal.attempt_failed", `Iteration ${iteration} failed`, { iteration, explanation: attempt.explanation });
121
+ }
122
+ }
123
+
124
+ // All iterations exhausted
125
+ console.log(chalk.red(` 🛑 Goal not achieved after ${this.maxIterations} iterations`));
126
+
127
+ if (this.logger) {
128
+ this.logger.error("goal.failed", `Goal failed: ${this.goal}`, { attempts: this._attempts.length });
129
+ }
130
+
131
+ return {
132
+ success: false,
133
+ iteration: this.maxIterations,
134
+ explanation: `Failed after ${this.maxIterations} iterations`,
135
+ attempts: this._attempts,
136
+ };
137
+ }
138
+ }
139
+
140
+ module.exports = { GoalLoop };
@@ -0,0 +1,120 @@
1
+ const chalk = require("chalk");
2
+ const { aiCall } = require("../core/ai-client");
3
+ const { getModel } = require("../core/models");
4
+
5
+ /**
6
+ * Research Agent — deep research + learning from experience.
7
+ *
8
+ * Uses RESEARCH_MODEL for deep analysis of errors and solutions.
9
+ * Stores everything in the brain so wolverine gets smarter over time.
10
+ *
11
+ * Loop prevention:
12
+ * - Before fixing, checks brain for past attempts on the same error
13
+ * - If past fix failed, provides that context so agent tries something different
14
+ * - Stores both successes AND failures
15
+ */
16
+
17
+ class ResearchAgent {
18
+ constructor(options = {}) {
19
+ this.brain = options.brain;
20
+ this.logger = options.logger;
21
+ this.redactor = options.redactor;
22
+ }
23
+
24
+ /**
25
+ * Check if we've seen this error before. Returns past attempts.
26
+ */
27
+ async checkHistory(errorMessage) {
28
+ if (!this.brain || !this.brain._initialized) return null;
29
+
30
+ const fixes = await this.brain.recall(errorMessage, { topK: 3, namespace: "fixes" });
31
+ const errors = await this.brain.recall(errorMessage, { topK: 3, namespace: "errors" });
32
+ const learnings = await this.brain.recall(errorMessage, { topK: 2, namespace: "learnings" });
33
+
34
+ const total = fixes.length + errors.length + learnings.length;
35
+ if (total === 0) return null;
36
+
37
+ console.log(chalk.gray(` 🔍 Research: ${fixes.length} past fixes, ${errors.length} past errors, ${learnings.length} learnings`));
38
+
39
+ return {
40
+ hasPastAttempts: true,
41
+ fixes: fixes.map(r => r.text),
42
+ errors: errors.map(r => r.text),
43
+ learnings: learnings.map(r => r.text),
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Record a fix attempt (success or failure).
49
+ */
50
+ async recordAttempt({ errorMessage, filePath, fix, success, explanation }) {
51
+ if (!this.brain || !this.brain._initialized) return;
52
+
53
+ const safeError = this.redactor ? this.redactor.redact(errorMessage) : errorMessage;
54
+ const safeExplanation = this.redactor ? this.redactor.redact(explanation || fix || "") : (explanation || fix || "");
55
+
56
+ const namespace = success ? "fixes" : "errors";
57
+ const prefix = success ? "FIXED" : "FAILED";
58
+ const text = `${prefix}: ${safeError} in ${filePath}. ${success ? "Solution" : "Attempted"}: ${safeExplanation}`;
59
+
60
+ await this.brain.remember(namespace, text, { type: success ? "fix-success" : "fix-failure", file: filePath });
61
+ console.log(chalk.gray(` 🧠 ${success ? "✅" : "❌"} Recorded ${prefix.toLowerCase()} for ${safeError.slice(0, 50)}`));
62
+ }
63
+
64
+ /**
65
+ * Deep research using RESEARCH_MODEL. Called when normal fixes fail.
66
+ * Stores findings in brain for future reference.
67
+ */
68
+ async research(errorMessage, context) {
69
+ const safeError = this.redactor ? this.redactor.redact(errorMessage) : errorMessage;
70
+
71
+ console.log(chalk.magenta(` 🔬 Deep research (${getModel("research")})...`));
72
+
73
+ try {
74
+ const result = await aiCall({
75
+ model: getModel("research"),
76
+ systemPrompt: "You are a Node.js debugging researcher. Given an error and context of failed fix attempts, research the root cause deeply and provide a specific, actionable solution. Include exact code changes. If the previous fix attempts failed, explain WHY they failed and what to do differently.",
77
+ userPrompt: `Error: ${safeError}\n\n${context || "No previous attempts."}`,
78
+ maxTokens: 1024,
79
+ category: "research",
80
+ });
81
+
82
+ const findings = result.content || "";
83
+
84
+ // Store research in brain
85
+ if (this.brain && this.brain._initialized && findings.length > 20) {
86
+ await this.brain.remember("learnings", `Research for "${safeError.slice(0, 60)}": ${findings.slice(0, 500)}`, { type: "deep-research" });
87
+ this.brain.store.save();
88
+ }
89
+
90
+ const tokens = (result.usage?.prompt_tokens || result.usage?.input_tokens || 0)
91
+ + (result.usage?.completion_tokens || result.usage?.output_tokens || 0);
92
+ console.log(chalk.magenta(` 🔬 Research complete (${tokens} tokens)`));
93
+
94
+ return findings;
95
+ } catch (err) {
96
+ console.log(chalk.yellow(` Research failed: ${err.message}`));
97
+ return null;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Build full context for the agent — past attempts + research.
103
+ */
104
+ async buildFixContext(errorMessage) {
105
+ const parts = [];
106
+
107
+ const history = await this.checkHistory(errorMessage);
108
+ if (history && history.hasPastAttempts) {
109
+ parts.push("## Past Attempts (from brain)");
110
+ if (history.fixes.length > 0) parts.push("Successful fixes for similar errors:\n" + history.fixes.join("\n"));
111
+ if (history.errors.length > 0) parts.push("Failed attempts (DO NOT repeat these):\n" + history.errors.join("\n"));
112
+ if (history.learnings.length > 0) parts.push("Research findings:\n" + history.learnings.join("\n"));
113
+ parts.push("\nIMPORTANT: Use a DIFFERENT approach from any failed attempts listed above.");
114
+ }
115
+
116
+ return parts.join("\n");
117
+ }
118
+ }
119
+
120
+ module.exports = { ResearchAgent };
@@ -0,0 +1,176 @@
1
+ const chalk = require("chalk");
2
+ const { AgentEngine } = require("./agent-engine");
3
+ const { getModel } = require("../core/models");
4
+
5
+ /**
6
+ * Sub-Agent System — spawn specialized agents for divide-and-conquer.
7
+ *
8
+ * claw-code pattern:
9
+ * - Parent agent identifies sub-tasks
10
+ * - Each sub-agent gets: restricted tools, specific model, focused prompt
11
+ * - Sub-agents run in parallel (Promise.all)
12
+ * - Results aggregated back to parent
13
+ *
14
+ * Built-in agent types:
15
+ * - explore: Read-only. Investigates codebase, finds relevant files.
16
+ * - plan: Read-only. Analyzes problem, proposes a fix strategy.
17
+ * - fix: Read+write. Applies a specific, targeted fix.
18
+ * - verify: Read-only. Checks if a fix actually works.
19
+ * - research: Read-only. Searches brain + web for solutions.
20
+ * - security: Read-only. Audits code for vulnerabilities.
21
+ * - database: Read+write. Handles database-related fixes (uses SQL skill).
22
+ */
23
+
24
+ // Tool restrictions per agent type (claw-code: allowed_tools_for_subagent)
25
+ const AGENT_TOOL_SETS = {
26
+ explore: ["read_file", "glob_files", "grep_code", "git_log", "git_diff", "done"],
27
+ plan: ["read_file", "glob_files", "grep_code", "search_brain", "done"],
28
+ fix: ["read_file", "write_file", "edit_file", "glob_files", "grep_code", "done"],
29
+ verify: ["read_file", "glob_files", "grep_code", "bash_exec", "done"],
30
+ research: ["read_file", "grep_code", "web_fetch", "search_brain", "done"],
31
+ security: ["read_file", "glob_files", "grep_code", "done"],
32
+ database: ["read_file", "write_file", "edit_file", "glob_files", "grep_code", "bash_exec", "done"],
33
+ };
34
+
35
+ // Default model + budget per agent type
36
+ const AGENT_CONFIGS = {
37
+ explore: { model: "reasoning", maxTurns: 5, maxTokens: 10000 },
38
+ plan: { model: "reasoning", maxTurns: 3, maxTokens: 8000 },
39
+ fix: { model: "coding", maxTurns: 5, maxTokens: 15000 },
40
+ verify: { model: "reasoning", maxTurns: 3, maxTokens: 5000 },
41
+ research: { model: "research", maxTurns: 3, maxTokens: 10000 },
42
+ security: { model: "audit", maxTurns: 3, maxTokens: 8000 },
43
+ database: { model: "coding", maxTurns: 5, maxTokens: 15000 },
44
+ };
45
+
46
+ // System prompts per agent type
47
+ const AGENT_PROMPTS = {
48
+ 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.",
49
+ plan: "You are a Planner agent. Your job is to analyze the problem and propose a fix strategy. Read the relevant files, understand the root cause, and describe step-by-step what needs to change. Do NOT make changes.",
50
+ fix: "You are a Fixer agent. You receive a specific fix plan. Execute it precisely — edit only the files mentioned, make only the changes described. Use edit_file for surgical changes.",
51
+ verify: "You are a Verifier agent. Check if a fix actually works. Read the modified files, look for issues, run tests if available. Report whether the fix is correct.",
52
+ research: "You are a Research agent. Search the brain for past fixes to similar errors, and search the web for solutions. Report your findings.",
53
+ security: "You are a Security agent. Audit the code for vulnerabilities: SQL injection, XSS, path traversal, hardcoded secrets, missing input validation. Report all findings.",
54
+ database: "You are a Database agent. Handle database-related issues: schema problems, query errors, connection failures, migration issues. You have access to the SQL skill.",
55
+ };
56
+
57
+ /**
58
+ * Spawn a single sub-agent.
59
+ *
60
+ * @param {string} type — agent type (explore, plan, fix, verify, research, security, database)
61
+ * @param {string} task — what this agent should do
62
+ * @param {object} options — { sandbox, logger, cwd, mcp, brainContext }
63
+ * @returns {{ success, summary, filesModified, turnCount, totalTokens, type }}
64
+ */
65
+ async function spawnAgent(type, task, options = {}) {
66
+ const config = AGENT_CONFIGS[type] || AGENT_CONFIGS.explore;
67
+ const allowedTools = AGENT_TOOL_SETS[type] || AGENT_TOOL_SETS.explore;
68
+ const systemPrompt = AGENT_PROMPTS[type] || AGENT_PROMPTS.explore;
69
+
70
+ console.log(chalk.magenta(` 🤖 [${type}] Spawning sub-agent (${getModel(config.model)}, ${config.maxTurns} turns)`));
71
+
72
+ if (options.logger) {
73
+ options.logger.info("agent.spawn", `Sub-agent [${type}]: ${task.slice(0, 80)}`, {
74
+ type, model: getModel(config.model), maxTurns: config.maxTurns,
75
+ });
76
+ }
77
+
78
+ const agent = new AgentEngine({
79
+ sandbox: options.sandbox,
80
+ logger: options.logger,
81
+ cwd: options.cwd || process.cwd(),
82
+ mcp: options.mcp,
83
+ maxTurns: config.maxTurns,
84
+ maxTokens: config.maxTokens,
85
+ });
86
+
87
+ // Override the system prompt for this agent type
88
+ const result = await agent.run({
89
+ errorMessage: task,
90
+ stackTrace: "",
91
+ primaryFile: "N/A",
92
+ sourceCode: `// Sub-agent type: ${type}\n// ${systemPrompt}`,
93
+ brainContext: options.brainContext || "",
94
+ });
95
+
96
+ console.log(chalk.magenta(` 🤖 [${type}] ${result.success ? "✅" : "❌"} ${(result.summary || "").slice(0, 80)}`));
97
+
98
+ return {
99
+ ...result,
100
+ type,
101
+ model: getModel(config.model),
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Spawn multiple sub-agents in parallel.
107
+ *
108
+ * @param {Array<{ type, task }>} agents — agents to spawn
109
+ * @param {object} options — shared options for all agents
110
+ * @returns {Array} — results from all agents
111
+ */
112
+ async function spawnParallel(agents, options = {}) {
113
+ console.log(chalk.magenta(`\n 🤖 Spawning ${agents.length} sub-agents in parallel...`));
114
+
115
+ if (options.logger) {
116
+ options.logger.info("agent.spawn_parallel", `Spawning ${agents.length} sub-agents`, {
117
+ agents: agents.map(a => a.type),
118
+ });
119
+ }
120
+
121
+ const promises = agents.map(({ type, task }) =>
122
+ spawnAgent(type, task, options).catch(err => ({
123
+ success: false,
124
+ summary: `[${type}] Error: ${err.message}`,
125
+ filesModified: [],
126
+ turnCount: 0,
127
+ totalTokens: 0,
128
+ type,
129
+ }))
130
+ );
131
+
132
+ const results = await Promise.all(promises);
133
+
134
+ const totalTokens = results.reduce((sum, r) => sum + (r.totalTokens || 0), 0);
135
+ const successes = results.filter(r => r.success).length;
136
+
137
+ console.log(chalk.magenta(` 🤖 Parallel complete: ${successes}/${results.length} succeeded, ${totalTokens} tokens total`));
138
+
139
+ return results;
140
+ }
141
+
142
+ /**
143
+ * Common workflow: explore → plan → fix → verify
144
+ * Used by the goal loop for complex repairs.
145
+ */
146
+ async function exploreAndFix(task, options = {}) {
147
+ // Step 1: Explore — find relevant files
148
+ const exploration = await spawnAgent("explore", `Investigate: ${task}`, options);
149
+
150
+ // Step 2: Plan — propose a fix using exploration results
151
+ const planTask = `Based on exploration:\n${exploration.summary}\n\nPlan a fix for: ${task}`;
152
+ const plan = await spawnAgent("plan", planTask, options);
153
+
154
+ // Step 3: Fix — execute the plan
155
+ const fixTask = `Execute this fix plan:\n${plan.summary}\n\nOriginal problem: ${task}`;
156
+ const fix = await spawnAgent("fix", fixTask, options);
157
+
158
+ return {
159
+ exploration,
160
+ plan,
161
+ fix,
162
+ success: fix.success,
163
+ summary: fix.summary,
164
+ filesModified: fix.filesModified || [],
165
+ totalTokens: (exploration.totalTokens || 0) + (plan.totalTokens || 0) + (fix.totalTokens || 0),
166
+ };
167
+ }
168
+
169
+ module.exports = {
170
+ spawnAgent,
171
+ spawnParallel,
172
+ exploreAndFix,
173
+ AGENT_TOOL_SETS,
174
+ AGENT_CONFIGS,
175
+ AGENT_PROMPTS,
176
+ };