@databricks/appkit 0.31.0 → 0.33.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 (218) hide show
  1. package/CLAUDE.md +54 -1
  2. package/NOTICE.md +2 -0
  3. package/dist/agents/databricks.d.ts.map +1 -1
  4. package/dist/agents/databricks.js +8 -3
  5. package/dist/agents/databricks.js.map +1 -1
  6. package/dist/appkit/package.js +1 -1
  7. package/dist/beta.d.ts +16 -1
  8. package/dist/beta.js +14 -1
  9. package/dist/connectors/index.js +3 -0
  10. package/dist/connectors/mcp/client.d.ts +85 -0
  11. package/dist/connectors/mcp/client.d.ts.map +1 -0
  12. package/dist/connectors/mcp/client.js +296 -0
  13. package/dist/connectors/mcp/client.js.map +1 -0
  14. package/dist/connectors/mcp/host-policy.d.ts +51 -0
  15. package/dist/connectors/mcp/host-policy.d.ts.map +1 -0
  16. package/dist/connectors/mcp/host-policy.js +168 -0
  17. package/dist/connectors/mcp/host-policy.js.map +1 -0
  18. package/dist/connectors/mcp/index.d.ts +3 -0
  19. package/dist/connectors/mcp/index.js +4 -0
  20. package/dist/connectors/mcp/types.d.ts +16 -0
  21. package/dist/connectors/mcp/types.d.ts.map +1 -0
  22. package/dist/context/index.js +1 -1
  23. package/dist/core/agent/build-toolkit.d.ts +2 -0
  24. package/dist/core/agent/build-toolkit.js +45 -0
  25. package/dist/core/agent/build-toolkit.js.map +1 -0
  26. package/dist/core/agent/consume-adapter-stream.js +33 -0
  27. package/dist/core/agent/consume-adapter-stream.js.map +1 -0
  28. package/dist/core/agent/create-agent.d.ts +27 -0
  29. package/dist/core/agent/create-agent.d.ts.map +1 -0
  30. package/dist/core/agent/create-agent.js +50 -0
  31. package/dist/core/agent/create-agent.js.map +1 -0
  32. package/dist/core/agent/load-agents.d.ts +72 -0
  33. package/dist/core/agent/load-agents.d.ts.map +1 -0
  34. package/dist/core/agent/load-agents.js +268 -0
  35. package/dist/core/agent/load-agents.js.map +1 -0
  36. package/dist/core/agent/normalize-result.js +39 -0
  37. package/dist/core/agent/normalize-result.js.map +1 -0
  38. package/dist/core/agent/plugins-map.js +44 -0
  39. package/dist/core/agent/plugins-map.js.map +1 -0
  40. package/dist/core/agent/run-agent.d.ts +58 -0
  41. package/dist/core/agent/run-agent.d.ts.map +1 -0
  42. package/dist/core/agent/run-agent.js +257 -0
  43. package/dist/core/agent/run-agent.js.map +1 -0
  44. package/dist/core/agent/system-prompt.js +38 -0
  45. package/dist/core/agent/system-prompt.js.map +1 -0
  46. package/dist/core/agent/toolkit-options.js +28 -0
  47. package/dist/core/agent/toolkit-options.js.map +1 -0
  48. package/dist/core/agent/toolkit-resolver.js +44 -0
  49. package/dist/core/agent/toolkit-resolver.js.map +1 -0
  50. package/dist/core/agent/tools/define-tool.d.ts +66 -0
  51. package/dist/core/agent/tools/define-tool.d.ts.map +1 -0
  52. package/dist/core/agent/tools/define-tool.js +50 -0
  53. package/dist/core/agent/tools/define-tool.js.map +1 -0
  54. package/dist/core/agent/tools/function-tool.d.ts +38 -0
  55. package/dist/core/agent/tools/function-tool.d.ts.map +1 -0
  56. package/dist/core/agent/tools/function-tool.js +22 -0
  57. package/dist/core/agent/tools/function-tool.js.map +1 -0
  58. package/dist/core/agent/tools/hosted-tools.d.ts +47 -0
  59. package/dist/core/agent/tools/hosted-tools.d.ts.map +1 -0
  60. package/dist/core/agent/tools/hosted-tools.js +67 -0
  61. package/dist/core/agent/tools/hosted-tools.js.map +1 -0
  62. package/dist/core/agent/tools/index.d.ts +5 -0
  63. package/dist/core/agent/tools/index.js +7 -0
  64. package/dist/core/agent/tools/json-schema.js +24 -0
  65. package/dist/core/agent/tools/json-schema.js.map +1 -0
  66. package/dist/core/agent/tools/sql-policy.js +256 -0
  67. package/dist/core/agent/tools/sql-policy.js.map +1 -0
  68. package/dist/core/agent/tools/tool.d.ts +63 -0
  69. package/dist/core/agent/tools/tool.d.ts.map +1 -0
  70. package/dist/core/agent/tools/tool.js +42 -0
  71. package/dist/core/agent/tools/tool.js.map +1 -0
  72. package/dist/core/agent/types.d.ts +299 -0
  73. package/dist/core/agent/types.d.ts.map +1 -0
  74. package/dist/core/agent/types.js +12 -0
  75. package/dist/core/agent/types.js.map +1 -0
  76. package/dist/core/appkit.d.ts +1 -0
  77. package/dist/core/appkit.d.ts.map +1 -1
  78. package/dist/core/appkit.js +31 -4
  79. package/dist/core/appkit.js.map +1 -1
  80. package/dist/core/plugin-context.d.ts +133 -0
  81. package/dist/core/plugin-context.d.ts.map +1 -0
  82. package/dist/core/plugin-context.js +220 -0
  83. package/dist/core/plugin-context.js.map +1 -0
  84. package/dist/index.d.ts +11 -11
  85. package/dist/internal-telemetry/appkit-log.js +19 -0
  86. package/dist/internal-telemetry/appkit-log.js.map +1 -0
  87. package/dist/internal-telemetry/config.js +15 -0
  88. package/dist/internal-telemetry/config.js.map +1 -0
  89. package/dist/internal-telemetry/index.js +4 -0
  90. package/dist/internal-telemetry/reporter.js +132 -0
  91. package/dist/internal-telemetry/reporter.js.map +1 -0
  92. package/dist/plugin/plugin.d.ts +18 -3
  93. package/dist/plugin/plugin.d.ts.map +1 -1
  94. package/dist/plugin/plugin.js +26 -2
  95. package/dist/plugin/plugin.js.map +1 -1
  96. package/dist/plugin/to-plugin.d.ts +3 -2
  97. package/dist/plugin/to-plugin.d.ts.map +1 -1
  98. package/dist/plugin/to-plugin.js +7 -4
  99. package/dist/plugin/to-plugin.js.map +1 -1
  100. package/dist/plugins/agents/agents.d.ts +186 -0
  101. package/dist/plugins/agents/agents.d.ts.map +1 -0
  102. package/dist/plugins/agents/agents.js +979 -0
  103. package/dist/plugins/agents/agents.js.map +1 -0
  104. package/dist/plugins/agents/defaults.js +13 -0
  105. package/dist/plugins/agents/defaults.js.map +1 -0
  106. package/dist/plugins/agents/event-channel.js +64 -0
  107. package/dist/plugins/agents/event-channel.js.map +1 -0
  108. package/dist/plugins/agents/event-translator.js +224 -0
  109. package/dist/plugins/agents/event-translator.js.map +1 -0
  110. package/dist/plugins/agents/index.d.ts +4 -0
  111. package/dist/plugins/agents/index.js +6 -0
  112. package/dist/plugins/agents/manifest.js +26 -0
  113. package/dist/plugins/agents/manifest.js.map +1 -0
  114. package/dist/plugins/agents/schemas.js +51 -0
  115. package/dist/plugins/agents/schemas.js.map +1 -0
  116. package/dist/plugins/agents/thread-store.js +58 -0
  117. package/dist/plugins/agents/thread-store.js.map +1 -0
  118. package/dist/plugins/agents/tool-approval-gate.js +75 -0
  119. package/dist/plugins/agents/tool-approval-gate.js.map +1 -0
  120. package/dist/plugins/analytics/analytics.d.ts +15 -1
  121. package/dist/plugins/analytics/analytics.d.ts.map +1 -1
  122. package/dist/plugins/analytics/analytics.js +37 -2
  123. package/dist/plugins/analytics/analytics.js.map +1 -1
  124. package/dist/plugins/analytics/index.js +1 -0
  125. package/dist/plugins/analytics/types.js +15 -0
  126. package/dist/plugins/analytics/types.js.map +1 -0
  127. package/dist/plugins/beta-exports.generated.d.ts +2 -0
  128. package/dist/plugins/beta-exports.generated.js +4 -0
  129. package/dist/plugins/files/plugin.d.ts +20 -2
  130. package/dist/plugins/files/plugin.d.ts.map +1 -1
  131. package/dist/plugins/files/plugin.js +120 -2
  132. package/dist/plugins/files/plugin.js.map +1 -1
  133. package/dist/plugins/genie/genie.d.ts +17 -3
  134. package/dist/plugins/genie/genie.d.ts.map +1 -1
  135. package/dist/plugins/genie/genie.js +61 -2
  136. package/dist/plugins/genie/genie.js.map +1 -1
  137. package/dist/plugins/genie/types.d.ts +10 -2
  138. package/dist/plugins/genie/types.d.ts.map +1 -1
  139. package/dist/plugins/jobs/plugin.js +1 -1
  140. package/dist/plugins/lakebase/index.d.ts +2 -2
  141. package/dist/plugins/lakebase/index.js +1 -1
  142. package/dist/plugins/lakebase/lakebase.d.ts +31 -3
  143. package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
  144. package/dist/plugins/lakebase/lakebase.js +77 -5
  145. package/dist/plugins/lakebase/lakebase.js.map +1 -1
  146. package/dist/plugins/lakebase/types.d.ts +39 -1
  147. package/dist/plugins/lakebase/types.d.ts.map +1 -1
  148. package/dist/plugins/server/index.d.ts +12 -0
  149. package/dist/plugins/server/index.d.ts.map +1 -1
  150. package/dist/plugins/server/index.js +47 -10
  151. package/dist/plugins/server/index.js.map +1 -1
  152. package/dist/plugins/server/types.d.ts +11 -3
  153. package/dist/plugins/server/types.d.ts.map +1 -1
  154. package/dist/shared/src/agent.d.ts +75 -1
  155. package/dist/shared/src/agent.d.ts.map +1 -1
  156. package/dist/shared/src/index.d.ts +1 -1
  157. package/dist/shared/src/plugin.d.ts +8 -0
  158. package/dist/shared/src/plugin.d.ts.map +1 -1
  159. package/docs/api/appkit/Class.AppKitMcpClient.md +157 -0
  160. package/docs/api/appkit/Class.DatabricksAdapter.md +151 -0
  161. package/docs/api/appkit/Class.Plugin.md +65 -23
  162. package/docs/api/appkit/Function.agentIdFromMarkdownPath.md +18 -0
  163. package/docs/api/appkit/Function.createAgent.md +33 -0
  164. package/docs/api/appkit/Function.createApp.md +10 -8
  165. package/docs/api/appkit/Function.defineTool.md +26 -0
  166. package/docs/api/appkit/Function.executeFromRegistry.md +25 -0
  167. package/docs/api/appkit/Function.functionToolToDefinition.md +16 -0
  168. package/docs/api/appkit/Function.isFunctionTool.md +16 -0
  169. package/docs/api/appkit/Function.isHostedTool.md +16 -0
  170. package/docs/api/appkit/Function.isToolkitEntry.md +18 -0
  171. package/docs/api/appkit/Function.loadAgentFromFile.md +21 -0
  172. package/docs/api/appkit/Function.loadAgentsFromDir.md +26 -0
  173. package/docs/api/appkit/Function.mcpServer.md +28 -0
  174. package/docs/api/appkit/Function.parseTextToolCalls.md +26 -0
  175. package/docs/api/appkit/Function.resolveHostedTools.md +16 -0
  176. package/docs/api/appkit/Function.runAgent.md +26 -0
  177. package/docs/api/appkit/Function.tool.md +28 -0
  178. package/docs/api/appkit/Function.toolsFromRegistry.md +20 -0
  179. package/docs/api/appkit/Interface.AgentAdapter.md +21 -0
  180. package/docs/api/appkit/Interface.AgentDefinition.md +112 -0
  181. package/docs/api/appkit/Interface.AgentInput.md +37 -0
  182. package/docs/api/appkit/Interface.AgentRunContext.md +32 -0
  183. package/docs/api/appkit/Interface.AgentToolDefinition.md +37 -0
  184. package/docs/api/appkit/Interface.AgentsPluginConfig.md +241 -0
  185. package/docs/api/appkit/Interface.AutoInheritToolsConfig.md +27 -0
  186. package/docs/api/appkit/Interface.BasePluginConfig.md +1 -0
  187. package/docs/api/appkit/Interface.FunctionTool.md +80 -0
  188. package/docs/api/appkit/Interface.McpConnectAllResult.md +38 -0
  189. package/docs/api/appkit/Interface.Message.md +55 -0
  190. package/docs/api/appkit/Interface.PluginToolkitProvider.md +22 -0
  191. package/docs/api/appkit/Interface.PromptContext.md +30 -0
  192. package/docs/api/appkit/Interface.RegisteredAgent.md +75 -0
  193. package/docs/api/appkit/Interface.RunAgentInput.md +34 -0
  194. package/docs/api/appkit/Interface.RunAgentResult.md +23 -0
  195. package/docs/api/appkit/Interface.Thread.md +46 -0
  196. package/docs/api/appkit/Interface.ThreadStore.md +103 -0
  197. package/docs/api/appkit/Interface.ToolAnnotations.md +56 -0
  198. package/docs/api/appkit/Interface.ToolConfig.md +72 -0
  199. package/docs/api/appkit/Interface.ToolEntry.md +73 -0
  200. package/docs/api/appkit/Interface.ToolProvider.md +38 -0
  201. package/docs/api/appkit/Interface.ToolkitEntry.md +59 -0
  202. package/docs/api/appkit/Interface.ToolkitOptions.md +45 -0
  203. package/docs/api/appkit/TypeAlias.AgentEvent.md +299 -0
  204. package/docs/api/appkit/TypeAlias.AgentTool.md +11 -0
  205. package/docs/api/appkit/TypeAlias.AgentTools.md +8 -0
  206. package/docs/api/appkit/TypeAlias.AgentToolsFn.md +20 -0
  207. package/docs/api/appkit/TypeAlias.BaseSystemPromptOption.md +9 -0
  208. package/docs/api/appkit/TypeAlias.HostedTool.md +10 -0
  209. package/docs/api/appkit/TypeAlias.Plugins.md +26 -0
  210. package/docs/api/appkit/TypeAlias.ResolvedToolEntry.md +29 -0
  211. package/docs/api/appkit/TypeAlias.ToolRegistry.md +6 -0
  212. package/docs/api/appkit/Variable.agents.md +19 -0
  213. package/docs/api/appkit.md +113 -62
  214. package/docs/plugins/agents.md +441 -0
  215. package/docs/privacy.md +41 -0
  216. package/llms.txt +54 -1
  217. package/package.json +4 -2
  218. package/sbom.cdx.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":["manifest","files"],"sources":["../../../src/plugins/files/plugin.ts"],"sourcesContent":["import { STATUS_CODES } from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport { ApiError } from \"@databricks/sdk-experimental\";\nimport type express from \"express\";\nimport type { IAppRouter, PluginExecutionSettings } from \"shared\";\nimport {\n contentTypeFromPath,\n FilesConnector,\n isSafeInlineContentType,\n validateCustomContentTypes,\n} from \"../../connectors/files\";\nimport { getCurrentUserId, getWorkspaceClient } from \"../../context\";\nimport { AuthenticationError } from \"../../errors\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest, ResourceRequirement } from \"../../registry\";\nimport { ResourceType } from \"../../registry\";\nimport {\n FILES_DOWNLOAD_DEFAULTS,\n FILES_MAX_UPLOAD_SIZE,\n FILES_READ_DEFAULTS,\n FILES_WRITE_DEFAULTS,\n} from \"./defaults\";\nimport { parentDirectory, sanitizeFilename } from \"./helpers\";\nimport manifest from \"./manifest.json\";\nimport {\n type FileAction,\n type FilePolicyUser,\n type FileResource,\n PolicyDeniedError,\n policy,\n} from \"./policy\";\nimport type {\n DownloadResponse,\n FilesExport,\n IFilesConfig,\n VolumeAPI,\n VolumeConfig,\n VolumeHandle,\n} from \"./types\";\n\nconst logger = createLogger(\"files\");\n\nexport class FilesPlugin extends Plugin {\n name = \"files\";\n\n /** Plugin manifest declaring metadata and resource requirements. */\n static manifest = manifest as PluginManifest;\n protected static description = \"Files plugin for Databricks file operations\";\n protected declare config: IFilesConfig;\n\n private volumeConnectors: Record<string, FilesConnector> = {};\n private volumeConfigs: Record<string, VolumeConfig> = {};\n private volumeKeys: string[] = [];\n\n /**\n * Scans `process.env` for `DATABRICKS_VOLUME_*` keys and merges them with\n * any explicitly configured volumes. Explicit config wins for per-volume\n * overrides; auto-discovered volumes get default `{}` config.\n */\n static discoverVolumes(config: IFilesConfig): Record<string, VolumeConfig> {\n const explicit = config.volumes ?? {};\n const discovered: Record<string, VolumeConfig> = {};\n\n const prefix = \"DATABRICKS_VOLUME_\";\n for (const key of Object.keys(process.env)) {\n if (!key.startsWith(prefix)) continue;\n const suffix = key.slice(prefix.length);\n if (!suffix) continue;\n if (!process.env[key]) continue;\n const volumeKey = suffix.toLowerCase();\n if (!(volumeKey in explicit)) {\n discovered[volumeKey] = {};\n }\n }\n\n return { ...discovered, ...explicit };\n }\n\n /**\n * Generates resource requirements dynamically from discovered + configured volumes.\n * Each volume key maps to a `DATABRICKS_VOLUME_{KEY_UPPERCASE}` env var.\n */\n static getResourceRequirements(config: IFilesConfig): ResourceRequirement[] {\n const volumes = FilesPlugin.discoverVolumes(config);\n return Object.keys(volumes).map((key) => ({\n type: ResourceType.VOLUME,\n alias: `volume-${key}`,\n resourceKey: `volume-${key}`,\n description: `Unity Catalog Volume for \"${key}\" file storage`,\n permission: \"WRITE_VOLUME\",\n fields: {\n path: {\n env: `DATABRICKS_VOLUME_${key.toUpperCase()}`,\n description: `Volume path for \"${key}\" (e.g. /Volumes/catalog/schema/volume_name)`,\n },\n },\n required: true,\n }));\n }\n\n /**\n * Extract user identity from the request.\n * Falls back to `getCurrentUserId()` in development mode.\n */\n private _extractUser(req: express.Request): FilePolicyUser {\n const userId = req.header(\"x-forwarded-user\")?.trim();\n if (userId) return { id: userId };\n if (process.env.NODE_ENV === \"development\") {\n logger.warn(\n \"No x-forwarded-user header — falling back to service principal identity for policy checks. \" +\n \"Ensure your proxy forwards user headers to test per-user policies.\",\n );\n return { id: getCurrentUserId() };\n }\n throw AuthenticationError.missingToken(\n \"Missing x-forwarded-user header. Cannot resolve user ID.\",\n );\n }\n\n /**\n * Check the policy for a volume. No-op if no policy is configured.\n * Throws `PolicyDeniedError` if denied.\n */\n private async _checkPolicy(\n volumeKey: string,\n action: FileAction,\n path: string,\n user: FilePolicyUser,\n resourceOverrides?: Partial<FileResource>,\n ): Promise<void> {\n const policyFn = this.volumeConfigs[volumeKey]?.policy;\n if (typeof policyFn !== \"function\") return;\n\n const resource: FileResource = {\n path,\n volume: volumeKey,\n ...resourceOverrides,\n };\n const allowed = await policyFn(action, resource, user);\n if (!allowed) {\n const userId = user.isServicePrincipal ? \"<service-principal>\" : user.id;\n logger.warn(\n 'Policy denied \"%s\" on volume \"%s\" for user \"%s\"',\n action,\n volumeKey,\n userId,\n );\n throw new PolicyDeniedError(action, volumeKey);\n }\n }\n\n /**\n * HTTP-level wrapper around `_checkPolicy`.\n * Extracts user (401 on failure), runs policy (403 on denial).\n * Returns `true` if the request may proceed, `false` if a response was sent.\n */\n private async _enforcePolicy(\n req: express.Request,\n res: express.Response,\n volumeKey: string,\n action: FileAction,\n path: string,\n resourceOverrides?: Partial<FileResource>,\n ): Promise<boolean> {\n let user: FilePolicyUser;\n try {\n user = this._extractUser(req);\n } catch (error) {\n if (error instanceof AuthenticationError) {\n res.status(401).json({ error: error.message, plugin: this.name });\n return false;\n }\n throw error;\n }\n\n try {\n await this._checkPolicy(volumeKey, action, path, user, resourceOverrides);\n } catch (error) {\n if (error instanceof PolicyDeniedError) {\n res.status(403).json({ error: error.message, plugin: this.name });\n return false;\n }\n // A crashing policy is treated as a server error (fail closed).\n logger.error(\"Policy function threw on volume %s: %O\", volumeKey, error);\n res.status(500).json({\n error: \"Policy evaluation failed\",\n plugin: this.name,\n });\n return false;\n }\n\n return true;\n }\n\n constructor(config: IFilesConfig) {\n super(config);\n this.config = config;\n\n if (config.customContentTypes) {\n validateCustomContentTypes(config.customContentTypes);\n }\n\n const volumes = FilesPlugin.discoverVolumes(config);\n this.volumeKeys = Object.keys(volumes);\n\n for (const key of this.volumeKeys) {\n const volumeCfg = volumes[key];\n const envVar = `DATABRICKS_VOLUME_${key.toUpperCase()}`;\n const volumePath = process.env[envVar];\n\n // Merge per-volume config with plugin-level defaults\n const mergedConfig: VolumeConfig = {\n maxUploadSize: volumeCfg.maxUploadSize ?? config.maxUploadSize,\n customContentTypes:\n volumeCfg.customContentTypes ?? config.customContentTypes,\n policy: volumeCfg.policy ?? policy.publicRead(),\n };\n this.volumeConfigs[key] = mergedConfig;\n\n this.volumeConnectors[key] = new FilesConnector({\n defaultVolume: volumePath,\n timeout: config.timeout,\n telemetry: config.telemetry,\n customContentTypes: mergedConfig.customContentTypes,\n });\n }\n\n // Warn at startup for volumes without an explicit policy\n for (const key of this.volumeKeys) {\n if (!volumes[key].policy) {\n logger.warn(\n 'Volume \"%s\" has no explicit policy — defaulting to publicRead(). ' +\n \"Set a policy in files({ volumes: { %s: { policy: ... } } }) to silence this warning.\",\n key,\n key,\n );\n }\n }\n }\n\n injectRoutes(router: IAppRouter) {\n this.route(router, {\n name: \"volumes\",\n method: \"get\",\n path: \"/volumes\",\n handler: async (_req: express.Request, res: express.Response) => {\n res.json({ volumes: this.volumeKeys });\n },\n });\n\n this.route(router, {\n name: \"list\",\n method: \"get\",\n path: \"/:volumeKey/list\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleList(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"read\",\n method: \"get\",\n path: \"/:volumeKey/read\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleRead(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"download\",\n method: \"get\",\n path: \"/:volumeKey/download\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleDownload(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"raw\",\n method: \"get\",\n path: \"/:volumeKey/raw\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleRaw(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"exists\",\n method: \"get\",\n path: \"/:volumeKey/exists\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleExists(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"metadata\",\n method: \"get\",\n path: \"/:volumeKey/metadata\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleMetadata(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"preview\",\n method: \"get\",\n path: \"/:volumeKey/preview\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handlePreview(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"upload\",\n method: \"post\",\n path: \"/:volumeKey/upload\",\n skipBodyParsing: true,\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleUpload(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"mkdir\",\n method: \"post\",\n path: \"/:volumeKey/mkdir\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleMkdir(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"delete\",\n method: \"delete\",\n path: \"/:volumeKey\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleDelete(req, res, connector, volumeKey);\n },\n });\n }\n\n /**\n * Resolve `:volumeKey` from the request. Returns the connector and key,\n * or sends a 404 and returns `{ connector: undefined }`.\n */\n private _resolveVolume(\n req: express.Request,\n res: express.Response,\n ):\n | { connector: FilesConnector; volumeKey: string }\n | { connector: undefined; volumeKey: undefined } {\n const volumeKey = req.params.volumeKey;\n const connector = this.volumeConnectors[volumeKey];\n if (!connector) {\n const safeKey = volumeKey.replace(/[^a-zA-Z0-9_-]/g, \"\");\n res.status(404).json({\n error: `Unknown volume \"${safeKey}\"`,\n plugin: this.name,\n });\n return { connector: undefined, volumeKey: undefined };\n }\n return { connector, volumeKey };\n }\n\n /**\n * Validate a file/directory path from user input.\n * Returns `true` if valid, or an error message string if invalid.\n */\n private _isValidPath(path: string | undefined): true | string {\n if (!path) return \"path is required\";\n if (path.length > 4096)\n return `path exceeds maximum length of 4096 characters (got ${path.length})`;\n if (path.includes(\"\\0\")) return \"path must not contain null bytes\";\n return true;\n }\n\n private _readSettings(\n cacheKey: (string | number | object)[],\n ): PluginExecutionSettings {\n return {\n default: {\n ...FILES_READ_DEFAULTS,\n cache: { ...FILES_READ_DEFAULTS.cache, cacheKey },\n },\n };\n }\n\n /**\n * Invalidate cached list entries for a directory after a write operation.\n * Uses the same cache-key format as `_handleList`: resolved path for\n * subdirectories, `\"__root__\"` for the volume root.\n *\n * Cache keys include `getCurrentUserId()` — must match the identity used\n * by `this.execute()` in `_handleList`. Both run in service-principal\n * context; wrapping either in `runInUserContext` would break invalidation.\n */\n private _invalidateListCache(\n volumeKey: string,\n parentPath: string,\n connector: FilesConnector,\n ): void {\n const parent = parentDirectory(parentPath);\n const cachePathSegment = parent\n ? connector.resolvePath(parent)\n : \"__root__\";\n const listKey = this.cache.generateKey(\n [`files:${volumeKey}:list`, cachePathSegment],\n getCurrentUserId(),\n );\n this.cache.delete(listKey);\n }\n\n private _handleApiError(\n res: express.Response,\n error: unknown,\n fallbackMessage: string,\n ): void {\n if (error instanceof PolicyDeniedError) {\n res.status(403).json({\n error: error.message,\n plugin: this.name,\n });\n return;\n }\n if (error instanceof AuthenticationError) {\n res.status(401).json({\n error: error.message,\n plugin: this.name,\n });\n return;\n }\n if (error instanceof ApiError) {\n const status = error.statusCode ?? 500;\n if (status >= 400 && status < 500) {\n res.status(status).json({\n error: error.message,\n statusCode: status,\n plugin: this.name,\n });\n return;\n }\n logger.error(\"Upstream server error in %s: %O\", this.name, error);\n res.status(500).json({ error: fallbackMessage, plugin: this.name });\n return;\n }\n logger.error(\"Unhandled error in %s: %O\", this.name, error);\n res.status(500).json({ error: fallbackMessage, plugin: this.name });\n }\n\n private _sendStatusError(res: express.Response, status: number): void {\n res.status(status).json({\n error: STATUS_CODES[status] ?? \"Unknown Error\",\n plugin: this.name,\n });\n }\n\n private async _handleList(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string | undefined;\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"list\", path ?? \"/\")))\n return;\n\n try {\n const result = await this.execute(\n async () => connector.list(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:list`,\n path ? connector.resolvePath(path) : \"__root__\",\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"List failed\");\n }\n }\n\n private async _handleRead(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"read\", path))) return;\n\n try {\n const result = await this.execute(\n async () => connector.read(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:read`,\n connector.resolvePath(path),\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.type(\"text/plain\").send(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Read failed\");\n }\n }\n\n private async _handleDownload(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n return this._serveFile(req, res, connector, volumeKey, {\n mode: \"download\",\n });\n }\n\n private async _handleRaw(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n return this._serveFile(req, res, connector, volumeKey, {\n mode: \"raw\",\n });\n }\n\n /**\n * Shared handler for `/download` and `/raw` endpoints.\n * - `download`: always forces `Content-Disposition: attachment`.\n * - `raw`: adds CSP sandbox; forces attachment only for unsafe content types.\n */\n private async _serveFile(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n opts: { mode: \"download\" | \"raw\" },\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, opts.mode, path)))\n return;\n\n const label = opts.mode === \"download\" ? \"Download\" : \"Raw fetch\";\n const volumeCfg = this.volumeConfigs[volumeKey];\n\n try {\n const settings: PluginExecutionSettings = {\n default: FILES_DOWNLOAD_DEFAULTS,\n };\n const response = await this.execute(\n async () => connector.download(getWorkspaceClient(), path),\n settings,\n );\n\n if (!response.ok) {\n this._sendStatusError(res, response.status);\n return;\n }\n\n const resolvedType = contentTypeFromPath(\n path,\n undefined,\n volumeCfg.customContentTypes,\n );\n const fileName = sanitizeFilename(path.split(\"/\").pop() ?? \"download\");\n\n res.setHeader(\"Content-Type\", resolvedType);\n res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n\n if (opts.mode === \"raw\") {\n res.setHeader(\"Content-Security-Policy\", \"sandbox\");\n if (!isSafeInlineContentType(resolvedType)) {\n res.setHeader(\n \"Content-Disposition\",\n `attachment; filename=\"${fileName}\"`,\n );\n }\n } else {\n res.setHeader(\n \"Content-Disposition\",\n `attachment; filename=\"${fileName}\"`,\n );\n }\n\n if (response.data.contents) {\n const nodeStream = Readable.fromWeb(\n response.data.contents as import(\"node:stream/web\").ReadableStream,\n );\n nodeStream.on(\"error\", (err) => {\n logger.error(\"Stream error during %s: %O\", opts.mode, err);\n if (!res.headersSent) {\n this._sendStatusError(res, 500);\n } else {\n res.destroy();\n }\n });\n nodeStream.pipe(res);\n } else {\n res.end();\n }\n } catch (error) {\n this._handleApiError(res, error, `${label} failed`);\n }\n }\n\n private async _handleExists(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"exists\", path)))\n return;\n\n try {\n const result = await this.execute(\n async () => connector.exists(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:exists`,\n connector.resolvePath(path),\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.json({ exists: result.data });\n } catch (error) {\n this._handleApiError(res, error, \"Exists check failed\");\n }\n }\n\n private async _handleMetadata(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"metadata\", path)))\n return;\n\n try {\n const result = await this.execute(\n async () => connector.metadata(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:metadata`,\n connector.resolvePath(path),\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Metadata fetch failed\");\n }\n }\n\n private async _handlePreview(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"preview\", path)))\n return;\n\n try {\n const result = await this.execute(\n async () => connector.preview(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:preview`,\n connector.resolvePath(path),\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Preview failed\");\n }\n }\n\n private async _handleUpload(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n const volumeCfg = this.volumeConfigs[volumeKey];\n const maxSize = volumeCfg.maxUploadSize ?? FILES_MAX_UPLOAD_SIZE;\n const rawContentLength = req.headers[\"content-length\"];\n let contentLength: number | undefined;\n\n if (typeof rawContentLength === \"string\" && rawContentLength.length > 0) {\n if (!/^\\d+$/.test(rawContentLength)) {\n res.status(400).json({\n error: \"Invalid Content-Length header.\",\n plugin: this.name,\n });\n return;\n }\n contentLength = Number(rawContentLength);\n }\n\n if (\n !(await this._enforcePolicy(req, res, volumeKey, \"upload\", path, {\n size: contentLength,\n }))\n )\n return;\n\n if (contentLength !== undefined && contentLength > maxSize) {\n res.status(413).json({\n error: `File size (${contentLength} bytes) exceeds maximum allowed size (${maxSize} bytes).`,\n plugin: this.name,\n });\n return;\n }\n\n logger.debug(req, \"Upload started: volume=%s path=%s\", volumeKey, path);\n\n try {\n const rawStream: ReadableStream<Uint8Array> = Readable.toWeb(req);\n\n let bytesReceived = 0;\n const webStream = rawStream.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n bytesReceived += chunk.byteLength;\n if (bytesReceived > maxSize) {\n controller.error(\n new Error(\n `Upload stream exceeds maximum allowed size (${maxSize} bytes)`,\n ),\n );\n return;\n }\n controller.enqueue(chunk);\n },\n }),\n );\n\n logger.debug(\n req,\n \"Upload body received: volume=%s path=%s, size=%d bytes\",\n volumeKey,\n path,\n contentLength ?? 0,\n );\n const settings: PluginExecutionSettings = {\n default: FILES_WRITE_DEFAULTS,\n };\n const result = await this.trackWrite(() =>\n this.execute(async () => {\n await connector.upload(getWorkspaceClient(), path, webStream);\n return { success: true as const };\n }, settings),\n );\n\n this._invalidateListCache(volumeKey, path, connector);\n\n if (!result.ok) {\n logger.error(\n req,\n \"Upload failed: volume=%s path=%s, size=%d bytes\",\n volumeKey,\n path,\n contentLength ?? 0,\n );\n this._sendStatusError(res, result.status);\n return;\n }\n\n logger.debug(req, \"Upload complete: volume=%s path=%s\", volumeKey, path);\n res.json(result.data);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"exceeds maximum allowed size\")\n ) {\n res.status(413).json({ error: error.message, plugin: this.name });\n return;\n }\n this._handleApiError(res, error, \"Upload failed\");\n }\n }\n\n private async _handleMkdir(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const dirPath =\n typeof req.body?.path === \"string\" ? req.body.path : undefined;\n\n const valid = this._isValidPath(dirPath);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"mkdir\", dirPath)))\n return;\n\n try {\n const settings: PluginExecutionSettings = {\n default: FILES_WRITE_DEFAULTS,\n };\n const result = await this.trackWrite(() =>\n this.execute(async () => {\n await connector.createDirectory(getWorkspaceClient(), dirPath);\n return { success: true as const };\n }, settings),\n );\n\n this._invalidateListCache(volumeKey, dirPath, connector);\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Create directory failed\");\n }\n }\n\n private async _handleDelete(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const rawPath = req.query.path as string | undefined;\n\n const valid = this._isValidPath(rawPath);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n const path = rawPath as string;\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"delete\", path)))\n return;\n\n try {\n const settings: PluginExecutionSettings = {\n default: FILES_WRITE_DEFAULTS,\n };\n const result = await this.trackWrite(() =>\n this.execute(async () => {\n await connector.delete(getWorkspaceClient(), path);\n return { success: true as const };\n }, settings),\n );\n\n this._invalidateListCache(volumeKey, path, connector);\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Delete failed\");\n }\n }\n\n /**\n * Creates a VolumeAPI for a specific volume key.\n *\n * By default, enforces the volume's policy before each operation.\n * Pass `bypassPolicy: true` to skip policy checks — useful for\n * background jobs or migrations that should bypass user-facing policies.\n *\n * @security When `bypassPolicy` is `true`, no policy enforcement runs.\n * Do not expose bypassed APIs to HTTP routes or end-user code paths.\n */\n protected createVolumeAPI(\n volumeKey: string,\n user: FilePolicyUser,\n options?: { bypassPolicy?: boolean },\n ): VolumeAPI {\n const connector = this.volumeConnectors[volumeKey];\n const noop = () => Promise.resolve();\n const check = options?.bypassPolicy\n ? noop\n : (action: FileAction, path: string, overrides?: Partial<FileResource>) =>\n this._checkPolicy(volumeKey, action, path, user, overrides);\n\n return {\n list: async (directoryPath?: string) => {\n await check(\"list\", directoryPath ?? \"/\");\n return connector.list(getWorkspaceClient(), directoryPath);\n },\n read: async (filePath: string, opts?: { maxSize?: number }) => {\n await check(\"read\", filePath);\n return connector.read(getWorkspaceClient(), filePath, opts);\n },\n download: async (filePath: string) => {\n await check(\"download\", filePath);\n return connector.download(getWorkspaceClient(), filePath);\n },\n exists: async (filePath: string) => {\n await check(\"exists\", filePath);\n return connector.exists(getWorkspaceClient(), filePath);\n },\n metadata: async (filePath: string) => {\n await check(\"metadata\", filePath);\n return connector.metadata(getWorkspaceClient(), filePath);\n },\n upload: async (\n filePath: string,\n contents: ReadableStream | Buffer | string,\n opts?: { overwrite?: boolean },\n ) => {\n await check(\"upload\", filePath);\n return connector.upload(getWorkspaceClient(), filePath, contents, opts);\n },\n createDirectory: async (directoryPath: string) => {\n await check(\"mkdir\", directoryPath);\n return connector.createDirectory(getWorkspaceClient(), directoryPath);\n },\n delete: async (filePath: string) => {\n await check(\"delete\", filePath);\n return connector.delete(getWorkspaceClient(), filePath);\n },\n preview: async (filePath: string) => {\n await check(\"preview\", filePath);\n return connector.preview(getWorkspaceClient(), filePath);\n },\n };\n }\n\n private inflightWrites = 0;\n\n private trackWrite<T>(fn: () => Promise<T>): Promise<T> {\n this.inflightWrites++;\n return fn().finally(() => {\n this.inflightWrites--;\n });\n }\n\n async shutdown(): Promise<void> {\n // Wait up to 10 seconds for in-flight write operations to finish\n const deadline = Date.now() + 10_000;\n while (this.inflightWrites > 0 && Date.now() < deadline) {\n logger.info(\n \"Waiting for %d in-flight write(s) to complete before shutdown…\",\n this.inflightWrites,\n );\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n if (this.inflightWrites > 0) {\n logger.warn(\n \"Shutdown deadline reached with %d in-flight write(s) still pending.\",\n this.inflightWrites,\n );\n }\n this.streamManager.abortAll();\n }\n\n /**\n * Returns the programmatic API for the Files plugin.\n * Callable with a volume key to get a volume-scoped handle.\n *\n * All operations execute as the service principal.\n * Use policies to control per-user access.\n *\n * @example\n * ```ts\n * // Service principal access\n * appKit.files(\"uploads\").list()\n *\n * // With policy: pass user identity for access control\n * appKit.files(\"uploads\").asUser(req).list()\n * ```\n */\n exports(): FilesExport {\n const resolveVolume = (volumeKey: string): VolumeHandle => {\n if (!this.volumeKeys.includes(volumeKey)) {\n throw new Error(\n `Unknown volume \"${volumeKey}\". Available volumes: ${this.volumeKeys.join(\", \")}`,\n );\n }\n\n // Lazy user resolution: getCurrentUserId() is called when a method\n // is invoked (policy check), not when exports() is called.\n const spUser: FilePolicyUser = {\n get id() {\n return getCurrentUserId();\n },\n isServicePrincipal: true,\n };\n const spApi = this.createVolumeAPI(volumeKey, spUser);\n\n return {\n ...spApi,\n asUser: (req: express.Request) => {\n const user = this._extractUser(req);\n return this.createVolumeAPI(volumeKey, user);\n },\n };\n };\n\n const filesExport = ((volumeKey: string) =>\n resolveVolume(volumeKey)) as FilesExport;\n filesExport.volume = resolveVolume;\n\n return filesExport;\n }\n\n clientConfig(): Record<string, unknown> {\n return { volumes: this.volumeKeys };\n }\n}\n\n/**\n * @internal\n */\nexport const files = Object.assign(toPlugin(FilesPlugin), { policy });\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","names":["manifest","files"],"sources":["../../../src/plugins/files/plugin.ts"],"sourcesContent":["import { STATUS_CODES } from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport { ApiError } from \"@databricks/sdk-experimental\";\nimport type express from \"express\";\nimport type {\n AgentToolDefinition,\n IAppRouter,\n PluginExecutionSettings,\n ToolProvider,\n} from \"shared\";\nimport { z } from \"zod\";\nimport {\n contentTypeFromPath,\n FilesConnector,\n isSafeInlineContentType,\n validateCustomContentTypes,\n} from \"../../connectors/files\";\nimport {\n getCurrentUserId,\n getExecutionContext,\n getWorkspaceClient,\n} from \"../../context\";\nimport { isUserContext } from \"../../context/user-context\";\nimport { buildToolkitEntries } from \"../../core/agent/build-toolkit\";\nimport {\n defineTool,\n executeFromRegistry,\n type ToolRegistry,\n toolsFromRegistry,\n} from \"../../core/agent/tools/define-tool\";\nimport { AuthenticationError } from \"../../errors\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest, ResourceRequirement } from \"../../registry\";\nimport { ResourceType } from \"../../registry\";\nimport {\n FILES_DOWNLOAD_DEFAULTS,\n FILES_MAX_UPLOAD_SIZE,\n FILES_READ_DEFAULTS,\n FILES_WRITE_DEFAULTS,\n} from \"./defaults\";\nimport { parentDirectory, sanitizeFilename } from \"./helpers\";\nimport manifest from \"./manifest.json\";\nimport {\n type FileAction,\n type FilePolicyUser,\n type FileResource,\n PolicyDeniedError,\n policy,\n} from \"./policy\";\nimport type {\n FilesExport,\n IFilesConfig,\n VolumeAPI,\n VolumeConfig,\n VolumeHandle,\n} from \"./types\";\n\nconst logger = createLogger(\"files\");\n\nexport class FilesPlugin extends Plugin implements ToolProvider {\n name = \"files\";\n\n /** Plugin manifest declaring metadata and resource requirements. */\n static manifest = manifest as PluginManifest;\n protected static description = \"Files plugin for Databricks file operations\";\n protected declare config: IFilesConfig;\n\n private volumeConnectors: Record<string, FilesConnector> = {};\n private volumeConfigs: Record<string, VolumeConfig> = {};\n private volumeKeys: string[] = [];\n private tools: ToolRegistry = {};\n\n /**\n * Scans `process.env` for `DATABRICKS_VOLUME_*` keys and merges them with\n * any explicitly configured volumes. Explicit config wins for per-volume\n * overrides; auto-discovered volumes get default `{}` config.\n */\n static discoverVolumes(config: IFilesConfig): Record<string, VolumeConfig> {\n const explicit = config.volumes ?? {};\n const discovered: Record<string, VolumeConfig> = {};\n\n const prefix = \"DATABRICKS_VOLUME_\";\n for (const key of Object.keys(process.env)) {\n if (!key.startsWith(prefix)) continue;\n const suffix = key.slice(prefix.length);\n if (!suffix) continue;\n if (!process.env[key]) continue;\n const volumeKey = suffix.toLowerCase();\n if (!(volumeKey in explicit)) {\n discovered[volumeKey] = {};\n }\n }\n\n return { ...discovered, ...explicit };\n }\n\n /**\n * Generates resource requirements dynamically from discovered + configured volumes.\n * Each volume key maps to a `DATABRICKS_VOLUME_{KEY_UPPERCASE}` env var.\n */\n static getResourceRequirements(config: IFilesConfig): ResourceRequirement[] {\n const volumes = FilesPlugin.discoverVolumes(config);\n return Object.keys(volumes).map((key) => ({\n type: ResourceType.VOLUME,\n alias: `volume-${key}`,\n resourceKey: `volume-${key}`,\n description: `Unity Catalog Volume for \"${key}\" file storage`,\n permission: \"WRITE_VOLUME\",\n fields: {\n path: {\n env: `DATABRICKS_VOLUME_${key.toUpperCase()}`,\n description: `Volume path for \"${key}\" (e.g. /Volumes/catalog/schema/volume_name)`,\n },\n },\n required: true,\n }));\n }\n\n /**\n * Extract user identity from the request.\n * Falls back to `getCurrentUserId()` in development mode.\n */\n private _extractUser(req: express.Request): FilePolicyUser {\n const userId = req.header(\"x-forwarded-user\")?.trim();\n if (userId) return { id: userId };\n if (process.env.NODE_ENV === \"development\") {\n logger.warn(\n \"No x-forwarded-user header — falling back to service principal identity for policy checks. \" +\n \"Ensure your proxy forwards user headers to test per-user policies.\",\n );\n return { id: getCurrentUserId() };\n }\n throw AuthenticationError.missingToken(\n \"Missing x-forwarded-user header. Cannot resolve user ID.\",\n );\n }\n\n /**\n * Check the policy for a volume. No-op if no policy is configured.\n * Throws `PolicyDeniedError` if denied.\n */\n private async _checkPolicy(\n volumeKey: string,\n action: FileAction,\n path: string,\n user: FilePolicyUser,\n resourceOverrides?: Partial<FileResource>,\n ): Promise<void> {\n const policyFn = this.volumeConfigs[volumeKey]?.policy;\n if (typeof policyFn !== \"function\") return;\n\n const resource: FileResource = {\n path,\n volume: volumeKey,\n ...resourceOverrides,\n };\n const allowed = await policyFn(action, resource, user);\n if (!allowed) {\n const userId = user.isServicePrincipal ? \"<service-principal>\" : user.id;\n logger.warn(\n 'Policy denied \"%s\" on volume \"%s\" for user \"%s\"',\n action,\n volumeKey,\n userId,\n );\n throw new PolicyDeniedError(action, volumeKey);\n }\n }\n\n /**\n * HTTP-level wrapper around `_checkPolicy`.\n * Extracts user (401 on failure), runs policy (403 on denial).\n * Returns `true` if the request may proceed, `false` if a response was sent.\n */\n private async _enforcePolicy(\n req: express.Request,\n res: express.Response,\n volumeKey: string,\n action: FileAction,\n path: string,\n resourceOverrides?: Partial<FileResource>,\n ): Promise<boolean> {\n let user: FilePolicyUser;\n try {\n user = this._extractUser(req);\n } catch (error) {\n if (error instanceof AuthenticationError) {\n res.status(401).json({ error: error.message, plugin: this.name });\n return false;\n }\n throw error;\n }\n\n try {\n await this._checkPolicy(volumeKey, action, path, user, resourceOverrides);\n } catch (error) {\n if (error instanceof PolicyDeniedError) {\n res.status(403).json({ error: error.message, plugin: this.name });\n return false;\n }\n // A crashing policy is treated as a server error (fail closed).\n logger.error(\"Policy function threw on volume %s: %O\", volumeKey, error);\n res.status(500).json({\n error: \"Policy evaluation failed\",\n plugin: this.name,\n });\n return false;\n }\n\n return true;\n }\n\n constructor(config: IFilesConfig) {\n super(config);\n this.config = config;\n\n if (config.customContentTypes) {\n validateCustomContentTypes(config.customContentTypes);\n }\n\n const volumes = FilesPlugin.discoverVolumes(config);\n this.volumeKeys = Object.keys(volumes);\n\n for (const key of this.volumeKeys) {\n const volumeCfg = volumes[key];\n const envVar = `DATABRICKS_VOLUME_${key.toUpperCase()}`;\n const volumePath = process.env[envVar];\n\n // Merge per-volume config with plugin-level defaults\n const mergedConfig: VolumeConfig = {\n maxUploadSize: volumeCfg.maxUploadSize ?? config.maxUploadSize,\n customContentTypes:\n volumeCfg.customContentTypes ?? config.customContentTypes,\n policy: volumeCfg.policy ?? policy.publicRead(),\n };\n this.volumeConfigs[key] = mergedConfig;\n\n this.volumeConnectors[key] = new FilesConnector({\n defaultVolume: volumePath,\n timeout: config.timeout,\n telemetry: config.telemetry,\n customContentTypes: mergedConfig.customContentTypes,\n });\n\n Object.assign(this.tools, this._defineVolumeTools(key));\n\n // Warn at startup for volumes without an explicit policy\n if (!volumeCfg.policy) {\n logger.warn(\n 'Volume \"%s\" has no explicit policy — defaulting to publicRead(). ' +\n \"Set a policy in files({ volumes: { %s: { policy: ... } } }) to silence this warning.\",\n key,\n key,\n );\n }\n }\n }\n\n injectRoutes(router: IAppRouter) {\n this.route(router, {\n name: \"volumes\",\n method: \"get\",\n path: \"/volumes\",\n handler: async (_req: express.Request, res: express.Response) => {\n res.json({ volumes: this.volumeKeys });\n },\n });\n\n this.route(router, {\n name: \"list\",\n method: \"get\",\n path: \"/:volumeKey/list\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleList(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"read\",\n method: \"get\",\n path: \"/:volumeKey/read\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleRead(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"download\",\n method: \"get\",\n path: \"/:volumeKey/download\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleDownload(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"raw\",\n method: \"get\",\n path: \"/:volumeKey/raw\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleRaw(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"exists\",\n method: \"get\",\n path: \"/:volumeKey/exists\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleExists(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"metadata\",\n method: \"get\",\n path: \"/:volumeKey/metadata\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleMetadata(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"preview\",\n method: \"get\",\n path: \"/:volumeKey/preview\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handlePreview(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"upload\",\n method: \"post\",\n path: \"/:volumeKey/upload\",\n skipBodyParsing: true,\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleUpload(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"mkdir\",\n method: \"post\",\n path: \"/:volumeKey/mkdir\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleMkdir(req, res, connector, volumeKey);\n },\n });\n\n this.route(router, {\n name: \"delete\",\n method: \"delete\",\n path: \"/:volumeKey\",\n handler: async (req: express.Request, res: express.Response) => {\n const { connector, volumeKey } = this._resolveVolume(req, res);\n if (!connector) return;\n await this._handleDelete(req, res, connector, volumeKey);\n },\n });\n }\n\n /**\n * Resolve `:volumeKey` from the request. Returns the connector and key,\n * or sends a 404 and returns `{ connector: undefined }`.\n */\n private _resolveVolume(\n req: express.Request,\n res: express.Response,\n ):\n | { connector: FilesConnector; volumeKey: string }\n | { connector: undefined; volumeKey: undefined } {\n const volumeKey = req.params.volumeKey;\n const connector = this.volumeConnectors[volumeKey];\n if (!connector) {\n const safeKey = volumeKey.replace(/[^a-zA-Z0-9_-]/g, \"\");\n res.status(404).json({\n error: `Unknown volume \"${safeKey}\"`,\n plugin: this.name,\n });\n return { connector: undefined, volumeKey: undefined };\n }\n return { connector, volumeKey };\n }\n\n /**\n * Validate a file/directory path from user input.\n * Returns `true` if valid, or an error message string if invalid.\n */\n private _isValidPath(path: string | undefined): true | string {\n if (!path) return \"path is required\";\n if (path.length > 4096)\n return `path exceeds maximum length of 4096 characters (got ${path.length})`;\n if (path.includes(\"\\0\")) return \"path must not contain null bytes\";\n return true;\n }\n\n private _readSettings(\n cacheKey: (string | number | object)[],\n ): PluginExecutionSettings {\n return {\n default: {\n ...FILES_READ_DEFAULTS,\n cache: { ...FILES_READ_DEFAULTS.cache, cacheKey },\n },\n };\n }\n\n /**\n * Invalidate cached list entries for a directory after a write operation.\n * Uses the same cache-key format as `_handleList`: resolved path for\n * subdirectories, `\"__root__\"` for the volume root.\n *\n * Cache keys include `getCurrentUserId()` — must match the identity used\n * by `this.execute()` in `_handleList`. Both run in service-principal\n * context; wrapping either in `runInUserContext` would break invalidation.\n */\n private _invalidateListCache(\n volumeKey: string,\n parentPath: string,\n connector: FilesConnector,\n ): void {\n const parent = parentDirectory(parentPath);\n const cachePathSegment = parent\n ? connector.resolvePath(parent)\n : \"__root__\";\n const listKey = this.cache.generateKey(\n [`files:${volumeKey}:list`, cachePathSegment],\n getCurrentUserId(),\n );\n this.cache.delete(listKey);\n }\n\n private _handleApiError(\n res: express.Response,\n error: unknown,\n fallbackMessage: string,\n ): void {\n if (error instanceof PolicyDeniedError) {\n res.status(403).json({\n error: error.message,\n plugin: this.name,\n });\n return;\n }\n if (error instanceof AuthenticationError) {\n res.status(401).json({\n error: error.message,\n plugin: this.name,\n });\n return;\n }\n if (error instanceof ApiError) {\n const status = error.statusCode ?? 500;\n if (status >= 400 && status < 500) {\n res.status(status).json({\n error: error.message,\n statusCode: status,\n plugin: this.name,\n });\n return;\n }\n logger.error(\"Upstream server error in %s: %O\", this.name, error);\n res.status(500).json({ error: fallbackMessage, plugin: this.name });\n return;\n }\n logger.error(\"Unhandled error in %s: %O\", this.name, error);\n res.status(500).json({ error: fallbackMessage, plugin: this.name });\n }\n\n private _sendStatusError(res: express.Response, status: number): void {\n res.status(status).json({\n error: STATUS_CODES[status] ?? \"Unknown Error\",\n plugin: this.name,\n });\n }\n\n private async _handleList(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string | undefined;\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"list\", path ?? \"/\")))\n return;\n\n try {\n const result = await this.execute(\n async () => connector.list(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:list`,\n path ? connector.resolvePath(path) : \"__root__\",\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"List failed\");\n }\n }\n\n private async _handleRead(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"read\", path))) return;\n\n try {\n const result = await this.execute(\n async () => connector.read(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:read`,\n connector.resolvePath(path),\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.type(\"text/plain\").send(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Read failed\");\n }\n }\n\n private async _handleDownload(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n return this._serveFile(req, res, connector, volumeKey, {\n mode: \"download\",\n });\n }\n\n private async _handleRaw(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n return this._serveFile(req, res, connector, volumeKey, {\n mode: \"raw\",\n });\n }\n\n /**\n * Shared handler for `/download` and `/raw` endpoints.\n * - `download`: always forces `Content-Disposition: attachment`.\n * - `raw`: adds CSP sandbox; forces attachment only for unsafe content types.\n */\n private async _serveFile(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n opts: { mode: \"download\" | \"raw\" },\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, opts.mode, path)))\n return;\n\n const label = opts.mode === \"download\" ? \"Download\" : \"Raw fetch\";\n const volumeCfg = this.volumeConfigs[volumeKey];\n\n try {\n const settings: PluginExecutionSettings = {\n default: FILES_DOWNLOAD_DEFAULTS,\n };\n const response = await this.execute(\n async () => connector.download(getWorkspaceClient(), path),\n settings,\n );\n\n if (!response.ok) {\n this._sendStatusError(res, response.status);\n return;\n }\n\n const resolvedType = contentTypeFromPath(\n path,\n undefined,\n volumeCfg.customContentTypes,\n );\n const fileName = sanitizeFilename(path.split(\"/\").pop() ?? \"download\");\n\n res.setHeader(\"Content-Type\", resolvedType);\n res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n\n if (opts.mode === \"raw\") {\n res.setHeader(\"Content-Security-Policy\", \"sandbox\");\n if (!isSafeInlineContentType(resolvedType)) {\n res.setHeader(\n \"Content-Disposition\",\n `attachment; filename=\"${fileName}\"`,\n );\n }\n } else {\n res.setHeader(\n \"Content-Disposition\",\n `attachment; filename=\"${fileName}\"`,\n );\n }\n\n if (response.data.contents) {\n const nodeStream = Readable.fromWeb(\n response.data.contents as import(\"node:stream/web\").ReadableStream,\n );\n nodeStream.on(\"error\", (err) => {\n logger.error(\"Stream error during %s: %O\", opts.mode, err);\n if (!res.headersSent) {\n this._sendStatusError(res, 500);\n } else {\n res.destroy();\n }\n });\n nodeStream.pipe(res);\n } else {\n res.end();\n }\n } catch (error) {\n this._handleApiError(res, error, `${label} failed`);\n }\n }\n\n private async _handleExists(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"exists\", path)))\n return;\n\n try {\n const result = await this.execute(\n async () => connector.exists(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:exists`,\n connector.resolvePath(path),\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.json({ exists: result.data });\n } catch (error) {\n this._handleApiError(res, error, \"Exists check failed\");\n }\n }\n\n private async _handleMetadata(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"metadata\", path)))\n return;\n\n try {\n const result = await this.execute(\n async () => connector.metadata(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:metadata`,\n connector.resolvePath(path),\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Metadata fetch failed\");\n }\n }\n\n private async _handlePreview(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"preview\", path)))\n return;\n\n try {\n const result = await this.execute(\n async () => connector.preview(getWorkspaceClient(), path),\n this._readSettings([\n `files:${volumeKey}:preview`,\n connector.resolvePath(path),\n ]),\n );\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Preview failed\");\n }\n }\n\n private async _handleUpload(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const path = req.query.path as string;\n const valid = this._isValidPath(path);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n const volumeCfg = this.volumeConfigs[volumeKey];\n const maxSize = volumeCfg.maxUploadSize ?? FILES_MAX_UPLOAD_SIZE;\n const rawContentLength = req.headers[\"content-length\"];\n let contentLength: number | undefined;\n\n if (typeof rawContentLength === \"string\" && rawContentLength.length > 0) {\n if (!/^\\d+$/.test(rawContentLength)) {\n res.status(400).json({\n error: \"Invalid Content-Length header.\",\n plugin: this.name,\n });\n return;\n }\n contentLength = Number(rawContentLength);\n }\n\n if (\n !(await this._enforcePolicy(req, res, volumeKey, \"upload\", path, {\n size: contentLength,\n }))\n )\n return;\n\n if (contentLength !== undefined && contentLength > maxSize) {\n res.status(413).json({\n error: `File size (${contentLength} bytes) exceeds maximum allowed size (${maxSize} bytes).`,\n plugin: this.name,\n });\n return;\n }\n\n logger.debug(req, \"Upload started: volume=%s path=%s\", volumeKey, path);\n\n try {\n const rawStream: ReadableStream<Uint8Array> = Readable.toWeb(req);\n\n let bytesReceived = 0;\n const webStream = rawStream.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n bytesReceived += chunk.byteLength;\n if (bytesReceived > maxSize) {\n controller.error(\n new Error(\n `Upload stream exceeds maximum allowed size (${maxSize} bytes)`,\n ),\n );\n return;\n }\n controller.enqueue(chunk);\n },\n }),\n );\n\n logger.debug(\n req,\n \"Upload body received: volume=%s path=%s, size=%d bytes\",\n volumeKey,\n path,\n contentLength ?? 0,\n );\n const settings: PluginExecutionSettings = {\n default: FILES_WRITE_DEFAULTS,\n };\n const result = await this.trackWrite(() =>\n this.execute(async () => {\n await connector.upload(getWorkspaceClient(), path, webStream);\n return { success: true as const };\n }, settings),\n );\n\n this._invalidateListCache(volumeKey, path, connector);\n\n if (!result.ok) {\n logger.error(\n req,\n \"Upload failed: volume=%s path=%s, size=%d bytes\",\n volumeKey,\n path,\n contentLength ?? 0,\n );\n this._sendStatusError(res, result.status);\n return;\n }\n\n logger.debug(req, \"Upload complete: volume=%s path=%s\", volumeKey, path);\n res.json(result.data);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"exceeds maximum allowed size\")\n ) {\n res.status(413).json({ error: error.message, plugin: this.name });\n return;\n }\n this._handleApiError(res, error, \"Upload failed\");\n }\n }\n\n private async _handleMkdir(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const dirPath =\n typeof req.body?.path === \"string\" ? req.body.path : undefined;\n\n const valid = this._isValidPath(dirPath);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"mkdir\", dirPath)))\n return;\n\n try {\n const settings: PluginExecutionSettings = {\n default: FILES_WRITE_DEFAULTS,\n };\n const result = await this.trackWrite(() =>\n this.execute(async () => {\n await connector.createDirectory(getWorkspaceClient(), dirPath);\n return { success: true as const };\n }, settings),\n );\n\n this._invalidateListCache(volumeKey, dirPath, connector);\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Create directory failed\");\n }\n }\n\n private async _handleDelete(\n req: express.Request,\n res: express.Response,\n connector: FilesConnector,\n volumeKey: string,\n ): Promise<void> {\n const rawPath = req.query.path as string | undefined;\n\n const valid = this._isValidPath(rawPath);\n if (valid !== true) {\n res.status(400).json({ error: valid, plugin: this.name });\n return;\n }\n const path = rawPath as string;\n\n if (!(await this._enforcePolicy(req, res, volumeKey, \"delete\", path)))\n return;\n\n try {\n const settings: PluginExecutionSettings = {\n default: FILES_WRITE_DEFAULTS,\n };\n const result = await this.trackWrite(() =>\n this.execute(async () => {\n await connector.delete(getWorkspaceClient(), path);\n return { success: true as const };\n }, settings),\n );\n\n this._invalidateListCache(volumeKey, path, connector);\n\n if (!result.ok) {\n this._sendStatusError(res, result.status);\n return;\n }\n\n res.json(result.data);\n } catch (error) {\n this._handleApiError(res, error, \"Delete failed\");\n }\n }\n\n /**\n * Creates a VolumeAPI for a specific volume key.\n *\n * By default, enforces the volume's policy before each operation.\n * Pass `bypassPolicy: true` to skip policy checks — useful for\n * background jobs or migrations that should bypass user-facing policies.\n *\n * @security When `bypassPolicy` is `true`, no policy enforcement runs.\n * Do not expose bypassed APIs to HTTP routes or end-user code paths.\n */\n protected createVolumeAPI(\n volumeKey: string,\n user: FilePolicyUser,\n options?: { bypassPolicy?: boolean },\n ): VolumeAPI {\n const connector = this.volumeConnectors[volumeKey];\n const noop = () => Promise.resolve();\n const check = options?.bypassPolicy\n ? noop\n : (action: FileAction, path: string, overrides?: Partial<FileResource>) =>\n this._checkPolicy(volumeKey, action, path, user, overrides);\n\n return {\n list: async (directoryPath?: string) => {\n await check(\"list\", directoryPath ?? \"/\");\n return connector.list(getWorkspaceClient(), directoryPath);\n },\n read: async (filePath: string, opts?: { maxSize?: number }) => {\n await check(\"read\", filePath);\n return connector.read(getWorkspaceClient(), filePath, opts);\n },\n download: async (filePath: string) => {\n await check(\"download\", filePath);\n return connector.download(getWorkspaceClient(), filePath);\n },\n exists: async (filePath: string) => {\n await check(\"exists\", filePath);\n return connector.exists(getWorkspaceClient(), filePath);\n },\n metadata: async (filePath: string) => {\n await check(\"metadata\", filePath);\n return connector.metadata(getWorkspaceClient(), filePath);\n },\n upload: async (\n filePath: string,\n contents: ReadableStream | Buffer | string,\n opts?: { overwrite?: boolean },\n ) => {\n await check(\"upload\", filePath);\n return connector.upload(getWorkspaceClient(), filePath, contents, opts);\n },\n createDirectory: async (directoryPath: string) => {\n await check(\"mkdir\", directoryPath);\n return connector.createDirectory(getWorkspaceClient(), directoryPath);\n },\n delete: async (filePath: string) => {\n await check(\"delete\", filePath);\n return connector.delete(getWorkspaceClient(), filePath);\n },\n preview: async (filePath: string) => {\n await check(\"preview\", filePath);\n return connector.preview(getWorkspaceClient(), filePath);\n },\n };\n }\n\n /**\n * Builds the agent-tool registry entries for a single volume. One set of\n * tools per configured volume, keyed by `${volumeKey}.${method}`.\n *\n * Each handler resolves the caller's identity from the current execution\n * context (OBO user when the agent run is wrapped in `asUser(req)`, service\n * principal otherwise in local dev) and dispatches through\n * `createVolumeAPI(volumeKey, user)` so the volume's policy is enforced\n * uniformly for agent and HTTP callers.\n */\n private _defineVolumeTools(volumeKey: string): ToolRegistry {\n const buildUser = (): FilePolicyUser => {\n const ctx = getExecutionContext();\n return isUserContext(ctx)\n ? { id: ctx.userId }\n : { id: ctx.serviceUserId, isServicePrincipal: true };\n };\n const api = () => this.createVolumeAPI(volumeKey, buildUser());\n return {\n [`${volumeKey}.list`]: defineTool({\n description: `List files and directories in the \"${volumeKey}\" volume`,\n schema: z.object({\n path: z\n .string()\n .optional()\n .describe(\"Directory path to list (optional, defaults to root)\"),\n }),\n annotations: { effect: \"read\", requiresUserContext: true },\n autoInheritable: true,\n execute: (args, signal) => {\n signal?.throwIfAborted();\n return api().list(args.path);\n },\n }),\n [`${volumeKey}.read`]: defineTool({\n description: `Read a text file from the \"${volumeKey}\" volume`,\n schema: z.object({\n path: z.string().describe(\"File path to read\"),\n }),\n annotations: { effect: \"read\", requiresUserContext: true },\n autoInheritable: true,\n execute: (args, signal) => {\n signal?.throwIfAborted();\n return api().read(args.path);\n },\n }),\n [`${volumeKey}.exists`]: defineTool({\n description: `Check if a file or directory exists in the \"${volumeKey}\" volume`,\n schema: z.object({\n path: z.string().describe(\"Path to check\"),\n }),\n annotations: { effect: \"read\", requiresUserContext: true },\n autoInheritable: true,\n execute: (args, signal) => {\n signal?.throwIfAborted();\n return api().exists(args.path);\n },\n }),\n [`${volumeKey}.metadata`]: defineTool({\n description: `Get metadata (size, type, last modified) for a file in the \"${volumeKey}\" volume`,\n schema: z.object({\n path: z.string().describe(\"File path\"),\n }),\n annotations: { effect: \"read\", requiresUserContext: true },\n autoInheritable: true,\n execute: (args, signal) => {\n signal?.throwIfAborted();\n return api().metadata(args.path);\n },\n }),\n [`${volumeKey}.upload`]: defineTool({\n description: `Upload a text file to the \"${volumeKey}\" volume`,\n schema: z.object({\n path: z.string().describe(\"Destination file path\"),\n contents: z.string().describe(\"File contents as a string\"),\n overwrite: z\n .boolean()\n .optional()\n .describe(\"Whether to overwrite existing file\"),\n }),\n annotations: { effect: \"destructive\", requiresUserContext: true },\n execute: (args, signal) => {\n signal?.throwIfAborted();\n return api().upload(args.path, args.contents, {\n overwrite: args.overwrite,\n });\n },\n }),\n [`${volumeKey}.delete`]: defineTool({\n description: `Delete a file from the \"${volumeKey}\" volume`,\n schema: z.object({\n path: z.string().describe(\"File path to delete\"),\n }),\n annotations: { effect: \"destructive\", requiresUserContext: true },\n execute: (args, signal) => {\n signal?.throwIfAborted();\n return api().delete(args.path);\n },\n }),\n };\n }\n\n private inflightWrites = 0;\n\n private trackWrite<T>(fn: () => Promise<T>): Promise<T> {\n this.inflightWrites++;\n return fn().finally(() => {\n this.inflightWrites--;\n });\n }\n\n async shutdown(): Promise<void> {\n // Wait up to 10 seconds for in-flight write operations to finish\n const deadline = Date.now() + 10_000;\n while (this.inflightWrites > 0 && Date.now() < deadline) {\n logger.info(\n \"Waiting for %d in-flight write(s) to complete before shutdown…\",\n this.inflightWrites,\n );\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n if (this.inflightWrites > 0) {\n logger.warn(\n \"Shutdown deadline reached with %d in-flight write(s) still pending.\",\n this.inflightWrites,\n );\n }\n this.streamManager.abortAll();\n }\n\n getAgentTools(): AgentToolDefinition[] {\n return toolsFromRegistry(this.tools);\n }\n\n async executeAgentTool(\n name: string,\n args: unknown,\n signal?: AbortSignal,\n ): Promise<unknown> {\n return executeFromRegistry(this.tools, name, args, signal);\n }\n\n toolkit(opts?: import(\"../../core/agent/types\").ToolkitOptions) {\n return buildToolkitEntries(this.name, this.tools, opts);\n }\n\n /**\n * Returns the programmatic API for the Files plugin.\n * Callable with a volume key to get a volume-scoped handle.\n *\n * All operations execute as the service principal.\n * Use policies to control per-user access.\n *\n * @example\n * ```ts\n * // Service principal access\n * appKit.files(\"uploads\").list()\n *\n * // With policy: pass user identity for access control\n * appKit.files(\"uploads\").asUser(req).list()\n * ```\n */\n exports(): FilesExport {\n const resolveVolume = (volumeKey: string): VolumeHandle => {\n if (!this.volumeKeys.includes(volumeKey)) {\n throw new Error(\n `Unknown volume \"${volumeKey}\". Available volumes: ${this.volumeKeys.join(\", \")}`,\n );\n }\n\n // Lazy user resolution: getCurrentUserId() is called when a method\n // is invoked (policy check), not when exports() is called.\n const spUser: FilePolicyUser = {\n get id() {\n return getCurrentUserId();\n },\n isServicePrincipal: true,\n };\n const spApi = this.createVolumeAPI(volumeKey, spUser);\n\n return {\n ...spApi,\n asUser: (req: express.Request) => {\n const user = this._extractUser(req);\n return this.createVolumeAPI(volumeKey, user);\n },\n };\n };\n\n const filesExport = ((volumeKey: string) =>\n resolveVolume(volumeKey)) as FilesExport;\n filesExport.volume = resolveVolume;\n\n return filesExport;\n }\n\n clientConfig(): Record<string, unknown> {\n return { volumes: this.volumeKeys };\n }\n}\n\n/**\n * @internal\n */\nexport const files = Object.assign(toPlugin(FilesPlugin), { policy });\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;cAqBuB;mBACoC;aAQR;AA4BnD,MAAM,SAAS,aAAa,QAAQ;AAEpC,IAAa,cAAb,MAAa,oBAAoB,OAA+B;CAC9D,OAAO;;CAGP,OAAO,WAAWA;CAClB,OAAiB,cAAc;CAG/B,AAAQ,mBAAmD,EAAE;CAC7D,AAAQ,gBAA8C,EAAE;CACxD,AAAQ,aAAuB,EAAE;CACjC,AAAQ,QAAsB,EAAE;;;;;;CAOhC,OAAO,gBAAgB,QAAoD;EACzE,MAAM,WAAW,OAAO,WAAW,EAAE;EACrC,MAAM,aAA2C,EAAE;EAEnD,MAAM,SAAS;AACf,OAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,IAAI,EAAE;AAC1C,OAAI,CAAC,IAAI,WAAW,OAAO,CAAE;GAC7B,MAAM,SAAS,IAAI,MAAM,GAAc;AACvC,OAAI,CAAC,OAAQ;AACb,OAAI,CAAC,QAAQ,IAAI,KAAM;GACvB,MAAM,YAAY,OAAO,aAAa;AACtC,OAAI,EAAE,aAAa,UACjB,YAAW,aAAa,EAAE;;AAI9B,SAAO;GAAE,GAAG;GAAY,GAAG;GAAU;;;;;;CAOvC,OAAO,wBAAwB,QAA6C;EAC1E,MAAM,UAAU,YAAY,gBAAgB,OAAO;AACnD,SAAO,OAAO,KAAK,QAAQ,CAAC,KAAK,SAAS;GACxC,MAAM,aAAa;GACnB,OAAO,UAAU;GACjB,aAAa,UAAU;GACvB,aAAa,6BAA6B,IAAI;GAC9C,YAAY;GACZ,QAAQ,EACN,MAAM;IACJ,KAAK,qBAAqB,IAAI,aAAa;IAC3C,aAAa,oBAAoB,IAAI;IACtC,EACF;GACD,UAAU;GACX,EAAE;;;;;;CAOL,AAAQ,aAAa,KAAsC;EACzD,MAAM,SAAS,IAAI,OAAO,mBAAmB,EAAE,MAAM;AACrD,MAAI,OAAQ,QAAO,EAAE,IAAI,QAAQ;AACjC,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,UAAO,KACL,gKAED;AACD,UAAO,EAAE,IAAI,kBAAkB,EAAE;;AAEnC,QAAM,oBAAoB,aACxB,2DACD;;;;;;CAOH,MAAc,aACZ,WACA,QACA,MACA,MACA,mBACe;EACf,MAAM,WAAW,KAAK,cAAc,YAAY;AAChD,MAAI,OAAO,aAAa,WAAY;AAQpC,MAAI,CADY,MAAM,SAAS,QALA;GAC7B;GACA,QAAQ;GACR,GAAG;GACJ,EACgD,KAAK,EACxC;GACZ,MAAM,SAAS,KAAK,qBAAqB,wBAAwB,KAAK;AACtE,UAAO,KACL,yDACA,QACA,WACA,OACD;AACD,SAAM,IAAI,kBAAkB,QAAQ,UAAU;;;;;;;;CASlD,MAAc,eACZ,KACA,KACA,WACA,QACA,MACA,mBACkB;EAClB,IAAI;AACJ,MAAI;AACF,UAAO,KAAK,aAAa,IAAI;WACtB,OAAO;AACd,OAAI,iBAAiB,qBAAqB;AACxC,QAAI,OAAO,IAAI,CAAC,KAAK;KAAE,OAAO,MAAM;KAAS,QAAQ,KAAK;KAAM,CAAC;AACjE,WAAO;;AAET,SAAM;;AAGR,MAAI;AACF,SAAM,KAAK,aAAa,WAAW,QAAQ,MAAM,MAAM,kBAAkB;WAClE,OAAO;AACd,OAAI,iBAAiB,mBAAmB;AACtC,QAAI,OAAO,IAAI,CAAC,KAAK;KAAE,OAAO,MAAM;KAAS,QAAQ,KAAK;KAAM,CAAC;AACjE,WAAO;;AAGT,UAAO,MAAM,0CAA0C,WAAW,MAAM;AACxE,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO;IACP,QAAQ,KAAK;IACd,CAAC;AACF,UAAO;;AAGT,SAAO;;CAGT,YAAY,QAAsB;AAChC,QAAM,OAAO;AACb,OAAK,SAAS;AAEd,MAAI,OAAO,mBACT,4BAA2B,OAAO,mBAAmB;EAGvD,MAAM,UAAU,YAAY,gBAAgB,OAAO;AACnD,OAAK,aAAa,OAAO,KAAK,QAAQ;AAEtC,OAAK,MAAM,OAAO,KAAK,YAAY;GACjC,MAAM,YAAY,QAAQ;GAC1B,MAAM,SAAS,qBAAqB,IAAI,aAAa;GACrD,MAAM,aAAa,QAAQ,IAAI;GAG/B,MAAM,eAA6B;IACjC,eAAe,UAAU,iBAAiB,OAAO;IACjD,oBACE,UAAU,sBAAsB,OAAO;IACzC,QAAQ,UAAU,UAAU,OAAO,YAAY;IAChD;AACD,QAAK,cAAc,OAAO;AAE1B,QAAK,iBAAiB,OAAO,IAAI,eAAe;IAC9C,eAAe;IACf,SAAS,OAAO;IAChB,WAAW,OAAO;IAClB,oBAAoB,aAAa;IAClC,CAAC;AAEF,UAAO,OAAO,KAAK,OAAO,KAAK,mBAAmB,IAAI,CAAC;AAGvD,OAAI,CAAC,UAAU,OACb,QAAO,KACL,2JAEA,KACA,IACD;;;CAKP,aAAa,QAAoB;AAC/B,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,MAAuB,QAA0B;AAC/D,QAAI,KAAK,EAAE,SAAS,KAAK,YAAY,CAAC;;GAEzC,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,YAAY,KAAK,KAAK,WAAW,UAAU;;GAEzD,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,YAAY,KAAK,KAAK,WAAW,UAAU;;GAEzD,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,gBAAgB,KAAK,KAAK,WAAW,UAAU;;GAE7D,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,WAAW,KAAK,KAAK,WAAW,UAAU;;GAExD,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,cAAc,KAAK,KAAK,WAAW,UAAU;;GAE3D,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,gBAAgB,KAAK,KAAK,WAAW,UAAU;;GAE7D,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,eAAe,KAAK,KAAK,WAAW,UAAU;;GAE5D,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,iBAAiB;GACjB,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,cAAc,KAAK,KAAK,WAAW,UAAU;;GAE3D,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,aAAa,KAAK,KAAK,WAAW,UAAU;;GAE1D,CAAC;AAEF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;IAC9D,MAAM,EAAE,WAAW,cAAc,KAAK,eAAe,KAAK,IAAI;AAC9D,QAAI,CAAC,UAAW;AAChB,UAAM,KAAK,cAAc,KAAK,KAAK,WAAW,UAAU;;GAE3D,CAAC;;;;;;CAOJ,AAAQ,eACN,KACA,KAGiD;EACjD,MAAM,YAAY,IAAI,OAAO;EAC7B,MAAM,YAAY,KAAK,iBAAiB;AACxC,MAAI,CAAC,WAAW;GACd,MAAM,UAAU,UAAU,QAAQ,mBAAmB,GAAG;AACxD,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO,mBAAmB,QAAQ;IAClC,QAAQ,KAAK;IACd,CAAC;AACF,UAAO;IAAE,WAAW;IAAW,WAAW;IAAW;;AAEvD,SAAO;GAAE;GAAW;GAAW;;;;;;CAOjC,AAAQ,aAAa,MAAyC;AAC5D,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,SAAS,KAChB,QAAO,uDAAuD,KAAK,OAAO;AAC5E,MAAI,KAAK,SAAS,KAAK,CAAE,QAAO;AAChC,SAAO;;CAGT,AAAQ,cACN,UACyB;AACzB,SAAO,EACL,SAAS;GACP,GAAG;GACH,OAAO;IAAE,GAAG,oBAAoB;IAAO;IAAU;GAClD,EACF;;;;;;;;;;;CAYH,AAAQ,qBACN,WACA,YACA,WACM;EACN,MAAM,SAAS,gBAAgB,WAAW;EAC1C,MAAM,mBAAmB,SACrB,UAAU,YAAY,OAAO,GAC7B;EACJ,MAAM,UAAU,KAAK,MAAM,YACzB,CAAC,SAAS,UAAU,QAAQ,iBAAiB,EAC7C,kBAAkB,CACnB;AACD,OAAK,MAAM,OAAO,QAAQ;;CAG5B,AAAQ,gBACN,KACA,OACA,iBACM;AACN,MAAI,iBAAiB,mBAAmB;AACtC,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO,MAAM;IACb,QAAQ,KAAK;IACd,CAAC;AACF;;AAEF,MAAI,iBAAiB,qBAAqB;AACxC,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO,MAAM;IACb,QAAQ,KAAK;IACd,CAAC;AACF;;AAEF,MAAI,iBAAiB,UAAU;GAC7B,MAAM,SAAS,MAAM,cAAc;AACnC,OAAI,UAAU,OAAO,SAAS,KAAK;AACjC,QAAI,OAAO,OAAO,CAAC,KAAK;KACtB,OAAO,MAAM;KACb,YAAY;KACZ,QAAQ,KAAK;KACd,CAAC;AACF;;AAEF,UAAO,MAAM,mCAAmC,KAAK,MAAM,MAAM;AACjE,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAiB,QAAQ,KAAK;IAAM,CAAC;AACnE;;AAEF,SAAO,MAAM,6BAA6B,KAAK,MAAM,MAAM;AAC3D,MAAI,OAAO,IAAI,CAAC,KAAK;GAAE,OAAO;GAAiB,QAAQ,KAAK;GAAM,CAAC;;CAGrE,AAAQ,iBAAiB,KAAuB,QAAsB;AACpE,MAAI,OAAO,OAAO,CAAC,KAAK;GACtB,OAAO,aAAa,WAAW;GAC/B,QAAQ,KAAK;GACd,CAAC;;CAGJ,MAAc,YACZ,KACA,KACA,WACA,WACe;EACf,MAAM,OAAO,IAAI,MAAM;AAEvB,MAAI,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,QAAQ,QAAQ,IAAI,CACvE;AAEF,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,QACxB,YAAY,UAAU,KAAK,oBAAoB,EAAE,KAAK,EACtD,KAAK,cAAc,CACjB,SAAS,UAAU,QACnB,OAAO,UAAU,YAAY,KAAK,GAAG,WACtC,CAAC,CACH;AAED,OAAI,CAAC,OAAO,IAAI;AACd,SAAK,iBAAiB,KAAK,OAAO,OAAO;AACzC;;AAEF,OAAI,KAAK,OAAO,KAAK;WACd,OAAO;AACd,QAAK,gBAAgB,KAAK,OAAO,cAAc;;;CAInD,MAAc,YACZ,KACA,KACA,WACA,WACe;EACf,MAAM,OAAO,IAAI,MAAM;EAEvB,MAAM,QAAQ,KAAK,aAAa,KAAK;AACrC,MAAI,UAAU,MAAM;AAClB,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAO,QAAQ,KAAK;IAAM,CAAC;AACzD;;AAGF,MAAI,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,QAAQ,KAAK,CAAG;AAErE,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,QACxB,YAAY,UAAU,KAAK,oBAAoB,EAAE,KAAK,EACtD,KAAK,cAAc,CACjB,SAAS,UAAU,QACnB,UAAU,YAAY,KAAK,CAC5B,CAAC,CACH;AAED,OAAI,CAAC,OAAO,IAAI;AACd,SAAK,iBAAiB,KAAK,OAAO,OAAO;AACzC;;AAEF,OAAI,KAAK,aAAa,CAAC,KAAK,OAAO,KAAK;WACjC,OAAO;AACd,QAAK,gBAAgB,KAAK,OAAO,cAAc;;;CAInD,MAAc,gBACZ,KACA,KACA,WACA,WACe;AACf,SAAO,KAAK,WAAW,KAAK,KAAK,WAAW,WAAW,EACrD,MAAM,YACP,CAAC;;CAGJ,MAAc,WACZ,KACA,KACA,WACA,WACe;AACf,SAAO,KAAK,WAAW,KAAK,KAAK,WAAW,WAAW,EACrD,MAAM,OACP,CAAC;;;;;;;CAQJ,MAAc,WACZ,KACA,KACA,WACA,WACA,MACe;EACf,MAAM,OAAO,IAAI,MAAM;EAEvB,MAAM,QAAQ,KAAK,aAAa,KAAK;AACrC,MAAI,UAAU,MAAM;AAClB,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAO,QAAQ,KAAK;IAAM,CAAC;AACzD;;AAGF,MAAI,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,KAAK,MAAM,KAAK,CACnE;EAEF,MAAM,QAAQ,KAAK,SAAS,aAAa,aAAa;EACtD,MAAM,YAAY,KAAK,cAAc;AAErC,MAAI;GACF,MAAM,WAAoC,EACxC,SAAS,yBACV;GACD,MAAM,WAAW,MAAM,KAAK,QAC1B,YAAY,UAAU,SAAS,oBAAoB,EAAE,KAAK,EAC1D,SACD;AAED,OAAI,CAAC,SAAS,IAAI;AAChB,SAAK,iBAAiB,KAAK,SAAS,OAAO;AAC3C;;GAGF,MAAM,eAAe,oBACnB,MACA,QACA,UAAU,mBACX;GACD,MAAM,WAAW,iBAAiB,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,WAAW;AAEtE,OAAI,UAAU,gBAAgB,aAAa;AAC3C,OAAI,UAAU,0BAA0B,UAAU;AAElD,OAAI,KAAK,SAAS,OAAO;AACvB,QAAI,UAAU,2BAA2B,UAAU;AACnD,QAAI,CAAC,wBAAwB,aAAa,CACxC,KAAI,UACF,uBACA,yBAAyB,SAAS,GACnC;SAGH,KAAI,UACF,uBACA,yBAAyB,SAAS,GACnC;AAGH,OAAI,SAAS,KAAK,UAAU;IAC1B,MAAM,aAAa,SAAS,QAC1B,SAAS,KAAK,SACf;AACD,eAAW,GAAG,UAAU,QAAQ;AAC9B,YAAO,MAAM,8BAA8B,KAAK,MAAM,IAAI;AAC1D,SAAI,CAAC,IAAI,YACP,MAAK,iBAAiB,KAAK,IAAI;SAE/B,KAAI,SAAS;MAEf;AACF,eAAW,KAAK,IAAI;SAEpB,KAAI,KAAK;WAEJ,OAAO;AACd,QAAK,gBAAgB,KAAK,OAAO,GAAG,MAAM,SAAS;;;CAIvD,MAAc,cACZ,KACA,KACA,WACA,WACe;EACf,MAAM,OAAO,IAAI,MAAM;EAEvB,MAAM,QAAQ,KAAK,aAAa,KAAK;AACrC,MAAI,UAAU,MAAM;AAClB,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAO,QAAQ,KAAK;IAAM,CAAC;AACzD;;AAGF,MAAI,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,UAAU,KAAK,CAClE;AAEF,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,QACxB,YAAY,UAAU,OAAO,oBAAoB,EAAE,KAAK,EACxD,KAAK,cAAc,CACjB,SAAS,UAAU,UACnB,UAAU,YAAY,KAAK,CAC5B,CAAC,CACH;AAED,OAAI,CAAC,OAAO,IAAI;AACd,SAAK,iBAAiB,KAAK,OAAO,OAAO;AACzC;;AAEF,OAAI,KAAK,EAAE,QAAQ,OAAO,MAAM,CAAC;WAC1B,OAAO;AACd,QAAK,gBAAgB,KAAK,OAAO,sBAAsB;;;CAI3D,MAAc,gBACZ,KACA,KACA,WACA,WACe;EACf,MAAM,OAAO,IAAI,MAAM;EAEvB,MAAM,QAAQ,KAAK,aAAa,KAAK;AACrC,MAAI,UAAU,MAAM;AAClB,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAO,QAAQ,KAAK;IAAM,CAAC;AACzD;;AAGF,MAAI,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,YAAY,KAAK,CACpE;AAEF,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,QACxB,YAAY,UAAU,SAAS,oBAAoB,EAAE,KAAK,EAC1D,KAAK,cAAc,CACjB,SAAS,UAAU,YACnB,UAAU,YAAY,KAAK,CAC5B,CAAC,CACH;AAED,OAAI,CAAC,OAAO,IAAI;AACd,SAAK,iBAAiB,KAAK,OAAO,OAAO;AACzC;;AAEF,OAAI,KAAK,OAAO,KAAK;WACd,OAAO;AACd,QAAK,gBAAgB,KAAK,OAAO,wBAAwB;;;CAI7D,MAAc,eACZ,KACA,KACA,WACA,WACe;EACf,MAAM,OAAO,IAAI,MAAM;EAEvB,MAAM,QAAQ,KAAK,aAAa,KAAK;AACrC,MAAI,UAAU,MAAM;AAClB,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAO,QAAQ,KAAK;IAAM,CAAC;AACzD;;AAGF,MAAI,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,WAAW,KAAK,CACnE;AAEF,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,QACxB,YAAY,UAAU,QAAQ,oBAAoB,EAAE,KAAK,EACzD,KAAK,cAAc,CACjB,SAAS,UAAU,WACnB,UAAU,YAAY,KAAK,CAC5B,CAAC,CACH;AAED,OAAI,CAAC,OAAO,IAAI;AACd,SAAK,iBAAiB,KAAK,OAAO,OAAO;AACzC;;AAEF,OAAI,KAAK,OAAO,KAAK;WACd,OAAO;AACd,QAAK,gBAAgB,KAAK,OAAO,iBAAiB;;;CAItD,MAAc,cACZ,KACA,KACA,WACA,WACe;EACf,MAAM,OAAO,IAAI,MAAM;EACvB,MAAM,QAAQ,KAAK,aAAa,KAAK;AACrC,MAAI,UAAU,MAAM;AAClB,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAO,QAAQ,KAAK;IAAM,CAAC;AACzD;;EAIF,MAAM,UADY,KAAK,cAAc,WACX,iBAAiB;EAC3C,MAAM,mBAAmB,IAAI,QAAQ;EACrC,IAAI;AAEJ,MAAI,OAAO,qBAAqB,YAAY,iBAAiB,SAAS,GAAG;AACvE,OAAI,CAAC,QAAQ,KAAK,iBAAiB,EAAE;AACnC,QAAI,OAAO,IAAI,CAAC,KAAK;KACnB,OAAO;KACP,QAAQ,KAAK;KACd,CAAC;AACF;;AAEF,mBAAgB,OAAO,iBAAiB;;AAG1C,MACE,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,UAAU,MAAM,EAC/D,MAAM,eACP,CAAC,CAEF;AAEF,MAAI,kBAAkB,UAAa,gBAAgB,SAAS;AAC1D,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO,cAAc,cAAc,wCAAwC,QAAQ;IACnF,QAAQ,KAAK;IACd,CAAC;AACF;;AAGF,SAAO,MAAM,KAAK,qCAAqC,WAAW,KAAK;AAEvE,MAAI;GACF,MAAM,YAAwC,SAAS,MAAM,IAAI;GAEjE,IAAI,gBAAgB;GACpB,MAAM,YAAY,UAAU,YAC1B,IAAI,gBAAwC,EAC1C,UAAU,OAAO,YAAY;AAC3B,qBAAiB,MAAM;AACvB,QAAI,gBAAgB,SAAS;AAC3B,gBAAW,sBACT,IAAI,MACF,+CAA+C,QAAQ,SACxD,CACF;AACD;;AAEF,eAAW,QAAQ,MAAM;MAE5B,CAAC,CACH;AAED,UAAO,MACL,KACA,0DACA,WACA,MACA,iBAAiB,EAClB;GACD,MAAM,WAAoC,EACxC,SAAS,sBACV;GACD,MAAM,SAAS,MAAM,KAAK,iBACxB,KAAK,QAAQ,YAAY;AACvB,UAAM,UAAU,OAAO,oBAAoB,EAAE,MAAM,UAAU;AAC7D,WAAO,EAAE,SAAS,MAAe;MAChC,SAAS,CACb;AAED,QAAK,qBAAqB,WAAW,MAAM,UAAU;AAErD,OAAI,CAAC,OAAO,IAAI;AACd,WAAO,MACL,KACA,mDACA,WACA,MACA,iBAAiB,EAClB;AACD,SAAK,iBAAiB,KAAK,OAAO,OAAO;AACzC;;AAGF,UAAO,MAAM,KAAK,sCAAsC,WAAW,KAAK;AACxE,OAAI,KAAK,OAAO,KAAK;WACd,OAAO;AACd,OACE,iBAAiB,SACjB,MAAM,QAAQ,SAAS,+BAA+B,EACtD;AACA,QAAI,OAAO,IAAI,CAAC,KAAK;KAAE,OAAO,MAAM;KAAS,QAAQ,KAAK;KAAM,CAAC;AACjE;;AAEF,QAAK,gBAAgB,KAAK,OAAO,gBAAgB;;;CAIrD,MAAc,aACZ,KACA,KACA,WACA,WACe;EACf,MAAM,UACJ,OAAO,IAAI,MAAM,SAAS,WAAW,IAAI,KAAK,OAAO;EAEvD,MAAM,QAAQ,KAAK,aAAa,QAAQ;AACxC,MAAI,UAAU,MAAM;AAClB,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAO,QAAQ,KAAK;IAAM,CAAC;AACzD;;AAGF,MAAI,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,SAAS,QAAQ,CACpE;AAEF,MAAI;GACF,MAAM,WAAoC,EACxC,SAAS,sBACV;GACD,MAAM,SAAS,MAAM,KAAK,iBACxB,KAAK,QAAQ,YAAY;AACvB,UAAM,UAAU,gBAAgB,oBAAoB,EAAE,QAAQ;AAC9D,WAAO,EAAE,SAAS,MAAe;MAChC,SAAS,CACb;AAED,QAAK,qBAAqB,WAAW,SAAS,UAAU;AAExD,OAAI,CAAC,OAAO,IAAI;AACd,SAAK,iBAAiB,KAAK,OAAO,OAAO;AACzC;;AAGF,OAAI,KAAK,OAAO,KAAK;WACd,OAAO;AACd,QAAK,gBAAgB,KAAK,OAAO,0BAA0B;;;CAI/D,MAAc,cACZ,KACA,KACA,WACA,WACe;EACf,MAAM,UAAU,IAAI,MAAM;EAE1B,MAAM,QAAQ,KAAK,aAAa,QAAQ;AACxC,MAAI,UAAU,MAAM;AAClB,OAAI,OAAO,IAAI,CAAC,KAAK;IAAE,OAAO;IAAO,QAAQ,KAAK;IAAM,CAAC;AACzD;;EAEF,MAAM,OAAO;AAEb,MAAI,CAAE,MAAM,KAAK,eAAe,KAAK,KAAK,WAAW,UAAU,KAAK,CAClE;AAEF,MAAI;GACF,MAAM,WAAoC,EACxC,SAAS,sBACV;GACD,MAAM,SAAS,MAAM,KAAK,iBACxB,KAAK,QAAQ,YAAY;AACvB,UAAM,UAAU,OAAO,oBAAoB,EAAE,KAAK;AAClD,WAAO,EAAE,SAAS,MAAe;MAChC,SAAS,CACb;AAED,QAAK,qBAAqB,WAAW,MAAM,UAAU;AAErD,OAAI,CAAC,OAAO,IAAI;AACd,SAAK,iBAAiB,KAAK,OAAO,OAAO;AACzC;;AAGF,OAAI,KAAK,OAAO,KAAK;WACd,OAAO;AACd,QAAK,gBAAgB,KAAK,OAAO,gBAAgB;;;;;;;;;;;;;CAcrD,AAAU,gBACR,WACA,MACA,SACW;EACX,MAAM,YAAY,KAAK,iBAAiB;EACxC,MAAM,aAAa,QAAQ,SAAS;EACpC,MAAM,QAAQ,SAAS,eACnB,QACC,QAAoB,MAAc,cACjC,KAAK,aAAa,WAAW,QAAQ,MAAM,MAAM,UAAU;AAEjE,SAAO;GACL,MAAM,OAAO,kBAA2B;AACtC,UAAM,MAAM,QAAQ,iBAAiB,IAAI;AACzC,WAAO,UAAU,KAAK,oBAAoB,EAAE,cAAc;;GAE5D,MAAM,OAAO,UAAkB,SAAgC;AAC7D,UAAM,MAAM,QAAQ,SAAS;AAC7B,WAAO,UAAU,KAAK,oBAAoB,EAAE,UAAU,KAAK;;GAE7D,UAAU,OAAO,aAAqB;AACpC,UAAM,MAAM,YAAY,SAAS;AACjC,WAAO,UAAU,SAAS,oBAAoB,EAAE,SAAS;;GAE3D,QAAQ,OAAO,aAAqB;AAClC,UAAM,MAAM,UAAU,SAAS;AAC/B,WAAO,UAAU,OAAO,oBAAoB,EAAE,SAAS;;GAEzD,UAAU,OAAO,aAAqB;AACpC,UAAM,MAAM,YAAY,SAAS;AACjC,WAAO,UAAU,SAAS,oBAAoB,EAAE,SAAS;;GAE3D,QAAQ,OACN,UACA,UACA,SACG;AACH,UAAM,MAAM,UAAU,SAAS;AAC/B,WAAO,UAAU,OAAO,oBAAoB,EAAE,UAAU,UAAU,KAAK;;GAEzE,iBAAiB,OAAO,kBAA0B;AAChD,UAAM,MAAM,SAAS,cAAc;AACnC,WAAO,UAAU,gBAAgB,oBAAoB,EAAE,cAAc;;GAEvE,QAAQ,OAAO,aAAqB;AAClC,UAAM,MAAM,UAAU,SAAS;AAC/B,WAAO,UAAU,OAAO,oBAAoB,EAAE,SAAS;;GAEzD,SAAS,OAAO,aAAqB;AACnC,UAAM,MAAM,WAAW,SAAS;AAChC,WAAO,UAAU,QAAQ,oBAAoB,EAAE,SAAS;;GAE3D;;;;;;;;;;;;CAaH,AAAQ,mBAAmB,WAAiC;EAC1D,MAAM,kBAAkC;GACtC,MAAM,MAAM,qBAAqB;AACjC,UAAO,cAAc,IAAI,GACrB,EAAE,IAAI,IAAI,QAAQ,GAClB;IAAE,IAAI,IAAI;IAAe,oBAAoB;IAAM;;EAEzD,MAAM,YAAY,KAAK,gBAAgB,WAAW,WAAW,CAAC;AAC9D,SAAO;IACJ,GAAG,UAAU,SAAS,WAAW;IAChC,aAAa,sCAAsC,UAAU;IAC7D,QAAQ,EAAE,OAAO,EACf,MAAM,EACH,QAAQ,CACR,UAAU,CACV,SAAS,sDAAsD,EACnE,CAAC;IACF,aAAa;KAAE,QAAQ;KAAQ,qBAAqB;KAAM;IAC1D,iBAAiB;IACjB,UAAU,MAAM,WAAW;AACzB,aAAQ,gBAAgB;AACxB,YAAO,KAAK,CAAC,KAAK,KAAK,KAAK;;IAE/B,CAAC;IACD,GAAG,UAAU,SAAS,WAAW;IAChC,aAAa,8BAA8B,UAAU;IACrD,QAAQ,EAAE,OAAO,EACf,MAAM,EAAE,QAAQ,CAAC,SAAS,oBAAoB,EAC/C,CAAC;IACF,aAAa;KAAE,QAAQ;KAAQ,qBAAqB;KAAM;IAC1D,iBAAiB;IACjB,UAAU,MAAM,WAAW;AACzB,aAAQ,gBAAgB;AACxB,YAAO,KAAK,CAAC,KAAK,KAAK,KAAK;;IAE/B,CAAC;IACD,GAAG,UAAU,WAAW,WAAW;IAClC,aAAa,+CAA+C,UAAU;IACtE,QAAQ,EAAE,OAAO,EACf,MAAM,EAAE,QAAQ,CAAC,SAAS,gBAAgB,EAC3C,CAAC;IACF,aAAa;KAAE,QAAQ;KAAQ,qBAAqB;KAAM;IAC1D,iBAAiB;IACjB,UAAU,MAAM,WAAW;AACzB,aAAQ,gBAAgB;AACxB,YAAO,KAAK,CAAC,OAAO,KAAK,KAAK;;IAEjC,CAAC;IACD,GAAG,UAAU,aAAa,WAAW;IACpC,aAAa,+DAA+D,UAAU;IACtF,QAAQ,EAAE,OAAO,EACf,MAAM,EAAE,QAAQ,CAAC,SAAS,YAAY,EACvC,CAAC;IACF,aAAa;KAAE,QAAQ;KAAQ,qBAAqB;KAAM;IAC1D,iBAAiB;IACjB,UAAU,MAAM,WAAW;AACzB,aAAQ,gBAAgB;AACxB,YAAO,KAAK,CAAC,SAAS,KAAK,KAAK;;IAEnC,CAAC;IACD,GAAG,UAAU,WAAW,WAAW;IAClC,aAAa,8BAA8B,UAAU;IACrD,QAAQ,EAAE,OAAO;KACf,MAAM,EAAE,QAAQ,CAAC,SAAS,wBAAwB;KAClD,UAAU,EAAE,QAAQ,CAAC,SAAS,4BAA4B;KAC1D,WAAW,EACR,SAAS,CACT,UAAU,CACV,SAAS,qCAAqC;KAClD,CAAC;IACF,aAAa;KAAE,QAAQ;KAAe,qBAAqB;KAAM;IACjE,UAAU,MAAM,WAAW;AACzB,aAAQ,gBAAgB;AACxB,YAAO,KAAK,CAAC,OAAO,KAAK,MAAM,KAAK,UAAU,EAC5C,WAAW,KAAK,WACjB,CAAC;;IAEL,CAAC;IACD,GAAG,UAAU,WAAW,WAAW;IAClC,aAAa,2BAA2B,UAAU;IAClD,QAAQ,EAAE,OAAO,EACf,MAAM,EAAE,QAAQ,CAAC,SAAS,sBAAsB,EACjD,CAAC;IACF,aAAa;KAAE,QAAQ;KAAe,qBAAqB;KAAM;IACjE,UAAU,MAAM,WAAW;AACzB,aAAQ,gBAAgB;AACxB,YAAO,KAAK,CAAC,OAAO,KAAK,KAAK;;IAEjC,CAAC;GACH;;CAGH,AAAQ,iBAAiB;CAEzB,AAAQ,WAAc,IAAkC;AACtD,OAAK;AACL,SAAO,IAAI,CAAC,cAAc;AACxB,QAAK;IACL;;CAGJ,MAAM,WAA0B;EAE9B,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,SAAO,KAAK,iBAAiB,KAAK,KAAK,KAAK,GAAG,UAAU;AACvD,UAAO,KACL,kEACA,KAAK,eACN;AACD,SAAM,IAAI,SAAS,YAAY,WAAW,SAAS,IAAI,CAAC;;AAE1D,MAAI,KAAK,iBAAiB,EACxB,QAAO,KACL,uEACA,KAAK,eACN;AAEH,OAAK,cAAc,UAAU;;CAG/B,gBAAuC;AACrC,SAAO,kBAAkB,KAAK,MAAM;;CAGtC,MAAM,iBACJ,MACA,MACA,QACkB;AAClB,SAAO,oBAAoB,KAAK,OAAO,MAAM,MAAM,OAAO;;CAG5D,QAAQ,MAAwD;AAC9D,SAAO,oBAAoB,KAAK,MAAM,KAAK,OAAO,KAAK;;;;;;;;;;;;;;;;;;CAmBzD,UAAuB;EACrB,MAAM,iBAAiB,cAAoC;AACzD,OAAI,CAAC,KAAK,WAAW,SAAS,UAAU,CACtC,OAAM,IAAI,MACR,mBAAmB,UAAU,wBAAwB,KAAK,WAAW,KAAK,KAAK,GAChF;AAaH,UAAO;IACL,GAHY,KAAK,gBAAgB,WANJ;KAC7B,IAAI,KAAK;AACP,aAAO,kBAAkB;;KAE3B,oBAAoB;KACrB,CACoD;IAInD,SAAS,QAAyB;KAChC,MAAM,OAAO,KAAK,aAAa,IAAI;AACnC,YAAO,KAAK,gBAAgB,WAAW,KAAK;;IAE/C;;EAGH,MAAM,gBAAgB,cACpB,cAAc,UAAU;AAC1B,cAAY,SAAS;AAErB,SAAO;;CAGT,eAAwC;AACtC,SAAO,EAAE,SAAS,KAAK,YAAY;;;;;;AAOvC,MAAaC,UAAQ,OAAO,OAAO,SAAS,YAAY,EAAE,EAAE,QAAQ,CAAC"}
