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 +1 -1
- package/src/agent/agent-engine.js +4 -0
- package/src/core/ai-client.js +18 -4
- package/src/core/runner.js +11 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "3.9.
|
|
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);
|
package/src/core/ai-client.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
624
|
-
|
|
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
|
|
package/src/core/runner.js
CHANGED
|
@@ -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(),
|
|
299
|
-
},
|
|
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
|
-
|
|
309
|
-
|
|
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
|
-
|
|
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
|
|