Scripts runtime
What the swarm-scripts runtime exposes to user code, what it does NOT expose, and how the typecheck stays aligned.
Swarm scripts are TypeScript modules persisted in the catalog via
/api/scripts/upsert and executed by the
scripts-runtime. The save-time typecheck and the runtime are deliberately
aligned — what passes the typecheck is what actually runs. This page documents
the full surface so script authors don't have to bisect.
What the runtime provides
Scripts run inside a bun run subprocess (src/scripts-runtime/eval-harness.ts)
with the environment stripped to a small allowlist. Everything below is
available as a plain global in user code AND typechecks cleanly.
ES2022 standard library
The typecheck loads lib.es2022.d.ts. All standard ES2022 built-ins resolve:
- Primitives + boxed types:
Number,String,Boolean,Symbol,BigInt - Collections:
Array,Map,Set,WeakMap,WeakSet - Errors:
Error,TypeError,RangeError,SyntaxError,ReferenceError - Async:
Promise,Promise.all,Promise.allSettled,Promise.race,Promise.any - Iteration:
Iterator,IterableIterator,Generator - Reflection:
Object,Reflect,Proxy - JSON / Math / Date / RegExp
- Free functions:
isFinite,isNaN,parseInt,parseFloat,encodeURIComponent,decodeURIComponent,encodeURI,decodeURI
Array<T>, Promise<T>, Record<K, V>, and structural object types all work
as you'd expect. No any-everywhere required.
Web platform — fetch, URL, encoding, timers, abort
These are exposed by Bun's runtime and explicitly declared in the typecheck runtime-globals shim:
fetch,Request,Response,Headers,RequestInit,ResponseInit,Blob,FormDataURL,URLSearchParamssetTimeout,clearTimeout,setInterval,clearInterval,setImmediate,clearImmediate,queueMicrotaskAbortController,AbortSignalTextEncoder,TextDecoder,atob,btoa,structuredClonecrypto.randomUUID(),crypto.getRandomValues(...),crypto.subtle
The fetch available in the runtime IS the one Bun provides — same shape as
Node 18+'s undici fetch. Prefer ctx.stdlib.fetch / ctx.stdlib.fetchJson for
built-in retries; bare fetch works too.
For external APIs that need secrets, prefer
script connections for typed ctx.api /
ctx.mcp access, or the
scripts credential broker when you
truly need a hand-written fetch(). In both cases, avoid putting raw tokens in
source or args.
Console + logger
console.log / .warn / .error / .info / .debug are global. They also
hang off ctx.logger. Both write to the subprocess stderr (captured by the
executor and returned in the HTTP response's stderr field).
Node-compat surface
Bun's Node compatibility layer makes these available:
Buffer(Uint8Array-compatible, withBuffer.from(...),Buffer.concat(...), etc.)process.env— typed asRecord<string, string | undefined>. The environment is stripped to a small allowlist before the user script runs. You should NOT assume any specific env keys exist. Today's surviving keys:HOME,LANG,LC_ALL,PATH,TMPDIR,SWARM_SCRIPT_*(internal).process.platform,process.arch,process.version,process.cwd(),process.hrtime()
Swarm-specific imports
import { fetch, fetchJson, grep, glob, table, Redacted } from "stdlib";
// stdlib also flows through ctx.stdlib at runtimeimport type { ScriptContext, ScriptMain, SwarmConfig, Redacted } from "swarm-sdk";The ScriptContext passed to your default export carries:
ctx.swarm— typed SDK for in-swarm operations (memory, kv, tasks, scripts, repos, schedules). Method allowlist: seesrc/scripts-runtime/sdk-allowlist.ts.ctx.swarm.config—apiKey,agentId,mcpBaseUrl(all wrapped inRedacted<string>; unwrap viactx.stdlib.Redacted.value(...)).ctx.stdlib— the runtime helpers (fetch,fetchJson,grep,glob,table,Redacted).ctx.logger— Console-compatible logger; same destination as the globalconsole.
Allowed bare imports
The TypeScript import allowlist (src/scripts-runtime/import-allowlist.ts):
stdlibswarm-sdkzod— for declaringexport const argsSchema = z.object({...})
import "fs" / "node:fs" / "path" / any unlisted bare specifier is
rejected at upsert and at run time.
What the runtime does NOT provide
These are rejected by the typecheck and would also fail at runtime — they do NOT exist in the script sandbox:
- DOM APIs —
window,document,localStorage,sessionStorage,HTMLElement,Event, etc. The typecheck does NOT includelib.dom.d.ts. We did this on purpose; the runtime is not a browser. - Filesystem —
fs,node:fs,node:fs/promises. Usectx.stdlib.globandctx.stdlib.grepfor read-only file inspection inworkspace-rwmode (v2 only). - Subprocess —
node:child_process,Bun.spawn. Scripts cannot shell out. - Net —
node:net, raw TCP/UDP. Outbound HTTP viafetchonly. Bun.*globals — even though Bun is the runtime, scripts cannot access theBunobject directly. Usectx.stdlibinstead.
Typecheck diagnostics
When script-upsert rejects code, the response is structured:
{
"error": "typecheck_failed",
"diagnostics": ["...colorized full diagnostic..."],
"structured": [
{
"severity": "error",
"code": 2552,
"message": "Cannot find name 'Mat'. Did you mean 'Math'?",
"file": "/virtual/user-script.ts",
"line": 1,
"column": 28,
"endLine": 1,
"endColumn": 31,
"identifier": "Mat",
"suggestion": "Math"
}
]
}Each entry carries the TypeScript diagnostic code (TS2552, TS2304, …),
the precise location, the offending identifier when it's a name lookup, and
the compiler's "did you mean…" hint when one is offered.
Runtime errors
When a script throws at runtime, the HTTP response includes a
runtimeError field beside stderr:
{
"exitCode": 1,
"error": "eval_error",
"stderr": "Error: kaboom from line 4\n at user-script.ts:4:13",
"runtimeError": {
"name": "Error",
"message": "kaboom from line 4",
"stack": "Error: kaboom from line 4\n at .../user-script.ts:4:13\n ...",
"userFrames": [{ "file": "user-script.ts", "line": 4, "column": 13, "raw": "at user-script.ts:4:13" }],
"userScriptLine": 4,
"userScriptColumn": 13
}
}Stack frames inside the harness or node_modules are stripped from the
stderr text shown to clients (they remain in runtimeError.stack for
debugging). The user-script path is normalized to the basename
user-script.ts — absolute tmpdir paths never leak.
Exposing a script externally
A saved script can be opted in to a public HTTP endpoint —
POST /api/x/script/<id> — for callers
outside the swarm, with optional bearer auth and typed input validation
against the same argsJsonSchema this page describes.
When to escalate
If you find code that runs fine in the runtime but fails the typecheck, treat
it as a swarm bug, not a script bug. Add a probe to src/tests/scripts-typecheck.test.ts
and either expand the runtime-globals shim
(SCRIPT_RUNTIME_GLOBALS in src/be/scripts/typecheck.ts) or open an issue
with the failing snippet.
Cost & context computation
How cost and context-window numbers are computed across harness providers, and how to read the costSource / contextFormula badges in the UI.
Script connections
Register OpenAPI, GraphQL, and MCP APIs once — with optional OAuth or config-backed credentials — and call them from any swarm script via typed ctx.api / ctx.mcp clients.