agent-framework-github-copilot 1.0.0rc3__tar.gz → 1.0.0rc4__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.0rc3
3
+ Version: 1.0.0rc4
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
@@ -162,7 +162,16 @@ class GitHubCopilotSettings(TypedDict, total=False):
162
162
 
163
163
 
164
164
  class GitHubCopilotOptions(TypedDict, total=False):
165
- """GitHub Copilot-specific options."""
165
+ """GitHub Copilot-specific options.
166
+
167
+ The keys below have first-class typing and inline documentation because they are
168
+ the commonly used options. They are **not** an exhaustive list: any other
169
+ parameter accepted by the Copilot SDK's ``create_session`` (for example
170
+ ``reasoning_effort``, ``context_tier``, ``enable_citations``, ``available_tools``,
171
+ ``memory``, ...) may also be supplied and is forwarded verbatim to the SDK. An
172
+ unrecognized parameter name surfaces as a ``TypeError`` from the SDK, so typos are
173
+ caught rather than silently ignored.
174
+ """
166
175
 
167
176
  system_message: SystemMessageConfig
168
177
  """System message configuration for the session. Use mode 'append' to add to the default
@@ -355,11 +364,6 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
355
364
  timeout = opts.pop("timeout", None)
356
365
  log_level = opts.pop("log_level", None)
357
366
  on_permission_request: PermissionHandlerType | None = opts.pop("on_permission_request", None)
358
- mcp_servers: dict[str, MCPServerConfig] | None = opts.pop("mcp_servers", None)
359
- provider: ProviderConfig | None = opts.pop("provider", None)
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)
363
367
  on_pre_tool_use: PreToolUseHandler | None = opts.pop("on_pre_tool_use", None)
364
368
  on_function_approval: FunctionApprovalCallback | None = opts.pop("on_function_approval", None)
365
369
  base_directory = opts.pop("base_directory", None)
@@ -397,11 +401,9 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
397
401
  self._permission_handler = on_permission_request
398
402
  self._on_pre_tool_use: PreToolUseHandler | None = on_pre_tool_use
399
403
  self._function_approval_handler: FunctionApprovalCallback | None = on_function_approval
400
- self._mcp_servers = mcp_servers
401
- self._provider = provider
402
- self._instruction_directories = instruction_directories
403
- self._skill_directories = skill_directories
404
- self._disabled_skills = disabled_skills
404
+ # Remaining options (e.g. mcp_servers, provider, instruction_directories,
405
+ # skill_directories, disabled_skills, and any other create_session parameter)
406
+ # are forwarded verbatim to the Copilot SDK by _build_session_kwargs.
405
407
  self._default_options = opts
406
408
  self._started = False
407
409
 
@@ -635,8 +637,11 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
635
637
  parsed_usage_details = self._parse_usage_details_from_copilot(event.data)
636
638
  if parsed_usage_details:
637
639
  usage_details = add_usage_details(usage_details, parsed_usage_details)
638
- if event.data.finish_reason:
639
- finish_reason = event.data.finish_reason
640
+ event_finish_reason = (
641
+ "content_filter" if event.data.content_filter_triggered else event.data.finish_reason
642
+ )
643
+ if event_finish_reason:
644
+ finish_reason = event_finish_reason
640
645
  if event.data.model:
641
646
  model = event.data.model
642
647
  else:
@@ -780,7 +785,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
780
785
  )
781
786
  return
782
787
  usage_details = self._parse_usage_details_from_copilot(event.data)
783
- finish_reason = event.data.finish_reason or None
788
+ finish_reason = "content_filter" if event.data.content_filter_triggered else event.data.finish_reason
784
789
  model = event.data.model or None
785
790
  if usage_details or finish_reason or model:
786
791
  update = AgentResponseUpdate(
@@ -999,7 +1004,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
999
1004
  def _build_session_hooks(
1000
1005
  self,
1001
1006
  all_tools: Sequence[ToolTypes | CopilotTool],
1002
- opts: Mapping[str, Any],
1007
+ options: Mapping[str, Any],
1003
1008
  ) -> SessionHooks | None:
1004
1009
  """Build the ``SessionHooks`` to pass to the Copilot SDK for this session.
1005
1010
 
@@ -1007,11 +1012,14 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1007
1012
  ``approval_mode="always_require"`` is delegated to the Copilot SDK's native
1008
1013
  ``on_pre_tool_use`` hook:
1009
1014
 
1010
- - If the caller supplies their own ``on_pre_tool_use`` (via per-run ``options``
1011
- or ``default_options``), it takes precedence and is returned unchanged. A
1015
+ - If the caller supplies their own session hooks -- either the SDK-native
1016
+ ``hooks`` dict or the convenience ``on_pre_tool_use`` handler (via per-run
1017
+ ``options`` or ``default_options``) -- those take precedence and are used
1018
+ as-is. When both are given, the explicit ``hooks`` dict wins for any key it
1019
+ defines and the ``on_pre_tool_use`` shortcut fills in that key otherwise. A
1012
1020
  warning is logged naming any approval-required tool that will therefore not
