@independo/inderun-web 0.1.0

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.
@@ -0,0 +1,118 @@
1
+ import type { TaskRequest, TaskResult } from "@independo/inderun-contracts";
2
+ import type { HostServices } from "./host.js";
3
+ /**
4
+ * Static metadata descriptor defining the capabilities, requirements, and constraints
5
+ * of a provider. This is used by the routing engine and Router to select candidate providers.
6
+ */
7
+ export interface ProviderDescriptor {
8
+ /**
9
+ * Unique identifier of the provider (e.g., 'openai', 'apple-foundation-models').
10
+ */
11
+ id: string;
12
+ /**
13
+ * Execution target type: 'local' (on-device), 'edge', or 'cloud'.
14
+ */
15
+ type: "local" | "edge" | "cloud";
16
+ /**
17
+ * Connection transport layer protocol.
18
+ */
19
+ transport: "in_process" | "system_service" | "http" | "sse" | "realtime";
20
+ /**
21
+ * Optional streaming style, if token or chunk streaming is supported.
22
+ */
23
+ streamingStyle?: "tokens" | "chunks" | "snapshots";
24
+ /**
25
+ * Flags representing the features and modes supported by this provider.
26
+ */
27
+ supports: {
28
+ /** Whether the provider supports Mode 1 run (request/response). */
29
+ run: boolean;
30
+ /** Whether the provider supports Mode 2 stream. */
31
+ streaming: boolean;
32
+ /** Whether the provider supports Mode 3 realtime session. */
33
+ realtime: boolean;
34
+ /** Whether the provider supports tool calling definition. */
35
+ tools: boolean;
36
+ /** Whether the provider outputs reasoning events alongside contents. */
37
+ reasoningEvents: boolean;
38
+ /** Whether the provider supports structured outputs (JSON schema constraints). */
39
+ structuredOutput: boolean;
40
+ /** Whether the provider supports multimodal inputs (images, audio, etc.). */
41
+ multimodal: boolean;
42
+ };
43
+ /**
44
+ * Cancellation capabilities support:
45
+ * - 'hard': fully stops processing and interrupts remote operations.
46
+ * - 'soft': stops reading the stream locally, but the remote execution continues.
47
+ * - 'none': cancellation is unsupported.
48
+ */
49
+ cancel: "hard" | "soft" | "none";
50
+ /**
51
+ * List of supported task kinds (e.g., 'text_to_text', 'embeddings').
52
+ */
53
+ tasks: string[];
54
+ /**
55
+ * Optional input/output resource boundaries.
56
+ */
57
+ limits?: {
58
+ maxInputTokens?: number;
59
+ maxOutputTokens?: number;
60
+ maxImageBytes?: number;
61
+ maxAudioSeconds?: number;
62
+ };
63
+ /**
64
+ * Privacy settings regarding data egress.
65
+ */
66
+ privacy?: {
67
+ /** Set to true if the model input leaves the host device. */
68
+ dataLeavesDevice: boolean;
69
+ /** Geographic regions where the cloud service executes. */
70
+ regions?: string[];
71
+ };
72
+ }
73
+ /**
74
+ * Dynamic capability snapshot evaluated at runtime.
75
+ */
76
+ export interface ProviderDynamicCapabilities {
77
+ /**
78
+ * Whether the provider is available and functional on the host device now.
79
+ */
80
+ available: boolean;
81
+ /**
82
+ * Detail message if availability check fails.
83
+ */
84
+ reason?: string;
85
+ }
86
+ /**
87
+ * Execution context passed to provider run commands.
88
+ */
89
+ export interface RunContext {
90
+ /**
91
+ * Unique execution run ID.
92
+ */
93
+ runId: string;
94
+ /**
95
+ * Host service boundary for provider adapters. Cloud adapters use this to
96
+ * resolve authContextRef through secure storage and dispatch HTTP requests.
97
+ */
98
+ hostServices: HostServices;
99
+ }
100
+ /**
101
+ * Pluggable execution adapter contract that wraps a specific model runtime
102
+ * (system frameworks, local runtimes, or cloud APIs) and exposes normalized APIs.
103
+ */
104
+ export interface ProviderAdapter {
105
+ /**
106
+ * Returns static provider descriptor metadata.
107
+ */
108
+ describe(): ProviderDescriptor;
109
+ /**
110
+ * Performs dynamic capability check to determine if the provider can execute now.
111
+ */
112
+ capabilities(host: HostServices): Promise<ProviderDynamicCapabilities>;
113
+ /**
114
+ * Executes a task request in Mode 1 (request/response).
115
+ */
116
+ run(req: TaskRequest, ctx: RunContext): Promise<TaskResult>;
117
+ }
118
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;OAEG;IACH,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC;;OAEG;IACH,SAAS,EAAE,YAAY,GAAG,gBAAgB,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;IACzE;;OAEG;IACH,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IAEnD;;OAEG;IACH,QAAQ,EAAE;QACR,mEAAmE;QACnE,GAAG,EAAE,OAAO,CAAC;QACb,mDAAmD;QACnD,SAAS,EAAE,OAAO,CAAC;QACnB,6DAA6D;QAC7D,QAAQ,EAAE,OAAO,CAAC;QAClB,6DAA6D;QAC7D,KAAK,EAAE,OAAO,CAAC;QACf,wEAAwE;QACxE,eAAe,EAAE,OAAO,CAAC;QACzB,kFAAkF;QAClF,gBAAgB,EAAE,OAAO,CAAC;QAC1B,6EAA6E;QAC7E,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IAEF;;;;;OAKG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAEjC;;OAEG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAEhB;;OAEG;IACH,MAAM,CAAC,EAAE;QACP,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,6DAA6D;QAC7D,gBAAgB,EAAE,OAAO,CAAC;QAC1B,2DAA2D;QAC3D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,IAAI,kBAAkB,CAAC;IAC/B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACvE;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC7D"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import type { ProviderAdapter } from "./provider.js";
2
+ /**
3
+ * Registry class that holds and manages all active ProviderAdapter instances.
4
+ * Loaded adapters are queried by the Router to choose appropriate routes.
5
+ */
6
+ export declare class ProviderRegistry {
7
+ private providers;
8
+ /**
9
+ * Registers a provider adapter in the registry.
10
+ * @param provider - The provider adapter instance to register.
11
+ * @throws {Error} If the provider does not have a valid ID.
12
+ * @throws {Error} If another provider is already registered with the same ID.
13
+ */
14
+ register(provider: ProviderAdapter): void;
15
+ /**
16
+ * Retrieves a registered provider by its ID.
17
+ * @param id - Unique provider identifier.
18
+ */
19
+ get(id: string): ProviderAdapter | undefined;
20
+ /**
21
+ * Returns a list of all registered provider adapters.
22
+ */
23
+ list(): ProviderAdapter[];
24
+ /**
25
+ * Clears all registered providers.
26
+ */
27
+ clear(): void;
28
+ }
29
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAsC;IAEvD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAWzC;;;OAGG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI5C;;OAEG;IACH,IAAI,IAAI,eAAe,EAAE;IAIzB;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Registry class that holds and manages all active ProviderAdapter instances.
3
+ * Loaded adapters are queried by the Router to choose appropriate routes.
4
+ */
5
+ export class ProviderRegistry {
6
+ providers = new Map();
7
+ /**
8
+ * Registers a provider adapter in the registry.
9
+ * @param provider - The provider adapter instance to register.
10
+ * @throws {Error} If the provider does not have a valid ID.
11
+ * @throws {Error} If another provider is already registered with the same ID.
12
+ */
13
+ register(provider) {
14
+ const descriptor = provider.describe();
15
+ if (!descriptor.id) {
16
+ throw new Error("Provider must have a non-empty ID.");
17
+ }
18
+ if (this.providers.has(descriptor.id)) {
19
+ throw new Error(`Provider with ID "${descriptor.id}" is already registered.`);
20
+ }
21
+ this.providers.set(descriptor.id, provider);
22
+ }
23
+ /**
24
+ * Retrieves a registered provider by its ID.
25
+ * @param id - Unique provider identifier.
26
+ */
27
+ get(id) {
28
+ return this.providers.get(id);
29
+ }
30
+ /**
31
+ * Returns a list of all registered provider adapters.
32
+ */
33
+ list() {
34
+ return Array.from(this.providers.values());
35
+ }
36
+ /**
37
+ * Clears all registered providers.
38
+ */
39
+ clear() {
40
+ this.providers.clear();
41
+ }
42
+ }
@@ -0,0 +1,54 @@
1
+ import type { RoutePlan as SharedPlannerRoutePlan, RoutePlannerInput as SharedPlannerInput, TaskRequest } from "@independo/inderun-contracts";
2
+ import type { HostServices } from "./host.js";
3
+ import type { ProviderAdapter } from "./provider.js";
4
+ export type { SharedPlannerInput, SharedPlannerRoutePlan };
5
+ /**
6
+ * Minimal shape of the shared route-core module (the WASM wrapper), used to
7
+ * plan routes via JSON in/JSON out. `initSharedCore` initializes the WASM
8
+ * bindings when present.
9
+ */
10
+ export interface SharedPlannerModule {
11
+ initSharedCore?: () => Promise<void>;
12
+ planRouteJson: (inputJson: string) => string | Promise<string>;
13
+ }
14
+ /**
15
+ * Strategy that turns planner input into a deterministic route plan, or `null`
16
+ * when the planner is unavailable (callers then fall back to local selection).
17
+ */
18
+ export interface RoutePlanner {
19
+ planRoute(input: SharedPlannerInput): Promise<SharedPlannerRoutePlan | null>;
20
+ }
21
+ /**
22
+ * A provider paired with the descriptor/capability projection the shared route
23
+ * planner consumes.
24
+ */
25
+ export interface ProviderRuntimeSnapshot {
26
+ provider: ProviderAdapter;
27
+ descriptor: SharedPlannerInput["providers"][number]["descriptor"];
28
+ capabilities: SharedPlannerInput["providers"][number]["capabilities"];
29
+ }
30
+ /**
31
+ * {@link RoutePlanner} backed by the Rust route core compiled to WASM. The
32
+ * module is imported lazily and memoized; if it cannot be loaded, `planRoute`
33
+ * resolves to `null` so the engine can fall back to local selection.
34
+ */
35
+ export declare class WasmRoutePlanner implements RoutePlanner {
36
+ private readonly moduleSpecifier;
37
+ private modulePromise?;
38
+ constructor(moduleSpecifier?: string);
39
+ planRoute(input: SharedPlannerInput): Promise<SharedPlannerRoutePlan | null>;
40
+ private loadModule;
41
+ private importModule;
42
+ }
43
+ /**
44
+ * Describes each provider and evaluates its dynamic capabilities against the
45
+ * host, returning snapshots sorted by provider id for deterministic planning.
46
+ */
47
+ export declare function collectProviderRuntimeSnapshots(registryProviders: ProviderAdapter[], hostServices: HostServices): Promise<ProviderRuntimeSnapshot[]>;
48
+ /**
49
+ * Assembles the normalized {@link SharedPlannerInput} the shared route core
50
+ * consumes, applying default constraints/preferences and the current network
51
+ * state.
52
+ */
53
+ export declare function buildSharedPlannerInput(request: TaskRequest, snapshots: ProviderRuntimeSnapshot[], networkOnline: boolean): SharedPlannerInput;
54
+ //# sourceMappingURL=route-planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-planner.d.ts","sourceRoot":"","sources":["../src/route-planner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,IAAI,sBAAsB,EACnC,iBAAiB,IAAI,kBAAkB,EACvC,WAAW,EACZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAChE;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;CAC9E;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAClE,YAAY,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;CACvE;AAID;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IAGvC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAF5C,OAAO,CAAC,aAAa,CAAC,CAAsC;gBAE/B,eAAe,GAAE,MAA+B;IAEvE,SAAS,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC;YAWpE,UAAU;YAOV,YAAY;CAiB3B;AAED;;;GAGG;AACH,wBAAsB,+BAA+B,CACnD,iBAAiB,EAAE,eAAe,EAAE,EACpC,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAepC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,uBAAuB,EAAE,EACpC,aAAa,EAAE,OAAO,GACrB,kBAAkB,CAsBpB"}
@@ -0,0 +1,110 @@
1
+ const DEFAULT_WASM_SPECIFIER = "@independo/inderun-route-core-wasm";
2
+ /**
3
+ * {@link RoutePlanner} backed by the Rust route core compiled to WASM. The
4
+ * module is imported lazily and memoized; if it cannot be loaded, `planRoute`
5
+ * resolves to `null` so the engine can fall back to local selection.
6
+ */
7
+ export class WasmRoutePlanner {
8
+ moduleSpecifier;
9
+ modulePromise;
10
+ constructor(moduleSpecifier = DEFAULT_WASM_SPECIFIER) {
11
+ this.moduleSpecifier = moduleSpecifier;
12
+ }
13
+ async planRoute(input) {
14
+ const module = await this.loadModule();
15
+ if (!module) {
16
+ return null;
17
+ }
18
+ const json = JSON.stringify(input);
19
+ const result = await module.planRouteJson(json);
20
+ return JSON.parse(result);
21
+ }
22
+ async loadModule() {
23
+ if (!this.modulePromise) {
24
+ this.modulePromise = this.importModule();
25
+ }
26
+ return this.modulePromise;
27
+ }
28
+ async importModule() {
29
+ try {
30
+ const mod = (await import(/* @vite-ignore */ this.moduleSpecifier));
31
+ if (mod.initSharedCore) {
32
+ await mod.initSharedCore();
33
+ }
34
+ if (typeof mod.planRouteJson !== "function") {
35
+ return null;
36
+ }
37
+ return mod;
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ }
43
+ }
44
+ /**
45
+ * Describes each provider and evaluates its dynamic capabilities against the
46
+ * host, returning snapshots sorted by provider id for deterministic planning.
47
+ */
48
+ export async function collectProviderRuntimeSnapshots(registryProviders, hostServices) {
49
+ const snapshots = await Promise.all(registryProviders.map(async (provider) => {
50
+ const descriptor = provider.describe();
51
+ const capabilities = await provider.capabilities(hostServices);
52
+ return {
53
+ provider,
54
+ descriptor: toSharedPlannerDescriptor(descriptor),
55
+ capabilities: toSharedPlannerCapabilities(capabilities)
56
+ };
57
+ }));
58
+ return snapshots.sort((left, right) => left.descriptor.id.localeCompare(right.descriptor.id));
59
+ }
60
+ /**
61
+ * Assembles the normalized {@link SharedPlannerInput} the shared route core
62
+ * consumes, applying default constraints/preferences and the current network
63
+ * state.
64
+ */
65
+ export function buildSharedPlannerInput(request, snapshots, networkOnline) {
66
+ const constraints = request.constraints ?? {};
67
+ const preferences = request.preferences ?? {};
68
+ return {
69
+ task: {
70
+ kind: request.task.kind
71
+ },
72
+ constraints: {
73
+ privacy: constraints.privacy ?? "cloud_allowed",
74
+ cloud: constraints.cloud ?? "allowed",
75
+ networkOnline,
76
+ ...(constraints.timeoutMs !== undefined ? { timeoutMs: constraints.timeoutMs } : {})
77
+ },
78
+ preferences: {
79
+ optimizeFor: preferences.optimizeFor ?? "balanced"
80
+ },
81
+ providers: snapshots.map((snapshot) => ({
82
+ descriptor: snapshot.descriptor,
83
+ capabilities: snapshot.capabilities
84
+ }))
85
+ };
86
+ }
87
+ function toSharedPlannerDescriptor(descriptor) {
88
+ return {
89
+ id: descriptor.id,
90
+ type: descriptor.type,
91
+ supports: {
92
+ run: descriptor.supports.run
93
+ },
94
+ tasks: descriptor.tasks,
95
+ ...(descriptor.privacy
96
+ ? {
97
+ privacy: {
98
+ dataLeavesDevice: descriptor.privacy.dataLeavesDevice,
99
+ ...(descriptor.privacy.regions ? { regions: descriptor.privacy.regions } : {})
100
+ }
101
+ }
102
+ : {})
103
+ };
104
+ }
105
+ function toSharedPlannerCapabilities(capabilities) {
106
+ return {
107
+ available: capabilities.available,
108
+ ...(capabilities.reason ? { reason: capabilities.reason } : {})
109
+ };
110
+ }
@@ -0,0 +1,54 @@
1
+ import type { TaskRequest } from "@independo/inderun-contracts";
2
+ import type { HostServices } from "./host.js";
3
+ import type { ProviderAdapter } from "./provider.js";
4
+ import type { ProviderRegistry } from "./registry.js";
5
+ import { type RoutePlanner, type SharedPlannerRoutePlan } from "./route-planner.js";
6
+ /**
7
+ * Output structure from the Router containing the chosen provider and a route selection description.
8
+ */
9
+ export interface RouteSelection {
10
+ /**
11
+ * The selected provider adapter to run.
12
+ */
13
+ provider: ProviderAdapter;
14
+ /**
15
+ * Ordered fallback providers to try if the primary provider fails before producing a final result.
16
+ */
17
+ fallbackProviders: ProviderAdapter[];
18
+ /**
19
+ * The shared route plan used to select this provider chain.
20
+ */
21
+ routePlan: SharedPlannerRoutePlan;
22
+ /**
23
+ * Explanation detailing the selection decision. Useful for debugging and telemetry.
24
+ */
25
+ explanation: string;
26
+ }
27
+ /**
28
+ * Routing engine module. Filters registered providers against task types, request constraints,
29
+ * and dynamic host capabilities (such as connectivity and battery/thermal constraints)
30
+ * to output deterministic execution pathways.
31
+ */
32
+ export declare class Router {
33
+ private registry;
34
+ private planner;
35
+ constructor(registry: ProviderRegistry, planner?: RoutePlanner);
36
+ /**
37
+ * Selects an optimal and compatible execution provider based on the task request and host conditions.
38
+ * @param request - The canonical task request payload.
39
+ * @param hostServices - Host services containing network status and hardware indicators.
40
+ * @returns Resolves to the selected RouteSelection object.
41
+ * @throws {IndeRunException} Under the following routing failure rules:
42
+ * - `CapabilityMismatch` when `execution === 'on_device'` but no local provider is registered or available.
43
+ * - `Offline` when `execution === 'cloud'` but the device lacks internet connectivity.
44
+ * - `Unavailable` when `execution === 'cloud'` but no cloud provider is registered or functional.
45
+ */
46
+ selectRoute(request: TaskRequest, hostServices: HostServices): Promise<RouteSelection>;
47
+ private selectFromSharedPlan;
48
+ private selectFallbackRoute;
49
+ private buildSelectionFromRoutePlan;
50
+ private createFallbackPlan;
51
+ private buildFallbackFailureSummary;
52
+ private routePlanFailure;
53
+ }
54
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAOtD,OAAO,EAIL,KAAK,YAAY,EAEjB,KAAK,sBAAsB,EAE5B,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC;IAC1B;;OAEG;IACH,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC;;OAEG;IACH,SAAS,EAAE,sBAAsB,CAAC;IAClC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;gBADP,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,GAAE,YAAqC;IAGxD;;;;;;;;;OASG;IACG,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAc5F,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,2BAA2B;IAuBnC,OAAO,CAAC,kBAAkB;IAuF1B,OAAO,CAAC,2BAA2B;IAiCnC,OAAO,CAAC,gBAAgB;CAYzB"}
package/dist/router.js ADDED
@@ -0,0 +1,164 @@
1
+ import { createCapabilityMismatch, createInternal, createOffline, createUnavailable } from "./errors.js";
2
+ import { buildSharedPlannerInput, collectProviderRuntimeSnapshots, WasmRoutePlanner } from "./route-planner.js";
3
+ /**
4
+ * Routing engine module. Filters registered providers against task types, request constraints,
5
+ * and dynamic host capabilities (such as connectivity and battery/thermal constraints)
6
+ * to output deterministic execution pathways.
7
+ */
8
+ export class Router {
9
+ registry;
10
+ planner;
11
+ constructor(registry, planner = new WasmRoutePlanner()) {
12
+ this.registry = registry;
13
+ this.planner = planner;
14
+ }
15
+ /**
16
+ * Selects an optimal and compatible execution provider based on the task request and host conditions.
17
+ * @param request - The canonical task request payload.
18
+ * @param hostServices - Host services containing network status and hardware indicators.
19
+ * @returns Resolves to the selected RouteSelection object.
20
+ * @throws {IndeRunException} Under the following routing failure rules:
21
+ * - `CapabilityMismatch` when `execution === 'on_device'` but no local provider is registered or available.
22
+ * - `Offline` when `execution === 'cloud'` but the device lacks internet connectivity.
23
+ * - `Unavailable` when `execution === 'cloud'` but no cloud provider is registered or functional.
24
+ */
25
+ async selectRoute(request, hostServices) {
26
+ const online = await hostServices.connectivity.isOnline();
27
+ const snapshots = await collectProviderRuntimeSnapshots(this.registry.list(), hostServices);
28
+ const planInput = buildSharedPlannerInput(request, snapshots, online);
29
+ const routePlan = await this.planner.planRoute(planInput);
30
+ if (routePlan) {
31
+ return this.selectFromSharedPlan(snapshots, routePlan);
32
+ }
33
+ return this.selectFallbackRoute(request, snapshots, online);
34
+ }
35
+ selectFromSharedPlan(snapshots, routePlan) {
36
+ if (!routePlan.selectedProviderId) {
37
+ throw this.routePlanFailure(routePlan);
38
+ }
39
+ return this.buildSelectionFromRoutePlan(snapshots, routePlan);
40
+ }
41
+ selectFallbackRoute(request, snapshots, online) {
42
+ const plan = this.createFallbackPlan(request, snapshots, online);
43
+ if (!plan.selectedProviderId) {
44
+ throw this.routePlanFailure(plan);
45
+ }
46
+ return this.buildSelectionFromRoutePlan(snapshots, plan);
47
+ }
48
+ buildSelectionFromRoutePlan(snapshots, routePlan) {
49
+ const orderedProviders = routePlan.candidates
50
+ .map((candidate) => snapshots.find((snapshot) => snapshot.descriptor.id === candidate.providerId))
51
+ .filter((snapshot) => snapshot !== undefined);
52
+ const selected = orderedProviders[0];
53
+ if (!selected) {
54
+ throw createInternal("Route plan selected a provider that is no longer registered.");
55
+ }
56
+ return {
57
+ provider: selected.provider,
58
+ fallbackProviders: orderedProviders.slice(1).map((snapshot) => snapshot.provider),
59
+ routePlan,
60
+ explanation: routePlan.explanation.summary
61
+ };
62
+ }
63
+ createFallbackPlan(request, snapshots, online) {
64
+ const planInput = buildSharedPlannerInput(request, snapshots, online);
65
+ const eligible = snapshots
66
+ .filter((candidate) => candidate.descriptor.tasks.includes(planInput.task.kind))
67
+ .filter((candidate) => candidate.descriptor.supports.run);
68
+ const localCandidates = eligible.filter((candidate) => candidate.descriptor.type !== "cloud");
69
+ const cloudCandidates = eligible.filter((candidate) => candidate.descriptor.type === "cloud");
70
+ const selected = eligible.find((candidate) => {
71
+ const descriptor = candidate.descriptor;
72
+ const constraints = planInput.constraints;
73
+ const privacy = descriptor.privacy?.dataLeavesDevice ?? descriptor.type !== "cloud";
74
+ if (constraints.cloud === "forbidden" && descriptor.type === "cloud") {
75
+ return false;
76
+ }
77
+ if (constraints.cloud === "required" && descriptor.type !== "cloud") {
78
+ return false;
79
+ }
80
+ if (constraints.privacy === "local_required" && !privacy) {
81
+ return false;
82
+ }
83
+ if (constraints.privacy === "cloud_required" && descriptor.type !== "cloud") {
84
+ return false;
85
+ }
86
+ if (!online && descriptor.type === "cloud") {
87
+ return false;
88
+ }
89
+ return candidate.capabilities.available;
90
+ });
91
+ const ordered = selected
92
+ ? [selected, ...eligible.filter((candidate) => candidate !== selected)]
93
+ : [];
94
+ if (ordered.length === 0) {
95
+ const failureSummary = this.buildFallbackFailureSummary({
96
+ online,
97
+ constraints: planInput.constraints,
98
+ localCandidates,
99
+ cloudCandidates
100
+ });
101
+ const failureCode = !online
102
+ ? "offline"
103
+ : planInput.constraints.cloud === "required" ||
104
+ planInput.constraints.privacy === "cloud_required"
105
+ ? "unavailable"
106
+ : "capability_mismatch";
107
+ return {
108
+ fallbackProviderIds: [],
109
+ candidates: [],
110
+ rejectedProviders: [],
111
+ failureCode,
112
+ explanation: {
113
+ summary: failureSummary
114
+ }
115
+ };
116
+ }
117
+ const selectedProviderId = ordered[0]?.descriptor.id;
118
+ return {
119
+ selectedProviderId,
120
+ fallbackProviderIds: ordered.slice(1).map((candidate) => candidate.descriptor.id),
121
+ candidates: ordered.map((candidate, index) => ({
122
+ providerId: candidate.descriptor.id,
123
+ order: index
124
+ })),
125
+ rejectedProviders: [],
126
+ explanation: {
127
+ summary: `Selected provider '${selectedProviderId}' deterministically from ${ordered.length} eligible candidate(s).`,
128
+ selectedProviderId
129
+ }
130
+ };
131
+ }
132
+ buildFallbackFailureSummary(input) {
133
+ const wantsCloud = input.constraints.cloud === "required" || input.constraints.privacy === "cloud_required";
134
+ const wantsLocal = input.constraints.privacy === "local_required";
135
+ if (!input.online && (wantsCloud || input.cloudCandidates.length > 0)) {
136
+ return "No network connection is available.";
137
+ }
138
+ if (wantsCloud) {
139
+ if (input.cloudCandidates.length === 0) {
140
+ return "No cloud provider found.";
141
+ }
142
+ return "No cloud provider is currently available.";
143
+ }
144
+ if (wantsLocal) {
145
+ if (input.localCandidates.length === 0) {
146
+ return "No on-device provider found.";
147
+ }
148
+ return "No on-device provider is currently available.";
149
+ }
150
+ return "No eligible provider found for the current routing constraints.";
151
+ }
152
+ routePlanFailure(routePlan) {
153
+ const message = routePlan.explanation.summary;
154
+ switch (routePlan.failureCode) {
155
+ case "offline":
156
+ throw createOffline(message);
157
+ case "unavailable":
158
+ throw createUnavailable(message);
159
+ case "capability_mismatch":
160
+ default:
161
+ throw createCapabilityMismatch(message);
162
+ }
163
+ }
164
+ }
@@ -0,0 +1,13 @@
1
+ import type { TelemetryEvent, TelemetryService } from "@independo/inderun-contracts";
2
+ export type { TelemetryEvent, TelemetryEventType, TelemetryService } from "@independo/inderun-contracts";
3
+ /**
4
+ * Default no-op telemetry handler that discards all events.
5
+ */
6
+ export declare class NoOpTelemetryService implements TelemetryService {
7
+ /**
8
+ * Discards the telemetry event.
9
+ * @param _event - The telemetry event to discard.
10
+ */
11
+ emit(_event: TelemetryEvent): void;
12
+ }
13
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErF,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AAEtC;;GAEG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;IAC3D;;;OAGG;IACH,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;CAGnC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Default no-op telemetry handler that discards all events.
3
+ */
4
+ export class NoOpTelemetryService {
5
+ /**
6
+ * Discards the telemetry event.
7
+ * @param _event - The telemetry event to discard.
8
+ */
9
+ emit(_event) {
10
+ // No-op
11
+ }
12
+ }
@@ -0,0 +1,35 @@
1
+ import { type CreateBrowserHostServicesOptions } from "./browser-host.js";
2
+ import { IndeRun } from "./engine.js";
3
+ import { type OpenAIProviderOptions } from "./openai-provider.js";
4
+ /**
5
+ * Configuration for the default Web SDK factory.
6
+ */
7
+ export interface CreateIndeRunWebOptions {
8
+ /**
9
+ * OpenAI Responses provider configuration registered by the factory.
10
+ */
11
+ openAI: OpenAIProviderOptions;
12
+ /**
13
+ * Optional browser host service overrides.
14
+ */
15
+ hostServices?: CreateBrowserHostServicesOptions;
16
+ /**
17
+ * Explicit opt-in for calling the public OpenAI endpoint directly from this Web SDK factory.
18
+ *
19
+ * Browser apps should leave this unset and use a proxy endpoint with `openAI.auth = "none"` instead.
20
+ * Direct calls can expose credentials to client-side code and are appropriate only for controlled
21
+ * non-production environments.
22
+ */
23
+ allowDirectOpenAIEndpoint?: boolean;
24
+ }
25
+ /**
26
+ * Creates a Web SDK instance with the OpenAI Responses provider registered.
27
+ *
28
+ * Use `openAI.auth = "none"` with a proxy endpoint for production browser apps so the OpenAI API key never ships
29
+ * to the client. Direct OpenAI calls require `allowDirectOpenAIEndpoint` and should resolve credentials through
30
+ * `authContextRef` and `SecureStorageService`.
31
+ *
32
+ * @param options - OpenAI provider configuration and optional host service overrides.
33
+ */
34
+ export declare function createIndeRunWeb(options: CreateIndeRunWebOptions): IndeRun;
35
+ //# sourceMappingURL=web-sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-sdk.d.ts","sourceRoot":"","sources":["../src/web-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAG9B;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,MAAM,EAAE,qBAAqB,CAAC;IAC9B;;OAEG;IACH,YAAY,CAAC,EAAE,gCAAgC,CAAC;IAChD;;;;;;OAMG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAO1E"}