fix(cron): force fresh isolated session IDs

This commit is contained in:
Peter Steinberger
2026-02-22 19:40:31 +01:00
parent 6fef318fda
commit 91f75a2b33
3 changed files with 13 additions and 0 deletions

View File

@@ -41,6 +41,7 @@ Docs: https://docs.openclaw.ai
- Plugins/Discovery: ignore scanned extension backup/disabled directory patterns (for example `.backup-*`, `.bak`, `.disabled*`) and move updater backup directories under `.openclaw-install-backups`, preventing duplicate plugin-id collisions from archived copies.
- Dev tooling: prevent `CLAUDE.md` symlink target regressions by excluding CLAUDE symlink sentinels from `oxfmt` and marking them `-text` in `.gitattributes`, so formatter/EOL normalization cannot reintroduce trailing-newline targets. Thanks @vincentkoc.
- Cron: honor `cron.maxConcurrentRuns` in the timer loop so due jobs can execute up to the configured parallelism instead of always running serially. (#11595) Thanks @Takhoffman.
- Cron/Isolation: force fresh session IDs for isolated cron runs so `sessionTarget="isolated"` executions never reuse prior run context. (#23470) Thanks @echoVic.
- Agents/Compaction: restore embedded compaction safeguard/context-pruning extension loading in production by wiring bundled extension factories into the resource loader instead of runtime file-path resolution. (#22349) Thanks @Glucksberg.
- Feishu/Media: for inbound video messages that include both `file_key` (video) and `image_key` (thumbnail), prefer `file_key` when downloading media so video attachments are saved instead of silently failing on thumbnail keys. (#23633)
- Hooks/Cron: suppress duplicate main-session events for delivered hook turns and mark `SILENT_REPLY_TOKEN` (`NO_REPLY`) early exits as delivered to prevent hook context pollution. (#20678) Thanks @JonathanWorks.

View File

@@ -327,6 +327,16 @@ describe("runCronIsolatedAgentTurn — skill filter", () => {
]);
});
it("forces a fresh session for isolated cron runs", async () => {
const result = await runCronIsolatedAgentTurn(makeParams());
expect(result.status).toBe("ok");
expect(resolveCronSessionMock).toHaveBeenCalledOnce();
expect(resolveCronSessionMock.mock.calls[0]?.[0]).toMatchObject({
forceNew: true,
});
});
it("reuses cached snapshot when version and normalized skillFilter are unchanged", async () => {
resolveAgentSkillsFilterMock.mockReturnValue([" weather ", "meme-factory", "weather"]);
resolveCronSessionMock.mockReturnValue({

View File

@@ -270,6 +270,8 @@ export async function runCronIsolatedAgentTurn(params: {
sessionKey: agentSessionKey,
agentId,
nowMs: now,
// Isolated cron runs must not carry prior turn context across executions.
forceNew: params.job.sessionTarget === "isolated",
});
const runSessionId = cronSession.sessionEntry.sessionId;
const runSessionKey = baseSessionKey.startsWith("cron:")