Dropping an .xlsx into the composer did nothing: the accept filter silently
discarded it, and widening that filter alone would have killed the run, because
the pinned @ai-sdk/openai@3 converter accepts no document media type but
application/pdf and throws before a request is made.
Spreadsheets now work end to end:
- A dependency-free .xlsx / .csv reader (ZIP central directory + OOXML through
DecompressionStream and DOMParser). It decodes date serials via styles.xml so
a date does not render as 45231, and places cells by column reference so a
gap does not shift every later value under the wrong header.
- The sheet is rendered to a column-aligned table document. Comparisons of
table encodings put a markdown-style aligned table well ahead of CSV for
model comprehension, and the same document is what a human sees.
- buildPdf paginates. It emitted a single page and drew overflow past the
bottom margin, so anything after ~48 rows was silently absent from a
perfectly valid PDF. Single-page output is byte-identical, so the existing
golden assertions stand.
- A per-run bridge around the agent swaps the media type to application/pdf on
the model leg only, and restores the original type and metadata on the way
back. Both the composer chip and the transcript therefore read XLS with the
real filename. The bridge keys on the exact payload, so a genuine PDF
attachment is never mislabelled.
- onUploadFailed is wired. A rejected or unreadable file now says so instead of
vanishing with no trace.
The demo workbook is committed as a generator rather than a binary blob, so the
fixture exercises the real reader: shared strings, date-styled serials, a
second sheet, deflate.
Also moves @copilotkit/* to 1.71.0 and @ag-ui/* to 0.0.59, and deletes the
@ag-ui canary overrides from both pnpm-workspace.yaml and package.json. Those
existed only because a released @ag-ui/core rejected SUBAGENT_* events and
killed banking's harness console; stable 0.0.59 ships SUBAGENT_STARTED,
SUBAGENT_FINISHED and SUBAGENT_ERROR, and 1.71.0 depends on exactly 0.0.59, so
the overrides would now hold the tree behind its own dependency.
Verified on the running demo, not just in tests: gen UI renders, the subagent
harness completes the offsite run (14 rows, 6 merchants, $2,377.15, 1m 4s),
memory recall returns seeded memories, and a spreadsheet answers with a table
built from the real file. Lint, typecheck, 2494 unit tests and build all pass.
Checked the reskin skill for staleness: buildPdf was documented as single-page
and two passages said onUploadFailed is wired by nobody. Both corrected here.