Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · semantic ver

## [Unreleased]

### Added

- **feat(reliability):** named compression profiles for callers and both proxy
hosts. `OMNIGLYPH_PROFILE=coding-safe` keeps system authority, tool schemas,
reminders and live tool results as native text while allowing old closed
history to collapse; `balanced`, `aggressive` (the unchanged default) and
`passthrough` complete the policy set. Safe-profile boundaries cannot be
weakened by per-call or Worker overrides, and their model scope can only
narrow the measured default allowlist. (thanks @alteixeira20)

### Fixed

- **fix(render):** glyph surgery so the Spleen 5×8 `K` no longer reads as `H`.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ bulky request block ──► profitability gate ──► reflow + render (1-bi
All off by default — the compression path above is unchanged unless you set them. `omniglyph --help` lists every flag.

- `OMNIGLYPH_GUARD_SECRETS=text|redact` — keep API keys, tokens, and other credentials out of rendered images: `text` never images a block that holds one, `redact` masks it in place. Never alters what the upstream API receives.
- `OMNIGLYPH_PROFILE=coding-safe|balanced|aggressive|passthrough` — choose a semantic compression boundary. Safe profiles keep authority, schemas, reminders, and live tool results native while collapsing only eligible old history; `aggressive` preserves the existing default and `passthrough` disables transforms.
- `OMNIGLYPH_KEEP_SYSTEM_TEXT=1` — never image the session config (system prompt, tool docs, reminders); tool outputs and old history still convert. Guards against Anthropic's refusal classifier on system-shaped images.
- `OMNIGLYPH_MODELS` — comma-separated model bases to image (default `claude-fable-5,gpt-5.6`; `off` disables). **Grok** is supported opt-in on the OpenAI-compatible wire but stays fail-closed: text-only until acked via `OMNIGLYPH_UNVERIFIED_MODELS=grok-4.5`, pending its own reading receipt.

Expand Down
57 changes: 51 additions & 6 deletions src/core/applicability.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
/** Applicability helpers for OmniGlyph's production-safe model scope. */

import {
resolveCompressionProfile,
type CompressionProfileName,
} from './safety-policy.js';

export type OmniGlyphApplicabilityReason =
| 'eligible'
| 'unsupported_model'
Expand Down Expand Up @@ -45,7 +50,7 @@ let runtimeModelBases: readonly string[] | null = null;
* ~2pp arithmetic, 6/15 dense-hex recall vs Fable's 100/100; GPT 5.5 likewise
* degrades on imaged history/context) — so silently imaging them is the wrong
* default. Both stay opt-in via the dashboard chips or OMNIGLYPH_MODELS. */
const DEFAULT_MODEL_BASES = ['claude-fable-5', 'gpt-5.6'];
export const DEFAULT_MODEL_BASES = ['claude-fable-5', 'gpt-5.6'] as const;

