feat(compression): adota omniglyph 1.4.0 e tira o gate de modelo da env do host

O 1.4.0 introduziu escopos de segurança e passou a resolvê-los dentro de
isOmniGlyphSupportedModel() lendo process.env.OMNIGLYPH_PROFILE. Somado ao
OMNIGLYPH_MODELS que já existia, duas variáveis do ambiente do host decidiam em
silêncio o gate de TODO request do OmniRoute: passthrough desligaria a engine
inteira e OMNIGLYPH_MODELS admitiria modelos sem recibo medido, enquanto a UI
segue prometendo "Claude Fable 5 na rota direta medida".

O adapter passa a usar isOmniGlyphSupportedModelForScope() com escopo explícito
e fixa o escopo mais restrito como teto: a env só pode ESTREITAR a allowlist,
nunca alargar. Os dois wires compartilham a mesma lista no pacote desde o
1.4.0, então uma checagem cobre Anthropic e GPT.

- omniglyph ^1.3.1 -> ^1.4.0 (lock em 1.4.0);
- testes de regressão para os dois caminhos de sequestro por env;
- teste de contrato dos exports novos (escopo, perfis, accounting).

O 1.4.0 também traz, sem mudança de código aqui: correção do glyph K que era
lido como H, remoção do backtracking polinomial no secret-guard, overrides do
pnpm em pnpm-workspace.yaml e as transitivas vulneráveis resolvidas.
This commit is contained in:
Xiangzhe
2026-08-18 00:04:26 -03:00
parent 6b05eedc8d
commit ce89143232
5 changed files with 110 additions and 17 deletions

View File

