libfx 0.0.15 → 0.0.17
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 +12 -2
- package/browser-agent.js +21 -0
- package/browser-terminal.js +7 -0
- package/browser.js +12 -37
- package/bundled-asset.js +12 -0
- package/custom-provider.js +53 -21
- package/durable/local.mjs +3 -0
- package/durable/local.mjs.map +1 -0
- package/durable/vercel.mjs +3 -0
- package/durable/vercel.mjs.map +1 -0
- package/durable/world.mjs +3 -0
- package/durable/world.mjs.map +1 -0
- package/durable.js +274 -71
- package/engine.js +2498 -0
- package/fetch-cleanup.js +8 -0
- package/fx-core.wasm +0 -0
- package/fx-harness.js +1 -1
- package/fx-sdk.js +27 -3694
- package/fx-term.wasm +0 -0
- package/node-wasm.js +63 -0
- package/node.cjs +8 -8095
- package/node.js +100 -105
- package/package.json +10 -3
- package/types/libfx.d.cts +21 -8
- package/types/node.d.ts +1 -1
- package/wasm-runtime.js +1056 -0
- package/wasm-terminal.js +80 -0
- package/xterm.js +112 -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
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
import { access
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
2
|
import { closeSync } from "node:fs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { Socket } from "node:net";
|
|
5
5
|
import { homedir, tmpdir } from "node:os";
|
|
6
6
|
import { isAbsolute, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
|
+
import { bundledAssetUrl } from "./bundled-asset.js";
|
|
8
9
|
import { CoreOutput } from "./core-output.js";
|
|
9
|
-
import {
|
|
10
|
-
import { fetchCleanupAction, cleanupTimeoutMs, cleanupByteLimit } from "./fetch-cleanup.js";
|
|
10
|
+
import { fetchCleanupAction, cleanupTimeoutMs, cleanupByteLimit, retryAfterSeconds } from "./fetch-cleanup.js";
|
|
11
11
|
import { createDurableAgentFactory, memory, wakeHandler } from "./durable.js";
|
|
12
12
|
import { fxHarness } from "./fx-harness.js";
|
|
13
13
|
import {
|
|
14
|
-
createFxEngine as
|
|
15
|
-
createFxTerminal as createWasmTerminal,
|
|
14
|
+
createFxEngine as createEngine,
|
|
16
15
|
createMemoryPersistence,
|
|
17
16
|
FxFencedError,
|
|
18
17
|
FxJournalVersionError,
|
|
19
|
-
encodeXtermKeyEvent,
|
|
20
18
|
fxSdkApiVersion,
|
|
21
19
|
listModels,
|
|
22
20
|
normalizeAgentOptions,
|
|
23
21
|
supportsJspi,
|
|
24
|
-
|
|
25
|
-
} from "./
|
|
22
|
+
} from "./engine.js";
|
|
23
|
+
import { encodeXtermKeyEvent, xtermAdapter } from "./xterm.js";
|
|
26
24
|
|
|
27
25
|
export { createMemoryPersistence, encodeXtermKeyEvent, FxFencedError, FxJournalVersionError, fxSdkApiVersion, listModels, memory, supportsJspi, wakeHandler, xtermAdapter };
|
|
28
26
|
export const libfxApiVersion = 3;
|
|
@@ -33,10 +31,7 @@ const fetchOperationApplied = 1;
|
|
|
33
31
|
const fetchOperationBackpressure = 2;
|
|
34
32
|
|
|
35
33
|
const nodeRequire = createRequire(import.meta.url);
|
|
36
|
-
const defaultCoreWasm = new URL("./fx-core.wasm", import.meta.url);
|
|
37
|
-
const defaultTermWasm = new URL("./fx-term.wasm", import.meta.url);
|
|
38
34
|
let nativeBackendPromise;
|
|
39
|
-
const wasmFilePromises = new Map();
|
|
40
35
|
|
|
41
36
|
const backendReasonCodes = {
|
|
42
37
|
unsupportedPlatform: "LIBFX_UNSUPPORTED_PLATFORM",
|
|
@@ -49,25 +44,24 @@ const backendReasonCodes = {
|
|
|
49
44
|
wasmLoad: "LIBFX_WASM_LOAD_FAILED",
|
|
50
45
|
};
|
|
51
46
|
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
typeof __webpack_base_uri__ === "undefined" || typeof __webpack_public_path__ !== "string" ||
|
|
55
|
-
!asset.href.startsWith(__webpack_public_path__)) {
|
|
56
|
-
throw new TypeError("Bundled asset URL has no filesystem mapping");
|
|
57
|
-
}
|
|
58
|
-
// Webpack's relative URL is a public asset address, not a Node URL instance.
|
|
59
|
-
return new URL(asset.href.slice(__webpack_public_path__.length), __webpack_base_uri__);
|
|
47
|
+
function sentence(message) {
|
|
48
|
+
return `${message[0].toUpperCase()}${message.slice(1)}.`;
|
|
60
49
|
}
|
|
61
50
|
|
|
62
|
-
function jspiFallbackError(surface, nativeError) {
|
|
63
|
-
const
|
|
51
|
+
function jspiFallbackError(surface, native, nativeError) {
|
|
52
|
+
const missingPackage = !nativeError && native?.failure === "missing" ? native.nativePackage : undefined;
|
|
53
|
+
const nativeDetail = nativeError ? ` Native loading failed: ${nativeError.message}.`
|
|
54
|
+
: missingPackage ? ` ${sentence(nativeFailureReason(native).message)}`
|
|
55
|
+
: " No compatible native addon was found.";
|
|
56
|
+
const remedy = missingPackage
|
|
57
|
+
? `Install ${missingPackage}, which libfx lists as an optional dependency, or run Node with --experimental-wasm-jspi.`
|
|
58
|
+
: "Run Node with --experimental-wasm-jspi or install a libfx package containing a compatible native addon.";
|
|
64
59
|
const error = new Error(
|
|
65
60
|
`libfx could not start the ${surface} backend.${nativeDetail} ` +
|
|
66
|
-
|
|
67
|
-
"Run Node with --experimental-wasm-jspi or install a libfx package containing a compatible native addon.",
|
|
61
|
+
`The WebAssembly fallback requires JavaScript Promise Integration (JSPI). ${remedy}`,
|
|
68
62
|
);
|
|
69
63
|
error.code = "LIBFX_JSPI_REQUIRED";
|
|
70
|
-
error.cause = nativeError;
|
|
64
|
+
error.cause = nativeError ?? native?.probeError;
|
|
71
65
|
return error;
|
|
72
66
|
}
|
|
73
67
|
|
|
@@ -79,7 +73,8 @@ async function loadNativeCandidate(candidate) {
|
|
|
79
73
|
// Bundlers trace the asset URL; Node must load the native file at runtime.
|
|
80
74
|
return Reflect.apply(nodeRequire, undefined, [fileURLToPath(candidate)]);
|
|
81
75
|
}
|
|
82
|
-
|
|
76
|
+
// A module the caller names is its own file, which Node loads as is.
|
|
77
|
+
const imported = await import(/* webpackIgnore: true */ /* turbopackIgnore: true */ candidate.href);
|
|
83
78
|
return imported.default ?? imported;
|
|
84
79
|
}
|
|
85
80
|
if (typeof candidate === "object") return candidate.default ?? candidate;
|
|
@@ -89,37 +84,54 @@ async function loadNativeCandidate(candidate) {
|
|
|
89
84
|
if (candidate.endsWith(".node")) {
|
|
90
85
|
return Reflect.apply(nodeRequire, undefined, [isAbsolute(candidate) ? candidate : resolve(candidate)]);
|
|
91
86
|
}
|
|
92
|
-
const imported = await import(candidate.startsWith("file:") ? candidate : pathToFileURL(candidate).href);
|
|
87
|
+
const imported = await import(/* webpackIgnore: true */ /* turbopackIgnore: true */ candidate.startsWith("file:") ? candidate : pathToFileURL(candidate).href);
|
|
93
88
|
return imported.default ?? imported;
|
|
94
89
|
}
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
91
|
+
// Each platform's addon ships in a package of its own, which libfx lists as
|
|
92
|
+
// an optional dependency, so a package manager installs only the one for its
|
|
93
|
+
// machine. The package's index.js names the addon with a literal
|
|
94
|
+
// `new URL("./libfx.node", import.meta.url)`, so a bundler that bundles
|
|
95
|
+
// libfx copies that one addon and traces it.
|
|
96
|
+
const nativePackages = {
|
|
97
|
+
"darwin-arm64": "@libfx/libfx-darwin-arm64",
|
|
98
|
+
"darwin-x64": "@libfx/libfx-darwin-x64",
|
|
99
|
+
"linux-arm64": "@libfx/libfx-linux-arm64-gnu",
|
|
100
|
+
"linux-x64": "@libfx/libfx-linux-x64-gnu",
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Each specifier is a literal, so Node, Turbopack, and file tracers find the
|
|
104
|
+
// installed package. A missing one fails here at run time, and inside `try`
|
|
105
|
+
// it does not fail a Turbopack build. webpack folds the `typeof` test and
|
|
106
|
+
// sees only a context over the platform packages that are installed, so it
|
|
107
|
+
// does not warn about the ones that are missing.
|
|
108
|
+
async function importNativePackage(name) {
|
|
109
|
+
try {
|
|
110
|
+
if (typeof __webpack_require__ === "function") {
|
|
111
|
+
return await import(
|
|
112
|
+
/* webpackInclude: /[\\/]libfx-(darwin|linux)-[a-z0-9-]+[\\/]index\.js$/ */
|
|
113
|
+
`@libfx/libfx-${name.slice("@libfx/libfx-".length)}`
|
|
114
|
+
);
|
|
115
|
+
} else {
|
|
116
|
+
if (name === "@libfx/libfx-darwin-arm64") return await import("@libfx/libfx-darwin-arm64");
|
|
117
|
+
if (name === "@libfx/libfx-darwin-x64") return await import("@libfx/libfx-darwin-x64");
|
|
118
|
+
if (name === "@libfx/libfx-linux-arm64-gnu") return await import("@libfx/libfx-linux-arm64-gnu");
|
|
119
|
+
if (name === "@libfx/libfx-linux-x64-gnu") return await import("@libfx/libfx-linux-x64-gnu");
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return { error };
|
|
123
|
+
}
|
|
124
|
+
return { error: new Error(`${name} is not a libfx native addon package`) };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The path of the addon the platform package names, or the error that kept
|
|
128
|
+
// the package from loading.
|
|
129
|
+
async function defaultNativeCandidate(name) {
|
|
130
|
+
const imported = await importNativePackage(name);
|
|
131
|
+
if (imported.error) return imported;
|
|
132
|
+
const asset = imported.default;
|
|
133
|
+
if (!(asset instanceof URL)) return { error: new TypeError(`${name} does not export its addon's URL`) };
|
|
134
|
+
return { candidate: fileURLToPath(asset.protocol === "" ? bundledAssetUrl(asset) : asset) };
|
|
123
135
|
}
|
|
124
136
|
|
|
125
137
|
function validateNativeBackend(backend) {
|
|
@@ -180,10 +192,17 @@ async function loadAndValidateNativeCandidate(candidate, artifactMissing = false
|
|
|
180
192
|
}
|
|
181
193
|
|
|
182
194
|
async function discoverNativeBackend() {
|
|
183
|
-
const
|
|
184
|
-
if (!
|
|
195
|
+
const nativePackage = nativePackages[`${process.platform}-${process.arch}`];
|
|
196
|
+
if (!nativePackage) {
|
|
185
197
|
return { backend: null, error: null, failure: "unsupported" };
|
|
186
198
|
}
|
|
199
|
+
// The package's index.js only names its addon, so a package that cannot be
|
|
200
|
+
// found is not installed or not part of the bundle.
|
|
201
|
+
const { candidate, error: packageError } = await defaultNativeCandidate(nativePackage);
|
|
202
|
+
if (packageError) {
|
|
203
|
+
if (missingArtifact(packageError)) return { backend: null, error: null, probeError: packageError, failure: "missing", nativePackage };
|
|
204
|
+
return { backend: null, error: packageError, failure: "load" };
|
|
205
|
+
}
|
|
187
206
|
try {
|
|
188
207
|
await access(candidate);
|
|
189
208
|
} catch (error) {
|
|
@@ -204,35 +223,6 @@ async function resolveNativeBackend(nativeAddon) {
|
|
|
204
223
|
return nativeBackendPromise;
|
|
205
224
|
}
|
|
206
225
|
|
|
207
|
-
function wasmInput(input) {
|
|
208
|
-
const path = wasmFilePath(input);
|
|
209
|
-
if (path === null) {
|
|
210
|
-
if (input instanceof URL) return input.href;
|
|
211
|
-
return input;
|
|
212
|
-
}
|
|
213
|
-
const cached = wasmFilePromises.get(path);
|
|
214
|
-
if (cached) return cached;
|
|
215
|
-
const pendingRead = readFile(path);
|
|
216
|
-
let pending;
|
|
217
|
-
pending = withModuleFailure(pendingRead, () => {
|
|
218
|
-
if (wasmFilePromises.get(path) === pending) wasmFilePromises.delete(path);
|
|
219
|
-
});
|
|
220
|
-
wasmFilePromises.set(path, pending);
|
|
221
|
-
pendingRead.catch(() => {
|
|
222
|
-
if (wasmFilePromises.get(path) === pending) wasmFilePromises.delete(path);
|
|
223
|
-
});
|
|
224
|
-
return pending;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function wasmFilePath(input) {
|
|
228
|
-
if (input instanceof URL) {
|
|
229
|
-
if (input.protocol === "") input = bundledAssetUrl(input);
|
|
230
|
-
if (input.protocol === "file:") return fileURLToPath(input);
|
|
231
|
-
}
|
|
232
|
-
if (typeof input === "string" && !URL.canParse(input)) return resolve(input);
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
226
|
function reason(code, message, error) {
|
|
237
227
|
const causeCode = error?.code;
|
|
238
228
|
return {
|
|
@@ -253,7 +243,9 @@ function nativeFailureReason(result) {
|
|
|
253
243
|
case "missing":
|
|
254
244
|
return reason(
|
|
255
245
|
backendReasonCodes.missingArtifact,
|
|
256
|
-
|
|
246
|
+
result.nativePackage
|
|
247
|
+
? `the native addon package ${result.nativePackage} for ${process.platform}-${process.arch} is not installed`
|
|
248
|
+
: `native addon artifact was not found${detailError?.message ? `: ${detailError.message}` : ""}`,
|
|
257
249
|
detailError,
|
|
258
250
|
);
|
|
259
251
|
case "load":
|
|
@@ -326,15 +318,14 @@ export async function getBackendInfo(value = {}) {
|
|
|
326
318
|
available: false,
|
|
327
319
|
reason: reason(
|
|
328
320
|
backendReasonCodes.jspiUnavailable,
|
|
329
|
-
"WebAssembly backend requires JavaScript Promise Integration (JSPI)",
|
|
321
|
+
"WebAssembly backend requires JavaScript Promise Integration (JSPI); run Node with --experimental-wasm-jspi",
|
|
330
322
|
),
|
|
331
323
|
});
|
|
332
324
|
return { surface, backend: "unavailable", attempts };
|
|
333
325
|
}
|
|
334
|
-
const defaultWasm = surface === "agent" ? defaultCoreWasm : defaultTermWasm;
|
|
335
|
-
const wasmSource = wasm ?? defaultWasm;
|
|
336
326
|
try {
|
|
337
|
-
|
|
327
|
+
const { probeWasm } = await import("./node-wasm.js");
|
|
328
|
+
await probeWasm(surface, wasm);
|
|
338
329
|
attempts.push({ backend: "wasm-jspi", available: true, reason: null });
|
|
339
330
|
return { surface, backend: "wasm-jspi", attempts };
|
|
340
331
|
} catch (error) {
|
|
@@ -480,7 +471,11 @@ function createNativeCoreRuntime(addon, options) {
|
|
|
480
471
|
});
|
|
481
472
|
const response = await wait(responseTask);
|
|
482
473
|
if (settled && !state.consumed) throw new DOMException("Aborted", "AbortError");
|
|
483
|
-
const started = settled
|
|
474
|
+
const started = settled
|
|
475
|
+
? fetchOperationStale
|
|
476
|
+
: typeof addon.startCoreFetchResponseWithRetryAfter === "function"
|
|
477
|
+
? addon.startCoreFetchResponseWithRetryAfter(core, state.handle, response.status, retryAfterSeconds(response.headers))
|
|
478
|
+
: addon.startCoreFetchResponse(core, state.handle, response.status);
|
|
484
479
|
if (started !== fetchOperationApplied && started !== fetchOperationStale) throw new Error(`invalid native fetch start result ${started}`);
|
|
485
480
|
state.active = started === fetchOperationApplied;
|
|
486
481
|
reader = response.body?.getReader();
|
|
@@ -610,7 +605,7 @@ function createNativeCoreRuntime(addon, options) {
|
|
|
610
605
|
}
|
|
611
606
|
|
|
612
607
|
function createNativeAgent(addon, options) {
|
|
613
|
-
return
|
|
608
|
+
return createEngine({
|
|
614
609
|
...options,
|
|
615
610
|
runtimeFactory(runtimeOptions) {
|
|
616
611
|
return createNativeCoreRuntime(addon, runtimeOptions);
|
|
@@ -618,17 +613,18 @@ function createNativeAgent(addon, options) {
|
|
|
618
613
|
});
|
|
619
614
|
}
|
|
620
615
|
|
|
621
|
-
async function createWithFallback(surface, nativeMethod,
|
|
616
|
+
async function createWithFallback(surface, nativeMethod, options) {
|
|
622
617
|
const { nativeAddon, backend = "auto", ...unvalidatedOptions } = options ?? {};
|
|
623
618
|
if (!new Set(["auto", "native", "wasm"]).has(backend)) {
|
|
624
619
|
throw new TypeError('backend must be "auto", "native", or "wasm"');
|
|
625
620
|
}
|
|
626
621
|
const runtimeOptions = surface === "agent" ? normalizeAgentOptions(unvalidatedOptions) : unvalidatedOptions;
|
|
627
622
|
|
|
623
|
+
let native = null;
|
|
628
624
|
let nativeError;
|
|
629
625
|
let nativeAttempted = false;
|
|
630
626
|
if (backend !== "wasm") {
|
|
631
|
-
|
|
627
|
+
native = await resolveNativeBackend(nativeAddon);
|
|
632
628
|
nativeError = native.error;
|
|
633
629
|
if (typeof native.backend?.[nativeMethod] === "function") {
|
|
634
630
|
nativeAttempted = true;
|
|
@@ -643,7 +639,12 @@ async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWas
|
|
|
643
639
|
}
|
|
644
640
|
}
|
|
645
641
|
if (backend === "native") {
|
|
646
|
-
const
|
|
642
|
+
const missingPackage = native.failure === "missing" ? native.nativePackage : undefined;
|
|
643
|
+
const error = nativeError ?? new Error(native.backend
|
|
644
|
+
? `native addon does not provide ${nativeMethod}()`
|
|
645
|
+
: missingPackage
|
|
646
|
+
? `${sentence(nativeFailureReason(native).message)} Install ${missingPackage}, which libfx lists as an optional dependency.`
|
|
647
|
+
: nativeFailureReason(native).message, native.probeError ? { cause: native.probeError } : undefined);
|
|
647
648
|
error.code ??= "LIBFX_NATIVE_UNAVAILABLE";
|
|
648
649
|
throw error;
|
|
649
650
|
}
|
|
@@ -651,10 +652,10 @@ async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWas
|
|
|
651
652
|
|
|
652
653
|
if (!supportsJspi()) {
|
|
653
654
|
if (nativeAttempted) throw nativeError;
|
|
654
|
-
throw jspiFallbackError(surface, nativeError);
|
|
655
|
+
throw jspiFallbackError(surface, native, nativeError);
|
|
655
656
|
}
|
|
656
|
-
const
|
|
657
|
-
return
|
|
657
|
+
const wasmBackend = await import("./node-wasm.js");
|
|
658
|
+
return surface === "agent" ? wasmBackend.createWasmEngine(runtimeOptions) : wasmBackend.createWasmTerminal(runtimeOptions);
|
|
658
659
|
}
|
|
659
660
|
|
|
660
661
|
/**
|
|
@@ -663,13 +664,7 @@ async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWas
|
|
|
663
664
|
* runs durable sessions on these.
|
|
664
665
|
*/
|
|
665
666
|
export async function createFxEngine(options = {}) {
|
|
666
|
-
return createWithFallback(
|
|
667
|
-
"agent",
|
|
668
|
-
"createCore",
|
|
669
|
-
createWasmAgent,
|
|
670
|
-
defaultCoreWasm,
|
|
671
|
-
options,
|
|
672
|
-
);
|
|
667
|
+
return createWithFallback("agent", "createCore", options);
|
|
673
668
|
}
|
|
674
669
|
|
|
675
670
|
// The durability a session gets when the caller names none: Vercel's World
|
|
@@ -723,5 +718,5 @@ export const createFxAgent = createDurableAgentFactory({
|
|
|
723
718
|
});
|
|
724
719
|
|
|
725
720
|
export function createFxTerminal(options = {}) {
|
|
726
|
-
return createWithFallback("terminal", "createFxTerminal",
|
|
721
|
+
return createWithFallback("terminal", "createFxTerminal", options);
|
|
727
722
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libfx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "Embed fx agents and terminals in JavaScript hosts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -87,12 +87,19 @@
|
|
|
87
87
|
"default": "./skills-node.js"
|
|
88
88
|
}
|
|
89
89
|
},
|
|
90
|
+
"sideEffects": false,
|
|
90
91
|
"engines": {
|
|
91
|
-
"node": ">=
|
|
92
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
92
93
|
},
|
|
93
94
|
"publishConfig": {
|
|
94
95
|
"access": "public",
|
|
95
96
|
"registry": "https://registry.npmjs.org/"
|
|
96
97
|
},
|
|
97
|
-
"license": "Apache-2.0"
|
|
98
|
+
"license": "Apache-2.0",
|
|
99
|
+
"optionalDependencies": {
|
|
100
|
+
"@libfx/libfx-linux-x64-gnu": "0.0.17",
|
|
101
|
+
"@libfx/libfx-linux-arm64-gnu": "0.0.17",
|
|
102
|
+
"@libfx/libfx-darwin-x64": "0.0.17",
|
|
103
|
+
"@libfx/libfx-darwin-arm64": "0.0.17"
|
|
104
|
+
}
|
|
98
105
|
}
|
package/types/libfx.d.cts
CHANGED
|
@@ -46,7 +46,7 @@ export interface FxModelOptions {
|
|
|
46
46
|
effort?: FxEffort;
|
|
47
47
|
/** Enables the fast lane for models that advertise one. */
|
|
48
48
|
fast?: boolean;
|
|
49
|
-
/** Requests
|
|
49
|
+
/** Requests Ultrafast mode, the higher-cost service tier, for models whose metadata advertises it. Disables Fast. */
|
|
50
50
|
ultrafast?: boolean;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -261,8 +261,8 @@ export interface FxUsage {
|
|
|
261
261
|
reasoningTokens?: number;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
/** Why an engine turn ended. */
|
|
265
|
-
export type FxStopReason = "end_turn" | "max_output_tokens" | "max_model_turns" | "refused" | "cancelled";
|
|
264
|
+
/** Why an engine turn ended. `yielded`: it stopped at its `yieldAt` and stays open for `resume()`. */
|
|
265
|
+
export type FxStopReason = "end_turn" | "max_output_tokens" | "max_model_turns" | "refused" | "cancelled" | "yielded";
|
|
266
266
|
|
|
267
267
|
// ---------------------------------------------------------------------------
|
|
268
268
|
// Diagnostics
|
|
@@ -304,7 +304,8 @@ export type FxEngineEvent = FxEventBase & (
|
|
|
304
304
|
| { type: "permission.request"; request: unknown }
|
|
305
305
|
| { type: "permission.resolve"; optionId: unknown }
|
|
306
306
|
| { type: "journal.open"; events: number; checkpoint: boolean; turns: unknown; resumable: boolean }
|
|
307
|
-
|
|
307
|
+
/** `barrier`: the record's `barrier`, as `Persistence.append` receives it. */
|
|
308
|
+
| { type: "journal.append"; events: number; barrier: boolean }
|
|
308
309
|
| { type: "journal.error"; error: string }
|
|
309
310
|
| { type: "checkpoint.save"; through: string; bytes: number }
|
|
310
311
|
| { type: "checkpoint.error"; error: string; message: string }
|
|
@@ -423,7 +424,7 @@ export type FxHookEvent = FxHookEnvelope & (
|
|
|
423
424
|
| { type: "mcp.start"; data: { operationId: number | string; callId: string | null; server: string | null; tool: string; transport: string | null } }
|
|
424
425
|
| { type: "mcp.request"; data: { operationId: number | string; requestId: number | string; requestIdKind: "jsonrpc" | "engine" | "correlation" } }
|
|
425
426
|
| { type: "mcp.end"; data: { operationId: number | string; status: "success" | "failure" | "cancelled" | "input_required" | "unavailable" | "unknown"; error: string | null } }
|
|
426
|
-
| { type: "delegation"; data: { callId: string; childSessionId: string; workId: string; operationId: string | null; phase: "dispatched" | "completed" | "
|
|
427
|
+
| { type: "delegation"; data: { callId: string; childSessionId: string; workId: string; operationId: string | null; phase: "dispatched" | "completed" | "replayed" | "failed" | "cancelled" | "feedback_queued" | "feedback_applied" | "feedback_not_applied" } }
|
|
427
428
|
/** Emitted only after append resolves and its cursor validates. */
|
|
428
429
|
| { type: "journal.commit"; data: { cursor: string; firstSeq: number | null; lastSeq: number | null; eventCount: number } }
|
|
429
430
|
| { type: "journal.restore"; data: { cursor: string | null; lastSeq: number; resumable: boolean; openTurnId: string | null; lastTurnId: string | null } }
|
|
@@ -669,6 +670,12 @@ export interface FxCustomProviderModel {
|
|
|
669
670
|
maxOutputTokens?: number;
|
|
670
671
|
supportsToolUse?: boolean;
|
|
671
672
|
supportsVision?: boolean;
|
|
673
|
+
/** The request field that carries the output limit. Defaults to `"max_tokens"`. */
|
|
674
|
+
outputLimitField?: "max_tokens" | "max_completion_tokens";
|
|
675
|
+
/** Fields sent in the body of every request for the model, such as `{ temperature: 0.2 }`. A field fx writes itself, such as `messages`, is refused. */
|
|
676
|
+
request?: Record<string, unknown>;
|
|
677
|
+
/** The effort levels the model offers, in order, each with the request fields that choose it, such as `{ high: { reasoning_effort: "high" } }`. A level the model does not list is not sent. */
|
|
678
|
+
reasoningEfforts?: Record<string, Record<string, unknown>>;
|
|
672
679
|
}
|
|
673
680
|
|
|
674
681
|
/** A server that speaks OpenAI Chat Completions, such as a local model server or a router. */
|
|
@@ -679,6 +686,12 @@ export interface FxCustomProviderDefinition {
|
|
|
679
686
|
baseUrl: string;
|
|
680
687
|
/** The models it serves, by id. */
|
|
681
688
|
models?: Record<string, FxCustomProviderModel>;
|
|
689
|
+
/** Fields for every model, including one `models` does not list, where the model sets none itself. */
|
|
690
|
+
modelDefaults?: FxCustomProviderModel;
|
|
691
|
+
/** The model that names a session from its first prompt. Without it, a session keeps the title derived from the prompt. */
|
|
692
|
+
titleModel?: string;
|
|
693
|
+
/** The model that reads images for a model that cannot. Without it, the provider has no image fallback. */
|
|
694
|
+
visionModel?: string;
|
|
682
695
|
}
|
|
683
696
|
|
|
684
697
|
/** A custom provider's API key. */
|
|
@@ -796,8 +809,8 @@ export interface FxAccepted {
|
|
|
796
809
|
/** How a durable turn ended. */
|
|
797
810
|
export interface FxTurnResult {
|
|
798
811
|
messageId: string;
|
|
799
|
-
/** `error` when the turn failed, with the reason in `error`; `unknown` when the process that ran it stopped before writing how it ended. */
|
|
800
|
-
stopReason: FxStopReason | "error" | "unknown";
|
|
812
|
+
/** `error` when the turn failed, with the reason in `error`; `unknown` when the process that ran it stopped before writing how it ended. A yielded turn continues, so it never ends `yielded`. */
|
|
813
|
+
stopReason: Exclude<FxStopReason, "yielded"> | "error" | "unknown";
|
|
801
814
|
/** Absent when the result is a failure read from the session's log rather than from the turn's end. */
|
|
802
815
|
usage?: FxUsage;
|
|
803
816
|
error?: FxErrorSummary;
|
|
@@ -848,7 +861,7 @@ export type FxStreamLine = (
|
|
|
848
861
|
| {
|
|
849
862
|
type: "turn_end";
|
|
850
863
|
messageId: string;
|
|
851
|
-
stopReason: FxStopReason | "error" | "unknown";
|
|
864
|
+
stopReason: Exclude<FxStopReason, "yielded"> | "error" | "unknown";
|
|
852
865
|
usage?: FxUsage;
|
|
853
866
|
error?: FxErrorSummary;
|
|
854
867
|
repeated?: true;
|
package/types/node.d.ts
CHANGED