MCPcopy Index your code
hub / github.com/codeaashu/claude-code / collectCandidatesByMessage

Function collectCandidatesByMessage

src/utils/toolResultStorage.ts:600–639  ·  view source on GitHub ↗

* Extract candidate tool_result blocks grouped by API-level user message. * * normalizeMessagesForAPI merges consecutive user messages into one * (Bedrock compat; 1P does the same server-side), so parallel tool * results that arrive as N separate user messages in our state become * ONE user mes

(
  messages: Message[],
)

Source from the content-addressed store, hash-verified

598 * Only groups with at least one eligible candidate are returned.
599 */
600function collectCandidatesByMessage(
601 messages: Message[],
602): ToolResultCandidate[][] {
603 const groups: ToolResultCandidate[][] = []
604 let current: ToolResultCandidate[] = []
605
606 const flush = () => {
607 if (current.length > 0) groups.push(current)
608 current = []
609 }
610
611 // Track all assistant message.ids seen so far — same-ID fragments are
612 // merged by normalizeMessagesForAPI (messages.ts ~2126 walks back PAST
613 // different-ID assistants via `continue`), so any re-appearance of a
614 // previously-seen ID must NOT create a group boundary. Two scenarios:
615 // • Consecutive: streamingToolExecution yields one AssistantMessage per
616 // content_block_stop (same id); a fast tool drains between blocks;
617 // abort/hook-stop leaves [asst(X), user(trA), asst(X), user(trB)].
618 // • Interleaved: coordinator/teammate streams mix different responses
619 // so [asst(X), user(trA), asst(Y), user(trB), asst(X), user(trC)].
620 // In both, normalizeMessagesForAPI merges the X fragments into one wire
621 // assistant, and their following tool_results merge into one wire user
622 // message — so the budget must see them as one group too.
623 const seenAsstIds = new Set<string>()
624 for (const message of messages) {
625 if (message.type === 'user') {
626 current.push(...collectCandidatesFromMessage(message))
627 } else if (message.type === 'assistant') {
628 if (!seenAsstIds.has(message.message.id)) {
629 flush()
630 seenAsstIds.add(message.message.id)
631 }
632 }
633 // progress / attachment / system are filtered or merged by
634 // normalizeMessagesForAPI — they don't create wire boundaries.
635 }
636 flush()
637
638 return groups
639}
640
641/**
642 * Partition candidates by their prior decision state:

Callers 2

enforceToolResultBudgetFunction · 0.85

Calls 5

flushFunction · 0.70
pushMethod · 0.45
hasMethod · 0.45
addMethod · 0.45

Tested by

no test coverage detected