---
title: Vault Operator v2.10
---

# Vault Operator v2.10: Cost-reduction wave -- bundle slim-down, auto-routing, cost-display alignment

The v2.10 line drops main.js from 7.3 MB to 4.17 MB by pulling the office-document and PDF libraries out of the main bundle, adds automatic model-routing so simple tool tasks get billed at helper-model rates instead of main-model rates, and corrects several cost-display issues that were misleading users about what their queries actually cost.

## Highlights

### Optional asset bundles for office + PDF (v2.9.0)

The exceljs, docx, pptxgenjs, and pdfjs-dist libraries used to sit inside main.js, accounting for about 3 MB of the bundle. They are now built as two separate Optional Assets that the plugin downloads on first use:

- `office-bundle.js` (~1.7 MB): exceljs + docx + pptxgenjs. Powers the `create_xlsx`, `create_docx`, and `create_pptx` tools plus the DOCX/XLSX/PPTX document parsers.
- `pdfjs-bundle.js` (~1.6 MB): pdfjs-dist plus its worker. Powers PDF text extraction in document ingestion.

Both ship in the `{version}-assets` release tag, are SHA256-verified before evaluation, and live in `<vault>/.vault-operator/assets/`. The plugin works without them; the affected tools report "not installed" with a link to Settings until the user installs. main.js is now below the Obsidian Sync Standard 5 MB threshold.

The First-Run Wizard offers them as recommended downloads; existing users get the install path under Settings > Vault Operator > Optional Assets.

### TaskRouter (v2.10.0, v2.10.1)

A new pre-call classifier routes simple tool tasks onto the configured helper model. When you type something like "create a docx with title Foo", the router classifies the prompt as `simple` and runs the request on Haiku or Sonnet instead of your main model (Opus or similar). Research-style prompts like "find me all notes on X" stay on the main model.

Two-stage design:

- Regex classifier matches tool verbs in German and English ("erstelle xlsx", "lies note.md") and research verbs ("such", "fasse zusammen", "analyse"). Returns `simple` / `complex` / `unknown`.
- Optional LLM fallback for `unknown` cases issues a tiny Haiku call that answers `simple` or `complex` in one token.

The router escalates back to the main model after two consecutive tool errors so a weaker helper never gets stuck.

Toggle: Settings > Vault Operator > Loop > "Auto-route simple tasks to helper model". Default on when a helper model is configured.

### Locale-correct EUR cost display (v2.10.0)

The cost footer used a mixed `4.2¢` / `1.23€` format that did not match German conventions. v2.10.0 switches to `Intl.NumberFormat('de-DE', currency: EUR)` with up to four fraction digits so sub-cent amounts stay legible. Examples: `0,02 €`, `0,84 €`, `0,0005 €`.

### Cost display follows the actual model (v2.10.2)

Before v2.10.2, the footer always showed Opus pricing even when TaskRouter routed a call to Haiku. A typical XLSX creation looked like 0,57 EUR in the UI but actually ran on Haiku and cost about 0,04 EUR. v2.10.2 threads the actual model id through the usage callback so the footer prices the call on the correct model and resolves provider state from the same id. Subscription providers still show the would-be API spend with a `(~ via Sub)` tag so you can see what the call would have cost outside your subscription.

### Default output folder for generated files (v2.10.0)

When the agent creates a DOCX, XLSX, PPTX, Excalidraw, or drawio file with only a filename (no path), the file now lands in `Inbox/` instead of the vault root. The default folder is configurable under Settings > Vault Operator > Vault > "Default output folder".

### Pricing-maintenance reminder (v2.10.0)

The pricing table is hand-maintained; the three vendors (Anthropic, OpenAI, Google) do not publish machine-readable rate cards. v2.10.0 adds a `PRICING_LAST_UPDATED` constant and a one-shot console warning at plugin load when the table is older than 90 days.

### Office tools no longer deferred (v2.10.0)

The `create_xlsx`, `create_docx`, and `create_pptx` tools used to be deferred (FEATURE-1600) -- the model had to call `find_tool` first to activate them, which invalidated the prompt cache and cost about 75 cents extra per call at Opus rates. They are back in the standard prompt now. The schemas add about 150 tokens to every prompt, but the cache stays warm.

## Security

