fix(video): skip delivering tasks in active-task prompt guard (#96018)

Merged via squash.

Prepared head SHA: cbf32de95e
Co-authored-by: palomyates516-alt <231502129+palomyates516-alt@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
This commit is contained in:
palomyates516-alt
2026-06-24 20:37:11 +08:00
committed by GitHub
parent e4763b0631
commit ae9474b5fd
5 changed files with 145 additions and 3 deletions

View File

@@ -0,0 +1,126 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { TaskRecord } from "../tasks/task-registry.types.js";
import {
buildActiveMediaGenerationTaskPromptContextForSession,
findActiveMediaGenerationTaskForSession,
findDuplicateGuardMediaGenerationTaskForSession,
listActiveMediaGenerationTasksForSession,
MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
resetRecentMediaGenerationDuplicateGuardsForTests,
} from "./media-generation-task-status-shared.js";
const taskRuntimeInternalMocks = vi.hoisted(() => ({
listFreshTasksForOwnerKey: vi.fn(),
}));
vi.mock("../tasks/runtime-internal.js", () => taskRuntimeInternalMocks);
function makeTask(overrides: Partial<TaskRecord> = {}): TaskRecord {
const now = Date.now();
return {
taskId: "task-1",
runtime: "cli",
taskKind: "video-generate",
sourceId: "video-generate:byteplus",
requesterSessionKey: "session/A",
ownerKey: "session/A",
scopeKind: "session",
runId: "run-1",
task: "generate clip 01",
status: "running",
deliveryStatus: "not_applicable",
notifyPolicy: "silent",
createdAt: now,
startedAt: now,
lastEventAt: now,
...overrides,
};
}
beforeEach(() => {
resetRecentMediaGenerationDuplicateGuardsForTests();
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReset();
});
describe("media generation delivery-phase prompt guard", () => {
it("does not warn about a task waiting only for completion delivery", () => {
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([
makeTask({ progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS }),
]);
expect(
buildActiveMediaGenerationTaskPromptContextForSession({
sessionKey: "session/A",
taskKind: "video-generate",
sourcePrefix: "video-generate",
nounLabel: "video",
toolName: "video_generate",
completionLabel: "video",
}),
).toBeUndefined();
});
it("still warns while media generation is running", () => {
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([
makeTask({ progressSummary: "Generating video" }),
]);
expect(
buildActiveMediaGenerationTaskPromptContextForSession({
sessionKey: "session/A",
taskKind: "video-generate",
sourcePrefix: "video-generate",
nounLabel: "video",
toolName: "video_generate",
completionLabel: "video",
}),
).toContain("Do not call `video_generate` again for the same request");
});
it("keeps delivery-phase tasks available to duplicate/status lookups", () => {
const task = makeTask({ progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS });
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([task]);
expect(
listActiveMediaGenerationTasksForSession({
sessionKey: "session/A",
taskKind: "video-generate",
sourcePrefix: "video-generate",
}),
).toEqual([task]);
expect(
findActiveMediaGenerationTaskForSession({
sessionKey: "session/A",
taskKind: "video-generate",
sourcePrefix: "video-generate",
}),
).toEqual(task);
});
it("blocks the same prompt while allowing a distinct prompt", () => {
const task = makeTask({
task: "generate clip 01",
progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
});
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([task]);
expect(
findDuplicateGuardMediaGenerationTaskForSession({
sessionKey: "session/A",
taskKind: "video-generate",
sourcePrefix: "video-generate",
taskLabel: "generate clip 01",
maxAgeMs: 120_000,
}),
).toEqual(task);
expect(
findDuplicateGuardMediaGenerationTaskForSession({
sessionKey: "session/A",
taskKind: "video-generate",
sourcePrefix: "video-generate",
taskLabel: "generate clip 02",
maxAgeMs: 120_000,
}),
).toBeUndefined();
});
});

View File

@@ -14,6 +14,10 @@ import type { TaskRecord } from "../tasks/task-registry.types.js";
import { buildSessionAsyncTaskStatusDetails } from "./session-async-task-status.js";
import { stableStringify } from "./stable-stringify.js";
/** Marks media as ready while requester delivery is still being confirmed. */
export const MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS =
"Generated media; delivering completion";
type RecentMediaGenerationTaskStart = {
task: TaskRecord;
requestKey?: string;
@@ -299,6 +303,7 @@ export function findActiveMediaGenerationTaskForSession(params: {
taskKind: string;
sourcePrefix: string;
taskLabel?: string;
excludeDeliveringCompletion?: boolean;
}): TaskRecord | undefined {
return listActiveMediaGenerationTasksForSession(params)[0];
}
@@ -309,6 +314,7 @@ export function listActiveMediaGenerationTasksForSession(params: {
taskKind: string;
sourcePrefix: string;
taskLabel?: string;
excludeDeliveringCompletion?: boolean;
}): TaskRecord[] {
const sessionKey = normalizeOptionalString(params.sessionKey);
if (!sessionKey) {
@@ -331,6 +337,12 @@ export function listActiveMediaGenerationTasksForSession(params: {
if (taskLabel && !mediaGenerationTaskLabelMatches(task, taskLabel)) {
return false;
}
if (
params.excludeDeliveringCompletion &&
task.progressSummary === MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS
) {
return false;
}
return true;
});
return [
@@ -456,6 +468,7 @@ export function buildActiveMediaGenerationTaskPromptContextForSession(params: {
sessionKey: params.sessionKey,
taskKind: params.taskKind,
sourcePrefix: params.sourcePrefix,
excludeDeliveringCompletion: true,
});
if (!task) {
return undefined;

View File

@@ -34,6 +34,7 @@ import {
type AgentGeneratedAttachment,
} from "../generated-attachments.js";
import { formatAgentInternalEventsForPrompt, type AgentInternalEvent } from "../internal-events.js";
import { MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS } from "../media-generation-task-status-shared.js";
import {
deliverSubagentAnnouncement,
loadRequesterSessionEntry,
@@ -447,7 +448,7 @@ export function scheduleMediaGenerationTaskCompletion<
try {
params.lifecycle.recordTaskProgress({
handle: params.handle,
progressSummary: "Generated media; delivering completion",
progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
});
} catch (error) {
params.onWakeFailure(`${params.toolName} completion progress update failed`, {

View File

@@ -646,6 +646,7 @@ export function createMusicGenerateTool(options?: {
const activeDuplicateGuardResult = createMusicGenerateDuplicateGuardResult(
options?.agentSessionKey,
{ prompt },
);
if (activeDuplicateGuardResult) {
return activeDuplicateGuardResult;
@@ -703,7 +704,7 @@ export function createMusicGenerateTool(options?: {
});
const duplicateGuardResult = createMusicGenerateDuplicateGuardResult(
options?.agentSessionKey,
{ requestKey },
{ prompt, requestKey },
);
if (duplicateGuardResult) {
return duplicateGuardResult;

View File

@@ -1005,6 +1005,7 @@ export function createVideoGenerateTool(options?: {
const activeDuplicateGuardResult = createVideoGenerateDuplicateGuardResult(
options?.agentSessionKey,
{ prompt },
);
if (activeDuplicateGuardResult) {
return activeDuplicateGuardResult;
@@ -1113,7 +1114,7 @@ export function createVideoGenerateTool(options?: {
});
const duplicateGuardResult = createVideoGenerateDuplicateGuardResult(
options?.agentSessionKey,
{ requestKey },
{ prompt, requestKey },
);
if (duplicateGuardResult) {
return duplicateGuardResult;