feat(engine): capture stack traces at task-failure catch sites

Add formatError() helper that extracts both message and full stack from
unknown caught values, and use it at every status:"failed" catch site in
executor, agent-heartbeat, and triage. Stack traces now land in
store.logEntry outcome (persisted to task.log/activityLog) and in stderr
logger output, so failures like "Cannot read properties of undefined
(reading 'filter')" can be diagnosed without re-running.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-24 12:54:13 -07:00
parent 4e488f5b0e
commit 1fe1b395be
10 changed files with 134 additions and 31 deletions

View File

@@ -109,3 +109,32 @@ export const nodeHealthMonitorLog = createLogger("node-health-monitor");
/** Logger for the peer exchange (gossip) subsystem. */
export const peerExchangeLog = createLogger("peer-exchange");
/**
* Extract both a short message and a full stack trace from an unknown caught
* value. Use this at catch sites instead of the
* `err instanceof Error ? err.message : String(err)` idiom so that the stack
* is preserved for logs, task `activityLog` entries, and surfaced diagnostics.
*
* `detail` is `message` when no stack is available and `message + "\n" + stack`
* otherwise — suitable for `store.logEntry(taskId, action, detail)`.
*/
export function formatError(err: unknown): { message: string; stack?: string; detail: string } {
if (err instanceof Error) {
const message = err.message || err.name || "Error";
const stack = err.stack;
const detail = stack && stack.includes(message) ? stack : stack ? `${message}\n${stack}` : message;
return { message, stack, detail };
}
let message: string;
if (typeof err === "string") {
message = err;
} else {
try {
message = JSON.stringify(err);
} catch {
message = String(err);
}
}
return { message, detail: message };
}