diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d927b6..2b6f25d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixed +- Bound loaded daily summaries to the configured history recap character budget. - Prevent mixed reaction replies from narrating the bot's internal choice to react while preserving natural reaction-plus-text responses. - Run Discord-initiated Claude login in a pseudo-terminal so the CLI accepts submitted OAuth codes. - Isolate saved history and summaries by Discord channel ID in dedicated storage namespaces so same-named channels do not share automatic context. diff --git a/src/storage/summaries.ts b/src/storage/summaries.ts index bea842f..2265389 100644 --- a/src/storage/summaries.ts +++ b/src/storage/summaries.ts @@ -1,6 +1,7 @@ import fs from "fs"; import { CLAUDE_WORKLOAD_CONFIG, + HISTORY_RECAP_MAX_CHARS, HISTORY_V2_DIR, SUMMARIES_V2_DIR, } from "../config.js"; @@ -32,10 +33,13 @@ export function loadRecentSummaries( const date = new Date(Date.now() - i * 86400000); const summaryPath = getSummaryPath(channelId, date, channelName); if (fs.existsSync(summaryPath)) { + const summary = fs + .readFileSync(summaryPath, "utf-8") + .trim() + .slice(0, HISTORY_RECAP_MAX_CHARS); + if (!summary) continue; const dateStr = date.toISOString().split("T")[0]; - summaries.push( - `[${dateStr}] ${fs.readFileSync(summaryPath, "utf-8").trim()}`, - ); + summaries.push(`[${dateStr}] ${summary}`); } } return summaries.reverse().join("\n\n"); diff --git a/tests/summaryContext.test.mjs b/tests/summaryContext.test.mjs new file mode 100644 index 0000000..d3729ef --- /dev/null +++ b/tests/summaryContext.test.mjs @@ -0,0 +1,31 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; + +const messagesDir = fs.mkdtempSync( + path.join(os.tmpdir(), "claudify-summary-context-"), +); +process.env.MESSAGES_DIR = messagesDir; +process.env.HISTORY_RECAP_MAX_CHARS = "80"; + +const { loadRecentHistory } = await import("../build/storage/history.js"); +const { getSummaryPath } = await import("../build/storage/summaries.js"); + +test.after(() => fs.rmSync(messagesDir, { recursive: true, force: true })); + +test("loaded daily summaries stay within the configured recap character budget", () => { + const date = new Date(Date.now() - 86400000); + const summary = `important opening context ${"x".repeat(200)} trailing data`; + fs.writeFileSync( + getSummaryPath("summary-channel", date, "general"), + summary, + "utf8", + ); + + const history = loadRecentHistory("summary-channel", "ordinary question", "general"); + + assert.match(history, /important opening context/); + assert.doesNotMatch(history, /trailing data/); +});