Skip to content

Commit 176b63e

Browse files
committed
feat(protocol): negotiate experimental protocol versions
1 parent ee2f950 commit 176b63e

5 files changed

Lines changed: 605 additions & 7 deletions

File tree

src/acp/agent/connection.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pydantic import TypeAdapter
88

99
from .._transport import Transport
10-
from ..connection import Connection
10+
from ..connection import Connection, MethodHandler
1111
from ..interfaces import Agent, Client
1212
from ..meta import CLIENT_METHODS
1313
from ..schema import (
@@ -88,8 +88,7 @@ def __init__(
8888
use_unstable_protocol: bool = False,
8989
**connection_kwargs: Any,
9090
) -> None:
91-
agent = to_agent(self) if callable(to_agent) else to_agent
92-
handler = build_agent_router(cast(Agent, agent), use_unstable_protocol=use_unstable_protocol)
91+
agent, handler = self._prepare(to_agent, use_unstable_protocol=use_unstable_protocol)
9392
if isinstance(input_stream, Transport):
9493
if output_stream is not None:
9594
raise TypeError(_AGENT_CONNECTION_ERROR)
@@ -100,6 +99,32 @@ def __init__(
10099
):
101100
raise TypeError(_AGENT_CONNECTION_ERROR)
102101
self._conn = Connection(handler, input_stream, output_stream, listening=listening, **connection_kwargs)
102+
self._notify_connected(agent)
103+
104+
@classmethod
105+
def _attach(
106+
cls,
107+
to_agent: Callable[[Client], Agent] | Agent,
108+
connection: Connection,
109+
*,
110+
use_unstable_protocol: bool = False,
111+
) -> tuple[AgentSideConnection, MethodHandler]:
112+
self = cls.__new__(cls)
113+
agent, handler = self._prepare(to_agent, use_unstable_protocol=use_unstable_protocol)
114+
self._conn = connection
115+
self._notify_connected(agent)
116+
return self, handler
117+
118+
def _prepare(
119+
self,
120+
to_agent: Callable[[Client], Agent] | Agent,
121+
*,
122+
use_unstable_protocol: bool,
123+
) -> tuple[Agent, MethodHandler]:
124+
agent = cast(Agent, to_agent(self) if callable(to_agent) else to_agent)
125+
return agent, build_agent_router(agent, use_unstable_protocol=use_unstable_protocol)
126+
127+
def _notify_connected(self, agent: Agent) -> None:
103128
if on_connect := getattr(agent, "on_connect", None):
104129
on_connect(self)
105130

src/acp/client/connection.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from typing import Any, cast, final
77

88
from .._transport import Transport
9-
from ..connection import Connection
9+
from ..connection import Connection, MethodHandler
1010
from ..exceptions import RequestError
1111
from ..interfaces import Agent, Client
1212
from ..meta import AGENT_METHODS, CLIENT_METHODS
@@ -122,9 +122,7 @@ def __init__(
122122
use_unstable_protocol: bool = False,
123123
**connection_kwargs: Any,
124124
) -> None:
125-
client = to_client(self) if callable(to_client) else to_client
126-
self._session_updates = _SessionUpdateTracker(cast(Client, client))
127-
handler = build_client_router(cast(Client, self._session_updates), use_unstable_protocol=use_unstable_protocol)
125+
client, handler = self._prepare(to_client, use_unstable_protocol=use_unstable_protocol)
128126

129127
if isinstance(input_stream, Transport):
130128
if output_stream is not None:
@@ -136,6 +134,34 @@ def __init__(
136134
):
137135
raise TypeError(_CLIENT_CONNECTION_ERROR)
138136
self._conn = Connection(handler, input_stream, output_stream, **connection_kwargs)
137+
self._notify_connected(client)
138+
139+
@classmethod
140+
def _attach(
141+
cls,
142+
to_client: Callable[[Agent], Client] | Client,
143+
connection: Connection,
144+
*,
145+
use_unstable_protocol: bool = False,
146+
) -> tuple[ClientSideConnection, MethodHandler]:
147+
self = cls.__new__(cls)
148+
client, handler = self._prepare(to_client, use_unstable_protocol=use_unstable_protocol)
149+
self._conn = connection
150+
self._notify_connected(client)
151+
return self, handler
152+
153+
def _prepare(
154+
self,
155+
to_client: Callable[[Agent], Client] | Client,
156+
*,
157+
use_unstable_protocol: bool,
158+
) -> tuple[Client, MethodHandler]:
159+
client = cast(Client, to_client(self) if callable(to_client) else to_client)
160+
self._session_updates = _SessionUpdateTracker(client)
161+
handler = build_client_router(cast(Client, self._session_updates), use_unstable_protocol=use_unstable_protocol)
162+
return client, handler
163+
164+
def _notify_connected(self, client: Client) -> None:
139165
if on_connect := getattr(client, "on_connect", None):
140166
on_connect(self)
141167

src/acp/experimental/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,27 @@
11
"""Experimental ACP APIs."""
2+
3+
from . import v2
4+
from .negotiation import (
5+
AgentProtocolConnection,
6+
AgentProtocolRouter,
7+
ClientNegotiator,
8+
NegotiatedClient,
9+
NegotiatedV1,
10+
NegotiatedV2,
11+
UnsupportedProtocolVersionError,
12+
V1ClientConfig,
13+
V2ClientConfig,
14+
)
15+
16+
__all__ = [
17+
"AgentProtocolConnection",
18+
"AgentProtocolRouter",
19+
"ClientNegotiator",
20+
"NegotiatedClient",
21+
"NegotiatedV1",
22+
"NegotiatedV2",
23+
"UnsupportedProtocolVersionError",
24+
"V1ClientConfig",
25+
"V2ClientConfig",
26+
"v2",
27+
]

0 commit comments

Comments
 (0)