Add opt-in Ultrafast mode

This commit is contained in:
Pranit committed 2026-10-01 03:54:09 -04:00
1 parent 14893f6460
commit 110823f201
78 files changed
+4454 -203

No files matched your search

+4 -1
View File
@@ -384,7 +384,10 @@ progress are not delivered to operations yet.
## ACP Embedding
`fx acp` extends ACP v1 for clients that embed it. Extensions are read and
written under `_meta.fx`.
written under `_meta.fx`. Start the server with `fx acp --ultrafast` or
`fx acp --no-ultrafast` to set a process-local default request for sessions
created by that server; the request still requires a model that advertises
Ultra eligibility.
* **Client MCP tools stay loaded:** tool schemas from servers in `mcpServers`
are advertised on every turn within the `mcp_selected_schema_bytes` budget.
+26
View File
@@ -74,6 +74,32 @@ FX_PROVIDER=openrouter FX_MODEL=openai/gpt-4.1 fx ask "review this change"
See [Custom model connections](https://fx.sh/docs/configure-fx/custom-model-connections) for connection JSON, model metadata, and behavior details.
## Ultrafast mode
Ultrafast mode is off by default. It requests OpenAI's higher-cost Gateway service tier with `openai.serviceTier: "ultrafast"` for models whose Gateway metadata advertises Ultra eligibility. `ultrafast_requested` in `fx status --json` and `/status` reports the request, not a guarantee that a provider served the tier.
Set a profile default in `~/.fx/settings.json`:
```jsonc
{
"provider": "gateway",
"models": { "gateway": "openai/gpt-6-astra" },
"ultrafast_mode": true
}
```
Use it explicitly in an interactive session, a one-shot request, or ACP:
```bash
fx --ultrafast
fx ask --ultrafast "review this change"
fx acp --ultrafast
```
Use `/ultrafast on`, `/ultrafast off`, or `/ultrafast status` in the shell. The Settings menu includes an Ultra mode row. `FX_ULTRAFAST=1` and `--ultrafast` are process-local opt-ins and are not persisted. `FX_ULTRAFAST=0`, `--no-ultrafast`, and `/ultrafast off` explicitly disable it. A resumed session keeps its saved request unless a higher-precedence explicit disable applies.
Ultra mode is available only through the Vercel AI Gateway's OpenAI service tier. Gateway metadata currently marks Astra eligible. fx does not select Ultra automatically, and switching models clears an existing Ultra request. Subagents inherit the parent turn's request; an explicit parent disable and capability checks override an existing child preference. Background side calls, including titles, reviews, and compaction, do not use Ultra mode.
## Gateway provider routing
When the active model goes through the Vercel AI Gateway, one model is often served by several providers (for example Anthropic directly, AWS Bedrock, or Google Vertex). fx can tell the gateway which providers to use, in what order:
+1
View File
@@ -126,6 +126,7 @@
{"name": "verify-tui-slash-commands", "argv": ["bun", "test", "--max-concurrency", "1", "./tui-slash-commands.test.ts"], "test_file": "tui-slash-commands.test.ts"},
{"name": "verify-tui-slash-extra", "argv": ["bun", "test", "--max-concurrency", "1", "./tui-slash-extra.test.ts"], "test_file": "tui-slash-extra.test.ts"},
{"name": "verify-tui-slash-menu", "argv": ["bun", "test", "--max-concurrency", "1", "./tui-slash-menu.test.ts"], "test_file": "tui-slash-menu.test.ts"},
{"name": "verify-ultrafast-fake-gateway", "argv": ["bun", "test", "--max-concurrency", "1", "./ultrafast-fake-gateway.test.ts"], "test_file": "ultrafast-fake-gateway.test.ts"},
{"name": "verify-web-fetch-permission-progress", "argv": ["bun", "test", "--max-concurrency", "1", "./web-fetch-permission-progress.test.ts"], "test_file": "web-fetch-permission-progress.test.ts", "requires_tmux": false},
{"name": "verify-web-search-permission-progress", "argv": ["bun", "test", "--max-concurrency", "1", "./web-search-permission-progress.test.ts"], "test_file": "web-search-permission-progress.test.ts", "requires_tmux": false},
{"name": "verify-yolo-permission-mode", "argv": ["bun", "test", "--max-concurrency", "1", "./yolo-permission-mode.test.ts"], "test_file": "yolo-permission-mode.test.ts"}
+15 -4
View File
@@ -66,12 +66,23 @@ Omitting effort or using `"default"` leaves the model default in place.
fx CLI's `--fast` flag. A model without a fast path rejects at creation with
`code: "LIBFX_MODEL_UNSUPPORTED_FAST"`, `model`, and `capability: "fast"`.
Omitting fast or setting it to `false` leaves the model default in place.
`model.ultrafast` requests Ultra mode. It is off by default and maps to
`openai.serviceTier: "ultrafast"` through the Vercel AI Gateway for models
whose metadata advertises Ultra eligibility. It uses the higher-cost service tier.
Set it to `true` only after the host has selected an eligible model; an
unsupported request rejects with `code: "LIBFX_MODEL_UNSUPPORTED_ULTRAFAST"`,
`model`, and `capability: "ultrafast"`. Set it to `false` to explicitly disable
an inherited request. Ultra and Fast are mutually exclusive, so `ultrafast:
true` disables Fast for the agent. Gateway metadata currently marks Astra
eligible; libfx does not select Ultra automatically.
The new codes replace `LIBFX_UNSUPPORTED_EFFORT` and `LIBFX_UNSUPPORTED_FAST`
for both nested and legacy top-level settings. Callers that check the old codes
must update their error handling.
The host selects the model. Agent creation does not fetch the Gateway model
catalog unless effort requests a named level or fast is enabled. Prompting
catalog unless effort requests a named level, fast is enabled, or ultrafast is enabled. Prompting
can resolve model capabilities and context capacity through the supplied
`fetch`; fx caches that metadata for the agent.
@@ -188,10 +199,10 @@ or a history change. The next prompt can run normally.
The checkpoint contains conversation history and usage only. The host owns
durable storage and must resupply models, credentials, instructions, tools,
MCP clients, and skill records. Reasoning effort and fast mode are
MCP clients, and skill records. Reasoning effort, Fast mode, and Ultra mode are
agent-creation options and are not stored in a checkpoint: recreate the agent
with new `model.effort` or `model.fast` values to change them, the same path as
switching models.
with new `model.effort`, `model.fast`, or `model.ultrafast` values to change
them, the same path as switching models.
## Models
+16 -5
View File
@@ -84,6 +84,14 @@ function normalizeFast(value) {
return value;
}
function normalizeUltrafast(value) {
if (value === undefined) return undefined;
if (typeof value !== "boolean") {
throw new TypeError("ultrafast must be a boolean");
}
return value;
}
export function normalizeAgentOptions(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new TypeError("createFxAgent() options must be an object");
@@ -94,22 +102,24 @@ export function normalizeAgentOptions(value) {
}
options.apiKey = boundedString(options.apiKey, "apiKey", maxApiKeyBytes, true);
if (options.model !== null && typeof options.model === "object" && !Array.isArray(options.model)) {
if (Object.hasOwn(options, "effort") || Object.hasOwn(options, "fast")) {
throw new TypeError("model options cannot be mixed with top-level effort or fast");
if (Object.hasOwn(options, "effort") || Object.hasOwn(options, "fast") || Object.hasOwn(options, "ultrafast")) {
throw new TypeError("model options cannot be mixed with top-level effort, fast, or ultrafast");
}
const model = options.model;
for (const name of Object.keys(model)) {
if (name !== "id" && name !== "effort" && name !== "fast") {
if (name !== "id" && name !== "effort" && name !== "fast" && name !== "ultrafast") {
throw new TypeError(`unsupported model option: ${name}`);
}
}
options.model = boundedString(model.id, "model.id", maxModelBytes, true);
options.effort = normalizeEffort(model.effort);
options.fast = normalizeFast(model.fast);
options.ultrafast = normalizeUltrafast(model.ultrafast);
} else {
options.model = boundedString(options.model, "model", maxModelBytes, false);
options.effort = normalizeEffort(options.effort);
options.fast = normalizeFast(options.fast);
options.ultrafast = normalizeUltrafast(options.ultrafast);
}
validateGatewayChatUrl(options.gatewayChatUrl);
return options;
@@ -121,6 +131,7 @@ function agentEnvironment(options) {
...(options.model === undefined ? {} : { FX_MODEL: options.model }),
...(options.effort === undefined ? {} : { FX_EFFORT: options.effort }),
...(options.fast === undefined ? {} : { FX_FAST: options.fast ? "true" : "false" }),
...(options.ultrafast === undefined ? {} : { FX_ULTRAFAST: options.ultrafast ? "true" : "false" }),
...(options.gatewayChatUrl === undefined ? {} : { FX_GATEWAY_CHAT_URL: options.gatewayChatUrl }),
};
}
@@ -128,9 +139,9 @@ function agentEnvironment(options) {
function agentRpcError(response) {
const error = new Error(response.message);
const data = response.data;
if (data && ["LIBFX_MODEL_UNSUPPORTED_EFFORT", "LIBFX_MODEL_UNSUPPORTED_FAST"].includes(data.code) &&
if (data && ["LIBFX_MODEL_UNSUPPORTED_EFFORT", "LIBFX_MODEL_UNSUPPORTED_FAST", "LIBFX_MODEL_UNSUPPORTED_ULTRAFAST"].includes(data.code) &&
typeof data.model === "string" &&
data.capability === (data.code === "LIBFX_MODEL_UNSUPPORTED_FAST" ? "fast" : "effort")) {
data.capability === (data.code === "LIBFX_MODEL_UNSUPPORTED_FAST" ? "fast" : data.code === "LIBFX_MODEL_UNSUPPORTED_ULTRAFAST" ? "ultrafast" : "effort")) {
error.code = data.code;
error.model = data.model;
error.capability = data.capability;
+7 -1
View File
@@ -346,7 +346,12 @@ export async function getBackendInfo(value = {}) {
}
function createNativeCoreRuntime(addon, options) {
const { apiKey, model, effort, fast, gatewayChatUrl } = options;
const { apiKey, model, effort, fast, ultrafast, gatewayChatUrl } = options;
if (ultrafast !== undefined && addon.supportsUltrafast !== true) {
const error = new Error("native addon does not support the ultrafast option");
error.code = "LIBFX_NATIVE_CAPABILITY_UNAVAILABLE";
throw error;
}
const core = addon.createCore({
apiKey,
home: options.home ?? homedir(),
@@ -354,6 +359,7 @@ function createNativeCoreRuntime(addon, options) {
...(model === undefined ? {} : { model }),
...(effort === undefined ? {} : { effort }),
...(fast === undefined ? {} : { fast }),
...(ultrafast === undefined ? {} : { ultrafast }),
...(gatewayChatUrl === undefined ? {} : { gatewayChatUrl }),
});
let readyFd;
+473
View File
@@ -0,0 +1,473 @@
#!/usr/bin/env node
import { strict as assert } from "node:assert";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { createFxAgent, supportsJspi } from "../node.js";
const scriptDir = fileURLToPath(new URL(".", import.meta.url));
const backend = process.argv[2] || "native";
if (!new Set(["native", "wasm"]).has(backend)) {
throw new Error("usage: test-agent-ultrafast-options.mjs [native|wasm]");
}
if (backend === "wasm" && !supportsJspi()) {
console.error("Node JSPI is disabled. Run with --experimental-wasm-jspi");
process.exit(2);
}
const encoded = new TextEncoder();
const supportedModel = "openai/gpt-6-astra";
const unsupportedModel = "openai/gpt-6-plain";
const solModel = "openai/gpt-5.6-sol";
const defaultModel = "spacexai/grok-4.7";
const catalog = {
object: "list",
data: [
{
id: supportedModel,
type: "language",
owned_by: "openai",
pricing: {
service_tiers: {
ultrafast: { input: 0.00006, output: 0.0003 },
},
},
},
// The capability must stay OpenAI-only even if another catalog entry
// advertises a positively priced tier with the same name.
{
id: "anthropic/gpt-6-astra",
type: "language",
owned_by: "anthropic",
pricing: {
service_tiers: {
ultrafast: { input: 0.00006, output: 0.0003 },
},
},
},
{ id: unsupportedModel, type: "language", owned_by: "openai" },
{
id: solModel,
type: "language",
owned_by: "openai",
fast_options: [{ type: "toggle" }],
pricing: {
input_cache_read: "0.000001",
service_tiers: {
ultrafast: { input: "0.00006", output: "0.0003", input_cache_read: "0.000006" },
},
},
},
{ id: defaultModel, type: "language", owned_by: "spacexai" },
],
};
function mockGateway(providerMetadata = { gateway: { serviceTier: "ultrafast", cost: "0.00036" } }) {
const state = { catalogFetches: 0, chatBodies: [], chatModels: [] };
const finish = {
type: "finish",
finishReason: { unified: "stop", raw: "stop" },
usage: { inputTokens: { total: 3 }, outputTokens: { total: 2 } },
...(providerMetadata === null ? {} : { providerMetadata }),
};
const fetch = async (_url, init = {}) => {
const method = String(init.method ?? "GET").toUpperCase();
if (method === "GET") {
state.catalogFetches += 1;
return Response.json(catalog);
}
state.chatBodies.push(JSON.parse(new TextDecoder().decode(init.body)));
state.chatModels.push(new Headers(init.headers).get("ai-language-model-id"));
return new Response(new ReadableStream({
start(controller) {
controller.enqueue(encoded.encode('data: {"type":"text-delta","delta":"ok"}\n\n'));
controller.enqueue(encoded.encode(`data: ${JSON.stringify(finish)}\n\n`));
controller.enqueue(encoded.encode("data: [DONE]\n\n"));
controller.close();
},
}), { status: 200, headers: { "content-type": "text/event-stream" } });
};
return { state, fetch };
}
async function runPrompt(agent, input = "say ok", expectedText = "ok") {
const turn = agent.prompt(input);
let text = "";
for await (const event of turn) {
if (event.type === "text_delta") text += event.delta;
}
assert.equal(text, expectedText);
return turn.result;
}
function assertUltrafastRoute(body) {
assert.equal(body.providerOptions?.openai?.serviceTier, "ultrafast");
assert.deepEqual(body.providerOptions?.gateway?.only, ["openai"]);
assert.equal(body.providerOptions?.gateway?.speed, undefined);
assert.equal(body.providerOptions?.gateway?.fast, undefined);
}
function promptText(body, role) {
return body.prompt
.filter((message) => message.role === role)
.flatMap((message) => message.content)
.filter((part) => part.type === "text")
.map((part) => part.text);
}
function configOption(events, id) {
for (const event of events) {
const options = event.message?.result?.configOptions;
const option = options?.find((candidate) => candidate.id === id);
if (option) return option;
}
return null;
}
const home = await mkdtemp(join(tmpdir(), "libfx-ultrafast-"));
const previousHome = process.env.HOME;
try {
process.env.HOME = home;
const baseOptions = {
backend,
apiKey: "sdk-ultrafast-test-key",
home,
workspaceRoot: home,
...(backend === "native"
? { nativeAddon: resolve(scriptDir, "../../zig-out/lib/libfx.node") }
: { wasm: await readFile(resolve(scriptDir, "../../zig-out/bin/fx-core.wasm")) }),
};
const createAgent = (gateway, overrides, events = []) =>
createFxAgent({
...baseOptions,
fetch: gateway.fetch,
onEvent(event) {
events.push(event);
},
...overrides,
});
const withAgent = async (gateway, overrides, exercise, events = []) => {
const agent = await createAgent(gateway, overrides, events);
try {
await exercise(agent);
} finally {
await agent.close();
}
assert.deepEqual(events.filter((event) => event.type === "runtime.exit").map((event) => event.code), [0]);
assert.equal(await agent.close(), undefined);
assert.equal(events.filter((event) => event.type === "runtime.exit").length, 1);
return agent;
};
const assertCreationRejects = async (gateway, overrides, expected, events = []) => {
let agent;
try {
await assert.rejects(async () => {
agent = await createAgent(gateway, overrides, events);
}, expected);
} finally {
await agent?.close();
}
};
// A catalog-verified OpenAI ultrafast model creates a session, advertises
// the ACP config option, and serializes the exclusive ultrafast provider route.
{
const gateway = mockGateway();
const events = [];
await withAgent(gateway, {
model: { id: supportedModel, ultrafast: true },
}, async (agent) => {
assert.equal(gateway.state.catalogFetches, 1);
const option = configOption(events, "ultrafast");
assert.equal(option?.currentValue, "true");
assert.deepEqual(option?.options?.map((entry) => entry.value), ["false", "true"]);
const result = await runPrompt(agent);
assert.equal(result.stopReason, "end_turn");
assert.equal(gateway.state.chatBodies.length, 1);
const providerOptions = gateway.state.chatBodies[0].providerOptions;
assert.equal(providerOptions?.openai?.serviceTier, "ultrafast");
assert.deepEqual(providerOptions?.gateway?.only, ["openai"]);
assert.equal(providerOptions?.gateway?.speed, undefined);
assert.equal(providerOptions?.gateway?.fast, undefined);
}, events);
}
// Unsupported catalog models reject during creation and never send a prompt.
{
const gateway = mockGateway();
const events = [];
await assertCreationRejects(
gateway,
{ model: { id: unsupportedModel, ultrafast: true } },
(error) => {
assert.equal(error.code, "LIBFX_MODEL_UNSUPPORTED_ULTRAFAST");
assert.equal(error.model, unsupportedModel);
assert.equal(error.capability, "ultrafast");
assert.match(error.message, /Ultrafast mode is not available/);
return true;
},
events,
);
assert.equal(gateway.state.catalogFetches, 1);
assert.equal(gateway.state.chatBodies.length, 0);
assert.deepEqual(events.filter((event) => event.type === "runtime.exit").map((event) => event.code), [0]);
}
// Explicit false preserves the ordinary route and does not send ultrafast
// or fast provider options.
{
const gateway = mockGateway();
await withAgent(gateway, {
model: { id: supportedModel, ultrafast: false },
}, async (agent) => {
assert.equal(gateway.state.catalogFetches, 0);
const result = await runPrompt(agent);
assert.equal(result.stopReason, "end_turn");
const providerOptions = gateway.state.chatBodies[0].providerOptions;
assert.equal(providerOptions?.openai?.serviceTier, undefined);
assert.equal(providerOptions?.gateway?.speed, undefined);
assert.equal(providerOptions?.gateway?.only, undefined);
assert.equal(providerOptions?.gateway?.fast, undefined);
});
}
// A string model accepts the top-level option and stays usable after bad
// prompt input, repeated turns, close, and recreation in the same process.
{
const gateway = mockGateway();
const agent = await withAgent(gateway, { model: supportedModel, ultrafast: true }, async (agent) => {
assert.equal(gateway.state.catalogFetches, 1);
assert.throws(() => agent.prompt(null), {
name: "TypeError",
message: "prompt input must be a string or an array of prompt blocks",
});
assert.equal(gateway.state.chatBodies.length, 0);
const inputs = ["first Ultra turn", "second Ultra turn", "third Ultra turn"];
for (const [index, input] of inputs.entries()) {
assert.equal((await runPrompt(agent, input)).stopReason, "end_turn");
assert.equal(gateway.state.chatBodies.length, index + 1);
const body = gateway.state.chatBodies[index];
assertUltrafastRoute(body);
assert.deepEqual(promptText(body, "user"), inputs.slice(0, index + 1));
assert.deepEqual(promptText(body, "assistant"), Array(index).fill("ok"));
}
assert.equal(gateway.state.catalogFetches, 1);
});
assert.throws(() => agent.prompt("after close"), /fx agent is closed/);
await assert.rejects(agent.checkpoint(), /fx agent is closed/);
assert.equal(gateway.state.chatBodies.length, 3);
const nextGateway = mockGateway();
await withAgent(nextGateway, { model: supportedModel, ultrafast: true }, async (next) => {
assert.equal((await runPrompt(next, "fresh after close")).stopReason, "end_turn");
assert.equal(nextGateway.state.catalogFetches, 1);
assert.equal(nextGateway.state.chatBodies.length, 1);
assertUltrafastRoute(nextGateway.state.chatBodies[0]);
assert.deepEqual(promptText(nextGateway.state.chatBodies[0], "user"), ["fresh after close"]);
assert.deepEqual(promptText(nextGateway.state.chatBodies[0], "assistant"), []);
});
}
// False works with a string or omitted model without a creation-time fetch.
for (const overrides of [
{ model: supportedModel, ultrafast: false },
{ ultrafast: false },
]) {
const gateway = mockGateway();
await withAgent(gateway, overrides, async (agent) => {
assert.equal(gateway.state.catalogFetches, 0);
assert.equal((await runPrompt(agent)).stopReason, "end_turn");
assert.equal(gateway.state.chatBodies.length, 1);
const body = gateway.state.chatBodies[0];
assert.deepEqual(gateway.state.chatModels, [overrides.model ?? defaultModel]);
assert.equal(body.providerOptions?.openai?.serviceTier, undefined);
assert.equal(body.providerOptions?.gateway?.speed, undefined);
assert.equal(body.providerOptions?.gateway?.only, undefined);
assert.equal(body.providerOptions?.gateway?.fast, undefined);
});
}
// Omitted model means the kernel default, not automatic Ultra model selection.
// Neither an unsupported OpenAI model nor a priced non-OpenAI model can opt in.
for (const [overrides, model] of [
[{ ultrafast: true }, defaultModel],
[{ model: unsupportedModel, ultrafast: true }, unsupportedModel],
[{ model: "anthropic/gpt-6-astra", ultrafast: true }, "anthropic/gpt-6-astra"],
]) {
const gateway = mockGateway();
const events = [];
await assertCreationRejects(gateway, overrides, (error) => {
assert.equal(error.code, "LIBFX_MODEL_UNSUPPORTED_ULTRAFAST");
assert.equal(error.model, model);
assert.equal(error.capability, "ultrafast");
assert.match(error.message, /Ultrafast mode is not available/);
return true;
}, events);
assert.equal(gateway.state.catalogFetches, 1);
assert.equal(gateway.state.chatBodies.length, 0);
assert.deepEqual(events.filter((event) => event.type === "runtime.exit").map((event) => event.code), [0]);
}
// Validation is strict on both option shapes and precedes runtime/transport.
for (const ultrafast of [null, "true", "false", 0, 1, [], {}]) {
for (const overrides of [
{ model: supportedModel, ultrafast },
{ ultrafast },
{ model: { id: supportedModel, ultrafast } },
]) {
const gateway = mockGateway();
const events = [];
await assertCreationRejects(gateway, overrides, {
name: "TypeError",
message: "ultrafast must be a boolean",
}, events);
assert.equal(gateway.state.catalogFetches, 0);
assert.equal(gateway.state.chatBodies.length, 0);
assert.deepEqual(events, []);
}
}
// Even false or an explicitly present undefined cannot mix a model object
// with top-level mode options; no precedence rule is invented for this shape.
for (const model of [
{ id: supportedModel },
{ id: supportedModel, ultrafast: true },
{ id: supportedModel, ultrafast: false },
]) {
for (const topLevel of [
{ ultrafast: true },
{ ultrafast: false },
{ ultrafast: undefined },
{ fast: false },
{ effort: "auto" },
]) {
const gateway = mockGateway();
const events = [];
await assertCreationRejects(gateway, { model, ...topLevel }, {
name: "TypeError",
message: "model options cannot be mixed with top-level effort, fast, or ultrafast",
}, events);
assert.equal(gateway.state.catalogFetches, 0);
assert.equal(gateway.state.chatBodies.length, 0);
assert.deepEqual(events, []);
}
}
// Sol is eligible because its catalog has positive Ultra prices, including
// cache reads, not because of its name or an Astra-only allowlist.
{
const gateway = mockGateway();
const events = [];
await withAgent(gateway, { model: { id: solModel, ultrafast: true } }, async (agent) => {
assert.equal(gateway.state.catalogFetches, 1);
assert.equal(configOption(events, "ultrafast")?.currentValue, "true");
assert.equal((await runPrompt(agent)).stopReason, "end_turn");
assert.equal(gateway.state.chatBodies.length, 1);
assert.deepEqual(gateway.state.chatModels, [solModel]);
assertUltrafastRoute(gateway.state.chatBodies[0]);
}, events);
}
// Requested Ultra is not proof of the served tier. The public turn result
// currently exposes only stopReason and token usage, not tier confirmation.
// Unconfirmed serving appends a warning to the streamed text instead.
for (const metadata of [null, { gateway: { serviceTier: "default", cost: "0.00001" } }]) {
const gateway = mockGateway(metadata);
const events = [];
await withAgent(gateway, { model: supportedModel, ultrafast: true }, async (agent) => {
assert.equal(configOption(events, "ultrafast")?.currentValue, "true");
const result = await runPrompt(
agent,
"say ok",
"okUltrafast was requested, but Gateway did not confirm it was served; this response may have used a standard or lower tier.\n",
);
assert.deepEqual(result, { stopReason: "end_turn", usage: { inputTokens: 3, outputTokens: 2 } });
assert.equal(gateway.state.chatBodies.length, 1);
assertUltrafastRoute(gateway.state.chatBodies[0]);
}, events);
}
// Both modes validate independently at creation. For a model supporting
// both, Ultra wins on the wire; disabling Ultra leaves explicit Fast intact.
for (const ultrafast of [true, false]) {
for (const overrides of [
{ model: { id: solModel, fast: true, ultrafast } },
{ model: solModel, fast: true, ultrafast },
]) {
const gateway = mockGateway();
await withAgent(gateway, overrides, async (agent) => {
assert.equal(gateway.state.catalogFetches, 1);
assert.equal((await runPrompt(agent)).stopReason, "end_turn");
assert.equal(gateway.state.chatBodies.length, 1);
const body = gateway.state.chatBodies[0];
if (ultrafast) {
assertUltrafastRoute(body);
} else {
assert.equal(body.providerOptions?.gateway?.speed, "fast");
assert.equal(body.providerOptions?.gateway?.only, undefined);
assert.equal(body.providerOptions?.openai?.serviceTier, undefined);
assert.equal(body.providerOptions?.gateway?.fast, undefined);
}
});
}
}
{
const gateway = mockGateway();
await assertCreationRejects(gateway, {
model: { id: supportedModel, fast: true, ultrafast: true },
}, (error) => {
assert.equal(error.code, "LIBFX_MODEL_UNSUPPORTED_FAST");
assert.equal(error.model, supportedModel);
assert.equal(error.capability, "fast");
assert.match(error.message, /Fast mode is not available/);
return true;
});
assert.equal(gateway.state.catalogFetches, 1);
assert.equal(gateway.state.chatBodies.length, 0);
}
// The current kernel checkpoint restores conversation, not creation options.
// Recreating with omitted/false Ultra uses the ordinary route; true opts in anew.
{
const sourceGateway = mockGateway();
let checkpoint;
await withAgent(sourceGateway, { model: solModel, ultrafast: true }, async (source) => {
assert.equal((await runPrompt(source, "keep Ultra context")).stopReason, "end_turn");
assert.equal(sourceGateway.state.chatBodies.length, 1);
assertUltrafastRoute(sourceGateway.state.chatBodies[0]);
checkpoint = await source.checkpoint();
assert.ok(checkpoint instanceof Uint8Array && checkpoint.length > 48);
});
for (const mode of [{}, { ultrafast: false }, { ultrafast: true }]) {
const gateway = mockGateway();
const events = [];
await withAgent(gateway, { model: solModel, checkpoint, ...mode }, async (restored) => {
assert.equal(gateway.state.catalogFetches, mode.ultrafast === true ? 1 : 0);
assert.equal((await runPrompt(restored, "continue")).stopReason, "end_turn");
assert.equal(gateway.state.chatBodies.length, 1);
const body = gateway.state.chatBodies[0];
assert.deepEqual(gateway.state.chatModels, [solModel]);
assert.deepEqual(promptText(body, "user"), ["keep Ultra context", "continue"]);
assert.deepEqual(promptText(body, "assistant"), ["ok"]);
if (mode.ultrafast === true) {
assert.equal(configOption(events, "ultrafast")?.currentValue, "true");
assertUltrafastRoute(body);
} else {
assert.equal(body.providerOptions?.openai?.serviceTier, undefined);
assert.equal(body.providerOptions?.gateway?.only, undefined);
assert.equal(body.providerOptions?.gateway?.speed, undefined);
assert.equal(body.providerOptions?.gateway?.fast, undefined);
}
}, events);
}
}
} finally {
if (previousHome === undefined) delete process.env.HOME;
else process.env.HOME = previousHome;
await rm(home, { recursive: true, force: true });
}
console.log(`${backend} agent ultrafast support passed`);
+98
View File
@@ -211,6 +211,104 @@ try {
expect(result.state === "unsupported", `unexpected state ${result.state}`);
});
// Exercise the public browser SDK without starting the demo's live transport.
{
const { targetId } = await command("Target.createTarget", { url: "about:blank" });
try {
const { sessionId } = await command("Target.attachToTarget", { targetId, flatten: true });
await command("Runtime.enable", {}, sessionId);
await command("Page.enable", {}, sessionId);
await command("Page.navigate", { url: `http://127.0.0.1:${port}/sdk/index.html?force-unsupported=1` }, sessionId);
await waitFor("window.__fxCoreTest?.state === 'unsupported'", sessionId);
const evaluated = await withTimeout(command("Runtime.evaluate", {
awaitPromise: true,
returnByValue: true,
expression: `(async () => {
const expect = ${expect.toString()};
const { createFxAgent } = await import('/sdk/browser.js');
const response = await fetch('/zig-out/bin/fx-core.wasm', { cache: 'no-store' });
expect(response.ok, 'local core WASM fetch failed: ' + response.status);
const wasm = await response.arrayBuffer();
const astra = 'openai/gpt-6-astra';
const sol = 'openai/gpt-5.6-sol';
const pricing = { input_cache_read: '0.000001', service_tiers: {
ultrafast: { input: '0.00006', output: '0.0003', input_cache_read: '0.000006' },
} };
const cases = [
{ name: 'Astra true', model: astra, ultrafast: true },
{ name: 'priced Sol true', model: sol, ultrafast: true },
{ name: 'Astra false', model: astra, ultrafast: false },
{ name: 'priced Sol false', model: sol, ultrafast: false },
{ name: 'unpriced Sol rejection', model: sol, ultrafast: true, reject: true },
];
const passed = [];
for (const scenario of cases) {
let agent;
let catalogFetches = 0;
const requests = [];
const fakeFetch = async (_url, init = {}) => {
const method = String(init.method ?? 'GET').toUpperCase();
if (method === 'GET') {
catalogFetches++;
return Response.json({ object: 'list', data: [
{ id: astra, type: 'language', owned_by: 'openai', pricing },
{ id: sol, type: 'language', owned_by: 'openai', ...(scenario.reject ? {} : { pricing }) },
] });
}
expect(method === 'POST', scenario.name + ': unexpected method ' + method);
requests.push({ body: JSON.parse(new TextDecoder().decode(init.body)),
model: new Headers(init.headers).get('ai-language-model-id') });
const frames = [
{ type: 'text-delta', delta: 'ok' },
{ type: 'finish', finishReason: { unified: 'stop', raw: 'stop' },
usage: { inputTokens: { total: 3 }, outputTokens: { total: 2 } },
providerMetadata: { gateway: { serviceTier: 'ultrafast', cost: '0.00036' } } },
];
return new Response(frames.map(frame => 'data: ' + JSON.stringify(frame) + '\\n\\n').join('') + 'data: [DONE]\\n\\n',
{ headers: { 'content-type': 'text/event-stream' } });
};
try {
try {
agent = await createFxAgent({ wasm, apiKey: 'browser-ultrafast-test-key',
model: { id: scenario.model, ultrafast: scenario.ultrafast }, fetch: fakeFetch });
} catch (error) {
if (!scenario.reject) throw error;
expect(error.code === 'LIBFX_MODEL_UNSUPPORTED_ULTRAFAST', 'unexpected rejection code: ' + error.code);
expect(error.model === sol && error.capability === 'ultrafast', 'rejection omitted Sol capability');
expect(/Ultrafast mode is not available/.test(error.message), 'unexpected rejection: ' + error.message);
expect(catalogFetches === 1 && requests.length === 0, 'unpriced Sol sent a completion POST');
passed.push(scenario.name);
continue;
}
expect(!scenario.reject, 'unpriced Sol creation unexpectedly succeeded');
expect(catalogFetches === (scenario.ultrafast ? 1 : 0), scenario.name + ': wrong creation fetch count');
const turn = agent.prompt('say ok');
let text = '';
for await (const event of turn) if (event.type === 'text_delta') text += event.delta;
expect((await turn.result).stopReason === 'end_turn' && text === 'ok', scenario.name + ': unexpected reply ' + text);
expect(requests.length === 1 && requests[0].model === scenario.model, scenario.name + ': wrong completion model/count');
const options = requests[0].body.providerOptions;
expect(options?.openai?.serviceTier === (scenario.ultrafast ? 'ultrafast' : undefined), scenario.name + ': wrong service tier');
expect(JSON.stringify(options?.gateway?.only) === (scenario.ultrafast ? '["openai"]' : undefined), scenario.name + ': wrong provider route');
expect(options?.gateway?.speed === undefined && options?.gateway?.fast === undefined, scenario.name + ': Fast leaked');
passed.push(scenario.name);
} finally {
await agent?.close();
}
}
return passed;
})()`,
}, sessionId), "browser Ultrafast case timed out", 15000);
expect(!evaluated.exceptionDetails, evaluated.result?.description || evaluated.exceptionDetails?.text || "browser Ultrafast exception");
expect(JSON.stringify(evaluated.result.value) === JSON.stringify([
"Astra true", "priced Sol true", "Astra false", "priced Sol false", "unpriced Sol rejection",
]), `unexpected browser Ultrafast cases ${JSON.stringify(evaluated.result.value)}`);
console.log("browser core ultrafast routing and eligibility passed");
} finally {
await command("Target.closeTarget", { targetId });
}
}
const { targetId } = await command("Target.createTarget", { url: "about:blank" });
const { sessionId } = await command("Target.attachToTarget", { targetId, flatten: true });
try {
+14
View File
@@ -80,6 +80,20 @@ await assert.rejects(
!String(error.cause).includes("high-level createFxAgent invoked"),
);
await assert.rejects(
createFxAgent({
backend: "native",
nativeAddon: {
libfxApiVersion: 3,
createCore() { throw new Error("old addon must not receive ultrafast"); },
},
apiKey: "loader-key",
ultrafast: true,
}),
(error) => error?.code === "LIBFX_NATIVE_CAPABILITY_UNAVAILABLE" &&
!String(error.cause).includes("old addon must not receive ultrafast"),
);
for (const gatewayChatUrl of [
"http://attacker.example/chat",
"https://[redacted]@example.com/chat",
+48
View File
@@ -144,6 +144,42 @@ const ProviderTerminalPublication = enum {
published,
};
/// Pure final admission for every ACP model request. A persisted or
/// reconfigured session cannot issue an ultrafast request unless the current
/// Gateway catalog still verifies the selected model capability.
fn ultrafastPromptAllowed(
state: *const server.ServerState,
session: *const server.ActiveSessionState,
) bool {
if (!session.ultrafast_mode) return true;
const catalog_ready = state.capability_resolver.catalogEntries() != null;
const bundle = state.cfg.provider_set.select(session.provider);
const capabilities = state.capability_resolver.available(
session.model,
bundle.fallbackModelCapabilities(session.model),
);
return ultrafastCapabilityAllowed(
session.provider,
catalog_ready,
capabilities.supports_ultrafast_mode,
);
}
fn ultrafastCapabilityAllowed(
provider: model_provider.ProviderId,
catalog_ready: bool,
supports_ultrafast: bool,
) bool {
return provider == .gateway and catalog_ready and supports_ultrafast;
}
test "ultrafastPromptAllowed requires a verified Gateway capability" {
try std.testing.expect(ultrafastCapabilityAllowed(.gateway, true, true));
try std.testing.expect(!ultrafastCapabilityAllowed(.codex, true, true));
try std.testing.expect(!ultrafastCapabilityAllowed(.gateway, false, true));
try std.testing.expect(!ultrafastCapabilityAllowed(.gateway, true, false));
}
const AgentMessageKind = enum {
assistant,
operational,
@@ -401,6 +437,7 @@ const AcpContext = struct {
.gateway_models_path = self.state.cfg.gateway_models_path,
.agent_step_limit = session.agent_step_limit,
.fast_mode = session.fast_mode,
.ultrafast_mode = session.ultrafast_mode,
.effort = session.effort,
.first_call_tool_choice = session.first_call_tool_choice,
.permission_mode = self.captured_permission_mode orelse session.permission_mode,
@@ -708,6 +745,12 @@ pub fn handlePrompt(
credentials.missing_credential_message,
} };
}
if (!ultrafastPromptAllowed(state, session)) {
return .{ .rpc_error = .{
.code = ErrorCode.invalid_request,
.message = "Ultrafast mode requires a verified compatible Gateway model",
} };
}
const params = msg.params_raw orelse return .{
.rpc_error = .{
@@ -1186,6 +1229,7 @@ fn buildAgentConfig(
.max_tool_result_bytes = session.max_tool_result_bytes,
.cancel_flag = &session.cancel_flag,
.fast_mode = session.fast_mode,
.ultrafast_mode = session.ultrafast_mode,
.effort = session.effort,
.first_call_tool_choice = session.first_call_tool_choice,
.workspace_root = state.workspace_root,
@@ -2399,6 +2443,7 @@ fn commitContextCompaction(
next.preferences.provider = session.provider;
next.preferences.effort = session.effort;
next.preferences.fast_mode = session.fast_mode;
next.preferences.ultrafast_mode = session.ultrafast_mode;
const usage = try session.session_rt.usage.snapshot(ctx.alloc);
if (next.usage) |*old| old.deinit(ctx.alloc);
next.usage = usage;
@@ -5036,6 +5081,7 @@ test "ACP prompt agent config carries request options from active session" {
.source = .command_line,
};
state.active_session.?.fast_mode = true;
state.active_session.?.ultrafast_mode = true;
state.active_session.?.effort = types.ReasoningEffort.literal("high");
const session = &state.active_session.?;
@@ -5046,6 +5092,7 @@ test "ACP prompt agent config carries request options from active session" {
}, true);
try std.testing.expect(config.fast_mode);
try std.testing.expect(config.ultrafast_mode);
try std.testing.expectEqual(types.ReasoningEffort.literal("high"), config.effort);
try std.testing.expectEqual(@as(usize, 17), config.context_limits.project_instruction_file_bytes.effectiveBytes());
try std.testing.expectEqual(config_runtime.context_limits.Source.command_line, config.context_limits.project_instruction_file_bytes.source);
@@ -5065,4 +5112,5 @@ test "ACP prompt agent config carries request options from active session" {
try std.testing.expectEqual(state.cfg.gateway_retry_count, state.web_search_runtime.gateway_retry_count);
try std.testing.expectEqualStrings(state.cfg.gateway_chat_url, state.web_search_runtime.gateway_chat_url);
try std.testing.expectEqualStrings("/models", tool_ctx.gateway_models_path);
try std.testing.expect(tool_ctx.ultrafast_mode);
}
+311
View File
@@ -217,6 +217,7 @@ pub const ActiveSessionState = struct {
agent_step_limit: usize,
max_tool_result_bytes: usize,
fast_mode: bool,
ultrafast_mode: bool = false,
effort: types.ReasoningEffort,
first_call_tool_choice: types.ToolChoice,
permission_mode: types.PermissionMode,
@@ -300,6 +301,9 @@ pub const ServerState = struct {
max_tool_result_bytes: usize = 64 * 1024,
context_limits: config_runtime.context_limits.Values = .{},
fast_mode: bool = false,
ultrafast_mode: bool = false,
configured_ultrafast_mode: bool = false,
process_ultrafast_override: ?bool = null,
effort: types.ReasoningEffort = .auto,
first_call_tool_choice: types.ToolChoice = .auto,
context_enabled: bool = true,
@@ -2099,6 +2103,37 @@ fn loadConfiguredStartupState(state: *const ServerState, alloc: Allocator) !app_
);
}
fn configureUltrafastStartup(state: *ServerState, startup: *const app_lifecycle.StartupState) void {
state.configured_ultrafast_mode = startup.configured_ultrafast_mode;
state.process_ultrafast_override = state.cfg.ultrafast_override orelse startup.ultrafast_process_override;
state.ultrafast_mode = state.process_ultrafast_override orelse state.configured_ultrafast_mode;
}
test "ACP ultrafast startup separates configured preferences from CLI and env overrides" {
const overrides = [_]?bool{ null, false, true };
for ([_]bool{ false, true }) |baseline| {
for (overrides) |env| {
for (overrides) |cli| {
var state = ServerState{
.alloc = std.testing.allocator,
.cfg = undefined,
.writer = jsonrpc.Writer.init(),
};
state.cfg.ultrafast_override = cli;
const startup = app_lifecycle.StartupState{
.agent_step_limit = 1,
.configured_ultrafast_mode = baseline,
.ultrafast_process_override = env,
};
configureUltrafastStartup(&state, &startup);
try std.testing.expectEqual(baseline, state.configured_ultrafast_mode);
try std.testing.expectEqual(cli orelse env, state.process_ultrafast_override);
try std.testing.expectEqual(cli orelse env orelse baseline, state.ultrafast_mode);
}
}
}
}
fn handleInitialize(state: *ServerState, alloc: Allocator, msg: *jsonrpc.Message) !void {
if (state.initialized) {
return state.writer.writeError(alloc, msg.id, .{
@@ -2244,6 +2279,7 @@ fn handleInitialize(state: *ServerState, alloc: Allocator, msg: *jsonrpc.Message
state.context_limits.applyCommandLine(state.cfg.context_limit_overrides);
state.fast_mode = startup.fast_mode and
(state.cfg.model_override == null or startup.fast_mode_source != .compiled_default);
configureUltrafastStartup(state, &startup);
state.effort = startup.effort;
state.first_call_tool_choice = startup.first_call_tool_choice;
state.context_enabled = startup.context_enabled;
@@ -2299,6 +2335,7 @@ fn handleInitialize(state: *ServerState, alloc: Allocator, msg: *jsonrpc.Message
if (state.cfg.fast_override) |fast| {
if (!try applyFastOverride(state, alloc, msg, fast)) return;
}
if (!try applyUltrafastOverride(state, alloc, msg, state.ultrafast_mode)) return;
state.terminal_ui = request.terminal_ui;
state.client_fs_read = request.client_fs_read;
@@ -2535,6 +2572,81 @@ test "fastOverrideRejection names models without a fast path" {
try std.testing.expectEqualStrings("Fast mode is not available for model \"provider/plain\"", rejection);
}
/// Applies a host-supplied ultrafast override before session creation. Unlike
/// fast mode, enabling it requires a verified Gateway catalog capability; a
/// catalog failure cannot silently send an unverified ultrafast request.
fn applyUltrafastOverride(state: *ServerState, alloc: Allocator, msg: *jsonrpc.Message, ultrafast: bool) !bool {
if (!ultrafast) {
state.ultrafast_mode = false;
return true;
}
if (state.provider != .gateway) {
return writeUltrafastOverrideFailure(state, alloc, msg, "Ultrafast mode is unavailable for the selected provider");
}
const catalog_provider = catalogProviderFor(state, state.provider) orelse
return writeUltrafastOverrideFailure(state, alloc, msg, "Ultrafast mode is unavailable for the selected provider");
var catalog_cancel_flag = std.atomic.Value(bool).init(false);
const bundle = state.cfg.provider_set.select(state.provider);
const capabilities = try state.capability_resolver.resolve(alloc, catalog_provider, .{
.access = if (state.cfg.auth_mode == .host_managed)
.host_managed
else
credentials.catalogAccessForCredentialAndAccount(
state.credential_source,
state.api_key,
state.gateway_team,
state.account_id,
),
.endpoint = state.cfg.gateway_models_path,
.cancel_flag = &catalog_cancel_flag,
}, state.selected_model, bundle.fallbackModelCapabilities(state.selected_model));
if (state.capability_resolver.state == .failed) {
return writeUltrafastOverrideFailure(state, alloc, msg, "Ultrafast mode requires a verified Gateway model catalog");
}
const rejection = try ultrafastOverrideRejection(alloc, state.provider, capabilities, state.selected_model);
if (rejection) |message| {
defer alloc.free(message);
return writeUltrafastOverrideFailure(state, alloc, msg, message);
}
state.ultrafast_mode = true;
return true;
}
fn writeUltrafastOverrideFailure(state: *ServerState, alloc: Allocator, msg: *jsonrpc.Message, message: []const u8) !bool {
try state.writer.writeError(alloc, msg.id, .{
.code = ErrorCode.invalid_params,
.message = message,
.data = .{
.code = "LIBFX_MODEL_UNSUPPORTED_ULTRAFAST",
.model = state.selected_model,
.capability = "ultrafast",
},
});
return false;
}
/// Pure capability policy: ultrafast is an opt-in Gateway OpenAI catalog lane.
/// Caller frees a non-null rejection message.
fn ultrafastOverrideRejection(
alloc: Allocator,
provider: model_provider.ProviderId,
capabilities: model_capabilities.Capabilities,
model: []const u8,
) Allocator.Error!?[]u8 {
if (provider == .gateway and capabilities.supports_ultrafast_mode) return null;
return try std.fmt.allocPrint(alloc, "Ultrafast mode is not available for model \"{s}\"", .{model});
}
test "ultrafastOverrideRejection accepts only Gateway models with the catalog capability" {
const alloc = std.testing.allocator;
try std.testing.expectEqual(@as(?[]u8, null), try ultrafastOverrideRejection(alloc, .gateway, .{ .supports_ultrafast_mode = true }, "openai/model"));
const rejection = (try ultrafastOverrideRejection(alloc, .codex, .{ .supports_ultrafast_mode = true }, "openai/model")).?;
defer alloc.free(rejection);
try std.testing.expect(std.mem.find(u8, rejection, "openai/model") != null);
const unavailable = (try ultrafastOverrideRejection(alloc, .gateway, .{}, "openai/model")).?;
defer alloc.free(unavailable);
}
fn handleCancel(state: *ServerState, notify_client: bool) void {
if (state.active_session) |*session| {
debug_trace.eventf("interrupt", "cancel_requested", .{}, "source=acp active_tool_known=false", .{});
@@ -2835,6 +2947,17 @@ fn handleSetConfigOption(state: *ServerState, alloc: Allocator, msg: *jsonrpc.Me
defer state.subagent_authority_mutex.unlock(io_mod.getIo());
applySessionMode(state.cfg.mode_registry, session, value);
}
} else if (std.mem.eql(u8, config_id, "ultrafast")) {
const session = if (state.active_session) |*active| active else return state.writer.writeError(alloc, msg.id, .{
.code = ErrorCode.invalid_request,
.message = "No active session",
});
const ultrafast = parseConfigBool(value) orelse
return state.writer.writeError(alloc, msg.id, .{
.code = ErrorCode.invalid_params,
.message = "Ultrafast mode must be true or false",
});
if (!try applyActiveSessionUltrafast(state, alloc, msg, session, ultrafast)) return;
} else if (std.mem.eql(u8, config_id, "effort")) {
const session = if (state.active_session) |*active| active else return state.writer.writeError(alloc, msg.id, .{
.code = ErrorCode.invalid_request,
@@ -2890,6 +3013,10 @@ fn handleSetConfigOption(state: *ServerState, alloc: Allocator, msg: *jsonrpc.Me
try out.writer.writeAll(",");
try sessions.writeEffortConfigOption(&out.writer, config.efforts, config.current);
}
if (sessions.ultrafastConfigState(state)) |current| {
try out.writer.writeAll(",");
try sessions.writeUltrafastConfigOption(&out.writer, current);
}
try out.writer.writeAll("]}");
try state.writer.writeResponse(alloc, msg.id, out.writer.buffered());
}
@@ -2957,11 +3084,14 @@ fn setV2Preferences(
model: []const u8,
effort: types.ReasoningEffort,
) !void {
var durable = try v2.currentPreferences(v2.alloc);
defer durable.deinit(v2.alloc);
try v2.setPreferences(.{
.provider = provider,
.model = @constCast(model),
.effort = effort,
.fast_mode = session.fast_mode,
.ultrafast_mode = durable.ultrafast_mode,
});
}
@@ -3042,6 +3172,105 @@ fn commitActiveSessionEffort(
session.effort = effort;
}
fn parseConfigBool(value: []const u8) ?bool {
if (std.mem.eql(u8, value, "true")) return true;
if (std.mem.eql(u8, value, "false")) return false;
return null;
}
/// Applies a strict ultrafast-mode transition to the active session. Every
/// enabled request is admitted against its current provider/model capability.
fn applyActiveSessionUltrafast(
state: *ServerState,
alloc: Allocator,
msg: *jsonrpc.Message,
session: *ActiveSessionState,
ultrafast: bool,
) !bool {
if (ultrafast) {
const bundle = state.cfg.provider_set.select(session.provider);
const capabilities = state.capability_resolver.available(
session.model,
bundle.fallbackModelCapabilities(session.model),
);
const rejection = try ultrafastOverrideRejection(alloc, session.provider, capabilities, session.model);
if (rejection) |message| {
defer alloc.free(message);
try state.writer.writeError(alloc, msg.id, .{
.code = ErrorCode.invalid_params,
.message = message,
.data = .{
.code = "LIBFX_MODEL_UNSUPPORTED_ULTRAFAST",
.model = session.model,
.capability = "ultrafast",
},
});
return false;
}
}
if (state.cfg.minimal_kernel and session.writable == null and session.v2 == null and session.wasm_state == null) {
session.ultrafast_mode = ultrafast;
} else {
commitActiveSessionUltrafast(alloc, session, ultrafast) catch {
try state.writer.writeError(alloc, msg.id, .{
.code = ErrorCode.internal_error,
.message = "Failed to persist session ultrafast preference",
});
return false;
};
}
return true;
}
test "ACP ultrafast disable applies to volatile SDK sessions without a durable store" {
var state = ServerState{
.alloc = std.testing.allocator,
.cfg = undefined,
.writer = jsonrpc.Writer.init(),
.configured_ultrafast_mode = true,
.process_ultrafast_override = true,
};
state.cfg.minimal_kernel = true;
var active: ActiveSessionState = undefined;
active.writable = null;
active.v2 = null;
active.wasm_state = null;
active.ultrafast_mode = true;
var msg = jsonrpc.Message{ .id = .{ .integer = 1 }, .method = "session/set_config_option" };
try std.testing.expect(try applyActiveSessionUltrafast(&state, std.testing.allocator, &msg, &active, false));
try std.testing.expect(!active.ultrafast_mode);
try std.testing.expect(state.configured_ultrafast_mode);
try std.testing.expectEqual(@as(?bool, true), state.process_ultrafast_override);
}
fn commitActiveSessionUltrafast(
alloc: Allocator,
session: *ActiveSessionState,
ultrafast: bool,
) !void {
if (comptime host_target.is_wasm) {
return sessions.commitWasmUltrafastPreference(alloc, session, ultrafast);
}
session.session_write_mutex.lockUncancelable(io_mod.getIo());
defer session.session_write_mutex.unlock(io_mod.getIo());
if (session.v2) |v2| {
var preferences = try v2.currentPreferences(alloc);
defer preferences.deinit(alloc);
preferences.ultrafast_mode = ultrafast;
try v2.setPreferences(preferences);
} else {
const writable = if (session.writable) |*active| active else return error.SessionPersistenceUnavailable;
if (ultrafast or writable.state.preferences.ultrafast_mode) {
_ = try writable.appendEvent(
alloc,
.{ .preferences_changed = .{ .ultrafast_mode = ultrafast } },
io_mod.milliTimestamp(),
);
}
}
session.ultrafast_mode = ultrafast;
}
fn handleSetMode(state: *ServerState, alloc: Allocator, msg: *jsonrpc.Message) !void {
const params = msg.params_raw orelse return state.writer.writeError(alloc, msg.id, .{
.code = ErrorCode.invalid_params,
@@ -3527,6 +3756,88 @@ fn acpModelTestState(
};
}
test "ACP ultrafast writes preserve v2 baselines across other preference changes" {
const alloc = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const home = try io_mod.dirRealpathAlloc(alloc, tmp.dir, ".");
defer alloc.free(home);
var store = try session_adapter.Store.open(alloc, home);
defer store.deinit(alloc);
const v2 = try session_adapter.Session.create(alloc, &store, home, .acp, .{
.preferences = .{ .model = @constCast("old-model"), .effort = .auto, .fast_mode = false },
.language = types.ConversationLanguage.default(),
.permission_state = .{},
});
defer v2.close();
var active: ActiveSessionState = undefined;
active.session_write_mutex = .init;
active.wasm_state = null;
active.writable = null;
active.v2 = v2;
active.model = try alloc.dupe(u8, "old-model");
defer alloc.free(active.model);
active.provider = .gateway;
active.effort = .auto;
active.fast_mode = false;
active.ultrafast_mode = false;
try commitActiveSessionUltrafast(alloc, &active, true);
try std.testing.expect(active.ultrafast_mode);
active.ultrafast_mode = false;
try commitActiveSessionModel(alloc, &active, "new-model");
try commitActiveSessionProvider(alloc, &active, .codex, "child-model");
try commitActiveSessionEffort(alloc, &active, .literal("high"));
{
var preferences = try v2.currentPreferences(alloc);
defer preferences.deinit(alloc);
try std.testing.expect(preferences.ultrafast_mode);
try std.testing.expectEqualStrings("child-model", preferences.model);
try std.testing.expectEqual(model_provider.ProviderId.codex, preferences.provider);
try std.testing.expectEqualStrings("high", preferences.effort.label());
}
var failing = std.testing.FailingAllocator.init(alloc, .{ .fail_index = 0 });
try std.testing.expectError(error.OutOfMemory, commitActiveSessionUltrafast(failing.allocator(), &active, false));
try std.testing.expect(!active.ultrafast_mode);
{
var preferences = try v2.currentPreferences(alloc);
defer preferences.deinit(alloc);
try std.testing.expect(preferences.ultrafast_mode);
}
try commitActiveSessionUltrafast(alloc, &active, false);
var preferences = try v2.currentPreferences(alloc);
defer preferences.deinit(alloc);
try std.testing.expect(!preferences.ultrafast_mode);
try std.testing.expect(!active.ultrafast_mode);
}
test "ACP ultrafast v1 writes omit default-off changes and persist explicit disable" {
const alloc = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const home = try io_mod.dirRealpathAlloc(alloc, tmp.dir, ".");
defer alloc.free(home);
var store = try session_store.Store.initFromHome(alloc, home, home);
defer store.deinit(alloc);
var seed = try acpModelTestState(alloc, "acp-ultrafast", home);
defer seed.deinit(alloc);
var active: ActiveSessionState = undefined;
active.session_write_mutex = .init;
active.wasm_state = null;
active.v2 = null;
active.writable = try store.startWritableSession(alloc, seed);
defer active.writable.?.deinit(alloc);
active.ultrafast_mode = false;
const sequence = active.writable.?.conversation_writer.last_seq;
try commitActiveSessionUltrafast(alloc, &active, false);
try std.testing.expectEqual(sequence, active.writable.?.conversation_writer.last_seq);
try commitActiveSessionUltrafast(alloc, &active, true);
try std.testing.expect(active.ultrafast_mode);
try std.testing.expect(active.writable.?.state.preferences.ultrafast_mode);
try commitActiveSessionUltrafast(alloc, &active, false);
try std.testing.expect(!active.ultrafast_mode);
try std.testing.expect(!active.writable.?.state.preferences.ultrafast_mode);
}
test "ACP model commits honor the active session write boundary" {
const alloc = std.testing.allocator;
var active: ActiveSessionState = undefined;
+254 -1
View File
@@ -83,6 +83,7 @@ pub fn handleNewLibfxSession(
.agent_step_limit = state.agent_step_limit,
.max_tool_result_bytes = state.max_tool_result_bytes,
.fast_mode = state.fast_mode,
.ultrafast_mode = state.ultrafast_mode,
.effort = state.effort,
.first_call_tool_choice = state.first_call_tool_choice,
.permission_mode = state.permission_mode,
@@ -138,6 +139,7 @@ pub fn handleNewWasmSession(state: *server.ServerState, alloc: Allocator, msg: *
.agent_step_limit = state.agent_step_limit,
.max_tool_result_bytes = state.max_tool_result_bytes,
.fast_mode = state.fast_mode,
.ultrafast_mode = state.ultrafast_mode,
.effort = state.effort,
.first_call_tool_choice = state.first_call_tool_choice,
.permission_mode = state.permission_mode,
@@ -169,11 +171,13 @@ pub fn commitWasmSessionLocked(alloc: Allocator, session: *server.ActiveSessionS
next.context_history_start = 0;
next.conversation_language = session.session_rt.languageSnapshot();
next.updated_at_ms = io_mod.milliTimestamp();
const model = try alloc.dupe(u8, session.model);
alloc.free(next.preferences.model);
next.preferences.model = try alloc.dupe(u8, session.model);
next.preferences.model = model;
next.preferences.provider = session.provider;
next.preferences.effort = session.effort;
next.preferences.fast_mode = session.fast_mode;
// Ultrafast keeps its durable baseline, not the process-local request.
const usage = try session.session_rt.usage.snapshot(alloc);
if (next.usage) |*old| old.deinit(alloc);
next.usage = usage;
@@ -192,6 +196,117 @@ pub fn commitWasmSession(alloc: Allocator, session: *server.ActiveSessionState)
try commitWasmSessionLocked(alloc, session);
}
pub fn commitWasmUltrafastPreference(alloc: Allocator, session: *server.ActiveSessionState, ultrafast: bool) !void {
session.session_write_mutex.lockUncancelable(io_mod.getIo());
defer session.session_write_mutex.unlock(io_mod.getIo());
const durable = if (session.wasm_state) |*value| value else return error.SessionPersistenceUnavailable;
const previous = durable.preferences.ultrafast_mode;
durable.preferences.ultrafast_mode = ultrafast;
commitWasmSessionLocked(alloc, session) catch |err| {
durable.preferences.ultrafast_mode = previous;
return err;
};
session.ultrafast_mode = ultrafast;
}
test "ACP ultrafast WASM saves preserve baselines and failed preference writes roll back" {
if (comptime host_target.is_wasm) return error.SkipZigTest;
const Host = struct {
var status: i32 = 0;
var stored_ultrafast: bool = false;
var expected_revision_matched: bool = false;
fn commit(
_: [*]const u8,
_: usize,
bytes: [*]const u8,
length: usize,
expected: [*]const u8,
expected_len: usize,
revision: [*]u8,
capacity: usize,
revision_len: *usize,
) callconv(.c) i32 {
expected_revision_matched = std.mem.eql(u8, expected[0..expected_len], "next");
if (status != 0) return status;
var reader = std.Io.Reader.fixed(bytes[0..length]);
var saved = session_codec.decodeState(std.testing.allocator, &reader, .{}) catch return -1;
defer saved.deinit(std.testing.allocator);
stored_ultrafast = saved.preferences.ultrafast_mode;
if (capacity < 4) return -1;
@memcpy(revision[0..4], "next");
revision_len.* = 4;
return 0;
}
};
@export(&Host.commit, .{ .name = "fx_session_commit" });
Host.status = 0;
Host.stored_ultrafast = false;
Host.expected_revision_matched = false;
const alloc = std.testing.allocator;
var state = server.ServerState{
.alloc = alloc,
.cfg = acpSessionTestConfig(),
.writer = jsonrpc.Writer.init(),
.configured_model = @constCast("openai/test"),
.configured_ultrafast_mode = true,
};
var active = server.ActiveSessionState{
.session_id = @constCast("wasm-test"),
.wasm_state = try freshAcpState(&state, alloc, "/workspace"),
.model = @constCast("openai/test"),
.mode = "default",
.workspace_root = "/workspace",
.api_key = "",
.agent_step_limit = 1,
.max_tool_result_bytes = 1024,
.fast_mode = false,
.ultrafast_mode = false,
.effort = .auto,
.first_call_tool_choice = .auto,
.permission_mode = .ask,
.permission_rules = .{},
.session_rt = session_runtime.SessionRuntime.initWithProviders(4, state.cfg.provider_set.deferredUsageProviders()),
.cancel_flag = .init(false),
.pending_prompt_id = null,
};
defer active.session_rt.deinit(alloc);
defer active.wasm_state.?.deinit(alloc);
defer if (active.wasm_revision) |revision| alloc.free(revision);
try commitWasmSession(alloc, &active);
try std.testing.expect(Host.stored_ultrafast);
try std.testing.expect(active.wasm_state.?.preferences.ultrafast_mode);
try std.testing.expect(!active.ultrafast_mode);
try commitWasmUltrafastPreference(alloc, &active, false);
try std.testing.expect(!Host.stored_ultrafast);
try std.testing.expect(!active.wasm_state.?.preferences.ultrafast_mode);
for ([_]i32{ -2, -1 }) |status| {
Host.status = status;
try std.testing.expectError(
if (status == -2) error.SessionRevisionConflict else error.SessionStoreUnavailable,
commitWasmUltrafastPreference(alloc, &active, true),
);
try std.testing.expect(!active.ultrafast_mode);
try std.testing.expect(!active.wasm_state.?.preferences.ultrafast_mode);
try std.testing.expect(!Host.stored_ultrafast);
try std.testing.expect(Host.expected_revision_matched);
try std.testing.expectEqualStrings("next", active.wasm_revision.?);
}
Host.status = 0;
try commitWasmUltrafastPreference(alloc, &active, true);
try std.testing.expect(active.ultrafast_mode);
try std.testing.expect(active.wasm_state.?.preferences.ultrafast_mode);
Host.status = -2;
try std.testing.expectError(error.SessionRevisionConflict, commitWasmUltrafastPreference(alloc, &active, false));
try std.testing.expect(active.ultrafast_mode);
try std.testing.expect(active.wasm_state.?.preferences.ultrafast_mode);
try std.testing.expect(Host.stored_ultrafast);
Host.status = 0;
active.ultrafast_mode = false;
try commitWasmSession(alloc, &active);
try std.testing.expect(Host.stored_ultrafast);
}
pub fn handleNewSession(state: *server.ServerState, alloc: Allocator, msg: *jsonrpc.Message) !void {
// Validate the client system prompt before any session side effect.
var client_system_prompt = client_instructions.parse(alloc, msg.params_raw) catch |err| switch (err) {
@@ -337,6 +452,7 @@ pub fn handleNewSession(state: *server.ServerState, alloc: Allocator, msg: *json
.model = model_copy,
.provider = state.provider,
.fast_mode = state.fast_mode,
.ultrafast_mode = state.ultrafast_mode,
.effort = state.effort,
.session_rt = session_rt,
.mcp = session_mcp,
@@ -397,6 +513,7 @@ fn startV2Session(
.model = state.configured_model,
.effort = state.effort,
.fast_mode = state.fast_mode,
.ultrafast_mode = state.configured_ultrafast_mode,
},
.language = session_runtime.ConversationLanguage.default(),
.permission_state = .{},
@@ -435,6 +552,7 @@ fn startV2Session(
.model = model_copy,
.provider = state.provider,
.fast_mode = state.fast_mode,
.ultrafast_mode = state.ultrafast_mode,
.effort = state.effort,
.session_rt = session_rt,
.mcp = session_mcp,
@@ -486,6 +604,10 @@ fn writeNewSessionResponse(
try out.writer.writeAll(",");
try writeEffortConfigOption(&out.writer, config.efforts, config.current);
}
if (ultrafastConfigState(state)) |current| {
try out.writer.writeAll(",");
try writeUltrafastConfigOption(&out.writer, current);
}
try out.writer.writeAll("],\"modes\":{\"currentModeId\":");
try writeJsonStr(state.cfg.mode_registry.default_mode_id, &out.writer);
try out.writer.writeAll(",\"availableModes\":");
@@ -565,6 +687,7 @@ pub fn handleLoadWasmSession(state: *server.ServerState, alloc: Allocator, msg:
.agent_step_limit = state.agent_step_limit,
.max_tool_result_bytes = state.max_tool_result_bytes,
.fast_mode = loaded.state.preferences.fast_mode,
.ultrafast_mode = restoredUltrafastMode(state, loaded.state.preferences.ultrafast_mode),
.effort = loaded.state.preferences.effort,
.first_call_tool_choice = state.first_call_tool_choice,
.permission_mode = state.permission_mode,
@@ -804,6 +927,7 @@ fn handleRestoreSession(
.model = state.configured_model,
.effort = state.effort,
.fast_mode = state.fast_mode,
.ultrafast_mode = state.configured_ultrafast_mode,
};
writable = subagent_resume_admission.resumeForExternalPrompt(
store.?,
@@ -901,6 +1025,7 @@ fn handleRestoreSession(
.provider = effective_provider,
.credential = if (staged_credential) |*credential| credential else null,
.fast_mode = durable.preferences.fast_mode,
.ultrafast_mode = restoredUltrafastMode(state, durable.preferences.ultrafast_mode),
.effort = durable.preferences.effort,
.session_rt = session_rt,
.mcp = session_mcp,
@@ -1138,6 +1263,10 @@ fn writeLoadSessionResponse(
try out.writer.writeAll(",");
try writeEffortConfigOption(&out.writer, config.efforts, config.current);
}
if (ultrafastConfigState(state)) |current| {
try out.writer.writeAll(",");
try writeUltrafastConfigOption(&out.writer, current);
}
try out.writer.writeAll("],\"modes\":{\"currentModeId\":");
try writeJsonStr(state.cfg.mode_registry.default_mode_id, &out.writer);
try out.writer.writeAll(",\"availableModes\":");
@@ -1175,6 +1304,7 @@ fn freshAcpState(
.model = model,
.effort = state.effort,
.fast_mode = state.fast_mode,
.ultrafast_mode = state.configured_ultrafast_mode,
},
.history = history,
.total_input_tokens = 0,
@@ -1182,6 +1312,26 @@ fn freshAcpState(
};
}
fn restoredUltrafastMode(state: *const server.ServerState, preference: bool) bool {
return state.process_ultrafast_override orelse preference;
}
test "ACP ultrafast resume uses saved preferences with process-only overrides" {
var state = server.ServerState{
.alloc = std.testing.allocator,
.cfg = acpSessionTestConfig(),
.writer = jsonrpc.Writer.init(),
.configured_ultrafast_mode = true,
};
try std.testing.expect(!restoredUltrafastMode(&state, false));
try std.testing.expect(restoredUltrafastMode(&state, true));
state.process_ultrafast_override = false;
try std.testing.expect(!restoredUltrafastMode(&state, true));
state.process_ultrafast_override = true;
try std.testing.expect(restoredUltrafastMode(&state, false));
try std.testing.expect(state.configured_ultrafast_mode);
}
const SessionActivation = struct {
session_id: []u8,
/// Exactly one of `writable` (with the store) and `v2` is set.
@@ -1191,6 +1341,7 @@ const SessionActivation = struct {
provider: model_provider.ProviderId,
credential: ?*credentials.Credential = null,
fast_mode: bool,
ultrafast_mode: bool,
effort: types.ReasoningEffort,
session_rt: session_runtime.SessionRuntime,
mcp: ?*mcp_runtime.McpRuntime,
@@ -1332,6 +1483,7 @@ fn activateSession(
.agent_step_limit = state.agent_step_limit,
.max_tool_result_bytes = state.max_tool_result_bytes,
.fast_mode = activation.fast_mode,
.ultrafast_mode = activation.ultrafast_mode,
.effort = activation.effort,
.first_call_tool_choice = state.first_call_tool_choice,
.permission_mode = state.permission_mode,
@@ -2227,6 +2379,37 @@ pub fn effortConfigState(state: *server.ServerState) ?EffortConfigState {
return .{ .efforts = capabilities.reasoning_efforts, .current = active.effort };
}
/// Active-session ultrafast selector state. The option is exposed only for a
/// verified Gateway catalog model with the explicit capability.
pub fn ultrafastConfigState(state: *server.ServerState) ?bool {
const active = if (state.active_session) |*session| session else return null;
if (active.provider != .gateway) return null;
const bundle = state.cfg.provider_set.select(active.provider);
const capabilities = state.capability_resolver.available(
active.model,
bundle.fallbackModelCapabilities(active.model),
);
if (!capabilities.supports_ultrafast_mode) return null;
return active.ultrafast_mode;
}
pub fn writeUltrafastConfigOption(w: *std.Io.Writer, current: bool) !void {
try w.writeAll("{\"id\":\"ultrafast\",\"name\":\"Ultrafast Mode\",\"description\":\"Uses the model's ultrafast Gateway lane\",\"category\":\"model\",\"type\":\"select\",\"currentValue\":");
try writeJsonStr(if (current) "true" else "false", w);
try w.writeAll(",\"options\":[{\"value\":\"false\",\"name\":\"off\"},{\"value\":\"true\",\"name\":\"on\"}]}");
}
test "writeUltrafastConfigOption produces a strict boolean select" {
const alloc = std.testing.allocator;
var out: std.Io.Writer.Allocating = .init(alloc);
defer out.deinit();
try writeUltrafastConfigOption(&out.writer, true);
var parsed = try std.json.parseFromSlice(std.json.Value, alloc, out.writer.buffered(), .{});
defer parsed.deinit();
try std.testing.expectEqualStrings("ultrafast", parsed.value.object.get("id").?.string);
try std.testing.expectEqualStrings("true", parsed.value.object.get("currentValue").?.string);
}
pub fn effortSupportedBy(efforts: model_capabilities.ReasoningEffortOptions, effort: types.ReasoningEffort) bool {
if (effort == .auto) return true;
for (efforts.slice()) |option| {
@@ -2886,6 +3069,76 @@ test "ACP project MCP loading expands workspace environment templates" {
try std.testing.expectEqualStrings("secret-value", config.env[0].value);
}
test "ACP ultrafast new load and resume preserve configured baselines on both backends" {
const alloc = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
try tmp.dir.createDirPath(io_mod.getIo(), "home/.fx");
try tmp.dir.createDirPath(io_mod.getIo(), "workspace");
const home_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "home");
defer alloc.free(home_path);
const workspace_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "workspace");
defer alloc.free(workspace_path);
const env = try AcpSessionTestHome.install(alloc, home_path);
defer env.deinit();
var capture = try tmp.dir.createFile(io_mod.getIo(), "ultrafast.jsonl", .{ .read = true });
defer capture.close(io_mod.getIo());
for ([_]bool{ false, true }) |v2_backend| {
for ([_]bool{ false, true }) |baseline| {
for ([_]?bool{ null, false, true }) |process| {
var arena_state = std.heap.ArenaAllocator.init(alloc);
defer arena_state.deinit();
const arena = arena_state.allocator();
var state = try initAcpSessionTestState(arena, workspace_path, capture);
defer state.deinit();
state.cfg.auth_mode = .host_managed;
state.credential_source = .host_managed;
state.cfg.minimal_kernel = true;
state.cfg.allow_acp_mcp = false;
state.cfg.home_override = home_path;
state.configured_ultrafast_mode = baseline;
state.process_ultrafast_override = process;
state.ultrafast_mode = process orelse baseline;
if (v2_backend) {
state.sessions_v2_requested = true;
state.sessions_v2 = try session_adapter.Store.open(arena, home_path);
}
var msg = jsonrpc.Message{
.id = .{ .integer = 1 },
.method = "session/new",
.params_raw = "{\"mcpServers\":[]}",
};
try handleNewSession(&state, arena, &msg);
const active = &state.active_session.?;
try std.testing.expectEqual(process orelse baseline, active.ultrafast_mode);
if (active.v2) |v2| {
const preferences = try v2.currentPreferences(arena);
try std.testing.expectEqual(baseline, preferences.ultrafast_mode);
try v2.commitTurn(.{ .assistant = .{
.user = .{ .text = @constCast("saved request") },
.assistant = @constCast("saved answer"),
} }, types.ConversationLanguage.default());
} else {
try std.testing.expectEqual(baseline, active.writable.?.state.preferences.ultrafast_mode);
}
const id = try arena.dupe(u8, active.session_id);
inline for (.{ handleLoadSession, handleResumeSession }) |restore| {
try server.releaseActiveSession(&state);
msg.params_raw = try std.fmt.allocPrint(arena, "{{\"sessionId\":\"{s}\",\"mcpServers\":[]}}", .{id});
try restore(&state, arena, &msg);
const resumed = &state.active_session.?;
try std.testing.expectEqual(process orelse baseline, resumed.ultrafast_mode);
const saved = if (resumed.v2) |v2|
(try v2.currentPreferences(arena)).ultrafast_mode
else
resumed.writable.?.state.preferences.ultrafast_mode;
try std.testing.expectEqual(baseline, saved);
}
}
}
}
}
test "ACP host-disabled new load and resume skip project MCP effects" {
const alloc = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
+12 -2
View File
@@ -29,7 +29,7 @@ pub const top_level_specs = [_]TopLevelSpec{
.{
.kind = .ask,
.token = "ask",
.usage = "ask [--auto|--full-access] [--model <id>] [--effort <level>] [--fast|--no-fast] [--provider-order <a,b,...>] [--provider-strict|--no-provider-strict] [--image PATH] [--system TEXT] [--json] [--quiet] [--prompt-permissions] [--no-save] [--sessions-v2] [--no-color] [--resume <last|id>|--resume-id <id>] [--continue-recovery] [--] <prompt>",
.usage = "ask [--auto|--full-access] [--model <id>] [--effort <level>] [--fast|--no-fast] [--ultrafast|--no-ultrafast] [--provider-order <a,b,...>] [--provider-strict|--no-provider-strict] [--image PATH] [--system TEXT] [--json] [--quiet] [--prompt-permissions] [--no-save] [--sessions-v2] [--no-color] [--resume <last|id>|--resume-id <id>] [--continue-recovery] [--] <prompt>",
.summary = "Run one noninteractive request",
.options = &.{
.{ .flag = "--auto", .description = "Automatically review unresolved permission requests" },
@@ -39,6 +39,8 @@ pub const top_level_specs = [_]TopLevelSpec{
.{ .flag = "--effort <level>", .description = "Override the reasoning effort for this request" },
.{ .flag = "--fast", .description = "Enable Fast mode for this request when the model supports it" },
.{ .flag = "--no-fast", .description = "Disable Fast mode for this request" },
.{ .flag = "--ultrafast", .description = "Request Ultra mode for this request when the model supports it" },
.{ .flag = "--no-ultrafast", .description = "Disable Ultra mode for this request" },
.{ .flag = "--provider-order <a,b,...>", .description = "Prefer these gateway providers in order for this request" },
.{ .flag = "--provider-strict", .description = "Restrict this request to only the providers in --provider-order" },
.{ .flag = "--no-provider-strict", .description = "Clear the provider restriction for this request" },
@@ -67,10 +69,12 @@ pub const top_level_specs = [_]TopLevelSpec{
.{
.kind = .acp,
.token = "acp",
.usage = "acp [--model <id>] [--log-file <path>]",
.usage = "acp [--model <id>] [--ultrafast|--no-ultrafast] [--log-file <path>]",
.summary = "Start an ACP server over stdio",
.options = &.{
.{ .flag = "--model <id>", .description = "Override the default model" },
.{ .flag = "--ultrafast", .description = "Request Ultra mode when the model supports it" },
.{ .flag = "--no-ultrafast", .description = "Disable Ultra mode" },
.{ .flag = "--log-file <path>", .description = "Write ACP logs to a file" },
},
},
@@ -364,6 +368,10 @@ pub const top_level_flags = [_]TopLevelFlag{
.usage = "--fast, --no-fast",
.description = "Turn Fast mode on or off for an interactive session",
},
.{
.usage = "--ultrafast, --no-ultrafast",
.description = "Request Ultra mode on or off for an interactive session",
},
.{
.usage = "-c, --continue",
.description = "Resume the remembered workspace session",
@@ -481,6 +489,7 @@ pub const slash_specs = [_]SlashSpec{
.{ .kind = .credits, .command = "/credits", .aliases = &.{"/balance"}, .help_entry = "/credits (/balance)", .completion_description = "show gateway credits balance", .presentation_category = .account, .requires_prompt_credential = true },
.{ .kind = .paste, .command = "/paste", .help_entry = "/paste", .completion_description = "attach an image from the clipboard when supported", .presentation_category = .media },
.{ .kind = .fast, .command = "/fast", .help_entry = "/fast", .completion_description = "toggle Fast mode when supported", .presentation_category = .model },
.{ .kind = .ultrafast, .command = "/ultrafast", .help_entry = "/ultrafast [on|off|status]", .completion_description = "request Ultra mode when supported", .presentation_category = .model, .has_args = true, .accepts_payload = true },
.{ .kind = .statusline, .command = "/statusline", .help_entry = "/statusline [context|session|workspace]", .completion_description = "toggle status line segments", .presentation_category = .appearance, .has_args = true, .accepts_payload = true },
.{ .kind = .notifications, .command = "/sound", .help_entry = "/sound [on|off|max]", .completion_description = "toggle sounds and terminal bells", .presentation_category = .appearance, .has_args = true, .accepts_payload = true },
.{ .kind = .workspace, .command = "/workspace", .help_entry = "/workspace [list|add PATH|remove PATH|clear]", .completion_description = "manage additional workspace directories", .presentation_category = .workspace, .show_in_welcome = true, .has_args = true, .accepts_payload = true },
@@ -567,6 +576,7 @@ test "built-in slash commands register exact active order" {
"/credits",
"/paste",
"/fast",
"/ultrafast",
"/statusline",
"/sound",
"/workspace",
+39
View File
@@ -2868,6 +2868,7 @@ fn parseModelCatalogEntry(alloc: std.mem.Allocator, entry: std.json.Value) !?Mod
.has_reasoning = has_reasoning,
.reasoning_efforts = reasoning_efforts,
.supports_fast_mode = supports_fast_mode,
.supports_ultrafast_mode = supportsUltrafastMode(entry.object),
.has_vision = has_vision,
.has_file_input = has_file_input,
.has_web_search = has_web_search,
@@ -2925,6 +2926,44 @@ fn supportsFastMode(entry: std.json.ObjectMap) bool {
return hasObjectField(objectField(pricing, "service_tiers"), "priority");
}
fn supportsUltrafastMode(entry: std.json.ObjectMap) bool {
const owner = entry.get("owned_by") orelse return false;
if (owner != .string or !std.ascii.eqlIgnoreCase(owner.string, "openai")) return false;
const pricing = entry.get("pricing");
const tier = objectField(objectField(pricing, "service_tiers"), "ultrafast") orelse return false;
if (!positivePrice(objectField(tier, "input")) or !positivePrice(objectField(tier, "output"))) return false;
if (objectField(pricing, "input_cache_read") != null and !positivePrice(objectField(tier, "input_cache_read"))) return false;
return true;
}
fn positivePrice(value: ?std.json.Value) bool {
const actual = value orelse return false;
const price: f64 = switch (actual) {
.string => std.fmt.parseFloat(f64, actual.string) catch return false,
.float => actual.float,
.integer => @floatFromInt(actual.integer),
else => return false,
};
return std.math.isFinite(price) and price > 0;
}
test "Ultrafast catalog support requires OpenAI priced metadata rather than model names or tags" {
const json =
\\{"data":[
\\{"id":"openai/gpt-6-astra","type":"language","owned_by":"openai","pricing":{"input_cache_read":"0.000001","service_tiers":{"ultrafast":{"input":"0.00006","output":"0.0003","input_cache_read":"0.000006"}}}},
\\{"id":"openai/gpt-5.6-sol","type":"language","owned_by":"openai","tags":["ultrafast"],"pricing":{"service_tiers":{"priority":{"input":"0.1","output":"0.2"}}}},
\\{"id":"openai/incomplete","type":"language","owned_by":"openai","pricing":{"service_tiers":{"ultrafast":{"input":"0.1"}}}},
\\{"id":"provider/model","type":"language","owned_by":"provider","pricing":{"service_tiers":{"ultrafast":{"input":"0.1","output":"0.2"}}}}
\\]}
;
var catalog = try parseSortedModelCatalog(std.testing.allocator, json);
defer freeModelCatalog(std.testing.allocator, &catalog);
try std.testing.expectEqual(@as(usize, 4), catalog.items.len);
for (catalog.items) |entry| {
try std.testing.expectEqual(std.mem.eql(u8, entry.id, "openai/gpt-6-astra"), entry.supports_ultrafast_mode);
}
}
fn objectField(value: ?std.json.Value, key: []const u8) ?std.json.Value {
const actual = value orelse return null;
if (actual != .object) return null;
+1
View File
@@ -47,6 +47,7 @@ pub const Config = struct {
cancel_flag: *std.atomic.Value(bool),
review_enabled: bool = false,
fast_mode: bool = false,
ultrafast_mode: bool = false,
effort: ReasoningEffort = .auto,
/// Borrowed gateway provider slugs in preference order; empty leaves
/// routing to the gateway. Backing memory is owned by the caller and must
+23 -1
View File
@@ -390,6 +390,9 @@ fn runSummaryCall(
.account_id = request.account_id,
.tenant_context = request.gateway_team,
} };
// Compaction is a side call, not an opt-in to the working turn's premium tier.
var provider_options = request.provider_options;
provider_options.ultrafast = false;
var usage: types.ToolUsage = .{};
for (0..2) |attempt| {
if (request.cancel_flag.load(.seq_cst)) {
@@ -412,7 +415,7 @@ fn runSummaryCall(
.messages = &messages,
.tools = .{},
.tool_choice = .none,
.provider_options = request.provider_options,
.provider_options = provider_options,
.max_output_tokens = request.max_output_tokens,
.budget = .{ .cancel_flag = request.cancel_flag, .deadline = deadline },
.deadline = deadline,
@@ -673,6 +676,25 @@ test "assistant first compaction keeps normal model limits and options" {
}
}
test "Ultrafast is not inherited by compaction side calls" {
const alloc = std.testing.allocator;
var provider = FakeProvider{ .response = "Continue the original task." };
var cancel = std.atomic.Value(bool).init(false);
const summary = try runSummaryCall(alloc, .{
.stream_provider = provider.provider(),
.model = "openai/gpt-6-astra",
.api_key = "fixture-key",
.retry_count = 1,
.cancel_flag = &cancel,
.accepted_tokens = 1000,
.provider_options = .{ .ultrafast = true, .prompt_caching = true },
.trace_ctx = .{},
}, "Historical task context", 4096);
defer alloc.free(summary.text);
try std.testing.expect(!provider.observed_provider_options.ultrafast);
try std.testing.expect(provider.observed_provider_options.prompt_caching);
}
test "assistant first compaction reselects older users when the actual summary needs more room" {
const alloc = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
+24 -5
View File
@@ -3732,12 +3732,14 @@ fn recoverySelectionChanged(
selected_provider: model_provider.ProviderId,
selected_model: []const u8,
selected_fast_mode: bool,
selected_ultrafast_mode: bool,
) bool {
return !checkpoint.authority.provider.same_authority(selected_provider) or !std.mem.eql(
u8,
checkpoint.authority.model,
selected_model,
) or checkpoint.requested_fast_mode != selected_fast_mode;
) or checkpoint.requested_fast_mode != selected_fast_mode or
checkpoint.requested_ultrafast_mode != selected_ultrafast_mode;
}
fn recoveryCredentialAuthorityMatches(
@@ -3984,6 +3986,8 @@ fn persistRecoveryCheckpoint(
},
.requested_fast_mode = requested_fast_mode,
.fast_mode = fast_mode,
.requested_ultrafast_mode = job.agent_settings.ultrafast_mode,
.ultrafast_mode = job.agent_settings.ultrafast_mode,
.max_provider_attempts = attempt_limit,
.consumed_provider_attempts = consumed_attempts,
.outstanding_reservation = outstanding_reservation,
@@ -5522,7 +5526,7 @@ fn processQueuedPromptInner(
const vision_fallback_available = config.provider_capabilities.vision_fallback and
deps.tool_registry.lookup("vision") != null;
var request_capabilities = deps.available_model_capabilities(deps.ctx, job.model);
if (requiresResolvedRequestCapabilities(
if (config.ultrafast_mode or requiresResolvedRequestCapabilities(
job.images.len > 0 or job.authorized_image_catalog.len > 0,
vision_fallback_available,
config.effort,
@@ -7073,9 +7077,16 @@ fn processQueuedPromptLoop(
restoredConsumedAttempts(checkpoint)
else
0;
const selected_fast_mode = config.fast_mode;
const selected_fast_mode = config.fast_mode and !config.ultrafast_mode;
const selected_ultrafast_mode = job.agent_settings.ultrafast_mode;
const selection_changed = if (job.recovery_checkpoint) |checkpoint|
recoverySelectionChanged(checkpoint, job.provider, job.model, selected_fast_mode)
recoverySelectionChanged(
checkpoint,
job.provider,
job.model,
selected_fast_mode,
selected_ultrafast_mode,
)
else
false;
if (job.recovery_checkpoint) |checkpoint| {
@@ -7104,6 +7115,7 @@ fn processQueuedPromptLoop(
else
selected_fast_mode;
var fast_unavailable_notified = false;
var ultrafast_unconfirmed_notified = false;
var tool_image_strip_notified = false;
var attachment_withheld_notified = false;
// Attachment pixel sizes probed during this turn, so each step does not
@@ -7523,7 +7535,7 @@ fn processQueuedPromptLoop(
try deps.push_text(deps.ctx, .{ .operational = "\n" });
}
last_gateway_message_count = gateway_instructions.items.len + request_messages.len;
var provider_opts = model_capabilities.resolveProviderOptionsForCapabilities(request_capabilities, config.effort, route_fast_mode);
var provider_opts = try model_capabilities.resolveUltrafastProviderOptions(request_capabilities, job.provider, gateway_model, config.effort, route_fast_mode, config.ultrafast_mode);
provider_opts.prompt_caching = config.provider_capabilities.gateway_prompt_caching;
provider_opts.provider_order = config.provider_order;
provider_opts.provider_strict = config.provider_strict;
@@ -8352,6 +8364,13 @@ fn processQueuedPromptLoop(
}
}
if (streamCompletionPtr(&stream_result)) |completion| {
if (config.ultrafast_mode) {
debug_trace.eventf("gateway", "ultrafast_served_tier", step_ctx, "requested=ultrafast served={s}", .{if (completion.service_tier) |tier| @tagName(tier) else "unconfirmed"});
if (completion.service_tier != .ultrafast and !ultrafast_unconfirmed_notified) {
ultrafast_unconfirmed_notified = true;
try deps.push_text(deps.ctx, .{ .operational = "Ultrafast was requested, but Gateway did not confirm it was served; this response may have used a standard or lower tier.\n" });
}
}
agent.observeUsage(completion.usage);
// The completion buffer is step-scoped, so no cross-step
// dedupe: each completion reports its serving provider once.
+3 -1
View File
@@ -426,10 +426,11 @@ fn dupeUsageReference(alloc: Allocator, source: DeferredUsageReference) Allocato
};
}
test "owned stream result copies survive the source allocator and allocation failures" {
test "owned stream result copies preserve service tier metadata" {
const source = Result{ .completed = .{
.completion = .{
.content = "answer",
.service_tier = .ultrafast,
.tool_calls = &.{.{
.id = "call_1",
.name = "read_file",
@@ -474,6 +475,7 @@ test "owned stream result copies survive the source allocator and allocation fai
defer copied.deinit(alloc);
const completion = copied.completed.completion;
try std.testing.expectEqualStrings("answer", completion.content.?);
try std.testing.expectEqual(types.ProviderServiceTier.ultrafast, completion.service_tier.?);
try std.testing.expectEqualStrings("gen_1", completion.generation_id.?);
try std.testing.expectEqualStrings("detail", completion.provider_failure_detail.?);
try std.testing.expectEqualStrings("[]", completion.provider_state_json.?);
+8
View File
@@ -25,6 +25,7 @@ pub const AgentTurnSettings = struct {
max_tool_result_bytes: usize = tool_result_limits.default_max_tool_result_bytes,
first_call_tool_choice: types.ToolChoice = .auto,
fast_mode: bool = false,
ultrafast_mode: bool = false,
effort: types.ReasoningEffort = .auto,
/// Gateway provider routing for turns built from these settings. When set
/// on WorkerRuntime.agent_turn_settings the slice is owned by the worker
@@ -1888,6 +1889,13 @@ pub const WorkerRuntime = struct {
for (self.queued_prompts.items) |*prompt| prompt.agent_settings.fast_mode = enabled;
}
pub fn syncQueuedPromptUltrafastMode(self: *WorkerRuntime, enabled: bool) void {
self.worker_mutex.lockUncancelable(io_mod.getIo());
defer self.worker_mutex.unlock(io_mod.getIo());
self.agent_turn_settings.ultrafast_mode = enabled;
for (self.queued_prompts.items) |*prompt| prompt.agent_settings.ultrafast_mode = enabled;
}
pub fn syncQueuedPromptEffort(self: *WorkerRuntime, effort: types.ReasoningEffort) void {
self.worker_mutex.lockUncancelable(io_mod.getIo());
defer self.worker_mutex.unlock(io_mod.getIo());
+2
View File
@@ -240,6 +240,7 @@ pub fn Runtime(comptime App: type) type {
.gateway_models_path = if (comptime @hasField(App, "web_search_models_path")) app.web_search_models_path else "/v1/models",
.agent_step_limit = app.agent_step_limit,
.fast_mode = agent_settings.fast_mode,
.ultrafast_mode = agent_settings.ultrafast_mode,
.effort = agent_settings.effort,
.provider_order = if (selected_provider == .gateway) agent_settings.provider_order else &.{},
.provider_strict = selected_provider == .gateway and agent_settings.provider_strict,
@@ -1325,6 +1326,7 @@ pub fn Runtime(comptime App: type) type {
.cancel_flag = &app.worker.worker_cancel_requested,
.review_enabled = false,
.fast_mode = job.agent_settings.fast_mode,
.ultrafast_mode = job.agent_settings.ultrafast_mode,
.effort = job.agent_settings.effort,
.provider_order = if (job.provider == .gateway) job.agent_settings.provider_order else &.{},
.provider_strict = job.provider == .gateway and job.agent_settings.provider_strict,
+26 -1
View File
@@ -121,6 +121,8 @@ fn BootstrapDeps(comptime App: type) type {
types.ReasoningEffort,
bool,
bool,
bool,
?bool,
?types.ReasoningEffort,
?bool,
?model_provider.ProviderId,
@@ -154,6 +156,7 @@ pub fn Runtime(comptime App: type) type {
model: ?[]const u8 = null,
effort: ?types.ReasoningEffort = null,
fast: ?bool = null,
ultrafast: ?bool = null,
/// Borrowed from the launch arguments; StartupState dupes on apply.
provider_order: ?[]const []const u8 = null,
provider_strict: ?bool = null,
@@ -217,6 +220,8 @@ pub fn Runtime(comptime App: type) type {
effort: types.ReasoningEffort,
fast_mode: bool,
fast_mode_model_bound: bool,
configured_ultrafast_mode: bool,
ultrafast_process_override: ?bool,
effort_process_override: ?types.ReasoningEffort,
fast_process_override: ?bool,
provider_process_override: ?model_provider.ProviderId,
@@ -230,6 +235,8 @@ pub fn Runtime(comptime App: type) type {
effort,
fast_mode,
fast_mode_model_bound,
configured_ultrafast_mode,
ultrafast_process_override,
effort_process_override,
fast_process_override,
provider_process_override,
@@ -436,7 +443,8 @@ pub fn Runtime(comptime App: type) type {
// configured and stored preferences keep their pre-flag values.
const persisted_effort = startup.effort;
const persisted_fast_mode = startup.fast_mode;
startup.applyLaunchTurnOverrides(launch_overrides.effort, launch_overrides.fast);
const persisted_ultrafast_mode = startup.configured_ultrafast_mode;
startup.applyLaunchTurnOverrides(launch_overrides.effort, launch_overrides.fast, launch_overrides.ultrafast);
if (launch_overrides.provider_order != null or launch_overrides.provider_strict != null) {
try startup.applyLaunchProviderRouting(app.alloc, launch_overrides.provider_order, launch_overrides.provider_strict);
}
@@ -449,6 +457,8 @@ pub fn Runtime(comptime App: type) type {
persisted_effort,
persisted_fast_mode,
startup.fast_mode_model_bound,
persisted_ultrafast_mode,
startup.ultrafast_process_override,
launch_overrides.effort,
launch_overrides.fast,
launch_overrides.provider,
@@ -460,6 +470,7 @@ pub fn Runtime(comptime App: type) type {
if (comptime @hasField(App, "context_limits")) app.context_limits = startup.context_limits;
app.worker.agent_turn_settings.first_call_tool_choice = startup.first_call_tool_choice;
app.worker.agent_turn_settings.fast_mode = startup.fast_mode;
app.worker.agent_turn_settings.ultrafast_mode = startup.ultrafast_mode;
app.worker.agent_turn_settings.effort = startup.effort;
// Worker-owned memory uses the C allocator, matching worker deinit.
try app.worker.setProviderRouting(std.heap.c_allocator, startup.provider_order, startup.provider_strict);
@@ -673,6 +684,8 @@ const TestCapture = struct {
configured_effort: types.ReasoningEffort = .auto,
configured_fast_mode: bool = false,
configured_fast_mode_model_bound: bool = false,
configured_ultrafast_mode: bool = false,
ultrafast_process_override: ?bool = null,
effort_process_override: ?types.ReasoningEffort = null,
fast_process_override: ?bool = null,
provider_process_override: ?model_provider.ProviderId = null,
@@ -904,6 +917,9 @@ fn makeStartupState(alloc: Allocator) !app_lifecycle.StartupState {
state.context_enabled = false;
state.fast_mode = true;
state.fast_mode_model_bound = true;
state.configured_ultrafast_mode = true;
state.ultrafast_mode = true;
state.ultrafast_process_override = false;
state.auto_upgrade = false;
state.update_channel = .dev;
state.effort = types.ReasoningEffort.literal("high");
@@ -972,6 +988,8 @@ fn configureSessionPreferencesForTest(
effort: types.ReasoningEffort,
fast_mode: bool,
fast_mode_model_bound: bool,
configured_ultrafast_mode: bool,
ultrafast_process_override: ?bool,
effort_process_override: ?types.ReasoningEffort,
fast_process_override: ?bool,
provider_process_override: ?model_provider.ProviderId,
@@ -997,6 +1015,8 @@ fn configureSessionPreferencesForTest(
capture.configured_effort = effort;
capture.configured_fast_mode = fast_mode;
capture.configured_fast_mode_model_bound = fast_mode_model_bound;
capture.configured_ultrafast_mode = configured_ultrafast_mode;
capture.ultrafast_process_override = ultrafast_process_override;
capture.effort_process_override = effort_process_override;
capture.fast_process_override = fast_process_override;
capture.provider_process_override = provider_process_override;
@@ -1077,17 +1097,21 @@ test "app_bootstrap_runtime applies interactive launch flag overrides" {
.model = "launch-model",
.effort = types.ReasoningEffort.literal("low"),
.fast = true,
.ultrafast = false,
});
try std.testing.expectEqualStrings("launch-model", capture.runtimeModel());
try std.testing.expectEqualStrings("launch-model", app.selected_model.items);
try std.testing.expect(app.fast_mode);
try std.testing.expect(!app.worker.agent_turn_settings.ultrafast_mode);
try std.testing.expect(app.effort.eql(types.ReasoningEffort.literal("low")));
// Stored preferences keep the configured values; the flags stay per-launch.
try std.testing.expect(capture.configured_effort.eql(types.ReasoningEffort.literal("high")));
try std.testing.expect(!capture.configured_fast_mode);
// --fast binds to the launch model so the footer indicator reflects it.
try std.testing.expect(capture.configured_fast_mode_model_bound);
try std.testing.expect(capture.configured_ultrafast_mode);
try std.testing.expectEqual(@as(?bool, false), capture.ultrafast_process_override);
try std.testing.expectEqualStrings("configured-model", capture.configuredModel());
// The process overrides carry the flag values so a resume re-applies them.
try std.testing.expect(capture.effort_process_override.?.eql(types.ReasoningEffort.literal("low")));
@@ -1210,6 +1234,7 @@ test "app_bootstrap_runtime transfers startup state and starts a fresh session"
try std.testing.expectEqual(@as(usize, 131072), app.worker.agent_turn_settings.max_tool_result_bytes);
try std.testing.expectEqual(types.ToolChoice.none, app.worker.agent_turn_settings.first_call_tool_choice);
try std.testing.expect(app.worker.agent_turn_settings.fast_mode);
try std.testing.expect(!app.worker.agent_turn_settings.ultrafast_mode);
try std.testing.expectEqual(types.ReasoningEffort.literal("high"), app.worker.agent_turn_settings.effort);
try std.testing.expect(!app.context_enabled);
try std.testing.expect(app.fast_mode);
+21
View File
@@ -386,6 +386,7 @@ pub fn Handlers(comptime App: type) type {
.show_credits = commandShowCredits,
.paste_clipboard = commandPasteClipboard,
.toggle_fast = commandToggleFast,
.handle_ultrafast = commandHandleUltrafast,
.handle_statusline = commandHandleStatusline,
.rename_session = commandRenameSession,
.handle_notifications = commandHandleNotifications,
@@ -2018,6 +2019,11 @@ pub fn Handlers(comptime App: type) type {
try session_commands.Commands(App).toggleFast(app);
}
fn commandHandleUltrafast(ctx: *anyopaque, rest: []const u8) !void {
const app: *App = @ptrCast(@alignCast(ctx));
try session_commands.Commands(App).handleUltrafast(app, rest);
}
fn commandHandleStatusline(ctx: *anyopaque, rest: []const u8) !void {
const app: *App = @ptrCast(@alignCast(ctx));
if (std.mem.trim(u8, rest, " \t").len == 0) {
@@ -3956,10 +3962,16 @@ pub fn settingsCatalogSnapshot(app: anytype) settings_catalog.Snapshot {
if (comptime provider_runtime.supported(App)) snapshot.model = provider_runtime.model(app);
if (comptime @hasField(App, "effort")) snapshot.effort = app.effort.displayLabel();
if (comptime @hasField(App, "fast_mode")) snapshot.fast_mode = app.fast_mode;
if (comptime @hasField(App, "worker") and
@hasField(@TypeOf(app.worker), "agent_turn_settings"))
{
snapshot.ultrafast_mode = app.worker.agent_turn_settings.ultrafast_mode;
}
if (comptime @hasDecl(App, "resolvedModelCapabilities") and provider_runtime.supported(App)) {
const capabilities = app.resolvedModelCapabilities(provider_runtime.model(app));
snapshot.reasoning_efforts = capabilities.reasoning_efforts;
snapshot.supports_fast_mode = capabilities.supports_fast_mode;
snapshot.supports_ultrafast_mode = capabilities.supports_ultrafast_mode;
}
if (comptime @hasField(App, "permission_engine")) snapshot.permission_mode = @tagName(app.permission_engine.mode);
if (comptime @hasField(App, "input_runtime")) {
@@ -4100,6 +4112,15 @@ pub fn applySettingsCatalogChange(app: anytype, change: settings_catalog.Change)
const enabled = parseOnOff(change.value) orelse return error.InvalidSettingsCatalogValue;
if (enabled != app.fast_mode) try session_commands.Commands(@TypeOf(app.*)).toggleFast(app);
},
.ultrafast_mode => {
const enabled = parseOnOff(change.value) orelse return error.InvalidSettingsCatalogValue;
if (enabled != app.worker.agent_turn_settings.ultrafast_mode) {
try session_commands.Commands(@TypeOf(app.*)).handleUltrafast(
app,
if (enabled) "on" else "off",
);
}
},
.permission_mode => try session_commands.Commands(@TypeOf(app.*)).handlePermissions(app, change.value),
.sound_level => try handleNotificationsCommand(app, change.value),
.startup_scrollback => {
+49 -15
View File
@@ -102,6 +102,7 @@ const ExplicitModelSelection = struct {
model: []const u8,
effort: types.ReasoningEffort,
fast_mode: bool,
ultrafast_mode: bool = false,
has_fast_mode_token: bool = false,
};
@@ -157,23 +158,30 @@ fn parseExplicitModelSelection(input: []const u8) ExplicitModelSelectionParse {
}
if (token_count != 3) return .invalid;
const fast_mode = if (std.ascii.eqlIgnoreCase(tokens[2], "fast"))
true
else if (std.ascii.eqlIgnoreCase(tokens[2], "normal"))
false
else
const fast_mode = std.ascii.eqlIgnoreCase(tokens[2], "fast");
const ultrafast_mode = std.ascii.eqlIgnoreCase(tokens[2], "ultrafast");
if (!fast_mode and !ultrafast_mode and !std.ascii.eqlIgnoreCase(tokens[2], "normal")) {
return .invalid;
}
return .{ .selection = .{
.model = tokens[0],
.effort = effort,
.fast_mode = fast_mode,
.ultrafast_mode = ultrafast_mode,
.has_fast_mode_token = true,
} };
}
fn validateExplicitModelSelection(selection: ExplicitModelSelection, capabilities: model_capabilities.Capabilities) ExplicitModelSelectionParse {
if (!model_capabilities.reasoningEffortSupported(capabilities, selection.effort)) return .invalid;
if (capabilities.supports_fast_mode != selection.has_fast_mode_token) return .invalid;
if (!selection.has_fast_mode_token) return .{ .selection = selection };
if (selection.ultrafast_mode and !capabilities.supports_ultrafast_mode) return .invalid;
if (selection.fast_mode and !capabilities.supports_fast_mode) return .invalid;
if (!selection.fast_mode and !selection.ultrafast_mode and
!capabilities.supports_fast_mode and !capabilities.supports_ultrafast_mode)
{
return .invalid;
}
return .{ .selection = selection };
}
@@ -2506,6 +2514,7 @@ pub fn Runtime(comptime App: type) type {
selected,
app.effort,
app.fast_mode,
false,
);
app.model_cache.closeMenu();
app.shell.render_requests.request(.footer);
@@ -2857,7 +2866,7 @@ pub fn Runtime(comptime App: type) type {
try app.writeDomainNotice(.{
.topic = "",
.tone = .@"error",
.body = "usage: /model <id> <effort> [normal|fast]",
.body = "usage: /model <id> <effort> [normal|fast|ultrafast]",
}, true);
},
.selection => |selection| {
@@ -2866,6 +2875,7 @@ pub fn Runtime(comptime App: type) type {
selection.model,
selection.effort,
selection.fast_mode,
selection.ultrafast_mode,
);
app.input_runtime.inputResetState().clearCurrent(app.alloc);
},
@@ -3596,6 +3606,7 @@ const RoutingWorker = struct {
queued_steer_text: ?[]const u8 = null,
synced_permission_mode: ?types.PermissionMode = null,
permission_mode_sync_count: usize = 0,
agent_turn_settings: worker_runtime.AgentTurnSettings = .{},
pub fn compactionActivitySnapshot(self: *RoutingWorker) @import("../output/compaction_activity.zig").Snapshot {
return self.compaction.snapshot;
@@ -3706,6 +3717,10 @@ const RoutingWorker = struct {
pub fn syncQueuedPromptFastMode(_: *RoutingWorker, _: bool) void {}
pub fn syncQueuedPromptUltrafastMode(self: *RoutingWorker, enabled: bool) void {
self.agent_turn_settings.ultrafast_mode = enabled;
}
pub fn syncQueuedPromptEffort(_: *RoutingWorker, _: types.ReasoningEffort) void {}
pub fn clearQueuedPrompts(
@@ -6551,6 +6566,7 @@ test "explicit model selection parser accepts complete picker syntax" {
);
try std.testing.expectEqual(types.ReasoningEffort.auto, selection.effort);
try std.testing.expect(!selection.fast_mode);
try std.testing.expect(!selection.ultrafast_mode);
try std.testing.expect(selection.has_fast_mode_token);
}
@@ -6603,6 +6619,21 @@ test "explicit model selection semantic validation uses resolved capabilities" {
.supports_fast_mode = true,
}),
) != .invalid);
const ultrafast = switch (parseExplicitModelSelection(
"/model provider/astra high ultrafast",
)) {
.selection => |selection| selection,
else => return error.TestExpectedEqual,
};
try std.testing.expect(ultrafast.ultrafast_mode);
try std.testing.expect(validateExplicitModelSelection(
ultrafast,
model_capabilities.resolveCapabilities(ultrafast.model, .{
.reasoning_efforts = .fromSlice(&high_efforts),
.supports_ultrafast_mode = true,
}),
) != .invalid);
}
test "model picker spaces do not commit before enter" {
@@ -6618,6 +6649,7 @@ test "model picker spaces do not commit before enter" {
"anthropic/claude-sonnet-4.6",
0,
false,
false,
.effort,
);
@@ -6630,7 +6662,7 @@ test "model picker spaces do not commit before enter" {
);
}
test "model picker enter commits selected fast option from fast stage" {
test "model picker enter commits normal speed from the speed stage" {
const alloc = std.testing.allocator;
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
@@ -6646,6 +6678,7 @@ test "model picker enter commits selected fast option from fast stage" {
"anthropic/claude-opus-4.7",
0,
false,
false,
.fast,
);
@@ -6666,7 +6699,7 @@ test "model picker enter commits selected fast option from fast stage" {
try std.testing.expectEqual(false, app.last_preference_fast_mode.?);
try std.testing.expectEqualStrings("", app.input_runtime.edit_state.input.items);
try std.testing.expectEqualStrings(
"Switched to anthropic/claude-opus-4.7",
"Switched to anthropic/claude-opus-4.7 (effort: default, speed: normal)",
app.notice_body.items,
);
try std.testing.expect(std.mem.find(u8, app.transcript.items, "Invalid /model selection") == null);
@@ -6686,6 +6719,7 @@ test "active stream Enter commits a complete model choice for the next turn" {
model,
0,
false,
false,
.effort,
);
app.stream.active = true;
@@ -6702,7 +6736,7 @@ test "active stream Enter commits a complete model choice for the next turn" {
try std.testing.expectEqualStrings("", app.input_runtime.edit_state.input.items);
try std.testing.expectEqual(@as(usize, 0), app.submitted_prompt_count);
try std.testing.expectEqualStrings(
"Next turn will use " ++ model,
"Next turn will use " ++ model ++ " (effort: default, speed: normal)",
app.notice_body.items,
);
}
@@ -6724,7 +6758,7 @@ test "active stream Enter selects the visible model choice" {
try std.testing.expectEqualStrings(model, app.selected_model.items);
try std.testing.expectEqualStrings("", app.input_runtime.edit_state.input.items);
try std.testing.expectEqualStrings(
"Next turn will use " ++ model,
"Next turn will use " ++ model ++ " (effort: default, speed: normal)",
app.notice_body.items,
);
try std.testing.expectEqual(@as(usize, 0), app.submitted_prompt_count);
@@ -12749,7 +12783,7 @@ test "composer shortcut line delete handles decoded and raw mutations" {
try primeComposerHistoryForTest(RoutingFakeApp, &app, "draft");
try app.input_runtime.textReplacementState().replace(alloc, "alpha\nleftMIDright\ngamma");
app.input_runtime.edit_state.cursor = "alpha\nleftMID".len;
try app.input_runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 3, true, .fast);
try app.input_runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 3, true, false, .fast);
app.input_runtime.picker.file_completion_index = 4;
_ = try Runtime(RoutingFakeApp).routeResolvedEscapeAction(
@@ -12818,7 +12852,7 @@ test "composer shortcut line delete preserves no-op picker redraw and metadata s
.path = try alloc.dupe(u8, "/tmp/8.png"),
.media_type = try alloc.dupe(u8, "image/png"),
});
try app.input_runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 3, true, .effort);
try app.input_runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 3, true, false, .effort);
app.input_runtime.edit_state.cursor = 0;
const line_start_action = test_ui_input.shortcutFromControlByte(21);
@@ -12844,7 +12878,7 @@ test "composer shortcut line delete preserves no-op picker redraw and metadata s
try std.testing.expectEqual(picker_state.ModelPickerStage.effort, app.input_runtime.picker.model_picker_stage);
app.shell.render_requests.clearReason(.footer);
try app.input_runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 4, true, .fast);
try app.input_runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 4, true, false, .fast);
app.input_runtime.edit_state.cursor = app.input_runtime.edit_state.input.items.len;
_ = try Runtime(RoutingFakeApp).routeResolvedEscapeAction(
&app,
@@ -15298,7 +15332,7 @@ test "app_input_runtime image path paste rejects the complete edit without chang
defer app.deinit();
try app.input_runtime.edit_state.input.appendSlice(alloc, "draft");
app.input_runtime.edit_state.cursor = 2;
try app.input_runtime.picker.beginModelPickerFlow(alloc, "test/model", 2, true, .fast);
try app.input_runtime.picker.beginModelPickerFlow(alloc, "test/model", 2, true, false, .fast);
app.input_runtime.picker.file_completion_index = 4;
try appendOwnedPendingImage(&app, 3, "/tmp/original.png");
app.next_image_id_counter = 7;
+87 -6
View File
@@ -139,6 +139,10 @@ pub const StartupState = struct {
context_limits: config_runtime.context_limits.Values = .{},
context_enabled: bool = true,
fast_mode: bool = false,
ultrafast_mode: bool = false,
configured_ultrafast_mode: bool = false,
ultrafast_process_override: ?bool = null,
ultrafast_mode_source: config_runtime.ConfigSource = .compiled_default,
fast_mode_model_bound: bool = false,
fast_mode_source: config_runtime.ConfigSource = .compiled_default,
slash_menu_categories: bool = true,
@@ -248,10 +252,9 @@ pub const StartupState = struct {
}
}
/// Applies the per-launch `--effort`/`--fast` overrides after session
/// preferences are configured, so the flags shape runtime state without
/// rewriting what the workspace or session stored.
pub fn applyLaunchTurnOverrides(self: *StartupState, effort: ?types.ReasoningEffort, fast: ?bool) void {
/// Applies per-launch turn overrides after session preferences are
/// configured, so they shape runtime state without rewriting persistence.
pub fn applyLaunchTurnOverrides(self: *StartupState, effort: ?types.ReasoningEffort, fast: ?bool, ultrafast: ?bool) void {
if (effort) |value| self.effort = value;
if (fast) |value| {
self.fast_mode = value;
@@ -259,6 +262,11 @@ pub const StartupState = struct {
// footer indicator reflects it; --no-fast clears the binding.
self.fast_mode_model_bound = value;
}
if (ultrafast) |value| {
self.ultrafast_process_override = value;
self.ultrafast_mode = value;
}
if (self.ultrafast_mode) self.fast_mode = false;
}
/// Applies per-launch `--provider-order`/`--provider-strict` flags. Like
@@ -326,6 +334,7 @@ pub const StartupStatus = struct {
selected_model: []const u8,
owned_selected_model: ?[]u8 = null,
model_origin: ModelOrigin = .default,
ultrafast_mode: bool = false,
auth: auth_runtime.StatusSnapshot = .{},
permission_mode: PermissionMode,
agent_step_limit: usize,
@@ -538,6 +547,7 @@ pub fn loadStartupStatusWithAuthMode(
.selected_model = selected_model.value,
.owned_selected_model = selected_model.owned,
.model_origin = ModelOrigin.of(settings, configured_selection.provider, run_model),
.ultrafast_mode = detailed.ultrafast_mode_env_override orelse (settings.ultrafast_mode orelse false),
.auth = auth_status,
.permission_mode = loadPermissionMode(settings.permission_mode),
.agent_step_limit = loadAgentStepLimit(default_agent_step_limit, settings.max_agent_steps),
@@ -597,7 +607,14 @@ fn loadStartupStateFromOwnedWorkspace(
try config_runtime.loadMergedSettingsDetailed(alloc, state.workspace_root);
defer detailed.deinit(alloc);
const settings = &detailed.settings;
// A rejected profile cannot safely identify the destination of model data.
// A malformed process override must never silently fall through to a
// potentially paid profile preference, regardless of credential mode.
for (detailed.diagnostics) |diagnostic| {
if (diagnostic.cause == .invalid_ultrafast_mode_override) {
return error.InvalidUltrafastOverride;
}
}
// A rejected local profile cannot safely identify the destination of model data.
if (auth_mode == .local) for (detailed.diagnostics) |diagnostic| {
if (diagnostic.layer != .user) continue;
switch (diagnostic.cause) {
@@ -605,7 +622,10 @@ fn loadStartupStateFromOwnedWorkspace(
if (credential_mode != .stored) return error.InvalidProfileConfiguration;
state.model_requests_blocked = true;
},
.malformed_settings, .settings_too_large, .invalid_model_id => return error.InvalidProfileConfiguration,
.malformed_settings,
.settings_too_large,
.invalid_model_id,
=> return error.InvalidProfileConfiguration,
else => {},
}
};
@@ -677,6 +697,10 @@ fn loadStartupStateFromOwnedWorkspace(
state.fast_mode = fast_mode.enabled;
state.fast_mode_model_bound = fast_mode.model_bound;
state.fast_mode_source = detailed.sources.fast_mode;
state.configured_ultrafast_mode = settings.ultrafast_mode orelse false;
state.ultrafast_process_override = detailed.ultrafast_mode_env_override;
state.ultrafast_mode = state.ultrafast_process_override orelse state.configured_ultrafast_mode;
state.ultrafast_mode_source = detailed.sources.ultrafast_mode;
state.slash_menu_categories = settings.slash_menu_categories orelse true;
state.collapse_tool_calls = settings.collapse_tool_calls orelse false;
state.auto_upgrade = settings.auto_upgrade orelse true;
@@ -2370,6 +2394,63 @@ test "loadStartupState applies core env overrides" {
try std.testing.expectEqual(@as(usize, 37), state.agent_step_limit);
}
test "Ultrafast launch disable overrides an enabled environment request" {
var state = StartupState{
.agent_step_limit = 25,
.ultrafast_mode = true,
.ultrafast_process_override = true,
};
state.applyLaunchTurnOverrides(null, null, false);
try std.testing.expect(!state.ultrafast_mode);
try std.testing.expectEqual(@as(?bool, false), state.ultrafast_process_override);
}
test "ultrafast startup separates profile preferences from process overrides" {
const alloc = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
try tmp.dir.createDirPath(io_mod.getIo(), "home/.fx");
try tmp.dir.createDirPath(io_mod.getIo(), "profile-off");
try tmp.dir.createDirPath(io_mod.getIo(), "profile-on");
const home_root = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "home");
defer alloc.free(home_root);
const profile_off_root = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "profile-off");
defer alloc.free(profile_off_root);
const profile_on_root = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "profile-on");
defer alloc.free(profile_on_root);
const settings = try std.fmt.allocPrint(
alloc,
"{{\"workspaces\":{{\"{s}\":{{\"ultrafast_mode\":false}},\"{s}\":{{\"ultrafast_mode\":true}}}}}}\n",
.{ profile_off_root, profile_on_root },
);
defer alloc.free(settings);
try writeFixtureFile(tmp.dir, "home/.fx/settings.json", settings);
const env = try TestEnv.install(alloc, &.{
.{ .key = "HOME", .value = home_root },
.{ .key = "FX_ULTRAFAST", .value = "1" },
});
defer env.deinit();
var profile_off_env_on = try loadStartupStateForWorkspace(alloc, profile_off_root, "default/model", 25);
defer profile_off_env_on.deinit(alloc);
try std.testing.expect(!profile_off_env_on.configured_ultrafast_mode);
try std.testing.expect(profile_off_env_on.ultrafast_mode);
try std.testing.expectEqual(@as(?bool, true), profile_off_env_on.ultrafast_process_override);
try env.map.put("FX_ULTRAFAST", "0");
var profile_on_env_off = try loadStartupStateForWorkspace(alloc, profile_on_root, "default/model", 25);
defer profile_on_env_off.deinit(alloc);
try std.testing.expect(profile_on_env_off.configured_ultrafast_mode);
try std.testing.expect(!profile_on_env_off.ultrafast_mode);
try std.testing.expectEqual(@as(?bool, false), profile_on_env_off.ultrafast_process_override);
try env.map.put("FX_ULTRAFAST", "paid");
try std.testing.expectError(
error.InvalidUltrafastOverride,
loadStartupStateForWorkspace(alloc, profile_on_root, "default/model", 25),
);
}
test "host-managed startup skips every local credential source" {
var env = try TestEnv.install(std.testing.allocator, &.{
.{ .key = "AI_GATEWAY_API_KEY", .value = "must-not-load" },
+28 -12
View File
@@ -539,7 +539,7 @@ pub fn Runtime(comptime App: type) type {
var model_completions_buf: [32][]const u8 = undefined;
var effort_picker_values_buf: [types.ReasoningEffort.max_options + 1]types.ReasoningEffort = undefined;
var effort_picker_labels_buf: [types.ReasoningEffort.max_options + 1][]const u8 = undefined;
var fast_picker_labels_buf: [2][]const u8 = undefined;
var fast_picker_labels_buf: [3][]const u8 = undefined;
var provider_picker_column: provider_picker_runtime.ColumnBuffer = .{};
noinline fn footerContext(
app: *App,
@@ -579,10 +579,16 @@ pub fn Runtime(comptime App: type) type {
picker_window_start = app.input_runtime.picker.model_picker_effort_window_start;
},
.fast => {
for (picker_state.model_picker_fast_options, 0..) |option, i| fast_picker_labels_buf[i] = option;
const count = picker_state.filterCompletionLabels(picker_query.query, fast_picker_labels_buf[0..], fast_picker_labels_buf[0..]);
const count = input_completion_runtime.CompletionRuntime(App).speedPickerLabels(
app,
picker_query.query,
&fast_picker_labels_buf,
);
picker_items = fast_picker_labels_buf[0..count];
picker_index = app.input_runtime.picker.model_picker_fast_index;
picker_index = input_completion_runtime.CompletionRuntime(App).speedPickerIndex(
app,
picker_items,
);
picker_window_start = app.input_runtime.picker.model_picker_fast_window_start;
},
}
@@ -702,6 +708,14 @@ pub fn Runtime(comptime App: type) type {
}
}
var statusline = buildStatuslineItems(app, visible_model);
statusline.ultrafast_indicator_active = if (pending_model != null)
visible_capabilities.supports_ultrafast_mode and std.mem.eql(u8, pendingPickerSpeed(model_query, app.input_runtime.picker.model_picker_fast_index), "ultrafast")
else if (comptime @hasField(@TypeOf(app.worker), "agent_turn_settings"))
app.worker.agent_turn_settings.ultrafast_mode
else
false;
return .{
.slash_registry = app.slashRegistry(),
.stream = visible_stream,
@@ -837,10 +851,7 @@ pub fn Runtime(comptime App: type) type {
.esc_clear_armed = app.input_runtime.gestures.escapeClearArmed(),
.esc_interrupt_armed = app.input_runtime.gestures.escapeInterruptArmed(),
.question = app.question_prompt.projection(),
.statusline = buildStatuslineItems(
app,
visible_model,
),
.statusline = statusline,
.activity = activityProjection(app),
.input = &app.input_runtime,
};
@@ -886,15 +897,20 @@ pub fn Runtime(comptime App: type) type {
return items;
}
fn pendingPickerFastMode(query: ?picker_state.ModelPickerQuery, fast_index: usize) bool {
fn pendingPickerSpeed(query: ?picker_state.ModelPickerQuery, speed_index: usize) []const u8 {
if (query) |picker_query| {
if (picker_query.stage == .fast) {
const typed = std.mem.trim(u8, picker_query.query, " \t");
if (std.ascii.eqlIgnoreCase(typed, picker_state.model_picker_fast_options[0])) return false;
if (std.ascii.eqlIgnoreCase(typed, picker_state.model_picker_fast_options[1])) return true;
for (picker_state.model_picker_speed_options) |speed| {
if (std.ascii.eqlIgnoreCase(typed, speed)) return speed;
}
}
}
return fast_index % picker_state.model_picker_fast_options.len == 1;
return picker_state.model_picker_speed_options[speed_index % picker_state.model_picker_speed_options.len];
}
fn pendingPickerFastMode(query: ?picker_state.ModelPickerQuery, speed_index: usize) bool {
return std.mem.eql(u8, pendingPickerSpeed(query, speed_index), "fast");
}
pub fn flushRequestedFrame(app: *App) !void {
+173 -2
View File
@@ -431,12 +431,14 @@ pub const SessionPreferencePatch = struct {
model: ?[]const u8 = null,
effort: ?types.ReasoningEffort = null,
fast_mode: ?bool = null,
ultrafast_mode: ?bool = null,
pub fn userSettingsPatch(self: SessionPreferencePatch) config_runtime.UserSettingsPatch {
var patch = config_runtime.UserSettingsPatch{
.provider = self.provider,
.effort = self.effort,
.fast_mode = self.fast_mode,
.ultrafast_mode = self.ultrafast_mode,
};
if (self.model) |model| patch.model_preference = .{
.provider = self.provider orelse .gateway,
@@ -1294,6 +1296,7 @@ pub const Persistence = struct {
process_model_override: ?[]u8 = null,
process_effort_override: ?types.ReasoningEffort = null,
process_fast_override: ?bool = null,
process_ultrafast_override: ?bool = null,
session_picker: SessionPicker = .{},
session_picker_load: SessionPickerLoad = .{},
session_picker_cache: SessionPickerCatalogCache = .{},
@@ -1315,7 +1318,7 @@ pub const Persistence = struct {
/// in a static release-binary template.
pub fn initInto(storage: *Persistence) void {
comptime {
if (std.meta.fields(Persistence).len != 29) {
if (std.meta.fields(Persistence).len != 30) {
@compileError("update Persistence.initInto for the changed field set");
}
}
@@ -1335,6 +1338,7 @@ pub const Persistence = struct {
storage.process_model_override = null;
storage.process_effort_override = null;
storage.process_fast_override = null;
storage.process_ultrafast_override = null;
storage.session_picker = .{};
storage.session_picker_load = .{};
storage.session_picker_cache = .{};
@@ -1472,6 +1476,8 @@ pub fn Runtime(comptime App: type) type {
effort: types.ReasoningEffort,
fast_mode: bool,
fast_mode_model_bound: bool,
configured_ultrafast_mode: bool,
ultrafast_process_override: ?bool,
effort_process_override: ?types.ReasoningEffort,
fast_process_override: ?bool,
provider_process_override: ?model_provider.ProviderId,
@@ -1484,6 +1490,7 @@ pub fn Runtime(comptime App: type) type {
.model = @constCast(configured_model),
.effort = effort,
.fast_mode = fast_mode,
.ultrafast_mode = configured_ultrafast_mode,
},
);
try replacePreferences(
@@ -1502,6 +1509,7 @@ pub fn Runtime(comptime App: type) type {
}
app.session_persistence.process_effort_override = effort_process_override;
app.session_persistence.process_fast_override = fast_process_override;
app.session_persistence.process_ultrafast_override = ultrafast_process_override;
app.session_persistence.process_provider_override = provider_process_override;
// A provider override must pin the resolved model too, or a resume
// would restore the session's model from a different provider.
@@ -2339,10 +2347,11 @@ pub fn Runtime(comptime App: type) type {
fn hydrateResumedSession(
app: *App,
state: session_codec.DurableSessionState,
loaded_state: session_codec.DurableSessionState,
display: *const session_display_metadata.DisplayMetadata,
notice: ResumeNotice,
) !void {
var state = loaded_state;
const previous_provider = provider_runtime.provider(app);
if (comptime @hasField(App, "next_image_id")) {
app.next_image_id = try nextImageIdForResume(
@@ -2373,6 +2382,7 @@ pub fn Runtime(comptime App: type) type {
state.preferences,
);
try restoreRuntimePreferences(app, state.preferences);
normalizeRecoveryUltrafastForProcessOverride(app, &state);
app.total_input_tokens = state.total_input_tokens;
app.total_output_tokens = state.total_output_tokens;
@@ -3494,6 +3504,18 @@ pub fn Runtime(comptime App: type) type {
else
return result;
if (result.session_error != null) return result;
const durable_ultrafast_mode = durableUltrafastEventDelta(
loaded.state.preferences.ultrafast_mode,
patch.ultrafast_mode,
);
if (patch.model == null and
patch.provider == null and
patch.effort == null and
patch.fast_mode == null and
durable_ultrafast_mode == null)
{
return result;
}
_ = loaded.appendEvent(
app.alloc,
.{ .preferences_changed = .{
@@ -3504,6 +3526,7 @@ pub fn Runtime(comptime App: type) type {
.provider = patch.provider,
.effort = patch.effort,
.fast_mode = patch.fast_mode,
.ultrafast_mode = durable_ultrafast_mode,
} },
io_mod.milliTimestamp(),
) catch |err| {
@@ -6040,6 +6063,41 @@ pub fn Runtime(comptime App: type) type {
};
}
fn normalizeRecoveryUltrafastForProcessOverride(
app: *App,
resumed: *session_codec.DurableSessionState,
) void {
const process_override = app.session_persistence.process_ultrafast_override;
const local_changed = if (resumed.recovery_checkpoint) |*checkpoint|
suppressRecoveryUltrafast(checkpoint, process_override)
else
false;
app.session_persistence.write_mutex.lockUncancelable(io_mod.getIo());
defer app.session_persistence.write_mutex.unlock(io_mod.getIo());
const durable_changed = if (app.session_persistence.writable) |*loaded|
if (loaded.state.recovery_checkpoint) |*checkpoint|
suppressRecoveryUltrafast(checkpoint, process_override)
else
false
else
false;
const js_changed = if (app.session_persistence.js_host_session) |*owner|
if (owner.state.recovery_checkpoint) |*checkpoint|
suppressRecoveryUltrafast(checkpoint, process_override)
else
false
else
false;
if (local_changed or durable_changed or js_changed) {
debug_trace.logf(
"session",
"event=recovery_ultrafast_suppressed reason=explicit_process_disable",
.{},
);
}
}
fn restoreRuntimePreferences(
app: *App,
preferences: session_codec.DurableSessionPreferences,
@@ -6075,6 +6133,10 @@ pub fn Runtime(comptime App: type) type {
// stored preferences for this launch, without rewriting them.
const effective_effort = app.session_persistence.process_effort_override orelse preferences.effort;
const effective_fast_mode = app.session_persistence.process_fast_override orelse preferences.fast_mode;
const effective_ultrafast_mode = resolveUltrafastMode(
preferences.ultrafast_mode,
app.session_persistence.process_ultrafast_override,
);
app.effort = effective_effort;
app.fast_mode = effective_fast_mode;
app.session_persistence.fast_mode_model_bound = if (app.session_persistence.process_fast_override != null)
@@ -6083,6 +6145,7 @@ pub fn Runtime(comptime App: type) type {
fast_mode_model_bound;
app.worker.syncQueuedPromptEffort(effective_effort);
app.worker.syncQueuedPromptFastMode(effective_fast_mode);
app.worker.syncQueuedPromptUltrafastMode(effective_ultrafast_mode);
}
pub fn fastModeModelBound(app: *const App) bool {
@@ -6187,6 +6250,53 @@ fn replacePreferences(
target.* = replacement;
}
/// The old durable event schema exact-matches preference keys. Omit only an
/// idempotent default-off patch; a stored true must receive an explicit false.
fn durableUltrafastEventDelta(stored: bool, requested: ?bool) ?bool {
const value = requested orelse return null;
return if (!value and !stored) null else value;
}
test "durable ultrafast event delta omits only default-off patches" {
try std.testing.expect(durableUltrafastEventDelta(false, false) == null);
try std.testing.expectEqual(@as(?bool, false), durableUltrafastEventDelta(true, false));
try std.testing.expectEqual(@as(?bool, true), durableUltrafastEventDelta(false, true));
}
fn resolveUltrafastMode(preference: bool, process_override: ?bool) bool {
return process_override orelse preference;
}
fn suppressRecoveryUltrafast(
checkpoint: *session_codec.RecoveryCheckpoint,
process_override: ?bool,
) bool {
if (process_override != false) return false;
const changed = checkpoint.requested_ultrafast_mode or checkpoint.ultrafast_mode;
checkpoint.requested_ultrafast_mode = false;
checkpoint.ultrafast_mode = false;
return changed;
}
test "ultrafast process overrides preserve the saved preference baseline" {
try std.testing.expect(!resolveUltrafastMode(false, null));
try std.testing.expect(resolveUltrafastMode(true, null));
try std.testing.expect(!resolveUltrafastMode(true, false));
try std.testing.expect(resolveUltrafastMode(false, true));
}
test "explicit ultrafast disable clears both resumed checkpoint flags" {
var checkpoint = try parseTestRecoveryCheckpoint(std.testing.allocator, "network_interrupted");
defer checkpoint.deinit(std.testing.allocator);
checkpoint.requested_ultrafast_mode = true;
checkpoint.ultrafast_mode = true;
try std.testing.expect(suppressRecoveryUltrafast(&checkpoint, false));
try std.testing.expect(!checkpoint.requested_ultrafast_mode);
try std.testing.expect(!checkpoint.ultrafast_mode);
try std.testing.expect(!suppressRecoveryUltrafast(&checkpoint, false));
}
fn restoredFastModeModelBound(
current_bound: bool,
configured: ?session_codec.DurableSessionPreferences,
@@ -6241,6 +6351,7 @@ fn applyPreferencePatch(
}
if (patch.effort) |effort| current.effort = effort;
if (patch.fast_mode) |fast_mode| current.fast_mode = fast_mode;
if (patch.ultrafast_mode) |ultrafast_mode| current.ultrafast_mode = ultrafast_mode;
target.* = current;
}
@@ -6401,6 +6512,7 @@ const FakeWorker = struct {
model: std.ArrayList(u8) = .empty,
effort: types.ReasoningEffort = .auto,
fast_mode: bool = false,
ultrafast_mode: bool = false,
active_prompt_is_root_authority: bool = false,
cancel_requested: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
@@ -6437,6 +6549,10 @@ const FakeWorker = struct {
fn syncQueuedPromptFastMode(self: *FakeWorker, fast_mode: bool) void {
self.fast_mode = fast_mode;
}
fn syncQueuedPromptUltrafastMode(self: *FakeWorker, ultrafast_mode: bool) void {
self.ultrafast_mode = ultrafast_mode;
}
};
const PromptCard = struct {
@@ -6991,6 +7107,8 @@ test "js-host resume restores transcript context preferences usage and revision"
.auto,
false,
true,
false,
null,
null,
null,
null,
@@ -7082,6 +7200,8 @@ test "js-host resume store failures and missing records fall back to fresh sessi
.auto,
false,
true,
false,
null,
null,
null,
null,
@@ -7115,6 +7235,8 @@ test "js-host picker request stays unsupported and starts fresh" {
.auto,
false,
true,
false,
null,
null,
null,
null,
@@ -7144,6 +7266,8 @@ test "js-host completed and interrupted turns propagate revisions preserve owner
.auto,
false,
true,
false,
null,
null,
null,
null,
@@ -7215,6 +7339,8 @@ test "js-host preference changes snapshot the updated session preferences" {
.auto,
false,
true,
false,
null,
null,
null,
null,
@@ -7314,6 +7440,8 @@ fn configureTestPreferences(app: *TestApp) !void {
types.ReasoningEffort.literal("high"),
true,
true,
false,
null,
null,
null,
null,
@@ -7383,6 +7511,45 @@ test "initializePersistence silently skips optional missing home and errors when
try std.testing.expectError(error.HomeNotSet, Runtime(TestApp).initializePersistence(&app, true));
}
test "v2 ultrafast resume applies process overrides without persisting them" {
const alloc = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const paths = try testPaths(alloc, &tmp);
defer {
alloc.free(paths.home);
alloc.free(paths.workspace);
}
const home = try TestHome.install(alloc, paths.home);
defer home.deinit();
for ([_]bool{ false, true }) |baseline| {
var app = try TestApp.init(alloc, paths.workspace);
defer app.deinit();
try configureTestPreferences(&app);
app.session_persistence.workspace_preferences.?.ultrafast_mode = baseline;
app.session_persistence.process_ultrafast_override = !baseline;
app.session_persistence.sessions_v2 = true;
try Runtime(TestApp).initializePersistence(&app, true);
try Runtime(TestApp).beginFreshPersistedSession(&app);
const v2 = app.session_persistence.v2.?;
try v2.commitTurn(.{ .assistant = .{
.user = .{ .text = @constCast("saved request") },
.assistant = @constCast("saved answer"),
} }, types.ConversationLanguage.default());
const id = try alloc.dupe(u8, v2.id());
defer alloc.free(id);
const store = &app.session_persistence.v2_store.?;
Runtime(TestApp).closeWritableSession(&app);
const resumed = try session_adapter.Session.resumeSession(alloc, store, .{ .id = id }, paths.workspace, .app);
try Runtime(TestApp).installResumedV2Session(&app, resumed, .session);
try std.testing.expectEqual(!baseline, app.worker.ultrafast_mode);
try std.testing.expectEqual(baseline, app.session_persistence.session_preferences.?.ultrafast_mode);
var durable = try app.session_persistence.v2.?.currentPreferences(alloc);
defer durable.deinit(alloc);
try std.testing.expectEqual(baseline, durable.ultrafast_mode);
}
}
test "beginFreshPersistedSession and enableSessionStores create per-session stores" {
const alloc = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
@@ -9100,6 +9267,8 @@ test "upgrade resume restores active session with the installed version notice"
types.ReasoningEffort.literal("high"),
true,
false,
false,
null,
null,
null,
null,
@@ -10951,6 +11120,8 @@ test "fresh interactive session retains one writable schema-v3 handle" {
types.ReasoningEffort.literal("high"),
true,
true,
false,
null,
null,
null,
null,
+91 -30
View File
@@ -999,8 +999,19 @@ pub fn CompletionRuntime(comptime App: type) type {
navigatePickerOptions(&app.input_runtime.picker.model_picker_effort_index, &app.input_runtime.picker.model_picker_effort_window_start, effortCompletionValues(app, query.query, &values), delta);
},
.fast => {
var labels: [2][]const u8 = undefined;
navigatePickerOptions(&app.input_runtime.picker.model_picker_fast_index, &app.input_runtime.picker.model_picker_fast_window_start, fastCompletionLabels(query.query, &labels), delta);
var labels: [3][]const u8 = undefined;
const count = speedPickerLabels(app, query.query, &labels);
if (count == 0) return;
var display_index = speedPickerIndex(app, labels[0..count]);
navigatePickerOptions(
&display_index,
&app.input_runtime.picker.model_picker_fast_window_start,
count,
delta,
);
app.input_runtime.picker.model_picker_fast_index = speedOptionIndex(
labels[display_index % count],
);
},
}
}
@@ -1091,6 +1102,7 @@ pub fn CompletionRuntime(comptime App: type) type {
model,
model_capabilities.reasoningEffortIndex(capabilities, effort),
capabilities.supports_fast_mode,
false,
.effort,
);
},
@@ -1100,10 +1112,9 @@ pub fn CompletionRuntime(comptime App: type) type {
const capabilities = model_capabilities.resolveForApp(App, app, model);
const effort = model_capabilities.reasoningEffortAtIndex(capabilities, app.input_runtime.picker.model_picker_effort_index);
const effort_index = model_capabilities.reasoningEffortIndex(capabilities, effort);
const fast_mode = selectedFastCompletion(app) orelse return;
const fast_label = if (fast_mode) picker_state.model_picker_fast_options[1] else picker_state.model_picker_fast_options[0];
try setModelComposerText(app, "/model {s} {s} {s}", .{ model, effort.label(), fast_label });
try app.input_runtime.picker.beginModelPickerFlow(app.alloc, model, effort_index, fast_mode, .fast);
const speed = selectedSpeedCompletion(app) orelse return;
try setModelComposerText(app, "/model {s} {s} {s}", .{ model, effort.label(), speed.label });
try app.input_runtime.picker.beginModelPickerFlow(app.alloc, model, effort_index, speed.fast_mode, speed.ultrafast_mode, .fast);
},
}
app.shell.render_requests.request(.footer);
@@ -1125,10 +1136,10 @@ pub fn CompletionRuntime(comptime App: type) type {
defer app.alloc.free(model);
const effort = exactEffortCompletion(app, query.query) orelse return false;
const capabilities = model_capabilities.resolveForApp(App, app, model);
if (!capabilities.supports_fast_mode) return false;
if (!capabilities.supports_fast_mode and !capabilities.supports_ultrafast_mode) return false;
try setModelComposerText(app, "/model {s} {s} ", .{ model, effort.label() });
try app.input_runtime.picker.beginModelPickerFlow(app.alloc, model, model_capabilities.reasoningEffortIndex(capabilities, effort), true, .fast);
try app.input_runtime.picker.beginModelPickerFlow(app.alloc, model, model_capabilities.reasoningEffortIndex(capabilities, effort), capabilities.supports_fast_mode, false, .fast);
return true;
},
.fast => return false,
@@ -1171,9 +1182,9 @@ pub fn CompletionRuntime(comptime App: type) type {
const capabilities = model_capabilities.resolveForApp(App, app, model);
if (capabilities.supports_fast_mode) {
try setModelComposerText(app, "/model {s} {s} ", .{ model, effort.label() });
try app.input_runtime.picker.beginModelPickerFlow(app.alloc, model, model_capabilities.reasoningEffortIndex(capabilities, effort), true, .fast);
try app.input_runtime.picker.beginModelPickerFlow(app.alloc, model, model_capabilities.reasoningEffortIndex(capabilities, effort), true, false, .fast);
} else {
try session_commands.Commands(App).selectModelFromPicker(app, model, effort, app.fast_mode);
try session_commands.Commands(App).selectModelFromPicker(app, model, effort, app.fast_mode, false);
app.input_runtime.inputResetState().clearCurrent(app.alloc);
}
app.shell.render_requests.request(.footer);
@@ -1183,12 +1194,12 @@ pub fn CompletionRuntime(comptime App: type) type {
if (!app.input_runtime.picker.hasPendingModelPickerSelection()) return false;
const model = try app.alloc.dupe(u8, app.input_runtime.picker.model_picker_pending_model.items);
defer app.alloc.free(model);
const fast_mode = selectedFastCompletion(app) orelse {
const speed = selectedSpeedCompletion(app) orelse {
app.shell.render_requests.request(.footer);
return true;
};
const effort = model_capabilities.reasoningEffortAtIndex(model_capabilities.resolveForApp(App, app, model), app.input_runtime.picker.model_picker_effort_index);
try session_commands.Commands(App).selectModelFromPicker(app, model, effort, fast_mode);
try session_commands.Commands(App).selectModelFromPicker(app, model, effort, speed.fast_mode, speed.ultrafast_mode);
app.input_runtime.inputResetState().clearCurrent(app.alloc);
app.shell.render_requests.request(.footer);
return true;
@@ -1217,9 +1228,11 @@ pub fn CompletionRuntime(comptime App: type) type {
const capabilities = model_capabilities.resolveForApp(App, app, model);
const supports_effort = capabilities.reasoning_efforts.len > 0;
const supports_fast = capabilities.supports_fast_mode;
const supports_ultrafast = capabilities.supports_ultrafast_mode;
const supports_speed = supports_fast or supports_ultrafast;
if (!supports_effort and !supports_fast) {
try session_commands.Commands(App).selectModelFromPicker(app, model, app.effort, app.fast_mode);
if (!supports_effort and !supports_speed) {
try session_commands.Commands(App).selectModelFromPicker(app, model, app.effort, app.fast_mode, false);
app.input_runtime.inputResetState().clearCurrent(app.alloc);
app.shell.render_requests.request(.footer);
return;
@@ -1231,12 +1244,13 @@ pub fn CompletionRuntime(comptime App: type) type {
} else {
try setModelComposerText(app, "/model {s} auto ", .{model});
}
// Preselect the product effort default and enable Fast mode when supported.
// Preselect the product effort default and Fast mode when supported.
try app.input_runtime.picker.beginModelPickerFlow(
app.alloc,
model,
model_capabilities.reasoningEffortIndex(capabilities, .auto),
supports_fast,
false,
stage,
);
app.shell.render_requests.request(.footer);
@@ -1264,7 +1278,7 @@ pub fn CompletionRuntime(comptime App: type) type {
if (capabilities.reasoning_efforts.len > 0) {
const effort_index = model_capabilities.reasoningEffortIndex(capabilities, model_capabilities.reasoningEffortAtIndex(capabilities, app.input_runtime.picker.model_picker_effort_index));
try setModelComposerText(app, "/model {s} ", .{model});
try app.input_runtime.picker.beginModelPickerFlow(app.alloc, model, effort_index, app.fast_mode, .effort);
try app.input_runtime.picker.beginModelPickerFlow(app.alloc, model, effort_index, app.fast_mode, app.worker.agent_turn_settings.ultrafast_mode, .effort);
} else {
try restoreModelPickerModelStage(app, model);
}
@@ -1354,29 +1368,76 @@ pub fn CompletionRuntime(comptime App: type) type {
return values[app.input_runtime.picker.model_picker_effort_index % count];
}
fn fastCompletionLabels(query: []const u8, out: *[2][]const u8) usize {
return picker_state.filterCompletionLabels(query, picker_state.model_picker_fast_options[0..], out[0..]);
const SpeedSelection = struct {
label: []const u8,
fast_mode: bool,
ultrafast_mode: bool,
};
pub fn speedPickerLabels(app: *App, query: []const u8, out: *[3][]const u8) usize {
const capabilities = model_capabilities.resolveForApp(
App,
app,
app.input_runtime.picker.model_picker_pending_model.items,
);
var options: [3][]const u8 = .{ picker_state.model_picker_speed_options[0], undefined, undefined };
var option_count: usize = 1;
if (capabilities.supports_fast_mode) {
options[option_count] = picker_state.model_picker_speed_options[1];
option_count += 1;
}
if (capabilities.supports_ultrafast_mode) {
options[option_count] = picker_state.model_picker_speed_options[2];
option_count += 1;
}
return picker_state.filterCompletionLabels(query, options[0..option_count], out[0..]);
}
fn exactFastCompletion(raw_query: []const u8) ?bool {
const query = std.mem.trim(u8, raw_query, " \t");
if (std.ascii.eqlIgnoreCase(query, picker_state.model_picker_fast_options[0])) return false;
if (std.ascii.eqlIgnoreCase(query, picker_state.model_picker_fast_options[1])) return true;
pub fn speedPickerIndex(app: *const App, labels: []const []const u8) usize {
const selected = picker_state.model_picker_speed_options[
app.input_runtime.picker.model_picker_fast_index % picker_state.model_picker_speed_options.len
];
for (labels, 0..) |label, index| {
if (std.ascii.eqlIgnoreCase(label, selected)) return index;
}
return 0;
}
fn speedOptionIndex(label: []const u8) usize {
for (picker_state.model_picker_speed_options, 0..) |option, index| {
if (std.ascii.eqlIgnoreCase(label, option)) return index;
}
return 0;
}
fn speedSelection(label: []const u8) ?SpeedSelection {
if (std.ascii.eqlIgnoreCase(label, picker_state.model_picker_speed_options[0])) {
return .{ .label = picker_state.model_picker_speed_options[0], .fast_mode = false, .ultrafast_mode = false };
}
if (std.ascii.eqlIgnoreCase(label, picker_state.model_picker_speed_options[1])) {
return .{ .label = picker_state.model_picker_speed_options[1], .fast_mode = true, .ultrafast_mode = false };
}
if (std.ascii.eqlIgnoreCase(label, picker_state.model_picker_speed_options[2])) {
return .{ .label = picker_state.model_picker_speed_options[2], .fast_mode = false, .ultrafast_mode = true };
}
return null;
}
fn selectedFastCompletion(app: *App) ?bool {
fn selectedSpeedCompletion(app: *App) ?SpeedSelection {
const query = app.input_runtime.picker.activeModelPickerQuery(&app.input_runtime.edit_state) orelse return null;
if (query.stage != .fast) return null;
const normalized_query = std.mem.trim(u8, query.query, " \t");
if (exactFastCompletion(normalized_query)) |fast_mode| return fast_mode;
var labels: [2][]const u8 = undefined;
const count = fastCompletionLabels(normalized_query, &labels);
var labels: [3][]const u8 = undefined;
const count = speedPickerLabels(app, normalized_query, &labels);
if (count == 0) return null;
const label = labels[app.input_runtime.picker.model_picker_fast_index % count];
return std.mem.eql(u8, label, picker_state.model_picker_fast_options[1]);
if (speedSelection(normalized_query)) |selection| {
for (labels[0..count]) |label| {
if (std.ascii.eqlIgnoreCase(label, selection.label)) return selection;
}
return null;
}
return speedSelection(labels[speedPickerIndex(app, labels[0..count])]);
}
};
}
@@ -1584,7 +1645,7 @@ test "model picker effort completion labels survive capability resolution" {
const rt = CompletionRuntime(ModelPickerCompletionTestApp);
var app = ModelPickerCompletionTestApp{ .alloc = alloc };
defer app.deinit();
try app.input_runtime.picker.beginModelPickerFlow(alloc, "test/model", 0, false, .effort);
try app.input_runtime.picker.beginModelPickerFlow(alloc, "test/model", 0, false, false, .effort);
var values: [types.ReasoningEffort.max_options + 1]types.ReasoningEffort = undefined;
const count = rt.effortCompletionValues(&app, "", &values);
+2
View File
@@ -1457,6 +1457,7 @@ test "model menu owns resolved catalog state and filters without changing catalo
.has_file_input = true,
.has_web_search = true,
.supports_fast_mode = true,
.supports_ultrafast_mode = true,
},
.{
.id = @constCast("private/blue-hornbill"),
@@ -1479,6 +1480,7 @@ test "model menu owns resolved catalog state and filters without changing catalo
try std.testing.expect(runtime.menu.itemAt(0).?.capabilities.supports_vision);
try std.testing.expectEqual(@as(?u32, 256_000), runtime.menu.itemAt(0).?.capabilities.context_window);
try std.testing.expect(runtime.menu.itemAt(1).?.capabilities.supports_fast_mode);
try std.testing.expect(runtime.menu.itemAt(1).?.capabilities.supports_ultrafast_mode);
try std.testing.expect(runtime.menu.itemAt(1).?.capabilities.supports_file_input);
try std.testing.expect(runtime.menu.itemAt(1).?.capabilities.supports_web_search);
+3
View File
@@ -40,6 +40,9 @@ pub const Config = struct {
/// Fast-lane override matching --fast/--no-fast. Null leaves the startup
/// and session defaults untouched.
fast_override: ?bool = null,
/// Ultrafast-lane override. Null leaves the startup and session defaults
/// untouched.
ultrafast_override: ?bool = null,
credential_override: ?[]const u8 = null,
home_override: ?[]const u8 = null,
workspace_root_override: ?[]const u8 = null,
+35
View File
@@ -352,6 +352,7 @@ const AskOptions = struct {
model_override: ?[]u8 = null,
effort_override: ?types.ReasoningEffort = null,
fast_override: ?bool = null,
ultrafast_override: ?bool = null,
provider_order_override: ?[][]const u8 = null,
provider_strict_override: ?bool = null,
image_paths: std.ArrayList([]u8) = .empty,
@@ -497,6 +498,7 @@ const RunOptions = struct {
model_override: ?[]const u8 = null,
effort_override: ?types.ReasoningEffort = null,
fast_override: ?bool = null,
ultrafast_override: ?bool = null,
provider_order_override: ?[]const []const u8 = null,
provider_strict_override: ?bool = null,
deps: RunDeps,
@@ -582,6 +584,10 @@ const AskContext = struct {
max_tool_result_bytes: usize = 64 * 1024,
context_limits: config_runtime.context_limits.Values = .{},
fast_mode: bool = false,
/// Profile preference used only when creating a new durable session.
/// Process and CLI overrides remain per-run and never rewrite this value.
persisted_ultrafast_mode: bool = false,
ultrafast_mode: bool = false,
effort: types.ReasoningEffort = .auto,
/// Borrowed gateway provider routing for this run; startup state owns the
/// backing memory.
@@ -964,6 +970,7 @@ const AskContext = struct {
.model = @constCast(self.seed_model),
.effort = self.effort,
.fast_mode = self.fast_mode,
.ultrafast_mode = self.persisted_ultrafast_mode,
};
var writable = if (self.requested_resume) |target|
try subagent_resume_admission.resumeForExternalPrompt(
@@ -1022,6 +1029,7 @@ const AskContext = struct {
self.model = preferences.model;
self.effort = preferences.effort;
self.fast_mode = preferences.fast_mode;
self.ultrafast_mode = preferences.ultrafast_mode;
}
self.subagent_host = subagent_tool_host.Runtime.create(
self.alloc,
@@ -1087,6 +1095,7 @@ const AskContext = struct {
.model = @constCast(self.seed_model),
.effort = self.effort,
.fast_mode = self.fast_mode,
.ultrafast_mode = self.persisted_ultrafast_mode,
};
const v2 = if (self.requested_resume) |target|
try session_adapter.Session.resumeSession(self.alloc, store, switch (target) {
@@ -1126,6 +1135,7 @@ const AskContext = struct {
self.model = preferences.model;
self.effort = preferences.effort;
self.fast_mode = preferences.fast_mode;
self.ultrafast_mode = preferences.ultrafast_mode;
}
}
self.session.configureWebFetchArtifacts(self.alloc, v2.filesPath());
@@ -1195,6 +1205,7 @@ const AskContext = struct {
.gateway_models_path = self.cfg.gateway_models_path,
.agent_step_limit = self.agent_step_limit,
.fast_mode = self.fast_mode,
.ultrafast_mode = self.ultrafast_mode,
.effort = self.effort,
.provider_order = if (self.provider == .gateway) self.provider_order else &.{},
.provider_strict = self.provider == .gateway and self.provider_strict,
@@ -1504,6 +1515,7 @@ fn runWithDeps(alloc: Allocator, args: []const [:0]const u8, cfg: Config, deps:
.model_override = options.model_override,
.effort_override = options.effort_override,
.fast_override = options.fast_override,
.ultrafast_override = options.ultrafast_override,
.provider_order_override = options.provider_order_override,
.provider_strict_override = options.provider_strict_override,
.deps = deps,
@@ -1753,6 +1765,8 @@ fn runPromptInternal(alloc: Allocator, prompt: []const u8, permission_override:
ctx.context_limits = startup.context_limits;
ctx.context_limits.applyCommandLine(cfg.context_limit_overrides);
ctx.fast_mode = startup.fast_mode;
ctx.persisted_ultrafast_mode = startup.configured_ultrafast_mode;
ctx.ultrafast_mode = startup.ultrafast_mode;
ctx.provider_order = startup.provider_order;
ctx.provider_strict = startup.provider_strict;
ctx.effort = toCoreReasoningEffort(startup.effort);
@@ -1788,6 +1802,9 @@ fn runPromptInternal(alloc: Allocator, prompt: []const u8, permission_override:
owned_resumed_model = try alloc.dupe(u8, ctx.model);
ctx.model = owned_resumed_model.?;
}
// Environment precedence survives resume without rewriting the saved
// profile preference. CLI flags below remain the final per-run layer.
if (startup.ultrafast_process_override) |ultrafast| ctx.ultrafast_mode = ultrafast;
ctx.session.setConversationLanguageFromUserMessage(owned_prompt);
}
@@ -1801,6 +1818,7 @@ fn runPromptInternal(alloc: Allocator, prompt: []const u8, permission_override:
}
if (options.fast_override) |fast| {
ctx.fast_mode = fast;
if (fast) ctx.ultrafast_mode = false;
} else if (options.model_override != null and ctx.requested_resume == null and
startup.fast_mode_source == .compiled_default)
{
@@ -1808,6 +1826,15 @@ fn runPromptInternal(alloc: Allocator, prompt: []const u8, permission_override:
// explicit model override drops it unless --fast restores it.
ctx.fast_mode = false;
}
if (options.ultrafast_override) |ultrafast| {
ctx.ultrafast_mode = ultrafast;
if (ultrafast) ctx.fast_mode = false;
} else if (options.model_override != null and ctx.requested_resume == null and
startup.ultrafast_mode_source == .compiled_default)
{
// Ultra mode must never follow an explicit model override by default.
ctx.ultrafast_mode = false;
}
if (options.provider_order_override) |order| {
ctx.provider_order = order;
}
@@ -2073,6 +2100,7 @@ fn runPromptInternal(alloc: Allocator, prompt: []const u8, permission_override:
.max_tool_result_bytes = startup.max_tool_result_bytes,
.cancel_flag = ctx.cancelFlag(),
.fast_mode = ctx.fast_mode,
.ultrafast_mode = ctx.ultrafast_mode,
.effort = ctx.effort,
.provider_order = if (ctx.provider == .gateway) ctx.provider_order else &.{},
.provider_strict = ctx.provider == .gateway and ctx.provider_strict,
@@ -4141,6 +4169,13 @@ fn parseOptionsWithStdin(alloc: Allocator, args: []const [:0]const u8, stdin: St
if (opts.fast_override != null and opts.fast_override.? != enabled)
return error.InvalidAskArgs;
opts.fast_override = enabled;
if (enabled) opts.ultrafast_override = false;
} else if (std.mem.eql(u8, arg, "--ultrafast") or std.mem.eql(u8, arg, "--no-ultrafast")) {
const enabled = std.mem.eql(u8, arg, "--ultrafast");
if (opts.ultrafast_override != null and opts.ultrafast_override.? != enabled)
return error.InvalidAskArgs;
opts.ultrafast_override = enabled;
if (enabled) opts.fast_override = false;
} else if (std.mem.eql(u8, arg, "--provider-order")) {
i += 1;
if (i >= args.len) return error.InvalidAskArgs;
+75 -6
View File
@@ -134,6 +134,7 @@ pub const LaunchModifiers = struct {
model_override: ?[]u8 = null,
effort_override: ?types.ReasoningEffort = null,
fast_override: ?bool = null,
ultrafast_override: ?bool = null,
provider_order_override: ?[][]const u8 = null,
provider_strict_override: ?bool = null,
/// `--sessions-v2`: keep this process's sessions in the v2 store.
@@ -155,7 +156,14 @@ pub const LaunchModifiers = struct {
pub fn hasModelOverrides(self: LaunchModifiers) bool {
return self.provider_override != null or self.model_override != null or
self.effort_override != null or self.fast_override != null or
self.provider_order_override != null or self.provider_strict_override != null;
self.ultrafast_override != null or self.provider_order_override != null or self.provider_strict_override != null;
}
pub fn hasOnlyUltrafastOverride(self: LaunchModifiers) bool {
return self.ultrafast_override != null and self.provider_override == null and
self.model_override == null and self.effort_override == null and
self.fast_override != true and self.provider_order_override == null and
self.provider_strict_override == null;
}
};
@@ -327,6 +335,7 @@ const SessionRecoveryOptions = struct {
const AcpOptions = struct {
model: ?[]const u8 = null,
ultrafast_override: ?bool = null,
log_file: ?[]const u8 = null,
};
@@ -403,6 +412,7 @@ fn parseGlobalLaunchArgs(
errdefer if (model_override) |model| alloc.free(model);
var effort_override: ?types.ReasoningEffort = null;
var fast_override: ?bool = null;
var ultrafast_override: ?bool = null;
var provider_order_override: ?[][]const u8 = null;
errdefer if (provider_order_override) |order| freeProviderOrderOverride(alloc, order);
var provider_strict_override: ?bool = null;
@@ -465,6 +475,13 @@ fn parseGlobalLaunchArgs(
if (fast_override != null and fast_override.? != enabled)
return error.ConflictingFastFlags;
fast_override = enabled;
if (enabled) ultrafast_override = false;
} else if (std.mem.eql(u8, arg, "--ultrafast") or std.mem.eql(u8, arg, "--no-ultrafast")) {
const enabled = std.mem.eql(u8, arg, "--ultrafast");
if (ultrafast_override != null and ultrafast_override.? != enabled)
return error.ConflictingUltrafastFlags;
ultrafast_override = enabled;
if (enabled) fast_override = false;
} else if (std.mem.eql(u8, arg, "--provider-order")) {
index += 1;
if (index >= args.len) return error.MissingProviderOrderValue;
@@ -495,6 +512,7 @@ fn parseGlobalLaunchArgs(
.model_override = model_override,
.effort_override = effort_override,
.fast_override = fast_override,
.ultrafast_override = ultrafast_override,
.provider_order_override = provider_order_override,
.provider_strict_override = provider_strict_override,
.sessions_v2 = sessions_v2,
@@ -532,6 +550,8 @@ pub fn argsAfterGlobalLaunchArgs(args: []const [:0]const u8) []const [:0]const u
!std.mem.eql(u8, arg, "--no-additional-dirs") and
!std.mem.eql(u8, arg, "--fast") and
!std.mem.eql(u8, arg, "--no-fast") and
!std.mem.eql(u8, arg, "--ultrafast") and
!std.mem.eql(u8, arg, "--no-ultrafast") and
!std.mem.eql(u8, arg, "--provider-strict") and
!std.mem.eql(u8, arg, "--no-provider-strict") and
!std.mem.eql(u8, arg, sessions_v2_arg))
@@ -1032,7 +1052,7 @@ fn runIfRequestedWithDeps(alloc: Allocator, args: []const [:0]const u8, cfg: Con
} else {
try writer.writer.print("fx: invalid global launch option: {s}\n", .{@errorName(err)});
}
try writer.writer.writeAll("usage: fx [--context-limit NAME=BYTES|off] [--add-dir PATH]... [--no-additional-dirs] [--provider <name>] [--model <id>] [--effort <level>] [--fast|--no-fast] [--provider-order <a,b,...>] [--provider-strict|--no-provider-strict] <command>\n");
try writer.writer.writeAll("usage: fx [--context-limit NAME=BYTES|off] [--add-dir PATH]... [--no-additional-dirs] [--provider <name>] [--model <id>] [--effort <level>] [--fast|--no-fast] [--ultrafast|--no-ultrafast] [--provider-order <a,b,...>] [--provider-strict|--no-provider-strict] <command>\n");
try writeStderr(deps, writer.written());
return .handled_failure;
};
@@ -1067,7 +1087,11 @@ fn runNonInteractiveWithDeps(
return .handled_failure;
}
if (global_args.modifiers.hasModelOverrides()) {
const acp_ultrafast_override = switch (parsed_command) {
.acp => global_args.modifiers.hasOnlyUltrafastOverride(),
else => false,
};
if (global_args.modifiers.hasModelOverrides() and !acp_ultrafast_override) {
try writeModelModifierUsage(deps);
return .handled_failure;
}
@@ -1102,7 +1126,7 @@ fn runNonInteractiveWithDeps(
},
.acp => |rest| {
const acp_opts = parseAcpArgs(rest) catch {
try writeStderr(deps, "usage: fx acp [--model <id>] [--log-file <path>]\n");
try writeStderr(deps, "usage: fx acp [--model <id>] [--ultrafast|--no-ultrafast] [--log-file <path>]\n");
return .handled_failure;
};
try cfg.acp_runner.run(alloc, .{
@@ -1131,6 +1155,7 @@ fn runNonInteractiveWithDeps(
.additional_directories = global_args.modifiers.additional_directories,
.saved_directories_suppressed = global_args.modifiers.saved_directories_suppressed,
.model_override = acp_opts.model,
.ultrafast_override = acp_opts.ultrafast_override orelse global_args.modifiers.ultrafast_override,
.log_file = acp_opts.log_file,
});
return .handled_success;
@@ -2248,6 +2273,7 @@ fn statusSnapshotFromStartupWithBuild(
.history_turns = 0,
.session_permission_grants = 0,
.agent_step_limit = startup.agent_step_limit,
.ultrafast_requested = startup.ultrafast_mode,
.update_channel = startup.update_channel.label(),
.build_channel = build.channel.label(),
.build_revision = build.revision,
@@ -3535,7 +3561,7 @@ fn writeWorkspaceModifierUsage(deps: RunDeps) !void {
fn writeModelModifierUsage(deps: RunDeps) !void {
try writeStderr(
deps,
"fx: --provider, --model, --effort, --fast, --provider-order, and --provider-strict apply to interactive sessions; for one-shot runs pass model flags after `fx ask`\n",
"fx: --provider, --model, --effort, --fast, --ultrafast, --provider-order, and --provider-strict apply to interactive sessions; for one-shot runs pass model flags after `fx ask`\n",
);
}
@@ -3547,6 +3573,7 @@ fn globalLaunchErrorMessage(err: anyerror) ?[]const u8 {
error.MissingEffortValue => "--effort requires a value",
error.InvalidEffortValue => "--effort value is not a valid reasoning effort",
error.ConflictingFastFlags => "--fast and --no-fast cannot be used together",
error.ConflictingUltrafastFlags => "--ultrafast and --no-ultrafast cannot be used together",
error.MissingProviderValue => "--provider requires a provider name",
error.InvalidProviderValue => "--provider accepts gateway, codex, grok, or a configured provider name",
error.MissingProviderOrderValue => "--provider-order requires a comma-separated provider list",
@@ -3564,6 +3591,10 @@ fn parseAcpArgs(args: []const [:0]const u8) !AcpOptions {
if (opts.model != null or i + 1 >= args.len) return error.InvalidAcpArgs;
i += 1;
opts.model = args[i];
} else if (std.mem.eql(u8, args[i], "--ultrafast") or std.mem.eql(u8, args[i], "--no-ultrafast")) {
const enabled = std.mem.eql(u8, args[i], "--ultrafast");
if (opts.ultrafast_override != null and opts.ultrafast_override.? != enabled) return error.InvalidAcpArgs;
opts.ultrafast_override = enabled;
} else if (std.mem.eql(u8, args[i], "--log-file")) {
if (opts.log_file != null or i + 1 >= args.len) return error.InvalidAcpArgs;
i += 1;
@@ -3575,6 +3606,23 @@ fn parseAcpArgs(args: []const [:0]const u8) !AcpOptions {
return opts;
}
test "ACP arguments accept an explicit ultrafast override" {
const enabled = try parseAcpArgs(&.{
@constCast("--model"),
@constCast("provider/astra"),
@constCast("--ultrafast"),
});
try std.testing.expectEqualStrings("provider/astra", enabled.model.?);
try std.testing.expectEqual(@as(?bool, true), enabled.ultrafast_override);
const disabled = try parseAcpArgs(&.{@constCast("--no-ultrafast")});
try std.testing.expectEqual(@as(?bool, false), disabled.ultrafast_override);
try std.testing.expectError(
error.InvalidAcpArgs,
parseAcpArgs(&.{ @constCast("--ultrafast"), @constCast("--no-ultrafast") }),
);
}
fn parseLocalSurfaceArgs(args: []const [:0]const u8) !LocalSurfaceOptions {
var options = LocalSurfaceOptions{};
for (args) |arg| {
@@ -5209,6 +5257,26 @@ test "runIfRequested rejects removed record flag as unknown input" {
try std.testing.expect(std.mem.find(u8, capture.stderr.written(), "fx: unknown subcommand: --record") != null);
}
test "global ultrafast launch modifier is ACP-only" {
var acp = try parseGlobalLaunchArgs(
std.testing.allocator,
&.{ @constCast("--ultrafast"), @constCast("acp") },
);
defer acp.deinit(std.testing.allocator);
try std.testing.expectEqual(@as(?bool, true), acp.modifiers.ultrafast_override);
try std.testing.expect(acp.modifiers.hasOnlyUltrafastOverride());
try std.testing.expectEqualStrings("acp", acp.remaining[0]);
try std.testing.expectError(error.ConflictingFastFlags, parseGlobalLaunchArgs(
std.testing.allocator,
&.{ @constCast("--ultrafast"), @constCast("--fast"), @constCast("acp") },
));
try std.testing.expectError(error.ConflictingUltrafastFlags, parseGlobalLaunchArgs(
std.testing.allocator,
&.{ @constCast("--fast"), @constCast("--ultrafast"), @constCast("acp") },
));
}
test "runNoConfigIfRequested handles help without config" {
var capture = CaptureOutput.init(std.testing.allocator);
defer capture.deinit();
@@ -5763,6 +5831,7 @@ test "writeRenderedJsonLine falls back to heap and appends exactly one newline"
.selected_model = "test-model",
.permission_mode = .ask,
.agent_step_limit = 42,
.ultrafast_mode = true,
};
try writeRenderedJsonLine(
@@ -5773,7 +5842,7 @@ test "writeRenderedJsonLine falls back to heap and appends exactly one newline"
);
try std.testing.expectEqualStrings(
"{\"kind\":\"status\",\"model\":\"test-model\",\"model_origin\":\"default\",\"update_channel\":\"stable\",\"build_channel\":\"stable\",\"build_revision\":\"\",\"auth\":\"missing\",\"auth_refreshable\":false,\"auth_help\":\"fx needs access to Vercel AI Gateway. Run fx login to sign in, fx setup to use an API key, or set AI_GATEWAY_API_KEY.\",\"permission_mode\":\"ask\",\"workspace\":\"/tmp/fx\",\"history_turns\":0,\"session_permission_grants\":0,\"agent_step_limit\":42}\n",
"{\"kind\":\"status\",\"model\":\"test-model\",\"model_origin\":\"default\",\"update_channel\":\"stable\",\"build_channel\":\"stable\",\"build_revision\":\"\",\"auth\":\"missing\",\"auth_refreshable\":false,\"auth_help\":\"fx needs access to Vercel AI Gateway. Run fx login to sign in, fx setup to use an API key, or set AI_GATEWAY_API_KEY.\",\"permission_mode\":\"ask\",\"workspace\":\"/tmp/fx\",\"history_turns\":0,\"session_permission_grants\":0,\"agent_step_limit\":42,\"ultrafast_requested\":true}\n",
capture.stdout.written(),
);
}
+1
View File
@@ -191,6 +191,7 @@ fn configLayerRejected(
.invalid_model_id,
.retired_skill_match_fuzzy,
.invalid_context_limits,
.invalid_ultrafast_mode_override,
.invalid_skill_symlink_authorities,
=> return true,
.invalid_additional_directories,
+77
View File
@@ -50,6 +50,7 @@ pub const Settings = struct {
first_call_tool_choice: ?types.ToolChoice = null,
context: ?bool = null,
fast_mode: ?bool = null,
ultrafast_mode: ?bool = null,
fast_mode_model_bound: ?bool = null,
/// Owned gateway provider slugs in preference order; null leaves routing
/// to the gateway. Freed in deinit.
@@ -138,6 +139,7 @@ pub const ConfigSources = struct {
permission_mode: ConfigSource = .compiled_default,
effort: ConfigSource = .compiled_default,
fast_mode: ConfigSource = .compiled_default,
ultrafast_mode: ConfigSource = .compiled_default,
fast_mode_model_bound: ConfigSource = .compiled_default,
slash_menu_categories: ConfigSource = .compiled_default,
collapse_tool_calls: ConfigSource = .compiled_default,
@@ -209,6 +211,7 @@ pub const ConfigDiagnosticCause = enum {
invalid_context_limits,
invalid_additional_directories,
invalid_skill_symlink_authorities,
invalid_ultrafast_mode_override,
};
pub const ConfigDiagnostic = struct {
@@ -249,6 +252,9 @@ pub fn writeDiagnosticMetadata(writer: *std.Io.Writer, diagnostic: ConfigDiagnos
.{max_skill_symlink_authorities},
);
}
if (diagnostic.cause == .invalid_ultrafast_mode_override) {
try writer.writeAll("; FX_ULTRAFAST must be a boolean value");
}
}
/// Upper bound on profile `skill_symlink_authorities` entries.
@@ -259,6 +265,8 @@ pub const DetailedSettings = struct {
diagnostics: []ConfigDiagnostic = &.{},
model_source: ?ModelSource = null,
sources: ConfigSources = .{},
/// Process-only override; null leaves the profile preference in effect.
ultrafast_mode_env_override: ?bool = null,
permission_sources: PermissionSourceViews = .{},
prompt_history_store_allowed: bool = true,
additional_directories: ?[][]u8 = null,
@@ -662,12 +670,21 @@ fn loadMergedSettingsDetailedWithOptionalHome(
}
}
}
const ultrafast_mode_env_override = ultrafastModeEnvOverride() catch |err| blk: {
try diagnostics.append(alloc, .{
.layer = .user,
.cause = .invalid_ultrafast_mode_override,
});
debug_trace.logf("config", "invalid FX_ULTRAFAST value err={s}", .{@errorName(err)});
break :blk null;
};
return .{
.settings = settings,
.diagnostics = try diagnostics.toOwnedSlice(alloc),
.model_source = sources.models.get(model_provider.NameKey.fromProvider(settings.provider orelse .gateway)),
.sources = sources,
.ultrafast_mode_env_override = ultrafast_mode_env_override,
.permission_sources = permission_sources,
.prompt_history_store_allowed = prompt_history_store_allowed,
.additional_directories = additional_directories,
@@ -717,6 +734,16 @@ fn parseProviderOrderListInner(alloc: Allocator, raw: []const u8) !?[][]const u8
return try slugs.toOwnedSlice(alloc);
}
pub const UltrafastModeEnvError = error{InvalidUltrafastMode};
/// Reads the process-only paid lane override using the repository's standard
/// environment-boolean vocabulary. A malformed value cannot widen cost.
pub fn ultrafastModeEnvOverride() UltrafastModeEnvError!?bool {
const raw = io_mod.getenv("FX_ULTRAFAST") orelse return null;
const trimmed = std.mem.trim(u8, raw, " \t\r\n");
return parseEnvBool(trimmed) orelse error.InvalidUltrafastMode;
}
fn parseEnvBool(raw: []const u8) ?bool {
if (std.ascii.eqlIgnoreCase(raw, "1") or std.ascii.eqlIgnoreCase(raw, "true") or
std.ascii.eqlIgnoreCase(raw, "on") or std.ascii.eqlIgnoreCase(raw, "yes")) return true;
@@ -777,6 +804,7 @@ fn hasLegacyWorkspacePreferences(root: std.json.Value) bool {
"model",
"effort",
"fast_mode",
"ultrafast_mode",
"fast_mode_model_bound",
"slash_menu_categories",
"collapse_tool_calls",
@@ -810,6 +838,7 @@ fn isProfileOnlySettingKey(key: []const u8) bool {
"review_model",
"effort",
"fast_mode",
"ultrafast_mode",
"fast_mode_model_bound",
"slash_menu_categories",
"collapse_tool_calls",
@@ -902,6 +931,7 @@ fn updateConfigSources(sources: *ConfigSources, settings: Settings, source: Conf
if (settings.permission_mode != null) sources.permission_mode = source;
if (settings.effort != null) sources.effort = source;
if (settings.fast_mode != null) sources.fast_mode = source;
if (settings.ultrafast_mode != null) sources.ultrafast_mode = source;
if (settings.fast_mode_model_bound != null) sources.fast_mode_model_bound = source;
if (settings.slash_menu_categories != null) sources.slash_menu_categories = source;
if (settings.collapse_tool_calls != null) sources.collapse_tool_calls = source;
@@ -1688,6 +1718,12 @@ fn parseProfileOnlyFields(
settings.fast_mode = value.bool;
}
if (root.object.get("ultrafast_mode")) |ultrafast_mode_value| {
const value = ultrafast_mode_value;
if (value != .bool) return error.InvalidUltrafastModeType;
settings.ultrafast_mode = value.bool;
}
if (root.object.get("fast_mode_model_bound")) |bound_value| {
if (bound_value != .bool) return error.InvalidFastModeBindingType;
settings.fast_mode_model_bound = bound_value.bool;
@@ -1915,6 +1951,7 @@ fn mergeSettings(target: *Settings, incoming: *Settings, alloc: Allocator) !void
if (incoming.first_call_tool_choice) |value| target.first_call_tool_choice = value;
if (incoming.context) |value| target.context = value;
if (incoming.fast_mode) |value| target.fast_mode = value;
if (incoming.ultrafast_mode) |value| target.ultrafast_mode = value;
if (incoming.fast_mode_model_bound) |value| target.fast_mode_model_bound = value;
if (incoming.provider_order) |value| {
if (target.provider_order) |old| {
@@ -4653,3 +4690,43 @@ test "modelNotSelectedMessage names the provider and both ways to recover" {
try std.testing.expect(std.mem.find(u8, modelNotSelectedMessage(error.ConfiguredModelNotSelected).?, "\"models\" in ~/.fx/settings.json") != null);
try std.testing.expect(modelNotSelectedMessage(error.OutOfMemory) == null);
}
test "FX_ULTRAFAST overrides the profile and diagnoses malformed values" {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
try tmp.dir.createDirPath(io_mod.getIo(), "home/.fx");
try tmp.dir.createDirPath(io_mod.getIo(), "workspace");
const home_root = try io_mod.dirRealpathAlloc(std.testing.allocator, tmp.dir, "home");
defer std.testing.allocator.free(home_root);
const workspace_root = try io_mod.dirRealpathAlloc(std.testing.allocator, tmp.dir, "workspace");
defer std.testing.allocator.free(workspace_root);
try writeFixtureFile(tmp.dir, "home/.fx/settings.json", "{\"ultrafast_mode\":true}\n");
const home = try TestHome.install(std.testing.allocator, home_root);
defer home.deinit();
try home.map.put("FX_ULTRAFAST", "true");
var enabled = try loadMergedSettingsDetailedFromHome(std.testing.allocator, home_root, workspace_root);
defer enabled.deinit(std.testing.allocator);
try std.testing.expectEqual(@as(?bool, true), enabled.ultrafast_mode_env_override);
try home.map.put("FX_ULTRAFAST", "1");
var numeric_enabled = try loadMergedSettingsDetailedFromHome(std.testing.allocator, home_root, workspace_root);
defer numeric_enabled.deinit(std.testing.allocator);
try std.testing.expectEqual(@as(?bool, true), numeric_enabled.ultrafast_mode_env_override);
try home.map.put("FX_ULTRAFAST", "0");
var disabled = try loadMergedSettingsDetailedFromHome(std.testing.allocator, home_root, workspace_root);
defer disabled.deinit(std.testing.allocator);
try std.testing.expect(disabled.settings.ultrafast_mode.?);
try std.testing.expectEqual(@as(?bool, false), disabled.ultrafast_mode_env_override);
try home.map.put("FX_ULTRAFAST", "paid");
var malformed = try loadMergedSettingsDetailedFromHome(std.testing.allocator, home_root, workspace_root);
defer malformed.deinit(std.testing.allocator);
try std.testing.expect(malformed.ultrafast_mode_env_override == null);
var diagnosed = false;
for (malformed.diagnostics) |diagnostic| {
if (diagnostic.cause == .invalid_ultrafast_mode_override) diagnosed = true;
}
try std.testing.expect(diagnosed);
}
+36
View File
@@ -1,9 +1,11 @@
const std = @import("std");
const types = @import("../shared/types.zig");
const model_provider = @import("model_provider.zig");
pub const ResolvedProviderOptions = struct {
reasoning: ?types.ReasoningEffort = null,
fast: bool = false,
ultrafast: bool = false,
parallel_tool_calls: ?bool = null,
prompt_caching: bool = false,
/// Borrowed gateway provider slugs in preference order. Empty leaves
@@ -41,6 +43,7 @@ pub const GatewayMetadata = struct {
supports_reasoning: bool = false,
reasoning_efforts: ReasoningEffortOptions = .{},
supports_fast_mode: bool = false,
supports_ultrafast_mode: bool = false,
supports_tool_use: bool = false,
supports_vision: bool = false,
supports_file_input: bool = false,
@@ -55,6 +58,7 @@ pub const Capabilities = struct {
supports_reasoning: bool = false,
reasoning_efforts: ReasoningEffortOptions = .{},
supports_fast_mode: bool = false,
supports_ultrafast_mode: bool = false,
intrinsic_fast: bool = false,
supports_tool_use: bool = false,
supports_vision: bool = false,
@@ -89,6 +93,7 @@ pub fn mergeCapabilities(capabilities_value: Capabilities, gateway_metadata: ?Ga
capabilities.supports_reasoning = metadata.supports_reasoning or metadata.reasoning_efforts.len > 0;
capabilities.reasoning_efforts = metadata.reasoning_efforts;
capabilities.supports_fast_mode = metadata.supports_fast_mode;
capabilities.supports_ultrafast_mode = metadata.supports_ultrafast_mode;
capabilities.supports_tool_use = metadata.supports_tool_use;
capabilities.supports_vision = metadata.supports_vision;
capabilities.supports_file_input = metadata.supports_file_input;
@@ -199,6 +204,37 @@ pub fn resolveProviderOptionsForCapabilities(
return resolved;
}
/// Ultrafast is an explicit, catalog-verified OpenAI Gateway opt-in. Unlike Fast,
/// unsupported or unknown capabilities must not silently downgrade before sending.
pub fn resolveUltrafastProviderOptions(
capabilities: Capabilities,
provider: model_provider.ProviderId,
model: []const u8,
effort: types.ReasoningEffort,
fast_mode: bool,
ultrafast_mode: bool,
) error{UltrafastUnavailable}!ResolvedProviderOptions {
var options = resolveProviderOptionsForCapabilities(capabilities, effort, fast_mode);
if (!ultrafast_mode) return options;
if (provider != .gateway or !std.mem.startsWith(u8, model, "openai/") or !capabilities.supports_ultrafast_mode) {
return error.UltrafastUnavailable;
}
options.fast = false;
options.ultrafast = true;
return options;
}
test "Ultrafast requires explicit opt-in and verified OpenAI Gateway capability" {
const caps = Capabilities{ .supports_fast_mode = true, .supports_ultrafast_mode = true };
const normal = try resolveUltrafastProviderOptions(caps, .gateway, "openai/gpt-6-astra", .auto, true, false);
try std.testing.expect(normal.fast and !normal.ultrafast);
const ultra = try resolveUltrafastProviderOptions(caps, .gateway, "openai/gpt-6-astra", .auto, true, true);
try std.testing.expect(ultra.ultrafast and !ultra.fast);
try std.testing.expectError(error.UltrafastUnavailable, resolveUltrafastProviderOptions(.{}, .gateway, "openai/gpt-6-astra", .auto, false, true));
try std.testing.expectError(error.UltrafastUnavailable, resolveUltrafastProviderOptions(caps, .codex, "openai/gpt-6-astra", .auto, false, true));
try std.testing.expectError(error.UltrafastUnavailable, resolveUltrafastProviderOptions(caps, .gateway, "anthropic/model", .auto, false, true));
}
test "capabilities infer intrinsic fast identity but not controls from model IDs" {
const models = [_]struct { id: []const u8, intrinsic_fast: bool }{
.{ .id = "openai/gpt-5.6-sol", .intrinsic_fast = false },
+9 -2
View File
@@ -32,6 +32,7 @@ pub const SettingId = enum {
model,
effort,
fast_mode,
ultrafast_mode,
permission_mode,
sound_level,
startup_scrollback,
@@ -51,6 +52,8 @@ pub const Snapshot = struct {
reasoning_efforts: model_capabilities.ReasoningEffortOptions = .{},
fast_mode: bool = false,
supports_fast_mode: bool = false,
ultrafast_mode: bool = false,
supports_ultrafast_mode: bool = false,
permission_mode: []const u8 = "ask",
statusline_context: bool = false,
statusline_session: bool = false,
@@ -67,6 +70,7 @@ pub const Snapshot = struct {
.model => self.model,
.effort => self.effort,
.fast_mode => onOff(self.fast_mode),
.ultrafast_mode => onOff(self.ultrafast_mode),
.permission_mode => if (std.mem.eql(u8, self.permission_mode, "yolo")) "full access" else self.permission_mode,
.statusline_context => onOff(self.statusline_context),
.statusline_session => onOff(self.statusline_session),
@@ -263,6 +267,7 @@ const specs = [_]Spec{
.{ .id = .model, .category = .agent, .label = "Model", .description = "Choose the model used for new turns" },
.{ .id = .effort, .category = .agent, .label = "Reasoning effort", .description = "Control how much reasoning the model applies" },
.{ .id = .fast_mode, .category = .agent, .label = "Fast mode", .description = "Use faster inference when the model supports it" },
.{ .id = .ultrafast_mode, .category = .agent, .label = "Ultra mode", .description = "Request the highest-speed mode when the model supports it" },
.{ .id = .permission_mode, .category = .agent, .label = "Permission mode", .description = "Choose when fx asks before taking actions" },
.{ .id = .session_titles, .category = .agent, .label = "Session titles", .description = "Generate a short session title from the first prompt" },
.{ .id = .sound_level, .category = .notifications, .label = "Sound level", .description = "Choose off, on, or max sounds and terminal bells" },
@@ -307,6 +312,7 @@ pub fn optionCount(snapshot: *const Snapshot, id: SettingId) usize {
else
0,
.fast_mode => if (snapshot.supports_fast_mode or snapshot.fast_mode) on_off_options.len else 0,
.ultrafast_mode => if (snapshot.supports_ultrafast_mode or snapshot.ultrafast_mode) on_off_options.len else 0,
else => staticOptionsFor(id).len,
};
}
@@ -355,6 +361,7 @@ fn staticOptionsFor(id: SettingId) []const []const u8 {
return switch (id) {
.model, .effort => &.{},
.fast_mode,
.ultrafast_mode,
.statusline_context,
.statusline_session,
.statusline_workspace,
@@ -419,9 +426,9 @@ test "settings catalog projects grouped searchable preferences" {
.sound_level = "on",
};
try std.testing.expectEqual(@as(usize, 13), filteredCount(snapshot, .all, ""));
try std.testing.expectEqual(@as(usize, 14), filteredCount(snapshot, .all, ""));
try std.testing.expectEqual(@as(usize, 5), filteredCount(snapshot, .interface, ""));
try std.testing.expectEqual(@as(usize, 5), filteredCount(snapshot, .agent, ""));
try std.testing.expectEqual(@as(usize, 6), filteredCount(snapshot, .agent, ""));
try std.testing.expectEqual(@as(usize, 1), filteredCount(snapshot, .notifications, ""));
try std.testing.expectEqual(@as(usize, 2), filteredCount(snapshot, .advanced, ""));
+27 -1
View File
@@ -102,6 +102,7 @@ pub const UserSettingsPatch = struct {
yolo_acknowledged: ?bool = null,
effort: ?types.ReasoningEffort = null,
fast_mode: ?bool = null,
ultrafast_mode: ?bool = null,
slash_menu_categories: ?bool = null,
collapse_tool_calls: ?bool = null,
update_channel: ?update_target.Channel = null,
@@ -122,6 +123,7 @@ pub const UserSettingsPatch = struct {
self.yolo_acknowledged == null and
self.effort == null and
self.fast_mode == null and
self.ultrafast_mode == null and
self.slash_menu_categories == null and
self.collapse_tool_calls == null and
self.update_channel == null and
@@ -216,6 +218,7 @@ const UserPreferenceField = enum(u4) {
permission_mode,
effort,
fast_mode,
ultrafast_mode,
slash_menu_categories,
collapse_tool_calls,
update_channel,
@@ -235,6 +238,7 @@ const UserPreferenceField = enum(u4) {
.permission_mode => "settings.json.preference-migration.permission_mode.json",
.effort => "settings.json.preference-migration.effort.json",
.fast_mode => "settings.json.preference-migration.fast_mode.json",
.ultrafast_mode => "settings.json.preference-migration.ultrafast_mode.json",
.slash_menu_categories => "settings.json.preference-migration.slash_menu_categories.json",
.collapse_tool_calls => "settings.json.preference-migration.collapse_tool_calls.json",
.update_channel => "settings.json.preference-migration.update_channel.json",
@@ -252,6 +256,7 @@ const user_preference_fields = [_]UserPreferenceField{
.permission_mode,
.effort,
.fast_mode,
.ultrafast_mode,
.slash_menu_categories,
.collapse_tool_calls,
.update_channel,
@@ -941,6 +946,19 @@ test "clearing the credential choice removes the key rather than blanking it" {
try std.testing.expect(!application.changed);
}
test "ultrafast user patch writes a profile-owned bool" {
const alloc = std.testing.allocator;
var arena = std.heap.ArenaAllocator.init(alloc);
defer arena.deinit();
var parsed = try std.json.parseFromSlice(std.json.Value, arena.allocator(), "{}", .{});
defer parsed.deinit();
var root = parsed.value;
const application = try applyUserPatchToRoot(arena.allocator(), &root, .{ .ultrafast_mode = false });
try std.testing.expect(application.changed);
try std.testing.expect(!root.object.get("ultrafast_mode").?.bool);
}
test "collapse tool calls user patch writes the profile preference" {
const alloc = std.testing.allocator;
var arena = std.heap.ArenaAllocator.init(alloc);
@@ -1041,6 +1059,7 @@ fn applyUserPatchToRoot(
if (patch.yolo_acknowledged) |value| application.changed = try putBool(arena, &root.object, "yolo_acknowledged", value) or application.changed;
if (patch.effort) |value| application.changed = try putString(arena, &root.object, "effort", value.label()) or application.changed;
if (patch.fast_mode) |value| application.changed = try putBool(arena, &root.object, "fast_mode", value) or application.changed;
if (patch.ultrafast_mode) |value| application.changed = try putBool(arena, &root.object, "ultrafast_mode", value) or application.changed;
if (patch.model_preference != null and patch.fast_mode != null) {
application.changed = try putBool(arena, &root.object, "fast_mode_model_bound", true) or application.changed;
} else if ((patch.model_preference != null or patch.fast_mode != null) and root.object.contains("fast_mode_model_bound")) {
@@ -1168,6 +1187,13 @@ fn cleanupLegacyWorkspacePreferences(
patch.fast_mode != null,
application,
);
removeLegacyLeaf(
&entry.value_ptr.object,
"ultrafast_mode",
.ultrafast_mode,
patch.ultrafast_mode != null,
application,
);
if ((patch.model_preference != null or patch.fast_mode != null) and
entry.value_ptr.object.contains("fast_mode_model_bound"))
{
@@ -1944,7 +1970,7 @@ fn validateKnownSettingsObject(
}
}
}
inline for (&.{ "context", "fast_mode", "auto_upgrade", "slash_menu_categories", "startup_scrollback", "yolo_acknowledged", "provider_strict" }) |key| {
inline for (&.{ "context", "fast_mode", "ultrafast_mode", "auto_upgrade", "slash_menu_categories", "startup_scrollback", "yolo_acknowledged", "provider_strict" }) |key| {
if (object.get(key)) |value| {
if (value != .bool) return error.InvalidSettingsFormat;
}
+2
View File
@@ -319,6 +319,7 @@ pub const ModelCatalogEntry = struct {
has_reasoning: bool = false,
reasoning_efforts: std.ArrayList(types.ReasoningEffort) = .empty,
supports_fast_mode: bool = false,
supports_ultrafast_mode: bool = false,
has_vision: bool = false,
has_file_input: bool = false,
has_web_search: bool = false,
@@ -378,6 +379,7 @@ fn cloneModelCatalogEntry(alloc: std.mem.Allocator, entry: ModelCatalogEntry) !M
.has_reasoning = entry.has_reasoning,
.reasoning_efforts = reasoning_efforts,
.supports_fast_mode = entry.supports_fast_mode,
.supports_ultrafast_mode = entry.supports_ultrafast_mode,
.has_vision = entry.has_vision,
.has_file_input = entry.has_file_input,
.has_web_search = entry.has_web_search,
@@ -12,6 +12,7 @@ pub fn fromCatalogEntry(entry: model_catalog.ModelCatalogEntry) model_capabiliti
.supports_reasoning = entry.has_reasoning,
.reasoning_efforts = .fromSlice(entry.reasoning_efforts.items),
.supports_fast_mode = entry.supports_fast_mode,
.supports_ultrafast_mode = entry.supports_ultrafast_mode,
.supports_tool_use = entry.has_tool_use,
.supports_vision = entry.has_vision,
.supports_file_input = entry.has_file_input,
+1
View File
@@ -250,6 +250,7 @@ test "selection deletion wins and picker policy respects empty boundaries" {
"provider/model",
0,
false,
false,
.effort,
);
_ = fixture.edit.beginSelection(2);
+1 -1
View File
@@ -198,7 +198,7 @@ test "bounded insertion rejects before applying the successful transition" {
var picker: picker_state.State = .{};
defer picker.deinit(alloc);
try picker.beginModelPickerFlow(alloc, "openai/gpt-5", 2, false, .effort);
try picker.beginModelPickerFlow(alloc, "openai/gpt-5", 2, false, false, .effort);
picker.file_completion_index = 3;
picker.file_completion_window_start = 1;
@@ -120,6 +120,7 @@ test "line continuation records one edit and reconciles transient state" {
"provider/model",
2,
true,
false,
.effort,
);
fixture.picker.slash_completion_index = 3;
@@ -172,6 +173,7 @@ test "line continuation no-op only resets vertical navigation" {
"provider/model",
1,
false,
false,
.effort,
);
fixture.picker.file_completion_index = 3;
+1 -1
View File
@@ -353,7 +353,7 @@ test "bounded selection replacement preserves state on rejection" {
_ = fixture.edit.beginSelection(2);
_ = fixture.edit.extendSelection(6);
try fixture.picker.beginModelPickerFlow(alloc, "provider/model", 2, false, .effort);
try fixture.picker.beginModelPickerFlow(alloc, "provider/model", 2, false, false, .effort);
fixture.picker.file_completion_index = 3;
fixture.picker.file_completion_window_start = 1;
fixture.vertical.preferred_column = 7;
@@ -164,6 +164,7 @@ const Fixture = struct {
"provider/model",
2,
true,
false,
.effort,
);
self.picker.slash_completion_index = 4;
+1 -1
View File
@@ -200,7 +200,7 @@ test "composer undo and redo apply the recorded transition and reset edit policy
try fixture.edit.setText(alloc, "ab ");
try fixture.record(alloc, 1, "", "b", 1, 2);
try fixture.picker.beginModelPickerFlow(alloc, "provider/model", 2, true, .effort);
try fixture.picker.beginModelPickerFlow(alloc, "provider/model", 2, true, false, .effort);
fixture.picker.file_completion_index = 3;
fixture.picker.file_completion_window_start = 1;
fixture.entities.markFileCompletionSeparator(
+1 -1
View File
@@ -152,7 +152,7 @@ test "session input reset clears transient state and preserves prompt history" {
picker.model_completion_window_start = 2;
picker.file_completion_index = 4;
picker.file_completion_window_start = 1;
try picker.beginModelPickerFlow(alloc, "provider/model", 2, true, .effort);
try picker.beginModelPickerFlow(alloc, "provider/model", 2, true, false, .effort);
try std.testing.expect(vertical.applyTarget(&edit, &entities, edit.input.items.len, 4));
_ = edit.beginSelection(0);
_ = edit.extendSelection(edit.input.items.len);
+15 -7
View File
@@ -45,7 +45,7 @@ const InlinePickerSuppression = union(enum) {
}
};
pub const model_picker_fast_options = [_][]const u8{ "normal", "fast" };
pub const model_picker_speed_options = [_][]const u8{ "normal", "fast", "ultrafast" };
/// `/login` and `/setup` are aliases of `/provider`: all open the same
/// columnar picker. Typed text keeps whichever spelling the user wrote;
@@ -403,6 +403,7 @@ pub const State = struct {
model: []const u8,
effort_index: usize,
fast_mode: bool,
ultrafast_mode: bool,
stage: ModelPickerStage,
) Allocator.Error!void {
const stable_model = try alloc.dupe(u8, model);
@@ -414,7 +415,7 @@ pub const State = struct {
self.model_picker_stage = stage;
self.model_picker_effort_index = effort_index;
self.model_picker_effort_window_start = 0;
self.model_picker_fast_index = if (fast_mode) 1 else 0;
self.model_picker_fast_index = if (ultrafast_mode) 2 else if (fast_mode) 1 else 0;
self.model_picker_fast_window_start = 0;
}
@@ -438,8 +439,12 @@ pub const State = struct {
return self.model_picker_effort_index;
}
pub fn selectedModelPickerSpeed(self: *const State) []const u8 {
return model_picker_speed_options[self.model_picker_fast_index % model_picker_speed_options.len];
}
pub fn selectedModelPickerFast(self: *const State) bool {
return self.model_picker_fast_index % model_picker_fast_options.len == 1;
return std.mem.eql(u8, self.selectedModelPickerSpeed(), "fast");
}
};
@@ -826,14 +831,16 @@ test "model picker flow accepts aliased pending model input" {
var state: State = .{};
defer state.deinit(alloc);
try state.beginModelPickerFlow(alloc, "openai/gpt-5", 2, false, .effort);
try state.beginModelPickerFlow(alloc, "openai/gpt-5", 2, false, false, .effort);
const aliased_model = state.model_picker_pending_model.items;
try state.beginModelPickerFlow(alloc, aliased_model, 3, true, .fast);
try state.beginModelPickerFlow(alloc, aliased_model, 3, true, false, .fast);
try std.testing.expectEqual(ModelPickerStage.fast, state.model_picker_stage);
try std.testing.expectEqualStrings("openai/gpt-5", state.model_picker_pending_model.items);
try std.testing.expectEqual(@as(usize, 3), state.selectedModelPickerEffortIndex());
try std.testing.expect(state.selectedModelPickerFast());
try std.testing.expectEqualStrings("fast", state.selectedModelPickerSpeed());
try state.beginModelPickerFlow(alloc, aliased_model, 3, false, true, .fast);
try std.testing.expectEqualStrings("ultrafast", state.selectedModelPickerSpeed());
}
test "model picker flow preserves state when allocation fails" {
@@ -841,7 +848,7 @@ test "model picker flow preserves state when allocation fails" {
var state: State = .{};
defer state.deinit(alloc);
try state.beginModelPickerFlow(alloc, "old", 2, false, .effort);
try state.beginModelPickerFlow(alloc, "old", 2, false, false, .effort);
var failing = std.testing.FailingAllocator.init(alloc, .{
.fail_index = 1,
@@ -856,6 +863,7 @@ test "model picker flow preserves state when allocation fails" {
&large_model,
5,
true,
false,
.fast,
),
);
+9 -5
View File
@@ -484,6 +484,8 @@ pub const StatusSnapshot = struct {
history_turns: usize,
session_permission_grants: usize,
agent_step_limit: usize,
/// Requested Ultra mode. The serving provider may still fall back.
ultrafast_requested: bool = false,
pub fn render(self: StatusSnapshot, alloc: Allocator, format: OutputFormat) ![]u8 {
return switch (format) {
@@ -543,6 +545,7 @@ pub const StatusSnapshot = struct {
try out.writer.print("[status] history_turns={d}\n", .{self.history_turns});
try out.writer.print("[status] session_permission_grants={d}\n", .{self.session_permission_grants});
try out.writer.print("[status] agent_step_limit={d}\n", .{self.agent_step_limit});
try out.writer.print("[status] ultrafast_requested={}\n", .{self.ultrafast_requested});
if (self.mcp) |mcp| try mcp.writeText(&out.writer, alloc, "status");
return try out.toOwnedSlice();
}
@@ -575,7 +578,7 @@ pub const StatusSnapshot = struct {
try out.writer.print("workspace={s}\n", .{self.workspace_root});
try out.writer.print("history_turns={d}\n", .{self.history_turns});
try out.writer.print("session_permission_grants={d}\n", .{self.session_permission_grants});
try out.writer.print("agent_step_limit={d}", .{self.agent_step_limit});
try out.writer.print("agent_step_limit={d}\nultrafast_requested={}", .{ self.agent_step_limit, self.ultrafast_requested });
return try out.toOwnedSlice();
}
@@ -677,6 +680,7 @@ pub const StatusSnapshot = struct {
try writer.print(",\"history_turns\":{d}", .{self.history_turns});
try writer.print(",\"session_permission_grants\":{d}", .{self.session_permission_grants});
try writer.print(",\"agent_step_limit\":{d}", .{self.agent_step_limit});
try writer.print(",\"ultrafast_requested\":{}", .{self.ultrafast_requested});
if (self.mcp) |mcp| {
try writer.writeAll(",\"mcp\":");
try mcp.writeJson(writer);
@@ -1944,14 +1948,14 @@ test "core status snapshot text and json stay stable" {
const text = try snapshot.renderText(std.testing.allocator);
defer std.testing.allocator.free(text);
try std.testing.expectEqualStrings(
"[status] model=alpha\n[status] update_channel=stable\n[status] build_channel=stable\n[status] auth=missing\n[status] auth_refreshable=false\n[status] auth_help=fx needs access to Vercel AI Gateway. Run fx login to sign in, fx setup to use an API key, or set AI_GATEWAY_API_KEY.\n[status] permission_mode=ask\n[status] workspace=/tmp/fx\n[status] history_turns=3\n[status] session_permission_grants=1\n[status] agent_step_limit=24\n",
"[status] model=alpha\n[status] update_channel=stable\n[status] build_channel=stable\n[status] auth=missing\n[status] auth_refreshable=false\n[status] auth_help=fx needs access to Vercel AI Gateway. Run fx login to sign in, fx setup to use an API key, or set AI_GATEWAY_API_KEY.\n[status] permission_mode=ask\n[status] workspace=/tmp/fx\n[status] history_turns=3\n[status] session_permission_grants=1\n[status] agent_step_limit=24\n[status] ultrafast_requested=false\n",
text,
);
const json = try snapshot.renderJson(std.testing.allocator);
defer std.testing.allocator.free(json);
try std.testing.expectEqualStrings(
"{\"kind\":\"status\",\"model\":\"alpha\",\"update_channel\":\"stable\",\"build_channel\":\"stable\",\"build_revision\":\"\",\"auth\":\"missing\",\"auth_refreshable\":false,\"auth_help\":\"fx needs access to Vercel AI Gateway. Run fx login to sign in, fx setup to use an API key, or set AI_GATEWAY_API_KEY.\",\"permission_mode\":\"ask\",\"workspace\":\"/tmp/fx\",\"history_turns\":3,\"session_permission_grants\":1,\"agent_step_limit\":24}",
"{\"kind\":\"status\",\"model\":\"alpha\",\"update_channel\":\"stable\",\"build_channel\":\"stable\",\"build_revision\":\"\",\"auth\":\"missing\",\"auth_refreshable\":false,\"auth_help\":\"fx needs access to Vercel AI Gateway. Run fx login to sign in, fx setup to use an API key, or set AI_GATEWAY_API_KEY.\",\"permission_mode\":\"ask\",\"workspace\":\"/tmp/fx\",\"history_turns\":3,\"session_permission_grants\":1,\"agent_step_limit\":24,\"ultrafast_requested\":false}",
json,
);
}
@@ -1970,14 +1974,14 @@ test "core status snapshot includes selected team when present" {
const text = try snapshot.renderText(std.testing.allocator);
defer std.testing.allocator.free(text);
try std.testing.expectEqualStrings(
"[status] model=alpha\n[status] update_channel=stable\n[status] build_channel=stable\n[status] auth=fx login\n[status] auth_refreshable=true\n[status] team=example-team\n[status] permission_mode=ask\n[status] workspace=/tmp/fx\n[status] history_turns=0\n[status] session_permission_grants=0\n[status] agent_step_limit=24\n",
"[status] model=alpha\n[status] update_channel=stable\n[status] build_channel=stable\n[status] auth=fx login\n[status] auth_refreshable=true\n[status] team=example-team\n[status] permission_mode=ask\n[status] workspace=/tmp/fx\n[status] history_turns=0\n[status] session_permission_grants=0\n[status] agent_step_limit=24\n[status] ultrafast_requested=false\n",
text,
);
const json = try snapshot.renderJson(std.testing.allocator);
defer std.testing.allocator.free(json);
try std.testing.expectEqualStrings(
"{\"kind\":\"status\",\"model\":\"alpha\",\"update_channel\":\"stable\",\"build_channel\":\"stable\",\"build_revision\":\"\",\"auth\":\"fx login\",\"auth_refreshable\":true,\"team\":\"example-team\",\"permission_mode\":\"ask\",\"workspace\":\"/tmp/fx\",\"history_turns\":0,\"session_permission_grants\":0,\"agent_step_limit\":24}",
"{\"kind\":\"status\",\"model\":\"alpha\",\"update_channel\":\"stable\",\"build_channel\":\"stable\",\"build_revision\":\"\",\"auth\":\"fx login\",\"auth_refreshable\":true,\"team\":\"example-team\",\"permission_mode\":\"ask\",\"workspace\":\"/tmp/fx\",\"history_turns\":0,\"session_permission_grants\":0,\"agent_step_limit\":24,\"ultrafast_requested\":false}",
json,
);
}
+61 -18
View File
@@ -2134,28 +2134,20 @@ fn encodePreferences(alloc: Allocator, preferences: session_codec.DurableSession
model: []const u8,
effort: []const u8,
fast_mode: bool,
ultrafast_mode: ?bool,
instructions: ?[]const u8,
};
const saved: Saved = .{
var out: std.Io.Writer.Allocating = .init(alloc);
errdefer out.deinit();
try std.json.Stringify.value(Saved{
.provider = preferences.provider,
.model = preferences.model,
.effort = preferences.effort.label(),
.fast_mode = preferences.fast_mode,
};
const text = instructions orelse return jsonValue(alloc, saved);
const ChildSaved = struct {
provider: model_provider.ProviderId,
model: []const u8,
effort: []const u8,
fast_mode: bool,
instructions: []const u8,
};
return jsonValue(alloc, ChildSaved{
.provider = saved.provider,
.model = saved.model,
.effort = saved.effort,
.fast_mode = saved.fast_mode,
.instructions = text,
});
.ultrafast_mode = if (preferences.ultrafast_mode) true else null,
.instructions = instructions,
}, .{ .emit_null_optional_fields = false }, &out.writer);
return out.toOwnedSlice();
}
fn decodePreferences(alloc: Allocator, raw: []const u8) !session_codec.DurableSessionPreferences {
@@ -2237,15 +2229,22 @@ test "preferences round trip through v1's decoder" {
var model = "vendor/model-1".*;
const efforts = [_]types.ReasoningEffort{ .auto, types.ReasoningEffort.parse("high").? };
for (efforts) |effort| {
const raw = try encodePreferences(arena.allocator(), .{ .model = &model, .effort = effort, .fast_mode = true }, null);
const raw = try encodePreferences(arena.allocator(), .{ .model = &model, .effort = effort, .fast_mode = true, .ultrafast_mode = true }, null);
try testing.expect((try decodeInstructions(testing.allocator, raw)) == null);
var decoded = try decodePreferences(testing.allocator, raw);
defer decoded.deinit(testing.allocator);
try testing.expectEqualStrings("vendor/model-1", decoded.model);
try testing.expectEqualStrings(effort.label(), decoded.effort.label());
try testing.expect(decoded.fast_mode);
try testing.expect(decoded.ultrafast_mode);
try testing.expectEqual(model_provider.ProviderId.gateway, decoded.provider);
}
const off = try encodePreferences(arena.allocator(), .{ .model = &model, .effort = .auto, .fast_mode = false }, null);
try testing.expectEqualStrings("{\"provider\":\"gateway\",\"model\":\"vendor/model-1\",\"effort\":\"auto\",\"fast_mode\":false}", off);
var decoded_off = try decodePreferences(testing.allocator, off);
defer decoded_off.deinit(testing.allocator);
try testing.expect(!decoded_off.ultrafast_mode);
}
test "the switch is the flag or FX_SESSIONS_V2" {
@@ -2424,6 +2423,50 @@ test "the manager guards child lines, and a child without a log reopens under it
try testing.expectEqualStrings("", child.childInstructions());
}
test "v2 child ultrafast preferences survive instruction changes and resume" {
var t: TestHome = undefined;
try t.init();
defer t.deinit();
const alloc = testing.allocator;
var model = "test-model".*;
const parent = try Session.create(alloc, &t.store, "/w", .ask, testSeed(&model));
defer parent.close();
try parent.commitTurn(assistantTurn("delegate", "ok"), types.ConversationLanguage.default());
const child_id = "1786460757753-ultra";
try parent.appendChildLines(&.{.{ .spawned = .{ .child = child_id, .work_id = "w1" } }});
var seed = childSeed(&model, "Be brief.");
seed.preferences.ultrafast_mode = true;
{
const child = try Session.openChild(alloc, &t.store, parent.id(), child_id, "/w", seed);
defer child.close();
try child.commitTurn(assistantTurn("task", "done"), types.ConversationLanguage.default());
var preferences = try child.currentPreferences(alloc);
defer preferences.deinit(alloc);
try testing.expect(preferences.ultrafast_mode);
}
seed.instructions = "Be thorough.";
{
const child = try Session.openChild(alloc, &t.store, parent.id(), child_id, "/w", seed);
defer child.close();
var preferences = try child.currentPreferences(alloc);
defer preferences.deinit(alloc);
try testing.expect(preferences.ultrafast_mode);
preferences.ultrafast_mode = false;
try child.setPreferences(preferences);
try testing.expectEqualStrings("Be thorough.", child.childInstructions());
}
seed.instructions = "Answer in one line.";
const child = try Session.openChild(alloc, &t.store, parent.id(), child_id, "/w", seed);
defer child.close();
var preferences = try child.currentPreferences(alloc);
defer preferences.deinit(alloc);
try testing.expect(!preferences.ultrafast_mode);
try testing.expectEqualStrings("Answer in one line.", child.childInstructions());
var state = try child.handle.state(alloc);
defer state.deinit(alloc);
try testing.expect(std.mem.find(u8, state.prefs.?, "\"ultrafast_mode\"") == null);
}
test "a copy of a parent's children frees every part when memory runs out" {
var t: TestHome = undefined;
try t.init();
+144 -11
View File
@@ -19,6 +19,7 @@ pub const DurableSessionPreferences = struct {
model: []u8,
effort: types.ReasoningEffort,
fast_mode: bool,
ultrafast_mode: bool = false,
pub fn deinit(self: *DurableSessionPreferences, alloc: Allocator) void {
alloc.free(self.model);
@@ -31,6 +32,7 @@ pub const DurableSessionPreferences = struct {
.model = try alloc.dupe(u8, self.model),
.effort = self.effort,
.fast_mode = self.fast_mode,
.ultrafast_mode = self.ultrafast_mode,
};
}
};
@@ -39,10 +41,19 @@ pub const DurableSessionPreferences = struct {
pub fn parse_preferences(alloc: Allocator, value: std.json.Value) !DurableSessionPreferences {
const raw = try requireObject(value);
const legacy = raw.contains("connection_id") or raw.contains("model_id");
const has_ultrafast_mode = raw.contains("ultrafast_mode");
const object = if (legacy)
try exactObject(value, &.{ "connection_id", "model_id", "effort", "fast_mode" })
if (has_ultrafast_mode)
try exactObject(value, &.{ "connection_id", "model_id", "effort", "fast_mode", "ultrafast_mode" })
else
try exactObject(value, &.{ "connection_id", "model_id", "effort", "fast_mode" })
else if (raw.contains("provider"))
try exactObject(value, &.{ "provider", "model", "effort", "fast_mode" })
if (has_ultrafast_mode)
try exactObject(value, &.{ "provider", "model", "effort", "fast_mode", "ultrafast_mode" })
else
try exactObject(value, &.{ "provider", "model", "effort", "fast_mode" })
else if (has_ultrafast_mode)
try exactObject(value, &.{ "model", "effort", "fast_mode", "ultrafast_mode" })
else
try exactObject(value, &.{ "model", "effort", "fast_mode" });
const provider: model_provider.ProviderId = if (legacy) blk: {
@@ -56,7 +67,8 @@ pub fn parse_preferences(alloc: Allocator, value: std.json.Value) !DurableSessio
try validateModel(model);
const effort = types.ReasoningEffort.parse(try requireString(object, "effort")) orelse return error.InvalidDurableField;
const fast_mode = try requireBool(object, "fast_mode");
return .{ .provider = provider, .model = try alloc.dupe(u8, model), .effort = effort, .fast_mode = fast_mode };
const ultrafast_mode = if (has_ultrafast_mode) try requireBool(object, "ultrafast_mode") else false;
return .{ .provider = provider, .model = try alloc.dupe(u8, model), .effort = effort, .fast_mode = fast_mode, .ultrafast_mode = ultrafast_mode };
}
test "legacy connection preferences decode through the shared state codec" {
@@ -134,6 +146,8 @@ pub const RecoveryCheckpoint = struct {
authority: TurnAuthority,
requested_fast_mode: bool,
fast_mode: bool,
requested_ultrafast_mode: bool = false,
ultrafast_mode: bool = false,
max_provider_attempts: usize,
consumed_provider_attempts: usize,
outstanding_reservation: bool = false,
@@ -177,6 +191,8 @@ pub const RecoveryCheckpoint = struct {
.authority = authority,
.requested_fast_mode = self.requested_fast_mode,
.fast_mode = self.fast_mode,
.requested_ultrafast_mode = self.requested_ultrafast_mode,
.ultrafast_mode = self.ultrafast_mode,
.max_provider_attempts = self.max_provider_attempts,
.consumed_provider_attempts = self.consumed_provider_attempts,
.outstanding_reservation = self.outstanding_reservation,
@@ -307,6 +323,8 @@ pub const SessionMetadata = struct {
model: []const u8,
effort: []const u8,
fast_mode: bool,
/// Omitted when false so schema-v4 headers remain readable by old binaries.
ultrafast_mode: ?bool = null,
title: ?[]const u8 = null,
subagent_child: bool = false,
};
@@ -322,7 +340,11 @@ pub fn encodeSessionMetadata(
try validateSessionMetadata(metadata);
var out: std.Io.Writer.Allocating = .init(alloc);
errdefer out.deinit();
try std.json.Stringify.value(metadata, .{}, &out.writer);
try std.json.Stringify.value(
metadata,
.{ .emit_null_optional_fields = false },
&out.writer,
);
if (out.written().len == 0 or out.written().len > max_session_metadata_bytes) {
return error.SessionMetadataTooLarge;
}
@@ -874,9 +896,13 @@ fn writeState(writer: *std.Io.Writer, state: DurableSessionState) !void {
try writeJsonString(writer, state.preferences.model);
try writer.writeAll(",\"effort\":");
try writeJsonString(writer, state.preferences.effort.label());
try writer.print(",\"fast_mode\":{s},\"provider\":", .{
try writer.print(",\"fast_mode\":{s}", .{
if (state.preferences.fast_mode) "true" else "false",
});
if (state.preferences.ultrafast_mode) {
try writer.writeAll(",\"ultrafast_mode\":true");
}
try writer.writeAll(",\"provider\":");
try std.json.Stringify.value(state.preferences.provider, .{}, writer);
try writer.writeAll("},\"history\":[");
for (state.history, 0..) |turn, i| {
@@ -1041,9 +1067,20 @@ pub fn writeRecoveryCheckpoint(writer: *std.Io.Writer, checkpoint: RecoveryCheck
try writer.writeAll("null");
}
try writer.writeByte('}');
try writer.print(",\"requested_fast_mode\":{s},\"fast_mode\":{s},\"max_provider_attempts\":{d},\"consumed_provider_attempts\":{d},\"outstanding_reservation\":{s}}}", .{
try writer.print(",\"requested_fast_mode\":{s},\"fast_mode\":{s}", .{
if (checkpoint.requested_fast_mode) "true" else "false",
if (checkpoint.fast_mode) "true" else "false",
});
// Old readers exact-match the v2 checkpoint keys. Keep the optional pair
// absent at its all-false default, but preserve both values once either
// reports an Ultra selection.
if (checkpoint.requested_ultrafast_mode or checkpoint.ultrafast_mode) {
try writer.print(",\"requested_ultrafast_mode\":{s},\"ultrafast_mode\":{s}", .{
if (checkpoint.requested_ultrafast_mode) "true" else "false",
if (checkpoint.ultrafast_mode) "true" else "false",
});
}
try writer.print(",\"max_provider_attempts\":{d},\"consumed_provider_attempts\":{d},\"outstanding_reservation\":{s}}}", .{
checkpoint.max_provider_attempts,
checkpoint.consumed_provider_attempts,
if (checkpoint.outstanding_reservation) "true" else "false",
@@ -1291,11 +1328,17 @@ pub fn parseRecoveryCheckpoint(alloc: Allocator, value: std.json.Value) !Recover
"cause", "action", "tool_state", "route_model", "requested_fast_mode",
"fast_mode", "max_provider_attempts", "consumed_provider_attempts", "outstanding_reservation",
}),
2 => try exactObject(value, &.{
"version", "turn_id", "user", "assistant_source", "execution",
"cause", "action", "tool_state", "authority", "requested_fast_mode",
"fast_mode", "max_provider_attempts", "consumed_provider_attempts", "outstanding_reservation",
}),
2 => if (raw_object.contains("requested_ultrafast_mode") or raw_object.contains("ultrafast_mode"))
try exactObject(value, &.{
"version", "turn_id", "user", "assistant_source", "execution", "cause", "action", "tool_state", "authority",
"requested_fast_mode", "fast_mode", "requested_ultrafast_mode", "ultrafast_mode", "max_provider_attempts", "consumed_provider_attempts", "outstanding_reservation",
})
else
try exactObject(value, &.{
"version", "turn_id", "user", "assistant_source", "execution",
"cause", "action", "tool_state", "authority", "requested_fast_mode",
"fast_mode", "max_provider_attempts", "consumed_provider_attempts", "outstanding_reservation",
}),
else => return error.InvalidDurableField,
};
const user = try parseUserTurn(alloc, object.get("user") orelse return error.InvalidSessionFormat);
@@ -1341,6 +1384,8 @@ pub fn parseRecoveryCheckpoint(alloc: Allocator, value: std.json.Value) !Recover
.authority = authority,
.requested_fast_mode = try requireBool(object, "requested_fast_mode"),
.fast_mode = try requireBool(object, "fast_mode"),
.requested_ultrafast_mode = if (object.contains("requested_ultrafast_mode")) try requireBool(object, "requested_ultrafast_mode") else false,
.ultrafast_mode = if (object.contains("ultrafast_mode")) try requireBool(object, "ultrafast_mode") else false,
.max_provider_attempts = max_provider_attempts,
.consumed_provider_attempts = consumed_provider_attempts,
.outstanding_reservation = try requireBool(object, "outstanding_reservation"),
@@ -4872,6 +4917,35 @@ test "codec structural helpers enforce exact objects and required strings" {
try std.testing.expectEqualStrings("value", try requireString(valid_object, "known"));
}
test "default ultrafast state writes the legacy-compatible preference shape" {
const alloc = std.testing.allocator;
const state = DurableSessionState{
.id = @constCast("legacy-compatible"),
.origin_workspace_root = @constCast("/workspace"),
.workspace_root = @constCast("/workspace"),
.created_at_ms = 1,
.updated_at_ms = 2,
.conversation_language = session.ConversationLanguage.literal("en"),
.preferences = .{
.model = @constCast("test/model"),
.effort = .auto,
.fast_mode = false,
},
.history = &.{},
.total_input_tokens = 0,
.total_output_tokens = 0,
};
var encoded: std.Io.Writer.Allocating = .init(alloc);
defer encoded.deinit();
_ = try encodeState(state, &encoded.writer);
try std.testing.expect(std.mem.find(u8, encoded.written(), "\"ultrafast_mode\"") == null);
var source = std.Io.Reader.fixed(encoded.written());
var decoded = try decodeState(alloc, &source, .{});
defer decoded.deinit(alloc);
try std.testing.expect(!decoded.preferences.ultrafast_mode);
}
test "recovery checkpoint round trips while legacy state stays absent" {
const alloc = std.testing.allocator;
const checkpoint = RecoveryCheckpoint{
@@ -4892,6 +4966,8 @@ test "recovery checkpoint round trips while legacy state stays absent" {
},
.requested_fast_mode = true,
.fast_mode = true,
.requested_ultrafast_mode = true,
.ultrafast_mode = true,
.max_provider_attempts = 10,
.consumed_provider_attempts = 4,
.outstanding_reservation = true,
@@ -4908,6 +4984,7 @@ test "recovery checkpoint round trips while legacy state stays absent" {
.model = @constCast("gpt-5.4-mini"),
.effort = .auto,
.fast_mode = false,
.ultrafast_mode = true,
},
.history = &.{},
.total_input_tokens = 0,
@@ -4918,6 +4995,8 @@ test "recovery checkpoint round trips while legacy state stays absent" {
var encoded: std.Io.Writer.Allocating = .init(alloc);
defer encoded.deinit();
_ = try encodeState(state, &encoded.writer);
try std.testing.expect(std.mem.find(u8, encoded.written(), "\"requested_ultrafast_mode\":true") != null);
try std.testing.expect(std.mem.find(u8, encoded.written(), "\"ultrafast_mode\":true") != null);
var source = std.Io.Reader.fixed(encoded.written());
var decoded = try decodeState(alloc, &source, .{});
defer decoded.deinit(alloc);
@@ -4936,6 +5015,9 @@ test "recovery checkpoint round trips while legacy state stays absent" {
try std.testing.expectEqual(model_provider.ProviderId.codex, decoded.preferences.provider);
try std.testing.expect(restored.requested_fast_mode);
try std.testing.expect(restored.fast_mode);
try std.testing.expect(restored.requested_ultrafast_mode);
try std.testing.expect(restored.ultrafast_mode);
try std.testing.expect(decoded.preferences.ultrafast_mode);
try std.testing.expectEqual(@as(usize, 4), restored.consumed_provider_attempts);
try std.testing.expect(restored.outstanding_reservation);
@@ -4945,6 +5027,7 @@ test "recovery checkpoint round trips while legacy state stays absent" {
var legacy_state = try decodeState(alloc, &legacy_source, .{});
defer legacy_state.deinit(alloc);
try std.testing.expectEqual(model_provider.ProviderId.gateway, legacy_state.preferences.provider);
try std.testing.expect(!legacy_state.preferences.ultrafast_mode);
try std.testing.expectEqual(@as(?RecoveryCheckpoint, null), legacy_state.recovery_checkpoint);
}
@@ -4969,6 +5052,14 @@ test "recovery checkpoint accepts the exact composer byte limit" {
defer alloc.free(encoded);
try std.testing.expect(encoded.len > prompt.len);
try std.testing.expect(encoded.len <= max_recovery_checkpoint_bytes);
try std.testing.expect(std.mem.find(u8, encoded, "\"requested_ultrafast_mode\"") == null);
try std.testing.expect(std.mem.find(u8, encoded, "\"ultrafast_mode\"") == null);
var parsed = try std.json.parseFromSlice(std.json.Value, alloc, encoded, .{});
defer parsed.deinit();
var decoded = try parseRecoveryCheckpoint(alloc, parsed.value);
defer decoded.deinit(alloc);
try std.testing.expect(!decoded.requested_ultrafast_mode);
try std.testing.expect(!decoded.ultrafast_mode);
}
test "session permission state round trips while legacy state stays empty" {
@@ -5196,11 +5287,53 @@ test "session metadata round trips without conversation or control state" {
try std.testing.expect(std.mem.find(u8, encoded, "usage") == null);
try std.testing.expect(std.mem.find(u8, encoded, "permission") == null);
try std.testing.expect(std.mem.find(u8, encoded, "checkpoint") == null);
try std.testing.expect(std.mem.find(u8, encoded, "ultrafast_mode") == null);
var decoded = try decodeSessionMetadata(alloc, encoded);
defer decoded.deinit();
try std.testing.expectEqualStrings("session-1", decoded.value.id);
try std.testing.expectEqualStrings("/tmp/current", decoded.value.workspace_root);
try std.testing.expectEqualStrings("openai/gpt-5.6", decoded.value.model);
try std.testing.expect(decoded.value.ultrafast_mode == null);
try std.testing.expectEqualStrings("Compaction work", decoded.value.title.?);
}
test "session metadata persists opted-in ultrafast mode" {
const alloc = std.testing.allocator;
const encoded = try encodeSessionMetadata(alloc, .{
.id = "session-ultrafast",
.origin_workspace_root = "/tmp/origin",
.workspace_root = "/tmp/current",
.created_at_ms = 10,
.updated_at_ms = 20,
.conversation_language = "en",
.provider = .gateway,
.model = "openai/gpt-5.6",
.effort = "high",
.fast_mode = false,
.ultrafast_mode = true,
});
defer alloc.free(encoded);
try std.testing.expect(std.mem.find(u8, encoded, "\"ultrafast_mode\":true") != null);
var decoded = try decodeSessionMetadata(alloc, encoded);
defer decoded.deinit();
try std.testing.expectEqual(@as(?bool, true), decoded.value.ultrafast_mode);
}
test "ultrafast durable preference requires a bool when present" {
const alloc = std.testing.allocator;
var parsed = try std.json.parseFromSlice(
std.json.Value,
alloc,
"{\"provider\":\"gateway\",\"model\":\"test/model\",\"effort\":\"auto\",\"fast_mode\":false,\"ultrafast_mode\":\"true\"}",
.{},
);
defer parsed.deinit();
if (parse_preferences(alloc, parsed.value)) |preferences| {
var owned = preferences;
owned.deinit(alloc);
return error.TestUnexpectedResult;
} else |err| {
try std.testing.expect(err == error.InvalidSessionFormat or err == error.InvalidDurableField);
}
}
+204 -25
View File
@@ -155,7 +155,9 @@ fn postCommitResolutionError(
.retired_skill_match_fuzzy => return error.InvalidSettingsFormat,
.invalid_context_limits => return error.InvalidSettingsFormat,
.invalid_additional_directories => return error.InvalidSettingsFormat,
.invalid_skill_symlink_authorities => return error.InvalidSettingsFormat,
.invalid_skill_symlink_authorities,
.invalid_ultrafast_mode_override,
=> return error.InvalidSettingsFormat,
.ignored_project_user_only_setting,
.legacy_workspace_preferences,
.manual_backup_available,
@@ -179,6 +181,7 @@ fn appendShadowedUserSources(
try appendShadowedUserSource(writer, "permission_mode", patch.permission_mode != null, sources.permission_mode, &wrote_header);
try appendShadowedUserSource(writer, "effort", patch.effort != null, sources.effort, &wrote_header);
try appendShadowedUserSource(writer, "fast_mode", patch.fast_mode != null, sources.fast_mode, &wrote_header);
try appendShadowedUserSource(writer, "ultrafast_mode", patch.ultrafast_mode != null, sources.ultrafast_mode, &wrote_header);
try appendShadowedUserSource(writer, "startup_scrollback", patch.startup_scrollback != null, sources.startup_scrollback, &wrote_header);
try appendShadowedUserSource(writer, "prompt_history", patch.prompt_history_enabled != null, sources.prompt_history_enabled, &wrote_header);
if (patch.statusline_item) |item| {
@@ -301,6 +304,7 @@ pub fn Commands(comptime App: type) type {
.history_turns = app.session.historyLen(),
.session_permission_grants = app.permission_engine.grants.items.len,
.agent_step_limit = app.agent_step_limit,
.ultrafast_requested = app.worker.agent_turn_settings.ultrafast_mode,
}).renderInteractiveBody(app.alloc);
defer app.alloc.free(text);
try app.writeDomainNotice(.{ .topic = "status", .tone = .neutral, .body = text }, true);
@@ -697,6 +701,35 @@ pub fn Commands(comptime App: type) type {
try toggleFastForModel(app, provider_runtime.model(app), true);
}
pub fn handleUltrafast(app: *App, rest: []const u8) !void {
const command = std.mem.trim(u8, rest, " \t");
if (command.len == 0 or std.ascii.eqlIgnoreCase(command, "status")) {
try writeUltrafastStatus(app);
return;
}
if (std.ascii.eqlIgnoreCase(command, "on")) {
try applyUltrafastMode(app, true, true, true);
return;
}
if (std.ascii.eqlIgnoreCase(command, "off")) {
try applyUltrafastMode(app, false, true, true);
return;
}
try app.writeDomainNotice(.{
.topic = "",
.tone = .@"error",
.body = "usage: /ultrafast [on|off|status]",
}, true);
}
fn writeUltrafastStatus(app: *App) !void {
try app.writeDomainNotice(.{
.topic = "ultrafast",
.tone = .neutral,
.body = if (app.worker.agent_turn_settings.ultrafast_mode) "requested: on" else "requested: off",
}, true);
}
fn toggleFastForModel(app: *App, model: []const u8, announce: bool) !void {
if (app.fast_mode) {
try applyFastMode(app, false, announce, true);
@@ -719,14 +752,26 @@ pub fn Commands(comptime App: type) type {
fn applyFastMode(app: *App, enabled: bool, announce: bool, persist: bool) !void {
const previous = app.fast_mode;
const previous_ultrafast = app.worker.agent_turn_settings.ultrafast_mode;
app.fast_mode = enabled;
if (enabled) {
app.worker.agent_turn_settings.ultrafast_mode = false;
if (comptime @hasField(App, "session_persistence")) {
app.session_persistence.process_ultrafast_override = false;
}
}
app.worker.syncQueuedPromptFastMode(app.fast_mode);
if (previous_ultrafast != app.worker.agent_turn_settings.ultrafast_mode) {
app.worker.syncQueuedPromptUltrafastMode(app.worker.agent_turn_settings.ultrafast_mode);
}
debug_trace.logf(
"session",
"fast mode changed old={s} new={s} model={s} supports_fast={s}",
"fast mode changed old={s} new={s} ultrafast_old={s} ultrafast_new={s} model={s} supports_fast={s}",
.{
if (previous) "true" else "false",
if (app.fast_mode) "true" else "false",
if (previous_ultrafast) "true" else "false",
if (app.worker.agent_turn_settings.ultrafast_mode) "true" else "false",
provider_runtime.model(app),
if (model_capabilities.resolveForApp(App, app, provider_runtime.model(app)).supports_fast_mode) "true" else "false",
},
@@ -739,6 +784,7 @@ pub fn Commands(comptime App: type) type {
.provider = provider_runtime.provider(app),
.model = provider_runtime.model(app),
.fast_mode = app.fast_mode,
.ultrafast_mode = app.worker.agent_turn_settings.ultrafast_mode,
},
"fast",
!announce,
@@ -753,6 +799,67 @@ pub fn Commands(comptime App: type) type {
app.shell.render_requests.request(.footer);
}
fn applyUltrafastMode(app: *App, enabled: bool, announce: bool, persist: bool) !void {
const capabilities = model_capabilities.resolveForApp(App, app, provider_runtime.model(app));
if (enabled and !capabilities.supports_ultrafast_mode) {
if (announce) {
try app.writeDomainNotice(.{
.topic = "ultrafast",
.tone = .warning,
.body = "Ultra mode is unavailable for this model and may increase cost.",
}, true);
}
app.shell.render_requests.request(.footer);
return;
}
const previous_ultrafast = app.worker.agent_turn_settings.ultrafast_mode;
const previous_fast = app.fast_mode;
app.worker.agent_turn_settings.ultrafast_mode = enabled;
if (comptime @hasField(App, "session_persistence")) {
app.session_persistence.process_ultrafast_override = enabled;
}
if (enabled) app.fast_mode = false;
app.worker.syncQueuedPromptUltrafastMode(app.worker.agent_turn_settings.ultrafast_mode);
if (previous_fast != app.fast_mode) app.worker.syncQueuedPromptFastMode(app.fast_mode);
debug_trace.logf(
"session",
"ultrafast mode changed old={s} new={s} fast_old={s} fast_new={s} model={s} supports_ultrafast={s}",
.{
if (previous_ultrafast) "true" else "false",
if (app.worker.agent_turn_settings.ultrafast_mode) "true" else "false",
if (previous_fast) "true" else "false",
if (app.fast_mode) "true" else "false",
provider_runtime.model(app),
if (capabilities.supports_ultrafast_mode) "true" else "false",
},
);
if (persist) {
try persistPreferenceTargets(
app,
.{
.provider = provider_runtime.provider(app),
.model = provider_runtime.model(app),
.fast_mode = app.fast_mode,
.ultrafast_mode = app.worker.agent_turn_settings.ultrafast_mode,
},
"ultrafast",
!announce,
);
}
if (announce) {
try app.writeDomainNotice(.{
.topic = "ultrafast",
.tone = .neutral,
.body = if (app.worker.agent_turn_settings.ultrafast_mode) "requested on" else "requested off",
}, true);
if (comptime @hasDecl(App, "playInteractionSound")) app.playInteractionSound();
}
app.shell.render_requests.request(.footer);
}
fn applyEffort(app: *App, effort: types.ReasoningEffort, announce: bool, persist: bool) !void {
app.effort = effort;
app.worker.syncQueuedPromptEffort(app.effort);
@@ -776,8 +883,14 @@ pub fn Commands(comptime App: type) type {
try applyEffort(app, effort, false, true);
}
pub fn selectModelFromPicker(app: *App, model: []const u8, effort: types.ReasoningEffort, fast_mode: bool) !void {
try setResolvedModelRuntime(app, model, true);
pub fn selectModelFromPicker(
app: *App,
model: []const u8,
effort: types.ReasoningEffort,
fast_mode: bool,
ultrafast_mode: bool,
) !void {
try setResolvedModelRuntime(app, model);
var patch = app_session_runtime.SessionPreferencePatch{
.provider = provider_runtime.provider(app),
.model = model,
@@ -787,11 +900,17 @@ pub fn Commands(comptime App: type) type {
try applyEffort(app, effort, false, false);
patch.effort = effort;
}
const selected_fast_mode = capabilities.supports_fast_mode and fast_mode;
const selected_ultrafast_mode = capabilities.supports_ultrafast_mode and ultrafast_mode;
const selected_fast_mode = capabilities.supports_fast_mode and fast_mode and !selected_ultrafast_mode;
if (selected_fast_mode != app.fast_mode) {
try applyFastMode(app, selected_fast_mode, false, false);
}
patch.fast_mode = selected_fast_mode;
if (selected_ultrafast_mode != app.worker.agent_turn_settings.ultrafast_mode) {
try applyUltrafastMode(app, selected_ultrafast_mode, false, false);
}
patch.fast_mode = app.fast_mode;
patch.ultrafast_mode = app.worker.agent_turn_settings.ultrafast_mode;
try announceModelSelection(app);
try persistPreferenceTargets(app, patch, "model picker", false);
}
@@ -1048,16 +1167,21 @@ pub fn Commands(comptime App: type) type {
fn setResolvedModel(app: *App, resolved: []const u8, announce: bool) !void {
const model_changed = !std.mem.eql(u8, provider_runtime.model(app), resolved);
try setResolvedModelRuntime(app, resolved, announce);
try setResolvedModelRuntime(app, resolved);
if (model_changed and app.fast_mode) {
try applyFastMode(app, false, false, false);
}
if (model_changed and app.worker.agent_turn_settings.ultrafast_mode) {
try applyUltrafastMode(app, false, false, false);
}
if (announce) try announceModelSelection(app);
try persistPreferenceTargets(
app,
.{
.provider = provider_runtime.provider(app),
.model = resolved,
.fast_mode = app.fast_mode,
.ultrafast_mode = app.worker.agent_turn_settings.ultrafast_mode,
},
"model",
!announce,
@@ -1129,7 +1253,7 @@ pub fn Commands(comptime App: type) type {
}
}
fn setResolvedModelRuntime(app: *App, resolved: []const u8, announce: bool) !void {
fn setResolvedModelRuntime(app: *App, resolved: []const u8) !void {
if (!std.mem.eql(u8, provider_runtime.model(app), resolved)) {
try provider_runtime.replaceModel(app, resolved);
}
@@ -1137,18 +1261,22 @@ pub fn Commands(comptime App: type) type {
try app.worker.syncQueuedPromptModel(std.heap.c_allocator, selected);
if (comptime @hasDecl(App, "persistAcceptedModel")) try app.persistAcceptedModel(selected);
app_session_runtime.Runtime(App).syncTerminalTitle(app);
}
if (announce) {
const active_response = if (comptime @hasField(App, "stream"))
app.stream.active
else
false;
const prefix: []const u8 = if (active_response) "Next turn will use " else "Switched to ";
const line = try std.fmt.allocPrint(app.alloc, "{s}{s}", .{ prefix, selected });
defer app.alloc.free(line);
try app.writeDomainNotice(.{ .topic = "", .tone = .neutral, .body = line }, true);
if (comptime @hasDecl(App, "playInteractionSound")) app.playInteractionSound();
}
fn announceModelSelection(app: *App) !void {
const selected = provider_runtime.model(app);
const active_response = if (comptime @hasField(App, "stream")) app.stream.active else false;
const prefix: []const u8 = if (active_response) "Next turn will use " else "Switched to ";
const speed: []const u8 = if (app.worker.agent_turn_settings.ultrafast_mode)
"ultrafast"
else if (app.fast_mode or model_capabilities.resolveForApp(App, app, selected).intrinsic_fast)
"fast"
else
"normal";
const line = try std.fmt.allocPrint(app.alloc, "{s}{s} (effort: {s}, speed: {s})", .{ prefix, selected, app.effort.displayLabel(), speed });
defer app.alloc.free(line);
try app.writeDomainNotice(.{ .topic = "", .tone = .neutral, .body = line }, true);
if (comptime @hasDecl(App, "playInteractionSound")) app.playInteractionSound();
}
fn resolveModelQueryFromIds(alloc: std.mem.Allocator, ids: []const []u8, query: []const u8) !?[]u8 {
@@ -1576,6 +1704,11 @@ const FakeWorker = struct {
synced_model_alloc: ?std.mem.Allocator = null,
synced_fast_mode: ?bool = null,
fast_sync_count: usize = 0,
agent_turn_settings: struct {
ultrafast_mode: bool = false,
} = .{},
synced_ultrafast_mode: ?bool = null,
ultrafast_sync_count: usize = 0,
synced_effort: ?types.ReasoningEffort = null,
effort_sync_count: usize = 0,
@@ -1609,6 +1742,11 @@ const FakeWorker = struct {
self.fast_sync_count += 1;
}
fn syncQueuedPromptUltrafastMode(self: *FakeWorker, enabled: bool) void {
self.synced_ultrafast_mode = enabled;
self.ultrafast_sync_count += 1;
}
fn syncQueuedPromptEffort(self: *FakeWorker, effort: types.ReasoningEffort) void {
self.synced_effort = effort;
self.effort_sync_count += 1;
@@ -1679,6 +1817,7 @@ const FakeApp = struct {
last_preference_provider: ?model_provider.ProviderId = null,
last_preference_effort: ?types.ReasoningEffort = null,
last_preference_fast_mode: ?bool = null,
last_preference_ultrafast_mode: ?bool = null,
preference_settings_error: ?anyerror = null,
preference_session_error: ?anyerror = null,
preference_failure_cleanup: config_runtime.LegacyCleanup = .{},
@@ -1832,6 +1971,7 @@ const FakeApp = struct {
}
self.last_preference_effort = patch.effort;
self.last_preference_fast_mode = patch.fast_mode;
self.last_preference_ultrafast_mode = patch.ultrafast_mode;
if (self.preference_settings_error == null) {
const attempt = config_runtime.attemptUserPreferences(
self.alloc,
@@ -2659,6 +2799,17 @@ test "session_commands handleAllowlist recognizes tools from the active registry
try std.testing.expect(parseAllowlistTarget(.{}, "tool memory") == null);
}
test "session_commands rejects invalid ultrafast payloads without changing the requested state" {
const alloc = std.testing.allocator;
var app = try FakeApp.init(alloc, "/tmp/workspace", "openai/gpt-4o");
defer app.deinit();
try Commands(FakeApp).handleUltrafast(&app, "status on");
try std.testing.expect(!app.worker.agent_turn_settings.ultrafast_mode);
try expectTranscriptContains(&app, "usage: /ultrafast [on|off|status]");
}
test "session_commands toggleFast reports unsupported model and redraws footer" {
const alloc = std.testing.allocator;
var app = try FakeApp.init(alloc, "/tmp/workspace", "openai/gpt-4o");
@@ -2740,7 +2891,7 @@ test "session_commands selectModelFromPicker skips effort changes for models wit
defer app.deinit();
app.effort = types.ReasoningEffort.literal("high");
try Commands(FakeApp).selectModelFromPicker(&app, "openai/gpt-4o", types.ReasoningEffort.literal("low"), true);
try Commands(FakeApp).selectModelFromPicker(&app, "openai/gpt-4o", types.ReasoningEffort.literal("low"), true, false);
try std.testing.expectEqualStrings("openai/gpt-4o", app.selected_model.items);
try std.testing.expectEqualStrings("openai/gpt-4o", app.worker.synced_model.?);
@@ -2760,6 +2911,7 @@ test "session_commands model picker accepts the current selected model slice" {
app.selected_model.items,
types.ReasoningEffort.literal("high"),
false,
false,
);
try std.testing.expectEqualStrings("anthropic/claude-opus-4.6", app.selected_model.items);
@@ -2780,7 +2932,7 @@ test "session_commands selectModelFromPicker persists portable Gateway reasoning
const efforts = [_]types.ReasoningEffort{types.ReasoningEffort.literal("low")};
app.setGatewayControls("provider/new-reasoning-model", &efforts, false);
try Commands(FakeApp).selectModelFromPicker(&app, "provider/new-reasoning-model", types.ReasoningEffort.literal("low"), true);
try Commands(FakeApp).selectModelFromPicker(&app, "provider/new-reasoning-model", types.ReasoningEffort.literal("low"), true, false);
try std.testing.expectEqualStrings("provider/new-reasoning-model", app.selected_model.items);
try std.testing.expectEqualStrings("provider/new-reasoning-model", app.worker.synced_model.?);
@@ -2806,6 +2958,7 @@ test "session_commands model selection clears fast mode when the selected model
"zai/glm-5.3",
types.ReasoningEffort.literal("max"),
true,
false,
);
try std.testing.expectEqualStrings("zai/glm-5.3", app.selected_model.items);
@@ -2833,7 +2986,7 @@ test "session_commands selectModelFromPicker syncs queued fast mode and effort f
const efforts = [_]types.ReasoningEffort{types.ReasoningEffort.literal("high")};
app.setGatewayControls("anthropic/claude-opus-4.6", &efforts, true);
try Commands(FakeApp).selectModelFromPicker(&app, "anthropic/claude-opus-4.6", types.ReasoningEffort.literal("high"), true);
try Commands(FakeApp).selectModelFromPicker(&app, "anthropic/claude-opus-4.6", types.ReasoningEffort.literal("high"), true, false);
try std.testing.expect(app.fast_mode);
try std.testing.expectEqual(types.ReasoningEffort.literal("high"), app.effort);
@@ -2850,7 +3003,7 @@ test "session_commands model picker follows mock catalog controls independent of
const efforts = [_]types.ReasoningEffort{types.ReasoningEffort.literal("future-tier")};
app.setGatewayControls("provider/model", &efforts, true);
try Commands(FakeApp).selectModelFromPicker(&app, "provider/model", types.ReasoningEffort.literal("future-tier"), true);
try Commands(FakeApp).selectModelFromPicker(&app, "provider/model", types.ReasoningEffort.literal("future-tier"), true, false);
try std.testing.expect(app.fast_mode);
try std.testing.expectEqualStrings("future-tier", app.effort.label());
@@ -2858,6 +3011,31 @@ test "session_commands model picker follows mock catalog controls independent of
try std.testing.expectEqualStrings("future-tier", app.worker.synced_effort.?.label());
}
test "model switch notice reports the final chosen effort and Ultrafast speed" {
const alloc = std.testing.allocator;
var app = try FakeApp.init(alloc, "/tmp/workspace", "openai/gpt-6-astra");
defer app.deinit();
const efforts = [_]types.ReasoningEffort{types.ReasoningEffort.literal("xhigh")};
app.setGatewayControls("openai/gpt-6-astra", &efforts, true);
app.gateway_metadata.supports_ultrafast_mode = true;
try Commands(FakeApp).selectModelFromPicker(&app, "openai/gpt-6-astra", types.ReasoningEffort.literal("xhigh"), false, true);
try expectTranscriptContains(&app, "Switched to openai/gpt-6-astra (effort: xhigh, speed: ultrafast)");
try std.testing.expect(std.mem.find(u8, app.text(), "effort: default") == null);
try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, app.text(), "Switched to "));
try std.testing.expectEqual(@as(usize, 1), app.preference_commit_count);
}
test "plain model switch notice does not report the previous model's speed" {
const alloc = std.testing.allocator;
var app = try FakeApp.init(alloc, "/tmp/workspace", "openai/gpt-4o");
defer app.deinit();
app.worker.agent_turn_settings.ultrafast_mode = true;
app.fail_fetch = true;
try Commands(FakeApp).handleModel(&app, "openai/gpt-6-astra");
try expectTranscriptContains(&app, "Switched to openai/gpt-6-astra (effort: default, speed: normal)");
try std.testing.expect(!app.worker.agent_turn_settings.ultrafast_mode);
}
test "session_commands model picker emits one combined preference transaction" {
const alloc = std.testing.allocator;
var app = try FakeApp.init(
@@ -2875,6 +3053,7 @@ test "session_commands model picker emits one combined preference transaction" {
"anthropic/claude-opus-4.7",
types.ReasoningEffort.literal("high"),
true,
false,
);
try std.testing.expectEqual(@as(usize, 1), app.preference_commit_count);
@@ -3119,7 +3298,7 @@ test "session_commands model controls remain catalog validated and clear unsuppo
app.fast_mode = true;
app.worker.synced_fast_mode = true;
try Commands(FakeApp).selectModelFromPicker(&app, "openai/gpt-4o", types.ReasoningEffort.literal("low"), false);
try Commands(FakeApp).selectModelFromPicker(&app, "openai/gpt-4o", types.ReasoningEffort.literal("low"), false, false);
try std.testing.expect(!app.fast_mode);
try std.testing.expectEqual(@as(usize, 1), app.worker.fast_sync_count);
@@ -3139,7 +3318,7 @@ test "session_commands model controls remain catalog validated and clear unsuppo
.supports_fast_mode = true,
};
try Commands(FakeApp).selectModelFromPicker(&app, "anthropic/claude-opus-4.6", types.ReasoningEffort.literal("high"), true);
try Commands(FakeApp).selectModelFromPicker(&app, "anthropic/claude-opus-4.6", types.ReasoningEffort.literal("high"), true, false);
try std.testing.expectEqual(@as(?bool, true), app.worker.synced_fast_mode);
try std.testing.expectEqual(@as(usize, 2), app.worker.fast_sync_count);
+18 -4
View File
@@ -735,6 +735,7 @@ pub const PreferencesChanged = struct {
model: ?[]u8 = null,
effort: ?types.ReasoningEffort = null,
fast_mode: ?bool = null,
ultrafast_mode: ?bool = null,
fn deinit(self: *PreferencesChanged, alloc: Allocator) void {
if (self.model) |model| alloc.free(model);
@@ -1410,6 +1411,7 @@ fn applyDelta(
if (payload.model) |model| proposed.preferences.model = model;
if (payload.effort) |effort| proposed.preferences.effort = effort;
if (payload.fast_mode) |fast_mode| proposed.preferences.fast_mode = fast_mode;
if (payload.ultrafast_mode) |ultrafast_mode| proposed.preferences.ultrafast_mode = ultrafast_mode;
proposed.updated_at_ms = envelope.timestamp_ms;
try session_codec.validateState(proposed);
const model_copy = if (payload.model) |model|
@@ -1423,6 +1425,7 @@ fn applyDelta(
if (payload.provider) |provider| current.preferences.provider = provider;
if (payload.effort) |effort| current.preferences.effort = effort;
if (payload.fast_mode) |fast_mode| current.preferences.fast_mode = fast_mode;
if (payload.ultrafast_mode) |ultrafast_mode| current.preferences.ultrafast_mode = ultrafast_mode;
current.updated_at_ms = envelope.timestamp_ms;
},
.workspace_rebound => |payload| {
@@ -1527,7 +1530,7 @@ fn validateEnvelope(envelope: Envelope) !void {
try session_codec.validateState(state);
},
.preferences_changed => |payload| {
if (payload.provider == null and payload.model == null and payload.effort == null and payload.fast_mode == null) {
if (payload.provider == null and payload.model == null and payload.effort == null and payload.fast_mode == null and payload.ultrafast_mode == null) {
return error.InvalidEventFrame;
}
if (payload.model) |model| {
@@ -1647,6 +1650,11 @@ fn writePayload(writer: *std.Io.Writer, event: Event) !void {
if (payload.fast_mode) |fast_mode| {
if (wrote) try writer.writeByte(',');
try writer.print("\"fast_mode\":{s}", .{if (fast_mode) "true" else "false"});
wrote = true;
}
if (payload.ultrafast_mode) |ultrafast_mode| {
if (wrote) try writer.writeByte(',');
try writer.print("\"ultrafast_mode\":{s}", .{if (ultrafast_mode) "true" else "false"});
}
try writer.writeByte('}');
},
@@ -1775,8 +1783,8 @@ fn parsePayload(alloc: Allocator, kind: Kind, value: std.json.Value) !Event {
},
.preferences_changed => blk: {
const object = try requireObject(value);
if (object.count() == 0 or object.count() > 4) return error.InvalidEventFrame;
try rejectUnknownKeys(object, &.{ "provider", "model", "effort", "fast_mode" });
if (object.count() == 0 or object.count() > 5) return error.InvalidEventFrame;
try rejectUnknownKeys(object, &.{ "provider", "model", "effort", "fast_mode", "ultrafast_mode" });
const provider = if (object.get("provider")) |provider_value| provider_blk: {
break :provider_blk model_provider.parse_saved(provider_value) catch return error.InvalidEventFrame;
} else null;
@@ -1788,11 +1796,13 @@ fn parsePayload(alloc: Allocator, kind: Kind, value: std.json.Value) !Event {
else
null;
const fast_mode = if (object.get("fast_mode")) |_| try requireBool(object, "fast_mode") else null;
const ultrafast_mode = if (object.get("ultrafast_mode")) |_| try requireBool(object, "ultrafast_mode") else null;
break :blk .{ .preferences_changed = .{
.provider = provider,
.model = model,
.effort = effort,
.fast_mode = fast_mode,
.ultrafast_mode = ultrafast_mode,
} };
},
.workspace_rebound => blk: {
@@ -1966,9 +1976,13 @@ fn writePreferences(
try writeJsonString(writer, preferences.model);
try writer.writeAll(",\"effort\":");
try writeJsonString(writer, preferences.effort.label());
try writer.print(",\"fast_mode\":{s},\"provider\":", .{
try writer.print(",\"fast_mode\":{s}", .{
if (preferences.fast_mode) "true" else "false",
});
if (preferences.ultrafast_mode) {
try writer.writeAll(",\"ultrafast_mode\":true");
}
try writer.writeAll(",\"provider\":");
try std.json.Stringify.value(preferences.provider, .{}, writer);
try writer.writeByte('}');
}
+5
View File
@@ -693,6 +693,7 @@ fn encodeConversationMetadataWithTitle(
.model = state.preferences.model,
.effort = state.preferences.effort.label(),
.fast_mode = state.preferences.fast_mode,
.ultrafast_mode = if (state.preferences.ultrafast_mode) true else null,
.title = title,
.subagent_child = state.subagent_child,
});
@@ -1294,6 +1295,7 @@ fn load_conversation_state_at_boundary(
.model = model,
.effort = effort,
.fast_mode = metadata.value.fast_mode,
.ultrafast_mode = metadata.value.ultrafast_mode orelse false,
},
.history = history,
.context_history_start = latestConversationCheckpointIndex(history),
@@ -3318,6 +3320,7 @@ pub const LoadedWritableSession = struct {
.model = metadata.value.model,
.effort = metadata.value.effort,
.fast_mode = metadata.value.fast_mode,
.ultrafast_mode = metadata.value.ultrafast_mode,
.title = title,
.subagent_child = metadata.value.subagent_child,
});
@@ -3534,6 +3537,7 @@ pub const LoadedWritableSession = struct {
if (patch.provider) |provider| preferences.provider = provider;
if (patch.effort) |effort| preferences.effort = effort;
if (patch.fast_mode) |fast_mode| preferences.fast_mode = fast_mode;
if (patch.ultrafast_mode) |ultrafast_mode| preferences.ultrafast_mode = ultrafast_mode;
var proposed = self.state;
proposed.preferences = preferences;
proposed.updated_at_ms = timestamp_ms;
@@ -4492,6 +4496,7 @@ fn createNativeSession(
.model = initial_state.preferences.model,
.effort = initial_state.preferences.effort.label(),
.fast_mode = initial_state.preferences.fast_mode,
.ultrafast_mode = if (initial_state.preferences.ultrafast_mode) true else null,
.title = if (display.present) display.title else null,
.subagent_child = initial_state.subagent_child,
});
+1
View File
@@ -356,6 +356,7 @@ fn loadMigrationPreferences(
),
.effort = detailed.settings.effort orelse .auto,
.fast_mode = detailed.settings.fast_mode orelse false,
.ultrafast_mode = detailed.settings.ultrafast_mode orelse false,
};
}
+7 -2
View File
@@ -262,7 +262,8 @@ fn durablePreferencesEqual(
return std.mem.eql(u8, left.model, right.model) and
left.provider.same_authority(right.provider) and
left.effort.eql(right.effort) and
left.fast_mode == right.fast_mode;
left.fast_mode == right.fast_mode and
left.ultrafast_mode == right.ultrafast_mode;
}
pub fn encodeCheckpoint(alloc: Allocator, checkpoint: Checkpoint) ![]u8 {
@@ -434,9 +435,13 @@ fn writePreferences(
try writeJsonString(writer, preferences.model);
try writer.writeAll(",\"effort\":");
try writeJsonString(writer, preferences.effort.label());
try writer.print(",\"fast_mode\":{s},\"provider\":", .{
try writer.print(",\"fast_mode\":{s}", .{
if (preferences.fast_mode) "true" else "false",
});
if (preferences.ultrafast_mode) {
try writer.writeAll(",\"ultrafast_mode\":true");
}
try writer.writeAll(",\"provider\":");
try std.json.Stringify.value(preferences.provider, .{}, writer);
try writer.writeByte('}');
}
+2
View File
@@ -1708,6 +1708,7 @@ pub const Store = struct {
.effort = core_types.ReasoningEffort.parse(metadata.value.effort) orelse
return error.InvalidSessionMetadata,
.fast_mode = metadata.value.fast_mode,
.ultrafast_mode = metadata.value.ultrafast_mode orelse false,
};
}
@@ -1754,6 +1755,7 @@ pub const Store = struct {
),
.effort = detailed.settings.effort orelse .auto,
.fast_mode = detailed.settings.fast_mode orelse false,
.ultrafast_mode = detailed.settings.ultrafast_mode orelse false,
};
}
+24
View File
@@ -2189,6 +2189,30 @@ const session_tests = struct {
try testing.expect(snapshots >= 5);
}
test "snapshot resume preserves ultrafast preference payload" {
var t: TestEnv = undefined;
t.init(.{ .snapshot_every_bytes = 1 });
defer t.deinit();
const s = try newRoot(&t);
_ = try s.append(&.{
.turn_started,
.{ .set = .{ .key = .prefs, .value = "{\"model\":\"grok/default\",\"fast_mode\":false,\"ultrafast_mode\":true}" } },
.turn_committed,
});
const id = try gpa.dupe(u8, s.id());
defer gpa.free(id);
crash(&t, s);
const resumed = try resumeRoot(&t, id);
defer closeAndDestroy(resumed) catch {};
var state = try resumed.stateCopy(gpa);
defer state.deinit(gpa);
try testing.expectEqualStrings(
"{\"model\":\"grok/default\",\"fast_mode\":false,\"ultrafast_mode\":true}",
state.prefs.?,
);
}
test "a compaction is followed by a snapshot" {
var t: TestEnv = undefined;
t.init(.{});
+35
View File
@@ -1658,6 +1658,39 @@ pub const ProviderFailureCause = enum {
rate_limited,
};
pub const ProviderServiceTier = enum {
standard,
flex,
priority,
ultrafast,
pub fn parse(raw: []const u8) ?ProviderServiceTier {
if (std.mem.eql(u8, raw, "default") or std.mem.eql(u8, raw, "standard")) return .standard;
if (std.mem.eql(u8, raw, "priority") or std.mem.eql(u8, raw, "fast")) return .priority;
if (std.mem.eql(u8, raw, "flex")) return .flex;
if (std.mem.eql(u8, raw, "ultrafast")) return .ultrafast;
return null;
}
};
test "Provider service tier parser recognizes Gateway tiers" {
const cases = [_]struct {
raw: []const u8,
expected: ?ProviderServiceTier,
}{
.{ .raw = "default", .expected = .standard },
.{ .raw = "standard", .expected = .standard },
.{ .raw = "priority", .expected = .priority },
.{ .raw = "fast", .expected = .priority },
.{ .raw = "flex", .expected = .flex },
.{ .raw = "ultrafast", .expected = .ultrafast },
.{ .raw = "unknown", .expected = null },
};
for (cases) |case| {
try std.testing.expectEqual(case.expected, ProviderServiceTier.parse(case.raw));
}
}
pub const ModelCompletion = struct {
content: ?[]const u8 = null,
tool_calls: []const ToolCall = &.{},
@@ -1667,6 +1700,8 @@ pub const ModelCompletion = struct {
/// provider did not report routing metadata.
resolved_provider: ?[]const u8 = null,
billing: ?ProviderBilling = null,
/// Gateway-applied service tier. Null when the provider did not confirm it.
service_tier: ?ProviderServiceTier = null,
/// Gateway generation or resolved-model metadata was malformed or conflicting.
generation_metadata_invalid: bool = false,
/// An earlier delivery may have billed outside this generation identity.
@@ -35,6 +35,7 @@ pub const ParsedCommand = union(enum) {
credits,
paste,
fast,
ultrafast: []const u8,
statusline: []const u8,
notifications: []const u8,
workspace: []const u8,
@@ -73,6 +74,7 @@ pub const CommandHandlers = struct {
show_credits: *const fn (ctx: *anyopaque) anyerror!void,
paste_clipboard: *const fn (ctx: *anyopaque) anyerror!void,
toggle_fast: *const fn (ctx: *anyopaque) anyerror!void,
handle_ultrafast: *const fn (ctx: *anyopaque, rest: []const u8) anyerror!void,
handle_statusline: *const fn (ctx: *anyopaque, rest: []const u8) anyerror!void,
rename_session: *const fn (ctx: *anyopaque, rest: []const u8) anyerror!void,
handle_notifications: *const fn (ctx: *anyopaque, rest: []const u8) anyerror!void,
@@ -117,6 +119,7 @@ fn parsedCommand(kind: SlashKind, payload: []const u8) ParsedCommand {
.credits => .credits,
.paste => .paste,
.fast => .fast,
.ultrafast => .{ .ultrafast = payload },
.statusline => .{ .statusline = payload },
.notifications => .{ .notifications = payload },
.workspace => .{ .workspace = payload },
@@ -169,6 +172,7 @@ pub fn route(registry: SlashRegistry, handlers: *const CommandHandlers, cmd: []c
.credits => try handlers.show_credits(handlers.ctx),
.paste => try handlers.paste_clipboard(handlers.ctx),
.fast => try handlers.toggle_fast(handlers.ctx),
.ultrafast => |rest| try handlers.handle_ultrafast(handlers.ctx, rest),
.statusline => |rest| try handlers.handle_statusline(handlers.ctx, rest),
.notifications => |rest| try handlers.handle_notifications(handlers.ctx, rest),
.workspace => |rest| try handlers.handle_workspace(handlers.ctx, rest),
@@ -283,6 +287,17 @@ test "parse recognizes exact no-payload commands" {
try std.testing.expectEqual(ParsedCommand.version, parse(testSlashRegistry(), "/version"));
}
test "parse extracts ultrafast command payload" {
switch (parse(testSlashRegistry(), "/ultrafast")) {
.ultrafast => |rest| try std.testing.expectEqualStrings("", rest),
else => return error.TestExpectedEqual,
}
switch (parse(testSlashRegistry(), "/ultrafast status")) {
.ultrafast => |rest| try std.testing.expectEqualStrings("status", rest),
else => return error.TestExpectedEqual,
}
}
test "parse extracts settings command payload" {
switch (parse(testSlashRegistry(), "/settings")) {
.settings => |rest| try std.testing.expectEqualStrings("", rest),
@@ -476,6 +491,7 @@ fn testHandlers(ctx: *TestContext) CommandHandlers {
.show_credits = unexpectedNoPayload,
.paste_clipboard = unexpectedNoPayload,
.toggle_fast = unexpectedNoPayload,
.handle_ultrafast = unexpectedPayload,
.handle_statusline = unexpectedPayload,
.rename_session = unexpectedPayload,
.handle_notifications = unexpectedPayload,
@@ -65,6 +65,7 @@ pub const SlashKind = enum {
credits,
paste,
fast,
ultrafast,
statusline,
notifications,
workspace,
+16
View File
@@ -37,6 +37,18 @@ const tool_host = @import("tool_host.zig");
const Allocator = std.mem.Allocator;
// A parent disable caps resumed children too; durable child opt-ins cannot bypass it.
fn childUltrafastMode(parent_requested: bool, child_preference: bool) bool {
return parent_requested and child_preference;
}
test "Ultrafast parent disable caps durable child opt-ins" {
try std.testing.expect(childUltrafastMode(true, true));
try std.testing.expect(!childUltrafastMode(false, true));
try std.testing.expect(!childUltrafastMode(true, false));
try std.testing.expect(!childUltrafastMode(false, false));
}
fn childModelCapabilityResolver(
parent: ?model_capabilities.Resolver,
) ?model_capabilities.Resolver {
@@ -170,8 +182,10 @@ pub fn run(
routed_config.tool_context.credential_source = credential.source;
routed_config.tool_context.account_id = credential.accountId();
}
const ultrafast_mode = childUltrafastMode(config.tool_context.ultrafast_mode, admission.ultrafast_mode);
routed_config.tool_context.model = admission.model;
routed_config.tool_context.provider = admission.provider;
routed_config.tool_context.ultrafast_mode = ultrafast_mode;
routed_config.tool_context.provider_capabilities = config.provider_set.select(admission.provider).capabilities;
debug_trace.logf(
"subagent",
@@ -237,6 +251,7 @@ pub fn run(
.max_tool_result_bytes = config.tool_context.max_tool_result_bytes,
.first_call_tool_choice = config.tool_context.first_call_tool_choice,
.fast_mode = config.tool_context.fast_mode,
.ultrafast_mode = ultrafast_mode,
.effort = admission.effort,
.provider_order = if (admission.provider == .gateway) config.tool_context.provider_order else &.{},
.provider_strict = admission.provider == .gateway and config.tool_context.provider_strict,
@@ -300,6 +315,7 @@ pub fn run(
.max_tool_result_bytes = config.tool_context.max_tool_result_bytes,
.cancel_flag = cancel,
.fast_mode = config.tool_context.fast_mode,
.ultrafast_mode = ultrafast_mode,
.effort = admission.effort,
.provider_order = if (admission.provider == .gateway) config.tool_context.provider_order else &.{},
.provider_strict = admission.provider == .gateway and config.tool_context.provider_strict,
+3
View File
@@ -47,6 +47,7 @@ pub const AdmissionSnapshot = struct {
model: []u8,
provider: model_provider.ProviderId = .gateway,
effort: types.ReasoningEffort,
ultrafast_mode: bool = false,
permission_mode: types.PermissionMode = .yolo,
tool_names: [][]u8,
rules: types.PermissionRuleSet,
@@ -76,6 +77,7 @@ pub const AdmissionInput = struct {
model: []const u8,
provider: model_provider.ProviderId = .gateway,
effort: types.ReasoningEffort,
ultrafast_mode: bool = false,
permission_mode: types.PermissionMode = .yolo,
tool_names: []const []const u8 = &.{},
rules: types.PermissionRuleSet = .{},
@@ -153,6 +155,7 @@ pub fn captureAdmission(
.model = model,
.provider = input.provider,
.effort = input.effort,
.ultrafast_mode = input.ultrafast_mode,
.permission_mode = input.permission_mode,
.tool_names = tool_names,
.rules = rules,
+3
View File
@@ -156,6 +156,9 @@ pub const TurnPreferences = struct {
provider: @import("../config/model_provider.zig").ProviderId = .gateway,
model: []const u8,
effort: types.ReasoningEffort,
/// The durable child preference wins when work resumes. Parent defaults are
/// used only while creating a new child session.
ultrafast_mode: bool = false,
};
pub const CaptureRequest = struct {
+30 -5
View File
@@ -583,11 +583,7 @@ fn runOne(slot: *Slot) OneOutcome {
.child_id = slot.child_id,
.parent_id = owner.state_store.parent_id,
.source_id = owner.state_store.parent_id,
.preferences = .{
.provider = child.preferences().provider,
.model = child.preferences().model,
.effort = child.preferences().effort,
},
.preferences = turnPreferencesForDurable(child.preferences()),
}) catch |err| return if (err == error.Cancelled) .{
.work_id = work_id,
.outcome = .cancelled,
@@ -831,6 +827,35 @@ fn loadRunSnapshot(owner: *Owner, child_id: []const u8) !RunSnapshot {
};
}
/// Borrows the child's durable preferences; a persisted disable stays disabled.
fn turnPreferencesForDurable(
preferences: session_codec.DurableSessionPreferences,
) execution.TurnPreferences {
return .{
.provider = preferences.provider,
.model = preferences.model,
.effort = preferences.effort,
.ultrafast_mode = preferences.ultrafast_mode,
};
}
test "resumed child turn preferences preserve explicit ultrafast disable" {
const enabled = turnPreferencesForDurable(.{
.model = @constCast("child"),
.effort = .auto,
.fast_mode = false,
.ultrafast_mode = true,
});
const disabled = turnPreferencesForDurable(.{
.model = @constCast("child"),
.effort = .auto,
.fast_mode = false,
.ultrafast_mode = false,
});
try std.testing.expect(enabled.ultrafast_mode);
try std.testing.expect(!disabled.ultrafast_mode);
}
fn fallbackOutcome(alloc: Allocator, work_id: []const u8, outcome: child_state.Outcome) OneOutcome {
return .{
.work_id = alloc.dupe(u8, work_id) catch &.{},
+8 -3
View File
@@ -35,6 +35,7 @@ pub const Defaults = struct {
model: []const u8,
effort: types.ReasoningEffort,
fast_mode: bool = false,
ultrafast_mode: bool = false,
conversation_language: session.ConversationLanguage,
};
@@ -709,13 +710,14 @@ pub const Runtime = struct {
.model = @constCast(defaults.model),
.effort = defaults.effort,
.fast_mode = defaults.fast_mode,
.ultrafast_mode = defaults.ultrafast_mode,
},
.language = defaults.conversation_language,
});
debug_trace.logf(
"subagent",
"child session reserved child_id={s} work_id={s} provider={s} model={s} effort={s} fast_mode={}",
.{ child_id, work_id, @tagName(defaults.provider), defaults.model, defaults.effort.label(), defaults.fast_mode },
"child session reserved child_id={s} work_id={s} provider={s} model={s} effort={s} fast_mode={} ultrafast_mode={}",
.{ child_id, work_id, @tagName(defaults.provider), defaults.model, defaults.effort.label(), defaults.fast_mode, defaults.ultrafast_mode },
);
return;
},
@@ -735,7 +737,7 @@ pub const Runtime = struct {
writable.deinit(alloc);
debug_trace.logf(
"subagent",
"child session created child_id={s} work_id={s} provider={s} model={s} effort={s} fast_mode={}",
"child session created child_id={s} work_id={s} provider={s} model={s} effort={s} fast_mode={} ultrafast_mode={}",
.{
child_id,
work_id,
@@ -743,6 +745,7 @@ pub const Runtime = struct {
state.preferences.model,
state.preferences.effort.label(),
state.preferences.fast_mode,
state.preferences.ultrafast_mode,
},
);
} else |err| switch (err) {
@@ -2143,6 +2146,7 @@ fn freshChildState(
.model = model,
.effort = defaults.effort,
.fast_mode = defaults.fast_mode,
.ultrafast_mode = defaults.ultrafast_mode,
},
.history = try alloc.alloc(types.HistoryTurn, 0),
.total_input_tokens = 0,
@@ -2167,6 +2171,7 @@ fn captureAdmission(
.model = request.preferences.model,
.provider = request.preferences.provider,
.effort = request.preferences.effort,
.ultrafast_mode = request.preferences.ultrafast_mode,
.permission_mode = snapshot.permission_mode,
.tool_names = snapshot.tools,
.rules = snapshot.rules,
+2
View File
@@ -145,6 +145,7 @@ pub const Context = struct {
gateway_models_path: []const u8 = "/v1/models",
agent_step_limit: usize,
fast_mode: bool = false,
ultrafast_mode: bool = false,
effort: types.ReasoningEffort = .auto,
/// Borrowed gateway provider routing inherited by subagent turns. Never
/// applied to tool-internal provider requests (vision, web search), which
@@ -2183,6 +2184,7 @@ fn executeSubagentProvider(
.model = ctx.model,
.effort = ctx.effort,
.fast_mode = ctx.fast_mode,
.ultrafast_mode = ctx.ultrafast_mode,
.conversation_language = ctx.session.languageSnapshot(),
},
.max_result_bytes = ctx.max_tool_result_bytes,
+65
View File
@@ -3448,6 +3448,20 @@ fn parseSseResolvedProvider(alloc: std.mem.Allocator, root: std.json.Value) ?[]c
return alloc.dupe(u8, provider_value.string) catch null;
}
/// Extracts the Gateway-confirmed applied service tier from terminal metadata.
/// Request options and provider-specific metadata are intentionally ignored.
fn parseSseServiceTier(root: std.json.Value) ?types.ProviderServiceTier {
if (root != .object) return null;
const provider_metadata = root.object.get("providerMetadata") orelse
root.object.get("provider_metadata") orelse return null;
if (provider_metadata != .object) return null;
const gateway = provider_metadata.object.get("gateway") orelse return null;
if (gateway != .object) return null;
const service_tier = gateway.object.get("serviceTier") orelse return null;
if (service_tier != .string) return null;
return types.ProviderServiceTier.parse(service_tier.string);
}
fn captureGenerationMetadata(
alloc: std.mem.Allocator,
root: std.json.Value,
@@ -3559,6 +3573,7 @@ fn consumeSseStreamTraced(
defer if (finish_resolved_provider) |provider| alloc.free(@constCast(provider));
var finish_billing: ?types.ProviderBilling = null;
defer if (finish_billing) |billing| alloc.free(@constCast(billing.model));
var finish_service_tier: ?types.ProviderServiceTier = null;
var generation_id: ?[]u8 = null;
defer if (generation_id) |id| alloc.free(id);
var resolved_model: ?[]u8 = null;
@@ -3998,6 +4013,7 @@ fn consumeSseStreamTraced(
finish_reason_holder = finish_event.finish_reason;
finish_usage = finish_event.usage;
finish_resolved_provider = parseSseResolvedProvider(alloc, root);
finish_service_tier = parseSseServiceTier(root);
finish_billing = parseSseBilling(
alloc,
root,
@@ -4041,6 +4057,7 @@ fn consumeSseStreamTraced(
finish_resolved_provider = null;
completion.billing = finish_billing;
finish_billing = null;
completion.service_tier = finish_service_tier;
completion.generation_metadata_invalid = generation_metadata_invalid;
completion.finish_reason = finish_reason_holder;
completion.usage = finish_usage;
@@ -4258,6 +4275,54 @@ test "consumeSseStream captures the resolved routing provider" {
try std.testing.expect(second_completion.resolved_provider == null);
}
test "consumeSseStream captures Gateway-applied service tiers" {
const alloc = std.testing.allocator;
const cases = [_]struct {
payload: []const u8,
expected: types.ProviderServiceTier,
}{
.{
.payload = "data: {\"type\":\"finish\",\"finishReason\":{\"unified\":\"stop\"},\"providerMetadata\":{\"gateway\":{\"serviceTier\":\"ultrafast\"}}}\n\n",
.expected = .ultrafast,
},
.{
.payload = "data: {\"type\":\"finish\",\"finishReason\":{\"unified\":\"stop\"},\"provider_metadata\":{\"gateway\":{\"serviceTier\":\"priority\"}}}\n\n",
.expected = .priority,
},
};
const Noop = struct {
fn chunk(_: *anyopaque, _: []const u8) void {}
};
for (cases) |case| {
var reader = std.Io.Reader.fixed(case.payload);
var cancel_flag = std.atomic.Value(bool).init(false);
var completion = try consumeSseStream(alloc, &reader, undefined, Noop.chunk, null, &cancel_flag);
defer deinitGatewayCompletion(alloc, &completion);
try std.testing.expectEqual(case.expected, completion.service_tier.?);
}
}
test "consumeSseStream leaves missing and unknown service tiers unconfirmed" {
const alloc = std.testing.allocator;
const payloads = [_][]const u8{
"data: {\"type\":\"finish\",\"finishReason\":{\"unified\":\"stop\"}}\n\n",
"data: {\"type\":\"finish\",\"finishReason\":{\"unified\":\"stop\"},\"providerMetadata\":{\"gateway\":{\"serviceTier\":\"unsupported\"}}}\n\n",
"data: {\"type\":\"finish\",\"finishReason\":{\"unified\":\"stop\"},\"providerMetadata\":{\"openai\":{\"serviceTier\":\"ultrafast\"}}}\n\n",
};
const Noop = struct {
fn chunk(_: *anyopaque, _: []const u8) void {}
};
for (payloads) |payload| {
var reader = std.Io.Reader.fixed(payload);
var cancel_flag = std.atomic.Value(bool).init(false);
var completion = try consumeSseStream(alloc, &reader, undefined, Noop.chunk, null, &cancel_flag);
defer deinitGatewayCompletion(alloc, &completion);
try std.testing.expect(completion.service_tier == null);
}
}
test "consumeSseStream captures exact terminal billing" {
const payload =
"data: {\"type\":\"response-metadata\",\"modelId\":\"provider/resolved\",\"timestamp\":\"2026-07-29T03:31:07.000Z\"}\n\n" ++
+26 -3
View File
@@ -452,14 +452,21 @@ fn validatePendingToolReviewMessages(
pub fn writeProviderOptions(writer: *std.Io.Writer, options: model_capabilities.ResolvedProviderOptions) !void {
const routing = options.provider_order.len > 0;
const gateway_options = options.fast or options.prompt_caching or routing;
if (options.ultrafast and options.provider_strict and routing) {
var allows_openai = false;
for (options.provider_order) |provider| {
if (std.mem.eql(u8, provider, "openai")) allows_openai = true;
}
if (!allows_openai) return error.UltrafastProviderRestricted;
}
const gateway_options = options.fast or options.ultrafast or options.prompt_caching or routing;
if (!gateway_options and options.parallel_tool_calls == null) return;
try writer.writeAll(",\"providerOptions\":{");
if (gateway_options) {
try writer.writeAll("\"gateway\":{");
var needs_comma = false;
if (options.fast) {
if (options.fast and !options.ultrafast) {
try writer.writeAll("\"speed\":\"fast\"");
needs_comma = true;
}
@@ -468,7 +475,10 @@ pub fn writeProviderOptions(writer: *std.Io.Writer, options: model_capabilities.
try writer.writeAll("\"caching\":\"auto\"");
needs_comma = true;
}
if (routing) {
if (options.ultrafast) {
if (needs_comma) try writer.writeByte(',');
try writer.writeAll("\"only\":[\"openai\"]");
} else if (routing) {
if (needs_comma) try writer.writeByte(',');
try writer.writeAll(if (options.provider_strict) "\"only\":[" else "\"order\":[");
for (options.provider_order, 0..) |slug, index| {
@@ -479,6 +489,9 @@ pub fn writeProviderOptions(writer: *std.Io.Writer, options: model_capabilities.
}
try writer.writeByte('}');
}
if (options.ultrafast) {
try writer.writeAll(",\"openai\":{\"serviceTier\":\"ultrafast\"}");
}
if (options.parallel_tool_calls) |parallel_tool_calls| {
if (gateway_options) try writer.writeByte(',');
try writer.writeAll("\"xai\":{\"parallelToolCalls\":");
@@ -488,6 +501,16 @@ pub fn writeProviderOptions(writer: *std.Io.Writer, options: model_capabilities.
try writer.writeByte('}');
}
test "Ultrafast serializes only the OpenAI tier and strict OpenAI routing" {
var out: std.Io.Writer.Allocating = .init(std.testing.allocator);
defer out.deinit();
try writeProviderOptions(&out.writer, .{ .ultrafast = true, .fast = true, .prompt_caching = true });
try std.testing.expect(std.mem.find(u8, out.written(), "\"openai\":{\"serviceTier\":\"ultrafast\"}") != null);
try std.testing.expect(std.mem.find(u8, out.written(), "\"only\":[\"openai\"]") != null);
try std.testing.expect(std.mem.find(u8, out.written(), "\"speed\"") == null);
try std.testing.expectError(error.UltrafastProviderRestricted, writeProviderOptions(&out.writer, .{ .ultrafast = true, .provider_strict = true, .provider_order = &.{"azure"} }));
}
pub fn validateToolMessageHistory(alloc: std.mem.Allocator, messages: []const ChatMessage) !void {
var i: usize = 0;
while (i < messages.len) {
+1
View File
@@ -686,6 +686,7 @@ const App = struct {
.model = launch.modifiers.model_override,
.effort = launch.modifiers.effort_override,
.fast = launch.modifiers.fast_override,
.ultrafast = launch.modifiers.ultrafast_override,
.provider_order = launch.modifiers.provider_order_override,
.provider_strict = launch.modifiers.provider_strict_override,
},
+12
View File
@@ -486,6 +486,7 @@ const Runtime = struct {
model: ?[]u8,
effort: ?[]u8,
fast: ?bool,
ultrafast: ?bool,
home: []u8,
workspace_root: []u8,
gateway_chat_url: []u8,
@@ -550,6 +551,7 @@ const Runtime = struct {
.model_override = self.model,
.effort_override = self.effort,
.fast_override = self.fast,
.ultrafast_override = self.ultrafast,
.home_override = self.home,
.workspace_root_override = self.workspace_root,
.allow_acp_mcp = false,
@@ -744,6 +746,7 @@ const CreateError = error{
InvalidModel,
InvalidEffort,
InvalidFast,
InvalidUltrafast,
InvalidHome,
InvalidWorkspaceRoot,
InvalidGatewayUrl,
@@ -779,6 +782,10 @@ fn createRuntime(env: c.napi_env, options: c.napi_value) CreateError!*Runtime {
error.JavaScriptException => return error.JavaScriptException,
else => return error.InvalidFast,
};
const ultrafast = getNamedBool(env, options, "ultrafast") catch |err| switch (err) {
error.JavaScriptException => return error.JavaScriptException,
else => return error.InvalidUltrafast,
};
const home = (getNamedString(env, options, "home", alloc, max_path_bytes) catch |err| switch (err) {
error.JavaScriptException => return error.JavaScriptException,
error.OutOfMemory => return error.OutOfMemory,
@@ -813,6 +820,7 @@ fn createRuntime(env: c.napi_env, options: c.napi_value) CreateError!*Runtime {
.model = model,
.effort = effort,
.fast = fast,
.ultrafast = ultrafast,
.home = home,
.workspace_root = workspace_root,
.gateway_chat_url = gateway_chat_url,
@@ -840,6 +848,7 @@ fn throwCreateError(env: c.napi_env, err: CreateError) c.napi_value {
error.InvalidModel => throw(env, "LIBFX_INVALID_ARGUMENT", "model must be a bounded string"),
error.InvalidEffort => throw(env, "LIBFX_INVALID_ARGUMENT", "effort must be a bounded string"),
error.InvalidFast => throw(env, "LIBFX_INVALID_ARGUMENT", "fast must be a boolean"),
error.InvalidUltrafast => throw(env, "LIBFX_INVALID_ARGUMENT", "ultrafast must be a boolean"),
error.InvalidHome => throw(env, "LIBFX_INVALID_ARGUMENT", "home is required and must be a bounded string"),
error.InvalidWorkspaceRoot => throw(env, "LIBFX_INVALID_ARGUMENT", "workspaceRoot is required and must be a bounded string"),
error.InvalidGatewayUrl => throw(env, "LIBFX_INVALID_ARGUMENT", "gatewayChatUrl must be a bounded string"),
@@ -1138,6 +1147,9 @@ export fn napi_register_module_v1(env: c.napi_env, exports: c.napi_value) callco
var api_version: c.napi_value = undefined;
if (!statusOk(env, c.napi_create_uint32(env, 3, &api_version), "could not create API version")) return null;
if (!statusOk(env, c.napi_set_named_property(env, exports, "libfxApiVersion", api_version), "could not export API version")) return null;
var supports_ultrafast: c.napi_value = undefined;
if (!statusOk(env, c.napi_get_boolean(env, true, &supports_ultrafast), "could not create ultrafast capability")) return null;
if (!statusOk(env, c.napi_set_named_property(env, exports, "supportsUltrafast", supports_ultrafast), "could not export ultrafast capability")) return null;
if (!exportFunction(env, exports, "createCore", createCore)) return null;
if (!exportFunction(env, exports, "takeCoreReadyFd", takeCoreReadyFd)) return null;
if (!exportFunction(env, exports, "writeCore", writeCore)) return null;
+17
View File
@@ -1613,6 +1613,23 @@ test "compose hint row prioritizes the armed interrupt hint and shrinks it on na
try std.testing.expect(std.mem.find(u8, clear_only.items, "esc again to clear") != null);
}
test "compose hint row carries the Ultrafast marker projection" {
var input = InputRuntime{};
defer input.deinit(std.testing.allocator);
const ctx: RenderContext = .{
.stream = .{},
.has_api_key = true,
.model = "openai/gpt-6-astra",
.effort = types.ReasoningEffort.literal("xhigh"),
.model_supports_effort = true,
.statusline = .{ .ultrafast_indicator_active = true },
.input = &input,
};
var row = try composeHintRow(std.testing.allocator, false, ctx, 80);
defer row.deinit(std.testing.allocator);
try std.testing.expect(std.mem.find(u8, row.items, "gpt-6-astra · xhigh · ⚡︎⚡︎⚡︎") != null);
}
test "compose hint row keeps model in left hint text" {
var input = InputRuntime{};
defer input.deinit(std.testing.allocator);
+7 -1
View File
@@ -340,6 +340,7 @@ fn appendCompactFacts(
if (capabilities.context_window) |tokens| try appendTokenFact(alloc, facts, tokens, "context");
if (capabilities.max_output_tokens) |tokens| try appendTokenFact(alloc, facts, tokens, "output");
if (capabilities.supports_fast_mode) try appendMetadataFact(alloc, facts, "Fast");
if (capabilities.supports_ultrafast_mode) try appendMetadataFact(alloc, facts, "Ultra");
}
fn compactFactsWidth(capabilities: model_capabilities.Capabilities) usize {
@@ -356,6 +357,10 @@ fn compactFactsWidth(capabilities: model_capabilities.Capabilities) usize {
if (width > 0) width += display_width.visibleWidth(" · ");
width += display_width.visibleWidth("Fast");
}
if (capabilities.supports_ultrafast_mode) {
if (width > 0) width += display_width.visibleWidth(" · ");
width += display_width.visibleWidth("Ultra");
}
return width;
}
@@ -467,6 +472,7 @@ test "model menu renders provider tabs and compact model facts" {
.supports_file_input = true,
.supports_web_search = true,
.supports_fast_mode = true,
.supports_ultrafast_mode = true,
.context_window = 1_000_000,
.max_output_tokens = 128_000,
},
@@ -496,7 +502,7 @@ test "model menu renders provider tabs and compact model facts" {
try std.testing.expect(std.mem.find(u8, title.items, "anthropic/claude-opus-4.8") != null);
try std.testing.expect(std.mem.find(u8, title.items, "●") == null);
try std.testing.expect(std.mem.find(u8, title.items, "○") == null);
try std.testing.expect(std.mem.find(u8, title.items, "1M context · 128K output · Fast") != null);
try std.testing.expect(std.mem.find(u8, title.items, "1M context · 128K output · Fast · Ultra") != null);
try std.testing.expect(std.mem.find(u8, title.items, "Anthropic") == null);
try std.testing.expect(std.mem.find(u8, title.items, "Current") == null);
try std.testing.expect(std.mem.find(u8, title.items, "Reasoning") == null);
+5 -5
View File
@@ -361,7 +361,7 @@ test "settings menu renders each setting on one row at wide and narrow widths" {
.snapshot = test_snapshot,
};
const wide_rows = menuRowCount(projection, 100, 40);
try std.testing.expectEqual(@as(u16, 15), wide_rows);
try std.testing.expectEqual(@as(u16, 16), wide_rows);
var header = try composeSettingsMenuRow(alloc, projection, 0, 100, wide_rows);
defer header.deinit(alloc);
@@ -382,7 +382,7 @@ test "settings menu renders each setting on one row at wide and narrow widths" {
try std.testing.expect(std.mem.find(u8, compact_item.items, "on") != null);
const narrow_rows = menuRowCount(projection, 24, 40);
try std.testing.expectEqual(@as(u16, 15), narrow_rows);
try std.testing.expectEqual(@as(u16, 16), narrow_rows);
var narrow_item = try composeSettingsMenuRow(alloc, projection, 2, 24, narrow_rows);
defer narrow_item.deinit(alloc);
try std.testing.expect(std.mem.find(u8, narrow_item.items, "Status line") != null);
@@ -468,7 +468,7 @@ test "settings menu renders category tabs and a flat full list through the VT" {
.active = true,
.snapshot = test_snapshot,
};
const row_budget: u16 = 15;
const row_budget: u16 = 16;
const rows = menuRowCount(projection, width, row_budget);
try std.testing.expectEqual(row_budget, rows);
@@ -487,7 +487,7 @@ test "settings menu renders category tabs and a flat full list through the VT" {
var text: std.ArrayList(u8) = .empty;
defer text.deinit(alloc);
try grid.rowTextTrimmed(1, &text);
try std.testing.expect(std.mem.find(u8, text.items, "Settings 13") != null);
try std.testing.expect(std.mem.find(u8, text.items, "Settings 14") != null);
try std.testing.expect(std.mem.find(u8, text.items, "[All]") != null);
try std.testing.expect(std.mem.find(u8, text.items, "Interface") != null);
text.clearRetainingCapacity();
@@ -495,6 +495,6 @@ test "settings menu renders category tabs and a flat full list through the VT" {
try std.testing.expect(std.mem.find(u8, text.items, "Status line context") != null);
try std.testing.expect(std.mem.find(u8, text.items, "Interface") == null);
text.clearRetainingCapacity();
try grid.rowTextTrimmed(15, &text);
try grid.rowTextTrimmed(16, &text);
try std.testing.expect(std.mem.find(u8, text.items, "Prompt history") != null);
}
+8 -8
View File
@@ -885,7 +885,7 @@ test "model picker flow stores pending model and selections" {
var runtime = InputRuntime{};
defer runtime.deinit(std.testing.allocator);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "openai/gpt-5", 4, true, .effort);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "openai/gpt-5", 4, true, false, .effort);
try std.testing.expectEqual(picker_state.ModelPickerStage.effort, runtime.picker.model_picker_stage);
try std.testing.expectEqualStrings("openai/gpt-5", runtime.picker.model_picker_pending_model.items);
@@ -897,10 +897,10 @@ test "model picker flow accepts aliased pending model slice" {
var runtime = InputRuntime{};
defer runtime.deinit(std.testing.allocator);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "anthropic/claude-opus-4.6", 2, false, .effort);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "anthropic/claude-opus-4.6", 2, false, false, .effort);
const aliased_model = runtime.picker.model_picker_pending_model.items;
try runtime.picker.beginModelPickerFlow(std.testing.allocator, aliased_model, 3, true, .fast);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, aliased_model, 3, true, false, .fast);
try std.testing.expectEqual(picker_state.ModelPickerStage.fast, runtime.picker.model_picker_stage);
try std.testing.expectEqualStrings("anthropic/claude-opus-4.6", runtime.picker.model_picker_pending_model.items);
@@ -912,7 +912,7 @@ test "editing input clears model picker flow" {
var runtime = InputRuntime{};
defer runtime.deinit(std.testing.allocator);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "openai/gpt-5", 3, false, .fast);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "openai/gpt-5", 3, false, false, .fast);
try runtime.edit_state.input.appendSlice(std.testing.allocator, "/model ");
runtime.edit_state.cursor = runtime.edit_state.input.items.len;
@@ -928,7 +928,7 @@ test "preserving picker edit keeps effort flow" {
try runtime.edit_state.input.appendSlice(std.testing.allocator, "/model openai/gpt-5 ");
runtime.edit_state.cursor = runtime.edit_state.input.items.len;
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "openai/gpt-5", 2, false, .effort);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "openai/gpt-5", 2, false, false, .effort);
try runtime.insertionState().insertByte(std.testing.allocator, 'h', .preserve);
@@ -944,7 +944,7 @@ test "active model picker query tracks fast token start" {
try runtime.edit_state.input.appendSlice(std.testing.allocator, "/model anthropic/claude-opus-4.6 high f");
runtime.edit_state.cursor = runtime.edit_state.input.items.len;
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "anthropic/claude-opus-4.6", 3, true, .fast);
try runtime.picker.beginModelPickerFlow(std.testing.allocator, "anthropic/claude-opus-4.6", 3, true, false, .fast);
const query = runtime.picker.activeModelPickerQuery(&runtime.edit_state).?;
try std.testing.expectEqual(picker_state.ModelPickerStage.fast, query.stage);
@@ -4402,7 +4402,7 @@ test "line deletion no-op preserves input state and metadata" {
try runtime.kill_ring.text.appendSlice(alloc, "existing kill");
try appendPastedBlockForTest(&runtime, alloc, 7, "pasted");
try appendImageBlockForTest(&runtime, &images, alloc, 8);
try runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 3, true, .effort);
try runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 3, true, false, .effort);
runtime.picker.slash_completion_index = 4;
runtime.picker.model_completion_index = 5;
runtime.picker.file_completion_index = 6;
@@ -4459,7 +4459,7 @@ test "successful line deletion resets picker state and preserves history draft"
try primeComposerHistoryDraftForTest(&runtime, alloc, "draft");
try runtime.textReplacementState().replace(alloc, "alpha\nleftMIDright\ngamma");
runtime.edit_state.cursor = "alpha\nleftMID".len;
try runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 3, true, .fast);
try runtime.picker.beginModelPickerFlow(alloc, "openai/gpt-5", 3, true, false, .fast);
runtime.picker.file_completion_index = 6;
try std.testing.expect(try runtime.killRingState(null).delete(alloc, .line_start));
try std.testing.expectEqual(picker_state.ModelPickerStage.model, runtime.picker.model_picker_stage);
+19 -1
View File
@@ -194,6 +194,7 @@ pub fn welcomeMessage(alloc: std.mem.Allocator) ![]u8 {
}
pub const StatuslineItems = struct {
ultrafast_indicator_active: bool = false,
workspace_label: []const u8 = "",
git_branch: ?[]const u8 = null,
context_used: u64 = 0,
@@ -392,7 +393,9 @@ fn appendSessionStatusSegments(
if (model_supports_effort and !effort.isDefault()) {
appendStatusSegment(out, end, effort.displayLabel());
}
if (fast_indicator_active) {
if (statusline.ultrafast_indicator_active) {
appendStatusSegment(out, end, "⚡︎⚡︎⚡︎");
} else if (fast_indicator_active) {
appendStatusSegment(out, end, "⚡︎");
}
if (statusline.session_title) |title| {
@@ -1020,6 +1023,21 @@ test "buildHintLine uses a monochrome lightning marker for fast mode" {
try std.testing.expectEqual(@as(usize, 25), display_width.visibleWidthIgnoringAnsi(line));
}
test "buildHintLine uses three monochrome lightning markers for Ultrafast" {
initTheme(false, null);
defer initTheme(false, null);
var buf: [128]u8 = undefined;
const line = buildHintLine(false, true, "openai/gpt-6-astra", .auto, true, types.ReasoningEffort.literal("xhigh"), true, .{ .ultrafast_indicator_active = true }, 80, &buf);
var expected_buf: [128]u8 = undefined;
const expected = try std.fmt.bufPrint(&expected_buf, "{s}auto{s} · gpt-6-astra · xhigh · ⚡︎⚡︎⚡︎", .{ permission_auto_style, statusline_style });
try std.testing.expectEqualStrings(expected, line);
try std.testing.expectEqual(@as(usize, 35), display_width.visibleWidthIgnoringAnsi(line));
for (0..36) |width| {
const clipped = buildHintLine(false, true, "openai/gpt-6-astra", .auto, false, types.ReasoningEffort.literal("xhigh"), true, .{ .ultrafast_indicator_active = true }, @intCast(width), &buf);
try std.testing.expect(display_width.visibleWidthIgnoringAnsi(clipped) <= width);
}
}
test "buildHintLine shows effort when active" {
var buf: [128]u8 = undefined;
const line = buildHintLine(false, true, "openai/gpt-5", .ask, false, types.ReasoningEffort.literal("high"), true, .{}, 80, &buf);
+3 -2
View File
@@ -54,6 +54,7 @@ pub fn main(init: std.process.Init) !void {
.model_override = io_mod.getenv("FX_MODEL"),
.effort_override = io_mod.getenv("FX_EFFORT"),
.fast_override = fastOverrideFromEnv(io_mod.getenv("FX_FAST")),
.ultrafast_override = fastOverrideFromEnv(io_mod.getenv("FX_ULTRAFAST")),
.workspace_root_override = "/",
.allow_acp_mcp = false,
.allow_native_tools = false,
@@ -117,8 +118,8 @@ fn fetchCredits(
return .{};
}
/// Parses the FX_FAST host toggle: "true"/"1" enable the fast lane,
/// "false"/"0" disable it, anything else leaves the default in place.
/// Parses an FX_FAST or FX_ULTRAFAST host toggle: "true"/"1" enable the
/// lane, "false"/"0" disable it, and anything else leaves the default in place.
fn fastOverrideFromEnv(value: ?[]const u8) ?bool {
const raw = value orelse return null;
if (std.ascii.eqlIgnoreCase(raw, "true") or std.mem.eql(u8, raw, "1")) return true;
+1
View File
@@ -58,6 +58,7 @@
{ "file": "tui-slash-menu.test.ts", "weight": 88 },
{ "file": "tui-startup.test.ts", "weight": 7 },
{ "file": "tui-terminal-tool.test.ts", "weight": 129 },
{ "file": "ultrafast-fake-gateway.test.ts", "weight": 8 },
{ "file": "vision-route-fake-gateway.test.ts", "weight": 20 },
{ "file": "web-fetch-fake-network.test.ts", "weight": 3 },
{ "file": "web-fetch-live.test.ts", "weight": 1 },
+7 -3
View File
@@ -383,7 +383,7 @@ describe("cli: help", () => {
Run one noninteractive request
Usage:
fx ask [--auto|--full-access] [--model <id>] [--effort <level>] [--fast|--no-fast] [--provider-order <a,b,...>] [--provider-strict|--no-provider-strict] [--image PATH] [--system TEXT] [--json] [--quiet] [--prompt-permissions] [--no-save] [--sessions-v2] [--no-color] [--resume <last|id>|--resume-id <id>] [--continue-recovery] [--] <prompt>
fx ask [--auto|--full-access] [--model <id>] [--effort <level>] [--fast|--no-fast] [--ultrafast|--no-ultrafast] [--provider-order <a,b,...>] [--provider-strict|--no-provider-strict] [--image PATH] [--system TEXT] [--json] [--quiet] [--prompt-permissions] [--no-save] [--sessions-v2] [--no-color] [--resume <last|id>|--resume-id <id>] [--continue-recovery] [--] <prompt>
Options:
--auto Automatically review unresolved permission requests
@@ -393,6 +393,8 @@ Options:
--effort <level> Override the reasoning effort for this request
--fast Enable Fast mode for this request when the model supports it
--no-fast Disable Fast mode for this request
--ultrafast Request Ultra mode for this request when the model supports it
--no-ultrafast Disable Ultra mode for this request
--provider-order <a,b,...> Prefer these gateway providers in order for this request
--provider-strict Restrict this request to only the providers in --provider-order
--no-provider-strict Clear the provider restriction for this request
@@ -455,9 +457,11 @@ With --prompt-permissions, JSON and quiet requests may prompt on stderr only whe
expect(r.code).toBe(0);
expect(r.stderr).toBe("");
expect(r.stdout).toContain(
"Usage:\n fx acp [--model <id>] [--log-file <path>]",
"Usage:\n fx acp [--model <id>] [--ultrafast|--no-ultrafast] [--log-file <path>]",
);
expect(r.stdout).toContain("--model <id>");
expect(r.stdout).toContain("--ultrafast");
expect(r.stdout).toContain("--no-ultrafast");
expect(r.stdout).toContain("--log-file <path>");
}
},
@@ -485,7 +489,7 @@ With --prompt-permissions, JSON and quiet requests may prompt on stderr only whe
expect(result.code).toBe(1);
expect(result.stdout).toBe("");
expect(result.stderr).toBe(
"usage: fx acp [--model <id>] [--log-file <path>]\n",
"usage: fx acp [--model <id>] [--ultrafast|--no-ultrafast] [--log-file <path>]\n",
);
}
},
File diff suppressed because it is too large. Load diff