mirror of
https://github.com/dtzp555-max/memory-continuity.git
synced 2026-07-21 21:15:07 +00:00
feat: add session logging to memory/sessions/ daily files (v3.1)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -170,6 +170,68 @@ function cleanupMemoryDir(workspaceDir) {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a session summary to the daily session log.
|
||||||
|
* File: memory/sessions/YYYY-MM-DD.md (one per day, append-only)
|
||||||
|
*/
|
||||||
|
function writeSessionLog(workspaceDir, messages, config = {}) {
|
||||||
|
if (config.sessionLogging === false) return;
|
||||||
|
if (!messages || messages.length === 0) return;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const pad = (n) => String(n).padStart(2, "0");
|
||||||
|
const dateStr = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
|
||||||
|
const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}`;
|
||||||
|
|
||||||
|
const sessionsDir = path.join(workspaceDir, "memory", "sessions");
|
||||||
|
fs.mkdirSync(sessionsDir, { recursive: true });
|
||||||
|
|
||||||
|
const logFile = path.join(sessionsDir, `${dateStr}.md`);
|
||||||
|
|
||||||
|
// Extract first meaningful user message as topic
|
||||||
|
let topic = "(no topic)";
|
||||||
|
for (const msg of messages) {
|
||||||
|
if (msg?.role !== "user") continue;
|
||||||
|
const text = typeof msg.content === "string"
|
||||||
|
? msg.content
|
||||||
|
: Array.isArray(msg.content)
|
||||||
|
? msg.content.filter(b => b?.type === "text").map(b => b.text).join("\n")
|
||||||
|
: "";
|
||||||
|
const cleaned = text
|
||||||
|
.replace(/^Conversation info \(untrusted metadata\):[\s\S]*?\n\n/m, "")
|
||||||
|
.replace(/^Sender \(untrusted metadata\):[\s\S]*?\n\n/m, "")
|
||||||
|
.trim();
|
||||||
|
if (cleaned.length > 10) {
|
||||||
|
topic = cleaned.split("\n")[0].slice(0, 120);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count messages by role
|
||||||
|
const userCount = messages.filter(m => m?.role === "user").length;
|
||||||
|
const assistantCount = messages.filter(m => m?.role === "assistant").length;
|
||||||
|
const totalTokens = estimateTokens(
|
||||||
|
messages.map(m => typeof m?.content === "string" ? m.content : "").join("")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build log entry
|
||||||
|
const entry = [
|
||||||
|
`### ${timeStr}`,
|
||||||
|
`- **Topic:** ${topic}`,
|
||||||
|
`- **Messages:** ${userCount} user / ${assistantCount} assistant`,
|
||||||
|
`- **Est. tokens:** ~${totalTokens}`,
|
||||||
|
"",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
// Append to daily log (create with header if new)
|
||||||
|
if (!fs.existsSync(logFile)) {
|
||||||
|
const header = `# Session Log — ${dateStr}\n\n`;
|
||||||
|
fs.writeFileSync(logFile, header + entry, "utf8");
|
||||||
|
} else {
|
||||||
|
fs.appendFileSync(logFile, entry, "utf8");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function extractStateFromMessages(messages) {
|
function extractStateFromMessages(messages) {
|
||||||
if (!messages || messages.length === 0) return null;
|
if (!messages || messages.length === 0) return null;
|
||||||
|
|
||||||
@@ -368,6 +430,9 @@ const plugin = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write session log entry
|
||||||
|
writeSessionLog(ws, messages, config);
|
||||||
|
|
||||||
const existing = readFile(statePath);
|
const existing = readFile(statePath);
|
||||||
const newState = extractStateFromMessages(messages);
|
const newState = extractStateFromMessages(messages);
|
||||||
if (!newState) {
|
if (!newState) {
|
||||||
|
|||||||
+10
-1
@@ -2,7 +2,7 @@
|
|||||||
"id": "memory-continuity",
|
"id": "memory-continuity",
|
||||||
"name": "Memory Continuity",
|
"name": "Memory Continuity",
|
||||||
"description": "Preserves working state across /new, reset, compaction, and gateway restarts via a simple markdown checkpoint file.",
|
"description": "Preserves working state across /new, reset, compaction, and gateway restarts via a simple markdown checkpoint file.",
|
||||||
"version": "3.0.0",
|
"version": "3.1.0",
|
||||||
"source": "https://github.com/dtzp555-max/memory-continuity",
|
"source": "https://github.com/dtzp555-max/memory-continuity",
|
||||||
"configSchema": {
|
"configSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -32,6 +32,11 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"default": [],
|
"default": [],
|
||||||
"description": "Regex patterns to ignore sessions (e.g. cron jobs, subagent noise). Matched against first user message."
|
"description": "Regex patterns to ignore sessions (e.g. cron jobs, subagent noise). Matched against first user message."
|
||||||
|
},
|
||||||
|
"sessionLogging": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true,
|
||||||
|
"description": "Write session summaries to memory/sessions/ daily logs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -55,6 +60,10 @@
|
|||||||
"ignorePatterns": {
|
"ignorePatterns": {
|
||||||
"label": "Ignore patterns",
|
"label": "Ignore patterns",
|
||||||
"help": "Skip state extraction for sessions matching these patterns (regex)"
|
"help": "Skip state extraction for sessions matching these patterns (regex)"
|
||||||
|
},
|
||||||
|
"sessionLogging": {
|
||||||
|
"label": "Session logging",
|
||||||
|
"help": "Append session summaries to daily markdown logs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user