openclaw / src /acp /control-plane /manager.runtime-config-validation.test.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
f778c12 verified
Raw History Blame Contribute Delete
8.58 kB
/** Tests ACP runtime config validation and backend-applied model persistence. */
import { describe, expect, it } from "vitest";
import {
AcpSessionManager,
baseCfg,
createRuntime,
expectMockCallFields,
expectNoMockCallFields,
expectRejectedRecord,
hoisted,
installAcpSessionManagerTestLifecycle,
readySessionMeta,
type SessionAcpMeta,
} from "./manager.test-helpers.js";
describe("AcpSessionManager runtime config validation", () => {
installAcpSessionManagerTestLifecycle();
it("rejects invalid runtime option values before backend controls run", async () => {
const runtimeState = createRuntime();
hoisted.requireAcpRuntimeBackendMock.mockReturnValue({
id: "acpx",
runtime: runtimeState.runtime,
});
hoisted.readAcpSessionEntryMock.mockReturnValue({
sessionKey: "agent:codex:acp:session-1",
storeSessionKey: "agent:codex:acp:session-1",
acp: readySessionMeta(),
});
const manager = new AcpSessionManager();
await expectRejectedRecord(
manager.setSessionConfigOption({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
key: "timeout",
value: "not-a-number",
}),
{ code: "ACP_INVALID_RUNTIME_OPTION" },
);
expect(runtimeState.setConfigOption).not.toHaveBeenCalled();
await expectRejectedRecord(
manager.updateSessionRuntimeOptions({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
patch: { cwd: "relative/path" },
}),
{ code: "ACP_INVALID_RUNTIME_OPTION" },
);
});
it("never replays an inherited non-openai default that the backend dropped at session init", async () => {
const sessionKey = "agent:codex:acp:session-dropped-default";
const runtimeState = createRuntime();
runtimeState.ensureSession.mockImplementation(async (input) => ({
sessionKey: input.sessionKey,
backend: "acpx",
runtimeSessionName: `${input.sessionKey}:runtime`,
appliedModel: { kind: "dropped" as const },
}));
hoisted.requireAcpRuntimeBackendMock.mockReturnValue({
id: "acpx",
runtime: runtimeState.runtime,
});
let persistedMeta: SessionAcpMeta | undefined;
hoisted.upsertAcpSessionMetaMock.mockImplementation(
async (payload: {
mutate: (current: SessionAcpMeta | undefined) => SessionAcpMeta | null | undefined;
}) => {
persistedMeta = payload.mutate(undefined) ?? undefined;
return persistedMeta
? { sessionKey, storeSessionKey: sessionKey, acp: persistedMeta }
: null;
},
);
const manager = new AcpSessionManager();
await manager.initializeSession({
cfg: baseCfg,
sessionKey,
agent: "codex",
mode: "persistent",
runtimeOptions: {
model: "google/gemini-3.1-flash-lite",
thinking: "low",
},
});
expect(persistedMeta?.runtimeOptions).toEqual({ thinking: "low" });
hoisted.readAcpSessionEntryMock.mockReturnValue({
sessionKey,
storeSessionKey: sessionKey,
acp: persistedMeta as SessionAcpMeta,
});
await manager.runTurn({
cfg: baseCfg,
sessionKey,
text: "do work",
mode: "prompt",
requestId: "run-dropped-default",
provenance: "system",
});
expectNoMockCallFields(runtimeState.setConfigOption, { key: "model" });
expectMockCallFields(runtimeState.setConfigOption, { key: "thinking", value: "low" });
expect(runtimeState.runTurn).toHaveBeenCalledTimes(1);
});
it("persists and replays a supported codex model the backend applied at session init", async () => {
const sessionKey = "agent:codex:acp:session-applied-model";
const runtimeState = createRuntime();
runtimeState.ensureSession.mockImplementation(async (input) => ({
sessionKey: input.sessionKey,
backend: "acpx",
runtimeSessionName: `${input.sessionKey}:runtime`,
appliedModel: { kind: "applied" as const, model: input.model ?? "" },
}));
hoisted.requireAcpRuntimeBackendMock.mockReturnValue({
id: "acpx",
runtime: runtimeState.runtime,
});
let persistedMeta: SessionAcpMeta | undefined;
hoisted.upsertAcpSessionMetaMock.mockImplementation(
async (payload: {
mutate: (current: SessionAcpMeta | undefined) => SessionAcpMeta | null | undefined;
}) => {
persistedMeta = payload.mutate(undefined) ?? undefined;
return persistedMeta
? { sessionKey, storeSessionKey: sessionKey, acp: persistedMeta }
: null;
},
);
const manager = new AcpSessionManager();
await manager.initializeSession({
cfg: baseCfg,
sessionKey,
agent: "codex",
mode: "persistent",
runtimeOptions: {
model: "openai/gpt-5.5",
},
});
expect(persistedMeta?.runtimeOptions).toEqual({ model: "openai/gpt-5.5" });
hoisted.readAcpSessionEntryMock.mockReturnValue({
sessionKey,
storeSessionKey: sessionKey,
acp: persistedMeta as SessionAcpMeta,
});
await manager.runTurn({
cfg: baseCfg,
sessionKey,
text: "do work",
mode: "prompt",
requestId: "run-applied-model",
provenance: "system",
});
expectMockCallFields(runtimeState.setConfigOption, {
key: "model",
value: "openai/gpt-5.5",
});
expect(runtimeState.runTurn).toHaveBeenCalledTimes(1);
});
it.each(["Invalid value for config option effort: off", "Unknown config option: effort"])(
"continues turns when adapters reject automatic thinking: %s",
async (details) => {
const runtimeState = createRuntime();
runtimeState.getCapabilities.mockResolvedValue({
controls: ["session/set_mode", "session/set_config_option", "session/status"],
configOptionKeys: ["mode", "model", "effort"],
});
runtimeState.setConfigOption.mockImplementation(async (input: { key: string }) => {
if (input.key === "effort") {
throw Object.assign(new Error("Internal error"), {
name: "RequestError",
code: -32603,
data: { details },
});
}
});
hoisted.requireAcpRuntimeBackendMock.mockReturnValue({
id: "acpx",
runtime: runtimeState.runtime,
});
hoisted.readAcpSessionEntryMock.mockReturnValue({
sessionKey: "agent:claude:acp:session-1",
storeSessionKey: "agent:claude:acp:session-1",
acp: {
...readySessionMeta({ agent: "claude" }),
runtimeOptions: {
thinking: "off",
},
},
});
const manager = new AcpSessionManager();
await manager.runTurn({
provenance: "system",
cfg: baseCfg,
sessionKey: "agent:claude:acp:session-1",
text: "do work",
mode: "prompt",
requestId: "run-1",
});
expectMockCallFields(runtimeState.setConfigOption, {
key: "effort",
value: "off",
});
expect(runtimeState.runTurn).toHaveBeenCalledTimes(1);
},
);
it("fails turns when thinking config writes hit runtime failures", async () => {
const runtimeState = createRuntime();
runtimeState.getCapabilities.mockResolvedValue({
controls: ["session/set_mode", "session/set_config_option", "session/status"],
configOptionKeys: ["mode", "model", "effort"],
});
runtimeState.setConfigOption.mockImplementation(async (input: { key: string }) => {
if (input.key === "effort") {
throw Object.assign(new Error("Internal error"), {
name: "RequestError",
code: -32603,
data: { details: "unsupported transport protocol" },
});
}
});
hoisted.requireAcpRuntimeBackendMock.mockReturnValue({
id: "acpx",
runtime: runtimeState.runtime,
});
hoisted.readAcpSessionEntryMock.mockReturnValue({
sessionKey: "agent:claude:acp:session-1",
storeSessionKey: "agent:claude:acp:session-1",
acp: {
...readySessionMeta({ agent: "claude" }),
runtimeOptions: {
thinking: "off",
},
},
});
const manager = new AcpSessionManager();
await expectRejectedRecord(
manager.runTurn({
provenance: "system",
cfg: baseCfg,
sessionKey: "agent:claude:acp:session-1",
text: "do work",
mode: "prompt",
requestId: "run-1",
}),
{
code: "ACP_TURN_FAILED",
},
);
expect(runtimeState.runTurn).not.toHaveBeenCalled();
});
});