fix: expose code agent prompt metadata

This commit is contained in:
lyon
2026-06-15 16:12:30 +08:00
parent c569503fc5
commit 06df7d17b0
3 changed files with 241 additions and 2 deletions
+128 -1
View File
@@ -1,4 +1,4 @@
import { randomUUID } from "node:crypto";
import { createHash, randomUUID } from "node:crypto";
import { defaultCodeAgentTraceStore } from "./code-agent-trace-store.ts";
import { decorateCodeAgentSession } from "./code-agent-session-lifecycle.ts";
@@ -215,6 +215,7 @@ export function initialAgentRunChatResult({ params = {}, options = {}, traceId }
const env = options.env ?? process.env;
const timestamp = nowIso(options.now);
const backendProfile = resolveAgentRunBackendProfile(env, params);
const promptEvidence = agentRunPromptMetadataFromParams(params, traceId);
return {
accepted: true,
status: "running",
@@ -234,6 +235,7 @@ export function initialAgentRunChatResult({ params = {}, options = {}, traceId }
sessionMode: AGENTRUN_SESSION_MODE,
implementationType: AGENTRUN_IMPLEMENTATION_TYPE,
threadContinuityPolicy: THREAD_CONTINUITY_POLICY,
...agentRunPromptFields(promptEvidence),
agentRun: {
adapter: ADAPTER_ID,
status: "pending-dispatch",
@@ -257,6 +259,7 @@ export async function submitAgentRunChatTurn({ params = {}, options = {}, traceI
const startedAt = nowIso(options.now);
const ownerApiKey = await resolveOwnerApiKey({ params, options, now: () => nowIso(options.now) });
const toolCapabilities = await resolveToolCapabilities({ params, options });
const promptEvidence = agentRunPromptMetadataFromParams(params, traceId);
traceStore.ensure(traceId, agentRunTraceMeta(env, params));
traceStore.append(traceId, {
@@ -267,6 +270,7 @@ export async function submitAgentRunChatTurn({ params = {}, options = {}, traceI
waitingFor: "agentrun-run-reuse-or-create",
adapter: ADAPTER_ID,
managerHost: new URL(managerUrl).hostname,
...agentRunPromptEventFields(promptEvidence),
valuesPrinted: false
}, agentRunTraceMeta(env, params));
@@ -674,6 +678,7 @@ export async function loadPersistedAgentRunResult(traceId, options = {}) {
if (!agentRun?.runId) return null;
const backendProfile = firstNonEmpty(agentRun.backendProfile, "deepseek");
const status = firstNonEmpty(session?.status === "canceled" ? "canceled" : null, agentRun.terminalStatus, agentRun.commandState, agentRun.status, "running");
const promptEvidence = agentRunPromptMetadataFromSession(session, safeId);
return {
accepted: true,
status,
@@ -683,6 +688,7 @@ export async function loadPersistedAgentRunResult(traceId, options = {}) {
sessionId: safeSessionId(session?.id) || agentRun.sessionId || null,
threadId: safeOpaqueId(session?.threadId) || agentRun.threadId || null,
messageId: `msg_${safeId.slice(4)}`,
...agentRunPromptFields(promptEvidence),
createdAt: session?.startedAt ?? session?.updatedAt ?? nowIso(options.now),
updatedAt: session?.updatedAt ?? nowIso(options.now),
ownerUserId: session?.ownerUserId,
@@ -879,6 +885,121 @@ function rejectRemovedResourceWorkspaceFiles(value) {
throw adapterError("legacy_workspace_files_removed", "workspaceFiles/resourceWorkspaceFiles are removed; use AgentRun ResourceBundleRef kind=gitbundle with bundles[]");
}
function agentRunPromptMetadataFromParams(params = {}, traceId = null) {
return agentRunPromptMetadataFromText(firstNonEmpty(params.message, params.prompt, params.text), traceId, "hwlab-code-agent-request", { fullText: true });
}
function agentRunPromptMetadataFromSession(session = null, traceId = null) {
const snapshot = session?.session && typeof session.session === "object" ? session.session : null;
const fromTraceResult = agentRunPromptMetadataFromPayload(snapshot?.traceResults?.[traceId], traceId);
if (fromTraceResult) return fromTraceResult;
const messageText = agentRunPromptTextFromMessages(snapshot?.messages, traceId);
if (messageText) return agentRunPromptMetadataFromText(messageText, traceId, "agent-session-owner-message", { fullText: true });
const preview = firstNonEmpty(snapshot?.firstUserMessagePreview);
return preview ? agentRunPromptMetadataFromText(preview, traceId, "agent-session-first-user-preview", { fullText: false }) : null;
}
function agentRunPromptMetadataFromPayload(payload = null, traceId = null) {
if (!payload || typeof payload !== "object") return null;
const prompt = payload.prompt && typeof payload.prompt === "object" ? payload.prompt : null;
const textValue = firstNonEmpty(prompt?.text, prompt?.summary, prompt?.preview, payload.promptSummary, payload.promptPreview);
const fromText = agentRunPromptMetadataFromText(textValue, traceId, prompt?.source ?? payload.promptSource ?? "agentrun-prompt-metadata", { fullText: Boolean(prompt?.text) });
return agentRunPromptMetadataFromParts({
traceId,
source: prompt?.source ?? payload.promptSource ?? fromText?.prompt?.source ?? "agentrun-prompt-metadata",
promptId: firstNonEmpty(payload.promptId, prompt?.id, fromText?.promptId),
promptSha256: firstNonEmpty(payload.promptSha256, prompt?.sha256, fromText?.promptSha256),
promptSummary: firstNonEmpty(payload.promptSummary, prompt?.summary, fromText?.promptSummary),
promptPreview: firstNonEmpty(payload.promptPreview, prompt?.preview, fromText?.promptPreview),
textChars: Number.isInteger(prompt?.textChars) ? prompt.textChars : fromText?.prompt?.textChars
});
}
function agentRunPromptTextFromMessages(messages = [], traceId = null) {
const list = Array.isArray(messages) ? messages : [];
const resolvedTraceId = safeTraceId(traceId);
const exact = list.find((message) => String(message?.role ?? "").toLowerCase() === "user" && (!resolvedTraceId || safeTraceId(message?.traceId) === resolvedTraceId));
const fallback = list.find((message) => String(message?.role ?? "").toLowerCase() === "user");
return firstNonEmpty(exact?.text, exact?.content, fallback?.text, fallback?.content);
}
function agentRunPromptMetadataFromText(value, traceId = null, source = "hwlab-code-agent-request", options = {}) {
const promptText = String(value ?? "").trim();
if (!promptText) return null;
return agentRunPromptMetadataFromParts({
traceId,
source,
promptSha256: options.fullText === true ? sha256Text(promptText) : undefined,
promptSummary: promptText,
promptPreview: promptText,
textChars: options.fullText === true ? promptText.length : undefined
});
}
function agentRunPromptMetadataFromParts({ traceId = null, source = "hwlab-code-agent-request", promptId = null, promptSha256 = null, promptSummary = null, promptPreview = null, textChars = undefined } = {}) {
const digest = firstNonEmpty(promptSha256);
const summary = clippedPromptText(firstNonEmpty(promptSummary, promptPreview), 240);
const preview = clippedPromptText(firstNonEmpty(promptPreview, promptSummary), 500);
const explicitId = firstNonEmpty(promptId);
if (!explicitId && !digest && !summary && !preview) return null;
const id = explicitId || agentRunPromptId(traceId, digest);
const prompt = compactObject({
id,
sha256: digest || undefined,
summary,
preview,
textChars: Number.isInteger(textChars) ? textChars : undefined,
source,
valuesPrinted: false
});
return compactObject({
promptId: id,
promptSha256: digest || undefined,
promptSummary: summary,
promptPreview: preview,
prompt,
valuesPrinted: false
}) ?? null;
}
function agentRunPromptFields(metadata = null) {
if (!metadata) return {};
return compactObject({
promptId: metadata.promptId,
promptSha256: metadata.promptSha256,
promptSummary: metadata.promptSummary,
promptPreview: metadata.promptPreview,
prompt: metadata.prompt
}) ?? {};
}
function agentRunPromptEventFields(metadata = null) {
if (!metadata) return {};
return compactObject({
promptId: metadata.promptId,
promptSha256: metadata.promptSha256,
promptSummary: metadata.promptSummary,
promptPreview: metadata.promptPreview
}) ?? {};
}
function agentRunPromptId(traceId = null, digest = null) {
const traceSuffix = safeTraceId(traceId)?.slice(4).replace(/[^A-Za-z0-9_.:-]/gu, "_");
const digestSuffix = firstNonEmpty(digest)?.slice(0, 16);
const suffix = firstNonEmpty(traceSuffix, digestSuffix);
return suffix ? `prm_${suffix}` : undefined;
}
function clippedPromptText(value, limit) {
const promptText = String(value ?? "").trim();
if (!promptText) return undefined;
return promptText.length > limit ? `${promptText.slice(0, limit)}...` : promptText;
}
function sha256Text(value) {
return createHash("sha256").update(String(value)).digest("hex");
}
function adapterError(code, message, details = {}) {
return Object.assign(new Error(message), {
code,
@@ -889,12 +1010,15 @@ function adapterError(code, message, details = {}) {
function buildAgentRunCommandInput({ params, traceId, backendProfile, sessionId }) {
const prompt = String(params.message ?? params.prompt ?? "").trim();
const promptEvidence = agentRunPromptMetadataFromText(prompt, traceId, "agentrun-command-payload", { fullText: true });
const threadId = safeOpaqueId(params.threadId);
return {
type: "turn",
payload: {
prompt,
message: prompt,
...agentRunPromptEventFields(promptEvidence),
...(promptEvidence?.prompt ? { promptMetadata: promptEvidence.prompt } : {}),
traceId,
projectId: firstNonEmpty(params.projectId) || null,
conversationId: safeConversationId(params.conversationId) || null,
@@ -913,12 +1037,15 @@ function buildAgentRunCommandInput({ params, traceId, backendProfile, sessionId
function buildAgentRunSteerCommandInput({ params, traceId, steerTraceId, mapped }) {
const prompt = String(params.message ?? params.prompt ?? params.text ?? "").trim();
const promptEvidence = agentRunPromptMetadataFromText(prompt, steerTraceId, "agentrun-steer-command-payload", { fullText: true });
return {
type: "steer",
payload: {
prompt,
message: prompt,
text: prompt,
...agentRunPromptEventFields(promptEvidence),
...(promptEvidence?.prompt ? { promptMetadata: promptEvidence.prompt } : {}),
traceId: steerTraceId,
targetTraceId: traceId,
targetCommandId: mapped.agentRun.commandId,