(messages: T[], briefToolNames: string[])
| 91 | * That's on the model to get right; the filter does not second-guess it. |
| 92 | */ |
| 93 | export function filterForBriefTool<T extends { |
| 94 | type: string; |
| 95 | subtype?: string; |
| 96 | isMeta?: boolean; |
| 97 | isApiErrorMessage?: boolean; |
| 98 | message?: { |
| 99 | content: Array<{ |
| 100 | type: string; |
| 101 | name?: string; |
| 102 | tool_use_id?: string; |
| 103 | }>; |
| 104 | }; |
| 105 | attachment?: { |
| 106 | type: string; |
| 107 | isMeta?: boolean; |
| 108 | origin?: unknown; |
| 109 | commandMode?: string; |
| 110 | }; |
| 111 | }>(messages: T[], briefToolNames: string[]): T[] { |
| 112 | const nameSet = new Set(briefToolNames); |
| 113 | // tool_use always precedes its tool_result in the array, so we can collect |
| 114 | // IDs and match against them in a single pass. |
| 115 | const briefToolUseIDs = new Set<string>(); |
| 116 | return messages.filter(msg => { |
| 117 | // System messages (attach confirmation, remote errors, compact boundaries) |
| 118 | // must stay visible — dropping them leaves the viewer with no feedback. |
| 119 | // Exception: api_metrics is per-turn debug noise (TTFT, config writes, |
| 120 | // hook timing) that defeats the point of brief mode. Still visible in |
| 121 | // transcript mode (ctrl+o) which bypasses this filter. |
| 122 | if (msg.type === 'system') return msg.subtype !== 'api_metrics'; |
| 123 | const block = msg.message?.content[0]; |
| 124 | if (msg.type === 'assistant') { |
| 125 | // API error messages (auth failures, rate limits, etc.) must stay visible |
| 126 | if (msg.isApiErrorMessage) return true; |
| 127 | // Keep Brief tool_use blocks (renders with standard tool call chrome, |
| 128 | // and must be in the list so buildMessageLookups can resolve tool results) |
| 129 | if (block?.type === 'tool_use' && block.name && nameSet.has(block.name)) { |
| 130 | if ('id' in block) { |
| 131 | briefToolUseIDs.add((block as { |
| 132 | id: string; |
| 133 | }).id); |
| 134 | } |
| 135 | return true; |
| 136 | } |
| 137 | return false; |
| 138 | } |
| 139 | if (msg.type === 'user') { |
| 140 | if (block?.type === 'tool_result') { |
| 141 | return block.tool_use_id !== undefined && briefToolUseIDs.has(block.tool_use_id); |
| 142 | } |
| 143 | // Real user input only — drop meta/tick messages. |
| 144 | return !msg.isMeta; |
| 145 | } |
| 146 | if (msg.type === 'attachment') { |
| 147 | // Human input drained mid-turn arrives as a queued_command attachment |
| 148 | // (query.ts mid-chain drain → getQueuedCommandAttachments). Keep it — |
| 149 | // it's what the user typed. commandMode === 'prompt' positively |
| 150 | // identifies human-typed input; task-notification callers set |
no test coverage detected