memory: block dreaming self-ingestion (#66852)

Merged via squash.

Prepared head SHA: 4742656a0d
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
Gustavo Madeira Santana
2026-04-14 20:29:12 -04:00
committed by GitHub
parent 5702ab695b
commit 0c4e0d7030
8 changed files with 477 additions and 9 deletions

View File

@@ -174,4 +174,58 @@ describe("buildSessionEntry", () => {
expect(entry).not.toBeNull();
expect(entry?.generatedByDreamingNarrative).toBe(true);
});
it("does not flag ordinary transcripts that quote the dream-diary prompt", async () => {
const jsonlLines = [
JSON.stringify({
type: "message",
message: {
role: "user",
content:
"Write a dream diary entry from these memory fragments:\n- Candidate: durable note",
},
}),
JSON.stringify({
type: "message",
message: { role: "assistant", content: "A drifting archive breathed in moonlight." },
}),
];
const filePath = path.join(tmpDir, "dreaming-prompt-session.jsonl");
await fs.writeFile(filePath, jsonlLines.join("\n"));
const entry = await buildSessionEntry(filePath);
expect(entry).not.toBeNull();
expect(entry?.generatedByDreamingNarrative).toBeUndefined();
expect(entry?.content).toContain(
"User: Write a dream diary entry from these memory fragments:",
);
expect(entry?.content).toContain("Assistant: A drifting archive breathed in moonlight.");
expect(entry?.lineMap).toEqual([1, 2]);
});
it("does not flag transcripts when dreaming markers only appear mid-string", async () => {
const jsonlLines = [
JSON.stringify({
type: "custom",
customType: "note",
data: {
runId: "user-context-dreaming-narrative-light-1775894400455",
},
}),
JSON.stringify({
type: "message",
message: { role: "user", content: "Keep the archive index updated." },
}),
];
const filePath = path.join(tmpDir, "substring-marker-session.jsonl");
await fs.writeFile(filePath, jsonlLines.join("\n"));
const entry = await buildSessionEntry(filePath);
expect(entry).not.toBeNull();
expect(entry?.generatedByDreamingNarrative).toBeUndefined();
expect(entry?.content).toContain("User: Keep the archive index updated.");
expect(entry?.lineMap).toEqual([2]);
});
});

View File

@@ -7,6 +7,7 @@ import { createSubsystemLogger } from "../../logging/subsystem.js";
import { hashText } from "./internal.js";
const log = createSubsystemLogger("memory");
const DREAMING_NARRATIVE_RUN_PREFIX = "dreaming-narrative-";
export type SessionFileEntry = {
path: string;
@@ -42,7 +43,39 @@ function isDreamingNarrativeBootstrapRecord(record: unknown): boolean {
return false;
}
const runId = (candidate.data as { runId?: unknown }).runId;
return typeof runId === "string" && runId.startsWith("dreaming-narrative-");
return typeof runId === "string" && runId.startsWith(DREAMING_NARRATIVE_RUN_PREFIX);
}
function hasDreamingNarrativeRunId(value: unknown): boolean {
return typeof value === "string" && value.startsWith(DREAMING_NARRATIVE_RUN_PREFIX);
}
function isDreamingNarrativeGeneratedRecord(record: unknown): boolean {
if (isDreamingNarrativeBootstrapRecord(record)) {
return true;
}
if (!record || typeof record !== "object" || Array.isArray(record)) {
return false;
}
const candidate = record as {
runId?: unknown;
sessionKey?: unknown;
data?: unknown;
};
if (
hasDreamingNarrativeRunId(candidate.runId) ||
hasDreamingNarrativeRunId(candidate.sessionKey)
) {
return true;
}
if (!candidate.data || typeof candidate.data !== "object" || Array.isArray(candidate.data)) {
return false;
}
const nested = candidate.data as {
runId?: unknown;
sessionKey?: unknown;
};
return hasDreamingNarrativeRunId(nested.runId) || hasDreamingNarrativeRunId(nested.sessionKey);
}
export async function listSessionFilesForAgent(agentId: string): Promise<string[]> {
@@ -140,7 +173,7 @@ export async function buildSessionEntry(absPath: string): Promise<SessionFileEnt
} catch {
continue;
}
if (!generatedByDreamingNarrative && isDreamingNarrativeBootstrapRecord(record)) {
if (!generatedByDreamingNarrative && isDreamingNarrativeGeneratedRecord(record)) {
generatedByDreamingNarrative = true;
}
if (
@@ -163,6 +196,9 @@ export async function buildSessionEntry(absPath: string): Promise<SessionFileEnt
if (!text) {
continue;
}
if (generatedByDreamingNarrative) {
continue;
}
const safe = redactSensitiveText(text, { mode: "tools" });
const label = message.role === "user" ? "User" : "Assistant";
collected.push(`${label}: ${safe}`);