agent-framework-core 1.22.0__tar.gz → 1.23.0__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.
Files changed (112) hide show
  1. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/PKG-INFO +16 -15
  2. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_agents.py +50 -42
  3. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_feature_stage.py +0 -1
  4. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_filesystem.py +29 -0
  5. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_background_agents.py +10 -5
  6. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_loop.py +4 -3
  7. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_memory.py +9 -19
  8. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_todo.py +7 -12
  9. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_tool_approval.py +2 -3
  10. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_mcp.py +879 -956
  11. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_skills.py +84 -70
  12. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_tools.py +50 -16
  13. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_types.py +77 -25
  14. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_agent_executor.py +59 -8
  15. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_functional.py +10 -3
  16. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_typing_utils.py +15 -0
  17. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/ag_ui/__init__.py +2 -0
  18. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/ag_ui/__init__.pyi +2 -0
  19. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/security.py +7 -9
  20. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/pyproject.toml +16 -15
  21. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/LICENSE +0 -0
  22. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/README.md +0 -0
  23. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/__init__.py +0 -0
  24. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/__init__.pyi +0 -0
  25. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_agent_hooks.py +0 -0
  26. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_clients.py +0 -0
  27. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_compaction.py +0 -0
  28. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_docstrings.py +0 -0
  29. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_evaluation.py +0 -0
  30. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/__init__.py +0 -0
  31. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_agent.py +0 -0
  32. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_agent.pyi +0 -0
  33. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_file_access.py +0 -0
  34. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_file_memory.py +0 -0
  35. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_harness/_mode.py +0 -0
  36. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_in_memory.py +0 -0
  37. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_middleware.py +0 -0
  38. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_serialization.py +0 -0
  39. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_sessions.py +0 -0
  40. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_settings.py +0 -0
  41. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_telemetry.py +0 -0
  42. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_vector_filters.py +0 -0
  43. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_vectors.py +0 -0
  44. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/__init__.py +0 -0
  45. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_agent.py +0 -0
  46. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_agent_utils.py +0 -0
  47. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_checkpoint.py +0 -0
  48. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_checkpoint_encoding.py +0 -0
  49. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_const.py +0 -0
  50. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_conversation_history.py +0 -0
  51. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_edge.py +0 -0
  52. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_edge_runner.py +0 -0
  53. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_events.py +0 -0
  54. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_executor.py +0 -0
  55. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_function_executor.py +0 -0
  56. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_message_utils.py +0 -0
  57. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_model_utils.py +0 -0
  58. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_request_info_mixin.py +0 -0
  59. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_runner.py +0 -0
  60. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_runner_context.py +0 -0
  61. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_state.py +0 -0
  62. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_validation.py +0 -0
  63. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_viz.py +0 -0
  64. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_workflow.py +0 -0
  65. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_workflow_builder.py +0 -0
  66. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_workflow_context.py +0 -0
  67. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/_workflows/_workflow_executor.py +0 -0
  68. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/a2a/__init__.py +0 -0
  69. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/a2a/__init__.pyi +0 -0
  70. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/amazon/__init__.py +0 -0
  71. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/amazon/__init__.pyi +0 -0
  72. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/anthropic/__init__.py +0 -0
  73. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/anthropic/__init__.pyi +0 -0
  74. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/azure/__init__.py +0 -0
  75. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/azure/__init__.pyi +0 -0
  76. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/chatkit/__init__.py +0 -0
  77. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/chatkit/__init__.pyi +0 -0
  78. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/declarative/__init__.py +0 -0
  79. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/declarative/__init__.pyi +0 -0
  80. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/devui/__init__.py +0 -0
  81. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/devui/__init__.pyi +0 -0
  82. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/exceptions.py +0 -0
  83. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/foundry/__init__.py +0 -0
  84. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/foundry/__init__.pyi +0 -0
  85. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/gemini/__init__.py +0 -0
  86. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/gemini/__init__.pyi +0 -0
  87. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/github/__init__.py +0 -0
  88. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/github/__init__.pyi +0 -0
  89. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/google/__init__.py +0 -0
  90. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/google/__init__.pyi +0 -0
  91. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/hyperlight/__init__.py +0 -0
  92. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/lab/__init__.py +0 -0
  93. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/mem0/__init__.py +0 -0
  94. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/mem0/__init__.pyi +0 -0
  95. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/microsoft/__init__.py +0 -0
  96. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/microsoft/__init__.pyi +0 -0
  97. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/mistral/__init__.py +0 -0
  98. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/mistral/__init__.pyi +0 -0
  99. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/monty/__init__.py +0 -0
  100. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/monty/__init__.pyi +0 -0
  101. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/observability.py +0 -0
  102. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/ollama/__init__.py +0 -0
  103. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/ollama/__init__.pyi +0 -0
  104. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/openai/__init__.py +0 -0
  105. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/openai/__init__.pyi +0 -0
  106. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/orchestrations/__init__.py +0 -0
  107. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/orchestrations/__init__.pyi +0 -0
  108. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/py.typed +0 -0
  109. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/redis/__init__.py +0 -0
  110. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/redis/__init__.pyi +0 -0
  111. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/tools/__init__.py +0 -0
  112. {agent_framework_core-1.22.0 → agent_framework_core-1.23.0}/agent_framework/tools/__init__.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agent-framework-core