@@ -1,41 +1,55 @@
1
+ import { AgentToolDefinition, ToolProvider } from "../../shared/src/agent.js";
1
2
  import { IAppRouter, ToPlugin } from "../../shared/src/plugin.js";
2
3
  import { GenieStreamEvent } from "../../shared/src/genie.js";
3
4
  import "../../shared/src/index.js";
5
+ import { ToolkitEntry, ToolkitOptions } from "../../core/agent/types.js";
4
6
  import { Plugin } from "../../plugin/plugin.js";
5
7
  import "../../plugin/index.js";
6
8
  import { PluginManifest } from "../../registry/types.js";
7
9
  import "../../registry/index.js";
10
+ import "../agents/index.js";
8
11
  import { GenieConversationHistoryResponse } from "../../connectors/genie/types.js";
9
12
  import { IGenieConfig } from "./types.js";
10
13
  import express from "express";
11
14
 
12
15
  //#region src/plugins/genie/genie.d.ts
13
- declare class GeniePlugin extends Plugin {
16
+ declare class GeniePlugin extends Plugin implements ToolProvider {
14
17
  static manifest: PluginManifest<"genie">;
15
18
  protected static description: string;
16
19
  protected config: IGenieConfig;
17
20
  private readonly genieConnector;
21
+ private tools;
18
22
  constructor(config: IGenieConfig);
23
+ /**
24
+ * Builds the registry entries for a single Genie space alias.
25
+ * One set of tools per configured space, keyed by `${alias}.${method}`.
26
+ */
27
+ private _defineSpaceTools;
19
28
  private defaultSpaces;
20
29
  private resolveSpaceId;
21
30
  injectRoutes(router: IAppRouter): void;
22
31
  _handleSendMessage(req: express.Request, res: express.Response): Promise<void>;
23
32
  _handleGetConversation(req: express.Request, res: express.Response): Promise<void>;
24
33
  _handleGetMessage(req: express.Request, res: express.Response): Promise<void>;
25
- getConversation(alias: string, conversationId: string): Promise<GenieConversationHistoryResponse>;
34
+ getConversation(alias: string, conversationId: string, signal?: AbortSignal): Promise<GenieConversationHistoryResponse>;
26
35
  /**
27
36
  * Send a message and consume events as a stream (message_start, status,
28
37
  * message_result, query_result, error).
29
38
  */
30
39
  sendMessage(alias: string, content: string, conversationId?: string, options?: {
31
40
  timeout?: number;
41
+ signal?: AbortSignal;
32
42
  }): AsyncGenerator<GenieStreamEvent>;
33
43
  shutdown(): Promise<void>;
44
+ getAgentTools(): AgentToolDefinition[];
45
+ executeAgentTool(name: string, args: unknown, signal?: AbortSignal): Promise<unknown>;
46
+ toolkit(opts?: ToolkitOptions): Record<string, ToolkitEntry>;
34
47
  exports(): {
35
48
  sendMessage: (alias: string, content: string, conversationId?: string, options?: {
36
49
  timeout?: number;
50
+ signal?: AbortSignal;
37
51
  }) => AsyncGenerator<GenieStreamEvent>;
38
- getConversation: (alias: string, conversationId: string) => Promise<GenieConversationHistoryResponse>;
52
+ getConversation: (alias: string, conversationId: string, signal?: AbortSignal) => Promise<GenieConversationHistoryResponse>;
39
53
  };
40
54
  }
41
55
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"genie.d.ts","names":[],"sources":["../../../src/plugins/genie/genie.ts"],"mappings":";;;;;;;;;;;;cAmBa,WAAA,SAAoB,MAAA;EAAA,OACxB,QAAA,EAAuB,cAAA;EAAA,iBAEb,WAAA;EAAA,UAEC,MAAA,EAAQ,YAAA;EAAA,iBAET,cAAA;cAEL,MAAA,EAAQ,YAAA;EAAA,QAYZ,aAAA;EAAA,QAKA,cAAA;EAIR,YAAA,CAAa,MAAA,EAAQ,UAAA;EA6Bf,kBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAwDG,sBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAgDG,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAkDG,eAAA,CACJ,KAAA,UACA,cAAA,WACC,OAAA,CAAQ,gCAAA;EAnMU;;;;EAuNd,WAAA,CACL,KAAA,UACA,OAAA,UACA,cAAA,WACA,OAAA;IAAY,OAAA;EAAA,IACX,cAAA,CAAe,gBAAA;EAgBZ,QAAA,CAAA,GAAY,OAAA;EAIlB,OAAA,CAAA;iCAxBe,OAAA,UACE,cAAA,WACQ,OAAA;MACX,OAAA;IAAA,MACX,cAAA,CAAe,gBAAA;qCA3BH,cAAA,aAEZ,OAAA,CAAQ,gCAAA;EAAA;AAAA;;;;cAwDA,KAAA,EAAK,QAAA,QAAA,WAAA,EAAA,YAAA"}
1
+ {"version":3,"file":"genie.d.ts","names":[],"sources":["../../../src/plugins/genie/genie.ts"],"mappings":";;;;;;;;;;;;;;;cAgCa,WAAA,SAAoB,MAAA,YAAkB,YAAA;EAAA,OAC1C,QAAA,EAAuB,cAAA;EAAA,iBAEb,WAAA;EAAA,UAEC,MAAA,EAAQ,YAAA;EAAA,iBAET,cAAA;EAAA,QACT,KAAA;cAEI,MAAA,EAAQ,YAAA;;;;AAVtB;UA+CU,iBAAA;EAAA,QA0CA,aAAA;EAAA,QAKA,cAAA;EAIR,YAAA,CAAa,MAAA,EAAQ,UAAA;EA6Bf,kBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAwDG,sBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAgDG,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAkDG,eAAA,CACJ,KAAA,UACA,cAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,gCAAA;EApKR;;;;EA6LI,WAAA,CACL,KAAA,UACA,OAAA,UACA,cAAA,WACA,OAAA;IAAY,OAAA;IAAkB,MAAA,GAAS,WAAA;EAAA,IACtC,cAAA,CAAe,gBAAA;EAiBZ,QAAA,CAAA,GAAY,OAAA;EAIlB,aAAA,CAAA,GAAiB,mBAAA;EAIX,gBAAA,CACJ,IAAA,UACA,IAAA,WACA,MAAA,GAAS,WAAA,GACR,OAAA;EAIH,OAAA,CAAQ,IAAA,GAJE,cAAA,GAIoD,MAAA,SAAA,YAAA;EAI9D,OAAA,CAAA;iCAzCe,OAAA,UACE,cAAA,WACQ,OAAA;MACX,OAAA;MAAkB,MAAA,GAAS,WAAA;IAAA,MACtC,cAAA,CAAe,gBAAA;qCAjCH,cAAA,UACS,MAAA,GACb,WAAA,KACR,OAAA,CAAQ,gCAAA;EAAA;AAAA;;;;cA8EA,KAAA,EAAK,QAAA,QAAA,WAAA,EAAA,YAAA"}
@@ -7,9 +7,12 @@ import "../../plugin/index.js";
7
7
  import "../../logging/index.js";
