fix(FN-000): align qmd memory lifecycle

This commit is contained in:
gsxdsm
2026-04-17 09:41:05 -07:00
parent 28accdb085
commit 383033fcdc
26 changed files with 388 additions and 73 deletions

View File

@@ -64,7 +64,7 @@ const TABS: { id: TabId; label: string; icon: typeof Activity }[] = [
{ id: "employees", label: "Employees", icon: GitBranch },
{ id: "soul", label: "Soul", icon: Heart },
{ id: "instructions", label: "Instructions", icon: BookOpen },
{ id: "memory", label: "Memory", icon: FileText },
{ id: "memory", label: "Agent Memory", icon: FileText },
{ id: "reflections", label: "Reflections", icon: BarChart3 },
{ id: "performance", label: "Performance", icon: Star },
{ id: "config", label: "Settings", icon: Settings },
@@ -1697,9 +1697,9 @@ function MemoryTab({
return (
<div className="config-tab">
<div className="config-section">
<h3>Memory</h3>
<h3>Agent Memory</h3>
<p className="config-description">
Store accumulated context and learnings for this agent.
Store context that belongs to this agent only. Workspace memory, daily notes, dreams, and qmd search live in project settings under Project Memory.
</p>
{isReadOnly && (
<p className="config-hint" style={{ marginBottom: 12 }}>
@@ -1714,7 +1714,7 @@ function MemoryTab({
id="agent-memory"
className="input"
rows={15}
placeholder="Agent's accumulated knowledge, learnings, and preferences..."
placeholder="Durable preferences, operating habits, and context this agent should carry across tasks..."
value={memory}
readOnly={isReadOnly}
onChange={(e) => {
@@ -1723,7 +1723,7 @@ function MemoryTab({
}}
style={{ fontFamily: "monospace", fontSize: "0.875rem", resize: "vertical" }}
/>
<span className="config-hint">Per-agent memory stores learnings and context the agent has gathered. Max 50,000 characters.</span>
<span className="config-hint">This is injected as Agent Memory in the prompt and kept separate from workspace Project Memory. Max 50,000 characters.</span>
</div>
</div>

View File

@@ -326,12 +326,12 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
/>
</div>
<div className="agent-dialog-field">
<label htmlFor="agent-memory">Memory <span className="agent-dialog-optional">(optional)</span></label>
<label htmlFor="agent-memory">Agent Memory <span className="agent-dialog-optional">(optional)</span></label>
<textarea
id="agent-memory"
className="input"
rows={2}
placeholder="Per-agent memory — stores learnings and context the agent has gathered..."
placeholder="Private to this agent — durable preferences, operating habits, and context it should carry across tasks..."
value={memory}
onChange={e => setMemory(e.target.value)}
/>

View File

@@ -129,13 +129,13 @@ describe("SettingsModal", () => {
query: "pattern",
qmdAvailable: true,
usedFallback: false,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
results: [],
});
mockInstallQmd.mockResolvedValue({
success: true,
qmdAvailable: true,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
});
mockImportSettings.mockResolvedValue({ success: true, globalCount: 0, projectCount: 0 });
mockFetchGlobalConcurrency.mockResolvedValue({ globalMaxConcurrent: 4, currentlyActive: 0, queuedCount: 0, projectsActive: {} });
@@ -151,7 +151,7 @@ describe("SettingsModal", () => {
},
availableBackends: ["file", "readonly", "qmd"],
qmdAvailable: true,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
});
mockUseMemoryBackendStatus.mockReturnValue({
status: {
@@ -165,7 +165,7 @@ describe("SettingsModal", () => {
},
availableBackends: ["file", "readonly", "qmd"],
qmdAvailable: true,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
},
currentBackend: "file",
capabilities: {
@@ -459,7 +459,7 @@ describe("SettingsModal", () => {
},
availableBackends: ["file", "readonly", "qmd"],
qmdAvailable: false,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
},
currentBackend: "qmd",
capabilities: {

View File

@@ -2271,7 +2271,7 @@ export function SettingsModal({
<div className="settings-empty-state memory-status-message">
<span>
qmd is not installed. Search will use local files.
Install indexed retrieval: <code>{backendStatus.qmdInstallCommand || "bun add -g qmd"}</code>
Install indexed retrieval: <code>{backendStatus.qmdInstallCommand || "bun install -g @tobilu/qmd"}</code>
</span>
<button
type="button"

View File

@@ -486,7 +486,7 @@ describe("AgentDetailView", () => {
expect(screen.getByText("Employees")).toBeInTheDocument();
expect(screen.getByText("Soul")).toBeInTheDocument();
expect(screen.getByText("Instructions")).toBeInTheDocument();
expect(screen.getByText("Memory")).toBeInTheDocument();
expect(screen.getByText("Agent Memory")).toBeInTheDocument();
expect(screen.getByText("Settings")).toBeInTheDocument();
});
});

View File

@@ -103,12 +103,12 @@ vi.mock("../../api", () => ({
path: ".fusion/memory/DREAMS.md",
content: "# Compacted Memory\n\nImportant content.",
})),
installQmd: vi.fn(() => Promise.resolve({ success: true, qmdAvailable: true, qmdInstallCommand: "bun add -g qmd" })),
installQmd: vi.fn(() => Promise.resolve({ success: true, qmdAvailable: true, qmdInstallCommand: "bun install -g @tobilu/qmd" })),
testMemoryRetrieval: vi.fn(() => Promise.resolve({
query: "project memory",
qmdAvailable: true,
usedFallback: false,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
results: [],
})),
}));
@@ -127,7 +127,7 @@ vi.mock("../../hooks/useMemoryBackendStatus", () => ({
},
availableBackends: ["file", "readonly", "qmd"],
qmdAvailable: true,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
},
currentBackend: "file",
capabilities: {
@@ -158,7 +158,7 @@ vi.mock("../../hooks/useMemoryBackendStatus", () => ({
},
availableBackends: ["file", "readonly", "qmd"],
qmdAvailable: true,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
},
currentBackend: "file",
capabilities: {

View File

@@ -69,12 +69,12 @@ vi.mock("../../api", () => ({
})),
fetchMemoryFile: vi.fn((path = ".fusion/memory/DREAMS.md") => Promise.resolve({ path, content: "" })),
saveMemoryFile: vi.fn(() => Promise.resolve({ success: true })),
installQmd: vi.fn(() => Promise.resolve({ success: true, qmdAvailable: true, qmdInstallCommand: "bun add -g qmd" })),
installQmd: vi.fn(() => Promise.resolve({ success: true, qmdAvailable: true, qmdInstallCommand: "bun install -g @tobilu/qmd" })),
testMemoryRetrieval: vi.fn(() => Promise.resolve({
query: "project memory",
qmdAvailable: true,
usedFallback: false,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
results: [],
})),
fetchGlobalConcurrency: vi.fn(() => Promise.resolve({ globalMaxConcurrent: 4, currentlyActive: 0, queuedCount: 0, projectsActive: {} })),
@@ -90,7 +90,7 @@ vi.mock("../../api", () => ({
},
availableBackends: ["file", "readonly", "qmd"],
qmdAvailable: true,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
})),
}));
@@ -107,7 +107,7 @@ vi.mock("../../hooks/useMemoryBackendStatus", () => ({
},
availableBackends: ["file", "readonly", "qmd"],
qmdAvailable: true,
qmdInstallCommand: "bun add -g qmd",
qmdInstallCommand: "bun install -g @tobilu/qmd",
},
currentBackend: "file",
capabilities: {