fix(agents): reject partial fallback skip TTL env values (#107234)

* fix(agents): reject partial fallback skip TTL env values

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(agents): tighten fallback skip TTL validation

Co-authored-by: wahaha1223 <0668001153@xydigit.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
wahaha1223
2026-07-15 00:18:47 +08:00
committed by GitHub
parent 5f13921c26
commit acd67cf3e4
2 changed files with 45 additions and 33 deletions
+42 -31
View File
@@ -1,5 +1,5 @@
// Exercises per-session fallback skip markers, TTL expiry, and opt-in cache defaults.
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
resetFallbackSkipCacheForTest,
getFallbackCandidateSkipReason,
@@ -13,6 +13,7 @@ describe("fallback-skip-cache", () => {
});
afterEach(() => {
vi.unstubAllEnvs();
resetFallbackSkipCacheForTest();
});
@@ -262,39 +263,49 @@ describe("fallback-skip-cache", () => {
).toBe(false);
});
it("uses OPENCLAW_FALLBACK_SKIP_TTL_MS as an opt-in default TTL", () => {
const previous = process.env.OPENCLAW_FALLBACK_SKIP_TTL_MS;
process.env.OPENCLAW_FALLBACK_SKIP_TTL_MS = "60000";
try {
markFallbackCandidateSkipped({
it("does not enable the cache for a suffixed TTL value", () => {
vi.stubEnv("OPENCLAW_FALLBACK_SKIP_TTL_MS", "1000ms");
markFallbackCandidateSkipped({
sessionId: "s1",
provider: "anthropic",
model: "claude-opus-4-7",
reason: "auth",
now: 1_000,
});
expect(
isFallbackCandidateSkipped({
sessionId: "s1",
provider: "anthropic",
model: "claude-opus-4-7",
reason: "auth",
now: 1_000,
});
expect(
isFallbackCandidateSkipped({
sessionId: "s1",
provider: "anthropic",
model: "claude-opus-4-7",
now: 60_000,
}),
).toBe(true);
expect(
isFallbackCandidateSkipped({
sessionId: "s1",
provider: "anthropic",
model: "claude-opus-4-7",
now: 61_001,
}),
).toBe(false);
} finally {
if (previous === undefined) {
delete process.env.OPENCLAW_FALLBACK_SKIP_TTL_MS;
} else {
process.env.OPENCLAW_FALLBACK_SKIP_TTL_MS = previous;
}
}
}),
).toBe(false);
});
it("uses OPENCLAW_FALLBACK_SKIP_TTL_MS as an opt-in default TTL", () => {
vi.stubEnv("OPENCLAW_FALLBACK_SKIP_TTL_MS", "60000");
markFallbackCandidateSkipped({
sessionId: "s1",
provider: "anthropic",
model: "claude-opus-4-7",
reason: "auth",
now: 1_000,
});
expect(
isFallbackCandidateSkipped({
sessionId: "s1",
provider: "anthropic",
model: "claude-opus-4-7",
now: 60_000,
}),
).toBe(true);
expect(
isFallbackCandidateSkipped({
sessionId: "s1",
provider: "anthropic",
model: "claude-opus-4-7",
now: 61_001,
}),
).toBe(false);
});
});
+3 -2
View File
@@ -15,6 +15,7 @@
* `resetFallbackSkipCacheForTest()`.
*/
import { parseStrictNonNegativeInteger } from "@openclaw/normalization-core/number-coercion";
import { modelKey } from "./model-selection-normalize.js";
/**
@@ -36,8 +37,8 @@ function resolveConfiguredSkipTtlMs(env: NodeJS.ProcessEnv = process.env): numbe
if (!trimmed) {
return DEFAULT_FALLBACK_SKIP_TTL_MS;
}
const parsed = Number.parseInt(trimmed, 10);
if (!Number.isFinite(parsed) || parsed < 0) {
const parsed = parseStrictNonNegativeInteger(trimmed);
if (parsed === undefined) {
return DEFAULT_FALLBACK_SKIP_TTL_MS;
}
if (parsed === 0) {