[BREAKING] Python: Update github-copilot-sdk integration to use ToolInvocation/ToolResult types (#4551)

* Update github_copilot package for github-copilot-sdk>=0.1.32 (#4549)

- Update requires-python from >=3.10 to >=3.11
- Remove Python 3.10 classifier
- Update mypy python_version to 3.11
- Update dependency to github-copilot-sdk>=0.1.32
- Fix ToolResult API: use snake_case kwargs (text_result_for_llm,
  result_type) instead of camelCase (textResultForLlm, resultType)
- Update test assertions to use attribute access on ToolResult
- Add ToolResult type assertions to tool handler tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix tests to use ToolInvocation dataclass instead of plain dict (#4549)

Update test_github_copilot_agent.py to pass ToolInvocation objects to tool
handlers instead of plain dicts, matching the github-copilot-sdk>=0.1.32 API
where ToolInvocation is a dataclass with an .arguments attribute.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add regression tests for ToolInvocation contract (#4549)

Add tests to lock in the new ToolInvocation-based calling convention:
- test_tool_handler_rejects_raw_dict_invocation: verifies passing a raw
  dict (old calling convention) raises TypeError/AttributeError
- test_tool_handler_with_empty_arguments: verifies ToolInvocation with
  empty arguments works correctly for no-arg tools

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert requires-python to >=3.10 to avoid breaking CI (#4549)

The repo CI runs with Python 3.10 (uv sync --all-packages) and all other
packages require >=3.10. Raising this package to >=3.11 would break the
shared install flow. The SDK dependency version constraint (>=0.1.32) will
enforce any Python version requirement from the SDK itself.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix min Python version for github_copilot package to >=3.11

github-copilot-sdk>=0.1.32 requires Python>=3.11, which conflicts
with the package's declared >=3.10 minimum, breaking uv sync.

* Bump py version for GH workflows to 3.11, exclude GHCP sdk from 3.10 items

* Fix uv command

* Fixes

* Update samples

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Evan Mattson
2026-03-09 18:57:51 +09:00
committed by GitHub
Unverified
parent 1ca43f9643
commit d5e240b375
17 changed files with 169 additions and 984 deletions
@@ -15,16 +15,18 @@ SECURITY NOTE: Only enable file permissions when you trust the agent's actions.
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import PermissionRequest, PermissionRequestResult
from copilot.generated.session_events import PermissionRequest
from copilot.types import PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
def prompt_permission(
request: PermissionRequest, context: dict[str, str]
) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
print(f"\n[Permission Request: {request.kind}]")
if "path" in request:
print(f" Path: {request.get('path')}")
if request.path is not None:
print(f" Path: {request.path}")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
@@ -15,7 +15,8 @@ of MCP-related actions.
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import MCPServerConfig, PermissionRequest, PermissionRequestResult
from copilot.generated.session_events import PermissionRequest
from copilot.types import MCPServerConfig, PermissionRequestResult
from dotenv import load_dotenv
# Load environment variables from .env file
@@ -24,8 +25,7 @@ load_dotenv()
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
print(f"\n[Permission Request: {request.kind}]")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
@@ -21,18 +21,18 @@ More permissions mean more potential for unintended actions.
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import PermissionRequest, PermissionRequestResult
from copilot.generated.session_events import PermissionRequest
from copilot.types import PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
print(f"\n[Permission Request: {request.kind}]")
if "command" in request:
print(f" Command: {request.get('command')}")
if "path" in request:
print(f" Path: {request.get('path')}")
if request.full_command_text is not None:
print(f" Command: {request.full_command_text}")
if request.path is not None:
print(f" Path: {request.path}")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
@@ -14,16 +14,16 @@ Shell commands have full access to your system within the permissions of the run
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import PermissionRequest, PermissionRequestResult
from copilot.generated.session_events import PermissionRequest
from copilot.types import PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
print(f"\n[Permission Request: {request.kind}]")
if "command" in request:
print(f" Command: {request.get('command')}")
if request.full_command_text is not None:
print(f" Command: {request.full_command_text}")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
@@ -14,16 +14,16 @@ URL fetching allows the agent to access any URL accessible from your network.
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import PermissionRequest, PermissionRequestResult
from copilot.generated.session_events import PermissionRequest
from copilot.types import PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
print(f"\n[Permission Request: {request.kind}]")
if "url" in request:
print(f" URL: {request.get('url')}")
if request.url is not None:
print(f" URL: {request.url}")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):