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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions extensions/gentle-ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,16 @@ const CORE_MODEL_AGENT_NAMES = [
] as const;
const CORE_MODEL_AGENT_NAME_SET = new Set<string>(CORE_MODEL_AGENT_NAMES);

type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
const THINKING_LEVELS = [
"off",
"minimal",
"low",
"medium",
"high",
"xhigh",
"max",
] as const;
type ThinkingLevel = (typeof THINKING_LEVELS)[number];
interface AgentRoutingEntry {
model?: string;
thinking?: ThinkingLevel;
Expand All @@ -580,12 +589,7 @@ const CUSTOM_MODEL = "Custom model id";
const INHERIT_THINKING = "Inherit effort";
const THINKING_OPTIONS: (ThinkingLevel | typeof INHERIT_THINKING)[] = [
INHERIT_THINKING,
"off",
"minimal",
"low",
"medium",
"high",
"xhigh",
...THINKING_LEVELS,
];

const MODEL_CONTROL_OPTIONS = [
Expand Down Expand Up @@ -805,12 +809,8 @@ function writePersonaMode(cwd: string, mode: PersonaMode): string[] {

function isThinkingLevel(value: unknown): value is ThinkingLevel {
return (
value === "off" ||
value === "minimal" ||
value === "low" ||
value === "medium" ||
value === "high" ||
value === "xhigh"
typeof value === "string" &&
(THINKING_LEVELS as readonly string[]).includes(value)
);
}

Expand Down
38 changes: 38 additions & 0 deletions tests/runtime-harness.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,44 @@ async function run() {
const restoredAgent = await readFile(join(modelsCwd, ".pi", "agents", "sdd-apply.md"), "utf8");
assert.match(restoredAgent, /model: restore\/provider/);
assert.match(restoredAgent, /thinking: high/);

// issue #286: `thinking: "max"` must survive save normalization and
// reach both subagents.json (effort) and agent frontmatter (thinking).
ctx.ui.custom = () =>
Promise.resolve({
type: "save",
config: { "sdd-apply": { model: "openai/gpt-5", thinking: "max" } },
});
await commands.get("gentle:models").handler("", ctx);
const maxSavedConfig = JSON.parse(await readFile(globalModelsPath, "utf8"));
assert.equal(maxSavedConfig["sdd-apply"].thinking, "max");
const maxSubagents = JSON.parse(
await readFile(join(modelsCwd, ".pi", "subagents.json"), "utf8"),
);
assert.equal(maxSubagents.model_profiles["sdd-apply"].effort, "max");
const maxApplyAgent = await readFile(
join(modelsCwd, ".pi", "agents", "sdd-apply.md"),
"utf8",
);
assert.match(maxApplyAgent, /thinking: max/);

// issue #286: effort picker must offer `max` after `xhigh` and save it.
let maxPickerCalls = 0;
ctx.ui.custom = (factory) =>
new Promise((resolve) => {
maxPickerCalls += 1;
const panel = factory(null, null, null, resolve);
if (maxPickerCalls === 1) {
panel.handleInput(kittyE); // open effort picker (set-all row)
for (let i = 0; i < 7; i++) panel.handleInput("j"); // max
panel.handleInput("\r");
panel.handleInput("\u0013"); // ctrl+s saves the draft
return;
}
});
await commands.get("gentle:models").handler("", ctx);
const pickerMaxConfig = JSON.parse(await readFile(globalModelsPath, "utf8"));
assert.equal(pickerMaxConfig["sdd-apply"].thinking, "max");
} finally {
await rm(modelsCwd, { recursive: true, force: true });
await rm(globalModelsPath, { force: true });
Expand Down
Loading