Add local agent auth provider hook support

This commit is contained in:
musistudio
2026-08-04 15:34:47 +08:00
parent 4cba6b51a8
commit 561c2d813c
6 changed files with 747 additions and 2 deletions
@@ -13,14 +13,16 @@ import { mediaToolsMcpServer } from "@ccr/core/mcp/grok-media-config";
import { resolveGatewayPublicModelId } from "@ccr/core/gateway/features/model-discovery";
import { activeProviderCredentials, inferProtocol, normalizedProviderCapabilities, normalizeProviderProtocol, providerCapabilityForClientProtocol, providerCapabilityInternalName, providerCapabilityNameMatches, providerCredentialInternalName, providerProtocolForClientProtocol, sortProviderCredentialsForConfig, toCoreGatewayProviders } from "@ccr/core/providers/runtime-topology";
import { buildRawTraceConfig } from "@ccr/core/observability/raw-trace-sync";
import { endpoint, resolveUndiciProxyAgentModule, resolveUpstreamHeaderSanitizerEntry, writeGatewayProxyPreloadFile } from "@ccr/core/gateway/core-runtime/supervisor";
import { endpoint, resolveLocalAgentAuthProviderHookEntry, resolveUndiciProxyAgentModule, resolveUpstreamHeaderSanitizerEntry, writeGatewayProxyPreloadFile } from "@ccr/core/gateway/core-runtime/supervisor";
import { billingUsageSyncHeader, billingUsageSyncPath, claudeCodeOauthBetaHeader, claudeCodeOauthRequiredBeta, coreGatewayAuthHeader, coreGatewayAuthTokenEnv } from "@ccr/core/gateway/internal/shared";
import type { BrowserWebSearchMcpIntegration, CoreGatewayProvider } from "@ccr/core/gateway/internal/shared";
import { uniqueStrings } from "@ccr/core/gateway/internal/collections";
import { isLocalClaudeCodeOauthProviderPlugin, mergeAnthropicBetaValues } from "@ccr/core/providers/oauth-plugin";
import { isLocalAgentOauthProviderPlugin } from "@ccr/core/gateway/core-runtime/local-agent-auth-provider-hook";
import { resolveConfiguredProviderModelSelector, resolveUniqueConfiguredProviderModelSelector } from "@ccr/core/routing/model-resolution";
const upstreamHeaderSanitizerPluginKey = "ccr-upstream-header-sanitizer";
const localAgentAuthProviderHookPluginKey = "ccr-local-agent-auth-provider-hooks";
export const unlimitedVirtualModelToolCalls = Number.MAX_SAFE_INTEGER;
export const unlimitedVirtualModelToolTurns = Number.MAX_SAFE_INTEGER;
@@ -36,7 +38,8 @@ export async function compileCoreGatewayConfig(
const pluginCoreGatewayConfig = pluginService.getCoreGatewayConfig();
const configuredGatewayPlugins = Array.isArray(pluginCoreGatewayConfig.plugins)
? pluginCoreGatewayConfig.plugins.filter((plugin) =>
!isRecord(plugin) || stringValue(plugin.key) !== upstreamHeaderSanitizerPluginKey
!isRecord(plugin) ||
![localAgentAuthProviderHookPluginKey, upstreamHeaderSanitizerPluginKey].includes(stringValue(plugin.key) ?? "")
)
: [];
const pluginBillingConfig = isRecord(pluginCoreGatewayConfig.billing) ? pluginCoreGatewayConfig.billing : {};
@@ -82,6 +85,7 @@ export async function compileCoreGatewayConfig(
.filter((provider): provider is CoreGatewayProvider => Boolean(provider)),
...builtinToolArtifacts.providers
];
const localAgentAuthProviderHookPlugin = localAgentAuthProviderHookPluginConfig(providerPluginsWithCapabilityAliases);
const pluginAgentConfig = isRecord(pluginCoreGatewayConfig.agent) ? pluginCoreGatewayConfig.agent : {};
const pluginMcpServers = Array.isArray(pluginAgentConfig.mcpServers) ? pluginAgentConfig.mcpServers : [];
const externalMcpServers = [
@@ -138,6 +142,7 @@ export async function compileCoreGatewayConfig(
port: config.gateway.corePort,
plugins: [
...configuredGatewayPlugins,
...(localAgentAuthProviderHookPlugin ? [localAgentAuthProviderHookPlugin] : []),
{
enabled: true,
key: upstreamHeaderSanitizerPluginKey,
@@ -156,6 +161,17 @@ export async function compileCoreGatewayConfig(
};
}
function localAgentAuthProviderHookPluginConfig(providerPlugins: unknown[]): Record<string, unknown> | undefined {
if (!providerPlugins.some(isLocalAgentOauthProviderPlugin)) {
return undefined;
}
return {
enabled: true,
key: localAgentAuthProviderHookPluginKey,
modulePath: resolveLocalAgentAuthProviderHookEntry()
};
}
function withProviderCapabilityPluginAliases(
providerPlugins: unknown[],
providers: GatewayProviderConfig[]
@@ -518,6 +534,7 @@ async function withGrokOauthRuntimeDefaults(providerPlugins: unknown[]): Promise
const currentHeaders = isRecord(currentAuth.headers) ? currentAuth.headers : {};
const currentRequest = isRecord(plugin.request) ? plugin.request : {};
const currentRequestHeaders = isRecord(currentRequest.headers) ? currentRequest.headers : {};
const currentBodyRemove = Array.isArray(currentRequest.bodyRemove) ? currentRequest.bodyRemove : [];
return {
...plugin,
auth: {
@@ -529,6 +546,12 @@ async function withGrokOauthRuntimeDefaults(providerPlugins: unknown[]): Promise
},
request: {
...currentRequest,
bodyRemove: uniqueStrings([
...currentBodyRemove
.map((value) => stringValue(value))
.filter((value): value is string => Boolean(value)),
"external_web_access"
]),
headers: {
...currentRequestHeaders,
"x-grok-client-identifier": "xai-grok-cli",
@@ -0,0 +1,438 @@
import { readClaudeCodeOauth, readGrokAuth, readKimiAuth, resolveGrokAuth, resolveKimiAuth } from "@ccr/core/agents/local-providers/service";
import { grokAccessTokenExpired, grokClientVersion } from "@ccr/core/agents/local-providers/grok";
import { kimiAccessTokenExpired, kimiIdentityHeaders } from "@ccr/core/agents/local-providers/kimi";
import { transformCodexApplyPatchBridgeRequestBody } from "@ccr/core/gateway/features/codex-patch-bridge";
import { claudeCodeOauthBetaHeader, claudeCodeOauthRequiredBeta } from "@ccr/core/gateway/internal/shared";
import { isRecord, stringValue } from "@ccr/core/gateway/internal/value";
import { mergeAnthropicBetaValues } from "@ccr/core/providers/oauth-plugin";
const configProviderPluginKeyPrefix = "config:";
const localAgentProviderPluginKeyPrefix = "ccr-local-agent-";
type HeaderRecord = Record<string, string>;
type UpstreamRequest = {
body?: unknown;
bodyEncoding?: "bytes" | "form" | "json" | "none" | "text";
headers?: HeaderRecord;
method?: string;
url: string;
};
type ProviderPluginInput = {
model?: string;
request?: {
headers?: Record<string, string | string[] | undefined>;
};
upstreamRequest: UpstreamRequest;
};
type ProviderHookResult = {
ok: true;
value: UpstreamRequest;
} | {
error: string;
ok: false;
};
type ProviderHook = {
authenticate?: (input: ProviderPluginInput) => Promise<ProviderHookResult> | ProviderHookResult;
key: string;
provider?: string;
providerName?: string;
transformRequest?: (input: ProviderPluginInput) => Promise<ProviderHookResult> | ProviderHookResult;
};
type LocalAgentOauthKind = "claude-code" | "grok" | "kimi";
export function createGatewayPlugin(input: { config?: Record<string, unknown> } = {}) {
return {
providerHooks: localAgentOauthProviderHooks(input.config)
};
}
export function localAgentOauthProviderHooks(config: Record<string, unknown> | undefined): ProviderHook[] {
const providerPlugins = Array.isArray(config?.providerPlugins) ? config.providerPlugins : [];
return providerPlugins
.map(localAgentOauthProviderHook)
.filter((hook): hook is ProviderHook => Boolean(hook));
}
export function isLocalAgentOauthProviderPlugin(value: unknown): boolean {
if (!isRecord(value)) {
return false;
}
return localAgentOauthKind(value) !== undefined;
}
function localAgentOauthProviderHook(plugin: unknown): ProviderHook | undefined {
if (!isRecord(plugin)) {
return undefined;
}
const kind = localAgentOauthKind(plugin);
const key = stringValue(plugin.key);
if (!kind || !key) {
return undefined;
}
const hook: ProviderHook = {
key: `${configProviderPluginKeyPrefix}${key}`,
provider: stringValue(plugin.provider),
providerName: stringValue(plugin.providerName)
};
if (kind === "grok") {
hook.authenticate = (input) => authenticateWithBearer(input, () => resolveLiveGrokAccessToken(plugin), plugin, "Grok CLI access token was not found.");
hook.transformRequest = (input) => transformGrokRequest(input, plugin);
return hook;
}
if (kind === "kimi") {
hook.authenticate = (input) => authenticateWithBearer(input, () => resolveLiveKimiAccessToken(plugin), plugin, "Kimi CLI access token was not found.");
hook.transformRequest = (input) => transformWithHeaders(input, kimiIdentityHeaders());
return hook;
}
hook.authenticate = (input) => authenticateClaudeCode(input, plugin);
return hook;
}
function localAgentOauthKind(plugin: Record<string, unknown>): LocalAgentOauthKind | undefined {
const key = stringValue(plugin.key)?.toLowerCase();
if (!key?.startsWith(localAgentProviderPluginKeyPrefix)) {
return undefined;
}
if (key.includes("grok-cli-oauth")) {
return "grok";
}
if (key.includes("kimi-cli-oauth")) {
return "kimi";
}
if (key.includes("claude-code-oauth")) {
return "claude-code";
}
return undefined;
}
async function authenticateWithBearer(
input: ProviderPluginInput,
tokenResolver: () => Promise<string | undefined> | string | undefined,
plugin: Record<string, unknown>,
missingTokenError: string
): Promise<ProviderHookResult> {
const token = await tokenResolver() || originalBearerToken(plugin);
if (!token) {
return { error: missingTokenError, ok: false };
}
return {
ok: true,
value: {
...input.upstreamRequest,
headers: withBearerAuth(input.upstreamRequest.headers, token, originalRemoveHeaders(plugin))
}
};
}
async function authenticateClaudeCode(
input: ProviderPluginInput,
plugin: Record<string, unknown>
): Promise<ProviderHookResult> {
const token = readClaudeCodeOauth()?.accessToken || originalBearerToken(plugin);
if (!token) {
return { error: "Claude Code access token was not found.", ok: false };
}
const headers = withBearerAuth(input.upstreamRequest.headers, token, originalRemoveHeaders(plugin));
headers[claudeCodeOauthBetaHeader] = mergeAnthropicBetaValues(
requestHeader(input.request?.headers, claudeCodeOauthBetaHeader),
originalAnthropicBetaDefault(plugin),
claudeCodeOauthRequiredBeta
);
return {
ok: true,
value: {
...input.upstreamRequest,
headers
}
};
}
async function resolveLiveGrokAccessToken(plugin: Record<string, unknown>): Promise<string | undefined> {
const auth = await resolveGrokAuth().catch(() => readGrokAuth());
if (auth?.accessToken && !grokAccessTokenExpired(auth)) {
return auth.accessToken;
}
return originalBearerToken(plugin);
}
async function resolveLiveKimiAccessToken(plugin: Record<string, unknown>): Promise<string | undefined> {
const reference = kimiOauthReference(plugin);
const auth = await resolveKimiAuth(reference).catch(() => readKimiAuth(reference));
if (auth?.accessToken && !kimiAccessTokenExpired(auth)) {
return auth.accessToken;
}
return originalBearerToken(plugin);
}
function transformWithHeaders(input: ProviderPluginInput, headers: HeaderRecord): ProviderHookResult {
return {
ok: true,
value: {
...input.upstreamRequest,
headers: {
...(input.upstreamRequest.headers ?? {}),
...headers
}
}
};
}
function transformGrokRequest(input: ProviderPluginInput, plugin: Record<string, unknown>): ProviderHookResult {
const headers = {
...(input.upstreamRequest.headers ?? {}),
"x-grok-client-identifier": "xai-grok-cli",
"x-grok-client-version": grokClientVersion(),
"x-grok-model-override": input.model || originalRequestHeader(plugin, "x-grok-model-override") || ""
};
const body = transformGrokResponsesBody(input.upstreamRequest.body);
return {
ok: true,
value: {
...input.upstreamRequest,
body: body.changed ? body.value : input.upstreamRequest.body,
headers
}
};
}
function transformGrokResponsesBody(value: unknown): { value: unknown; changed: boolean } {
if (!isRecord(value)) {
return { value, changed: false };
}
const patched = transformCodexApplyPatchBridgeRequestBody(value);
const sanitizedOptions = sanitizeGrokUnsupportedResponsesOptions(patched.body);
const sanitized = sanitizeGrokResponsesTools(sanitizedOptions.value);
return {
value: sanitized.value,
changed: patched.changed || sanitizedOptions.changed || sanitized.changed
};
}
function sanitizeGrokUnsupportedResponsesOptions(body: Record<string, unknown>): {
value: Record<string, unknown>;
changed: boolean;
} {
let next: Record<string, unknown> | undefined;
for (const key of grokUnsupportedResponsesOptions) {
if (Object.hasOwn(body, key)) {
next ??= { ...body };
delete next[key];
}
}
return next ? { value: next, changed: true } : { value: body, changed: false };
}
function sanitizeGrokResponsesTools(body: Record<string, unknown>): { value: Record<string, unknown>; changed: boolean } {
if (!Array.isArray(body.tools)) {
return { value: body, changed: false };
}
let changed = false;
const removedToolNames = new Set<string>();
const tools = body.tools.filter((tool) => {
const supported = isGrokSupportedResponsesTool(tool);
if (!supported) {
changed = true;
const name = responseToolName(tool);
if (name) {
removedToolNames.add(name);
}
}
return supported;
});
if (!changed) {
return { value: body, changed: false };
}
const next = { ...body };
if (tools.length > 0) {
next.tools = tools;
} else {
delete next.tools;
delete next.parallel_tool_calls;
}
if (tools.length === 0 || grokToolChoiceNamesRemovedTool(next.tool_choice, removedToolNames)) {
delete next.tool_choice;
}
return { value: next, changed: true };
}
function isGrokSupportedResponsesTool(value: unknown): boolean {
if (!isRecord(value)) {
return false;
}
return grokSupportedResponsesToolTypes.has(stringValue(value.type) ?? "");
}
function responseToolName(value: unknown): string | undefined {
if (!isRecord(value)) {
return undefined;
}
return stringValue(value.name) || stringValue(value.server_label);
}
function grokToolChoiceNamesRemovedTool(value: unknown, removedToolNames: Set<string>): boolean {
if (removedToolNames.size === 0) {
return false;
}
if (!isRecord(value)) {
return false;
}
const name = stringValue(value.name) || stringValue(value.server_label);
return name ? removedToolNames.has(name) : false;
}
function withBearerAuth(
headers: HeaderRecord | undefined,
token: string,
removeHeaders: string[]
): HeaderRecord {
const next = { ...(headers ?? {}) };
for (const name of removeHeaders) {
deleteHeader(next, name);
}
setHeader(next, "authorization", `Bearer ${token}`);
return next;
}
function originalRemoveHeaders(plugin: Record<string, unknown>): string[] {
const auth = isRecord(plugin.auth) ? plugin.auth : undefined;
const removeHeaders = Array.isArray(auth?.removeHeaders) ? auth.removeHeaders : [];
return uniqueStrings([
...removeHeaders
.map((value) => stringValue(value))
.filter((value): value is string => Boolean(value)),
"x-api-key"
]);
}
function originalBearerToken(plugin: Record<string, unknown>): string | undefined {
const authorization = originalAuthHeader(plugin, "authorization");
const prefix = "Bearer ";
return authorization?.startsWith(prefix) ? authorization.slice(prefix.length).trim() || undefined : undefined;
}
function originalAuthHeader(plugin: Record<string, unknown>, name: string): string | undefined {
const auth = isRecord(plugin.auth) ? plugin.auth : undefined;
const headers = isRecord(auth?.headers) ? auth.headers : undefined;
return recordHeader(headers, name);
}
function originalRequestHeader(plugin: Record<string, unknown>, name: string): string | undefined {
const request = isRecord(plugin.request) ? plugin.request : undefined;
const headers = isRecord(request?.headers) ? request.headers : undefined;
return recordHeader(headers, name);
}
function originalAnthropicBetaDefault(plugin: Record<string, unknown>): string | undefined {
const value = originalAuthHeaderValue(plugin, claudeCodeOauthBetaHeader);
if (typeof value === "string") {
return value;
}
if (isRecord(value)) {
return stringValue(value.default);
}
return undefined;
}
function originalAuthHeaderValue(plugin: Record<string, unknown>, name: string): unknown {
const auth = isRecord(plugin.auth) ? plugin.auth : undefined;
const headers = isRecord(auth?.headers) ? auth.headers : undefined;
if (!headers) {
return undefined;
}
const normalizedName = name.toLowerCase();
return Object.entries(headers).find(([key]) => key.trim().toLowerCase() === normalizedName)?.[1];
}
function recordHeader(headers: Record<string, unknown> | undefined, name: string): string | undefined {
const value = recordHeaderValue(headers, name);
return typeof value === "string" ? value : undefined;
}
function recordHeaderValue(headers: Record<string, unknown> | undefined, name: string): unknown {
if (!headers) {
return undefined;
}
const normalizedName = name.toLowerCase();
return Object.entries(headers).find(([key]) => key.trim().toLowerCase() === normalizedName)?.[1];
}
function requestHeader(
headers: Record<string, string | string[] | undefined> | undefined,
name: string
): string | undefined {
if (!headers) {
return undefined;
}
const normalizedName = name.toLowerCase();
const value = Object.entries(headers).find(([key]) => key.trim().toLowerCase() === normalizedName)?.[1];
if (Array.isArray(value)) {
return value.join(",");
}
return value;
}
function setHeader(headers: HeaderRecord, name: string, value: string): void {
deleteHeader(headers, name);
headers[name] = value;
}
function deleteHeader(headers: HeaderRecord, name: string): void {
const normalizedName = name.toLowerCase();
for (const key of Object.keys(headers)) {
if (key.trim().toLowerCase() === normalizedName) {
delete headers[key];
}
}
}
function kimiOauthReference(plugin: Record<string, unknown>): { key?: string; oauthHost?: string } | undefined {
const oauth = isRecord(plugin.kimiOauth) ? plugin.kimiOauth : undefined;
if (!oauth) {
return undefined;
}
const key = stringValue(oauth.key);
const oauthHost = stringValue(oauth.oauthHost) || stringValue(oauth.oauth_host);
return key || oauthHost ? { key, oauthHost } : undefined;
}
const grokSupportedResponsesToolTypes = new Set([
"function",
"x_search",
"image_generation",
"collections_search",
"file_search",
"code_execution",
"code_interpreter",
"mcp",
"shell"
]);
const grokUnsupportedResponsesOptions = new Set([
"external_web_access"
]);
function uniqueStrings(values: string[]): string[] {
const seen = new Set<string>();
const unique: string[] = [];
for (const value of values) {
const key = value.trim().toLowerCase();
if (!key || seen.has(key)) {
continue;
}
seen.add(key);
unique.push(value);
}
return unique;
}
@@ -184,6 +184,20 @@ export function resolveUpstreamHeaderSanitizerEntry(): string {
].find((candidate) => existsSync(candidate)) ?? pathJoin(__dirname, "upstream-header-sanitizer.js");
}
export function resolveLocalAgentAuthProviderHookEntry(): string {
const resourcesPath = (process as NodeJS.Process & { resourcesPath?: string }).resourcesPath;
return [
pathJoin(__dirname, "local-agent-auth-provider-hook.js"),
pathJoin(process.cwd(), ".test-dist", "core", "runtime", "local-agent-auth-provider-hook.js"),
...(resourcesPath
? [
pathJoin(resourcesPath, "app.asar", "dist", "main", "local-agent-auth-provider-hook.js"),
pathJoin(resourcesPath, "app", "dist", "main", "local-agent-auth-provider-hook.js")
]
: [])
].find((candidate) => existsSync(candidate)) ?? pathJoin(__dirname, "local-agent-auth-provider-hook.js");
}
function resolveGatewayEntry(): string {
const override = process.env[gatewayEntryOverrideEnv]?.trim();
if (override) {
@@ -0,0 +1,265 @@
import assert from "node:assert/strict";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { createDefaultAppConfig } from "@ccr/core/config/default-config.ts";
import { compileCoreGatewayConfig } from "@ccr/core/gateway/core-runtime/config-compiler.ts";
import {
createGatewayPlugin,
isLocalAgentOauthProviderPlugin
} from "@ccr/core/gateway/core-runtime/local-agent-auth-provider-hook.ts";
import { virtualApplyPatchToolName } from "@ccr/core/gateway/internal/shared.ts";
test("Grok local agent auth hook refreshes live login state before authenticating upstream requests", async (t) => {
await withGrokHome(t, async (grokHome) => {
writeGrokAuth(grokHome, {
expires_at: "2000-01-01T00:00:00Z",
key: "expired-grok-access-token",
oidc_client_id: "grok-client-id",
oidc_issuer: "https://auth.x.ai",
refresh_token: "grok-refresh-token"
});
const previousFetch = globalThis.fetch;
const previousTokenEndpoint = process.env.GROK_OIDC_TOKEN_ENDPOINT;
process.env.GROK_OIDC_TOKEN_ENDPOINT = "http://127.0.0.1/grok/oauth/token";
globalThis.fetch = async (input, init) => {
assert.equal(String(input), "http://127.0.0.1/grok/oauth/token");
assert.equal(String(init?.body ?? ""), "client_id=grok-client-id&grant_type=refresh_token&refresh_token=grok-refresh-token");
return new Response(JSON.stringify({
access_token: "refreshed-grok-access-token",
expires_in: 3600,
refresh_token: "refreshed-grok-refresh-token"
}), { headers: { "content-type": "application/json" }, status: 200 });
};
t.after(() => {
globalThis.fetch = previousFetch;
restoreEnv("GROK_OIDC_TOKEN_ENDPOINT", previousTokenEndpoint);
});
const [hook] = createGatewayPlugin({
config: {
providerPlugins: [grokOauthProviderPlugin()]
}
}).providerHooks;
assert.equal(hook.key, "config:ccr-local-agent-grok-cli-api-grok-cli-oauth");
const patch = "*** Begin Patch\n*** Add File: grok.txt\n+hi\n*** End Patch\n";
const upstreamRequest = {
body: {
external_web_access: true,
input: [
{ type: "custom_tool_call", call_id: "call_patch", name: "apply_patch", input: patch },
{ type: "custom_tool_call_output", call_id: "call_patch", output: "Success" }
],
model: "wrong-model",
parallel_tool_calls: true,
tool_choice: "auto",
tools: [
{ type: "function", name: "exec_command" },
{ type: "custom", name: "apply_patch", format: { type: "grammar", syntax: "lark", definition: "start: begin_patch" } },
{ type: "namespace", name: "multi_agent_v1" },
{ type: "web_search" }
]
},
headers: {
"content-type": "application/json",
"x-api-key": "client-key"
},
method: "POST",
url: "https://cli-chat-proxy.grok.com/v1/responses"
};
const authResult = await hook.authenticate({
model: "grok-4.5",
upstreamRequest
});
assert.equal(authResult.ok, true);
assert.equal(authResult.value.headers.authorization, "Bearer refreshed-grok-access-token");
assert.equal(authResult.value.headers["x-api-key"], undefined);
assert.equal(upstreamRequest.headers["x-api-key"], "client-key");
const requestResult = await hook.transformRequest({
model: "grok-4.5",
upstreamRequest: authResult.value
});
assert.equal(requestResult.ok, true);
assert.equal(requestResult.value.headers["x-grok-client-identifier"], "xai-grok-cli");
assert.equal(requestResult.value.headers["x-grok-client-version"], "0.2.93");
assert.equal(requestResult.value.headers["x-grok-model-override"], "grok-4.5");
assert.equal(requestResult.value.body.external_web_access, undefined);
assert.deepEqual(requestResult.value.body.tools.map((tool) => tool.type), ["function", "function"]);
assert.equal(requestResult.value.body.tools[1].name, virtualApplyPatchToolName);
assert.equal(requestResult.value.body.tools.some((tool) => tool.type === "custom" || tool.type === "namespace"), false);
assert.equal(requestResult.value.body.tool_choice, "auto");
assert.equal(requestResult.value.body.parallel_tool_calls, true);
assert.match(requestResult.value.body.instructions, /When modifying files, call virtual_apply_patch/);
assert.equal(requestResult.value.body.input[0].type, "function_call");
assert.equal(requestResult.value.body.input[0].name, virtualApplyPatchToolName);
assert.deepEqual(JSON.parse(requestResult.value.body.input[0].arguments), { patch });
assert.equal(requestResult.value.body.input[1].type, "function_call_output");
const persisted = JSON.parse(readFileSync(path.join(grokHome, "auth.json"), "utf8"));
assert.equal(persisted["https://auth.x.ai::test-account"].key, "refreshed-grok-access-token");
});
});
test("Grok local agent request hook removes unsupported Responses tools and stale tool choice", () => {
const [hook] = createGatewayPlugin({
config: {
providerPlugins: [grokOauthProviderPlugin()]
}
}).providerHooks;
const requestResult = hook.transformRequest({
model: "grok-4.5",
upstreamRequest: {
body: {
parallel_tool_calls: true,
tool_choice: { type: "tool", name: "multi_agent_v1" },
tools: [
{ type: "namespace", name: "multi_agent_v1" },
{ type: "function", name: "exec_command" }
]
},
headers: {},
method: "POST",
url: "https://cli-chat-proxy.grok.com/v1/responses"
}
});
assert.equal(requestResult.ok, true);
assert.deepEqual(requestResult.value.body.tools, [{ type: "function", name: "exec_command" }]);
assert.equal(requestResult.value.body.tool_choice, undefined);
assert.equal(requestResult.value.body.parallel_tool_calls, true);
});
test("core gateway config installs the local agent dynamic auth runtime hook when OAuth plugins are present", async () => {
const config = createDefaultAppConfig();
config.providerPlugins = [grokOauthProviderPlugin()];
config.Providers = [
{
api_base_url: "https://cli-chat-proxy.grok.com/v1",
api_key: "ccr-local-agent-login",
id: "grok-cli-api",
models: ["grok-4.5"],
name: "Grok CLI API",
type: "openai_responses"
}
];
const compiled = await compileCoreGatewayConfig(
config,
"raw-trace-token",
"billing-usage-token",
"core-auth-token"
);
const plugins = Array.isArray(compiled.plugins) ? compiled.plugins : [];
const localAgentAuthPlugin = plugins.find((plugin) => plugin.key === "ccr-local-agent-auth-provider-hooks");
assert.ok(localAgentAuthPlugin);
assert.match(localAgentAuthPlugin.modulePath, /local-agent-auth-provider-hook\.js$/);
});
test("core gateway config removes Grok unsupported Responses options through declarative request transforms", async (t) => {
await withGrokHome(t, async (grokHome) => {
writeGrokAuth(grokHome, {
expires_at: "2099-01-01T00:00:00Z",
key: "live-grok-access-token",
oidc_client_id: "grok-client-id",
oidc_issuer: "https://auth.x.ai",
refresh_token: "grok-refresh-token"
});
const plugin = grokOauthProviderPlugin();
plugin.request.bodyRemove = ["metadata"];
const config = createDefaultAppConfig();
config.providerPlugins = [plugin];
config.Providers = [
{
api_base_url: "https://cli-chat-proxy.grok.com/v1",
api_key: "ccr-local-agent-login",
id: "grok-cli-api",
models: ["grok-4.5"],
name: "Grok CLI API",
type: "openai_responses"
}
];
const compiled = await compileCoreGatewayConfig(
config,
"raw-trace-token",
"billing-usage-token",
"core-auth-token"
);
const providerPlugins = Array.isArray(compiled.providerPlugins) ? compiled.providerPlugins : [];
const grokPlugin = providerPlugins.find((value) => value.key === "ccr-local-agent-grok-cli-api-grok-cli-oauth");
assert.ok(grokPlugin);
assert.deepEqual(grokPlugin.request.bodyRemove, ["metadata", "external_web_access"]);
});
});
test("local agent OAuth plugin detector only matches managed OAuth imports", () => {
assert.equal(isLocalAgentOauthProviderPlugin(grokOauthProviderPlugin()), true);
assert.equal(isLocalAgentOauthProviderPlugin({
key: "ccr-local-agent-grok-cli-api-grok-cli-api-key"
}), false);
assert.equal(isLocalAgentOauthProviderPlugin({
key: "external-grok-cli-oauth"
}), false);
});
function grokOauthProviderPlugin() {
return {
auth: {
headers: {
authorization: "Bearer imported-stale-token"
},
removeHeaders: ["x-api-key"],
strict: true
},
key: "ccr-local-agent-grok-cli-api-grok-cli-oauth",
providerName: "Grok CLI API",
request: {
headers: {
"x-grok-client-identifier": "xai-grok-cli",
"x-grok-client-version": "0.2.93",
"x-grok-model-override": "{{ model }}"
},
strict: true
}
};
}
async function withGrokHome(t, run) {
const previousGrokHome = process.env.GROK_HOME;
const previousGrokAuthFile = process.env.GROK_AUTH_FILE;
const previousGrokCliVersion = process.env.GROK_CLI_VERSION;
const grokHome = mkdtempSync(path.join(os.tmpdir(), "ccr-grok-hook-test-"));
process.env.GROK_HOME = grokHome;
process.env.GROK_CLI_VERSION = "0.2.93";
delete process.env.GROK_AUTH_FILE;
try {
await run(grokHome);
} finally {
restoreEnv("GROK_HOME", previousGrokHome);
restoreEnv("GROK_AUTH_FILE", previousGrokAuthFile);
restoreEnv("GROK_CLI_VERSION", previousGrokCliVersion);
rmSync(grokHome, { force: true, recursive: true });
}
}
function restoreEnv(name, value) {
if (value === undefined) {
delete process.env[name];
} else {
process.env[name] = value;
}
}
function writeGrokAuth(grokHome, auth) {
writeFileSync(path.join(grokHome, "auth.json"), JSON.stringify({
"https://auth.x.ai::test-account": auth
}, null, 2));
}