* Inner implementation of call, separated to allow ENOENT handling in the outer call.
( file_path: string, fullFilePath: string, resolvedFilePath: string, ext: string, offset: number, limit: number | undefined, pages: string | undefined, maxSizeBytes: number, maxTokens: number, readFileState: ToolUseContext['readFileState'], context: ToolUseContext, messageId: string | undefined, )
| 802 | * Inner implementation of call, separated to allow ENOENT handling in the outer call. |
| 803 | */ |
| 804 | async function callInner( |
| 805 | file_path: string, |
| 806 | fullFilePath: string, |
| 807 | resolvedFilePath: string, |
| 808 | ext: string, |
| 809 | offset: number, |
| 810 | limit: number | undefined, |
| 811 | pages: string | undefined, |
| 812 | maxSizeBytes: number, |
| 813 | maxTokens: number, |
| 814 | readFileState: ToolUseContext['readFileState'], |
| 815 | context: ToolUseContext, |
| 816 | messageId: string | undefined, |
| 817 | ): Promise<{ |
| 818 | data: Output |
| 819 | newMessages?: ReturnType<typeof createUserMessage>[] |
| 820 | }> { |
| 821 | // --- Notebook --- |
| 822 | if (ext === 'ipynb') { |
| 823 | const cells = await readNotebook(resolvedFilePath) |
| 824 | const cellsJson = jsonStringify(cells) |
| 825 | |
| 826 | const cellsJsonBytes = Buffer.byteLength(cellsJson) |
| 827 | if (cellsJsonBytes > maxSizeBytes) { |
| 828 | throw new Error( |
| 829 | `Notebook content (${formatFileSize(cellsJsonBytes)}) exceeds maximum allowed size (${formatFileSize(maxSizeBytes)}). ` + |
| 830 | `Use ${BASH_TOOL_NAME} with jq to read specific portions:\n` + |
| 831 | ` cat "${file_path}" | jq '.cells[:20]' # First 20 cells\n` + |
| 832 | ` cat "${file_path}" | jq '.cells[100:120]' # Cells 100-120\n` + |
| 833 | ` cat "${file_path}" | jq '.cells | length' # Count total cells\n` + |
| 834 | ` cat "${file_path}" | jq '.cells[] | select(.cell_type=="code") | .source' # All code sources`, |
| 835 | ) |
| 836 | } |
| 837 | |
| 838 | await validateContentTokens(cellsJson, ext, maxTokens) |
| 839 | |
| 840 | // Get mtime via async stat (single call, no prior existence check) |
| 841 | const stats = await getFsImplementation().stat(resolvedFilePath) |
| 842 | readFileState.set(fullFilePath, { |
| 843 | content: cellsJson, |
| 844 | timestamp: Math.floor(stats.mtimeMs), |
| 845 | offset, |
| 846 | limit, |
| 847 | }) |
| 848 | context.nestedMemoryAttachmentTriggers?.add(fullFilePath) |
| 849 | |
| 850 | const data = { |
| 851 | type: 'notebook' as const, |
| 852 | file: { filePath: file_path, cells }, |
| 853 | } |
| 854 | |
| 855 | logFileOperation({ |
| 856 | operation: 'read', |
| 857 | tool: 'FileReadTool', |
| 858 | filePath: fullFilePath, |
| 859 | content: cellsJson, |
| 860 | }) |
| 861 |
no test coverage detected