fix(server): a rewound plugin session reopens with its options as they stand

The reopen merged the plugin's reported options over the launch record, so a
key the plugin reset by leaving it out came back at its launch value. The
session now keeps the committed options, removals included. The provider-direct
example also refuses a combined request before changing anything.
This commit is contained in:
Mohamed Boudra
2026-11-02 00:52:17 +01:00
parent 00ec4bc80c
commit 5b4636b856
4 changed files with 125 additions and 25 deletions
@@ -98,11 +98,21 @@ describe("direct provider example", () => {
type: "session.configure",
requestId: "configure-refused",
sessionId: "session-complete",
changes: { providerOptions: { voice: "loud" } },
changes: { model: "other-model", providerOptions: { voice: "loud" } },
});
expect(events).toContainEqual(
expect.objectContaining({ type: "request.failed", requestId: "configure-refused" }),
);
// A refused request changes nothing, the model it named included.
await connection.send({
type: "session.configure",
requestId: "configure-after-refused",
sessionId: "session-complete",
changes: {},
});
expect(events.findLast((event) => event.type === "session.config")).not.toMatchObject({
config: { model: "other-model" },
});
await connection.send({
type: "session.prompt",
sessionId: "session-complete",
@@ -372,6 +372,19 @@ function configureSession(
): void {
const session = requireSession(state, input.sessionId);
const changes = input.changes;
// Work out everything first, so a refused request changes nothing.
let options: ExampleSession["options"];
try {
options = applyOptions(session.options, changes.providerOptions ?? {});
} catch (error) {
state.emit({
type: "request.failed",
requestId: input.requestId,
error: { message: error instanceof Error ? error.message : String(error) },
});
return;
}
session.options = options;
session.config = {
...session.config,
model: changes.model === null ? undefined : (changes.model ?? session.config.model),
@@ -381,16 +394,6 @@ function configureSession(
? undefined
: (changes.thinkingOption ?? session.config.thinkingOption),
};
try {
session.options = applyOptions(session.options, changes.providerOptions ?? {});
} catch (error) {
state.emit({
type: "request.failed",
requestId: input.requestId,
error: { message: error instanceof Error ? error.message : String(error) },
});
return;
}
state.emit({ type: "session.config", sessionId: input.sessionId, config: configState(session) });
state.emit({ type: "request.completed", requestId: input.requestId });
}