@opengeni/api-router 0.12.1 → 0.12.2

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/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApp
3
- } from "./chunk-S2N4252E.js";
3
+ } from "./chunk-3JR6L6NJ.js";
4
4
 
5
5
  // src/index.ts
6
6
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/api-router",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "OpenGeni HTTP surface: the Hono adapter/router (createApp), routes, MCP HTTP transport, and HTTP access adapters over @opengeni/core. An engine-distribution surface — its runtime closure includes engine-internal packages.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -46,14 +46,14 @@
46
46
  "@opengeni/codex": "^0.2.7",
47
47
  "@opengeni/config": "^0.7.8",
48
48
  "@opengeni/contracts": "^0.20.1",
49
- "@opengeni/core": "^0.12.1",
49
+ "@opengeni/core": "^0.12.2",
50
50
  "@opengeni/db": "^0.13.1",
51
51
  "@opengeni/documents": "^0.2.38",
52
52
  "@opengeni/events": "^0.3.29",
53
53
  "@opengeni/github": "^0.3.20",
54
54
  "@opengeni/network": "^0.1.1",
55
55
  "@opengeni/observability": "^0.3.0",
56
- "@opengeni/runtime": "^0.13.12",
56
+ "@opengeni/runtime": "^0.13.13",
57
57
  "@opengeni/storage": "^0.2.32",
58
58
  "@temporalio/client": "^1.17.0",
59
59
  "better-auth": "^1.6.14",
package/src/app.ts CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  configuredAllowedModels,
4
4
  configuredAllowedReasoningEfforts,
5
5
  configuredModels,
6
+ withCodexCatalogProvider,
6
7
  } from "@opengeni/config";
