Skip to content

Commit c9de90b

Browse files
committed
fix: preserve release variants and refresh dependencies for 3.2.3
1 parent 550c8ec commit c9de90b

26 files changed

Lines changed: 889 additions & 174 deletions

‎.mvn/wrapper/maven-wrapper.properties‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# KIND, either express or implied. See the License for the
1515
# specific language governing permissions and limitations
1616
# under the License.
17-
wrapperVersion=3.3.2
17+
wrapperVersion=3.3.4
1818
distributionType=only-script
19-
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
19+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.10.0/apache-maven-3.10.0-bin.zip
20+
distributionSha256Sum=1f6d9909266510f039f59aa0e13dcd2c66da85f043e56276e41f2918f8bddaff

‎CHANGELOG.md‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
### Changed (Unreleased)
10+
## [3.2.3] - 2026-10-04
1111

12+
### Fixed (3.2.3)
13+
14+
- Preserve recognized release variants such as Guava `jre`/`android` and JDBC `jre11`/`jre17` when comparing versions and recommending POM upgrades. Keep snapshot, release-candidate and unknown qualifiers conservative.
15+
- Resolve OSV fixed boundaries to actual published, compatible Maven versions before verification. Keep vulnerability findings visible if metadata or verification fails; never invent remediation versions.
16+
- Correct the documented default stability filter for `get_latest_version` to `PREFER_STABLE`.
17+
18+
### Changed (3.2.3)
19+
20+
- Update Tomcat to 11.0.26, embedded Maven components and the build distribution to 3.10.0, and the Apache Maven wrapper to 3.3.4 with a verified distribution checksum.
21+
- Update the optional Python agent runtime and development dependencies, including GitHub Copilot SDK 1.0.16. Adapt client configuration, awaited response handling and session disconnection to the current SDK API.
22+
- Normalize SDK session errors for CLI reporting and complete client cleanup even when session disconnection fails. Cleanup exception groups do not discard completed reviews.
23+
- Align project, runtime, registry and build fallback versions at 3.2.3.
1224
- Align architecture, setup, tool schemas, examples, certificate guidance and agent documentation with the released 3.2.2 behavior and known response-field limitations.
13-
- Start the 3.2.3-SNAPSHOT development cycle. Published registry metadata and build fallback versions remain at 3.2.2.
1425

1526
## [3.2.2] - 2026-09-06
1627

@@ -769,7 +780,8 @@ This major release updates tool names and adds stability parameters while mainta
769780
- Unit and integration tests
770781
- Maven Central API integration
771782

772-
[Unreleased]: https://github.com/arvindand/maven-tools-mcp/compare/v3.2.2...HEAD
783+
[Unreleased]: https://github.com/arvindand/maven-tools-mcp/compare/v3.2.3...HEAD
784+
[3.2.3]: https://github.com/arvindand/maven-tools-mcp/compare/v3.2.2...v3.2.3
773785
[3.2.2]: https://github.com/arvindand/maven-tools-mcp/compare/v3.2.1...v3.2.2
774786
[3.2.0]: https://github.com/arvindand/maven-tools-mcp/compare/v3.1.2...v3.2.0
775787
[3.1.2]: https://github.com/arvindand/maven-tools-mcp/compare/v3.1.1...v3.1.2

‎agents/copilot-maven-tools-agent/README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ Apache Maven on the server resolves the effective model. The Python editor uses
2828
pip install .
2929
```
3030

31+
The agent requires Python 3.12 or later and uses Copilot SDK 1.0.16. The SDK
32+
downloads and verifies its matching runtime on first managed use; an existing
33+
runtime can be selected with `COPILOT_CLI_PATH`. Only `--mode major` starts a
34+
Copilot session. Dependency checks in the other modes call MCP directly.
35+
Major-review mode waits for the final assistant reply rather than concatenating
36+
streaming events. Session cleanup disconnects the session and stops its runtime.
37+
3138
### Run
3239

3340
```bash

