This document provides a comprehensive compatibility matrix for the Pi extension runtime. It covers Node.js built-in module shims, Bun API surface, npm package stubs, and the Pi SDK -- everything an extension author needs to know about what works in the QuickJS-based runtime.
The extension runtime provides shims for standard Node.js built-in modules. Each shim runs inside the QuickJS sandbox and routes I/O through the capability-gated hostcall interface.
| Module | Coverage | Key APIs |
|---|---|---|
node:path |
Full | join, dirname, resolve, basename, relative, isAbsolute, extname, normalize, parse, format, sep, delimiter, posix |
node:os |
Full | homedir, tmpdir, hostname, platform, arch, type, release, cpus, totalmem, freemem, uptime, loadavg, networkInterfaces, userInfo, endianness, EOL, devNull, constants |
node:fs |
Partial | readFileSync, writeFileSync, existsSync, readdirSync, statSync, mkdirSync, rmdirSync, unlinkSync, renameSync, symlinkSync, readlinkSync, realpathSync, copyFileSync, appendFileSync, chmodSync/chownSync path checks, accessSync, lstatSync, openSync, closeSync, readSync, writeSync, fstatSync, createReadStream, createWriteStream, promises.*, callback variants |
node:buffer |
Full | Buffer.from, Buffer.alloc, Buffer.allocUnsafe, Buffer.concat, Buffer.isBuffer, Buffer.byteLength, toString(encoding) (utf8, hex, base64, latin1, ascii) |
node:events |
Full | EventEmitter class with on, once, off, emit, removeListener, removeAllListeners, listenerCount, listeners, prependListener |
node:util |
Full | inspect, promisify, callbackify, format, deprecate, inherits, debuglog, stripVTControlCharacters, types.*, TextEncoder, TextDecoder |
node:child_process |
Full | spawn, spawnSync, execSync, exec, execFile, execFileSync, fork (stub) |
node:crypto |
Partial | randomBytes, randomUUID, createHash (SHA-256/SHA-384/SHA-512, SHA-1, MD5), createHmac, timingSafeEqual, getHashes, pbkdf2/scrypt, AES-128-GCM/AES-256-GCM createCipheriv/createDecipheriv, Ed25519 sign/verify |
node:http |
Partial | request, get, createServer (stub), STATUS_CODES, METHODS, IncomingMessage, ClientRequest -- routes through pi.http() hostcall |
node:url |
Partial | URL (globalThis), URLSearchParams, parse, format, resolve, fileURLToPath, pathToFileURL |
node:stream |
Partial | Readable, Writable, Transform, PassThrough, Duplex, pipeline, finished |
node:net |
Stub | createConnection (stub), Socket (stub); createServer throws -- network APIs outside sandbox |
node:readline |
Partial | createInterface, promises.createInterface -- questions use pi.ui('input') when available and resolve to empty strings otherwise |
The filesystem shim uses a virtual filesystem (VFS) backed by an in-memory
Map. Host filesystem access is available through a fallback mechanism that
routes readFileSync and statSync calls through the hostcall boundary
(capability-gated by the read capability).
| API | Implementation | Notes |
|---|---|---|
readFileSync |
Real | VFS first, host fallback for real files |
writeFileSync |
Real | VFS-only (sandboxed) |
existsSync |
Real | VFS + host fallback |
statSync |
Real | VFS + host fallback |
readdirSync |
Real | VFS-only |
mkdirSync |
Real | VFS-only, supports recursive |
promises.readFile |
Real | Async wrapper around sync |
promises.writeFile |
Real | Async wrapper around sync |
promises.stat |
Real | Async wrapper around sync |
promises.mkdir |
Real | Async wrapper around sync |
promises.readdir |
Real | Async wrapper around sync |
promises.access |
Real | Async wrapper around sync |
promises.rm |
Real | Async wrapper around sync |
promises.rename |
Real | Async wrapper around sync |
promises.chmod/promises.chown/promises.utimes |
Partial | Path-checking no-ops; missing paths fail with ENOENT |
openSync/closeSync/readSync/writeSync |
Real | File descriptor API |
readlink/readlinkSync |
Real | VFS symlink target lookup |
chmodSync/chownSync |
Partial | Path-checking no-ops; missing paths fail with ENOENT |
createReadStream |
Real | Returns Readable stream |
createWriteStream |
Real | Returns Writable stream |
watch/watchFile |
Stub | No real file watching; existing paths return no-op watcher facades and missing paths fail with ENOENT |
Callback variants (readFile, writeFile, etc.) |
Real | 9 callback-style functions |
| API | Implementation | Notes |
|---|---|---|
randomBytes(n) |
Real | Uses QuickJS random source |
randomUUID() |
Real | v4 UUID generation |
createHash(algo) |
Real | SHA-256, SHA-384, SHA-512, SHA-1, MD5 via native hostcall |
createHmac(algo, key) |
Real | HMAC-SHA256, HMAC-SHA384, HMAC-SHA512, HMAC-SHA1, HMAC-MD5 |
timingSafeEqual(a, b) |
Real | Constant-time comparison |
getHashes() |
Real | Returns supported algorithm list |
pbkdf2/scrypt |
Real | Key derivation via native hostcall |
sign/verify |
Partial | Ed25519 with PKCS#8 private and SPKI public keys; RSA/ECDSA fail closed |
createCipheriv/createDecipheriv |
Partial | Buffered AES-128-GCM and AES-256-GCM with 12-byte IVs, 16-byte auth tags, optional AAD, and fail-closed unsupported algorithms |
createCipher/createDecipher |
Missing | Deprecated password-derived cipher APIs are not implemented |
All subprocess APIs route through the pi.exec() hostcall, which requires
the exec capability. The exec capability is denied by default in
Standard and Safe policy profiles.
| API | Implementation | Notes |
|---|---|---|
spawn(cmd, args, opts) |
Real | Returns ChildProcess with stdout/stderr streams |
spawnSync(cmd, args, opts) |
Real | Synchronous via __pi_exec_sync_native |
execSync(cmd, opts) |
Real | Shell execution, returns stdout |
exec(cmd, opts, cb) |
Real | Async shell execution |
execFile(file, args, opts, cb) |
Real | Async file execution |
execFileSync(file, args, opts) |
Real | Synchronous file execution |
fork(modulePath) |
Stub | Not supported in sandbox |
The runtime provides a Bun global object for compatibility with extensions
that target the Bun runtime.
| API | Status | Notes |
|---|---|---|
Bun.argv |
Supported | Process arguments array |
Bun.file(path) |
Supported | Returns object with exists(), text(), arrayBuffer(), json() |
Bun.write(dest, data) |
Supported | Write file via node:fs shim |
Bun.which(cmd) |
Supported | Locate command via which exec |
Bun.spawn(cmd, opts) |
Supported | Via node:child_process shim |
Bun.connect(...) |
Unsupported | Network connections outside sandbox |
Bun.listen(...) |
Unsupported | Server listeners outside sandbox |
Extensions that import npm packages get virtual module stubs. These provide
enough API surface for extensions to load and register without errors. Some
stubs are functional (e.g., uuid, shell-quote), while others are no-ops
for optional features.
| Package | Coverage | Key APIs |
|---|---|---|
uuid |
Functional | v4(), v7(), v1(), v3(), v5(), validate(), version() |
shell-quote |
Functional | parse(cmd), quote(args) |
diff |
Functional | createTwoFilesPatch, createPatch, diffLines, diffChars, diffWords |
dotenv |
Functional | config(opts), parse(src) |
ms |
Functional | Duration parsing (ms("2h") -> 7200000) |
glob |
Partial | globSync, glob, Glob class (basic *, ?, ** over VFS-known files) |
jsonwebtoken |
Partial | decode(), HS256/HS384/HS512 sign()/verify(); asymmetric algorithms fail closed |
| Package | Stubbed APIs | Reason |
|---|---|---|
chalk |
Passthrough (no color) | Terminal colors not applicable in QuickJS |
chokidar |
watch() returns no-op |
File watching not available |
jsdom |
JSDOM class (empty) |
DOM not available |
turndown |
TurndownService (passthrough) |
HTML-to-markdown not available |
node-pty |
spawn() returns no-op |
PTY not available in sandbox |
@opentelemetry/* |
No-op spans/metrics | Telemetry not collected |
@xterm/* |
No-op terminal | Terminal emulation not available |
vscode-languageserver-protocol |
Type constants only | LSP types for compatibility |
@sinclair/typebox |
Type schema builder |
JSON Schema construction |
@modelcontextprotocol/sdk |
Client, transport classes |
MCP client stubs |
c12 (config loader) |
define(), loadConfig() |
Config loading stub |
execa |
bash() returns empty |
Process execution via hostcall instead |
@anthropic-ai/sdk |
Anthropic class |
API client stub |
@anthropic-ai/bedrock-sdk |
SandboxManager |
Sandbox manager stub |
openai |
OpenAI class |
API client stub |
adm-zip |
AdmZip class |
Zip handling stub |
linkedom |
parseHTML() |
DOM parser stub |
@sourcegraph/scip-typescript |
scip.Index class |
Indexer stub |
The Pi SDK virtual module provides the primary extension API surface.
| Export | Type | Description |
|---|---|---|
keyHint(action, fallback) |
Function | Keyboard hint display helper |
compact(prep, model, key, instr, signal) |
Function | Context compaction |
completeSimple(model, prompt, opts) |
Function | Simple LLM completion |
fuzzyMatch(query, text, opts) |
Function | Fuzzy string matching (returns {score, positions}) |
fuzzyFilter(query, items, opts) |
Function | Filter items by fuzzy match score |
Text, Container, Markdown, Spacer |
Classes | TUI rendering components |
Editor, Box, SelectList, Input |
Classes | TUI input components |
Image, DynamicBorder, CancellableLoader |
Classes | TUI display components |
Key |
Object | Key binding constants |
CURSOR_MARKER |
String | Cursor position marker |
truncateToWidth, visibleWidth, wrapTextWithAnsi |
Functions | Text rendering utilities |
getEditorKeybindings |
Function | Editor key binding configuration |
VERSION, DEFAULT_MAX_LINES, DEFAULT_MAX_BYTES |
Constants | Runtime constants |
truncateHead, truncateTail |
Functions | Content truncation |
parseSessionEntries, convertToLlm, serializeConversation |
Functions | Session data utilities |
createBashTool, createReadTool, createWriteTool, etc. |
Functions | Tool factory functions |
getAgentDir, copyToClipboard |
Functions | System utilities |
highlightCode, getLanguageFromPath |
Functions | Code display helpers |
AssistantMessageComponent, ToolExecutionComponent, UserMessageComponent |
Classes | Message rendering |
SessionManager |
Class | Session state management |
| Export | Type | Description |
|---|---|---|
StringEnum(values) |
Function | Enum type builder |
calculateCost() |
Function | Token cost calculation (uses model.cost × usage tokens) |
getEnvApiKey(provider) |
Function | Capability-filtered environment key lookup |
getOAuthApiKey(provider) |
Function | Unsupported in PiJS; fails closed |
complete(model, messages, opts) |
Function | Unsupported in PiJS without a provider host bridge; fails closed |
completeSimple(model, prompt, opts) |
Function | Unsupported in PiJS without a provider host bridge; fails closed |
createAssistantMessageEventStream() |
Function | Async-iterable assistant event stream factory for extension providers |
streamSimpleAnthropic() |
Function | Unsupported in PiJS without a provider host bridge; fails closed |
streamSimpleOpenAIResponses() |
Function | Unsupported in PiJS without a provider host bridge; fails closed |
streamSimpleOpenAICompletions() |
Function | Unsupported in PiJS without a provider host bridge; fails closed |
getProviders(), getModel(provider, modelId), getModels(provider) |
Functions | Synchronous built-in registry lookup for bundled provider metadata; currently includes OpenAI Codex models needed by extension provider mirrors |
getApiProvider(api) |
Function | Synchronous provider bridge for known API IDs; stream calls route through the host provider bridge and fail closed if unavailable |
getModel(), getApiProvider(), getModels() with no lookup arguments |
Functions | Session/model host-context helpers; fail closed when no host bridge is configured |
Tested against a corpus of 223 real-world extensions:
| Source Tier | Pass | Fail | N/A | Total | Pass Rate |
|---|---|---|---|---|---|
| Official (pi-mono) | 56 | 4 | 6 | 66 | 93.3% |
| Community | 58 | 0 | 0 | 58 | 100%* |
| npm Registry | 63 | 12 | 0 | 75 | 84.0%* |
| Third-party GitHub | 20 | 3 | 0 | 23 | 87.0%* |
| Agents | 1 | 0 | 0 | 1 | 100%* |
*Community/npm/third-party extensions are tested via the compatibility validation pack, which uses a broader load-and-register test rather than the full differential oracle.
| Category | Count | Root Cause |
|---|---|---|
| Multi-file dependency resolution | 4 | Relative specifiers across files not yet supported |
| Missing npm package specifiers | 5 | Extensions import real npm packages not stubbed |
| Host-read policy denial | 4 | Extension reads files outside allowed root |
| Runtime shape/load errors | 4 | Miscellaneous load-time errors |
| Test fixture (not a real extension) | 1 | base_fixtures is test infrastructure |
| Surface | Pass | Fail | Total |
|---|---|---|---|
| Node.js APIs | 13 | 0 | 13 |
| Bun APIs | 5 | 2 | 7 |
Failing Bun APIs: Bun.connect and Bun.listen (network server/client
APIs outside the extension sandbox).
These features are intentionally not supported in the extension sandbox:
| Feature | Reason | Alternative |
|---|---|---|
Native Node addons (.node) |
Binary modules cannot run in QuickJS | Use hostcalls or WASM |
| Direct network sockets | Security boundary enforcement | Use pi.http() hostcall |
| Direct filesystem access | Capability-gated for safety | Use pi.tool("Read", ...) or node:fs shim |
Server listeners (net.createServer) |
Extensions are clients, not servers | Not applicable |
| Worker threads | QuickJS is single-threaded | Not applicable |
node:cluster |
Process clustering not available | Not applicable |
node:dgram |
UDP sockets outside sandbox | Not applicable |
node:tls |
TLS handled by host HTTP client | Use pi.http() with HTTPS |
- QuickJS runtime: embedded via
rquickjscrate - Node.js API target: Node 18+ compatibility subset
- Bun API target: Bun 1.x compatibility subset
- Extension API version:
1.0