Part of the Gmail epic. Independent of the Gmail work — useful anywhere a form must be produced.
Why
There is no code in PAGS that writes a PDF. The only PDF handling is lib/resume-parse.ts, which sends one to Claude to read:
// lib/resume-parse.ts:65
{ type: "document", source: { type: "base64", media_type: "application/pdf", data: bytesToBase64(bytes) } },
Reading is solved; producing a filled form is not attempted anywhere.
Scope
A fill_pdf_form tool, tier: "standard", scope: "read" (it touches no external system), mutates: true (it writes a new file into the instance store).
Input: a source file id in the instance store, plus values as a field-name → value map. Output: a new file id for the filled PDF.
Two paths, because real forms are not all the same:
- AcroForm present — fill the named fields with
pdf-lib and flatten. Deterministic, and the field names can be listed back to the model first so it knows what it is filling.
- No form fields (flat scan) — do NOT silently produce an unchanged PDF, which is the failure mode worth designing against. Report that the source has no fillable fields, and produce a clean typed answer document instead: a generated PDF listing every question and the supplied answer, suitable to attach alongside the original.
A companion read-only inspect_pdf_form that lists the field names (or reports there are none) is what makes path 1 usable — the model must not guess field names.
Risks to check during implementation
- Bundle size.
pdf-lib is roughly 1 MB minified. workers/api is on the paid 10 MB limit, so this should fit, but the built size must be measured before and after rather than assumed, and it should be a dynamic import() on the handler path like lib/crypto.js and lib/gmail.js already are in storage-tools.ts.
- Worker CPU. Flattening a large scanned PDF is not free. Cap the input size and fail with a clear message rather than hitting the wall.
Not verified
Whether the forms this is actually wanted for are AcroForms. The motivating attachment has not been inspected yet, so path 2 is designed as a first-class outcome rather than an error branch. If real club forms turn out to be flat scans in the common case, path 2 is the product and path 1 is the bonus — worth re-reading this issue once a real sample exists.
Part of the Gmail epic. Independent of the Gmail work — useful anywhere a form must be produced.
Why
There is no code in PAGS that writes a PDF. The only PDF handling is
lib/resume-parse.ts, which sends one to Claude to read:Reading is solved; producing a filled form is not attempted anywhere.
Scope
A
fill_pdf_formtool,tier: "standard",scope: "read"(it touches no external system),mutates: true(it writes a new file into the instance store).Input: a source file id in the instance store, plus
valuesas a field-name → value map. Output: a new file id for the filled PDF.Two paths, because real forms are not all the same:
pdf-liband flatten. Deterministic, and the field names can be listed back to the model first so it knows what it is filling.A companion read-only
inspect_pdf_formthat lists the field names (or reports there are none) is what makes path 1 usable — the model must not guess field names.Risks to check during implementation
pdf-libis roughly 1 MB minified.workers/apiis on the paid 10 MB limit, so this should fit, but the built size must be measured before and after rather than assumed, and it should be a dynamicimport()on the handler path likelib/crypto.jsandlib/gmail.jsalready are instorage-tools.ts.Not verified
Whether the forms this is actually wanted for are AcroForms. The motivating attachment has not been inspected yet, so path 2 is designed as a first-class outcome rather than an error branch. If real club forms turn out to be flat scans in the common case, path 2 is the product and path 1 is the bonus — worth re-reading this issue once a real sample exists.