agent-framework-github-copilot 1.0.0rc1__tar.gz → 1.0.0rc2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_framework_github_copilot-1.0.0rc2/PKG-INFO +87 -0
- agent_framework_github_copilot-1.0.0rc2/README.md +61 -0
- {agent_framework_github_copilot-1.0.0rc1 → agent_framework_github_copilot-1.0.0rc2}/agent_framework_github_copilot/_agent.py +181 -41
- {agent_framework_github_copilot-1.0.0rc1 → agent_framework_github_copilot-1.0.0rc2}/pyproject.toml +3 -3
- agent_framework_github_copilot-1.0.0rc1/PKG-INFO +0 -37
- agent_framework_github_copilot-1.0.0rc1/README.md +0 -11
- {agent_framework_github_copilot-1.0.0rc1 → agent_framework_github_copilot-1.0.0rc2}/LICENSE +0 -0
- {agent_framework_github_copilot-1.0.0rc1 → agent_framework_github_copilot-1.0.0rc2}/agent_framework_github_copilot/__init__.py +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-framework-github-copilot
|
|
3
|
+
Version: 1.0.0rc2
|
|
4
|
+
Summary: GitHub Copilot integration for Microsoft Agent Framework.
|
|
5
|
+
Author-email: Microsoft <af-support@microsoft.com>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: agent-framework-core>=1.10.0,<2
|
|
20
|
+
Requires-Dist: github-copilot-sdk==1.0.2; python_version >= '3.11'
|
|
21
|
+
Project-URL: homepage, https://aka.ms/agent-framework
|
|
22
|
+
Project-URL: issues, https://github.com/microsoft/agent-framework/issues
|
|
23
|
+
Project-URL: release_notes, https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true
|
|
24
|
+
Project-URL: source, https://github.com/microsoft/agent-framework/tree/main/python
|
|
25
|
+
|
|
26
|
+
# Get Started with Microsoft Agent Framework GitHub Copilot
|
|
27
|
+
|
|
28
|
+
Please install this package via pip:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install agent-framework-github-copilot --pre
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## GitHub Copilot Agent
|
|
35
|
+
|
|
36
|
+
The GitHub Copilot agent enables integration with GitHub Copilot, allowing you to interact with Copilot's agentic capabilities through the Agent Framework.
|
|
37
|
+
|
|
38
|
+
## Tool approval (`approval_mode="always_require"`)
|
|
39
|
+
|
|
40
|
+
The GitHub Copilot SDK owns the tool-calling loop for this provider, so approval for
|
|
41
|
+
custom function tools is enforced through the SDK's native pre-execution hook rather
|
|
42
|
+
than the standard Agent Framework approval round-trip.
|
|
43
|
+
|
|
44
|
+
When you register a `FunctionTool` declared with `approval_mode="always_require"` and you
|
|
45
|
+
do **not** supply your own `on_pre_tool_use` hook, `GitHubCopilotAgent` installs a default
|
|
46
|
+
`on_pre_tool_use` hook that returns `"ask"` for that tool and defers (`None`) for all other
|
|
47
|
+
tools. The `"ask"` decision routes to your `on_permission_request` handler, where you
|
|
48
|
+
approve or deny the call:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from agent_framework import tool
|
|
52
|
+
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
|
53
|
+
from copilot.session import PermissionHandler
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@tool(approval_mode="always_require")
|
|
57
|
+
def delete_file(path: str) -> str:
|
|
58
|
+
"""Delete a file."""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
agent = GitHubCopilotAgent(
|
|
63
|
+
tools=[delete_file],
|
|
64
|
+
# The "ask" decision is routed here; approve or deny the call.
|
|
65
|
+
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> **⚠️ If you provide your own `on_pre_tool_use` hook**, it takes precedence and the agent
|
|
70
|
+
> does **not** install its default approval hook. In that case **you are fully responsible**
|
|
71
|
+
> for enforcing approval — including for any `approval_mode="always_require"` tool (e.g. by
|
|
72
|
+
> returning a `"deny"` or `"ask"` decision). The agent logs a warning naming any
|
|
73
|
+
> approval-required tool that your hook must handle.
|
|
74
|
+
>
|
|
75
|
+
> Note: with the default (deny-all) permission handler, an `always_require` tool is denied
|
|
76
|
+
> unless you wire an approving `on_permission_request`.
|
|
77
|
+
|
|
78
|
+
### Deprecated: `on_function_approval`
|
|
79
|
+
|
|
80
|
+
The `on_function_approval` callback is **deprecated**. It still works (and is still enforced
|
|
81
|
+
inside the tool handler for backward compatibility), but it emits a `DeprecationWarning` and
|
|
82
|
+
will be removed in a future version. Migrate to the `on_pre_tool_use` + `on_permission_request`
|
|
83
|
+
model described above. When `on_function_approval` is set, it gates `always_require` tools and
|
|
84
|
+
the default ask-hook is not installed. It is **mutually exclusive** with `on_pre_tool_use` —
|
|
85
|
+
setting both (whether at construction or per run) raises `ValueError`.
|
|
86
|
+
|
|
87
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Get Started with Microsoft Agent Framework GitHub Copilot
|
|
2
|
+
|
|
3
|
+
Please install this package via pip:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install agent-framework-github-copilot --pre
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## GitHub Copilot Agent
|
|
10
|
+
|
|
11
|
+
The GitHub Copilot agent enables integration with GitHub Copilot, allowing you to interact with Copilot's agentic capabilities through the Agent Framework.
|
|
12
|
+
|
|
13
|
+
## Tool approval (`approval_mode="always_require"`)
|
|
14
|
+
|
|
15
|
+
The GitHub Copilot SDK owns the tool-calling loop for this provider, so approval for
|
|
16
|
+
custom function tools is enforced through the SDK's native pre-execution hook rather
|
|
17
|
+
than the standard Agent Framework approval round-trip.
|
|
18
|
+
|
|
19
|
+
When you register a `FunctionTool` declared with `approval_mode="always_require"` and you
|
|
20
|
+
do **not** supply your own `on_pre_tool_use` hook, `GitHubCopilotAgent` installs a default
|
|
21
|
+
`on_pre_tool_use` hook that returns `"ask"` for that tool and defers (`None`) for all other
|
|
22
|
+
tools. The `"ask"` decision routes to your `on_permission_request` handler, where you
|
|
23
|
+
approve or deny the call:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from agent_framework import tool
|
|
27
|
+
from agent_framework.github import GitHubCopilotAgent, GitHubCopilotOptions
|
|
28
|
+
from copilot.session import PermissionHandler
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@tool(approval_mode="always_require")
|
|
32
|
+
def delete_file(path: str) -> str:
|
|
33
|
+
"""Delete a file."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
agent = GitHubCopilotAgent(
|
|
38
|
+
tools=[delete_file],
|
|
39
|
+
# The "ask" decision is routed here; approve or deny the call.
|
|
40
|
+
default_options=GitHubCopilotOptions(on_permission_request=PermissionHandler.approve_all),
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> **⚠️ If you provide your own `on_pre_tool_use` hook**, it takes precedence and the agent
|
|
45
|
+
> does **not** install its default approval hook. In that case **you are fully responsible**
|
|
46
|
+
> for enforcing approval — including for any `approval_mode="always_require"` tool (e.g. by
|
|
47
|
+
> returning a `"deny"` or `"ask"` decision). The agent logs a warning naming any
|
|
48
|
+
> approval-required tool that your hook must handle.
|
|
49
|
+
>
|
|
50
|
+
> Note: with the default (deny-all) permission handler, an `always_require` tool is denied
|
|
51
|
+
> unless you wire an approving `on_permission_request`.
|
|
52
|
+
|
|
53
|
+
### Deprecated: `on_function_approval`
|
|
54
|
+
|
|
55
|
+
The `on_function_approval` callback is **deprecated**. It still works (and is still enforced
|
|
56
|
+
inside the tool handler for backward compatibility), but it emits a `DeprecationWarning` and
|
|
57
|
+
will be removed in a future version. Migrate to the `on_pre_tool_use` + `on_permission_request`
|
|
58
|
+
model described above. When `on_function_approval` is set, it gates `always_require` tools and
|
|
59
|
+
the default ask-hook is not installed. It is **mutually exclusive** with `on_pre_tool_use` —
|
|
60
|
+
setting both (whether at construction or per run) raises `ValueError`.
|
|
61
|
+
|
|
@@ -7,6 +7,7 @@ import contextlib
|
|
|
7
7
|
import inspect
|
|
8
8
|
import logging
|
|
9
9
|
import sys
|
|
10
|
+
import warnings
|
|
10
11
|
from collections.abc import AsyncIterable, Awaitable, Callable, Mapping, MutableMapping, Sequence
|
|
11
12
|
from typing import Any, ClassVar, Generic, Literal, TypedDict, overload
|
|
12
13
|
|
|
@@ -39,7 +40,15 @@ from agent_framework.observability import AgentTelemetryLayer
|
|
|
39
40
|
try:
|
|
40
41
|
from copilot import CopilotClient, CopilotSession, RuntimeConnection
|
|
41
42
|
from copilot.generated.rpc import PermissionDecisionUserNotAvailable
|
|
42
|
-
from copilot.session import
|
|
43
|
+
from copilot.session import (
|
|
44
|
+
MCPServerConfig,
|
|
45
|
+
PermissionRequestResult,
|
|
46
|
+
PreToolUseHandler,
|
|
47
|
+
PreToolUseHookOutput,
|
|
48
|
+
ProviderConfig,
|
|
49
|
+
SessionHooks,
|
|
50
|
+
SystemMessageConfig,
|
|
51
|
+
)
|
|
43
52
|
from copilot.session_events import PermissionRequest, SessionEvent, SessionEventType
|
|
44
53
|
from copilot.tools import Tool as CopilotTool
|
|
45
54
|
from copilot.tools import ToolInvocation, ToolResult
|
|
@@ -50,9 +59,9 @@ except ImportError as _copilot_import_error:
|
|
|
50
59
|
) from _copilot_import_error
|
|
51
60
|
|
|
52
61
|
if sys.version_info >= (3, 13):
|
|
53
|
-
from typing import TypeVar
|
|
62
|
+
from typing import TypeVar # pragma: no cover
|
|
54
63
|
else:
|
|
55
|
-
from typing_extensions import TypeVar
|
|
64
|
+
from typing_extensions import TypeVar # pragma: no cover
|
|
56
65
|
|
|
57
66
|
|
|
58
67
|
DEFAULT_TIMEOUT_SECONDS: float = 60.0
|
|
@@ -65,23 +74,18 @@ PermissionHandlerType = Callable[
|
|
|
65
74
|
|
|
66
75
|
|
|
67
76
|
FunctionApprovalCallback = Callable[[Content], "bool | Awaitable[bool]"]
|
|
68
|
-
"""
|
|
77
|
+
"""Deprecated approval callback for ``FunctionTool`` instances declared with
|
|
78
|
+
``approval_mode="always_require"``.
|
|
79
|
+
|
|
80
|
+
.. deprecated::
|
|
81
|
+
Use the SDK ``on_pre_tool_use`` hook together with ``on_permission_request``
|
|
82
|
+
instead. The default ``on_pre_tool_use`` hook returns ``"ask"`` for
|
|
83
|
+
``always_require`` tools and routes the decision to ``on_permission_request``.
|
|
69
84
|
|
|
70
85
|
The callback receives a ``FunctionCallContent`` describing the pending call
|
|
71
86
|
(``name``, ``arguments``, and a synthetic ``call_id``) and must return ``True``
|
|
72
87
|
to allow execution or ``False`` to deny it. Both synchronous and ``await``-able
|
|
73
88
|
return values are supported.
|
|
74
|
-
|
|
75
|
-
The Copilot CLI manages its own tool-calling loop, so the framework cannot
|
|
76
|
-
round-trip a ``FunctionApprovalRequestContent`` / ``FunctionApprovalResponseContent``
|
|
77
|
-
pair the way the standard chat-client pipeline does. This callback is the
|
|
78
|
-
agent-level enforcement point for tools declared with
|
|
79
|
-
``approval_mode="always_require"``: when no callback is configured the agent
|
|
80
|
-
denies these calls by default.
|
|
81
|
-
|
|
82
|
-
Note: this is independent of ``on_permission_request``, which gates the
|
|
83
|
-
Copilot SDK's *built-in* shell/file actions; ``on_function_approval`` gates
|
|
84
|
-
agent-framework ``FunctionTool`` calls.
|
|
85
89
|
"""
|
|
86
90
|
|
|
87
91
|
|
|
@@ -90,7 +94,7 @@ async def _resolve_function_approval(
|
|
|
90
94
|
func_tool: FunctionTool,
|
|
91
95
|
arguments: Mapping[str, Any] | None,
|
|
92
96
|
) -> bool:
|
|
93
|
-
"""Run the agent-level approval callback for a pending tool call.
|
|
97
|
+
"""Run the deprecated agent-level approval callback for a pending tool call.
|
|
94
98
|
|
|
95
99
|
Returns ``True`` only when ``callback`` is configured and explicitly returns
|
|
96
100
|
a truthy value. A missing callback or any callback failure is treated as a
|
|
@@ -202,13 +206,39 @@ class GitHubCopilotOptions(TypedDict, total=False):
|
|
|
202
206
|
files beyond the default locations.
|
|
203
207
|
"""
|
|
204
208
|
|
|
209
|
+
base_directory: str
|
|
210
|
+
"""Directory where the CLI stores session state, configuration, and other persistent data."""
|
|
211
|
+
|
|
212
|
+
on_pre_tool_use: PreToolUseHandler
|
|
213
|
+
"""Pre-tool-use hook handler for the Copilot SDK.
|
|
214
|
+
|
|
215
|
+
Called by the Copilot SDK before any tool is executed. The handler receives a
|
|
216
|
+
``PreToolUseHookInput`` and a context dict, and returns a ``PreToolUseHookOutput``
|
|
217
|
+
(or ``None`` to defer). Returning ``{"permissionDecision": "ask"}`` routes the
|
|
218
|
+
decision to ``on_permission_request``; ``"allow"`` / ``"deny"`` gate the call
|
|
219
|
+
directly.
|
|
220
|
+
|
|
221
|
+
If you do **not** supply this hook, the agent installs a default ``on_pre_tool_use``
|
|
222
|
+
hook that returns ``"ask"`` for ``FunctionTool`` instances declared with
|
|
223
|
+
``approval_mode="always_require"`` (deferring all other tools), so those tools are
|
|
224
|
+
gated through ``on_permission_request``. If you **do** supply your own hook, it
|
|
225
|
+
takes precedence and **you** are responsible for enforcing approval for any
|
|
226
|
+
``always_require`` tool; the agent logs a warning naming such tools."""
|
|
227
|
+
|
|
205
228
|
on_function_approval: FunctionApprovalCallback
|
|
206
|
-
"""
|
|
207
|
-
``approval_mode="always_require"``.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
229
|
+
"""Deprecated approval callback for ``FunctionTool`` instances declared with
|
|
230
|
+
``approval_mode="always_require"``.
|
|
231
|
+
|
|
232
|
+
.. deprecated::
|
|
233
|
+
Use ``on_pre_tool_use`` together with ``on_permission_request`` instead.
|
|
234
|
+
When neither this callback nor ``on_pre_tool_use`` is set, the agent
|
|
235
|
+
installs a default ``on_pre_tool_use`` hook that returns ``"ask"`` for
|
|
236
|
+
``always_require`` tools and routes the decision to ``on_permission_request``.
|
|
237
|
+
|
|
238
|
+
When set, this callback is enforced inside the SDK tool-handler before the tool
|
|
239
|
+
runs; a falsy return value denies the call. Setting it emits a
|
|
240
|
+
``DeprecationWarning``. It is **mutually exclusive** with ``on_pre_tool_use`` —
|
|
241
|
+
setting both raises ``ValueError``."""
|
|
212
242
|
|
|
213
243
|
|
|
214
244
|
OptionsT = TypeVar(
|
|
@@ -316,9 +346,27 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
316
346
|
mcp_servers: dict[str, MCPServerConfig] | None = opts.pop("mcp_servers", None)
|
|
317
347
|
provider: ProviderConfig | None = opts.pop("provider", None)
|
|
318
348
|
instruction_directories: list[str] | None = opts.pop("instruction_directories", None)
|
|
349
|
+
on_pre_tool_use: PreToolUseHandler | None = opts.pop("on_pre_tool_use", None)
|
|
319
350
|
on_function_approval: FunctionApprovalCallback | None = opts.pop("on_function_approval", None)
|
|
320
351
|
base_directory = opts.pop("base_directory", None)
|
|
321
352
|
|
|
353
|
+
if on_function_approval is not None and on_pre_tool_use is not None:
|
|
354
|
+
raise ValueError(
|
|
355
|
+
"on_function_approval and on_pre_tool_use cannot both be set. "
|
|
356
|
+
"on_function_approval is deprecated; use on_pre_tool_use together with "
|
|
357
|
+
"on_permission_request instead."
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
if on_function_approval is not None:
|
|
361
|
+
warnings.warn(
|
|
362
|
+
"on_function_approval is deprecated and will be removed in a future version. "
|
|
363
|
+
"Use the SDK 'on_pre_tool_use' hook together with 'on_permission_request' instead: "
|
|
364
|
+
"the default 'on_pre_tool_use' hook returns 'ask' for approval_mode='always_require' "
|
|
365
|
+
"tools and routes the decision to 'on_permission_request'.",
|
|
366
|
+
DeprecationWarning,
|
|
367
|
+
stacklevel=2,
|
|
368
|
+
)
|
|
369
|
+
|
|
322
370
|
self._settings = load_settings(
|
|
323
371
|
GitHubCopilotSettings,
|
|
324
372
|
env_prefix="GITHUB_COPILOT_",
|
|
@@ -333,6 +381,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
333
381
|
|
|
334
382
|
self._tools = normalize_tools(tools)
|
|
335
383
|
self._permission_handler = on_permission_request
|
|
384
|
+
self._on_pre_tool_use: PreToolUseHandler | None = on_pre_tool_use
|
|
336
385
|
self._function_approval_handler: FunctionApprovalCallback | None = on_function_approval
|
|
337
386
|
self._mcp_servers = mcp_servers
|
|
338
387
|
self._provider = provider
|
|
@@ -441,7 +490,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
441
490
|
session: AgentSession | None = None,
|
|
442
491
|
middleware: Sequence[AgentMiddlewareTypes] | None = None,
|
|
443
492
|
options: OptionsT | None = None,
|
|
444
|
-
**kwargs: Any,
|
|
493
|
+
**kwargs: Any,
|
|
445
494
|
) -> Awaitable[AgentResponse] | ResponseStream[AgentResponseUpdate, AgentResponse]:
|
|
446
495
|
"""Get a response from the agent.
|
|
447
496
|
|
|
@@ -519,6 +568,12 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
519
568
|
"via default_options at agent construction time. It cannot be overridden "
|
|
520
569
|
"per run."
|
|
521
570
|
)
|
|
571
|
+
if "on_pre_tool_use" in opts and self._function_approval_handler is not None:
|
|
572
|
+
raise ValueError(
|
|
573
|
+
"on_pre_tool_use cannot be combined with the deprecated on_function_approval "
|
|
574
|
+
"(set via default_options). Remove on_function_approval and use on_pre_tool_use "
|
|
575
|
+
"together with on_permission_request instead."
|
|
576
|
+
)
|
|
522
577
|
timeout = opts.get("timeout") or self._settings.get("timeout") or DEFAULT_TIMEOUT_SECONDS
|
|
523
578
|
|
|
524
579
|
input_messages = normalize_messages(messages)
|
|
@@ -608,6 +663,12 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
608
663
|
"via default_options at agent construction time. It cannot be overridden "
|
|
609
664
|
"per run."
|
|
610
665
|
)
|
|
666
|
+
if "on_pre_tool_use" in opts and self._function_approval_handler is not None:
|
|
667
|
+
raise ValueError(
|
|
668
|
+
"on_pre_tool_use cannot be combined with the deprecated on_function_approval "
|
|
669
|
+
"(set via default_options). Remove on_function_approval and use on_pre_tool_use "
|
|
670
|
+
"together with on_permission_request instead."
|
|
671
|
+
)
|
|
611
672
|
|
|
612
673
|
input_messages = normalize_messages(messages)
|
|
613
674
|
|
|
@@ -732,7 +793,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
732
793
|
if isinstance(provider, HistoryProvider) and not provider.load_messages:
|
|
733
794
|
continue
|
|
734
795
|
await provider.before_run(
|
|
735
|
-
agent=self,
|
|
796
|
+
agent=self,
|
|
736
797
|
session=session,
|
|
737
798
|
context=session_context,
|
|
738
799
|
state=session.state.setdefault(provider.source_id, {}),
|
|
@@ -781,7 +842,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
781
842
|
if isinstance(tool, CopilotTool):
|
|
782
843
|
copilot_tools.append(tool)
|
|
783
844
|
elif isinstance(tool, FunctionTool):
|
|
784
|
-
copilot_tools.append(self._tool_to_copilot_tool(tool))
|
|
845
|
+
copilot_tools.append(self._tool_to_copilot_tool(tool))
|
|
785
846
|
elif isinstance(tool, MutableMapping):
|
|
786
847
|
copilot_tools.append(tool) # type: ignore[arg-type]
|
|
787
848
|
# Note: Other tool types (e.g., dict-based hosted tools) are skipped
|
|
@@ -789,31 +850,32 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
789
850
|
return copilot_tools
|
|
790
851
|
|
|
791
852
|
def _tool_to_copilot_tool(self, ai_func: FunctionTool) -> CopilotTool:
|
|
792
|
-
"""Convert an FunctionTool to a Copilot SDK tool.
|
|
853
|
+
"""Convert an FunctionTool to a Copilot SDK tool.
|
|
854
|
+
|
|
855
|
+
Approval for tools declared with ``approval_mode="always_require"`` is normally
|
|
856
|
+
enforced by the Copilot SDK's native ``on_pre_tool_use`` hook (see
|
|
857
|
+
:meth:`_build_session_hooks`). When the deprecated ``on_function_approval``
|
|
858
|
+
callback is configured instead, approval is enforced inside this handler for
|
|
859
|
+
backward compatibility. (``on_function_approval`` and ``on_pre_tool_use`` are
|
|
860
|
+
mutually exclusive, so only one mechanism is ever active.)
|
|
861
|
+
"""
|
|
793
862
|
approval_handler = self._function_approval_handler
|
|
794
|
-
|
|
863
|
+
enforce = approval_handler is not None and ai_func.approval_mode == "always_require"
|
|
795
864
|
|
|
796
865
|
async def handler(invocation: ToolInvocation) -> ToolResult:
|
|
797
866
|
args: dict[str, Any] = invocation.arguments or {}
|
|
798
867
|
try:
|
|
799
|
-
if
|
|
800
|
-
deny_text = (
|
|
801
|
-
f"Tool '{ai_func.name}' requires human approval "
|
|
802
|
-
"(approval_mode='always_require') and the request was denied."
|
|
803
|
-
if approval_handler is not None
|
|
804
|
-
else (
|
|
805
|
-
f"Tool '{ai_func.name}' requires human approval "
|
|
806
|
-
"(approval_mode='always_require') but no on_function_approval "
|
|
807
|
-
"callback is configured on the agent; the request was denied."
|
|
808
|
-
)
|
|
809
|
-
)
|
|
868
|
+
if enforce and not await _resolve_function_approval(approval_handler, ai_func, args):
|
|
810
869
|
logger.info(
|
|
811
|
-
"Denying execution of tool '%s' (approval_mode='always_require',
|
|
870
|
+
"Denying execution of tool '%s' (approval_mode='always_require', "
|
|
871
|
+
"on_function_approval callback denied).",
|
|
812
872
|
ai_func.name,
|
|
813
|
-
"callback denied" if approval_handler is not None else "no callback configured",
|
|
814
873
|
)
|
|
815
874
|
return ToolResult(
|
|
816
|
-
text_result_for_llm=
|
|
875
|
+
text_result_for_llm=(
|
|
876
|
+
f"Tool '{ai_func.name}' requires human approval "
|
|
877
|
+
"(approval_mode='always_require') and the request was denied."
|
|
878
|
+
),
|
|
817
879
|
result_type="failure",
|
|
818
880
|
error="approval_denied",
|
|
819
881
|
)
|
|
@@ -847,6 +909,80 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
847
909
|
parameters=ai_func.parameters(),
|
|
848
910
|
)
|
|
849
911
|
|
|
912
|
+
def _build_session_hooks(
|
|
913
|
+
self,
|
|
914
|
+
all_tools: Sequence[ToolTypes | CopilotTool],
|
|
915
|
+
opts: Mapping[str, Any],
|
|
916
|
+
) -> SessionHooks | None:
|
|
917
|
+
"""Build the ``SessionHooks`` to pass to the Copilot SDK for this session.
|
|
918
|
+
|
|
919
|
+
Approval enforcement for ``FunctionTool`` instances declared with
|
|
920
|
+
``approval_mode="always_require"`` is delegated to the Copilot SDK's native
|
|
921
|
+
``on_pre_tool_use`` hook:
|
|
922
|
+
|
|
923
|
+
- If the caller supplies their own ``on_pre_tool_use`` (via per-run ``options``
|
|
924
|
+
or ``default_options``), it takes precedence and is returned unchanged. A
|
|
925
|
+
warning is logged naming any approval-required tool that will therefore not
|
|
926
|
+
be automatically gated, since the caller's hook is responsible for enforcing
|
|
927
|
+
approval.
|
|
928
|
+
- Otherwise, when any approval-required tool is present, a default hook is
|
|
929
|
+
installed that returns ``"ask"`` for those tools (routing the decision to
|
|
930
|
+
``on_permission_request``) and defers (``None``) for all other tools.
|
|
931
|
+
- The default hook is **not** installed when the deprecated
|
|
932
|
+
``on_function_approval`` callback is configured: in that case approval is
|
|
933
|
+
enforced inside the tool handler (see :meth:`_tool_to_copilot_tool`) to
|
|
934
|
+
preserve backward-compatible behavior.
|
|
935
|
+
- When there are no approval-required tools and no caller hook, ``None`` is
|
|
936
|
+
returned so no hooks are registered.
|
|
937
|
+
|
|
938
|
+
Args:
|
|
939
|
+
all_tools: The full set of tools resolved for the session.
|
|
940
|
+
opts: Runtime options that take precedence over ``default_options``.
|
|
941
|
+
|
|
942
|
+
Returns:
|
|
943
|
+
The hooks to register for the session, or ``None`` if none are needed.
|
|
944
|
+
"""
|
|
945
|
+
user_hook: PreToolUseHandler | None = opts.get("on_pre_tool_use") or self._on_pre_tool_use
|
|
946
|
+
|
|
947
|
+
approval_required_names = {
|
|
948
|
+
tool.name for tool in all_tools if isinstance(tool, FunctionTool) and tool.approval_mode == "always_require"
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
if user_hook is not None:
|
|
952
|
+
if approval_required_names:
|
|
953
|
+
logger.warning(
|
|
954
|
+
"A custom 'on_pre_tool_use' hook is configured, so %d approval-required tool(s) (%s) "
|
|
955
|
+
"will not be automatically gated by GitHubCopilotAgent. The custom hook is responsible "
|
|
956
|
+
"for enforcing approval (for example, by returning a 'deny' or 'ask' decision).",
|
|
957
|
+
len(approval_required_names),
|
|
958
|
+
", ".join(sorted(approval_required_names)),
|
|
959
|
+
)
|
|
960
|
+
return {"on_pre_tool_use": user_hook}
|
|
961
|
+
|
|
962
|
+
if not approval_required_names:
|
|
963
|
+
return None
|
|
964
|
+
|
|
965
|
+
# The deprecated on_function_approval callback enforces approval in the tool
|
|
966
|
+
# handler; don't also install the default ask-hook (which would double-gate).
|
|
967
|
+
if self._function_approval_handler is not None:
|
|
968
|
+
return None
|
|
969
|
+
|
|
970
|
+
def default_pre_tool_use(
|
|
971
|
+
hook_input: Mapping[str, Any],
|
|
972
|
+
_context: Mapping[str, str],
|
|
973
|
+
) -> PreToolUseHookOutput | None:
|
|
974
|
+
tool_name = hook_input.get("toolName")
|
|
975
|
+
if tool_name in approval_required_names:
|
|
976
|
+
return {
|
|
977
|
+
"permissionDecision": "ask",
|
|
978
|
+
"permissionDecisionReason": (
|
|
979
|
+
f"Tool '{tool_name}' is marked as requiring approval (approval_mode='always_require')."
|
|
980
|
+
),
|
|
981
|
+
}
|
|
982
|
+
return None
|
|
983
|
+
|
|
984
|
+
return {"on_pre_tool_use": default_pre_tool_use}
|
|
985
|
+
|
|
850
986
|
async def _get_or_create_session(
|
|
851
987
|
self,
|
|
852
988
|
agent_session: AgentSession,
|
|
@@ -904,6 +1040,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
904
1040
|
instruction_directories = opts.get("instruction_directories", self._instruction_directories)
|
|
905
1041
|
all_tools = list(self._tools or []) + list(opts.get("tools") or [])
|
|
906
1042
|
tools = self._prepare_tools(all_tools) if all_tools else None
|
|
1043
|
+
hooks = self._build_session_hooks(all_tools, opts)
|
|
907
1044
|
|
|
908
1045
|
return await self._client.create_session(
|
|
909
1046
|
on_permission_request=permission_handler,
|
|
@@ -914,6 +1051,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
914
1051
|
mcp_servers=mcp_servers or None,
|
|
915
1052
|
provider=provider or None,
|
|
916
1053
|
instruction_directories=instruction_directories,
|
|
1054
|
+
hooks=hooks,
|
|
917
1055
|
)
|
|
918
1056
|
|
|
919
1057
|
async def _resume_session(
|
|
@@ -943,6 +1081,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
943
1081
|
instruction_directories = opts.get("instruction_directories", self._instruction_directories)
|
|
944
1082
|
all_tools = list(self._tools or []) + list(opts.get("tools") or [])
|
|
945
1083
|
tools = self._prepare_tools(all_tools) if all_tools else None
|
|
1084
|
+
hooks = self._build_session_hooks(all_tools, opts)
|
|
946
1085
|
|
|
947
1086
|
return await self._client.resume_session(
|
|
948
1087
|
session_id,
|
|
@@ -954,6 +1093,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
954
1093
|
mcp_servers=mcp_servers or None,
|
|
955
1094
|
provider=provider or None,
|
|
956
1095
|
instruction_directories=instruction_directories,
|
|
1096
|
+
hooks=hooks,
|
|
957
1097
|
)
|
|
958
1098
|
|
|
959
1099
|
|
{agent_framework_github_copilot-1.0.0rc1 → agent_framework_github_copilot-1.0.0rc2}/pyproject.toml
RENAMED
|
@@ -4,7 +4,7 @@ description = "GitHub Copilot integration for Microsoft Agent Framework."
|
|
|
4
4
|
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.0rc2"
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
9
|
urls.homepage = "https://aka.ms/agent-framework"
|
|
10
10
|
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
|
@@ -23,8 +23,8 @@ classifiers = [
|
|
|
23
23
|
"Typing :: Typed",
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"agent-framework-core>=1.
|
|
27
|
-
"github-copilot-sdk
|
|
26
|
+
"agent-framework-core>=1.10.0,<2",
|
|
27
|
+
"github-copilot-sdk==1.0.2; python_version >= '3.11'",
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
[tool.uv]
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: agent-framework-github-copilot
|
|
3
|
-
Version: 1.0.0rc1
|
|
4
|
-
Summary: GitHub Copilot integration for Microsoft Agent Framework.
|
|
5
|
-
Author-email: Microsoft <af-support@microsoft.com>
|
|
6
|
-
Requires-Python: >=3.10
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
-
Classifier: Typing :: Typed
|
|
18
|
-
License-File: LICENSE
|
|
19
|
-
Requires-Dist: agent-framework-core>=1.8.0,<2
|
|
20
|
-
Requires-Dist: github-copilot-sdk>=1.0.0,<2; python_version >= '3.11'
|
|
21
|
-
Project-URL: homepage, https://aka.ms/agent-framework
|
|
22
|
-
Project-URL: issues, https://github.com/microsoft/agent-framework/issues
|
|
23
|
-
Project-URL: release_notes, https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true
|
|
24
|
-
Project-URL: source, https://github.com/microsoft/agent-framework/tree/main/python
|
|
25
|
-
|
|
26
|
-
# Get Started with Microsoft Agent Framework GitHub Copilot
|
|
27
|
-
|
|
28
|
-
Please install this package via pip:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
pip install agent-framework-github-copilot --pre
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## GitHub Copilot Agent
|
|
35
|
-
|
|
36
|
-
The GitHub Copilot agent enables integration with GitHub Copilot, allowing you to interact with Copilot's agentic capabilities through the Agent Framework.
|
|
37
|
-
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Get Started with Microsoft Agent Framework GitHub Copilot
|
|
2
|
-
|
|
3
|
-
Please install this package via pip:
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
pip install agent-framework-github-copilot --pre
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## GitHub Copilot Agent
|
|
10
|
-
|
|
11
|
-
The GitHub Copilot agent enables integration with GitHub Copilot, allowing you to interact with Copilot's agentic capabilities through the Agent Framework.
|
|
File without changes
|