MCPcopy
hub / github.com/claude-code-best/claude-code / normalizeMessagesForAPI

Function normalizeMessagesForAPI

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

Source from the content-addressed store, hash-verified

2290}
2291
2292export function normalizeMessagesForAPI(
2293 messages: Message[],
2294 tools: Tools = [],
2295): (UserMessage | AssistantMessage)[] {
2296 // Build set of available tool names for filtering unavailable tool references
2297 const availableToolNames = new Set(tools.map(t => t.name))
2298
2299 // First, reorder attachments to bubble up until they hit a tool result or assistant message
2300 // Then strip virtual messages — they're display-only (e.g. REPL inner tool
2301 // calls) and must never reach the API.
2302 const reorderedMessages = reorderAttachmentsForAPI(messages).filter(
2303 m => !((m.type === 'user' || m.type === 'assistant') && m.isVirtual),
2304 )
2305
2306 // Build a map from error text → which block types to strip from the preceding user message.
2307 const errorToBlockTypes: Record<string, Set<string>> = {
2308 [getPdfTooLargeErrorMessage()]: new Set(['document']),
2309 [getPdfPasswordProtectedErrorMessage()]: new Set(['document']),
2310 [getPdfInvalidErrorMessage()]: new Set(['document']),
2311 [getImageTooLargeErrorMessage()]: new Set(['image']),
2312 [getRequestTooLargeErrorMessage()]: new Set(['document', 'image']),
2313 }
2314
2315 // Walk the reordered messages to build a targeted strip map:
2316 // userMessageUUID → set of block types to strip from that message.
2317 const stripTargets = new Map<string, Set<string>>()
2318 for (let i = 0; i < reorderedMessages.length; i++) {
2319 const msg = reorderedMessages[i]!
2320 if (!isSyntheticApiErrorMessage(msg)) {
2321 continue
2322 }
2323 // Determine which error this is
2324 const errorText =
2325 Array.isArray(msg.message.content) &&
2326 msg.message.content[0]?.type === 'text'
2327 ? msg.message.content[0].text
2328 : undefined
2329 if (!errorText) {
2330 continue
2331 }
2332 const blockTypesToStrip = errorToBlockTypes[errorText]
2333 if (!blockTypesToStrip) {
2334 continue
2335 }
2336 // Walk backward to find the nearest preceding isMeta user message
2337 for (let j = i - 1; j >= 0; j--) {
2338 const candidate = reorderedMessages[j]!
2339 if (candidate.type === 'user' && candidate.isMeta) {
2340 const existing = stripTargets.get(candidate.uuid)
2341 if (existing) {
2342 for (const t of blockTypesToStrip) {
2343 existing.add(t)
2344 }
2345 } else {
2346 stripTargets.set(candidate.uuid, new Set(blockTypesToStrip))
2347 }
2348 break
2349 }

Callers 13

messages.test.tsFile · 0.90
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
queryModelGeminiFunction · 0.85
queryModelGrokFunction · 0.85

Tested by

no test coverage detected