From 6a897df716283f5a6ea00435b10bb0e845c9921c Mon Sep 17 00:00:00 2001 From: Josh Wright Date: Thu, 8 Oct 2026 00:09:53 -0400 Subject: [PATCH] Resolve stored blob references when reading agent files --- src/lib/server/backends/files.ts | 9 ++++-- tests/unit/native-observations.test.ts | 38 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/lib/server/backends/files.ts b/src/lib/server/backends/files.ts index 23298e1..2ce16fb 100644 --- a/src/lib/server/backends/files.ts +++ b/src/lib/server/backends/files.ts @@ -3,6 +3,7 @@ import { join, posix, relative, resolve, sep } from 'node:path'; import { config } from '../config.js'; import { nativeInstance, nativeRequest } from './native.js'; import { getHost } from '../hosts/index.js'; +import { getFileBlob } from '../db/file-blobs.js'; function remote(path: string) { for (const root of [config.serversRoot, config.t6ServersRoot]) { @@ -49,8 +50,12 @@ export async function nativeFileManifest(root: string, directories: string[], ig export async function readFile(path: string): Promise { const target = remote(path); if (!target) return fs.readFile(path); - const result = await nativeRequest<{ contentBase64: string | null }>(target.instance, 'files.read', { path: target.path }); - return result.contentBase64 === null ? missing(path) : Buffer.from(result.contentBase64, 'base64'); + const result = await nativeRequest<{ contentSha256?: string; contentBase64?: string | null } | null>(target.instance, 'files.read', { path: target.path }); + // Stored receipts reference shared bytes; read them directly without a base64 round trip. + if (typeof result?.contentSha256 === 'string') return getFileBlob(result.contentSha256); + if (result?.contentBase64 === null) return missing(path); + if (typeof result?.contentBase64 === 'string') return Buffer.from(result.contentBase64, 'base64'); + throw new Error(`Agent returned an invalid response for files.read: ${target.path}`); } export async function exists(path: string): Promise { diff --git a/tests/unit/native-observations.test.ts b/tests/unit/native-observations.test.ts index 2281133..cf9070f 100644 --- a/tests/unit/native-observations.test.ts +++ b/tests/unit/native-observations.test.ts @@ -1,3 +1,4 @@ +import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { newFixture, type Fixture } from '../helpers/fixture.js'; @@ -74,3 +75,40 @@ describe('overlapping native observations', () => { expect(await current).toEqual({ generation: 2 }); }); }); + +describe('native file reads through stored agent receipts', () => { + it.each([ + { name: 'binary content', content: Buffer.from([0, 255, 1, 128, 10]) }, + { name: 'empty content', content: Buffer.alloc(0) } + ])('resolves $name from a stored blob reference', async ({ content }) => { + const api = await setup(); + const { readFile } = await import('../../src/lib/server/backends/files.js'); + const reading = readFile(join(fixture.t5, 'test', 'configs/server.cfg')); + const operation = api.listHostOperations(api.host.id)[0]; + expect(operation).toMatchObject({ kind: 'files.read', payload: { path: 'configs/server.cfg' } }); + api.reportHostOperationStatus(api.host.id, operation.id, { + state: 'completed', result: { contentBase64: content.toString('base64') } + }); + expect(api.getHostOperationStatus(operation.id)?.result).toEqual({ contentSha256: expect.any(String) }); + expect(await reading).toEqual(content); + }); + + it('preserves missing-file errors from the agent', async () => { + const api = await setup(); + const { readFile } = await import('../../src/lib/server/backends/files.js'); + const reading = expect(readFile(join(fixture.t5, 'test', 'missing'))).rejects.toMatchObject({ code: 'ENOENT' }); + api.reportHostOperationStatus(api.host.id, api.listHostOperations(api.host.id)[0].id, { + state: 'completed', result: { contentBase64: null } + }); + await reading; + }); + + it.each([null, {}, { contentBase64: 123 }, { contentSha256: 123 }])('reports an invalid response for malformed file content: %j', async result => { + const api = await setup(); + const { readFile } = await import('../../src/lib/server/backends/files.js'); + const reading = expect(readFile(join(fixture.t5, 'test', 'configs/server.cfg'))) + .rejects.toThrow('Agent returned an invalid response for files.read: configs/server.cfg'); + api.reportHostOperationStatus(api.host.id, api.listHostOperations(api.host.id)[0].id, { state: 'completed', result }); + await reading; + }); +});