libfx 0.0.7-dev.632.g9fe8a30c19a7 → 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 +130 -5
- package/core-output.js +58 -0
- package/fx-core.wasm +0 -0
- package/fx-sdk.js +234 -92
- 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 +47 -18
- package/node.cjs +2542 -0
- package/node.js +302 -61
- package/package.json +9 -3
- package/wasm-module.js +50 -0
package/README.md
CHANGED
|
@@ -41,8 +41,9 @@ await agent.close();
|
|
|
41
41
|
Agent configuration uses named options; `env` is reserved for
|
|
42
42
|
`createFxTerminal()`.
|
|
43
43
|
|
|
44
|
-
The host selects the model. Agent creation
|
|
45
|
-
|
|
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.
|
|
46
47
|
|
|
47
48
|
`onEvent` receives runtime diagnostics separately from model output. Transport
|
|
48
49
|
events report request start, response status and elapsed time, safe Gateway
|
|
@@ -59,6 +60,28 @@ returns an async iterable of normalized events:
|
|
|
59
60
|
- `tool_start`
|
|
60
61
|
- `tool_end`
|
|
61
62
|
|
|
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:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
const turn = agent.prompt("Update the index.");
|
|
70
|
+
for await (const _ of turn) {}
|
|
71
|
+
const result = await turn.result;
|
|
72
|
+
```
|
|
73
|
+
|
|
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.
|
|
78
|
+
|
|
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.
|
|
84
|
+
|
|
62
85
|
Only one prompt may run at a time. `checkpoint()` is idle-only and returns
|
|
63
86
|
opaque, bounded, versioned bytes. Restore them only when creating a fresh
|
|
64
87
|
agent:
|
|
@@ -67,6 +90,9 @@ agent:
|
|
|
67
90
|
const restored = await createFxAgent({ apiKey, model, checkpoint });
|
|
68
91
|
```
|
|
69
92
|
|
|
93
|
+
An already-aborted prompt signal returns `cancelled` without a model request
|
|
94
|
+
or a history change. The next prompt can run normally.
|
|
95
|
+
|
|
70
96
|
The checkpoint contains conversation history and usage only. The host owns
|
|
71
97
|
durable storage and must resupply models, credentials, instructions, tools,
|
|
72
98
|
MCP clients, and skill records.
|
|
@@ -112,6 +138,9 @@ const agent = await createFxAgent({
|
|
|
112
138
|
|
|
113
139
|
The JavaScript host is the authority for tool effects. The same descriptors,
|
|
114
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.
|
|
115
144
|
Instructions are limited to 64 KiB of UTF-8 text, including text assembled by
|
|
116
145
|
the MCP and skills adapters. They are the complete host-owned system context:
|
|
117
146
|
libfx adds no hidden base prompt, and omitting `instructions` sends no system
|
|
@@ -120,7 +149,19 @@ message.
|
|
|
120
149
|
## MCP
|
|
121
150
|
|
|
122
151
|
`libfx/mcp` accepts a host-owned MCP client. Transport, authentication,
|
|
123
|
-
elicitation, and cleanup remain outside the kernel.
|
|
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.
|
|
124
165
|
|
|
125
166
|
```js
|
|
126
167
|
import { createMcpAdapter } from "libfx/mcp";
|
|
@@ -165,13 +206,97 @@ await createFxAgent({ apiKey, backend: "native" }); // require N-API
|
|
|
165
206
|
await createFxAgent({ apiKey, backend: "wasm" }); // require Wasm + JSPI
|
|
166
207
|
```
|
|
167
208
|
|
|
168
|
-
|
|
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.
|
|
212
|
+
|
|
213
|
+
Use `getBackendInfo()` to inspect backend availability without creating an
|
|
214
|
+
Agent or terminal:
|
|
215
|
+
|
|
216
|
+
```js
|
|
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
|
+
// }
|
|
225
|
+
```
|
|
226
|
+
|
|
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:
|
|
237
|
+
|
|
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
|
|
169
255
|
creates a separate WebAssembly instance for every Agent. Agent memory, history,
|
|
170
256
|
tools, cancellation, and shutdown remain isolated. Workers and separate
|
|
171
|
-
processes maintain their own module caches.
|
|
257
|
+
processes maintain their own module caches, as do the ESM and CommonJS entries.
|
|
172
258
|
|
|
173
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.
|
|
174
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.
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
import { createFxAgent } from "libfx";
|
|
276
|
+
|
|
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
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
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.
|
|
175
300
|
|
|
176
301
|
## Interactive terminal
|
|
177
302
|
|
package/core-output.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const maxCoreMessageBytes = 64 * 1024 * 1024;
|
|
2
|
+
|
|
3
|
+
// One ordered writer per core; a pending handler holds admission of the next message.
|
|
4
|
+
export class CoreOutput {
|
|
5
|
+
constructor(handler) {
|
|
6
|
+
this.handler = handler;
|
|
7
|
+
this.decoder = new TextDecoder("utf-8", { fatal: true });
|
|
8
|
+
this.buffer = new Uint8Array(0);
|
|
9
|
+
this.bytes = 0;
|
|
10
|
+
this.closed = false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
write(chunk) {
|
|
14
|
+
let offset = 0;
|
|
15
|
+
const consume = () => {
|
|
16
|
+
while (offset < chunk.length) {
|
|
17
|
+
if (this.closed) throw new Error("core output is closed");
|
|
18
|
+
const newline = chunk.indexOf(10, offset);
|
|
19
|
+
const end = newline < 0 ? chunk.length : newline;
|
|
20
|
+
const fragment = chunk.subarray(offset, end);
|
|
21
|
+
const length = this.bytes + fragment.length;
|
|
22
|
+
if (length + (newline < 0 ? 0 : 1) > maxCoreMessageBytes) throw new RangeError("core output message exceeds 64 MiB");
|
|
23
|
+
let data = fragment;
|
|
24
|
+
if (this.bytes || newline < 0) {
|
|
25
|
+
if (length > this.buffer.length) {
|
|
26
|
+
const next = new Uint8Array(Math.min(maxCoreMessageBytes, Math.max(length, this.buffer.length * 2, 4096)));
|
|
27
|
+
next.set(this.buffer.subarray(0, this.bytes));
|
|
28
|
+
this.buffer = next;
|
|
29
|
+
}
|
|
30
|
+
this.buffer.set(fragment, this.bytes);
|
|
31
|
+
data = this.buffer.subarray(0, length);
|
|
32
|
+
}
|
|
33
|
+
this.bytes = length;
|
|
34
|
+
offset = newline < 0 ? end : end + 1;
|
|
35
|
+
if (newline < 0) return;
|
|
36
|
+
const line = this.decoder.decode(data);
|
|
37
|
+
const size = length + 1;
|
|
38
|
+
this.bytes = 0;
|
|
39
|
+
if (this.buffer.length > 1024 * 1024) this.buffer = new Uint8Array(0);
|
|
40
|
+
if (line) {
|
|
41
|
+
const pending = this.handler(JSON.parse(line), size);
|
|
42
|
+
if (pending) return Promise.resolve(pending).then(consume);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return consume();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
finish() {
|
|
50
|
+
if (this.bytes) throw new Error("core output ended within a message");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
close() {
|
|
54
|
+
this.closed = true;
|
|
55
|
+
this.buffer = new Uint8Array(0);
|
|
56
|
+
this.bytes = 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/fx-core.wasm
CHANGED
|
Binary file
|