kimi-cli 0.39__tar.gz → 0.40__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kimi-cli might be problematic. Click here for more details.
- {kimi_cli-0.39 → kimi_cli-0.40}/PKG-INFO +2 -2
- {kimi_cli-0.39 → kimi_cli-0.40}/README.md +1 -1
- {kimi_cli-0.39 → kimi_cli-0.40}/pyproject.toml +1 -1
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/CHANGELOG.md +11 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/approval.py +1 -3
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/compaction.py +6 -6
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/kimisoul.py +5 -4
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/message.py +5 -5
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/bash/__init__.py +2 -2
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/grep.py +2 -1
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/web/fetch.py +2 -1
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/web/search.py +2 -2
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/__init__.py +4 -2
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/liveview.py +12 -3
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/setup.py +2 -1
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/update.py +2 -1
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/visualize.py +9 -3
- kimi_cli-0.40/src/kimi_cli/utils/aiohttp.py +10 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/utils/provider.py +0 -2
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/agent.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/agents/koder/README.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/agents/koder/agent.yaml +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/agents/koder/sub.yaml +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/agents/koder/system.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/config.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/llm.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/metadata.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/prompts/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/prompts/compact.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/prompts/init.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/py.typed +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/share.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/context.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/denwarenji.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/toolset.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/soul/wire.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/bash/bash.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/dmail/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/dmail/dmail.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/glob.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/glob.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/grep.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/patch.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/patch.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/read.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/read.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/replace.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/replace.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/write.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/file/write.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/mcp.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/task/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/task/task.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/test.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/think/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/think/think.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/todo/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/todo/set_todo_list.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/utils.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/web/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/web/fetch.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/tools/web/search.md +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/acp/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/print/__init__.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/console.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/debug.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/keyboard.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/metacmd.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/ui/shell/prompt.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/utils/changelog.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/utils/logging.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/utils/message.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/utils/path.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/utils/pyinstaller.py +0 -0
- {kimi_cli-0.39 → kimi_cli-0.40}/src/kimi_cli/utils/string.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: kimi-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.40
|
|
4
4
|
Summary: Kimi CLI is your next CLI agent.
|
|
5
5
|
Requires-Dist: agent-client-protocol>=0.4.9
|
|
6
6
|
Requires-Dist: aiofiles>=25.1.0
|
|
@@ -108,7 +108,7 @@ After restarting Zsh, you can switch to agent mode by pressing `Ctrl-K`.
|
|
|
108
108
|
|
|
109
109
|
### ACP support
|
|
110
110
|
|
|
111
|
-
Kimi CLI supports [Agent Client Protocol] out of the box. You can use it together with any ACP-compatible
|
|
111
|
+
Kimi CLI supports [Agent Client Protocol] out of the box. You can use it together with any ACP-compatible editor or IDE.
|
|
112
112
|
|
|
113
113
|
For example, to use Kimi CLI with [Zed](https://zed.dev/), add the following configuration to your `~/.config/zed/settings.json`:
|
|
114
114
|
|
|
@@ -84,7 +84,7 @@ After restarting Zsh, you can switch to agent mode by pressing `Ctrl-K`.
|
|
|
84
84
|
|
|
85
85
|
### ACP support
|
|
86
86
|
|
|
87
|
-
Kimi CLI supports [Agent Client Protocol] out of the box. You can use it together with any ACP-compatible
|
|
87
|
+
Kimi CLI supports [Agent Client Protocol] out of the box. You can use it together with any ACP-compatible editor or IDE.
|
|
88
88
|
|
|
89
89
|
For example, to use Kimi CLI with [Zed](https://zed.dev/), add the following configuration to your `~/.config/zed/settings.json`:
|
|
90
90
|
|
|
@@ -9,6 +9,17 @@ Internal builds may append content to the Unreleased section.
|
|
|
9
9
|
Only write entries that are worth mentioning to users.
|
|
10
10
|
-->
|
|
11
11
|
|
|
12
|
+
## [0.40] - 2025-10-24
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Support `ESC` key to interrupt the agent loop
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Fix SSL certificate verification error in some rare cases
|
|
21
|
+
- Fix possible decoding error in Bash tool
|
|
22
|
+
|
|
12
23
|
## [0.39] - 2025-10-24
|
|
13
24
|
|
|
14
25
|
### Fixed
|
|
@@ -9,7 +9,7 @@ class Approval:
|
|
|
9
9
|
def __init__(self, yolo: bool = False):
|
|
10
10
|
self._request_queue = asyncio.Queue[ApprovalRequest]()
|
|
11
11
|
self._yolo = yolo
|
|
12
|
-
self._auto_approve_actions = set() # TODO: persist across sessions
|
|
12
|
+
self._auto_approve_actions: set[str] = set() # TODO: persist across sessions
|
|
13
13
|
"""Set of action names that should automatically be approved."""
|
|
14
14
|
|
|
15
15
|
def set_yolo(self, yolo: bool) -> None:
|
|
@@ -60,8 +60,6 @@ class Approval:
|
|
|
60
60
|
return True
|
|
61
61
|
case ApprovalResponse.REJECT:
|
|
62
62
|
return False
|
|
63
|
-
case _:
|
|
64
|
-
raise ValueError(f"Unknown approval response: {response}")
|
|
65
63
|
|
|
66
64
|
async def fetch_request(self) -> ApprovalRequest:
|
|
67
65
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from collections.abc import Sequence
|
|
2
2
|
from string import Template
|
|
3
|
-
from typing import Protocol, runtime_checkable
|
|
3
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
4
4
|
|
|
5
5
|
from kosong.base import generate
|
|
6
6
|
from kosong.base.message import ContentPart, Message, TextPart
|
|
@@ -32,7 +32,7 @@ class Compaction(Protocol):
|
|
|
32
32
|
...
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
class SimpleCompaction:
|
|
35
|
+
class SimpleCompaction(Compaction):
|
|
36
36
|
async def compact(self, messages: Sequence[Message], llm: LLM) -> Sequence[Message]:
|
|
37
37
|
history = list(messages)
|
|
38
38
|
if not history:
|
|
@@ -99,7 +99,7 @@ class SimpleCompaction:
|
|
|
99
99
|
return compacted_messages
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
):
|
|
105
|
-
|
|
102
|
+
if TYPE_CHECKING:
|
|
103
|
+
|
|
104
|
+
def type_check(simple: SimpleCompaction):
|
|
105
|
+
_: Compaction = simple
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from collections.abc import Sequence
|
|
3
3
|
from functools import partial
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
4
5
|
|
|
5
6
|
import kosong
|
|
6
7
|
import tenacity
|
|
@@ -302,7 +303,7 @@ class BackToTheFuture(Exception):
|
|
|
302
303
|
self.messages = messages
|
|
303
304
|
|
|
304
305
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
):
|
|
308
|
-
|
|
306
|
+
if TYPE_CHECKING:
|
|
307
|
+
|
|
308
|
+
def type_check(kimi_soul: KimiSoul):
|
|
309
|
+
_: Soul = kimi_soul
|
|
@@ -14,7 +14,7 @@ def tool_result_to_messages(tool_result: ToolResult) -> list[Message]:
|
|
|
14
14
|
message = tool_result.result.message
|
|
15
15
|
if isinstance(tool_result.result, ToolRuntimeError):
|
|
16
16
|
message += "\nThis is an unexpected error and the tool is probably not working."
|
|
17
|
-
content = [system(message)]
|
|
17
|
+
content: list[ContentPart] = [system(message)]
|
|
18
18
|
if tool_result.result.output:
|
|
19
19
|
content.append(TextPart(text=tool_result.result.output))
|
|
20
20
|
return [
|
|
@@ -26,8 +26,8 @@ def tool_result_to_messages(tool_result: ToolResult) -> list[Message]:
|
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
content = tool_ok_to_message_content(tool_result.result)
|
|
29
|
-
text_parts = []
|
|
30
|
-
non_text_parts = []
|
|
29
|
+
text_parts: list[ContentPart] = []
|
|
30
|
+
non_text_parts: list[ContentPart] = []
|
|
31
31
|
for part in content:
|
|
32
32
|
if isinstance(part, TextPart):
|
|
33
33
|
text_parts.append(part)
|
|
@@ -60,7 +60,7 @@ def tool_result_to_messages(tool_result: ToolResult) -> list[Message]:
|
|
|
60
60
|
|
|
61
61
|
def tool_ok_to_message_content(result: ToolOk) -> list[ContentPart]:
|
|
62
62
|
"""Convert a tool return value to a list of message content parts."""
|
|
63
|
-
content = []
|
|
63
|
+
content: list[ContentPart] = []
|
|
64
64
|
if result.message:
|
|
65
65
|
content.append(system(result.message))
|
|
66
66
|
match output := result.output:
|
|
@@ -70,7 +70,7 @@ def tool_ok_to_message_content(result: ToolOk) -> list[ContentPart]:
|
|
|
70
70
|
case ContentPart():
|
|
71
71
|
content.append(output)
|
|
72
72
|
case _:
|
|
73
|
-
content.extend(
|
|
73
|
+
content.extend(output)
|
|
74
74
|
if not content:
|
|
75
75
|
content.append(system("Tool output is empty."))
|
|
76
76
|
return content
|
|
@@ -45,11 +45,11 @@ class Bash(CallableTool2[Params]):
|
|
|
45
45
|
return ToolRejectedError()
|
|
46
46
|
|
|
47
47
|
def stdout_cb(line: bytes):
|
|
48
|
-
line_str = line.decode()
|
|
48
|
+
line_str = line.decode(errors="replace")
|
|
49
49
|
builder.write(line_str)
|
|
50
50
|
|
|
51
51
|
def stderr_cb(line: bytes):
|
|
52
|
-
line_str = line.decode()
|
|
52
|
+
line_str = line.decode(errors="replace")
|
|
53
53
|
builder.write(line_str)
|
|
54
54
|
|
|
55
55
|
try:
|
|
@@ -15,6 +15,7 @@ from pydantic import BaseModel, Field
|
|
|
15
15
|
|
|
16
16
|
import kimi_cli
|
|
17
17
|
from kimi_cli.share import get_share_dir
|
|
18
|
+
from kimi_cli.utils.aiohttp import new_client_session
|
|
18
19
|
from kimi_cli.utils.logging import logger
|
|
19
20
|
|
|
20
21
|
|
|
@@ -167,7 +168,7 @@ async def _download_and_install_rg(bin_name: str) -> Path:
|
|
|
167
168
|
share_bin_dir.mkdir(parents=True, exist_ok=True)
|
|
168
169
|
destination = share_bin_dir / bin_name
|
|
169
170
|
|
|
170
|
-
async with
|
|
171
|
+
async with new_client_session() as session:
|
|
171
172
|
with tempfile.TemporaryDirectory(prefix="kimi-rg-") as tmpdir:
|
|
172
173
|
tar_path = Path(tmpdir) / filename
|
|
173
174
|
|
|
@@ -7,6 +7,7 @@ from kosong.tooling import CallableTool2, ToolReturnType
|
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
9
9
|
from kimi_cli.tools.utils import ToolResultBuilder, load_desc
|
|
10
|
+
from kimi_cli.utils.aiohttp import new_client_session
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class Params(BaseModel):
|
|
@@ -24,7 +25,7 @@ class FetchURL(CallableTool2[Params]):
|
|
|
24
25
|
|
|
25
26
|
try:
|
|
26
27
|
async with (
|
|
27
|
-
|
|
28
|
+
new_client_session() as session,
|
|
28
29
|
session.get(
|
|
29
30
|
params.url,
|
|
30
31
|
headers={
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from typing import override
|
|
3
3
|
|
|
4
|
-
import aiohttp
|
|
5
4
|
from kosong.tooling import CallableTool2, ToolReturnType
|
|
6
5
|
from pydantic import BaseModel, Field, ValidationError
|
|
7
6
|
|
|
@@ -9,6 +8,7 @@ import kimi_cli
|
|
|
9
8
|
from kimi_cli.config import Config
|
|
10
9
|
from kimi_cli.soul.toolset import get_current_tool_call_or_none
|
|
11
10
|
from kimi_cli.tools.utils import ToolResultBuilder, load_desc
|
|
11
|
+
from kimi_cli.utils.aiohttp import new_client_session
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class Params(BaseModel):
|
|
@@ -62,7 +62,7 @@ class SearchWeb(CallableTool2[Params]):
|
|
|
62
62
|
assert tool_call is not None, "Tool call is expected to be set"
|
|
63
63
|
|
|
64
64
|
async with (
|
|
65
|
-
|
|
65
|
+
new_client_session() as session,
|
|
66
66
|
session.post(
|
|
67
67
|
self._base_url,
|
|
68
68
|
headers={
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import signal
|
|
3
3
|
from collections.abc import Awaitable, Coroutine
|
|
4
|
-
from functools import partial
|
|
5
4
|
from typing import Any
|
|
6
5
|
|
|
7
6
|
from kosong.chat_provider import APIStatusError, ChatProviderError
|
|
@@ -160,10 +159,13 @@ class ShellApp:
|
|
|
160
159
|
loop.add_signal_handler(signal.SIGINT, _handler)
|
|
161
160
|
|
|
162
161
|
try:
|
|
162
|
+
# Use lambda to pass cancel_event via closure
|
|
163
163
|
await run_soul(
|
|
164
164
|
self.soul,
|
|
165
165
|
command,
|
|
166
|
-
|
|
166
|
+
lambda wire: visualize(
|
|
167
|
+
wire, initial_status=self.soul.status, cancel_event=cancel_event
|
|
168
|
+
),
|
|
167
169
|
cancel_event,
|
|
168
170
|
)
|
|
169
171
|
return True
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from collections import deque
|
|
2
3
|
|
|
3
4
|
import streamingjson
|
|
@@ -125,7 +126,7 @@ class _ApprovalRequestDisplay:
|
|
|
125
126
|
|
|
126
127
|
|
|
127
128
|
class StepLiveView:
|
|
128
|
-
def __init__(self, status: StatusSnapshot):
|
|
129
|
+
def __init__(self, status: StatusSnapshot, cancel_event: asyncio.Event | None = None):
|
|
129
130
|
# message content
|
|
130
131
|
self._line_buffer = Text("")
|
|
131
132
|
|
|
@@ -144,6 +145,9 @@ class StepLiveView:
|
|
|
144
145
|
)
|
|
145
146
|
self._buffer_status: RenderableType | None = None
|
|
146
147
|
|
|
148
|
+
# cancel event for ESC key handling
|
|
149
|
+
self._cancel_event = cancel_event
|
|
150
|
+
|
|
147
151
|
def __enter__(self):
|
|
148
152
|
self._live = Live(
|
|
149
153
|
self._compose(),
|
|
@@ -243,6 +247,11 @@ class StepLiveView:
|
|
|
243
247
|
self._status_text.plain = self._format_status(status)
|
|
244
248
|
|
|
245
249
|
def handle_keyboard_event(self, event: KeyEvent):
|
|
250
|
+
# Handle ESC key to cancel the run
|
|
251
|
+
if event == KeyEvent.ESCAPE and self._cancel_event is not None:
|
|
252
|
+
self._cancel_event.set()
|
|
253
|
+
return
|
|
254
|
+
|
|
246
255
|
if not self._current_approval:
|
|
247
256
|
# just ignore any keyboard event when there's no approval request
|
|
248
257
|
return
|
|
@@ -298,8 +307,8 @@ class StepLiveView:
|
|
|
298
307
|
class StepLiveViewWithMarkdown(StepLiveView):
|
|
299
308
|
# TODO: figure out a streaming implementation for this
|
|
300
309
|
|
|
301
|
-
def __init__(self, status: StatusSnapshot):
|
|
302
|
-
super().__init__(status)
|
|
310
|
+
def __init__(self, status: StatusSnapshot, cancel_event: asyncio.Event | None = None):
|
|
311
|
+
super().__init__(status, cancel_event)
|
|
303
312
|
self._pending_markdown_parts: list[str] = []
|
|
304
313
|
self._buffer_status_active = False
|
|
305
314
|
self._buffer_status_obj: Status | None = None
|
|
@@ -9,6 +9,7 @@ from pydantic import SecretStr
|
|
|
9
9
|
from kimi_cli.config import LLMModel, LLMProvider, MoonshotSearchConfig, load_config, save_config
|
|
10
10
|
from kimi_cli.ui.shell.console import console
|
|
11
11
|
from kimi_cli.ui.shell.metacmd import meta_command
|
|
12
|
+
from kimi_cli.utils.aiohttp import new_client_session
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
14
15
|
from kimi_cli.ui.shell import ShellApp
|
|
@@ -109,7 +110,7 @@ async def _setup() -> _SetupResult | None:
|
|
|
109
110
|
models_url = f"{platform.base_url}/models"
|
|
110
111
|
try:
|
|
111
112
|
async with (
|
|
112
|
-
|
|
113
|
+
new_client_session() as session,
|
|
113
114
|
session.get(
|
|
114
115
|
models_url,
|
|
115
116
|
headers={
|
|
@@ -13,6 +13,7 @@ import aiohttp
|
|
|
13
13
|
|
|
14
14
|
from kimi_cli.share import get_share_dir
|
|
15
15
|
from kimi_cli.ui.shell.console import console
|
|
16
|
+
from kimi_cli.utils.aiohttp import new_client_session
|
|
16
17
|
from kimi_cli.utils.logging import logger
|
|
17
18
|
|
|
18
19
|
BASE_URL = "https://cdn.kimi.com/binaries/kimi-cli"
|
|
@@ -95,7 +96,7 @@ async def _do_update(*, print: bool, check_only: bool) -> UpdateResult:
|
|
|
95
96
|
_print("[red]Failed to detect target platform.[/red]")
|
|
96
97
|
return UpdateResult.UNSUPPORTED
|
|
97
98
|
|
|
98
|
-
async with
|
|
99
|
+
async with new_client_session() as session:
|
|
99
100
|
logger.info("Checking for updates...")
|
|
100
101
|
_print("Checking for updates...")
|
|
101
102
|
latest_version = await _get_latest_version(session)
|
|
@@ -25,7 +25,6 @@ async def _keyboard_listener(step: StepLiveView):
|
|
|
25
25
|
async def _keyboard():
|
|
26
26
|
try:
|
|
27
27
|
async for event in listen_for_keyboard():
|
|
28
|
-
# TODO: ESCAPE to interrupt
|
|
29
28
|
step.handle_keyboard_event(event)
|
|
30
29
|
except asyncio.CancelledError:
|
|
31
30
|
return
|
|
@@ -39,10 +38,17 @@ async def _keyboard_listener(step: StepLiveView):
|
|
|
39
38
|
await task
|
|
40
39
|
|
|
41
40
|
|
|
42
|
-
async def visualize(
|
|
41
|
+
async def visualize(
|
|
42
|
+
wire: Wire, *, initial_status: StatusSnapshot, cancel_event: asyncio.Event | None = None
|
|
43
|
+
):
|
|
43
44
|
"""
|
|
44
45
|
A loop to consume agent events and visualize the agent behavior.
|
|
45
46
|
This loop never raise any exception except asyncio.CancelledError.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
wire: Communication channel with the agent
|
|
50
|
+
initial_status: Initial status snapshot
|
|
51
|
+
cancel_event: Event that can be set (e.g., by ESC key) to cancel the run
|
|
46
52
|
"""
|
|
47
53
|
latest_status = initial_status
|
|
48
54
|
try:
|
|
@@ -52,7 +58,7 @@ async def visualize(wire: Wire, *, initial_status: StatusSnapshot):
|
|
|
52
58
|
while True:
|
|
53
59
|
# TODO: Maybe we can always have a StepLiveView here.
|
|
54
60
|
# No need to recreate for each step.
|
|
55
|
-
with StepLiveViewWithMarkdown(latest_status) as step:
|
|
61
|
+
with StepLiveViewWithMarkdown(latest_status, cancel_event) as step:
|
|
56
62
|
async with _keyboard_listener(step):
|
|
57
63
|
# spin the moon at the beginning of each step
|
|
58
64
|
with console.status("", spinner="moon"):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|