mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-29 21:08:43 +00:00
Summary: - This PR prompt-scopes `image_generate` duplicate detection, adds same-prompt and distinct-prompt regression tests, and updates task guardrail docs and changelog. - Reproducibility: yes. Current-main source shows the duplicate guard runs before prompt parsing and active lookup ignores prompt identity, matching the linked distinct-second-image failure mode. Automerge notes: - PR branch already contained follow-up commit before automerge: docs(tasks): clarify image generation guardrail - PR branch already contained follow-up commit before automerge: fix(image-generate): allow distinct active image requests Validation: - ClawSweeper review passed for head9f19a96427. - Required merge gates passed before the squash merge. Prepared head SHA:9f19a96427Review: https://github.com/openclaw/openclaw/pull/83614#issuecomment-4478236891 Co-authored-by: Elarwei <elarweis@gmail.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
|
import { listTasksForOwnerKey } from "../tasks/runtime-internal.js";
|
|
import type { TaskRecord, TaskRuntime, TaskStatus } from "../tasks/task-registry.types.js";
|
|
|
|
const DEFAULT_ACTIVE_STATUSES = new Set<TaskStatus>(["queued", "running"]);
|
|
|
|
export function findActiveSessionTask(params: {
|
|
sessionKey?: string;
|
|
runtime?: TaskRuntime;
|
|
taskKind?: string;
|
|
task?: string;
|
|
statuses?: ReadonlySet<TaskStatus>;
|
|
sourceIdPrefix?: string;
|
|
}): TaskRecord | undefined {
|
|
const normalizedSessionKey = normalizeOptionalString(params.sessionKey);
|
|
if (!normalizedSessionKey) {
|
|
return undefined;
|
|
}
|
|
const statuses = params.statuses ?? DEFAULT_ACTIVE_STATUSES;
|
|
const taskKind = normalizeOptionalString(params.taskKind);
|
|
const taskLabel = normalizeOptionalString(params.task);
|
|
const sourceIdPrefix = normalizeOptionalString(params.sourceIdPrefix);
|
|
const matches = listTasksForOwnerKey(normalizedSessionKey).filter((task) => {
|
|
if (task.scopeKind !== "session") {
|
|
return false;
|
|
}
|
|
if (params.runtime && task.runtime !== params.runtime) {
|
|
return false;
|
|
}
|
|
if (!statuses.has(task.status)) {
|
|
return false;
|
|
}
|
|
if (taskKind && task.taskKind !== taskKind) {
|
|
return false;
|
|
}
|
|
if (taskLabel) {
|
|
const currentTaskLabel = normalizeOptionalString(task.task);
|
|
if (currentTaskLabel !== taskLabel) {
|
|
return false;
|
|
}
|
|
}
|
|
if (sourceIdPrefix) {
|
|
const sourceId = normalizeOptionalString(task.sourceId) ?? "";
|
|
if (sourceId !== sourceIdPrefix && !sourceId.startsWith(`${sourceIdPrefix}:`)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
if (matches.length === 0) {
|
|
return undefined;
|
|
}
|
|
return matches.find((task) => task.status === "running") ?? matches[0];
|
|
}
|
|
|
|
export function buildSessionAsyncTaskStatusDetails(task: TaskRecord): Record<string, unknown> {
|
|
return {
|
|
async: true,
|
|
active: true,
|
|
existingTask: true,
|
|
status: task.status,
|
|
task: {
|
|
taskId: task.taskId,
|
|
...(task.runId ? { runId: task.runId } : {}),
|
|
},
|
|
...(task.taskKind ? { taskKind: task.taskKind } : {}),
|
|
...(task.progressSummary ? { progressSummary: task.progressSummary } : {}),
|
|
...(task.sourceId ? { sourceId: task.sourceId } : {}),
|
|
};
|
|
}
|