Skip to content

Commit 8233f9b

Browse files
PierrunoYTclaude
andcommitted
fix(chat): keep API request and reasoning rows after a batched group
batchNearby discarded every ignorable message collected while batching, so the api_req_started and reasoning rows after the last batched target (which belong to the next turn) disappeared. Only consume ignorable items between targets and re-emit the trailing ones. Fixes #2061 Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
1 parent 16425bc commit 8233f9b

2 files changed

Lines changed: 66 additions & 4 deletions

File tree

‎webview-ui/src/utils/__tests__/batchNearby.spec.ts‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,4 +450,62 @@ describe("batchNearby", () => {
450450
expect(result[1].text).toBe("feedback")
451451
expect(result[2].text).toBe('BATCH:{"tool":"readFile","path":"c.ts"},{"tool":"readFile","path":"d.ts"}')
452452
})
453+
454+
describe("ignorable items around a batch", () => {
455+
const options = {
456+
isTarget: isMatch,
457+
isIgnorableBetweenTargets,
458+
isBoundary,
459+
synthesize: synthesizeBatch,
460+
}
461+
462+
test("consumes ignorable items between targets but keeps the ones after the last target", () => {
463+
const betweenRequest = msg("between request", "say", "api_req_started")
464+
const nextRequest = msg("next request", "say", "api_req_started")
465+
const nextReasoning = msg("next reasoning", "say", "reasoning")
466+
467+
const result = batchNearby(
468+
[msg("match-1", "ask"), betweenRequest, msg("match-2", "ask"), nextRequest, nextReasoning],
469+
options,
470+
)
471+
472+
expect(result.map((m) => m.text)).toEqual(["BATCH:match-1,match-2", "next request", "next reasoning"])
473+
})
474+
475+
test("keeps trailing ignorable items when the batch is followed by a boundary", () => {
476+
const result = batchNearby(
477+
[
478+
msg("match-1", "ask"),
479+
msg("match-2", "ask"),
480+
msg("next request", "say", "api_req_started"),
481+
msg("visible text", "say", "text"),
482+
],
483+
options,
484+
)
485+
486+
expect(result.map((m) => m.text)).toEqual(["BATCH:match-1,match-2", "next request", "visible text"])
487+
})
488+
489+
test("keeps ignorable items that follow a single target", () => {
490+
const messages = [msg("match-1", "ask"), msg("next request", "say", "api_req_started")]
491+
492+
expect(batchNearby(messages, options)).toEqual(messages)
493+
})
494+
495+
test("does not duplicate ignorable items between three targets", () => {
496+
const result = batchNearby(
497+
[
498+
msg("match-1", "ask"),
499+
msg("req a", "say", "api_req_started"),
500+
msg("match-2", "ask"),
501+
msg("req b", "say", "api_req_started"),
502+
msg("match-3", "ask"),
503+
msg("req c", "say", "api_req_started"),
504+
],
505+
options,
506+
)
507+
508+
expect(result.map((m) => m.text)).toEqual(["BATCH:match-1,match-2,match-3", "req c"])
509+
})
510+
})
453511
})

‎webview-ui/src/utils/batchNearby.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export function batchNearby<T>(items: T[], options: BatchNearbyOptions<T>): T[]
5757
break // boundary stops the batch
5858
} else if (isTarget(items[j])) {
5959
batch.push(items[j])
60+
// Ignorable items seen so far sit between two targets, so the batch consumes them
61+
pendingIgnorable.length = 0
6062
j++
6163
} else if (isIgnorableBetweenTargets(items[j], batchContext)) {
6264
pendingIgnorable.push(items[j]) // track but don't commit yet
@@ -67,14 +69,16 @@ export function batchNearby<T>(items: T[], options: BatchNearbyOptions<T>): T[]
6769
}
6870

6971
if (batch.length > 1) {
70-
// Bridge succeeded — pending ignorable items are metadata consumed by the batch
72+
// Bridge succeeded — ignorable items between targets were consumed by the batch.
73+
// Those left in pendingIgnorable come after the last target and belong to the
74+
// next turn (its api_req_started / reasoning rows), so they must stay visible.
7175
result.push(synthesize(batch))
7276
} else {
7377
// Bridge failed — restore pending ignorable items to preserve in-order semantics
7478
result.push(batch[0])
75-
if (pendingIgnorable.length > 0) {
76-
result.push(...pendingIgnorable)
77-
}
79+
}
80+
if (pendingIgnorable.length > 0) {
81+
result.push(...pendingIgnorable)
7882
}
7983

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

0 commit comments

Comments
 (0)