// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-24-p1-opencode-message-part-authority. // Responsibility: AgentRun prompt evidence, message-authority text normalization, and prompt hash/preview DTO fields. import { createHash } from "node:crypto"; import { safeTraceId } from "./server-http-utils.ts"; export function agentRunPromptMetadataFromParams(params = {}, traceId = null) { return agentRunPromptMetadataFromText(firstNonEmpty(params.message, params.prompt, params.text), traceId, "hwlab-code-agent-request", { fullText: true }); } export 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; } export function agentRunPromptMetadataFromText(value, traceId = null, source = "hwlab-code-agent-request", options = {}) { const promptText = promptTextValue(value); 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 }); } export function agentRunPromptFields(metadata = null) { if (!metadata) return {}; return compactObject({ promptId: metadata.promptId, promptSha256: metadata.promptSha256, promptSummary: metadata.promptSummary, promptPreview: metadata.promptPreview, prompt: metadata.prompt }) ?? {}; } export function agentRunPromptEventFields(metadata = null) { if (!metadata) return {}; return compactObject({ promptId: metadata.promptId, promptSha256: metadata.promptSha256, promptSummary: metadata.promptSummary, promptPreview: metadata.promptPreview }) ?? {}; } export function messageAuthorityTextValue(value) { const extracted = promptTextRaw(value).replace(/\r\n?/gu, "\n"); if (!extracted.trim() || extracted.trim() === "[object Object]") return ""; return extracted; } 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 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 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 = promptTextValue(value); if (!promptText) return undefined; return promptText.length > limit ? `${promptText.slice(0, limit)}...` : promptText; } function promptTextValue(value) { const extracted = promptTextRaw(value).replace(/\s+/gu, " ").trim(); if (!extracted || extracted === "[object Object]") return ""; return extracted; } function promptTextRaw(value) { if (value === undefined || value === null) return ""; if (typeof value === "string") return value; if (typeof value === "number" || typeof value === "boolean") return String(value); if (Array.isArray(value)) return value.map(promptTextRaw).filter(Boolean).join("\n"); if (typeof value === "object") { for (const key of ["text", "content", "message", "prompt", "value", "summary", "preview", "title"]) { const text = promptTextRaw(value[key]); if (text) return text; } } return ""; } function sha256Text(value) { return createHash("sha256").update(String(value)).digest("hex"); } function compactObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return undefined; const result = {}; for (const [key, item] of Object.entries(value)) { if (item === undefined || item === null) continue; if (Array.isArray(item) && item.length === 0) continue; if (typeof item === "object" && !Array.isArray(item) && Object.keys(item).length === 0) continue; result[key] = item; } return Object.keys(result).length > 0 ? result : undefined; } function firstNonEmpty(...values) { for (const value of values) { const text = String(value ?? "").trim(); if (text) return text; } return null; }