diff --git a/docs/reference/memory-config.md b/docs/reference/memory-config.md
index 64aca9a7970c..81fbc8a76e1f 100644
--- a/docs/reference/memory-config.md
+++ b/docs/reference/memory-config.md
@@ -459,7 +459,7 @@ Index session transcripts and surface them via `memory_search`:
| `sources` | `string[]` | `["memory"]` | Add `"sessions"` to include transcripts |
-Session indexing is opt-in and runs asynchronously. Results can be slightly stale. Session logs live on disk, so treat filesystem access as the trust boundary.
+Session indexing is opt-in and runs asynchronously. Results can be slightly stale. Active transcripts live in the agent's SQLite database, while retained transcript artifacts can live on disk. Treat access to both as part of the same trust boundary.
diff --git a/extensions/memory-core/src/cli-index-search.runtime.ts b/extensions/memory-core/src/cli-index-search.runtime.ts
index 3b17569dc1ea..74a8d39f417b 100644
--- a/extensions/memory-core/src/cli-index-search.runtime.ts
+++ b/extensions/memory-core/src/cli-index-search.runtime.ts
@@ -1,4 +1,3 @@
-import os from "node:os";
import path from "node:path";
import { resolveMemorySearchStaleness } from "openclaw/plugin-sdk/memory-core-host-engine-storage";
import { resolveMemoryDreamingConfig } from "openclaw/plugin-sdk/memory-core-host-status";
@@ -14,7 +13,6 @@ import {
import {
defaultRuntime,
formatErrorMessage,
- resolveStateDir,
setVerbose,
shortenHomeInString,
shortenHomePath,
@@ -39,17 +37,14 @@ import {
resolveShortTermRecallStorePath,
} from "./short-term-promotion.js";
const { accent, heading, info, muted, success, warn } = theme;
-function formatSourceLabel(source: string, workspaceDir: string, agentId: string): string {
+function formatSourceLabel(source: string, workspaceDir: string): string {
if (source === "memory") {
return shortenHomeInString(
`memory (MEMORY.md + ${path.join(workspaceDir, "memory")}${path.sep}*.md)`,
);
}
if (source === "sessions") {
- const stateDir = resolveStateDir(process.env, os.homedir);
- return shortenHomeInString(
- `sessions (${path.join(stateDir, "agents", agentId, "sessions")}${path.sep}*.jsonl)`,
- );
+ return "sessions (current transcripts + retained transcript artifacts)";
}
return source;
}
@@ -71,7 +66,7 @@ export async function runMemoryIndex(
const status = manager.status();
const label = (text: string) => muted(`${text}:`);
const sourceLabels = (status.sources ?? []).map((source) =>
- formatSourceLabel(source, status.workspaceDir ?? "", agentId),
+ formatSourceLabel(source, status.workspaceDir ?? ""),
);
const extraPaths = status.workspaceDir
? formatExtraPaths(status.workspaceDir, status.extraPaths ?? [])
diff --git a/extensions/memory-core/src/cli-runtime-common.ts b/extensions/memory-core/src/cli-runtime-common.ts
index d27a3acc311c..917d65416300 100644
--- a/extensions/memory-core/src/cli-runtime-common.ts
+++ b/extensions/memory-core/src/cli-runtime-common.ts
@@ -394,10 +394,10 @@ export function formatMemoryIndexOutcome(
scan: MemorySourceScan | undefined,
agentId: string,
): string {
- if (status.workspaceDir && scan?.totalFiles === 0) {
+ const indexedFiles = status.files ?? 0;
+ if (indexedFiles === 0 && status.workspaceDir && scan?.totalFiles === 0) {
return `No memory files found in ${shortenHomePath(status.workspaceDir)}; nothing indexed (${agentId}).`;
}
- const indexedFiles = status.files ?? 0;
const fileLabel = indexedFiles === 1 ? "file" : "files";
return `Memory index updated (${agentId}): ${indexedFiles} ${fileLabel} indexed.`;
}
diff --git a/extensions/memory-core/src/cli.host.runtime.ts b/extensions/memory-core/src/cli.host.runtime.ts
index 94a6686c039e..69911ac2f41f 100644
--- a/extensions/memory-core/src/cli.host.runtime.ts
+++ b/extensions/memory-core/src/cli.host.runtime.ts
@@ -15,7 +15,6 @@ export {
getRuntimeConfig,
resolveDefaultAgentId,
resolveSessionTranscriptsDirForAgent,
- resolveStateDir,
type OpenClawConfig,
} from "openclaw/plugin-sdk/memory-core-host-runtime-core";
export {
diff --git a/extensions/memory-core/src/cli.test.ts b/extensions/memory-core/src/cli.test.ts
index fb4b7bca8a56..832826b65dc0 100644
--- a/extensions/memory-core/src/cli.test.ts
+++ b/extensions/memory-core/src/cli.test.ts
@@ -19,6 +19,7 @@ import {
spyRuntimeLogs,
} from "openclaw/plugin-sdk/test-fixtures";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
+import { formatMemoryIndexOutcome } from "./cli-runtime-common.js";
import { openMemoryCoreStateStore } from "./dreaming-state.js";
import { readShortTermRecallEntries, recordShortTermRecalls } from "./short-term-promotion.js";
import {
@@ -188,7 +189,7 @@ describe("memory cli", () => {
function makeMemoryStatus(overrides: Record = {}) {
return {
- backend: "builtin",
+ backend: "builtin" as const,
files: 0,
chunks: 0,
dirty: false,
@@ -230,6 +231,27 @@ describe("memory cli", () => {
);
}
+ it.each([
+ {
+ name: "reports indexed SQLite session rows when the physical-file scan is empty",
+ files: 2,
+ expected: "Memory index updated (main): 2 files indexed.",
+ },
+ {
+ name: "keeps the genuine empty-index result as a no-op",
+ files: 0,
+ expected: `No memory files found in /tmp/openclaw; nothing indexed (main).`,
+ },
+ ])("$name", ({ files, expected }) => {
+ expect(
+ formatMemoryIndexOutcome(
+ makeMemoryStatus({ files }),
+ { sources: [], totalFiles: 0, issues: [] },
+ "main",
+ ),
+ ).toBe(expected);
+ });
+
function stripAnsi(value: string) {
let output = "";
for (let index = 0; index < value.length; index += 1) {
@@ -1423,6 +1445,26 @@ describe("memory cli", () => {
});
});
+ it("describes session index sources without implying active JSONL storage", async () => {
+ await withTempWorkspace(async (workspaceDir) => {
+ const close = vi.fn(async () => {});
+ const sync = vi.fn(async () => {});
+ mockManager({
+ sync,
+ status: () => makeMemoryStatus({ workspaceDir, sources: ["sessions"], files: 1 }),
+ close,
+ });
+
+ const log = spyRuntimeLogs(defaultRuntime);
+ await runMemoryCli(["index", "--verbose"]);
+
+ expectLogged(log, "sessions (current transcripts + retained transcript artifacts)");
+ expectNotLogged(log, "*.jsonl");
+ expectLogged(log, "Memory index updated (main): 1 file indexed.");
+ expect(close).toHaveBeenCalled();
+ });
+ });
+
it("warns on stderr when index completes without sqlite-vec embeddings", async () => {
const close = vi.fn(async () => {});
const sync = vi.fn(async () => {});