Skip to content

Commit 12418f6

Browse files
committed
Cap archive grep max results on match hits
Context lines and separators were counted against max_results, so later matching occurrences were dropped.
1 parent cbba84f commit 12418f6

2 files changed

Lines changed: 49 additions & 8 deletions

File tree

src/plugins/evidence-archive-search-plugin.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,36 @@ describe("evidenceArchiveSearchPlugin", () => {
291291
expect(String(hits.content)).toContain(`${ref}:2:beta-hit`);
292292
expect(String(hits.content)).toContain(`${ref}-3-gamma`);
293293
});
294+
295+
test("archive grep max_results caps match hits rather than context lines", async () => {
296+
const archive = memoryArchive("sess-max-results-context");
297+
const first = await archive.recordAuthorizedPayload({
298+
kind: "tool_result",
299+
payload: "before-one\nneedle\nafter-one",
300+
});
301+
const second = await archive.recordAuthorizedPayload({
302+
kind: "tool_result",
303+
payload: "before-two\nneedle\nafter-two",
304+
});
305+
const plugin = evidenceArchiveSearchPlugin(() => archive);
306+
const handler = plugin.middleware ? plugin.middleware(nextHandler) : nextHandler;
307+
const hits = await handler(
308+
makeCall("grep", {
309+
pattern: "needle",
310+
path: "archive:///",
311+
context: 1,
312+
max_results: 2,
313+
}),
314+
new AbortController().signal,
315+
);
316+
const content = String(hits.content);
317+
const firstRef = formatArchiveRef(first.occurrenceId);
318+
const secondRef = formatArchiveRef(second.occurrenceId);
319+
expect(content).toContain(`${firstRef}:2:needle`);
320+
expect(content).toContain(`${secondRef}:2:needle`);
321+
expect(content).toContain(`${firstRef}-1-before-one`);
322+
expect(content).toContain(`${secondRef}-1-before-two`);
323+
});
294324
});
295325

296326
describe("createAgentToolset archive mount", () => {

src/plugins/evidence-archive-search-plugin.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -271,16 +271,19 @@ function grepPayloadHits(
271271
regex: RegExp,
272272
context: number,
273273
remaining: number,
274-
): string[] {
275-
if (remaining <= 0) return [];
274+
): { lines: string[]; matchCount: number } {
275+
if (remaining <= 0) return { lines: [], matchCount: 0 };
276276
const matchLines: number[] = [];
277277
for (let i = 0; i < lines.length; i++) {
278278
if (regex.test(lines[i] ?? "")) matchLines.push(i);
279279
if (matchLines.length >= remaining) break;
280280
}
281-
if (matchLines.length === 0) return [];
281+
if (matchLines.length === 0) return { lines: [], matchCount: 0 };
282282
if (context <= 0) {
283-
return matchLines.map((i) => `${ref}:${i + 1}:${lines[i] ?? ""}`);
283+
return {
284+
lines: matchLines.map((i) => `${ref}:${i + 1}:${lines[i] ?? ""}`),
285+
matchCount: matchLines.length,
286+
};
284287
}
285288
const matchSet = new Set(matchLines);
286289
const ranges: { start: number; end: number }[] = [];
@@ -304,7 +307,7 @@ function grepPayloadHits(
304307
out.push(`${ref}${sep}${i + 1}${sep}${lines[i] ?? ""}`);
305308
}
306309
}
307-
return out;
310+
return { lines: out, matchCount: matchLines.length };
308311
}
309312

310313
async function grepArchive(
@@ -325,13 +328,15 @@ async function grepArchive(
325328
}
326329
const occurrences = await selectOccurrences(archive, occurrenceId);
327330
const hits: string[] = [];
331+
let matchCount = 0;
328332
for (const occ of occurrences) {
329333
signal.throwIfAborted();
330-
if (hits.length >= maxResults) break;
334+
if (matchCount >= maxResults) break;
331335
if (glob !== undefined && !matchesArchiveName(glob, occ)) continue;
332336
const ref = formatArchiveRef(occ.occurrenceId);
333337
if (regex.test(metadataBlob(occ))) {
334338
hits.push(`${ref}:1:${formatHit(occ)}`);
339+
matchCount++;
335340
continue;
336341
}
337342
if (occ.gap === true) continue;
@@ -343,9 +348,15 @@ async function grepArchive(
343348
continue;
344349
}
345350
signal.throwIfAborted();
346-
hits.push(
347-
...grepPayloadHits(ref, payload.split("\n"), regex, context, maxResults - hits.length),
351+
const payloadHits = grepPayloadHits(
352+
ref,
353+
payload.split("\n"),
354+
regex,
355+
context,
356+
maxResults - matchCount,
348357
);
358+
hits.push(...payloadHits.lines);
359+
matchCount += payloadHits.matchCount;
349360
}
350361
if (hits.length === 0) return `no matches for /${pattern}/`;
351362
return hits.join("\n");

0 commit comments

Comments
 (0)