diff --git a/plugin/index.ts b/plugin/index.ts index 7f8ce75..2c2f727 100644 --- a/plugin/index.ts +++ b/plugin/index.ts @@ -69,30 +69,29 @@ function extractUntrustedConversationInfo(text: string): Record } } -function deriveDecisionInputFromAgentCtx( - ctx: Record, -): { channel: string; channelId?: string; senderId?: string; content: string } { - const channel = normalizeChannel(ctx); - const content = typeof ctx.input === "string" ? ctx.input : ""; - const conv = extractUntrustedConversationInfo(content) || {}; - const channelIdRaw = - (typeof ctx.channelId === "string" && ctx.channelId) || +function deriveDecisionInputFromPrompt( + prompt: string, + messageProvider?: string, +): { + channel: string; + channelId?: string; + senderId?: string; + content: string; + conv: Record; +} { + const conv = extractUntrustedConversationInfo(prompt) || {}; + const channel = (messageProvider || "").toLowerCase(); + const channelId = (typeof conv.channel_id === "string" && conv.channel_id) || (typeof conv.chat_id === "string" && conv.chat_id.startsWith("channel:") ? conv.chat_id.slice("channel:".length) : undefined); - const senderIdRaw = - (typeof ctx.senderId === "string" && ctx.senderId) || + const senderId = (typeof conv.sender_id === "string" && conv.sender_id) || (typeof conv.sender === "string" && conv.sender) || undefined; - return { - channel, - channelId: channelIdRaw, - senderId: senderIdRaw, - content, - }; + return { channel, channelId, senderId, content: prompt, conv }; } function pruneDecisionMap(now = Date.now()) { @@ -363,41 +362,32 @@ export default { const live = getLivePluginConfig(api, baseConfig as WhisperGateConfig) as WhisperGateConfig & DebugConfig; ensurePolicyStateLoaded(api, live); - // In before_model_resolve, ctx only has: agentId, sessionKey, sessionId, workspaceDir, messageProvider. - // senderId/channelId/input are NOT available. Use event.prompt (user message incl. untrusted metadata). - const channel = (ctx.messageProvider || "").toLowerCase(); - if (live.discordOnly !== false && channel !== "discord") return; - const prompt = ((event as Record).prompt as string) || ""; - const conv = extractUntrustedConversationInfo(prompt) || {}; - const senderId = - (typeof conv.sender_id === "string" && conv.sender_id) || - (typeof conv.sender === "string" && conv.sender) || - undefined; - const channelId = - (typeof conv.channel_id === "string" && conv.channel_id) || - (typeof (conv as Record).conversation_label === "string" - ? undefined - : undefined); + const derived = deriveDecisionInputFromPrompt(prompt, ctx.messageProvider); + if (live.discordOnly !== false && derived.channel !== "discord") return; let rec = sessionDecision.get(key); if (!rec || Date.now() - rec.createdAt > DECISION_TTL_MS) { if (rec) sessionDecision.delete(key); const decision = evaluateDecision({ config: live, - channel, - channelId, + channel: derived.channel, + channelId: derived.channelId, channelPolicies: policyState.channelPolicies, - senderId, - content: prompt, + senderId: derived.senderId, + content: derived.content, }); rec = { decision, createdAt: Date.now() }; sessionDecision.set(key, rec); pruneDecisionMap(); - if (shouldDebugLog(live, channelId)) { + if (shouldDebugLog(live, derived.channelId)) { api.logger.info( - `whispergate: debug before_model_resolve recompute session=${key} senderId=${senderId} decision=${decision.reason} ` + - `shouldNoReply=${decision.shouldUseNoReply} shouldInject=${decision.shouldInjectEndMarkerPrompt}`, + `whispergate: debug before_model_resolve recompute session=${key} ` + + `channel=${derived.channel} channelId=${derived.channelId ?? ""} senderId=${derived.senderId ?? ""} ` + + `convSenderId=${String((derived.conv as Record).sender_id ?? "")} ` + + `convSender=${String((derived.conv as Record).sender ?? "")} ` + + `convChannelId=${String((derived.conv as Record).channel_id ?? "")} ` + + `decision=${decision.reason} shouldNoReply=${decision.shouldUseNoReply} shouldInject=${decision.shouldInjectEndMarkerPrompt}`, ); } } @@ -425,31 +415,26 @@ export default { if (!rec || Date.now() - rec.createdAt > DECISION_TTL_MS) { if (rec) sessionDecision.delete(key); - // before_prompt_build has event.prompt and event.messages available. - const channel = (ctx.messageProvider || "").toLowerCase(); const prompt = ((event as Record).prompt as string) || ""; - const conv = extractUntrustedConversationInfo(prompt) || {}; - const senderId = - (typeof conv.sender_id === "string" && conv.sender_id) || - (typeof conv.sender === "string" && conv.sender) || - undefined; - const channelId = - (typeof conv.channel_id === "string" && conv.channel_id) || - undefined; + const derived = deriveDecisionInputFromPrompt(prompt, ctx.messageProvider); const decision = evaluateDecision({ config: live, - channel, - channelId, + channel: derived.channel, + channelId: derived.channelId, channelPolicies: policyState.channelPolicies, - senderId, - content: prompt, + senderId: derived.senderId, + content: derived.content, }); rec = { decision, createdAt: Date.now() }; - if (shouldDebugLog(live, channelId)) { + if (shouldDebugLog(live, derived.channelId)) { api.logger.info( - `whispergate: debug before_prompt_build recompute session=${key} senderId=${senderId} decision=${decision.reason} ` + - `shouldNoReply=${decision.shouldUseNoReply} shouldInject=${decision.shouldInjectEndMarkerPrompt}`, + `whispergate: debug before_prompt_build recompute session=${key} ` + + `channel=${derived.channel} channelId=${derived.channelId ?? ""} senderId=${derived.senderId ?? ""} ` + + `convSenderId=${String((derived.conv as Record).sender_id ?? "")} ` + + `convSender=${String((derived.conv as Record).sender ?? "")} ` + + `convChannelId=${String((derived.conv as Record).channel_id ?? "")} ` + + `decision=${decision.reason} shouldNoReply=${decision.shouldUseNoReply} shouldInject=${decision.shouldInjectEndMarkerPrompt}`, ); } }