Files
AgentCompany/src/main/ipc-agent-coordination-handlers.ts
T
ZacharyZhang-NY 118e3d8795 Remove final AppDatabase pass-through methods with ESLint guard (C1 part 3/3)
Migrate all remaining pass-through call sites (social, skills, budget,
entityCrud, documents, knowledge, automation, wakeup, backup, threads,
settings, snapshot, connectors, workspaces, activity) to their owning
domain modules. `AppDatabase` now carries only three methods:

  - createHireRequest (multi-module composite: agents + approvalsMod)
  - rollbackConfigRevision (multi-module composite: documents + entityCrud)
  - queryAll (raw-SQL escape hatch, @internal)

The vault/env-binding infrastructure previously passed argument-by-
argument into `entityCrud.saveSecret`/`resolveSecretEnv` now binds at
module construction, letting callers invoke `db.entityCrud.saveSecret({...})`
without carrying vault plumbing.

Structural types that narrowed AppDatabase to a bag of flat methods
(`ConnectorModelPolicyDeps` callers, `CapabilityMatchDb`, test mocks in
budget-enforcement / budget-incidents / task-quality-gates /
failure-escalation / task-quality-gates-sweep) are reshaped to the new
namespace. `connectorPolicyDepsFromDb` replaces the old pattern of
handing the whole AppDatabase to `validateConnectorModelSelection`.

A new ESLint rule in eslint.config.mjs blocks new top-level methods on
AppDatabase so the pass-through layer cannot silently grow back.
`AppDatabase.ts` dropped from 193 lines to 58; `AppDatabaseBase` lost
the 270-line trailing pass-through block.
2026-04-20 13:31:39 -04:00

95 lines
3.0 KiB
TypeScript

import {
IPC_CHANNELS,
agentMessageInputSchema,
markMessageReadSchema,
searchMessagesSchema,
} from "@shared/contracts";
import { fail, ok, type RegisterDomainHandlersDependencies } from "./ipc-domain-common";
import { dispatchAgentMessage } from "./messaging/dispatch";
export function registerAgentCoordinationHandlers({
registerHandle,
db,
ensureCompanyExists,
publishDomainChanged,
emitEvent,
notify,
wakeAgentIfPossible,
onHumanMessage,
}: Pick<
RegisterDomainHandlersDependencies,
| "registerHandle"
| "db"
| "ensureCompanyExists"
| "publishDomainChanged"
| "emitEvent"
| "notify"
| "wakeAgentIfPossible"
| "onHumanMessage"
>) {
registerHandle(IPC_CHANNELS.sendAgentMessage, async (_event, payload) => {
const parsed = agentMessageInputSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
const dispatchResult = dispatchAgentMessage(
{
db,
emitEvent,
notify,
publishDomainChanged,
wakeAgentIfPossible,
onHumanMessage,
},
parsed,
"message",
);
if (!dispatchResult.ok) {
return fail(dispatchResult.error.code, dispatchResult.error.message);
}
return ok({
id: dispatchResult.data.id,
mentionResults: dispatchResult.data.mentionResults,
});
});
registerHandle(IPC_CHANNELS.markMessageRead, async (_event, payload) => {
const parsed = markMessageReadSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
const message = db.messages.getAgentMessage(parsed.messageId, parsed.companyId);
if (!message) {
return fail("MESSAGE_NOT_FOUND", "Message not found in the current company.");
}
if (parsed.readerAgentId && !db.settings.belongsToCompany("agents", parsed.readerAgentId, parsed.companyId)) {
return fail("INVALID_COMPANY_REFERENCE", "The selected message reader does not belong to the current company.");
}
if (parsed.readerAgentId) {
const visibleToReader = db.messages
.listAgentMessages(parsed.companyId, {
agentId: parsed.readerAgentId,
messageId: parsed.messageId,
limit: 1,
})
.some((entry) => entry.id === parsed.messageId);
if (!visibleToReader) {
return fail("MESSAGE_NOT_VISIBLE", "This message is not visible to the selected reader.");
}
}
db.messages.markMessageReadForReader(parsed.messageId, parsed.companyId, parsed.readerAgentId ?? null);
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.searchMessages, async (_event, payload) => {
const parsed = searchMessagesSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
return ok(db.messages.searchMessages(parsed.companyId, parsed.query, {
channel: parsed.channel,
channelTargetId: parsed.channelTargetId,
limit: parsed.limit,
}));
});
}