agent-framework-github-copilot 1.0.0rc4__tar.gz → 1.0.2__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.
@@ -1,22 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-framework-github-copilot
3
- Version: 1.0.0rc4
3
+ Version: 1.0.2
4
4
  Summary: GitHub Copilot integration for Microsoft Agent Framework.
5
5
  Author-email: Microsoft <af-support@microsoft.com>
6
- Requires-Python: >=3.10
6
+ Requires-Python: >=3.11
7
7
  Description-Content-Type: text/markdown
8
8
  Classifier: License :: OSI Approved :: MIT License
9
- Classifier: Development Status :: 4 - Beta
9
+ Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
12
  Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: 3.13
16
15
  Classifier: Programming Language :: Python :: 3.14
17
16
  Classifier: Typing :: Typed
18
17
  License-File: LICENSE
19
- Requires-Dist: agent-framework-core>=1.11.0,<2
18
+ Requires-Dist: agent-framework-core>=1.13.0,<2
20
19
  Requires-Dist: github-copilot-sdk==1.0.2; python_version >= '3.11'
21
20
  Project-URL: homepage, https://aka.ms/agent-framework
22
21
  Project-URL: issues, https://github.com/microsoft/agent-framework/issues
@@ -28,7 +27,7 @@ Project-URL: source, https://github.com/microsoft/agent-framework/tree/main/pyth
28
27
  Please install this package via pip:
29
28
 
30
29
  ```bash
31
- pip install agent-framework-github-copilot --pre
30
+ pip install agent-framework-github-copilot
32
31
  ```
33
32
 
34
33
  ## GitHub Copilot Agent
