Implement unique filename generation to prevent overwriting existing session files #4

Merged
space merged 1 commits from no-overrides into main 2026-04-08 20:16:19 +02:00
3 changed files with 31 additions and 1 deletions

View File

@@ -17,6 +17,9 @@ metadata:
Writes full user/assistant transcript context from the previous session into
`<workspace>/memory/YYYY-MM-DD-slug.md` when `/new` or `/reset` runs.
This hook avoids overwriting existing files by choosing a unique filename
when a collision occurs (it appends `-1`, `-2`, etc. to the base name).
Differences from bundled session-memory:
- Uses full session context in output file

View File

@@ -14,6 +14,8 @@ Custom OpenClaw session-memory hook.
- Saves the **full** user/assistant session context into `workspace/memory/`
- Uses `agents.defaults.userTimezone` for date/time formatting
- Uses a slugged filename, with slug generation based on a small excerpt
- Avoids overwriting existing files: if a filename already exists the hook
appends `-1`, `-2`, ... to produce a unique filename.
## Disable the bundled hook
@@ -42,3 +44,8 @@ Or set it false in config:
## Notes
This repo is intentionally lightweight, it exists so the custom hook can live in the managed hooks directory and stay separate from the bundled one.
If you prefer atomic creation to fully eliminate a race window (two processes
creating the same new filename simultaneously), replace the simple existence
check with an atomic open/write using `fs.open(..., 'wx')` or write to a
temporary file and `fs.rename()` it into place.

View File

@@ -197,6 +197,26 @@ function buildSlugExcerpt(full: string): string {
return tail.slice(-3000);
}
async function getUniqueFilePath(dir: string, filename: string): Promise<string> {
const candidate = path.join(dir, filename);
try {
await fs.access(candidate);
} catch {
return candidate;
}
const parsed = path.parse(filename);
for (let i = 1; ; i += 1) {
const name = `${parsed.name}-${i}${parsed.ext}`;
const p = path.join(dir, name);
try {
await fs.access(p);
} catch {
return p;
}
}
}
async function generateSlug(params: {
excerpt: string;
cfg: AnyObj;
@@ -274,7 +294,7 @@ const handler = async (event: {
await fs.mkdir(memoryDir, { recursive: true });
const filename = `${dateStr}-${slug}.md`;
const memoryFilePath = path.join(memoryDir, filename);
const memoryFilePath = await getUniqueFilePath(memoryDir, filename);
const source = typeof context.commandSource === "string" ? context.commandSource : "unknown";