// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-19-p1-agentrun-incremental-cursor; draft-2026-06-20-p2-terminal-outbox-recovery; PJ2026-010205 HWLAB接入 draft-2026-06-17-r0; draft-2026-06-25-p0-session-warm-runner-contract; PJ2026-01060505 Workbench Performance draft-2026-06-17-p0. // Responsibility: AgentRun command-result/event mapping and trace observability helpers; Kafka owns persistence. import { randomUUID } from "node:crypto"; import { defaultCodeAgentTraceStore } from "./code-agent-trace-store.ts"; import { decorateCodeAgentSession } from "./code-agent-session-lifecycle.ts"; import { parsePositiveInteger, safeConversationId, safeOpaqueId, safeSessionId, safeTraceId, text } from "./server-http-utils.ts"; import { currentBackendPerformanceContext } from "./backend-performance.ts"; import { DEFAULT_AGENTRUN_MGR_URL, agentRunJson, isAgentRunManagerInternalServiceHost, resolveAgentRunManagerUrl } from "./code-agent-agentrun-runtime.ts"; import { agentRunAlreadyTerminalCancelPayload, agentRunCancelBlockedPayload, agentRunCancelTerminalStatus } from "./code-agent-agentrun-cancel.ts"; import { DEFAULT_AGENTRUN_REPO_URL, agentRunAvailabilityBlocker, agentRunProviderIdOrNull, agentRunTraceMeta, backendForBackendProfile, modelForBackendProfile, providerForBackendProfile, requireAgentRunSourceCommit, resolveAgentRunBackendProfile, resolveAgentRunProviderId, resolveAgentRunRepoUrl } from "./code-agent-agentrun-profile.ts"; import { agentRunPromptEventFields, agentRunPromptFields, agentRunPromptMetadataFromParams, agentRunPromptMetadataFromSession, agentRunPromptMetadataFromText, messageAuthorityTextValue } from "./code-agent-agentrun-prompt.ts"; import { codeAgentOtelTraceFields, codeAgentOtelTraceContext, emitCodeAgentOtelSpan } from "./otel-trace.ts"; const ADAPTER_ID = "agentrun-v01"; const ADAPTER_IDS = new Set([ADAPTER_ID, "agentrun-v02"]); const ADAPTER_ALIASES = new Set(["agentrun", "agentrun-v0.1", "agentrun-v0.2"]); const DEFAULT_TENANT_ID = "hwlab"; const DEFAULT_PROJECT_ID = "pikasTech/HWLAB"; const DEFAULT_RUNNER_NAMESPACE = "agentrun-v01"; const DEFAULT_TIMEOUT_MS = 1_200_000; const AGENTRUN_RUNNER_KIND = "agentrun-v01-shared-runner"; const AGENTRUN_SESSION_MODE = "agentrun-v01-durable-session"; const AGENTRUN_IMPLEMENTATION_TYPE = "agentrun-v01-shared-execution-infra"; const AGENTRUN_CAPABILITY_LEVEL = "agentrun-v01-shared-code-agent-session"; const AGENTRUN_PROVIDER_TRACE_PROTOCOL = "agentrun-v01-jsonrpc"; const AGENTRUN_PROVIDER_TRACE_WIRE_API = "agentrun-v01-command-result"; 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_GIT_BUNDLES = Object.freeze([ Object.freeze({ name: "hwlab-tools", subpath: "tools", target_path: "tools" }), Object.freeze({ name: "hwlab-agent-skills", subpath: "skills", target_path: ".agents/skills" }) ]); 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 RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS = new Set([ "agentrun-connect-failed", "agentrun-proxy-exec-failed", "agentrun-manager-fetch-failed", "agentrun-timeout", "timeout", "fetch-failed", "network-error" ]); export function isRetryableAgentRunTransportFailureKind(failureKind) { return RETRYABLE_AGENTRUN_TRANSPORT_FAILURE_KINDS.has(normalizedFailureKind(failureKind)); } 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_AGENTRUN_REPO_URL, status: payload.agentRun.status ?? null, runStatus: payload.agentRun.runStatus ?? null, commandState: payload.agentRun.commandState ?? null, terminalStatus: payload.agentRun.terminalStatus ?? null, traceId: payload.agentRun.traceId ?? payload.traceId ?? payload.providerTrace?.traceId ?? null, lastSeq: payload.agentRun.lastSeq ?? 0, providerId: payload.agentRun.providerId ?? agentRunProviderIdOrNull(), 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 } }; } export function agentRunFailureRequiresFreshSession(failureKind, failureMessage) { const kind = String(failureKind ?? "").trim().toLowerCase().replace(/_/gu, "-"); const message = String(failureMessage ?? ""); return kind === "session-store-evicted" || kind === "thread-resume-failed" || /session stor(?:e|age).*evicted|pvc-backed session|no rollout found for thread id|thread\/resume failed/iu.test(message); } export function agentRunFreshSessionFailure(result = {}) { const code = result?.failureKind ?? result?.errorCode ?? result?.code ?? (result?.terminalStatus === "failed" ? "agentrun_failed" : null); const message = result?.failureMessage ?? result?.blocker?.message ?? result?.error?.message ?? result?.message ?? ""; return { code: normalizedFailureKind(code) ?? "agentrun_failed", message, requiresFreshSession: agentRunFailureRequiresFreshSession(code, message), valuesPrinted: false }; } export 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") } }; } export function agentRunProviderTrace({ base = {}, result = {}, terminalStatus = null } = {}) { return { transport: "agentrun-v01", protocol: AGENTRUN_PROVIDER_TRACE_PROTOCOL, wireApi: AGENTRUN_PROVIDER_TRACE_WIRE_API, runnerKind: AGENTRUN_RUNNER_KIND, implementationType: AGENTRUN_IMPLEMENTATION_TYPE, command: "agentrun.v01.command.turn", 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 ?? agentRunEffectiveThreadId(base), turnId: result?.turnId ?? null, terminalStatus: terminalStatus ?? result?.terminalStatus ?? null, failureKind: result?.failureKind ?? null, failureMessage: result?.failureMessage ?? result?.blocker?.message ?? null, willRetry: result?.willRetry === true ? true : undefined, valuesPrinted: false }; } export 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: "AgentRun/provider 返回了无效 tool-call arguments JSON;这不是 HWPOD 或 Cloud API 端点失败,请查看 providerTrace.failureKind 和 runnerTrace 定位上游 tool_call_id。", summary: message || "AgentRun/provider returned invalid tool-call arguments JSON." }; } if (code === "thread-resume-failed") { return { category: "thread_resume_failed", retryable: true, userMessage: "AgentRun 复用的 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." }; } export function agentRunLongLivedSessionGate(base = {}) { const session = agentRunSessionSummary(base, "idle"); return { status: "pass", pass: true, requiredCapability: AGENTRUN_CAPABILITY_LEVEL, currentCapability: AGENTRUN_IMPLEMENTATION_TYPE, sessionId: session.sessionId ?? null, sessionStatus: session.status ?? null, runnerKind: AGENTRUN_RUNNER_KIND, provider: providerForBackendProfile(base.agentRun?.backendProfile ?? "deepseek"), sessionMode: AGENTRUN_SESSION_MODE, implementationType: AGENTRUN_IMPLEMENTATION_TYPE, adapter: ADAPTER_ID, delegatedToAgentRun: true, codexStdio: false, blockers: [], valuesPrinted: false }; } export function agentRunToolCalls(result = {}, status = "completed", replyTextOverride = null) { const replyText = messageAuthorityTextValue(replyTextOverride) || agentRunReplyText(result); return [{ name: "agentrun.v01.command.turn", status, runId: result?.runId ?? null, commandId: result?.commandId ?? null, outputSummary: replyText ? `assistantChars=${replyText.length}` : null, valuesPrinted: false }]; } export function agentRunReplyText(result = {}) { for (const value of [result?.reply, result?.assistantMessage, result?.finalResponse, result?.content, result?.text, result?.message]) { const text = messageAuthorityTextValue(value); if (text) return text; } return ""; } export function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId, appendResultEvent = true }) { const terminalStatus = String(result?.terminalStatus ?? ""); const normalizedTerminalStatus = terminalStatus.trim().toLowerCase().replace(/_/gu, "-"); const retryableInterruption = normalizedTerminalStatus !== "completed" && isRetryableProviderInterruptionPayload(result); const terminal = !retryableInterruption && ["completed", "failed", "blocked", "cancelled", "canceled"].includes(normalizedTerminalStatus); if (!terminal) { if (retryableInterruption && appendResultEvent) { traceStore.append(traceId, agentRunProviderRetryEvent({ base, payload: result, status: "retrying", label: "agentrun:provider-retry:provider-stream-disconnected" }), base.agentRun); } const runningMapping = retryableInterruption ? { ...base.agentRun, status: "retrying", commandState: "retrying", failureKind: normalizedFailureKind(result?.failureKind), willRetry: true } : base.agentRun; const running = decorateAgentRunRunningResult({ base, mapping: runningMapping, traceStore, traceId }); if (!retryableInterruption) return running; return { ...running, status: "retrying", providerTrace: agentRunProviderTrace({ base, result, terminalStatus }), transient: { failureKind: normalizedFailureKind(result?.failureKind), willRetry: true, valuesRedacted: true }, valuesPrinted: false }; } const now = nowIso(); const runnerTrace = traceStore.snapshot(traceId, agentRunTraceMeta({}, {})); const terminalEventCreatedAt = agentRunResultTraceCreatedAt(runnerTrace, now); const providerTrace = agentRunProviderTrace({ base, result, terminalStatus }); const replyText = agentRunReplyText(result); if (normalizedTerminalStatus === "completed") { const traceReplyText = agentRunTraceTerminalAssistantText(runnerTrace); const completedReplyText = replyText || traceReplyText; const finalResponse = completedReplyText ? agentRunCompletedFinalResponse({ base, result, traceId, now, replyText: completedReplyText }) : null; const traceSummary = agentRunCompletedTraceSummary({ base, runnerTrace, finalResponse, traceId }); if (appendResultEvent) { traceStore.append(traceId, agentRunTraceEvent({ type: "result", status: "completed", label: "agentrun:result:completed", createdAt: terminalEventCreatedAt, message: "AgentRun result is committed to the Workbench projection event stream.", runId: base.agentRun.runId, commandId: base.agentRun.commandId, terminal: true, valuesPrinted: false }, base.agentRun)); } return { ...base, status: "completed", messageId: finalResponse?.messageId ?? base.messageId ?? `msg_${traceId.slice(4)}`, threadId: agentRunEffectiveThreadId(base), 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", completedReplyText), skills: { status: "delegated", provider: ADAPTER_ID, count: 0, items: [], valuesPrinted: false }, longLivedSessionGate: agentRunLongLivedSessionGate(base), providerTrace, finalResponse: finalResponse ?? null, traceSummary, reply: finalResponse ? { messageId: finalResponse.messageId, role: "assistant", content: finalResponse.text, createdAt: finalResponse.createdAt } : null, 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 }); const finalResponse = agentRunTerminalFailureFinalResponse({ base, traceId, now, status: canceled ? "canceled" : "failed", text: firstNonEmpty(message, attribution.summary, attribution.userMessage) }); const traceSummary = agentRunTerminalFailureTraceSummary({ base, runnerTrace, finalResponse, traceId, terminalStatus: canceled ? "canceled" : terminalStatus || "failed" }); if (appendResultEvent) { traceStore.append(traceId, agentRunTraceEvent({ 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 }, base.agentRun)); } const partialContext = partialAgentRunContext(runnerTrace); const freshSessionRequired = !canceled && agentRunFailureRequiresFreshSession(code, message); const failedSessionStatus = freshSessionRequired ? "thread-resume-failed" : "failed"; const resumableAfterFailure = !canceled && !freshSessionRequired && Boolean(agentRunEffectiveThreadId(base)); return { ...base, status: canceled ? "canceled" : "failed", messageId: finalResponse.messageId, threadId: agentRunEffectiveThreadId(base), canceled, updatedAt: now, session: agentRunSessionSummary(base, canceled ? "canceled" : failedSessionStatus), sessionReuse: agentRunSessionReuseSummary(base, base.agentRun.reused === true, { status: canceled ? undefined : freshSessionRequired ? "thread-resume-failed" : resumableAfterFailure ? "failed-resumable" : "failed-requires-new-session" }), 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, finalResponse, traceSummary, 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, reuseEligible: resumableAfterFailure, providerTrace, valuesPrinted: false }, reuseEligible: resumableAfterFailure, ...(partialContext ? { partialContext } : {}), valuesPrinted: false }; } export function agentRunCompletedFinalResponse({ base, result, traceId, now, replyText = null }) { const textValue = messageAuthorityTextValue(replyText) || agentRunReplyText(result); return { text: textValue, textChars: textValue.length, role: "assistant", status: "completed", traceId, messageId: base.messageId ?? base.reply?.messageId ?? `msg_${traceId.slice(4)}`, createdAt: base.reply?.createdAt ?? base.createdAt ?? now, updatedAt: now, valuesPrinted: false }; } export function agentRunTraceTerminalAssistantText(runnerTrace = {}) { const events = Array.isArray(runnerTrace?.events) ? runnerTrace.events : []; for (let index = events.length - 1; index >= 0; index -= 1) { const event = events[index]; if (!event || typeof event !== "object") continue; if (!isAgentRunAssistantTraceEvent(event)) continue; if (!(event.terminal === true || event.final === true || event.replyAuthority === true)) continue; const textValue = messageAuthorityTextValue(event.finalResponse ?? event.text ?? event.content ?? event.message ?? event.summary ?? event.payload?.text ?? event.payload?.content ?? event.payload?.message); if (textValue) return textValue; } return ""; } export function isAgentRunAssistantTraceEvent(event = {}) { const type = String(event.type ?? event.eventType ?? "").trim().toLowerCase(); if (type === "assistant" || type === "assistant_message") return true; return /assistant:message|assistant_message/u.test(String(event.label ?? "").toLowerCase()); } export function agentRunTerminalFailureFinalResponse({ base, traceId, now, status, text }) { const textValue = String(text ?? "").trim(); return { text: textValue || "AgentRun command failed.", textChars: (textValue || "AgentRun command failed.").length, role: "assistant", status, traceId, messageId: base.messageId ?? base.reply?.messageId ?? `msg_${traceId.slice(4)}`, createdAt: base.reply?.createdAt ?? base.createdAt ?? now, updatedAt: now, valuesPrinted: false }; } export function agentRunTerminalFailureTraceSummary({ base, runnerTrace, finalResponse, traceId, terminalStatus }) { const events = Array.isArray(runnerTrace?.events) ? runnerTrace.events : []; const lastSeq = Math.max(0, ...events.map((event) => Number(event?.seq ?? 0)).filter(Number.isFinite)); return { traceId, source: "agentrun-command-result", sourceEventCount: Number(runnerTrace?.eventCount ?? events.length ?? 0), terminalStatus, finalAssistantRow: { role: finalResponse.role, status: finalResponse.status, textChars: finalResponse.textChars, textPreview: finalResponse.text.slice(0, 240), messageId: finalResponse.messageId, valuesPrinted: false }, agentRun: { runId: base.agentRun?.runId ?? null, commandId: base.agentRun?.commandId ?? null, lastSeq, valuesPrinted: false }, valuesPrinted: false }; } export function agentRunCompletedTraceSummary({ base, runnerTrace, finalResponse, traceId }) { const events = Array.isArray(runnerTrace?.events) ? runnerTrace.events : []; const lastSeq = Math.max(0, ...events.map((event) => Number(event?.seq ?? 0)).filter(Number.isFinite)); return { traceId, source: "agentrun-command-result", sourceEventCount: Number(runnerTrace?.eventCount ?? events.length ?? 0), terminalStatus: "completed", finalAssistantRow: finalResponse ? { role: finalResponse.role, status: finalResponse.status, textChars: finalResponse.textChars, textPreview: finalResponse.text.slice(0, 240), messageId: finalResponse.messageId, valuesPrinted: false } : null, agentRun: { runId: base.agentRun?.runId ?? null, commandId: base.agentRun?.commandId ?? null, lastSeq, valuesPrinted: false }, valuesPrinted: false }; } export 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 }; } export function agentRunResultTraceCreatedAt(runnerTrace = {}, fallback) { const last = String(runnerTrace?.lastEvent?.createdAt ?? runnerTrace?.updatedAt ?? ""); return Number.isFinite(Date.parse(last)) ? last : fallback; } export function decorateAgentRunRunningResult({ base, mapping, traceStore, traceId }) { return { ...base, status: "running", threadId: agentRunEffectiveThreadId({ agentRun: mapping }), 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 }; } export 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 agentRunEventCommandId(event) { const payload = event?.payload && typeof event.payload === "object" ? event.payload : {}; if (typeof event?.commandId === "string") return event.commandId; return typeof payload.commandId === "string" ? payload.commandId : ""; } export function isForeignAgentRunCommandEvent(event, mapping = {}) { const currentCommandId = typeof mapping.commandId === "string" ? mapping.commandId : ""; if (!currentCommandId) return false; const eventCommandId = agentRunEventCommandId(event); return Boolean(eventCommandId && eventCommandId !== currentCommandId); } export 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, backend: backendForBackendProfile(mapping.backendProfile ?? payload.backendProfile ?? payload.providerProfile ?? "deepseek"), 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", eventType: "backend", status: "running", label: `agentrun:backend:${phase}`, message: textPayload(payload, phase), waitingFor: waitingForForPhase(phase), details: agentRunBackendStatusDetails(phase, payload) }; } if (event.type === "assistant_message") { const terminal = payload.replyAuthority === true || payload.final === true; return { ...base, type: "assistant", eventType: "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; const preview = agentRunToolCallPreviewSummary(payload); const toolName = firstNonEmpty( payload.toolName, payload.name, payload.item?.type, payload.type, preview.toolName, preview.name, preview.type, isProtocolToolLifecycleMethod(payload.method) ? null : payload.method, "tool call" ); const command = firstNonEmpty(payload.command, payload.commandLine, payload.item?.command, payload.item?.commandLine, preview.command, preview.commandLine); const output = firstNonEmpty(payload.outputSummary, payload.stdoutSummary, preview.outputSummary, preview.stdoutSummary, payload.summary?.text, payload.itemPreview); return { ...base, type: "tool_call", eventType: "tool_call", status: String(payload.status ?? preview.status ?? (payload.method === "item/completed" ? "completed" : "running")), label: `agentrun:tool:${String(toolName)}`, itemId: payload.item?.id ?? payload.itemId ?? preview.itemId ?? null, toolName, command, exitCode: Number.isInteger(payload.item?.exitCode) ? payload.item.exitCode : Number.isInteger(payload.exitCode) ? payload.exitCode : undefined, durationMs: typeof payload.item?.durationMs === "number" ? payload.item.durationMs : typeof payload.durationMs === "number" ? payload.durationMs : undefined, stdoutSummary: output, outputSummary: output, message: output || command || 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", eventType: "status", status: "running", label: `agentrun:output:${stream}`, stream, message: textPayload(payload, ""), outputTruncated: payload.outputTruncated === true }; } if (event.type === "diff") { return { ...base, type: "diff", eventType: "status", status: "running", label: "agentrun:diff", message: textPayload(payload, "diff") }; } if (event.type === "error") { const failureKind = normalizedFailureKind(payload.failureKind ?? payload.errorCode ?? "backend") ?? "backend"; const normalizedPayload = { ...payload, failureKind, errorCode: failureKind }; if (isRetryableProviderInterruptionPayload(normalizedPayload, { activeError: true })) return agentRunProviderRetryEvent({ base, payload: { ...normalizedPayload, willRetry: true }, status: "retrying", label: `agentrun:provider-retry:${failureKind}` }); return { ...base, type: "error", eventType: "error", status: "failed", label: `agentrun:error:${failureKind}`, errorCode: failureKind, failureKind, willRetry: payload.willRetry === true ? true : undefined, message: textPayload(payload, "AgentRun error") }; } if (event.type === "terminal_status") { const terminalStatus = String(payload.terminalStatus ?? event.terminalStatus ?? payload.status ?? event.status ?? "failed"); const failureKind = normalizedFailureKind(payload.failureKind ?? payload.errorCode ?? event.failureKind ?? event.errorCode) ?? null; const normalizedPayload = failureKind ? { ...payload, failureKind, errorCode: failureKind } : payload; if (isRetryableProviderInterruptionPayload(normalizedPayload)) return agentRunProviderRetryEvent({ base, payload: normalizedPayload, status: "retrying", label: `agentrun:provider-retry:${terminalStatus}` }); return { ...base, type: "result", eventType: "terminal", status: terminalStatus === "cancelled" ? "canceled" : terminalStatus, label: `agentrun:terminal:${terminalStatus}`, errorCode: failureKind, failureKind, willRetry: payload.willRetry === true || event.willRetry === true ? true : undefined, message: textPayload({ ...event, ...payload }, `AgentRun terminal status ${terminalStatus}`), terminal: true }; } return { ...base, type: "backend", eventType: "backend", status: "running", label: `agentrun:event:${String(event.type ?? "unknown")}`, message: textPayload(payload, "AgentRun event") }; } export function agentRunProviderRetryEvent({ base = {}, payload = {}, status = "retrying", label = "agentrun:provider-retry" } = {}) { const failureKind = normalizedFailureKind(payload.failureKind ?? payload.errorCode ?? "provider-stream-disconnected"); const retryMessage = providerRetryMessage(textPayload(payload, "Provider stream disconnected; AgentRun will retry."), payload); return { ...base, type: "provider_retry", eventType: "backend", status, label, errorCode: failureKind, failureKind, willRetry: payload.willRetry === true, retryable: true, transient: true, terminal: false, retryAttempt: numberOrNull(payload.retryAttempt), retryMax: numberOrNull(payload.retryMax), retryDelayMs: numberOrNull(payload.retryDelayMs), nextRetryAt: firstNonEmpty(payload.nextRetryAt) || null, retryOf: payload.retryOf ?? null, terminalStatus: payload.terminalStatus ?? null, message: retryMessage, valuesPrinted: false }; } export function providerRetryMessage(message, payload = {}) { const attempt = numberOrNull(payload.retryAttempt); const max = numberOrNull(payload.retryMax); const delayMs = numberOrNull(payload.retryDelayMs); const nextRetryAt = firstNonEmpty(payload.nextRetryAt) || null; const parts = []; if (attempt !== null && max !== null) parts.push(`retry ${attempt}/${max}`); else if (attempt !== null) parts.push(`retry attempt ${attempt}`); else if (max !== null) parts.push(`retry max ${max}`); if (delayMs !== null) parts.push(`after ${delayMs}ms`); if (nextRetryAt) parts.push(`next=${nextRetryAt}`); if (parts.length === 0) return message; const text = String(message || "Provider stream disconnected; AgentRun will retry.").trim(); if (attempt !== null && max !== null && text.includes(`${attempt}/${max}`)) return text; return text.replace(/[.。]\s*$/u, "") + `; ${parts.join(" ")}.`; } export function isRetryableProviderInterruptionPayload(payload = {}, options = {}) { const kind = normalizedFailureKind(payload?.failureKind ?? payload?.errorCode ?? payload?.code); if (kind === "provider-stream-disconnected") return payload?.willRetry === true; if (kind === "provider-unavailable") return payload?.willRetry === true || (options.activeError === true && payload?.terminal !== true && payload?.final !== true); if (isRetryableAgentRunTransportFailureKind(kind)) return payload?.willRetry === true; return false; } export function normalizedFailureKind(value) { const normalized = String(value ?? "").trim().toLowerCase().replace(/_/gu, "-") || null; if (normalized === "agentrun-unreachable") return "agentrun-connect-failed"; return normalized; } export function numberOrNull(value) { const number = Number(value); return Number.isFinite(number) ? number : null; } export function agentRunTraceEvent(event = {}, backendSource = {}) { const source = backendSource && typeof backendSource === "object" ? backendSource : { backendProfile: backendSource }; const backendProfile = firstNonEmpty(event.backendProfile, source.backendProfile, source.providerProfile, source.agentRun?.backendProfile); return { ...event, eventType: event.eventType ?? agentRunManualEventType(event), backend: event.backend ?? (backendProfile ? backendForBackendProfile(backendProfile) : null), backendProfile: event.backendProfile ?? backendProfile ?? null, valuesPrinted: event.valuesPrinted ?? false }; } export function agentRunManualEventType(event = {}) { const type = String(event.type ?? "").trim().toLowerCase(); if (type === "request") return "request"; if (type === "cancel" || type === "result") return "terminal"; if (type === "assistant") return "assistant"; if (type === "tool" || type === "tool_call") return "tool_call"; if (type === "error") return "error"; if (type === "backend") return "backend"; const raw = String(`${event.label ?? ""} ${event.status ?? ""}`).toLowerCase(); if (/request|accepted/u.test(raw)) return "request"; if (/cancel|terminal|complete|final/u.test(raw)) return "terminal"; if (/assistant|message|reply/u.test(raw)) return "assistant"; if (/error|fail|timeout/u.test(raw)) return "error"; if (/backend|runner|agentrun/u.test(raw)) return "backend"; return "status"; } export function isProtocolToolLifecycleMethod(value) { return /^item\/(started|completed)$/u.test(String(value ?? "")); } export function agentRunToolCallPreviewSummary(payload = {}) { const text = firstNonEmpty(payload.itemPreview, payload.summary?.text); return { itemId: jsonPreviewStringField(text, "itemId"), type: jsonPreviewStringField(text, "type"), toolName: jsonPreviewStringField(text, "toolName"), name: jsonPreviewStringField(text, "name"), status: jsonPreviewStringField(text, "status"), command: jsonPreviewStringField(text, "command"), commandLine: jsonPreviewStringField(text, "commandLine"), outputSummary: jsonPreviewStringField(text, "outputSummary"), stdoutSummary: jsonPreviewStringField(text, "stdoutSummary") }; } export function jsonPreviewStringField(text, key) { const body = String(text ?? ""); if (!body || !/^[A-Za-z][A-Za-z0-9_]*$/u.test(key)) return null; const match = body.match(new RegExp(`"${key}"\\s*:\\s*"((?:\\\\.|[^"\\\\])*)"`, "u")); if (!match) return null; try { const parsed = JSON.parse(`"${match[1]}"`); return typeof parsed === "string" && parsed.trim() ? parsed.trim() : null; } catch { return match[1]?.trim() || null; } } export function agentRunBackendStatusDetails(phase, payload = {}) { if (phase === "initial-prompt-assembly") { return compactObject({ initialPromptInjected: payload.initialPromptInjected === true, reason: firstNonEmpty(payload.reason), initialPrompt: agentRunResourceSummary(payload.initialPrompt), valuesPrinted: false }); } if (phase === "resource-bundle-materialized") { return compactObject({ kind: firstNonEmpty(payload.kind), commitId: firstNonEmpty(payload.commitId), bundles: agentRunResourceSummary(payload.bundles), tools: agentRunResourceSummary(payload.tools), promptRefs: agentRunResourceSummary(payload.promptRefs), skillDirs: agentRunResourceSummary(payload.skillDirs), initialPrompt: agentRunResourceSummary(payload.initialPrompt), valuesPrinted: false }); } return undefined; } export function agentRunResourceSummary(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return undefined; return compactObject({ available: typeof value.available === "boolean" ? value.available : undefined, count: Number.isInteger(value.count) ? value.count : undefined, bytes: Number.isInteger(value.bytes) ? value.bytes : undefined, sha256: firstNonEmpty(value.sha256), promptRefCount: Number.isInteger(value.promptRefCount) ? value.promptRefCount : undefined, skillCount: Number.isInteger(value.skillCount) ? value.skillCount : undefined, toolCount: Number.isInteger(value.toolCount) ? value.toolCount : undefined, names: Array.isArray(value.names) ? value.names.map((item) => firstNonEmpty(item)).filter(Boolean).slice(0, 20) : undefined, targetPaths: Array.isArray(value.targetPaths) ? value.targetPaths.map((item) => firstNonEmpty(item)).filter(Boolean).slice(0, 20) : undefined, skillNames: Array.isArray(value.skillNames) ? value.skillNames.map((item) => firstNonEmpty(item)).filter(Boolean).slice(0, 20) : undefined, requiredMissing: Array.isArray(value.requiredMissing) ? value.requiredMissing.map((item) => firstNonEmpty(item)).filter(Boolean).slice(0, 20) : undefined, valuesPrinted: false }); } export function compactObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return undefined; const result = {}; for (const [key, item] of Object.entries(value)) { if (item === undefined || item === null) continue; if (Array.isArray(item) && item.length === 0) continue; if (typeof item === "object" && !Array.isArray(item) && Object.keys(item).length === 0) continue; result[key] = item; } return Object.keys(result).length > 0 ? result : undefined; } export 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") }; } export function nowMs() { if (typeof performance !== "undefined" && typeof performance.now === "function") return performance.now(); return Date.now(); } export function agentRunMapping({ env, managerUrl, backendProfile, run, command, dispatchIntent, traceId, startedAt, params = {} }) { if (dispatchIntent?.durable !== true) { throw new Error("AgentRun mapping requires a confirmed durable dispatch intent."); } const threadReused = Boolean(safeOpaqueId(params.threadId)); return { adapter: ADAPTER_ID, managerUrl, backendProfile, providerId: resolveAgentRunProviderId(env), runId: run.id, commandId: command.id, attemptId: null, runnerId: null, runnerJobId: dispatchIntent.runnerJobId, jobName: null, namespace: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE, env.AGENTRUN_RUNTIME_NAMESPACE, DEFAULT_RUNNER_NAMESPACE), status: `dispatch-intent-${dispatchIntent.state}`, dispatchIntentId: dispatchIntent.id, dispatchIntentState: dispatchIntent.state, durableDispatch: true, runStatus: run.status ?? null, commandState: command.state ?? null, terminalStatus: null, lastSeq: 0, traceId, ...codeAgentOtelTraceFields(traceId, env), sessionId: run?.sessionRef?.sessionId ?? null, conversationId: run?.sessionRef?.conversationId ?? null, threadId: run?.sessionRef?.threadId ?? null, runnerReused: false, runnerJobCount: dispatchIntent.state === "dispatched" ? 1 : 0, threadReused, persistentResume: threadReused, reused: false, reuseEligible: true, createdAt: startedAt, updatedAt: nowIso(), valuesPrinted: false }; } export function agentRunReusedMapping({ previous = {}, run = {}, command = {}, traceId, startedAt, backendProfile, managerUrl, env }) { const threadReused = Boolean(safeOpaqueId(run?.sessionRef?.threadId ?? previous.threadId)); return { ...previous, adapter: ADAPTER_ID, managerUrl, backendProfile, providerId: previous.providerId ?? resolveAgentRunProviderId(env), runId: previous.runId ?? run?.id, commandId: command.id, attemptId: previous.attemptId ?? null, runnerId: previous.runnerId ?? null, runnerJobId: previous.runnerJobId ?? null, jobName: previous.jobName ?? null, namespace: previous.namespace ?? DEFAULT_RUNNER_NAMESPACE, status: "runner-job-reused", runStatus: run?.status ?? previous.runStatus ?? null, commandState: command.state ?? null, terminalStatus: null, lastSeq: previous.lastSeq ?? 0, runnerJobCount: Number(previous.runnerJobCount ?? 0), traceId, sessionId: run?.sessionRef?.sessionId ?? previous.sessionId ?? null, conversationId: run?.sessionRef?.conversationId ?? previous.conversationId ?? null, threadId: run?.sessionRef?.threadId ?? previous.threadId ?? null, runnerReused: true, threadReused, persistentResume: threadReused, reused: true, reuseEligible: true, createdAt: previous.createdAt ?? startedAt, updatedAt: nowIso(), valuesPrinted: false }; } export 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; } export function withAgentRunRunnerJobCount(mapping = {}) { if (!mapping || typeof mapping !== "object") return mapping; const count = Number(mapping.runnerJobCount); if (Number.isFinite(count) && count >= 0) return mapping; const runnerJobEnsured = mapping.reused === true || mapping.status === "runner-job-ensured"; if (runnerJobEnsured && (mapping.runnerJobId || mapping.jobName || mapping.attemptId || mapping.runnerId)) { return { ...mapping, runnerJobCount: 1 }; } return mapping; } export function hwlabSessionIdForParams(params = {}) { return safeSessionId(params.sessionId) || null; } export function scopedAgentRunSessionIdForParams(params = {}, traceId) { const baseSessionId = hwlabSessionIdForParams(params, traceId); if (!safeSessionId(baseSessionId)) { throw adapterError( "agentrun_session_id_required", "AgentRun persistent session/PVC requires params.sessionId; refusing to derive sessionRef.sessionId from traceId because that would create a new PVC per turn." ); } const base = String(baseSessionId).replace(/^ses_/u, "").replace(/[^A-Za-z0-9_]+/gu, "_").replace(/^_+|_+$/gu, "") || "session"; return `ses_agentrun_${base}`; } export function agentRunSessionId(traceId) { return `ses_agentrun_${String(safeTraceId(traceId) || `trc_${randomUUID()}`).slice(4)}`; } export function agentRunEffectiveThreadId(base = {}) { const agentRun = base?.agentRun && typeof base.agentRun === "object" ? base.agentRun : null; if (agentRun && Object.hasOwn(agentRun, "threadId")) return safeOpaqueId(agentRun.threadId) || null; return safeOpaqueId(base?.threadId) || null; } export function agentRunSessionSummary(base, status) { return decorateCodeAgentSession({ sessionId: base.sessionId ?? base.agentRun?.sessionId ?? null, conversationId: base.conversationId ?? base.agentRun?.conversationId ?? null, threadId: agentRunEffectiveThreadId(base), status, sessionMode: AGENTRUN_SESSION_MODE, runnerKind: AGENTRUN_RUNNER_KIND, lastTraceId: base.traceId ?? base.agentRun?.traceId ?? null, longLivedSession: true, codexStdio: false, delegatedToAgentRun: true, writeCapable: true, durable: true, durableSession: true, idleTimeoutMs: parsePositiveInteger(base.agentRun?.idleTimeoutMs, 600000), secretMaterialStored: false, valuesRedacted: true }); } export function agentRunThreadReused(base = {}, options = {}) { if (options.status === "failed-requires-new-session") return false; if (base.agentRun?.threadReused === true || base.agentRun?.persistentResume === true) return true; return Boolean(agentRunEffectiveThreadId(base)); } export function agentRunSessionReuseSummary(base, reused, options = {}) { const runnerReused = Boolean(reused); const threadReused = agentRunThreadReused(base, options); const persistentResume = threadReused; const sessionReused = runnerReused || persistentResume; return { sessionId: base.sessionId ?? base.agentRun?.sessionId ?? null, conversationId: base.conversationId ?? base.agentRun?.conversationId ?? null, threadId: agentRunEffectiveThreadId(base), reused: sessionReused, status: options.status ?? (runnerReused ? "reused" : persistentResume ? "thread-resumed" : "new"), runnerReused, threadReused, persistentResume, valuesRedacted: true }; } export function agentRunRunnerSummary(mapping = {}) { return { kind: AGENTRUN_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, dispatchIntentId: mapping.dispatchIntentId ?? null, dispatchIntentState: mapping.dispatchIntentState ?? null, durableDispatch: mapping.durableDispatch === true, backendProfile: mapping.backendProfile ?? null, provider: providerForBackendProfile(mapping.backendProfile ?? "deepseek"), sessionMode: AGENTRUN_SESSION_MODE, implementationType: AGENTRUN_IMPLEMENTATION_TYPE, capabilityLevel: AGENTRUN_CAPABILITY_LEVEL, codexStdio: false, delegatedToAgentRun: true, writeCapable: true, durableSession: true, longLivedSession: true, sandbox: "danger-full-access", valuesPrinted: false }; } export function hostForUrl(value) { if (!value) return null; try { return new URL(value).hostname.toLowerCase(); } catch { return null; } } export 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"; } export 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; } export 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 }); } export function firstNonEmpty(...values) { for (const value of values) { const text = String(value ?? "").trim(); if (text) return text; } return null; } export function nowIso(now) { return typeof now === "function" ? now() : new Date().toISOString(); } export function timestampIsoOrNow(value = null) { const parsed = Date.parse(String(value ?? "")); return Number.isFinite(parsed) ? new Date(parsed).toISOString() : new Date().toISOString(); } export function redactUrl(value) { try { const url = new URL(value); if (url.username || url.password) { url.username = "***"; url.password = ""; } return url.toString(); } catch { return ""; } }