1013
- be automatically gated, since the caller's hook is responsible for enforcing
1014
- approval.
1021
+ be automatically gated, since the caller's hooks are responsible for
1022
+ enforcing approval.
1015
1023
  - Otherwise, when any approval-required tool is present, a default hook is
1016
1024
  installed that returns ``"ask"`` for those tools (routing the decision to
1017
1025
  ``on_permission_request``) and defers (``None``) for all other tools.
@@ -1019,32 +1027,43 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1019
1027
  ``on_function_approval`` callback is configured: in that case approval is
1020
1028
  enforced inside the tool handler (see :meth:`_tool_to_copilot_tool`) to
1021
1029
  preserve backward-compatible behavior.
1022
- - When there are no approval-required tools and no caller hook, ``None`` is
1030
+ - When there are no approval-required tools and no caller hooks, ``None`` is
1023
1031
  returned so no hooks are registered.
1024
1032
 
1025
1033
  Args:
1026
1034
  all_tools: The full set of tools resolved for the session.
1027
- opts: Runtime options that take precedence over ``default_options``.
1035
+ options: The merged session options (``default_options`` overlaid with
1036
+ per-run ``options``).
1028
1037
 
1029
1038
  Returns:
1030
1039
  The hooks to register for the session, or ``None`` if none are needed.
