fix(FN-1952): bound recovery and task log growth

This commit is contained in:
gsxdsm
2026-04-16 19:32:35 -07:00
parent 80e49b8e40
commit 4a576c028c
16 changed files with 332 additions and 37 deletions

View File

@@ -487,6 +487,26 @@ describe("GET /tasks/:id", () => {
expect(res.body.prompt).toBe("# KB-001\n\nTest task");
});
it("caps task detail activity logs to keep the modal payload bounded", async () => {
const log = Array.from({ length: 510 }, (_, index) => ({
timestamp: `2026-01-01T00:${String(index % 60).padStart(2, "0")}:00.000Z`,
action: `entry-${index}`,
}));
(store.getTask as ReturnType<typeof vi.fn>).mockResolvedValue({
...FAKE_TASK_DETAIL,
log,
});
const res = await GET(buildApp(), "/api/tasks/KB-001");
expect(res.status).toBe(200);
expect(res.body.log).toHaveLength(500);
expect(res.body.log[0].action).toBe("entry-10");
expect(res.body.log[499].action).toBe("entry-509");
expect(res.body.activityLogTotal).toBe(510);
expect(res.body.activityLogTruncatedCount).toBe(10);
});
it("returns 404 when task genuinely does not exist (ENOENT)", async () => {
const err: NodeJS.ErrnoException = new Error("ENOENT: no such file or directory");
err.code = "ENOENT";

View File

@@ -64,6 +64,8 @@ import {
import { rateLimit, RATE_LIMITS } from "./rate-limit.js";
import { resolvePluginManifest } from "./plugin-routes.js";
const TASK_DETAIL_ACTIVITY_LOG_LIMIT = 500;
/**
* Minimal interface matching pi-coding-agent's ModelRegistry API surface
* used by the models route. Avoids a direct dependency on the pi-coding-agent package.
@@ -642,6 +644,19 @@ function logEntryToTimelineEntry(entry: import("@fusion/core").AgentLogEntry): T
};
}
function trimTaskDetailActivityLog<T extends Task>(task: T): T {
if (!Array.isArray(task.log) || task.log.length <= TASK_DETAIL_ACTIVITY_LOG_LIMIT) {
return task;
}
return {
...task,
log: task.log.slice(-TASK_DETAIL_ACTIVITY_LOG_LIMIT),
activityLogTotal: task.log.length,
activityLogTruncatedCount: task.log.length - TASK_DETAIL_ACTIVITY_LOG_LIMIT,
} as T;
}
// ── Git Remote Detection ──────────────────────────────────────────
/** Git remote info returned by the remotes endpoint */
@@ -3784,8 +3799,10 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
router.get("/tasks/:id", async (req, res) => {
try {
const { store: scopedStore } = await getProjectContext(req);
const task = await scopedStore.getTask(req.params.id);
res.json(task);
const task = await scopedStore.getTask(req.params.id, {
activityLogLimit: TASK_DETAIL_ACTIVITY_LOG_LIMIT,
});
res.json(trimTaskDetailActivityLog(task));
} catch (err: unknown) {
if (err instanceof ApiError) {
throw err;