fix(memory): preserve session indexing during full reindex (#39732)

Merged via squash.

Prepared head SHA: 0dbaf5fffb
Co-authored-by: upupc <12829489+upupc@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
This commit is contained in:
upupc 2026-04-01 18:12:30 +08:00 committed by GitHub
parent 1654c3a851
commit d766bfc6b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 82 additions and 3 deletions

View File

@ -43,6 +43,7 @@ Docs: https://docs.openclaw.ai
- WebChat/exec approvals: use native approval UI guidance in agent system prompts instead of telling agents to paste manual `/approve` commands in webchat sessions. Thanks @vincentkoc.
- Plugins/install: forward `--dangerously-force-unsafe-install` through archive and npm-spec plugin installs so the documented override reaches the security scanner on those install paths. (#58879) Thanks @ryanlee-gemini.
- Chat/error replies: stop leaking raw provider/runtime failures into external chat channels, return a friendly retry message instead, and add a specific `/new` hint for Bedrock toolResult/toolUse session mismatches. (#58831) Thanks @ImLukeF.
- Memory/session indexing: keep full reindexes from skipping session transcripts when sync is triggered by `session-start` or `watch`, so restart-driven reindexes preserve session memory (#39732) thanks @upupc
## 2026.3.31

View File

@ -680,13 +680,13 @@ export abstract class MemoryManagerSyncOps {
if (params?.force) {
return true;
}
if (needsFullReindex) {
return true;
}
const reason = params?.reason;
if (reason === "session-start" || reason === "watch") {
return false;
}
if (needsFullReindex) {
return true;
}
return this.sessionsDirty && this.sessionsDirtyFiles.size > 0;
}

View File

@ -0,0 +1,78 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import type { OpenClawConfig } from "openclaw/plugin-sdk/memory-core-host-engine-foundation";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import type { MemoryIndexManager } from "./index.js";
import { getRequiredMemoryIndexManager } from "./test-manager-helpers.js";
describe("memory manager session reindex gating", () => {
let fixtureRoot = "";
let caseId = 0;
let workspaceDir: string;
let indexPath: string;
let manager: MemoryIndexManager | null = null;
beforeAll(async () => {
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-mem-session-reindex-"));
});
beforeEach(async () => {
workspaceDir = path.join(fixtureRoot, `case-${caseId++}`);
await fs.mkdir(path.join(workspaceDir, "memory"), { recursive: true });
await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), "Hello memory.");
indexPath = path.join(workspaceDir, "index.sqlite");
});
afterEach(async () => {
if (manager) {
await manager.close();
manager = null;
}
});
afterAll(async () => {
if (!fixtureRoot) {
return;
}
await fs.rm(fixtureRoot, { recursive: true, force: true });
});
it("keeps session syncing enabled for full reindexes triggered from session-start/watch", async () => {
const cfg = {
agents: {
defaults: {
workspace: workspaceDir,
memorySearch: {
provider: "openai",
model: "mock-embed",
store: { path: indexPath, vector: { enabled: false } },
cache: { enabled: false },
query: { minScore: 0, hybrid: { enabled: false } },
chunking: { tokens: 4000, overlap: 0 },
experimental: { sessionMemory: true },
sources: ["memory", "sessions"],
sync: { watch: false, onSessionStart: false, onSearch: false },
},
},
list: [{ id: "main", default: true }],
},
} as OpenClawConfig;
manager = await getRequiredMemoryIndexManager({ cfg, agentId: "main" });
const shouldSyncSessions = (
manager as unknown as {
shouldSyncSessions: (
params?: { reason?: string; force?: boolean },
needsFullReindex?: boolean,
) => boolean;
}
).shouldSyncSessions.bind(manager);
expect(shouldSyncSessions({ reason: "session-start" }, true)).toBe(true);
expect(shouldSyncSessions({ reason: "watch" }, true)).toBe(true);
expect(shouldSyncSessions({ reason: "session-start" }, false)).toBe(false);
expect(shouldSyncSessions({ reason: "watch" }, false)).toBe(false);
});
});