diff --git a/server/src/__tests__/task-interruption-query.test.ts b/server/src/__tests__/task-interruption-query.test.ts index 59d4bad1f43..89368dceef8 100644 --- a/server/src/__tests__/task-interruption-query.test.ts +++ b/server/src/__tests__/task-interruption-query.test.ts @@ -384,6 +384,10 @@ describe.skipIf(!support.supported)("task interruption latest run query", () => const text = JSON.stringify(presentation); for (let index = 0; index + 8 <= secret.length; index += 1) expect(text).not.toContain(secret.slice(index, index + 8)); } + // A long older log still shows its final error line. + await db.update(heartbeatRuns).set({ error: `Execution workspace provision command "${command}" failed: ${"filler\n".repeat(800)}No such file or directory` }) + .where(eq(heartbeatRuns.id, run!.id)); + for (const presentation of await read()) expect(presentation?.setupFailure?.output).toMatch(/No such file or directory$/); // A transient retry shows its setup_failed predecessor's cause. const predecessorId = randomUUID(); await db.insert(heartbeatRuns).values({ id: predecessorId, companyId: f.companyId, agentId: run!.agentId, status: "failed", diff --git a/server/src/services/task-interruption-presentation.ts b/server/src/services/task-interruption-presentation.ts index 2cbd5f2b52b..5009f22e5f6 100644 --- a/server/src/services/task-interruption-presentation.ts +++ b/server/src/services/task-interruption-presentation.ts @@ -629,9 +629,11 @@ export async function taskInterruptionPresentations(db: Db, companyId: string, i } if (presentation.setupFailure) { const value = safe[index]?.setupFailure; - const command = safeMessage(value?.command), message = safeMessage(value?.message); + // Redact the whole text, then shorten: the output keeps its final lines, not a head slice. + const whole = (text: unknown) => typeof text === "string" ? redactCurrentUserText(text, { enabled: settings.censorUsernameInLogs }) : ""; + const command = whole(value?.command), message = whole(value?.message); const failure = value && command.trim() && message.trim() - ? boundSetupCommandFailure({ ...value, command, output: value.output == null ? null : safeMessage(value.output) || null, message }) + ? boundSetupCommandFailure({ ...value, command, output: value.output == null ? null : whole(value.output) || null, message }) : null; if (failure) presentation.setupFailure = failure; else delete presentation.setupFailure;