@databricks/appkit 0.32.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 (153) hide show
  1. package/CLAUDE.md +53 -1
  2. package/NOTICE.md +1 -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 +5 -3
  8. package/dist/beta.js +3 -1
  9. package/dist/connectors/mcp/client.d.ts +27 -2
  10. package/dist/connectors/mcp/client.d.ts.map +1 -1
  11. package/dist/connectors/mcp/client.js +117 -18
  12. package/dist/connectors/mcp/client.js.map +1 -1
  13. package/dist/connectors/mcp/index.d.ts +1 -1
  14. package/dist/connectors/mcp/types.d.ts +1 -1
  15. package/dist/core/agent/build-toolkit.js +3 -8
  16. package/dist/core/agent/build-toolkit.js.map +1 -1
  17. package/dist/core/agent/load-agents.d.ts +6 -1
  18. package/dist/core/agent/load-agents.d.ts.map +1 -1
  19. package/dist/core/agent/load-agents.js +67 -27
  20. package/dist/core/agent/load-agents.js.map +1 -1
  21. package/dist/core/agent/plugins-map.js +44 -0
  22. package/dist/core/agent/plugins-map.js.map +1 -0
  23. package/dist/core/agent/run-agent.d.ts +31 -7
  24. package/dist/core/agent/run-agent.d.ts.map +1 -1
  25. package/dist/core/agent/run-agent.js +138 -27
  26. package/dist/core/agent/run-agent.js.map +1 -1
  27. package/dist/core/agent/toolkit-options.js +28 -0
  28. package/dist/core/agent/toolkit-options.js.map +1 -0
  29. package/dist/core/agent/toolkit-resolver.js +44 -0
  30. package/dist/core/agent/toolkit-resolver.js.map +1 -0
  31. package/dist/core/agent/tools/define-tool.d.ts +14 -2
  32. package/dist/core/agent/tools/define-tool.d.ts.map +1 -1
  33. package/dist/core/agent/tools/define-tool.js +1 -1
  34. package/dist/core/agent/tools/define-tool.js.map +1 -1
  35. package/dist/core/agent/tools/function-tool.d.ts +13 -2
  36. package/dist/core/agent/tools/function-tool.d.ts.map +1 -1
  37. package/dist/core/agent/tools/function-tool.js +4 -3
  38. package/dist/core/agent/tools/function-tool.js.map +1 -1
  39. package/dist/core/agent/tools/index.d.ts +1 -1
  40. package/dist/core/agent/tools/tool.d.ts +32 -3
  41. package/dist/core/agent/tools/tool.d.ts.map +1 -1
  42. package/dist/core/agent/tools/tool.js +4 -3
  43. package/dist/core/agent/tools/tool.js.map +1 -1
  44. package/dist/core/agent/types.d.ts +95 -10
  45. package/dist/core/agent/types.d.ts.map +1 -1
  46. package/dist/core/agent/types.js.map +1 -1
  47. package/dist/plugin/index.d.ts +1 -1
  48. package/dist/plugin/to-plugin.d.ts +3 -13
  49. package/dist/plugin/to-plugin.d.ts.map +1 -1
  50. package/dist/plugin/to-plugin.js +1 -8
  51. package/dist/plugin/to-plugin.js.map +1 -1
  52. package/dist/plugins/agents/agents.d.ts +184 -2
  53. package/dist/plugins/agents/agents.d.ts.map +1 -0
  54. package/dist/plugins/agents/agents.js +169 -72
  55. package/dist/plugins/agents/agents.js.map +1 -1
  56. package/dist/plugins/agents/index.d.ts +2 -2
  57. package/dist/plugins/agents/index.js +1 -1
  58. package/dist/plugins/agents/manifest.js +4 -5
  59. package/dist/plugins/agents/tool-approval-gate.js.map +1 -1
  60. package/dist/plugins/analytics/analytics.d.ts +3 -4
  61. package/dist/plugins/analytics/analytics.d.ts.map +1 -1
  62. package/dist/plugins/analytics/analytics.js +8 -6
  63. package/dist/plugins/analytics/analytics.js.map +1 -1
  64. package/dist/plugins/analytics/index.js +1 -0
  65. package/dist/plugins/analytics/types.js +15 -0
  66. package/dist/plugins/analytics/types.js.map +1 -0
  67. package/dist/plugins/beta-exports.generated.d.ts +2 -0
  68. package/dist/plugins/beta-exports.generated.js +4 -0
  69. package/dist/plugins/files/plugin.d.ts +1 -2
  70. package/dist/plugins/files/plugin.d.ts.map +1 -1
  71. package/dist/plugins/files/plugin.js +30 -12
  72. package/dist/plugins/files/plugin.js.map +1 -1
  73. package/dist/plugins/genie/genie.d.ts +5 -4
  74. package/dist/plugins/genie/genie.d.ts.map +1 -1
  75. package/dist/plugins/genie/genie.js +22 -8
  76. package/dist/plugins/genie/genie.js.map +1 -1
  77. package/dist/plugins/genie/types.d.ts +10 -2
  78. package/dist/plugins/genie/types.d.ts.map +1 -1
  79. package/dist/plugins/jobs/plugin.d.ts +1 -2
  80. package/dist/plugins/jobs/plugin.d.ts.map +1 -1
  81. package/dist/plugins/lakebase/lakebase.d.ts +1 -2
  82. package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
  83. package/dist/plugins/lakebase/lakebase.js +3 -3
  84. package/dist/plugins/lakebase/lakebase.js.map +1 -1
  85. package/dist/plugins/lakebase/types.d.ts +5 -4
  86. package/dist/plugins/lakebase/types.d.ts.map +1 -1
  87. package/dist/plugins/server/index.d.ts +3 -2
  88. package/dist/plugins/server/index.d.ts.map +1 -1
  89. package/dist/plugins/server/index.js +8 -5
  90. package/dist/plugins/server/index.js.map +1 -1
  91. package/dist/plugins/server/types.d.ts +11 -0
  92. package/dist/plugins/server/types.d.ts.map +1 -1
  93. package/dist/plugins/serving/serving.d.ts +1 -2
  94. package/dist/plugins/serving/serving.d.ts.map +1 -1
  95. package/dist/shared/src/agent.d.ts +16 -4
  96. package/dist/shared/src/agent.d.ts.map +1 -1
  97. package/docs/api/appkit/Class.AppKitMcpClient.md +157 -0
  98. package/docs/api/appkit/Class.DatabricksAdapter.md +151 -0
  99. package/docs/api/appkit/Function.agentIdFromMarkdownPath.md +18 -0
  100. package/docs/api/appkit/Function.createAgent.md +33 -0
  101. package/docs/api/appkit/Function.defineTool.md +26 -0
  102. package/docs/api/appkit/Function.executeFromRegistry.md +25 -0
  103. package/docs/api/appkit/Function.functionToolToDefinition.md +16 -0
  104. package/docs/api/appkit/Function.isFunctionTool.md +16 -0
  105. package/docs/api/appkit/Function.isHostedTool.md +16 -0
  106. package/docs/api/appkit/Function.isToolkitEntry.md +18 -0
  107. package/docs/api/appkit/Function.loadAgentFromFile.md +21 -0
  108. package/docs/api/appkit/Function.loadAgentsFromDir.md +26 -0
  109. package/docs/api/appkit/Function.mcpServer.md +28 -0
  110. package/docs/api/appkit/Function.parseTextToolCalls.md +26 -0
  111. package/docs/api/appkit/Function.resolveHostedTools.md +16 -0
  112. package/docs/api/appkit/Function.runAgent.md +26 -0
  113. package/docs/api/appkit/Function.tool.md +28 -0
  114. package/docs/api/appkit/Function.toolsFromRegistry.md +20 -0
  115. package/docs/api/appkit/Interface.AgentAdapter.md +21 -0
  116. package/docs/api/appkit/Interface.AgentDefinition.md +112 -0
  117. package/docs/api/appkit/Interface.AgentInput.md +37 -0
  118. package/docs/api/appkit/Interface.AgentRunContext.md +32 -0
  119. package/docs/api/appkit/Interface.AgentToolDefinition.md +37 -0
  120. package/docs/api/appkit/Interface.AgentsPluginConfig.md +241 -0
  121. package/docs/api/appkit/Interface.AutoInheritToolsConfig.md +27 -0
  122. package/docs/api/appkit/Interface.BasePluginConfig.md +1 -0
  123. package/docs/api/appkit/Interface.FunctionTool.md +80 -0
  124. package/docs/api/appkit/Interface.McpConnectAllResult.md +38 -0
  125. package/docs/api/appkit/Interface.Message.md +55 -0
  126. package/docs/api/appkit/Interface.PluginToolkitProvider.md +22 -0
  127. package/docs/api/appkit/Interface.PromptContext.md +30 -0
  128. package/docs/api/appkit/Interface.RegisteredAgent.md +75 -0
  129. package/docs/api/appkit/Interface.RunAgentInput.md +34 -0
  130. package/docs/api/appkit/Interface.RunAgentResult.md +23 -0
  131. package/docs/api/appkit/Interface.Thread.md +46 -0
  132. package/docs/api/appkit/Interface.ThreadStore.md +103 -0
  133. package/docs/api/appkit/Interface.ToolAnnotations.md +56 -0
  134. package/docs/api/appkit/Interface.ToolConfig.md +72 -0
  135. package/docs/api/appkit/Interface.ToolEntry.md +73 -0
  136. package/docs/api/appkit/Interface.ToolProvider.md +38 -0
  137. package/docs/api/appkit/Interface.ToolkitEntry.md +59 -0
  138. package/docs/api/appkit/Interface.ToolkitOptions.md +45 -0
  139. package/docs/api/appkit/TypeAlias.AgentEvent.md +299 -0
  140. package/docs/api/appkit/TypeAlias.AgentTool.md +11 -0
  141. package/docs/api/appkit/TypeAlias.AgentTools.md +8 -0
  142. package/docs/api/appkit/TypeAlias.AgentToolsFn.md +20 -0
  143. package/docs/api/appkit/TypeAlias.BaseSystemPromptOption.md +9 -0
  144. package/docs/api/appkit/TypeAlias.HostedTool.md +10 -0
  145. package/docs/api/appkit/TypeAlias.Plugins.md +26 -0
  146. package/docs/api/appkit/TypeAlias.ResolvedToolEntry.md +29 -0
  147. package/docs/api/appkit/TypeAlias.ToolRegistry.md +6 -0
  148. package/docs/api/appkit/Variable.agents.md +19 -0
  149. package/docs/api/appkit.md +113 -62
  150. package/docs/plugins/agents.md +441 -0
  151. package/llms.txt +53 -1
  152. package/package.json +1 -1
  153. package/sbom.cdx.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"agents.js","names":["manifest"],"sources":["../../../src/plugins/agents/agents.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\nimport type express from \"express\";\nimport pc from \"picocolors\";\nimport type {\n AgentAdapter,\n AgentEvent,\n AgentRunContext,\n AgentToolDefinition,\n IAppRouter,\n Message,\n PluginPhase,\n ResponseStreamEvent,\n Thread,\n ToolAnnotations,\n ToolProvider,\n} from \"shared\";\nimport { AppKitMcpClient, buildMcpHostPolicy } from \"../../connectors/mcp\";\nimport { consumeAdapterStream } from \"../../core/agent/consume-adapter-stream\";\nimport { loadAgentsFromDir } from \"../../core/agent/load-agents\";\nimport { normalizeToolResult } from \"../../core/agent/normalize-result\";\nimport {\n buildBaseSystemPrompt,\n composeSystemPrompt,\n} from \"../../core/agent/system-prompt\";\nimport {\n functionToolToDefinition,\n isFunctionTool,\n isHostedTool,\n resolveHostedTools,\n} from \"../../core/agent/tools\";\nimport type {\n AgentDefinition,\n AgentsPluginConfig,\n BaseSystemPromptOption,\n PromptContext,\n RegisteredAgent,\n ResolvedToolEntry,\n} from \"../../core/agent/types\";\nimport { isToolkitEntry } from \"../../core/agent/types\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { agentStreamDefaults } from \"./defaults\";\nimport { EventChannel } from \"./event-channel\";\nimport { AgentEventTranslator } from \"./event-translator\";\nimport manifest from \"./manifest.json\";\nimport {\n approvalRequestSchema,\n cancelRequestSchema,\n chatRequestSchema,\n invocationsRequestSchema,\n} from \"./schemas\";\nimport { InMemoryThreadStore } from \"./thread-store\";\nimport { ToolApprovalGate } from \"./tool-approval-gate\";\n\nconst logger = createLogger(\"agents\");\n\nconst DEFAULT_AGENTS_DIR = \"./config/agents\";\n\n/**\n * Context flag recorded on the in-memory AgentDefinition to indicate whether\n * it came from markdown (file) or from user code. Drives the asymmetric\n * `autoInheritTools` default.\n */\ninterface AgentSource {\n origin: \"file\" | \"code\";\n}\n\n/**\n * Decide whether a tool call must traverse the approval gate. Honours both\n * the modern `effect` field (mutating values: write / update / destructive)\n * and the legacy `destructive: true` boolean. The contract is documented on\n * `ToolAnnotations.effect` in shared/agent.ts.\n *\n * Without this, a tool authored only with `effect: \"destructive\"` (the\n * preferred API) bypassed the gate entirely.\n */\nfunction requiresApproval(annotations: ToolAnnotations | undefined): boolean {\n if (!annotations) return false;\n if (annotations.destructive === true) return true;\n switch (annotations.effect) {\n case \"write\":\n case \"update\":\n case \"destructive\":\n return true;\n case \"read\":\n case undefined:\n return false;\n default: {\n const _exhaustive: never = annotations.effect;\n return false;\n }\n }\n}\n\n/**\n * Per-stream state shared between the top-level `executeTool` and any\n * `runSubAgent` calls below it. Carrying the budget counter, abort signal,\n * approval policy, and event-channel through one object is what lets the\n * sub-agent path enforce the same limits and approval gate as the parent.\n *\n * Without this shared state the sub-agent path silently bypassed both the\n * tool-call budget and the destructive-tool approval gate.\n */\ninterface RunState {\n req: express.Request;\n userId: string;\n requestId: string;\n abortController: AbortController;\n signal: AbortSignal;\n approvalPolicy: { requireForDestructive: boolean; timeoutMs: number };\n limits: {\n maxConcurrentStreamsPerUser: number;\n maxToolCalls: number;\n maxSubAgentDepth: number;\n toolCallTimeoutMs: number;\n };\n translator: AgentEventTranslator;\n outboundEvents: EventChannel<ResponseStreamEvent>;\n /** Boxed mutable counter shared across parent + all sub-agent dispatches. */\n toolCallsUsed: { count: number };\n}\n\nexport class AgentsPlugin extends Plugin implements ToolProvider {\n static manifest = manifest as PluginManifest;\n static phase: PluginPhase = \"deferred\";\n\n protected declare config: AgentsPluginConfig;\n\n private agents = new Map<string, RegisteredAgent>();\n private defaultAgentName: string | null = null;\n private activeStreams = new Map<\n string,\n { controller: AbortController; userId: string }\n >();\n private mcpClient: AppKitMcpClient | null = null;\n private threadStore;\n private approvalGate = new ToolApprovalGate();\n\n constructor(config: AgentsPluginConfig) {\n super(config);\n this.config = config;\n if (config.threadStore) {\n this.threadStore = config.threadStore;\n } else {\n this.threadStore = new InMemoryThreadStore();\n if (process.env.NODE_ENV === \"production\") {\n logger.warn(\n \"InMemoryThreadStore is in use in a production build (NODE_ENV=production). \" +\n \"Thread history is unbounded and lost on restart. \" +\n \"Pass agents({ threadStore: <persistent impl> }) for real deployments.\",\n );\n } else {\n logger.info(\n \"Using default InMemoryThreadStore (dev-only — threads are lost on restart and grow without bound).\",\n );\n }\n }\n }\n\n /** Effective approval policy with defaults applied. */\n private get resolvedApprovalPolicy(): {\n requireForDestructive: boolean;\n timeoutMs: number;\n } {\n const cfg = this.config.approval ?? {};\n return {\n requireForDestructive: cfg.requireForDestructive ?? true,\n timeoutMs: cfg.timeoutMs ?? 60_000,\n };\n }\n\n /** Effective DoS limits with defaults applied. */\n private get resolvedLimits(): {\n maxConcurrentStreamsPerUser: number;\n maxToolCalls: number;\n maxSubAgentDepth: number;\n toolCallTimeoutMs: number;\n } {\n const cfg = this.config.limits ?? {};\n return {\n maxConcurrentStreamsPerUser: cfg.maxConcurrentStreamsPerUser ?? 5,\n maxToolCalls: cfg.maxToolCalls ?? 50,\n maxSubAgentDepth: cfg.maxSubAgentDepth ?? 3,\n // 5 minutes is the floor for cold SQL Warehouse / long Genie /\n // long Lakebase calls. The previous PluginContext default of 30s\n // truncated legitimate analytics queries on cold compute.\n toolCallTimeoutMs: cfg.toolCallTimeoutMs ?? 300_000,\n };\n }\n\n /** Count active streams owned by a given user. */\n private countUserStreams(userId: string): number {\n let n = 0;\n for (const entry of this.activeStreams.values()) {\n if (entry.userId === userId) n++;\n }\n return n;\n }\n\n async setup() {\n const { agents, defaultAgentName } = await this.buildAgentRegistry();\n this.agents = agents;\n this.defaultAgentName = defaultAgentName;\n this.mountInvocationsRoute();\n this.printRegistry();\n }\n\n /**\n * Reload agents from the configured directory, preserving code-defined\n * agents. Builds a fresh registry first and only swaps on success — if\n * `loadAgents` throws (malformed markdown, missing tool reference) the\n * existing live registry stays in place and serving requests keep working.\n */\n async reload(): Promise<void> {\n const next = await this.buildAgentRegistry();\n if (this.mcpClient) {\n await this.mcpClient.close();\n this.mcpClient = null;\n }\n this.agents = next.agents;\n this.defaultAgentName = next.defaultAgentName;\n }\n\n /**\n * Builds the agent registry into a fresh `Map` without touching live state.\n * Called by both `setup` and `reload`; the latter only swaps the live\n * registry once this resolves successfully (atomic reload).\n */\n private async buildAgentRegistry(): Promise<{\n agents: Map<string, RegisteredAgent>;\n defaultAgentName: string | null;\n }> {\n const { defs: fileDefs, defaultAgent: fileDefault } =\n await this.loadFileDefinitions();\n\n const codeDefs = this.config.agents ?? {};\n\n for (const name of Object.keys(fileDefs)) {\n if (codeDefs[name]) {\n logger.warn(\n \"Agent '%s' defined in both code and a markdown file. Code definition takes precedence.\",\n name,\n );\n }\n }\n\n const merged: Record<string, { def: AgentDefinition; src: AgentSource }> =\n {};\n for (const [name, def] of Object.entries(fileDefs)) {\n merged[name] = { def, src: { origin: \"file\" } };\n }\n for (const [name, def] of Object.entries(codeDefs)) {\n merged[name] = { def, src: { origin: \"code\" } };\n }\n\n const agents = new Map<string, RegisteredAgent>();\n let defaultAgentName: string | null = null;\n\n if (Object.keys(merged).length === 0) {\n logger.info(\n \"No agents registered (no files in %s, no code-defined agents)\",\n this.resolvedAgentsDir() ?? \"<disabled>\",\n );\n return { agents, defaultAgentName };\n }\n\n for (const [name, { def, src }] of Object.entries(merged)) {\n try {\n const registered = await this.buildRegisteredAgent(name, def, src);\n agents.set(name, registered);\n if (!defaultAgentName) defaultAgentName = name;\n } catch (err) {\n throw new Error(\n `Failed to register agent '${name}' (${src.origin}): ${\n err instanceof Error ? err.message : String(err)\n }`,\n { cause: err instanceof Error ? err : undefined },\n );\n }\n }\n\n if (this.config.defaultAgent) {\n if (!agents.has(this.config.defaultAgent)) {\n throw new Error(\n `defaultAgent '${this.config.defaultAgent}' is not registered. Available: ${Array.from(agents.keys()).join(\", \")}`,\n );\n }\n defaultAgentName = this.config.defaultAgent;\n } else if (fileDefault && agents.has(fileDefault)) {\n defaultAgentName = fileDefault;\n }\n\n return { agents, defaultAgentName };\n }\n\n private resolvedAgentsDir(): string | null {\n if (this.config.dir === false) return null;\n const dir = this.config.dir ?? DEFAULT_AGENTS_DIR;\n return path.isAbsolute(dir) ? dir : path.resolve(process.cwd(), dir);\n }\n\n private async loadFileDefinitions(): Promise<{\n defs: Record<string, AgentDefinition>;\n defaultAgent: string | null;\n }> {\n const dir = this.resolvedAgentsDir();\n if (!dir) return { defs: {}, defaultAgent: null };\n\n const pluginToolProviders = this.pluginProviderIndex();\n const ambient = this.config.tools ?? {};\n\n const result = await loadAgentsFromDir(dir, {\n defaultModel: this.config.defaultModel,\n availableTools: ambient,\n plugins: pluginToolProviders,\n codeAgents: this.config.agents,\n });\n\n return result;\n }\n\n /**\n * Builds the map of plugin-name → toolkit that the markdown loader consults\n * when resolving `toolkits:` frontmatter entries.\n */\n private pluginProviderIndex(): Map<\n string,\n { toolkit: (opts?: unknown) => Record<string, unknown> }\n > {\n const out = new Map();\n if (!this.context) return out;\n for (const { name, provider } of this.context.getToolProviders()) {\n const withToolkit = provider as ToolProvider & {\n toolkit?: (opts?: unknown) => Record<string, unknown>;\n };\n if (typeof withToolkit.toolkit === \"function\") {\n out.set(name, {\n toolkit: withToolkit.toolkit.bind(withToolkit),\n });\n }\n }\n return out;\n }\n\n private async buildRegisteredAgent(\n name: string,\n def: AgentDefinition,\n src: AgentSource,\n ): Promise<RegisteredAgent> {\n const adapter = await this.resolveAdapter(def, name);\n const toolIndex = await this.buildToolIndex(name, def, src);\n\n return {\n name,\n instructions: def.instructions,\n adapter,\n toolIndex,\n baseSystemPrompt: def.baseSystemPrompt,\n maxSteps: def.maxSteps,\n maxTokens: def.maxTokens,\n ephemeral: def.ephemeral,\n };\n }\n\n private async resolveAdapter(\n def: AgentDefinition,\n name: string,\n ): Promise<AgentAdapter> {\n const source = def.model ?? this.config.defaultModel;\n // Per-agent adapter knobs from `AgentDefinition` / markdown frontmatter.\n // Only applied when AppKit builds the adapter itself (string or omitted\n // model). Users who pass a pre-built `AgentAdapter` own these settings.\n const adapterOptions: { maxSteps?: number; maxTokens?: number } = {};\n if (def.maxSteps !== undefined) adapterOptions.maxSteps = def.maxSteps;\n if (def.maxTokens !== undefined) adapterOptions.maxTokens = def.maxTokens;\n\n if (!source) {\n const { DatabricksAdapter } = await import(\"../../agents/databricks\");\n try {\n return await DatabricksAdapter.fromModelServing(\n undefined,\n adapterOptions,\n );\n } catch (err) {\n throw new Error(\n `Agent '${name}' has no model configured and no DATABRICKS_AGENT_ENDPOINT default available`,\n { cause: err instanceof Error ? err : undefined },\n );\n }\n }\n if (typeof source === \"string\") {\n const { DatabricksAdapter } = await import(\"../../agents/databricks\");\n return DatabricksAdapter.fromModelServing(source, adapterOptions);\n }\n return await source;\n }\n\n /**\n * Resolves an agent's tool record into a per-agent dispatch index. Connects\n * hosted tools via MCP client. Applies `autoInheritTools` defaults when the\n * definition has no declared tools/agents.\n */\n private async buildToolIndex(\n agentName: string,\n def: AgentDefinition,\n src: AgentSource,\n ): Promise<Map<string, ResolvedToolEntry>> {\n const index = new Map<string, ResolvedToolEntry>();\n const hasExplicitTools = def.tools && Object.keys(def.tools).length > 0;\n const hasExplicitSubAgents =\n def.agents && Object.keys(def.agents).length > 0;\n\n const inheritDefaults = normalizeAutoInherit(this.config.autoInheritTools);\n const shouldInherit =\n !hasExplicitTools &&\n !hasExplicitSubAgents &&\n (src.origin === \"file\" ? inheritDefaults.file : inheritDefaults.code);\n\n if (shouldInherit) {\n await this.applyAutoInherit(agentName, index);\n }\n\n // 1. Sub-agents → agent-<key>\n for (const [childKey, childDef] of Object.entries(def.agents ?? {})) {\n const toolName = `agent-${childKey}`;\n index.set(toolName, {\n source: \"subagent\",\n agentName: childDef.name ?? childKey,\n def: {\n name: toolName,\n description:\n childDef.instructions.slice(0, 120) ||\n `Delegate to the ${childKey} sub-agent`,\n parameters: {\n type: \"object\",\n properties: {\n input: {\n type: \"string\",\n description: \"Message to send to the sub-agent.\",\n },\n },\n required: [\"input\"],\n },\n },\n });\n }\n\n // 2. Explicit tools (toolkit entries, function tools, hosted tools)\n const hostedToCollect: import(\"../../core/agent/tools/hosted-tools\").HostedTool[] =\n [];\n for (const [key, tool] of Object.entries(def.tools ?? {})) {\n if (isToolkitEntry(tool)) {\n index.set(key, {\n source: \"toolkit\",\n pluginName: tool.pluginName,\n localName: tool.localName,\n def: { ...tool.def, name: key },\n });\n continue;\n }\n if (isFunctionTool(tool)) {\n index.set(key, {\n source: \"function\",\n functionTool: tool,\n def: { ...functionToolToDefinition(tool), name: key },\n });\n continue;\n }\n if (isHostedTool(tool)) {\n hostedToCollect.push(tool);\n continue;\n }\n throw new Error(\n `Agent '${agentName}' tool '${key}' has an unrecognized shape`,\n );\n }\n\n if (hostedToCollect.length > 0) {\n await this.connectHostedTools(hostedToCollect, index);\n }\n\n return index;\n }\n\n private async applyAutoInherit(\n agentName: string,\n index: Map<string, ResolvedToolEntry>,\n ): Promise<void> {\n if (!this.context) return;\n const inherited: string[] = [];\n const skippedByPlugin = new Map<string, string[]>();\n const recordSkip = (pluginName: string, localName: string) => {\n const list = skippedByPlugin.get(pluginName) ?? [];\n list.push(localName);\n skippedByPlugin.set(pluginName, list);\n };\n\n for (const {\n name: pluginName,\n provider,\n } of this.context.getToolProviders()) {\n if (pluginName === this.name) continue;\n const withToolkit = provider as ToolProvider & {\n toolkit?: (opts?: unknown) => Record<string, unknown>;\n };\n if (typeof withToolkit.toolkit === \"function\") {\n const entries = withToolkit.toolkit() as Record<string, unknown>;\n for (const [key, maybeEntry] of Object.entries(entries)) {\n if (!isToolkitEntry(maybeEntry)) continue;\n if (maybeEntry.autoInheritable !== true) {\n recordSkip(maybeEntry.pluginName, maybeEntry.localName);\n continue;\n }\n index.set(key, {\n source: \"toolkit\",\n pluginName: maybeEntry.pluginName,\n localName: maybeEntry.localName,\n def: { ...maybeEntry.def, name: key },\n });\n inherited.push(key);\n }\n continue;\n }\n // Fallback: providers without a toolkit() still expose getAgentTools().\n // These cannot be selectively opted in per tool, so we conservatively\n // skip them during auto-inherit and require explicit `tools:` wiring.\n for (const tool of provider.getAgentTools()) {\n recordSkip(pluginName, tool.name);\n }\n }\n\n if (inherited.length > 0) {\n logger.info(\n \"[agent %s] auto-inherited %d tool(s): %s\",\n agentName,\n inherited.length,\n inherited.join(\", \"),\n );\n }\n if (skippedByPlugin.size > 0) {\n const summary = Array.from(skippedByPlugin.entries())\n .map(([p, tools]) => `${p}(${tools.length})`)\n .join(\", \");\n logger.info(\n \"[agent %s] auto-inherit skipped %d tool(s) not marked autoInheritable: %s. Wire them explicitly via `tools:` if needed.\",\n agentName,\n Array.from(skippedByPlugin.values()).reduce(\n (n, list) => n + list.length,\n 0,\n ),\n summary,\n );\n }\n }\n\n private async connectHostedTools(\n hostedTools: import(\"../../core/agent/tools/hosted-tools\").HostedTool[],\n index: Map<string, ResolvedToolEntry>,\n ): Promise<void> {\n let host: string | undefined;\n let authenticate: () => Promise<Record<string, string>>;\n\n try {\n const { getWorkspaceClient } = await import(\"../../context\");\n const wsClient = getWorkspaceClient();\n await wsClient.config.ensureResolved();\n host = wsClient.config.host;\n authenticate = async () => {\n const headers = new Headers();\n await wsClient.config.authenticate(headers);\n return Object.fromEntries(headers.entries());\n };\n } catch {\n host = process.env.DATABRICKS_HOST;\n authenticate = async (): Promise<Record<string, string>> => {\n const token = process.env.DATABRICKS_TOKEN;\n return token ? { Authorization: `Bearer ${token}` } : {};\n };\n }\n\n if (!host) {\n logger.warn(\n \"No Databricks host available — skipping %d hosted tool(s)\",\n hostedTools.length,\n );\n return;\n }\n\n if (!this.mcpClient) {\n const policy = buildMcpHostPolicy(this.config.mcp, host);\n this.mcpClient = new AppKitMcpClient(host, authenticate, policy);\n }\n\n const endpoints = resolveHostedTools(hostedTools);\n await this.mcpClient.connectAll(endpoints);\n\n for (const def of this.mcpClient.getAllToolDefinitions()) {\n index.set(def.name, {\n source: \"mcp\",\n mcpToolName: def.name,\n def,\n });\n }\n }\n\n // ----------------- ToolProvider (no tools of our own) --------------------\n\n getAgentTools(): AgentToolDefinition[] {\n return [];\n }\n\n async executeAgentTool(): Promise<unknown> {\n throw new Error(\"AgentsPlugin does not expose executeAgentTool directly\");\n }\n\n // ----------------- Route mounting and handlers ---------------------------\n\n private mountInvocationsRoute() {\n if (!this.context) return;\n this.context.addRoute(\n \"post\",\n \"/invocations\",\n (req: express.Request, res: express.Response) => {\n this._handleInvocations(req, res);\n },\n );\n }\n\n injectRoutes(router: IAppRouter) {\n this.route(router, {\n name: \"chat\",\n method: \"post\",\n path: \"/chat\",\n handler: async (req, res) => this._handleChat(req, res),\n });\n this.route(router, {\n name: \"cancel\",\n method: \"post\",\n path: \"/cancel\",\n handler: async (req, res) => this._handleCancel(req, res),\n });\n this.route(router, {\n name: \"approve\",\n method: \"post\",\n path: \"/approve\",\n handler: async (req, res) => this._handleApprove(req, res),\n });\n this.route(router, {\n name: \"threads\",\n method: \"get\",\n path: \"/threads\",\n handler: async (req, res) => this._handleListThreads(req, res),\n });\n this.route(router, {\n name: \"thread\",\n method: \"get\",\n path: \"/threads/:threadId\",\n handler: async (req, res) => this._handleGetThread(req, res),\n });\n this.route(router, {\n name: \"deleteThread\",\n method: \"delete\",\n path: \"/threads/:threadId\",\n handler: async (req, res) => this._handleDeleteThread(req, res),\n });\n this.route(router, {\n name: \"info\",\n method: \"get\",\n path: \"/info\",\n handler: async (_req, res) => {\n res.json({\n agents: Array.from(this.agents.keys()),\n defaultAgent: this.defaultAgentName,\n });\n },\n });\n }\n\n clientConfig(): Record<string, unknown> {\n return {\n agents: Array.from(this.agents.keys()),\n defaultAgent: this.defaultAgentName,\n };\n }\n\n private async _handleChat(req: express.Request, res: express.Response) {\n const parsed = chatRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n res.status(400).json({\n error: \"Invalid request\",\n details: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { message, threadId, agent: agentName } = parsed.data;\n\n const registered = this.resolveAgent(agentName);\n if (!registered) {\n res.status(400).json({\n error: agentName\n ? `Agent \"${agentName}\" not found`\n : \"No agent registered\",\n });\n return;\n }\n\n const userId = this.resolveUserId(req);\n\n // Reject early (before allocating a thread) when the user is already at\n // their concurrent-stream limit. Prevents a misbehaving client from\n // churning thread rows while being denied elsewhere.\n const limits = this.resolvedLimits;\n if (this.countUserStreams(userId) >= limits.maxConcurrentStreamsPerUser) {\n res.setHeader(\"Retry-After\", \"5\");\n res.status(429).json({\n error: `Too many concurrent streams for this user (limit ${limits.maxConcurrentStreamsPerUser}). Wait for an existing stream to complete before starting another.`,\n });\n return;\n }\n\n let thread = threadId ? await this.threadStore.get(threadId, userId) : null;\n if (threadId && !thread) {\n res.status(404).json({ error: `Thread ${threadId} not found` });\n return;\n }\n if (!thread) {\n thread = await this.threadStore.create(userId);\n }\n\n const userMessage: Message = {\n id: randomUUID(),\n role: \"user\",\n content: message,\n createdAt: new Date(),\n };\n await this.threadStore.addMessage(thread.id, userId, userMessage);\n return this._streamAgent(req, res, registered, thread, userId);\n }\n\n private async _handleInvocations(\n req: express.Request,\n res: express.Response,\n ) {\n const parsed = invocationsRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n res.status(400).json({\n error: \"Invalid request\",\n details: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { input } = parsed.data;\n const registered = this.resolveAgent();\n if (!registered) {\n res.status(400).json({ error: \"No agent registered\" });\n return;\n }\n const userId = this.resolveUserId(req);\n\n // Match the rate-limit gate on /chat. Without this, a client can bypass\n // `limits.maxConcurrentStreamsPerUser` by hitting /invocations instead.\n const limits = this.resolvedLimits;\n if (this.countUserStreams(userId) >= limits.maxConcurrentStreamsPerUser) {\n res.setHeader(\"Retry-After\", \"5\");\n res.status(429).json({\n error: `Too many concurrent streams for this user (limit ${limits.maxConcurrentStreamsPerUser}). Wait for an existing stream to complete before starting another.`,\n });\n return;\n }\n\n const thread = await this.threadStore.create(userId);\n\n if (typeof input === \"string\") {\n await this.threadStore.addMessage(thread.id, userId, {\n id: randomUUID(),\n role: \"user\",\n content: input,\n createdAt: new Date(),\n });\n } else {\n for (const item of input) {\n const role = (item.role ?? \"user\") as Message[\"role\"];\n const content =\n typeof item.content === \"string\"\n ? item.content\n : JSON.stringify(item.content ?? \"\");\n if (!content) continue;\n await this.threadStore.addMessage(thread.id, userId, {\n id: randomUUID(),\n role,\n content,\n createdAt: new Date(),\n });\n }\n }\n\n return this._streamAgent(req, res, registered, thread, userId);\n }\n\n private async _streamAgent(\n req: express.Request,\n res: express.Response,\n registered: RegisteredAgent,\n thread: Thread,\n userId: string,\n ): Promise<void> {\n const abortController = new AbortController();\n const signal = abortController.signal;\n const requestId = randomUUID();\n this.activeStreams.set(requestId, { controller: abortController, userId });\n\n const tools = Array.from(registered.toolIndex.values()).map((e) => e.def);\n const approvalPolicy = this.resolvedApprovalPolicy;\n const limits = this.resolvedLimits;\n const outboundEvents = new EventChannel<ResponseStreamEvent>();\n const translator = new AgentEventTranslator();\n\n // Per-run state shared with any sub-agents this run invokes. The boxed\n // tool-call counter, approval policy, and outbound event channel must\n // travel through the sub-agent path so it enforces the same budget and\n // approval gate as the top-level executeTool.\n const runState: RunState = {\n req,\n userId,\n requestId,\n abortController,\n signal,\n approvalPolicy,\n limits,\n translator,\n outboundEvents,\n toolCallsUsed: { count: 0 },\n };\n\n const executeTool = (name: string, args: unknown): Promise<unknown> =>\n this.dispatchToolCall(runState, registered.toolIndex, name, args, 0);\n\n // Drive the adapter and the approval-event side-channel concurrently.\n // Outbound events from both sources flow through `outboundEvents`; the\n // generator below drains the channel in order. executeTool pushes\n // approval-pending events into the same channel before awaiting the gate.\n const driver = (async () => {\n try {\n for (const evt of translator.translate({\n type: \"metadata\",\n data: { threadId: thread.id },\n })) {\n outboundEvents.push(evt);\n }\n\n const pluginNames = this.context\n ? this.context\n .getPluginNames()\n .filter((n) => n !== this.name && n !== \"server\")\n : [];\n const fullPrompt = composePromptForAgent(\n registered,\n this.config.baseSystemPrompt,\n {\n agentName: registered.name,\n pluginNames,\n toolNames: tools.map((t) => t.name),\n },\n );\n\n const messagesWithSystem: Message[] = [\n {\n id: \"system\",\n role: \"system\",\n content: fullPrompt,\n createdAt: new Date(),\n },\n ...thread.messages,\n ];\n\n const stream = registered.adapter.run(\n {\n messages: messagesWithSystem,\n tools,\n threadId: thread.id,\n signal,\n },\n { executeTool, signal },\n );\n\n // The accumulation rule (deltas append, `message` replaces) is shared\n // with `runAgent` and `runSubAgent`; see `consumeAdapterStream` for\n // the rationale.\n const fullContent = await consumeAdapterStream(stream, {\n signal,\n onEvent: (event) => {\n for (const translated of translator.translate(event)) {\n outboundEvents.push(translated);\n }\n },\n });\n\n if (fullContent) {\n await this.threadStore.addMessage(thread.id, userId, {\n id: randomUUID(),\n role: \"assistant\",\n content: fullContent,\n createdAt: new Date(),\n });\n }\n\n for (const evt of translator.finalize()) outboundEvents.push(evt);\n } catch (error) {\n if (signal.aborted) {\n outboundEvents.close();\n return;\n }\n logger.error(\"Agent chat error: %O\", error);\n outboundEvents.close(error);\n return;\n } finally {\n // Any pending approval gates for this stream are auto-denied so the\n // adapter can unwind if it was still waiting.\n this.approvalGate.abortStream(requestId);\n this.activeStreams.delete(requestId);\n // Stateless agents (e.g. autocomplete) don't persist history; drop\n // the thread so `InMemoryThreadStore` doesn't accumulate one record\n // per request. Swallow delete errors — the stream has already\n // finished and the client has the response.\n if (registered.ephemeral) {\n try {\n await this.threadStore.delete(thread.id, userId);\n } catch (err) {\n logger.warn(\n \"Failed to delete ephemeral thread %s: %O\",\n thread.id,\n err,\n );\n }\n }\n }\n outboundEvents.close();\n })();\n\n await this.executeStream<ResponseStreamEvent>(\n res,\n async function* () {\n try {\n for await (const ev of outboundEvents) {\n yield ev;\n }\n } finally {\n await driver.catch(() => undefined);\n }\n },\n {\n ...agentStreamDefaults,\n stream: { ...agentStreamDefaults.stream, streamId: requestId },\n },\n );\n }\n\n /**\n * Dispatch a single tool call from either the top-level adapter or a\n * sub-agent. Centralising this in one method is what makes the budget\n * counter, approval gate, and abort signal observe sub-agent activity:\n * `runSubAgent` reuses the same `runState` and so increments the same\n * counter and emits approval events through the same channel.\n *\n * `depth` is the current sub-agent recursion depth (0 at the top level).\n * It is forwarded to `runSubAgent` when the dispatched entry is itself a\n * sub-agent, so depth limits remain enforced.\n */\n private async dispatchToolCall(\n runState: RunState,\n toolIndex: Map<string, ResolvedToolEntry>,\n name: string,\n args: unknown,\n depth: number,\n ): Promise<unknown> {\n if (runState.toolCallsUsed.count >= runState.limits.maxToolCalls) {\n runState.abortController.abort(\n new Error(\n `Tool-call budget exhausted (limit ${runState.limits.maxToolCalls}).`,\n ),\n );\n throw new Error(\n `Tool-call budget exhausted (limit ${runState.limits.maxToolCalls}). Raise agents({ limits: { maxToolCalls } }) or review the agent's tool-selection logic.`,\n );\n }\n runState.toolCallsUsed.count++;\n\n const entry = toolIndex.get(name);\n if (!entry) throw new Error(`Unknown tool: ${name}`);\n\n if (\n runState.approvalPolicy.requireForDestructive &&\n requiresApproval(entry.def.annotations)\n ) {\n const approvalId = randomUUID();\n for (const ev of runState.translator.translate({\n type: \"approval_pending\",\n approvalId,\n streamId: runState.requestId,\n toolName: name,\n args,\n annotations: entry.def.annotations,\n })) {\n runState.outboundEvents.push(ev);\n }\n const decision = await this.approvalGate.wait({\n approvalId,\n streamId: runState.requestId,\n userId: runState.userId,\n timeoutMs: runState.approvalPolicy.timeoutMs,\n });\n if (decision === \"deny\") {\n return `Tool execution denied by user approval gate (tool: ${name}).`;\n }\n }\n\n let result: unknown;\n if (entry.source === \"toolkit\") {\n if (!this.context) {\n throw new Error(\n \"Plugin tool execution requires PluginContext; this should never happen through createApp\",\n );\n }\n result = await this.context.executeTool(\n runState.req,\n entry.pluginName,\n entry.localName,\n args,\n runState.signal,\n runState.limits.toolCallTimeoutMs,\n );\n } else if (entry.source === \"function\") {\n result = await entry.functionTool.execute(\n args as Record<string, unknown>,\n );\n } else if (entry.source === \"mcp\") {\n if (!this.mcpClient) throw new Error(\"MCP client not connected\");\n const oboToken = runState.req.headers[\"x-forwarded-access-token\"];\n const mcpAuth =\n typeof oboToken === \"string\"\n ? { Authorization: `Bearer ${oboToken}` }\n : undefined;\n result = await this.mcpClient.callTool(entry.mcpToolName, args, mcpAuth);\n } else if (entry.source === \"subagent\") {\n const childAgent = this.agents.get(entry.agentName);\n if (!childAgent)\n throw new Error(`Sub-agent not found: ${entry.agentName}`);\n result = await this.runSubAgent(runState, childAgent, args, depth + 1);\n }\n\n return normalizeToolResult(result);\n }\n\n /**\n * Runs a sub-agent in response to an `agent-<key>` tool call. Returns the\n * concatenated text output to hand back to the parent adapter as the tool\n * result.\n *\n * `depth` starts at 1 for a top-level sub-agent invocation (i.e. the\n * outer `_streamAgent` calls `runSubAgent(..., 1)`) and increments on\n * each nested `runSubAgent` call. Depths exceeding\n * `limits.maxSubAgentDepth` are rejected before any adapter work.\n *\n * Sub-agent tool calls run through `dispatchToolCall` with the same\n * `runState` as the parent — the budget counter and approval gate are\n * therefore enforced for every nested call, not only at the top level.\n */\n private async runSubAgent(\n runState: RunState,\n child: RegisteredAgent,\n args: unknown,\n depth: number,\n ): Promise<string> {\n if (depth > runState.limits.maxSubAgentDepth) {\n throw new Error(\n `Sub-agent depth exceeded (limit ${runState.limits.maxSubAgentDepth}). ` +\n `Raise agents({ limits: { maxSubAgentDepth } }) or break the delegation cycle.`,\n );\n }\n\n const input =\n typeof args === \"object\" &&\n args !== null &&\n typeof (args as { input?: unknown }).input === \"string\"\n ? (args as { input: string }).input\n : JSON.stringify(args);\n const childTools = Array.from(child.toolIndex.values()).map((e) => e.def);\n\n const childExecute = (name: string, childArgs: unknown): Promise<unknown> =>\n this.dispatchToolCall(runState, child.toolIndex, name, childArgs, depth);\n\n const runContext: AgentRunContext = {\n executeTool: childExecute,\n signal: runState.signal,\n };\n\n const pluginNames = this.context\n ? this.context\n .getPluginNames()\n .filter((n) => n !== this.name && n !== \"server\")\n : [];\n const systemPrompt = composePromptForAgent(\n child,\n this.config.baseSystemPrompt,\n {\n agentName: child.name,\n pluginNames,\n toolNames: childTools.map((t) => t.name),\n },\n );\n\n const messages: Message[] = [\n {\n id: \"system\",\n role: \"system\",\n content: systemPrompt,\n createdAt: new Date(),\n },\n {\n id: randomUUID(),\n role: \"user\",\n content: input,\n createdAt: new Date(),\n },\n ];\n\n return consumeAdapterStream(\n child.adapter.run(\n {\n messages,\n tools: childTools,\n threadId: randomUUID(),\n signal: runState.signal,\n },\n runContext,\n ),\n {\n signal: runState.signal,\n // Forward every sub-agent event into the parent's outbound SSE\n // stream so the client sees nested tool_call / tool_result events\n // (UI-action tools like apply_filter / highlight_period rely on\n // this) and the sub-agent's streaming text as it's generated.\n //\n // `metadata` is the one exception: sub-agents have their own\n // threadId, and forwarding it would overwrite the parent's\n // thread state on the client and break multi-turn continuity.\n // Approval-pending events emitted by `dispatchToolCall` already\n // reach `outboundEvents` directly, so they are not routed here.\n onEvent: (event) => {\n if (event.type === \"metadata\") return;\n for (const translated of runState.translator.translate(event)) {\n runState.outboundEvents.push(translated);\n }\n },\n },\n );\n }\n\n private async _handleCancel(req: express.Request, res: express.Response) {\n const parsed = cancelRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n res.status(400).json({\n error: \"Invalid request\",\n details: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { streamId } = parsed.data;\n const entry = this.activeStreams.get(streamId);\n if (!entry) {\n // Stream is unknown or already completed — idempotent no-op.\n res.json({ cancelled: true });\n return;\n }\n const userId = this.resolveUserId(req);\n if (entry.userId !== userId) {\n res.status(403).json({ error: \"Forbidden\" });\n return;\n }\n entry.controller.abort(\"Cancelled by user\");\n this.activeStreams.delete(streamId);\n this.approvalGate.abortStream(streamId);\n res.json({ cancelled: true });\n }\n\n private async _handleApprove(req: express.Request, res: express.Response) {\n const parsed = approvalRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n res.status(400).json({\n error: \"Invalid request\",\n details: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { streamId, approvalId, decision } = parsed.data;\n\n const streamEntry = this.activeStreams.get(streamId);\n if (!streamEntry) {\n // Stream has already completed or never existed. Return 404 so the UI\n // knows the approval token is no longer valid (the waiter, if any, has\n // already been timed out or aborted).\n res.status(404).json({ error: \"Stream not found or already completed\" });\n return;\n }\n\n const userId = this.resolveUserId(req);\n if (streamEntry.userId !== userId) {\n res.status(403).json({ error: \"Forbidden\" });\n return;\n }\n\n const result = this.approvalGate.submit({ approvalId, userId, decision });\n if (!result.ok) {\n if (result.reason === \"forbidden\") {\n res.status(403).json({ error: \"Forbidden\" });\n return;\n }\n res.status(404).json({ error: \"Approval not found or already settled\" });\n return;\n }\n\n res.json({ decision });\n }\n\n private async _handleListThreads(\n req: express.Request,\n res: express.Response,\n ) {\n const userId = this.resolveUserId(req);\n const threads = await this.threadStore.list(userId);\n res.json({ threads });\n }\n\n private async _handleGetThread(req: express.Request, res: express.Response) {\n const userId = this.resolveUserId(req);\n const thread = await this.threadStore.get(req.params.threadId, userId);\n if (!thread) {\n res.status(404).json({ error: \"Thread not found\" });\n return;\n }\n res.json(thread);\n }\n\n private async _handleDeleteThread(\n req: express.Request,\n res: express.Response,\n ) {\n const userId = this.resolveUserId(req);\n const deleted = await this.threadStore.delete(req.params.threadId, userId);\n if (!deleted) {\n res.status(404).json({ error: \"Thread not found\" });\n return;\n }\n res.json({ deleted: true });\n }\n\n private resolveAgent(name?: string): RegisteredAgent | null {\n if (name) return this.agents.get(name) ?? null;\n if (this.defaultAgentName) {\n return this.agents.get(this.defaultAgentName) ?? null;\n }\n const first = this.agents.values().next();\n return first.done ? null : first.value;\n }\n\n private printRegistry(): void {\n if (this.agents.size === 0) return;\n console.log(\"\");\n console.log(` ${pc.bold(\"Agents\")} ${pc.dim(`(${this.agents.size})`)}`);\n console.log(` ${pc.dim(\"─\".repeat(60))}`);\n for (const [name, reg] of this.agents) {\n const tools = reg.toolIndex.size;\n const marker = name === this.defaultAgentName ? pc.green(\"●\") : \" \";\n console.log(\n ` ${marker} ${pc.bold(name.padEnd(24))} ${pc.dim(`${tools} tools`)}`,\n );\n }\n console.log(` ${pc.dim(\"─\".repeat(60))}`);\n console.log(\"\");\n }\n\n async shutdown(): Promise<void> {\n this.approvalGate.abortAll();\n if (this.mcpClient) {\n await this.mcpClient.close();\n this.mcpClient = null;\n }\n }\n\n exports() {\n return {\n register: (name: string, def: AgentDefinition) =>\n this.registerCodeAgent(name, def),\n list: () => Array.from(this.agents.keys()),\n get: (name: string) => this.agents.get(name) ?? null,\n reload: () => this.reload(),\n getDefault: () => this.defaultAgentName,\n getThreads: (userId: string) => this.threadStore.list(userId),\n };\n }\n\n private async registerCodeAgent(\n name: string,\n def: AgentDefinition,\n ): Promise<void> {\n const registered = await this.buildRegisteredAgent(name, def, {\n origin: \"code\",\n });\n this.agents.set(name, registered);\n if (!this.defaultAgentName) this.defaultAgentName = name;\n }\n}\n\nfunction normalizeAutoInherit(value: AgentsPluginConfig[\"autoInheritTools\"]): {\n file: boolean;\n code: boolean;\n} {\n // Default is opt-out for both origins. A markdown agent or code-defined\n // agent with no declared `tools:` gets an empty tool index unless the\n // developer explicitly flips `autoInheritTools` on. Even then, only tools\n // whose plugin author marked `autoInheritable: true` are spread — see\n // `applyAutoInherit` for the filter.\n if (value === undefined) return { file: false, code: false };\n if (typeof value === \"boolean\") return { file: value, code: value };\n return { file: value.file ?? false, code: value.code ?? false };\n}\n\nfunction composePromptForAgent(\n registered: RegisteredAgent,\n pluginLevel: BaseSystemPromptOption | undefined,\n ctx: PromptContext,\n): string {\n const perAgent = registered.baseSystemPrompt;\n const resolved = perAgent !== undefined ? perAgent : pluginLevel;\n\n let base = \"\";\n if (resolved === false) {\n base = \"\";\n } else if (typeof resolved === \"string\") {\n base = resolved;\n } else if (typeof resolved === \"function\") {\n base = resolved(ctx);\n } else {\n base = buildBaseSystemPrompt(ctx);\n }\n\n return composeSystemPrompt(base, registered.instructions);\n}\n\n/**\n * Plugin factory for the agents plugin. Reads `config/agents/*.md` by default,\n * resolves toolkits/tools from registered plugins, exposes `appkit.agents.*`\n * runtime API and mounts `/invocations`.\n *\n * @example\n * ```ts\n * import { agents, analytics, createApp, server } from \"@databricks/appkit\";\n *\n * await createApp({\n * plugins: [server(), analytics(), agents()],\n * });\n * ```\n */\nexport const agents = toPlugin(AgentsPlugin);\n"],"mappings":"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"}
