diff --git a/src/main/app/ipc-contracts/collaboration.ts b/src/main/app/ipc-contracts/collaboration.ts index b3167174..49023c09 100644 --- a/src/main/app/ipc-contracts/collaboration.ts +++ b/src/main/app/ipc-contracts/collaboration.ts @@ -5,7 +5,6 @@ const id = z.string(); const nullableId = z.string().nullable(); const actor = z.enum(["employee", "agent"]); const sender = z.enum(["employee", "agent", "board", "system"]); -const settings = z.record(z.string(), z.unknown()); export const collaborationContracts = { createDocument: z.strictObject({ @@ -27,15 +26,6 @@ export const collaborationContracts = { }) satisfies z.ZodType, deleteKnowledgeEntry: z.strictObject({ orgId: id, entryId: id, deletedByEmployeeId: id }) satisfies z.ZodType, listKnowledgeRevisions: z.strictObject({ orgId: id, entryId: id }) satisfies z.ZodType, - createAutomationRule: z.strictObject({ - orgId: id, name: z.string(), triggerEvent: z.string(), conditions: settings, actionType: z.string(), actionConfig: settings, - priority: z.number(), enabled: z.boolean(), - }) satisfies z.ZodType, - updateAutomationRule: z.strictObject({ - orgId: id, ruleId: id, name: z.string(), triggerEvent: z.string(), conditions: settings, actionType: z.string(), actionConfig: settings, - priority: z.number(), enabled: z.boolean(), - }) satisfies z.ZodType, - deleteAutomationRule: z.strictObject({ orgId: id, ruleId: id }) satisfies z.ZodType, createMeeting: z.strictObject({ orgId: id, title: z.string(), meetingType: z.string(), scheduledAt: z.string().nullable(), durationMinutes: z.number(), agenda: z.string(), }) satisfies z.ZodType, diff --git a/src/main/app/ipc-handlers.ts b/src/main/app/ipc-handlers.ts index 71940ee5..c2cd1f0b 100644 --- a/src/main/app/ipc-handlers.ts +++ b/src/main/app/ipc-handlers.ts @@ -23,7 +23,6 @@ import { OperationsService } from "../services/operations-service"; import { TaskExecutionService } from "../services/task-execution-service"; import { DocumentService } from "../services/document-service"; import { KnowledgeService } from "../services/knowledge-service"; -import { AutomationService } from "../services/automation-service"; import { MeetingService } from "../services/meeting-service"; import { SprintService } from "../services/sprint-service"; import { ComposioConnectionService } from "../services/composio/composio-connection-service"; @@ -92,7 +91,6 @@ export function registerIpcHandlers(deps: IpcDeps): void { const operations = new OperationsService(deps.connection.db); const documents = new DocumentService(deps.connection.db); const knowledge = new KnowledgeService(deps.connection.db); - const automation = new AutomationService(deps.connection.db); const meetingService = new MeetingService(deps.connection.db); const sprintService = new SprintService(deps.connection.db); const onboarding = new OnboardingService({ @@ -242,9 +240,6 @@ export function registerIpcHandlers(deps: IpcDeps): void { revision: r.revision, topic: r.topic, category: r.category, content: r.content, importance: r.importance, changedByType: r.changed_by_type, changedById: r.changed_by_id, reason: r.reason, createdAt: r.created_at, })))); - handle("createAutomationRule", (input) => mutate(() => automation.create(input))); - handle("updateAutomationRule", (input) => mutate(() => automation.update(input))); - handle("deleteAutomationRule", (input) => mutate(() => automation.delete(input))); handle("createMeeting", (input) => mutate(() => meetingService.create(input))); handle("deleteMeeting", (input) => mutate(() => meetingService.delete(input))); handle("createSprint", (input) => mutate(() => sprintService.create(input))); diff --git a/src/main/database/migrations/108-drop-automation-rules.ts b/src/main/database/migrations/108-drop-automation-rules.ts new file mode 100644 index 00000000..12607cf4 --- /dev/null +++ b/src/main/database/migrations/108-drop-automation-rules.ts @@ -0,0 +1,5 @@ +// Automation rules were saved but never ran; the records are removed rather than kept as a promise. +export const dropAutomationRulesSchema = ` + drop index if exists idx_automation_org; + drop table if exists automation_rules; +`; diff --git a/src/main/database/schema.ts b/src/main/database/schema.ts index 0844a5f7..526b295e 100644 --- a/src/main/database/schema.ts +++ b/src/main/database/schema.ts @@ -106,6 +106,7 @@ import { workLanguageSchema } from "./migrations/104-work-language"; import { onboardingProgressSchema } from "./migrations/105-onboarding-progress"; import { viewStateSchema } from "./migrations/106-view-state"; import { searchPendingSchema } from "./migrations/107-search-pending"; +import { dropAutomationRulesSchema } from "./migrations/108-drop-automation-rules"; interface Migration { version: number; @@ -221,6 +222,7 @@ const MIGRATIONS: Migration[] = [ { version: 105, name: "onboarding_progress", sql: onboardingProgressSchema }, { version: 106, name: "view_state", sql: viewStateSchema }, { version: 107, name: "search_pending", sql: searchPendingSchema }, + { version: 108, name: "drop_automation_rules", sql: dropAutomationRulesSchema }, ]; const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version; diff --git a/src/main/services/automation-service.ts b/src/main/services/automation-service.ts deleted file mode 100644 index a7d50aed..00000000 --- a/src/main/services/automation-service.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { DatabaseSync } from "node:sqlite"; -import type { CreateAutomationRuleInput, UpdateAutomationRuleInput, DeleteAutomationRuleInput } from "../../shared/commands"; -import { createId } from "../../shared/ids"; -import { AuditService } from "./audit-service"; - -export class AutomationService { - private readonly audit: AuditService; - - constructor(private readonly db: DatabaseSync) { - this.audit = new AuditService(db); - } - - create(input: CreateAutomationRuleInput): string { - const id = createId("aut"); - const now = new Date().toISOString(); - this.db.prepare(` - insert into automation_rules(id, org_id, name, trigger_event, condition_json, action_type, action_config_json, priority, enabled, fire_count, created_at, updated_at) - values (?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?) - `).run(id, input.orgId, input.name.trim(), input.triggerEvent, JSON.stringify(input.conditions), input.actionType, JSON.stringify(input.actionConfig), input.priority, input.enabled ? 1 : 0, now, now); - this.audit.record({ - orgId: input.orgId, - entityType: "automation_rule", - entityId: id, - actorType: "system", - actorId: null, - action: "created", - payload: { name: input.name.trim(), triggerEvent: input.triggerEvent }, - }); - return id; - } - - update(input: UpdateAutomationRuleInput): void { - const result = this.db.prepare(` - update automation_rules set name = ?, trigger_event = ?, condition_json = ?, action_type = ?, action_config_json = ?, priority = ?, enabled = ?, updated_at = ? - where id = ? and org_id = ? - `).run(input.name.trim(), input.triggerEvent, JSON.stringify(input.conditions), input.actionType, JSON.stringify(input.actionConfig), input.priority, input.enabled ? 1 : 0, new Date().toISOString(), input.ruleId, input.orgId); - if (result.changes === 0) throw new Error("Automation rule was not found."); - } - - delete(input: DeleteAutomationRuleInput): void { - const result = this.db.prepare("delete from automation_rules where id = ? and org_id = ?").run(input.ruleId, input.orgId); - if (result.changes === 0) throw new Error("Automation rule was not found."); - } -} diff --git a/src/main/services/snapshot-service.ts b/src/main/services/snapshot-service.ts index 4572b37b..76c4b736 100644 --- a/src/main/services/snapshot-service.ts +++ b/src/main/services/snapshot-service.ts @@ -151,8 +151,6 @@ export class SnapshotService { auditEvents: recentInOrg(this.db, "audit_events", "created_at desc", 200, activeOrgId), documents: list(this.db, "documents", "updated_at desc"), knowledgeEntries: list(this.db, "knowledge_entries", "updated_at desc"), - automationRules: list(this.db, "automation_rules", "priority asc") - .map((rule) => ({ ...rule, enabled: toBool(rule.enabled) })), meetings: list(this.db, "meetings", "scheduled_at desc"), sprints: list(this.db, "sprints", "start_date desc"), connectorAccounts: list(this.db, "connector_accounts", "toolkit_slug asc"), diff --git a/src/preload/index.ts b/src/preload/index.ts index 20d21400..85ca5ac1 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -44,9 +44,6 @@ const api: DesktopApi = { updateKnowledgeEntry: (input) => invoke(IPC_CHANNELS.updateKnowledgeEntry, input), listDocumentVersions: (input) => invoke(IPC_CHANNELS.listDocumentVersions, input), listKnowledgeRevisions: (input) => invoke(IPC_CHANNELS.listKnowledgeRevisions, input), - createAutomationRule: (input) => invoke(IPC_CHANNELS.createAutomationRule, input), - updateAutomationRule: (input) => invoke(IPC_CHANNELS.updateAutomationRule, input), - deleteAutomationRule: (input) => invoke(IPC_CHANNELS.deleteAutomationRule, input), createMeeting: (input) => invoke(IPC_CHANNELS.createMeeting, input), deleteMeeting: (input) => invoke(IPC_CHANNELS.deleteMeeting, input), createSprint: (input) => invoke(IPC_CHANNELS.createSprint, input), diff --git a/src/shared/commands/collaboration.ts b/src/shared/commands/collaboration.ts index f7d954ae..6d89ec2b 100644 --- a/src/shared/commands/collaboration.ts +++ b/src/shared/commands/collaboration.ts @@ -104,34 +104,6 @@ export interface DeleteKnowledgeEntryInput { deletedByEmployeeId: string; } -export interface CreateAutomationRuleInput { - orgId: string; - name: string; - triggerEvent: string; - conditions: Record; - actionType: string; - actionConfig: Record; - priority: number; - enabled: boolean; -} - -export interface UpdateAutomationRuleInput { - orgId: string; - ruleId: string; - name: string; - triggerEvent: string; - conditions: Record; - actionType: string; - actionConfig: Record; - priority: number; - enabled: boolean; -} - -export interface DeleteAutomationRuleInput { - orgId: string; - ruleId: string; -} - export interface CreateMeetingInput { orgId: string; title: string; diff --git a/src/shared/desktop-api.ts b/src/shared/desktop-api.ts index 78a506ef..b48439d6 100644 --- a/src/shared/desktop-api.ts +++ b/src/shared/desktop-api.ts @@ -17,7 +17,6 @@ import type { UpdateAgentInput, UpdateEmployeeProfileInput, CreateApprovalInput, - CreateAutomationRuleInput, CreateDocumentInput, CreateGoalInput, CreateMeetingInput, @@ -32,7 +31,6 @@ import type { ListKnowledgeRevisionsInput, CreateProjectInput, CreateWorkspaceInput, - DeleteAutomationRuleInput, DeleteDocumentInput, DeleteTaskInput, SetGoalDueDateInput, @@ -41,7 +39,6 @@ import type { UpdateWorkspaceInput, DeleteKnowledgeEntryInput, ResumeGoalInput, - UpdateAutomationRuleInput, UpdateDocumentInput, UpdateTaskInput, CreateChatSpaceInput, @@ -164,9 +161,6 @@ export interface DesktopApi { updateKnowledgeEntry(input: UpdateKnowledgeEntryInput): Promise>; listDocumentVersions(input: ListDocumentVersionsInput): Promise>; listKnowledgeRevisions(input: ListKnowledgeRevisionsInput): Promise>; - createAutomationRule(input: CreateAutomationRuleInput): Promise>; - updateAutomationRule(input: UpdateAutomationRuleInput): Promise>; - deleteAutomationRule(input: DeleteAutomationRuleInput): Promise>; createMeeting(input: CreateMeetingInput): Promise>; deleteMeeting(input: DeleteMeetingInput): Promise>; createSprint(input: CreateSprintInput): Promise>; diff --git a/src/shared/domain.ts b/src/shared/domain.ts index 2d46f85f..ce5b5fa4 100644 --- a/src/shared/domain.ts +++ b/src/shared/domain.ts @@ -539,18 +539,3 @@ export interface ConnectorGrantRecord { createdAt: string; } -export interface AutomationRuleRecord { - id: string; - orgId: string; - name: string; - triggerEvent: string; - conditionJson: string; - actionType: string; - actionConfigJson: string; - priority: number; - enabled: boolean; - lastFiredAt: string | null; - fireCount: number; - createdAt: string; - updatedAt: string; -} diff --git a/src/shared/ipc.ts b/src/shared/ipc.ts index a68c41c9..3e80af13 100644 --- a/src/shared/ipc.ts +++ b/src/shared/ipc.ts @@ -77,9 +77,6 @@ export const IPC_CHANNELS = { updateKnowledgeEntry: "agentcompany:update-knowledge-entry", listDocumentVersions: "agentcompany:list-document-versions", listKnowledgeRevisions: "agentcompany:list-knowledge-revisions", - createAutomationRule: "agentcompany:create-automation-rule", - updateAutomationRule: "agentcompany:update-automation-rule", - deleteAutomationRule: "agentcompany:delete-automation-rule", createMeeting: "agentcompany:create-meeting", deleteMeeting: "agentcompany:delete-meeting", createSprint: "agentcompany:create-sprint", diff --git a/src/shared/snapshot-domain.ts b/src/shared/snapshot-domain.ts index aea52d42..f35ea324 100644 --- a/src/shared/snapshot-domain.ts +++ b/src/shared/snapshot-domain.ts @@ -19,7 +19,6 @@ import type { AgentRecord, ApprovalRecord, AuditEventRecord, - AutomationRuleRecord, ConnectorAccountRecord, ConnectorGrantRecord, DocumentRecord, @@ -127,7 +126,6 @@ export interface AppSnapshot { auditEvents: AuditEventRecord[]; documents: DocumentRecord[]; knowledgeEntries: KnowledgeEntryRecord[]; - automationRules: AutomationRuleRecord[]; meetings: MeetingRecord[]; sprints: SprintRecord[]; connectorAccounts: ConnectorAccountRecord[]; diff --git a/tests/unit/domain-services.test.ts b/tests/unit/domain-services.test.ts index cbe76a8d..84053a1c 100644 --- a/tests/unit/domain-services.test.ts +++ b/tests/unit/domain-services.test.ts @@ -36,7 +36,7 @@ describe("domain services", () => { const connection = createConnection(); const rows = connection.db.prepare("select version from schema_migrations order by version asc").all() as Array<{ version: number }>; - expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107]); + expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108]); connection.close(); }); diff --git a/tests/unit/unused-records.test.ts b/tests/unit/unused-records.test.ts new file mode 100644 index 00000000..0133f4d9 --- /dev/null +++ b/tests/unit/unused-records.test.ts @@ -0,0 +1,15 @@ +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { randomUUID } from "node:crypto"; +import { expect, test } from "vitest"; +import { DatabaseConnection } from "../../src/main/database/connection"; +import { IPC_CHANNELS } from "../../src/shared/ipc"; + +// A record type nothing runs or shows is not kept: saving it would only claim it works. +test("automation rules are gone: no table, no channels", () => { + const connection = new DatabaseConnection(join(tmpdir(), `agentcompany-new-${randomUUID()}.db`)); + const tables = connection.db.prepare("select name from sqlite_master where type = 'table'").all() as Array<{ name: string }>; + connection.close(); + expect(tables.map((row) => row.name)).not.toContain("automation_rules"); + expect(Object.keys(IPC_CHANNELS).filter((key) => /automation/i.test(key))).toEqual([]); +});