Agents: surface tool failures without assistant output

This commit is contained in:
vrknetha
2026-01-18 18:35:03 +05:30
parent e944f21ec0
commit 65710932ff
9 changed files with 124 additions and 0 deletions

View File

@@ -431,6 +431,7 @@ export async function runEmbeddedPiAgent(
assistantTexts: attempt.assistantTexts,
toolMetas: attempt.toolMetas,
lastAssistant: attempt.lastAssistant,
lastToolError: attempt.lastToolError,
config: params.config,
sessionKey: params.sessionKey ?? params.sessionId,
verboseLevel: params.verboseLevel,

View File

@@ -433,6 +433,7 @@ export async function runEmbeddedAttempt(
getMessagingToolSentTexts,
getMessagingToolSentTargets,
didSendViaMessagingTool,
getLastToolError,
} = subscription;
const queueHandle: EmbeddedPiQueueHandle = {
@@ -665,6 +666,7 @@ export async function runEmbeddedAttempt(
assistantTexts,
toolMetas: toolMetasNormalized,
lastAssistant,
lastToolError: getLastToolError?.(),
didSendViaMessagingTool: didSendViaMessagingTool(),
messagingToolSentTexts: getMessagingToolSentTexts(),
messagingToolSentTargets: getMessagingToolSentTargets(),

View File

@@ -111,4 +111,40 @@ describe("buildEmbeddedRunPayloads", () => {
expect(payloads).toHaveLength(1);
expect(payloads[0]?.text).toBe(errorJsonPretty.trim());
});
it("adds a fallback error when a tool fails and no assistant output exists", () => {
const payloads = buildEmbeddedRunPayloads({
assistantTexts: [],
toolMetas: [],
lastAssistant: undefined,
lastToolError: { toolName: "browser", error: "tab not found" },
sessionKey: "session:telegram",
inlineToolResultsAllowed: false,
verboseLevel: "off",
reasoningLevel: "off",
toolResultFormat: "plain",
});
expect(payloads).toHaveLength(1);
expect(payloads[0]?.isError).toBe(true);
expect(payloads[0]?.text).toContain("browser");
expect(payloads[0]?.text).toContain("tab not found");
});
it("does not add tool error fallback when assistant output exists", () => {
const payloads = buildEmbeddedRunPayloads({
assistantTexts: ["All good"],
toolMetas: [],
lastAssistant: { stopReason: "end_turn" } as AssistantMessage,
lastToolError: { toolName: "browser", error: "tab not found" },
sessionKey: "session:telegram",
inlineToolResultsAllowed: false,
verboseLevel: "off",
reasoningLevel: "off",
toolResultFormat: "plain",
});
expect(payloads).toHaveLength(1);
expect(payloads[0]?.text).toBe("All good");
});
});

View File

@@ -23,6 +23,7 @@ export function buildEmbeddedRunPayloads(params: {
assistantTexts: string[];
toolMetas: ToolMetaEntry[];
lastAssistant: AssistantMessage | undefined;
lastToolError?: { toolName: string; meta?: string; error?: string };
config?: ClawdbotConfig;
sessionKey: string;
verboseLevel?: VerboseLevel;
@@ -155,6 +156,19 @@ export function buildEmbeddedRunPayloads(params: {
});
}
if (replyItems.length === 0 && params.lastToolError) {
const toolSummary = formatToolAggregate(
params.lastToolError.toolName,
params.lastToolError.meta ? [params.lastToolError.meta] : undefined,
{ markdown: useMarkdown },
);
const errorSuffix = params.lastToolError.error ? `: ${params.lastToolError.error}` : "";
replyItems.push({
text: `⚠️ ${toolSummary} failed${errorSuffix}`,
isError: true,
});
}
const hasAudioAsVoiceTag = replyItems.some((item) => item.audioAsVoice);
return replyItems
.map((item) => ({

View File

@@ -74,6 +74,7 @@ export type EmbeddedRunAttemptResult = {
assistantTexts: string[];
toolMetas: Array<{ toolName: string; meta?: string }>;
lastAssistant: AssistantMessage | undefined;
lastToolError?: { toolName: string; meta?: string; error?: string };
didSendViaMessagingTool: boolean;
messagingToolSentTexts: string[];
messagingToolSentTargets: MessagingToolSend[];