1031
1040
  """
1032
- user_hook: PreToolUseHandler | None = opts.get("on_pre_tool_use") or self._on_pre_tool_use
1041
+ user_hook: PreToolUseHandler | None = options.get("on_pre_tool_use") or self._on_pre_tool_use
1042
+ caller_hooks: Mapping[str, Any] | None = options.get("hooks")
1043
+
1044
+ # Combine caller-provided hooks: the SDK-native ``hooks`` dict plus the
1045
+ # convenience ``on_pre_tool_use`` shortcut. The explicit dict wins for the
1046
+ # keys it defines; the shortcut only fills in ``on_pre_tool_use`` otherwise.
1047
+ combined: dict[str, Any] = {}
1048
+ if user_hook is not None:
1049
+ combined["on_pre_tool_use"] = user_hook
1050
+ if caller_hooks:
1051
+ combined.update(caller_hooks)
1033
1052
 
1034
1053
  approval_required_names = {
1035
1054
  tool.name for tool in all_tools if isinstance(tool, FunctionTool) and tool.approval_mode == "always_require"
1036
1055
  }
1037
1056
 
1038
- if user_hook is not None:
1057
+ if combined:
1039
1058
  if approval_required_names:
1040
1059
  logger.warning(
1041
- "A custom 'on_pre_tool_use' hook is configured, so %d approval-required tool(s) (%s) "
1042
- "will not be automatically gated by GitHubCopilotAgent. The custom hook is responsible "
1060
+ "Custom session hooks are configured, so %d approval-required tool(s) (%s) "
1061
+ "will not be automatically gated by GitHubCopilotAgent. The custom hooks are responsible "
1043
1062
  "for enforcing approval (for example, by returning a 'deny' or 'ask' decision).",
1044
1063
  len(approval_required_names),
1045
1064
  ", ".join(sorted(approval_required_names)),
1046
1065
  )
1047
- return {"on_pre_tool_use": user_hook}
1066
+ return cast("SessionHooks", combined)
1048
1067
 
1049
1068
  if not approval_required_names:
1050
1069
  return None
@@ -1107,6 +1126,57 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1107
1126
  except Exception as ex:
1108
1127
  raise AgentException(f"Failed to create GitHub Copilot session: {ex}") from ex
1109
1128
 
1129
+ def _build_session_kwargs(
1130
+ self,
1131
+ streaming: bool,
1132
+ runtime_options: dict[str, Any] | None,
1133
+ ) -> dict[str, Any]:
1134
+ """Assemble keyword arguments for ``create_session`` / ``resume_session``.
1135
+
1136
+ Options are layered: the agent's ``default_options`` first, then per-run
1137
+ ``runtime_options`` which override them. Every key is forwarded verbatim to
1138
+ the Copilot SDK, so any ``create_session`` parameter is supported without a
1139
+ dedicated mapping here (an unknown name surfaces as a ``TypeError`` from the
1140
+ 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``.
1144
+
1145
+ Args:
1146
+ streaming: Whether to enable streaming for the session.
1147
+ runtime_options: Runtime options that take precedence over default_options.
1148
+
1149
+ Returns:
1150
+ The keyword arguments to splat into the SDK session factory.
1151
+ """
1152
+ opts = runtime_options or {}
1153
+
1154
+ # Passthrough layer: agent defaults first, per-run options override.
1155
+ kwargs: dict[str, Any] = {**self._default_options, **opts}
1156
+
1157
+ # Merge agent-level tools with any caller-supplied tools (from default_options
1158
+ # or per-run options, the latter winning) and convert to SDK tools.
1159
+ all_tools = list(self._tools or []) + list(kwargs.get("tools") or [])
1160
+ kwargs["tools"] = self._prepare_tools(all_tools) if all_tools else None
1161
+
1162
+ kwargs["streaming"] = streaming
1163
+ # model may already be present from per-run options (merged above); otherwise fall
1164
+ # back to the resolved setting (which carries the default_options / env model).
1165
+ if not kwargs.get("model"):
1166
+ kwargs["model"] = self._settings.get("model") or None
1167
+ kwargs["on_permission_request"] = (
1168
+ opts.get("on_permission_request") or self._permission_handler or _deny_all_permissions
1169
+ )
1170
+ kwargs["hooks"] = self._build_session_hooks(all_tools, kwargs)
1171
+
1172
+ # Strip agent-internal and client-level keys that are consumed here or in the
1173
+ # run methods (and settings) but are NOT valid create_session parameters, so
1174
+ # they don't leak through the passthrough layer and raise TypeError.
1175
+ for key in ("on_pre_tool_use", "on_function_approval", "timeout", "cli_path", "log_level", "base_directory"):
1176
+ kwargs.pop(key, None)
1177
+
1178
+ return kwargs
1179
+
1110
1180
  async def _create_session(
1111
1181
  self,
1112
1182
  streaming: bool,
@@ -1121,34 +1191,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1121
1191
  if not self._client:
1122
1192
  raise RuntimeError("GitHub Copilot client not initialized. Call start() first.")
1123
1193
 
1124
- opts = runtime_options or {}
1125
- model = opts.get("model") or self._settings.get("model") or None
1126
- system_message = opts.get("system_message") or self._default_options.get("system_message") or None
1127
- permission_handler: PermissionHandlerType = (
1128
- opts.get("on_permission_request") or self._permission_handler or _deny_all_permissions
1129
- )
1130
- mcp_servers = opts.get("mcp_servers") or self._mcp_servers or None
1131
- provider = opts.get("provider") or self._provider or None
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)
1135
- all_tools = list(self._tools or []) + list(opts.get("tools") or [])
1136
- tools = self._prepare_tools(all_tools) if all_tools else None
1137
- hooks = self._build_session_hooks(all_tools, opts)
1138
-
1139
- return await self._client.create_session(
1140
- on_permission_request=permission_handler,
1141
- streaming=streaming,
1142
- model=model or None,
1143
- system_message=system_message or None,
1144
- tools=tools or None,
1145
- mcp_servers=mcp_servers or None,
1146
- provider=provider or None,
1147
- instruction_directories=instruction_directories,
1148
- skill_directories=skill_directories,
1149
- disabled_skills=disabled_skills,
1150
- hooks=hooks,
1151
- )
1194
+ return await self._client.create_session(**self._build_session_kwargs(streaming, runtime_options))
1152
1195
 
1153
1196
  async def _resume_session(
1154
1197
  self,
@@ -1166,35 +1209,7 @@ class RawGitHubCopilotAgent(BaseAgent, Generic[OptionsT]):
1166
1209
  if not self._client:
1167
1210
  raise RuntimeError("GitHub Copilot client not initialized. Call start() first.")
1168
1211
 
1169
- opts = runtime_options or {}
1170
- model = opts.get("model") or self._settings.get("model") or None
1171
- system_message = opts.get("system_message") or self._default_options.get("system_message") or None
1172
- permission_handler: PermissionHandlerType = (
1173
- opts.get("on_permission_request") or self._permission_handler or _deny_all_permissions
1174
- )
1175
- mcp_servers = opts.get("mcp_servers") or self._mcp_servers or None
1176
- provider = opts.get("provider") or self._provider or None
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)
1180
- all_tools = list(self._tools or []) + list(opts.get("tools") or [])
1181
- tools = self._prepare_tools(all_tools) if all_tools else None
1182
- hooks = self._build_session_hooks(all_tools, opts)
1183
-
1184
- return await self._client.resume_session(
1185
- session_id,
1186
- on_permission_request=permission_handler,
1187
- streaming=streaming,
1188
- model=model or None,
1189
- system_message=system_message or None,
1190
- tools=tools or None,
1191
- mcp_servers=mcp_servers or None,
1192
- provider=provider or None,
1193
- instruction_directories=instruction_directories,
1194
- skill_directories=skill_directories,
1195
- disabled_skills=disabled_skills,
1196
- hooks=hooks,
1197
- )
1212
+ return await self._client.resume_session(session_id, **self._build_session_kwargs(streaming, runtime_options))
1198
1213
 
1199
1214
 
1200
1215
  class GitHubCopilotAgent( # type: ignore[misc]
@@ -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.0rc3"
7
+ version = "1.0.0rc4"
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"