Files
AgentCompany/src/main/ipc-content-operations-handlers.ts
T
2026-03-18 01:15:58 -04:00

193 lines
7.2 KiB
TypeScript

import {
IPC_CHANNELS,
deleteEntitySchema,
documentInputSchema,
knowledgeEntryInputSchema,
meetingInputSchema,
sprintInputSchema,
} from "@shared/contracts";
import { fail, ok, type RegisterDomainHandlersDependencies } from "./ipc-domain-common";
const COMPANY_SCOPED_REFERENCE_TABLES = new Set([
"agents",
"goals",
"projects",
"tasks",
"approvals",
"runs",
"secrets",
"meetings",
"documents",
"knowledge_base",
"sprints",
"automation_rules",
"workflow_pipelines",
]);
export function registerContentOperationsHandlers({
registerHandle,
db,
ensureCompanyExists,
findInvalidReference,
publishDomainChanged,
wakeAgentIfPossible,
dispatchAutomation,
}: Pick<
RegisterDomainHandlersDependencies,
| "registerHandle"
| "db"
| "ensureCompanyExists"
| "findInvalidReference"
| "publishDomainChanged"
| "wakeAgentIfPossible"
| "dispatchAutomation"
>) {
registerHandle(IPC_CHANNELS.saveMeeting, async (_event, payload) => {
const parsed = meetingInputSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
let participantAgentIds: string[] = [];
try {
const parsedParticipantIds = JSON.parse(parsed.participantAgentIds);
participantAgentIds = Array.isArray(parsedParticipantIds)
? parsedParticipantIds.filter((value): value is string => typeof value === "string")
: [];
} catch {
return fail("INVALID_PARTICIPANTS", "Meeting participants must be valid JSON.");
}
const invalidReference = findInvalidReference(parsed.companyId, [
{ id: parsed.organizerAgentId, table: "agents", label: "meeting organizer" },
...participantAgentIds.map((agentId) => ({ id: agentId, table: "agents", label: "meeting participant" })),
]);
if (invalidReference) {
return fail("INVALID_COMPANY_REFERENCE", `The selected ${invalidReference.label} does not belong to the current company.`);
}
const id = db.saveMeeting(parsed);
publishDomainChanged();
return ok(id);
});
registerHandle(IPC_CHANNELS.deleteMeeting, async (_event, payload) => {
const parsed = deleteEntitySchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("meetings", parsed.id, parsed.companyId)) {
return fail("ENTITY_NOT_FOUND", "Meeting not found in the current company.");
}
db.deleteMeeting(parsed.id, parsed.companyId);
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.saveDocument, async (_event, payload) => {
const parsed = documentInputSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
const invalidReference = findInvalidReference(parsed.companyId, [
{ id: parsed.authorAgentId, table: "agents", label: "document author" },
{ id: parsed.reviewerAgentId, table: "agents", label: "document reviewer" },
{ id: parsed.projectId, table: "projects", label: "document project" },
{ id: parsed.goalId, table: "goals", label: "document goal" },
{ id: parsed.parentDocId, table: "documents", label: "parent document" },
]);
if (invalidReference) {
return fail("INVALID_COMPANY_REFERENCE", `The selected ${invalidReference.label} does not belong to the current company.`);
}
const isNew = !parsed.id;
const id = db.saveDocument(parsed);
if (isNew) {
dispatchAutomation("document_created", {
companyId: parsed.companyId,
agentId: parsed.authorAgentId ?? undefined,
projectId: parsed.projectId ?? undefined,
});
if (parsed.authorAgentId && parsed.reviewerAgentId) {
wakeAgentIfPossible(parsed.reviewerAgentId, parsed.companyId, "assignment");
}
}
publishDomainChanged();
return ok(id);
});
registerHandle(IPC_CHANNELS.deleteDocument, async (_event, payload) => {
const parsed = deleteEntitySchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("documents", parsed.id, parsed.companyId)) {
return fail("ENTITY_NOT_FOUND", "Document not found in the current company.");
}
db.deleteDocument(parsed.id, parsed.companyId);
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.saveKnowledgeEntry, async (_event, payload) => {
const parsed = knowledgeEntryInputSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
const knowledgeReferences = [
{ id: parsed.authorAgentId, table: "agents", label: "knowledge author" },
...(parsed.referencedEntityId && parsed.referencedEntityType
&& COMPANY_SCOPED_REFERENCE_TABLES.has(parsed.referencedEntityType)
? [{ id: parsed.referencedEntityId, table: parsed.referencedEntityType, label: "referenced entity" }]
: []),
];
const invalidReference = findInvalidReference(parsed.companyId, [
...knowledgeReferences,
]);
if (invalidReference) {
return fail("INVALID_COMPANY_REFERENCE", `The selected ${invalidReference.label} does not belong to the current company.`);
}
const id = db.saveKnowledgeEntry(parsed);
publishDomainChanged();
return ok(id);
});
registerHandle(IPC_CHANNELS.deleteKnowledgeEntry, async (_event, payload) => {
const parsed = deleteEntitySchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("knowledge_base", parsed.id, parsed.companyId)) {
return fail("ENTITY_NOT_FOUND", "Knowledge entry not found in the current company.");
}
db.deleteKnowledgeEntry(parsed.id, parsed.companyId);
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.saveSprint, async (_event, payload) => {
const parsed = sprintInputSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
let previousSprintStatus: string | null = null;
if (parsed.id) {
try {
previousSprintStatus = db.listSnapshot().sprints.find((s: { id: string }) => s.id === parsed.id)?.status ?? null;
} catch {
previousSprintStatus = null;
}
}
const id = db.saveSprint(parsed);
if (parsed.status === "active" && previousSprintStatus !== "active") {
dispatchAutomation("sprint_started", { companyId: parsed.companyId });
}
if (parsed.status === "completed" && previousSprintStatus !== "completed") {
dispatchAutomation("sprint_ended", { companyId: parsed.companyId });
}
publishDomainChanged();
return ok(id);
});
registerHandle(IPC_CHANNELS.deleteSprint, async (_event, payload) => {
const parsed = deleteEntitySchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("sprints", parsed.id, parsed.companyId)) {
return fail("ENTITY_NOT_FOUND", "Sprint not found in the current company.");
}
db.deleteSprint(parsed.id, parsed.companyId);
publishDomainChanged();
return ok(true);
});
}