agent-framework-github-copilot 1.0.0rc2__tar.gz → 1.0.0rc3__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-framework-github-copilot
3
- Version: 1.0.0rc2
3
+ Version: 1.0.0rc3
4
4
  Summary: GitHub Copilot integration for Microsoft Agent Framework.
5
5
  Author-email: Microsoft <af-support@microsoft.com>
6
6
  Requires-Python: >=3.10
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Programming Language :: Python :: 3.14
17
17
  Classifier: Typing :: Typed
18
18
  License-File: LICENSE
19
- Requires-Dist: agent-framework-core>=1.10.0,<2
19
+ Requires-Dist: agent-framework-core>=1.11.0,<2
20
20
  Requires-Dist: github-copilot-sdk==1.0.2; python_version >= '3.11'
21
21
  Project-URL: homepage, https://aka.ms/agent-framework
22
22
  Project-URL: issues, https://github.com/microsoft/agent-framework/issues
@@ -9,12 +9,7 @@ import logging
9
9
  import sys
10
10
  import warnings
11
11
  from collections.abc import AsyncIterable, Awaitable, Callable, Mapping, MutableMapping, Sequence
12
- from typing import Any, ClassVar, Generic, Literal, TypedDict, overload
13
-
14
- if sys.version_info >= (3, 11):
15
- from typing import Self # pragma: no cover
16
- else:
17
- from typing_extensions import Self # pragma: no cover
12
+ from typing import Any, ClassVar, Generic, Literal, TypedDict, cast, overload
18
13
 
19
14
  from agent_framework import (
20
15
  AgentMiddlewareLayer,
@@ -29,6 +24,8 @@ from agent_framework import (
29
24
  Message,
30
25
  ResponseStream,
31
26
  SessionContext,
27
+ UsageDetails,
28
+ add_usage_details,
32
29
  normalize_messages,
33
30
  )
34
31
  from agent_framework._settings import load_settings
@@ -37,6 +34,15 @@ from agent_framework._types import AgentRunInputs, normalize_tools
37
34
  from agent_framework.exceptions import AgentException
38
35
  from agent_framework.observability import AgentTelemetryLayer
39
36
 
37
+ if sys.version_info >= (3, 11):
38
+ from typing import Self # pragma: no cover
39
+ else:
40
+ from typing_extensions import Self # pragma: no cover
41
+ if sys.version_info >= (3, 13):
42
+ from typing import TypeVar # pragma: no cover
43
+ else:
44
+ from typing_extensions import TypeVar # pragma: no cover
45
+
40
46
  try:
41
47
  from copilot import CopilotClient, CopilotSession, RuntimeConnection
42
48
  from copilot.generated.rpc import PermissionDecisionUserNotAvailable
@@ -49,7 +55,7 @@ try:
49
55
  SessionHooks,
50
56
  SystemMessageConfig,
51
57
  )
52
- from copilot.session_events import PermissionRequest, SessionEvent, SessionEventType
58
+ from copilot.session_events import AssistantUsageData, PermissionRequest, SessionEvent, SessionEventType
53
59
  from copilot.tools import Tool as CopilotTool
54
60
  from copilot.tools import ToolInvocation, ToolResult
55
61
  except ImportError as _copilot_import_error:
@@ -58,12 +64,6 @@ except ImportError as _copilot_import_error:
58
64
  "Please use Python 3.11 or later."
59
65
  ) from _copilot_import_error
60
66
 
61
- if sys.version_info >= (3, 13):
62
- from typing import TypeVar # pragma: no cover
63
- else:
64
- from typing_extensions import TypeVar # pragma: no cover
65
-
66
-
67
67
  DEFAULT_TIMEOUT_SECONDS: float = 60.0
68
68
  """Default timeout in seconds for Copilot requests."""
69
69
 
@@ -206,6 +206,18 @@ class GitHubCopilotOptions(TypedDict, total=False):
206
206
  files beyond the default locations.
