Files
AgentCompany/src/main/api-server/routes-agents.ts
T
ZacharyZhang-NY f041336721 refactor: nuke dead exports, dead files, dead DB column, dead zero-value wrappers
Full-codebase ts-prune + manual audit sweep. Every change below is a pure
deletion (or a 2-line re-wire where a dead function was supposed to be
alive). No scope additions.

Dead wrappers / phantom features
  - shouldAutoApproveApproval: zero-value wrapper over shouldAutoApprove.
    Four call sites now import shouldAutoApprove from execution-policy
    directly.
  - evaluateRotation(session, connectorId, thresholdsOverride): the
    connectorId arg was never read (underscored), and thresholdsOverride
    was only exercised by the test suite — no production caller. Signature
    collapses to evaluateRotation(session). DEFAULT_THRESHOLDS /
    RotationThresholds exports and their tests go with it.
  - installLogRedactor: defensive console wrapper that was never wired
    from worker-entry. buildRedactor handles the known threat (env-value
    leaks); the pattern-based redactTokens stays for tests and future
    composition.

Dead files
  - resume-interrupted-tasks.ts: helper function never imported.
  - service-graph-orchestration-deps.ts: three factory closures
    superseded by the inline closures in create-orchestration-service.

Dead exports
  - listConnectorsByAuthKind (shared/connector-catalog)
  - getLocalePromptInstruction + whole localization.ts file
  - ImportantDocType
  - main-side ConnectorAuthKind (duplicated the shared one; not referenced)
  - CheckpointEngineService, RunHousekeepingService types
  - WorkspaceResolution type
  - cmdOrCtrlSymbol
  - AgentEventPush, CredentialPayload type aliases
  - parseApprovalPayloadJson, parseEnvValue, ProviderCard
  - getSOP — buildSOPSection reads the SOPS map directly; the individual
    getter had no callers. Deduped to a flat array.
  - renderWakeReason in wake-reasons/en.ts: byte-for-byte duplicate of the
    one in wake-reasons/index.ts.

Forgotten wiring (not cleanup, but adjacent)
  - registerAgentStandupRoute: the GET /api/agents/me/standup endpoint is
    advertised in every system prompt and wake-reason doc, but the route
    handler was never registered in api-server/index.ts. Agents have been
    hitting 404 since Pi Mono. Registered.

Dead DB column
  - v44 migration drops runs.context_snapshot_json. Column was added by
    v16-v25, never written or read, and v43's deliberate preservation
    was an interim measure. Same idempotent table-rebuild pattern as
    v41/v43.

Test tree
  - Phantom-feature threshold override tests removed (3). The rest of
    the rotation suite re-expressed against the real maxRuns=20 floor.

495/495 unit tests pass. typecheck + build clean.
2026-04-18 23:37:37 -04:00

388 lines
15 KiB
TypeScript

