// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-24-p1-opencode-message-part-authority. // Responsibility: AgentRun cancel disposition payloads and cancel trace diagnostics. export function agentRunCancelTerminalStatus(payload = null) { if (!payload || typeof payload !== "object") return null; for (const value of [payload.agentRun?.terminalStatus, payload.agentRun?.commandState, payload.agentRun?.commandStatus]) { const status = normalizeAgentRunStatus(value); if (isAgentRunTerminalStatus(status)) return status; } return null; } export function agentRunAlreadyTerminalCancelPayload({ traceId, payload, terminalStatus, options = {}, traceStore } = {}) { const normalizedTerminalStatus = normalizeAgentRunStatus(terminalStatus); const agentRun = payload?.agentRun && typeof payload.agentRun === "object" ? payload.agentRun : {}; traceStore?.append?.(traceId, agentRunCancelTraceEvent({ type: "cancel", status: normalizedTerminalStatus || "completed", label: "agentrun:cancel:already-terminal", message: "AgentRun command is already terminal; HWLAB did not forward cancel or overwrite the terminal Workbench projection.", runId: agentRun.runId, commandId: agentRun.commandId, terminal: true, valuesPrinted: false }, agentRun)); const payloadStatus = normalizeAgentRunStatus(payload?.status); const status = isAgentRunTerminalStatus(payloadStatus) ? payloadStatus : normalizedTerminalStatus || payloadStatus || "completed"; const result = { ...payload, status, canceled: false, alreadyTerminal: true, cancelStatus: "already_terminal", cancelDisposition: { status: "already_terminal", forwarded: false, terminal: true, traceId, runId: agentRun.runId ?? null, commandId: agentRun.commandId ?? null, route: "/v1/agent/chat/cancel", valuesPrinted: false }, updatedAt: nowIso(options.now), runnerTrace: traceStore?.snapshot?.(traceId), agentRun: { ...agentRun, terminalStatus: agentRun.terminalStatus ?? normalizedTerminalStatus, valuesPrinted: false }, valuesPrinted: false }; options.codeAgentChatResults?.set?.(traceId, result); return result; } export function agentRunCancelBlockedPayload({ traceId, payload, error, cancelPath, options = {}, traceStore } = {}) { const agentRun = payload?.agentRun && typeof payload.agentRun === "object" ? payload.agentRun : {}; const code = firstNonEmpty(error?.code, error?.agentRunError?.failureKind, "agentrun_cancel_failed"); const statusCode = Number.isFinite(Number(error?.statusCode)) ? Number(error.statusCode) : 502; const message = firstNonEmpty(error?.message, error?.agentRunError?.message, "AgentRun command cancel API did not accept the cancel request."); const userMessage = "取消请求未被 AgentRun 接受;已保留当前 trace/run/command 证据,页面不能把本轮伪装成已取消。"; const now = nowIso(options.now); const nextAgentRun = { ...agentRun, cancelStatus: "blocked", cancelErrorCode: code, cancelStatusCode: statusCode, updatedAt: now, valuesPrinted: false }; traceStore?.append?.(traceId, agentRunCancelTraceEvent({ type: "cancel", status: "blocked", label: "agentrun:cancel:blocked", errorCode: code, message: `AgentRun cancel was not accepted by the command cancel API: ${message}`, runId: agentRun.runId, commandId: agentRun.commandId, waitingFor: "agentrun-cancel-disposition", terminal: false, valuesPrinted: false }, nextAgentRun)); const cancelDisposition = { status: "blocked", code, statusCode, message, userMessage, retryable: true, terminal: false, route: "/v1/agent/chat/cancel", upstreamRoute: cancelPath, traceId, runId: agentRun.runId ?? null, commandId: agentRun.commandId ?? null, valuesPrinted: false }; const result = { ...payload, status: "blocked", canceled: false, cancelStatus: "blocked", cancelDisposition, updatedAt: now, agentRun: nextAgentRun, runnerTrace: traceStore?.snapshot?.(traceId), error: { code, layer: "agentrun", category: "cancel-blocked", retryable: true, message, userMessage, traceId, route: "/v1/agent/chat/cancel", upstreamRoute: cancelPath, statusCode, toolName: "agentrun.command.cancel", valuesPrinted: false }, userMessage, valuesPrinted: false }; options.codeAgentChatResults?.set?.(traceId, result); return result; } function agentRunCancelTraceEvent(event = {}, agentRun = {}) { return { ...event, backend: "agentrun-v01", adapter: "agentrun-v01", runId: event.runId ?? agentRun.runId ?? null, commandId: event.commandId ?? agentRun.commandId ?? null, valuesPrinted: false }; } function isAgentRunTerminalStatus(value) { return ["completed", "failed", "blocked", "cancelled", "canceled"].includes(normalizeAgentRunStatus(value)); } function normalizeAgentRunStatus(value) { return String(value ?? "").trim().toLowerCase().replace(/_/gu, "-"); } function firstNonEmpty(...values) { for (const value of values) { const text = String(value ?? "").trim(); if (text) return text; } return null; } function nowIso(now) { return typeof now === "function" ? now() : new Date().toISOString(); }