import type { TRPCContext } from "../../trpc.js"; import { CreateClientSchema, CreateOrgUnitSchema, UpdateClientSchema, UpdateOrgUnitSchema, } from "@capakraken/shared"; import { z } from "zod"; import type { ToolContext, ToolDef, ToolExecutor } from "./shared.js"; type AssistantToolErrorResult = { error: string }; type ClientsOrgUnitsDeps = { createClientCaller: (ctx: TRPCContext) => { create: (params: z.input) => Promise<{ id: string; name: string; }>; update: (params: { id: string; data: z.input; }) => Promise<{ id: string; name: string; }>; getById: (params: { id: string }) => Promise<{ name: string }>; delete: (params: { id: string }) => Promise; }; createOrgUnitCaller: (ctx: TRPCContext) => { create: (params: z.input) => Promise<{ id: string; name: string; }>; update: (params: { id: string; data: z.input; }) => Promise<{ id: string; name: string; }>; }; createScopedCallerContext: (ctx: ToolContext) => TRPCContext; toAssistantClientMutationError: ( error: unknown, action?: "create" | "update" | "delete", ) => AssistantToolErrorResult | null; toAssistantOrgUnitMutationError: ( error: unknown, ) => AssistantToolErrorResult | null; }; export const clientMutationToolDefinitions: ToolDef[] = [ { type: "function", function: { name: "create_client", description: "Create a new client. Requires manager or admin role. Always confirm first.", parameters: { type: "object", properties: { name: { type: "string", description: "Client name" }, code: { type: "string", description: "Client code" }, parentId: { type: "string", description: "Optional parent client ID" }, sortOrder: { type: "integer", description: "Sort order. Default: 0" }, tags: { type: "array", items: { type: "string" }, description: "Optional client tags" }, }, required: ["name"], }, }, }, { type: "function", function: { name: "update_client", description: "Update a client. Requires manager or admin role. Always confirm first.", parameters: { type: "object", properties: { id: { type: "string", description: "Client ID" }, name: { type: "string", description: "New name" }, code: { type: "string", description: "New code" }, sortOrder: { type: "integer", description: "New sort order" }, isActive: { type: "boolean", description: "Set active state" }, parentId: { type: "string", description: "Parent client ID; use null to clear" }, tags: { type: "array", items: { type: "string" }, description: "Replacement client tags" }, }, required: ["id"], }, }, }, { type: "function", function: { name: "delete_client", description: "Delete a client. Requires admin role. Always confirm first.", parameters: { type: "object", properties: { id: { type: "string", description: "Client ID" }, }, required: ["id"], }, }, }, ]; export const orgUnitMutationToolDefinitions: ToolDef[] = [ { type: "function", function: { name: "create_org_unit", description: "Create a new organizational unit. Admin role required. Always confirm first.", parameters: { type: "object", properties: { name: { type: "string", description: "Org unit name" }, shortName: { type: "string", description: "Short name/code" }, level: { type: "integer", description: "Level (5, 6, or 7)" }, parentId: { type: "string", description: "Parent org unit ID (optional)" }, sortOrder: { type: "integer", description: "Sort order. Default: 0" }, }, required: ["name", "level"], }, }, }, { type: "function", function: { name: "update_org_unit", description: "Update an organizational unit. Admin role required. Always confirm first.", parameters: { type: "object", properties: { id: { type: "string", description: "Org unit ID" }, name: { type: "string", description: "New name" }, shortName: { type: "string", description: "New short name" }, sortOrder: { type: "integer", description: "New sort order" }, isActive: { type: "boolean", description: "Set active state" }, parentId: { type: "string", description: "Parent org unit ID; use null to clear" }, }, required: ["id"], }, }, }, ]; export function createClientsOrgUnitsExecutors( deps: ClientsOrgUnitsDeps, ): Record { return { async create_client(params: { name: string; code?: string; parentId?: string; sortOrder?: number; tags?: string[]; }, ctx: ToolContext) { const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx)); let client; try { client = await caller.create(CreateClientSchema.parse(params)); } catch (error) { const mapped = deps.toAssistantClientMutationError(error); if (mapped) { return mapped; } throw error; } return { __action: "invalidate" as const, scope: ["client"], success: true, message: `Created client: ${client.name}`, clientId: client.id, client, }; }, async update_client(params: { id: string; name?: string; code?: string | null; sortOrder?: number; isActive?: boolean; parentId?: string | null; tags?: string[]; }, ctx: ToolContext) { const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx)); const data = UpdateClientSchema.parse({ ...(params.name !== undefined ? { name: params.name } : {}), ...(params.code !== undefined ? { code: params.code } : {}), ...(params.sortOrder !== undefined ? { sortOrder: params.sortOrder } : {}), ...(params.isActive !== undefined ? { isActive: params.isActive } : {}), ...(params.parentId !== undefined ? { parentId: params.parentId } : {}), ...(params.tags !== undefined ? { tags: params.tags } : {}), }); if (Object.keys(data).length === 0) { return { error: "No fields to update" }; } let client; try { client = await caller.update({ id: params.id, data }); } catch (error) { const mapped = deps.toAssistantClientMutationError(error); if (mapped) { return mapped; } throw error; } return { __action: "invalidate" as const, scope: ["client"], success: true, message: `Updated client: ${client.name}`, clientId: client.id, client, }; }, async delete_client(params: { id: string }, ctx: ToolContext) { const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx)); let client; try { client = await caller.getById({ id: params.id }); await caller.delete({ id: params.id }); } catch (error) { const mapped = deps.toAssistantClientMutationError(error, "delete"); if (mapped) { return mapped; } throw error; } return { __action: "invalidate" as const, scope: ["client"], success: true, message: `Deleted client: ${client.name}`, }; }, async create_org_unit(params: { name: string; shortName?: string; level: number; parentId?: string; sortOrder?: number; }, ctx: ToolContext) { const caller = deps.createOrgUnitCaller(deps.createScopedCallerContext(ctx)); let orgUnit; try { orgUnit = await caller.create(CreateOrgUnitSchema.parse(params)); } catch (error) { const mapped = deps.toAssistantOrgUnitMutationError(error); if (mapped) { return mapped; } throw error; } return { __action: "invalidate" as const, scope: ["orgUnit"], success: true, message: `Created org unit: ${orgUnit.name}`, orgUnitId: orgUnit.id, orgUnit, }; }, async update_org_unit(params: { id: string; name?: string; shortName?: string | null; sortOrder?: number; isActive?: boolean; parentId?: string | null; }, ctx: ToolContext) { const caller = deps.createOrgUnitCaller(deps.createScopedCallerContext(ctx)); const data = UpdateOrgUnitSchema.parse({ ...(params.name !== undefined ? { name: params.name } : {}), ...(params.shortName !== undefined ? { shortName: params.shortName } : {}), ...(params.sortOrder !== undefined ? { sortOrder: params.sortOrder } : {}), ...(params.isActive !== undefined ? { isActive: params.isActive } : {}), ...(params.parentId !== undefined ? { parentId: params.parentId } : {}), }); if (Object.keys(data).length === 0) { return { error: "No fields to update" }; } let orgUnit; try { orgUnit = await caller.update({ id: params.id, data }); } catch (error) { const mapped = deps.toAssistantOrgUnitMutationError(error); if (mapped) { return mapped; } throw error; } return { __action: "invalidate" as const, scope: ["orgUnit"], success: true, message: `Updated org unit: ${orgUnit.name}`, orgUnitId: orgUnit.id, orgUnit, }; }, }; }