Files
AgentCompany/src/main/ipc-core-operations-handlers.ts
T
ZacharyZhang-NY 5e9b7acbc6 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.
2026-04-20 13:17:01 -04:00

243 lines
9.1 KiB
TypeScript

import {
IPC_CHANNELS,
approvalDecisionSchema,
approvalInputSchema,
cancelRunSchema,
commentInputSchema,
heartbeatSettingsSchema,
runLogChunkSchema,
runLogSchema,
startRunSchema,
triggerHeartbeatSchema,
} from "@shared/contracts";
import { resolveApprovalDecision } from "./approval-policy";
import { shouldAutoApprove } from "./execution-policy";
import { fail, ok, type RegisterCoreHandlersDependencies } from "./ipc-core-common";
export function registerCoreOperationHandlers({
registerHandle,
db,
ensureCompanyExists,
publishDomainChanged,
emitEvent,
wakeAgentIfPossible,
handleApprovalCreated,
handleApprovalResolved,
dispatchAutomation,
queueTaskRun,
getWorkerProcess,
notify,
logger,
}: Pick<
RegisterCoreHandlersDependencies,
| "registerHandle"
| "db"
| "ensureCompanyExists"
| "publishDomainChanged"
| "emitEvent"
| "wakeAgentIfPossible"
| "handleApprovalCreated"
| "handleApprovalResolved"
| "dispatchAutomation"
| "queueTaskRun"
| "getWorkerProcess"
| "notify"
| "logger"
>) {
registerHandle(IPC_CHANNELS.requestApproval, async (_event, payload) => {
const parsed = approvalInputSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
const invalidTask = parsed.relatedTaskId && !db.belongsToCompany("tasks", parsed.relatedTaskId, parsed.companyId);
const invalidAgent = parsed.requestedByAgentId && !db.belongsToCompany("agents", parsed.requestedByAgentId, parsed.companyId);
const invalidRelatedAgent = parsed.relatedAgentId && !db.belongsToCompany("agents", parsed.relatedAgentId, parsed.companyId);
if (invalidTask || invalidAgent || invalidRelatedAgent) {
return fail("INVALID_COMPANY_REFERENCE", "The approval references data outside the current company.");
}
let id: string;
try {
id = db.requestApproval(parsed);
} catch (error) {
return fail("APPROVAL_INVALID", error instanceof Error ? error.message : "Approval could not be created.");
}
db.addActivity({
companyId: parsed.companyId,
actor: parsed.requestedByAgentId ?? "board",
action: "approval.created",
entityType: "approval",
entityId: id,
detail: parsed.payloadSummary,
});
// Notify thread integration about the new approval
handleApprovalCreated(id, parsed.companyId, parsed.relatedTaskId ?? null, parsed.type, parsed.payloadSummary ?? "");
if (shouldAutoApprove(db, parsed.companyId, parsed.type)) {
resolveApprovalDecision({
db,
handleApprovalResolved,
companyId: parsed.companyId,
approvalId: id,
state: "approved",
decisionNote: "Auto-approved by company policy",
detail: "Auto-approved by company policy",
});
publishDomainChanged();
return ok(id);
}
// Approval was NOT auto-approved — wake decision-makers to review it
dispatchAutomation("approval_created", {
companyId: parsed.companyId,
agentId: parsed.requestedByAgentId ?? undefined,
taskId: parsed.relatedTaskId ?? undefined,
});
notify({
title: "Approval Required",
body: parsed.payloadSummary ?? `${parsed.type} approval pending`,
urgency: "critical",
navigation: { section: "approvals", entityId: id },
batchKey: "pending_approval",
});
publishDomainChanged();
return ok(id);
});
registerHandle(IPC_CHANNELS.decideApproval, async (_event, payload) => {
const parsed = approvalDecisionSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("approvals", parsed.approvalId, parsed.companyId)) {
return fail("APPROVAL_NOT_FOUND", "Approval not found in the current company.");
}
const approval = db.getApprovalRecord(parsed.approvalId);
if (!approval || approval.companyId !== parsed.companyId) {
return fail("APPROVAL_NOT_FOUND", "Approval not found in the current company.");
}
resolveApprovalDecision({
db,
handleApprovalResolved,
companyId: approval.companyId,
approvalId: approval.id,
state: parsed.state,
decisionNote: parsed.decisionNote,
detail: parsed.decisionNote,
});
emitEvent({ type: "approval-updated", approvalId: approval.id, action: "decided" });
return ok(true);
});
registerHandle(IPC_CHANNELS.startTaskRun, async (_event, payload) => {
const parsed = startRunSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("tasks", parsed.taskId, parsed.companyId)) {
return fail("TASK_NOT_FOUND", "Task not found in the current company.");
}
return queueTaskRun(parsed.taskId, "manual", "board");
});
registerHandle(IPC_CHANNELS.cancelRun, (_event, payload) => {
const parsed = cancelRunSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("runs", parsed.runId, parsed.companyId)) {
return fail("RUN_NOT_FOUND", "Run not found in the current company.");
}
getWorkerProcess()?.postMessage({ type: "cancel-run", runId: parsed.runId });
return ok(true);
});
registerHandle(IPC_CHANNELS.getRunLog, (_event, payload) => {
const parsed = runLogSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("runs", parsed.runId, parsed.companyId)) {
return fail("RUN_NOT_FOUND", "Run not found in the current company.");
}
return ok(db.readRunLog(parsed.runId));
});
registerHandle(IPC_CHANNELS.getRunLogChunk, (_event, payload) => {
const parsed = runLogChunkSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("runs", parsed.runId, parsed.companyId)) {
return fail("RUN_NOT_FOUND", "Run not found in the current company.");
}
return ok(db.readRunLogChunk(parsed.runId, parsed.offset, parsed.limit));
});
registerHandle(IPC_CHANNELS.addComment, async (_event, payload) => {
const parsed = commentInputSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
const comment = db.addComment({
companyId: parsed.companyId,
taskId: parsed.taskId,
authorAgentId: parsed.authorAgentId ?? null,
authorName: parsed.authorName,
body: parsed.body,
});
db.addActivity({
companyId: parsed.companyId,
actor: parsed.authorAgentId ?? "board",
action: "comment.created",
entityType: "task",
entityId: parsed.taskId,
detail: parsed.body.slice(0, 100),
});
emitEvent({ type: "agent-message", taskId: parsed.taskId, comment });
emitEvent({ type: "task-updated", taskId: parsed.taskId, action: "updated" });
try {
const task = db.entityCrud.getTask(parsed.taskId);
if (task.assigneeAgentId) {
wakeAgentIfPossible(task.assigneeAgentId, task.companyId, "comment");
}
dispatchAutomation("comment_posted", {
companyId: parsed.companyId,
taskId: parsed.taskId,
agentId: parsed.authorAgentId ?? undefined,
});
} catch (err) {
logger.warn(`[ipc/core-operations] Post-comment wake/dispatch failed for task ${parsed.taskId}:`, err instanceof Error ? err.message : String(err));
}
return ok(comment.id);
});
registerHandle(IPC_CHANNELS.setHeartbeat, async (_event, payload) => {
const parsed = heartbeatSettingsSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("agents", parsed.agentId, parsed.companyId)) {
return fail("AGENT_NOT_FOUND", "Agent not found in the current company.");
}
db.agents.setHeartbeat(parsed.agentId, parsed.enabled, parsed.intervalSec);
db.addActivity({
companyId: parsed.companyId,
actor: "board",
action: parsed.enabled ? "heartbeat.enabled" : "heartbeat.disabled",
entityType: "agent",
entityId: parsed.agentId,
detail: `Interval: ${parsed.intervalSec}s`,
});
emitEvent({ type: "heartbeat-status", agentId: parsed.agentId, enabled: parsed.enabled, nextWakeAt: null });
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.triggerHeartbeat, async (_event, payload) => {
const parsed = triggerHeartbeatSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("agents", parsed.agentId, parsed.companyId)) {
return fail("AGENT_NOT_FOUND", "Agent not found in the current company.");
}
const runId = wakeAgentIfPossible(parsed.agentId, parsed.companyId, parsed.trigger);
if (!runId) {
return fail("HEARTBEAT_SKIPPED", "Agent cannot be woken right now (check connector status, budget, or active runs).");
}
return ok(runId);
});
}