@elsium-ai/app 0.7.0 → 0.9.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.
package/dist/app.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { type Gateway } from '@elsium-ai/gateway';
1
+ import { type Gateway, type ProviderMesh } from '@elsium-ai/gateway';
2
2
  import { type Tracer } from '@elsium-ai/observe';
3
3
  import { Hono } from 'hono';
4
4
  import type { AppConfig } from './types';
5
5
  export interface ElsiumApp {
6
6
  readonly hono: Hono;
7
7
  readonly gateway: Gateway;
8
+ readonly mesh: ProviderMesh | undefined;
8
9
  readonly tracer: Tracer;
9
10
  listen(port?: number): {
10
11
  port: number;
package/dist/app.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,OAAO,EAAW,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,KAAK,MAAM,EAAW,MAAM,oBAAoB,CAAA;AAIzD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAS3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAA;CAClE;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAgItD"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAA+B,MAAM,oBAAoB,CAAA;AACjG,OAAO,EAAE,KAAK,MAAM,EAAW,MAAM,oBAAoB,CAAA;AAIzD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAS3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAA;CAClE;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CA8JtD"}
package/dist/index.js CHANGED
@@ -477,6 +477,134 @@ function zodToJsonSchema(schema) {
477
477
  // ../core/src/registry.ts
478
478
  var log2 = createLogger();
479
479
  var BLOCKED_KEYS = new Set(["__proto__", "constructor", "prototype"]);
480
+ // ../core/src/circuit-breaker.ts
481
+ function defaultShouldCount(error) {
482
+ if (error && typeof error === "object" && "retryable" in error) {
483
+ return error.retryable === true;
484
+ }
485
+ return true;
486
+ }
487
+ function createCircuitBreaker(config) {
488
+ const failureThreshold = config?.failureThreshold ?? 5;
489
+ const resetTimeoutMs = config?.resetTimeoutMs ?? 30000;
490
+ const halfOpenMaxAttempts = config?.halfOpenMaxAttempts ?? 3;
491
+ const windowMs = config?.windowMs ?? 60000;
492
+ if (failureThreshold < 1 || !Number.isFinite(failureThreshold)) {
493
+ throw new ElsiumError({
494
+ code: "CONFIG_ERROR",
495
+ message: "failureThreshold must be >= 1",
496
+ retryable: false
497
+ });
498
+ }
499
+ if (resetTimeoutMs < 0 || !Number.isFinite(resetTimeoutMs)) {
500
+ throw new ElsiumError({
501
+ code: "CONFIG_ERROR",
502
+ message: "resetTimeoutMs must be >= 0 and finite",
503
+ retryable: false
504
+ });
505
+ }
506
+ if (halfOpenMaxAttempts < 1 || !Number.isFinite(halfOpenMaxAttempts)) {
507
+ throw new ElsiumError({
508
+ code: "CONFIG_ERROR",
509
+ message: "halfOpenMaxAttempts must be >= 1",
510
+ retryable: false
511
+ });
512
+ }
513
+ if (windowMs < 0 || !Number.isFinite(windowMs)) {
514
+ throw new ElsiumError({
515
+ code: "CONFIG_ERROR",
516
+ message: "windowMs must be >= 0 and finite",
517
+ retryable: false
518
+ });
519
+ }
520
+ const onStateChange = config?.onStateChange;
521
+ const shouldCount = config?.shouldCount ?? defaultShouldCount;
522
+ let currentState = "closed";
523
+ let failureTimestamps = [];
524
+ let lastOpenedAt = 0;
525
+ let halfOpenAttempts = 0;
526
+ let halfOpenInFlight = 0;
527
+ function transition(to) {
528
+ if (currentState === to)
529
+ return;
530
+ const from = currentState;
531
+ currentState = to;
532
+ onStateChange?.(from, to);
533
+ }
534
+ function recordFailure() {
535
+ const now = Date.now();
536
+ failureTimestamps.push(now);
537
+ failureTimestamps = failureTimestamps.filter((t) => now - t < windowMs);
538
+ if (failureTimestamps.length >= failureThreshold) {
539
+ lastOpenedAt = now;
540
+ halfOpenAttempts = 0;
541
+ transition("open");
542
+ }
543
+ }
544
+ function recordSuccess() {
545
+ if (currentState === "half-open") {
546
+ failureTimestamps = [];
547
+ halfOpenAttempts = 0;
548
+ halfOpenInFlight = 0;
549
+ transition("closed");
550
+ }
551
+ }
552
+ return {
553
+ get state() {
554
+ if (currentState === "open" && Date.now() - lastOpenedAt >= resetTimeoutMs) {
555
+ transition("half-open");
556
+ }
557
+ return currentState;
558
+ },
559
+ get failureCount() {
560
+ const now = Date.now();
561
+ return failureTimestamps.filter((t) => now - t < windowMs).length;
562
+ },
563
+ async execute(fn) {
564
+ const state = this.state;
565
+ if (state === "open") {
566
+ throw new ElsiumError({
567
+ code: "PROVIDER_ERROR",
568
+ message: "Circuit breaker is open",
569
+ retryable: true
570
+ });
571
+ }
572
+ if (state === "half-open" && halfOpenInFlight >= halfOpenMaxAttempts) {
573
+ lastOpenedAt = Date.now();
574
+ transition("open");
575
+ throw new ElsiumError({
576
+ code: "PROVIDER_ERROR",
577
+ message: "Circuit breaker is open",
578
+ retryable: true
579
+ });
580
+ }
581
+ if (state === "half-open") {
582
+ halfOpenAttempts++;
583
+ halfOpenInFlight++;
584
+ }
585
+ try {
586
+ const result = await fn();
587
+ recordSuccess();
588
+ return result;
589
+ } catch (error) {
590
+ if (shouldCount(error)) {
591
+ recordFailure();
592
+ }
593
+ throw error;
594
+ } finally {
595
+ if (state === "half-open") {
596
+ halfOpenInFlight = Math.max(0, halfOpenInFlight - 1);
597
+ }
598
+ }
599
+ },
600
+ reset() {
601
+ failureTimestamps = [];
602
+ halfOpenAttempts = 0;
603
+ halfOpenInFlight = 0;
604
+ transition("closed");
605
+ }
606
+ };
607
+ }
480
608
  // ../core/src/shutdown.ts
481
609
  function createShutdownManager(config) {
482
610
  const drainTimeoutMs = config?.drainTimeoutMs ?? 30000;
@@ -1900,11 +2028,44 @@ async function processOpenAISSEStream(body, emit) {
1900
2028
  }
1901
2029
  }
1902
2030
 
2031
+ // ../gateway/src/providers/openai-compatible.ts
2032
+ function createOpenAICompatibleProvider(config) {
2033
+ const providerName = config.name ?? "openai-compatible";
2034
+ const model = config.defaultModel ?? "default";
2035
+ const inner = createOpenAIProvider({
2036
+ apiKey: config.apiKey,
2037
+ baseUrl: config.baseUrl,
2038
+ timeout: config.timeout,
2039
+ maxRetries: config.maxRetries
2040
+ });
2041
+ const metadata = {
2042
+ baseUrl: `${config.baseUrl}/v1/chat/completions`,
2043
+ capabilities: config.capabilities ?? ["tools", "streaming", "system"],
2044
+ authStyle: "bearer"
2045
+ };
2046
+ return {
2047
+ name: providerName,
2048
+ defaultModel: model,
2049
+ metadata,
2050
+ async complete(request) {
2051
+ const response = await inner.complete(request);
2052
+ return { ...response, provider: providerName };
2053
+ },
2054
+ stream(request) {
2055
+ return inner.stream(request);
2056
+ },
2057
+ async listModels() {
2058
+ return inner.listModels();
2059
+ }
2060
+ };
2061
+ }
2062
+
1903
2063
  // ../gateway/src/gateway.ts
1904
2064
  var PROVIDER_FACTORIES = {
1905
2065
  anthropic: createAnthropicProvider,
1906
2066
  openai: createOpenAIProvider,
1907
- google: createGoogleProvider
2067
+ google: createGoogleProvider,
2068
+ "openai-compatible": (cfg) => createOpenAICompatibleProvider({ ...cfg, baseUrl: cfg.baseUrl ?? "" })
1908
2069
  };
1909
2070
  registerProviderMetadata("anthropic", {
1910
2071
  baseUrl: "https://api.anthropic.com/v1/messages",
@@ -2123,6 +2284,38 @@ function gateway(config) {
2123
2284
  });
2124
2285
  }
2125
2286
  return { data: result.data, response };
2287
+ },
2288
+ async extract(schema, input, options) {
2289
+ const maxRetries = options?.maxRetries ?? 3;
2290
+ const messages = [{ role: "user", content: input }];
2291
+ let lastError;
2292
+ for (let attempt = 0;attempt <= maxRetries; attempt++) {
2293
+ try {
2294
+ const result = await this.generate({
2295
+ messages: [...messages],
2296
+ schema,
2297
+ model: options?.model,
2298
+ system: options?.system,
2299
+ temperature: options?.temperature
2300
+ });
2301
+ return result.data;
2302
+ } catch (e) {
2303
+ if (e instanceof ElsiumError && e.code === "VALIDATION_ERROR") {
2304
+ lastError = e;
2305
+ messages.push({
2306
+ role: "assistant",
2307
+ content: "Invalid output"
2308
+ });
2309
+ messages.push({
2310
+ role: "user",
2311
+ content: `The previous response failed validation: ${e.message}. Please try again and return valid JSON matching the schema.`
2312
+ });
2313
+ continue;
2314
+ }
2315
+ throw e;
2316
+ }
2317
+ }
2318
+ throw lastError;
2126
2319
  }
2127
2320
  };
