( query: string, logs: LogOption[], signal?: AbortSignal, )
| 144 | * based on semantic understanding of the query. |
| 145 | */ |
| 146 | export async function agenticSessionSearch( |
| 147 | query: string, |
| 148 | logs: LogOption[], |
| 149 | signal?: AbortSignal, |
| 150 | ): Promise<LogOption[]> { |
| 151 | if (!query.trim() || logs.length === 0) { |
| 152 | return [] |
| 153 | } |
| 154 | |
| 155 | const queryLower = query.toLowerCase() |
| 156 | |
| 157 | // Pre-filter: find sessions that contain the query term |
| 158 | // This ensures we search relevant sessions, not just recent ones |
| 159 | const matchingLogs = logs.filter(log => logContainsQuery(log, queryLower)) |
| 160 | |
| 161 | // Take up to MAX_SESSIONS_TO_SEARCH matching logs |
| 162 | // If fewer matches, fill remaining slots with recent non-matching logs for context |
| 163 | let logsToSearch: LogOption[] |
| 164 | if (matchingLogs.length >= MAX_SESSIONS_TO_SEARCH) { |
| 165 | logsToSearch = matchingLogs.slice(0, MAX_SESSIONS_TO_SEARCH) |
| 166 | } else { |
| 167 | const nonMatchingLogs = logs.filter( |
| 168 | log => !logContainsQuery(log, queryLower), |
| 169 | ) |
| 170 | const remainingSlots = MAX_SESSIONS_TO_SEARCH - matchingLogs.length |
| 171 | logsToSearch = [ |
| 172 | ...matchingLogs, |
| 173 | ...nonMatchingLogs.slice(0, remainingSlots), |
| 174 | ] |
| 175 | } |
| 176 | |
| 177 | // Debug: log what data we have |
| 178 | logForDebugging( |
| 179 | `Agentic search: ${logsToSearch.length}/${logs.length} logs, query="${query}", ` + |
| 180 | `matching: ${matchingLogs.length}, with messages: ${count(logsToSearch, l => l.messages?.length > 0)}`, |
| 181 | ) |
| 182 | |
| 183 | // Load full logs for lite logs to get transcript content |
| 184 | const logsWithTranscriptsPromises = logsToSearch.map(async log => { |
| 185 | if (isLiteLog(log)) { |
| 186 | try { |
| 187 | return await loadFullLog(log) |
| 188 | } catch (error) { |
| 189 | logError(error as Error) |
| 190 | // If loading fails, use the lite log (no transcript) |
| 191 | return log |
| 192 | } |
| 193 | } |
| 194 | return log |
| 195 | }) |
| 196 | const logsWithTranscripts = await Promise.all(logsWithTranscriptsPromises) |
| 197 | |
| 198 | logForDebugging( |
| 199 | `Agentic search: loaded ${count(logsWithTranscripts, l => l.messages?.length > 0)}/${logsToSearch.length} logs with transcripts`, |
| 200 | ) |
| 201 | |
| 202 | // Build session list for the prompt with all searchable metadata |
| 203 | const sessionList = logsWithTranscripts |
nothing calls this directly
no test coverage detected