8
8
  import { GenieConnector } from "../../connectors/genie/client.js";
9
9
  import "../../connectors/index.js";
10
+ import { buildToolkitEntries } from "../../core/agent/build-toolkit.js";
11
+ import { defineTool, executeFromRegistry, toolsFromRegistry } from "../../core/agent/tools/define-tool.js";
10
12
  import { genieStreamDefaults } from "./defaults.js";
11
13
  import manifest_default from "./manifest.js";
12
14
  import { randomUUID } from "node:crypto";
15
+ import { z } from "zod";
13
16
 
14
17
  //#region src/plugins/genie/genie.ts
15
18
  init_context();
@@ -18,6 +21,7 @@ var GeniePlugin = class extends Plugin {
18
21
  static manifest = manifest_default;
19
22
  static description = "AI/BI Genie space integration for natural language data queries";
20
23
  genieConnector;
24
+ tools = {};
21
25
  constructor(config) {
22
26
  super(config);
23
27
  this.config = {
@@ -28,6 +32,47 @@ var GeniePlugin = class extends Plugin {
28
32
  timeout: this.config.timeout,
29
33
  maxMessages: 200
30
34
  });
35
+ const spaces = this.config.spaces ?? {};
36
+ const missingAliases = Object.entries(spaces).filter(([, id]) => !id).map(([alias]) => alias);
37
+ if (missingAliases.length > 0) {
38
+ const plural = missingAliases.length > 1;
39
+ throw new Error(`GeniePlugin: space ${plural ? "aliases" : "alias"} ${missingAliases.map((a) => `"${a}"`).join(", ")} ${plural ? "were" : "was"} configured with a missing Genie Space ID. This usually means an environment variable used to populate the config is unset. Set the env var, or remove the alias from the config.`);
40
+ }
41
+ for (const alias of Object.keys(spaces)) Object.assign(this.tools, this._defineSpaceTools(alias));
42
+ }
43
+ /**
44
+ * Builds the registry entries for a single Genie space alias.
45
+ * One set of tools per configured space, keyed by `${alias}.${method}`.
46
+ */
47
+ _defineSpaceTools(alias) {
48
+ return {
49
+ [`${alias}.sendMessage`]: defineTool({
50
+ description: `Send a natural language question to the Genie space "${alias}" and get data analysis results`,
51
+ schema: z.object({
52
+ content: z.string().describe("The natural language question to ask"),
53
+ conversationId: z.string().optional().describe("Optional conversation ID to continue an existing conversation")
54
+ }),
55
+ annotations: {
56
+ effect: "read",
57
+ requiresUserContext: true
58
+ },
59
+ execute: async (args, signal) => {
60
+ const events = [];
61
+ for await (const event of this.sendMessage(alias, args.content, args.conversationId, { signal })) events.push(event);
62
+ return events;
63
+ }
64
+ }),
65
+ [`${alias}.getConversation`]: defineTool({
66
+ description: `Retrieve the conversation history from the Genie space "${alias}"`,
67
+ schema: z.object({ conversationId: z.string().describe("The conversation ID to retrieve") }),
68
+ annotations: {
69
+ effect: "read",
70
+ requiresUserContext: true
71
+ },
72
+ autoInheritable: true,
73
+ execute: (args, signal) => this.getConversation(alias, args.conversationId, signal)
74
+ })
75
+ };
31
76
  }
32
77
  defaultSpaces() {
33
78
  const spaceId = process.env.DATABRICKS_GENIE_SPACE_ID;
@@ -146,7 +191,8 @@ var GeniePlugin = class extends Plugin {
146
191
  signal
147
192
  }), streamSettings);
