fix(tasks): tolerate invalid flow timestamps

This commit is contained in:
Peter Steinberger
2026-05-30 05:41:18 -04:00
parent 6b41a0692f
commit 029c17de41
2 changed files with 30 additions and 4 deletions
+21
View File
@@ -172,6 +172,27 @@ describe("flows commands", () => {
});
});
it("shows TaskFlows with Date-invalid timestamps without crashing", async () => {
await withTaskFlowCommandStateDir(async () => {
const flow = createManagedTaskFlow({
ownerKey: "agent:main:main",
controllerId: "tests/flows-command",
goal: "Inspect malformed flow timestamp",
status: "running",
createdAt: 100,
updatedAt: 8_700_000_000_000_000,
});
const runtime = createRuntime();
await flowsShowCommand({ lookup: flow.flowId, json: false }, runtime);
const lines = vi.mocked(runtime.log).mock.calls.map(([line]) => String(line));
expect(lines).toContain(`flowId: ${flow.flowId}`);
expect(lines).toContain("createdAt: 1970-01-01T00:00:00.100Z");
expect(lines).toContain("updatedAt: n/a");
});
});
it("sanitizes TaskFlow text output before printing to the terminal", async () => {
await withTaskFlowCommandStateDir(async () => {
const unsafeOwnerKey = "agent:main:\u001b[31mowner";
+9 -4
View File
@@ -4,6 +4,7 @@ import { formatCliCommand } from "../cli/command-format.js";
import { getRuntimeConfig } from "../config/config.js";
import { info } from "../globals.js";
import type { RuntimeEnv } from "../runtime.js";
import { timestampMsToIsoString } from "../shared/number-coercion.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { listTasksForFlowId } from "../tasks/runtime-internal.js";
import { cancelFlowById, getFlowTaskSummary } from "../tasks/task-executor.js";
@@ -50,6 +51,10 @@ function shortToken(value: string | undefined, maxChars = ID_PAD): string {
return truncate(trimmed, maxChars);
}
function formatFlowTimestamp(value: number | undefined | null): string {
return timestampMsToIsoString(value) ?? "n/a";
}
function formatFlowStatusCell(status: TaskFlowStatus, rich: boolean) {
const padded = status.padEnd(STATUS_PAD);
if (!rich) {
@@ -228,11 +233,11 @@ export async function flowsShowCommand(
`notify: ${flow.notifyPolicy}`,
...(stateSummary ? [`state: ${safeFlowDisplayText(stateSummary)}`] : []),
...(flow.cancelRequestedAt
? [`cancelRequestedAt: ${new Date(flow.cancelRequestedAt).toISOString()}`]
? [`cancelRequestedAt: ${formatFlowTimestamp(flow.cancelRequestedAt)}`]
: []),
`createdAt: ${new Date(flow.createdAt).toISOString()}`,
`updatedAt: ${new Date(flow.updatedAt).toISOString()}`,
`endedAt: ${flow.endedAt ? new Date(flow.endedAt).toISOString() : "n/a"}`,
`createdAt: ${formatFlowTimestamp(flow.createdAt)}`,
`updatedAt: ${formatFlowTimestamp(flow.updatedAt)}`,
`endedAt: ${formatFlowTimestamp(flow.endedAt)}`,
`tasks: ${taskSummary.total} total · ${taskSummary.active} active · ${taskSummary.failures} issues`,
];
for (const line of lines) {