2128
2321
  }
@@ -2132,6 +2325,410 @@ var log4 = createLogger();
2132
2325
  var log5 = createLogger();
2133
2326
  // ../gateway/src/batch.ts
2134
2327
  var log6 = createLogger();
2328
+ // ../gateway/src/router.ts
2329
+ var REASONING_KEYWORDS = /\b(prove|explain why|analyze|compare|contrast|evaluate|critique|debate|reason|deduce|infer|justify|argue|synthesize|hypothesize|derive)\b/i;
2330
+ var CODE_KEYWORDS = /\b(implement|refactor|debug|optimize|architect|design pattern|algorithm|data structure|write code|code review|fix the bug|type system)\b/i;
2331
+ var CREATIVE_KEYWORDS = /\b(write a (story|essay|poem|article|report|paper)|compose|draft|create a (plan|proposal|strategy))\b/i;
2332
+ var MATH_KEYWORDS = /\b(calculate|compute|solve|equation|integral|derivative|matrix|probability|statistical|proof|theorem|formula)\b/i;
2333
+ function extractTextContent(request) {
2334
+ const parts = [];
2335
+ for (const m of request.messages) {
2336
+ if (typeof m.content === "string") {
2337
+ parts.push(m.content);
2338
+ } else if (Array.isArray(m.content)) {
2339
+ for (const p of m.content) {
2340
+ if (p.type === "text")
2341
+ parts.push(p.text);
2342
+ }
2343
+ }
2344
+ }
2345
+ if (request.system)
2346
+ parts.push(request.system);
2347
+ return parts.join(" ");
2348
+ }
2349
+ function estimateComplexity(request) {
2350
+ let score = 0;
2351
+ const totalChars = request.messages.reduce((sum, m) => {
2352
+ const len = typeof m.content === "string" ? m.content.length : JSON.stringify(m.content).length;
2353
+ return sum + len;
2354
+ }, 0);
2355
+ if (totalChars > 2000)
2356
+ score += 0.3;
2357
+ if (totalChars > 5000)
2358
+ score += 0.2;
2359
+ if (request.tools?.length)
2360
+ score += 0.2;
2361
+ if ((request.tools?.length ?? 0) > 3)
2362
+ score += 0.1;
2363
+ if (request.system && request.system.length > 500)
2364
+ score += 0.1;
2365
+ if (request.messages.length > 10)
2366
+ score += 0.1;
2367
+ const text = extractTextContent(request);
2368
+ if (REASONING_KEYWORDS.test(text))
2369
+ score += 0.5;
2370
+ if (CODE_KEYWORDS.test(text))
2371
+ score += 0.5;
2372
+ if (CREATIVE_KEYWORDS.test(text))
2373
+ score += 0.2;
2374
+ if (MATH_KEYWORDS.test(text))
2375
+ score += 0.5;
2376
+ return Math.min(score, 1);
2377
+ }
2378
+ function createProviderMesh(config) {
2379
+ if (config.providers.length === 0) {
2380
+ throw new ElsiumError({
2381
+ code: "CONFIG_ERROR",
2382
+ message: "Provider mesh requires at least one provider",
2383
+ retryable: false
2384
+ });
2385
+ }
2386
+ const sortedProviders = [...config.providers];
2387
+ const gateways = new Map;
2388
+ const circuitBreakers = new Map;
2389
+ const audit = config.audit;
2390
+ for (const entry of sortedProviders) {
2391
+ const gw = gateway({
2392
+ provider: entry.name,
2393
+ apiKey: entry.config.apiKey,
2394
+ baseUrl: entry.config.baseUrl,
2395
+ model: entry.model
2396
+ });
2397
+ gateways.set(entry.name, gw);
2398
+ if (config.circuitBreaker) {
2399
+ const cbConfig = typeof config.circuitBreaker === "boolean" ? {} : config.circuitBreaker;
2400
+ const providerName = entry.name;
2401
+ const wrappedConfig = {
2402
+ ...cbConfig,
2403
+ onStateChange(from, to) {
2404
+ cbConfig.onStateChange?.(from, to);
2405
+ audit?.log("circuit_breaker_state_change", {
2406
+ provider: providerName,
2407
+ fromState: from,
2408
+ toState: to
2409
+ });
2410
+ }
2411
+ };
2412
+ circuitBreakers.set(entry.name, createCircuitBreaker(wrappedConfig));
2413
+ }
2414
+ }
2415
+ function callWithCircuitBreaker(providerName, fn) {
2416
+ const cb = circuitBreakers.get(providerName);
2417
+ return cb ? cb.execute(fn) : fn();
2418
+ }
2419
+ function isProviderAvailable(providerName) {
2420
+ const cb = circuitBreakers.get(providerName);
2421
+ return !cb || cb.state !== "open";
2422
+ }
2423
+ function getGateway(providerName) {
2424
+ const gw = gateways.get(providerName);
2425
+ if (!gw) {
2426
+ throw new ElsiumError({
2427
+ code: "CONFIG_ERROR",
2428
+ message: `Provider "${providerName}" not found in mesh`,
2429
+ retryable: false
2430
+ });
2431
+ }
2432
+ return gw;
2433
+ }
2434
+ function attemptProvider(entry, request) {
2435
+ const gw = getGateway(entry.name);
2436
+ return callWithCircuitBreaker(entry.name, () => gw.complete({ ...request, model: request.model ?? entry.model }));
2437
+ }
2438
+ function logFailover(fromProvider, toProvider, reason) {
2439
+ audit?.log("provider_failover", {
2440
+ fromProvider,
2441
+ toProvider,
2442
+ strategy: config.strategy,
2443
+ reason
2444
+ });
2445
+ }
2446
+ function toError2(err2) {
2447
+ return err2 instanceof Error ? err2 : new Error(String(err2));
2448
+ }
2449
+ async function tryProvidersWithAudit(providers, request, errorMessage) {
2450
+ let lastError = null;
2451
+ let failedProvider = null;
2452
+ for (const entry of providers) {
2453
+ if (!isProviderAvailable(entry.name))
2454
+ continue;
2455
+ try {
2456
+ const response = await attemptProvider(entry, request);
2457
+ if (failedProvider)
2458
+ logFailover(failedProvider, entry.name, lastError?.message);
2459
+ return response;
2460
+ } catch (err2) {
2461
+ failedProvider = entry.name;
2462
+ lastError = toError2(err2);
2463
+ }
2464
+ }
2465
+ throw lastError ?? new ElsiumError({ code: "PROVIDER_ERROR", message: errorMessage, retryable: false });
2466
+ }
2467
+ async function fallbackComplete(request) {
2468
+ return tryProvidersWithAudit(sortedProviders, request, "All providers failed");
2469
+ }
2470
+ async function costOptimizedComplete(request) {
2471
+ const optimizer = config.costOptimizer;
2472
+ if (!optimizer) {
2473
+ return fallbackComplete(request);
2474
+ }
2475
+ const complexity = estimateComplexity(request);
2476
+ const threshold = optimizer.complexityThreshold ?? 0.5;
2477
+ const target = complexity < threshold ? optimizer.simpleModel : optimizer.complexModel;
2478
+ const gw = getGateway(target.provider);
2479
+ try {
2480
+ return await gw.complete({ ...request, model: target.model });
2481
+ } catch (err2) {
2482
+ audit?.log("provider_failover", {
2483
+ fromProvider: target.provider,
2484
+ toProvider: "fallback-chain",
2485
+ strategy: "cost-optimized",
2486
+ reason: err2 instanceof Error ? err2.message : String(err2)
2487
+ });
2488
+ return fallbackComplete(request);
2489
+ }
2490
+ }
2491
+ async function latencyOptimizedComplete(request) {
2492
+ const controller = new AbortController;
2493
+ const availableProviders = sortedProviders.filter((e) => isProviderAvailable(e.name));
2494
+ const promises = availableProviders.map(async (entry) => {
2495
+ const gw = getGateway(entry.name);
2496
+ return callWithCircuitBreaker(entry.name, () => gw.complete({
2497
+ ...request,
2498
+ model: request.model ?? entry.model,
2499
+ signal: controller.signal
2500
+ }));
2501
+ });
2502
+ try {
2503
+ const result = await Promise.any(promises);
2504
+ controller.abort();
2505
+ return result;
2506
+ } catch {
2507
+ throw new ElsiumError({
2508
+ code: "PROVIDER_ERROR",
2509
+ message: "All providers failed",
2510
+ retryable: false
2511
+ });
2512
+ }
2513
+ }
2514
+ function detectRequiredCapabilities(request) {
2515
+ const capabilities = [];
2516
+ if ((request.tools?.length ?? 0) > 0)
2517
+ capabilities.push("tools");
2518
+ const needsVision = request.messages.some((m) => Array.isArray(m.content) && m.content.some((p) => p.type === "image"));
2519
+ if (needsVision)
2520
+ capabilities.push("vision");
2521
+ return capabilities;
2522
+ }
2523
+ function filterCapableProviders(capabilities) {
2524
+ return sortedProviders.filter((entry) => {
2525
+ if (capabilities.length === 0)
2526
+ return true;
2527
+ const providerCaps = entry.capabilities ?? defaultCapabilities(entry.name);
2528
+ return capabilities.every((c) => providerCaps.includes(c));
2529
+ });
2530
+ }
2531
+ async function capabilityAwareComplete(request) {
2532
+ const capabilities = detectRequiredCapabilities(request);
2533
+ const capable = filterCapableProviders(capabilities);
2534
+ if (capable.length === 0) {
2535
+ return fallbackComplete(request);
2536
+ }
2537
+ return tryProvidersWithAudit(capable, request, "No capable provider succeeded");
2538
+ }
2539
+ function defaultCapabilities(provider) {
2540
+ const meta = getProviderMetadata(provider);
2541
+ if (meta?.capabilities)
2542
+ return meta.capabilities;
2543
+ switch (provider) {
2544
+ case "anthropic":
2545
+ return ["tools", "vision", "streaming", "system"];
2546
+ case "openai":
2547
+ return ["tools", "vision", "streaming", "system", "json_mode"];
2548
+ case "google":
2549
+ return ["tools", "vision", "streaming", "system"];
2550
+ default:
2551
+ return ["streaming"];
2552
+ }
2553
+ }
2554
+ function errorStream(message) {
2555
+ return new ElsiumStream(async function* () {
2556
+ yield {
2557
+ type: "error",
2558
+ error: new ElsiumError({
2559
+ code: "PROVIDER_ERROR",
2560
+ message,
2561
+ retryable: false
2562
+ })
2563
+ };
2564
+ }());
2565
+ }
2566
+ function logStreamFailover(provider, toProvider, error) {
2567
+ audit?.log("provider_failover", {
2568
+ fromProvider: provider,
2569
+ toProvider,
2570
+ strategy: config.strategy,
2571
+ reason: error?.message
2572
+ });
2573
+ }
2574
+ async function tryStreamProvider(entry, request, emit) {
2575
+ const gw = getGateway(entry.name);
2576
+ const providerStream = await callWithCircuitBreaker(entry.name, async () => gw.stream({ ...request, model: request.model ?? entry.model }));
2577
+ let hasEmittedContent = false;
2578
+ for await (const event of providerStream) {
2579
+ if (event.type === "error") {
2580
+ const err2 = event.error instanceof Error ? event.error : new Error(String(event.error));
2581
+ if (hasEmittedContent) {
2582
+ emit(event);
2583
+ return { success: true };
2584
+ }
2585
+ return { success: false, error: err2 };
2586
+ }
2587
+ hasEmittedContent = true;
2588
+ emit(event);
2589
+ }
2590
+ return { success: true };
2591
+ }
2592
+ async function runStreamFallbackLoop(available, request, emit) {
2593
+ let lastError = null;
2594
+ let failedProvider = null;
2595
+ for (let i = 0;i < available.length; i++) {
2596
+ const entry = available[i];
2597
+ const nextProvider = i + 1 < available.length ? available[i + 1].name : "none";
2598
+ try {
2599
+ const result = await tryStreamProvider(entry, request, emit);
2600
+ if (result.success) {
2601
+ if (failedProvider)
2602
+ logFailover(failedProvider, entry.name, lastError?.message);
2603
+ return;
2604
+ }
2605
+ lastError = result.error ?? null;
2606
+ failedProvider = entry.name;
2607
+ logStreamFailover(entry.name, nextProvider, result.error);
2608
+ } catch (err2) {
2609
+ failedProvider = entry.name;
2610
+ lastError = toError2(err2);
2611
+ logStreamFailover(entry.name, nextProvider, lastError);
2612
+ }
2613
+ }
2614
+ emit({
2615
+ type: "error",
2616
+ error: lastError ?? new ElsiumError({
2617
+ code: "PROVIDER_ERROR",
2618
+ message: "All providers failed during streaming",
2619
+ retryable: false
2620
+ })
2621
+ });
2622
+ }
2623
+ function streamWithFallback(providers, request) {
2624
+ const available = providers.filter((e) => isProviderAvailable(e.name));
2625
+ if (available.length === 0) {
2626
+ return errorStream("All providers unavailable");
2627
+ }
2628
+ return createStream(async (emit) => {
2629
+ await runStreamFallbackLoop(available, request, emit);
2630
+ });
2631
+ }
2632
+ function streamCostOptimized(request) {
2633
+ const optimizer = config.costOptimizer;
2634
+ if (!optimizer) {
2635
+ return streamWithFallback(sortedProviders, request);
2636
+ }
2637
+ const complexity = estimateComplexity(request);
2638
+ const threshold = optimizer.complexityThreshold ?? 0.5;
2639
+ const target = complexity < threshold ? optimizer.simpleModel : optimizer.complexModel;
2640
+ return createStream(async (emit) => {
2641
+ try {
2642
+ const gw = getGateway(target.provider);
2643
+ const providerStream = gw.stream({ ...request, model: target.model });
2644
+ for await (const event of providerStream) {
2645
+ emit(event);
2646
+ }
2647
+ } catch {
2648
+ const fallbackStream = streamWithFallback(sortedProviders, request);
2649
+ for await (const event of fallbackStream) {
2650
+ emit(event);
2651
+ }
2652
+ }
2653
+ });
2654
+ }
2655
+ function streamLatencyOptimized(request) {
2656
+ const available = sortedProviders.filter((e) => isProviderAvailable(e.name));
2657
+ if (available.length === 0) {
2658
+ return errorStream("All providers unavailable");
2659
+ }
2660
+ return createStream(async (emit) => {
2661
+ const controller = new AbortController;
2662
+ const racePromises = available.map(async (entry) => {
2663
+ const gw = getGateway(entry.name);
2664
+ return callWithCircuitBreaker(entry.name, async () => ({
2665
+ entry,
2666
+ stream: gw.stream({
2667
+ ...request,
2668
+ model: request.model ?? entry.model,
2669
+ signal: controller.signal
2670
+ })
2671
+ }));
2672
+ });
2673
+ try {
2674
+ const winner = await Promise.any(racePromises);
2675
+ controller.abort();
2676
+ for await (const event of winner.stream) {
2677
+ emit(event);
2678
+ }
2679
+ } catch {
2680
+ emit({
2681
+ type: "error",
2682
+ error: new ElsiumError({
2683
+ code: "PROVIDER_ERROR",
2684
+ message: "All providers failed during streaming",
2685
+ retryable: false
2686
+ })
2687
+ });
2688
+ }
2689
+ });
2690
+ }
2691
+ function streamCapabilityAware(request) {
2692
+ const capabilities = detectRequiredCapabilities(request);
2693
+ const capable = filterCapableProviders(capabilities);
2694
+ if (capable.length === 0) {
2695
+ return streamWithFallback(sortedProviders, request);
2696
+ }
2697
+ return streamWithFallback(capable, request);
2698
+ }
2699
+ return {
2700
+ providers: sortedProviders.map((p) => p.name),
2701
+ strategy: config.strategy,
2702
+ async complete(request) {
2703
+ switch (config.strategy) {
2704
+ case "fallback":
2705
+ return fallbackComplete(request);
2706
+ case "cost-optimized":
2707
+ return costOptimizedComplete(request);
2708
+ case "latency-optimized":
2709
+ return latencyOptimizedComplete(request);
2710
+ case "capability-aware":
2711
+ return capabilityAwareComplete(request);
2712
+ default:
2713
+ return fallbackComplete(request);
2714
+ }
2715
+ },
2716
+ stream(request) {
2717
+ switch (config.strategy) {
2718
+ case "fallback":
2719
+ return streamWithFallback(sortedProviders, request);
2720
+ case "cost-optimized":
2721
+ return streamCostOptimized(request);
2722
+ case "latency-optimized":
2723
+ return streamLatencyOptimized(request);
2724
+ case "capability-aware":
2725
+ return streamCapabilityAware(request);
2726
+ default:
2727
+ return streamWithFallback(sortedProviders, request);
2728
+ }
2729
+ }
2730
+ };
2731
+ }
2135
2732
  // ../observe/src/span.ts
