diff --git a/src/kimi_cli/acp/kaos.py b/src/kimi_cli/acp/kaos.py index d6e1b3496..e96b89291 100644 --- a/src/kimi_cli/acp/kaos.py +++ b/src/kimi_cli/acp/kaos.py @@ -10,6 +10,8 @@ from kaos.local import local_kaos from kaos.path import KaosPath +from kimi_cli.acp.terminal import TerminalHandle + _DEFAULT_TERMINAL_OUTPUT_LIMIT = 50_000 _DEFAULT_POLL_INTERVAL = 0.2 _TRUNCATION_NOTICE = "[acp output truncated]\n" @@ -46,7 +48,7 @@ class ACPProcess: def __init__( self, - terminal: acp.TerminalHandle, + terminal: TerminalHandle, *, poll_interval: float = _DEFAULT_POLL_INTERVAL, ) -> None: diff --git a/src/kimi_cli/acp/terminal.py b/src/kimi_cli/acp/terminal.py new file mode 100644 index 000000000..a714d8535 --- /dev/null +++ b/src/kimi_cli/acp/terminal.py @@ -0,0 +1,59 @@ +"""Adapter for ACP terminal operations. + +The ``acp.TerminalHandle`` convenience class was removed in +agent-client-protocol >= 0.8.0. This module provides a lightweight +replacement that wraps the raw ``acp.Client`` terminal methods behind +the same interface that ``tools.py`` and ``kaos.py`` expect. +""" + +from __future__ import annotations + +from dataclasses import dataclass + +import acp +import acp.schema + + +@dataclass(slots=True) +class TerminalHandle: + """Thin wrapper exposing terminal operations on an ``acp.Client``.""" + + id: str + _client: acp.Client + _session_id: str + + async def wait_for_exit(self) -> acp.schema.WaitForTerminalExitResponse: + return await self._client.wait_for_terminal_exit( + session_id=self._session_id, terminal_id=self.id + ) + + async def current_output(self) -> acp.schema.TerminalOutputResponse: + return await self._client.terminal_output( + session_id=self._session_id, terminal_id=self.id + ) + + async def kill(self) -> None: + await self._client.kill_terminal( + session_id=self._session_id, terminal_id=self.id + ) + + async def release(self) -> None: + await self._client.release_terminal( + session_id=self._session_id, terminal_id=self.id + ) + + +async def create_terminal( + client: acp.Client, + *, + command: str, + session_id: str, + output_byte_limit: int | None = None, +) -> TerminalHandle: + """Create a terminal and return a :class:`TerminalHandle`.""" + resp = await client.create_terminal( + command=command, + session_id=session_id, + output_byte_limit=output_byte_limit, + ) + return TerminalHandle(id=resp.terminal_id, _client=client, _session_id=session_id) diff --git a/src/kimi_cli/acp/tools.py b/src/kimi_cli/acp/tools.py index 8833bb6ae..f356eb679 100644 --- a/src/kimi_cli/acp/tools.py +++ b/src/kimi_cli/acp/tools.py @@ -6,6 +6,7 @@ from kaos.local import local_kaos from kosong.tooling import CallableTool2, ToolReturnValue +from kimi_cli.acp.terminal import TerminalHandle, create_terminal from kimi_cli.soul.agent import Runtime from kimi_cli.soul.approval import Approval from kimi_cli.soul.toolset import KimiToolset @@ -80,23 +81,19 @@ async def __call__(self, params: ShellParams) -> ToolReturnValue: timeout_seconds = float(params.timeout) timeout_label = f"{timeout_seconds:g}s" - terminal: acp.TerminalHandle | None = None + terminal: TerminalHandle | None = None exit_status: ( acp.schema.WaitForTerminalExitResponse | acp.schema.TerminalExitStatus | None ) = None timed_out = False try: - term = await self._acp_conn.create_terminal( + terminal = await create_terminal( + self._acp_conn, command=params.command, session_id=self._acp_session_id, output_byte_limit=builder.max_chars, ) - # FIXME: update ACP sdk for the fix - assert isinstance(term, acp.TerminalHandle), ( - "Expected TerminalHandle from create_terminal" - ) - terminal = term acp_tool_call_id = get_current_acp_tool_call_id_or_none() assert acp_tool_call_id, "Expected to have an ACP tool call ID in context"