74 lines
2.3 KiB
JavaScript
74 lines
2.3 KiB
JavaScript
// Task progress state helpers. Kept DOM-free so Node tests can cover the
|
|
// "update_step without title" merge behavior used by the Web UI.
|
|
|
|
export function cloneProgressSteps(steps) {
|
|
return Array.isArray(steps) ? steps.map(s => ({ ...s })) : [];
|
|
}
|
|
|
|
export function normalizeProgressStatus(status) {
|
|
return ["pending", "in_progress", "completed"].includes(status) ? status : "pending";
|
|
}
|
|
|
|
export function normalizeProgressStep(step) {
|
|
if (!step || typeof step !== "object") return null;
|
|
const id = String(step.id || "").trim();
|
|
const title = String(step.title || "").trim();
|
|
const status = normalizeProgressStatus(step.status);
|
|
if (!id || !title) return null;
|
|
return { id, title, status };
|
|
}
|
|
|
|
export function applyProgressAction(progress, args) {
|
|
const current = cloneProgressSteps(progress);
|
|
if (!args || typeof args !== "object") return current;
|
|
const action = args.action || "";
|
|
if (action === "clear") return [];
|
|
if (action === "set_plan") {
|
|
return Array.isArray(args.steps) ? args.steps.map(normalizeProgressStep).filter(Boolean) : [];
|
|
}
|
|
if (action === "update_step") {
|
|
const raw = args.step;
|
|
if (!raw || typeof raw !== "object") return current;
|
|
const id = String(raw.id || "").trim();
|
|
if (!id) return current;
|
|
let found = false;
|
|
const next = current.map((s) => {
|
|
if (s.id !== id) return s;
|
|
found = true;
|
|
return {
|
|
id: s.id,
|
|
title: String(raw.title || s.title || "").trim(),
|
|
status: normalizeProgressStatus(raw.status || s.status),
|
|
};
|
|
}).filter(s => s.title);
|
|
if (!found && raw.title) {
|
|
next.push({
|
|
id,
|
|
title: String(raw.title).trim(),
|
|
status: normalizeProgressStatus(raw.status),
|
|
});
|
|
}
|
|
return next;
|
|
}
|
|
return current;
|
|
}
|
|
|
|
export function progressActionsFromToolCalls(toolCalls, baseSteps = []) {
|
|
let steps = cloneProgressSteps(baseSteps);
|
|
let sawProgress = false;
|
|
if (!Array.isArray(toolCalls)) return { steps, sawProgress };
|
|
for (const tc of toolCalls) {
|
|
const fn = (tc && tc.function && tc.function.name) || "";
|
|
if (fn !== "task_progress") continue;
|
|
let args = {};
|
|
try {
|
|
args = JSON.parse((tc.function && tc.function.arguments) || "{}");
|
|
} catch (e) {
|
|
args = {};
|
|
}
|
|
steps = applyProgressAction(steps, args);
|
|
sawProgress = true;
|
|
}
|
|
return { steps, sawProgress };
|
|
}
|