From 64be499d96342d0b214cd21327c438a55dfe5917 Mon Sep 17 00:00:00 2001 From: Rome Thorstenson Date: Fri, 30 Jan 2026 07:25:48 -0800 Subject: [PATCH] feat(security): integrate secret detection into message dispatch --- src/auto-reply/dispatch.ts | 53 +++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/auto-reply/dispatch.ts b/src/auto-reply/dispatch.ts index 13fc37a1c..39bec406a 100644 --- a/src/auto-reply/dispatch.ts +++ b/src/auto-reply/dispatch.ts @@ -11,6 +11,13 @@ import { type ReplyDispatcherOptions, type ReplyDispatcherWithTypingOptions, } from "./reply/reply-dispatcher.js"; +import { processSecretsInMessage } from "../security/process-secrets.js"; +import { + generatePromptKey, + hasPendingPrompt, + resolvePendingPrompt, + parsePromptResponse, +} from "../security/interactive-prompts.js"; export type DispatchInboundResult = DispatchFromConfigResult; @@ -22,8 +29,52 @@ export async function dispatchInboundMessage(params: { replyResolver?: typeof import("./reply.js").getReplyFromConfig; }): Promise { const finalized = finalizeInboundContext(params.ctx); + + // Check if this user has a pending security prompt + const channel = finalized.Provider || finalized.Surface || 'unknown'; + const senderId = finalized.SenderId || 'unknown'; + const promptKey = generatePromptKey(channel, senderId); + + if (hasPendingPrompt(promptKey)) { + // This message is a response to a security prompt + const body = finalized.Body || ''; + const parsedAction = parsePromptResponse(body); + + if (parsedAction) { + // Valid response - resolve the pending prompt + resolvePendingPrompt(promptKey, parsedAction); + // Send confirmation + await params.dispatcher.sendText(`✓ Applying action: ${parsedAction}`); + } else { + // Invalid response - send help message and keep the prompt pending + await params.dispatcher.sendText( + `❌ Invalid response. Please reply with **1**, **2**, **3**, or **4**.`, + ); + } + + // Don't process this message further + return { + handled: true, + replies: [], + errors: [], + }; + } + + // Process secrets before dispatching to agent + const secretProcessing = await processSecretsInMessage(finalized, params.cfg, params.dispatcher); + + // If the message was blocked by the user (cancelled), don't proceed + if (secretProcessing.blocked) { + return { + handled: true, + replies: [], + errors: [], + }; + } + + // Use the updated context (with redacted/replaced secrets if applicable) return await dispatchReplyFromConfig({ - ctx: finalized, + ctx: secretProcessing.ctx, cfg: params.cfg, dispatcher: params.dispatcher, replyOptions: params.replyOptions,