libfx 0.0.7 → 0.0.8-dev.820.g1d9d3b63d6ea

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 CHANGED
@@ -1,361 +1,326 @@
1
1
  # libfx
2
2
 
3
- `libfx` embeds fx agents and interactive terminals in JavaScript
4
- applications. It supports Node.js hosts and browser environments with
5
- JavaScript Promise Integration (JSPI).
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
- Requirements:
14
-
15
- - Node.js 20 or later
16
- - Chrome or Edge 137 or later for browser WebAssembly
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
- | Import | Environment | Description |
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
- env: {
54
- AI_GATEWAY_API_KEY: process.env.AI_GATEWAY_API_KEY,
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.type);
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 session = await agent.createSession();
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 update of turn) {
70
- console.log(update);
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("Stopped:", await turn.stopReason);
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
- A prompt may be a string or an array of text and resource blocks:
80
-
81
- ```js
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
- Image prompt blocks are not currently supported.
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
- ### Agent lifecycle
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
- The object returned by `createFxAgent()` provides:
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
- | Member | Description |
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
- A session provides:
58
+ - `text_delta`
59
+ - `reasoning_delta` when supplied by the provider
60
+ - `tool_start`
61
+ - `tool_end`
110
62
 
111
- | Member | Description |
112
- | --- | --- |
113
- | `prompt(input, options?)` | Starts an async iterable turn |
114
- | `setModel(model)` | Changes the active model |
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 controller = new AbortController();
127
- const turn = session.prompt("Wait for more instructions.", {
128
- signal: controller.signal,
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
- ## Browser agent
136
-
137
- Browser hosts always use WebAssembly.
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
- const agent = await createFxAgent({
150
- env: {
151
- AI_GATEWAY_API_KEY: "<short-lived credential>",
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
- const session = await agent.createSession();
156
- const turn = session.prompt("Describe this workspace.");
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
- for await (const update of turn) {
159
- console.log(update);
160
- }
89
+ ```js
90
+ const restored = await createFxAgent({ apiKey, model, checkpoint });
161
91
  ```
162
92
 
163
- The browser entry point resolves `fx-core.wasm` and `fx-term.wasm` relative to
164
- the installed package. Pass `wasm` explicitly to provide a URL, `Response`,
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
- ## Interactive terminal
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
- Install xterm.js in the host application:
173
-
174
- ```sh
175
- npm install @xterm/xterm @xterm/addon-fit
176
- ```
100
+ ## Models
177
101
 
178
- Create the terminal and connect it to fx:
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 { Terminal } from "@xterm/xterm";
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 terminal = new Terminal({
195
- cursorBlink: true,
196
- scrollback: 10_000,
108
+ const models = await listModels({
109
+ apiKey: process.env.AI_GATEWAY_API_KEY,
197
110
  });
111
+ ```
198
112
 
199
- const fit = new FitAddon();
200
- terminal.loadAddon(fit);
201
- terminal.open(document.querySelector("#terminal"));
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
- const runtime = await createFxTerminal({
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
- window.addEventListener("resize", () => {
214
- fit.fit();
215
- runtime.resize();
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 terminal runtime provides:
220
-
221
- | Member | Description |
222
- | --- | --- |
223
- | `interactive` | Resolves after the terminal is ready for input |
224
- | `exited` | Resolves with the terminal exit code |
225
- | `write(data)` | Writes input directly to fx |
226
- | `resize()` | Notifies fx of terminal geometry changes |
227
- | `abort()` | Stops the terminal and releases subscriptions |
228
-
229
- Try the hosted terminal at [fx.sh/try](https://fx.sh/try).
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
- ## Backend selection
166
+ ```js
167
+ import { createMcpAdapter } from "libfx/mcp";
232
168
 
233
- Node hosts may select a backend explicitly:
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
- backend: "native",
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
- ```text
250
- libfx.<platform>-<arch>.node
182
+ // ...
183
+ await agent.close();
184
+ await mcp.close();
251
185
  ```
252
186
 
253
- Supported packaged targets:
187
+ ## Skills
254
188
 
255
- - `linux-x64`
256
- - `linux-arm64`
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
- error.code === "LIBFX_JSPI_REQUIRED"
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
- On Node versions where JSPI remains behind a flag, start the process with:
201
+ ## Backends
268
202
 
269
- ```sh
270
- node --experimental-wasm-jspi app.mjs
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
- ## Host integrations
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
- Hosts may provide adapters for runtime state and external effects:
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
- { action: "exec", command }
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
- The host remains responsible for admitting commands, enforcing limits, and
322
- returning bounded output.
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
- ## Local development
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.js 20+ is supported. Browser WebAssembly requires a JSPI-capable browser.
260
+ The Linux x64 and arm64 native addons require glibc 2.34 or newer. Native
261
+ agents do not require JSPI or experimental Node flags.
262
+ Some Node versions require `--experimental-wasm-jspi`.
263
+ Bun 1.4.2 is the tested recommendation for Bun's WebAssembly backend.
264
+ Bun 1.3.14 can crash when a hot WebAssembly loop resumes through JSPI during
265
+ JIT tier-up.
266
+
267
+ ### Next.js and Vercel
268
+
269
+ Create agents in a server route using the Node.js runtime. Import `libfx`
270
+ normally; the package includes its native assets and exposes both ESM and
271
+ CommonJS Node entrypoints. No `serverExternalPackages` setting or manual native
272
+ file inclusion is required.
325
273
 
326
- From the fx repository root, build the native addon and both WebAssembly
327
- surfaces:
274
+ ```js
275
+ import { createFxAgent } from "libfx";
328
276
 
329
- ```sh
330
- zig build -Dnapi-surface=core -Doptimize=ReleaseSafe
331
- zig build -Dwasm-surface=core -Doptimize=ReleaseSmall
332
- zig build -Dwasm-surface=term -Doptimize=ReleaseSmall
277
+ export const runtime = "nodejs";
278
+
279
+ export async function POST(request) {
280
+ const { prompt } = await request.json();
281
+ const agent = await createFxAgent({ apiKey: process.env.AI_GATEWAY_API_KEY });
282
+ try {
283
+ let text = "";
284
+ const turn = agent.prompt(prompt, { signal: request.signal });
285
+ for await (const event of turn) {
286
+ if (event.type === "text_delta") text += event.delta;
287
+ }
288
+ await turn.result;
289
+ return Response.json({ text });
290
+ } finally {
291
+ await agent.close();
292
+ }
293
+ }
333
294
  ```
334
295
 
335
- Run the SDK test suites:
296
+ Use the application's normal authentication and request limits around the
297
+ route. JavaScript tools and MCP clients remain host-owned and must be supplied
298
+ when creating an agent, including after checkpoint restoration. The native
299
+ backend does not enable the CLI's built-in shell or filesystem tools.
336
300
 
337
- ```sh
338
- npm ci --prefix sdk/node
339
- npm run --prefix sdk test:node-napi
340
- npm run --prefix sdk test:node-wasm
341
- ```
301
+ ## Interactive terminal
342
302
 
343
- Serve the repository:
303
+ `createFxTerminal()` remains a separate terminal harness API. In browsers,
304
+ connect it to xterm.js with `xtermAdapter()`:
344
305
 
345
- ```sh
346
- python3 -m http.server 8080
347
- ```
306
+ ```js
307
+ import { createFxTerminal, xtermAdapter } from "libfx/browser";
348
308
 
349
- After starting the server, open these local URLs:
309
+ const runtime = await createFxTerminal({
310
+ terminal: xtermAdapter(term),
311
+ env: { AI_GATEWAY_API_KEY: "<short-lived credential>" },
312
+ });
350
313
 
351
- ```text
352
- Core debugger: http://localhost:8080/sdk/index.html
353
- Interactive terminal: http://localhost:8080/sdk/term-demo.html
314
+ await runtime.interactive;
354
315
  ```
355
316
 
356
- These are local development pages and are not publicly hosted links.
317
+ The terminal runtime exposes `interactive`, `exited`, `write`, `resize`, and
318
+ `abort`. Terminal session, config, OAuth, prompt-history, URL, and workspace
319
+ stores remain terminal-only host integrations.
357
320
 
358
- Maintainer references:
321
+ ## Security
359
322
 
360
- - [SDK contributor guide](https://github.com/vercel-labs/fx/blob/main/sdk/AGENTS.md)
361
- - [Native Node-API design and security model](https://github.com/vercel-labs/fx/blob/main/sdk/NAPI.md)
323
+ Treat `nativeAddon` and `gatewayChatUrl` as trusted host
324
+ configuration. Do not embed long-lived credentials in public browser code.
325
+ Host tool functions, MCP clients, and skill loaders retain their own authority;
326
+ 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;