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 eb13669a94
commit 59e270980e
10 changed files with 134 additions and 31 deletions

View File

@@ -24,7 +24,7 @@ import { buildSessionSkillContext } from "./session-skill-context.js";
import { PRIORITY_SPECIFY, type AgentSemaphore } from "./concurrency.js";
import { AgentLogger } from "./agent-logger.js";
import { resolveAgentInstructions, buildSystemPromptWithInstructions } from "./agent-instructions.js";
import { triageLog, reviewerLog } from "./logger.js";
import { triageLog, reviewerLog, formatError } from "./logger.js";
import {
isUsageLimitError,
checkSessionError,
@@ -1084,7 +1084,7 @@ export class TriageProcessor {
await retryableWork();
}
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : String(err);
const { message: errorMessage, detail: errorDetail, stack: errorStack } = formatError(err);
// Race condition: task was deleted (e.g. as a duplicate) between listTasks()
// and specifyTask(). The file is gone, so just log and skip — no point retrying.
if ((err as Record<string, unknown>).code === "ENOENT") {
@@ -1172,7 +1172,13 @@ export class TriageProcessor {
const msg = restoreErr instanceof Error ? restoreErr.message : String(restoreErr);
triageLog.warn(`${task.id}: failed to restore status to '${restoreStatus}' after specification error: ${msg}`);
});
triageLog.error(`${task.id} specification failed:`, errorMessage);
triageLog.error(`${task.id} specification failed:`, errorDetail);
if (errorStack) {
await this.store.logEntry(task.id, `Specification failed: ${errorMessage}`, errorStack).catch((logErr: unknown) => {
const msg = logErr instanceof Error ? logErr.message : String(logErr);
triageLog.warn(`${task.id}: failed to persist specification-failure stack trace: ${msg}`);
});
}
this.options.onSpecifyError?.(task, err instanceof Error ? err : new Error(errorMessage));
}
} finally {