Skip to content

Commit 11de5c7

Browse files
committed
fix(agents): apply acp fast mode runtime override
1 parent 59aac23 commit 11de5c7

7 files changed

Lines changed: 82 additions & 8 deletions

File tree

packages/acp-core/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ export type AcpSessionRuntimeOptions = {
7777
permissionProfile?: string;
7878
/** ACP runtime config option: per-turn timeout in seconds. */
7979
timeoutSeconds?: number;
80+
/** ACP runtime config option: fast-mode toggle. */
81+
fastMode?: boolean;
8082
/** Backend-specific option bag mapped through session/set_config_option. */
8183
backendExtras?: Record<string, string>;
8284
};

src/acp/control-plane/manager.runtime-config.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ describe("AcpSessionManager runtime config", () => {
403403
thinking: "high",
404404
permissionProfile: "strict",
405405
timeoutSeconds: 120,
406+
fastMode: false,
406407
},
407408
},
408409
});
@@ -435,6 +436,10 @@ describe("AcpSessionManager runtime config", () => {
435436
key: "timeout",
436437
value: "120",
437438
});
439+
expectMockCallFields(runtimeState.setConfigOption, {
440+
key: "fast_mode",
441+
value: "false",
442+
});
438443
});
439444

440445
it("continues turns when adapters reject optional timeout config", async () => {
@@ -606,7 +611,7 @@ describe("AcpSessionManager runtime config", () => {
606611
const runtimeState = createRuntime();
607612
runtimeState.getCapabilities.mockResolvedValue({
608613
controls: ["session/set_config_option", "session/status"],
609-
configOptionKeys: ["model", "thought_level", "permissions", "timeout_seconds"],
614+
configOptionKeys: ["model", "thought_level", "permissions", "timeout_seconds", "fast-mode"],
610615
});
611616
hoisted.requireAcpRuntimeBackendMock.mockReturnValue({
612617
id: "acpx",
@@ -622,6 +627,7 @@ describe("AcpSessionManager runtime config", () => {
622627
thinking: "high",
623628
permissionProfile: "strict",
624629
timeoutSeconds: 120,
630+
fastMode: true,
625631
},
626632
},
627633
});
@@ -647,6 +653,10 @@ describe("AcpSessionManager runtime config", () => {
647653
key: "timeout_seconds",
648654
value: "120",
649655
});
656+
expectMockCallFields(runtimeState.setConfigOption, {
657+
key: "fast-mode",
658+
value: "true",
659+
});
650660
expectNoMockCallFields(runtimeState.setConfigOption, {
651661
key: "thinking",
652662
});

src/acp/control-plane/runtime-options.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,11 @@ describe("buildRuntimeConfigOptionPairs timeout advertisement", () => {
4040
["thinking", "high"],
4141
]);
4242
});
43+
44+
it("maps fast mode to backend-advertised aliases", () => {
45+
expect(buildRuntimeConfigOptionPairs({ fastMode: false })).toEqual([["fast_mode", "false"]]);
46+
expect(buildRuntimeConfigOptionPairs({ fastMode: true }, ["fast-mode"])).toEqual([
47+
["fast-mode", "true"],
48+
]);
49+
});
4350
});

src/acp/control-plane/runtime-options.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const RUNTIME_CONFIG_OPTION_ALIASES = {
2626
thinking: ["thinking", "effort", "reasoning_effort", "thought_level"],
2727
permissionProfile: ["approval_policy", "permission_profile", "permissions", "permission_mode"],
2828
timeoutSeconds: ["timeout", "timeout_seconds"],
29+
fastMode: ["fast_mode", "fast-mode", "fastMode"],
2930
} as const;
3031

