import { randomUUID } from "node:crypto"; import { defaultCodeAgentTraceStore } from "./code-agent-trace-store.ts"; import { CODEX_APP_SERVER_PROTOCOL, CODEX_APP_SERVER_WIRE_API, CODEX_STDIO_BACKEND, CODEX_STDIO_CAPABILITY_LEVEL, CODEX_STDIO_IMPLEMENTATION_TYPE, CODEX_STDIO_PROVIDER, CODEX_STDIO_RUNNER_KIND, CODEX_STDIO_SESSION_MODE, longLivedSessionGate } from "./codex-stdio-session.ts"; import { parsePositiveInteger, safeConversationId, safeOpaqueId, safeSessionId, safeTraceId, truthyFlag } from "./server-http-utils.ts"; const ADAPTER_ID = "agentrun-v01"; const DEFAULT_AGENTRUN_MGR_URL = "http://agentrun-mgr.agentrun-v01.svc.cluster.local:8080"; const DEFAULT_TENANT_ID = "hwlab"; const DEFAULT_PROJECT_ID = "pikasTech/HWLAB"; const DEFAULT_PROVIDER_ID = "G14"; const DEFAULT_REPO_URL = "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git"; const DEFAULT_RUNNER_NAMESPACE = "agentrun-v01"; const DEFAULT_TIMEOUT_MS = 1_200_000; const AGENTRUN_BACKENDS = Object.freeze(["codex", "deepseek", "minimax-m3"]); const THREAD_CONTINUITY_POLICY = "hwlab-agentrun-v01-reuse-runner-thread"; const SESSION_POLICY_RUN_LOCAL = "hwlab-agentrun-v01-session-runner-reuse"; const TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "blocked", "cancelled", "canceled"]); const HWLAB_RESOURCE_TOOL_ALIASES = Object.freeze([ Object.freeze({ name: "hwpod", path: "tools/device-pod-cli.mjs", kind: "node-script" }), Object.freeze({ name: "unidesk-ssh", path: "tools/unidesk-ssh.mjs", kind: "bun-script" }) ]); const HWLAB_RESOURCE_PROMPT_REFS = Object.freeze([ Object.freeze({ name: "hwlab-v02-runtime", path: "internal/agent/prompts/hwlab-v02-runtime.md", inject: "thread-start", required: true }) ]); const HWLAB_RESOURCE_SKILL_REFS = Object.freeze([ Object.freeze({ name: "device-pod-cli", path: "skills/device-pod-cli/SKILL.md", required: true, aggregateAs: "device-pod-cli" }), Object.freeze({ name: "hwlab-agent-runtime", path: "skills/hwlab-agent-runtime/SKILL.md", required: true, aggregateAs: "hwlab-agent-runtime" }) ]); export function codeAgentAgentRunAdapterEnabled(env = process.env) { const value = String(env.HWLAB_CODE_AGENT_ADAPTER ?? env.HWLAB_CODE_AGENT_PROVIDER ?? "").trim().toLowerCase(); return value === ADAPTER_ID || value === "agentrun" || value === "agentrun-v0.1"; } export function initialAgentRunChatResult({ params = {}, options = {}, traceId }) { const env = options.env ?? process.env; const timestamp = nowIso(options.now); const backendProfile = resolveAgentRunBackendProfile(env, params); return { accepted: true, status: "running", shortConnection: true, traceId, conversationId: safeConversationId(params.conversationId) || null, sessionId: safeSessionId(params.sessionId) || agentRunSessionId(traceId), threadId: safeOpaqueId(params.threadId) || null, messageId: `msg_${safeTraceId(traceId)?.slice(4) || randomUUID()}`, createdAt: timestamp, updatedAt: timestamp, provider: CODEX_STDIO_PROVIDER, model: modelForBackendProfile(backendProfile, env), backend: CODEX_STDIO_BACKEND, infrastructureBackend: `agentrun-v01/${backendProfile}`, capabilityLevel: CODEX_STDIO_CAPABILITY_LEVEL, sessionMode: CODEX_STDIO_SESSION_MODE, implementationType: CODEX_STDIO_IMPLEMENTATION_TYPE, threadContinuityPolicy: THREAD_CONTINUITY_POLICY, agentRun: { adapter: ADAPTER_ID, status: "pending-dispatch", backendProfile, providerId: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID, DEFAULT_PROVIDER_ID), managerUrl: resolveAgentRunManagerUrl(env), repoUrl: resolveAgentRunRepoUrl(env), valuesPrinted: false }, valuesPrinted: false }; } export async function submitAgentRunChatTurn({ params = {}, options = {}, traceId, traceStore = defaultCodeAgentTraceStore, results }) { const env = options.env ?? process.env; const managerUrl = resolveAgentRunManagerUrl(env); const backendProfile = resolveAgentRunBackendProfile(env, params); const fetchImpl = options.fetchImpl ?? globalThis.fetch; const timeoutMs = parsePositiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, 20_000); const startedAt = nowIso(options.now); traceStore.ensure(traceId, agentRunTraceMeta(env, params)); traceStore.append(traceId, { type: "request", status: "accepted", label: "agentrun:request:accepted", message: "HWLAB Code Agent request accepted by the AgentRun v0.1 adapter; hwlab-cloud-api will reuse an active runner when the HWLAB session has an active AgentRun reuse window, otherwise it will create run/command/runner-job over the k3s Service DNS.", waitingFor: "agentrun-run-reuse-or-create", adapter: ADAPTER_ID, managerHost: new URL(managerUrl).hostname, valuesPrinted: false }, agentRunTraceMeta(env, params)); const reusable = await resolveReusableAgentRun({ params, options, env, managerUrl, fetchImpl, timeoutMs, backendProfile, traceId, traceStore }); if (reusable?.mapping) { try { const commandInput = buildAgentRunCommandInput({ params, traceId, backendProfile, sessionId: reusable.sessionId }); const command = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(reusable.mapping.runId)}/commands`, { method: "POST", body: commandInput, timeoutMs }); const commandId = requiredString(command?.id, "command.id"); const mapping = agentRunReusedMapping({ previous: reusable.mapping, run: reusable.run, command, traceId, startedAt, backendProfile, managerUrl, env }); traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:command:created", message: `AgentRun command ${commandId} created on reused run ${mapping.runId}; hwlab-cloud-api will not start a new runner Job for this turn.`, runId: mapping.runId, commandId, backendProfile, waitingFor: "agentrun-result", valuesPrinted: false }); traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:runner-job:reused", message: `AgentRun runner Job ${mapping.jobName ?? "unknown"} is reused for this HWLAB session turn; no new bundle materialization is requested.`, runId: mapping.runId, commandId: mapping.commandId, attemptId: mapping.attemptId, runnerId: mapping.runnerId, jobName: mapping.jobName, namespace: mapping.namespace, waitingFor: "agentrun-result", valuesPrinted: false }); return decorateAgentRunRunningResult({ base: initialAgentRunChatResult({ params, options, traceId }), mapping, traceStore, traceId }); } catch (error) { traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:run:reuse-command-failed", message: `AgentRun reused run ${reusable.mapping.runId} rejected the new command; hwlab-cloud-api will create a fresh run/runner Job for this turn.`, errorCode: error?.code ?? "agentrun_reuse_command_failed", runId: reusable.mapping.runId, commandId: reusable.mapping.commandId ?? null, waitingFor: "agentrun-run-create", valuesPrinted: false }); } } const sessionId = scopedAgentRunSessionIdForParams(params, traceId, backendProfile); const runInput = buildAgentRunCreateRunInput({ params, env, traceId, backendProfile, sessionId }); const run = await agentRunJson(fetchImpl, managerUrl, "/api/v1/runs", { method: "POST", body: runInput, timeoutMs }); const runId = requiredString(run?.id, "run.id"); traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:run:created", message: `AgentRun run ${runId} created through internal k3s Service DNS.`, runId, backendProfile, waitingFor: "agentrun-command-create", valuesPrinted: false }); const commandInput = buildAgentRunCommandInput({ params, traceId, backendProfile, sessionId }); const command = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(runId)}/commands`, { method: "POST", body: commandInput, timeoutMs }); const commandId = requiredString(command?.id, "command.id"); traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:command:created", message: `AgentRun command ${commandId} created; hwlab-cloud-api will start a runner Job explicitly without relying on scheduler automation.`, runId, commandId, backendProfile, waitingFor: "agentrun-runner-job-create", valuesPrinted: false }); const runnerJobInput = buildAgentRunRunnerJobInput({ env, traceId, commandId }); const runnerJob = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs`, { method: "POST", body: runnerJobInput, timeoutMs }); const mapping = agentRunMapping({ env, managerUrl, backendProfile, run, command, runnerJob, traceId, startedAt }); traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:runner-job:created", message: `AgentRun runner Job ${mapping.jobName ?? "unknown"} created in namespace ${mapping.namespace ?? DEFAULT_RUNNER_NAMESPACE}.`, runId: mapping.runId, commandId: mapping.commandId, attemptId: mapping.attemptId, runnerId: mapping.runnerId, jobName: mapping.jobName, namespace: mapping.namespace, waitingFor: "agentrun-result", valuesPrinted: false }); return decorateAgentRunRunningResult({ base: initialAgentRunChatResult({ params, options, traceId }), mapping, traceStore, traceId }); } export async function syncAgentRunChatResult({ traceId, currentResult = null, options = {}, traceStore = defaultCodeAgentTraceStore }) { const mapped = currentResult ?? await loadPersistedAgentRunResult(traceId, options); if (!mapped?.agentRun?.runId || !mapped?.agentRun?.commandId) return { result: currentResult, runnerTrace: traceStore.snapshot(traceId), found: Boolean(currentResult) }; const env = options.env ?? process.env; const fetchImpl = options.fetchImpl ?? globalThis.fetch; const managerUrl = resolveAgentRunManagerUrl(env, mapped.agentRun.managerUrl); const timeoutMs = parsePositiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, 20_000); const eventsResponse = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(mapped.agentRun.runId)}/events?afterSeq=${encodeURIComponent(String(mapped.agentRun.lastSeq ?? 0))}&limit=500`, { method: "GET", timeoutMs }); const events = Array.isArray(eventsResponse?.items) ? eventsResponse.items : []; appendAgentRunEventsToTrace(traceStore, traceId, events, mapped.agentRun); const result = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(mapped.agentRun.runId)}/commands/${encodeURIComponent(mapped.agentRun.commandId)}/result`, { method: "GET", timeoutMs }); const nextMapping = { ...mapped.agentRun, ...agentRunResultRefs(result), lastSeq: Math.max(Number(mapped.agentRun.lastSeq ?? 0), Number(result?.lastSeq ?? 0), ...events.map((event) => Number(event?.seq ?? 0))), status: result?.status ?? mapped.agentRun.status ?? "running", runStatus: result?.runStatus ?? mapped.agentRun.runStatus ?? null, commandState: result?.commandState ?? mapped.agentRun.commandState ?? null, terminalStatus: result?.terminalStatus ?? mapped.agentRun.terminalStatus ?? null, updatedAt: nowIso(options.now), valuesPrinted: false }; const base = { ...mapped, agentRun: nextMapping, updatedAt: nowIso(options.now) }; const payload = agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId }); options.codeAgentChatResults?.set?.(traceId, payload); return { result: payload, runnerTrace: payload.runnerTrace ?? traceStore.snapshot(traceId), found: true }; } export async function refreshAgentRunTrace({ traceId, result = null, options = {}, traceStore = defaultCodeAgentTraceStore }) { const mapped = result ?? await loadPersistedAgentRunResult(traceId, options); if (!mapped?.agentRun?.runId) return traceStore.snapshot(traceId); const env = options.env ?? process.env; const fetchImpl = options.fetchImpl ?? globalThis.fetch; const managerUrl = resolveAgentRunManagerUrl(env, mapped.agentRun.managerUrl); const timeoutMs = parsePositiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, 20_000); const eventsResponse = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(mapped.agentRun.runId)}/events?afterSeq=${encodeURIComponent(String(mapped.agentRun.lastSeq ?? 0))}&limit=500`, { method: "GET", timeoutMs }); const events = Array.isArray(eventsResponse?.items) ? eventsResponse.items : []; appendAgentRunEventsToTrace(traceStore, traceId, events, mapped.agentRun); const lastSeq = Math.max(Number(mapped.agentRun.lastSeq ?? 0), ...events.map((event) => Number(event?.seq ?? 0))); if (lastSeq !== Number(mapped.agentRun.lastSeq ?? 0)) { options.codeAgentChatResults?.set?.(traceId, { ...mapped, agentRun: { ...mapped.agentRun, lastSeq, updatedAt: nowIso(options.now) } }); } return traceStore.snapshot(traceId); } export async function cancelAgentRunChatTurn({ traceId, currentResult = null, options = {}, traceStore = defaultCodeAgentTraceStore }) { const mapped = currentResult ?? await loadPersistedAgentRunResult(traceId, options); if (!mapped?.agentRun?.commandId) return null; const env = options.env ?? process.env; const fetchImpl = options.fetchImpl ?? globalThis.fetch; const managerUrl = resolveAgentRunManagerUrl(env, mapped.agentRun.managerUrl); const timeoutMs = parsePositiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, 20_000); await agentRunJson(fetchImpl, managerUrl, `/api/v1/commands/${encodeURIComponent(mapped.agentRun.commandId)}/cancel`, { method: "POST", body: { reason: "hwlab-user-cancel", traceId }, timeoutMs }); traceStore.append(traceId, { type: "cancel", status: "canceled", label: "agentrun:cancel:canceled", message: "HWLAB forwarded cancel to AgentRun command cancel API.", runId: mapped.agentRun.runId, commandId: mapped.agentRun.commandId, terminal: true, valuesPrinted: false }); const now = nowIso(options.now); const agentRun = { ...mapped.agentRun, status: "cancelled", commandState: "cancelled", terminalStatus: "cancelled", updatedAt: now }; const payload = { ...mapped, status: "canceled", canceled: true, updatedAt: now, agentRun, runnerTrace: traceStore.snapshot(traceId), error: { code: "agentrun_canceled", layer: "agentrun", category: "canceled", retryable: true, message: "AgentRun command was canceled by user request", userMessage: "当前 AgentRun 请求已取消;traceId/runId/commandId 已保留,可重试上一条消息。", traceId, route: "/v1/agent/chat/cancel", toolName: "agentrun.command.cancel" }, userMessage: "当前 AgentRun 请求已取消;traceId/runId/commandId 已保留,可重试上一条消息。" }; options.codeAgentChatResults?.set?.(traceId, payload); return payload; } export async function steerAgentRunChatTurn({ traceId, currentResult = null, params = {}, options = {}, traceStore = defaultCodeAgentTraceStore }) { const mapped = currentResult ?? await loadPersistedAgentRunResult(traceId, options); if (!safeTraceId(traceId) || !mapped?.agentRun?.runId || !mapped?.agentRun?.commandId) return null; const message = firstNonEmpty(params.message, params.prompt, params.text); if (!message) { throw Object.assign(new Error("steer command requires non-empty message text"), { code: "steer_message_missing", statusCode: 400 }); } const env = options.env ?? process.env; const fetchImpl = options.fetchImpl ?? globalThis.fetch; const managerUrl = resolveAgentRunManagerUrl(env, mapped.agentRun.managerUrl); const timeoutMs = parsePositiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, 20_000); const steerTraceId = safeTraceId(params.steerTraceId ?? params.steerId) || `trc_steer_${randomUUID().replace(/-/gu, "")}`; traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:steer:accepted", message: "HWLAB accepted an in-flight steer request and will create an AgentRun type=steer command on the existing run.", runId: mapped.agentRun.runId, commandId: mapped.agentRun.commandId, steerTraceId, waitingFor: "agentrun-steer-command-create", valuesPrinted: false }); const commandInput = buildAgentRunSteerCommandInput({ params: { ...params, message }, traceId, steerTraceId, mapped }); const command = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(mapped.agentRun.runId)}/commands`, { method: "POST", body: commandInput, timeoutMs }); const steerCommandId = requiredString(command?.id, "command.id"); const now = nowIso(options.now); const agentRun = { ...mapped.agentRun, lastSteerCommandId: steerCommandId, lastSteerTraceId: steerTraceId, updatedAt: now, valuesPrinted: false }; traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:steer:command-created", message: `AgentRun steer command ${steerCommandId} created on run ${mapped.agentRun.runId}; runner will apply it if the target Codex turn is still active.`, runId: mapped.agentRun.runId, commandId: mapped.agentRun.commandId, steerCommandId, targetCommandId: mapped.agentRun.commandId, steerTraceId, waitingFor: "agentrun-steer-apply", valuesPrinted: false }); const payload = { ok: true, accepted: true, status: "running", shortConnection: true, controlSemantics: "steer-active-turn-and-poll-target-trace", route: "/v1/agent/chat/steer", traceId, targetTraceId: traceId, steerTraceId, conversationId: safeConversationId(mapped.conversationId ?? params.conversationId) || null, sessionId: safeSessionId(mapped.sessionId ?? params.sessionId) || null, threadId: safeOpaqueId(mapped.threadId ?? params.threadId) || null, resultUrl: `/v1/agent/chat/result/${encodeURIComponent(traceId)}`, traceUrl: `/v1/agent/chat/trace/${encodeURIComponent(traceId)}`, agentRun: { ...agentRun, steerCommandId, targetCommandId: mapped.agentRun.commandId, steerCommandState: command.state ?? null, valuesPrinted: false }, runnerTrace: traceStore.snapshot(traceId, agentRunTraceMeta({}, {})), updatedAt: now, valuesPrinted: false }; options.codeAgentChatResults?.set?.(traceId, { ...mapped, agentRun, runnerTrace: payload.runnerTrace, updatedAt: now }); return payload; } export async function loadPersistedAgentRunResult(traceId, options = {}) { if (!safeTraceId(traceId) || typeof options.accessController?.getAgentSessionByTraceId !== "function") return null; const session = await options.accessController.getAgentSessionByTraceId(traceId); const agentRun = session?.session?.agentRun; if (!agentRun || typeof agentRun !== "object" || !agentRun.runId || !agentRun.commandId) return null; return { accepted: true, status: session.status === "canceled" ? "canceled" : "running", shortConnection: true, traceId, conversationId: safeConversationId(session.conversationId) || agentRun.conversationId || null, sessionId: safeSessionId(session.id) || agentRun.sessionId || null, threadId: safeOpaqueId(session.threadId) || agentRun.threadId || null, messageId: session.session?.messageId ?? `msg_${traceId.slice(4)}`, createdAt: session.startedAt ?? session.updatedAt ?? nowIso(options.now), updatedAt: session.updatedAt ?? nowIso(options.now), ownerUserId: session.ownerUserId, provider: CODEX_STDIO_PROVIDER, model: modelForBackendProfile(agentRun.backendProfile, options.env ?? process.env), backend: CODEX_STDIO_BACKEND, infrastructureBackend: `agentrun-v01/${agentRun.backendProfile ?? "deepseek"}`, capabilityLevel: CODEX_STDIO_CAPABILITY_LEVEL, sessionMode: CODEX_STDIO_SESSION_MODE, implementationType: CODEX_STDIO_IMPLEMENTATION_TYPE, agentRun: { ...agentRun, adapter: ADAPTER_ID, valuesPrinted: false }, valuesPrinted: false }; } export function agentRunSessionEvidence(payload = {}) { if (!payload?.agentRun) return {}; return { agentRun: { adapter: ADAPTER_ID, 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, backendProfile: payload.agentRun.backendProfile ?? null, managerUrl: payload.agentRun.managerUrl ?? DEFAULT_AGENTRUN_MGR_URL, repoUrl: payload.agentRun.repoUrl ?? DEFAULT_REPO_URL, status: payload.agentRun.status ?? null, runStatus: payload.agentRun.runStatus ?? null, commandState: payload.agentRun.commandState ?? null, terminalStatus: payload.agentRun.terminalStatus ?? null, lastSeq: payload.agentRun.lastSeq ?? 0, providerId: payload.agentRun.providerId ?? DEFAULT_PROVIDER_ID, reuseEligible: payload.agentRun.reuseEligible ?? false, sessionId: payload.agentRun.sessionId ?? payload.sessionId ?? null, conversationId: payload.conversationId ?? payload.agentRun.conversationId ?? null, threadId: payload.threadId ?? payload.agentRun.threadId ?? null, providerTrace: payload.providerTrace ?? payload.agentRun.providerTrace ?? null, valuesPrinted: false } }; } function buildAgentRunCreateRunInput({ params, env, traceId, backendProfile, sessionId }) { const commitId = fullSourceCommit(env); const threadId = safeOpaqueId(params.threadId); const resourceBundleRef = commitId ? { kind: "git", repoUrl: resolveAgentRunRepoUrl(env), commitId, submodules: false, lfs: false, toolAliases: HWLAB_RESOURCE_TOOL_ALIASES, promptRefs: HWLAB_RESOURCE_PROMPT_REFS, skillRefs: HWLAB_RESOURCE_SKILL_REFS } : null; return { tenantId: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_TENANT_ID, DEFAULT_TENANT_ID), projectId: firstNonEmpty(params.projectId, env.HWLAB_CODE_AGENT_AGENTRUN_PROJECT_ID, DEFAULT_PROJECT_ID), workspaceRef: { kind: "opaque", repo: DEFAULT_PROJECT_ID, branch: firstNonEmpty(env.HWLAB_BOOT_REF, env.HWLAB_CODE_AGENT_AGENTRUN_BRANCH, "v0.2"), runtimeNamespace: firstNonEmpty(env.HWLAB_RUNTIME_NAMESPACE, env.POD_NAMESPACE, env.HWLAB_NAMESPACE, "hwlab-v02"), hwlabTraceId: traceId, valuesPrinted: false }, sessionRef: { sessionId: sessionId ?? scopedAgentRunSessionIdForParams(params, traceId, backendProfile), ...(safeConversationId(params.conversationId) ? { conversationId: safeConversationId(params.conversationId) } : {}), ...(threadId ? { threadId } : {}), metadata: { adapter: ADAPTER_ID, hwlabTraceId: traceId, hwlabApi: "/v1/agent/chat", hwlabSessionId: safeSessionId(params.sessionId) || null, threadContinuityPolicy: THREAD_CONTINUITY_POLICY, sessionPolicy: SESSION_POLICY_RUN_LOCAL, agentRunSessionProfile: backendProfile, agentRunSessionPolicy: "backend-profile-scoped", valuesPrinted: false } }, resourceBundleRef, providerId: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID, DEFAULT_PROVIDER_ID), backendProfile, executionPolicy: { sandbox: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_SANDBOX, env.HWLAB_CODE_AGENT_CODEX_SANDBOX, "danger-full-access"), approval: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_APPROVAL, "never"), timeoutMs: parsePositiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_TIMEOUT_MS, parsePositiveInteger(env.HWLAB_CODE_AGENT_TIMEOUT_MS, DEFAULT_TIMEOUT_MS)), network: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_NETWORK, "enabled"), secretScope: { allowCredentialEcho: false, providerCredentials: [providerCredentialSecretRef(backendProfile, env)], toolCredentials: agentRunToolCredentials(env) } }, traceSink: { kind: "hwlab-cloud-api", traceId, resultUrl: `/v1/agent/chat/result/${encodeURIComponent(traceId)}`, traceUrl: `/v1/agent/chat/trace/${encodeURIComponent(traceId)}`, valuesPrinted: false } }; } function buildAgentRunCommandInput({ params, traceId, backendProfile, sessionId }) { const prompt = String(params.message ?? params.prompt ?? "").trim(); const threadId = safeOpaqueId(params.threadId); return { type: "turn", payload: { prompt, message: prompt, traceId, conversationId: safeConversationId(params.conversationId) || null, sessionId: sessionId ?? scopedAgentRunSessionIdForParams(params, traceId, backendProfile), hwlabSessionId: safeSessionId(params.sessionId) || null, threadId: threadId || null, threadContinuityPolicy: THREAD_CONTINUITY_POLICY, sessionPolicy: SESSION_POLICY_RUN_LOCAL, providerProfile: backendProfile, source: "hwlab-cloud-api", valuesPrinted: false }, idempotencyKey: traceId }; } function buildAgentRunSteerCommandInput({ params, traceId, steerTraceId, mapped }) { const prompt = String(params.message ?? params.prompt ?? params.text ?? "").trim(); return { type: "steer", payload: { prompt, message: prompt, text: prompt, traceId: steerTraceId, targetTraceId: traceId, targetCommandId: mapped.agentRun.commandId, conversationId: safeConversationId(mapped.conversationId ?? params.conversationId) || null, sessionId: mapped.agentRun.sessionId ?? safeSessionId(mapped.sessionId ?? params.sessionId) ?? null, hwlabSessionId: safeSessionId(mapped.sessionId ?? params.sessionId) || null, threadId: safeOpaqueId(mapped.threadId ?? params.threadId) || null, source: "hwlab-cloud-api", valuesPrinted: false }, idempotencyKey: steerTraceId }; } function buildAgentRunRunnerJobInput({ env, traceId, commandId }) { const transientEnv = buildAgentRunTransientEnv(env); return { commandId, idempotencyKey: traceId, namespace: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE, env.AGENTRUN_RUNTIME_NAMESPACE, DEFAULT_RUNNER_NAMESPACE), ...(firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_IMAGE, env.AGENTRUN_RUNNER_IMAGE) ? { image: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_IMAGE, env.AGENTRUN_RUNNER_IMAGE) } : {}), ...(firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_SERVICE_ACCOUNT, env.AGENTRUN_RUNNER_SERVICE_ACCOUNT) ? { serviceAccountName: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_SERVICE_ACCOUNT, env.AGENTRUN_RUNNER_SERVICE_ACCOUNT) } : {}), ...(transientEnv.length > 0 ? { transientEnv } : {}) }; } function buildAgentRunTransientEnv(env = process.env) { const entries = []; for (const name of [ "HWLAB_RUNTIME_API_URL", "HWLAB_RUNTIME_WEB_URL", "HWLAB_RUNTIME_NAMESPACE", "HWLAB_RUNTIME_LANE", "HWLAB_RUNTIME_ENDPOINT_LOCKED", "HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME", "HWLAB_DEVICE_POD_API_KEY", "UNIDESK_MAIN_SERVER_IP" ]) { const value = firstNonEmpty(env[name]); if (value) entries.push({ name, value, sensitive: name === "HWLAB_DEVICE_POD_API_KEY" }); } return entries; } function agentRunToolCredentials(env = process.env) { const namespace = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_TOOL_SECRET_NAMESPACE, env.HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE, DEFAULT_RUNNER_NAMESPACE); const githubSecretName = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_GITHUB_TOOL_SECRET_NAME, "agentrun-v01-tool-github-pr"); const githubSecretKey = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_GITHUB_TOOL_SECRET_KEY, "GH_TOKEN"); const unideskSecretName = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_UNIDESK_SSH_TOOL_SECRET_NAME, "agentrun-v01-tool-unidesk-ssh"); return [ { tool: "github", purpose: "pull-request", secretRef: { namespace, name: githubSecretName, keys: [githubSecretKey] }, projection: { kind: "env", envName: githubSecretKey, secretKey: githubSecretKey } }, { tool: "unidesk-ssh", purpose: "ssh-passthrough", secretRef: { namespace, name: unideskSecretName, keys: ["UNIDESK_SSH_CLIENT_TOKEN"] }, projection: { kind: "env", envName: "UNIDESK_SSH_CLIENT_TOKEN", secretKey: "UNIDESK_SSH_CLIENT_TOKEN" } } ]; } async function resolveReusableAgentRun({ params = {}, options = {}, env = process.env, managerUrl, fetchImpl, timeoutMs, backendProfile, traceId, traceStore }) { const hwlabSessionId = hwlabSessionIdForParams(params, traceId); if (!safeSessionId(hwlabSessionId) || hwlabSessionId === agentRunSessionId(traceId) || typeof options.accessController?.getAgentSession !== "function") { return null; } const session = await options.accessController.getAgentSession(hwlabSessionId); if (!canReuseAgentRunSessionForOwner(session, params, options)) { traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:run:reuse-owner-skipped", message: "Stored AgentRun session is not visible to the current actor; a new runner Job will be created for this request.", sessionId: hwlabSessionId, waitingFor: "agentrun-run-create", valuesPrinted: false }); return null; } const mapping = session?.session?.agentRun; if (!isReusableAgentRunMapping(mapping, backendProfile)) { traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:run:reuse-skipped", message: "No reusable AgentRun run was found for this HWLAB session; a new runner Job will be created.", sessionId: hwlabSessionId, waitingFor: "agentrun-run-create", valuesPrinted: false }); return null; } let run = null; try { run = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(mapping.runId)}`, { method: "GET", timeoutMs }); } catch (error) { traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:run:reuse-unavailable", message: `Stored AgentRun run ${mapping.runId} could not be read; a new runner Job will be created.`, errorCode: error?.code ?? "agentrun_run_lookup_failed", runId: mapping.runId, sessionId: hwlabSessionId, waitingFor: "agentrun-run-create", valuesPrinted: false }); return null; } const reason = agentRunReuseBlocker(run, mapping); if (reason) { traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:run:reuse-blocked", message: `Stored AgentRun run ${mapping.runId} cannot be reused: ${reason}; a new runner Job will be created.`, runId: mapping.runId, sessionId: hwlabSessionId, waitingFor: "agentrun-run-create", valuesPrinted: false }); return null; } traceStore.append(traceId, { type: "backend", status: "running", label: "agentrun:run:reused", message: `AgentRun run ${mapping.runId} is reused for HWLAB session ${hwlabSessionId}; runner reuse window is active and no new bundle will be requested for this turn.`, runId: mapping.runId, commandId: mapping.commandId ?? null, runnerId: mapping.runnerId ?? null, jobName: mapping.jobName ?? null, namespace: mapping.namespace ?? null, sessionId: hwlabSessionId, waitingFor: "agentrun-command-create", valuesPrinted: false }); return { sessionId: mapping.sessionId ?? scopedAgentRunSessionIdForParams(params, traceId, backendProfile), hwlabSessionId, session, mapping, run }; } function isReusableAgentRunMapping(mapping, backendProfile) { return Boolean( mapping && typeof mapping === "object" && mapping.adapter === ADAPTER_ID && mapping.reuseEligible !== false && mapping.runId && mapping.jobName && (!mapping.backendProfile || mapping.backendProfile === backendProfile) ); } function canReuseAgentRunSessionForOwner(session, params = {}, options = {}) { if (!session) return false; const ownerUserId = options.actor?.id ?? params.ownerUserId ?? null; const ownerRole = options.actor?.role ?? params.ownerRole ?? null; if (ownerRole === "admin") return true; if (!session.ownerUserId) return true; return Boolean(ownerUserId && session.ownerUserId === ownerUserId); } function agentRunReuseBlocker(run = {}, mapping = {}) { const status = String(run?.terminalStatus ?? run?.status ?? mapping.runStatus ?? "").toLowerCase(); if (TERMINAL_RUN_STATUSES.has(status)) return `run_status_${status}`; if (!run?.claimedBy) return "runner_not_claimed"; if (runLeaseExpired(run.leaseExpiresAt)) return "runner_reuse_window_expired"; return null; } function runLeaseExpired(leaseExpiresAt) { const expiresMs = Date.parse(String(leaseExpiresAt ?? "")); return !Number.isFinite(expiresMs) || expiresMs <= Date.now(); } function providerCredentialSecretRef(profile, env) { const profileEnvKey = String(profile ?? "").toUpperCase().replace(/[^A-Z0-9]+/gu, "_"); return { profile, secretRef: { namespace: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE, DEFAULT_RUNNER_NAMESPACE), name: firstNonEmpty(env[`HWLAB_CODE_AGENT_AGENTRUN_${profileEnvKey}_SECRET_NAME`], env[`HWLAB_CODE_AGENT_AGENTRUN_${String(profile ?? "").toUpperCase()}_SECRET_NAME`], env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_SECRET_NAME, `agentrun-v01-provider-${profile}`), keys: ["auth.json", "config.toml"], mountPath: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_CODEX_HOME_MOUNT, "~/.codex") } }; } function agentRunProviderTrace({ base = {}, result = {}, terminalStatus = null } = {}) { return { transport: "stdio", protocol: CODEX_APP_SERVER_PROTOCOL, wireApi: CODEX_APP_SERVER_WIRE_API, runnerKind: CODEX_STDIO_RUNNER_KIND, implementationType: CODEX_STDIO_IMPLEMENTATION_TYPE, command: "codex app-server --listen stdio://", toolName: "agentrun.v01.command.turn", source: ADAPTER_ID, backendProfile: base.agentRun?.backendProfile ?? null, runId: base.agentRun?.runId ?? result?.runId ?? null, commandId: base.agentRun?.commandId ?? result?.commandId ?? null, runnerId: base.agentRun?.runnerId ?? result?.runnerId ?? null, jobName: base.agentRun?.jobName ?? result?.jobName ?? null, namespace: base.agentRun?.namespace ?? result?.namespace ?? null, traceId: base.traceId ?? base.agentRun?.traceId ?? null, threadId: result?.sessionRef?.threadId ?? base.agentRun?.threadId ?? base.threadId ?? null, turnId: result?.turnId ?? null, terminalStatus: terminalStatus ?? result?.terminalStatus ?? null, failureKind: result?.failureKind ?? null, failureMessage: result?.failureMessage ?? result?.blocker?.message ?? null, valuesPrinted: false }; } function agentRunFailureAttribution({ code, message, canceled = false } = {}) { if (canceled) { return { category: "canceled", retryable: true, userMessage: "AgentRun 请求已取消。", summary: message || "AgentRun command was canceled." }; } if (code === "provider-invalid-tool-call" || /invalid function arguments json string|invalid_prompt|tool_call_id/iu.test(String(message ?? ""))) { return { category: "provider_invalid_tool_call", retryable: true, userMessage: "Codex app-server/provider 返回了无效 tool-call arguments JSON;这不是 HWLAB device-pod 或 Cloud API 端点失败,请查看 providerTrace.failureKind 和 runnerTrace 定位上游 tool_call_id。", summary: message || "Codex app-server/provider returned invalid tool-call arguments JSON." }; } if (code === "thread-resume-failed") { return { category: "thread_resume_failed", retryable: true, userMessage: "AgentRun 复用的 Codex thread 已失效;当前 turn 会按失败终止,Workbench 会清理 stale continuation 指针,下一轮自动从新 session/thread 启动。", summary: message || "AgentRun thread/resume failed for an existing thread." }; } return { category: "agentrun_failed", retryable: true, userMessage: "AgentRun 请求失败;请查看 runnerTrace、providerTrace 和 agentRun 字段定位 runId/commandId/jobName。", summary: message || "AgentRun command failed." }; } function agentRunLongLivedSessionGate(base = {}) { return longLivedSessionGate({ provider: CODEX_STDIO_PROVIDER, runnerKind: CODEX_STDIO_RUNNER_KIND, session: agentRunSessionSummary(base, "idle"), sessionMode: CODEX_STDIO_SESSION_MODE, implementationType: CODEX_STDIO_IMPLEMENTATION_TYPE, codexStdioFeasibility: { ready: true, canStartLongLivedCodexStdio: true, source: ADAPTER_ID, valuesPrinted: false } }); } function agentRunToolCalls(result = {}, status = "completed") { return [{ name: "codex-app-server.turn", status, runId: result?.runId ?? null, commandId: result?.commandId ?? null, outputSummary: result?.reply ? `assistantChars=${String(result.reply).length}` : null, valuesPrinted: false }]; } function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId }) { const terminalStatus = String(result?.terminalStatus ?? ""); const terminal = terminalStatus === "completed" || terminalStatus === "failed" || terminalStatus === "blocked" || terminalStatus === "cancelled"; if (!terminal) { return decorateAgentRunRunningResult({ base, mapping: base.agentRun, traceStore, traceId }); } const now = nowIso(); const runnerTrace = traceStore.snapshot(traceId, agentRunTraceMeta({}, {})); const terminalEventCreatedAt = agentRunResultTraceCreatedAt(runnerTrace, now); const providerTrace = agentRunProviderTrace({ base, result, terminalStatus }); if (terminalStatus === "completed" && String(result?.reply ?? "").trim()) { traceStore.append(traceId, { type: "result", status: "completed", label: "agentrun:result:completed", createdAt: terminalEventCreatedAt, message: "AgentRun result is ready for HWLAB short-connection polling.", runId: base.agentRun.runId, commandId: base.agentRun.commandId, terminal: true, valuesPrinted: false }); return { ...base, status: "completed", updatedAt: now, workspace: base.workspace ?? "/home/agentrun/workspace", sandbox: base.sandbox ?? "danger-full-access", session: agentRunSessionSummary(base, "idle"), sessionReuse: agentRunSessionReuseSummary(base, base.agentRun.reused === true), runner: agentRunRunnerSummary(base.agentRun), runnerTrace: traceStore.snapshot(traceId, agentRunTraceMeta({}, {})), toolCalls: agentRunToolCalls(result, "completed"), skills: { status: "delegated", provider: ADAPTER_ID, count: 0, items: [], valuesPrinted: false }, longLivedSessionGate: agentRunLongLivedSessionGate(base), providerTrace, reply: { messageId: base.messageId ?? `msg_${traceId.slice(4)}`, role: "assistant", content: String(result.reply), createdAt: now }, usage: null, agentRun: { ...base.agentRun, terminalStatus, completed: true, reuseEligible: true, providerTrace, valuesPrinted: false }, valuesPrinted: false }; } const canceled = terminalStatus === "cancelled"; const code = canceled ? "agentrun_canceled" : result?.failureKind ?? (terminalStatus === "blocked" ? "agentrun_blocked" : "agentrun_failed"); const message = result?.failureMessage ?? result?.blocker?.message ?? (canceled ? "AgentRun command was canceled" : "AgentRun command failed"); const attribution = agentRunFailureAttribution({ code, message, canceled }); traceStore.append(traceId, { type: "result", status: canceled ? "canceled" : "failed", label: `agentrun:result:${canceled ? "canceled" : terminalStatus || "failed"}`, createdAt: terminalEventCreatedAt, errorCode: code, message: attribution.summary, runId: base.agentRun.runId, commandId: base.agentRun.commandId, terminal: true, valuesPrinted: false }); const partialContext = partialAgentRunContext(runnerTrace); return { ...base, status: canceled ? "canceled" : "failed", canceled, updatedAt: now, session: agentRunSessionSummary(base, canceled ? "canceled" : "failed"), sessionReuse: agentRunSessionReuseSummary(base, base.agentRun.reused === true), runner: agentRunRunnerSummary(base.agentRun), runnerTrace: partialContext ? { ...runnerTrace, partialContext } : runnerTrace, toolCalls: agentRunToolCalls(result, canceled ? "canceled" : "failed"), skills: { status: "delegated", provider: ADAPTER_ID, count: 0, items: [], valuesPrinted: false }, providerTrace, error: { code, layer: "agentrun", category: attribution.category, retryable: attribution.retryable, message, userMessage: attribution.userMessage, traceId, route: "/v1/agent/chat", toolName: "agentrun.manual-dispatch" }, blocker: canceled ? null : { code, layer: "agentrun", category: attribution.category, retryable: attribution.retryable, summary: attribution.summary, traceId, route: "/v1/agent/chat", toolName: "agentrun.manual-dispatch" }, agentRun: { ...base.agentRun, terminalStatus, completed: false, providerTrace, valuesPrinted: false }, ...(partialContext ? { partialContext } : {}), valuesPrinted: false }; } function partialAgentRunContext(runnerTrace = {}) { const events = Array.isArray(runnerTrace.events) ? runnerTrace.events : []; const assistantMessages = events .filter((event) => event?.type === "assistant" && String(event.text ?? event.message ?? "").trim()) .map((event) => String(event.text ?? event.message).trim()) .slice(-4); const toolEvidence = events .filter((event) => event?.type === "tool_call" && String(event.status ?? "") === "completed") .map((event) => [event.toolName ?? event.label ?? "tool", event.command ?? event.outputSummary ?? event.stdoutSummary ?? event.message ?? ""].filter(Boolean).join(": ")) .filter(Boolean) .slice(-6); if (assistantMessages.length === 0 && toolEvidence.length === 0) return null; return { status: "partial-before-terminal", summary: "AgentRun terminal 前已有可延续的 assistant/tool 上下文;后续同 conversation/session/thread 轮次必须能继续使用。", assistantMessages, toolEvidence, valuesPrinted: false }; } function agentRunResultTraceCreatedAt(runnerTrace = {}, fallback) { const last = String(runnerTrace?.lastEvent?.createdAt ?? runnerTrace?.updatedAt ?? ""); return Number.isFinite(Date.parse(last)) ? last : fallback; } function decorateAgentRunRunningResult({ base, mapping, traceStore, traceId }) { return { ...base, status: "running", accepted: true, shortConnection: true, updatedAt: nowIso(), session: agentRunSessionSummary({ ...base, agentRun: mapping }, "running"), sessionReuse: agentRunSessionReuseSummary({ ...base, agentRun: mapping }, mapping.reused === true), runner: agentRunRunnerSummary(mapping), runnerTrace: traceStore.snapshot(traceId, agentRunTraceMeta({}, {})), agentRun: { ...mapping, adapter: ADAPTER_ID, valuesPrinted: false }, valuesPrinted: false }; } function appendAgentRunEventsToTrace(traceStore, traceId, events, mapping = {}) { for (const event of events) { if (isForeignAgentRunCommandEvent(event, mapping)) continue; const normalized = mapAgentRunEvent(event, mapping); if (normalized) traceStore.append(traceId, normalized, agentRunTraceMeta({}, {})); } } function isForeignAgentRunCommandEvent(event, mapping = {}) { const currentCommandId = typeof mapping.commandId === "string" ? mapping.commandId : ""; if (!currentCommandId) return false; const payload = event?.payload && typeof event.payload === "object" ? event.payload : {}; const eventCommandId = typeof payload.commandId === "string" ? payload.commandId : ""; return Boolean(eventCommandId && eventCommandId !== currentCommandId); } function mapAgentRunEvent(event, mapping = {}) { if (!event || typeof event !== "object") return null; const payload = event.payload && typeof event.payload === "object" ? event.payload : {}; const base = { createdAt: event.createdAt ?? null, source: "agentrun", sourceSeq: event.seq ?? null, runId: event.runId ?? mapping.runId ?? null, commandId: payload.commandId ?? mapping.commandId ?? null, attemptId: payload.attemptId ?? mapping.attemptId ?? null, runnerId: payload.runnerId ?? mapping.runnerId ?? null, jobName: payload.jobName ?? mapping.jobName ?? null, namespace: payload.namespace ?? mapping.namespace ?? null, valuesPrinted: false }; if (event.type === "backend_status") { const phase = String(payload.phase ?? "status"); return { ...base, type: "backend", status: "running", label: `agentrun:backend:${phase}`, message: textPayload(payload, phase), waitingFor: waitingForForPhase(phase) }; } if (event.type === "assistant_message") { const terminal = payload.replyAuthority === true || payload.final === true; return { ...base, type: "assistant", status: terminal ? "completed" : "running", label: "agentrun:assistant:message", message: textPayload(payload, "assistant message"), text: textPayload(payload, ""), itemId: payload.itemId ?? null, messageIndex: payload.messageIndex ?? null, messageCount: payload.messageCount ?? null, replyAuthority: payload.replyAuthority === true, final: payload.final === true, terminal }; } if (event.type === "tool_call") { const commandExecution = agentRunCommandExecutionEvent(base, payload); if (commandExecution) return commandExecution; return { ...base, type: "tool_call", status: String(payload.status ?? (payload.method === "item/completed" ? "completed" : "running")), label: `agentrun:tool:${String(payload.name ?? payload.method ?? "call")}`, itemId: payload.item?.id ?? payload.itemId ?? null, toolName: payload.item?.type ?? payload.name ?? payload.method ?? "call", message: textPayload(payload, "tool call"), outputBytes: typeof payload.outputBytes === "number" ? payload.outputBytes : payload.summary?.outputBytes, outputTruncated: payload.outputTruncated === true || payload.summary?.outputTruncated === true }; } if (event.type === "command_output") { const stream = payload.stream === "stderr" ? "stderr" : "stdout"; return { ...base, type: "output", status: "running", label: `agentrun:output:${stream}`, stream, message: textPayload(payload, ""), outputTruncated: payload.outputTruncated === true }; } if (event.type === "diff") { return { ...base, type: "diff", status: "running", label: "agentrun:diff", message: textPayload(payload, "diff") }; } if (event.type === "error") { return { ...base, type: "error", status: "failed", label: `agentrun:error:${String(payload.failureKind ?? "backend")}`, errorCode: payload.failureKind ?? "agentrun_error", message: textPayload(payload, "AgentRun error") }; } if (event.type === "terminal_status") { const terminalStatus = String(payload.terminalStatus ?? "failed"); return { ...base, type: "result", status: terminalStatus === "cancelled" ? "canceled" : terminalStatus, label: `agentrun:terminal:${terminalStatus}`, errorCode: payload.failureKind ?? null, message: textPayload(payload, `AgentRun terminal status ${terminalStatus}`), terminal: true }; } return { ...base, type: "backend", status: "running", label: `agentrun:event:${String(event.type ?? "unknown")}`, message: textPayload(payload, "AgentRun event") }; } function agentRunCommandExecutionEvent(base, payload = {}) { const item = payload.item && typeof payload.item === "object" ? payload.item : null; const payloadToolType = firstNonEmpty(payload.toolName, payload.type, payload.name); if (item?.type !== "commandExecution" && payloadToolType !== "commandExecution") return null; const method = String(payload.method ?? ""); const completed = method === "item/completed" || payload.status === "completed" || item?.status === "completed"; const output = firstNonEmpty(item?.aggregatedOutput, item?.stdout, item?.output, payload.outputSummary, payload.stdoutSummary, payload.summary?.text, payload.itemPreview); return { ...base, type: "tool_call", status: completed ? "completed" : "started", label: completed ? "item/commandExecution:completed" : "item/commandExecution:started", toolName: "commandExecution", itemId: item?.id ?? payload.itemId ?? null, command: firstNonEmpty(item?.command, item?.commandLine, payload.command, payload.commandLine), exitCode: Number.isInteger(item?.exitCode) ? item.exitCode : Number.isInteger(payload.exitCode) ? payload.exitCode : undefined, durationMs: typeof item?.durationMs === "number" ? item.durationMs : typeof payload.durationMs === "number" ? payload.durationMs : undefined, outputBytes: typeof payload.outputBytes === "number" ? payload.outputBytes : payload.summary?.outputBytes, stdoutSummary: output, outputSummary: output, outputTruncated: payload.outputTruncated === true || payload.summary?.outputTruncated === true, message: output || textPayload(payload, "commandExecution") }; } async function agentRunJson(fetchImpl, managerUrl, path, { method = "GET", body, timeoutMs = 20_000 } = {}) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetchImpl(`${managerUrl}${path}`, { method, headers: body === undefined ? undefined : { "content-type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), signal: controller.signal }); const text = await response.text(); const parsed = text ? JSON.parse(text) : {}; if (!response.ok || parsed?.ok === false) { throw Object.assign(new Error(parsed?.message ?? `AgentRun ${method} ${path} failed with HTTP ${response.status}`), { code: parsed?.failureKind ?? "agentrun_request_failed", statusCode: response.status, agentRunError: parsed }); } return parsed?.ok === true && Object.hasOwn(parsed, "data") ? parsed.data : parsed; } catch (error) { if (error?.name === "AbortError") { throw Object.assign(new Error(`AgentRun ${method} ${path} timed out after ${timeoutMs}ms`), { code: "agentrun_timeout", statusCode: 504 }); } throw error; } finally { clearTimeout(timeout); } } function resolveAgentRunManagerUrl(env = process.env, override = null) { const raw = firstNonEmpty(override, env.AGENTRUN_MGR_URL, env.HWLAB_CODE_AGENT_AGENTRUN_MGR_URL, DEFAULT_AGENTRUN_MGR_URL); const url = new URL(raw); const host = url.hostname.toLowerCase(); const allowedHost = "agentrun-mgr.agentrun-v01.svc.cluster.local"; const allowedByTest = truthyFlag(env.HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL) && ["127.0.0.1", "localhost"].includes(host); if (url.protocol !== "http:" || (host !== allowedHost && !allowedByTest)) { throw Object.assign(new Error(`AGENTRUN_MGR_URL must use internal k3s Service DNS ${DEFAULT_AGENTRUN_MGR_URL}; got ${redactUrl(raw)}`), { code: "agentrun_internal_url_required", statusCode: 500 }); } url.pathname = url.pathname.replace(/\/+$/u, ""); url.search = ""; url.hash = ""; return url.toString().replace(/\/+$/u, ""); } function resolveAgentRunBackendProfile(env = process.env, params = {}) { const value = String(params.providerProfile ?? params.codeAgentProviderProfile ?? env.HWLAB_CODE_AGENT_AGENTRUN_BACKEND_PROFILE ?? env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE ?? "deepseek").trim().toLowerCase(); const mapped = value === "codex-api" || value === "codex" ? "codex" : value === "runtime-default" ? String(env.HWLAB_CODE_AGENT_AGENTRUN_DEFAULT_BACKEND_PROFILE ?? "deepseek").trim().toLowerCase() : value; return AGENTRUN_BACKENDS.includes(mapped) ? mapped : "deepseek"; } function agentRunMapping({ env, managerUrl, backendProfile, run, command, runnerJob, traceId, startedAt }) { return { adapter: ADAPTER_ID, managerUrl, backendProfile, providerId: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID, DEFAULT_PROVIDER_ID), runId: run.id, commandId: command.id, attemptId: runnerJob?.attemptId ?? runnerJob?.runner?.attemptId ?? null, runnerId: runnerJob?.runnerId ?? runnerJob?.runner?.runnerId ?? null, runnerJobId: runnerJob?.id ?? null, jobName: runnerJob?.jobName ?? runnerJob?.jobIdentity?.name ?? null, namespace: runnerJob?.namespace ?? runnerJob?.jobIdentity?.namespace ?? DEFAULT_RUNNER_NAMESPACE, status: "runner-job-created", runStatus: run.status ?? null, commandState: command.state ?? null, terminalStatus: null, lastSeq: 0, traceId, sessionId: run?.sessionRef?.sessionId ?? null, conversationId: run?.sessionRef?.conversationId ?? null, threadId: run?.sessionRef?.threadId ?? null, reused: false, reuseEligible: true, createdAt: startedAt, updatedAt: nowIso(), valuesPrinted: false }; } function agentRunReusedMapping({ previous = {}, run = {}, command = {}, traceId, startedAt, backendProfile, managerUrl, env }) { return { ...previous, adapter: ADAPTER_ID, managerUrl, backendProfile, providerId: previous.providerId ?? firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID, DEFAULT_PROVIDER_ID), runId: previous.runId ?? run?.id, commandId: command.id, status: "runner-job-reused", runStatus: run?.status ?? previous.runStatus ?? null, commandState: command.state ?? null, terminalStatus: null, lastSeq: previous.lastSeq ?? 0, runnerJobCount: 0, traceId, sessionId: run?.sessionRef?.sessionId ?? previous.sessionId ?? null, conversationId: run?.sessionRef?.conversationId ?? previous.conversationId ?? null, threadId: run?.sessionRef?.threadId ?? previous.threadId ?? null, reused: true, reuseEligible: true, createdAt: previous.createdAt ?? startedAt, updatedAt: nowIso(), valuesPrinted: false }; } function agentRunResultRefs(result = {}) { const refs = {}; for (const key of ["attemptId", "runnerId", "jobName", "namespace", "runnerJobCount"]) { if (result?.[key] !== undefined && result?.[key] !== null) refs[key] = result[key]; } if (result?.sessionRef && typeof result.sessionRef === "object") { if (result.sessionRef.sessionId) refs.sessionId = result.sessionRef.sessionId; if (result.sessionRef.conversationId) refs.conversationId = result.sessionRef.conversationId; if (result.sessionRef.threadId) refs.threadId = result.sessionRef.threadId; } return refs; } function hwlabSessionIdForParams(params = {}, traceId) { return safeSessionId(params.sessionId) || agentRunSessionId(traceId); } function scopedAgentRunSessionIdForParams(params = {}, traceId, backendProfile) { const baseSessionId = hwlabSessionIdForParams(params, traceId); const profile = agentRunSessionProfileToken(backendProfile); const base = String(baseSessionId).replace(/^ses_/u, "").replace(/[^A-Za-z0-9_]+/gu, "_").replace(/^_+|_+$/gu, "") || "session"; return `ses_agentrun_${profile}_${base}`; } function agentRunSessionProfileToken(backendProfile) { return String(backendProfile ?? "deepseek").trim().toLowerCase().replace(/[^a-z0-9]+/gu, "_").replace(/^_+|_+$/gu, "") || "default"; } function agentRunSessionId(traceId) { return `ses_agentrun_${String(safeTraceId(traceId) || `trc_${randomUUID()}`).slice(4)}`; } function agentRunSessionSummary(base, status) { return { sessionId: base.sessionId ?? base.agentRun?.sessionId ?? null, conversationId: base.conversationId ?? base.agentRun?.conversationId ?? null, threadId: base.threadId ?? base.agentRun?.threadId ?? null, status, sessionMode: CODEX_STDIO_SESSION_MODE, runnerKind: CODEX_STDIO_RUNNER_KIND, lastTraceId: base.traceId ?? base.agentRun?.traceId ?? null, longLivedSession: true, codexStdio: true, writeCapable: true, durable: true, durableSession: true, idleTimeoutMs: parsePositiveInteger(base.agentRun?.idleTimeoutMs, 600000), secretMaterialStored: false, valuesRedacted: true }; } function agentRunSessionReuseSummary(base, reused) { return { sessionId: base.sessionId ?? base.agentRun?.sessionId ?? null, conversationId: base.conversationId ?? base.agentRun?.conversationId ?? null, threadId: base.threadId ?? base.agentRun?.threadId ?? null, reused: Boolean(reused), status: reused ? "reused" : "new", runnerReused: Boolean(reused), valuesRedacted: true }; } function agentRunRunnerSummary(mapping = {}) { return { kind: CODEX_STDIO_RUNNER_KIND, adapter: ADAPTER_ID, runId: mapping.runId ?? null, commandId: mapping.commandId ?? null, attemptId: mapping.attemptId ?? null, runnerId: mapping.runnerId ?? null, jobName: mapping.jobName ?? null, namespace: mapping.namespace ?? null, backendProfile: mapping.backendProfile ?? null, provider: CODEX_STDIO_PROVIDER, sessionMode: CODEX_STDIO_SESSION_MODE, implementationType: CODEX_STDIO_IMPLEMENTATION_TYPE, capabilityLevel: CODEX_STDIO_CAPABILITY_LEVEL, codexStdio: true, writeCapable: true, durableSession: true, longLivedSession: true, sandbox: "danger-full-access", valuesPrinted: false }; } function agentRunTraceMeta(env = process.env) { return { runnerKind: CODEX_STDIO_RUNNER_KIND, workspace: resolveAgentRunRepoUrl(env), sandbox: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_SANDBOX, env.HWLAB_CODE_AGENT_CODEX_SANDBOX, "danger-full-access"), protocol: CODEX_APP_SERVER_PROTOCOL, sessionMode: CODEX_STDIO_SESSION_MODE, implementationType: CODEX_STDIO_IMPLEMENTATION_TYPE }; } function resolveAgentRunRepoUrl(env = process.env) { const raw = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_REPO_URL, env.HWLAB_BOOT_READ_URL, DEFAULT_REPO_URL); const url = new URL(raw); const host = url.hostname.toLowerCase(); const allowedHost = "git-mirror-http.devops-infra.svc.cluster.local"; const allowedByTest = truthyFlag(env.HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL) && ["127.0.0.1", "localhost"].includes(host); if (url.protocol !== "http:" || (host !== allowedHost && !allowedByTest)) { throw Object.assign(new Error(`HWLAB_CODE_AGENT_AGENTRUN_REPO_URL must use internal k3s git mirror ${DEFAULT_REPO_URL}; got ${redactUrl(raw)}`), { code: "agentrun_internal_repo_url_required", statusCode: 500 }); } url.pathname = url.pathname.replace(/\/+$/u, ""); url.search = ""; url.hash = ""; return url.toString().replace(/\/+$/u, ""); } function fullSourceCommit(env) { for (const value of [ env.HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT, env.HWLAB_BOOT_COMMIT, env.HWLAB_GITOPS_SOURCE_COMMIT, env.HWLAB_COMMIT_ID, env.HWLAB_GIT_SHA, env.GIT_COMMIT, env.HWLAB_REVISION ]) { const text = String(value ?? "").trim().toLowerCase(); if (/^[0-9a-f]{40}$/u.test(text)) return text; } return null; } function modelForBackendProfile(profile, env = process.env) { if (profile === "codex") return firstNonEmpty(env.HWLAB_CODE_AGENT_CODEX_API_MODEL, env.HWLAB_CODE_AGENT_MODEL, "gpt-5.5"); if (profile === "minimax-m3") return firstNonEmpty(env.HWLAB_CODE_AGENT_MINIMAX_M3_MODEL, "MiniMax-M3"); return firstNonEmpty(env.HWLAB_CODE_AGENT_DEEPSEEK_MODEL, "deepseek-chat"); } function waitingForForPhase(phase) { if (phase.includes("runner-job")) return "agentrun-runner"; if (phase.includes("resource-bundle")) return "agentrun-resource-bundle"; if (phase.includes("backend")) return "agentrun-backend"; return "agentrun-result"; } function textPayload(payload, fallback) { for (const key of ["message", "text", "content", "delta", "summary", "phase"]) { if (typeof payload?.[key] === "string" && payload[key].length > 0) return payload[key]; } return fallback; } function requiredString(value, fieldName) { if (typeof value === "string" && value.trim()) return value.trim(); throw Object.assign(new Error(`AgentRun response missing ${fieldName}`), { code: "agentrun_response_invalid", statusCode: 502 }); } function firstNonEmpty(...values) { for (const value of values) { const text = String(value ?? "").trim(); if (text) return text; } return null; } function nowIso(now) { return typeof now === "function" ? now() : new Date().toISOString(); } function redactUrl(value) { try { const url = new URL(value); if (url.username || url.password) { url.username = "***"; url.password = ""; } return url.toString(); } catch { return ""; } }