248 lines
11 KiB
Markdown
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.
|