fix(agents): persist claude cli session ids

This commit is contained in:
Vincent Koc 2026-04-05 14:35:11 +01:00
parent 28955a36e7
commit 84eb617a79
3 changed files with 87 additions and 24 deletions

View File

@ -0,0 +1,73 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import { loadSessionStore, type SessionEntry } from "../../config/sessions.js";
import type { EmbeddedPiRunResult } from "../pi-embedded.js";
import { updateSessionStoreAfterAgentRun } from "./session-store.js";
describe("updateSessionStoreAfterAgentRun", () => {
let tmpDir: string;
let storePath: string;
beforeEach(async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-session-store-"));
storePath = path.join(tmpDir, "sessions.json");
});
afterEach(async () => {
await fs.rm(tmpDir, { recursive: true, force: true });
});
it("persists claude-cli session bindings without explicit cliBackends config", async () => {
const cfg = {} as OpenClawConfig;
const sessionKey = "agent:main:explicit:test-claude-cli";
const sessionId = "test-openclaw-session";
const sessionStore: Record<string, SessionEntry> = {
[sessionKey]: {
sessionId,
updatedAt: 1,
},
};
await fs.writeFile(storePath, JSON.stringify(sessionStore, null, 2));
const result: EmbeddedPiRunResult = {
meta: {
durationMs: 1,
agentMeta: {
sessionId: "cli-session-123",
provider: "claude-cli",
model: "claude-sonnet-4-6",
cliSessionBinding: {
sessionId: "cli-session-123",
},
},
},
};
await updateSessionStoreAfterAgentRun({
cfg,
sessionId,
sessionKey,
storePath,
sessionStore,
defaultProvider: "claude-cli",
defaultModel: "claude-sonnet-4-6",
result,
});
expect(sessionStore[sessionKey]?.cliSessionBindings?.["claude-cli"]).toEqual({
sessionId: "cli-session-123",
});
expect(sessionStore[sessionKey]?.cliSessionIds?.["claude-cli"]).toBe("cli-session-123");
expect(sessionStore[sessionKey]?.claudeCliSessionId).toBe("cli-session-123");
const persisted = loadSessionStore(storePath);
expect(persisted[sessionKey]?.cliSessionBindings?.["claude-cli"]).toEqual({
sessionId: "cli-session-123",
});
expect(persisted[sessionKey]?.cliSessionIds?.["claude-cli"]).toBe("cli-session-123");
expect(persisted[sessionKey]?.claudeCliSessionId).toBe("cli-session-123");
});
});

View File

@ -4,6 +4,7 @@ import { resetLogger, setLoggerOverride } from "../logging/logger.js";
import {
buildAllowedModelSet,
inferUniqueProviderFromConfiguredModels,
isCliProvider,
parseModelRef,
buildModelAliasIndex,
normalizeModelSelection,
@ -130,6 +131,12 @@ describe("model-selection", () => {
});
});
describe("isCliProvider", () => {
it("treats claude-cli as a CLI provider even without explicit cliBackends config", () => {
expect(isCliProvider("claude-cli", {} as OpenClawConfig)).toBe(true);
});
});
describe("modelKey", () => {
it("keeps canonical OpenRouter native ids without duplicating the provider", () => {
expect(modelKey("openrouter", "openrouter/hunter-alpha")).toBe("openrouter/hunter-alpha");

View File

@ -1,3 +1,4 @@
import { CLAUDE_CLI_BACKEND_ID } from "../../extensions/anthropic/cli-backend-api.js";
import { resolveThinkingDefaultForModel } from "../auto-reply/thinking.shared.js";
import type { OpenClawConfig } from "../config/config.js";
import {
@ -6,6 +7,7 @@ import {
toAgentModelListLike,
} from "../config/model-input.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveRuntimeCliBackends } from "../plugins/cli-backends.runtime.js";
import { sanitizeForLog, stripAnsi } from "../terminal/ansi.js";
import {
resolveAgentConfig,
@ -27,29 +29,7 @@ import { normalizeProviderModelIdWithRuntime } from "./provider-model-normalizat
let log: ReturnType<typeof createSubsystemLogger> | null = null;
type CliBackendRuntimeModule = typeof import("../plugins/cli-backends.runtime.js");
const CLI_BACKEND_RUNTIME_CANDIDATES = [
"../plugins/cli-backends.runtime.js",
"../plugins/cli-backends.runtime.ts",
] as const;
let cliBackendRuntimeModule: CliBackendRuntimeModule | undefined;
function loadCliBackendRuntime(): CliBackendRuntimeModule | null {
if (cliBackendRuntimeModule) {
return cliBackendRuntimeModule;
}
for (const candidate of CLI_BACKEND_RUNTIME_CANDIDATES) {
try {
cliBackendRuntimeModule = require(candidate) as CliBackendRuntimeModule;
return cliBackendRuntimeModule;
} catch {
// Try source/runtime candidates in order.
}
}
return null;
}
const BUILTIN_CLI_PROVIDER_IDS = new Set([normalizeProviderId(CLAUDE_CLI_BACKEND_ID)]);
function getLog(): ReturnType<typeof createSubsystemLogger> {
log ??= createSubsystemLogger("model-selection");
@ -112,7 +92,10 @@ export {
export function isCliProvider(provider: string, cfg?: OpenClawConfig): boolean {
const normalized = normalizeProviderId(provider);
const cliBackends = loadCliBackendRuntime()?.resolveRuntimeCliBackends() ?? [];
if (BUILTIN_CLI_PROVIDER_IDS.has(normalized)) {
return true;
}
const cliBackends = resolveRuntimeCliBackends();
if (cliBackends.some((backend) => normalizeProviderId(backend.id) === normalized)) {
return true;
}