- AUDIT-025 closed three High-severity findings: OptionalAssetManager `load()` now verifies both the sidecar SHA256 file AND the binary content (closes a TOCTOU window where a vault-write attacker could swap the binary while leaving the sidecar intact); two GitHub code-scanning alerts (#67 insecure-randomness fallback in ConversationStore, #68 URL substring sanitization in AdapterRegistry test) also resolved.
- AUDIT-026 verdict: Green. 0 Critical / 0 High / 0 Medium / 1 Low (cosmetic provider-label) / 2 Info (intentional layering, theoretical ReDoS). npm audit clean across all severities.

## Review-bot pass

The v2.10.3 release closed the source-code warnings from the bot's source-level scan tier (BundleLoader file-level disable header, pdfjs-entry type, unused destructured imports in CreateDocxTool, unused Notice import in DebugTab) plus CSS quick wins (6-digit hex, `text-decoration` shorthand).

v2.10.4 addressed the CSS lint follow-ups: 13 redundant `user-select: text !important` declarations consolidated into one ruleset, both `:has()` selectors replaced with explicit class flags set by the relevant component, the `all: unset` shorthand replaced with explicit resets.

v2.10.5 (this release) continues the CSS pass with the remaining Tier 7 (CSS lint) findings:

- Nine duplicate selectors in `styles.css` merged in place (`.tool-status`, `.chat-messages`, `.message-content`, `.user-message .message-content`, `.assistant-message .message-content` + its `pre` and `code` children, `.tp-subcat-label`, `.history-row-action.history-row-action-pinned`, `.context-condensed-badge`). Cascade-order values preserved so the rendered result is identical.
- Five `!important` declarations in the VitePress docs theme replaced with class-repetition specificity bumps (`.VPHero.VPHero .text`, `.VPNavBarExtra.VPNavBarExtra`). The doubled class adds one specificity level without needing a new parent selector, beating the VitePress default rules without `!important`.
- The three `:root` blocks in `docs/.vitepress/theme/custom.css` consolidated into one block with grouped comments.
- The `text-decoration: underline dotted` shorthand split into longhand (`text-decoration-line` + `text-decoration-style`) because the shorthand with a style argument is only partially supported on Obsidian 1.11.x.
- A dead `.agent-u-visible { display: '' !important; }` rule (invalid CSS value, the class was never referenced in source) removed.

Ten `!important` declarations remain in `styles.css`: all are utility classes (`.agent-u-*`) or chat-input theme overrides where higher specificity cannot replace `!important` cleanly. These are flagged by the bot as Warnings, not Errors, and do not block submission.

v2.10.6 takes the CSS pass one step further by removing the remaining theme-override `!important` instances and resolving the `text-decoration` Chromium-coverage warning:

- The `.chat-input-wrapper:focus-within` focus-ring overrides (three properties) now use the `.chat-input-wrapper.chat-input-wrapper:focus-within` selector. Class-repetition bumps specificity to (0,3,0) without `!important`.
- `.followup-item` whitespace and height overrides (two properties) use the same trick.
- All four `!important` declarations in `docs/.vitepress/theme/custom.css` (`VPNavBarTitle`, `VPNavBarSearch`, and the two print-media hides) replaced with class-repetition.
- The `text-decoration` family was flagged by the bot as only partially supported on Obsidian's Chromium even after the longhand split in v2.10.5. The `.vault-health-note-link` dotted underline is now drawn with `border-bottom: 1px dotted currentColor`, which is fully supported and produces the same visual.

After v2.10.6 the bot still reports four CSS `!important` declarations, all on utility classes (`.agent-u-hidden`, `.agent-u-visibility-hidden`, `.agent-u-height-auto`). Utility classes must beat any element-specific theme rule and `!important` is the correct CSS for that role. The bot flags them; the warnings are accepted by design.

Two bundle-heuristic warnings remain as documented false positives: the "Bitcoin wallet address" finding is a 35-character base58-look-alike substring in the base64-encoded sql.js WASM (the plugin has no funding mechanic), and the four dynamic `<script>` element creations come from the setimmediate polyfill transitively via jszip (dead code path in modern Chromium). Both are Warnings, not Errors; the plugin submission is not blocked.

One informational finding ("Plugin might make requests to 1 external domain: esm.sh") refers to the EvaluateExpression sandbox loading user-requested npm ESM bundles from a CDN when the user runs custom agent code. The README describes this under "Network usage".

## Migration notes

- Existing users with Office documents will see "Office Document Support is not installed" on the first xlsx/docx/pptx call. One click in Settings installs the bundle; no manual migration.
- The TaskRouter is on by default. Users who prefer the old behaviour can toggle it off in Settings > Loop.
- The default output folder defaults to `Inbox/`. Users with `Inbox/` already in the vault see no change. Users without can edit the path under Settings > Vault.

## Version index

| Version | What |
|---------|------|
| v2.9.0 | Office + PDF libraries as Optional Assets, BundleLoader, main.js 7.3 → 4.2 MB |
| v2.9.1 | OptionalAssetManager content-hash verification, GitHub alerts #67 + #68 resolved |
| v2.9.2 | sandbox-worker eslint-disable removed (review-bot tier 4) |
| v2.10.0 | Cost-reduction wave: defer-cleanup, formatEur, defaultOutputFolder, TaskRouter |
| v2.10.1 | TaskRouter `<context>` strip fix |
| v2.10.2 | Cost display follows routed model |
| v2.10.3 | Review-bot pass: source warnings + CSS quick wins |
| v2.10.4 | AUDIT-026 L-1 fix, CSS !important consolidation, :has() and all: shorthand removed |
| v2.10.5 | Review-bot CSS pass: 9 duplicate selectors merged, 5 !important removed via specificity bumps, text-decoration longhand, :root consolidated |
| v2.10.6 | Review-bot CSS follow-up: text-decoration replaced with border-bottom, 9 more !important removed via class-repetition (utility classes only remain) |
