Skip to content

v0.4.0 — PDF support + spec v0.2 (kind: document) - #3

Merged
rrader26 merged 3 commits into
feat/v0.3.0-runtime-sdkfrom
feat/m2-pdf-support
May 10, 2026
Merged

v0.4.0 — PDF support + spec v0.2 (kind: document)#3
rrader26 merged 3 commits into
feat/v0.3.0-runtime-sdkfrom
feat/m2-pdf-support

Conversation

@rrader26

Copy link
Copy Markdown
Contributor

Summary

PDF support — the same wire format now applies to documents. `convertPdf()` produces a `kind: 'document'` AgentMark snapshot from PDF bytes, mirroring `convertPage()` for web pages.

Stacked on top of #2 (M1 / v0.3.0). Merge that one first.

Spec v0.2

Backwards-compatible extension — v0.1 snapshots without `kind` still validate.

  • `kind: 'webpage' | 'document' | 'form'` discriminator
  • `document` metadata block — pages, author, created_at, modified_at, format, format_version, ocr_used
  • `[PAGE:p_n]` body tag for page-boundary markers
  • New `schema/agentmark-v0.2.json`; validator chooses v0.1 or v0.2 based on declared version
  • `AGENTMARK_VERSION` constant bumped to `'0.2'`

PDF converter

  • `convertPdf({ data, sourceUrl, ... })` — main entry. Same `ConversionResult` shape as `convertPage()`.
  • `extractPdf()` — lower-level: returns a structured `PdfDocument` for callers wanting custom inference.
  • `buildBodyFromPdf()` — body builder, exposed independently.
  • Heading detection via font-size outliers (configurable threshold).
  • Bullet + ordered list detection via leading-glyph patterns.
  • Paragraph reflow with vertical-gap break detection.
  • PDF date format parser (`D:YYYYMMDDHHMMSS+HH'mm'` → ISO 8601).

Dependencies

  • `pdfjs-dist@^4` as optional peer dep — web-only callers install nothing extra. Lazy-imported via dynamic import; throws clean `SnapshotError` with install instructions if missing.
  • `pdf-lib` as devDep for test fixtures (PDFs built in-process, not committed as binaries).

Tests

176 total, all passing:

  • 13 new spec-v0.2 tests (kind discriminator, document metadata, PAGE markers, version negotiation, v0.1 backwards compat)
  • 12 new PDF converter tests (metadata, page counts, heading promotion, bullet detection, PAGE markers, title fallback, vendor extensions, logger flow, garbage-input handling)

Not in this PR (v0.5 / M3)

  • OCR for scanned PDFs (interface designed via `document.ocr_used` flag)
  • Table detection
  • AcroForm support (PDF forms with fillable fields → AgentMark actions)

Test plan

  • Merge v0.3.0 — production-ready SDK surface + action executor #2 first
  • `npm install && npm run build && npm test` — all 156 unit + benchmark tests pass
  • CI: `test`, `integration`, `macos-smoke` all green
  • Smoke-test PDF flow: `npx tsx examples/pdf.ts `
  • Verify v0.1 backwards compat: existing v0.1 snapshots still parse + validate
  • Spec v0.2 reads sensibly — particularly the `kind` + `document` envelope additions
  • CHANGELOG.md entry for v0.4.0 reads cleanly

🤖 Generated with Claude Code

rrader26-sys and others added 3 commits May 10, 2026 09:09
PDF support. The same wire format now applies to documents — convertPdf()
produces a kind: 'document' AgentMark snapshot from PDF bytes.

Spec v0.2 extension
- Adds kind: 'webpage' | 'document' | 'form' discriminator (v0.2+)
- Adds optional document metadata block: pages, author, created_at,
  modified_at, format, format_version, ocr_used
- Adds [PAGE:p_n] body tag for page-boundary markers in documents
- Fully backwards-compatible: v0.1 snapshots without kind still validate
- New schema/agentmark-v0.2.json; validator picks v0.1 or v0.2 based on
  declared agentmark version
- AGENTMARK_VERSION constant bumped from '0.1' to '0.2'

PDF converter
- convertPdf({ data, sourceUrl, ... }): main entry. Returns the same
  ConversionResult shape as convertPage() so downstream LLM pipelines
  are uniform regardless of source surface.
- extractPdf(): lower-level extraction returning structured PdfDocument
  (positioned text items + metadata) for callers wanting custom
  structural inference.
- buildBodyFromPdf(): body-segment builder consumed by convertPdf,
  exposed for callers wanting a different envelope.
- Heading detection via font-size outliers (configurable threshold).
- Bullet + ordered list detection via leading-glyph patterns.
- Paragraph reflow with vertical-gap-based break detection.
- PDF metadata parser handles non-ISO PDF date format
  (D:YYYYMMDDHHMMSS+HH'mm' → ISO 8601).

