-
+
Documentation
-
+
Terms
diff --git a/src/shared/components/docs/DocsBreadcrumbs.tsx b/src/shared/components/docs/DocsBreadcrumbs.tsx
index 42c2be99e8..231cb1798b 100644
--- a/src/shared/components/docs/DocsBreadcrumbs.tsx
+++ b/src/shared/components/docs/DocsBreadcrumbs.tsx
@@ -26,11 +26,7 @@ export default function DocsBreadcrumbs({ labels, className }: DocsBreadcrumbsPr
aria-label="Breadcrumb"
className={cn("flex items-center gap-2 text-sm text-text-muted mb-6", className)}
>
-<<<<<<< HEAD
-
-=======
->>>>>>> upstream/release/v3.7.9
Docs
{crumbs.map((crumb, i) => (
diff --git a/src/shared/components/docs/Tabs.stories.tsx b/src/shared/components/docs/Tabs.stories.tsx
index e02ea44f79..3d8521e6c4 100644
--- a/src/shared/components/docs/Tabs.stories.tsx
+++ b/src/shared/components/docs/Tabs.stories.tsx
@@ -14,21 +14,6 @@ export const Default: Story = {
tabs: [
{
label: "Installation",
-<<<<<<< HEAD
- content:
Run npm install to get started.
,
- },
- {
- label: "Configuration",
- content:
Edit your config.json file.
,
- },
- {
- label: "Usage",
- content:
Import the provider and start routing.
,
- },
- ],
- },
-};
-=======
content: '
Run npm install to get started.
',
},
{ label: "Configuration", content: '
Edit your config.json file.
' },
@@ -39,4 +24,3 @@ export const Default: Story = {
],
},
};
->>>>>>> upstream/release/v3.7.9
diff --git a/tests/unit/account-fallback-service.test.ts b/tests/unit/account-fallback-service.test.ts
index 4cfdda2913..4d0d9e5a77 100644
--- a/tests/unit/account-fallback-service.test.ts
+++ b/tests/unit/account-fallback-service.test.ts
@@ -291,7 +291,8 @@ test("shouldMarkAccountExhaustedFrom429 skips connection poisoning for compatibl
shouldMarkAccountExhaustedFrom429("openai-compatible-custom-node", "any-model"),
false
);
- assert.equal(shouldMarkAccountExhaustedFrom429("openai", "gpt-4o-mini"), true);
+ assert.equal(shouldMarkAccountExhaustedFrom429("openai", "gpt-4o-mini"), false);
+ assert.equal(shouldMarkAccountExhaustedFrom429("claude", "claude-sonnet-4-6"), true);
});
test("hasPerModelQuota returns true for GitHub Copilot provider (#1624)", () => {
diff --git a/tests/unit/antigravity-model-aliases.test.ts b/tests/unit/antigravity-model-aliases.test.ts
index 070e42d2d4..44a20fcbea 100644
--- a/tests/unit/antigravity-model-aliases.test.ts
+++ b/tests/unit/antigravity-model-aliases.test.ts
@@ -76,6 +76,6 @@ test("AntigravityExecutor.transformRequest keeps Claude bridge output cap and st
projectId: "project-1",
});
- assert.equal(result.request.generationConfig.maxOutputTokens, 16_384);
- assert.equal(result.request.generationConfig.thinkingConfig, undefined);
+ assert.equal(result.request.max_tokens, 16_384);
+ assert.equal(result.request.thinking, undefined);
});
diff --git a/tests/unit/chatcore-translation-paths.test.ts b/tests/unit/chatcore-translation-paths.test.ts
index 6eb72e4b0c..57669d0bf9 100644
--- a/tests/unit/chatcore-translation-paths.test.ts
+++ b/tests/unit/chatcore-translation-paths.test.ts
@@ -840,7 +840,7 @@ test("chatCore strips unsupported reasoning params and caps provider token field
assert.equal(call.body.temperature, undefined);
assert.equal(call.body.presence_penalty, undefined);
- assert.equal(call.body.max_tokens, 16384);
+ assert.equal(call.body.max_tokens, undefined);
assert.equal(call.body.max_completion_tokens, 16384);
});
diff --git a/tests/unit/chatgpt-web.test.ts b/tests/unit/chatgpt-web.test.ts
index dc401b20e1..de24971381 100644
--- a/tests/unit/chatgpt-web.test.ts
+++ b/tests/unit/chatgpt-web.test.ts
@@ -1092,6 +1092,7 @@ test("Provider registry: chatgpt-web exposes the current ChatGPT Web model catal
assert.deepEqual(ids, [
"gpt-5.5-pro",
"gpt-5.5-thinking",
+ "gpt-5.5",
"gpt-5.4-pro",
"gpt-5.4-thinking",
"gpt-5.4-thinking-mini",
diff --git a/tests/unit/docs-site-overhaul.test.ts b/tests/unit/docs-site-overhaul.test.ts
index 6abab48948..202422f4e0 100644
--- a/tests/unit/docs-site-overhaul.test.ts
+++ b/tests/unit/docs-site-overhaul.test.ts
@@ -14,8 +14,19 @@ import { SEARCH_INDEX } from "../../src/app/docs/lib/searchIndex";
// docsNavigation structure
// ──────────────────────────────────────────────
-test("docsNavigation has 6 sections", () => {
- assert.equal(docsNavigation.length, 6);
+test("docsNavigation has expected sections", () => {
+ assert.deepEqual(
+ docsNavigation.map((section) => section.title),
+ [
+ "Getting Started",
+ "Features",
+ "API & Protocols",
+ "Deployment",
+ "Operations",
+ "Development",
+ "Other",
+ ]
+ );
});
test("every section has title and items", () => {
@@ -147,12 +158,12 @@ test("extractHeadings strips bold and code from heading text", () => {
test("renderMarkdown converts headings to HTML", () => {
const html = renderMarkdown("# Title\n## Section\n### Subsection\n#### Details");
- assert.ok(html.includes('
{
diff --git a/tests/unit/encryption.spec.ts b/tests/unit/encryption.spec.ts
index c9b5658105..f490f4b3a1 100644
--- a/tests/unit/encryption.spec.ts
+++ b/tests/unit/encryption.spec.ts
@@ -66,6 +66,41 @@ describe("encryption module", () => {
});
});
+ describe("migrateLegacyEncryptedString() behavior", () => {
+ it("should upgrade legacy encrypted tokens to static key tokens", async () => {
+ const secret = "test-secret-key-12345";
+ const plaintext = "legacy-token";
+ const legacyEncrypted = createLegacyEncrypted(plaintext, secret);
+
+ vi.stubEnv("STORAGE_ENCRYPTION_KEY", secret);
+ vi.resetModules();
+
+ const { migrateLegacyEncryptedString, decrypt } = await import("@/lib/db/encryption");
+
+ const result = migrateLegacyEncryptedString(legacyEncrypted);
+ expect(result.updated).toBe(true);
+ expect(result.value).not.toBe(legacyEncrypted);
+ expect(result.value).toMatch(/^enc:v1:/);
+
+ const decrypted = decrypt(result.value);
+ expect(decrypted).toBe(plaintext);
+ });
+
+ it("should NOT upgrade static-key encrypted tokens", async () => {
+ vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
+ vi.resetModules();
+
+ const { encrypt, migrateLegacyEncryptedString } = await import("@/lib/db/encryption");
+
+ const plaintext = "modern-token";
+ const encrypted = encrypt(plaintext);
+
+ const result = migrateLegacyEncryptedString(encrypted!);
+ expect(result.updated).toBe(false);
+ expect(result.value).toBe(encrypted);
+ });
+ });
+
describe("passthrough mode: no STORAGE_ENCRYPTION_KEY set → plaintext stored", () => {
it("should return plaintext when encryption key is not set", async () => {
// No STORAGE_ENCRYPTION_KEY set
diff --git a/tests/unit/encryption.test.ts b/tests/unit/encryption.test.ts
deleted file mode 100644
index 327aa0562d..0000000000
--- a/tests/unit/encryption.test.ts
+++ /dev/null
@@ -1,327 +0,0 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
-import { createCipheriv, randomBytes, scryptSync, createHash } from "crypto";
-
-// Test helper to manually create legacy-encrypted values
-function createLegacyEncrypted(plaintext: string, secret: string): string {
- const ALGORITHM = "aes-256-gcm";
- const IV_LENGTH = 16;
- const KEY_LENGTH = 32;
- const PREFIX = "enc:v1:";
-
- // OLD dynamic salt derivation (the bug)
- const dynamicSalt = createHash("sha256").update(secret).digest().slice(0, 16);
- const legacyKey = scryptSync(secret, dynamicSalt, KEY_LENGTH);
-
- const iv = randomBytes(IV_LENGTH);
- const cipher = createCipheriv(ALGORITHM, legacyKey, iv);
-
- let encrypted = cipher.update(plaintext, "utf8", "hex");
- encrypted += cipher.final("hex");
- const authTag = cipher.getAuthTag().toString("hex");
-
- return `${PREFIX}${iv.toString("hex")}:${encrypted}:${authTag}`;
-}
-
-describe("encryption module", () => {
- beforeEach(() => {
- // Clear all env vars and reset modules before each test
- vi.unstubAllEnvs();
- vi.resetModules();
- });
-
- afterEach(() => {
- vi.unstubAllEnvs();
- vi.resetModules();
- });
-
- describe("encrypt/decrypt roundtrip with static key (PRIMARY path)", () => {
- it("should encrypt and decrypt a value successfully", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encrypt, decrypt } = await import("@/lib/db/encryption");
-
- const plaintext = "my-secret-api-key";
- const encrypted = encrypt(plaintext);
-
- expect(encrypted).toBeDefined();
- expect(encrypted).toMatch(/^enc:v1:/);
- expect(encrypted).not.toBe(plaintext);
-
- const decrypted = decrypt(encrypted!);
- expect(decrypted).toBe(plaintext);
- });
-
- it("should handle multiple encrypt/decrypt cycles", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encrypt, decrypt } = await import("@/lib/db/encryption");
-
- const values = ["token1", "token2", "token3"];
- const encrypted = values.map((v) => encrypt(v));
- const decrypted = encrypted.map((e) => decrypt(e!));
-
- expect(decrypted).toEqual(values);
- });
- });
-
- describe("migrateLegacyEncryptedString() behavior", () => {
- it("should upgrade legacy encrypted tokens to static key tokens", async () => {
- const secret = "test-secret-key-12345";
- const plaintext = "legacy-token";
- const legacyEncrypted = createLegacyEncrypted(plaintext, secret);
-
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", secret);
- vi.resetModules();
-
- const { migrateLegacyEncryptedString, decrypt } = await import("@/lib/db/encryption");
-
- const result = migrateLegacyEncryptedString(legacyEncrypted);
- expect(result.updated).toBe(true);
- expect(result.value).not.toBe(legacyEncrypted);
- expect(result.value).toMatch(/^enc:v1:/);
-
- const decrypted = decrypt(result.value);
- expect(decrypted).toBe(plaintext);
- });
-
- it("should NOT upgrade static-key encrypted tokens", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encrypt, migrateLegacyEncryptedString } = await import("@/lib/db/encryption");
-
- const plaintext = "modern-token";
- const encrypted = encrypt(plaintext);
-
- const result = migrateLegacyEncryptedString(encrypted!);
- expect(result.updated).toBe(false);
- expect(result.value).toBe(encrypted);
- });
- });
-
- describe("passthrough mode: no STORAGE_ENCRYPTION_KEY set → plaintext stored", () => {
- it("should return plaintext when encryption key is not set", async () => {
- // No STORAGE_ENCRYPTION_KEY set
- vi.resetModules();
-
- const { encrypt, decrypt, isEncryptionEnabled } = await import("@/lib/db/encryption");
-
- expect(isEncryptionEnabled()).toBe(false);
-
- const plaintext = "my-api-key";
- const encrypted = encrypt(plaintext);
-
- expect(encrypted).toBe(plaintext);
-
- const decrypted = decrypt(plaintext);
- expect(decrypted).toBe(plaintext);
- });
-
- it("should handle null and undefined in passthrough mode", async () => {
- vi.resetModules();
-
- const { encrypt, decrypt } = await import("@/lib/db/encryption");
-
- expect(encrypt(null)).toBeNull();
- expect(encrypt(undefined)).toBeUndefined();
- expect(decrypt(null)).toBeNull();
- expect(decrypt(undefined)).toBeUndefined();
- });
- });
-
- describe("encryptConnectionFields / decryptConnectionFields helpers", () => {
- it("should encrypt all credential fields in a connection object", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encryptConnectionFields } = await import("@/lib/db/encryption");
-
- const conn = {
- id: "conn-123",
- apiKey: "plain-api-key",
- accessToken: "plain-access-token",
- refreshToken: "plain-refresh-token",
- idToken: "plain-id-token",
- };
-
- const encrypted = encryptConnectionFields(conn);
-
- expect(encrypted.id).toBe("conn-123");
- expect(encrypted.apiKey).toMatch(/^enc:v1:/);
- expect(encrypted.accessToken).toMatch(/^enc:v1:/);
- expect(encrypted.refreshToken).toMatch(/^enc:v1:/);
- expect(encrypted.idToken).toMatch(/^enc:v1:/);
- });
-
- it("should decrypt all credential fields in a connection object", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encryptConnectionFields, decryptConnectionFields } = await import("@/lib/db/encryption");
-
- const conn = {
- id: "conn-123",
- apiKey: "plain-api-key",
- accessToken: "plain-access-token",
- refreshToken: "plain-refresh-token",
- idToken: "plain-id-token",
- };
-
- const encrypted = encryptConnectionFields({ ...conn });
- const decrypted = decryptConnectionFields(encrypted);
-
- expect(decrypted.id).toBe("conn-123");
- expect(decrypted.apiKey).toBe("plain-api-key");
- expect(decrypted.accessToken).toBe("plain-access-token");
- expect(decrypted.refreshToken).toBe("plain-refresh-token");
- expect(decrypted.idToken).toBe("plain-id-token");
- });
- });
-
- describe("edge cases: null/undefined inputs, already-encrypted, malformed ciphertext", () => {
- it("should handle null and undefined inputs", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encrypt, decrypt } = await import("@/lib/db/encryption");
-
- expect(encrypt(null)).toBeNull();
- expect(encrypt(undefined)).toBeUndefined();
- expect(decrypt(null)).toBeNull();
- expect(decrypt(undefined)).toBeUndefined();
- });
-
- it("should not double-encrypt already-encrypted values", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encrypt } = await import("@/lib/db/encryption");
-
- const plaintext = "my-secret";
- const encrypted = encrypt(plaintext);
-
- expect(encrypted).toMatch(/^enc:v1:/);
-
- // Try to encrypt again
- const doubleEncrypted = encrypt(encrypted!);
- expect(doubleEncrypted).toBe(encrypted);
- });
-
- it("should return null for malformed ciphertext (missing parts)", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { decrypt } = await import("@/lib/db/encryption");
-
- const malformed = "enc:v1:onlyonepart";
- const result = decrypt(malformed);
- expect(result).toBeNull();
- });
-
- it("should return null for malformed ciphertext (invalid hex)", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { decrypt } = await import("@/lib/db/encryption");
-
- const malformed = "enc:v1:notvalidhex:notvalidhex:notvalidhex";
- const result = decrypt(malformed);
- expect(result).toBeNull();
- });
-
- it("should return null for ciphertext with wrong auth tag", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encrypt, decrypt } = await import("@/lib/db/encryption");
-
- const plaintext = "my-secret";
- const encrypted = encrypt(plaintext);
-
- // Tamper with the auth tag
- const parts = encrypted!.split(":");
- parts[parts.length - 1] = "0000000000000000000000000000000000000000000000000000000000000000";
- const tampered = parts.join(":");
-
- const result = decrypt(tampered);
- expect(result).toBeNull();
- });
-
- it("should return plaintext unchanged if not encrypted (legacy plaintext)", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { decrypt } = await import("@/lib/db/encryption");
-
- const plaintext = "not-encrypted-value";
- const result = decrypt(plaintext);
- expect(result).toBe(plaintext);
- });
-
- it("should return null when trying to decrypt without encryption key", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { encrypt } = await import("@/lib/db/encryption");
-
- const plaintext = "my-secret";
- const encrypted = encrypt(plaintext);
-
- // Now remove the key and try to decrypt
- vi.unstubAllEnvs();
- vi.resetModules();
-
- const { decrypt } = await import("@/lib/db/encryption");
-
- const result = decrypt(encrypted!);
- expect(result).toBeNull();
- });
- });
-
- describe("validateEncryptionConfig() with various key states", () => {
- it("should return valid when no key is set (passthrough mode)", async () => {
- vi.resetModules();
-
- const { validateEncryptionConfig } = await import("@/lib/db/encryption");
-
- const result = validateEncryptionConfig();
- expect(result.valid).toBe(true);
- expect(result.error).toBeUndefined();
- });
-
- it("should return valid when a proper key is set", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "test-secret-key-12345");
- vi.resetModules();
-
- const { validateEncryptionConfig } = await import("@/lib/db/encryption");
-
- const result = validateEncryptionConfig();
- expect(result.valid).toBe(true);
- expect(result.error).toBeUndefined();
- });
-
- it("should return valid when key is empty string (treated as not set)", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", "");
- vi.resetModules();
-
- const { validateEncryptionConfig } = await import("@/lib/db/encryption");
-
- const result = validateEncryptionConfig();
- expect(result.valid).toBe(true);
- expect(result.error).toBeUndefined();
- });
-
- it("should return invalid when key is whitespace only", async () => {
- vi.stubEnv("STORAGE_ENCRYPTION_KEY", " ");
- vi.resetModules();
-
- const { validateEncryptionConfig } = await import("@/lib/db/encryption");
-
- const result = validateEncryptionConfig();
- expect(result.valid).toBe(false);
- expect(result.error).toContain("empty");
- });
- });
-});
diff --git a/tests/unit/node-runtime-support.test.ts b/tests/unit/node-runtime-support.test.ts
index 37a2fe531a..51c5178025 100644
--- a/tests/unit/node-runtime-support.test.ts
+++ b/tests/unit/node-runtime-support.test.ts
@@ -35,6 +35,7 @@ test("getNodeRuntimeSupport accepts patched Node 24, 22 and 20 LTS lines", () =>
});
assert.equal(getNodeRuntimeSupport("20.20.2").nodeCompatible, true);
+ assert.equal(getNodeRuntimeSupport("26.0.0").nodeCompatible, true);
assert.deepEqual(getNodeRuntimeSupport("24.1.0"), {
nodeVersion: "v24.1.0",
nodeCompatible: true,
diff --git a/tests/unit/openai-to-claude-strip-empty.test.ts b/tests/unit/openai-to-claude-strip-empty.test.ts
index b139853cb3..bb0500a69a 100644
--- a/tests/unit/openai-to-claude-strip-empty.test.ts
+++ b/tests/unit/openai-to-claude-strip-empty.test.ts
@@ -102,6 +102,5 @@ test("T15: openaiToClaudeRequest converts system array content into a Claude sys
const translated = openaiToClaudeRequest("claude-sonnet-4", request, false);
assert.ok(Array.isArray(translated.system));
- // system[0] is the injected Claude prompt; user-provided system content is system[1].
- assert.equal(translated.system[1].text, "System rules A\nSystem rules B");
+ assert.equal(translated.system[0].text, "System rules A\nSystem rules B");
});
diff --git a/tests/unit/plan3-p0.test.ts b/tests/unit/plan3-p0.test.ts
index 4a969318d2..3f9749651a 100644
--- a/tests/unit/plan3-p0.test.ts
+++ b/tests/unit/plan3-p0.test.ts
@@ -185,8 +185,7 @@ test("Claude native messages can be round-tripped through OpenAI into Claude OAu
content: [{ type: "text", text: "reply with OK only" }],
},
]);
- assert.ok(Array.isArray(translated.system));
- assert.equal(translated.system[0]?.text?.includes("You are Claude Code"), true);
+ assert.equal(translated.system, undefined);
});
test("CodexExecutor maps fast service tier to priority", () => {