feat: SessionMemory service with post-sampling hook integration

- SessionMemoryService: threshold-based memory extraction (50K init, 20K update)
- Async extraction via virtual threads with forked agent
- Post-sampling hook in AgentLoop after each model response
- System prompt injection of existing session memory
- /memory session sub-command to view session memory
- AppConfig wiring: bean registration, agent factory, AgentLoop hook

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
abel533
2026-04-05 09:44:28 +08:00
co-authored by Copilot
parent 6088678c4f
commit 6e49c4fdc7
4 changed files with 356 additions and 4 deletions
@@ -48,6 +48,8 @@ public class MemoryCommand implements SlashCommand {
return handleEdit();
} else if (args.equals("user")) {
return showUserMemory();
} else if (args.equals("session")) {
return showSessionMemory();
} else {
return showProjectMemory();
}
@@ -154,4 +156,41 @@ public class MemoryCommand implements SlashCommand {
return AnsiStyle.red(" ✗ Failed to open editor: " + e.getMessage());
}
}
/** 显示会话记忆 (SESSION_MEMORY.md) */
private String showSessionMemory() {
Path projectDir = Path.of(System.getProperty("user.dir"));
String sanitized = projectDir.toAbsolutePath().toString()
.replace(":", "_")
.replace("\\", "_")
.replace("/", "_");
Path memoryFile = Path.of(System.getProperty("user.home"))
.resolve(".claude").resolve("projects").resolve(sanitized)
.resolve("memory").resolve("SESSION_MEMORY.md");
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append(AnsiStyle.bold(" 🧠 Session Memory\n"));
sb.append(" ").append("".repeat(50)).append("\n");
sb.append(" ").append(AnsiStyle.dim("Path: " + memoryFile)).append("\n\n");
if (Files.exists(memoryFile)) {
try {
String content = Files.readString(memoryFile, StandardCharsets.UTF_8);
if (content.isBlank()) {
sb.append(AnsiStyle.dim(" (Session memory is empty)\n"));
} else {
content.lines().forEach(line -> sb.append(" ").append(line).append("\n"));
}
} catch (IOException e) {
sb.append(AnsiStyle.red(" ✗ Read failed: " + e.getMessage() + "\n"));
}
} else {
sb.append(AnsiStyle.dim(" (No session memory yet)\n\n"));
sb.append(AnsiStyle.dim(" Session memory is automatically created after extended conversations.\n"));
sb.append(AnsiStyle.dim(" It captures key decisions, code changes, and discoveries.\n"));
}
return sb.toString();
}
}