refactor(api): extract assistant audit-history slice
This commit is contained in:
@@ -41,12 +41,13 @@
|
||||
- the blueprint and rate-card read helpers now live in their own domain module, keeping reference-data and pricing lookups out of the monolithic assistant router without changing the assistant contract
|
||||
- the dashboard detail, insight summary, anomaly detection, and dynamic report read helpers now live in their own domain module, keeping controller-side analytics reads out of the monolithic assistant router without changing the assistant contract
|
||||
- the comment listing and comment mutation assistant helpers now live in their own domain module, keeping collaboration-side comment flows out of the monolithic assistant router without changing the assistant contract
|
||||
- the audit-history assistant helpers now live in their own domain module, keeping controller-side change-history reads out of the monolithic assistant router without changing the assistant contract
|
||||
|
||||
## Next Up
|
||||
|
||||
Pin the next structural cleanup on the API side:
|
||||
continue splitting `packages/api/src/router/assistant-tools.ts` into domain-oriented tool modules without changing the public tool contract.
|
||||
The next clean slice should stay adjacent to the extracted domains and target one cohesive analytics or audit block such as `query_change_history` / `get_entity_timeline` or the remaining scenario/narrative/rate-analysis helpers still living in the monolithic router.
|
||||
The next clean slice should stay adjacent to the extracted domains and target one cohesive analytics block such as the remaining scenario/narrative/rate-analysis helpers still living in the monolithic router.
|
||||
|
||||
## Remaining Major Themes
|
||||
|
||||
|
||||
@@ -132,6 +132,10 @@ import {
|
||||
commentReadToolDefinitions,
|
||||
createCommentExecutors,
|
||||
} from "./assistant-tools/comments.js";
|
||||
import {
|
||||
auditHistoryToolDefinitions,
|
||||
createAuditHistoryExecutors,
|
||||
} from "./assistant-tools/audit-history.js";
|
||||
import {
|
||||
withToolAccess,
|
||||
type ToolAccessRequirements,
|
||||
@@ -425,8 +429,6 @@ const LEGACY_MONOLITHIC_TOOL_ACCESS: Partial<Record<string, ToolAccessRequiremen
|
||||
lookup_rate: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||
simulate_scenario: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||
generate_project_narrative: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||
query_change_history: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||
get_entity_timeline: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||
export_resources_csv: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||
export_projects_csv: { allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES] },
|
||||
import_csv_data: {
|
||||
@@ -2387,40 +2389,7 @@ export const TOOL_DEFINITIONS: ToolDef[] = withToolAccess([
|
||||
},
|
||||
},
|
||||
...commentMutationToolDefinitions,
|
||||
{
|
||||
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"],
|
||||
},
|
||||
},
|
||||
},
|
||||
...auditHistoryToolDefinitions,
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
@@ -2824,6 +2793,10 @@ const executors = {
|
||||
toAssistantCommentCreationError,
|
||||
toAssistantCommentResolveError,
|
||||
}),
|
||||
...createAuditHistoryExecutors({
|
||||
createAuditLogCaller,
|
||||
createScopedCallerContext,
|
||||
}),
|
||||
|
||||
async search_estimates(params: {
|
||||
projectCode?: string; query?: string; status?: string; limit?: number;
|
||||
@@ -3364,57 +3337,6 @@ const executors = {
|
||||
return caller.generateProjectNarrative({ projectId: params.projectId });
|
||||
},
|
||||
|
||||
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 = createAuditLogCaller(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 = createAuditLogCaller(createScopedCallerContext(ctx));
|
||||
return caller.getByEntityDetail({
|
||||
entityType: params.entityType,
|
||||
entityId: params.entityId,
|
||||
limit,
|
||||
});
|
||||
},
|
||||
|
||||
async export_resources_csv(_params: Record<string, never>, ctx: ToolContext) {
|
||||
const caller = createImportExportCaller(createScopedCallerContext(ctx));
|
||||
const csv = await caller.exportResourcesCSV();
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
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<unknown>;
|
||||
};
|
||||
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<string, ToolExecutor> {
|
||||
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,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user