Files
openclaw/src/agents/provider-attribution.ts
2026-03-16 21:47:16 -07:00

175 lines
5.3 KiB
TypeScript

import type { RuntimeVersionEnv } from "../version.js";
import { resolveRuntimeServiceVersion } from "../version.js";
import { normalizeProviderId } from "./model-selection.js";
export type ProviderAttributionVerification =
| "vendor-documented"
| "vendor-hidden-api-spec"
| "vendor-sdk-hook-only"
| "internal-runtime";
export type ProviderAttributionHook =
| "request-headers"
| "default-headers"
| "user-agent-extra"
| "custom-user-agent";
export type ProviderAttributionPolicy = {
provider: string;
enabledByDefault: boolean;
verification: ProviderAttributionVerification;
hook?: ProviderAttributionHook;
docsUrl?: string;
reviewNote?: string;
product: string;
version: string;
headers?: Record<string, string>;
};
export type ProviderAttributionIdentity = Pick<ProviderAttributionPolicy, "product" | "version">;
const OPENCLAW_ATTRIBUTION_PRODUCT = "OpenClaw";
const OPENCLAW_ATTRIBUTION_ORIGINATOR = "openclaw";
export function resolveProviderAttributionIdentity(
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
): ProviderAttributionIdentity {
return {
product: OPENCLAW_ATTRIBUTION_PRODUCT,
version: resolveRuntimeServiceVersion(env),
};
}
function buildOpenRouterAttributionPolicy(
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
): ProviderAttributionPolicy {
const identity = resolveProviderAttributionIdentity(env);
return {
provider: "openrouter",
enabledByDefault: true,
verification: "vendor-documented",
hook: "request-headers",
docsUrl: "https://openrouter.ai/docs/app-attribution",
reviewNote: "Documented app attribution headers. Verified in OpenClaw runtime wrapper.",
...identity,
headers: {
"HTTP-Referer": "https://openclaw.ai",
"X-OpenRouter-Title": identity.product,
"X-OpenRouter-Categories": "cli-agent",
},
};
}
function buildOpenAIAttributionPolicy(
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
): ProviderAttributionPolicy {
const identity = resolveProviderAttributionIdentity(env);
return {
provider: "openai",
enabledByDefault: true,
verification: "vendor-hidden-api-spec",
hook: "request-headers",
reviewNote:
"OpenAI native traffic supports hidden originator/User-Agent attribution. Verified against the Codex wire contract.",
...identity,
headers: {
originator: OPENCLAW_ATTRIBUTION_ORIGINATOR,
"User-Agent": `${OPENCLAW_ATTRIBUTION_ORIGINATOR}/${identity.version}`,
},
};
}
function buildOpenAICodexAttributionPolicy(
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
): ProviderAttributionPolicy {
const identity = resolveProviderAttributionIdentity(env);
return {
provider: "openai-codex",
enabledByDefault: true,
verification: "vendor-hidden-api-spec",
hook: "request-headers",
reviewNote:
"OpenAI Codex ChatGPT-backed traffic supports the same hidden originator/User-Agent attribution contract.",
...identity,
headers: {
originator: OPENCLAW_ATTRIBUTION_ORIGINATOR,
"User-Agent": `${OPENCLAW_ATTRIBUTION_ORIGINATOR}/${identity.version}`,
},
};
}
function buildSdkHookOnlyPolicy(
provider: string,
hook: ProviderAttributionHook,
reviewNote: string,
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
): ProviderAttributionPolicy {
return {
provider,
enabledByDefault: false,
verification: "vendor-sdk-hook-only",
hook,
reviewNote,
...resolveProviderAttributionIdentity(env),
};
}
export function listProviderAttributionPolicies(
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
): ProviderAttributionPolicy[] {
return [
buildOpenRouterAttributionPolicy(env),
buildOpenAIAttributionPolicy(env),
buildOpenAICodexAttributionPolicy(env),
buildSdkHookOnlyPolicy(
"anthropic",
"default-headers",
"Anthropic JS SDK exposes defaultHeaders, but app attribution is not yet verified.",
env,
),
buildSdkHookOnlyPolicy(
"google",
"user-agent-extra",
"Google GenAI JS SDK exposes userAgentExtra/httpOptions, but provider-side attribution is not yet verified.",
env,
),
buildSdkHookOnlyPolicy(
"groq",
"default-headers",
"Groq JS SDK exposes defaultHeaders, but app attribution is not yet verified.",
env,
),
buildSdkHookOnlyPolicy(
"mistral",
"custom-user-agent",
"Mistral JS SDK exposes a custom userAgent option, but app attribution is not yet verified.",
env,
),
buildSdkHookOnlyPolicy(
"together",
"default-headers",
"Together JS SDK exposes defaultHeaders, but app attribution is not yet verified.",
env,
),
];
}
export function resolveProviderAttributionPolicy(
provider?: string | null,
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
): ProviderAttributionPolicy | undefined {
const normalized = normalizeProviderId(provider ?? "");
return listProviderAttributionPolicies(env).find((policy) => policy.provider === normalized);
}
export function resolveProviderAttributionHeaders(
provider?: string | null,
env: RuntimeVersionEnv = process.env as RuntimeVersionEnv,
): Record<string, string> | undefined {
const policy = resolveProviderAttributionPolicy(provider, env);
if (!policy?.enabledByDefault) {
return undefined;
}
return policy.headers;
}