feat(FN-3485): align memory scope guidance and dashboard tooling

- Clarify task memory scope behavior across core types, project memory logic, engine tool prompts, and related docs
- Add regression coverage for memory scope and runtime plugin alias handling in core/dashboard/engine tests
- Add agent avatar API routes and dashboard UI support for avatar display and storage documentation
- Add line-number gutter toggle support in FileEditor and Files modal with accompanying component tests
- Include changeset for @runfusion/fusion documenting memory scope guidance update

Fusion-Task-Id: FN-3485
This commit is contained in:
Fusion
2026-05-05 16:23:36 -07:00
committed by gsxdsm
parent c5039eb52f
commit ff66c20417
11 changed files with 57 additions and 32 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Clarify runtime memory guidance so agents explicitly distinguish private `scope="agent"` memory from shared `scope="project"` memory in prompts and tool metadata.

View File

@@ -167,6 +167,9 @@ When engine sessions include per-agent memory context, the memory tools operate
Runtime behavior:
- `fn_memory_append` supports dual scope writes:
- `scope="agent"` for private per-agent operating context (personal playbooks/checklists, self-management notes)
- `scope="project"` for shared repo-wide durable knowledge (architecture constraints, conventions, pitfalls)
- `fn_memory_search` can surface snippets from:
- `.fusion/agent-memory/{agentId}/MEMORY.md` (long-term)
- `.fusion/agent-memory/{agentId}/DREAMS.md` (synthesized patterns)

View File

@@ -16,7 +16,7 @@ These tools are **not** part of the user-invokable extension surface. They are i
| `fn_task_document_read` | triage, executor, heartbeat | Read one task document or list all | `key?` (string) |
| `fn_memory_search` | triage, executor, heartbeat | Search project memory plus per-agent layered memory snippets | `query` (string), `limit?` (number) |
| `fn_memory_get` | triage, executor, heartbeat | Read a bounded memory file window (including bounded per-agent layered paths) | `path` (string), `startLine?` (number), `lineCount?` (number) |
| `fn_memory_append` | executor, heartbeat (when writable backend enabled) | Append long-term/daily memory notes | `scope?` (`project` \| `agent`), `layer` (`long-term` \| `daily`), `content` (string) |
| `fn_memory_append` | executor, heartbeat (when writable backend enabled) | Append memory notes with explicit scope: `scope="agent"` for private operating context, `scope="project"` for workspace-wide durable knowledge | `scope?` (`project` \| `agent`), `layer` (`long-term` \| `daily`), `content` (string) |
| `fn_research_run` | triage, executor | Start a bounded research run (optionally wait for completion) and return structured findings metadata | `query` (string), `wait_for_completion?` (boolean), `max_wait_ms?` (number) |
| `fn_research_list` | triage, executor | List recent research runs with status/summary metadata | `status?` (`pending` \| `running` \| `completed` \| `failed` \| `cancelled`), `limit?` (number) |
| `fn_research_get` | triage, executor | Read one research run's structured findings/citations payload | `id` (string) |

View File

