feat(FN-1399): add background memory summarization after task completion

- Add MemoryInsights class in @fusion/core for AI-powered memory audit generation
- Add post-run hook to CronRunner for triggering memory summarization after scheduled tasks
- Wire memory background processing in both dashboard and serve commands
- Add memoryAuditEnabled and memoryAuditSchedule settings for configurable automation
- Fix startup ordering: sync automation before cronRunner.start() to prevent race conditions
- Add comprehensive tests for memory-insights and dashboard/serve integration
- Update contributing.md and settings-reference.md with documentation
This commit is contained in:
gsxdsm
2026-04-09 16:14:44 -07:00
parent 2dd775ebf3
commit 04f835f821
13 changed files with 1803 additions and 4 deletions

View File

@@ -276,6 +276,120 @@ describe("CronRunner", () => {
expect(result.success).toBe(true);
expect(result.output).toContain("err");
});
it("calls recordRun before callback on success", async () => {
const store = createMockStore();
const schedule = createMockSchedule({ command: "echo success" });
const automationStore = createMockAutomationStore([schedule]);
const callOrder: string[] = [];
const onProcessed = vi.fn().mockImplementation(() => {
callOrder.push("callback");
});
runner = new CronRunner(store, automationStore, { onScheduleRunProcessed: onProcessed });
await runner.executeSchedule(schedule);
expect(callOrder).toContain("callback");
// recordRun should have been called first
expect(automationStore.recordRun).toHaveBeenCalledBefore(onProcessed);
});
it("invokes callback on successful execution", async () => {
const store = createMockStore();
const schedule = createMockSchedule({ command: "echo success" });
const automationStore = createMockAutomationStore([schedule]);
const onProcessed = vi.fn();
runner = new CronRunner(store, automationStore, { onScheduleRunProcessed: onProcessed });
await runner.executeSchedule(schedule);
expect(onProcessed).toHaveBeenCalledTimes(1);
expect(onProcessed).toHaveBeenCalledWith(
schedule,
expect.objectContaining({ success: true }),
);
});
it("invokes callback on failed execution", async () => {
const store = createMockStore();
const schedule = createMockSchedule({ command: "exit 1" });
const automationStore = createMockAutomationStore([schedule]);
const onProcessed = vi.fn();
runner = new CronRunner(store, automationStore, { onScheduleRunProcessed: onProcessed });
await runner.executeSchedule(schedule);
expect(onProcessed).toHaveBeenCalledTimes(1);
expect(onProcessed).toHaveBeenCalledWith(
schedule,
expect.objectContaining({ success: false }),
);
});
it("does not invoke callback when not provided", async () => {
const store = createMockStore();
const schedule = createMockSchedule({ command: "echo test" });
const automationStore = createMockAutomationStore([schedule]);
runner = new CronRunner(store, automationStore);
// Should not throw
await runner.executeSchedule(schedule);
});
it("callback errors do not throw or alter returned result", async () => {
const store = createMockStore();
const schedule = createMockSchedule({ command: "echo success" });
const automationStore = createMockAutomationStore([schedule]);
const onProcessed = vi.fn().mockRejectedValue(new Error("callback failed"));
runner = new CronRunner(store, automationStore, { onScheduleRunProcessed: onProcessed });
// Should not throw
const result = await runner.executeSchedule(schedule);
expect(result.success).toBe(true);
expect(result.output).toContain("success");
});
it("callback errors do not prevent recordRun", async () => {
const store = createMockStore();
const schedule = createMockSchedule({ command: "echo test" });
const automationStore = createMockAutomationStore([schedule]);
const onProcessed = vi.fn().mockRejectedValue(new Error("callback failed"));
runner = new CronRunner(store, automationStore, { onScheduleRunProcessed: onProcessed });
await runner.executeSchedule(schedule);
expect(automationStore.recordRun).toHaveBeenCalledTimes(1);
});
it("passes schedule and result to callback", async () => {
const store = createMockStore();
const schedule = createMockSchedule({
id: "custom-id",
name: "Custom Schedule",
command: "echo custom",
});
const automationStore = createMockAutomationStore([schedule]);
let receivedSchedule: ScheduledTask | undefined;
let receivedResult: AutomationRunResult | undefined;
const onProcessed = vi.fn().mockImplementation((s: ScheduledTask, r: AutomationRunResult) => {
receivedSchedule = s;
receivedResult = r;
});
runner = new CronRunner(store, automationStore, { onScheduleRunProcessed: onProcessed });
await runner.executeSchedule(schedule);
expect(receivedSchedule).toEqual(schedule);
expect(receivedResult).toBeDefined();
expect(receivedResult!.success).toBe(true);
});
});
describe("concurrent schedule prevention in tick", () => {