Files
AgentCompany/src/main/ipc-core-operations-handlers.ts
T
ZacharyZhang-NY 40040b3dd2 refactor(errors): surface 57 deferred catch-and-swallow sites via logger
The earlier sweep cleaned the hot path (worker/runtime/automation/api-
server/ipc core — 59 sites). This pass finishes the deferred long tail:
main/* services, database/* CRUD modules, api-server/* routes, ipc-*
handlers, thread-integration, automation helpers, browser, skills.

Default strategy: catch (error) { logger.warn("[module] context:", ...) }
— preserve current behavior, make the failure visible instead of
silently hidden. Widened 3 logger interface signatures to include warn
where callers only passed info/error previously (thread-helpers,
host-environment-service, ipc-domain-common).

Legitimate boundaries left intact: JSON.parse on untrusted input, DNS
resolver no-records paths, explicit safe* helpers (safeJson, parseJson,
parseTriggerDetail), URL-parse predicates, and `<unserialisable>`
fallbacks inside the prompt-builder summarisers. Each kept catch now
either has a reason in context or is a documented safe* helper.

Tests adjusted only where test-constructed no-op loggers needed a warn
stub (message-dispatch, company-mode-switch, ipc-agent-graph-handlers).

With the earlier pass, AgentCompany no longer has silently-swallowing
catch blocks in main-process or renderer business code.
2026-04-17 15:25:13 -04:00

311 lines
12 KiB
TypeScript

import {
IPC_CHANNELS,
approvalDecisionSchema,
approvalInputSchema,
cancelRunSchema,
commentInputSchema,
heartbeatPolicySchema,
heartbeatSettingsSchema,
listCommentsSchema,
listRunEventsSchema,
runtimeStateSchema,
runLogChunkSchema,
runLogSchema,
setHeartbeatPolicySchema,
setRuntimeStateSchema,
startRunSchema,
triggerHeartbeatSchema,
} from "@shared/contracts";
import { resolveApprovalDecision, shouldAutoApproveApproval } from "./approval-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 (shouldAutoApproveApproval(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.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.listComments, async (_event, payload) => {
const parsed = listCommentsSchema.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 ok(db.listComments(parsed.taskId));
});
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.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);
});
registerHandle(IPC_CHANNELS.listRunEvents, async (_event, payload) => {
const parsed = listRunEventsSchema.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.listRunEvents(parsed.runId, parsed.companyId, { offset: parsed.offset, limit: parsed.limit }));
});
registerHandle(IPC_CHANNELS.getHeartbeatPolicy, async (_event, payload) => {
const parsed = heartbeatPolicySchema.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.");
}
return ok(db.getHeartbeatPolicy(parsed.agentId));
});
registerHandle(IPC_CHANNELS.setHeartbeatPolicy, async (_event, payload) => {
const parsed = setHeartbeatPolicySchema.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 { companyId, agentId, ...policy } = parsed;
db.setHeartbeatPolicy(agentId, companyId, policy);
publishDomainChanged();
return ok(null);
});
registerHandle(IPC_CHANNELS.getRuntimeState, async (_event, payload) => {
const parsed = runtimeStateSchema.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.");
}
return ok(db.getRuntimeState(parsed.agentId));
});
registerHandle(IPC_CHANNELS.setRuntimeState, async (_event, payload) => {
const parsed = setRuntimeStateSchema.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.setRuntimeState(parsed.agentId, parsed.companyId, parsed.state);
return ok(null);
});
}