cloudflare / cloudflare/agents
think: make createSandboxTools actually work
- Dominant language
- TypeScript
- Stars
- 5.6k
- Forks
- 711
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 53
Description
Parent roadmap: #1439
## Summary
`@cloudflare/think/tools/sandbox` currently exports `createSandboxTools()`, but the implementation is intentionally a no-op: it logs a warning once and returns `{}`.
That means the blog/docs-level execution ladder says Think can wire in Cloudflare Sandbox as Tier 4, but the package export does not yet provide any usable tools.
This issue tracks turning `createSandboxTools()` into a real integration with the Cloudflare Sandbox SDK, or narrowing/removing the public promise until the integration exists.
## Current behavior
Current implementation in `packages/think/src/tools/sandbox.ts`:
```ts
export function createSandboxTools(
_binding?: unknown,
_options?: CreateSandboxToolsOptions
): ToolSet {
if (!warned) {
warned = true;
console.warn(
"[@cloudflare/think] createSandboxTools is not yet implemented. " +
"No tools will be registered."
);
}
return {};
}
```
The export exists in `packages/think/package.json` as `@cloudflare/think/tools/sandbox`, so users can import and spread it, but they get no tools.
## Why this matters
Project Think's execution ladder is meant to be additive:
- Tier 0: Think Workspace / `@cloudflare/shell`
- Tier 1: Dynamic Worker / codemode execution
- Tier 2: npm/runtime dependency loading
- Tier 3: browser automation
- Tier 4: full Sandbox for OS-level toolchains, repos, test runners, compilers, and long-running processes
Tier 4 is the escape hatch for tasks that cannot fit in a Workers isolate: `git clone`, `npm test`, `cargo build`, real CLIs, shell scripts, code review bots, and heavyweight project work.
Right now Think has a named Sandbox tool export, but no actual Sandbox bridge. That creates a confusing gap for users and for the roadmap.
## Sandbox SDK capabilities to target
The Sandbox SDK already exposes useful primitives:
- `getSandbox(env.Sandbox, id)` to create/reuse a sandbox instance
- `exec()` for command execution with complete stdout/stderr/exit code
- `execStream()` for long-running commands with streamed output
- `startProcess()` / process log streaming for services
- file operations: `readFile`, `writeFile`, `mkdir`, `exists`, delete/move/rename
- code interpreter contexts for Python/JavaScript/TypeScript
- preview URLs / port exposure for web servers
- terminal and WebSocket connections
- request-proxy patterns so real credentials stay in the Worker, not inside sandbox code
For Think v1, we should probably start with a small subset instead of exposing the whole SDK.
## Proposed first milestone
Implement a minimal `createSandboxTools(binding, options)` that exposes a conservative tool set for Think agents:
1. `sandbox_exec`
- Run a shell command in a named sandbox.
- Inputs: `command`, optional `sandboxId`, optional `cwd`, optional `timeout`.
- Output: `stdout`, `stderr`, `exitCode`, `success`, maybe truncated output metadata.
2. `sandbox_write_file`
- Write a file into `/workspace` or another allowlisted path.
- Inputs: `path`, `content`, optional encoding.
3. `sandbox_read_file`
- Read a file back from the sandbox.
- Inputs: `path`, optional encoding / max bytes.
4. Optional later: `sandbox_exec_stream`
- Stream long-running output into Think chunks or a retained run/event model.
- This is probably not required for the first PR unless the API shape is obvious.
5. Optional later: `sandbox_expose_port` / preview URL helpers.
Keep the first implementation boring and testable: command execution + file transfer.
## Workspace sync question
The important design question is how Think's DO-local Workspace interacts with the Sandbox filesystem.
Possible approaches:
### A. Explicit file transfer tools
The model calls `read` / `find` / `grep` against Think Workspace, then calls `sandbox_write_file` for files it wants in the Sandbox. After commands run, it calls `sandbox_read_file` to pull results back.
Pros: simple, no hidden sync, minimal API.
Cons: tedious for whole repos / many files.
### B. Helper methods for push/pull workspace snapshots
Expose tools like:
- `sandbox_push_workspace({ paths, sandboxPath })`
- `sandbox_pull_workspace({ sandboxPath, paths })`
Pros: better UX for project work.
Cons: needs careful path filtering, binary handling, limits, and conflict behavior.
### C. Git/Artifacts handoff
Use Git/Artifacts as the handoff boundary: Think commits workspace state to an Artifacts repo, Sandbox clones/mounts it, pushes results, and Think imports/reviews the diff.
Pros: clean versioned boundary, aligns with #1440.
Cons: larger integration; should not block the minimal `createSandboxTools()` milestone.
Recommendation: start with A, design B after using it, and keep C linked to #1440.
## API shape questions
- Should `createSandboxTools()` accept a Sandbox Durable Object namespace directly, or a factory function like `(id) => getSandbox(binding, id)`?
- How should the sandbox id be chosen by default: Think agent name, chat/session id, caller-provided id, or a generated per-run id?
- Should tools reuse a long-lived sandbox per Think agent/session, or create per-run sandboxes?
- How should timeouts work, given Sandbox command timeout behavior does not necessarily kill the underlying process?
- Should path access be restricted to `/workspace` by default?
- Should network/proxy setup be part of `createSandboxTools()` or left to application code?
- How do we surface preview URLs / running background processes without turning v1 into a full IDE API?
## Security constraints
- Do not pass durable secrets directly into sandbox commands by default.
- Prefer Worker-side proxy patterns for external APIs so the sandbox gets short-lived scoped credentials, not real API keys.
- Require explicit opt-in for network credentials, repo write tokens, or access to user data.
- Default path access should be scoped, probably to `/workspace`.
- Tool outputs must be truncated/structured to avoid blowing up model context.
- Commands should have explicit timeouts and clear behavior for non-zero exit codes.
- Avoid logging secrets in command strings, outputs, or chat history.
## Relationship to existing Think tools
- `createExecuteTool()` remains the default for small JavaScript programs in Dynamic Worker isolates.
- `createSandboxTools()` is for full OS/toolchain work that cannot run in codemode: real shell commands, package managers, compilers, tests, CLI agents, and services.
- Browser tools remain separate; Sandbox should not replace `createBrowserTools()`.
- Artifacts integration (#1440) may become the preferred handoff mechanism for large repo workflows, but this issue should make the Sandbox export useful on its own first.
## Acceptance criteria
- `createSandboxTools()` returns at least one real AI SDK tool instead of `{}` when passed a valid Sandbox binding/factory.
- The minimal tools can run a command in a sandbox and return structured result data.
- The minimal tools can write a file into the sandbox and read it back.
- Tool outputs are bounded/truncated and include enough metadata for the model to continue.
- Timeouts and non-zero exit codes have documented behavior.
- The implementation has worker tests or a focused example covering command execution and file round trip.
- Docs explain when to use `createExecuteTool()` versus `createSandboxTools()`.
- Docs show the required Wrangler binding/configuration.
- The no-op warning is removed, or retained only when the function is called without a usable binding.
## Nice-to-have follow-ups
- Streaming command output into Think events.
- Background process and preview URL tools.
- Workspace push/pull helpers.
- Artifacts-backed clone/push handoff with #1440.
- Sandbox request proxy helpers for GitHub, package registries, model providers, or R2.
- A Think example that runs tests/builds in Sandbox and reports results back into chat.
## Useful files
- `packages/think/src/tools/sandbox.ts`
- `packages/think/src/tools/execute.ts`
- `docs/think/tools.md`
- `examples/assistant/src/server.ts`
- `experimental/gadgets-sandbox/README.md`
## References
- Roadmap umbrella: #1439
- Artifacts handoff issue: #1440
- Sandbox SDK overview: https://developers.cloudflare.com/sandbox/
- Sandbox command execution: https://developers.cloudflare.com/sandbox/guides/execute-commands/
- Sandbox file operations: https://developers.cloudflare.com/sandbox/guides/manage-files/
- Sandbox streaming output: https://developers.cloudflare.com/sandbox/guides/streaming-output/
- Sandbox request proxy pattern: https://developers.cloudflare.com/sandbox/guides/proxy-requests/
- Claude Code on Sandbox tutorial: https://developers.cloudflare.com/sandbox/tutorials/claude-code/
Contributor guide
Assessment
This issue has not been assessed yet.