12 KiB
Devcontainer Resources
Devcontainer resources integrate the Development Containers Specification into the CleverAgents resource model. Two resource types and an auto-discovery mechanism allow CleverAgents to detect, register, and use devcontainer environments automatically.
Resource Types
devcontainer-instance
A container execution environment defined by a
.devcontainer/devcontainer.json configuration file. Inherits from
container-instance (see ADR-042 for resource type inheritance).
| Property | Value |
|---|---|
| Kind | physical |
| Sandbox strategy | none (container provides isolation) |
| User-addable | yes |
| Parent types | git-checkout, fs-directory |
| Child types | devcontainer-file |
| Handler | DevcontainerHandler |
CLI arguments:
--path(path, required): Path to the directory containing.devcontainer/devcontainer.json.
devcontainer-file
A single-file resource pointing to the devcontainer.json
configuration file itself. Created automatically as a child of
devcontainer-instance during auto-discovery.
| Property | Value |
|---|---|
| Kind | physical |
| Sandbox strategy | copy_on_write |
| User-addable | no |
| Parent types | devcontainer-instance |
| Child types | (none) |
| Handler | DevcontainerHandler |
container-instance
The base container resource type from which devcontainer-instance
inherits. Represents a generic container execution environment.
| Property | Value |
|---|---|
| Kind | physical |
| Sandbox strategy | none (container provides isolation) |
| User-addable | yes |
| Parent types | git-checkout, fs-directory |
| Child types | (none) |
| Handler | DevcontainerHandler |
Auto-Discovery (Planned)
Not yet wired (F31/F23): The discovery module (
discover_devcontainers()) exists and is tested in isolation, but is not invoked duringproject link-resourceor any other production code path. Auto-discovery will be wired in a follow-up PR. For now, devcontainer-instance resources must be added manually viaagents resource add.
When wired, linking a git-checkout or fs-directory resource to a
project will trigger an auto-discovery hook that scans for devcontainer
configurations in the following locations (relative to the resource
root):
.devcontainer/devcontainer.json.devcontainer.json(root-level)
Discovery Process (Planned)
- Trigger: Linking a
git-checkoutorfs-directoryresource. - Scan: The discovery module checks for configuration files at the well-known paths listed above.
- Validate: Each discovered file is parsed as JSON. Invalid files are skipped with a warning.
- Register: For each valid configuration:
- A
devcontainer-instancechild resource is created under the parent resource. - A
devcontainer-filechild resource is created under the devcontainer instance, pointing to the JSON file.
- A
Lazy Activation
Devcontainers use lazy activation: the container is only built and started when first needed by a plan execution. The discovery phase only registers the resource metadata.
Lifecycle Management
Devcontainer-instance resources support a full lifecycle state machine with six states and validated transitions.
State Diagram
detected ──► building ──► running ──► stopping ──► stopped
│ │
▼ │
failed ◄───────────────────────────────┘
│ ▲
└──► building │
(retry) (rebuild)
│
stopped ───────┘
States
| State | Description |
|---|---|
detected |
Container not yet started (default for new) |
building |
Container is being activated (devcontainer up) |
running |
Container is running and healthy |
stopping |
Container is being stopped |
stopped |
Container has been stopped cleanly |
failed |
Container is in an error state |
Valid Transitions
| From | To | Trigger |
|---|---|---|
| detected | building | lazy activation |
| building | running | devcontainer up OK |
| building | failed | devcontainer up fail |
| running | stopping | stop command |
| stopping | stopped | container stopped |
| stopping | failed | stop failed |
| stopped | building | rebuild |
| failed | building | retry |
Lazy Activation Flow
When a tool targets a devcontainer-instance that is in detected
state:
- Transition to
building. - Run
devcontainer up --workspace-folder <location>. - Parse JSON output for
containerIdandremoteWorkspaceFolder. - On success: transition to
running. - On failure: transition to
failed.
Health Checking
Implementation-ahead-of-spec (SPEC-3): Health checking and session-scoped cleanup are implemented but not yet formally specified in
docs/specification.mdfor container resources (the spec only defines health checking for LSP servers). These features fulfil Issue #514 acceptance criteria and will be added to the specification in a follow-up PR.
Active containers receive periodic health probes. Health checking
starts automatically when a container transitions to running state
via activate_container.
- Command:
devcontainer exec ... echo ping - Default interval: 30 seconds (configurable via
health_check_intervalon the lifecycle tracker). - On failure: transitions to
failedviastopping. - Health checks run as background daemon threads.
- Health checks are stopped automatically before container stop.
Session/Plan Cleanup
When a session closes or a plan completes/fails/is-cancelled, active
devcontainer-instances scoped to that session or plan are stopped
automatically. Each ContainerLifecycleTracker carries an optional
session_id field, set during activation, used to scope cleanup.
Cleanup is wired into the following hooks:
A2aLocalFacade._handle_session_close— session closePlanLifecycleService.complete_apply— plan successPlanLifecycleService.fail_apply— plan apply failurePlanLifecycleService.fail_execute— plan execute failurePlanLifecycleService.cancel_plan— plan cancellation
CleanupService.stop_active_devcontainers(session_id="...")
CLI Usage
# Manually add a devcontainer instance
agents resource add devcontainer-instance local/my-dc --path /home/user/project
# Add a container instance
agents resource add container-instance local/my-ctr --image ubuntu:latest
# List all devcontainer resources
agents resource list --type devcontainer-instance
# Show devcontainer details
agents resource show local/my-dc
# View resource tree (shows devcontainer children)
agents resource tree local/my-repo
# Inspect resource with tree view
agents resource inspect local/my-repo --tree
# Stop an active devcontainer
agents resource stop local/my-dc
# Stop without confirmation prompt (for scripts)
agents resource stop local/my-dc --yes
# Rebuild a stopped or errored devcontainer
agents resource rebuild local/my-dc --yes
Stop Command
agents resource stop <devcontainer-name> [--yes|-y]
Transitions an active container through running → stopping → stopped.
Only devcontainer-instance resources may be stopped (F19 fix:
container-instance was removed because it has no lifecycle tracker
wiring). Prompts for confirmation unless --yes (-y) is passed.
If the container is already in a terminal state (stopped or
failed), the stop is a no-op and returns immediately.
Rebuild Command
agents resource rebuild <devcontainer-name> [--yes|-y]
Transitions a stopped or errored container through
stopped/failed → building → running. Runs devcontainer up to
rebuild the container. Only devcontainer-instance resources may
be rebuilt (not generic container-instance). Prompts for
confirmation unless --yes (-y) is passed.
Known Limitations
| Limitation | Detail | Planned Fix |
|---|---|---|
| In-memory lifecycle state (F20) | Lifecycle state is tracked in an in-memory registry (_lifecycle_registry dict), not persisted on the resource model or database. State is lost on process restart. CLI stop/rebuild in a new process cannot see trackers from a previous process. |
M7+: Persist activation_state on the resource model per the spec field definition, hydrating trackers on CLI operations. |
| Registry eviction | Terminal-state trackers (stopped/failed) are evicted when count exceeds 200 (_MAX_TERMINAL_TRACKERS). Eviction runs automatically after bulk cleanup via stop_all_active_containers. Long-running processes with many container cycles may lose historical tracker data. |
Acceptable for MVP; persisted state in M7+ eliminates this. |
| Sandbox strategy (F22/F25) | Specification uses container_snapshot; SandboxFactory does not yet implement snapshot. Handler now uses SandboxStrategy.NONE — the container itself provides isolation. |
Implement container_snapshot in SandboxFactory and switch handler back to it. |
Hardcoded docker stop (F21) |
stop_container() calls docker stop directly. Devcontainer CLI can target Podman or other engines; the devcontainer CLI does not yet offer a stop subcommand. |
Detect the container engine from resource properties or devcontainer CLI config and dispatch to the appropriate stop command. |
| Auto-discovery not wired (F23) | Documentation describes auto-discovery on project link-resource, but no code path invokes discover_devcontainers() during resource linking. |
Wire the discovery hook into the resource linking flow. |
| Container execution stubbed (F24) | ToolRunner returns an error for ExecutionEnvironment.CONTAINER, so lazy activation cannot trigger via tool use in production. Lazy activation currently works through DevcontainerHandler.resolve() on plan sandbox resolution. |
Full container execution support is scoped for a follow-up PR (#616). |
Architecture
The devcontainer integration follows ADR-043 and builds on the resource type inheritance model defined in ADR-042.
Handler
DevcontainerHandler extends BaseResourceHandler and uses the
none sandbox strategy (the container itself provides isolation).
For devcontainer-instance resources, the handler overrides
resolve() to trigger lazy activation via activate_container()
when the resource is in a non-running state. It handles both
devcontainer-instance and devcontainer-file resource types.
Discovery Module
The cleveragents.resource.handlers.discovery module provides:
discover_devcontainers(location, type): Scans a path for devcontainer configs.is_trigger_type(type): Checks if a resource type triggers auto-discovery.DevcontainerDiscoveryResult: Data class holding discovery results.