diff --git a/internal/cloud/code-agent-agentrun-adapter.ts b/internal/cloud/code-agent-agentrun-adapter.ts index 8380499c..7463d4de 100644 --- a/internal/cloud/code-agent-agentrun-adapter.ts +++ b/internal/cloud/code-agent-agentrun-adapter.ts @@ -2,17 +2,6 @@ import { randomUUID } from "node:crypto"; import { defaultCodeAgentTraceStore } from "./code-agent-trace-store.ts"; import { decorateCodeAgentSession } from "./code-agent-session-lifecycle.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, @@ -31,9 +20,13 @@ 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_BACKEND_PREFIX = ADAPTER_ID; 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 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"; @@ -82,7 +75,7 @@ export function describeAgentRunAdapterAvailability(env = process.env, options = const managerHost = hostForUrl(managerUrl); const repoHost = hostForUrl(repoUrl); const status = ready ? "agentrun-ready" : "blocked"; - const capabilityLevel = ready ? CODEX_STDIO_CAPABILITY_LEVEL : "blocked"; + const capabilityLevel = ready ? AGENTRUN_CAPABILITY_LEVEL : "blocked"; return { endpoint: "POST /v1/agent/chat", @@ -224,13 +217,13 @@ export function initialAgentRunChatResult({ params = {}, options = {}, traceId } messageId: `msg_${safeTraceId(traceId)?.slice(4) || randomUUID()}`, createdAt: timestamp, updatedAt: timestamp, - provider: CODEX_STDIO_PROVIDER, + provider: providerForBackendProfile(backendProfile), model: modelForBackendProfile(backendProfile, env), - backend: CODEX_STDIO_BACKEND, + backend: backendForBackendProfile(backendProfile), infrastructureBackend: `agentrun-v01/${backendProfile}`, - capabilityLevel: CODEX_STDIO_CAPABILITY_LEVEL, - sessionMode: CODEX_STDIO_SESSION_MODE, - implementationType: CODEX_STDIO_IMPLEMENTATION_TYPE, + capabilityLevel: AGENTRUN_CAPABILITY_LEVEL, + sessionMode: AGENTRUN_SESSION_MODE, + implementationType: AGENTRUN_IMPLEMENTATION_TYPE, threadContinuityPolicy: THREAD_CONTINUITY_POLICY, agentRun: { adapter: ADAPTER_ID, @@ -623,13 +616,13 @@ export async function loadPersistedAgentRunResult(traceId, options = {}) { createdAt: session.startedAt ?? session.updatedAt ?? nowIso(options.now), updatedAt: session.updatedAt ?? nowIso(options.now), ownerUserId: session.ownerUserId, - provider: CODEX_STDIO_PROVIDER, + provider: providerForBackendProfile(agentRun.backendProfile ?? "deepseek"), model: modelForBackendProfile(agentRun.backendProfile, options.env ?? process.env), - backend: CODEX_STDIO_BACKEND, + backend: backendForBackendProfile(agentRun.backendProfile ?? "deepseek"), infrastructureBackend: `agentrun-v01/${agentRun.backendProfile ?? "deepseek"}`, - capabilityLevel: CODEX_STDIO_CAPABILITY_LEVEL, - sessionMode: CODEX_STDIO_SESSION_MODE, - implementationType: CODEX_STDIO_IMPLEMENTATION_TYPE, + capabilityLevel: AGENTRUN_CAPABILITY_LEVEL, + sessionMode: AGENTRUN_SESSION_MODE, + implementationType: AGENTRUN_IMPLEMENTATION_TYPE, finalResponse: session.session?.finalResponse ?? null, traceSummary: session.session?.traceSummary ?? null, agentRun: { ...agentRun, adapter: ADAPTER_ID, valuesPrinted: false }, @@ -1025,12 +1018,12 @@ function providerCredentialSecretRef(profile, env) { 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://", + 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, @@ -1062,15 +1055,15 @@ function agentRunFailureAttribution({ code, message, canceled = false } = {}) { 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." + userMessage: "AgentRun/provider 返回了无效 tool-call arguments JSON;这不是 HWLAB device-pod 或 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 复用的 Codex thread 已失效;当前 turn 会按失败终止,Workbench 会清理 stale continuation 指针,下一轮自动从新 session/thread 启动。", + userMessage: "AgentRun 复用的 thread 已失效;当前 turn 会按失败终止,Workbench 会清理 stale continuation 指针,下一轮自动从新 session/thread 启动。", summary: message || "AgentRun thread/resume failed for an existing thread." }; } @@ -1083,24 +1076,29 @@ function agentRunFailureAttribution({ code, message, canceled = false } = {}) { } 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 - } - }); + 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 + }; } function agentRunToolCalls(result = {}, status = "completed") { return [{ - name: "codex-app-server.turn", + name: "agentrun.v01.command.turn", status, runId: result?.runId ?? null, commandId: result?.commandId ?? null, @@ -1584,11 +1582,12 @@ function agentRunSessionSummary(base, status) { 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, + sessionMode: AGENTRUN_SESSION_MODE, + runnerKind: AGENTRUN_RUNNER_KIND, lastTraceId: base.traceId ?? base.agentRun?.traceId ?? null, longLivedSession: true, - codexStdio: true, + codexStdio: false, + delegatedToAgentRun: true, writeCapable: true, durable: true, durableSession: true, @@ -1624,7 +1623,7 @@ function agentRunSessionReuseSummary(base, reused, options = {}) { function agentRunRunnerSummary(mapping = {}) { return { - kind: CODEX_STDIO_RUNNER_KIND, + kind: AGENTRUN_RUNNER_KIND, adapter: ADAPTER_ID, runId: mapping.runId ?? null, commandId: mapping.commandId ?? null, @@ -1633,11 +1632,12 @@ function agentRunRunnerSummary(mapping = {}) { 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, + 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, @@ -1648,15 +1648,19 @@ function agentRunRunnerSummary(mapping = {}) { function agentRunTraceMeta(env = process.env) { return { - runnerKind: CODEX_STDIO_RUNNER_KIND, + runnerKind: AGENTRUN_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 + protocol: AGENTRUN_PROVIDER_TRACE_PROTOCOL, + sessionMode: AGENTRUN_SESSION_MODE, + implementationType: AGENTRUN_IMPLEMENTATION_TYPE }; } +function backendForBackendProfile(profile) { + return `${AGENTRUN_BACKEND_PREFIX}/${resolveAgentRunBackendProfile({}, { providerProfile: profile })}`; +} + 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); diff --git a/internal/cloud/server-agent-chat.test.ts b/internal/cloud/server-agent-chat.test.ts index 11c8076d..33c0e336 100644 --- a/internal/cloud/server-agent-chat.test.ts +++ b/internal/cloud/server-agent-chat.test.ts @@ -437,15 +437,32 @@ test("cloud api /v1/agent/chat delegates v0.2 turns to AgentRun v0.1 over adapte const payload = await pollAgentResult(port, traceId); validateCodeAgentChatSchema(payload); assert.equal(payload.status, "completed"); - assert.equal(payload.provider, "codex-stdio"); + assert.equal(payload.provider, "deepseek"); + assert.equal(payload.backend, "agentrun-v01/deepseek"); assert.equal(payload.infrastructureBackend, "agentrun-v01/deepseek"); - assert.equal(payload.providerTrace.protocol, "codex-app-server-jsonrpc-stdio"); - assert.equal(payload.providerTrace.command, "codex app-server --listen stdio://"); + assert.equal(payload.capabilityLevel, "agentrun-v01-shared-code-agent-session"); + assert.equal(payload.sessionMode, "agentrun-v01-durable-session"); + assert.equal(payload.implementationType, "agentrun-v01-shared-execution-infra"); + assert.equal(payload.runner.kind, "agentrun-v01-shared-runner"); + assert.equal(payload.runner.provider, "deepseek"); + assert.equal(payload.runner.codexStdio, false); + assert.equal(payload.runner.delegatedToAgentRun, true); + assert.equal(payload.providerTrace.protocol, "agentrun-v01-jsonrpc"); + assert.equal(payload.providerTrace.command, "agentrun.v01.command.turn"); + assert.equal(payload.providerTrace.runnerKind, "agentrun-v01-shared-runner"); assert.equal(payload.providerTrace.terminalStatus, "completed"); assert.equal(payload.longLivedSessionGate.status, "pass"); + assert.equal(payload.longLivedSessionGate.provider, "deepseek"); + assert.equal(payload.longLivedSessionGate.codexStdio, false); + assert.equal(payload.longLivedSessionGate.delegatedToAgentRun, true); assert.equal(payload.agentRun.runId, "run_hwlab_adapter"); assert.equal(payload.agentRun.commandId, "cmd_hwlab_adapter"); assert.equal(payload.agentRun.jobName, "agentrun-v01-runner-hwlab-adapter"); + const serializedPayload = JSON.stringify(payload); + assert.equal(serializedPayload.includes("repo-owned-codex"), false); + assert.equal(serializedPayload.includes("codex-app-server-stdio"), false); + assert.equal(serializedPayload.includes("\"provider\":\"codex-stdio\""), false); + assert.equal(serializedPayload.includes("\"codexStdio\":true"), false); assert.match(payload.reply.content, /AgentRun adapter/u); assert.ok(payload.runnerTrace.events.some((event) => event.label === "agentrun:backend:runner-job-created")); const bundleEvent = payload.runnerTrace.events.find((event) => event.label === "agentrun:backend:resource-bundle-materialized"); @@ -560,6 +577,10 @@ test("cloud api /v1/agent/chat delegates v0.2 turns to AgentRun v0.1 over adapte const secondPayload = await pollAgentResult(port, secondTraceId); validateCodeAgentChatSchema(secondPayload); assert.equal(secondPayload.status, "completed"); + assert.equal(secondPayload.provider, "deepseek"); + assert.equal(secondPayload.backend, "agentrun-v01/deepseek"); + assert.equal(secondPayload.runner.kind, "agentrun-v01-shared-runner"); + assert.equal(secondPayload.runner.codexStdio, false); assert.equal(secondPayload.agentRun.runId, "run_hwlab_adapter"); assert.equal(secondPayload.agentRun.commandId, "cmd_hwlab_adapter_second"); assert.equal(secondPayload.providerTrace.commandId, "cmd_hwlab_adapter_second"); @@ -889,6 +910,7 @@ test("cloud api AgentRun adapter exposes invalid tool-call attribution in result assert.equal(payload.error.code, "provider-invalid-tool-call"); assert.equal(payload.error.category, "provider_invalid_tool_call"); assert.match(payload.error.userMessage, /无效 tool-call arguments JSON/u); + assert.match(payload.error.userMessage, /AgentRun\/provider/u); assert.match(payload.error.userMessage, /不是 HWLAB device-pod/u); assert.equal(payload.blocker.category, "provider_invalid_tool_call"); assert.match(payload.blocker.summary, /invalid function arguments json string/u); @@ -897,6 +919,10 @@ test("cloud api AgentRun adapter exposes invalid tool-call attribution in result assert.equal(payload.sessionReuse.threadId, "thread_invalid_tool"); assert.equal(payload.agentRun.reuseEligible, false); assert.equal(payload.reuseEligible, false); + const serializedPayload = JSON.stringify(payload); + assert.equal(serializedPayload.includes("repo-owned-codex"), false); + assert.equal(serializedPayload.includes("codex-app-server-stdio"), false); + assert.equal(serializedPayload.includes("\"provider\":\"codex-stdio\""), false); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve()))); @@ -1031,7 +1057,12 @@ test("cloud api AgentRun adapter maps minimax-m3 provider profile to AgentRun ba const payload = await pollAgentResult(port, traceId); validateCodeAgentChatSchema(payload); assert.equal(payload.status, "completed"); + assert.equal(payload.provider, "minimax-m3"); + assert.equal(payload.backend, "agentrun-v01/minimax-m3"); assert.equal(payload.infrastructureBackend, "agentrun-v01/minimax-m3"); + assert.equal(payload.runner.kind, "agentrun-v01-shared-runner"); + assert.equal(payload.runner.provider, "minimax-m3"); + assert.equal(payload.runner.codexStdio, false); assert.equal(payload.agentRun.backendProfile, "minimax-m3"); assert.equal(payload.sessionId, "ses_server-test-minimax-m3"); assert.equal(payload.agentRun.sessionId, "ses_agentrun_minimax_m3_server_test_minimax_m3");