Collapse the GET /api/agents/me/standup duplicate to one module
Two registrations of the same route lived in the codebase: one inline in routes-agents.ts that filtered three full company-wide SELECTs in memory, and the dedicated routes-agents-standup.ts that uses targeted standup-shaped DB queries. The router keeps the first match so the inline copy was serving every request and the focused module was dead code — any future fix to standup would have silently missed production. Remove the inline standup handler and its three companion helpers (`listAgentsForStandup`, `listTasksForStandup`, `listApprovalsForStandup`) plus the unused `AgentStandupRow` type. The dedicated standup module is now the single source of truth.
This commit is contained in:
1 parent
efb4bddab6
commit
1a0fdc6f31
5 files changed
+4
-167
No files matched your search
@@ -23,109 +23,10 @@ export function registerAgentRoutes(server: AgentApiServer) {
|
|||||||
server.json(res, 200, { agent, chainOfCommand, directReports });
|
server.json(res, 200, { agent, chainOfCommand, directReports });
|
||||||
});
|
});
|
||||||
|
|
||||||
server.addRoute("GET", "/api/agents/me/standup", (_req, res, _params, token) => {
|
// GET /api/agents/me/standup lives in routes-agents-standup.ts, which
|
||||||
const agent = db.getAgent(token.agentId);
|
// uses targeted standup-shaped DB queries. The inline duplicate that
|
||||||
if (!agent || agent.companyId !== token.companyId) {
|
// used to sit here was dead code — the router keeps the first match
|
||||||
server.json(res, 404, { error: "Agent not found" });
|
// and the standup module is registered separately in api-server/index.ts.
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const companyAgents = db.listAgentsForStandup(token.companyId);
|
|
||||||
const companyTasks = db.listTasksForStandup(token.companyId);
|
|
||||||
const companyApprovals = db.listApprovalsForStandup(token.companyId);
|
|
||||||
|
|
||||||
const myTasks = companyTasks.filter((t) => t.assigneeAgentId === token.agentId);
|
|
||||||
const directReports = companyAgents.filter((a) => a.reportsTo === token.agentId);
|
|
||||||
const reportIds = new Set(directReports.map((a) => a.id));
|
|
||||||
const reportTasks = companyTasks.filter((t) => t.assigneeAgentId && reportIds.has(t.assigneeAgentId));
|
|
||||||
|
|
||||||
const tasksAwaitingMyReview = reportTasks.filter((t) => t.status === "in_review");
|
|
||||||
const blockedReportTasks = reportTasks.filter((t) => t.status === "blocked");
|
|
||||||
const myBlockedTasks = myTasks.filter((t) => t.status === "blocked");
|
|
||||||
const myInProgressTasks = myTasks.filter((t) => t.status === "in_progress");
|
|
||||||
const myTodoTasks = myTasks.filter((t) => t.status === "todo");
|
|
||||||
|
|
||||||
const allRelevantTaskIds = new Set([...myTasks.map((t) => t.id), ...reportTasks.map((t) => t.id)]);
|
|
||||||
const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
|
|
||||||
const recentComments = db.listRecentTaskComments([...allRelevantTaskIds], twoHoursAgo, 20);
|
|
||||||
|
|
||||||
const myPendingApprovals = companyApprovals.filter(
|
|
||||||
(a) => a.requestedByAgentId === token.agentId && a.state === "pending",
|
|
||||||
);
|
|
||||||
const myResolvedApprovals = companyApprovals.filter(
|
|
||||||
(a) => a.requestedByAgentId === token.agentId && a.state !== "pending",
|
|
||||||
).slice(0, 5);
|
|
||||||
|
|
||||||
const agentNameMap = new Map(companyAgents.map((a) => [a.id, a.name]));
|
|
||||||
const taskTitleMap = new Map(companyTasks.map((t) => [t.id, t.title]));
|
|
||||||
const socialAccounts = db.listSocialAccounts(token.companyId);
|
|
||||||
|
|
||||||
server.json(res, 200, {
|
|
||||||
agent: { id: agent.id, name: agent.name, role: agent.role, status: agent.status },
|
|
||||||
summary: {
|
|
||||||
tasksAwaitingReview: tasksAwaitingMyReview.length,
|
|
||||||
blockedReportTasks: blockedReportTasks.length,
|
|
||||||
myBlockedTasks: myBlockedTasks.length,
|
|
||||||
myInProgress: myInProgressTasks.length,
|
|
||||||
myTodo: myTodoTasks.length,
|
|
||||||
pendingApprovals: myPendingApprovals.length,
|
|
||||||
directReports: directReports.length,
|
|
||||||
},
|
|
||||||
tasksAwaitingReview: tasksAwaitingMyReview.map((t) => ({
|
|
||||||
id: t.id,
|
|
||||||
title: t.title,
|
|
||||||
assignee: agentNameMap.get(t.assigneeAgentId ?? "") ?? "Unknown",
|
|
||||||
priority: t.priority,
|
|
||||||
})),
|
|
||||||
blockedTasks: [...blockedReportTasks, ...myBlockedTasks].map((t) => ({
|
|
||||||
id: t.id,
|
|
||||||
title: t.title,
|
|
||||||
assignee: agentNameMap.get(t.assigneeAgentId ?? "") ?? "Unknown",
|
|
||||||
priority: t.priority,
|
|
||||||
})),
|
|
||||||
recentComments: recentComments.map((c) => ({
|
|
||||||
taskId: c.taskId,
|
|
||||||
taskTitle: taskTitleMap.get(c.taskId) ?? "",
|
|
||||||
author: c.authorName,
|
|
||||||
body: c.body.slice(0, 200),
|
|
||||||
createdAt: c.createdAt,
|
|
||||||
})),
|
|
||||||
pendingApprovals: myPendingApprovals.map((a) => ({
|
|
||||||
id: a.id,
|
|
||||||
type: a.type,
|
|
||||||
summary: a.payloadSummary,
|
|
||||||
state: a.state,
|
|
||||||
})),
|
|
||||||
resolvedApprovals: myResolvedApprovals.map((a) => ({
|
|
||||||
id: a.id,
|
|
||||||
type: a.type,
|
|
||||||
summary: a.payloadSummary,
|
|
||||||
state: a.state,
|
|
||||||
decisionNote: a.decisionNote,
|
|
||||||
})),
|
|
||||||
reportStatuses: directReports.map((r) => {
|
|
||||||
const rTasks = companyTasks.filter((t) => t.assigneeAgentId === r.id);
|
|
||||||
return {
|
|
||||||
id: r.id,
|
|
||||||
name: r.name,
|
|
||||||
role: r.role,
|
|
||||||
status: r.status,
|
|
||||||
tasksInProgress: rTasks.filter((t) => t.status === "in_progress").length,
|
|
||||||
tasksInReview: rTasks.filter((t) => t.status === "in_review").length,
|
|
||||||
tasksBlocked: rTasks.filter((t) => t.status === "blocked").length,
|
|
||||||
tasksDone: rTasks.filter((t) => t.status === "done").length,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
socialAccounts: socialAccounts.map((a) => ({
|
|
||||||
id: a.id,
|
|
||||||
platform: a.platform,
|
|
||||||
accountName: a.accountName,
|
|
||||||
displayName: a.displayName,
|
|
||||||
status: a.status,
|
|
||||||
requireApproval: a.requireApproval,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
server.addRoute("GET", "/api/agents/me/metrics", (_req, res, _params, token) => {
|
server.addRoute("GET", "/api/agents/me/metrics", (_req, res, _params, token) => {
|
||||||
const companyMetrics = db.getCompanyMetrics(token.companyId, 30);
|
const companyMetrics = db.getCompanyMetrics(token.companyId, 30);
|
||||||
|
|||||||
@@ -233,7 +233,6 @@ export class AppDatabaseBase {
|
|||||||
getCeoForCompany(companyId: string) { return this.agents.getCeoForCompany(companyId); }
|
getCeoForCompany(companyId: string) { return this.agents.getCeoForCompany(companyId); }
|
||||||
listActiveAgentsForCompany(companyId: string) { return this.agents.listActiveAgentsForCompany(companyId); }
|
listActiveAgentsForCompany(companyId: string) { return this.agents.listActiveAgentsForCompany(companyId); }
|
||||||
listAgentsForApi(companyId: string) { return this.agents.listAgentsForApi(companyId); }
|
listAgentsForApi(companyId: string) { return this.agents.listAgentsForApi(companyId); }
|
||||||
listAgentsForStandup(companyId: string) { return this.agents.listAgentsForStandup(companyId); }
|
|
||||||
listDirectReportsForStandup(companyId: string, managerId: string) { return this.agents.listDirectReportsForStandup(companyId, managerId); }
|
listDirectReportsForStandup(companyId: string, managerId: string) { return this.agents.listDirectReportsForStandup(companyId, managerId); }
|
||||||
listReportStatusesForStandup(companyId: string, managerId: string) { return this.agents.listReportStatusesForStandup(companyId, managerId); }
|
listReportStatusesForStandup(companyId: string, managerId: string) { return this.agents.listReportStatusesForStandup(companyId, managerId); }
|
||||||
listExistingAgentsForHireCheck(companyId: string) { return this.agents.listExistingAgentsForHireCheck(companyId); }
|
listExistingAgentsForHireCheck(companyId: string) { return this.agents.listExistingAgentsForHireCheck(companyId); }
|
||||||
@@ -276,7 +275,6 @@ export class AppDatabaseBase {
|
|||||||
renewTaskLease(taskId: string, runId: string) { return this.tasks.renewTaskLease(taskId, runId); }
|
renewTaskLease(taskId: string, runId: string) { return this.tasks.renewTaskLease(taskId, runId); }
|
||||||
listAgentTasks(agentId: string, companyId: string, statuses: string[]) { return this.tasks.listAgentTasks(agentId, companyId, statuses); }
|
listAgentTasks(agentId: string, companyId: string, statuses: string[]) { return this.tasks.listAgentTasks(agentId, companyId, statuses); }
|
||||||
listTasksByStatuses(companyId: string, statuses: string[]) { return this.tasks.listTasksByStatuses(companyId, statuses); }
|
listTasksByStatuses(companyId: string, statuses: string[]) { return this.tasks.listTasksByStatuses(companyId, statuses); }
|
||||||
listTasksForStandup(companyId: string) { return this.tasks.listTasksForStandup(companyId); }
|
|
||||||
listAssigneeTasksForStandup(companyId: string, agentId: string) { return this.tasks.listAssigneeTasksForStandup(companyId, agentId); }
|
listAssigneeTasksForStandup(companyId: string, agentId: string) { return this.tasks.listAssigneeTasksForStandup(companyId, agentId); }
|
||||||
listTasksForAssignees(companyId: string, assigneeIds: string[]) { return this.tasks.listTasksForAssignees(companyId, assigneeIds); }
|
listTasksForAssignees(companyId: string, assigneeIds: string[]) { return this.tasks.listTasksForAssignees(companyId, assigneeIds); }
|
||||||
listReviewTasksForManager(managerId: string, companyId: string) { return this.tasks.listReviewTasksForManager(managerId, companyId); }
|
listReviewTasksForManager(managerId: string, companyId: string) { return this.tasks.listReviewTasksForManager(managerId, companyId); }
|
||||||
@@ -334,7 +332,6 @@ export class AppDatabaseBase {
|
|||||||
listApprovalsForTask(taskId: string) { return this.approvalsMod.listApprovalsForTask(taskId); }
|
listApprovalsForTask(taskId: string) { return this.approvalsMod.listApprovalsForTask(taskId); }
|
||||||
listTasksForApproval(approvalId: string) { return this.approvalsMod.listTasksForApproval(approvalId); }
|
listTasksForApproval(approvalId: string) { return this.approvalsMod.listTasksForApproval(approvalId); }
|
||||||
linkManyTasksToApproval(approvalId: string, taskIds: string[], companyId: string, linkedByAgentId?: string | null) { this.approvalsMod.linkManyTasksToApproval(approvalId, taskIds, companyId, linkedByAgentId); }
|
linkManyTasksToApproval(approvalId: string, taskIds: string[], companyId: string, linkedByAgentId?: string | null) { this.approvalsMod.linkManyTasksToApproval(approvalId, taskIds, companyId, linkedByAgentId); }
|
||||||
listApprovalsForStandup(companyId: string) { return this.approvalsMod.listApprovalsForStandup(companyId); }
|
|
||||||
listAgentPendingApprovals(companyId: string, agentId: string) { return this.approvalsMod.listAgentPendingApprovals(companyId, agentId); }
|
listAgentPendingApprovals(companyId: string, agentId: string) { return this.approvalsMod.listAgentPendingApprovals(companyId, agentId); }
|
||||||
listAgentResolvedApprovals(companyId: string, agentId: string, limit?: number) { return this.approvalsMod.listAgentResolvedApprovals(companyId, agentId, limit); }
|
listAgentResolvedApprovals(companyId: string, agentId: string, limit?: number) { return this.approvalsMod.listAgentResolvedApprovals(companyId, agentId, limit); }
|
||||||
listPendingHireApprovalPayloads(companyId: string) { return this.approvalsMod.listPendingHireApprovalPayloads(companyId); }
|
listPendingHireApprovalPayloads(companyId: string) { return this.approvalsMod.listPendingHireApprovalPayloads(companyId); }
|
||||||
|
|||||||
@@ -35,18 +35,6 @@ export type AgentApiRow = {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Slim agent projection used by the me/standup aggregation.
|
|
||||||
*/
|
|
||||||
export type AgentStandupRow = {
|
|
||||||
id: string;
|
|
||||||
companyId: string;
|
|
||||||
name: string;
|
|
||||||
role: string;
|
|
||||||
status: string;
|
|
||||||
reportsTo: string | null;
|
|
||||||
department: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function buildAgentHierarchyMethods(
|
export function buildAgentHierarchyMethods(
|
||||||
getDb: () => DatabaseSync,
|
getDb: () => DatabaseSync,
|
||||||
@@ -126,18 +114,6 @@ export function buildAgentHierarchyMethods(
|
|||||||
).map((a) => ({ ...a, heartbeatEnabled: Boolean(a.heartbeatEnabled) })) as AgentApiRow[];
|
).map((a) => ({ ...a, heartbeatEnabled: Boolean(a.heartbeatEnabled) })) as AgentApiRow[];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the slim standup projection used by
|
|
||||||
* GET /api/agents/me/standup — only the fields that feed the
|
|
||||||
* aggregation (id, name, role, status, reportsTo, department).
|
|
||||||
*/
|
|
||||||
listAgentsForStandup(companyId: string): AgentStandupRow[] {
|
|
||||||
return readAll<AgentStandupRow>(
|
|
||||||
"select id, company_id as companyId, name, role, status, reports_to as reportsTo, department from agents where company_id = ? order by created_at asc",
|
|
||||||
companyId,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the slim direct-reports projection used by
|
* Returns the slim direct-reports projection used by
|
||||||
* GET /api/agents/me/standup — only (id, name, role, status).
|
* GET /api/agents/me/standup — only (id, name, role, status).
|
||||||
|
|||||||
@@ -179,25 +179,6 @@ export function buildApprovalMethods(
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the slim approval projection used by
|
|
||||||
* GET /api/agents/me/standup. Ordered by created_at desc.
|
|
||||||
*/
|
|
||||||
listApprovalsForStandup(companyId: string): Array<{
|
|
||||||
id: string;
|
|
||||||
companyId: string;
|
|
||||||
type: string;
|
|
||||||
requestedByAgentId: string | null;
|
|
||||||
state: string;
|
|
||||||
payloadSummary: string | null;
|
|
||||||
decisionNote: string | null;
|
|
||||||
}> {
|
|
||||||
return readAll(
|
|
||||||
"select id, company_id as companyId, type, requested_by_agent_id as requestedByAgentId, state, payload_summary as payloadSummary, decision_note as decisionNote from approvals where company_id = ? order by created_at desc",
|
|
||||||
companyId,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns pending `hire_agent` approval payloads for a company, used
|
* Returns pending `hire_agent` approval payloads for a company, used
|
||||||
* during hire-request creation to detect duplicate names across
|
* during hire-request creation to detect duplicate names across
|
||||||
|
|||||||
@@ -82,24 +82,6 @@ export function buildTaskQueryMethods(
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the slim task projection used by the me/standup aggregation.
|
|
||||||
* Ordered by created_at desc.
|
|
||||||
*/
|
|
||||||
listTasksForStandup(companyId: string): Array<{
|
|
||||||
id: string;
|
|
||||||
companyId: string;
|
|
||||||
title: string;
|
|
||||||
assigneeAgentId: string | null;
|
|
||||||
status: string;
|
|
||||||
priority: string;
|
|
||||||
}> {
|
|
||||||
return readAll(
|
|
||||||
"select id, company_id as companyId, title, assignee_agent_id as assigneeAgentId, status, priority from tasks where company_id = ? order by created_at desc",
|
|
||||||
companyId,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns tasks filtered by status.
|
* Returns tasks filtered by status.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in new issue
Block a user