diff --git a/src/index.ts b/src/index.ts index 5128167..a233b51 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1023,8 +1023,16 @@ function formatMemoriesForCompaction(memories: any[]): string { let output = `## Restored Session Memory\n\n`; memories.forEach((m, i) => { + // Auto-capture stores the tags footer inside the memory body itself + // ("…\n\nTags: …"). Strip it when a canonical tags line follows, so the + // tags are not duplicated in the restored context (#131). + const body = + m.tags && m.tags.length > 0 + ? (m.memory ?? "").replace(/\n*Tags: [^\n]*\s*$/, "") + : (m.memory ?? ""); + output += `### Memory ${i + 1}\n`; - output += `${m.memory}\n\n`; + output += `${body}\n\n`; if (m.tags && m.tags.length > 0) { output += `Tags: ${m.tags.join(", ")}\n\n`; } diff --git a/tests/compaction-agent-preservation.test.ts b/tests/compaction-agent-preservation.test.ts index 614e9c4..e3b288b 100644 --- a/tests/compaction-agent-preservation.test.ts +++ b/tests/compaction-agent-preservation.test.ts @@ -200,6 +200,37 @@ describe("session.compacted agent preservation (#236)", () => { expect(result.parsed?.promptCalls[0]?.body?.noReply).toBe(true); }); + it("does not duplicate the Tags line when the stored memory already embeds one (regression from #131)", () => { + const result = runCompactionScenario({ + sessionAgent: "my-orchestrator", + memories: [ + { + memory: "We chose libSQL over sqlite3.\n\nTags: architecture, decision", + tags: ["architecture", "decision"], + }, + ], + messages: [{ info: { role: "user", agent: "my-orchestrator" } }], + }); + + expect(result.exitCode).toBe(0); + const text = result.parsed?.promptCalls[0]?.body?.parts?.[0]?.text ?? ""; + const tagsLines = text.match(/^Tags: /gm) ?? []; + expect(tagsLines).toHaveLength(1); + }); + + it("still appends a single Tags line when the memory body has no embedded footer", () => { + const result = runCompactionScenario({ + sessionAgent: "my-orchestrator", + memories: [{ memory: "Plain memory body without footer.", tags: ["decision"] }], + messages: [{ info: { role: "user", agent: "my-orchestrator" } }], + }); + + expect(result.exitCode).toBe(0); + const text = result.parsed?.promptCalls[0]?.body?.parts?.[0]?.text ?? ""; + const tagsLines = text.match(/^Tags: /gm) ?? []; + expect(tagsLines).toHaveLength(1); + }); + it("does not call session.prompt when there are no memories", () => { const result = runCompactionScenario({ sessionAgent: "my-orchestrator",