wolverine-ai 3.0.1 → 3.1.1
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/.env.example +20 -0
- package/CLAUDE.md +146 -0
- package/README.md +2 -0
- package/package.json +4 -5
- 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/.env.example
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Wolverine — Secrets Only
|
|
2
|
+
# Copy to .env.local and fill in your keys.
|
|
3
|
+
# All other settings are in server/config/settings.json
|
|
4
|
+
|
|
5
|
+
# ── API Keys ─────────────────────────────────────────────────────
|
|
6
|
+
# Your OpenAI API key (required)
|
|
7
|
+
OPENAI_API_KEY=
|
|
8
|
+
ANTHROPIC_API_KEY=
|
|
9
|
+
# ── Dashboard Admin Key (make your own) ──────────────────────────────────────────
|
|
10
|
+
# Required for the agent command interface on the dashboard.
|
|
11
|
+
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
12
|
+
WOLVERINE_ADMIN_KEY=
|
|
13
|
+
|
|
14
|
+
# ── Custom Secrets ───────────────────────────────────────────────
|
|
15
|
+
# Add any secret here — wolverine automatically redacts its value
|
|
16
|
+
# from all AI calls, logs, brain storage, and dashboard output.
|
|
17
|
+
#
|
|
18
|
+
# DATABASE_URL=postgres://user:pass@host:5432/db
|
|
19
|
+
# JWT_SECRET=my-secret
|
|
20
|
+
# STRIPE_SECRET_KEY=sk_live_xxx
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What This Is
|
|
6
|
+
|
|
7
|
+
Wolverine is a self-healing Node.js server framework. It wraps a server process, catches crashes AND caught 500 errors, diagnoses them with AI (OpenAI or Anthropic), generates fixes, verifies them, and restarts — automatically. Published as `wolverine-ai` on npm (v3.1.0). 65 exports, 83 files, 6 skills.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm start # Run server/index.js under wolverine (self-healing)
|
|
13
|
+
npm run server # Run server/index.js directly (no healing)
|
|
14
|
+
npm run test:pentest # Security scan for secret leakage
|
|
15
|
+
npm run demo:list # List demo scenarios
|
|
16
|
+
npm run demo -- 01 # Run specific demo
|
|
17
|
+
npx wolverine server/index.js # CLI entry point
|
|
18
|
+
wolverine --info # System detection
|
|
19
|
+
wolverine --update # Safe framework upgrade
|
|
20
|
+
wolverine --backup "reason" # Create server snapshot
|
|
21
|
+
wolverine --list-backups # Show all snapshots
|
|
22
|
+
wolverine --rollback <id> # Restore specific backup
|
|
23
|
+
wolverine --rollback-latest # Restore most recent
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
No standard test runner — demos in `tests/fixtures/` serve as integration tests.
|
|
27
|
+
|
|
28
|
+
## Architecture
|
|
29
|
+
|
|
30
|
+
### Heal Pipeline (src/core/wolverine.js)
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Error detected (crash OR caught 500 via IPC)
|
|
34
|
+
→ Empty stderr? → Just restart, no AI ($0.00)
|
|
35
|
+
→ Parse error → classify type → redact secrets
|
|
36
|
+
→ Injection scan (skip if < 20 chars)
|
|
37
|
+
→ Loop guard: same error failed 3+ times in 10min? → File bug report, stop
|
|
38
|
+
→ Rate limit: 5 heals per 5min max
|
|
39
|
+
→ Operational fix (zero tokens):
|
|
40
|
+
missing_module → deps.diagnose() → npm install
|
|
41
|
+
EADDRINUSE → kill stale process
|
|
42
|
+
ENOENT → create missing file
|
|
43
|
+
EACCES → chmod
|
|
44
|
+
→ Token budget by complexity: simple=20K, moderate=50K, complex=100K
|
|
45
|
+
→ Goal Loop (3 iterations):
|
|
46
|
+
1. Fast path: CODING_MODEL, JSON with code+commands, backup diff context
|
|
47
|
+
2. Agent: dynamic prompt (400 tokens simple, 1200 complex), 18 tools
|
|
48
|
+
3. Sub-agents: explore→plan→fix (Haiku triage, Sonnet/Opus fix only)
|
|
49
|
+
→ Verify: syntax → boot probe (route probe skipped — ErrorMonitor is safety net)
|
|
50
|
+
→ Success: retryCount reset, record to brain with full context
|
|
51
|
+
→ Fail: rollback, brain records "DO NOT REPEAT", next iteration
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`heal()` wraps `_healImpl()` with 5-minute `Promise.race` timeout.
|
|
55
|
+
|
|
56
|
+
### IPC Error Chain (caught 500s without crash)
|
|
57
|
+
|
|
58
|
+
1. **error-hook.js** — preloaded via `--require`, patches Fastify/Express for IPC. WeakSet dedup.
|
|
59
|
+
2. **runner.js** — spawns child with `stdio: ["inherit","inherit","pipe","ipc"]`, listens `child.on("message")`
|
|
60
|
+
3. **error-monitor.js** — tracks errors per normalized route (`/api/users/123` → `/api/users/:id`), threshold=1, 60s cooldown. Health check failures also trigger heal.
|
|
61
|
+
|
|
62
|
+
### AI Client (src/core/ai-client.js)
|
|
63
|
+
|
|
64
|
+
Dual provider: OpenAI + Anthropic. Auto-detected from model name (`claude-*` → Anthropic). All responses normalized to `{content, toolCalls, usage}`. **Anthropic prompt caching** — system prompt marked `cache_control: ephemeral`, 90% cheaper on repeat calls. Per-model output limits with 10% buffer. Every call tracked: latencyMs, success/failure, tokens, cost.
|
|
65
|
+
|
|
66
|
+
Embeddings always use OpenAI (Anthropic has no embedding API).
|
|
67
|
+
|
|
68
|
+
### Agent (src/agent/agent-engine.js)
|
|
69
|
+
|
|
70
|
+
**Dynamic system prompt**: simple errors (TypeError/ReferenceError) get 400-token compact prompt with 7 tools. Complex errors get full prompt with all 18 tools + strategy table.
|
|
71
|
+
|
|
72
|
+
18 tools: file (read/write/edit/glob/grep/list_dir/move_file), shell (bash_exec/git_log/git_diff), database (inspect_db/run_db_fix), diagnostics (check_port/check_env), deps (audit_deps/check_migration), research (web_fetch), control (done).
|
|
73
|
+
|
|
74
|
+
**Cost optimizations**: zero-cost structural compaction (no LLM, extracts signals from messages), tool result truncation (4K cap), token estimation (`text.length/4`), pre/post tool hooks (`.wolverine/hooks.json`), error-graceful tools (`[ERROR]` results not thrown).
|
|
75
|
+
|
|
76
|
+
**Protected paths**: agent cannot modify `src/`, `bin/`, `tests/`, `node_modules/`, `.env`, `package.json`. Only `server/` is editable.
|
|
77
|
+
|
|
78
|
+
### Provider Config (server/config/settings.json)
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{ "provider": "hybrid", "openai_settings": {...}, "anthropic_settings": {...}, "hybrid_settings": {...} }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Config loader reads `{provider}_settings`. Env vars override per-role. Missing config sections auto-patched on startup via `_ensureDefaults()`.
|
|
85
|
+
|
|
86
|
+
### Brain (src/brain/vector-store.js + brain.js)
|
|
87
|
+
|
|
88
|
+
IVF-indexed vector store: k-means++ clustering, BM25 keyword search, binary persistence. 60 seed docs. Benchmarks: 100=0.2ms, 10K=4.4ms, 50K=23.7ms.
|
|
89
|
+
|
|
90
|
+
**Namespace isolation**: error heals search only `errors/fixes/learnings/functions` — seed docs (20K tokens) excluded unless query is about wolverine itself. Function map hash check skips re-embedding if unchanged.
|
|
91
|
+
|
|
92
|
+
### Backup (src/backup/backup-manager.js)
|
|
93
|
+
|
|
94
|
+
All backups in `~/.wolverine-safe-backups/` (outside project, survives git pull/npm install). States: UNSTABLE → VERIFIED → STABLE (30min). Protected files never rolled back: `settings.json`, `db.js`, `.env.local`.
|
|
95
|
+
|
|
96
|
+
### Skills (src/skills/ — 6 files)
|
|
97
|
+
|
|
98
|
+
- **sql.js** — injection prevention, SafeDB, idempotency guard
|
|
99
|
+
- **deps.js** — dependency diagnosis (zero tokens), npm audit, migration paths
|
|
100
|
+
- **update.js** — safe framework upgrade, emergency backup, brain seed merge
|
|
101
|
+
- **backup.js** — agent-friendly backup/rollback with CLI commands
|
|
102
|
+
- **loop-guard.js** — infinite loop detection, bug reports, process dedup (PID file)
|
|
103
|
+
- **skill-registry.js** — auto-discovery + token-scored matching
|
|
104
|
+
|
|
105
|
+
### Telemetry (src/platform/)
|
|
106
|
+
|
|
107
|
+
Heartbeats every 60s. Stable instance ID (persisted to `.wolverine/instance-id`). Cumulative usage from disk (not session-only). `byModel` with latency/success/tokens-per-sec/cost-per-call. `byProvider` aggregated. Auto-update checks every 5min, selective git checkout (never touches `server/`).
|
|
108
|
+
|
|
109
|
+
## Key Constraints
|
|
110
|
+
|
|
111
|
+
- **Server port is always 3000.** Any other port breaks APIs. Kill 3000 and bind there.
|
|
112
|
+
- **Dashboard on PORT+1** (3001).
|
|
113
|
+
- **heal() has 5-minute timeout.** `Promise.race` recovery.
|
|
114
|
+
- **Global rate limit: 5 heals per 5 minutes.**
|
|
115
|
+
- **Loop guard: 3 failed heals on same error in 10min → stop + bug report.**
|
|
116
|
+
- **Error threshold: 1** — single 500 triggers heal. 60s cooldown per route.
|
|
117
|
+
- **Empty stderr → just restart, no AI.** Prevents token burn on signal kills.
|
|
118
|
+
- **bash_exec: 30s default, 60s cap.**
|
|
119
|
+
- **Process dedup via PID file.** Kills old process on startup.
|
|
120
|
+
- **Both API keys needed for hybrid mode** — OPENAI_API_KEY for embeddings.
|
|
121
|
+
- **Auto-update: selective git checkout** — only updates `src/`, `bin/`, `package.json`. Never touches `server/`.
|
|
122
|
+
- **Rollback protects:** `settings.json`, `db.js`, `.env.local` never overwritten.
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
- **Secrets:** `.env.local` (OPENAI_API_KEY, ANTHROPIC_API_KEY, WOLVERINE_ADMIN_KEY)
|
|
127
|
+
- **Settings:** `server/config/settings.json` — provider, 3 model presets, cluster, telemetry, rate limits, health checks, autoUpdate, errorMonitor
|
|
128
|
+
- **10 model slots:** reasoning, coding, chat, tool, classifier, audit, compacting, research, embedding
|
|
129
|
+
- **Config priority:** env vars > `{provider}_settings` > defaults
|
|
130
|
+
|
|
131
|
+
## Files That Matter Most
|
|
132
|
+
|
|
133
|
+
| File | Why |
|
|
134
|
+
|------|-----|
|
|
135
|
+
| `src/core/wolverine.js` | Heal pipeline, operational fixes, goal loop, token budgets |
|
|
136
|
+
| `src/core/runner.js` | Process manager, IPC, health/error monitors, loop guard, auto-update |
|
|
137
|
+
| `src/core/ai-client.js` | Dual provider, prompt caching, output limits, latency tracking |
|
|
138
|
+
| `src/agent/agent-engine.js` | Dynamic prompt, 18 tools, zero-cost compaction, hooks |
|
|
139
|
+
| `src/agent/sub-agents.js` | Dynamic token budgets, Haiku triage, restricted tool sets |
|
|
140
|
+
| `src/core/verifier.js` | Syntax + boot probe, error classification comparison |
|
|
141
|
+
| `src/brain/vector-store.js` | IVF + BM25 + binary persistence |
|
|
142
|
+
| `src/brain/brain.js` | 60 seed docs, namespace isolation, function map hash |
|
|
143
|
+
| `src/skills/loop-guard.js` | Infinite loop detection, bug reports, process dedup |
|
|
144
|
+
| `src/skills/update.js` | Safe upgrade, emergency backup, brain seed merge |
|
|
145
|
+
| `src/platform/auto-update.js` | Version lock, dep verification, max 1 attempt per boot |
|
|
146
|
+
| `server/config/settings.json` | Provider selection, 3 model presets, all config |
|
package/README.md
CHANGED
|
@@ -450,6 +450,8 @@ Three layers prevent token waste:
|
|
|
450
450
|
|
|
451
451
|
| Technique | What it does | Cost |
|
|
452
452
|
|-----------|-------------|------|
|
|
453
|
+
| **Dynamic system prompt** | Simple errors get 400-token prompt with 7 tools. Complex get 1200 with 18 + strategy | 50% on 70% of heals |
|
|
454
|
+
| **Brain namespace isolation** | Seed docs (20K tokens) excluded from error heals — only searched for wolverine queries | 50% context reduction |
|
|
453
455
|
| **Prompt caching** | Anthropic system prompt cached server-side — 90% cheaper on repeat calls | 12-16K tokens saved per heal |
|
|
454
456
|
| **Tool result truncation** | Tool output capped at 4K chars — prevents context blowup from large reads | Up to 30K saved per turn |
|
|
455
457
|
| **Zero-cost compaction** | Extracts structural signals (tools, files, errors) from history — no LLM call | $0.00 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
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": {
|
|
@@ -49,10 +49,9 @@
|
|
|
49
49
|
"src/",
|
|
50
50
|
"server/",
|
|
51
51
|
"examples/",
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"README.md"
|
|
52
|
+
"README.md",
|
|
53
|
+
"CLAUDE.md",
|
|
54
|
+
".env.example"
|
|
56
55
|
],
|
|
57
56
|
"dependencies": {
|
|
58
57
|
"@anthropic-ai/sdk": "^0.82.0",
|
|
@@ -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
|
|