mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-22 23:22:09 +03:00
* feat(compression): target-wire OmniGlyph stage and transport fidelity gate Roda o OmniGlyph depois da tradução para o wire real do provedor, em vez do corpo de origem. Um cliente OpenAI roteado para Claude deixava de comprimir com skip:source_format_not_claude porque o corpo ainda estava em formato OpenAI quando a engine era avaliada. - dispatch nativo por wire: Anthropic Messages, OpenAI Chat Completions e OpenAI Responses (input[] preservado, sem achatar para messages[]); - estágio target-wire pós-translateRequest, com guarda contra dupla compressão no caminho Claude→OpenAI; - preserveSystemPrompt do OmniRoute mapeado para compressSystem: false; - imageTransportPolicy: fidelidade de bytes/dimensões separada de supportsVision; só Anthropic/Claude tem recibo byte-preserving, o resto é fail-closed; - contagem de tokens de data URL PNG no wire OpenAI (marcador ;base64,); - README e i18n en/pt-BR com claims escopados ao caminho medido. * 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. * feat(compression): expõe os perfis semânticos do omniglyph nos três wires O 1.4.0 trouxe perfis nomeados (coding-safe, balanced, aggressive, passthrough), mas só transformAnthropicMessages() os resolve sozinho: os transformadores OpenAI recebem TransformOptions cru e ignorariam o campo. Um perfil escolhido pelo operador valeria no wire Claude e sumiria no OpenAI. O adapter passa a mesclar o perfil com mergeCompressionProfileOptions() antes de chamar Chat Completions e Responses. O default segue aggressive — a política que os recibos publicados mediram. Medido nesta base: com coding-safe/balanced, uma sessão sem histórico acumulado para em below_min_chars e a engine não faz nada, porque os dois fixam minCompressChars no máximo e desligam system/tools/tool-results. Como a engine é opt-in, um default assim entregaria "ligado, 0% de ganho". O perfil é TETO, não piso: mergeCompressionProfileOptions não deixa um override do chamador reabrir uma lane lossy que o perfil fechou. Coberto por teste, por ser contra-intuitivo. Também fecha um caminho em que o OmniRoute violaria a própria política: o wire OpenAI do pacote não tem compressSystem — honra apenas compressTools, gptHistory, minCompressChars e reflow, e sempre troca a instrução por um ponteiro para a imagem. Com preserveSystemPrompt ligado, imagear assim queimaria o prefixo quente que a decisão cache-aware está protegendo, sem nada no corpo devolvido denunciando. A engine agora pula com skip:system_preservation_unsupported_on_wire. * feat(compression): contabilidade física do omniglyph com grau de evidência O adapter descartava o TransformInfo inteiro, então a UI mostrava um número de economia sem dizer de onde ele vinha — contagem do provider, estimativa ou só diferença de bytes. O 1.4.0 expõe normalizeAccounting(), que classifica essa evidência e resolve a semântica de cache por família: Anthropic reporta input, cache-create e cache-read em buckets DISJUNTOS, enquanto OpenAI e xAI reportam cached como SUBCONJUNTO do input. Somar à mão dá double-count silencioso. O novo omniglyphTelemetry.ts não filtra por denylist — MONTA um objeto novo, campo a campo, só com número e enum. TransformInfo mistura contadores inofensivos com material que não pode ser persistido: bytes PNG, imageSourceText(s), recoverable[].text, os sha8 de system/CLAUDE.md/primeira mensagem, nomes de tags observadas e o bloco env (cwd, branch, versões). Copiar o objeto inteiro transformaria telemetria de compressão em vazamento de prompt. O teste de negação prova que segredo, caminho do operador, texto do system e base64 não aparecem, e varre a allowlist exigindo que toda string seja de um enum conhecido. - provider threaded do chatCore e do bridge Codex WS até a engine; ausente vira `unknown`, que faz o upstream recusar adivinhar buckets de cache; - contabilidade propagada para o engineBreakdown do passo (o agregado do pipeline soma todas as engines e não serviria); - skip não emite contabilidade: zeros ali seriam indistinguíveis de "a engine nem rodou". * feat(compression): perfil do omniglyph configurável, persistido e documentado Fecha o caminho do operador: o perfil já existia no adapter, mas só como default de código. Agora atravessa schema Zod, normalizador do banco, API de settings e a página dedicada do engine. - OmniglyphConfig tipado + omniglyphConfigSchema (z.enum dos quatro perfis); - normalizeOmniglyphConfig: nome desconhecido vindo do storage cai para o default em vez de virar "roda com a política padrão"; - seletor na página do engine, com PATCH próprio — o perfil vive fora do mapa `engines`, e mandá-lo junto reescreveria o mapa inteiro (o store persiste o mapa como uma linha JSON só); - i18n en/pt-BR descrevendo o custo medido de cada perfil, não só o nome; - README e COMPRESSION_ENGINES.md com a regra do teto e o motivo de o default não ser o perfil mais seguro. Corrige de passagem um teste-irmão que ninguém via: o gate de transporte na UI deixou de dizer "direct Anthropic" quando os wires OpenAI nativos entraram, mas tests/unit/ui/omniglyphContextPage.test.tsx continuou afirmando a cópia antiga. O arquivo inteiro estava excluído do vitest.config.ts como "#8618 pre-existing failure", então a quebra passou silenciosa. Com a asserção alinhada o arquivo fecha 3/3, e a exclusão sai — o próprio comentário mandava removê-la quando corrigida. A doc não nomeia OMNIGLYPH_MODELS: o gate de docs fabricadas está certo em apontar que o OmniRoute nunca lê essa env — quem lê é o pacote. * fix(i18n): paridade do locale vi com as chaves novas do perfil do omniglyph `tests/unit/i18n-vi-completeness.test.ts` exige paridade ESTRITA de chaves entre en e vi — diferente do ratchet `i18n:check-ui-coverage`, que passa com 80%. As 11 chaves do seletor de perfil entraram só em en e pt-BR, e o gate de cobertura seguiu verde, então a quebra só apareceu na matriz completa do CI. --------- Co-authored-by: Xiangzhe <bakryun0718@proton.me> Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
404 lines
15 KiB
TypeScript
404 lines
15 KiB
TypeScript
import { z } from "zod";
|
|
|
|
export const compressionModeSchema = z.enum([
|
|
"off",
|
|
"lite",
|
|
"standard",
|
|
"aggressive",
|
|
"ultra",
|
|
"rtk",
|
|
"codex-responses",
|
|
"omniglyph",
|
|
"stacked",
|
|
]);
|
|
|
|
export const cavemanIntensitySchema = z.enum(["lite", "full", "ultra"]);
|
|
export const rtkIntensitySchema = z.enum(["minimal", "standard", "aggressive"]);
|
|
export const rtkRawOutputRetentionSchema = z.enum(["never", "failures", "always"]);
|
|
|
|
export const codexResponsesConfigSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
minBytes: z.number().int().min(0).max(2_000_000).optional(),
|
|
maxOutputBytes: z.number().int().min(1).max(10_000_000).optional(),
|
|
maxCandidateBytes: z.number().int().min(1).max(2_000_000).optional(),
|
|
maxLines: z.number().int().min(1).max(10_000).optional(),
|
|
minSearchMatches: z.number().int().min(2).max(10_000).optional(),
|
|
minLogLines: z.number().int().min(2).max(10_000).optional(),
|
|
preserveToolNames: z.array(z.string().trim().min(1).max(100)).max(100).optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const cavemanConfigSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
compressRoles: z.array(z.enum(["user", "assistant", "system"])).optional(),
|
|
skipRules: z.array(z.string()).optional(),
|
|
minMessageLength: z.number().int().min(0).optional(),
|
|
preservePatterns: z.array(z.string()).optional(),
|
|
intensity: cavemanIntensitySchema.optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const cavemanOutputModeSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
intensity: cavemanIntensitySchema.optional(),
|
|
autoClarity: z.boolean().optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const outputStyleSelectionSchema = z
|
|
.object({
|
|
id: z.string().trim().min(1),
|
|
level: cavemanIntensitySchema,
|
|
})
|
|
.strict();
|
|
|
|
export const rtkConfigSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
intensity: rtkIntensitySchema.optional(),
|
|
applyToToolResults: z.boolean().optional(),
|
|
applyToCodeBlocks: z.boolean().optional(),
|
|
applyToAssistantMessages: z.boolean().optional(),
|
|
enabledFilters: z.array(z.string()).optional(),
|
|
disabledFilters: z.array(z.string()).optional(),
|
|
maxLinesPerResult: z.number().int().min(0).max(100000).optional(),
|
|
maxCharsPerResult: z.number().int().min(0).max(1000000).optional(),
|
|
deduplicateThreshold: z.number().int().min(2).max(100).optional(),
|
|
customFiltersEnabled: z.boolean().optional(),
|
|
trustProjectFilters: z.boolean().optional(),
|
|
rawOutputRetention: rtkRawOutputRetentionSchema.optional(),
|
|
rawOutputMaxBytes: z.number().int().min(1024).max(10_000_000).optional(),
|
|
enableGrouping: z.boolean().optional(),
|
|
groupingThreshold: z.number().int().min(2).max(100).optional(),
|
|
stripCodeComments: z.boolean().optional(),
|
|
preserveDocstrings: z.boolean().optional(),
|
|
enableRenderers: z.boolean().optional(),
|
|
})
|
|
.strict();
|
|
|
|
// mcpAccessibility tunes how the MCP server trims oversized tool outputs before returning them.
|
|
// The schema only enforces structural validity (positive integers / booleans); the numeric floors
|
|
// (e.g. maxTextChars below the truncation-tail reserve) are owned by clampMcpAccessibilityConfig
|
|
// on the write path, which folds out-of-range values back to the safe defaults. All fields are
|
|
// optional so the settings sub-route can apply a partial merge over the current config.
|
|
export const mcpAccessibilityConfigSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
maxTextChars: z.number().int().min(1).optional(),
|
|
collapseThreshold: z.number().int().min(1).optional(),
|
|
collapseKeepHead: z.number().int().min(0).optional(),
|
|
collapseKeepTail: z.number().int().min(0).optional(),
|
|
minLengthToProcess: z.number().int().min(1).optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const languageConfigSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
defaultLanguage: z.string().trim().min(1).optional(),
|
|
autoDetect: z.boolean().optional(),
|
|
enabledPacks: z.array(z.string().trim().min(1)).optional(),
|
|
})
|
|
.strict();
|
|
|
|
// Context Editing is a provider-delegated compression mode (Claude/Anthropic only):
|
|
// the provider clears old tool-use blocks server-side. This config only carries the
|
|
// on/off flag; the request-time header/body injection is a separate slice.
|
|
export const contextEditingConfigSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
})
|
|
.strip();
|
|
|
|
export const aggressiveConfigSchema = z
|
|
.object({
|
|
thresholds: z
|
|
.object({
|
|
fullSummary: z.number().int().min(1).max(100).optional(),
|
|
moderate: z.number().int().min(1).max(100).optional(),
|
|
light: z.number().int().min(1).max(100).optional(),
|
|
verbatim: z.number().int().min(1).max(100).optional(),
|
|
})
|
|
.strict()
|
|
.optional(),
|
|
toolStrategies: z
|
|
.object({
|
|
fileContent: z.boolean().optional(),
|
|
grepSearch: z.boolean().optional(),
|
|
shellOutput: z.boolean().optional(),
|
|
json: z.boolean().optional(),
|
|
errorMessage: z.boolean().optional(),
|
|
})
|
|
.strict()
|
|
.optional(),
|
|
summarizerEnabled: z.boolean().optional(),
|
|
maxTokensPerMessage: z.number().int().min(256).max(32768).optional(),
|
|
minSavingsThreshold: z.number().min(0).max(1).optional(),
|
|
preserveSystemPrompt: z.boolean().optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const ultraConfigSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
compressionRate: z.number().min(0).max(1).optional(),
|
|
minScoreThreshold: z.number().min(0).max(1).optional(),
|
|
slmFallbackToAggressive: z.boolean().optional(),
|
|
modelPath: z.string().trim().min(1).optional(),
|
|
maxTokensPerMessage: z.number().int().min(0).max(32768).optional(),
|
|
preserveSystemPrompt: z.boolean().optional(),
|
|
})
|
|
.strict();
|
|
|
|
/** Headroom SmartCrusher detail settings (persisted under settings.headroom). */
|
|
export const headroomConfigSchema = z
|
|
.object({
|
|
// Matches engine schema min 2 / max 10000 and DEFAULT_MIN_ROWS=8.
|
|
minRows: z.number().int().min(2).max(10000).optional(),
|
|
})
|
|
.strict();
|
|
|
|
// Session Dedup / CCR detail settings (#8388 — sibling gap to headroom/#8056: the
|
|
// EngineConfigPage detail form was renderable but PUT bodies had no slot to persist
|
|
// into). Ranges mirror SESSION_DEDUP_SCHEMA / CCR_SCHEMA (engines/session-dedup,
|
|
// engines/ccr) so the validation layer stays in lockstep with the engine's own bounds.
|
|
export const sessionDedupConfigSchema = z
|
|
.object({
|
|
minBlockChars: z.number().int().min(1).max(100000).optional(),
|
|
fuzzy: z.boolean().optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const ccrConfigSchema = z
|
|
.object({
|
|
minChars: z.number().int().min(100).max(1_000_000).optional(),
|
|
retrievalRampFactor: z.number().min(1).max(100).optional(),
|
|
})
|
|
.strict();
|
|
|
|
const noConfigSchema = z.object({}).strict();
|
|
|
|
// Structural engines (session-dedup / ccr / headroom / relevance / llmlingua) do not
|
|
// expose a fixed intensity enum in ENGINE_CATALOG — accept optional free-form intensity
|
|
// and a loose config bag so GET→PUT round-trips of stackedPipeline succeed (#6747).
|
|
const structuralStepConfigSchema = z.record(z.string(), z.unknown()).optional();
|
|
|
|
/**
|
|
* Writable stacked-pipeline step shape for PUT /api/settings/compression and
|
|
* PUT /api/context/combos/[id]. MUST accept every engine id in ENGINE_CATALOG /
|
|
* GET /api/compression/engines and every engine the DB normalizer keeps
|
|
* (src/lib/db/compression.ts STACKED_PIPELINE_ENGINE_IDS). Issue #6747: a 5-engine
|
|
* discriminator rejected session-dedup/ccr/headroom/relevance/llmlingua on write even
|
|
* though GET returned them.
|
|
*/
|
|
export const stackedPipelineStepSchema = z.discriminatedUnion("engine", [
|
|
z
|
|
.object({
|
|
engine: z.literal("lite"),
|
|
intensity: z.literal("lite").optional(),
|
|
config: noConfigSchema.optional(),
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("caveman"),
|
|
intensity: cavemanIntensitySchema.optional(),
|
|
config: cavemanConfigSchema.optional(),
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("aggressive"),
|
|
// #6747: previously only "standard"; GET / engines.level may echo "ultra"
|
|
intensity: z.enum(["standard", "ultra"]).optional(),
|
|
config: aggressiveConfigSchema.optional(),
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("ultra"),
|
|
intensity: z.literal("ultra").optional(),
|
|
config: ultraConfigSchema.optional(),
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("rtk"),
|
|
intensity: rtkIntensitySchema.optional(),
|
|
config: rtkConfigSchema.optional(),
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("codex-responses"),
|
|
intensity: z.string().optional(),
|
|
config: codexResponsesConfigSchema.optional(),
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("session-dedup"),
|
|
intensity: z.string().optional(),
|
|
config: structuralStepConfigSchema,
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("ccr"),
|
|
intensity: z.string().optional(),
|
|
config: structuralStepConfigSchema,
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("headroom"),
|
|
intensity: z.string().optional(),
|
|
config: structuralStepConfigSchema,
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("relevance"),
|
|
intensity: z.string().optional(),
|
|
config: structuralStepConfigSchema,
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("llmlingua"),
|
|
intensity: z.string().optional(),
|
|
config: structuralStepConfigSchema,
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
engine: z.literal("omniglyph"),
|
|
intensity: z.string().optional(),
|
|
config: structuralStepConfigSchema,
|
|
})
|
|
.strict(),
|
|
]);
|
|
|
|
/**
|
|
* Canonical engine → selectable-intensities map for the named-combos pipeline editor
|
|
* (Engine Combos UI). This is the SINGLE source of truth shared by the dashboard
|
|
* dropdowns and `stackedPipelineStepSchema`: every engine/intensity offered here is,
|
|
* by construction, accepted by the API update schema.
|
|
*
|
|
* Every ENGINE_CATALOG id must appear here (empty intensity list = no level selector).
|
|
* Parity with `stackedPipelineStepSchema` is guarded by unit tests (#4955 / #6747).
|
|
* #4955 fixed a UI/schema drift by shrinking the UI; #6747 expands the schema so the
|
|
* full catalog (and GET stackedPipeline) can round-trip on PUT.
|
|
*/
|
|
export const STACKED_PIPELINE_ENGINE_INTENSITIES: Record<string, readonly string[]> = {
|
|
// Order matches ENGINE_CATALOG stackPriority for readability
|
|
"session-dedup": [],
|
|
ccr: [],
|
|
lite: ["lite"],
|
|
rtk: ["minimal", "standard", "aggressive"],
|
|
"codex-responses": [],
|
|
headroom: [],
|
|
relevance: [],
|
|
caveman: ["lite", "full", "ultra"],
|
|
aggressive: ["standard", "ultra"],
|
|
llmlingua: [],
|
|
omniglyph: [],
|
|
ultra: ["ultra"],
|
|
};
|
|
|
|
export const liteConfigSchema = z
|
|
.object({
|
|
compressToolResults: z.boolean().optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const engineToggleSchema = z.object({
|
|
enabled: z.boolean(),
|
|
level: z.string().optional(),
|
|
});
|
|
|
|
export const contextBudgetModeSchema = z.enum(["floor", "replace-autotrigger", "off"]);
|
|
export const contextBudgetPolicySchema = z.enum(["reserve-output", "percentage", "absolute"]);
|
|
|
|
export const contextBudgetLadderStageSchema = z
|
|
.object({
|
|
engine: z.string().trim().min(1),
|
|
intensity: z.string().optional(),
|
|
})
|
|
.strict();
|
|
|
|
// Adaptive context-budget "dial" (#7005): the compute engine shipped in PR #4716 but was
|
|
// never wired to this update schema, so any PUT containing `contextBudget` was rejected
|
|
// with 400. Mirrors ContextBudgetConfig (open-sse/services/compression/adaptiveCompression/
|
|
// types.ts) and the `.strict()` pattern used by ultraConfigSchema/aggressiveConfigSchema.
|
|
export const contextBudgetConfigSchema = z
|
|
.object({
|
|
mode: contextBudgetModeSchema.optional(),
|
|
policy: contextBudgetPolicySchema.optional(),
|
|
outputReserve: z.number().int().min(0).optional(),
|
|
safetyMargin: z.number().int().min(0).optional(),
|
|
pct: z.number().min(0).max(1).optional(),
|
|
absoluteBudget: z.number().int().min(0).optional(),
|
|
ladderOverride: z.array(contextBudgetLadderStageSchema).optional(),
|
|
})
|
|
.strict();
|
|
|
|
/**
|
|
* Perfil semântico do OmniGlyph. O perfil é um TETO: o pacote não deixa um
|
|
* override reabrir uma lane que o perfil fechou. `aggressive` é o default e a
|
|
* política que os recibos publicados mediram; `coding-safe`/`balanced` mantêm
|
|
* system, schemas de tools e tool results nativos, e não comprimem nada até a
|
|
* sessão acumular histórico.
|
|
*/
|
|
export const omniglyphConfigSchema = z
|
|
.object({
|
|
profile: z.enum(["coding-safe", "balanced", "aggressive", "passthrough"]),
|
|
})
|
|
.strict();
|
|
|
|
export const compressionSettingsUpdateSchema = z
|
|
.object({
|
|
enabled: z.boolean().optional(),
|
|
defaultMode: compressionModeSchema.optional(),
|
|
autoTriggerMode: compressionModeSchema.optional(),
|
|
autoTriggerTokens: z.number().int().min(0).optional(),
|
|
cacheMinutes: z.number().int().min(1).max(60).optional(),
|
|
preserveSystemPrompt: z.boolean().optional(),
|
|
preserveSystemPromptMode: z.enum(["always", "whenNoCache", "never"]).optional(),
|
|
mcpDescriptionCompressionEnabled: z.boolean().optional(),
|
|
comboOverrides: z.record(z.string(), compressionModeSchema).optional(),
|
|
compressionComboId: z.string().trim().min(1).nullable().optional(),
|
|
stackedPipeline: z.array(stackedPipelineStepSchema).optional(),
|
|
cavemanConfig: cavemanConfigSchema.optional(),
|
|
cavemanOutputMode: cavemanOutputModeSchema.optional(),
|
|
outputStyles: z.array(outputStyleSelectionSchema).optional(),
|
|
rtkConfig: rtkConfigSchema.optional(),
|
|
codexResponsesConfig: codexResponsesConfigSchema.optional(),
|
|
languageConfig: languageConfigSchema.optional(),
|
|
aggressive: aggressiveConfigSchema.optional(),
|
|
ultra: ultraConfigSchema.optional(),
|
|
lite: liteConfigSchema.optional(),
|
|
headroom: headroomConfigSchema.optional(),
|
|
sessionDedup: sessionDedupConfigSchema.optional(),
|
|
ccr: ccrConfigSchema.optional(),
|
|
contextBudget: contextBudgetConfigSchema.optional(),
|
|
contextEditing: contextEditingConfigSchema.optional(),
|
|
omniglyph: omniglyphConfigSchema.optional(),
|
|
liveZone: z.object({ enabled: z.boolean() }).strict().optional(),
|
|
engines: z.record(z.string(), engineToggleSchema).optional(),
|
|
enginesExplicit: z.boolean().optional(),
|
|
activeComboId: z.string().nullable().optional(),
|
|
ultraEngine: z.enum(["heuristic", "slm"]).optional(),
|
|
ultraSlmPrewarm: z.boolean().optional(),
|
|
// #8034 — per-model/endpoint compression exclusion patterns. Bounded length/size so a
|
|
// pathological PUT body can't blow up the per-request matcher; normalizeCompressionExclusions
|
|
// (open-sse/services/compression/exclusions.ts) is the authoritative post-read normalizer.
|
|
exclusions: z.array(z.string().max(200)).max(200).optional(),
|
|
})
|
|
.strict();
|
|
|
|
export const compressionPreviewConfigSchema = compressionSettingsUpdateSchema;
|