import { createHash, randomUUID } from "node:crypto"; import { M3_IO_CONTROL_ROUTE, M3_STATUS_ROUTE, describeM3StatusLive, handleM3IoControl } from "./m3-io-control.ts"; import { agentRunSessionEvidence, cancelAgentRunChatTurn, codeAgentAgentRunAdapterEnabled, initialAgentRunChatResult, loadPersistedAgentRunResult, refreshAgentRunTrace, steerAgentRunChatTurn, submitAgentRunChatTurn, syncAgentRunChatResult } from "./code-agent-agentrun-adapter.ts"; import { createCodeAgentErrorPayload, handleCodeAgentChat } from "./code-agent-chat.ts"; import { defaultCodeAgentTraceStore } from "./code-agent-trace-store.ts"; import { codeAgentSessionLifecycleSummary } from "./code-agent-session-lifecycle.ts"; import { firstHeaderValue, getHeader, parsePositiveInteger, positiveInteger, readBody, safeConversationId, safeOpaqueId, safeSessionId, safeTraceId, sendJson, truthyFlag } from "./server-http-utils.ts"; const DEFAULT_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT = 120; const DEFAULT_CODE_AGENT_PROJECT_ID = "prj_hwpod_workbench"; const DEFAULT_CODE_AGENT_PROVIDER_PROFILE = "deepseek"; const CODE_AGENT_TERMINAL_STATUSES = new Set(["completed", "failed", "blocked", "timeout", "cancelled", "canceled"]); const CODE_AGENT_PROVIDER_PROFILE_ALIASES = Object.freeze({ codex: "codex-api" }); const CODE_AGENT_PROVIDER_PROFILE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/u; const CODE_AGENT_PROVIDER_PROFILE_LABELS = Object.freeze({ "deepseek": "DeepSeek", "codex-api": "Codex API", "minimax-m3": "MiniMax-M3 via AgentRun", "runtime-default": "运行默认" }); const DEFAULT_CODE_AGENT_CODEX_API_MODEL = "gpt-5.5"; const DEFAULT_CODE_AGENT_CODEX_API_BASE_URL = "http://127.0.0.1:49280/responses"; const DEFAULT_CODE_AGENT_DEEPSEEK_MODEL = "deepseek-chat"; const DEFAULT_CODE_AGENT_MINIMAX_M3_MODEL = "MiniMax-M3"; export async function handleCodeAgentChatHttp(request, response, options) { const body = await readBody(request, options.bodyLimitBytes); let params = {}; try { params = body ? JSON.parse(body) : {}; } catch (error) { sendJson(response, 400, { ...createCodeAgentErrorPayload({ code: "parse_error", message: "Invalid JSON body", reason: error.message, traceId: getHeader(request, "x-trace-id") || "trc_unassigned", model: options.env?.HWLAB_CODE_AGENT_MODEL || options.env?.OPENAI_MODEL || process.env.HWLAB_CODE_AGENT_MODEL || process.env.OPENAI_MODEL || "unknown", layer: "api", retryable: true }) }); return; } if (!params || typeof params !== "object" || Array.isArray(params)) { sendJson(response, 400, { ...createCodeAgentErrorPayload({ code: "invalid_params", message: "Code Agent chat body must be a JSON object", traceId: getHeader(request, "x-trace-id") || "trc_unassigned", model: options.env?.HWLAB_CODE_AGENT_MODEL || options.env?.OPENAI_MODEL || process.env.HWLAB_CODE_AGENT_MODEL || process.env.OPENAI_MODEL || "unknown", layer: "api", retryable: true }) }); return; } const traceId = safeTraceId(getHeader(request, "x-trace-id") || params.traceId) || `trc_${randomUUID()}`; const chatParams = { ...params, traceId, ownerUserId: options.actor?.id ?? params.ownerUserId, ownerRole: options.actor?.role ?? params.ownerRole }; const manualSession = await prepareManualCodeAgentSession({ params: chatParams, options, traceId, response }); if (manualSession?.blocked) return; const workspaceClaim = await claimWorkbenchWorkspaceTurn({ params: chatParams, options, traceId, response }); if (workspaceClaim?.blocked) return; const billingPreflight = await preflightCodeAgentBilling({ params: chatParams, options, traceId, response }); if (billingPreflight?.blocked) return; const nativeSessionChatParams = stripSyntheticConversationContext({ ...chatParams, userBillingReservation: billingPreflight?.reservation ?? null }, traceId, options); if (codeAgentChatShortConnectionRequested(request, params, options)) { submitCodeAgentChatTurn({ params: nativeSessionChatParams, options, traceId }); const traceUrl = `/v1/agent/chat/trace/${encodeURIComponent(traceId)}`; sendJson(response, 202, { accepted: true, status: "running", shortConnection: true, controlSemantics: "submit-and-poll", traceId, workspaceId: workspaceClaim?.workspace?.id ?? null, workspaceRevision: workspaceClaim?.workspace?.revision ?? null, conversationId: safeConversationId(nativeSessionChatParams.conversationId) || null, sessionId: safeSessionId(nativeSessionChatParams.sessionId) || null, traceUrl, resultUrl: `/v1/agent/chat/result/${encodeURIComponent(traceId)}`, streamUrl: `${traceUrl}/stream`, cancelUrl: "/v1/agent/chat/cancel", polling: { resultIntervalMs: parsePositiveInteger(options.env?.HWLAB_CODE_AGENT_RESULT_POLL_INTERVAL_MS, 1000), traceIntervalMs: parsePositiveInteger(options.env?.HWLAB_CODE_AGENT_TRACE_POLL_INTERVAL_MS, 1000) }, runnerTrace: (options.traceStore ?? defaultCodeAgentTraceStore).snapshot(traceId) }); return; } const payload = await runCodeAgentChat(nativeSessionChatParams, options); await finalizeCodeAgentBillingUsage({ payload, params: nativeSessionChatParams, options }); await recordCodeAgentSessionOwner({ payload, params: nativeSessionChatParams, options, status: payload.status === "completed" ? "active" : payload.status }); const responsePayload = annotateOwner(payload, nativeSessionChatParams); sendJson(response, responsePayload.status === "failed" && responsePayload.error?.code === "invalid_params" ? 400 : 200, responsePayload); } export async function handleCodeAgentSessionsHttp(request, response, url, options) { const match = url.pathname.match(/^\/v1\/agent\/sessions(?:\/([^/]+)(?:\/(select))?)?$/u); const sessionId = safeSessionId(match?.[1] ? decodeURIComponent(match[1]) : ""); const action = match?.[2] ?? null; if (!match) { sendJson(response, 404, manualSessionErrorPayload({ code: "session_route_not_found", message: "Code Agent session route is not implemented." })); return; } if (request.method === "GET" && !sessionId) { await listManualCodeAgentSessions(request, response, url, options); return; } if (request.method === "POST" && !sessionId) { await createManualCodeAgentSession(request, response, options); return; } if (request.method === "GET" && sessionId && !action) { await getManualCodeAgentSession(response, options, sessionId); return; } if (request.method === "POST" && sessionId && action === "select") { await selectManualCodeAgentSession(request, response, options, sessionId); return; } sendJson(response, 405, manualSessionErrorPayload({ code: "session_method_not_allowed", message: "Unsupported Code Agent session method." })); } async function listManualCodeAgentSessions(request, response, url, options) { const projectId = textValue(url.searchParams.get("projectId")) || DEFAULT_CODE_AGENT_PROJECT_ID; const limit = parsePositiveInteger(url.searchParams.get("limit"), 20); const sessions = await options.accessController?.store?.listAgentSessionsForUser?.({ ownerUserId: options.actor?.id, actorRole: options.actor?.role, ownerScoped: true, projectId, limit }) ?? []; sendJson(response, 200, { ok: true, status: "succeeded", contractVersion: "code-agent-manual-session-v1", projectId, sessions: sessions.map(publicManualAgentSession), count: sessions.length, valuesRedacted: true, secretMaterialStored: false }); } async function createManualCodeAgentSession(request, response, options) { const body = await readJsonObjectBody(request, options.bodyLimitBytes); if (!body.ok) { sendJson(response, 400, manualSessionErrorPayload({ code: body.code, message: body.message, reason: body.reason })); return; } const params = body.value; const projectId = textValue(params.projectId) || DEFAULT_CODE_AGENT_PROJECT_ID; const conversationId = safeConversationId(params.conversationId) || `cnv_${randomUUID()}`; const sessionId = safeSessionId(params.sessionId) || `ses_${randomUUID()}`; const providerProfile = normalizeCodeAgentProviderProfile(params.providerProfile ?? params.codeAgentProviderProfile); const now = new Date().toISOString(); const session = await options.accessController.recordAgentSessionOwner({ ownerUserId: options.actor?.id, ownerRole: options.actor?.role, sessionId, projectId, agentId: "hwlab-code-agent", status: "idle", conversationId, threadId: null, traceId: null, session: { source: "manual-session-create", providerProfile, sessionStatus: "idle", createdAt: now, valuesRedacted: true, secretMaterialStored: false } }); const workspace = await updateManualSessionWorkspace({ params, options, session, providerProfile, source: "code-agent-manual-session-create" }); sendJson(response, 201, { ok: true, status: "created", contractVersion: "code-agent-manual-session-v1", session: publicManualAgentSession(session), workspace, nextCommands: manualSessionNextCommands(session), valuesRedacted: true, secretMaterialStored: false }); } async function getManualCodeAgentSession(response, options, sessionId) { const session = await getVisibleManualAgentSession(options, sessionId); if (!session) { sendJson(response, 404, manualSessionErrorPayload({ code: "session_not_found", message: "Code Agent session is not visible to the current actor.", sessionId })); return; } sendJson(response, 200, { ok: true, status: "found", contractVersion: "code-agent-manual-session-v1", session: publicManualAgentSession(session), valuesRedacted: true, secretMaterialStored: false }); } async function selectManualCodeAgentSession(request, response, options, sessionId) { const body = await readJsonObjectBody(request, options.bodyLimitBytes); if (!body.ok) { sendJson(response, 400, manualSessionErrorPayload({ code: body.code, message: body.message, reason: body.reason, sessionId })); return; } const session = await getVisibleManualAgentSession(options, sessionId); if (!session) { sendJson(response, 404, manualSessionErrorPayload({ code: "session_not_found", message: "Code Agent session is not visible to the current actor.", sessionId })); return; } if (!manualSessionUsable(session.status)) { sendJson(response, 409, manualSessionErrorPayload({ code: "session_not_usable", message: `Code Agent session ${sessionId} is ${session.status}; create a new session before continuing.`, sessionId, session })); return; } const providerProfile = textValue(body.value.providerProfile) || session.session?.providerProfile || DEFAULT_CODE_AGENT_PROVIDER_PROFILE; const workspace = await updateManualSessionWorkspace({ params: body.value, options, session, providerProfile, source: "code-agent-manual-session-select" }); sendJson(response, 200, { ok: true, status: "selected", contractVersion: "code-agent-manual-session-v1", session: publicManualAgentSession(session), workspace, nextCommands: manualSessionNextCommands(session), valuesRedacted: true, secretMaterialStored: false }); } async function prepareManualCodeAgentSession({ params, options, traceId, response }) { if (!options.actor?.id) { sendJson(response, 401, manualSessionErrorPayload({ code: "auth_required", message: "Authentication is required before using an explicit HWLAB Code Agent session.", traceId, retryable: true })); return { blocked: true }; } const sessionId = safeSessionId(params.sessionId); if (!sessionId) { sendJson(response, 409, manualSessionErrorPayload({ code: "session_required", message: "Code Agent session is explicit in HWLAB v0.2; create/select a session before sending a turn.", traceId, retryable: true, nextCommands: ["hwlab-cli client agent session create --provider-profile PROFILE", "hwlab-cli client agent send --session-id --message TEXT"] })); return { blocked: true }; } const session = await getVisibleManualAgentSession(options, sessionId); if (!session) { sendJson(response, 404, manualSessionErrorPayload({ code: "session_not_found", message: "Code Agent session is not visible to the current actor.", traceId, sessionId })); return { blocked: true }; } if (!manualSessionUsable(session.status)) { sendJson(response, 409, manualSessionErrorPayload({ code: "session_not_usable", message: `Code Agent session ${sessionId} is ${session.status}; create a new session before continuing.`, traceId, sessionId, session })); return { blocked: true }; } const storedConversationId = safeConversationId(session.conversationId); const requestedConversationId = safeConversationId(params.conversationId); if (requestedConversationId && storedConversationId && requestedConversationId !== storedConversationId) { sendJson(response, 409, manualSessionErrorPayload({ code: "session_conversation_mismatch", message: `sessionId ${sessionId} is bound to ${storedConversationId}; requested ${requestedConversationId}.`, traceId, sessionId, session })); return { blocked: true }; } const conversationId = requestedConversationId || storedConversationId; if (!conversationId) { sendJson(response, 409, manualSessionErrorPayload({ code: "session_conversation_required", message: "Code Agent session has no bound conversationId; create a new session before continuing.", traceId, sessionId, session })); return { blocked: true }; } params.conversationId = conversationId; params.sessionId = sessionId; params.projectId = textValue(params.projectId) || session.projectId || DEFAULT_CODE_AGENT_PROJECT_ID; params.threadId = safeOpaqueId(params.threadId) || safeOpaqueId(session.threadId) || undefined; return { session }; } async function getVisibleManualAgentSession(options, sessionId) { if (!safeSessionId(sessionId) || !options.actor?.id || !options.accessController?.getAgentSession) return null; const session = await options.accessController.getAgentSession(sessionId); if (!session) return null; if (session.ownerUserId === options.actor.id) return session; return null; } async function updateManualSessionWorkspace({ params = {}, options = {}, session, providerProfile, source }) { const store = options.accessController?.store; if (!store || !options.actor?.id || !session) return null; const workspaceId = safeWorkspaceId(params.workspaceId); const projectId = textValue(params.projectId) || session.projectId || DEFAULT_CODE_AGENT_PROJECT_ID; const current = workspaceId ? await store.getWorkspaceForUser?.({ workspaceId, ownerUserId: options.actor.id, actorRole: options.actor.role }) : await store.getOrCreateDefaultWorkspace?.({ ownerUserId: options.actor.id, projectId }); if (!current) return null; const selectedActiveTraceId = manualSessionSelectableActiveTraceId(session); const selectedLastTraceId = safeTraceId(session.lastTraceId) || selectedActiveTraceId || null; const updated = await store.updateWorkspace?.({ workspaceId: current.id, ownerUserId: options.actor.id, actorRole: options.actor.role, projectId: current.projectId ?? projectId, name: current.name, status: current.status, selectedConversationId: session.conversationId, selectedAgentSessionId: session.id, activeTraceId: selectedActiveTraceId, providerProfile, patch: { ...(current.workspace && typeof current.workspace === "object" ? current.workspace : {}), selectedConversationId: session.conversationId, selectedAgentSessionId: session.id, activeTraceId: selectedActiveTraceId, ...(selectedLastTraceId ? { lastTraceId: selectedLastTraceId } : {}), sessionStatus: session.status, threadId: safeOpaqueId(session.threadId) || null, providerProfile, source, updatedAt: new Date().toISOString(), valuesRedacted: true, secretMaterialStored: false }, updatedBySessionId: options.authSession?.id ?? null, updatedByClient: textValue(params.updatedByClient ?? params.client) || source, now: new Date().toISOString() }); if (typeof options.accessController.publicWorkbenchWorkspace === "function") { return await options.accessController.publicWorkbenchWorkspace(updated ?? current, options.actor); } return updated ?? current; } function manualSessionSelectableActiveTraceId(session = {}) { if (!manualSessionHasRunningTraceStatus(session)) return null; return safeTraceId( session.currentTraceId ?? session.session?.currentTraceId ?? session.session?.traceId ?? session.session?.runnerTrace?.traceId ?? session.session?.agentRun?.traceId ) || safeTraceId(session.lastTraceId) || null; } function manualSessionHasRunningTraceStatus(session = {}) { return [ session.status, session.session?.sessionStatus, session.session?.lifecycleStatus, session.session?.runnerTrace?.sessionStatus, session.session?.runnerTrace?.status, session.session?.agentRun?.runStatus, session.session?.agentRun?.commandState, session.session?.agentRun?.terminalStatus ].map((value) => String(value ?? "").trim().toLowerCase().replace(/_/gu, "-")) .some((value) => ["running", "busy", "pending", "creating"].includes(value)); } function publicManualAgentSession(session) { if (!session || typeof session !== "object") return null; return { sessionId: session.id, projectId: session.projectId ?? null, agentId: session.agentId ?? "hwlab-code-agent", status: session.status ?? null, usable: manualSessionUsable(session.status), conversationId: safeConversationId(session.conversationId) || null, threadId: safeOpaqueId(session.threadId) || null, lastTraceId: safeTraceId(session.lastTraceId) || null, providerProfile: textValue(session.session?.providerProfile) || null, createdAt: session.startedAt ?? null, updatedAt: session.updatedAt ?? null, valuesRedacted: true, secretMaterialStored: false }; } function manualSessionUsable(status) { const value = String(status ?? "").trim().toLowerCase().replace(/_/gu, "-"); return !["blocked", "expired", "stale", "thread-resume-failed"].includes(value); } function manualSessionErrorPayload({ code, message, reason = null, traceId = null, sessionId = null, session = null, retryable = true, nextCommands = undefined } = {}) { return { ok: false, accepted: false, status: "blocked", traceId: safeTraceId(traceId) || null, sessionId: safeSessionId(sessionId) || safeSessionId(session?.id) || null, session: session ? publicManualAgentSession(session) : null, error: { code, layer: "api", category: "session_blocked", retryable, message, reason, userMessage: message, route: "/v1/agent/chat" }, blocker: { code, layer: "api", retryable, summary: message }, nextCommands, valuesRedacted: true, secretMaterialStored: false }; } function manualSessionNextCommands(session) { const sessionId = session?.id ?? ""; return [ `hwlab-cli client agent send --session-id ${sessionId} --message TEXT`, `hwlab-cli client agent session status ${sessionId}` ]; } async function readJsonObjectBody(request, bodyLimitBytes) { const body = await readBody(request, bodyLimitBytes); try { const value = body ? JSON.parse(body) : {}; if (!value || typeof value !== "object" || Array.isArray(value)) return { ok: false, code: "invalid_params", message: "body must be a JSON object" }; return { ok: true, value }; } catch (error) { return { ok: false, code: "parse_error", message: "Invalid JSON body", reason: error.message }; } } async function runCodeAgentChat(params, options) { return handleCodeAgentChat(params, await codeAgentChatExecutionOptions(options, params)); } function stripSyntheticConversationContext(params = {}, traceId, options = {}) { if (Array.isArray(params.conversationContext?.messages) || Array.isArray(params.messages)) { (options.traceStore ?? defaultCodeAgentTraceStore).append(traceId, { type: "synthetic-context", status: "ignored", label: "synthetic-context:ignored", message: "Synthetic conversation context was stripped; Code Agent continuation uses only Codex stdio native thread/resume and turn/start.", valuesPrinted: false }); } const { conversationContext, conversationContextTruncated, contextSource, messages, ...nativeParams } = params; return nativeParams; } async function codeAgentChatExecutionOptions(options = {}, params = {}) { const env = codeAgentProviderProfileEnv(options.env ?? process.env, params); Object.assign(env, await codeAgentAuthEnv(options, env, params)); return { callProvider: options.callCodeAgentProvider, timeoutMs: options.codeAgentTimeoutMs, hardTimeoutMs: options.codeAgentHardTimeoutMs, env, workspace: options.workspace, skillsDirs: options.skillsDirs, skillsDirsExact: options.skillsDirsExact, skillsDiscoveryDelayMs: options.skillsDiscoveryDelayMs, sessionRegistry: options.sessionRegistry, m3IoSkillRequestJson: options.m3IoSkillRequestJson ?? createCodeAgentM3HwlabApiRequestJson(options), codexStdioManager: options.codexStdioManager, traceStore: options.traceStore, gatewayRegistry: options.gatewayRegistry }; } async function codeAgentAuthEnv(options = {}, env = process.env, params = {}) { const ownerUserId = textValue(params.ownerUserId ?? options.actor?.id ?? ""); const store = options.accessController?.store; const key = ownerUserId && store?.findActiveDefaultApiKeyForUser ? await store.findActiveDefaultApiKeyForUser(ownerUserId) : null; const runtimeNamespace = runtimeNamespaceForEnv(env); const runtimeLane = runtimeLaneForNamespace(runtimeNamespace) ?? runtimeLaneForEnv(env); const inClusterApiUrl = internalCodeAgentApiUrl(env, runtimeNamespace); const inClusterWebUrl = internalCodeAgentWebUrl(env, runtimeNamespace); const apiUrl = firstNonEmptyValue( codeAgentAgentRunAdapterEnabled(env) ? inClusterApiUrl : null, env.HWLAB_RUNTIME_API_URL, sameRuntimeEndpoint(env.HWLAB_CLOUD_API_URL, runtimeNamespace, runtimeLane), env.HWLAB_PUBLIC_ENDPOINT, localCloudApiUrl(env) ); return { HWLAB_CLOUD_API_URL: apiUrl, HWLAB_RUNTIME_API_URL: apiUrl, HWLAB_RUNTIME_WEB_URL: firstNonEmptyValue(codeAgentAgentRunAdapterEnabled(env) ? inClusterWebUrl : null, env.HWLAB_RUNTIME_WEB_URL, apiUrl), HWLAB_RUNTIME_NAMESPACE: runtimeNamespace, HWLAB_RUNTIME_LANE: runtimeLane, HWLAB_RUNTIME_ENDPOINT_SOURCE: codeAgentAgentRunAdapterEnabled(env) ? "runtime-namespace" : "runtime-env", HWLAB_RUNTIME_ENDPOINT_LOCKED: "1", HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME: "1", ...(options.actorApiKeySecret ? { HWLAB_API_KEY: options.actorApiKeySecret } : key?.displaySecret ? { HWLAB_API_KEY: key.displaySecret } : {}) }; } function internalCodeAgentApiUrl(env = process.env, namespace = runtimeNamespaceForEnv(env)) { const serviceName = firstNonEmptyValue(env.HWLAB_CODE_AGENT_INTERNAL_API_SERVICE_NAME, env.HWLAB_CLOUD_API_SERVICE_NAME, "hwlab-cloud-api"); const port = parsePositiveInteger(firstNonEmptyValue(env.HWLAB_CODE_AGENT_INTERNAL_API_PORT, env.HWLAB_CLOUD_API_PORT), 6667); return `http://${serviceName}.${namespace}.svc.cluster.local:${port}`; } function internalCodeAgentWebUrl(env = process.env, namespace = runtimeNamespaceForEnv(env)) { const serviceName = firstNonEmptyValue(env.HWLAB_CODE_AGENT_INTERNAL_WEB_SERVICE_NAME, env.HWLAB_CLOUD_WEB_SERVICE_NAME, "hwlab-cloud-web"); const port = parsePositiveInteger(firstNonEmptyValue(env.HWLAB_CODE_AGENT_INTERNAL_WEB_PORT, env.HWLAB_CLOUD_WEB_PORT), 8080); return `http://${serviceName}.${namespace}.svc.cluster.local:${port}`; } function runtimeNamespaceForEnv(env = process.env) { return firstNonEmptyValue( env.HWLAB_RUNTIME_NAMESPACE, env.POD_NAMESPACE, env.HWLAB_NAMESPACE, defaultRuntimeNamespace(env) ); } function runtimeLaneForEnv(env = process.env) { const profile = String(firstNonEmptyValue(env.HWLAB_RUNTIME_LANE, env.HWLAB_GITOPS_LANE, env.HWLAB_GITOPS_PROFILE, env.HWLAB_ENVIRONMENT, env.HWLAB_BOOT_REF, "dev") ?? "dev").trim().toLowerCase(); if (profile === "prod" || profile === "production") return "prod"; if (profile === "v02" || profile === "v0.2" || profile === "0.2") return "v02"; return "dev"; } function runtimeLaneForNamespace(namespace) { if (namespace === "hwlab-v02") return "v02"; if (namespace === "hwlab-prod") return "prod"; if (namespace === "hwlab-dev") return "dev"; return null; } function sameRuntimeEndpoint(value, namespace, lane) { const text = firstNonEmptyValue(value); if (!text) return null; const endpointLane = runtimeLaneForEndpoint(text); const endpointNamespace = runtimeNamespaceForEndpoint(text); if (namespace && endpointNamespace && endpointNamespace !== namespace) return null; if (lane && endpointLane && endpointLane !== lane) return null; return text; } function runtimeNamespaceForEndpoint(value) { const match = String(value ?? "").match(/\.((?:hwlab-[a-z0-9-]+))\.svc\.cluster\.local(?::|\/|$)/iu); return match?.[1] ?? null; } function runtimeLaneForEndpoint(value) { const text = String(value ?? "").toLowerCase(); if (/hwlab-v02|:19666(?:\/|$)|:19667(?:\/|$)/u.test(text)) return "v02"; if (/hwlab-prod|:18666(?:\/|$)|:18667(?:\/|$)/u.test(text)) return "prod"; if (/hwlab-dev|:17666(?:\/|$)|:17667(?:\/|$)|:16666(?:\/|$)|:16667(?:\/|$)/u.test(text)) return "dev"; return null; } function defaultRuntimeNamespace(env = process.env) { const profile = runtimeLaneForEnv(env); if (profile === "prod") return "hwlab-prod"; if (profile === "v02") return "hwlab-v02"; return "hwlab-dev"; } function localCloudApiUrl(env = process.env) { const port = parsePositiveInteger(env.HWLAB_CLOUD_API_PORT, 6667); return `http://127.0.0.1:${port}`; } function codeAgentProviderProfileEnv(env = process.env, params = {}) { const profile = normalizeCodeAgentProviderProfile(params.providerProfile ?? params.codeAgentProviderProfile ?? env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE); if (profile === "runtime-default") return env; const overlay = { ...env }; overlay.HWLAB_CODE_AGENT_SELECTED_PROVIDER_PROFILE = profile; overlay.HWLAB_CODE_AGENT_PROVIDER_PROFILE_LABEL = CODE_AGENT_PROVIDER_PROFILE_LABELS[profile] ?? profile; overlay.HWLAB_CODE_AGENT_PROVIDER = "codex-stdio"; if (profile === "codex-api") { overlay.HWLAB_CODE_AGENT_MODEL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_CODEX_API_MODEL, env.HWLAB_CODE_AGENT_LEGACY_CODEX_MODEL, DEFAULT_CODE_AGENT_CODEX_API_MODEL); overlay.HWLAB_CODE_AGENT_OPENAI_BASE_URL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_CODEX_API_BASE_URL, env.HWLAB_CODE_AGENT_LEGACY_OPENAI_BASE_URL, DEFAULT_CODE_AGENT_CODEX_API_BASE_URL); } else if (profile === "minimax-m3") { overlay.HWLAB_CODE_AGENT_MODEL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_MINIMAX_M3_MODEL, DEFAULT_CODE_AGENT_MINIMAX_M3_MODEL); } else { const dynamicProfile = profile !== "deepseek"; overlay.HWLAB_CODE_AGENT_MODEL = dynamicProfile ? firstNonEmptyValue(env[`HWLAB_CODE_AGENT_${providerProfileEnvKey(profile)}_MODEL`], profile) : firstNonEmptyValue(env.HWLAB_CODE_AGENT_DEEPSEEK_MODEL, DEFAULT_CODE_AGENT_DEEPSEEK_MODEL); if (!dynamicProfile || !codeAgentAgentRunAdapterEnabled(env)) { overlay.HWLAB_CODE_AGENT_OPENAI_BASE_URL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_DEEPSEEK_BASE_URL, defaultDeepSeekBaseUrlForEnv(env)); } } overlay.NO_PROXY = mergeNoProxyEntries(env.NO_PROXY, codeAgentClusterNoProxyEntries(env)); overlay.no_proxy = mergeNoProxyEntries(env.no_proxy ?? env.NO_PROXY, codeAgentClusterNoProxyEntries(env)); return overlay; } function normalizeCodeAgentProviderProfile(value) { const text = String(value ?? DEFAULT_CODE_AGENT_PROVIDER_PROFILE).trim().toLowerCase(); if (!text) return DEFAULT_CODE_AGENT_PROVIDER_PROFILE; if (text === "runtime-default") return text; const normalized = CODE_AGENT_PROVIDER_PROFILE_ALIASES[text] ?? text; return CODE_AGENT_PROVIDER_PROFILE_ID_PATTERN.test(normalized) ? normalized : DEFAULT_CODE_AGENT_PROVIDER_PROFILE; } function providerProfileEnvKey(profile) { return String(profile ?? "") .trim() .toUpperCase() .replace(/[^A-Z0-9]+/gu, "_") .replace(/^_+|_+$/gu, "") || "PROFILE"; } function defaultDeepSeekBaseUrlForEnv(env = process.env) { const namespace = env.HWLAB_GITOPS_PROFILE === "prod" || env.HWLAB_ENVIRONMENT === "prod" ? "hwlab-prod" : "hwlab-dev"; return `http://hwlab-deepseek-proxy.${namespace}.svc.cluster.local:4000/v1/responses`; } function codeAgentClusterNoProxyEntries(env = process.env) { const namespace = env.HWLAB_GITOPS_PROFILE === "prod" || env.HWLAB_ENVIRONMENT === "prod" ? "hwlab-prod" : "hwlab-dev"; return [ `${namespace}.svc.cluster.local`, ".svc", ".cluster.local", "127.0.0.1", "localhost", "::1", "10.0.0.0/8", "10.42.0.0/16", "10.43.0.0/16", "hyueapi.com", ".hyueapi.com", "hyui.com", ".hyui.com", "api.minimaxi.com", ".minimaxi.com" ]; } function mergeNoProxyEntries(current, additions = []) { return uniqueStrings([...String(current ?? "").split(","), ...additions]).join(","); } function firstNonEmptyValue(...values) { for (const value of values) { const text = String(value ?? "").trim(); if (text) return text; } return null; } function submitCodeAgentChatTurn({ params, options, traceId }) { const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; const results = options.codeAgentChatResults ?? createCodeAgentChatResultStore(); const acceptedPayload = { accepted: true, status: "running", traceId, conversationId: safeConversationId(params.conversationId) || null, sessionId: safeSessionId(params.sessionId) || null, threadId: safeOpaqueId(params.threadId) || null, projectId: params.projectId ?? null, updatedAt: new Date().toISOString() }; void recordCodeAgentSessionOwner({ payload: acceptedPayload, params, options, status: "running" }); if (codeAgentAgentRunAdapterEnabled(options.env ?? process.env)) { const initial = withCodeAgentBillingReservation(initialAgentRunChatResult({ params, options, traceId }), params); results.set(traceId, annotateOwner(initial, params)); const run = async () => { let executionOptions = options; try { executionOptions = { ...options, ...(await codeAgentChatExecutionOptions(options, params)) }; const payload = await submitAgentRunChatTurn({ params, options: executionOptions, traceId, traceStore, results }); if (isCodeAgentResultCanceled(results.get(traceId))) return; const owned = annotateOwner(withCodeAgentBillingReservation(payload, params), params); await recordCodeAgentSessionOwner({ payload: owned, params, options: executionOptions, status: "running" }); results.set(traceId, owned); } catch (error) { if (isCodeAgentResultCanceled(results.get(traceId))) return; const payload = annotateOwner({ ...initial, status: "failed", error: { code: error?.code ?? "agentrun_dispatch_failed", layer: "agentrun", retryable: true, message: error?.message ?? "AgentRun dispatch failed", userMessage: "AgentRun 调度失败;trace/result 轮询已保留错误。" }, blocker: { code: error?.code ?? "agentrun_dispatch_failed", layer: "agentrun", retryable: true, summary: error?.message ?? "AgentRun dispatch failed" }, runnerTrace: traceStore.snapshot(traceId), updatedAt: new Date().toISOString() }, params); recordCodeAgentConversationFact(payload, executionOptions); await finalizeCodeAgentBillingUsage({ payload, params, options: executionOptions }); results.set(traceId, payload); traceStore.append(traceId, { type: "result", status: "failed", label: "agentrun:dispatch:failed", errorCode: payload.error.code, message: payload.error.message, terminal: true }); await recordCodeAgentSessionOwner({ payload, params, options: executionOptions, status: "failed" }); } }; setImmediate(() => { run(); }); return; } results.set(traceId, acceptedPayload); traceStore.ensure(traceId, { runnerKind: "codex-app-server-stdio-runner", workspace: options.workspace ?? options.env?.HWLAB_CODE_AGENT_CODEX_WORKSPACE ?? options.env?.HWLAB_CODE_AGENT_WORKSPACE ?? null, sandbox: options.env?.HWLAB_CODE_AGENT_CODEX_SANDBOX ?? null, sessionMode: "codex-app-server-stdio-long-lived", implementationType: "repo-owned-codex-app-server-stdio-session" }); traceStore.append(traceId, { type: "request", status: "accepted", label: "request:accepted-short-connection", message: "Code Agent request accepted; client must poll trace/result with short HTTP requests.", waitingFor: "codex-stdio" }); const run = async () => { try { const payload = await runCodeAgentChat(params, options); if (isCodeAgentResultCanceled(results.get(traceId))) return; await finalizeCodeAgentBillingUsage({ payload, params, options }); await recordCodeAgentSessionOwner({ payload, params, options, status: payload.status === "completed" ? "active" : payload.status }); results.set(traceId, annotateOwner(payload, params)); traceStore.append(traceId, { type: "result", status: payload.status === "completed" ? "completed" : "failed", label: `result:${payload.status}`, message: payload.status === "completed" ? "Code Agent result is ready for short-connection polling." : payload.error?.message ?? "Code Agent completed without a successful reply.", sessionId: payload.sessionId ?? payload.session?.sessionId, sessionStatus: payload.session?.status, terminal: true }); } catch (error) { if (isCodeAgentResultCanceled(results.get(traceId))) return; const payload = { status: "failed", traceId, conversationId: safeConversationId(params.conversationId) || null, sessionId: safeSessionId(params.sessionId) || null, error: { code: "code_agent_background_failed", layer: "api", retryable: true, message: error?.message ?? "Code Agent background turn failed", userMessage: "Code Agent 后台任务失败;trace/result 轮询已保留错误。" }, updatedAt: new Date().toISOString() }; await finalizeCodeAgentBillingUsage({ payload, params, options }); results.set(traceId, payload); traceStore.append(traceId, { type: "result", status: "failed", label: "result:failed", errorCode: payload.error.code, message: payload.error.message, terminal: true }); } }; setImmediate(() => { run(); }); } function codeAgentChatShortConnectionRequested(request, params, options = {}) { const header = firstHeaderValue(request, "prefer"); const explicit = firstHeaderValue(request, "x-hwlab-short-connection"); const envValue = options.env?.HWLAB_CODE_AGENT_CHAT_SHORT_CONNECTION; return codeAgentAgentRunAdapterEnabled(options.env ?? process.env) || /(?:^|,)\s*respond-async\s*(?:,|$)/iu.test(String(header ?? "")) || truthyFlag(explicit) || params?.shortConnection === true || truthyFlag(envValue); } async function preflightCodeAgentBilling({ params = {}, options = {}, traceId, response } = {}) { const client = options.userBillingClient; if (!codeAgentBillingEnabled(options) || !client?.configured || !options.userBillingAuth?.active) return null; const estimatedCredits = parsePositiveInteger(options.env?.HWLAB_USER_BILLING_CODE_AGENT_ESTIMATED_CREDITS, 1); const body = { ...(options.actorApiKeySecret ? { apiKey: options.actorApiKeySecret } : { userId: options.actor?.id }), serviceId: "hwlab-code-agent", estimatedCredits, idempotencyKey: `code-agent:${traceId}:preflight`, metadata: codeAgentBillingMetadata(params, traceId, { stage: "preflight" }) }; const result = await client.billingPreflight(body); if (result.ok && result.body?.allowed !== false) return { reservation: sanitizeBillingReservation(result.body) }; const status = [402, 403, 429].includes(Number(result.status)) ? Number(result.status) : 503; sendJson(response, status, { ok: false, accepted: false, status: status === 402 ? "payment_required" : status === 403 ? "not_entitled" : status === 429 ? "rate_limited" : "billing_unavailable", traceId, error: { code: result.error?.code ?? (status === 402 ? "insufficient_credits" : "user_billing_preflight_failed"), layer: "billing", retryable: status === 429 || status === 503, message: result.error?.message ?? "Code Agent billing preflight failed", route: "/v1/agent/chat" }, blocker: { code: result.error?.code ?? (status === 402 ? "insufficient_credits" : "user_billing_preflight_failed"), layer: "billing", retryable: status === 429 || status === 503, summary: result.error?.message ?? "Code Agent billing preflight failed" }, valuesRedacted: true }); return { blocked: true }; } async function recordCodeAgentBillingUsage({ payload = {}, params = {}, options = {} } = {}) { const reservation = params.userBillingReservation ?? payload.userBillingReservation; const reservationId = typeof reservation?.reservationId === "string" ? reservation.reservationId : ""; const client = options.userBillingClient; if (payload.billing?.recorded === true || payload.billing?.released === true || payload.status === "running") return payload.billing ?? null; if (!reservationId || !client?.configured) return null; const traceId = safeTraceId(payload.traceId ?? params.traceId); const usedTokens = codeAgentUsedTokens(payload); const usedCredits = parsePositiveInteger(options.env?.HWLAB_USER_BILLING_CODE_AGENT_USED_CREDITS, 1); const result = await client.billingRecord({ reservationId, serviceId: "hwlab-code-agent", usedCredits, usedTokens, idempotencyKey: `code-agent:${traceId}:record`, metadata: codeAgentBillingMetadata(params, traceId, { stage: "record", status: payload.status ?? "unknown" }) }); const billing = result.ok ? sanitizeBillingRecord(result.body, reservation) : { recorded: false, reservationId, errorCode: result.error?.code ?? "user_billing_record_failed", valuesRedacted: true }; payload.billing = billing; const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; if (traceId) { traceStore.append(traceId, { type: "billing", status: result.ok ? "recorded" : "degraded", label: result.ok ? "billing:recorded" : "billing:record_failed", reservationId, recordId: result.ok ? result.body?.recordId ?? null : null, errorCode: result.ok ? null : billing.errorCode, valuesPrinted: false }); } return billing; } async function finalizeCodeAgentBillingUsage({ payload = {}, params = {}, options = {} } = {}) { if (payload.billing?.recorded === true || payload.billing?.released === true) return payload.billing; if (payload.status === "running") return null; if (payload.status === "completed") { return recordCodeAgentBillingUsage({ payload, params, options }); } return releaseCodeAgentBillingReservation({ payload, params, options }); } async function releaseCodeAgentBillingReservation({ payload = {}, params = {}, options = {} } = {}) { const reservation = params.userBillingReservation ?? payload.userBillingReservation; const reservationId = typeof reservation?.reservationId === "string" ? reservation.reservationId : ""; const client = options.userBillingClient; if (payload.billing?.recorded === true || payload.billing?.released === true || !reservationId || !client?.configured) return payload.billing ?? null; const traceId = safeTraceId(payload.traceId ?? params.traceId); const releaseReason = String(payload.status ?? "not_completed").trim() || "not_completed"; const releaseBody = { reservationId, serviceId: "hwlab-code-agent", idempotencyKey: `code-agent:${traceId}:release`, reason: releaseReason, metadata: codeAgentBillingMetadata(params, traceId, { stage: "release", status: payload.status ?? "unknown", errorCode: payload.error?.code ?? payload.blocker?.code ?? null }) }; const result = typeof client.billingRelease === "function" ? await client.billingRelease(releaseBody) : { ok: false, error: { code: "user_billing_release_not_supported" } }; const billing = result.ok ? sanitizeBillingRelease(result.body, reservation) : { released: false, reservationId, errorCode: result.error?.code ?? "user_billing_release_failed", valuesRedacted: true }; payload.billing = billing; const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; if (traceId) { traceStore.append(traceId, { type: "billing", status: result.ok ? "released" : "degraded", label: result.ok ? "billing:released" : "billing:release_failed", reservationId, reason: releaseReason, errorCode: result.ok ? null : billing.errorCode, valuesPrinted: false }); } return billing; } function withCodeAgentBillingReservation(payload = {}, params = {}) { if (!payload || typeof payload !== "object" || payload.userBillingReservation) return payload; const reservation = params.userBillingReservation; return reservation ? { ...payload, userBillingReservation: reservation } : payload; } function codeAgentBillingEnabled(options = {}) { const env = options.env ?? process.env; if (String(env.HWLAB_USER_BILLING_CODE_AGENT_ENABLED ?? "").trim() === "0") return false; return Boolean(options.userBillingClient?.configured); } function codeAgentBillingMetadata(params = {}, traceId, extra = {}) { return { traceId, conversationId: safeConversationId(params.conversationId) || null, sessionId: safeSessionId(params.sessionId) || null, projectId: textValue(params.projectId) || null, route: "/v1/agent/chat", resource: "code-agent-turn", ...extra, valuesRedacted: true }; } function sanitizeBillingReservation(value = {}) { return { allowed: value.allowed === true, reservationId: textValue(value.reservationId) || null, resourceType: textValue(value.resourceType) || null, planId: textValue(value.planId) || null, estimatedCredits: Number.isFinite(Number(value.estimatedCredits)) ? Number(value.estimatedCredits) : null, estimatedTokens: Number.isFinite(Number(value.estimatedTokens)) ? Number(value.estimatedTokens) : null, expiresAt: textValue(value.expiresAt) || null, valuesRedacted: true }; } function sanitizeBillingRecord(value = {}, reservation = {}) { return { recorded: true, reservationId: textValue(reservation.reservationId) || null, recordId: textValue(value.recordId) || null, credits: Number.isFinite(Number(value.credits)) ? Number(value.credits) : null, balance: Number.isFinite(Number(value.balance)) ? Number(value.balance) : null, valuesRedacted: true }; } function sanitizeBillingRelease(value = {}, reservation = {}) { return { released: true, reservationId: textValue(value.reservationId) || textValue(reservation.reservationId) || null, releasedCredits: Number.isFinite(Number(value.releasedCredits)) ? Number(value.releasedCredits) : null, status: textValue(value.status) || "cancelled", valuesRedacted: true }; } function codeAgentUsedTokens(payload = {}) { const usage = payload.usage && typeof payload.usage === "object" ? payload.usage : null; const traceSummary = payload.traceSummary && typeof payload.traceSummary === "object" ? payload.traceSummary : null; const candidates = [ usage?.totalTokens, usage?.total_tokens, usage?.tokens, Number(usage?.inputTokens ?? usage?.input_tokens ?? 0) + Number(usage?.outputTokens ?? usage?.output_tokens ?? 0), traceSummary?.tokenCount, traceSummary?.tokens ]; for (const value of candidates) { const parsed = Number(value); if (Number.isFinite(parsed) && parsed > 0) return Math.ceil(parsed); } return 0; } export async function handleCodeAgentChatResultHttp(request, response, url, options) { const parts = url.pathname.split("/").filter(Boolean); const traceId = decodeURIComponent(parts[4] ?? ""); if (!safeTraceId(traceId)) { sendJson(response, 400, { error: { code: "invalid_trace_id", message: "traceId must start with trc_ and contain only safe identifier characters" } }); return; } const results = options.codeAgentChatResults; const result = results?.get(traceId) ?? null; if (result && !canAccessOwnedResult(result, options.actor)) { sendJson(response, 403, { error: { code: "agent_session_owner_required", message: "Only the session owner or admin can read this Code Agent result" } }); return; } const adapterEnabled = codeAgentAgentRunAdapterEnabled(options.env ?? process.env); if ((result?.agentRun?.runId && adapterEnabled) || (!result && adapterEnabled)) { try { const synced = await syncAgentRunChatResult({ traceId, currentResult: result, options, traceStore: options.traceStore ?? defaultCodeAgentTraceStore }); if (synced.result && !canAccessOwnedResult(synced.result, options.actor)) { sendJson(response, 403, { error: { code: "agent_session_owner_required", message: "Only the session owner or admin can read this Code Agent result" } }); return; } if (synced.result && synced.result.status !== "running") { await finalizeCodeAgentBillingUsage({ payload: synced.result, params: synced.result, options }); recordCodeAgentConversationFact(synced.result, options); await recordCodeAgentSessionOwner({ payload: synced.result, params: synced.result, options, status: codeAgentOwnerStatusForResult(synced.result) }); sendJson(response, 200, compactCodeAgentChatResultPayload(synced.result, options)); return; } if (synced.result) { sendJson(response, 202, { accepted: true, status: "running", shortConnection: true, traceId, agentRun: synced.result.agentRun, runnerTrace: compactRunnerTraceForResult(synced.runnerTrace, resultTraceEventLimit(options)), waitingFor: synced.runnerTrace?.waitingFor ?? "agentrun-result" }); return; } } catch (error) { const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; traceStore.append(traceId, { type: "result", status: "degraded", label: "agentrun:result:poll-failed", errorCode: error?.code ?? "agentrun_result_poll_failed", message: error?.message ?? "AgentRun result polling failed", waitingFor: "agentrun-result" }); if (result?.agentRun && result.status === "running") { sendJson(response, 202, { accepted: true, status: "running", shortConnection: true, traceId, agentRun: result.agentRun, runnerTrace: compactRunnerTraceForResult(traceStore.snapshot(traceId), resultTraceEventLimit(options)), waitingFor: "agentrun-result", degraded: true, error: { code: error?.code ?? "agentrun_result_poll_failed", message: error?.message ?? "AgentRun result polling failed" } }); return; } if (result?.agentRun) { sendJson(response, error?.statusCode === 404 ? 404 : 502, { error: { code: error?.code ?? "agentrun_result_poll_failed", message: error?.message ?? "AgentRun result polling failed", traceId, runId: result.agentRun.runId ?? null, commandId: result.agentRun.commandId ?? null } }); return; } } } if (result && result.status !== "running") { sendJson(response, 200, compactCodeAgentChatResultPayload(result, options)); return; } const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; const runnerTrace = traceStore.snapshot(traceId); if (result || runnerTrace.status !== "missing") { sendJson(response, 202, { accepted: true, status: "running", shortConnection: true, traceId, runnerTrace: compactRunnerTraceForResult(runnerTrace, resultTraceEventLimit(options)), waitingFor: runnerTrace.waitingFor ?? "codex-stdio" }); return; } sendJson(response, 404, { error: { code: "code_agent_result_not_found", message: `No Code Agent result is registered for ${traceId}` } }); } export async function handleCodeAgentInspectHttp(request, response, url, options) { const query = { conversationId: safeConversationId(url.searchParams.get("conversationId")), sessionId: safeSessionId(url.searchParams.get("sessionId")), threadId: safeOpaqueId(url.searchParams.get("threadId")), traceId: safeTraceId(url.searchParams.get("traceId")) }; const sessionRegistry = options.sessionRegistry; const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; const manager = options.codexStdioManager; const managerDescription = manager && typeof manager.describe === "function" ? manager.describe() : null; const managerRecentSessions = Array.isArray(managerDescription?.recentSessions) ? managerDescription.recentSessions : []; const directManagerSession = query.sessionId && manager && typeof manager.get === "function" ? manager.get(query.sessionId, { conversationId: query.conversationId }) : null; const matchedManagerSession = directManagerSession ?? managerRecentSessions.find((session) => sessionMatchesInspectQuery(session, query)) ?? null; const registryInspect = sessionRegistry && typeof sessionRegistry.inspect === "function" ? sessionRegistry.inspect(query) : { ok: false, status: "unavailable", traceIds: [], session: null, conversationFacts: null }; const resultEvidence = await codeAgentInspectResultEvidence(query.traceId, options); const session = matchedManagerSession ?? registryInspect.session ?? resultEvidence.session ?? null; const conversationFacts = registryInspect.conversationFacts ?? resultEvidence.conversationFacts ?? null; const requestedRunnerTrace = query.traceId ? traceStore.snapshot(query.traceId) : null; const requestedTraceFound = Boolean(requestedRunnerTrace && requestedRunnerTrace.status !== "missing"); const traceIds = uniqueStrings([ requestedTraceFound ? query.traceId : null, session?.currentTraceId, session?.lastTraceId, conversationFacts?.latestTraceId, resultEvidence.latestTraceId, ...(Array.isArray(conversationFacts?.traceIds) ? conversationFacts.traceIds : []), ...(Array.isArray(registryInspect.traceIds) ? registryInspect.traceIds : []) ]); const latestTraceId = traceIds[0] ?? null; const runnerTrace = latestTraceId ? latestTraceId === query.traceId && requestedRunnerTrace ? requestedRunnerTrace : traceStore.snapshot(latestTraceId) : null; const found = Boolean(registryInspect.ok || session || latestTraceId); sendJson(response, found ? 200 : 404, { ok: found, action: "code-agent.chat.inspect", status: found ? "found" : "not_found", query, session, conversationFacts, traceIds, latestTraceId, traceUrl: latestTraceId ? `/v1/agent/chat/trace/${encodeURIComponent(latestTraceId)}` : null, resultUrl: latestTraceId ? `/v1/agent/chat/result/${encodeURIComponent(latestTraceId)}` : null, runnerTrace, valuesRedacted: true, secretMaterialStored: false }); } async function codeAgentInspectResultEvidence(traceId, options = {}) { if (!safeTraceId(traceId)) return { session: null, conversationFacts: null, latestTraceId: null }; const cached = options.codeAgentChatResults?.get?.(traceId) ?? null; const persisted = cached ? null : await loadPersistedAgentRunResult(traceId, options); const result = cached ?? persisted ?? null; if (!result || typeof result !== "object") return { session: null, conversationFacts: null, latestTraceId: null }; const agentRun = result.agentRun && typeof result.agentRun === "object" ? result.agentRun : null; const resultSession = result.session && typeof result.session === "object" ? result.session : null; const sessionReuse = result.sessionReuse && typeof result.sessionReuse === "object" ? result.sessionReuse : null; const providerTrace = result.providerTrace && typeof result.providerTrace === "object" ? result.providerTrace : null; const sessionId = safeSessionId(result.sessionId ?? resultSession?.sessionId ?? sessionReuse?.sessionId ?? agentRun?.sessionId) || null; const conversationId = safeConversationId(result.conversationId ?? resultSession?.conversationId ?? sessionReuse?.conversationId ?? agentRun?.conversationId) || null; const threadId = safeOpaqueId(result.threadId ?? resultSession?.threadId ?? sessionReuse?.threadId ?? providerTrace?.threadId ?? agentRun?.threadId) || null; const status = textValue(resultSession?.status ?? result.status ?? agentRun?.status) || null; const updatedAt = textValue(result.updatedAt ?? agentRun?.updatedAt ?? resultSession?.updatedAt) || null; const session = sessionId || conversationId || threadId ? { sessionId, conversationId, threadId, status, lastTraceId: traceId, source: "code-agent-result", agentRun: agentRun ? agentRunSessionEvidence(result).agentRun : null, valuesRedacted: true, secretMaterialStored: false } : null; const conversationFacts = conversationId ? { conversationId, sessionId, threadId, latestTraceId: traceId, traceIds: [traceId], turnCount: 1, source: "code-agent-result", latestStatus: status, updatedAt, valuesRedacted: true, secretMaterialStored: false } : null; return { session, conversationFacts, latestTraceId: traceId }; } function sessionMatchesInspectQuery(session, query) { if (!session || typeof session !== "object") return false; if (query.sessionId && session.sessionId === query.sessionId) return true; if (query.threadId && session.threadId === query.threadId) return true; if (query.conversationId && session.conversationId === query.conversationId) return true; if (query.traceId && (session.currentTraceId === query.traceId || session.lastTraceId === query.traceId)) return true; return false; } export async function handleCodeAgentCancelHttp(request, response, options) { const body = await readBody(request, options.bodyLimitBytes); let params = {}; try { params = body ? JSON.parse(body) : {}; } catch (error) { sendJson(response, 400, { ...createCodeAgentErrorPayload({ code: "parse_error", message: "Invalid JSON body", reason: error.message, traceId: getHeader(request, "x-trace-id") || "trc_unassigned", layer: "api", retryable: true }) }); return; } if (!params || typeof params !== "object" || Array.isArray(params)) { sendJson(response, 400, { ...createCodeAgentErrorPayload({ code: "invalid_params", message: "Code Agent cancel body must be a JSON object", traceId: getHeader(request, "x-trace-id") || "trc_unassigned", layer: "api", retryable: true }) }); return; } const traceId = safeTraceId(getHeader(request, "x-trace-id") || params.traceId); const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; const snapshot = traceId ? traceStore.snapshot(traceId) : null; const currentResult = traceId ? options.codeAgentChatResults?.get(traceId) ?? await loadPersistedAgentRunResult(traceId, options) : null; if (currentResult?.agentRun?.commandId) { const mismatch = codeAgentCancelScopeMismatch(params, currentResult); if (mismatch) { traceStore.append(traceId, { type: "cancel", status: "blocked", label: "agentrun:cancel:scope_mismatch", errorCode: "cancel_scope_mismatch", message: `Cancel request ${mismatch.field} does not match the trace owner; AgentRun cancel was not forwarded.`, requested: mismatch.requested, expected: mismatch.expected, waitingFor: "cancel-scope", valuesPrinted: false }); sendJson(response, 409, cancelBlockedPayload({ code: "cancel_scope_mismatch", message: `取消请求的 ${mismatch.field} 与 trace 归属不一致,已拒绝转发 AgentRun cancel,避免误取消其他 session。`, traceId, conversationId: safeConversationId(params.conversationId) || safeConversationId(currentResult.conversationId) || null, sessionId: safeSessionId(params.sessionId) || safeSessionId(currentResult.sessionId) || null, runnerTrace: traceStore.snapshot(traceId), status: "blocked" })); return; } const payload = await cancelAgentRunChatTurn({ traceId, currentResult, options, traceStore }); if (payload) { await recordCodeAgentSessionOwner({ payload, params: { ...params, traceId, ownerUserId: options.actor?.id, ownerRole: options.actor?.role }, options, status: "canceled" }); sendJson(response, 200, payload); return; } } const sessionId = safeSessionId(params.sessionId) || safeSessionId(snapshot?.sessionId); const conversationId = safeConversationId(params.conversationId); const manager = options.codexStdioManager; if (!traceId) { sendJson(response, 400, cancelBlockedPayload({ code: "cancel_trace_missing", message: "traceId is required to cancel the current Code Agent request.", traceId: "trc_unassigned", conversationId, sessionId })); return; } if (!manager || typeof manager.get !== "function" || typeof manager.cancel !== "function") { traceStore.append(traceId, { type: "cancel", status: "unsupported", label: "cancel:unsupported", errorCode: "cancel_unsupported", message: "Codex stdio cancel/interrupt backend is not available on this runtime.", waitingFor: "session-control" }); const runnerTrace = traceStore.snapshot(traceId); sendJson(response, 501, cancelBlockedPayload({ code: "cancel_unsupported", message: "当前后端不支持 Code Agent interrupt/cancel;本次按 unsupported/degraded 返回,不会静默假装已中断。", traceId, conversationId, sessionId, runnerTrace, status: "degraded", unsupported: true, degraded: true })); return; } if (!sessionId) { traceStore.append(traceId, { type: "cancel", status: "blocked", label: "cancel:not_cancelable", errorCode: "cancel_session_missing", message: "Cancel request did not include a bound Codex stdio sessionId.", waitingFor: "session-binding" }); sendJson(response, 409, cancelBlockedPayload({ code: "cancel_session_missing", message: "当前请求尚未暴露可取消的 Codex stdio sessionId;页面不能只隐藏 UI,已保留输入和 trace。", traceId, conversationId, sessionId: null, runnerTrace: traceStore.snapshot(traceId) })); return; } const currentSession = manager.get(sessionId, { conversationId }) ?? null; if (!currentSession || !["busy", "creating"].includes(currentSession.status)) { traceStore.append(traceId, { type: "cancel", status: "blocked", label: "cancel:not_in_flight", errorCode: "cancel_not_in_flight", message: `Session ${sessionId} is not an in-flight Codex stdio request.`, sessionId, sessionStatus: currentSession?.status ?? "missing" }); sendJson(response, 409, cancelBlockedPayload({ code: currentSession ? "cancel_not_in_flight" : "cancel_session_not_found", message: currentSession ? `当前 session 状态为 ${currentSession.status},没有可取消的 in-flight Codex stdio 请求。` : `没有找到 sessionId=${sessionId} 的 Codex stdio session。`, traceId, conversationId, sessionId, session: currentSession, runnerTrace: traceStore.snapshot(traceId) })); return; } const canceledSession = manager.cancel(sessionId, { traceId, conversationId, reason: "user_cancel" }); traceStore.append(traceId, { type: "cancel", status: "canceled", label: "cancel:canceled", message: "User canceled the current Codex stdio request.", sessionId, sessionStatus: canceledSession?.status ?? "canceled", sessionLifecycleStatus: codeAgentSessionLifecycleSummary({ session: canceledSession, status: "canceled" }).status, waitingFor: "user-retry", terminal: true }); const runnerTraceSnapshot = traceStore.snapshot(traceId); const sessionSummary = codeAgentSessionLifecycleSummary({ session: canceledSession, runnerTrace: runnerTraceSnapshot, status: "canceled" }); const runnerTrace = { ...runnerTraceSnapshot, sessionLifecycleStatus: sessionSummary.status }; const payload = { accepted: true, canceled: true, status: "canceled", conversationId: conversationId ?? canceledSession?.conversationId ?? null, sessionId, traceId, session: canceledSession, sessionLifecycleStatus: sessionSummary.status, sessionLifecycle: sessionSummary, sessionSummary, runnerTrace, lastTraceEvent: runnerTrace.lastEvent, retryable: true, error: { code: "codex_stdio_canceled", layer: "session", category: "canceled", retryable: true, message: "user canceled current Code Agent request", userMessage: "当前 Codex stdio 请求已取消;输入、sessionId、traceId 和最后 trace event 已保留,可重试上一条消息。", traceId, route: "/v1/agent/chat/cancel", toolName: "codex-stdio.cancel" }, userMessage: "当前 Codex stdio 请求已取消;输入、sessionId、traceId 和最后 trace event 已保留,可重试上一条消息。", updatedAt: new Date().toISOString() }; await recordCodeAgentSessionOwner({ payload, params: { ...params, traceId, ownerUserId: options.actor?.id, ownerRole: options.actor?.role }, options, status: "canceled" }); recordCodeAgentConversationFact(payload, options); options.codeAgentChatResults?.set(traceId, annotateOwner(payload, { ownerUserId: options.actor?.id, ownerRole: options.actor?.role })); sendJson(response, 200, payload); } function recordCodeAgentConversationFact(payload = {}, options = {}) { const registry = options.sessionRegistry; const conversationId = safeConversationId(payload.conversationId); if (!conversationId || typeof registry?.recordFact !== "function") return null; try { return registry.recordFact(conversationId, { kind: payload.status === "completed" ? "runner_turn" : "runner_turn_terminal", conversationId, sessionId: payload.sessionId, traceId: payload.traceId, provider: payload.provider, backend: payload.backend, workspace: payload.workspace, sandbox: payload.sandbox, session: payload.session, sessionMode: payload.sessionMode, sessionReuse: payload.sessionReuse, implementationType: payload.implementationType, runner: payload.runner, runnerTrace: payload.runnerTrace, partialContext: payload.partialContext ?? payload.runnerTrace?.partialContext, capabilityLevel: payload.capabilityLevel, toolCalls: payload.toolCalls, skills: payload.skills }, { now: options.now }); } catch { return null; } } export async function handleCodeAgentSteerHttp(request, response, options) { const body = await readBody(request, options.bodyLimitBytes); let params = {}; try { params = body ? JSON.parse(body) : {}; } catch (error) { sendJson(response, 400, { ...createCodeAgentErrorPayload({ code: "parse_error", message: "Invalid JSON body", reason: error.message, traceId: getHeader(request, "x-trace-id") || "trc_unassigned", layer: "api", retryable: true, route: "/v1/agent/chat/steer" }) }); return; } if (!params || typeof params !== "object" || Array.isArray(params)) { sendJson(response, 400, { ...createCodeAgentErrorPayload({ code: "invalid_params", message: "Code Agent steer body must be a JSON object", traceId: getHeader(request, "x-trace-id") || "trc_unassigned", layer: "api", retryable: true, route: "/v1/agent/chat/steer" }) }); return; } const traceId = safeTraceId(getHeader(request, "x-trace-id") || params.traceId || params.targetTraceId); const message = firstNonEmptyValue(params.message, params.prompt, params.text); if (!traceId || !message) { sendJson(response, 400, { ...createCodeAgentErrorPayload({ code: !traceId ? "steer_trace_missing" : "steer_message_missing", message: !traceId ? "traceId is required to steer the current Code Agent turn." : "message is required to steer the current Code Agent turn.", traceId: traceId || "trc_unassigned", layer: "api", retryable: true, route: "/v1/agent/chat/steer" }) }); return; } const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; const currentResult = options.codeAgentChatResults?.get?.(traceId) ?? await loadPersistedAgentRunResult(traceId, options); if (!canAccessOwnedResult(currentResult, options.actor)) { sendJson(response, 403, { ok: false, status: "forbidden", traceId, error: { code: "code_agent_trace_forbidden", message: "Code Agent trace is not visible to the current actor" } }); return; } if (!currentResult?.agentRun?.runId || !currentResult?.agentRun?.commandId) { await clearStaleWorkbenchSteerTrace({ params, options, traceId, reason: "steer-trace-not-found" }); sendJson(response, 404, { ok: false, status: "not_found", traceId, error: { code: "steer_trace_not_found", message: "No AgentRun-backed Code Agent turn was found for the requested traceId." }, route: "/v1/agent/chat/steer", runnerTrace: traceStore.snapshot(traceId) }); return; } try { const payload = await steerAgentRunChatTurn({ traceId, currentResult, params: { ...params, message, ownerUserId: options.actor?.id ?? params.ownerUserId, ownerRole: options.actor?.role ?? params.ownerRole }, options, traceStore }); sendJson(response, 202, payload); setImmediate(() => { void recordCodeAgentSessionOwner({ payload: currentResult, params: { ...params, traceId, ownerUserId: options.actor?.id, ownerRole: options.actor?.role }, options, status: "running" }); }); } catch (error) { traceStore.append(traceId, { type: "backend", status: "failed", label: "agentrun:steer:failed", errorCode: error?.code ?? "agentrun_steer_failed", message: error?.message ?? "AgentRun steer command failed.", terminal: false, valuesPrinted: false }); sendJson(response, error?.statusCode === 404 ? 404 : 409, { ok: false, accepted: false, status: "failed", traceId, route: "/v1/agent/chat/steer", conversationId: safeConversationId(currentResult.conversationId ?? params.conversationId) || null, sessionId: safeSessionId(currentResult.sessionId ?? params.sessionId) || null, threadId: safeOpaqueId(currentResult.threadId ?? params.threadId) || null, agentRun: currentResult.agentRun, runnerTrace: traceStore.snapshot(traceId), error: { code: error?.code ?? "agentrun_steer_failed", layer: "agentrun", category: "steer_failed", retryable: true, message: error?.message ?? "AgentRun steer command failed.", traceId, route: "/v1/agent/chat/steer", toolName: "agentrun.command.steer" } }); } } async function recordCodeAgentSessionOwner({ payload = {}, params = {}, options = {}, status = "active", preserveLastTraceId = false } = {}) { const ownerUserId = options.actor?.id ?? params.ownerUserId; if (!ownerUserId || !options.accessController?.recordAgentSessionOwner) return null; const traceId = safeTraceId(payload.traceId ?? params.traceId); const session = payload.session && typeof payload.session === "object" ? payload.session : null; const sessionReuse = payload.sessionReuse && typeof payload.sessionReuse === "object" ? payload.sessionReuse : null; const sessionId = safeSessionId(payload.sessionId ?? session?.sessionId ?? sessionReuse?.sessionId ?? params.sessionId) || null; const conversationId = safeConversationId(payload.conversationId ?? session?.conversationId ?? sessionReuse?.conversationId ?? params.conversationId) || null; const threadId = safeOpaqueId(session?.threadId ?? sessionReuse?.threadId ?? payload.threadId ?? params.threadId) || null; try { return await options.accessController.recordAgentSessionOwner({ ownerUserId, ownerRole: options.actor?.role ?? params.ownerRole ?? null, sessionId, projectId: params.projectId ?? payload.projectId ?? null, conversationId, threadId, traceId: preserveLastTraceId ? undefined : traceId, status, session: codeAgentSessionOwnerEvidence(payload, params) }); } catch (error) { const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; if (traceId) { traceStore.append(traceId, { type: "session-owner", status: "degraded", label: "session-owner:persist-failed", errorCode: "agent_session_owner_persist_failed", message: error?.message ?? "Code Agent session owner binding persistence failed.", valuesPrinted: false }); } return null; } } function codeAgentOwnerStatusForResult(result = {}) { const sessionStatus = textValue(result?.session?.status ?? result?.sessionSummary?.status ?? result?.sessionLifecycleStatus); if (sessionStatus === "thread-resume-failed") return "thread-resume-failed"; if (result?.status === "completed") return "active"; if (result?.status === "canceled" || result?.status === "cancelled") return "canceled"; return result?.status ?? "active"; } async function claimWorkbenchWorkspaceTurn({ params = {}, options = {}, traceId, response } = {}) { const workspaceId = safeWorkspaceId(params.workspaceId); if (!workspaceId || !options.accessController?.store || !options.actor) return null; const store = options.accessController.store; const workspace = await store.getWorkspaceForUser?.({ workspaceId, ownerUserId: options.actor.id, actorRole: options.actor.role }); if (!workspace) { sendJson(response, 404, { ok: false, status: "failed", error: { code: "workbench_workspace_not_found", message: "Workbench workspace is not visible to the current actor" }, workspaceId }); return { blocked: true }; } const expectedRevision = Number.parseInt(String(params.expectedWorkspaceRevision ?? params.workspaceRevision ?? ""), 10); const workspaceRevisionConflict = Number.isInteger(expectedRevision) && expectedRevision > 0 && workspace.revision !== expectedRevision; const requestedConversationId = safeConversationId(params.conversationId) || null; const requestedSessionId = safeSessionId(params.sessionId) || null; const nextSelectedConversationId = requestedConversationId || workspace.selectedConversationId; const nextSelectedAgentSessionId = requestedSessionId || (requestedConversationId && requestedConversationId !== workspace.selectedConversationId ? null : workspace.selectedAgentSessionId); const activeTraceId = safeTraceId(workspace.activeTraceId); const activeTraceSnapshot = activeTraceId ? (options.traceStore ?? defaultCodeAgentTraceStore).snapshot(activeTraceId) : null; const activeTraceOrphaned = Boolean(activeTraceId && isWorkbenchOrphanActiveTrace(activeTraceSnapshot)); if (activeTraceOrphaned) { await store.updateWorkspace?.({ workspaceId, ownerUserId: options.actor.id, actorRole: options.actor.role, selectedConversationId: workspace.selectedConversationId, selectedAgentSessionId: workspace.selectedAgentSessionId, activeTraceId: null, providerProfile: workspace.providerProfile, patch: { ...(workspace.workspace && typeof workspace.workspace === "object" ? workspace.workspace : {}), activeTraceId: null, staleActiveTraceId: activeTraceId, staleActiveTraceReason: "orphan-cancel-session-missing", sessionStatus: "failed", updatedAt: new Date().toISOString(), source: "code-agent-submit-active-trace-repair", valuesRedacted: true, secretMaterialStored: false }, updatedBySessionId: options.authSession?.id ?? null, updatedByClient: "code-agent-submit-active-trace-repair", now: new Date().toISOString() }); } const updated = await store.updateWorkspace?.({ workspaceId, ownerUserId: options.actor.id, actorRole: options.actor.role, selectedConversationId: nextSelectedConversationId, selectedAgentSessionId: nextSelectedAgentSessionId, activeTraceId: traceId, providerProfile: textValue(params.providerProfile) || workspace.providerProfile, patch: { selectedConversationId: nextSelectedConversationId, selectedAgentSessionId: nextSelectedAgentSessionId, previousActiveTraceId: activeTraceId || null, workspaceRevisionConflict, expectedWorkspaceRevision: Number.isInteger(expectedRevision) && expectedRevision > 0 ? expectedRevision : null, activeTraceId: traceId, providerProfile: textValue(params.providerProfile) || workspace.providerProfile, sessionStatus: "running", updatedAt: new Date().toISOString(), source: "code-agent-submit", valuesRedacted: true, secretMaterialStored: false }, updatedBySessionId: options.authSession?.id ?? null, updatedByClient: textValue(params.updatedByClient ?? params.client) || "code-agent-chat", now: new Date().toISOString() }); return { workspace: updated ?? workspace }; } async function clearStaleWorkbenchSteerTrace({ params = {}, options = {}, traceId, reason }) { const workspaceId = safeWorkspaceId(params.workspaceId); if (!workspaceId || !safeTraceId(traceId) || !options.accessController?.store || !options.actor) return null; const store = options.accessController.store; const workspace = await store.getWorkspaceForUser?.({ workspaceId, ownerUserId: options.actor.id, actorRole: options.actor.role }); if (!workspace || safeTraceId(workspace.activeTraceId) !== traceId) return null; return await store.updateWorkspace?.({ workspaceId, ownerUserId: options.actor.id, actorRole: options.actor.role, selectedConversationId: workspace.selectedConversationId, selectedAgentSessionId: workspace.selectedAgentSessionId, activeTraceId: null, providerProfile: workspace.providerProfile, patch: { ...(workspace.workspace && typeof workspace.workspace === "object" ? workspace.workspace : {}), activeTraceId: null, staleActiveTraceId: traceId, staleActiveTraceReason: reason, sessionStatus: "failed", updatedAt: new Date().toISOString(), source: "code-agent-steer-active-trace-repair", valuesRedacted: true, secretMaterialStored: false }, updatedBySessionId: options.authSession?.id ?? null, updatedByClient: "code-agent-steer-active-trace-repair", now: new Date().toISOString() }); } function isWorkbenchOrphanActiveTrace(snapshot) { if (!snapshot || typeof snapshot !== "object") return false; const last = snapshot.lastEvent && typeof snapshot.lastEvent === "object" ? snapshot.lastEvent : null; if (!last) return false; if (last.errorCode !== "cancel_session_missing" && last.label !== "cancel:not_cancelable") return false; return !snapshot.sessionId && !snapshot.runId && !snapshot.commandId && !snapshot.sessionStatus && !snapshot.turn; } async function resolveWorkbenchActiveTraceResult(traceId, options = {}) { if (!safeTraceId(traceId)) return null; const cached = options.codeAgentChatResults?.get?.(traceId) ?? null; if (cached?.agentRun?.runId) { try { const synced = await syncAgentRunChatResult({ traceId, currentResult: cached, options, traceStore: options.traceStore ?? defaultCodeAgentTraceStore }); return synced.result ?? null; } catch { return null; } } if (cached) return cached; if (!codeAgentAgentRunAdapterEnabled(options.env ?? process.env)) return null; try { const persisted = await loadPersistedAgentRunResult(traceId, options); if (!persisted?.agentRun?.runId) return persisted; const synced = await syncAgentRunChatResult({ traceId, currentResult: persisted, options, traceStore: options.traceStore ?? defaultCodeAgentTraceStore }); return synced.result ?? null; } catch { return null; } } function safeWorkspaceId(value) { const text = textValue(value); return /^wsp_[A-Za-z0-9_.:-]+$/u.test(text) ? text : ""; } function textValue(value) { return String(value ?? "").trim(); } function codeAgentSessionOwnerEvidence(payload = {}, params = {}) { const traceId = payload.traceId ?? params.traceId ?? null; const finalResponse = codeAgentFinalResponseEvidence(payload, traceId); const traceSummary = codeAgentTraceSummaryEvidence(payload, traceId, finalResponse); const messages = codeAgentConversationMessagesEvidence(payload, params, traceId, finalResponse); const firstUserMessagePreview = messages.find((message) => message.role === "user")?.text?.slice(0, 240) ?? null; const promptEvidence = codeAgentPromptMetadata(payload, params, traceId); const traceResult = codeAgentTraceResultEvidence(payload, params, traceId, finalResponse, traceSummary); return { provider: payload.provider ?? null, model: payload.model ?? null, backend: payload.backend ?? null, status: payload.status ?? null, sessionMode: payload.sessionMode ?? payload.session?.sessionMode ?? null, sessionLifecycleStatus: payload.sessionLifecycleStatus ?? null, runnerKind: payload.runner?.kind ?? payload.runnerTrace?.runnerKind ?? null, conversationId: payload.conversationId ?? params.conversationId ?? null, sessionId: payload.sessionId ?? payload.session?.sessionId ?? payload.sessionReuse?.sessionId ?? params.sessionId ?? null, threadId: payload.session?.threadId ?? payload.sessionReuse?.threadId ?? payload.threadId ?? params.threadId ?? null, traceId, projectId: payload.projectId ?? params.projectId ?? null, ...codeAgentPromptFields(promptEvidence), finalResponse, traceSummary, ...(traceResult ? { traceResults: { [traceResult.traceId]: traceResult } } : {}), ...(messages.length ? { messages, messageCount: messages.length } : {}), ...(firstUserMessagePreview ? { firstUserMessagePreview } : {}), ...agentRunSessionEvidence(payload), secretMaterialStored: false, valuesRedacted: true }; } function codeAgentTraceResultEvidence(payload = {}, params = {}, traceId = null, finalResponse = null, traceSummary = null) { const resolvedTraceId = safeTraceId(traceId); if (!resolvedTraceId) return null; const agentRun = agentRunSessionEvidence(payload).agentRun ?? null; const userBillingReservation = codeAgentBillingReservationEvidence(payload.userBillingReservation ?? params.userBillingReservation); const resultSession = 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 ?? agentRun?.conversationId ?? resultSession?.conversationId ?? sessionReuse?.conversationId ?? params.conversationId) || null; const sessionId = safeSessionId(payload.sessionId ?? resultSession?.sessionId ?? sessionReuse?.sessionId ?? params.sessionId) || null; const threadId = safeOpaqueId(payload.threadId ?? resultSession?.threadId ?? sessionReuse?.threadId ?? params.threadId ?? agentRun?.threadId) || null; const promptEvidence = codeAgentPromptMetadata(payload, params, resolvedTraceId); if (!agentRun && !finalResponse && !traceSummary) return null; return { traceId: resolvedTraceId, status: payload.status ?? agentRun?.terminalStatus ?? agentRun?.commandState ?? null, conversationId, sessionId, threadId, ...codeAgentPromptFields(promptEvidence), messageId: finalResponse?.messageId ?? payload.messageId ?? null, createdAt: payload.createdAt ?? finalResponse?.createdAt ?? null, updatedAt: payload.updatedAt ?? finalResponse?.updatedAt ?? null, finalResponse, traceSummary, agentRun, ...(userBillingReservation ? { userBillingReservation } : {}), valuesRedacted: true, secretMaterialStored: false }; } function codeAgentPromptMetadata(payload = {}, params = {}, traceId = null) { const prompt = payload?.prompt && typeof payload.prompt === "object" ? payload.prompt : null; const rawPrompt = firstNonEmptyValue( params.message, params.prompt, params.text, payload.userMessage, typeof payload.prompt === "string" ? payload.prompt : null ); const fromText = codeAgentPromptMetadataFromText(rawPrompt, traceId, "code-agent-request", { fullText: Boolean(rawPrompt) }); return codeAgentPromptMetadataFromParts({ traceId, source: prompt?.source ?? payload.promptSource ?? fromText?.prompt?.source ?? "code-agent-request", promptId: firstNonEmptyValue(payload.promptId, prompt?.id, fromText?.promptId), promptSha256: firstNonEmptyValue(payload.promptSha256, prompt?.sha256, fromText?.promptSha256), promptSummary: firstNonEmptyValue(payload.promptSummary, prompt?.summary, fromText?.promptSummary), promptPreview: firstNonEmptyValue(payload.promptPreview, prompt?.preview, fromText?.promptPreview), textChars: Number.isInteger(prompt?.textChars) ? prompt.textChars : fromText?.prompt?.textChars }); } function codeAgentPromptMetadataFromText(value, traceId = null, source = "code-agent-request", options = {}) { const promptText = String(value ?? "").trim(); if (!promptText) return null; return codeAgentPromptMetadataFromParts({ traceId, source, promptSha256: options.fullText === true ? sha256Text(promptText) : undefined, promptSummary: promptText, promptPreview: promptText, textChars: options.fullText === true ? promptText.length : undefined }); } function codeAgentPromptMetadataFromParts({ traceId = null, source = "code-agent-request", promptId = null, promptSha256 = null, promptSummary = null, promptPreview = null, textChars = undefined } = {}) { const digest = firstNonEmptyValue(promptSha256); const summary = clippedPromptText(firstNonEmptyValue(promptSummary, promptPreview), 240); const preview = clippedPromptText(firstNonEmptyValue(promptPreview, promptSummary), 500); const explicitId = firstNonEmptyValue(promptId); if (!explicitId && !digest && !summary && !preview) return null; const id = explicitId || codeAgentPromptId(traceId, digest); const prompt = compactCodeAgentObject({ id, sha256: digest || undefined, summary, preview, textChars: Number.isInteger(textChars) ? textChars : undefined, source, valuesPrinted: false }); return compactCodeAgentObject({ promptId: id, promptSha256: digest || undefined, promptSummary: summary, promptPreview: preview, prompt, valuesPrinted: false }) ?? null; } function codeAgentPromptFields(metadata = null) { if (!metadata) return {}; return compactCodeAgentObject({ promptId: metadata.promptId, promptSha256: metadata.promptSha256, promptSummary: metadata.promptSummary, promptPreview: metadata.promptPreview, prompt: metadata.prompt }) ?? {}; } function codeAgentPromptId(traceId = null, digest = null) { const traceSuffix = safeTraceId(traceId)?.slice(4).replace(/[^A-Za-z0-9_.:-]/gu, "_"); const digestSuffix = firstNonEmptyValue(digest)?.slice(0, 16); const suffix = firstNonEmptyValue(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 compactCodeAgentObject(value) { 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 codeAgentBillingReservationEvidence(value = null) { if (!value || typeof value !== "object") return null; const reservationId = textValue(value.reservationId); if (!reservationId) return null; return { reservationId, resourceType: textValue(value.resourceType) || null, planId: textValue(value.planId) || null, estimatedCredits: numberOrNull(value.estimatedCredits), estimatedTokens: numberOrNull(value.estimatedTokens), expiresAt: textValue(value.expiresAt) || null, valuesRedacted: true, secretMaterialStored: false }; } function codeAgentConversationMessagesEvidence(payload = {}, params = {}, traceId = null, finalResponse = null) { const userText = boundedConversationMessageText(params.message ?? params.prompt ?? payload.userMessage ?? payload.prompt); if (!userText) return []; const resolvedTraceId = safeTraceId(traceId) || null; 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; const sessionId = safeSessionId(payload.sessionId ?? session?.sessionId ?? sessionReuse?.sessionId ?? params.sessionId) || null; 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`, role: "user", title: "用户", text: userText, status: "sent", traceId: resolvedTraceId, conversationId, sessionId, threadId, createdAt, updatedAt, source: "code-agent-submit", secretMaterialStored: false, valuesRedacted: true }; const agentText = boundedConversationMessageText( finalResponse?.text ?? payload.reply?.content ?? payload.message?.content ?? payload.assistantText ?? payload.error?.userMessage ?? payload.error?.message ?? payload.blocker?.userMessage ?? payload.blocker?.message ?? payload.blocker?.summary ); const agentStatus = codeAgentConversationAgentMessageStatus(payload); const agentMessage = { id: `msg_${traceSuffix}_agent`, role: "agent", title: agentStatus === "failed" ? "Code Agent 返回阻塞" : agentStatus === "running" ? "Code Agent 处理中" : "Code Agent 回复", text: agentText || (agentStatus === "running" ? "" : "Code Agent 请求已结束,请查看 Trace 详情。"), status: agentStatus, traceId: resolvedTraceId, conversationId, sessionId, threadId, createdAt: payload.reply?.createdAt ?? updatedAt, updatedAt, source: "code-agent-result", ...(payload.runnerTrace && typeof payload.runnerTrace === "object" ? { runnerTrace: codeAgentConversationRunnerTraceEvidence(payload.runnerTrace, resolvedTraceId) } : {}), secretMaterialStored: false, valuesRedacted: true }; return [userMessage, agentMessage]; } function boundedConversationMessageText(value, limit = 12000) { const text = conversationText(value); if (!text) return ""; return text.length > limit ? text.slice(0, limit) : text; } function conversationText(value) { const extracted = conversationTextRaw(value).replace(/\s+/gu, " ").trim(); if (!extracted || extracted === "[object Object]") return ""; return extracted; } function conversationTextRaw(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(conversationTextRaw).filter(Boolean).join("\n"); if (typeof value === "object") { for (const key of ["text", "content", "message", "prompt", "value", "summary", "preview", "title"]) { const text = conversationTextRaw(value[key]); if (text) return text; } } return ""; } function codeAgentMessageTraceSuffix(traceId) { const text = safeTraceId(traceId) || `local_${Date.now().toString(36)}`; return text.replace(/^trc_/u, "").replace(/[^A-Za-z0-9_.:-]/gu, "_").slice(0, 48) || "trace"; } 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"; if (["failed", "blocked", "error", "timeout", "canceled", "cancelled"].includes(status)) return "failed"; if (payload.error || payload.blocker) return "failed"; return "running"; } function codeAgentConversationRunnerTraceEvidence(runnerTrace = {}, traceId = null) { const events = Array.isArray(runnerTrace.events) ? runnerTrace.events : []; const lastEvent = runnerTrace.lastEvent && typeof runnerTrace.lastEvent === "object" ? runnerTrace.lastEvent : events.at(-1) ?? null; const eventCount = Number.isFinite(Number(runnerTrace.eventCount)) ? Number(runnerTrace.eventCount) : events.length; return { traceId: safeTraceId(runnerTrace.traceId ?? traceId) || traceId || null, status: runnerTrace.status ?? lastEvent?.status ?? null, sessionId: runnerTrace.sessionId ?? null, threadId: runnerTrace.threadId ?? null, runId: runnerTrace.runId ?? lastEvent?.runId ?? null, commandId: runnerTrace.commandId ?? lastEvent?.commandId ?? null, eventCount, lastEvent: lastEvent ? { label: lastEvent.label ?? null, status: lastEvent.status ?? null, terminal: lastEvent.terminal === true, createdAt: lastEvent.createdAt ?? null, message: boundedConversationMessageText(lastEvent.message ?? lastEvent.text, 1000), valuesPrinted: false } : null, eventsCompacted: true, fullTraceLoaded: false, valuesPrinted: false }; } function codeAgentFinalResponseEvidence(payload = {}, traceId = null) { const text = textValue(payload.reply?.content ?? payload.message?.content ?? payload.assistantText); if (!text) return null; return { text, textChars: text.length, messageId: payload.reply?.messageId ?? payload.messageId ?? null, role: payload.reply?.role ?? payload.message?.role ?? "assistant", status: payload.status ?? null, traceId, createdAt: payload.reply?.createdAt ?? payload.createdAt ?? null, updatedAt: payload.updatedAt ?? null, source: "code-agent-result", valuesPrinted: false }; } function codeAgentTraceSummaryEvidence(payload = {}, traceId = null, finalResponse = null) { const runnerTrace = payload.runnerTrace && typeof payload.runnerTrace === "object" ? payload.runnerTrace : null; const events = Array.isArray(runnerTrace?.events) ? runnerTrace.events : []; const terminalEvent = [...events].reverse().find((event) => event?.terminal === true) ?? runnerTrace?.lastEvent ?? null; return { traceId, source: "code-agent-derived-session-evidence", sourceEventCount: Number.isFinite(Number(runnerTrace?.eventCount)) ? Number(runnerTrace.eventCount) : events.length, terminalStatus: payload.agentRun?.terminalStatus ?? payload.status ?? terminalEvent?.status ?? null, lastEventLabel: terminalEvent?.label ?? null, finalAssistantRow: finalResponse ? { role: finalResponse.role, status: finalResponse.status, textChars: finalResponse.textChars, textPreview: finalResponse.text.slice(0, 240), messageId: finalResponse.messageId, valuesPrinted: false } : null, agentRun: payload.agentRun ? { runId: payload.agentRun.runId ?? null, commandId: payload.agentRun.commandId ?? null, attemptId: payload.agentRun.attemptId ?? null, runnerId: payload.agentRun.runnerId ?? null, jobName: payload.agentRun.jobName ?? null, namespace: payload.agentRun.namespace ?? null, lastSeq: payload.agentRun.lastSeq ?? null, valuesPrinted: false } : null, updatedAt: payload.updatedAt ?? null, valuesPrinted: false }; } function annotateOwner(payload, params = {}) { if (!params.ownerUserId) return payload; return { ...payload, ownerUserId: params.ownerUserId, ownerRole: params.ownerRole ?? null }; } function canAccessOwnedResult(result, actor) { if (!result?.ownerUserId || !actor) return true; return actor.role === "admin" || actor.id === result.ownerUserId; } function isCodeAgentResultCanceled(result) { return result?.status === "canceled" || result?.canceled === true || result?.error?.code === "codex_stdio_canceled"; } function codeAgentCancelScopeMismatch(params = {}, result = {}) { return cancelScopeFieldMismatch("conversationId", safeConversationId(params.conversationId), cancelExpectedValues(safeConversationId, result.conversationId, result.agentRun?.conversationId, result.session?.conversationId, result.sessionReuse?.conversationId, result.runnerTrace?.conversationId )) ?? cancelScopeFieldMismatch("sessionId", safeSessionId(params.sessionId), cancelExpectedValues(safeSessionId, result.sessionId, result.session?.sessionId, result.sessionReuse?.sessionId, result.agentRun?.hwlabSessionId )) ?? cancelScopeFieldMismatch("threadId", safeOpaqueId(params.threadId), cancelExpectedValues(safeOpaqueId, result.threadId, result.agentRun?.threadId, result.session?.threadId, result.sessionReuse?.threadId, result.runnerTrace?.threadId )); } function cancelExpectedValues(normalize, ...values) { return [...new Set(values.map((value) => normalize(value)).filter(Boolean))]; } function cancelScopeFieldMismatch(field, requested, expectedValues) { if (!requested || expectedValues.length === 0 || expectedValues.includes(requested)) return null; return { field, requested, expected: expectedValues[0] }; } function cancelBlockedPayload({ code, message, traceId, conversationId = null, sessionId = null, session = null, runnerTrace = null, status = "failed", unsupported = false, degraded = false }) { const sessionSummary = codeAgentSessionLifecycleSummary({ session, runnerTrace, status, unsupported, degraded, error: { code, userMessage: message, message } }); return { accepted: false, canceled: false, status, conversationId, sessionId, traceId, session, sessionLifecycleStatus: sessionSummary.status, sessionLifecycle: sessionSummary, sessionSummary, unsupported, degraded, runnerTrace, lastTraceEvent: runnerTrace?.lastEvent ?? null, error: { code, layer: "session", category: "cancel_blocked", retryable: true, userMessage: message, message, traceId, route: "/v1/agent/chat/cancel", toolName: "codex-stdio.cancel" }, blocker: { code, layer: "session", category: "cancel_blocked", retryable: true, summary: message, userMessage: message, traceId, route: "/v1/agent/chat/cancel", toolName: "codex-stdio.cancel" } }; } export async function handleCodeAgentTraceHttp(request, response, url, options) { const parts = url.pathname.split("/").filter(Boolean); const traceId = decodeURIComponent(parts[4] ?? ""); const traceStore = options.traceStore ?? defaultCodeAgentTraceStore; if (!/^trc_[A-Za-z0-9_.:-]+$/u.test(traceId)) { sendJson(response, 400, { error: { code: "invalid_trace_id", message: "traceId must start with trc_ and contain only safe identifier characters" } }); return; } const result = options.codeAgentChatResults?.get(traceId) ?? null; if (result && !canAccessOwnedResult(result, options.actor)) { sendJson(response, 403, { error: { code: "agent_session_owner_required", message: "Only the session owner or admin can read this Code Agent trace" } }); return; } let agentRunResult = result?.agentRun ? result : await loadPersistedAgentRunResult(traceId, options); let refreshError = null; if (agentRunResult && !canAccessOwnedResult(agentRunResult, options.actor)) { sendJson(response, 403, { error: { code: "agent_session_owner_required", message: "Only the session owner or admin can read this Code Agent trace" } }); return; } if (agentRunResult?.agentRun) { try { const snapshot = await refreshAgentRunTrace({ traceId, result: agentRunResult, options, traceStore }); agentRunResult = options.codeAgentChatResults?.get?.(traceId) ?? agentRunResult; if (traceNeedsCommandResultSync(agentRunResult, snapshot)) { const synced = await syncAgentRunChatResult({ traceId, currentResult: agentRunResult, options, traceStore, appendResultEvent: false, refreshEvents: false }); agentRunResult = synced.result ?? agentRunResult; } if (isTraceCommandTerminalStatus(agentRunResult?.status)) { await finalizeCodeAgentBillingUsage({ payload: agentRunResult, params: agentRunResult, options }); recordCodeAgentConversationFact(agentRunResult, options); await recordCodeAgentSessionOwner({ payload: agentRunResult, params: agentRunResult, options, status: codeAgentOwnerStatusForResult(agentRunResult), preserveLastTraceId: true }); } } catch (error) { sendJson(response, Number(error?.statusCode ?? 502), { error: { code: error?.code ?? "agentrun_trace_command_result_unavailable", message: error?.message ?? "AgentRun command result could not be resolved for this traceId", traceId, valuesPrinted: false } }); return; } } if (parts[5] === "stream") { sendTraceSse(response, traceStore, traceId, options); return; } const snapshot = traceStore.snapshot(traceId); sendJson(response, 200, traceSnapshotWithTerminalEvidence(snapshot, agentRunResult, traceId, refreshError)); } function traceNeedsCommandResultSync(result, snapshot) { if (!result?.agentRun) return false; const status = String(result.status ?? result.agentRun.status ?? result.agentRun.commandState ?? result.agentRun.terminalStatus ?? "running"); if (isTraceCommandTerminalStatus(status)) return true; if (snapshotHasTerminalEvent(snapshot)) return true; return false; } function isTraceCommandTerminalStatus(status) { return CODE_AGENT_TERMINAL_STATUSES.has(String(status ?? "").trim().toLowerCase().replace(/_/gu, "-")); } function snapshotHasTerminalEvent(snapshot) { const events = Array.isArray(snapshot?.events) ? snapshot.events : []; return events.some((event) => event?.terminal === true || (event?.type === "result" && isTraceCommandTerminalStatus(event?.status))); } function traceSnapshotWithTerminalEvidence(snapshot, result, traceId, refreshError = null) { const evidence = agentRunTerminalTraceEvidence(result, traceId); if (snapshot?.status !== "missing") return traceSnapshotWithAttachedTerminalEvidence(snapshot, evidence, refreshError); if (!evidence) return { ...snapshot, ok: false, traceStatus: "missing", retention: traceRetentionSummary("missing") }; return { ...snapshot, ok: true, status: "expired", traceStatus: "expired", persisted: true, retention: traceRetentionSummary("expired"), conversationId: evidence.conversationId, sessionId: evidence.sessionId, threadId: evidence.threadId, ...codeAgentPromptFields(evidence), agentRun: evidence.agentRun, finalResponse: evidence.finalResponse, traceSummary: evidence.traceSummary, terminalEvidence: { available: true, source: evidence.source, refresh: refreshError ? { attempted: true, ok: false, code: refreshError?.code ?? "agentrun_trace_refresh_failed", message: refreshError?.message ?? "AgentRun trace events could not be refreshed; command result remains authoritative for final response.", valuesPrinted: false } : { attempted: false, ok: null }, conversationId: evidence.conversationId, sessionId: evidence.sessionId, threadId: evidence.threadId, ...codeAgentPromptFields(evidence), agentRun: evidence.agentRun, traceSummary: evidence.traceSummary, finalResponse: evidence.finalResponse, valuesPrinted: false } }; } function traceSnapshotWithAttachedTerminalEvidence(snapshot, evidence, refreshError = null) { if (!evidence) return snapshot; return { ...snapshot, conversationId: snapshot.conversationId ?? evidence.conversationId, sessionId: snapshot.sessionId ?? evidence.sessionId, threadId: snapshot.threadId ?? evidence.threadId, ...codeAgentPromptFields(evidence), agentRun: snapshot.agentRun ?? evidence.agentRun, finalResponse: snapshot.finalResponse ?? evidence.finalResponse, traceSummary: snapshot.traceSummary ?? evidence.traceSummary, terminalEvidence: snapshot.terminalEvidence ?? (refreshError ? { available: true, source: evidence.source, refresh: { attempted: true, ok: false, code: refreshError?.code ?? "agentrun_trace_refresh_failed", message: refreshError?.message ?? "AgentRun trace events could not be refreshed; live events may be partial.", valuesPrinted: false }, valuesPrinted: false } : undefined) }; } function agentRunTerminalTraceEvidence(result, traceId) { if (!result || typeof result !== "object") return null; const storedSummary = result.traceSummary && typeof result.traceSummary === "object" && result.traceSummary.source === "agentrun-command-result" ? result.traceSummary : null; const finalResponse = agentRunTerminalFinalResponse(result, traceId); const promptEvidence = codeAgentPromptMetadata(result, {}, traceId); const agentRun = result.agentRun && typeof result.agentRun === "object" ? { adapter: result.agentRun.adapter ?? null, runId: result.agentRun.runId ?? null, commandId: result.agentRun.commandId ?? null, attemptId: result.agentRun.attemptId ?? null, runnerId: result.agentRun.runnerId ?? null, jobName: result.agentRun.jobName ?? null, namespace: result.agentRun.namespace ?? null, terminalStatus: result.agentRun.terminalStatus ?? null, lastSeq: result.agentRun.lastSeq ?? null, valuesPrinted: false } : null; if (!agentRun?.runId || !agentRun?.commandId || (!storedSummary && !finalResponse)) return null; const traceSummary = { traceId, source: "agentrun-command-result", sourceEventCount: numberOrNull(storedSummary?.sourceEventCount ?? storedSummary?.eventCount ?? result.runnerTrace?.eventCount), renderedRowSummary: storedSummary?.renderedRowSummary ?? null, noiseEventCount: numberOrNull(storedSummary?.noiseEventCount), omittedNoiseCount: numberOrNull(storedSummary?.omittedNoiseCount), terminalStatus: storedSummary?.terminalStatus ?? result.agentRun?.terminalStatus ?? result.status ?? null, updatedAt: storedSummary?.updatedAt ?? result.updatedAt ?? null, valuesPrinted: false }; return { source: "agentrun-command-result", conversationId: safeConversationId(result.conversationId) || null, sessionId: safeSessionId(result.sessionId) || null, threadId: safeOpaqueId(result.threadId) || null, ...codeAgentPromptFields(promptEvidence), agentRun, finalResponse, traceSummary }; } function agentRunTerminalFinalResponse(result, traceId) { const stored = result.finalResponse && typeof result.finalResponse === "object" ? result.finalResponse : null; const text = textValue(stored?.text ?? result.reply?.content ?? result.message?.content ?? result.assistantText); if (!text) return null; const storedTraceId = safeTraceId(stored?.traceId ?? result.traceId) || null; if (storedTraceId && storedTraceId !== traceId) return null; return { text, textChars: text.length, messageId: stored?.messageId ?? result.reply?.messageId ?? result.messageId ?? null, role: stored?.role ?? result.reply?.role ?? "assistant", status: stored?.status ?? result.status ?? null, traceId: traceId, createdAt: stored?.createdAt ?? result.reply?.createdAt ?? result.createdAt ?? null, updatedAt: stored?.updatedAt ?? result.updatedAt ?? null, source: "agentrun-command-result", valuesPrinted: false }; } function traceRetentionSummary(status) { return { traceStatus: status, liveTraceStore: status === "missing" ? "missing" : "expired-or-evicted", policy: "live trace events are short-term storage; terminal final response and summary are resolved from AgentRun command result by traceId and commandId", replacementEvidence: status === "missing" ? "resolve the trace through AgentRun command registry/result or report the trace as missing" : "use terminalEvidence.finalResponse, terminalEvidence.traceSummary, conversationId, sessionId, threadId, and agentRun ids", valuesPrinted: false }; } function numberOrNull(value) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; } function sendTraceSse(response, traceStore, traceId, options = {}) { response.writeHead(200, { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-store", connection: "keep-alive", "x-accel-buffering": "no" }); const writeEvent = (eventName, payload) => { response.write(`event: ${eventName}\n`); response.write(`data: ${JSON.stringify(payload)}\n\n`); }; writeEvent("snapshot", traceStore.snapshot(traceId)); const unsubscribe = traceStore.subscribe(traceId, (event, snapshot) => { writeEvent("runnerTrace", { event, snapshot }); }); const heartbeatMs = parsePositiveInteger(options.env?.HWLAB_CODE_AGENT_TRACE_HEARTBEAT_MS, 15000); const heartbeat = setInterval(() => { writeEvent("heartbeat", traceStore.snapshot(traceId)); }, heartbeatMs); response.on("close", () => { clearInterval(heartbeat); unsubscribe(); }); } export function createCodeAgentChatResultStore({ maxResults = 256 } = {}) { const limit = positiveInteger(maxResults, 256); const results = new Map(); function set(traceId, payload) { const id = safeTraceId(traceId); if (!id) return; results.set(id, { ...payload, traceId: id, cachedAt: new Date().toISOString() }); while (results.size > limit) { const oldest = results.keys().next().value; if (!oldest) break; results.delete(oldest); } } return { set, get: (traceId) => results.get(safeTraceId(traceId)) ?? null, clear: () => results.clear() }; } function compactCodeAgentChatResultPayload(payload, options = {}) { if (!payload || typeof payload !== "object") return payload; const limit = resultTraceEventLimit(options); const terminalEvidence = agentRunTerminalTraceEvidence(payload, payload.traceId); const { userBillingReservation, ...publicPayload } = payload; return { ...publicPayload, ...(terminalEvidence ? { terminalEvidence: terminalEvidencePayload(terminalEvidence) } : {}), ...(publicPayload.runnerTrace && typeof publicPayload.runnerTrace === "object" ? { runnerTrace: compactRunnerTraceForResult(publicPayload.runnerTrace, limit) } : {}) }; } function terminalEvidencePayload(evidence) { return { available: true, source: evidence.source, conversationId: evidence.conversationId, sessionId: evidence.sessionId, threadId: evidence.threadId, ...codeAgentPromptFields(evidence), agentRun: evidence.agentRun, traceSummary: evidence.traceSummary, finalResponse: evidence.finalResponse, valuesPrinted: false }; } function compactRunnerTraceForResult(runnerTrace, limit = DEFAULT_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT) { if (!runnerTrace || typeof runnerTrace !== "object") return runnerTrace; const events = Array.isArray(runnerTrace.events) ? runnerTrace.events : []; const total = Number.isInteger(runnerTrace.eventCount) ? runnerTrace.eventCount : events.length; const effectiveLimit = Math.max(8, positiveInteger(limit, DEFAULT_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT)); if (events.length <= effectiveLimit) { return { ...runnerTrace, eventCount: total, eventsCompacted: false }; } const headCount = Math.min(24, Math.max(1, Math.floor(effectiveLimit / 4))); const tailCount = Math.max(1, effectiveLimit - headCount - 1); const omitted = Math.max(0, events.length - headCount - tailCount); const head = events.slice(0, headCount); const tail = events.slice(events.length - tailCount); const marker = { traceId: runnerTrace.traceId ?? head[0]?.traceId ?? tail[0]?.traceId ?? null, type: "trace", stage: "trace", status: "truncated", label: "trace:compacted", createdAt: tail[0]?.createdAt ?? runnerTrace.updatedAt ?? new Date().toISOString(), message: `Result polling response compacted ${omitted} trace events; fetch /v1/agent/chat/trace/${encodeURIComponent(runnerTrace.traceId ?? "")} for the full trace.`, omittedEventCount: omitted, retainedEventCount: head.length + tail.length, totalEventCount: total, valuesPrinted: false }; const compactEvents = [...head, marker, ...tail]; return { ...runnerTrace, eventCount: total, events: compactEvents, eventLabels: compactEvents.map(traceEventLabel).filter(Boolean), eventsCompacted: true, eventWindow: { mode: "head-tail", retained: compactEvents.length, omitted, total } }; } function traceEventLabel(event) { return typeof event === "string" ? event : event?.label; } function resultTraceEventLimit(options = {}) { return positiveInteger( options.env?.HWLAB_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT, DEFAULT_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT ); } function createCodeAgentM3HwlabApiRequestJson(options = {}) { return async (targetUrl, request = {}) => { const url = new URL(targetUrl); const route = url.pathname; if (route === M3_IO_CONTROL_ROUTE && request.method === "POST") { return { ok: true, status: 200, body: await handleM3IoControl(request.body ?? {}, { runtimeStore: options.runtimeStore, now: options.now, env: options.env, requestJson: options.m3IoRequestJson }) }; } if (route === M3_STATUS_ROUTE && request.method === "GET") { return { ok: true, status: 200, body: await describeM3StatusLive(options) }; } return { ok: false, status: 404, body: { error: { code: "not_found", message: `Unsupported Code Agent M3 HWLAB API route ${route}` } }, error: `Unsupported Code Agent M3 HWLAB API route ${route}` }; }; } function uniqueStrings(values) { return [...new Set(values.map((value) => String(value ?? "").trim()).filter(Boolean))]; }