Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/goal-step-cap-error-banner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Stop showing a spurious error banner when a goal turn reaches the per-turn step limit (`loop_control.max_steps_per_turn`) during autonomous goal pursuit; the limit is expected control flow there and still surfaces for ordinary turns.
9 changes: 8 additions & 1 deletion packages/agent-core/src/agent/turn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> = {
Expand Down
9 changes: 9 additions & 0 deletions packages/agent-core/test/agent/turn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/agent-core/test/harness/goal-session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading