diff --git a/src/services/api/claude.ts b/src/services/api/claude.ts index 6795a6d2..40b23105 100644 --- a/src/services/api/claude.ts +++ b/src/services/api/claude.ts @@ -281,7 +281,7 @@ export function getExtraBodyParams(betaHeaders?: string[]): JsonObject { const parsed = safeParseJSON(extraBodyStr) // We expect an object with key-value pairs to spread into API parameters if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { - // Shallow clone 鈥?safeParseJSON is LRU-cached and returns the same + // Shallow clone — safeParseJSON is LRU-cached and returns the same // object reference for the same string. Mutating `result` below // would poison the cache, causing stale values to persist. result = { ...(parsed as JsonObject) } @@ -384,15 +384,15 @@ export function getCacheControl({ * GrowthBook config shape: { allowlist: string[] } * Patterns support trailing '*' for prefix matching. * Examples: - * - { allowlist: ["repl_main_thread*", "sdk"] } 鈥?main thread + SDK only - * - { allowlist: ["repl_main_thread*", "sdk", "agent:*"] } 鈥?also subagents - * - { allowlist: ["*"] } 鈥?all sources + * - { allowlist: ["repl_main_thread*", "sdk"] } — main thread + SDK only + * - { allowlist: ["repl_main_thread*", "sdk", "agent:*"] } — also subagents + * - { allowlist: ["*"] } — all sources * - * The allowlist is cached in STATE for session stability 鈥?prevents mixed + * The allowlist is cached in STATE for session stability — prevents mixed * TTLs when GrowthBook's disk cache updates mid-request. */ function should1hCacheTTL(querySource?: QuerySource): boolean { - // 3P Bedrock users get 1h TTL when opted in via env var 鈥?they manage their own billing + // 3P Bedrock users get 1h TTL when opted in via env var — they manage their own billing // No GrowthBook gating needed since 3P users don't have GrowthBook configured if ( getAPIProvider() === 'bedrock' && @@ -401,7 +401,7 @@ function should1hCacheTTL(querySource?: QuerySource): boolean { return true } - // Latch eligibility in bootstrap state for session stability 鈥?prevents + // Latch eligibility in bootstrap state for session stability — prevents // mid-session overage flips from changing the cache_control TTL, which // would bust the server-side prompt cache (~20K tokens per flip). let userEligible = getPromptCache1hEligible() @@ -413,7 +413,7 @@ function should1hCacheTTL(querySource?: QuerySource): boolean { } if (!userEligible) return false - // Cache allowlist in bootstrap state for session stability 鈥?prevents mixed + // Cache allowlist in bootstrap state for session stability — prevents mixed // TTLs when GrowthBook's disk cache updates mid-request let allowlist = getPromptCache1hAllowlist() if (allowlist === null) { @@ -466,7 +466,7 @@ function configureEffortParams( } } -// output_config.task_budget 鈥?API-side token budget awareness for the model. +// output_config.task_budget — API-side token budget awareness for the model. // Stainless SDK types don't yet include task_budget on BetaOutputConfig, so we // define the wire shape locally and cast. The API validates on receipt; see // api/api/schemas/messages/request/output_config.py:12-39 in the monorepo. @@ -701,7 +701,7 @@ export type Options = { advisorModel?: string addNotification?: (notif: Notification) => void // API-side task budget (output_config.task_budget). Distinct from the - // tokenBudget.ts +500k auto-continue feature 鈥?this one is sent to the API + // tokenBudget.ts +500k auto-continue feature — this one is sent to the API // so the model can pace itself. `remaining` is computed by the caller // (query.ts decrements across the agentic loop). taskBudget?: { total: number; remaining?: number } @@ -802,7 +802,7 @@ function shouldDeferLspTool(tool: Tool): boolean { * (~5min) so a hung fallback to a wedged backend surfaces a clean * APIConnectionTimeoutError instead of stalling past SIGKILL. * - * Otherwise defaults to 300s 鈥?long enough for slow backends without + * Otherwise defaults to 300s — long enough for slow backends without * approaching the API's 10-minute non-streaming boundary. */ function getNonstreamingFallbackTimeoutMs(): number { @@ -873,7 +873,7 @@ export async function* executeNonStreamingRequest( }, ) } catch (err) { - // User aborts are not errors 鈥?re-throw immediately without logging + // User aborts are not errors — re-throw immediately without logging if (err instanceof APIUserAbortError) throw err // Instrumentation: record when the non-streaming request errors (including @@ -1073,7 +1073,7 @@ async function* queryModel( return } - // Check cheap conditions first 鈥?the off-switch await blocks on GrowthBook + // Check cheap conditions first — the off-switch await blocks on GrowthBook // init (~10ms). For non-Opus models (haiku, sonnet) this skips the await // entirely. Subscribers don't hit this path at all. if ( @@ -1173,7 +1173,7 @@ async function* queryModel( 'query', ) - // Precompute once 鈥?isDeferredTool does 2 GrowthBook lookups per call + // Precompute once — isDeferredTool does 2 GrowthBook lookups per call const deferredToolNames = new Set() if (useToolSearch) { for (const t of tools) { @@ -1348,7 +1348,7 @@ async function* queryModel( // tool_uses and strips orphaned tool_results referencing non-existent tool_uses. messagesForAPI = ensureToolResultPairing(messagesForAPI) - // Strip advisor blocks 鈥?the API rejects them without the beta header. + // Strip advisor blocks — the API rejects them without the beta header. if (!betas.includes(ADVISOR_BETA_HEADER)) { messagesForAPI = stripAdvisorBlocks(messagesForAPI) } @@ -1736,7 +1736,7 @@ async function* queryModel( ) } - // Only send temperature when thinking is disabled 鈥?the API requires + // Only send temperature when thinking is disabled — the API requires // temperature: 1 when thinking is enabled, which is already the default. const temperature = !hasThinking ? (options.temperatureOverride ?? 1) @@ -1778,7 +1778,7 @@ async function* queryModel( // Compute log scalars synchronously so the fire-and-forget .then() closure // captures only primitives instead of paramsFromContext's full closure scope - // (messagesForAPI, system, allTools, betas 鈥?the entire request-building + // (messagesForAPI, system, allTools, betas — the entire request-building // context), which would otherwise be pinned until the promise resolves. { const queryParams = paramsFromContext({ @@ -1857,7 +1857,7 @@ async function* queryModel( // Generate and track client request ID so timeouts (which return no // server request ID) can still be correlated with server logs. - // First-party only 鈥?3P providers don't log it (inc-4029 class). + // First-party only — 3P providers don't log it (inc-4029 class). clientRequestId = getAPIProvider() === 'firstParty' && isFirstPartyAnthropicBaseUrl() ? randomUUID() @@ -2329,7 +2329,7 @@ async function* queryModel( max_tokens: maxOutputTokens, output_tokens: usage.output_tokens, }) - // Reuse the max_output_tokens recovery path 鈥?from the model's + // Reuse the max_output_tokens recovery path — from the model's // perspective, both mean "response was cut off, continue from // where you left off." yield createAssistantAPIErrorMessage({ @@ -2646,7 +2646,7 @@ async function* queryModel( } catch (errorFromRetry) { // FallbackTriggeredError must propagate to query.ts, which performs the // actual model switch. Swallowing it here would turn the fallback into a - // no-op 鈥?the user would just see "Model fallback triggered: X -> Y" as + // no-op — the user would just see "Model fallback triggered: X -> Y" as // an error message with no actual retry on the fallback model. if (errorFromRetry instanceof FallbackTriggeredError) { throw errorFromRetry @@ -2665,7 +2665,7 @@ async function* queryModel( if (is404StreamCreationError) { // 404 is thrown at .withResponse() before streamRequestId is assigned, - // and CannotRetryError means every retry failed 鈥?so grab the failed + // and CannotRetryError means every retry failed — so grab the failed // request's ID from the error header instead. const failedRequestId = (errorFromRetry.originalError as APIError).requestID ?? 'unknown' @@ -2886,7 +2886,7 @@ async function* queryModel( // Track the last requestId for the main conversation chain so shutdown // can send a cache eviction hint to inference. Exclude backgrounded // sessions (Ctrl+B) which share the repl_main_thread querySource but - // run inside an agent context 鈥?they are independent conversation chains + // run inside an agent context — they are independent conversation chains // whose cache should not be evicted when the foreground session clears. if ( streamRequestId && @@ -3067,7 +3067,7 @@ export function accumulateUsage( totalUsage.cache_creation.ephemeral_5m_input_tokens + messageUsage.cache_creation.ephemeral_5m_input_tokens, }, - // See comment in updateUsage 鈥?field is not on NonNullableUsage to keep + // See comment in updateUsage — field is not on NonNullableUsage to keep // the string out of external builds. ...(feature('CACHED_MICROCOMPACT') ? { @@ -3128,7 +3128,7 @@ export function addCacheBreakpoints( // local-attention KV pages at any cached prefix position NOT in // cache_store_int_token_boundaries. With two markers the second-to-last // position is protected and its locals survive an extra turn even though - // nothing will ever resume from there 鈥?with one marker they're freed + // nothing will ever resume from there — with one marker they're freed // immediately. For fire-and-forget forks (skipCacheWrite) we shift the // marker to the second-to-last message: that's the last shared-prefix // point, so the write is a no-op merge on mycro (entry already exists) @@ -3227,7 +3227,7 @@ export function addCacheBreakpoints( // Add cache_reference to tool_result blocks that are strictly before // the last cache_control marker. The API requires cache_reference to - // appear "before or on" the last cache_control 鈥?we use strict "before" + // appear "before or on" the last cache_control — we use strict "before" // to avoid edge cases where cache_edits splicing shifts block indices. // // Create new objects instead of mutating in-place to avoid contaminating