Files
OmniRoute/docs/compression/RTK_COMPRESSION.md
Diego Rodrigues de Sa e Souza 5ac4637816 docs: reorganize docs/ + accuracy audit + drop Node 20 (rebased onto current release tip) (#5262)
* docs: move superpowers/research artifacts to isolated _tasks repo + docs tree cleanup

- Move docs/superpowers/{plans,specs} and docs/research/* into the gitignored,
  separately-versioned _tasks/ repo; untrack the two tracked research design docs.
- Add CLAUDE.md "Planning & Research Artifacts" section overriding the superpowers
  default save paths (docs/... -> _tasks/...); align REPOSITORY_MAP and
  DOCUMENTATION_OVERHAUL_PLAN with the new convention.
- Drop 4 now-obsolete /api/discovery/* entries from check-docs-symbols allowlist
  (stale-enforcement) and refresh code/spec path comments to _tasks/...
- Sweeps in concurrent docs-tree restructuring (root-level provider/guide docs,
  compression spec cleanup, .mcp.json.example removal).

* docs: reorganize docs/ tree + fix stale facts across ~26 docs

Phase A — reorganization:
- Move 7 orphan root docs into subfolders (providers/ created; TIERS+USAGE_QUOTA→guides/;
  plugins+PLUGIN_SDK→frameworks/); delete 8 obsolete/redundant docs (SUBMIT_PR superseded
  by CONTRIBUTING; DOCUMENTATION_OVERHAUL_PLAN; INCIDENT_RESPONSE/PERF_BUDGETS/THREAT_MODEL;
  3 ops snapshots). Rebuild README index (was missing ~40 files) + per-folder meta.json nav.
- Clean 14 dangling doc-path references in bin/ ops scripts, scripts/, workflow, tests;
  fix the dockerignore-docs-coverage required-docs path (PROVIDERS→providers/CLAUDE_WEB).

Phase B — content accuracy (verified against code, not the audit summary):
- Functional: ENVIRONMENT flag defaults (INPUT_SANITIZER/MCP_ENFORCE_SCOPES=true,
  COMPRESS_DESCRIPTIONS=false, dynamic heap); MCP-SERVER notion tool names (omniroute_*→
  notion_*) + counts 87→94; coverage gate 75/70→60/60/60/60 (RELEASE_CHECKLIST, COVERAGE_PLAN,
  ERROR_SANITIZATION, CONTRIBUTING); pre-push hook description; regenerate PROVIDER_REFERENCE (237).
- Count drift: providers 237, executors 70, migrations 106, db modules 94, oauth 19,
  strategies 17, MCP 94, flags 38, TS 6.0, open-sse ~900/services 294 across architecture/
  frameworks/ops docs; AUTO-COMBO 9→12 factors w/ correct DEFAULT_WEIGHTS; REASONING +2
  patterns; STEALTH UA defaults; AGENT_PROTOCOLS +cursor-cloud/list-capabilities;
  LANGUAGE_PACKS +id pack.
- Kept Node 20 (runtime guard accepts 20.20.2+; only engines is stricter) and MCP scopes=13
  (mcpScopes.ts) — both were correct in the docs; corrected only the attribution.

* docs: finish content refresh — compression engines, CLI_TOKEN merge, metadata sweep

- Compression: document the additional built-in engines (CCR, headroom, ionizer,
  session-dedup) in COMPRESSION_ENGINES; clarify LLMLingua-2 is the ultra-mode SLM
  backend + cross-ref the extra engines in EXTENDING_COMPRESSION; add the id
  (Indonesian) language pack to LANGUAGE_PACKS.
- AUTO-COMBO: replace the orphan 'How tiers fit' weight table (stale weights) with a
  pointer to the canonical 12-factor DEFAULT_WEIGHTS table.
- Security: merge CLI_TOKEN_AUTH.md (legacy 32-char SHA-256 format) into CLI_TOKEN.md
  as a 'Legacy format — still accepted' section (server accepts both HMAC + legacy),
  delete CLI_TOKEN_AUTH.md, drop it from the index + security nav.
- Metadata: bump stale frontmatter (version/lastUpdated) to 3.8.40/2026-06-28 across the
  doc set audited this pass, and normalize the in-body 'Last updated' header lines to match.

* fix(runtime): drop Node 20 from supported range + align all docs/diagrams/counts

- Node minimum is now 22 (aligned with package.json engines). SUPPORTED_NODE_RANGE in
  src/shared/utils/nodeRuntimeSupport.ts (and the bin/ mirror) drops the 20.x line →
  '>=22.22.2 <23 || >=24.0.0 <27'; getNodeRuntimeSupport now rejects Node 20 as
  unsupported-major. Test updated (TDD): node-runtime-support.test.ts asserts Node 20
  rejected. Docs aligned (TROUBLESHOOTING ×2, TERMUX, RELEASE_CHECKLIST, CODEBASE,
  CLI-TOOLS, README, llm.txt + 42 i18n llm.txt mirrors, skills/cli-serve).
- Diagrams regenerated: mcp-tools-87 -> mcp-tools-94 (34 base + pool 6 = 94) and
  auto-combo-9factor -> auto-combo-12factor (correct DEFAULT_WEIGHTS); SVGs re-rendered
  via mermaid-cli; doc refs + diagrams/README updated; fixed a pre-existing broken
  resilience-3layers image path.
- CLAUDE.md + AGENTS.md aligned to real counts (237 providers, 94 MCP tools / 34 base,
  106 migrations, 94 db modules, 12-factor auto-combo, 17 strategies); README provider
  count 231 -> 237; executor count corrected to 68 (provider executors, excl base/index)
  and OAuth to 18 across architecture docs. check:docs-all now passes (0 strict drift,
  0 broken links); removed dead .mcp.json.example doc link.

* fix(services): update installer Node hint to >=22.22.2 (aligned with dropped Node 20)

* docs: realign counts to current release tip after rebase

The release tip advanced while this work was in flight (Gemini CLI provider/executor
removed by #5246, plus other PRs). Re-counted against the current code and updated:
providers 237->236, executors 68->67, OAuth modules 18->17, open-sse services 294->298;
regenerated PROVIDER_REFERENCE.md (236). check:docs-all passes (0 strict drift).

* docs(changelog) + i18n: record Node 20 drop + fix nodeIncompatibleHint

- CHANGELOG: add [3.8.40] entries for the Node 20.x removal (runtime) and the docs
  reorganization/accuracy audit.
- i18n: nodeIncompatibleHint across all 42 locales no longer lists Node 20.x as
  supported (ASCII + CJK full-width variants), aligned with the dropped Node 20.

* fix(docs): repair CI breakages from the doc moves

- test: cli-plugin-system asserted docs/dev/plugins.md exists; the file moved to
  docs/frameworks/PLUGINS.md — point the test at the new path (Unit fast-path 2/2 fix).
- frontmatter: PLUGINS.md and PLUGIN_SDK.md moved into the fumadocs-indexed
  docs/frameworks/ which requires a 'title' frontmatter; the missing frontmatter
  failed the Next.js MDX build (dast-smoke 'invalid frontmatter'). Added frontmatter
  to both, plus the providers/ docs (consistency; that folder is not indexed).
2026-06-28 22:07:18 -03:00

27 KiB
Raw Blame History

title, version, lastUpdated
title version lastUpdated
RTK Compression 3.8.40 2026-06-28

RTK Compression

RTK compression is OmniRoute's command-aware compression engine for terminal and tool output. It is designed for coding-agent sessions where most context growth comes from test logs, build output, package manager noise, shell transcripts, Docker output, git output, and stack traces.

RTK can run directly with defaultMode: "rtk" or as the first step in a stacked pipeline, usually:

rtk -> caveman

That order compresses noisy machine output first, then lets Caveman condense remaining prose.

Upstream RTK reports 60-90% command-output savings. Its README sample session goes from ~118,000 standard tokens to ~23,900 RTK tokens, which is 79.7% saved (~80%). OmniRoute uses that upstream average for the stacked savings calculation with Caveman input compression:

RTK average:    80% saved
Caveman input: 46% saved
Stacked:       1 - (1 - 0.80) * (1 - 0.46) = 89.2% saved
Range:         1 - (1 - 0.60..0.90) * (1 - 0.46) = 78.4-94.6%

What It Compresses

The built-in catalog currently ships 49 filters across these categories:

Category Examples
git git status, git branch, git diff, git log
test Vitest, Jest, Pytest, Playwright, Go tests, Cargo tests
build TypeScript, ESLint, Biome, Prettier, Vite, Webpack, Turbo, Nx
package npm install, npm audit, pip, uv sync, Poetry, Bundler
shell ls, find, grep, generic shell logs
docker docker ps, Docker logs
infra Terraform, OpenTofu, systemctl status
generic JSON output, stack traces, generic output fallback

The detector in open-sse/services/compression/engines/rtk/commandDetector.ts classifies output before filter selection. Filters can also match by command pattern or output regex when a command class is not enough.

Filter Resolution

RTK loads filters in this order:

  1. Project filters from .rtk/filters.json, only when trusted.
  2. Global filters from DATA_DIR/rtk/filters.json.
  3. Built-in filters from open-sse/services/compression/engines/rtk/filters/.

Project filters are intentionally trust-gated because regex filters can change how tool output is shown to agents. A project filter file is accepted when one of these is true:

  • rtkConfig.trustProjectFilters is true.
  • OMNIROUTE_RTK_TRUST_PROJECT_FILTERS=1 is set.
  • .rtk/trust.json contains the SHA-256 hash of .rtk/filters.json.

Trust file example:

{
  "filtersSha256": "0123456789abcdef..."
}

Custom filters can be one filter object or an array of filter objects. Invalid custom filters are skipped and reported by /api/context/rtk/filters diagnostics. Invalid built-in filters fail fast.

Filter DSL

Filters use the JSON schema described in Compression Rules Format. The runtime applies these stages in order:

stripAnsi -> filterStderr -> replace -> matchOutput -> drop/include lines
  -> truncateLineAt -> head/tail/maxLines -> onEmpty

Important fields:

Field Purpose
rules.stripAnsi Remove terminal color/control sequences before matching
rules.filterStderr Normalize common stderr prefixes before matching/filtering
rules.replace Apply ordered regex replacements
rules.matchOutput Return a compact summary when output matches a known condition
rules.matchOutput[].unless Skip the shortcut when an error/failure pattern is present
rules.dropPatterns Remove noisy lines
rules.includePatterns Prefer actionable lines
rules.collapsePatterns Collapse repeated matching lines
rules.deduplicate Per-filter opt-in: collapse consecutive duplicate lines
rules.truncateLineAt Unicode-safe per-line truncation
rules.onEmpty Fallback message if all lines are filtered out
tests[] Inline samples used by the verify gate

Built-in filters are expected to include inline tests[] samples. Custom filters should include them too, especially when they are shared across projects.

Line Deduplication (two layers)

RTK collapses duplicate lines at two independent layers:

  1. Per-filter deduplicate (opt-in, default false). A filter can set rules.deduplicate: true to collapse consecutive duplicate lines within that filter's matched output, before truncation. This runs inside lineFilter.ts. For legacy filters, it is auto-enabled when the filter defines collapsePatterns. Schema: deduplicate: z.boolean().default(false) in open-sse/services/compression/engines/rtk/filterSchema.ts.
  2. Engine-wide deduplicateThreshold (default 3). After all filters run, the engine collapses any run of >= deduplicateThreshold identical consecutive lines across the whole result (deduplicateRepeatedLines, applied in engines/rtk/index.ts). The value is bounded to 2100 on normalization.

The per-filter pass runs first (inside the filter), the engine-wide pass runs last (over the joined output), so the two compose without double-counting.

Line Grouping (enableGrouping)

When rtkConfig.enableGrouping is true (default false), RTK runs an additional groupSimilarLines pass over the post-dedup result that collapses runs of near-equivalent (not byte-identical) consecutive lines. rtkConfig.groupingThreshold (default 3) is the minimum run length that triggers grouping. This is the structural counterpart to deduplicateThreshold: dedup handles exact repeats, grouping handles "the same shape with small differences". Both flags are part of the rtkConfig JSON persisted in the key_value table (see Configuration above), so the setting survives restarts.

Code Comment Stripping (stripCodeComments / preserveDocstrings)

When rtkConfig.applyToCodeBlocks is enabled, RTK can also strip comments from fenced code blocks:

  • stripCodeComments (default false) — opt-in. When true, RTK removes comments from JavaScript and TypeScript fenced blocks. The flag was historically read but never applied, so the default stays at "preserve" to avoid a silent production change.
  • preserveDocstrings (default true) — when stripping comments, JSDoc//** … */ block comments are kept (they carry API documentation worth more than the bytes they cost). Set to false to strip those too.

Comment removal is implemented in open-sse/services/compression/engines/rtk/codeStripper.ts. It uses the TypeScript parser (not a regex) so that string, template, and regex literals are never mistaken for comments, and it bails out entirely when JSX is detected (so JSX expression-container comments are never corrupted). Comment stripping currently applies to JavaScript and TypeScript only — other languages in the stripper's CodeLanguage set (Python, Rust, Go, Ruby, Java) have empty-line and whitespace collapse but no comment removal. The stripped-block run is tagged rtk:code-strip in rulesApplied.

Note — GCF / tabular encoding is a separate engine. RTK does not contain the "GCF" (Graph Compact Format) tabular/columnar JSON encoder. That encoder — which replaced an older omni-tabular encoder — lives in the headroom engine (open-sse/services/compression/engines/headroom/, with the vendored codec under headroom/gcf/). It is unrelated to the RTK filter pipeline documented here.

Configuration

Global settings are available through /api/settings/compression. RTK-specific settings are also available through /api/context/rtk/config.

{
  "defaultMode": "stacked",
  "autoTriggerMode": "stacked",
  "autoTriggerTokens": 32000,
  "stackedPipeline": [
    { "engine": "rtk", "intensity": "standard" },
    { "engine": "caveman", "intensity": "full" }
  ],
  "rtkConfig": {
    "enabled": true,
    "intensity": "standard",
    "applyToToolResults": true,
    "applyToCodeBlocks": false,
    "applyToAssistantMessages": false,
    "enabledFilters": [],
    "disabledFilters": [],
    "maxLinesPerResult": 120,
    "maxCharsPerResult": 12000,
    "deduplicateThreshold": 3,
    "customFiltersEnabled": true,
    "trustProjectFilters": false,
    "rawOutputRetention": "never",
    "rawOutputMaxBytes": 1048576,
    "enableGrouping": false,
    "groupingThreshold": 3,
    "stripCodeComments": false,
    "preserveDocstrings": true
  }
}

enabledFilters and disabledFilters use filter ids, for example test-vitest or git-diff.

The full rtkConfig shape is defined by RtkConfig / DEFAULT_RTK_CONFIG in open-sse/services/compression/types.ts. The whole object is persisted as a single JSON value in the SQLite key_value table under namespace = "compression", key = "rtkConfig" (src/lib/db/compression.ts), and normalized on read by normalizeRtkConfig. So every field below — including enableGrouping, groupingThreshold, stripCodeComments, and preserveDocstrings — round-trips through the same store and survives a restart.

Key Default Purpose
deduplicateThreshold 3 Engine-wide: min consecutive identical lines to collapse (bounded 2100)
enableGrouping false Opt-in: collapse runs of near-equivalent consecutive lines
groupingThreshold 3 Min consecutive similar-line run that triggers grouping
stripCodeComments false Opt-in: remove comments from fenced code blocks (needs applyToCodeBlocks)
preserveDocstrings true When stripping comments, keep JSDoc//** … */ blocks

API

Route Method Purpose
/api/context/rtk/config GET Read RTK config
/api/context/rtk/config PUT Update RTK config
/api/context/rtk/filters GET List filter catalog and load diagnostics
/api/context/rtk/test POST Preview RTK compression for one text payload
/api/context/rtk/raw-output/[id] GET Read retained redacted raw output
/api/compression/preview POST Preview any compression mode

RTK test payload:

{
  "command": "npm test",
  "text": "FAIL tests/example.test.ts\nAssertionError: expected true\nTest Files 1 failed",
  "config": {
    "intensity": "standard"
  }
}

Compression preview payload:

{
  "mode": "stacked",
  "messages": [
    {
      "role": "tool",
      "content": "FAIL tests/example.test.ts\nAssertionError: expected true\nTest Files 1 failed"
    }
  ],
  "config": {
    "rtkConfig": {
      "rawOutputRetention": "failures"
    }
  }
}

Management routes require dashboard management auth or the matching API-key policy.

Raw Output Recovery

RTK normally returns only compressed text. For debugging, rawOutputRetention can retain redacted raw output:

Value Behavior
never Do not retain raw output
failures Retain only likely failure output
always Retain every compressed RTK raw output, after redaction

Retained files are written under:

DATA_DIR/rtk/raw-output/

Secrets are redacted before persistence, including common bearer tokens, API keys, Slack tokens, AWS access keys, and assignment-style token=..., secret=..., password=... values. Analytics stores only the pointer id, size, and hash metadata.

Verify Gate

The focused verify gate runs built-in inline filter tests without shelling out to external commands:

node --import tsx/esm --test tests/unit/compression/rtk-verify.test.ts

The broader RTK gate is:

node --import tsx/esm --test \
  tests/unit/compression/rtk-*.test.ts \
  tests/unit/compression/pipeline-integration.test.ts \
  tests/unit/compression/context-compression-api.test.ts

Run the broad compression gate before release:

node --import tsx/esm --test \
  tests/unit/compression/*.test.ts \
  tests/golden-set/*.test.ts \
  tests/integration/compression-pipeline.test.ts \
  tests/unit/api/compression/compression-api.test.ts

Extending RTK

  1. Add or update a filter JSON file.
  2. Include at least one tests[] sample that proves the important behavior.
  3. Add a fixture under tests/unit/compression/fixtures/rtk/ for new command families.
  4. Add command detection coverage when introducing a new output class.
  5. Run the verify and broad RTK gates.
  6. If the filter is project-local, commit .rtk/filters.json and refresh .rtk/trust.json only after review.

Intensity Levels (v3.8.16+)

RTK supports 3 intensity levels that trade off between compression aggressiveness and safety. The level is set via config.intensity in the engine config.

The 3 Levels

Level Truncation threshold Token savings Risk Best for
minimal 24 lines per section ~20-40% Very low Production with critical context
standard (default) 24 lines per section ~50-70% Low Daily coding sessions
aggressive 16 lines per section ~70-90% Medium Long sessions, max savings

Where the Truncation Happens

The truncation threshold affects lineFilter.ts:

// From open-sse/services/compression/engines/rtk/index.ts:329-330
config.intensity === "aggressive" ? 16 : 24,
config.intensity === "aggressive" ? 16 : 24,

Both the head and tail of each section are preserved; middle content is dropped when truncation kicks in.

What Stays vs. What Gets Cut

Content minimal standard aggressive
Errors / stack traces preserved preserved preserved
Test failures preserved preserved preserved
Build errors preserved preserved preserved
Test passes (verbose) preserved 🟡 collapsed 🟡 collapsed
Routine output (info logs) 🟡 collapsed 🟡 collapsed dropped
Progress bars 🟡 collapsed dropped dropped
Banner / ASCII art 🟡 collapsed dropped dropped

Choosing the Right Intensity

                  Is losing context catastrophic?
                  │
      ┌───────────┼───────────┐
      │           │           │
    YES          NO          NOT SURE
      │           │           │
      ▼           │           │
   minimal        │           │
      │           │           │
      │           ▼           ▼
      │      How critical    Try `standard` first
      │      is throughput?  (works for 80% of
      │           │          cases)
      │      ┌────┴────┐
      │      │         │
      │     LOW       HIGH
      │      │         │
      │      ▼         ▼
      │   standard   aggressive
      │      │         │
      └──────┴─────────┘

Configuring Intensity

Per-combo (in combo config):

{
  "combo": "my-coding-combo",
  "routing": {
    /* ... */
  },
  "compression": {
    "engine": "rtk",
    "intensity": "aggressive"
  }
}