3132
function failInvalidOption(message: string): never {
@@ -167,6 +168,7 @@ export function validateRuntimeOptionPatch(
167168
"cwd",
168169
"permissionProfile",
169170
"timeoutSeconds",
171+
"fastMode",
170172
"backendExtras",
171173
]);
172174
for (const key of Object.keys(rawPatch)) {
@@ -218,6 +220,15 @@ export function validateRuntimeOptionPatch(
218220
next.timeoutSeconds = validateRuntimeTimeoutSecondsInput(rawPatch.timeoutSeconds);
219221
}
220222
}
223+
if (Object.hasOwn(rawPatch, "fastMode")) {
224+
if (rawPatch.fastMode === undefined) {
225+
next.fastMode = undefined;
226+
} else if (typeof rawPatch.fastMode === "boolean") {
227+
next.fastMode = rawPatch.fastMode;
228+
} else {
229+
failInvalidOption("Fast mode must be a boolean.");
230+
}
231+
}
221232
if (Object.hasOwn(rawPatch, "backendExtras")) {
222233
const rawExtras = rawPatch.backendExtras;
223234
if (rawExtras === undefined) {
@@ -268,6 +279,7 @@ export function normalizeRuntimeOptions(
268279
...(cwd ? { cwd } : {}),
269280
...(permissionProfile ? { permissionProfile } : {}),
270281
...(typeof timeoutSeconds === "number" ? { timeoutSeconds } : {}),
282+
...(typeof options?.fastMode === "boolean" ? { fastMode: options.fastMode } : {}),
271283
...(backendExtras ? { backendExtras } : {}),
272284
};
273285
}
@@ -318,6 +330,7 @@ export function buildRuntimeControlSignature(options: AcpSessionRuntimeOptions):
318330
thinking: normalized.thinking ?? null,
319331
permissionProfile: normalized.permissionProfile ?? null,
320332
timeoutSeconds: normalized.timeoutSeconds ?? null,
333+
fastMode: normalized.fastMode ?? null,
321334
backendExtras: extras,
322335
});
323336
}
@@ -352,6 +365,12 @@ export function buildRuntimeConfigOptionPairs(
352365
String(normalized.timeoutSeconds),
353366
);
354367
}
368+
if (typeof normalized.fastMode === "boolean") {
369+
pairs.set(
370+
resolveRuntimeConfigOptionKey("fast_mode", advertisedConfigOptionKeys),
371+
String(normalized.fastMode),
372+
);
373+
}
355374
for (const [key, value] of Object.entries(normalized.backendExtras ?? {})) {
356375
const wireKey = resolveRuntimeConfigOptionKey(key, advertisedConfigOptionKeys);
357376
if (!pairs.has(wireKey)) {
@@ -446,6 +465,13 @@ export function inferRuntimeOptionPatchFromConfigOption(
446465
if (normalizedKey === "timeout" || normalizedKey === "timeout_seconds") {
447466
return { timeoutSeconds: parseRuntimeTimeoutSecondsInput(validated.value) };
448467
}
468+
if (
469+
normalizedKey === "fast_mode" ||
470+
normalizedKey === "fast-mode" ||
471+
normalizedKey === "fastmode"
472+
) {
473+
return { fastMode: normalizeLowercaseStringOrEmpty(validated.value) === "true" };
474+
}
449475
if (normalizedKey === "cwd") {
450476
return { cwd: validateRuntimeCwdInput(validated.value) };
451477
}

src/agents/acp-spawn.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,28 @@ describe("spawnAcpDirect", () => {
951951
expect(initInput.sessionKey).toMatch(/^agent:codex:acp:/);
952952
});
953953

954+
it("passes fast mode overrides into ACP session initialization", async () => {
955+
const result = await spawnAcpDirect(
956+
{
957+
task: "Investigate flaky tests",
958+
agentId: "codex",
959+
fastMode: false,
960+
},
961+
{
962+
agentSessionKey: "agent:main:main",
963+
},
964+
);
965+
966+
expectAcceptedSpawn(result);
967+
const initInput = expectInitializeSessionFields({
968+
agent: "codex",
969+
runtimeOptions: {
970+
fastMode: false,
971+
},
972+
});
973+
expect(initInput.sessionKey).toMatch(/^agent:codex:acp:/);
974+
});
975+
954976
it("applies existing subagent model and model-profile thinking defaults to ACP runtime options", async () => {
955977
replaceSpawnConfig({
956978
...createDefaultSpawnConfig(),

src/agents/acp-spawn.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ import { getRuntimeConfig } from "../config/config.js";
4545
import { resolveStorePath } from "../config/sessions/paths.js";
4646
import { loadSessionStore } from "../config/sessions/store.js";
4747
import { resolveSessionTranscriptFile } from "../config/sessions/transcript.js";
48-
import type { SessionAcpMeta, SessionEntry } from "../config/sessions/types.js";
48+
import type {
49+
AcpSessionRuntimeOptions,
50+
SessionAcpMeta,
51+
SessionEntry,
52+
} from "../config/sessions/types.js";
4953
import type { OpenClawConfig } from "../config/types.openclaw.js";
5054
import { callGateway } from "../gateway/call.js";
5155
import { formatErrorMessage } from "../infra/errors.js";
@@ -997,11 +1001,10 @@ function validateAcpResumeSessionOwnership(params: {
9971001
};
9981002
}
9991003

1000-
type AcpSpawnRuntimeOptions = {
1001-
model?: string;
1002-
thinking?: string;
1003-
timeoutSeconds?: number;
1004-
};
1004+
type AcpSpawnRuntimeOptions = Pick<
1005+
AcpSessionRuntimeOptions,
1006+
"model" | "thinking" | "timeoutSeconds" | "fastMode"
1007+
>;
10051008

10061009
function resolveAcpRuntimeTimeoutSeconds(runTimeoutSeconds?: number): number | undefined {
10071010
if (!runTimeoutSeconds) {
@@ -1017,6 +1020,7 @@ function resolveAcpSpawnRuntimeOptions(params: {
10171020
model?: string;
10181021
thinking?: string;
10191022
runTimeoutSeconds?: number;
1023+
fastMode?: boolean;
10201024
}): { ok: true; runtimeOptions?: AcpSpawnRuntimeOptions } | { ok: false; error: string } {
10211025
const policyAgentId = params.configAgentId ?? params.targetAgentId;
10221026
const model = resolveConfiguredSubagentSpawnModelSelection({
@@ -1052,11 +1056,12 @@ function resolveAcpSpawnRuntimeOptions(params: {
10521056

10531057
const timeoutSeconds = resolveAcpRuntimeTimeoutSeconds(params.runTimeoutSeconds);
10541058
const runtimeOptions =
1055-
model || thinking || timeoutSeconds
1059+
model || thinking || timeoutSeconds || typeof params.fastMode === "boolean"
10561060
? {
10571061
...(model ? { model } : {}),
10581062
...(thinking ? { thinking } : {}),
10591063
...(timeoutSeconds ? { timeoutSeconds } : {}),
1064+
...(typeof params.fastMode === "boolean" ? { fastMode: params.fastMode } : {}),
10601065
}
10611066
: undefined;
10621067
return { ok: true, runtimeOptions };
@@ -1409,6 +1414,7 @@ export async function spawnAcpDirect(
14091414
model: params.model,
14101415
thinking: params.thinking,
14111416
runTimeoutSeconds,
1417+
fastMode: params.fastMode,
14121418
});
14131419
if (!runtimeOptionsResult.ok) {
14141420
return createAcpSpawnFailure({

src/auto-reply/reply/commands-acp/shared.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ export function formatRuntimeOptionsText(options: AcpSessionRuntimeOptions): str
475475
options.cwd ? `cwd=${options.cwd}` : null,
476476
options.permissionProfile ? `permissionProfile=${options.permissionProfile}` : null,
477477
typeof options.timeoutSeconds === "number" ? `timeoutSeconds=${options.timeoutSeconds}` : null,
478+
typeof options.fastMode === "boolean" ? `fastMode=${options.fastMode}` : null,
478479
extras ? `extras={${extras}}` : null,
479480
].filter(Boolean) as string[];
480481
if (parts.length === 0) {

0 commit comments

Comments
 (0)