mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-16 17:51:42 +00:00
* fix(cron): preserve cron context in session entry for async completion wakes (#99919) Persist bootstrapContextRunKind on the session entry after cron agent runs, and restore provider/model/thinking/runKind from the session entry into directAgentParams when an async completion wake (e.g. media generation) resumes a cron session. Before this fix, async completion wakes lost the original cron run context and fell back to account defaults, causing multi-step cron tasks to silently dead-end after the first async media generation call. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cron): authorize model override on trusted in-process dispatch for cron context restoration * fix(cron): persist bootstrapContextRunKind on base session entry for async completion wakes * fix(cron): route async completion wake to base cron session key instead of ephemeral run key When the cron run-key fallback fires, record the base cron key as effectiveSessionKey so deliverDirectSubagentAnnounce can route the agent call to the persisted session row with its transcript and task context instead of starting a fresh turn (#99919). * fix(cron): add bootstrapContextRunKind to session entry slot keys and declare loadRequesterSessionEntry return type * fix(cron): rebase onto upstream main, restore upstream agent-command behavior while keeping bootstrapContextRunKind persistence * fix(cron): persist bootstrapContextRunKind from a pre-mutation snapshot to produce a real delta * fix(cron): move pre-mutation snapshot inside guard so initialEntry is non-optional * fix(cron): preserve async media continuation context Co-authored-by: Peter Lee <li.xialong@xydigit.com> * fix(cron): retain continuation after base cleanup * test(gateway): complete agent scope mock * test(gateway): preserve agent scope exports * test(cron): persist mocked session mutations * test(cron): model persisted session fixtures * fix(agents): snapshot cron continuation retries * fix(cron): harden async continuation settlement * test(cron): control continuation recovery clock * fix(gateway): narrow continuation model row * chore(changelog): defer cron continuation note --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
74 lines
2.5 KiB
TypeScript
74 lines
2.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
clearGeneratedMediaTaskActivity,
|
|
registerGeneratedMediaTaskActivity,
|
|
resetGeneratedMediaTaskActivityForTests,
|
|
} from "./generated-media-task-activity.js";
|
|
import {
|
|
getGeneratedMediaTaskIdsForSessionKey,
|
|
hasNewGeneratedMediaTaskForSessionKey,
|
|
hasPendingGeneratedMediaTaskForSessionKey,
|
|
} from "./task-status-access.js";
|
|
|
|
const mocks = vi.hoisted(() => ({ listTaskRecords: vi.fn() }));
|
|
|
|
vi.mock("./task-registry.js", () => ({
|
|
findTaskByRunId: vi.fn(),
|
|
getTaskById: vi.fn(),
|
|
listTaskRecords: mocks.listTaskRecords,
|
|
listTasksForAgentId: vi.fn(),
|
|
listTasksForSessionKey: vi.fn(),
|
|
}));
|
|
|
|
describe("generated media task snapshots", () => {
|
|
const sessionKey = "agent:main:cron:job:run:run-id";
|
|
|
|
beforeEach(() => {
|
|
resetGeneratedMediaTaskActivityForTests();
|
|
mocks.listTaskRecords.mockReset();
|
|
});
|
|
|
|
it("detects only media admitted by the current exact-run attempt", () => {
|
|
const tasks = [
|
|
{
|
|
taskId: "old-image",
|
|
taskKind: "image_generation",
|
|
requesterSessionKey: sessionKey,
|
|
ownerKey: sessionKey,
|
|
},
|
|
];
|
|
mocks.listTaskRecords.mockImplementation(() => tasks);
|
|
const before = getGeneratedMediaTaskIdsForSessionKey(sessionKey);
|
|
|
|
expect(hasNewGeneratedMediaTaskForSessionKey(sessionKey, before)).toBe(false);
|
|
tasks.push({
|
|
taskId: "new-video",
|
|
taskKind: "video_generation",
|
|
requesterSessionKey: sessionKey,
|
|
ownerKey: sessionKey,
|
|
});
|
|
expect(hasNewGeneratedMediaTaskForSessionKey(sessionKey, before)).toBe(true);
|
|
});
|
|
|
|
it("does not apply exact-run replay guards to descendant sessions", () => {
|
|
mocks.listTaskRecords.mockReturnValue([]);
|
|
expect(getGeneratedMediaTaskIdsForSessionKey(`${sessionKey}:subagent:worker`)).toEqual(
|
|
new Set(),
|
|
);
|
|
expect(mocks.listTaskRecords).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("tracks active media when a detached runtime does not mirror core tasks", () => {
|
|
mocks.listTaskRecords.mockReturnValue([]);
|
|
const before = getGeneratedMediaTaskIdsForSessionKey(sessionKey);
|
|
|
|
registerGeneratedMediaTaskActivity("tool:image_generate:run-1", sessionKey);
|
|
expect(hasNewGeneratedMediaTaskForSessionKey(sessionKey, before)).toBe(true);
|
|
expect(hasPendingGeneratedMediaTaskForSessionKey(sessionKey)).toBe(true);
|
|
|
|
clearGeneratedMediaTaskActivity("tool:image_generate:run-1");
|
|
expect(hasNewGeneratedMediaTaskForSessionKey(sessionKey, before)).toBe(true);
|
|
expect(hasPendingGeneratedMediaTaskForSessionKey(sessionKey)).toBe(false);
|
|
});
|
|
});
|