Programmatically:

rtkEngine (@omniroute/open-sse/services/compression/engines/rtk) is a CompressionEngine and has no updateConfig method. Update an engine's config through the registry helper instead:

import { updateEngineConfig } from "@omniroute/open-sse/services/compression/engines/registry";

updateEngineConfig("rtk", { intensity: "aggressive" });

Verifying the Effect

Use the Verify Gate (see below) to confirm your filter is safe at your chosen intensity:

import { runRtkFilterTests } from "omniroute/compression/engines/rtk/verify";

const result = runRtkFilterTests({ intensity: "aggressive" });
if (!result.passed) {
  console.error("Filters failed at aggressive intensity");
}

Custom Filter Development (v3.8.16+)

The engines/rtk/filters/ directory contains 49+ built-in filter JSON files. You can add your own to compress output from custom tools not covered by the defaults.

Filter Schema (Zod)

{
  "id": "string",                      // Required. Filter identifier (kebab-case, e.g., "python-traceback")
  "label": "string",                   // Required. Human-readable filter name
  "description": "string",             // Optional (default: ""). Short description of what filter does
  "category": "git|test|build|shell|docker|package|infra|cloud|generic",
  "priority": number,                  // Optional (0-100, default: 50). Execution order (higher = first)
  "match": {
    "commands": ["string"],            // Command names to match (e.g., "python", "pytest")
    "patterns": ["string"],            // Regex patterns to match output
    "outputTypes": ["string"]          // Detected output classes (e.g., "test-failure")
  },
  "rules": {
    "stripAnsi": boolean,              // Optional (default: false). Strip ANSI color codes
    "replace": [                       // Find-and-replace rules (default: [])
      { "pattern": "regex", "replacement": "..." }
    ],
    "matchOutput": [                   // Short-circuit on pattern match (default: [])
      {
        "pattern": "regex",
        "message": "short summary",
        "unless": "regex"              // Skip if this pattern matches
      }
    ],
    "includePatterns": ["string"],     // Lines to keep (regex patterns, default: [])
    "dropPatterns": ["string"],        // Lines to drop (regex patterns, default: [])
    "collapsePatterns": ["string"],    // Lines to collapse to single occurrence (default: [])
    "deduplicate": boolean,            // Optional (default: false). Remove duplicate lines
    "truncateLineAt": number,          // Optional (default: 0). Truncate lines to max chars
    "maxLines": number,                // Optional (default: 0). Hard cap on total lines
    "headLines": number,               // Optional (default: 20). Keep first N lines of matched output
    "tailLines": number,               // Optional (default: 20). Keep last N lines of matched output
    "onEmpty": "string",               // Optional (default: ""). Fallback message if all lines filtered
    "filterStderr": boolean            // Optional (default: false). Also filter stderr output
  },
  "preserve": {
    "errorPatterns": ["string"],       // Patterns that must always be preserved (default: [])
    "summaryPatterns": ["string"]      // Patterns for final summary line (default: [])
  },
  "tests": [                           // Inline tests for verification (default: [])
    {
      "name": "string",               // Required. Test name
      "input": "sample output",        // Required. Sample input text
      "expected": "expected output",   // Required. Expected compressed output
      "command": "optional command"    // Optional. Command context
    }
  ]
}

