Skip to content

Commit ae9474b

Browse files
fix(video): skip delivering tasks in active-task prompt guard (#96018)
Merged via squash. Prepared head SHA: cbf32de Co-authored-by: palomyates516-alt <[email protected]> Co-authored-by: vincentkoc <[email protected]> Reviewed-by: @vincentkoc
1 parent e4763b0 commit ae9474b

5 files changed

Lines changed: 145 additions & 3 deletions
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { beforeEach, describe, expect, it, vi } from "vitest";
2+
import type { TaskRecord } from "../tasks/task-registry.types.js";
3+
import {
4+
buildActiveMediaGenerationTaskPromptContextForSession,
5+
findActiveMediaGenerationTaskForSession,
6+
findDuplicateGuardMediaGenerationTaskForSession,
7+
listActiveMediaGenerationTasksForSession,
8+
MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
9+
resetRecentMediaGenerationDuplicateGuardsForTests,
10+
} from "./media-generation-task-status-shared.js";
11+
12+
const taskRuntimeInternalMocks = vi.hoisted(() => ({
13+
listFreshTasksForOwnerKey: vi.fn(),
14+
}));
15+
16+
vi.mock("../tasks/runtime-internal.js", () => taskRuntimeInternalMocks);
17+
18+
function makeTask(overrides: Partial<TaskRecord> = {}): TaskRecord {
19+
const now = Date.now();
20+
return {
21+
taskId: "task-1",
22+
runtime: "cli",
23+
taskKind: "video-generate",
24+
sourceId: "video-generate:byteplus",
25+
requesterSessionKey: "session/A",
26+
ownerKey: "session/A",
27+
scopeKind: "session",
28+
runId: "run-1",
29+
task: "generate clip 01",
30+
status: "running",
31+
deliveryStatus: "not_applicable",
32+
notifyPolicy: "silent",
33+
createdAt: now,
34+
startedAt: now,
35+
lastEventAt: now,
36+
...overrides,
37+
};
38+
}
39+
40+
beforeEach(() => {
41+
resetRecentMediaGenerationDuplicateGuardsForTests();
42+
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReset();
43+
});
44+
45+
describe("media generation delivery-phase prompt guard", () => {
46+
it("does not warn about a task waiting only for completion delivery", () => {
47+
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([
48+
makeTask({ progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS }),
49+
]);
50+
51+
expect(
52+
buildActiveMediaGenerationTaskPromptContextForSession({
53+
sessionKey: "session/A",
54+
taskKind: "video-generate",
55+
sourcePrefix: "video-generate",
56+
nounLabel: "video",
57+
toolName: "video_generate",
58+
completionLabel: "video",
59+
}),
60+
).toBeUndefined();
61+
});
62+
63+
it("still warns while media generation is running", () => {
64+
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([
65+
makeTask({ progressSummary: "Generating video" }),
66+
]);
67+
68+
expect(
69+
buildActiveMediaGenerationTaskPromptContextForSession({
70+
sessionKey: "session/A",
71+
taskKind: "video-generate",
72+
sourcePrefix: "video-generate",
73+
nounLabel: "video",
74+
toolName: "video_generate",
75+
completionLabel: "video",
76+
}),
77+
).toContain("Do not call `video_generate` again for the same request");
78+
});
79+
80+
it("keeps delivery-phase tasks available to duplicate/status lookups", () => {
81+
const task = makeTask({ progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS });
82+
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([task]);
83+
84+
expect(
85+
listActiveMediaGenerationTasksForSession({
86+
sessionKey: "session/A",
87+
taskKind: "video-generate",
88+
sourcePrefix: "video-generate",
89+
}),
90+
).toEqual([task]);
91+
expect(
92+
findActiveMediaGenerationTaskForSession({
93+
sessionKey: "session/A",
94+
taskKind: "video-generate",
95+
sourcePrefix: "video-generate",
96+
}),
97+
).toEqual(task);
98+
});
99+
100+
it("blocks the same prompt while allowing a distinct prompt", () => {
101+
const task = makeTask({
102+
task: "generate clip 01",
103+
progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
104+
});
105+
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([task]);
106+
107+
expect(
108+
findDuplicateGuardMediaGenerationTaskForSession({
109+
sessionKey: "session/A",
110+
taskKind: "video-generate",
111+
sourcePrefix: "video-generate",
112+
taskLabel: "generate clip 01",
113+
maxAgeMs: 120_000,
114+
}),
115+
).toEqual(task);
116+
expect(
117+
findDuplicateGuardMediaGenerationTaskForSession({
118+
sessionKey: "session/A",
119+
taskKind: "video-generate",
120+
sourcePrefix: "video-generate",
121+
taskLabel: "generate clip 02",
122+
maxAgeMs: 120_000,
123+
}),
124+
).toBeUndefined();
125+
});
126+
});

src/agents/media-generation-task-status-shared.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ import type { TaskRecord } from "../tasks/task-registry.types.js";
1414
import { buildSessionAsyncTaskStatusDetails } from "./session-async-task-status.js";
1515
import { stableStringify } from "./stable-stringify.js";
1616

17+
/** Marks media as ready while requester delivery is still being confirmed. */
18+
export const MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS =
19+
"Generated media; delivering completion";
20+
1721
type RecentMediaGenerationTaskStart = {
1822
task: TaskRecord;
1923
requestKey?: string;
@@ -299,6 +303,7 @@ export function findActiveMediaGenerationTaskForSession(params: {
299303
taskKind: string;
300304
sourcePrefix: string;
301305
taskLabel?: string;
306+
excludeDeliveringCompletion?: boolean;
302307
}): TaskRecord | undefined {
303308
return listActiveMediaGenerationTasksForSession(params)[0];
304309
}
@@ -309,6 +314,7 @@ export function listActiveMediaGenerationTasksForSession(params: {
309314
taskKind: string;
310315
sourcePrefix: string;
311316
taskLabel?: string;
317+
excludeDeliveringCompletion?: boolean;
312318
}): TaskRecord[] {
313319
const sessionKey = normalizeOptionalString(params.sessionKey);
314320
if (!sessionKey) {
@@ -331,6 +337,12 @@ export function listActiveMediaGenerationTasksForSession(params: {
331337
if (taskLabel && !mediaGenerationTaskLabelMatches(task, taskLabel)) {
332338
return false;
333339
}
340+
if (
341+
params.excludeDeliveringCompletion &&
342+
task.progressSummary === MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS
343+
) {
344+
return false;
345+
}
334346
return true;
335347
});
336348
return [
@@ -456,6 +468,7 @@ export function buildActiveMediaGenerationTaskPromptContextForSession(params: {
456468
sessionKey: params.sessionKey,
457469
taskKind: params.taskKind,
458470
sourcePrefix: params.sourcePrefix,
471+
excludeDeliveringCompletion: true,
459472
});
460473
if (!task) {
461474
return undefined;

src/agents/tools/media-generate-background-shared.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
type AgentGeneratedAttachment,
3535
} from "../generated-attachments.js";
3636
import { formatAgentInternalEventsForPrompt, type AgentInternalEvent } from "../internal-events.js";
37+
import { MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS } from "../media-generation-task-status-shared.js";
3738
import {
3839
deliverSubagentAnnouncement,
3940
loadRequesterSessionEntry,
@@ -447,7 +448,7 @@ export function scheduleMediaGenerationTaskCompletion<
447448
try {
448449
params.lifecycle.recordTaskProgress({
449450
handle: params.handle,
450-
progressSummary: "Generated media; delivering completion",
451+
progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
451452
});
452453
} catch (error) {
453454
params.onWakeFailure(`${params.toolName} completion progress update failed`, {

src/agents/tools/music-generate-tool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ export function createMusicGenerateTool(options?: {
646646

647647
const activeDuplicateGuardResult = createMusicGenerateDuplicateGuardResult(
648648
options?.agentSessionKey,
649+
{ prompt },
649650
);
650651
if (activeDuplicateGuardResult) {
651652
return activeDuplicateGuardResult;
@@ -703,7 +704,7 @@ export function createMusicGenerateTool(options?: {
703704
});
704705
const duplicateGuardResult = createMusicGenerateDuplicateGuardResult(
705706
options?.agentSessionKey,
706-
{ requestKey },
707+
{ prompt, requestKey },
707708
);
708709
if (duplicateGuardResult) {
709710
return duplicateGuardResult;

src/agents/tools/video-generate-tool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,7 @@ export function createVideoGenerateTool(options?: {
10051005

10061006
const activeDuplicateGuardResult = createVideoGenerateDuplicateGuardResult(
10071007
options?.agentSessionKey,
1008+
{ prompt },
10081009
);
10091010
if (activeDuplicateGuardResult) {
10101011
return activeDuplicateGuardResult;
@@ -1113,7 +1114,7 @@ export function createVideoGenerateTool(options?: {
11131114
});
11141115
const duplicateGuardResult = createVideoGenerateDuplicateGuardResult(
11151116
options?.agentSessionKey,
1116-
{ requestKey },
1117+
{ prompt, requestKey },
11171118
);
11181119
if (duplicateGuardResult) {
11191120
return duplicateGuardResult;

0 commit comments

Comments
 (0)