Office Script (Excel Automate) implementation of the SheetCompressor encoding.
Developing this port? Read
docs/agents/host-port-constraints.mdfirst — the Office Scripts compiler is stricter than our localtsc, and fixes belong in the TS source (never the generated.osts).
Office Script is TypeScript, so this port does not re-implement the
algorithm. A checked-in generator bundles the verified TypeScript reference core
(packages/typescript/src/, covered by the 288-test suite) into a single
namespace SheetCompressorInternal and emits three .osts artifacts. Only the
main() host glue and chart extraction are hand-written. Because the core is
byte-derived from the reference, divergence risk is ~zero — and a drift-guard
test enforces it.
| File | What it is |
|---|---|
scripts/generate-bundle.ts |
The generator. Reads the core modules in dependency order, strips imports, drops the gpt-tokenizer surface from tokens.ts (keeping only the SPEC heuristic estimateTokens), and concatenates everything into one namespace. Fails loudly on cross-file name collisions. |
src/SheetCompressorInternal.generated.osts |
Generated, drift-guarded. The core namespace only — byte-identical to the TS reference. Do not edit by hand. |
src/SheetCompressor.osts |
The shippable script. Minimal declare namespace ExcelScript host types + the generated core + hand-written main(workbook, sheetName?). Paste this into Automate. |
src/conformance.osts |
In-host conformance harness. Embeds every fixtures/corpus/* input + golden as inlined literals, runs compress() over each, and prints a PASS/FAIL summary. |
test/driftguard.test.ts |
Vitest drift guard: re-runs the generator in memory and asserts every committed .osts matches byte-for-byte (mirrors the TS prompts.test.ts pattern). |
-
In Excel (Online or desktop), open Automate → New Script.
-
Open
src/SheetCompressor.osts, copy its entire contents, and paste over the editor's default script. -
Run.
mainreads the target sheet's used range, extracts chart descriptors, and returns:{ sheetName: string; origin: { row: number; col: number }; // 1-indexed A1 of the used range's top-left encodings: { anchor: { string; json; tokenEstimate }; invertedIndex: { string; json; tokenEstimate }; formatAggregation: { string; json; tokenEstimate }; }; charts: ChartDescriptor[]; // echo of the extracted charts rawBaseline: { tokenEstimate: number }; // un-compressed baseline tokens images: { name; base64 }[]; // empty unless includeImage = true }
main(workbook)runs on the active sheet. Pass a sheet name —main(workbook, "Sheet1")— to target a specific one.- The
jsonfields are pretty-printed (2-space) strings with a trailing newline, matching the golden format. - When wired into Power Automate, this return object is the flow's output.
Chart descriptors come from sheet.getCharts() metadata, mapped to the SPEC's
six type buckets (bar/line/pie/scatter/area/other). All host
accessors are defensive (try/catch), so a quirky chart yields a partial
descriptor rather than aborting the run. The optional base64 render
(chart.getImage()) is off by default — it is not part of the SPEC
ChartDescriptor and would bloat the prompt. Call main(workbook, sheet, true)
to also receive base64 images in the images field.
Host-verification note:
anchorRangeis left empty because the ExcelScriptChartsurface does not expose a clean anchor address. The SPEC treatsanchorRangeas opaque, so it renders asCHART(type)@. If a future host exposes the anchor, fill it inextractCharts()(in the generator'smainGlue()).
The returned encodings object carries all three encodings — anchor, invertedIndex,
formatAggregation — each with a string, a json, and a tokenEstimate. See the
project README for
side-by-side examples of what each string looks like.
This port produces the encodings in-host; the Office Scripts sandbox can't read repo files, so it does not bundle the reader/task prompt templates. To feed the output to a model:
- Take the encoding you want from the return object — e.g.
encodings.anchor.string. - Pair it with the shared templates in
prompts/: the reader explainer for that encoding (readers/anchor.md,readers/invertedIndex.md,readers/formatAggregation.md) as the system prompt, and a task template (tasks/sheetQA.md,tasks/cellValueLookup.md,tasks/tableRegionDetection.md) as the user message — fill the{ENCODING}/{ADDRESS}/{QUESTION}placeholders. - Send it to your model. Office Scripts can't call external APIs from the sandbox, so wire the
script into Power Automate:
main's return object becomes the flow input, where you build the prompt and call an LLM connector (an HTTP action, Azure OpenAI, etc.). For a quick manual check, copy a.stringout and paste it under the reader + task templates in any chat model.
The TypeScript port ships these templates as constants with a runnable end-to-end example — see the project README.
The sandbox can't read repo files, so the harness embeds the corpus.
-
Automate → New Script → paste the entire contents of
src/conformance.osts. -
Run. It needs no workbook data. Read the output log:
=== SheetCompressor conformance === PASS 14 / 14 fixtures All fixtures byte-identical to goldens. -
Paste that summary onto issue #18 to sign off. On any mismatch it prints the failing fixture id, field, and the first diff.
cd packages/officescript
npm install
npm run generate # regenerate all three .osts from the TS source + corpus
npm test # drift guard: committed .osts must match a fresh regen
npm run typecheck # tsc over each .osts in isolation, with NO node libsIf npm test fails after you change the TS reference core or a fixture, that's
drift — run npm run generate and commit the updated .osts.
The shippable and conformance scripts are scripts (script-scope main, a
declare namespace ExcelScript block) and share a global scope, so they can't
be compiled together. scripts/typecheck.ts copies each .osts into its own
isolated temp dir (under .build/, gitignored) as a .ts file, supplies a
minimal console host-global declaration, and runs tsc against a node-free
config — proving the bundle type-checks as plain ExcelScript.