mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
Fix GitHub Copilot agent mode with Antigravity
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -263,21 +263,34 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
* - Inject AG default decoy tools after client tools
|
||||
* Returns { cloakedBody, toolNameMap } where toolNameMap maps suffixed → original
|
||||
*/
|
||||
static cloakTools(body) {
|
||||
static cloakTools(body, clientTool = null) {
|
||||
const tools = body.request?.tools;
|
||||
if (!tools || tools.length === 0) {
|
||||
return { cloakedBody: body, toolNameMap: null };
|
||||
}
|
||||
|
||||
const isCopilot = clientTool === "github-copilot";
|
||||
const toolNameMap = new Map();
|
||||
const clientDeclarations = [];
|
||||
const decoyNames = new Set(AG_DECOY_TOOLS.map(tool => tool.name));
|
||||
|
||||
// First: collect renamed client tools
|
||||
for (const toolGroup of tools) {
|
||||
if (!toolGroup.functionDeclarations) continue;
|
||||
|
||||
for (const func of toolGroup.functionDeclarations) {
|
||||
// Skip if already an AG default tool name
|
||||
// For GitHub Copilot, avoid emitting duplicate native Antigravity tool names.
|
||||
// Keep the decoys only once in the final declaration list.
|
||||
if (isCopilot && AG_DEFAULT_TOOLS.has(func.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if already covered by decoys for Copilot
|
||||
if (isCopilot && decoyNames.has(func.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Preserve native AG names for non-Copilot clients
|
||||
if (AG_DEFAULT_TOOLS.has(func.name)) {
|
||||
clientDeclarations.push(func);
|
||||
continue;
|
||||
@@ -290,7 +303,13 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
}
|
||||
|
||||
// Client tools first, then AG decoy tools
|
||||
const allDeclarations = [...clientDeclarations, ...AG_DECOY_TOOLS];
|
||||
const allDeclarations = [];
|
||||
const seenNames = new Set();
|
||||
for (const decl of [...clientDeclarations, ...AG_DECOY_TOOLS]) {
|
||||
if (!decl?.name || seenNames.has(decl.name)) continue;
|
||||
seenNames.add(decl.name);
|
||||
allDeclarations.push(decl);
|
||||
}
|
||||
|
||||
// Rename tool names in conversation history (contents)
|
||||
const cloakedContents = body.request?.contents?.map(msg => {
|
||||
|
||||
@@ -82,7 +82,7 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
|
||||
log?.debug?.("PASSTHROUGH", `${clientTool} → ${provider} | native lossless`);
|
||||
translatedBody = { ...body, model };
|
||||
} else {
|
||||
translatedBody = translateRequest(sourceFormat, targetFormat, model, body, stream, credentials, provider, reqLogger, stripList, connectionId, rtkEnabled);
|
||||
translatedBody = translateRequest(sourceFormat, targetFormat, model, body, stream, credentials, provider, reqLogger, stripList, connectionId, rtkEnabled, clientTool);
|
||||
if (!translatedBody) {
|
||||
trackPendingRequest(model, provider, connectionId, false, true);
|
||||
return createErrorResult(HTTP_STATUS.BAD_REQUEST, `Failed to translate request for ${sourceFormat} → ${targetFormat}`);
|
||||
|
||||
@@ -8,9 +8,9 @@ export const UNSUPPORTED_SCHEMA_CONSTRAINTS = [
|
||||
// Claude rejects these in VALIDATED mode
|
||||
"default", "examples",
|
||||
// JSON Schema meta keywords
|
||||
"$schema", "$defs", "definitions", "const", "$ref",
|
||||
"$schema", "$defs", "definitions", "const", "$ref", "$comment",
|
||||
// Object validation keywords (not supported)
|
||||
"additionalProperties", "propertyNames", "patternProperties",
|
||||
"additionalProperties", "propertyNames", "patternProperties", "enumDescriptions",
|
||||
// Complex schema keywords (handled by flattenAnyOfOneOf/mergeAllOf)
|
||||
"anyOf", "oneOf", "allOf", "not",
|
||||
// Dependency keywords (not supported)
|
||||
@@ -111,16 +111,18 @@ function removeUnsupportedKeywords(obj, keywords) {
|
||||
for (const item of obj) {
|
||||
removeUnsupportedKeywords(item, keywords);
|
||||
}
|
||||
} else {
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (keywords.includes(key) || key.startsWith("x-")) {
|
||||
delete obj[key];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (keywords.includes(key) || key.startsWith("x-")) {
|
||||
delete obj[key];
|
||||
continue;
|
||||
}
|
||||
for (const value of Object.values(obj)) {
|
||||
if (value && typeof value === "object") {
|
||||
removeUnsupportedKeywords(value, keywords);
|
||||
}
|
||||
|
||||
const value = obj[key];
|
||||
if (value && typeof value === "object") {
|
||||
removeUnsupportedKeywords(value, keywords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ function stripContentTypes(body, stripList = []) {
|
||||
}
|
||||
|
||||
// Translate request: source -> openai -> target
|
||||
export function translateRequest(sourceFormat, targetFormat, model, body, stream = true, credentials = null, provider = null, reqLogger = null, stripList = [], connectionId = null, rtkEnabled = false) {
|
||||
export function translateRequest(sourceFormat, targetFormat, model, body, stream = true, credentials = null, provider = null, reqLogger = null, stripList = [], connectionId = null, rtkEnabled = false, clientTool = null) {
|
||||
ensureInitialized();
|
||||
let result = body;
|
||||
|
||||
@@ -141,9 +141,9 @@ export function translateRequest(sourceFormat, targetFormat, model, body, stream
|
||||
}
|
||||
|
||||
// Antigravity cloaking: rename client tools + inject decoys (anti-ban)
|
||||
// Skip if client is native AG (userAgent = antigravity)
|
||||
if (provider === FORMATS.ANTIGRAVITY && body.userAgent !== FORMATS.ANTIGRAVITY) {
|
||||
const { cloakedBody, toolNameMap } = AntigravityExecutor.cloakTools(result);
|
||||
// Only apply for GitHub Copilot requests so other clients are unaffected.
|
||||
if (provider === FORMATS.ANTIGRAVITY && clientTool === "github-copilot") {
|
||||
const { cloakedBody, toolNameMap } = AntigravityExecutor.cloakTools(result, clientTool);
|
||||
result = cloakedBody;
|
||||
if (toolNameMap?.size > 0) {
|
||||
result._toolNameMap = toolNameMap;
|
||||
|
||||
@@ -20,10 +20,17 @@ const NATIVE_PAIRS = {
|
||||
export function detectClientTool(headers = {}, body = {}) {
|
||||
const ua = (headers["user-agent"] || "").toLowerCase();
|
||||
const xApp = (headers["x-app"] || "").toLowerCase();
|
||||
const openaiIntent = (headers["openai-intent"] || "").toLowerCase();
|
||||
const initiator = (headers["x-initiator"] || headers["X-Initiator"] || "").toLowerCase();
|
||||
|
||||
// Antigravity: detected via body field (not header)
|
||||
if (body.userAgent === "antigravity") return "antigravity";
|
||||
|
||||
// GitHub Copilot / OAI compatible extension using Copilot chat headers
|
||||
if (ua.includes("githubcopilotchat") || openaiIntent === "conversation-panel" || initiator === "user") {
|
||||
return "github-copilot";
|
||||
}
|
||||
|
||||
// Claude Code / Claude CLI
|
||||
if (ua.includes("claude-cli") || ua.includes("claude-code") || xApp === "cli") return "claude";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user