From c112ec9844ff086a683e02f3f1ebfb35a975fab7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 6 Sep 2026 11:35:59 +0100 Subject: [PATCH] chore: update managed runtime pins (#3416) Co-authored-by: kandev-runtime-updater[bot] Co-authored-by: Carlos Florencio --- .../agents/managed_npm_runtime_versions.json | 10 ++-- apps/web/e2e/fixtures/managed-runtime-npx.sh | 9 +-- apps/web/e2e/helpers/api-client.ts | 6 +- .../e2e/helpers/managed-runtime-recovery.ts | 58 ++++++++++++++----- .../managed-runtime-npm-recovery.spec.ts | 7 +-- .../ssh/managed-runtime-npm-recovery.spec.ts | 8 +-- 6 files changed, 65 insertions(+), 33 deletions(-) diff --git a/apps/backend/internal/agent/agents/managed_npm_runtime_versions.json b/apps/backend/internal/agent/agents/managed_npm_runtime_versions.json index 98b07417bc..a05b6d46ae 100644 --- a/apps/backend/internal/agent/agents/managed_npm_runtime_versions.json +++ b/apps/backend/internal/agent/agents/managed_npm_runtime_versions.json @@ -1,7 +1,7 @@ { - "@agentclientprotocol/claude-agent-acp": "0.70.0", - "@agentclientprotocol/codex-acp": "1.6.0", - "@github/copilot": "1.0.75", - "@google/gemini-cli": "0.52.0", - "opencode-ai": "1.18.18" + "@agentclientprotocol/claude-agent-acp": "0.75.1", + "@agentclientprotocol/codex-acp": "1.10.0", + "@github/copilot": "1.0.83", + "@google/gemini-cli": "0.58.0", + "opencode-ai": "1.18.29" } diff --git a/apps/web/e2e/fixtures/managed-runtime-npx.sh b/apps/web/e2e/fixtures/managed-runtime-npx.sh index 90200abd25..93cc7f5634 100644 --- a/apps/web/e2e/fixtures/managed-runtime-npx.sh +++ b/apps/web/e2e/fixtures/managed-runtime-npx.sh @@ -4,15 +4,16 @@ set -eu cache_root=${NPM_CONFIG_CACHE:-${npm_config_cache:-"$HOME/.npm"}} package_spec=${3:-} preference=${2:-} -managed_package_spec=opencode-ai@1.18.18 +managed_package_name=opencode-ai real_npx=/usr/bin/npx # This image replaces npx only to make the selected managed runtime failure # deterministic. Let every other package or invocation use the image's real # npm implementation. -if [ "$package_spec" != "$managed_package_spec" ]; then - exec "$real_npx" "$@" -fi +case "$package_spec" in + "$managed_package_name"@*) ;; + *) exec "$real_npx" "$@" ;; +esac key=$(printf '%s' "$package_spec" | sha512sum | cut -c1-16) target_dir="$cache_root/_npx/$key" diff --git a/apps/web/e2e/helpers/api-client.ts b/apps/web/e2e/helpers/api-client.ts index 36f6ba4368..afc3f4907a 100644 --- a/apps/web/e2e/helpers/api-client.ts +++ b/apps/web/e2e/helpers/api-client.ts @@ -13,7 +13,7 @@ import type { WorkflowProfileSessionEndPolicy, TaskPriority, } from "../../lib/types/http"; -import type { Agent, AgentProfile } from "../../lib/types/http-agents"; +import type { Agent, AgentProfile, AvailableAgent } from "../../lib/types/http-agents"; import { normalizeAgentProfile } from "../../lib/api/domains/agent-profile-normalize"; import type { PRCommitDetail, @@ -562,6 +562,10 @@ export class ApiClient { }; } + async listAvailableAgents(): Promise<{ agents: AvailableAgent[]; total: number }> { + return this.request("GET", "/api/v1/agents/available"); + } + async deleteAgentProfile(profileId: string, force?: boolean): Promise { const qs = force ? "?force=true" : ""; await this.request("DELETE", `/api/v1/agent-profiles/${profileId}${qs}`); diff --git a/apps/web/e2e/helpers/managed-runtime-recovery.ts b/apps/web/e2e/helpers/managed-runtime-recovery.ts index 85a79f57de..dab97678df 100644 --- a/apps/web/e2e/helpers/managed-runtime-recovery.ts +++ b/apps/web/e2e/helpers/managed-runtime-recovery.ts @@ -4,15 +4,18 @@ import type { BackendContext } from "../fixtures/backend"; import type { ApiClient } from "./api-client"; import type { AgentProfile } from "../../lib/types/http-agents"; -export const MANAGED_RUNTIME_PACKAGE_SPEC = "opencode-ai@1.18.18"; export const MANAGED_RUNTIME_CACHE_ROOT = "/tmp/kandev-managed-npm-cache"; +const MANAGED_RUNTIME_AGENT_NAME = "opencode-acp"; -export function managedRuntimeExecutionCacheKey( - packageSpec = MANAGED_RUNTIME_PACKAGE_SPEC, -): string { +export function managedRuntimeExecutionCacheKey(packageSpec: string): string { return createSha512(packageSpec).slice(0, 16); } +export type ManagedRuntimePreparation = { + profile: AgentProfile; + packageSpec: string; +}; + /** * The real managed OpenCode agent is enabled only for this container-backed * test. Its command runs through the image's npx wrapper, while the wrapper @@ -21,23 +24,47 @@ export function managedRuntimeExecutionCacheKey( export async function prepareManagedRuntimeProfile( apiClient: ApiClient, backend: BackendContext, -): Promise { +): Promise { await backend.restart({ KANDEV_MOCK_AGENT: "true" }); let agentId = ""; + let packageSpec = ""; let observedAgents = ""; try { await expect .poll( async () => { - const { agents } = await apiClient.listAgents(); - observedAgents = agents.map((agent) => `${agent.id}:${agent.name}`).join(", "); - agentId = agents.find((agent) => agent.name === "opencode-acp")?.id ?? ""; - return agentId; + const [{ agents: availableAgents }, { agents: persistedAgents }] = await Promise.all([ + apiClient.listAvailableAgents(), + apiClient.listAgents(), + ]); + observedAgents = availableAgents + .map((agent) => { + const runtime = agent.runtime_update; + const version = runtime?.effective_version ? `@${runtime.effective_version}` : ""; + return `${agent.name}:${agent.available ? "available" : "unavailable"}${version}`; + }) + .join(", "); + const managedAgent = availableAgents.find( + (agent) => + agent.name === MANAGED_RUNTIME_AGENT_NAME && + agent.available && + agent.runtime_update?.supported && + agent.runtime_update.package && + agent.runtime_update.effective_version, + ); + const persistedAgent = persistedAgents.find( + (agent) => agent.name === MANAGED_RUNTIME_AGENT_NAME, + ); + agentId = persistedAgent?.id ?? ""; + packageSpec = managedAgent?.runtime_update + ? `${managedAgent.runtime_update.package}@${managedAgent.runtime_update.effective_version}` + : ""; + return agentId && packageSpec ? `${agentId}:${packageSpec}` : ""; }, { timeout: 30_000, - message: "OpenCode managed runtime should be registered for container recovery", + message: "OpenCode managed runtime metadata should be available for container recovery", }, ) .not.toBe(""); @@ -47,10 +74,13 @@ export async function prepareManagedRuntimeProfile( ); } - return apiClient.createAgentProfile(agentId, "E2E managed npm recovery", { - model: "mock-fast", - env_vars: [{ key: "NPM_CONFIG_CACHE", value: MANAGED_RUNTIME_CACHE_ROOT }], - }); + return { + profile: await apiClient.createAgentProfile(agentId, "E2E managed npm recovery", { + model: "mock-fast", + env_vars: [{ key: "NPM_CONFIG_CACHE", value: MANAGED_RUNTIME_CACHE_ROOT }], + }), + packageSpec, + }; } /** Restore the normal e2e-only mock registry after a managed-runtime test. */ diff --git a/apps/web/e2e/tests/docker/managed-runtime-npm-recovery.spec.ts b/apps/web/e2e/tests/docker/managed-runtime-npm-recovery.spec.ts index e2c7d04854..fa9721c51e 100644 --- a/apps/web/e2e/tests/docker/managed-runtime-npm-recovery.spec.ts +++ b/apps/web/e2e/tests/docker/managed-runtime-npm-recovery.spec.ts @@ -3,7 +3,6 @@ import { dockerFileContent, dockerPathExists } from "../../helpers/docker"; import { waitForLatestSessionDone } from "../../helpers/session"; import { MANAGED_RUNTIME_CACHE_ROOT, - MANAGED_RUNTIME_PACKAGE_SPEC, managedRuntimeExecutionCacheKey, prepareManagedRuntimeProfile, restoreE2EAgentRegistry, @@ -20,7 +19,7 @@ test.describe("Docker executor - managed npm runtime recovery", () => { test.setTimeout(240_000); let profileId = ""; try { - const profile = await prepareManagedRuntimeProfile(apiClient, backend); + const { profile, packageSpec } = await prepareManagedRuntimeProfile(apiClient, backend); profileId = profile.id; const task = await apiClient.createTaskWithAgent( seedData.workspaceId, @@ -38,7 +37,7 @@ test.describe("Docker executor - managed npm runtime recovery", () => { await waitForLatestSessionDone(apiClient, task.id, 1, "Wait for Docker managed recovery"); const environment = await apiClient.getTaskEnvironment(task.id); expect(environment?.container_id).toBeTruthy(); - const target = `${MANAGED_RUNTIME_CACHE_ROOT}/_npx/${managedRuntimeExecutionCacheKey()}`; + const target = `${MANAGED_RUNTIME_CACHE_ROOT}/_npx/${managedRuntimeExecutionCacheKey(packageSpec)}`; const sibling = `${MANAGED_RUNTIME_CACHE_ROOT}/_npx/0123456789abcdef`; const containerID = environment!.container_id!; expect(dockerPathExists(containerID, `${target}/stale-marker`)).toBe(false); @@ -48,7 +47,7 @@ test.describe("Docker executor - managed npm runtime recovery", () => { containerID, `${MANAGED_RUNTIME_CACHE_ROOT}/online-invocations`, ); - expect(onlineInvocations.trim().split(/\r?\n/)).toEqual([MANAGED_RUNTIME_PACKAGE_SPEC]); + expect(onlineInvocations.trim().split(/\r?\n/)).toEqual([packageSpec]); await testPage.goto(`/t/${task.id}`); const session = new SessionPage(testPage); diff --git a/apps/web/e2e/tests/ssh/managed-runtime-npm-recovery.spec.ts b/apps/web/e2e/tests/ssh/managed-runtime-npm-recovery.spec.ts index c22277e0e0..4784ae0a88 100644 --- a/apps/web/e2e/tests/ssh/managed-runtime-npm-recovery.spec.ts +++ b/apps/web/e2e/tests/ssh/managed-runtime-npm-recovery.spec.ts @@ -3,7 +3,6 @@ import { readRemoteFile, remotePathExists } from "../../helpers/ssh"; import { waitForLatestSessionDone } from "../../helpers/session"; import { MANAGED_RUNTIME_CACHE_ROOT, - MANAGED_RUNTIME_PACKAGE_SPEC, managedRuntimeExecutionCacheKey, prepareManagedRuntimeProfile, restoreE2EAgentRegistry, @@ -20,7 +19,7 @@ test.describe("SSH executor - managed npm runtime recovery", () => { test.setTimeout(240_000); let profileId = ""; try { - const profile = await prepareManagedRuntimeProfile(apiClient, backend); + const { profile, packageSpec } = await prepareManagedRuntimeProfile(apiClient, backend); profileId = profile.id; const task = await apiClient.createTaskWithAgent( seedData.workspaceId, @@ -40,7 +39,7 @@ test.describe("SSH executor - managed npm runtime recovery", () => { (candidate) => candidate.task_id === task.id, ); expect(session?.remote_task_dir).toBeTruthy(); - const target = `${MANAGED_RUNTIME_CACHE_ROOT}/_npx/${managedRuntimeExecutionCacheKey()}`; + const target = `${MANAGED_RUNTIME_CACHE_ROOT}/_npx/${managedRuntimeExecutionCacheKey(packageSpec)}`; const sibling = `${MANAGED_RUNTIME_CACHE_ROOT}/_npx/0123456789abcdef`; expect(remotePathExists(seedData.sshTarget, `${target}/stale-marker`)).toBe(false); expect(readRemoteFile(seedData.sshTarget, `${target}/fresh-marker`)).toBe("fresh\n"); @@ -49,8 +48,7 @@ test.describe("SSH executor - managed npm runtime recovery", () => { seedData.sshTarget, `${MANAGED_RUNTIME_CACHE_ROOT}/online-invocations`, ); - expect(onlineInvocations.trim().split(/\r?\n/)).toEqual([MANAGED_RUNTIME_PACKAGE_SPEC]); - expect(MANAGED_RUNTIME_PACKAGE_SPEC).toBe("opencode-ai@1.18.18"); + expect(onlineInvocations.trim().split(/\r?\n/)).toEqual([packageSpec]); await testPage.goto(`/t/${task.id}`); const page = new SessionPage(testPage);