mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-04 10:10:01 +00:00
fix(agents): media generation blocks channel turns sharing the main session (#160998)
Pass the durable runSessionKey to image/video/music tools so shared-main channel sessions detach media generation again (regression from #159179), and wait for the terminal image task state in the image-generation-lifecycle e2e.
This commit is contained in:
parent
8634a33466
commit
e61efb6cd8
3 changed files with 31 additions and 9 deletions
|
|
@ -146,10 +146,6 @@ export function createOpenClawTools(options?: OpenClawToolsOptions): AnyAgentToo
|
|||
});
|
||||
const trimmedRunSessionKey = options?.runSessionKey?.trim();
|
||||
const requesterSessionKey = trimmedRunSessionKey || options?.agentSessionKey;
|
||||
const mediaGenerationAgentSessionKey =
|
||||
trimmedRunSessionKey && isCronRunSessionKey(trimmedRunSessionKey)
|
||||
? trimmedRunSessionKey
|
||||
: options?.agentSessionKey;
|
||||
const imageTool =
|
||||
options?.agentDir &&
|
||||
resolveImageToolFactoryAvailable({
|
||||
|
|
@ -182,7 +178,7 @@ export function createOpenClawTools(options?: OpenClawToolsOptions): AnyAgentToo
|
|||
config: options?.config,
|
||||
agentDir: options?.agentDir,
|
||||
authProfileStore: options?.authProfileStore,
|
||||
agentSessionKey: mediaGenerationAgentSessionKey,
|
||||
agentSessionKey: requesterSessionKey,
|
||||
requesterAgentId: sessionAgentId,
|
||||
requesterOrigin: widgetPresentation.deliveryContext ?? undefined,
|
||||
workspaceDir,
|
||||
|
|
|
|||
|
|
@ -180,16 +180,19 @@ describe("createOpenClawTools context wiring", () => {
|
|||
expect(onYield).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses the isolated cron run key for background media completions", () => {
|
||||
it.each([
|
||||
["agent:main:cron:daily-media", "agent:main:cron:daily-media:run:run-123"],
|
||||
["agent:main:qa-channel:default:direct:media-requester", "agent:main:main"],
|
||||
])("uses the durable run key for background media from %s", (agentSessionKey, runSessionKey) => {
|
||||
createTools({
|
||||
config: mediaConfig,
|
||||
agentSessionKey: "agent:main:cron:daily-media",
|
||||
runSessionKey: "agent:main:cron:daily-media:run:run-123",
|
||||
agentSessionKey,
|
||||
runSessionKey,
|
||||
onYield: vi.fn(),
|
||||
});
|
||||
for (const factory of [mocks.image, mocks.video, mocks.music]) {
|
||||
expect(factory).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ agentSessionKey: "agent:main:cron:daily-media:run:run-123" }),
|
||||
expect.objectContaining({ agentSessionKey: runSessionKey }),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ const IMAGE_MODEL_REF = "openai/gpt-image-1";
|
|||
const REQUEST_TEXT =
|
||||
"Image generation check IMAGE_TASK_LIFECYCLE: generate the QA lighthouse image.";
|
||||
const CONVERSATION = { id: "image-generation-lifecycle", kind: "direct" as const };
|
||||
// QA direct chats share the agent main session, which owns the media task.
|
||||
const REQUESTER_SESSION_KEY = "agent:qa:main";
|
||||
|
||||
async function readRequestBody(request: IncomingMessage) {
|
||||
const chunks: Buffer[] = [];
|
||||
|
|
@ -156,6 +158,17 @@ async function waitForToolOutput(baseUrl: string, needle: string) {
|
|||
return matched as MockOpenAiRequestSnapshot;
|
||||
}
|
||||
|
||||
async function readImageTaskStatus(gateway: {
|
||||
call: (method: string, params: unknown) => Promise<unknown>;
|
||||
}) {
|
||||
const result = (await gateway.call("tools.invoke", {
|
||||
name: "image_generate",
|
||||
args: { action: "status" },
|
||||
sessionKey: REQUESTER_SESSION_KEY,
|
||||
})) as { ok?: boolean; output?: unknown; result?: unknown };
|
||||
return JSON.stringify(result);
|
||||
}
|
||||
|
||||
describe("image generation task lifecycle through QA-channel", () => {
|
||||
const cleanups: Array<() => Promise<void>> = [];
|
||||
|
||||
|
|
@ -225,6 +238,7 @@ describe("image generation task lifecycle through QA-channel", () => {
|
|||
)?.[1];
|
||||
expect(runningReceipt).toEqual(expect.any(String));
|
||||
expect(imageProvider.requests).toHaveLength(1);
|
||||
expect(await readImageTaskStatus(gateway)).toContain(runningReceipt);
|
||||
imageProvider.release();
|
||||
await vi.waitFor(
|
||||
() => {
|
||||
|
|
@ -263,6 +277,15 @@ describe("image generation task lifecycle through QA-channel", () => {
|
|||
);
|
||||
expect(completionReentry.allInputText).toContain("sourceChannel=internal");
|
||||
|
||||
// Completion delivery precedes the terminal task state; wait for it before the next duplicate.
|
||||
await vi.waitFor(
|
||||
async () =>
|
||||
expect(await readImageTaskStatus(gateway)).toContain(
|
||||
"No active image generation task is currently running",
|
||||
),
|
||||
{ interval: 50, timeout: 30_000 },
|
||||
);
|
||||
|
||||
await sendExactRequest();
|
||||
const completedDuplicate = await waitForToolOutput(mock.baseUrl, "recently succeeded");
|
||||
expect(completedDuplicate.toolOutput).toContain(runningReceipt);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue