mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-08 19:12:22 +00:00
fix(acpx): normalize Claude ACP model refs
This commit is contained in:
@@ -224,6 +224,56 @@ describe("AcpxRuntime fresh reset wrapper", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("strips the OpenClaw Anthropic provider prefix for Claude ACP startup", async () => {
|
||||
const baseStore: TestSessionStore = {
|
||||
load: vi.fn(async () => undefined),
|
||||
save: vi.fn(async () => {}),
|
||||
};
|
||||
const { runtime, delegate } = makeRuntime(baseStore, {
|
||||
agentRegistry: {
|
||||
resolve: (agentName: string) =>
|
||||
agentName === "claude" ? "npx @agentclientprotocol/claude-agent-acp" : agentName,
|
||||
list: () => ["claude", "openclaw"],
|
||||
},
|
||||
});
|
||||
const ensure = vi.spyOn(delegate, "ensureSession").mockResolvedValue({
|
||||
sessionKey: "agent:claude:acp:test",
|
||||
backend: "acpx",
|
||||
runtimeSessionName: "claude",
|
||||
});
|
||||
|
||||
await runtime.ensureSession({
|
||||
sessionKey: "agent:claude:acp:test",
|
||||
agent: "claude",
|
||||
mode: "persistent",
|
||||
model: "anthropic/claude-sonnet-4-6",
|
||||
});
|
||||
|
||||
expect(readFirstEnsureSessionInput(ensure)).toEqual({
|
||||
sessionKey: "agent:claude:acp:test",
|
||||
agent: "claude",
|
||||
mode: "persistent",
|
||||
model: "claude-sonnet-4-6",
|
||||
sessionOptions: { model: "claude-sonnet-4-6" },
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps Claude ACP model ids intact after stripping the OpenClaw provider prefix", () => {
|
||||
expect(testing.normalizeClaudeAcpModelOverride("anthropic/claude-sonnet-4-6")).toBe(
|
||||
"claude-sonnet-4-6",
|
||||
);
|
||||
expect(testing.normalizeClaudeAcpModelOverride("anthropic/claude-opus-4-8")).toBe(
|
||||
"claude-opus-4-8",
|
||||
);
|
||||
expect(testing.normalizeClaudeAcpModelOverride("anthropic/claude-haiku-4-5")).toBe(
|
||||
"claude-haiku-4-5",
|
||||
);
|
||||
expect(testing.normalizeClaudeAcpModelOverride("anthropic/claude-sonnet-4-6-1m")).toBe(
|
||||
"claude-sonnet-4-6-1m",
|
||||
);
|
||||
expect(testing.normalizeClaudeAcpModelOverride("custom-model")).toBe("custom-model");
|
||||
});
|
||||
|
||||
it("leaves Codex ACP startup defaults alone when no model or thinking is provided", async () => {
|
||||
const baseStore: TestSessionStore = {
|
||||
load: vi.fn(async () => undefined),
|
||||
@@ -898,7 +948,7 @@ describe("AcpxRuntime fresh reset wrapper", () => {
|
||||
expect(setConfigOption).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("still forwards non-timeout config controls for claude-agent-acp", async () => {
|
||||
it("normalizes model config controls for claude-agent-acp", async () => {
|
||||
const baseStore: TestSessionStore = {
|
||||
load: vi.fn(async () => ({
|
||||
acpxRecordId: "agent:claude:acp:test",
|
||||
@@ -918,14 +968,14 @@ describe("AcpxRuntime fresh reset wrapper", () => {
|
||||
await runtime.setConfigOption({
|
||||
handle,
|
||||
key: "model",
|
||||
value: "claude-sonnet-4.6",
|
||||
value: "anthropic/claude-sonnet-4-6",
|
||||
});
|
||||
|
||||
expect(setConfigOption).toHaveBeenCalledOnce();
|
||||
expect(setConfigOption).toHaveBeenCalledWith({
|
||||
handle,
|
||||
key: "model",
|
||||
value: "claude-sonnet-4.6",
|
||||
value: "claude-sonnet-4-6",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -329,6 +329,7 @@ const OPENCLAW_BRIDGE_EXECUTABLE = "openclaw";
|
||||
const OPENCLAW_BRIDGE_SUBCOMMAND = "acp";
|
||||
const CODEX_ACP_AGENT_ID = "codex";
|
||||
const CODEX_ACP_OPENCLAW_PREFIX = "openai/";
|
||||
const CLAUDE_ACP_OPENCLAW_PREFIX = "anthropic/";
|
||||
const CODEX_ACP_REASONING_EFFORTS = new Set(["low", "medium", "high", "xhigh"]);
|
||||
const CODEX_ACP_THINKING_ALIASES = new Map<string, string | undefined>([
|
||||
["off", undefined],
|
||||
@@ -564,6 +565,17 @@ function codexAcpSessionModelId(override: CodexAcpModelOverride): string {
|
||||
: override.model;
|
||||
}
|
||||
|
||||
function normalizeClaudeAcpModelOverride(rawModel: string | undefined): string | undefined {
|
||||
const raw = rawModel?.trim();
|
||||
if (!raw) {
|
||||
return undefined;
|
||||
}
|
||||
if (!raw.toLowerCase().startsWith(CLAUDE_ACP_OPENCLAW_PREFIX)) {
|
||||
return raw;
|
||||
}
|
||||
return raw.slice(CLAUDE_ACP_OPENCLAW_PREFIX.length).trim() || undefined;
|
||||
}
|
||||
|
||||
function withAcpxSessionOptions(input: OpenClawRuntimeEnsureInput): AcpxDelegateEnsureInput {
|
||||
const existingOptions = (input as { sessionOptions?: SessionAgentOptions }).sessionOptions;
|
||||
const model = input.model?.trim() || existingOptions?.model;
|
||||
@@ -948,10 +960,14 @@ export class AcpxRuntime implements AcpRuntime {
|
||||
agentRegistry: this.agentRegistry,
|
||||
});
|
||||
const delegate = this.resolveDelegateForCommand(command);
|
||||
const claudeModelOverride = isClaudeAcpCommand(command)
|
||||
? normalizeClaudeAcpModelOverride(input.model)
|
||||
: undefined;
|
||||
const codexModelOverride =
|
||||
normalizeAgentName(input.agent) === CODEX_ACP_AGENT_ID && isCodexAcpCommand(command)
|
||||
? normalizeCodexAcpModelOverride(input.model, input.thinking)
|
||||
: undefined;
|
||||
const ensureInput = claudeModelOverride ? { ...input, model: claudeModelOverride } : input;
|
||||
const stableLaunchCommand =
|
||||
codexModelOverride && command
|
||||
? appendCodexAcpConfigOverrides(command, codexModelOverride)
|
||||
@@ -966,20 +982,20 @@ export class AcpxRuntime implements AcpRuntime {
|
||||
|
||||
if (!codexModelOverride) {
|
||||
return await this.runWithLaunchLease({
|
||||
sessionKey: input.sessionKey,
|
||||
sessionKey: ensureInput.sessionKey,
|
||||
command: stableLaunchCommand,
|
||||
enabled: shouldStartWithLease,
|
||||
run: () =>
|
||||
this.withCodexWrapperDiagnostics({
|
||||
command: stableLaunchCommand,
|
||||
fallbackCode: "ACP_SESSION_INIT_FAILED",
|
||||
run: () => delegate.ensureSession(withAcpxSessionOptions(input)),
|
||||
run: () => delegate.ensureSession(withAcpxSessionOptions(ensureInput)),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const normalizedInput = {
|
||||
...input,
|
||||
...ensureInput,
|
||||
...(codexAcpSessionModelId(codexModelOverride)
|
||||
? { model: codexAcpSessionModelId(codexModelOverride) }
|
||||
: {}),
|
||||
@@ -1208,6 +1224,13 @@ export class AcpxRuntime implements AcpRuntime {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isClaudeAcpCommand(command) && key === "model") {
|
||||
await delegate.setConfigOption({
|
||||
...input,
|
||||
value: normalizeClaudeAcpModelOverride(input.value) ?? input.value,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await delegate.setConfigOption(input);
|
||||
}
|
||||
|
||||
@@ -1260,6 +1283,7 @@ export const testing = {
|
||||
codexAcpSessionModelId,
|
||||
isClaudeAcpCommand,
|
||||
isCodexAcpCommand,
|
||||
normalizeClaudeAcpModelOverride,
|
||||
normalizeCodexAcpModelOverride,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user