fix: fire notification hooks for approvals#2284
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a486b111a
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| self._approval_hook_subscription = self._runtime.approval_runtime.subscribe( | ||
| self._on_approval_runtime_event | ||
| ) |
There was a problem hiding this comment.
Unsubscribe approval-event listener when creating souls
Avoid registering a permanent approval-runtime subscriber in KimiSoul.__init__ without removing it later. ApprovalRuntime.subscribe() stores the bound callback strongly, and this class currently has no corresponding unsubscribe(), so temporary root souls (for example /init, which creates KimiSoul(soul.agent, ...) in src/kimi_cli/soul/slash.py) accumulate listeners. After each /init, every new approval request will fire the Notification hook multiple times and keep old soul instances alive in memory.
Useful? React with 👍 / 👎.
| self._approval_hook_subscription: str | None = None | ||
| if self.is_root: | ||
| self._runtime.notifications.ack_ids("llm", extract_notification_ids(context.history)) | ||
| if self._runtime.approval_runtime is not None: | ||
| self._approval_hook_subscription = self._runtime.approval_runtime.subscribe( | ||
| self._on_approval_runtime_event | ||
| ) |
There was a problem hiding this comment.
🟡 Approval runtime subscription is never unsubscribed, causing resource leak
The subscription token is stored in _approval_hook_subscription but unsubscribe() is never called anywhere in KimiSoul's lifecycle. The class has no cleanup/close method. This leaks the subscription callback (a bound method holding a strong reference to self) for the lifetime of the ApprovalRuntime.
The concrete impact is in src/kimi_cli/soul/slash.py:44 where the /init command creates a temporary KimiSoul sharing the same root runtime. That temporary soul subscribes at line 217, but when it goes out of scope, the subscription keeps it alive. Every subsequent create_request() call invokes the leaked callback, which calls fire_and_forget_trigger on a stale, empty HookEngine. The established pattern in this codebase (see src/kimi_cli/background/agent_runner.py:128) is to always pair subscribe() with unsubscribe() in a cleanup block.
Prompt for agents
The _approval_hook_subscription token is stored but never used to unsubscribe from the ApprovalRuntime. KimiSoul has no cleanup/close method.
The most straightforward fix is to add a cleanup method (e.g. close() or dispose()) to KimiSoul that calls self._runtime.approval_runtime.unsubscribe(self._approval_hook_subscription) when the token is not None. Then ensure this cleanup is called wherever KimiSoul instances are discarded — notably in src/kimi_cli/soul/slash.py:44-45 (the /init command creates a temporary KimiSoul that goes out of scope after the with block).
Alternatively, the /init path could be fixed by having the temporary soul not subscribe (e.g. by passing a flag), or by wrapping the temporary soul usage in a try/finally that calls unsubscribe.
See src/kimi_cli/background/agent_runner.py:128 for the established pattern of pairing subscribe() with unsubscribe() in a finally block.
Was this helpful? React with 👍 or 👎 to provide feedback.
5a486b1 to
f4d4a72
Compare
|
Addressed the subscription cleanup review in Changes:
Validated locally:
|
Summary
Notificationhooks when an approval request is createdpermission_promptas the matcher value for approval-needed notificationsFixes #2281.
To verify
uv run python -m pytest tests\core\test_approval_runtime.py tests\hooks\test_integration.py::test_notification_hook -q --basetemp .tmp\pytest -p no:cacheprovideruv run ruff check src\kimi_cli\soul\kimisoul.py tests\core\test_approval_runtime.pygit diff --check