Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Only write entries that are worth mentioning to users.

## Unreleased

- Shell: Fix `Rejected by user` misleading message when an approval modal times out — after the 300s safety timeout, the tool call now rejects with `Rejected: approval timed out`, so users returning to their session after stepping away can tell the rejection was a timeout rather than a manual rejection. Pass `--yolo`/`-y` to auto-approve tool calls if you regularly leave sessions unattended
- Kosong: Fix parallel tool results being split into multiple user messages in Anthropic provider — consecutive tool-result-only user messages are now merged into a single message, complying with the Anthropic Messages API spec that all `tool_use` blocks in an assistant turn must be answered within one user message; this fixes 400 errors on strict Anthropic-compatible backends (e.g. DeepSeek `/anthropic` endpoint) and prevents the official backend from silently teaching the model to avoid parallel tool calls

## 1.37.0 (2026-04-20)
Expand Down
1 change: 1 addition & 0 deletions docs/en/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This page documents the changes in each Kimi Code CLI release.

## Unreleased

- Shell: Fix `Rejected by user` misleading message when an approval modal times out — after the 300s safety timeout, the tool call now rejects with `Rejected: approval timed out`, so users returning to their session after stepping away can tell the rejection was a timeout rather than a manual rejection. Pass `--yolo`/`-y` to auto-approve tool calls if you regularly leave sessions unattended
- Kosong: Fix parallel tool results being split into multiple user messages in Anthropic provider — consecutive tool-result-only user messages are now merged into a single message, complying with the Anthropic Messages API spec that all `tool_use` blocks in an assistant turn must be answered within one user message; this fixes 400 errors on strict Anthropic-compatible backends (e.g. DeepSeek `/anthropic` endpoint) and prevents the official backend from silently teaching the model to avoid parallel tool calls

## 1.37.0 (2026-04-20)
Expand Down
1 change: 1 addition & 0 deletions docs/zh/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## 未发布

- Shell:修复 approval 弹窗超时后被误报为 `Rejected by user` 的问题——300 秒安全超时后,工具调用会以 `Rejected: approval timed out` 拒绝,让离开电脑一段时间后回来的用户能分辨出这是超时而非自己的手动拒绝。经常长时间离开的话可以加 `--yolo`/`-y` 自动批准工具调用
- Kosong:修复 Anthropic 供应商将并行工具结果拆分到多个 user message 的问题——现在会将仅包含工具结果的连续 user message 合并为单条消息,以符合 Anthropic Messages API 规范(assistant 一轮中的所有 `tool_use` 必须在同一条 user message 内回答);修复了严格兼容后端(如 DeepSeek `/anthropic` 接口)返回 400 错误的问题,并避免官方后端静默地引导模型放弃并行工具调用

## 1.37.0 (2026-04-20)
Expand Down
3 changes: 2 additions & 1 deletion src/kimi_cli/soul/approval.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ async def request(
tool_name=tool_call.function.name,
approval_mode="cancelled",
)
return ApprovalResult(approved=False)
record = self._runtime.get_request(request_id)
return ApprovalResult(approved=False, feedback=record.feedback if record else "")
from kimi_cli.telemetry import track

match response:
Expand Down
46 changes: 46 additions & 0 deletions tests/core/test_approval_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,49 @@ async def test_approval_runtime_wait_for_response_times_out() -> None:
record = runtime.get_request(request.id)
assert record is not None
assert record.status == "cancelled"
assert record.feedback == "approval timed out"


@pytest.mark.asyncio
async def test_approval_request_timeout_carries_feedback_to_result() -> None:
"""Timeout feedback must survive round-trip through ``Approval.request``.

Regression test: when the 300s ``wait_for_response`` safety timeout fires
(e.g. the user stepped away from their session), ``_cancel_request`` sets
``record.feedback = "approval timed out"`` before raising
``ApprovalCancelledError``. ``Approval.request`` must read that feedback
back into the returned ``ApprovalResult`` — otherwise the resulting
``ToolRejectedError`` falls back to the generic "Rejected by user" brief,
hiding the timeout cause from the user.
"""
from kimi_cli.soul.approval import Approval, ApprovalState
from kimi_cli.soul.toolset import current_tool_call
from kimi_cli.wire.types import ToolCall

runtime = ApprovalRuntime()
approval = Approval(state=ApprovalState(), runtime=runtime)

token = current_tool_call.set(
ToolCall(id="test", function=ToolCall.FunctionBody(name="Shell", arguments=None))
)
try:
request_task = asyncio.create_task(
approval.request(sender="Shell", action="shell_exec", description="ls")
)
while not runtime.list_pending():
await asyncio.sleep(0)
pending = runtime.list_pending()[0]
# Drive the timeout path directly instead of waiting 300s: this is
# the same internal call ``wait_for_response`` makes when its own
# timeout expires (runtime.py uses ``feedback="approval timed out"``).
runtime._cancel_request(pending.id, feedback="approval timed out")
result = await request_task
Comment thread
wbxl2000 marked this conversation as resolved.
finally:
current_tool_call.reset(token)

assert result.approved is False
assert result.feedback == "approval timed out"
# The user-visible rejection surface reflects the real reason rather
# than the generic "Rejected by user" fallback.
err = result.rejection_error()
assert err.brief == "Rejected: approval timed out"
Loading