chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# GitHub Copilot Agent Examples
|
||||
|
||||
This directory contains examples demonstrating how to use the `GitHubCopilotAgent` from the Microsoft Agent Framework.
|
||||
|
||||
> **Security Note**: These examples demonstrate various permission types (shell, read, write, url). Only enable permissions that are necessary for your use case. Each permission grants the agent additional capabilities that could affect your system.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **GitHub Copilot CLI**: Install and authenticate the Copilot CLI
|
||||
2. **GitHub Copilot Subscription**: An active GitHub Copilot subscription
|
||||
3. **Install the package**:
|
||||
```bash
|
||||
pip install agent-framework-github-copilot --pre
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The following environment variables can be configured:
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `GITHUB_COPILOT_CLI_PATH` | Path to the Copilot CLI executable | `copilot` |
|
||||
| `GITHUB_COPILOT_MODEL` | Model to use (e.g., "gpt-5", "claude-sonnet-4") | Server default |
|
||||
| `GITHUB_COPILOT_TIMEOUT` | Request timeout in seconds | `60` |
|
||||
| `GITHUB_COPILOT_LOG_LEVEL` | CLI log level | `info` |
|
||||
| `GITHUB_COPILOT_BASE_DIRECTORY` | Directory for CLI session state and config | `~/.copilot` |
|
||||
|
||||
## Observability
|
||||
|
||||
`GitHubCopilotAgent` has OpenTelemetry tracing built-in. To enable it, call `configure_otel_providers()` before running the agent:
|
||||
|
||||
```python
|
||||
from agent_framework.observability import configure_otel_providers
|
||||
from agent_framework.github import GitHubCopilotAgent
|
||||
|
||||
configure_otel_providers(enable_console_exporters=True)
|
||||
|
||||
async with GitHubCopilotAgent() as agent:
|
||||
response = await agent.run("Hello!")
|
||||
```
|
||||
|
||||
See the [observability samples](../../../02-agents/observability/) for full examples with OTLP exporters.
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`github_copilot_basic.py`](github_copilot_basic.py) | The simplest way to create an agent using `GitHubCopilotAgent`. Demonstrates both streaming and non-streaming responses with function tools. |
|
||||
| [`github_copilot_with_session.py`](github_copilot_with_session.py) | Shows session management with automatic creation, persistence via session objects, and resuming sessions by ID. |
|
||||
| [`github_copilot_with_shell.py`](github_copilot_with_shell.py) | Shows how to enable shell command execution permissions. Demonstrates running system commands like listing files and getting system information. |
|
||||
| [`github_copilot_with_file_operations.py`](github_copilot_with_file_operations.py) | Shows how to enable file read and write permissions. Demonstrates reading file contents and creating new files. |
|
||||
| [`github_copilot_with_url.py`](github_copilot_with_url.py) | Shows how to enable URL fetching permissions. Demonstrates fetching and processing web content. |
|
||||
| [`github_copilot_with_mcp.py`](github_copilot_with_mcp.py) | Shows how to configure MCP (Model Context Protocol) servers, including local (stdio) and remote (HTTP) servers. |
|
||||
| [`github_copilot_with_instruction_directories.py`](github_copilot_with_instruction_directories.py) | Shows how to configure custom instruction directories for project-specific or team-shared guidelines. |
|
||||
| [`github_copilot_with_multiple_permissions.py`](github_copilot_with_multiple_permissions.py) | Shows how to combine multiple permission types for complex tasks that require shell, read, and write access. |
|
||||
@@ -0,0 +1,123 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent Basic Example
|
||||
|
||||
This sample demonstrates basic usage of GitHubCopilotAgent.
|
||||
Shows both streaming and non-streaming responses with function tools.
|
||||
|
||||
Environment variables (optional):
|
||||
- GITHUB_COPILOT_CLI_PATH - Path to the Copilot CLI executable
|
||||
- GITHUB_COPILOT_MODEL - Model to use (e.g., "gpt-5", "claude-sonnet-4")
|
||||
- GITHUB_COPILOT_TIMEOUT - Request timeout in seconds
|
||||
- GITHUB_COPILOT_LOG_LEVEL - CLI log level
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.session import PermissionHandler
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production;
|
||||
# see samples/02-agents/tools/function_tool_with_approval.py
|
||||
# and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}C."
|
||||
|
||||
|
||||
async def non_streaming_example() -> None:
|
||||
"""Example of non-streaming response (get the complete result at once)."""
|
||||
print("=== Non-streaming Response Example ===")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "What's the weather like in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
|
||||
async def streaming_example() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
print("=== Streaming Response Example ===")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "What's the weather like in Tokyo?"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run(query, stream=True):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
print("\n")
|
||||
|
||||
|
||||
async def runtime_options_example() -> None:
|
||||
"""Example of overriding system message at runtime."""
|
||||
print("=== Runtime Options Example ===")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="Always respond in exactly 3 words.",
|
||||
tools=[get_weather],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "What's the weather like in Paris?"
|
||||
|
||||
# First call uses default instructions (3 words response)
|
||||
print("Using default instructions (3 words):")
|
||||
print(f"User: {query}")
|
||||
result1 = await agent.run(query)
|
||||
print(f"Agent: {result1}\n")
|
||||
|
||||
# Second call overrides with runtime system_message in replace mode
|
||||
print("Using runtime system_message with replace mode (detailed response):")
|
||||
print(f"User: {query}")
|
||||
result2 = await agent.run( # pyright: ignore[reportCallIssue]
|
||||
query,
|
||||
options=GitHubCopilotOptions( # pyright: ignore[reportArgumentType]
|
||||
system_message={
|
||||
"mode": "replace",
|
||||
"content": "You are a weather expert. Provide detailed weather information "
|
||||
"with temperature, and recommendations.",
|
||||
}
|
||||
),
|
||||
)
|
||||
print(f"Agent: {result2}\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Basic GitHub Copilot Agent Example ===")
|
||||
|
||||
await non_streaming_example()
|
||||
await streaming_example()
|
||||
await runtime_options_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent with File Operation Permissions
|
||||
|
||||
This sample demonstrates how to enable file read and write operations with GitHubCopilotAgent.
|
||||
By providing a permission handler, the agent can read from and write to files on the filesystem.
|
||||
|
||||
SECURITY NOTE: Only enable file permissions when you trust the agent's actions.
|
||||
- "read" allows the agent to read any accessible file
|
||||
- "write" allows the agent to create or modify files
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.generated.rpc import PermissionDecisionDeniedInteractivelyByUser
|
||||
from copilot.session import PermissionHandler, PermissionRequestResult
|
||||
from copilot.session_events import PermissionRequest
|
||||
|
||||
|
||||
async def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
|
||||
"""Permission handler that prompts the user for approval."""
|
||||
print(f"\n[Permission Request: {request.kind}]")
|
||||
response = (await asyncio.to_thread(input, "Approve? (y/n): ")).strip().lower()
|
||||
if response in ("y", "yes"):
|
||||
return PermissionHandler.approve_all(request, context)
|
||||
return PermissionDecisionDeniedInteractivelyByUser()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== GitHub Copilot Agent with File Operation Permissions ===\n")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful assistant that can read and write files.",
|
||||
default_options=GitHubCopilotOptions(on_permission_request=prompt_permission),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "Read the contents of README.md and summarize it"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent with Function Approval
|
||||
|
||||
This sample demonstrates how ``approval_mode="always_require"`` on a
|
||||
``FunctionTool`` is enforced when using ``GitHubCopilotAgent``. Because the
|
||||
Copilot CLI runs its own tool-calling loop, approval is enforced through the
|
||||
Copilot SDK's native pre-execution hook (``on_pre_tool_use``) rather than the
|
||||
standard agent-framework approval round-trip.
|
||||
|
||||
How it works:
|
||||
- When you register a tool declared with ``approval_mode="always_require"`` and
|
||||
you do **not** supply your own ``on_pre_tool_use`` hook, the agent installs a
|
||||
default ``on_pre_tool_use`` hook that returns ``"ask"`` for that tool and
|
||||
defers (``None``) for all other tools.
|
||||
- The ``"ask"`` decision routes to your ``on_permission_request`` handler, where
|
||||
you approve or deny the call. With the default deny-all permission handler,
|
||||
such a tool is therefore denied unless you wire an approving handler.
|
||||
- If you supply your own ``on_pre_tool_use`` hook, it takes precedence and you
|
||||
are responsible for enforcing approval; the agent logs a warning naming any
|
||||
approval-required tool that your hook must handle.
|
||||
|
||||
Environment variables (optional):
|
||||
- GITHUB_COPILOT_CLI_PATH: Path to the Copilot CLI executable.
|
||||
- GITHUB_COPILOT_MODEL: Model to use.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from random import randrange
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.generated.rpc import PermissionDecisionReject
|
||||
from copilot.session import (
|
||||
PermissionHandler,
|
||||
PermissionRequestResult,
|
||||
PreToolUseHookInput,
|
||||
PreToolUseHookOutput,
|
||||
)
|
||||
from copilot.session_events import PermissionRequest
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
INSTRUCTIONS = (
|
||||
"You are a helpful weather assistant. Always answer weather questions by calling the "
|
||||
"get_weather_detail tool. Do not browse the web or use any other source."
|
||||
)
|
||||
|
||||
|
||||
# Always-require tool: execution is gated by the default on_pre_tool_use hook,
|
||||
# which routes the decision to on_permission_request.
|
||||
@tool(approval_mode="always_require")
|
||||
def get_weather_detail(location: Annotated[str, "The city and state, e.g. San Francisco, CA"]) -> str:
|
||||
"""Get a detailed weather report for a location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return (
|
||||
f"The weather in {location} is {conditions[randrange(0, len(conditions))]} "
|
||||
f"with a high of {randrange(10, 30)}C and humidity of 88%."
|
||||
)
|
||||
|
||||
|
||||
def approve_all_requests(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
|
||||
"""Permission handler that approves every request, including the gated tool."""
|
||||
print(f"\n [Permission requested: {request.kind}] -> approved")
|
||||
return PermissionHandler.approve_all(request, context)
|
||||
|
||||
|
||||
def deny_all_requests(request: PermissionRequest, _context: dict[str, str]) -> PermissionRequestResult:
|
||||
"""Permission handler that denies every request."""
|
||||
print(f"\n [Permission requested: {request.kind}] -> denied")
|
||||
return PermissionDecisionReject(feedback="Denied by the operator's policy.")
|
||||
|
||||
|
||||
async def run_with_approval() -> None:
|
||||
"""The approval-required tool runs because on_permission_request approves it."""
|
||||
print("\n=== GitHub Copilot Agent: approval-required tool (approved) ===")
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions=INSTRUCTIONS,
|
||||
tools=[get_weather_detail],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=approve_all_requests),
|
||||
)
|
||||
async with agent:
|
||||
query = "Give me the detailed weather for Seattle."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}")
|
||||
|
||||
|
||||
async def run_with_denial() -> None:
|
||||
"""The approval-required tool is blocked because on_permission_request denies it."""
|
||||
print("\n=== GitHub Copilot Agent: approval-required tool (denied) ===")
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions=INSTRUCTIONS,
|
||||
tools=[get_weather_detail],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=deny_all_requests),
|
||||
)
|
||||
async with agent:
|
||||
query = "Give me the detailed weather for Paris."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}")
|
||||
|
||||
|
||||
async def run_with_custom_hook() -> None:
|
||||
"""A caller-supplied on_pre_tool_use hook takes precedence over the default.
|
||||
|
||||
When you provide your own hook you own approval enforcement entirely, so the
|
||||
agent does not install its default ask-hook and logs a warning naming any
|
||||
``always_require`` tool it will no longer auto-gate. Here the custom hook
|
||||
approves the tool directly by returning ``"allow"`` — note that, unlike the
|
||||
default ``"ask"`` flow, this does not route through ``on_permission_request``
|
||||
(so no permission request is raised for the tool).
|
||||
"""
|
||||
print("\n=== GitHub Copilot Agent: custom on_pre_tool_use hook (takes precedence) ===")
|
||||
|
||||
def my_pre_tool_use(hook_input: PreToolUseHookInput, _context: dict[str, str]) -> PreToolUseHookOutput | None:
|
||||
if hook_input.get("toolName") == "get_weather_detail":
|
||||
return {"permissionDecision": "allow", "permissionDecisionReason": "Allowed by custom policy."}
|
||||
return None
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions=INSTRUCTIONS,
|
||||
tools=[get_weather_detail],
|
||||
default_options=GitHubCopilotOptions(
|
||||
on_pre_tool_use=my_pre_tool_use,
|
||||
on_permission_request=approve_all_requests,
|
||||
),
|
||||
)
|
||||
async with agent:
|
||||
query = "Give me the detailed weather for Tokyo."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== GitHub Copilot Agent: Function approval enforcement ===")
|
||||
await run_with_approval()
|
||||
await run_with_denial()
|
||||
await run_with_custom_hook()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent with Instruction Directories
|
||||
|
||||
This sample demonstrates how to configure custom instruction directories with
|
||||
GitHubCopilotAgent. Instruction directories let the CLI load project-specific
|
||||
or team-shared instruction files that shape the agent's behavior beyond the
|
||||
default system message.
|
||||
|
||||
Use cases:
|
||||
- Point the agent at a team-shared set of coding conventions.
|
||||
- Load project-specific guidelines from a local `.copilot/instructions/` folder.
|
||||
- Override or augment default instructions per session at runtime.
|
||||
|
||||
Environment variables (optional):
|
||||
- GITHUB_COPILOT_CLI_PATH - Path to the Copilot CLI executable
|
||||
- GITHUB_COPILOT_MODEL - Model to use (e.g., "gpt-5", "claude-sonnet-4")
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.session import PermissionHandler
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def default_instructions_example() -> None:
|
||||
"""Example of pointing the agent at project-specific instruction directories."""
|
||||
print("=== Instruction Directories (Default) ===\n")
|
||||
|
||||
# 1. Define instruction directories.
|
||||
# These paths contain custom instruction files the CLI will load
|
||||
# alongside its built-in instructions.
|
||||
project_root = Path.cwd()
|
||||
instruction_dirs = [
|
||||
str(project_root / ".copilot" / "instructions"),
|
||||
str(project_root / "docs" / "agent-guidelines"),
|
||||
]
|
||||
|
||||
# 2. Create the agent with instruction directories in default_options.
|
||||
# These directories apply to every session created by this agent.
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful coding assistant.",
|
||||
default_options=GitHubCopilotOptions(
|
||||
on_permission_request=PermissionHandler.approve_all,
|
||||
instruction_directories=instruction_dirs,
|
||||
),
|
||||
)
|
||||
|
||||
# 3. Run the agent — instruction files from those directories are loaded
|
||||
# automatically by the CLI when the session starts.
|
||||
async with agent:
|
||||
query = "Summarize the coding conventions I should follow in this project."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
|
||||
async def runtime_override_example() -> None:
|
||||
"""Example of overriding instruction directories at runtime."""
|
||||
print("=== Instruction Directories (Runtime Override) ===\n")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful assistant.",
|
||||
default_options=GitHubCopilotOptions(
|
||||
on_permission_request=PermissionHandler.approve_all,
|
||||
instruction_directories=["/team/shared/instructions"],
|
||||
),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
# First call uses the default instruction directories
|
||||
query = "What instructions are you following?"
|
||||
print(f"User: {query}")
|
||||
result1 = await agent.run(query)
|
||||
print(f"Agent: {result1}\n")
|
||||
|
||||
# Second call overrides with different instruction directories at runtime.
|
||||
# Runtime options take precedence over the defaults for that session.
|
||||
print("Overriding with project-specific instructions...\n")
|
||||
query2 = "Now what instructions are you following?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await agent.run( # pyright: ignore[reportCallIssue]
|
||||
query2,
|
||||
options=GitHubCopilotOptions( # pyright: ignore[reportArgumentType]
|
||||
instruction_directories=["/project/specific/instructions"],
|
||||
),
|
||||
)
|
||||
print(f"Agent: {result2}\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== GitHub Copilot Agent with Instruction Directories ===\n")
|
||||
|
||||
await default_instructions_example()
|
||||
await runtime_override_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
"""
|
||||
Sample output:
|
||||
|
||||
=== GitHub Copilot Agent with Instruction Directories ===
|
||||
|
||||
=== Instruction Directories (Default) ===
|
||||
|
||||
User: Summarize the coding conventions I should follow in this project.
|
||||
Agent: Based on the project instructions, you should follow these conventions...
|
||||
|
||||
=== Instruction Directories (Runtime Override) ===
|
||||
|
||||
User: What instructions are you following?
|
||||
Agent: I'm following the team-shared coding guidelines which include...
|
||||
|
||||
Overriding with project-specific instructions...
|
||||
|
||||
User: Now what instructions are you following?
|
||||
Agent: I'm now following the project-specific instructions which include...
|
||||
"""
|
||||
@@ -0,0 +1,72 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent with MCP Servers
|
||||
|
||||
This sample demonstrates how to configure MCP (Model Context Protocol) servers
|
||||
with GitHubCopilotAgent. It shows both local (stdio) and remote (HTTP) server
|
||||
configurations, giving the agent access to external tools and data sources.
|
||||
|
||||
SECURITY NOTE: MCP servers can expose powerful capabilities. Only configure
|
||||
servers you trust. The permission handler below prompts the user for approval
|
||||
of MCP-related actions.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.session import MCPServerConfig, PermissionHandler
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== GitHub Copilot Agent with MCP Servers ===\n")
|
||||
|
||||
# Configure both local and remote MCP servers
|
||||
mcp_servers: dict[str, MCPServerConfig] = {
|
||||
# Local stdio server: provides filesystem access tools
|
||||
"filesystem": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
||||
"tools": ["*"],
|
||||
},
|
||||
# Remote HTTP server: Microsoft Learn documentation
|
||||
"microsoft-learn": {
|
||||
"type": "http",
|
||||
"url": "https://learn.microsoft.com/api/mcp",
|
||||
"tools": ["*"],
|
||||
},
|
||||
}
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful assistant with access to the local filesystem and Microsoft Learn.",
|
||||
default_options=GitHubCopilotOptions(
|
||||
on_permission_request=PermissionHandler.approve_all,
|
||||
mcp_servers=mcp_servers,
|
||||
),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
# Query that exercises the local filesystem MCP server
|
||||
query1 = "List the files in the current directory"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1)
|
||||
print(f"Agent: {result1}\n")
|
||||
|
||||
# Query that exercises the remote Microsoft Learn MCP server
|
||||
# Remote MCP calls may take longer, so increase the timeout
|
||||
query2 = "Search Microsoft Learn for 'Azure Functions Python' and summarize the top result"
|
||||
print(f"User: {query2}")
|
||||
result2 = await agent.run( # pyright: ignore[reportCallIssue]
|
||||
query2,
|
||||
options=GitHubCopilotOptions(timeout=120), # pyright: ignore[reportArgumentType]
|
||||
)
|
||||
print(f"Agent: {result2}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent with Multiple Permissions
|
||||
|
||||
This sample demonstrates how multiple permission types are requested when GitHubCopilotAgent
|
||||
performs complex tasks that require different capabilities.
|
||||
|
||||
Available permission kinds:
|
||||
- "shell": Execute shell commands
|
||||
- "read": Read files from the filesystem
|
||||
- "write": Write files to the filesystem
|
||||
- "mcp": Use MCP (Model Context Protocol) servers
|
||||
- "url": Fetch content from URLs
|
||||
|
||||
SECURITY NOTE: Only enable permissions that are necessary for your use case.
|
||||
More permissions mean more potential for unintended actions.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.session import PermissionHandler, PermissionRequestResult
|
||||
from copilot.session_events import PermissionRequest
|
||||
|
||||
|
||||
def approve_and_log(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
|
||||
"""Permission handler that auto-approves and logs each permission kind."""
|
||||
print(f" [Permission: {request.kind}]", flush=True)
|
||||
return PermissionHandler.approve_all(request, context)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== GitHub Copilot Agent with Multiple Permissions ===\n")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful development assistant that can read, write files and run commands.",
|
||||
default_options=GitHubCopilotOptions(on_permission_request=approve_and_log),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "List the first 3 Python files, then read the first one and create a summary in summary.txt"
|
||||
print(f"User: {query}\n")
|
||||
result = await agent.run(query)
|
||||
print(f"\nAgent: {result}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,147 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent with Session Management
|
||||
|
||||
This sample demonstrates session management with GitHubCopilotAgent, showing
|
||||
persistent conversation capabilities. Sessions are automatically persisted
|
||||
server-side by the Copilot CLI.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.session import PermissionHandler
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production;
|
||||
# see samples/02-agents/tools/function_tool_with_approval.py
|
||||
# and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Get the weather for a given location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
|
||||
|
||||
|
||||
async def example_with_automatic_session_creation() -> None:
|
||||
"""Each run() without thread creates a new session."""
|
||||
print("=== Automatic Session Creation Example ===")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
# First query - creates a new session
|
||||
query1 = "What's the weather like in Seattle?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1)
|
||||
print(f"Agent: {result1}")
|
||||
|
||||
# Second query - without thread, creates another new session
|
||||
query2 = "What was the last city I asked about?"
|
||||
print(f"\nUser: {query2}")
|
||||
result2 = await agent.run(query2)
|
||||
print(f"Agent: {result2}")
|
||||
print("Note: Each call creates a separate session, so the agent may not remember previous context.\n")
|
||||
|
||||
|
||||
async def example_with_session_persistence() -> None:
|
||||
"""Reuse session via thread object for multi-turn conversations."""
|
||||
print("=== Session Persistence Example ===")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
# Create a session to maintain conversation context
|
||||
session = agent.create_session()
|
||||
|
||||
# First query
|
||||
query1 = "What's the weather like in Tokyo?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1, session=session)
|
||||
print(f"Agent: {result1}")
|
||||
|
||||
# Second query - using same thread maintains context
|
||||
query2 = "How about London?"
|
||||
print(f"\nUser: {query2}")
|
||||
result2 = await agent.run(query2, session=session)
|
||||
print(f"Agent: {result2}")
|
||||
|
||||
# Third query - agent should remember both previous cities
|
||||
query3 = "Which of the cities I asked about has better weather?"
|
||||
print(f"\nUser: {query3}")
|
||||
result3 = await agent.run(query3, session=session)
|
||||
print(f"Agent: {result3}")
|
||||
print("Note: The agent remembers context from previous messages in the same session.\n")
|
||||
|
||||
|
||||
async def example_with_existing_session_id() -> None:
|
||||
"""Resume session in new agent instance using service_session_id."""
|
||||
print("=== Existing Session ID Example ===")
|
||||
|
||||
existing_session_id = None
|
||||
|
||||
# First agent instance - start a conversation
|
||||
agent1 = GitHubCopilotAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
||||
)
|
||||
|
||||
async with agent1:
|
||||
session = agent1.create_session()
|
||||
|
||||
query1 = "What's the weather in Paris?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent1.run(query1, session=session)
|
||||
print(f"Agent: {result1}")
|
||||
|
||||
# Capture the session ID for later use
|
||||
existing_session_id = session.service_session_id
|
||||
print(f"Session ID: {existing_session_id}")
|
||||
|
||||
if existing_session_id:
|
||||
print("\n--- Continuing with the same session ID in a new agent instance ---")
|
||||
|
||||
# Second agent instance - resume the conversation
|
||||
agent2 = GitHubCopilotAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
||||
)
|
||||
|
||||
async with agent2:
|
||||
# Get session with existing session ID
|
||||
session = agent2.get_session(service_session_id=existing_session_id)
|
||||
|
||||
query2 = "What was the last city I asked about?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await agent2.run(query2, session=session)
|
||||
print(f"Agent: {result2}")
|
||||
print("Note: The agent continues the conversation using the session ID.\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== GitHub Copilot Agent Session Management Examples ===\n")
|
||||
|
||||
await example_with_automatic_session_creation()
|
||||
await example_with_session_persistence()
|
||||
await example_with_existing_session_id()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,49 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent with Shell Permissions
|
||||
|
||||
This sample demonstrates how to enable shell command execution with GitHubCopilotAgent.
|
||||
By providing a permission handler that approves "shell" requests, the agent can execute
|
||||
shell commands to perform tasks like listing files, running scripts, or executing system commands.
|
||||
|
||||
SECURITY NOTE: Only enable shell permissions when you trust the agent's actions.
|
||||
Shell commands have full access to your system within the permissions of the running process.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.generated.rpc import PermissionDecisionUserNotAvailable
|
||||
from copilot.session import PermissionHandler, PermissionRequestResult
|
||||
from copilot.session_events import PermissionRequest
|
||||
|
||||
|
||||
def approve_and_log(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
|
||||
"""Permission handler that approves only shell commands and logs them."""
|
||||
if request.kind == "shell":
|
||||
print(f"\n [Permission: {request.kind}]", flush=True)
|
||||
command = getattr(request, "full_command_text", None)
|
||||
if command is not None:
|
||||
print(f" Command: {command}", flush=True)
|
||||
return PermissionHandler.approve_all(request, context)
|
||||
return PermissionDecisionUserNotAvailable()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== GitHub Copilot Agent with Shell Permissions ===\n")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful assistant that can execute shell commands.",
|
||||
default_options=GitHubCopilotOptions(on_permission_request=approve_and_log),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "List the first 3 Python files in the current directory"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"\nAgent: {result}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,49 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
GitHub Copilot Agent with URL Fetching
|
||||
|
||||
This sample demonstrates how to enable URL fetching with GitHubCopilotAgent.
|
||||
By providing a permission handler that approves "url" requests, the agent can
|
||||
fetch and process content from web URLs.
|
||||
|
||||
SECURITY NOTE: Only enable URL permissions when you trust the agent's actions.
|
||||
URL fetching allows the agent to access any URL accessible from your network.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
||||
from copilot.generated.rpc import PermissionDecisionUserNotAvailable
|
||||
from copilot.session import PermissionHandler, PermissionRequestResult
|
||||
from copilot.session_events import PermissionRequest
|
||||
|
||||
|
||||
def approve_and_log(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
|
||||
"""Permission handler that approves only URL requests and logs them."""
|
||||
if request.kind == "url":
|
||||
print(f"\n [Permission: {request.kind}]", flush=True)
|
||||
url = getattr(request, "url", None)
|
||||
if url is not None:
|
||||
print(f" URL: {url}", flush=True)
|
||||
return PermissionHandler.approve_all(request, context)
|
||||
return PermissionDecisionUserNotAvailable()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== GitHub Copilot Agent with URL Fetching ===\n")
|
||||
|
||||
agent: GitHubCopilotAgent[GitHubCopilotOptions] = GitHubCopilotAgent(
|
||||
instructions="You are a helpful assistant that can fetch and summarize web content.",
|
||||
default_options=GitHubCopilotOptions(on_permission_request=approve_and_log),
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "Fetch https://learn.microsoft.com/agent-framework/tutorials/quick-start and summarize its contents"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"\nAgent: {result}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user