207
207
  """
208
208
 
209
+ skill_directories: list[str]
210
+ """Directories containing SKILL.md files to load into the Copilot CLI session.
211
+ These are loaded natively by the Copilot CLI process, letting applications point
212
+ the CLI at project-specific or team-shared skills beyond the default locations.
213
+ """
214
+
215
+ disabled_skills: list[str]
216
+ """Names of skills to disable for the session.
217
+ Lets applications opt out of specific skills that would otherwise be discovered
218
+ from ``skill_directories`` or the default locations.
219
+ """
220
+
209
221
  base_directory: str
210
222
  """Directory where the CLI stores session state, configuration, and other persistent data."""
211
223
 
@@ -346,6 +358,8 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
346
358
  mcp_servers: dict[str, MCPServerConfig] | None = opts.pop("mcp_servers", None)
347
359
  provider: ProviderConfig | None = opts.pop("provider", None)
348
360
  instruction_directories: list[str] | None = opts.pop("instruction_directories", None)
361
+ skill_directories: list[str] | None = opts.pop("skill_directories", None)
362
+ disabled_skills: list[str] | None = opts.pop("disabled_skills", None)
349
363
  on_pre_tool_use: PreToolUseHandler | None = opts.pop("on_pre_tool_use", None)
350
364
  on_function_approval: FunctionApprovalCallback | None = opts.pop("on_function_approval", None)
351
365
  base_directory = opts.pop("base_directory", None)
@@ -386,6 +400,8 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
386
400
  self._mcp_servers = mcp_servers
387
401
  self._provider = provider
388
402
  self._instruction_directories = instruction_directories
403
+ self._skill_directories = skill_directories
404
+ self._disabled_skills = disabled_skills
389
405
  self._default_options = opts
390
406
  self._started = False
391
407
 
@@ -547,6 +563,27 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
547
563
  )
548
564
  return self._run_impl(messages=messages, session=session, options=options)
549
565
 
566
+ @staticmethod
567
+ def _parse_usage_details_from_copilot(data: AssistantUsageData) -> UsageDetails | None:
568
+ total_token_count = (
569
+ data.input_tokens + data.output_tokens
570
+ if data.input_tokens is not None and data.output_tokens is not None
571
+ else None
572
+ )
573
+ usage_details = UsageDetails(**{
574
+ key: value
575
+ for key, value in {
576
+ "input_token_count": data.input_tokens,
577
+ "output_token_count": data.output_tokens,
578
+ "total_token_count": total_token_count,
579
+ "cache_read_input_token_count": data.cache_read_tokens,
580
+ "cache_creation_input_token_count": data.cache_write_tokens,
581
+ "reasoning_output_token_count": data.reasoning_tokens,
582
+ }.items()
583
+ if value is not None
584
+ })
585
+ return usage_details or None
586
+
550
587
  async def _run_impl(
551
588
  self,
552
589
  messages: AgentRunInputs | None = None,
@@ -586,6 +623,27 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
586
623
  opts["tools"] = existing + list(session_context.tools)
587
624
 
588
625
  copilot_session = await self._get_or_create_session(session, streaming=False, runtime_options=opts)
626
+ usage_details: UsageDetails | None = None
627
+ finish_reason: str | None = None
628
+ model: str | None = None
629
+
630
+ def usage_event_handler(event: SessionEvent) -> None:
631
+ nonlocal usage_details, finish_reason, model
632
+ if event.type != SessionEventType.ASSISTANT_USAGE:
633
+ return
634
+ if isinstance(event.data, AssistantUsageData):
635
+ parsed_usage_details = self._parse_usage_details_from_copilot(event.data)
636
+ if parsed_usage_details:
637
+ usage_details = add_usage_details(usage_details, parsed_usage_details)
638
+ if event.data.finish_reason:
639
+ finish_reason = event.data.finish_reason
640
+ if event.data.model:
641
+ model = event.data.model
642
+ else:
643
+ logger.warning(
644
+ "Ignoring GitHub Copilot assistant usage event with unexpected payload type: %s",
645
+ type(event.data).__name__,
646
+ )
589
647
 
590
648
  # Build the prompt from the full set of messages in the session context,
591
649
  # so that any context/history provider-injected messages are included.
@@ -594,10 +652,13 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
594
652
  if session_context.instructions:
595
653
  prompt = "\n".join(session_context.instructions) + "\n" + prompt
596
654
 
655
+ unsubscribe = copilot_session.on(usage_event_handler)
597
656
  try:
598
657
  response_event = await copilot_session.send_and_wait(prompt, timeout=timeout)
599
658
  except Exception as ex:
600
659
  raise AgentException(f"GitHub Copilot request failed: {ex}") from ex
660
+ finally:
661
+ unsubscribe()
601
662
 
602
663
  response_messages: list[Message] = []
603
664
  response_id: str | None = None
@@ -619,7 +680,13 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
619
680
  )
620
681
  response_id = message_id
621
682
 
622
- response = AgentResponse(messages=response_messages, response_id=response_id)
683
+ response = AgentResponse(
684
+ messages=response_messages,
685
+ response_id=response_id,
686
+ finish_reason=cast(Any, finish_reason),
687
+ usage_details=usage_details,
688
+ additional_properties={"model": model} if model else None,
689
+ )
623
690
  session_context._response = response # type: ignore[assignment]
624
691
  await self._run_after_providers(session=session, context=session_context)
625
692
  return response
@@ -705,6 +772,26 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
705
772
  raw_representation=event,
706
773
  )
707
774
  queue.put_nowait(update)
775
+ elif event.type == SessionEventType.ASSISTANT_USAGE:
776
+ if not isinstance(event.data, AssistantUsageData):
777
+ logger.warning(
778
+ "Ignoring GitHub Copilot assistant usage event with unexpected payload type: %s",
779
+ type(event.data).__name__,
780
+ )
781
+ return
782
+ usage_details = self._parse_usage_details_from_copilot(event.data)
783
+ finish_reason = event.data.finish_reason or None
784
+ model = event.data.model or None
785
+ if usage_details or finish_reason or model:
786
+ update = AgentResponseUpdate(
787
+ contents=[Content.from_usage(usage_details, raw_representation=event.data)]
788
+ if usage_details
789
+ else None,
790
+ finish_reason=cast(Any, finish_reason),
791
+ additional_properties={"model": model} if model else None,
792
+ raw_representation=event,
793
+ )
794
+ queue.put_nowait(update)
708
795
  elif event.type == SessionEventType.TOOL_EXECUTION_START:
709
796
  tool_call_id = getattr(event.data, "tool_call_id", None) or ""
710
797
  tool_name = getattr(event.data, "tool_name", None) or ""
@@ -1007,7 +1094,12 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1007
1094
 
1008
1095
  try:
1009
1096
  if agent_session.service_session_id:
1010
- return await self._resume_session(agent_session.service_session_id, streaming, runtime_options)
1097
+ service_session_id = agent_session.service_session_id
1098
+ if not isinstance(service_session_id, str):
1099
+ raise AgentException(
1100
+ "GitHubCopilotAgent expects a string service_session_id for session resumption."
1101
+ )
1102
+ return await self._resume_session(service_session_id, streaming, runtime_options)
1011
1103
 
1012
1104
  session = await self._create_session(streaming, runtime_options)
1013
1105
  agent_session.service_session_id = session.session_id
@@ -1038,6 +1130,8 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1038
1130
  mcp_servers = opts.get("mcp_servers") or self._mcp_servers or None
1039
1131
  provider = opts.get("provider") or self._provider or None
1040
1132
  instruction_directories = opts.get("instruction_directories", self._instruction_directories)
1133
+ skill_directories = opts.get("skill_directories", self._skill_directories)
1134
+ disabled_skills = opts.get("disabled_skills", self._disabled_skills)
1041
1135
  all_tools = list(self._tools or []) + list(opts.get("tools") or [])
1042
1136
  tools = self._prepare_tools(all_tools) if all_tools else None
1043
1137
  hooks = self._build_session_hooks(all_tools, opts)
@@ -1051,6 +1145,8 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1051
1145
  mcp_servers=mcp_servers or None,
1052
1146
  provider=provider or None,
1053
1147
  instruction_directories=instruction_directories,
1148
+ skill_directories=skill_directories,
1149
+ disabled_skills=disabled_skills,
1054
1150
  hooks=hooks,
1055
1151
  )
1056
1152
 
@@ -1079,6 +1175,8 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1079
1175
  mcp_servers = opts.get("mcp_servers") or self._mcp_servers or None
1080
1176
  provider = opts.get("provider") or self._provider or None
1081
1177
  instruction_directories = opts.get("instruction_directories", self._instruction_directories)
1178
+ skill_directories = opts.get("skill_directories", self._skill_directories)
1179
+ disabled_skills = opts.get("disabled_skills", self._disabled_skills)
1082
1180
  all_tools = list(self._tools or []) + list(opts.get("tools") or [])
1083
1181
  tools = self._prepare_tools(all_tools) if all_tools else None
1084
1182
  hooks = self._build_session_hooks(all_tools, opts)
@@ -1093,6 +1191,8 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1093
1191
  mcp_servers=mcp_servers or None,
1094
1192
  provider=provider or None,
1095
1193
  instruction_directories=instruction_directories,
1194
+ skill_directories=skill_directories,
1195
+ disabled_skills=disabled_skills,
1096
1196
  hooks=hooks,
1097
1197
  )
1098
1198
 
@@ -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.0rc2"
7
+ version = "1.0.0rc3"
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,7 +23,7 @@ classifiers = [
23
23
  "Typing :: Typed",
24
24
  ]
25
25
  dependencies = [
26
- "agent-framework-core>=1.10.0,<2",
26
+ "agent-framework-core>=1.11.0,<2",
27
27
  "github-copilot-sdk==1.0.2; python_version >= '3.11'",
28
28
  ]
29
29
 
@@ -102,4 +102,3 @@ interpreter = "posix"
102
102
  [build-system]
103
103
  requires = ["flit-core >= 3.11,<4.0"]
104
104
  build-backend = "flit_core.buildapi"
105
-