mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:20:43 +00:00
fix(cron): clean up deleteAfterRun direct deliveries (#67807)
Merged via squash.
Prepared head SHA: d23711c2e9
Co-authored-by: MonkeyLeeT <6754057+MonkeyLeeT@users.noreply.github.com>
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
Reviewed-by: @hxy91819
This commit is contained in:
@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Changes
|
||||
|
||||
- macOS/gateway: add `screen.snapshot` support for macOS app nodes, including runtime plumbing, default macOS allowlisting, and docs for monitor preview flows. (#67954) Thanks @BunsDev.
|
||||
- fix(cron): clean up deleteAfterRun direct deliveries (#67807). Thanks @MonkeyLeeT
|
||||
|
||||
### Fixes
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ const { countActiveDescendantRunsMock } = vi.hoisted(() => ({
|
||||
countActiveDescendantRunsMock: vi.fn().mockReturnValue(0),
|
||||
}));
|
||||
|
||||
vi.mock("../../config/sessions.js", () => ({
|
||||
vi.mock("../../config/sessions/main-session.js", () => ({
|
||||
resolveAgentMainSessionKey: vi.fn(({ agentId }: { agentId: string }) => `agent:${agentId}:main`),
|
||||
resolveMainSessionKey: vi.fn(() => "global"),
|
||||
}));
|
||||
@@ -853,6 +853,34 @@ describe("dispatchCronDelivery — double-announce guard", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("cleans up the direct cron session after threaded direct delivery when deleteAfterRun is enabled", async () => {
|
||||
vi.mocked(countActiveDescendantRuns).mockReturnValue(0);
|
||||
vi.mocked(isLikelyInterimCronMessage).mockReturnValue(false);
|
||||
|
||||
const params = makeBaseParams({ synthesizedText: "Final weather summary" });
|
||||
params.resolvedDelivery = {
|
||||
...makeResolvedDelivery(),
|
||||
mode: "implicit",
|
||||
threadId: 42,
|
||||
};
|
||||
(params.job as { deleteAfterRun?: boolean }).deleteAfterRun = true;
|
||||
|
||||
const state = await dispatchCronDelivery(params);
|
||||
|
||||
expect(state.result).toBeUndefined();
|
||||
expect(state.delivered).toBe(true);
|
||||
expect(deliverOutboundPayloads).toHaveBeenCalledTimes(1);
|
||||
expect(callGateway).toHaveBeenCalledWith({
|
||||
method: "sessions.delete",
|
||||
params: {
|
||||
key: "agent:main",
|
||||
deleteTranscript: true,
|
||||
emitLifecycleHooks: false,
|
||||
},
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
});
|
||||
|
||||
it("delivers structured heartbeat/media payloads once through the outbound adapter", async () => {
|
||||
vi.mocked(countActiveDescendantRuns).mockReturnValue(0);
|
||||
vi.mocked(isLikelyInterimCronMessage).mockReturnValue(false);
|
||||
@@ -884,6 +912,33 @@ describe("dispatchCronDelivery — double-announce guard", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("cleans up the direct cron session after structured direct delivery when deleteAfterRun is enabled", async () => {
|
||||
vi.mocked(countActiveDescendantRuns).mockReturnValue(0);
|
||||
vi.mocked(isLikelyInterimCronMessage).mockReturnValue(false);
|
||||
|
||||
const params = makeBaseParams({ synthesizedText: "HEARTBEAT_OK" });
|
||||
params.deliveryPayloadHasStructuredContent = true;
|
||||
params.deliveryPayloads = [
|
||||
{ text: "HEARTBEAT_OK", mediaUrl: "https://example.com/img.png" },
|
||||
] as never;
|
||||
(params.job as { deleteAfterRun?: boolean }).deleteAfterRun = true;
|
||||
|
||||
const state = await dispatchCronDelivery(params);
|
||||
|
||||
expect(state.result).toBeUndefined();
|
||||
expect(state.delivered).toBe(true);
|
||||
expect(deliverOutboundPayloads).toHaveBeenCalledTimes(1);
|
||||
expect(callGateway).toHaveBeenCalledWith({
|
||||
method: "sessions.delete",
|
||||
params: {
|
||||
key: "agent:main",
|
||||
deleteTranscript: true,
|
||||
emitLifecycleHooks: false,
|
||||
},
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
});
|
||||
|
||||
it("suppresses NO_REPLY payload with surrounding whitespace", async () => {
|
||||
vi.mocked(countActiveDescendantRuns).mockReturnValue(0);
|
||||
vi.mocked(isLikelyInterimCronMessage).mockReturnValue(false);
|
||||
@@ -966,6 +1021,7 @@ describe("dispatchCronDelivery — double-announce guard", () => {
|
||||
},
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
expect(callGateway).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("suppresses trailing NO_REPLY after summary text in direct delivery (#64976)", async () => {
|
||||
|
||||
@@ -442,6 +442,7 @@ export async function dispatchCronDelivery(
|
||||
// remains the only source of delivered state.
|
||||
let delivered = skipMessagingToolDelivery;
|
||||
let deliveryAttempted = skipMessagingToolDelivery;
|
||||
let directCronSessionDeleted = false;
|
||||
const failDeliveryTarget = (error: string) =>
|
||||
params.withRunSession({
|
||||
status: "error",
|
||||
@@ -453,7 +454,7 @@ export async function dispatchCronDelivery(
|
||||
...params.telemetry,
|
||||
});
|
||||
const cleanupDirectCronSessionIfNeeded = async (): Promise<void> => {
|
||||
if (!params.job.deleteAfterRun) {
|
||||
if (!params.job.deleteAfterRun || directCronSessionDeleted) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -467,6 +468,7 @@ export async function dispatchCronDelivery(
|
||||
},
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
directCronSessionDeleted = true;
|
||||
} catch {
|
||||
// Best-effort; direct delivery result should still be returned.
|
||||
}
|
||||
@@ -649,6 +651,17 @@ export async function dispatchCronDelivery(
|
||||
}
|
||||
};
|
||||
|
||||
const deliverViaDirectAndCleanup = async (
|
||||
delivery: SuccessfulDeliveryTarget,
|
||||
options?: { retryTransient?: boolean },
|
||||
): Promise<RunCronAgentTurnResult | null> => {
|
||||
try {
|
||||
return await deliverViaDirect(delivery, options);
|
||||
} finally {
|
||||
await cleanupDirectCronSessionIfNeeded();
|
||||
}
|
||||
};
|
||||
|
||||
const finalizeTextDelivery = async (
|
||||
delivery: SuccessfulDeliveryTarget,
|
||||
): Promise<RunCronAgentTurnResult | null> => {
|
||||
@@ -759,11 +772,7 @@ export async function dispatchCronDelivery(
|
||||
...params.telemetry,
|
||||
});
|
||||
}
|
||||
try {
|
||||
return await deliverViaDirect(delivery, { retryTransient: true });
|
||||
} finally {
|
||||
await cleanupDirectCronSessionIfNeeded();
|
||||
}
|
||||
return await deliverViaDirectAndCleanup(delivery, { retryTransient: true });
|
||||
};
|
||||
|
||||
if (params.deliveryRequested && !params.skipHeartbeatDelivery && !skipMessagingToolDelivery) {
|
||||
@@ -803,7 +812,7 @@ export async function dispatchCronDelivery(
|
||||
const useDirectDelivery =
|
||||
params.deliveryPayloadHasStructuredContent || params.resolvedDelivery.threadId != null;
|
||||
if (useDirectDelivery) {
|
||||
const directResult = await deliverViaDirect(params.resolvedDelivery);
|
||||
const directResult = await deliverViaDirectAndCleanup(params.resolvedDelivery);
|
||||
if (directResult) {
|
||||
return {
|
||||
result: directResult,
|
||||
|
||||
Reference in New Issue
Block a user