Merge 580b59f6e6 into 4583f88626
This commit is contained in:
commit
215d2f9db5
@ -48,6 +48,8 @@ export type AgentRunLoopResult =
|
|||||||
autoCompactionCompleted: boolean;
|
autoCompactionCompleted: boolean;
|
||||||
/** Payload keys sent directly (not via pipeline) during tool flush. */
|
/** Payload keys sent directly (not via pipeline) during tool flush. */
|
||||||
directlySentBlockKeys?: Set<string>;
|
directlySentBlockKeys?: Set<string>;
|
||||||
|
/** Tool call IDs that were executed successfully (phase === "result" without error). */
|
||||||
|
executedToolCallIds?: Set<string>;
|
||||||
}
|
}
|
||||||
| { kind: "final"; payload: ReplyPayload };
|
| { kind: "final"; payload: ReplyPayload };
|
||||||
|
|
||||||
@ -82,6 +84,8 @@ export async function runAgentTurnWithFallback(params: {
|
|||||||
let autoCompactionCompleted = false;
|
let autoCompactionCompleted = false;
|
||||||
// Track payloads sent directly (not via pipeline) during tool flush to avoid duplicates.
|
// Track payloads sent directly (not via pipeline) during tool flush to avoid duplicates.
|
||||||
const directlySentBlockKeys = new Set<string>();
|
const directlySentBlockKeys = new Set<string>();
|
||||||
|
// Track tool calls that were executed successfully (completed with result phase).
|
||||||
|
const executedToolCallIds = new Set<string>();
|
||||||
|
|
||||||
const runId = params.opts?.runId ?? crypto.randomUUID();
|
const runId = params.opts?.runId ?? crypto.randomUUID();
|
||||||
params.opts?.onAgentRunStart?.(runId);
|
params.opts?.onAgentRunStart?.(runId);
|
||||||
@ -307,6 +311,14 @@ export async function runAgentTurnWithFallback(params: {
|
|||||||
if (phase === "start" || phase === "update") {
|
if (phase === "start" || phase === "update") {
|
||||||
await params.typingSignals.signalToolStart();
|
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
|
// Track auto-compaction completion
|
||||||
if (evt.stream === "compaction") {
|
if (evt.stream === "compaction") {
|
||||||
@ -554,5 +566,6 @@ export async function runAgentTurnWithFallback(params: {
|
|||||||
didLogHeartbeatStrip,
|
didLogHeartbeatStrip,
|
||||||
autoCompactionCompleted,
|
autoCompactionCompleted,
|
||||||
directlySentBlockKeys: directlySentBlockKeys.size > 0 ? directlySentBlockKeys : undefined,
|
directlySentBlockKeys: directlySentBlockKeys.size > 0 ? directlySentBlockKeys : undefined,
|
||||||
|
executedToolCallIds: executedToolCallIds.size > 0 ? executedToolCallIds : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -328,7 +328,13 @@ export async function runReplyAgent(params: {
|
|||||||
return finalizeWithFollowup(runOutcome.payload, queueKey, runFollowupTurn);
|
return finalizeWithFollowup(runOutcome.payload, queueKey, runFollowupTurn);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { runResult, fallbackProvider, fallbackModel, directlySentBlockKeys } = runOutcome;
|
const {
|
||||||
|
runResult,
|
||||||
|
fallbackProvider,
|
||||||
|
fallbackModel,
|
||||||
|
directlySentBlockKeys,
|
||||||
|
executedToolCallIds,
|
||||||
|
} = runOutcome;
|
||||||
let { didLogHeartbeatStrip, autoCompactionCompleted } = runOutcome;
|
let { didLogHeartbeatStrip, autoCompactionCompleted } = runOutcome;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -391,8 +397,33 @@ export async function runReplyAgent(params: {
|
|||||||
// Drain any late tool/block deliveries before deciding there's "nothing to send".
|
// 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
|
// Otherwise, a late typing trigger (e.g. from a tool callback) can outlive the run and
|
||||||
// keep the typing indicator stuck.
|
// 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);
|
return finalizeWithFollowup(undefined, queueKey, runFollowupTurn);
|
||||||
|
}
|
||||||
|
|
||||||
const payloadResult = buildReplyPayloads({
|
const payloadResult = buildReplyPayloads({
|
||||||
payloads: payloadArray,
|
payloads: payloadArray,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user