Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,5 @@ See [AIR extensions](docs/air-extensions.md#async-tasks) for the capability, lif
## License

By contributing, you agree that your contributions will be licensed under the Apache 2.0 License.

See [session speed selection](docs/speed-options.md) for Standard, Fast, and model-advertised Ultrafast controls.
33 changes: 33 additions & 0 deletions docs/speed-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Session speed selection

The `fast-mode` session config option selects a service tier. Its ID and existing
`off`/`on` values remain compatible with older clients:

| Display name | Select value | Turn service tier |
| --- | --- | --- |
| Standard | `off` | `null` |
| Fast | `on` | `fast` |
| Ultrafast | `ultrafast` | `ultrafast` |

Ultrafast is offered only when the selected model advertises it through
`serviceTiers` or the older `additionalSpeedTiers` field. Model names are not
hardcoded. Availability and usage remain subject to Codex account eligibility.

For models with Ultrafast, the option is a select even if the client supports
boolean options. For other Fast-capable models, boolean-capable clients retain
the existing Fast mode toggle; clients without boolean support see Standard/Fast.
Models supporting neither accelerated tier do not advertise the option.

Use `session/set_config_option` with `configId: "fast-mode"` and the advertised
value. ACP v2 clients use `type: "id"` for the select, or `type: "boolean"` for
the toggle. Successful changes return updated config options and subsequent
prompts use the selected tier. Invalid or unsupported Ultrafast choices are
rejected. Older boolean and `on`/`off` requests retain their meaning in ACP v1.

New and restored sessions recognize Ultrafast returned by Codex. When switching
to a model without Ultrafast, an Ultrafast selection becomes Fast if supported,
otherwise Standard; updated options report this fallback.

This adds no model, reasoning-effort, or speed default. Other externally selected
tiers, such as Flex, are outside this control's scope; preserving them is tracked
separately in #673.
9 changes: 4 additions & 5 deletions src/CodexAcpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import type {
ReasoningEffort,
ServerNotification
} from "./app-server";
import type {ServiceTier} from "./app-server/ServiceTier";
import type {JsonValue} from "./app-server/serde_json/JsonValue";
import {ModelId} from "./ModelId";
import {AgentMode} from "./AgentMode";
Expand Down Expand Up @@ -724,7 +723,7 @@ export class CodexAcpClient {
threadStatus: response.thread.status,
sessionTitle: normalizeSessionTitle(listedSessionTitle(response.thread)),
modelProvider: response.modelProvider,
currentServiceTier: response.serviceTier as ServiceTier ?? null,
currentServiceTier: response.serviceTier ?? null,
additionalDirectories,
skippedMcpServers: sessionConfig.skippedMcpServers,
...(instructions ? {instructions} : {}),
Expand Down Expand Up @@ -804,7 +803,7 @@ export class CodexAcpClient {
// Codex sends no thread/settings/updated on a resume. The response holds the mode.
collaborationMode: response.collaborationMode ?? this.getCollaborationMode(response.thread.id),
modelProvider: response.modelProvider,
currentServiceTier: response.serviceTier as ServiceTier ?? null,
currentServiceTier: response.serviceTier ?? null,
thread,
history,
additionalDirectories,
Expand Down Expand Up @@ -886,7 +885,7 @@ export class CodexAcpClient {
models: codexModels,
collaborationMode: this.getCollaborationMode(response.thread.id),
modelProvider: response.modelProvider,
currentServiceTier: response.serviceTier as ServiceTier ?? null,
currentServiceTier: response.serviceTier ?? null,
additionalDirectories,
skippedMcpServers: sessionConfig.skippedMcpServers,
...(instructions ? {instructions} : {}),
Expand Down Expand Up @@ -1362,7 +1361,7 @@ export class CodexAcpClient {
request: acp.PromptRequest,
agentMode: AgentMode,
modelId: ModelId,
serviceTier: ServiceTier | null,
serviceTier: string | null,
disableSummary: boolean,
cwd: string,
additionalDirectories: string[],
Expand Down
22 changes: 18 additions & 4 deletions src/CodexAcpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ import {
FAST_MODE_ON,
modelSupportsFast,
resolveFastServiceTier,
fastModeFromServiceTier,
normalizeFastMode,
modelSupportsUltrafast,
ULTRAFAST_MODE,
type FastModeSelection,
} from "./FastModeConfig";
import packageJson from "../package.json";
import {isJetBrains2026_1Client} from "./JBUtils";
Expand Down Expand Up @@ -276,7 +281,7 @@ export interface SessionState extends SessionIndexTitleState {
cwd: string;
additionalDirectories: string[];
mcpServers?: Array<AcpMcpServer>;
fastModeEnabled: boolean;
fastModeEnabled: FastModeSelection;
currentModelSupportsFast: boolean;
sessionMcpServers?: Array<string>;
/** The capability choices of the client for tool call and plan reports. */
Expand Down Expand Up @@ -1386,7 +1391,7 @@ export class CodexAcpServer {
cwd: request.cwd,
additionalDirectories: sessionMetadata.additionalDirectories,
mcpServers: requestedMcpServers,
fastModeEnabled: sessionMetadata.currentServiceTier === "fast",
fastModeEnabled: normalizeFastMode(fastModeFromServiceTier(sessionMetadata.currentServiceTier), currentModel),
currentModelSupportsFast: currentModelSupportsFast,
sessionMcpServers: sessionMcpServers,
clientCapabilities: this.capabilities,
Expand Down Expand Up @@ -2816,6 +2821,12 @@ export class CodexAcpServer {

private applyFastModeChange(sessionState: SessionState, params: acp.SetSessionConfigOptionRequest): void {
const value = params.value;
if (value === ULTRAFAST_MODE) {
const model = this.findCurrentModel(sessionState.availableModels, sessionState.currentModelId);
if (!modelSupportsUltrafast(model)) throw RequestError.invalidParams();
sessionState.fastModeEnabled = ULTRAFAST_MODE;
return;
}
if (typeof value === "boolean") {
sessionState.fastModeEnabled = value;
return;
Expand Down Expand Up @@ -2892,6 +2903,7 @@ export class CodexAcpServer {
sessionState.supportedReasoningEfforts = currentModel?.supportedReasoningEfforts ?? [];
sessionState.supportedInputModalities = currentModel?.inputModalities ?? ["text", "image"];
sessionState.currentModelSupportsFast = modelSupportsFast(currentModel);
sessionState.fastModeEnabled = normalizeFastMode(sessionState.fastModeEnabled, currentModel);
});
}

Expand All @@ -2900,6 +2912,7 @@ export class CodexAcpServer {
sessionState.supportedReasoningEfforts = model.supportedReasoningEfforts;
sessionState.supportedInputModalities = model.inputModalities;
sessionState.currentModelSupportsFast = modelSupportsFast(model);
sessionState.fastModeEnabled = normalizeFastMode(sessionState.fastModeEnabled, model);
}

async unstable_setSessionModel(params: LegacySetSessionModelRequest): Promise<LegacySetSessionModelResponse> {
Expand Down Expand Up @@ -3267,10 +3280,11 @@ export class CodexAcpServer {
),
);
}
if (sessionState.currentModelSupportsFast) {
if (sessionState.currentModelSupportsFast || modelSupportsUltrafast(currentModel)) {
configOptions.push(createFastModeConfigOption(
sessionState.fastModeEnabled,
this.booleanConfigOptionsSupported,
currentModel,
));
}
return configOptions;
Expand Down Expand Up @@ -3554,7 +3568,7 @@ export class CodexAcpServer {
cwd: request.cwd,
additionalDirectories: sessionMetadata.additionalDirectories,
mcpServers: requestedMcpServers,
fastModeEnabled: sessionMetadata.currentServiceTier === "fast",
fastModeEnabled: normalizeFastMode(fastModeFromServiceTier(sessionMetadata.currentServiceTier), currentModel),
currentModelSupportsFast: currentModelSupportsFast,
sessionMcpServers: sessionMcpServers,
clientCapabilities: this.capabilities,
Expand Down
50 changes: 36 additions & 14 deletions src/FastModeConfig.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,79 @@
import type {SessionConfigOption} from "@agentclientprotocol/sdk/unstable";
import type * as acp from "@agentclientprotocol/sdk/unstable";
import type {ServiceTier} from "./app-server/ServiceTier";
import type {Model} from "./app-server/v2";

export const FAST_MODE_CONFIG_ID = "fast-mode";
export const FAST_MODE_CATEGORY = "model_config";
export const FAST_MODE_ON = "on";
export const FAST_MODE_OFF = "off";

const FAST_MODE_DESCRIPTION = "1.5x speed, increased usage";
export const ULTRAFAST_MODE = "ultrafast";
export type FastModeSelection = boolean | typeof ULTRAFAST_MODE;

const FAST_MODE_DESCRIPTION = "Faster responses, increased usage";

export function modelSupportsUltrafast(model: Model | undefined): boolean {
return model?.serviceTiers?.some(tier => tier.id === ULTRAFAST_MODE)
|| (model?.additionalSpeedTiers?.includes(ULTRAFAST_MODE) ?? false);
}

export function fastModeFromServiceTier(tier: string | null | undefined): FastModeSelection {
return tier === ULTRAFAST_MODE ? ULTRAFAST_MODE : tier === "fast" || tier === "priority";
}

export function normalizeFastMode(selection: FastModeSelection, model: Model | undefined): FastModeSelection {
return selection === ULTRAFAST_MODE && !modelSupportsUltrafast(model) ? modelSupportsFast(model) : selection;
}

export function modelSupportsFast(model: Model | undefined): boolean {
return model?.additionalSpeedTiers?.includes("fast") ?? false;
return model?.serviceTiers?.some(tier => tier.id === "fast" || tier.id === "priority")
|| (model?.additionalSpeedTiers?.includes("fast") ?? false);
}

export function resolveFastServiceTier(fastModeEnabled: boolean, currentModelSupportsFast: boolean): ServiceTier | null {
export function resolveFastServiceTier(fastModeEnabled: FastModeSelection, currentModelSupportsFast: boolean): string | null {
if (fastModeEnabled === ULTRAFAST_MODE) return ULTRAFAST_MODE;
return fastModeEnabled && currentModelSupportsFast ? "fast" : null;
}

export function clientSupportsBooleanConfigOptions(clientCapabilities?: acp.ClientCapabilities | null): boolean {
return clientCapabilities?.session?.configOptions?.boolean != null;
}

export function createFastModeConfigOption(fastModeEnabled: boolean, useBooleanConfigOption = false): SessionConfigOption {
if (useBooleanConfigOption) {
export function createFastModeConfigOption(fastModeEnabled: FastModeSelection, useBooleanConfigOption = false, model?: Model): SessionConfigOption {
if (useBooleanConfigOption && !modelSupportsUltrafast(model)) {
return {
id: FAST_MODE_CONFIG_ID,
name: "Fast mode",
description: FAST_MODE_DESCRIPTION,
category: FAST_MODE_CATEGORY,
type: "boolean",
currentValue: fastModeEnabled,
currentValue: fastModeEnabled === true,
};
}

return {
id: FAST_MODE_CONFIG_ID,
name: "Fast mode",
description: FAST_MODE_DESCRIPTION,
name: "Speed",
description: "Service tier for the current model",
category: FAST_MODE_CATEGORY,
type: "select",
currentValue: fastModeEnabled ? FAST_MODE_ON : FAST_MODE_OFF,
currentValue: fastModeEnabled === ULTRAFAST_MODE ? ULTRAFAST_MODE : fastModeEnabled ? FAST_MODE_ON : FAST_MODE_OFF,
options: [
{
value: FAST_MODE_OFF,
name: "Off",
name: "Standard",
description: "Default speed, normal usage",
},
{
...(model === undefined || modelSupportsFast(model) ? [{
value: FAST_MODE_ON,
name: "On",
name: "Fast",
description: FAST_MODE_DESCRIPTION,
},
}] : []),
...(modelSupportsUltrafast(model) ? [{
value: ULTRAFAST_MODE,
name: "Ultrafast",
description: "Fastest responses, increased usage",
}] : []),
],
};
}
3 changes: 1 addition & 2 deletions src/SessionMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type {ModeKind} from "./app-server/ModeKind";
import type {ServiceTier} from "./app-server/ServiceTier";
import type {Model, Thread, ThreadItemEntry, ThreadStatus} from "./app-server/v2";
import type {SessionInstructions} from "./ClientSystemPrompt";

Expand All @@ -9,7 +8,7 @@ export type SessionMetadata = {
models: Model[],
collaborationMode: ModeKind,
modelProvider?: string | null,
currentServiceTier?: ServiceTier | null,
currentServiceTier?: string | null,
threadStatus?: ThreadStatus,
additionalDirectories: string[],
skippedMcpServers?: string[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
{
"configId": "fast-mode",
"name": "Fast mode",
"description": "1.5x speed, increased usage",
"description": "Faster responses, increased usage",
"category": "model_config",
"type": "boolean",
"currentValue": false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
"type": "boolean",
"configId": "fast-mode",
"name": "Fast mode",
"description": "1.5x speed, increased usage",
"description": "Faster responses, increased usage",
"category": "model_config"
}
]
Expand Down Expand Up @@ -260,7 +260,7 @@
"type": "boolean",
"configId": "fast-mode",
"name": "Fast mode",
"description": "1.5x speed, increased usage",
"description": "Faster responses, increased usage",
"category": "model_config"
}
]
Expand Down
Loading