2136
2733
  function createSpan(name, options = {}) {
2137
2734
  const id = generateId("spn");
@@ -2337,12 +2934,17 @@ function createNoopSpan(name, kind) {
2337
2934
  }
2338
2935
  };
2339
2936
  }
2937
+ // ../observe/src/audit-sink.ts
2938
+ var log8 = createLogger();
2939
+
2340
2940
  // ../observe/src/audit.ts
2341
2941
  var ZERO_HASH = "0".repeat(64);
2342
2942
  // ../observe/src/experiment.ts
2343
- var log8 = createLogger();
2344
- // ../observe/src/otel.ts
2345
2943
  var log9 = createLogger();
2944
+ // ../observe/src/studio-exporter.ts
2945
+ var log10 = createLogger();
2946
+ // ../observe/src/otel.ts
2947
+ var log11 = createLogger();
2346
2948
  // ../../node_modules/.bun/@hono+node-server@1.19.10/node_modules/@hono/node-server/dist/index.mjs
2347
2949
  import { createServer as createServerHTTP } from "http";
2348
2950
  import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
@@ -2877,7 +3479,7 @@ var serve = (options, listeningListener) => {
2877
3479
  return server;
2878
3480
  };
2879
3481
 
2880
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/compose.js
3482
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/compose.js
2881
3483
  var compose = (middleware, onError, onNotFound) => {
2882
3484
  return (context, next) => {
2883
3485
  let index = -1;
@@ -2921,10 +3523,10 @@ var compose = (middleware, onError, onNotFound) => {
2921
3523
  };
2922
3524
  };
2923
3525
 
2924
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/request/constants.js
3526
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/request/constants.js
2925
3527
  var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
2926
3528
 
2927
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/utils/body.js
3529
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/utils/body.js
2928
3530
  var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
2929
3531
  const { all = false, dot = false } = options;
2930
3532
  const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
@@ -2978,6 +3580,9 @@ var handleParsingAllValues = (form, key, value) => {
2978
3580
  }
2979
3581
  };
