-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Description
Task: Structured memory with USER.md and character limits
Description
Split agent memory into two files: MEMORY.md (agent notes, environment facts, lessons learned) and USER.md (user preferences, habits, personal details). Both live in the group's directory alongside the existing CLAUDE.md. Inject both files into the agent's system prompt at session start via the container runner. Character limits force the agent to consolidate entries rather than growing unbounded.
Acceptance Criteria
- USER.md is created alongside MEMORY.md in
groups/{name}/when a group is first registered (or on first agent invocation if missing) - Both files are injected into the system prompt append string in
container/agent-runner/src/index.ts(via the global CLAUDE.md injection path or container-runner mount) - MEMORY.md has a 3000-character limit documented in the injected prompt instructions
- USER.md has a 2000-character limit documented in the injected prompt instructions
- Character limits are enforced by prompt instructions (agent-driven consolidation), not by code truncation
- Files use atomic writes (write to
.tmpthen rename) to prevent corruption from concurrent access - Existing MEMORY.md content is preserved — no data loss on upgrade
- Groups without USER.md get an empty one created automatically
Technical Details
System prompt injection
In container/agent-runner/src/index.ts, the globalClaudeMd variable already reads /workspace/global/CLAUDE.md. Extend this to also read /workspace/group/USER.md and /workspace/group/MEMORY.md:
// After loading globalClaudeMd:
const userMdPath = '/workspace/group/USER.md';
const memoryMdPath = '/workspace/group/MEMORY.md';
const userMd = fs.existsSync(userMdPath) ? fs.readFileSync(userMdPath, 'utf-8') : '';
const memoryMd = fs.existsSync(memoryMdPath) ? fs.readFileSync(memoryMdPath, 'utf-8') : '';
// Append memory context to system prompt
const memoryContext = [
userMd ? `\n## User Profile (USER.md — max 2000 chars)\n${userMd}` : '',
memoryMd ? `\n## Agent Memory (MEMORY.md — max 3000 chars)\n${memoryMd}` : '',
].filter(Boolean).join('\n');Memory instructions (added to system prompt append)
## Memory Management Instructions
- Save user preferences, habits, and personal details to USER.md (max 2000 chars)
- Save environment facts, project conventions, and lessons learned to MEMORY.md (max 3000 chars)
- When a memory file exceeds 80% of its character limit, consolidate related entries and remove stale/redundant ones before adding new content
- Use atomic writes: write to a .tmp file then rename to avoid corruption
- Mid-session: write to disk immediately but note that the system prompt snapshot is frozen at session startContainer runner mounts
Verify that groups/{name}/ is already mounted writable at /workspace/group/ — it should be from existing architecture. USER.md and MEMORY.md are just files in that directory.
Auto-creation
In src/container-runner.ts buildVolumeMounts() or before runContainerAgent(), ensure USER.md exists:
const userMdPath = path.join(groupDir, 'USER.md');
if (!fs.existsSync(userMdPath)) {
fs.writeFileSync(userMdPath, '# User Profile\n\n_No preferences saved yet._\n');
}Files affected
container/agent-runner/src/index.ts— read USER.md + MEMORY.md, append to system promptsrc/container-runner.ts— ensure USER.md exists before agent spawn
Dependencies
- None — uses existing file mounts and system prompt injection
Effort Estimate
- Size: S
- Hours: 3
- Parallel: true (independent of search tasks)
Definition of Done
- USER.md created automatically for groups that don't have one
- Both files injected into system prompt with character limit instructions
- Existing MEMORY.md preserved intact
-
npm run buildsucceeds for both host and agent-runner - Manual test: agent saves user preference to USER.md, persists across sessions
Part of Epic: #907