@axiom-lattice/gateway 8.2.0 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/{A2AAuthService-7UGQ7GMW.mjs → A2AAuthService-UKN53ZGP.mjs} +2 -2
  2. package/dist/{a2a-BHBLACJV.mjs → a2a-TDZ3TT6H.mjs} +3 -3
  3. package/dist/{a2a-standard-VL5I7B4W.mjs → a2a-standard-INVCUSAW.mjs} +39 -2
  4. package/dist/a2a-standard-INVCUSAW.mjs.map +1 -0
  5. package/dist/chunk-A7YNAU5L.mjs +377 -0
  6. package/dist/chunk-A7YNAU5L.mjs.map +1 -0
  7. package/dist/{chunk-3XIIQ6EK.mjs → chunk-BOPNXTMO.mjs} +66 -5
  8. package/dist/chunk-BOPNXTMO.mjs.map +1 -0
  9. package/dist/{chunk-L6MBEEII.mjs → chunk-JGQURU44.mjs} +24 -6
  10. package/dist/chunk-JGQURU44.mjs.map +1 -0
  11. package/dist/{chunk-GPHOYR3X.mjs → chunk-LCM4OQTI.mjs} +30 -1
  12. package/dist/chunk-LCM4OQTI.mjs.map +1 -0
  13. package/dist/{chunk-HVIKCUOF.mjs → chunk-MFWTGNQR.mjs} +74 -5
  14. package/dist/chunk-MFWTGNQR.mjs.map +1 -0
  15. package/dist/{chunk-HYYLYIMH.mjs → chunk-OBODBHIS.mjs} +2 -2
  16. package/dist/chunk-T2HIWZGP.mjs +580 -0
  17. package/dist/chunk-T2HIWZGP.mjs.map +1 -0
  18. package/dist/chunk-Y7OVVPVU.mjs +13 -0
  19. package/dist/chunk-Y7OVVPVU.mjs.map +1 -0
  20. package/dist/{chunk-GL7RV5L3.mjs → chunk-YR32MWEJ.mjs} +36 -73
  21. package/dist/chunk-YR32MWEJ.mjs.map +1 -0
  22. package/dist/embed/{embed-page-AgentDetailSideApp-O6G4NJ7I.css → embed-page-AgentDetailSideApp-JMJOOC4V.css} +1 -1
  23. package/dist/embed/{embed-page-AgentDetailSideApp-WDDE7BEH.js → embed-page-AgentDetailSideApp-TQEJK6ZF.js} +1 -1
  24. package/dist/embed/{embed-page-AgentGovernanceWorkbench-SFZNVMUH.css → embed-page-AgentGovernanceWorkbench-3T3ZFJI5.css} +1 -1
  25. package/dist/embed/{embed-page-AgentGovernanceWorkbench-7YPU3QB2.js → embed-page-AgentGovernanceWorkbench-II2MRGCG.js} +1 -1
  26. package/dist/embed/{embed-page-LangfuseDashboard-BEOJDMWF.js → embed-page-LangfuseDashboard-7OANGQXY.js} +1 -1
  27. package/dist/embed/embed-page-MessageGraph-KJPQWTHE.js +6 -0
  28. package/dist/embed/{embed-page-TopologyRuntimeView-CWOXLVFW.css → embed-page-MessageGraph-UDC6GQA5.css} +1 -1
  29. package/dist/embed/{embed-page-ReactChart-D7RZQB2H.js → embed-page-ReactChart-2CAIM4JD.js} +1 -1
  30. package/dist/embed/{embed-page-ReactChart-MXGF35OQ.css → embed-page-ReactChart-URBKYQYE.css} +1 -1
  31. package/dist/embed/{embed-page-SkillFlow-3X24ILKU.js → embed-page-SkillFlow-243MXRXC.js} +1 -1
  32. package/dist/embed/{embed-page-TasksSideApp-KD4S22SL.js → embed-page-TasksSideApp-OA7CULHS.js} +1 -1
  33. package/dist/embed/{embed-page-TasksSideApp-HV6DKQ26.css → embed-page-TasksSideApp-TTCP4ENP.css} +1 -1
  34. package/dist/embed/{embed-page-ThreadMaintenancePanel-V327PV5R.js → embed-page-ThreadMaintenancePanel-Z4424X3J.js} +1 -1
  35. package/dist/embed/{embed-page-MessageGraph-UT2TCPSO.css → embed-page-TopologyRuntimeView-S4AMB4RI.css} +1 -1
  36. package/dist/embed/{embed-page-TopologyRuntimeView-Z7MY6U5T.js → embed-page-TopologyRuntimeView-THTCZUS7.js} +1 -1
  37. package/dist/embed/{embed-page-WorkflowAutomationView-YQFB3CTV.css → embed-page-WorkflowAutomationView-FWVCQ3YM.css} +1 -1
  38. package/dist/embed/{embed-page-WorkflowAutomationView-4MY4HG7R.js → embed-page-WorkflowAutomationView-YDIDG35E.js} +1 -1
  39. package/dist/embed/{embed-page-chunk-4XKCMM5C.js → embed-page-chunk-7ULTMRM2.js} +1 -1
  40. package/dist/embed/{embed-page-chunk-GSUSX54K.js → embed-page-chunk-FE6KYRCO.js} +1 -1
  41. package/dist/embed/{embed-page-chunk-BSRASK7V.js → embed-page-chunk-FZX4A32D.js} +5 -5
  42. package/dist/embed/{embed-page-chunk-PMOGSY6W.js → embed-page-chunk-JMP7EQEF.js} +1 -1
  43. package/dist/embed/{embed-page-chunk-EM6VGUZ2.js → embed-page-chunk-L2EGVVJS.js} +1 -1
  44. package/dist/embed/{embed-page-chunk-HZZ5HNTF.js → embed-page-chunk-LDQGH44H.js} +2 -2
  45. package/dist/embed/{embed-page-chunk-BZKAPY7M.js → embed-page-chunk-OMN6G52V.js} +1 -1
  46. package/dist/embed/{embed-page-chunk-G5MXFDNZ.js → embed-page-chunk-RKQJBKN2.js} +1 -1
  47. package/dist/embed/{embed-page-chunk-G5PDHDBZ.js → embed-page-chunk-VYS3TAK4.js} +1 -1
  48. package/dist/embed/{embed-page-chunk-ASEIOK7X.js → embed-page-chunk-W5OP2CZG.js} +2 -2
  49. package/dist/embed/embed-page-chunk-Z6KWW64I.js +6837 -0
  50. package/dist/embed/{embed-page-en-E35GE7TA.js → embed-page-en-D4M3DPFT.js} +1 -1
  51. package/dist/embed/{embed-page-en-XJYWUJEL.js → embed-page-en-DNDGCPMA.js} +1 -1
  52. package/dist/embed/embed-page-en-H5NOZEKE.js +1 -0
  53. package/dist/embed/{embed-page-en-HDSVQ4O6.js → embed-page-en-QJRX2NQE.js} +1 -1
  54. package/dist/embed/{embed-page-zh-6EBUEFKP.js → embed-page-zh-2H4EB3F3.js} +1 -1
  55. package/dist/embed/embed-page-zh-4C2K3RH2.js +1 -0
  56. package/dist/embed/{embed-page-zh-IR652A6J.js → embed-page-zh-HTEHFCSL.js} +1 -1
  57. package/dist/embed/{embed-page-zh-ZLK3GLM3.js → embed-page-zh-NEXLMDHF.js} +1 -1
  58. package/dist/embed/embed-page.css +1 -1
  59. package/dist/embed/embed-page.js +3 -3
  60. package/dist/embed/mcp-app-page.js +61 -0
  61. package/dist/index.d.mts +29 -0
  62. package/dist/index.d.ts +29 -0
  63. package/dist/index.js +2609 -1491
  64. package/dist/index.js.map +1 -1
  65. package/dist/index.mjs +174 -705
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/mcp-app-shares-SHPBJQXS.mjs +300 -0
  68. package/dist/mcp-app-shares-SHPBJQXS.mjs.map +1 -0
  69. package/dist/mcp-apps-QVUK6UBA.mjs +26 -0
  70. package/dist/{open-7ED27I5B.mjs → open-27ZPLBIW.mjs} +101 -18
  71. package/dist/open-27ZPLBIW.mjs.map +1 -0
  72. package/dist/{open-catalog-SZQ435AY.mjs → open-catalog-NVGP6SQP.mjs} +38 -7
  73. package/dist/open-catalog-NVGP6SQP.mjs.map +1 -0
  74. package/dist/{open-exposure-I3DN2X4E.mjs → open-exposure-BPLYUIX6.mjs} +4 -4
  75. package/dist/{open-files-V3R2BFTV.mjs → open-files-JPB2NBL6.mjs} +4 -4
  76. package/dist/{open-session-2Y7XJJW2.mjs → open-session-CLZT3HVG.mjs} +5 -5
  77. package/dist/open-session-CLZT3HVG.mjs.map +1 -0
  78. package/dist/openLattice-D5YTSZBY.mjs +28 -0
  79. package/dist/{registerExposedPlugins-6GCOE64C.mjs → registerExposedPlugins-2JGXSMAL.mjs} +4 -3
  80. package/dist/{resources-DAU5ZIPB.mjs → resources-YD5DOPGD.mjs} +83 -3
  81. package/dist/resources-YD5DOPGD.mjs.map +1 -0
  82. package/dist/{sandboxOpenCredentialProvider-AT227D4N.mjs → sandboxOpenCredentialProvider-RTYOYRCT.mjs} +19 -14
  83. package/dist/sandboxOpenCredentialProvider-RTYOYRCT.mjs.map +1 -0
  84. package/package.json +10 -8
  85. package/dist/a2a-standard-VL5I7B4W.mjs.map +0 -1
  86. package/dist/chunk-3XIIQ6EK.mjs.map +0 -1
  87. package/dist/chunk-GL7RV5L3.mjs.map +0 -1
  88. package/dist/chunk-GPHOYR3X.mjs.map +0 -1
  89. package/dist/chunk-GXZFO3VU.mjs +0 -42
  90. package/dist/chunk-GXZFO3VU.mjs.map +0 -1
  91. package/dist/chunk-HVIKCUOF.mjs.map +0 -1
  92. package/dist/chunk-L6MBEEII.mjs.map +0 -1
  93. package/dist/embed/embed-page-MessageGraph-LNNMCH6I.js +0 -6
  94. package/dist/embed/embed-page-chunk-CFFDP526.js +0 -6835
  95. package/dist/embed/embed-page-en-GLT2HEZZ.js +0 -1
  96. package/dist/embed/embed-page-zh-XMEJPDMF.js +0 -1
  97. package/dist/open-7ED27I5B.mjs.map +0 -1
  98. package/dist/open-catalog-SZQ435AY.mjs.map +0 -1
  99. package/dist/open-session-2Y7XJJW2.mjs.map +0 -1
  100. package/dist/openLattice-Q6M5S6CY.mjs +0 -18
  101. package/dist/openRateLimiter-XEOL3A5G.mjs +0 -8
  102. package/dist/resources-DAU5ZIPB.mjs.map +0 -1
  103. package/dist/sandboxOpenCredentialProvider-AT227D4N.mjs.map +0 -1
  104. /package/dist/{A2AAuthService-7UGQ7GMW.mjs.map → A2AAuthService-UKN53ZGP.mjs.map} +0 -0
  105. /package/dist/{a2a-BHBLACJV.mjs.map → a2a-TDZ3TT6H.mjs.map} +0 -0
  106. /package/dist/{chunk-HYYLYIMH.mjs.map → chunk-OBODBHIS.mjs.map} +0 -0
  107. /package/dist/{openLattice-Q6M5S6CY.mjs.map → mcp-apps-QVUK6UBA.mjs.map} +0 -0
  108. /package/dist/{open-exposure-I3DN2X4E.mjs.map → open-exposure-BPLYUIX6.mjs.map} +0 -0
  109. /package/dist/{open-files-V3R2BFTV.mjs.map → open-files-JPB2NBL6.mjs.map} +0 -0
  110. /package/dist/{openRateLimiter-XEOL3A5G.mjs.map → openLattice-D5YTSZBY.mjs.map} +0 -0
  111. /package/dist/{registerExposedPlugins-6GCOE64C.mjs.map → registerExposedPlugins-2JGXSMAL.mjs.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/routes/mcp-apps.ts","../src/services/mcp-ui/UiResourceResolver.ts","../src/services/mcp-ui/McpUiResourceCacheStore.ts","../src/services/mcp-ui/buildCsp.ts","../src/services/mcp-ui/sandboxPage.ts","../src/services/plugins/tenantPluginResolver.ts"],"sourcesContent":["/**\n * MCP App routes.\n *\n * Exposes the two thin HTTP seams the sandboxed MCP App frame needs: resolving a\n * UI resource and proxying a whitelisted JSON-RPC envelope to the owning MCP\n * server. The pure handler factory keeps the security-relevant decisions\n * (ref parsing, method allow-list, plugin tool scoping) framework-agnostic and\n * unit-testable; {@link registerMcpAppRoutes} only translates HTTP to those\n * handlers.\n */\n\nimport type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport {\n parseMcpUiRef,\n type McpUiRef,\n type McpUiResourcePayload,\n type Plugin,\n type SandboxPluginManifest,\n} from \"@axiom-lattice/protocols\";\nimport {\n getSandboxPluginCatalog,\n mcpManager,\n resolvePluginConnections,\n verifyMcpUiScope,\n type SandboxPluginInvokeArgs,\n} from \"@axiom-lattice/core\";\nimport { UiResourceResolver } from \"../services/mcp-ui/UiResourceResolver\";\nimport { InMemoryMcpUiResourceCacheStore } from \"../services/mcp-ui/McpUiResourceCacheStore\";\nimport { buildCsp } from \"../services/mcp-ui/buildCsp\";\nimport { SANDBOX_HTML } from \"../services/mcp-ui/sandboxPage\";\nimport { resolveTenantPluginByType } from \"../services/plugins/tenantPluginResolver\";\nimport {\n invokePluginTool,\n PluginToolScopeError,\n} from \"../services/plugins/pluginToolExecution\";\n\nconst SANDBOX_HTML_CONTENT_TYPE = \"text/html; charset=utf-8\";\n\n// Re-exported so existing importers keep resolving the error from this module;\n// the class itself now lives in the shared plugin tool execution helper.\nexport { PluginToolScopeError };\n\n/**\n * Thrown by the plugin backend for MCP methods the plugin surface does not\n * support (Plan 2 serves UI resources and the plugin's own tool calls only).\n * {@link makeMcpAppHandlers} maps it to 403.\n */\nexport class PluginMethodNotSupportedError extends Error {\n /**\n * @param method - Unsupported JSON-RPC method.\n */\n constructor(method: string) {\n super(`plugin backend does not support ${method}`);\n this.name = \"PluginMethodNotSupportedError\";\n }\n}\n\n/**\n * Client-facing scope denial from the plugin tool path.\n *\n * `status`/`code` are stable so the MCP App host can surface a precise error\n * instead of a generic 502.\n */\nexport class McpAppScopeError extends Error {\n /**\n * @param status - HTTP status to return.\n * @param code - Stable machine-readable denial code.\n */\n constructor(\n readonly status: 403 | 409,\n readonly code:\n | \"SCOPE_INVALID\"\n | \"SCOPE_MISMATCH\"\n | \"CONNECTION_REQUIRED\"\n | \"CONNECTION_NOT_ALLOWED\",\n ) {\n super(code);\n this.name = \"McpAppScopeError\";\n }\n}\n\n/** Result envelope returned by the pure handlers. */\nexport interface McpAppHandlerResult<T = unknown> {\n status: number;\n body: T;\n}\n\n/**\n * Request-scoped run context forwarded from the console request to plugin\n * execution.\n *\n * Plugin handlers run in a project-isolated sandbox whose path layout is\n * `/project/<plugin>/...`; without the workspace/project the executor resolves a\n * tenant-level sandbox and tool calls read the wrong (empty) files.\n */\nexport interface McpAppRunContext {\n tenantId: string;\n workspaceId?: string;\n projectId?: string;\n}\n\n/** Injectable seams for scope verification/connection resolution in tests. */\nexport interface McpAppScopeDeps {\n verifyScope?: typeof verifyMcpUiScope;\n resolveConnections?: typeof resolvePluginConnections;\n}\n\n/** Log and build a scope denial. */\nfunction denyScope(\n status: 403 | 409,\n code: McpAppScopeError[\"code\"],\n detail: Record<string, unknown>,\n): McpAppScopeError {\n console.warn(JSON.stringify({ event: \"mcp-apps:scope-denied\", ...detail, code }));\n return new McpAppScopeError(status, code);\n}\n\n/** Dependencies injected into the route handlers. */\nexport interface McpAppRouteDeps {\n resolveUiResource: (tenantId: string, ref: McpUiRef) => Promise<McpUiResourcePayload>;\n callTool: (\n tenantId: string,\n ref: McpUiRef,\n name: string,\n args: Record<string, unknown>,\n context?: McpAppRunContext,\n ) => Promise<unknown>;\n readResource: (tenantId: string, ref: McpUiRef, uri: string) => Promise<unknown>;\n listTools: (tenantId: string, ref: McpUiRef) => Promise<unknown>;\n}\n\n/**\n * Arguments the plugin backend forwards to the sandbox executor.\n *\n * Aliased to core's executor contract so the shared {@link invokePluginTool}\n * seam cannot drift from the real executor.\n */\nexport type McpAppPluginInvokeArgs = SandboxPluginInvokeArgs;\n\n/** Normalized sandbox handler outcome consumed by the plugin backend. */\nexport interface McpAppPluginInvokeResult {\n ok: boolean;\n result?: unknown;\n error?: string;\n}\n\n/** Minimal tenant plugin catalog surface needed by the plugin backend. */\nexport interface McpAppPluginCatalog {\n readPluginFile: (tenantId: string, pluginType: string, relPath: string) => Promise<string>;\n /**\n * Validated (normalized) manifest for the plugin.\n *\n * This is the authoritative source for handler paths and tool caps: it is the\n * output of the manifest validator, not a fresh parse of the raw\n * `plugin.json`, so execution cannot diverge from what validation accepted.\n */\n getValidatedManifest: (tenantId: string, pluginType: string) => Promise<SandboxPluginManifest | undefined>;\n getExecutor: () => {\n invoke: (args: McpAppPluginInvokeArgs) => Promise<McpAppPluginInvokeResult>;\n };\n}\n\n/**\n * Plugin seam injected into {@link resolveMcpAppDeps}: the tenant plugin\n * resolver plus the sandbox catalog/executor. Extracted so tests can supply a\n * fake and exercise the real plugin backend without touching the module\n * singleton.\n */\nexport interface McpAppPluginBackend {\n resolvePlugin: (tenantId: string, type: string) => Promise<Plugin | undefined>;\n catalog: McpAppPluginCatalog;\n}\n\nconst ALLOWED_METHODS = new Set([\"tools/call\", \"tools/list\", \"resources/read\"]);\n\n/** Narrow an unknown value to a plain data record, otherwise `undefined`. */\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/**\n * Build pure, framework-agnostic handlers for the MCP app routes.\n *\n * @param deps Backend proxies.\n * @returns `resource` and `rpc` handlers.\n */\nexport function makeMcpAppHandlers(deps: McpAppRouteDeps) {\n return {\n /**\n * Resolve the `mcp_app` UI resource for a parsed ref.\n *\n * @param tenantId Tenant scope.\n * @param rawRef Untrusted ref payload.\n * @returns The resource payload plus a ready-to-use `cspPolicy` string\n * ({@link buildCsp}, always non-empty), or a 400/502 error envelope.\n */\n async resource(tenantId: string, rawRef: unknown): Promise<McpAppHandlerResult> {\n const ref = parseMcpUiRef(rawRef);\n if (!ref) return { status: 400, body: { error: \"INVALID_REF\" } };\n if (ref.kind === \"plugin\" && ref.pluginType === \"\") {\n return { status: 400, body: { error: \"INVALID_REF\" } };\n }\n try {\n const payload = await deps.resolveUiResource(tenantId, ref);\n return { status: 200, body: { ...payload, cspPolicy: buildCsp(payload.csp) } };\n } catch (error) {\n console.warn(JSON.stringify({\n event: \"mcp-apps:resource-failed\",\n tenantId,\n kind: ref.kind,\n error: error instanceof Error ? error.message : String(error),\n }));\n return { status: 502, body: { error: \"RESOURCE_UNAVAILABLE\" } };\n }\n },\n\n /**\n * Proxy an allow-listed JSON-RPC method for a parsed ref.\n *\n * @param tenantId Tenant scope.\n * @param rawRef Untrusted ref payload.\n * @param method JSON-RPC method name.\n * @param params JSON-RPC params.\n * @param context Request-scoped run context forwarded to `tools/call`.\n * @returns The backend result, or a 400/403/502 error envelope.\n */\n async rpc(\n tenantId: string,\n rawRef: unknown,\n method: string,\n params: Record<string, unknown>,\n context?: McpAppRunContext,\n ): Promise<McpAppHandlerResult> {\n const ref = parseMcpUiRef(rawRef);\n if (!ref) return { status: 400, body: { error: \"INVALID_REF\" } };\n if (!ALLOWED_METHODS.has(method)) return { status: 403, body: { error: \"METHOD_NOT_ALLOWED\" } };\n try {\n if (method === \"tools/call\") {\n const name = typeof params.name === \"string\" ? params.name : \"\";\n if (name === \"\") return { status: 400, body: { error: \"INVALID_PARAMS\" } };\n if (\"pluginType\" in ref && ref.pluginType === \"\") {\n return { status: 400, body: { error: \"INVALID_REF\" } };\n }\n return {\n status: 200,\n body: await deps.callTool(tenantId, ref, name, asRecord(params.arguments) ?? {}, context),\n };\n }\n if (method === \"resources/read\") {\n const uri = typeof params.uri === \"string\" ? params.uri : \"\";\n if (uri === \"\") return { status: 400, body: { error: \"INVALID_PARAMS\" } };\n return { status: 200, body: await deps.readResource(tenantId, ref, uri) };\n }\n return { status: 200, body: { tools: await deps.listTools(tenantId, ref) } };\n } catch (error) {\n if (error instanceof McpAppScopeError) {\n return { status: error.status, body: { error: error.code } };\n }\n if (error instanceof PluginToolScopeError) {\n return { status: 403, body: { error: \"PLUGIN_TOOL_SCOPE\" } };\n }\n if (error instanceof PluginMethodNotSupportedError) {\n return { status: 403, body: { error: \"PLUGIN_METHOD_UNSUPPORTED\" } };\n }\n const context: Record<string, unknown> = { tenantId, kind: ref.kind, method };\n if (method === \"resources/read\") context.uri = typeof params.uri === \"string\" ? params.uri : \"\";\n if (method === \"tools/call\") context.name = typeof params.name === \"string\" ? params.name : \"\";\n console.warn(JSON.stringify({\n event: \"mcp-apps:rpc-failed\",\n ...context,\n error: error instanceof Error ? error.message : String(error),\n }));\n return { status: 502, body: { error: \"RPC_FAILED\" } };\n }\n },\n };\n}\n\n/**\n * Read a plugin UI resource and enrich it with the declaring tool's\n * CSP/permissions/border preferences.\n *\n * Two backends serve plugin UIs:\n * - **Global plugins** declare inline `meta.uiResources[resource]` HTML, keyed\n * by the `ui://` resource itself (the same key a tool's `ui.resource` points\n * at). That HTML is returned directly with no sandbox read and defaults to\n * `text/html;profile=mcp-app`.\n * - **Tenant sandbox plugins** declare a relative `ui/<tool>.html` path; the\n * file is read from the tenant volume and served as `text/html`.\n *\n * In both cases the resource must be declared by a tool of the plugin, so an\n * arbitrary `uiResources` key or volume path cannot be fetched.\n *\n * @param backend Plugin resolver + sandbox catalog seam.\n * @param tenantId Tenant scope.\n * @param pluginType Plugin type addressed by the ref.\n * @param resource Resource key: an inline `ui://` key for global plugins, or a\n * safe relative `.html` path for tenant plugins.\n * @returns The UI resource payload.\n * @throws When the plugin, declaring tool, or file is missing. A resource not\n * declared by any tool is rejected before the catalog is asked to read it.\n *\n * @remarks\n * Tenant plugin files live at the tenant-level `/root/.agents/plugins`, so\n * unlike tool execution this path needs no workspace/project context.\n */\nexport async function fetchPluginUiResource(\n backend: McpAppPluginBackend,\n tenantId: string,\n pluginType: string,\n resource: string,\n): Promise<McpUiResourcePayload> {\n const plugin = await backend.resolvePlugin(tenantId, pluginType);\n if (!plugin) throw new Error(`plugin \"${pluginType}\" not found`);\n const tool = (plugin.meta.tools ?? []).find((t) => t.ui?.resource === resource);\n if (!tool?.ui) throw new Error(`plugin \"${pluginType}\" has no tool declaring ui resource \"${resource}\"`);\n\n const inline = plugin.meta.uiResources?.[resource];\n if (inline) {\n return {\n mimeType: inline.mimeType ?? \"text/html;profile=mcp-app\",\n html: inline.html,\n csp: inline.csp ?? tool.ui.csp,\n permissions: tool.ui.permissions,\n prefersBorder: tool.ui.prefersBorder,\n };\n }\n\n const html = await backend.catalog.readPluginFile(tenantId, pluginType, resource);\n return {\n mimeType: \"text/html\",\n html,\n csp: tool.ui.csp,\n permissions: tool.ui.permissions,\n prefersBorder: tool.ui.prefersBorder,\n };\n}\n\n/**\n * Execute one of a plugin's own tools through the injected sandbox executor.\n *\n * A scope-bearing ref is verified, its connection key is resolved against the\n * tenant Connection Store, and the connection is injected. A scope-less legacy\n * ref keeps the request-context path; a connection-declaring plugin without a\n * scope fails with `CONNECTION_REQUIRED`.\n *\n * @param backend - Plugin resolver + sandbox catalog seam.\n * @param tenantId - Tenant scope.\n * @param ref - Parsed plugin ref.\n * @param name - Tool name requested by the app.\n * @param args - Tool arguments.\n * @param context - Legacy request context for scope-less refs.\n * @param deps - Injectable scope/connection seams for tests.\n * @returns The handler result.\n * @throws McpAppScopeError when the scope is invalid/mismatched or its\n * connection key is disallowed/missing.\n */\nexport async function callPluginTool(\n backend: McpAppPluginBackend,\n tenantId: string,\n ref: Extract<McpUiRef, { kind: \"plugin\" }>,\n name: string,\n args: Record<string, unknown>,\n context?: McpAppRunContext,\n deps?: McpAppScopeDeps,\n): Promise<unknown> {\n const scope = ref.scope ? (deps?.verifyScope ?? verifyMcpUiScope)(ref.scope) : null;\n if (ref.scope && !scope) {\n throw denyScope(403, \"SCOPE_INVALID\", {\n tenantId,\n pluginType: ref.pluginType,\n tool: name,\n });\n }\n if (scope) {\n const mismatched =\n scope.tid !== tenantId ||\n scope.plugin !== ref.pluginType ||\n scope.resource !== ref.resource;\n if (mismatched) {\n throw denyScope(403, \"SCOPE_MISMATCH\", {\n tenantId,\n pluginType: ref.pluginType,\n tool: name,\n jti: scope.jti,\n threadId: scope.thid,\n });\n }\n }\n\n const manifest = await backend.catalog.getValidatedManifest(tenantId, ref.pluginType);\n const rawConnectionKey = args.connectionKey;\n const explicit =\n typeof rawConnectionKey === \"string\" ? rawConnectionKey : undefined;\n const scoped = scope?.ck;\n const connectionRelevant = Boolean(manifest?.connection) || scoped !== undefined;\n if (\n connectionRelevant &&\n (explicit !== undefined\n ? explicit !== scoped\n : rawConnectionKey !== undefined)\n ) {\n throw denyScope(403, \"CONNECTION_NOT_ALLOWED\", {\n tenantId,\n pluginType: ref.pluginType,\n tool: name,\n jti: scope?.jti,\n threadId: scope?.thid,\n });\n }\n\n let connection: Record<string, unknown> | undefined;\n let connectionKey: string | undefined;\n if (scoped !== undefined) {\n const resolveConnections = deps?.resolveConnections ?? resolvePluginConnections;\n const resolved = await resolveConnections(\n ref.pluginType,\n { connections: [scoped] },\n { tenantId },\n );\n if (resolved.length === 0) {\n throw denyScope(403, \"CONNECTION_NOT_ALLOWED\", {\n tenantId,\n pluginType: ref.pluginType,\n tool: name,\n jti: scope?.jti,\n threadId: scope?.thid,\n });\n }\n connection = resolved[0]!.config;\n connectionKey = resolved[0]!.key;\n }\n\n if (manifest?.connection && !connectionKey) {\n throw denyScope(409, \"CONNECTION_REQUIRED\", {\n tenantId,\n pluginType: ref.pluginType,\n tool: name,\n jti: scope?.jti,\n threadId: scope?.thid,\n });\n }\n\n const identity = scope\n ? {\n workspaceId: scope.wid,\n projectId: scope.pid,\n threadId: scope.thid,\n assistantId: scope.aid,\n }\n : {\n workspaceId: context?.workspaceId,\n projectId: context?.projectId,\n };\n\n return invokePluginTool(backend, tenantId, ref.pluginType, name, args, {\n ...identity,\n ...(connection === undefined ? {} : { connection }),\n });\n}\n\n/**\n * Default plugin backend: the shared tenant plugin resolver plus the\n * process-wide sandbox plugin catalog and executor.\n *\n * @returns The live plugin backend.\n */\nfunction createDefaultPluginBackend(): McpAppPluginBackend {\n return {\n resolvePlugin: (tenantId, type) => resolveTenantPluginByType(tenantId, type),\n catalog: {\n readPluginFile: (tenantId, pluginType, relPath) =>\n getSandboxPluginCatalog().readPluginFile(tenantId, pluginType, relPath),\n getValidatedManifest: (tenantId, pluginType) =>\n getSandboxPluginCatalog().getValidatedManifest(tenantId, pluginType),\n getExecutor: () => getSandboxPluginCatalog().getExecutor(),\n },\n };\n}\n\n/**\n * Process-wide UI resource resolver installed by the default route wiring.\n *\n * @remarks The plugin-catalog-change subscriber invalidates a tenant's plugin\n * UI resources through this so an edited `ui/*.html` re-resolves immediately\n * instead of being served from the 5-minute TTL cache.\n */\nlet uiResourceResolverSingleton: UiResourceResolver | undefined;\n\n/**\n * The UI resource resolver used by the live MCP app routes.\n *\n * @returns The resolver installed by {@link resolveMcpAppDeps}, or `undefined`\n * before the routes are registered.\n */\nexport function getMcpUiResourceResolver(): UiResourceResolver | undefined {\n return uiResourceResolverSingleton;\n}\n\n/**\n * Wire the handler deps to the live MCP runtime and tenant plugin backend.\n *\n * @param pluginBackend Plugin seam; defaults to the live catalog/executor.\n * @returns The concrete route dependencies.\n */\nexport function resolveMcpAppDeps(\n pluginBackend: McpAppPluginBackend = createDefaultPluginBackend(),\n): McpAppRouteDeps {\n const uiResourceResolver = new UiResourceResolver({\n store: new InMemoryMcpUiResourceCacheStore(),\n fetchMcp: (tenantId, serverKey, resourceUri) => mcpManager.readUiResource(tenantId, serverKey, resourceUri),\n fetchPlugin: (tenantId, pluginType, resource) =>\n fetchPluginUiResource(pluginBackend, tenantId, pluginType, resource),\n });\n uiResourceResolverSingleton = uiResourceResolver;\n return {\n resolveUiResource: (tenantId, ref) => uiResourceResolver.resolve(tenantId, ref),\n callTool: (tenantId, ref, name, args, context) =>\n ref.kind === \"mcp\"\n ? mcpManager.callServerTool(tenantId, ref.serverKey, name, args)\n : callPluginTool(pluginBackend, tenantId, ref, name, args, context),\n // Plugin refs reach these only after `tools/call`; the handler treats the\n // rejection as an unsupported method (403). The branches are live because\n // the pure handler deliberately delegates method policy to the deps.\n readResource: (tenantId, ref, uri) =>\n ref.kind === \"mcp\"\n ? mcpManager.readServerResource(tenantId, ref.serverKey, uri)\n : Promise.reject(new PluginMethodNotSupportedError(\"resources/read\")),\n listTools: (tenantId, ref) =>\n ref.kind === \"mcp\"\n ? mcpManager.getAvailableTools(tenantId, ref.serverKey)\n : Promise.reject(new PluginMethodNotSupportedError(\"tools/list\")),\n };\n}\n\ntype RequestWithUser = FastifyRequest & { user?: { tenantId?: string } };\n\n/** Read a header as a single value, using the first entry when repeated. */\nfunction firstHeader(request: FastifyRequest, name: string): string | undefined {\n const header = request.headers[name];\n return Array.isArray(header) ? header[0] : header;\n}\n\n/** Resolve the tenant from the console session, then the legacy header. */\nfunction tenantOf(request: FastifyRequest): string {\n const user = (request as RequestWithUser).user;\n return user?.tenantId ?? firstHeader(request, \"x-tenant-id\") ?? \"default\";\n}\n\n/**\n * Build the request-scoped run context from the console request.\n *\n * The console sends the active workspace/project on every API call\n * (`x-workspace-id`/`x-project-id`); forwarding them lets a UI-initiated\n * `tools/call` resolve the same project-isolated sandbox the agent run used.\n */\nfunction runContextOf(request: FastifyRequest): McpAppRunContext {\n return {\n tenantId: tenantOf(request),\n workspaceId: firstHeader(request, \"x-workspace-id\"),\n projectId: firstHeader(request, \"x-project-id\"),\n };\n}\n\n/**\n * Register MCP app routes on the Fastify instance.\n *\n * @param fastify Gateway Fastify instance.\n * @param deps Route dependencies; defaults to live runtime wiring.\n * @remarks Resource/rpc derive `tenantId` from the console session. The sandbox\n * bootstrap page is loaded as an iframe navigation (no `Authorization` header),\n * so `GET /mcp-apps/sandbox.html` is added to the console-auth bypass list; it\n * is a static document carrying no secrets and no identity, while the\n * `/api/mcp-apps/*` seams stay authenticated. The MCP Apps spec prefers the\n * sandbox proxy origin to differ from the console SPA origin (defense-in-depth\n * containment); single-origin deployments are supported, where the outer proxy\n * frame shares the console origin.\n */\nexport function registerMcpAppRoutes(\n fastify: FastifyInstance,\n deps: McpAppRouteDeps = resolveMcpAppDeps(),\n): void {\n const handlers = makeMcpAppHandlers(deps);\n\n fastify.get(\"/mcp-apps/sandbox.html\", async (_request, reply) => {\n return reply\n .header(\"content-type\", SANDBOX_HTML_CONTENT_TYPE)\n .header(\"x-content-type-options\", \"nosniff\")\n .header(\"cache-control\", \"no-store\")\n .send(SANDBOX_HTML);\n });\n\n fastify.post(\"/api/mcp-apps/resource\", async (request, reply) => {\n const body = asRecord(request.body) ?? {};\n const result = await handlers.resource(tenantOf(request), body.ref);\n return reply.code(result.status).send(result.body);\n });\n\n fastify.post(\"/api/mcp-apps/rpc\", async (request, reply) => {\n const body = asRecord(request.body) ?? {};\n const params = asRecord(body.params) ?? {};\n const context = runContextOf(request);\n const result = await handlers.rpc(\n context.tenantId,\n body.ref,\n String(body.method ?? \"\"),\n params,\n context,\n );\n return reply.code(result.status).send(result.body);\n });\n}\n","import { createHash } from \"node:crypto\";\nimport type { McpUiRef, McpUiResourcePayload } from \"@axiom-lattice/protocols\";\nimport type { McpUiCacheEntry, McpUiResourceCacheStore } from \"./McpUiResourceCacheStore\";\n\n/** Dependencies for {@link UiResourceResolver}. */\nexport interface UiResourceResolverDeps {\n store: McpUiResourceCacheStore;\n fetchMcp: (tenantId: string, serverKey: string, resourceUri: string) => Promise<McpUiResourcePayload>;\n fetchPlugin: (tenantId: string, pluginType: string, resource: string) => Promise<McpUiResourcePayload>;\n ttlMs?: number;\n}\n\nconst DEFAULT_TTL_MS = 5 * 60 * 1000;\n\n/** Public payload fields, excluding internal cache metadata (`hash`/`fetchedAt`/`stale`). */\ntype McpUiPublicFields = Omit<McpUiResourcePayload, \"stale\">;\n\nfunction publicFieldsOf(payload: McpUiResourcePayload): McpUiPublicFields {\n return {\n mimeType: payload.mimeType,\n html: payload.html,\n ...(payload.csp !== undefined ? { csp: payload.csp } : {}),\n ...(payload.permissions !== undefined ? { permissions: payload.permissions } : {}),\n ...(payload.prefersBorder !== undefined ? { prefersBorder: payload.prefersBorder } : {}),\n };\n}\n\n/**\n * Resolve `mcp_app` refs from either the MCP or plugin backend, with a\n * stale-while-error cache.\n *\n * @remarks Concurrent resolves for the same tenant/ref share a single backend\n * fetch (single-flight). On backend failure, a cached entry (even expired) is\n * returned with `stale: true` instead of throwing.\n */\nexport class UiResourceResolver {\n private readonly inFlight = new Map<string, Promise<McpUiResourcePayload>>();\n\n constructor(private deps: UiResourceResolverDeps) {}\n\n private keyOf(ref: McpUiRef): string {\n return ref.kind === \"mcp\"\n ? `mcp\\u0000${ref.serverKey}\\u0000${ref.resourceUri}`\n : `plugin\\u0000${ref.pluginType}\\u0000${ref.resource}`;\n }\n\n private fullKey(tenantId: string, ref: McpUiRef): string {\n return `${tenantId}\\u0000${this.keyOf(ref)}`;\n }\n\n /**\n * Resolve a UI resource for a tenant.\n *\n * @param tenantId Tenant scope.\n * @param ref The fence ref.\n * @returns Public UI resource payload (no cache metadata).\n */\n async resolve(tenantId: string, ref: McpUiRef): Promise<McpUiResourcePayload> {\n const key = this.fullKey(tenantId, ref);\n const ttl = this.deps.ttlMs ?? DEFAULT_TTL_MS;\n const cached = await this.deps.store.get(key);\n if (cached && Date.now() - cached.fetchedAt < ttl) return publicFieldsOf(cached);\n\n const existing = this.inFlight.get(key);\n if (existing) return existing;\n\n const promise = this.fetchAndCache(tenantId, ref, key, cached);\n this.inFlight.set(key, promise);\n try {\n return await promise;\n } finally {\n this.inFlight.delete(key);\n }\n }\n\n /**\n * Evict the cached entry for a tenant/ref.\n *\n * @param tenantId Tenant scope.\n * @param ref The fence ref.\n */\n async invalidate(tenantId: string, ref: McpUiRef): Promise<void> {\n await this.deps.store.delete(this.fullKey(tenantId, ref));\n }\n\n /**\n * Evict every cached plugin UI resource for a tenant.\n *\n * @param tenantId Tenant whose plugin UI resources to drop.\n * @remarks Called when a tenant's plugin catalog changes so an edited\n * `ui/*.html` is re-fetched instead of being served from the TTL cache\n * (which is keyed by ref identity, not content).\n */\n async invalidateTenantPlugins(tenantId: string): Promise<void> {\n await this.deps.store.deleteByPrefix(`${tenantId}\\u0000plugin\\u0000`);\n }\n\n private async fetchAndCache(\n tenantId: string,\n ref: McpUiRef,\n key: string,\n cached: McpUiCacheEntry | undefined,\n ): Promise<McpUiResourcePayload> {\n try {\n const fresh =\n ref.kind === \"mcp\"\n ? await this.deps.fetchMcp(tenantId, ref.serverKey, ref.resourceUri)\n : await this.deps.fetchPlugin(tenantId, ref.pluginType, ref.resource);\n const entry: McpUiCacheEntry = {\n ...publicFieldsOf(fresh),\n hash: createHash(\"sha256\").update(fresh.html).digest(\"hex\"),\n fetchedAt: Date.now(),\n };\n await this.deps.store.set(key, entry);\n return publicFieldsOf(fresh);\n } catch (error) {\n if (cached) return { ...publicFieldsOf(cached), stale: true };\n throw error;\n }\n }\n}\n","import type { McpUiResourcePayload } from \"@axiom-lattice/protocols\";\n\n/** Cache entry with provenance. */\nexport interface McpUiCacheEntry extends McpUiResourcePayload {\n hash: string;\n fetchedAt: number;\n}\n\n/** Persistence seam for resolved UI resources. */\nexport interface McpUiResourceCacheStore {\n get(key: string): Promise<McpUiCacheEntry | undefined>;\n set(key: string, entry: McpUiCacheEntry): Promise<void>;\n delete(key: string): Promise<void>;\n deleteByPrefix(prefix: string): Promise<void>;\n}\n\n/** Process-local cache implementation. */\nexport class InMemoryMcpUiResourceCacheStore implements McpUiResourceCacheStore {\n private map = new Map<string, McpUiCacheEntry>();\n\n async get(key: string): Promise<McpUiCacheEntry | undefined> {\n return this.map.get(key);\n }\n\n async set(key: string, entry: McpUiCacheEntry): Promise<void> {\n this.map.set(key, entry);\n }\n\n async delete(key: string): Promise<void> {\n this.map.delete(key);\n }\n\n async deleteByPrefix(prefix: string): Promise<void> {\n for (const key of this.map.keys()) {\n if (key.startsWith(prefix)) this.map.delete(key);\n }\n }\n}\n","import type { McpUiCsp } from \"@axiom-lattice/protocols\";\n\n// `'wasm-unsafe-eval'` permits WebAssembly compilation (e.g. CesiumJS) without\n// the broader `'unsafe-eval'`; MCP Apps' declared CSP has no wasm field, so the\n// host always grants it. Browsers without support simply ignore the token.\nconst DEFAULT_CSP = [\n \"default-src 'none'\",\n \"script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data:\",\n \"media-src 'self' data:\",\n // Fonts are restricted to the document origin; declared `resourceDomains`\n // are the only origins ever added (no `data:` — a base64-embedded font must\n // be served from a declared origin).\n \"font-src 'self'\",\n \"object-src 'none'\",\n \"connect-src 'none'\",\n \"frame-src 'none'\",\n \"base-uri 'self'\",\n].join(\"; \");\n\n/**\n * A single safe CSP source expression: an explicit `http(s)://` / `ws(s)://`\n * origin (optionally with a leading `*.` wildcard subdomain and optionally with\n * a path). The negated class forbids whitespace and the `;`, `,`, `'`, `\"`\n * characters, and — because `*` is only allowed as the leading label via the\n * `(\\*\\.)?` group — a bare `*` or a non-leading wildcard is rejected too, so a\n * declared value cannot terminate the token or smuggle an extra directive.\n */\nconst ORIGIN_PATTERN = /^(https?|wss?):\\/\\/(\\*\\.)?[^\\s;,'\"*]+$/;\n\n/** Keep only well-formed origin strings, silently dropping junk or injections. */\nfunction declaredOrigins(domains: string[] | undefined): string[] {\n if (!Array.isArray(domains)) return [];\n return domains.filter((domain) => typeof domain === \"string\" && ORIGIN_PATTERN.test(domain));\n}\n\n/**\n * Build a CSP header value from a UI resource's declared origins.\n *\n * @param csp Declared CSP metadata (may be undefined).\n * @returns A CSP string. Never loosens beyond declared origins.\n * @remarks Omitted `csp` yields the restrictive MCP Apps default.\n */\nexport function buildCsp(csp: McpUiCsp | undefined): string {\n if (!csp) return DEFAULT_CSP;\n const resourceOrigins = declaredOrigins(csp.resourceDomains);\n const resource = resourceOrigins.join(\" \");\n // Per MCP Apps, `resourceDomains` covers scripts/styles/fonts as well as\n // images and media, so the same validated origins feed every resource\n // directive. Only declared origins are ever appended.\n const withResourceOrigins = (base: string): string => (resource ? `${base} ${resource}` : base);\n const img = [\"'self'\", \"data:\", ...resourceOrigins].join(\" \");\n const connect = declaredOrigins(csp.connectDomains);\n const frame = declaredOrigins(csp.frameDomains);\n const baseUri = declaredOrigins(csp.baseUriDomains);\n const parts = [\n \"default-src 'none'\",\n withResourceOrigins(\"script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'\"),\n withResourceOrigins(\"style-src 'self' 'unsafe-inline'\"),\n `img-src ${img}`,\n `media-src ${[\"'self'\", \"data:\", ...resourceOrigins].join(\" \")}`,\n withResourceOrigins(\"font-src 'self'\"),\n \"object-src 'none'\",\n `connect-src ${connect.length ? connect.join(\" \") : \"'none'\"}`,\n `frame-src ${frame.length ? frame.join(\" \") : \"'none'\"}`,\n `base-uri ${baseUri.length ? baseUri.join(\" \") : \"'self'\"}`,\n ];\n return parts.join(\"; \");\n}\n","/**\n * Static bootstrap document for the MCP App double-iframe sandbox proxy.\n *\n * The MCP Apps spec prefers the host and the sandbox proxy to be cross-origin\n * (defense-in-depth), so this page is served from the gateway origin; the\n * console SPA typically runs on its own origin, but single-origin deployments\n * are supported (the outer proxy then shares the console origin). It owns\n * exactly two responsibilities:\n *\n * 1. On load, announce `ui/notifications/sandbox-proxy-ready` to its parent.\n * The host then sends `ui/notifications/sandbox-resource-ready` with the\n * resource `{ html, sandbox?, csp? }`, and this page builds the nested\n * View iframe (opaque origin, `allow-scripts` by default).\n * 2. Relay JSON-RPC between host and View, skipping the control channel:\n * messages whose method starts with `ui/notifications/sandbox-` are\n * consumed here and never forwarded. Relaying is direction-aware so a View\n * response is never echoed back into the View.\n *\n * CSP delivery: the View document is loaded through `srcdoc`, which has no\n * HTTP response of its own, so a response header is impossible and a\n * parent-document `<meta>` (processed only during that document's parse) cannot\n * apply a per-resource policy. The only faithful mechanism is to splice a\n * `<meta http-equiv=\"Content-Security-Policy\">` into the resource HTML before\n * assigning `srcdoc`; the browser then enforces it against the View document.\n *\n * The host sends `params.csp` as a ready-to-use CSP header string (the gateway\n * resource route returns `cspPolicy`). If it is missing or not a string, the\n * page injects the restrictive {@link buildCsp} default rather than\n * `String()`-coercing an object into `[object Object]`.\n *\n * CONTRACT: the host MUST forward the resource's `cspPolicy` string as\n * `params.csp`; it must NOT forward the raw `csp` object from the resource\n * payload (that object is only the declared-origin input to `buildCsp`).\n *\n * Kept as a string constant (rather than a `public/` file plus a build copy\n * step) because the page is a single small document with no runtime imports.\n */\nimport { buildCsp } from \"./buildCsp\";\n\nconst DEFAULT_CSP_LITERAL = JSON.stringify(buildCsp(undefined));\n\nexport const SANDBOX_HTML = `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>MCP App Sandbox</title>\n <style>html, body { margin: 0; height: 100%; }</style>\n </head>\n <body>\n <script>\n (function () {\n var HOST_READY = \"ui/notifications/sandbox-proxy-ready\";\n var RESOURCE_READY = \"ui/notifications/sandbox-resource-ready\";\n var CONTROL_PREFIX = \"ui/notifications/sandbox-\";\n var DEFAULT_CSP = ${DEFAULT_CSP_LITERAL};\n\n function withCsp(html, csp) {\n var policy = typeof csp === \"string\" && csp.length > 0 ? csp : DEFAULT_CSP;\n var escaped = policy.replace(/&/g, \"&amp;\").replace(/\"/g, \"&quot;\");\n var meta = '<meta http-equiv=\"Content-Security-Policy\" content=\"' + escaped + '\">';\n // Insert at the very start (after an optional doctype) so the parser\n // places the meta in the implicit head. Never search for a literal\n // <head>: a resource could hide a decoy inside a comment/string and\n // slip its real head past the splice.\n var doctype = /^\\\\s*<!doctype[^>]*>/i.exec(html);\n if (doctype) {\n return html.slice(0, doctype[0].length) + meta + html.slice(doctype[0].length);\n }\n return meta + html;\n }\n\n // Size reporter injected into every View document. The host renders the\n // View inside an opaque-origin iframe it cannot measure, so auto-height\n // only happens when the View posts ui/notifications/size-changed. Most\n // hand-written plugin apps never do; this script makes every app report\n // its own content size. It only measures and postMessages — it never\n // mutates the app's DOM.\n var SIZE_REPORTER = [\n \"(function(){\",\n \"function send(){\",\n \"try{\",\n \"var d=document.documentElement,b=document.body;\",\n \"var w=Math.ceil(Math.max(d?d.scrollWidth:0,b?b.scrollWidth:0));\",\n \"var h=Math.ceil(Math.max(d?d.scrollHeight:0,b?b.scrollHeight:0));\",\n \"window.parent.postMessage({jsonrpc:'2.0',method:'ui/notifications/size-changed',params:{width:w,height:h}},'*');\",\n \"}catch(e){}\",\n \"}\",\n \"send();\",\n \"window.addEventListener('load',send);\",\n \"if(typeof ResizeObserver!=='undefined'){\",\n \"var ro=new ResizeObserver(send);\",\n \"ro.observe(document.documentElement);\",\n \"if(document.body)ro.observe(document.body);\",\n \"}else{window.addEventListener('resize',send);}\",\n \"})();\"\n ].join(\"\");\n\n function withSizeReporter(html) {\n // Split the closing tag so this template literal never contains a\n // literal script terminator that would end the sandbox page's own\n // script block during HTML parsing.\n var tag = \"<scr\" + \"ipt>\" + SIZE_REPORTER + \"<\" + \"/scr\" + \"ipt>\";\n var lower = html.toLowerCase();\n var bodyEnd = lower.lastIndexOf(\"</body>\");\n if (bodyEnd >= 0) return html.slice(0, bodyEnd) + tag + html.slice(bodyEnd);\n return html + tag;\n }\n\n var inner = null;\n\n window.parent.postMessage({ jsonrpc: \"2.0\", method: HOST_READY, params: {} }, \"*\");\n\n window.addEventListener(\"message\", function (event) {\n if (event.source !== window.parent) return;\n var data = event.data;\n if (!data || typeof data !== \"object\") return;\n\n if (data.method === RESOURCE_READY) {\n var params = data.params || {};\n if (inner && inner.parentNode) inner.parentNode.removeChild(inner);\n inner = document.createElement(\"iframe\");\n inner.setAttribute(\"sandbox\", params.sandbox || \"allow-scripts\");\n inner.setAttribute(\"srcdoc\", withCsp(withSizeReporter(String(params.html || \"\")), params.csp));\n inner.style.cssText = \"width:100%;height:100%;border:0;display:block\";\n document.body.appendChild(inner);\n return;\n }\n\n if (typeof data.method === \"string\" && data.method.indexOf(CONTROL_PREFIX) === 0) return;\n if (inner && inner.contentWindow) inner.contentWindow.postMessage(data, \"*\");\n });\n\n window.addEventListener(\"message\", function (event) {\n if (inner && event.source === inner.contentWindow) {\n var data = event.data;\n if (data && typeof data === \"object\" && typeof data.method === \"string\"\n && data.method.indexOf(CONTROL_PREFIX) === 0) return;\n window.parent.postMessage(event.data, \"*\");\n }\n });\n })();\n </script>\n </body>\n</html>\n`;\n","import { listTenantPlugins } from \"@axiom-lattice/core\";\nimport type { Plugin } from \"@axiom-lattice/protocols\";\n\n/**\n * Load and merge the global ∪ tenant plugin set for a tenant.\n *\n * Thin gateway re-export of the core {@link listTenantPlugins} primitive — the\n * single source of truth for tenant plugin resolution, shared with tenant\n * plugin agents, the architect's `list_middleware_types`, and the connection\n * routes. Kept as a module so gateway services keep one import site.\n */\nexport { listTenantPlugins };\n\n/**\n * Resolve one merged plugin by `meta.type` for a tenant.\n *\n * @param tenantId - Tenant whose plugins to search.\n * @param type - Plugin type to resolve.\n * @returns The merged plugin, or `undefined` when no source declares the type.\n */\nexport async function resolveTenantPluginByType(\n tenantId: string,\n type: string,\n): Promise<Plugin | undefined> {\n return (await listTenantPlugins(tenantId)).find((p) => p.meta.type === type);\n}\n"],"mappings":"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","names":["context"]}
