Codemod db pass-through calls to domain modules: tasks + agents (C1 part 1/3)

Rewrite ~525 call sites from `db.<method>()` to `db.<module>.<method>()`
for tasks and agents domains (including `getAgent`/`findAgent`/`getTask`
which live on the `entityCrud` field). Delete the corresponding
pass-through methods on AppDatabaseBase. Structural types that narrowed
`db` to a bag of flat methods (CapabilityMatchDb, EscalationDeps,
SweepStaleReviewsDeps) are reshaped to match the new namespace. Internal
callback references inside database.ts/database-base.ts now invoke the
owning module directly (e.g. `this.agents.saveAgent`) rather than the
deleted pass-through.

The codemod lives at scripts/codemod/remove-appdatabase-passthrough.mjs
and filters by owning module + optional explicit method list. It is
idempotent and does not re-rewrite already-namespaced calls.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-20 13:17:01 -04:00
1 parent 411294afa6
commit 5e9b7acbc6
95 files changed
+1123 -793

No files matched your search

+16 -16
View File
@@ -55,9 +55,9 @@ export function createHeartbeat(
} = deps;
function ensureAgentHeartbeatEnabled(agentId: string) {
const agent = db.getAgent(agentId);
const agent = db.entityCrud.getAgent(agentId);
if (!agent.heartbeatEnabled) {
db.setHeartbeat(agentId, true, agent.heartbeatIntervalSec || 120);
db.agents.setHeartbeat(agentId, true, agent.heartbeatIntervalSec || 120);
}
}
@@ -68,7 +68,7 @@ export function createHeartbeat(
if (!hasWorkerReady()) return SKIPPED("worker_not_ready");
const companyStatus = db.getCompanyStatus(companyId);
if (companyStatus !== "active") return SKIPPED("company_inactive");
const agent = db.getAgent(agentId);
const agent = db.entityCrud.getAgent(agentId);
if (agent.companyId !== companyId) {
logger.warn(`[heartbeat] Refusing cross-company run for agent ${agentId}: ${agent.companyId} !== ${companyId}`);
return SKIPPED("cross_company");
@@ -112,10 +112,10 @@ export function createHeartbeat(
// Let getHeartbeatPolicy throw — a missing row signals data corruption
// and silently falling back hides the gate that decides whether the
// agent runs at all.
const heartbeatPolicy = db.getHeartbeatPolicy(agent.id);
const heartbeatPolicy = db.agents.getHeartbeatPolicy(agent.id);
// maxConcurrentRuns check: count active runs for this agent
const activeRunCount = db.countActiveRunsForAgent(agent.id, companyId);
const activeRunCount = db.agents.countActiveRunsForAgent(agent.id, companyId);
if (activeRunCount >= heartbeatPolicy.maxConcurrentRuns) return SKIPPED("max_concurrent_runs");
// Cooldown check: ensure enough time has passed since last heartbeat
@@ -129,7 +129,7 @@ export function createHeartbeat(
if (!heartbeatPolicy.wakeOnDemand && trigger !== "timer" && trigger !== "bootstrap") {
return SKIPPED("wake_on_demand_disabled");
}
if (db.isAgentPendingApproval(agentId)) return SKIPPED("pending_approval");
if (db.agents.isAgentPendingApproval(agentId)) return SKIPPED("pending_approval");
const companyMode = db.getCompanyExecutionMode(companyId);
const approvalSettings = db.getCompanyApprovalSettings(companyId);
@@ -140,7 +140,7 @@ export function createHeartbeat(
if (
shouldEnforceHumanReview &&
!isMessageWake &&
(db.hasAgentPendingDeliverableReview(agentId, companyId) || db.hasAgentTaskAwaitingReview(agentId, companyId))
(db.tasks.hasAgentPendingDeliverableReview(agentId, companyId) || db.tasks.hasAgentTaskAwaitingReview(agentId, companyId))
) {
return SKIPPED("awaiting_user_review");
}
@@ -150,23 +150,23 @@ export function createHeartbeat(
shouldEnforceHumanReview &&
!isMessageWake &&
!agent.reportsTo &&
db.hasPendingDeliverableReviews(companyId)
db.tasks.hasPendingDeliverableReviews(companyId)
) {
return SKIPPED("pending_deliverable_review");
}
const assignedTasks = isCommunicationWake
? []
: db.listAgentTasks(agentId, companyId, ["todo", "in_progress", "blocked"]);
: db.tasks.listAgentTasks(agentId, companyId, ["todo", "in_progress", "blocked"]);
const isTimer = trigger === "timer";
const candidateTasks = assignedTasks;
const candidateTask = candidateTasks.find((entry) => !entry.activeRunId) ?? null;
const runId = randomUUID();
let task: ReturnType<AppDatabase["getTask"]> | null = null;
let task: ReturnType<AppDatabase["entityCrud"]["getTask"]> | null = null;
if (candidateTask) {
const checkoutSuccess = db.checkoutTask(candidateTask.id, runId);
const checkoutSuccess = db.tasks.checkoutTask(candidateTask.id, runId);
if (!checkoutSuccess) {
db.addActivity({
companyId,
@@ -178,7 +178,7 @@ export function createHeartbeat(
});
return SKIPPED("checkout_race_lost");
}
task = db.getTask(candidateTask.id);
task = db.entityCrud.getTask(candidateTask.id);
} else {
const hasActiveAssignedRun = candidateTasks.some(
(entry) => entry.activeRunId && hasActiveRun({ taskId: entry.id, companyId }),
@@ -186,9 +186,9 @@ export function createHeartbeat(
if (hasActiveAssignedRun) return SKIPPED("max_concurrent_runs");
const isCeo = !agent.reportsTo;
const isManager = db.getDirectReports(agent.id, companyId).length > 0;
const isManager = db.agents.getDirectReports(agent.id, companyId).length > 0;
const goalCount = db.countActiveGoalsForCompany(companyId);
const taskCount = db.countActiveTasksForCompany(companyId);
const taskCount = db.tasks.countActiveTasksForCompany(companyId);
const companyNeedsBootstrap = goalCount > 0 && taskCount < goalCount;
if (isTimer && !isCeo && !isManager && !companyNeedsBootstrap) {
@@ -232,7 +232,7 @@ export function createHeartbeat(
resolvedModel: assignment.modelId,
});
db.markHeartbeatAt(agentId);
db.agents.markHeartbeatAt(agentId);
// Actor audit: record what triggered this run
try {
@@ -266,7 +266,7 @@ export function createHeartbeat(
// raw) from spamming the log with "Agent not found: __board__".
if (agentId === HUMAN_SENDER_ID) return null;
try {
const agent = db.getAgent(agentId);
const agent = db.entityCrud.getAgent(agentId);
if (agent.companyId !== companyId) {
logger.warn(`[wake] Refusing cross-company wake for agent ${agentId}: ${agent.companyId} !== ${companyId}`);
return null;
+4 -4
View File
@@ -30,7 +30,7 @@ const SUBJECT_PREVIEW_LEN = 80;
export function resolveInitialUserTurn(
params: {
trigger: string | null;
task: ReturnType<AppDatabase["getTask"]> | null;
task: ReturnType<AppDatabase["entityCrud"]["getTask"]> | null;
companyId: string;
agentId: string;
},
@@ -61,9 +61,9 @@ export function resolveInitialUserTurn(
// mode prompt.
if (trigger === "timer" || trigger === "bootstrap" || trigger === "app_restart_resume") {
try {
const agent = deps.db.getAgent(agentId);
const agent = deps.db.entityCrud.getAgent(agentId);
if (!agent.reportsTo) {
const activeReports = deps.db
const activeReports = deps.db.agents
.getDirectReports(agentId, companyId)
.filter((r) => r.status !== "pending_approval" && r.status !== "terminated");
if (activeReports.length === 0) {
@@ -145,7 +145,7 @@ function buildGenericWakeTurn(trigger: string | null): string {
function resolveAgentLabel(db: AppDatabase, agentId: string): string {
try {
const name = db.findAgentName(agentId);
const name = db.agents.findAgentName(agentId);
if (name && name.length > 0) return name;
} catch {
// fall through
@@ -23,8 +23,8 @@ export function createHeartbeatScheduler(
function persistSkip(agentId: string, companyId: string, reason: string) {
try {
const existing = db.getRuntimeState(agentId) as Record<string, unknown>;
db.setRuntimeState(agentId, companyId, {
const existing = db.agents.getRuntimeState(agentId) as Record<string, unknown>;
db.agents.setRuntimeState(agentId, companyId, {
...existing,
heartbeatSkip: { reason, at: new Date().toISOString() },
});
@@ -37,11 +37,11 @@ export function createHeartbeatScheduler(
function clearSkip(agentId: string, companyId: string) {
try {
const existing = db.getRuntimeState(agentId) as Record<string, unknown>;
const existing = db.agents.getRuntimeState(agentId) as Record<string, unknown>;
if (!("heartbeatSkip" in existing)) return;
const next = { ...existing };
delete next.heartbeatSkip;
db.setRuntimeState(agentId, companyId, next);
db.agents.setRuntimeState(agentId, companyId, next);
} catch (error) {
logger.warn(
`[heartbeat] Failed to clear skip reason for agent ${agentId}: ${error instanceof Error ? error.message : String(error)}`,
@@ -72,7 +72,7 @@ export function createHeartbeatScheduler(
for (const company of activeCompanies) {
try {
const eligibleAgents = db.listHeartbeatEligibleAgents(company.id);
const eligibleAgents = db.agents.listHeartbeatEligibleAgents(company.id);
for (const agent of eligibleAgents) {
if (hasActiveRun({ agentId: agent.id, companyId: company.id })) continue;
@@ -103,9 +103,9 @@ export function createHeartbeatScheduler(
// regular timer interval. countAdvancingTasksForCompany
// counts only todo + in_progress so bootstrapping still
// fires once the team stalls.
const advancingCount = db.countAdvancingTasksForCompany(company.id);
const advancingCount = db.tasks.countAdvancingTasksForCompany(company.id);
if (goalCount > 0 && advancingCount === 0) {
const ceo = db.getCeoForCompany(company.id);
const ceo = db.agents.getCeoForCompany(company.id);
const ceoLastWake = ceo?.lastHeartbeatAt ? new Date(ceo.lastHeartbeatAt).getTime() : 0;
const ceoLastAttempt = ceo ? attemptedAtByAgent.get(ceo.id) ?? 0 : 0;
const ceoLastTouch = Math.max(ceoLastWake, ceoLastAttempt);
@@ -34,7 +34,7 @@ export function createHousekeepingScheduler(
const currentMonth = new Date().toISOString().slice(0, 7);
const lastResetMonth = db.getLastBudgetResetMonth(company.id) ?? "";
if (currentMonth !== lastResetMonth) {
db.resetMonthlyBudgets(company.id);
db.agents.resetMonthlyBudgets(company.id);
db.setLastBudgetResetMonth(company.id, currentMonth);
logger.info(`[housekeeping] Reset monthly budgets for company ${company.name} (${currentMonth})`);
}
@@ -17,9 +17,9 @@ export function createIdleAutomationScheduler(deps: OrchestrationDependencies) {
for (const company of activeCompanies) {
try {
const companyAgents = db.listActiveAgentsForCompany(company.id);
const companyAgents = db.agents.listActiveAgentsForCompany(company.id);
for (const agent of companyAgents) {
if (!db.hasAgentAssignedTasks(agent.id, company.id)) {
if (!db.agents.hasAgentAssignedTasks(agent.id, company.id)) {
const lastActive = agent.lastHeartbeatAt ? new Date(agent.lastHeartbeatAt).getTime() : 0;
const idleThreshold = 20 * 60 * 1000;
if (now - lastActive > idleThreshold) {
+6 -6
View File
@@ -37,13 +37,13 @@ export function createTaskRunner(
logger.warn(`[run] Worker not ready — cannot queue run for task ${taskId} (trigger: ${trigger})`);
return fail("WORKER_UNAVAILABLE", "Orchestration worker is not ready.");
}
const task = db.getTask(taskId);
const task = db.entityCrud.getTask(taskId);
const companyStatus = db.getCompanyStatus(task.companyId);
if (companyStatus !== "active") {
return fail("COMPANY_NOT_ACTIVE", "Runs can only start while the company is active.");
}
if (!task.assigneeAgentId) return fail("TASK_UNASSIGNED", "Assign an agent before starting a run.");
const agent = db.getAgent(task.assigneeAgentId);
const agent = db.entityCrud.getAgent(task.assigneeAgentId);
if (agent.companyId !== task.companyId) {
return fail("AGENT_COMPANY_MISMATCH", "The assigned agent does not belong to the task company.");
}
@@ -75,7 +75,7 @@ export function createTaskRunner(
if (hasActiveRun({ agentId: agent.id, companyId: task.companyId })) {
return fail("AGENT_ALREADY_RUNNING", "The assigned agent already has an active run.");
}
if (db.isAgentPendingApproval(agent.id)) {
if (db.agents.isAgentPendingApproval(agent.id)) {
return fail("AGENT_PENDING_APPROVAL", "This agent is pending hire approval and cannot run tasks.");
}
const blockingApproval = db.getBlockingApproval(task.companyId, agent.id, task.id);
@@ -88,7 +88,7 @@ export function createTaskRunner(
// CAS checkout — atomic claim (EXEC-01, EXEC-02)
const runId = randomUUID();
const checkoutSuccess = db.checkoutTask(task.id, runId);
const checkoutSuccess = db.tasks.checkoutTask(task.id, runId);
if (!checkoutSuccess) {
return fail("CHECKOUT_RACE_LOST", "Another run claimed this task. Refresh and try again.");
}
@@ -180,8 +180,8 @@ export function createTaskRunner(
}
try {
const task = run.taskId ? db.getTask(run.taskId) : null;
const agent = db.getAgent(run.agentId);
const task = run.taskId ? db.entityCrud.getTask(run.taskId) : null;
const agent = db.entityCrud.getAgent(run.agentId);
const connector = db.getConnector(run.connectorId);
const eligibilityFailure = getRunEligibilityFailure(agent, connector, task?.projectId ?? null);
if (eligibilityFailure) {
+9 -9
View File
@@ -18,11 +18,11 @@ import { resolveInitialUserTurn } from "./initial-user-turn";
export interface RunDispatchParams {
runId: string;
companyId: string;
agent: ReturnType<AppDatabase["getAgent"]>;
agent: ReturnType<AppDatabase["entityCrud"]["getAgent"]>;
// `task` is null for heartbeat runs (agent wake-ups with no assigned task).
// Downstream code (prompt builder, worker payload, session pointer)
// branches on this — when null, skip task-scoped state.
task: ReturnType<AppDatabase["getTask"]> | null;
task: ReturnType<AppDatabase["entityCrud"]["getTask"]> | null;
connector: ReturnType<AppDatabase["getConnector"]>;
workspace: ReturnType<AppDatabase["getWorkspace"]>;
workspaceMode: WorkspaceLockMode;
@@ -81,8 +81,8 @@ function assembleApiEnv(
}
function assembleRecentComments(
assignedTasks: ReturnType<AppDatabase["listAgentTasks"]>,
reviewTasks: ReturnType<AppDatabase["listAgentTasks"]>,
assignedTasks: ReturnType<AppDatabase["tasks"]["listAgentTasks"]>,
reviewTasks: ReturnType<AppDatabase["tasks"]["listAgentTasks"]>,
snapshot: RunDispatchParams["snapshot"],
) {
const relevantTaskIds = new Set([
@@ -150,7 +150,7 @@ function resolveSession(
// Per-agent session: one ongoing transcript per agent; switching tasks
// (or running taskless heartbeats) becomes a "now work on X" user turn
// on the ongoing transcript rather than starting a fresh session.
const agentSessionId = db.ensureAgentSession({
const agentSessionId = db.tasks.ensureAgentSession({
agentId: agent.id,
companyId,
adapterType: connectorId,
@@ -273,8 +273,8 @@ export function buildRunPayload(params: RunDispatchParams, deps: TurnContextDeps
const { env, apiUrl, apiKey } = assembleApiEnv(db, connector, companyId, agent.id, runId, trigger, deps);
env.AGENT_COMPANY_TASK_ID = task?.id ?? "";
const assignedTasks = db.listAgentTasks(agent.id, companyId, ["todo", "in_progress", "blocked"]);
const reviewTasks = db.listReviewTasksForManager(agent.id, companyId);
const assignedTasks = db.tasks.listAgentTasks(agent.id, companyId, ["todo", "in_progress", "blocked"]);
const reviewTasks = db.tasks.listReviewTasksForManager(agent.id, companyId);
const recentComments = assembleRecentComments(assignedTasks, reviewTasks, snapshot);
const initialHandoff = resolveSessionHandoff(agent.id, deps);
const { taskSessionId, sessionHandoff } = resolveSession(db, agent, companyId, connector.id, initialHandoff, deps);
@@ -291,8 +291,8 @@ export function buildRunPayload(params: RunDispatchParams, deps: TurnContextDeps
executionMode: db.getCompanyExecutionMode(companyId) as ExecutionMode,
goals: snapshot.goals.filter((goal) => goal.companyId === companyId),
projects: snapshot.projects.filter((project) => project.companyId === companyId),
directReports: db.getDirectReports(agent.id, companyId),
chainOfCommand: db.getChainOfCommand(agent.id),
directReports: db.agents.getDirectReports(agent.id, companyId),
chainOfCommand: db.agents.getChainOfCommand(agent.id),
assignedTasks,
allAgents: snapshot.agents.filter((entry) => entry.companyId === companyId),
pendingApprovals: filterPendingApprovals(snapshot.approvals, companyId, agent.id),