fix: prevent session model change when sending notifications

- background-agent: use only parentModel, remove prevMessage fallback
- todo-continuation: don't pass model to preserve session's lastModel
- Remove unused imports (findNearestMessageWithFields, fs, path)

Root cause: session.prompt with model param changes session's lastModel
This commit is contained in:
YeonGyu-Kim 2026-01-08 15:05:05 +09:00
parent 0d0bf4d384
commit 4b4386dbad
2 changed files with 8 additions and 33 deletions

View File

@ -1,5 +1,4 @@
import { existsSync, readdirSync } from "node:fs"
import { join } from "node:path"
import type { PluginInput } from "@opencode-ai/plugin" import type { PluginInput } from "@opencode-ai/plugin"
import type { import type {
BackgroundTask, BackgroundTask,
@ -9,10 +8,7 @@ import type {
import { log } from "../../shared/logger" import { log } from "../../shared/logger"
import { ConcurrencyManager } from "./concurrency" import { ConcurrencyManager } from "./concurrency"
import type { BackgroundTaskConfig } from "../../config/schema" import type { BackgroundTaskConfig } from "../../config/schema"
import {
findNearestMessageWithFields,
MESSAGE_STORAGE,
} from "../hook-message-injector"
import { subagentSessions } from "../claude-code-session-state" import { subagentSessions } from "../claude-code-session-state"
import { getTaskToastManager } from "../task-toast-manager" import { getTaskToastManager } from "../task-toast-manager"
@ -44,20 +40,6 @@ interface Todo {
id: string id: string
} }
function getMessageDir(sessionID: string): string | null {
if (!existsSync(MESSAGE_STORAGE)) return null
const directPath = join(MESSAGE_STORAGE, sessionID)
if (existsSync(directPath)) return directPath
for (const dir of readdirSync(MESSAGE_STORAGE)) {
const sessionPath = join(MESSAGE_STORAGE, dir, sessionID)
if (existsSync(sessionPath)) return sessionPath
}
return null
}
export class BackgroundManager { export class BackgroundManager {
private tasks: Map<string, BackgroundTask> private tasks: Map<string, BackgroundTask>
private notifications: Map<string, BackgroundTask[]> private notifications: Map<string, BackgroundTask[]>
@ -456,18 +438,15 @@ export class BackgroundManager {
} }
try { try {
const messageDir = getMessageDir(task.parentSessionID) // Use only parentModel - don't fallback to prevMessage.model
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null // This prevents accidentally changing parent session's model
const modelField = task.parentModel?.providerID && task.parentModel?.modelID
const modelContext = task.parentModel ?? prevMessage?.model ? { providerID: task.parentModel.providerID, modelID: task.parentModel.modelID }
const modelField = modelContext?.providerID && modelContext?.modelID
? { providerID: modelContext.providerID, modelID: modelContext.modelID }
: undefined : undefined
await this.client.session.prompt({ await this.client.session.prompt({
path: { id: task.parentSessionID }, path: { id: task.parentSessionID },
body: { body: {
agent: prevMessage?.agent,
model: modelField, model: modelField,
parts: [{ type: "text", text: message }], parts: [{ type: "text", text: message }],
}, },

View File

@ -205,18 +205,14 @@ export function createTodoContinuationEnforcer(
const prompt = `${CONTINUATION_PROMPT}\n\n[Status: ${todos.length - freshIncompleteCount}/${todos.length} completed, ${freshIncompleteCount} remaining]` const prompt = `${CONTINUATION_PROMPT}\n\n[Status: ${todos.length - freshIncompleteCount}/${todos.length} completed, ${freshIncompleteCount} remaining]`
const modelField = prevMessage?.model?.providerID && prevMessage?.model?.modelID
? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID }
: undefined
try { try {
log(`[${HOOK_NAME}] Injecting continuation`, { sessionID, agent: prevMessage?.agent, model: modelField, incompleteCount: freshIncompleteCount }) log(`[${HOOK_NAME}] Injecting continuation`, { sessionID, agent: prevMessage?.agent, incompleteCount: freshIncompleteCount })
// Don't pass model - let OpenCode use session's existing lastModel
await ctx.client.session.prompt({ await ctx.client.session.prompt({
path: { id: sessionID }, path: { id: sessionID },
body: { body: {
agent: prevMessage?.agent, agent: prevMessage?.agent,
model: modelField,
parts: [{ type: "text", text: prompt }], parts: [{ type: "text", text: prompt }],
}, },
query: { directory: ctx.directory }, query: { directory: ctx.directory },