Files
kandev/docs/specs/agents/runtime-updates.md
T
2026-07-27 19:34:13 +01:00

248 lines
11 KiB
Markdown

---
status: approved
created: 2026-07-26
owner: Kandev
---
# Managed Agent Runtime Updates
## Why
Operators need newly released agent capabilities and models without waiting for
a Kandev release. They also need normal agent launches to avoid paying an
upstream package-resolution cost on every session when a usable npm execution
cache is already present.
## What
- Kandev launches the managed npm runtime for Claude, Codex, OpenCode, Copilot,
and Gemini by package name without an exact version or an explicit `latest`
tag.
- Normal launches may reuse npm's execution cache. Cache reuse is best-effort;
Kandev does not present it as a durable installed-version guarantee.
- Each supported installed agent has a compact, accessible update icon action
on its Settings > Agents card on desktop and mobile. Update versions,
command details, progress, output, and results are not rendered in the card.
- Opening the update action is read-only. It opens an update dialog that
resolves and shows the current runtime version, upstream target version,
exact built-in command, host-only scope, capability refresh, and active
session behavior before an update can start.
- The update starts only after the operator approves it in the dialog. The
dialog then shows live stdout/stderr, progress, and the terminal success or
failure state.
- A successful package update automatically starts a fresh host capability
probe. The returned runtime version, models, modes, configuration options,
commands, and capability status replace the previous cached values.
- An update affects new host probes, utility calls, and sessions. It does not
restart or mutate an already-running agent session.
- Kandev uses the ACP initialization protocol version and advertised
capabilities as the compatibility boundary. Kandev does not gate managed
npm runtimes by a repository-maintained package-version allowlist.
- Package update commands are defined by Kandev's built-in agent metadata.
Callers cannot submit package names, versions, registry URLs, or shell text.
- Update jobs for the same agent are idempotent while queued or running. An
install and update for the same agent cannot run concurrently.
The initial managed package set is:
| Agent | Managed runtime package |
| --- | --- |
| Claude | `@agentclientprotocol/claude-agent-acp` |
| Codex | `@agentclientprotocol/codex-acp` |
| OpenCode | `opencode-ai` |
| Copilot | `@github/copilot` |
| Gemini | `@google/gemini-cli` |
The managed package is the runtime Kandev uses for ACP capability discovery.
Separately configured passthrough commands and native authentication helpers
remain outside this update action when they use another package or installer.
Decision: [ADR-2026-07-26-user-managed-agent-runtime-updates](../../decisions/2026-07-26-user-managed-agent-runtime-updates.md).
## API surface
### Agent catalogue
Installed-agent catalogue entries expose optional runtime-management metadata:
```json
{
"runtime_update": {
"supported": true,
"package": "@agentclientprotocol/claude-agent-acp",
"current_version": "0.62.0"
}
}
```
`current_version` is omitted when no successful capability probe has reported a
runtime version. Unmanaged agents omit `runtime_update`.
### Update jobs
- `GET /api/v1/agent-update/:agentName/preview` resolves the current and
upstream target versions and returns the exact trusted built-in update
command without starting an update.
- `POST /api/v1/agent-update/:agentName` starts or returns the active update
job for a built-in managed agent.
- `GET /api/v1/agent-update/jobs` returns active and recently completed update
jobs.
- `GET /api/v1/agent-update/jobs/:id` returns one retained update job.
- State-changing update requests use the same Settings interlock as agent
installation and profile mutation.
An update job contains:
```json
{
"job_id": "uuid",
"agent_name": "claude-acp",
"status": "resolving",
"current_version": "0.62.0",
"target_version": "0.63.0",
"output": "",
"error": "",
"refresh_error": "",
"started_at": "timestamp",
"finished_at": "timestamp"
}
```
`current_version`, `target_version`, `finished_at`, `error`, and
`refresh_error` are optional until known. The backend emits
`agent.update.started`, `agent.update.output`, and `agent.update.finished`
notifications with the same job identity and state. Output notifications carry
only the appended output chunk.
An update preview contains:
```json
{
"agent_name": "claude-acp",
"package": "@agentclientprotocol/claude-agent-acp",
"current_version": "0.62.0",
"target_version": "0.63.0",
"command": [
"npm",
"exec",
"--yes",
"--prefer-online",
"--package=@agentclientprotocol/claude-agent-acp",
"--",
"node",
"-e",
""
],
"command_string": "npm exec --yes --prefer-online --package=@agentclientprotocol/claude-agent-acp -- node -e \"\""
}
```
The preview endpoint accepts only the built-in agent name. Package names,
versions, registry URLs, and command arguments are not caller-controlled.
## State machine
| State | Trigger | Observable behavior |
| --- | --- | --- |
| `queued` | The backend accepts an update request. | The action is disabled and shows that the update is queued. |
| `resolving` | A worker starts the job. | Kandev discovers the current runtime version and upstream npm target. |
| `updating` | Version resolution succeeds. | Kandev streams package-update output and shows current → target. |
| `refreshing` | The package update succeeds. | Kandev re-probes the host runtime and keeps the action disabled. |
| `succeeded` | The capability probe succeeds, or the package updated but capability refresh returned a recoverable error. | The UI shows the installed target. When refresh succeeded, it replaces model and mode data. A refresh-only error is shown without claiming the package update was rolled back. |
| `failed` | Registry lookup or package update fails, the command times out, or ACP initialization is incompatible. | The UI retains the previous model list and shows the captured error and output. |
Jobs are terminal after `succeeded` or `failed`. Retrying creates a new job.
## Failure modes
- If npm registry metadata cannot be resolved, the job fails before changing
the runtime and retains the prior capability data.
- If preview version resolution fails, the dialog shows the error and keeps
approval disabled. No update job or package command starts.
- If the package update command fails or times out, the job fails and retains
the prior capability data.
- If the package update succeeds but the capability probe fails because
authentication is required or another recoverable probe error occurs, the
job reports package-update success plus `refresh_error`; the previous model
list remains visible and the operator can authenticate or retry the refresh.
- If the updated runtime negotiates an unsupported ACP protocol version or
cannot initialize, the job fails visibly. Kandev does not silently fall back
to a repository-pinned runtime.
- Raw process output is bounded using the existing in-memory job output limit.
Package-manager credentials and configured registry authentication are never
returned as structured fields.
- Loss of the browser connection does not cancel a running job. Reopening the
page recovers retained job progress through the jobs endpoint.
## Persistence guarantees
- Update jobs and capability data are process-local and do not survive a Kandev
backend restart.
- Completed jobs remain queryable for the existing short job-retention window.
- Settings does not rehydrate retained update jobs after a browser page
restart. Update dialog state, output, and results are intentionally
page-local and disappear when the page is restarted.
- npm's host cache may survive Kandev restarts, but Kandev does not own or
guarantee that cache.
- After a backend restart, normal host capability probing reports whichever
runtime npm resolves in that environment.
## Scenarios
- **GIVEN** a managed agent with a cached runtime, **WHEN** a new session starts
without an explicit update, **THEN** Kandev invokes the unversioned package
spec and does not require a repository-maintained version pin.
- **GIVEN** Claude reports runtime version `0.62.0` and npm reports `0.63.0`,
**WHEN** the operator opens its update action, **THEN** the dialog shows
`0.62.0 → 0.63.0`, the exact built-in command, and how the host update
affects capabilities and active sessions without starting the command.
- **GIVEN** the update dialog has a resolved preview, **WHEN** the operator
approves the update, **THEN** Kandev starts exactly one update and the dialog
streams stdout/stderr until the job is terminal.
- **GIVEN** an update succeeds and the new runtime advertises an additional
model, **WHEN** the automatic capability probe completes, **THEN** the new
model appears without a page reload or manual Rescan.
- **GIVEN** an update is already queued, resolving, updating, or refreshing,
**WHEN** the operator selects the action again, **THEN** Kandev returns the
existing job and does not run a second update.
- **GIVEN** an install is active for an agent, **WHEN** an update is requested
for that agent, **THEN** Kandev returns the active maintenance job rather
than running install and update concurrently.
- **GIVEN** npm registry lookup fails while preparing the dialog, **WHEN** the
update action is opened, **THEN** the dialog shows a retryable preview failure,
keeps approval disabled, and starts no update job.
- **GIVEN** npm registry lookup succeeds for the preview but fails after
approval because the registry changed, **WHEN** the update job resolves its
target again, **THEN** the dialog shows a retryable update failure and retains
the previous models.
- **GIVEN** the package update succeeds but the fresh probe requires
authentication, **WHEN** the job finishes, **THEN** the dialog reports the new
package version and refresh error, the previous models remain available, and
the card keeps its existing authentication recovery action.
- **GIVEN** an agent is unmanaged or native-only, **WHEN** Settings renders its
installed card, **THEN** no update action is shown.
- **GIVEN** an update dialog has shown progress or a terminal result, **WHEN**
the operator restarts the page, **THEN** the dialog is closed and no prior
update details, output, or result appear on the agent card or in a newly
opened dialog.
- **GIVEN** an update is running on a phone viewport, **WHEN** the operator
views the update surface, **THEN** current and target versions, the command,
progress, output, and retry state are reachable by touch without horizontal
page scrolling.
- **GIVEN** an agent session is already running, **WHEN** its host runtime is
updated, **THEN** the existing session continues unchanged and only later
probes or launches use the updated runtime.
## Out of scope
- Scheduled or automatic package updates.
- Exact package-version pins, version allowlists, rollback, or user-selected
historical versions.
- Updating configured remote executors or every running container from the
host Settings action.
- Restarting or hot-swapping active agent sessions.
- Managing native-only update channels such as Cursor.
- Updating separately distributed passthrough or authentication helper
packages when they are not the managed ACP runtime.
- Resuming or recovering update dialog state after a browser page restart.