Example: Python Traceback Filter

{
  "id": "python-traceback",
  "label": "Python Traceback Filter",
  "description": "Compresses Python tracebacks to essential file/line locations and error type",
  "category": "test",
  "priority": 60,
  "match": {
    "commands": ["python", "python3", "pytest", "uv", "poetry"],
    "patterns": ["Traceback \\(most recent call last\\)", "Error", "Exception"],
    "outputTypes": ["error-traceback"]
  },
  "rules": {
    "stripAnsi": true,
    "includePatterns": [
      "Traceback \\(most recent call last\\)",
      "^\\s*File \".+\", line \\d+",
      "^\\s*[A-Z][a-zA-Z]+Error:",
      "^\\s*[A-Z][a-zA-Z]+Exception"
    ],
    "dropPatterns": ["site-packages/", "^\\s+[a-z_]+\\([^)]*\\)$"],
    "headLines": 5,
    "tailLines": 3,
    "maxLines": 25,
    "filterStderr": true
  },
  "preserve": {
    "errorPatterns": ["Error:", "Exception:", "Traceback"],
    "summaryPatterns": ["^[A-Z][a-zA-Z]+(?:Error|Exception):"]
  },
  "tests": [
    {
      "name": "preserves-error-type-and-location",
      "input": "Traceback (most recent call last):\n  File \"app.py\", line 42, in main\n    do_thing()\n  File \"lib/utils.py\", line 17, in helper\n    return 1 / 0\nZeroDivisionError: division by zero",
      "expected": "Traceback (most recent call last):\n  File \"app.py\", line 42, in main\n  File \"lib/utils.py\", line 17, in helper\nZeroDivisionError: division by zero",
      "command": "python app.py"
    }
  ]
}

