fix(chat): searchable tool/reasoning rows and live markdown (#1361)

This commit is contained in:
m57 authored and GitHub committed 2026-09-28 19:31:43 +03:00
1 parent 580f3b9697
commit 85903afd38
19 files changed
+454 -146

No files matched your search

@@ -34,15 +34,25 @@ class ChatSearchControllerTest {
}
@Test
fun `excludes tool rows even when the payload contains the query`() {
fun `finds visible tool name without indexing hidden raw payload`() {
val messages =
listOf(
message(MessageRole.USER, "check the logs"),
message(MessageRole.TOOL, "{\"result\": \"check the logs: all green\"}"),
message(MessageRole.ASSISTANT, "all good"),
message(MessageRole.TOOL, "unrendered private payload").copy(toolName = "terminal"),
)
assertEquals(listOf(SearchMatch(0, 0)), hits(controller.findMatches(messages, "check the logs")))
assertEquals(listOf(SearchMatch(1, 0, SearchTarget.TOOL)), hits(controller.findMatches(messages, "terminal")))
assertEquals(emptyList<SearchMatch>(), hits(controller.findMatches(messages, "private payload")))
}
@Test
fun `system marker carried as user does not yield an invisible hit`() {
val marker =
message(
MessageRole.USER,
"You've reached the maximum number of tool-calling iterations allowed.",
)
assertEquals(emptyList<SearchMatch>(), hits(controller.findMatches(listOf(marker), "maximum")))
}
@Test
@@ -57,7 +67,7 @@ class ChatSearchControllerTest {
}
@Test
fun `excludes reasoning text that never made it to visible content`() {
fun `finds reasoning text independently of assistant prose`() {
val messages =
listOf(
message(
@@ -67,8 +77,10 @@ class ChatSearchControllerTest {
),
)
// Only the visible content is searchable — reasoning is invisible.
assertEquals(emptyList<SearchMatch>(), hits(controller.findMatches(messages, "deployment")))
assertEquals(
listOf(SearchMatch(0, 19, SearchTarget.REASONING)),
hits(controller.findMatches(messages, "deployment")),
)
assertEquals(listOf(SearchMatch(0, 12)), hits(controller.findMatches(messages, "answer")))
}
@@ -139,6 +139,39 @@ class ChatSearchDelegateTest {
assertEquals(0, delegate.searchState.currentIndex)
}
@Test
fun `search tracks target kind while navigating reasoning tool and prose`() =
runTest {
val uiState =
MutableStateFlow(
ChatUiState(
messages =
listOf(
ChatMessage("m0", MessageRole.ASSISTANT, "answer", reasoningText = "thinking"),
ChatMessage("m1", MessageRole.TOOL, "opaque", toolName = "thinking_tool"),
),
),
)
val delegate =
ChatSearchDelegate(
scope = backgroundScope,
uiState = uiState,
dispatcher = StandardTestDispatcher(testScheduler),
debounceMs = 150,
)
delegate.setSearchQuery("thinking")
advanceTimeBy(150)
runCurrent()
assertEquals(listOf(SearchTarget.REASONING, SearchTarget.TOOL), delegate.searchState.matchTargets)
assertEquals("m0", delegate.searchState.currentMatchId)
delegate.navigateSearchMatch(1)
assertEquals("m1", delegate.searchState.currentMatchId)
assertEquals(SearchTarget.TOOL, delegate.searchState.matchTargets[delegate.searchState.currentIndex])
delegate.clearSearch()
assertEquals(emptyList<SearchTarget>(), delegate.searchState.matchTargets)
}
@Test
fun `clearSearch resets everything including matched sets`() =
runTest {
@@ -3,6 +3,7 @@ package com.m57.hermescontrol.ui.chat
import com.m57.hermescontrol.ui.chat.fullbleed.fullBleedItemKeys
import com.m57.hermescontrol.ui.chat.fullbleed.groupIntoTurns
import com.m57.hermescontrol.ui.chat.fullbleed.messageIdToLazyIndex
import com.m57.hermescontrol.ui.chat.fullbleed.searchMatchToLazyIndex
import org.junit.Assert.assertEquals
import org.junit.Test
@@ -24,6 +25,21 @@ class FullBleedItemKeysTest {
assertEquals(mapOf("u" to 2, "a" to 5), messageIdToLazyIndex(turns, leadingItems = 2))
}
@Test
fun searchTargetsResolveHoistedReasoningAndToolRows() {
val messages =
listOf(
ChatMessage("u", MessageRole.USER, "question"),
ChatMessage("t", MessageRole.TOOL, "output", toolName = "terminal"),
ChatMessage("a", MessageRole.ASSISTANT, "answer", reasoningText = "thinking"),
)
val turns = groupIntoTurns(messages)
assertEquals(1, searchMatchToLazyIndex(turns, messages, SearchMatch(2, 0, SearchTarget.REASONING)))
assertEquals(2, searchMatchToLazyIndex(turns, messages, SearchMatch(1, 0, SearchTarget.TOOL)))
assertEquals(3, searchMatchToLazyIndex(turns, messages, SearchMatch(2, 0)))
}
@Test
fun toolAnchorSurvivesPrependThatExtendsItsAgentTurn() {
val tool = ChatMessage("live-tool", MessageRole.TOOL, "output")