fix(gateway): bound maintenance run expiry checks

This commit is contained in:
Peter Steinberger
2026-05-30 13:43:21 -04:00
parent 912a276ca1
commit 9ef699fedc
2 changed files with 49 additions and 2 deletions

View File

@@ -394,6 +394,52 @@ describe("startGatewayMaintenanceTimers", () => {
stopMaintenanceTimers(timers);
});
it("evicts pending accepted agent dedupe entries with invalid run expiry", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-03-22T00:00:00Z"));
const { startGatewayMaintenanceTimers } = await import("./server-maintenance.js");
const deps = createMaintenanceTimerDeps();
const now = Date.now();
deps.dedupe.set("agent:invalid-expiry-pending-agent", {
ts: now - DEDUPE_TTL_MS - 1,
ok: true,
payload: {
runId: "invalid-expiry-pending-agent",
sessionKey: "agent:main:main",
status: "accepted",
expiresAtMs: Number.POSITIVE_INFINITY,
},
});
const timers = startGatewayMaintenanceTimers(deps);
await vi.advanceTimersByTimeAsync(60_000);
expect(deps.dedupe.has("agent:invalid-expiry-pending-agent")).toBe(false);
stopMaintenanceTimers(timers);
});
it("aborts active runs with invalid expiry timestamps", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-03-22T00:00:00Z"));
const { startGatewayMaintenanceTimers } = await import("./server-maintenance.js");
const deps = createMaintenanceTimerDeps();
const runId = "run-invalid-expiry";
const activeRun = createActiveRun("main");
activeRun.expiresAtMs = Number.POSITIVE_INFINITY;
deps.chatAbortControllers.set(runId, activeRun);
const timers = startGatewayMaintenanceTimers(deps);
await vi.advanceTimersByTimeAsync(60_000);
expect(activeRun.controller.signal.aborted).toBe(true);
expect(deps.chatAbortControllers.has(runId)).toBe(false);
stopMaintenanceTimers(timers);
});
it("keeps active exec approval dedupe aliases past the normal ttl", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-03-22T00:00:00Z"));

View File

@@ -1,6 +1,7 @@
import type { HealthSummary } from "../commands/health.js";
import { sweepStaleRunContexts } from "../infra/agent-events.js";
import { cleanOldMedia } from "../media/store.js";
import { isFutureDateTimestampMs } from "../shared/number-coercion.js";
import { abortChatRunById, type ChatAbortControllerEntry } from "./chat-abort.js";
import { pruneStaleControlPlaneBuckets } from "./control-plane-rate-limit.js";
import type { ChatRunState } from "./server-chat-state.js";
@@ -124,7 +125,7 @@ export function startGatewayMaintenanceTimers(params: {
return false;
}
const expiresAtMs = (payload as { expiresAtMs?: unknown }).expiresAtMs;
return typeof expiresAtMs === "number" && Number.isFinite(expiresAtMs) && expiresAtMs > now;
return isFutureDateTimestampMs(expiresAtMs, { nowMs: now });
};
const isActiveRunDedupeKey = (key: string, dedupeEntry: DedupeEntry) => {
if (!key.startsWith("agent:") && !key.startsWith("chat:")) {
@@ -183,7 +184,7 @@ export function startGatewayMaintenanceTimers(params: {
};
for (const [runId, entry] of params.chatAbortControllers) {
if (now <= entry.expiresAtMs) {
if (isFutureDateTimestampMs(entry.expiresAtMs, { nowMs: now })) {
continue;
}
abortChatRunById(