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:
1 parent
411294afa6
commit
5e9b7acbc6
95 files changed
+1123
-793
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in new issue
Block a user