fix: refresh model preset settings on resume (#1052)

Co-authored-by: Letta <noreply@letta.com>
Co-authored-by: letta-code <248085862+letta-code@users.noreply.github.com>
Co-authored-by: jnjpng <jnjpng@users.noreply.github.com>
This commit is contained in:
jnjpng
2026-02-21 14:38:13 -08:00
committed by GitHub
parent f5cc69c9d1
commit 7ad3a975b1
5 changed files with 382 additions and 83 deletions

View File

@@ -2,6 +2,7 @@
* Model resolution and handling utilities * Model resolution and handling utilities
*/ */
import modelsData from "../models.json"; import modelsData from "../models.json";
import { OPENAI_CODEX_PROVIDER_NAME } from "../providers/openai-codex-provider";
export const models = modelsData; export const models = modelsData;
@@ -186,6 +187,68 @@ export function getModelUpdateArgs(
return modelInfo?.updateArgs; return modelInfo?.updateArgs;
} }
type AgentModelSnapshot = {
model?: string | null;
llm_config?: {
model?: string | null;
model_endpoint_type?: string | null;
reasoning_effort?: string | null;
enable_reasoner?: boolean | null;
} | null;
};
/**
* Resolve the current model preset + updateArgs for an existing agent.
*
* Used during startup/resume refresh to re-apply only preset-defined fields
* (without requiring an explicit --model flag).
*/
export function getModelPresetUpdateForAgent(
agent: AgentModelSnapshot,
): { modelHandle: string; updateArgs: Record<string, unknown> } | null {
const directHandle =
typeof agent.model === "string" && agent.model.length > 0
? agent.model
: null;
const endpointType = agent.llm_config?.model_endpoint_type;
const llmModel = agent.llm_config?.model;
const llmDerivedHandle =
typeof endpointType === "string" &&
endpointType.length > 0 &&
typeof llmModel === "string" &&
llmModel.length > 0
? `${
endpointType === "chatgpt_oauth"
? OPENAI_CODEX_PROVIDER_NAME
: endpointType
}/${llmModel}`
: typeof llmModel === "string" && llmModel.includes("/")
? llmModel
: null;
const modelHandle = directHandle ?? llmDerivedHandle;
if (!modelHandle) return null;
const modelInfo = getModelInfoForLlmConfig(modelHandle, {
reasoning_effort: agent.llm_config?.reasoning_effort ?? null,
enable_reasoner: agent.llm_config?.enable_reasoner ?? null,
});
const updateArgs =
(modelInfo?.updateArgs as Record<string, unknown> | undefined) ??
getModelUpdateArgs(modelHandle);
if (!updateArgs || Object.keys(updateArgs).length === 0) {
return null;
}
return {
modelHandle: modelInfo?.handle ?? modelHandle,
updateArgs,
};
}
/** /**
* Find a model entry by handle with fuzzy matching support * Find a model entry by handle with fuzzy matching support
* @param handle - The full model handle * @param handle - The full model handle

View File

@@ -878,23 +878,56 @@ export async function handleHeadlessCommand(
(!forceNew && !fromAfFile) (!forceNew && !fromAfFile)
); );
// If resuming and a model or system prompt was specified, apply those changes // If resuming, always refresh model settings from presets to keep
if (isResumingAgent && (model || systemPromptPreset)) { // preset-derived fields in sync, then apply optional command-line
// overrides (model/system prompt).
if (isResumingAgent) {
const { updateAgentLLMConfig } = await import("./agent/modify");
if (model) { if (model) {
const { resolveModel } = await import("./agent/model"); const { resolveModel } = await import("./agent/model");
const modelHandle = resolveModel(model); const modelHandle = resolveModel(model);
if (!modelHandle) { if (typeof modelHandle !== "string") {
console.error(`Error: Invalid model "${model}"`); console.error(`Error: Invalid model "${model}"`);
process.exit(1); process.exit(1);
} }
// Always apply model update - different model IDs can share the same // Always apply model update - different model IDs can share the same
// handle but have different settings (e.g., gpt-5.2-medium vs gpt-5.2-xhigh) // handle but have different settings (e.g., gpt-5.2-medium vs gpt-5.2-xhigh)
const { updateAgentLLMConfig } = await import("./agent/modify");
const updateArgs = getModelUpdateArgs(model); const updateArgs = getModelUpdateArgs(model);
await updateAgentLLMConfig(agent.id, modelHandle, updateArgs); await updateAgentLLMConfig(agent.id, modelHandle, updateArgs);
// Refresh agent state after model update // Refresh agent state after model update
agent = await client.agents.retrieve(agent.id); agent = await client.agents.retrieve(agent.id);
} else {
const { getModelPresetUpdateForAgent } = await import("./agent/model");
const presetRefresh = getModelPresetUpdateForAgent(agent);
if (presetRefresh) {
// Resume preset refresh is intentionally scoped for now.
// We only force-refresh max_output_tokens + parallel_tool_calls.
// Other preset fields available in models.json (for example:
// context_window, reasoning_effort, enable_reasoner,
// max_reasoning_tokens, verbosity, temperature,
// thinking_budget) are intentionally not auto-applied yet.
const resumeRefreshUpdateArgs: Record<string, unknown> = {};
if (typeof presetRefresh.updateArgs.max_output_tokens === "number") {
resumeRefreshUpdateArgs.max_output_tokens =
presetRefresh.updateArgs.max_output_tokens;
}
if (typeof presetRefresh.updateArgs.parallel_tool_calls === "boolean") {
resumeRefreshUpdateArgs.parallel_tool_calls =
presetRefresh.updateArgs.parallel_tool_calls;
}
if (Object.keys(resumeRefreshUpdateArgs).length > 0) {
await updateAgentLLMConfig(
agent.id,
presetRefresh.modelHandle,
resumeRefreshUpdateArgs,
);
// Refresh agent state after model update
agent = await client.agents.retrieve(agent.id);
}
}
} }
if (systemPromptPreset) { if (systemPromptPreset) {

View File

@@ -1789,8 +1789,12 @@ async function main(): Promise<void> {
); );
setIsResumingSession(resuming); setIsResumingSession(resuming);
// If resuming and a model or system prompt was specified, apply those changes // If resuming, always refresh model settings from presets to keep
if (resuming && (model || systemPromptPreset)) { // preset-derived fields in sync, then apply optional command-line
// overrides (model/system prompt).
if (resuming) {
const { updateAgentLLMConfig } = await import("./agent/modify");
if (model) { if (model) {
const { resolveModel, getModelUpdateArgs } = await import( const { resolveModel, getModelUpdateArgs } = await import(
"./agent/model" "./agent/model"
@@ -1803,11 +1807,47 @@ async function main(): Promise<void> {
// Always apply model update - different model IDs can share the same // Always apply model update - different model IDs can share the same
// handle but have different settings (e.g., gpt-5.2-medium vs gpt-5.2-xhigh) // handle but have different settings (e.g., gpt-5.2-medium vs gpt-5.2-xhigh)
const { updateAgentLLMConfig } = await import("./agent/modify");
const updateArgs = getModelUpdateArgs(model); const updateArgs = getModelUpdateArgs(model);
await updateAgentLLMConfig(agent.id, modelHandle, updateArgs); await updateAgentLLMConfig(agent.id, modelHandle, updateArgs);
// Refresh agent state after model update // Refresh agent state after model update
agent = await client.agents.retrieve(agent.id); agent = await client.agents.retrieve(agent.id);
} else {
const { getModelPresetUpdateForAgent } = await import(
"./agent/model"
);
const presetRefresh = getModelPresetUpdateForAgent(agent);
if (presetRefresh) {
// Resume preset refresh is intentionally scoped for now.
// We only force-refresh max_output_tokens + parallel_tool_calls.
// Other preset fields available in models.json (for example:
// context_window, reasoning_effort, enable_reasoner,
// max_reasoning_tokens, verbosity, temperature,
// thinking_budget) are intentionally not auto-applied yet.
const resumeRefreshUpdateArgs: Record<string, unknown> = {};
if (
typeof presetRefresh.updateArgs.max_output_tokens === "number"
) {
resumeRefreshUpdateArgs.max_output_tokens =
presetRefresh.updateArgs.max_output_tokens;
}
if (
typeof presetRefresh.updateArgs.parallel_tool_calls ===
"boolean"
) {
resumeRefreshUpdateArgs.parallel_tool_calls =
presetRefresh.updateArgs.parallel_tool_calls;
}
if (Object.keys(resumeRefreshUpdateArgs).length > 0) {
await updateAgentLLMConfig(
agent.id,
presetRefresh.modelHandle,
resumeRefreshUpdateArgs,
);
// Refresh agent state after model update
agent = await client.agents.retrieve(agent.id);
}
}
} }
if (systemPromptPreset) { if (systemPromptPreset) {

View File

@@ -10,7 +10,8 @@
"context_window": 200000, "context_window": 200000,
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "high", "reasoning_effort": "high",
"enable_reasoner": true "enable_reasoner": true,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -22,7 +23,8 @@
"context_window": 1000000, "context_window": 1000000,
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "high", "reasoning_effort": "high",
"enable_reasoner": true "enable_reasoner": true,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -34,7 +36,8 @@
"context_window": 200000, "context_window": 200000,
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "none", "reasoning_effort": "none",
"enable_reasoner": false "enable_reasoner": false,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -47,7 +50,8 @@
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "low", "reasoning_effort": "low",
"enable_reasoner": true, "enable_reasoner": true,
"max_reasoning_tokens": 4000 "max_reasoning_tokens": 4000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -60,7 +64,8 @@
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "medium", "reasoning_effort": "medium",
"enable_reasoner": true, "enable_reasoner": true,
"max_reasoning_tokens": 12000 "max_reasoning_tokens": 12000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -73,7 +78,8 @@
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "xhigh", "reasoning_effort": "xhigh",
"enable_reasoner": true, "enable_reasoner": true,
"max_reasoning_tokens": 31999 "max_reasoning_tokens": 31999,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -84,7 +90,8 @@
"updateArgs": { "updateArgs": {
"context_window": 180000, "context_window": 180000,
"max_output_tokens": 64000, "max_output_tokens": 64000,
"max_reasoning_tokens": 31999 "max_reasoning_tokens": 31999,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -95,7 +102,8 @@
"updateArgs": { "updateArgs": {
"enable_reasoner": false, "enable_reasoner": false,
"context_window": 180000, "context_window": 180000,
"max_output_tokens": 64000 "max_output_tokens": 64000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -108,7 +116,8 @@
"context_window": 200000, "context_window": 200000,
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "high", "reasoning_effort": "high",
"enable_reasoner": true "enable_reasoner": true,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -120,7 +129,8 @@
"context_window": 200000, "context_window": 200000,
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "none", "reasoning_effort": "none",
"enable_reasoner": false "enable_reasoner": false,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -133,7 +143,8 @@
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "low", "reasoning_effort": "low",
"enable_reasoner": true, "enable_reasoner": true,
"max_reasoning_tokens": 4000 "max_reasoning_tokens": 4000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -146,7 +157,8 @@
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "medium", "reasoning_effort": "medium",
"enable_reasoner": true, "enable_reasoner": true,
"max_reasoning_tokens": 12000 "max_reasoning_tokens": 12000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -159,7 +171,8 @@
"max_output_tokens": 128000, "max_output_tokens": 128000,
"reasoning_effort": "xhigh", "reasoning_effort": "xhigh",
"enable_reasoner": true, "enable_reasoner": true,
"max_reasoning_tokens": 31999 "max_reasoning_tokens": 31999,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -170,7 +183,8 @@
"updateArgs": { "updateArgs": {
"context_window": 180000, "context_window": 180000,
"max_output_tokens": 64000, "max_output_tokens": 64000,
"max_reasoning_tokens": 31999 "max_reasoning_tokens": 31999,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -183,7 +197,8 @@
"updateArgs": { "updateArgs": {
"context_window": 180000, "context_window": 180000,
"max_output_tokens": 64000, "max_output_tokens": 64000,
"max_reasoning_tokens": 31999 "max_reasoning_tokens": 31999,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -194,7 +209,8 @@
"isFeatured": true, "isFeatured": true,
"updateArgs": { "updateArgs": {
"context_window": 180000, "context_window": 180000,
"max_output_tokens": 64000 "max_output_tokens": 64000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -206,7 +222,8 @@
"reasoning_effort": "none", "reasoning_effort": "none",
"verbosity": "low", "verbosity": "low",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -218,7 +235,8 @@
"reasoning_effort": "low", "reasoning_effort": "low",
"verbosity": "low", "verbosity": "low",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -230,7 +248,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "low", "verbosity": "low",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -243,7 +262,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "low", "verbosity": "low",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -255,7 +275,8 @@
"reasoning_effort": "xhigh", "reasoning_effort": "xhigh",
"verbosity": "low", "verbosity": "low",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -267,7 +288,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -279,7 +301,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -291,7 +314,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -303,7 +327,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -315,7 +340,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -327,7 +353,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -339,7 +366,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -351,7 +379,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -363,7 +392,8 @@
"reasoning_effort": "xhigh", "reasoning_effort": "xhigh",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -375,7 +405,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -387,7 +418,8 @@
"reasoning_effort": "none", "reasoning_effort": "none",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -399,7 +431,8 @@
"reasoning_effort": "low", "reasoning_effort": "low",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -412,7 +445,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -424,7 +458,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -436,7 +471,8 @@
"reasoning_effort": "xhigh", "reasoning_effort": "xhigh",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -448,7 +484,8 @@
"reasoning_effort": "none", "reasoning_effort": "none",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -460,7 +497,8 @@
"reasoning_effort": "low", "reasoning_effort": "low",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -473,7 +511,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -485,7 +524,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -497,7 +537,8 @@
"reasoning_effort": "xhigh", "reasoning_effort": "xhigh",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -509,7 +550,8 @@
"reasoning_effort": "none", "reasoning_effort": "none",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -521,7 +563,8 @@
"reasoning_effort": "low", "reasoning_effort": "low",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -533,7 +576,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -545,7 +589,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -557,7 +602,8 @@
"reasoning_effort": "none", "reasoning_effort": "none",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -570,7 +616,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -582,7 +629,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -594,7 +642,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -606,7 +655,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -618,7 +668,8 @@
"reasoning_effort": "xhigh", "reasoning_effort": "xhigh",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -630,7 +681,8 @@
"reasoning_effort": "minimal", "reasoning_effort": "minimal",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -642,7 +694,8 @@
"reasoning_effort": "low", "reasoning_effort": "low",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -654,7 +707,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -666,7 +720,8 @@
"reasoning_effort": "high", "reasoning_effort": "high",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -678,7 +733,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -690,7 +746,8 @@
"reasoning_effort": "medium", "reasoning_effort": "medium",
"verbosity": "medium", "verbosity": "medium",
"context_window": 272000, "context_window": 272000,
"max_output_tokens": 128000 "max_output_tokens": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -701,7 +758,8 @@
"isFeatured": true, "isFeatured": true,
"free": true, "free": true,
"updateArgs": { "updateArgs": {
"context_window": 200000 "context_window": 200000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -712,7 +770,8 @@
"isFeatured": true, "isFeatured": true,
"free": true, "free": true,
"updateArgs": { "updateArgs": {
"context_window": 200000 "context_window": 200000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -724,7 +783,8 @@
"free": true, "free": true,
"updateArgs": { "updateArgs": {
"context_window": 160000, "context_window": 160000,
"max_output_tokens": 64000 "max_output_tokens": 64000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -736,7 +796,8 @@
"free": true, "free": true,
"updateArgs": { "updateArgs": {
"context_window": 160000, "context_window": 160000,
"max_output_tokens": 64000 "max_output_tokens": 64000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -746,7 +807,8 @@
"description": "Minimax's latest model", "description": "Minimax's latest model",
"updateArgs": { "updateArgs": {
"context_window": 160000, "context_window": 160000,
"max_output_tokens": 64000 "max_output_tokens": 64000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -755,7 +817,8 @@
"label": "Kimi K2", "label": "Kimi K2",
"description": "Kimi's K2 model", "description": "Kimi's K2 model",
"updateArgs": { "updateArgs": {
"context_window": 262144 "context_window": 262144,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -766,7 +829,8 @@
"updateArgs": { "updateArgs": {
"context_window": 256000, "context_window": 256000,
"max_output_tokens": 16000, "max_output_tokens": 16000,
"temperature": 1.0 "temperature": 1.0,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -776,7 +840,8 @@
"description": "Kimi's latest coding model", "description": "Kimi's latest coding model",
"isFeatured": true, "isFeatured": true,
"updateArgs": { "updateArgs": {
"context_window": 262144 "context_window": 262144,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -785,7 +850,8 @@
"label": "DeepSeek Chat V3.1", "label": "DeepSeek Chat V3.1",
"description": "DeepSeek V3.1 model", "description": "DeepSeek V3.1 model",
"updateArgs": { "updateArgs": {
"context_window": 128000 "context_window": 128000,
"parallel_tool_calls": true
} }
}, },
{ {
@@ -794,7 +860,11 @@
"label": "Gemini 3.1 Pro", "label": "Gemini 3.1 Pro",
"description": "Google's latest and smartest model", "description": "Google's latest and smartest model",
"isFeatured": true, "isFeatured": true,
"updateArgs": { "context_window": 180000, "temperature": 1.0 } "updateArgs": {
"context_window": 180000,
"temperature": 1.0,
"parallel_tool_calls": true
}
}, },
{ {
"id": "gemini-3", "id": "gemini-3",
@@ -802,7 +872,11 @@
"label": "Gemini 3 Pro", "label": "Gemini 3 Pro",
"description": "Google's smartest model", "description": "Google's smartest model",
"isFeatured": true, "isFeatured": true,
"updateArgs": { "context_window": 180000, "temperature": 1.0 } "updateArgs": {
"context_window": 180000,
"temperature": 1.0,
"parallel_tool_calls": true
}
}, },
{ {
"id": "gemini-3-flash", "id": "gemini-3-flash",
@@ -810,55 +884,63 @@
"label": "Gemini 3 Flash", "label": "Gemini 3 Flash",
"description": "Google's fastest Gemini 3 model", "description": "Google's fastest Gemini 3 model",
"isFeatured": true, "isFeatured": true,
"updateArgs": { "context_window": 180000, "temperature": 1.0 } "updateArgs": {
"context_window": 180000,
"temperature": 1.0,
"parallel_tool_calls": true
}
}, },
{ {
"id": "gemini-flash", "id": "gemini-flash",
"handle": "google_ai/gemini-2.5-flash", "handle": "google_ai/gemini-2.5-flash",
"label": "Gemini 2.5 Flash", "label": "Gemini 2.5 Flash",
"description": "Google's fastest model", "description": "Google's fastest model",
"updateArgs": { "context_window": 180000 } "updateArgs": { "context_window": 180000, "parallel_tool_calls": true }
}, },
{ {
"id": "gemini-pro", "id": "gemini-pro",
"handle": "google_ai/gemini-2.5-pro", "handle": "google_ai/gemini-2.5-pro",
"label": "Gemini 2.5 Pro", "label": "Gemini 2.5 Pro",
"description": "Google's last generation flagship model", "description": "Google's last generation flagship model",
"updateArgs": { "context_window": 180000 } "updateArgs": { "context_window": 180000, "parallel_tool_calls": true }
}, },
{ {
"id": "gpt-4.1", "id": "gpt-4.1",
"handle": "openai/gpt-4.1", "handle": "openai/gpt-4.1",
"label": "GPT-4.1", "label": "GPT-4.1",
"description": "OpenAI's most recent non-reasoner model", "description": "OpenAI's most recent non-reasoner model",
"updateArgs": { "context_window": 1047576 } "updateArgs": { "context_window": 1047576, "parallel_tool_calls": true }
}, },
{ {
"id": "gpt-4.1-mini", "id": "gpt-4.1-mini",
"handle": "openai/gpt-4.1-mini-2025-04-14", "handle": "openai/gpt-4.1-mini-2025-04-14",
"label": "GPT-4.1-Mini", "label": "GPT-4.1-Mini",
"description": "OpenAI's most recent non-reasoner model (mini version)", "description": "OpenAI's most recent non-reasoner model (mini version)",
"updateArgs": { "context_window": 1047576 } "updateArgs": { "context_window": 1047576, "parallel_tool_calls": true }
}, },
{ {
"id": "gpt-4.1-nano", "id": "gpt-4.1-nano",
"handle": "openai/gpt-4.1-nano-2025-04-14", "handle": "openai/gpt-4.1-nano-2025-04-14",
"label": "GPT-4.1-Nano", "label": "GPT-4.1-Nano",
"description": "OpenAI's most recent non-reasoner model (nano version)", "description": "OpenAI's most recent non-reasoner model (nano version)",
"updateArgs": { "context_window": 1047576 } "updateArgs": { "context_window": 1047576, "parallel_tool_calls": true }
}, },
{ {
"id": "o4-mini", "id": "o4-mini",
"handle": "openai/o4-mini", "handle": "openai/o4-mini",
"label": "o4-mini", "label": "o4-mini",
"description": "OpenAI's latest o-series reasoning model", "description": "OpenAI's latest o-series reasoning model",
"updateArgs": { "context_window": 180000 } "updateArgs": { "context_window": 180000, "parallel_tool_calls": true }
}, },
{ {
"id": "gemini-3-vertex", "id": "gemini-3-vertex",
"handle": "google_vertex/gemini-3-pro-preview", "handle": "google_vertex/gemini-3-pro-preview",
"label": "Gemini 3 Pro", "label": "Gemini 3 Pro",
"description": "Google's smartest Gemini 3 Pro model (via Vertex AI)", "description": "Google's smartest Gemini 3 Pro model (via Vertex AI)",
"updateArgs": { "context_window": 180000, "temperature": 1.0 } "updateArgs": {
"context_window": 180000,
"temperature": 1.0,
"parallel_tool_calls": true
}
} }
] ]

View File

@@ -0,0 +1,81 @@
import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
describe("model preset refresh wiring", () => {
test("model.ts exports preset refresh helper", () => {
const path = fileURLToPath(
new URL("../../agent/model.ts", import.meta.url),
);
const source = readFileSync(path, "utf-8");
expect(source).toContain("export function getModelPresetUpdateForAgent(");
expect(source).toContain("OPENAI_CODEX_PROVIDER_NAME");
expect(source).toContain("getModelInfoForLlmConfig(modelHandle");
});
test("modify.ts keeps direct updateArgs-driven model update flow", () => {
const path = fileURLToPath(
new URL("../../agent/modify.ts", import.meta.url),
);
const source = readFileSync(path, "utf-8");
const start = source.indexOf("export async function updateAgentLLMConfig(");
const end = source.indexOf(
"export interface SystemPromptUpdateResult",
start,
);
expect(start).toBeGreaterThanOrEqual(0);
expect(end).toBeGreaterThan(start);
const updateSegment = source.slice(start, end);
expect(updateSegment).toContain(
"buildModelSettings(modelHandle, updateArgs)",
);
expect(updateSegment).toContain("getModelContextWindow(modelHandle)");
expect(updateSegment).not.toContain(
"const currentAgent = await client.agents.retrieve(",
);
expect(source).not.toContain(
'hasUpdateArg(updateArgs, "parallel_tool_calls")',
);
});
test("interactive resume flow refreshes model preset without explicit --model", () => {
const path = fileURLToPath(new URL("../../index.ts", import.meta.url));
const source = readFileSync(path, "utf-8");
expect(source).toContain("if (resuming)");
expect(source).toContain("getModelPresetUpdateForAgent");
expect(source).toContain(
"const presetRefresh = getModelPresetUpdateForAgent(agent)",
);
expect(source).toContain("resumeRefreshUpdateArgs");
expect(source).toContain("presetRefresh.updateArgs.max_output_tokens");
expect(source).toContain("presetRefresh.updateArgs.parallel_tool_calls");
expect(source).toContain("await updateAgentLLMConfig(");
expect(source).toContain("presetRefresh.modelHandle");
expect(source).not.toContain(
"await updateAgentLLMConfig(\n agent.id,\n presetRefresh.modelHandle,\n presetRefresh.updateArgs,",
);
});
test("headless resume flow refreshes model preset without explicit --model", () => {
const path = fileURLToPath(new URL("../../headless.ts", import.meta.url));
const source = readFileSync(path, "utf-8");
expect(source).toContain("if (isResumingAgent)");
expect(source).toContain("getModelPresetUpdateForAgent");
expect(source).toContain(
"const presetRefresh = getModelPresetUpdateForAgent(agent)",
);
expect(source).toContain("resumeRefreshUpdateArgs");
expect(source).toContain("presetRefresh.updateArgs.max_output_tokens");
expect(source).toContain("presetRefresh.updateArgs.parallel_tool_calls");
expect(source).toContain("await updateAgentLLMConfig(");
expect(source).toContain("presetRefresh.modelHandle");
expect(source).not.toContain(
"await updateAgentLLMConfig(\n agent.id,\n presetRefresh.modelHandle,\n presetRefresh.updateArgs,",
);
});
});