* Attempt to read dynamic workspace file content from storage. * Handles explicit /content reads for images, PDFs, documents, and text files. * Also handles: * `files/{path}/{name}/style` — style extraction (.docx / .pptx / .pdf) * `files/{path}/{name}/compiled-check` — co
(path: string)
| 823 | * Returns null if the path doesn't match a dynamic file path or the file isn't found. |
| 824 | */ |
| 825 | async readFileContent(path: string): Promise<FileReadResult | null> { |
| 826 | const compiledMatch = /^files\/.+\/compiled$/.test(path) |
| 827 | if (compiledMatch) { |
| 828 | let record: WorkspaceFileRecord | null = null |
| 829 | try { |
| 830 | record = await this.resolveWorkspaceFileForDynamicRead(path, 'compiled') |
| 831 | if (!record) return null |
| 832 | const ext = record.name.split('.').pop()?.toLowerCase() ?? '' |
| 833 | const e2bFmt = isE2BDocEnabled ? await getE2BDocFormat(record.name) : null |
| 834 | const taskId = BINARY_DOC_TASKS[ext] |
| 835 | if (!e2bFmt && !taskId) return null |
| 836 | |
| 837 | // Only PDF can be attached as a model-readable `document` block — |
| 838 | // Bedrock/Anthropic document blocks accept application/pdf ONLY. Attaching |
| 839 | // raw pptx/docx/xlsx binary is rejected by the provider (400). So for |
| 840 | // pptx/docx, render to page images (which the model CAN read) and return |
| 841 | // those directly — /compiled can never emit an invalid document block for |
| 842 | // these formats. xlsx isn't renderable; direct to /extract for its content. |
| 843 | if (ext !== 'pdf') { |
| 844 | if (isRenderableDocExt(ext)) { |
| 845 | const compiledName = record.name |
| 846 | return await this.renderDocRecordResult( |
| 847 | record, |
| 848 | ext, |
| 849 | (pageCount) => |
| 850 | `${compiledName}: the raw ${ext.toUpperCase()} binary isn't model-readable, so it was rendered to ${pageCount} page image(s) for inspection.` |
| 851 | ) |
| 852 | } |
| 853 | const extractPath = `${canonicalWorkspaceFilePath({ |
| 854 | folderPath: record.folderPath, |
| 855 | name: record.name, |
| 856 | })}/extract` |
| 857 | return { |
| 858 | content: `${record.name} is a spreadsheet — read "${extractPath}" for its contents.`, |
| 859 | totalLines: 1, |
| 860 | } |
| 861 | } |
| 862 | |
| 863 | const buffer = await fetchWorkspaceFileBuffer(record) |
| 864 | const code = buffer.toString('utf-8') |
| 865 | if (Buffer.byteLength(code, 'utf-8') > MAX_DOCUMENT_PREVIEW_CODE_BYTES) { |
| 866 | return { |
| 867 | content: JSON.stringify({ ok: false, error: 'File source exceeds maximum size' }), |
| 868 | totalLines: 1, |
| 869 | } |
| 870 | } |
| 871 | const compiled = e2bFmt |
| 872 | ? ( |
| 873 | await compileDoc({ |
| 874 | source: code, |
| 875 | fileName: record.name, |
| 876 | workspaceId: this._workspaceId, |
| 877 | }) |
| 878 | ).buffer |
| 879 | : await runSandboxTask(taskId, { code, workspaceId: this._workspaceId }) |
| 880 | if (compiled.length > MAX_COMPILED_ATTACHMENT_BYTES) { |
| 881 | return { |
| 882 | content: `[Compiled artifact too large: ${record.name} (${compiled.length} bytes, limit ${MAX_COMPILED_ATTACHMENT_BYTES})]`, |
no test coverage detected