Files
pikasTech-HWLAB/internal/cloud/code-agent-agentrun-cancel.ts

135 lines
5.2 KiB
TypeScript

// 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();
}