function falsey(v: string): boolean {
return /^(0|false|no|off|none)$/i.test(v.trim());
Expand All @@ -66,11 +71,38 @@ function envOrDefaultBases(): string[] {
return trimmed.split(',').map((s) => s.trim()).filter(Boolean);
}

function allowedModelBases(): string[] {
function configuredModelBases(): string[] {
if (runtimeModelBases !== null) return [...runtimeModelBases];
return envOrDefaultBases();
}

function matchesModelBase(model: string, candidate: string): boolean {
return model === candidate || model.startsWith(`${candidate}-`);
}

function allowedModelBasesForScope(scope: CompressionProfileName): string[] {
if (scope === 'passthrough') return [];
const configured = configuredModelBases();
if (scope === 'aggressive') return configured;
return configured.filter((candidate) => {
const base = baseModelId(candidate);
return DEFAULT_MODEL_BASES.some((verified) => matchesModelBase(base, verified));
});
}

function activeCompressionScope(): CompressionProfileName {
const raw = typeof process !== 'undefined' ? process.env?.OMNIGLYPH_PROFILE : undefined;
try {
return resolveCompressionProfile(raw).name;
} catch {
return 'passthrough';
}
}

function allowedModelBases(): string[] {
return allowedModelBasesForScope(activeCompressionScope());
}

/** Current effective allowed-model scope (Claude + GPT). */
export function getAllowedModelBases(): string[] {
return allowedModelBases();
Expand All @@ -89,20 +121,33 @@ export function setAllowedModelBases(list: readonly string[] | null): void {

/** Membership test against the single allowed scope. Matches exact base or `-suffix`
* alias; [variant] tags stripped first. */
function isAllowed(model: string | null | undefined): boolean {
function isAllowedForScope(
model: string | null | undefined,
scope: CompressionProfileName,
): boolean {
if (typeof model !== 'string') return false;
const base = baseModelId(model);
return allowedModelBases().some((b) => base === b || base.startsWith(`${b}-`));
return allowedModelBasesForScope(scope).some((candidate) => matchesModelBase(base, candidate));
}

/** Pure model gate for a caller-selected compression profile. Safe profiles
* may narrow configured scope but never promote a model beyond the measured
* built-in defaults. */
export function isOmniGlyphSupportedModelForScope(
model: string | null | undefined,
scope: CompressionProfileName,
): boolean {
return isAllowedForScope(model, scope);
}

/** True when OmniGlyph may transform this Anthropic model. */
export function isOmniGlyphSupportedModel(model: string | null | undefined): boolean {
return isAllowed(model);
return isAllowedForScope(model, activeCompressionScope());
}

/** True when OmniGlyph may transform this GPT model. Shares the single OMNIGLYPH_MODELS scope. */
export function isOmniGlyphSupportedGptModel(model: string | null | undefined): boolean {
return isAllowed(model);
return isAllowedForScope(model, activeCompressionScope());
}

/** Canonical set of Anthropic Messages routes OmniGlyph transforms. Shared with
Expand Down
8 changes: 8 additions & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
export {
DEFAULT_MODEL_BASES,
getAllowedModelBases,
getConfiguredModelBases,
isOmniGlyphSupportedGptModel,
isOmniGlyphSupportedModel,
isOmniGlyphSupportedModelForScope,
setAllowedModelBases,
shouldTransformAnthropicMessages,
type OmniGlyphApplicabilityInput,
type OmniGlyphApplicabilityReason,
} from './applicability.js';
export {
mergeCompressionProfileOptions,
resolveCompressionProfile,
type CompressionProfile,
type CompressionProfileName,
} from './safety-policy.js';
export {
buildCountTokensBodies,
buildBaselineCountTokensBody,
Expand Down
37 changes: 24 additions & 13 deletions src/core/library.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isOmniGlyphSupportedModel } from './applicability.js';
import { isOmniGlyphSupportedModelForScope } from './applicability.js';
import { countCacheControlMarkers } from './measurement.js';
import {
renderTextToPngsWithCharLimit,
Expand All @@ -19,8 +19,13 @@
type KeepSharpBlock,
type RecoverableBlock,
} from './transform.js';
import {
mergeCompressionProfileOptions,
resolveCompressionProfile,
type CompressionProfileName,
} from './safety-policy.js';

export type { KeepSharpBlock, RecoverableBlock };
export type { CompressionProfileName, KeepSharpBlock, RecoverableBlock };

Check warning on line 28 in src/core/library.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use `export…from` to re-export `KeepSharpBlock`.

See more on https://sonarcloud.io/project/issues?id=diegosouzapw_omniglyph&issues=AaARDdcnFeabPHXuc6ZX&open=AaARDdcnFeabPHXuc6ZX&pullRequest=52

Check warning on line 28 in src/core/library.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use `export…from` to re-export `RecoverableBlock`.

See more on https://sonarcloud.io/project/issues?id=diegosouzapw_omniglyph&issues=AaARDdcnFeabPHXuc6ZY&open=AaARDdcnFeabPHXuc6ZY&pullRequest=52

export type BytesLike = Uint8Array | ArrayBuffer | ArrayBufferView;

Expand All @@ -31,6 +36,8 @@
> {
/** Test/debug-only bypass. Product hosts should prefer their dashboard setting. */
readonly compress?: boolean;
/** Semantic compression policy. Unset preserves the existing behavior. */
readonly profile?: CompressionProfileName;
}

export interface OmniGlyphTransformInput {
Expand Down Expand Up @@ -106,19 +113,23 @@
input: OmniGlyphTransformInput,
): Promise<OmniGlyphTransformResult> {
const original = toUint8Array(input.body);
if (!isOmniGlyphSupportedModel(input.model)) {
return {
body: original,
applied: false,
reason: 'unsupported_model',
detail: input.model ?? undefined,
info: emptyInfo('unsupported_model'),
cache: { ownsCacheControl: false, markerCount: countCacheControlMarkers(original) },
};
}

try {
const { body, info } = await transformRequest(original, input.options);
const { profile: requestedProfile, ...overrides } = input.options ?? {};
const profile = resolveCompressionProfile(requestedProfile);
if (!isOmniGlyphSupportedModelForScope(input.model, profile.name)) {
return {
body: original,
applied: false,
reason: 'unsupported_model',
detail: input.model ?? undefined,
info: emptyInfo('unsupported_model'),
cache: { ownsCacheControl: false, markerCount: countCacheControlMarkers(original) },
};
}

const options = mergeCompressionProfileOptions(profile, overrides);
const { body, info } = await transformRequest(original, options);
const reason = classifyReason(info);
const markerCount = countCacheControlMarkers(body);
return {
Expand Down
116 changes: 116 additions & 0 deletions src/core/safety-policy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import type { TransformOptions } from './transform.js';

export type CompressionProfileName =
| 'coding-safe'
| 'balanced'
| 'aggressive'
| 'passthrough';

export interface CompressionProfile {
readonly name: CompressionProfileName;
readonly description: string;
readonly transform: Readonly<TransformOptions>;
}

const CODING_SAFE: CompressionProfile = {
name: 'coding-safe',
description: 'keep authority and live request state native; collapse only old history',
transform: {
compress: true,
compressSystem: false,
compressTools: false,
compressReminders: false,
compressToolResults: false,
collapseHistory: true,
historyAmortizationHorizon: 4,
gptHistory: {
keepTail: 12,
keepRecentPairs: 12,
minCollapsePrefix: 16,
minCollapseTokens: 4_000,
},
},
};

const BALANCED: CompressionProfile = {
name: 'balanced',
description: 'keep authority and live request state native with a shorter protected history tail',
transform: {
compress: true,
compressSystem: false,
compressTools: false,
compressReminders: false,
compressToolResults: false,
collapseHistory: true,
historyAmortizationHorizon: 3,
gptHistory: {
keepTail: 8,
keepRecentPairs: 8,
minCollapsePrefix: 12,
minCollapseTokens: 3_000,
},
},
};

const AGGRESSIVE: CompressionProfile = {
name: 'aggressive',
description: 'existing transform policy',
transform: {},
};

const PASSTHROUGH: CompressionProfile = {
name: 'passthrough',
description: 'route only; disable context transforms',
transform: { compress: false },
};

export function resolveCompressionProfile(raw?: string): CompressionProfile {
const value = (raw ?? '').trim().toLowerCase();
if (!value || value === 'aggressive' || value === 'legacy') return AGGRESSIVE;
if (value === 'safe' || value === 'coding' || value === 'coding-safe') return CODING_SAFE;
if (value === 'balanced') return BALANCED;
if (value === 'off' || value === 'disabled' || value === 'passthrough') return PASSTHROUGH;
throw new Error(
`invalid OMNIGLYPH_PROFILE '${raw}'; expected coding-safe, balanced, aggressive or passthrough`,
);
}

export function mergeCompressionProfileOptions(
profile: CompressionProfile,
overrides: TransformOptions = {},
): TransformOptions {
if (profile.name === 'passthrough') return { ...overrides, compress: false };
if (profile.name === 'aggressive') return { ...overrides };

const baseHistory = profile.transform.gptHistory;
const callerHistory = overrides.gptHistory;
const floor = (base: number | undefined, value: number | undefined): number | undefined => {
if (base === undefined) return value;
if (value === undefined) return base;
return Math.max(base, value);
};

return {
...profile.transform,
...overrides,
compressSystem: false,
compressTools: false,
compressReminders: false,
compressToolResults: false,
collapseHistory: overrides.collapseHistory === false
? false
: profile.transform.collapseHistory,
historyAmortizationHorizon: floor(
profile.transform.historyAmortizationHorizon,
overrides.historyAmortizationHorizon,
),
gptHistory: {
...baseHistory,
...callerHistory,
keepTail: floor(baseHistory?.keepTail, callerHistory?.keepTail),
keepRecentPairs: floor(baseHistory?.keepRecentPairs, callerHistory?.keepRecentPairs),
minCollapsePrefix: floor(baseHistory?.minCollapsePrefix, callerHistory?.minCollapsePrefix),
minCollapseTokens: floor(baseHistory?.minCollapseTokens, callerHistory?.minCollapseTokens),
},
};
}
Loading