Fix dreaming replay, repair polluted artifacts, and gate wiki tabs (#65138)

* fix(active-memory): preserve parent channel context for recall runs

* fix(active-memory): keep recall runs on the resolved channel

* fix(active-memory): prefer resolved recall channel over wrapper hints

* fix(active-memory): trust explicit recall channel hints

* fix(active-memory): rank recall channel fallbacks by trust

* Fix dreaming replay and recovery flows

* fix: prevent dreaming event loss and diary write races

* chore: add changelog entry for memory fixes

* fix: harden dreaming repair and diary writes

* fix: harden dreaming artifact archive naming
This commit is contained in:
Tak Hoffman
2026-04-12 00:25:11 -05:00
committed by GitHub
parent 5543925cd2
commit 847739d82c
45 changed files with 2016 additions and 148 deletions

View File

@@ -17,8 +17,32 @@ export type SessionFileEntry = {
content: string;
/** Maps each content line (0-indexed) to its 1-indexed JSONL source line. */
lineMap: number[];
/** True when this transcript belongs to an internal dreaming narrative run. */
generatedByDreamingNarrative?: boolean;
};
function isDreamingNarrativeBootstrapRecord(record: unknown): boolean {
if (!record || typeof record !== "object" || Array.isArray(record)) {
return false;
}
const candidate = record as {
type?: unknown;
customType?: unknown;
data?: unknown;
};
if (
candidate.type !== "custom" ||
candidate.customType !== "openclaw:bootstrap-context:full" ||
!candidate.data ||
typeof candidate.data !== "object" ||
Array.isArray(candidate.data)
) {
return false;
}
const runId = (candidate.data as { runId?: unknown }).runId;
return typeof runId === "string" && runId.startsWith("dreaming-narrative-");
}
export async function listSessionFilesForAgent(agentId: string): Promise<string[]> {
const dir = resolveSessionTranscriptsDirForAgent(agentId);
try {
@@ -79,6 +103,7 @@ export async function buildSessionEntry(absPath: string): Promise<SessionFileEnt
const lines = raw.split("\n");
const collected: string[] = [];
const lineMap: number[] = [];
let generatedByDreamingNarrative = false;
for (let jsonlIdx = 0; jsonlIdx < lines.length; jsonlIdx++) {
const line = lines[jsonlIdx];
if (!line.trim()) {
@@ -90,6 +115,9 @@ export async function buildSessionEntry(absPath: string): Promise<SessionFileEnt
} catch {
continue;
}
if (!generatedByDreamingNarrative && isDreamingNarrativeBootstrapRecord(record)) {
generatedByDreamingNarrative = true;
}
if (
!record ||
typeof record !== "object" ||
@@ -124,6 +152,7 @@ export async function buildSessionEntry(absPath: string): Promise<SessionFileEnt
hash: hashText(content + "\n" + lineMap.join(",")),
content,
lineMap,
...(generatedByDreamingNarrative ? { generatedByDreamingNarrative: true } : {}),
};
} catch (err) {
log.debug(`Failed reading session file ${absPath}: ${String(err)}`);