




















@@ -7,6 +7,30 @@ import {
77} from "./rate-limits.js";
8899describe("formatCodexUsageLimitErrorMessage", () => {
10+it("gives actionable guidance when Codex omits reset details", () => {
11+const message = formatCodexUsageLimitErrorMessage({
12+message: "You've reached your usage limit.",
13+codexErrorInfo: "usageLimitExceeded",
14+rateLimits: {
15+rateLimits: {
16+limitId: "codex",
17+primary: { usedPercent: 100, windowDurationMins: 10_080, resetsAt: null },
18+secondary: null,
19+},
20+},
21+nowMs: Date.UTC(2026, 4, 10, 23, 0, 0),
22+});
23+24+expect(message).toContain("You've reached your Codex subscription usage limit.");
25+expect(message).toContain("Your weekly Codex usage limit is reached.");
26+expect(message).toContain("OpenClaw could not determine a reset time from Codex.");
27+expect(message).toContain("Wait until Codex becomes available");
28+expect(message).toContain("use another Codex account if available");
29+expect(message).toContain("switch to another configured model/provider");
30+expect(message).not.toContain("Codex did not return a reset time");
31+expect(message).not.toContain("/codex account");
32+});
33+1034it("preserves Codex retry hints when structured reset windows are absent", () => {
1135const message = formatCodexUsageLimitErrorMessage({
1236message:
@@ -24,6 +48,7 @@ describe("formatCodexUsageLimitErrorMessage", () => {
24482549expect(message).toContain("You've reached your Codex subscription usage limit.");
2650expect(message).toContain("Codex says to try again at May 11th, 2026 9:00 AM.");
51+expect(message).toContain("Wait until the retry time");
2752expect(message).not.toContain("Codex did not return a reset time");
2853});
2954@@ -42,10 +67,67 @@ describe("formatCodexUsageLimitErrorMessage", () => {
4267});
43684469expect(message).toContain("Next reset in 1 hour, ");
70+expect(message).toContain("Wait until the reset time");
4571expect(message).toMatch(/\b[A-Z][a-z]{2} \d{1,2}(?:, \d{4})? at \d{1,2}:\d{2} [AP]M\b/u);
4672expect(message).not.toMatch(/\(\d{4}-\d{2}-\d{2}T/u);
4773expect(message).not.toContain("Codex did not return a reset time");
4874});
75+76+it("uses the blocking reset when multiple Codex windows are exhausted", () => {
77+const nowMs = 1_700_000_000_000;
78+const nowSeconds = nowMs / 1000;
79+const message = formatCodexUsageLimitErrorMessage({
80+message: "You've reached your usage limit.",
81+codexErrorInfo: "usageLimitExceeded",
82+rateLimits: {
83+rateLimits: {
84+limitId: "codex",
85+primary: { usedPercent: 100, windowDurationMins: 300, resetsAt: nowSeconds + 3600 },
86+secondary: {
87+usedPercent: 100,
88+windowDurationMins: 10_080,
89+resetsAt: nowSeconds + 24 * 3600,
90+},
91+},
92+},
93+ nowMs,
94+});
95+96+expect(message).toContain("Next reset in 1 day");
97+expect(message).not.toContain("Next reset in 1 hour");
98+expect(message).toContain("Wait until the reset time");
99+});
100+101+it("does not use sibling bucket resets when the blocked Codex bucket omits a reset", () => {
102+const nowMs = 1_700_000_000_000;
103+const nowSeconds = nowMs / 1000;
104+const message = formatCodexUsageLimitErrorMessage({
105+message: "You've reached your usage limit.",
106+codexErrorInfo: "usageLimitExceeded",
107+rateLimits: {
108+rateLimitsByLimitId: {
109+codex: {
110+limitId: "codex",
111+limitName: "Codex",
112+primary: { usedPercent: 100, windowDurationMins: 300, resetsAt: null },
113+secondary: null,
114+},
115+"gpt-5.3-codex-spark": {
116+limitId: "gpt-5.3-codex-spark",
117+limitName: "GPT 5.3 Codex Spark",
118+primary: { usedPercent: 0, windowDurationMins: 300, resetsAt: nowSeconds + 3600 },
119+secondary: null,
120+},
121+},
122+},
123+ nowMs,
124+});
125+126+expect(message).toContain("OpenClaw could not determine a reset time from Codex.");
127+expect(message).toContain("Wait until Codex becomes available");
128+expect(message).not.toContain("Next reset");
129+expect(message).not.toContain("1 hour");
130+});
49131});
5013251133describe("Codex rate limit blocking resets", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。