Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions packages/contracts/src/orchestration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,6 @@ it.effect("tolerates attachment types from newer builds when decoding messages",
}),
);

// The tolerant member must not catch malformed known attachments: a file over
// the size cap or an image with a bad mime has to fail its own schema, not
// slide through the open one with those constraints unchecked.
it.effect("rejects malformed known attachment types instead of tolerating them", () =>
Effect.gen(function* () {
const base = {
Expand All @@ -380,10 +377,13 @@ it.effect("rejects malformed known attachment types instead of tolerating them",
updatedAt: "2026-01-01T00:00:00.000Z",
});

const oversizedFile = yield* Effect.exit(
decode({ ...base, type: "file", sizeBytes: PROVIDER_SEND_TURN_MAX_FILE_BYTES + 1 }),
);
assert.strictEqual(Exit.isFailure(oversizedFile), true);
// A newer build may raise the upload cap; this build must still read those files.
const aboveUploadCap = PROVIDER_SEND_TURN_MAX_FILE_BYTES + 1;
const largeFile = yield* decode({ ...base, type: "file", sizeBytes: aboveUploadCap });
assert.strictEqual(largeFile.attachments?.[0]?.sizeBytes, aboveUploadCap);

const emptyFile = yield* Effect.exit(decode({ ...base, type: "file", sizeBytes: 0 }));
assert.strictEqual(Exit.isFailure(emptyFile), true);

const badMimeImage = yield* Effect.exit(
decode({ ...base, type: "image", mimeType: "application/pdf", sizeBytes: 12 }),
Expand Down
8 changes: 4 additions & 4 deletions packages/contracts/src/orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,10 @@ export const ChatFileAttachment = Schema.Struct({
id: ChatAttachmentId,
name: TrimmedNonEmptyString.check(Schema.isMaxLength(255)),
mimeType: TrimmedNonEmptyString.check(Schema.isMaxLength(100)),
sizeBytes: NonNegativeInt.check(
Schema.isGreaterThanOrEqualTo(1),
Schema.isLessThanOrEqualTo(PROVIDER_SEND_TURN_MAX_FILE_BYTES),
),
/** No upper bound: history and thread streams can carry files that a newer
build accepted under a higher limit. `AttachmentCreateUploadUrlInput`
enforces the upload cap. */
sizeBytes: NonNegativeInt.check(Schema.isGreaterThanOrEqualTo(1)),
/** Clipboard text folded by a client. Providers keep these path-only so the
agent can inspect the file selectively instead of eagerly spending the
same context the fold is intended to preserve. */
Expand Down
Loading