fix(commitments): bound terminal failure cooldown expiry

This commit is contained in:
Peter Steinberger
2026-05-30 13:33:01 -04:00
parent 8d3fe21b53
commit a4f62400a7
2 changed files with 64 additions and 6 deletions

View File

@@ -293,6 +293,49 @@ describe("commitment extraction runtime", () => {
).toBe(true);
});
it("uses the queued item timestamp for terminal failure cooldowns", async () => {
const cfg = await createConfig();
const extractBatch = vi.fn(async () => {
throw new Error("OAuth token refresh failed");
});
const dateNow = vi.spyOn(Date, "now").mockReturnValue(Number.NaN);
configureCommitmentExtractionRuntime({
forceInTests: true,
extractBatch,
setTimer: () => ({ unref() {} }) as ReturnType<typeof setTimeout>,
clearTimer: () => undefined,
});
expect(
enqueueCommitmentExtraction({
cfg,
nowMs,
agentId: "main",
sessionKey: "agent:main:discord:channel-1",
channel: "discord",
userText: "I have an interview tomorrow.",
assistantText: "Good luck.",
}),
).toBe(true);
try {
await expect(drainCommitmentExtractionQueue()).rejects.toThrow("OAuth token refresh failed");
expect(
enqueueCommitmentExtraction({
cfg,
nowMs: nowMs + 1,
agentId: "main",
sessionKey: "agent:main:discord:channel-1",
channel: "discord",
userText: "The interview is tomorrow.",
assistantText: "I hope it goes well.",
}),
).toBe(false);
} finally {
dateNow.mockRestore();
}
});
it("bounds hidden extraction queue growth before spending extractor tokens", async () => {
const cfg = await createConfig();
const extractBatch = vi.fn(

View File

@@ -4,6 +4,7 @@ import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveStateDir } from "../config/paths.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveExpiresAtMsFromDurationMs } from "../shared/number-coercion.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { resolveCommitmentTimezone, resolveCommitmentsConfig } from "./config.js";
import {
@@ -168,11 +169,20 @@ function isTerminalExtractionError(error: unknown): boolean {
);
}
function openTerminalFailureCooldown(agentId: string, error: unknown): void {
terminalFailureCooldownUntilByAgent.set(
agentId,
Date.now() + TERMINAL_EXTRACTION_FAILURE_COOLDOWN_MS,
);
function openTerminalFailureCooldown(
agentId: string,
error: unknown,
nowMs: number,
fallbackNowMs: number,
): void {
const cooldownUntil =
resolveExpiresAtMsFromDurationMs(TERMINAL_EXTRACTION_FAILURE_COOLDOWN_MS, { nowMs }) ??
resolveExpiresAtMsFromDurationMs(TERMINAL_EXTRACTION_FAILURE_COOLDOWN_MS, {
nowMs: fallbackNowMs,
});
if (cooldownUntil !== undefined) {
terminalFailureCooldownUntilByAgent.set(agentId, cooldownUntil);
}
queue = queue.filter((item) => item.agentId !== agentId);
log.warn("commitment extraction disabled temporarily after terminal model/auth failure", {
agentId,
@@ -281,7 +291,12 @@ export async function drainCommitmentExtractionQueue(): Promise<number> {
result = await extractor({ cfg: firstCfg, items });
} catch (error) {
if (isTerminalExtractionError(error)) {
openTerminalFailureCooldown(items[0]?.agentId ?? "", error);
openTerminalFailureCooldown(
items[0]?.agentId ?? "",
error,
Date.now(),
items[0]?.nowMs ?? Date.now(),
);
}
throw error;
}