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 CHANGED
@@ -19,7 +19,8 @@ filesystem read when imported.
19
19
  import { createFxAgent } from "libfx";
20
20
 
21
21
  const agent = await createFxAgent({
22
- env: { AI_GATEWAY_API_KEY: process.env.AI_GATEWAY_API_KEY },
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({ checkpoint, env });
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, env });
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 `env.FX_GATEWAY_CHAT_URL` as trusted host
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 = { ...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 = { ...options, args: ["acp"], hostToolExecutor: executeHostTool };
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
Binary file
Binary file
Binary file
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.createFxAgent !== "function" && typeof backend.createCore !== "function" &&
71
- typeof backend.createFxTerminal !== "function") {
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.env?.AI_GATEWAY_API_KEY;
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 (typeof native.backend?.[nativeMethod] === "function") {
276
- return await native.backend[nativeMethod](runtimeOptions);
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
- return createWithFallback("agent", "createFxAgent", createWasmAgent, defaultCoreWasm, options);
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 = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libfx",
3
- "version": "0.0.7-dev.607.ga90fc3585568",
3
+ "version": "0.0.7-dev.617.g49fc251a51aa",
4
4
  "description": "Embed fx agents and terminals in JavaScript hosts",
5
5
  "type": "module",
6
6
  "repository": {