fix(cli): preserve forked session variants

This commit is contained in:
marius-kilocode
2026-07-13 16:02:36 +02:00
parent 032f3bb55f
commit bd08c13412
5 changed files with 71 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@kilocode/cli": patch
---
Preserve the selected model reasoning variant when forking a session.
+5 -1
View File
@@ -778,6 +778,7 @@ export const layer = Layer.effect(
.get(),
)
const model = input.model ?? ag.model ?? (yield* currentModel(input.sessionID))
const stored = !input.model && !ag.model ? model : undefined
const same = ag.model && model.providerID === ag.model.providerID && model.modelID === ag.model.modelID
const full =
!input.variant && ag.variant && same
@@ -785,7 +786,10 @@ export const layer = Layer.effect(
.getModel(model.providerID, model.modelID)
.pipe(Effect.catchIf(Provider.ModelNotFoundError.isInstance, () => Effect.succeed(undefined)))
: undefined
const variant = input.variant ?? (ag.variant && full?.variants?.[ag.variant] ? ag.variant : undefined)
const variant =
input.variant ??
(stored && "variant" in stored && typeof stored.variant === "string" ? stored.variant : undefined) ??
(ag.variant && full?.variants?.[ag.variant] ? ag.variant : undefined)
const info: MessageV2.User = {
id: input.messageID ?? MessageID.ascending(),
+1
View File
@@ -784,6 +784,7 @@ export const layer: Layer.Layer<
workspaceID: original.workspaceID,
title,
metadata: structuredClone(original.metadata),
model: original.model ? { ...original.model } : undefined, // kilocode_change - preserve model + variant from the source session
sourceID: input.sessionID, // kilocode_change - forks preserve initialized confinement
sandboxFallback, // kilocode_change - seed confinement from the source session's original directory
})
@@ -2535,6 +2535,39 @@ it.instance(
// Agent variant
noLLMServer.instance(
"preserves the session variant through a model-less handoff",
() =>
Effect.gen(function* () {
const prompt = yield* SessionPrompt.Service
const sessions = yield* Session.Service
const source = yield* sessions.create({
model: {
id: ModelID.make("test-model"),
providerID: ProviderID.make("test"),
variant: "high",
},
})
const fork = yield* sessions.fork({ sessionID: source.id })
const handoff = yield* prompt.prompt({
sessionID: fork.id,
noReply: true,
parts: [{ type: "text", text: "fork handoff", synthetic: true }],
})
if (handoff.info.role !== "user") throw new Error("expected user message")
expect(handoff.info.model).toEqual({
providerID: ProviderID.make("test"),
modelID: ModelID.make("test-model"),
variant: "high",
})
const saved = yield* sessions.get(fork.id)
expect(saved.model?.variant).toBe("high")
}),
{ config: cfg },
)
noLLMServer.instance(
"applies agent variant only when using agent model",
() =>
@@ -5,6 +5,7 @@ import { GlobalBus, type GlobalEvent } from "../../src/bus/global"
import * as Log from "@opencode-ai/core/util/log"
import { MessageV2 } from "../../src/session/message-v2"
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
import { ModelID, ProviderID } from "@/provider/schema"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { provideInstance, tmpdirScoped } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
@@ -215,4 +216,30 @@ describe("Session", () => {
expect(saved.metadata).toBeUndefined()
}),
)
it.instance("fork preserves model and variant", () =>
Effect.gen(function* () {
const session = yield* SessionNs.Service
const model = {
id: ModelID.make("test-model"),
providerID: ProviderID.make("test-provider"),
variant: "high",
}
const created = yield* Effect.acquireRelease(
session.create({ title: "with-model", model }),
(info) => session.remove(info.id).pipe(Effect.ignore),
)
const saved = yield* session.get(created.id)
expect(saved.model).toEqual(model)
const fork = yield* Effect.acquireRelease(session.fork({ sessionID: created.id }), (info) =>
session.remove(info.id).pipe(Effect.ignore),
)
const forked = yield* session.get(fork.id)
expect(forked.model).toEqual(model)
expect(forked.model?.variant).toBe("high")
expect(forked.model).not.toBe(saved.model)
}),
)
})