diff --git a/src/main/artifacts/artifact-create-intent-store.test.ts b/src/main/artifacts/artifact-create-intent-store.test.ts index 70cdbec4bb1..f512f5ab439 100644 --- a/src/main/artifacts/artifact-create-intent-store.test.ts +++ b/src/main/artifacts/artifact-create-intent-store.test.ts @@ -274,9 +274,12 @@ describe('artifact create intent store', () => { ).toThrow(/unsupported format/) }) - it('persists a 5 MiB escaped artifact within the recovery limit', async () => { + it('persists an escaped artifact within the recovery limit', async () => { const userDataPath = await createUserDataPath() - const nearLimitBody = { ...body, content: '"'.repeat(ARTIFACT_MAX_CONTENT_BYTES) } + const nearLimitBody = { + ...body, + content: '"'.repeat(Math.floor(ARTIFACT_MAX_CONTENT_BYTES / 2)) + } expect( artifactWriteRequestByteLength({ sourceKey: '/repo/report.html', ...nearLimitBody }) ).toBeLessThanOrEqual(ARTIFACT_MAX_REQUEST_BYTES) @@ -307,7 +310,7 @@ describe('artifact create intent store', () => { 'key-a', { ...body, content: 'x'.repeat(ARTIFACT_MAX_CONTENT_BYTES + 1) } ) - ).toThrow(/5 MiB limit/) + ).toThrow(/10 MiB limit/) }) it('rejects a recovery body whose escaped request exceeds the transport budget', async () => { diff --git a/src/main/artifacts/artifact-create-intent-store.ts b/src/main/artifacts/artifact-create-intent-store.ts index 8816f5e1d97..02869245ebd 100644 --- a/src/main/artifacts/artifact-create-intent-store.ts +++ b/src/main/artifacts/artifact-create-intent-store.ts @@ -207,7 +207,7 @@ export function getOrCreateArtifactCreateIntent( body: ArtifactWriteBody ): ArtifactCreateIntent { if (artifactContentByteLength(body.content) > ARTIFACT_MAX_CONTENT_BYTES) { - throw new Error('Artifact content exceeds the 5 MiB limit.') + throw new Error('Artifact content exceeds the 10 MiB limit.') } if (artifactIntentRequestByteLength(sourceKey, body) > ARTIFACT_MAX_REQUEST_BYTES) { throw new Error('Artifact create recovery record exceeds the supported size.') diff --git a/src/main/runtime/rpc/methods/artifacts.test.ts b/src/main/runtime/rpc/methods/artifacts.test.ts index 478e5b030d5..f08db1e0c88 100644 --- a/src/main/runtime/rpc/methods/artifacts.test.ts +++ b/src/main/runtime/rpc/methods/artifacts.test.ts @@ -46,7 +46,7 @@ describe('artifact RPC schemas', () => { ).toBe(false) }) - it('accepts a 5 MiB UTF-8 artifact at the content boundary', () => { + it('accepts a 10 MiB UTF-8 artifact at the content boundary', () => { expect( writeSchema('artifacts.publish').safeParse({ ...validRequest, @@ -56,7 +56,9 @@ describe('artifact RPC schemas', () => { }) it('measures the content boundary in UTF-8 bytes', () => { - const exact = `${'€'.repeat(Math.floor(ARTIFACT_MAX_CONTENT_BYTES / 3))}aa` + const euroCount = Math.floor(ARTIFACT_MAX_CONTENT_BYTES / 3) + const asciiBytes = ARTIFACT_MAX_CONTENT_BYTES - euroCount * 3 + const exact = `${'€'.repeat(euroCount)}${'a'.repeat(asciiBytes)}` const oversized = `${exact}€` expect(new TextEncoder().encode(exact).byteLength).toBe(ARTIFACT_MAX_CONTENT_BYTES) expect(new TextEncoder().encode(oversized).byteLength).toBeGreaterThan( @@ -70,11 +72,11 @@ describe('artifact RPC schemas', () => { ).toBe(false) }) - it('allows JSON escaping within the bounded 5 MiB content request', () => { + it('allows JSON escaping within the bounded content request', () => { expect( writeSchema('artifacts.publish').safeParse({ ...validRequest, - content: '"'.repeat(ARTIFACT_MAX_CONTENT_BYTES) + content: '"'.repeat(Math.floor(ARTIFACT_MAX_CONTENT_BYTES / 2)) }).success ).toBe(true) }) diff --git a/src/main/runtime/rpc/methods/artifacts.ts b/src/main/runtime/rpc/methods/artifacts.ts index ace49a53ed3..4b5d7b5ab3a 100644 --- a/src/main/runtime/rpc/methods/artifacts.ts +++ b/src/main/runtime/rpc/methods/artifacts.ts @@ -30,7 +30,7 @@ const WriteRequest = z .min(1) .max(ARTIFACT_MAX_CONTENT_BYTES) .refine((content) => artifactContentByteLength(content) <= ARTIFACT_MAX_CONTENT_BYTES, { - message: 'Artifact content exceeds the 5 MiB limit.' + message: 'Artifact content exceeds the 10 MiB limit.' }), contentType: z.enum(['text/html', 'text/markdown']), fileName: z.string().min(1).max(512), diff --git a/src/renderer/src/components/artifacts/artifact-publish-flow.test.ts b/src/renderer/src/components/artifacts/artifact-publish-flow.test.ts index e962b435476..f966c9bed13 100644 --- a/src/renderer/src/components/artifacts/artifact-publish-flow.test.ts +++ b/src/renderer/src/components/artifacts/artifact-publish-flow.test.ts @@ -101,7 +101,7 @@ describe('artifact publish flow', () => { }) }) - it('publishes content at the 5 MiB boundary', async () => { + it('publishes content at the 10 MiB boundary', async () => { mocks.callRuntimeRpc.mockResolvedValue({ status: 'ok', value: published }) const createRequest = vi.fn().mockResolvedValue({ ...request, diff --git a/src/renderer/src/components/browser-pane/describe-page/browser-artifact-upload.test.ts b/src/renderer/src/components/browser-pane/describe-page/browser-artifact-upload.test.ts index a5708a04a4a..5490e8de27a 100644 --- a/src/renderer/src/components/browser-pane/describe-page/browser-artifact-upload.test.ts +++ b/src/renderer/src/components/browser-pane/describe-page/browser-artifact-upload.test.ts @@ -64,7 +64,7 @@ describe('browser artifact upload', () => { } satisfies Partial) }) - it('accepts a file whose stat is exactly at the 5 MiB boundary', async () => { + it('accepts a file whose stat is exactly at the 10 MiB boundary', async () => { stat.mockResolvedValueOnce({ size: ARTIFACT_MAX_CONTENT_BYTES, isDirectory: false, diff --git a/src/shared/artifacts.ts b/src/shared/artifacts.ts index 3a65dbfe7ae..8ca4bd25b84 100644 --- a/src/shared/artifacts.ts +++ b/src/shared/artifacts.ts @@ -1,5 +1,5 @@ /** Maximum UTF-8 bytes accepted for a manually shared artifact. */ -export const ARTIFACT_MAX_CONTENT_BYTES = 5 * 1024 * 1024 +export const ARTIFACT_MAX_CONTENT_BYTES = 10 * 1024 * 1024 /** Legacy CLI/SSH envelope cap; those transports still have ~1 MiB control frames. */ export const ARTIFACT_CLI_MAX_RPC_BYTES = 800 * 1024