feat: persist workbench session inputs (#2175)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-20-p2-terminal-outbox-recovery; PJ2026-010403 API契约 draft-2026-06-20-p2-terminal-outbox-recovery; PJ2026-010205 HWLAB接入 draft-2026-06-17-r0; PJ2026-0102 Agent编排 draft-2026-06-17-r0; PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
|
||||
// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-20-p2-terminal-outbox-recovery; PJ2026-010403 API契约 draft-2026-06-20-p2-terminal-outbox-recovery; PJ2026-010205 HWLAB接入 draft-2026-06-25-p0-session-warm-runner-contract; PJ2026-0102 Agent编排 draft-2026-06-17-r0; PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
|
||||
// Responsibility: Code Agent HTTP turn admission, lifecycle compatibility wrappers, and Workbench projection writer/finalizer integration.
|
||||
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
@@ -969,6 +969,15 @@ async function recordCodeAgentTurnAdmission({ payload = {}, params = {}, options
|
||||
error.valuesRedacted = true;
|
||||
throw error;
|
||||
}
|
||||
await recordCodeAgentSessionInputFact({
|
||||
params,
|
||||
options,
|
||||
traceId,
|
||||
delivery: "queue",
|
||||
status: "admitted",
|
||||
messageId: codeAgentTurnLifecycleFields(traceId, params).userMessageId,
|
||||
commandId: payload.agentRun?.commandId ?? null
|
||||
});
|
||||
} catch (error) {
|
||||
throw codeAgentAdmissionUnavailableError(error, { params, traceId });
|
||||
}
|
||||
@@ -989,6 +998,64 @@ async function recordCodeAgentTurnAdmission({ payload = {}, params = {}, options
|
||||
return ownerRecord;
|
||||
}
|
||||
|
||||
async function recordCodeAgentSessionInputFact({ params = {}, options = {}, traceId, delivery = "queue", status = "admitted", messageId = null, commandId = null, error = null } = {}) {
|
||||
const runtimeStore = options.runtimeStore ?? null;
|
||||
if (typeof runtimeStore?.writeWorkbenchFacts !== "function") return null;
|
||||
const resolvedTraceId = safeTraceId(traceId ?? params.traceId) || null;
|
||||
const lifecycle = codeAgentTurnLifecycleFields(resolvedTraceId, params);
|
||||
const sessionId = safeSessionId(params.sessionId ?? params.session?.sessionId) || null;
|
||||
if (!sessionId) return null;
|
||||
const resolvedMessageId = safeMessageId(messageId ?? params.messageId ?? lifecycle.userMessageId) || lifecycle.userMessageId;
|
||||
const resolvedCommandId = textValue(commandId ?? params.commandId ?? params.agentRun?.commandId) || null;
|
||||
const normalizedDelivery = codeAgentInputDelivery(delivery);
|
||||
const normalizedStatus = codeAgentInputStatus(status);
|
||||
const now = new Date().toISOString();
|
||||
const sourceEventId = `${sessionId}:${lifecycle.turnId}:${normalizedDelivery}:${resolvedTraceId ?? resolvedCommandId ?? resolvedMessageId}`;
|
||||
const inputId = stableCodeAgentInputId({ sessionId, turnId: lifecycle.turnId, traceId: resolvedTraceId, messageId: resolvedMessageId, commandId: resolvedCommandId, delivery: normalizedDelivery, sourceEventId });
|
||||
const fact = {
|
||||
inputId,
|
||||
sessionId,
|
||||
turnId: lifecycle.turnId,
|
||||
traceId: resolvedTraceId,
|
||||
messageId: resolvedMessageId,
|
||||
commandId: resolvedCommandId,
|
||||
delivery: normalizedDelivery,
|
||||
status: normalizedStatus,
|
||||
errorCode: textValue(error?.code ?? error?.error?.code) || null,
|
||||
sourceEventId,
|
||||
promptHash: params.message || params.prompt || params.text ? sha256Text(params.message ?? params.prompt ?? params.text).slice(0, 16) : null,
|
||||
textBytes: params.message || params.prompt || params.text ? Buffer.byteLength(String(params.message ?? params.prompt ?? params.text), "utf8") : 0,
|
||||
valuesRedacted: true,
|
||||
secretMaterialStored: false,
|
||||
createdAt: now,
|
||||
updatedAt: now
|
||||
};
|
||||
return runtimeStore.writeWorkbenchFacts({ facts: { inputs: [fact] } }, {
|
||||
sessionId,
|
||||
traceId: resolvedTraceId,
|
||||
turnId: lifecycle.turnId,
|
||||
messageId: resolvedMessageId,
|
||||
ownerUserId: options.actor?.id ?? params.ownerUserId,
|
||||
projectId: params.projectId ?? DEFAULT_CODE_AGENT_PROJECT_ID,
|
||||
valuesPrinted: false
|
||||
});
|
||||
}
|
||||
|
||||
function stableCodeAgentInputId(value) {
|
||||
const payload = Object.fromEntries(Object.entries(value ?? {}).filter(([, item]) => item !== undefined && item !== null && item !== "").sort(([left], [right]) => left.localeCompare(right)));
|
||||
return `wsi_${sha256Text(JSON.stringify(payload)).slice(0, 32)}`;
|
||||
}
|
||||
|
||||
function codeAgentInputDelivery(value) {
|
||||
const text = textValue(value).toLowerCase();
|
||||
return ["queue", "steer", "cancel"].includes(text) ? text : "queue";
|
||||
}
|
||||
|
||||
function codeAgentInputStatus(value) {
|
||||
const text = textValue(value).toLowerCase().replace(/-/gu, "_");
|
||||
return ["admitted", "promoted", "blocked", "failed", "canceled", "completed"].includes(text) ? text : "admitted";
|
||||
}
|
||||
|
||||
function codeAgentAdmissionUnavailableError(error, { params = {}, traceId } = {}) {
|
||||
const payload = codeAgentAdmissionUnavailablePayload({ error, params, traceId });
|
||||
return Object.assign(new Error(payload.error.message), {
|
||||
@@ -2614,6 +2681,20 @@ export async function handleCodeAgentCancelHttp(request, response, options) {
|
||||
}));
|
||||
return;
|
||||
}
|
||||
await recordCodeAgentSessionInputFact({
|
||||
params: {
|
||||
...params,
|
||||
sessionId: currentResult.sessionId ?? currentResult.session?.sessionId ?? params.sessionId,
|
||||
messageId: codeAgentTurnLifecycleFields(traceId, currentResult).userMessageId,
|
||||
ownerUserId: options.actor?.id,
|
||||
ownerRole: options.actor?.role
|
||||
},
|
||||
options,
|
||||
traceId,
|
||||
delivery: "cancel",
|
||||
status: "promoted",
|
||||
commandId: currentResult.agentRun.commandId
|
||||
});
|
||||
const payload = await cancelAgentRunChatTurn({ traceId, currentResult, options, traceStore });
|
||||
if (payload) {
|
||||
if (payload.alreadyTerminal === true || isTraceCommandTerminalStatus(payload.status)) {
|
||||
@@ -3056,6 +3137,19 @@ export async function handleCodeAgentSteerHttp(request, response, options) {
|
||||
}
|
||||
|
||||
try {
|
||||
await recordCodeAgentSessionInputFact({
|
||||
params: {
|
||||
...params,
|
||||
sessionId: currentResult.sessionId ?? currentResult.session?.sessionId ?? params.sessionId,
|
||||
ownerUserId: options.actor?.id,
|
||||
ownerRole: options.actor?.role
|
||||
},
|
||||
options,
|
||||
traceId,
|
||||
delivery: "steer",
|
||||
status: "promoted",
|
||||
commandId: currentResult.agentRun.commandId
|
||||
});
|
||||
const payload = await steerAgentRunChatTurn({
|
||||
traceId,
|
||||
currentResult,
|
||||
|
||||
Reference in New Issue
Block a user