Actual Behavior
Start the SDK Agent Server with uv run openhands-agent-server and conversation_runtime: docker. When the outer Agent Server uses this mode, every accessed conversation gets a dedicated agent-server-conversation-* container. DockerConversationRegistry removes owned containers at outer-server startup, explicit deletion, or shutdown, but it has no idle-runtime eviction loop. The inner Agent Server evicts its in-process conversation after conversation_idle_ttl_seconds, while its container continues running indefinitely.
On the OSS Agent Canvas VM this left 32 containers running for up to 32 hours. Each had an approximately 169 MB writable layer, filling the 100 GB system disk and leaving only 9.9 MB free.
Steps to Reproduce
- Start Agent Server with
conversation_runtime: docker and the default conversation idle TTL.
- Open several conversations and allow them to reach a terminal or idle state.
- Wait longer than the configured idle TTL.
- Observe that the inner conversation is evicted but each
agent-server-conversation-* container remains running and continues consuming memory and disk.
Desired Behavior
The Docker conversation registry should stop an inactive runtime after the configured idle TTL while preserving the conversation, provisioning identity, workspace, and persisted event history. A later interaction should reprovision the runtime normally. Running conversations and recently accessed runtimes must not be stopped.
Acceptance Criteria
OpenHands AI triage
The following comments and acceptance criteria were added by the OpenHands AI agent.
Triage
Confirmed against openhands-agent-server/openhands/agent_server/docker_runtime/registry.py: DockerConversationRegistry only removes owned containers at startup cleanup, explicit delete, or shutdown — it has no idle-runtime eviction loop, so a container outlives the inner conversation that is evicted after conversation_idle_ttl_seconds. The fix is scoped to the Docker runtime registry adding an idle-eviction loop keyed on the existing Config.conversation_idle_ttl_seconds (null already means "never evict"). It must stop an inactive runtime while preserving the provisioning identity, workspace bind mount, and persisted history, then let a later access reprovision normally, and must skip running/subscribed conversations.
Non-goals: no change to inner-server in-process eviction, no change to the provisioning/persistence format, no new config knobs, and no change to the non-Docker (local) runtime path.
Acceptance Criteria
Actual Behavior
Start the SDK Agent Server with
uv run openhands-agent-serverandconversation_runtime: docker. When the outer Agent Server uses this mode, every accessed conversation gets a dedicatedagent-server-conversation-*container.DockerConversationRegistryremoves owned containers at outer-server startup, explicit deletion, or shutdown, but it has no idle-runtime eviction loop. The inner Agent Server evicts its in-process conversation afterconversation_idle_ttl_seconds, while its container continues running indefinitely.On the OSS Agent Canvas VM this left 32 containers running for up to 32 hours. Each had an approximately 169 MB writable layer, filling the 100 GB system disk and leaving only 9.9 MB free.
Steps to Reproduce
conversation_runtime: dockerand the default conversation idle TTL.agent-server-conversation-*container remains running and continues consuming memory and disk.Desired Behavior
The Docker conversation registry should stop an inactive runtime after the configured idle TTL while preserving the conversation, provisioning identity, workspace, and persisted event history. A later interaction should reprovision the runtime normally. Running conversations and recently accessed runtimes must not be stopped.
Acceptance Criteria
OpenHands AI triage
The following comments and acceptance criteria were added by the OpenHands AI agent.
Triage
Confirmed against
openhands-agent-server/openhands/agent_server/docker_runtime/registry.py:DockerConversationRegistryonly removes owned containers at startup cleanup, explicit delete, or shutdown — it has no idle-runtime eviction loop, so a container outlives the inner conversation that is evicted afterconversation_idle_ttl_seconds. The fix is scoped to the Docker runtime registry adding an idle-eviction loop keyed on the existingConfig.conversation_idle_ttl_seconds(null already means "never evict"). It must stop an inactive runtime while preserving the provisioning identity, workspace bind mount, and persisted history, then let a later access reprovision normally, and must skip running/subscribed conversations.Non-goals: no change to inner-server in-process eviction, no change to the provisioning/persistence format, no new config knobs, and no change to the non-Docker (
local) runtime path.Acceptance Criteria
conversation_runtime: dockerand a setconversation_idle_ttl_seconds, a container whose conversation is in a terminal or idle state and unaccessed for at least the TTL is stopped (itsagent-server-conversation-*container is no longer running) and no longer held in the registry's in-memory container map.conversation_idle_ttl_secondstonulldisables eviction entirely (no container is stopped for idleness).shutdown()cancels and awaits the eviction task, leaving no orphaned task and no owned container still running.