Workspace & Exec SPI - Sandboxing Overview¶
The agent tools are decoupled from the host machine through two small SPIs in spring-ai-agent-utils-common. Together they answer the two questions any sandboxed agent deployment has to answer:
| Seam | Package | Question it answers |
|---|---|---|
ExecBackend |
org.springaicommunity.agent.common.exec |
Where do shell commands run? |
Workspace |
org.springaicommunity.agent.common.workspace |
Where do files live, and how are paths shown to the model? |
Tools describe what to do; the backend and workspace decide where and how. Swapping the host for a container changes configuration, never tool code.
ExecBackend — where commands run¶
public interface ExecBackend {
ExecResult run(ExecSpec spec); // synchronous, honors the spec's timeout
ExecHandle start(ExecSpec spec); // background: poll output, kill
}
ExecSpec— the command line as the model authored it, a wall-clock timeout (policy owned by the spec: default 2 min, capped at 10 min), and per-invocation environment variables. Shell selection and working directory are deliberately backend concerns.ExecResult— status (COMPLETED,TIMED_OUT,LAUNCH_FAILED,INTERRUPTED), exit code, separate stdout/stderr. Failures are reported, never thrown.ExecHandle— a background command:isAlive(), cursor-basednewOutput(filter)(each call returns only output produced since the last one),exitCode(),kill()(graceful, then forced).
Who uses it: ShellTools routes Bash/BashOutput/KillShell through the configured backend, and AgentEnvironment runs its git-status commands through it, so a sandboxed agent reports the sandbox's view of the repository.
Implementations:
| Implementation | Module | Runs commands |
|---|---|---|
LocalExecBackend (default) |
spring-ai-agent-utils |
On the host JVM, with working directory, clean-environment mode and shell selection |
DockerCliExecBackend |
spring-ai-agent-utils-docker-cli |
Inside a Docker container, via the docker CLI |
Custom implementations are a small class: implement run (and start if you support background shells — throwing UnsupportedOperationException there is acceptable for remote/queued executors). An adapter over agent-sandbox's Sandbox.exec() is straightforward for the synchronous path — the shapes are compatible.
Workspace — where files live, and what the model sees¶
@FunctionalInterface
public interface Workspace {
Path root(); // host-side root directory
default String display(String hostPath) { ... } // host path -> model-visible path
static Workspace local(Path root) { ... } // identity display
}
A workspace is a root directory plus a display mapping. For local execution the mapping is the identity; for a container with a bind-mounted workspace it rewrites host paths to their in-container form, so the model is never shown a path its shell cannot use.
Who uses it — one workspace(...) call per tool builder, with per-tool semantics:
| Tool | workspace(...) effect |
|---|---|
| GrepTool / GlobTool / ListDirectoryTool | Working directory and allowed-directory confinement |
| FileSystemTools | Allowed-directory confinement |
| ShellTools | Working directory only (a shell can't be confined from Java — that's what a sandboxed ExecBackend is for) |
| SkillsTool | Announces skill base directories via display(...) |
| AgentEnvironment | Environment-info block describes the workspace, not the host |
How the two compose¶
The file/search tools run host-side, confined to the workspace root; shell commands run in the backend. With a bind mount connecting the two, both operate on the same files and the model sees one consistent world:
try (DockerCliExecBackend backend = DockerCliExecBackend.builder()
.image("alpine:3.20")
.mount(hostDir, "/workspace")
.build()) {
Workspace workspace = backend.workspace(); // hostDir root, /workspace display
ShellTools shell = ShellTools.builder().execBackend(backend).build();
FileSystemTools files = FileSystemTools.builder().workspace(workspace).build();
GrepTool grep = GrepTool.builder().workspace(workspace).build();
String envInfo = AgentEnvironment.info(workspace);
}
See DockerCliExecBackend for the complete recipe and its operational caveats.
Module map¶
| Module | Contents |
|---|---|
spring-ai-agent-utils-common |
The SPIs: ExecBackend, ExecSpec, ExecResult, ExecHandle, Workspace — the exec and workspace packages are plain JDK, so backend implementations need no Spring on their classpath |
spring-ai-agent-utils |
The tools plus LocalExecBackend (host default) |
exec-backends/spring-ai-agent-utils-docker-cli |
DockerCliExecBackend (Docker sandbox) |
The exec-backends/ folder is the home for further backend implementations as they land.