Loading Custom Filters

Place the file in a recognized location:

~/.omniroute/rtk/filters/my-filter.json     # User-level
<project>/.rtk/filters/my-filter.json      # Project-level

Filters are loaded automatically on startup via loadRtkFilters() in open-sse/services/compression/engines/rtk/filterLoader.ts. The loader discovers filters from:

  • Built-in catalog: open-sse/services/compression/engines/rtk/filters/
  • User directory: ~/.omniroute/rtk/filters/
  • Project directory: <project>/.rtk/filters/

To load filters programmatically:

import { loadRtkFilters } from "@omniroute/open-sse/services/compression/engines/rtk/filterLoader";

// Options: customFiltersEnabled (load user/project filters, default on),
// trustProjectFilters, refresh.
const filters = loadRtkFilters({ customFiltersEnabled: true });

Validation

Filters are validated against the Zod schema on load. A filter with bad structure will fail to load and log an error:

RTK_FILTER_LOADER: filter "my-filter" failed validation:
  - rules.replace.0.pattern: Invalid regex
  - match.commands: must not be empty

To validate all installed filters, call runRtkFilterTests() which is exported from open-sse/services/compression/engines/rtk/verify.ts.

Best Practices

  1. Always include tests[] — they prove your filter works and prevent regressions
  2. Use matchOutput for short-circuits — if a single line tells the story, replace the whole block
  3. Prefer keep over strip — explicit "always preserve" rules are safer than "always remove"
  4. Test at all 3 intensity levelsminimal should be a no-op, aggressive should still preserve errors
  5. Use the unless field — guard short-circuits with "don't trigger if X is present"

