wolverine-ai 6.3.0 → 6.3.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.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": {
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Wolverine Integration Plugin for OpenClaw
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - Brain: semantic memory, project context, and learning
|
|
6
|
-
* - Backup: workspace snapshots and rollback
|
|
7
|
-
* - Healing: error diagnosis and auto-fix pipeline
|
|
8
|
-
* - Health: process monitoring and status reporting
|
|
4
|
+
* Two integration layers:
|
|
9
5
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* 1. TOOLS — 7 wolverine tools injected into the agent toolkit
|
|
7
|
+
* (backup, rollback, brain search/learn, health, self-heal)
|
|
8
|
+
*
|
|
9
|
+
* 2. HOOKS — taps into OpenClaw's 28-hook plugin system for deep
|
|
10
|
+
* error observability and self-healing triggers:
|
|
11
|
+
* - after_tool_call: catch skill/tool failures
|
|
12
|
+
* - agent_end: catch agent crashes and timeouts
|
|
13
|
+
* - before_tool_call: detect and report tool loops
|
|
14
|
+
* - message_sent: catch channel send failures
|
|
15
|
+
* - llm_output: catch provider errors, failovers, billing blocks
|
|
16
|
+
* - subagent_ended: catch subagent failures
|
|
17
|
+
* - gateway_start/gateway_stop: lifecycle tracking
|
|
18
|
+
* - session_end: session error tracking
|
|
12
19
|
*/
|
|
13
20
|
|
|
14
21
|
const path = require("path");
|
|
@@ -16,51 +23,348 @@ const fs = require("fs");
|
|
|
16
23
|
|
|
17
24
|
const PLUGIN_NAME = "wolverine-integration";
|
|
18
25
|
|
|
26
|
+
// ── Error tracking state ────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
const _errorCounts = {
|
|
29
|
+
tool: 0,
|
|
30
|
+
agent: 0,
|
|
31
|
+
channel: 0,
|
|
32
|
+
llm: 0,
|
|
33
|
+
subagent: 0,
|
|
34
|
+
total: 0,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const _recentErrors = []; // last 50 errors for context
|
|
38
|
+
const MAX_RECENT = 50;
|
|
39
|
+
|
|
40
|
+
function _trackError(category, summary, detail) {
|
|
41
|
+
_errorCounts[category] = (_errorCounts[category] || 0) + 1;
|
|
42
|
+
_errorCounts.total++;
|
|
43
|
+
|
|
44
|
+
const entry = {
|
|
45
|
+
category,
|
|
46
|
+
summary: summary?.slice(0, 200),
|
|
47
|
+
timestamp: Date.now(),
|
|
48
|
+
detail: detail?.slice?.(0, 500),
|
|
49
|
+
};
|
|
50
|
+
_recentErrors.push(entry);
|
|
51
|
+
if (_recentErrors.length > MAX_RECENT) _recentErrors.shift();
|
|
52
|
+
|
|
53
|
+
return entry;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Main registration ───────────────────────────────────────────
|
|
57
|
+
|
|
19
58
|
/**
|
|
20
59
|
* Register the wolverine integration with the OpenClaw gateway.
|
|
60
|
+
* Supports both Plugin SDK (api.registerPluginHook) and basic EventEmitter.
|
|
21
61
|
*/
|
|
22
62
|
async function register(gateway, config) {
|
|
23
63
|
const projectRoot = path.resolve(__dirname, "../..");
|
|
24
64
|
|
|
25
|
-
// Register wolverine tools
|
|
65
|
+
// Register wolverine tools
|
|
26
66
|
const tools = buildWolverineTools(projectRoot, config);
|
|
67
|
+
_registerTools(gateway, tools);
|
|
27
68
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
gateway.skills.register(PLUGIN_NAME, {
|
|
34
|
-
description: "Wolverine self-healing integration — backup, brain, and health tools",
|
|
35
|
-
tools,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Hook into gateway events for self-healing
|
|
40
|
-
if (gateway.on) {
|
|
41
|
-
gateway.on("error", (err) => handleGatewayError(err, projectRoot));
|
|
42
|
-
gateway.on("agent:error", (err) => handleAgentError(err, projectRoot));
|
|
43
|
-
gateway.on("skill:error", (err) => handleSkillError(err, projectRoot));
|
|
69
|
+
// Register into OpenClaw Plugin SDK hooks (preferred)
|
|
70
|
+
const api = _getPluginApi(gateway);
|
|
71
|
+
if (api) {
|
|
72
|
+
_registerPluginHooks(api, projectRoot, config);
|
|
73
|
+
console.log("[CLAW] Wolverine plugin hooks registered (Plugin SDK)");
|
|
44
74
|
}
|
|
75
|
+
// Also register EventEmitter hooks (fallback / additional coverage)
|
|
76
|
+
_registerEventHooks(gateway, projectRoot);
|
|
45
77
|
|
|
46
|
-
// Periodic health reporting
|
|
78
|
+
// Periodic health reporting with error stats
|
|
47
79
|
const healthInterval = setInterval(() => {
|
|
48
80
|
reportToWolverine("claw_heartbeat", {
|
|
49
81
|
uptime: process.uptime(),
|
|
50
82
|
memory: process.memoryUsage(),
|
|
83
|
+
errorCounts: { ..._errorCounts },
|
|
51
84
|
timestamp: Date.now(),
|
|
52
85
|
});
|
|
53
86
|
}, 30000);
|
|
54
87
|
|
|
55
|
-
// Cleanup on shutdown
|
|
56
88
|
if (gateway.on) {
|
|
57
89
|
gateway.on("shutdown", () => clearInterval(healthInterval));
|
|
58
90
|
}
|
|
59
91
|
}
|
|
60
92
|
|
|
93
|
+
// ── Plugin SDK Hooks ────────────────────────────────────────────
|
|
94
|
+
|
|
61
95
|
/**
|
|
62
|
-
*
|
|
96
|
+
* Get the Plugin SDK API from the gateway (if available).
|
|
63
97
|
*/
|
|
98
|
+
function _getPluginApi(gateway) {
|
|
99
|
+
// OpenClaw exposes plugin registration via multiple paths
|
|
100
|
+
if (gateway.pluginApi) return gateway.pluginApi;
|
|
101
|
+
if (gateway.api) return gateway.api;
|
|
102
|
+
if (gateway.plugins?.api) return gateway.plugins.api;
|
|
103
|
+
if (typeof gateway.registerPluginHook === "function") return gateway;
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Register all wolverine hooks into the OpenClaw Plugin SDK.
|
|
109
|
+
*/
|
|
110
|
+
function _registerPluginHooks(api, projectRoot, config) {
|
|
111
|
+
const registerHook = api.registerPluginHook || api.register;
|
|
112
|
+
if (typeof registerHook !== "function") return;
|
|
113
|
+
|
|
114
|
+
const reg = (name, handler, priority) => {
|
|
115
|
+
try {
|
|
116
|
+
registerHook.call(api, name, { handler, priority: priority || 100 });
|
|
117
|
+
} catch (e) {
|
|
118
|
+
// Hook not supported in this OpenClaw version — skip silently
|
|
119
|
+
try {
|
|
120
|
+
registerHook.call(api, name, handler);
|
|
121
|
+
} catch {}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// ── after_tool_call: catch skill and tool failures ──────────
|
|
126
|
+
reg("after_tool_call", (event, ctx) => {
|
|
127
|
+
if (!event) return;
|
|
128
|
+
const hasError = event.error || event.status === "error" || event.exitCode;
|
|
129
|
+
|
|
130
|
+
if (hasError) {
|
|
131
|
+
const toolName = event.toolName || event.name || "unknown";
|
|
132
|
+
const errorMsg = event.error?.message || event.error || event.stderr || "Tool failed";
|
|
133
|
+
const entry = _trackError("tool", `Tool ${toolName} failed: ${errorMsg}`);
|
|
134
|
+
|
|
135
|
+
console.error(`[CLAW] Tool error: ${toolName} — ${errorMsg.slice(0, 100)}`);
|
|
136
|
+
|
|
137
|
+
// Skill errors are wrapped as tool errors — detect them
|
|
138
|
+
const isSkill = toolName.includes("skill") || event.source === "skill";
|
|
139
|
+
|
|
140
|
+
reportToWolverine("route_error", {
|
|
141
|
+
path: `claw://tool/${toolName}`,
|
|
142
|
+
method: isSkill ? "SKILL" : "TOOL",
|
|
143
|
+
statusCode: 500,
|
|
144
|
+
message: `${isSkill ? "Skill" : "Tool"} ${toolName}: ${errorMsg}`.slice(0, 500),
|
|
145
|
+
stack: event.error?.stack || event.stderr || null,
|
|
146
|
+
file: event.file || null,
|
|
147
|
+
line: null,
|
|
148
|
+
timestamp: Date.now(),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// ── agent_end: catch agent failures and timeouts ────────────
|
|
154
|
+
reg("agent_end", (event, ctx) => {
|
|
155
|
+
if (!event) return;
|
|
156
|
+
const hasError = event.error || event.status === "error" || event.lifecycle === "error";
|
|
157
|
+
|
|
158
|
+
if (hasError) {
|
|
159
|
+
const reason = event.reason || event.error?.message || "Agent failed";
|
|
160
|
+
const isTimeout = reason.includes("timeout") || event.reason === "timeout";
|
|
161
|
+
const entry = _trackError("agent", `Agent ${isTimeout ? "timeout" : "error"}: ${reason}`);
|
|
162
|
+
|
|
163
|
+
console.error(`[CLAW] Agent ${isTimeout ? "timeout" : "error"}: ${reason.slice(0, 100)}`);
|
|
164
|
+
|
|
165
|
+
reportToWolverine("route_error", {
|
|
166
|
+
path: "claw://agent",
|
|
167
|
+
method: isTimeout ? "TIMEOUT" : "AGENT",
|
|
168
|
+
statusCode: isTimeout ? 408 : 500,
|
|
169
|
+
message: reason.slice(0, 500),
|
|
170
|
+
stack: event.error?.stack || null,
|
|
171
|
+
file: null,
|
|
172
|
+
line: null,
|
|
173
|
+
timestamp: Date.now(),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// ── before_tool_call: detect tool loops ─────────────────────
|
|
179
|
+
reg("before_tool_call", (event, ctx) => {
|
|
180
|
+
if (!event) return;
|
|
181
|
+
|
|
182
|
+
// OpenClaw's loop detection may have already flagged this
|
|
183
|
+
if (event.block || event.loopDetected) {
|
|
184
|
+
const toolName = event.toolName || event.name || "unknown";
|
|
185
|
+
const reason = event.blockReason || event.reason || "Loop detected";
|
|
186
|
+
_trackError("tool", `Tool loop blocked: ${toolName} — ${reason}`);
|
|
187
|
+
|
|
188
|
+
console.warn(`[CLAW] Tool loop blocked: ${toolName} — ${reason}`);
|
|
189
|
+
|
|
190
|
+
reportToWolverine("claw_warning", {
|
|
191
|
+
category: "tool_loop",
|
|
192
|
+
tool: toolName,
|
|
193
|
+
reason,
|
|
194
|
+
timestamp: Date.now(),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// ── message_sent: catch channel send failures ───────────────
|
|
200
|
+
reg("message_sent", (event, ctx) => {
|
|
201
|
+
if (!event) return;
|
|
202
|
+
const failed = event.success === false || event.error;
|
|
203
|
+
|
|
204
|
+
if (failed) {
|
|
205
|
+
const channel = event.channelId || event.channel || "unknown";
|
|
206
|
+
const errorMsg = event.error?.message || event.error || "Send failed";
|
|
207
|
+
_trackError("channel", `Channel ${channel}: ${errorMsg}`);
|
|
208
|
+
|
|
209
|
+
console.error(`[CLAW] Channel send failed: ${channel} — ${errorMsg.slice(0, 100)}`);
|
|
210
|
+
|
|
211
|
+
reportToWolverine("claw_warning", {
|
|
212
|
+
category: "channel_failure",
|
|
213
|
+
channel,
|
|
214
|
+
error: errorMsg.slice(0, 500),
|
|
215
|
+
timestamp: Date.now(),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// ── llm_output: catch provider errors and failovers ─────────
|
|
221
|
+
reg("llm_output", (event, ctx) => {
|
|
222
|
+
if (!event) return;
|
|
223
|
+
const hasError = event.error || event.failover;
|
|
224
|
+
|
|
225
|
+
if (hasError) {
|
|
226
|
+
const provider = event.provider || event.model || "unknown";
|
|
227
|
+
const reason = event.failoverReason || event.error?.message || event.error || "LLM error";
|
|
228
|
+
const status = event.httpCode || event.status;
|
|
229
|
+
_trackError("llm", `LLM ${provider}: ${reason} (${status || "?"})`);
|
|
230
|
+
|
|
231
|
+
// Billing errors (402) are critical — stop healing
|
|
232
|
+
const isBilling = status === 402 || reason.includes("billing");
|
|
233
|
+
// Rate limits (429) are transient
|
|
234
|
+
const isRateLimit = status === 429 || reason.includes("rate_limit");
|
|
235
|
+
|
|
236
|
+
if (isBilling) {
|
|
237
|
+
console.error(`[CLAW] BILLING ERROR: ${provider} — ${reason}`);
|
|
238
|
+
reportToWolverine("claw_critical", {
|
|
239
|
+
category: "billing",
|
|
240
|
+
provider,
|
|
241
|
+
error: reason.slice(0, 500),
|
|
242
|
+
timestamp: Date.now(),
|
|
243
|
+
});
|
|
244
|
+
} else if (!isRateLimit) {
|
|
245
|
+
// Don't spam wolverine with rate limit warnings
|
|
246
|
+
console.warn(`[CLAW] LLM error: ${provider} — ${reason.slice(0, 100)}`);
|
|
247
|
+
reportToWolverine("claw_warning", {
|
|
248
|
+
category: "llm_error",
|
|
249
|
+
provider,
|
|
250
|
+
status,
|
|
251
|
+
error: reason.slice(0, 500),
|
|
252
|
+
timestamp: Date.now(),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// ── subagent_ended: catch subagent failures ─────────────────
|
|
259
|
+
reg("subagent_ended", (event, ctx) => {
|
|
260
|
+
if (!event) return;
|
|
261
|
+
const failed = event.status === "error" || event.status === "timeout" || event.error;
|
|
262
|
+
|
|
263
|
+
if (failed) {
|
|
264
|
+
const agentId = event.subagentId || event.runId || "unknown";
|
|
265
|
+
const errorMsg = event.error?.message || event.error || `Subagent ${event.status}`;
|
|
266
|
+
_trackError("subagent", `Subagent ${agentId}: ${errorMsg}`);
|
|
267
|
+
|
|
268
|
+
console.error(`[CLAW] Subagent failed: ${agentId} — ${errorMsg.slice(0, 100)}`);
|
|
269
|
+
|
|
270
|
+
reportToWolverine("route_error", {
|
|
271
|
+
path: `claw://subagent/${agentId}`,
|
|
272
|
+
method: "SUBAGENT",
|
|
273
|
+
statusCode: 500,
|
|
274
|
+
message: errorMsg.slice(0, 500),
|
|
275
|
+
stack: event.error?.stack || null,
|
|
276
|
+
file: null,
|
|
277
|
+
line: null,
|
|
278
|
+
timestamp: Date.now(),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// ── gateway_start: lifecycle tracking ───────────────────────
|
|
284
|
+
reg("gateway_start", (event, ctx) => {
|
|
285
|
+
console.log("[CLAW] OpenClaw gateway started — wolverine watching");
|
|
286
|
+
reportToWolverine("claw_health", {
|
|
287
|
+
status: "running",
|
|
288
|
+
detail: "gateway_start",
|
|
289
|
+
timestamp: Date.now(),
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// ── gateway_stop: lifecycle tracking ────────────────────────
|
|
294
|
+
reg("gateway_stop", (event, ctx) => {
|
|
295
|
+
console.log("[CLAW] OpenClaw gateway stopping");
|
|
296
|
+
reportToWolverine("claw_health", {
|
|
297
|
+
status: "stopping",
|
|
298
|
+
detail: "gateway_stop",
|
|
299
|
+
errorCounts: { ..._errorCounts },
|
|
300
|
+
timestamp: Date.now(),
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// ── session_end: track session errors ───────────────────────
|
|
305
|
+
reg("session_end", (event, ctx) => {
|
|
306
|
+
if (event?.error) {
|
|
307
|
+
console.warn(`[CLAW] Session error: ${event.error?.message || event.error}`);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ── EventEmitter fallback hooks ─────────────────────────────────
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Register basic EventEmitter hooks (works with all versions).
|
|
316
|
+
*/
|
|
317
|
+
function _registerEventHooks(gateway, projectRoot) {
|
|
318
|
+
if (!gateway.on) return;
|
|
319
|
+
|
|
320
|
+
gateway.on("error", (err) => {
|
|
321
|
+
const msg = err?.message || String(err);
|
|
322
|
+
_trackError("agent", `Gateway error: ${msg}`);
|
|
323
|
+
console.error(`[CLAW] Gateway error: ${msg}`);
|
|
324
|
+
|
|
325
|
+
reportToWolverine("route_error", {
|
|
326
|
+
path: "claw://gateway",
|
|
327
|
+
method: "INTERNAL",
|
|
328
|
+
statusCode: 500,
|
|
329
|
+
message: msg.slice(0, 500),
|
|
330
|
+
stack: err?.stack || null,
|
|
331
|
+
file: null,
|
|
332
|
+
line: null,
|
|
333
|
+
timestamp: Date.now(),
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// These may or may not exist depending on OpenClaw version
|
|
338
|
+
const optionalEvents = ["agent:error", "skill:error", "channel:error", "channel:disconnect"];
|
|
339
|
+
for (const evt of optionalEvents) {
|
|
340
|
+
try {
|
|
341
|
+
gateway.on(evt, (err) => {
|
|
342
|
+
const msg = err?.message || String(err);
|
|
343
|
+
const category = evt.split(":")[0];
|
|
344
|
+
_trackError(category === "skill" ? "tool" : "agent", `${evt}: ${msg}`);
|
|
345
|
+
console.error(`[CLAW] ${evt}: ${msg.slice(0, 100)}`);
|
|
346
|
+
});
|
|
347
|
+
} catch {}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ── Tool registration ───────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
function _registerTools(gateway, tools) {
|
|
354
|
+
if (gateway.registerTools) {
|
|
355
|
+
gateway.registerTools(PLUGIN_NAME, tools);
|
|
356
|
+
} else if (gateway.addTools) {
|
|
357
|
+
gateway.addTools(tools);
|
|
358
|
+
} else if (gateway.skills?.register) {
|
|
359
|
+
gateway.skills.register(PLUGIN_NAME, {
|
|
360
|
+
description: "Wolverine self-healing integration — backup, brain, health, and error pipeline",
|
|
361
|
+
tools,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ── Wolverine tools for the agent ───────────────────────────────
|
|
367
|
+
|
|
64
368
|
function buildWolverineTools(projectRoot, config) {
|
|
65
369
|
return [
|
|
66
370
|
{
|
|
@@ -163,7 +467,7 @@ function buildWolverineTools(projectRoot, config) {
|
|
|
163
467
|
},
|
|
164
468
|
{
|
|
165
469
|
name: "wolverine_health",
|
|
166
|
-
description: "Get wolverine system health status
|
|
470
|
+
description: "Get wolverine system health status including error tracking from all OpenClaw subsystems",
|
|
167
471
|
parameters: { type: "object", properties: {} },
|
|
168
472
|
execute: async () => {
|
|
169
473
|
const status = {
|
|
@@ -171,6 +475,8 @@ function buildWolverineTools(projectRoot, config) {
|
|
|
171
475
|
memory: `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`,
|
|
172
476
|
pid: process.pid,
|
|
173
477
|
nodeVersion: process.version,
|
|
478
|
+
errors: { ..._errorCounts },
|
|
479
|
+
recentErrors: _recentErrors.slice(-5).map(e => `[${e.category}] ${e.summary}`),
|
|
174
480
|
};
|
|
175
481
|
|
|
176
482
|
try {
|
|
@@ -211,7 +517,6 @@ function buildWolverineTools(projectRoot, config) {
|
|
|
211
517
|
required: ["error"],
|
|
212
518
|
},
|
|
213
519
|
execute: async ({ error, file }) => {
|
|
214
|
-
// Report to parent wolverine process for healing
|
|
215
520
|
reportToWolverine("route_error", {
|
|
216
521
|
path: "claw://agent",
|
|
217
522
|
method: "INTERNAL",
|
|
@@ -225,50 +530,20 @@ function buildWolverineTools(projectRoot, config) {
|
|
|
225
530
|
return "Error reported to wolverine healing pipeline.";
|
|
226
531
|
},
|
|
227
532
|
},
|
|
533
|
+
{
|
|
534
|
+
name: "wolverine_error_stats",
|
|
535
|
+
description: "Get error statistics from all OpenClaw subsystems (tools, agent, channels, LLM, subagents)",
|
|
536
|
+
parameters: { type: "object", properties: {} },
|
|
537
|
+
execute: async () => {
|
|
538
|
+
return JSON.stringify({
|
|
539
|
+
counts: { ..._errorCounts },
|
|
540
|
+
recent: _recentErrors.slice(-10),
|
|
541
|
+
}, null, 2);
|
|
542
|
+
},
|
|
543
|
+
},
|
|
228
544
|
];
|
|
229
545
|
}
|
|
230
546
|
|
|
231
|
-
/**
|
|
232
|
-
* Handle gateway errors — report to wolverine for healing.
|
|
233
|
-
*/
|
|
234
|
-
function handleGatewayError(err, projectRoot) {
|
|
235
|
-
console.error(`[CLAW] Gateway error: ${err.message}`);
|
|
236
|
-
reportToWolverine("route_error", {
|
|
237
|
-
path: "claw://gateway",
|
|
238
|
-
method: "INTERNAL",
|
|
239
|
-
statusCode: 500,
|
|
240
|
-
message: err.message,
|
|
241
|
-
stack: err.stack,
|
|
242
|
-
file: null,
|
|
243
|
-
line: null,
|
|
244
|
-
timestamp: Date.now(),
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Handle agent errors.
|
|
250
|
-
*/
|
|
251
|
-
function handleAgentError(err, projectRoot) {
|
|
252
|
-
console.error(`[CLAW] Agent error: ${err.message}`);
|
|
253
|
-
reportToWolverine("route_error", {
|
|
254
|
-
path: "claw://agent",
|
|
255
|
-
method: "INTERNAL",
|
|
256
|
-
statusCode: 500,
|
|
257
|
-
message: err.message,
|
|
258
|
-
stack: err.stack,
|
|
259
|
-
file: null,
|
|
260
|
-
line: null,
|
|
261
|
-
timestamp: Date.now(),
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Handle skill errors.
|
|
267
|
-
*/
|
|
268
|
-
function handleSkillError(err, projectRoot) {
|
|
269
|
-
console.warn(`[CLAW] Skill error (non-fatal): ${err.message}`);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
547
|
/**
|
|
273
548
|
* Report to wolverine parent process via IPC.
|
|
274
549
|
*/
|