mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-08 07:43:19 +00:00
fix(core): migrate legacy small model (#40966)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
This commit is contained in:
parent
047d434aa2
commit
439ed66c7b
5 changed files with 66 additions and 6 deletions
|
|
@ -41,7 +41,7 @@ export type Result =
|
|||
| { readonly type: "rejected"; readonly diagnostics: readonly Diagnostic[] }
|
||||
|
||||
const options = { errors: "all", onExcessProperty: "ignore", propertyOrder: "original" } as const
|
||||
const unsupportedTopLevel = ["logLevel", "server", "small_model", "subagent_depth", "layout"] as const
|
||||
const unsupportedTopLevel = ["logLevel", "server", "subagent_depth", "layout"] as const
|
||||
const unsupportedExperimental = [
|
||||
"disable_paste_summary",
|
||||
"batch_tool",
|
||||
|
|
@ -113,6 +113,23 @@ export function normalize(input: unknown): Result {
|
|||
const legacyAgents = mapValues(decodeMap(input.agent, ConfigAgentV1.Info, ["agent"], diagnostics), (value) =>
|
||||
canonical(ConfigAgent.Info, ConfigMigrateV1.migrateAgent(value)),
|
||||
)
|
||||
const legacySmallModel = own(input, "small_model")
|
||||
? decodeValue(Schema.String, input.small_model, ["small_model"], diagnostics)
|
||||
: undefined
|
||||
const migratedSmallModel = legacySmallModel
|
||||
? ConfigMigrateV1.migrate({ small_model: legacySmallModel }).agents?.title?.model
|
||||
: undefined
|
||||
if (legacySmallModel && !migratedSmallModel)
|
||||
diagnostics.push({
|
||||
kind: "unsupported",
|
||||
path: ["small_model"],
|
||||
message: "omitted unsupported legacy model reference",
|
||||
})
|
||||
if (migratedSmallModel)
|
||||
legacyAgents.title = {
|
||||
model: migratedSmallModel,
|
||||
...legacyAgents.title,
|
||||
}
|
||||
const modeAgents = mapValues(decodeMap(input.mode, ConfigAgentV1.Info, ["mode"], diagnostics), (value) =>
|
||||
canonical(ConfigAgent.Info, ConfigMigrateV1.migrateAgent({ ...value, mode: "primary" })),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -119,8 +119,16 @@ function agents(info: typeof ConfigV1.Info.Type) {
|
|||
...Object.entries(info.agent ?? {}),
|
||||
...Object.entries(info.mode ?? {}).map(([name, agent]) => [name, { ...agent, mode: "primary" as const }] as const),
|
||||
]
|
||||
if (!entries.length) return undefined
|
||||
return Object.fromEntries(entries.flatMap(([name, agent]) => (agent ? [[name, migrateAgent(agent)]] : [])))
|
||||
const result = Object.fromEntries(entries.flatMap(([name, agent]) => (agent ? [[name, migrateAgent(agent)]] : [])))
|
||||
const small = modelSelection(info.small_model)
|
||||
if (!small) return entries.length ? result : undefined
|
||||
return {
|
||||
...result,
|
||||
title: {
|
||||
model: small,
|
||||
...result.title,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function migrateAgent(info: ConfigAgentV1.Info) {
|
||||
|
|
|
|||
|
|
@ -512,6 +512,20 @@ describe("Config", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("migrates the v1 small model to the title agent", () =>
|
||||
Effect.sync(() => {
|
||||
expect(
|
||||
ConfigMigrateV1.migrate({
|
||||
small_model: "anthropic/claude-haiku-4-5",
|
||||
agent: { title: { prompt: "Custom title prompt" } },
|
||||
}).agents?.title,
|
||||
).toEqual({
|
||||
model: { providerID: "anthropic", model: "claude-haiku-4-5" },
|
||||
system: "Custom title prompt",
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("migrates v1 provider lists to policies", () =>
|
||||
Effect.sync(() => {
|
||||
expect(
|
||||
|
|
|
|||
|
|
@ -149,6 +149,28 @@ describe("ConfigNormalize", () => {
|
|||
expect(() => Schema.decodeUnknownSync(Info)(result.encoded)).not.toThrow()
|
||||
})
|
||||
|
||||
test("migrates the legacy small model to the title agent", () => {
|
||||
const result = normalized({
|
||||
small_model: "anthropic/claude-haiku-4-5",
|
||||
agent: { title: { prompt: "Custom title prompt" } },
|
||||
})
|
||||
expect(result.encoded.agents).toEqual({
|
||||
title: {
|
||||
model: { providerID: "anthropic", model: "claude-haiku-4-5" },
|
||||
system: "Custom title prompt",
|
||||
},
|
||||
})
|
||||
expect(result.diagnostics).toEqual([])
|
||||
})
|
||||
|
||||
test("omits an invalid legacy small model without exposing its value", () => {
|
||||
const secret = "do-not-log-this-value"
|
||||
const result = normalized({ small_model: secret })
|
||||
expect(result.encoded.agents).toBeUndefined()
|
||||
expect(result.diagnostics.map((item) => [item.kind, item.path])).toEqual([["unsupported", ["small_model"]]])
|
||||
expect(JSON.stringify(result.diagnostics)).not.toContain(secret)
|
||||
})
|
||||
|
||||
test("recovers malformed named entries and retains a valid legacy collision", () => {
|
||||
const result = normalized({
|
||||
command: { fallback: { template: "legacy" } },
|
||||
|
|
@ -390,7 +412,6 @@ describe("ConfigNormalize", () => {
|
|||
const secret = "do-not-log-this-value"
|
||||
const result = normalized({
|
||||
logLevel: "DEBUG",
|
||||
small_model: secret,
|
||||
agent: { reviewer: { name: secret, prompt: "review" } },
|
||||
provider: {
|
||||
custom: {
|
||||
|
|
@ -409,7 +430,6 @@ describe("ConfigNormalize", () => {
|
|||
})
|
||||
expect(result.diagnostics.filter((item) => item.kind === "unsupported").map((item) => item.path)).toEqual([
|
||||
["logLevel"],
|
||||
["small_model"],
|
||||
["agent", "reviewer", "name"],
|
||||
["provider", "custom", "id"],
|
||||
["provider", "custom", "whitelist"],
|
||||
|
|
|
|||
|
|
@ -419,6 +419,8 @@ The V1 provider filters do not have one-to-one native V2 config fields, but thei
|
|||
|
||||
- `enabled_providers` becomes an internal deny-by-default provider policy followed by allows for the listed providers.
|
||||
- `disabled_providers` becomes internal deny policies for the listed providers.
|
||||
- `small_model` becomes the `model` selection for the built-in `title` agent. Native V2 configuration should use
|
||||
`agents.title.model` instead.
|
||||
|
||||
You may keep these fields in V1 syntax. OpenCode normalizes them without warning.
|
||||
|
||||
|
|
@ -430,7 +432,6 @@ they are not mistaken for active configuration:
|
|||
- `logLevel`: use `OPENCODE_LOG_LEVEL` when starting OpenCode.
|
||||
- `server`: use the V2 service and explicit server options; the server API is an intentional breaking change.
|
||||
- `layout`: remove it; V1 already treated it as deprecated and always used stretch layout.
|
||||
- `small_model`: V2 selects models for internal maintenance agents without a separate top-level field.
|
||||
- Top-level `subagent_depth`: use `experimental.subagent_depth` instead.
|
||||
- `compaction.tail_turns` and `compaction.prune`: V2 uses `compaction.keep.tokens` and checkpoint-based compaction instead.
|
||||
- Agent `name` inside V1 JSON configuration.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue