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 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.0.1",
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
- "PLATFORM.md",
53
- "TELEMETRY.md",
54
- "SERVER_BEST_PRACTICES.md",
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
- const systemPrompt = `You are Wolverine, an autonomous Node.js server repair agent. A server has an error and you must diagnose and fix it.
355
-
356
- You are NOT just a code editor — you are a full server doctor. Errors can be code bugs, missing dependencies, database problems, misplaced files, configuration issues, port conflicts, permission errors, corrupted state, or environment problems. Use your tools to investigate the ACTUAL root cause before attempting a fix.
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
  /**
@@ -33,19 +33,38 @@ const AGENT_TOOL_SETS = {
33
33
  };
34
34
 
35
35
  // Default model + budget per agent type
36
- // Cost optimization: triage agents use cheap models (classifier slot = Haiku),
37
- // only the fixer needs the expensive coding model (Sonnet/Opus).
38
- // This cuts sub-agent cost by ~90% (6 Haiku calls vs 6 Sonnet calls).
39
- const AGENT_CONFIGS = {
40
- explore: { model: "classifier", maxTurns: 5, maxTokens: 15000 }, // Haiku — just reading
41
- plan: { model: "classifier", maxTurns: 3, maxTokens: 10000 }, // Haiku — simple planning
42
- fix: { model: "coding", maxTurns: 5, maxTokens: 50000 }, // Sonnet/Opus — needs reasoning
43
- verify: { model: "classifier", maxTurns: 3, maxTokens: 8000 }, // Haiku — just checking
44
- research: { model: "classifier", maxTurns: 3, maxTokens: 10000 }, // Haiku — summarization
45
- security: { model: "audit", maxTurns: 3, maxTokens: 8000 }, // Haiku — pattern matching
46
- database: { model: "coding", maxTurns: 5, maxTokens: 50000 }, // Sonnet/Opus — needs reasoning
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.",
@@ -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 (replace old "functions" entries)
307
- await this._embedFunctionMap();
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
- // Two-tier recall: keyword first, semantic fallback
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 memories = await this.recall(errorMessage, { topK: 5, minScore: 0.3 });
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) {
@@ -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 (this._lastBackupId && this.running) {
648
- this.backupManager.markStable(this._lastBackupId);
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 ${this._lastBackupId} promoted to stable`, { backupId: this._lastBackupId });
657
+ this.logger.info(EVENT_TYPES.BACKUP_STABLE, `Backup ${backupId} promoted to stable`, { backupId });
655
658
  }
656
659
  }, STABILITY_THRESHOLD_MS);
657
660
  }
@@ -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, 100)}`
263
+ `- Attempt ${a.iteration} (${a.mode}): ${a.explanation?.slice(0, 50)}`
264
264
  ).join("\n") + "\n";
265
265
  }
266
266