// Critical system constants extracted to break circular dependencies import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' import { logForDebugging } from '../utils/debug.js' import { isEnvDefinedFalsy } from '../utils/envUtils.js' import { getAPIProvider } from '../utils/model/providers.js' import { getWorkload } from '../utils/workloadContext.js' import { formatClaudeCodeBillingHeader } from './claudeCodeCompatibility.js' const DEFAULT_PREFIX = `You are Claude Code, Anthropic's official CLI for Claude.` const AGENT_SDK_CLAUDE_CODE_PRESET_PREFIX = `You are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK.` const AGENT_SDK_PREFIX = `You are a Claude agent, built on Anthropic's Claude Agent SDK.` const CLI_SYSPROMPT_PREFIX_VALUES = [ DEFAULT_PREFIX, AGENT_SDK_CLAUDE_CODE_PRESET_PREFIX, AGENT_SDK_PREFIX, ] as const export type CLISyspromptPrefix = (typeof CLI_SYSPROMPT_PREFIX_VALUES)[number] /** * All possible CLI sysprompt prefix values, used by splitSysPromptPrefix * to identify prefix blocks by content rather than position. */ export const CLI_SYSPROMPT_PREFIXES: ReadonlySet = new Set( CLI_SYSPROMPT_PREFIX_VALUES, ) export function getCLISyspromptPrefix(options?: { isNonInteractive: boolean hasAppendSystemPrompt: boolean }): CLISyspromptPrefix { const apiProvider = getAPIProvider() if (apiProvider === 'vertex') { return DEFAULT_PREFIX } if (options?.isNonInteractive) { if (options.hasAppendSystemPrompt) { return AGENT_SDK_CLAUDE_CODE_PRESET_PREFIX } return AGENT_SDK_PREFIX } return DEFAULT_PREFIX } /** * Check if attribution header is enabled. * Enabled by default, can be disabled via env var or GrowthBook killswitch. */ function isAttributionHeaderEnabled(): boolean { if (isEnvDefinedFalsy(process.env.CLAUDE_CODE_ATTRIBUTION_HEADER)) { return false } return getFeatureValue_CACHED_MAY_BE_STALE('tengu_attribution_header', true) } /** * Get attribution header for API requests. * Returns a header string with cc_version (including fingerprint) and cc_entrypoint. * Enabled by default, can be disabled via env var or GrowthBook killswitch. * * Includes a `cch=00000` placeholder. Before the request is sent, * signClaudeCodeCCHBody overwrites the zeros with a computed hash. * The server verifies this token to confirm the request came from a real * Claude Code client. */ export function getAttributionHeader(fingerprint: string): string { if (!isAttributionHeaderEnabled()) { return '' } const entrypoint = process.env.CLAUDE_CODE_ENTRYPOINT ?? 'unknown' // cc_workload: turn-scoped hint so the API can route e.g. cron-initiated // requests to a lower QoS pool. Absent = interactive default. Safe re: // fingerprint (computed from msg chars + version only) and cch signature // (placeholder overwritten in serialized body bytes after this string is // built). Server _parse_cc_header tolerates unknown extra fields so old API // deploys silently ignore this. const header = formatClaudeCodeBillingHeader({ fingerprint, entrypoint, workload: getWorkload(), }) logForDebugging(`attribution header ${header}`) return header }