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