feat(compression): composite-command splitter for RTK detection — roadmap #16 (#5283)

roadmap #16: composite-command splitter for RTK detection (opt-in, fail-open). Integrated into release/v3.8.40.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-29 00:16:14 -03:00
committed by GitHub
parent 9b0adabb17
commit f862074209
5 changed files with 192 additions and 2 deletions

View File

@@ -10,6 +10,7 @@ _In development — bullets added per PR; finalized at release._
### ✨ New Features
- **feat(compression): composite-command splitter for RTK detection**`cd /x && git status` now detects as `git-status` (previously the whole string was treated as one command and matched no filter). A quote-aware top-level tokenizer splits on `&&`/`||`/`;` (never inside quotes or `$(…)`/backtick subshells) and feeds the **last** segment to RTK command detection, so every RTK filter/renderer fires on commands wrapped in `cd … &&`/`||`/`;` chains. O(n), no RegExp over the command (ReDoS-safe). Tier-3 item of the compression feature-extraction roadmap (#16).
- **feat(mcp): `omniroute_tool_search` tool + one-line TS signatures** — new MCP tool that does lexical keyword search over every MCP tool's name/description and returns the top matches as compact one-line TypeScript signatures (~half the JSON-schema token cost), so agents discover tools on demand instead of carrying all ~88 schemas every turn. Search is ReDoS-safe (substring scoring, never `new RegExp` on the query) and deterministic; `tools/list` stays complete (no hidden tools). Adds the `read:tools` scope. Tier-1 item of the compression feature-extraction roadmap. ([#5269](https://github.com/diegosouzapw/OmniRoute/pull/5269))
- **feat(compression): RTK semantic command-output renderers (opt-in)** — adds a second, opt-in compaction layer to the RTK engine that rewrites structured command output into a far more compact semantic form: `git diff` → file headers + `@@` hunks + changed lines only; an all-green `pytest`/`jest`/`vitest`/`eslint` run → its one-line summary; `terraform`/`tofu plan``Plan: +N ~M -K` plus the resource list; `kubectl`/`aws` JSON arrays → a minimal table. Each renderer is conservative (no-op when the shape doesn't match) and the integration is fail-open; the test-green renderer never collapses output that carries any failure signal. Gated by `RtkConfig.enableRenderers` (default off → zero behavioral change). Eighth item of the compression feature-extraction roadmap. ([#5268](https://github.com/diegosouzapw/OmniRoute/pull/5268))
- **feat(compression): QuantumLock cache-prefix stabilization (opt-in, default off)** — recovers upstream prompt-cache hits that a volatile fragment in the system prompt would otherwise bust. When a caller injects a session UUID, unix timestamp, request-id, JWT, API-key shape, or long hex digest into the `role:system` message every turn, the longest common prefix across turns ends at that changing byte → the whole system prompt after it is re-billed and re-processed each turn. QuantumLock replaces each non-semantic volatile fragment with a **positional, value-independent** placeholder `⟦Q{i}⟧` and appends the real values in a delimited `⟦QUANTUMLOCK⟧` tail. The rewrite is **sent to the model** (lossless — not restored), so the system-prompt body becomes **byte-identical across turns** and the provider caches the long stable prefix while only the small tail differs. Opt-in, default off, applied only for caching providers (`isCachingProvider && config.quantumLock.enabled`); bounded ReDoS-safe patterns; idempotent; **no date/time patterns** (semantically meaningful — explicit non-goal). Studio gets a toggle + a "🔒 N volatile fragment(s) stabilized" dry-run badge. Seventh item of the compression feature-extraction roadmap (bench: [#5080](https://github.com/diegosouzapw/OmniRoute/pull/5080), gate: [#5127](https://github.com/diegosouzapw/OmniRoute/pull/5127), fuzzy: [#5143](https://github.com/diegosouzapw/OmniRoute/pull/5143), ionizer: [#5148](https://github.com/diegosouzapw/OmniRoute/pull/5148), TOON: [#5163](https://github.com/diegosouzapw/OmniRoute/pull/5163), CCR ranged: [#5187](https://github.com/diegosouzapw/OmniRoute/pull/5187), risk-gate: [#5243](https://github.com/diegosouzapw/OmniRoute/pull/5243)). ([#5260](https://github.com/diegosouzapw/OmniRoute/pull/5260))

View File

@@ -1,3 +1,5 @@
import { lastCommandSegment } from "./splitCompositeCommand.ts";
export interface CommandDetectionResult {
type: string;
command: string | null;
@@ -412,14 +414,15 @@ export function detectCommandFromText(text: string): string | null {
const trimmed = line.trim().replace(/^\$\s+/, "");
if (!trimmed) continue;
if (COMMAND_PREFIX_PATTERN.test(trimmed)) {
return trimmed;
return lastCommandSegment(trimmed);
}
}
return null;
}
export function detectCommandType(text: string, command?: string | null): CommandDetectionResult {
const detectedCommand = command?.trim() || detectCommandFromText(text);
const detectedCommand =
lastCommandSegment(command?.trim() || detectCommandFromText(text) || "") || null;
let best: CommandDetectionResult | null = null;
for (const detector of DETECTORS) {

View File

@@ -0,0 +1,111 @@
/**
* Quote-aware composite-command splitter.
*
* Splits a shell command string on top-level `&&`, `||`, or `;` separators
* (i.e. those NOT inside single quotes, double quotes, backtick subshells,
* or `$(...)` subshells) and returns the LAST significant segment (trimmed).
*
* - No separator found → returns the input unchanged.
* - Last segment is empty (e.g. trailing `&&`) → falls back to the previous non-empty one.
* - O(n) char-by-char scan; zero RegExp over the full input (anti-ReDoS).
*/
export function lastCommandSegment(command: string): string {
if (!command) return command;
const segments: string[] = [];
let current = 0; // start index of the current segment
let depth = 0; // nesting depth for $(...) / (...)
let inSingle = false;
let inDouble = false;
let inBacktick = false;
const push = (end: number): void => {
segments.push(command.slice(current, end));
};
for (let i = 0; i < command.length; i++) {
const ch = command[i];
// ── quote / subshell state tracking ──────────────────────────────────
if (inSingle) {
if (ch === "'") inSingle = false;
continue;
}
if (inBacktick) {
if (ch === "`") inBacktick = false;
continue;
}
if (inDouble) {
if (ch === '"') inDouble = false;
// $( inside double-quotes still opens a subshell
if (ch === "$" && command[i + 1] === "(") {
depth++;
i++; // skip the '('
}
continue;
}
if (depth > 0) {
if (ch === "(") depth++;
else if (ch === ")") depth--;
continue;
}
// ── open new quote / subshell context ────────────────────────────────
if (ch === "'") {
inSingle = true;
continue;
}
if (ch === '"') {
inDouble = true;
continue;
}
if (ch === "`") {
inBacktick = true;
continue;
}
if (ch === "$" && command[i + 1] === "(") {
depth++;
i++; // skip the '('
continue;
}
if (ch === "(") {
depth++;
continue;
}
// ── top-level separator detection ─────────────────────────────────────
if (ch === "&" && command[i + 1] === "&") {
push(i);
i += 1; // skip second '&'
current = i + 1;
continue;
}
if (ch === "|" && command[i + 1] === "|") {
push(i);
i += 1; // skip second '|'
current = i + 1;
continue;
}
if (ch === ";") {
push(i);
current = i + 1;
continue;
}
}
// push the remainder
push(command.length);
if (segments.length === 1) {
// no top-level separator found → return unchanged
return command;
}
// find the last non-empty (after trim) segment
for (let i = segments.length - 1; i >= 0; i--) {
const trimmed = segments[i].trim();
if (trimmed) return trimmed;
}
return command;
}

View File

@@ -97,6 +97,17 @@ describe("RTK command detector", () => {
}
});
it("detects git-status when command is a composite like 'cd /x && git status'", () => {
assert.equal(
detectCommandType(fixture("git-status-sample.txt"), "cd /x && git status").type,
"git-status"
);
assert.equal(
detectCommandType(fixture("git-diff-sample.txt"), "cd /repo && git diff").type,
"git-diff"
);
});
it("returns unknown for generic text and exposes planned alias", () => {
const detection = detectCommandOutput("ordinary prose without command output");

View File

@@ -0,0 +1,64 @@
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { lastCommandSegment } from "../../../open-sse/services/compression/engines/rtk/splitCompositeCommand.ts";
describe("lastCommandSegment", () => {
it("splits on && and returns the last segment", () => {
assert.equal(lastCommandSegment("cd /x && git status"), "git status");
});
it("splits on || and returns the last segment", () => {
assert.equal(lastCommandSegment("npm i || yarn"), "yarn");
});
it("splits on ; and returns the last segment", () => {
assert.equal(lastCommandSegment("make; git status"), "git status");
});
it("does not split inside double quotes", () => {
assert.equal(lastCommandSegment('cd "a && b" && git log'), "git log");
});
it("does not split inside single quotes", () => {
assert.equal(lastCommandSegment("cd 'a && b' && git log"), "git log");
});
it("does not split inside backtick subshell", () => {
assert.equal(lastCommandSegment("echo `git rev-parse` && git status"), "git status");
});
it("does not split inside $(...) subshell", () => {
assert.equal(lastCommandSegment("echo $(git rev-parse) && git status"), "git status");
});
it("returns input unchanged when no top-level separator exists", () => {
assert.equal(lastCommandSegment("git status"), "git status");
assert.equal(lastCommandSegment("npm install"), "npm install");
});
it("returns input unchanged for empty string", () => {
assert.equal(lastCommandSegment(""), "");
});
it("falls back to previous segment when last segment is empty", () => {
// trailing separator — last segment is empty, should fall back
assert.equal(lastCommandSegment("git status &&"), "git status");
});
it("trims whitespace from returned segment", () => {
assert.equal(lastCommandSegment("cd /tmp && git log"), "git log");
});
it("handles chained operators returning the very last non-empty segment", () => {
assert.equal(lastCommandSegment("a && b && git diff"), "git diff");
});
it("returns fast on a pathological ReDoS-style string (< 50ms)", () => {
const pathological = "a".repeat(10000) + " && " + "b".repeat(10000);
const start = Date.now();
lastCommandSegment(pathological);
const elapsed = Date.now() - start;
assert.ok(elapsed < 50, `took ${elapsed}ms, expected < 50ms`);
});
});