Zapcode
Run AI-generated code. Safely. Instantly.
A minimal, secure TypeScript interpreter written in Rust for use by AI agents
______________________________________________________________________
实验性 --Zapcode正在积极开发中。API可能会发生变化。
为什么代理应该编写代码
AI代理在以下情况下更有能力 写代码 而不是链接工具调用。代码为代理提供了循环、条件、变量和组合——这些都是工具链模拟不佳的东西。
但运行人工智能生成的代码既危险又缓慢。
Docker增加了200-500ms的冷启动延迟,并需要容器运行时。V8隔离带来了约20MB的二进制和毫秒级启动。两者都不支持快照执行中间功能。
Zapcode采用了一种不同的方法:一个专门构建的TypeScript解释器,从 2微秒,在语言级别强制执行安全沙箱,并可以将执行状态快照为字节以供以后恢复——所有这些都在一个单一的、可嵌入的库中,对Node.js或V8没有依赖关系。
受启发于 蒙蒂Pydantic的Python子集解释器,对Python采用相同的方法。
替代方案
| 语言完整性 | 安全性 | 启动 | 快照 | 安装 | |
|---|---|---|---|---|---|
| Zapcode | TypeScript子集 | 语言级沙盒 | 约2µs | 内置,\ sum + i.price * i.qty, 0); |
({ total, names: items.map(i => i.name) }) `); console.log(processor.run().output); // { total: 127.96, names: ["Widget", "Gadget"] }
// External function (snapshot/resume) const app = new Zapcode(const data = await fetch(url); data, { inputs: ['url'], externalFunctions: ['fetch'], }); const state = app.start({ url: 'https://api.example.com' }); if (!state.completed) { console.log(state.functionName); // "fetch" const snapshot = ZapcodeSnapshotHandle.load(state.snapshot); const final_ = snapshot.resume({ status: 'ok' }); console.log(final_.output); // { status: "ok" } }
看 [`examples/typescript/basic/main.ts`](examples/typescript/basic/main.ts) 更多。
### python
from zapcode import Zapcode, ZapcodeSnapshot
Simple expression
b = Zapcode("1 + 2 * 3") print(b.run()["output"]) # 7
With inputs
b = Zapcode( 'Hello, ${name}!', inputs=["name"], ) print(b.run({"name": "Zapcode"})["output"]) # "Hello, Zapcode!"
External function (snapshot/resume)
b = Zapcode( "const w = await getWeather(city); ${city}: ${w.temp}°C", inputs=["city"], external_functions=["getWeather"], ) state = b.start({"city": "London"}) if state.get("suspended"): result = state["snapshot"].resume({"condition": "Cloudy", "temp": 12}) print(result["output"]) # "London: 12°C"
Snapshot persistence
state = b.start({"city": "Tokyo"}) if state.get("suspended"): bytes_ = state["snapshot"].dump() # serialize to bytes restored = ZapcodeSnapshot.load(bytes_) # load from bytes result = restored.resume({"condition": "Clear", "temp": 26})
看 [`examples/python/basic/main.py`](examples/python/basic/main.py) 更多。
Rust
use zapcode_core::{ZapcodeRun, Value, ResourceLimits, VmState};
// Simple expression let runner = ZapcodeRun::new( "1 + 2 * 3".to_string(), vec![], vec![], ResourceLimits::default(), )?; assert_eq!(runner.run_simple()?, Value::Int(7));
// With inputs and external functions (snapshot/resume) let runner = ZapcodeRun::new( r#"const weather = await getWeather(city); ${city}: ${weather.condition}, ${weather.temp}°C"#.to_string(), vec!["city".to_string()], vec!["getWeather".to_string()], ResourceLimits::default(), )?;
let state = runner.start(vec![ ("city".to_string(), Value::String("London".into())), ])?;
if let VmState::Suspended { snapshot, .. } = state { let weather = Value::Object(indexmap::indexmap! { "condition".into() => Value::String("Cloudy".into()), "temp".into() => Value::Int(12), }); let final_state = snapshot.resume(weather)?; // VmState::Complete("London: Cloudy, 12°C") }
看 [`examples/rust/basic/basic.rs`](examples/rust/basic/basic.rs) 更多。
WebAssembly (browser)
import init, { Zapcode } from './zapcode-wasm/zapcode_wasm.js';
await init();
const b = new Zapcode( const items = [10, 20, 30]; items.map(x => x * 2).reduce((a, b) => a + b, 0) ); const result = b.run(); console.log(result.output); // 120
看 [`examples/wasm/basic/index.html`](examples/wasm/basic/index.html) 一个完整的游乐场。
## AI代理使用
### Vercel AI SDK(@uncartedfr/zapcode-AI)
npm install @unchartedfr/zapcode-ai ai @ai-sdk/anthropic # or @ai-sdk/amazon-bedrock, @ai-sdk/openai
推荐的方式——一个电话给你 `{ system, tools }` 直接插入 `generateText` / `streamText`:
import { zapcode } from "@unchartedfr/zapcode-ai"; import { generateText } from "ai"; import { anthropic } from "@ai-sdk/anthropic";
const { system, tools } = zapcode({ system: "You are a helpful travel assistant.", tools: { getWeather: { description: "Get current weather for a city", parameters: { city: { type: "string", description: "City name" } }, execute: async ({ city }) => { const res = await fetch(https://api.weather.com/${city}); return res.json(); }, }, searchFlights: { description: "Search flights between two cities", parameters: { from: { type: "string" }, to: { type: "string" }, date: { type: "string" }, }, execute: async ({ from, to, date }) => { return flightAPI.search(from, to, date); }, }, }, });
// Works with any AI SDK model — Anthropic, OpenAI, Google, etc. const { text } = await generateText({ model: anthropic("claude-sonnet-4-20250514"), system, tools, messages: [{ role: "user", content: "Weather in Tokyo and cheapest flight from London?" }], });
幕后:LLM编写TypeScript代码来调用你的工具→ Zapcode在沙箱中执行它→ 工具调用挂起VM→ your `execute` 函数在主机上运行→ 结果返回。全部在约2µs的启动时间+工具执行时间内完成。
看 [`examples/typescript/ai-agent/ai-agent-zapcode-ai.ts`](examples/typescript/ai-agent/ai-agent-zapcode-ai.ts) 对于完整的工作示例。
Anthropic SDK
**TypeScript:**
import Anthropic from "@anthropic-ai/sdk"; import { Zapcode, ZapcodeSnapshotHandle } from "@unchartedfr/zapcode";
const tools = { getWeather: async (city: string) => { const res = await fetch(https://api.weather.com/${city}); return res.json(); }, };
const client = new Anthropic(); const response = await client.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 1024, system: Write TypeScript to answer the user's question. Available functions (use await): getWeather(city: string) → { condition, temp } Last expression = output. No markdown fences., messages: [{ role: "user", content: "What's the weather in Tokyo?" }], });
const code = response.content[0].type === "text" ? response.content[0].text : "";
// Execute + resolve tool calls via snapshot/resume const sandbox = new Zapcode(code, { externalFunctions: ["getWeather"] }); let state = sandbox.start(); while (!state.completed) { const result = await toolsstate.functionName; state = ZapcodeSnapshotHandle.load(state.snapshot).resume(result); } console.log(state.output);
**python**
import anthropic from zapcode import Zapcode
client = anthropic.Anthropic() response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, system="""Write TypeScript to answer the user's question. Available functions (use await): getWeather(city: string) → { condition, temp } Last expression = output. No markdown fences.""", messages=[{"role": "user", "content": "What's the weather in Tokyo?"}], ) code = response.content[0].text
sandbox = Zapcode(code, external_functions=["getWeather"]) state = sandbox.start() while state.get("suspended"): result = get_weather(*state["args"]) state = state["snapshot"].resume(result) print(state["output"])
看 [`examples/typescript/ai-agent/ai-agent-anthropic.ts`](examples/typescript/ai-agent/ai-agent-anthropic.ts) 和 [`examples/python/ai-agent/ai_agent_anthropic.py`](examples/python/ai-agent/ai_agent_anthropic.py).
Multi-SDK support
`zapcode()` 通过一次调用返回所有主要AI SDK的适配器:
const { system, tools, openaiTools, anthropicTools, handleToolCall } = zapcode({ tools: { getWeather: { ... } }, });
// Vercel AI SDK await generateText({ model: anthropic("claude-sonnet-4-20250514"), system, tools, messages });
// OpenAI SDK await openai.chat.completions.create({ messages: [{ role: "system", content: system }, ...userMessages], tools: openaiTools, });
// Anthropic SDK await anthropic.messages.create({ system, tools: anthropicTools, messages });
// Any SDK — just extract the code from the tool call and pass it to handleToolCall const result = await handleToolCall(codeFromToolCall);
b = zapcode(tools={...}) b.anthropic_tools # → Anthropic SDK format b.openai_tools # → OpenAI SDK format b.handle_tool_call(code) # → Universal handler
Custom adapters
无需分叉Zapcode,即可为任何AI SDK构建自定义适配器:
import { zapcode, createAdapter } from "@unchartedfr/zapcode-ai";
const myAdapter = createAdapter("my-sdk", (ctx) => { return { systemMessage: ctx.system, actions: [{ id: ctx.toolName, schema: ctx.toolSchema, run: async (input: { code: string }) => { return ctx.handleToolCall(input.code); }, }], }; });
const { custom } = zapcode({ tools: { ... }, adapters: [myAdapter], });
const myConfig = custom["my-sdk"];
from zapcode_ai import zapcode, Adapter, AdapterContext
class LangChainAdapter(Adapter): name = "langchain"
def adapt(self, ctx: AdapterContext): from langchain_core.tools import StructuredTool return StructuredTool.from_function( func=lambda code: ctx.handle_tool_call(code), name=ctx.tool_name, description=ctx.tool_description, )
b = zapcode(tools={...}, adapters=[LangChainAdapter()]) langchain_tool = b.custom["langchain"]
适配器接收 `AdapterContext` 包含所需的一切:系统提示、工具名称、工具JSON模式和 `handleToolCall` 功能。返回SDK所期望的任何形状。
## 自动修复、调试和执行跟踪
### 自动修复(`autoFix`)
启用后,执行错误将作为工具结果返回,而不是抛出——让LLM看到错误并在下一步进行自我纠正。
**TypeScript:**
const { system, tools } = zapcode({ autoFix: true, tools: { /* ... */ }, });
**python**
zap = zapcode(auto_fix=True, tools={...})
### 执行跟踪
每次执行都会生成一个跟踪树,其中包含每个阶段的时间(解析→ 编译→ 执行)。使用 `printTrace()` / `print_trace()` 显示完整的会话跟踪,或 `getTrace()` / `get_trace()` 以编程方式访问跟踪。
**TypeScript:**
const { system, tools, printTrace, getTrace } = zapcode({ autoFix: true, tools: { /* ... */ }, });
// After running... printTrace(); // ✓ zapcode.session 12.3ms // ✓ execute_code 8.1ms // ✓ parse 0.2ms // ✓ compile 0.1ms // ✓ execute 7.8ms
const trace = getTrace(); // TraceSpan tree
**python**
zap = zapcode(auto_fix=True, tools={...})
After running...
zap.print_trace() trace = zap.get_trace() # TraceSpan tree
### 调试日志
有关生成代码、工具调用和输出的详细日志记录,请参阅调试跟踪示例,其中显示了如何检查每个执行步骤:
- [TypeScript调试跟踪示例](examples/typescript/debug-tracing/main.ts)
- [Python调试跟踪示例](examples/python/debug-tracing/main.py)
## Zapcode能做什么和不能做什么
**可以做到:**
- 执行TypeScript的一个有用子集——变量、函数、类、生成器、async/await、闭包、解构、spread/rest、可选链接、null合并、模板文字、try/catch
- 在解析时通过以下方式删除TypeScript类型 [oxc](https://oxc.rs) --没有 `tsc` 需要
- 快照执行到字节,稍后恢复,即使在不同的进程或机器中也是如此
- 从Rust、Node.js、Python或WebAssembly调用
- 跟踪和限制资源——内存、分配、堆栈深度和挂钟时间
- 30+字符串方法,25+数组方法,以及Math、JSON、Object和Promise内置函数
- 异步回调 `.map()` 和 `.forEach()` --每个 `await` 按顺序挂起和恢复VM
**无法执行以下操作:**
- 运行任意npm包或完整的Node.js标准库
- 执行正则表达式(支持解析,不执行)
- 提供完整 `Promise` 语义学(`Promise.race`等等)-- `.then()`, `.catch()`, `.finally()`,以及 `Promise.all` 支持
- 运行需要以下条件的代码 `this` 在非课堂环境中
这些是有意的约束,而不是bug。Zapcode针对一个用例: **运行由AI代理编写的代码** 在一个安全的、可嵌入的沙箱中。
## 支持的语法
|功能|状态|
|---|---|
|变量(`const`, `let`)|支持|
|函数(声明、箭头、表达式)|支持|
|课程(`constructor`方法, `extends`, `super`, `static`)|支持|
|发电机(`function*`, `yield`, `.next()`)|支持|
|异步/等待|支持|
|控制流程(`if`, `for`, `while`, `do-while`, `switch`, `for-of`)|支持|
|尝试/抓住/终于, `throw` |支持|
|带有可变捕获的闭包|支持|
|解构(对象和数组)|支持|
|Spread/rest运算符|支持|
|可选链接(`?.`)|支持|
|无效凝聚(`??`)|支持|
|模板文字|支持|
|类型注释、接口、类型别名|在解析时被剥离|
|字符串方法(30+)|支持|
|数组方法(25+,包括 `map`, `filter`, `reduce`)|支持|
|异步回调 `.map()`, `.forEach()` |支持|
|数学、JSON、对象、Promise |支持|
| `import` / `require` / `eval` |已阻止(沙盒)|
|正则表达式|已解析,未执行|
| `var` 声明|不支持(使用 `let`/`const`) |
|装饰器|不支持|
| `Symbol`, `WeakMap`, `WeakSet` |不支持|
## 安全
运行人工智能生成的代码本质上是危险的。与在操作系统级别隔离的Docker不同,Zapcode在 **语言水平** --没有容器,没有进程边界,没有系统调用过滤器。沙盒的构造必须正确,而不是配置。
### 默认情况下拒绝沙盒
来宾代码在字节码VM内运行,无法访问主机:
|封锁|如何封锁|
|---|---|
|文件系统(`fs`, `path`)|没有 `std::fs` 在岩芯箱中|
|网络(`net`, `http`, `fetch`)|没有 `std::net` 在岩芯箱中|
|环境(`process.env`, `os`)|没有 `std::env` 在岩芯箱中|
| `eval`, `Function()`,动态导入|解析时被阻止|
| `import`, `require` |解析时被阻止|
| `globalThis`, `global` |解析时被阻止|
|原型污染|不适用-对象是普通的 `IndexMap` 价值观|
这 **仅** escape-chatch是您显式注册的外部函数。当客户机代码调用一个时,VM会挂起并返回一个快照——您的代码会解析调用,而不是客户机。
### 资源限制
|限制|默认值|可配置|
|---|---|---|
|内存|32 MB| `memory_limit_bytes` |
|执行时间|5秒| `time_limit_ms` |
|调用堆栈深度|512帧| `max_stack_depth` |
|堆分配|100000| `max_allocations` |
### 零 `unsafe` 代码
这 `zapcode-core` 板条箱包含 **零 `unsafe` 块**内存安全由Rust编译器保证。
Adversarial test suite — 65 tests across 19 attack categories
|攻击类别|测试|结果|
|---|---|---|
|原型污染(`Object.prototype`, `__proto__`)|4|已阻止|
|施工人员链式逃生(`({}).constructor.constructor(...)`)|3|已阻止|
| `eval`, `Function()`,间接求值,动态导入|5|解析时被阻止|
| `globalThis`, `process`, `require`, `import` |6|解析时被阻止|
|堆栈溢出(直接+相互递归)|2|被堆栈深度限制捕获|
|内存耗尽(巨大数组,字符串加倍)|4|被分配限制捕获|
|无限循环(`while(true)`, `for(;;)`)|2|受时间/分配限制|
|JSON炸弹(深度嵌套,巨大有效载荷)|2|深度有限(最多64个)|
|稀疏阵列攻击(`arr[1e9]`, `arr[MAX_SAFE_INTEGER]`)|3|增长上限(最大+1024)|
|toString/value强制期间的劫持|3|未被调用(按设计)|
|被阻止关键字的Unicode转义|2|被阻止|
|计算属性访问技巧|2|返回未定义|
|定时侧通道(`performance.now`)|1|已阻止|
|错误消息信息泄漏|3|未暴露主机路径/env|
|类型混淆攻击|4|正确的TypeError|
|Promise/Generator内部滥用|4|无法逃脱|
|负数组索引|2|返回未定义|
| `setTimeout`, `setInterval`, `Proxy`, `Reflect` |6 |已阻止|
| `with` 声明, `arguments.callee` |3 |已阻止|
cargo test -p zapcode-core --test security # run the security tests
**已知限制:**
- `Object.freeze()` 尚未实现——冻结的对象仍然可以进行变异(正确性差距,而不是沙盒逃逸)
- 用户定义的 `toString()`/`valueOf()` 在隐式类型强制过程中不调用(故意——防止注入)
## 建筑
TypeScript source │ ▼ ┌─────────┐ oxc_parser (fastest TS parser in Rust) │ Parse │──────────────────────────────────────────► Strip types └────┬────┘ ▼ ┌─────────┐ │ IR │ ZapcodeIR (statements, expressions, operators) └────┬────┘ ▼ ┌─────────┐ │ Compile │ Stack-based bytecode (~50 instructions) └────┬────┘ ▼ ┌─────────┐ │ VM │ Execute, snapshot at external calls, resume later └────┬────┘ ▼ Result / Suspended { snapshot }
## 贡献
git clone https://github.com/TheUncharted/zapcode.git cd zapcode ./scripts/dev-setup.sh # installs toolchain, builds, runs tests
## 许可证
麻省理工学院