Raw Output Recovery & Verify Gate

When RTK compresses output aggressively, you can recover the original text for debugging, audit, or replay.

How Raw Output Recovery Works

Original output (10K tokens)
        │
        ▼
RTK compress (with rawOutput.enabled=true)
        │
        ├─▶ Compressed output (2K tokens)  ──▶ to LLM
        │
        └─▶ Original output (10K tokens)   ──▶ stored in DB
                                                  (linked by request_id)

Enabling Raw Output Storage

Per-request (in combo config):

{
  "compression": {
    "engine": "rtk",
    "intensity": "aggressive",
    "rawOutput": {
      "enabled": true,
      "maxBytes": 1048576 // 1MB cap
    }
  }
}

Default: rawOutput.enabled: false (saves storage).

Storage Cost

Per-request 1MB cap 10MB cap
Average compressed output ~5KB ~5KB
Raw output stored ~50-500KB ~500KB-5MB
With 1000 requests/day 50-500MB/day 500MB-5GB/day

Recommendation: Only enable raw output for debugging sessions or sampled auditing, not always-on.

Recovering the Original

import { readRtkRawOutput } from "omniroute/compression/engines/rtk/rawOutput";

const raw = readRtkRawOutput(pointerId); // pointerId from compression stats
if (raw) {
  console.log("Original output:", raw);
}

