fix(workbench): expose turn lifecycle ids
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// SPEC: PJ2026-010403 API契约 draft-2026-06-17-r0; PJ2026-0102 Agent编排 draft-2026-06-17-r0.
|
||||
// Responsibility: Code Agent HTTP turn admission, lifecycle compatibility wrappers, and session evidence projection.
|
||||
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
|
||||
@@ -85,9 +87,11 @@ export async function handleCodeAgentChatHttp(request, response, options) {
|
||||
}
|
||||
|
||||
const traceId = safeTraceId(getHeader(request, "x-trace-id") || params.traceId) || `trc_${randomUUID()}`;
|
||||
const lifecycle = codeAgentTurnLifecycleFields(traceId, params);
|
||||
const chatParams = {
|
||||
...params,
|
||||
traceId,
|
||||
...lifecycle,
|
||||
ownerUserId: options.actor?.id ?? params.ownerUserId,
|
||||
ownerRole: options.actor?.role ?? params.ownerRole
|
||||
};
|
||||
@@ -113,6 +117,7 @@ export async function handleCodeAgentChatHttp(request, response, options) {
|
||||
shortConnection: true,
|
||||
controlSemantics: "submit-and-poll",
|
||||
traceId,
|
||||
...lifecycle,
|
||||
workspaceId: workspaceClaim?.workspace?.id ?? null,
|
||||
workspaceRevision: workspaceClaim?.workspace?.revision ?? null,
|
||||
conversationId: safeConversationId(nativeSessionChatParams.conversationId) || null,
|
||||
@@ -702,6 +707,7 @@ function submitCodeAgentChatTurn({ params, options, traceId }) {
|
||||
accepted: true,
|
||||
status: "running",
|
||||
traceId,
|
||||
...codeAgentTurnLifecycleFields(traceId, params),
|
||||
conversationId: safeConversationId(params.conversationId) || null,
|
||||
sessionId: safeSessionId(params.sessionId) || null,
|
||||
threadId: safeOpaqueId(params.threadId) || null,
|
||||
@@ -1276,6 +1282,7 @@ function forbiddenTurnSnapshot(traceId) {
|
||||
function codeAgentTurnStatusPayload({ traceId, result, snapshot, resultPollError, refreshError, options }) {
|
||||
const resultObject = result && typeof result === "object" ? result : null;
|
||||
const snapshotObject = snapshot && typeof snapshot === "object" ? snapshot : null;
|
||||
const lifecycle = codeAgentTurnLifecycleFields(traceId, resultObject ?? snapshotObject ?? {});
|
||||
const events = Array.isArray(snapshotObject?.events) ? snapshotObject.events : Array.isArray(resultObject?.runnerTrace?.events) ? resultObject.runnerTrace.events : [];
|
||||
const lastEvent = events.at(-1) ?? null;
|
||||
const status = normalizeTurnStatus(
|
||||
@@ -1300,6 +1307,7 @@ function codeAgentTurnStatusPayload({ traceId, result, snapshot, resultPollError
|
||||
running: found ? running : false,
|
||||
terminal: found ? terminal : false,
|
||||
traceId,
|
||||
...lifecycle,
|
||||
conversationId: safeConversationId(resultObject?.conversationId ?? snapshotObject?.conversationId) || null,
|
||||
sessionId: safeSessionId(resultObject?.sessionId ?? resultObject?.session?.sessionId ?? snapshotObject?.sessionId) || null,
|
||||
threadId: safeOpaqueId(resultObject?.threadId ?? resultObject?.session?.threadId ?? snapshotObject?.threadId) || null,
|
||||
@@ -2219,6 +2227,7 @@ function codeAgentConversationMessagesEvidence(payload = {}, params = {}, traceI
|
||||
const userText = boundedConversationMessageText(params.message ?? params.prompt ?? payload.userMessage ?? payload.prompt);
|
||||
if (!userText) return [];
|
||||
const resolvedTraceId = safeTraceId(traceId) || null;
|
||||
const lifecycle = codeAgentTurnLifecycleFields(resolvedTraceId, payload);
|
||||
const session = payload.session && typeof payload.session === "object" ? payload.session : null;
|
||||
const sessionReuse = payload.sessionReuse && typeof payload.sessionReuse === "object" ? payload.sessionReuse : null;
|
||||
const conversationId = safeConversationId(payload.conversationId ?? session?.conversationId ?? sessionReuse?.conversationId ?? params.conversationId) || null;
|
||||
@@ -2226,14 +2235,15 @@ function codeAgentConversationMessagesEvidence(payload = {}, params = {}, traceI
|
||||
const threadId = safeOpaqueId(session?.threadId ?? sessionReuse?.threadId ?? payload.threadId ?? params.threadId) || null;
|
||||
const createdAt = payload.createdAt ?? payload.reply?.createdAt ?? new Date().toISOString();
|
||||
const updatedAt = payload.updatedAt ?? createdAt;
|
||||
const traceSuffix = codeAgentMessageTraceSuffix(resolvedTraceId);
|
||||
const userMessage = {
|
||||
id: `msg_${traceSuffix}_user`,
|
||||
id: lifecycle.userMessageId,
|
||||
messageId: lifecycle.userMessageId,
|
||||
role: "user",
|
||||
title: "用户",
|
||||
text: userText,
|
||||
status: "sent",
|
||||
traceId: resolvedTraceId,
|
||||
turnId: lifecycle.turnId,
|
||||
conversationId,
|
||||
sessionId,
|
||||
threadId,
|
||||
@@ -2256,12 +2266,14 @@ function codeAgentConversationMessagesEvidence(payload = {}, params = {}, traceI
|
||||
);
|
||||
const agentStatus = codeAgentConversationAgentMessageStatus(payload);
|
||||
const agentMessage = {
|
||||
id: `msg_${traceSuffix}_agent`,
|
||||
id: lifecycle.assistantMessageId,
|
||||
messageId: lifecycle.assistantMessageId,
|
||||
role: "agent",
|
||||
title: agentStatus === "failed" ? "Code Agent 返回阻塞" : agentStatus === "running" ? "Code Agent 处理中" : "Code Agent 回复",
|
||||
text: agentText || (agentStatus === "running" ? "" : "Code Agent 请求已结束,请查看 Trace 详情。"),
|
||||
status: agentStatus,
|
||||
traceId: resolvedTraceId,
|
||||
turnId: lifecycle.turnId,
|
||||
conversationId,
|
||||
sessionId,
|
||||
threadId,
|
||||
@@ -2306,6 +2318,31 @@ function codeAgentMessageTraceSuffix(traceId) {
|
||||
return text.replace(/^trc_/u, "").replace(/[^A-Za-z0-9_.:-]/gu, "_").slice(0, 48) || "trace";
|
||||
}
|
||||
|
||||
function codeAgentTurnLifecycleFields(traceId, source = {}) {
|
||||
const resolvedTraceId = safeTraceId(source?.traceId ?? traceId) || safeTraceId(traceId) || null;
|
||||
const turnId = safeTurnId(source?.turnId) || resolvedTraceId;
|
||||
const traceSuffix = codeAgentMessageTraceSuffix(resolvedTraceId);
|
||||
return {
|
||||
turnId,
|
||||
userMessageId: safeMessageId(source?.userMessageId ?? source?.userMessage?.messageId) || codeAgentLifecycleMessageId(traceSuffix, "user"),
|
||||
assistantMessageId: safeMessageId(source?.assistantMessageId ?? source?.assistantMessage?.messageId) || codeAgentLifecycleMessageId(traceSuffix, "agent")
|
||||
};
|
||||
}
|
||||
|
||||
function codeAgentLifecycleMessageId(traceSuffix, role) {
|
||||
return `msg_${traceSuffix}_${role}`;
|
||||
}
|
||||
|
||||
function safeTurnId(value) {
|
||||
const text = textValue(value);
|
||||
return /^turn_[A-Za-z0-9_.:-]+$/u.test(text) ? text : null;
|
||||
}
|
||||
|
||||
function safeMessageId(value) {
|
||||
const text = textValue(value);
|
||||
return /^msg_[A-Za-z0-9_.:-]+$/u.test(text) ? text : null;
|
||||
}
|
||||
|
||||
function codeAgentConversationAgentMessageStatus(payload = {}) {
|
||||
const status = textValue(payload.status ?? payload.agentRun?.terminalStatus ?? payload.commandState ?? payload.runStatus).toLowerCase();
|
||||
if (["completed", "done", "success", "active"].includes(status)) return "completed";
|
||||
@@ -2831,6 +2868,7 @@ function compactCodeAgentChatResultPayload(payload, options = {}) {
|
||||
const { userBillingReservation, ...publicPayload } = payload;
|
||||
return {
|
||||
...publicPayload,
|
||||
...codeAgentTurnLifecycleFields(publicPayload.traceId, publicPayload),
|
||||
...(terminalEvidence ? { terminalEvidence: terminalEvidencePayload(terminalEvidence) } : {}),
|
||||
...(publicPayload.runnerTrace && typeof publicPayload.runnerTrace === "object"
|
||||
? { runnerTrace: compactRunnerTraceForResult(publicPayload.runnerTrace, limit) }
|
||||
|
||||
Reference in New Issue
Block a user