mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-01 22:23:07 +00:00
fix(server): plugins reject keys they do not report as settings, and Codex opening reports the speed it runs
A plugin session applied the settings it knew and silently stored the rest; it now rejects a patch that names a key the plugin does not report. Codex opening on a model without the saved speed ran Normal while the stored options still said priority; opening now reports the tier it runs, as a later model switch does.
This commit is contained in:
@@ -1136,6 +1136,18 @@ describe("PluginAgentClientRegistry", () => {
|
||||
await session.close();
|
||||
});
|
||||
|
||||
test("rejects a key the plugin does not report as a setting, and configures none of a mixed patch", async () => {
|
||||
const { harness, session } = await openStubbornVoiceSession({ launch_key: "original" });
|
||||
const configureBefore = lastConfigure(harness.inputs);
|
||||
|
||||
await expect(session.applyOptions?.({ loud: true, launch_key: "changed" })).rejects.toThrow(
|
||||
/launch_key.*cannot change on a running session/,
|
||||
);
|
||||
|
||||
expect(lastConfigure(harness.inputs)).toBe(configureBefore);
|
||||
await session.close();
|
||||
});
|
||||
|
||||
test("applying options returns the settings that ended up different, and emits nothing", async () => {
|
||||
const { session, events } = await openStubbornVoiceSession({});
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ import {
|
||||
createProviderEnv,
|
||||
type ProviderRuntimeSettings,
|
||||
} from "./provider-launch-config.js";
|
||||
import { ProviderOptionsValidationError } from "./provider-options.js";
|
||||
import type { RegisteredProviderDefinition } from "./provider-registry.js";
|
||||
import { runProviderTurn } from "./providers/provider-runner.js";
|
||||
import { StaleProviderSessionError } from "./stale-provider-session-error.js";
|
||||
@@ -1437,11 +1438,20 @@ class PluginAgentSession implements AgentSession {
|
||||
async applyOptions(options: ProviderOptions): Promise<Record<string, JsonValue>> {
|
||||
this.assertReplayComplete();
|
||||
const before = this.currentSettings();
|
||||
// A key the plugin does not report as a setting is one it only reads when the session opens.
|
||||
const unsupported = Object.keys(options).filter((key) => !(key in before));
|
||||
if (unsupported.length > 0) {
|
||||
throw new ProviderOptionsValidationError(
|
||||
this.provider,
|
||||
unsupported.map((key) => ({
|
||||
path: [key],
|
||||
message: "cannot change on a running session; it applies when the agent is opened",
|
||||
})),
|
||||
);
|
||||
}
|
||||
const settings: Record<string, JsonValue> = {};
|
||||
for (const setting of this.bridge.config.settings) {
|
||||
const value = options[setting.id];
|
||||
if (value === undefined) continue;
|
||||
settings[setting.id] = toJsonValue(value, `setting '${setting.id}'`);
|
||||
for (const [id, value] of Object.entries(options)) {
|
||||
settings[id] = toJsonValue(value, `setting '${id}'`);
|
||||
}
|
||||
if (Object.keys(settings).length === 0) return {};
|
||||
this.applyingOptions = true;
|
||||
|
||||
@@ -558,6 +558,21 @@ describe("Codex app-server provider controls", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("opening on a model without the saved tier runs Normal and the open patch says so", async () => {
|
||||
const models = [
|
||||
{ id: "plain", isDefault: true, defaultReasoningEffort: "medium", serviceTiers: [] },
|
||||
];
|
||||
const { session, appServer } = await createConnectedSession(
|
||||
{ model: "plain", providerOptions: { service_tier: "priority" } },
|
||||
{ models },
|
||||
);
|
||||
|
||||
expect(session.openOptionsPatch).toEqual({ service_tier: "default", fast_mode: null });
|
||||
|
||||
await session.startTurn("hello");
|
||||
await expect(appServer.waitForTurnStart()).resolves.toMatchObject({ serviceTier: "default" });
|
||||
});
|
||||
|
||||
test("a saved service_tier: fast runs as the model's Fast tier and the open patch names that tier", async () => {
|
||||
const { session, appServer } = await createConnectedSession({
|
||||
providerOptions: { service_tier: "fast" },
|
||||
|
||||
@@ -3873,11 +3873,15 @@ export class CodexAppServerAgentSession implements AgentSession {
|
||||
const requested = this.requestedServiceTier;
|
||||
const resolved = this.resolveServiceTier(requested);
|
||||
const legacy = this.providerOptions.fast_mode !== undefined || requested !== resolved;
|
||||
if (!legacy || resolved === null) return;
|
||||
if (!legacy) return;
|
||||
// A tier the opening model lacks runs as Normal, and the options say so (as when the model
|
||||
// is switched later).
|
||||
const runs = resolved ?? (requested === null ? null : "default");
|
||||
if (runs === null) return;
|
||||
const { fast_mode: _legacy, ...rest } = this.providerOptions;
|
||||
this.providerOptions = { ...rest, service_tier: resolved };
|
||||
this.requestedServiceTier = resolved;
|
||||
this.openOptionsPatch = { service_tier: resolved, fast_mode: null };
|
||||
this.providerOptions = { ...rest, service_tier: runs };
|
||||
this.requestedServiceTier = runs;
|
||||
this.openOptionsPatch = { service_tier: runs, fast_mode: null };
|
||||
}
|
||||
|
||||
private get serviceTier(): string | null {
|
||||
|
||||
Reference in New Issue
Block a user