@@ -0,0 +1,13 @@
1
+ // src/services/open/pluginConnectionMetadata.ts
2
+ function isConnectionBacked(plugin) {
3
+ if (plugin.connection) return true;
4
+ const schema = plugin.meta.configSchema;
5
+ if (!schema || typeof schema !== "object") return false;
6
+ const properties = schema.properties;
7
+ return !!properties && typeof properties === "object" && !Array.isArray(properties) && Object.prototype.hasOwnProperty.call(properties, "connections");
8
+ }
9
+
10
+ export {
11
+ isConnectionBacked
12
+ };
13
+ //# sourceMappingURL=chunk-Y7OVVPVU.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/open/pluginConnectionMetadata.ts"],"sourcesContent":["import type { Plugin } from \"@axiom-lattice/protocols\";\n\n/**\n * Whether a plugin's tools resolve tenant connections.\n *\n * A compiled sandbox plugin carries `connection`; global plugins declare the\n * standard `connections` config field (and may also implement `connection`).\n */\nexport function isConnectionBacked(\n plugin: Pick<Plugin, \"meta\" | \"connection\">,\n): boolean {\n if (plugin.connection) return true;\n const schema = plugin.meta.configSchema;\n if (!schema || typeof schema !== \"object\") return false;\n const properties = (schema as { properties?: unknown }).properties;\n return (\n !!properties\n && typeof properties === \"object\"\n && !Array.isArray(properties)\n && Object.prototype.hasOwnProperty.call(properties, \"connections\")\n );\n}\n"],"mappings":";AAQO,SAAS,mBACd,QACS;AACT,MAAI,OAAO,WAAY,QAAO;AAC9B,QAAM,SAAS,OAAO,KAAK;AAC3B,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,aAAc,OAAoC;AACxD,SACE,CAAC,CAAC,cACC,OAAO,eAAe,YACtB,CAAC,MAAM,QAAQ,UAAU,KACzB,OAAO,UAAU,eAAe,KAAK,YAAY,aAAa;AAErE;","names":[]}
@@ -1,9 +1,12 @@
1
+ import {
2
+ normalizeGrantConnectionKeys
3
+ } from "./chunk-MFWTGNQR.mjs";
4
+
1
5
  // src/services/open/sandboxOpenReflection.ts
