import type { prisma } from "@capakraken/db"; import type { PermissionKey, SystemRole } from "@capakraken/shared"; import type { z } from "zod"; import type { TRPCContext } from "../../trpc.js"; export type ToolContext = { db: typeof prisma; userId: string; userRole: string; permissions: Set; session?: TRPCContext["session"]; dbUser?: TRPCContext["dbUser"]; roleDefaults?: TRPCContext["roleDefaults"]; /** * If true, the ctx.db passed in is already wrapped by * `createReadOnlyProxy` and any scoped tRPC caller the tool spawns * MUST also receive the proxied client — otherwise a read-only tool * can smuggle writes through a tRPC caller that bypasses the proxy. */ isReadOnly?: boolean; }; export interface ToolAccessRequirements { requiredPermissions?: PermissionKey[]; allowedSystemRoles?: SystemRole[]; requiresPlanningRead?: boolean; requiresCostView?: boolean; requiresAdvancedAssistant?: boolean; requiresResourceOverview?: boolean; } export interface ToolDef { type: "function"; function: { name: string; description: string; parameters: Record; }; access?: ToolAccessRequirements; /** EGAI 4.3.1.2 — optional Zod schema to validate tool results before returning to the AI */ resultSchema?: z.ZodType; } // eslint-disable-next-line @typescript-eslint/no-explicit-any export type ToolExecutor = (params: any, ctx: ToolContext) => Promise; export function withToolAccess( tools: ToolDef[], accessByName: Partial>, ): ToolDef[] { return tools.map((tool) => ({ ...tool, ...(accessByName[tool.function.name] ? { access: accessByName[tool.function.name] } : {}), })); }