feat(e2b): use Filesystem REST API for native binary I/O#2196
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
Suggest supplementary test |
AgentScopeJavaBot
left a comment
There was a problem hiding this comment.
🤖 AI Review
This PR improves the Connect protocol frame parsing in E2bEnvdProcessClient by properly handling end-stream frames (flag 0x02), compressed frames (flag 0x01), and unknown flags — replacing the previous naive flags != 0x00 check. It also adds SLF4J debug logging for better diagnosability and a clean parseEndStreamResponse() / EndStreamMessage record for end-stream error parsing. The changes are well-scoped and align with the Connect protocol specification. A few minor robustness gaps were identified around defensive null handling and a silently-swallowed exception that should be addressed for consistency with the new logging additions.
(inline comments could not be attached — line numbers fell outside PR hunks. See archived report.)
AgentScopeJavaBot
left a comment
There was a problem hiding this comment.
🤖 AI Review
This PR improves the Connect protocol frame parsing in E2bEnvdProcessClient by properly handling end-stream frames (flag 0x02), compressed frames (flag 0x01), and unknown flags — replacing the previous naive flags != 0x00 check. It also adds SLF4J debug logging for better diagnosability and a clean parseEndStreamResponse() / EndStreamMessage record for end-stream error parsing. The changes are well-scoped and align with the Connect protocol specification. A few minor robustness gaps were identified around defensive null handling and a silently-swallowed exception that should be addressed for consistency with the new logging additions.
(inline comments could not be attached — line numbers fell outside PR hunks. See archived report.)
- Parse EndStreamResponse (flags & 0x02) per Connect protocol spec - Use getField() instead of hasField() for proto3 scalar exit_code - Proto3 omits default values (0) from wire; getField() returns default - Extract EndEvent.error to stderr (signal-killed processes) - Fix JSON codec: always set end event when end node exists - Parsed error field from JSON (proto3_optional) - Reject compressed frames (flags & 0x01) with explicit error - Skip reserved flag bits (flags & 0xFC) - Add debug logging for frame processing diagnostics - Add 7 unit tests covering exit_code=0, EndStreamResponse, error field - Add integration test: signal-killed process via runShell + threads - Add evidence comments referencing Connect spec, Python SDK, Go handler
- Replace base64 shell transfers with HTTP multipart upload (uploadFile) and GET download (downloadFile) via E2B Filesystem REST API - Rewrite doHydrateWorkspace: chunked Python base64 -> uploadFile + tar xf - Rewrite doPersistWorkspace: protobuf streaming -> tar file + downloadFile - Add E2bSandboxFilesystem: override uploadFiles/downloadFiles to use REST API, avoiding 33% base64 overhead and 512KB stdout truncation bug - Add createFilesystem() factory on SandboxFilesystemSpec for custom fs impl - Add E2bSandboxFilesystemTest: 6 unit tests covering delegation, error handling, and fallback to super for non-E2B sandboxes
9240999 to
0f18e9f
Compare
Summary
Fix E2B envd
process.Process/Startserver-streaming response parsing and replace shell+base64 file transfers with native binary HTTP via the E2B Filesystem REST API.Root Cause
sint32 exit_code = 1is a regular proto3 scalar field (not proto3_optional, not oneof). When exit code is 0, the field is omitted from both protobuf binary wire format and JSON output (proto3 default-value omission). The original code usedDynamicMessage.hasField()to check presence — returnsfalsefor default values — causing exit code 0 to be treated as "no exit code received", resulting inexitCode = -2147483648(Integer.MIN_VALUE).Changes — Streaming Response Parsing
EndStreamMessage, throw on error, break cleanly on successIOExceptionhasField()+getField()withgetField()forexit_codeevent.event.end.exit_codeerrorfieldevent.event.end.errordirectly& 0x7FFFFFFFstrippedbuildEnvdRequest()extracted — deduplicate envd request header constructionrunShellanddrainStartStreamChanges — Filesystem REST API
Replace shell+base64 file transfers (33% overhead, 512KB truncation) with native binary HTTP.
E2bEnvdProcessClient.javauploadFile()(POST multipart) +downloadFile()(GET). RefactoredbuildFilesystemRequest()to share headers without hardcoding HTTP method.E2bSandbox.javauploadFile()+tar xf. doPersistWorkspace(): protobuf streaming → tar file +downloadFile()GET. Added publicuploadFile()/downloadFile()delegates.E2bSandboxFilesystem.javauploadFiles()/downloadFiles()to use REST API. Falls back tosuperfor non-E2B sandboxes (test mocks).E2bFilesystemSpec.javacreateFilesystem()→new E2bSandboxFilesystem().SandboxFilesystemSpec.javacreateFilesystem()default method returningSandboxBackedFilesystem().HarnessAgent.javanew SandboxBackedFilesystem()→sandboxFilesystemSpec.createFilesystem().Performance (before → after)
tar xfTests
Unit Tests — Streaming Parsing (
E2bEnvdProcessClientTest) — 14 testsendStreamResponseWithErrorThrowsSandboxRuntimeExceptionendStreamResponseWithoutErrorBreaksCleanly{}→ clean breakexitCodeZeroDefaultWhenOmittedend:{}(exitCode omitted) → exit=0 (proto3 default via getField)exitCodeNonZeroend:{exitCode:42}→ exit=42endEventErrorWrittenToStderrend:{error:"signal: 9"}→ stderr contains errorcompressedFrameThrowsIOExceptionreservedFlagsFrameIsSkippedUnit Tests — Filesystem (
E2bSandboxFilesystemTest) — 6 testsuploadFiles_delegatesToE2bSandboxE2bSandbox.uploadFile()uploadFiles_returnsFailureOnExceptionFileUploadResponse.errordownloadFiles_delegatesToE2bSandboxE2bSandbox.downloadFile()downloadFiles_returnsFailureOnExceptionFileDownloadResponse.erroruploadFiles_fallsBackToSuperWhenNotE2bSandboxSandboxBackedFilesystemfallbackdownloadFiles_fallsBackToSuperWhenNotE2bSandboxIntegration Tests (
E2bEnvdProcessClientIntegrationTest) — 2 tests (require-DE2B_API_KEY)captureRawConnectFramessignalKillViaRunShellsleep 120; Thread B kills viakillall; verifies EndEvent.error captured to stderrTest Plan