bilig

Agent Framework Spreadsheet Tools

This page is for agent builders who already have an AI SDK, LangChain, Mastra, LlamaIndex.TS, LangGraph.js, CopilotKit, or Cloudflare Agents loop and need a spreadsheet tool that can do more than return a screenshot.

@bilig/headless gives the agent a WorkPaper object: sheets, addresses, formulas, computed readback, and JSON persistence. The framework wrapper should stay thin. Keep the workbook behavior in ordinary Node functions, then expose those functions through the tool shape your agent framework expects.

Real AI SDK generateText() Smoke

Run this first if you use the Vercel AI SDK and want to see the complete generateText() path execute WorkPaper tools:

git clone https://github.com/proompteng/bilig.git
cd bilig/examples/headless-workpaper
npm install
npm run agent:ai-sdk-generate-text

The smoke test uses the real ai package: generateText(), tool(), stepCountIs(), and MockLanguageModelV3 from ai/test. The mock model keeps the example provider-free. The WorkPaper tool execution is real TypeScript: readWorkPaperSummary reads Summary!A1:B5, then setWorkPaperInputCell writes Inputs!B3 = 0.4, recalculates dependent formulas, serializes the document, restores it, and returns structured readback.

Passing output includes:

{
  "apiShape": "AI SDK generateText -> tool -> execute",
  "modelCallCount": 2,
  "toolNames": ["readWorkPaperSummary", "setWorkPaperInputCell"],
  "writeResult": {
    "editedCell": "Inputs!B3",
    "before": { "expectedArr": 60000, "targetGap": -34000 },
    "after": { "expectedArr": 96000, "targetGap": 5600 },
    "checks": {
      "formulasPersisted": true,
      "restoredMatchesAfter": true,
      "expectedArrChanged": true
    }
  }
}

Inspect the runnable file here: examples/headless-workpaper/ai-sdk-generate-text-tool-smoke.ts.

Real AI SDK streamText() Smoke

Use this command when your agent path streams model output:

git clone https://github.com/proompteng/bilig.git
cd bilig/examples/headless-workpaper
npm install
npm run agent:ai-sdk-stream-text

The smoke test uses the real streamText() API, tool() wrappers, and simulateReadableStream() from ai. The deterministic MockLanguageModelV3 streams two WorkPaper tool calls, the AI SDK executes those tools, and the second model step streams the final answer. The WorkPaper behavior is the same as the generateText() smoke: read Summary!A1:B5, write Inputs!B3 = 0.4, recalculate, serialize, restore, and verify the restored summary.

Passing output includes:

{
  "apiShape": "AI SDK streamText -> tool -> execute",
  "modelStreamCallCount": 2,
  "streamChunkTypes": ["tool-call", "tool-result", "tool-call", "tool-result", "text-delta", "text-delta"],
  "writeResult": {
    "editedCell": "Inputs!B3",
    "before": { "expectedArr": 60000, "targetGap": -34000 },
    "after": { "expectedArr": 96000, "targetGap": 5600 },
    "checks": {
      "formulasPersisted": true,
      "restoredMatchesAfter": true,
      "expectedArrChanged": true
    }
  }
}

Inspect the runnable file here: examples/headless-workpaper/ai-sdk-stream-text-tool-smoke.ts.

Runnable Adapter Example

Run the dependency-free adapter example from a clean checkout:

git clone https://github.com/proompteng/bilig.git
cd bilig/examples/headless-workpaper
npm install
npm run agent:framework-adapters

The script builds the same workbook once per adapter family and exposes the same operations in the shapes those frameworks expect:

The example installs zod for real schemas, but it does not install the agent frameworks. That is deliberate. It keeps the WorkPaper contract visible and avoids hiding workbook logic behind framework setup.

What A Passing Run Proves

The mutating tool edits Inputs!B3 and then verifies the dependent summary formulas:

{
  "editedCell": "Inputs!B3",
  "before": {
    "expectedCustomers": 5,
    "expectedArr": 60000,
    "expansionArr": 66000,
    "targetGap": -34000
  },
  "after": {
    "expectedCustomers": 8,
    "expectedArr": 96000,
    "expansionArr": 105600,
    "targetGap": 5600
  },
  "checks": {
    "previousValue": 0.25,
    "newValue": 0.4,
    "formulasPersisted": true,
    "restoredMatchesAfter": true,
    "expectedArrChanged": true
  }
}

That is the useful part for agents. The tool result names the exact edited cell, returns before and after computed values, preserves formula contracts, serializes the workbook, restores it, and proves the restored output still matches the post-write state.

LangChain.js Structured Tool Smoke

The runnable adapter includes a LangChain-shaped smoke that keeps the tool output structured. The model can decide to call the tools, but the WorkPaper functions return JSON evidence instead of prose:

const tools = createLangChainTools(createWorkPaperTools(workbook))

const readResult = requireTool(tools, 'read_workpaper_summary').invoke({
  range: 'Summary!A1:B5',
})

const writeResult = requireTool(tools, 'set_workpaper_input_cell').invoke({
  sheetName: 'Inputs',
  address: 'B3',
  value: 0.4,
})

return {
  readResult,
  writeResult: {
    editedCell: writeResult.editedCell,
    before: writeResult.before,
    after: writeResult.after,
    checks: writeResult.checks,
  },
}

Those four fields are the important LangChain.js contract: editedCell says what changed, before and after prove the dependent formulas recalculated, and checks records persistence and restored-readback assertions. Keep that shape as the tool return value; do not collapse it into a sentence like “spreadsheet updated successfully.”

Adapter Boundary

Keep the adapter boring:

  1. Build small SDK-neutral WorkPaper functions first.
  2. Validate the sheet name and A1 address before writing.
  3. Read dependent formulas before and after the edit.
  4. Serialize and restore the WorkPaper document.
  5. Return formula contracts and restored readback in the tool result.

The framework wrapper can then expose those functions with the local tool shape: inputSchema and execute, schema and invoke, parameters and handler, or a ToolNode-style dispatch wrapper. The workbook behavior should not care which framework called it.

Official docs for the framework shapes:

Framework-specific WorkPaper pages:

Files To Inspect

When This Is A Good Fit

Use this pattern when the agent needs to change a forecast, pricing model, pipeline summary, budget check, or workbook-backed business rule and then prove the formulas reacted. If the tool only says “I updated the spreadsheet” without computed readback, it is not enough for production workflows.

Start with the adapter command above. If it saves you an agent-tooling spike, star the repository so the next person searching for spreadsheet tools can find it: https://github.com/proompteng/bilig/stargazers.

If it almost matches but a gap blocks adoption, use the adoption blocker form: https://github.com/proompteng/bilig/discussions/new?category=general.