3
- Version: 1.22.0
3
+ Version: 1.23.0
4
4
  Summary: Microsoft Agent Framework for building AI Agents with Python. This is the core package that has all the core abstractions and implementations.
5
5
  Author-email: Microsoft <af-support@microsoft.com>
6
6
  Requires-Python: >=3.10
@@ -24,36 +24,37 @@ Requires-Dist: python-dotenv>=1,<2
24
24
  Requires-Dist: opentelemetry-api>=1.39.0,<2
25
25
  Requires-Dist: pyyaml>=6.0,<7.0
26
26
  Requires-Dist: regex>=2024.11.6
27
- Requires-Dist: mcp>=1.24.0,<2 ; extra == "all"
28
- Requires-Dist: agent-framework-a2a>=1.0.0b261015,<2 ; extra == "all"
29
- Requires-Dist: agent-framework-ag-ui>=1.6.0,<2 ; extra == "all"
30
- Requires-Dist: agent-framework-anthropic>=1.0.0b261015,<2 ; extra == "all"
27
+ Requires-Dist: mcp>=2.2.0,<3 ; extra == "all"
28
+ Requires-Dist: tenacity>=9.1.4,<9.2.0 ; extra == "all"
29
+ Requires-Dist: agent-framework-a2a>=1.0.0b261022,<2 ; extra == "all"
30
+ Requires-Dist: agent-framework-ag-ui>=1.7.0,<2 ; extra == "all"
31
+ Requires-Dist: agent-framework-anthropic>=1.0.0b261022,<2 ; extra == "all"
31
32
  Requires-Dist: agent-framework-azure-ai-search>=1.0.0b261015,<2 ; extra == "all"
32
33
  Requires-Dist: agent-framework-azure-contentunderstanding>=1.0.0b261015,<2 ; extra == "all"
33
34
  Requires-Dist: agent-framework-azure-cosmos>=1.0.0b261015,<2 ; extra == "all"
34
35
  Requires-Dist: agent-framework-azurefunctions>=1.0.0b260922,<2 ; extra == "all"
35
- Requires-Dist: agent-framework-bedrock>=1.0.0b261015,<2 ; extra == "all"
36
+ Requires-Dist: agent-framework-bedrock>=1.0.0b261022,<2 ; extra == "all"
36
37
  Requires-Dist: agent-framework-chatkit>=1.0.0b261015,<2 ; extra == "all"
37
38
  Requires-Dist: agent-framework-claude>=1.0.0b261015,<2 ; extra == "all"
38
39
  Requires-Dist: agent-framework-copilotstudio>=1.0.0b261015,<2 ; extra == "all"
39
- Requires-Dist: agent-framework-declarative>=1.3.0,<2 ; extra == "all"
40
- Requires-Dist: agent-framework-devui>=1.0.0b261015,<2 ; extra == "all"
40
+ Requires-Dist: agent-framework-declarative>=1.4.0,<2 ; extra == "all"
41
+ Requires-Dist: agent-framework-devui>=1.0.0b261022,<2 ; extra == "all"
41
42
  Requires-Dist: agent-framework-durabletask>=1.0.0b260922,<2 ; extra == "all"
