MCPcopy Create free account
hub / github.com/WJX20/claude-code / normalizeMessagesForAPI

Function normalizeMessagesForAPI

src/utils/messages.ts:2008–2391  ·  view source on GitHub ↗
(
  messages: Message[],
  tools: Tools = [],
)

Source from the content-addressed store, hash-verified

2006}
2007
2008export function normalizeMessagesForAPI(
2009 messages: Message[],
2010 tools: Tools = [],
2011): (UserMessage | AssistantMessage)[] {
2012 // Build set of available tool names for filtering unavailable tool references
2013 const availableToolNames = new Set(tools.map(t => t.name))
2014
2015 // First, reorder attachments to bubble up until they hit a tool result or assistant message
2016 // Then strip virtual messages — they're display-only (e.g. REPL inner tool
2017 // calls) and must never reach the API.
2018 const reorderedMessages = reorderAttachmentsForAPI(messages).filter(
2019 m => !((m.type === 'user' || m.type === 'assistant') && m.isVirtual),
2020 )
2021
2022 // Build a map from error text → which block types to strip from the preceding user message.
2023 const errorToBlockTypes: Record<string, Set<string>> = {
2024 [getPdfTooLargeErrorMessage()]: new Set(['document']),
2025 [getPdfPasswordProtectedErrorMessage()]: new Set(['document']),
2026 [getPdfInvalidErrorMessage()]: new Set(['document']),
2027 [getImageTooLargeErrorMessage()]: new Set(['image']),
2028 [getRequestTooLargeErrorMessage()]: new Set(['document', 'image']),
2029 }
2030
2031 // Walk the reordered messages to build a targeted strip map:
2032 // userMessageUUID → set of block types to strip from that message.
2033 const stripTargets = new Map<string, Set<string>>()
2034 for (let i = 0; i < reorderedMessages.length; i++) {
2035 const msg = reorderedMessages[i]!
2036 if (!isSyntheticApiErrorMessage(msg)) {
2037 continue
2038 }
2039 // Determine which error this is
2040 const errorText =
2041 Array.isArray(msg.message.content) &&
2042 msg.message.content[0]?.type === 'text'
2043 ? msg.message.content[0].text
2044 : undefined
2045 if (!errorText) {
2046 continue
2047 }
2048 const blockTypesToStrip = errorToBlockTypes[errorText]
2049 if (!blockTypesToStrip) {
2050 continue
2051 }
2052 // Walk backward to find the nearest preceding isMeta user message
2053 for (let j = i - 1; j >= 0; j--) {
2054 const candidate = reorderedMessages[j]!
2055 if (candidate.type === 'user' && candidate.isMeta) {
2056 const existing = stripTargets.get(candidate.uuid)
2057 if (existing) {
2058 for (const t of blockTypesToStrip) {
2059 existing.add(t)
2060 }
2061 } else {
2062 stripTargets.set(candidate.uuid, new Set(blockTypesToStrip))
2063 }
2064 break
2065 }

Callers 9

queryLoopFunction · 0.85
FeedbackFunction · 0.85
submitTranscriptShareFunction · 0.85
generateAgentFunction · 0.85
analyzeContextFunction · 0.85
approximateMessageTokensFunction · 0.85
withVCRFunction · 0.85
streamCompactSummaryFunction · 0.85
queryModelFunction · 0.85

Tested by

no test coverage detected