Skip to content
Closed
30 changes: 19 additions & 11 deletions .agents/workflows/generate-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,21 +159,29 @@ git push origin release/v2.x.y

### 9. Open PR to main

### 9. Open PR to main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Duplicate section header. This header is already present at line 160.


// turbo

```bash
gh pr create \
--repo diegosouzapw/OmniRoute \
--base main \
--head release/v2.x.y \
--title "chore(release): v2.x.y — summary" \
--body "## 🚀 Release v2.x.y
VERSION=$(node -p "require('./package.json').version")

### Changes
...
# Extract the exact changelog entry for this version from the root CHANGELOG.md
awk "/^## \\[$VERSION\\]/{flag=1; print; next} /^---/{if(flag) {flag=0; exit}} flag" CHANGELOG.md > /tmp/changelog_body.txt

### Tests
- X/X tests pass
# Append test status and next steps
echo "" >> /tmp/changelog_body.txt
echo "### Tests" >> /tmp/changelog_body.txt
echo "- All tests pass" >> /tmp/changelog_body.txt
echo "" >> /tmp/changelog_body.txt
echo "### ⚠️ After merging: run Phase 2 steps to tag, publish, and deploy." >> /tmp/changelog_body.txt

### ⚠️ After merging: run Phase 2 steps to tag, publish, and deploy."
gh pr create \
--repo diegosouzapw/OmniRoute \
--base main \
--head release/v$VERSION \
--title "Release v$VERSION" \
--body-file /tmp/changelog_body.txt
```

### 10. 🛑 STOP — Notify User & Await PR Confirmation
Expand Down
32 changes: 14 additions & 18 deletions .agents/workflows/review-prs.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,34 +193,30 @@ Perform a **global impact assessment** to verify whether the PR changes are comp

### 8. Merge into Release Branch

### 8. Merge into Release Branch (NEVER SILENTLY CLOSE!)
### 8. Merge into Release Branch (NEVER CLOSE!)

> **⚠️ CRITICAL**: NEVER use `gh pr close` for a PR whose idea or code was accepted, even if you had to rewrite it manually. Closing a PR in a contributor's face after taking their idea is unacceptable.
> ALWAYS ensure the contributor gets proper credit.
> **⚠️ CRITICAL**: NEVER use `gh pr close` for a PR whose idea or code was accepted. Closing a PR in a contributor's face after taking their idea is unacceptable.
> You MUST ALWAYS merge the PR using GitHub so the contributor gets the official "Merged" badge and proper credit on their profile.

If the PR is green and can be merged directly:
Even if the PR had severe conflicts or required significant architectural adjustments, you MUST:

- Merge the PR into the release branch using the GitHub CLI.
```bash
# Merge the PR (base is already set to release/vX.Y.Z from step 3.5)
gh pr merge <NUMBER> --repo <owner>/<repo> --squash --body "Integrated into release/vX.Y.Z"
```

If the PR had severe conflicts or you had to implement the feature manually on our branch instead:
1. Apply the fixes directly to their PR branch (as detailed in step 7).
2. Once the PR branch is green and correct, merge it into the release branch using the GitHub CLI.

- You MUST still post a comment giving them full credit before closing it.
- Explain that their idea was great, that it was integrated in a specific commit (mention the commit hash), and that due to merge conflicts or architectural adjustments, it was integrated manually.
- Add their name as a Co-authored-by in the manual commit if possible.
```bash
# Merge the PR (base is already set to release/vX.Y.Z from step 3.5)
gh pr merge <NUMBER> --repo <owner>/<repo> --squash --body "Integrated into release/vX.Y.Z"
```

In ALL accepted cases (merged directly or implemented manually):
In ALL cases:

