fix: stable auth profile resolution for isolated cron jobs (#62797) (thanks @neeravmakwana)

* Cron: stable auth profile resolution for isolated jobs (#62783)

* Tests: clearer assertion for isolated cron auth profile spy (#62797)
This commit is contained in:
Neerav Makwana
2026-04-09 07:18:05 -04:00
committed by GitHub
parent 4977c4ab82
commit 12544e24d7
3 changed files with 90 additions and 2 deletions

View File

@@ -186,6 +186,7 @@ Docs: https://docs.openclaw.ai
- Agents/model resolution: let explicit `openai-codex/gpt-5.4` selection prefer provider runtime metadata when it reports a larger context window, keeping configured Codex runs aligned with the live provider limits. (#62694) Thanks @ruclaw7.
- Agents/model resolution: keep explicit-model runtime comparisons on the configured workspace plugin registry, so workspace-installed providers do not silently fall back to stale explicit metadata during runtime model lookup.
- Providers/Z.AI: default onboarding and endpoint detection to GLM-5.1 instead of GLM-5. (#61998) Thanks @serg0x.
- Cron/isolated: resolve auth profiles without treating every isolated run as a brand-new auth session, so profile-based providers (for example OpenRouter) keep a stable credential choice instead of rotating or ignoring stored keys. (#62783) Thanks @neeravmakwana.
## 2026.4.5

View File

@@ -0,0 +1,88 @@
import "./isolated-agent.mocks.js";
import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as sessionOverride from "../agents/auth-profiles/session-override.js";
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
import { createCliDeps } from "./isolated-agent.delivery.test-helpers.js";
import { runCronIsolatedAgentTurn } from "./isolated-agent.js";
import {
makeCfg,
makeJob,
withTempCronHome,
writeSessionStore,
} from "./isolated-agent.test-harness.js";
import { setupIsolatedAgentTurnMocks } from "./isolated-agent.test-setup.js";
describe("isolated cron resolveSessionAuthProfileOverride isNewSession (#62783)", () => {
beforeEach(() => {
setupIsolatedAgentTurnMocks({ fast: true });
});
afterEach(() => {
vi.restoreAllMocks();
});
it("passes isNewSession=false when sessionTarget is isolated", async () => {
const spy = vi.spyOn(sessionOverride, "resolveSessionAuthProfileOverride");
spy.mockResolvedValue("openrouter:default");
await withTempCronHome(async (home) => {
const storePath = await writeSessionStore(home, { lastProvider: "webchat", lastTo: "" });
const agentDir = path.join(home, ".openclaw", "agents", "main", "agent");
await fs.mkdir(agentDir, { recursive: true });
await fs.writeFile(
path.join(agentDir, "auth-profiles.json"),
JSON.stringify({
version: 1,
profiles: {
"openrouter:default": {
type: "api_key",
provider: "openrouter",
key: "sk-or-test-key",
},
},
order: { openrouter: ["openrouter:default"] },
}),
"utf-8",
);
vi.mocked(runEmbeddedPiAgent).mockResolvedValue({
payloads: [{ text: "ok" }],
meta: {
durationMs: 1,
agentMeta: { sessionId: "s", provider: "openrouter", model: "kimi-k2.5" },
},
});
const cfg = makeCfg(home, storePath, {
agents: {
defaults: {
model: { primary: "openrouter/moonshotai/kimi-k2.5" },
workspace: path.join(home, "openclaw"),
},
},
});
await runCronIsolatedAgentTurn({
cfg,
deps: createCliDeps(),
job: {
...makeJob({ kind: "agentTurn", message: "hi" }),
sessionTarget: "isolated",
delivery: { mode: "none" },
},
message: "hi",
sessionKey: "cron:auth-flag-1",
lane: "cron",
});
});
const openRouterCall = spy.mock.calls.find((c) => c[0]?.provider === "openrouter");
expect(
openRouterCall,
"resolveSessionAuthProfileOverride was not called with provider openrouter",
).toBeDefined();
expect(openRouterCall?.[0]?.isNewSession).toBe(false);
});
});

View File

@@ -409,7 +409,6 @@ async function prepareCronRunContext(params: {
} catch (err) {
logWarn(`[cron:${input.job.id}] Failed to persist pre-run session entry: ${String(err)}`);
}
const authProfileId = await resolveSessionAuthProfileOverride({
cfg: cfgWithAgentDefaults,
provider,
@@ -418,7 +417,7 @@ async function prepareCronRunContext(params: {
sessionStore: cronSession.store,
sessionKey: agentSessionKey,
storePath: cronSession.storePath,
isNewSession: cronSession.isNewSession,
isNewSession: cronSession.isNewSession && input.job.sessionTarget !== "isolated",
});
const liveSelection: CronLiveSelection = {
provider,