42
43
  Requires-Dist: agent-framework-foundry>=1.15.0,<2 ; extra == "all"
43
- Requires-Dist: agent-framework-foundry-hosting>=1.0.0b261015,<2 ; extra == "all"
44
+ Requires-Dist: agent-framework-foundry-hosting>=1.0.0b261022,<2 ; extra == "all"
44
45
  Requires-Dist: agent-framework-foundry-local>=1.0.0b261015,<2 ; extra == "all"
45
- Requires-Dist: agent-framework-gemini>=1.0.0b261015,<2 ; extra == "all"
46
+ Requires-Dist: agent-framework-gemini>=1.0.0b261022,<2 ; extra == "all"
46
47
  Requires-Dist: agent-framework-github-copilot>=2.1.0,<3 ; extra == "all" and ( python_version >= '3.11')
47
- Requires-Dist: agent-framework-hyperlight>=1.0.0b261015,<2 ; extra == "all" and ( python_version < '3.15' and ((sys_platform == 'linux' and platform_machine == 'x86_64') or (sys_platform == 'win32' and platform_machine == 'AMD64')))
48
+ Requires-Dist: agent-framework-hyperlight>=1.0.0b261022,<2 ; extra == "all" and ( (sys_platform == 'linux' and platform_machine == 'x86_64') or (sys_platform == 'win32' and platform_machine == 'AMD64'))
48
49
  Requires-Dist: agent-framework-mem0>=1.0.0b261015,<2 ; extra == "all"
49
50
  Requires-Dist: agent-framework-mistral>=1.0.0b261015,<2 ; extra == "all"
50
51
  Requires-Dist: agent-framework-monty>=1.0.0b261015,<2 ; extra == "all"
51
- Requires-Dist: agent-framework-ollama>=1.0.0b261015,<2 ; extra == "all"
52
+ Requires-Dist: agent-framework-ollama>=1.0.0b261022,<2 ; extra == "all"
52
53
  Requires-Dist: agent-framework-openai>=1.17.0,<2 ; extra == "all"
53
- Requires-Dist: agent-framework-orchestrations>=1.3.2,<2 ; extra == "all"
54
+ Requires-Dist: agent-framework-orchestrations>=1.4.0,<2 ; extra == "all"
54
55
  Requires-Dist: agent-framework-purview>=1.0.0b261015,<2 ; extra == "all"
55
- Requires-Dist: agent-framework-redis>=1.0.0b261015,<2 ; extra == "all" and ( python_version < '3.15')
56
- Requires-Dist: agent-framework-tools>=1.0.0b261015,<2 ; extra == "all"
56
+ Requires-Dist: agent-framework-redis>=1.0.0b261022,<2 ; extra == "all" and ( python_version < '3.15')
57
+ Requires-Dist: agent-framework-tools>=1.0.0b261022,<2 ; extra == "all"
57
58
  Project-URL: homepage, https://aka.ms/agent-framework
58
59
  Project-URL: issues, https://github.com/microsoft/agent-framework/issues
59
60
  Project-URL: release_notes, https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true
