fix(coding-agent/eval): hardened eval parser against stray non-marker lines

- Updated parseEvalInput to verify begin-cell markers before dereferencing regex matches.
- Skipped stray non-marker lines between and after cells, preserving valid cells when model output is noisy.
- Added eval parse regression tests for stray content and trailing chatter, and kept abort-line handling explicit.
This commit is contained in:
can1357
2026-05-12 09:41:06 +02:00
parent 2726ee03e3
commit c562f4dc2a
3 changed files with 45 additions and 2 deletions
+1 -1
View File
@@ -397,7 +397,7 @@ function truncateAtLineAndAppendSentinel(
}
function sha8(text: string): string {
return new Bun.CryptoHasher("sha256").update(text).digest("hex").slice(0, 8);
return Bun.sha(text, "hex").slice(0, 8);
}
const PREVIEW_KEEP_RE = new RegExp(`[${SCRIPT_CLASS}\\s】【”“…」「、。]`, "u");
+13 -1
View File
@@ -148,7 +148,19 @@ export function parseEvalInput(input: string): ParsedEvalInput {
}
while (i < lines.length) {
const beginMatch = BEGIN_RE.exec(lines[i])!;
const beginMatch = BEGIN_RE.exec(lines[i]);
if (!beginMatch) {
// Stray content between/after cells (blank lines were already
// consumed). `*** Abort` here terminates parsing; anything else
// — typically a harmony-leak fragment or model garbage — is
// skipped rather than crashing the parser.
if (ABORT_RE.test(lines[i])) {
aborted = true;
break;
}
i++;
continue;
}
const langToken = beginMatch[1];
const explicitLanguage = resolveLang(langToken);
i++;
@@ -270,4 +270,35 @@ print(1)
expect(result.aborted).toBeUndefined();
});
});
it("does not crash on stray non-marker lines between cells", () => {
// Regression: prior to fix, the outer parse loop unconditionally ran
// `BEGIN_RE.exec(lines[i])!` on whatever followed a closed cell, so a
// stray line — common in harmony-leak fragments — threw
// "null is not an object (evaluating 'BEGIN_RE.exec(lines[i])[1]')".
const result = parseEvalInput(`*** Begin PY
print("a")
*** End PY
stray junk that is not a marker
*** Begin PY
print("b")
*** End PY
`);
expect(result.aborted).toBeUndefined();
expect(result.cells).toHaveLength(2);
expect(result.cells[0].code).toBe('print("a")');
expect(result.cells[1].code).toBe('print("b")');
});
it("does not crash on trailing stray content after the final cell", () => {
const result = parseEvalInput(`*** Begin PY
print(1)
*** End PY
leftover model chatter
more junk
`);
expect(result.aborted).toBeUndefined();
expect(result.cells).toHaveLength(1);
expect(result.cells[0].code).toBe("print(1)");
});
});