fix: deliver cron output to explicit targets (#16360) (thanks @rubyrunsstuff)
This commit is contained in:
@@ -42,6 +42,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/BlueBubbles: reject ambiguous shared-path webhook routing when multiple webhook targets match the same guid/password.
|
||||
- Security/BlueBubbles: require explicit `mediaLocalRoots` allowlists for local outbound media path reads to prevent local file disclosure. (#16322) Thanks @mbelinky.
|
||||
- Cron/Slack: preserve agent identity (name and icon) when cron jobs deliver outbound messages. (#16242) Thanks @robbyczgw-cla.
|
||||
- Cron: deliver text-only output directly when `delivery.to` is set so cron recipients get full output instead of summaries. (#16360) Thanks @rubyrunsstuff.
|
||||
- Cron: repair missing/corrupt `nextRunAtMs` for the updated job without globally recomputing unrelated due jobs during `cron update`. (#15750)
|
||||
- Discord: prefer gateway guild id when logging inbound messages so cached-miss guilds do not appear as `guild=dm`. Thanks @thewilloftheshadow.
|
||||
- TUI: refactor searchable select list description layout and add regression coverage for ANSI-highlight width bounds.
|
||||
|
||||
@@ -46,8 +46,9 @@ async function writeSessionStore(home: string) {
|
||||
"agent:main:main": {
|
||||
sessionId: "main-session",
|
||||
updatedAt: Date.now(),
|
||||
lastProvider: "webchat",
|
||||
lastTo: "",
|
||||
lastProvider: "telegram",
|
||||
lastChannel: "telegram",
|
||||
lastTo: "123",
|
||||
},
|
||||
},
|
||||
null,
|
||||
@@ -184,7 +185,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
kind: "agentTurn",
|
||||
message: "do it",
|
||||
}),
|
||||
delivery: { mode: "announce", channel: "telegram", to: "123" },
|
||||
delivery: { mode: "announce", channel: "last" },
|
||||
},
|
||||
message: "do it",
|
||||
sessionKey: "cron:job-1",
|
||||
@@ -210,7 +211,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
message: "do it",
|
||||
}),
|
||||
deleteAfterRun: true,
|
||||
delivery: { mode: "announce", channel: "telegram", to: "123" },
|
||||
delivery: { mode: "announce", channel: "last" },
|
||||
},
|
||||
message: "do it",
|
||||
sessionKey: "cron:job-1",
|
||||
|
||||
@@ -102,7 +102,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("announces via shared subagent flow when delivery is requested", async () => {
|
||||
it("delivers directly when delivery has an explicit target", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const storePath = await writeSessionStore(home);
|
||||
const deps: CliDeps = {
|
||||
@@ -136,16 +136,15 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
|
||||
expect(res.status).toBe("ok");
|
||||
expect(res.delivered).toBe(true);
|
||||
expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(1);
|
||||
const announceArgs = vi.mocked(runSubagentAnnounceFlow).mock.calls[0]?.[0] as
|
||||
| { announceType?: string }
|
||||
| undefined;
|
||||
expect(announceArgs?.announceType).toBe("cron job");
|
||||
expect(deps.sendMessageTelegram).not.toHaveBeenCalled();
|
||||
expect(runSubagentAnnounceFlow).not.toHaveBeenCalled();
|
||||
expect(deps.sendMessageTelegram).toHaveBeenCalledTimes(1);
|
||||
const [to, text] = vi.mocked(deps.sendMessageTelegram).mock.calls[0] ?? [];
|
||||
expect(to).toBe("123");
|
||||
expect(text).toBe("hello from cron");
|
||||
});
|
||||
});
|
||||
|
||||
it("passes final payload text into shared subagent announce flow", async () => {
|
||||
it("delivers the final payload text when delivery has an explicit target", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const storePath = await writeSessionStore(home);
|
||||
const deps: CliDeps = {
|
||||
@@ -178,12 +177,12 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
});
|
||||
|
||||
expect(res.status).toBe("ok");
|
||||
expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(1);
|
||||
const announceArgs = vi.mocked(runSubagentAnnounceFlow).mock.calls[0]?.[0] as
|
||||
| { roundOneReply?: string; requesterOrigin?: { threadId?: string | number } }
|
||||
| undefined;
|
||||
expect(announceArgs?.roundOneReply).toBe("Final weather summary");
|
||||
expect(announceArgs?.requesterOrigin?.threadId).toBeUndefined();
|
||||
expect(res.delivered).toBe(true);
|
||||
expect(runSubagentAnnounceFlow).not.toHaveBeenCalled();
|
||||
expect(deps.sendMessageTelegram).toHaveBeenCalledTimes(1);
|
||||
const [to, text] = vi.mocked(deps.sendMessageTelegram).mock.calls[0] ?? [];
|
||||
expect(to).toBe("123");
|
||||
expect(text).toBe("Final weather summary");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -237,6 +236,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
});
|
||||
|
||||
expect(res.status).toBe("ok");
|
||||
expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(1);
|
||||
const announceArgs = vi.mocked(runSubagentAnnounceFlow).mock.calls[0]?.[0] as
|
||||
| { requesterOrigin?: { threadId?: string | number; channel?: string; to?: string } }
|
||||
| undefined;
|
||||
@@ -369,7 +369,7 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("fails when shared announce flow fails and best-effort is disabled", async () => {
|
||||
it("fails when direct delivery fails and best-effort is disabled", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const storePath = await writeSessionStore(home);
|
||||
const deps: CliDeps = {
|
||||
@@ -386,7 +386,6 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
vi.mocked(runSubagentAnnounceFlow).mockResolvedValue(false);
|
||||
const res = await runCronIsolatedAgentTurn({
|
||||
cfg: makeCfg(home, storePath, {
|
||||
channels: { telegram: { botToken: "t-1" } },
|
||||
@@ -402,11 +401,13 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
});
|
||||
|
||||
expect(res.status).toBe("error");
|
||||
expect(res.error).toBe("cron announce delivery failed");
|
||||
expect(res.error).toContain("boom");
|
||||
expect(runSubagentAnnounceFlow).not.toHaveBeenCalled();
|
||||
expect(deps.sendMessageTelegram).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores shared announce flow failures when best-effort is enabled", async () => {
|
||||
it("ignores direct delivery failures when best-effort is enabled", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const storePath = await writeSessionStore(home);
|
||||
const deps: CliDeps = {
|
||||
@@ -423,7 +424,6 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
});
|
||||
vi.mocked(runSubagentAnnounceFlow).mockResolvedValue(false);
|
||||
const res = await runCronIsolatedAgentTurn({
|
||||
cfg: makeCfg(home, storePath, {
|
||||
channels: { telegram: { botToken: "t-1" } },
|
||||
@@ -444,8 +444,9 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
});
|
||||
|
||||
expect(res.status).toBe("ok");
|
||||
expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(1);
|
||||
expect(deps.sendMessageTelegram).not.toHaveBeenCalled();
|
||||
expect(res.delivered).toBe(false);
|
||||
expect(runSubagentAnnounceFlow).not.toHaveBeenCalled();
|
||||
expect(deps.sendMessageTelegram).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -558,9 +558,12 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
}
|
||||
const identity = resolveAgentOutboundIdentity(cfgWithAgentDefaults, agentId);
|
||||
|
||||
// Shared subagent announce flow is text-based. When we have an explicit sender
|
||||
// identity to preserve, prefer direct outbound delivery even for plain-text payloads.
|
||||
if (deliveryPayloadHasStructuredContent || identity) {
|
||||
// Shared subagent announce flow is text-based and prompts the main agent to
|
||||
// summarize. When we have an explicit delivery target (delivery.to), sender
|
||||
// identity, or structured content, prefer direct outbound delivery to send
|
||||
// the actual cron output without summarization.
|
||||
const hasExplicitDeliveryTarget = Boolean(deliveryPlan.to);
|
||||
if (deliveryPayloadHasStructuredContent || identity || hasExplicitDeliveryTarget) {
|
||||
try {
|
||||
const payloadsForDelivery =
|
||||
deliveryPayloadHasStructuredContent && deliveryPayloads.length > 0
|
||||
|
||||
Reference in New Issue
Block a user