The pointerId is returned in CompressionStats.rtkRawOutputPointers[] after compression. See open-sse/services/compression/engines/rtk/rawOutput.ts:102 for the function signature.

The Verify Gate

The RTK Filter Verification (open-sse/services/compression/engines/rtk/verify.ts) validates all filters against their tests[] and ensures behavior is correct at all 3 intensity levels.

Call runRtkFilterTests() to run verification:

import { runRtkFilterTests } from "open-sse/services/compression/engines/rtk/verify";

const result = runRtkFilterTests();
console.log(`Passed: ${result.outcomes.filter((o) => o.passed).length}`);
console.log(`Failed: ${result.outcomes.filter((o) => !o.passed).length}`);
if (!result.passed) {
  console.error("Filters failed verification");
  result.outcomes
    .filter((o) => !o.passed)
    .forEach((o) => {
      console.error(
        `  - ${o.filterId} / ${o.testName}: expected "${o.expected}", got "${o.actual}"`
      );
    });
}

What it validates:

  1. Every filter loads and passes schema validation
  2. Every tests[] entry produces expected output
  3. minimal intensity is a no-op (preserves original, only applies structural filters)
  4. aggressive intensity preserves errors, test failures, and stack traces
  5. Compressed output is never larger than original input
  • Source: open-sse/services/compression/engines/rtk/ (63 files, ~70KB)

  • Before merging a filter change — always ensure tests pass

  • After upgrading RTK engine — schema may have changed

  • Periodically in monitoring — protects against drift in test fixtures

  • When adding a new tool/command family — proves the new filter works


See Also