libfx 0.0.7-dev.607.ga90fc3585568 → 0.0.7-dev.617.g49fc251a51aa
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 +16 -9
- package/fx-core.wasm +0 -0
- package/fx-sdk.js +58 -2
- 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/node.js +17 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,8 @@ filesystem read when imported.
|
|
|
19
19
|
import { createFxAgent } from "libfx";
|
|
20
20
|
|
|
21
21
|
const agent = await createFxAgent({
|
|
22
|
-
|
|
22
|
+
apiKey: process.env.AI_GATEWAY_API_KEY,
|
|
23
|
+
model: "google/gemini-2.5-flash-lite",
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
const turn = agent.prompt("Explain this project.");
|
|
@@ -33,6 +34,10 @@ const checkpoint = await agent.checkpoint();
|
|
|
33
34
|
await agent.close();
|
|
34
35
|
```
|
|
35
36
|
|
|
37
|
+
`apiKey` is required. `model` is optional and defaults to fx's built-in model.
|
|
38
|
+
Agent configuration uses named options; `env` is reserved for
|
|
39
|
+
`createFxTerminal()`.
|
|
40
|
+
|
|
36
41
|
`prompt(input, { signal? })` accepts a string or text/resource blocks. It
|
|
37
42
|
returns an async iterable of normalized events:
|
|
38
43
|
|
|
@@ -46,7 +51,7 @@ opaque, bounded, versioned bytes. Restore them only when creating a fresh
|
|
|
46
51
|
agent:
|
|
47
52
|
|
|
48
53
|
```js
|
|
49
|
-
const restored = await createFxAgent({
|
|
54
|
+
const restored = await createFxAgent({ apiKey, model, checkpoint });
|
|
50
55
|
```
|
|
51
56
|
|
|
52
57
|
The checkpoint contains conversation history and usage only. The host owns
|
|
@@ -57,6 +62,8 @@ MCP clients, and skill records.
|
|
|
57
62
|
|
|
58
63
|
```js
|
|
59
64
|
const agent = await createFxAgent({
|
|
65
|
+
apiKey,
|
|
66
|
+
model,
|
|
60
67
|
instructions: "Keep answers concise.",
|
|
61
68
|
tools: [{
|
|
62
69
|
name: "lookup",
|
|
@@ -70,7 +77,6 @@ const agent = await createFxAgent({
|
|
|
70
77
|
return database.get(input.key, { signal });
|
|
71
78
|
},
|
|
72
79
|
}],
|
|
73
|
-
env,
|
|
74
80
|
});
|
|
75
81
|
```
|
|
76
82
|
|
|
@@ -94,9 +100,10 @@ const mcp = await createMcpAdapter(client, {
|
|
|
94
100
|
});
|
|
95
101
|
|
|
96
102
|
const agent = await createFxAgent({
|
|
103
|
+
apiKey,
|
|
104
|
+
model,
|
|
97
105
|
tools: mcp.tools,
|
|
98
106
|
instructions: mcp.instructions,
|
|
99
|
-
env,
|
|
100
107
|
});
|
|
101
108
|
|
|
102
109
|
// ...
|
|
@@ -115,15 +122,15 @@ import { createSkillsAdapter } from "libfx/skills";
|
|
|
115
122
|
|
|
116
123
|
const record = await loadSkillFile("./skills/review/SKILL.md");
|
|
117
124
|
const skills = createSkillsAdapter([record]);
|
|
118
|
-
const agent = await createFxAgent({ ...skills
|
|
125
|
+
const agent = await createFxAgent({ apiKey, model, ...skills });
|
|
119
126
|
```
|
|
120
127
|
|
|
121
128
|
## Backends
|
|
122
129
|
|
|
123
130
|
```js
|
|
124
|
-
await createFxAgent({ backend: "auto" }); // native, then Wasm fallback
|
|
125
|
-
await createFxAgent({ backend: "native" }); // require N-API
|
|
126
|
-
await createFxAgent({ backend: "wasm" }); // require Wasm + JSPI
|
|
131
|
+
await createFxAgent({ apiKey, backend: "auto" }); // native, then Wasm fallback
|
|
132
|
+
await createFxAgent({ apiKey, backend: "native" }); // require N-API
|
|
133
|
+
await createFxAgent({ apiKey, backend: "wasm" }); // require Wasm + JSPI
|
|
127
134
|
```
|
|
128
135
|
|
|
129
136
|
Within one JavaScript realm, libfx compiles each stable Wasm source once and
|
|
@@ -156,7 +163,7 @@ stores remain terminal-only host integrations.
|
|
|
156
163
|
|
|
157
164
|
## Security
|
|
158
165
|
|
|
159
|
-
Treat `nativeAddon` and `
|
|
166
|
+
Treat `nativeAddon` and `gatewayChatUrl` as trusted host
|
|
160
167
|
configuration. Do not embed long-lived credentials in public browser code.
|
|
161
168
|
Host tool functions, MCP clients, and skill loaders retain their own authority;
|
|
162
169
|
libfx validates and sequences them but does not grant operating-system access.
|
package/fx-core.wasm
CHANGED
|
Binary file
|
package/fx-sdk.js
CHANGED
|
@@ -5,8 +5,59 @@ const workspaceInfoLimit = 4 * 1024;
|
|
|
5
5
|
const workspaceCommandLimit = 64 * 1024;
|
|
6
6
|
const workspaceOutputLimit = 64 * 1024;
|
|
7
7
|
const maxInstructionsBytes = 64 * 1024;
|
|
8
|
+
const maxApiKeyBytes = 64 * 1024;
|
|
9
|
+
const maxModelBytes = 1024;
|
|
10
|
+
const maxUrlBytes = 16 * 1024;
|
|
8
11
|
const streamReadsPerTaskYield = 32;
|
|
9
12
|
|
|
13
|
+
function boundedString(value, name, maxBytes, required) {
|
|
14
|
+
if (value === undefined && !required) return undefined;
|
|
15
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
16
|
+
throw new TypeError(`${name} ${required ? "is required and " : ""}must be a non-empty string`);
|
|
17
|
+
}
|
|
18
|
+
if (encoder.encode(value).length > maxBytes) {
|
|
19
|
+
throw new RangeError(`${name} exceeds the ${maxBytes} byte libfx limit`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function validateGatewayChatUrl(value) {
|
|
25
|
+
if (value === undefined) return;
|
|
26
|
+
boundedString(value, "gatewayChatUrl", maxUrlBytes, false);
|
|
27
|
+
let url;
|
|
28
|
+
try { url = new URL(value); } catch { throw new TypeError("gatewayChatUrl must be a valid URL"); }
|
|
29
|
+
if (url.username || url.password || url.hash) {
|
|
30
|
+
throw new TypeError("gatewayChatUrl must not contain credentials or a fragment");
|
|
31
|
+
}
|
|
32
|
+
if (url.href === "https://ai-gateway.vercel.sh/v3/ai/language-model") return;
|
|
33
|
+
const loopback = url.hostname === "127.0.0.1" || url.hostname === "[::1]" || url.hostname === "localhost";
|
|
34
|
+
if (url.protocol !== "http:" || !loopback || !url.port) {
|
|
35
|
+
throw new TypeError("gatewayChatUrl must use the canonical Gateway or explicit loopback HTTP");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeAgentOptions(value) {
|
|
40
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
41
|
+
throw new TypeError("createFxAgent() options must be an object");
|
|
42
|
+
}
|
|
43
|
+
const options = { ...value };
|
|
44
|
+
if (Object.hasOwn(options, "env")) {
|
|
45
|
+
throw new TypeError("createFxAgent() does not accept env; pass apiKey and model directly");
|
|
46
|
+
}
|
|
47
|
+
options.apiKey = boundedString(options.apiKey, "apiKey", maxApiKeyBytes, true);
|
|
48
|
+
options.model = boundedString(options.model, "model", maxModelBytes, false);
|
|
49
|
+
validateGatewayChatUrl(options.gatewayChatUrl);
|
|
50
|
+
return options;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function agentEnvironment(options) {
|
|
54
|
+
return {
|
|
55
|
+
AI_GATEWAY_API_KEY: options.apiKey,
|
|
56
|
+
...(options.model === undefined ? {} : { FX_MODEL: options.model }),
|
|
57
|
+
...(options.gatewayChatUrl === undefined ? {} : { FX_GATEWAY_CHAT_URL: options.gatewayChatUrl }),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
10
61
|
function validWorkspacePath(path) {
|
|
11
62
|
if (typeof path !== "string" || !path.startsWith("/") || path.includes("\0")) return false;
|
|
12
63
|
if (strictDecoder.decode(encoder.encode(path)) !== path) return false;
|
|
@@ -1014,7 +1065,7 @@ function base64ToBytes(value) {
|
|
|
1014
1065
|
}
|
|
1015
1066
|
|
|
1016
1067
|
export async function createFxAgent(options = {}) {
|
|
1017
|
-
options =
|
|
1068
|
+
options = normalizeAgentOptions(options);
|
|
1018
1069
|
const hostTools = normalizeHostTools(options.tools);
|
|
1019
1070
|
const instructions = normalizeInstructions(options.instructions);
|
|
1020
1071
|
const initialCheckpoint = checkpointBytes(options.checkpoint);
|
|
@@ -1047,7 +1098,12 @@ export async function createFxAgent(options = {}) {
|
|
|
1047
1098
|
return { content, isError, cancelled: controller.signal.aborted };
|
|
1048
1099
|
};
|
|
1049
1100
|
emit("runtime.start");
|
|
1050
|
-
const runtimeOptions = {
|
|
1101
|
+
const runtimeOptions = {
|
|
1102
|
+
...options,
|
|
1103
|
+
args: ["acp"],
|
|
1104
|
+
env: agentEnvironment(options),
|
|
1105
|
+
hostToolExecutor: executeHostTool,
|
|
1106
|
+
};
|
|
1051
1107
|
const runtime = options.runtimeFactory
|
|
1052
1108
|
? await options.runtimeFactory(runtimeOptions)
|
|
1053
1109
|
: await instantiate(runtimeOptions);
|
package/fx-term.wasm
CHANGED
|
Binary file
|
package/libfx.darwin-arm64.node
CHANGED
|
Binary file
|
package/libfx.darwin-x64.node
CHANGED
|
Binary file
|
package/libfx.linux-arm64.node
CHANGED
|
Binary file
|
package/libfx.linux-x64.node
CHANGED
|
Binary file
|
package/node.js
CHANGED
|
@@ -67,9 +67,8 @@ function validateNativeBackend(backend) {
|
|
|
67
67
|
const actualVersion = backend.libfxApiVersion ?? "missing";
|
|
68
68
|
throw new Error(`native addon API version ${actualVersion} is incompatible with libfx API version ${libfxApiVersion}`);
|
|
69
69
|
}
|
|
70
|
-
if (typeof backend.
|
|
71
|
-
|
|
72
|
-
throw new Error("native addon must export createFxAgent(), createCore(), or createFxTerminal()");
|
|
70
|
+
if (typeof backend.createCore !== "function" && typeof backend.createFxTerminal !== "function") {
|
|
71
|
+
throw new Error("native addon must export createCore() or createFxTerminal()");
|
|
73
72
|
}
|
|
74
73
|
return backend;
|
|
75
74
|
}
|
|
@@ -120,26 +119,8 @@ function wasmBytes(input) {
|
|
|
120
119
|
return pending;
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
function validateGatewayChatUrl(value) {
|
|
124
|
-
if (value === undefined) return;
|
|
125
|
-
if (typeof value !== "string") throw new TypeError("FX_GATEWAY_CHAT_URL must be a string");
|
|
126
|
-
let url;
|
|
127
|
-
try { url = new URL(value); } catch { throw new TypeError("FX_GATEWAY_CHAT_URL must be a valid URL"); }
|
|
128
|
-
if (url.username || url.password || url.hash) {
|
|
129
|
-
throw new TypeError("FX_GATEWAY_CHAT_URL must not contain credentials or a fragment");
|
|
130
|
-
}
|
|
131
|
-
if (url.href === "https://ai-gateway.vercel.sh/v3/ai/language-model") return;
|
|
132
|
-
const loopback = url.hostname === "127.0.0.1" || url.hostname === "[::1]" || url.hostname === "localhost";
|
|
133
|
-
if (url.protocol !== "http:" || !loopback || !url.port) {
|
|
134
|
-
throw new TypeError("FX_GATEWAY_CHAT_URL must use the canonical Gateway or explicit loopback HTTP");
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
122
|
function createNativeCoreRuntime(addon, options) {
|
|
139
|
-
const apiKey = options
|
|
140
|
-
const model = options.env?.FX_MODEL;
|
|
141
|
-
const gatewayChatUrl = options.env?.FX_GATEWAY_CHAT_URL;
|
|
142
|
-
validateGatewayChatUrl(gatewayChatUrl);
|
|
123
|
+
const { apiKey, model, gatewayChatUrl } = options;
|
|
143
124
|
const core = addon.createCore({
|
|
144
125
|
apiKey,
|
|
145
126
|
home: options.home ?? homedir(),
|
|
@@ -258,7 +239,6 @@ function createNativeAgent(addon, options) {
|
|
|
258
239
|
|
|
259
240
|
async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWasm, options) {
|
|
260
241
|
const { nativeAddon, backend = "auto", ...runtimeOptions } = options ?? {};
|
|
261
|
-
validateGatewayChatUrl(runtimeOptions.env?.FX_GATEWAY_CHAT_URL);
|
|
262
242
|
if (!new Set(["auto", "native", "wasm"]).has(backend)) {
|
|
263
243
|
throw new TypeError('backend must be "auto", "native", or "wasm"');
|
|
264
244
|
}
|
|
@@ -268,14 +248,11 @@ async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWas
|
|
|
268
248
|
if (backend !== "wasm") {
|
|
269
249
|
const native = await resolveNativeBackend(nativeAddon);
|
|
270
250
|
nativeError = native.error;
|
|
271
|
-
if (typeof native.backend?.[nativeMethod] === "function"
|
|
272
|
-
(surface === "agent" && typeof native.backend?.createCore === "function")) {
|
|
251
|
+
if (typeof native.backend?.[nativeMethod] === "function") {
|
|
273
252
|
nativeAttempted = true;
|
|
274
253
|
try {
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
return await createNativeAgent(native.backend, runtimeOptions);
|
|
254
|
+
if (surface === "agent") return await createNativeAgent(native.backend, runtimeOptions);
|
|
255
|
+
return await native.backend[nativeMethod](runtimeOptions);
|
|
279
256
|
} catch (error) {
|
|
280
257
|
nativeError = error;
|
|
281
258
|
if (backend === "native") throw error;
|
|
@@ -298,8 +275,17 @@ async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWas
|
|
|
298
275
|
});
|
|
299
276
|
}
|
|
300
277
|
|
|
301
|
-
export function createFxAgent(options = {}) {
|
|
302
|
-
|
|
278
|
+
export async function createFxAgent(options = {}) {
|
|
279
|
+
if (options != null && Object.hasOwn(Object(options), "env")) {
|
|
280
|
+
throw new TypeError("createFxAgent() does not accept env; pass apiKey and model directly");
|
|
281
|
+
}
|
|
282
|
+
return createWithFallback(
|
|
283
|
+
"agent",
|
|
284
|
+
"createCore",
|
|
285
|
+
createWasmAgent,
|
|
286
|
+
defaultCoreWasm,
|
|
287
|
+
options,
|
|
288
|
+
);
|
|
303
289
|
}
|
|
304
290
|
|
|
305
291
|
export function createFxTerminal(options = {}) {
|