MoonshotAI / MoonshotAI/kimi-cli
feat(hook): include LLM response and stop reason in Stop hook payload
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 11.4k
- Forks
- 1.3k
- Avg merge
- 9h 47m
- Merged PRs (30d)
- 2
Description
What feature would you like to see?
Summary
Currently, the Stop hook fires at the end of every agent turn but its payload is minimal:
{
"hook_event_name": "Stop",
"session_id": "abc123",
"cwd": "/path/to/project",
"stop_hook_active": false
}
There is no way for a hook to inspect what the agent actually said or why the turn ended. This limits use cases such as:
- Post-turn logging / auditing — recording the agent's final response to an external system
- Conditional approval — blocking the session if the agent's response contains certain patterns (e.g., "I am not sure", "I cannot help with that")
- Metrics / analytics — tracking turn outcomes (natural language vs. tool calls) without parsing wire logs
SubagentStop already provides a response field (the subagent's final summary). It would be consistent and useful for the root-agent Stop hook to expose the same class of information.
Proposed behavior
Enhance the Stop hook payload with two new fields:
| Field | Type | Description |
|---|---|---|
response |
string |
The agent's natural-language response text from the final assistant message. Empty if the turn ended due to tool rejection or produced no text. |
stop_reason |
string |
Why the turn stopped. "no_tool_calls" when the agent spoke naturally, "tool_rejected" when a tool call was rejected. |
Example payload after the change:
{
"hook_event_name": "Stop",
"session_id": "abc123",
"cwd": "/path/to/project",
"stop_hook_active": false,
"stop_reason": "no_tool_calls",
"response": "The fix is complete. I updated src/main.py and added a regression test."
}
Additional information
- Precedent:
SubagentStopalready includes"response"(truncated to 500 chars). This change brings parity to the root-agent lifecycle. - Backwards compatibility: The new fields are optional and default to empty strings. Existing hooks that do not read them will continue to work unchanged.
- Implementation sketch:
KimiSoul.run()already receives aTurnOutcomefrom_turn(). Thefinal_message(aMessageobject) can have its text extracted viaextract_text(" ").strip(), identical to howUserPromptSubmitextracts user text.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in KimiSoul.run(), following the TurnOutcome returned by _turn() and the existing Stop-hook payload construction; inspect final_message and the extract_text(" ").strip() precedent. Done means Stop payloads include response and stop_reason with the specified values and empty-string defaults, while existing fields remain unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cli
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100