11import crypto from "node:crypto" ;
22import fs from "node:fs" ;
3+ import path from "node:path" ;
34import type { AgentMessage } from "@earendil-works/pi-agent-core" ;
45import { resolveBootstrapWarningSignaturesSeen } from "../../agents/bootstrap-budget.js" ;
56import { estimateMessagesTokens } from "../../agents/compaction.js" ;
@@ -85,11 +86,36 @@ async function runEmbeddedPiAgentDefault(
8586 return await runEmbeddedPiAgent ( ...args ) ;
8687}
8788
89+ async function ensureMemoryFlushTargetFile ( params : {
90+ workspaceDir : string ;
91+ relativePath : string ;
92+ } ) : Promise < void > {
93+ const workspaceDir = normalizeOptionalString ( params . workspaceDir ) ;
94+ const relativePath = normalizeOptionalString ( params . relativePath ) ;
95+ if ( ! workspaceDir || ! relativePath || path . isAbsolute ( relativePath ) ) {
96+ throw new Error ( "Invalid memory flush target path" ) ;
97+ }
98+ const workspaceRoot = path . resolve ( workspaceDir ) ;
99+ const targetPath = path . resolve ( workspaceRoot , relativePath ) ;
100+ const targetRelativePath = path . relative ( workspaceRoot , targetPath ) ;
101+ if (
102+ ! targetRelativePath ||
103+ targetRelativePath . startsWith ( ".." ) ||
104+ path . isAbsolute ( targetRelativePath )
105+ ) {
106+ throw new Error ( "Memory flush target path must stay inside the workspace" ) ;
107+ }
108+ await fs . promises . mkdir ( path . dirname ( targetPath ) , { recursive : true } ) ;
109+ const handle = await fs . promises . open ( targetPath , "a" ) ;
110+ await handle . close ( ) ;
111+ }
112+
88113const memoryDeps = {
89114 compactEmbeddedPiSession : compactEmbeddedPiSessionDefault ,
90115 runWithModelFallback,
91116 ensureSelectedAgentHarnessPlugin,
92117 runEmbeddedPiAgent : runEmbeddedPiAgentDefault ,
118+ ensureMemoryFlushTargetFile,
93119 registerAgentRunContext,
94120 refreshQueuedFollowupSession,
95121 incrementCompactionCount,
@@ -104,6 +130,7 @@ export function setAgentRunnerMemoryTestDeps(overrides?: Partial<typeof memoryDe
104130 ensureSelectedAgentHarnessPlugin,
105131 compactEmbeddedPiSession : compactEmbeddedPiSessionDefault ,
106132 runEmbeddedPiAgent : runEmbeddedPiAgentDefault ,
133+ ensureMemoryFlushTargetFile,
107134 registerAgentRunContext,
108135 refreshQueuedFollowupSession,
109136 incrementCompactionCount,
@@ -1013,6 +1040,10 @@ export async function runMemoryFlushIfNeeded(params: {
10131040 nowMs : memoryFlushNowMs ,
10141041 } ) ?? memoryFlushPlan ;
10151042 const memoryFlushWritePath = activeMemoryFlushPlan . relativePath ;
1043+ await memoryDeps . ensureMemoryFlushTargetFile ( {
1044+ workspaceDir : params . followupRun . run . workspaceDir ,
1045+ relativePath : memoryFlushWritePath ,
1046+ } ) ;
10161047 const flushSystemPrompt = [
10171048 params . followupRun . run . extraSystemPrompt ,
10181049 activeMemoryFlushPlan . systemPrompt ,
0 commit comments