fix(agents): ignore failed subagent placeholders

This commit is contained in:
Vincent Koc
2026-05-27 09:31:35 +02:00
parent 2c3190d9de
commit 31ecbbd5bf
2 changed files with 58 additions and 1 deletions

View File

@@ -1003,6 +1003,41 @@ describe("deliverSubagentAnnouncement completion delivery", () => {
);
});
it("does not directly deliver failed subagent placeholder output", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sendMessage,
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "direct completion smoke",
status: "error",
statusLabel: "failed: all models failed",
result: "(no output)",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: false,
path: "direct",
error: "completion agent did not produce a visible reply",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("directly delivers unprefixed direct targets recognized by the channel grammar", async () => {
registerDirectTargetTestChannel("qa-channel");
const callGateway = createGatewayMock({

View File

@@ -767,14 +767,29 @@ function resolveTextCompletionDirectFallback(events: readonly AgentInternalEvent
if (event?.type !== "task_completion" || event.source !== "subagent") {
continue;
}
if (event.status !== "ok") {
continue;
}
const result = typeof event.result === "string" ? event.result.trim() : "";
if (result) {
if (result && result !== "(no output)") {
return result;
}
}
return undefined;
}
function hasFailedSubagentNoOutputCompletion(events: readonly AgentInternalEvent[] | undefined) {
return (
events?.some(
(event) =>
event.type === "task_completion" &&
event.source === "subagent" &&
event.status !== "ok" &&
event.result.trim() === "(no output)",
) === true
);
}
async function deliverTextCompletionDirect(params: {
cfg: OpenClawConfig;
requesterSessionKey: string;
@@ -1190,6 +1205,13 @@ async function sendSubagentAnnounceDirectly(params: {
if (textDelivery) {
return textDelivery;
}
if (hasFailedSubagentNoOutputCompletion(params.internalEvents)) {
return {
delivered: false,
path: "direct",
error: "completion agent did not produce a visible reply",
};
}
}
if (
params.expectsCompletionMessage &&