libfx 0.0.7 → 0.0.8-dev.857.gba60fd94fa57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +251 -275
- package/browser.js +2 -1
- package/core-output.js +58 -0
- package/fx-core.wasm +0 -0
- package/fx-sdk.js +675 -211
- package/fx-term.wasm +0 -0
- package/libfx.darwin-arm64.node +0 -0
- package/libfx.darwin-x64.node +0 -0
- package/libfx.linux-arm64.node +0 -0
- package/libfx.linux-x64.node +0 -0
- package/mcp.js +118 -0
- package/node.cjs +2583 -0
- package/node.js +358 -87
- package/package.json +13 -4
- package/skills-node.js +29 -0
- package/skills.js +44 -0
- package/wasm-module.js +50 -0
package/README.md
CHANGED
|
@@ -1,361 +1,337 @@
|
|
|
1
1
|
# libfx
|
|
2
2
|
|
|
3
|
-
`libfx`
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
3
|
+
`libfx` is the small fx agent kernel for JavaScript hosts. One agent is one
|
|
4
|
+
in-memory conversation with three operations: `prompt`, `checkpoint`, and
|
|
5
|
+
`close`.
|
|
8
6
|
|
|
9
7
|
```sh
|
|
10
8
|
npm install libfx
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- JSPI when using the WebAssembly backend
|
|
18
|
-
- A Vercel AI Gateway credential or a host-provided authenticated `fetch`
|
|
19
|
-
|
|
20
|
-
The package includes:
|
|
21
|
-
|
|
22
|
-
- Native Node addons for Linux and macOS on x64 and arm64
|
|
23
|
-
- `fx-core.wasm` for headless agents
|
|
24
|
-
- `fx-term.wasm` for interactive terminals
|
|
25
|
-
- A dependency-free JavaScript host layer
|
|
26
|
-
|
|
27
|
-
## Exports
|
|
11
|
+
Node.js uses the native addon when available and falls back to WebAssembly.
|
|
12
|
+
Browsers use WebAssembly with JSPI. The default package has no runtime
|
|
13
|
+
dependencies and performs no MCP connection, skill scan, process spawn, or
|
|
14
|
+
filesystem read when imported.
|
|
28
15
|
|
|
29
|
-
|
|
30
|
-
| --- | --- | --- |
|
|
31
|
-
| `libfx` | Node.js or browser | Environment-aware default |
|
|
32
|
-
| `libfx/node` | Node.js | Native-first Node entry point |
|
|
33
|
-
| `libfx/browser` | Browser | WebAssembly browser entry point |
|
|
34
|
-
| `libfx/wasm` | Browser or Node.js | Direct WebAssembly host layer |
|
|
35
|
-
|
|
36
|
-
Public exports:
|
|
37
|
-
|
|
38
|
-
- `createFxAgent()` creates a headless ACP agent.
|
|
39
|
-
- `createFxTerminal()` runs the interactive fx terminal.
|
|
40
|
-
- `supportsJspi()` detects WebAssembly JSPI support.
|
|
41
|
-
- `xtermAdapter()` connects fx to an xterm.js terminal.
|
|
42
|
-
- `encodeXtermKeyEvent()` translates browser keyboard events into terminal input.
|
|
43
|
-
|
|
44
|
-
## Headless agent
|
|
45
|
-
|
|
46
|
-
The default Node entry point prefers the native addon and falls back to
|
|
47
|
-
WebAssembly when necessary.
|
|
16
|
+
## Agent
|
|
48
17
|
|
|
49
18
|
```js
|
|
50
19
|
import { createFxAgent } from "libfx";
|
|
51
20
|
|
|
52
21
|
const agent = await createFxAgent({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
22
|
+
apiKey: process.env.AI_GATEWAY_API_KEY,
|
|
23
|
+
model: "google/gemini-2.5-flash-lite",
|
|
56
24
|
onEvent(event) {
|
|
57
|
-
console.log(event.
|
|
58
|
-
},
|
|
59
|
-
async onPermission(request) {
|
|
60
|
-
// Return one of request.options[*].optionId to approve it.
|
|
61
|
-
// Returning null or undefined cancels the request.
|
|
62
|
-
return null;
|
|
25
|
+
if (event.type === "transport.response") console.log(event.elapsedMs);
|
|
63
26
|
},
|
|
64
27
|
});
|
|
65
28
|
|
|
66
|
-
const
|
|
67
|
-
const turn = session.prompt("Explain the files in this project.");
|
|
29
|
+
const turn = agent.prompt("Explain this project.");
|
|
68
30
|
|
|
69
|
-
for await (const
|
|
70
|
-
|
|
31
|
+
for await (const event of turn) {
|
|
32
|
+
if (event.type === "text_delta") process.stdout.write(event.delta);
|
|
71
33
|
}
|
|
72
34
|
|
|
73
|
-
console.log(
|
|
74
|
-
|
|
75
|
-
await session.close();
|
|
35
|
+
console.log(await turn.result); // { stopReason, usage }
|
|
36
|
+
const checkpoint = await agent.checkpoint();
|
|
76
37
|
await agent.close();
|
|
77
38
|
```
|
|
78
39
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const turn = session.prompt([
|
|
83
|
-
{ type: "text", text: "Summarize this file." },
|
|
84
|
-
{
|
|
85
|
-
type: "resource",
|
|
86
|
-
resource: {
|
|
87
|
-
uri: "file:///workspace/README.md",
|
|
88
|
-
text: readmeContents,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
]);
|
|
92
|
-
```
|
|
40
|
+
`apiKey` is required. `model` is optional and defaults to fx's built-in model.
|
|
41
|
+
Agent configuration uses named options; `env` is reserved for
|
|
42
|
+
`createFxTerminal()`.
|
|
93
43
|
|
|
94
|
-
|
|
44
|
+
The host selects the model. Agent creation does not fetch the Gateway model
|
|
45
|
+
catalog. Prompting can resolve model capabilities and context capacity through
|
|
46
|
+
the supplied `fetch`; fx caches that metadata for the agent.
|
|
95
47
|
|
|
96
|
-
|
|
48
|
+
`onEvent` receives runtime diagnostics separately from model output. Transport
|
|
49
|
+
events report request start, response status and elapsed time, safe Gateway
|
|
50
|
+
request metadata, and failures. Credentials and raw headers are never included.
|
|
97
51
|
|
|
98
|
-
|
|
52
|
+
libfx makes at most one automatic retry after a retryable transport failure and
|
|
53
|
+
only before model output or tool effects escape. Cancellation prevents a retry.
|
|
99
54
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
| `createSession()` | Creates a new active session |
|
|
103
|
-
| `openSession(id)` | Loads a stored session |
|
|
104
|
-
| `listSessions()` | Lists stored sessions |
|
|
105
|
-
| `close()` | Closes the active session and shuts down cleanly |
|
|
106
|
-
| `abort()` | Immediately aborts the runtime |
|
|
107
|
-
| `exited` | Promise that resolves with the process exit code |
|
|
55
|
+
`prompt(input, { signal? })` accepts a string or text/resource blocks. It
|
|
56
|
+
returns an async iterable of normalized events:
|
|
108
57
|
|
|
109
|
-
|
|
58
|
+
- `text_delta`
|
|
59
|
+
- `reasoning_delta` when supplied by the provider
|
|
60
|
+
- `tool_start`
|
|
61
|
+
- `tool_end`
|
|
110
62
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
| `setMode(mode)` | Changes the active mode |
|
|
116
|
-
| `setConfig(config)` | Applies multiple configuration values |
|
|
117
|
-
| `close()` | Closes the active session |
|
|
118
|
-
| `remove()` | Removes the stored session |
|
|
119
|
-
| `history` | Previously loaded session updates |
|
|
120
|
-
| `configOptions` | Current configurable values |
|
|
121
|
-
|
|
122
|
-
Each session allows one active prompt at a time. Cancel a turn directly or
|
|
123
|
-
with an `AbortSignal`:
|
|
63
|
+
Consume the turn while it runs, then await `turn.result`. Output is lossless and
|
|
64
|
+
backpressured: a slow reader pauses production instead of growing an unlimited
|
|
65
|
+
event queue. Awaiting only `turn.result` can wait for an unread stream to drain.
|
|
66
|
+
If you only need the result, explicitly discard events:
|
|
124
67
|
|
|
125
68
|
```js
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
controller.abort();
|
|
132
|
-
console.log(await turn.stopReason); // "cancelled"
|
|
69
|
+
const turn = agent.prompt("Update the index.");
|
|
70
|
+
for await (const _ of turn) {}
|
|
71
|
+
const result = await turn.result;
|
|
133
72
|
```
|
|
134
73
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```js
|
|
140
|
-
import {
|
|
141
|
-
createFxAgent,
|
|
142
|
-
supportsJspi,
|
|
143
|
-
} from "libfx/browser";
|
|
144
|
-
|
|
145
|
-
if (!supportsJspi()) {
|
|
146
|
-
throw new Error("This browser does not support WebAssembly JSPI.");
|
|
147
|
-
}
|
|
74
|
+
A turn has one event consumer. Breaking out of its iterator cancels the turn;
|
|
75
|
+
`turn.cancel()` and `agent.close()` also release blocked output. Transport or
|
|
76
|
+
message-decoding failures reject the result instead of returning success with
|
|
77
|
+
missing text.
|
|
148
78
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
79
|
+
Native transport buffers at most 8 MiB of output bytes. Unread SDK events apply
|
|
80
|
+
backpressure at 1 MiB of encoded messages or 256 events. One message can exceed
|
|
81
|
+
that threshold when the queue is empty; an individual encoded ACP message is
|
|
82
|
+
limited to 64 MiB on both backends. These are transport bounds, not a total
|
|
83
|
+
answer-size limit or a bound on retained conversation history.
|
|
154
84
|
|
|
155
|
-
|
|
156
|
-
|
|
85
|
+
Only one prompt may run at a time. `checkpoint()` is idle-only and returns
|
|
86
|
+
opaque, bounded, versioned bytes. Restore them only when creating a fresh
|
|
87
|
+
agent:
|
|
157
88
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
89
|
+
```js
|
|
90
|
+
const restored = await createFxAgent({ apiKey, model, checkpoint });
|
|
161
91
|
```
|
|
162
92
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
`ArrayBuffer`, typed array, or precompiled `WebAssembly.Module`.
|
|
166
|
-
|
|
167
|
-
Do not embed a long-lived API key in public browser code. Use a short-lived
|
|
168
|
-
credential or an authenticated server-side proxy.
|
|
93
|
+
An already-aborted prompt signal returns `cancelled` without a model request
|
|
94
|
+
or a history change. The next prompt can run normally.
|
|
169
95
|
|
|
170
|
-
|
|
96
|
+
The checkpoint contains conversation history and usage only. The host owns
|
|
97
|
+
durable storage and must resupply models, credentials, instructions, tools,
|
|
98
|
+
MCP clients, and skill records.
|
|
171
99
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
```sh
|
|
175
|
-
npm install @xterm/xterm @xterm/addon-fit
|
|
176
|
-
```
|
|
100
|
+
## Models
|
|
177
101
|
|
|
178
|
-
|
|
102
|
+
Model discovery is explicit and does not create an Agent or load native or Wasm
|
|
103
|
+
artifacts:
|
|
179
104
|
|
|
180
105
|
```js
|
|
181
|
-
import {
|
|
182
|
-
import { FitAddon } from "@xterm/addon-fit";
|
|
183
|
-
import "@xterm/xterm/css/xterm.css";
|
|
184
|
-
import {
|
|
185
|
-
createFxTerminal,
|
|
186
|
-
supportsJspi,
|
|
187
|
-
xtermAdapter,
|
|
188
|
-
} from "libfx/browser";
|
|
189
|
-
|
|
190
|
-
if (!supportsJspi()) {
|
|
191
|
-
throw new Error("This browser does not support WebAssembly JSPI.");
|
|
192
|
-
}
|
|
106
|
+
import { listModels } from "libfx";
|
|
193
107
|
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
scrollback: 10_000,
|
|
108
|
+
const models = await listModels({
|
|
109
|
+
apiKey: process.env.AI_GATEWAY_API_KEY,
|
|
197
110
|
});
|
|
111
|
+
```
|
|
198
112
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
fit.fit();
|
|
113
|
+
`listModels()` performs one bounded Gateway request and returns sorted, unique
|
|
114
|
+
language-model IDs. It accepts the same optional `fetch` override as the Agent
|
|
115
|
+
API.
|
|
203
116
|
|
|
204
|
-
|
|
205
|
-
terminal: xtermAdapter(terminal),
|
|
206
|
-
env: {
|
|
207
|
-
AI_GATEWAY_API_KEY: "<short-lived credential>",
|
|
208
|
-
},
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
await runtime.interactive;
|
|
117
|
+
## JavaScript tools and instructions
|
|
212
118
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
119
|
+
```js
|
|
120
|
+
const agent = await createFxAgent({
|
|
121
|
+
apiKey,
|
|
122
|
+
model,
|
|
123
|
+
instructions: "Keep answers concise.",
|
|
124
|
+
tools: [{
|
|
125
|
+
name: "lookup",
|
|
126
|
+
description: "Look up a value.",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: { key: { type: "string" } },
|
|
130
|
+
required: ["key"],
|
|
131
|
+
},
|
|
132
|
+
async execute(input, { signal }) {
|
|
133
|
+
return database.get(input.key, { signal });
|
|
134
|
+
},
|
|
135
|
+
}],
|
|
216
136
|
});
|
|
217
137
|
```
|
|
218
138
|
|
|
219
|
-
The
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
139
|
+
The JavaScript host is the authority for tool effects. The same descriptors,
|
|
140
|
+
schemas, cancellation, results, and events are used by N-API and WebAssembly.
|
|
141
|
+
Cancelling a prompt aborts its tools' signals and stops waiting for their
|
|
142
|
+
callbacks. Late results and rejections are ignored. Tools remain responsible
|
|
143
|
+
for stopping their own work when their signal is aborted.
|
|
144
|
+
Instructions are limited to 64 KiB of UTF-8 text, including text assembled by
|
|
145
|
+
the MCP and skills adapters. They are the complete host-owned system context:
|
|
146
|
+
libfx adds no hidden base prompt, and omitting `instructions` sends no system
|
|
147
|
+
message.
|
|
148
|
+
|
|
149
|
+
## MCP
|
|
150
|
+
|
|
151
|
+
`libfx/mcp` accepts a host-owned MCP client. Transport, authentication,
|
|
152
|
+
elicitation, and cleanup remain outside the kernel. The client uses the MCP
|
|
153
|
+
TypeScript SDK v1 signature: `callTool(params, resultSchema?, options?)`, with
|
|
154
|
+
cancellation passed in `options`. Tool text and structured data
|
|
155
|
+
reach the model together. PNG, JPEG, GIF, and WebP tool images reach models that
|
|
156
|
+
advertise image input support; other models receive an explicit omission notice.
|
|
157
|
+
Images are retained in checkpoints within the existing checkpoint size limit.
|
|
158
|
+
Each image may contain up to 5 MiB of base64 data, with at most eight images and
|
|
159
|
+
an 8 MiB result frame. Ordinary host tool objects remain JSON text. Resource and
|
|
160
|
+
prompt options supply text instructions; non-text context has an omission notice.
|
|
161
|
+
Tool catalogs are paginated up to the existing 64-tool bound. Tool names are
|
|
162
|
+
normalized for model APIs, with collisions kept distinct and original names used
|
|
163
|
+
for calls to the MCP client. Each tool description and JSON schema may contain up
|
|
164
|
+
to 64 KiB, within the control message's 8 MiB limit.
|
|
230
165
|
|
|
231
|
-
|
|
166
|
+
```js
|
|
167
|
+
import { createMcpAdapter } from "libfx/mcp";
|
|
232
168
|
|
|
233
|
-
|
|
169
|
+
const mcp = await createMcpAdapter(client, {
|
|
170
|
+
prefix: "github_",
|
|
171
|
+
resources: ["repo://instructions"],
|
|
172
|
+
prompts: ["review"],
|
|
173
|
+
});
|
|
234
174
|
|
|
235
|
-
```js
|
|
236
175
|
const agent = await createFxAgent({
|
|
237
|
-
|
|
176
|
+
apiKey,
|
|
177
|
+
model,
|
|
178
|
+
tools: mcp.tools,
|
|
179
|
+
instructions: mcp.instructions,
|
|
238
180
|
});
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
| Backend | Behavior |
|
|
242
|
-
| --- | --- |
|
|
243
|
-
| `auto` | Prefer a compatible native addon and fall back to WebAssembly |
|
|
244
|
-
| `native` | Require the native backend and fail if it cannot load |
|
|
245
|
-
| `wasm` | Require WebAssembly and JSPI |
|
|
246
|
-
|
|
247
|
-
The native loader checks `libfx.node` followed by the platform-specific addon:
|
|
248
181
|
|
|
249
|
-
|
|
250
|
-
|
|
182
|
+
// ...
|
|
183
|
+
await agent.close();
|
|
184
|
+
await mcp.close();
|
|
251
185
|
```
|
|
252
186
|
|
|
253
|
-
|
|
187
|
+
## Skills
|
|
254
188
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
- `darwin-x64`
|
|
258
|
-
- `darwin-arm64`
|
|
259
|
-
|
|
260
|
-
If no compatible native backend is available and JSPI cannot run, startup
|
|
261
|
-
rejects with:
|
|
189
|
+
Use `libfx/skills` for already-loaded records or `libfx/skills/node` to load a
|
|
190
|
+
`SKILL.md` explicitly in Node or Bun.
|
|
262
191
|
|
|
263
192
|
```js
|
|
264
|
-
|
|
193
|
+
import { loadSkillFile } from "libfx/skills/node";
|
|
194
|
+
import { createSkillsAdapter } from "libfx/skills";
|
|
195
|
+
|
|
196
|
+
const record = await loadSkillFile("./skills/review/SKILL.md");
|
|
197
|
+
const skills = createSkillsAdapter([record]);
|
|
198
|
+
const agent = await createFxAgent({ apiKey, model, ...skills });
|
|
265
199
|
```
|
|
266
200
|
|
|
267
|
-
|
|
201
|
+
## Backends
|
|
268
202
|
|
|
269
|
-
```
|
|
270
|
-
|
|
203
|
+
```js
|
|
204
|
+
await createFxAgent({ apiKey, backend: "auto" }); // native, then Wasm fallback
|
|
205
|
+
await createFxAgent({ apiKey, backend: "native" }); // require N-API
|
|
206
|
+
await createFxAgent({ apiKey, backend: "wasm" }); // require Wasm + JSPI
|
|
271
207
|
```
|
|
272
208
|
|
|
273
|
-
|
|
209
|
+
CommonJS applications can load the same Node API with `require("libfx")`. The
|
|
210
|
+
package chooses its generated CommonJS entry automatically and keeps asset
|
|
211
|
+
paths relative to the installed package.
|
|
274
212
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
| Option | Purpose |
|
|
278
|
-
| --- | --- |
|
|
279
|
-
| `fetch` | Routes Gateway requests through the host |
|
|
280
|
-
| `env` | Supplies runtime configuration without changing process globals |
|
|
281
|
-
| `onEvent` | Receives runtime, ACP, terminal, and lifecycle events |
|
|
282
|
-
| `onPermission` | Resolves agent permission requests |
|
|
283
|
-
| `configStore` | Persists accepted configuration values |
|
|
284
|
-
| `sessionStore` | Persists agent or terminal sessions |
|
|
285
|
-
| `oauthSessionStore` | Persists browser device-login sessions |
|
|
286
|
-
| `promptHistoryStore` | Stores terminal prompt history |
|
|
287
|
-
| `openUrl` | Opens authentication and verification URLs |
|
|
288
|
-
| `workspace` | Provides the constrained browser workspace adapter |
|
|
289
|
-
|
|
290
|
-
## Security boundaries
|
|
291
|
-
|
|
292
|
-
`nativeAddon` and `env.FX_GATEWAY_CHAT_URL` are trusted host configuration. Do
|
|
293
|
-
not populate them from request, tenant, or other untrusted input.
|
|
294
|
-
|
|
295
|
-
The native backend sends production credentials only to the canonical Vercel
|
|
296
|
-
AI Gateway endpoint. Custom Gateway endpoints are limited to explicit loopback
|
|
297
|
-
HTTP URLs for local development.
|
|
298
|
-
|
|
299
|
-
The WebAssembly runtime intentionally does not provide:
|
|
300
|
-
|
|
301
|
-
- Native processes
|
|
302
|
-
- OS sandboxing
|
|
303
|
-
- Native MCP servers
|
|
304
|
-
- Subagents or skills
|
|
305
|
-
- Automatic upgrades
|
|
306
|
-
- Clipboard integration
|
|
307
|
-
- Arbitrary WASI filesystem access
|
|
308
|
-
- Public web fetch, web search, and general outbound network access
|
|
309
|
-
|
|
310
|
-
The embedded runtime tells the model not to retry unavailable network work
|
|
311
|
-
through terminal commands. Use locally installed fx when the full native tool
|
|
312
|
-
suite is required.
|
|
313
|
-
|
|
314
|
-
The optional browser workspace exposes foreground terminal execution through
|
|
315
|
-
the typed contract:
|
|
213
|
+
Use `getBackendInfo()` to inspect backend availability without creating an
|
|
214
|
+
Agent or terminal:
|
|
316
215
|
|
|
317
216
|
```js
|
|
318
|
-
{
|
|
217
|
+
import { getBackendInfo } from "libfx";
|
|
218
|
+
|
|
219
|
+
const info = await getBackendInfo({ surface: "agent", backend: "auto" });
|
|
220
|
+
// {
|
|
221
|
+
// surface: "agent",
|
|
222
|
+
// backend: "native" | "wasm-jspi" | "unavailable",
|
|
223
|
+
// attempts: [{ backend, available, reason }]
|
|
224
|
+
// }
|
|
319
225
|
```
|
|
320
226
|
|
|
321
|
-
|
|
322
|
-
|
|
227
|
+
`surface` is `agent` by default and may also be `terminal`. `backend` has the
|
|
228
|
+
same `auto`, `native`, and `wasm` selection as the factories. `nativeAddon` and
|
|
229
|
+
`wasm` select explicit assets with the same meanings as their factory options.
|
|
230
|
+
The probe loads and validates the native module or compiles the selected Wasm
|
|
231
|
+
asset, then stops. It does not create a core, open a runtime socket, read
|
|
232
|
+
credentials, start a model request, or write session state. A remote Wasm
|
|
233
|
+
source can perform its normal asset fetch.
|
|
234
|
+
|
|
235
|
+
Expected environmental failures resolve as structured attempts. Invalid
|
|
236
|
+
options reject with `TypeError`. The stable reason codes are:
|
|
323
237
|
|
|
324
|
-
|
|
238
|
+
| Code | Meaning |
|
|
239
|
+
| --- | --- |
|
|
240
|
+
| `LIBFX_UNSUPPORTED_PLATFORM` | No packaged native addon supports the current platform and architecture. |
|
|
241
|
+
| `LIBFX_NATIVE_ARTIFACT_MISSING` | The selected native addon file is absent. |
|
|
242
|
+
| `LIBFX_NATIVE_LOAD_FAILED` | Node could not load the selected native addon. |
|
|
243
|
+
| `LIBFX_NATIVE_API_MISMATCH` | The addon API version is incompatible. |
|
|
244
|
+
| `LIBFX_NATIVE_SURFACE_MISSING` | The addon does not implement the selected Agent or terminal surface. |
|
|
245
|
+
| `LIBFX_NATIVE_DISABLED` | `nativeAddon: false` disabled native loading. |
|
|
246
|
+
| `LIBFX_JSPI_UNAVAILABLE` | The current JavaScript runtime does not provide JSPI. |
|
|
247
|
+
| `LIBFX_WASM_LOAD_FAILED` | The selected Wasm asset could not be loaded or compiled. |
|
|
248
|
+
|
|
249
|
+
The optional `causeCode` field retains a Node error code such as `ENOENT` or
|
|
250
|
+
`ERR_DLOPEN_FAILED` when one exists. Probe success establishes backend loading
|
|
251
|
+
only; it does not validate credentials, a future Agent initialization, or a
|
|
252
|
+
model request.
|
|
253
|
+
|
|
254
|
+
Within one loaded SDK module, libfx compiles each stable Wasm source once and
|
|
255
|
+
creates a separate WebAssembly instance for every Agent. Agent memory, history,
|
|
256
|
+
tools, cancellation, and shutdown remain isolated. Workers and separate
|
|
257
|
+
processes maintain their own module caches, as do the ESM and CommonJS entries.
|
|
258
|
+
|
|
259
|
+
Node factories and `getBackendInfo()` also accept a `wasm` Promise resolving
|
|
260
|
+
to an HTTP(S) URL string, a `Response`, Wasm bytes, or a compiled
|
|
261
|
+
`WebAssembly.Module`. Asset resolver failures propagate from factories and
|
|
262
|
+
appear as `LIBFX_WASM_LOAD_FAILED` in diagnostics.
|
|
263
|
+
|
|
264
|
+
Node.js 20+ is supported. Browser WebAssembly requires a JSPI-capable browser.
|
|
265
|
+
The Linux x64 and arm64 native addons require glibc 2.34 or newer. Native
|
|
266
|
+
agents do not require JSPI or experimental Node flags.
|
|
267
|
+
Some Node versions require `--experimental-wasm-jspi`.
|
|
268
|
+
Bun 1.4.2 is the tested recommendation for Bun's WebAssembly backend.
|
|
269
|
+
Bun 1.3.14 can crash when a hot WebAssembly loop resumes through JSPI during
|
|
270
|
+
JIT tier-up.
|
|
271
|
+
|
|
272
|
+
### Next.js and Vercel
|
|
273
|
+
|
|
274
|
+
Create agents in a server route using the Node.js runtime. Import `libfx`
|
|
275
|
+
normally; the package includes its native assets and exposes both ESM and
|
|
276
|
+
CommonJS Node entrypoints. Native agents support Next.js 15 with webpack and
|
|
277
|
+
Next.js 16 with webpack or Turbopack, without `serverExternalPackages` or manual
|
|
278
|
+
native-file inclusion. Webpack's emitted assets are resolved relative to the
|
|
279
|
+
server bundle, including standalone builds with a custom `distDir` or `assetPrefix`.
|
|
280
|
+
|
|
281
|
+
This native setup does not require JSPI. Explicit WebAssembly use still needs
|
|
282
|
+
JSPI and available Wasm assets; Next.js's standalone tracer excludes `.wasm`
|
|
283
|
+
files, so a standalone Wasm host must supply those assets separately.
|
|
325
284
|
|
|
326
|
-
|
|
327
|
-
|
|
285
|
+
```js
|
|
286
|
+
import { createFxAgent } from "libfx";
|
|
328
287
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
288
|
+
export const runtime = "nodejs";
|
|
289
|
+
|
|
290
|
+
export async function POST(request) {
|
|
291
|
+
const { prompt } = await request.json();
|
|
292
|
+
const agent = await createFxAgent({ apiKey: process.env.AI_GATEWAY_API_KEY });
|
|
293
|
+
try {
|
|
294
|
+
let text = "";
|
|
295
|
+
const turn = agent.prompt(prompt, { signal: request.signal });
|
|
296
|
+
for await (const event of turn) {
|
|
297
|
+
if (event.type === "text_delta") text += event.delta;
|
|
298
|
+
}
|
|
299
|
+
await turn.result;
|
|
300
|
+
return Response.json({ text });
|
|
301
|
+
} finally {
|
|
302
|
+
await agent.close();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
333
305
|
```
|
|
334
306
|
|
|
335
|
-
|
|
307
|
+
Use the application's normal authentication and request limits around the
|
|
308
|
+
route. JavaScript tools and MCP clients remain host-owned and must be supplied
|
|
309
|
+
when creating an agent, including after checkpoint restoration. The native
|
|
310
|
+
backend does not enable the CLI's built-in shell or filesystem tools.
|
|
336
311
|
|
|
337
|
-
|
|
338
|
-
npm ci --prefix sdk/node
|
|
339
|
-
npm run --prefix sdk test:node-napi
|
|
340
|
-
npm run --prefix sdk test:node-wasm
|
|
341
|
-
```
|
|
312
|
+
## Interactive terminal
|
|
342
313
|
|
|
343
|
-
|
|
314
|
+
`createFxTerminal()` remains a separate terminal harness API. In browsers,
|
|
315
|
+
connect it to xterm.js with `xtermAdapter()`:
|
|
344
316
|
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
```
|
|
317
|
+
```js
|
|
318
|
+
import { createFxTerminal, xtermAdapter } from "libfx/browser";
|
|
348
319
|
|
|
349
|
-
|
|
320
|
+
const runtime = await createFxTerminal({
|
|
321
|
+
terminal: xtermAdapter(term),
|
|
322
|
+
env: { AI_GATEWAY_API_KEY: "<short-lived credential>" },
|
|
323
|
+
});
|
|
350
324
|
|
|
351
|
-
|
|
352
|
-
Core debugger: http://localhost:8080/sdk/index.html
|
|
353
|
-
Interactive terminal: http://localhost:8080/sdk/term-demo.html
|
|
325
|
+
await runtime.interactive;
|
|
354
326
|
```
|
|
355
327
|
|
|
356
|
-
|
|
328
|
+
The terminal runtime exposes `interactive`, `exited`, `write`, `resize`, and
|
|
329
|
+
`abort`. Terminal session, config, OAuth, prompt-history, URL, and workspace
|
|
330
|
+
stores remain terminal-only host integrations.
|
|
357
331
|
|
|
358
|
-
|
|
332
|
+
## Security
|
|
359
333
|
|
|
360
|
-
|
|
361
|
-
|
|
334
|
+
Treat `nativeAddon` and `gatewayChatUrl` as trusted host
|
|
335
|
+
configuration. Do not embed long-lived credentials in public browser code.
|
|
336
|
+
Host tool functions, MCP clients, and skill loaders retain their own authority;
|
|
337
|
+
libfx validates and sequences them but does not grant operating-system access.
|
package/browser.js
CHANGED
|
@@ -3,11 +3,12 @@ import {
|
|
|
3
3
|
createFxTerminal as createWasmTerminal,
|
|
4
4
|
encodeXtermKeyEvent,
|
|
5
5
|
fxSdkApiVersion,
|
|
6
|
+
listModels,
|
|
6
7
|
supportsJspi,
|
|
7
8
|
xtermAdapter,
|
|
8
9
|
} from "./fx-sdk.js";
|
|
9
10
|
|
|
10
|
-
export { encodeXtermKeyEvent, fxSdkApiVersion, supportsJspi, xtermAdapter };
|
|
11
|
+
export { encodeXtermKeyEvent, fxSdkApiVersion, listModels, supportsJspi, xtermAdapter };
|
|
11
12
|
export const libfxApiVersion = 2;
|
|
12
13
|
|
|
13
14
|
const defaultCoreWasm = new URL("./fx-core.wasm", import.meta.url).href;
|