zcbot/web/static/js/progress.js

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 };
}