Merge pull request #2089 from pikasTech/fix/2085-cancel-final-response
fix: seal canceled workbench final response
This commit is contained in:
@@ -90,6 +90,74 @@ test("workbench projection writer commits terminal owner evidence as sealed dura
|
||||
assert.equal(facts.checkpoints[0].timing.finishedAt, "2026-06-20T11:00:00.000Z");
|
||||
});
|
||||
|
||||
test("workbench projection writer seals canceled AgentRun turns with canonical cancel final response", async () => {
|
||||
const factWrites = [];
|
||||
const runtimeStore = {
|
||||
async writeWorkbenchFacts(params, requestMeta) {
|
||||
factWrites.push({ params, requestMeta });
|
||||
return { written: true, facts: params.facts };
|
||||
}
|
||||
};
|
||||
const accessController = {
|
||||
async recordAgentSessionOwner(input) {
|
||||
return {
|
||||
id: input.sessionId,
|
||||
projectId: input.projectId,
|
||||
ownerUserId: input.ownerUserId,
|
||||
conversationId: input.conversationId,
|
||||
threadId: input.threadId,
|
||||
lastTraceId: input.traceId,
|
||||
status: input.status,
|
||||
session: input.session,
|
||||
updatedAt: "2026-06-20T11:02:00.000Z"
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
await writeWorkbenchProjectionSession({
|
||||
accessController,
|
||||
runtimeStore,
|
||||
traceId: "trc_writer_canceled",
|
||||
ownerUserId: "usr_writer",
|
||||
ownerRole: "user",
|
||||
sessionId: "ses_writer_canceled",
|
||||
projectId: "prj_writer",
|
||||
conversationId: "cnv_writer_canceled",
|
||||
threadId: "thread-writer-canceled",
|
||||
status: "canceled",
|
||||
payload: {
|
||||
traceId: "trc_writer_canceled",
|
||||
status: "canceled",
|
||||
userMessage: "当前 AgentRun 请求已取消;traceId/runId/commandId 已保留,可重试上一条消息。",
|
||||
runnerTrace: {
|
||||
traceId: "trc_writer_canceled",
|
||||
status: "canceled",
|
||||
events: [{ type: "cancel", status: "canceled", terminal: true, createdAt: "2026-06-20T11:02:00.000Z" }],
|
||||
updatedAt: "2026-06-20T11:02:00.000Z"
|
||||
},
|
||||
agentRun: { runId: "run_writer_canceled", commandId: "cmd_writer_canceled", status: "cancelled", commandState: "cancelled", terminalStatus: "cancelled", lastSeq: 12 },
|
||||
updatedAt: "2026-06-20T11:02:00.000Z"
|
||||
},
|
||||
session: {
|
||||
sessionStatus: "canceled",
|
||||
messages: [
|
||||
{ messageId: "msg_writer_canceled_user", role: "user", text: "cancel me", status: "sent", turnId: "trc_writer_canceled", traceId: "trc_writer_canceled" },
|
||||
{ messageId: "msg_writer_canceled_agent", role: "agent", text: "", status: "canceled", turnId: "trc_writer_canceled", traceId: "trc_writer_canceled" }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(factWrites.length, 1);
|
||||
const facts = factWrites[0].params.facts;
|
||||
const agentMessage = facts.messages.find((message) => message.messageId === "msg_writer_canceled_agent");
|
||||
assert.equal(facts.sessions[0].status, "canceled");
|
||||
assert.equal(facts.turns[0].terminal, true);
|
||||
assert.equal(facts.turns[0].finalResponse.text, "hwlab-user-cancel");
|
||||
assert.equal(facts.turns[0].assistantText, "hwlab-user-cancel");
|
||||
assert.equal(agentMessage.text, "hwlab-user-cancel");
|
||||
assert.equal(facts.parts.some((part) => part.messageId === "msg_writer_canceled_agent" && part.partType === "final_response" && part.text === "hwlab-user-cancel" && part.sealed === true), true);
|
||||
});
|
||||
|
||||
test("workbench projection writer does not seal running assistant text as final response", async () => {
|
||||
const factWrites = [];
|
||||
const runtimeStore = {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
export const TERMINAL_STATUSES = new Set(["completed", "failed", "blocked", "timeout", "cancelled", "canceled", "idle"]);
|
||||
export const RUNNING_STATUSES = new Set(["running", "retrying", "pending", "queued", "accepted", "dispatching", "streaming", "active", "processing", "busy", "creating"]);
|
||||
const CANCEL_FINAL_RESPONSE_TEXT = "hwlab-user-cancel";
|
||||
|
||||
export function createWorkbenchTurnProjection({ turnId = null, traceId = null, result = null, session = null, trace = null } = {}) {
|
||||
const projectionTraceId = textValue(traceId ?? trace?.traceId ?? result?.traceId ?? session?.lastTraceId) || null;
|
||||
@@ -175,17 +176,29 @@ function terminalTurnEvidence({ result = null, traceTerminal = null } = {}) {
|
||||
);
|
||||
if (resultStatus) {
|
||||
if (resultStatus !== "completed" && retryableProviderInterruptionEvidence(result, result?.agentRun, result?.providerTrace, traceTerminal?.evidence)) return null;
|
||||
return { source: "result", status: resultStatus, finalResponse: traceTerminal?.finalResponse ?? null, valuesRedacted: true };
|
||||
return { source: "result", status: resultStatus, finalResponse: terminalFinalResponse(resultStatus, result, traceTerminal), valuesRedacted: true };
|
||||
}
|
||||
const statusOnly = terminalStatusFromValue(result?.status);
|
||||
if (statusOnly && resultHasTerminalAuthority(result, traceTerminal)) {
|
||||
if (statusOnly !== "completed" && retryableProviderInterruptionEvidence(result, result?.agentRun, result?.providerTrace, traceTerminal?.evidence)) return null;
|
||||
return { source: "result", status: statusOnly, finalResponse: traceTerminal?.finalResponse ?? null, valuesRedacted: true };
|
||||
return { source: "result", status: statusOnly, finalResponse: terminalFinalResponse(statusOnly, result, traceTerminal), valuesRedacted: true };
|
||||
}
|
||||
if (traceTerminal && retryableProviderInterruptionEvidence(traceTerminal.evidence)) return null;
|
||||
return traceTerminal;
|
||||
}
|
||||
|
||||
function terminalFinalResponse(status, result = null, traceTerminal = null) {
|
||||
const direct = traceTerminal?.finalResponse ?? result?.finalResponse;
|
||||
if (direct) return direct;
|
||||
if (normalizeWorkbenchStatus(status) !== "canceled") return null;
|
||||
return {
|
||||
text: CANCEL_FINAL_RESPONSE_TEXT,
|
||||
status: "canceled",
|
||||
traceId: textValue(result?.traceId ?? traceTerminal?.finalResponse?.traceId ?? traceTerminal?.evidence?.traceId) || null,
|
||||
valuesPrinted: false
|
||||
};
|
||||
}
|
||||
|
||||
function resultHasTerminalAuthority(result = null, traceTerminal = null) {
|
||||
if (!result || typeof result !== "object") return false;
|
||||
if (traceTerminal) return true;
|
||||
|
||||
Reference in New Issue
Block a user