7
8
  import {
8
9
  ClientConfig,
@@ -322,18 +323,21 @@ export function createApp(deps: AppDependencies): Hono {
322
323
 
323
324
  app.get("/v1/config/client", (c) => {
324
325
  c.header("cache-control", "no-store");
326
+ const catalogSettings = deps.settings.codexSubscriptionEnabled
327
+ ? withCodexCatalogProvider(deps.settings)
328
+ : deps.settings;
325
329
  return c.json(
326
330
  ClientConfig.parse({
327
331
  deploymentRevision: deps.settings.deploymentRevision,
328
332
  apiContractRevision: OPENGENI_API_CONTRACT_REVISION,
329
333
  ...(deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {}),
330
- defaultModel: canonicalizeConfiguredModelId(deps.settings, deps.settings.openaiModel),
331
- allowedModels: configuredAllowedModels(deps.settings),
334
+ defaultModel: canonicalizeConfiguredModelId(catalogSettings, catalogSettings.openaiModel),
335
+ allowedModels: configuredAllowedModels(catalogSettings),
332
336
  // Provider-grouped model list for the picker. configuredModels() carries the
333
337
  // union of the built-in allow-list and every registry provider's models, in
334
338
  // selection order (default model first); project each to the client-safe
335
339
  // ClientModel shape (ConfiguredModel.providerId → ClientModel.provider).
336
- models: configuredModels(deps.settings).map(projectClientModel),
340
+ models: configuredModels(catalogSettings).map(projectClientModel),
337
341
  defaultReasoningEffort: deps.settings.openaiReasoningEffort,
338
342
  allowedReasoningEfforts: configuredAllowedReasoningEfforts(deps.settings),
339
343
  mcpServers: deps.settings.mcpServers.map((server) => ({
package/src/mcp/server.ts CHANGED
@@ -776,6 +776,26 @@ function registerToolspaceProxyTools(server: McpServer, surface: ToolspaceMcpSur
776
776
  if (!surface) {
777
777
  return;
778
778
  }
779
+ // McpServer installs its tools/list handler lazily on the first registered
780
+ // tool. A legitimate empty Toolspace surface (no selected proxyable servers,
781
+ // no active turn, or all optional upstreams unavailable) must therefore seed
782
+ // and disable one invisible tool; otherwise `ogtool list` receives JSON-RPC
783
+ // "Method not found" instead of the valid `{ tools: [] }` response.
784
+ if (surface.tools.length === 0) {
785
+ server
786
+ .registerTool(
787
+ "__opengeni_empty_toolspace_surface__",
788
+ {
789
+ description: "Internal disabled placeholder for an empty Toolspace surface.",
790
+ inputSchema: z4.object({}),
791
+ },
792
+ async () => ({
793
+ content: [{ type: "text" as const, text: '{"unavailable":true}' }],
794
+ }),
795
+ )
796
+ .disable();
797
+ return;
798
+ }
779
799
  for (const tool of surface.tools) {
780
800
  server.registerTool(
781
801
  tool.name,
@@ -367,7 +367,13 @@ async function resolveToolListing(input: {
367
367
  sessionId,
368
368
  rootSessionId,
369
369
  turn: activeTurn,
370
- }).catch(() => null);
370
+ }).catch((error) => {
371
+ deps.observability?.warn("toolspace upstream connection failed", {
372
+ serverId,
373
+ ...toolspaceErrorAttributes(error),
374
+ });
375
+ return null;
376
+ });
371
377
  if (!connection) {
372
378
  aggregateBudget.replace(serverId, []);
373
379
  return;
@@ -375,7 +381,13 @@ async function resolveToolListing(input: {
375
381
  try {
376
382
  const listed = await connection.client
377
383
  .listTools(undefined, toolspaceRequestOptions(config))
378
- .catch(() => ({ tools: [] }));
384
+ .catch((error) => {
385
+ deps.observability?.warn("toolspace upstream tool list failed", {
386
+ serverId,
387
+ ...toolspaceErrorAttributes(error),
388
+ });
389
+ return { tools: [] };
390
+ });
379
391
  let boundedTools: readonly McpTool[];
380
392
  try {
381
393
  boundedTools = assertMcpToolListWithinBounds(listed.tools as McpTool[]) as McpTool[];
@@ -494,7 +506,18 @@ async function connectToolspaceServer(input: {
494
506
  rootSessionId: string;
495
507
  turn: SessionTurn;
496
508
  }): Promise<ConnectedToolspaceServer> {
497
- const guardedFetch = guardedMcpFetch(input.deps.settings, undiciFetch);
509
+ // npm Undici's dispatcher transport is not reliable under Bun. The worker's
510
+ // model-visible MCP path already uses Bun's native fetch while retaining the
511
+ // same pre-request destination-policy check; Toolspace must do the same or an
512
+ // embedded Bun API can expose a server to the model while silently dropping
513
+ // that exact server from `ogtool list`.
514
+ const useBunNativeFetch = !!process.versions.bun;
515
+ const mcpFetchImpl: FetchLike = useBunNativeFetch
516
+ ? globalThis.fetch.bind(globalThis)
517
+ : undiciFetch;
518
+ const guardedFetch = guardedMcpFetch(input.deps.settings, mcpFetchImpl, {
519
+ ...(useBunNativeFetch ? { pinResolvedDestination: false } : {}),
520
+ });
498
521
  const baseFetch: FetchLike = input.config.connectionRef
499
522
  ? connectionBrokerFetch(guardedFetch, input)
500
523
  : guardedFetch;
@@ -730,6 +753,24 @@ function toolspaceAuditSummary(value: unknown): {
730
753
  };
731
754
  }
732
755
 
756
+ function toolspaceErrorAttributes(error: unknown): {
757
+ errorClass: string;
758
+ errorCode?: string;
759
+ errorMessage?: string;
760
+ } {
761
+ if (!(error instanceof Error)) {
762
+ return { errorClass: typeof error };
763
+ }
764
+ const code = (error as Error & { code?: unknown }).code;
765
+ return {
766
+ errorClass: error.name,
767
+ ...(typeof code === "string" ? { errorCode: code } : {}),
768
+ ...(error.message
769
+ ? { errorMessage: error.message.replaceAll(/[\r\n]+/gu, " ").slice(0, 512) }
770
+ : {}),
771
+ };
772
+ }
773
+
733
774
  type ToolspaceReservation =
734
775
  | { status: "ok"; turn: SessionTurn }
735
776
  | { status: "no_active_turn" }