( content: NonNullable<ToolResultBlockParam['content']>, toolUseId: string, )
| 135 | * @returns Information about the persisted file including filepath and preview |
| 136 | */ |
| 137 | export async function persistToolResult( |
| 138 | content: NonNullable<ToolResultBlockParam['content']>, |
| 139 | toolUseId: string, |
| 140 | ): Promise<PersistedToolResult | PersistToolResultError> { |
| 141 | const isJson = Array.isArray(content) |
| 142 | |
| 143 | // Check for non-text content - we can only persist text blocks |
| 144 | if (isJson) { |
| 145 | const hasNonTextContent = content.some(block => block.type !== 'text') |
| 146 | if (hasNonTextContent) { |
| 147 | return { |
| 148 | error: 'Cannot persist tool results containing non-text content', |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | await ensureToolResultsDir() |
| 154 | const filepath = getToolResultPath(toolUseId, isJson) |
| 155 | const contentStr = isJson ? jsonStringify(content, null, 2) : content |
| 156 | |
| 157 | // tool_use_id is unique per invocation and content is deterministic for a |
| 158 | // given id, so skip if the file already exists. This prevents re-writing |
| 159 | // the same content on every API turn when microcompact replays the |
| 160 | // original messages. Use 'wx' instead of a stat-then-write race. |
| 161 | try { |
| 162 | await writeFile(filepath, contentStr, { encoding: 'utf-8', flag: 'wx' }) |
| 163 | logForDebugging( |
| 164 | `Persisted tool result to ${filepath} (${formatFileSize(contentStr.length)})`, |
| 165 | ) |
| 166 | } catch (error) { |
| 167 | if (getErrnoCode(error) !== 'EEXIST') { |
| 168 | logError(toError(error)) |
| 169 | return { error: getFileSystemErrorMessage(toError(error)) } |
| 170 | } |
| 171 | // EEXIST: already persisted on a prior turn, fall through to preview |
| 172 | } |
| 173 | |
| 174 | // Generate a preview |
| 175 | const { preview, hasMore } = generatePreview(contentStr, PREVIEW_SIZE_BYTES) |
| 176 | |
| 177 | return { |
| 178 | filepath, |
| 179 | originalSize: contentStr.length, |
| 180 | isJson, |
| 181 | preview, |
| 182 | hasMore, |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Build a message for large tool results with preview |
no test coverage detected