@@ -264,6 +264,38 @@ stale/missing sidecars, and scope isolation.
264264
265265## Critical Rules for Outputs
266266
267+ ### File Downloads and Generated Files
268+
269+ Internal operations return ` createInternalToolFileResult ` / ` createInternalToolFilesResult ` from
270+ ` lib/internal/tool-operations/file-result.ts ` with bounded Buffers and a callback that places the
271+ stored descriptors in the response. Their handlers preserve this result through dispatch, using
272+ ` InternalToolOperationHandler<InternalToolOperationResult> ` . Do not serialize file bytes as base64
273+ JSON: the executor's 10 MiB response cap runs before ordinary file postprocessing or large-value
274+ externalization. The shared executor stores files using trusted run or Copilot ownership.
275+
276+ External endpoints that return raw binary files explicitly declare ` request.responseType: 'binary' `
277+ and return ` output.file ` with ` { name, mimeType, data: buffer, size } ` from ` transformResponse ` .
278+ The executor applies the bounded file-transfer budget and persists the descriptor. This opt-in is
279+ for raw binary responses, not provider JSON containing base64 or tools that fetch attachments later.
280+ Keep provider-specific limits and bounded reads; a file declaration is not permission to enlarge
281+ arbitrary JSON responses.
282+
283+ Attachment readers that download files inside ` transformResponse ` need their own bounded reads:
284+ the first response cap does not cover subsequent fetches. Accept ` ToolResponseContext ` as the third
285+ transform argument, forward its ` signal ` , and share one ` AttachmentDownloadBudget ` across sequential
286+ downloads. Prefer raw provider endpoints over base64 metadata. Return the same file object in the
287+ declared ` file ` / ` file[] ` output and nested message associations; ` FileToolProcessor ` stores it once
288+ and replaces every alias with the same ` UserFile ` in both workflow and Copilot execution.
289+
290+ Preserve stored ` UserFile ` fields (` id ` , ` key ` , ` url ` , ` context ` , ` type ` , ` name ` , ` size ` ) in transforms;
291+ rebuilding the old ` { name, mimeType, data, size } ` shape discards the reference. File outputs do not
292+ need duplicate inline text/base64 aliases; the file system handles content materialization. When
293+ an existing tool explicitly exposes content aliases in its contract, preserve its legacy version and
294+ use the existing block/tool version pattern for a file-only output. Test a file over 10 MiB through
295+ executor admission, single persistence, trusted ownership, and the unchanged JSON cap. Avoid adding
296+ top-level filename, size, MIME type, URL, or success fields that merely repeat the canonical file or
297+ tool result; keep additional provider fields only when they convey distinct information.
298+
267299### Output Types
268300- ` 'string' ` , ` 'number' ` , ` 'boolean' ` - Primitives
269301- ` 'json' ` - Complex objects (use this, NOT 'object')
0 commit comments