( toolUseContext: ToolUseContext, )
| 182 | } |
| 183 | |
| 184 | async function setupSessionMemoryFile( |
| 185 | toolUseContext: ToolUseContext, |
| 186 | ): Promise<{ memoryPath: string; currentMemory: string }> { |
| 187 | const fs = getFsImplementation() |
| 188 | |
| 189 | // Set up directory and file |
| 190 | const sessionMemoryDir = getSessionMemoryDir() |
| 191 | await fs.mkdir(sessionMemoryDir, { mode: 0o700 }) |
| 192 | |
| 193 | const memoryPath = getSessionMemoryPath() |
| 194 | |
| 195 | // Create the memory file if it doesn't exist (wx = O_CREAT|O_EXCL) |
| 196 | try { |
| 197 | await writeFile(memoryPath, '', { |
| 198 | encoding: 'utf-8', |
| 199 | mode: 0o600, |
| 200 | flag: 'wx', |
| 201 | }) |
| 202 | // Only load template if file was just created |
| 203 | const template = await loadSessionMemoryTemplate() |
| 204 | await writeFile(memoryPath, template, { |
| 205 | encoding: 'utf-8', |
| 206 | mode: 0o600, |
| 207 | }) |
| 208 | } catch (e: unknown) { |
| 209 | const code = getErrnoCode(e) |
| 210 | if (code !== 'EEXIST') { |
| 211 | throw e |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | // Drop any cached entry so FileReadTool's dedup doesn't return a |
| 216 | // file_unchanged stub — we need the actual content. The Read repopulates it. |
| 217 | toolUseContext.readFileState.delete(memoryPath) |
| 218 | const result = await FileReadTool.call( |
| 219 | { file_path: memoryPath }, |
| 220 | toolUseContext, |
| 221 | ) |
| 222 | let currentMemory = '' |
| 223 | |
| 224 | const output = result.data as FileReadToolOutput |
| 225 | if (output.type === 'text') { |
| 226 | currentMemory = output.file.content |
| 227 | } |
| 228 | |
| 229 | logEvent('ncode_session_memory_file_read', { |
| 230 | content_length: currentMemory.length, |
| 231 | }) |
| 232 | |
| 233 | return { memoryPath, currentMemory } |
| 234 | } |
| 235 | |
| 236 | /** |
| 237 | * Initialize session memory config from remote config (lazy initialization). |
no test coverage detected