Files
openclaw/src/agents/pi-embedded-runner/session-manager-init.ts
Xinhua Gu ae0b110e44 fix(security): set 0o600 on remaining session file write paths
Follow-up to #18066 — three session file write sites were missed:

- auto-reply/reply/session.ts: forked session transcript header
- pi-embedded-runner/session-manager-init.ts: session file reset
- gateway/server-methods/sessions.ts: compacted transcript rewrite

All now use mode 0o600 consistent with transcript.ts and chat.ts.
2026-02-16 23:53:28 +01:00

54 lines
1.9 KiB
TypeScript

import fs from "node:fs/promises";
type SessionHeaderEntry = { type: "session"; id?: string; cwd?: string };
type SessionMessageEntry = { type: "message"; message?: { role?: string } };
/**
* pi-coding-agent SessionManager persistence quirk:
* - If the file exists but has no assistant message, SessionManager marks itself `flushed=true`
* and will never persist the initial user message.
* - If the file doesn't exist yet, SessionManager builds a new session in memory and flushes
* header+user+assistant once the first assistant arrives (good).
*
* This normalizes the file/session state so the first user prompt is persisted before the first
* assistant entry, even for pre-created session files.
*/
export async function prepareSessionManagerForRun(params: {
sessionManager: unknown;
sessionFile: string;
hadSessionFile: boolean;
sessionId: string;
cwd: string;
}): Promise<void> {
const sm = params.sessionManager as {
sessionId: string;
flushed: boolean;
fileEntries: Array<SessionHeaderEntry | SessionMessageEntry | { type: string }>;
byId?: Map<string, unknown>;
labelsById?: Map<string, unknown>;
leafId?: string | null;
};
const header = sm.fileEntries.find((e): e is SessionHeaderEntry => e.type === "session");
const hasAssistant = sm.fileEntries.some(
(e) => e.type === "message" && (e as SessionMessageEntry).message?.role === "assistant",
);
if (!params.hadSessionFile && header) {
header.id = params.sessionId;
header.cwd = params.cwd;
sm.sessionId = params.sessionId;
return;
}
if (params.hadSessionFile && header && !hasAssistant) {
// Reset file so the first assistant flush includes header+user+assistant in order.
await fs.writeFile(params.sessionFile, "", { encoding: "utf-8", mode: 0o600 });
sm.fileEntries = [header];
sm.byId?.clear?.();
sm.labelsById?.clear?.();
sm.leafId = null;
sm.flushed = false;
}
}