diff --git a/docs/docs/guides/migrating-to-agents-plugin.md b/docs/docs/guides/migrating-to-agents-plugin.md index da1fd091..bdea44f3 100644 --- a/docs/docs/guides/migrating-to-agents-plugin.md +++ b/docs/docs/guides/migrating-to-agents-plugin.md @@ -1,14 +1,12 @@ # Migrating to the `agents()` plugin -The old `createAgent({ adapter, port, tools, plugins })` shortcut from the agent PR stack is deprecated. The new shape splits agent *definition* (pure data) from app *composition* (plugin registration). - -The old exports still work — they're kept side-by-side until a future removal release. This guide shows how to port code incrementally. +The old `createAgent({ adapter, port, tools, plugins })` shortcut has been removed. The new shape splits agent *definition* (pure data) from app *composition* (plugin registration). ## Name changes at a glance | Old | New | |---|---| -| `createAgent(config)` (app shortcut) | `createAgentApp(config)` (deprecated) | +| `createAgent(config)` (app shortcut) | `createApp({ plugins: [..., agents()] })` | | — | `createAgent(def)` (pure factory, **same name, new meaning**) | | `agent()` plugin | `agents()` plugin (plural) | | `tools: AgentTool[]` | `tools: Record` | @@ -215,20 +213,3 @@ await runAgent(classifier, { ``` Hosted/MCP tools are still `agents()`-only (they need the live MCP client). Raw `ToolkitEntry` spreads from `.toolkit()` can't be dispatched standalone — `runAgent` throws a clear error pointing you at `fromPlugin`. - -## Gradual migration - -Both APIs coexist. You can land the dependency bump today, keep using `createAgentApp` (the renamed old shortcut), and migrate call sites one at a time: - -```ts -import { createAgentApp, analytics, tool } from "@databricks/appkit"; - -// Old shape still works, just renamed: -createAgentApp({ plugins: [analytics()], tools: [/* ... */] }); -``` - -When you're ready, switch the import to `createApp({ plugins: [..., agents()] })` and remove the `createAgentApp` call. No other code needs to change. - -## Removal timeline - -The old `agent()` and `createAgentApp` exports remain until feedback on the new shape stabilizes. A follow-up PR will remove them in a future release; use of the deprecated exports surfaces via IDE strikethrough (JSDoc `@deprecated`) but does not log runtime warnings. diff --git a/packages/appkit/src/core/create-agent.ts b/packages/appkit/src/core/create-agent.ts deleted file mode 100644 index 9fad2547..00000000 --- a/packages/appkit/src/core/create-agent.ts +++ /dev/null @@ -1,156 +0,0 @@ -import type { WorkspaceClient } from "@databricks/sdk-experimental"; -import type { - AgentAdapter, - AgentToolDefinition, - CacheConfig, - PluginConstructor, - PluginData, -} from "shared"; -import { agent } from "../plugins/agent"; -import type { AgentTool } from "../plugins/agent/types"; -import type { FunctionTool } from "../plugins/agents/tools/function-tool"; -import { server } from "../plugins/server"; -import type { TelemetryConfig } from "../telemetry"; -import { createApp } from "./appkit"; - -export interface CreateAgentConfig { - /** Single agent adapter (mutually exclusive with `agents`). Registered as "assistant". */ - adapter?: AgentAdapter | Promise; - /** Multiple named agents (mutually exclusive with `adapter`). */ - agents?: Record>; - /** Which agent to use when the client doesn't specify one. */ - defaultAgent?: string; - /** Tool-providing plugins (analytics, files, genie, lakebase, etc.) */ - plugins?: PluginData[]; - /** Server port. Defaults to DATABRICKS_APP_PORT or 8000. */ - port?: number; - /** Server host. Defaults to FLASK_RUN_HOST or 0.0.0.0. */ - host?: string; - /** Telemetry configuration. */ - telemetry?: TelemetryConfig; - /** Cache configuration. */ - cache?: CacheConfig; - /** Explicit tools (FunctionTool, HostedTool) alongside auto-discovered ToolProvider tools. */ - tools?: AgentTool[]; - /** Pre-configured WorkspaceClient. */ - client?: WorkspaceClient; -} - -export interface AgentHandle { - /** Register an additional agent at runtime. */ - registerAgent: (name: string, adapter: AgentAdapter) => void; - /** Add function tools at runtime (HostedTools must be configured at setup). */ - addTools: (tools: FunctionTool[]) => void; - /** Get all tool definitions available to agents. */ - getTools: () => AgentToolDefinition[]; - /** List threads for a user. */ - getThreads: (userId: string) => Promise; - /** Access to user-provided plugin APIs. */ - plugins: Record; -} - -/** - * Creates an agent-powered app with batteries included. - * - * Wraps `createApp` with `server()` and `agent()` pre-configured. - * Automatically starts an HTTP server with agent chat routes. - * - * For apps that need custom routes or manual server control, - * use `createApp` with `server()` and `agent()` directly. - * - * @example Single agent - * ```ts - * import { createAgent, analytics } from "@databricks/appkit"; - * import { DatabricksAdapter } from "@databricks/appkit/agents/databricks"; - * - * createAgent({ - * plugins: [analytics()], - * adapter: DatabricksAdapter.fromServingEndpoint({ - * workspaceClient: new WorkspaceClient({}), - * endpointName: "databricks-claude-sonnet-4-5", - * systemPrompt: "You are a data assistant...", - * }), - * }).then(agent => { - * console.log("Tools:", agent.getTools()); - * }); - * ``` - * - * @example Multiple agents - * ```ts - * createAgent({ - * plugins: [analytics(), files()], - * agents: { - * assistant: DatabricksAdapter.fromServingEndpoint({ ... }), - * autocomplete: DatabricksAdapter.fromServingEndpoint({ ... }), - * }, - * defaultAgent: "assistant", - * }); - * ``` - */ -/** - * @deprecated Use `createAgent(def)` (pure factory) + `agents()` plugin + - * `createApp()` instead. The new shape separates agent *definition* from - * *app composition*. Re-exported as `createAgentApp` in the main package - * index for migration; will be removed in a future release. - */ -export async function createAgent( - config: CreateAgentConfig = {}, -): Promise { - if (config.adapter && config.agents) { - throw new Error( - "createAgent: 'adapter' and 'agents' are mutually exclusive. " + - "Use 'adapter' for a single agent or 'agents' for multiple.", - ); - } - - let agents = config.adapter ? { assistant: config.adapter } : config.agents; - - // Default: if no adapter or agents provided, use DatabricksAdapter.fromModelServing() - // which reads from DATABRICKS_AGENT_ENDPOINT env var. Config-file agents - // (from config/agents/*.md) will also be loaded during agent plugin setup. - if (!agents && !config.adapter) { - try { - const { DatabricksAdapter } = await import("../agents/databricks"); - agents = { assistant: DatabricksAdapter.fromModelServing() }; - } catch { - // No adapter available — agent plugin will rely on config files - } - } - - const appkit = await createApp({ - plugins: [ - agent({ - agents, - defaultAgent: config.defaultAgent, - tools: config.tools, - }), - ...(config.plugins ?? []), - server({ - autoStart: true, - ...(config.port !== undefined && { port: config.port }), - ...(config.host !== undefined && { host: config.host }), - }), - ], - telemetry: config.telemetry, - cache: config.cache, - client: config.client, - }); - - const agentExports = (appkit as any).agent; - const hiddenKeys = new Set(["agent", "server"]); - - const plugins: Record = {}; - for (const [key, value] of Object.entries(appkit as Record)) { - if (!hiddenKeys.has(key)) { - plugins[key] = value; - } - } - - return { - registerAgent: agentExports.registerAgent, - addTools: agentExports.addTools, - getTools: agentExports.getTools, - getThreads: agentExports.getThreads, - plugins, - }; -} diff --git a/packages/appkit/src/core/tests/create-agent.test.ts b/packages/appkit/src/core/tests/create-agent.test.ts deleted file mode 100644 index 58df743b..00000000 --- a/packages/appkit/src/core/tests/create-agent.test.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { describe, expect, test, vi } from "vitest"; - -vi.mock("../../cache", () => ({ - CacheManager: { - getInstance: vi.fn().mockResolvedValue({ - get: vi.fn(), - set: vi.fn(), - delete: vi.fn(), - getOrExecute: vi.fn(), - }), - getInstanceSync: vi.fn().mockReturnValue({ - get: vi.fn(), - set: vi.fn(), - delete: vi.fn(), - getOrExecute: vi.fn(), - }), - }, -})); - -vi.mock("../../telemetry", () => ({ - TelemetryManager: { - initialize: vi.fn(), - getProvider: vi.fn(() => ({ - getTracer: vi.fn(), - getMeter: vi.fn(), - getLogger: vi.fn(), - emit: vi.fn(), - startActiveSpan: vi.fn(), - registerInstrumentations: vi.fn(), - })), - }, - normalizeTelemetryOptions: vi.fn(() => ({ - traces: false, - metrics: false, - logs: false, - })), -})); - -vi.mock("../../context/service-context", () => { - const mockClient = { - statementExecution: { executeStatement: vi.fn() }, - currentUser: { me: vi.fn().mockResolvedValue({ id: "test-user" }) }, - config: { host: "https://test.databricks.com" }, - }; - - return { - ServiceContext: { - initialize: vi.fn().mockResolvedValue({ - client: mockClient, - serviceUserId: "test-service-user", - workspaceId: Promise.resolve("test-workspace"), - }), - get: vi.fn().mockReturnValue({ - client: mockClient, - serviceUserId: "test-service-user", - workspaceId: Promise.resolve("test-workspace"), - }), - isInitialized: vi.fn().mockReturnValue(true), - createUserContext: vi.fn(), - }, - }; -}); - -vi.mock("../../registry", () => ({ - ResourceRegistry: vi.fn().mockImplementation(() => ({ - collectResources: vi.fn(), - getRequired: vi.fn().mockReturnValue([]), - enforceValidation: vi.fn(), - })), - ResourceType: { SQL_WAREHOUSE: "sql_warehouse" }, - getPluginManifest: vi.fn(), - getResourceRequirements: vi.fn(), -})); - -// Mock server plugin to avoid actually starting a server -vi.mock("../../plugins/server", () => { - const manifest = { - name: "server", - displayName: "Server", - description: "Server", - resources: { required: [], optional: [] }, - }; - - class MockServerPlugin { - static manifest = manifest; - static phase = "deferred"; - static DEFAULT_CONFIG = {}; - name = "server"; - config: any; - constructor(config: any) { - this.config = config; - } - async setup() {} - injectRoutes() {} - getEndpoints() { - return {}; - } - exports() { - return { - start: vi.fn(), - extend: vi.fn(), - getServer: vi.fn(), - getConfig: vi.fn(() => this.config), - }; - } - } - - return { - server: (config: any = {}) => ({ - plugin: MockServerPlugin, - config, - name: "server", - }), - ServerPlugin: MockServerPlugin, - }; -}); - -import type { AgentAdapter, AgentEvent } from "shared"; -import { createAgent } from "../create-agent"; - -function createMockAdapter(): AgentAdapter { - return { - async *run(): AsyncGenerator { - yield { type: "message_delta", content: "hello" }; - }, - }; -} - -describe("createAgent", () => { - test("returns an AgentHandle with registerAgent, getTools, getThreads", async () => { - const handle = await createAgent({ - adapter: createMockAdapter(), - }); - - expect(handle.registerAgent).toBeTypeOf("function"); - expect(handle.getTools).toBeTypeOf("function"); - expect(handle.getThreads).toBeTypeOf("function"); - expect(handle.plugins).toBeDefined(); - }); - - test("adapter shorthand registers as 'assistant'", async () => { - const handle = await createAgent({ - adapter: createMockAdapter(), - }); - - const tools = handle.getTools(); - expect(tools).toBeInstanceOf(Array); - }); - - test("agents record is passed through", async () => { - const handle = await createAgent({ - agents: { - main: createMockAdapter(), - secondary: createMockAdapter(), - }, - defaultAgent: "main", - }); - - expect(handle.getTools).toBeTypeOf("function"); - }); - - test("throws when both adapter and agents are provided", async () => { - await expect( - createAgent({ - adapter: createMockAdapter(), - agents: { other: createMockAdapter() }, - }), - ).rejects.toThrow("mutually exclusive"); - }); - - test("plugins namespace excludes agent and server", async () => { - const handle = await createAgent({ - adapter: createMockAdapter(), - }); - - expect(handle.plugins).not.toHaveProperty("agent"); - expect(handle.plugins).not.toHaveProperty("server"); - }); - - test("accepts port and host config", async () => { - const handle = await createAgent({ - adapter: createMockAdapter(), - port: 9000, - host: "127.0.0.1", - }); - - expect(handle).toBeDefined(); - }); - - test("works with promised adapters", async () => { - const handle = await createAgent({ - adapter: Promise.resolve(createMockAdapter()), - }); - - expect(handle.registerAgent).toBeTypeOf("function"); - }); - - test("registerAgent allows adding agents after creation", async () => { - const handle = await createAgent({ - adapter: createMockAdapter(), - }); - - handle.registerAgent("second", createMockAdapter()); - expect(handle.getTools).toBeTypeOf("function"); - }); - - test("tools config is forwarded to agent plugin", async () => { - const handle = await createAgent({ - adapter: createMockAdapter(), - tools: [ - { - type: "function" as const, - name: "greet", - description: "Say hello", - parameters: { type: "object", properties: {} }, - execute: async () => "hello", - }, - ], - }); - - const tools = handle.getTools(); - expect(tools.some((t) => t.name === "greet")).toBe(true); - }); - - test("addTools is exposed on AgentHandle", async () => { - const handle = await createAgent({ - adapter: createMockAdapter(), - }); - - expect(handle.addTools).toBeTypeOf("function"); - - handle.addTools([ - { - type: "function" as const, - name: "farewell", - execute: async () => "bye", - }, - ]); - - const tools = handle.getTools(); - expect(tools.some((t) => t.name === "farewell")).toBe(true); - }); -}); diff --git a/packages/appkit/src/index.ts b/packages/appkit/src/index.ts index f78df631..eca79f84 100644 --- a/packages/appkit/src/index.ts +++ b/packages/appkit/src/index.ts @@ -43,16 +43,6 @@ export { } from "./connectors/lakebase"; export { getExecutionContext } from "./context"; export { createApp } from "./core"; -export type { AgentHandle, CreateAgentConfig } from "./core/create-agent"; -/** - * @deprecated Use `createAgent(def)` (pure factory) together with the - * `agents()` plugin and `createApp`. This shortcut composes server + - * agent plugins in a single call; the new shape separates those concerns. - * Import path preserved for backward compatibility during migration. - */ -export { createAgent as createAgentApp } from "./core/create-agent"; -// New pure-data agent factory (replaces the old createAgent shortcut once -// callers migrate — they coexist during the deprecation window). export { createAgent } from "./core/create-agent-def"; export { type RunAgentInput, @@ -79,8 +69,6 @@ export { toPlugin, } from "./plugin"; export { - /** @deprecated Use `agents()` (plural) instead. Kept for migration. */ - agent, analytics, files, genie, diff --git a/packages/appkit/src/plugins/agent/agent.ts b/packages/appkit/src/plugins/agent/agent.ts deleted file mode 100644 index 295541be..00000000 --- a/packages/appkit/src/plugins/agent/agent.ts +++ /dev/null @@ -1,764 +0,0 @@ -import { randomUUID } from "node:crypto"; -import path from "node:path"; -import type express from "express"; -import pc from "picocolors"; -import type { - AgentAdapter, - AgentToolDefinition, - IAppRouter, - Message, - PluginPhase, - ResponseStreamEvent, -} from "shared"; -import { createLogger } from "../../logging/logger"; -import { Plugin, toPlugin } from "../../plugin"; -import type { PluginManifest } from "../../registry"; -import { agentStreamDefaults } from "../agents/defaults"; -import { AgentEventTranslator } from "../agents/event-translator"; -import { chatRequestSchema, invocationsRequestSchema } from "../agents/schemas"; -import { - buildBaseSystemPrompt, - composeSystemPrompt, -} from "../agents/system-prompt"; -import { InMemoryThreadStore } from "../agents/thread-store"; -import { - AppKitMcpClient, - type FunctionTool, - functionToolToDefinition, - isFunctionTool, - isHostedTool, - resolveHostedTools, -} from "../agents/tools"; -import { loadAgentConfigs } from "./config-loader"; -import manifest from "./manifest.json"; -import type { AgentPluginConfig, RegisteredAgent, ToolEntry } from "./types"; - -const logger = createLogger("agent"); - -export class AgentPlugin extends Plugin { - static manifest = manifest as PluginManifest<"agent">; - static phase: PluginPhase = "deferred"; - - protected declare config: AgentPluginConfig; - - private agents = new Map(); - private defaultAgentName: string | null = null; - private toolIndex = new Map(); - private threadStore; - private activeStreams = new Map(); - private mcpClient: AppKitMcpClient | null = null; - - constructor(config: AgentPluginConfig) { - super(config); - this.config = config; - this.threadStore = config.threadStore ?? new InMemoryThreadStore(); - } - - async setup() { - await this.collectTools(); - await this.loadAgents(); - this.mountInvocationsRoute(); - } - - private async loadAgents() { - // 1. Load config-file agents first - const agentsDir = - this.config.agentsDir ?? path.join(process.cwd(), "config/agents"); - const fileConfigs = loadAgentConfigs(agentsDir); - - for (const fc of fileConfigs) { - try { - const { DatabricksAdapter } = await import("../../agents/databricks"); - const adapter = await DatabricksAdapter.fromModelServing(fc.endpoint, { - maxSteps: fc.maxSteps, - maxTokens: fc.maxTokens, - }); - this.agents.set(fc.name, { - name: fc.name, - adapter, - systemPrompt: fc.systemPrompt || undefined, - }); - if (fc.default && !this.defaultAgentName) { - this.defaultAgentName = fc.name; - } - if (!this.defaultAgentName) { - this.defaultAgentName = fc.name; - } - } catch (error) { - logger.error( - "Failed to create agent '%s' from config: %O", - fc.name, - error, - ); - } - } - - // 2. Code-defined agents override config-file agents per-name - if (this.config.agents) { - const entries = Object.entries(this.config.agents); - for (const [name, entry] of entries) { - if ( - this.agents.has(name) && - fileConfigs.some((fc) => fc.name === name) - ) { - logger.warn( - "Agent '%s' defined in both code and config file. Code takes precedence.", - name, - ); - } - - const { adapter, systemPrompt } = await this.resolveAgentEntry(entry); - this.agents.set(name, { name, adapter, systemPrompt }); - if (!this.defaultAgentName) { - this.defaultAgentName = name; - } - } - } - - if (this.config.defaultAgent) { - this.defaultAgentName = this.config.defaultAgent; - } - - if (fileConfigs.length > 0) { - logger.info( - "Loaded %d agent(s) from config files: %s", - fileConfigs.length, - fileConfigs.map((c) => c.name).join(", "), - ); - } - } - - private async resolveAgentEntry( - entry: import("./types").AgentEntry, - ): Promise<{ adapter: AgentAdapter; systemPrompt?: string }> { - if (this.isAgentDefinition(entry)) { - const adapter = await entry.adapter; - return { adapter, systemPrompt: entry.systemPrompt }; - } - const adapter = await (entry as AgentAdapter | Promise); - return { adapter }; - } - - private isAgentDefinition( - entry: unknown, - ): entry is import("./types").AgentDefinition { - return typeof entry === "object" && entry !== null && "adapter" in entry; - } - - async reloadAgents() { - this.agents.clear(); - this.defaultAgentName = null; - await this.loadAgents(); - } - - private mountInvocationsRoute() { - if (!this.context) return; - - this.context.addRoute( - "post", - "/invocations", - (req: express.Request, res: express.Response) => { - this._handleInvocations(req, res); - }, - ); - - logger.info("Registered POST /invocations route via PluginContext"); - } - - private async collectTools() { - // 1. Auto-discover from sibling ToolProvider plugins via PluginContext - if (this.context) { - for (const { - name: pluginName, - provider, - } of this.context.getToolProviders()) { - const tools = provider.getAgentTools(); - for (const tool of tools) { - const qualifiedName = `${pluginName}.${tool.name}`; - this.toolIndex.set(qualifiedName, { - source: "plugin", - pluginName, - def: { ...tool, name: qualifiedName }, - localName: tool.name, - }); - } - - logger.info( - "Collected %d tools from plugin %s", - tools.length, - pluginName, - ); - } - } - - // 2. Process explicit tools from config - if (this.config.tools) { - const hostedTools = this.config.tools.filter(isHostedTool); - const functionTools = this.config.tools.filter(isFunctionTool); - - // 2a. Resolve HostedTools via MCP client - if (hostedTools.length > 0) { - await this.connectHostedTools(hostedTools); - } - - // 2b. Add FunctionTools - for (const ft of functionTools) { - this.addFunctionToolToIndex(ft); - } - } - - this.printTools(); - } - - private printTools() { - const entries = Array.from(this.toolIndex.values()); - if (entries.length === 0) return; - - const SOURCE_COLORS: Record string> = { - plugin: pc.blue, - function: pc.yellow, - mcp: pc.magenta, - }; - - const rows = entries - .map((e) => ({ - source: e.source, - name: e.def.name, - description: e.def.description.slice(0, 60), - })) - .sort( - (a, b) => - a.source.localeCompare(b.source) || a.name.localeCompare(b.name), - ); - - const maxSourceLen = Math.max(...rows.map((r) => r.source.length)); - const maxNameLen = Math.min( - 40, - Math.max(...rows.map((r) => r.name.length)), - ); - const separator = pc.dim("─".repeat(60)); - - console.log(""); - console.log(` ${pc.bold("Agent Tools")} ${pc.dim(`(${rows.length})`)}`); - console.log(` ${separator}`); - - for (const { source, name, description } of rows) { - const colorize = SOURCE_COLORS[source] ?? pc.white; - const sourceStr = colorize(pc.bold(source.padEnd(maxSourceLen))); - const nameStr = - name.length > maxNameLen - ? `${name.slice(0, maxNameLen - 1)}…` - : name.padEnd(maxNameLen); - console.log(` ${sourceStr} ${nameStr} ${pc.dim(description)}`); - } - - console.log(` ${separator}`); - console.log(""); - } - - private async connectHostedTools( - hostedTools: import("../agents/tools/hosted-tools").HostedTool[], - ) { - let host: string | undefined; - let authenticate: () => Promise>; - - try { - const { getWorkspaceClient } = await import("../../context"); - const wsClient = getWorkspaceClient(); - await wsClient.config.ensureResolved(); - host = wsClient.config.host; - authenticate = async (): Promise> => { - const headers = new Headers(); - await wsClient.config.authenticate(headers); - return Object.fromEntries(headers.entries()); - }; - } catch { - host = process.env.DATABRICKS_HOST; - authenticate = async (): Promise> => { - const token = process.env.DATABRICKS_TOKEN; - if (token) return { Authorization: `Bearer ${token}` }; - return {}; - }; - } - - if (!host) { - logger.warn( - "No Databricks host available — skipping %d hosted tools", - hostedTools.length, - ); - return; - } - - this.mcpClient = new AppKitMcpClient(host, authenticate); - - const endpoints = resolveHostedTools(hostedTools); - await this.mcpClient.connectAll(endpoints); - - for (const def of this.mcpClient.getAllToolDefinitions()) { - this.toolIndex.set(def.name, { - source: "mcp", - mcpToolName: def.name, - def, - }); - } - - logger.info( - "Connected %d MCP tools from %d hosted tool(s)", - this.mcpClient.getAllToolDefinitions().length, - hostedTools.length, - ); - } - - private addFunctionToolToIndex(ft: FunctionTool) { - const def = functionToolToDefinition(ft); - this.toolIndex.set(ft.name, { - source: "function", - functionTool: ft, - def, - }); - } - - addTools(tools: FunctionTool[]) { - for (const ft of tools) { - this.addFunctionToolToIndex(ft); - } - logger.info( - "Added %d function tools, total: %d", - tools.length, - this.toolIndex.size, - ); - } - - injectRoutes(router: IAppRouter) { - this.route(router, { - name: "chat", - method: "post", - path: "/chat", - handler: async (req, res) => this._handleChat(req, res), - }); - - this.route(router, { - name: "cancel", - method: "post", - path: "/cancel", - handler: async (req, res) => this._handleCancel(req, res), - }); - - this.route(router, { - name: "threads", - method: "get", - path: "/threads", - handler: async (req, res) => this._handleListThreads(req, res), - }); - - this.route(router, { - name: "thread", - method: "get", - path: "/threads/:threadId", - handler: async (req, res) => this._handleGetThread(req, res), - }); - - this.route(router, { - name: "deleteThread", - method: "delete", - path: "/threads/:threadId", - handler: async (req, res) => this._handleDeleteThread(req, res), - }); - - this.route(router, { - name: "info", - method: "get", - path: "/info", - handler: async (_req, res) => { - res.json({ - toolCount: this.toolIndex.size, - tools: this.getAllToolDefinitions(), - agents: Array.from(this.agents.keys()), - defaultAgent: this.defaultAgentName, - }); - }, - }); - } - - clientConfig(): Record { - return { - tools: this.getAllToolDefinitions(), - agents: Array.from(this.agents.keys()), - defaultAgent: this.defaultAgentName, - }; - } - - private async _handleChat( - req: express.Request, - res: express.Response, - ): Promise { - const parsed = chatRequestSchema.safeParse(req.body); - if (!parsed.success) { - res.status(400).json({ - error: "Invalid request", - details: parsed.error.flatten().fieldErrors, - }); - return; - } - - const { message, threadId, agent: agentName } = parsed.data; - - const resolvedAgent = this.resolveAgent(agentName); - if (!resolvedAgent) { - res.status(400).json({ - error: agentName - ? `Agent "${agentName}" not found` - : "No agent registered", - }); - return; - } - - const userId = this.resolveUserId(req); - - let thread = threadId ? await this.threadStore.get(threadId, userId) : null; - - if (threadId && !thread) { - res.status(404).json({ error: `Thread ${threadId} not found` }); - return; - } - - if (!thread) { - thread = await this.threadStore.create(userId); - } - - const userMessage: Message = { - id: randomUUID(), - role: "user", - content: message, - createdAt: new Date(), - }; - await this.threadStore.addMessage(thread.id, userId, userMessage); - - return this._streamChat(req, res, resolvedAgent, thread, userId); - } - - private async _streamChat( - req: express.Request, - res: express.Response, - resolvedAgent: RegisteredAgent, - thread: import("shared").Thread, - userId: string, - ): Promise { - const tools = this.getAllToolDefinitions(); - const abortController = new AbortController(); - const signal = abortController.signal; - - const self = this; - const executeTool = async ( - qualifiedName: string, - args: unknown, - ): Promise => { - const entry = self.toolIndex.get(qualifiedName); - if (!entry) throw new Error(`Unknown tool: ${qualifiedName}`); - - let result: unknown; - - if (entry.source === "plugin" && self.context) { - result = await self.context.executeTool( - req, - entry.pluginName, - entry.localName, - args, - signal, - ); - } else { - result = await self.execute( - async (_execSignal) => { - switch (entry.source) { - case "plugin": - throw new Error("Plugin tool execution requires PluginContext"); - case "function": - return entry.functionTool.execute( - args as Record, - ); - case "mcp": { - if (!self.mcpClient) { - throw new Error("MCP client not connected"); - } - const oboToken = req.headers["x-forwarded-access-token"]; - const mcpAuth = - typeof oboToken === "string" - ? { Authorization: `Bearer ${oboToken}` } - : undefined; - return self.mcpClient.callTool( - entry.mcpToolName, - args, - mcpAuth, - ); - } - } - }, - { - default: { - telemetryInterceptor: { enabled: true }, - timeout: 30_000, - }, - }, - ); - } - - if (result === undefined) { - return `Error: Tool "${qualifiedName}" execution failed`; - } - - const MAX_TOOL_RESULT_CHARS = 50_000; - const serialized = - typeof result === "string" ? result : JSON.stringify(result); - if (serialized.length > MAX_TOOL_RESULT_CHARS) { - return `${serialized.slice(0, MAX_TOOL_RESULT_CHARS)}\n\n[Result truncated: ${serialized.length} chars exceeds ${MAX_TOOL_RESULT_CHARS} limit]`; - } - return result; - }; - - const requestId = randomUUID(); - this.activeStreams.set(requestId, abortController); - - await this.executeStream( - res, - async function* () { - const translator = new AgentEventTranslator(); - try { - for (const evt of translator.translate({ - type: "metadata", - data: { threadId: thread.id }, - })) { - yield evt; - } - - const pluginNames = self.context - ? self.context - .getPluginNames() - .filter((n) => n !== "agent" && n !== "server") - : []; - const basePrompt = buildBaseSystemPrompt(pluginNames); - const fullPrompt = composeSystemPrompt( - basePrompt, - resolvedAgent.systemPrompt, - ); - - const messagesWithSystem: Message[] = [ - { - id: "system", - role: "system", - content: fullPrompt, - createdAt: new Date(), - }, - ...thread.messages, - ]; - - const stream = resolvedAgent.adapter.run( - { - messages: messagesWithSystem, - tools, - threadId: thread.id, - signal, - }, - { executeTool, signal }, - ); - - let fullContent = ""; - - for await (const event of stream) { - if (signal.aborted) break; - - if (event.type === "message_delta") { - fullContent += event.content; - } - - for (const translated of translator.translate(event)) { - yield translated; - } - } - - if (fullContent) { - const assistantMessage: Message = { - id: randomUUID(), - role: "assistant", - content: fullContent, - createdAt: new Date(), - }; - await self.threadStore.addMessage( - thread.id, - userId, - assistantMessage, - ); - } - - for (const evt of translator.finalize()) { - yield evt; - } - } catch (error) { - if (signal.aborted) return; - logger.error("Agent chat error: %O", error); - throw error; - } finally { - self.activeStreams.delete(requestId); - } - }, - { - ...agentStreamDefaults, - stream: { - ...agentStreamDefaults.stream, - streamId: requestId, - }, - }, - ); - } - - private async _handleInvocations( - req: express.Request, - res: express.Response, - ): Promise { - const parsed = invocationsRequestSchema.safeParse(req.body); - if (!parsed.success) { - res.status(400).json({ - error: "Invalid request", - details: parsed.error.flatten().fieldErrors, - }); - return; - } - - const { input } = parsed.data; - const resolvedAgent = this.resolveAgent(); - if (!resolvedAgent) { - res.status(400).json({ error: "No agent registered" }); - return; - } - - const userId = this.resolveUserId(req); - const thread = await this.threadStore.create(userId); - - if (typeof input === "string") { - const msg: Message = { - id: randomUUID(), - role: "user", - content: input, - createdAt: new Date(), - }; - await this.threadStore.addMessage(thread.id, userId, msg); - } else { - for (const item of input) { - const role = item.role ?? "user"; - const content = - typeof item.content === "string" - ? item.content - : JSON.stringify(item.content ?? ""); - if (!content) continue; - const msg: Message = { - id: randomUUID(), - role: role as Message["role"], - content, - createdAt: new Date(), - }; - await this.threadStore.addMessage(thread.id, userId, msg); - } - } - - return this._streamChat(req, res, resolvedAgent, thread, userId); - } - - private async _handleCancel( - req: express.Request, - res: express.Response, - ): Promise { - const { streamId } = req.body as { streamId?: string }; - if (!streamId) { - res.status(400).json({ error: "streamId is required" }); - return; - } - const controller = this.activeStreams.get(streamId); - if (controller) { - controller.abort("Cancelled by user"); - this.activeStreams.delete(streamId); - } - res.json({ cancelled: true }); - } - - private async _handleListThreads( - req: express.Request, - res: express.Response, - ): Promise { - const userId = this.resolveUserId(req); - const threads = await this.threadStore.list(userId); - res.json({ threads }); - } - - private async _handleGetThread( - req: express.Request, - res: express.Response, - ): Promise { - const userId = this.resolveUserId(req); - const thread = await this.threadStore.get(req.params.threadId, userId); - if (!thread) { - res.status(404).json({ error: "Thread not found" }); - return; - } - res.json(thread); - } - - private async _handleDeleteThread( - req: express.Request, - res: express.Response, - ): Promise { - const userId = this.resolveUserId(req); - const deleted = await this.threadStore.delete(req.params.threadId, userId); - if (!deleted) { - res.status(404).json({ error: "Thread not found" }); - return; - } - res.json({ deleted: true }); - } - - private resolveAgent(name?: string): RegisteredAgent | null { - if (name) return this.agents.get(name) ?? null; - if (this.defaultAgentName) { - return this.agents.get(this.defaultAgentName) ?? null; - } - const first = this.agents.values().next(); - return first.done ? null : first.value; - } - - private getAllToolDefinitions(): AgentToolDefinition[] { - return Array.from(this.toolIndex.values()).map((e) => e.def); - } - - async shutdown() { - if (this.mcpClient) { - await this.mcpClient.close(); - this.mcpClient = null; - } - } - - exports() { - return { - registerAgent: (name: string, adapter: AgentAdapter) => { - this.agents.set(name, { name, adapter }); - if (!this.defaultAgentName) { - this.defaultAgentName = name; - } - }, - addTools: (tools: FunctionTool[]) => this.addTools(tools), - getTools: () => this.getAllToolDefinitions(), - getThreads: (userId: string) => this.threadStore.list(userId), - getAgents: () => ({ - agents: Array.from(this.agents.keys()), - default: this.defaultAgentName, - }), - reloadAgents: () => this.reloadAgents(), - }; - } -} - -/** - * @internal - * @deprecated Use `agents()` (plural) from `@databricks/appkit` instead. The - * new plugin supports per-agent tool indexes, the keyed `tools: { ... }` - * record shape, and the `createAgent(def)` pure factory. This export stays - * for migration; it will be removed in a future release. - */ -export const agent = toPlugin(AgentPlugin); diff --git a/packages/appkit/src/plugins/agent/config-loader.ts b/packages/appkit/src/plugins/agent/config-loader.ts deleted file mode 100644 index c9ae512a..00000000 --- a/packages/appkit/src/plugins/agent/config-loader.ts +++ /dev/null @@ -1,94 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import { createLogger } from "../../logging/logger"; - -const logger = createLogger("agent:config"); - -export interface AgentFileConfig { - name: string; - endpoint?: string; - maxSteps?: number; - maxTokens?: number; - default?: boolean; - systemPrompt: string; -} - -/** - * Parse a frontmatter markdown string into data + content. - * Handles flat YAML key-value pairs (string, number, boolean). - */ -export function parseFrontmatter(raw: string): { - data: Record; - content: string; -} { - const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/); - if (!match) return { data: {}, content: raw.trim() }; - - const data: Record = {}; - for (const line of match[1].split("\n")) { - const colonIdx = line.indexOf(":"); - if (colonIdx === -1) continue; - const key = line.slice(0, colonIdx).trim(); - const rawVal = line.slice(colonIdx + 1).trim(); - if (!key) continue; - - if (rawVal === "true") data[key] = true; - else if (rawVal === "false") data[key] = false; - else if (/^\d+$/.test(rawVal)) data[key] = Number(rawVal); - else if (/^\d+\.\d+$/.test(rawVal)) data[key] = Number(rawVal); - else data[key] = rawVal; - } - - return { data, content: match[2].trim() }; -} - -/** - * Load agent configs from a directory of frontmatter markdown files. - * Returns an empty array if the directory doesn't exist. - */ -export function loadAgentConfigs(agentsDir: string): AgentFileConfig[] { - if (!fs.existsSync(agentsDir)) return []; - - const files = fs.readdirSync(agentsDir).filter((f) => f.endsWith(".md")); - const configs: AgentFileConfig[] = []; - - for (const file of files) { - try { - const raw = fs.readFileSync(path.join(agentsDir, file), "utf-8"); - const { data, content } = parseFrontmatter(raw); - const name = path.basename(file, ".md"); - - const config: AgentFileConfig = { - name, - systemPrompt: content, - }; - - if (typeof data.endpoint === "string") config.endpoint = data.endpoint; - if (typeof data.maxSteps === "number") config.maxSteps = data.maxSteps; - if (typeof data.maxTokens === "number") config.maxTokens = data.maxTokens; - if (typeof data.default === "boolean") config.default = data.default; - - if (data.maxSteps !== undefined && typeof data.maxSteps !== "number") { - logger.warn( - "Agent '%s': maxSteps should be a number, got %s. Using default.", - name, - typeof data.maxSteps, - ); - } - - if (data.maxTokens !== undefined && typeof data.maxTokens !== "number") { - logger.warn( - "Agent '%s': maxTokens should be a number, got %s. Using default.", - name, - typeof data.maxTokens, - ); - } - - configs.push(config); - } catch (error) { - logger.error("Failed to load agent config '%s': %O", file, error); - } - } - - return configs; -} diff --git a/packages/appkit/src/plugins/agent/index.ts b/packages/appkit/src/plugins/agent/index.ts deleted file mode 100644 index 861a68cc..00000000 --- a/packages/appkit/src/plugins/agent/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { agent } from "./agent"; diff --git a/packages/appkit/src/plugins/agent/manifest.json b/packages/appkit/src/plugins/agent/manifest.json deleted file mode 100644 index d73b94ea..00000000 --- a/packages/appkit/src/plugins/agent/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "https://databricks.github.io/appkit/schemas/plugin-manifest.schema.json", - "name": "agent", - "displayName": "Agent Plugin", - "description": "Framework-agnostic AI agent with auto-tool-discovery from all registered plugins", - "resources": { - "required": [], - "optional": [] - } -} diff --git a/packages/appkit/src/plugins/agent/tests/agent.test.ts b/packages/appkit/src/plugins/agent/tests/agent.test.ts deleted file mode 100644 index 128e6157..00000000 --- a/packages/appkit/src/plugins/agent/tests/agent.test.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { - createMockRequest, - createMockResponse, - createMockRouter, - setupDatabricksEnv, -} from "@tools/test-helpers"; -import type { - AgentAdapter, - AgentEvent, - AgentInput, - AgentRunContext, - AgentToolDefinition, - ToolProvider, -} from "shared"; -import { beforeEach, describe, expect, test, vi } from "vitest"; -import { PluginContext } from "../../../core/plugin-context"; -import { AgentPlugin } from "../agent"; - -vi.mock("../../../cache", () => ({ - CacheManager: { - getInstanceSync: vi.fn(() => ({ - get: vi.fn(), - set: vi.fn(), - delete: vi.fn(), - getOrExecute: vi.fn(), - generateKey: vi.fn(), - })), - }, -})); - -vi.mock("../../../context", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - getCurrentUserId: vi.fn(() => "test-user"), - getExecutionContext: vi.fn(() => ({ - userId: "test-user", - isUserContext: false, - })), - }; -}); - -vi.mock("../../../telemetry", () => ({ - TelemetryManager: { - getProvider: vi.fn(() => ({ - getTracer: vi.fn(() => ({ - startActiveSpan: (_name: string, fn: (span: any) => any) => { - const span = { - setStatus: vi.fn(), - recordException: vi.fn(), - end: vi.fn(), - }; - return fn(span); - }, - })), - getMeter: vi.fn(), - getLogger: vi.fn(), - emit: vi.fn(), - startActiveSpan: vi.fn(), - registerInstrumentations: vi.fn(), - })), - }, - normalizeTelemetryOptions: vi.fn(() => ({ - traces: false, - metrics: false, - logs: false, - })), -})); - -function createMockToolProvider( - tools: AgentToolDefinition[], -): ToolProvider & { asUser: any } { - return { - name: "mock-plugin", - getAgentTools: () => tools, - executeAgentTool: vi.fn().mockResolvedValue({ result: "ok" }), - asUser: vi.fn().mockReturnThis(), - } as any; -} - -function createMockContext( - providers: Array<{ - name: string; - provider: ToolProvider & { asUser: any }; - }> = [], -): PluginContext { - const ctx = new PluginContext(); - for (const { name, provider } of providers) { - ctx.registerToolProvider(name, provider as any); - ctx.registerPlugin(name, provider as any); - } - return ctx; -} - -async function* mockAdapterRun(): AsyncGenerator { - yield { type: "message_delta", content: "Hello " }; - yield { type: "message_delta", content: "world" }; -} - -function createMockAdapter(): AgentAdapter { - return { - run: vi.fn().mockReturnValue(mockAdapterRun()), - }; -} - -describe("AgentPlugin", () => { - beforeEach(() => { - setupDatabricksEnv(); - }); - - test("collectTools discovers ToolProvider plugins via context", async () => { - const mockProvider = createMockToolProvider([ - { - name: "query", - description: "Run a query", - parameters: { type: "object", properties: {} }, - }, - ]); - - const context = createMockContext([ - { name: "analytics", provider: mockProvider }, - ]); - - const plugin = new AgentPlugin({ - name: "agent", - context, - } as any); - - await plugin.setup(); - - const exports = plugin.exports(); - const tools = exports.getTools(); - - expect(tools).toHaveLength(1); - expect(tools[0].name).toBe("analytics.query"); - }); - - test("works with no context (backward compat)", async () => { - const plugin = new AgentPlugin({ - name: "agent", - }); - - await plugin.setup(); - const tools = plugin.exports().getTools(); - expect(tools).toEqual([]); - }); - - test("registerAgent and resolveAgent", () => { - const plugin = new AgentPlugin({ name: "agent" }); - const adapter = createMockAdapter(); - - plugin.exports().registerAgent("assistant", adapter); - - const tools = plugin.exports().getTools(); - expect(tools).toEqual([]); - }); - - test("injectRoutes registers chat, cancel, and thread routes", () => { - const plugin = new AgentPlugin({ name: "agent" }); - const { router, handlers } = createMockRouter(); - - plugin.injectRoutes(router); - - expect(handlers["POST:/chat"]).toBeDefined(); - expect(handlers["POST:/cancel"]).toBeDefined(); - expect(handlers["GET:/threads"]).toBeDefined(); - expect(handlers["GET:/threads/:threadId"]).toBeDefined(); - expect(handlers["DELETE:/threads/:threadId"]).toBeDefined(); - }); - - test("clientConfig exposes tools and agents", async () => { - const plugin = new AgentPlugin({ - name: "agent", - agents: { assistant: createMockAdapter() }, - }); - await plugin.setup(); - - const config = plugin.clientConfig(); - expect(config.tools).toEqual([]); - expect(config.agents).toEqual(["assistant"]); - expect(config.defaultAgent).toBe("assistant"); - }); - - test("exports().addTools adds function tools", () => { - const plugin = new AgentPlugin({ name: "agent" }); - - plugin.exports().addTools([ - { - type: "function" as const, - name: "myTool", - description: "A custom tool", - parameters: { type: "object", properties: {} }, - execute: async () => "result", - }, - ]); - - const tools = plugin.exports().getTools(); - expect(tools).toHaveLength(1); - expect(tools[0].name).toBe("myTool"); - }); - - test("mountInvocationsRoute registers via context.addRoute", async () => { - const context = createMockContext(); - const addRouteSpy = vi.spyOn(context, "addRoute"); - - const plugin = new AgentPlugin({ - name: "agent", - agents: { assistant: createMockAdapter() }, - context, - } as any); - - await plugin.setup(); - - expect(addRouteSpy).toHaveBeenCalledWith( - "post", - "/invocations", - expect.any(Function), - ); - }); - - test("executeTool calls context.executeTool for plugin tools", async () => { - const mockProvider = createMockToolProvider([ - { - name: "action", - description: "An action", - parameters: { type: "object", properties: {} }, - }, - ]); - - const context = createMockContext([ - { name: "testplugin", provider: mockProvider }, - ]); - const executeToolSpy = vi.spyOn(context, "executeTool"); - - function createToolCallingAdapter(): AgentAdapter { - return { - async *run( - _input: AgentInput, - context: AgentRunContext, - ): AsyncGenerator { - await context.executeTool("testplugin.action", {}); - yield { type: "message_delta", content: "done" }; - }, - }; - } - - const plugin = new AgentPlugin({ - name: "agent", - agents: { assistant: createToolCallingAdapter() }, - context, - } as any); - await plugin.setup(); - - const { router, getHandler } = createMockRouter(); - plugin.injectRoutes(router); - const handler = getHandler("POST", "/chat"); - - const req = createMockRequest({ - body: { message: "hi" }, - headers: { - "x-forwarded-user": "test-user", - "x-forwarded-access-token": "test-token", - }, - }); - const res = createMockResponse(); - - await handler(req, res); - - expect(executeToolSpy).toHaveBeenCalledWith( - req, - "testplugin", - "action", - {}, - expect.anything(), - ); - }); -}); diff --git a/packages/appkit/src/plugins/agent/tests/config-loader.test.ts b/packages/appkit/src/plugins/agent/tests/config-loader.test.ts deleted file mode 100644 index 19b69bdb..00000000 --- a/packages/appkit/src/plugins/agent/tests/config-loader.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import { describe, expect, test, vi } from "vitest"; -import { loadAgentConfigs, parseFrontmatter } from "../config-loader"; - -vi.mock("../../../logging/logger", () => ({ - createLogger: () => ({ - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - debug: vi.fn(), - }), -})); - -describe("parseFrontmatter", () => { - test("parses frontmatter + body", () => { - const result = parseFrontmatter( - "---\nendpoint: my-model\nmaxSteps: 10\n---\nYou are helpful.", - ); - expect(result.data).toEqual({ endpoint: "my-model", maxSteps: 10 }); - expect(result.content).toBe("You are helpful."); - }); - - test("returns full content when no frontmatter", () => { - const result = parseFrontmatter("Just a plain prompt."); - expect(result.data).toEqual({}); - expect(result.content).toBe("Just a plain prompt."); - }); - - test("parses boolean values", () => { - const result = parseFrontmatter("---\ndefault: true\n---\nPrompt."); - expect(result.data.default).toBe(true); - }); - - test("parses numeric values", () => { - const result = parseFrontmatter( - "---\nmaxSteps: 5\nmaxTokens: 2048\n---\nPrompt.", - ); - expect(result.data.maxSteps).toBe(5); - expect(result.data.maxTokens).toBe(2048); - }); - - test("handles empty body", () => { - const result = parseFrontmatter("---\nendpoint: model\n---\n"); - expect(result.data.endpoint).toBe("model"); - expect(result.content).toBe(""); - }); - - test("handles colons in values", () => { - const result = parseFrontmatter( - "---\nendpoint: https://host:443/path\n---\nPrompt.", - ); - expect(result.data.endpoint).toBe("https://host:443/path"); - }); -}); - -describe("loadAgentConfigs", () => { - test("returns empty array for non-existent directory", () => { - const result = loadAgentConfigs("/nonexistent/path"); - expect(result).toEqual([]); - }); - - test("parses .md files from directory", () => { - const tmpDir = fs.mkdtempSync(path.join(import.meta.dirname, "tmp-")); - - try { - fs.writeFileSync( - path.join(tmpDir, "assistant.md"), - "---\nendpoint: claude\ndefault: true\n---\nYou are helpful.", - ); - fs.writeFileSync( - path.join(tmpDir, "autocomplete.md"), - "---\nendpoint: gemini\nmaxSteps: 1\n---\nJust continue.", - ); - - const configs = loadAgentConfigs(tmpDir); - - expect(configs).toHaveLength(2); - - const assistant = configs.find((c) => c.name === "assistant"); - expect(assistant).toBeDefined(); - expect(assistant?.endpoint).toBe("claude"); - expect(assistant?.default).toBe(true); - expect(assistant?.systemPrompt).toBe("You are helpful."); - - const autocomplete = configs.find((c) => c.name === "autocomplete"); - expect(autocomplete).toBeDefined(); - expect(autocomplete?.endpoint).toBe("gemini"); - expect(autocomplete?.maxSteps).toBe(1); - expect(autocomplete?.systemPrompt).toBe("Just continue."); - } finally { - fs.rmSync(tmpDir, { recursive: true }); - } - }); - - test("handles file with no frontmatter", () => { - const tmpDir = fs.mkdtempSync(path.join(import.meta.dirname, "tmp-")); - - try { - fs.writeFileSync( - path.join(tmpDir, "simple.md"), - "Just a plain system prompt.", - ); - - const configs = loadAgentConfigs(tmpDir); - expect(configs).toHaveLength(1); - expect(configs[0].name).toBe("simple"); - expect(configs[0].endpoint).toBeUndefined(); - expect(configs[0].systemPrompt).toBe("Just a plain system prompt."); - } finally { - fs.rmSync(tmpDir, { recursive: true }); - } - }); - - test("ignores non-.md files", () => { - const tmpDir = fs.mkdtempSync(path.join(import.meta.dirname, "tmp-")); - - try { - fs.writeFileSync(path.join(tmpDir, "agent.md"), "Prompt."); - fs.writeFileSync(path.join(tmpDir, "config.yaml"), "key: value"); - fs.writeFileSync(path.join(tmpDir, "notes.txt"), "Notes."); - - const configs = loadAgentConfigs(tmpDir); - expect(configs).toHaveLength(1); - expect(configs[0].name).toBe("agent"); - } finally { - fs.rmSync(tmpDir, { recursive: true }); - } - }); -}); diff --git a/packages/appkit/src/plugins/agent/types.ts b/packages/appkit/src/plugins/agent/types.ts deleted file mode 100644 index 58acf5ea..00000000 --- a/packages/appkit/src/plugins/agent/types.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { - AgentAdapter, - AgentToolDefinition, - BasePluginConfig, - ThreadStore, -} from "shared"; -import type { FunctionTool } from "../agents/tools/function-tool"; -import type { HostedTool } from "../agents/tools/hosted-tools"; - -export type AgentTool = FunctionTool | HostedTool; - -export interface AgentDefinition { - adapter: AgentAdapter | Promise; - systemPrompt?: string; -} - -export type AgentEntry = AgentAdapter | AgentDefinition | Promise; - -export interface AgentPluginConfig extends BasePluginConfig { - agents?: Record; - defaultAgent?: string; - threadStore?: ThreadStore; - tools?: AgentTool[]; - agentsDir?: string; - plugins?: Record; -} - -export type ToolEntry = - | { - source: "plugin"; - pluginName: string; - def: AgentToolDefinition; - localName: string; - } - | { - source: "function"; - functionTool: FunctionTool; - def: AgentToolDefinition; - } - | { - source: "mcp"; - mcpToolName: string; - def: AgentToolDefinition; - }; - -export type RegisteredAgent = { - name: string; - adapter: AgentAdapter; - systemPrompt?: string; -}; - -export type { AgentAdapter, AgentToolDefinition } from "shared"; diff --git a/packages/appkit/src/plugins/index.ts b/packages/appkit/src/plugins/index.ts index 17c92621..4d58082f 100644 --- a/packages/appkit/src/plugins/index.ts +++ b/packages/appkit/src/plugins/index.ts @@ -1,4 +1,3 @@ -export * from "./agent"; export * from "./analytics"; export * from "./files"; export * from "./genie";