- Post a **thank-you comment** on the PR via the GitHub API.
- Post a **thank-you comment** on the PR via the GitHub API before or immediately after merging.
- The message should:
- Thank the author by name/username for their contribution.
- Explain what was adjusted or improved (if anything).
- Explain what was adjusted or improved (if we pushed fixes to their branch).
- Note it will be included in the upcoming release.
- Be friendly, professional, and encouraging.
- Example: _"Thanks @author for this great contribution! 🎉 The [feature/fix] has been integrated into the release/vX.Y.Z branch and will be part of the next release. We appreciate your effort!"_
- Example: _"Thanks @author for this great contribution! 🎉 We've added a few small adjustments to your branch to align with our latest architecture, and it's now officially merged into the release/vX.Y.Z branch. It will be part of the next release. We appreciate your effort!"_

### 9. Sync Local Release Branch

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ jobs:
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
base: ${{ github.event.before || github.event.repository.default_branch }}
head: HEAD
extra_args: --only-verified

Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@

### ✨ New Features

- **feat(claude-code):** Update Claude Code obfuscation to version 2.1.114, centralize hardcoded version strings, and use standard logger (#1403)
- **feat(cli-tools):** Add direct configuration file generation and override support for Qwen Code local settings (#1394)
- **feat(providers):** Derive Claude CLI model defaults dynamically from provider registry to stay current with upstream API changes (#1393)
- **feat(core):** Implement persistent API key, backup pruning, and GPU optimization (#1350, #1367, #1369)

### 🐛 Bug Fixes

- **fix(claude-code):** Scope thinking stripping to executor boundaries to prevent issues with normal API requests (#1401)
- **fix(mitm):** Resolve MITM not working when connecting Antigravity (#1399)
- **fix(security):** Resolve CodeQL password hash alert and fix TruffleHog CI failure (#161)
- **fix(combo):** Fallback to the next model when all provider accounts return a 503 rate-limited signal instead of aborting the routing sequence (#1398)
- **fix(codex):** Strip server-generated IDs from response items in input to prevent 404 lookup errors in multi-turn Codex Conversations (#1397)
- **fix(codex):** Optimize Chat Completions paths by converting `system` to `developer` roles instead of hoisting them into instructions, enabling prompt caching for system messages on GPT-5 models (#1400)
Expand All @@ -23,6 +27,10 @@
- **fix(electron):** Resolve type error in Header electronAPI properties
- **fix(security):** Resolve CodeQL security alerts including safe prototype bindings (#151, #152, #154, #155-159)

### 🧪 Tests

- **test(core):** Resolve typescript strictness complaints and fix combo-routing-engine test regression

### 🛠️ Maintenance

- **chore:** Sync infrastructure with docker postinstall components and secondary CodeQL analysis rules
Expand Down
31 changes: 23 additions & 8 deletions open-sse/config/cliFingerprints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,39 @@ export const CLI_FINGERPRINTS: Record<string, CliFingerprint> = {
"Content-Type",
"x-api-key",
"anthropic-version",
"Accept",
"anthropic-beta",
"anthropic-dangerous-direct-browser-access",
"x-app",
"User-Agent",
"Accept-Encoding",
"X-Claude-Code-Session-Id",
"x-client-request-id",
"X-Stainless-Retry-Count",
"X-Stainless-Timeout",
"X-Stainless-Lang",
"X-Stainless-Package-Version",
"X-Stainless-OS",
"X-Stainless-Arch",
"X-Stainless-Runtime",
"X-Stainless-Runtime-Version",
"Accept",
"accept-language",
"accept-encoding",
"sec-fetch-mode",
"Connection",
],
bodyFieldOrder: [
"model",
"max_tokens",
"messages",
"system",
"temperature",
"top_p",
"top_k",
"stream",
"tools",
"tool_choice",
"metadata",
"max_tokens",
"thinking",
"context_management",
"output_config",
"stream",
],
userAgent: "claude-code",
},
"claude-code-compatible": {
headerOrder: [
Expand Down
24 changes: 16 additions & 8 deletions open-sse/executors/antigravity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import { persistCreditBalance, getAllPersistedCreditBalances } from "@/lib/db/cr
import { obfuscateSensitiveWords } from "../services/antigravityObfuscation.ts";
import { resolveAntigravityVersion } from "../services/antigravityVersion.ts";
import { resolveAntigravityModelId } from "../config/antigravityModelAliases.ts";
import {
shouldStripCloudCodeThinking,
stripCloudCodeThinkingConfig,
} from "../services/cloudCodeThinking.ts";

const MAX_RETRY_AFTER_MS = 60_000;
const LONG_RETRY_THRESHOLD_MS = 60_000;
Expand Down Expand Up @@ -166,9 +170,15 @@ export class AntigravityExecutor extends BaseExecutor {
return resp as unknown as never;
}

const upstreamModel = cleanModelName(model);
const baseBody = body && typeof body === "object" ? body : {};
const normalizedBody = shouldStripCloudCodeThinking(this.provider, upstreamModel)
? stripCloudCodeThinkingConfig(baseBody)
: baseBody;

// Fix contents for Claude models via Antigravity
const normalizedContents =
body.request?.contents?.map((c) => {
normalizedBody.request?.contents?.map((c) => {
let role = c.role;
// functionResponse must be role "user" for Claude models
if (c.parts?.some((p) => p.functionResponse)) {
Expand Down Expand Up @@ -203,18 +213,16 @@ export class AntigravityExecutor extends BaseExecutor {
}

const transformedRequest = {
...body.request,
...normalizedBody.request,
...(contents.length > 0 && { contents }),
sessionId: body.request?.sessionId || this.generateSessionId(),
sessionId: normalizedBody.request?.sessionId || this.generateSessionId(),
safetySettings: undefined,
toolConfig:
body.request?.tools?.length > 0
normalizedBody.request?.tools?.length > 0
? { functionCallingConfig: { mode: "VALIDATED" } }
: body.request?.toolConfig,
: normalizedBody.request?.toolConfig,
};

const upstreamModel = cleanModelName(model);

// Obfuscate sensitive client names in user content (e.g. "OpenCode", "Cursor")
const requestContents = transformedRequest.contents;
if (Array.isArray(requestContents)) {
Expand All @@ -230,7 +238,7 @@ export class AntigravityExecutor extends BaseExecutor {
}

return {
...body,
...normalizedBody,
project: projectId,
model: upstreamModel,
userAgent: "antigravity",
Expand Down
106 changes: 102 additions & 4 deletions open-sse/executors/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import {
modelSupportsContext1mBeta,
} from "../services/claudeCodeCompatible.ts";
import { getClaudeCodeCompatibleRequestDefaults } from "@/lib/providers/requestDefaults";
import { remapToolNamesInRequest } from "../services/claudeCodeToolRemapper.ts";
import { obfuscateInBody } from "../services/claudeCodeObfuscation.ts";
import {
computeFingerprint,
extractFirstUserMessageText,
} from "../services/claudeCodeFingerprint.ts";
import { randomUUID } from "node:crypto";
import { createHash } from "node:crypto";

/**
* Sanitizes a custom API path to prevent path traversal attacks.
Expand Down Expand Up @@ -429,6 +437,97 @@ export class BaseExecutor {
? mergeAbortSignals(signal, timeoutSignal)
: signal || timeoutSignal;

const isClaudeCodeClient =
clientHeaders?.["x-app"] === "cli" ||
(clientHeaders?.["user-agent"] &&
clientHeaders["user-agent"].toLowerCase().includes("claude-code")) ||
(clientHeaders?.["user-agent"] &&
clientHeaders["user-agent"].toLowerCase().includes("claude-cli"));

if (
this.provider === "claude" &&
isClaudeCodeClient &&
typeof transformedBody === "object" &&
transformedBody !== null
) {
const tb = transformedBody as Record<string, unknown>;
remapToolNamesInRequest(tb);
obfuscateInBody(tb);

const ccVersion = "2.1.114";
const messages = tb.messages as Array<{ role?: string; content?: unknown }> | undefined;
const msgText = extractFirstUserMessageText(messages);
const fp = computeFingerprint(msgText, ccVersion);
const billingLine = `x-anthropic-billing-header: cc_version=${ccVersion}.${fp}; cc_entrypoint=cli; cch=00000;`;

if (Array.isArray(tb.system)) {
const sysBlocks = tb.system as Array<Record<string, unknown>>;
sysBlocks.unshift({ type: "text", text: billingLine });
} else if (typeof tb.system === "string") {
tb.system = [
{ type: "text", text: billingLine },
{ type: "text", text: tb.system },
];
} else {
tb.system = [{ type: "text", text: billingLine }];
}

if (!tb.metadata || typeof tb.metadata !== "object") {
tb.metadata = {
user_id: JSON.stringify({
device_id: createHash("sha256").update("omniroute").digest("hex").slice(0, 24),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The device_id is hardcoded as a static hash of the string "omniroute". This means every installation of OmniRoute will present the same device ID to Anthropic, which significantly increases the risk of account flagging or detection as a proxy. Consider generating a unique, persistent ID per installation or using a random UUID to improve anonymity.

Suggested change
device_id: createHash("sha256").update("omniroute").digest("hex").slice(0, 24),
device_id: createHash("sha256").update(randomUUID()).digest("hex").slice(0, 24),

account_uuid: "",
session_id: randomUUID(),
}),
};
}

if (!tb.thinking) {
tb.thinking = { type: "adaptive" };
}

if (!tb.context_management) {
tb.context_management = {
edits: [{ type: "clear_thinking_20251015", keep: "all" }],
};
}

if (!tb.output_config) {
tb.output_config = { effort: "high" };
}

const ccHeaders: Record<string, string> = {
"anthropic-version": "2023-06-01",
"anthropic-beta":
"claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,redact-thinking-2026-02-12,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advisor-tool-2026-03-01,advanced-tool-use-2025-11-20,effort-2025-11-24",
"anthropic-dangerous-direct-browser-access": "true",
"x-app": "cli",
"User-Agent": `claude-cli/${ccVersion} (external, cli)`,
"X-Stainless-Package-Version": "0.81.0",
"X-Stainless-Timeout": "600",
"accept-language": "*",
"accept-encoding": "gzip, deflate, br, zstd",
connection: "keep-alive",
"x-client-request-id": randomUUID(),
"X-Claude-Code-Session-Id": randomUUID(),
};
Object.assign(headers, ccHeaders);
delete headers["X-Stainless-Helper-Method"];

// Add X-Stainless headers to match real Claude Code
headers["X-Stainless-Arch"] = "x64";
headers["X-Stainless-Lang"] = "js";
headers["X-Stainless-OS"] = "Windows";
headers["X-Stainless-Runtime"] = "node";
headers["X-Stainless-Runtime-Version"] = "v24.3.0";
headers["X-Stainless-Retry-Count"] = "0";
delete headers["X-Stainless-Os"];

console.log(
`[CLAUDE-PATCH] provider=${this.provider} tools remapped, billing header injected, body fields added, headers patched`
);
}

// Apply CLI fingerprint ordering if enabled for this provider
let finalHeaders = headers;
let bodyString = JSON.stringify(transformedBody);
Expand All @@ -439,10 +538,9 @@ export class BaseExecutor {
bodyString = fingerprinted.bodyString;
}

// CCH signing: Claude Code-compatible providers require an xxHash64 integrity
// token over the serialized body. Sign after fingerprint ordering so the hash
// covers the exact bytes that will be sent upstream.
if (isClaudeCodeCompatible(this.provider)) {
// CCH signing: Claude Code-compatible providers AND native claude provider
// require an xxHash64 integrity token over the serialized body.
if (isClaudeCodeCompatible(this.provider) || this.provider === "claude") {
bodyString = await signRequestBody(bodyString);
}

Expand Down
Loading