From 580b59f6e67a8055416614fd520d18b6fc45ecda Mon Sep 17 00:00:00 2001 From: HelderSantos Date: Wed, 28 Jan 2026 21:28:22 -0300 Subject: [PATCH] =?UTF-8?q?auto-reply:=20adicionar=20notifica=C3=A7=C3=B5e?= =?UTF-8?q?s=20autom=C3=A1ticas=20de=20conclus=C3=A3o=20de=20tarefas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quando o Moltbot executa uma tarefa (via ferramenta) mas o modelo não gera uma resposta final, agora envia uma confirmação automática ao usuário. - Rastreia ferramentas executadas com sucesso via eventos tool - Gera confirmação '✅ Concluído' quando não há payloads mas há ferramentas executadas - Evita duplicação quando verbose está ligado (onToolResult já notifica) - Não envia confirmação para heartbeats --- .../reply/agent-runner-execution.ts | 13 +++++++ src/auto-reply/reply/agent-runner.ts | 35 +++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/auto-reply/reply/agent-runner-execution.ts b/src/auto-reply/reply/agent-runner-execution.ts index f86ecb8a9..70c8bb0a3 100644 --- a/src/auto-reply/reply/agent-runner-execution.ts +++ b/src/auto-reply/reply/agent-runner-execution.ts @@ -48,6 +48,8 @@ export type AgentRunLoopResult = autoCompactionCompleted: boolean; /** Payload keys sent directly (not via pipeline) during tool flush. */ directlySentBlockKeys?: Set; + /** Tool call IDs that were executed successfully (phase === "result" without error). */ + executedToolCallIds?: Set; } | { kind: "final"; payload: ReplyPayload }; @@ -82,6 +84,8 @@ export async function runAgentTurnWithFallback(params: { let autoCompactionCompleted = false; // Track payloads sent directly (not via pipeline) during tool flush to avoid duplicates. const directlySentBlockKeys = new Set(); + // Track tool calls that were executed successfully (completed with result phase). + const executedToolCallIds = new Set(); const runId = params.opts?.runId ?? crypto.randomUUID(); params.opts?.onAgentRunStart?.(runId); @@ -307,6 +311,14 @@ export async function runAgentTurnWithFallback(params: { if (phase === "start" || phase === "update") { await params.typingSignals.signalToolStart(); } + // Track successful tool execution (result phase without error). + if (phase === "result") { + const toolCallId = typeof evt.data.toolCallId === "string" ? evt.data.toolCallId : ""; + const isError = Boolean(evt.data.isError); + if (toolCallId && !isError) { + executedToolCallIds.add(toolCallId); + } + } } // Track auto-compaction completion if (evt.stream === "compaction") { @@ -554,5 +566,6 @@ export async function runAgentTurnWithFallback(params: { didLogHeartbeatStrip, autoCompactionCompleted, directlySentBlockKeys: directlySentBlockKeys.size > 0 ? directlySentBlockKeys : undefined, + executedToolCallIds: executedToolCallIds.size > 0 ? executedToolCallIds : undefined, }; } diff --git a/src/auto-reply/reply/agent-runner.ts b/src/auto-reply/reply/agent-runner.ts index 227e6f17e..30de62225 100644 --- a/src/auto-reply/reply/agent-runner.ts +++ b/src/auto-reply/reply/agent-runner.ts @@ -328,7 +328,13 @@ export async function runReplyAgent(params: { return finalizeWithFollowup(runOutcome.payload, queueKey, runFollowupTurn); } - const { runResult, fallbackProvider, fallbackModel, directlySentBlockKeys } = runOutcome; + const { + runResult, + fallbackProvider, + fallbackModel, + directlySentBlockKeys, + executedToolCallIds, + } = runOutcome; let { didLogHeartbeatStrip, autoCompactionCompleted } = runOutcome; if ( @@ -391,8 +397,33 @@ export async function runReplyAgent(params: { // Drain any late tool/block deliveries before deciding there's "nothing to send". // Otherwise, a late typing trigger (e.g. from a tool callback) can outlive the run and // keep the typing indicator stuck. - if (payloadArray.length === 0) + if (payloadArray.length === 0) { + // If tools were executed but no final response was generated, send a confirmation. + // Skip if verbose is enabled (onToolResult already sent notifications via tool callbacks) + // or if it's a heartbeat (which should be silent). + const hasExecutedTools = + executedToolCallIds && executedToolCallIds.size > 0; + const verboseEnabled = shouldEmitToolResult(); + // Only send confirmation if: + // 1. Tools were executed successfully + // 2. Verbose is disabled (onToolResult wasn't called, so no notifications were sent) + // 3. Not a heartbeat (heartbeats should be silent) + // 4. All pending tool tasks completed (to avoid race conditions) + if ( + hasExecutedTools && + !verboseEnabled && + !isHeartbeat && + pendingToolTasks.size === 0 + ) { + const confirmationPayload: ReplyPayload = { + text: "✅ Concluído", + }; + const taggedConfirmation = applyReplyToMode(confirmationPayload); + await signalTypingIfNeeded([taggedConfirmation], typingSignals); + return finalizeWithFollowup(taggedConfirmation, queueKey, runFollowupTurn); + } return finalizeWithFollowup(undefined, queueKey, runFollowupTurn); + } const payloadResult = buildReplyPayloads({ payloads: payloadArray,