@axiom-lattice/gateway 8.3.0 → 8.4.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.
Files changed (109) hide show
  1. package/dist/{A2AAuthService-7UGQ7GMW.mjs → A2AAuthService-UKN53ZGP.mjs} +2 -2
  2. package/dist/{a2a-BHBLACJV.mjs → a2a-TDZ3TT6H.mjs} +3 -3
  3. package/dist/{a2a-standard-VL5I7B4W.mjs → a2a-standard-INVCUSAW.mjs} +39 -2
  4. package/dist/a2a-standard-INVCUSAW.mjs.map +1 -0
  5. package/dist/chunk-A7YNAU5L.mjs +377 -0
  6. package/dist/chunk-A7YNAU5L.mjs.map +1 -0
  7. package/dist/{chunk-3XIIQ6EK.mjs → chunk-BOPNXTMO.mjs} +66 -5
  8. package/dist/chunk-BOPNXTMO.mjs.map +1 -0
  9. package/dist/{chunk-L6MBEEII.mjs → chunk-JGQURU44.mjs} +24 -6
  10. package/dist/chunk-JGQURU44.mjs.map +1 -0
  11. package/dist/{chunk-GPHOYR3X.mjs → chunk-LCM4OQTI.mjs} +30 -1
  12. package/dist/chunk-LCM4OQTI.mjs.map +1 -0
  13. package/dist/{chunk-HVIKCUOF.mjs → chunk-MFWTGNQR.mjs} +74 -5
  14. package/dist/chunk-MFWTGNQR.mjs.map +1 -0
  15. package/dist/{chunk-HYYLYIMH.mjs → chunk-OBODBHIS.mjs} +2 -2
  16. package/dist/chunk-T2HIWZGP.mjs +580 -0
  17. package/dist/chunk-T2HIWZGP.mjs.map +1 -0
  18. package/dist/chunk-Y7OVVPVU.mjs +13 -0
  19. package/dist/chunk-Y7OVVPVU.mjs.map +1 -0
  20. package/dist/{chunk-GL7RV5L3.mjs → chunk-YR32MWEJ.mjs} +36 -73
  21. package/dist/chunk-YR32MWEJ.mjs.map +1 -0
  22. package/dist/embed/{embed-page-AgentDetailSideApp-O6G4NJ7I.css → embed-page-AgentDetailSideApp-JMJOOC4V.css} +1 -1
  23. package/dist/embed/{embed-page-AgentDetailSideApp-7YTMN643.js → embed-page-AgentDetailSideApp-TQEJK6ZF.js} +1 -1
  24. package/dist/embed/{embed-page-AgentGovernanceWorkbench-SFZNVMUH.css → embed-page-AgentGovernanceWorkbench-3T3ZFJI5.css} +1 -1
  25. package/dist/embed/{embed-page-AgentGovernanceWorkbench-YWJ4ZLEV.js → embed-page-AgentGovernanceWorkbench-II2MRGCG.js} +1 -1
  26. package/dist/embed/{embed-page-LangfuseDashboard-BEOJDMWF.js → embed-page-LangfuseDashboard-7OANGQXY.js} +1 -1
  27. package/dist/embed/{embed-page-MessageGraph-N2DXVMEK.js → embed-page-MessageGraph-KJPQWTHE.js} +1 -1
  28. package/dist/embed/{embed-page-TopologyRuntimeView-CWOXLVFW.css → embed-page-MessageGraph-UDC6GQA5.css} +1 -1
  29. package/dist/embed/{embed-page-ReactChart-3QDZX2WG.js → embed-page-ReactChart-2CAIM4JD.js} +1 -1
  30. package/dist/embed/{embed-page-ReactChart-MXGF35OQ.css → embed-page-ReactChart-URBKYQYE.css} +1 -1
  31. package/dist/embed/{embed-page-SkillFlow-3X24ILKU.js → embed-page-SkillFlow-243MXRXC.js} +1 -1
  32. package/dist/embed/{embed-page-TasksSideApp-MYIRJWFM.js → embed-page-TasksSideApp-OA7CULHS.js} +1 -1
  33. package/dist/embed/{embed-page-TasksSideApp-HV6DKQ26.css → embed-page-TasksSideApp-TTCP4ENP.css} +1 -1
  34. package/dist/embed/{embed-page-ThreadMaintenancePanel-V327PV5R.js → embed-page-ThreadMaintenancePanel-Z4424X3J.js} +1 -1
  35. package/dist/embed/{embed-page-WorkflowAutomationView-YQFB3CTV.css → embed-page-TopologyRuntimeView-S4AMB4RI.css} +1 -1
  36. package/dist/embed/{embed-page-TopologyRuntimeView-3O6VJUZR.js → embed-page-TopologyRuntimeView-THTCZUS7.js} +1 -1
  37. package/dist/embed/{embed-page-MessageGraph-UT2TCPSO.css → embed-page-WorkflowAutomationView-FWVCQ3YM.css} +1 -1
  38. package/dist/embed/{embed-page-WorkflowAutomationView-YHOS4WQR.js → embed-page-WorkflowAutomationView-YDIDG35E.js} +1 -1
  39. package/dist/embed/{embed-page-chunk-4XKCMM5C.js → embed-page-chunk-7ULTMRM2.js} +1 -1
  40. package/dist/embed/{embed-page-chunk-BSRASK7V.js → embed-page-chunk-FZX4A32D.js} +5 -5
  41. package/dist/embed/{embed-page-chunk-PMOGSY6W.js → embed-page-chunk-JMP7EQEF.js} +1 -1
  42. package/dist/embed/{embed-page-chunk-IELKCYQX.js → embed-page-chunk-L2EGVVJS.js} +1 -1
  43. package/dist/embed/{embed-page-chunk-WGEXK773.js → embed-page-chunk-LDQGH44H.js} +1 -1
  44. package/dist/embed/{embed-page-chunk-JTL73EBV.js → embed-page-chunk-OMN6G52V.js} +1 -1
  45. package/dist/embed/{embed-page-chunk-PROZXFWS.js → embed-page-chunk-RKQJBKN2.js} +1 -1
  46. package/dist/embed/{embed-page-chunk-YMVKQQXJ.js → embed-page-chunk-VYS3TAK4.js} +1 -1
  47. package/dist/embed/{embed-page-chunk-ASEIOK7X.js → embed-page-chunk-W5OP2CZG.js} +2 -2
  48. package/dist/embed/embed-page-chunk-Z6KWW64I.js +6837 -0
  49. package/dist/embed/{embed-page-en-E35GE7TA.js → embed-page-en-D4M3DPFT.js} +1 -1
  50. package/dist/embed/{embed-page-en-XJYWUJEL.js → embed-page-en-DNDGCPMA.js} +1 -1
  51. package/dist/embed/embed-page-en-H5NOZEKE.js +1 -0
  52. package/dist/embed/{embed-page-en-HDSVQ4O6.js → embed-page-en-QJRX2NQE.js} +1 -1
  53. package/dist/embed/{embed-page-zh-6EBUEFKP.js → embed-page-zh-2H4EB3F3.js} +1 -1
  54. package/dist/embed/embed-page-zh-4C2K3RH2.js +1 -0
  55. package/dist/embed/{embed-page-zh-IR652A6J.js → embed-page-zh-HTEHFCSL.js} +1 -1
  56. package/dist/embed/{embed-page-zh-ZLK3GLM3.js → embed-page-zh-NEXLMDHF.js} +1 -1
  57. package/dist/embed/embed-page.css +1 -1
  58. package/dist/embed/embed-page.js +1 -1
  59. package/dist/embed/mcp-app-page.js +61 -0
  60. package/dist/index.d.mts +29 -0
  61. package/dist/index.d.ts +29 -0
  62. package/dist/index.js +2551 -1476
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +116 -690
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/mcp-app-shares-SHPBJQXS.mjs +300 -0
  67. package/dist/mcp-app-shares-SHPBJQXS.mjs.map +1 -0
  68. package/dist/mcp-apps-QVUK6UBA.mjs +26 -0
  69. package/dist/{open-7ED27I5B.mjs → open-27ZPLBIW.mjs} +101 -18
  70. package/dist/open-27ZPLBIW.mjs.map +1 -0
  71. package/dist/{open-catalog-SZQ435AY.mjs → open-catalog-NVGP6SQP.mjs} +38 -7
  72. package/dist/open-catalog-NVGP6SQP.mjs.map +1 -0
  73. package/dist/{open-exposure-I3DN2X4E.mjs → open-exposure-BPLYUIX6.mjs} +4 -4
  74. package/dist/{open-files-V3R2BFTV.mjs → open-files-JPB2NBL6.mjs} +4 -4
  75. package/dist/{open-session-2Y7XJJW2.mjs → open-session-CLZT3HVG.mjs} +5 -5
  76. package/dist/open-session-CLZT3HVG.mjs.map +1 -0
  77. package/dist/openLattice-D5YTSZBY.mjs +28 -0
  78. package/dist/{registerExposedPlugins-6GCOE64C.mjs → registerExposedPlugins-2JGXSMAL.mjs} +4 -3
  79. package/dist/{resources-DAU5ZIPB.mjs → resources-YD5DOPGD.mjs} +83 -3
  80. package/dist/resources-YD5DOPGD.mjs.map +1 -0
  81. package/dist/{sandboxOpenCredentialProvider-AT227D4N.mjs → sandboxOpenCredentialProvider-RTYOYRCT.mjs} +19 -14
  82. package/dist/sandboxOpenCredentialProvider-RTYOYRCT.mjs.map +1 -0
  83. package/package.json +10 -8
  84. package/dist/a2a-standard-VL5I7B4W.mjs.map +0 -1
  85. package/dist/chunk-3XIIQ6EK.mjs.map +0 -1
  86. package/dist/chunk-GL7RV5L3.mjs.map +0 -1
  87. package/dist/chunk-GPHOYR3X.mjs.map +0 -1
  88. package/dist/chunk-GXZFO3VU.mjs +0 -42
  89. package/dist/chunk-GXZFO3VU.mjs.map +0 -1
  90. package/dist/chunk-HVIKCUOF.mjs.map +0 -1
  91. package/dist/chunk-L6MBEEII.mjs.map +0 -1
  92. package/dist/embed/embed-page-chunk-IBRMWDHE.js +0 -6835
  93. package/dist/embed/embed-page-en-GLT2HEZZ.js +0 -1
  94. package/dist/embed/embed-page-zh-XMEJPDMF.js +0 -1
  95. package/dist/open-7ED27I5B.mjs.map +0 -1
  96. package/dist/open-catalog-SZQ435AY.mjs.map +0 -1
  97. package/dist/open-session-2Y7XJJW2.mjs.map +0 -1
  98. package/dist/openLattice-Q6M5S6CY.mjs +0 -18
  99. package/dist/openRateLimiter-XEOL3A5G.mjs +0 -8
  100. package/dist/resources-DAU5ZIPB.mjs.map +0 -1
  101. package/dist/sandboxOpenCredentialProvider-AT227D4N.mjs.map +0 -1
  102. /package/dist/{A2AAuthService-7UGQ7GMW.mjs.map → A2AAuthService-UKN53ZGP.mjs.map} +0 -0
  103. /package/dist/{a2a-BHBLACJV.mjs.map → a2a-TDZ3TT6H.mjs.map} +0 -0
  104. /package/dist/{chunk-HYYLYIMH.mjs.map → chunk-OBODBHIS.mjs.map} +0 -0
  105. /package/dist/{openLattice-Q6M5S6CY.mjs.map → mcp-apps-QVUK6UBA.mjs.map} +0 -0
  106. /package/dist/{open-exposure-I3DN2X4E.mjs.map → open-exposure-BPLYUIX6.mjs.map} +0 -0
  107. /package/dist/{open-files-V3R2BFTV.mjs.map → open-files-JPB2NBL6.mjs.map} +0 -0
  108. /package/dist/{openRateLimiter-XEOL3A5G.mjs.map → openLattice-D5YTSZBY.mjs.map} +0 -0
  109. /package/dist/{registerExposedPlugins-6GCOE64C.mjs.map → registerExposedPlugins-2JGXSMAL.mjs.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/open/tenantOpenPlugins.ts"],"sourcesContent":["/**\n * Tenant sandbox plugin exposure for the Open MCP surface.\n *\n * Global plugins are registered into `openLattice` by `registerExposedPlugins`;\n * tenant sandbox plugins live in per-tenant volumes and are not part of that\n * registry. This module derives the credential's grant-filtered view of those\n * tenant plugins (tools + `ui://` resources) and routes execution back into the\n * tenant sandbox executor through the shared {@link invokePluginTool} seam.\n *\n * Scope is strictly the credential's `tenantId`; globals are merged by the\n * caller (`routes/open.ts`).\n */\n\nimport {\n getSandboxPluginCatalog,\n jsonSchemaToZod,\n resolveSandboxPluginConnections,\n} from \"@axiom-lattice/core\";\nimport type {\n OpenExecutionResult,\n OpenGrant,\n Plugin,\n SandboxPluginDiagnostic,\n} from \"@axiom-lattice/protocols\";\nimport {\n ConnectionNotAllowedError,\n grantCovers,\n type OpenToolDescriptor,\n} from \"./openLattice\";\nimport type { OpenUiResource } from \"./openMcpServer\";\nimport {\n invokePluginTool,\n PluginToolScopeError,\n type PluginToolCatalog,\n} from \"../plugins/pluginToolExecution\";\n\n/** Catalog surface needed to read tenant plugin files and execute tools. */\nexport interface TenantOpenPluginCatalog extends PluginToolCatalog {\n /** Read a file inside a tenant plugin directory (e.g. a `ui/*.html`). */\n readPluginFile(tenantId: string, pluginType: string, relPath: string): Promise<string>;\n}\n\n/**\n * Result of listing a tenant's sandbox plugins.\n *\n * The live catalog's `ensureLoaded` reports read failures as error-level\n * diagnostics instead of throwing, so the richer object shape is preferred.\n * A bare `Plugin[]` remains accepted for test/back-compat seams and is treated\n * as a healthy listing (`degraded: false`).\n */\nexport type TenantPluginListing =\n | Plugin[]\n | { plugins: Plugin[]; diagnostics?: SandboxPluginDiagnostic[] };\n\n/** Test seams for {@link buildTenantOpenPlugins}; both default to the live catalog. */\nexport interface TenantOpenPluginDeps {\n catalog?: TenantOpenPluginCatalog;\n listPlugins?: (tenantId: string) => Promise<TenantPluginListing>;\n /**\n * Resolve the invoked plugin's connection credentials.\n *\n * Defaults to core's {@link resolveSandboxPluginConnections}, scoped to the\n * credential tenant: grant-pinned keys when present, otherwise\n * `connectAll: true`. Injected by tests and by callers that have already\n * resolved connections.\n */\n resolveConnections?: (\n pluginType: string,\n ctx: TenantOpenPluginInvokeContext,\n ) => Promise<TenantConnectionResolution>;\n /**\n * Tool names already owned by the global/built-in Open catalog.\n *\n * Defense in depth for the `mergeOpenCatalog` guarantee: a tenant tool whose\n * name collides is skipped at build time, so the session never registers a\n * duplicate (the SDK throws on `registerTool`) **and** the bundle's\n * `hasTool` never claims a tool that must route to the global provider.\n */\n reservedToolNames?: ReadonlySet<string>;\n}\n\n/** Credentials resolved for one plugin domain, forwarded to the sandbox executor. */\nexport interface TenantConnectionResolution {\n connection?: Record<string, unknown>;\n connections?: Array<{ key: string; config: Record<string, unknown> }>;\n}\n\n/** Request-scoped identity forwarded when invoking a tenant plugin tool. */\nexport interface TenantOpenPluginInvokeContext {\n tenantId: string;\n workspaceId?: string;\n projectId?: string;\n /** Connection keys pinned by the covering grant (plugin domains only). */\n connectionKeys?: string[];\n /** Synthesized run identity forwarded alongside the invocation. */\n runConfig?: Record<string, unknown>;\n}\n\n/** The credential's grant-filtered tenant plugin bundle for one MCP session. */\nexport interface TenantOpenPlugins {\n tools: OpenToolDescriptor[];\n uiResources: Map<string, OpenUiResource>;\n hasTool(toolName: string): boolean;\n invoke(\n toolName: string,\n args: unknown,\n ctx: TenantOpenPluginInvokeContext,\n ): Promise<OpenExecutionResult>;\n /**\n * True when the tenant catalog could not be read reliably: it reported at\n * least one error-level diagnostic (e.g. `SANDBOX_UNAVAILABLE`,\n * `PLUGINS_LIST_FAILED`). Callers should log this and serve global tools only;\n * the bundle is still safe to use (empty tools, `hasTool` returns false).\n */\n degraded: boolean;\n}\n\n/** Narrow an unknown value to a plain data record, otherwise an empty record. */\nfunction asArgs(value: unknown): Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/** Serialize a handler result the way the Open surface renders tool output. */\nfunction toText(result: unknown): string {\n return typeof result === \"string\" ? result : JSON.stringify(result ?? null);\n}\n\n/** Adapter over the process-wide sandbox plugin catalog. */\nfunction defaultCatalog(): TenantOpenPluginCatalog {\n return {\n getValidatedManifest: (tenantId, type) =>\n getSandboxPluginCatalog().getValidatedManifest(tenantId, type),\n getExecutor: () => getSandboxPluginCatalog().getExecutor(),\n readPluginFile: (tenantId, type, relPath) =>\n getSandboxPluginCatalog().readPluginFile(tenantId, type, relPath),\n };\n}\n\n/**\n * Build the resolver config for one invocation.\n *\n * Pinned keys replace `connectAll`; an absent/empty list keeps the legacy\n * connect-all behavior used by the Open surface before selectors existed.\n */\nexport function connectionResolutionConfig(\n connectionKeys?: string[],\n): { connections: string[] } | { connectAll: true } {\n return connectionKeys && connectionKeys.length > 0\n ? { connections: connectionKeys }\n : { connectAll: true };\n}\n\n/**\n * Default connection resolution: every connection of the invoked plugin type,\n * scoped to the credential tenant (or only the grant-pinned keys).\n *\n * A missing connection store degrades to no credentials, matching the Agent\n * path's tolerance, so an unconfigured deployment fails at the plugin boundary\n * rather than throwing from the Open session.\n */\nasync function defaultResolveConnections(\n pluginType: string,\n ctx: TenantOpenPluginInvokeContext,\n): Promise<TenantConnectionResolution> {\n try {\n return await resolveSandboxPluginConnections(\n pluginType,\n connectionResolutionConfig(ctx.connectionKeys),\n {\n tenantId: ctx.tenantId,\n workspaceId: ctx.workspaceId,\n projectId: ctx.projectId,\n },\n );\n } catch (err) {\n if (err instanceof Error && err.message === \"ConnectionStore not configured\") {\n return {};\n }\n throw err;\n }\n}\n\n/**\n * Merge global/built-in Open tools with tenant sandbox plugin tools.\n *\n * The MCP SDK throws when `registerTool` sees a duplicate tool name, and the\n * built-in Open domains (`kb`/`sandbox`/`agent`) are not sandbox plugin types,\n * so a tenant plugin of the same type could otherwise shadow/crash the session.\n * **Global wins**: a tenant tool whose `toolName` already exists in the global\n * catalog is dropped. Tenant-internal duplicates are also collapsed\n * (first-wins) as a defensive measure.\n *\n * @param globalCatalog - Grant-filtered OpenLattice descriptors.\n * @param tenantTools - Tenant sandbox plugin descriptors.\n * @returns A duplicate-free catalog with global entries taking precedence.\n */\nexport function mergeOpenCatalog(\n globalCatalog: OpenToolDescriptor[],\n tenantTools: OpenToolDescriptor[],\n): OpenToolDescriptor[] {\n const seen = new Set(globalCatalog.map((t) => t.toolName));\n const merged = [...globalCatalog];\n for (const tool of tenantTools) {\n if (seen.has(tool.toolName)) continue;\n seen.add(tool.toolName);\n merged.push(tool);\n }\n return merged;\n}\n\n/**\n * Build the credential-scoped tenant plugin tool catalog and UI resources.\n *\n * Only tools whose manifest marks `expose: true` and whose `(domain, action)` is\n * covered by the credential's grants are included. A tool's declared\n * `ui.resource` becomes a deterministic `ui://${domain}/${resource}` URI whose\n * reader pulls the HTML from the tenant volume lazily.\n *\n * @param tenantId - Credential tenant; never caller-supplied.\n * @param grants - Credential grants (domain/action allow-list).\n * @param deps - Optional catalog/list seams for tests.\n * @returns The merged tool descriptors, UI resources, and invoke router.\n *\n * @remarks\n * Sandbox tools carry a restricted JSON Schema object, which is converted with\n * core's {@link jsonSchemaToZod} (the same conversion the manifest validator\n * used), so the registered MCP input schema matches the sandbox contract.\n */\nexport async function buildTenantOpenPlugins(\n tenantId: string,\n grants: OpenGrant[],\n deps: TenantOpenPluginDeps = {},\n): Promise<TenantOpenPlugins> {\n const catalog = deps.catalog ?? defaultCatalog();\n const listPlugins =\n deps.listPlugins ??\n (async (tenant: string) => {\n // `ensureLoaded` reports read failures as diagnostics (it does not throw),\n // so capture both halves of the snapshot to detect degradation.\n const snapshot = await getSandboxPluginCatalog().ensureLoaded(tenant);\n return { plugins: snapshot.plugins, diagnostics: snapshot.diagnostics };\n });\n const resolveConnections = deps.resolveConnections ?? defaultResolveConnections;\n const listed = await listPlugins(tenantId);\n const plugins = Array.isArray(listed) ? listed : listed.plugins;\n const diagnostics = Array.isArray(listed) ? [] : listed.diagnostics ?? [];\n // A throw from `listPlugins` still propagates: callers keep their own catch\n // (which degrades to an empty bundle). Any error-level diagnostic marks the\n // bundle degraded so callers can surface the real, non-throwing failure mode.\n const degraded = diagnostics.some((d) => d.level === \"error\");\n\n const tools: OpenToolDescriptor[] = [];\n const uiResources = new Map<string, OpenUiResource>();\n const toolIndex = new Map<string, { pluginType: string; action: string }>();\n\n for (const plugin of plugins) {\n const domain = plugin.meta.type;\n const manifest = await catalog.getValidatedManifest(tenantId, domain);\n if (!manifest) continue;\n for (const tool of manifest.tools ?? []) {\n if (tool.expose !== true) continue;\n if (!grantCovers(grants, domain, tool.name)) continue;\n const toolName = `${domain}_${tool.name}`;\n if (deps.reservedToolNames?.has(toolName)) continue;\n const ui = tool.ui?.resource\n ? {\n resourceUri: `ui://${domain}/${tool.ui.resource}`,\n csp: tool.ui.csp,\n displayMode: tool.ui.displayMode,\n }\n : undefined;\n if (tool.ui?.resource) {\n const resource = tool.ui.resource;\n const csp = tool.ui.csp;\n uiResources.set(ui!.resourceUri, {\n mimeType: \"text/html;profile=mcp-app\",\n csp,\n read: async () => ({\n html: await catalog.readPluginFile(tenantId, domain, resource),\n csp,\n }),\n });\n }\n tools.push({\n toolName,\n domain,\n action: tool.name,\n description: tool.description,\n schema: jsonSchemaToZod(tool.schema),\n annotations: {\n readOnlyHint: tool.readOnly ?? false,\n destructiveHint: tool.destructive ?? false,\n },\n ui,\n });\n toolIndex.set(toolName, { pluginType: domain, action: tool.name });\n }\n }\n\n return {\n tools,\n uiResources,\n hasTool: (toolName) => toolIndex.has(toolName),\n degraded,\n invoke: async (toolName, args, ctx) => {\n const entry = toolIndex.get(toolName);\n if (!entry) {\n return {\n content: [{ type: \"text\", text: `Unknown action: ${toolName}` }],\n isError: true,\n };\n }\n try {\n // Resolve the plugin's own connections from the credential tenant. The\n // Open surface has no agent run to select connection keys, so exposure\n // uses the Agent path's `connectAll` semantics unless the grant pins\n // specific connections.\n const resolution = await resolveConnections(entry.pluginType, ctx);\n const requested = ctx.connectionKeys;\n let connection = resolution.connection;\n let connections = resolution.connections;\n if (requested?.length) {\n const found = new Set((connections ?? []).map((item) => item.key));\n const missing = requested.filter((key) => !found.has(key));\n if (missing.length > 0) {\n throw new ConnectionNotAllowedError(\n `${entry.pluginType}: ${missing.join(\", \")}`,\n );\n }\n connections = (connections ?? []).filter((item) => requested.includes(item.key));\n connection = connections[0]?.config;\n }\n const result = await invokePluginTool(\n { catalog },\n ctx.tenantId,\n entry.pluginType,\n entry.action,\n asArgs(args),\n {\n workspaceId: ctx.workspaceId,\n projectId: ctx.projectId,\n ...(connection === undefined ? {} : { connection }),\n ...(connections === undefined ? {} : { connections }),\n },\n );\n return { content: [{ type: \"text\", text: toText(result) }] };\n } catch (error) {\n if (error instanceof PluginToolScopeError) {\n return {\n content: [{ type: \"text\", text: error.message }],\n isError: true,\n };\n }\n throw error;\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -31,6 +31,33 @@ function devModeDefaults() {
31
31
  const projectId = process.env[DEFAULT_PROJECT_ENV] || void 0;
32
32
  return { tenantId, projectId };
33
33
  }
