Files
orcs-code/src/utils/fingerprint.ts
step325 70cfa61582 fix: disable experimental API betas by default, reduce side query token usage, standardize Headers type (#281)
* fix: disable experimental API betas by default to prevent 500 errors

Tool search (defer_loading), global cache scope, and context management
betas require internal Anthropic server-side support. External accounts
receive 500 Internal Server Error when these are sent.

Set CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=true by default in the CLI
entrypoint. Users with internal access can opt back in with =false.

Also includes: cache key stability fixes (Sonnet 1M latch, system-before-
messages key ordering, resume fingerprint isMeta skip), sideQuery default
cleanup, and /dream command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: standardize API headers to Headers type and enable tengu feature flags by default

* fix: address PR review — dream lock, MCP betas guard, redundant Partial

- Call recordConsolidation() programmatically in /dream instead of
  delegating to model prompt (unreliable)
- Add CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS guard to MCP entrypoint
  (was only in CLI entrypoint, causing 500s in MCP server mode)
- Remove redundant ? markers from SecretValueSource Partial<{}> type

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 01:40:07 +08:00

83 lines
2.5 KiB
TypeScript

import { createHash } from 'crypto'
import type { AssistantMessage, UserMessage } from '../types/message.js'
/**
* Hardcoded salt from backend validation.
* Must match exactly for fingerprint validation to pass.
*/
export const FINGERPRINT_SALT = '59cf53e54c78'
/**
* Extracts text content from the first user message.
*
* @param messages - Array of internal message types
* @returns First text content, or empty string if not found
*/
export function extractFirstMessageText(
messages: (UserMessage | AssistantMessage)[],
): string {
// Skip isMeta messages (system-injected attachments) so the fingerprint
// reflects the actual user input. On --resume, reorderAttachmentsForAPI
// can bubble meta messages before the real first user message, changing
// the fingerprint and breaking cache.
const firstUserMessage =
messages.find(msg => msg.type === 'user' && !msg.isMeta) ??
messages.find(msg => msg.type === 'user')
if (!firstUserMessage) {
return ''
}
const content = firstUserMessage.message.content
if (typeof content === 'string') {
return content
}
if (Array.isArray(content)) {
const textBlock = content.find(block => block.type === 'text')
if (textBlock && textBlock.type === 'text') {
return textBlock.text
}
}
return ''
}
/**
* Computes 3-character fingerprint for Claude Code attribution.
* Algorithm: SHA256(SALT + msg[4] + msg[7] + msg[20] + version)[:3]
* IMPORTANT: Do not change this method without careful coordination with
* 1P and 3P (Bedrock, Vertex, Azure) APIs.
*
* @param messageText - First user message text content
* @param version - Version string (from MACRO.VERSION)
* @returns 3-character hex fingerprint
*/
export function computeFingerprint(
messageText: string,
version: string,
): string {
// Extract chars at indices [4, 7, 20], use "0" if index not found
const indices = [4, 7, 20]
const chars = indices.map(i => messageText[i] || '0').join('')
const fingerprintInput = `${FINGERPRINT_SALT}${chars}${version}`
// SHA256 hash, return first 3 hex chars
const hash = createHash('sha256').update(fingerprintInput).digest('hex')
return hash.slice(0, 3)
}
/**
* Computes fingerprint from the first user message.
*
* @param messages - Array of normalized messages
* @returns 3-character hex fingerprint
*/
export function computeFingerprintFromMessages(
messages: (UserMessage | AssistantMessage)[],
): string {
const firstMessageText = extractFirstMessageText(messages)
return computeFingerprint(firstMessageText, MACRO.VERSION)
}