test: clear acp manager broad matchers

This commit is contained in:
Peter Steinberger
2026-05-10 06:22:55 +01:00
parent f1ced1961a
commit 74dc2a6830

View File

@@ -97,6 +97,60 @@ function createDeferred(): { promise: Promise<void>; resolve: () => void } {
return { promise, resolve };
}
function expectRecordFields(record: unknown, expected: Record<string, unknown>) {
expect(record).toBeDefined();
const actual = record as Record<string, unknown>;
for (const [key, value] of Object.entries(expected)) {
expect(actual[key]).toEqual(value);
}
return actual;
}
async function expectRejectedRecord(promise: Promise<unknown>, expected: Record<string, unknown>) {
await promise.then(
() => {
throw new Error("Expected promise to reject.");
},
(error) => {
expectRecordFields(error, expected);
},
);
}
function mockCallArg(mock: ReturnType<typeof vi.fn>, callIndex = 0): Record<string, unknown> {
const call = mock.mock.calls[callIndex];
if (!call) {
throw new Error(`Expected mock call ${callIndex}`);
}
return call[0] as Record<string, unknown>;
}
function mockCallArgs(mock: ReturnType<typeof vi.fn>): Array<Record<string, unknown>> {
return mock.mock.calls.map((call) => call[0] as Record<string, unknown>);
}
function findMockCallFields(mock: ReturnType<typeof vi.fn>, expected: Record<string, unknown>) {
return mockCallArgs(mock).find((actual) =>
Object.entries(expected).every(([key, value]) => Object.is(actual[key], value)),
);
}
function expectMockCallFields(mock: ReturnType<typeof vi.fn>, expected: Record<string, unknown>) {
expect(findMockCallFields(mock, expected)).toBeDefined();
}
function expectNoMockCallFields(mock: ReturnType<typeof vi.fn>, expected: Record<string, unknown>) {
expect(findMockCallFields(mock, expected)).toBeUndefined();
}
function requireTaskByRunId(runId: string) {
const task = findTaskByRunId(runId);
if (!task) {
throw new Error(`Expected task for run ${runId}`);
}
return task;
}
function createRuntime(): {
runtime: AcpRuntime;
ensureSession: ReturnType<typeof vi.fn>;
@@ -295,18 +349,14 @@ describe("AcpSessionManager", () => {
requestId: "r-main",
});
expect(hoisted.readAcpSessionEntryMock).toHaveBeenCalledWith(
expect.objectContaining({
cfg,
sessionKey: "agent:main:main",
}),
);
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
agent: "main",
sessionKey: "agent:main:main",
}),
);
expectRecordFields(mockCallArg(hoisted.readAcpSessionEntryMock), {
cfg,
sessionKey: "agent:main:main",
});
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
agent: "main",
sessionKey: "agent:main:main",
});
});
it("tracks parented direct ACP turns in the task registry", async () => {
@@ -387,7 +437,7 @@ describe("AcpSessionManager", () => {
});
await flushMicrotasks();
expect(findTaskByRunId("direct-parented-run")).toMatchObject({
expectRecordFields(requireTaskByRunId("direct-parented-run"), {
runtime: "acp",
ownerKey: "agent:quant:telegram:quant:direct:822430204",
scopeKind: "session",
@@ -473,7 +523,7 @@ describe("AcpSessionManager", () => {
});
await flushMicrotasks();
expect(findTaskByRunId("direct-parented-korean-path-run")).toMatchObject({
expectRecordFields(requireTaskByRunId("direct-parented-korean-path-run"), {
runtime: "acp",
ownerKey: "agent:quant:telegram:quant:direct:822430204",
scopeKind: "session",
@@ -618,7 +668,7 @@ describe("AcpSessionManager", () => {
return;
}
expect(secondOutcome.error).toBeInstanceOf(AcpRuntimeError);
expect(secondOutcome.error).toMatchObject({
expectRecordFields(secondOutcome.error, {
code: "ACP_TURN_FAILED",
message: "ACP operation aborted.",
});
@@ -683,7 +733,7 @@ describe("AcpSessionManager", () => {
await vi.advanceTimersByTimeAsync(3_500);
await expect(first).rejects.toMatchObject({
await expectRejectedRecord(first, {
code: "ACP_TURN_FAILED",
message: "ACP turn timed out after 1s.",
});
@@ -691,22 +741,17 @@ describe("AcpSessionManager", () => {
expect(runtimeState.ensureSession).toHaveBeenCalledTimes(1);
expect(runtimeState.runTurn).toHaveBeenCalledTimes(2);
expect(runtimeState.cancel).toHaveBeenCalledWith(
expect.objectContaining({
reason: "turn-timeout",
}),
);
expectRecordFields(mockCallArg(runtimeState.cancel), {
reason: "turn-timeout",
});
expect(runtimeState.close).not.toHaveBeenCalled();
expect(manager.getObservabilitySnapshot(cfg)).toMatchObject({
runtimeCache: {
activeSessions: 1,
},
turns: {
active: 0,
queueDepth: 0,
completed: 1,
failed: 1,
},
const snapshot = manager.getObservabilitySnapshot(cfg);
expect(snapshot.runtimeCache.activeSessions).toBe(1);
expectRecordFields(snapshot.turns, {
active: 0,
queueDepth: 0,
completed: 1,
failed: 1,
});
const states = extractStatesFromUpserts();
@@ -778,13 +823,13 @@ describe("AcpSessionManager", () => {
await vi.advanceTimersByTimeAsync(4_500);
await expect(first).rejects.toMatchObject({
await expectRejectedRecord(first, {
code: "ACP_TURN_FAILED",
message: "ACP turn timed out after 1s.",
});
expect(manager.getObservabilitySnapshot(cfg).runtimeCache.activeSessions).toBe(1);
await expect(
await expectRejectedRecord(
manager.runTurn({
cfg,
sessionKey: "agent:codex:acp:session-b",
@@ -792,10 +837,11 @@ describe("AcpSessionManager", () => {
mode: "prompt",
requestId: "r2",
}),
).rejects.toMatchObject({
code: "ACP_SESSION_INIT_FAILED",
message: expect.stringContaining("max concurrent sessions"),
});
{
code: "ACP_SESSION_INIT_FAILED",
message: "ACP max concurrent sessions reached (1/1).",
},
);
expect(runtimeState.ensureSession).toHaveBeenCalledTimes(1);
} finally {
vi.useRealTimers();
@@ -1080,13 +1126,11 @@ describe("AcpSessionManager", () => {
requestId: "r-binding-restart",
});
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey,
agent: "codex",
resumeSessionId: "acpx-sid-1",
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey,
agent: "codex",
resumeSessionId: "acpx-sid-1",
});
});
it("prefers the persisted agent session id when reopening an ACP runtime after restart", async () => {
@@ -1125,13 +1169,11 @@ describe("AcpSessionManager", () => {
requestId: "r-binding-restart-gemini",
});
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey,
agent: "gemini",
resumeSessionId: "gemini-sid-1",
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey,
agent: "gemini",
resumeSessionId: "gemini-sid-1",
});
});
it("passes persisted cwd runtime options into ensureSession after restart", async () => {
@@ -1165,12 +1207,10 @@ describe("AcpSessionManager", () => {
requestId: "r-binding-restart-cwd",
});
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey,
cwd: "/workspace/project",
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey,
cwd: "/workspace/project",
});
});
it("passes persisted model runtime options into ensureSession after restart", async () => {
@@ -1203,12 +1243,10 @@ describe("AcpSessionManager", () => {
requestId: "r-binding-restart-model",
});
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey,
model: "openai-codex/gpt-5.4",
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey,
model: "openai-codex/gpt-5.4",
});
});
it("passes persisted thinking runtime options into ensureSession after restart", async () => {
@@ -1241,12 +1279,10 @@ describe("AcpSessionManager", () => {
requestId: "r-binding-restart-thinking",
});
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey,
thinking: "high",
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey,
thinking: "high",
});
});
it("does not resume persisted ACP identity for oneshot sessions after restart", async () => {
@@ -1288,7 +1324,7 @@ describe("AcpSessionManager", () => {
const ensureInput = runtimeState.ensureSession.mock.calls[0]?.[0] as
| { resumeSessionId?: string; mode?: string }
| undefined;
expect(ensureInput).toMatchObject({
expectRecordFields(ensureInput, {
sessionKey,
agent: "codex",
mode: "oneshot",
@@ -1375,7 +1411,7 @@ describe("AcpSessionManager", () => {
});
expect(runtimeState.ensureSession).toHaveBeenCalledTimes(2);
expect(runtimeState.ensureSession.mock.calls[0]?.[0]).toMatchObject({
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey,
agent: "codex",
resumeSessionId: "agent-sid-stale",
@@ -1426,7 +1462,7 @@ describe("AcpSessionManager", () => {
requestId: "r1",
});
await expect(
await expectRejectedRecord(
manager.runTurn({
cfg: limitedCfg,
sessionKey: "agent:codex:acp:session-b",
@@ -1434,10 +1470,11 @@ describe("AcpSessionManager", () => {
mode: "prompt",
requestId: "r2",
}),
).rejects.toMatchObject({
code: "ACP_SESSION_INIT_FAILED",
message: expect.stringContaining("max concurrent sessions"),
});
{
code: "ACP_SESSION_INIT_FAILED",
message: "ACP max concurrent sessions reached (1/1).",
},
);
expect(runtimeState.ensureSession).toHaveBeenCalledTimes(1);
});
@@ -1467,17 +1504,18 @@ describe("AcpSessionManager", () => {
mode: "persistent",
});
await expect(
await expectRejectedRecord(
manager.initializeSession({
cfg: limitedCfg,
sessionKey: "agent:codex:acp:session-b",
agent: "codex",
mode: "persistent",
}),
).rejects.toMatchObject({
code: "ACP_SESSION_INIT_FAILED",
message: expect.stringContaining("max concurrent sessions"),
});
{
code: "ACP_SESSION_INIT_FAILED",
message: "ACP max concurrent sessions reached (1/1).",
},
);
expect(runtimeState.ensureSession).toHaveBeenCalledTimes(1);
});
@@ -1514,13 +1552,11 @@ describe("AcpSessionManager", () => {
model: "openai-codex/gpt-5.4",
thinking: "high",
});
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey: "agent:codex:acp:session-a",
model: "openai-codex/gpt-5.4",
thinking: "high",
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey: "agent:codex:acp:session-a",
model: "openai-codex/gpt-5.4",
thinking: "high",
});
});
it("preserves runtimeOptions cwd when initializeSession cwd is omitted", async () => {
@@ -1551,12 +1587,10 @@ describe("AcpSessionManager", () => {
},
});
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey: "agent:codex:acp:session-cwd-runtime-options",
cwd: "/workspace/from-runtime-options",
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey: "agent:codex:acp:session-cwd-runtime-options",
cwd: "/workspace/from-runtime-options",
});
expect(extractRuntimeOptionsFromUpserts()).toContainEqual({
cwd: "/workspace/from-runtime-options",
});
@@ -1760,7 +1794,7 @@ describe("AcpSessionManager", () => {
expect(result.runtimeClosed).toBe(true);
expect(entry.acp?.state).toBe("idle");
expect(entry.acp?.lastError).toBeUndefined();
expect(entry.acp?.identity).toMatchObject({
expectRecordFields(entry.acp?.identity, {
state: "pending",
acpxRecordId: sessionKey,
source: "status",
@@ -1833,11 +1867,9 @@ describe("AcpSessionManager", () => {
expect(runtimeState.prepareFreshSession).toHaveBeenCalledWith({
sessionKey,
});
expect(runtimeState.ensureSession).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey,
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey,
});
expect(runtimeState.prepareFreshSession.mock.invocationCallOrder[0]).toBeLessThan(
runtimeState.ensureSession.mock.invocationCallOrder[0],
);
@@ -1898,7 +1930,7 @@ describe("AcpSessionManager", () => {
});
expect(runtimeState.ensureSession).not.toHaveBeenCalled();
expect(runtimeState.close).not.toHaveBeenCalled();
expect(entry.acp?.identity).toMatchObject({
expectRecordFields(entry.acp?.identity, {
state: "pending",
acpxRecordId: sessionKey,
source: "ensure",
@@ -1956,14 +1988,13 @@ describe("AcpSessionManager", () => {
});
expect(runtimeState.ensureSession).toHaveBeenCalledTimes(2);
expect(runtimeState.close).toHaveBeenCalledWith(
expect.objectContaining({
reason: "idle-evicted",
handle: expect.objectContaining({
sessionKey: "agent:codex:acp:session-a",
}),
}),
);
const closeInput = mockCallArg(runtimeState.close);
expectRecordFields(closeInput, {
reason: "idle-evicted",
});
expectRecordFields(closeInput.handle, {
sessionKey: "agent:codex:acp:session-a",
});
} finally {
vi.useRealTimers();
}
@@ -2001,7 +2032,7 @@ describe("AcpSessionManager", () => {
mode: "prompt",
requestId: "ok",
});
await expect(
await expectRejectedRecord(
manager.runTurn({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
@@ -2009,9 +2040,8 @@ describe("AcpSessionManager", () => {
mode: "prompt",
requestId: "fail",
}),
).rejects.toMatchObject({
code: "ACP_TURN_FAILED",
});
{ code: "ACP_TURN_FAILED" },
);
const snapshot = manager.getObservabilitySnapshot(baseCfg);
expect(snapshot.turns.completed).toBe(1);
@@ -2038,14 +2068,13 @@ describe("AcpSessionManager", () => {
mode: "persistent",
}),
).rejects.toThrow("disk full");
expect(runtimeState.close).toHaveBeenCalledWith(
expect.objectContaining({
reason: "init-meta-failed",
handle: expect.objectContaining({
sessionKey: "agent:codex:acp:session-1",
}),
}),
);
const closeInput = mockCallArg(runtimeState.close);
expectRecordFields(closeInput, {
reason: "init-meta-failed",
});
expectRecordFields(closeInput.handle, {
sessionKey: "agent:codex:acp:session-1",
});
});
it("preempts an active turn on cancel and returns to idle state", async () => {
@@ -2096,11 +2125,9 @@ describe("AcpSessionManager", () => {
await runPromise;
expect(runtimeState.cancel).toHaveBeenCalledTimes(1);
expect(runtimeState.cancel).toHaveBeenCalledWith(
expect.objectContaining({
reason: "manual-cancel",
}),
);
expectRecordFields(mockCallArg(runtimeState.cancel), {
reason: "manual-cancel",
});
const states = extractStatesFromUpserts();
expect(states).toContain("running");
expect(states).toContain("idle");
@@ -2167,7 +2194,7 @@ describe("AcpSessionManager", () => {
});
const manager = new AcpSessionManager();
await expect(
await expectRejectedRecord(
manager.runTurn({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
@@ -2175,10 +2202,11 @@ describe("AcpSessionManager", () => {
mode: "prompt",
requestId: "run-1",
}),
).rejects.toMatchObject({
code: "ACP_TURN_FAILED",
message: "acpx exited with code 1",
});
{
code: "ACP_TURN_FAILED",
message: "acpx exited with code 1",
},
);
const states = extractStatesFromUpserts();
expect(states).toContain("running");
@@ -2206,7 +2234,7 @@ describe("AcpSessionManager", () => {
});
const manager = new AcpSessionManager();
await expect(
await expectRejectedRecord(
manager.runTurn({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
@@ -2214,10 +2242,11 @@ describe("AcpSessionManager", () => {
mode: "prompt",
requestId: "run-1",
}),
).rejects.toMatchObject({
code: "ACP_TURN_FAILED",
message: "ACP turn ended without a terminal done event.",
});
{
code: "ACP_TURN_FAILED",
message: "ACP turn ended without a terminal done event.",
},
);
const states = extractStatesFromUpserts();
expect(states).toContain("running");
@@ -2241,7 +2270,7 @@ describe("AcpSessionManager", () => {
});
const manager = new AcpSessionManager();
await expect(
await expectRejectedRecord(
manager.runTurn({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
@@ -2249,10 +2278,11 @@ describe("AcpSessionManager", () => {
mode: "prompt",
requestId: "run-1",
}),
).rejects.toMatchObject({
code: "ACP_SESSION_INIT_FAILED",
message: "acpx exited with code 1",
});
{
code: "ACP_SESSION_INIT_FAILED",
message: "acpx exited with code 1",
},
);
const states = extractStatesFromUpserts();
expect(states).not.toContain("running");
@@ -2395,7 +2425,7 @@ describe("AcpSessionManager", () => {
sessionKey,
});
expect(runtimeState.ensureSession).toHaveBeenCalledTimes(2);
expect(runtimeState.ensureSession.mock.calls[0]?.[0]).toMatchObject({
expectRecordFields(mockCallArg(runtimeState.ensureSession), {
sessionKey,
resumeSessionId: "acpx-sid-stale",
});
@@ -2430,11 +2460,9 @@ describe("AcpSessionManager", () => {
runtimeMode: "plan",
});
expect(runtimeState.setMode).toHaveBeenCalledWith(
expect.objectContaining({
mode: "plan",
}),
);
expectMockCallFields(runtimeState.setMode, {
mode: "plan",
});
expect(options.runtimeMode).toBe("plan");
const persistedRuntimeModes = extractRuntimeOptionsFromUpserts().map(
(entry) => entry?.runtimeMode,
@@ -2499,11 +2527,9 @@ describe("AcpSessionManager", () => {
requestId: "run-1",
});
expect(runtimeState.setMode).toHaveBeenCalledWith(
expect.objectContaining({
mode: "plan",
}),
);
expectMockCallFields(runtimeState.setMode, {
mode: "plan",
});
});
it("reconciles persisted ACP session identifiers from runtime status after a turn", async () => {
@@ -2632,7 +2658,7 @@ describe("AcpSessionManager", () => {
mode: "oneshot",
});
expect(currentMeta?.identity).toMatchObject({
expectRecordFields(currentMeta?.identity, {
state: "pending",
acpxSessionId: "acpx-oneshot",
source: "ensure",
@@ -2647,14 +2673,15 @@ describe("AcpSessionManager", () => {
});
expect(runtimeState.getStatus).toHaveBeenCalledTimes(2);
expect(runtimeState.close).toHaveBeenCalledWith({
handle: expect.objectContaining({
backendSessionId: "acpx-oneshot",
agentSessionId: "agent-oneshot",
}),
const closeInput = mockCallArg(runtimeState.close);
expectRecordFields(closeInput, {
reason: "oneshot-complete",
});
expect(currentMeta?.identity).toMatchObject({
expectRecordFields(closeInput.handle, {
backendSessionId: "acpx-oneshot",
agentSessionId: "agent-oneshot",
});
expectRecordFields(currentMeta?.identity, {
state: "resolved",
acpxSessionId: "acpx-oneshot",
agentSessionId: "agent-oneshot",
@@ -2974,35 +3001,25 @@ describe("AcpSessionManager", () => {
requestId: "run-1",
});
expect(runtimeState.setMode).toHaveBeenCalledWith(
expect.objectContaining({
mode: "plan",
}),
);
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "model",
value: "openai-codex/gpt-5.4",
}),
);
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "thinking",
value: "high",
}),
);
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "approval_policy",
value: "strict",
}),
);
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "timeout",
value: "120",
}),
);
expectMockCallFields(runtimeState.setMode, {
mode: "plan",
});
expectMockCallFields(runtimeState.setConfigOption, {
key: "model",
value: "openai-codex/gpt-5.4",
});
expectMockCallFields(runtimeState.setConfigOption, {
key: "thinking",
value: "high",
});
expectMockCallFields(runtimeState.setConfigOption, {
key: "approval_policy",
value: "strict",
});
expectMockCallFields(runtimeState.setConfigOption, {
key: "timeout",
value: "120",
});
});
it("maps persisted thinking runtime options to advertised effort config keys before running turns", async () => {
@@ -3035,17 +3052,13 @@ describe("AcpSessionManager", () => {
requestId: "run-1",
});
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "effort",
value: "high",
}),
);
expect(runtimeState.setConfigOption).not.toHaveBeenCalledWith(
expect.objectContaining({
key: "thinking",
}),
);
expectMockCallFields(runtimeState.setConfigOption, {
key: "effort",
value: "high",
});
expectNoMockCallFields(runtimeState.setConfigOption, {
key: "thinking",
});
});
it("maps persisted runtime options to backend-advertised aliases before running turns", async () => {
@@ -3081,39 +3094,27 @@ describe("AcpSessionManager", () => {
requestId: "run-1",
});
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "thought_level",
value: "high",
}),
);
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "permissions",
value: "strict",
}),
);
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "timeout_seconds",
value: "120",
}),
);
expect(runtimeState.setConfigOption).not.toHaveBeenCalledWith(
expect.objectContaining({
key: "thinking",
}),
);
expect(runtimeState.setConfigOption).not.toHaveBeenCalledWith(
expect.objectContaining({
key: "approval_policy",
}),
);
expect(runtimeState.setConfigOption).not.toHaveBeenCalledWith(
expect.objectContaining({
key: "timeout",
}),
);
expectMockCallFields(runtimeState.setConfigOption, {
key: "thought_level",
value: "high",
});
expectMockCallFields(runtimeState.setConfigOption, {
key: "permissions",
value: "strict",
});
expectMockCallFields(runtimeState.setConfigOption, {
key: "timeout_seconds",
value: "120",
});
expectNoMockCallFields(runtimeState.setConfigOption, {
key: "thinking",
});
expectNoMockCallFields(runtimeState.setConfigOption, {
key: "approval_policy",
});
expectNoMockCallFields(runtimeState.setConfigOption, {
key: "timeout",
});
});
it("re-ensures runtime handles after cwd runtime option updates", async () => {
@@ -3180,13 +3181,10 @@ describe("AcpSessionManager", () => {
});
expect(runtimeState.ensureSession).toHaveBeenCalledTimes(2);
expect(runtimeState.ensureSession).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
sessionKey,
cwd: "/workspace/next",
}),
);
expectRecordFields(mockCallArg(runtimeState.ensureSession, 1), {
sessionKey,
cwd: "/workspace/next",
});
});
it("returns unsupported-control error when backend does not support set_config_option", async () => {
@@ -3209,16 +3207,15 @@ describe("AcpSessionManager", () => {
});
const manager = new AcpSessionManager();
await expect(
await expectRejectedRecord(
manager.setSessionConfigOption({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
key: "model",
value: "gpt-5.4",
}),
).rejects.toMatchObject({
code: "ACP_BACKEND_UNSUPPORTED_CONTROL",
});
{ code: "ACP_BACKEND_UNSUPPORTED_CONTROL" },
);
});
it("maps explicit thinking config updates to advertised effort keys", async () => {
@@ -3245,12 +3242,10 @@ describe("AcpSessionManager", () => {
value: "high",
});
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "effort",
value: "high",
}),
);
expectMockCallFields(runtimeState.setConfigOption, {
key: "effort",
value: "high",
});
expect(nextOptions).toEqual({ thinking: "high" });
});
@@ -3284,12 +3279,10 @@ describe("AcpSessionManager", () => {
});
expect(runtimeState.getStatus).toHaveBeenCalled();
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "effort",
value: "high",
}),
);
expectMockCallFields(runtimeState.setConfigOption, {
key: "effort",
value: "high",
});
expect(nextOptions).toEqual({ thinking: "high" });
});
@@ -3317,12 +3310,10 @@ describe("AcpSessionManager", () => {
value: "high",
});
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "effort",
value: "high",
}),
);
expectMockCallFields(runtimeState.setConfigOption, {
key: "effort",
value: "high",
});
expect(nextOptions).toEqual({ thinking: "high" });
});
@@ -3350,12 +3341,10 @@ describe("AcpSessionManager", () => {
value: "strict",
});
expect(runtimeState.setConfigOption).toHaveBeenCalledWith(
expect.objectContaining({
key: "permission_mode",
value: "strict",
}),
);
expectMockCallFields(runtimeState.setConfigOption, {
key: "permission_mode",
value: "strict",
});
expect(nextOptions).toEqual({ permissionProfile: "strict" });
});
@@ -3372,27 +3361,25 @@ describe("AcpSessionManager", () => {
});
const manager = new AcpSessionManager();
await expect(
await expectRejectedRecord(
manager.setSessionConfigOption({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
key: "timeout",
value: "not-a-number",
}),
).rejects.toMatchObject({
code: "ACP_INVALID_RUNTIME_OPTION",
});
{ code: "ACP_INVALID_RUNTIME_OPTION" },
);
expect(runtimeState.setConfigOption).not.toHaveBeenCalled();
await expect(
await expectRejectedRecord(
manager.updateSessionRuntimeOptions({
cfg: baseCfg,
sessionKey: "agent:codex:acp:session-1",
patch: { cwd: "relative/path" },
}),
).rejects.toMatchObject({
code: "ACP_INVALID_RUNTIME_OPTION",
});
{ code: "ACP_INVALID_RUNTIME_OPTION" },
);
});
it("can close and clear metadata when backend is unavailable", async () => {