Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | /** * Utility functions for message filtering, grouping, and manipulation */ import { combineApiRequests } from "@shared/combineApiRequests" import { combineCommandSequences } from "@shared/combineCommandSequences" import { AiHydroMessage, AiHydroSayBrowserAction } from "@shared/ExtensionMessage" /** * Combine API requests and command sequences in messages */ export function processMessages(messages: AiHydroMessage[]): AiHydroMessage[] { return combineApiRequests(combineCommandSequences(messages)) } /** * Filter messages that should be visible in the chat */ export function filterVisibleMessages(messages: AiHydroMessage[]): AiHydroMessage[] { return messages.filter((message) => { switch (message.ask) { case "completion_result": // don't show a chat row for a completion_result ask without text. This specific type of message only occurs if AI-Hydro wants to execute a command as part of its completion result, in which case we interject the completion_result tool with the execute_command tool. if (message.text === "") { return false } break case "api_req_failed": // this message is used to update the latest api_req_started that the request failed case "resume_task": case "resume_completed_task": return false } switch (message.say) { case "api_req_finished": // combineApiRequests removes this from modifiedMessages anyways case "api_req_retried": // this message is used to update the latest api_req_started that the request was retried case "deleted_api_reqs": // aggregated api_req metrics from deleted messages case "task_progress": // task progress messages are displayed in TaskHeader, not in main chat return false case "text": // Sometimes AI-Hydro returns an empty text message, we don't want to render these. (We also use a say text for user messages, so in case they just sent images we still render that) if ((message.text ?? "") === "" && (message.images?.length ?? 0) === 0) { return false } break case "mcp_server_request_started": return false } return true }) } /** * Check if a message is part of a browser session */ export function isBrowserSessionMessage(message: AiHydroMessage): boolean { if (message.type === "ask") { return ["browser_action_launch"].includes(message.ask!) } if (message.type === "say") { return [ "browser_action_launch", "api_req_started", "text", "browser_action", "browser_action_result", "checkpoint_created", "reasoning", ].includes(message.say!) } return false } /** * Group messages, combining browser session messages into arrays */ export function groupMessages(visibleMessages: AiHydroMessage[]): (AiHydroMessage | AiHydroMessage[])[] { const result: (AiHydroMessage | AiHydroMessage[])[] = [] let currentGroup: AiHydroMessage[] = [] let isInBrowserSession = false const endBrowserSession = () => { if (currentGroup.length > 0) { result.push([...currentGroup]) currentGroup = [] isInBrowserSession = false } } visibleMessages.forEach((message) => { if (message.ask === "browser_action_launch" || message.say === "browser_action_launch") { // complete existing browser session if any endBrowserSession() // start new isInBrowserSession = true currentGroup.push(message) } else if (isInBrowserSession) { // end session if api_req_started is cancelled if (message.say === "api_req_started") { // get last api_req_started in currentGroup to check if it's cancelled const lastApiReqStarted = [...currentGroup].reverse().find((m) => m.say === "api_req_started") if (lastApiReqStarted?.text != null) { const info = JSON.parse(lastApiReqStarted.text) const isCancelled = info.cancelReason != null if (isCancelled) { endBrowserSession() result.push(message) return } } } if (isBrowserSessionMessage(message)) { currentGroup.push(message) // Check if this is a close action if (message.say === "browser_action") { const browserAction = JSON.parse(message.text || "{}") as AiHydroSayBrowserAction if (browserAction.action === "close") { endBrowserSession() } } } else { // complete existing browser session if any endBrowserSession() result.push(message) } } else { result.push(message) } }) // Handle case where browser session is the last group if (currentGroup.length > 0) { result.push([...currentGroup]) } return result } /** * Get the task message from the messages array */ export function getTaskMessage(messages: AiHydroMessage[]): AiHydroMessage | undefined { return messages.at(0) } /** * Check if we should show the scroll to bottom button */ export function shouldShowScrollButton(disableAutoScroll: boolean, isAtBottom: boolean): boolean { return disableAutoScroll && !isAtBottom } |