Files
OmniRoute/tests/integration/chatcore-compression-integration.test.ts
Diego Rodrigues de Sa e Souza 3432dfd280 Release v3.6.9 (#1404)
* test: resolve typescript strictness complaints in unit tests

* Update Claude Code obfuscation to version 2.1.114 (#1403)

* fix(cloud-code): scope thinking stripping to executor boundaries (#1401)

* fix(cloud-code): scope thinking stripping to executors

* fix(cloud-code): guard antigravity normalized body

* Update Claude Code obfuscation to version 2.1.114

- Update Claude Code version from 2.1.87 to 2.1.114
- Update X-Stainless-Package-Version from 0.80.0 to 0.81.0
- Add new beta flags: redact-thinking-2026-02-12, advisor-tool-2026-03-01, advanced-tool-use-2025-11-20
- Add missing headers: anthropic-version, anthropic-dangerous-direct-browser-access, x-app, X-Stainless-Timeout
- Add all X-Stainless-* headers (Arch, Lang, OS, Runtime, Runtime-Version, Retry-Count)
- Fix accept-encoding header: identity -> gzip, deflate, br, zstd
- Add connection: keep-alive header
- Update tool name mapping: add lsp, apply_patch, websearch

These changes ensure that requests from OpenCode through Omniroute are indistinguishable from genuine Claude Code 2.1.114 requests, allowing proper authentication with Anthropic's API without triggering extra credits errors.

* fix: resolve CodeQL password hash alert and TruffleHog CI failure

---------

Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Nikolay Popov <ekklesio.dev@gmail.com>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>

* fix(claude-code): scope obfuscation to cli clients and fix tests

* docs(workflows): enforce PR merge instead of manual close

* docs(changelog): update 3.6.9 notes with missing PR 1403 and fixes

* docs(workflows): update generate-release to use full changelog for PR body

* fix(tsc): silence baseUrl deprecation warnings for TS 5.5+

* fix(chatcore): apply proactive compression before provider translation (#1406)

Integrated into release/v3.6.9

* docs(changelog): add PR 1406

* Makes text visible in dark-mode (#1409)

Integrated into release/v3.6.9

* docs(changelog): add PR 1409

* chore: save local work

* chore(release): sync version references to 3.6.9

* fix(codex): prevent proactive token refresh consumption and strip background parameter

* ci: shard long-running suites and relax timeouts

* ci: allow manual CI dispatch for release branches

* feat(skills): provider-aware marketplace UX, scored AUTO injection, and memory pipeline hardening (#1411)

* fix/400 for GeminiCLI(add "ref" in GEMINI_UNSUPPORTED_SCHEMA_KEYS)

* feat(cc-compatible): align request shape with Claude CLI

* fix(cc-compatible): add Claude CLI system skeleton for OpenAI input

* preserve reasoning when translating chat to responses (#1414)

Integrated into release/v3.6.9

* fix(skills): optimize AUTO scoring and include Responses input context (#1418)

Integrated into release/v3.6.9

* chore: fix TS errors and update review-prs workflow

* fix(api): stop sending unsupported Gemini and Codex parameters

Prevent Gemini request translation from injecting default
thoughtSignature values that the upstream API strictly validates and
rejects. Only preserve real signatures resolved from prior upstream
responses, and strip additionalProperties from Gemini function schemas
to avoid 400 "Unknown name" errors.

Also remove fallback-injected session_id and conversation_id fields
before sending Codex requests, and restore compatibility with the
legacy OUTBOUND_SSRF_GUARD_ENABLED flag when determining whether
private provider URLs are allowed.

Updates the Gemini translator and regression tests for issue #1410
and related 400 error cases.

* fix(core): stabilization fixes for token refresh, usage translation, and testing

- Update Codex token refresh detection logic
- Mark provider connections invalid on unrecoverable refresh error
- Fix Claude usage translation under-reporting cached tokens
- Update test expectations
- Update CHANGELOG.md for v3.6.9

* fix(auth): reload fresh token state and unify expiry persistence

Refresh checks now re-read the latest stored provider connection before
attempting rotation so they do not use stale refresh tokens captured by
an earlier sweep.

Token updates also persist both expiresAt and tokenExpiresAt across the
health check, usage-limit refresh path, and SSE refresh flow. This keeps
known token expiry metadata in sync and avoids interval-based refreshes
for connections whose tokens are still valid well into the future.

* fix: resolve SSRF environment static evaluation bug (#1427)

Fix import aliases and strict TS typings for tests and ACP agents.

* test: resolve remaining strict type errors in test files

* test: fix provider service assertion for anthropic-compatible header

* fix(codex): respect openaiStoreEnabled setting during native passthrough (#1432)

* fix(codex): fix token refresh unrecoverable detection for expired tokens

* fix(ci): restore release v3.6.9 build and flaky tests

* fix(cc-compatible): trim default OpenAI system skeleton (#1433)

Integrated into release/v3.6.9

* fix: prevent masked API keys from being written to CLI tool configs (#1435)

* feat: mark Qwen provider as deprecated and add deprecation warning to CLI tool (#1437)

* docs(changelog): comprehensive v3.6.9 update with all 59 commits since v3.6.8

* test(ci): align qwen guide settings assertions

* fix(security): resolve CodeQL alert 163 for incomplete URL sanitization in Qwen CLI settings

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: Nikolay Popov <74762779+nikolay-popov-ideogram@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Nikolay Popov <ekklesio.dev@gmail.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Tim Massey <tim-massey@users.noreply.github.com>
Co-authored-by: Paijo <oyi77@users.noreply.github.com>
Co-authored-by: dail45 <dail45@yandex.ru>
Co-authored-by: R.D. <rogerproself@gmail.com>
2026-04-19 19:50:30 -03:00

430 lines
14 KiB
TypeScript

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";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-compression-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.REQUIRE_API_KEY = "false";
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "test-compression-secret";
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const readCacheDb = await import("../../src/lib/db/readCache.ts");
const combosDb = await import("../../src/lib/db/combos.ts");
const { handleChatCore } = await import("../../open-sse/handlers/chatCore.ts");
const { estimateTokens, getTokenLimit } = await import("../../open-sse/services/contextManager.ts");
const { resetAllAvailability } = await import("../../src/domain/modelAvailability.ts");
const { resetAllCircuitBreakers } = await import("../../src/shared/utils/circuitBreaker.ts");
const originalFetch = globalThis.fetch;
async function resetStorage() {
globalThis.fetch = originalFetch;
resetAllAvailability();
resetAllCircuitBreakers();
readCacheDb.invalidateDbCache();
await new Promise((resolve) => setTimeout(resolve, 20));
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.beforeEach(async () => {
await resetStorage();
});
test.after(async () => {
globalThis.fetch = originalFetch;
core.closeDbInstance();
try {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
} catch {}
});
test("chatCore integration: compressContext called proactively when context exceeds 85% threshold", async () => {
const provider = "openai";
const model = "gpt-4";
// Create multiple messages with history that can be compressed
// Use the same pattern as test 3 which successfully tests compression
const body = {
model,
messages: [
{ role: "system", content: "You are helpful." },
{ role: "user", content: "x".repeat(50000) },
{ role: "assistant", content: "Response 1" },
{ role: "user", content: "x".repeat(50000) },
{ role: "assistant", content: "Response 2" },
{ role: "user", content: "x".repeat(50000) },
{ role: "assistant", content: "Response 3" },
{ role: "user", content: "Final question" },
],
};
// Create provider connection
const connectionId = await providersDb.createProviderConnection({
provider,
apiKey: "test-key",
isActive: true,
});
// Mock fetch to capture the request
let capturedBody: any = null;
globalThis.fetch = async (url: string | URL | Request, init?: RequestInit) => {
if (init?.body) {
capturedBody = JSON.parse(init.body as string);
}
return new Response(
JSON.stringify({
choices: [{ message: { role: "assistant", content: "test" } }],
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};
try {
const result = await handleChatCore({
body,
modelInfo: { provider, model },
credentials: { apiKey: "test-key" },
log: {
debug: () => {},
info: () => {},
warn: () => {},
error: () => {},
},
clientRawRequest: { endpoint: "/v1/chat/completions", headers: new Map() },
connectionId,
});
assert.ok(result.success, "Request should succeed");
assert.ok(capturedBody, "Fetch should have been called");
// Verify that compression preserved the message structure
assert.ok(Array.isArray(capturedBody.messages), "Messages should remain an array");
assert.ok(capturedBody.messages.length > 0, "Messages should not be empty");
// Verify that the final question was preserved (compression keeps recent messages)
const lastMessage = capturedBody.messages[capturedBody.messages.length - 1];
assert.equal(lastMessage.content, "Final question", "Last user message should be preserved");
} finally {
globalThis.fetch = originalFetch;
}
});
test("chatCore integration: compressContext NOT called when context is below 85% threshold", async () => {
const provider = "openai";
const model = "gpt-4";
const contextLimit = getTokenLimit(provider, model);
const threshold = Math.floor(contextLimit * 0.85);
const smallMessage = "Hello, how are you?";
const body = {
model,
messages: [
{ role: "system", content: "You are helpful." },
{ role: "user", content: smallMessage },
],
};
const estimatedTokens = estimateTokens(JSON.stringify(body.messages));
assert.ok(
estimatedTokens < threshold,
`Expected ${estimatedTokens} to be below threshold ${threshold}`
);
// Create provider connection
const connectionId = await providersDb.createProviderConnection({
provider,
apiKey: "test-key",
isActive: true,
});
// Mock fetch to capture the request
let capturedBody: any = null;
globalThis.fetch = async (url: string | URL | Request, init?: RequestInit) => {
if (init?.body) {
capturedBody = JSON.parse(init.body as string);
}
return new Response(
JSON.stringify({
choices: [{ message: { role: "assistant", content: "test" } }],
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};
try {
const result = await handleChatCore({
body,
modelInfo: { provider, model },
credentials: { apiKey: "test-key" },
log: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
clientRawRequest: { endpoint: "/v1/chat/completions", headers: new Map() },
connectionId,
});
assert.ok(result.success, "Request should succeed");
assert.ok(capturedBody, "Fetch should have been called");
// Verify NO compression occurred
const originalTokens = estimateTokens(JSON.stringify(body.messages));
const finalTokens = estimateTokens(JSON.stringify(capturedBody.messages));
assert.equal(
finalTokens,
originalTokens,
`Context should NOT be compressed: ${finalTokens} === ${originalTokens}`
);
} finally {
globalThis.fetch = originalFetch;
}
});
test("chatCore integration: compression preserves message structure", async () => {
const provider = "openai";
const model = "gpt-4";
const body = {
model,
messages: [
{ role: "system", content: "You are helpful." },
{ role: "user", content: "x".repeat(50000) },
{ role: "assistant", content: "Response 1" },
{ role: "user", content: "x".repeat(50000) },
{ role: "assistant", content: "Response 2" },
{ role: "user", content: "Final question" },
],
};
// Create provider connection
const connectionId = await providersDb.createProviderConnection({
provider,
apiKey: "test-key",
isActive: true,
});
// Mock fetch to capture the request
let capturedBody: any = null;
globalThis.fetch = async (url: string | URL | Request, init?: RequestInit) => {
if (init?.body) {
capturedBody = JSON.parse(init.body as string);
}
return new Response(
JSON.stringify({
choices: [{ message: { role: "assistant", content: "test" } }],
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};
try {
const result = await handleChatCore({
body,
modelInfo: { provider, model },
credentials: { apiKey: "test-key" },
log: {
debug: (tag: string, msg: string) => console.log(`[DEBUG] ${tag}: ${msg}`),
info: (tag: string, msg: string) => console.log(`[INFO] ${tag}: ${msg}`),
warn: (tag: string, msg: string) => console.log(`[WARN] ${tag}: ${msg}`),
error: (tag: string, msg: string) => console.log(`[ERROR] ${tag}: ${msg}`),
},
clientRawRequest: { endpoint: "/v1/chat/completions", headers: new Map() },
connectionId,
});
assert.ok(result.success, "Request should succeed");
assert.ok(capturedBody, "Fetch should have been called");
assert.ok(Array.isArray(capturedBody.messages), "Messages should remain an array");
assert.ok(capturedBody.messages.length > 0, "Messages should not be empty");
const hasSystem = capturedBody.messages.some((m: any) => m.role === "system");
assert.ok(hasSystem, "System message should be preserved");
const lastMessage = capturedBody.messages[capturedBody.messages.length - 1];
assert.equal(lastMessage.content, "Final question", "Last user message should be preserved");
} finally {
globalThis.fetch = originalFetch;
}
});
test("chatCore integration: compression handles tool messages", async () => {
const provider = "openai";
const model = "gpt-4";
const longToolOutput = "x".repeat(10000);
const body = {
model,
messages: [
{ role: "system", content: "You are helpful." },
{ role: "user", content: "Run the tool" },
{ role: "assistant", content: "Running tool", tool_calls: [{ id: "t1", type: "function" }] },
{ role: "tool", content: longToolOutput, tool_call_id: "t1" },
{ role: "user", content: "What's the result?" },
],
};
// Create provider connection
const connectionId = await providersDb.createProviderConnection({
provider,
apiKey: "test-key",
isActive: true,
});
// Mock fetch to capture the request
let capturedBody: any = null;
globalThis.fetch = async (url: string | URL | Request, init?: RequestInit) => {
if (init?.body) {
capturedBody = JSON.parse(init.body as string);
}
return new Response(
JSON.stringify({
choices: [{ message: { role: "assistant", content: "test" } }],
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};
try {
const result = await handleChatCore({
body,
modelInfo: { provider, model },
credentials: { apiKey: "test-key" },
log: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
clientRawRequest: { endpoint: "/v1/chat/completions", headers: new Map() },
connectionId,
});
assert.ok(result.success, "Request should succeed");
assert.ok(capturedBody, "Fetch should have been called");
const toolMessage = capturedBody.messages.find((m: any) => m.role === "tool");
assert.ok(toolMessage, "Tool message should exist");
// Tool message should be truncated if compression was triggered
if (toolMessage.content.length < longToolOutput.length) {
assert.ok(
toolMessage.content.includes("[truncated]"),
"Tool message should have truncation marker"
);
}
} finally {
globalThis.fetch = originalFetch;
}
});
test("chatCore integration: combo requests run proactive compression before Kiro translation", async () => {
const provider = "kiro";
const model = "claude-sonnet-4.5";
const connectionId = await providersDb.createProviderConnection({
provider,
apiKey: "test-key",
isActive: true,
});
await combosDb.createCombo({
name: "test-kiro-compression-combo",
strategy: "priority",
models: [
{
kind: "model",
model: `${provider}/${model}`,
connectionId,
},
],
});
const body = {
model: "combo/test-kiro-compression-combo",
stream: false,
messages: [
{ role: "system", content: "You are helpful." },
{ role: "user", content: "x".repeat(50000) },
{ role: "assistant", content: "Ack 1" },
{ role: "user", content: "x".repeat(50000) },
{ role: "assistant", content: "Ack 2" },
{ role: "user", content: "x".repeat(50000) },
{ role: "assistant", content: "Ack 3" },
{ role: "user", content: "Please summarize everything." },
],
};
let capturedTranslatedBody: Record<string, unknown> | null = null;
globalThis.fetch = async (_url: string | URL | Request, init?: RequestInit) => {
if (init?.body) {
capturedTranslatedBody = JSON.parse(init.body as string) as Record<string, unknown>;
}
return new Response(
JSON.stringify({
choices: [{ message: { role: "assistant", content: "ok" } }],
usage: { prompt_tokens: 11, completion_tokens: 5, total_tokens: 16 },
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
};
try {
const result = await handleChatCore({
body,
modelInfo: { provider, model },
credentials: { apiKey: "test-key" },
log: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
clientRawRequest: { endpoint: "/v1/chat/completions", headers: new Map() },
connectionId,
isCombo: true,
comboName: "test-kiro-compression-combo",
});
// Kiro response translation in this integration harness may fail depending on upstream
// payload shape, but the regression target is request-side behavior before translation.
assert.ok(result, "Handler should return a result object");
assert.ok(capturedTranslatedBody, "Translated body should be sent upstream");
// Ensure request was translated to Kiro shape (messages are not sent directly upstream).
const conversationState = capturedTranslatedBody?.conversationState as
| Record<string, unknown>
| undefined;
assert.ok(conversationState, "Kiro translated request should include conversationState");
const history = Array.isArray(conversationState?.history)
? (conversationState.history as unknown[])
: [];
assert.ok(
history.length < body.messages.length - 1,
"History should be reduced by proactive compression before translation"
);
const currentMessage = conversationState?.currentMessage as Record<string, unknown> | undefined;
const userInputMessage = currentMessage?.userInputMessage as
| Record<string, unknown>
| undefined;
const currentContent =
typeof userInputMessage?.content === "string" ? userInputMessage.content : "";
assert.match(currentContent, /Please summarize everything\./);
} finally {
globalThis.fetch = originalFetch;
}
});