-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix(agent-core-v2): carry unmaterialized steer requests over to the next turn #3445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| Fix loss of Ctrl+S steered messages when the current turn is interrupted. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -442,11 +442,11 @@ export class AgentLoopService extends Disposable implements IAgentLoopService { | |
| return step; | ||
| } | ||
|
|
||
| private cancelStep(job: TurnJob, step: MutableStep, request: StepRequest, reason?: unknown): boolean { | ||
| private cancelStep(job: TurnJob, step: MutableStep, request: StepRequest, reason?: unknown, abortRequest = true): boolean { | ||
| if (step.state === 'completed' || step.state === 'failed' || step.state === 'cancelled') return false; | ||
| const cancellation = reason ?? userCancellationReason(); | ||
| step.state = 'cancelled'; | ||
| request.abort(); | ||
| if (abortRequest) request.abort(); | ||
| step.controller?.abort(cancellation); | ||
| step.resultControl?.resolve({ type: 'cancelled', reason: cancellation }); | ||
| return true; | ||
|
|
@@ -599,8 +599,21 @@ export class AgentLoopService extends Disposable implements IAgentLoopService { | |
| const job = this.activeTurnJob?.turn === turn ? this.activeTurnJob : undefined; | ||
| if (job === undefined) return; | ||
| const reason = result?.type === 'cancelled' ? result.reason : abortError('Turn ended'); | ||
| const transferred = new Map<string, StepRequest>(); | ||
| for (const request of job.queue.drain()) { | ||
| if (request.state === 'pending' && !request.turnScoped) { | ||
| this.standaloneStepQueue.enqueue(request, 'tail'); | ||
| transferred.set(request.id, request); | ||
|
Comment on lines
+603
to
+606
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
| for (const step of job.steps.values()) { | ||
| if (step.state === 'queued' || step.state === 'running') step.cancel(reason); | ||
| if (step.state !== 'queued' && step.state !== 'running') continue; | ||
| const request = transferred.get(step.id); | ||
| if (request === undefined) { | ||
| step.cancel(reason); | ||
| } else { | ||
| this.cancelStep(job, step, request, reason, false); | ||
| } | ||
| } | ||
| this.activeTurnJob = undefined; | ||
| this.maybeSettle(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When another
newTurnrequest is already inpendingTurns, this moves the unmaterialized steer only tostandaloneStepQueue;pumpTurns()then starts the pre-existing turn, whose queue was populated earlier increatePendingTurn(), without moving these standalone requests into it. If that queued turn is the last one, the Ctrl+S message remains pending indefinitely instead of reaching the next turn, and may be injected into an unrelated later prompt. Transfer these requests into the next pending job when one exists, or merge standalone requests when starting every turn.Useful? React with 👍 / 👎.