import log from "electron-log/main.js";
import { hireRequestSchema } from "@shared/contracts";
import { getConnectorExecutionReadinessIssue } from "@shared/connector-policy";
import {
canManageOrganization,
canRequestHire,
getHireNameValidationError,
getHireRoutingValidationError,
resolveHireReportsTo,
} from "../agent-permissions";
import { resolveApprovalDecision } from "../approval-policy";
import { shouldAutoApprove } from "../execution-policy";
import { requireParam, type AgentApiServer } from "./server-core";
export function registerAgentRoutes(server: AgentApiServer) {
const db = server.db;
const callbacks = server.callbacks;
const onDomainChanged = server.onDomainChanged;
server.addRoute("GET", "/api/agents/me", (_req, res, _params, token) => {
const agent = db.getAgent(token.agentId);
const chainOfCommand = db.getChainOfCommand(token.agentId);
const directReports = db.getDirectReports(token.agentId, token.companyId);
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,
})),
});
});
server.addRoute("GET", "/api/agents/me/metrics", (_req, res, _params, token) => {
const companyMetrics = db.getCompanyMetrics(token.companyId, 30);
const agentMetric = companyMetrics.agentMetrics.find(m => m.agentId === token.agentId);
if (!agentMetric) {
server.json(res, 404, { error: "Metrics not found" });
return;
}
server.json(res, 200, agentMetric);
});
server.addRoute("GET", "/api/agents/:agentId/metrics", (_req, res, params, token) => {
const agentId = requireParam(params, "agentId");
const agent = db.getAgent(agentId);
if (!agent || agent.companyId !== token.companyId) {
server.json(res, 404, { error: "Agent not found" });
return;
}
const companyMetrics = db.getCompanyMetrics(token.companyId, 30);
const agentMetric = companyMetrics.agentMetrics.find((metric) => metric.agentId === agentId);
if (!agentMetric) {
server.json(res, 404, { error: "Metrics not found" });
return;
}
server.json(res, 200, agentMetric);
});
server.addRoute("GET", "/api/agents/:agentId", (_req, res, params, token) => {
let agent;
try {
agent = db.getAgent(requireParam(params, "agentId"));
} catch (err) {
log.warn(`[api/agents] getAgent failed:`, err instanceof Error ? err.message : String(err));
server.json(res, 404, { error: "Agent not found" });
return;
}
if (agent.companyId !== token.companyId) {
server.json(res, 404, { error: "Agent not found" });
return;
}
const directReports = db.getDirectReports(agent.id, token.companyId);
const reportsTo = agent.reportsTo;
const manager = reportsTo
? (() => {
try {
return db.getAgent(reportsTo);
} catch (error) {
log.warn(`[api/agents] manager lookup failed for ${reportsTo}: ${error instanceof Error ? error.message : String(error)}`);
return null;
}
})()
: null;
server.json(res, 200, {
...agent,
manager: manager ? { id: manager.id, name: manager.name, role: manager.role } : null,
directReports: directReports.map(r => ({ id: r.id, name: r.name, role: r.role, status: r.status })),
});
});
server.addRoute("GET", "/api/companies/:companyId/agents", (_req, res, params, token) => {
if (params.companyId !== token.companyId) {
server.json(res, 403, { error: "Company mismatch" });
return;
}
const agents = db.listAgentsForApi(params.companyId);
server.json(res, 200, agents);
});
server.addRoute("POST", "/api/companies/:companyId/agents", (_req, res, params, token, body) => {
const companyId = requireParam(params, "companyId");
if (companyId !== token.companyId) {
server.json(res, 403, { error: "Company mismatch" });
return;
}
if (!canRequestHire(db, companyId, token.agentId)) {
server.json(res, 403, { error: "Current agent is not allowed to request hires" });
return;
}
const reportsToProvided =
typeof body === "object" &&
body !== null &&
Object.prototype.hasOwnProperty.call(body, "reportsTo");
const parsed = hireRequestSchema.safeParse({
companyId,
requestedByAgentId: token.agentId,
...(typeof body === "object" && body ? body : {}),
});
if (!parsed.success) {
server.json(res, 400, { error: "Invalid hire payload" });
return;
}
const input = parsed.data;
const resolvedReportsTo = resolveHireReportsTo(
db,
companyId,
token.agentId,
reportsToProvided ? input.reportsTo : undefined,
input.department ?? null,
);
const nameError = getHireNameValidationError({
name: input.name,
role: input.role,
title: input.title,
});
if (nameError) {
server.json(res, 422, { error: nameError });
return;
}
const routingError = getHireRoutingValidationError(
db,
companyId,
token.agentId,
resolvedReportsTo,
input.department ?? null,
);
if (routingError) {
server.json(res, 422, { error: routingError });
return;
}
if (server.companyRefError(res, companyId, [
{ id: input.requestedByAgentId, table: "agents", label: "requesting agent" },
{ id: resolvedReportsTo, table: "agents", label: "manager" },
{ id: input.workspaceId, table: "workspaces", label: "workspace" },
])) {
return;
}
if (
resolvedReportsTo &&
resolvedReportsTo !== token.agentId &&
!canManageOrganization(db, companyId, token.agentId) &&
!db.getChainOfCommand(resolvedReportsTo).some((agent) => agent.id === token.agentId)
) {
server.json(res, 403, { error: "Requested hire must report to you or a manager in your reporting chain." });
return;
}
const connectorRecord = (() => {
try {
return db.getConnector(input.connectorId);
} catch (err) {
log.warn(`[api/agents] getConnector failed for ${input.connectorId}:`, err instanceof Error ? err.message : String(err));
return null;
}
})();
if (!connectorRecord) {
server.json(res, 422, { error: "The selected connector does not exist." });
return;
}
const connectorIssue = getConnectorExecutionReadinessIssue(connectorRecord);
if (connectorIssue) {
server.json(res, 422, { error: connectorIssue });
return;
}
// Prevent duplicate agents: reject if a non-terminated agent with the same name already exists
const existingAgents = db.listExistingAgentsForHireCheck(companyId);
const duplicateByName = existingAgents.find(
(a) => a.name.toLowerCase() === input.name.toLowerCase(),
);
if (duplicateByName) {
server.json(res, 409, { error: `Agent "${input.name}" already exists in this company (id: ${duplicateByName.id}, role: ${duplicateByName.role}). Use the existing agent instead of creating a duplicate.` });
return;
}
// Also check pending hire approvals with the same name to prevent concurrent duplicate requests
const pendingHires = db.listPendingHireApprovalPayloads(companyId);
for (const pending of pendingHires) {
try {
const payload = pending.payloadJson ? JSON.parse(pending.payloadJson) as Record<string, unknown> : {};
if (typeof payload.name === "string" && payload.name.toLowerCase() === input.name.toLowerCase()) {
server.json(res, 409, { error: `A hire request for "${input.name}" is already pending approval.` });
return;
}
} catch (error) {
log.warn(
`[api/agents] invalid pending-hire payloadJson for company ${companyId}: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
const managerLabel = resolvedReportsTo
? (() => {
try {
return db.getAgent(resolvedReportsTo).name;
} catch (err) {
log.warn(`[api/agents] getAgent failed for manager ${resolvedReportsTo}:`, err instanceof Error ? err.message : String(err));
return resolvedReportsTo;
}
})()
: "the company board";
let result: { agentId: string; approvalId: string };
try {
result = db.createHireRequest({
companyId,
requestedByAgentId: input.requestedByAgentId ?? token.agentId,
name: input.name,
role: input.role,
title: input.title,
department: input.department ?? null,
reportsTo: resolvedReportsTo,
connectorId: input.connectorId,
workspaceId: input.workspaceId ?? null,
model: input.model ?? null,
metadataJson: input.metadataJson ?? "{}",
capabilities: input.capabilities,
budgetMonthlyUsd: input.budgetMonthlyUsd,
payloadSummary: `Hire ${input.name} as ${input.role}`,
impactSummary: input.capabilities || `${input.name} will report to ${managerLabel}.`,
payloadJson: JSON.stringify({
name: input.name,
role: input.role,
title: input.title,
requestedByAgentId: input.requestedByAgentId ?? token.agentId,
requestedConfigurationSnapshot: {
connectorId: input.connectorId,
workspaceId: input.workspaceId ?? null,
model: input.model ?? null,
metadataJson: input.metadataJson ?? "{}",
capabilities: input.capabilities,
budgetMonthlyUsd: input.budgetMonthlyUsd,
reportsTo: resolvedReportsTo,
department: input.department ?? null,
title: input.title,
},
}),
});
} catch (error) {
server.json(res, 422, { error: error instanceof Error ? error.message : "Hire could not be created" });
return;
}
db.addActivity({
companyId,
actor: token.agentId,
action: "agent.hire_requested",
entityType: "agent",
entityId: result.agentId,
detail: input.name,
});
let autoApproved = false;
if (shouldAutoApprove(db, companyId, "hire_agent")) {
resolveApprovalDecision({
db,
handleApprovalResolved: callbacks.onApprovalResolved,
companyId,
approvalId: result.approvalId,
state: "approved",
decisionNote: "Auto-approved by company policy",
detail: `Auto-approved hire: ${input.name}`,
});
autoApproved = true;
log.info(`[auto-approve] Auto-approved hire of ${input.name} via agent API`);
}
onDomainChanged();
server.json(res, 201, { ...result, autoApproved });
});
}