fix(cron): recover no-deliver tool warnings

This commit is contained in:
ai-hpc
2026-06-05 15:38:53 +02:00
committed by Shakker
parent 0176429ad7
commit 4eb4b87c8e
5 changed files with 74 additions and 4 deletions
@@ -45,6 +45,19 @@ describe("cron channel output policy", () => {
});
});
it("prefers final visible text only for unresolved no-delivery runs", async () => {
await expect(
resolveCronChannelOutputPolicy(undefined, { deliveryRequested: false }),
).resolves.toEqual({
preferFinalAssistantVisibleText: true,
});
await expect(
resolveCronChannelOutputPolicy(undefined, { deliveryRequested: true }),
).resolves.toEqual({
preferFinalAssistantVisibleText: false,
});
});
it("lets channel plugins format current tool context targets", async () => {
await expect(
resolveCurrentChannelTarget({
@@ -13,12 +13,15 @@ async function loadChannelPluginRuntime() {
}
/** Resolves channel-specific cron output preferences from loaded channel plugins. */
export async function resolveCronChannelOutputPolicy(channel: string | undefined): Promise<{
export async function resolveCronChannelOutputPolicy(
channel: string | undefined,
opts?: { deliveryRequested?: boolean },
): Promise<{
preferFinalAssistantVisibleText: boolean;
}> {
const channelId = normalizeOptionalLowercaseString(channel);
if (!channelId) {
return { preferFinalAssistantVisibleText: false };
return { preferFinalAssistantVisibleText: opts?.deliveryRequested === false };
}
const { getChannelPlugin } = await loadChannelPluginRuntime();
return {
+6 -1
View File
@@ -141,6 +141,7 @@ export function createCronPromptExecutor(params: {
to?: string;
threadId?: string | number;
};
deliveryRequested?: boolean;
sourceDelivery: SourceDeliveryPlan;
skillsSnapshot: SkillSnapshot;
agentPayload: AgentTurnPayload;
@@ -378,6 +379,7 @@ export async function executeCronRun(params: {
to?: string;
threadId?: string | number;
};
deliveryRequested?: boolean;
sourceDelivery: SourceDeliveryPlan;
skillsSnapshot: SkillSnapshot;
agentPayload: AgentTurnPayload;
@@ -429,6 +431,7 @@ export async function executeCronRun(params: {
runTimeoutOverrideMs: params.runTimeoutOverrideMs,
suppressExecNotifyOnExit: params.suppressExecNotifyOnExit,
resolvedDelivery: params.resolvedDelivery,
deliveryRequested: params.deliveryRequested,
sourceDelivery: params.sourceDelivery,
skillsSnapshot: params.skillsSnapshot,
agentPayload: params.agentPayload,
@@ -501,7 +504,9 @@ export async function executeCronRun(params: {
failureSignal: runResult.meta?.failureSignal,
finalAssistantVisibleText: runResult.meta?.finalAssistantVisibleText,
preferFinalAssistantVisibleText: (
await resolveCronChannelOutputPolicy(params.resolvedDelivery.channel)
await resolveCronChannelOutputPolicy(params.resolvedDelivery.channel, {
deliveryRequested: params.deliveryRequested,
})
).preferFinalAssistantVisibleText,
});
const interimText = interimOutputText?.trim() ?? "";
@@ -395,6 +395,52 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
expect(embeddedRun.messageTo).toBeUndefined();
});
it("uses final assistant text to recover tool warnings for bare no-deliver runs", async () => {
mockRunCronFallbackPassthrough();
resolveCronDeliveryPlanMock.mockReturnValue({
requested: false,
mode: "none",
});
resolveCronPayloadOutcomeMock.mockReturnValue({
summary: "Final cron report from the agent.",
outputText: "Final cron report from the agent.",
synthesizedText: "Final cron report from the agent.",
deliveryPayload: { text: "Final cron report from the agent." },
deliveryPayloads: [{ text: "Final cron report from the agent." }],
deliveryPayloadHasStructuredContent: false,
hasFatalErrorPayload: false,
hasFatalStructuredErrorPayload: false,
embeddedRunError: undefined,
});
runEmbeddedAgentMock.mockResolvedValue({
payloads: [{ text: "⚠️ 🛠️ show > (agent) failed", isError: true }],
meta: {
finalAssistantVisibleText: "Final cron report from the agent.",
agentMeta: { usage: { input: 10, output: 20 } },
},
});
const result = await runCronIsolatedAgentTurn({
...makeParams(),
job: makeMessageToolPolicyJob({ mode: "none" }),
});
expect(result.status).toBe("ok");
expect(result.error).toBeUndefined();
expect(result.summary).toBe("Final cron report from the agent.");
expect(result.outputText).toBe("Final cron report from the agent.");
expect(resolveCronPayloadOutcomeMock).toHaveBeenCalledWith(
expect.objectContaining({
finalAssistantVisibleText: "Final cron report from the agent.",
preferFinalAssistantVisibleText: true,
}),
);
expectDispatchFields({
deliveryRequested: false,
deliveryPayloads: [{ text: "Final cron report from the agent." }],
});
});
it('suppresses automatic exec completion notifications when delivery.mode is "none"', async () => {
mockRunCronFallbackPassthrough();
resolveCronDeliveryPlanMock.mockReturnValue({
+4 -1
View File
@@ -1049,7 +1049,9 @@ async function finalizeCronRun(params: {
failureSignal: finalRunResult.meta?.failureSignal,
finalAssistantVisibleText: finalRunResult.meta?.finalAssistantVisibleText,
preferFinalAssistantVisibleText: (
await resolveCronChannelOutputPolicy(prepared.resolvedDelivery.channel)
await resolveCronChannelOutputPolicy(prepared.resolvedDelivery.channel, {
deliveryRequested: prepared.deliveryRequested,
})
).preferFinalAssistantVisibleText,
});
const {
@@ -1323,6 +1325,7 @@ export async function runCronIsolatedAgentTurn(params: {
accountId: prepared.context.resolvedDelivery.accountId,
threadId: prepared.context.resolvedDelivery.threadId,
},
deliveryRequested: prepared.context.deliveryRequested,
sourceDelivery: prepared.context.sourceDelivery,
skillsSnapshot: prepared.context.skillsSnapshot,
agentPayload: prepared.context.agentPayload,