@axiom-lattice/gateway 8.3.0 → 9.0.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/{A2AAuthService-7UGQ7GMW.mjs → A2AAuthService-UKN53ZGP.mjs} +2 -2
- package/dist/{a2a-BHBLACJV.mjs → a2a-YLKZE5JG.mjs} +7 -5
- package/dist/{a2a-standard-VL5I7B4W.mjs → a2a-standard-MD7WNDTP.mjs} +79 -127
- package/dist/a2a-standard-MD7WNDTP.mjs.map +1 -0
- package/dist/chunk-A7YNAU5L.mjs +377 -0
- package/dist/chunk-A7YNAU5L.mjs.map +1 -0
- package/dist/{chunk-3XIIQ6EK.mjs → chunk-BOPNXTMO.mjs} +66 -5
- package/dist/chunk-BOPNXTMO.mjs.map +1 -0
- package/dist/{chunk-L6MBEEII.mjs → chunk-JGQURU44.mjs} +24 -6
- package/dist/chunk-JGQURU44.mjs.map +1 -0
- package/dist/{chunk-GPHOYR3X.mjs → chunk-LCM4OQTI.mjs} +30 -1
- package/dist/chunk-LCM4OQTI.mjs.map +1 -0
- package/dist/{chunk-HVIKCUOF.mjs → chunk-MFWTGNQR.mjs} +74 -5
- package/dist/chunk-MFWTGNQR.mjs.map +1 -0
- package/dist/chunk-NYYSBNDG.mjs +575 -0
- package/dist/chunk-NYYSBNDG.mjs.map +1 -0
- package/dist/{chunk-W3YH4S6G.mjs → chunk-SIMEG2JT.mjs} +19 -2
- package/dist/chunk-SIMEG2JT.mjs.map +1 -0
- package/dist/chunk-T2HIWZGP.mjs +580 -0
- package/dist/chunk-T2HIWZGP.mjs.map +1 -0
- package/dist/chunk-Y7OVVPVU.mjs +13 -0
- package/dist/chunk-Y7OVVPVU.mjs.map +1 -0
- package/dist/{chunk-GL7RV5L3.mjs → chunk-YR32MWEJ.mjs} +36 -73
- package/dist/chunk-YR32MWEJ.mjs.map +1 -0
- package/dist/embed/{embed-page-AgentDetailSideApp-7YTMN643.js → embed-page-AgentDetailSideApp-6RYNHS5F.js} +1 -1
- package/dist/embed/{embed-page-AgentDetailSideApp-O6G4NJ7I.css → embed-page-AgentDetailSideApp-JMJOOC4V.css} +1 -1
- package/dist/embed/{embed-page-AgentGovernanceWorkbench-SFZNVMUH.css → embed-page-AgentGovernanceWorkbench-3T3ZFJI5.css} +1 -1
- package/dist/embed/embed-page-AgentGovernanceWorkbench-UAPXF3SZ.js +1 -0
- package/dist/embed/{embed-page-LangfuseDashboard-BEOJDMWF.js → embed-page-LangfuseDashboard-ZYGZNW7P.js} +1 -1
- package/dist/embed/{embed-page-MessageGraph-N2DXVMEK.js → embed-page-MessageGraph-S2P3WACC.js} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-CWOXLVFW.css → embed-page-MessageGraph-UDC6GQA5.css} +1 -1
- package/dist/embed/{embed-page-ReactChart-3QDZX2WG.js → embed-page-ReactChart-I3UZVE4Z.js} +1 -1
- package/dist/embed/{embed-page-ReactChart-MXGF35OQ.css → embed-page-ReactChart-URBKYQYE.css} +1 -1
- package/dist/embed/{embed-page-SkillFlow-3X24ILKU.js → embed-page-SkillFlow-JMIJ46FX.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-MYIRJWFM.js → embed-page-TasksSideApp-IVFEFLSH.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-HV6DKQ26.css → embed-page-TasksSideApp-TTCP4ENP.css} +1 -1
- package/dist/embed/{embed-page-ThreadMaintenancePanel-V327PV5R.js → embed-page-ThreadMaintenancePanel-KQPPAEWJ.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-YQFB3CTV.css → embed-page-TopologyRuntimeView-S4AMB4RI.css} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-3O6VJUZR.js → embed-page-TopologyRuntimeView-XCFQ23HF.js} +1 -1
- package/dist/embed/{embed-page-MessageGraph-UT2TCPSO.css → embed-page-WorkflowAutomationView-FWVCQ3YM.css} +1 -1
- package/dist/embed/embed-page-WorkflowAutomationView-UTKCKP75.js +3 -0
- package/dist/embed/embed-page-chunk-5X4MNQAW.js +242 -0
- package/dist/embed/{embed-page-chunk-PMOGSY6W.js → embed-page-chunk-77CQOFKU.js} +1 -1
- package/dist/embed/{embed-page-chunk-PROZXFWS.js → embed-page-chunk-7VDAOX3D.js} +1 -1
- package/dist/embed/{embed-page-chunk-BSRASK7V.js → embed-page-chunk-MD4U4DHC.js} +5 -5
- package/dist/embed/embed-page-chunk-RANB2CUO.js +3 -0
- package/dist/embed/{embed-page-chunk-IELKCYQX.js → embed-page-chunk-RVIS6CKE.js} +1 -1
- package/dist/embed/embed-page-chunk-SEOTZ4Q7.js +6829 -0
- package/dist/embed/{embed-page-chunk-ASEIOK7X.js → embed-page-chunk-TCVPYXPL.js} +2 -2
- package/dist/embed/embed-page-chunk-WHGU76CU.js +211 -0
- package/dist/embed/{embed-page-chunk-WGEXK773.js → embed-page-chunk-ZZFTCBM6.js} +1 -1
- package/dist/embed/embed-page-en-4HSTKY4Q.js +1 -0
- package/dist/embed/{embed-page-en-XJYWUJEL.js → embed-page-en-DNDGCPMA.js} +1 -1
- package/dist/embed/embed-page-en-H5NOZEKE.js +1 -0
- package/dist/embed/embed-page-en-MIVBTEEL.js +1 -0
- package/dist/embed/{embed-page-en-HDSVQ4O6.js → embed-page-en-QJRX2NQE.js} +1 -1
- package/dist/embed/{embed-page-zh-6EBUEFKP.js → embed-page-zh-2H4EB3F3.js} +1 -1
- package/dist/embed/embed-page-zh-4C2K3RH2.js +1 -0
- package/dist/embed/embed-page-zh-7PJNJJZY.js +1 -0
- package/dist/embed/{embed-page-zh-IR652A6J.js → embed-page-zh-HTEHFCSL.js} +1 -1
- package/dist/embed/embed-page-zh-UZ4GPIU7.js +1 -0
- package/dist/embed/embed-page.css +1 -1
- package/dist/embed/embed-page.js +1 -1
- package/dist/embed/mcp-app-page.js +61 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +3574 -2349
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +327 -1049
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-app-shares-SHPBJQXS.mjs +300 -0
- package/dist/mcp-app-shares-SHPBJQXS.mjs.map +1 -0
- package/dist/mcp-apps-QVUK6UBA.mjs +26 -0
- package/dist/{open-7ED27I5B.mjs → open-27ZPLBIW.mjs} +101 -18
- package/dist/open-27ZPLBIW.mjs.map +1 -0
- package/dist/{open-catalog-SZQ435AY.mjs → open-catalog-NVGP6SQP.mjs} +38 -7
- package/dist/open-catalog-NVGP6SQP.mjs.map +1 -0
- package/dist/{open-exposure-I3DN2X4E.mjs → open-exposure-HGYT5VFC.mjs} +8 -9
- package/dist/open-exposure-HGYT5VFC.mjs.map +1 -0
- package/dist/{open-files-V3R2BFTV.mjs → open-files-JPB2NBL6.mjs} +4 -4
- package/dist/{open-session-2Y7XJJW2.mjs → open-session-CLZT3HVG.mjs} +5 -5
- package/dist/open-session-CLZT3HVG.mjs.map +1 -0
- package/dist/openLattice-D5YTSZBY.mjs +28 -0
- package/dist/{registerExposedPlugins-6GCOE64C.mjs → registerExposedPlugins-2JGXSMAL.mjs} +4 -3
- package/dist/{resources-DAU5ZIPB.mjs → resources-YD5DOPGD.mjs} +83 -3
- package/dist/resources-YD5DOPGD.mjs.map +1 -0
- package/dist/{sandboxOpenCredentialProvider-AT227D4N.mjs → sandboxOpenCredentialProvider-RTYOYRCT.mjs} +19 -14
- package/dist/sandboxOpenCredentialProvider-RTYOYRCT.mjs.map +1 -0
- package/package.json +10 -8
- package/dist/a2a-standard-VL5I7B4W.mjs.map +0 -1
- package/dist/chunk-3XIIQ6EK.mjs.map +0 -1
- package/dist/chunk-GL7RV5L3.mjs.map +0 -1
- package/dist/chunk-GPHOYR3X.mjs.map +0 -1
- package/dist/chunk-GXZFO3VU.mjs +0 -42
- package/dist/chunk-GXZFO3VU.mjs.map +0 -1
- package/dist/chunk-HVIKCUOF.mjs.map +0 -1
- package/dist/chunk-HYYLYIMH.mjs +0 -230
- package/dist/chunk-HYYLYIMH.mjs.map +0 -1
- package/dist/chunk-L6MBEEII.mjs.map +0 -1
- package/dist/chunk-W3YH4S6G.mjs.map +0 -1
- package/dist/embed/embed-page-AgentGovernanceWorkbench-YWJ4ZLEV.js +0 -1
- package/dist/embed/embed-page-WorkflowAutomationView-YHOS4WQR.js +0 -3
- package/dist/embed/embed-page-chunk-4XKCMM5C.js +0 -211
- package/dist/embed/embed-page-chunk-IBRMWDHE.js +0 -6835
- package/dist/embed/embed-page-chunk-JTL73EBV.js +0 -3
- package/dist/embed/embed-page-chunk-YMVKQQXJ.js +0 -242
- package/dist/embed/embed-page-en-E35GE7TA.js +0 -1
- package/dist/embed/embed-page-en-GLT2HEZZ.js +0 -1
- package/dist/embed/embed-page-en-JOTELYAL.js +0 -1
- package/dist/embed/embed-page-zh-BPO6ZSJM.js +0 -1
- package/dist/embed/embed-page-zh-XMEJPDMF.js +0 -1
- package/dist/embed/embed-page-zh-ZLK3GLM3.js +0 -1
- package/dist/open-7ED27I5B.mjs.map +0 -1
- package/dist/open-catalog-SZQ435AY.mjs.map +0 -1
- package/dist/open-exposure-I3DN2X4E.mjs.map +0 -1
- package/dist/open-session-2Y7XJJW2.mjs.map +0 -1
- package/dist/openLattice-Q6M5S6CY.mjs +0 -18
- package/dist/openRateLimiter-XEOL3A5G.mjs +0 -8
- package/dist/resources-DAU5ZIPB.mjs.map +0 -1
- package/dist/sandboxOpenCredentialProvider-AT227D4N.mjs.map +0 -1
- /package/dist/{A2AAuthService-7UGQ7GMW.mjs.map → A2AAuthService-UKN53ZGP.mjs.map} +0 -0
- /package/dist/{a2a-BHBLACJV.mjs.map → a2a-YLKZE5JG.mjs.map} +0 -0
- /package/dist/{openLattice-Q6M5S6CY.mjs.map → mcp-apps-QVUK6UBA.mjs.map} +0 -0
- /package/dist/{open-files-V3R2BFTV.mjs.map → open-files-JPB2NBL6.mjs.map} +0 -0
- /package/dist/{openRateLimiter-XEOL3A5G.mjs.map → openLattice-D5YTSZBY.mjs.map} +0 -0
- /package/dist/{registerExposedPlugins-6GCOE64C.mjs.map → registerExposedPlugins-2JGXSMAL.mjs.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/open/registerExposedPlugins.ts","../src/services/open/pluginExposeAdapter.ts"],"sourcesContent":["import { PluginRegistry } from \"@axiom-lattice/core\";\nimport type { OpenGrant, Plugin, PluginToolUi } from \"@axiom-lattice/protocols\";\nimport { grantCovers, openLattice } from \"./openLattice\";\nimport { createPluginProvider } from \"./pluginExposeAdapter\";\nimport type { OpenUiResource } from \"./openMcpServer\";\n\n/** A single exposed-tool declaration (annotation hints optional). */\ninterface ExposedDeclaration {\n name: string;\n description: string;\n readOnly?: boolean;\n destructive?: boolean;\n}\n\n/**\n * Declaration source: meta.openExpose (preferred, standalone) or\n * meta.tools[].expose (per-tool flags).\n */\nfunction exposedDeclarations(plugin: Plugin): ExposedDeclaration[] {\n const openExpose = plugin.meta.openExpose;\n if (openExpose) {\n return openExpose.map((entry) =>\n typeof entry === \"string\"\n ? { name: entry, description: \"\" }\n : {\n name: entry.name,\n description: \"\",\n readOnly: entry.readOnly,\n destructive: entry.destructive,\n },\n );\n }\n return (plugin.meta.tools ?? []).filter((t) => t.expose);\n}\n\n/** Walk PluginRegistry and register every exposed tool as an Open provider. */\nexport async function registerExposedPluginTools(): Promise<void> {\n for (const key of PluginRegistry.list()) {\n const plugin = PluginRegistry.get(key);\n if (!plugin?.middleware) continue;\n const exposed = exposedDeclarations(plugin);\n if (exposed.length === 0) continue;\n // Tool-level UI declarations keyed by tool name (raw meta, unlike listMeta).\n const uiByTool = new Map<string, PluginToolUi>();\n for (const toolMeta of plugin.meta.tools ?? []) {\n if (toolMeta.ui) uiByTool.set(toolMeta.name, toolMeta.ui);\n }\n try {\n // Open-door semantics: resolve the tenant's connections at call time\n // (credential → tenant → tenant's own Connection Store entries).\n const middleware = await plugin.middleware({\n ...(plugin.meta.defaultConfig ?? {}),\n connectAll: true,\n });\n const toolObjects = (middleware as { tools?: Array<{ name: string; description: string; invoke: (input: unknown, config?: unknown) => Promise<string> }> }).tools ?? [];\n const matched = exposed\n .map((tm) => {\n const tool = toolObjects.find((t: { name: string }) => t.name === tm.name);\n if (!tool) return null;\n return {\n name: tm.name,\n description: tm.description,\n tool,\n readOnly: tm.readOnly,\n destructive: tm.destructive,\n ui: uiByTool.get(tm.name),\n };\n })\n .filter(Boolean) as Array<{\n name: string;\n description?: string;\n tool: { name: string; description: string; invoke: (input: unknown, config?: unknown) => Promise<string> };\n readOnly?: boolean;\n destructive?: boolean;\n ui?: PluginToolUi;\n }>;\n if (matched.length === 0) continue;\n const provider = createPluginProvider(key, matched, { label: plugin.meta.name });\n openLattice.register(provider);\n } catch {\n // skip plugins whose middleware fails to instantiate\n }\n }\n}\n\n/**\n * Inline `ui://` resources declared by GLOBAL plugins (raw meta, not listMeta).\n *\n * `serializePluginMeta` is a whitelist and drops `meta.uiResources`, so the raw\n * plugin is read back from `PluginRegistry.get(type)`. A resource is included\n * only when an **exposed** tool of that domain declares it (`meta.tools[].ui`)\n * **and** the credential's grant covers that exact tool (item-level) — a domain\n * grant alone must not leak another tool's UI document, and an unexposed or\n * unauthorized plugin must not leak any. `mimeType` defaults to\n * `text/html;profile=mcp-app` (the `PluginUiResource` contract).\n *\n * Synchronous: `PluginRegistry` is synchronous. The returned `read` callback\n * serves the plugin's inline HTML lazily per `resources/read`.\n *\n * @param grants - Credential grants; an item not granted (or unknown resource)\n * is skipped.\n * @returns Map of `ui://` URI → Open session UI resource.\n */\nexport function collectExposedPluginUiResources(grants: OpenGrant[]): Map<string, OpenUiResource> {\n const resources = new Map<string, OpenUiResource>();\n for (const type of PluginRegistry.list()) {\n const plugin = PluginRegistry.get(type);\n if (!plugin?.middleware) continue;\n const exposedNames = new Set(exposedDeclarations(plugin).map((d) => d.name));\n if (exposedNames.size === 0) continue;\n // Only resources owned by an exposed, item-level-granted tool are reachable.\n const allowedUris = new Set<string>();\n for (const toolMeta of plugin.meta.tools ?? []) {\n const uri = toolMeta.ui?.resource;\n if (!uri) continue;\n if (!exposedNames.has(toolMeta.name)) continue;\n if (!grantCovers(grants, type, toolMeta.name)) continue;\n allowedUris.add(uri);\n }\n if (allowedUris.size === 0) continue;\n for (const [uri, res] of Object.entries(plugin.meta.uiResources ?? {})) {\n if (!allowedUris.has(uri)) continue;\n resources.set(uri, {\n mimeType: res.mimeType ?? \"text/html;profile=mcp-app\",\n csp: res.csp,\n read: async () => ({ html: res.html, csp: res.csp }),\n });\n }\n }\n return resources;\n}\n","/**\n * Plugin expose adapter: converts a plugin's middleware-emitted tools into an\n * OpenProvider, so `expose: true` tools surface on the Open API catalog.\n *\n * The LangChain tool emits schema via .lc_kwargs.schema (ZodType) and runs via\n * .invoke(input, exeConfig). We synthesize the exeConfig the way the Agent\n * path would — injecting run identity (tenantId/workspaceId/projectId) into\n * runConfig. Plugin tools resolve their own connections from the\n * tenant-scoped Connection Store at invocation time.\n */\n\nimport { z } from \"zod\";\nimport type { OpenExecutionContext, PluginToolUi } from \"@axiom-lattice/protocols\";\nimport type { OpenProviderDeclaration, OpenActionDeclaration } from \"./openLattice\";\n\nexport interface PluginToolLike {\n name: string;\n description: string;\n invoke: (input: unknown, exeConfig?: unknown) => Promise<string>;\n lc_kwargs?: { schema?: z.ZodTypeAny };\n schema?: z.ZodTypeAny;\n}\n\nfunction schemaOf(tool: PluginToolLike): z.ZodTypeAny {\n return tool.lc_kwargs?.schema ?? tool.schema ?? z.object({}).passthrough();\n}\n\n/** Build an Open provider exposing the given plugin tools under a domain. */\nexport interface PluginExposedTool {\n name: string;\n description?: string;\n tool: PluginToolLike;\n /** MCP readOnlyHint (default false — conservative). */\n readOnly?: boolean;\n /** MCP destructiveHint (default false). */\n destructive?: boolean;\n /**\n * MCP App UI declaration from the plugin's tool meta. For global plugins\n * `resource` is already a `ui://` URI (unlike sandbox plugins, where it is a\n * file path relative to the plugin directory).\n */\n ui?: PluginToolUi;\n}\n\nexport function createPluginProvider(\n domain: string,\n tools: PluginExposedTool[],\n options: { label?: string } = {},\n): OpenProviderDeclaration {\n function toAction(info: PluginExposedTool): OpenActionDeclaration {\n return {\n name: info.name,\n description:\n info.description || info.tool.description || `Plugin tool ${info.name}`,\n schema: schemaOf(info.tool),\n annotations: {\n readOnlyHint: info.readOnly ?? false,\n destructiveHint: info.destructive ?? false,\n },\n ui: info.ui\n ? {\n resourceUri: info.ui.resource,\n csp: info.ui.csp,\n displayMode: info.ui.displayMode,\n }\n : undefined,\n };\n }\n\n const actions = tools.map(toAction);\n return {\n domain,\n label: options.label ?? domain,\n source: \"plugin\",\n scopeKind: \"tenant\",\n actions,\n execute: async (action: string, args: unknown, ctx: OpenExecutionContext) => {\n const info = tools.find((t) => t.name === action);\n if (!info) {\n return {\n content: [{ type: \"text\", text: `Unknown action: ${action}` }],\n isError: true,\n };\n }\n const exeConfig = {\n configurable: {\n runConfig: {\n tenantId: ctx.tenantId,\n workspaceId: ctx.workspaceId,\n projectId: ctx.projectId,\n },\n },\n };\n const output = await info.tool.invoke(args, exeConfig);\n return { content: [{ type: \"text\", text: output }] };\n },\n };\n}"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/open/sandboxOpenReflection.ts"],"sourcesContent":["/**\n * Agent Open-grant reflection (B1).\n *\n * Computes the extra Open grants a sandbox caller may use, derived from the\n * caller agent's own configuration (never from author declarations). The\n * invariant is: *what sandbox code can call ⊆ what the caller could already\n * call*. Grants are additive to the conservative B0 defaults and are bounded by\n * tenant-scoped resolution plus depth/rate/audit on the Open door.\n *\n * Mapping (`middleware[].enabled !== false`):\n * - middleware `type` whose plugin declares `meta.openExpose` → same-name domain\n * (identity; covers `task`, `document-parser`, and plugin types);\n * - `subAgents` (DeepAgentConfig) intersected with tenant-resolvable assistants\n * → the `agent` domain;\n * - anything else → ignored.\n *\n * Results are cached per `(tenantId, assistantId)` for\n * {@link AGENT_REFLECTION_CACHE_TTL_MS}. Any resolution error degrades to `[]`\n * so the caller falls back to the default grants rather than failing.\n */\n\nimport type { AssistantStore, OpenGrant } from \"@axiom-lattice/protocols\";\nimport {\n PluginRegistry,\n agentLatticeManager,\n ensureBuiltinAgentsForTenant,\n getSandboxPluginCatalog,\n getStoreLattice,\n} from \"@axiom-lattice/core\";\n\n/** The subset of an agent config relevant to Open-grant reflection. */\nexport interface SandboxOpenReflectionContext {\n middleware?: Array<{ type: string; enabled?: boolean; allowedTools?: string[] }>;\n subAgents?: string[];\n}\n\n/** Injectable sources for {@link resolveAgentOpenGrants}. */\nexport interface AgentReflectionDeps {\n /** Optional: hydrate stored/builtin/plugin agents for the tenant before resolution. */\n ensure?: (tenantId: string) => Promise<void>;\n /** Resolve an agent's config (middleware + subAgents) or undefined. */\n getAgentConfig?: (tenantId: string, assistantId: string) => SandboxOpenReflectionContext | undefined;\n /** Fallback: the assistant's persisted middleware. */\n getAssistantMiddleware?: (tenantId: string, assistantId: string) => Promise<Array<{ type: string; enabled?: boolean; allowedTools?: string[] }> | undefined>;\n /** Normalized exposed action names for a middleware/plugin type, or [] if none. */\n exposedActionsByType: (type: string) => string[];\n /** Assistant ids resolvable in the tenant (for subAgents filtering). */\n resolvableAssistants?: (tenantId: string) => Promise<Set<string>>;\n /** Clock seam for cache TTL tests. */\n now?: () => number;\n}\n\n/** How long a per-agent reflection result is reused. */\nexport const AGENT_REFLECTION_CACHE_TTL_MS = 60_000;\n\ninterface CacheEntry {\n grants: OpenGrant[];\n expiresAt: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\n/** Clear the reflection cache. Test-only seam. */\nexport function clearAgentReflectionCacheForTests(): void {\n cache.clear();\n}\n\n/** Drop cached agent grants for a tenant (optionally a single assistant). */\nexport function invalidateAgentReflectionCache(tenantId?: string, assistantId?: string): void {\n if (!tenantId) {\n cache.clear();\n return;\n }\n if (assistantId) {\n cache.delete(`${tenantId}:${assistantId}`);\n return;\n }\n const prefix = `${tenantId}:`;\n for (const key of cache.keys()) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n}\n\nfunction cloneGrants(grants: OpenGrant[]): OpenGrant[] {\n return grants.map((grant) => ({\n ...grant,\n items: grant.items ? [...grant.items] : undefined,\n }));\n}\n\n/** Normalize `meta.openExpose` (`string[] | {name}[]`) to action names. */\nexport function normalizeOpenExpose(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n const out: string[] = [];\n for (const entry of value) {\n if (typeof entry === \"string\") {\n if (entry) out.push(entry);\n } else if (entry && typeof entry === \"object\") {\n const name = (entry as { name?: unknown }).name;\n if (typeof name === \"string\" && name) out.push(name);\n }\n }\n return out;\n}\n\n/** Extract middleware type list from an agent config / graphDefinition. */\nfunction extractMiddleware(\n container: unknown,\n): Array<{ type: string; enabled?: boolean; allowedTools?: string[] }> | undefined {\n if (!container || typeof container !== \"object\") return undefined;\n const raw = (container as { middleware?: unknown }).middleware;\n if (!Array.isArray(raw)) return undefined;\n const out: Array<{ type: string; enabled?: boolean; allowedTools?: string[] }> = [];\n for (const entry of raw) {\n if (!entry || typeof entry !== \"object\") continue;\n const type = (entry as { type?: unknown }).type;\n if (typeof type !== \"string\" || type.length === 0) continue;\n const enabled = (entry as { enabled?: unknown }).enabled;\n const rawAllowed = (entry as { allowedTools?: unknown }).allowedTools;\n const allowedTools = Array.isArray(rawAllowed)\n ? rawAllowed.filter((t): t is string => typeof t === \"string\" && t.length > 0)\n : undefined;\n out.push({ type, enabled: typeof enabled === \"boolean\" ? enabled : undefined, allowedTools });\n }\n return out;\n}\n\n/** Extract subAgent ids from an agent config. */\nfunction extractSubAgents(config: unknown): string[] | undefined {\n if (!config || typeof config !== \"object\") return undefined;\n const raw = (config as { subAgents?: unknown }).subAgents;\n if (!Array.isArray(raw)) return undefined;\n const out = raw.filter((id): id is string => typeof id === \"string\" && id.length > 0);\n return out.length > 0 ? out : undefined;\n}\n\nasync function computeGrants(\n tenantId: string,\n assistantId: string,\n deps: AgentReflectionDeps,\n): Promise<OpenGrant[]> {\n await deps.ensure?.(tenantId);\n\n let context = await deps.getAgentConfig?.(tenantId, assistantId);\n if (context === undefined) {\n const middleware = await deps.getAssistantMiddleware?.(tenantId, assistantId);\n if (middleware === undefined) return [];\n context = { middleware };\n }\n\n const byDomain = new Map<string, Set<string>>();\n const add = (domain: string, items: Iterable<string>): void => {\n let bucket = byDomain.get(domain);\n if (!bucket) {\n bucket = new Set<string>();\n byDomain.set(domain, bucket);\n }\n for (const item of items) bucket.add(item);\n };\n\n for (const entry of context.middleware ?? []) {\n if (!entry || entry.enabled === false) continue;\n if (typeof entry.type !== \"string\" || entry.type.length === 0) continue;\n let actions = deps.exposedActionsByType(entry.type);\n if (actions.length === 0) continue;\n const allowedTools = entry.allowedTools;\n if (Array.isArray(allowedTools) && allowedTools.length > 0) {\n const allowed = new Set(allowedTools);\n actions = actions.filter((action) => allowed.has(action));\n if (actions.length === 0) continue;\n }\n add(entry.type, actions);\n }\n\n const subAgents = context.subAgents ?? [];\n if (subAgents.length > 0 && deps.resolvableAssistants) {\n const resolvable = await deps.resolvableAssistants(tenantId);\n const kept = subAgents.filter(\n (id) => typeof id === \"string\" && id.length > 0 && resolvable.has(id),\n );\n if (kept.length > 0) add(\"agent\", kept);\n }\n\n return [...byDomain].map(([domain, items]) => ({ domain, items: [...items] }));\n}\n\n/**\n * Resolve the reflected Open grants for an agent caller.\n *\n * @param tenantId - Tenant scope (grants never cross tenants).\n * @param assistantId - Caller agent id.\n * @param deps - Resolvers; {@link createDefaultAgentReflectionDeps} wires the\n * real registries/store.\n * @returns Reflected grants (possibly `[]`); never throws.\n */\nexport async function resolveAgentOpenGrants(\n tenantId: string,\n assistantId: string,\n deps: AgentReflectionDeps,\n): Promise<OpenGrant[]> {\n try {\n const key = `${tenantId}:${assistantId}`;\n const now = deps.now ? deps.now() : Date.now();\n const hit = cache.get(key);\n if (hit && hit.expiresAt > now) return cloneGrants(hit.grants);\n\n const grants = await computeGrants(tenantId, assistantId, deps);\n cache.set(key, { grants, expiresAt: now + AGENT_REFLECTION_CACHE_TTL_MS });\n return cloneGrants(grants);\n } catch {\n return [];\n }\n}\n\n/**\n * Default reflection deps wired to the live registries.\n *\n * Reads the agent lattice manager (seeded with built-ins for the tenant),\n * falls back to the assistant store, resolves plugin exposes from\n * {@link PluginRegistry}, and filters subAgents against tenant-resolvable\n * assistants.\n *\n * @param tenantId - Tenant whose built-in/plugin agents are ensured.\n * @returns Deps for {@link resolveAgentOpenGrants}.\n */\nexport function createDefaultAgentReflectionDeps(tenantId: string): AgentReflectionDeps {\n ensureBuiltinAgentsForTenant(tenantId);\n return {\n ensure: (t) => agentLatticeManager.initializeStoredAssistantsForTenant(t),\n getAgentConfig: (t, assistantId) => {\n const config = agentLatticeManager.getAgentConfigWithTenant(t, assistantId);\n if (!config) return undefined;\n return {\n middleware: extractMiddleware(config),\n subAgents: extractSubAgents(config),\n };\n },\n getAssistantMiddleware: async (t, assistantId) => {\n const store = getStoreLattice(\"default\", \"assistant\").store as AssistantStore;\n const assistant = await store.getAssistantById(t, assistantId);\n return extractMiddleware(assistant?.graphDefinition);\n },\n exposedActionsByType: (type) => {\n const plugin = PluginRegistry.get(type);\n return plugin ? normalizeOpenExpose(plugin.meta.openExpose) : [];\n },\n resolvableAssistants: async (t) =>\n new Set(\n agentLatticeManager.getAllAgentLatticesByTenant(t).map((lattice) => lattice.config.key),\n ),\n };\n}\n\n/** How long a per-tenant plugin reflection result is reused. */\nexport const PLUGIN_REFLECTION_CACHE_TTL_MS = 60_000;\n\n/**\n * Injectable sources for {@link resolvePluginOpenGrants}.\n *\n * Plugin handlers have no `assistantId`, so reflection targets the tenant's\n * grantable Open catalog instead of a caller's own configuration.\n *\n * @property ensure - Optional: hydrate stored/builtin/plugin agents before reflecting the agent domain.\n * @property globalExposedDomains - Global plugin `openExpose` domains (tenant-agnostic).\n * @property tenantExposedDomains - Tenant sandbox plugin exposed domains.\n * @property resolvableAssistants - Tenant-resolvable assistant ids for the `agent` domain.\n * @property now - Clock seam for cache TTL tests.\n */\nexport interface PluginReflectionDeps {\n /** Optional: hydrate stored/builtin/plugin agents before reflecting the agent domain. */\n ensure?: (tenantId: string) => Promise<void>;\n globalExposedDomains?: () => OpenGrant[];\n tenantExposedDomains?: (tenantId: string) => Promise<OpenGrant[]>;\n resolvableAssistants?: (tenantId: string) => Promise<Set<string>>;\n now?: () => number;\n}\n\ninterface PluginCacheEntry {\n grants: OpenGrant[];\n expiresAt: number;\n}\n\nconst pluginCache = new Map<string, PluginCacheEntry>();\n\n/** Clear the plugin reflection cache. Test-only seam. */\nexport function clearPluginReflectionCacheForTests(): void {\n pluginCache.clear();\n}\n\n/** Drop cached plugin grants for a tenant (or every tenant). */\nexport function invalidatePluginReflectionCache(tenantId?: string): void {\n if (!tenantId) {\n pluginCache.clear();\n return;\n }\n pluginCache.delete(tenantId);\n}\n\n/**\n * Merge grants by domain, preserving first-seen domain order and unioning items.\n *\n * An absent/empty `items` list means \"every action in the domain\" per\n * {@link OpenGrant}, so a domain carrying one collapses to `{ domain }`.\n *\n * @param grants - Raw grants from every reflection source.\n * @returns One normalized grant per domain.\n */\nexport function mergeGrantsByDomain(grants: OpenGrant[]): OpenGrant[] {\n const order: string[] = [];\n const items = new Map<string, Set<string>>();\n const unrestricted = new Set<string>();\n\n for (const grant of grants) {\n if (!grant || typeof grant.domain !== \"string\" || grant.domain.length === 0) continue;\n if (!items.has(grant.domain)) {\n items.set(grant.domain, new Set());\n order.push(grant.domain);\n }\n if (!grant.items || grant.items.length === 0) {\n unrestricted.add(grant.domain);\n continue;\n }\n const bucket = items.get(grant.domain);\n if (!bucket) continue;\n for (const item of grant.items) {\n if (typeof item === \"string\" && item.length > 0) bucket.add(item);\n }\n }\n\n return order.map((domain) => {\n if (unrestricted.has(domain)) return { domain };\n return { domain, items: [...(items.get(domain) ?? [])] };\n });\n}\n\nasync function computePluginGrants(\n tenantId: string,\n deps: PluginReflectionDeps,\n): Promise<OpenGrant[]> {\n const grants: OpenGrant[] = [...(deps.globalExposedDomains?.() ?? [])];\n if (deps.tenantExposedDomains) {\n grants.push(...(await deps.tenantExposedDomains(tenantId)));\n }\n if (deps.resolvableAssistants) {\n const resolvable = await deps.resolvableAssistants(tenantId);\n if (resolvable.size > 0) {\n grants.push({ domain: \"agent\", items: [...resolvable] });\n }\n }\n return mergeGrantsByDomain(grants);\n}\n\n/**\n * Resolve the reflected Open grants for a plugin handler caller.\n *\n * Plugin handlers carry no `assistantId`, so grants come from the tenant's\n * grantable Open catalog (global plugin exposes ∪ tenant sandbox plugin\n * exposes) plus the `agent` domain for tenant-resolvable assistants. Results\n * are cached per tenant for {@link PLUGIN_REFLECTION_CACHE_TTL_MS}; any\n * resolution error degrades to `[]` so the caller falls back to the default\n * grants rather than failing.\n *\n * @param tenantId - Tenant scope (grants never cross tenants).\n * @param deps - Resolvers; {@link createDefaultPluginReflectionDeps} wires the\n * real registries/catalog.\n * @returns Reflected grants (possibly `[]`); never throws.\n */\nexport async function resolvePluginOpenGrants(\n tenantId: string,\n deps: PluginReflectionDeps,\n): Promise<OpenGrant[]> {\n try {\n const now = deps.now ? deps.now() : Date.now();\n const hit = pluginCache.get(tenantId);\n if (hit && hit.expiresAt > now) return cloneGrants(hit.grants);\n\n await deps.ensure?.(tenantId);\n const grants = await computePluginGrants(tenantId, deps);\n pluginCache.set(tenantId, { grants, expiresAt: now + PLUGIN_REFLECTION_CACHE_TTL_MS });\n return cloneGrants(grants);\n } catch {\n return [];\n }\n}\n\n/**\n * Map validated tenant plugin manifests to exposed Open grants.\n *\n * Only tools declaring `expose === true` become grant items; plugins with no\n * exposed tools are skipped. The domain is the plugin `type`.\n *\n * @param manifests - Validated tenant plugin manifests (or any shape with a\n * `type` and `tools[].name`/`tools[].expose`).\n * @returns One grant per plugin that exposes at least one tool.\n */\nexport function buildTenantExposedGrants(\n manifests: Array<{ type: string; tools?: Array<{ name: string; expose?: boolean }> }>,\n): OpenGrant[] {\n const grants: OpenGrant[] = [];\n for (const manifest of manifests) {\n const items = (manifest.tools ?? [])\n .filter((tool) => tool.expose === true)\n .map((tool) => tool.name);\n if (items.length > 0) grants.push({ domain: manifest.type, items });\n }\n return grants;\n}\n\n/**\n * Default plugin reflection deps wired to the live registries.\n *\n * Ensures stored/builtin/plugin agents for the tenant, then reads global plugin\n * exposes from {@link PluginRegistry}, tenant sandbox plugin exposes from\n * {@link getSandboxPluginCatalog} manifests, and tenant-resolvable assistants\n * from the agent lattice manager.\n *\n * @returns Deps for {@link resolvePluginOpenGrants}.\n */\nexport function createDefaultPluginReflectionDeps(): PluginReflectionDeps {\n return {\n ensure: (tenantId) => agentLatticeManager.initializeStoredAssistantsForTenant(tenantId),\n globalExposedDomains: () => {\n const grants: OpenGrant[] = [];\n for (const type of PluginRegistry.list()) {\n const plugin = PluginRegistry.get(type);\n const items = normalizeOpenExpose(plugin?.meta.openExpose);\n if (items.length > 0) grants.push({ domain: type, items });\n }\n return grants;\n },\n tenantExposedDomains: async (tenantId) => {\n const { manifests } = await getSandboxPluginCatalog().ensureLoaded(tenantId);\n return buildTenantExposedGrants(Object.values(manifests));\n },\n resolvableAssistants: async (tenantId) =>\n new Set(\n agentLatticeManager\n .getAllAgentLatticesByTenant(tenantId)\n .map((lattice) => lattice.config.key),\n ),\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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/**\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 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 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":[]}
|
package/dist/chunk-GXZFO3VU.mjs
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// src/services/open/openRateLimiter.ts
|
|
2
|
-
var OpenRateLimiter = class {
|
|
3
|
-
constructor(options = {}) {
|
|
4
|
-
this.buckets = /* @__PURE__ */ new Map();
|
|
5
|
-
this.defaultPerMinute = options.defaultPerMinute ?? 6e4;
|
|
6
|
-
this.agentPerMinute = options.agentPerMinute ?? 2e3;
|
|
7
|
-
}
|
|
8
|
-
bucketClass(domain) {
|
|
9
|
-
return domain === "agent" ? "agent" : "default";
|
|
10
|
-
}
|
|
11
|
-
limitFor(domain) {
|
|
12
|
-
return domain === "agent" ? this.agentPerMinute : this.defaultPerMinute;
|
|
13
|
-
}
|
|
14
|
-
/** Consume one token; denied calls report how long to wait. */
|
|
15
|
-
check(credentialId, domain) {
|
|
16
|
-
const limit = this.limitFor(domain);
|
|
17
|
-
const key = `${this.bucketClass(domain)}:${credentialId}`;
|
|
18
|
-
const now = Date.now();
|
|
19
|
-
const bucket = this.buckets.get(key) ?? { tokens: limit, updatedAt: now };
|
|
20
|
-
const refillPerMs = limit / 6e4;
|
|
21
|
-
const elapsed = now - bucket.updatedAt;
|
|
22
|
-
bucket.tokens = Math.min(limit, bucket.tokens + elapsed * refillPerMs);
|
|
23
|
-
bucket.updatedAt = now;
|
|
24
|
-
if (bucket.tokens >= 1) {
|
|
25
|
-
bucket.tokens -= 1;
|
|
26
|
-
this.buckets.set(key, bucket);
|
|
27
|
-
return { allowed: true };
|
|
28
|
-
}
|
|
29
|
-
this.buckets.set(key, bucket);
|
|
30
|
-
const deficit = 1 - bucket.tokens;
|
|
31
|
-
return { allowed: false, retryAfterMs: Math.ceil(deficit / refillPerMs) };
|
|
32
|
-
}
|
|
33
|
-
/** Test helper. */
|
|
34
|
-
reset() {
|
|
35
|
-
this.buckets.clear();
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export {
|
|
40
|
-
OpenRateLimiter
|
|
41
|
-
};
|
|
42
|
-
//# sourceMappingURL=chunk-GXZFO3VU.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/open/openRateLimiter.ts"],"sourcesContent":["/**\n * In-memory per-credential rate limiter (token bucket). The agent domain gets\n * a stricter bucket because each call consumes LLM budget.\n *\n * Process-local by design (V1 single-gateway); a distributed limiter is V2.\n */\n\nexport interface RateLimitDecision {\n allowed: boolean;\n retryAfterMs?: number;\n}\n\ninterface Bucket {\n tokens: number;\n updatedAt: number;\n}\n\nexport interface OpenRateLimiterOptions {\n /** Default requests per minute per credential. */\n defaultPerMinute?: number;\n /** Stricter limit for the agent domain. */\n agentPerMinute?: number;\n}\n\nexport class OpenRateLimiter {\n private readonly buckets = new Map<string, Bucket>();\n private readonly defaultPerMinute: number;\n private readonly agentPerMinute: number;\n\n constructor(options: OpenRateLimiterOptions = {}) {\n this.defaultPerMinute = options.defaultPerMinute ?? 60000;\n this.agentPerMinute = options.agentPerMinute ?? 2000;\n }\n\n private bucketClass(domain: string): string {\n return domain === \"agent\" ? \"agent\" : \"default\";\n }\n\n private limitFor(domain: string): number {\n return domain === \"agent\" ? this.agentPerMinute : this.defaultPerMinute;\n }\n\n /** Consume one token; denied calls report how long to wait. */\n check(credentialId: string, domain: string): RateLimitDecision {\n const limit = this.limitFor(domain);\n const key = `${this.bucketClass(domain)}:${credentialId}`;\n const now = Date.now();\n const bucket = this.buckets.get(key) ?? { tokens: limit, updatedAt: now };\n\n const refillPerMs = limit / 60_000;\n const elapsed = now - bucket.updatedAt;\n bucket.tokens = Math.min(limit, bucket.tokens + elapsed * refillPerMs);\n bucket.updatedAt = now;\n\n if (bucket.tokens >= 1) {\n bucket.tokens -= 1;\n this.buckets.set(key, bucket);\n return { allowed: true };\n }\n\n this.buckets.set(key, bucket);\n const deficit = 1 - bucket.tokens;\n return { allowed: false, retryAfterMs: Math.ceil(deficit / refillPerMs) };\n }\n\n /** Test helper. */\n reset(): void {\n this.buckets.clear();\n }\n}\n"],"mappings":";AAwBO,IAAM,kBAAN,MAAsB;AAAA,EAK3B,YAAY,UAAkC,CAAC,GAAG;AAJlD,SAAiB,UAAU,oBAAI,IAAoB;AAKjD,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,iBAAiB,QAAQ,kBAAkB;AAAA,EAClD;AAAA,EAEQ,YAAY,QAAwB;AAC1C,WAAO,WAAW,UAAU,UAAU;AAAA,EACxC;AAAA,EAEQ,SAAS,QAAwB;AACvC,WAAO,WAAW,UAAU,KAAK,iBAAiB,KAAK;AAAA,EACzD;AAAA;AAAA,EAGA,MAAM,cAAsB,QAAmC;AAC7D,UAAM,QAAQ,KAAK,SAAS,MAAM;AAClC,UAAM,MAAM,GAAG,KAAK,YAAY,MAAM,CAAC,IAAI,YAAY;AACvD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,KAAK,QAAQ,IAAI,GAAG,KAAK,EAAE,QAAQ,OAAO,WAAW,IAAI;AAExE,UAAM,cAAc,QAAQ;AAC5B,UAAM,UAAU,MAAM,OAAO;AAC7B,WAAO,SAAS,KAAK,IAAI,OAAO,OAAO,SAAS,UAAU,WAAW;AACrE,WAAO,YAAY;AAEnB,QAAI,OAAO,UAAU,GAAG;AACtB,aAAO,UAAU;AACjB,WAAK,QAAQ,IAAI,KAAK,MAAM;AAC5B,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB;AAEA,SAAK,QAAQ,IAAI,KAAK,MAAM;AAC5B,UAAM,UAAU,IAAI,OAAO;AAC3B,WAAO,EAAE,SAAS,OAAO,cAAc,KAAK,KAAK,UAAU,WAAW,EAAE;AAAA,EAC1E;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,QAAQ,MAAM;AAAA,EACrB;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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 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 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/**\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 grants.some(\n (g) => g.domain === domain && (!g.items || g.items.length === 0 || g.items.includes(action)),\n );\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 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 if (!grantCovers(grants, resolved.domain, resolved.actionName)) {\n throw new GrantDeniedError(toolName);\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(resolved.actionName, parsed.data, ctx);\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":[]}
|
package/dist/chunk-HYYLYIMH.mjs
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
A2AAuthError
|
|
3
|
-
} from "./chunk-GPHOYR3X.mjs";
|
|
4
|
-
|
|
5
|
-
// src/routes/a2a.ts
|
|
6
|
-
import { getStoreLattice, getReplicaId } from "@axiom-lattice/core";
|
|
7
|
-
|
|
8
|
-
// src/services/a2aKeySync.ts
|
|
9
|
-
var A2A_KEY_CHANGED_TOPIC = "a2a-key-changed";
|
|
10
|
-
async function publishA2AKeyChanged(outbox, owner, tenantId) {
|
|
11
|
-
if (!tenantId) return;
|
|
12
|
-
try {
|
|
13
|
-
await outbox.append({
|
|
14
|
-
topic: A2A_KEY_CHANGED_TOPIC,
|
|
15
|
-
payload: { tenantId },
|
|
16
|
-
originReplicaId: owner
|
|
17
|
-
});
|
|
18
|
-
} catch {
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
async function handleA2AKeyChanged(payload, reload) {
|
|
22
|
-
const tenantId = payload?.tenantId;
|
|
23
|
-
if (typeof tenantId !== "string" || tenantId.length === 0) return;
|
|
24
|
-
await reload();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// src/routes/a2a.ts
|
|
28
|
-
var _a2aKeyStore = null;
|
|
29
|
-
var _a2aAuthService = null;
|
|
30
|
-
function setA2AKeyStore(store) {
|
|
31
|
-
_a2aKeyStore = store;
|
|
32
|
-
}
|
|
33
|
-
function setA2AAuthService(service) {
|
|
34
|
-
_a2aAuthService = service;
|
|
35
|
-
}
|
|
36
|
-
async function refreshStoreKeyMap() {
|
|
37
|
-
if (_a2aAuthService) await _a2aAuthService.refresh();
|
|
38
|
-
}
|
|
39
|
-
function broadcastA2AKeyChanged(tenantId) {
|
|
40
|
-
if (!tenantId) return;
|
|
41
|
-
try {
|
|
42
|
-
const outbox = getStoreLattice("default", "outbox").store;
|
|
43
|
-
void publishA2AKeyChanged(outbox, getReplicaId(), tenantId);
|
|
44
|
-
} catch {
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
async function requireTenantSession(request, reply) {
|
|
48
|
-
if (!getUserTenantId(request)) {
|
|
49
|
-
await reply.status(401).send({ success: false, error: "Unauthorized" });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async function authorizeKeyMutation(request, reply) {
|
|
53
|
-
if (!_a2aKeyStore) {
|
|
54
|
-
await reply.status(501).send({ error: "Key store not configured" });
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
const record = await _a2aKeyStore.findById(request.params.id);
|
|
58
|
-
if (!record) {
|
|
59
|
-
await reply.status(404).send({ success: false, error: "A2A API key not found" });
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
if (record.tenantId !== getUserTenantId(request)) {
|
|
63
|
-
await reply.status(403).send({ success: false, error: "Forbidden" });
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
return record;
|
|
67
|
-
}
|
|
68
|
-
function getUserTenantId(request) {
|
|
69
|
-
return request.user?.tenantId;
|
|
70
|
-
}
|
|
71
|
-
async function handleApiKeyList(request, reply) {
|
|
72
|
-
if (!_a2aKeyStore) {
|
|
73
|
-
reply.status(501).send({ error: "Key store not configured" });
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
const records = await _a2aKeyStore.list({
|
|
77
|
-
tenantId: getUserTenantId(request),
|
|
78
|
-
limit: request.query.limit ? parseInt(request.query.limit, 10) : void 0,
|
|
79
|
-
offset: request.query.offset ? parseInt(request.query.offset, 10) : void 0
|
|
80
|
-
});
|
|
81
|
-
reply.status(200).send({
|
|
82
|
-
success: true,
|
|
83
|
-
data: {
|
|
84
|
-
records: records.map((r) => ({ ...r, key: r.key.slice(0, 8) + "..." })),
|
|
85
|
-
total: records.length
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
async function handleApiKeyCreate(request, reply) {
|
|
90
|
-
if (!_a2aKeyStore || !_a2aAuthService) {
|
|
91
|
-
reply.status(501).send({ error: "Key store not configured" });
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const body = request.body;
|
|
95
|
-
const tenantId = getUserTenantId(request);
|
|
96
|
-
try {
|
|
97
|
-
const record = await _a2aAuthService.createKeyScoped({
|
|
98
|
-
tenantId,
|
|
99
|
-
projectId: body.projectId,
|
|
100
|
-
assistantIds: body.assistantIds,
|
|
101
|
-
grants: body.grants,
|
|
102
|
-
label: body.label
|
|
103
|
-
});
|
|
104
|
-
await refreshStoreKeyMap();
|
|
105
|
-
broadcastA2AKeyChanged(tenantId);
|
|
106
|
-
reply.status(201).send({ success: true, data: record });
|
|
107
|
-
} catch (err) {
|
|
108
|
-
if (err instanceof A2AAuthError) {
|
|
109
|
-
reply.status(err.statusCode).send({ success: false, error: err.message });
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
request.log.error({ err }, "a2a:keys:create");
|
|
113
|
-
reply.status(500).send({ success: false, error: "Failed to create key" });
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
async function handleApiKeyUpdate(request, reply) {
|
|
117
|
-
if (!_a2aKeyStore || !_a2aAuthService) {
|
|
118
|
-
reply.status(501).send({ error: "Key store not configured" });
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (!await authorizeKeyMutation(request, reply)) return;
|
|
122
|
-
const body = request.body;
|
|
123
|
-
const tenantId = getUserTenantId(request);
|
|
124
|
-
try {
|
|
125
|
-
const record = await _a2aAuthService.updateKeyScoped(
|
|
126
|
-
request.params.id,
|
|
127
|
-
tenantId,
|
|
128
|
-
{
|
|
129
|
-
label: body.label,
|
|
130
|
-
projectId: body.projectId,
|
|
131
|
-
assistantIds: body.assistantIds,
|
|
132
|
-
grants: body.grants
|
|
133
|
-
}
|
|
134
|
-
);
|
|
135
|
-
await refreshStoreKeyMap();
|
|
136
|
-
broadcastA2AKeyChanged(tenantId);
|
|
137
|
-
reply.status(200).send({ success: true, data: record });
|
|
138
|
-
} catch (err) {
|
|
139
|
-
if (err instanceof A2AAuthError) {
|
|
140
|
-
reply.status(err.statusCode).send({ success: false, error: err.message });
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
request.log.error({ err }, "a2a:keys:update");
|
|
144
|
-
reply.status(500).send({ success: false, error: "Failed to update key" });
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async function handleApiKeyDelete(request, reply) {
|
|
148
|
-
const record = await authorizeKeyMutation(request, reply);
|
|
149
|
-
if (!record) return;
|
|
150
|
-
await _a2aKeyStore.delete(request.params.id);
|
|
151
|
-
await refreshStoreKeyMap();
|
|
152
|
-
broadcastA2AKeyChanged(record.tenantId);
|
|
153
|
-
reply.status(200).send({ success: true });
|
|
154
|
-
}
|
|
155
|
-
async function handleApiKeyDisable(request, reply) {
|
|
156
|
-
const record = await authorizeKeyMutation(request, reply);
|
|
157
|
-
if (!record) return;
|
|
158
|
-
await _a2aKeyStore.disable(request.params.id);
|
|
159
|
-
await refreshStoreKeyMap();
|
|
160
|
-
broadcastA2AKeyChanged(record.tenantId);
|
|
161
|
-
reply.status(200).send({ success: true });
|
|
162
|
-
}
|
|
163
|
-
async function handleApiKeyEnable(request, reply) {
|
|
164
|
-
const record = await authorizeKeyMutation(request, reply);
|
|
165
|
-
if (!record) return;
|
|
166
|
-
await _a2aKeyStore.enable(request.params.id);
|
|
167
|
-
await refreshStoreKeyMap();
|
|
168
|
-
broadcastA2AKeyChanged(record.tenantId);
|
|
169
|
-
reply.status(200).send({ success: true });
|
|
170
|
-
}
|
|
171
|
-
async function handleApiKeyRotate(request, reply) {
|
|
172
|
-
const record = await authorizeKeyMutation(request, reply);
|
|
173
|
-
if (!record) return;
|
|
174
|
-
const rotated = await _a2aKeyStore.rotate(request.params.id);
|
|
175
|
-
await refreshStoreKeyMap();
|
|
176
|
-
broadcastA2AKeyChanged(record.tenantId);
|
|
177
|
-
reply.status(200).send({ success: true, data: rotated });
|
|
178
|
-
}
|
|
179
|
-
function registerA2ARoutes(app) {
|
|
180
|
-
const options = { preHandler: requireTenantSession };
|
|
181
|
-
for (const base of ["/api/keys", "/api/a2a/keys"]) {
|
|
182
|
-
app.get(
|
|
183
|
-
base,
|
|
184
|
-
options,
|
|
185
|
-
handleApiKeyList
|
|
186
|
-
);
|
|
187
|
-
app.post(
|
|
188
|
-
base,
|
|
189
|
-
options,
|
|
190
|
-
handleApiKeyCreate
|
|
191
|
-
);
|
|
192
|
-
app.delete(
|
|
193
|
-
`${base}/:id`,
|
|
194
|
-
options,
|
|
195
|
-
handleApiKeyDelete
|
|
196
|
-
);
|
|
197
|
-
app.post(
|
|
198
|
-
`${base}/:id/disable`,
|
|
199
|
-
options,
|
|
200
|
-
handleApiKeyDisable
|
|
201
|
-
);
|
|
202
|
-
app.post(
|
|
203
|
-
`${base}/:id/enable`,
|
|
204
|
-
options,
|
|
205
|
-
handleApiKeyEnable
|
|
206
|
-
);
|
|
207
|
-
app.post(
|
|
208
|
-
`${base}/:id/rotate`,
|
|
209
|
-
options,
|
|
210
|
-
handleApiKeyRotate
|
|
211
|
-
);
|
|
212
|
-
app.patch(
|
|
213
|
-
`${base}/:id`,
|
|
214
|
-
options,
|
|
215
|
-
handleApiKeyUpdate
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export {
|
|
221
|
-
A2A_KEY_CHANGED_TOPIC,
|
|
222
|
-
publishA2AKeyChanged,
|
|
223
|
-
handleA2AKeyChanged,
|
|
224
|
-
setA2AKeyStore,
|
|
225
|
-
setA2AAuthService,
|
|
226
|
-
refreshStoreKeyMap,
|
|
227
|
-
broadcastA2AKeyChanged,
|
|
228
|
-
registerA2ARoutes
|
|
229
|
-
};
|
|
230
|
-
//# sourceMappingURL=chunk-HYYLYIMH.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/routes/a2a.ts","../src/services/a2aKeySync.ts"],"sourcesContent":["import type { FastifyInstance, FastifyRequest, FastifyReply } from \"fastify\";\nimport type {\n A2AApiKeyRecord,\n A2AApiKeyStore,\n CreateA2AApiKeyInput,\n OutboxStore,\n} from \"@axiom-lattice/protocols\";\nimport { getStoreLattice, getReplicaId } from \"@axiom-lattice/core\";\nimport { A2AAuthError } from \"../services/a2a/A2AAuthService\";\nimport type { A2AAuthService } from \"../services/a2a/A2AAuthService\";\nimport { publishA2AKeyChanged } from \"../services/a2aKeySync\";\n\n// ─── Key Store ────────────────────────────────────────────────────────────\n\nlet _a2aKeyStore: A2AApiKeyStore | null = null;\nlet _a2aAuthService: A2AAuthService | null = null;\n\n/** Inject the A2A key store used by the keys management routes. */\nexport function setA2AKeyStore(store: A2AApiKeyStore): void {\n _a2aKeyStore = store;\n}\n\n/** Inject the A2A auth service used to create keys and refresh the key cache. */\nexport function setA2AAuthService(service: A2AAuthService): void {\n _a2aAuthService = service;\n}\n\n/**\n * Reload cached key maps after a keys CRUD mutation. Delegates to the auth\n * service's key-map cache so authentication reflects the latest keys without\n * hitting the store on every request.\n */\nexport async function refreshStoreKeyMap(): Promise<void> {\n if (_a2aAuthService) await _a2aAuthService.refresh();\n}\n\n/**\n * Best-effort broadcast of an A2A key change so peer replicas reload their key map.\n *\n * @param tenantId Tenant whose keys changed.\n * @remarks Fire-and-forget; never affects the request outcome.\n */\nexport function broadcastA2AKeyChanged(tenantId: string | undefined): void {\n if (!tenantId) return;\n try {\n const outbox = getStoreLattice(\"default\", \"outbox\").store as OutboxStore;\n void publishA2AKeyChanged(outbox, getReplicaId(), tenantId);\n } catch {\n // Best-effort: startup load / the next change corrects a miss.\n }\n}\n\ninterface RequestWithUser extends FastifyRequest {\n user?: { id: string; tenantId?: string };\n}\n\nasync function requireTenantSession(\n request: FastifyRequest,\n reply: FastifyReply,\n): Promise<void> {\n if (!getUserTenantId(request)) {\n await reply.status(401).send({ success: false, error: \"Unauthorized\" });\n }\n}\n\nasync function authorizeKeyMutation(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<A2AApiKeyRecord | null> {\n if (!_a2aKeyStore) {\n await reply.status(501).send({ error: \"Key store not configured\" });\n return null;\n }\n const record = await _a2aKeyStore.findById(request.params.id);\n if (!record) {\n await reply.status(404).send({ success: false, error: \"A2A API key not found\" });\n return null;\n }\n if (record.tenantId !== getUserTenantId(request)) {\n await reply.status(403).send({ success: false, error: \"Forbidden\" });\n return null;\n }\n return record;\n}\n\nfunction getUserTenantId(request: FastifyRequest): string | undefined {\n return (request as RequestWithUser).user?.tenantId;\n}\n\n// ─── API Key Management Handlers ─────────────────────────────────────────\n\nasync function handleApiKeyList(\n request: FastifyRequest<{ Querystring: { tenantId?: string; limit?: string; offset?: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n if (!_a2aKeyStore) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n const records = await _a2aKeyStore.list({\n tenantId: getUserTenantId(request),\n limit: request.query.limit ? parseInt(request.query.limit, 10) : undefined,\n offset: request.query.offset ? parseInt(request.query.offset, 10) : undefined,\n });\n\n reply.status(200).send({\n success: true,\n data: {\n records: records.map((r) => ({ ...r, key: r.key.slice(0, 8) + \"...\" })),\n total: records.length,\n },\n });\n}\n\nasync function handleApiKeyCreate(\n request: FastifyRequest<{ Body: Partial<CreateA2AApiKeyInput> }>,\n reply: FastifyReply,\n): Promise<void> {\n if (!_a2aKeyStore || !_a2aAuthService) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n\n const body = request.body as Partial<CreateA2AApiKeyInput>;\n const tenantId = getUserTenantId(request)!;\n\n try {\n const record = await _a2aAuthService.createKeyScoped({\n tenantId,\n projectId: body.projectId,\n assistantIds: body.assistantIds,\n grants: body.grants,\n label: body.label,\n });\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(tenantId);\n reply.status(201).send({ success: true, data: record });\n } catch (err) {\n if (err instanceof A2AAuthError) {\n reply.status(err.statusCode).send({ success: false, error: err.message });\n return;\n }\n request.log.error({ err }, \"a2a:keys:create\");\n reply.status(500).send({ success: false, error: \"Failed to create key\" });\n }\n}\n\nasync function handleApiKeyUpdate(\n request: FastifyRequest<{ Params: { id: string }; Body: Partial<CreateA2AApiKeyInput> }>,\n reply: FastifyReply,\n): Promise<void> {\n if (!_a2aKeyStore || !_a2aAuthService) {\n reply.status(501).send({ error: \"Key store not configured\" });\n return;\n }\n if (!(await authorizeKeyMutation(request, reply))) return;\n\n const body = request.body as Partial<CreateA2AApiKeyInput>;\n const tenantId = getUserTenantId(request)!;\n try {\n const record = await _a2aAuthService.updateKeyScoped(\n request.params.id,\n tenantId,\n {\n label: body.label,\n projectId: body.projectId,\n assistantIds: body.assistantIds,\n grants: body.grants,\n },\n );\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(tenantId);\n reply.status(200).send({ success: true, data: record });\n } catch (err) {\n if (err instanceof A2AAuthError) {\n reply.status(err.statusCode).send({ success: false, error: err.message });\n return;\n }\n request.log.error({ err }, \"a2a:keys:update\");\n reply.status(500).send({ success: false, error: \"Failed to update key\" });\n }\n}\n\nasync function handleApiKeyDelete(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const record = await authorizeKeyMutation(request, reply);\n if (!record) return;\n\n await _a2aKeyStore!.delete(request.params.id);\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(record.tenantId);\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyDisable(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const record = await authorizeKeyMutation(request, reply);\n if (!record) return;\n\n await _a2aKeyStore!.disable(request.params.id);\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(record.tenantId);\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyEnable(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const record = await authorizeKeyMutation(request, reply);\n if (!record) return;\n\n await _a2aKeyStore!.enable(request.params.id);\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(record.tenantId);\n\n reply.status(200).send({ success: true });\n}\n\nasync function handleApiKeyRotate(\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n): Promise<void> {\n const record = await authorizeKeyMutation(request, reply);\n if (!record) return;\n\n const rotated = await _a2aKeyStore!.rotate(request.params.id);\n await refreshStoreKeyMap();\n broadcastA2AKeyChanged(record.tenantId);\n\n reply.status(200).send({ success: true, data: rotated });\n}\n\n// ─── Route Registration ───────────────────────────────────────────────────\n\n/**\n * Register the API key management routes.\n *\n * `/api/keys` is the canonical unified surface; `/api/a2a/keys` remains as a\n * backward-compatible alias for existing clients.\n */\nexport function registerA2ARoutes(app: FastifyInstance): void {\n const options = { preHandler: requireTenantSession };\n for (const base of [\"/api/keys\", \"/api/a2a/keys\"]) {\n app.get<{ Querystring: { tenantId?: string; limit?: string; offset?: string } }>(\n base,\n options,\n handleApiKeyList,\n );\n\n app.post<{ Body: Partial<CreateA2AApiKeyInput> }>(\n base,\n options,\n handleApiKeyCreate,\n );\n\n app.delete<{ Params: { id: string } }>(\n `${base}/:id`,\n options,\n handleApiKeyDelete,\n );\n\n app.post<{ Params: { id: string } }>(\n `${base}/:id/disable`,\n options,\n handleApiKeyDisable,\n );\n\n app.post<{ Params: { id: string } }>(\n `${base}/:id/enable`,\n options,\n handleApiKeyEnable,\n );\n\n app.post<{ Params: { id: string } }>(\n `${base}/:id/rotate`,\n options,\n handleApiKeyRotate,\n );\n app.patch<{ Params: { id: string }; Body: Partial<CreateA2AApiKeyInput> }>(\n `${base}/:id`,\n options,\n handleApiKeyUpdate,\n );\n }\n}\n","import type { OutboxStore } from \"@axiom-lattice/protocols\";\n\n/** Outbox topic carrying cross-replica A2A API-key invalidation. */\nexport const A2A_KEY_CHANGED_TOPIC = \"a2a-key-changed\";\n\n/**\n * Best-effort publish of an A2A key change to the durable outbox.\n *\n * @param outbox Shared outbox store.\n * @param owner This replica id (recorded as the event origin).\n * @param tenantId Tenant whose keys changed.\n * @remarks Never throws; peers fall back to startup load / the next change.\n */\nexport async function publishA2AKeyChanged(\n outbox: OutboxStore,\n owner: string,\n tenantId: string,\n): Promise<void> {\n if (!tenantId) return;\n try {\n await outbox.append({\n topic: A2A_KEY_CHANGED_TOPIC,\n payload: { tenantId },\n originReplicaId: owner,\n });\n } catch {\n // Best-effort: startup load / the next change corrects a miss.\n }\n}\n\n/**\n * Reloads this replica's key map after a foreign key change.\n *\n * @param payload Outbox payload, expected to carry a non-empty `tenantId`.\n * @param reload Store-backed reload callback (refreshes the global key map).\n */\nexport async function handleA2AKeyChanged(\n payload: unknown,\n reload: () => Promise<void>,\n): Promise<void> {\n const tenantId = (payload as { tenantId?: unknown } | null)?.tenantId;\n if (typeof tenantId !== \"string\" || tenantId.length === 0) return;\n await reload();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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 { grantCovers, type OpenToolDescriptor } 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} with\n * `connectAll: true`, scoped to the credential tenant. Injected by tests and\n * by callers that have already 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 /** 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 * Default connection resolution: every connection of the invoked plugin type,\n * scoped to the credential tenant.\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 { connectAll: true },\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 (see\n // `registerExposedPluginTools` for global plugins).\n const { connection, connections } = await resolveConnections(entry.pluginType, ctx);\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":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/a2a/A2AAgentCardBuilder.ts"],"sourcesContent":["import type { AgentCard, A2ASkill, AgentMiddlewareConfig } from \"@axiom-lattice/protocols\";\n\n/** Minimal assistant shape needed to derive a card from its own definition. */\nexport interface CardAssistant {\n id: string;\n name: string;\n description?: string;\n graphDefinition?: {\n middleware?: AgentMiddlewareConfig[];\n } | null;\n}\n\n/**\n * Parameters for building a per-assistant A2A AgentCard.\n */\nexport interface BuildAgentCardParams {\n assistant: CardAssistant;\n /** Gateway origin, e.g. \"https://gw.example.com\". */\n baseUrl: string;\n /** Optional JSON-RPC endpoint path override (official `/a2a/:id` alias). */\n jsonRpcPath?: string;\n /** Advertise push notification support; defaults to `true`. */\n pushNotifications?: boolean;\n}\n\n/**\n * Derive the card's advertised skills from the agent's own definition: every\n * enabled middleware contributes a skill (id = middleware type, name/description\n * from its config). No manual `a2aExposure` declaration — the card reflects\n * what the agent is actually built from.\n */\nexport function deriveAgentSkills(assistant: CardAssistant): A2ASkill[] {\n const enabled = assistant.graphDefinition?.middleware?.filter((m) => m.enabled) ?? [];\n return enabled.map((m) => ({\n id: m.type || m.id,\n name: m.name || m.type || m.id,\n description: m.description || \"\",\n tags: [],\n examples: [],\n }));\n}\n\n/**\n * Build an A2A 0.3 AgentCard (discovery document) for a single assistant.\n *\n * `url` points at the assistant's JSON-RPC endpoint (also advertised via\n * `additionalInterfaces`). Skills are derived from the agent definition;\n * input/output modes default to text.\n */\nexport function buildAgentCardForAssistant(params: BuildAgentCardParams): AgentCard {\n const { assistant, baseUrl } = params;\n const jsonRpcUrl = `${baseUrl}${\n params.jsonRpcPath ?? `/api/a2a/agents/${assistant.id}/jsonrpc`\n }`;\n\n return {\n name: assistant.name,\n description: assistant.description ?? \"\",\n url: jsonRpcUrl,\n protocolVersion: \"0.3.0\",\n version: \"1.0.0\",\n supportsAuthenticatedExtendedCard: true,\n defaultInputModes: [\"text\"],\n defaultOutputModes: [\"text\"],\n capabilities: {\n streaming: true,\n pushNotifications: params.pushNotifications ?? true,\n stateTransitionHistory: true,\n extensions: [\n {\n uri: \"https://axiom-lattice.dev/a2a/extensions/push-notifications-experimental\",\n description:\n \"Push notifications are experimental and best-effort. Delivery is at-most-once; clients should poll tasks/get or use tasks/resubscribe for reliability.\",\n params: {\n experimental: true,\n reliable: false,\n delivery: \"at-most-once\",\n },\n required: false,\n },\n ],\n },\n skills: deriveAgentSkills(assistant),\n additionalInterfaces: [{ transport: \"JSONRPC\", url: jsonRpcUrl }],\n securitySchemes: {\n bearer: {\n type: \"http\",\n scheme: \"Bearer\",\n description: \"API key sent as an Authorization Bearer token\",\n },\n apiKeyHeader: {\n type: \"apiKey\",\n in: \"header\",\n name: \"x-api-key\",\n description: \"API key sent via the x-api-key header\",\n },\n },\n security: [{ bearer: [] }, { apiKeyHeader: [] }],\n };\n}\n"],"mappings":";AA+BO,SAAS,kBAAkB,WAAsC;AACtE,QAAM,UAAU,UAAU,iBAAiB,YAAY,OAAO,CAAC,MAAM,EAAE,OAAO,KAAK,CAAC;AACpF,SAAO,QAAQ,IAAI,CAAC,OAAO;AAAA,IACzB,IAAI,EAAE,QAAQ,EAAE;AAAA,IAChB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAC5B,aAAa,EAAE,eAAe;AAAA,IAC9B,MAAM,CAAC;AAAA,IACP,UAAU,CAAC;AAAA,EACb,EAAE;AACJ;AASO,SAAS,2BAA2B,QAAyC;AAClF,QAAM,EAAE,WAAW,QAAQ,IAAI;AAC/B,QAAM,aAAa,GAAG,OAAO,GAC3B,OAAO,eAAe,mBAAmB,UAAU,EAAE,UACvD;AAEA,SAAO;AAAA,IACL,MAAM,UAAU;AAAA,IAChB,aAAa,UAAU,eAAe;AAAA,IACtC,KAAK;AAAA,IACL,iBAAiB;AAAA,IACjB,SAAS;AAAA,IACT,mCAAmC;AAAA,IACnC,mBAAmB,CAAC,MAAM;AAAA,IAC1B,oBAAoB,CAAC,MAAM;AAAA,IAC3B,cAAc;AAAA,MACZ,WAAW;AAAA,MACX,mBAAmB,OAAO,qBAAqB;AAAA,MAC/C,wBAAwB;AAAA,MACxB,YAAY;AAAA,QACV;AAAA,UACE,KAAK;AAAA,UACL,aACE;AAAA,UACF,QAAQ;AAAA,YACN,cAAc;AAAA,YACd,UAAU;AAAA,YACV,UAAU;AAAA,UACZ;AAAA,UACA,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,IACA,QAAQ,kBAAkB,SAAS;AAAA,IACnC,sBAAsB,CAAC,EAAE,WAAW,WAAW,KAAK,WAAW,CAAC;AAAA,IAChE,iBAAiB;AAAA,MACf,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,MACA,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,cAAc,CAAC,EAAE,CAAC;AAAA,EACjD;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as Ie,b as Re,c as vt,d as xt,e as wt,f as Tt,g as It,h as St,i as Ft,j as Pt,k as Rt}from"./embed-page-chunk-YMVKQQXJ.js";import{a as _e}from"./embed-page-chunk-JTL73EBV.js";import"./embed-page-chunk-PROZXFWS.js";import"./embed-page-chunk-IELKCYQX.js";import"./embed-page-chunk-FE6KYRCO.js";import{b as Oe,e as Ve,f as ft,g as yt,h as ht,i as Ct,l as At,m as bt}from"./embed-page-chunk-DJKD2PHO.js";import"./embed-page-chunk-D7YTSCUO.js";import{a as ct,b as dt,c as gt,d as pt,e as mt}from"./embed-page-chunk-SV2BM3WO.js";import"./embed-page-chunk-S4XZPBRM.js";import"./embed-page-chunk-WGEXK773.js";import{T as kt,e as lt,h as ut,r as Ne}from"./embed-page-chunk-IBRMWDHE.js";import{i as rt}from"./embed-page-chunk-4XKCMM5C.js";import{a as me}from"./embed-page-chunk-PMOGSY6W.js";import{o as Ze}from"./embed-page-chunk-BSRASK7V.js";import{C as et,La as st,Q as tt,U as nt,ba as at,n as Te,oa as ot,ua as it}from"./embed-page-chunk-F7ZUYG42.js";import{k as z}from"./embed-page-chunk-ASEIOK7X.js";import{a as E}from"./embed-page-chunk-QV7QAJ4N.js";import"./embed-page-chunk-VW3NF5NT.js";import"./embed-page-chunk-34RWKVD7.js";import{Ja as Xe,Q as Je,g as Ye,na as Pe,za as Qe}from"./embed-page-chunk-B2KNZ3EW.js";import{Da as R,Ia as V,Ka as Le,La as w,Ma as pe,Sa as $e,Za as we,_ as W,_a as We,bb as X,fa as xe,ha as je,na as Fe,ua as Be,va as Ue,xa as qe,za as He}from"./embed-page-chunk-F7TA3LRH.js";import{a as ge}from"./embed-page-chunk-R5QL57JJ.js";import"./embed-page-chunk-QOWM3LMZ.js";import"./embed-page-chunk-EUOOO2LS.js";import"./embed-page-chunk-5JDQ456X.js";import{f as I}from"./embed-page-chunk-QBTAJ2YI.js";var h=I(ge());var te=I(ge());var Z=I(ge());var Dt=I(ge());var se=I(E()),Mt=60,Et=72,Ge=(0,Dt.memo)(({data:i})=>{let l=Ie(i.type),f=Math.min(12+Math.sqrt(i.subAgentCount)*10,48);return(0,se.jsxs)("div",{style:{display:"flex",flexDirection:"column",alignItems:"center"},children:[(0,se.jsx)(Ve,{type:"target",position:Oe.Top,style:{left:"50%",top:"50%",transform:"translate(-50%, -50%)",background:"transparent",border:"none",width:0,height:0},isConnectable:!1}),(0,se.jsx)("div",{title:i.label,style:{width:f,height:f,borderRadius:"50%",background:Re(l.color),display:"flex",alignItems:"center",justifyContent:"center",color:"#fff",fontSize:Math.max(10,f*.4),fontWeight:600,cursor:"pointer",boxShadow:"0 2px 8px rgba(0,0,0,0.15)"},children:i.label.charAt(0).toUpperCase()}),(0,se.jsx)("div",{style:{fontSize:11,maxWidth:120,textAlign:"center",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",marginTop:4},children:i.label}),(0,se.jsx)(Ve,{type:"source",position:Oe.Top,style:{left:"50%",top:"50%",transform:"translate(-50%, -50%)",background:"transparent",border:"none",width:0,height:0},isConnectable:!1})]})});Ge.displayName="AgentGraphNode";var ee=I(E()),en=({assistants:i,onNodeClick:l})=>{let[f,c,u]=ht([]),[o,e,C]=Ct([]),[r,m]=(0,Z.useState)(null),S=(0,Z.useMemo)(()=>({agent:Ge}),[]),M=(0,Z.useMemo)(()=>wt(i),[i]);(0,Z.useEffect)(()=>{let v=M.nodes.map(g=>({id:g.id})),P=M.edges.map(g=>({source:g.source,target:g.target})),D=new Map(M.nodes.map(g=>[g.id,g])),$=pt(v).force("link",gt(P).id(g=>g.id).distance(80)).force("charge",mt().strength(-220)).force("center",ct(0,0)).force("collide",dt(45)).stop();for(let g=0;g<300;g++)$.tick();let j=v.map(g=>({id:g.id,type:"agent",data:D.get(g.id),position:{x:g.x-Mt/2,y:g.y-Et/2}})),a=M.edges.map(g=>({id:`e${g.source}-${g.target}`,source:g.source,target:g.target,style:{stroke:"#1677ff",strokeWidth:2},type:"straight"}));c(j),e(a)},[M,c,e]);let H=(0,Z.useMemo)(()=>r===null?f:f.map(v=>{let P=v.id===r||o.some(D=>D.source===r&&D.target===v.id||D.target===r&&D.source===v.id);return{...v,style:{opacity:P?1:.2,transition:"opacity 0.2s ease"}}}),[f,o,r]),d=(0,Z.useMemo)(()=>r===null?o.map(v=>({...v,style:{stroke:"#1677ff",strokeWidth:1.5,opacity:.25,transition:"opacity 0.2s ease"}})):o.map(v=>{let P=v.source===r||v.target===r;return{...v,style:{stroke:"#1677ff",strokeWidth:P?2.5:1,opacity:P?1:.04,transition:"opacity 0.2s ease"}}}),[o,r]);return(0,ee.jsx)("div",{style:{width:"100%",height:"100%",background:"#fafafa",position:"relative"},children:(0,ee.jsxs)(yt,{nodes:H,edges:d,onNodesChange:u,onEdgesChange:C,onNodeMouseEnter:(v,P)=>m(P.id),onNodeMouseLeave:()=>m(null),onNodeClick:(v,P)=>l?.(String(P.id)),nodeTypes:S,fitView:!0,fitViewOptions:{padding:.25},children:[(0,ee.jsx)(At,{color:"#eee",gap:20}),(0,ee.jsx)(bt,{})]})})},Kt=i=>(0,ee.jsx)(ft,{children:(0,ee.jsx)(en,{...i})});var T=I(E()),tn=[{key:"all",labelKey:"governance.list.tab.all"},{key:"capability",labelKey:"governance.list.tab.capability"},{key:"orchestrator",labelKey:"governance.list.tab.orchestrator"},{key:"workflow",labelKey:"governance.list.tab.workflow"}],Bt=({items:i,assistants:l=[],loading:f,onItemClick:c,onOpenPlayground:u,onOpenEval:o,onCreate:e,onDelete:C})=>{let r=z(),[m,S]=(0,te.useState)("all"),[M,H]=(0,te.useState)(null),[d,v]=(0,te.useState)(null),P=(0,te.useMemo)(()=>{let a=new Set;return i.forEach(g=>g.tags.forEach(U=>a.add(U))),Array.from(a).map(g=>({value:g,label:g}))},[i]),D=(0,te.useMemo)(()=>i.filter(a=>!(m!=="all"&&a.type!==m||M&&!a.tags.includes(M)||d&&a.status!==d)),[i,m,M,d]),$=[{key:"edit",icon:it,label:r("common.edit"),onClick:a=>c(a.id)},{key:"playground",icon:et,label:r("governance.list.action.playground"),onClick:a=>u(a.id)},{key:"eval",icon:nt,label:r("governance.list.action.eval"),onClick:a=>o(a.id)}];C&&$.push({key:"delete",icon:st,label:r("common.delete"),danger:!0,onClick:a=>C(a.id)});let j=(0,te.useMemo)(()=>[{mode:"graph",icon:ot,label:r("governance.list.viewMode.graph"),render:()=>(0,T.jsx)(Kt,{assistants:l,onNodeClick:c})}],[l,c,r]);return(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)("span",{"data-testid":"agent-list-state","data-loading":String(f),"data-count":D.length,hidden:!0,"aria-hidden":"true"}),(0,T.jsx)(lt,{title:r("governance.list.title"),itemName:"common.item.agent",items:D,loading:f,keyExtractor:a=>a.id,tabs:tn.map(a=>({key:a.key,label:r(a.labelKey)})),activeTab:m,onTabChange:a=>S(a),headerLeft:(0,T.jsxs)("div",{style:{display:"flex",gap:8,alignItems:"center"},children:[(0,T.jsx)(Ne,{value:M,onChange:H,options:P,placeholder:r("governance.list.filter.domain"),allLabel:r("governance.list.filter.allDomains")}),(0,T.jsx)(Ne,{value:d,onChange:v,options:[{value:"enabled",label:r("governance.list.status.enabled")},{value:"draft",label:r("governance.list.status.draft")},{value:"disabled",label:r("governance.list.status.disabled")}],placeholder:r("governance.list.filter.status"),allLabel:r("governance.list.filter.allStatuses")})]}),renderIcon:a=>{let g=Re(Ie(a.type).color);return(0,T.jsx)("div",{title:r("governance.list.typeIconTitle",{type:a.type}),style:{width:32,height:32,borderRadius:8,background:`${g}1A`,color:g,display:"flex",alignItems:"center",justifyContent:"center",flexShrink:0},children:(0,T.jsx)(Te,{size:18})})},renderPrimary:a=>(0,T.jsx)("span",{"data-testid":"agent-name","data-agent-id":a.id,children:a.name}),renderSecondary:a=>a.description||void 0,renderDetails:a=>(0,T.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",gap:8,flexWrap:"wrap"},children:[(0,T.jsxs)("div",{style:{display:"flex",gap:4,flexWrap:"wrap",alignItems:"center"},children:[a.tags.map(g=>(0,T.jsx)(we,{color:"magenta",style:{margin:0,borderRadius:10,border:"none"},children:g},g)),(0,T.jsx)(we,{color:Ie(a.type).color,style:{margin:0,borderRadius:10,border:"none"},children:a.type})]}),(0,T.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,flexShrink:0},children:[(0,T.jsx)(je,{title:r("governance.list.statusTooltip",{status:a.status}),children:(0,T.jsx)("span",{style:{width:8,height:8,borderRadius:"50%",background:vt(a.status),display:"inline-block",cursor:"help"}})}),(0,T.jsx)("span",{style:{fontSize:12,color:"#8c8c8c"},children:a.version})]})]}),itemActions:$,actionsStyle:"icon",actionsPosition:"hover",onItemClick:a=>c(a.id),createLabel:r("governance.list.create"),onCreateClick:e,viewModeExtensions:j,emptyIcon:(0,T.jsx)(Te,{size:48,style:{opacity:.5}}),emptyTitle:r("governance.list.emptyTitle"),emptyDescription:r("governance.list.emptyDescription")})]})};var A=I(ge());var re=I(ge());var ue=I(E()),{Text:nn}=X,Lt=({onSelect:i,value:l,placeholder:f})=>{let c=z(),u=f??c("chatConfig.parentAgent.selectPlaceholder"),[o,e]=(0,re.useState)([]),[C,r]=(0,re.useState)(!1),{get:m}=me(),S=(0,re.useCallback)(async()=>{r(!0);try{let d=await m("/api/assistants");d.success&&e(d.data?.records||[])}catch(d){console.error("Failed to fetch assistants:",d)}finally{r(!1)}},[m]);return(0,re.useEffect)(()=>{S()},[S]),(0,ue.jsx)(xe,{showSearch:!0,allowClear:!0,value:l,placeholder:u,loading:C,onChange:d=>{if(!d){i(null);return}let v=o.find(P=>P.id===d);i(v||null)},onDropdownVisibleChange:d=>{d&&S()},optionFilterProp:"label",style:{width:"100%"},options:o.map(d=>({value:d.id,label:d.name||d.id,description:d.description})),optionRender:d=>(0,ue.jsxs)("div",{children:[(0,ue.jsx)("div",{children:d.label}),d.data.description&&(0,ue.jsx)(nn,{type:"secondary",style:{fontSize:12},children:d.data.description})]})})};var L=I(E()),{Text:Nt}=X,Ot=({subAgentKeys:i,decisions:l,onChange:f})=>{let{token:c}=We.useToken(),u=z(),o=(C,r)=>{let m=l.map(S=>S.originalKey===C?{...S,clone:r,cloneKey:r?`${C}_clone`:void 0}:S);f(m)},e=(C,r)=>{let m=l.map(S=>S.originalKey===C?{...S,cloneKey:r}:S);f(m)};return i.length===0?null:(0,L.jsxs)("div",{children:[(0,L.jsx)(Nt,{type:"secondary",style:{display:"block",marginBottom:8},children:u("chatConfig.subAgentInherit.hint")}),(0,L.jsx)(Le,{size:"small",bordered:!0,dataSource:l,style:{borderRadius:c.borderRadius},renderItem:C=>(0,L.jsxs)(Le.Item,{style:{display:"flex",flexDirection:"column",alignItems:"stretch",gap:8,padding:"8px 16px"},children:[(0,L.jsxs)("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[(0,L.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:[C.clone?(0,L.jsx)(Pe,{style:{color:c.colorPrimary}}):(0,L.jsx)(Qe,{style:{color:c.colorTextSecondary}}),(0,L.jsx)(Nt,{style:{fontFamily:"monospace"},children:C.originalKey})]}),(0,L.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8},children:[(0,L.jsx)(we,{color:C.clone?"blue":"default",children:C.clone?u("chatConfig.subAgentInherit.clone"):u("chatConfig.subAgentInherit.reuse")}),(0,L.jsx)($e,{size:"small",checked:C.clone,onChange:r=>o(C.originalKey,r)})]})]}),C.clone&&(0,L.jsx)(V,{size:"small",placeholder:u("chatConfig.subAgentInherit.cloneKeyPlaceholder"),value:C.cloneKey,onChange:r=>e(C.originalKey,r.target.value),style:{fontFamily:"monospace",fontSize:12},addonBefore:u("chatConfig.keyValue.key")})]})})]})};function Vt(i,l){return i.mode!=="local"||i.templateKey||typeof i.executable=="string"&&i.executable.trim().length>0?null:l?l("chat.config.executableRequired"):"Choose a template or enter a custom executable"}var n=I(E()),{TextArea:_t}=V,{Text:ne}=X,Gt="/.well-known/agent-card.json",an=[{value:"http://",label:"http://"},{value:"https://",label:"https://"}],on={react:"chatConfig.createAssistant.typeDesc.react",deep_agent:"chatConfig.createAssistant.typeDesc.deepAgent",processing:"chatConfig.createAssistant.typeDesc.processing",a2a_remote:"chatConfig.createAssistant.typeDesc.a2aRemote"};function zt(i,l,f="https://"){let c=String(i??"").trim();if(!c)return"";let u=l==="http://"||l==="https://"?l:f,e=(/^https?:\/\//i.test(c)?c:`${u}${c.replace(/^\/+/,"")}`).replace(/\/+$/,"");return e.endsWith(Gt)?e:`${e}${Gt}`}function sn(i){try{return new URL(i).port||void 0}catch{return}}function rn(i){return i.some(l=>l==="--port"||l==="-p"||/^--port=/.test(l)||/^-p=/.test(l))}function jt(i,l){let f=[...i.args],c=sn(l);return c&&!rn(f)&&f.push("--port",c),{...i,args:f,...i.env?{env:{...i.env}}:{}}}var Ut=({open:i,onCancel:l,onSuccess:f,initialMode:c,initialValues:u})=>{let[o]=R.useForm(),e=z(),[C,r]=(0,A.useState)(!1),[m,S]=(0,A.useState)("learn"),[M,H]=(0,A.useState)(""),[d,v]=(0,A.useState)(null),[P,D]=(0,A.useState)([]),$=R.useWatch("type",o)||"react",j=$==="a2a_remote",[a,g]=(0,A.useState)("remote"),[U,fe]=(0,A.useState)(void 0),[le,ye]=(0,A.useState)([]),[Se,ce]=(0,A.useState)(!1),ae=(0,A.useRef)(0),[oe,ie]=(0,A.useState)(!1),[De,Y]=(0,A.useState)(!1),{get:N,post:J,put:he}=me(),Me=Ze("agent-architect"),{workspaceId:Ce,trainingProjects:Q,loading:de}=rt(),[Ee,Ae]=(0,A.useState)(null),Ke=JSON.stringify(u??null),K=Q.find(t=>t.id===Ee)??Q[0]??null,[be,ke]=(0,A.useState)([]),p=(0,A.useCallback)(()=>{v(null),D([])},[]),b=(0,A.useCallback)(()=>{o.resetFields(),p(),S("learn"),H(""),ke([]),Ae(null),g("remote"),fe(void 0),ye([]),Y(!1),ie(!1)},[o,p]);(0,A.useEffect)(()=>{if(i){b(),S(c??"learn");let t=u?.a2aMode;(t==="remote"||t==="local")&&g(t),o.setFieldsValue({type:"react",agentCardProtocol:"https://",a2aMode:"remote",timeout:3e5,...u})}},[i,o,c,Ke,b]),(0,A.useEffect)(()=>{let t=++ae.current,s=!0;return i&&j?(o.setFieldValue("prompt",""),a==="local"?(ce(!0),N("/api/local-a2a/templates").then(k=>{s&&t===ae.current&&k?.data?.records&&ye(k.data.records)}).catch(()=>{s&&t===ae.current&&ye([])}).finally(()=>{s&&t===ae.current&&ce(!1)})):ce(!1)):ce(!1),()=>{s=!1,ae.current+=1}},[o,j,a,N,i]);let x=le.find(t=>t.key===U),q=(0,A.useCallback)(()=>{let t=Vt({mode:a,templateKey:U,executable:o.getFieldValue("commandExecutable")},e);return t?(Y(!0),o.setFields([{name:"commandExecutable",errors:[t]}]),w.warning(t),!1):!0},[a,U,o,e]);(0,A.useEffect)(()=>{x&&o.setFieldsValue({agentCardProtocol:"http://"})},[o,x]);let ve=(0,A.useCallback)(async()=>{if(!q())return;let t=zt(o.getFieldValue("agentCardUrl"),o.getFieldValue("agentCardProtocol"),a==="local"?"http://":"https://");if(!t){w.warning(e("chatConfig.createAssistant.a2aUrlWarning"));return}ie(!0);try{let s={agentCardUrl:t,apiKey:o.getFieldValue("apiKey")};if(a==="local"){let y=x;if(y)s.localRuntime={label:o.getFieldValue("name")||void 0,agentCardUrl:t,command:jt(y.command,t)};else{let B=o.getFieldValue("commandArgs")||"",G=B?B.split(/\s+/).filter(Boolean):[],F=String(o.getFieldValue(["command","cwd"])||"").trim();s.localRuntime={label:o.getFieldValue("name")||void 0,agentCardUrl:t,command:{executable:String(o.getFieldValue("commandExecutable")||"").trim(),args:G,...F?{cwd:F}:{}}}}}let k=await J("/api/a2a/test-connection",s);if(k.data?.reachable){let y=k.data.agentCard;w.success(e("chatConfig.createAssistant.connectedTo",{name:k.data.agentName||t}))}else w.error(k.data?.error||e("chatConfig.createAssistant.connectionFailed"))}catch{w.error(e("chatConfig.createAssistant.connectionTestFailed"))}finally{ie(!1)}},[o,J,a,x,q,e]),$t=(0,A.useCallback)(t=>{if(v(t),!t){o.resetFields(["key","name","description","type","prompt"]),D([]);return}let s=typeof t.graphDefinition=="object"&&t.graphDefinition!==null?t.graphDefinition:{},k=s.key||t.id,y=t.name||s.name||"";o.setFieldsValue({key:`${k}_ext`,name:y?`${y} (Extended)`:"",description:t.description||s.description||"",type:s.type||"react",prompt:s.prompt||""});let B=s.subAgents||[];D(B.map(G=>({originalKey:G,clone:!1})))},[o]),Wt=async(t,s)=>{let k=await N(`/api/assistants/${t}`);if(!k.success)throw new Error(e("chatConfig.createAssistant.cloneFetchFailed",{key:t}));let y=k.data,B=typeof y.graphDefinition=="object"?y.graphDefinition:{},G={id:s,name:`${y.name} (Clone)`,description:y.description||"",graphDefinition:{...B,key:s,name:`${y.name} (Clone)`}},F=await J("/api/assistants",G);if(!F.success)throw new Error(F.message||e("chatConfig.createAssistant.cloneFailed",{key:t}));return F.data},Zt=t=>{let s={id:t.key,name:t.name,description:t.description||"",graphDefinition:{key:t.key,name:t.name,description:t.description||"",type:t.type,prompt:t.prompt||"",tools:[],skills:[],subAgents:t.type==="deep_agent"||t.type==="processing"?[]:void 0,middleware:[]}};if(t.type==="a2a_remote"&&(s.graphDefinition.agentCardUrl=zt(t.agentCardUrl,t.agentCardProtocol,(t.a2aMode||a)==="local"?"http://":"https://"),t.apiKey&&(s.graphDefinition.apiKey=t.apiKey),t.timeout&&(s.graphDefinition.timeout=t.timeout),(t.a2aMode||a)==="local")){let k=x;if(k)s.graphDefinition.localRuntime={label:t.name||void 0,agentCardUrl:s.graphDefinition.agentCardUrl,command:jt(k.command,s.graphDefinition.agentCardUrl)};else{let y=t.commandArgs||"",B=y?y.split(/\s+/).filter(Boolean):[],G=String(t.command?.cwd||"").trim();s.graphDefinition.localRuntime={label:t.name||void 0,agentCardUrl:s.graphDefinition.agentCardUrl,command:{executable:String(t.commandExecutable||"").trim(),args:B,...G?{cwd:G}:{}}}}}return s},Yt=async(t,s)=>{if(!d)throw new Error(e("chatConfig.createAssistant.noParentAgent"));let k=P.filter(F=>F.clone);for(let F of k){if(!F.cloneKey||!F.cloneKey.trim())throw new Error(e("chatConfig.createAssistant.cloneKeyRequired",{key:F.originalKey}));if(!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(F.cloneKey))throw new Error(e("chatConfig.createAssistant.cloneKeyInvalid",{key:F.cloneKey,originalKey:F.originalKey}))}let y=[];for(let F of P)if(F.clone){let Xt=await Wt(F.originalKey,F.cloneKey);s.push(Xt),y.push(F.cloneKey)}else y.push(F.originalKey);let B=typeof d.graphDefinition=="object"?d.graphDefinition:{},G={key:t.key,name:t.name,description:t.description||"",extendsAgent:d.id,type:t.type||B.type};return t.prompt&&t.prompt!==B.prompt&&(G.prompt=t.prompt),(y.length>0||B.subAgents&&B.subAgents.length>0)&&(G.subAgents=y),{id:t.key,name:t.name,description:t.description||"",graphDefinition:G}},Jt=async()=>{if(m==="learn"){let t=M.trim();if(!t){w.error(e("chatConfig.createAssistant.trainingGoalRequired"));return}if(!K){w.error(e("chatConfig.createAssistant.noTrainingProject"));return}r(!0);try{let s=`agent-${It(t+Date.now())}`,k=await J("/api/assistants",{id:s,name:s,graphDefinition:{key:s,name:s,description:"",type:"react",prompt:"To be trained",metadata:{trainingProjectId:K.id,learningPlaceholder:"true",architectureStatus:"undecided"}}});if(!k.data)throw new Error(e("chatConfig.createAssistant.agentCreateFailed"));let{roundTaskId:y}=await Ft({goal:t,targetAgentId:s,workspaceId:Ce??void 0,projectId:K.id,existingThreadId:St(Me.tenantId,s),files:be,post:J,put:he});w.success(e("chatConfig.createAssistant.agentCreated")),H(""),ke([]),S("learn"),f?.(k.data,{initialMessage:Pt({goal:t,roundTaskId:y,targetAgentId:s,targetMode:"placeholder",files:be,projectId:K.id,workspaceId:K.workspaceId??Ce??void 0}),projectId:K.id,initialMessageCustomRunConfig:{execution:{taskId:y}}}),l()}catch(s){w.error(s instanceof Error?s.message:e("chatConfig.createAssistant.agentCreateFailed"))}finally{r(!1)}return}try{if(m==="scratch"&&j&&!q())return;let t=await o.validateFields();r(!0);let s=[],k=m==="scratch"?Zt(t):await Yt(t,s),y=await J("/api/assistants",k);if(y.success){w.success(m==="extend"?e("chatConfig.createAssistant.createdByExtending",{name:d?.name??""}):e("chatConfig.createAssistant.created")),b();for(let B of s)f?.(B);f?.(y.data),l()}else w.error(y.message||e("chatConfig.createAssistant.createFailed"))}catch(t){if(t?.errorFields){let s={key:e("chatConfig.createAssistant.field.key"),name:e("chatConfig.createAssistant.field.name"),description:e("chatConfig.createAssistant.field.description"),prompt:e("chatConfig.createAssistant.field.systemPrompt")},k=t.errorFields.map(y=>s[String((y.name??[])[0])]??String((y.name??[])[0])).filter(Boolean);w.warning(k.length>0?e("chatConfig.createAssistant.requiredFields",{fields:[...new Set(k)].join(", ")}):e("chatConfig.createAssistant.requiredFieldsGeneric"));return}w.error(t.message||e("chatConfig.createAssistant.createError"))}finally{r(!1)}},ze=d?.graphDefinition?.subAgents||[],Qt=m==="extend"&&d&&ze.length>0;return(0,n.jsxs)(pe,{title:e("chatConfig.createAssistant.title"),open:i,onCancel:()=>{b(),l()},footer:[(0,n.jsx)(W,{onClick:()=>{b(),l()},children:e("common.cancel")},"cancel"),(0,n.jsx)(W,{type:"primary",icon:m==="extend"?(0,n.jsx)(Pe,{}):m==="learn"?(0,n.jsx)(Je,{}):(0,n.jsx)(Xe,{}),loading:C,disabled:m==="learn"&&(de||!K),onClick:Jt,children:e(m==="extend"?"chatConfig.createAssistant.createExtend":m==="learn"?"chatConfig.createAssistant.startLearning":"common.create")},"submit")],width:760,children:[m==="extend"&&(0,n.jsxs)("div",{style:{marginBottom:20},children:[(0,n.jsx)(ne,{type:"secondary",style:{display:"block",marginBottom:8},children:e("chatConfig.createAssistant.parentAgent")}),(0,n.jsx)(Lt,{value:d?.id,onSelect:$t}),d&&(0,n.jsx)(ne,{type:"secondary",style:{display:"block",marginTop:4,fontSize:12},children:e("chatConfig.createAssistant.inheritHint")})]}),m==="extend"&&d&&(0,n.jsx)(Be,{style:{margin:"12px 0"}}),m==="learn"&&(0,n.jsxs)("div",{children:[(0,n.jsx)(ne,{style:{display:"block",marginBottom:8},children:e("chatConfig.createAssistant.learnHint")}),(0,n.jsx)(V.TextArea,{rows:3,value:M,onChange:t=>H(t.target.value),placeholder:e("chatConfig.createAssistant.learnGoalPlaceholder")}),K&&(0,n.jsxs)("div",{style:{marginTop:12},children:[(0,n.jsxs)("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:12,marginBottom:8},children:[(0,n.jsx)(ne,{type:"secondary",children:e("chatConfig.createAssistant.referenceFiles")}),(0,n.jsx)(_e,{project:K,projects:Q,value:K?.id??null,onChange:Ae,loading:de})]}),(0,n.jsx)(Tt,{projectId:K.id,value:be,onChange:ke}),(0,n.jsx)(ne,{type:"secondary",style:{display:"block",marginTop:6,fontSize:12},children:e("chatConfig.createAssistant.referenceFilesHint")})]}),!K&&!de&&(0,n.jsx)("div",{style:{marginTop:12},children:(0,n.jsx)(_e,{project:K,projects:Q,value:null,onChange:Ae,loading:de})})]}),m!=="learn"&&(0,n.jsxs)(R,{form:o,layout:"vertical",autoComplete:"off",initialValues:{type:"react",agentCardProtocol:"https://",a2aMode:"remote"},children:[(0,n.jsx)(R.Item,{name:"key",label:e("chatConfig.createAssistant.field.key"),rules:[{required:!0,message:e("chatConfig.createAssistant.validation.keyRequired")},{pattern:/^[a-zA-Z][a-zA-Z0-9_-]*$/,message:e("chatConfig.createAssistant.validation.keyPattern")}],style:{marginBottom:16},children:(0,n.jsx)(V,{placeholder:e("chatConfig.createAssistant.keyPlaceholder")})}),(0,n.jsx)(R.Item,{name:"name",label:e("chatConfig.createAssistant.field.name"),rules:[{required:!0,message:e("chatConfig.createAssistant.validation.nameRequired")}],style:{marginBottom:16},children:(0,n.jsx)(V,{placeholder:e("chatConfig.createAssistant.namePlaceholder")})}),(0,n.jsx)(R.Item,{name:"description",label:e("chatConfig.createAssistant.field.description"),rules:[{required:!0,message:e("chatConfig.createAssistant.validation.descriptionRequired")}],style:{marginBottom:16},children:(0,n.jsx)(_t,{placeholder:e("chatConfig.createAssistant.descriptionPlaceholder"),autoSize:{minRows:2,maxRows:4}})}),(0,n.jsx)(R.Item,{name:"type",label:e("chatConfig.createAssistant.field.agentType"),rules:[{required:!0}],style:{marginBottom:16},children:(0,n.jsx)(Ue,{options:[{value:"react",label:e("chatConfig.agentType.executor")},{value:"deep_agent",label:e("chatConfig.agentType.orchestrator")},{value:"processing",label:e("chatConfig.agentType.workflow")},{value:"a2a_remote",label:e("chatConfig.agentType.a2aRemote")}],block:!0})}),(0,n.jsx)(ne,{type:"secondary",style:{fontSize:12,display:"block",marginTop:-12,marginBottom:16,lineHeight:1.5},children:e(on[$]??"chatConfig.createAssistant.typeDesc.react")}),j&&(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(R.Item,{label:e("chatConfig.createAssistant.field.connectionMode"),style:{marginBottom:16},children:(0,n.jsxs)(Fe.Group,{value:a,onChange:t=>{let s=t.target.value;g(s),o.setFieldValue("a2aMode",s),s==="local"&&o.setFieldValue("agentCardProtocol","http://"),fe(void 0)},optionType:"button",buttonStyle:"solid",children:[(0,n.jsx)(Fe.Button,{value:"remote",children:e("chatConfig.createAssistant.mode.remoteUrl")}),(0,n.jsx)(Fe.Button,{value:"local",children:e("chatConfig.createAssistant.mode.localManaged")})]})}),(0,n.jsx)(R.Item,{name:"a2aMode",noStyle:!0,hidden:!0,children:(0,n.jsx)(V,{})}),a==="local"&&(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(R.Item,{label:e("chatConfig.createAssistant.field.template"),style:{marginBottom:16},children:(0,n.jsx)(xe,{placeholder:e("chatConfig.createAssistant.templatePlaceholder"),allowClear:!0,loading:Se,value:U,onChange:t=>{fe(t),t&&Y(!1)},options:le.map(t=>({value:t.key,label:t.name||t.key})),notFoundContent:Se?void 0:(0,n.jsx)(ne,{type:"secondary",style:{padding:8,display:"block"},children:e("chatConfig.createAssistant.noTemplates")})})}),!U&&(0,n.jsx)(n.Fragment,{children:De?(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(R.Item,{name:"commandExecutable",label:e("chatConfig.createAssistant.field.executable"),rules:[{required:!0,whitespace:!0,message:e("chatConfig.createAssistant.validation.executableRequired")}],style:{marginBottom:16},children:(0,n.jsx)(V,{placeholder:e("chatConfig.createAssistant.executablePlaceholder")})}),(0,n.jsx)(R.Item,{name:"commandArgs",label:e("chatConfig.createAssistant.field.arguments"),style:{marginBottom:16},children:(0,n.jsx)(V,{placeholder:e("chatConfig.createAssistant.argumentsPlaceholder")})}),(0,n.jsx)(R.Item,{name:["command","cwd"],label:e("chatConfig.createAssistant.field.workingDirectory"),style:{marginBottom:16},children:(0,n.jsx)(V,{placeholder:e("chatConfig.createAssistant.workingDirectoryPlaceholder")})}),(0,n.jsx)(W,{type:"link",onClick:()=>{Y(!1),o.setFieldsValue({commandExecutable:"",commandArgs:"",command:{cwd:void 0}})},style:{padding:0,marginBottom:16,display:"block"},children:e("chatConfig.createAssistant.hideCustomCommand")})]}):(0,n.jsx)(W,{type:"link",onClick:()=>Y(!0),style:{padding:0,marginBottom:16,display:"block"},children:e("chatConfig.createAssistant.customCommand")})})]}),(0,n.jsx)(R.Item,{label:e("chatConfig.createAssistant.field.agentCardUrl"),required:!0,style:{marginBottom:16},children:(0,n.jsxs)(He.Compact,{style:{width:"100%"},children:[(0,n.jsx)(R.Item,{name:"agentCardProtocol",noStyle:!0,children:(0,n.jsx)(xe,{style:{width:100},options:an})}),(0,n.jsx)(R.Item,{name:"agentCardUrl",noStyle:!0,rules:[{required:!0,message:e("chatConfig.createAssistant.validation.urlRequired")}],children:(0,n.jsx)(V,{placeholder:a==="local"?"localhost:4301":"host:port or full URL"})}),(0,n.jsx)(W,{icon:(0,n.jsx)(Ye,{}),onClick:ve,loading:oe,children:e("chatConfig.connection.action.test")})]})}),a==="remote"&&(0,n.jsx)(R.Item,{name:"apiKey",label:e("chatConfig.createAssistant.field.apiKeyOptional"),style:{marginBottom:16},children:(0,n.jsx)(V.Password,{placeholder:e("chatConfig.createAssistant.apiKeyPlaceholder")})}),(0,n.jsx)(R.Item,{name:"timeout",label:e("chatConfig.createAssistant.field.timeout"),style:{marginBottom:16},initialValue:3e5,children:(0,n.jsx)(qe,{min:1e3,max:36e5,style:{width:"100%"},placeholder:"300000"})})]}),!j&&(0,n.jsx)(R.Item,{name:"prompt",label:e(m==="extend"&&d?"chatConfig.createAssistant.field.systemPromptInherit":"chatConfig.createAssistant.field.systemPrompt"),rules:m==="scratch"?[{required:!0,message:e("chatConfig.createAssistant.validation.systemPromptRequired")}]:[],children:(0,n.jsx)(_t,{placeholder:e(m==="extend"&&d?"chatConfig.createAssistant.systemPromptInheritPlaceholder":"chatConfig.createAssistant.systemPromptPlaceholder"),autoSize:{minRows:4,maxRows:8},style:{fontFamily:"monospace",fontSize:13,lineHeight:1.6}})})]}),Qt&&(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(Be,{style:{margin:"12px 0"}}),(0,n.jsx)(ne,{strong:!0,style:{display:"block",marginBottom:8},children:e("chatConfig.createAssistant.subAgents")}),(0,n.jsx)(Ot,{subAgentKeys:ze,decisions:P,onChange:D})]})]})};var O=I(E()),{Paragraph:ln,Text:qt}=X,cn=[{key:"manual",icon:Te,titleKey:"governance.creation.manual.title",descriptionKey:"governance.creation.manual.description"},{key:"external",icon:tt,titleKey:"governance.creation.external.title",descriptionKey:"governance.creation.external.description"},{key:"learn",icon:at,titleKey:"governance.creation.learn.title",descriptionKey:"governance.creation.learn.description"}],Ht=({open:i,onCancel:l,onSelectMethod:f,focusTriggerAfterClose:c=!0})=>{let u=z();return(0,O.jsx)(pe,{title:u("governance.creation.title"),open:i,onCancel:l,footer:null,width:560,centered:!0,destroyOnHidden:!0,focusTriggerAfterClose:c,children:(0,O.jsxs)("div",{style:{padding:"8px 4px 4px"},children:[(0,O.jsx)(ln,{style:{color:"#595959",fontSize:15,margin:"8px 0 24px"},children:u("governance.creation.subtitle")}),(0,O.jsx)("div",{style:{display:"flex",flexDirection:"column",gap:12},children:cn.map(o=>{let e=o.icon,C=u(o.titleKey);return(0,O.jsxs)(W,{type:"default",onClick:()=>f(o.key),"aria-label":C,style:{display:"flex",flexDirection:"row",alignItems:"center",gap:16,width:"100%",minHeight:88,height:"auto",padding:16,borderRadius:16,textAlign:"left",whiteSpace:"normal"},children:[(0,O.jsx)("div",{style:{width:40,height:40,borderRadius:12,display:"flex",alignItems:"center",justifyContent:"center",flexShrink:0,color:"#6366f1",background:"#eeeeff"},children:(0,O.jsx)(e,{size:19})}),(0,O.jsxs)("div",{style:{minWidth:0},children:[(0,O.jsx)(qt,{strong:!0,style:{display:"block",fontSize:15,marginBottom:3},children:C}),(0,O.jsx)(qt,{style:{color:"#737373",lineHeight:1.5},children:u(o.descriptionKey)})]})]},o.key)})}),(0,O.jsx)("div",{style:{display:"flex",justifyContent:"flex-end",gap:8,flexWrap:"wrap",marginTop:24},children:(0,O.jsx)(W,{onClick:l,children:u("governance.creation.notNow")})})]})})};var _=I(E()),dn=i=>{let l=i;if(typeof i=="string")try{l=JSON.parse(i)}catch{return{}}return l!==null&&typeof l=="object"&&!Array.isArray(l)?l:{}},ka=()=>{let{get:i,del:l,put:f}=me(),c=z(),[u,o]=(0,h.useState)([]),[e,C]=(0,h.useState)(!1),[r,m]=(0,h.useState)(null),[S,M]=(0,h.useState)("graph"),[H,d]=(0,h.useState)(null),[v,P]=(0,h.useState)(null),[D,$]=(0,h.useState)(null),[j,a]=(0,h.useState)(!1),[g,U]=(0,h.useState)(!0),[fe,le]=(0,h.useState)(!1),[ye,Se]=(0,h.useState)("learn"),[ce,ae]=(0,h.useState)({}),[oe,ie]=(0,h.useState)(null),[De,Y]=(0,h.useState)(!1),N=(0,h.useCallback)(async()=>{C(!0);try{let p=await i("/api/assistants");p.success&&p.data?.records&&o(p.data.records)}catch{w.error(c("governance.workbench.loadFailed"))}finally{C(!1)}},[i,c]);(0,h.useEffect)(()=>{N()},[N]);let J=h.default.useMemo(()=>xt(u),[u]),he=u.find(p=>p.id===r)??null,Me=(0,h.useCallback)((p,b="graph")=>{m(p),M(b),d(null),$(null)},[]),Ce=ut(),Q=(0,h.useCallback)((p,b)=>{let x=u.find(q=>q.id===p)?.name??c("governance.workbench.agentFallback");Ce({component_key:"agent_detail_side_app",data:{assistantId:p,initialTab:b},message:x})},[u,Ce,c]),de=(0,h.useCallback)(()=>{U(!0),a(!0)},[]),Ee=(0,h.useCallback)(p=>{let x={manual:{mode:"scratch",values:{}},external:{mode:"scratch",values:{type:"a2a_remote"}},learn:{mode:"learn",values:{}}}[p];Se(x.mode),ae(x.values),U(!1),a(!1),le(!0)},[]),Ae=(0,h.useCallback)(async(p,b)=>{let x=dn(p.graphDefinition),q;try{q=await f(`/api/assistants/${p.id}`,{name:b.name!==void 0?b.name:p.name,description:b.description!==void 0?b.description:p.description,graphDefinition:{...x,...b}})}catch(ve){throw w.error(c("governance.detail.saveFailed")),ve}if(!q.success)throw w.error(c("governance.detail.saveFailed")),new Error("Failed to save agent");w.success(c("common.saved")),N()},[f,N,c]),Ke=(0,h.useCallback)(p=>{o(b=>b.map(x=>x.id===p.id?p:x))},[]),K=(0,h.useCallback)(p=>{ie(p)},[]),be=(0,h.useCallback)(async()=>{if(oe){Y(!0);try{(await l(`/api/assistants/${oe}`)).success?(w.success(c("governance.detail.deleted")),o(b=>b.filter(x=>x.id!==oe)),N(),ie(null)):w.error(c("governance.detail.deleteFailed"))}catch{w.error(c("governance.detail.deleteFailed"))}finally{Y(!1)}}},[oe,l,N,c]),ke=(0,h.useCallback)(async p=>{if(!(await l(`/api/assistants/${p}`)).success)throw new Error("Failed to delete agent");w.success(c("governance.detail.deleted")),o(x=>x.filter(q=>q.id!==p)),N()},[l,N,c]);return(0,_.jsxs)("div",{style:{height:"100%",background:"#fafafa",overflow:"hidden"},children:[he?(0,_.jsx)(kt,{defaultProjectId:D??void 0,children:(0,_.jsx)(Rt,{assistant:he,allAssistants:u,onBack:()=>m(null),onDeleted:ke,onSave:Ae,onAssistantUpdated:Ke,onAgentListInvalidated:N,initialTab:S,initialMessage:H,initialMessageCustomRunConfig:v,initialProjectId:D})},he.id):(0,_.jsx)(_.Fragment,{children:(0,_.jsx)(Bt,{items:J,assistants:u,loading:e,onItemClick:p=>Q(p,"graph"),onOpenPlayground:p=>Q(p,"playground"),onOpenEval:p=>Q(p,"eval"),onCreate:de,onDelete:K})}),(0,_.jsx)(Ht,{open:j,onCancel:()=>{U(!0),a(!1)},onSelectMethod:Ee,focusTriggerAfterClose:g}),(0,_.jsx)(Ut,{open:fe,initialMode:ye,initialValues:ce,onCancel:()=>le(!1),onSuccess:(p,b)=>{le(!1),o(x=>x.some(ve=>ve.id===p.id)?x:[p,...x]),Me(p.id,"graph"),d(b?.initialMessage??null),P(b?.initialMessageCustomRunConfig??null),$(b?.projectId??null),N()}}),(0,_.jsx)(pe,{title:c("governance.deleteAgent"),open:oe!==null,onCancel:()=>ie(null),onOk:be,okText:c("common.delete"),okButtonProps:{danger:!0,loading:De},cancelText:c("common.cancel"),children:(0,_.jsx)("p",{children:c("governance.delete.irreversible")})})]})};export{ka as AgentGovernanceWorkbench};
|