‎agents/copilot-maven-tools-agent/pyproject.toml‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["setuptools>=61.0", "wheel"]
2+
requires = ["setuptools>=84.0.0", "wheel>=0.48.0"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
@@ -22,21 +22,21 @@ classifiers = [
2222
]
2323

2424
dependencies = [
25-
"aiohttp>=3.13.3",
26-
"pydantic>=2.12.5",
27-
"click>=8.1.8",
28-
"rich>=14.3.2",
29-
"github-copilot-sdk==0.2.0",
25+
"aiohttp>=3.14.3,<4",
26+
"pydantic>=2.13.5,<3",
27+
"click>=8.5.0,<9",
28+
"rich>=15.0.0,<16",
29+
"github-copilot-sdk==1.0.16",
3030
]
3131

3232
[project.optional-dependencies]
3333
dev = [
34-
"pytest>=8.4.2",
35-
"pytest-asyncio>=1.2.0",
36-
"pytest-cov>=7.0.0",
37-
"black>=25.11.0",
38-
"ruff>=0.14.14",
39-
"mypy>=1.19.1",
34+
"pytest>=9.1.1,<10",
35+
"pytest-asyncio>=1.4.0,<2",
36+
"pytest-cov>=7.1.0,<8",
37+
"black>=26.5.1",
38+
"ruff>=0.16.10,<0.17",
39+
"mypy>=2.4.0,<3",
4040
]
4141

4242
[project.scripts]
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# Core dependencies
2-
aiohttp>=3.13.3
3-
pydantic>=2.12.5
4-
click>=8.1.8
5-
rich>=14.3.2
6-
github-copilot-sdk>=0.2.0
2+
aiohttp>=3.14.3,<4
3+
pydantic>=2.13.5,<3
4+
click>=8.5.0,<9
5+
rich>=15.0.0,<16
6+
github-copilot-sdk==1.0.16
77

88
# Development dependencies
9-
pytest>=8.4.2
10-
pytest-asyncio>=1.2.0
11-
pytest-cov>=7.0.0
12-
black>=25.11.0
13-
ruff>=0.14.14
14-
mypy>=1.19.1
9+
pytest>=9.1.1,<10
10+
pytest-asyncio>=1.4.0,<2
11+
pytest-cov>=7.1.0,<8
12+
black>=26.5.1
13+
ruff>=0.16.10,<0.17
14+
mypy>=2.4.0,<3

‎agents/copilot-maven-tools-agent/src/copilot/sdk_client.py‎

Lines changed: 36 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
from __future__ import annotations
1111

12-
import asyncio
1312
import copy
1413
import json
1514
import logging
@@ -43,7 +42,7 @@ class CopilotSDKClient:
4342
"command": "docker",
4443
"args": ["run", "-i", "--rm", "arvindand/maven-tools-mcp:latest"],
4544
"tools": ["*"],
46-
"timeout": 60000
45+
"timeout": 60000,
4746
}
4847
}
4948

@@ -53,7 +52,7 @@ class CopilotSDKClient:
5352
"type": "http",
5453
"url": "http://localhost:8080/mcp",
5554
"tools": ["*"],
56-
"timeout": 60000
55+
"timeout": 60000,
5756
}
5857
}
5958

@@ -86,24 +85,25 @@ def __init__(
8685
async def start(self) -> None:
8786
"""Start the Copilot client."""
8887
try:
89-
from copilot import CopilotClient, PermissionHandler, SubprocessConfig
88+
from copilot import CopilotClient, PermissionHandler
9089
except ImportError:
9190
raise ImportError(
9291
"github-copilot-sdk not installed. Run: pip install github-copilot-sdk"
9392
)
9493

95-
# Build subprocess config for the Copilot CLI (SDK 0.2.0+ dataclass API)
94+
# SDK 1.x configures the managed runtime through keyword arguments.
9695
token = get_github_token()
9796
if not token:
98-
logger.warning("No GitHub token found in COPILOT_GITHUB_TOKEN, GH_TOKEN, or GITHUB_TOKEN")
97+
logger.warning(
98+
"No GitHub token found in COPILOT_GITHUB_TOKEN, GH_TOKEN, or GITHUB_TOKEN"
99+
)
99100

100-
subprocess_config = SubprocessConfig(
101-
cwd=self.working_dir,
101+
self._client = CopilotClient(
102+
working_directory=self.working_dir,
102103
log_level="warning",
103104
github_token=token,
104105
)
105106

106-
self._client = CopilotClient(subprocess_config)
107107
await self._client.start()
108108
logger.info("Copilot SDK client started")
109109

@@ -123,25 +123,29 @@ async def start(self) -> None:
123123
self._session = await self._client.create_session(
124124
on_permission_request=PermissionHandler.approve_all,
125125
model=self.model,
126-
streaming=True,
126+
streaming=False,
127127
mcp_servers=mcp_servers,
128128
)
129129
logger.info(f"Session created with model {self.model}")
130130

131131
async def stop(self) -> None:
132132
"""Stop the Copilot client."""
133-
if self._session:
134-
try:
135-
await self._session.destroy()
136-
except (RuntimeError, OSError) as e:
137-
logger.debug(f"Session destroy error: {e}")
138-
self._session = None
139-
if self._client:
140-
try:
141-
await self._client.stop()
142-
except (RuntimeError, OSError) as e:
143-
logger.debug(f"Client stop error: {e}")
144-
self._client = None
133+
session, self._session = self._session, None
134+
client, self._client = self._client, None
135+
try:
136+
if session:
137+
try:
138+
await session.disconnect()
139+
except Exception as error:
140+
logger.debug("Session disconnect error: %s", error)
141+
finally:
142+
if client:
143+
try:
144+
await client.stop()
145+
except Exception as error:
146+
# SDK1.x may report cleanup failures as ExceptionGroup.
147+
# Cleanup must not replace a completed review or its error.
148+
logger.debug("Client stop error: %s", error)
145149
logger.info("Copilot SDK client stopped")
146150

147151
async def __aenter__(self) -> "CopilotSDKClient":
@@ -167,24 +171,19 @@ async def submit(self, prompt: str, timeout: float = 300.0) -> str:
167171
if not self._session:
168172
raise RuntimeError("Session not started")
169173

170-
result_parts: list[str] = []
171-
done = asyncio.Event()
172-
error_holder: dict[str, Optional[str]] = {"error": None}
173-
174-
handler = _create_event_handler(result_parts, done, error_holder)
175-
self._session.on(handler)
176-
177174
try:
178-
await self._session.send(prompt)
179-
async with asyncio.timeout(timeout):
180-
await done.wait()
181-
except (TimeoutError, asyncio.TimeoutError):
182-
raise RuntimeError(f"Timeout waiting for response after {timeout}s")
175+
response = await self._session.send_and_wait(prompt, timeout=timeout)
176+
except TimeoutError as error:
177+
raise RuntimeError(f"Timeout waiting for response after {timeout}s") from error
178+
except Exception as error:
179+
raise RuntimeError(f"Copilot error: {error}") from error
183180

184-
if error_holder["error"]:
185-
raise RuntimeError(f"Copilot error: {error_holder['error']}")
181+
if response is None:
182+
raise RuntimeError("Copilot session became idle without an assistant response")
186183

187-
return "".join(result_parts)
184+
# send_and_wait returns the final root assistant message, excluding tool
185+
# progress, streaming deltas and responses from child agents.
186+
return response.data.content
188187

189188
async def analyze_dependencies(self, pom_content: str) -> dict[str, Any]:
190189
"""
@@ -263,45 +262,6 @@ async def get_latest_version(self, dependency: str) -> str:
263262
return versions[-1]
264263
return response.strip()
265264

266-
def _get_event_type(event: Any) -> str:
267-
"""Extract event type string from event object."""
268-
return event.type.value if hasattr(event.type, "value") else str(event.type)
269-
270-
271-
def _create_event_handler(
272-
result_parts: list[str],
273-
done: asyncio.Event,
274-
error_holder: dict[str, Optional[str]],
275-
) -> Any:
276-
"""Create an event handler for SDK session events."""
277-
278-
def on_event(event: Any) -> None:
279-
event_type = _get_event_type(event)
280-
281-
if event_type == "assistant.message":
282-
_handle_message_event(event, result_parts)
283-
elif event_type == "assistant.message_delta":
284-
_handle_delta_event(event, result_parts)
285-
elif event_type == "session.idle":
286-
done.set()
287-
elif event_type == "error":
288-
error_holder["error"] = str(event.data) if hasattr(event, "data") else "Unknown error"
289-
done.set()
290-
291-
return on_event
292-
293-
294-
def _handle_message_event(event: Any, result_parts: list[str]) -> None:
295-
"""Handle assistant.message event."""
296-
if hasattr(event.data, "content") and event.data.content:
297-
result_parts.append(event.data.content)
298-
299-
300-
def _handle_delta_event(event: Any, result_parts: list[str]) -> None:
301-
"""Handle assistant.message_delta event."""
302-
if hasattr(event.data, "delta_content") and event.data.delta_content:
303-
result_parts.append(event.data.delta_content)
304-
305265

306266
def _extract_json_object(response: str) -> dict[str, Any]:
307267
"""Extract first complete JSON object from response."""

0 commit comments

Comments
 (0)