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
*/
import modelsData from "../models.json";
import { OPENAI_CODEX_PROVIDER_NAME } from "../providers/openai-codex-provider";
export const models = modelsData;
@@ -186,6 +187,68 @@ export function getModelUpdateArgs(
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
* @param handle - The full model handle

View File

@@ -878,23 +878,56 @@ export async function handleHeadlessCommand(
(!forceNew && !fromAfFile)
);
// If resuming and a model or system prompt was specified, apply those changes
if (isResumingAgent && (model || systemPromptPreset)) {
// If resuming, always refresh model settings from presets to keep
// preset-derived fields in sync, then apply optional command-line
// overrides (model/system prompt).
if (isResumingAgent) {
const { updateAgentLLMConfig } = await import("./agent/modify");
if (model) {
const { resolveModel } = await import("./agent/model");
const modelHandle = resolveModel(model);
if (!modelHandle) {
if (typeof modelHandle !== "string") {
console.error(`Error: Invalid model "${model}"`);
process.exit(1);
}
// 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)
const { updateAgentLLMConfig } = await import("./agent/modify");
const updateArgs = getModelUpdateArgs(model);
await updateAgentLLMConfig(agent.id, modelHandle, updateArgs);
// Refresh agent state after model update
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) {

View File

@@ -1789,8 +1789,12 @@ async function main(): Promise<void> {
);
setIsResumingSession(resuming);
// If resuming and a model or system prompt was specified, apply those changes
if (resuming && (model || systemPromptPreset)) {
// If resuming, always refresh model settings from presets to keep
// preset-derived fields in sync, then apply optional command-line
// overrides (model/system prompt).
if (resuming) {
const { updateAgentLLMConfig } = await import("./agent/modify");
if (model) {
const { resolveModel, getModelUpdateArgs } = await import(
"./agent/model"
@@ -1803,11 +1807,47 @@ async function main(): Promise<void> {
// 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)
const { updateAgentLLMConfig } = await import("./agent/modify");
const updateArgs = getModelUpdateArgs(model);
await updateAgentLLMConfig(agent.id, modelHandle, updateArgs);
// Refresh agent state after model update
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) {

View File

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