MUTATION_TESTING
AgentThe agent package's pure, deterministic core (the chat permission gate, the control-tool plumbing, JSON extraction, and the…
**Navigation**: [Agents package rules](../AGENTS.md) → **JavaScript Sandbox**
Claude Code
$ npx -y skills add nodetool-ai/nodetool --agent claude-code
Cursor
Cursor also loads subagents from .claude/agents, so copy this file there to use it in Cursor.
**Navigation**: [Agents package rules](../AGENTS.md) → **JavaScript Sandbox**
Paths are relative to `packages/agents/` unless they name another package.
Reader-facing reference for this whole section: [docs/javascript-sandbox.md](../../../docs/javascript-sandbox.md).
User-authored JS from a CodeAct action and `nodetool.code.Code` runs in a **QuickJS WebAssembly sandbox** via `@sebastianwessel/quickjs`. The guest lives in its own WASM heap, so runaway or malicious code can't corrupt the host V8 heap the way it could under the previous `node:vm` implementation.
Windows test portability: filesystem-containment tests must use `tests/_helpers/filesystem-links.ts` instead of calling `symlink()` directly. The helper creates directory junctions on Windows, so the tests do not require Developer Mode or elevation, and regular directory symlinks elsewhere. Normalize paths before comparing them with repository-style `/` paths, and use Node itself for cross-platform process-spawn tests instead of POSIX utilities such as `true`. Normalize wrapper-produced module IDs before matching them: QuickJS uses `\` on Windows and `/` on POSIX. Keep native media fixtures on software encoders; a Windows hardware probe can find a codec that the installed driver cannot open.
On Node the interpreter runs on a **`worker_threads` worker** (`src/js-sandbox-worker/`): `buildSandbox` and every host bridge stay on the main thread, the worker rebuilds each bridge as an RPC proxy over the port, and a CPU-bound guest therefore blocks only its own thread — the server keeps serving, and the websocket `stop` frame that cancels the run can still be read. Abort is `worker.terminate()`, immediately — equally instant for a spinning guest and a parked one, and nothing of value dies with the thread: logs and emitted values accumulate main-side through the RPC dispatches, and a cancelled action leaving no partial `state` write-back is the cleaner contract. Three runs stay in-process: the browser (no `worker_threads`), a run with input streams (the synchronous `stream.open` mirror has no handle names to seed), and `NODETOOL_SANDBOX_INPROC=1`. `NODETOOL_SANDBOX_WORKER=require` turns any other fallback into an error for CI. Under tsx/vitest the worker boots from an eval'd bootstrap that registers `tsx/esm/api` — tsx's own `--import` hook skips worker threads, and Node's built-in strip-only TS loader cannot parse the workspace packages. The packaged backend ships the worker as a second esbuild bundle (`scripts/bundle-backend.mjs` → `backend/js-sandbox-worker/worker-entry.js`). The interpreter itself (`js-sandbox-worker/interpreter.ts`) is shared by both paths, so they cannot drift; bridge results must be plain data — a function in a bridge return value cannot cross `postMessage`, which is why the fetch bridge returns body bytes and the guest prelude builds `text()`/`bytes()`/ `arrayBuffer()` over them.
Hard limits enforced by the runtime. Each row's default can be overridden per invocation via `RunSandboxOptions.limits`, clamped to the ceiling in the last column by `resolveSandboxLimits`:
| Limit | Default | Configured by | Ceiling | |-------|---------|---------------|---------| | Execution time | `timeoutMs` (30 s) | `setInterruptHandler` (CPU budget) + wall-clock race | — | | Suspended time | `DEFAULT_SUSPEND_ALLOWANCE_MS` (30 min), or `INPUT_STREAM_SUSPEND_ALLOWANCE_MS` (unbounded) for a run with `onTakeInput` | `RunSandboxOptions.suspendAllowanceMs`, only with a `clock` | — | | Guest heap | `GUEST_MEMORY_LIMIT` = 64 MB | `runtime.setMemoryLimit` (`limits.memoryLimitBytes`) | 512 MB | | Call stack | `GUEST_STACK_LIMIT` = 512 KB | `runtime.setMaxStackSize` (`limits.stackLimitBytes`) | 8 MB | | Fetch calls | `MAX_FETCH_CALLS` = 20 per run | counter inside bridge (`limits.maxFetchCalls`) | 100 | | Fetch body | `MAX_RESPONSE_BODY_SIZE` = 1 MB | truncation inside bridge (`limits.maxResponseBodyBytes`) | 50 MB | | Fetch timeout | `FETCH_TIMEOUT_MS` = 15 s | per-request `AbortController` (`limits.fetchTimeoutMs`) | 120 s | | Output | `MAX_OUTPUT_SIZE` = 100 KB | `serializeResult` truncation (`limits.maxOutputSize`) | 10 MB | | Random bytes | `MAX_RANDOM_BYTES` = 64 KB | `crypto.getRandomValues` clamp | — | | Progress reports | `MAX_PROGRESS_CALLS` = 1000 per run, one per `PROGRESS_MIN_INTERVAL_MS` = 100 ms | counter + timestamp inside the bridge | — | | Host module text input | `MAX_HOST_INPUT_CHARS` = 5 MB | check inside `host-modules/limits.ts` | — | | Host module byte input | `MAX_HOST_INPUT_BYTES` = 10 MB | check inside `host-modules/limits.ts` | — | | `sandbox-html` matches | `DEFAULT_SELECT_HTML_LIMIT` = 100 | `options.limit` | `MAX_SELECT_HTML_LIMIT` = 1000 | | `sandbox-zip` inflation | `MAX_UNZIP_TOTAL_BYTES` = 50 MB total | check inside `host-modules/zip.ts` | — | | `sandbox-xlsx` write | `MAX_WRITE_SHEETS` = 64, `MAX_WRITE_CELLS` = 250 000 | checks inside `host-modules/xlsx.ts` | — | | `sandbox-ocr` words | `MAX_OCR_WORDS` = 20 000 | check inside `host-modules/ocr.ts` | — | | `image.*` input | `MAX_IMAGE_INPUT_BYTES` = 25 MB | length check inside each bridge | — | | Image / canvas pixels | `MAX_IMAGE_PIXELS` = 32 M, longest edge `MAX_IMAGE_DIMENSION` = 16384 | `assertSurfaceSize` | — | | `image.decode` pixels | `MAX_DECODE_PIXELS` = 8 M | check inside the bridge | — | | Canvas draw ops | `MAX_CANVAS_OPS` = 10 000 per render | count inside `renderCanvas` | — |
QuickJS's memory limiter counts its own heap objects; string and typed-array payloads are not charged against it, so `memoryLimitBytes` bites on object allocation, not on `new Uint8Array(n)`.
Secrets are scoped. `SandboxLimits.secretScope` (host-set, `null` for an unscoped run) is the list of names a run may read; the `getSecret` bridge refuses everything else, and `nodetool.secrets.get/tryGet/list` sit on top of that one check rather than beside it. `nodetool.code.Code` exposes the list as its `secrets` property, empty by def
Open-source AI film studio · agent + node workflows Direct AI films, shot by shot. Start with one sentence. The agent writes the story, draws a still for every shot, and cuts the clips on a timeline.
The agent package's pure, deterministic core (the chat permission gate, the control-tool plumbing, JSON extraction, and the…
**Navigation**: [Agents package rules](../AGENTS.md) → **Capabilities and tool sources**
**Navigation**: [Agents package rules](../AGENTS.md) → **Running Agents from CLI**
**Navigation**: [Agents package rules](../AGENTS.md) → **CodeAct Execution**
**Navigation**: [Agents package rules](../AGENTS.md) → **Eval suites**
**Navigation**: [Agents package rules](../AGENTS.md) → **Graph authoring**
Agent for managing AI prompts on prompts.chat - search, save, improve, and organize your prompt library.
정밀(strict) 모드 1단계 진단 에이전트. 글 전체를 한 번에 보고 "가장 지배적인 AI 티 패턴 3~6개"를 taxonomy ID와 함께 진단한다. 불안정한 span 열거(0↔18개로 요동) 대신 "무엇이 이 글을…
Topic Clusters curator for the Claude Blog Brain. Maintains and answers from the Topic Clusters theme of the brain, grounded in…
Optional gallery research helper. Use when a caller wants to find references, explore styles, build a mood board, or needs…