Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/sandbox-image-pull-if-missing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@electric-ax/agents-runtime': patch
---

Docker sandbox creation now pulls the image only when it isn't already present
locally, honoring the documented `pullIfMissing` semantics. Previously every
container create called `docker pull`, which round-trips to the registry even
for a fully cached digest-pinned image — making creation needlessly slow and
prone to failing whenever the registry was briefly unreachable.
25 changes: 25 additions & 0 deletions .changeset/zombie-sandbox-containers-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
'@electric-ax/agents-runtime': patch
'@electric-ax/agents': patch
---

Fix leftover Docker sandbox containers (`electric-sbx-*`) piling up.

Sandbox containers are meant to be short-lived, but several gaps let them
outlive the work they were created for — opening the desktop app could leave
15+ containers running that were never explicitly started. This closes those
gaps so a container only exists while something is actually using it:

- **Created only when used.** A container now starts the first time an agent
actually uses its sandbox (runs a command, reads/writes a file), so trivial
wakes (scheduled ticks, bookkeeping) no longer spin one up.
- **Cleaned up on quit.** Shutdown now tears down idle containers immediately
instead of leaving their delayed-teardown timers to die with the process.
- **Leftovers reclaimed at startup.** Containers are tagged with the process
that created them; at startup, those whose owner is gone are reclaimed
(throwaway ones removed, reusable ones stopped so their files survive), while
containers a live process is still using are left untouched.

Also: a failed container setup step no longer strands an untracked container,
and all sandboxes are grouped under one `electric-sandboxes` entry in Docker
Desktop so they can be stopped/removed together.
11 changes: 9 additions & 2 deletions packages/agents-runtime/src/process-wake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createEntityLogPrefix, runtimeLog } from './log'
import { createRuntimeServerClient } from './runtime-server-client'
import type { RuntimeServerClient } from './runtime-server-client'
import { unrestrictedSandbox } from './sandbox/unrestricted'
import { ensureSandboxMaterialized } from './sandbox/lazy'
import { resolveSandboxIdentity } from './sandbox/identity'
import { appendPathToUrl } from './url'
import { manifestChildKey } from './manifest-helpers'
Expand Down Expand Up @@ -1322,7 +1323,7 @@ export async function processWake(
const requestedInherit =
opts?.sandbox === `inherit` ||
(typeof opts?.sandbox === `object` && opts.sandbox.inherit === true)
const sandbox = requestedInherit
const childSandbox = requestedInherit
? resolvedSandboxSelection
? {
profile: resolvedSandboxSelection.profile,
Expand All @@ -1336,6 +1337,12 @@ export async function processWake(
: opts?.sandbox === `inherit`
? undefined
: opts?.sandbox
// An inheriting child only ever ATTACHES by key — make sure the
// owner's (lazily-created) container/workspace actually exists before
// the child can wake, even if this wake never ran a tool itself.
if (requestedInherit && resolvedSandboxSelection && sandbox) {
await ensureSandboxMaterialized(sandbox)
}
return serverClient.spawnEntity({
type: childType,
id: childId,
Expand All @@ -1344,7 +1351,7 @@ export async function processWake(
initialMessage: opts?.initialMessage,
initialMessageType: opts?.initialMessageType,
tags: opts?.tags,
sandbox,
sandbox: childSandbox,
wake: wakeOpt,
})
},
Expand Down
2 changes: 2 additions & 0 deletions packages/agents-runtime/src/sandbox-docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

export {
dockerSandbox,
reclaimDockerSandboxByKey,
shutdownAllDockerSandboxes,
sweepOrphanedDockerSandboxes,
__resetPersistentRegistryForTests,
} from './sandbox/docker'
Expand Down
2 changes: 2 additions & 0 deletions packages/agents-runtime/src/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export type { RemoteProvider, RemoteSandboxOpts } from './sandbox/remote'
export type { RemoteSandboxClient } from './sandbox/remote/types'
export { isE2BAvailable } from './sandbox/remote/e2b'
export { chooseDefaultSandbox } from './sandbox/default'
export { ensureSandboxMaterialized, lazySandbox } from './sandbox/lazy'
export type { LazySandboxOpts } from './sandbox/lazy'
export { SandboxError } from './sandbox/types'
export type {
Sandbox,
Expand Down
Loading
Loading