agent-framework-github-copilot 1.0.3__tar.gz → 2.0.1__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.3 → agent_framework_github_copilot-2.0.1}/PKG-INFO +31 -2
- {agent_framework_github_copilot-1.0.3 → agent_framework_github_copilot-2.0.1}/README.md +29 -0
- {agent_framework_github_copilot-1.0.3 → agent_framework_github_copilot-2.0.1}/agent_framework_github_copilot/_agent.py +354 -50
- agent_framework_github_copilot-2.0.1/agent_framework_github_copilot/py.typed +0 -0
- {agent_framework_github_copilot-1.0.3 → agent_framework_github_copilot-2.0.1}/pyproject.toml +3 -3
- {agent_framework_github_copilot-1.0.3 → agent_framework_github_copilot-2.0.1}/LICENSE +0 -0
- {agent_framework_github_copilot-1.0.3 → agent_framework_github_copilot-2.0.1}/agent_framework_github_copilot/__init__.py +0 -0
- {agent_framework_github_copilot-1.0.3 → agent_framework_github_copilot-2.0.1}/agent_framework_github_copilot/_feature_usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-framework-github-copilot
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: GitHub Copilot integration for Microsoft Agent Framework.
|
|
5
5
|
Author-email: Microsoft <af-support@microsoft.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
16
16
|
Classifier: Typing :: Typed
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: agent-framework-core>=1.15.0,<2
|
|
19
|
-
Requires-Dist: github-copilot-sdk==1.0.
|
|
19
|
+
Requires-Dist: github-copilot-sdk==1.0.11; python_version >= '3.11'
|
|
20
20
|
Project-URL: homepage, https://aka.ms/agent-framework
|
|
21
21
|
Project-URL: issues, https://github.com/microsoft/agent-framework/issues
|
|
22
22
|
Project-URL: release_notes, https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true
|
|
@@ -111,4 +111,33 @@ model described above. When `on_function_approval` is set, it gates `always_requ
|
|
|
111
111
|
the default ask-hook is not installed. It is **mutually exclusive** with `on_pre_tool_use` —
|
|
112
112
|
setting both (whether at construction or per run) raises `ValueError`.
|
|
113
113
|
|
|
114
|
+
## Workspace-driven session options
|
|
115
|
+
|
|
116
|
+
`on_permission_request` and `on_pre_tool_use` gate **tool calls**. They do not cover
|
|
117
|
+
configuration the CLI picks up from the working directory it runs in, which is a separate
|
|
118
|
+
mechanism with its own switches.
|
|
119
|
+
|
|
120
|
+
So that a session behaves the same way in every checkout, `GitHubCopilotAgent` leaves the
|
|
121
|
+
following off by default:
|
|
122
|
+
|
|
123
|
+
| Option | Default | Effect when enabled |
|
|
124
|
+
| --- | --- | --- |
|
|
125
|
+
| `enable_file_hooks` | `False` | The CLI loads file hooks from the working directory's `.github/hooks/` and runs the commands they define, independently of the tool-approval path. |
|
|
126
|
+
|
|
127
|
+
Opt in per agent or per run when your workflow needs the checkout to drive the session:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
agent = GitHubCopilotAgent(
|
|
131
|
+
default_options=GitHubCopilotOptions(enable_file_hooks=True),
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Only enable these for a working directory whose contents you trust to act on the host.
|
|
136
|
+
|
|
137
|
+
To make the default visible rather than silent, the agent logs a warning through the
|
|
138
|
+
`agent_framework.github_copilot` logger the first time it starts a session in a working
|
|
139
|
+
directory that defines hooks it is not loading. See
|
|
140
|
+
[`github_copilot_with_file_hooks.py`](../../samples/02-agents/providers/github_copilot/github_copilot_with_file_hooks.py)
|
|
141
|
+
for a runnable example.
|
|
142
|
+
|
|
114
143
|
|
|
@@ -87,3 +87,32 @@ model described above. When `on_function_approval` is set, it gates `always_requ
|
|
|
87
87
|
the default ask-hook is not installed. It is **mutually exclusive** with `on_pre_tool_use` —
|
|
88
88
|
setting both (whether at construction or per run) raises `ValueError`.
|
|
89
89
|
|
|
90
|
+
## Workspace-driven session options
|
|
91
|
+
|
|
92
|
+
`on_permission_request` and `on_pre_tool_use` gate **tool calls**. They do not cover
|
|
93
|
+
configuration the CLI picks up from the working directory it runs in, which is a separate
|
|
94
|
+
mechanism with its own switches.
|
|
95
|
+
|
|
96
|
+
So that a session behaves the same way in every checkout, `GitHubCopilotAgent` leaves the
|
|
97
|
+
following off by default:
|
|
98
|
+
|
|
99
|
+
| Option | Default | Effect when enabled |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `enable_file_hooks` | `False` | The CLI loads file hooks from the working directory's `.github/hooks/` and runs the commands they define, independently of the tool-approval path. |
|
|
102
|
+
|
|
103
|
+
Opt in per agent or per run when your workflow needs the checkout to drive the session:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
agent = GitHubCopilotAgent(
|
|
107
|
+
default_options=GitHubCopilotOptions(enable_file_hooks=True),
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Only enable these for a working directory whose contents you trust to act on the host.
|
|
112
|
+
|
|
113
|
+
To make the default visible rather than silent, the agent logs a warning through the
|
|
114
|
+
`agent_framework.github_copilot` logger the first time it starts a session in a working
|
|
115
|
+
directory that defines hooks it is not loading. See
|
|
116
|
+
[`github_copilot_with_file_hooks.py`](../../samples/02-agents/providers/github_copilot/github_copilot_with_file_hooks.py)
|
|
117
|
+
for a runnable example.
|
|
118
|
+
|
|
@@ -9,7 +9,9 @@ import json
|
|
|
9
9
|
import logging
|
|
10
10
|
import sys
|
|
11
11
|
import warnings
|
|
12
|
-
|
|
12
|
+
import weakref
|
|
13
|
+
from collections.abc import AsyncGenerator, AsyncIterable, Awaitable, Callable, Mapping, MutableMapping, Sequence
|
|
14
|
+
from pathlib import Path
|
|
13
15
|
from typing import Any, ClassVar, Generic, Literal, TypedDict, cast, overload
|
|
14
16
|
from urllib.parse import urlparse
|
|
15
17
|
|
|
@@ -22,6 +24,7 @@ from agent_framework import (
|
|
|
22
24
|
BaseAgent,
|
|
23
25
|
Content,
|
|
24
26
|
ContextProvider,
|
|
27
|
+
FunctionInvocationContext,
|
|
25
28
|
HistoryProvider,
|
|
26
29
|
Message,
|
|
27
30
|
ResponseStream,
|
|
@@ -30,6 +33,7 @@ from agent_framework import (
|
|
|
30
33
|
add_usage_details,
|
|
31
34
|
normalize_messages,
|
|
32
35
|
)
|
|
36
|
+
from agent_framework._mcp import MCPTool
|
|
33
37
|
from agent_framework._settings import load_settings
|
|
34
38
|
from agent_framework._telemetry import mark_feature_used
|
|
35
39
|
from agent_framework._tools import FunctionTool, ToolTypes
|
|
@@ -77,6 +81,7 @@ try:
|
|
|
77
81
|
Attachment,
|
|
78
82
|
BlobAttachment,
|
|
79
83
|
MCPServerConfig,
|
|
84
|
+
PermissionInvocation,
|
|
80
85
|
PermissionRequestResult,
|
|
81
86
|
PreToolUseHandler,
|
|
82
87
|
PreToolUseHookOutput,
|
|
@@ -110,14 +115,24 @@ except ImportError as _copilot_import_error:
|
|
|
110
115
|
DEFAULT_TIMEOUT_SECONDS: float = 60.0
|
|
111
116
|
"""Default timeout in seconds for Copilot requests."""
|
|
112
117
|
|
|
118
|
+
_PermissionHandlerContext = Any
|
|
119
|
+
"""Compatibility context accepted by permission handlers across SDK versions."""
|
|
120
|
+
|
|
113
121
|
PermissionHandlerType = Callable[
|
|
114
|
-
[PermissionRequest,
|
|
122
|
+
[PermissionRequest, _PermissionHandlerContext],
|
|
123
|
+
"PermissionRequestResult | Awaitable[PermissionRequestResult]",
|
|
115
124
|
]
|
|
116
125
|
"""Type for permission request handlers. Supports both sync and async callbacks."""
|
|
117
126
|
|
|
118
|
-
AsyncPermissionHandlerType = Callable[
|
|
127
|
+
AsyncPermissionHandlerType = Callable[
|
|
128
|
+
[PermissionRequest, _PermissionHandlerContext], "Awaitable[PermissionRequestResult]"
|
|
129
|
+
]
|
|
119
130
|
"""Type for permission request handlers that are always asynchronous."""
|
|
120
131
|
|
|
132
|
+
_SdkAsyncPermissionHandlerType = Callable[
|
|
133
|
+
[PermissionRequest, PermissionInvocation], "Awaitable[PermissionRequestResult]"
|
|
134
|
+
]
|
|
135
|
+
|
|
121
136
|
|
|
122
137
|
FunctionApprovalCallback = Callable[[Content], "bool | Awaitable[bool]"]
|
|
123
138
|
"""Deprecated approval callback for ``FunctionTool`` instances declared with
|
|
@@ -168,10 +183,30 @@ async def _resolve_function_approval(
|
|
|
168
183
|
|
|
169
184
|
logger = logging.getLogger("agent_framework.github_copilot")
|
|
170
185
|
|
|
186
|
+
# One lock per (CopilotClient, service session ID), shared across agents that share a client.
|
|
187
|
+
# Entries disappear once no run holds or awaits the lock.
|
|
188
|
+
_SESSION_RUN_LOCKS: weakref.WeakValueDictionary[tuple[int, str], asyncio.Lock] = weakref.WeakValueDictionary()
|
|
189
|
+
|
|
190
|
+
_MCP_TOOL_MESSAGE = (
|
|
191
|
+
"MCP server '{name}' cannot be passed to GitHubCopilotAgent as a tool: the Copilot SDK "
|
|
192
|
+
"connects to MCP servers itself, so a framework-managed MCPTool would keep none of its "
|
|
193
|
+
"framework behavior. Configure the server natively instead, for example "
|
|
194
|
+
"default_options={{'mcp_servers': {{'{name}': {{'type': 'stdio', 'command': 'python', "
|
|
195
|
+
"'args': ['server.py'], 'tools': ['*']}}}}}}, or use a ChatAgent, where the framework owns "
|
|
196
|
+
"the connection."
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _reject_mcp_tools(tools: Sequence[Any]) -> None:
|
|
201
|
+
"""Refuse MCP servers handed in as tools, from whichever option carried them."""
|
|
202
|
+
for tool in tools:
|
|
203
|
+
if isinstance(tool, MCPTool):
|
|
204
|
+
raise TypeError(_MCP_TOOL_MESSAGE.format(name=tool.name))
|
|
205
|
+
|
|
171
206
|
|
|
172
207
|
def _deny_all_permissions(
|
|
173
208
|
_request: PermissionRequest,
|
|
174
|
-
_invocation:
|
|
209
|
+
_invocation: _PermissionHandlerContext,
|
|
175
210
|
) -> PermissionRequestResult:
|
|
176
211
|
"""Default permission handler that denies all requests."""
|
|
177
212
|
return PermissionDecisionUserNotAvailable()
|
|
@@ -322,7 +357,7 @@ def _normalize_permission_decision(
|
|
|
322
357
|
return PermissionDecisionApproveForSession(approval=approval)
|
|
323
358
|
|
|
324
359
|
|
|
325
|
-
def _with_normalized_permission_decisions(handler: PermissionHandlerType) ->
|
|
360
|
+
def _with_normalized_permission_decisions(handler: PermissionHandlerType) -> _SdkAsyncPermissionHandlerType:
|
|
326
361
|
"""Wrap a permission handler so its decisions are normalized before reaching the SDK.
|
|
327
362
|
|
|
328
363
|
Exceptions raised by ``handler`` deliberately propagate: the SDK already catches them
|
|
@@ -335,8 +370,10 @@ def _with_normalized_permission_decisions(handler: PermissionHandlerType) -> Asy
|
|
|
335
370
|
An async handler delegating to ``handler`` and normalizing its result.
|
|
336
371
|
"""
|
|
337
372
|
|
|
338
|
-
async def normalized_handler(
|
|
339
|
-
|
|
373
|
+
async def normalized_handler(
|
|
374
|
+
request: PermissionRequest, invocation: PermissionInvocation
|
|
375
|
+
) -> PermissionRequestResult:
|
|
376
|
+
result = handler(request, cast(PermissionInvocation, dict(invocation)))
|
|
340
377
|
if inspect.isawaitable(result):
|
|
341
378
|
result = await result
|
|
342
379
|
return _normalize_permission_decision(result, request)
|
|
@@ -364,6 +401,45 @@ def _parse_telemetry_config(raw: str) -> TelemetryConfig | None:
|
|
|
364
401
|
return cast(TelemetryConfig, parsed)
|
|
365
402
|
|
|
366
403
|
|
|
404
|
+
def _client_working_directory(client: CopilotClient | None) -> str | None:
|
|
405
|
+
"""Best-effort read of the working directory an injected client configured.
|
|
406
|
+
|
|
407
|
+
A client created with ``CopilotClient(working_directory=...)`` spawns the CLI process
|
|
408
|
+
in that directory, so the CLI resolves ``.github/hooks/`` relative to it rather than to
|
|
409
|
+
this process. The SDK keeps the value on a private options object, so this is read
|
|
410
|
+
defensively: if the attribute ever moves, hook detection falls back to the process
|
|
411
|
+
working directory instead of failing.
|
|
412
|
+
"""
|
|
413
|
+
options = getattr(client, "_options", None)
|
|
414
|
+
working_directory = getattr(options, "working_directory", None)
|
|
415
|
+
return working_directory if isinstance(working_directory, str) else None
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _resolve_effective_working_directory(session_working_directory: str | None, client: CopilotClient | None) -> Path:
|
|
419
|
+
"""Resolve the directory the CLI will treat as the workspace, mirroring the SDK.
|
|
420
|
+
|
|
421
|
+
The SDK resolves in this order: the session's ``working_directory`` when supplied,
|
|
422
|
+
otherwise the CLI process's own directory, which is the injected client's configured
|
|
423
|
+
``working_directory`` when it set one and this process's directory otherwise.
|
|
424
|
+
"""
|
|
425
|
+
if session_working_directory:
|
|
426
|
+
return Path(session_working_directory)
|
|
427
|
+
client_working_directory = _client_working_directory(client)
|
|
428
|
+
if client_working_directory:
|
|
429
|
+
return Path(client_working_directory)
|
|
430
|
+
return Path.cwd()
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def _has_file_hooks(working_directory: Path) -> bool:
|
|
434
|
+
"""Return whether ``.github/hooks/`` holds hook definitions the CLI would otherwise load."""
|
|
435
|
+
try:
|
|
436
|
+
return any((working_directory / ".github" / "hooks").iterdir())
|
|
437
|
+
except OSError:
|
|
438
|
+
# Missing directory, unreadable path, or a file where the directory would be:
|
|
439
|
+
# there is nothing the caller needs to know about.
|
|
440
|
+
return False
|
|
441
|
+
|
|
442
|
+
|
|
367
443
|
class GitHubCopilotSettings(TypedDict, total=False):
|
|
368
444
|
"""GitHub Copilot model settings.
|
|
369
445
|
|
|
@@ -468,6 +544,14 @@ class GitHubCopilotOptions(TypedDict, total=False):
|
|
|
468
544
|
base_directory: str
|
|
469
545
|
"""Directory where the CLI stores session state, configuration, and other persistent data."""
|
|
470
546
|
|
|
547
|
+
enable_file_hooks: bool
|
|
548
|
+
"""Whether the CLI loads file hooks from the working directory's ``.github/hooks/``.
|
|
549
|
+
|
|
550
|
+
Defaults to ``False``: hook definitions checked into the working directory are ignored
|
|
551
|
+
unless you opt in, so a session behaves the same way regardless of which checkout it
|
|
552
|
+
runs in. Unrelated to the SDK callback hooks configured through ``on_pre_tool_use``.
|
|
553
|
+
"""
|
|
554
|
+
|
|
471
555
|
telemetry: TelemetryConfig
|
|
472
556
|
"""OpenTelemetry configuration for the Copilot CLI process."""
|
|
473
557
|
|
|
@@ -641,6 +725,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
641
725
|
)
|
|
642
726
|
|
|
643
727
|
self._tools = normalize_tools(tools)
|
|
728
|
+
_reject_mcp_tools(self._tools)
|
|
644
729
|
self._permission_handler = on_permission_request
|
|
645
730
|
self._on_pre_tool_use: PreToolUseHandler | None = on_pre_tool_use
|
|
646
731
|
self._function_approval_handler: FunctionApprovalCallback | None = on_function_approval
|
|
@@ -649,6 +734,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
649
734
|
# are forwarded verbatim to the Copilot SDK by _build_session_kwargs.
|
|
650
735
|
self._default_options = opts
|
|
651
736
|
self._started = False
|
|
737
|
+
self._file_hooks_warning_emitted = False
|
|
652
738
|
|
|
653
739
|
async def __aenter__(self) -> Self:
|
|
654
740
|
"""Start the agent when entering async context."""
|
|
@@ -733,6 +819,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
733
819
|
session: AgentSession | None = None,
|
|
734
820
|
middleware: Sequence[AgentMiddlewareTypes] | None = None,
|
|
735
821
|
options: OptionsT | None = None,
|
|
822
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
736
823
|
**kwargs: Any,
|
|
737
824
|
) -> Awaitable[AgentResponse]: ...
|
|
738
825
|
|
|
@@ -745,6 +832,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
745
832
|
session: AgentSession | None = None,
|
|
746
833
|
middleware: Sequence[AgentMiddlewareTypes] | None = None,
|
|
747
834
|
options: OptionsT | None = None,
|
|
835
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
748
836
|
**kwargs: Any,
|
|
749
837
|
) -> ResponseStream[AgentResponseUpdate, AgentResponse]: ...
|
|
750
838
|
|
|
@@ -756,6 +844,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
756
844
|
session: AgentSession | None = None,
|
|
757
845
|
middleware: Sequence[AgentMiddlewareTypes] | None = None,
|
|
758
846
|
options: OptionsT | None = None,
|
|
847
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
759
848
|
**kwargs: Any,
|
|
760
849
|
) -> Awaitable[AgentResponse] | ResponseStream[AgentResponseUpdate, AgentResponse]:
|
|
761
850
|
"""Get a response from the agent.
|
|
@@ -774,6 +863,9 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
774
863
|
compatibility; pass middleware via :class:`GitHubCopilotAgent` which
|
|
775
864
|
forwards it through :class:`AgentTelemetryLayer`.
|
|
776
865
|
options: Runtime options (model, timeout, etc.).
|
|
866
|
+
function_invocation_kwargs: Keyword arguments forwarded to tool invocations.
|
|
867
|
+
Tools receive them through ``FunctionInvocationContext.kwargs`` when they
|
|
868
|
+
declare a ``FunctionInvocationContext`` parameter.
|
|
777
869
|
kwargs: Additional keyword arguments for compatibility with the shared agent
|
|
778
870
|
interface (e.g. compaction_strategy, tokenizer). Not used by this agent.
|
|
779
871
|
|
|
@@ -807,11 +899,22 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
807
899
|
return AgentResponse.from_updates(updates)
|
|
808
900
|
|
|
809
901
|
return ResponseStream(
|
|
810
|
-
self._stream_updates(
|
|
902
|
+
self._stream_updates(
|
|
903
|
+
messages=messages,
|
|
904
|
+
session=session,
|
|
905
|
+
options=options,
|
|
906
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
907
|
+
_ctx_holder=ctx_holder,
|
|
908
|
+
),
|
|
811
909
|
finalizer=_finalize,
|
|
812
910
|
result_hooks=[_after_run_hook],
|
|
813
911
|
)
|
|
814
|
-
return self._run_impl(
|
|
912
|
+
return self._run_impl(
|
|
913
|
+
messages=messages,
|
|
914
|
+
session=session,
|
|
915
|
+
options=options,
|
|
916
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
917
|
+
)
|
|
815
918
|
|
|
816
919
|
@staticmethod
|
|
817
920
|
def _parse_usage_details_from_copilot(data: AssistantUsageData) -> UsageDetails | None:
|
|
@@ -840,6 +943,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
840
943
|
*,
|
|
841
944
|
session: AgentSession | None = None,
|
|
842
945
|
options: OptionsT | None = None,
|
|
946
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
843
947
|
) -> AgentResponse:
|
|
844
948
|
"""Non-streaming implementation of run."""
|
|
845
949
|
if not self._started:
|
|
@@ -872,7 +976,6 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
872
976
|
existing = list(opts.get("tools") or [])
|
|
873
977
|
opts["tools"] = existing + list(session_context.tools)
|
|
874
978
|
|
|
875
|
-
copilot_session = await self._get_or_create_session(session, streaming=False, runtime_options=opts)
|
|
876
979
|
usage_details: UsageDetails | None = None
|
|
877
980
|
finish_reason: str | None = None
|
|
878
981
|
model: str | None = None
|
|
@@ -906,14 +1009,20 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
906
1009
|
prompt = "\n".join(session_context.instructions) + "\n" + prompt
|
|
907
1010
|
attachments = self._prepare_attachments_for_copilot(context_messages)
|
|
908
1011
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1012
|
+
async with self._session_run_scope(
|
|
1013
|
+
session,
|
|
1014
|
+
streaming=False,
|
|
1015
|
+
runtime_options=opts,
|
|
1016
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1017
|
+
) as copilot_session:
|
|
1018
|
+
unsubscribe = copilot_session.on(usage_event_handler)
|
|
1019
|
+
try:
|
|
1020
|
+
mark_feature_used(FeatureIndex.GITHUB_COPILOT)
|
|
1021
|
+
response_event = await copilot_session.send_and_wait(prompt, attachments=attachments, timeout=timeout)
|
|
1022
|
+
except Exception as ex:
|
|
1023
|
+
raise AgentException(f"GitHub Copilot request failed: {ex}") from ex
|
|
1024
|
+
finally:
|
|
1025
|
+
unsubscribe()
|
|
917
1026
|
|
|
918
1027
|
response_messages: list[Message] = []
|
|
919
1028
|
response_id: str | None = None
|
|
@@ -952,6 +1061,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
952
1061
|
*,
|
|
953
1062
|
session: AgentSession | None = None,
|
|
954
1063
|
options: OptionsT | None = None,
|
|
1064
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
955
1065
|
_ctx_holder: dict[str, Any] | None = None,
|
|
956
1066
|
) -> AsyncIterable[AgentResponseUpdate]:
|
|
957
1067
|
"""Internal method to stream updates from GitHub Copilot.
|
|
@@ -962,6 +1072,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
962
1072
|
Keyword Args:
|
|
963
1073
|
session: The conversation session associated with the message(s).
|
|
964
1074
|
options: Runtime options (model, timeout, etc.).
|
|
1075
|
+
function_invocation_kwargs: Keyword arguments forwarded to tool invocations.
|
|
965
1076
|
_ctx_holder: Internal dict populated with session_context and session
|
|
966
1077
|
so that the caller (via a ResponseStream result_hook) can run
|
|
967
1078
|
after_run providers without duplicating the updates buffer.
|
|
@@ -1001,12 +1112,6 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1001
1112
|
existing = list(opts.get("tools") or [])
|
|
1002
1113
|
opts["tools"] = existing + list(session_context.tools)
|
|
1003
1114
|
|
|
1004
|
-
copilot_session = await self._get_or_create_session(session, streaming=True, runtime_options=opts)
|
|
1005
|
-
|
|
1006
|
-
if _ctx_holder is not None:
|
|
1007
|
-
_ctx_holder["session_context"] = session_context
|
|
1008
|
-
_ctx_holder["session"] = session
|
|
1009
|
-
|
|
1010
1115
|
# Build the prompt from the full session context so provider-injected messages are included.
|
|
1011
1116
|
context_messages = session_context.get_messages(include_input=True)
|
|
1012
1117
|
prompt = "\n".join([message.text for message in context_messages])
|
|
@@ -1092,18 +1197,28 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1092
1197
|
error_msg = error_data.message or "Unknown error"
|
|
1093
1198
|
queue.put_nowait(AgentException(f"GitHub Copilot session error: {error_msg}"))
|
|
1094
1199
|
|
|
1095
|
-
|
|
1200
|
+
async with self._session_run_scope(
|
|
1201
|
+
session,
|
|
1202
|
+
streaming=True,
|
|
1203
|
+
runtime_options=opts,
|
|
1204
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1205
|
+
) as copilot_session:
|
|
1206
|
+
if _ctx_holder is not None:
|
|
1207
|
+
_ctx_holder["session_context"] = session_context
|
|
1208
|
+
_ctx_holder["session"] = session
|
|
1096
1209
|
|
|
1097
|
-
|
|
1098
|
-
mark_feature_used(FeatureIndex.GITHUB_COPILOT)
|
|
1099
|
-
await copilot_session.send(prompt, attachments=attachments)
|
|
1210
|
+
unsubscribe = copilot_session.on(event_handler)
|
|
1100
1211
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1212
|
+
try:
|
|
1213
|
+
mark_feature_used(FeatureIndex.GITHUB_COPILOT)
|
|
1214
|
+
await copilot_session.send(prompt, attachments=attachments)
|
|
1215
|
+
|
|
1216
|
+
while (item := await queue.get()) is not None:
|
|
1217
|
+
if isinstance(item, Exception):
|
|
1218
|
+
raise item
|
|
1219
|
+
yield item
|
|
1220
|
+
finally:
|
|
1221
|
+
unsubscribe()
|
|
1107
1222
|
|
|
1108
1223
|
async def _run_before_providers(
|
|
1109
1224
|
self,
|
|
@@ -1221,12 +1336,20 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1221
1336
|
def _prepare_tools(
|
|
1222
1337
|
self,
|
|
1223
1338
|
tools: Sequence[ToolTypes | CopilotTool],
|
|
1339
|
+
*,
|
|
1340
|
+
agent_session: AgentSession | None = None,
|
|
1341
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
1224
1342
|
) -> list[CopilotTool]:
|
|
1225
1343
|
"""Convert Agent Framework tools to Copilot SDK tools.
|
|
1226
1344
|
|
|
1227
1345
|
Args:
|
|
1228
1346
|
tools: List of Agent Framework tools.
|
|
1229
1347
|
|
|
1348
|
+
Keyword Args:
|
|
1349
|
+
agent_session: The conversation session, exposed to tools through their
|
|
1350
|
+
``FunctionInvocationContext``.
|
|
1351
|
+
function_invocation_kwargs: Keyword arguments forwarded to tool invocations.
|
|
1352
|
+
|
|
1230
1353
|
Returns:
|
|
1231
1354
|
List of Copilot SDK tools.
|
|
1232
1355
|
"""
|
|
@@ -1236,16 +1359,33 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1236
1359
|
if isinstance(tool, CopilotTool):
|
|
1237
1360
|
copilot_tools.append(tool)
|
|
1238
1361
|
elif isinstance(tool, FunctionTool):
|
|
1239
|
-
copilot_tools.append(
|
|
1362
|
+
copilot_tools.append(
|
|
1363
|
+
self._tool_to_copilot_tool(
|
|
1364
|
+
tool,
|
|
1365
|
+
agent_session=agent_session,
|
|
1366
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1367
|
+
)
|
|
1368
|
+
)
|
|
1240
1369
|
elif isinstance(tool, MutableMapping):
|
|
1241
1370
|
copilot_tools.append(tool) # type: ignore[arg-type]
|
|
1242
1371
|
# Note: Other tool types (e.g., dict-based hosted tools) are skipped
|
|
1243
1372
|
|
|
1244
1373
|
return copilot_tools
|
|
1245
1374
|
|
|
1246
|
-
def _tool_to_copilot_tool(
|
|
1375
|
+
def _tool_to_copilot_tool(
|
|
1376
|
+
self,
|
|
1377
|
+
ai_func: FunctionTool,
|
|
1378
|
+
*,
|
|
1379
|
+
agent_session: AgentSession | None = None,
|
|
1380
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
1381
|
+
) -> CopilotTool:
|
|
1247
1382
|
"""Convert an FunctionTool to a Copilot SDK tool.
|
|
1248
1383
|
|
|
1384
|
+
Each invocation receives a ``FunctionInvocationContext`` carrying the run's
|
|
1385
|
+
``function_invocation_kwargs`` and ``agent_session``, so tools that declare a
|
|
1386
|
+
``FunctionInvocationContext`` parameter can read them the same way they do
|
|
1387
|
+
with a chat-client-backed agent.
|
|
1388
|
+
|
|
1249
1389
|
Approval for tools declared with ``approval_mode="always_require"`` is normally
|
|
1250
1390
|
enforced by the Copilot SDK's native ``on_pre_tool_use`` hook (see
|
|
1251
1391
|
:meth:`_build_session_hooks`). When the deprecated ``on_function_approval``
|
|
@@ -1255,6 +1395,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1255
1395
|
"""
|
|
1256
1396
|
approval_handler = self._function_approval_handler
|
|
1257
1397
|
enforce = approval_handler is not None and ai_func.approval_mode == "always_require"
|
|
1398
|
+
runtime_kwargs = dict(function_invocation_kwargs) if function_invocation_kwargs is not None else {}
|
|
1258
1399
|
|
|
1259
1400
|
async def handler(invocation: ToolInvocation) -> ToolResult:
|
|
1260
1401
|
args: dict[str, Any] = invocation.arguments or {}
|
|
@@ -1273,11 +1414,21 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1273
1414
|
result_type="failure",
|
|
1274
1415
|
error="approval_denied",
|
|
1275
1416
|
)
|
|
1417
|
+
context = FunctionInvocationContext(
|
|
1418
|
+
function=ai_func,
|
|
1419
|
+
arguments=args,
|
|
1420
|
+
session=agent_session,
|
|
1421
|
+
kwargs=runtime_kwargs,
|
|
1422
|
+
)
|
|
1276
1423
|
if ai_func.input_model:
|
|
1277
1424
|
args_instance = ai_func.input_model(**args)
|
|
1278
|
-
result = await ai_func.invoke(
|
|
1425
|
+
result = await ai_func.invoke(
|
|
1426
|
+
arguments=args_instance, context=context, tool_call_id=invocation.tool_call_id or None
|
|
1427
|
+
)
|
|
1279
1428
|
else:
|
|
1280
|
-
result = await ai_func.invoke(
|
|
1429
|
+
result = await ai_func.invoke(
|
|
1430
|
+
arguments=args, context=context, tool_call_id=invocation.tool_call_id or None
|
|
1431
|
+
)
|
|
1281
1432
|
rich = [c for c in result if c.type in ("data", "uri")]
|
|
1282
1433
|
if rich:
|
|
1283
1434
|
logger.warning(
|
|
@@ -1391,11 +1542,73 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1391
1542
|
|
|
1392
1543
|
return {"on_pre_tool_use": default_pre_tool_use}
|
|
1393
1544
|
|
|
1545
|
+
def _session_run_lock(self, service_session_id: str) -> asyncio.Lock:
|
|
1546
|
+
"""Return the lock that serializes runs on one Copilot service session."""
|
|
1547
|
+
key = (id(self._client), service_session_id)
|
|
1548
|
+
lock = _SESSION_RUN_LOCKS.get(key)
|
|
1549
|
+
if lock is None:
|
|
1550
|
+
lock = asyncio.Lock()
|
|
1551
|
+
_SESSION_RUN_LOCKS[key] = lock
|
|
1552
|
+
return lock
|
|
1553
|
+
|
|
1554
|
+
@contextlib.asynccontextmanager
|
|
1555
|
+
async def _session_run_scope(
|
|
1556
|
+
self,
|
|
1557
|
+
agent_session: AgentSession,
|
|
1558
|
+
*,
|
|
1559
|
+
streaming: bool,
|
|
1560
|
+
runtime_options: dict[str, Any],
|
|
1561
|
+
function_invocation_kwargs: Mapping[str, Any] | None,
|
|
1562
|
+
) -> AsyncGenerator[CopilotSession]:
|
|
1563
|
+
"""Open the Copilot session for one run and hold it exclusively until the run ends.
|
|
1564
|
+
|
|
1565
|
+
The Copilot SDK keeps a single session object per service session ID and
|
|
1566
|
+
re-registers tools on it on every resume, dispatching tool calls by session ID.
|
|
1567
|
+
Tool handlers capture the run's ``function_invocation_kwargs`` and session, so
|
|
1568
|
+
an overlapping resume would let one run's tool calls execute with another run's
|
|
1569
|
+
context. Runs on the same service session are therefore serialized.
|
|
1570
|
+
|
|
1571
|
+
Args:
|
|
1572
|
+
agent_session: The conversation session.
|
|
1573
|
+
|
|
1574
|
+
Keyword Args:
|
|
1575
|
+
streaming: Whether to enable streaming for the session.
|
|
1576
|
+
runtime_options: Runtime options from run that take precedence.
|
|
1577
|
+
function_invocation_kwargs: Keyword arguments forwarded to tool invocations.
|
|
1578
|
+
|
|
1579
|
+
Yields:
|
|
1580
|
+
The created or resumed CopilotSession.
|
|
1581
|
+
"""
|
|
1582
|
+
lock: asyncio.Lock | None = None
|
|
1583
|
+
service_session_id = agent_session.service_session_id
|
|
1584
|
+
if isinstance(service_session_id, str):
|
|
1585
|
+
resume_lock = self._session_run_lock(service_session_id)
|
|
1586
|
+
await resume_lock.acquire()
|
|
1587
|
+
lock = resume_lock
|
|
1588
|
+
try:
|
|
1589
|
+
copilot_session = await self._get_or_create_session(
|
|
1590
|
+
agent_session,
|
|
1591
|
+
streaming=streaming,
|
|
1592
|
+
runtime_options=runtime_options,
|
|
1593
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1594
|
+
)
|
|
1595
|
+
if lock is None:
|
|
1596
|
+
# A newly created service session ID is not known to any other run yet,
|
|
1597
|
+
# so this acquire never waits.
|
|
1598
|
+
create_lock = self._session_run_lock(copilot_session.session_id)
|
|
1599
|
+
await create_lock.acquire()
|
|
1600
|
+
lock = create_lock
|
|
1601
|
+
yield copilot_session
|
|
1602
|
+
finally:
|
|
1603
|
+
if lock is not None:
|
|
1604
|
+
lock.release()
|
|
1605
|
+
|
|
1394
1606
|
async def _get_or_create_session(
|
|
1395
1607
|
self,
|
|
1396
1608
|
agent_session: AgentSession,
|
|
1397
1609
|
streaming: bool = False,
|
|
1398
1610
|
runtime_options: dict[str, Any] | None = None,
|
|
1611
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
1399
1612
|
) -> CopilotSession:
|
|
1400
1613
|
"""Get an existing session or create a new one for the session.
|
|
1401
1614
|
|
|
@@ -1403,6 +1616,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1403
1616
|
agent_session: The conversation session.
|
|
1404
1617
|
streaming: Whether to enable streaming for the session.
|
|
1405
1618
|
runtime_options: Runtime options from run that take precedence.
|
|
1619
|
+
function_invocation_kwargs: Keyword arguments forwarded to tool invocations.
|
|
1406
1620
|
|
|
1407
1621
|
Returns:
|
|
1408
1622
|
A CopilotSession instance.
|
|
@@ -1420,18 +1634,53 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1420
1634
|
raise AgentException(
|
|
1421
1635
|
"GitHubCopilotAgent expects a string service_session_id for session resumption."
|
|
1422
1636
|
)
|
|
1423
|
-
return await self._resume_session(
|
|
1637
|
+
return await self._resume_session(
|
|
1638
|
+
service_session_id,
|
|
1639
|
+
streaming,
|
|
1640
|
+
runtime_options,
|
|
1641
|
+
agent_session=agent_session,
|
|
1642
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1643
|
+
)
|
|
1424
1644
|
|
|
1425
|
-
session = await self._create_session(
|
|
1645
|
+
session = await self._create_session(
|
|
1646
|
+
streaming,
|
|
1647
|
+
runtime_options,
|
|
1648
|
+
agent_session=agent_session,
|
|
1649
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1650
|
+
)
|
|
1426
1651
|
agent_session.service_session_id = session.session_id
|
|
1427
1652
|
return session
|
|
1428
1653
|
except Exception as ex:
|
|
1429
1654
|
raise AgentException(f"Failed to create GitHub Copilot session: {ex}") from ex
|
|
1430
1655
|
|
|
1656
|
+
def _warn_once_about_unloaded_file_hooks(self, session_working_directory: str | None) -> None:
|
|
1657
|
+
"""Warn once when the effective working directory defines file hooks that will not run.
|
|
1658
|
+
|
|
1659
|
+
Without this the default is silent: hooks simply stop running, with nothing to
|
|
1660
|
+
point at the cause. The warning fires at most once per agent so a long-lived agent
|
|
1661
|
+
does not repeat it on every run.
|
|
1662
|
+
"""
|
|
1663
|
+
if self._file_hooks_warning_emitted:
|
|
1664
|
+
return
|
|
1665
|
+
working_directory = _resolve_effective_working_directory(session_working_directory, self._client)
|
|
1666
|
+
if not _has_file_hooks(working_directory):
|
|
1667
|
+
return
|
|
1668
|
+
self._file_hooks_warning_emitted = True
|
|
1669
|
+
logger.warning(
|
|
1670
|
+
"Not loading the file hooks defined in '%s': GitHubCopilotAgent leaves "
|
|
1671
|
+
"enable_file_hooks off so a session behaves the same way in every working "
|
|
1672
|
+
"directory. Set enable_file_hooks=True in default_options (or in per-run "
|
|
1673
|
+
"options) to run them.",
|
|
1674
|
+
working_directory / ".github" / "hooks",
|
|
1675
|
+
)
|
|
1676
|
+
|
|
1431
1677
|
def _build_session_kwargs(
|
|
1432
1678
|
self,
|
|
1433
1679
|
streaming: bool,
|
|
1434
1680
|
runtime_options: dict[str, Any] | None,
|
|
1681
|
+
*,
|
|
1682
|
+
agent_session: AgentSession | None = None,
|
|
1683
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
1435
1684
|
) -> dict[str, Any]:
|
|
1436
1685
|
"""Assemble keyword arguments for ``create_session`` / ``resume_session``.
|
|
1437
1686
|
|
|
@@ -1439,16 +1688,22 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1439
1688
|
``runtime_options`` which override them. Every key is forwarded verbatim to
|
|
1440
1689
|
the Copilot SDK, so any ``create_session`` parameter is supported without a
|
|
1441
1690
|
dedicated mapping here (an unknown name surfaces as a ``TypeError`` from the
|
|
1442
|
-
SDK). A few keys are handled specially because they need a
|
|
1443
|
-
|
|
1444
|
-
under-specified ``approve-for-session`` decisions are scoped to the
|
|
1445
|
-
triggered them
|
|
1446
|
-
converted to SDK tools, and approval callbacks
|
|
1691
|
+
SDK). A few keys are handled specially because they need a specific default or
|
|
1692
|
+
transforming: ``on_permission_request`` defaults to denying all requests and is
|
|
1693
|
+
wrapped so under-specified ``approve-for-session`` decisions are scoped to the
|
|
1694
|
+
request that triggered them, ``enable_file_hooks`` defaults to off, ``tools`` are
|
|
1695
|
+
merged with the agent's tools and converted to SDK tools, and approval callbacks
|
|
1696
|
+
are turned into ``hooks``.
|
|
1447
1697
|
|
|
1448
1698
|
Args:
|
|
1449
1699
|
streaming: Whether to enable streaming for the session.
|
|
1450
1700
|
runtime_options: Runtime options that take precedence over default_options.
|
|
1451
1701
|
|
|
1702
|
+
Keyword Args:
|
|
1703
|
+
agent_session: The conversation session, exposed to tools through their
|
|
1704
|
+
``FunctionInvocationContext``.
|
|
1705
|
+
function_invocation_kwargs: Keyword arguments forwarded to tool invocations.
|
|
1706
|
+
|
|
1452
1707
|
Returns:
|
|
1453
1708
|
The keyword arguments to splat into the SDK session factory.
|
|
1454
1709
|
"""
|
|
@@ -1459,8 +1714,19 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1459
1714
|
|
|
1460
1715
|
# Merge agent-level tools with any caller-supplied tools (from default_options
|
|
1461
1716
|
# or per-run options, the latter winning) and convert to SDK tools.
|
|
1462
|
-
|
|
1463
|
-
|
|
1717
|
+
# Normalize the option-supplied tools the way the constructor does: it converts callables
|
|
1718
|
+
# and flattens tool-collection wrappers, which can otherwise hide an MCPTool.
|
|
1719
|
+
all_tools = normalize_tools(list(self._tools or []) + list(kwargs.get("tools") or []))
|
|
1720
|
+
_reject_mcp_tools(all_tools)
|
|
1721
|
+
kwargs["tools"] = (
|
|
1722
|
+
self._prepare_tools(
|
|
1723
|
+
all_tools,
|
|
1724
|
+
agent_session=agent_session,
|
|
1725
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1726
|
+
)
|
|
1727
|
+
if all_tools
|
|
1728
|
+
else None
|
|
1729
|
+
)
|
|
1464
1730
|
|
|
1465
1731
|
kwargs["streaming"] = streaming
|
|
1466
1732
|
# model may already be present from per-run options (merged above); otherwise fall
|
|
@@ -1468,8 +1734,17 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1468
1734
|
if not kwargs.get("model"):
|
|
1469
1735
|
kwargs["model"] = self._settings.get("model") or None
|
|
1470
1736
|
kwargs["on_permission_request"] = _with_normalized_permission_decisions(
|
|
1471
|
-
|
|
1737
|
+
cast(
|
|
1738
|
+
PermissionHandlerType,
|
|
1739
|
+
opts.get("on_permission_request") or self._permission_handler or _deny_all_permissions,
|
|
1740
|
+
)
|
|
1472
1741
|
)
|
|
1742
|
+
# File hooks let the working directory's checked-in configuration influence what the
|
|
1743
|
+
# CLI does on the host, so the agent leaves them off for a consistent session in every
|
|
1744
|
+
# checkout. Callers opt in through ``default_options`` or per-run options.
|
|
1745
|
+
if kwargs.get("enable_file_hooks") is None:
|
|
1746
|
+
kwargs["enable_file_hooks"] = False
|
|
1747
|
+
self._warn_once_about_unloaded_file_hooks(kwargs.get("working_directory"))
|
|
1473
1748
|
kwargs["hooks"] = self._build_session_hooks(all_tools, kwargs)
|
|
1474
1749
|
|
|
1475
1750
|
# Strip agent-internal and client-level keys that are consumed here or in the
|
|
@@ -1492,23 +1767,40 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1492
1767
|
self,
|
|
1493
1768
|
streaming: bool,
|
|
1494
1769
|
runtime_options: dict[str, Any] | None = None,
|
|
1770
|
+
*,
|
|
1771
|
+
agent_session: AgentSession | None = None,
|
|
1772
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
1495
1773
|
) -> CopilotSession:
|
|
1496
1774
|
"""Create a new Copilot session.
|
|
1497
1775
|
|
|
1498
1776
|
Args:
|
|
1499
1777
|
streaming: Whether to enable streaming for the session.
|
|
1500
1778
|
runtime_options: Runtime options that take precedence over default_options.
|
|
1779
|
+
|
|
1780
|
+
Keyword Args:
|
|
1781
|
+
agent_session: The conversation session, exposed to tools.
|
|
1782
|
+
function_invocation_kwargs: Keyword arguments forwarded to tool invocations.
|
|
1501
1783
|
"""
|
|
1502
1784
|
if not self._client:
|
|
1503
1785
|
raise RuntimeError("GitHub Copilot client not initialized. Call start() first.")
|
|
1504
1786
|
|
|
1505
|
-
return await self._client.create_session(
|
|
1787
|
+
return await self._client.create_session(
|
|
1788
|
+
**self._build_session_kwargs(
|
|
1789
|
+
streaming,
|
|
1790
|
+
runtime_options,
|
|
1791
|
+
agent_session=agent_session,
|
|
1792
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1793
|
+
)
|
|
1794
|
+
)
|
|
1506
1795
|
|
|
1507
1796
|
async def _resume_session(
|
|
1508
1797
|
self,
|
|
1509
1798
|
session_id: str,
|
|
1510
1799
|
streaming: bool,
|
|
1511
1800
|
runtime_options: dict[str, Any] | None = None,
|
|
1801
|
+
*,
|
|
1802
|
+
agent_session: AgentSession | None = None,
|
|
1803
|
+
function_invocation_kwargs: Mapping[str, Any] | None = None,
|
|
1512
1804
|
) -> CopilotSession:
|
|
1513
1805
|
"""Resume an existing Copilot session by ID.
|
|
1514
1806
|
|
|
@@ -1516,11 +1808,23 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
|
|
|
1516
1808
|
session_id: The session ID to resume.
|
|
1517
1809
|
streaming: Whether to enable streaming for the session.
|
|
1518
1810
|
runtime_options: Runtime options that take precedence over default_options.
|
|
1811
|
+
|
|
1812
|
+
Keyword Args:
|
|
1813
|
+
agent_session: The conversation session, exposed to tools.
|
|
1814
|
+
function_invocation_kwargs: Keyword arguments forwarded to tool invocations.
|
|
1519
1815
|
"""
|
|
1520
1816
|
if not self._client:
|
|
1521
1817
|
raise RuntimeError("GitHub Copilot client not initialized. Call start() first.")
|
|
1522
1818
|
|
|
1523
|
-
return await self._client.resume_session(
|
|
1819
|
+
return await self._client.resume_session(
|
|
1820
|
+
session_id,
|
|
1821
|
+
**self._build_session_kwargs(
|
|
1822
|
+
streaming,
|
|
1823
|
+
runtime_options,
|
|
1824
|
+
agent_session=agent_session,
|
|
1825
|
+
function_invocation_kwargs=function_invocation_kwargs,
|
|
1826
|
+
),
|
|
1827
|
+
)
|
|
1524
1828
|
|
|
1525
1829
|
|
|
1526
1830
|
class GitHubCopilotAgent( # type: ignore[misc]
|
|
File without changes
|
{agent_framework_github_copilot-1.0.3 → agent_framework_github_copilot-2.0.1}/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.11"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.1"
|
|
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,11 +23,11 @@ classifiers = [
|
|
|
23
23
|
]
|
|
24
24
|
dependencies = [
|
|
25
25
|
"agent-framework-core>=1.15.0,<2",
|
|
26
|
-
"github-copilot-sdk==1.0.
|
|
26
|
+
"github-copilot-sdk==1.0.11; python_version >= '3.11'",
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[tool.uv]
|
|
30
|
-
prerelease = "if-necessary
|
|
30
|
+
prerelease = "if-necessary"
|
|
31
31
|
environments = [
|
|
32
32
|
"sys_platform == 'darwin'",
|
|
33
33
|
"sys_platform == 'linux'",
|
|
File without changes
|
|
File without changes
|