Node SDK
The libfx package embeds the fx agent in a Node application. It runs as a native Node addon and falls back to WebAssembly when no compatible addon is available.
Install
npm install libfx
libfx requires Node 20 or later and has no dependencies.1
The SDK is early and changes with each fx release. The changelog records what each release adds or changes.
Choose an entry point
| Import | Environment | Loads |
|---|---|---|
libfx | Node or browser | Resolves to the entry point for the current environment |
libfx/node | Node | Native addon first, WebAssembly fallback |
libfx/browser | Browser | WebAssembly |
libfx/wasm | Node or browser | The WebAssembly host layer directly |
Every entry point exports createFxAgent(), createFxTerminal(), and supportsJspi(), along with the xterm.js helpers the WebAssembly SDK uses.
Choose a backend
The native addon implements only the agent core, so createFxAgent() runs natively while createFxTerminal() runs on WebAssembly and needs JavaScript Promise Integration (JSPI), even in the same Node process.
| Call | auto | native |
|---|---|---|
createFxAgent() | Native addon | Native addon |
createFxTerminal() | WebAssembly | Fails |
A backend of wasm runs WebAssembly for both calls, and every WebAssembly path needs JSPI.
backend defaults to auto, which prefers a compatible addon and falls back to WebAssembly. Set it when you would rather fail than run on the backend you did not plan for:
import { createFxAgent } from 'libfx'
const agent = await createFxAgent({ backend: 'native' })
The loader checks libfx.node first, then libfx.<platform>-<arch>.node. The package ships addons for linux-x64, linux-arm64, darwin-x64, and darwin-arm64. Windows has no packaged addon, so Node on Windows uses WebAssembly.
The unsuffixed libfx.node is the slot for an addon you build yourself. Pass one directly with the nativeAddon option, which accepts a module, a path, or a URL.
Handle a missing backend
Two error codes cover every failure to start.
LIBFX_JSPI_REQUIRED means no addon loaded and the runtime has no JSPI either. Run Node with --experimental-wasm-jspi, or install a package carrying an addon for your platform.
LIBFX_NATIVE_UNAVAILABLE means you asked for backend: 'native' on a call the addon does not implement, which today is createFxTerminal(). Use auto, or switch to createFxAgent().
Call supportsJspi() before choosing the WebAssembly backend rather than checking the Node version.
Run a headless agent
import { createFxAgent } from 'libfx'
const agent = await createFxAgent({
env: {
AI_GATEWAY_API_KEY: process.env.AI_GATEWAY_API_KEY,
},
onEvent(event) {
console.log(event.type)
},
async onPermission(request) {
return request.options[0].optionId
},
})
const session = await agent.createSession()
const turn = session.prompt('Review this schema for naming consistency: ...')
for await (const update of turn) {
console.log(update)
}
await agent.close()
The package README documents the full agent and session API, including openSession() and listSessions() for stored conversations, and setModel(), setMode(), and setConfigOption() for changing a session while it runs.
Each session runs one prompt at a time. Cancel a turn directly or with an AbortSignal:
const controller = new AbortController()
const turn = session.prompt('Wait for more instructions.', {
signal: controller.signal,
})
controller.abort()
console.log(await turn.stopReason) // "cancelled"
What the embedded agent can do
The embedded agent advertises no tools to the model. It cannot read files, run commands, start background processes, reach Model Context Protocol servers, or open the native secret store. A prompt returns text, and any capability beyond that comes from your host.
The embedded core is not the CLI
The empty tool set is a security boundary. Running inside your process does not grant the agent the authority the fx command line has.
The same boundary sets three more limits:
- Models come through Vercel AI Gateway only. The Codex and Grok providers are unavailable.
- There is no automatic permission reviewer.
onPermissiondecides every request. - The modes a session reports, including
code, do not change the empty tool set.
The runtime also caps values the command line leaves configurable: 64 agent steps per turn, 64 KiB per tool result, 100 turns of history, and no Gateway retries.
Supply host adapters
The native and WebAssembly backends share one JavaScript host layer, so a Node host passes the same options a browser host does:
| Option | Purpose |
|---|---|
env | Supplies runtime configuration without touching process globals |
fetch | Routes Gateway requests through your own client |
onEvent | Receives runtime, ACP, terminal, and lifecycle events |
onPermission | Resolves permission requests |
configStore | Persists accepted model and mode settings |
sessionStore | Persists conversations |
Browser integration carries the method-level contract for configStore and sessionStore. The adapters it marks as terminal-only, including the browser workspace and device login, have no effect on a Node agent.
Security boundaries
Treat nativeAddon and env.FX_GATEWAY_CHAT_URL as trusted configuration that your application sets. Neither should ever carry a value that arrived in a request, a tenant record, or user input, because both decide what code runs and where credentials go.
The native backend sends production credentials to the canonical Vercel AI Gateway endpoint. A custom FX_GATEWAY_CHAT_URL is accepted only as an explicit loopback HTTP address, which keeps local development possible without opening a path to an arbitrary host.
Footnotes
-
Due to npm limitations, one package carries the native addons for all four platforms along with both WebAssembly builds, so it unpacks to about 37 MB. ↩