Skip to content

fix(approval): scope pending requests to turn lifecycle#2087

Merged
RealKai42 merged 6 commits into
mainfrom
approval-lifecycle-cleanup
Apr 28, 2026
Merged

fix(approval): scope pending requests to turn lifecycle#2087
RealKai42 merged 6 commits into
mainfrom
approval-lifecycle-cleanup

Conversation

@wbxl2000

@wbxl2000 wbxl2000 commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Make approval waits indefinite by default so active foreground/subagent
    approvals are not auto-rejected after 5 minutes.
  • Cancel foreground-turn approvals through the existing
    ApprovalSource/cancel_by_source lifecycle when the owning KimiSoul run
    exits.
  • Cancel abandoned KimiCLI.run streams with an internal run cancel event,
    without mutating the caller-owned cancel_event.
  • Keep explicit timeout behavior covered for future policy/config use.

Closes #1823. Supersedes #1837 — thanks @bloodycoder for the prototype
and problem write-up that pushed us to ask what the 300s timer was
actually defending against.

Context

This separates lifecycle correctness from timeout policy. The old 300s
fallback prevented some hangs, but it also caused active approvals to be
rejected as if the user rejected them. This PR uses source-scoped
lifecycle cleanup as the primary completion path instead.

The cancel_by_source primitive used here has existed since #1552 but
was never wired into the foreground-turn lifecycle; PR #1724 later added
a 300s wait_for_response timeout as a defense against orphaned
approval futures (the parallel-subagent sink-switching bug it was
actually fixing). This PR completes the wiring and removes the
now-redundant timer.

Behavior change

Before After
Foreground approval idle for 5 min Auto-rejected (Rejected: approval timed out since 1.38, Rejected by user on ≤1.37) Waits until user responds, turn ends, or stream is abandoned
Turn cancelled mid-approval Pending request leaked until 300s timer Cleaned up immediately by cancel_by_source in KimiSoul.run finally
Web / ACP client disconnects mid-approval Pending request leaked until 300s timer Cleaned up immediately by KimiCLI.run abandonment detection
Awaiting task cancelled directly Waiter Future left orphaned in _waiters Removed by wait_for_response's except CancelledError
Explicit wait_for_response(timeout=N) Same Unchanged
background_agent approvals Same lifecycle Unchanged — still cleaned by agent_runner finally only

Tests

  • tests/core/test_approval_runtime.py — default-no-timeout (parametrized
    for both source kinds), explicit-timeout still works, turn-cancel
    cleanup asserts foreground-only (not background) cancellation
  • tests/core/test_notifications.pyKimiCLI.run abandonment cleanup,
    external cancel propagation
  • tests/acp/test_session_notifications.py — ACP transport path
    independent regression

Run:

  • uv run pytest tests/core/test_approval_runtime.py tests/core/test_notifications.py tests/acp/test_session_notifications.py
  • make check-kimi-cli

Open in Devin Review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts approval-request lifecycle handling so approvals don’t get auto-rejected after a fixed timeout, and instead are cancelled deterministically when their owning run/stream ends.

Changes:

  • Make ApprovalRuntime.wait_for_response() wait indefinitely by default (timeout now opt-in).
  • Cancel foreground-turn approvals via cancel_by_source when a KimiSoul turn finishes/aborts, and cancel abandoned KimiCLI.run() streams using an internal cancel event.
  • Add/adjust tests to cover indefinite waiting, explicit timeout behavior, abandoned-stream cleanup, and external cancellation propagation.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/kimi_cli/approval_runtime/runtime.py Changes approval wait default from finite timeout to indefinite (opt-in timeout remains).
src/kimi_cli/soul/kimisoul.py Introduces per-turn foreground ApprovalSource tracking and cancels its pending approvals on exit.
src/kimi_cli/app.py Adds internal run-cancel event to cleanly cancel abandoned KimiCLI.run() streams without mutating caller cancel state; propagates external cancellation.
tests/core/test_approval_runtime.py Adds coverage for “indefinite by default”, explicit timeout path, and run-cancel lifecycle scoping.
tests/core/test_notifications.py Adds coverage that abandoning a stream cancels pending approvals; verifies external cancel still raises RunCancelled.
CHANGELOG.md Documents the user-visible change to approval timeout behavior.
docs/en/release-notes/changelog.md Syncs release notes entry for the approval timeout behavior change.
docs/zh/release-notes/changelog.md Adds corresponding Chinese release note entry for the approval timeout behavior change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

devin-ai-integration[bot]

This comment was marked as resolved.

…eanup

# Conflicts:
#	CHANGELOG.md
#	docs/en/release-notes/changelog.md
#	docs/zh/release-notes/changelog.md

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 520223d473

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread CHANGELOG.md Outdated
Comment on lines +14 to +18
<<<<<<< HEAD
- Core: Approval requests no longer auto-timeout after 5 minutes, which previously surfaced as `Rejected by user`; active foreground and subagent approvals now wait indefinitely for user response
=======
- Core: Fix yolo mode reminder being lost after context compaction — the non-interactive-mode guidance ("don't call AskUserQuestion, plan-mode toggles are auto-approved") is now re-injected on the first LLM step after each compaction while yolo remains active, instead of being silently dropped when the original reminder is folded into the compaction summary
>>>>>>> origin/main

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve leftover merge conflict markers in changelog

The Unreleased section still contains Git conflict markers (<<<<<<<, =======, >>>>>>>) instead of a resolved entry, so this commit publishes raw merge syntax and two competing bullets verbatim. That is user-visible in release notes and can break tooling that parses CHANGELOG.md as clean markdown; the same unresolved block is also mirrored into docs/en/release-notes/changelog.md and docs/zh/release-notes/changelog.md.

Useful? React with 👍 / 👎.

devin-ai-integration[bot]

This comment was marked as resolved.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0aa249d7cd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +104 to +106
except asyncio.CancelledError:
if self._waiters.get(request_id) is waiter:
self._waiters.pop(request_id, None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep shared waiter registered when one awaiter is cancelled

ApprovalRuntime.wait_for_response reuses a single future per request ID, but this new CancelledError handler removes that shared future from _waiters whenever any waiting task is cancelled. If two coroutines are waiting on the same request and one is cancelled (for example, one caller aborts while another still awaits), resolve()/cancel_by_source() can no longer find the waiter to complete, leaving the remaining waiter hung indefinitely despite the request being resolved.

Useful? React with 👍 / 👎.

The outer ``assert runtime.approval_runtime is not None`` does not
propagate into nested ``async def`` bodies, so pyright flagged the
``runtime.approval_runtime.create_request(...)`` / ``get_request(...)``
calls inside the new abandoned-stream / external-cancel / ACP-cancel
tests. Re-assert at the top of each closure so the type narrows there.
@wbxl2000

Copy link
Copy Markdown
Collaborator Author

@codex

@wbxl2000

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NOT ACTUALLY CLOSED: Feature Request: Configurable Approval Request Timeout (or Unlimited)

3 participants