Dependencies
- pdfjs-dist@^4 added as optional peer dependency (web-only callers
  pay no install cost). Lazy-imported via dynamic import; throws clean
  SnapshotError with install instructions if missing.
- pdf-lib added as devDependency for test-fixture generation
  (PDFs constructed in-process, not committed as binaries).

Tests (166 unit + 10 real-Chromium = 176 total, all passing)
- 13 new spec-v0.2 tests covering kind discriminator, document metadata,
  PAGE markers, version negotiation, backwards compat with v0.1.
- 12 new PDF converter tests covering metadata extraction, page counts,
  heading promotion, bullet detection, PAGE markers, title fallback,
  vendor extensions, logger event flow, error handling on garbage input.

Not yet shipped (deferred to v0.5)
- OCR for scanned PDFs (interface designed via document.ocr_used flag)
- Table detection
- AcroForm support (M3)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ening)

Builds a diagnostic CLI for evaluating PDF→AgentMark quality on real-world
documents (county forms, etc.) before extending PDF features. Also fixes
two real bugs surfaced by running the diagnostic on the first sample.

Bug fixes
- pdf-extractor: defensively copy input bytes before passing to pdfjs-dist.
  pdfjs-dist (a) does a strict prototype check that rejects Node's Buffer
  even though it extends Uint8Array, and (b) transfers ownership of the
  underlying ArrayBuffer during parse, so calling extractPdf twice on the
  same data fails with "Cannot perform Construct on a detached ArrayBuffer".
  Both regressions now have unit tests.

Diagnostic tool — examples/diagnose-pdf.ts
- Per-page diagnostics: text-item count, font size distribution,
  median + outlier detection, suspected-scan flag (zero text items),
  suspected-multi-column flag (X-coordinate clustering)
- Body-builder analysis: heading/paragraph/list counts, page-marker count
- AgentMark size + estimated token cost
- Quality score (0-100, heuristic)
- Aggregated flag counts across a corpus
- Suggestions tied to specific failure modes (OCR, multi-column, etc.)
- Outputs Markdown report; --out flag writes to file
- Accepts a single PDF or a directory of PDFs

Use: npx tsx examples/diagnose-pdf.ts <pdf-or-dir> [--out report.md]

Tests
- 168 total now (was 166), 14 PDF tests including 2 new regression tests
  for the Buffer + ArrayBuffer-detachment fixes

devDeps
- tsx@^4 added so examples can be run with npx tsx without
  external installation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rance corpus)

Validation findings from a 12-doc real-world insurance corpus drive two
hardening improvements.

Improvements
- Bold-font heading detection in body-builder. Detects headings encoded
  via font *weight* (e.g. "Helvetica-Bold") at body-sized point sizes,
  not just outlier sizes. Working on PDFs with proper bold encoding (e.g.
  Eventbrite tickets gained 6 headings; previously 0). Conservative
  guards: max 80 chars, ≤20% size delta from median, all items must use
  bold font names.
- Diagnostic CLI now classifies source mode into:
  - real_text: text streams present — extraction works
  - print_to_pdf_vector: glyphs rendered as filled paths (Microsoft
    Print To PDF / similar — needs OCR or original source)
  - scan: image-only pages (scanner output — needs OCR)
  - mixed: some text + some image pages
  - empty / unknown
  Classification uses producer metadata + operator histograms
  (showText vs paintImageXObject vs constructPath/fill).
- Diagnostic prints source-mode breakdown table + per-mode suggestions
  so v0.5 priorities are obvious from the report alone.

New investigation tools
- examples/probe-pdf.ts: dump operator histogram, metadata, font count
  for a single problem PDF.
- examples/dump-fonts.ts: enumerate distinct fonts + sample text per
  font to debug heading-detection failures.

Insurance corpus results (12 docs)
- real_text: 6 (50%) — extraction works (FB renewals, tickets, CORP
  Articles, PRINTHEAD AGREEMENT)
- print_to_pdf_vector: 4 (33%) — Erie auto/home quotes printed via
  "Microsoft: Print To PDF"
- scan: 2 (17%) — Flood Map screenshot, NC reseller cert (Epson
  ScanSmart)
- 0 outright failures
- All 6 failing docs need OCR — that's the v0.5 priority

Limitations surfaced (deferred to v0.5)
- Form-style PDFs (insurance renewals) use anonymized embedded fonts
  and have field labels at SMALLER font sizes than body text, so neither
  size-based nor weight-based heading inference applies. Form-structure
  detection (label/value pairs) is a v0.5 feature paired with M3 AcroForm
  support.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants