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:
@@ -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");
|
||||
|
||||
@@ -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)");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user