2
6
  import {
3
7
  PluginRegistry,
4
8
  agentLatticeManager,
5
9
  ensureBuiltinAgentsForTenant,
6
- getSandboxPluginCatalog,
7
10
  getStoreLattice
8
11
  } from "@axiom-lattice/core";
9
12
  var AGENT_REFLECTION_CACHE_TTL_MS = 6e4;
@@ -25,7 +28,11 @@ function invalidateAgentReflectionCache(tenantId, assistantId) {
25
28
  function cloneGrants(grants) {
26
29
  return grants.map((grant) => ({
27
30
  ...grant,
28
- items: grant.items ? [...grant.items] : void 0
31
+ items: grant.items ? [...grant.items] : void 0,
32
+ selector: grant.selector ? {
33
+ ...grant.selector,
34
+ connections: grant.selector.connections ? [...grant.selector.connections] : void 0
35
+ } : void 0
29
36
  }));
30
37
  }
31
38
  function normalizeOpenExpose(value) {
@@ -143,24 +150,30 @@ function createDefaultAgentReflectionDeps(tenantId) {
143
150
  )
144
151
  };
145
152
  }
146
- var PLUGIN_REFLECTION_CACHE_TTL_MS = 6e4;
147
- var pluginCache = /* @__PURE__ */ new Map();
148
- function invalidatePluginReflectionCache(tenantId) {
149
- if (!tenantId) {
150
- pluginCache.clear();
151
- return;
152
- }
153
- pluginCache.delete(tenantId);
154
- }
155
153
  function mergeGrantsByDomain(grants) {
156
154
  const order = [];
157
155
  const items = /* @__PURE__ */ new Map();
158
156
  const unrestricted = /* @__PURE__ */ new Set();
157
+ const selectors = /* @__PURE__ */ new Map();
159
158
  for (const grant of grants) {
160
159
  if (!grant || typeof grant.domain !== "string" || grant.domain.length === 0) continue;
161
160
  if (!items.has(grant.domain)) {
162
161
  items.set(grant.domain, /* @__PURE__ */ new Set());
163
162
  order.push(grant.domain);
163
+ selectors.set(grant.domain, {
164
+ connections: /* @__PURE__ */ new Set(),
165
+ connectionsUnrestricted: false,
166
+ sandboxSelf: false
167
+ });
168
+ }
169
+ const selectorState = selectors.get(grant.domain);
170
+ if (!selectorState) continue;
171
+ if (grant.selector?.sandbox === "self") selectorState.sandboxSelf = true;
172
+ const selected = normalizeGrantConnectionKeys(grant);
173
+ if (!selected) {
174
+ selectorState.connectionsUnrestricted = true;
175
+ } else {
176
+ for (const key of selected) selectorState.connections.add(key);
164
177
  }
165
178
  if (!grant.items || grant.items.length === 0) {
166
179
  unrestricted.add(grant.domain);
@@ -173,73 +186,23 @@ function mergeGrantsByDomain(grants) {
173
186
  }
174
187
  }
175
188
  return order.map((domain) => {
176
- if (unrestricted.has(domain)) return { domain };
177
- return { domain, items: [...items.get(domain) ?? []] };
178
- });
179
- }
180
- async function computePluginGrants(tenantId, deps) {
181
- const grants = [...deps.globalExposedDomains?.() ?? []];
182
- if (deps.tenantExposedDomains) {
183
- grants.push(...await deps.tenantExposedDomains(tenantId));
184
- }
185
- if (deps.resolvableAssistants) {
186
- const resolvable = await deps.resolvableAssistants(tenantId);
187
- if (resolvable.size > 0) {
188
- grants.push({ domain: "agent", items: [...resolvable] });
189
+ const result = unrestricted.has(domain) ? { domain } : { domain, items: [...items.get(domain) ?? []] };
190
+ const selectorState = selectors.get(domain);
191
+ if (!selectorState) return result;
192
+ const selector = {};
193
+ if (selectorState.sandboxSelf) selector.sandbox = "self";
194
+ if (!selectorState.connectionsUnrestricted && selectorState.connections.size > 0) {
195
+ selector.connections = [...selectorState.connections];
189
196
  }
190
- }
191
- return mergeGrantsByDomain(grants);
192
- }
193
- async function resolvePluginOpenGrants(tenantId, deps) {
194
- try {
195
- const now = deps.now ? deps.now() : Date.now();
196
- const hit = pluginCache.get(tenantId);
197
- if (hit && hit.expiresAt > now) return cloneGrants(hit.grants);
198
- await deps.ensure?.(tenantId);
199
- const grants = await computePluginGrants(tenantId, deps);
200
- pluginCache.set(tenantId, { grants, expiresAt: now + PLUGIN_REFLECTION_CACHE_TTL_MS });
201
- return cloneGrants(grants);
202
- } catch {
203
- return [];
204
- }
205
- }
206
- function buildTenantExposedGrants(manifests) {
207
- const grants = [];
208
- for (const manifest of manifests) {
209
- const items = (manifest.tools ?? []).filter((tool) => tool.expose === true).map((tool) => tool.name);
210
- if (items.length > 0) grants.push({ domain: manifest.type, items });
211
- }
212
- return grants;
213
- }
214
- function createDefaultPluginReflectionDeps() {
215
- return {
216
- ensure: (tenantId) => agentLatticeManager.initializeStoredAssistantsForTenant(tenantId),
217
- globalExposedDomains: () => {
218
- const grants = [];
219
- for (const type of PluginRegistry.list()) {
220
- const plugin = PluginRegistry.get(type);
221
- const items = normalizeOpenExpose(plugin?.meta.openExpose);
222
- if (items.length > 0) grants.push({ domain: type, items });
223
- }
224
- return grants;
225
- },
226
- tenantExposedDomains: async (tenantId) => {
227
- const { manifests } = await getSandboxPluginCatalog().ensureLoaded(tenantId);
228
- return buildTenantExposedGrants(Object.values(manifests));
229
- },
230
- resolvableAssistants: async (tenantId) => new Set(
231
- agentLatticeManager.getAllAgentLatticesByTenant(tenantId).map((lattice) => lattice.config.key)
232
- )
233
- };
197
+ if (selector.sandbox || selector.connections) result.selector = selector;
198
+ return result;
199
+ });
234
200
  }
