Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions webview-ui/src/utils/__tests__/batchNearby.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,4 +450,62 @@ describe("batchNearby", () => {
expect(result[1].text).toBe("feedback")
expect(result[2].text).toBe('BATCH:{"tool":"readFile","path":"c.ts"},{"tool":"readFile","path":"d.ts"}')
})

describe("ignorable items around a batch", () => {
const options = {
isTarget: isMatch,
isIgnorableBetweenTargets,
isBoundary,
synthesize: synthesizeBatch,
}

test("consumes ignorable items between targets but keeps the ones after the last target", () => {
const betweenRequest = msg("between request", "say", "api_req_started")
const nextRequest = msg("next request", "say", "api_req_started")
const nextReasoning = msg("next reasoning", "say", "reasoning")

const result = batchNearby(
[msg("match-1", "ask"), betweenRequest, msg("match-2", "ask"), nextRequest, nextReasoning],
options,
)

expect(result.map((m) => m.text)).toEqual(["BATCH:match-1,match-2", "next request", "next reasoning"])
})

test("keeps trailing ignorable items when the batch is followed by a boundary", () => {
const result = batchNearby(
[
msg("match-1", "ask"),
msg("match-2", "ask"),
msg("next request", "say", "api_req_started"),
msg("visible text", "say", "text"),
],
options,
)

expect(result.map((m) => m.text)).toEqual(["BATCH:match-1,match-2", "next request", "visible text"])
})

test("keeps ignorable items that follow a single target", () => {
const messages = [msg("match-1", "ask"), msg("next request", "say", "api_req_started")]

expect(batchNearby(messages, options)).toEqual(messages)
})

test("does not duplicate ignorable items between three targets", () => {
const result = batchNearby(
[
msg("match-1", "ask"),
msg("req a", "say", "api_req_started"),
msg("match-2", "ask"),
msg("req b", "say", "api_req_started"),
msg("match-3", "ask"),
msg("req c", "say", "api_req_started"),
],
options,
)

expect(result.map((m) => m.text)).toEqual(["BATCH:match-1,match-2,match-3", "req c"])
})
})
})
12 changes: 8 additions & 4 deletions webview-ui/src/utils/batchNearby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
break // boundary stops the batch
} else if (isTarget(items[j])) {
batch.push(items[j])
// Ignorable items seen so far sit between two targets, so the batch consumes them
pendingIgnorable.length = 0
j++
} else if (isIgnorableBetweenTargets(items[j], batchContext)) {
pendingIgnorable.push(items[j]) // track but don't commit yet
Expand All @@ -67,14 +69,16 @@
}

if (batch.length > 1) {
// Bridge succeeded — pending ignorable items are metadata consumed by the batch
// Bridge succeeded — ignorable items between targets were consumed by the batch.
// Those left in pendingIgnorable come after the last target and belong to the
// next turn (its api_req_started / reasoning rows), so they must stay visible.
result.push(synthesize(batch))
} else {
// Bridge failed — restore pending ignorable items to preserve in-order semantics
result.push(batch[0])
if (pendingIgnorable.length > 0) {
result.push(...pendingIgnorable)
}
}
if (pendingIgnorable.length > 0) {

Check warning on line 80 in webview-ui/src/utils/batchNearby.ts

View workflow job for this annotation

GitHub Actions / mutation-diff

Mutation test advisory

webview-ui/src/utils/batchNearby.ts:80: 2 mutation test gaps; example: Survived ConditionalExpression mutant (replacement: true). See the job summary for the complete list and resolution guidance.
result.push(...pendingIgnorable)
}

i = j // items[j] was not consumed — re-examine it on next iteration
Expand Down
Loading