1
+ {"version":3,"file":"agents.js","names":["manifest"],"sources":["../../../src/plugins/agents/agents.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\nimport type express from \"express\";\nimport pc from \"picocolors\";\nimport type {\n AgentAdapter,\n AgentEvent,\n AgentRunContext,\n AgentToolDefinition,\n IAppRouter,\n Message,\n PluginPhase,\n ResponseStreamEvent,\n Thread,\n ToolAnnotations,\n ToolProvider,\n} from \"shared\";\nimport { AppKitMcpClient, buildMcpHostPolicy } from \"../../connectors/mcp\";\nimport { getWorkspaceClient } from \"../../context\";\nimport { consumeAdapterStream } from \"../../core/agent/consume-adapter-stream\";\nimport { loadAgentsFromDir } from \"../../core/agent/load-agents\";\nimport { normalizeToolResult } from \"../../core/agent/normalize-result\";\nimport { createPluginsProxy } from \"../../core/agent/plugins-map\";\nimport {\n buildBaseSystemPrompt,\n composeSystemPrompt,\n} from \"../../core/agent/system-prompt\";\nimport { resolveToolkitFromProvider } from \"../../core/agent/toolkit-resolver\";\nimport {\n functionToolToDefinition,\n isFunctionTool,\n isHostedTool,\n resolveHostedTools,\n} from \"../../core/agent/tools\";\nimport type {\n AgentDefinition,\n AgentsPluginConfig,\n AgentTools,\n BaseSystemPromptOption,\n Plugins,\n PluginToolkitProvider,\n PromptContext,\n RegisteredAgent,\n ResolvedToolEntry,\n} from \"../../core/agent/types\";\nimport { isToolkitEntry } from \"../../core/agent/types\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { agentStreamDefaults } from \"./defaults\";\nimport { EventChannel } from \"./event-channel\";\nimport { AgentEventTranslator } from \"./event-translator\";\nimport manifest from \"./manifest.json\";\nimport {\n approvalRequestSchema,\n cancelRequestSchema,\n chatRequestSchema,\n invocationsRequestSchema,\n} from \"./schemas\";\nimport { InMemoryThreadStore } from \"./thread-store\";\nimport { ToolApprovalGate } from \"./tool-approval-gate\";\n\nconst logger = createLogger(\"agents\");\n\nconst DEFAULT_AGENTS_DIR = \"./config/agents\";\n\n/**\n * Context flag recorded on the in-memory AgentDefinition to indicate whether\n * it came from markdown (file) or from user code. Drives the asymmetric\n * `autoInheritTools` default.\n */\ninterface AgentSource {\n origin: \"file\" | \"code\";\n}\n\n/**\n * Decide whether a tool call must traverse the approval gate. Honours both\n * the modern `effect` field (mutating values: write / update / destructive)\n * and the legacy `destructive: true` boolean. The contract is documented on\n * `ToolAnnotations.effect` in shared/agent.ts.\n *\n * Without this, a tool authored only with `effect: \"destructive\"` (the\n * preferred API) bypassed the gate entirely.\n */\nfunction requiresApproval(annotations: ToolAnnotations | undefined): boolean {\n if (!annotations) return false;\n if (annotations.destructive === true) return true;\n switch (annotations.effect) {\n case \"write\":\n case \"update\":\n case \"destructive\":\n return true;\n case \"read\":\n case undefined:\n return false;\n default: {\n const _exhaustive: never = annotations.effect;\n return false;\n }\n }\n}\n\n/**\n * Per-stream state shared between the top-level `executeTool` and any\n * `runSubAgent` calls below it. Carrying the budget counter, abort signal,\n * approval policy, and event-channel through one object is what lets the\n * sub-agent path enforce the same limits and approval gate as the parent.\n *\n * Without this shared state the sub-agent path silently bypassed both the\n * tool-call budget and the destructive-tool approval gate.\n */\ninterface RunState {\n req: express.Request;\n userId: string;\n requestId: string;\n abortController: AbortController;\n signal: AbortSignal;\n approvalPolicy: { requireForDestructive: boolean; timeoutMs: number };\n limits: {\n maxConcurrentStreamsPerUser: number;\n maxToolCalls: number;\n maxSubAgentDepth: number;\n toolCallTimeoutMs: number;\n };\n translator: AgentEventTranslator;\n outboundEvents: EventChannel<ResponseStreamEvent>;\n /** Boxed mutable counter shared across parent + all sub-agent dispatches. */\n toolCallsUsed: { count: number };\n}\n\nexport class AgentsPlugin extends Plugin implements ToolProvider {\n static manifest = manifest as PluginManifest;\n static phase: PluginPhase = \"deferred\";\n\n protected declare config: AgentsPluginConfig;\n\n private agents = new Map<string, RegisteredAgent>();\n private defaultAgentName: string | null = null;\n private activeStreams = new Map<\n string,\n { controller: AbortController; userId: string }\n >();\n /**\n * Per-user stream count, kept in sync with `activeStreams` so the\n * concurrent-stream rate limit check is O(1) instead of O(n) over every\n * active stream on every request. Mutated only via {@link trackStream}\n * and {@link untrackStream}.\n */\n private userStreamCounts = new Map<string, number>();\n private mcpClient: AppKitMcpClient | null = null;\n private threadStore;\n private approvalGate = new ToolApprovalGate();\n\n constructor(config: AgentsPluginConfig) {\n super(config);\n this.config = config;\n if (config.threadStore) {\n this.threadStore = config.threadStore;\n } else {\n this.threadStore = new InMemoryThreadStore();\n if (process.env.NODE_ENV === \"production\") {\n logger.warn(\n \"InMemoryThreadStore is in use in a production build (NODE_ENV=production). \" +\n \"Thread history is unbounded and lost on restart. \" +\n \"Pass agents({ threadStore: <persistent impl> }) for real deployments.\",\n );\n } else {\n logger.info(\n \"Using default InMemoryThreadStore (dev-only — threads are lost on restart and grow without bound).\",\n );\n }\n }\n }\n\n /**\n * Effective approval policy with defaults applied. Memoised so the\n * `timeoutMs` validation warning fires at most once per plugin instance —\n * `resolvedApprovalPolicy` gets hit on every chat stream and a noisy\n * misconfig would otherwise spam the logs.\n *\n * `timeoutMs` is clamped to a 1s floor so a misconfigured value (`0`,\n * negative, or `NaN`) can't degrade into immediate auto-denial of every\n * mutating tool call.\n */\n private cachedApprovalPolicy: {\n requireForDestructive: boolean;\n timeoutMs: number;\n } | null = null;\n\n private get resolvedApprovalPolicy(): {\n requireForDestructive: boolean;\n timeoutMs: number;\n } {\n if (this.cachedApprovalPolicy) return this.cachedApprovalPolicy;\n const cfg = this.config.approval ?? {};\n const APPROVAL_TIMEOUT_FLOOR_MS = 1_000;\n const APPROVAL_TIMEOUT_DEFAULT_MS = 60_000;\n let timeoutMs = cfg.timeoutMs ?? APPROVAL_TIMEOUT_DEFAULT_MS;\n if (!Number.isFinite(timeoutMs) || timeoutMs < APPROVAL_TIMEOUT_FLOOR_MS) {\n logger.warn(\n \"approval.timeoutMs=%s is below the %sms floor; using default %sms instead. Mutating tool calls would otherwise auto-deny before any UI could respond.\",\n cfg.timeoutMs,\n APPROVAL_TIMEOUT_FLOOR_MS,\n APPROVAL_TIMEOUT_DEFAULT_MS,\n );\n timeoutMs = APPROVAL_TIMEOUT_DEFAULT_MS;\n }\n this.cachedApprovalPolicy = {\n requireForDestructive: cfg.requireForDestructive ?? true,\n timeoutMs,\n };\n return this.cachedApprovalPolicy;\n }\n\n /** Effective DoS limits with defaults applied. */\n private get resolvedLimits(): {\n maxConcurrentStreamsPerUser: number;\n maxToolCalls: number;\n maxSubAgentDepth: number;\n toolCallTimeoutMs: number;\n } {\n const cfg = this.config.limits ?? {};\n return {\n maxConcurrentStreamsPerUser: cfg.maxConcurrentStreamsPerUser ?? 5,\n maxToolCalls: cfg.maxToolCalls ?? 50,\n maxSubAgentDepth: cfg.maxSubAgentDepth ?? 3,\n // 5 minutes is the floor for cold SQL Warehouse / long Genie /\n // long Lakebase calls. The previous PluginContext default of 30s\n // truncated legitimate analytics queries on cold compute.\n toolCallTimeoutMs: cfg.toolCallTimeoutMs ?? 300_000,\n };\n }\n\n /** Count active streams owned by a given user. O(1). */\n private countUserStreams(userId: string): number {\n return this.userStreamCounts.get(userId) ?? 0;\n }\n\n /**\n * Register a stream for `userId` and bump the per-user counter. Paired\n * with {@link untrackStream}; the two helpers are the only writers to\n * `activeStreams` + `userStreamCounts`, so the counter cannot drift from\n * the map.\n */\n private trackStream(\n requestId: string,\n userId: string,\n controller: AbortController,\n ): void {\n this.activeStreams.set(requestId, { controller, userId });\n this.userStreamCounts.set(\n userId,\n (this.userStreamCounts.get(userId) ?? 0) + 1,\n );\n }\n\n /**\n * Remove a stream from the active map and decrement the per-user\n * counter. Idempotent — calling twice for the same `requestId` is a\n * no-op (the second call sees no entry and returns early).\n */\n private untrackStream(requestId: string): void {\n const entry = this.activeStreams.get(requestId);\n if (!entry) return;\n this.activeStreams.delete(requestId);\n const next = (this.userStreamCounts.get(entry.userId) ?? 0) - 1;\n if (next <= 0) {\n this.userStreamCounts.delete(entry.userId);\n } else {\n this.userStreamCounts.set(entry.userId, next);\n }\n }\n\n async setup() {\n const { agents, defaultAgentName } = await this.buildAgentRegistry();\n this.agents = agents;\n this.defaultAgentName = defaultAgentName;\n this.mountInvocationsRoute();\n this.printRegistry();\n }\n\n /**\n * Reload agents from the configured directory, preserving code-defined\n * agents. Builds a fresh registry first and only swaps on success — if\n * `loadAgents` throws (malformed markdown, missing tool reference) the\n * existing live registry stays in place and serving requests keep working.\n */\n async reload(): Promise<void> {\n const next = await this.buildAgentRegistry();\n // Deliberately NOT closing the existing mcpClient here. Tool\n // dispatch in `dispatchToolCall` reads `this.mcpClient` at call\n // time; closing it mid-stream throws \"MCP client is closed\" from\n // the next sendRpc and kills the in-flight conversation. The\n // client owns only short-lived `fetch` handles (no keep-alive\n // sockets) and the connections map persists in the live instance,\n // so dropping `this.mcpClient` would also strand in-flight tool\n // calls that resolved the field a moment earlier. Leave the live\n // client in place; `buildAgentRegistry` -> `connectHostedTools`\n // adds any new endpoints to the same instance, and stale\n // connections from a removed config become unreachable through\n // the new agent tool indexes (small memory cost, no correctness\n // hazard). The shutdown path still closes — that's process\n // teardown, where in-flight streams have already been aborted via\n // `abortActiveOperations`.\n this.agents = next.agents;\n this.defaultAgentName = next.defaultAgentName;\n }\n\n /**\n * Builds the agent registry into a fresh `Map` without touching live state.\n * Called by both `setup` and `reload`; the latter only swaps the live\n * registry once this resolves successfully (atomic reload).\n */\n private async buildAgentRegistry(): Promise<{\n agents: Map<string, RegisteredAgent>;\n defaultAgentName: string | null;\n }> {\n const { defs: fileDefs, defaultAgent: fileDefault } =\n await this.loadFileDefinitions();\n\n const codeDefs = this.config.agents ?? {};\n\n for (const name of Object.keys(fileDefs)) {\n if (codeDefs[name]) {\n logger.warn(\n \"Agent '%s' defined in both code and a markdown file. Code definition takes precedence.\",\n name,\n );\n }\n }\n\n const merged: Record<string, { def: AgentDefinition; src: AgentSource }> =\n {};\n for (const [name, def] of Object.entries(fileDefs)) {\n merged[name] = { def, src: { origin: \"file\" } };\n }\n for (const [name, def] of Object.entries(codeDefs)) {\n merged[name] = { def, src: { origin: \"code\" } };\n }\n\n const agents = new Map<string, RegisteredAgent>();\n let defaultAgentName: string | null = null;\n\n if (Object.keys(merged).length === 0) {\n logger.info(\n \"No agents registered (no files in %s, no code-defined agents)\",\n this.resolvedAgentsDir() ?? \"<disabled>\",\n );\n return { agents, defaultAgentName };\n }\n\n for (const [name, { def, src }] of Object.entries(merged)) {\n try {\n const registered = await this.buildRegisteredAgent(name, def, src);\n agents.set(name, registered);\n if (!defaultAgentName) defaultAgentName = name;\n } catch (err) {\n throw new Error(\n `Failed to register agent '${name}' (${src.origin}): ${\n err instanceof Error ? err.message : String(err)\n }`,\n { cause: err instanceof Error ? err : undefined },\n );\n }\n }\n\n if (this.config.defaultAgent) {\n if (!agents.has(this.config.defaultAgent)) {\n throw new Error(\n `defaultAgent '${this.config.defaultAgent}' is not registered. Available: ${Array.from(agents.keys()).join(\", \")}`,\n );\n }\n defaultAgentName = this.config.defaultAgent;\n } else if (fileDefault && agents.has(fileDefault)) {\n defaultAgentName = fileDefault;\n }\n\n return { agents, defaultAgentName };\n }\n\n private resolvedAgentsDir(): string | null {\n if (this.config.dir === false) return null;\n const dir = this.config.dir ?? DEFAULT_AGENTS_DIR;\n return path.isAbsolute(dir) ? dir : path.resolve(process.cwd(), dir);\n }\n\n private async loadFileDefinitions(): Promise<{\n defs: Record<string, AgentDefinition>;\n defaultAgent: string | null;\n }> {\n const dir = this.resolvedAgentsDir();\n if (!dir) return { defs: {}, defaultAgent: null };\n\n const pluginToolProviders = this.pluginProviderIndex();\n const ambient = this.config.tools ?? {};\n\n const result = await loadAgentsFromDir(dir, {\n defaultModel: this.config.defaultModel,\n availableTools: ambient,\n plugins: pluginToolProviders,\n codeAgents: this.config.agents,\n });\n\n return result;\n }\n\n /**\n * Builds the map of plugin-name → toolkit that the markdown loader consults\n * when resolving `plugin:NAME` entries in the unified `tools:` frontmatter\n * list (and, equivalently, that the code form passes as the `plugins`\n * argument to `tools(plugins) => Record<...>`).\n */\n private pluginProviderIndex(): Map<\n string,\n { toolkit: (opts?: unknown) => Record<string, unknown> }\n > {\n const out = new Map();\n if (!this.context) return out;\n for (const { name, provider } of this.context.getToolProviders()) {\n const withToolkit = provider as ToolProvider & {\n toolkit?: (opts?: unknown) => Record<string, unknown>;\n };\n if (typeof withToolkit.toolkit === \"function\") {\n out.set(name, {\n toolkit: withToolkit.toolkit.bind(withToolkit),\n });\n }\n }\n return out;\n }\n\n private async buildRegisteredAgent(\n name: string,\n def: AgentDefinition,\n src: AgentSource,\n ): Promise<RegisteredAgent> {\n const adapter = await this.resolveAdapter(def, name);\n const toolIndex = await this.buildToolIndex(name, def, src);\n\n return {\n name,\n instructions: def.instructions,\n adapter,\n toolIndex,\n baseSystemPrompt: def.baseSystemPrompt,\n maxSteps: def.maxSteps,\n maxTokens: def.maxTokens,\n ephemeral: def.ephemeral,\n };\n }\n\n private async resolveAdapter(\n def: AgentDefinition,\n name: string,\n ): Promise<AgentAdapter> {\n const source = def.model ?? this.config.defaultModel;\n // Per-agent adapter knobs from `AgentDefinition` / markdown frontmatter.\n // Only applied when AppKit builds the adapter itself (string or omitted\n // model). Users who pass a pre-built `AgentAdapter` own these settings.\n const adapterOptions: { maxSteps?: number; maxTokens?: number } = {};\n if (def.maxSteps !== undefined) adapterOptions.maxSteps = def.maxSteps;\n if (def.maxTokens !== undefined) adapterOptions.maxTokens = def.maxTokens;\n\n if (!source) {\n const { DatabricksAdapter } = await import(\"../../agents/databricks\");\n try {\n return await DatabricksAdapter.fromModelServing(\n undefined,\n adapterOptions,\n );\n } catch (err) {\n throw new Error(\n `Agent '${name}' has no model configured and no DATABRICKS_SERVING_ENDPOINT_NAME default available`,\n { cause: err instanceof Error ? err : undefined },\n );\n }\n }\n if (typeof source === \"string\") {\n const { DatabricksAdapter } = await import(\"../../agents/databricks\");\n return DatabricksAdapter.fromModelServing(source, adapterOptions);\n }\n return await source;\n }\n\n /**\n * Resolves an agent's tool record into a per-agent dispatch index. Connects\n * hosted tools via MCP client. Applies `autoInheritTools` defaults when the\n * definition has no declared tools/agents.\n */\n private async buildToolIndex(\n agentName: string,\n def: AgentDefinition,\n src: AgentSource,\n ): Promise<Map<string, ResolvedToolEntry>> {\n const index = new Map<string, ResolvedToolEntry>();\n const hasDeclaredTools = def.tools !== undefined;\n const toolsRecord = this.resolveDefTools(agentName, def);\n const hasExplicitSubAgents =\n def.agents && Object.keys(def.agents).length > 0;\n\n const inheritDefaults = normalizeAutoInherit(this.config.autoInheritTools);\n // Declaring `tools` (object or function, even an empty record) opts out\n // of auto-inherit. Same rule for both forms — see plan decision (E1/I1).\n const shouldInherit =\n !hasDeclaredTools &&\n !hasExplicitSubAgents &&\n (src.origin === \"file\" ? inheritDefaults.file : inheritDefaults.code);\n\n if (shouldInherit) {\n await this.applyAutoInherit(agentName, index);\n }\n\n // 1. Sub-agents → agent-<key>\n for (const [childKey, childDef] of Object.entries(def.agents ?? {})) {\n const toolName = `agent-${childKey}`;\n index.set(toolName, {\n source: \"subagent\",\n agentName: childDef.name ?? childKey,\n def: {\n name: toolName,\n description:\n childDef.instructions.slice(0, 120) ||\n `Delegate to the ${childKey} sub-agent`,\n parameters: {\n type: \"object\",\n properties: {\n input: {\n type: \"string\",\n description: \"Message to send to the sub-agent.\",\n },\n },\n required: [\"input\"],\n },\n },\n });\n }\n\n // 2. Explicit tools (toolkit entries, function tools, hosted tools)\n const hostedToCollect: import(\"../../core/agent/tools/hosted-tools\").HostedTool[] =\n [];\n for (const [key, tool] of Object.entries(toolsRecord)) {\n if (isToolkitEntry(tool)) {\n index.set(key, {\n source: \"toolkit\",\n pluginName: tool.pluginName,\n localName: tool.localName,\n def: { ...tool.def, name: key },\n });\n continue;\n }\n if (isFunctionTool(tool)) {\n index.set(key, {\n source: \"function\",\n functionTool: tool,\n def: { ...functionToolToDefinition(tool), name: key },\n });\n continue;\n }\n if (isHostedTool(tool)) {\n hostedToCollect.push(tool);\n continue;\n }\n throw new Error(\n `Agent '${agentName}' tool '${key}' has an unrecognized shape`,\n );\n }\n\n if (hostedToCollect.length > 0) {\n await this.connectHostedTools(hostedToCollect, index);\n }\n\n return index;\n }\n\n /**\n * Resolves an `AgentDefinition.tools` field to a plain tool record. The\n * function form is invoked exactly once at agent setup with the typed\n * {@link Plugins} map; the result replaces the function reference for the\n * remainder of the registered agent's lifetime.\n *\n * Plain object form is returned as-is; an undefined `tools` returns an\n * empty record. The function form is wrapped in a try/catch so a thrown\n * callback fails registration with a useful message instead of leaking\n * the raw stack.\n */\n private resolveDefTools(agentName: string, def: AgentDefinition): AgentTools {\n if (typeof def.tools !== \"function\") {\n return def.tools ?? {};\n }\n try {\n return def.tools(this.buildPluginsMap());\n } catch (err) {\n throw new Error(\n `Agent '${agentName}': tools(plugins) callback threw: ${\n err instanceof Error ? err.message : String(err)\n }`,\n { cause: err instanceof Error ? err : undefined },\n );\n }\n }\n\n /**\n * Builds the typed {@link Plugins} map passed to the function form of\n * `AgentDefinition.tools`. Each entry exposes the plugin instance directly\n * (so user code can call typed instance methods including `.toolkit()`);\n * plugins missing `.toolkit()` get a synthesized fallback that walks\n * `getAgentTools()` via `resolveToolkitFromProvider`.\n *\n * Wrapped in {@link createPluginsProxy} so that accessing an unknown\n * plugin name throws a named \"not registered, Available: ...\" error\n * instead of bubbling up a generic `Cannot read properties of undefined`\n * from the agent's `tools(plugins)` callback.\n */\n private buildPluginsMap(): Plugins {\n const out: Record<string, PluginToolkitProvider> = {};\n if (!this.context) {\n return createPluginsProxy(out, `Agent '${this.name}': tools(plugins)`);\n }\n for (const { name, provider } of this.context.getToolProviders()) {\n const direct = (provider as { toolkit?: unknown }).toolkit;\n if (typeof direct === \"function\") {\n out[name] = provider as unknown as PluginToolkitProvider;\n } else {\n out[name] = {\n toolkit: (opts) => resolveToolkitFromProvider(name, provider, opts),\n };\n }\n }\n return createPluginsProxy(out, `Agent '${this.name}': tools(plugins)`);\n }\n\n private async applyAutoInherit(\n agentName: string,\n index: Map<string, ResolvedToolEntry>,\n ): Promise<void> {\n if (!this.context) return;\n const inherited: string[] = [];\n const skippedByPlugin = new Map<string, string[]>();\n const recordSkip = (pluginName: string, localName: string) => {\n const list = skippedByPlugin.get(pluginName) ?? [];\n list.push(localName);\n skippedByPlugin.set(pluginName, list);\n };\n\n for (const {\n name: pluginName,\n provider,\n } of this.context.getToolProviders()) {\n if (pluginName === this.name) continue;\n const entries = resolveToolkitFromProvider(pluginName, provider);\n for (const [key, entry] of Object.entries(entries)) {\n if (entry.autoInheritable !== true) {\n recordSkip(entry.pluginName, entry.localName);\n continue;\n }\n index.set(key, {\n source: \"toolkit\",\n pluginName: entry.pluginName,\n localName: entry.localName,\n def: { ...entry.def, name: key },\n });\n inherited.push(key);\n }\n }\n\n if (inherited.length > 0) {\n logger.info(\n \"[agent %s] auto-inherited %d tool(s): %s\",\n agentName,\n inherited.length,\n inherited.join(\", \"),\n );\n }\n if (skippedByPlugin.size > 0) {\n const summary = Array.from(skippedByPlugin.entries())\n .map(([p, tools]) => `${p}(${tools.length})`)\n .join(\", \");\n logger.info(\n \"[agent %s] auto-inherit skipped %d tool(s) not marked autoInheritable: %s. Wire them explicitly via `tools:` if needed.\",\n agentName,\n Array.from(skippedByPlugin.values()).reduce(\n (n, list) => n + list.length,\n 0,\n ),\n summary,\n );\n }\n }\n\n private async connectHostedTools(\n hostedTools: import(\"../../core/agent/tools/hosted-tools\").HostedTool[],\n index: Map<string, ResolvedToolEntry>,\n ): Promise<void> {\n let host: string | undefined;\n let authenticate: () => Promise<Record<string, string>>;\n\n try {\n const { getWorkspaceClient } = await import(\"../../context\");\n const wsClient = getWorkspaceClient();\n await wsClient.config.ensureResolved();\n host = wsClient.config.host;\n authenticate = async () => {\n const headers = new Headers();\n await wsClient.config.authenticate(headers);\n return Object.fromEntries(headers.entries());\n };\n } catch {\n host = process.env.DATABRICKS_HOST;\n authenticate = async (): Promise<Record<string, string>> => {\n const token = process.env.DATABRICKS_TOKEN;\n return token ? { Authorization: `Bearer ${token}` } : {};\n };\n }\n\n if (!host) {\n logger.warn(\n \"No Databricks host available — skipping %d hosted tool(s)\",\n hostedTools.length,\n );\n return;\n }\n\n if (!this.mcpClient) {\n const policy = buildMcpHostPolicy(this.config.mcp, host);\n this.mcpClient = new AppKitMcpClient(host, authenticate, policy);\n }\n\n const endpoints = resolveHostedTools(hostedTools);\n const result = await this.mcpClient.connectAll(endpoints);\n if (result.failed.length > 0) {\n // Per-endpoint errors are already logged inside `connectAll`; this\n // aggregate warning makes the partial-success state visible at the\n // agent-registration boundary so operators see \"agent X registered\n // without N hosted-tool endpoints\" alongside the connect-time\n // errors, instead of just an opaque list of MCP failures.\n logger.warn(\n \"MCP: %s of %s endpoints failed to connect (%s). Agents that reference these endpoints will boot without their hosted tools.\",\n result.failed.length,\n endpoints.length,\n result.failed.map((f) => f.name).join(\", \"),\n );\n }\n\n for (const def of this.mcpClient.getAllToolDefinitions()) {\n index.set(def.name, {\n source: \"mcp\",\n mcpToolName: def.name,\n def,\n });\n }\n }\n\n // ----------------- ToolProvider (no tools of our own) --------------------\n\n getAgentTools(): AgentToolDefinition[] {\n return [];\n }\n\n async executeAgentTool(): Promise<unknown> {\n throw new Error(\"AgentsPlugin does not expose executeAgentTool directly\");\n }\n\n // ----------------- Route mounting and handlers ---------------------------\n\n private mountInvocationsRoute() {\n if (!this.context) return;\n this.context.addRoute(\n \"post\",\n \"/invocations\",\n (req: express.Request, res: express.Response) => {\n this._handleInvocations(req, res);\n },\n );\n }\n\n injectRoutes(router: IAppRouter) {\n this.route(router, {\n name: \"chat\",\n method: \"post\",\n path: \"/chat\",\n handler: async (req, res) => this._handleChat(req, res),\n });\n this.route(router, {\n name: \"cancel\",\n method: \"post\",\n path: \"/cancel\",\n handler: async (req, res) => this._handleCancel(req, res),\n });\n this.route(router, {\n name: \"approve\",\n method: \"post\",\n path: \"/approve\",\n handler: async (req, res) => this._handleApprove(req, res),\n });\n this.route(router, {\n name: \"threads\",\n method: \"get\",\n path: \"/threads\",\n handler: async (req, res) => this._handleListThreads(req, res),\n });\n this.route(router, {\n name: \"thread\",\n method: \"get\",\n path: \"/threads/:threadId\",\n handler: async (req, res) => this._handleGetThread(req, res),\n });\n this.route(router, {\n name: \"deleteThread\",\n method: \"delete\",\n path: \"/threads/:threadId\",\n handler: async (req, res) => this._handleDeleteThread(req, res),\n });\n this.route(router, {\n name: \"info\",\n method: \"get\",\n path: \"/info\",\n handler: async (_req, res) => {\n res.json({\n agents: Array.from(this.agents.keys()),\n defaultAgent: this.defaultAgentName,\n });\n },\n });\n }\n\n clientConfig(): Record<string, unknown> {\n return {\n agents: Array.from(this.agents.keys()),\n defaultAgent: this.defaultAgentName,\n };\n }\n\n private async _handleChat(req: express.Request, res: express.Response) {\n const parsed = chatRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n res.status(400).json({\n error: \"Invalid request\",\n details: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { message, threadId, agent: agentName } = parsed.data;\n\n const registered = this.resolveAgent(agentName);\n if (!registered) {\n res.status(400).json({\n error: agentName\n ? `Agent \"${agentName}\" not found`\n : \"No agent registered\",\n });\n return;\n }\n\n const userId = this.resolveUserId(req);\n\n // Reject early (before allocating a thread) when the user is already at\n // their concurrent-stream limit. Prevents a misbehaving client from\n // churning thread rows while being denied elsewhere.\n const limits = this.resolvedLimits;\n if (this.countUserStreams(userId) >= limits.maxConcurrentStreamsPerUser) {\n res.setHeader(\"Retry-After\", \"5\");\n res.status(429).json({\n error: `Too many concurrent streams for this user (limit ${limits.maxConcurrentStreamsPerUser}). Wait for an existing stream to complete before starting another.`,\n });\n return;\n }\n\n // ThreadStore can throw on backing-storage failures (DB unreachable,\n // permission errors, transient I/O). Without a try/catch the\n // `async` Express handler bubbles the rejection without a response and\n // the client connection hangs until the proxy times out. Surface the\n // failure as a 500 so the SSE client falls back instead of waiting.\n let thread: Thread;\n try {\n const existing = threadId\n ? await this.threadStore.get(threadId, userId)\n : null;\n if (threadId && !existing) {\n res.status(404).json({ error: `Thread ${threadId} not found` });\n return;\n }\n thread = existing ?? (await this.threadStore.create(userId));\n\n const userMessage: Message = {\n id: randomUUID(),\n role: \"user\",\n content: message,\n createdAt: new Date(),\n };\n await this.threadStore.addMessage(thread.id, userId, userMessage);\n } catch (err) {\n logger.error(\"threadStore failed in /chat: %O\", err);\n res.status(500).json({ error: \"Thread operation failed\" });\n return;\n }\n return this._streamAgent(req, res, registered, thread, userId);\n }\n\n private async _handleInvocations(\n req: express.Request,\n res: express.Response,\n ) {\n const parsed = invocationsRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n res.status(400).json({\n error: \"Invalid request\",\n details: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { input } = parsed.data;\n const registered = this.resolveAgent();\n if (!registered) {\n res.status(400).json({ error: \"No agent registered\" });\n return;\n }\n const userId = this.resolveUserId(req);\n\n // Match the rate-limit gate on /chat. Without this, a client can bypass\n // `limits.maxConcurrentStreamsPerUser` by hitting /invocations instead.\n const limits = this.resolvedLimits;\n if (this.countUserStreams(userId) >= limits.maxConcurrentStreamsPerUser) {\n res.setHeader(\"Retry-After\", \"5\");\n res.status(429).json({\n error: `Too many concurrent streams for this user (limit ${limits.maxConcurrentStreamsPerUser}). Wait for an existing stream to complete before starting another.`,\n });\n return;\n }\n\n // Same rationale as `_handleChat`: surface threadStore failures as a\n // 500 instead of letting the async handler hang the client connection.\n let thread: Thread;\n try {\n thread = await this.threadStore.create(userId);\n\n if (typeof input === \"string\") {\n await this.threadStore.addMessage(thread.id, userId, {\n id: randomUUID(),\n role: \"user\",\n content: input,\n createdAt: new Date(),\n });\n } else {\n for (const item of input) {\n const role = (item.role ?? \"user\") as Message[\"role\"];\n const content =\n typeof item.content === \"string\"\n ? item.content\n : JSON.stringify(item.content ?? \"\");\n if (!content) continue;\n await this.threadStore.addMessage(thread.id, userId, {\n id: randomUUID(),\n role,\n content,\n createdAt: new Date(),\n });\n }\n }\n } catch (err) {\n logger.error(\"threadStore failed in /invocations: %O\", err);\n res.status(500).json({ error: \"Thread operation failed\" });\n return;\n }\n\n return this._streamAgent(req, res, registered, thread, userId);\n }\n\n private async _streamAgent(\n req: express.Request,\n res: express.Response,\n registered: RegisteredAgent,\n thread: Thread,\n userId: string,\n ): Promise<void> {\n const abortController = new AbortController();\n const signal = abortController.signal;\n const requestId = randomUUID();\n this.trackStream(requestId, userId, abortController);\n\n const tools = Array.from(registered.toolIndex.values()).map((e) => e.def);\n const approvalPolicy = this.resolvedApprovalPolicy;\n const limits = this.resolvedLimits;\n const outboundEvents = new EventChannel<ResponseStreamEvent>();\n const translator = new AgentEventTranslator();\n\n // Per-run state shared with any sub-agents this run invokes. The boxed\n // tool-call counter, approval policy, and outbound event channel must\n // travel through the sub-agent path so it enforces the same budget and\n // approval gate as the top-level executeTool.\n const runState: RunState = {\n req,\n userId,\n requestId,\n abortController,\n signal,\n approvalPolicy,\n limits,\n translator,\n outboundEvents,\n toolCallsUsed: { count: 0 },\n };\n\n const executeTool = (name: string, args: unknown): Promise<unknown> =>\n this.dispatchToolCall(runState, registered.toolIndex, name, args, 0);\n\n // Drive the adapter and the approval-event side-channel concurrently.\n // Outbound events from both sources flow through `outboundEvents`; the\n // generator below drains the channel in order. executeTool pushes\n // approval-pending events into the same channel before awaiting the gate.\n const driver = (async () => {\n try {\n for (const evt of translator.translate({\n type: \"metadata\",\n data: { threadId: thread.id },\n })) {\n outboundEvents.push(evt);\n }\n\n const pluginNames = this.context\n ? this.context\n .getPluginNames()\n .filter((n) => n !== this.name && n !== \"server\")\n : [];\n const fullPrompt = composePromptForAgent(\n registered,\n this.config.baseSystemPrompt,\n {\n agentName: registered.name,\n pluginNames,\n toolNames: tools.map((t) => t.name),\n },\n );\n\n const messagesWithSystem: Message[] = [\n {\n id: \"system\",\n role: \"system\",\n content: fullPrompt,\n createdAt: new Date(),\n },\n ...thread.messages,\n ];\n\n const stream = registered.adapter.run(\n {\n messages: messagesWithSystem,\n tools,\n threadId: thread.id,\n signal,\n },\n { executeTool, signal },\n );\n\n // The accumulation rule (deltas append, `message` replaces) is shared\n // with `runAgent` and `runSubAgent`; see `consumeAdapterStream` for\n // the rationale.\n const fullContent = await consumeAdapterStream(stream, {\n signal,\n onEvent: (event) => {\n for (const translated of translator.translate(event)) {\n outboundEvents.push(translated);\n }\n },\n });\n\n if (fullContent) {\n await this.threadStore.addMessage(thread.id, userId, {\n id: randomUUID(),\n role: \"assistant\",\n content: fullContent,\n createdAt: new Date(),\n });\n }\n\n for (const evt of translator.finalize()) outboundEvents.push(evt);\n } catch (error) {\n if (signal.aborted) {\n outboundEvents.close();\n return;\n }\n logger.error(\"Agent chat error: %O\", error);\n outboundEvents.close(error);\n return;\n } finally {\n // Any pending approval gates for this stream are auto-denied so the\n // adapter can unwind if it was still waiting.\n this.approvalGate.abortStream(requestId);\n this.untrackStream(requestId);\n // Stateless agents (e.g. autocomplete) don't persist history; drop\n // the thread so `InMemoryThreadStore` doesn't accumulate one record\n // per request. Swallow delete errors — the stream has already\n // finished and the client has the response.\n if (registered.ephemeral) {\n try {\n await this.threadStore.delete(thread.id, userId);\n } catch (err) {\n logger.warn(\n \"Failed to delete ephemeral thread %s: %O\",\n thread.id,\n err,\n );\n }\n }\n }\n outboundEvents.close();\n })();\n\n await this.executeStream<ResponseStreamEvent>(\n res,\n async function* () {\n try {\n for await (const ev of outboundEvents) {\n yield ev;\n }\n } finally {\n await driver.catch(() => undefined);\n }\n },\n {\n ...agentStreamDefaults,\n stream: { ...agentStreamDefaults.stream, streamId: requestId },\n },\n );\n }\n\n /**\n * Dispatch a single tool call from either the top-level adapter or a\n * sub-agent. Centralising this in one method is what makes the budget\n * counter, approval gate, and abort signal observe sub-agent activity:\n * `runSubAgent` reuses the same `runState` and so increments the same\n * counter and emits approval events through the same channel.\n *\n * `depth` is the current sub-agent recursion depth (0 at the top level).\n * It is forwarded to `runSubAgent` when the dispatched entry is itself a\n * sub-agent, so depth limits remain enforced.\n */\n private async dispatchToolCall(\n runState: RunState,\n toolIndex: Map<string, ResolvedToolEntry>,\n name: string,\n args: unknown,\n depth: number,\n ): Promise<unknown> {\n if (runState.toolCallsUsed.count >= runState.limits.maxToolCalls) {\n runState.abortController.abort(\n new Error(\n `Tool-call budget exhausted (limit ${runState.limits.maxToolCalls}).`,\n ),\n );\n throw new Error(\n `Tool-call budget exhausted (limit ${runState.limits.maxToolCalls}). Raise agents({ limits: { maxToolCalls } }) or review the agent's tool-selection logic.`,\n );\n }\n runState.toolCallsUsed.count++;\n\n const entry = toolIndex.get(name);\n if (!entry) throw new Error(`Unknown tool: ${name}`);\n\n if (\n runState.approvalPolicy.requireForDestructive &&\n requiresApproval(entry.def.annotations)\n ) {\n const approvalId = randomUUID();\n for (const ev of runState.translator.translate({\n type: \"approval_pending\",\n approvalId,\n streamId: runState.requestId,\n toolName: name,\n args,\n annotations: entry.def.annotations,\n })) {\n runState.outboundEvents.push(ev);\n }\n const decision = await this.approvalGate.wait({\n approvalId,\n streamId: runState.requestId,\n userId: runState.userId,\n timeoutMs: runState.approvalPolicy.timeoutMs,\n });\n if (decision === \"deny\") {\n return `Tool execution denied by user approval gate (tool: ${name}).`;\n }\n }\n\n let result: unknown;\n if (entry.source === \"toolkit\") {\n if (!this.context) {\n throw new Error(\n \"Plugin tool execution requires PluginContext; this should never happen through createApp\",\n );\n }\n result = await this.context.executeTool(\n runState.req,\n entry.pluginName,\n entry.localName,\n args,\n runState.signal,\n runState.limits.toolCallTimeoutMs,\n );\n } else if (entry.source === \"function\") {\n // Function tools declare their parameters as a JSON-object schema,\n // so adapters always serialize `args` as an object. A non-object\n // value here means the upstream model emitted malformed tool-call\n // JSON; surface a clear error rather than silently passing through\n // a wrong-shape value the tool will then choke on.\n if (typeof args !== \"object\" || args === null || Array.isArray(args)) {\n throw new Error(\n `Function tool '${name}' received non-object arguments (got ${args === null ? \"null\" : Array.isArray(args) ? \"array\" : typeof args}); expected a JSON object.`,\n );\n }\n result = await entry.functionTool.execute(\n args as Record<string, unknown>,\n );\n } else if (entry.source === \"mcp\") {\n if (!this.mcpClient) throw new Error(\"MCP client not connected\");\n const oboToken = runState.req.headers[\"x-forwarded-access-token\"];\n const mcpAuth =\n typeof oboToken === \"string\"\n ? { Authorization: `Bearer ${oboToken}` }\n : undefined;\n result = await this.mcpClient.callTool(entry.mcpToolName, args, mcpAuth);\n } else if (entry.source === \"subagent\") {\n const childAgent = this.agents.get(entry.agentName);\n if (!childAgent)\n throw new Error(`Sub-agent not found: ${entry.agentName}`);\n result = await this.runSubAgent(runState, childAgent, args, depth + 1);\n }\n\n return normalizeToolResult(result);\n }\n\n /**\n * Runs a sub-agent in response to an `agent-<key>` tool call. Returns the\n * concatenated text output to hand back to the parent adapter as the tool\n * result.\n *\n * `depth` starts at 1 for a top-level sub-agent invocation (i.e. the\n * outer `_streamAgent` calls `runSubAgent(..., 1)`) and increments on\n * each nested `runSubAgent` call. Depths exceeding\n * `limits.maxSubAgentDepth` are rejected before any adapter work.\n *\n * Sub-agent tool calls run through `dispatchToolCall` with the same\n * `runState` as the parent — the budget counter and approval gate are\n * therefore enforced for every nested call, not only at the top level.\n */\n private async runSubAgent(\n runState: RunState,\n child: RegisteredAgent,\n args: unknown,\n depth: number,\n ): Promise<string> {\n if (depth > runState.limits.maxSubAgentDepth) {\n throw new Error(\n `Sub-agent depth exceeded (limit ${runState.limits.maxSubAgentDepth}). ` +\n `Raise agents({ limits: { maxSubAgentDepth } }) or break the delegation cycle.`,\n );\n }\n\n const input =\n typeof args === \"object\" &&\n args !== null &&\n typeof (args as { input?: unknown }).input === \"string\"\n ? (args as { input: string }).input\n : JSON.stringify(args);\n const childTools = Array.from(child.toolIndex.values()).map((e) => e.def);\n\n const childExecute = (name: string, childArgs: unknown): Promise<unknown> =>\n this.dispatchToolCall(runState, child.toolIndex, name, childArgs, depth);\n\n const runContext: AgentRunContext = {\n executeTool: childExecute,\n signal: runState.signal,\n };\n\n const pluginNames = this.context\n ? this.context\n .getPluginNames()\n .filter((n) => n !== this.name && n !== \"server\")\n : [];\n const systemPrompt = composePromptForAgent(\n child,\n this.config.baseSystemPrompt,\n {\n agentName: child.name,\n pluginNames,\n toolNames: childTools.map((t) => t.name),\n },\n );\n\n const messages: Message[] = [\n {\n id: \"system\",\n role: \"system\",\n content: systemPrompt,\n createdAt: new Date(),\n },\n {\n id: randomUUID(),\n role: \"user\",\n content: input,\n createdAt: new Date(),\n },\n ];\n\n return consumeAdapterStream(\n child.adapter.run(\n {\n messages,\n tools: childTools,\n threadId: randomUUID(),\n signal: runState.signal,\n },\n runContext,\n ),\n {\n signal: runState.signal,\n // Forward every sub-agent event into the parent's outbound SSE\n // stream so the client sees nested tool_call / tool_result events\n // (UI-action tools like apply_filter / highlight_period rely on\n // this) and the sub-agent's streaming text as it's generated.\n //\n // `metadata` is the one exception: sub-agents have their own\n // threadId, and forwarding it would overwrite the parent's\n // thread state on the client and break multi-turn continuity.\n // Approval-pending events emitted by `dispatchToolCall` already\n // reach `outboundEvents` directly, so they are not routed here.\n onEvent: (event) => {\n if (event.type === \"metadata\") return;\n for (const translated of runState.translator.translate(event)) {\n runState.outboundEvents.push(translated);\n }\n },\n },\n );\n }\n\n private async _handleCancel(req: express.Request, res: express.Response) {\n const parsed = cancelRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n res.status(400).json({\n error: \"Invalid request\",\n details: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { streamId } = parsed.data;\n const entry = this.activeStreams.get(streamId);\n if (!entry) {\n // Stream is unknown or already completed — idempotent no-op.\n res.json({ cancelled: true });\n return;\n }\n const userId = this.resolveUserId(req);\n if (entry.userId !== userId) {\n res.status(403).json({ error: \"Forbidden\" });\n return;\n }\n entry.controller.abort(\"Cancelled by user\");\n this.untrackStream(streamId);\n this.approvalGate.abortStream(streamId);\n res.json({ cancelled: true });\n }\n\n private async _handleApprove(req: express.Request, res: express.Response) {\n const parsed = approvalRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n res.status(400).json({\n error: \"Invalid request\",\n details: parsed.error.flatten().fieldErrors,\n });\n return;\n }\n const { streamId, approvalId, decision } = parsed.data;\n\n const streamEntry = this.activeStreams.get(streamId);\n if (!streamEntry) {\n // Stream has already completed or never existed. Return 404 so the UI\n // knows the approval token is no longer valid (the waiter, if any, has\n // already been timed out or aborted).\n res.status(404).json({ error: \"Stream not found or already completed\" });\n return;\n }\n\n const userId = this.resolveUserId(req);\n if (streamEntry.userId !== userId) {\n res.status(403).json({ error: \"Forbidden\" });\n return;\n }\n\n const result = this.approvalGate.submit({ approvalId, userId, decision });\n if (!result.ok) {\n if (result.reason === \"forbidden\") {\n res.status(403).json({ error: \"Forbidden\" });\n return;\n }\n res.status(404).json({ error: \"Approval not found or already settled\" });\n return;\n }\n\n res.json({ decision });\n }\n\n private async _handleListThreads(\n req: express.Request,\n res: express.Response,\n ) {\n const userId = this.resolveUserId(req);\n const threads = await this.threadStore.list(userId);\n res.json({ threads });\n }\n\n private async _handleGetThread(req: express.Request, res: express.Response) {\n const userId = this.resolveUserId(req);\n const thread = await this.threadStore.get(req.params.threadId, userId);\n if (!thread) {\n res.status(404).json({ error: \"Thread not found\" });\n return;\n }\n res.json(thread);\n }\n\n private async _handleDeleteThread(\n req: express.Request,\n res: express.Response,\n ) {\n const userId = this.resolveUserId(req);\n const deleted = await this.threadStore.delete(req.params.threadId, userId);\n if (!deleted) {\n res.status(404).json({ error: \"Thread not found\" });\n return;\n }\n res.json({ deleted: true });\n }\n\n private resolveAgent(name?: string): RegisteredAgent | null {\n if (name) return this.agents.get(name) ?? null;\n if (this.defaultAgentName) {\n return this.agents.get(this.defaultAgentName) ?? null;\n }\n const first = this.agents.values().next();\n return first.done ? null : first.value;\n }\n\n private printRegistry(): void {\n if (this.agents.size === 0) return;\n console.log(\"\");\n console.log(` ${pc.bold(\"Agents\")} ${pc.dim(`(${this.agents.size})`)}`);\n console.log(` ${pc.dim(\"─\".repeat(60))}`);\n for (const [name, reg] of this.agents) {\n const tools = reg.toolIndex.size;\n const marker = name === this.defaultAgentName ? pc.green(\"●\") : \" \";\n console.log(\n ` ${marker} ${pc.bold(name.padEnd(24))} ${pc.dim(`${tools} tools`)}`,\n );\n }\n console.log(` ${pc.dim(\"─\".repeat(60))}`);\n console.log(\"\");\n }\n\n async shutdown(): Promise<void> {\n this.approvalGate.abortAll();\n if (this.mcpClient) {\n await this.mcpClient.close();\n this.mcpClient = null;\n }\n }\n\n exports() {\n return {\n register: (name: string, def: AgentDefinition) =>\n this.registerCodeAgent(name, def),\n list: () => Array.from(this.agents.keys()),\n get: (name: string) => this.agents.get(name) ?? null,\n reload: () => this.reload(),\n getDefault: () => this.defaultAgentName,\n getThreads: (userId: string) => this.threadStore.list(userId),\n };\n }\n\n private async registerCodeAgent(\n name: string,\n def: AgentDefinition,\n ): Promise<void> {\n const registered = await this.buildRegisteredAgent(name, def, {\n origin: \"code\",\n });\n this.agents.set(name, registered);\n if (!this.defaultAgentName) this.defaultAgentName = name;\n }\n}\n\nfunction normalizeAutoInherit(value: AgentsPluginConfig[\"autoInheritTools\"]): {\n file: boolean;\n code: boolean;\n} {\n // Default is opt-out for both origins. A markdown agent or code-defined\n // agent with no declared `tools:` gets an empty tool index unless the\n // developer explicitly flips `autoInheritTools` on. Even then, only tools\n // whose plugin author marked `autoInheritable: true` are spread — see\n // `applyAutoInherit` for the filter.\n if (value === undefined) return { file: false, code: false };\n if (typeof value === \"boolean\") return { file: value, code: value };\n return { file: value.file ?? false, code: value.code ?? false };\n}\n\nfunction composePromptForAgent(\n registered: RegisteredAgent,\n pluginLevel: BaseSystemPromptOption | undefined,\n ctx: PromptContext,\n): string {\n const perAgent = registered.baseSystemPrompt;\n const resolved = perAgent !== undefined ? perAgent : pluginLevel;\n\n let base = \"\";\n if (resolved === false) {\n base = \"\";\n } else if (typeof resolved === \"string\") {\n base = resolved;\n } else if (typeof resolved === \"function\") {\n base = resolved(ctx);\n } else {\n base = buildBaseSystemPrompt(ctx);\n }\n\n return composeSystemPrompt(base, registered.instructions);\n}\n\n/**\n * Plugin factory for the agents plugin. Reads `config/agents/*.md` by default,\n * resolves toolkits/tools from registered plugins, exposes `appkit.agents.*`\n * runtime API and mounts `/invocations`.\n *\n * @example\n * ```ts\n * import { agents, analytics, createApp, server } from \"@databricks/appkit\";\n *\n * await createApp({\n * plugins: [server(), analytics(), agents()],\n * });\n * ```\n */\nexport const agents = toPlugin(AgentsPlugin);\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { AgentDefinition, AgentTool, AgentsPluginConfig, AutoInheritToolsConfig, BaseSystemPromptOption, PromptContext, RegisteredAgent, ResolvedToolEntry, ToolkitEntry, ToolkitOptions, isToolkitEntry } from "../../core/agent/types.js";
1
+ import { AgentDefinition, AgentTool, AgentTools, AgentToolsFn, AgentsPluginConfig, AutoInheritToolsConfig, BaseSystemPromptOption, PluginToolkitProvider, Plugins, PromptContext, RegisteredAgent, ResolvedToolEntry, ToolkitEntry, ToolkitOptions, isToolkitEntry } from "../../core/agent/types.js";
2
2
  import "../../core/agent/build-toolkit.js";
3
3
  import { LoadContext, LoadResult, agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from "../../core/agent/load-agents.js";
4
- import "./agents.js";
4
+ import { AgentsPlugin, agents } from "./agents.js";
@@ -1,6 +1,6 @@
1
1
  import { buildToolkitEntries } from "../../core/agent/build-toolkit.js";
2
2
  import { isToolkitEntry } from "../../core/agent/types.js";
3
3
  import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir, parseFrontmatter } from "../../core/agent/load-agents.js";
4
- import { AgentsPlugin } from "./agents.js";
4
+ import { AgentsPlugin, agents } from "./agents.js";
5
5
 
6
6
  export { };
@@ -4,19 +4,18 @@ var manifest_default = {
4
4
  name: "agents",
5
5
  displayName: "Agents Plugin",
6
6
  stability: "beta",
7
- hidden: true,
8
7
  description: "AI agents driven by markdown configs or code, with auto-tool-discovery from registered plugins",
9
8
  resources: {
10
9
  "required": [],
11
10
  "optional": [{
12
11
  "type": "serving_endpoint",
13
- "alias": "Model Serving (agents)",
12
+ "alias": "Default LLM for agents",
14
13
  "resourceKey": "agents-serving-endpoint",
15
- "description": "Databricks Model Serving endpoint for agents using workspace-hosted models (`DatabricksAdapter.fromModelServing`). Wire the same endpoint name AppKit reads from `DATABRICKS_AGENT_ENDPOINT` when no per-agent model is configured. Omit when agents use only external adapters.",
14
+ "description": "Default streaming-capable LLM endpoint for agents that don't pin their own model",
16
15
  "permission": "CAN_QUERY",
17
16
  "fields": { "name": {
18
- "env": "DATABRICKS_AGENT_ENDPOINT",
19
- "description": "Endpoint name passed to Model Serving when agents default to `DatabricksAdapter.fromModelServing()`"
17
+ "env": "DATABRICKS_SERVING_ENDPOINT_NAME",
18
+ "description": "Default LLM serving endpoint name"
20
19
  } }
21
20
  }]
22
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool-approval-gate.js","names":[],"sources":["../../../src/plugins/agents/tool-approval-gate.ts"],"sourcesContent":["/**\n * Server-side state for the human-in-the-loop approval gate on\n * `destructive: true` agent tool calls.\n *\n * Lifecycle:\n *\n * 1. `wait(...)` is called from inside `executeTool` when a destructive tool\n * is about to execute. A `Pending` record is registered and a timer is\n * scheduled for auto-deny. The returned promise is what blocks the\n * adapter until the decision arrives.\n * 2. The client receives an `appkit.approval_pending` SSE event carrying the\n * `approvalId` + `streamId` and posts a decision to `POST /chat/approve`.\n * The route calls {@link ToolApprovalGate.submit} which resolves the\n * pending promise and clears the timer.\n * 3. If no submit arrives within `timeoutMs`, the timer fires and the\n * promise resolves with `\"deny\"`.\n *\n * Security invariants:\n *\n * - `submit` verifies that the decider's user id matches the user who\n * initiated the stream (set by `wait`). Mismatches are rejected without\n * resolving the pending promise — this prevents a second user from\n * approving (or denying) another user's destructive action.\n * - `abort(streamId)` cancels every pending gate for a stream and denies\n * each one. Used when the enclosing stream is cancelled or the plugin is\n * shutting down.\n */\ntype ApprovalDecision = \"approve\" | \"deny\";\n\ninterface Pending {\n resolve: (decision: ApprovalDecision) => void;\n userId: string;\n streamId: string;\n timeout: ReturnType<typeof setTimeout>;\n}\n\ntype ApprovalSubmitResult =\n | { ok: true }\n | { ok: false; reason: \"unknown\" | \"forbidden\" };\n\nexport class ToolApprovalGate {\n private pending = new Map<string, Pending>();\n\n /**\n * Register a pending approval and return a promise that resolves with the\n * user's decision or with `\"deny\"` when the timeout elapses. The returned\n * promise never rejects.\n */\n wait(args: {\n approvalId: string;\n streamId: string;\n userId: string;\n timeoutMs: number;\n }): Promise<ApprovalDecision> {\n const { approvalId, streamId, userId, timeoutMs } = args;\n return new Promise<ApprovalDecision>((resolve) => {\n const timeout = setTimeout(() => {\n if (this.pending.delete(approvalId)) {\n resolve(\"deny\");\n }\n }, timeoutMs);\n this.pending.set(approvalId, {\n resolve,\n userId,\n streamId,\n timeout,\n });\n });\n }\n\n /**\n * Settle an approval with a user decision. Returns:\n * - `{ ok: true }` if the pending record existed, the userId matched, and\n * the promise was resolved.\n * - `{ ok: false, reason: \"unknown\" }` if no pending record matches the id.\n * - `{ ok: false, reason: \"forbidden\" }` if the userId does not match the\n * user who initiated the stream.\n */\n submit(args: {\n approvalId: string;\n userId: string;\n decision: ApprovalDecision;\n }): ApprovalSubmitResult {\n const { approvalId, userId, decision } = args;\n const p = this.pending.get(approvalId);\n if (!p) return { ok: false, reason: \"unknown\" };\n if (p.userId !== userId) return { ok: false, reason: \"forbidden\" };\n clearTimeout(p.timeout);\n this.pending.delete(approvalId);\n p.resolve(decision);\n return { ok: true };\n }\n\n /**\n * Cancel all pending gates for a specific stream (e.g., when the user\n * cancels the stream). Each gate resolves with `\"deny\"` so the adapter\n * unwinds cleanly.\n */\n abortStream(streamId: string): void {\n for (const [id, p] of this.pending) {\n if (p.streamId === streamId) {\n clearTimeout(p.timeout);\n this.pending.delete(id);\n p.resolve(\"deny\");\n }\n }\n }\n\n /** Cancel every pending gate. Used at plugin shutdown. */\n abortAll(): void {\n for (const [id, p] of this.pending) {\n clearTimeout(p.timeout);\n this.pending.delete(id);\n p.resolve(\"deny\");\n }\n }\n\n /** Number of pending approvals (test/diagnostic helper). */\n get size(): number {\n return this.pending.size;\n }\n}\n"],"mappings":";AAwCA,IAAa,mBAAb,MAA8B;CAC5B,AAAQ,0BAAU,IAAI,KAAsB;;;;;;CAO5C,KAAK,MAKyB;EAC5B,MAAM,EAAE,YAAY,UAAU,QAAQ,cAAc;AACpD,SAAO,IAAI,SAA2B,YAAY;GAChD,MAAM,UAAU,iBAAiB;AAC/B,QAAI,KAAK,QAAQ,OAAO,WAAW,CACjC,SAAQ,OAAO;MAEhB,UAAU;AACb,QAAK,QAAQ,IAAI,YAAY;IAC3B;IACA;IACA;IACA;IACD,CAAC;IACF;;;;;;;;;;CAWJ,OAAO,MAIkB;EACvB,MAAM,EAAE,YAAY,QAAQ,aAAa;EACzC,MAAM,IAAI,KAAK,QAAQ,IAAI,WAAW;AACtC,MAAI,CAAC,EAAG,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAW;AAC/C,MAAI,EAAE,WAAW,OAAQ,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAa;AAClE,eAAa,EAAE,QAAQ;AACvB,OAAK,QAAQ,OAAO,WAAW;AAC/B,IAAE,QAAQ,SAAS;AACnB,SAAO,EAAE,IAAI,MAAM;;;;;;;CAQrB,YAAY,UAAwB;AAClC,OAAK,MAAM,CAAC,IAAI,MAAM,KAAK,QACzB,KAAI,EAAE,aAAa,UAAU;AAC3B,gBAAa,EAAE,QAAQ;AACvB,QAAK,QAAQ,OAAO,GAAG;AACvB,KAAE,QAAQ,OAAO;;;;CAMvB,WAAiB;AACf,OAAK,MAAM,CAAC,IAAI,MAAM,KAAK,SAAS;AAClC,gBAAa,EAAE,QAAQ;AACvB,QAAK,QAAQ,OAAO,GAAG;AACvB,KAAE,QAAQ,OAAO;;;;CAKrB,IAAI,OAAe;AACjB,SAAO,KAAK,QAAQ"}
1
+ {"version":3,"file":"tool-approval-gate.js","names":[],"sources":["../../../src/plugins/agents/tool-approval-gate.ts"],"sourcesContent":["/**\n * Server-side state for the human-in-the-loop approval gate on mutating\n * agent tool calls — tools annotated with `effect: \"write\" | \"update\" |\n * \"destructive\"` (preferred) or the legacy `destructive: true` boolean.\n *\n * Lifecycle:\n *\n * 1. `wait(...)` is called from inside `executeTool` when a mutating tool\n * is about to execute. A `Pending` record is registered and a timer is\n * scheduled for auto-deny. The returned promise is what blocks the\n * adapter until the decision arrives.\n * 2. The client receives an `appkit.approval_pending` SSE event carrying the\n * `approvalId` + `streamId` and posts a decision to `POST /chat/approve`.\n * The route calls {@link ToolApprovalGate.submit} which resolves the\n * pending promise and clears the timer.\n * 3. If no submit arrives within `timeoutMs`, the timer fires and the\n * promise resolves with `\"deny\"`.\n *\n * Security invariants:\n *\n * - `submit` verifies that the decider's user id matches the user who\n * initiated the stream (set by `wait`). Mismatches are rejected without\n * resolving the pending promise — this prevents a second user from\n * approving (or denying) another user's destructive action.\n * - `abort(streamId)` cancels every pending gate for a stream and denies\n * each one. Used when the enclosing stream is cancelled or the plugin is\n * shutting down.\n */\ntype ApprovalDecision = \"approve\" | \"deny\";\n\ninterface Pending {\n resolve: (decision: ApprovalDecision) => void;\n userId: string;\n streamId: string;\n timeout: ReturnType<typeof setTimeout>;\n}\n\ntype ApprovalSubmitResult =\n | { ok: true }\n | { ok: false; reason: \"unknown\" | \"forbidden\" };\n\nexport class ToolApprovalGate {\n private pending = new Map<string, Pending>();\n\n /**\n * Register a pending approval and return a promise that resolves with the\n * user's decision or with `\"deny\"` when the timeout elapses. The returned\n * promise never rejects.\n */\n wait(args: {\n approvalId: string;\n streamId: string;\n userId: string;\n timeoutMs: number;\n }): Promise<ApprovalDecision> {\n const { approvalId, streamId, userId, timeoutMs } = args;\n return new Promise<ApprovalDecision>((resolve) => {\n const timeout = setTimeout(() => {\n if (this.pending.delete(approvalId)) {\n resolve(\"deny\");\n }\n }, timeoutMs);\n this.pending.set(approvalId, {\n resolve,\n userId,\n streamId,\n timeout,\n });\n });\n }\n\n /**\n * Settle an approval with a user decision. Returns:\n * - `{ ok: true }` if the pending record existed, the userId matched, and\n * the promise was resolved.\n * - `{ ok: false, reason: \"unknown\" }` if no pending record matches the id.\n * - `{ ok: false, reason: \"forbidden\" }` if the userId does not match the\n * user who initiated the stream.\n */\n submit(args: {\n approvalId: string;\n userId: string;\n decision: ApprovalDecision;\n }): ApprovalSubmitResult {\n const { approvalId, userId, decision } = args;\n const p = this.pending.get(approvalId);\n if (!p) return { ok: false, reason: \"unknown\" };\n if (p.userId !== userId) return { ok: false, reason: \"forbidden\" };\n clearTimeout(p.timeout);\n this.pending.delete(approvalId);\n p.resolve(decision);\n return { ok: true };\n }\n\n /**\n * Cancel all pending gates for a specific stream (e.g., when the user\n * cancels the stream). Each gate resolves with `\"deny\"` so the adapter\n * unwinds cleanly.\n */\n abortStream(streamId: string): void {\n for (const [id, p] of this.pending) {\n if (p.streamId === streamId) {\n clearTimeout(p.timeout);\n this.pending.delete(id);\n p.resolve(\"deny\");\n }\n }\n }\n\n /** Cancel every pending gate. Used at plugin shutdown. */\n abortAll(): void {\n for (const [id, p] of this.pending) {\n clearTimeout(p.timeout);\n this.pending.delete(id);\n p.resolve(\"deny\");\n }\n }\n\n /** Number of pending approvals (test/diagnostic helper). */\n get size(): number {\n return this.pending.size;\n }\n}\n"],"mappings":";AAyCA,IAAa,mBAAb,MAA8B;CAC5B,AAAQ,0BAAU,IAAI,KAAsB;;;;;;CAO5C,KAAK,MAKyB;EAC5B,MAAM,EAAE,YAAY,UAAU,QAAQ,cAAc;AACpD,SAAO,IAAI,SAA2B,YAAY;GAChD,MAAM,UAAU,iBAAiB;AAC/B,QAAI,KAAK,QAAQ,OAAO,WAAW,CACjC,SAAQ,OAAO;MAEhB,UAAU;AACb,QAAK,QAAQ,IAAI,YAAY;IAC3B;IACA;IACA;IACA;IACD,CAAC;IACF;;;;;;;;;;CAWJ,OAAO,MAIkB;EACvB,MAAM,EAAE,YAAY,QAAQ,aAAa;EACzC,MAAM,IAAI,KAAK,QAAQ,IAAI,WAAW;AACtC,MAAI,CAAC,EAAG,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAW;AAC/C,MAAI,EAAE,WAAW,OAAQ,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAa;AAClE,eAAa,EAAE,QAAQ;AACvB,OAAK,QAAQ,OAAO,WAAW;AAC/B,IAAE,QAAQ,SAAS;AACnB,SAAO,EAAE,IAAI,MAAM;;;;;;;CAQrB,YAAY,UAAwB;AAClC,OAAK,MAAM,CAAC,IAAI,MAAM,KAAK,QACzB,KAAI,EAAE,aAAa,UAAU;AAC3B,gBAAa,EAAE,QAAQ;AACvB,QAAK,QAAQ,OAAO,GAAG;AACvB,KAAE,QAAQ,OAAO;;;;CAMvB,WAAiB;AACf,OAAK,MAAM,CAAC,IAAI,MAAM,KAAK,SAAS;AAClC,gBAAa,EAAE,QAAQ;AACvB,QAAK,QAAQ,OAAO,GAAG;AACvB,KAAE,QAAQ,OAAO;;;;CAKrB,IAAI,OAAe;AACjB,SAAO,KAAK,QAAQ"}
@@ -4,7 +4,6 @@ import { SQLTypeMarker } from "../../shared/src/sql/types.js";
4
4
  import "../../shared/src/index.js";
5
5
  import { ToolkitEntry, ToolkitOptions } from "../../core/agent/types.js";
6
6
  import { Plugin } from "../../plugin/plugin.js";
7
- import { NamedPluginFactory } from "../../plugin/to-plugin.js";
8
7
  import "../../plugin/index.js";
9
8
  import { PluginManifest } from "../../registry/types.js";
10
9
  import "../../registry/index.js";
@@ -61,8 +60,8 @@ declare class AnalyticsPlugin extends Plugin implements ToolProvider {
61
60
  * Returns the plugin's tools as a keyed record of `ToolkitEntry` markers.
62
61
  * Called by the agents plugin (via `resolveToolkitFromProvider`) to spread
63
62
  * a filtered, renamed view of the plugin's tools into an agent's tool
64
- * index. Most callers should go through `fromPlugin(analytics, opts)` at
65
- * module scope instead of reaching for this directly.
63
+ * index. Inside the function form of `AgentDefinition.tools`, callers
64
+ * reach this method via `plugins.analytics.toolkit(opts)`.
66
65
  */
67
66
  toolkit(opts?: ToolkitOptions): Record<string, ToolkitEntry>;
68
67
  /**
@@ -79,7 +78,7 @@ declare class AnalyticsPlugin extends Plugin implements ToolProvider {
79
78
  /**
80
79
  * @internal
81
80
  */
82
- declare const analytics: ToPlugin<typeof AnalyticsPlugin, IAnalyticsConfig, "analytics"> & NamedPluginFactory<"analytics">;
81
+ declare const analytics: ToPlugin<typeof AnalyticsPlugin, IAnalyticsConfig, "analytics">;
83
82
  //#endregion
84
83
  export { AnalyticsPlugin, analytics };
85
84
  //# sourceMappingURL=analytics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","names":[],"sources":["../../../src/plugins/analytics/analytics.ts"],"mappings":";;;;;;;;;;;;;;;;cAkCa,eAAA,SAAwB,MAAA,YAAkB,YAAA;;SAE9C,QAAA,EAAuB,cAAA;EAAA,iBAEb,WAAA;EAAA,UACC,MAAA,EAAQ,gBAAA;EAAA,QAGlB,SAAA;EAAA,QACA,cAAA;cAEI,MAAA,EAAQ,gBAAA;EAWpB,YAAA,CAAa,MAAA,EAAQ,UAAA;;AAtBvB;;;EAkDQ,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAhDuB;;;;EAsFpB,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAzCA;;;;;;;;;;;;;;;EAqJG,KAAA,CACJ,KAAA,UACA,UAAA,GAAa,MAAA,SAAe,aAAA,sBAC5B,gBAAA,GAAmB,MAAA,eACnB,MAAA,GAAS,WAAA,GACR,OAAA;EAmEA;;;EAAA,UA3Ca,YAAA,CACd,eAAA,EAAiB,eAAA,EACjB,KAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,UAAA,aAAuB,SAAA,CAAU,YAAA;EAItC,QAAA,CAAA,GAAY,OAAA;EAAA,QAIV,KAAA;EAuBR,aAAA,CAAA,GAAiB,mBAAA;EAIX,gBAAA,CACJ,IAAA,UACA,IAAA,WACA,MAAA,GAAS,WAAA,GACR,OAAA;EAnEA;;;;;;;EA8EH,OAAA,CAAQ,IAAA,GAXE,cAAA,GAWoD,MAAA,SAAA,YAAA;EA3RhC;;;;EAmS9B,OAAA,CAAA;IA5RQ;;;2BAkMO,UAAA,GACA,MAAA,SAAe,aAAA,sBAAiC,gBAAA,GAC1C,MAAA,eAAmB,MAAA,GAC7B,WAAA,KACR,OAAA;EAAA;AAAA;;;;cAmGQ,SAAA,EAAS,QAAA,QAAA,eAAA,EAAA,gBAAA,iBAAA,kBAAA"}
1
+ {"version":3,"file":"analytics.d.ts","names":[],"sources":["../../../src/plugins/analytics/analytics.ts"],"mappings":";;;;;;;;;;;;;;;cAmCa,eAAA,SAAwB,MAAA,YAAkB,YAAA;;SAE9C,QAAA,EAAuB,cAAA;EAAA,iBAEb,WAAA;EAAA,UACC,MAAA,EAAQ,gBAAA;EAAA,QAGlB,SAAA;EAAA,QACA,cAAA;cAEI,MAAA,EAAQ,gBAAA;EAWpB,YAAA,CAAa,MAAA,EAAQ,UAAA;EAtBV;;;;EAkDL,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EA1CiB;;;;EAgFd,iBAAA,CACJ,GAAA,EAAK,OAAA,CAAQ,OAAA,EACb,GAAA,EAAK,OAAA,CAAQ,QAAA,GACZ,OAAA;EAFI;;;;;;;;;;;;;;;EAgHD,KAAA,CACJ,KAAA,UACA,UAAA,GAAa,MAAA,SAAe,aAAA,sBAC5B,gBAAA,GAAmB,MAAA,eACnB,MAAA,GAAS,WAAA,GACR,OAAA;EAmEO;;;EAAA,UA3CM,YAAA,CACd,eAAA,EAAiB,eAAA,EACjB,KAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,UAAA,aAAuB,SAAA,CAAU,YAAA;EAItC,QAAA,CAAA,GAAY,OAAA;EAAA,QAIV,KAAA;EAuBR,aAAA,CAAA,GAAiB,mBAAA;EAIX,gBAAA,CACJ,IAAA,UACA,IAAA,WACA,MAAA,GAAS,WAAA,GACR,OAAA;EApRgC;;;;;;;EA+RnC,OAAA,CAAQ,IAAA,GAXE,cAAA,GAWoD,MAAA,SAAA,YAAA;EA3R7C;;;;EAmSjB,OAAA,CAAA;;;;2BA1Fe,UAAA,GACA,MAAA,SAAe,aAAA,sBAAiC,gBAAA,GAC1C,MAAA,eAAmB,MAAA,GAC7B,WAAA,KACR,OAAA;EAAA;AAAA;;;;cAmGQ,SAAA,EAAS,QAAA,QAAA,eAAA,EAAA,gBAAA"}
@@ -12,6 +12,7 @@ import { assertReadOnlySql } from "../../core/agent/tools/sql-policy.js";
12
12
  import { queryDefaults } from "./defaults.js";
13
13
  import manifest_default from "./manifest.js";
14
14
  import { QueryProcessor } from "./query.js";
15
+ import { normalizeAnalyticsFormat } from "./types.js";
15
16
  import { z } from "zod";
16
17
 
17
18
  //#region src/plugins/analytics/analytics.ts
@@ -83,7 +84,8 @@ var AnalyticsPlugin = class extends Plugin {
83
84
  */
84
85
  async _handleQueryRoute(req, res) {
85
86
  const { query_key } = req.params;
86
- const { parameters, format = "JSON" } = req.body;
87
+ const { parameters, format: rawFormat = "JSON_ARRAY" } = req.body;
88
+ const format = normalizeAnalyticsFormat(rawFormat);
87
89
  logger.debug(req, "Executing query: %s (format=%s)", query_key, format);
88
90
  logger.event(req)?.setComponent("analytics", "executeQuery").setContext("analytics", {
89
91
  query_key,
@@ -103,7 +105,7 @@ var AnalyticsPlugin = class extends Plugin {
103
105
  const { query, isAsUser } = queryResult;
104
106
  const executor = isAsUser ? this.asUser(req) : this;
105
107
  const executorKey = isAsUser ? this.resolveUserId(req) : "global";
106
- const queryParameters = format === "ARROW" ? {
108
+ const queryParameters = format === "ARROW_STREAM" ? {
107
109
  formatParameters: {
108
110
  disposition: "EXTERNAL_LINKS",
109
111
  format: "ARROW_STREAM"
@@ -173,11 +175,11 @@ var AnalyticsPlugin = class extends Plugin {
173
175
  description: "Execute a read-only SQL query against the Databricks SQL warehouse. Only SELECT, WITH, SHOW, EXPLAIN, and DESCRIBE statements are accepted; writes are rejected. Returns the query results as JSON.",
174
176
  schema: z.object({ query: z.string().describe("The SQL query to execute. Must be a SELECT, WITH, SHOW, EXPLAIN, or DESCRIBE statement.") }),
175
177
  annotations: {
176
- readOnly: true,
178
+ effect: "read",
177
179
  requiresUserContext: true
178
180
  },
179
181
  autoInheritable: true,
180
- handler: (args, signal) => {
182
+ execute: (args, signal) => {
181
183
  assertReadOnlySql(args.query);
182
184
  return this.query(args.query, void 0, void 0, signal);
183
185
  }
@@ -192,8 +194,8 @@ var AnalyticsPlugin = class extends Plugin {
192
194
  * Returns the plugin's tools as a keyed record of `ToolkitEntry` markers.
193
195
  * Called by the agents plugin (via `resolveToolkitFromProvider`) to spread
194
196
  * a filtered, renamed view of the plugin's tools into an agent's tool
195
- * index. Most callers should go through `fromPlugin(analytics, opts)` at
196
- * module scope instead of reaching for this directly.
197
+ * index. Inside the function form of `AgentDefinition.tools`, callers
198
+ * reach this method via `plugins.analytics.toolkit(opts)`.
197
199
  */
198
200
  toolkit(opts) {
199
201
  return buildToolkitEntries(this.name, this.tools, opts);
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.js","names":["manifest"],"sources":["../../../src/plugins/analytics/analytics.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type express from \"express\";\nimport type {\n AgentToolDefinition,\n IAppRouter,\n PluginExecuteConfig,\n SQLTypeMarker,\n StreamExecutionSettings,\n ToolProvider,\n} from \"shared\";\nimport { z } from \"zod\";\nimport { SQLWarehouseConnector } from \"../../connectors\";\nimport { getWarehouseId, getWorkspaceClient } from \"../../context\";\nimport { buildToolkitEntries } from \"../../core/agent/build-toolkit\";\nimport {\n defineTool,\n executeFromRegistry,\n toolsFromRegistry,\n} from \"../../core/agent/tools/define-tool\";\nimport { assertReadOnlySql } from \"../../core/agent/tools/sql-policy\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { queryDefaults } from \"./defaults\";\nimport manifest from \"./manifest.json\";\nimport { QueryProcessor } from \"./query\";\nimport type {\n AnalyticsQueryResponse,\n IAnalyticsConfig,\n IAnalyticsQueryRequest,\n} from \"./types\";\n\nconst logger = createLogger(\"analytics\");\n\nexport class AnalyticsPlugin extends Plugin implements ToolProvider {\n /** Plugin manifest declaring metadata and resource requirements */\n static manifest = manifest as PluginManifest<\"analytics\">;\n\n protected static description = \"Analytics plugin for data analysis\";\n protected declare config: IAnalyticsConfig;\n\n // analytics services\n private SQLClient: SQLWarehouseConnector;\n private queryProcessor: QueryProcessor;\n\n constructor(config: IAnalyticsConfig) {\n super(config);\n this.config = config;\n this.queryProcessor = new QueryProcessor();\n\n this.SQLClient = new SQLWarehouseConnector({\n timeout: config.timeout,\n telemetry: config.telemetry,\n });\n }\n\n injectRoutes(router: IAppRouter) {\n // Arrow data downloads always run as service principal and bypass the\n // interceptor chain (execute/executeStream). The original query execution\n // handles OBO via executeStream(); this endpoint fetches pre-computed\n // results by job ID.\n this.route(router, {\n name: \"arrow\",\n method: \"get\",\n path: \"/arrow-result/:jobId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleArrowRoute(req, res);\n },\n });\n\n this.route<AnalyticsQueryResponse>(router, {\n name: \"query\",\n method: \"post\",\n path: \"/query/:query_key\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleQueryRoute(req, res);\n },\n });\n }\n\n /**\n * Handle Arrow data download requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleArrowRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n try {\n const { jobId } = req.params;\n const workspaceClient = getWorkspaceClient();\n\n logger.debug(\"Processing Arrow job request for jobId=%s\", jobId);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"getArrowData\").setContext(\"analytics\", {\n job_id: jobId,\n plugin: this.name,\n });\n\n const result = await this.getArrowData(workspaceClient, jobId);\n\n res.setHeader(\"Content-Type\", \"application/octet-stream\");\n res.setHeader(\"Content-Length\", result.data.length.toString());\n res.setHeader(\"Cache-Control\", \"public, max-age=3600\");\n\n logger.debug(\n \"Sending Arrow buffer: %d bytes for job %s\",\n result.data.length,\n jobId,\n );\n res.send(Buffer.from(result.data));\n } catch (error) {\n logger.error(\"Arrow job error: %O\", error);\n res.status(404).json({\n error: error instanceof Error ? error.message : \"Arrow job not found\",\n plugin: this.name,\n });\n }\n }\n\n /**\n * Handle SQL query execution requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleQueryRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { query_key } = req.params;\n const { parameters, format = \"JSON\" } = req.body as IAnalyticsQueryRequest;\n\n // Request-scoped logging with WideEvent tracking\n logger.debug(req, \"Executing query: %s (format=%s)\", query_key, format);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"executeQuery\").setContext(\"analytics\", {\n query_key,\n format,\n parameter_count: parameters ? Object.keys(parameters).length : 0,\n plugin: this.name,\n });\n\n if (!query_key) {\n res.status(400).json({ error: \"query_key is required\" });\n return;\n }\n\n const queryResult = await this.app.getAppQuery(\n query_key,\n req,\n this.devFileReader,\n );\n\n if (!queryResult) {\n res.status(404).json({ error: \"Query not found\" });\n return;\n }\n\n const { query, isAsUser } = queryResult;\n\n // get execution context - user-scoped if .obo.sql, otherwise service principal\n const executor = isAsUser ? this.asUser(req) : this;\n const executorKey = isAsUser ? this.resolveUserId(req) : \"global\";\n\n const queryParameters =\n format === \"ARROW\"\n ? {\n formatParameters: {\n disposition: \"EXTERNAL_LINKS\",\n format: \"ARROW_STREAM\",\n },\n type: \"arrow\",\n }\n : {\n type: \"result\",\n };\n\n const hashedQuery = this.queryProcessor.hashQuery(query);\n\n const defaultConfig: PluginExecuteConfig = {\n ...queryDefaults,\n cache: {\n ...queryDefaults.cache,\n cacheKey: [\n \"analytics:query\",\n query_key,\n JSON.stringify(parameters),\n JSON.stringify(format),\n hashedQuery,\n executorKey,\n ],\n },\n };\n\n const streamExecutionSettings: StreamExecutionSettings = {\n default: defaultConfig,\n };\n\n await executor.executeStream(\n res,\n async (signal) => {\n const processedParams = await this.queryProcessor.processQueryParams(\n query,\n parameters,\n );\n\n const result = await executor.query(\n query,\n processedParams,\n queryParameters.formatParameters,\n signal,\n );\n\n return { type: queryParameters.type, ...result };\n },\n streamExecutionSettings,\n executorKey,\n );\n }\n\n /**\n * Execute a SQL query using the current execution context.\n *\n * When called directly: uses service principal credentials.\n * When called via asUser(req).query(...): uses user's credentials.\n *\n * @example\n * ```typescript\n * // Service principal execution\n * const result = await analytics.query(\"SELECT * FROM table\")\n *\n * // User context execution (in route handler)\n * const result = await this.asUser(req).query(\"SELECT * FROM table\")\n * ```\n */\n async query(\n query: string,\n parameters?: Record<string, SQLTypeMarker | null | undefined>,\n formatParameters?: Record<string, any>,\n signal?: AbortSignal,\n ): Promise<any> {\n const workspaceClient = getWorkspaceClient();\n const warehouseId = await getWarehouseId();\n\n const { statement, parameters: sqlParameters } =\n this.queryProcessor.convertToSQLParameters(query, parameters);\n\n const response = await this.SQLClient.executeStatement(\n workspaceClient,\n {\n statement,\n warehouse_id: warehouseId,\n parameters: sqlParameters,\n ...formatParameters,\n },\n signal,\n );\n\n return response.result;\n }\n\n /**\n * Get Arrow-formatted data for a completed query job.\n */\n protected async getArrowData(\n workspaceClient: WorkspaceClient,\n jobId: string,\n signal?: AbortSignal,\n ): Promise<ReturnType<typeof this.SQLClient.getArrowData>> {\n return await this.SQLClient.getArrowData(workspaceClient, jobId, signal);\n }\n\n async shutdown(): Promise<void> {\n this.streamManager.abortAll();\n }\n\n private tools = {\n query: defineTool({\n description:\n \"Execute a read-only SQL query against the Databricks SQL warehouse. Only SELECT, WITH, SHOW, EXPLAIN, and DESCRIBE statements are accepted; writes are rejected. Returns the query results as JSON.\",\n schema: z.object({\n query: z\n .string()\n .describe(\n \"The SQL query to execute. Must be a SELECT, WITH, SHOW, EXPLAIN, or DESCRIBE statement.\",\n ),\n }),\n annotations: {\n readOnly: true,\n requiresUserContext: true,\n },\n autoInheritable: true,\n handler: (args, signal) => {\n assertReadOnlySql(args.query);\n return this.query(args.query, undefined, undefined, signal);\n },\n }),\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 /**\n * Returns the plugin's tools as a keyed record of `ToolkitEntry` markers.\n * Called by the agents plugin (via `resolveToolkitFromProvider`) to spread\n * a filtered, renamed view of the plugin's tools into an agent's tool\n * index. Most callers should go through `fromPlugin(analytics, opts)` at\n * module scope instead of reaching for this directly.\n */\n toolkit(opts?: import(\"../../core/agent/types\").ToolkitOptions) {\n return buildToolkitEntries(this.name, this.tools, opts);\n }\n\n /**\n * Returns the public exports for the analytics plugin.\n * Note: `asUser()` is automatically added by AppKit.\n */\n exports() {\n return {\n /**\n * Execute a SQL query using service principal credentials.\n */\n query: this.query,\n };\n }\n}\n\n/**\n * @internal\n */\nexport const analytics = toPlugin(AnalyticsPlugin);\n"],"mappings":";;;;;;;;;;;;;;;;;cAYmE;AAoBnE,MAAM,SAAS,aAAa,YAAY;AAExC,IAAa,kBAAb,cAAqC,OAA+B;;CAElE,OAAO,WAAWA;CAElB,OAAiB,cAAc;CAI/B,AAAQ;CACR,AAAQ;CAER,YAAY,QAA0B;AACpC,QAAM,OAAO;AACb,OAAK,SAAS;AACd,OAAK,iBAAiB,IAAI,gBAAgB;AAE1C,OAAK,YAAY,IAAI,sBAAsB;GACzC,SAAS,OAAO;GAChB,WAAW,OAAO;GACnB,CAAC;;CAGJ,aAAa,QAAoB;AAK/B,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;AAC9D,UAAM,KAAK,kBAAkB,KAAK,IAAI;;GAEzC,CAAC;AAEF,OAAK,MAA8B,QAAQ;GACzC,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAsB,QAA0B;AAC9D,UAAM,KAAK,kBAAkB,KAAK,IAAI;;GAEzC,CAAC;;;;;;CAOJ,MAAM,kBACJ,KACA,KACe;AACf,MAAI;GACF,MAAM,EAAE,UAAU,IAAI;GACtB,MAAM,kBAAkB,oBAAoB;AAE5C,UAAO,MAAM,6CAA6C,MAAM;AAGhE,GADc,OAAO,MAAM,IAAI,EACxB,aAAa,aAAa,eAAe,CAAC,WAAW,aAAa;IACvE,QAAQ;IACR,QAAQ,KAAK;IACd,CAAC;GAEF,MAAM,SAAS,MAAM,KAAK,aAAa,iBAAiB,MAAM;AAE9D,OAAI,UAAU,gBAAgB,2BAA2B;AACzD,OAAI,UAAU,kBAAkB,OAAO,KAAK,OAAO,UAAU,CAAC;AAC9D,OAAI,UAAU,iBAAiB,uBAAuB;AAEtD,UAAO,MACL,6CACA,OAAO,KAAK,QACZ,MACD;AACD,OAAI,KAAK,OAAO,KAAK,OAAO,KAAK,CAAC;WAC3B,OAAO;AACd,UAAO,MAAM,uBAAuB,MAAM;AAC1C,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;IAChD,QAAQ,KAAK;IACd,CAAC;;;;;;;CAQN,MAAM,kBACJ,KACA,KACe;EACf,MAAM,EAAE,cAAc,IAAI;EAC1B,MAAM,EAAE,YAAY,SAAS,WAAW,IAAI;AAG5C,SAAO,MAAM,KAAK,mCAAmC,WAAW,OAAO;AAGvE,EADc,OAAO,MAAM,IAAI,EACxB,aAAa,aAAa,eAAe,CAAC,WAAW,aAAa;GACvE;GACA;GACA,iBAAiB,aAAa,OAAO,KAAK,WAAW,CAAC,SAAS;GAC/D,QAAQ,KAAK;GACd,CAAC;AAEF,MAAI,CAAC,WAAW;AACd,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,yBAAyB,CAAC;AACxD;;EAGF,MAAM,cAAc,MAAM,KAAK,IAAI,YACjC,WACA,KACA,KAAK,cACN;AAED,MAAI,CAAC,aAAa;AAChB,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD;;EAGF,MAAM,EAAE,OAAO,aAAa;EAG5B,MAAM,WAAW,WAAW,KAAK,OAAO,IAAI,GAAG;EAC/C,MAAM,cAAc,WAAW,KAAK,cAAc,IAAI,GAAG;EAEzD,MAAM,kBACJ,WAAW,UACP;GACE,kBAAkB;IAChB,aAAa;IACb,QAAQ;IACT;GACD,MAAM;GACP,GACD,EACE,MAAM,UACP;EAEP,MAAM,cAAc,KAAK,eAAe,UAAU,MAAM;EAiBxD,MAAM,0BAAmD,EACvD,SAhByC;GACzC,GAAG;GACH,OAAO;IACL,GAAG,cAAc;IACjB,UAAU;KACR;KACA;KACA,KAAK,UAAU,WAAW;KAC1B,KAAK,UAAU,OAAO;KACtB;KACA;KACD;IACF;GACF,EAIA;AAED,QAAM,SAAS,cACb,KACA,OAAO,WAAW;GAChB,MAAM,kBAAkB,MAAM,KAAK,eAAe,mBAChD,OACA,WACD;GAED,MAAM,SAAS,MAAM,SAAS,MAC5B,OACA,iBACA,gBAAgB,kBAChB,OACD;AAED,UAAO;IAAE,MAAM,gBAAgB;IAAM,GAAG;IAAQ;KAElD,yBACA,YACD;;;;;;;;;;;;;;;;;CAkBH,MAAM,MACJ,OACA,YACA,kBACA,QACc;EACd,MAAM,kBAAkB,oBAAoB;EAC5C,MAAM,cAAc,MAAM,gBAAgB;EAE1C,MAAM,EAAE,WAAW,YAAY,kBAC7B,KAAK,eAAe,uBAAuB,OAAO,WAAW;AAa/D,UAXiB,MAAM,KAAK,UAAU,iBACpC,iBACA;GACE;GACA,cAAc;GACd,YAAY;GACZ,GAAG;GACJ,EACD,OACD,EAEe;;;;;CAMlB,MAAgB,aACd,iBACA,OACA,QACyD;AACzD,SAAO,MAAM,KAAK,UAAU,aAAa,iBAAiB,OAAO,OAAO;;CAG1E,MAAM,WAA0B;AAC9B,OAAK,cAAc,UAAU;;CAG/B,AAAQ,QAAQ,EACd,OAAO,WAAW;EAChB,aACE;EACF,QAAQ,EAAE,OAAO,EACf,OAAO,EACJ,QAAQ,CACR,SACC,0FACD,EACJ,CAAC;EACF,aAAa;GACX,UAAU;GACV,qBAAqB;GACtB;EACD,iBAAiB;EACjB,UAAU,MAAM,WAAW;AACzB,qBAAkB,KAAK,MAAM;AAC7B,UAAO,KAAK,MAAM,KAAK,OAAO,QAAW,QAAW,OAAO;;EAE9D,CAAC,EACH;CAED,gBAAuC;AACrC,SAAO,kBAAkB,KAAK,MAAM;;CAGtC,MAAM,iBACJ,MACA,MACA,QACkB;AAClB,SAAO,oBAAoB,KAAK,OAAO,MAAM,MAAM,OAAO;;;;;;;;;CAU5D,QAAQ,MAAwD;AAC9D,SAAO,oBAAoB,KAAK,MAAM,KAAK,OAAO,KAAK;;;;;;CAOzD,UAAU;AACR,SAAO,EAIL,OAAO,KAAK,OACb;;;;;;AAOL,MAAa,YAAY,SAAS,gBAAgB"}
1
+ {"version":3,"file":"analytics.js","names":["manifest"],"sources":["../../../src/plugins/analytics/analytics.ts"],"sourcesContent":["import type { WorkspaceClient } from \"@databricks/sdk-experimental\";\nimport type express from \"express\";\nimport type {\n AgentToolDefinition,\n IAppRouter,\n PluginExecuteConfig,\n SQLTypeMarker,\n StreamExecutionSettings,\n ToolProvider,\n} from \"shared\";\nimport { z } from \"zod\";\nimport { SQLWarehouseConnector } from \"../../connectors\";\nimport { getWarehouseId, getWorkspaceClient } from \"../../context\";\nimport { buildToolkitEntries } from \"../../core/agent/build-toolkit\";\nimport {\n defineTool,\n executeFromRegistry,\n toolsFromRegistry,\n} from \"../../core/agent/tools/define-tool\";\nimport { assertReadOnlySql } from \"../../core/agent/tools/sql-policy\";\nimport { createLogger } from \"../../logging/logger\";\nimport { Plugin, toPlugin } from \"../../plugin\";\nimport type { PluginManifest } from \"../../registry\";\nimport { queryDefaults } from \"./defaults\";\nimport manifest from \"./manifest.json\";\nimport { QueryProcessor } from \"./query\";\nimport type {\n AnalyticsQueryResponse,\n IAnalyticsConfig,\n IAnalyticsQueryRequest,\n} from \"./types\";\nimport { normalizeAnalyticsFormat } from \"./types\";\n\nconst logger = createLogger(\"analytics\");\n\nexport class AnalyticsPlugin extends Plugin implements ToolProvider {\n /** Plugin manifest declaring metadata and resource requirements */\n static manifest = manifest as PluginManifest<\"analytics\">;\n\n protected static description = \"Analytics plugin for data analysis\";\n protected declare config: IAnalyticsConfig;\n\n // analytics services\n private SQLClient: SQLWarehouseConnector;\n private queryProcessor: QueryProcessor;\n\n constructor(config: IAnalyticsConfig) {\n super(config);\n this.config = config;\n this.queryProcessor = new QueryProcessor();\n\n this.SQLClient = new SQLWarehouseConnector({\n timeout: config.timeout,\n telemetry: config.telemetry,\n });\n }\n\n injectRoutes(router: IAppRouter) {\n // Arrow data downloads always run as service principal and bypass the\n // interceptor chain (execute/executeStream). The original query execution\n // handles OBO via executeStream(); this endpoint fetches pre-computed\n // results by job ID.\n this.route(router, {\n name: \"arrow\",\n method: \"get\",\n path: \"/arrow-result/:jobId\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleArrowRoute(req, res);\n },\n });\n\n this.route<AnalyticsQueryResponse>(router, {\n name: \"query\",\n method: \"post\",\n path: \"/query/:query_key\",\n handler: async (req: express.Request, res: express.Response) => {\n await this._handleQueryRoute(req, res);\n },\n });\n }\n\n /**\n * Handle Arrow data download requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleArrowRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n try {\n const { jobId } = req.params;\n const workspaceClient = getWorkspaceClient();\n\n logger.debug(\"Processing Arrow job request for jobId=%s\", jobId);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"getArrowData\").setContext(\"analytics\", {\n job_id: jobId,\n plugin: this.name,\n });\n\n const result = await this.getArrowData(workspaceClient, jobId);\n\n res.setHeader(\"Content-Type\", \"application/octet-stream\");\n res.setHeader(\"Content-Length\", result.data.length.toString());\n res.setHeader(\"Cache-Control\", \"public, max-age=3600\");\n\n logger.debug(\n \"Sending Arrow buffer: %d bytes for job %s\",\n result.data.length,\n jobId,\n );\n res.send(Buffer.from(result.data));\n } catch (error) {\n logger.error(\"Arrow job error: %O\", error);\n res.status(404).json({\n error: error instanceof Error ? error.message : \"Arrow job not found\",\n plugin: this.name,\n });\n }\n }\n\n /**\n * Handle SQL query execution requests.\n * When called via asUser(req), uses the user's Databricks credentials.\n */\n async _handleQueryRoute(\n req: express.Request,\n res: express.Response,\n ): Promise<void> {\n const { query_key } = req.params;\n const { parameters, format: rawFormat = \"JSON_ARRAY\" } =\n req.body as IAnalyticsQueryRequest;\n const format = normalizeAnalyticsFormat(rawFormat);\n\n // Request-scoped logging with WideEvent tracking\n logger.debug(req, \"Executing query: %s (format=%s)\", query_key, format);\n\n const event = logger.event(req);\n event?.setComponent(\"analytics\", \"executeQuery\").setContext(\"analytics\", {\n query_key,\n format,\n parameter_count: parameters ? Object.keys(parameters).length : 0,\n plugin: this.name,\n });\n\n if (!query_key) {\n res.status(400).json({ error: \"query_key is required\" });\n return;\n }\n\n const queryResult = await this.app.getAppQuery(\n query_key,\n req,\n this.devFileReader,\n );\n\n if (!queryResult) {\n res.status(404).json({ error: \"Query not found\" });\n return;\n }\n\n const { query, isAsUser } = queryResult;\n\n // get execution context - user-scoped if .obo.sql, otherwise service principal\n const executor = isAsUser ? this.asUser(req) : this;\n const executorKey = isAsUser ? this.resolveUserId(req) : \"global\";\n\n const queryParameters =\n format === \"ARROW_STREAM\"\n ? {\n formatParameters: {\n disposition: \"EXTERNAL_LINKS\",\n format: \"ARROW_STREAM\",\n },\n type: \"arrow\",\n }\n : {\n type: \"result\",\n };\n\n const hashedQuery = this.queryProcessor.hashQuery(query);\n\n const defaultConfig: PluginExecuteConfig = {\n ...queryDefaults,\n cache: {\n ...queryDefaults.cache,\n cacheKey: [\n \"analytics:query\",\n query_key,\n JSON.stringify(parameters),\n JSON.stringify(format),\n hashedQuery,\n executorKey,\n ],\n },\n };\n\n const streamExecutionSettings: StreamExecutionSettings = {\n default: defaultConfig,\n };\n\n await executor.executeStream(\n res,\n async (signal) => {\n const processedParams = await this.queryProcessor.processQueryParams(\n query,\n parameters,\n );\n\n const result = await executor.query(\n query,\n processedParams,\n queryParameters.formatParameters,\n signal,\n );\n\n return { type: queryParameters.type, ...result };\n },\n streamExecutionSettings,\n executorKey,\n );\n }\n\n /**\n * Execute a SQL query using the current execution context.\n *\n * When called directly: uses service principal credentials.\n * When called via asUser(req).query(...): uses user's credentials.\n *\n * @example\n * ```typescript\n * // Service principal execution\n * const result = await analytics.query(\"SELECT * FROM table\")\n *\n * // User context execution (in route handler)\n * const result = await this.asUser(req).query(\"SELECT * FROM table\")\n * ```\n */\n async query(\n query: string,\n parameters?: Record<string, SQLTypeMarker | null | undefined>,\n formatParameters?: Record<string, any>,\n signal?: AbortSignal,\n ): Promise<any> {\n const workspaceClient = getWorkspaceClient();\n const warehouseId = await getWarehouseId();\n\n const { statement, parameters: sqlParameters } =\n this.queryProcessor.convertToSQLParameters(query, parameters);\n\n const response = await this.SQLClient.executeStatement(\n workspaceClient,\n {\n statement,\n warehouse_id: warehouseId,\n parameters: sqlParameters,\n ...formatParameters,\n },\n signal,\n );\n\n return response.result;\n }\n\n /**\n * Get Arrow-formatted data for a completed query job.\n */\n protected async getArrowData(\n workspaceClient: WorkspaceClient,\n jobId: string,\n signal?: AbortSignal,\n ): Promise<ReturnType<typeof this.SQLClient.getArrowData>> {\n return await this.SQLClient.getArrowData(workspaceClient, jobId, signal);\n }\n\n async shutdown(): Promise<void> {\n this.streamManager.abortAll();\n }\n\n private tools = {\n query: defineTool({\n description:\n \"Execute a read-only SQL query against the Databricks SQL warehouse. Only SELECT, WITH, SHOW, EXPLAIN, and DESCRIBE statements are accepted; writes are rejected. Returns the query results as JSON.\",\n schema: z.object({\n query: z\n .string()\n .describe(\n \"The SQL query to execute. Must be a SELECT, WITH, SHOW, EXPLAIN, or DESCRIBE statement.\",\n ),\n }),\n annotations: {\n effect: \"read\",\n requiresUserContext: true,\n },\n autoInheritable: true,\n execute: (args, signal) => {\n assertReadOnlySql(args.query);\n return this.query(args.query, undefined, undefined, signal);\n },\n }),\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 /**\n * Returns the plugin's tools as a keyed record of `ToolkitEntry` markers.\n * Called by the agents plugin (via `resolveToolkitFromProvider`) to spread\n * a filtered, renamed view of the plugin's tools into an agent's tool\n * index. Inside the function form of `AgentDefinition.tools`, callers\n * reach this method via `plugins.analytics.toolkit(opts)`.\n */\n toolkit(opts?: import(\"../../core/agent/types\").ToolkitOptions) {\n return buildToolkitEntries(this.name, this.tools, opts);\n }\n\n /**\n * Returns the public exports for the analytics plugin.\n * Note: `asUser()` is automatically added by AppKit.\n */\n exports() {\n return {\n /**\n * Execute a SQL query using service principal credentials.\n */\n query: this.query,\n };\n }\n}\n\n/**\n * @internal\n */\nexport const analytics = toPlugin(AnalyticsPlugin);\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { normalizeAnalyticsFormat } from "./types.js";
1
2
  import { AnalyticsPlugin, analytics } from "./analytics.js";
2
3
 
3
4
  export { };
@@ -0,0 +1,15 @@
1
+ //#region src/plugins/analytics/types.ts
2
+ /**
3
+ * Map a (possibly legacy) AnalyticsFormat to its canonical form.
4
+ * Legacy values come from appkit/appkit-ui < 0.33.0 and can be removed
5
+ * along with the deprecated aliases once no such consumer remains.
6
+ */
7
+ function normalizeAnalyticsFormat(f) {
8
+ if (f === "JSON") return "JSON_ARRAY";
9
+ if (f === "ARROW") return "ARROW_STREAM";
10
+ return f;
11
+ }
12
+
13
+ //#endregion
14
+ export { normalizeAnalyticsFormat };
15
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../../src/plugins/analytics/types.ts"],"sourcesContent":["import type { BasePluginConfig } from \"shared\";\n\nexport interface IAnalyticsConfig extends BasePluginConfig {\n timeout?: number;\n}\n\n/**\n * Supported response formats for analytics queries.\n *\n * \"JSON\" and \"ARROW\" are legacy aliases kept for backwards compatibility\n * with appkit/appkit-ui < 0.33.0 — safe to remove once no consumer is on\n * a pre-0.33.0 version. The route handler normalizes them to their\n * canonical equivalents before any downstream code reads the value.\n */\nexport type AnalyticsFormat =\n | \"JSON_ARRAY\"\n | \"ARROW_STREAM\"\n /** @deprecated Use \"JSON_ARRAY\". Safe to remove once no consumer is on appkit < 0.33.0. */\n | \"JSON\"\n /** @deprecated Use \"ARROW_STREAM\". Safe to remove once no consumer is on appkit < 0.33.0. */\n | \"ARROW\";\n\n/** Canonical (post-normalization) analytics format values. */\ntype CanonicalAnalyticsFormat = \"JSON_ARRAY\" | \"ARROW_STREAM\";\n\n/**\n * Map a (possibly legacy) AnalyticsFormat to its canonical form.\n * Legacy values come from appkit/appkit-ui < 0.33.0 and can be removed\n * along with the deprecated aliases once no such consumer remains.\n */\nexport function normalizeAnalyticsFormat(\n f: AnalyticsFormat,\n): CanonicalAnalyticsFormat {\n if (f === \"JSON\") return \"JSON_ARRAY\";\n if (f === \"ARROW\") return \"ARROW_STREAM\";\n return f;\n}\n\nexport interface IAnalyticsQueryRequest {\n parameters?: Record<string, any>;\n format?: AnalyticsFormat;\n}\n\nexport interface AnalyticsQueryResponse {\n chunk_index: number;\n row_offset: number;\n row_count: number;\n data: any[];\n}\n"],"mappings":";;;;;;AA8BA,SAAgB,yBACd,GAC0B;AAC1B,KAAI,MAAM,OAAQ,QAAO;AACzB,KAAI,MAAM,QAAS,QAAO;AAC1B,QAAO"}
@@ -0,0 +1,2 @@
1
+ import { agents } from "./agents/agents.js";
2
+ import "./agents/index.js";