import { mkdtempSync, readFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { Effect } from "effect"; import { appsPlugin, connectionNameForScope } from "./apps-plugin"; import { makeSelfHostAppsRuntime } from "./self-host-runtime"; import { makeInMemoryAppsStore, makeTestResolver } from "../testing"; const run = (effect: Effect.Effect): Promise => Effect.runPromise(effect); const PROTOTYPE_ROOT = "/Users/rhyssullivan/agent-workspace/prototypes/custom-tools"; const prototypeFileSet = (): Map => new Map([ ["executor.json", readFileSync(join(PROTOTYPE_ROOT, "executor.json"), "utf8")], [ "tools/deal-pipeline-sync.ts", readFileSync(join(PROTOTYPE_ROOT, "tools/deal-pipeline-sync.ts"), "utf8"), ], [ "tools/find-deal-docs.ts", readFileSync(join(PROTOTYPE_ROOT, "tools/find-deal-docs.ts"), "utf8"), ], ]); describe("appsPlugin custom-tools contract", () => { it("round-trips prototype files through publish, resolveTools, and invokeTool", async () => { let dealListArgs: unknown; const resolver = makeTestResolver( { dealcloud: { "deals.list": (args) => { dealListArgs = args[0]; return []; }, }, "microsoft-sharepoint": { "search.query": () => [], }, }, [ { address: "tools.dealcloud.user.crm-main", integration: "dealcloud", name: "crm-main", }, { address: "tools.microsoft-sharepoint.user.sharepoint-main", integration: "microsoft-sharepoint", name: "sharepoint-main", }, ], ); const host = makeSelfHostAppsRuntime({ dataDir: mkdtempSync(join(tmpdir(), "apps-plugin-")), store: makeInMemoryAppsStore(), resolver, inMemory: true, }); const runtime = host.runtime; const plugin = appsPlugin({ runtime }); const appConnection = connectionNameForScope("rhys"); await run(runtime.publish({ scope: "rhys", files: prototypeFileSet() })); const resolved = await run( plugin.resolveTools!({ connection: { name: appConnection }, } as never), ); const syncTool = resolved.tools.find((tool) => String(tool.name) === "deal-pipeline-sync"); expect(syncTool).toBeTruthy(); const inputSchema = syncTool!.inputSchema as { properties: Record; required?: string[]; }; expect(inputSchema.properties.crm.enum).toEqual(["tools.dealcloud.user.crm-main"]); expect(inputSchema.properties.crm.default).toBe("tools.dealcloud.user.crm-main"); expect(inputSchema.properties.crm.description).toBe("Connection to use for crm (dealcloud)"); expect(inputSchema.required ?? []).not.toContain("crm"); const output = await run( plugin.invokeTool!({ ctx: {}, toolRow: { name: "deal-pipeline-sync", connection: appConnection, }, args: { crm: "tools.dealcloud.user.crm-main", updatedSince: "2026-01-01T00:00:00Z", }, } as never), ); expect(output).toEqual({ synced: 0 }); expect(dealListArgs).toEqual({ status: "active", updatedSince: "2026-01-01T00:00:00Z", pageSize: 200, }); await host.close(); }); });