@databricks/appkit 0.68.0 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/appkit/package.js +1 -1
- package/dist/core/agent/skills/types.d.ts +6 -1
- package/dist/core/agent/skills/types.d.ts.map +1 -1
- package/dist/plugins/agents/active-stream-tracker.js +52 -0
- package/dist/plugins/agents/active-stream-tracker.js.map +1 -0
- package/dist/plugins/agents/adapter-extensions.js +50 -0
- package/dist/plugins/agents/adapter-extensions.js.map +1 -0
- package/dist/plugins/agents/agents.d.ts +6 -77
- package/dist/plugins/agents/agents.d.ts.map +1 -1
- package/dist/plugins/agents/agents.js +57 -431
- package/dist/plugins/agents/agents.js.map +1 -1
- package/dist/plugins/agents/approval.js +28 -0
- package/dist/plugins/agents/approval.js.map +1 -0
- package/dist/plugins/agents/builtin-tools.js +39 -0
- package/dist/plugins/agents/builtin-tools.js.map +1 -0
- package/dist/plugins/agents/prompt.js +27 -0
- package/dist/plugins/agents/prompt.js.map +1 -0
- package/dist/plugins/agents/registry.js +56 -0
- package/dist/plugins/agents/registry.js.map +1 -0
- package/dist/plugins/agents/resolve-config.js +40 -0
- package/dist/plugins/agents/resolve-config.js.map +1 -0
- package/dist/plugins/agents/skill-loader.js +139 -0
- package/dist/plugins/agents/skill-loader.js.map +1 -0
- package/dist/plugins/agents/tool-dispatch.js +124 -0
- package/dist/plugins/agents/tool-dispatch.js.map +1 -0
- package/dist/plugins/server/index.js +2 -2
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js +3 -3
- package/dist/plugins/server/remote-tunnel/remote-tunnel-manager.js.map +1 -1
- package/dist/plugins/server/static-server.js +3 -3
- package/dist/plugins/server/static-server.js.map +1 -1
- package/dist/plugins/server/utils.js +3 -3
- package/dist/plugins/server/utils.js.map +1 -1
- package/dist/plugins/server/vite-dev-server.js +4 -4
- package/dist/plugins/server/vite-dev-server.js.map +1 -1
- package/dist/shared/src/schemas/manifest.d.ts +33 -33
- package/dist/type-generator/database/generate.js +3 -3
- package/dist/type-generator/database/generate.js.map +1 -1
- package/dist/type-generator/migration.js +2 -2
- package/dist/type-generator/migration.js.map +1 -1
- package/dist/type-generator/serving/server-file-extractor.js +3 -3
- package/dist/type-generator/serving/server-file-extractor.js.map +1 -1
- package/package.json +1 -1
- 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 { existsSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type express from \"express\";\nimport pc from \"picocolors\";\nimport type {\n AgentAdapter,\n AgentRunContext,\n AgentToolDefinition,\n IAppRouter,\n Message,\n PluginPhase,\n ResponseOutputMessage,\n ResponseStreamEvent,\n Thread,\n ToolAnnotations,\n ToolProvider,\n} from \"shared\";\n\nimport {\n isSupervisorTool,\n SUPERVISOR_EXTENSION_KEY,\n type SupervisorTool,\n} from \"../../agents/supervisor-api\";\nimport { FilesConnector } from \"../../connectors/files\";\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 {\n CODE_AGENTS_SOURCE_DIR,\n loadCodeAgentsFromDir,\n resolveCodeAgentsDir,\n} from \"../../core/agent/load-code-agents\";\nimport { normalizeToolResult } from \"../../core/agent/normalize-result\";\nimport { createPluginsProxy } from \"../../core/agent/plugins-map\";\nimport {\n loadSkillsFromDir,\n parseSkill,\n type ResolvedSkillCatalog,\n readSkillResource,\n renderLoadedSkill,\n renderSkillCatalog,\n resolveSkill,\n resolveSkillCatalog,\n type SkillDefinition,\n} from \"../../core/agent/skills\";\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 { defineManifest } from \"../../registry\";\nimport type { WorkspaceClient } from \"../../workspace-client\";\nimport { agentStreamDefaults } from \"./defaults\";\nimport { EventChannel } from \"./event-channel\";\nimport { AgentEventTranslator } from \"./event-translator\";\nimport manifest from \"./manifest.json\";\nimport {\n currentTraceId,\n initAgentTracing,\n linkTraceToRun,\n traceAgent,\n traceTool,\n} from \"./mlflow\";\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\n/** Deprecated markdown location, read as a fallback with a one-time warning. */\nconst LEGACY_MARKDOWN_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 = defineManifest<\"agents\">(manifest);\n static phase: PluginPhase = \"deferred\";\n\n declare protected 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 /** Guards the `agents({ agents })` deprecation warning to once per instance. */\n private agentsMapDeprecationWarned = false;\n /** Guards the `config/agents` deprecation warning to once per instance. */\n private configAgentsDeprecationWarned = false;\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 /**\n * Shared global skill pool (bundle `skills/` + catalog volume), loaded once\n * per registry build. Read by {@link buildRegisteredAgent} to resolve each\n * agent's visible catalog and by the live `register` path.\n */\n private globalSkills: SkillDefinition[] = [];\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 await initAgentTracing();\n const { agents, defaultAgentName } = await this.buildAgentRegistry();\n this.agents = agents;\n this.defaultAgentName = defaultAgentName;\n this.mountInvokeRoutes();\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 // Two \"code\" sources: discovered files and the deprecated `agents({ agents })` map.\n const discovered = await this.loadCodeAgents();\n const deprecatedMapRaw = this.config.agents ?? {};\n\n // Global skills (bundle + configured UC volume) load in parallel; per-agent\n // skills resolve later during agent build.\n const [bundleSkills, volumeSkills] = await Promise.all([\n this.loadGlobalSkills(),\n this.loadVolumeSkills(),\n ]);\n this.globalSkills = [...bundleSkills, ...volumeSkills];\n\n if (Object.keys(deprecatedMapRaw).length > 0) {\n this.warnAgentsMapDeprecated();\n }\n\n // On a discovered/map id clash, discovery wins (drop the map entry) rather\n // than crash boot on upgrade.\n const deprecatedMap: Record<string, AgentDefinition> = {};\n for (const [id, def] of Object.entries(deprecatedMapRaw)) {\n if (discovered[id]) {\n logger.warn(\n \"Agent '%s' is both discovered in %s and passed to agents({ agents }). \" +\n \"Using the discovered file; ignoring the map entry.\",\n id,\n CODE_AGENTS_SOURCE_DIR,\n );\n continue;\n }\n deprecatedMap[id] = def;\n }\n\n // Code agents also resolve markdown `agents: [child]` sub-agent references.\n const codeAgents: Record<string, AgentDefinition> = {\n ...discovered,\n ...deprecatedMap,\n };\n\n const { defs: fileDefs, defaultAgent: fileDefault } =\n await this.loadFileDefinitions(codeAgents);\n\n // Merge order (markdown, discovered, map) sets precedence and the\n // first-registered default fallback.\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(discovered)) {\n if (merged[name]?.src.origin === \"file\") {\n // Discovery is new API — clash with markdown is a hard error (the\n // deprecated map only warns). A folder with both agent.ts and\n // agent.md lands here too: one kind per folder.\n throw new Error(\n `Agent '${name}' is defined as both a code agent (agent.ts) and a markdown agent (agent.md) — ` +\n `in one folder, or across server/agents and the deprecated config/agents fallback. ` +\n `Keep a single kind per id. Available: ${Object.keys(merged).sort().join(\", \")}`,\n );\n }\n merged[name] = { def, src: { origin: \"code\" } };\n }\n for (const [name, def] of Object.entries(deprecatedMap)) {\n if (merged[name]?.src.origin === \"file\") {\n logger.warn(\n \"Agent '%s' defined in both code and a markdown file. Code definition takes precedence.\",\n name,\n );\n }\n merged[name] = { def, src: { origin: \"code\" } };\n }\n\n const agents = new Map<string, RegisteredAgent>();\n\n if (Object.keys(merged).length === 0) {\n logger.info(\n \"No agents registered (no files in %s, no discovered or code-defined agents)\",\n this.resolvedAgentsDir(),\n );\n return { agents, defaultAgentName: null };\n }\n\n for (const [name, { def, src }] of Object.entries(merged)) {\n try {\n agents.set(name, await this.buildRegisteredAgent(name, def, src));\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 return {\n agents,\n defaultAgentName: this.resolveDefaultAgent(agents, merged, fileDefault),\n };\n }\n\n /**\n * Resolves the default agent. Precedence: explicit `config.defaultAgent` >\n * a code/discovered agent flagged `default: true` (stable id order) >\n * markdown `default: true` > first registered (insertion order).\n */\n private resolveDefaultAgent(\n agents: Map<string, RegisteredAgent>,\n merged: Record<string, { def: AgentDefinition; src: AgentSource }>,\n fileDefault: string | null,\n ): string | null {\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 return this.config.defaultAgent;\n }\n\n const codeDefault = Object.keys(merged)\n .filter(\n (id) => merged[id].src.origin === \"code\" && merged[id].def.default,\n )\n .sort()[0];\n if (codeDefault) return codeDefault;\n\n if (fileDefault && agents.has(fileDefault)) return fileDefault;\n\n return agents.keys().next().value ?? null;\n }\n\n /**\n * Emits the one-time deprecation warning for the `agents({ agents })` map.\n * Guarded so `reload()` (which re-runs `buildAgentRegistry`) doesn't spam it.\n */\n private warnAgentsMapDeprecated(): void {\n if (this.agentsMapDeprecationWarned) return;\n this.agentsMapDeprecationWarned = true;\n logger.warn(\n \"agents({ agents: { ... } }) is deprecated. Put each code agent in its own folder under \" +\n \"server/agents/<id>/agent.ts (export default createAgent({ ... })) and it is discovered \" +\n \"automatically — the call collapses to agents({ ... }) with no agent map. The `agents` field \" +\n \"still works but will be removed in a future minor. See docs/plugins/agents.md.\",\n );\n }\n\n /**\n * One-time deprecation warning for markdown agents still living in\n * `config/agents/`. Guarded so `reload()` doesn't spam it.\n */\n private warnConfigAgentsDeprecated(): void {\n if (this.configAgentsDeprecationWarned) return;\n this.configAgentsDeprecationWarned = true;\n logger.warn(\n \"Markdown agents under config/agents/ are deprecated. Move each config/agents/<id>/agent.md to \" +\n \"server/agents/<id>/agent.md — every agent now lives in one place (server/agents). config/agents \" +\n \"is still read for now but will be removed in a future minor. See docs/plugins/agents.md.\",\n );\n }\n\n private resolvedAgentsDir(): string {\n return path.resolve(process.cwd(), CODE_AGENTS_SOURCE_DIR);\n }\n\n /**\n * Discovers code agents (see {@link resolveCodeAgentsDir} and\n * {@link loadCodeAgentsFromDir}). Warns if sources exist but nothing was\n * discovered — usually the build didn't emit the compiled agents — unless\n * the deprecated `agents({ agents })` map is carrying them instead.\n */\n private async loadCodeAgents(): Promise<Record<string, AgentDefinition>> {\n const resolved = resolveCodeAgentsDir({\n cwd: process.cwd(),\n exists: existsSync,\n });\n\n const discovered = await loadCodeAgentsFromDir(resolved.dir, {\n extensions: resolved.extensions,\n });\n\n const usingDeprecatedMap = Object.keys(this.config.agents ?? {}).length > 0;\n const sourceDir = this.resolvedAgentsDir();\n if (\n Object.keys(discovered).length === 0 &&\n !usingDeprecatedMap &&\n this.hasCodeAgentSources(sourceDir)\n ) {\n logger.warn(\n \"Found code-agent sources in %s but discovered no code agents (scanned %s). \" +\n \"In a production build, ensure `<dir>/*/agent.ts` is included as tsdown entries so the compiled agents are emitted.\",\n sourceDir,\n resolved.dir,\n );\n }\n\n return discovered;\n }\n\n /** True when `dir` holds at least one `<id>/agent.ts` folder. */\n private hasCodeAgentSources(dir: string): boolean {\n try {\n return readdirSync(dir, { withFileTypes: true }).some((e) => {\n if (!e.isDirectory() && !e.isSymbolicLink()) return false;\n try {\n return readdirSync(path.join(dir, e.name)).includes(\"agent.ts\");\n } catch {\n return false;\n }\n });\n } catch {\n return false;\n }\n }\n\n private async loadFileDefinitions(\n codeAgents: Record<string, AgentDefinition>,\n ): Promise<{\n defs: Record<string, AgentDefinition>;\n defaultAgent: string | null;\n }> {\n const primaryDir = this.resolvedAgentsDir();\n\n // Discovered code agents + the deprecated map resolve markdown `agents:`\n // sub-agent references, so a markdown parent can delegate to a code child.\n const baseCtx = {\n defaultModel: this.config.defaultModel,\n availableTools: this.config.tools ?? {},\n plugins: this.pluginProviderIndex(),\n };\n\n const legacyDir = path.resolve(process.cwd(), LEGACY_MARKDOWN_DIR);\n\n // Common case: no deprecated config/agents/ dir → a single scan of server/agents.\n if (!existsSync(legacyDir)) {\n return loadAgentsFromDir(primaryDir, { ...baseCtx, codeAgents });\n }\n\n // Deprecated fallback: config/agents/ exists → scan it first, then server/agents\n // with the legacy defs added as resolvable sub-agent targets, so a parent already\n // moved to server/agents can still reference a child left in config/agents\n // mid-migration. Code agents keep precedence; server/agents wins on an id clash.\n const legacy = await loadAgentsFromDir(legacyDir, {\n ...baseCtx,\n codeAgents,\n });\n const primary = await loadAgentsFromDir(primaryDir, {\n ...baseCtx,\n codeAgents: { ...legacy.defs, ...codeAgents },\n });\n\n if (Object.keys(legacy.defs).length === 0) return primary;\n\n this.warnConfigAgentsDeprecated();\n return {\n defs: { ...legacy.defs, ...primary.defs },\n defaultAgent: primary.defaultAgent ?? legacy.defaultAgent,\n };\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 skills = await this.resolveAgentSkills(name, def, src);\n const toolIndex = await this.buildToolIndex(name, def, src, skills);\n\n warnOnCapabilityMismatch(name, adapter, toolIndex);\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 generationParams: def.generationParams,\n ephemeral: def.ephemeral,\n skills,\n };\n }\n\n /** Loads the shared global skill pool from `<agentsDir>/skills/`. */\n private async loadGlobalSkills(): Promise<SkillDefinition[]> {\n const dir = this.resolvedAgentsDir();\n if (!dir) return [];\n return loadSkillsFromDir(path.join(dir, \"skills\"), \"bundle-global\");\n }\n\n /** Configured catalog-skills volume path, or null when none is set. */\n private resolveSkillsVolume(): string | null {\n const configured =\n this.config.skillsVolume ?? process.env.DATABRICKS_VOLUME_AGENT_SKILLS;\n return configured && configured.trim() !== \"\" ? configured.trim() : null;\n }\n\n /**\n * Workspace client used to read catalog skills. v1 reads as the app service\n * principal; `getWorkspaceClient()` resolves to SP outside a user scope\n * (boot and skill-tool dispatch are both unscoped). This is the single\n * switch point for a future OBO mode.\n */\n private skillWorkspaceClient(): WorkspaceClient {\n return getWorkspaceClient();\n }\n\n /**\n * Discovers catalog skills from the configured UC Volume, read as the\n * service principal at boot (and on `reload()`). Each `<volume>/<name>/`\n * folder with a `SKILL.md` becomes a `source: \"volume\"` skill. Best-effort:\n * a missing volume, unavailable workspace client, or a malformed individual\n * skill is logged and skipped rather than failing the whole registry build.\n */\n private async loadVolumeSkills(): Promise<SkillDefinition[]> {\n const volume = this.resolveSkillsVolume();\n if (!volume) return [];\n\n if ((this.config.skillCredentialMode ?? \"sp\") === \"obo\") {\n logger.warn(\n \"skillCredentialMode 'obo' is not wired yet; reading catalog skills as the service principal.\",\n );\n }\n\n let client: WorkspaceClient;\n try {\n client = this.skillWorkspaceClient();\n } catch (err) {\n logger.warn(\n \"Skipping catalog skills at '%s': no workspace client available (%s).\",\n volume,\n err instanceof Error ? err.message : String(err),\n );\n return [];\n }\n\n const connector = new FilesConnector({ defaultVolume: volume });\n let entries: Awaited<ReturnType<FilesConnector[\"list\"]>>;\n try {\n entries = await connector.list(client, volume);\n } catch (err) {\n logger.warn(\n \"Failed to list catalog skills volume '%s': %s\",\n volume,\n err instanceof Error ? err.message : String(err),\n );\n return [];\n }\n\n // Read every skill concurrently — each is a network round-trip to the\n // volume, so serial reads would add up. A per-skill failure skips only\n // that skill; Promise.all preserves the (sorted) entry order.\n const loaded = await Promise.all(\n entries.map(async (entry): Promise<SkillDefinition | null> => {\n if (!entry.is_directory || !entry.name || !entry.path) return null;\n const skillDir = entry.path;\n const skillFile = `${skillDir}/SKILL.md`;\n try {\n const raw = await connector.read(client, skillFile);\n const parsed = parseSkill(raw, skillFile);\n const files = await this.listVolumeSkillFiles(\n connector,\n client,\n skillDir,\n );\n return {\n name: parsed.name,\n description: parsed.description,\n body: parsed.body,\n source: \"volume\",\n dir: skillDir,\n files,\n allowedTools: parsed.allowedTools,\n };\n } catch (err) {\n logger.warn(\n \"Skipping catalog skill '%s': %s\",\n skillDir,\n err instanceof Error ? err.message : String(err),\n );\n return null;\n }\n }),\n );\n return loaded.filter((s): s is SkillDefinition => s !== null);\n }\n\n /** Lists a volume skill's resource files (one level, excluding SKILL.md). */\n private async listVolumeSkillFiles(\n connector: FilesConnector,\n client: WorkspaceClient,\n skillDir: string,\n ): Promise<string[]> {\n try {\n const entries = await connector.list(client, skillDir);\n return entries\n .filter((e) => !e.is_directory && e.name && e.name !== \"SKILL.md\")\n .map((e) => e.name as string)\n .sort();\n } catch {\n return [];\n }\n }\n\n /**\n * Resolves the per-agent skill catalog: loads this agent's private skills\n * (`<agentsDir>/<id>/skills/`, file-origin only), then applies visibility\n * (opt-in via `def.skills` or `autoInheritSkills`) and collision rules\n * against the shared global pool. Returns `undefined` when nothing is\n * visible so the prompt catalog and dispatch can cheaply skip skills.\n */\n private async resolveAgentSkills(\n name: string,\n def: AgentDefinition,\n src: AgentSource,\n ): Promise<RegisteredAgent[\"skills\"]> {\n const dir = this.resolvedAgentsDir();\n const perAgentSkills =\n src.origin === \"file\" && dir\n ? await loadSkillsFromDir(\n path.join(dir, name, \"skills\"),\n \"bundle-agent\",\n )\n : [];\n\n const inherit = normalizeAutoInherit(this.config.autoInheritSkills);\n const autoInherit = src.origin === \"file\" ? inherit.file : inherit.code;\n\n const catalog = resolveSkillCatalog({\n agentName: name,\n agentSkillNames: def.skills,\n perAgentSkills,\n globalSkills: this.globalSkills,\n autoInherit,\n });\n\n return catalog.byAddress.size > 0 ? catalog : undefined;\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: {\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: AgentDefinition[\"generationParams\"];\n } = {};\n if (def.maxSteps !== undefined) adapterOptions.maxSteps = def.maxSteps;\n if (def.maxTokens !== undefined) adapterOptions.maxTokens = def.maxTokens;\n if (def.generationParams !== undefined)\n adapterOptions.generationParams = def.generationParams;\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 skills?: ResolvedSkillCatalog,\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 (isSupervisorTool(tool)) {\n index.set(key, {\n source: \"hosted-supervisor\",\n spec: tool.spec,\n def: {\n // `def` is a placeholder so the index has a uniform shape; it\n // is intentionally not passed to the adapter's `tools` array\n // (the SA endpoint owns its own tool execution and would\n // reject our synthetic schema).\n name: key,\n description: supervisorToolDescription(tool.spec),\n parameters: { type: \"object\", properties: {} },\n },\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 // 3. Built-in skill tools, present only when this agent has a visible\n // catalog. Injected last so they reliably shadow any same-named tool.\n if (skills && skills.byAddress.size > 0) {\n index.set(\"load_skill\", {\n source: \"skill\",\n builtin: \"load_skill\",\n catalog: skills,\n def: LOAD_SKILL_TOOL_DEF,\n });\n index.set(\"read_skill_file\", {\n source: \"skill\",\n builtin: \"read_skill_file\",\n catalog: skills,\n def: READ_SKILL_FILE_TOOL_DEF,\n });\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 /**\n * Mount the non-streaming invoke endpoints outside the `/api/<plugin>`\n * namespace. `/invocations` and `/responses` are aliases — both run the\n * default agent to completion and return a single JSON response. Streaming\n * lives on `POST /chat` (mounted in `injectRoutes`).\n */\n private mountInvokeRoutes() {\n if (!this.context) return;\n // Return the promise so the forwardAsyncErrors wrapper applied by\n // PluginContext.addRoute can forward rejections to the error middleware.\n const handler = (req: express.Request, res: express.Response) =>\n this._handleInvoke(req, res);\n this.context.addRoute(\"post\", \"/invocations\", handler);\n this.context.addRoute(\"post\", \"/responses\", handler);\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 // Per-agent skill catalog (name + description) so the client can offer a\n // `/skill-name` autocomplete / picker. Only agents with a visible catalog\n // appear here.\n const skills: Record<string, { name: string; description: string }[]> = {};\n for (const [name, agent] of this.agents) {\n if (agent.skills) skills[name] = agent.skills.catalog;\n }\n return {\n agents: Array.from(this.agents.keys()),\n defaultAgent: this.defaultAgentName,\n skills,\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 {\n message,\n threadId,\n agent: agentName,\n mlflowRunId,\n skill,\n } = 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(\n req,\n res,\n registered,\n thread,\n userId,\n mlflowRunId,\n skill,\n );\n }\n\n /**\n * Returns the names of tools in `registered.toolIndex` whose annotations\n * would trip the approval gate. Used by the non-streaming invoke path\n * (`/invocations`, `/responses`) to fail-fast before the adapter runs:\n * those endpoints have no channel back to the user mid-call, so an agent\n * whose tool surface includes approval-gated tools cannot be served.\n *\n * Returns an empty list when the plugin is configured with\n * `approval.requireForDestructive: false` — operators who explicitly\n * disabled HITL keep the invoke surface unrestricted.\n */\n private collectApprovalRequiredToolNames(\n registered: RegisteredAgent,\n ): string[] {\n if (!this.resolvedApprovalPolicy.requireForDestructive) return [];\n const names: string[] = [];\n for (const entry of registered.toolIndex.values()) {\n if (requiresApproval(entry.def.annotations)) {\n names.push(entry.def.name);\n }\n }\n return names;\n }\n\n /**\n * Shared handler for `POST /invocations` and `POST /responses`. Runs the\n * default agent to completion and returns a single JSON response in the\n * OpenAI Responses non-streaming shape. The two endpoints are aliases —\n * streaming clients must use `POST /chat`.\n *\n * Rejects with HTTP 400 when the resolved agent has any approval-gated\n * tool in scope: HITL requires a live SSE channel, which this surface\n * does not provide. See {@link collectApprovalRequiredToolNames}.\n */\n private async _handleInvoke(req: express.Request, res: express.Response) {\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, mlflowRunId } = parsed.data;\n const registered = this.resolveAgent();\n if (!registered) {\n res.status(400).json({ error: \"No agent registered\" });\n return;\n }\n\n // Pre-flight HITL gate. The non-streaming invoke surface has no way to\n // surface an approval prompt back to the caller and no way to receive\n // a decision mid-run, so we reject up-front instead of having the\n // approval gate auto-deny mid-stream (which would leave the caller\n // with a confusing \"denied by user\" tool result in the final text).\n const approvalGated = this.collectApprovalRequiredToolNames(registered);\n if (approvalGated.length > 0) {\n res.status(400).json({\n error:\n `Agent '${registered.name}' exposes ${approvalGated.length} approval-gated tool(s) ` +\n `(${approvalGated.join(\", \")}); /invocations and /responses are non-streaming and ` +\n \"cannot run HITL. Use POST /chat for HITL-capable agents, or disable approval via \" +\n \"agents({ approval: { requireForDestructive: false } }).\",\n });\n return;\n }\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._runAgentNonStreaming(\n req,\n res,\n registered,\n thread,\n userId,\n mlflowRunId,\n );\n }\n\n private async _streamAgent(\n req: express.Request,\n res: express.Response,\n registered: RegisteredAgent,\n thread: Thread,\n userId: string,\n mlflowRunId?: string,\n forcedSkill?: 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 // `hosted-supervisor` entries are not callable from the Node process\n // (the SA endpoint executes them server-side). Their `def` is a\n // placeholder; including it in the adapter's `tools` array would\n // make the SA endpoint reject the request with a schema mismatch.\n // The hosted-tool specs are routed via `AgentInput.extensions` below.\n const tools = Array.from(registered.toolIndex.values())\n .filter((e) => e.source !== \"hosted-supervisor\")\n .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 // Root MLflow span for the turn; tool-call spans nest under it.\n await traceAgent(\n registered.name ?? \"agent\",\n {\n messages: thread.messages.map((m) => ({\n role: m.role,\n content: m.content,\n })),\n },\n async (span) => {\n // Link this turn's trace to an eval run when the eval runner\n // supplied one, so the trace shows under the MLflow evaluation run.\n if (mlflowRunId) linkTraceToRun(mlflowRunId);\n\n const pluginNames = this.context\n ? this.context\n .getPluginNames()\n .filter((n) => n !== this.name && n !== \"server\")\n : [];\n let 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 // Deterministic `/skill-name` invocation: eagerly inject the\n // requested skill's instructions into this turn rather than waiting\n // for the model to call load_skill. The tool stays available for\n // the model to auto-select others.\n if (forcedSkill) {\n const addendum = this.renderForcedSkill(registered, forcedSkill);\n if (addendum) fullPrompt = `${fullPrompt}\\n\\n${addendum}`;\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 extensions: buildAdapterExtensions(registered.toolIndex),\n },\n { executeTool, signal },\n );\n\n // The accumulation rule (deltas append, `message` replaces) is\n // shared with `runAgent` and `runSubAgent`; see\n // `consumeAdapterStream` for 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 span.setOutputs({ role: \"assistant\", content: 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 // Surface the MLflow trace id so eval runs can attach assessments\n // to this turn's trace. No-op when tracing is disabled.\n const mlflowTraceId = currentTraceId();\n if (mlflowTraceId) {\n for (const evt of translator.translate({\n type: \"metadata\",\n data: { mlflowTraceId },\n })) {\n outboundEvents.push(evt);\n }\n }\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 * Non-streaming counterpart to {@link _streamAgent} used by `/invocations`\n * and `/responses`. Drives the adapter to completion, persists the\n * assistant turn to the thread store, and returns a single JSON envelope\n * shaped like the OpenAI Responses non-streaming API.\n *\n * No `EventChannel`, no `AgentEventTranslator`, no SSE — the caller is\n * waiting on one HTTP response. The approval gate is force-disabled in\n * the per-run state as defense-in-depth: `_handleInvoke` already rejects\n * up-front if any tool in scope would require approval, but pinning\n * `requireForDestructive: false` here means a tool that somehow slips\n * past the precheck (e.g. annotations mutated at runtime) still won't\n * stall the request waiting for an approval prompt that no one can\n * answer.\n *\n * The `RunState` shape is otherwise unchanged so {@link dispatchToolCall}\n * — including sub-agent recursion via {@link runSubAgent} — keeps the\n * same tool-call budget, abort signal, and timeout enforcement as the\n * streaming path. A still-typed translator is constructed but only\n * consulted for `finalize()` so any in-flight `approval_pending` event\n * synthesis (which would have been a coding bug given the precheck) is\n * a dropped no-op instead of a runtime crash.\n */\n private async _runAgentNonStreaming(\n req: express.Request,\n res: express.Response,\n registered: RegisteredAgent,\n thread: Thread,\n userId: string,\n mlflowRunId?: 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 limits = this.resolvedLimits;\n\n // Assigned inside the span below (the only place the active trace id\n // resolves), read into the response envelope after.\n let mlflowTraceId: string | undefined;\n\n const runState: RunState = {\n req,\n userId,\n requestId,\n abortController,\n signal,\n // Force approval off for the non-streaming invoke surface. The\n // precheck in `_handleInvoke` already guarantees no approval-gated\n // tool is reachable; this is belt-and-braces.\n approvalPolicy: { requireForDestructive: false, timeoutMs: 0 },\n limits,\n translator: new AgentEventTranslator(),\n outboundEvents: new EventChannel<ResponseStreamEvent>(),\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 let fullContent = \"\";\n try {\n // Root MLflow span for the turn; tool-call spans nest under it. Mirrors\n // the streaming path so the invoke surface produces the same trace shape\n // instead of orphan root TOOL spans.\n await traceAgent(\n registered.name ?? \"agent\",\n {\n messages: thread.messages.map((m) => ({\n role: m.role,\n content: m.content,\n })),\n },\n async (span) => {\n // Link this turn's trace to an eval run when the eval runner\n // supplied one, so the trace shows under the MLflow evaluation run.\n if (mlflowRunId) linkTraceToRun(mlflowRunId);\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 fullContent = await consumeAdapterStream(stream, { signal });\n\n if (fullContent) {\n span.setOutputs({ role: \"assistant\", content: 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 mlflowTraceId = currentTraceId();\n },\n );\n } catch (error) {\n if (signal.aborted) {\n res.status(499).json({ error: \"Request aborted\" });\n return;\n }\n logger.error(\"Agent invoke error: %O\", error);\n const message =\n process.env.NODE_ENV === \"production\"\n ? \"Internal server error\"\n : error instanceof Error\n ? error.message\n : String(error);\n res.status(500).json({ error: message });\n return;\n } finally {\n this.approvalGate.abortStream(requestId);\n this.untrackStream(requestId);\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\n const responseId = `resp_${randomUUID()}`;\n const messageId = `msg_${randomUUID()}`;\n const message: ResponseOutputMessage = {\n type: \"message\",\n id: messageId,\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: fullContent }],\n };\n res.json({\n id: responseId,\n object: \"response\",\n created_at: Math.floor(Date.now() / 1000),\n status: \"completed\",\n thread_id: thread.id,\n // Lets an eval runner attach assessments to this turn's trace; absent\n // when tracing is disabled.\n ...(mlflowTraceId ? { mlflow_trace_id: mlflowTraceId } : {}),\n output: [message],\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 // Traced from here so the span covers execution only, not the approval\n // wait above (which is human latency).\n const toolResult = await traceTool(name, args, async () => {\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(\n entry.mcpToolName,\n args,\n mcpAuth,\n );\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 } else if (entry.source === \"hosted-supervisor\") {\n // Defense-in-depth: should never fire. Hosted-supervisor entries are\n // routed via `AgentInput.extensions` and the SA endpoint executes\n // them server-side; their `def` is filtered out of the adapter's\n // `tools` array, so the model never sees a callable schema for them.\n // If we reach here, the agent is paired with a non-SA adapter that\n // somehow surfaced the placeholder def to the model — surface a\n // clear error rather than crash later in `normalizeToolResult`.\n throw new Error(\n `Tool '${name}' is a hosted-supervisor tool and cannot be invoked from the Node process. ` +\n \"It is executed server-side by the Databricks AI Gateway and is only reachable when the agent's model is a Supervisor API adapter.\",\n );\n } else if (entry.source === \"skill\") {\n result = await this.dispatchSkillTool(entry, args);\n }\n\n return result;\n });\n\n return normalizeToolResult(toolResult);\n }\n\n /**\n * Executes the built-in `load_skill` / `read_skill_file` tools against the\n * agent's resolved skill catalog. `load_skill` returns a skill's body plus a\n * manifest of bundled files; `read_skill_file` returns the contents of one\n * of those files (bundle skills only in v1 — catalog-volume resource reads\n * arrive with the volume source).\n */\n private async dispatchSkillTool(\n entry: Extract<ResolvedToolEntry, { source: \"skill\" }>,\n args: unknown,\n ): Promise<string> {\n const obj =\n typeof args === \"object\" && args !== null\n ? (args as Record<string, unknown>)\n : {};\n const skillName = typeof obj.skill === \"string\" ? obj.skill.trim() : \"\";\n if (!skillName) {\n throw new Error(\n `'${entry.builtin}' requires a 'skill' argument naming the skill to use.`,\n );\n }\n\n const skill = resolveSkill(entry.catalog, skillName);\n\n if (entry.builtin === \"load_skill\") {\n return renderLoadedSkill(skill);\n }\n\n // read_skill_file\n const filePath = typeof obj.path === \"string\" ? obj.path.trim() : \"\";\n if (!filePath) {\n throw new Error(\"'read_skill_file' requires a 'path' argument.\");\n }\n if (!skill.files.includes(filePath)) {\n throw new Error(\n `Skill '${skill.name}' has no bundled file '${filePath}'. Available: ${\n skill.files.join(\", \") || \"<none>\"\n }.`,\n );\n }\n\n if (skill.source === \"volume\") {\n const connector = new FilesConnector({ defaultVolume: skill.dir });\n return connector.read(\n this.skillWorkspaceClient(),\n `${skill.dir}/${filePath}`,\n );\n }\n return readSkillResource(skill.dir, filePath);\n }\n\n /**\n * Renders the prompt addendum for a force-loaded skill (`/skill-name`).\n * Returns null when the agent has no catalog or the name doesn't resolve —\n * an unusable request shouldn't fail the whole turn, so it's logged and the\n * model proceeds with the catalog + `load_skill` still available.\n */\n private renderForcedSkill(\n registered: RegisteredAgent,\n name: string,\n ): string | null {\n if (!registered.skills) return null;\n try {\n const skill = resolveSkill(registered.skills, name);\n return `The user explicitly requested the \"${skill.name}\" skill for this turn. Its instructions:\\n\\n${renderLoadedSkill(skill)}`;\n } catch (err) {\n logger.warn(\n \"Ignoring forced skill '%s': %s\",\n name,\n err instanceof Error ? err.message : String(err),\n );\n return null;\n }\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 // Same filter as the top-level path: hosted-supervisor `def` is a\n // placeholder, not a callable function — exclude from the adapter's\n // `tools` array. The specs are routed via `extensions` instead.\n const childTools = Array.from(child.toolIndex.values())\n .filter((e) => e.source !== \"hosted-supervisor\")\n .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 extensions: buildAdapterExtensions(child.toolIndex),\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\n/** Built-in tool the model calls to load a skill's full instructions on demand. */\nconst LOAD_SKILL_TOOL_DEF: AgentToolDefinition = {\n name: \"load_skill\",\n description:\n \"Load the full instructions for one of the available skills by name. Call this before acting on a task that matches a skill's description. Returns the skill's instructions plus a list of any bundled files you can read with read_skill_file.\",\n parameters: {\n type: \"object\",\n properties: {\n skill: {\n type: \"string\",\n description:\n \"The exact skill name to load, as shown in the available-skills list.\",\n },\n },\n required: [\"skill\"],\n },\n annotations: { effect: \"read\" },\n};\n\n/** Built-in tool for reading a resource file that a loaded skill references. */\nconst READ_SKILL_FILE_TOOL_DEF: AgentToolDefinition = {\n name: \"read_skill_file\",\n description:\n \"Read a bundled resource file that a loaded skill references (e.g. a reference doc). Only files listed by load_skill for that skill are readable.\",\n parameters: {\n type: \"object\",\n properties: {\n skill: { type: \"string\", description: \"The skill that owns the file.\" },\n path: {\n type: \"string\",\n description:\n \"Relative path of the file within the skill, as listed by load_skill.\",\n },\n },\n required: [\"skill\", \"path\"],\n },\n annotations: { effect: \"read\" },\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 const composed = composeSystemPrompt(base, registered.instructions);\n\n // Append the always-on skill catalog (name + description only). Done here,\n // after composeSystemPrompt, so it survives a custom/`false` base prompt.\n const catalog = registered.skills?.catalog;\n if (!catalog || catalog.length === 0) return composed;\n return `${composed}\\n\\n${renderSkillCatalog(catalog)}`;\n}\n\n/**\n * Pulls the LLM-readable description off any {@link SupervisorTool} kind.\n * Used to populate the synthetic placeholder `def.description` on\n * hosted-supervisor tool-index entries.\n */\nfunction supervisorToolDescription(spec: SupervisorTool): string {\n switch (spec.type) {\n case \"genie_space\":\n return spec.genie_space.description;\n case \"uc_function\":\n return spec.uc_function.description;\n case \"knowledge_assistant\":\n return spec.knowledge_assistant.description;\n case \"app\":\n return spec.app.description;\n case \"uc_connection\":\n return spec.uc_connection.description;\n }\n}\n\n/**\n * Builds the `AgentInput.extensions` payload from a tool index, aggregating\n * the hosted-supervisor specs under {@link SUPERVISOR_EXTENSION_KEY}. Returns\n * `undefined` when there are no adapter-side hosted tools so the field stays\n * absent on the wire — adapters that don't read extensions never see it.\n */\nfunction buildAdapterExtensions(\n toolIndex: Map<string, ResolvedToolEntry>,\n): Readonly<Record<string, unknown>> | undefined {\n const supervisorSpecs: SupervisorTool[] = [];\n for (const entry of toolIndex.values()) {\n if (entry.source === \"hosted-supervisor\") {\n supervisorSpecs.push(entry.spec);\n }\n }\n if (supervisorSpecs.length === 0) return undefined;\n return {\n [SUPERVISOR_EXTENSION_KEY]: { hostedTools: supervisorSpecs },\n };\n}\n\n/**\n * Compares the adapter's declared capabilities against the tool index and\n * logs a warning when the agent's tool declarations would be silently\n * dropped at runtime. Warn-not-throw: misconfiguration is loud enough to\n * notice without taking the whole app down.\n */\nfunction warnOnCapabilityMismatch(\n agentName: string,\n adapter: AgentAdapter,\n toolIndex: Map<string, ResolvedToolEntry>,\n): void {\n const accepted = new Set(adapter.acceptsExtensions ?? []);\n\n const hostedSupervisorKeys: string[] = [];\n const inputToolKeys: string[] = [];\n for (const [key, entry] of toolIndex) {\n if (entry.source === \"hosted-supervisor\") {\n hostedSupervisorKeys.push(key);\n } else {\n inputToolKeys.push(key);\n }\n }\n\n if (\n hostedSupervisorKeys.length > 0 &&\n !accepted.has(SUPERVISOR_EXTENSION_KEY)\n ) {\n logger.warn(\n `Agent '${agentName}' declares hosted-supervisor tools (${hostedSupervisorKeys.join(\", \")}) ` +\n \"but its model adapter does not accept the 'databricks.supervisor' extension. \" +\n \"These tools will not reach the model. Pair them with `DatabricksAdapter.fromSupervisorApi(...)`, or remove them.\",\n );\n }\n\n // `consumesInputTools` defaults to true. Only warn when an adapter\n // explicitly opts out (`false`) and an input tool would be silently\n // ignored.\n if (adapter.consumesInputTools === false && inputToolKeys.length > 0) {\n logger.warn(\n `Agent '${agentName}' declares function tools / sub-agents / MCP tools (${inputToolKeys.join(\", \")}) ` +\n \"but its model adapter does not consume input.tools (Supervisor API owns its own tool loop). \" +\n \"These tools will not be exposed to the model. See docs/plugins/agents.md.\",\n );\n }\n}\n\n/**\n * Plugin factory for the agents plugin. Discovers agents from\n * `server/agents/<id>/agent.{ts,md}` by default (markdown still in\n * `config/agents/` is read as a deprecated fallback), resolves toolkits/tools\n * from registered plugins, exposes the `appkit.agents.*` runtime API and mounts\n * `POST /invocations` and `POST /responses` (aliased non-streaming invoke\n * endpoints) plus `POST /chat` (streaming, HITL-capable).\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 { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport type express from \"express\";\nimport pc from \"picocolors\";\nimport type {\n AgentAdapter,\n AgentToolDefinition,\n IAppRouter,\n Message,\n PluginPhase,\n ResponseOutputMessage,\n ResponseStreamEvent,\n Thread,\n ToolProvider,\n} from \"shared\";\n\nimport { isSupervisorTool } from \"../../agents/supervisor-api\";\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 { CODE_AGENTS_SOURCE_DIR } from \"../../core/agent/load-code-agents\";\nimport { createPluginsProxy } from \"../../core/agent/plugins-map\";\nimport type {\n ResolvedSkillCatalog,\n SkillDefinition,\n} from \"../../core/agent/skills\";\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 Plugins,\n PluginToolkitProvider,\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 { defineManifest } from \"../../registry\";\nimport type { WorkspaceClient } from \"../../workspace-client\";\nimport { ActiveStreamTracker } from \"./active-stream-tracker\";\nimport {\n buildAdapterExtensions,\n supervisorToolDescription,\n warnOnCapabilityMismatch,\n} from \"./adapter-extensions\";\nimport { requiresApproval } from \"./approval\";\nimport { LOAD_SKILL_TOOL_DEF, READ_SKILL_FILE_TOOL_DEF } from \"./builtin-tools\";\nimport { agentStreamDefaults } from \"./defaults\";\nimport { EventChannel } from \"./event-channel\";\nimport { AgentEventTranslator } from \"./event-translator\";\nimport manifest from \"./manifest.json\";\nimport {\n currentTraceId,\n initAgentTracing,\n linkTraceToRun,\n traceAgent,\n} from \"./mlflow\";\nimport { composePromptForAgent } from \"./prompt\";\nimport {\n type AgentSource,\n loadCodeAgents,\n resolveDefaultAgent,\n} from \"./registry\";\nimport { resolveApprovalPolicy, resolveLimits } from \"./resolve-config\";\nimport {\n approvalRequestSchema,\n cancelRequestSchema,\n chatRequestSchema,\n invocationsRequestSchema,\n} from \"./schemas\";\nimport {\n dispatchSkillTool,\n loadGlobalSkills,\n loadVolumeSkills,\n renderForcedSkill,\n resolveAgentSkills,\n} from \"./skill-loader\";\nimport { InMemoryThreadStore } from \"./thread-store\";\nimport { ToolApprovalGate } from \"./tool-approval-gate\";\nimport {\n dispatchToolCall,\n type RunState,\n type ToolDispatchDeps,\n} from \"./tool-dispatch\";\n\nconst logger = createLogger(\"agents\");\n\n/** Deprecated markdown location, read as a fallback with a one-time warning. */\nconst LEGACY_MARKDOWN_DIR = \"config/agents\";\n\nexport class AgentsPlugin extends Plugin implements ToolProvider {\n static manifest = defineManifest<\"agents\">(manifest);\n static phase: PluginPhase = \"deferred\";\n\n declare protected config: AgentsPluginConfig;\n\n private agents = new Map<string, RegisteredAgent>();\n private defaultAgentName: string | null = null;\n /**\n * Active SSE streams + per-user counts (the O(1) concurrency-limit check).\n * Mutated only via {@link trackStream} / {@link untrackStream}.\n */\n private streams = new ActiveStreamTracker();\n private mcpClient: AppKitMcpClient | null = null;\n private threadStore;\n private approvalGate = new ToolApprovalGate();\n /** Guards the `agents({ agents })` deprecation warning to once per instance. */\n private agentsMapDeprecationWarned = false;\n /** Guards the `config/agents` deprecation warning to once per instance. */\n private configAgentsDeprecationWarned = false;\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 /**\n * Shared global skill pool (bundle `skills/` + catalog volume), loaded once\n * per registry build. Read by {@link buildRegisteredAgent} to resolve each\n * agent's visible catalog and by the live `register` path.\n */\n private globalSkills: SkillDefinition[] = [];\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 this.cachedApprovalPolicy = resolveApprovalPolicy(this.config);\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 return resolveLimits(this.config);\n }\n\n /** Count active streams owned by a given user. O(1). */\n private countUserStreams(userId: string): number {\n return this.streams.count(userId);\n }\n\n private trackStream(\n requestId: string,\n userId: string,\n controller: AbortController,\n ): void {\n this.streams.track(requestId, userId, controller);\n }\n\n private untrackStream(requestId: string): void {\n this.streams.untrack(requestId);\n }\n\n async setup() {\n await initAgentTracing();\n const { agents, defaultAgentName } = await this.buildAgentRegistry();\n this.agents = agents;\n this.defaultAgentName = defaultAgentName;\n this.mountInvokeRoutes();\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 // Two \"code\" sources: discovered files and the deprecated `agents({ agents })` map.\n const discovered = await this.loadCodeAgents();\n const deprecatedMapRaw = this.config.agents ?? {};\n\n // Global skills (bundle + configured UC volume) load in parallel; per-agent\n // skills resolve later during agent build.\n const [bundleSkills, volumeSkills] = await Promise.all([\n this.loadGlobalSkills(),\n this.loadVolumeSkills(),\n ]);\n this.globalSkills = [...bundleSkills, ...volumeSkills];\n\n if (Object.keys(deprecatedMapRaw).length > 0) {\n this.warnAgentsMapDeprecated();\n }\n\n // On a discovered/map id clash, discovery wins (drop the map entry) rather\n // than crash boot on upgrade.\n const deprecatedMap: Record<string, AgentDefinition> = {};\n for (const [id, def] of Object.entries(deprecatedMapRaw)) {\n if (discovered[id]) {\n logger.warn(\n \"Agent '%s' is both discovered in %s and passed to agents({ agents }). \" +\n \"Using the discovered file; ignoring the map entry.\",\n id,\n CODE_AGENTS_SOURCE_DIR,\n );\n continue;\n }\n deprecatedMap[id] = def;\n }\n\n // Code agents also resolve markdown `agents: [child]` sub-agent references.\n const codeAgents: Record<string, AgentDefinition> = {\n ...discovered,\n ...deprecatedMap,\n };\n\n const { defs: fileDefs, defaultAgent: fileDefault } =\n await this.loadFileDefinitions(codeAgents);\n\n // Merge order (markdown, discovered, map) sets precedence and the\n // first-registered default fallback.\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(discovered)) {\n if (merged[name]?.src.origin === \"file\") {\n // Discovery is new API — clash with markdown is a hard error (the\n // deprecated map only warns). A folder with both agent.ts and\n // agent.md lands here too: one kind per folder.\n throw new Error(\n `Agent '${name}' is defined as both a code agent (agent.ts) and a markdown agent (agent.md) — ` +\n `in one folder, or across server/agents and the deprecated config/agents fallback. ` +\n `Keep a single kind per id. Available: ${Object.keys(merged).sort().join(\", \")}`,\n );\n }\n merged[name] = { def, src: { origin: \"code\" } };\n }\n for (const [name, def] of Object.entries(deprecatedMap)) {\n if (merged[name]?.src.origin === \"file\") {\n logger.warn(\n \"Agent '%s' defined in both code and a markdown file. Code definition takes precedence.\",\n name,\n );\n }\n merged[name] = { def, src: { origin: \"code\" } };\n }\n\n const agents = new Map<string, RegisteredAgent>();\n\n if (Object.keys(merged).length === 0) {\n logger.info(\n \"No agents registered (no files in %s, no discovered or code-defined agents)\",\n this.resolvedAgentsDir(),\n );\n return { agents, defaultAgentName: null };\n }\n\n for (const [name, { def, src }] of Object.entries(merged)) {\n try {\n agents.set(name, await this.buildRegisteredAgent(name, def, src));\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 return {\n agents,\n defaultAgentName: this.resolveDefaultAgent(agents, merged, fileDefault),\n };\n }\n\n /**\n * Resolves the default agent. Precedence: explicit `config.defaultAgent` >\n * a code/discovered agent flagged `default: true` (stable id order) >\n * markdown `default: true` > first registered (insertion order).\n */\n private resolveDefaultAgent(\n agents: Map<string, RegisteredAgent>,\n merged: Record<string, { def: AgentDefinition; src: AgentSource }>,\n fileDefault: string | null,\n ): string | null {\n return resolveDefaultAgent(\n agents,\n merged,\n fileDefault,\n this.config.defaultAgent,\n );\n }\n\n /**\n * Emits the one-time deprecation warning for the `agents({ agents })` map.\n * Guarded so `reload()` (which re-runs `buildAgentRegistry`) doesn't spam it.\n */\n private warnAgentsMapDeprecated(): void {\n if (this.agentsMapDeprecationWarned) return;\n this.agentsMapDeprecationWarned = true;\n logger.warn(\n \"agents({ agents: { ... } }) is deprecated. Put each code agent in its own folder under \" +\n \"server/agents/<id>/agent.ts (export default createAgent({ ... })) and it is discovered \" +\n \"automatically — the call collapses to agents({ ... }) with no agent map. The `agents` field \" +\n \"still works but will be removed in a future minor. See docs/plugins/agents.md.\",\n );\n }\n\n /**\n * One-time deprecation warning for markdown agents still living in\n * `config/agents/`. Guarded so `reload()` doesn't spam it.\n */\n private warnConfigAgentsDeprecated(): void {\n if (this.configAgentsDeprecationWarned) return;\n this.configAgentsDeprecationWarned = true;\n logger.warn(\n \"Markdown agents under config/agents/ are deprecated. Move each config/agents/<id>/agent.md to \" +\n \"server/agents/<id>/agent.md — every agent now lives in one place (server/agents). config/agents \" +\n \"is still read for now but will be removed in a future minor. See docs/plugins/agents.md.\",\n );\n }\n\n private resolvedAgentsDir(): string {\n return path.resolve(process.cwd(), CODE_AGENTS_SOURCE_DIR);\n }\n\n private loadCodeAgents(): Promise<Record<string, AgentDefinition>> {\n return loadCodeAgents({\n agentsDir: this.resolvedAgentsDir(),\n hasDeprecatedMap: Object.keys(this.config.agents ?? {}).length > 0,\n });\n }\n\n private async loadFileDefinitions(\n codeAgents: Record<string, AgentDefinition>,\n ): Promise<{\n defs: Record<string, AgentDefinition>;\n defaultAgent: string | null;\n }> {\n const primaryDir = this.resolvedAgentsDir();\n\n // Discovered code agents + the deprecated map resolve markdown `agents:`\n // sub-agent references, so a markdown parent can delegate to a code child.\n const baseCtx = {\n defaultModel: this.config.defaultModel,\n availableTools: this.config.tools ?? {},\n plugins: this.pluginProviderIndex(),\n };\n\n const legacyDir = path.resolve(process.cwd(), LEGACY_MARKDOWN_DIR);\n\n // Common case: no deprecated config/agents/ dir → a single scan of server/agents.\n if (!existsSync(legacyDir)) {\n return loadAgentsFromDir(primaryDir, { ...baseCtx, codeAgents });\n }\n\n // Deprecated fallback: config/agents/ exists → scan it first, then server/agents\n // with the legacy defs added as resolvable sub-agent targets, so a parent already\n // moved to server/agents can still reference a child left in config/agents\n // mid-migration. Code agents keep precedence; server/agents wins on an id clash.\n const legacy = await loadAgentsFromDir(legacyDir, {\n ...baseCtx,\n codeAgents,\n });\n const primary = await loadAgentsFromDir(primaryDir, {\n ...baseCtx,\n codeAgents: { ...legacy.defs, ...codeAgents },\n });\n\n if (Object.keys(legacy.defs).length === 0) return primary;\n\n this.warnConfigAgentsDeprecated();\n return {\n defs: { ...legacy.defs, ...primary.defs },\n defaultAgent: primary.defaultAgent ?? legacy.defaultAgent,\n };\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 skills = await this.resolveAgentSkills(name, def, src);\n const toolIndex = await this.buildToolIndex(name, def, src, skills);\n\n warnOnCapabilityMismatch(name, adapter, toolIndex);\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 generationParams: def.generationParams,\n ephemeral: def.ephemeral,\n skills,\n };\n }\n\n /** Loads the shared global skill pool from `<agentsDir>/skills/`. */\n private loadGlobalSkills(): Promise<SkillDefinition[]> {\n return loadGlobalSkills(this.resolvedAgentsDir());\n }\n\n /**\n * Workspace client used to read catalog skills. v1 reads as the app service\n * principal; `getWorkspaceClient()` resolves to SP outside a user scope\n * (boot and skill-tool dispatch are both unscoped). This is the single\n * switch point for a future OBO mode.\n */\n private skillWorkspaceClient(): WorkspaceClient {\n return getWorkspaceClient();\n }\n\n /** Discovers catalog skills from the configured UC Volume (best-effort). */\n private loadVolumeSkills(): Promise<SkillDefinition[]> {\n return loadVolumeSkills(this.config, () => this.skillWorkspaceClient());\n }\n\n private async resolveAgentSkills(\n name: string,\n def: AgentDefinition,\n src: AgentSource,\n ): Promise<RegisteredAgent[\"skills\"]> {\n const inherit = normalizeAutoInherit(this.config.autoInheritSkills);\n return resolveAgentSkills({\n name,\n def,\n agentsDir: this.resolvedAgentsDir(),\n isFileOrigin: src.origin === \"file\",\n autoInherit: src.origin === \"file\" ? inherit.file : inherit.code,\n globalSkills: this.globalSkills,\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: {\n maxSteps?: number;\n maxTokens?: number;\n generationParams?: AgentDefinition[\"generationParams\"];\n } = {};\n if (def.maxSteps !== undefined) adapterOptions.maxSteps = def.maxSteps;\n if (def.maxTokens !== undefined) adapterOptions.maxTokens = def.maxTokens;\n if (def.generationParams !== undefined)\n adapterOptions.generationParams = def.generationParams;\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 skills?: ResolvedSkillCatalog,\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 (isSupervisorTool(tool)) {\n index.set(key, {\n source: \"hosted-supervisor\",\n spec: tool.spec,\n def: {\n // `def` is a placeholder so the index has a uniform shape; it\n // is intentionally not passed to the adapter's `tools` array\n // (the SA endpoint owns its own tool execution and would\n // reject our synthetic schema).\n name: key,\n description: supervisorToolDescription(tool.spec),\n parameters: { type: \"object\", properties: {} },\n },\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 // 3. Built-in skill tools, present only when this agent has a visible\n // catalog. Injected last so they reliably shadow any same-named tool.\n if (skills && skills.byAddress.size > 0) {\n index.set(\"load_skill\", {\n source: \"skill\",\n builtin: \"load_skill\",\n catalog: skills,\n def: LOAD_SKILL_TOOL_DEF,\n });\n index.set(\"read_skill_file\", {\n source: \"skill\",\n builtin: \"read_skill_file\",\n catalog: skills,\n def: READ_SKILL_FILE_TOOL_DEF,\n });\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 /**\n * Mount the non-streaming invoke endpoints outside the `/api/<plugin>`\n * namespace. `/invocations` and `/responses` are aliases — both run the\n * default agent to completion and return a single JSON response. Streaming\n * lives on `POST /chat` (mounted in `injectRoutes`).\n */\n private mountInvokeRoutes() {\n if (!this.context) return;\n // Return the promise so the forwardAsyncErrors wrapper applied by\n // PluginContext.addRoute can forward rejections to the error middleware.\n const handler = (req: express.Request, res: express.Response) =>\n this._handleInvoke(req, res);\n this.context.addRoute(\"post\", \"/invocations\", handler);\n this.context.addRoute(\"post\", \"/responses\", handler);\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 // Per-agent skill catalog (name + description) so the client can offer a\n // `/skill-name` autocomplete / picker. Only agents with a visible catalog\n // appear here.\n const skills: Record<string, { name: string; description: string }[]> = {};\n for (const [name, agent] of this.agents) {\n if (agent.skills) skills[name] = agent.skills.catalog;\n }\n return {\n agents: Array.from(this.agents.keys()),\n defaultAgent: this.defaultAgentName,\n skills,\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 {\n message,\n threadId,\n agent: agentName,\n mlflowRunId,\n skill,\n } = 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(\n req,\n res,\n registered,\n thread,\n userId,\n mlflowRunId,\n skill,\n );\n }\n\n /**\n * Returns the names of tools in `registered.toolIndex` whose annotations\n * would trip the approval gate. Used by the non-streaming invoke path\n * (`/invocations`, `/responses`) to fail-fast before the adapter runs:\n * those endpoints have no channel back to the user mid-call, so an agent\n * whose tool surface includes approval-gated tools cannot be served.\n *\n * Returns an empty list when the plugin is configured with\n * `approval.requireForDestructive: false` — operators who explicitly\n * disabled HITL keep the invoke surface unrestricted.\n */\n private collectApprovalRequiredToolNames(\n registered: RegisteredAgent,\n ): string[] {\n if (!this.resolvedApprovalPolicy.requireForDestructive) return [];\n const names: string[] = [];\n for (const entry of registered.toolIndex.values()) {\n if (requiresApproval(entry.def.annotations)) {\n names.push(entry.def.name);\n }\n }\n return names;\n }\n\n /**\n * Shared handler for `POST /invocations` and `POST /responses`. Runs the\n * default agent to completion and returns a single JSON response in the\n * OpenAI Responses non-streaming shape. The two endpoints are aliases —\n * streaming clients must use `POST /chat`.\n *\n * Rejects with HTTP 400 when the resolved agent has any approval-gated\n * tool in scope: HITL requires a live SSE channel, which this surface\n * does not provide. See {@link collectApprovalRequiredToolNames}.\n */\n private async _handleInvoke(req: express.Request, res: express.Response) {\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, mlflowRunId } = parsed.data;\n const registered = this.resolveAgent();\n if (!registered) {\n res.status(400).json({ error: \"No agent registered\" });\n return;\n }\n\n // Pre-flight HITL gate. The non-streaming invoke surface has no way to\n // surface an approval prompt back to the caller and no way to receive\n // a decision mid-run, so we reject up-front instead of having the\n // approval gate auto-deny mid-stream (which would leave the caller\n // with a confusing \"denied by user\" tool result in the final text).\n const approvalGated = this.collectApprovalRequiredToolNames(registered);\n if (approvalGated.length > 0) {\n res.status(400).json({\n error:\n `Agent '${registered.name}' exposes ${approvalGated.length} approval-gated tool(s) ` +\n `(${approvalGated.join(\", \")}); /invocations and /responses are non-streaming and ` +\n \"cannot run HITL. Use POST /chat for HITL-capable agents, or disable approval via \" +\n \"agents({ approval: { requireForDestructive: false } }).\",\n });\n return;\n }\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._runAgentNonStreaming(\n req,\n res,\n registered,\n thread,\n userId,\n mlflowRunId,\n );\n }\n\n private async _streamAgent(\n req: express.Request,\n res: express.Response,\n registered: RegisteredAgent,\n thread: Thread,\n userId: string,\n mlflowRunId?: string,\n forcedSkill?: 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 // `hosted-supervisor` entries are not callable from the Node process\n // (the SA endpoint executes them server-side). Their `def` is a\n // placeholder; including it in the adapter's `tools` array would\n // make the SA endpoint reject the request with a schema mismatch.\n // The hosted-tool specs are routed via `AgentInput.extensions` below.\n const tools = Array.from(registered.toolIndex.values())\n .filter((e) => e.source !== \"hosted-supervisor\")\n .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 deps = this.toolDispatchDeps();\n const executeTool = (name: string, args: unknown): Promise<unknown> =>\n dispatchToolCall(deps, 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 // Root MLflow span for the turn; tool-call spans nest under it.\n await traceAgent(\n registered.name ?? \"agent\",\n {\n messages: thread.messages.map((m) => ({\n role: m.role,\n content: m.content,\n })),\n },\n async (span) => {\n // Link this turn's trace to an eval run when the eval runner\n // supplied one, so the trace shows under the MLflow evaluation run.\n if (mlflowRunId) linkTraceToRun(mlflowRunId);\n\n const pluginNames = this.context\n ? this.context\n .getPluginNames()\n .filter((n) => n !== this.name && n !== \"server\")\n : [];\n let 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 // Deterministic `/skill-name` invocation: eagerly inject the\n // requested skill's instructions into this turn rather than waiting\n // for the model to call load_skill. The tool stays available for\n // the model to auto-select others.\n if (forcedSkill) {\n const addendum = this.renderForcedSkill(registered, forcedSkill);\n if (addendum) fullPrompt = `${fullPrompt}\\n\\n${addendum}`;\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 extensions: buildAdapterExtensions(registered.toolIndex),\n },\n { executeTool, signal },\n );\n\n // The accumulation rule (deltas append, `message` replaces) is\n // shared with `runAgent` and `runSubAgent`; see\n // `consumeAdapterStream` for 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 span.setOutputs({ role: \"assistant\", content: 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 // Surface the MLflow trace id so eval runs can attach assessments\n // to this turn's trace. No-op when tracing is disabled.\n const mlflowTraceId = currentTraceId();\n if (mlflowTraceId) {\n for (const evt of translator.translate({\n type: \"metadata\",\n data: { mlflowTraceId },\n })) {\n outboundEvents.push(evt);\n }\n }\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 * Non-streaming counterpart to {@link _streamAgent} used by `/invocations`\n * and `/responses`. Drives the adapter to completion, persists the\n * assistant turn to the thread store, and returns a single JSON envelope\n * shaped like the OpenAI Responses non-streaming API.\n *\n * No `EventChannel`, no `AgentEventTranslator`, no SSE — the caller is\n * waiting on one HTTP response. The approval gate is force-disabled in\n * the per-run state as defense-in-depth: `_handleInvoke` already rejects\n * up-front if any tool in scope would require approval, but pinning\n * `requireForDestructive: false` here means a tool that somehow slips\n * past the precheck (e.g. annotations mutated at runtime) still won't\n * stall the request waiting for an approval prompt that no one can\n * answer.\n *\n * The `RunState` shape is otherwise unchanged so {@link dispatchToolCall}\n * — including sub-agent recursion via {@link runSubAgent} — keeps the\n * same tool-call budget, abort signal, and timeout enforcement as the\n * streaming path. A still-typed translator is constructed but only\n * consulted for `finalize()` so any in-flight `approval_pending` event\n * synthesis (which would have been a coding bug given the precheck) is\n * a dropped no-op instead of a runtime crash.\n */\n private async _runAgentNonStreaming(\n req: express.Request,\n res: express.Response,\n registered: RegisteredAgent,\n thread: Thread,\n userId: string,\n mlflowRunId?: 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 limits = this.resolvedLimits;\n\n // Assigned inside the span below (the only place the active trace id\n // resolves), read into the response envelope after.\n let mlflowTraceId: string | undefined;\n\n const runState: RunState = {\n req,\n userId,\n requestId,\n abortController,\n signal,\n // Force approval off for the non-streaming invoke surface. The\n // precheck in `_handleInvoke` already guarantees no approval-gated\n // tool is reachable; this is belt-and-braces.\n approvalPolicy: { requireForDestructive: false, timeoutMs: 0 },\n limits,\n translator: new AgentEventTranslator(),\n outboundEvents: new EventChannel<ResponseStreamEvent>(),\n toolCallsUsed: { count: 0 },\n };\n\n const deps = this.toolDispatchDeps();\n const executeTool = (name: string, args: unknown): Promise<unknown> =>\n dispatchToolCall(deps, runState, registered.toolIndex, name, args, 0);\n\n let fullContent = \"\";\n try {\n // Root MLflow span for the turn; tool-call spans nest under it. Mirrors\n // the streaming path so the invoke surface produces the same trace shape\n // instead of orphan root TOOL spans.\n await traceAgent(\n registered.name ?? \"agent\",\n {\n messages: thread.messages.map((m) => ({\n role: m.role,\n content: m.content,\n })),\n },\n async (span) => {\n // Link this turn's trace to an eval run when the eval runner\n // supplied one, so the trace shows under the MLflow evaluation run.\n if (mlflowRunId) linkTraceToRun(mlflowRunId);\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 fullContent = await consumeAdapterStream(stream, { signal });\n\n if (fullContent) {\n span.setOutputs({ role: \"assistant\", content: 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 mlflowTraceId = currentTraceId();\n },\n );\n } catch (error) {\n if (signal.aborted) {\n res.status(499).json({ error: \"Request aborted\" });\n return;\n }\n logger.error(\"Agent invoke error: %O\", error);\n const message =\n process.env.NODE_ENV === \"production\"\n ? \"Internal server error\"\n : error instanceof Error\n ? error.message\n : String(error);\n res.status(500).json({ error: message });\n return;\n } finally {\n this.approvalGate.abortStream(requestId);\n this.untrackStream(requestId);\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\n const responseId = `resp_${randomUUID()}`;\n const messageId = `msg_${randomUUID()}`;\n const message: ResponseOutputMessage = {\n type: \"message\",\n id: messageId,\n status: \"completed\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: fullContent }],\n };\n res.json({\n id: responseId,\n object: \"response\",\n created_at: Math.floor(Date.now() / 1000),\n status: \"completed\",\n thread_id: thread.id,\n // Lets an eval runner attach assessments to this turn's trace; absent\n // when tracing is disabled.\n ...(mlflowTraceId ? { mlflow_trace_id: mlflowTraceId } : {}),\n output: [message],\n });\n }\n\n private dispatchSkillTool(\n entry: Extract<ResolvedToolEntry, { source: \"skill\" }>,\n args: unknown,\n ): Promise<string> {\n return dispatchSkillTool(entry, args, () => this.skillWorkspaceClient());\n }\n\n /**\n * Renders the prompt addendum for a force-loaded skill (`/skill-name`).\n * Returns null when the agent has no catalog or the name doesn't resolve —\n * an unusable request shouldn't fail the whole turn, so it's logged and the\n * model proceeds with the catalog + `load_skill` still available.\n */\n private renderForcedSkill(\n registered: RegisteredAgent,\n name: string,\n ): string | null {\n return renderForcedSkill(registered, name);\n }\n\n /** Collaborators the tool-dispatch path needs, bound to this instance. */\n private toolDispatchDeps(): ToolDispatchDeps {\n return {\n approvalGate: this.approvalGate,\n context: this.context,\n getMcpClient: () => this.mcpClient,\n agents: this.agents,\n dispatchSkillTool: (entry, args) => this.dispatchSkillTool(entry, args),\n pluginName: this.name,\n baseSystemPrompt: this.config.baseSystemPrompt,\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.streams.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.streams.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\n/**\n * Plugin factory for the agents plugin. Discovers agents from\n * `server/agents/<id>/agent.{ts,md}` by default (markdown still in\n * `config/agents/` is read as a deprecated fallback), resolves toolkits/tools\n * from registered plugins, exposes the `appkit.agents.*` runtime API and mounts\n * `POST /invocations` and `POST /responses` (aliased non-streaming invoke\n * endpoints) plus `POST /chat` (streaming, HITL-capable).\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgGA,MAAM,SAAS,aAAa,SAAS;;AAGrC,MAAM,sBAAsB;AAE5B,IAAa,eAAb,cAAkC,OAA+B;CAC/D,OAAO,WAAW,eAAyBA,iBAAS;CACpD,OAAO,QAAqB;CAI5B,AAAQ,yBAAS,IAAI,KAA8B;CACnD,AAAQ,mBAAkC;;;;;CAK1C,AAAQ,UAAU,IAAI,qBAAqB;CAC3C,AAAQ,YAAoC;CAC5C,AAAQ;CACR,AAAQ,eAAe,IAAI,kBAAkB;;CAE7C,AAAQ,6BAA6B;;CAErC,AAAQ,gCAAgC;CAExC,YAAY,QAA4B;AACtC,QAAM,OAAO;AACb,OAAK,SAAS;AACd,MAAI,OAAO,YACT,MAAK,cAAc,OAAO;OACrB;AACL,QAAK,cAAc,IAAI,qBAAqB;AAC5C,OAAI,QAAQ,IAAI,aAAa,aAC3B,QAAO,KACL,oMAGD;OAED,QAAO,KACL,qGACD;;;;;;;;;;;;;;;;;;CAoBP,AAAQ,eAAkC,EAAE;CAE5C,AAAQ,uBAGG;CAEX,IAAY,yBAGV;AACA,MAAI,KAAK,qBAAsB,QAAO,KAAK;AAC3C,OAAK,uBAAuB,sBAAsB,KAAK,OAAO;AAC9D,SAAO,KAAK;;;CAId,IAAY,iBAKV;AACA,SAAO,cAAc,KAAK,OAAO;;;CAInC,AAAQ,iBAAiB,QAAwB;AAC/C,SAAO,KAAK,QAAQ,MAAM,OAAO;;CAGnC,AAAQ,YACN,WACA,QACA,YACM;AACN,OAAK,QAAQ,MAAM,WAAW,QAAQ,WAAW;;CAGnD,AAAQ,cAAc,WAAyB;AAC7C,OAAK,QAAQ,QAAQ,UAAU;;CAGjC,MAAM,QAAQ;AACZ,QAAM,kBAAkB;EACxB,MAAM,EAAE,QAAQ,qBAAqB,MAAM,KAAK,oBAAoB;AACpE,OAAK,SAAS;AACd,OAAK,mBAAmB;AACxB,OAAK,mBAAmB;AACxB,OAAK,eAAe;;;;;;;;CAStB,MAAM,SAAwB;EAC5B,MAAM,OAAO,MAAM,KAAK,oBAAoB;AAgB5C,OAAK,SAAS,KAAK;AACnB,OAAK,mBAAmB,KAAK;;;;;;;CAQ/B,MAAc,qBAGX;EAED,MAAM,aAAa,MAAM,KAAK,gBAAgB;EAC9C,MAAM,mBAAmB,KAAK,OAAO,UAAU,EAAE;EAIjD,MAAM,CAAC,cAAc,gBAAgB,MAAM,QAAQ,IAAI,CACrD,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,CACxB,CAAC;AACF,OAAK,eAAe,CAAC,GAAG,cAAc,GAAG,aAAa;AAEtD,MAAI,OAAO,KAAK,iBAAiB,CAAC,SAAS,EACzC,MAAK,yBAAyB;EAKhC,MAAM,gBAAiD,EAAE;AACzD,OAAK,MAAM,CAAC,IAAI,QAAQ,OAAO,QAAQ,iBAAiB,EAAE;AACxD,OAAI,WAAW,KAAK;AAClB,WAAO,KACL,4HAEA,IACA,uBACD;AACD;;AAEF,iBAAc,MAAM;;EAItB,MAAM,aAA8C;GAClD,GAAG;GACH,GAAG;GACJ;EAED,MAAM,EAAE,MAAM,UAAU,cAAc,gBACpC,MAAM,KAAK,oBAAoB,WAAW;EAI5C,MAAM,SACJ,EAAE;AACJ,OAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,SAAS,CAChD,QAAO,QAAQ;GAAE;GAAK,KAAK,EAAE,QAAQ,QAAQ;GAAE;AAEjD,OAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,WAAW,EAAE;AACpD,OAAI,OAAO,OAAO,IAAI,WAAW,OAI/B,OAAM,IAAI,MACR,UAAU,KAAK,yMAE4B,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,GACjF;AAEH,UAAO,QAAQ;IAAE;IAAK,KAAK,EAAE,QAAQ,QAAQ;IAAE;;AAEjD,OAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,cAAc,EAAE;AACvD,OAAI,OAAO,OAAO,IAAI,WAAW,OAC/B,QAAO,KACL,0FACA,KACD;AAEH,UAAO,QAAQ;IAAE;IAAK,KAAK,EAAE,QAAQ,QAAQ;IAAE;;EAGjD,MAAM,yBAAS,IAAI,KAA8B;AAEjD,MAAI,OAAO,KAAK,OAAO,CAAC,WAAW,GAAG;AACpC,UAAO,KACL,+EACA,KAAK,mBAAmB,CACzB;AACD,UAAO;IAAE;IAAQ,kBAAkB;IAAM;;AAG3C,OAAK,MAAM,CAAC,MAAM,EAAE,KAAK,UAAU,OAAO,QAAQ,OAAO,CACvD,KAAI;AACF,UAAO,IAAI,MAAM,MAAM,KAAK,qBAAqB,MAAM,KAAK,IAAI,CAAC;WAC1D,KAAK;AACZ,SAAM,IAAI,MACR,6BAA6B,KAAK,KAAK,IAAI,OAAO,KAChD,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,IAElD,EAAE,OAAO,eAAe,QAAQ,MAAM,QAAW,CAClD;;AAIL,SAAO;GACL;GACA,kBAAkB,KAAK,oBAAoB,QAAQ,QAAQ,YAAY;GACxE;;;;;;;CAQH,AAAQ,oBACN,QACA,QACA,aACe;AACf,SAAO,oBACL,QACA,QACA,aACA,KAAK,OAAO,aACb;;;;;;CAOH,AAAQ,0BAAgC;AACtC,MAAI,KAAK,2BAA4B;AACrC,OAAK,6BAA6B;AAClC,SAAO,KACL,2VAID;;;;;;CAOH,AAAQ,6BAAmC;AACzC,MAAI,KAAK,8BAA+B;AACxC,OAAK,gCAAgC;AACrC,SAAO,KACL,yRAGD;;CAGH,AAAQ,oBAA4B;AAClC,SAAO,KAAK,QAAQ,QAAQ,KAAK,EAAE,uBAAuB;;CAG5D,AAAQ,iBAA2D;AACjE,SAAO,eAAe;GACpB,WAAW,KAAK,mBAAmB;GACnC,kBAAkB,OAAO,KAAK,KAAK,OAAO,UAAU,EAAE,CAAC,CAAC,SAAS;GAClE,CAAC;;CAGJ,MAAc,oBACZ,YAIC;EACD,MAAM,aAAa,KAAK,mBAAmB;EAI3C,MAAM,UAAU;GACd,cAAc,KAAK,OAAO;GAC1B,gBAAgB,KAAK,OAAO,SAAS,EAAE;GACvC,SAAS,KAAK,qBAAqB;GACpC;EAED,MAAM,YAAY,KAAK,QAAQ,QAAQ,KAAK,EAAE,oBAAoB;AAGlE,MAAI,CAAC,WAAW,UAAU,CACxB,QAAO,kBAAkB,YAAY;GAAE,GAAG;GAAS;GAAY,CAAC;EAOlE,MAAM,SAAS,MAAM,kBAAkB,WAAW;GAChD,GAAG;GACH;GACD,CAAC;EACF,MAAM,UAAU,MAAM,kBAAkB,YAAY;GAClD,GAAG;GACH,YAAY;IAAE,GAAG,OAAO;IAAM,GAAG;IAAY;GAC9C,CAAC;AAEF,MAAI,OAAO,KAAK,OAAO,KAAK,CAAC,WAAW,EAAG,QAAO;AAElD,OAAK,4BAA4B;AACjC,SAAO;GACL,MAAM;IAAE,GAAG,OAAO;IAAM,GAAG,QAAQ;IAAM;GACzC,cAAc,QAAQ,gBAAgB,OAAO;GAC9C;;;;;;;;CASH,AAAQ,sBAGN;EACA,MAAM,sBAAM,IAAI,KAAK;AACrB,MAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,OAAK,MAAM,EAAE,MAAM,cAAc,KAAK,QAAQ,kBAAkB,EAAE;GAChE,MAAM,cAAc;AAGpB,OAAI,OAAO,YAAY,YAAY,WACjC,KAAI,IAAI,MAAM,EACZ,SAAS,YAAY,QAAQ,KAAK,YAAY,EAC/C,CAAC;;AAGN,SAAO;;CAGT,MAAc,qBACZ,MACA,KACA,KAC0B;EAC1B,MAAM,UAAU,MAAM,KAAK,eAAe,KAAK,KAAK;EACpD,MAAM,SAAS,MAAM,KAAK,mBAAmB,MAAM,KAAK,IAAI;EAC5D,MAAM,YAAY,MAAM,KAAK,eAAe,MAAM,KAAK,KAAK,OAAO;AAEnE,2BAAyB,MAAM,SAAS,UAAU;AAElD,SAAO;GACL;GACA,cAAc,IAAI;GAClB;GACA;GACA,kBAAkB,IAAI;GACtB,UAAU,IAAI;GACd,WAAW,IAAI;GACf,kBAAkB,IAAI;GACtB,WAAW,IAAI;GACf;GACD;;;CAIH,AAAQ,mBAA+C;AACrD,SAAO,iBAAiB,KAAK,mBAAmB,CAAC;;;;;;;;CASnD,AAAQ,uBAAwC;AAC9C,SAAO,oBAAoB;;;CAI7B,AAAQ,mBAA+C;AACrD,SAAO,iBAAiB,KAAK,cAAc,KAAK,sBAAsB,CAAC;;CAGzE,MAAc,mBACZ,MACA,KACA,KACoC;EACpC,MAAM,UAAU,qBAAqB,KAAK,OAAO,kBAAkB;AACnE,SAAO,mBAAmB;GACxB;GACA;GACA,WAAW,KAAK,mBAAmB;GACnC,cAAc,IAAI,WAAW;GAC7B,aAAa,IAAI,WAAW,SAAS,QAAQ,OAAO,QAAQ;GAC5D,cAAc,KAAK;GACpB,CAAC;;CAGJ,MAAc,eACZ,KACA,MACuB;EACvB,MAAM,SAAS,IAAI,SAAS,KAAK,OAAO;EAIxC,MAAM,iBAIF,EAAE;AACN,MAAI,IAAI,aAAa,OAAW,gBAAe,WAAW,IAAI;AAC9D,MAAI,IAAI,cAAc,OAAW,gBAAe,YAAY,IAAI;AAChE,MAAI,IAAI,qBAAqB,OAC3B,gBAAe,mBAAmB,IAAI;AAExC,MAAI,CAAC,QAAQ;GACX,MAAM,EAAE,sBAAsB,MAAM,OAAO;AAC3C,OAAI;AACF,WAAO,MAAM,kBAAkB,iBAC7B,QACA,eACD;YACM,KAAK;AACZ,UAAM,IAAI,MACR,UAAU,KAAK,sFACf,EAAE,OAAO,eAAe,QAAQ,MAAM,QAAW,CAClD;;;AAGL,MAAI,OAAO,WAAW,UAAU;GAC9B,MAAM,EAAE,sBAAsB,MAAM,OAAO;AAC3C,UAAO,kBAAkB,iBAAiB,QAAQ,eAAe;;AAEnE,SAAO,MAAM;;;;;;;CAQf,MAAc,eACZ,WACA,KACA,KACA,QACyC;EACzC,MAAM,wBAAQ,IAAI,KAAgC;EAClD,MAAM,mBAAmB,IAAI,UAAU;EACvC,MAAM,cAAc,KAAK,gBAAgB,WAAW,IAAI;EACxD,MAAM,uBACJ,IAAI,UAAU,OAAO,KAAK,IAAI,OAAO,CAAC,SAAS;EAEjD,MAAM,kBAAkB,qBAAqB,KAAK,OAAO,iBAAiB;AAQ1E,MAJE,CAAC,oBACD,CAAC,yBACA,IAAI,WAAW,SAAS,gBAAgB,OAAO,gBAAgB,MAGhE,OAAM,KAAK,iBAAiB,WAAW,MAAM;AAI/C,OAAK,MAAM,CAAC,UAAU,aAAa,OAAO,QAAQ,IAAI,UAAU,EAAE,CAAC,EAAE;GACnE,MAAM,WAAW,SAAS;AAC1B,SAAM,IAAI,UAAU;IAClB,QAAQ;IACR,WAAW,SAAS,QAAQ;IAC5B,KAAK;KACH,MAAM;KACN,aACE,SAAS,aAAa,MAAM,GAAG,IAAI,IACnC,mBAAmB,SAAS;KAC9B,YAAY;MACV,MAAM;MACN,YAAY,EACV,OAAO;OACL,MAAM;OACN,aAAa;OACd,EACF;MACD,UAAU,CAAC,QAAQ;MACpB;KACF;IACF,CAAC;;EAIJ,MAAM,kBACJ,EAAE;AACJ,OAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,YAAY,EAAE;AACrD,OAAI,eAAe,KAAK,EAAE;AACxB,UAAM,IAAI,KAAK;KACb,QAAQ;KACR,YAAY,KAAK;KACjB,WAAW,KAAK;KAChB,KAAK;MAAE,GAAG,KAAK;MAAK,MAAM;MAAK;KAChC,CAAC;AACF;;AAEF,OAAI,eAAe,KAAK,EAAE;AACxB,UAAM,IAAI,KAAK;KACb,QAAQ;KACR,cAAc;KACd,KAAK;MAAE,GAAG,yBAAyB,KAAK;MAAE,MAAM;MAAK;KACtD,CAAC;AACF;;AAEF,OAAI,iBAAiB,KAAK,EAAE;AAC1B,UAAM,IAAI,KAAK;KACb,QAAQ;KACR,MAAM,KAAK;KACX,KAAK;MAKH,MAAM;MACN,aAAa,0BAA0B,KAAK,KAAK;MACjD,YAAY;OAAE,MAAM;OAAU,YAAY,EAAE;OAAE;MAC/C;KACF,CAAC;AACF;;AAEF,OAAI,aAAa,KAAK,EAAE;AACtB,oBAAgB,KAAK,KAAK;AAC1B;;AAEF,SAAM,IAAI,MACR,UAAU,UAAU,UAAU,IAAI,6BACnC;;AAGH,MAAI,gBAAgB,SAAS,EAC3B,OAAM,KAAK,mBAAmB,iBAAiB,MAAM;AAKvD,MAAI,UAAU,OAAO,UAAU,OAAO,GAAG;AACvC,SAAM,IAAI,cAAc;IACtB,QAAQ;IACR,SAAS;IACT,SAAS;IACT,KAAK;IACN,CAAC;AACF,SAAM,IAAI,mBAAmB;IAC3B,QAAQ;IACR,SAAS;IACT,SAAS;IACT,KAAK;IACN,CAAC;;AAGJ,SAAO;;;;;;;;;;;;;CAcT,AAAQ,gBAAgB,WAAmB,KAAkC;AAC3E,MAAI,OAAO,IAAI,UAAU,WACvB,QAAO,IAAI,SAAS,EAAE;AAExB,MAAI;AACF,UAAO,IAAI,MAAM,KAAK,iBAAiB,CAAC;WACjC,KAAK;AACZ,SAAM,IAAI,MACR,UAAU,UAAU,oCAClB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,IAElD,EAAE,OAAO,eAAe,QAAQ,MAAM,QAAW,CAClD;;;;;;;;;;;;;;;CAgBL,AAAQ,kBAA2B;EACjC,MAAM,MAA6C,EAAE;AACrD,MAAI,CAAC,KAAK,QACR,QAAO,mBAAmB,KAAK,UAAU,KAAK,KAAK,mBAAmB;AAExE,OAAK,MAAM,EAAE,MAAM,cAAc,KAAK,QAAQ,kBAAkB,CAE9D,KAAI,OADY,SAAmC,YAC7B,WACpB,KAAI,QAAQ;MAEZ,KAAI,QAAQ,EACV,UAAU,SAAS,2BAA2B,MAAM,UAAU,KAAK,EACpE;AAGL,SAAO,mBAAmB,KAAK,UAAU,KAAK,KAAK,mBAAmB;;CAGxE,MAAc,iBACZ,WACA,OACe;AACf,MAAI,CAAC,KAAK,QAAS;EACnB,MAAM,YAAsB,EAAE;EAC9B,MAAM,kCAAkB,IAAI,KAAuB;EACnD,MAAM,cAAc,YAAoB,cAAsB;GAC5D,MAAM,OAAO,gBAAgB,IAAI,WAAW,IAAI,EAAE;AAClD,QAAK,KAAK,UAAU;AACpB,mBAAgB,IAAI,YAAY,KAAK;;AAGvC,OAAK,MAAM,EACT,MAAM,YACN,cACG,KAAK,QAAQ,kBAAkB,EAAE;AACpC,OAAI,eAAe,KAAK,KAAM;GAC9B,MAAM,UAAU,2BAA2B,YAAY,SAAS;AAChE,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,QAAI,MAAM,oBAAoB,MAAM;AAClC,gBAAW,MAAM,YAAY,MAAM,UAAU;AAC7C;;AAEF,UAAM,IAAI,KAAK;KACb,QAAQ;KACR,YAAY,MAAM;KAClB,WAAW,MAAM;KACjB,KAAK;MAAE,GAAG,MAAM;MAAK,MAAM;MAAK;KACjC,CAAC;AACF,cAAU,KAAK,IAAI;;;AAIvB,MAAI,UAAU,SAAS,EACrB,QAAO,KACL,4CACA,WACA,UAAU,QACV,UAAU,KAAK,KAAK,CACrB;AAEH,MAAI,gBAAgB,OAAO,GAAG;GAC5B,MAAM,UAAU,MAAM,KAAK,gBAAgB,SAAS,CAAC,CAClD,KAAK,CAAC,GAAG,WAAW,GAAG,EAAE,GAAG,MAAM,OAAO,GAAG,CAC5C,KAAK,KAAK;AACb,UAAO,KACL,2HACA,WACA,MAAM,KAAK,gBAAgB,QAAQ,CAAC,CAAC,QAClC,GAAG,SAAS,IAAI,KAAK,QACtB,EACD,EACD,QACD;;;CAIL,MAAc,mBACZ,aACA,OACe;EACf,IAAI;EACJ,IAAI;AAEJ,MAAI;GACF,MAAM,EAAE,uBAAuB,MAAM,OAAO;GAC5C,MAAM,WAAW,oBAAoB;AACrC,SAAM,SAAS,OAAO,gBAAgB;AACtC,UAAO,SAAS,OAAO;AACvB,kBAAe,YAAY;IACzB,MAAM,UAAU,IAAI,SAAS;AAC7B,UAAM,SAAS,OAAO,aAAa,QAAQ;AAC3C,WAAO,OAAO,YAAY,QAAQ,SAAS,CAAC;;UAExC;AACN,UAAO,QAAQ,IAAI;AACnB,kBAAe,YAA6C;IAC1D,MAAM,QAAQ,QAAQ,IAAI;AAC1B,WAAO,QAAQ,EAAE,eAAe,UAAU,SAAS,GAAG,EAAE;;;AAI5D,MAAI,CAAC,MAAM;AACT,UAAO,KACL,6DACA,YAAY,OACb;AACD;;AAGF,MAAI,CAAC,KAAK,WAAW;GACnB,MAAM,SAAS,mBAAmB,KAAK,OAAO,KAAK,KAAK;AACxD,QAAK,YAAY,IAAI,gBAAgB,MAAM,cAAc,OAAO;;EAGlE,MAAM,YAAY,mBAAmB,YAAY;EACjD,MAAM,SAAS,MAAM,KAAK,UAAU,WAAW,UAAU;AACzD,MAAI,OAAO,OAAO,SAAS,EAMzB,QAAO,KACL,+HACA,OAAO,OAAO,QACd,UAAU,QACV,OAAO,OAAO,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,CAC5C;AAGH,OAAK,MAAM,OAAO,KAAK,UAAU,uBAAuB,CACtD,OAAM,IAAI,IAAI,MAAM;GAClB,QAAQ;GACR,aAAa,IAAI;GACjB;GACD,CAAC;;CAMN,gBAAuC;AACrC,SAAO,EAAE;;CAGX,MAAM,mBAAqC;AACzC,QAAM,IAAI,MAAM,yDAAyD;;;;;;;;CAW3E,AAAQ,oBAAoB;AAC1B,MAAI,CAAC,KAAK,QAAS;EAGnB,MAAM,WAAW,KAAsB,QACrC,KAAK,cAAc,KAAK,IAAI;AAC9B,OAAK,QAAQ,SAAS,QAAQ,gBAAgB,QAAQ;AACtD,OAAK,QAAQ,SAAS,QAAQ,cAAc,QAAQ;;CAGtD,aAAa,QAAoB;AAC/B,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAK,QAAQ,KAAK,YAAY,KAAK,IAAI;GACxD,CAAC;AACF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAK,QAAQ,KAAK,cAAc,KAAK,IAAI;GAC1D,CAAC;AACF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAK,QAAQ,KAAK,eAAe,KAAK,IAAI;GAC3D,CAAC;AACF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAK,QAAQ,KAAK,mBAAmB,KAAK,IAAI;GAC/D,CAAC;AACF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAK,QAAQ,KAAK,iBAAiB,KAAK,IAAI;GAC7D,CAAC;AACF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,KAAK,QAAQ,KAAK,oBAAoB,KAAK,IAAI;GAChE,CAAC;AACF,OAAK,MAAM,QAAQ;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACN,SAAS,OAAO,MAAM,QAAQ;AAC5B,QAAI,KAAK;KACP,QAAQ,MAAM,KAAK,KAAK,OAAO,MAAM,CAAC;KACtC,cAAc,KAAK;KACpB,CAAC;;GAEL,CAAC;;CAGJ,eAAwC;EAItC,MAAM,SAAkE,EAAE;AAC1E,OAAK,MAAM,CAAC,MAAM,UAAU,KAAK,OAC/B,KAAI,MAAM,OAAQ,QAAO,QAAQ,MAAM,OAAO;AAEhD,SAAO;GACL,QAAQ,MAAM,KAAK,KAAK,OAAO,MAAM,CAAC;GACtC,cAAc,KAAK;GACnB;GACD;;CAGH,MAAc,YAAY,KAAsB,KAAuB;EACrE,MAAM,SAAS,kBAAkB,UAAU,IAAI,KAAK;AACpD,MAAI,CAAC,OAAO,SAAS;AACnB,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO;IACP,SAAS,OAAO,MAAM,SAAS,CAAC;IACjC,CAAC;AACF;;EAEF,MAAM,EACJ,SACA,UACA,OAAO,WACP,aACA,UACE,OAAO;EAEX,MAAM,aAAa,KAAK,aAAa,UAAU;AAC/C,MAAI,CAAC,YAAY;AACf,OAAI,OAAO,IAAI,CAAC,KAAK,EACnB,OAAO,YACH,UAAU,UAAU,eACpB,uBACL,CAAC;AACF;;EAGF,MAAM,SAAS,KAAK,cAAc,IAAI;EAKtC,MAAM,SAAS,KAAK;AACpB,MAAI,KAAK,iBAAiB,OAAO,IAAI,OAAO,6BAA6B;AACvE,OAAI,UAAU,eAAe,IAAI;AACjC,OAAI,OAAO,IAAI,CAAC,KAAK,EACnB,OAAO,oDAAoD,OAAO,4BAA4B,sEAC/F,CAAC;AACF;;EAQF,IAAI;AACJ,MAAI;GACF,MAAM,WAAW,WACb,MAAM,KAAK,YAAY,IAAI,UAAU,OAAO,GAC5C;AACJ,OAAI,YAAY,CAAC,UAAU;AACzB,QAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,UAAU,SAAS,aAAa,CAAC;AAC/D;;AAEF,YAAS,YAAa,MAAM,KAAK,YAAY,OAAO,OAAO;GAE3D,MAAM,cAAuB;IAC3B,IAAI,YAAY;IAChB,MAAM;IACN,SAAS;IACT,2BAAW,IAAI,MAAM;IACtB;AACD,SAAM,KAAK,YAAY,WAAW,OAAO,IAAI,QAAQ,YAAY;WAC1D,KAAK;AACZ,UAAO,MAAM,mCAAmC,IAAI;AACpD,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,2BAA2B,CAAC;AAC1D;;AAEF,SAAO,KAAK,aACV,KACA,KACA,YACA,QACA,QACA,aACA,MACD;;;;;;;;;;;;;CAcH,AAAQ,iCACN,YACU;AACV,MAAI,CAAC,KAAK,uBAAuB,sBAAuB,QAAO,EAAE;EACjE,MAAM,QAAkB,EAAE;AAC1B,OAAK,MAAM,SAAS,WAAW,UAAU,QAAQ,CAC/C,KAAI,iBAAiB,MAAM,IAAI,YAAY,CACzC,OAAM,KAAK,MAAM,IAAI,KAAK;AAG9B,SAAO;;;;;;;;;;;;CAaT,MAAc,cAAc,KAAsB,KAAuB;EACvE,MAAM,SAAS,yBAAyB,UAAU,IAAI,KAAK;AAC3D,MAAI,CAAC,OAAO,SAAS;AACnB,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO;IACP,SAAS,OAAO,MAAM,SAAS,CAAC;IACjC,CAAC;AACF;;EAEF,MAAM,EAAE,OAAO,gBAAgB,OAAO;EACtC,MAAM,aAAa,KAAK,cAAc;AACtC,MAAI,CAAC,YAAY;AACf,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,uBAAuB,CAAC;AACtD;;EAQF,MAAM,gBAAgB,KAAK,iCAAiC,WAAW;AACvE,MAAI,cAAc,SAAS,GAAG;AAC5B,OAAI,OAAO,IAAI,CAAC,KAAK,EACnB,OACE,UAAU,WAAW,KAAK,YAAY,cAAc,OAAO,2BACvD,cAAc,KAAK,KAAK,CAAC,gMAGhC,CAAC;AACF;;EAGF,MAAM,SAAS,KAAK,cAAc,IAAI;EAItC,MAAM,SAAS,KAAK;AACpB,MAAI,KAAK,iBAAiB,OAAO,IAAI,OAAO,6BAA6B;AACvE,OAAI,UAAU,eAAe,IAAI;AACjC,OAAI,OAAO,IAAI,CAAC,KAAK,EACnB,OAAO,oDAAoD,OAAO,4BAA4B,sEAC/F,CAAC;AACF;;EAKF,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,KAAK,YAAY,OAAO,OAAO;AAE9C,OAAI,OAAO,UAAU,SACnB,OAAM,KAAK,YAAY,WAAW,OAAO,IAAI,QAAQ;IACnD,IAAI,YAAY;IAChB,MAAM;IACN,SAAS;IACT,2BAAW,IAAI,MAAM;IACtB,CAAC;OAEF,MAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,OAAQ,KAAK,QAAQ;IAC3B,MAAM,UACJ,OAAO,KAAK,YAAY,WACpB,KAAK,UACL,KAAK,UAAU,KAAK,WAAW,GAAG;AACxC,QAAI,CAAC,QAAS;AACd,UAAM,KAAK,YAAY,WAAW,OAAO,IAAI,QAAQ;KACnD,IAAI,YAAY;KAChB;KACA;KACA,2BAAW,IAAI,MAAM;KACtB,CAAC;;WAGC,KAAK;AACZ,UAAO,MAAM,0CAA0C,IAAI;AAC3D,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,2BAA2B,CAAC;AAC1D;;AAGF,SAAO,KAAK,sBACV,KACA,KACA,YACA,QACA,QACA,YACD;;CAGH,MAAc,aACZ,KACA,KACA,YACA,QACA,QACA,aACA,aACe;EACf,MAAM,kBAAkB,IAAI,iBAAiB;EAC7C,MAAM,SAAS,gBAAgB;EAC/B,MAAM,YAAY,YAAY;AAC9B,OAAK,YAAY,WAAW,QAAQ,gBAAgB;EAOpD,MAAM,QAAQ,MAAM,KAAK,WAAW,UAAU,QAAQ,CAAC,CACpD,QAAQ,MAAM,EAAE,WAAW,oBAAoB,CAC/C,KAAK,MAAM,EAAE,IAAI;EACpB,MAAM,iBAAiB,KAAK;EAC5B,MAAM,SAAS,KAAK;EACpB,MAAM,iBAAiB,IAAI,cAAmC;EAC9D,MAAM,aAAa,IAAI,sBAAsB;EAM7C,MAAM,WAAqB;GACzB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,eAAe,EAAE,OAAO,GAAG;GAC5B;EAED,MAAM,OAAO,KAAK,kBAAkB;EACpC,MAAM,eAAe,MAAc,SACjC,iBAAiB,MAAM,UAAU,WAAW,WAAW,MAAM,MAAM,EAAE;EAMvE,MAAM,UAAU,YAAY;AAC1B,OAAI;AACF,SAAK,MAAM,OAAO,WAAW,UAAU;KACrC,MAAM;KACN,MAAM,EAAE,UAAU,OAAO,IAAI;KAC9B,CAAC,CACA,gBAAe,KAAK,IAAI;AAI1B,UAAM,WACJ,WAAW,QAAQ,SACnB,EACE,UAAU,OAAO,SAAS,KAAK,OAAO;KACpC,MAAM,EAAE;KACR,SAAS,EAAE;KACZ,EAAE,EACJ,EACD,OAAO,SAAS;AAGd,SAAI,YAAa,gBAAe,YAAY;KAE5C,MAAM,cAAc,KAAK,UACrB,KAAK,QACF,gBAAgB,CAChB,QAAQ,MAAM,MAAM,KAAK,QAAQ,MAAM,SAAS,GACnD,EAAE;KACN,IAAI,aAAa,sBACf,YACA,KAAK,OAAO,kBACZ;MACE,WAAW,WAAW;MACtB;MACA,WAAW,MAAM,KAAK,MAAM,EAAE,KAAK;MACpC,CACF;AAMD,SAAI,aAAa;MACf,MAAM,WAAW,KAAK,kBAAkB,YAAY,YAAY;AAChE,UAAI,SAAU,cAAa,GAAG,WAAW,MAAM;;KAGjD,MAAM,qBAAgC,CACpC;MACE,IAAI;MACJ,MAAM;MACN,SAAS;MACT,2BAAW,IAAI,MAAM;MACtB,EACD,GAAG,OAAO,SACX;KAgBD,MAAM,cAAc,MAAM,qBAdX,WAAW,QAAQ,IAChC;MACE,UAAU;MACV;MACA,UAAU,OAAO;MACjB;MACA,YAAY,uBAAuB,WAAW,UAAU;MACzD,EACD;MAAE;MAAa;MAAQ,CACxB,EAKsD;MACrD;MACA,UAAU,UAAU;AAClB,YAAK,MAAM,cAAc,WAAW,UAAU,MAAM,CAClD,gBAAe,KAAK,WAAW;;MAGpC,CAAC;AAEF,SAAI,aAAa;AACf,WAAK,WAAW;OAAE,MAAM;OAAa,SAAS;OAAa,CAAC;AAC5D,YAAM,KAAK,YAAY,WAAW,OAAO,IAAI,QAAQ;OACnD,IAAI,YAAY;OAChB,MAAM;OACN,SAAS;OACT,2BAAW,IAAI,MAAM;OACtB,CAAC;;KAKJ,MAAM,gBAAgB,gBAAgB;AACtC,SAAI,cACF,MAAK,MAAM,OAAO,WAAW,UAAU;MACrC,MAAM;MACN,MAAM,EAAE,eAAe;MACxB,CAAC,CACA,gBAAe,KAAK,IAAI;MAI/B;AAED,SAAK,MAAM,OAAO,WAAW,UAAU,CAAE,gBAAe,KAAK,IAAI;YAC1D,OAAO;AACd,QAAI,OAAO,SAAS;AAClB,oBAAe,OAAO;AACtB;;AAEF,WAAO,MAAM,wBAAwB,MAAM;AAC3C,mBAAe,MAAM,MAAM;AAC3B;aACQ;AAGR,SAAK,aAAa,YAAY,UAAU;AACxC,SAAK,cAAc,UAAU;AAK7B,QAAI,WAAW,UACb,KAAI;AACF,WAAM,KAAK,YAAY,OAAO,OAAO,IAAI,OAAO;aACzC,KAAK;AACZ,YAAO,KACL,4CACA,OAAO,IACP,IACD;;;AAIP,kBAAe,OAAO;MACpB;AAEJ,QAAM,KAAK,cACT,KACA,mBAAmB;AACjB,OAAI;AACF,eAAW,MAAM,MAAM,eACrB,OAAM;aAEA;AACR,UAAM,OAAO,YAAY,OAAU;;KAGvC;GACE,GAAG;GACH,QAAQ;IAAE,GAAG,oBAAoB;IAAQ,UAAU;IAAW;GAC/D,CACF;;;;;;;;;;;;;;;;;;;;;;;;;CA0BH,MAAc,sBACZ,KACA,KACA,YACA,QACA,QACA,aACe;EACf,MAAM,kBAAkB,IAAI,iBAAiB;EAC7C,MAAM,SAAS,gBAAgB;EAC/B,MAAM,YAAY,YAAY;AAC9B,OAAK,YAAY,WAAW,QAAQ,gBAAgB;EAEpD,MAAM,QAAQ,MAAM,KAAK,WAAW,UAAU,QAAQ,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI;EACzE,MAAM,SAAS,KAAK;EAIpB,IAAI;EAEJ,MAAM,WAAqB;GACzB;GACA;GACA;GACA;GACA;GAIA,gBAAgB;IAAE,uBAAuB;IAAO,WAAW;IAAG;GAC9D;GACA,YAAY,IAAI,sBAAsB;GACtC,gBAAgB,IAAI,cAAmC;GACvD,eAAe,EAAE,OAAO,GAAG;GAC5B;EAED,MAAM,OAAO,KAAK,kBAAkB;EACpC,MAAM,eAAe,MAAc,SACjC,iBAAiB,MAAM,UAAU,WAAW,WAAW,MAAM,MAAM,EAAE;EAEvE,IAAI,cAAc;AAClB,MAAI;AAIF,SAAM,WACJ,WAAW,QAAQ,SACnB,EACE,UAAU,OAAO,SAAS,KAAK,OAAO;IACpC,MAAM,EAAE;IACR,SAAS,EAAE;IACZ,EAAE,EACJ,EACD,OAAO,SAAS;AAGd,QAAI,YAAa,gBAAe,YAAY;IAE5C,MAAM,cAAc,KAAK,UACrB,KAAK,QACF,gBAAgB,CAChB,QAAQ,MAAM,MAAM,KAAK,QAAQ,MAAM,SAAS,GACnD,EAAE;IAWN,MAAM,qBAAgC,CACpC;KACE,IAAI;KACJ,MAAM;KACN,SAde,sBACjB,YACA,KAAK,OAAO,kBACZ;MACE,WAAW,WAAW;MACtB;MACA,WAAW,MAAM,KAAK,MAAM,EAAE,KAAK;MACpC,CACF;KAOG,2BAAW,IAAI,MAAM;KACtB,EACD,GAAG,OAAO,SACX;AAYD,kBAAc,MAAM,qBAVL,WAAW,QAAQ,IAChC;KACE,UAAU;KACV;KACA,UAAU,OAAO;KACjB;KACD,EACD;KAAE;KAAa;KAAQ,CACxB,EAEgD,EAAE,QAAQ,CAAC;AAE5D,QAAI,aAAa;AACf,UAAK,WAAW;MAAE,MAAM;MAAa,SAAS;MAAa,CAAC;AAC5D,WAAM,KAAK,YAAY,WAAW,OAAO,IAAI,QAAQ;MACnD,IAAI,YAAY;MAChB,MAAM;MACN,SAAS;MACT,2BAAW,IAAI,MAAM;MACtB,CAAC;;AAGJ,oBAAgB,gBAAgB;KAEnC;WACM,OAAO;AACd,OAAI,OAAO,SAAS;AAClB,QAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD;;AAEF,UAAO,MAAM,0BAA0B,MAAM;GAC7C,MAAM,UACJ,QAAQ,IAAI,aAAa,eACrB,0BACA,iBAAiB,QACf,MAAM,UACN,OAAO,MAAM;AACrB,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,SAAS,CAAC;AACxC;YACQ;AACR,QAAK,aAAa,YAAY,UAAU;AACxC,QAAK,cAAc,UAAU;AAC7B,OAAI,WAAW,UACb,KAAI;AACF,UAAM,KAAK,YAAY,OAAO,OAAO,IAAI,OAAO;YACzC,KAAK;AACZ,WAAO,KACL,4CACA,OAAO,IACP,IACD;;;EAKP,MAAM,aAAa,QAAQ,YAAY;EAEvC,MAAM,UAAiC;GACrC,MAAM;GACN,IAHgB,OAAO,YAAY;GAInC,QAAQ;GACR,MAAM;GACN,SAAS,CAAC;IAAE,MAAM;IAAe,MAAM;IAAa,CAAC;GACtD;AACD,MAAI,KAAK;GACP,IAAI;GACJ,QAAQ;GACR,YAAY,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;GACzC,QAAQ;GACR,WAAW,OAAO;GAGlB,GAAI,gBAAgB,EAAE,iBAAiB,eAAe,GAAG,EAAE;GAC3D,QAAQ,CAAC,QAAQ;GAClB,CAAC;;CAGJ,AAAQ,kBACN,OACA,MACiB;AACjB,SAAO,kBAAkB,OAAO,YAAY,KAAK,sBAAsB,CAAC;;;;;;;;CAS1E,AAAQ,kBACN,YACA,MACe;AACf,SAAO,kBAAkB,YAAY,KAAK;;;CAI5C,AAAQ,mBAAqC;AAC3C,SAAO;GACL,cAAc,KAAK;GACnB,SAAS,KAAK;GACd,oBAAoB,KAAK;GACzB,QAAQ,KAAK;GACb,oBAAoB,OAAO,SAAS,KAAK,kBAAkB,OAAO,KAAK;GACvE,YAAY,KAAK;GACjB,kBAAkB,KAAK,OAAO;GAC/B;;CAGH,MAAc,cAAc,KAAsB,KAAuB;EACvE,MAAM,SAAS,oBAAoB,UAAU,IAAI,KAAK;AACtD,MAAI,CAAC,OAAO,SAAS;AACnB,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO;IACP,SAAS,OAAO,MAAM,SAAS,CAAC;IACjC,CAAC;AACF;;EAEF,MAAM,EAAE,aAAa,OAAO;EAC5B,MAAM,QAAQ,KAAK,QAAQ,IAAI,SAAS;AACxC,MAAI,CAAC,OAAO;AAEV,OAAI,KAAK,EAAE,WAAW,MAAM,CAAC;AAC7B;;EAEF,MAAM,SAAS,KAAK,cAAc,IAAI;AACtC,MAAI,MAAM,WAAW,QAAQ;AAC3B,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,aAAa,CAAC;AAC5C;;AAEF,QAAM,WAAW,MAAM,oBAAoB;AAC3C,OAAK,cAAc,SAAS;AAC5B,OAAK,aAAa,YAAY,SAAS;AACvC,MAAI,KAAK,EAAE,WAAW,MAAM,CAAC;;CAG/B,MAAc,eAAe,KAAsB,KAAuB;EACxE,MAAM,SAAS,sBAAsB,UAAU,IAAI,KAAK;AACxD,MAAI,CAAC,OAAO,SAAS;AACnB,OAAI,OAAO,IAAI,CAAC,KAAK;IACnB,OAAO;IACP,SAAS,OAAO,MAAM,SAAS,CAAC;IACjC,CAAC;AACF;;EAEF,MAAM,EAAE,UAAU,YAAY,aAAa,OAAO;EAElD,MAAM,cAAc,KAAK,QAAQ,IAAI,SAAS;AAC9C,MAAI,CAAC,aAAa;AAIhB,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,yCAAyC,CAAC;AACxE;;EAGF,MAAM,SAAS,KAAK,cAAc,IAAI;AACtC,MAAI,YAAY,WAAW,QAAQ;AACjC,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,aAAa,CAAC;AAC5C;;EAGF,MAAM,SAAS,KAAK,aAAa,OAAO;GAAE;GAAY;GAAQ;GAAU,CAAC;AACzE,MAAI,CAAC,OAAO,IAAI;AACd,OAAI,OAAO,WAAW,aAAa;AACjC,QAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,aAAa,CAAC;AAC5C;;AAEF,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,yCAAyC,CAAC;AACxE;;AAGF,MAAI,KAAK,EAAE,UAAU,CAAC;;CAGxB,MAAc,mBACZ,KACA,KACA;EACA,MAAM,SAAS,KAAK,cAAc,IAAI;EACtC,MAAM,UAAU,MAAM,KAAK,YAAY,KAAK,OAAO;AACnD,MAAI,KAAK,EAAE,SAAS,CAAC;;CAGvB,MAAc,iBAAiB,KAAsB,KAAuB;EAC1E,MAAM,SAAS,KAAK,cAAc,IAAI;EACtC,MAAM,SAAS,MAAM,KAAK,YAAY,IAAI,IAAI,OAAO,UAAU,OAAO;AACtE,MAAI,CAAC,QAAQ;AACX,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;;AAEF,MAAI,KAAK,OAAO;;CAGlB,MAAc,oBACZ,KACA,KACA;EACA,MAAM,SAAS,KAAK,cAAc,IAAI;AAEtC,MAAI,CADY,MAAM,KAAK,YAAY,OAAO,IAAI,OAAO,UAAU,OAAO,EAC5D;AACZ,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;;AAEF,MAAI,KAAK,EAAE,SAAS,MAAM,CAAC;;CAG7B,AAAQ,aAAa,MAAuC;AAC1D,MAAI,KAAM,QAAO,KAAK,OAAO,IAAI,KAAK,IAAI;AAC1C,MAAI,KAAK,iBACP,QAAO,KAAK,OAAO,IAAI,KAAK,iBAAiB,IAAI;EAEnD,MAAM,QAAQ,KAAK,OAAO,QAAQ,CAAC,MAAM;AACzC,SAAO,MAAM,OAAO,OAAO,MAAM;;CAGnC,AAAQ,gBAAsB;AAC5B,MAAI,KAAK,OAAO,SAAS,EAAG;AAC5B,UAAQ,IAAI,GAAG;AACf,UAAQ,IAAI,KAAK,GAAG,KAAK,SAAS,CAAC,GAAG,GAAG,IAAI,IAAI,KAAK,OAAO,KAAK,GAAG,GAAG;AACxE,UAAQ,IAAI,KAAK,GAAG,IAAI,IAAI,OAAO,GAAG,CAAC,GAAG;AAC1C,OAAK,MAAM,CAAC,MAAM,QAAQ,KAAK,QAAQ;GACrC,MAAM,QAAQ,IAAI,UAAU;GAC5B,MAAM,SAAS,SAAS,KAAK,mBAAmB,GAAG,MAAM,IAAI,GAAG;AAChE,WAAQ,IACN,KAAK,OAAO,GAAG,GAAG,KAAK,KAAK,OAAO,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI,GAAG,MAAM,QAAQ,GACpE;;AAEH,UAAQ,IAAI,KAAK,GAAG,IAAI,IAAI,OAAO,GAAG,CAAC,GAAG;AAC1C,UAAQ,IAAI,GAAG;;CAGjB,MAAM,WAA0B;AAC9B,OAAK,aAAa,UAAU;AAC5B,MAAI,KAAK,WAAW;AAClB,SAAM,KAAK,UAAU,OAAO;AAC5B,QAAK,YAAY;;;CAIrB,UAAU;AACR,SAAO;GACL,WAAW,MAAc,QACvB,KAAK,kBAAkB,MAAM,IAAI;GACnC,YAAY,MAAM,KAAK,KAAK,OAAO,MAAM,CAAC;GAC1C,MAAM,SAAiB,KAAK,OAAO,IAAI,KAAK,IAAI;GAChD,cAAc,KAAK,QAAQ;GAC3B,kBAAkB,KAAK;GACvB,aAAa,WAAmB,KAAK,YAAY,KAAK,OAAO;GAC9D;;CAGH,MAAc,kBACZ,MACA,KACe;EACf,MAAM,aAAa,MAAM,KAAK,qBAAqB,MAAM,KAAK,EAC5D,QAAQ,QACT,CAAC;AACF,OAAK,OAAO,IAAI,MAAM,WAAW;AACjC,MAAI,CAAC,KAAK,iBAAkB,MAAK,mBAAmB;;;AAIxD,SAAS,qBAAqB,OAG5B;AAMA,KAAI,UAAU,OAAW,QAAO;EAAE,MAAM;EAAO,MAAM;EAAO;AAC5D,KAAI,OAAO,UAAU,UAAW,QAAO;EAAE,MAAM;EAAO,MAAM;EAAO;AACnE,QAAO;EAAE,MAAM,MAAM,QAAQ;EAAO,MAAM,MAAM,QAAQ;EAAO;;;;;;;;;;;;;;;;;;;AAoBjE,MAAa,SAAS,SAAS,aAAa"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/plugins/agents/approval.ts
|
|
2
|
+
/**
|
|
3
|
+
* Decide whether a tool call must traverse the approval gate. Honours both
|
|
4
|
+
* the modern `effect` field (mutating values: write / update / destructive)
|
|
5
|
+
* and the legacy `destructive: true` boolean. The contract is documented on
|
|
6
|
+
* `ToolAnnotations.effect` in shared/agent.ts.
|
|
7
|
+
*
|
|
8
|
+
* Without this, a tool authored only with `effect: "destructive"` (the
|
|
9
|
+
* preferred API) bypassed the gate entirely.
|
|
10
|
+
*/
|
|
11
|
+
function requiresApproval(annotations) {
|
|
12
|
+
if (!annotations) return false;
|
|
13
|
+
if (annotations.destructive === true) return true;
|
|
14
|
+
switch (annotations.effect) {
|
|
15
|
+
case "write":
|
|
16
|
+
case "update":
|
|
17
|
+
case "destructive": return true;
|
|
18
|
+
case "read":
|
|
19
|
+
case void 0: return false;
|
|
20
|
+
default:
|
|
21
|
+
annotations.effect;
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { requiresApproval };
|
|
28
|
+
//# sourceMappingURL=approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.js","names":[],"sources":["../../../src/plugins/agents/approval.ts"],"sourcesContent":["import type { ToolAnnotations } from \"shared\";\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 */\nexport function requiresApproval(\n annotations: ToolAnnotations | undefined,\n): 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"],"mappings":";;;;;;;;;;AAWA,SAAgB,iBACd,aACS;AACT,KAAI,CAAC,YAAa,QAAO;AACzB,KAAI,YAAY,gBAAgB,KAAM,QAAO;AAC7C,SAAQ,YAAY,QAApB;EACE,KAAK;EACL,KAAK;EACL,KAAK,cACH,QAAO;EACT,KAAK;EACL,KAAK,OACH,QAAO;EACT;AAC6B,eAAY;AACvC,UAAO"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/plugins/agents/builtin-tools.ts
|
|
2
|
+
/** Built-in tool the model calls to load a skill's full instructions on demand. */
|
|
3
|
+
const LOAD_SKILL_TOOL_DEF = {
|
|
4
|
+
name: "load_skill",
|
|
5
|
+
description: "Load the full instructions for one of the available skills by name. Call this before acting on a task that matches a skill's description. Returns the skill's instructions plus a list of any bundled files you can read with read_skill_file.",
|
|
6
|
+
parameters: {
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: { skill: {
|
|
9
|
+
type: "string",
|
|
10
|
+
description: "The exact skill name to load, as shown in the available-skills list."
|
|
11
|
+
} },
|
|
12
|
+
required: ["skill"]
|
|
13
|
+
},
|
|
14
|
+
annotations: { effect: "read" }
|
|
15
|
+
};
|
|
16
|
+
/** Built-in tool for reading a resource file that a loaded skill references. */
|
|
17
|
+
const READ_SKILL_FILE_TOOL_DEF = {
|
|
18
|
+
name: "read_skill_file",
|
|
19
|
+
description: "Read a bundled resource file that a loaded skill references (e.g. a reference doc). Only files listed by load_skill for that skill are readable.",
|
|
20
|
+
parameters: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
skill: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "The skill that owns the file."
|
|
26
|
+
},
|
|
27
|
+
path: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Relative path of the file within the skill, as listed by load_skill."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
required: ["skill", "path"]
|
|
33
|
+
},
|
|
34
|
+
annotations: { effect: "read" }
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { LOAD_SKILL_TOOL_DEF, READ_SKILL_FILE_TOOL_DEF };
|
|
39
|
+
//# sourceMappingURL=builtin-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin-tools.js","names":[],"sources":["../../../src/plugins/agents/builtin-tools.ts"],"sourcesContent":["import type { AgentToolDefinition } from \"shared\";\n\n/** Built-in tool the model calls to load a skill's full instructions on demand. */\nexport const LOAD_SKILL_TOOL_DEF: AgentToolDefinition = {\n name: \"load_skill\",\n description:\n \"Load the full instructions for one of the available skills by name. Call this before acting on a task that matches a skill's description. Returns the skill's instructions plus a list of any bundled files you can read with read_skill_file.\",\n parameters: {\n type: \"object\",\n properties: {\n skill: {\n type: \"string\",\n description:\n \"The exact skill name to load, as shown in the available-skills list.\",\n },\n },\n required: [\"skill\"],\n },\n annotations: { effect: \"read\" },\n};\n\n/** Built-in tool for reading a resource file that a loaded skill references. */\nexport const READ_SKILL_FILE_TOOL_DEF: AgentToolDefinition = {\n name: \"read_skill_file\",\n description:\n \"Read a bundled resource file that a loaded skill references (e.g. a reference doc). Only files listed by load_skill for that skill are readable.\",\n parameters: {\n type: \"object\",\n properties: {\n skill: { type: \"string\", description: \"The skill that owns the file.\" },\n path: {\n type: \"string\",\n description:\n \"Relative path of the file within the skill, as listed by load_skill.\",\n },\n },\n required: [\"skill\", \"path\"],\n },\n annotations: { effect: \"read\" },\n};\n"],"mappings":";;AAGA,MAAa,sBAA2C;CACtD,MAAM;CACN,aACE;CACF,YAAY;EACV,MAAM;EACN,YAAY,EACV,OAAO;GACL,MAAM;GACN,aACE;GACH,EACF;EACD,UAAU,CAAC,QAAQ;EACpB;CACD,aAAa,EAAE,QAAQ,QAAQ;CAChC;;AAGD,MAAa,2BAAgD;CAC3D,MAAM;CACN,aACE;CACF,YAAY;EACV,MAAM;EACN,YAAY;GACV,OAAO;IAAE,MAAM;IAAU,aAAa;IAAiC;GACvE,MAAM;IACJ,MAAM;IACN,aACE;IACH;GACF;EACD,UAAU,CAAC,SAAS,OAAO;EAC5B;CACD,aAAa,EAAE,QAAQ,QAAQ;CAChC"}
|