bilig

Why Agents Need Workbook APIs, Not Spreadsheet Screenshots

Agents can click through a spreadsheet UI. That does not make the UI a good runtime boundary.

Screenshots hide formulas, make structural edits ambiguous, and turn verification into a visual guess. If the workflow depends on workbook state, the agent needs an API that can read formulas, write cells, recalculate, and prove what changed.

@bilig/workpaper is the public WorkPaper runtime from bilig. It is the canonical scoped npm package for Node services, coding agents, and local workbook automation that need spreadsheet behavior without opening a browser grid.

The Problem With Screen-Driven Spreadsheets

Spreadsheets are useful because they combine a document model, formulas, structural editing, validation, and persistence. A grid UI is only one way to operate that model.

When an agent drives the UI directly, several useful facts become hard to trust:

Screenshots are still useful for final human inspection. They should not be the main contract between an agent and workbook logic.

The Better Boundary

A workbook API gives the agent explicit operations and explicit readback:

That shape fits backend jobs, coding-agent tools, and local-first workflows better than asking a model to infer state from a rendered grid.

Run The Maintained Eval First

The shortest trial starts from an empty Node project and uses the published npm package:

mkdir bilig-workpaper-eval
cd bilig-workpaper-eval
npm init -y
npm pkg set type=module
npm install @bilig/workpaper
npm install -D tsx typescript @types/node
curl -fsSLo eval.ts https://proompteng.github.io/bilig/npm-eval.ts
npx tsx eval.ts

Expected output:

{
  "before": 24000,
  "after": 38400,
  "afterRestore": 38400,
  "sheets": ["Inputs", "Summary"],
  "bytes": 1000,
  "verified": true
}

The byte count can change between versions. The important check is that verified is true and the restored workbook returns the same calculated value as the edited workbook.

What The API Looks Like

The public package is installable as a normal Node dependency:

npm install @bilig/workpaper

Build a workbook, change one input, read the recalculated value, and restore the saved document:

import {
  WorkPaper,
  createWorkPaperFromDocument,
  exportWorkPaperDocument,
  parseWorkPaperDocument,
  serializeWorkPaperDocument,
} from '@bilig/workpaper'

const workbook = WorkPaper.buildFromSheets({
  Inputs: [
    ['Metric', 'Value'],
    ['Customers', 20],
    ['Average revenue', 1200],
  ],
  Summary: [
    ['Metric', 'Value'],
    ['Revenue', '=Inputs!B2*Inputs!B3'],
  ],
})

const inputs = workbook.getSheetId('Inputs')
const summary = workbook.getSheetId('Summary')
if (inputs === undefined || summary === undefined) {
  throw new Error('Workbook did not create the expected sheets')
}

const before = workbook.getCellValue({ sheet: summary, row: 1, col: 1 })
workbook.setCellContents({ sheet: inputs, row: 1, col: 1 }, 32)
const after = workbook.getCellValue({ sheet: summary, row: 1, col: 1 })

const saved = serializeWorkPaperDocument(exportWorkPaperDocument(workbook, { includeConfig: true }))
const restored = createWorkPaperFromDocument(parseWorkPaperDocument(saved))
const restoredSummary = restored.getSheetId('Summary')
if (restoredSummary === undefined) {
  throw new Error('Restored workbook did not create the Summary sheet')
}

console.log({
  before,
  after,
  afterRestore: restored.getCellValue({ sheet: restoredSummary, row: 1, col: 1 }),
  sheets: restored.getSheetNames(),
})

The maintained external-consumer example is in examples/headless-workpaper.

MCP In 30 Seconds

If the agent already supports MCP, skip the TypeScript wrapper and start the published stdio server in file-backed mode:

npm exec --package @bilig/workpaper@latest -- bilig-workpaper-mcp --workpaper ./pricing.workpaper.json --init-demo-workpaper --writable

That command creates pricing.workpaper.json only when it is missing, exposes typed workbook tools, and persists set_cell_contents edits back to the same WorkPaper JSON file.

Expected tools:

Ask the agent for this proof object after an edit:

{
  "editedCell": "Inputs!B3",
  "before": {
    "Summary!B3": 60000
  },
  "after": {
    "Summary!B3": 96000
  },
  "checks": {
    "formulaReadbackChanged": true,
    "exportedWorkPaperDocument": true,
    "restoredMatchesAfter": true
  }
}

The important distinction is that the agent is not reporting “clicked” or “updated.” It is reporting the exact edited cell, the recalculated dependent value, and persistence evidence.

What This Enables

For an agent tool, a WorkPaper API can expose a small set of reliable commands:

Those commands produce deterministic outputs that can be tested, logged, and replayed. The rendered spreadsheet can stay a human-facing view instead of the agent’s source of truth.

For a Node service, the same model supports formula-backed business logic without bundling a spreadsheet application into the service path.

What Bilig Does Not Claim

bilig is not a finished Excel clone. It does not claim full Excel formula parity. It does not claim every benchmark p95 row is faster than HyperFormula.

The current public claim is narrower: @bilig/workpaper exposes a WorkPaper API for programmatic workbook creation, formulas, structural operations, persistence, and checked-in benchmark evidence. The evidence note records WorkPaper 100/100 mean wins on scorecard-eligible comparable workloads against HyperFormula-style workloads, with the p95 evidence documented separately.

Read the benchmark note here: docs/headless-workpaper-benchmark-evidence.md.

Try It

If this is relevant to an agent or Node workflow, star the repo as a bookmark: https://github.com/proompteng/bilig/stargazers