refactor(db): group database domain modules

This commit is contained in:
ZacharyZhang-NY committed 2026-04-22 19:24:25 -04:00
1 parent e43a89096d
commit 6da0a58769
160 files changed
+2124 -1999

No files matched your search

@@ -58,7 +58,7 @@ function handleAssignTask({ deps, ctx }: TypedAutomationActionExecution<"assign_
const { db, logger, wakeAgentIfPossible } = deps;
if (ctx.taskId) {
const assignedId = db.tasks.autoAssignTask(ctx.taskId, ctx.companyId);
const assignedId = db.core.tasks.autoAssignTask(ctx.taskId, ctx.companyId);
if (assignedId) {
wakeAgentIfPossible(assignedId, ctx.companyId, "assignment");
}
@@ -67,14 +67,14 @@ function handleAssignTask({ deps, ctx }: TypedAutomationActionExecution<"assign_
if (!ctx.agentId) return;
const snapshot = db.snap.listSnapshot();
const snapshot = db.projection.snapshot.listSnapshot();
const unassigned = snapshot.tasks
.filter((task) => task.companyId === ctx.companyId && !task.assigneeAgentId && ["backlog", "todo"].includes(task.status))
.sort((a, b) => (TASK_PRIORITY_SCORE[b.priority] ?? 0) - (TASK_PRIORITY_SCORE[a.priority] ?? 0));
if (unassigned.length === 0) return;
const task = unassigned[0]!;
db.tasks.saveTask({
db.core.tasks.saveTask({
id: task.id,
companyId: task.companyId,
projectId: task.projectId,
@@ -106,7 +106,7 @@ function handleCreateTask({ deps, callbacks, ctx, config }: TypedAutomationActio
return;
}
const taskId = db.tasks.saveTask({
const taskId = db.core.tasks.saveTask({
companyId: ctx.companyId,
projectId: ctx.projectId ?? config.projectId ?? null,
title,
@@ -140,7 +140,7 @@ function handleAgentNotification<K extends "notify_agent" | "send_message">({
if (channel === "department" && !channelTargetId) {
try {
const sender = db.entityCrud.getAgent(fromAgentId);
const sender = db.core.entity.getAgent(fromAgentId);
channelTargetId = sender.department ?? null;
} catch (err) {
logger.warn(`[automation] getAgent failed for sender ${fromAgentId}; dropping department target:`, err instanceof Error ? err.message : String(err));
@@ -184,7 +184,7 @@ function handleTriggerHeartbeat({ deps, rule, ctx, config }: TypedAutomationActi
if (!targetAgentId && ctx.agentId) {
const triggerType = rule.trigger ?? "";
if (triggerType === "run_completed" || triggerType === "run_failed") {
const manager = db.agents.getManagerForAgent(ctx.agentId);
const manager = db.core.agents.getManagerForAgent(ctx.agentId);
targetAgentId = manager?.id ?? ctx.agentId;
} else {
targetAgentId = ctx.agentId;
@@ -197,7 +197,7 @@ function handleTriggerHeartbeat({ deps, rule, ctx, config }: TypedAutomationActi
}
function handleCreateApproval({ deps, ctx, config }: TypedAutomationActionExecution<"create_approval">): void {
deps.db.approvalsMod.requestApproval({
deps.db.core.approvals.requestApproval({
companyId: ctx.companyId,
relatedTaskId: ctx.taskId ?? null,
requestedByAgentId: ctx.agentId ?? null,
@@ -219,8 +219,8 @@ function handleUpdateTaskStatus({
const { db, publishDomainChanged } = deps;
const { handleTaskStatusChange } = callbacks;
const newStatus = config.status ?? "todo";
const previousTask = db.entityCrud.getTask(ctx.taskId);
db.tasks.advanceTaskStatus(ctx.taskId, newStatus, "Status changed by automation rule");
const previousTask = db.core.entity.getTask(ctx.taskId);
db.core.tasks.advanceTaskStatus(ctx.taskId, newStatus, "Status changed by automation rule");
publishDomainChanged();
if (previousTask.status !== newStatus && rule.trigger !== "task_status_changed") {
@@ -240,19 +240,19 @@ function handleEscalateToManager({
let agentName = "an agent";
try {
agentName = db.entityCrud.getAgent(targetAgentId).name;
agentName = db.core.entity.getAgent(targetAgentId).name;
} catch (err) {
logger.warn(`[automation] getAgent failed for escalation target ${targetAgentId}; using generic label:`, err instanceof Error ? err.message : String(err));
}
let taskTitle = ctx.taskId ?? "unknown";
try {
if (ctx.taskId) taskTitle = db.entityCrud.getTask(ctx.taskId).title;
if (ctx.taskId) taskTitle = db.core.entity.getTask(ctx.taskId).title;
} catch (err) {
logger.warn(`[automation] getTask failed for escalation task ${ctx.taskId}; using id as title:`, err instanceof Error ? err.message : String(err));
}
const manager = db.agents.getManagerForAgent(targetAgentId);
const manager = db.core.agents.getManagerForAgent(targetAgentId);
const escalationBody = config.body ?? `Task "${taskTitle}" needs your attention. ${agentName} has flagged this as requiring manager intervention.`;
const wakeTrigger = deriveEscalationWakeTrigger(ctx.newStatus ?? null, rule.trigger ?? null);
@@ -291,7 +291,7 @@ function handleCrossDepartmentNotify({
const targetDepartment = rule.targetDepartment ?? config.department;
if (!targetDepartment) return;
const departmentAgents = db.agents.getIdleAgentsForDepartment(ctx.companyId, targetDepartment);
const departmentAgents = db.core.agents.getIdleAgentsForDepartment(ctx.companyId, targetDepartment);
const senderAgentId = ctx.agentId ?? departmentAgents[0]?.id;
if (!senderAgentId) return;
@@ -322,7 +322,7 @@ function handleScheduleMeeting({ deps, ctx, config }: TypedAutomationActionExecu
const offsetMinutes = config.scheduleOffsetMinutes ?? 60;
const scheduledAt = config.scheduledAt ?? new Date(Date.now() + offsetMinutes * 60_000).toISOString();
deps.db.knowledgeMod.saveMeeting({
deps.db.core.knowledge.saveMeeting({
companyId: ctx.companyId,
type: meetingType,
title,
@@ -334,7 +334,7 @@ function handleScheduleMeeting({ deps, ctx, config }: TypedAutomationActionExecu
}
function handleCreateDocument({ deps, ctx, config }: TypedAutomationActionExecution<"create_document">): void {
deps.db.documentsMod.saveDocument({
deps.db.core.documents.saveDocument({
companyId: ctx.companyId,
type: config.documentType ?? "status_report",
title: config.title ?? "Auto-generated document",
@@ -345,13 +345,13 @@ function handleCreateDocument({ deps, ctx, config }: TypedAutomationActionExecut
}
function handleAutoApprove({ deps, callbacks, ctx }: TypedAutomationActionExecution<"auto_approve">): void {
const snapshot = deps.db.snap.listSnapshot();
const snapshot = deps.db.projection.snapshot.listSnapshot();
const pendingApprovals = snapshot.approvals.filter(
(approval) => approval.companyId === ctx.companyId && approval.state === "pending",
);
for (const approval of pendingApprovals.slice(0, 5)) {
deps.db.approvalsMod.decideApproval({
deps.db.core.approvals.decideApproval({
companyId: ctx.companyId,
approvalId: approval.id,
state: "approved",
@@ -364,7 +364,7 @@ function handleAutoApprove({ deps, callbacks, ctx }: TypedAutomationActionExecut
function handleReassignTask({ deps, ctx }: TypedAutomationActionExecution<"reassign_task">): void {
if (!ctx.taskId) return;
const newAssignee = deps.db.tasks.autoAssignTask(ctx.taskId, ctx.companyId);
const newAssignee = deps.db.core.tasks.autoAssignTask(ctx.taskId, ctx.companyId);
if (newAssignee) {
deps.wakeAgentIfPossible(newAssignee, ctx.companyId, "assignment");
}
@@ -374,7 +374,7 @@ function handleRunTask({ deps, ctx, config }: TypedAutomationActionExecution<"ru
const targetTaskId = config.taskId ?? ctx.taskId;
if (!targetTaskId) return;
const task = deps.db.entityCrud.getTask(targetTaskId);
const task = deps.db.core.entity.getTask(targetTaskId);
if (!task.assigneeAgentId) return;
deps.wakeAgentIfPossible(task.assigneeAgentId, ctx.companyId, "assignment");
}
@@ -30,7 +30,7 @@ function recordReviewFeedbackKnowledge(
if (!approval.relatedTaskId) return;
if (decision === "approved") return;
const task = db.entityCrud.getTask(approval.relatedTaskId);
const task = db.core.entity.getTask(approval.relatedTaskId);
const feedback = approval.decisionNote?.trim() || "Board requested revisions before approval.";
const topic = `Review feedback: ${task.title}`;
const content = [
@@ -39,7 +39,7 @@ function recordReviewFeedbackKnowledge(
"Use this review guidance to improve future deliverables for similar work.",
].join("\n");
db.knowledgeMod.saveKnowledgeEntry({
db.core.knowledge.saveKnowledgeEntry({
companyId: approval.companyId,
category: "lesson_learned",
topic,
@@ -84,7 +84,7 @@ export function handleApprovalResolved(
const { db, logger, publishDomainChanged, notify, wakeAgentIfPossible, dispatchBrowserAction } = deps;
const { dispatchAutomation, handleTaskStatusChange, runWorkflowPipeline, trackTimer } = callbacks;
const snapshot = db.snap.listSnapshot();
const snapshot = db.projection.snapshot.listSnapshot();
const approval = snapshot.approvals.find((entry) => entry.id === approvalId);
if (!approval) return;
@@ -95,7 +95,7 @@ export function handleApprovalResolved(
});
if (approval.type === "hire_agent" && decision === "approved" && approval.relatedAgentId) {
const agent = db.entityCrud.getAgent(approval.relatedAgentId);
const agent = db.core.entity.getAgent(approval.relatedAgentId);
if (agent.status === "idle") {
// Deterministic default skill kit. The CEO's operating prompt
@@ -111,7 +111,7 @@ export function handleApprovalResolved(
department: agent.department,
});
if (assignedSkillIds.length > 0) {
db.activityMod.addActivity({
db.core.activity.addActivity({
companyId,
actor: "system",
action: "agent.skills_defaulted",
@@ -121,7 +121,7 @@ export function handleApprovalResolved(
});
}
db.activityMod.addActivity({
db.core.activity.addActivity({
companyId,
actor: "system",
action: "agent.hired",
@@ -136,13 +136,13 @@ export function handleApprovalResolved(
});
wakeAgentIfPossible(agent.id, companyId, "assignment");
const ceo = db.agents.getCeoForCompany(companyId);
const ceo = db.core.agents.getCeoForCompany(companyId);
if (ceo && ceo.id !== agent.id) {
wakeAgentIfPossible(ceo.id, companyId, "assignment");
}
publishDomainChanged();
const hireLocale = db.snap.listSnapshot().locale;
const hireLocale = db.projection.snapshot.listSnapshot().locale;
notify({
title: localizeNotification(hireLocale, "Agent Hired", "新员工入职"),
body: localizeNotification(hireLocale, `${agent.name} joined as ${agent.role}`, `${agent.name} 已加入,职位:${agent.role}`),
@@ -170,31 +170,31 @@ export function handleApprovalResolved(
? "draft"
: "in_review";
for (const docId of documentIds) {
db.documentsMod.updateDocumentStatus(docId, companyId, docStatus);
db.core.documents.updateDocumentStatus(docId, companyId, docStatus);
}
if (approval.relatedTaskId) {
const task = db.entityCrud.getTask(approval.relatedTaskId);
const task = db.core.entity.getTask(approval.relatedTaskId);
if (task.assigneeAgentId) {
const agent = db.entityCrud.getAgent(task.assigneeAgentId);
const agent = db.core.entity.getAgent(task.assigneeAgentId);
if (!agent.heartbeatEnabled) {
db.agents.setHeartbeat(task.assigneeAgentId, true, agent.heartbeatIntervalSec || 120);
db.core.agents.setHeartbeat(task.assigneeAgentId, true, agent.heartbeatIntervalSec || 120);
logger.info(`[deliverable-review] Resumed heartbeat for ${agent.name} after user ${decision}.`);
}
}
if (decision === "approved") {
db.tasks.advanceTaskStatus(task.id, "done", "Board approved deliverable.");
db.core.tasks.advanceTaskStatus(task.id, "done", "Board approved deliverable.");
handleTaskStatusChange(task.id, companyId, task.status, "done");
db.activityMod.addActivity({ companyId, actor: "board", action: "deliverable.approved", entityType: "task", entityId: task.id, detail: approval.decisionNote || "Deliverable approved by board." });
db.core.activity.addActivity({ companyId, actor: "board", action: "deliverable.approved", entityType: "task", entityId: task.id, detail: approval.decisionNote || "Deliverable approved by board." });
} else if (decision === "rejected") {
recordReviewFeedbackKnowledge(db, approval, decision);
db.tasks.advanceTaskStatus(task.id, "in_progress", `Board rejected deliverable: ${approval.decisionNote || "No reason given."}`);
db.core.tasks.advanceTaskStatus(task.id, "in_progress", `Board rejected deliverable: ${approval.decisionNote || "No reason given."}`);
handleTaskStatusChange(task.id, companyId, task.status, "in_progress");
if (task.assigneeAgentId) {
wakeAgentIfPossible(task.assigneeAgentId, companyId, "revision_needed");
}
} else if (decision === "revision_requested") {
db.tasks.advanceTaskStatus(task.id, "in_progress", `Board requested revisions: ${approval.decisionNote || "No reason given."}`);
db.core.tasks.advanceTaskStatus(task.id, "in_progress", `Board requested revisions: ${approval.decisionNote || "No reason given."}`);
handleTaskStatusChange(task.id, companyId, task.status, "in_progress");
recordReviewFeedbackKnowledge(db, approval, decision);
if (task.assigneeAgentId) {
@@ -202,9 +202,9 @@ export function handleApprovalResolved(
}
}
} else if (approval.requestedByAgentId) {
const agent = db.entityCrud.getAgent(approval.requestedByAgentId);
const agent = db.core.entity.getAgent(approval.requestedByAgentId);
if (!agent.heartbeatEnabled) {
db.agents.setHeartbeat(approval.requestedByAgentId, true, agent.heartbeatIntervalSec || 120);
db.core.agents.setHeartbeat(approval.requestedByAgentId, true, agent.heartbeatIntervalSec || 120);
logger.info(`[deliverable-review] Resumed heartbeat for ${agent.name} after user ${decision}.`);
}
if (decision !== "approved") {
@@ -219,7 +219,7 @@ export function handleApprovalResolved(
const documentIds = extractApprovalDocumentIds(approval.payloadJson);
const docStatus = decision === "approved" ? "approved" : "draft";
for (const docId of documentIds) {
db.documentsMod.updateDocumentStatus(docId, companyId, docStatus);
db.core.documents.updateDocumentStatus(docId, companyId, docStatus);
}
publishDomainChanged();
}
@@ -235,8 +235,8 @@ export function handleApprovalResolved(
};
if (payload.policyId) {
// Resolve the budget incident that triggered this approval
db.budget.resolveBudgetIncident(payload.policyId, companyId);
db.activityMod.addActivity({
db.core.budget.resolveBudgetIncident(payload.policyId, companyId);
db.core.activity.addActivity({
companyId,
actor: "board",
action: "budget.override_approved",
@@ -250,10 +250,10 @@ export function handleApprovalResolved(
}
}
const browserAction = db.social.getBrowserActionByApprovalId(approvalId, companyId);
const browserAction = db.core.social.getBrowserActionByApprovalId(approvalId, companyId);
if (browserAction) {
if (decision === "approved") {
db.social.updateBrowserAction(browserAction.id, {
db.core.social.updateBrowserAction(browserAction.id, {
status: "queued",
resultSummary: "Approval granted. Dispatching browser action.",
errorMessage: null,
@@ -263,7 +263,7 @@ export function handleApprovalResolved(
logger.error(`[browser] Failed to dispatch approved browser action ${browserAction.id}: ${error instanceof Error ? error.message : String(error)}`);
});
} else if (decision === "rejected") {
db.social.updateBrowserAction(browserAction.id, {
db.core.social.updateBrowserAction(browserAction.id, {
status: "cancelled",
resultSummary: "Browser action was rejected during approval.",
errorMessage: "Approval rejected by board.",
@@ -271,7 +271,7 @@ export function handleApprovalResolved(
});
publishDomainChanged();
} else if (decision === "revision_requested") {
db.social.updateBrowserAction(browserAction.id, {
db.core.social.updateBrowserAction(browserAction.id, {
status: "approval_required",
resultSummary: "Board requested revisions before execution.",
errorMessage: "Approval requires revisions before the browser action can run.",
@@ -291,7 +291,7 @@ export function handleApprovalResolved(
};
const dispatchSocialAction = () => {
const actionId = db.social.saveBrowserAction({
const actionId = db.core.social.saveBrowserAction({
companyId: approval.companyId,
socialAccountId: draft.socialAccountId,
agentId: approval.requestedByAgentId ?? "",
@@ -333,14 +333,14 @@ export function handleApprovalResolved(
const payload = workflowApprovalPayloadSchema.safeParse(JSON.parse(approval.payloadJson));
if (payload.success && payload.data.workflowId && payload.data.stepId) {
const { workflowId, stepId } = payload.data;
const workflow = db.automationMod.getWorkflowRecord(workflowId, companyId);
const workflow = db.automation.rules.getWorkflowRecord(workflowId, companyId);
if (workflow) {
const steps = normalizeWorkflowSteps(JSON.parse(workflow.stepsJson || "[]"));
const stepIndex = steps.findIndex((step) => step.id === stepId);
if (stepIndex >= 0) {
if (decision === "approved") {
steps[stepIndex]!.status = "pending";
db.automationMod.updateWorkflowExecution({
db.automation.rules.updateWorkflowExecution({
id: workflow.id,
companyId,
status: "paused",
@@ -354,7 +354,7 @@ export function handleApprovalResolved(
});
} else {
steps[stepIndex]!.status = decision === "revision_requested" ? "pending" : "failed";
db.automationMod.updateWorkflowExecution({
db.automation.rules.updateWorkflowExecution({
id: workflow.id,
companyId,
status: decision === "revision_requested" ? "paused" : "failed",
+2 -2
View File
@@ -23,7 +23,7 @@ export function matchesEventConfig(
if (conditions.previousStatus && ctx.previousStatus && conditions.previousStatus !== ctx.previousStatus) return false;
if (conditions.priority) {
try {
const task = ctx.taskId ? db.entityCrud.getTask(ctx.taskId) : null;
const task = ctx.taskId ? db.core.entity.getTask(ctx.taskId) : null;
if (task && task.priority !== conditions.priority) return false;
// If task was deleted, skip this condition instead of rejecting the rule.
} catch (err) {
@@ -32,7 +32,7 @@ export function matchesEventConfig(
}
if (typeof conditions.budgetThresholdPct === "number" && ctx.agentId) {
try {
const agent = db.entityCrud.getAgent(ctx.agentId);
const agent = db.core.entity.getAgent(ctx.agentId);
if (agent.budgetMonthlyUsd > 0) {
const utilization = agent.spentMonthlyUsd / agent.budgetMonthlyUsd;
if (utilization < (conditions.budgetThresholdPct as number) / 100) return false;
+11 -11
View File
@@ -42,9 +42,9 @@ export function handleTaskStatusChange(
const { db, wakeAgentIfPossible } = deps;
const { dispatchAutomation } = callbacks;
let task: ReturnType<typeof db.entityCrud.getTask> | null = null;
let task: ReturnType<typeof db.core.entity.getTask> | null = null;
try {
task = db.entityCrud.getTask(taskId);
task = db.core.entity.getTask(taskId);
} catch (error) {
logAppError(deps.logger, error, {
kind: "recoverable",
@@ -56,7 +56,7 @@ export function handleTaskStatusChange(
}
if (!task) return;
db.activityMod.addActivity({
db.core.activity.addActivity({
companyId,
actor: task.assigneeAgentId ?? "board",
action: "task.status_changed",
@@ -74,7 +74,7 @@ export function handleTaskStatusChange(
};
try {
if (task.assigneeAgentId) {
const agent = db.entityCrud.getAgent(task.assigneeAgentId);
const agent = db.core.entity.getAgent(task.assigneeAgentId);
context.department = agent.department ?? undefined;
}
} catch (error) {
@@ -91,7 +91,7 @@ export function handleTaskStatusChange(
if (newStatus === "blocked") {
dispatchAutomation("task_blocked", context);
if (task.assigneeAgentId) {
const manager = db.agents.getManagerForAgent(task.assigneeAgentId);
const manager = db.core.agents.getManagerForAgent(task.assigneeAgentId);
if (manager) {
wakeAgentIfPossible(manager.id, companyId, "report_blocked");
}
@@ -100,14 +100,14 @@ export function handleTaskStatusChange(
if (newStatus === "done" && task.parentId) {
try {
const siblings = db.tasks.listChildTasks(task.parentId, companyId);
const siblings = db.core.tasks.listChildTasks(task.parentId, companyId);
const allDone = siblings.every((entry) => entry.id === taskId ? true : entry.status === "done");
if (allDone) {
const parent = db.entityCrud.getTask(task.parentId);
const parent = db.core.entity.getTask(task.parentId);
if (parent.assigneeAgentId) {
wakeAgentIfPossible(parent.assigneeAgentId, companyId, "subtask_completed");
} else {
const assignedId = db.tasks.autoAssignTask(parent.id, companyId);
const assignedId = db.core.tasks.autoAssignTask(parent.id, companyId);
if (assignedId) {
wakeAgentIfPossible(assignedId, companyId, "subtask_completed");
}
@@ -134,9 +134,9 @@ export function handleTaskCreated(
const { db, wakeAgentIfPossible } = deps;
const { dispatchAutomation } = callbacks;
let task: ReturnType<typeof db.entityCrud.getTask> | null = null;
let task: ReturnType<typeof db.core.entity.getTask> | null = null;
try {
task = db.entityCrud.getTask(taskId);
task = db.core.entity.getTask(taskId);
} catch (error) {
logAppError(deps.logger, error, {
kind: "recoverable",
@@ -155,7 +155,7 @@ export function handleTaskCreated(
});
if (!task.assigneeAgentId) {
const assignedId = db.tasks.autoAssignTask(taskId, companyId);
const assignedId = db.core.tasks.autoAssignTask(taskId, companyId);
if (assignedId) {
wakeAgentIfPossible(assignedId, companyId, "assignment");
}
@@ -151,7 +151,7 @@ function checkRunProducedDeliverable(
taskId: string,
taskType: string,
): boolean {
const signals = db.knowledgeMod.collectRunDeliverableSignals(runId, companyId, taskId);
const signals = db.core.knowledge.collectRunDeliverableSignals(runId, companyId, taskId);
return hasValidDeliverableFor(taskType, signals);
}
@@ -181,7 +181,7 @@ function createDeliverableReviewApproval(
): string {
const { db, logger, publishDomainChanged, notify } = deps;
const docTitles = docs.map((d) => d.title).join(", ");
const approvalId = db.approvalsMod.requestApproval({
const approvalId = db.core.approvals.requestApproval({
companyId,
relatedTaskId: taskId,
requestedByAgentId: agentId,
@@ -190,10 +190,10 @@ function createDeliverableReviewApproval(
impactSummary: reviewerNote ?? "Agent completed work with important deliverables. Awaiting your final review.",
payloadJson: JSON.stringify({ documentIds: docs.map((d) => d.id), documents: docs }),
});
db.tasks.setTaskRequiresUserReview(taskId, false);
const agent = db.entityCrud.getAgent(agentId);
db.core.tasks.setTaskRequiresUserReview(taskId, false);
const agent = db.core.entity.getAgent(agentId);
if (agent.heartbeatEnabled) {
db.agents.setHeartbeat(agentId, false, agent.heartbeatIntervalSec || 120);
db.core.agents.setHeartbeat(agentId, false, agent.heartbeatIntervalSec || 120);
logger.info(`[deliverable-review] Paused heartbeat for ${agent.name} — awaiting user review.`);
}
notify({
@@ -228,13 +228,13 @@ function createDeliverableReviewApproval(
*/
function decideTerminalStatus(
db: RunCompletionDeps["db"],
agent: ReturnType<RunCompletionDeps["db"]["entityCrud"]["getAgent"]>,
agent: ReturnType<RunCompletionDeps["db"]["core"]["entity"]["getAgent"]>,
companyId: string,
): "in_review" | "done" {
if (agent.reportsTo !== null) return "in_review";
const mode = db.company.getCompanyExecutionMode(companyId);
const mode = db.core.company.getCompanyExecutionMode(companyId);
if (mode !== "ai_ceo") return "in_review";
const settings = db.company.getCompanyApprovalSettings(companyId);
const settings = db.core.company.getCompanyApprovalSettings(companyId);
return settings?.reviewDeliverables ? "in_review" : "done";
}
@@ -247,7 +247,7 @@ function advanceToDone(
statusMessage: string,
): void {
const { db, publishDomainChanged } = deps;
db.tasks.advanceTaskStatus(taskId, "done", statusMessage);
db.core.tasks.advanceTaskStatus(taskId, "done", statusMessage);
callbacks.handleTaskStatusChange(taskId, run.companyId, "in_progress", "done");
publishDomainChanged();
}
@@ -268,22 +268,22 @@ function advanceToInReview(
// expected (`ai_cofounder` mode or `reviewDeliverables = true`), so
// pre-flagging here is the right semantics — the human IS the
// reviewer in those configurations.
const agent = db.entityCrud.getAgent(run.agentId);
const agent = db.core.entity.getAgent(run.agentId);
if (agent.reportsTo === null) {
db.tasks.setTaskRequiresUserReview(taskId, true);
db.core.tasks.setTaskRequiresUserReview(taskId, true);
}
db.tasks.advanceTaskStatus(taskId, "in_review", statusMessage);
db.core.tasks.advanceTaskStatus(taskId, "in_review", statusMessage);
callbacks.handleTaskStatusChange(taskId, run.companyId, "in_progress", "in_review");
const company = db.snap.listSnapshot().companies.find((c) => c.id === run.companyId);
const company = db.projection.snapshot.listSnapshot().companies.find((c) => c.id === run.companyId);
if (company?.reviewDeliverables) {
const importantDocs = findImportantDocsForRun(db, run.id, run.companyId);
if (importantDocs.length > 0) {
db.tasks.setTaskRequiresUserReview(taskId, true);
db.core.tasks.setTaskRequiresUserReview(taskId, true);
if (agent.heartbeatEnabled) {
db.agents.setHeartbeat(agent.id, false, agent.heartbeatIntervalSec || 120);
db.core.agents.setHeartbeat(agent.id, false, agent.heartbeatIntervalSec || 120);
logger.info(`[deliverable-review] Paused heartbeat for ${agent.name} — deliverable flagged for review.`);
}
const chain = db.agents.getChainOfCommand(agent.id);
const chain = db.core.agents.getChainOfCommand(agent.id);
if (chain.length === 0) {
createDeliverableReviewApproval(deps, run.companyId, taskId, run.agentId, importantDocs);
}
@@ -305,7 +305,7 @@ function advanceSuccessfulRun(
taskId: string,
statusMessage: string,
): "in_review" | "done" {
const agent = deps.db.entityCrud.getAgent(run.agentId);
const agent = deps.db.core.entity.getAgent(run.agentId);
const target = decideTerminalStatus(deps.db, agent, run.companyId);
if (target === "done") {
advanceToDone(deps, callbacks, run, taskId, statusMessage);
@@ -327,9 +327,9 @@ export function processRunCompletion(
const { dispatchAutomation, handleTaskStatusChange } = callbacks;
if (!run.taskId) return;
let task: ReturnType<typeof db.entityCrud.getTask> | null = null;
let task: ReturnType<typeof db.core.entity.getTask> | null = null;
try {
task = db.entityCrud.getTask(run.taskId);
task = db.core.entity.getTask(run.taskId);
} catch (error) {
deps.logger.warn(`[automation] processRunCompletion: task ${run.taskId} not found for run ${run.id}:`, error);
return;
@@ -363,7 +363,7 @@ export function processRunCompletion(
// the task in the manager's blocker queue with the diagnostic
// comment attached; the manager patches it back to `todo` (or
// `cancelled`) after deciding how to proceed.
db.tasks.advanceTaskStatus(
db.core.tasks.advanceTaskStatus(
task.id,
"blocked",
`${taskType} task run completed but no deliverable artifact was detected. Flagging for manager review.`,
@@ -371,8 +371,8 @@ export function processRunCompletion(
handleTaskStatusChange(task.id, run.companyId, "in_progress", "blocked");
// Wake manager for review
try {
const agent = db.entityCrud.getAgent(run.agentId);
const chain = db.agents.getChainOfCommand(agent.id);
const agent = db.core.entity.getAgent(run.agentId);
const chain = db.core.agents.getChainOfCommand(agent.id);
if (chain.length > 0) {
wakeAgentIfPossible(chain[0].id, run.companyId, "review_needed");
}
@@ -414,7 +414,7 @@ export function processRunCompletion(
runId: run.id,
};
try {
const agent = db.entityCrud.getAgent(run.agentId);
const agent = db.core.entity.getAgent(run.agentId);
automationContext.department = agent.department ?? undefined;
} catch (err) {
deps.logger.warn(`[automation] getAgent failed for ${run.agentId} during run completion; dropping department from context:`, err instanceof Error ? err.message : String(err));
@@ -424,14 +424,14 @@ export function processRunCompletion(
dispatchAutomation("run_completed", automationContext);
if (task.parentId) {
try {
const siblings = db.tasks.listChildTasks(task.parentId, run.companyId);
const siblings = db.core.tasks.listChildTasks(task.parentId, run.companyId);
const allSiblingsDoneOrReview = siblings.every((entry) => entry.status === "done" || entry.status === "in_review");
if (allSiblingsDoneOrReview) {
const parent = db.entityCrud.getTask(task.parentId);
const parent = db.core.entity.getTask(task.parentId);
if (parent.assigneeAgentId) {
wakeAgentIfPossible(parent.assigneeAgentId, run.companyId, "subtask_completed");
} else {
const assignedId = db.tasks.autoAssignTask(parent.id, run.companyId);
const assignedId = db.core.tasks.autoAssignTask(parent.id, run.companyId);
if (assignedId) {
wakeAgentIfPossible(assignedId, run.companyId, "subtask_completed");
}
@@ -452,7 +452,7 @@ export function processRunCompletion(
// just read the post-run spend and fire if it crosses 80%.
if (isSuccess) {
try {
const agent = db.entityCrud.getAgent(run.agentId);
const agent = db.core.entity.getAgent(run.agentId);
if (agent.budgetMonthlyUsd > 0) {
const utilization = agent.spentMonthlyUsd / agent.budgetMonthlyUsd;
if (utilization >= 0.8) {
@@ -462,7 +462,7 @@ export function processRunCompletion(
});
}
}
const budgetLocale = db.snap.listSnapshot().locale;
const budgetLocale = db.projection.snapshot.listSnapshot().locale;
checkBudgetThreshold(
agent,
notify,
+14 -14
View File
@@ -60,7 +60,7 @@ export function resolveWorkflowStepActor(
companyId: string,
): AgentRecord | null {
if (step.assigneeAgentId) {
const agent = db.entityCrud.getAgent(step.assigneeAgentId);
const agent = db.core.entity.getAgent(step.assigneeAgentId);
if (agent.companyId !== companyId) {
throw new Error(`Workflow step assignee ${step.assigneeAgentId} does not belong to company ${companyId}.`);
}
@@ -68,13 +68,13 @@ export function resolveWorkflowStepActor(
}
if (step.assigneeDepartment) {
const idleAgents = db.agents.getIdleAgentsForDepartment(companyId, step.assigneeDepartment);
const idleAgents = db.core.agents.getIdleAgentsForDepartment(companyId, step.assigneeDepartment);
if (idleAgents.length > 0) {
return db.entityCrud.getAgent(idleAgents[0]!.id);
return db.core.entity.getAgent(idleAgents[0]!.id);
}
}
const snapshot = db.snap.listSnapshot();
const snapshot = db.projection.snapshot.listSnapshot();
return snapshot.agents.find((agent) => agent.companyId === companyId && !agent.reportsTo && agent.status !== "terminated") ?? null;
}
@@ -113,7 +113,7 @@ export async function runWorkflowPipeline(
): Promise<void> {
const { db, publishDomainChanged } = deps;
const workflow = db.automationMod.getWorkflowRecord(workflowId, companyId);
const workflow = db.automation.rules.getWorkflowRecord(workflowId, companyId);
if (!workflow) {
throw new Error("Workflow not found.");
}
@@ -133,7 +133,7 @@ export async function runWorkflowPipeline(
return step;
});
db.automationMod.updateWorkflowExecution({
db.automation.rules.updateWorkflowExecution({
id: workflowId,
companyId,
status: "active",
@@ -147,7 +147,7 @@ export async function runWorkflowPipeline(
for (let index = resumeFrom; index < steps.length; index += 1) {
const step = steps[index]!;
step.status = "running";
db.automationMod.updateWorkflowExecution({
db.automation.rules.updateWorkflowExecution({
id: workflowId,
companyId,
status: "active",
@@ -161,7 +161,7 @@ export async function runWorkflowPipeline(
const { config, ctx } = buildWorkflowActionContext(companyId, step, actor);
if (step.requiresApproval) {
const approvalId = db.approvalsMod.requestApproval({
const approvalId = db.core.approvals.requestApproval({
companyId,
requestedByAgentId: actor?.id ?? null,
relatedTaskId: ctx.taskId ?? null,
@@ -171,7 +171,7 @@ export async function runWorkflowPipeline(
payloadJson: JSON.stringify({ workflowId, stepId: step.id, stepName: step.name }),
});
step.status = "pending";
db.automationMod.updateWorkflowExecution({
db.automation.rules.updateWorkflowExecution({
id: workflowId,
companyId,
status: "paused",
@@ -179,7 +179,7 @@ export async function runWorkflowPipeline(
stepsJson: JSON.stringify(steps),
lastRunAt: new Date().toISOString(),
});
db.activityMod.addActivity({
db.core.activity.addActivity({
companyId,
actor: actor?.id ?? "system",
action: "workflow.paused_for_approval",
@@ -204,7 +204,7 @@ export async function runWorkflowPipeline(
ctx,
);
step.status = "completed";
db.activityMod.addActivity({
db.core.activity.addActivity({
companyId,
actor: actor?.id ?? "system",
action: "workflow.step_completed",
@@ -214,7 +214,7 @@ export async function runWorkflowPipeline(
});
const isLastStep = index === steps.length - 1;
const completionStatus = isLastStep ? "completed" : "active";
db.automationMod.updateWorkflowExecution({
db.automation.rules.updateWorkflowExecution({
id: workflowId,
companyId,
status: completionStatus,
@@ -225,7 +225,7 @@ export async function runWorkflowPipeline(
publishDomainChanged();
} catch (error) {
step.status = "failed";
db.automationMod.updateWorkflowExecution({
db.automation.rules.updateWorkflowExecution({
id: workflowId,
companyId,
status: "failed",
@@ -233,7 +233,7 @@ export async function runWorkflowPipeline(
stepsJson: JSON.stringify(steps),
lastRunAt: new Date().toISOString(),
});
db.activityMod.addActivity({
db.core.activity.addActivity({
companyId,
actor: actor?.id ?? "system",
action: "workflow.step_failed",
+4 -4
View File
@@ -107,7 +107,7 @@ export function createAutomationService({
function evaluateAutomationRules(trigger: string, ctx: AutomationEventContext) {
try {
const rules = db.automationMod.listActiveAutomationRules(ctx.companyId, trigger);
const rules = db.automation.rules.listActiveAutomationRules(ctx.companyId, trigger);
for (const rule of rules) {
if (!matchesConditions(db, rule, ctx, logger)) continue;
const executionKey = JSON.stringify({
@@ -127,7 +127,7 @@ export function createAutomationService({
activeAutomationExecutions.add(executionKey);
try {
executeAutomationAction(actionDeps, actionCallbacks, rule, ctx);
db.automationMod.recordAutomationExecution(
db.automation.rules.recordAutomationExecution(
rule.id,
ctx.companyId,
rule.name,
@@ -145,7 +145,7 @@ export function createAutomationService({
logger.info(`[automation] Fired rule "${rule.name}" (${trigger} → ${rule.action})`);
} catch (error) {
logger.error(`[automation] Failed to execute action for rule "${rule.name}": ${error instanceof Error ? error.message : String(error)}`);
db.automationMod.recordAutomationExecution(
db.automation.rules.recordAutomationExecution(
rule.id,
ctx.companyId,
rule.name,
@@ -164,7 +164,7 @@ export function createAutomationService({
function evaluateWorkflowTriggers(trigger: string, ctx: AutomationEventContext) {
try {
const workflows = db.automationMod.listTriggerableWorkflows(ctx.companyId, trigger);
const workflows = db.automation.rules.listTriggerableWorkflows(ctx.companyId, trigger);
for (const workflow of workflows) {
if (!matchesEventConfig(db, workflow.triggerConfigJson, null, ctx, logger)) continue;
const executionKey = JSON.stringify({