fix(agents): rotate claude cli bindings on reset

This commit is contained in:
Vincent Koc 2026-04-05 14:54:02 +01:00
parent 21270c2586
commit 7cd015b203
3 changed files with 16 additions and 13 deletions

View File

@ -173,6 +173,8 @@ Docs: https://docs.openclaw.ai
- Voice-call/OpenAI: pass full plugin config into realtime transcription provider resolution so streaming calls can discover the bundled OpenAI realtime transcription provider again. Fixes #60936. Thanks @sliekens and @vincentkoc.
- WhatsApp: restore `channels.whatsapp.blockStreaming` and reset watchdog timeouts after reconnect so quiet chats stop falling into reconnect loops. (#60007, #60069) Thanks @MonkeyLeeT and @mcaxtr.
- Windows/restart: fall back to the installed Startup-entry launcher when the scheduled task was never registered, so `/restart` can relaunch the gateway on Windows setups where `schtasks` install fell back during onboarding. (#58943) Thanks @imechZhangLY.
- Agents/Claude CLI: persist routed Claude session bindings, rotate them on `/new` and `/reset`, and keep live Claude CLI model switches moving across the configured Claude family so resumed sessions follow the real active thread and model. Thanks @vincentkoc.
- Providers/Anthropic: when Claude CLI auth becomes the default, write a real `claude-cli` auth profile so local and gateway agent runs can use Claude CLI immediately without missing-API-key failures. Thanks @vincentkoc.
## 2026.4.2

View File

@ -1573,7 +1573,7 @@ describe("initSessionState preserves behavior overrides across /new and /reset",
}
});
it("preserves selected auth profile overrides across /new and /reset", async () => {
it("preserves selected auth profile overrides but clears stale cli session bindings across /new and /reset", async () => {
const storePath = await createStorePath("openclaw-reset-model-auth-");
const sessionKey = "agent:main:telegram:dm:user-model-auth";
const existingSessionId = "existing-session-model-auth";
@ -1641,9 +1641,14 @@ describe("initSessionState preserves behavior overrides across /new and /reset",
authProfileOverrideSource: overrides.authProfileOverrideSource,
authProfileOverrideCompactionCount: overrides.authProfileOverrideCompactionCount,
});
expect(result.sessionEntry.cliSessionIds).toEqual(overrides.cliSessionIds);
expect(result.sessionEntry.cliSessionBindings).toEqual(overrides.cliSessionBindings);
expect(result.sessionEntry.claudeCliSessionId).toBe(overrides.claudeCliSessionId);
expect(result.sessionEntry.cliSessionIds).toBeUndefined();
expect(result.sessionEntry.cliSessionBindings).toBeUndefined();
expect(result.sessionEntry.claudeCliSessionId).toBeUndefined();
const stored = JSON.parse(await fs.readFile(storePath, "utf-8"));
expect(stored[sessionKey].cliSessionIds).toBeUndefined();
expect(stored[sessionKey].cliSessionBindings).toBeUndefined();
expect(stored[sessionKey].claudeCliSessionId).toBeUndefined();
}
});

View File

@ -305,9 +305,6 @@ export async function initSessionState(params: {
let persistedAuthProfileOverride: string | undefined;
let persistedAuthProfileOverrideSource: SessionEntry["authProfileOverrideSource"];
let persistedAuthProfileOverrideCompactionCount: number | undefined;
let persistedCliSessionIds: SessionEntry["cliSessionIds"];
let persistedCliSessionBindings: SessionEntry["cliSessionBindings"];
let persistedClaudeCliSessionId: string | undefined;
let persistedLabel: string | undefined;
let persistedSpawnedBy: SessionEntry["spawnedBy"];
let persistedSpawnedWorkspaceDir: SessionEntry["spawnedWorkspaceDir"];
@ -501,9 +498,8 @@ export async function initSessionState(params: {
persistedAuthProfileOverride = entry.authProfileOverride;
persistedAuthProfileOverrideSource = entry.authProfileOverrideSource;
persistedAuthProfileOverrideCompactionCount = entry.authProfileOverrideCompactionCount;
persistedCliSessionIds = entry.cliSessionIds;
persistedCliSessionBindings = entry.cliSessionBindings;
persistedClaudeCliSessionId = entry.claudeCliSessionId;
// Explicit /new and /reset should rotate the underlying CLI conversation too.
// Keep the model/auth choice, but force the next turn to mint a fresh CLI binding.
persistedLabel = entry.label;
persistedSpawnedBy = entry.spawnedBy;
persistedSpawnedWorkspaceDir = entry.spawnedWorkspaceDir;
@ -573,9 +569,9 @@ export async function initSessionState(params: {
persistedAuthProfileOverrideSource ?? baseEntry?.authProfileOverrideSource,
authProfileOverrideCompactionCount:
persistedAuthProfileOverrideCompactionCount ?? baseEntry?.authProfileOverrideCompactionCount,
cliSessionIds: persistedCliSessionIds ?? baseEntry?.cliSessionIds,
cliSessionBindings: persistedCliSessionBindings ?? baseEntry?.cliSessionBindings,
claudeCliSessionId: persistedClaudeCliSessionId ?? baseEntry?.claudeCliSessionId,
cliSessionIds: baseEntry?.cliSessionIds,
cliSessionBindings: baseEntry?.cliSessionBindings,
claudeCliSessionId: baseEntry?.claudeCliSessionId,
label: persistedLabel ?? baseEntry?.label,
spawnedBy: persistedSpawnedBy ?? baseEntry?.spawnedBy,
spawnedWorkspaceDir: persistedSpawnedWorkspaceDir ?? baseEntry?.spawnedWorkspaceDir,