import { SystemRole } from "@capakraken/shared"; import type { TRPCContext } from "../../trpc.js"; import { withToolAccess, type ToolContext, type ToolDef, type ToolExecutor } from "./shared.js"; type AuditListItem = { id: string; entityType: string; entityId: string; entityName: string | null; action: string; userId: string | null; source: string | null; summary: string | null; createdAt: string; user: { id: string; name: string | null; email: string | null; } | null; }; type AuditHistoryDeps = { createAuditLogCaller: (ctx: TRPCContext) => { listDetail: (params: { entityType?: string; entityId?: string; userId?: string; action?: string; source?: string; startDate?: Date; endDate?: Date; search?: string; limit: number; cursor?: string; }) => Promise<{ items: AuditListItem[]; nextCursor: string | null; }>; getByEntityDetail: (params: { entityType: string; entityId: string; limit: number; }) => Promise; }; createScopedCallerContext: (ctx: ToolContext) => TRPCContext; }; export const auditHistoryToolDefinitions: ToolDef[] = withToolAccess([ { type: "function", function: { name: "query_change_history", description: "Search the audit history for changes to projects, resources, allocations, vacations, or any entity. Reuses the real audit log list API. Controller/manager/admin roles only.", parameters: { type: "object", properties: { entityType: { type: "string", description: "Filter by entity type (e.g. 'Project', 'Resource', 'Allocation', 'Vacation', 'Role', 'Estimate')" }, search: { type: "string", description: "Search in entity name or summary text" }, userId: { type: "string", description: "Filter by user ID who made the change" }, daysBack: { type: "integer", description: "How many days back to search. Default: 7" }, action: { type: "string", description: "Filter by action type: CREATE, UPDATE, DELETE, SHIFT, IMPORT" }, limit: { type: "integer", description: "Max results. Default: 20" }, }, }, }, }, { type: "function", function: { name: "get_entity_timeline", description: "Get the audit history for a specific entity (project, resource, etc.) via the real audit API. Controller/manager/admin roles only.", parameters: { type: "object", properties: { entityType: { type: "string", description: "Entity type (e.g. 'Project', 'Resource', 'Allocation')" }, entityId: { type: "string", description: "Entity ID" }, limit: { type: "integer", description: "Max results. Default: 50" }, }, required: ["entityType", "entityId"], }, }, }, ], { query_change_history: { allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER], }, get_entity_timeline: { allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER], }, }); export function createAuditHistoryExecutors( deps: AuditHistoryDeps, ): Record { return { async query_change_history( params: { entityType?: string; search?: string; userId?: string; daysBack?: number; action?: string; limit?: number; }, ctx: ToolContext, ) { const limit = Math.min(params.limit ?? 20, 50); const daysBack = params.daysBack ?? 7; const startDate = new Date(); startDate.setDate(startDate.getDate() - daysBack); const caller = deps.createAuditLogCaller(deps.createScopedCallerContext(ctx)); const result = await caller.listDetail({ ...(params.entityType ? { entityType: params.entityType } : {}), ...(params.userId ? { userId: params.userId } : {}), ...(params.action ? { action: params.action } : {}), ...(params.search ? { search: params.search } : {}), startDate, limit, }); return { filters: { entityType: params.entityType ?? null, userId: params.userId ?? null, action: params.action ?? null, search: params.search ?? null, daysBack, }, itemCount: result.items.length, nextCursor: result.nextCursor ?? null, items: result.items, }; }, async get_entity_timeline( params: { entityType: string; entityId: string; limit?: number }, ctx: ToolContext, ) { const limit = Math.min(params.limit ?? 50, 200); const caller = deps.createAuditLogCaller(deps.createScopedCallerContext(ctx)); return caller.getByEntityDetail({ entityType: params.entityType, entityId: params.entityId, limit, }); }, }; }