34
+ function validateGrantSelectors(grants) {
35
+ if (grants === void 0) return;
36
+ if (!Array.isArray(grants)) {
37
+ throw new A2AAuthError("grants must be an array", 400);
38
+ }
39
+ for (const entry of grants) {
40
+ const grant = entry;
41
+ if (typeof grant !== "object" || grant === null || Array.isArray(grant)) {
42
+ throw new A2AAuthError("grant must be an object", 400);
43
+ }
44
+ const selector = grant.selector;
45
+ if (selector === void 0) continue;
46
+ if (typeof selector !== "object" || selector === null || Array.isArray(selector)) {
47
+ throw new A2AAuthError("grant.selector must be an object", 400);
48
+ }
49
+ const connections = selector.connections;
50
+ if (connections === void 0) continue;
51
+ if (!Array.isArray(connections) || connections.some(
52
+ (key) => typeof key !== "string" || key.trim().length === 0
53
+ )) {
54
+ throw new A2AAuthError(
55
+ "selector.connections must be an array of non-empty connection keys",
56
+ 400
57
+ );
58
+ }
59
+ }
60
+ }
34
61
  var A2AAuthService = class {
35
62
  constructor(deps) {
36
63
  /** Lazily-populated store key map; reloaded via {@link refresh} after keys CRUD. */
@@ -119,6 +146,7 @@ var A2AAuthService = class {
119
146
  if (!project || project.tenantId !== tenantId) {
120
147
  throw new A2AAuthError("Project not found", 400);
121
148
  }
149
+ validateGrantSelectors(grants);
122
150
  if (assistantIds && assistantIds.length > 0) {
123
151
  for (const id of assistantIds) {
124
152
  const assistant = await this.assistantStore.getAssistantById(tenantId, id);
@@ -137,6 +165,7 @@ var A2AAuthService = class {
137
165
  throw new A2AAuthError("Project not found", 400);
138
166
  }
139
167
  }
168
+ validateGrantSelectors(grants);
140
169
  if (assistantIds && assistantIds.length > 0) {
141
170
  for (const aId of assistantIds) {
142
171
  const assistant = await this.assistantStore.getAssistantById(tenantId, aId);
@@ -162,4 +191,4 @@ export {
162
191
  parseEnvKeys,
163
192
  A2AAuthService
164
193
  };
165
- //# sourceMappingURL=chunk-GPHOYR3X.mjs.map
194
+ //# sourceMappingURL=chunk-LCM4OQTI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/a2a/A2AAuthService.ts"],"sourcesContent":["import { effectiveGrants } from \"@axiom-lattice/protocols\";\nimport type {\n A2AApiKeyEntry,\n A2AApiKeyRecord,\n A2AApiKeyStore,\n A2AAuthContext,\n Assistant,\n AssistantStore,\n ProjectStore,\n OpenGrant,\n} from \"@axiom-lattice/protocols\";\n\n/**\n * Error thrown by {@link A2AAuthService} for authz/authn failures.\n * Carries an HTTP status code for the route layer to surface.\n */\nexport class A2AAuthError extends Error {\n public readonly statusCode: number;\n\n constructor(message: string, statusCode: number) {\n super(message);\n this.name = \"A2AAuthError\";\n this.statusCode = statusCode;\n }\n}\n\n/**\n * Dependencies injected into {@link A2AAuthService} so stores can be mocked in tests.\n */\nexport interface A2AAuthServiceDeps {\n keyStore: A2AApiKeyStore;\n assistantStore: AssistantStore;\n projectStore: ProjectStore;\n envKeyMap?: Map<string, A2AApiKeyEntry>;\n /**\n * When true, requests with no key are allowed through when no keys are\n * configured (dev mode). Defaults to false — JSON-RPC stays locked until a\n * key is configured or this opt-in is explicitly enabled.\n */\n allowUnauthenticated?: boolean;\n}\n\nexport interface UpdateKeyScopedInput {\n label?: string;\n projectId?: string;\n assistantIds?: string[];\n grants?: OpenGrant[];\n}\n\n/** Input for creating an API key scoped to a tenant + project. */\nexport interface CreateKeyScopedInput {\n tenantId: string;\n projectId?: string;\n assistantIds?: string[];\n /** Open-door grants (validated minimally; unknown domains simply never match). */\n grants?: OpenGrant[];\n label?: string;\n}\n\nconst DEFAULT_TENANT_ENV = \"A2A_DEFAULT_TENANT_ID\";\nconst DEFAULT_TENANT_ID = \"a2a-default-tenant\";\nconst DEFAULT_PROJECT_ENV = \"A2A_DEFAULT_PROJECT_ID\";\n\n/** Matches an HTTP `Authorization` Bearer scheme, case-insensitively (`bearer`/`Bearer`). */\nconst BEARER_SCHEME_RE = /^Bearer\\s+/i;\n\n/**\n * Parse an `A2A_API_KEYS=\"key:tenant:project,...\"` env string into a lookup map.\n * Entries missing a key, tenant, or project segment are skipped.\n */\nexport function parseEnvKeys(env: string): Map<string, A2AApiKeyEntry> {\n const map = new Map<string, A2AApiKeyEntry>();\n\n if (!env) return map;\n\n for (const entry of env.split(\",\")) {\n const trimmed = entry.trim();\n if (!trimmed) continue;\n\n const parts = trimmed.split(\":\");\n const key = parts[0]?.trim();\n const tenantId = parts[1]?.trim();\n const projectId = parts[2]?.trim();\n if (!key || !tenantId || !projectId) continue;\n\n map.set(key, { key, tenantId, projectId });\n }\n\n return map;\n}\n\n/**\n * Resolve the dev-mode default tenant/project when no keys are configured.\n */\nfunction devModeDefaults(): { tenantId: string; projectId?: string } {\n const tenantId = process.env[DEFAULT_TENANT_ENV] || DEFAULT_TENANT_ID;\n const projectId = process.env[DEFAULT_PROJECT_ENV] || undefined;\n return { tenantId, projectId };\n}\n\n/** Reject malformed grant connection selectors before they are persisted. */\nfunction validateGrantSelectors(grants: OpenGrant[] | undefined): void {\n if (grants === undefined) return;\n if (!Array.isArray(grants)) {\n throw new A2AAuthError(\"grants must be an array\", 400);\n }\n for (const entry of grants as unknown[]) {\n const grant: unknown = entry;\n if (typeof grant !== \"object\" || grant === null || Array.isArray(grant)) {\n throw new A2AAuthError(\"grant must be an object\", 400);\n }\n const selector: unknown = (grant as { selector?: unknown }).selector;\n if (selector === undefined) continue;\n if (typeof selector !== \"object\" || selector === null || Array.isArray(selector)) {\n throw new A2AAuthError(\"grant.selector must be an object\", 400);\n }\n const connections = (selector as { connections?: unknown }).connections;\n if (connections === undefined) continue;\n if (\n !Array.isArray(connections)\n || connections.some(\n (key) => typeof key !== \"string\" || key.trim().length === 0,\n )\n ) {\n throw new A2AAuthError(\n \"selector.connections must be an array of non-empty connection keys\",\n 400,\n );\n }\n }\n}\n\n/**\n * Authenticates A2A requests against the key store + env key map,\n * enforces assistant whitelist / exposure, and validates key scope on creation.\n */\nexport class A2AAuthService {\n private readonly keyStore: A2AApiKeyStore;\n private readonly assistantStore: AssistantStore;\n private readonly projectStore: ProjectStore;\n private readonly envKeyMap: Map<string, A2AApiKeyEntry>;\n /** Lazily-populated store key map; reloaded via {@link refresh} after keys CRUD. */\n private storeKeyMapCache: Map<string, A2AApiKeyEntry> | null = null;\n private readonly allowUnauthenticated: boolean;\n\n constructor(deps: A2AAuthServiceDeps) {\n this.keyStore = deps.keyStore;\n this.assistantStore = deps.assistantStore;\n this.projectStore = deps.projectStore;\n this.envKeyMap = deps.envKeyMap ?? new Map();\n this.allowUnauthenticated = deps.allowUnauthenticated ?? false;\n }\n\n /**\n * Reload the cached store key map. Call once at startup and after any keys\n * CRUD mutation so {@link authenticate} does not hit the store on every request.\n */\n async refresh(): Promise<void> {\n this.storeKeyMapCache = await this.keyStore.loadIntoMap();\n }\n\n /** Return the cached store key map, loading it on first use. */\n private async getStoreKeyMap(): Promise<Map<string, A2AApiKeyEntry>> {\n if (this.storeKeyMapCache === null) {\n await this.refresh();\n }\n return this.storeKeyMapCache ?? new Map<string, A2AApiKeyEntry>();\n }\n\n /**\n * Authenticate an incoming request header (authorization or x-api-key value).\n * Returns an auth context; `authenticated: false` means the caller must reject.\n */\n async authenticate(header: string | undefined): Promise<A2AAuthContext> {\n const storeMap = await this.getStoreKeyMap();\n const noAuthRequired = storeMap.size === 0 && this.envKeyMap.size === 0;\n\n const token = this.extractToken(header);\n\n if (!token) {\n if (noAuthRequired && this.allowUnauthenticated) {\n const { tenantId, projectId } = devModeDefaults();\n return { authenticated: true, tenantId, projectId };\n }\n return { authenticated: false };\n }\n\n // Store keys take priority; env keys serve as fallback.\n const merged = new Map<string, A2AApiKeyEntry>([...this.envKeyMap, ...storeMap]);\n const entry = merged.get(token);\n\n if (!entry) return { authenticated: false };\n\n // Unified key management: the agent whitelist comes from the key's grants\n // when present, else the legacy assistantIds.\n //\n // - explicit grants: agent access only if an agent-domain grant exists\n // (missing agent grant => empty whitelist => no agents);\n // - bare agent grant (no items) => undefined => legacy \"all agents\";\n // - no grants: fall back to legacy assistantIds (undefined => all agents).\n const hasExplicitGrants = Array.isArray(entry.grants) && entry.grants.length > 0;\n const agentGrant = effectiveGrants(entry).find((g) => g.domain === \"agent\");\n const assistantIds = hasExplicitGrants\n ? (agentGrant?.items ?? [])\n : entry.assistantIds;\n\n return {\n authenticated: true,\n apiKey: token,\n tenantId: entry.tenantId,\n projectId: entry.projectId,\n assistantIds,\n source: header && BEARER_SCHEME_RE.test(header) ? \"bearer\" : \"x-api-key\",\n };\n }\n\n /**\n * Assert that the given assistant is reachable under the provided auth\n * context: authenticated, same tenant, and within the key's whitelist.\n *\n * Authorization is unified on the API key — the agent no longer carries an\n * exposure flag; the key's assistant whitelist / grants is the single source.\n */\n async assertAgentAccess(auth: A2AAuthContext, assistantId: string): Promise<Assistant> {\n if (!auth.authenticated || !auth.tenantId) {\n throw new A2AAuthError(\"Unauthorized\", 401);\n }\n\n const assistant = await this.assistantStore.getAssistantById(auth.tenantId, assistantId);\n if (!assistant || assistant.tenantId !== auth.tenantId) {\n throw new A2AAuthError(\"Agent not found\", 404);\n }\n\n // undefined = legacy \"all agents\"; [] = explicitly none; list = whitelist.\n const whitelist = auth.assistantIds;\n if (whitelist !== undefined && !whitelist.includes(assistantId)) {\n throw new A2AAuthError(\"Agent not allowed for this key\", 403);\n }\n\n return assistant;\n }\n\n /**\n * Create an API key scoped to a tenant + project, validating the project exists\n * in the tenant and every assistant belongs to the same tenant.\n */\n async createKeyScoped(input: CreateKeyScopedInput): Promise<A2AApiKeyRecord> {\n const { tenantId, projectId, assistantIds, grants, label } = input;\n\n if (!projectId) {\n throw new A2AAuthError(\"projectId is required\", 400);\n }\n\n const project = await this.projectStore.getProjectById(tenantId, projectId);\n if (!project || project.tenantId !== tenantId) {\n throw new A2AAuthError(\"Project not found\", 400);\n }\n\n validateGrantSelectors(grants);\n\n if (assistantIds && assistantIds.length > 0) {\n for (const id of assistantIds) {\n const assistant = await this.assistantStore.getAssistantById(tenantId, id);\n if (!assistant || assistant.tenantId !== tenantId) {\n throw new A2AAuthError(\"Assistant not found\", 400);\n }\n }\n }\n\n return this.keyStore.create({ tenantId, projectId, assistantIds, grants, label });\n }\n\n async updateKeyScoped(\n id: string,\n tenantId: string,\n input: UpdateKeyScopedInput,\n ): Promise<A2AApiKeyRecord> {\n const { projectId, assistantIds, grants, label } = input;\n if (projectId) {\n const project = await this.projectStore.getProjectById(tenantId, projectId);\n if (!project || project.tenantId !== tenantId) {\n throw new A2AAuthError(\"Project not found\", 400);\n }\n }\n validateGrantSelectors(grants);\n if (assistantIds && assistantIds.length > 0) {\n for (const aId of assistantIds) {\n const assistant = await this.assistantStore.getAssistantById(tenantId, aId);\n if (!assistant || assistant.tenantId !== tenantId) {\n throw new A2AAuthError(\"Assistant not found\", 400);\n }\n }\n }\n return this.keyStore.update(id, { label, projectId, assistantIds, grants });\n }\n\n private extractToken(header: string | undefined): string | undefined {\n if (!header) return undefined;\n const match = BEARER_SCHEME_RE.exec(header);\n if (match) {\n return header.substring(match[0].length);\n }\n return header;\n }\n}\n"],"mappings":"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","names":[]}
@@ -7,6 +7,13 @@ var GrantDeniedError = class extends Error {
7
7
  this.name = "GrantDeniedError";
8
8
  }
9
9
  };
10
+ var ConnectionNotAllowedError = class extends Error {
11
+ constructor(detail) {
12
+ super(`CONNECTION_NOT_ALLOWED: ${detail}`);
13
+ this.code = "CONNECTION_NOT_ALLOWED";
14
+ this.name = "ConnectionNotAllowedError";
15
+ }
16
+ };
10
17
  var ValidationError = class extends Error {
11
18
  constructor(detail) {
12
19
  super(`VALIDATION_FAILED: ${detail}`);
@@ -24,11 +31,52 @@ var ActionNotFoundError = class extends Error {
24
31
  function effectiveToolName(domain, action) {
25
32
  return action.toolName ?? `${domain}_${action.name}`;
26
33
  }
27
- function grantCovers(grants, domain, action) {
28
- return grants.some(
34
+ function findCoveringGrant(grants, domain, action) {
35
+ return grants.find(
29
36
  (g) => g.domain === domain && (!g.items || g.items.length === 0 || g.items.includes(action))
30
37
  );
31
38
  }
39
+ function normalizeConnectionKeys(value) {
40
+ if (value === void 0) return void 0;
41
+ if (!Array.isArray(value)) {
42
+ throw new ValidationError("selector.connections must be an array");
43
+ }
44
+ if (value.length === 0) return void 0;
45
+ const keys = /* @__PURE__ */ new Set();
46
+ for (const item of value) {
47
+ if (typeof item !== "string" || item.trim().length === 0) {
48
+ throw new ValidationError(
49
+ "selector.connections must contain non-empty strings"
50
+ );
51
+ }
52
+ keys.add(item);
53
+ }
54
+ return [...keys];
55
+ }
56
+ function normalizeGrantConnectionKeys(grant) {
57
+ const selector = grant.selector;
58
+ if (selector === void 0) return void 0;
59
+ if (typeof selector !== "object" || selector === null || Array.isArray(selector)) {
60
+ throw new ValidationError("grant.selector must be an object");
61
+ }
62
+ return normalizeConnectionKeys(selector.connections);
63
+ }
64
+ function resolveCoveringConnectionKeys(grants, domain, action) {
65
+ const covering = grants.filter(
66
+ (g) => g.domain === domain && (!g.items || g.items.length === 0 || g.items.includes(action))
67
+ );
68
+ if (covering.length === 0) return void 0;
69
+ const normalized = covering.map((grant) => normalizeGrantConnectionKeys(grant));
70
+ if (normalized.some((keys2) => !keys2)) return void 0;
71
+ const keys = /* @__PURE__ */ new Set();
72
+ for (const selected of normalized) {
73
+ for (const key of selected ?? []) keys.add(key);
74
+ }
75
+ return keys.size > 0 ? [...keys] : void 0;
76
+ }
77
+ function grantCovers(grants, domain, action) {
78
+ return findCoveringGrant(grants, domain, action) !== void 0;
79
+ }
32
80
  var OpenLattice = class {
33
81
  constructor() {
34
82
  this.providers = /* @__PURE__ */ new Map();
@@ -106,6 +154,7 @@ var OpenLattice = class {
106
154
  label: declaration.label ?? domain,
107
155
  source: declaration.source ?? "builtin",
108
156
  scopeKind: declaration.scopeKind ?? "tenant",
157
+ connectionBacked: declaration.connectionBacked === true,
109
158
  itemKind: declaration.itemKind ?? "action",
110
159
  items
111
160
  });
@@ -143,25 +192,45 @@ var OpenLattice = class {
143
192
  if (!resolved) {
144
193
  throw new ActionNotFoundError(toolName);
145
194
  }
146
- if (!grantCovers(grants, resolved.domain, resolved.actionName)) {
195
+ const grant = findCoveringGrant(grants, resolved.domain, resolved.actionName);
196
+ if (!grant) {
147
197
  throw new GrantDeniedError(toolName);
148
198
  }
199
+ const connectionKeys = resolveCoveringConnectionKeys(
200
+ grants,
201
+ resolved.domain,
202
+ resolved.actionName
203
+ );
204
+ if (connectionKeys && resolved.entry.declaration.connectionBacked !== true) {
205
+ throw new ValidationError(
206
+ `selector.connections is not supported for domain "${resolved.domain}"`
207
+ );
208
+ }
149
209
  const resolvedSchema = resolved.action.schema;
150
210
  const parsed = "safeParse" in resolvedSchema ? resolvedSchema.safeParse(args) : z.object(resolvedSchema).safeParse(args);
151
211
  if (!parsed.success) {
152
212
  throw new ValidationError(parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; "));
153
213
  }
154
- return resolved.entry.declaration.execute(resolved.actionName, parsed.data, ctx);
214
+ return resolved.entry.declaration.execute(
215
+ resolved.actionName,
216
+ parsed.data,
217
+ connectionKeys ? { ...ctx, connectionKeys } : ctx
218
+ );
155
219
  }
156
220
  };
157
221
  var openLattice = new OpenLattice();
158
222
 
159
223
  export {
160
224
  GrantDeniedError,
225
+ ConnectionNotAllowedError,
161
226
  ValidationError,
162
227
  ActionNotFoundError,
228
+ findCoveringGrant,
229
+ normalizeConnectionKeys,
230
+ normalizeGrantConnectionKeys,
231
+ resolveCoveringConnectionKeys,
163
232
  grantCovers,
164
233
  OpenLattice,
165
234
  openLattice
166
235
  };
167
- //# sourceMappingURL=chunk-HVIKCUOF.mjs.map
236
+ //# sourceMappingURL=chunk-MFWTGNQR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/open/openLattice.ts"],"sourcesContent":["/**\n * OpenLattice — the registry + invocation kernel for the Open API surface.\n *\n * Every credential-driven execution flows through exactly one path:\n * catalogFor(grants) → what the credential may see\n * invoke(grants, ...) → authorize → validate → execute → result\n *\n * Built-in providers (kb, sandbox, agent) and plugin-exposed tools all\n * register the same OpenProviderDeclaration contract.\n */\n\nimport { z } from \"zod\";\nimport type {\n McpUiCsp,\n McpUiDisplayMode,\n OpenCapabilitySource,\n OpenCatalogDomain,\n OpenCatalogItem,\n OpenExecutionContext,\n OpenExecutionResult,\n OpenGrant,\n} from \"@axiom-lattice/protocols\";\n\n/**\n * UI metadata exposed to MCP App hosts: `resourceUri` is advertised in a\n * tool's `_meta.ui`, while `csp`/`displayMode` are consumer hints.\n */\nexport type OpenUiMeta = {\n resourceUri: string;\n csp?: McpUiCsp;\n displayMode?: McpUiDisplayMode;\n};\n\nexport interface OpenActionDeclaration {\n name: string;\n /** Explicit MCP tool name override (defaults to `{domain}_{name}`). */\n toolName?: string;\n /** Friendly display label (falls back to the action name). */\n label?: string;\n description: string;\n schema: z.ZodRawShape | z.ZodTypeAny;\n annotations: { readOnlyHint: boolean; destructiveHint: boolean };\n /** MCP App UI metadata; advertised in the tool's `_meta.ui`. */\n ui?: OpenUiMeta;\n}\n\n/** Request-scoped context available while deriving the catalog. */\nexport interface OpenCatalogContext {\n tenantId?: string;\n}\n\nexport interface OpenProviderDeclaration {\n domain: string;\n /** Human label for the management catalog (defaults to domain). */\n label?: string;\n /** Catalog provenance (defaults to \"builtin\"). */\n source?: OpenCapabilitySource;\n /** \"action\" (kb/plugins) or \"instance\" (agent); defaults to \"action\". */\n itemKind?: \"action\" | \"instance\";\n /** Display-only scope hint (defaults to \"tenant\"). */\n scopeKind?: \"tenant\" | \"workspace\" | \"project\";\n /** Whether this domain's tools resolve tenant connections. */\n connectionBacked?: boolean;\n actions: OpenActionDeclaration[];\n /**\n * Dynamic actions derived from the credential's grants — used by instance\n * domains (e.g. agent: one tool per granted assistant).\n */\n instanceActions?: (grants: OpenGrant[], ctx?: OpenCatalogContext) => OpenActionDeclaration[] | Promise<OpenActionDeclaration[]>;\n execute: (\n action: string,\n args: unknown,\n ctx: OpenExecutionContext,\n ) => Promise<OpenExecutionResult>;\n}\n\nexport interface OpenToolDescriptor {\n /** MCP tool name, \"{domain}_{action}\". */\n toolName: string;\n domain: string;\n action: string;\n description: string;\n schema: z.ZodRawShape | z.ZodTypeAny;\n annotations: { readOnlyHint: boolean; destructiveHint: boolean };\n /** MCP App UI metadata; advertised in the tool's `_meta.ui`. */\n ui?: OpenUiMeta;\n}\n\n/** Thrown when a credential has no grant covering the requested action. */\nexport class GrantDeniedError extends Error {\n readonly code = \"GRANT_DENIED\";\n constructor(toolName: string) {\n super(`GRANT_DENIED: no grant covers ${toolName}`);\n this.name = \"GrantDeniedError\";\n }\n}\n\n/** Thrown when a grant pins a connection that is missing or foreign. */\nexport class ConnectionNotAllowedError extends Error {\n readonly code = \"CONNECTION_NOT_ALLOWED\";\n constructor(detail: string) {\n super(`CONNECTION_NOT_ALLOWED: ${detail}`);\n this.name = \"ConnectionNotAllowedError\";\n }\n}\n\n/** Thrown when args fail the action's zod schema. */\nexport class ValidationError extends Error {\n readonly code = \"VALIDATION_FAILED\";\n constructor(detail: string) {\n super(`VALIDATION_FAILED: ${detail}`);\n this.name = \"ValidationError\";\n }\n}\n\n/** Thrown for a toolName with no registered provider/action. */\nexport class ActionNotFoundError extends Error {\n readonly code = \"ACTION_NOT_FOUND\";\n constructor(toolName: string) {\n super(`ACTION_NOT_FOUND: ${toolName}`);\n this.name = \"ActionNotFoundError\";\n }\n}\n\ninterface RegisteredProvider {\n declaration: OpenProviderDeclaration;\n actions: Map<string, OpenActionDeclaration>;\n}\n\n/** Effective MCP tool name: explicit override, else `{domain}_{name}`. */\nfunction effectiveToolName(domain: string, action: OpenActionDeclaration): string {\n return action.toolName ?? `${domain}_${action.name}`;\n}\n\n/** The first grant covering one action of a domain (authorization predicate). */\nexport function findCoveringGrant(\n grants: OpenGrant[],\n domain: string,\n action: string,\n): OpenGrant | undefined {\n return grants.find(\n (g) =>\n g.domain === domain\n && (!g.items || g.items.length === 0 || g.items.includes(action)),\n );\n}\n\n/**\n * Normalize an untrusted selector connection list; throws on invalid\n * containers/entries.\n */\nexport function normalizeConnectionKeys(value: unknown): string[] | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value)) {\n throw new ValidationError(\"selector.connections must be an array\");\n }\n if (value.length === 0) return undefined;\n const keys = new Set<string>();\n for (const item of value) {\n if (typeof item !== \"string\" || item.trim().length === 0) {\n throw new ValidationError(\n \"selector.connections must contain non-empty strings\",\n );\n }\n keys.add(item);\n }\n return [...keys];\n}\n\n/**\n * Normalize the selector of one grant, validating its container shape.\n * A malformed selector fails closed instead of becoming connect-all.\n */\nexport function normalizeGrantConnectionKeys(grant: OpenGrant): string[] | undefined {\n const selector: unknown = grant.selector;\n if (selector === undefined) return undefined;\n if (typeof selector !== \"object\" || selector === null || Array.isArray(selector)) {\n throw new ValidationError(\"grant.selector must be an object\");\n }\n return normalizeConnectionKeys((selector as { connections?: unknown }).connections);\n}\n\n/**\n * Resolve the effective connection selector across every covering grant.\n *\n * Authorization is the union of grants, so the selector must not depend on\n * grant order. A covering grant without `selector.connections` means\n * unrestricted (legacy connect-all); otherwise the pinned keys are unioned.\n */\nexport function resolveCoveringConnectionKeys(\n grants: OpenGrant[],\n domain: string,\n action: string,\n): string[] | undefined {\n const covering = grants.filter(\n (g) =>\n g.domain === domain\n && (!g.items || g.items.length === 0 || g.items.includes(action)),\n );\n if (covering.length === 0) return undefined;\n const normalized = covering.map((grant) => normalizeGrantConnectionKeys(grant));\n if (normalized.some((keys) => !keys)) return undefined;\n const keys = new Set<string>();\n for (const selected of normalized) {\n for (const key of selected ?? []) keys.add(key);\n }\n return keys.size > 0 ? [...keys] : undefined;\n}\n\n/**\n * Whether the given grants cover one action of a domain (default-deny;\n * empty/absent `items` covers every action). Shared by tool and UI-resource\n * filtering so item-level grants apply identically to both.\n */\nexport function grantCovers(\n grants: OpenGrant[],\n domain: string,\n action: string,\n): boolean {\n return findCoveringGrant(grants, domain, action) !== undefined;\n}\n\nexport class OpenLattice {\n private providers = new Map<string, RegisteredProvider>();\n\n /** Register a provider; domain+action collisions are rejected. */\n register(declaration: OpenProviderDeclaration): void {\n const existing = this.providers.get(declaration.domain);\n if (existing) {\n throw new Error(\n `Open provider domain already registered: ${declaration.domain}`,\n );\n }\n const actions = new Map<string, OpenActionDeclaration>();\n for (const action of declaration.actions) {\n actions.set(action.name, action);\n }\n this.providers.set(declaration.domain, { declaration, actions });\n }\n\n /** Tool descriptors visible under the given grants (default-deny). */\n async catalogFor(grants: OpenGrant[], ctx?: OpenCatalogContext): Promise<OpenToolDescriptor[]> {\n const catalog: OpenToolDescriptor[] = [];\n for (const [domain, entry] of this.providers) {\n // Static actions.\n for (const [actionName, action] of entry.actions) {\n if (!grantCovers(grants, domain, actionName)) continue;\n catalog.push({\n toolName: effectiveToolName(domain, action),\n domain,\n action: actionName,\n description: action.description,\n schema: action.schema,\n annotations: action.annotations,\n ui: action.ui,\n });\n }\n // Instance-derived actions (e.g. agent: one tool per granted assistant).\n if (entry.declaration.instanceActions) {\n const instances = await entry.declaration.instanceActions(grants, ctx);\n for (const action of instances) {\n catalog.push({\n toolName: effectiveToolName(domain, action),\n domain,\n action: action.name,\n description: action.description,\n schema: action.schema,\n annotations: action.annotations,\n ui: action.ui,\n });\n }\n }\n }\n return catalog;\n }\n\n /**\n * Full grantable catalog (unfiltered by grants) for the management picker.\n * Instance domains (agent) return an empty item list — the catalog service\n * fills their per-tenant instances.\n */\n describeCatalog(): OpenCatalogDomain[] {\n const domains: OpenCatalogDomain[] = [];\n for (const [domain, entry] of this.providers) {\n const declaration = entry.declaration;\n const items: OpenCatalogItem[] = [];\n if ((declaration.itemKind ?? \"action\") === \"action\") {\n for (const [actionName, action] of entry.actions) {\n items.push({\n id: actionName,\n toolName: `${domain}_${actionName}`,\n label: action.label ?? actionName,\n description: action.description,\n annotations: action.annotations,\n });\n }\n }\n domains.push({\n domain,\n label: declaration.label ?? domain,\n source: declaration.source ?? \"builtin\",\n scopeKind: declaration.scopeKind ?? \"tenant\",\n connectionBacked: declaration.connectionBacked === true,\n itemKind: declaration.itemKind ?? \"action\",\n items,\n });\n }\n return domains;\n }\n\n private async resolveAction(\n grants: OpenGrant[],\n toolName: string,\n ctx?: OpenCatalogContext,\n ): Promise<{\n domain: string;\n entry: RegisteredProvider;\n action: OpenActionDeclaration;\n /** Grant-matching id: the action name (assistant id for agent instances). */\n actionName: string;\n } | null> {\n for (const [domain, entry] of this.providers) {\n for (const [actionName, action] of entry.actions) {\n if (effectiveToolName(domain, action) === toolName) {\n return { domain, entry, action, actionName };\n }\n }\n if (entry.declaration.instanceActions) {\n const instances = await entry.declaration.instanceActions(grants, ctx);\n for (const action of instances) {\n if (effectiveToolName(domain, action) === toolName) {\n return { domain, entry, action, actionName: action.name };\n }\n }\n }\n }\n return null;\n }\n\n /** Whether the given grants cover a tool (`{domain}_{action}`). */\n isAllowed(grants: OpenGrant[], toolName: string): boolean {\n const sep = toolName.indexOf(\"_\");\n const domain = sep > 0 ? toolName.slice(0, sep) : toolName;\n const action = sep > 0 ? toolName.slice(sep + 1) : \"\";\n return grantCovers(grants, domain, action);\n }\n\n /** The single credential-driven execution path. */\n async invoke(\n grants: OpenGrant[],\n ctx: OpenExecutionContext,\n toolName: string,\n args: unknown,\n ): Promise<OpenExecutionResult> {\n const resolved = await this.resolveAction(grants, toolName, { tenantId: ctx.tenantId });\n if (!resolved) {\n throw new ActionNotFoundError(toolName);\n }\n const grant = findCoveringGrant(grants, resolved.domain, resolved.actionName);\n if (!grant) {\n throw new GrantDeniedError(toolName);\n }\n const connectionKeys = resolveCoveringConnectionKeys(\n grants,\n resolved.domain,\n resolved.actionName,\n );\n if (connectionKeys && resolved.entry.declaration.connectionBacked !== true) {\n throw new ValidationError(\n `selector.connections is not supported for domain \"${resolved.domain}\"`,\n );\n }\n const resolvedSchema = resolved.action.schema;\n const parsed = (\n \"safeParse\" in resolvedSchema\n ? (resolvedSchema as z.ZodTypeAny).safeParse(args)\n : z.object(resolvedSchema as z.ZodRawShape).safeParse(args)\n );\n if (!parsed.success) {\n throw new ValidationError(parsed.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \"));\n }\n return resolved.entry.declaration.execute(\n resolved.actionName,\n parsed.data,\n connectionKeys ? { ...ctx, connectionKeys } : ctx,\n );\n }\n}\n\n/** Process-wide singleton for the gateway's Open surface. */\nexport const openLattice = new OpenLattice();\n"],"mappings":"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","names":["keys"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  A2AAuthError
3
- } from "./chunk-GPHOYR3X.mjs";
3
+ } from "./chunk-LCM4OQTI.mjs";
4
4
 
5
5
  // src/routes/a2a.ts
6
6
  import { getStoreLattice, getReplicaId } from "@axiom-lattice/core";
@@ -227,4 +227,4 @@ export {
227
227
  broadcastA2AKeyChanged,
228
228
  registerA2ARoutes
229
229
  };
230
- //# sourceMappingURL=chunk-HYYLYIMH.mjs.map
230
+ //# sourceMappingURL=chunk-OBODBHIS.mjs.map