2980
3582
  var handleParsingNestedValues = (form, key, value) => {
3583
+ if (/(?:^|\.)__proto__\./.test(key)) {
3584
+ return;
3585
+ }
2981
3586
  let nestedForm = form;
2982
3587
  const keys = key.split(".");
2983
3588
  keys.forEach((key2, index) => {
@@ -2992,7 +3597,7 @@ var handleParsingNestedValues = (form, key, value) => {
2992
3597
  });
2993
3598
  };
2994
3599
 
2995
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/utils/url.js
3600
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/utils/url.js
2996
3601
  var splitPath = (path) => {
2997
3602
  const paths = path.split("/");
2998
3603
  if (paths[0] === "") {
@@ -3192,7 +3797,7 @@ var getQueryParams = (url, key) => {
3192
3797
  };
3193
3798
  var decodeURIComponent_ = decodeURIComponent;
3194
3799
 
3195
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/request.js
3800
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/request.js
3196
3801
  var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
3197
3802
  var HonoRequest = class {
3198
3803
  raw;
@@ -3303,7 +3908,7 @@ var HonoRequest = class {
3303
3908
  }
3304
3909
  };
3305
3910
 
3306
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/utils/html.js
3911
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/utils/html.js
3307
3912
  var HtmlEscapedCallbackPhase = {
3308
3913
  Stringify: 1,
3309
3914
  BeforeStream: 2,
@@ -3341,7 +3946,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
3341
3946
  }
3342
3947
  };
3343
3948
 
3344
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/context.js
3949
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/context.js
3345
3950
  var TEXT_PLAIN = "text/plain; charset=UTF-8";
3346
3951
  var setDefaultContentType = (contentType, headers) => {
3347
3952
  return {
@@ -3508,7 +4113,7 @@ var Context = class {
3508
4113
  };
3509
4114
  };
3510
4115
 
3511
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router.js
4116
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router.js
3512
4117
  var METHOD_NAME_ALL = "ALL";
3513
4118
  var METHOD_NAME_ALL_LOWERCASE = "all";
3514
4119
  var METHODS = ["get", "post", "put", "delete", "options", "patch"];
@@ -3516,10 +4121,10 @@ var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is
3516
4121
  var UnsupportedPathError = class extends Error {
3517
4122
  };
3518
4123
 
3519
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/utils/constants.js
4124
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/utils/constants.js
3520
4125
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
3521
4126
 
3522
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/hono-base.js
4127
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/hono-base.js
3523
4128
  var notFoundHandler = (c) => {
3524
4129
  return c.text("404 Not Found", 404);
3525
4130
  };
@@ -3738,7 +4343,7 @@ var Hono = class _Hono {
3738
4343
  };
3739
4344
  };
3740
4345
 
3741
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router/reg-exp-router/matcher.js
4346
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/matcher.js
3742
4347
  var emptyParam = [];
3743
4348
  function match(method, path) {
3744
4349
  const matchers = this.buildAllMatchers();
@@ -3759,7 +4364,7 @@ function match(method, path) {
3759
4364
  return match2(method, path);
3760
4365
  }
3761
4366
 
3762
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router/reg-exp-router/node.js
4367
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/node.js
3763
4368
  var LABEL_REG_EXP_STR = "[^/]+";
3764
4369
  var ONLY_WILDCARD_REG_EXP_STR = ".*";
3765
4370
  var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
@@ -3863,7 +4468,7 @@ var Node = class _Node {
3863
4468
  }
3864
4469
  };
3865
4470
 
3866
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router/reg-exp-router/trie.js
4471
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/trie.js
3867
4472
  var Trie = class {
3868
4473
  #context = { varIndex: 0 };
3869
4474
  #root = new Node;
@@ -3919,7 +4524,7 @@ var Trie = class {
3919
4524
  }
3920
4525
  };
3921
4526
 
3922
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router/reg-exp-router/router.js
4527
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/router.js
3923
4528
  var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
3924
4529
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
3925
4530
  function buildWildcardRegExp(path) {
@@ -4084,7 +4689,7 @@ var RegExpRouter = class {
4084
4689
  }
4085
4690
  };
4086
4691
 
4087
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
4692
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
4088
4693
  var PreparedRegExpRouter = class {
4089
4694
  name = "PreparedRegExpRouter";
4090
4695
  #matchers;
@@ -4156,7 +4761,7 @@ var PreparedRegExpRouter = class {
4156
4761
  match = match;
4157
4762
  };
4158
4763
 
4159
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router/smart-router/router.js
4764
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router/smart-router/router.js
4160
4765
  var SmartRouter = class {
4161
4766
  name = "SmartRouter";
4162
4767
  #routers = [];
@@ -4211,7 +4816,7 @@ var SmartRouter = class {
4211
4816
  }
4212
4817
  };
4213
4818
 
4214
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router/trie-router/node.js
4819
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router/trie-router/node.js
4215
4820
  var emptyParams = /* @__PURE__ */ Object.create(null);
4216
4821
  var hasChildren = (children) => {
4217
4822
  for (const _ in children) {
@@ -4380,7 +4985,7 @@ var Node2 = class _Node2 {
4380
4985
  }
4381
4986
  };
4382
4987
 
4383
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/router/trie-router/router.js
4988
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/router/trie-router/router.js
4384
4989
  var TrieRouter = class {
4385
4990
  name = "TrieRouter";
4386
4991
  #node;
@@ -4402,7 +5007,7 @@ var TrieRouter = class {
4402
5007
  }
4403
5008
  };
4404
5009
 
4405
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/hono.js
5010
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/hono.js
4406
5011
  var Hono2 = class extends Hono {
4407
5012
  constructor(options = {}) {
4408
5013
  super(options);
@@ -4502,12 +5107,12 @@ function requestIdMiddleware() {
4502
5107
  };
4503
5108
  }
4504
5109
  function requestLoggerMiddleware(logger) {
4505
- const log10 = logger ?? createLogger();
5110
+ const log12 = logger ?? createLogger();
4506
5111
  return async (c, next) => {
4507
5112
  const start = Date.now();
4508
5113
  await next();
4509
5114
  const duration = Date.now() - start;
4510
- log10.info(`${c.req.method} ${c.req.path}`, {
5115
+ log12.info(`${c.req.method} ${c.req.path}`, {
4511
5116
  method: c.req.method,
4512
5117
  path: c.req.path,
4513
5118
  status: c.res.status,
@@ -4517,7 +5122,7 @@ function requestLoggerMiddleware(logger) {
4517
5122
  };
4518
5123
  }
4519
5124
 
4520
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/utils/stream.js
5125
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/utils/stream.js
4521
5126
  var StreamingApi = class {
4522
5127
  writer;
4523
5128
  encoder;
@@ -4583,7 +5188,7 @@ var StreamingApi = class {
4583
5188
  }
4584
5189
  };
4585
5190
 
4586
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/helper/streaming/utils.js
5191
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/helper/streaming/utils.js
4587
5192
  var isOldBunVersion = () => {
4588
5193
  const version = typeof Bun !== "undefined" ? Bun.version : undefined;
4589
5194
  if (version === undefined) {
@@ -4594,7 +5199,7 @@ var isOldBunVersion = () => {
4594
5199
  return result;
4595
5200
  };
4596
5201
 
4597
- // ../../node_modules/.bun/hono@4.12.5/node_modules/hono/dist/helper/streaming/stream.js
5202
+ // ../../node_modules/.bun/hono@4.12.8/node_modules/hono/dist/helper/streaming/stream.js
4598
5203
  var contextStash = /* @__PURE__ */ new WeakMap;
4599
5204
  var stream = (c, cb, onError) => {
4600
5205
  const { readable, writable } = new TransformStream;
@@ -4762,12 +5367,13 @@ function createRoutes(deps) {
4762
5367
  return c.json({ error: resolved.error }, 404);
4763
5368
  }
4764
5369
  if (body.stream) {
4765
- const stream2 = deps.gateway.stream({
5370
+ const streamSource = deps.mesh ?? deps.gateway;
5371
+ const agentStream = streamSource.stream({
4766
5372
  messages: [{ role: "user", content: body.message }],
4767
5373
  system: resolved.agent.config.system,
4768
5374
  model: resolved.agent.config.model
4769
5375
  });
4770
- return streamResponse(c, stream2);
5376
+ return streamResponse(c, agentStream);
4771
5377
  }
4772
5378
  let result;
4773
5379
  try {
@@ -4798,8 +5404,9 @@ function createRoutes(deps) {
4798
5404
  role: m.role,
4799
5405
  content: m.content
4800
5406
  }));
5407
+ const completeSource = deps.mesh ?? deps.gateway;
4801
5408
  if (body.stream) {
4802
- const stream2 = deps.gateway.stream({
5409
+ const stream2 = completeSource.stream({
4803
5410
  messages,
4804
5411
  model: body.model,
4805
5412
  system: body.system,
@@ -4810,7 +5417,7 @@ function createRoutes(deps) {
4810
5417
  }
4811
5418
  let response;
4812
5419
  try {
4813
- response = await deps.gateway.complete({
5420
+ response = await completeSource.complete({
4814
5421
  messages,
4815
5422
  model: body.model,
4816
5423
  system: body.system,
@@ -4848,34 +5455,59 @@ function createRoutes(deps) {
4848
5455
  }
4849
5456
 
4850
5457
  // src/app.ts
4851
- var log10 = createLogger();
5458
+ var log12 = createLogger();
4852
5459
  function createApp(config) {
4853
5460
  const app = new Hono2;
4854
5461
  app.onError((err2, c) => {
4855
5462
  const statusCode = err2 instanceof ElsiumError ? err2.statusCode ?? 500 : 500;
4856
5463
  const code = err2 instanceof ElsiumError ? err2.code : "UNKNOWN";
4857
- log10.error("Unhandled error", { error: err2.message, code, path: c.req.path });
5464
+ log12.error("Unhandled error", { error: err2.message, code, path: c.req.path });
4858
5465
  return c.json({ error: err2.message, code }, statusCode);
4859
5466
  });
4860
5467
  app.notFound((c) => {
4861
5468
  return c.json({ error: "Not found" }, 404);
4862
5469
  });
4863
5470
  const providerNames = Object.keys(config.gateway.providers);
4864
- const primaryProvider = providerNames[0];
4865
- const primaryConfig = config.gateway.providers[primaryProvider];
4866
- const gw = gateway({
4867
- provider: primaryProvider,
4868
- model: config.gateway.defaultModel,
4869
- apiKey: primaryConfig.apiKey,
4870
- baseUrl: primaryConfig.baseUrl
4871
- });
5471
+ let gw;
5472
+ let mesh;
5473
+ if (providerNames.length > 1) {
5474
+ const entries = providerNames.map((name) => ({
5475
+ name,
5476
+ config: {
5477
+ apiKey: config.gateway.providers[name].apiKey,
5478
+ baseUrl: config.gateway.providers[name].baseUrl
5479
+ },
5480
+ model: config.gateway.providers[name].model
5481
+ }));
5482
+ mesh = createProviderMesh({
5483
+ providers: entries,
5484
+ strategy: config.gateway.strategy ?? "fallback"
5485
+ });
5486
+ const primaryProvider = providerNames[0];
5487
+ const primaryConfig = config.gateway.providers[primaryProvider];
5488
+ gw = gateway({
5489
+ provider: primaryProvider,
5490
+ model: config.gateway.defaultModel,
5491
+ apiKey: primaryConfig.apiKey,
5492
+ baseUrl: primaryConfig.baseUrl
5493
+ });
5494
+ } else {
5495
+ const primaryProvider = providerNames[0];
5496
+ const primaryConfig = config.gateway.providers[primaryProvider];
5497
+ gw = gateway({
5498
+ provider: primaryProvider,
5499
+ model: config.gateway.defaultModel,
5500
+ apiKey: primaryConfig.apiKey,
5501
+ baseUrl: primaryConfig.baseUrl
5502
+ });
5503
+ }
4872
5504
  const tracer = observe({
4873
5505
  output: config.observe?.tracing ? ["console"] : [],
4874
5506
  costTracking: config.observe?.costTracking ?? true
4875
5507
  });
4876
5508
  const serverConfig = config.server ?? {};
4877
5509
  app.use("*", requestIdMiddleware());
4878
- app.use("*", requestLoggerMiddleware(log10));
5510
+ app.use("*", requestLoggerMiddleware(log12));
4879
5511
  if (serverConfig.cors) {
4880
5512
  app.use("*", corsMiddleware(serverConfig.cors));
4881
5513
  }
@@ -4894,6 +5526,7 @@ function createApp(config) {
4894
5526
  const defaultAgent = config.agents?.[0];
4895
5527
  const routes = createRoutes({
4896
5528
  gateway: gw,
5529
+ mesh,
4897
5530
  agents: agentMap,
4898
5531
  defaultAgent,
4899
5532
  tracer,
@@ -4905,6 +5538,7 @@ function createApp(config) {
4905
5538
  return {
4906
5539
  hono: app,
4907
5540
  gateway: gw,
5541
+ mesh,
4908
5542
  tracer,
4909
5543
  listen(port) {
4910
5544
  const listenPort = port ?? serverConfig.port ?? 3000;
@@ -4919,11 +5553,11 @@ function createApp(config) {
4919
5553
  const drainTimeoutMs = typeof serverConfig.gracefulShutdown === "object" ? serverConfig.gracefulShutdown.drainTimeoutMs : undefined;
4920
5554
  shutdownManager = createShutdownManager({
4921
5555
  drainTimeoutMs,
4922
- onDrainStart: () => log10.info("Draining connections..."),
4923
- onDrainComplete: () => log10.info("Drain complete")
5556
+ onDrainStart: () => log12.info("Draining connections..."),
5557
+ onDrainComplete: () => log12.info("Drain complete")
4924
5558
  });
4925
5559
  }
4926
- log10.info("ElsiumAI server started", {
5560
+ log12.info("ElsiumAI server started", {
4927
5561
  url: `http://${hostname}:${listenPort}`,
4928
5562
  routes: ["POST /chat", "POST /complete", "GET /health", "GET /metrics", "GET /agents"]
4929
5563
  });
@@ -4940,7 +5574,7 @@ function createApp(config) {
4940
5574
  };
4941
5575
  }
4942
5576
  // src/rbac.ts
4943
- var log11 = createLogger();
5577
+ var log13 = createLogger();
4944
5578
  var BUILT_IN_ROLES = [
4945
5579
  {
4946
5580
  name: "admin",
@@ -4978,7 +5612,7 @@ function matchPermission(granted, required) {
4978
5612
  }
4979
5613
  function createRBAC(config) {
4980
5614
  if (config.trustRoleHeader === true) {
4981
- log11.warn("RBAC: trustRoleHeader is enabled — any client can self-assign roles via the X-Role header. Only use this in development or behind a trusted reverse proxy.");
5615
+ log13.warn("RBAC: trustRoleHeader is enabled — any client can self-assign roles via the X-Role header. Only use this in development or behind a trusted reverse proxy.");
4982
5616
  }
4983
5617
  const roleMap = new Map;
4984
5618
  for (const role of BUILT_IN_ROLES) {
@@ -5035,7 +5669,7 @@ function createRBAC(config) {
5035
5669
  };
5036
5670
  }
5037
5671
  // src/tenant.ts
5038
- var log12 = createLogger();
5672
+ var log14 = createLogger();
5039
5673
  var tenantUsage = new Map;
5040
5674
  function tenantMiddleware(config) {
5041
5675
  const { extractTenant, onUnknownTenant = "reject", defaultTenant } = config;
@@ -5044,13 +5678,13 @@ function tenantMiddleware(config) {
5044
5678
  if (!tenant) {
5045
5679
  if (onUnknownTenant === "default" && defaultTenant) {
5046
5680
  c.set("tenant", defaultTenant);
5047
- log12.debug("Using default tenant", { tenantId: defaultTenant.tenantId });
5681
+ log14.debug("Using default tenant", { tenantId: defaultTenant.tenantId });
5048
5682
  } else {
5049
5683
  return c.json({ error: "Tenant identification required" }, 401);
5050
5684
  }
5051
5685
  } else {
5052
5686
  c.set("tenant", tenant);
5053
- log12.debug("Tenant identified", { tenantId: tenant.tenantId });
5687
+ log14.debug("Tenant identified", { tenantId: tenant.tenantId });
5054
5688
  }
5055
5689
  await next();
5056
5690
  };
package/dist/routes.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import type { Agent } from '@elsium-ai/agents';
2
- import type { Gateway } from '@elsium-ai/gateway';
2
+ import type { Gateway, ProviderMesh } from '@elsium-ai/gateway';
3
3
  import type { Tracer } from '@elsium-ai/observe';
4
4
  import { Hono } from 'hono';
5
5
  export interface RoutesDeps {
6
6
  gateway: Gateway;
7
+ mesh?: ProviderMesh;
7
8
  agents: Map<string, Agent>;
8
9
  defaultAgent?: Agent;
9
10
  tracer?: Tracer;
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAqE3B,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC1B,YAAY,CAAC,EAAE,KAAK,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAkKnD"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAqE3B,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC1B,YAAY,CAAC,EAAE,KAAK,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAqKnD"}
package/dist/sse.d.ts CHANGED
@@ -2,5 +2,5 @@ import type { ElsiumStream } from '@elsium-ai/core';
2
2
  import type { Context } from 'hono';
3
3
  export declare function sseHeaders(): Record<string, string>;
4
4
  export declare function formatSSE(event: string, data: unknown): string;
5
- export declare function streamResponse(c: Context, source: ElsiumStream): Response;
5
+ export declare function streamResponse(c: Context, source: ElsiumStream | AsyncIterable<unknown>): Response;
6
6
  //# sourceMappingURL=sse.d.ts.map
package/dist/sse.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,iBAAiB,CAAA;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOnD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAM9D;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,GAAG,QAAQ,CAwBzE"}
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,iBAAiB,CAAA;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOnD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAM9D;AAED,wBAAgB,cAAc,CAC7B,CAAC,EAAE,OAAO,EACV,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,GAC3C,QAAQ,CAwBV"}
package/dist/types.d.ts CHANGED
@@ -5,8 +5,10 @@ export interface AppConfig {
5
5
  providers: Record<string, {
6
6
  apiKey: string;
7
7
  baseUrl?: string;
8
+ model?: string;
8
9
  }>;
9
10
  defaultModel?: string;
11
+ strategy?: 'fallback' | 'cost-optimized' | 'latency-optimized' | 'capability-aware';
10
12
  };
11
13
  agents?: Agent[];
12
14
  rag?: RAGPipeline;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elsium-ai/app",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "App bootstrap, HTTP server, and API routes for ElsiumAI",
5
5
  "license": "MIT",
6
6
  "author": "Eric Utrera <ebutrera9103@gmail.com>",
@@ -26,13 +26,13 @@
26
26
  "dev": "bun --watch src/index.ts"
27
27
  },
28
28
  "dependencies": {
29
- "@elsium-ai/core": "^0.7.0",
30
- "@elsium-ai/gateway": "^0.7.0",
31
- "@elsium-ai/agents": "^0.7.0",
32
- "@elsium-ai/tools": "^0.7.0",
33
- "@elsium-ai/observe": "^0.7.0",
34
- "@elsium-ai/rag": "^0.7.0",
35
- "@elsium-ai/workflows": "^0.7.0",
29
+ "@elsium-ai/core": "^0.9.0",
30
+ "@elsium-ai/gateway": "^0.9.0",
31
+ "@elsium-ai/agents": "^0.9.0",
32
+ "@elsium-ai/tools": "^0.9.0",
33
+ "@elsium-ai/observe": "^0.9.0",
34
+ "@elsium-ai/rag": "^0.9.0",
35
+ "@elsium-ai/workflows": "^0.9.0",
36
36
  "@hono/node-server": "^1.19.10",
37
37
  "hono": "^4.12.4",
38
38
  "zod": "^3.24.0"