fix(hooks): extract text from ContentPart for UserPromptSubmit hook - #2176
fix(hooks): extract text from ContentPart for UserPromptSubmit hook#2176tears-mysthrala wants to merge 6 commits into
Conversation
The UserPromptSubmit hook received an empty prompt when user_input was a list[ContentPart] (the default for all messages). The code checked and fell back to , so the hook's matcher_value and prompt were always empty for non-string inputs. Use Message.extract_text() instead, which handles both str and list[ContentPart] uniformly — matching the pattern already used later in the same function for text_input. Fixes MoonshotAI#2148
There was a problem hiding this comment.
Pull request overview
Fixes the UserPromptSubmit hook payload so it receives the actual user prompt text when user_input is the default list[ContentPart] form, restoring regex matching behavior for hooks (issue #2148).
Changes:
- Use
Message.extract_text()to buildmatcher_value/promptforUserPromptSubmithooks from bothstrandlist[ContentPart]inputs. - Add async tests covering both string and
ContentPart-list user inputs for the hook payload. - Add changelog entries describing the fix.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/kimi_cli/soul/kimisoul.py |
Fix hook prompt extraction by using Message.extract_text() instead of falling back to "" for non-strings. |
tests/core/test_user_prompt_submit_hook.py |
Adds regression tests ensuring the hook receives extracted text for both input shapes. |
CHANGELOG.md |
Notes the core hook fix in the Unreleased section. |
docs/en/release-notes/changelog.md |
Mirrors the Unreleased changelog entry in the English docs. |
docs/zh/release-notes/changelog.md |
Adds the corresponding Unreleased changelog entry in Chinese. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| user_message = Message(role="user", content=user_input) | ||
| text_input_for_hook = user_message.extract_text(" ").strip() |
Signed-off-by: Tears Mysthrala <unaiup@gmail.com>
Signed-off-by: Tears Mysthrala <unaiup@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1902ff09a
ℹ️ 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".
| soul = _make_runnable_soul() | ||
|
|
||
| with patch("kimi_cli.soul.kimisoul.wire_send"): | ||
| await soul.run("hello world") |
There was a problem hiding this comment.
Initialize
_plan_mode before calling KimiSoul.run
The new tests build KimiSoul via object.__new__ but never initialize _plan_mode, and run() unconditionally reads that field when emitting telemetry ("plan" if self._plan_mode else "agent"). As written, this call path raises AttributeError before the hook assertions execute, so both tests fail when run. Set _plan_mode (and any other run()-required fields) in _make_runnable_soul to keep the tests runnable.
Useful? React with 👍 / 👎.
…submit-hook-empty-prompt # Conflicts: # CHANGELOG.md # docs/en/release-notes/changelog.md # docs/zh/release-notes/changelog.md
… factory KimiSoul.run() reads self._plan_mode unconditionally for telemetry, so the object.__new__-based test double raised AttributeError before the hook assertions. Addresses Codex review on PR MoonshotAI#2176.
The UserPromptSubmit hook block and the normal processing path both built Message(role="user", content=user_input) and called extract_text(); compute them once before the hook and reuse, keeping semantics identical. Addresses Copilot review on PR MoonshotAI#2176.
|
Status update: merged the latest upstream |
Related Issue
Resolve #2148
Description
The
UserPromptSubmithook received an emptypromptandmatcher_valuewheneveruser_inputwas alist[ContentPart](the default for all messages). The code only handled thestrcase and fell back to""for everything else, making regex matching in hooks ineffective and blocking hooks unreliable.This fix uses
Message.extract_text()— the same pattern already used later inrun()fortext_input— so the hook receives the concatenated text content regardless of input format.Checklist
make gen-changelogto update the changelog.