























@@ -17,7 +17,11 @@ import {
1717clearRuntimeAuthProfileStoreSnapshots,
1818ensureAuthProfileStore,
1919} from "./auth-profiles/store.js";
20-import { calculateAuthProfileCooldownMs, markAuthProfileFailure } from "./auth-profiles/usage.js";
20+import {
21+calculateAuthProfileCooldownMs,
22+markAuthProfileFailure,
23+setAuthProfileFailureHook,
24+} from "./auth-profiles/usage.js";
21252226type AuthProfileStore = ReturnType<typeof ensureAuthProfileStore>;
2327@@ -374,6 +378,46 @@ describe("markAuthProfileFailure", () => {
374378expect(reloaded.usageStats?.["openrouter:default"]).toBeUndefined();
375379});
376380});
381+382+it("fires the auth profile failure hook so callers can self-heal", async () => {
383+await withAuthProfileStore(async ({ agentDir, store }) => {
384+const hook = vi.fn();
385+setAuthProfileFailureHook(hook);
386+try {
387+await markAuthProfileFailure({
388+ store,
389+profileId: "anthropic:default",
390+reason: "auth",
391+ agentDir,
392+});
393+expect(hook).toHaveBeenCalledTimes(1);
394+} finally {
395+setAuthProfileFailureHook(undefined);
396+}
397+});
398+});
399+400+it("does not break failure recording when the hook throws", async () => {
401+await withAuthProfileStore(async ({ agentDir, store }) => {
402+const throwingHook = vi.fn(() => {
403+throw new Error("boom");
404+});
405+setAuthProfileFailureHook(throwingHook);
406+try {
407+await markAuthProfileFailure({
408+ store,
409+profileId: "anthropic:default",
410+reason: "auth",
411+ agentDir,
412+});
413+expect(throwingHook).toHaveBeenCalledTimes(1);
414+// Failure still got recorded despite the hook throwing.
415+expect(store.usageStats?.["anthropic:default"]?.errorCount ?? 0).toBeGreaterThan(0);
416+} finally {
417+setAuthProfileFailureHook(undefined);
418+}
419+});
420+});
377421});
378422379423describe("calculateAuthProfileCooldownMs", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。