-
{t("pipeline")}
+ Pipeline
setPipeline((current) => [...current, { engine: "caveman", intensity: "full" }])
}
className="rounded-lg border border-border px-3 py-1.5 text-xs text-text-main"
>
- {t("addStep")}
+ Adicionar etapa
{pipeline.map((step, index) => (
@@ -232,7 +249,7 @@ export default function CompressionCombosPageClient() {
className="rounded-lg border border-border px-3 py-2 text-sm text-text-main"
disabled={pipeline.length <= 1}
>
- {t("removeStep")}
+ Remover
))}
@@ -240,7 +257,7 @@ export default function CompressionCombosPageClient() {
-
{t("languagePacks")}
+
Pacotes de idioma
{languagePacks.map((pack) => (
@@ -258,14 +275,14 @@ export default function CompressionCombosPageClient() {
-
{t("outputMode")}
+ Output mode
setOutputMode(event.target.checked)}
/>
- {t("enabled")}
+ Ativado
-
{t("assignToRouting")}
+
Atribuir ao roteamento
{routingCombos.length === 0 ? (
-
{t("noAssignments")}
+
Nenhum combo de roteamento disponível.
) : (
routingCombos.map((combo) => {
const id = combo.id ?? combo.name ?? "";
@@ -308,14 +325,14 @@ export default function CompressionCombosPageClient() {
disabled={saving}
className="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white"
>
- {editingId ? t("save") : t("createCombo")}
+ {editingId ? "Salvar" : "Criar combo"}
{editingId && (
- {t("cancel")}
+ Cancelar
)}
@@ -326,12 +343,12 @@ export default function CompressionCombosPageClient() {
-
{combo.name}
+
{combo.name}
{combo.description}
{combo.isDefault && (
- {t("default")}
+ Padrão
)}
@@ -347,21 +364,21 @@ export default function CompressionCombosPageClient() {
))}
- {t("languagePacks")}: {combo.languagePacks.join(", ")}
+ Pacotes de idioma: {combo.languagePacks.join(", ")}
editCombo(combo)}
className="rounded-lg border border-border px-3 py-1.5 text-xs text-text-main"
>
- {t("editCombo")}
+ Editar
{!combo.isDefault && (
setDefault(combo.id)}
className="rounded-lg border border-border px-3 py-1.5 text-xs text-text-main"
>
- {t("setAsDefault")}
+ Definir como padrão
)}
{!combo.isDefault && (
@@ -369,7 +386,7 @@ export default function CompressionCombosPageClient() {
onClick={() => deleteCombo(combo)}
className="rounded-lg border border-danger/40 px-3 py-1.5 text-xs text-danger"
>
- {t("deleteCombo")}
+ Excluir
)}
@@ -379,3 +396,12 @@ export default function CompressionCombosPageClient() {
);
}
+
+export default function CompressionCombosPageClient() {
+ return (
+
+
+
+
+ );
+}
diff --git a/src/app/(dashboard)/dashboard/context/combos/CompressionHub.tsx b/src/app/(dashboard)/dashboard/context/combos/CompressionHub.tsx
new file mode 100644
index 00000000000..512cd4e3902
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/context/combos/CompressionHub.tsx
@@ -0,0 +1,541 @@
+"use client";
+
+// Compression Hub — the single place to understand and control compression.
+//
+// IMPORTANT (hydration): this component deliberately does NOT use `useTranslations`.
+// The previous combos redesign failed to hydrate on the production build; the only
+// structural difference from the engine pages (which hydrate fine) was a page-level
+// `useTranslations("contextCombos")`. To stay on the proven-good path, all strings
+// here are hardcoded (pt-BR), exactly like `EngineConfigPage`.
+
+import { useCallback, useEffect, useState } from "react";
+
+// ── Types ─────────────────────────────────────────────────────────────────────
+
+type CompressionMode = "off" | "lite" | "standard" | "aggressive" | "ultra" | "rtk" | "stacked";
+
+interface CompressionSettings {
+ enabled: boolean;
+ defaultMode: CompressionMode;
+ [key: string]: unknown;
+}
+
+interface EngineEntry {
+ id: string;
+ name: string;
+ description: string;
+ icon: string;
+ stackable: boolean;
+ stackPriority: number;
+ metadata: { stable?: boolean; description?: string; [key: string]: unknown };
+}
+
+interface PipelineStep {
+ engine: string;
+ intensity?: string;
+ config?: Record
;
+}
+
+interface DefaultCombo {
+ id: string;
+ name: string;
+ pipeline: PipelineStep[];
+}
+
+// ── Constants ──────────────────────────────────────────────────────────────────
+
+const MODES: { value: CompressionMode; label: string; hint: string }[] = [
+ { value: "off", label: "Off", hint: "Sem compressão" },
+ { value: "lite", label: "Lite", hint: "Limpeza rápida" },
+ { value: "standard", label: "Standard", hint: "Caveman padrão" },
+ { value: "aggressive", label: "Aggressive", hint: "Resumo + aging" },
+ { value: "ultra", label: "Ultra", hint: "Poda heurística" },
+ { value: "rtk", label: "RTK", hint: "Filtros de tool output" },
+ { value: "stacked", label: "Stacked", hint: "Roda as camadas abaixo em sequência" },
+];
+
+// ── Helpers ────────────────────────────────────────────────────────────────────
+
+function enginePagePath(engineId: string): string {
+ return `/dashboard/context/${engineId}`;
+}
+
+// ── Sub-components ──────────────────────────────────────────────────────────────
+
+function Toggle({
+ checked,
+ onChange,
+ ariaLabel,
+}: {
+ checked: boolean;
+ onChange: () => void;
+ ariaLabel: string;
+}) {
+ return (
+
+
+
+ );
+}
+
+// ── Main component ──────────────────────────────────────────────────────────────
+
+export default function CompressionHub() {
+ const [settings, setSettings] = useState(null);
+ const [engines, setEngines] = useState([]);
+ const [combo, setCombo] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [explainerOpen, setExplainerOpen] = useState(false);
+ const [error, setError] = useState(null);
+
+ // ── Initial load (parallel) ──────────────────────────────────────────────────
+ useEffect(() => {
+ let cancelled = false;
+ async function load() {
+ setLoading(true);
+ const asJson = (r: Response) => (r.ok ? r.json() : null);
+ const [settingsData, enginesData, comboData] = await Promise.all([
+ fetch("/api/settings/compression")
+ .then(asJson)
+ .catch(() => null),
+ fetch("/api/compression/engines")
+ .then(asJson)
+ .catch(() => null),
+ fetch("/api/context/combos/default")
+ .then(asJson)
+ .catch(() => null),
+ ]);
+ if (cancelled) return;
+ if (settingsData) {
+ setSettings(settingsData as CompressionSettings);
+ } else {
+ setSettings({ enabled: false, defaultMode: "off" });
+ }
+ if (enginesData?.engines) {
+ setEngines(
+ [...(enginesData.engines as EngineEntry[])].sort(
+ (a, b) => a.stackPriority - b.stackPriority
+ )
+ );
+ }
+ if (comboData?.id) {
+ setCombo({
+ id: String(comboData.id),
+ name: String(comboData.name ?? "Default"),
+ pipeline: Array.isArray(comboData.pipeline) ? comboData.pipeline : [],
+ });
+ }
+ setLoading(false);
+ }
+ void load();
+ return () => {
+ cancelled = true;
+ };
+ }, []);
+
+ // ── Settings mutations (master switch + mode) ────────────────────────────────
+ const saveSettings = useCallback(
+ async (patch: Partial) => {
+ if (!settings) return;
+ const next = { ...settings, ...patch };
+ setSettings(next);
+ setError(null);
+ try {
+ const res = await fetch("/api/settings/compression", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(next),
+ });
+ if (!res.ok) {
+ setSettings(settings); // revert
+ setError("Falha ao salvar as configurações.");
+ }
+ } catch {
+ setSettings(settings);
+ setError("Falha ao salvar as configurações.");
+ }
+ },
+ [settings]
+ );
+
+ // ── Toggle a layer (enable/disable) ───────────────────────────────────────────
+ // Routed through the dedicated `/default` endpoint (setEngineInDefaultCombo): it
+ // accepts an empty pipeline (disabling the last layer) and inserts at the
+ // stackPriority-correct position — the [id] route requires `pipeline.min(1)`.
+ const toggleEngine = useCallback(
+ async (engineId: string) => {
+ if (!combo) return;
+ const enabledNow = combo.pipeline.some((s) => s.engine === engineId);
+ const prev = combo;
+
+ // Optimistic update (mirrors the server's insert-at-priority / remove logic).
+ let optimistic: PipelineStep[];
+ if (enabledNow) {
+ optimistic = combo.pipeline.filter((s) => s.engine !== engineId);
+ } else {
+ const priorityOf = (eid: string) =>
+ engines.find((e) => e.id === eid)?.stackPriority ?? 50;
+ optimistic = [...combo.pipeline];
+ let insertAt = optimistic.findIndex(
+ (s) => priorityOf(s.engine) > priorityOf(engineId)
+ );
+ if (insertAt < 0) insertAt = optimistic.length;
+ optimistic.splice(insertAt, 0, { engine: engineId });
+ }
+ setCombo({ ...combo, pipeline: optimistic });
+ setError(null);
+
+ try {
+ const res = await fetch("/api/context/combos/default", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ engineId, enabled: !enabledNow }),
+ });
+ if (!res.ok) {
+ setCombo(prev);
+ setError("Falha ao atualizar a camada.");
+ return;
+ }
+ const updated = await res.json();
+ if (Array.isArray(updated?.pipeline)) {
+ setCombo({ ...prev, pipeline: updated.pipeline });
+ }
+ } catch {
+ setCombo(prev);
+ setError("Falha ao atualizar a camada.");
+ }
+ },
+ [combo, engines]
+ );
+
+ // ── Reorder an active layer ───────────────────────────────────────────────────
+ // Persisted via the [id] route so the custom order survives (the `/default` route
+ // re-sorts by stackPriority). Only callable with ≥2 active steps, so the route's
+ // `pipeline.min(1)` guard is always satisfied.
+ const moveStep = useCallback(
+ async (index: number, direction: -1 | 1) => {
+ if (!combo) return;
+ const target = index + direction;
+ if (target < 0 || target >= combo.pipeline.length) return;
+ const next = [...combo.pipeline];
+ [next[index], next[target]] = [next[target], next[index]];
+ const prev = combo;
+ setCombo({ ...combo, pipeline: next });
+ setError(null);
+ try {
+ const res = await fetch(`/api/context/combos/${combo.id}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ pipeline: next }),
+ });
+ if (!res.ok) {
+ setCombo(prev);
+ setError("Falha ao reordenar o pipeline.");
+ }
+ } catch {
+ setCombo(prev);
+ setError("Falha ao reordenar o pipeline.");
+ }
+ },
+ [combo]
+ );
+
+ // ── Derived state ─────────────────────────────────────────────────────────────
+ if (loading) {
+ return (
+
+ Carregando…
+
+ );
+ }
+
+ const enabled = settings?.enabled ?? false;
+ const mode = settings?.defaultMode ?? "off";
+ const pipelineActive = enabled && mode === "stacked";
+ const enabledIds = new Set((combo?.pipeline ?? []).map((s) => s.engine));
+ const activeSteps = combo?.pipeline ?? [];
+ const inactiveEngines = engines.filter((e) => !enabledIds.has(e.id));
+ const engineById = (id: string) => engines.find((e) => e.id === id);
+
+ return (
+
+ {/* ── Header ── */}
+
+
+
+ hub
+
+
+
Compression Hub
+
+ Ligue, configure e ordene as camadas de compressão num só lugar.
+
+
+
+
setExplainerOpen((v) => !v)}
+ className="shrink-0 rounded-lg border border-border px-3 py-1.5 text-xs text-text-main hover:bg-bg"
+ >
+ {explainerOpen ? "Ocultar explicação" : "Como funciona?"}
+
+
+
+ {error && (
+ {error}
+ )}
+
+ {/* ── Explainer ── */}
+ {explainerOpen && (
+
+
+ A compressão reduz tokens e custo reescrevendo
+ o histórico antes de enviar ao provider, preservando o sentido.
+
+
+
+ Token Saver (master) : precisa estar ligado.
+ Desligado, nada é comprimido.
+
+
+ Modo : define a estratégia. Os modos simples
+ (Lite/Standard/Aggressive/Ultra/RTK) rodam uma única técnica. O modo{" "}
+ Stacked roda várias camadas em sequência — é
+ o que usa a lista de camadas abaixo.
+
+
+ Camadas (pipeline) : no modo Stacked, cada
+ camada ativa roda na ordem definida, passando o texto já comprimido para a próxima
+ (ex.: Session Dedup → RTK → Caveman).
+
+
+ Configuração : cada camada tem liga/desliga e
+ parâmetros próprios (botão ⚙).
+
+
+ Combos nomeados : salve diferentes pipelines
+ e atribua a combos de roteamento específicos (seção abaixo).
+
+
+
+ )}
+
+ {/* ── Master switch + status ── */}
+
+
+
+
Token Saver
+
Chave geral da compressão.
+
+
saveSettings({ enabled: !enabled })}
+ ariaLabel="Ligar/desligar Token Saver"
+ />
+
+
+ {/* Mode selector */}
+
+
Modo
+
+ {MODES.map((m) => (
+ saveSettings({ defaultMode: m.value })}
+ title={m.hint}
+ className={`rounded-lg border px-2.5 py-2 text-left text-xs transition-all disabled:opacity-40 ${
+ mode === m.value
+ ? "border-primary/60 bg-primary/10 text-primary"
+ : "border-border text-text-main hover:bg-surface"
+ }`}
+ >
+ {m.label}
+
+ {m.hint}
+
+
+ ))}
+
+
+
+ {/* Pipeline status callout */}
+ {pipelineActive ? (
+
+ check_circle
+ Pipeline de camadas ativo — as camadas abaixo rodam em cada requisição.
+
+ ) : (
+
+ info
+ As camadas abaixo só rodam no modo Stacked com o Token Saver ligado.
+ {!enabled && (
+ saveSettings({ enabled: true })}
+ className="rounded border border-amber-500/50 px-2 py-0.5 font-medium hover:bg-amber-500/10"
+ >
+ Ligar Token Saver
+
+ )}
+ {enabled && mode !== "stacked" && (
+ saveSettings({ defaultMode: "stacked" })}
+ className="rounded border border-amber-500/50 px-2 py-0.5 font-medium hover:bg-amber-500/10"
+ >
+ Usar modo Stacked
+
+ )}
+
+ )}
+
+
+ {/* ── Active pipeline (ordered, reorderable) ── */}
+
+
+
+ Pipeline ativo (ordem de execução)
+
+ {activeSteps.length} camada(s)
+
+ {activeSteps.length === 0 ? (
+
+ Nenhuma camada ativa. Ligue uma camada abaixo para montar o pipeline.
+
+ ) : (
+
+ {activeSteps.map((step, index) => {
+ const engine = engineById(step.engine);
+ return (
+
+
+ moveStep(index, -1)}
+ disabled={index === 0}
+ aria-label="Mover para cima"
+ className="text-text-muted hover:text-text-main disabled:opacity-30"
+ >
+ arrow_upward
+
+ moveStep(index, 1)}
+ disabled={index === activeSteps.length - 1}
+ aria-label="Mover para baixo"
+ className="text-text-muted hover:text-text-main disabled:opacity-30"
+ >
+ arrow_downward
+
+
+
+ {index + 1}
+
+
+ {engine?.icon ?? "compress"}
+
+
+
+
+ {engine?.name ?? step.engine}
+
+ {engine && engine.metadata?.stable === false && (
+
+ beta
+
+ )}
+
+
+ {engine?.description ?? ""}
+
+
+
+ settings
+
+ toggleEngine(step.engine)}
+ ariaLabel={`Desligar ${engine?.name ?? step.engine}`}
+ />
+
+ );
+ })}
+
+ )}
+
+
+ {/* ── Inactive layers ── */}
+ {inactiveEngines.length > 0 && (
+
+
Camadas disponíveis
+
+ {inactiveEngines.map((engine) => (
+
+
+ {engine.icon}
+
+
+
+
{engine.name}
+ {engine.metadata?.stable === false && (
+
+ beta
+
+ )}
+
+ prio {engine.stackPriority}
+
+
+
{engine.description}
+
+
+ settings
+
+ toggleEngine(engine.id)}
+ ariaLabel={`Ligar ${engine.name}`}
+ />
+
+ ))}
+
+
+ )}
+
+ );
+}
diff --git a/src/app/(dashboard)/dashboard/context/headroom/page.tsx b/src/app/(dashboard)/dashboard/context/headroom/page.tsx
new file mode 100644
index 00000000000..116311a69d9
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/context/headroom/page.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { EngineConfigPage } from "@/shared/components/compression/EngineConfigPage";
+
+export default function HeadroomPage() {
+ return ;
+}
diff --git a/src/app/(dashboard)/dashboard/context/lite/page.tsx b/src/app/(dashboard)/dashboard/context/lite/page.tsx
new file mode 100644
index 00000000000..8627ca3465b
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/context/lite/page.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { EngineConfigPage } from "@/shared/components/compression/EngineConfigPage";
+
+export default function LitePage() {
+ return ;
+}
diff --git a/src/app/(dashboard)/dashboard/context/llmlingua/page.tsx b/src/app/(dashboard)/dashboard/context/llmlingua/page.tsx
new file mode 100644
index 00000000000..898bf16c83e
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/context/llmlingua/page.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { EngineConfigPage } from "@/shared/components/compression/EngineConfigPage";
+
+export default function LlmlinguaPage() {
+ return ;
+}
diff --git a/src/app/(dashboard)/dashboard/context/session-dedup/page.tsx b/src/app/(dashboard)/dashboard/context/session-dedup/page.tsx
new file mode 100644
index 00000000000..6108b5a4432
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/context/session-dedup/page.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { EngineConfigPage } from "@/shared/components/compression/EngineConfigPage";
+
+export default function SessionDedupPage() {
+ return ;
+}
diff --git a/src/app/(dashboard)/dashboard/context/settings/page.tsx b/src/app/(dashboard)/dashboard/context/settings/page.tsx
new file mode 100644
index 00000000000..bfae06fafff
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/context/settings/page.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import CompressionSettingsTab from "@/app/(dashboard)/dashboard/settings/components/CompressionSettingsTab";
+
+export default function CompressionSettingsPage() {
+ return (
+
+
+
+ );
+}
diff --git a/src/app/(dashboard)/dashboard/context/ultra/page.tsx b/src/app/(dashboard)/dashboard/context/ultra/page.tsx
new file mode 100644
index 00000000000..95abcd70ef1
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/context/ultra/page.tsx
@@ -0,0 +1,7 @@
+"use client";
+
+import { EngineConfigPage } from "@/shared/components/compression/EngineConfigPage";
+
+export default function UltraPage() {
+ return ;
+}
diff --git a/src/app/api/compression/engines/route.ts b/src/app/api/compression/engines/route.ts
new file mode 100644
index 00000000000..8a15ad61f9c
--- /dev/null
+++ b/src/app/api/compression/engines/route.ts
@@ -0,0 +1,26 @@
+import { NextResponse } from "next/server";
+import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
+import { buildErrorBody } from "@omniroute/open-sse/utils/error";
+import { registerBuiltinCompressionEngines } from "@omniroute/open-sse/services/compression/engines/index.ts";
+import { listCompressionEngines } from "@omniroute/open-sse/services/compression/engines/registry.ts";
+
+export async function GET(req: Request) {
+ const authError = await requireManagementAuth(req);
+ if (authError) return authError;
+ try {
+ registerBuiltinCompressionEngines();
+ const engines = listCompressionEngines().map((e) => ({
+ id: e.id,
+ name: e.name,
+ description: e.description,
+ icon: e.icon,
+ stackable: e.stackable,
+ stackPriority: e.stackPriority,
+ metadata: e.metadata,
+ configSchema: e.getConfigSchema(),
+ }));
+ return NextResponse.json({ engines });
+ } catch {
+ return NextResponse.json(buildErrorBody(500, "Failed to list engines"), { status: 500 });
+ }
+}
diff --git a/src/app/api/compression/preview/route.ts b/src/app/api/compression/preview/route.ts
index f368c777bfd..498bdfe5868 100644
--- a/src/app/api/compression/preview/route.ts
+++ b/src/app/api/compression/preview/route.ts
@@ -2,12 +2,16 @@ import { NextResponse } from "next/server";
import { z } from "zod";
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
import { compressionPreviewConfigSchema } from "@/shared/validation/compressionConfigSchemas";
-import { applyCompression } from "@omniroute/open-sse/services/compression/strategySelector";
+import {
+ applyCompression,
+ applyCompressionAsync,
+} from "@omniroute/open-sse/services/compression/strategySelector";
import type {
CompressionConfig,
CompressionMode,
} from "@omniroute/open-sse/services/compression/types";
import { buildCompressionPreviewDiff } from "@omniroute/open-sse/services/compression/diffHelper";
+import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
export const PreviewCompressionConfigSchema = compressionPreviewConfigSchema;
@@ -20,7 +24,11 @@ export const PreviewRequestSchema = z.object({
})
)
.min(1),
- mode: z.enum(["off", "lite", "standard", "aggressive", "ultra", "rtk", "stacked"]),
+ mode: z
+ .enum(["off", "lite", "standard", "aggressive", "ultra", "rtk", "stacked"])
+ .optional()
+ .default("stacked"),
+ engineId: z.string().optional(),
config: PreviewCompressionConfigSchema.optional(),
});
@@ -56,16 +64,25 @@ export async function POST(req: Request) {
);
}
- const { messages, mode, config } = parsed.data;
+ const { messages, mode, engineId, config } = parsed.data;
+ const effectiveMode: CompressionMode = engineId ? "stacked" : (mode as CompressionMode);
const originalText = messagesToText(messages);
const originalTokens = countTokens(originalText);
try {
const start = Date.now();
const requestBody = { messages };
- const result = await applyCompression(requestBody as Record, mode, {
- config: config as CompressionConfig | undefined,
- });
+ let result;
+ if (engineId) {
+ const engineConfig = { stackedPipeline: [{ engine: engineId }] } as CompressionConfig;
+ result = await applyCompressionAsync(requestBody as Record, "stacked", {
+ config: engineConfig,
+ });
+ } else {
+ result = await applyCompression(requestBody as Record, effectiveMode, {
+ config: config as CompressionConfig | undefined,
+ });
+ }
const durationMs = Date.now() - start;
const compressedMessages = (result.body.messages ?? messages) as Array<{
@@ -88,7 +105,7 @@ export async function POST(req: Request) {
savingsPct,
techniquesUsed,
durationMs,
- mode,
+ mode: effectiveMode,
intensity: null,
outputMode: null,
skippedReasons: [],
@@ -109,6 +126,9 @@ export async function POST(req: Request) {
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
console.error("[/api/compression/preview]", msg);
- return NextResponse.json({ error: "Compression failed", details: msg }, { status: 500 });
+ return NextResponse.json(
+ { error: "Compression failed", details: sanitizeErrorMessage(msg) },
+ { status: 500 }
+ );
}
}
diff --git a/src/app/api/context/analytics/engine/route.ts b/src/app/api/context/analytics/engine/route.ts
new file mode 100644
index 00000000000..d7323535079
--- /dev/null
+++ b/src/app/api/context/analytics/engine/route.ts
@@ -0,0 +1,38 @@
+/**
+ * GET /api/context/analytics/engine?engineId=&days=
+ *
+ * Returns per-engine compression analytics aggregated over the requested
+ * time window (default: last 7 days).
+ *
+ * Query params:
+ * engineId (required) — e.g. "headroom", "caveman", "rtk"
+ * days (optional, default 7) — lookback window in days
+ */
+
+import { NextResponse } from "next/server";
+import { getPerEngineAnalytics } from "@/lib/db/compressionAnalytics";
+import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
+
+export async function GET(req: Request) {
+ const authError = await requireManagementAuth(req);
+ if (authError) return authError;
+
+ try {
+ const url = new URL(req.url);
+ const engineId = url.searchParams.get("engineId");
+ if (!engineId) {
+ return NextResponse.json({ error: "engineId query parameter is required" }, { status: 400 });
+ }
+
+ const daysParam = url.searchParams.get("days");
+ const days = daysParam ? Math.max(1, Math.floor(Number(daysParam))) || 7 : 7;
+
+ const result = getPerEngineAnalytics(engineId, days);
+
+ return NextResponse.json(result);
+ } catch (err: unknown) {
+ const msg = err instanceof Error ? err.message : String(err);
+ console.error("[/api/context/analytics/engine]", msg);
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 });
+ }
+}
diff --git a/src/app/api/context/combos/default/route.ts b/src/app/api/context/combos/default/route.ts
new file mode 100644
index 00000000000..b7993f118e4
--- /dev/null
+++ b/src/app/api/context/combos/default/route.ts
@@ -0,0 +1,48 @@
+import { NextResponse } from "next/server";
+import { z } from "zod";
+import { getDefaultCompressionCombo, setEngineInDefaultCombo } from "@/lib/db/compressionCombos";
+import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
+import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
+
+const engineToggleSchema = z
+ .object({
+ engineId: z.string().trim().min(1).max(64),
+ enabled: z.boolean(),
+ config: z.record(z.string(), z.unknown()).optional(),
+ })
+ .strict();
+
+export async function GET(request: Request) {
+ const authError = await requireManagementAuth(request);
+ if (authError) return authError;
+
+ const combo = getDefaultCompressionCombo();
+ if (!combo) {
+ return NextResponse.json({ error: "No default compression combo found" }, { status: 404 });
+ }
+ return NextResponse.json(combo);
+}
+
+export async function PUT(request: Request) {
+ const authError = await requireManagementAuth(request);
+ if (authError) return authError;
+
+ let rawBody: unknown;
+ try {
+ rawBody = await request.json();
+ } catch {
+ return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
+ }
+
+ const validation = validateBody(engineToggleSchema, rawBody);
+ if (isValidationFailure(validation)) {
+ return NextResponse.json({ error: validation.error }, { status: 400 });
+ }
+
+ const { engineId, enabled, config } = validation.data;
+ const combo = setEngineInDefaultCombo(engineId, enabled, config);
+ if (!combo) {
+ return NextResponse.json({ error: "No default compression combo found" }, { status: 404 });
+ }
+ return NextResponse.json(combo);
+}
diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json
index e6bdf920aed..1358f77de4f 100644
--- a/src/i18n/messages/ar.json
+++ b/src/i18n/messages/ar.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/az.json b/src/i18n/messages/az.json
index 007b6926d91..47d64bb9dbc 100644
--- a/src/i18n/messages/az.json
+++ b/src/i18n/messages/az.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Marşrutlaşdırma",
"protocolsSection": "Protokollar",
"agentsAiSection": "Agentlər və AI",
diff --git a/src/i18n/messages/bg.json b/src/i18n/messages/bg.json
index 7cec7cab70b..3f547446136 100644
--- a/src/i18n/messages/bg.json
+++ b/src/i18n/messages/bg.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/bn.json b/src/i18n/messages/bn.json
index a6671e3acb7..0e390d56ac4 100644
--- a/src/i18n/messages/bn.json
+++ b/src/i18n/messages/bn.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/cs.json b/src/i18n/messages/cs.json
index 7eaa80ca5c2..a3236255905 100644
--- a/src/i18n/messages/cs.json
+++ b/src/i18n/messages/cs.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/da.json b/src/i18n/messages/da.json
index 6cea3716b8a..a02edf11c14 100644
--- a/src/i18n/messages/da.json
+++ b/src/i18n/messages/da.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json
index 458ba1124dc..123e4a6fcf3 100644
--- a/src/i18n/messages/de.json
+++ b/src/i18n/messages/de.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index b0e2ec8c2d8..2b2c87e5057 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -948,6 +948,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json
index 538b6ddb5aa..ec182d20968 100644
--- a/src/i18n/messages/es.json
+++ b/src/i18n/messages/es.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/fa.json b/src/i18n/messages/fa.json
index 6e4c216073f..c5ce1e09c03 100644
--- a/src/i18n/messages/fa.json
+++ b/src/i18n/messages/fa.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/fi.json b/src/i18n/messages/fi.json
index 9ffbf6f1043..e4d40844279 100644
--- a/src/i18n/messages/fi.json
+++ b/src/i18n/messages/fi.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json
index 99ca7b75908..07042fcc7c0 100644
--- a/src/i18n/messages/fr.json
+++ b/src/i18n/messages/fr.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/gu.json b/src/i18n/messages/gu.json
index 3a82753b970..b1a0f8124e7 100644
--- a/src/i18n/messages/gu.json
+++ b/src/i18n/messages/gu.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/he.json b/src/i18n/messages/he.json
index 9b4468580d1..5244613f53b 100644
--- a/src/i18n/messages/he.json
+++ b/src/i18n/messages/he.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/hi.json b/src/i18n/messages/hi.json
index 21cac7b418e..50c2f0693ea 100644
--- a/src/i18n/messages/hi.json
+++ b/src/i18n/messages/hi.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/hu.json b/src/i18n/messages/hu.json
index 702474a9578..925f66fcfec 100644
--- a/src/i18n/messages/hu.json
+++ b/src/i18n/messages/hu.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/id.json b/src/i18n/messages/id.json
index d576621b2d3..a2df9f457ee 100644
--- a/src/i18n/messages/id.json
+++ b/src/i18n/messages/id.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/in.json b/src/i18n/messages/in.json
index 6ca6123f418..22cbe043ce4 100644
--- a/src/i18n/messages/in.json
+++ b/src/i18n/messages/in.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json
index 67b9efdcdf9..14cf84d5f8a 100644
--- a/src/i18n/messages/it.json
+++ b/src/i18n/messages/it.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json
index df7e7d6d087..cc9601f1e0e 100644
--- a/src/i18n/messages/ja.json
+++ b/src/i18n/messages/ja.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json
index c9b0705f9ad..defb703c7a3 100644
--- a/src/i18n/messages/ko.json
+++ b/src/i18n/messages/ko.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/mr.json b/src/i18n/messages/mr.json
index 2749285761b..d4202a3cdfb 100644
--- a/src/i18n/messages/mr.json
+++ b/src/i18n/messages/mr.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/ms.json b/src/i18n/messages/ms.json
index 0207d74e169..b7641761cf4 100644
--- a/src/i18n/messages/ms.json
+++ b/src/i18n/messages/ms.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json
index 665f05bc16e..18cdca568cd 100644
--- a/src/i18n/messages/nl.json
+++ b/src/i18n/messages/nl.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/no.json b/src/i18n/messages/no.json
index 982dc05dc12..7c95b6775f0 100644
--- a/src/i18n/messages/no.json
+++ b/src/i18n/messages/no.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/phi.json b/src/i18n/messages/phi.json
index 10c3e787963..812d24e7b21 100644
--- a/src/i18n/messages/phi.json
+++ b/src/i18n/messages/phi.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/pl.json b/src/i18n/messages/pl.json
index c60db4c5c04..10d80cf90ac 100644
--- a/src/i18n/messages/pl.json
+++ b/src/i18n/messages/pl.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json
index 735431d8429..161cdefce23 100644
--- a/src/i18n/messages/pt-BR.json
+++ b/src/i18n/messages/pt-BR.json
@@ -6950,6 +6950,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Combos de Engines",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json
index 0f94cbd5ed8..7be9308edac 100644
--- a/src/i18n/messages/pt.json
+++ b/src/i18n/messages/pt.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Combos de Engines",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/ro.json b/src/i18n/messages/ro.json
index cf4411ad63b..870eeda8025 100644
--- a/src/i18n/messages/ro.json
+++ b/src/i18n/messages/ro.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json
index 461cd46dbe2..ae2426358b0 100644
--- a/src/i18n/messages/ru.json
+++ b/src/i18n/messages/ru.json
@@ -946,6 +946,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/sk.json b/src/i18n/messages/sk.json
index 3b3c21af0e0..642f26b44f1 100644
--- a/src/i18n/messages/sk.json
+++ b/src/i18n/messages/sk.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/sv.json b/src/i18n/messages/sv.json
index ff3431cd209..f93e3e51207 100644
--- a/src/i18n/messages/sv.json
+++ b/src/i18n/messages/sv.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/sw.json b/src/i18n/messages/sw.json
index ab45eebbd6f..3a0df74d851 100644
--- a/src/i18n/messages/sw.json
+++ b/src/i18n/messages/sw.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/ta.json b/src/i18n/messages/ta.json
index b8b2311b09b..2da5b4ef2f9 100644
--- a/src/i18n/messages/ta.json
+++ b/src/i18n/messages/ta.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/te.json b/src/i18n/messages/te.json
index 803ad8c4611..253cda5b8bf 100644
--- a/src/i18n/messages/te.json
+++ b/src/i18n/messages/te.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/th.json b/src/i18n/messages/th.json
index e61e0a803a3..ee67991a1c3 100644
--- a/src/i18n/messages/th.json
+++ b/src/i18n/messages/th.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/tr.json b/src/i18n/messages/tr.json
index be5b0489477..7173330b9d6 100644
--- a/src/i18n/messages/tr.json
+++ b/src/i18n/messages/tr.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/uk-UA.json b/src/i18n/messages/uk-UA.json
index c2ca0a307ed..9f677eb9c4e 100644
--- a/src/i18n/messages/uk-UA.json
+++ b/src/i18n/messages/uk-UA.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Комбінації двигунів",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Маршрутизація",
"protocolsSection": "Протоколи",
"agentsAiSection": "Агенти та AI",
diff --git a/src/i18n/messages/ur.json b/src/i18n/messages/ur.json
index 73c29be6417..81113e592ce 100644
--- a/src/i18n/messages/ur.json
+++ b/src/i18n/messages/ur.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/vi.json b/src/i18n/messages/vi.json
index 96a475fd173..144ef76eafb 100644
--- a/src/i18n/messages/vi.json
+++ b/src/i18n/messages/vi.json
@@ -945,6 +945,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "Engine Combos",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "Routing",
"protocolsSection": "Protocols",
"agentsAiSection": "Agents & AI",
diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json
index 7ce67b56991..72b4d9aafdc 100644
--- a/src/i18n/messages/zh-CN.json
+++ b/src/i18n/messages/zh-CN.json
@@ -946,6 +946,16 @@
"contextCaveman": "Caveman",
"contextRtk": "RTK",
"contextCombos": "引擎组合",
+ "contextLite": "Lite",
+ "contextAggressive": "Aggressive",
+ "contextUltra": "Ultra",
+ "contextSettings": "Compression Settings",
+ "contextHeadroom": "Headroom",
+ "contextSessionDedup": "Session Dedup",
+ "contextCcr": "CCR",
+ "contextLlmlingua": "LLMLingua",
+ "combosLive": "Combo Studio",
+ "compressionStudio": "Compression Studio",
"routingSection": "路由",
"protocolsSection": "协议",
"agentsAiSection": "代理与 AI",
diff --git a/src/lib/db/compressionAnalytics.ts b/src/lib/db/compressionAnalytics.ts
index ec5c0252129..8a051bd8a62 100644
--- a/src/lib/db/compressionAnalytics.ts
+++ b/src/lib/db/compressionAnalytics.ts
@@ -205,6 +205,26 @@ function appendCondition(whereClause: string, condition: string): string {
return whereClause ? `${whereClause} AND ${condition}` : `WHERE ${condition}`;
}
+export function getPerEngineAnalytics(engineId: string, days = 7) {
+ const db = getDbInstance();
+ ensureCompressionAnalyticsColumns();
+ const since = new Date(Date.now() - days * 86400_000).toISOString();
+ const row = db
+ .prepare(
+ `SELECT COUNT(*) AS runs,
+ COALESCE(SUM(original_tokens), 0) AS original,
+ COALESCE(SUM(compressed_tokens), 0) AS compressed,
+ COALESCE(SUM(tokens_saved), 0) AS saved
+ FROM compression_analytics
+ WHERE COALESCE(engine, mode) = ? AND timestamp >= ?`
+ )
+ .get(engineId, since) as { runs: number; original: number; compressed: number; saved: number };
+ const tokensSaved = Math.max(0, row.saved);
+ const avgSavingsPercent =
+ row.original > 0 ? Math.round(((row.original - row.compressed) / row.original) * 1000) / 10 : 0;
+ return { engineId, runs: row.runs, tokensSaved, avgSavingsPercent, days };
+}
+
export function getCompressionAnalyticsSummary(since?: string): CompressionAnalyticsSummary {
const db = getDbInstance();
ensureCompressionAnalyticsColumns();
diff --git a/src/lib/db/compressionCombos.ts b/src/lib/db/compressionCombos.ts
index 940cb3a9087..e7f3dcb8886 100644
--- a/src/lib/db/compressionCombos.ts
+++ b/src/lib/db/compressionCombos.ts
@@ -1,5 +1,8 @@
import { v4 as uuidv4 } from "uuid";
-import type { CompressionPipelineStep } from "@omniroute/open-sse/services/compression/types.ts";
+import type {
+ CompressionEngineId,
+ CompressionPipelineStep,
+} from "@omniroute/open-sse/services/compression/types.ts";
import { backupDbFile } from "./backup";
import { getDbInstance, rowToCamel } from "./core";
@@ -53,13 +56,21 @@ function parseJsonArray(value: unknown, fallback: T[]): T[] {
}
}
+const KNOWN_ENGINE_IDS = [
+ "lite",
+ "caveman",
+ "aggressive",
+ "ultra",
+ "rtk",
+ "headroom",
+ "session-dedup",
+ "ccr",
+ "llmlingua",
+];
+
function normalizePipeline(value: unknown): CompressionPipelineStep[] {
return parseJsonArray(value, []).filter((step) => {
- return (
- step &&
- typeof step === "object" &&
- ["lite", "caveman", "aggressive", "ultra", "rtk"].includes(String(step.engine))
- );
+ return step && typeof step === "object" && KNOWN_ENGINE_IDS.includes(String(step.engine));
});
}
@@ -388,6 +399,66 @@ export function unassignRoutingCombo(compressionComboId: string, routingComboId:
return result.changes > 0;
}
+// Static stackPriority map — mirrors the values defined in each engine file.
+// Using a static map avoids cross-workspace imports (open-sse → src/lib/db) that
+// would introduce a circular dependency detected by check:cycles.
+const ENGINE_STACK_PRIORITY: Record = {
+ "session-dedup": 3,
+ ccr: 4,
+ lite: 5,
+ rtk: 10,
+ headroom: 15,
+ caveman: 20,
+ aggressive: 30,
+ llmlingua: 35,
+ ultra: 40,
+};
+
+export function setEngineInDefaultCombo(
+ engineId: string,
+ enabled: boolean,
+ config?: Record
+): CompressionCombo | null {
+ if (!KNOWN_ENGINE_IDS.includes(engineId)) return null;
+ ensureCompressionComboTables();
+ const existing = getDefaultCompressionCombo();
+ if (!existing) return null;
+
+ let newPipeline = [...existing.pipeline];
+ if (enabled) {
+ const idx = newPipeline.findIndex((s) => s.engine === engineId);
+ if (idx >= 0) {
+ if (config !== undefined) {
+ newPipeline[idx] = { ...newPipeline[idx], config };
+ }
+ } else {
+ newPipeline.push({ engine: engineId as CompressionEngineId, ...(config ? { config } : {}) });
+ }
+ // Sort by stackPriority ascending so the pipeline runs in the correct order.
+ newPipeline.sort((a, b) => {
+ const pa = ENGINE_STACK_PRIORITY[a.engine] ?? 50;
+ const pb = ENGINE_STACK_PRIORITY[b.engine] ?? 50;
+ return pa - pb;
+ });
+ } else {
+ newPipeline = newPipeline.filter((s) => s.engine !== engineId);
+ }
+
+ // Direct UPDATE — preserves empty pipeline (Fix #2) without going through
+ // buildComboPayload which falls back to defaultCompressionComboPipeline() when
+ // the incoming array is empty.
+ const db = getDbInstance();
+ const now = new Date().toISOString();
+ db.prepare("UPDATE compression_combos SET pipeline = ?, updated_at = ? WHERE id = ?").run(
+ JSON.stringify(newPipeline),
+ now,
+ existing.id
+ );
+ backupDbFile("pre-write");
+
+ return getCompressionCombo(existing.id);
+}
+
export function updateAssignments(compressionComboId: string, routingComboIds: string[]): boolean {
ensureCompressionComboTables();
if (!getCompressionCombo(compressionComboId)) return false;
diff --git a/src/server/ws/liveServer.ts b/src/server/ws/liveServer.ts
index 017f931216f..74f5e48fd85 100644
--- a/src/server/ws/liveServer.ts
+++ b/src/server/ws/liveServer.ts
@@ -362,11 +362,14 @@ export async function startLiveDashboardServer(
});
}
-// ── Auto-start on import (opt-in) ────────────────────────────────────────
+// ── Auto-start on import ──────────────────────────────────────────────────
//
-// Default: OFF. The live dashboard WebSocket is an opt-in feature — operators
-// who want it must set OMNIROUTE_ENABLE_LIVE_WS=1 (or "true"). This avoids
-// silently opening a network listener on every Next.js boot.
+// Default: ON, bound to loopback (127.0.0.1). The live dashboard WebSocket
+// starts automatically unless explicitly disabled. To disable, set:
+// OMNIROUTE_ENABLE_LIVE_WS=0 (or "false")
+//
+// LAN exposure remains opt-in via LIVE_WS_HOST=0.0.0.0 combined with
+// LIVE_WS_ALLOWED_ORIGINS. DEFAULT_HOST stays "127.0.0.1".
//
// Build/test environments never auto-start regardless of the flag.
@@ -379,9 +382,10 @@ function isBuildOrTest(): boolean {
);
}
-function isLiveWsEnabled(): boolean {
+export function isLiveWsEnabled(): boolean {
const v = process.env.OMNIROUTE_ENABLE_LIVE_WS;
- return v === "1" || v === "true";
+ if (v === undefined) return true; // default ON (loopback-bound)
+ return v === "1" || v.toLowerCase() === "true";
}
if (!isBuildOrTest() && isLiveWsEnabled()) {
diff --git a/src/shared/components/Sidebar.tsx b/src/shared/components/Sidebar.tsx
index 5afc5bdf79f..d490fd0a5e8 100644
--- a/src/shared/components/Sidebar.tsx
+++ b/src/shared/components/Sidebar.tsx
@@ -173,10 +173,12 @@ export default function Sidebar({
const resolveItem = (item: SidebarItemDefinition, hidden: Set) => {
if (hidden.has(item.id)) return null;
- const subtitle = item.subtitleKey ? getSidebarLabel(item.subtitleKey, "") : undefined;
+ const subtitle = item.subtitleKey
+ ? getSidebarLabel(item.subtitleKey, item.subtitleFallback ?? "")
+ : item.subtitleFallback;
return {
...item,
- label: getSidebarLabel(item.i18nKey, item.id),
+ label: getSidebarLabel(item.i18nKey, item.labelFallback ?? item.id),
subtitle: subtitle || undefined,
};
};
diff --git a/src/shared/components/compression/EngineConfigForm.tsx b/src/shared/components/compression/EngineConfigForm.tsx
new file mode 100644
index 00000000000..0a6ff2771ad
--- /dev/null
+++ b/src/shared/components/compression/EngineConfigForm.tsx
@@ -0,0 +1,78 @@
+"use client";
+import type { EngineConfigField } from "@omniroute/open-sse/services/compression/engines/types";
+
+export interface EngineConfigFormProps {
+ schema: EngineConfigField[];
+ value: Record;
+ onChange: (next: Record) => void;
+}
+
+export function EngineConfigForm({ schema, value, onChange }: EngineConfigFormProps) {
+ const set = (k: string, v: unknown) => onChange({ ...value, [k]: v });
+ return (
+
+ {schema.map((f) => {
+ const v = value[f.key] ?? f.defaultValue;
+ return (
+
+ {f.label}
+ {f.description && {f.description} }
+ {f.type === "boolean" && (
+ set(f.key, e.target.checked)} />
+ )}
+ {f.type === "number" && (
+ set(f.key, Number(e.target.value))}
+ className="border border-border rounded px-2 py-1"
+ />
+ )}
+ {f.type === "string" && (
+ set(f.key, e.target.value)}
+ className="border border-border rounded px-2 py-1"
+ />
+ )}
+ {f.type === "select" && (
+ set(f.key, e.target.value)}
+ className="border border-border rounded px-2 py-1"
+ >
+ {(f.options ?? []).map((o) => (
+
+ {o.label}
+
+ ))}
+
+ )}
+ {f.type === "multiselect" &&
+ (f.options ?? []).map((o) => (
+
+ {
+ const arr = Array.isArray(v) ? [...(v as string[])] : [];
+ set(
+ f.key,
+ e.target.checked ? [...arr, o.value] : arr.filter((x) => x !== o.value)
+ );
+ }}
+ />
+ {o.label}
+
+ ))}
+
+ );
+ })}
+
+ );
+}
+
+export default EngineConfigForm;
diff --git a/src/shared/components/compression/EngineConfigPage.tsx b/src/shared/components/compression/EngineConfigPage.tsx
new file mode 100644
index 00000000000..663b0ec913e
--- /dev/null
+++ b/src/shared/components/compression/EngineConfigPage.tsx
@@ -0,0 +1,343 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import type { EngineConfigField } from "@omniroute/open-sse/services/compression/engines/types";
+import { EngineConfigForm } from "@/shared/components/compression/EngineConfigForm";
+
+// ── Types ─────────────────────────────────────────────────────────────────
+
+interface EngineEntry {
+ id: string;
+ name: string;
+ description: string;
+ icon: string;
+ stackable: boolean;
+ stackPriority: number;
+ metadata: { description?: string; [key: string]: unknown };
+ configSchema: EngineConfigField[];
+}
+
+interface ComboStep {
+ engine: string;
+ intensity?: string;
+ config?: Record;
+}
+
+interface Analytics {
+ engineId: string;
+ runs: number;
+ tokensSaved: number;
+ avgSavingsPercent: number;
+ days: number;
+}
+
+interface PreviewResult {
+ originalTokens: number;
+ compressedTokens: number;
+ savingsPct: number;
+}
+
+// ── Default preview sample ────────────────────────────────────────────────
+
+const PREVIEW_SAMPLE =
+ "The quick brown fox jumps over the lazy dog. " +
+ "This is a sample message used to preview compression. " +
+ "It contains enough text to show meaningful token savings.";
+
+// ── Sub-components ────────────────────────────────────────────────────────
+
+function StatCard({ label, value }: { label: string; value: string }) {
+ return (
+
+ {label}
+ {value}
+
+ );
+}
+
+// ── Main component ────────────────────────────────────────────────────────
+
+export function EngineConfigPage({ engineId }: { engineId: string }) {
+ // ── Data state ──────────────────────────────────────────────────────────
+ const [engine, setEngine] = useState(null);
+ const [enabled, setEnabled] = useState(false);
+ const [configState, setConfigState] = useState>({});
+ const [analytics, setAnalytics] = useState(null);
+ const [loadError, setLoadError] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ // ── Preview state ───────────────────────────────────────────────────────
+ const [previewText, setPreviewText] = useState(PREVIEW_SAMPLE);
+ const [preview, setPreview] = useState(null);
+ const [previewError, setPreviewError] = useState(null);
+ const [previewLoading, setPreviewLoading] = useState(false);
+
+ // ── Action state ────────────────────────────────────────────────────────
+ const [saveError, setSaveError] = useState(null);
+ const [toggleError, setToggleError] = useState(null);
+ const [saving, setSaving] = useState(false);
+
+ // ── Initial load ────────────────────────────────────────────────────────
+ useEffect(() => {
+ let cancelled = false;
+
+ async function load() {
+ setLoading(true);
+ setLoadError(null);
+
+ // Fire the three independent reads in parallel — load time is the slowest
+ // single request, not their sum. Each resolves to null on failure (fail-soft).
+ const asJson = (r: Response) => (r.ok ? r.json() : null);
+ const [enginesData, comboData, analyticsData] = await Promise.all([
+ fetch("/api/compression/engines")
+ .then(asJson)
+ .catch(() => null) as Promise<{ engines: EngineEntry[] } | null>,
+ fetch("/api/context/combos/default")
+ .then(asJson)
+ .catch(() => null) as Promise<{ pipeline?: ComboStep[] } | null>,
+ fetch(`/api/context/analytics/engine?engineId=${engineId}&days=7`)
+ .then(asJson)
+ .catch(() => null) as Promise,
+ ]);
+
+ let foundEngine: EngineEntry | null = null;
+ if (enginesData) {
+ foundEngine = enginesData.engines?.find((e) => e.id === engineId) ?? null;
+ } else {
+ setLoadError("Failed to load engine information.");
+ }
+
+ // Derive enabled + currentConfig from the default combo (404/null = defaults)
+ let currentEnabled = false;
+ let currentConfig: Record = {};
+ const step = comboData?.pipeline?.find((s) => s.engine === engineId);
+ if (step) {
+ currentEnabled = true;
+ currentConfig = step.config ?? {};
+ }
+
+ if (!cancelled) {
+ if (analyticsData) setAnalytics(analyticsData);
+ setEngine(foundEngine);
+ setEnabled(currentEnabled);
+ // Seed configState from defaultValues then override with currentConfig
+ const defaults: Record = {};
+ for (const field of foundEngine?.configSchema ?? []) {
+ defaults[field.key] = field.defaultValue;
+ }
+ setConfigState({ ...defaults, ...currentConfig });
+ setLoading(false);
+ }
+ }
+
+ void load();
+ return () => {
+ cancelled = true;
+ };
+ }, [engineId]);
+
+ // ── Handlers ─────────────────────────────────────────────────────────────
+
+ async function handleToggle() {
+ const next = !enabled;
+ setEnabled(next);
+ setToggleError(null);
+ try {
+ const res = await fetch("/api/context/combos/default", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ engineId, enabled: next }),
+ });
+ if (!res.ok) {
+ setToggleError("Failed to update engine state.");
+ setEnabled(!next); // revert
+ }
+ } catch {
+ setToggleError("Failed to update engine state.");
+ setEnabled(!next); // revert
+ }
+ }
+
+ async function handleSave() {
+ setSaving(true);
+ setSaveError(null);
+ try {
+ const res = await fetch("/api/context/combos/default", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ engineId, enabled, config: configState }),
+ });
+ if (!res.ok) {
+ setSaveError("Failed to save configuration.");
+ }
+ } catch {
+ setSaveError("Failed to save configuration.");
+ } finally {
+ setSaving(false);
+ }
+ }
+
+ async function handlePreview() {
+ setPreviewLoading(true);
+ setPreviewError(null);
+ setPreview(null);
+ try {
+ const res = await fetch("/api/compression/preview", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ engineId,
+ messages: [{ role: "user", content: previewText }],
+ }),
+ });
+ if (res.ok) {
+ const data = (await res.json()) as PreviewResult;
+ setPreview(data);
+ } else {
+ setPreviewError("Preview failed.");
+ }
+ } catch {
+ setPreviewError("Preview failed.");
+ } finally {
+ setPreviewLoading(false);
+ }
+ }
+
+ // ── Render ────────────────────────────────────────────────────────────────
+
+ if (loading) {
+ return (
+ Loading…
+ );
+ }
+
+ if (!engine) {
+ return (
+
+ {loadError ?? `Engine "${engineId}" not found.`}
+
+ );
+ }
+
+ const subtitle = engine.metadata?.description ?? engine.description;
+
+ return (
+
+ {/* ── Header ── */}
+
+
+ {engine.icon && {engine.icon} }
+
{engine.name}
+
+ {subtitle &&
{subtitle}
}
+
+
+ {loadError && (
+
+ {loadError}
+
+ )}
+
+ {/* ── Enable toggle ── */}
+
+
+
+ Ativar camada
+
+ {enabled ? "Esta camada está ativa no pipeline padrão." : "Esta camada está inativa."}
+
+
+
+
+ {toggleError &&
{toggleError}
}
+
+ As camadas ativadas rodam quando a compressão está no modo "stacked". Configure
+ em{" "}
+
+ Compression Settings
+
+ .
+
+
+
+ {/* ── Config form ── */}
+
+
Configuração
+
+
+
+ {saving ? "Salvando…" : "Salvar"}
+
+ {saveError &&
{saveError}
}
+
+
+
+ {/* ── Live preview ── */}
+
+
+ {/* ── Analytics strip ── */}
+
+
Últimos 7 dias
+ {analytics && analytics.runs === 0 ? (
+
Sem dados ainda / No data yet
+ ) : analytics ? (
+
+
+
+
+
+ ) : (
+
Sem dados ainda / No data yet
+ )}
+
+
+ );
+}
+
+export default EngineConfigPage;
diff --git a/src/shared/constants/featureFlagDefinitions.ts b/src/shared/constants/featureFlagDefinitions.ts
index 111d53e896f..19924d3e05d 100644
--- a/src/shared/constants/featureFlagDefinitions.ts
+++ b/src/shared/constants/featureFlagDefinitions.ts
@@ -259,7 +259,7 @@ export const FEATURE_FLAG_DEFINITIONS: FeatureFlagDefinition[] = [
key: "OMNIROUTE_ENABLE_LIVE_WS",
label: "Live Dashboard WebSocket",
description:
- "Start the real-time dashboard WebSocket server on import (port 20129 by default).",
+ "Start the real-time dashboard WebSocket server on import (port 20129, loopback-bound by default). Default: enabled. Set to '0' or 'false' to disable. LAN exposure requires LIVE_WS_HOST=0.0.0.0 + LIVE_WS_ALLOWED_ORIGINS.",
descriptionI18nKey: "featureFlagOmnirouteEnableLiveWsDescription",
category: "runtime",
defaultValue: "true",
diff --git a/src/shared/constants/sidebarVisibility.ts b/src/shared/constants/sidebarVisibility.ts
index c9385aa1bbd..d41e4ad7019 100644
--- a/src/shared/constants/sidebarVisibility.ts
+++ b/src/shared/constants/sidebarVisibility.ts
@@ -7,11 +7,21 @@ export const HIDEABLE_SIDEBAR_ITEM_IDS = [
"providers",
"embedded-services",
"combos",
+ "combos-live",
"quota",
// OmniProxy > Compression Context
+ "context-settings",
"context-caveman",
"context-rtk",
+ "context-headroom",
+ "context-session-dedup",
+ "context-ccr",
+ "context-llmlingua",
+ "context-lite",
+ "context-aggressive",
+ "context-ultra",
"context-combos",
+ "compression-studio",
// OmniProxy > Tools
"cli-code",
"cli-agents",
@@ -110,6 +120,10 @@ export interface SidebarItemDefinition {
href: string;
i18nKey: string;
subtitleKey?: string;
+ /** Literal label shown when `i18nKey` has no translation (avoids per-locale edits). */
+ labelFallback?: string;
+ /** Literal subtitle shown when `subtitleKey` is absent/untranslated. */
+ subtitleFallback?: string;
icon: string;
exact?: boolean;
external?: boolean;
@@ -192,6 +206,14 @@ const OMNI_PROXY_ITEMS: readonly SidebarItemDefinition[] = [
subtitleKey: "combosSubtitle",
icon: "layers",
},
+ {
+ id: "combos-live",
+ href: "/dashboard/combos/live",
+ i18nKey: "combosLive",
+ labelFallback: "Combo Studio",
+ subtitleFallback: "Live routing cascade",
+ icon: "account_tree",
+ },
{
id: "quota",
href: "/dashboard/quota",
@@ -208,12 +230,20 @@ const OMNI_PROXY_ITEMS: readonly SidebarItemDefinition[] = [
},
];
-const COMPRESSION_CONTEXT_GROUP: SidebarItemGroup = {
+export const COMPRESSION_CONTEXT_GROUP: SidebarItemGroup = {
type: "group",
id: "compression-context",
titleKey: "compressionContextGroup",
titleFallback: "Compression Context",
items: [
+ {
+ id: "context-settings",
+ href: "/dashboard/context/settings",
+ i18nKey: "contextSettings",
+ labelFallback: "Compression Settings",
+ subtitleFallback: "Global defaults",
+ icon: "settings",
+ },
{
id: "context-caveman",
href: "/dashboard/context/caveman",
@@ -228,6 +258,62 @@ const COMPRESSION_CONTEXT_GROUP: SidebarItemGroup = {
subtitleKey: "contextRtkSubtitle",
icon: "filter_alt",
},
+ {
+ id: "context-headroom",
+ href: "/dashboard/context/headroom",
+ i18nKey: "contextHeadroom",
+ labelFallback: "Headroom",
+ subtitleFallback: "Tabular compaction",
+ icon: "table_rows",
+ },
+ {
+ id: "context-session-dedup",
+ href: "/dashboard/context/session-dedup",
+ i18nKey: "contextSessionDedup",
+ labelFallback: "Session Dedup",
+ subtitleFallback: "Cross-turn dedup",
+ icon: "content_copy",
+ },
+ {
+ id: "context-ccr",
+ href: "/dashboard/context/ccr",
+ i18nKey: "contextCcr",
+ labelFallback: "CCR",
+ subtitleFallback: "Retrieve markers",
+ icon: "archive",
+ },
+ {
+ id: "context-llmlingua",
+ href: "/dashboard/context/llmlingua",
+ i18nKey: "contextLlmlingua",
+ labelFallback: "LLMLingua",
+ subtitleFallback: "Semantic pruning",
+ icon: "psychology",
+ },
+ {
+ id: "context-lite",
+ href: "/dashboard/context/lite",
+ i18nKey: "contextLite",
+ labelFallback: "Lite",
+ subtitleFallback: "Fast whitespace cleanup",
+ icon: "compress",
+ },
+ {
+ id: "context-aggressive",
+ href: "/dashboard/context/aggressive",
+ i18nKey: "contextAggressive",
+ labelFallback: "Aggressive",
+ subtitleFallback: "Summary + aging",
+ icon: "speed",
+ },
+ {
+ id: "context-ultra",
+ href: "/dashboard/context/ultra",
+ i18nKey: "contextUltra",
+ labelFallback: "Ultra",
+ subtitleFallback: "Heuristic pruning",
+ icon: "bolt",
+ },
{
id: "context-combos",
href: "/dashboard/context/combos",
@@ -235,6 +321,14 @@ const COMPRESSION_CONTEXT_GROUP: SidebarItemGroup = {
subtitleKey: "contextCombosSubtitle",
icon: "hub",
},
+ {
+ id: "compression-studio",
+ href: "/dashboard/compression/studio",
+ i18nKey: "compressionStudio",
+ labelFallback: "Compression Studio",
+ subtitleFallback: "Live engine cascade",
+ icon: "monitoring",
+ },
],
};
diff --git a/tests/unit/api/compression-engines-route.test.ts b/tests/unit/api/compression-engines-route.test.ts
new file mode 100644
index 00000000000..4ad921252ef
--- /dev/null
+++ b/tests/unit/api/compression-engines-route.test.ts
@@ -0,0 +1,126 @@
+/**
+ * GET /api/compression/engines
+ *
+ * Auth + isolation pattern mirrors tests/unit/api/context-combos-default-route.test.ts:
+ * - makeManagementSessionRequest() for JWT cookie auth.
+ * - Temp DATA_DIR, resetDbInstance() before each test, cleanup in test.after().
+ */
+import test, { describe } from "node:test";
+import assert from "node:assert/strict";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+
+import { makeManagementSessionRequest } from "../../helpers/managementSession.ts";
+
+// ─── isolated temp DB ─────────────────────────────────────────────────────────
+
+const TEST_DATA_DIR = fs.mkdtempSync(
+ path.join(os.tmpdir(), "omniroute-compression-engines-route-")
+);
+const originalDataDir = process.env.DATA_DIR;
+const originalJwtSecret = process.env.JWT_SECRET;
+
+process.env.DATA_DIR = TEST_DATA_DIR;
+
+const core = await import("../../../src/lib/db/core.ts");
+const settingsDb = await import("../../../src/lib/db/settings.ts");
+const enginesRoute = await import("../../../src/app/api/compression/engines/route.ts");
+
+// ─── helpers ──────────────────────────────────────────────────────────────────
+
+async function setupAuth(): Promise {
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+ fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
+ await settingsDb.updateSettings({
+ requireLogin: true,
+ setupComplete: true,
+ password: "test-password-hash",
+ });
+}
+
+// ─── lifecycle ────────────────────────────────────────────────────────────────
+
+test.beforeEach(async () => {
+ process.env.DATA_DIR = TEST_DATA_DIR;
+ await setupAuth();
+});
+
+test.after(() => {
+ if (originalDataDir === undefined) delete process.env.DATA_DIR;
+ else process.env.DATA_DIR = originalDataDir;
+ if (originalJwtSecret === undefined) delete process.env.JWT_SECRET;
+ else process.env.JWT_SECRET = originalJwtSecret;
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+});
+
+// ─── tests ────────────────────────────────────────────────────────────────────
+
+describe("GET /api/compression/engines", () => {
+ test("returns 401 without auth", async () => {
+ const req = new Request("http://localhost/api/compression/engines");
+ const res = await enginesRoute.GET(req);
+ assert.equal(res.status, 401, `Expected 401 without auth, got ${res.status}`);
+ });
+
+ test("returns an engines array", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/compression/engines");
+ const res = await enginesRoute.GET(req);
+ assert.strictEqual(res.status, 200);
+ const body = (await res.json()) as { engines: unknown[] };
+ assert.ok(Array.isArray(body.engines), "response should have an engines array");
+ assert.ok(body.engines.length > 0, "engines array should be non-empty");
+ });
+
+ test("includes headroom and caveman engines", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/compression/engines");
+ const res = await enginesRoute.GET(req);
+ const body = (await res.json()) as { engines: Array<{ id: string }> };
+ const ids = body.engines.map((e) => e.id);
+ assert.ok(ids.includes("headroom"), `engines should include headroom, got: ${ids.join(", ")}`);
+ assert.ok(ids.includes("caveman"), `engines should include caveman, got: ${ids.join(", ")}`);
+ });
+
+ test("headroom entry has non-empty configSchema and numeric stackPriority", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/compression/engines");
+ const res = await enginesRoute.GET(req);
+ const body = (await res.json()) as {
+ engines: Array<{
+ id: string;
+ configSchema: Array<{ key: string }>;
+ stackPriority: unknown;
+ }>;
+ };
+ const headroom = body.engines.find((e) => e.id === "headroom");
+ assert.ok(headroom, "headroom engine should be present");
+ assert.ok(
+ Array.isArray(headroom.configSchema) && headroom.configSchema.length > 0,
+ "headroom configSchema should be a non-empty array"
+ );
+ assert.strictEqual(
+ typeof headroom.stackPriority,
+ "number",
+ "headroom stackPriority should be a number"
+ );
+ });
+
+ test("headroom configSchema includes the 'minRows' field key", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/compression/engines");
+ const res = await enginesRoute.GET(req);
+ const body = (await res.json()) as {
+ engines: Array<{
+ id: string;
+ configSchema: Array<{ key: string }>;
+ }>;
+ };
+ const headroom = body.engines.find((e) => e.id === "headroom");
+ assert.ok(headroom, "headroom engine should be present");
+ const hasMinRows = headroom.configSchema.some((f) => f.key === "minRows");
+ assert.ok(
+ hasMinRows,
+ `headroom configSchema should contain a field with key 'minRows', got keys: ${headroom.configSchema.map((f) => f.key).join(", ")}`
+ );
+ });
+});
diff --git a/tests/unit/api/compression-preview-engine.test.ts b/tests/unit/api/compression-preview-engine.test.ts
new file mode 100644
index 00000000000..847939fbd57
--- /dev/null
+++ b/tests/unit/api/compression-preview-engine.test.ts
@@ -0,0 +1,168 @@
+/**
+ * TDD: per-engine compression preview via engineId param.
+ *
+ * Auth pattern mirrors tests/unit/compression/compression-preview-auth.test.ts:
+ * - Use makeManagementSessionRequest to create a JWT-auth'd Request.
+ * - Set DATA_DIR to a temp dir and run the DB setup before importing the route.
+ */
+import test from "node:test";
+import assert from "node:assert/strict";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+
+import { makeManagementSessionRequest } from "../../helpers/managementSession.ts";
+
+// ─── temp DB isolation ────────────────────────────────────────────────────────
+
+const TEST_DATA_DIR = fs.mkdtempSync(
+ path.join(os.tmpdir(), "omniroute-compression-preview-engine-")
+);
+const originalDataDir = process.env.DATA_DIR;
+const originalJwtSecret = process.env.JWT_SECRET;
+
+process.env.DATA_DIR = TEST_DATA_DIR;
+
+const core = await import("../../../src/lib/db/core.ts");
+const settingsDb = await import("../../../src/lib/db/settings.ts");
+const previewRoute = await import("../../../src/app/api/compression/preview/route.ts");
+
+// ─── helpers ──────────────────────────────────────────────────────────────────
+
+async function setupAuth(): Promise {
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+ fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
+ await settingsDb.updateSettings({
+ requireLogin: true,
+ setupComplete: true,
+ password: "test-password-hash",
+ });
+}
+
+/**
+ * Build a message array whose content is a pretty-printed JSON string of a homogeneous
+ * array of ≥8 rows. headroom's crushText() tries the whole string as a JSON array when
+ * it starts with "[", so passing JSON.stringify(rows, null, 2) as content triggers
+ * compaction. Pretty-printed JSON has whitespace so countTokens() also gives a
+ * meaningful baseline to compare against the compact tabular output.
+ */
+function buildHeadroomMessages(): Array<{ role: string; content: string }> {
+ const rows = Array.from({ length: 10 }, (_, i) => ({
+ id: i + 1,
+ name: `item-${i + 1}`,
+ value: (i + 1) * 100,
+ active: true,
+ }));
+ // Pretty-print so word-split countTokens() produces a non-trivial baseline count
+ return [{ role: "user", content: JSON.stringify(rows, null, 2) }];
+}
+
+// ─── lifecycle ────────────────────────────────────────────────────────────────
+
+test.beforeEach(async () => {
+ process.env.DATA_DIR = TEST_DATA_DIR;
+ await setupAuth();
+});
+
+test.after(() => {
+ if (originalDataDir === undefined) delete process.env.DATA_DIR;
+ else process.env.DATA_DIR = originalDataDir;
+ if (originalJwtSecret === undefined) delete process.env.JWT_SECRET;
+ else process.env.JWT_SECRET = originalJwtSecret;
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+});
+
+// ─── tests ────────────────────────────────────────────────────────────────────
+
+test("POST /api/compression/preview with engineId=headroom returns 200 with token savings", async () => {
+ const messages = buildHeadroomMessages();
+
+ const request = await makeManagementSessionRequest("http://localhost/api/compression/preview", {
+ method: "POST",
+ body: {
+ engineId: "headroom",
+ messages,
+ mode: "stacked",
+ },
+ });
+
+ const response = await previewRoute.POST(request);
+ assert.equal(response.status, 200, `Expected 200, got ${response.status}`);
+
+ const body = (await response.json()) as {
+ originalTokens: number;
+ compressedTokens: number;
+ tokensSaved: number;
+ savingsPct: number;
+ techniquesUsed: string[];
+ original: string;
+ compressed: string;
+ mode: string;
+ };
+
+ assert.ok(
+ typeof body.originalTokens === "number" && body.originalTokens > 0,
+ `originalTokens should be > 0, got: ${body.originalTokens}`
+ );
+ assert.ok(
+ typeof body.compressedTokens === "number",
+ `compressedTokens should be a number, got: ${body.compressedTokens}`
+ );
+ assert.ok(
+ body.compressedTokens < body.originalTokens,
+ `compressedTokens (${body.compressedTokens}) should be < originalTokens (${body.originalTokens}) — headroom should compact a 10-row homogeneous JSON array`
+ );
+ assert.ok(body.tokensSaved > 0, `tokensSaved should be > 0, got: ${body.tokensSaved}`);
+ assert.ok(body.savingsPct > 0, `savingsPct should be > 0, got: ${body.savingsPct}`);
+
+ // Response shape completeness checks
+ assert.ok(typeof body.original === "string", "should have original field");
+ assert.ok(typeof body.compressed === "string", "should have compressed field");
+ assert.ok(Array.isArray(body.techniquesUsed), "techniquesUsed should be an array");
+});
+
+test("POST /api/compression/preview with engineId but no mode still works (mode defaults to stacked)", async () => {
+ const messages = buildHeadroomMessages();
+
+ const request = await makeManagementSessionRequest("http://localhost/api/compression/preview", {
+ method: "POST",
+ body: {
+ engineId: "headroom",
+ messages,
+ // mode intentionally omitted — should default to "stacked" when engineId present
+ },
+ });
+
+ const response = await previewRoute.POST(request);
+ assert.equal(
+ response.status,
+ 200,
+ `Expected 200 when mode omitted with engineId, got ${response.status}`
+ );
+
+ const body = (await response.json()) as { tokensSaved: number };
+ assert.ok(body.tokensSaved > 0, `tokensSaved should be > 0 even without explicit mode`);
+});
+
+test("POST /api/compression/preview without engineId still works (existing path untouched)", async () => {
+ const request = await makeManagementSessionRequest("http://localhost/api/compression/preview", {
+ method: "POST",
+ body: {
+ messages: [{ role: "user", content: "Hello world" }],
+ mode: "standard",
+ },
+ });
+
+ const response = await previewRoute.POST(request);
+ assert.equal(
+ response.status,
+ 200,
+ `Existing path should still return 200, got ${response.status}`
+ );
+
+ const body = (await response.json()) as { original: string; mode: string };
+ assert.ok(typeof body.original === "string", "should have original field");
+ assert.equal(body.mode, "standard", "mode field should reflect the requested mode");
+});
diff --git a/tests/unit/api/context-analytics-engine-route.test.ts b/tests/unit/api/context-analytics-engine-route.test.ts
new file mode 100644
index 00000000000..ef583ddcc2f
--- /dev/null
+++ b/tests/unit/api/context-analytics-engine-route.test.ts
@@ -0,0 +1,138 @@
+/**
+ * TDD: GET /api/context/analytics/engine?engineId=&days=
+ *
+ * Auth + isolation pattern mirrors tests/unit/api/compression-preview-engine.test.ts:
+ * - makeManagementSessionRequest() for JWT cookie auth.
+ * - Temp DATA_DIR, resetDbInstance() before each test, cleanup in test.after().
+ */
+import test from "node:test";
+import assert from "node:assert/strict";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+
+import { makeManagementSessionRequest } from "../../helpers/managementSession.ts";
+
+// ─── isolated temp DB ─────────────────────────────────────────────────────────
+
+const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-ctx-analytics-engine-"));
+const originalDataDir = process.env.DATA_DIR;
+const originalJwtSecret = process.env.JWT_SECRET;
+
+process.env.DATA_DIR = TEST_DATA_DIR;
+
+const core = await import("../../../src/lib/db/core.ts");
+const settingsDb = await import("../../../src/lib/db/settings.ts");
+const { insertCompressionAnalyticsRow } =
+ await import("../../../src/lib/db/compressionAnalytics.ts");
+const engineRoute = await import("../../../src/app/api/context/analytics/engine/route.ts");
+
+// ─── helpers ──────────────────────────────────────────────────────────────────
+
+async function setupAuth(): Promise {
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+ fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
+ await settingsDb.updateSettings({
+ requireLogin: true,
+ setupComplete: true,
+ password: "test-password-hash",
+ });
+}
+
+// ─── lifecycle ────────────────────────────────────────────────────────────────
+
+test.beforeEach(async () => {
+ process.env.DATA_DIR = TEST_DATA_DIR;
+ await setupAuth();
+});
+
+test.after(() => {
+ if (originalDataDir === undefined) delete process.env.DATA_DIR;
+ else process.env.DATA_DIR = originalDataDir;
+ if (originalJwtSecret === undefined) delete process.env.JWT_SECRET;
+ else process.env.JWT_SECRET = originalJwtSecret;
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+});
+
+// ─── tests ────────────────────────────────────────────────────────────────────
+
+test("GET /api/context/analytics/engine returns 400 when engineId is missing", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/context/analytics/engine");
+ const res = await engineRoute.GET(req);
+ assert.equal(res.status, 400, `Expected 400, got ${res.status}`);
+ const body = (await res.json()) as { error: string };
+ assert.ok(typeof body.error === "string", "response should have an error string");
+});
+
+test("GET /api/context/analytics/engine returns 200 with correct aggregation for headroom", async () => {
+ const now = new Date().toISOString();
+
+ insertCompressionAnalyticsRow({
+ timestamp: now,
+ mode: "stacked",
+ engine: "headroom",
+ original_tokens: 1000,
+ compressed_tokens: 800,
+ tokens_saved: 200,
+ });
+ insertCompressionAnalyticsRow({
+ timestamp: now,
+ mode: "stacked",
+ engine: "headroom",
+ original_tokens: 500,
+ compressed_tokens: 350,
+ tokens_saved: 150,
+ });
+ // caveman row — must NOT appear in headroom result
+ insertCompressionAnalyticsRow({
+ timestamp: now,
+ mode: "stacked",
+ engine: "caveman",
+ original_tokens: 800,
+ compressed_tokens: 600,
+ tokens_saved: 200,
+ });
+
+ const req = await makeManagementSessionRequest(
+ "http://localhost/api/context/analytics/engine?engineId=headroom"
+ );
+ const res = await engineRoute.GET(req);
+ assert.equal(res.status, 200, `Expected 200, got ${res.status}`);
+
+ const body = (await res.json()) as {
+ engineId: string;
+ runs: number;
+ tokensSaved: number;
+ avgSavingsPercent: number;
+ days: number;
+ };
+
+ assert.equal(body.engineId, "headroom");
+ assert.equal(body.runs, 2, "should count only the 2 headroom rows");
+ assert.equal(body.tokensSaved, 350, "should sum tokens_saved for headroom rows");
+ assert.equal(typeof body.avgSavingsPercent, "number", "avgSavingsPercent should be a number");
+ assert.equal(body.days, 7, "default days should be 7");
+});
+
+test("GET /api/context/analytics/engine respects ?days= parameter", async () => {
+ const req = await makeManagementSessionRequest(
+ "http://localhost/api/context/analytics/engine?engineId=headroom&days=30"
+ );
+ const res = await engineRoute.GET(req);
+ assert.equal(res.status, 200, `Expected 200, got ${res.status}`);
+ const body = (await res.json()) as { days: number };
+ assert.equal(body.days, 30, "should pass days=30 through to the result");
+});
+
+test("GET /api/context/analytics/engine returns 200 with zero metrics for unknown engine", async () => {
+ const req = await makeManagementSessionRequest(
+ "http://localhost/api/context/analytics/engine?engineId=nonexistent"
+ );
+ const res = await engineRoute.GET(req);
+ assert.equal(res.status, 200, `Expected 200, got ${res.status}`);
+ const body = (await res.json()) as { runs: number; tokensSaved: number };
+ assert.equal(body.runs, 0);
+ assert.equal(body.tokensSaved, 0);
+});
diff --git a/tests/unit/api/context-combos-default-route.test.ts b/tests/unit/api/context-combos-default-route.test.ts
new file mode 100644
index 00000000000..f97605e1515
--- /dev/null
+++ b/tests/unit/api/context-combos-default-route.test.ts
@@ -0,0 +1,109 @@
+/**
+ * TDD: GET /api/context/combos/default and PUT /api/context/combos/default
+ *
+ * Auth + isolation pattern mirrors tests/unit/api/context-analytics-engine-route.test.ts:
+ * - makeManagementSessionRequest() for JWT cookie auth.
+ * - Temp DATA_DIR, resetDbInstance() before each test, cleanup in test.after().
+ */
+import test from "node:test";
+import assert from "node:assert/strict";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+
+import { makeManagementSessionRequest } from "../../helpers/managementSession.ts";
+
+// ─── isolated temp DB ─────────────────────────────────────────────────────────
+
+const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-ctx-combos-default-route-"));
+const originalDataDir = process.env.DATA_DIR;
+const originalJwtSecret = process.env.JWT_SECRET;
+
+process.env.DATA_DIR = TEST_DATA_DIR;
+
+const core = await import("../../../src/lib/db/core.ts");
+const settingsDb = await import("../../../src/lib/db/settings.ts");
+const defaultRoute = await import("../../../src/app/api/context/combos/default/route.ts");
+
+// ─── helpers ──────────────────────────────────────────────────────────────────
+
+async function setupAuth(): Promise {
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+ fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
+ await settingsDb.updateSettings({
+ requireLogin: true,
+ setupComplete: true,
+ password: "test-password-hash",
+ });
+}
+
+// ─── lifecycle ────────────────────────────────────────────────────────────────
+
+test.beforeEach(async () => {
+ process.env.DATA_DIR = TEST_DATA_DIR;
+ await setupAuth();
+});
+
+test.after(() => {
+ if (originalDataDir === undefined) delete process.env.DATA_DIR;
+ else process.env.DATA_DIR = originalDataDir;
+ if (originalJwtSecret === undefined) delete process.env.JWT_SECRET;
+ else process.env.JWT_SECRET = originalJwtSecret;
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+});
+
+// ─── tests ────────────────────────────────────────────────────────────────────
+
+test("GET /api/context/combos/default returns the default combo", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/context/combos/default");
+ const res = await defaultRoute.GET(req);
+ assert.equal(res.status, 200, `Expected 200, got ${res.status}`);
+
+ const body = (await res.json()) as {
+ id: string;
+ isDefault: boolean;
+ pipeline: Array<{ engine: string }>;
+ };
+ assert.equal(typeof body.id, "string", "response should have an id string");
+ assert.equal(body.isDefault, true, "returned combo should be the default");
+ assert.ok(Array.isArray(body.pipeline), "pipeline should be an array");
+});
+
+test("PUT /api/context/combos/default enabling headroom returns combo with headroom in pipeline", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/context/combos/default", {
+ method: "PUT",
+ body: JSON.stringify({ engineId: "headroom", enabled: true }),
+ });
+ const res = await defaultRoute.PUT(req);
+ assert.equal(res.status, 200, `Expected 200, got ${res.status}`);
+
+ const body = (await res.json()) as {
+ pipeline: Array<{ engine: string }>;
+ };
+ assert.ok(Array.isArray(body.pipeline), "pipeline should be an array");
+ const engineIds = body.pipeline.map((s) => s.engine);
+ assert.ok(engineIds.includes("headroom"), `expected headroom in pipeline, got: ${engineIds}`);
+});
+
+test("PUT /api/context/combos/default with bad input returns 400", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/context/combos/default", {
+ method: "PUT",
+ // Missing required 'enabled' field
+ body: JSON.stringify({ engineId: "headroom" }),
+ });
+ const res = await defaultRoute.PUT(req);
+ assert.equal(res.status, 400, `Expected 400 for missing 'enabled', got ${res.status}`);
+ const body = (await res.json()) as { error: unknown };
+ assert.ok(body.error !== undefined, "response should have an error field");
+});
+
+test("PUT /api/context/combos/default with invalid JSON body returns 400", async () => {
+ const req = await makeManagementSessionRequest("http://localhost/api/context/combos/default", {
+ method: "PUT",
+ body: "not-json",
+ });
+ const res = await defaultRoute.PUT(req);
+ assert.equal(res.status, 400, `Expected 400 for invalid JSON, got ${res.status}`);
+});
diff --git a/tests/unit/db/default-combo-toggle.test.ts b/tests/unit/db/default-combo-toggle.test.ts
new file mode 100644
index 00000000000..62434b9cd6c
--- /dev/null
+++ b/tests/unit/db/default-combo-toggle.test.ts
@@ -0,0 +1,192 @@
+/**
+ * TDD: setEngineInDefaultCombo + normalizePipeline (new engines).
+ *
+ * DB isolation pattern mirrors tests/unit/db/per-engine-analytics.test.ts:
+ * - Temp DATA_DIR, resetDbInstance() before each test, cleanup in test.after().
+ */
+import test from "node:test";
+import assert from "node:assert/strict";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+
+// ─── isolated temp DB ─────────────────────────────────────────────────────────
+
+const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-default-combo-toggle-"));
+const originalDataDir = process.env.DATA_DIR;
+
+process.env.DATA_DIR = TEST_DATA_DIR;
+
+const core = await import("../../../src/lib/db/core.ts");
+core.resetDbInstance();
+
+const { getDefaultCompressionCombo, setEngineInDefaultCombo, getCompressionCombo } =
+ await import("../../../src/lib/db/compressionCombos.ts");
+
+// ─── helpers ──────────────────────────────────────────────────────────────────
+
+function resetDb(): void {
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+ fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
+}
+
+// ─── lifecycle ────────────────────────────────────────────────────────────────
+
+test.beforeEach(() => {
+ resetDb();
+});
+
+test.after(() => {
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+ if (originalDataDir === undefined) delete process.env.DATA_DIR;
+ else process.env.DATA_DIR = originalDataDir;
+});
+
+// ─── tests ────────────────────────────────────────────────────────────────────
+
+test("Fix #1: normalizePipeline passes through new engine IDs (headroom, session-dedup, ccr, llmlingua)", () => {
+ // The default combo is seeded with [rtk, caveman]. Directly update the DB
+ // to include new engine IDs, then read back via getDefaultCompressionCombo
+ // to verify normalizePipeline no longer strips them.
+ const db = core.getDbInstance();
+
+ // Ensure the table is created by triggering a read first.
+ const combo = getDefaultCompressionCombo();
+ assert.ok(combo, "default combo should exist after table init");
+
+ const newPipeline = JSON.stringify([
+ { engine: "session-dedup" },
+ { engine: "ccr" },
+ { engine: "headroom" },
+ { engine: "caveman", intensity: "full" },
+ { engine: "llmlingua" },
+ ]);
+ db.prepare("UPDATE compression_combos SET pipeline = ? WHERE id = ?").run(newPipeline, combo.id);
+
+ const reloaded = getCompressionCombo(combo.id);
+ assert.ok(reloaded, "should reload the combo");
+ const engineIds = reloaded.pipeline.map((s) => s.engine);
+ assert.ok(engineIds.includes("headroom"), `expected headroom in pipeline, got: ${engineIds}`);
+ assert.ok(
+ engineIds.includes("session-dedup"),
+ `expected session-dedup in pipeline, got: ${engineIds}`
+ );
+ assert.ok(engineIds.includes("ccr"), `expected ccr in pipeline, got: ${engineIds}`);
+ assert.ok(engineIds.includes("llmlingua"), `expected llmlingua in pipeline, got: ${engineIds}`);
+ assert.equal(
+ reloaded.pipeline.length,
+ 5,
+ `expected 5 steps, got ${reloaded.pipeline.length}: ${engineIds}`
+ );
+});
+
+test("enabling headroom adds it to the pipeline sorted by stackPriority", () => {
+ // Default pipeline is [rtk(10), caveman(20)].
+ // headroom has stackPriority=15 so it should be inserted between rtk and caveman.
+ const result = setEngineInDefaultCombo("headroom", true);
+ assert.ok(result, "should return the updated combo");
+
+ const engineIds = result.pipeline.map((s) => s.engine);
+ assert.ok(engineIds.includes("headroom"), "headroom should be in the pipeline");
+
+ const rtkIdx = engineIds.indexOf("rtk");
+ const headroomIdx = engineIds.indexOf("headroom");
+ const cavemanIdx = engineIds.indexOf("caveman");
+
+ assert.ok(rtkIdx >= 0, "rtk should be in the pipeline");
+ assert.ok(headroomIdx >= 0, "headroom should be in the pipeline");
+ assert.ok(cavemanIdx >= 0, "caveman should be in the pipeline");
+
+ assert.ok(
+ rtkIdx < headroomIdx,
+ `rtk(10) should come before headroom(15), got order: ${engineIds}`
+ );
+ assert.ok(
+ headroomIdx < cavemanIdx,
+ `headroom(15) should come before caveman(20), got order: ${engineIds}`
+ );
+});
+
+test("enabling an engine with config persists the config", () => {
+ const customConfig = { minRows: 5 };
+ const result = setEngineInDefaultCombo("headroom", true, customConfig);
+ assert.ok(result, "should return the updated combo");
+
+ const headroomStep = result.pipeline.find((s) => s.engine === "headroom");
+ assert.ok(headroomStep, "headroom step should be present");
+ assert.deepEqual(
+ headroomStep.config,
+ customConfig,
+ "config should be persisted on the pipeline step"
+ );
+});
+
+test("updating config on an already-present engine merges correctly", () => {
+ // First enable headroom
+ setEngineInDefaultCombo("headroom", true);
+ // Then re-enable with a config — should update the existing step, not add a duplicate
+ const result = setEngineInDefaultCombo("headroom", true, { minRows: 8 });
+ assert.ok(result, "should return the updated combo");
+
+ const headroomSteps = result.pipeline.filter((s) => s.engine === "headroom");
+ assert.equal(headroomSteps.length, 1, "should not duplicate the headroom step");
+ assert.deepEqual(headroomSteps[0].config, { minRows: 8 });
+});
+
+test("disabling an engine removes it from the pipeline", () => {
+ setEngineInDefaultCombo("headroom", true);
+ const before = getDefaultCompressionCombo();
+ assert.ok(
+ before?.pipeline.some((s) => s.engine === "headroom"),
+ "headroom should be in pipeline before disabling"
+ );
+
+ const result = setEngineInDefaultCombo("headroom", false);
+ assert.ok(result, "should return the updated combo");
+ assert.ok(
+ !result.pipeline.some((s) => s.engine === "headroom"),
+ "headroom should be removed from pipeline"
+ );
+});
+
+test("Fix #8: setEngineInDefaultCombo with unknown engineId returns null and does not modify the pipeline", () => {
+ const before = getDefaultCompressionCombo();
+ assert.ok(before, "default combo must exist");
+ const originalPipeline = JSON.stringify(before.pipeline);
+
+ const result = setEngineInDefaultCombo("not-a-real-engine", true);
+ assert.equal(result, null, "should return null for unknown engine id");
+
+ // The combo must be unchanged
+ const after = getDefaultCompressionCombo();
+ assert.ok(after, "default combo should still exist");
+ assert.equal(
+ JSON.stringify(after.pipeline),
+ originalPipeline,
+ "pipeline should be unmodified when unknown engineId is rejected"
+ );
+});
+
+test("Fix #2: disabling last engine produces an empty pipeline (not silently reverted to default)", () => {
+ // Start with a pipeline that only has one engine by disabling everything except headroom.
+ // First set a pipeline with only one known engine via a raw DB update.
+ const db = core.getDbInstance();
+ const combo = getDefaultCompressionCombo();
+ assert.ok(combo, "default combo must exist");
+
+ db.prepare("UPDATE compression_combos SET pipeline = ? WHERE id = ?").run(
+ JSON.stringify([{ engine: "headroom" }]),
+ combo.id
+ );
+
+ // Now disable headroom — result should be empty pipeline, not a fallback.
+ const result = setEngineInDefaultCombo("headroom", false);
+ assert.ok(result, "should return the updated combo");
+ assert.equal(
+ result.pipeline.length,
+ 0,
+ `expected empty pipeline after disabling last engine, got: ${JSON.stringify(result.pipeline)}`
+ );
+});
diff --git a/tests/unit/db/per-engine-analytics.test.ts b/tests/unit/db/per-engine-analytics.test.ts
new file mode 100644
index 00000000000..018e14e1f29
--- /dev/null
+++ b/tests/unit/db/per-engine-analytics.test.ts
@@ -0,0 +1,151 @@
+/**
+ * TDD: getPerEngineAnalytics — per-engine aggregation from compression_analytics.
+ *
+ * DB isolation pattern mirrors tests/unit/db/api-keys.test.ts:
+ * - Temp DATA_DIR, resetDbInstance() before/after, close in test.after().
+ */
+import test from "node:test";
+import assert from "node:assert/strict";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+
+// ─── isolated temp DB ─────────────────────────────────────────────────────────
+
+const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-per-engine-analytics-"));
+const originalDataDir = process.env.DATA_DIR;
+
+process.env.DATA_DIR = TEST_DATA_DIR;
+
+const core = await import("../../../src/lib/db/core.ts");
+core.resetDbInstance();
+
+const { insertCompressionAnalyticsRow, getPerEngineAnalytics } =
+ await import("../../../src/lib/db/compressionAnalytics.ts");
+
+// ─── helpers ──────────────────────────────────────────────────────────────────
+
+function resetDb(): void {
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+ fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
+}
+
+// ─── lifecycle ────────────────────────────────────────────────────────────────
+
+test.beforeEach(() => {
+ resetDb();
+});
+
+test.after(() => {
+ core.resetDbInstance();
+ fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
+ if (originalDataDir === undefined) delete process.env.DATA_DIR;
+ else process.env.DATA_DIR = originalDataDir;
+});
+
+// ─── tests ────────────────────────────────────────────────────────────────────
+
+test("getPerEngineAnalytics returns runs=0 and zeroed metrics for unknown engine", () => {
+ const result = getPerEngineAnalytics("unknown-engine");
+ assert.equal(result.engineId, "unknown-engine");
+ assert.equal(result.runs, 0);
+ assert.equal(result.tokensSaved, 0);
+ assert.equal(result.avgSavingsPercent, 0);
+ assert.equal(result.days, 7);
+});
+
+test("getPerEngineAnalytics returns correct aggregation for headroom rows only", () => {
+ const now = new Date().toISOString();
+
+ // 2 headroom rows: original=1000/compressed=800/saved=200 and original=500/compressed=350/saved=150
+ insertCompressionAnalyticsRow({
+ timestamp: now,
+ mode: "stacked",
+ engine: "headroom",
+ original_tokens: 1000,
+ compressed_tokens: 800,
+ tokens_saved: 200,
+ });
+ insertCompressionAnalyticsRow({
+ timestamp: now,
+ mode: "stacked",
+ engine: "headroom",
+ original_tokens: 500,
+ compressed_tokens: 350,
+ tokens_saved: 150,
+ });
+
+ // 1 caveman row that must NOT appear in headroom results
+ insertCompressionAnalyticsRow({
+ timestamp: now,
+ mode: "stacked",
+ engine: "caveman",
+ original_tokens: 800,
+ compressed_tokens: 600,
+ tokens_saved: 200,
+ });
+
+ const result = getPerEngineAnalytics("headroom");
+
+ assert.equal(result.engineId, "headroom");
+ assert.equal(result.runs, 2, "should count only the 2 headroom rows");
+ assert.equal(result.tokensSaved, 350, "should sum tokens_saved for headroom rows");
+
+ // avgSavingsPercent = round(((1500 - 1150) / 1500) * 1000) / 10
+ // = round((350/1500) * 1000) / 10
+ // = round(233.33...) / 10
+ // = 233 / 10 = 23.3
+ assert.equal(result.avgSavingsPercent, 23.3, `expected 23.3%, got ${result.avgSavingsPercent}`);
+ assert.equal(result.days, 7);
+});
+
+test("getPerEngineAnalytics excludes rows outside the days window", () => {
+ const recent = new Date().toISOString();
+ // 30 days ago — outside the default 7-day window
+ const old = new Date(Date.now() - 30 * 86400_000).toISOString();
+
+ insertCompressionAnalyticsRow({
+ timestamp: recent,
+ mode: "stacked",
+ engine: "headroom",
+ original_tokens: 1000,
+ compressed_tokens: 800,
+ tokens_saved: 200,
+ });
+ insertCompressionAnalyticsRow({
+ timestamp: old,
+ mode: "stacked",
+ engine: "headroom",
+ original_tokens: 2000,
+ compressed_tokens: 1000,
+ tokens_saved: 1000,
+ });
+
+ const result = getPerEngineAnalytics("headroom", 7);
+ assert.equal(result.runs, 1, "should only count the recent row within 7 days");
+ assert.equal(result.tokensSaved, 200);
+});
+
+test("getPerEngineAnalytics falls back to mode column when engine is null (COALESCE behaviour)", () => {
+ const now = new Date().toISOString();
+
+ // Insert a row where engine is explicitly omitted — insertCompressionAnalyticsRow
+ // writes engine = row.engine ?? row.mode, so we cannot get NULL via the helper.
+ // Instead, insert raw via the DB to simulate a pre-engine row.
+ const db = core.getDbInstance();
+ db.prepare(
+ `INSERT INTO compression_analytics (timestamp, mode, engine, original_tokens, compressed_tokens, tokens_saved)
+ VALUES (?, ?, NULL, ?, ?, ?)`
+ ).run(now, "caveman", 600, 400, 200);
+
+ // COALESCE(engine, mode) = 'caveman' for the row above
+ const result = getPerEngineAnalytics("caveman");
+ assert.equal(result.runs, 1, "COALESCE fallback should match engine=NULL, mode=caveman");
+ assert.equal(result.tokensSaved, 200);
+});
+
+test("getPerEngineAnalytics accepts custom days parameter", () => {
+ const result = getPerEngineAnalytics("headroom", 30);
+ assert.equal(result.days, 30);
+});
diff --git a/tests/unit/runtime/live-ws-default.test.ts b/tests/unit/runtime/live-ws-default.test.ts
new file mode 100644
index 00000000000..961af6e9b4f
--- /dev/null
+++ b/tests/unit/runtime/live-ws-default.test.ts
@@ -0,0 +1,59 @@
+import { test, describe, after } from "node:test";
+import assert from "node:assert/strict";
+
+// NOTE: importing liveServer.ts triggers the module-level auto-start guard at
+// the bottom of the file. isBuildOrTest() short-circuits it because:
+// - process.argv for the Node test runner includes "--test"
+// - process.env.NODE_ENV is typically "test" in this suite
+// Either condition is sufficient — no socket is opened.
+
+const originalEnv = process.env.OMNIROUTE_ENABLE_LIVE_WS;
+
+after(() => {
+ // Restore original env so other tests in the same process are unaffected.
+ if (originalEnv === undefined) {
+ delete process.env.OMNIROUTE_ENABLE_LIVE_WS;
+ } else {
+ process.env.OMNIROUTE_ENABLE_LIVE_WS = originalEnv;
+ }
+});
+
+// Dynamically import so we can verify the named export exists.
+const { isLiveWsEnabled } = await import("../../../src/server/ws/liveServer.ts");
+
+describe("isLiveWsEnabled — default-ON behavior", () => {
+ test("unset → true (default ON)", () => {
+ delete process.env.OMNIROUTE_ENABLE_LIVE_WS;
+ assert.equal(isLiveWsEnabled(), true);
+ });
+
+ test('"0" → false', () => {
+ process.env.OMNIROUTE_ENABLE_LIVE_WS = "0";
+ assert.equal(isLiveWsEnabled(), false);
+ });
+
+ test('"false" → false', () => {
+ process.env.OMNIROUTE_ENABLE_LIVE_WS = "false";
+ assert.equal(isLiveWsEnabled(), false);
+ });
+
+ test('"FALSE" → false (case-insensitive)', () => {
+ process.env.OMNIROUTE_ENABLE_LIVE_WS = "FALSE";
+ assert.equal(isLiveWsEnabled(), false);
+ });
+
+ test('"1" → true', () => {
+ process.env.OMNIROUTE_ENABLE_LIVE_WS = "1";
+ assert.equal(isLiveWsEnabled(), true);
+ });
+
+ test('"true" → true', () => {
+ process.env.OMNIROUTE_ENABLE_LIVE_WS = "true";
+ assert.equal(isLiveWsEnabled(), true);
+ });
+
+ test('"TRUE" → true (case-insensitive)', () => {
+ process.env.OMNIROUTE_ENABLE_LIVE_WS = "TRUE";
+ assert.equal(isLiveWsEnabled(), true);
+ });
+});
diff --git a/tests/unit/sidebar-visibility.test.ts b/tests/unit/sidebar-visibility.test.ts
index 5b04a3dc1cf..2b5b57566e6 100644
--- a/tests/unit/sidebar-visibility.test.ts
+++ b/tests/unit/sidebar-visibility.test.ts
@@ -42,11 +42,21 @@ test("primary sidebar items place limits after cache", () => {
"providers",
"embedded-services",
"combos",
+ "combos-live",
"quota",
"costs-quota-share",
+ "context-settings",
"context-caveman",
"context-rtk",
+ "context-headroom",
+ "context-session-dedup",
+ "context-ccr",
+ "context-llmlingua",
+ "context-lite",
+ "context-aggressive",
+ "context-ultra",
"context-combos",
+ "compression-studio",
"cli-code",
"cli-agents",
"acp-agents",
@@ -70,8 +80,16 @@ test("context sidebar section sits between primary and cli", () => {
.filter((item) => item.id.startsWith("context-"))
.map((item) => ({ id: item.id, href: item.href })),
[
+ { id: "context-settings", href: "/dashboard/context/settings" },
{ id: "context-caveman", href: "/dashboard/context/caveman" },
{ id: "context-rtk", href: "/dashboard/context/rtk" },
+ { id: "context-headroom", href: "/dashboard/context/headroom" },
+ { id: "context-session-dedup", href: "/dashboard/context/session-dedup" },
+ { id: "context-ccr", href: "/dashboard/context/ccr" },
+ { id: "context-llmlingua", href: "/dashboard/context/llmlingua" },
+ { id: "context-lite", href: "/dashboard/context/lite" },
+ { id: "context-aggressive", href: "/dashboard/context/aggressive" },
+ { id: "context-ultra", href: "/dashboard/context/ultra" },
{ id: "context-combos", href: "/dashboard/context/combos" },
]
);
diff --git a/tests/unit/ui/compression-settings-page.test.tsx b/tests/unit/ui/compression-settings-page.test.tsx
new file mode 100644
index 00000000000..277b3867476
--- /dev/null
+++ b/tests/unit/ui/compression-settings-page.test.tsx
@@ -0,0 +1,125 @@
+// @vitest-environment jsdom
+import React, { act } from "react";
+import { createRoot } from "react-dom/client";
+import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
+
+// ── Mock next-intl (CompressionSettingsTab calls useTranslations) ──────────
+vi.mock("next-intl", () => ({
+ useTranslations: () => (key: string) => key,
+}));
+
+// ── Helpers ───────────────────────────────────────────────────────────────
+
+const containers: HTMLElement[] = [];
+const roots: Array<{ unmount: () => void }> = [];
+
+function mountInContainer(ui: React.ReactElement): HTMLElement {
+ const container = document.createElement("div");
+ document.body.appendChild(container);
+ containers.push(container);
+ const root = createRoot(container);
+ roots.push(root);
+ act(() => {
+ root.render(ui);
+ });
+ return container;
+}
+
+beforeEach(() => {
+ (
+ globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
+ ).IS_REACT_ACT_ENVIRONMENT = true;
+});
+
+afterEach(async () => {
+ vi.restoreAllMocks();
+ await act(async () => {
+ while (roots.length > 0) {
+ roots.pop()?.unmount();
+ }
+ });
+ for (let i = 0; i < 10; i++) {
+ await Promise.resolve();
+ }
+ while (containers.length > 0) {
+ containers.pop()?.remove();
+ }
+ document.body.innerHTML = "";
+});
+
+// ── Mock fetch ────────────────────────────────────────────────────────────
+// CompressionSettingsTab fetches:
+// GET /api/settings/compression → returns CompressionConfig (or null)
+// GET /api/compression/rules → returns { rules: RuleMetadata[] }
+
+function setupFetchMock() {
+ vi.spyOn(globalThis, "fetch").mockImplementation(async (input: RequestInfo | URL) => {
+ const url = input.toString();
+ if (url.includes("/api/settings/compression")) {
+ return new Response(
+ JSON.stringify({
+ enabled: false,
+ defaultMode: "off",
+ autoTriggerTokens: 0,
+ cacheMinutes: 5,
+ preserveSystemPrompt: true,
+ comboOverrides: {},
+ }),
+ {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ }
+ );
+ }
+ if (url.includes("/api/compression/rules")) {
+ return new Response(JSON.stringify({ rules: [] }), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ return new Response(JSON.stringify({}), { status: 404 });
+ });
+}
+
+// ── Tests ─────────────────────────────────────────────────────────────────
+
+describe("CompressionSettingsPage", () => {
+ it("mounts without throwing and renders content", { timeout: 15000 }, async () => {
+ setupFetchMock();
+ const { default: CompressionSettingsPage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/settings/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ // Flush any pending microtasks from effects
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container).toBeTruthy();
+ expect(container.children.length).toBeGreaterThan(0);
+ });
+
+ it("does not crash when fetch calls fail (fail-soft)", async () => {
+ vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("Network error"));
+
+ const { default: CompressionSettingsPage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/settings/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // Component should still be mounted (not crashed)
+ expect(container).toBeTruthy();
+ expect(container.parentNode).toBeTruthy();
+ });
+});
diff --git a/tests/unit/ui/compressionHub.test.tsx b/tests/unit/ui/compressionHub.test.tsx
new file mode 100644
index 00000000000..c62ac2216d1
--- /dev/null
+++ b/tests/unit/ui/compressionHub.test.tsx
@@ -0,0 +1,171 @@
+// @vitest-environment jsdom
+import React, { act } from "react";
+import { createRoot } from "react-dom/client";
+import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
+
+// ── Helpers ───────────────────────────────────────────────────────────────
+
+const containers: HTMLElement[] = [];
+const roots: Array<{ unmount: () => void }> = [];
+
+function mountInContainer(ui: React.ReactElement): HTMLElement {
+ const container = document.createElement("div");
+ document.body.appendChild(container);
+ containers.push(container);
+ const root = createRoot(container);
+ roots.push(root);
+ act(() => {
+ root.render(ui);
+ });
+ return container;
+}
+
+beforeEach(() => {
+ (
+ globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
+ ).IS_REACT_ACT_ENVIRONMENT = true;
+});
+
+afterEach(async () => {
+ vi.restoreAllMocks();
+ await act(async () => {
+ while (roots.length > 0) {
+ roots.pop()?.unmount();
+ }
+ });
+ for (let i = 0; i < 10; i++) {
+ await Promise.resolve();
+ }
+ while (containers.length > 0) {
+ containers.pop()?.remove();
+ }
+ document.body.innerHTML = "";
+});
+
+// ── Mock fetch ────────────────────────────────────────────────────────────
+
+const ENGINES = [
+ { id: "session-dedup", name: "Session Dedup", stackPriority: 3, stable: true },
+ { id: "rtk", name: "RTK", stackPriority: 10, stable: true },
+ { id: "caveman", name: "Caveman", stackPriority: 20, stable: true },
+ { id: "llmlingua", name: "LLMLingua-2", stackPriority: 35, stable: false },
+];
+
+function enginePayload() {
+ return {
+ engines: ENGINES.map((e) => ({
+ id: e.id,
+ name: e.name,
+ description: `${e.name} description`,
+ icon: "compress",
+ stackable: true,
+ stackPriority: e.stackPriority,
+ metadata: { stable: e.stable },
+ configSchema: [],
+ })),
+ };
+}
+
+function setupFetchMock(opts: {
+ enabled?: boolean;
+ mode?: string;
+ pipeline?: Array<{ engine: string }>;
+}) {
+ const { enabled = true, mode = "stacked", pipeline = [{ engine: "rtk" }] } = opts;
+ const json = (body: unknown, status = 200) =>
+ new Response(JSON.stringify(body), {
+ status,
+ headers: { "Content-Type": "application/json" },
+ });
+
+ vi.spyOn(globalThis, "fetch").mockImplementation(async (input: RequestInfo | URL) => {
+ const url = input.toString();
+ if (url.includes("/api/settings/compression")) {
+ return json({ enabled, defaultMode: mode });
+ }
+ if (url.includes("/api/compression/engines")) {
+ return json(enginePayload());
+ }
+ if (url.includes("/api/context/combos/default")) {
+ return json({ id: "default-caveman", name: "Standard Savings", pipeline });
+ }
+ if (url.includes("/api/context/combos")) {
+ return json({ combos: [] });
+ }
+ if (url.includes("/api/combos")) {
+ return json({ combos: [] });
+ }
+ if (url.includes("/api/compression/language-packs")) {
+ return json({ packs: [] });
+ }
+ return json({}, 404);
+ });
+}
+
+async function flush() {
+ await act(async () => {
+ for (let i = 0; i < 10; i++) await Promise.resolve();
+ });
+}
+
+// ── Tests ─────────────────────────────────────────────────────────────────
+
+describe("CompressionHub", () => {
+ it("renders the master switch, mode selector, and the layered pipeline", async () => {
+ setupFetchMock({ enabled: true, mode: "stacked", pipeline: [{ engine: "rtk" }] });
+ const { default: CompressionHub } =
+ await import("../../../src/app/(dashboard)/dashboard/context/combos/CompressionHub");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+ await flush();
+
+ const text = container.textContent ?? "";
+ expect(text).toContain("Compression Hub");
+ expect(text).toContain("Token Saver");
+ expect(text).toContain("Stacked");
+ // Active pipeline engine (from the default combo) renders
+ expect(text).toContain("RTK");
+ // Inactive engines from the catalog render too
+ expect(text).toContain("Caveman");
+ // Active-pipeline callout shows when enabled && stacked
+ expect(text).toContain("Pipeline de camadas ativo");
+ });
+
+ it("shows the activation warning when Token Saver is off", async () => {
+ setupFetchMock({ enabled: false, mode: "off", pipeline: [] });
+ const { default: CompressionHub } =
+ await import("../../../src/app/(dashboard)/dashboard/context/combos/CompressionHub");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+ await flush();
+
+ const text = container.textContent ?? "";
+ expect(text).toContain("Ligar Token Saver");
+ expect(text).toContain("só rodam no modo Stacked");
+ });
+});
+
+describe("CompressionCombosPageClient", () => {
+ it("renders the Hub on top and the named-combos manager below", async () => {
+ setupFetchMock({ enabled: true, mode: "stacked", pipeline: [{ engine: "rtk" }] });
+ const { default: CompressionCombosPageClient } = await import(
+ "../../../src/app/(dashboard)/dashboard/context/combos/CompressionCombosPageClient"
+ );
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+ await flush();
+
+ const text = container.textContent ?? "";
+ expect(text).toContain("Compression Hub");
+ expect(text).toContain("Combos nomeados");
+ });
+});
diff --git a/tests/unit/ui/engine-pages.test.tsx b/tests/unit/ui/engine-pages.test.tsx
new file mode 100644
index 00000000000..bdac0f52573
--- /dev/null
+++ b/tests/unit/ui/engine-pages.test.tsx
@@ -0,0 +1,234 @@
+// @vitest-environment jsdom
+import React, { act } from "react";
+import { createRoot } from "react-dom/client";
+import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
+
+// ── Helpers ───────────────────────────────────────────────────────────────
+
+const containers: HTMLElement[] = [];
+const roots: Array<{ unmount: () => void }> = [];
+
+function mountInContainer(ui: React.ReactElement): HTMLElement {
+ const container = document.createElement("div");
+ document.body.appendChild(container);
+ containers.push(container);
+ const root = createRoot(container);
+ roots.push(root);
+ act(() => {
+ root.render(ui);
+ });
+ return container;
+}
+
+beforeEach(() => {
+ (
+ globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
+ ).IS_REACT_ACT_ENVIRONMENT = true;
+});
+
+afterEach(async () => {
+ vi.restoreAllMocks();
+ await act(async () => {
+ while (roots.length > 0) {
+ roots.pop()?.unmount();
+ }
+ });
+ for (let i = 0; i < 10; i++) {
+ await Promise.resolve();
+ }
+ while (containers.length > 0) {
+ containers.pop()?.remove();
+ }
+ document.body.innerHTML = "";
+});
+
+// ── Mock fetch ────────────────────────────────────────────────────────────
+
+function makeEnginePayload(engines: Array<{ id: string; name: string }>) {
+ return {
+ engines: engines.map(({ id, name }) => ({
+ id,
+ name,
+ description: "",
+ icon: "table_rows",
+ stackable: true,
+ stackPriority: 15,
+ metadata: {},
+ configSchema: [],
+ })),
+ };
+}
+
+const ANALYTICS_PAYLOAD = {
+ runs: 0,
+ tokensSaved: 0,
+ avgSavingsPercent: 0,
+ days: 7,
+};
+
+function setupFetchMock(engines: Array<{ id: string; name: string }>) {
+ vi.spyOn(globalThis, "fetch").mockImplementation(async (input: RequestInfo | URL) => {
+ const url = input.toString();
+ if (url.includes("/api/compression/engines")) {
+ return new Response(JSON.stringify(makeEnginePayload(engines)), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ if (url.includes("/api/context/combos")) {
+ return new Response(JSON.stringify({ pipeline: [] }), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ if (url.includes("/api/context/analytics/engine")) {
+ return new Response(JSON.stringify(ANALYTICS_PAYLOAD), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ return new Response(JSON.stringify({}), { status: 404 });
+ });
+}
+
+// ── Tests ─────────────────────────────────────────────────────────────────
+
+describe("HeadroomPage", () => {
+ it("mounts without throwing and renders the engine name", async () => {
+ setupFetchMock([{ id: "headroom", name: "Headroom" }]);
+ const { default: HeadroomPage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/headroom/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container).toBeTruthy();
+ expect(container.textContent).toContain("Headroom");
+ });
+});
+
+describe("SessionDedupPage", () => {
+ it("mounts without throwing and renders the engine name", async () => {
+ setupFetchMock([{ id: "session-dedup", name: "Session Dedup" }]);
+ const { default: SessionDedupPage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/session-dedup/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container).toBeTruthy();
+ expect(container.textContent).toContain("Session Dedup");
+ });
+});
+
+describe("CcrPage", () => {
+ it("mounts without throwing and renders the engine name", async () => {
+ setupFetchMock([{ id: "ccr", name: "CCR" }]);
+ const { default: CcrPage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/ccr/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container).toBeTruthy();
+ expect(container.textContent).toContain("CCR");
+ });
+});
+
+describe("LlmlinguaPage", () => {
+ it("mounts without throwing and renders the engine name", async () => {
+ setupFetchMock([{ id: "llmlingua", name: "LLMLingua" }]);
+ const { default: LlmlinguaPage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/llmlingua/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container).toBeTruthy();
+ expect(container.textContent).toContain("LLMLingua");
+ });
+});
+
+describe("LitePage", () => {
+ it("mounts without throwing and renders the engine name", async () => {
+ setupFetchMock([{ id: "lite", name: "Lite" }]);
+ const { default: LitePage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/lite/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container).toBeTruthy();
+ expect(container.textContent).toContain("Lite");
+ });
+});
+
+describe("AggressivePage", () => {
+ it("mounts without throwing and renders the engine name", async () => {
+ setupFetchMock([{ id: "aggressive", name: "Aggressive" }]);
+ const { default: AggressivePage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/aggressive/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container).toBeTruthy();
+ expect(container.textContent).toContain("Aggressive");
+ });
+});
+
+describe("UltraPage", () => {
+ it("mounts without throwing and renders the engine name", async () => {
+ setupFetchMock([{ id: "ultra", name: "Ultra" }]);
+ const { default: UltraPage } =
+ await import("../../../src/app/(dashboard)/dashboard/context/ultra/page");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container).toBeTruthy();
+ expect(container.textContent).toContain("Ultra");
+ });
+});
diff --git a/tests/unit/ui/engineConfigForm.test.tsx b/tests/unit/ui/engineConfigForm.test.tsx
new file mode 100644
index 00000000000..e7503c6bbfa
--- /dev/null
+++ b/tests/unit/ui/engineConfigForm.test.tsx
@@ -0,0 +1,249 @@
+// @vitest-environment jsdom
+import React, { act } from "react";
+import { createRoot } from "react-dom/client";
+import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
+import type { EngineConfigField } from "@omniroute/open-sse/services/compression/engines/types";
+
+const containers: HTMLElement[] = [];
+const roots: Array<{ unmount: () => void }> = [];
+
+function mount(ui: React.ReactElement): HTMLElement {
+ const container = document.createElement("div");
+ document.body.appendChild(container);
+ containers.push(container);
+ const root = createRoot(container);
+ roots.push(root);
+ act(() => {
+ root.render(ui);
+ });
+ return container;
+}
+
+beforeEach(() => {
+ (
+ globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
+ ).IS_REACT_ACT_ENVIRONMENT = true;
+});
+
+afterEach(async () => {
+ vi.clearAllMocks();
+ await act(async () => {
+ while (roots.length > 0) {
+ roots.pop()?.unmount();
+ }
+ });
+ for (let i = 0; i < 10; i++) {
+ await Promise.resolve();
+ }
+ while (containers.length > 0) {
+ containers.pop()?.remove();
+ }
+ document.body.innerHTML = "";
+});
+
+const SCHEMA: EngineConfigField[] = [
+ {
+ key: "enabled",
+ type: "boolean",
+ label: "Enabled",
+ description: "Enable this engine",
+ defaultValue: false,
+ },
+ {
+ key: "maxTokens",
+ type: "number",
+ label: "Max Tokens",
+ defaultValue: 1000,
+ min: 100,
+ max: 8000,
+ },
+ {
+ key: "prefix",
+ type: "string",
+ label: "Prefix",
+ defaultValue: "",
+ },
+ {
+ key: "strategy",
+ type: "select",
+ label: "Strategy",
+ defaultValue: "fast",
+ options: [
+ { value: "fast", label: "Fast" },
+ { value: "thorough", label: "Thorough" },
+ ],
+ },
+ {
+ key: "techniques",
+ type: "multiselect",
+ label: "Techniques",
+ defaultValue: [],
+ options: [
+ { value: "strip-comments", label: "Strip Comments" },
+ { value: "minify", label: "Minify" },
+ ],
+ },
+];
+
+const INITIAL_VALUE: Record = {
+ enabled: false,
+ maxTokens: 1000,
+ prefix: "hello",
+ strategy: "fast",
+ techniques: [],
+};
+
+describe("EngineConfigForm", () => {
+ it("renders a checkbox for the boolean field", async () => {
+ const { EngineConfigForm } =
+ await import("../../../src/shared/components/compression/EngineConfigForm");
+ const onChange = vi.fn();
+ const container = mount(
+
+ );
+
+ // Label text appears
+ expect(container.textContent).toContain("Enabled");
+ // Description appears
+ expect(container.textContent).toContain("Enable this engine");
+ // Checkbox exists
+ const checkbox = container.querySelector("input[type='checkbox']") as HTMLInputElement | null;
+ expect(checkbox).toBeTruthy();
+ expect(checkbox?.checked).toBe(false);
+ });
+
+ it("renders a number input with min/max for the number field", async () => {
+ const { EngineConfigForm } =
+ await import("../../../src/shared/components/compression/EngineConfigForm");
+ const onChange = vi.fn();
+ const container = mount(
+
+ );
+
+ expect(container.textContent).toContain("Max Tokens");
+ const numInput = container.querySelector("input[type='number']") as HTMLInputElement | null;
+ expect(numInput).toBeTruthy();
+ expect(numInput?.getAttribute("min")).toBe("100");
+ expect(numInput?.getAttribute("max")).toBe("8000");
+ });
+
+ it("renders a text input for the string field", async () => {
+ const { EngineConfigForm } =
+ await import("../../../src/shared/components/compression/EngineConfigForm");
+ const onChange = vi.fn();
+ const container = mount(
+
+ );
+
+ expect(container.textContent).toContain("Prefix");
+ const textInput = container.querySelector("input[type='text']") as HTMLInputElement | null;
+ expect(textInput).toBeTruthy();
+ expect(textInput?.value).toBe("hello");
+ });
+
+ it("renders a select with 2 options for the select field", async () => {
+ const { EngineConfigForm } =
+ await import("../../../src/shared/components/compression/EngineConfigForm");
+ const onChange = vi.fn();
+ const container = mount(
+
+ );
+
+ expect(container.textContent).toContain("Strategy");
+ const select = container.querySelector("select") as HTMLSelectElement | null;
+ expect(select).toBeTruthy();
+ const options = Array.from(select?.querySelectorAll("option") ?? []);
+ expect(options).toHaveLength(2);
+ expect(options[0].value).toBe("fast");
+ expect(options[1].value).toBe("thorough");
+ });
+
+ it("renders 2 checkboxes for the multiselect field", async () => {
+ const { EngineConfigForm } =
+ await import("../../../src/shared/components/compression/EngineConfigForm");
+ const onChange = vi.fn();
+ const container = mount(
+
+ );
+
+ expect(container.textContent).toContain("Techniques");
+ expect(container.textContent).toContain("Strip Comments");
+ expect(container.textContent).toContain("Minify");
+ // boolean field checkbox + 2 multiselect checkboxes = 3 total
+ const allCheckboxes = Array.from(container.querySelectorAll("input[type='checkbox']"));
+ expect(allCheckboxes).toHaveLength(3); // boolean + 2 multiselect
+ });
+
+ it("calls onChange with flipped boolean when checkbox is toggled", async () => {
+ const { EngineConfigForm } =
+ await import("../../../src/shared/components/compression/EngineConfigForm");
+ const onChange = vi.fn();
+ const container = mount(
+
+ );
+
+ // The first checkbox is the boolean field
+ const checkbox = container.querySelector("input[type='checkbox']") as HTMLInputElement;
+ expect(checkbox).toBeTruthy();
+
+ // React listens to the native "click" event for checkboxes; setting
+ // .checked + dispatching a MouseEvent("click") is what triggers the
+ // synthetic onChange handler in jsdom.
+ act(() => {
+ checkbox.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ });
+
+ expect(onChange).toHaveBeenCalledWith({ ...INITIAL_VALUE, enabled: true });
+ });
+
+ it("calls onChange with new number when number input changes", async () => {
+ const { EngineConfigForm } =
+ await import("../../../src/shared/components/compression/EngineConfigForm");
+ const onChange = vi.fn();
+ const container = mount(
+
+ );
+
+ const numInput = container.querySelector("input[type='number']") as HTMLInputElement;
+ expect(numInput).toBeTruthy();
+
+ const nativeSetter = Object.getOwnPropertyDescriptor(
+ window.HTMLInputElement.prototype,
+ "value"
+ )?.set;
+
+ act(() => {
+ nativeSetter?.call(numInput, "2048");
+ numInput.dispatchEvent(new Event("change", { bubbles: true }));
+ });
+
+ expect(onChange).toHaveBeenCalledWith({ ...INITIAL_VALUE, maxTokens: 2048 });
+ });
+
+ it("calls onChange with updated array when a multiselect option is checked", async () => {
+ const { EngineConfigForm } =
+ await import("../../../src/shared/components/compression/EngineConfigForm");
+ const onChange = vi.fn();
+ const container = mount(
+
+ );
+
+ // The multiselect checkboxes are the 2nd and 3rd checkboxes
+ const allCheckboxes = Array.from(
+ container.querySelectorAll("input[type='checkbox']")
+ ) as HTMLInputElement[];
+ // Index 0 = boolean field, index 1 = "strip-comments", index 2 = "minify"
+ const stripCommentsCheckbox = allCheckboxes[1];
+ expect(stripCommentsCheckbox).toBeTruthy();
+
+ // React listens to native "click" for checkboxes to trigger synthetic onChange.
+ act(() => {
+ stripCommentsCheckbox.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ });
+
+ expect(onChange).toHaveBeenCalledWith({
+ ...INITIAL_VALUE,
+ techniques: ["strip-comments"],
+ });
+ });
+});
diff --git a/tests/unit/ui/engineConfigPage.test.tsx b/tests/unit/ui/engineConfigPage.test.tsx
new file mode 100644
index 00000000000..2d3a4f6d706
--- /dev/null
+++ b/tests/unit/ui/engineConfigPage.test.tsx
@@ -0,0 +1,304 @@
+// @vitest-environment jsdom
+import React, { act } from "react";
+import { createRoot } from "react-dom/client";
+import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
+
+// ── Helpers ───────────────────────────────────────────────────────────────
+
+const containers: HTMLElement[] = [];
+const roots: Array<{ unmount: () => void }> = [];
+
+function mountInContainer(ui: React.ReactElement): HTMLElement {
+ const container = document.createElement("div");
+ document.body.appendChild(container);
+ containers.push(container);
+ const root = createRoot(container);
+ roots.push(root);
+ act(() => {
+ root.render(ui);
+ });
+ return container;
+}
+
+beforeEach(() => {
+ (
+ globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
+ ).IS_REACT_ACT_ENVIRONMENT = true;
+});
+
+afterEach(async () => {
+ // Restore mocks first so any in-flight fetch promises settle without blocking
+ vi.restoreAllMocks();
+ await act(async () => {
+ while (roots.length > 0) {
+ roots.pop()?.unmount();
+ }
+ });
+ // Drain all remaining microtasks from effects that fired during unmount
+ for (let i = 0; i < 10; i++) {
+ await Promise.resolve();
+ }
+ while (containers.length > 0) {
+ containers.pop()?.remove();
+ }
+ document.body.innerHTML = "";
+});
+
+// ── Mock fetch ────────────────────────────────────────────────────────────
+
+const ENGINE_PAYLOAD = {
+ engines: [
+ {
+ id: "headroom",
+ name: "Headroom",
+ description: "Headroom engine description",
+ icon: "🗜️",
+ stackable: true,
+ stackPriority: 1,
+ metadata: { description: "Headroom metadata description" },
+ configSchema: [
+ {
+ key: "minRows",
+ type: "number",
+ label: "Min rows",
+ defaultValue: 8,
+ min: 1,
+ max: 1000,
+ },
+ ],
+ },
+ ],
+};
+
+const COMBO_PAYLOAD = {
+ id: "default",
+ name: "Default",
+ description: "Default combo",
+ pipeline: [],
+ languagePacks: [],
+ outputMode: null,
+};
+
+const ANALYTICS_PAYLOAD = {
+ engineId: "headroom",
+ runs: 0,
+ tokensSaved: 0,
+ avgSavingsPercent: 0,
+ days: 7,
+};
+
+function setupFetchMock() {
+ vi.spyOn(globalThis, "fetch").mockImplementation(async (input: RequestInfo | URL) => {
+ const url = input.toString();
+ if (url.includes("/api/compression/engines")) {
+ return new Response(JSON.stringify(ENGINE_PAYLOAD), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ if (url.includes("/api/context/combos/default")) {
+ return new Response(JSON.stringify(COMBO_PAYLOAD), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ if (url.includes("/api/context/analytics/engine")) {
+ return new Response(JSON.stringify(ANALYTICS_PAYLOAD), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ return new Response(JSON.stringify({}), { status: 404 });
+ });
+}
+
+// ── Tests ─────────────────────────────────────────────────────────────────
+
+describe("EngineConfigPage", () => {
+ it("renders the engine name after fetching engine list", async () => {
+ setupFetchMock();
+ const { EngineConfigPage } =
+ await import("../../../src/shared/components/compression/EngineConfigPage");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ // Flush any pending microtasks from effects
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container.textContent).toContain("Headroom");
+ });
+
+ it("renders the enable toggle for the engine", async () => {
+ setupFetchMock();
+ const { EngineConfigPage } =
+ await import("../../../src/shared/components/compression/EngineConfigPage");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // Either a checkbox or a button that says "Ativar"
+ const hasToggle =
+ container.querySelector("input[type='checkbox'][data-toggle='enable']") !== null ||
+ container.querySelector("[data-toggle='enable']") !== null ||
+ container.textContent?.includes("Ativar") === true;
+
+ expect(hasToggle).toBe(true);
+ });
+
+ it("renders the config form field label from fetched schema (EngineConfigForm mounted)", async () => {
+ setupFetchMock();
+ const { EngineConfigPage } =
+ await import("../../../src/shared/components/compression/EngineConfigPage");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // EngineConfigForm should render the "Min rows" field label from the schema
+ expect(container.textContent).toContain("Min rows");
+ });
+
+ it("shows empty-state text when analytics returns runs=0", async () => {
+ setupFetchMock();
+ const { EngineConfigPage } =
+ await import("../../../src/shared/components/compression/EngineConfigPage");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // Should show some "no data" copy
+ const hasEmptyState =
+ container.textContent?.includes("Sem dados") === true ||
+ container.textContent?.includes("No data") === true;
+ expect(hasEmptyState).toBe(true);
+ });
+
+ it("renders the stacked-mode prerequisite notice", async () => {
+ setupFetchMock();
+ const { EngineConfigPage } =
+ await import("../../../src/shared/components/compression/EngineConfigPage");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(container.textContent).toContain("stacked");
+ expect(container.textContent).toContain("Compression Settings");
+ });
+
+ it("Fix #4: handleSave sends enabled=false when engine is disabled", async () => {
+ // COMBO_PAYLOAD has empty pipeline → engine is disabled (enabled=false)
+ const putCalls: { body: unknown }[] = [];
+ vi.spyOn(globalThis, "fetch").mockImplementation(
+ async (input: RequestInfo | URL, init?: RequestInit) => {
+ const url = input.toString();
+ if (url.includes("/api/compression/engines")) {
+ return new Response(JSON.stringify(ENGINE_PAYLOAD), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ if (url.includes("/api/context/combos/default")) {
+ if (init?.method === "PUT") {
+ putCalls.push({ body: JSON.parse(init.body as string) });
+ return new Response(JSON.stringify(COMBO_PAYLOAD), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ return new Response(JSON.stringify(COMBO_PAYLOAD), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ if (url.includes("/api/context/analytics/engine")) {
+ return new Response(JSON.stringify(ANALYTICS_PAYLOAD), {
+ status: 200,
+ headers: { "Content-Type": "application/json" },
+ });
+ }
+ return new Response(JSON.stringify({}), { status: 404 });
+ }
+ );
+
+ const { EngineConfigPage } =
+ await import("../../../src/shared/components/compression/EngineConfigPage");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ // Let initial load complete
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // Click "Salvar" — engine is disabled (COMBO_PAYLOAD pipeline is empty)
+ const saveBtn = container.querySelector("button") as HTMLButtonElement | null;
+ const allButtons = Array.from(container.querySelectorAll("button"));
+ const salvarBtn = allButtons.find((b) => b.textContent?.includes("Salvar"));
+ if (salvarBtn) {
+ await act(async () => {
+ salvarBtn.click();
+ });
+ await act(async () => {
+ await Promise.resolve();
+ });
+ }
+
+ // There should be at least one PUT call with enabled=false
+ const putWithFalse = putCalls.find(
+ (c) => (c.body as { enabled: boolean; engineId: string }).enabled === false
+ );
+ expect(putCalls.length).toBeGreaterThan(0);
+ expect(putWithFalse).toBeDefined();
+ });
+
+ it("does not crash when all fetch calls fail (fail-soft)", async () => {
+ vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("Network error"));
+
+ const { EngineConfigPage } =
+ await import("../../../src/shared/components/compression/EngineConfigPage");
+
+ let container!: HTMLElement;
+ await act(async () => {
+ container = mountInContainer( );
+ });
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ // Component should still be mounted (not crashed)
+ expect(container).toBeTruthy();
+ expect(container.parentNode).toBeTruthy();
+ });
+});
diff --git a/tests/unit/ui/sidebar-engine-items.test.ts b/tests/unit/ui/sidebar-engine-items.test.ts
new file mode 100644
index 00000000000..52f6229fa61
--- /dev/null
+++ b/tests/unit/ui/sidebar-engine-items.test.ts
@@ -0,0 +1,79 @@
+import { describe, it } from "node:test";
+import assert from "node:assert/strict";
+
+// Import the sidebar constants we need to verify
+import {
+ HIDEABLE_SIDEBAR_ITEM_IDS,
+ COMPRESSION_CONTEXT_GROUP,
+} from "../../../src/shared/constants/sidebarVisibility";
+
+const ENGINE_IDS = [
+ "context-headroom",
+ "context-session-dedup",
+ "context-ccr",
+ "context-llmlingua",
+] as const;
+
+describe("HIDEABLE_SIDEBAR_ITEM_IDS includes all 4 engine items", () => {
+ for (const id of ENGINE_IDS) {
+ it(`includes "${id}"`, () => {
+ assert.ok(
+ (HIDEABLE_SIDEBAR_ITEM_IDS as readonly string[]).includes(id),
+ `Expected HIDEABLE_SIDEBAR_ITEM_IDS to include "${id}"`
+ );
+ });
+ }
+});
+
+describe("COMPRESSION_CONTEXT_GROUP contains all 4 engine items", () => {
+ const itemIds = COMPRESSION_CONTEXT_GROUP.items.map((item) => item.id);
+ const itemMap = new Map(COMPRESSION_CONTEXT_GROUP.items.map((item) => [item.id, item]));
+
+ for (const id of ENGINE_IDS) {
+ it(`contains item with id "${id}"`, () => {
+ assert.ok(itemIds.includes(id as (typeof itemIds)[number]), `Missing item "${id}"`);
+ });
+ }
+
+ it('headroom has href "/dashboard/context/headroom" and labelFallback "Headroom"', () => {
+ const item = itemMap.get("context-headroom");
+ assert.ok(item, "context-headroom item not found");
+ assert.equal(item.href, "/dashboard/context/headroom");
+ assert.equal(item.labelFallback, "Headroom");
+ });
+
+ it('session-dedup has href "/dashboard/context/session-dedup" and labelFallback "Session Dedup"', () => {
+ const item = itemMap.get("context-session-dedup");
+ assert.ok(item, "context-session-dedup item not found");
+ assert.equal(item.href, "/dashboard/context/session-dedup");
+ assert.equal(item.labelFallback, "Session Dedup");
+ });
+
+ it('ccr has href "/dashboard/context/ccr" and labelFallback "CCR"', () => {
+ const item = itemMap.get("context-ccr");
+ assert.ok(item, "context-ccr item not found");
+ assert.equal(item.href, "/dashboard/context/ccr");
+ assert.equal(item.labelFallback, "CCR");
+ });
+
+ it('llmlingua has href "/dashboard/context/llmlingua" and labelFallback "LLMLingua"', () => {
+ const item = itemMap.get("context-llmlingua");
+ assert.ok(item, "context-llmlingua item not found");
+ assert.equal(item.href, "/dashboard/context/llmlingua");
+ assert.equal(item.labelFallback, "LLMLingua");
+ });
+
+ it("4 engine items appear after context-rtk and before context-combos", () => {
+ const ids = itemIds as string[];
+ const rtkIdx = ids.indexOf("context-rtk");
+ const combosIdx = ids.indexOf("context-combos");
+ assert.ok(rtkIdx !== -1, "context-rtk not found");
+ assert.ok(combosIdx !== -1, "context-combos not found");
+
+ for (const id of ENGINE_IDS) {
+ const idx = ids.indexOf(id);
+ assert.ok(idx > rtkIdx, `${id} should appear after context-rtk`);
+ assert.ok(idx < combosIdx, `${id} should appear before context-combos`);
+ }
+ });
+});