feat(FN-3718): add agent workspace memory reader and include workspace memo

Merged branch lands two features: FN-3718 wires workspace memory into the agent instruction pipeline — adding a reader helper, injecting workspace memory into agent instructions and identity snapshots, and documenting the resolution order — and FN-3716 adds planning mode priority controls for task r

Fusion-Task-Id: FN-3718
This commit is contained in:
Fusion
2026-05-07 16:06:31 -07:00
committed by gsxdsm
parent c6d12ba5af
commit 324ab5d44e
9 changed files with 234 additions and 28 deletions

View File

@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { appendFileSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { appendFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import {
@@ -1881,6 +1881,41 @@ describe("executeHeartbeat", () => {
expect(callArgs.customTools![13]!.name).toBe("fn_heartbeat_done");
});
it("loads workspace memory into system prompt and identity snapshot when inline memory is empty", async () => {
const rootDir = mkdtempSync(join(tmpdir(), "heartbeat-workspace-memory-"));
mkdirSync(join(rootDir, ".fusion", "agent-memory", "agent-001"), { recursive: true });
writeFileSync(
join(rootDir, ".fusion", "agent-memory", "agent-001", "MEMORY.md"),
"workspace memory for heartbeat",
"utf-8",
);
try {
const store = createStoreWithAgentForExec({
memory: "",
instructionsText: undefined,
instructionsPath: undefined,
});
const mockSession = createMockAgentSession();
mockedCreateFnAgent.mockResolvedValue({
session: mockSession as any,
});
const monitor = new HeartbeatMonitor({ store, taskStore: mockTaskStore, rootDir });
await monitor.executeHeartbeat({ agentId: "agent-001", source: "timer" });
const callArgs = mockedCreateFnAgent.mock.calls[0]![0];
expect(callArgs.systemPrompt).toContain("## Agent Memory");
expect(callArgs.systemPrompt).toContain("workspace memory for heartbeat");
const executionPrompt = mockSession.prompt.mock.calls.at(-1)?.[0] as string;
expect(executionPrompt).toMatch(/- memory: loaded \(\d+ chars, sha256:[a-f0-9]{8}, source: workspace\)/);
} finally {
rmSync(rootDir, { recursive: true, force: true });
}
});
it("includes memory instructions even when agent has no custom instructions", async () => {
const store = createStoreWithAgentForExec({
soul: undefined,