fix(parser): stop warning on normal observation responses (#2074)

parseSummary runs on every agent response, not just summary turns. When the
turn is a normal observation, the LLM correctly emits <observation> and no
<summary> — but the fallthrough branch from #1345 treated this as prompt
misbehavior and logged "prompt conditioning may need strengthening" every
time. That assumption stopped holding after #1633 refactored the caller to
always invoke parseSummary with a coerceFromObservation flag.

Gate the whole observation-on-summary path on coerceFromObservation. On a
real summary turn, coercion still runs and logs the legitimate "coercion
failed" warning when the response has no usable content. On an observation
turn, parseSummary returns null silently, which is the correct behavior.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-04-19 16:30:05 -07:00
committed by GitHub
parent e6e5751ef5
commit 2337997c48
4 changed files with 254 additions and 253 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -137,22 +137,23 @@ export function parseSummary(text: string, sessionId?: number, coerceFromObserva
const summaryMatch = summaryRegex.exec(text);
if (!summaryMatch) {
// When the LLM returns <observation> tags instead of <summary> tags,
// coerce the observation content into summary fields rather than discarding it.
// This breaks the infinite retry loop described in #1633: without coercion,
// the summary is silently dropped, the session completes without a summary,
// a new session is spawned with an ever-growing prompt, and the cycle repeats.
// Only coerce when explicitly requested (i.e., when a summarize message was sent).
if (/<observation>/.test(text)) {
if (coerceFromObservation) {
const coerced = coerceObservationToSummary(text, sessionId);
if (coerced) {
return coerced;
}
logger.warn('PARSER', 'Summary response contained <observation> tags instead of <summary> — coercion failed, no usable content', { sessionId });
} else {
logger.warn('PARSER', 'Summary response contained <observation> tags instead of <summary> — prompt conditioning may need strengthening', { sessionId });
// When the LLM returns <observation> tags instead of <summary> tags on a
// summary turn, coerce the observation content into summary fields rather
// than discarding it. This breaks the infinite retry loop described in
// #1633: without coercion, the summary is silently dropped, the session
// completes without a summary, a new session is spawned with an ever-growing
// prompt, and the cycle repeats.
//
// parseSummary is called on every response (see ResponseProcessor), not just
// summary turns — so the absence of <summary> in an observation response is
// expected, not a prompt-conditioning failure. Only act when the caller
// actually expected a summary (coerceFromObservation=true).
if (coerceFromObservation && /<observation>/.test(text)) {
const coerced = coerceObservationToSummary(text, sessionId);
if (coerced) {
return coerced;
}
logger.warn('PARSER', 'Summary response contained <observation> tags instead of <summary> — coercion failed, no usable content', { sessionId });
}
return null;
}