Skip to content

Commit fb4395c

Browse files
fix(cron): preserve all fields in announce delivery by removing summarization instruction (openclaw#65638)
* fix(cron): preserve all fields in announce delivery by removing summarization instruction The delivery instruction appended to the cron agent prompt contained the word 'summary', causing LLMs to condense structured output non-deterministically and drop fields on delivery. Replace with 'response' and add explicit instruction to reproduce all fields exactly. Fixes openclaw#58535 * chore(changelog): add cron announce entry --------- Co-authored-by: Vincent Koc <[email protected]>
1 parent ea4889e commit fb4395c

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ Docs: https://docs.openclaw.ai
244244
- Agents/failover: scope assistant-side fallback classification and surfaced provider errors to the current attempt instead of stale session history, so cross-provider fallback runs stop inheriting the previous provider's failure. (#62907) Thanks @stainlu.
245245
- MiniMax/OAuth: write `api: "anthropic-messages"` and `authHeader: true` into the `minimax-portal` config patch during `openclaw configure`, so re-authenticated portal setups keep Bearer auth routing working. (#64964) Thanks @ryanlee666.
246246
- Agents/tools: stop repeated unavailable-tool retries from escaping loop detection when the model changes arguments, and rewrite over-threshold unknown tool calls into plain assistant text before dispatch. (#65922) Thanks @dutifulbob.
247+
- Cron/announce delivery: tell isolated cron jobs to return the full response exactly instead of a summary, so structured `--announce` deliveries stop dropping fields nondeterministically. (#65638) Thanks @srinivaspavan9 and @vincentkoc.
247248

248249
## 2026.4.10
249250

src/cron/isolated-agent/run.message-tool-policy.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,71 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
165165
);
166166
});
167167
});
168+
169+
describe("runCronIsolatedAgentTurn delivery instruction", () => {
170+
let previousFastTestEnv: string | undefined;
171+
172+
beforeEach(() => {
173+
previousFastTestEnv = clearFastTestEnv();
174+
resetRunCronIsolatedAgentTurnHarness();
175+
resolveDeliveryTargetMock.mockResolvedValue({
176+
ok: true,
177+
channel: "telegram",
178+
to: "123",
179+
accountId: undefined,
180+
error: undefined,
181+
});
182+
});
183+
184+
afterEach(() => {
185+
restoreFastTestEnv(previousFastTestEnv);
186+
});
187+
188+
it("appends a plain-text delivery instruction to the prompt when delivery is requested", async () => {
189+
mockRunCronFallbackPassthrough();
190+
resolveCronDeliveryPlanMock.mockReturnValue({
191+
requested: true,
192+
mode: "announce",
193+
channel: "telegram",
194+
to: "123",
195+
});
196+
197+
await runCronIsolatedAgentTurn(makeParams());
198+
199+
expect(runEmbeddedPiAgentMock).toHaveBeenCalledTimes(1);
200+
const prompt: string = runEmbeddedPiAgentMock.mock.calls[0]?.[0]?.prompt ?? "";
201+
expect(prompt).toContain("Return your response as plain text");
202+
expect(prompt).toContain("it will be delivered automatically");
203+
});
204+
205+
it("does not append a delivery instruction when delivery is not requested", async () => {
206+
mockRunCronFallbackPassthrough();
207+
resolveCronDeliveryPlanMock.mockReturnValue({ requested: false, mode: "none" });
208+
209+
await runCronIsolatedAgentTurn(makeParams());
210+
211+
expect(runEmbeddedPiAgentMock).toHaveBeenCalledTimes(1);
212+
const prompt: string = runEmbeddedPiAgentMock.mock.calls[0]?.[0]?.prompt ?? "";
213+
expect(prompt).not.toContain("Return your response as plain text");
214+
expect(prompt).not.toContain("it will be delivered automatically");
215+
});
216+
217+
it("does not instruct the agent to summarize when delivery is requested", async () => {
218+
// Regression for https://github.com/openclaw/openclaw/issues/58535:
219+
// "summary" caused LLMs to condense structured output and drop fields
220+
// non-deterministically on every run.
221+
mockRunCronFallbackPassthrough();
222+
resolveCronDeliveryPlanMock.mockReturnValue({
223+
requested: true,
224+
mode: "announce",
225+
channel: "telegram",
226+
to: "123",
227+
});
228+
229+
await runCronIsolatedAgentTurn(makeParams());
230+
231+
expect(runEmbeddedPiAgentMock).toHaveBeenCalledTimes(1);
232+
const prompt: string = runEmbeddedPiAgentMock.mock.calls[0]?.[0]?.prompt ?? "";
233+
expect(prompt).not.toMatch(/\bsummary\b/i);
234+
});
235+
});

src/cron/isolated-agent/run.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ function appendCronDeliveryInstruction(params: {
193193
if (!params.deliveryRequested) {
194194
return params.commandBody;
195195
}
196-
return `${params.commandBody}\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself.`.trim();
196+
return `${params.commandBody}\n\nReturn your response as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself.`.trim();
197197
}
198198

199199
function resolvePositiveContextTokens(value: unknown): number | undefined {

0 commit comments

Comments
 (0)