fix(agent-core): suppress the spurious error event for step-capped goal turns

This commit is contained in:
chengluyu 2026-07-27 00:38:19 +08:00
parent 0cef160c4b
commit aefbc7f727
4 changed files with 25 additions and 1 deletions

View file

@ -633,7 +633,14 @@ export class TurnFlow {
inputData: { errorType: summary.name, errorMessage: summary.message },
});
ended = { type: 'turn.ended', turnId, reason: 'failed', error: summary, durationMs: Date.now() - startedAt };
errorEvent = { type: 'error', ...summary };
// A goal-driven turn cut by the per-turn step limit is expected
// control flow — the goal driver immediately continues with a fresh
// continuation turn — so it must not raise the standalone error event
// that hosts render as an actionable failure. `turn.ended` and the
// `turn.interrupted` loop event still report the cap.
if (!(isMaxStepsExceededError(error) && this.agent.goal.getActiveGoal() !== null)) {
errorEvent = { type: 'error', ...summary };
}
if (this.shouldTrackApiError(turnId)) {
const classification = classifyApiError(error, summary);
const properties: Record<string, TelemetryPropertyValue> = {

View file

@ -1875,6 +1875,15 @@ describe('Agent turn flow', () => {
}),
}),
);
// Without an active goal the cap is a user-facing failure: the standalone
// error event (suppressed for goal-driven turns) is emitted right after
// turn.ended.
expect(ctx.newEvents()).toContainEqual(
expect.objectContaining({
event: 'error',
args: expect.objectContaining({ code: 'loop.max_steps_exceeded' }),
}),
);
});
describe('loop control env overrides', () => {

View file

@ -523,6 +523,9 @@ describe('goal session end-to-end', () => {
ErrorCodes.LOOP_MAX_STEPS_EXCEEDED,
);
expect(maxStepEnds).toHaveLength(2);
// Capped goal turns are expected control flow: no standalone error event
// is emitted for hosts to render as an actionable failure.
expect(events.filter((event) => event['type'] === 'error')).toEqual([]);
// The cap never pauses or blocks the goal: no stopped status is ever
// emitted, and the goal completes (record cleared) in the third turn.
expect(