From 113eea5047c49e11b5d014fafcb9c2c7bab5c2c2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 14 Jan 2026 23:20:19 -0500 Subject: [PATCH] fix: mutate prefixContext object instead of reassigning for closure correctness --- src/auto-reply/reply/agent-runner-execution.ts | 3 --- src/auto-reply/reply/reply-dispatcher.ts | 8 -------- src/discord/monitor/message-handler.process.ts | 12 +++++------- src/imessage/monitor/monitor-provider.ts | 12 +++++------- src/signal/monitor/event-handler.ts | 12 +++++------- src/slack/monitor/message-handler/dispatch.ts | 12 +++++------- src/telegram/bot-message-dispatch.ts | 12 +++++------- src/web/auto-reply/monitor/process-message.ts | 12 +++++------- 8 files changed, 30 insertions(+), 53 deletions(-) diff --git a/src/auto-reply/reply/agent-runner-execution.ts b/src/auto-reply/reply/agent-runner-execution.ts index b580dcc4b..d1f0e276d 100644 --- a/src/auto-reply/reply/agent-runner-execution.ts +++ b/src/auto-reply/reply/agent-runner-execution.ts @@ -127,9 +127,6 @@ export async function runAgentTurnWithFallback(params: { run: (provider, model) => { // Notify that model selection is complete (including after fallback). // This allows responsePrefix template interpolation with the actual model. - logVerbose( - `[prefix-debug] onModelSelected firing: provider=${provider} model=${model} thinkLevel=${params.followupRun.run.thinkLevel}`, - ); params.opts?.onModelSelected?.({ provider, model, diff --git a/src/auto-reply/reply/reply-dispatcher.ts b/src/auto-reply/reply/reply-dispatcher.ts index 4a45161d8..b8eeca477 100644 --- a/src/auto-reply/reply/reply-dispatcher.ts +++ b/src/auto-reply/reply/reply-dispatcher.ts @@ -75,14 +75,6 @@ function normalizeReplyPayloadInternal( // Prefer dynamic context provider over static context const prefixContext = opts.responsePrefixContextProvider?.() ?? opts.responsePrefixContext; - // Debug logging for prefix template resolution - if (opts.responsePrefix?.includes("{")) { - // eslint-disable-next-line no-console - console.log( - `[prefix-debug] normalizing with context: ${JSON.stringify(prefixContext)} prefix: ${opts.responsePrefix}`, - ); - } - return normalizeReplyPayload(payload, { responsePrefix: opts.responsePrefix, responsePrefixContext: prefixContext, diff --git a/src/discord/monitor/message-handler.process.ts b/src/discord/monitor/message-handler.process.ts index 4758feb09..dae301d04 100644 --- a/src/discord/monitor/message-handler.process.ts +++ b/src/discord/monitor/message-handler.process.ts @@ -329,13 +329,11 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext) ? !discordConfig.blockStreaming : undefined, onModelSelected: (ctx) => { - prefixContext = { - ...prefixContext, - provider: ctx.provider, - model: extractShortModelName(ctx.model), - modelFull: `${ctx.provider}/${ctx.model}`, - thinkingLevel: ctx.thinkLevel ?? "off", - }; + // Mutate the object directly instead of reassigning to ensure the closure sees updates + prefixContext.provider = ctx.provider; + prefixContext.model = extractShortModelName(ctx.model); + prefixContext.modelFull = `${ctx.provider}/${ctx.model}`; + prefixContext.thinkingLevel = ctx.thinkLevel ?? "off"; }, }, }); diff --git a/src/imessage/monitor/monitor-provider.ts b/src/imessage/monitor/monitor-provider.ts index b8a408419..1d86ec993 100644 --- a/src/imessage/monitor/monitor-provider.ts +++ b/src/imessage/monitor/monitor-provider.ts @@ -386,13 +386,11 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P ? !accountInfo.config.blockStreaming : undefined, onModelSelected: (ctx) => { - prefixContext = { - ...prefixContext, - provider: ctx.provider, - model: extractShortModelName(ctx.model), - modelFull: `${ctx.provider}/${ctx.model}`, - thinkingLevel: ctx.thinkLevel ?? "off", - }; + // Mutate the object directly instead of reassigning to ensure the closure sees updates + prefixContext.provider = ctx.provider; + prefixContext.model = extractShortModelName(ctx.model); + prefixContext.modelFull = `${ctx.provider}/${ctx.model}`; + prefixContext.thinkingLevel = ctx.thinkLevel ?? "off"; }, }, }); diff --git a/src/signal/monitor/event-handler.ts b/src/signal/monitor/event-handler.ts index 7f093a3c1..0aac7c97b 100644 --- a/src/signal/monitor/event-handler.ts +++ b/src/signal/monitor/event-handler.ts @@ -354,13 +354,11 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) { disableBlockStreaming: typeof deps.blockStreaming === "boolean" ? !deps.blockStreaming : undefined, onModelSelected: (ctx) => { - prefixContext = { - ...prefixContext, - provider: ctx.provider, - model: extractShortModelName(ctx.model), - modelFull: `${ctx.provider}/${ctx.model}`, - thinkingLevel: ctx.thinkLevel ?? "off", - }; + // Mutate the object directly instead of reassigning to ensure the closure sees updates + prefixContext.provider = ctx.provider; + prefixContext.model = extractShortModelName(ctx.model); + prefixContext.modelFull = `${ctx.provider}/${ctx.model}`; + prefixContext.thinkingLevel = ctx.thinkLevel ?? "off"; }, }, }); diff --git a/src/slack/monitor/message-handler/dispatch.ts b/src/slack/monitor/message-handler/dispatch.ts index 836e85d46..fb0eb66b1 100644 --- a/src/slack/monitor/message-handler/dispatch.ts +++ b/src/slack/monitor/message-handler/dispatch.ts @@ -117,13 +117,11 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag ? !account.config.blockStreaming : undefined, onModelSelected: (ctx) => { - prefixContext = { - ...prefixContext, - provider: ctx.provider, - model: extractShortModelName(ctx.model), - modelFull: `${ctx.provider}/${ctx.model}`, - thinkingLevel: ctx.thinkLevel ?? "off", - }; + // Mutate the object directly instead of reassigning to ensure the closure sees updates + prefixContext.provider = ctx.provider; + prefixContext.model = extractShortModelName(ctx.model); + prefixContext.modelFull = `${ctx.provider}/${ctx.model}`; + prefixContext.thinkingLevel = ctx.thinkLevel ?? "off"; }, }, }); diff --git a/src/telegram/bot-message-dispatch.ts b/src/telegram/bot-message-dispatch.ts index bae515f4b..c609a07c2 100644 --- a/src/telegram/bot-message-dispatch.ts +++ b/src/telegram/bot-message-dispatch.ts @@ -162,13 +162,11 @@ export const dispatchTelegramMessage = async ({ : undefined, disableBlockStreaming, onModelSelected: (ctx) => { - prefixContext = { - ...prefixContext, - provider: ctx.provider, - model: extractShortModelName(ctx.model), - modelFull: `${ctx.provider}/${ctx.model}`, - thinkingLevel: ctx.thinkLevel ?? "off", - }; + // Mutate the object directly instead of reassigning to ensure the closure sees updates + prefixContext.provider = ctx.provider; + prefixContext.model = extractShortModelName(ctx.model); + prefixContext.modelFull = `${ctx.provider}/${ctx.model}`; + prefixContext.thinkingLevel = ctx.thinkLevel ?? "off"; }, }, }); diff --git a/src/web/auto-reply/monitor/process-message.ts b/src/web/auto-reply/monitor/process-message.ts index 0a57e6877..205fd5699 100644 --- a/src/web/auto-reply/monitor/process-message.ts +++ b/src/web/auto-reply/monitor/process-message.ts @@ -278,13 +278,11 @@ export async function processMessage(params: { ? !params.cfg.channels.whatsapp.blockStreaming : undefined, onModelSelected: (ctx) => { - prefixContext = { - ...prefixContext, - provider: ctx.provider, - model: extractShortModelName(ctx.model), - modelFull: `${ctx.provider}/${ctx.model}`, - thinkingLevel: ctx.thinkLevel ?? "off", - }; + // Mutate the object directly instead of reassigning to ensure the closure sees updates + prefixContext.provider = ctx.provider; + prefixContext.model = extractShortModelName(ctx.model); + prefixContext.modelFull = `${ctx.provider}/${ctx.model}`; + prefixContext.thinkingLevel = ctx.thinkLevel ?? "off"; }, }, });