Files
boss/local-agent/master-task-progress-heartbeat.mjs
2026-06-08 12:22:50 +08:00

103 lines
3.3 KiB
JavaScript

function normalizeNumber(value, fallback) {
const numeric = Number(value);
return Number.isFinite(numeric) ? numeric : fallback;
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function formatElapsedSeconds(seconds) {
const safeSeconds = Math.max(0, Math.floor(seconds));
if (safeSeconds < 60) {
return `${safeSeconds}`;
}
const minutes = Math.floor(safeSeconds / 60);
const remainingSeconds = safeSeconds % 60;
return remainingSeconds > 0 ? `${minutes}${remainingSeconds}` : `${minutes} 分钟`;
}
function normalizeStepStatus(value, fallback = "pending") {
return value === "done" || value === "running" || value === "failed" || value === "pending"
? value
: fallback;
}
function normalizeSteps(steps) {
if (!Array.isArray(steps)) {
return [];
}
return steps
.map((step, index) => {
const text = typeof step?.text === "string" ? step.text.trim() : "";
if (!text) {
return null;
}
return {
id: typeof step?.id === "string" && step.id.trim() ? step.id.trim() : `step-${index + 1}`,
text,
status: normalizeStepStatus(step?.status),
};
})
.filter(Boolean)
.slice(0, 10);
}
function buildDefaultLongRunningSteps(elapsedSeconds) {
const elapsedText = formatElapsedSeconds(elapsedSeconds);
return [
{ id: "receive-task", text: "接收对话任务", status: "done" },
{ id: "locate-thread", text: "定位目标 Codex 线程", status: "done" },
{ id: "write-desktop-thread", text: "写入 Codex 桌面线程记录", status: "done" },
{ id: "await-thread-reply", text: `等待目标线程回复,已等待 ${elapsedText}`, status: "running" },
{ id: "write-back-boss", text: "回写 Boss 对话窗口", status: "pending" },
];
}
export function normalizeLongRunningProgressIntervalMs(value) {
const numeric = normalizeNumber(value, 20_000);
if (numeric <= 0) {
return 0;
}
return clamp(Math.floor(numeric), 5_000, 60_000);
}
export function buildLongRunningCodexProgressSnapshot({
task = {},
startedAtMs,
nowMs = Date.now(),
phase = "awaiting_reply",
baseProgress,
heartbeatCount = 0,
} = {}) {
const started = normalizeNumber(startedAtMs, nowMs);
const elapsedSeconds = Math.max(0, Math.round((nowMs - started) / 1000));
const liveSteps = normalizeSteps(baseProgress?.steps);
const steps = liveSteps.length > 0 ? liveSteps : buildDefaultLongRunningSteps(elapsedSeconds);
const warnings = Array.isArray(baseProgress?.warnings)
? baseProgress.warnings.filter(Boolean).slice(0, 8)
: [];
if (!warnings.some((warning) => warning?.id === "codex-turn-long-running")) {
warnings.unshift({
id: "codex-turn-long-running",
severity: "info",
message: `Codex 桌面线程仍在执行,已等待 ${formatElapsedSeconds(elapsedSeconds)}`,
});
}
return {
...(baseProgress && typeof baseProgress === "object" ? baseProgress : {}),
phase,
status: "running",
steps,
warnings,
longRunning: {
taskId: typeof task?.taskId === "string" ? task.taskId : undefined,
targetThreadDisplayName:
typeof task?.targetThreadDisplayName === "string" ? task.targetThreadDisplayName : undefined,
elapsedSeconds,
heartbeatCount: Math.max(0, Math.floor(normalizeNumber(heartbeatCount, 0))),
},
};
}