149 lines
8.3 KiB
Markdown
149 lines
8.3 KiB
Markdown
# ADR-2026-08-12-validated-managed-runtime-version-selection: Validate and Persist Managed Runtime Version Selection
|
|
|
|
**Status:** accepted (amended 2026-08-21)
|
|
**Date:** 2026-08-12
|
|
**Area:** backend, frontend, protocol, workflow
|
|
**Supersedes:**
|
|
[ADR-2026-07-26-user-managed-agent-runtime-updates](2026-07-26-user-managed-agent-runtime-updates.md)
|
|
|
|
## Context
|
|
|
|
Kandev originally invoked managed npm ACP runtimes by an unversioned package
|
|
name. The Settings action could prepare the current npm `latest` target, but it
|
|
could not select an older version. This made recovery impossible when an npm
|
|
release was only partly published, platform artifacts were missing, or the
|
|
latest runtime failed ACP initialization. Clearing the matching `_npx` tree did
|
|
not help because the next unversioned invocation selected the same broken
|
|
release.
|
|
|
|
The recovery surface must not turn into an arbitrary package installer. Package
|
|
identity, registry location, and ACP arguments are trusted Kandev metadata.
|
|
Users need to choose among valid versions of that package, validate the
|
|
candidate, and keep a known-good version across Kandev restarts.
|
|
|
|
Persisting a selection only after a manual update still leaves every fresh
|
|
installation and every unselected agent on an unversioned `npx <package>`
|
|
command. Removing npm from the launch path would require Kandev-owned or
|
|
user-installed runtime artifacts and is not a requirement for this iteration.
|
|
The immediate invariant is narrower: every managed npm ACP command names an
|
|
exact top-level package version, while npm remains responsible for cache and
|
|
network behavior.
|
|
|
|
## Decision
|
|
|
|
Kandev lets an operator select an exact stable version for a built-in managed
|
|
npm runtime. The backend obtains the version catalogue from npm metadata,
|
|
validates strict stable SemVer values, and accepts only a version published for
|
|
the trusted package. Callers cannot provide a package name, registry URL, tag,
|
|
prerelease, command argument, or shell text.
|
|
|
|
Kandev prepares the candidate under its exact `package@version` npm execution
|
|
key and probes it through ACP before activation. Candidate preparation or probe
|
|
failure leaves the existing active selection and capability catalogue
|
|
unchanged. Kandev persists the trusted package identity and candidate as the
|
|
install-wide active version only after a successful ACP probe, then publishes
|
|
the candidate capabilities.
|
|
This ordering makes the persisted active version the last known good version;
|
|
automatic silent rollback is unnecessary.
|
|
|
|
Each trusted managed package has an exact default version shipped in Kandev.
|
|
Command resolution uses the persisted operator selection when its package
|
|
identity still matches; otherwise it uses the shipped default. Kandev does not
|
|
copy the default into the database. An installation with no explicit selection
|
|
therefore follows newer defaults when Kandev upgrades, while an operator
|
|
selection remains stable until the operator changes or clears it.
|
|
|
|
The resulting effective version applies to every Kandev-built ACP command for
|
|
that managed package, including capability probes, utility prompts, local
|
|
sessions, containers, and SSH executors. Existing sessions keep their current
|
|
process. Native binaries, passthrough commands, and separately distributed
|
|
authentication helpers remain outside this package selection.
|
|
|
|
The managed package and exact-version override belong only to an agent's ACP
|
|
command surfaces. They do not replace `PassthroughConfig.PassthroughCmd`, an
|
|
interactive authentication helper, or either surface's install recipe. Agents
|
|
such as Pi can therefore use an ACP adapter package for structured execution
|
|
and a separately distributed native CLI for terminal passthrough.
|
|
|
|
The selection is install-wide and per built-in agent. A saved selection applies
|
|
only while its stored package identity matches the agent's current trusted
|
|
package metadata; a package change falls back to the replacement package's
|
|
shipped default. The selection survives backend and browser restarts
|
|
independently of npm's best-effort cache. If reading a saved selection fails,
|
|
Kandev fails the new managed launch or probe visibly instead of bypassing the
|
|
selection. If npm evicts the effective version's cache, npm may prepare that
|
|
same exact version again when needed.
|
|
|
|
Settings checks the registry for each managed package when the Agents page asks
|
|
for update status. The backend caches successful results for a bounded period,
|
|
returns an explicit unknown state when the registry is unavailable, and never
|
|
updates a runtime from this read-only check. The UI marks an available newer
|
|
stable version on the existing update control. Opening the control performs the
|
|
authoritative preview and retains the validated prepare, probe, and activation
|
|
flow.
|
|
|
|
Normal launches retain `--prefer-offline` because an exact package version
|
|
removes version-tag drift while preserving npm cache reuse. Explicit previews,
|
|
manual updates, and the bounded stale-metadata retry use online-preferred
|
|
metadata. Kandev accepts that a cold or incomplete npm cache can still place the
|
|
registry on the launch path.
|
|
|
|
A weekly repository workflow compares the shipped defaults with each trusted
|
|
package's stable npm `latest` tag and opens a reviewable PR for changes. It does
|
|
not merge automatically. The same centralized version catalogue drives command
|
|
construction, tests, and the workflow so runtime pins cannot drift across
|
|
separate constants.
|
|
|
|
## Consequences
|
|
|
|
Operators can recover from a broken latest release in Settings without shell
|
|
access or a Kandev restart. Runtime changes are attributable: every managed npm
|
|
launch uses either a reviewed Kandev default or an explicit validated operator
|
|
selection.
|
|
|
|
The backend now owns a small durable selection record and must route it through
|
|
every managed-runtime command path. It also owns a bounded, process-local update
|
|
status cache. Version catalogue lookup remains dependent on registry
|
|
availability, and cached versions are not a Kandev-owned artifact inventory. A
|
|
version that has disappeared from the registry can still fail to reinstall
|
|
after npm cache eviction.
|
|
|
|
An exact top-level package pin does not lock transitive dependency ranges. npm
|
|
process startup, cold-cache downloads, registry failures, and cache eviction
|
|
remain accepted costs. The weekly PR requires maintenance and compatibility
|
|
review, while the Settings override lets operators move ahead of a Kandev
|
|
release when necessary.
|
|
|
|
The ACP probe is the activation boundary, not an assertion that every provider,
|
|
model, or future prompt will succeed. Authentication-required or failed probes
|
|
do not activate a candidate because Kandev cannot validate and publish its
|
|
capabilities.
|
|
|
|
## Alternatives Considered
|
|
|
|
- Latest-only repair was rejected because cache replacement selects the same
|
|
broken release and gives the operator no recovery path.
|
|
- Free-form package specs or versions were rejected because they would let a
|
|
Settings request execute an arbitrary npm package.
|
|
- Automatic fallback after a failed launch was rejected because it hides the
|
|
version actually used and can make separate launches behave differently.
|
|
- Persisting the shipped default as an operator selection was rejected because
|
|
it would prevent existing unmodified installations from inheriting a newer
|
|
reviewed default in a later Kandev release.
|
|
- Removing `--prefer-offline` was rejected because exact package specs already
|
|
prevent top-level version drift, while online-preferred metadata on every
|
|
launch adds registry checks and latency. Explicit update paths remain the
|
|
freshness boundary.
|
|
- Automatically installing npm `latest` after an update check was rejected.
|
|
The checker is informational, and every version change remains an explicit
|
|
operator action or a reviewed Kandev pin PR.
|
|
- A Kandev-owned package directory and lockfile were rejected for this
|
|
iteration. Exact npm package specs and version-specific execution keys provide
|
|
transactional selection without reimplementing package storage.
|
|
- User-installed bridge discovery was rejected for this iteration because it
|
|
would require path ownership, compatibility discovery, and a separate update
|
|
contract. npm remains the supported bridge distribution mechanism.
|
|
- Reusing the managed ACP package for terminal passthrough was rejected because
|
|
an ACP JSON-RPC stdio adapter is not an interactive PTY application, and some
|
|
integrations distribute those surfaces as different packages and binaries.
|