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.addRoute("GET", "/api/agents/me/standup", (_req, res, _params, token) => {
|
||||
const agent = db.getAgent(token.agentId);
|
||||
if (!agent || agent.companyId !== token.companyId) {
|
||||
server.json(res, 404, { error: "Agent not found" });
|
||||
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,
|
||||
})),
|
||||
});
|
||||
});
|
||||
// GET /api/agents/me/standup lives in routes-agents-standup.ts, which
|
||||
// uses targeted standup-shaped DB queries. The inline duplicate that
|
||||
// used to sit here was dead code — the router keeps the first match
|
||||
// and the standup module is registered separately in api-server/index.ts.
|
||||
|
||||
server.addRoute("GET", "/api/agents/me/metrics", (_req, res, _params, token) => {
|
||||
const companyMetrics = db.getCompanyMetrics(token.companyId, 30);
|
||||
|
||||
@@ -233,7 +233,6 @@ export class AppDatabaseBase {
|
||||
getCeoForCompany(companyId: string) { return this.agents.getCeoForCompany(companyId); }
|
||||
listActiveAgentsForCompany(companyId: string) { return this.agents.listActiveAgentsForCompany(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); }
|
||||
listReportStatusesForStandup(companyId: string, managerId: string) { return this.agents.listReportStatusesForStandup(companyId, managerId); }
|
||||
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); }
|
||||
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); }
|
||||
listTasksForStandup(companyId: string) { return this.tasks.listTasksForStandup(companyId); }
|
||||
listAssigneeTasksForStandup(companyId: string, agentId: string) { return this.tasks.listAssigneeTasksForStandup(companyId, agentId); }
|
||||
listTasksForAssignees(companyId: string, assigneeIds: string[]) { return this.tasks.listTasksForAssignees(companyId, assigneeIds); }
|
||||
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); }
|
||||
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); }
|
||||
listApprovalsForStandup(companyId: string) { return this.approvalsMod.listApprovalsForStandup(companyId); }
|
||||
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); }
|
||||
listPendingHireApprovalPayloads(companyId: string) { return this.approvalsMod.listPendingHireApprovalPayloads(companyId); }
|
||||
|
||||
@@ -35,18 +35,6 @@ export type AgentApiRow = {
|
||||
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(
|
||||
getDb: () => DatabaseSync,
|
||||
@@ -126,18 +114,6 @@ export function buildAgentHierarchyMethods(
|
||||
).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
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user