Files
openclaw/src/agents/session-async-task-status.ts
Elarwei 9657b8e8ce fix(image-generate): allow distinct active image requests (#83614)
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 head 9f19a96427.
- Required merge gates passed before the squash merge.

Prepared head SHA: 9f19a96427
Review: 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>
2026-05-18 16:01:12 +00:00

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 } : {}),
};
}