fix(review): bound E2B readBytes at the seam, prove conditional-registration disposal, and align read_image contracts

- E2BFileSystem.readBytes now short-circuits on the stat size before any
  content transfer and streams the remote object, cancelling at the first
  chunk past the cap, honoring the seam's bounded-buffering contract; the
  fs-e2b README pair documents the new primitive.
- The tool-fs HMR test now proves the attachments-scoped registration:
  disposing the store withdraws read_image while read/write/edit stay,
  remounting restores it, and disposing the plugin withdraws everything.
- read_image caps reads at the smaller of maxImageBytes and
  maxMessageImageBytes, records an absent observation for a missing
  target like its sibling read, widens the mismatch remedy to cover
  out-of-family formats, and renames the gate's parameter to
  requestedPath; module/apply/registration JSDoc now match the shipped
  composition. zh terminology aligned; the examples manifest keeps its
  literal arrow.
This commit is contained in:
creatixchu
2026-08-10 15:35:33 +08:00
parent 1861a3fc7c
commit 97a9ec5a0e
12 changed files with 141 additions and 39 deletions

View File

@@ -488,6 +488,22 @@ describe('E2BFileSystem identity, metadata, and reads', () => {
await expectCode(fs.readBytes(target, undefined, 4), 'FS_ABORTED')
})
it('readBytes bounds a post-stat grower mid-stream and reads an empty file through the SDK quirk', async () => {
const remote = new FakeRemote()
remote.file('/workspace/grow.bin', [1, 1, 1, 1])
remote.file('/workspace/empty.bin', '')
const { fs } = await setup(remote)
remote.streamChunks = [bytes([1, 1, 1]), bytes([1, 2, 2])]
remote.streamKeepOpen = true
await expectCode(fs.readBytes(await fs.resolve('grow.bin'), undefined, 4), 'FS_TOO_LARGE')
expect(remote.streamCancel).toHaveBeenCalledOnce()
remote.streamChunks = undefined
remote.streamKeepOpen = false
expect((await fs.readBytes(await fs.resolve('empty.bin'), undefined, 4)).byteLength).toBe(0)
})
it('honors aborts before and during remote reads', async () => {
const remote = new FakeRemote()
remote.file('/workspace/a', 'a')