wolverine-ai 3.9.0 → 3.9.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "3.9.0",
3
+ "version": "3.9.2",
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": {
@@ -446,6 +446,10 @@ class AgentEngine {
446
446
  return { success: false, summary: "Token budget exhausted", filesModified: this.filesModified, turnCount: this.turnCount, totalTokens: this.totalTokens };
447
447
  }
448
448
 
449
+ if (!response.choices || !response.choices[0]) {
450
+ console.log(chalk.red(` ⚠️ AI returned no choices: ${JSON.stringify(response).slice(0, 200)}`));
451
+ return { success: false, summary: "AI returned empty response", filesModified: this.filesModified, turnCount: this.turnCount, totalTokens: this.totalTokens };
452
+ }
449
453
  const choice = response.choices[0];
450
454
  const assistantMessage = choice.message || choice;
451
455
  this.messages.push(assistantMessage);
@@ -615,13 +615,27 @@ ${backupSourceCode ? `## Last Known Working Version\n\`\`\`javascript\n${backupS
615
615
  Include both if needed, or just one.`;
616
616
 
617
617
  const result = await aiCall({ model, systemPrompt, userPrompt, maxTokens: 2048, category: "coding" });
618
- const content = result.content;
619
- const cleaned = content.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "");
618
+ const content = (result.content || "").trim();
619
+
620
+ // Strip thinking tags (Gemma), markdown fences, and any prefix text
621
+ let cleaned = content
622
+ .replace(/<\|channel>.*?<channel\|>/gs, "")
623
+ .replace(/<\|think\|>[\s\S]*?<\|\/think\|>/g, "")
624
+ .replace(/^```(?:json)?\s*/gm, "")
625
+ .replace(/```\s*$/gm, "")
626
+ .trim();
627
+
628
+ // Extract JSON object from response
629
+ const jsonMatch = cleaned.match(/\{[\s\S]*"(?:explanation|changes|commands)"[\s\S]*\}/);
630
+ if (jsonMatch) cleaned = jsonMatch[0];
620
631
 
621
632
  try {
622
633
  return JSON.parse(cleaned);
623
- } catch (parseErr) {
624
- throw new Error(`Failed to parse AI response as JSON.\nRaw response:\n${content}`);
634
+ } catch {
635
+ // Last resort: try to find any JSON object
636
+ const anyJson = cleaned.match(/\{[\s\S]*\}/);
637
+ if (anyJson) try { return JSON.parse(anyJson[0]); } catch {}
638
+ throw new Error(`AI response was not valid JSON`);
625
639
  }
626
640
  }
627
641
 
@@ -289,14 +289,17 @@ class WolverineRunner {
289
289
  if (this.child) {
290
290
  const oldChild = this.child;
291
291
  this.child = null;
292
+ let spawned = false;
292
293
 
293
294
  // Wait for old process to actually exit before spawning new one
294
295
  const onExit = () => {
296
+ if (spawned) return; // Prevent double-spawn from exit + force-kill timeout
297
+ spawned = true;
295
298
  // Give port time to fully release (TIME_WAIT)
296
299
  setTimeout(() => {
297
300
  this._ensurePortFree();
298
- setTimeout(() => this._spawn(), 200);
299
- }, 300);
301
+ setTimeout(() => this._spawn(), 500);
302
+ }, 500);
300
303
  };
301
304
 
302
305
  oldChild.removeAllListeners("exit");
@@ -305,8 +308,10 @@ class WolverineRunner {
305
308
 
306
309
  // Force kill if it doesn't exit in 3s
307
310
  setTimeout(() => {
308
- this._killProcessTree(oldChild.pid, "SIGKILL");
309
- onExit();
311
+ if (!spawned) {
312
+ this._killProcessTree(oldChild.pid, "SIGKILL");
313
+ onExit();
314
+ }
310
315
  }, 3000);
311
316
  } else {
312
317
  this._ensurePortFree();
@@ -579,7 +584,8 @@ class WolverineRunner {
579
584
  this._healStatus = null;
580
585
  // Clear pending errors — the heal fixed the root cause, stale errors are irrelevant
581
586
  this._pendingErrorHeal = null;
582
- this._spawn();
587
+ // Use restart() to properly kill old child before spawning — prevents EADDRINUSE
588
+ this.restart();
583
589
  } else {
584
590
  console.log(chalk.red(`\n🐺 Wolverine could not heal: ${result.explanation}`));
585
591