@@ -1881,8 +1881,9 @@ class RawAgent(BaseAgent, Generic[OptionsCoT]):
1881
1881
  """
1882
1882
  try:
1883
1883
  from mcp import types
1884
+ from mcp.server import ServerRequestContext
1884
1885
  from mcp.server.lowlevel import Server
1885
- from mcp.shared.exceptions import McpError
1886
+ from mcp.shared.exceptions import MCPError
1886
1887
  except ModuleNotFoundError as exc:
1887
1888
  raise ModuleNotFoundError(
1888
1889
  "`mcp` is required to use `Agent.as_mcp_server()`. Please install `mcp`."
@@ -1891,55 +1892,57 @@ class RawAgent(BaseAgent, Generic[OptionsCoT]):
1891
1892
 
1892
1893
  server_args: dict[str, Any] = {
1893
1894
  "name": server_name,
1894
- "version": version,
1895
1895
  "instructions": instructions,
1896
1896
  }
1897
+
1898
+ if version is not None:
1899
+ server_args["version"] = version
1900
+
1897
1901
  if lifespan:
1898
1902
  server_args["lifespan"] = lifespan
1899
1903
  if kwargs:
1900
1904
  server_args.update(kwargs)
1901
1905
 
1902
- server: Server[Any] = Server(**server_args)
1903
-
1904
1906
  agent_tool = self.as_tool(name=self._get_agent_name())
1905
1907
 
1906
- async def _log(level: types.LoggingLevel, data: Any) -> None:
1907
- """Log a message to the server and logger."""
1908
- # Log to the local logger
1908
+ async def _log(ctx: ServerRequestContext[dict[str, Any]], level: types.LoggingLevel, data: Any) -> None:
1909
+ """Log locally and forward through the session handling this request."""
1909
1910
  logger.log(LOG_LEVEL_MAPPING[level], data)
1910
- if server and server.request_context and server.request_context.session:
1911
- try:
1912
- await server.request_context.session.send_log_message(level=level, data=data)
1913
- except Exception as e:
1914
- logger.error("Failed to send log message to server: %s", e)
1915
-
1916
- @server.list_tools()
1917
- async def _list_tools() -> list[types.Tool]:
1911
+ try:
1912
+ await ctx.session.send_log_message( # pyright: ignore[reportDeprecated]
1913
+ level=level,
1914
+ data=data,
1915
+ )
1916
+ except Exception as exc:
1917
+ logger.error("Failed to send log message to MCP client: %s", exc)
1918
+
1919
+ async def _list_tools(
1920
+ ctx: ServerRequestContext[dict[str, Any]], _params: types.PaginatedRequestParams | None
1921
+ ) -> types.ListToolsResult:
1918
1922
  """List all tools in the agent."""
1919
1923
  schema = agent_tool.parameters()
1920
1924
 
1921
1925
  tool = types.Tool(
1922
1926
  name=agent_tool.name,
1923
1927
  description=agent_tool.description,
1924
- inputSchema=schema,
1928
+ input_schema=schema,
1925
1929
  )
1926
1930
 
1927
- await _log(level="debug", data=f"Agent tool: {agent_tool}")
1928
- return [tool]
1931
+ await _log(ctx, level="debug", data=f"Agent tool: {agent_tool}")
1932
+ return types.ListToolsResult(tools=[tool])
1929
1933
 
1930
- @server.call_tool()
1931
1934
  async def _call_tool(
1932
- name: str, arguments: dict[str, Any]
1933
- ) -> Sequence[types.TextContent | types.ImageContent | types.AudioContent | types.EmbeddedResource]:
1935
+ ctx: ServerRequestContext[dict[str, Any]], params: types.CallToolRequestParams
1936
+ ) -> types.CallToolResult:
1934
1937
  """Call a tool in the agent."""
1935
- await _log(level="debug", data=f"Calling tool with args: {arguments}")
1938
+ arguments = params.arguments or {}
1939
+ name = params.name
1940
+ await _log(ctx, level="debug", data=f"Calling tool with args: {arguments}")
1936
1941
 
1937
1942
  if name != agent_tool.name:
1938
- raise McpError(
1939
- error=types.ErrorData(
1940
- code=types.INTERNAL_ERROR,
1941
- message=f"Tool {name} not found",
1942
- ),
1943
+ raise MCPError(
1944
+ code=types.INTERNAL_ERROR,
1945
+ message=f"Tool {name} not found",
1943
1946
  )
1944
1947
 
1945
1948
  # Create an instance of the input model with the arguments
@@ -1949,17 +1952,15 @@ class RawAgent(BaseAgent, Generic[OptionsCoT]):
1949
1952
  )
1950
1953
  result = await agent_tool.invoke(arguments=args_instance)
1951
1954
  except Exception as e:
1952
- raise McpError(
1953
- error=types.ErrorData(
1954
- code=types.INTERNAL_ERROR,
1955
- message=f"Error calling tool {name}: {e}",
1956
- ),
1955
+ raise MCPError(
1956
+ code=types.INTERNAL_ERROR,
1957
+ message=f"Error calling tool {name}: {e}",
1957
1958
  ) from e
1958
1959
 
1959
1960
  # Convert result to MCP content.
1960
1961
  # Currently only text items are forwarded over MCP; rich content
1961
1962
  # (images, audio) is not yet supported in the MCP server path.
1962
- mcp_content: list[types.TextContent | types.ImageContent | types.EmbeddedResource] = []
1963
+ mcp_content: list[types.ContentBlock] = []
1963
1964
  for c in result:
1964
1965
  if c.type == "text" and c.text:
1965
1966
  mcp_content.append(types.TextContent(type="text", text=c.text))
@@ -1968,15 +1969,22 @@ class RawAgent(BaseAgent, Generic[OptionsCoT]):
1968
1969
  "MCP server does not yet forward rich content (images, audio) "
1969
1970
  "in tool results. Rich content items will be omitted."
1970
1971
  )
1971
- return mcp_content or [types.TextContent(type="text", text="")]
1972
-
1973
- @server.set_logging_level()
1974
- async def _set_logging_level(level: types.LoggingLevel) -> None:
1975
- """Set the logging level for the server."""
1976
- logger.setLevel(LOG_LEVEL_MAPPING[level])
1977
- # emit this log with the new minimum level
1978
- await _log(level=level, data=f"Log level set to {level}")
1979
-
1972
+ return types.CallToolResult(content=mcp_content)
1973
+
1974
+ async def _set_logging_level(
1975
+ ctx: ServerRequestContext[dict[str, Any]], params: types.SetLevelRequestParams
1976
+ ) -> types.EmptyResult:
1977
+ """Set the local threshold and confirm it through the requesting MCP session."""
1978
+ logger.setLevel(LOG_LEVEL_MAPPING[params.level])
1979
+ await _log(ctx, level=params.level, data=f"Log level set to {params.level}")
1980
+ return types.EmptyResult()
1981
+
1982
+ server: Server[Any] = Server( # pyright: ignore[reportDeprecated]
1983
+ **server_args,
1984
+ on_list_tools=_list_tools,
1985
+ on_call_tool=_call_tool,
1986
+ on_set_logging_level=_set_logging_level,
1987
+ )
1980
1988
  return server
1981
1989
 
1982
1990
  def _get_agent_name(self) -> str:
@@ -60,7 +60,6 @@ class ExperimentalFeature(str, Enum):
60
60
  FOUNDRY_PREVIEW_TOOLS = "FOUNDRY_PREVIEW_TOOLS"
61
61
  FUNCTIONAL_WORKFLOWS = "FUNCTIONAL_WORKFLOWS"
62
62
  HARNESS = "HARNESS"
63
- MCP_LONG_RUNNING_TASKS = "MCP_LONG_RUNNING_TASKS"
64
63
  MCP_SKILLS = "MCP_SKILLS"
65
64
  PROGRESSIVE_TOOLS = "PROGRESSIVE_TOOLS"
66
65
  SESSION_STORE = "SESSION_STORE"
@@ -5,7 +5,9 @@
5
5
  from __future__ import annotations
6
6
 
7
7
  import hashlib
8
+ import os
8
9
  import stat
10
+ import uuid
9
11
  from base64 import b32encode
10
12
  from pathlib import Path
11
13
 
@@ -44,6 +46,33 @@ _MAX_ENCODED_STORAGE_KEY_SEGMENT_LENGTH = 180
44
46
  _DIGEST_SEGMENT_MARKER = "sha256-"
45
47
 
46
48
 
49
+ def _atomic_write_text(path: Path, text: str, *, encoding: str = "utf-8") -> None: # pyright: ignore[reportUnusedFunction]
50
+ """Atomically write text through a securely created sibling temporary file.
51
+
52
+ Exclusive creation prevents an existing file or symbolic link from redirecting the write.
53
+ """
54
+ path.parent.mkdir(parents=True, exist_ok=True)
55
+ temp_path = path.with_name(f".maf-state-{uuid.uuid4().hex}.tmp")
56
+ cleanup_path: Path | None = None
57
+ file_descriptor: int | None = None
58
+ try:
59
+ file_descriptor = os.open(temp_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o666)
60
+ cleanup_path = temp_path
61
+ file_handle = os.fdopen(file_descriptor, "w", encoding=encoding, closefd=False)
62
+ with file_handle:
63
+ file_handle.write(text)
64
+ descriptor_to_close = file_descriptor
65
+ file_descriptor = None
66
+ os.close(descriptor_to_close)
67
+ os.replace(temp_path, path)
68
+ cleanup_path = None
69
+ finally:
70
+ if file_descriptor is not None:
71
+ os.close(file_descriptor)
72
+ if cleanup_path is not None:
73
+ cleanup_path.unlink(missing_ok=True)
74
+
75
+
47
76
  def _is_link_or_reparse_point(path: Path) -> bool: # pyright: ignore[reportUnusedFunction]
48
77
  """Return whether ``path`` is a symbolic link, junction, or other reparse point."""
49
78
  path_stat = path.lstat()
@@ -229,7 +229,9 @@ def _finalize_task(
229
229
  else:
230
230
  task_info.status = BackgroundTaskStatus.COMPLETED
231
231
  task_info.result_text = completed_task.result().text
232
- runtime.in_flight_tasks.pop(task_info.id, None)
232
+ # A stale waiter may observe a task ID already reused by a continuation.
233
+ if runtime.in_flight_tasks.get(task_info.id) is completed_task:
234
+ runtime.in_flight_tasks.pop(task_info.id, None)
233
235
 
234
236
 
235
237
  def _refresh_task_state(
@@ -559,18 +561,21 @@ class BackgroundAgentsProvider(ContextProvider):
559
561
 
560
562
  # Find which ID completed.
561
563
  completed_id: int | None = None
564
+ completed_task: asyncio.Task[AgentResponse[Any]] | None = None
562
565
  for tid, task in waitable:
563
566
  if task in done:
564
567
  completed_id = tid
568
+ completed_task = task
565
569
  break
566
570
 
567
571
  # Finalize the completed task.
568
572
  tasks = _get_tasks(provider_state)
569
573
  task_info = next((t for t in tasks if t.id == completed_id), None)
570
- if task_info is not None and completed_id is not None:
571
- completed_task = runtime.in_flight_tasks.get(completed_id)
572
- if completed_task is not None:
573
- _finalize_task(task_info, completed_task, runtime)
574
+ if task_info is not None and completed_task is not None:
575
+ # Report the waited-on execution without overwriting a newer one.
576
+ is_current_task = runtime.in_flight_tasks.get(task_info.id) is completed_task
577
+ _finalize_task(task_info, completed_task, runtime)
578
+ if is_current_task:
574
579
  _save_tasks(provider_state, tasks)
575
580
  _save_provider_state(session, provider_state, source_id=source_id)
576
581
 
@@ -654,10 +654,11 @@ class AgentLoopMiddleware(AgentMiddleware):
654
654
  f"got {type(inner).__name__}."
655
655
  )
656
656
 
657
- async for update in inner:
658
- yield update
657
+ async with inner:
658
+ async for update in inner:
659
+ yield update
659
660
 
660
- holder["final"] = await inner.get_final_response()
661
+ holder["final"] = await inner.get_final_response()
661
662
  except MiddlewareTermination:
662
663
  # The pipeline's MiddlewareTermination suppression is no longer active once
663
664
  # process() has returned (the stream is consumed lazily), so a termination
@@ -5,7 +5,6 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import json
7
7
  import logging
8
- import os
9
8
  import re
10
9
  import threading
11
10
  import weakref
@@ -20,7 +19,10 @@ from typing import Any, ClassVar, Final, cast
20
19
  from .._clients import SupportsChatGetResponse
21
20
  from .._compaction import group_messages
22
21
  from .._feature_stage import ExperimentalFeature, experimental
23
- from .._filesystem import _storage_key_segment # pyright: ignore[reportPrivateUsage]
22
+ from .._filesystem import (
23
+ _atomic_write_text, # pyright: ignore[reportPrivateUsage]
24
+ _storage_key_segment, # pyright: ignore[reportPrivateUsage]
25
+ )
24
26
  from .._sessions import AgentSession, FileHistoryProvider, HistoryProvider, JsonDumps, JsonLoads, SessionContext
25
27
  from .._telemetry import FeatureIndex, mark_feature_used
26
28
  from .._tools import tool
@@ -122,22 +124,6 @@ def _unescape_markdown_line(line: str) -> str:
122
124
  return line
123
125
 
124
126
 
125
- def _atomic_write_text(path: Path, text: str, *, encoding: str = "utf-8") -> None:
126
- """Write text to ``path`` atomically via a sibling temp file plus :func:`os.replace`.
127
-
128
- A crash, OOM, or disk-full mid-write leaves the previous file (if any) intact instead of
129
- producing a truncated file that breaks every subsequent read.
130
- """
131
- path.parent.mkdir(parents=True, exist_ok=True)
132
- temp_path = path.with_name(f"{path.name}.tmp.{os.getpid()}")
133
- try:
134
- temp_path.write_text(text, encoding=encoding)
135
- os.replace(temp_path, path)
136
- finally:
137
- if temp_path.exists():
138
- temp_path.unlink(missing_ok=True)
139
-
140
-
141
127
  def _slugify_topic(topic: str) -> str:
142
128
  slug = re.sub(r"[^a-z0-9]+", "-", _normalize_topic(topic).lower()).strip("-")
143
129
  return slug or "memory-topic"
@@ -661,7 +647,11 @@ class MemoryStore(ABC):
661
647
 
662
648
  @experimental(feature_id=ExperimentalFeature.HARNESS)
663
649
  class MemoryFileStore(MemoryStore):
664
- """Store memory files under ``MEMORY.md``, ``topics/``, and ``transcripts/``."""
650
+ """Store memory files under ``MEMORY.md``, ``topics/``, and ``transcripts/``.
651
+
652
+ The storage directory must not be writable by untrusted users, because they could
653
+ replace the final memory files independently of this store's secure temporary writes.
654
+ """
665
655
 
666
656
  def __init__(
667
657
  self,
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import json
7
- import os
8
7
  import weakref
9
8
  from abc import ABC, abstractmethod
10
9
  from collections.abc import Mapping, MutableMapping
@@ -15,6 +14,7 @@ from typing_extensions import NotRequired, TypedDict
15
14
 
16
15
  from .._feature_stage import ExperimentalFeature, experimental
17
16
  from .._filesystem import (
17
+ _atomic_write_text, # pyright: ignore[reportPrivateUsage]
18
18
  _is_literal_storage_key_segment_safe, # pyright: ignore[reportPrivateUsage]
19
19
  _storage_key_segment, # pyright: ignore[reportPrivateUsage]
20
20
  )
@@ -289,7 +289,11 @@ class TodoSessionStore(TodoStore):
289
289
 
290
290
  @experimental(feature_id=ExperimentalFeature.HARNESS)
291
291
  class TodoFileStore(TodoStore):
292
- """Store todo state in one JSON file per session and source ID."""
292
+ """Store todo state in one JSON file per session and source ID.
293
+
294
+ The storage directory must not be writable by untrusted users, because they could
295
+ replace the final todo files independently of this store's secure temporary writes.
296
+ """
293
297
 
294
298
  def __init__(
295
299
  self,
@@ -404,16 +408,7 @@ class TodoFileStore(TodoStore):
404
408
  @staticmethod
405
409
  def _save_state_sync(state_path: Path, payload: str) -> None:
406
410
  """Synchronous helper that atomically writes the JSON state file."""
407
- state_path.parent.mkdir(parents=True, exist_ok=True)
408
- # Write to a sibling temp file then atomically replace, so a crash mid-write cannot leave
409
- # a truncated state file that breaks every subsequent tool call.
410
- temp_path = state_path.with_name(f"{state_path.name}.tmp.{os.getpid()}")
411
- try:
412
- temp_path.write_text(payload, encoding="utf-8")
413
- os.replace(temp_path, state_path)
414
- finally:
415
- if temp_path.exists():
416
- temp_path.unlink(missing_ok=True)
411
+ _atomic_write_text(state_path, payload)
417
412
 
418
413
 
419
414
  class TodoProvider(ContextProvider):
@@ -130,10 +130,9 @@ class ToolApprovalRule(SerializationMixin):
130
130
  server_label: Optional hosted-tool server boundary. Hosted approvals
131
131
  only match future approvals from the same server label.
132
132
  """
133
- normalized_name = tool_name.strip()
134
- if not normalized_name:
133
+ if not tool_name.strip():
135
134
  raise ValueError("Tool approval rule tool_name must be a non-empty string.")
136
- self.tool_name = normalized_name
135
+ self.tool_name = tool_name
137
136
  self.arguments = dict(arguments) if arguments is not None else None
138
137
  self.server_label = server_label
139
138