@@ -21,15 +21,41 @@ import type { CompressionEngine, CompressionEngineApplyOptions } from "./types.t
import type { CompressionResult } from "../types.ts";
import { createCompressionStats } from "../stats.ts";
import {
isOmniGlyphSupportedGptModel,
isOmniGlyphSupportedModel,
isOmniGlyphSupportedModelForScope,
transformAnthropicMessages,
transformOpenAIChatCompletions,
transformOpenAIResponses,
transformRequest,
type OmniGlyphSafetyScope,
} from "omniglyph";
import { isModelImageable } from "omniglyph/applicability";
/**
* Teto de modelos do OmniRoute — sempre o escopo mais restrito do pacote.
*
* `isOmniGlyphSupportedModel()` resolve o escopo lendo `OMNIGLYPH_PROFILE` do
* processo, e a lista base sai de `OMNIGLYPH_MODELS`. Duas variáveis do HOST
* decidiriam, em silêncio, o gate de todo request do OmniRoute: `passthrough`
* desligaria a engine inteira e `OMNIGLYPH_MODELS` ADMITIRIA modelos sem
* recibo medido — enquanto a UI continua prometendo "Claude Fable 5 na rota
* direta medida". Fixar o escopo mais restrito faz o gate só poder ESTREITAR
* pela env, nunca alargar, e mantém a decisão na configuração do OmniRoute.
*/
const MEASURED_MODEL_SCOPE: OmniGlyphSafetyScope = "coding-safe";
/** Escopo semântico do transform. `aggressive` mantém a política medida atual. */
const DEFAULT_TRANSFORM_SCOPE: OmniGlyphSafetyScope = "aggressive";
/**
* O modelo precisa passar no teto medido E no escopo em vigor. Os dois wires
* (Anthropic e GPT) compartilham a mesma allowlist no pacote desde 1.4.0, então
* uma única checagem cobre os dois.
*/
function isModelWithinScope(model: string, scope: OmniGlyphSafetyScope): boolean {
if (!isOmniGlyphSupportedModelForScope(model, MEASURED_MODEL_SCOPE)) return false;
return isOmniGlyphSupportedModelForScope(model, scope);
}
function skip(body: Record<string, unknown>, reason: string): CompressionResult {
try {
return {
@@ -133,11 +159,9 @@ async function applyOmniglyph(
) {
return skip(body, "source_format_not_openai_responses");
}
const supportedModel =
wireFormat === "claude"
? isOmniGlyphSupportedModel(model)
: isOmniGlyphSupportedGptModel(model);
if (!supportedModel) return skip(body, "model_not_approved");
if (!isModelWithinScope(model, DEFAULT_TRANSFORM_SCOPE)) {
return skip(body, "model_not_approved");
}
// OmniGlyph 1.3.x deliberately keeps unverified families (currently Grok)
// text-only until the operator acknowledges them via its own env gate.
if (!isModelImageable(model)) return skip(body, "model_not_imageable");

8
package-lock.json generated
View File

@@ -60,7 +60,7 @@
"next-intl": "^4.13.6",
"next-themes": "^0.4.6",
"node-machine-id": "^1.1.12",
"omniglyph": "^1.3.1",
"omniglyph": "^1.4.0",
"onnxruntime-node": "~1.24.3",
"open": "^11.0.0",
"ora": "^9.4.1",
@@ -28871,9 +28871,9 @@
"license": "MIT"
},
"node_modules/omniglyph": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/omniglyph/-/omniglyph-1.3.1.tgz",
"integrity": "sha512-6QnZCoXYczjsPN2x+XpbimimjO6kCoSZUzsdSvoKjtw28U1U724VgLICBNaLX4FFs5jd7SrYNNs9Aee2iIkcoA==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/omniglyph/-/omniglyph-1.4.0.tgz",
"integrity": "sha512-4zAqDW9pBb2i+fiGOVLIKbdecZeo55UmKQoLku1apxo3TSA4gfqcMo2MqQpal1VicckUwTbexFLasW7qngXUHA==",
"license": "MIT",
"dependencies": {
"gpt-tokenizer": "^3.4.0"

View File

@@ -263,6 +263,7 @@
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@huggingface/transformers": "^4.2.0",
"@lobehub/icons": "^5.8.0",
"@modelcontextprotocol/sdk": "^1.29.0",
"@monaco-editor/react": "^4.7.0",
@@ -304,7 +305,8 @@
"next-intl": "^4.13.6",
"next-themes": "^0.4.6",
"node-machine-id": "^1.1.12",
"omniglyph": "^1.3.1",
"omniglyph": "^1.4.0",
"onnxruntime-node": "~1.24.3",
"open": "^11.0.0",
"ora": "^9.4.1",
"parse5": "^8.0.1",
@@ -335,9 +337,7 @@
"xxhash-wasm": "^1.1.0",
"yazl": "^3.3.1",
"zod": "^4.4.3",
"zustand": "^5.0.13",
"@huggingface/transformers": "^4.2.0",
"onnxruntime-node": "~1.24.3"
"zustand": "^5.0.13"
},
"optionalDependencies": {
"@atjsh/llmlingua-2": "2.0.3",
@@ -345,9 +345,9 @@
"better-sqlite3": "^13.0.2",
"js-tiktoken": "^1.0.20",
"keytar": "^7.9.0",
"sqlite-vec": "^0.1.9",
"tls-client-node": "^0.2.0",
"wreq-js": "^3.0.0",
"sqlite-vec": "^0.1.9"
"wreq-js": "^3.0.0"
},
"devDependencies": {
"@axe-core/playwright": "^4.11.3",

View File

@@ -187,3 +187,45 @@ test("OpenAI não roda no estágio pré-tradução", async () => {
assert.equal(r.compressed, false);
assert.ok(r.stats?.techniquesUsed.includes("skip:requires_post_translation"));
});
// OmniGlyph 1.4.0 introduziu escopos de segurança (`coding-safe`/`balanced`/
// `aggressive`/`passthrough`) e passou a resolvê-los, dentro de
// `isOmniGlyphSupportedModel()`, lendo `process.env.OMNIGLYPH_PROFILE`. Isso
// transforma uma variável de ambiente do HOST num gate silencioso de TODO
// request do OmniRoute: um `OMNIGLYPH_PROFILE=passthrough` exportado no shell
// do processo desligaria a engine sem que nenhuma configuração do OmniRoute
// tivesse mudado — e sem nenhum sinal na UI. A política é do OmniRoute; o
// adapter tem de passar o escopo explicitamente.
async function withEnv<T>(key: string, value: string, fn: () => Promise<T>): Promise<T> {
const had = Object.prototype.hasOwnProperty.call(process.env, key);
const previous = process.env[key];
process.env[key] = value;
try {
return await fn();
} finally {
if (had) process.env[key] = previous;
else delete process.env[key];
}
}
test("OMNIGLYPH_PROFILE do host não decide o gate de modelo do OmniRoute", async () => {
const r = await withEnv("OMNIGLYPH_PROFILE", "passthrough", () =>
omniglyphEngine.applyAsync!(claudeBody(), OK)
);
assert.equal(
r.compressed,
true,
"env do processo não pode desligar a engine: o escopo vem da config do OmniRoute"
);
});
test("OMNIGLYPH_PROFILE do host não amplia a allowlist de modelos do OmniRoute", async () => {
const body = { ...claudeBody(), model: "claude-sonnet-5" };
const r = await withEnv("OMNIGLYPH_PROFILE", "aggressive", () =>
withEnv("OMNIGLYPH_MODELS", "claude-fable-5,claude-sonnet-5", () =>
omniglyphEngine.applyAsync!(body, { ...OK, model: "claude-sonnet-5" })
)
);
assert.equal(r.compressed, false, "modelo sem recibo medido não pode entrar via env do host");
assert.ok(r.stats?.techniquesUsed.includes("skip:model_not_approved"));
});

View File

@@ -15,3 +15,30 @@ test("pacote omniglyph exporta a API que o adapter consome", async () => {
const applicability = await import("omniglyph/applicability");
assert.equal(typeof applicability.isModelImageable, "function");
});
// Superfícies introduzidas no 1.4.0. Sem esta asserção, uma remoção upstream só
// apareceria em runtime — o adapter importa esses símbolos diretamente.
test("omniglyph 1.4.0 exporta escopo de segurança, perfis e accounting", async () => {
const mod = await import("omniglyph");
// Gate de modelo por escopo explícito (não pela env do processo).
assert.equal(typeof mod.isOmniGlyphSupportedModelForScope, "function");
assert.equal(mod.isOmniGlyphSupportedModelForScope("claude-fable-5", "coding-safe"), true);
assert.equal(mod.isOmniGlyphSupportedModelForScope("claude-sonnet-5", "coding-safe"), false);
assert.equal(
mod.isOmniGlyphSupportedModelForScope("claude-fable-5", "passthrough"),
false,
"passthrough não habilita modelo nenhum"
);
// Perfis semânticos.
assert.equal(typeof mod.resolveCompressionProfile, "function");
assert.equal(typeof mod.mergeCompressionProfileOptions, "function");
assert.equal(typeof mod.shouldKeepToolResultSharp, "function");
assert.equal(mod.resolveCompressionProfile("coding-safe").name, "coding-safe");
assert.throws(() => mod.resolveCompressionProfile("nao-existe"));
// Contabilidade física normalizada.
assert.equal(typeof mod.normalizeAccounting, "function");
assert.equal(typeof mod.providerActualInputTokens, "function");
});