148
193
  }
149
- async getConversation(alias, conversationId) {
194
+ async getConversation(alias, conversationId, signal) {
195
+ signal?.throwIfAborted();
150
196
  const spaceId = this.resolveSpaceId(alias);
151
197
  if (!spaceId) throw new Error(`Unknown space alias: ${alias}`);
152
198
  const workspaceClient = getWorkspaceClient();
@@ -157,15 +203,28 @@ var GeniePlugin = class extends Plugin {
157
203
  * message_result, query_result, error).
158
204
  */
159
205
  async *sendMessage(alias, content, conversationId, options) {
206
+ options?.signal?.throwIfAborted();
160
207
  const spaceId = this.resolveSpaceId(alias);
161
208
  if (!spaceId) throw new Error(`Unknown space alias: ${alias}`);
162
209
  const workspaceClient = getWorkspaceClient();
163
210
  const timeout = options?.timeout ?? this.config.timeout ?? 12e4;
164
- yield* this.genieConnector.streamSendMessage(workspaceClient, spaceId, content, conversationId, { timeout });
211
+ yield* this.genieConnector.streamSendMessage(workspaceClient, spaceId, content, conversationId, {
212
+ timeout,
213
+ signal: options?.signal
214
+ });
165
215
  }
166
216
  async shutdown() {
167
217
  this.streamManager.abortAll();
168
218
  }
219
+ getAgentTools() {
220
+ return toolsFromRegistry(this.tools);
221
+ }
222
+ async executeAgentTool(name, args, signal) {
223
+ return executeFromRegistry(this.tools, name, args, signal);
224
+ }
225
+ toolkit(opts) {
226
+ return buildToolkitEntries(this.name, this.tools, opts);
227
+ }
169
228
  exports() {
170
229
  return {
171
230
  sendMessage: this.sendMessage,
@@ -1 +1 @@
1
- {"version":3,"file":"genie.js","names":["manifest"],"sources":["../../../src/plugins/genie/genie.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type express from \"express\";\nimport type { IAppRouter, StreamExecutionSettings } from \"shared\";\nimport { GenieConnector } from \"../../connectors\";\nimport { getWorkspaceClient } from \"../../context\";\nimport { createLogger } from \"../../logging\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { genieStreamDefaults } from \"./defaults\";\nimport manifest from \"./manifest.json\";\nimport type {\n GenieConversationHistoryResponse,\n GenieSendMessageRequest,\n GenieStreamEvent,\n IGenieConfig,\n} from \"./types\";\n\nconst logger = createLogger(\"genie\");\n\nexport class GeniePlugin extends Plugin {\n static manifest = manifest as PluginManifest<\"genie\">;\n\n protected static description =\n \"AI/BI Genie space integration for natural language data queries\";\n protected declare config: IGenieConfig;\n\n private readonly genieConnector: GenieConnector;\n\n constructor(config: IGenieConfig) {\n super(config);\n this.config = {\n ...config,\n spaces: config.spaces ?? this.defaultSpaces(),\n };\n this.genieConnector = new GenieConnector({\n timeout: this.config.timeout,\n maxMessages: 200,\n });\n }\n\n private defaultSpaces(): Record<string, string> {\n const spaceId = process.env.DATABRICKS_GENIE_SPACE_ID;\n return spaceId ? { default: spaceId } : {};\n }\n\n private resolveSpaceId(alias: string): string | null {\n return this.config.spaces?.[alias] ?? null;\n }\n\n injectRoutes(router: IAppRouter) {\n this.route(router, {\n name: \"sendMessage\",\n method: \"post\",\n path: \"/:alias/messages\",\n handler: async (req: express.Request, res: express.Response) => {\n await this.asUser(req)._handleSendMessage(req, res);\n },\n });\n\n this.route(router, {\n name: \"getConversation\",\n method: \"get\",\n path: \"/:alias/conversations/:conversationId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this.asUser(req)._handleGetConversation(req, res);\n },\n });\n\n this.route(router, {\n name: \"getMessage\",\n method: \"get\",\n path: \"/:alias/conversations/:conversationId/messages/:messageId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this.asUser(req)._handleGetMessage(req, res);\n },\n });\n }\n\n async _handleSendMessage(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { alias } = req.params;\n const spaceId = this.resolveSpaceId(alias);\n\n if (!spaceId) {\n res.status(404).json({ error: `Unknown space alias: ${alias}` });\n return;\n }\n\n const { content, conversationId } = req.body as GenieSendMessageRequest;\n\n if (!content) {\n res.status(400).json({ error: \"content is required\" });\n return;\n }\n\n logger.debug(\n \"Sending message to space %s (alias=%s, conversationId=%s)\",\n spaceId,\n alias,\n conversationId ?? \"new\",\n );\n\n const timeout = this.config.timeout ?? 120_000;\n const requestId =\n (typeof req.query.requestId === \"string\" && req.query.requestId) ||\n randomUUID();\n\n const streamSettings: StreamExecutionSettings = {\n ...genieStreamDefaults,\n default: {\n ...genieStreamDefaults.default,\n timeout,\n },\n stream: {\n ...genieStreamDefaults.stream,\n streamId: requestId,\n },\n };\n\n const workspaceClient = getWorkspaceClient();\n\n await this.executeStream<GenieStreamEvent>(\n res,\n (signal) =>\n this.genieConnector.streamSendMessage(\n workspaceClient,\n spaceId,\n content,\n conversationId,\n { timeout, signal },\n ),\n streamSettings,\n );\n }\n\n async _handleGetConversation(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { alias, conversationId } = req.params;\n const spaceId = this.resolveSpaceId(alias);\n\n if (!spaceId) {\n res.status(404).json({ error: `Unknown space alias: ${alias}` });\n return;\n }\n\n const includeQueryResults = req.query.includeQueryResults !== \"false\";\n const pageToken =\n typeof req.query.pageToken === \"string\" ? req.query.pageToken : undefined;\n const requestId =\n (typeof req.query.requestId === \"string\" && req.query.requestId) ||\n randomUUID();\n\n logger.debug(\n \"Fetching conversation %s from space %s (alias=%s, includeQueryResults=%s, pageToken=%s)\",\n conversationId,\n spaceId,\n alias,\n includeQueryResults,\n pageToken ?? \"none\",\n );\n\n const streamSettings: StreamExecutionSettings = {\n ...genieStreamDefaults,\n stream: {\n ...genieStreamDefaults.stream,\n streamId: requestId,\n },\n };\n\n const workspaceClient = getWorkspaceClient();\n\n await this.executeStream<GenieStreamEvent>(\n res,\n (signal) =>\n this.genieConnector.streamConversation(\n workspaceClient,\n spaceId,\n conversationId,\n { includeQueryResults, pageToken, signal },\n ),\n streamSettings,\n );\n }\n\n async _handleGetMessage(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { alias, conversationId, messageId } = req.params;\n const spaceId = this.resolveSpaceId(alias);\n\n if (!spaceId) {\n res.status(404).json({ error: `Unknown space alias: ${alias}` });\n return;\n }\n\n const requestId =\n (typeof req.query.requestId === \"string\" && req.query.requestId) ||\n randomUUID();\n\n logger.debug(\n \"Polling message %s in conversation %s from space %s (alias=%s)\",\n messageId,\n conversationId,\n spaceId,\n alias,\n );\n\n const timeout = this.config.timeout ?? 120_000;\n const streamSettings: StreamExecutionSettings = {\n ...genieStreamDefaults,\n default: {\n ...genieStreamDefaults.default,\n timeout,\n },\n stream: {\n ...genieStreamDefaults.stream,\n streamId: requestId,\n },\n };\n\n const workspaceClient = getWorkspaceClient();\n\n await this.executeStream<GenieStreamEvent>(\n res,\n (signal) =>\n this.genieConnector.streamGetMessage(\n workspaceClient,\n spaceId,\n conversationId,\n messageId,\n { timeout, signal },\n ),\n streamSettings,\n );\n }\n\n async getConversation(\n alias: string,\n conversationId: string,\n ): Promise<GenieConversationHistoryResponse> {\n const spaceId = this.resolveSpaceId(alias);\n\n if (!spaceId) {\n throw new Error(`Unknown space alias: ${alias}`);\n }\n\n const workspaceClient = getWorkspaceClient();\n\n return this.genieConnector.getConversation(\n workspaceClient,\n spaceId,\n conversationId,\n );\n }\n\n /**\n * Send a message and consume events as a stream (message_start, status,\n * message_result, query_result, error).\n */\n async *sendMessage(\n alias: string,\n content: string,\n conversationId?: string,\n options?: { timeout?: number },\n ): AsyncGenerator<GenieStreamEvent> {\n const spaceId = this.resolveSpaceId(alias);\n if (!spaceId) {\n throw new Error(`Unknown space alias: ${alias}`);\n }\n const workspaceClient = getWorkspaceClient();\n const timeout = options?.timeout ?? this.config.timeout ?? 120_000;\n yield* this.genieConnector.streamSendMessage(\n workspaceClient,\n spaceId,\n content,\n conversationId,\n { timeout },\n );\n }\n\n async shutdown(): Promise<void> {\n this.streamManager.abortAll();\n }\n\n exports() {\n return {\n sendMessage: this.sendMessage,\n getConversation: this.getConversation,\n };\n }\n}\n\n/**\n * @internal\n */\nexport const genie = toPlugin(GeniePlugin);\n"],"mappings":"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"}
1
+ {"version":3,"file":"genie.js","names":["manifest"],"sources":["../../../src/plugins/genie/genie.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type express from \"express\";\nimport type {\n AgentToolDefinition,\n IAppRouter,\n StreamExecutionSettings,\n ToolProvider,\n} from \"shared\";\nimport { z } from \"zod\";\nimport { GenieConnector } from \"../../connectors\";\nimport { getWorkspaceClient } from \"../../context\";\nimport { buildToolkitEntries } from \"../../core/agent/build-toolkit\";\nimport {\n defineTool,\n executeFromRegistry,\n type ToolRegistry,\n toolsFromRegistry,\n} from \"../../core/agent/tools/define-tool\";\nimport { createLogger } from \"../../logging\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { genieStreamDefaults } from \"./defaults\";\nimport manifest from \"./manifest.json\";\nimport type {\n GenieConversationHistoryResponse,\n GenieSendMessageRequest,\n GenieStreamEvent,\n IGenieConfig,\n} from \"./types\";\n\nconst logger = createLogger(\"genie\");\n\nexport class GeniePlugin extends Plugin implements ToolProvider {\n static manifest = manifest as PluginManifest<\"genie\">;\n\n protected static description =\n \"AI/BI Genie space integration for natural language data queries\";\n protected declare config: IGenieConfig;\n\n private readonly genieConnector: GenieConnector;\n private tools: ToolRegistry = {};\n\n constructor(config: IGenieConfig) {\n super(config);\n this.config = {\n ...config,\n spaces: config.spaces ?? this.defaultSpaces(),\n };\n this.genieConnector = new GenieConnector({\n timeout: this.config.timeout,\n maxMessages: 200,\n });\n\n const spaces = this.config.spaces ?? {};\n const missingAliases = Object.entries(spaces)\n .filter(([, id]) => !id)\n .map(([alias]) => alias);\n if (missingAliases.length > 0) {\n const plural = missingAliases.length > 1;\n throw new Error(\n `GeniePlugin: space ${plural ? \"aliases\" : \"alias\"} ${missingAliases\n .map((a) => `\"${a}\"`)\n .join(\n \", \",\n )} ${plural ? \"were\" : \"was\"} configured with a missing Genie Space ID. ` +\n \"This usually means an environment variable used to populate the config is unset. \" +\n \"Set the env var, or remove the alias from the config.\",\n );\n }\n\n for (const alias of Object.keys(spaces)) {\n Object.assign(this.tools, this._defineSpaceTools(alias));\n }\n }\n\n /**\n * Builds the registry entries for a single Genie space alias.\n * One set of tools per configured space, keyed by `${alias}.${method}`.\n */\n private _defineSpaceTools(alias: string): ToolRegistry {\n return {\n [`${alias}.sendMessage`]: defineTool({\n description: `Send a natural language question to the Genie space \"${alias}\" and get data analysis results`,\n schema: z.object({\n content: z.string().describe(\"The natural language question to ask\"),\n conversationId: z\n .string()\n .optional()\n .describe(\n \"Optional conversation ID to continue an existing conversation\",\n ),\n }),\n annotations: { effect: \"read\", requiresUserContext: true },\n execute: async (args, signal) => {\n const events: GenieStreamEvent[] = [];\n for await (const event of this.sendMessage(\n alias,\n args.content,\n args.conversationId,\n { signal },\n )) {\n events.push(event);\n }\n return events;\n },\n }),\n [`${alias}.getConversation`]: defineTool({\n description: `Retrieve the conversation history from the Genie space \"${alias}\"`,\n schema: z.object({\n conversationId: z\n .string()\n .describe(\"The conversation ID to retrieve\"),\n }),\n annotations: { effect: \"read\", requiresUserContext: true },\n autoInheritable: true,\n execute: (args, signal) =>\n this.getConversation(alias, args.conversationId, signal),\n }),\n };\n }\n\n private defaultSpaces(): Record<string, string | undefined> {\n const spaceId = process.env.DATABRICKS_GENIE_SPACE_ID;\n return spaceId ? { default: spaceId } : {};\n }\n\n private resolveSpaceId(alias: string): string | null {\n return this.config.spaces?.[alias] ?? null;\n }\n\n injectRoutes(router: IAppRouter) {\n this.route(router, {\n name: \"sendMessage\",\n method: \"post\",\n path: \"/:alias/messages\",\n handler: async (req: express.Request, res: express.Response) => {\n await this.asUser(req)._handleSendMessage(req, res);\n },\n });\n\n this.route(router, {\n name: \"getConversation\",\n method: \"get\",\n path: \"/:alias/conversations/:conversationId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this.asUser(req)._handleGetConversation(req, res);\n },\n });\n\n this.route(router, {\n name: \"getMessage\",\n method: \"get\",\n path: \"/:alias/conversations/:conversationId/messages/:messageId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this.asUser(req)._handleGetMessage(req, res);\n },\n });\n }\n\n async _handleSendMessage(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { alias } = req.params;\n const spaceId = this.resolveSpaceId(alias);\n\n if (!spaceId) {\n res.status(404).json({ error: `Unknown space alias: ${alias}` });\n return;\n }\n\n const { content, conversationId } = req.body as GenieSendMessageRequest;\n\n if (!content) {\n res.status(400).json({ error: \"content is required\" });\n return;\n }\n\n logger.debug(\n \"Sending message to space %s (alias=%s, conversationId=%s)\",\n spaceId,\n alias,\n conversationId ?? \"new\",\n );\n\n const timeout = this.config.timeout ?? 120_000;\n const requestId =\n (typeof req.query.requestId === \"string\" && req.query.requestId) ||\n randomUUID();\n\n const streamSettings: StreamExecutionSettings = {\n ...genieStreamDefaults,\n default: {\n ...genieStreamDefaults.default,\n timeout,\n },\n stream: {\n ...genieStreamDefaults.stream,\n streamId: requestId,\n },\n };\n\n const workspaceClient = getWorkspaceClient();\n\n await this.executeStream<GenieStreamEvent>(\n res,\n (signal) =>\n this.genieConnector.streamSendMessage(\n workspaceClient,\n spaceId,\n content,\n conversationId,\n { timeout, signal },\n ),\n streamSettings,\n );\n }\n\n async _handleGetConversation(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { alias, conversationId } = req.params;\n const spaceId = this.resolveSpaceId(alias);\n\n if (!spaceId) {\n res.status(404).json({ error: `Unknown space alias: ${alias}` });\n return;\n }\n\n const includeQueryResults = req.query.includeQueryResults !== \"false\";\n const pageToken =\n typeof req.query.pageToken === \"string\" ? req.query.pageToken : undefined;\n const requestId =\n (typeof req.query.requestId === \"string\" && req.query.requestId) ||\n randomUUID();\n\n logger.debug(\n \"Fetching conversation %s from space %s (alias=%s, includeQueryResults=%s, pageToken=%s)\",\n conversationId,\n spaceId,\n alias,\n includeQueryResults,\n pageToken ?? \"none\",\n );\n\n const streamSettings: StreamExecutionSettings = {\n ...genieStreamDefaults,\n stream: {\n ...genieStreamDefaults.stream,\n streamId: requestId,\n },\n };\n\n const workspaceClient = getWorkspaceClient();\n\n await this.executeStream<GenieStreamEvent>(\n res,\n (signal) =>\n this.genieConnector.streamConversation(\n workspaceClient,\n spaceId,\n conversationId,\n { includeQueryResults, pageToken, signal },\n ),\n streamSettings,\n );\n }\n\n async _handleGetMessage(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { alias, conversationId, messageId } = req.params;\n const spaceId = this.resolveSpaceId(alias);\n\n if (!spaceId) {\n res.status(404).json({ error: `Unknown space alias: ${alias}` });\n return;\n }\n\n const requestId =\n (typeof req.query.requestId === \"string\" && req.query.requestId) ||\n randomUUID();\n\n logger.debug(\n \"Polling message %s in conversation %s from space %s (alias=%s)\",\n messageId,\n conversationId,\n spaceId,\n alias,\n );\n\n const timeout = this.config.timeout ?? 120_000;\n const streamSettings: StreamExecutionSettings = {\n ...genieStreamDefaults,\n default: {\n ...genieStreamDefaults.default,\n timeout,\n },\n stream: {\n ...genieStreamDefaults.stream,\n streamId: requestId,\n },\n };\n\n const workspaceClient = getWorkspaceClient();\n\n await this.executeStream<GenieStreamEvent>(\n res,\n (signal) =>\n this.genieConnector.streamGetMessage(\n workspaceClient,\n spaceId,\n conversationId,\n messageId,\n { timeout, signal },\n ),\n streamSettings,\n );\n }\n\n async getConversation(\n alias: string,\n conversationId: string,\n signal?: AbortSignal,\n ): Promise<GenieConversationHistoryResponse> {\n // Honour an already-cancelled stream before paying any I/O cost. The\n // underlying connector's pagination loop is signal-agnostic today, so\n // this catches the common case (tool dispatched after the user\n // cancelled) without a deeper connector change.\n signal?.throwIfAborted();\n const spaceId = this.resolveSpaceId(alias);\n\n if (!spaceId) {\n throw new Error(`Unknown space alias: ${alias}`);\n }\n\n const workspaceClient = getWorkspaceClient();\n\n return this.genieConnector.getConversation(\n workspaceClient,\n spaceId,\n conversationId,\n );\n }\n\n /**\n * Send a message and consume events as a stream (message_start, status,\n * message_result, query_result, error).\n */\n async *sendMessage(\n alias: string,\n content: string,\n conversationId?: string,\n options?: { timeout?: number; signal?: AbortSignal },\n ): AsyncGenerator<GenieStreamEvent> {\n options?.signal?.throwIfAborted();\n const spaceId = this.resolveSpaceId(alias);\n if (!spaceId) {\n throw new Error(`Unknown space alias: ${alias}`);\n }\n const workspaceClient = getWorkspaceClient();\n const timeout = options?.timeout ?? this.config.timeout ?? 120_000;\n yield* this.genieConnector.streamSendMessage(\n workspaceClient,\n spaceId,\n content,\n conversationId,\n { timeout, signal: options?.signal },\n );\n }\n\n async shutdown(): Promise<void> {\n this.streamManager.abortAll();\n }\n\n getAgentTools(): AgentToolDefinition[] {\n return toolsFromRegistry(this.tools);\n }\n\n async executeAgentTool(\n name: string,\n args: unknown,\n signal?: AbortSignal,\n ): Promise<unknown> {\n return executeFromRegistry(this.tools, name, args, signal);\n }\n\n toolkit(opts?: import(\"../../core/agent/types\").ToolkitOptions) {\n return buildToolkitEntries(this.name, this.tools, opts);\n }\n\n exports() {\n return {\n sendMessage: this.sendMessage,\n getConversation: this.getConversation,\n };\n }\n}\n\n/**\n * @internal\n */\nexport const genie = toPlugin(GeniePlugin);\n"],"mappings":"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"}
@@ -6,8 +6,16 @@ import "../../connectors/genie/index.js";
6
6
 
7
7
  //#region src/plugins/genie/types.d.ts
8
8
  interface IGenieConfig extends BasePluginConfig {
9
- /** Map of alias → Genie Space ID. Defaults to { default: DATABRICKS_GENIE_SPACE_ID } if omitted. */
10
- spaces?: Record<string, string>;
9
+ /**
10
+ * Map of alias → Genie Space ID. Defaults to { default: DATABRICKS_GENIE_SPACE_ID } if omitted.
11
+ *
12
+ * Values are typed as `string | undefined` so callers can pass `process.env.X`
13
+ * directly (which is `string | undefined`) without a hoist-and-narrow dance.
14
+ * At construction the plugin validates that every configured alias has a
15
+ * defined ID — undefined values throw a clear error so a missing env var
16
+ * fails fast rather than masquerading as an unknown-alias 404 at request time.
17
+ */
18
+ spaces?: Record<string, string | undefined>;
11
19
  /** Genie polling timeout in ms. Set to 0 for indefinite. Default: 120000 (2 min) */
12
20
  timeout?: number;
13
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/plugins/genie/types.ts"],"mappings":";;;;;;;UAMiB,YAAA,SAAqB,gBAAA;;EAEpC,MAAA,GAAS,MAAA;;EAET,OAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/plugins/genie/types.ts"],"mappings":";;;;;;;UAMiB,YAAA,SAAqB,gBAAA;;;;AAAtC;;;;;;EAUE,MAAA,GAAS,MAAA;EAET;EAAA,OAAA;AAAA"}
@@ -14,8 +14,8 @@ import "../../connectors/jobs/index.js";
14
14
  import { JOBS_READ_DEFAULTS, JOBS_STREAM_DEFAULTS, JOBS_WRITE_DEFAULTS } from "./defaults.js";
15
15
  import manifest_default from "./manifest.js";
16
16
  import { mapParams } from "./params.js";
17
- import { STATUS_CODES } from "node:http";
18
17
  import { toJSONSchema } from "zod";
18
+ import { STATUS_CODES } from "node:http";
19
19
 
20
20
  //#region src/plugins/jobs/plugin.ts
21
21
  init_context();
@@ -1,2 +1,2 @@
1
- import { ILakebaseConfig } from "./types.js";
2
- import { lakebase } from "./lakebase.js";
1
+ import { ILakebaseConfig, LakebaseExposeAsAgentTool } from "./types.js";
2
+ import { LakebasePlugin, lakebase } from "./lakebase.js";
@@ -1,3 +1,3 @@
1
- import { lakebase } from "./lakebase.js";
1
+ import { LakebasePlugin, lakebase } from "./lakebase.js";
2
2
 
3
3
  export { };