@@ -75,6 +74,34 @@ agent = GitHubCopilotAgent(
75
74
  > Note: with the default (deny-all) permission handler, an `always_require` tool is denied
76
75
  > unless you wire an approving `on_permission_request`.
77
76
 
77
+ ### Approving for the rest of the session
78
+
79
+ `PermissionDecisionApproveForSession` scopes its approval with either an `approval` (tool
80
+ prompts) or a `domain` (URL prompts). Both are optional, so a bare
81
+ `PermissionDecisionApproveForSession()` carries no scope at all and the Copilot CLI cannot
82
+ interpret it.
83
+
84
+ `GitHubCopilotAgent` therefore scopes such a decision automatically, using the request that
85
+ triggered it — a shell prompt becomes an approval for that prompt's command identifiers, an
86
+ MCP prompt an approval for that server and tool, a URL prompt an approval for that URL's
87
+ domain, and so on:
88
+
89
+ ```python
90
+ from copilot.generated.rpc import PermissionDecisionApproveForSession
91
+
92
+
93
+ def on_permission_request(request, invocation):
94
+ # Scoped to `request` automatically; approves that kind of call for the whole session.
95
+ return PermissionDecisionApproveForSession()
96
+ ```
97
+
98
+ The decision is only ever narrowed, never widened. When the prompt reports that it cannot
99
+ offer session-scoped approval (`can_offer_session_approval=False`), or the request kind has
100
+ no session-scoped approval at all (such as a `hook` prompt), the decision is downgraded to a
101
+ single-use approval and a warning is logged. Pass an explicit `approval=` or `domain=` when
102
+ you want to approve something other than the request being handled — decisions that already
103
+ specify a scope are forwarded unchanged.
104
+
78
105
  ### Deprecated: `on_function_approval`
79
106
 
80
107
  The `on_function_approval` callback is **deprecated**. It still works (and is still enforced
@@ -3,7 +3,7 @@
3
3
  Please install this package via pip:
4
4
 
5
5
  ```bash
6
- pip install agent-framework-github-copilot --pre
6
+ pip install agent-framework-github-copilot
7
7
  ```
8
8
 
9
9
  ## GitHub Copilot Agent
@@ -50,6 +50,34 @@ agent = GitHubCopilotAgent(
50
50
  > Note: with the default (deny-all) permission handler, an `always_require` tool is denied
51
51
  > unless you wire an approving `on_permission_request`.
52
52
 
53
+ ### Approving for the rest of the session
54
+
55
+ `PermissionDecisionApproveForSession` scopes its approval with either an `approval` (tool
56
+ prompts) or a `domain` (URL prompts). Both are optional, so a bare
57
+ `PermissionDecisionApproveForSession()` carries no scope at all and the Copilot CLI cannot
58
+ interpret it.
59
+
60
+ `GitHubCopilotAgent` therefore scopes such a decision automatically, using the request that
61
+ triggered it — a shell prompt becomes an approval for that prompt's command identifiers, an
62
+ MCP prompt an approval for that server and tool, a URL prompt an approval for that URL's
63
+ domain, and so on:
64
+
65
+ ```python
66
+ from copilot.generated.rpc import PermissionDecisionApproveForSession
67
+
68
+
69
+ def on_permission_request(request, invocation):
70
+ # Scoped to `request` automatically; approves that kind of call for the whole session.
71
+ return PermissionDecisionApproveForSession()
72
+ ```
73
+
74
+ The decision is only ever narrowed, never widened. When the prompt reports that it cannot
75
+ offer session-scoped approval (`can_offer_session_approval=False`), or the request kind has
76
+ no session-scoped approval at all (such as a `hook` prompt), the decision is downgraded to a
77
+ single-use approval and a warning is logged. Pass an explicit `approval=` or `domain=` when
78
+ you want to approve something other than the request being handled — decisions that already
79
+ specify a scope are forwarded unchanged.
80
+
53
81
  ### Deprecated: `on_function_approval`
54
82
 
55
83
  The `on_function_approval` callback is **deprecated**. It still works (and is still enforced
@@ -10,6 +10,7 @@ import sys
10
10
  import warnings
11
11
  from collections.abc import AsyncIterable, Awaitable, Callable, Mapping, MutableMapping, Sequence
12
12
  from typing import Any, ClassVar, Generic, Literal, TypedDict, cast, overload
13
+ from urllib.parse import urlparse
13
14
 
14
15
  from agent_framework import (
15
16
  AgentMiddlewareLayer,
@@ -29,11 +30,18 @@ from agent_framework import (
29
30
  normalize_messages,
30
31
  )
31
32
  from agent_framework._settings import load_settings
33
+ from agent_framework._telemetry import mark_feature_used
32
34
  from agent_framework._tools import FunctionTool, ToolTypes
33
- from agent_framework._types import AgentRunInputs, normalize_tools
34
- from agent_framework.exceptions import AgentException
35
+ from agent_framework._types import (
36
+ AgentRunInputs,
37
+ _get_data_bytes_as_str, # pyright: ignore[reportPrivateUsage]
38
+ normalize_tools,
39
+ )
40
+ from agent_framework.exceptions import AgentException, ContentError
35
41
  from agent_framework.observability import AgentTelemetryLayer
36
42
 
43
+ from ._feature_usage import FeatureIndex
44
+
37
45
  if sys.version_info >= (3, 11):
38
46
  from typing import Self # pragma: no cover
39
47
  else:
@@ -45,8 +53,23 @@ else:
45
53
 
46
54
  try:
47
55
  from copilot import CopilotClient, CopilotSession, RuntimeConnection
48
- from copilot.generated.rpc import PermissionDecisionUserNotAvailable
56
+ from copilot.generated.rpc import (
57
+ PermissionDecisionApproveForSession,
58
+ PermissionDecisionApproveForSessionApproval,
59
+ PermissionDecisionApproveForSessionApprovalCommands,
60
+ PermissionDecisionApproveForSessionApprovalCustomTool,
61
+ PermissionDecisionApproveForSessionApprovalExtensionManagement,
62
+ PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess,
63
+ PermissionDecisionApproveForSessionApprovalMCP,
64
+ PermissionDecisionApproveForSessionApprovalMemory,
65
+ PermissionDecisionApproveForSessionApprovalRead,
66
+ PermissionDecisionApproveForSessionApprovalWrite,
67
+ PermissionDecisionApproveOnce,
68
+ PermissionDecisionUserNotAvailable,
69
+ )
49
70
  from copilot.session import (
71
+ Attachment,
72
+ BlobAttachment,
50
73
  MCPServerConfig,
51
74
  PermissionRequestResult,
52
75
  PreToolUseHandler,
@@ -55,7 +78,21 @@ try:
55
78
  SessionHooks,
56
79
  SystemMessageConfig,
57
80
  )
58
- from copilot.session_events import AssistantUsageData, PermissionRequest, SessionEvent, SessionEventType
81
+ from copilot.session_events import (
82
+ AssistantUsageData,
83
+ PermissionRequest,
84
+ PermissionRequestCustomTool,
85
+ PermissionRequestExtensionManagement,
86
+ PermissionRequestExtensionPermissionAccess,
87
+ PermissionRequestMcp,
88
+ PermissionRequestMemory,
89
+ PermissionRequestRead,
90
+ PermissionRequestShell,
91
+ PermissionRequestUrl,
92
+ PermissionRequestWrite,
93
+ SessionEvent,
94
+ SessionEventType,
95
+ )
59
96
  from copilot.tools import Tool as CopilotTool
60
97
  from copilot.tools import ToolInvocation, ToolResult
61
98
  except ImportError as _copilot_import_error:
@@ -72,6 +109,9 @@ PermissionHandlerType = Callable[
72
109
  ]
73
110
  """Type for permission request handlers. Supports both sync and async callbacks."""
74
111
 
112
+ AsyncPermissionHandlerType = Callable[[PermissionRequest, dict[str, str]], "Awaitable[PermissionRequestResult]"]
113
+ """Type for permission request handlers that are always asynchronous."""
114
+
75
115
 
76
116
  FunctionApprovalCallback = Callable[[Content], "bool | Awaitable[bool]"]
77
117
  """Deprecated approval callback for ``FunctionTool`` instances declared with
@@ -131,6 +171,173 @@ def _deny_all_permissions(
131
171
  return PermissionDecisionUserNotAvailable()
132
172
 
133
173
 
174
+ def _derive_session_approval(request: PermissionRequest) -> PermissionDecisionApproveForSessionApproval | None:
175
+ """Build the session-scoped approval implied by ``request``.
176
+
177
+ ``PermissionDecisionApproveForSession.approval`` describes *what* is being approved for
178
+ the remainder of the session. Its shape is dictated by the prompt that triggered it, so
179
+ it can be reconstructed from the request itself.
180
+
181
+ Args:
182
+ request: The permission request the decision is responding to.
183
+
184
+ Returns:
185
+ The approval covering ``request``, or ``None`` for request kinds that have no
186
+ session-scoped approval representation (such as ``hook`` prompts).
187
+ """
188
+ if isinstance(request, PermissionRequestShell):
189
+ return PermissionDecisionApproveForSessionApprovalCommands(
190
+ command_identifiers=[command.identifier for command in request.commands]
191
+ )
192
+ if isinstance(request, PermissionRequestRead):
193
+ return PermissionDecisionApproveForSessionApprovalRead()
194
+ if isinstance(request, PermissionRequestWrite):
195
+ return PermissionDecisionApproveForSessionApprovalWrite()
196
+ if isinstance(request, PermissionRequestMcp):
197
+ return PermissionDecisionApproveForSessionApprovalMCP(
198
+ server_name=request.server_name, tool_name=request.tool_name
199
+ )
200
+ if isinstance(request, PermissionRequestCustomTool):
201
+ return PermissionDecisionApproveForSessionApprovalCustomTool(tool_name=request.tool_name)
202
+ if isinstance(request, PermissionRequestMemory):
203
+ return PermissionDecisionApproveForSessionApprovalMemory()
204
+ if isinstance(request, PermissionRequestExtensionManagement):
205
+ return PermissionDecisionApproveForSessionApprovalExtensionManagement(operation=request.operation)
206
+ if isinstance(request, PermissionRequestExtensionPermissionAccess):
207
+ return PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess(
208
+ extension_name=request.extension_name
209
+ )
210
+ return None
211
+
212
+
213
+ # Characters the WHATWG URL parser (used by the Copilot CLI) treats specially for
214
+ # special-scheme URLs in ways that can move the authority boundary: backslashes are
215
+ # normalized to forward slashes, and tabs/newlines/carriage returns are stripped before
216
+ # parsing. Python's ``urlparse`` does none of this, so a URL containing any of them may
217
+ # resolve to a different host than the CLI actually contacts.
218
+ _WHATWG_AMBIGUOUS_URL_CHARS = ("\\", "\t", "\n", "\r")
219
+
220
+
221
+ def _derive_url_session_domain(url: str) -> str | None:
222
+ """Return the domain to persist for a URL prompt, or ``None`` when it is unsafe to.
223
+
224
+ The persisted domain must match the host the Copilot CLI actually contacts, but the CLI
225
+ parses URLs with WHATWG semantics while this runs on Python's ``urlparse``. The two
226
+ disagree on crafted authorities -- a backslash before the ``@`` in
227
+ ``https://example.com<backslash>@evil.com`` resolves to ``example.com`` under the CLI
228
+ but ``evil.com`` under ``urlparse`` -- so trusting ``urlparse`` here could persist a
229
+ session-wide approval for an unrelated, attacker-chosen domain.
230
+
231
+ To keep the "narrow, never widen" guarantee, the domain is only returned when the URL
232
+ contains none of the characters the two parsers handle differently; any ambiguity (or a
233
+ URL with no derivable host) yields ``None`` so the caller can approve the single request
234
+ without persisting a domain.
235
+
236
+ Args:
237
+ url: The URL from the permission request.
238
+
239
+ Returns:
240
+ The lower-cased host to approve for the session, or ``None`` when the URL is
241
+ parser-ambiguous or has no host.
242
+ """
243
+ if any(char in url for char in _WHATWG_AMBIGUOUS_URL_CHARS):
244
+ return None
245
+ return urlparse(url).hostname or None
246
+
247
+
248
+ def _normalize_permission_decision(
249
+ decision: PermissionRequestResult,
250
+ request: PermissionRequest,
251
+ ) -> PermissionRequestResult:
252
+ """Fill in the missing scope of an under-specified ``approve-for-session`` decision.
253
+
254
+ ``PermissionDecisionApproveForSession`` carries an optional ``approval`` (tool prompts)
255
+ and an optional ``domain`` (URL prompts), so ``PermissionDecisionApproveForSession()``
256
+ is constructible with neither. That serializes to ``{"kind": "approve-for-session"}``,
257
+ which the Copilot CLI cannot interpret -- it crashes with ``Cannot read properties of
258
+ undefined (reading 'commandIdentifiers')``, taking the whole run down with it. This
259
+ reconstructs the intended scope from ``request``.
260
+
261
+ The decision is only ever narrowed, never widened: when the prompt does not offer
262
+ session-scoped approval, or the request kind has no session approval representation,
263
+ the decision is downgraded to a single-use approval.
264
+
265
+ Args:
266
+ decision: The decision returned by the caller's permission handler.
267
+ request: The permission request the decision is responding to.
268
+
269
+ Returns:
270
+ ``decision`` unchanged unless it is an ``approve-for-session`` decision missing both
271
+ ``approval`` and ``domain``, in which case an equivalent fully-scoped decision (or a
272
+ narrower single-use approval) is returned. The input is never mutated.
273
+ """
274
+ if not isinstance(decision, PermissionDecisionApproveForSession):
275
+ return decision
276
+ if decision.approval is not None or decision.domain is not None:
277
+ return decision
278
+
279
+ try:
280
+ if isinstance(request, PermissionRequestUrl):
281
+ domain = _derive_url_session_domain(request.url)
282
+ if domain:
283
+ return PermissionDecisionApproveForSession(domain=domain)
284
+ logger.warning(
285
+ "Permission handler returned an unscoped 'approve-for-session' decision for a URL prompt, "
286
+ "but no unambiguous domain could be derived from '%s'. Approving this request only. Return "
287
+ "PermissionDecisionApproveForSession(domain=...) to approve a domain for the session.",
288
+ request.url,
289
+ )
290
+ return PermissionDecisionApproveOnce()
291
+
292
+ # Only shell and write prompts advertise this; other kinds always allow session approval.
293
+ if not getattr(request, "can_offer_session_approval", True):
294
+ logger.warning(
295
+ "Permission handler returned an 'approve-for-session' decision for a '%s' prompt that does not "
296
+ "offer session-scoped approval. Approving this request only.",
297
+ request.kind,
298
+ )
299
+ return PermissionDecisionApproveOnce()
300
+
301
+ approval = _derive_session_approval(request)
302
+ except Exception:
303
+ logger.exception(
304
+ "Failed to derive the session approval for a '%s' permission prompt. Approving this request only.",
305
+ getattr(request, "kind", "unknown"),
306
+ )
307
+ return PermissionDecisionApproveOnce()
308
+
309
+ if approval is None:
310
+ logger.warning(
311
+ "Permission handler returned an unscoped 'approve-for-session' decision for a '%s' prompt, which has "
312
+ "no session-scoped approval. Approving this request only.",
313
+ request.kind,
314
+ )
315
+ return PermissionDecisionApproveOnce()
316
+ return PermissionDecisionApproveForSession(approval=approval)
317
+
318
+
319
+ def _with_normalized_permission_decisions(handler: PermissionHandlerType) -> AsyncPermissionHandlerType:
320
+ """Wrap a permission handler so its decisions are normalized before reaching the SDK.
321
+
322
+ Exceptions raised by ``handler`` deliberately propagate: the SDK already catches them
323
+ and denies the request, and preserving that keeps the secure-by-default behavior.
324
+
325
+ Args:
326
+ handler: The caller-supplied permission handler. May be sync or async.
327
+
328
+ Returns:
329
+ An async handler delegating to ``handler`` and normalizing its result.
330
+ """
331
+
332
+ async def normalized_handler(request: PermissionRequest, invocation: dict[str, str]) -> PermissionRequestResult:
333
+ result = handler(request, invocation)
334
+ if inspect.isawaitable(result):
335
+ result = await result
336
+ return _normalize_permission_decision(result, request)
337
+
338
+ return normalized_handler
339
+
340
+
134
341
  class GitHubCopilotSettings(TypedDict, total=False):
135
342
  """GitHub Copilot model settings.
136
343
 
@@ -656,10 +863,12 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
656
863
  prompt = "\n".join([message.text for message in context_messages])
657
864
  if session_context.instructions:
658
865
  prompt = "\n".join(session_context.instructions) + "\n" + prompt
866
+ attachments = self._prepare_attachments_for_copilot(context_messages)
659
867
 
660
868
  unsubscribe = copilot_session.on(usage_event_handler)
661
869
  try:
662
- response_event = await copilot_session.send_and_wait(prompt, timeout=timeout)
870
+ mark_feature_used(FeatureIndex.GITHUB_COPILOT)
871
+ response_event = await copilot_session.send_and_wait(prompt, attachments=attachments, timeout=timeout)
663
872
  except Exception as ex:
664
873
  raise AgentException(f"GitHub Copilot request failed: {ex}") from ex
665
874
  finally:
@@ -762,6 +971,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
762
971
  prompt = "\n".join([message.text for message in context_messages])
763
972
  if session_context.instructions:
764
973
  prompt = "\n".join(session_context.instructions) + "\n" + prompt
974
+ attachments = self._prepare_attachments_for_copilot(context_messages)
765
975
 
766
976
  queue: asyncio.Queue[AgentResponseUpdate | Exception | None] = asyncio.Queue()
767
977
 
@@ -844,7 +1054,8 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
844
1054
  unsubscribe = copilot_session.on(event_handler)
845
1055
 
846
1056
  try:
847
- await copilot_session.send(prompt)
1057
+ mark_feature_used(FeatureIndex.GITHUB_COPILOT)
1058
+ await copilot_session.send(prompt, attachments=attachments)
848
1059
 
849
1060
  while (item := await queue.get()) is not None:
850
1061
  if isinstance(item, Exception):
@@ -916,6 +1127,56 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
916
1127
  elif opts_system_message is not None:
917
1128
  opts["system_message"] = opts_system_message
918
1129
 
1130
+ @staticmethod
1131
+ def _prepare_attachments_for_copilot(messages: Sequence[Message]) -> list[Attachment] | None:
1132
+ """Convert inline binary message content into Copilot SDK attachments.
1133
+
1134
+ Scans the outgoing messages for ``data`` content (binary payloads such as
1135
+ images or documents carried as base64 data URIs) and maps each one to an
1136
+ inline ``blob`` attachment understood by the Copilot SDK.
1137
+
1138
+ Only base64 ``data:`` content is forwarded as an attachment. Other content
1139
+ is not turned into an attachment: text content is already carried in the
1140
+ prompt, while remote URIs (for example ``https://`` links) and malformed or
1141
+ non-base64 ``data:`` URIs are skipped -- they are neither attached nor added
1142
+ to the prompt.
1143
+
1144
+ Args:
1145
+ messages: The messages being sent to the Copilot session.
1146
+
1147
+ Returns:
1148
+ A list of Copilot ``Attachment`` objects, or ``None`` when the messages
1149
+ contain no attachable binary content.
1150
+ """
1151
+ attachments: list[Attachment] = []
1152
+ for message in messages:
1153
+ for content in message.contents:
1154
+ if content.type != "data":
1155
+ continue
1156
+ try:
1157
+ data_str = _get_data_bytes_as_str(content)
1158
+ except ContentError:
1159
+ logger.warning(
1160
+ "Skipping GitHub Copilot attachment with an unsupported data URI; "
1161
+ "only base64-encoded 'data:' URIs can be forwarded as attachments."
1162
+ )
1163
+ continue
1164
+ if not data_str:
1165
+ continue
1166
+ if not content.media_type:
1167
+ logger.warning(
1168
+ "Dropping GitHub Copilot attachment with no media type; the Copilot SDK "
1169
+ "requires a MIME type for inline binary content."
1170
+ )
1171
+ continue
1172
+ blob: BlobAttachment = {
1173
+ "type": "blob",
1174
+ "data": data_str,
1175
+ "mimeType": content.media_type,
1176
+ }
1177
+ attachments.append(blob)
1178
+ return attachments or None
1179
+
919
1180
  def _prepare_tools(
920
1181
  self,
921
1182
  tools: Sequence[ToolTypes | CopilotTool],
@@ -1138,9 +1399,10 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1138
1399
  the Copilot SDK, so any ``create_session`` parameter is supported without a
1139
1400
  dedicated mapping here (an unknown name surfaces as a ``TypeError`` from the
1140
1401
  SDK). A few keys are handled specially because they need a secure default
1141
- (``on_permission_request`` defaults to denying all requests) or transforming:
1142
- ``tools`` are merged with the agent's tools and converted to SDK tools, and
1143
- approval callbacks are turned into ``hooks``.
1402
+ (``on_permission_request`` defaults to denying all requests, and is wrapped so
1403
+ under-specified ``approve-for-session`` decisions are scoped to the request that
1404
+ triggered them) or transforming: ``tools`` are merged with the agent's tools and
1405
+ converted to SDK tools, and approval callbacks are turned into ``hooks``.
1144
1406
 
1145
1407
  Args:
1146
1408
  streaming: Whether to enable streaming for the session.
@@ -1164,7 +1426,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1164
1426
  # back to the resolved setting (which carries the default_options / env model).
1165
1427
  if not kwargs.get("model"):
1166
1428
  kwargs["model"] = self._settings.get("model") or None
1167
- kwargs["on_permission_request"] = (
1429
+ kwargs["on_permission_request"] = _with_normalized_permission_decisions(
1168
1430
  opts.get("on_permission_request") or self._permission_handler or _deny_all_permissions
1169
1431
  )
1170
1432
  kwargs["hooks"] = self._build_session_hooks(all_tools, kwargs)
@@ -0,0 +1,9 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+
3
+ from enum import IntEnum
4
+
5
+
6
+ class FeatureIndex(IntEnum):
7
+ """GitHub Copilot-owned feature-usage indexes."""
8
+
9
+ GITHUB_COPILOT = 64
@@ -3,8 +3,8 @@ name = "agent-framework-github-copilot"
3
3
  description = "GitHub Copilot integration for Microsoft Agent Framework."
4
4
  authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
5
5
  readme = "README.md"
6
- requires-python = ">=3.10"
7
- version = "1.0.0rc4"
6
+ requires-python = ">=3.11"
7
+ version = "1.0.2"
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"
@@ -12,10 +12,9 @@ urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=ta
12
12
  urls.issues = "https://github.com/microsoft/agent-framework/issues"
13
13
  classifiers = [
14
14
  "License :: OSI Approved :: MIT License",
15
- "Development Status :: 4 - Beta",
15
+ "Development Status :: 5 - Production/Stable",
16
16
  "Intended Audience :: Developers",
17
17
  "Programming Language :: Python :: 3",
18
- "Programming Language :: Python :: 3.10",
19
18
  "Programming Language :: Python :: 3.11",
20
19
  "Programming Language :: Python :: 3.12",
21
20
  "Programming Language :: Python :: 3.13",
@@ -23,7 +22,7 @@ classifiers = [
23
22
  "Typing :: Typed",
24
23
  ]
25
24
  dependencies = [
26
- "agent-framework-core>=1.11.0,<2",
25
+ "agent-framework-core>=1.13.0,<2",
27
26
  "github-copilot-sdk==1.0.2; python_version >= '3.11'",
28
27
  ]
29
28