235
201
 
236
202
  export {
237
203
  invalidateAgentReflectionCache,
238
204
  resolveAgentOpenGrants,
239
205
  createDefaultAgentReflectionDeps,
240
- invalidatePluginReflectionCache,
241
- mergeGrantsByDomain,
242
- resolvePluginOpenGrants,
243
- createDefaultPluginReflectionDeps
206
+ mergeGrantsByDomain
244
207
  };
245
- //# sourceMappingURL=chunk-GL7RV5L3.mjs.map
208
+ //# sourceMappingURL=chunk-YR32MWEJ.mjs.map
@@ -0,0 +1 @@
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 getStoreLattice,\n} from \"@axiom-lattice/core\";\nimport { normalizeGrantConnectionKeys } from \"./openLattice\";\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\nexport function cloneGrants(grants: OpenGrant[]): OpenGrant[] {\n return grants.map((grant) => ({\n ...grant,\n items: grant.items ? [...grant.items] : undefined,\n selector: grant.selector\n ? {\n ...grant.selector,\n connections: grant.selector.connections\n ? [...grant.selector.connections]\n : undefined,\n }\n : 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/**\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 const selectors = new Map<\n string,\n {\n connections: Set<string>;\n connectionsUnrestricted: boolean;\n sandboxSelf: boolean;\n }\n >();\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 selectors.set(grant.domain, {\n connections: new Set(),\n connectionsUnrestricted: false,\n sandboxSelf: false,\n });\n }\n const selectorState = selectors.get(grant.domain);\n if (!selectorState) continue;\n if (grant.selector?.sandbox === \"self\") selectorState.sandboxSelf = true;\n const selected = normalizeGrantConnectionKeys(grant);\n if (!selected) {\n selectorState.connectionsUnrestricted = true;\n } else {\n for (const key of selected) selectorState.connections.add(key);\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 const result: OpenGrant = unrestricted.has(domain)\n ? { domain }\n : { domain, items: [...(items.get(domain) ?? [])] };\n const selectorState = selectors.get(domain);\n if (!selectorState) return result;\n const selector: NonNullable<OpenGrant[\"selector\"]> = {};\n if (selectorState.sandboxSelf) selector.sandbox = \"self\";\n if (!selectorState.connectionsUnrestricted && selectorState.connections.size > 0) {\n selector.connections = [...selectorState.connections];\n }\n if (selector.sandbox || selector.connections) result.selector = selector;\n return result;\n });\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- .x-markdown-debug-panel{position:fixed;z-index:9999;right:0;background:#000000d9;border-radius:8px;padding:12px;font-family:SF Mono,Monaco,Inconsolata,Fira Mono,Droid Sans Mono,Source Code Pro,monospace;font-size:12px;color:#fff;width:180px;box-shadow:0 2px 8px #00000026;backdrop-filter:blur(8px);user-select:none;transition:box-shadow .2s}.x-markdown-debug-panel:hover{box-shadow:0 4px 12px #00000040}.x-markdown-debug-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}.x-markdown-debug-row:last-child{margin-bottom:0}.x-markdown-debug-label{color:#ffffffa6;font-weight:500;min-width:60px}.x-markdown-debug-value{font-weight:600;min-width:50px;text-align:right}.x-markdown-debug-chart{margin-left:auto}.x-markdown-debug-actions{display:flex;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid rgba(255,255,255,.1)}.x-markdown-debug-action{flex:1;padding:6px 12px;background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:4px;color:#fff;font-size:11px;font-family:inherit;cursor:pointer;transition:all .2s;font-weight:500}.x-markdown-debug-action:hover{background:#ffffff26;border-color:#ffffff4d}.x-markdown-debug-action:active{transform:scale(.98)}.x-markdown-debug-record-btn.recording{background:#ff4d4f33;border-color:#ff4d4f;animation:recording-pulse 1.5s ease-in-out infinite}@keyframes recording-pulse{0%,to{box-shadow:0 0 #ff4d4f66}50%{box-shadow:0 0 0 8px #ff4d4f00}}.x-markdown-debug-modal-overlay{position:fixed;inset:0;background:#000000bf;backdrop-filter:blur(4px);z-index:10000;display:flex;align-items:center;justify-content:center;animation:fadeIn .2s ease-out}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.x-markdown-debug-modal{background:#1e1e1ef2;border-radius:12px;box-shadow:0 8px 32px #0006;max-width:90vw;max-height:90vh;overflow:auto;animation:slideIn .3s ease-out;border:1px solid rgba(255,255,255,.1)}@keyframes slideIn{0%{transform:translateY(-20px);opacity:0}to{transform:translateY(0);opacity:1}}.x-markdown-debug-modal-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid rgba(255,255,255,.1)}.x-markdown-debug-close-btn{width:32px;height:32px;border-radius:6px;background:#ffffff1a;border:none;color:#fff;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.x-markdown-debug-close-btn:hover{background:#ff4d4f4d}.x-markdown-debug-modal-content{padding:24px}.x-markdown-debug-stats-summary{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px}.x-markdown-debug-stat-item{background:#ffffff0d;border-radius:8px;padding:16px;border:1px solid rgba(255,255,255,.1)}.x-markdown-debug-stat-label{font-size:11px;color:#fff9;margin-bottom:8px;text-transform:uppercase;letter-spacing:.5px}.x-markdown-debug-stat-value{font-size:20px;font-weight:700;color:#fff}.x-markdown-debug-chart-full{background:#ffffff05;border-radius:8px;border:1px solid rgba(255,255,255,.1)}.x-markdown-debug-chart-full .fps-line,.x-markdown-debug-chart-full .memory-line{filter:drop-shadow(0 0 4px currentColor)}.x-markdown-debug-legend{display:flex;gap:24px;justify-content:center;margin-top:16px}.x-markdown-debug-legend-item{display:flex;align-items:center;gap:8px;font-size:13px;color:#fffc}.x-markdown-debug-legend-color{width:12px;height:12px;border-radius:2px}@media (max-width: 768px){.x-markdown-debug-stats-summary{grid-template-columns:repeat(2,1fr)}}@keyframes x-markdown-fade-in{0%{opacity:0}to{opacity:1}}.x-markdown{--text-color: inherit;--font-size: inherit;--margin-block: 0 0 1em 0;--td-th-padding: .85em 1em;--pre-th-td-padding: 2px 0;--border-font-weight: 600;--padding-ul-ol: 0 0 0 1em;--margin-ul-ol: 0 0 1em 1.8em;--margin-li: .25em 0;--margin-pre: 1em 0;--padding-code-inline: .2em .4em;--margin-code-inline: 0 .2em;--code-inline-text: .85em;--small-border-radius: 2px;--image-margin: .5em 0;--hr-margin: 1.5em 0;--table-margin: 1em 0;font-size:var(--font-size);line-height:1.5714285714285714;width:100%;color:var(--text-color)}.x-markdown p,.x-markdown div,.x-markdown span,.x-markdown li{word-break:break-word;overflow-wrap:break-word}.x-markdown pre,.x-markdown code{word-break:break-word;overflow-wrap:break-word;white-space:pre-wrap}.x-markdown th,.x-markdown td{padding:var(--td-th-padding)}.x-markdown th{font-weight:var(--border-font-weight)}.x-markdown pre table{box-shadow:none}.x-markdown pre td,.x-markdown pre th{padding:var(--pre-th-td-padding);border:none;text-align:left}.x-markdown p{margin:var(--margin-block)}.x-markdown p:first-child{margin-top:0}.x-markdown p:last-child{margin-bottom:0}.x-markdown ul,.x-markdown ol{margin:var(--margin-ul-ol);padding:var(--padding-ul-ol)}.x-markdown ul:first-child,.x-markdown ol:first-child{margin-top:0}.x-markdown ul:last-child,.x-markdown ol:last-child{margin-bottom:0}.x-markdown ol>li{list-style:decimal}.x-markdown ul>li{list-style:disc}.x-markdown li{margin:var(--margin-li)}.x-markdown li:first-child{margin-top:0}.x-markdown li:last-child{margin-bottom:0}.x-markdown pre{margin:var(--margin-pre);overflow-x:auto}.x-markdown pre:first-child{margin-top:0}.x-markdown pre:last-child{margin-bottom:0}.x-markdown code{padding:var(--padding-code-inline);margin:var(--margin-code-inline);font-size:var(--code-inline-text);border-radius:var(--small-border-radius)}.x-markdown pre code{padding:0;margin:0;font-size:inherit;border-radius:0;line-height:2}.x-markdown img{max-width:100%;height:auto;margin:var(--image-margin)}.x-markdown hr{margin:var(--hr-margin)}.x-markdown table:not(pre){margin:var(--table-margin);border-collapse:collapse;display:block;width:max-content;max-width:100%;overflow:auto}.x-markdown table:not(pre):first-child{margin-top:0}.x-markdown table:not(pre):last-child{margin-bottom:0}.x-markdown .inline-katex .katex-display{display:inline-block;margin:0;text-align:initial;vertical-align:middle}.x-markdown .inline-katex .katex-display>.katex{display:inline-block;text-align:initial;white-space:normal}.x-markdown .inline-katex .katex-display>.katex>.katex-html{display:inline-block;position:static}.x-markdown .inline-katex .katex-display>.katex>.katex-html>.tag{position:static}.diff-container{border-collapse:collapse;background:var(--diffViewerBackground);color:var(--diffViewerColor);width:100%}.diff-container pre{white-space:pre-wrap;margin:0;line-height:25px}.diff-container.light-theme{--diffViewerBackground: #fff;--diffViewerColor: #212529;--addedBackground: #e6ffed;--addedColor: #24292e;--removedBackground: #ffeef0;--removedColor: #24292e;--wordAddedBackground: #acf2bd;--wordRemovedBackground: #fdb8c0;--addedGutterBackground: #cdffd8;--removedGutterBackground: #ffdce0;--gutterBackground: #f7f7f7;--gutterBackgroundDark: #f3f1f1;--highlightBackground: #fffbdd;--highlightGutterBackground: #fff5b1;--codeFoldGutterBackground: #dbedff;--codeFoldBackground: #f1f8ff;--emptyLineBackground: #fafbfc;--gutterColor: #212529;--addedGutterColor: #212529;--removedGutterColor: #212529;--codeFoldContentColor: #212529;--diffViewerTitleBackground: #fafbfc;--diffViewerTitleColor: #212529;--diffViewerTitleBorderColor: #eee}.diff-container.dark-theme{--diffViewerBackground: #2e303c;--diffViewerColor: #fff;--addedBackground: #044b53;--addedColor: white;--removedBackground: #632f34;--removedColor: white;--wordAddedBackground: #055d67;--wordRemovedBackground: #7d383f;--addedGutterBackground: #034148;--removedGutterBackground: #632b30;--gutterBackground: #2c2f3a;--gutterBackgroundDark: #262933;--highlightBackground: #2a3967;--highlightGutterBackground: #2d4077;--codeFoldGutterBackground: #21232b;--codeFoldBackground: #262831;--emptyLineBackground: #363946;--gutterColor: #464c67;--addedGutterColor: #8c8c8c;--removedGutterColor: #8c8c8c;--codeFoldContentColor: #555a7b;--diffViewerTitleBackground: #2f323e;--diffViewerTitleColor: #555a7b;--diffViewerTitleBorderColor: #353846}.diff-container .gutter{background-color:var(--gutterBackground)}.diff-container .gutter:hover{background-color:var(--gutterBackgroundDark)}.diff-container .diff-added{background-color:var(--addedBackground);color:var(--addedColor)}.diff-container .diff-added pre{color:var(--addedColor)}.diff-container .diff-removed{background-color:var(--removedBackground);color:var(--removedColor)}.diff-container .diff-removed pre{color:var(--removedColor)}.diff-container .gutter.diff-added{background-color:var(--addedGutterBackground)}.diff-container .gutter.diff-added .line-number{color:var(--addedGutterColor)}.diff-container .gutter.diff-removed{background-color:var(--removedGutterBackground)}.diff-container .gutter.diff-removed .line-number{color:var(--removedGutterColor)}.diff-container .line-number{color:var(--gutterColor)}.diff-container .word-diff.word-added{background-color:var(--wordAddedBackground)}.diff-container .word-diff.word-removed{background-color:var(--wordRemovedBackground)}.diff-container .highlighted-line{background-color:var(--highlightBackground)}.diff-container .highlighted-gutter{background-color:var(--highlightGutterBackground)!important}.diff-container .code-fold-gutter{background-color:var(--codeFoldGutterBackground)}.diff-container .code-fold{background-color:var(--codeFoldBackground)}.diff-container .code-fold .code-fold-content{color:var(--codeFoldContentColor)}.diff-container .empty-line{background-color:var(--emptyLineBackground)}.diff-container .title-block{background-color:var(--diffViewerTitleBackground);border-bottom-color:var(--diffViewerTitleBorderColor)}.diff-container .title-block .content-text{color:var(--diffViewerTitleColor)}.diff-container .title-block:last-child{border-left-color:var(--diffViewerTitleBorderColor)}.diff-container .line{vertical-align:baseline}.diff-container .content{width:100%}.diff-container .split-view .content{width:50%}.diff-container .gutter{user-select:none;text-align:right;min-width:50px;padding:0 10px}.diff-container .gutter:hover{cursor:pointer}.diff-container .gutter:hover pre{opacity:1}.diff-container .gutter pre{opacity:.5}.diff-container .empty-gutter .gutter:hover{background:inherit;cursor:initial}.diff-container .marker{user-select:none;width:25px;padding-left:10px;padding-right:10px}.diff-container .word-diff{display:inline-flex;border-radius:1px;padding:2px}.diff-container .code-fold{height:40px;font-size:14px;font-weight:700}.diff-container .code-fold a{cursor:pointer;text-decoration:underline!important}.diff-container .code-fold a pre{display:inline}.diff-container .title-block{border-bottom:1px solid;padding:10px}.diff-container .title-block:last-child{border-left:1px solid}.diff-container .highlighted-line .word-diff.word-added{background-color:var(--wordAddedBackground)}.diff-container .highlighted-line .word-diff.word-removed{background-color:var(--wordRemovedBackground)}.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}.open-grants-picker{display:flex;border:1px solid var(--axiom-border-color, #f0f0f0);border-radius:10px;overflow:hidden}.open-grants-picker__loading{display:flex;align-items:center;padding:16px 0}.open-grants-picker__nav{width:190px;flex-shrink:0;border-right:1px solid var(--axiom-border-color, #f0f0f0);background:var(--axiom-fill-tertiary, #fafafa);padding:8px;overflow-y:auto}.open-grants-picker__nav-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 10px;border:none;border-radius:8px;background:transparent;cursor:pointer;text-align:left;font-size:13px;color:var(--axiom-text-color, rgba(0, 0, 0, .88));transition:background .15s ease}.open-grants-picker__nav-item:hover{background:var(--axiom-fill-secondary, #f5f5f5)}.open-grants-picker__nav-item--active{background:var(--axiom-primary-bg, #eef2ff);color:#6366f1;font-weight:600}.open-grants-picker__nav-label{display:inline-flex;align-items:center;gap:8px;min-width:0}.open-grants-picker__content{flex:1;min-width:0;padding:12px;overflow-y:auto}.open-grants-picker__content-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.open-grants-picker__content-meta{display:inline-flex;align-items:center}.open-grants-picker__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px}.open-grants-picker__card--selected{border-color:#6366f1!important;background:var(--axiom-primary-bg, #eef2ff)}.open-grants-picker__desc{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;color:var(--axiom-text-color-secondary, rgba(0, 0, 0, .45))}.open-grants-picker__hint{display:flex;align-items:center;gap:8px;margin-top:10px}.key-exposure{display:flex;flex-direction:column;gap:16px;max-height:70vh;overflow-y:auto}.key-exposure__loading{display:flex;justify-content:center;padding:24px 0}.key-exposure__endpoint{padding:10px 12px;background:var(--axiom-fill-tertiary, #fafafa);border-radius:10px}.key-exposure__endpoint-line{display:flex;align-items:center;gap:8px;margin-top:6px;overflow-wrap:anywhere}.key-exposure__section-title{display:flex;align-items:baseline;gap:8px;margin-bottom:8px}.key-exposure__domain+.key-exposure__domain{margin-top:12px}.key-exposure__domain-header{display:flex;align-items:center;gap:6px;margin-bottom:4px}.key-exposure__tool{padding:6px 0 6px 21px;border-bottom:1px solid var(--axiom-border-color, #f5f5f5)}.key-exposure__tool-desc{font-size:12px;color:var(--axiom-text-color-secondary, rgba(0, 0, 0, .45));margin-top:2px}.key-exposure__agent{padding:10px 12px;border:1px solid var(--axiom-border-color, #f0f0f0);border-radius:10px;margin-bottom:8px}.key-exposure__agent-header{display:flex;align-items:center;gap:6px}.key-exposure__agent-desc{font-size:12px;color:var(--axiom-text-color-secondary, rgba(0, 0, 0, .45));margin:4px 0}.key-exposure__agent-lines{display:flex;flex-direction:column;gap:2px;margin:4px 0;overflow-wrap:anywhere}.key-exposure__skills{display:flex;flex-wrap:wrap;gap:4px;margin:4px 0}.key-exposure__tab{display:flex;flex-direction:column;gap:16px}.key-exposure__scroll{max-height:calc(82vh - 130px);overflow-y:auto;padding-right:4px}.agent-card{border-radius:14px!important;margin-bottom:12px}.agent-card__header{display:flex;gap:12px;align-items:flex-start}.agent-card__avatar{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;flex-shrink:0;border-radius:10px;background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff}.agent-card__name{font-size:15px;font-weight:600;line-height:1.3}.agent-card__desc{font-size:12px;color:var(--axiom-text-color-secondary, rgba(0, 0, 0, .45));margin-top:2px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.agent-card__block{margin-top:12px}.agent-card__skills{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.agent-card__endpoints{display:flex;flex-direction:column;gap:4px;margin-top:6px}.agent-card__endpoint{display:flex;align-items:center;gap:8px;overflow-wrap:anywhere}.agent-card__modes{margin-top:12px}
1
+ .x-markdown-debug-panel{position:fixed;z-index:9999;right:0;background:#000000d9;border-radius:8px;padding:12px;font-family:SF Mono,Monaco,Inconsolata,Fira Mono,Droid Sans Mono,Source Code Pro,monospace;font-size:12px;color:#fff;width:180px;box-shadow:0 2px 8px #00000026;backdrop-filter:blur(8px);user-select:none;transition:box-shadow .2s}.x-markdown-debug-panel:hover{box-shadow:0 4px 12px #00000040}.x-markdown-debug-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}.x-markdown-debug-row:last-child{margin-bottom:0}.x-markdown-debug-label{color:#ffffffa6;font-weight:500;min-width:60px}.x-markdown-debug-value{font-weight:600;min-width:50px;text-align:right}.x-markdown-debug-chart{margin-left:auto}.x-markdown-debug-actions{display:flex;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid rgba(255,255,255,.1)}.x-markdown-debug-action{flex:1;padding:6px 12px;background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:4px;color:#fff;font-size:11px;font-family:inherit;cursor:pointer;transition:all .2s;font-weight:500}.x-markdown-debug-action:hover{background:#ffffff26;border-color:#ffffff4d}.x-markdown-debug-action:active{transform:scale(.98)}.x-markdown-debug-record-btn.recording{background:#ff4d4f33;border-color:#ff4d4f;animation:recording-pulse 1.5s ease-in-out infinite}@keyframes recording-pulse{0%,to{box-shadow:0 0 #ff4d4f66}50%{box-shadow:0 0 0 8px #ff4d4f00}}.x-markdown-debug-modal-overlay{position:fixed;inset:0;background:#000000bf;backdrop-filter:blur(4px);z-index:10000;display:flex;align-items:center;justify-content:center;animation:fadeIn .2s ease-out}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.x-markdown-debug-modal{background:#1e1e1ef2;border-radius:12px;box-shadow:0 8px 32px #0006;max-width:90vw;max-height:90vh;overflow:auto;animation:slideIn .3s ease-out;border:1px solid rgba(255,255,255,.1)}@keyframes slideIn{0%{transform:translateY(-20px);opacity:0}to{transform:translateY(0);opacity:1}}.x-markdown-debug-modal-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid rgba(255,255,255,.1)}.x-markdown-debug-close-btn{width:32px;height:32px;border-radius:6px;background:#ffffff1a;border:none;color:#fff;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.x-markdown-debug-close-btn:hover{background:#ff4d4f4d}.x-markdown-debug-modal-content{padding:24px}.x-markdown-debug-stats-summary{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px}.x-markdown-debug-stat-item{background:#ffffff0d;border-radius:8px;padding:16px;border:1px solid rgba(255,255,255,.1)}.x-markdown-debug-stat-label{font-size:11px;color:#fff9;margin-bottom:8px;text-transform:uppercase;letter-spacing:.5px}.x-markdown-debug-stat-value{font-size:20px;font-weight:700;color:#fff}.x-markdown-debug-chart-full{background:#ffffff05;border-radius:8px;border:1px solid rgba(255,255,255,.1)}.x-markdown-debug-chart-full .fps-line,.x-markdown-debug-chart-full .memory-line{filter:drop-shadow(0 0 4px currentColor)}.x-markdown-debug-legend{display:flex;gap:24px;justify-content:center;margin-top:16px}.x-markdown-debug-legend-item{display:flex;align-items:center;gap:8px;font-size:13px;color:#fffc}.x-markdown-debug-legend-color{width:12px;height:12px;border-radius:2px}@media (max-width: 768px){.x-markdown-debug-stats-summary{grid-template-columns:repeat(2,1fr)}}@keyframes x-markdown-fade-in{0%{opacity:0}to{opacity:1}}.x-markdown{--text-color: inherit;--font-size: inherit;--margin-block: 0 0 1em 0;--td-th-padding: .85em 1em;--pre-th-td-padding: 2px 0;--border-font-weight: 600;--padding-ul-ol: 0 0 0 1em;--margin-ul-ol: 0 0 1em 1.8em;--margin-li: .25em 0;--margin-pre: 1em 0;--padding-code-inline: .2em .4em;--margin-code-inline: 0 .2em;--code-inline-text: .85em;--small-border-radius: 2px;--image-margin: .5em 0;--hr-margin: 1.5em 0;--table-margin: 1em 0;font-size:var(--font-size);line-height:1.5714285714285714;width:100%;color:var(--text-color)}.x-markdown p,.x-markdown div,.x-markdown span,.x-markdown li{word-break:break-word;overflow-wrap:break-word}.x-markdown pre,.x-markdown code{word-break:break-word;overflow-wrap:break-word;white-space:pre-wrap}.x-markdown th,.x-markdown td{padding:var(--td-th-padding)}.x-markdown th{font-weight:var(--border-font-weight)}.x-markdown pre table{box-shadow:none}.x-markdown pre td,.x-markdown pre th{padding:var(--pre-th-td-padding);border:none;text-align:left}.x-markdown p{margin:var(--margin-block)}.x-markdown p:first-child{margin-top:0}.x-markdown p:last-child{margin-bottom:0}.x-markdown ul,.x-markdown ol{margin:var(--margin-ul-ol);padding:var(--padding-ul-ol)}.x-markdown ul:first-child,.x-markdown ol:first-child{margin-top:0}.x-markdown ul:last-child,.x-markdown ol:last-child{margin-bottom:0}.x-markdown ol>li{list-style:decimal}.x-markdown ul>li{list-style:disc}.x-markdown li{margin:var(--margin-li)}.x-markdown li:first-child{margin-top:0}.x-markdown li:last-child{margin-bottom:0}.x-markdown pre{margin:var(--margin-pre);overflow-x:auto}.x-markdown pre:first-child{margin-top:0}.x-markdown pre:last-child{margin-bottom:0}.x-markdown code{padding:var(--padding-code-inline);margin:var(--margin-code-inline);font-size:var(--code-inline-text);border-radius:var(--small-border-radius)}.x-markdown pre code{padding:0;margin:0;font-size:inherit;border-radius:0;line-height:2}.x-markdown img{max-width:100%;height:auto;margin:var(--image-margin)}.x-markdown hr{margin:var(--hr-margin)}.x-markdown table:not(pre){margin:var(--table-margin);border-collapse:collapse;display:block;width:max-content;max-width:100%;overflow:auto}.x-markdown table:not(pre):first-child{margin-top:0}.x-markdown table:not(pre):last-child{margin-bottom:0}.x-markdown .inline-katex .katex-display{display:inline-block;margin:0;text-align:initial;vertical-align:middle}.x-markdown .inline-katex .katex-display>.katex{display:inline-block;text-align:initial;white-space:normal}.x-markdown .inline-katex .katex-display>.katex>.katex-html{display:inline-block;position:static}.x-markdown .inline-katex .katex-display>.katex>.katex-html>.tag{position:static}.diff-container{border-collapse:collapse;background:var(--diffViewerBackground);color:var(--diffViewerColor);width:100%}.diff-container pre{white-space:pre-wrap;margin:0;line-height:25px}.diff-container.light-theme{--diffViewerBackground: #fff;--diffViewerColor: #212529;--addedBackground: #e6ffed;--addedColor: #24292e;--removedBackground: #ffeef0;--removedColor: #24292e;--wordAddedBackground: #acf2bd;--wordRemovedBackground: #fdb8c0;--addedGutterBackground: #cdffd8;--removedGutterBackground: #ffdce0;--gutterBackground: #f7f7f7;--gutterBackgroundDark: #f3f1f1;--highlightBackground: #fffbdd;--highlightGutterBackground: #fff5b1;--codeFoldGutterBackground: #dbedff;--codeFoldBackground: #f1f8ff;--emptyLineBackground: #fafbfc;--gutterColor: #212529;--addedGutterColor: #212529;--removedGutterColor: #212529;--codeFoldContentColor: #212529;--diffViewerTitleBackground: #fafbfc;--diffViewerTitleColor: #212529;--diffViewerTitleBorderColor: #eee}.diff-container.dark-theme{--diffViewerBackground: #2e303c;--diffViewerColor: #fff;--addedBackground: #044b53;--addedColor: white;--removedBackground: #632f34;--removedColor: white;--wordAddedBackground: #055d67;--wordRemovedBackground: #7d383f;--addedGutterBackground: #034148;--removedGutterBackground: #632b30;--gutterBackground: #2c2f3a;--gutterBackgroundDark: #262933;--highlightBackground: #2a3967;--highlightGutterBackground: #2d4077;--codeFoldGutterBackground: #21232b;--codeFoldBackground: #262831;--emptyLineBackground: #363946;--gutterColor: #464c67;--addedGutterColor: #8c8c8c;--removedGutterColor: #8c8c8c;--codeFoldContentColor: #555a7b;--diffViewerTitleBackground: #2f323e;--diffViewerTitleColor: #555a7b;--diffViewerTitleBorderColor: #353846}.diff-container .gutter{background-color:var(--gutterBackground)}.diff-container .gutter:hover{background-color:var(--gutterBackgroundDark)}.diff-container .diff-added{background-color:var(--addedBackground);color:var(--addedColor)}.diff-container .diff-added pre{color:var(--addedColor)}.diff-container .diff-removed{background-color:var(--removedBackground);color:var(--removedColor)}.diff-container .diff-removed pre{color:var(--removedColor)}.diff-container .gutter.diff-added{background-color:var(--addedGutterBackground)}.diff-container .gutter.diff-added .line-number{color:var(--addedGutterColor)}.diff-container .gutter.diff-removed{background-color:var(--removedGutterBackground)}.diff-container .gutter.diff-removed .line-number{color:var(--removedGutterColor)}.diff-container .line-number{color:var(--gutterColor)}.diff-container .word-diff.word-added{background-color:var(--wordAddedBackground)}.diff-container .word-diff.word-removed{background-color:var(--wordRemovedBackground)}.diff-container .highlighted-line{background-color:var(--highlightBackground)}.diff-container .highlighted-gutter{background-color:var(--highlightGutterBackground)!important}.diff-container .code-fold-gutter{background-color:var(--codeFoldGutterBackground)}.diff-container .code-fold{background-color:var(--codeFoldBackground)}.diff-container .code-fold .code-fold-content{color:var(--codeFoldContentColor)}.diff-container .empty-line{background-color:var(--emptyLineBackground)}.diff-container .title-block{background-color:var(--diffViewerTitleBackground);border-bottom-color:var(--diffViewerTitleBorderColor)}.diff-container .title-block .content-text{color:var(--diffViewerTitleColor)}.diff-container .title-block:last-child{border-left-color:var(--diffViewerTitleBorderColor)}.diff-container .line{vertical-align:baseline}.diff-container .content{width:100%}.diff-container .split-view .content{width:50%}.diff-container .gutter{user-select:none;text-align:right;min-width:50px;padding:0 10px}.diff-container .gutter:hover{cursor:pointer}.diff-container .gutter:hover pre{opacity:1}.diff-container .gutter pre{opacity:.5}.diff-container .empty-gutter .gutter:hover{background:inherit;cursor:initial}.diff-container .marker{user-select:none;width:25px;padding-left:10px;padding-right:10px}.diff-container .word-diff{display:inline-flex;border-radius:1px;padding:2px}.diff-container .code-fold{height:40px;font-size:14px;font-weight:700}.diff-container .code-fold a{cursor:pointer;text-decoration:underline!important}.diff-container .code-fold a pre{display:inline}.diff-container .title-block{border-bottom:1px solid;padding:10px}.diff-container .title-block:last-child{border-left:1px solid}.diff-container .highlighted-line .word-diff.word-added{background-color:var(--wordAddedBackground)}.diff-container .highlighted-line .word-diff.word-removed{background-color:var(--wordRemovedBackground)}.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}.open-grants-picker{display:flex;border:1px solid var(--axiom-border-color, #f0f0f0);border-radius:10px;overflow:hidden}.open-grants-picker__loading{display:flex;align-items:center;padding:16px 0}.open-grants-picker__nav{width:190px;flex-shrink:0;border-right:1px solid var(--axiom-border-color, #f0f0f0);background:var(--axiom-fill-tertiary, #fafafa);padding:8px;overflow-y:auto}.open-grants-picker__nav-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 10px;border:none;border-radius:8px;background:transparent;cursor:pointer;text-align:left;font-size:13px;color:var(--axiom-text-color, rgba(0, 0, 0, .88));transition:background .15s ease}.open-grants-picker__nav-item:hover{background:var(--axiom-fill-secondary, #f5f5f5)}.open-grants-picker__nav-item--active{background:var(--axiom-primary-bg, #eef2ff);color:#6366f1;font-weight:600}.open-grants-picker__nav-label{display:inline-flex;align-items:center;gap:8px;min-width:0}.open-grants-picker__content{flex:1;min-width:0;padding:12px;overflow-y:auto}.open-grants-picker__content-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.open-grants-picker__content-meta{display:inline-flex;align-items:center}.open-grants-picker__connection{display:flex;flex-direction:column;gap:4px;padding:0 0 12px;border-bottom:1px solid rgba(0,0,0,.06);margin-bottom:12px}.open-grants-picker__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px}.open-grants-picker__card--selected{border-color:#6366f1!important;background:var(--axiom-primary-bg, #eef2ff)}.open-grants-picker__desc{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:12px;color:var(--axiom-text-color-secondary, rgba(0, 0, 0, .45))}.open-grants-picker__hint{display:flex;align-items:center;gap:8px;margin-top:10px}.key-exposure{display:flex;flex-direction:column;gap:16px;max-height:70vh;overflow-y:auto}.key-exposure__loading{display:flex;justify-content:center;padding:24px 0}.key-exposure__endpoint{padding:10px 12px;background:var(--axiom-fill-tertiary, #fafafa);border-radius:10px}.key-exposure__endpoint-line{display:flex;align-items:center;gap:8px;margin-top:6px;overflow-wrap:anywhere}.key-exposure__section-title{display:flex;align-items:baseline;gap:8px;margin-bottom:8px}.key-exposure__domain+.key-exposure__domain{margin-top:12px}.key-exposure__domain-header{display:flex;align-items:center;gap:6px;margin-bottom:4px}.key-exposure__tool{padding:6px 0 6px 21px;border-bottom:1px solid var(--axiom-border-color, #f5f5f5)}.key-exposure__tool-desc{font-size:12px;color:var(--axiom-text-color-secondary, rgba(0, 0, 0, .45));margin-top:2px}.key-exposure__agent{padding:10px 12px;border:1px solid var(--axiom-border-color, #f0f0f0);border-radius:10px;margin-bottom:8px}.key-exposure__agent-header{display:flex;align-items:center;gap:6px}.key-exposure__agent-desc{font-size:12px;color:var(--axiom-text-color-secondary, rgba(0, 0, 0, .45));margin:4px 0}.key-exposure__agent-lines{display:flex;flex-direction:column;gap:2px;margin:4px 0;overflow-wrap:anywhere}.key-exposure__skills{display:flex;flex-wrap:wrap;gap:4px;margin:4px 0}.key-exposure__tab{display:flex;flex-direction:column;gap:16px}.key-exposure__scroll{max-height:calc(82vh - 130px);overflow-y:auto;padding-right:4px}.agent-card{border-radius:14px!important;margin-bottom:12px}.agent-card__header{display:flex;gap:12px;align-items:flex-start}.agent-card__avatar{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;flex-shrink:0;border-radius:10px;background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff}.agent-card__name{font-size:15px;font-weight:600;line-height:1.3}.agent-card__desc{font-size:12px;color:var(--axiom-text-color-secondary, rgba(0, 0, 0, .45));margin-top:2px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.agent-card__block{margin-top:12px}.agent-card__skills{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.agent-card__endpoints{display:flex;flex-direction:column;gap:4px;margin-top:6px}.agent-card__endpoint{display:flex;align-items:center;gap:8px;overflow-wrap:anywhere}.agent-card__modes{margin-top:12px}
@@ -1 +1 @@
1
- import{k as L}from"./embed-page-chunk-G5PDHDBZ.js";import"./embed-page-chunk-BZKAPY7M.js";import"./embed-page-chunk-G5MXFDNZ.js";import"./embed-page-chunk-EM6VGUZ2.js";import"./embed-page-chunk-GSUSX54K.js";import"./embed-page-chunk-DJKD2PHO.js";import"./embed-page-chunk-D7YTSCUO.js";import"./embed-page-chunk-S4XZPBRM.js";import"./embed-page-chunk-HZZ5HNTF.js";import{T as E,h as P}from"./embed-page-chunk-CFFDP526.js";import"./embed-page-chunk-4XKCMM5C.js";import{a as F}from"./embed-page-chunk-PMOGSY6W.js";import"./embed-page-chunk-BSRASK7V.js";import"./embed-page-chunk-F7ZUYG42.js";import{k as T}from"./embed-page-chunk-ASEIOK7X.js";import{a as C}from"./embed-page-chunk-QV7QAJ4N.js";import"./embed-page-chunk-VW3NF5NT.js";import"./embed-page-chunk-34RWKVD7.js";import"./embed-page-chunk-B2KNZ3EW.js";import{Ja as I,La as c,W as p,_ as m,za as b}from"./embed-page-chunk-F7TA3LRH.js";import{a as N}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 u}from"./embed-page-chunk-QBTAJ2YI.js";var n=u(N());var t=u(C()),O={component_key:"workspace_assistants",data:{}},$=d=>{let i=d;if(typeof d=="string")try{i=JSON.parse(d)}catch{return{}}return i!==null&&typeof i=="object"&&!Array.isArray(i)?i:{}},W=({data:d})=>{let{assistantId:i,initialTab:R}=d??{},{get:f,put:y,del:A}=F(),e=T(),v=P(),[g,w]=(0,n.useState)(null),[B,h]=(0,n.useState)([]),[_,S]=(0,n.useState)(!1),[j,k]=(0,n.useState)(null),l=(0,n.useCallback)(()=>{v({...O,message:e("governance.detail.listCardMessage")})},[v,e]),o=(0,n.useCallback)(async()=>{S(!0),k(null);try{let s=(await f("/api/assistants")).data?.records??[];h(s),w(s.find(r=>r.id===i)??null)}catch{k(e("governance.detail.loadFailed"))}finally{S(!1)}},[f,i,e]);(0,n.useEffect)(()=>{i&&o()},[o,i]);let x=(0,n.useCallback)(async(a,s)=>{let r=$(a.graphDefinition),D;try{D=await y(`/api/assistants/${a.id}`,{name:s.name!==void 0?s.name:a.name,description:s.description!==void 0?s.description:a.description,graphDefinition:{...r,...s}})}catch(G){throw c.error(e("governance.detail.saveFailed")),G}if(!D.success)throw c.error(e("governance.detail.saveFailed")),new Error("Failed to save agent");c.success(e("common.saved")),await o()},[y,o,e]),z=(0,n.useCallback)(a=>{w(s=>s&&s.id===a.id?a:s),h(s=>s.map(r=>r.id===a.id?a:r))},[]),U=(0,n.useCallback)(async a=>{if(!(await A(`/api/assistants/${a}`)).success)throw new Error("Failed to delete agent");c.success(e("governance.detail.deleted")),l()},[A,l,e]);return i?g?(0,t.jsx)("div",{style:{height:"100%",overflow:"auto"},children:(0,t.jsx)(E,{children:(0,t.jsx)(L,{assistant:g,allAssistants:B,onBack:l,onDeleted:U,onSave:x,onAssistantUpdated:z,onAgentListInvalidated:()=>void o(),initialTab:R})},g.id)}):_?(0,t.jsx)("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",height:"100%"},children:(0,t.jsx)(I,{})}):j?(0,t.jsx)("div",{style:{padding:24},children:(0,t.jsx)(p,{type:"error",showIcon:!0,message:e("governance.detail.loadFailed"),"data-testid":"detail-error",action:(0,t.jsxs)(b,{children:[(0,t.jsx)(m,{size:"small",onClick:()=>void o(),children:e("common.retry")}),(0,t.jsx)(m,{size:"small",type:"primary",onClick:l,children:e("governance.detail.backToList")})]})})}):(0,t.jsx)("div",{style:{padding:24},children:(0,t.jsx)(p,{type:"warning",showIcon:!0,message:e("governance.detail.notFound"),"data-testid":"detail-not-found",action:(0,t.jsx)(m,{size:"small",type:"primary",onClick:l,children:e("governance.detail.backToList")})})}):(0,t.jsx)("div",{style:{padding:24},children:(0,t.jsx)(p,{type:"warning",showIcon:!0,message:e("governance.detail.missingId"),"data-testid":"detail-missing-id"})})};export{W as AgentDetailSideApp};
1
+ import{k as L}from"./embed-page-chunk-VYS3TAK4.js";import"./embed-page-chunk-OMN6G52V.js";import"./embed-page-chunk-RKQJBKN2.js";import"./embed-page-chunk-L2EGVVJS.js";import"./embed-page-chunk-FE6KYRCO.js";import"./embed-page-chunk-DJKD2PHO.js";import"./embed-page-chunk-D7YTSCUO.js";import"./embed-page-chunk-S4XZPBRM.js";import"./embed-page-chunk-LDQGH44H.js";import{T as E,h as P}from"./embed-page-chunk-Z6KWW64I.js";import"./embed-page-chunk-7ULTMRM2.js";import{a as F}from"./embed-page-chunk-JMP7EQEF.js";import"./embed-page-chunk-FZX4A32D.js";import"./embed-page-chunk-F7ZUYG42.js";import{k as T}from"./embed-page-chunk-W5OP2CZG.js";import{a as C}from"./embed-page-chunk-QV7QAJ4N.js";import"./embed-page-chunk-VW3NF5NT.js";import"./embed-page-chunk-34RWKVD7.js";import"./embed-page-chunk-B2KNZ3EW.js";import{Ja as I,La as c,W as p,_ as m,za as b}from"./embed-page-chunk-F7TA3LRH.js";import{a as N}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 u}from"./embed-page-chunk-QBTAJ2YI.js";var n=u(N());var t=u(C()),O={component_key:"workspace_assistants",data:{}},$=d=>{let i=d;if(typeof d=="string")try{i=JSON.parse(d)}catch{return{}}return i!==null&&typeof i=="object"&&!Array.isArray(i)?i:{}},W=({data:d})=>{let{assistantId:i,initialTab:R}=d??{},{get:f,put:y,del:A}=F(),e=T(),v=P(),[g,w]=(0,n.useState)(null),[B,h]=(0,n.useState)([]),[_,S]=(0,n.useState)(!1),[j,k]=(0,n.useState)(null),l=(0,n.useCallback)(()=>{v({...O,message:e("governance.detail.listCardMessage")})},[v,e]),o=(0,n.useCallback)(async()=>{S(!0),k(null);try{let s=(await f("/api/assistants")).data?.records??[];h(s),w(s.find(r=>r.id===i)??null)}catch{k(e("governance.detail.loadFailed"))}finally{S(!1)}},[f,i,e]);(0,n.useEffect)(()=>{i&&o()},[o,i]);let x=(0,n.useCallback)(async(a,s)=>{let r=$(a.graphDefinition),D;try{D=await y(`/api/assistants/${a.id}`,{name:s.name!==void 0?s.name:a.name,description:s.description!==void 0?s.description:a.description,graphDefinition:{...r,...s}})}catch(G){throw c.error(e("governance.detail.saveFailed")),G}if(!D.success)throw c.error(e("governance.detail.saveFailed")),new Error("Failed to save agent");c.success(e("common.saved")),await o()},[y,o,e]),z=(0,n.useCallback)(a=>{w(s=>s&&s.id===a.id?a:s),h(s=>s.map(r=>r.id===a.id?a:r))},[]),U=(0,n.useCallback)(async a=>{if(!(await A(`/api/assistants/${a}`)).success)throw new Error("Failed to delete agent");c.success(e("governance.detail.deleted")),l()},[A,l,e]);return i?g?(0,t.jsx)("div",{style:{height:"100%",overflow:"auto"},children:(0,t.jsx)(E,{children:(0,t.jsx)(L,{assistant:g,allAssistants:B,onBack:l,onDeleted:U,onSave:x,onAssistantUpdated:z,onAgentListInvalidated:()=>void o(),initialTab:R})},g.id)}):_?(0,t.jsx)("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",height:"100%"},children:(0,t.jsx)(I,{})}):j?(0,t.jsx)("div",{style:{padding:24},children:(0,t.jsx)(p,{type:"error",showIcon:!0,message:e("governance.detail.loadFailed"),"data-testid":"detail-error",action:(0,t.jsxs)(b,{children:[(0,t.jsx)(m,{size:"small",onClick:()=>void o(),children:e("common.retry")}),(0,t.jsx)(m,{size:"small",type:"primary",onClick:l,children:e("governance.detail.backToList")})]})})}):(0,t.jsx)("div",{style:{padding:24},children:(0,t.jsx)(p,{type:"warning",showIcon:!0,message:e("governance.detail.notFound"),"data-testid":"detail-not-found",action:(0,t.jsx)(m,{size:"small",type:"primary",onClick:l,children:e("governance.detail.backToList")})})}):(0,t.jsx)("div",{style:{padding:24},children:(0,t.jsx)(p,{type:"warning",showIcon:!0,message:e("governance.detail.missingId"),"data-testid":"detail-missing-id"})})};export{W as AgentDetailSideApp};