@@ -250,6 +250,19 @@ describe("project-memory", () => {
expect(instructions).toMatch(/consolidate|update.*refine.*existing|edit.*existing/i);
});
it("includes explicit agent-vs-project memory scope guidance", () => {
const instructions = buildExecutionMemoryInstructions(testDir, { memoryBackendType: "file" });
expect(instructions).toContain('fn_memory_append(scope="agent")');
expect(instructions).toContain('fn_memory_append(scope="project")');
expect(instructions).toMatch(/private\/ephemeral|private operating context/i);
});
it("includes layer guidance for long-term vs daily memory", () => {
const instructions = buildExecutionMemoryInstructions(testDir, { memoryBackendType: "qmd" });
expect(instructions).toContain('layer="long-term"');
expect(instructions).toContain('layer="daily"');
});
it("keeps qmd default path-agnostic", () => {
const instructions = buildExecutionMemoryInstructions(testDir);
expect(instructions).not.toContain("`.fusion/memory/MEMORY.md`");

View File

@@ -455,20 +455,17 @@ This project has OpenClaw-style memory files:
**At the end of execution (before calling \`fn_task_done()\`):**
1. Review what you learned during this task that would genuinely benefit future runs
2. Write durable decisions, conventions, and pitfalls to \`.fusion/memory/MEMORY.md\`
3. Write running observations, unresolved context, and open loops to today's \`.fusion/memory/YYYY-MM-DD.md\`
4. **If nothing durable was learned, skip the memory update entirely** — do not append trivial or task-specific notes
5. Only write when you have genuinely durable, reusable insights such as:
- New architectural patterns or module boundaries discovered
- Conventions or standards that should be followed
- Pitfalls or anti-patterns to avoid in future work
- Important constraints or context that affects implementation decisions
6. **Avoid** writing task-specific trivia such as:
- Per-task implementation logs or changelog entries
- Transient failures resolved without broader lessons
- One-off file paths, variable names, or minor code changes
- Notes about what you did rather than what future agents should know
7. **Consolidate when possible**: If an existing entry already covers a concept, update or refine it rather than adding a duplicate. Delete entries that are no longer accurate.
2. Choose scope intentionally:
- Use \`fn_memory_append(scope="agent")\` for your private operating context (personal checklists, delegation habits, temporary playbooks, self-improvement notes)
- Use \`fn_memory_append(scope="project")\` for repository-wide durable knowledge any future agent should know
3. Choose layer intentionally:
- \`layer="long-term"\` for durable conventions/decisions/pitfalls
- \`layer="daily"\` for running observations, unresolved context, and open loops
4. If using project scope with file backend, write long-term memory to \`.fusion/memory/MEMORY.md\` and daily notes to today's \`.fusion/memory/YYYY-MM-DD.md\`
5. **If nothing durable was learned, skip the memory update entirely** — do not append trivial or task-specific notes
6. Only write to **project** memory when the insight is genuinely reusable across the workspace (architecture patterns, shared conventions, durable pitfalls, cross-task constraints)
7. **Do not** write private/ephemeral items to project memory, such as personal TODOs, one-off scratch notes, or preferences that only help you as an individual agent
8. **Consolidate when possible**: If an existing entry already covers a concept, update or refine it rather than adding a duplicate. Delete entries that are no longer accurate.
**Format for additions:** Add bullet points under the relevant section heading:
- Use \`- \` prefix for list items
@@ -490,18 +487,15 @@ This project has a memory system that stores durable project learnings accumulat
**At the end of execution (before calling \`fn_task_done()\`):**
1. Review what you learned during this task that would genuinely benefit future runs
2. **If nothing durable was learned, skip the memory update entirely** — do not append trivial or task-specific notes
3. Only write when you have genuinely durable, reusable insights such as:
- New architectural patterns or module boundaries discovered
- Conventions or standards that should be followed
- Pitfalls or anti-patterns to avoid in future work
- Important constraints or context that affects implementation decisions
4. **Avoid** writing task-specific trivia such as:
- Per-task implementation logs or changelog entries
- Transient failures resolved without broader lessons
- One-off file paths, variable names, or minor code changes
- Notes about what you did rather than what future agents should know
5. Consolidate when possible: refine an existing memory entry instead of adding duplicates.
2. Choose scope intentionally:
- Use \`fn_memory_append(scope="agent")\` for your private operating context
- Use \`fn_memory_append(scope="project")\` only for repo-wide durable knowledge
3. Choose layer intentionally:
- \`layer="long-term"\` for durable conventions/decisions/pitfalls
- \`layer="daily"\` for running observations and open loops
4. **If nothing durable was learned, skip the memory update entirely** — do not append trivial or task-specific notes
5. **Avoid task-specific trivia** in project scope (for example: personal reminders, one-off scratch thoughts, individual communication preferences)
6. Consolidate when possible: refine an existing memory entry instead of adding duplicates.
`;
}

View File

@@ -237,11 +237,15 @@ describe("createMemoryTools", () => {
});
it("includes fn_memory_append for writable memory backends", () => {
expect(createMemoryTools("/repo", { memoryBackendType: "file" }).map((tool) => tool.name)).toEqual([
const tools = createMemoryTools("/repo", { memoryBackendType: "file" });
expect(tools.map((tool) => tool.name)).toEqual([
"fn_memory_search",
"fn_memory_get",
"fn_memory_append",
]);
const appendTool = tools.find((tool) => tool.name === "fn_memory_append");
expect(appendTool?.description).toContain('scope="agent"');
expect(appendTool?.description).toContain('scope="project"');
});
it("searches per-agent memory through the fn_memory_search tool", async () => {

View File

@@ -2751,6 +2751,9 @@ describe("buildExecutionPrompt", () => {
// Should instruct selective writes, not unconditional appends
expect(result).toMatch(/skip.*memory.*update|selectively|durable.*learnings/i);
expect(result).toMatch(/end of execution|before calling.*fn_task_done/i);
// Should distinguish agent-private vs project-shared memory scope
expect(result).toContain('fn_memory_append(scope="agent")');
expect(result).toContain('fn_memory_append(scope="project")');
// Should forbid task-specific trivia
expect(result).toMatch(/avoid.*trivia|task-specific.*trivia|per-task.*log/i);
// Should allow consolidation/editing

View File

@@ -622,6 +622,8 @@ describe("executeHeartbeat", () => {
expect(systemPrompt).toContain("fn_send_message");
expect(systemPrompt).toContain("fn_memory_search");
expect(systemPrompt).toContain("fn_memory_append");
expect(systemPrompt).toContain('scope="agent"');
expect(systemPrompt).toContain('scope="project"');
expect(systemPrompt).toContain("fn_heartbeat_done");
});

View File

@@ -319,7 +319,7 @@ You have coding-capable workspace tools (read/write/edit/bash within worktree bo
Use this decision rule:
- **fn_task_create:** create executable work when ownership is not predetermined.
- **fn_delegate_task:** assign immediately when a specific agent should own the work now.
- **fn_memory_append:** persist durable conventions/pitfalls; avoid transient run-by-run chatter.
- **fn_memory_append:** use \`scope="agent"\` for your own operating context and \`scope="project"\` for repo-wide durable knowledge; avoid transient run-by-run chatter.
If unsure who should do the work, prefer fn_task_create and let scheduler routing happen naturally.

View File

@@ -851,8 +851,8 @@ export function createMemoryAppendTool(rootDir: string, settings?: MemoryToolSet
name: "fn_memory_append",
label: "Append Memory",
description:
"Append concise Markdown to project memory. Use long-term only for durable conventions/decisions/pitfalls; " +
"use daily for running observations and open loops. Skip this tool when there is no reusable memory.",
"Append concise Markdown to memory. Use scope=\"agent\" for private operating context and scope=\"project\" for workspace-wide durable knowledge. " +
"Use layer=\"long-term\" for durable conventions/decisions/pitfalls and layer=\"daily\" for running observations/open loops.",
parameters: memoryAppendParams,
execute: async (_id: string, params: Static<typeof memoryAppendParams>) => {
const content = params.content.trim();

View File

@@ -363,7 +363,8 @@ async function flushMemoryBeforeSessionCompaction(session: AgentSession): Promis
const flushPrompt = [
"Before context compaction, preserve only unresolved durable memory if needed.",
"If fn_memory_append is available and you learned reusable project decisions, conventions, pitfalls, or open loops that are not already saved, append them now.",
"If fn_memory_append is available and you learned reusable project decisions/conventions/pitfalls/open loops or private operating context that is not already saved, append it now.",
"Use scope=\"project\" for shared workspace knowledge and scope=\"agent\" for private operating context.",
"Use layer=\"long-term\" for durable facts and layer=\"daily\" for running notes/open loops.",
"If there is nothing durable to save, reply exactly: NONE.",
].join("\n");