mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
Suppress transient runner failures in channels (#87069)
This commit is contained in:
@@ -4854,6 +4854,95 @@ describe("runAgentTurnWithFallback", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["group", "channel"] as const)(
|
||||
"keeps classified non-transient failures visible in Discord %s chats",
|
||||
async (chatType) => {
|
||||
state.runEmbeddedPiAgentMock.mockRejectedValueOnce(
|
||||
new Error('No API key found for provider "openai"'),
|
||||
);
|
||||
|
||||
const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
|
||||
const result = await runAgentTurnWithFallback(
|
||||
createMinimalRunAgentTurnParams({
|
||||
sessionCtx: {
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
ChatType: chatType,
|
||||
GroupSubject: "agent group",
|
||||
GroupChannel: "#general",
|
||||
MessageSid: "msg",
|
||||
} as unknown as TemplateContext,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.kind).toBe("final");
|
||||
if (result.kind === "final") {
|
||||
expect(result.payload.text).not.toBe(SILENT_REPLY_TOKEN);
|
||||
expect(result.payload.text).toContain('Missing API key for provider "openai"');
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["group", "channel"] as const)(
|
||||
"keeps rate-limit fallback copy out of Discord %s chats",
|
||||
async (chatType) => {
|
||||
state.runEmbeddedPiAgentMock.mockRejectedValueOnce(new Error("429 rate limit exceeded"));
|
||||
|
||||
const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
|
||||
const result = await runAgentTurnWithFallback(
|
||||
createMinimalRunAgentTurnParams({
|
||||
sessionCtx: {
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
ChatType: chatType,
|
||||
GroupSubject: "agent group",
|
||||
GroupChannel: "#general",
|
||||
MessageSid: "msg",
|
||||
} as unknown as TemplateContext,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.kind).toBe("final");
|
||||
if (result.kind === "final") {
|
||||
expect(result.payload.text).toBe(SILENT_REPLY_TOKEN);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("surfaces rate-limit fallback copy in Discord group chats when silentReply.group is disallow", async () => {
|
||||
state.runEmbeddedPiAgentMock.mockRejectedValueOnce(new Error("429 rate limit exceeded"));
|
||||
|
||||
const followupRun = createFollowupRun();
|
||||
followupRun.run.config = {
|
||||
agents: {
|
||||
defaults: {
|
||||
silentReply: { group: "disallow" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
|
||||
const result = await runAgentTurnWithFallback(
|
||||
createMinimalRunAgentTurnParams({
|
||||
followupRun,
|
||||
sessionCtx: {
|
||||
Provider: "discord",
|
||||
Surface: "discord",
|
||||
ChatType: "group",
|
||||
GroupSubject: "agent group",
|
||||
GroupChannel: "#general",
|
||||
MessageSid: "msg",
|
||||
} as unknown as TemplateContext,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.kind).toBe("final");
|
||||
if (result.kind === "final") {
|
||||
expect(result.payload.text).not.toBe(SILENT_REPLY_TOKEN);
|
||||
expect(result.payload.text).toContain("rate-limited");
|
||||
}
|
||||
});
|
||||
|
||||
it("uses compact generic copy for raw runner failures in normal Discord direct chats", async () => {
|
||||
state.runEmbeddedPiAgentMock.mockRejectedValueOnce(
|
||||
new Error("openai-codex/gpt-5.5 ended with an incomplete terminal response"),
|
||||
|
||||
@@ -643,12 +643,17 @@ function resolveExternalRunFailureTextForConversation(params: {
|
||||
text: string;
|
||||
sessionCtx: TemplateContext;
|
||||
isGenericRunnerFailure: boolean;
|
||||
suppressInNonDirect?: boolean;
|
||||
cfg?: OpenClawConfig;
|
||||
}): string {
|
||||
if (!isNonDirectConversationContext(params.sessionCtx)) {
|
||||
return params.text;
|
||||
}
|
||||
if (!params.isGenericRunnerFailure && !params.text.includes(AGENT_FAILED_BEFORE_REPLY_TEXT)) {
|
||||
if (
|
||||
!params.suppressInNonDirect &&
|
||||
!params.isGenericRunnerFailure &&
|
||||
!params.text.includes(AGENT_FAILED_BEFORE_REPLY_TEXT)
|
||||
) {
|
||||
return params.text;
|
||||
}
|
||||
// Match normal reply routing: default group/channel failures stay silent,
|
||||
@@ -826,6 +831,7 @@ export function buildKnownAgentRunFailureReplyPayload(params: {
|
||||
text: buildRateLimitCooldownMessage(params.err),
|
||||
sessionCtx: params.sessionCtx,
|
||||
isGenericRunnerFailure: false,
|
||||
suppressInNonDirect: true,
|
||||
cfg: params.cfg,
|
||||
}),
|
||||
});
|
||||
@@ -837,6 +843,7 @@ export function buildKnownAgentRunFailureReplyPayload(params: {
|
||||
text: rateLimitOrOverloadedCopy,
|
||||
sessionCtx: params.sessionCtx,
|
||||
isGenericRunnerFailure: false,
|
||||
suppressInNonDirect: true,
|
||||
cfg: params.cfg,
|
||||
}),
|
||||
});
|
||||
@@ -2806,6 +2813,7 @@ export async function runAgentTurnWithFallback(params: {
|
||||
text: fallbackText,
|
||||
sessionCtx: params.sessionCtx,
|
||||
isGenericRunnerFailure: externalRunFailureReply?.isGenericRunnerFailure ?? false,
|
||||
suppressInNonDirect: Boolean(isRateLimit || rateLimitOrOverloadedCopy),
|
||||
cfg: params.followupRun.run.config,
|
||||
});
|
||||
|
||||
@@ -2870,7 +2878,13 @@ export async function runAgentTurnWithFallback(params: {
|
||||
if (formattedErrorCandidate) {
|
||||
runResult.payloads = [
|
||||
markAgentRunFailureReplyPayload({
|
||||
text: formattedErrorCandidate,
|
||||
text: resolveExternalRunFailureTextForConversation({
|
||||
text: formattedErrorCandidate,
|
||||
sessionCtx: params.sessionCtx,
|
||||
isGenericRunnerFailure: false,
|
||||
suppressInNonDirect: true,
|
||||
cfg: params.followupRun.run.config,
|
||||
}),
|
||||
isError: true,
|
||||
}),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user