microsoft-agents-a365-runtime 0.2.1.dev13__tar.gz → 0.2.1.dev15__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.
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/PKG-INFO +1 -1
- microsoft_agents_a365_runtime-0.2.1.dev15/microsoft_agents_a365/runtime/utility.py +249 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365_runtime.egg-info/PKG-INFO +1 -1
- microsoft_agents_a365_runtime-0.2.1.dev13/microsoft_agents_a365/runtime/utility.py +0 -111
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/README.md +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365/runtime/__init__.py +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365/runtime/environment_utils.py +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365/runtime/operation_error.py +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365/runtime/operation_result.py +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365/runtime/power_platform_api_discovery.py +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365/runtime/version_utils.py +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365_runtime.egg-info/SOURCES.txt +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365_runtime.egg-info/dependency_links.txt +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365_runtime.egg-info/requires.txt +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/microsoft_agents_a365_runtime.egg-info/top_level.txt +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/pyproject.toml +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/setup.cfg +0 -0
- {microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/setup.py +0 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Utility functions for Microsoft Agent 365 runtime operations.
|
|
6
|
+
|
|
7
|
+
This module provides utility functions for token handling, agent identity resolution,
|
|
8
|
+
and other common runtime operations.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import platform
|
|
15
|
+
import re
|
|
16
|
+
import threading
|
|
17
|
+
import uuid
|
|
18
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Optional
|
|
21
|
+
|
|
22
|
+
import jwt
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Utility:
|
|
26
|
+
"""
|
|
27
|
+
Utility class providing common runtime operations for Agent 365.
|
|
28
|
+
|
|
29
|
+
This class contains static methods for token processing, agent identity resolution,
|
|
30
|
+
and other utility functions used across the Agent 365 runtime.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
_cached_version: Optional[str] = None
|
|
34
|
+
_cached_application_name: Optional[str] = None
|
|
35
|
+
_application_name_initialized: bool = False
|
|
36
|
+
_cache_lock: threading.Lock = threading.Lock()
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def get_app_id_from_token(token: Optional[str]) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Decodes the current token and retrieves the App ID (appid or azp claim).
|
|
42
|
+
|
|
43
|
+
**WARNING: NO SIGNATURE VERIFICATION** - This method uses jwt.decode() which does NOT
|
|
44
|
+
verify the token signature. The token claims can be spoofed by malicious actors.
|
|
45
|
+
This method is ONLY suitable for logging, analytics, and diagnostics purposes.
|
|
46
|
+
Do NOT use the returned value for authorization, access control, or security decisions.
|
|
47
|
+
|
|
48
|
+
Note: Returns a default GUID ('00000000-0000-0000-0000-000000000000') for empty tokens
|
|
49
|
+
for backward compatibility with callers that expect a valid-looking GUID.
|
|
50
|
+
For agent identification where empty string is preferred, use get_agent_id_from_token().
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
token: JWT token to decode. Can be None or empty.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
str: The App ID from the token's claims, or empty GUID if token is invalid.
|
|
57
|
+
Returns "00000000-0000-0000-0000-000000000000" if no valid App ID is found.
|
|
58
|
+
"""
|
|
59
|
+
if not token or not token.strip():
|
|
60
|
+
return str(uuid.UUID(int=0))
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
# Decode the JWT token without verification (we only need the claims)
|
|
64
|
+
# Note: verify=False is used because we only need to extract claims,
|
|
65
|
+
# not verify the token's authenticity
|
|
66
|
+
decoded_payload = jwt.decode(token, options={"verify_signature": False})
|
|
67
|
+
|
|
68
|
+
# Look for appid or azp claims (appid takes precedence)
|
|
69
|
+
app_id = decoded_payload.get("appid") or decoded_payload.get("azp")
|
|
70
|
+
return app_id if app_id else ""
|
|
71
|
+
|
|
72
|
+
except (jwt.DecodeError, jwt.InvalidTokenError):
|
|
73
|
+
# Token is malformed or invalid
|
|
74
|
+
return ""
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def get_agent_id_from_token(token: Optional[str]) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Decodes the token and retrieves the best available agent identifier.
|
|
80
|
+
Checks claims in priority order: xms_par_app_azp (agent blueprint ID) > appid > azp.
|
|
81
|
+
|
|
82
|
+
**WARNING: NO SIGNATURE VERIFICATION** - This method uses jwt.decode() which does NOT
|
|
83
|
+
verify the token signature. The token claims can be spoofed by malicious actors.
|
|
84
|
+
This method is ONLY suitable for logging, analytics, and diagnostics purposes.
|
|
85
|
+
Do NOT use the returned value for authorization, access control, or security decisions.
|
|
86
|
+
|
|
87
|
+
Note: Returns empty string for empty/missing tokens (unlike get_app_id_from_token() which
|
|
88
|
+
returns a default GUID). This allows callers to omit headers when no identifier is available.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
token: JWT token to decode. Can be None or empty.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
str: Agent ID (GUID) or empty string if not found or token is empty.
|
|
95
|
+
"""
|
|
96
|
+
if not token or not token.strip():
|
|
97
|
+
return ""
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
decoded_payload = jwt.decode(token, options={"verify_signature": False})
|
|
101
|
+
|
|
102
|
+
# Priority: xms_par_app_azp (agent blueprint ID) > appid > azp
|
|
103
|
+
return (
|
|
104
|
+
decoded_payload.get("xms_par_app_azp")
|
|
105
|
+
or decoded_payload.get("appid")
|
|
106
|
+
or decoded_payload.get("azp")
|
|
107
|
+
or ""
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
except (jwt.DecodeError, jwt.InvalidTokenError):
|
|
111
|
+
# Silent error handling - return empty string on decode failure
|
|
112
|
+
return ""
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def resolve_agent_identity(context: Any, auth_token: Optional[str]) -> str:
|
|
116
|
+
"""
|
|
117
|
+
Resolves the agent identity from the turn context or auth token.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
context: Turn context of the conversation turn. Expected to have an Activity
|
|
121
|
+
with methods like is_agentic_request() and get_agentic_instance_id().
|
|
122
|
+
auth_token: Authentication token if available.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
str: The agent identity (App ID). Returns the agentic instance ID if the
|
|
126
|
+
request is agentic, otherwise returns the App ID from the auth token.
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
# App ID is required to pass to MCP server URL
|
|
130
|
+
# Try to get agentic instance ID if this is an agentic request
|
|
131
|
+
if context and context.activity and context.activity.is_agentic_request():
|
|
132
|
+
agentic_id = context.activity.get_agentic_instance_id()
|
|
133
|
+
return agentic_id if agentic_id else ""
|
|
134
|
+
|
|
135
|
+
except (AttributeError, TypeError, Exception):
|
|
136
|
+
# Context/activity doesn't have the expected methods or properties
|
|
137
|
+
# or any other error occurred while accessing context/activity
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
# Fallback to extracting App ID from the auth token
|
|
141
|
+
return Utility.get_app_id_from_token(auth_token)
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def get_user_agent_header(orchestrator: Optional[str] = None) -> str:
|
|
145
|
+
"""
|
|
146
|
+
Generates a User-Agent header string for SDK requests.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
orchestrator: Optional orchestrator name to include in the User-Agent header.
|
|
150
|
+
Defaults to empty string if not provided.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
str: A formatted User-Agent header string containing SDK version, OS type,
|
|
154
|
+
Python version, and optional orchestrator information.
|
|
155
|
+
"""
|
|
156
|
+
if Utility._cached_version is None:
|
|
157
|
+
try:
|
|
158
|
+
Utility._cached_version = version("microsoft-agents-a365-runtime")
|
|
159
|
+
except PackageNotFoundError:
|
|
160
|
+
Utility._cached_version = "unknown"
|
|
161
|
+
|
|
162
|
+
orchestrator_part = f"; {orchestrator}" if orchestrator else ""
|
|
163
|
+
os_type = platform.system()
|
|
164
|
+
python_version = platform.python_version()
|
|
165
|
+
return f"Agent365SDK/{Utility._cached_version} ({os_type}; Python {python_version}{orchestrator_part})"
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def get_application_name() -> Optional[str]:
|
|
169
|
+
"""
|
|
170
|
+
Gets the application name from environment variable or pyproject.toml.
|
|
171
|
+
The pyproject.toml result is cached at first access to avoid repeated file I/O.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Optional[str]: Application name or None if not available.
|
|
175
|
+
"""
|
|
176
|
+
# First try environment variable (highest priority)
|
|
177
|
+
env_name = os.environ.get("AGENT365_APPLICATION_NAME")
|
|
178
|
+
if env_name:
|
|
179
|
+
return env_name
|
|
180
|
+
|
|
181
|
+
# Fall back to cached pyproject.toml name with thread-safe caching
|
|
182
|
+
if not Utility._application_name_initialized:
|
|
183
|
+
with Utility._cache_lock:
|
|
184
|
+
# Double-checked locking pattern
|
|
185
|
+
if not Utility._application_name_initialized:
|
|
186
|
+
Utility._cached_application_name = (
|
|
187
|
+
Utility._read_application_name_from_pyproject()
|
|
188
|
+
)
|
|
189
|
+
Utility._application_name_initialized = True
|
|
190
|
+
|
|
191
|
+
return Utility._cached_application_name
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def _read_application_name_from_pyproject() -> Optional[str]:
|
|
195
|
+
"""
|
|
196
|
+
Reads the application name from pyproject.toml at the current working directory.
|
|
197
|
+
|
|
198
|
+
Note: Uses Path.cwd() which assumes the application is started from its root directory.
|
|
199
|
+
This is a fallback mechanism - AGENT365_APPLICATION_NAME env var is the preferred source.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Optional[str]: Application name from pyproject.toml or None if not found.
|
|
203
|
+
"""
|
|
204
|
+
# Regex pattern to match: name = "value" or name = 'value'
|
|
205
|
+
# This handles exact field name matching and ignores comments
|
|
206
|
+
name_pattern = re.compile(r'^\s*name\s*=\s*["\']([^"\']*)["\']')
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
pyproject_path = Path.cwd() / "pyproject.toml"
|
|
210
|
+
if not pyproject_path.exists():
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
content = pyproject_path.read_text(encoding="utf-8")
|
|
214
|
+
|
|
215
|
+
# Simple TOML parsing for [project] name = "..."
|
|
216
|
+
# We avoid importing tomli/tomllib for this simple case
|
|
217
|
+
in_project_section = False
|
|
218
|
+
for line in content.splitlines():
|
|
219
|
+
stripped = line.strip()
|
|
220
|
+
if stripped == "[project]":
|
|
221
|
+
in_project_section = True
|
|
222
|
+
continue
|
|
223
|
+
elif stripped.startswith("[") and stripped.endswith("]"):
|
|
224
|
+
in_project_section = False
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
if in_project_section:
|
|
228
|
+
# Use regex to properly parse name = "value" with exact field matching
|
|
229
|
+
match = name_pattern.match(stripped)
|
|
230
|
+
if match:
|
|
231
|
+
value = match.group(1)
|
|
232
|
+
if value:
|
|
233
|
+
return value
|
|
234
|
+
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
except (OSError, ValueError):
|
|
238
|
+
# File read errors or parsing errors
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
@staticmethod
|
|
242
|
+
def reset_application_name_cache() -> None:
|
|
243
|
+
"""
|
|
244
|
+
Resets the cached application name. Used for testing purposes.
|
|
245
|
+
|
|
246
|
+
This method is intended for internal testing only.
|
|
247
|
+
"""
|
|
248
|
+
Utility._cached_application_name = None
|
|
249
|
+
Utility._application_name_initialized = False
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Copyright (c) Microsoft Corporation.
|
|
2
|
-
# Licensed under the MIT License.
|
|
3
|
-
|
|
4
|
-
"""
|
|
5
|
-
Utility functions for Microsoft Agent 365 runtime operations.
|
|
6
|
-
|
|
7
|
-
This module provides utility functions for token handling, agent identity resolution,
|
|
8
|
-
and other common runtime operations.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
from __future__ import annotations
|
|
12
|
-
|
|
13
|
-
import platform
|
|
14
|
-
import uuid
|
|
15
|
-
from importlib.metadata import PackageNotFoundError, version
|
|
16
|
-
from typing import Any, Optional
|
|
17
|
-
|
|
18
|
-
import jwt
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class Utility:
|
|
22
|
-
"""
|
|
23
|
-
Utility class providing common runtime operations for Agent 365.
|
|
24
|
-
|
|
25
|
-
This class contains static methods for token processing, agent identity resolution,
|
|
26
|
-
and other utility functions used across the Agent 365 runtime.
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
_cached_version = None
|
|
30
|
-
|
|
31
|
-
@staticmethod
|
|
32
|
-
def get_app_id_from_token(token: Optional[str]) -> str:
|
|
33
|
-
"""
|
|
34
|
-
Decodes the current token and retrieves the App ID (appid or azp claim).
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
token: JWT token to decode. Can be None or empty.
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
str: The App ID from the token's claims, or empty GUID if token is invalid.
|
|
41
|
-
Returns "00000000-0000-0000-0000-000000000000" if no valid App ID is found.
|
|
42
|
-
"""
|
|
43
|
-
if not token or not token.strip():
|
|
44
|
-
return str(uuid.UUID(int=0))
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
# Decode the JWT token without verification (we only need the claims)
|
|
48
|
-
# Note: verify=False is used because we only need to extract claims,
|
|
49
|
-
# not verify the token's authenticity
|
|
50
|
-
decoded_payload = jwt.decode(token, options={"verify_signature": False})
|
|
51
|
-
|
|
52
|
-
# Look for appid or azp claims (appid takes precedence)
|
|
53
|
-
app_id = decoded_payload.get("appid") or decoded_payload.get("azp")
|
|
54
|
-
return app_id if app_id else ""
|
|
55
|
-
|
|
56
|
-
except (jwt.DecodeError, jwt.InvalidTokenError):
|
|
57
|
-
# Token is malformed or invalid
|
|
58
|
-
return ""
|
|
59
|
-
|
|
60
|
-
@staticmethod
|
|
61
|
-
def resolve_agent_identity(context: Any, auth_token: Optional[str]) -> str:
|
|
62
|
-
"""
|
|
63
|
-
Resolves the agent identity from the turn context or auth token.
|
|
64
|
-
|
|
65
|
-
Args:
|
|
66
|
-
context: Turn context of the conversation turn. Expected to have an Activity
|
|
67
|
-
with methods like is_agentic_request() and get_agentic_instance_id().
|
|
68
|
-
auth_token: Authentication token if available.
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
str: The agent identity (App ID). Returns the agentic instance ID if the
|
|
72
|
-
request is agentic, otherwise returns the App ID from the auth token.
|
|
73
|
-
"""
|
|
74
|
-
try:
|
|
75
|
-
# App ID is required to pass to MCP server URL
|
|
76
|
-
# Try to get agentic instance ID if this is an agentic request
|
|
77
|
-
if context and context.activity and context.activity.is_agentic_request():
|
|
78
|
-
agentic_id = context.activity.get_agentic_instance_id()
|
|
79
|
-
return agentic_id if agentic_id else ""
|
|
80
|
-
|
|
81
|
-
except (AttributeError, TypeError, Exception):
|
|
82
|
-
# Context/activity doesn't have the expected methods or properties
|
|
83
|
-
# or any other error occurred while accessing context/activity
|
|
84
|
-
pass
|
|
85
|
-
|
|
86
|
-
# Fallback to extracting App ID from the auth token
|
|
87
|
-
return Utility.get_app_id_from_token(auth_token)
|
|
88
|
-
|
|
89
|
-
@staticmethod
|
|
90
|
-
def get_user_agent_header(orchestrator: Optional[str] = None) -> str:
|
|
91
|
-
"""
|
|
92
|
-
Generates a User-Agent header string for SDK requests.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
orchestrator: Optional orchestrator name to include in the User-Agent header.
|
|
96
|
-
Defaults to empty string if not provided.
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
str: A formatted User-Agent header string containing SDK version, OS type,
|
|
100
|
-
Python version, and optional orchestrator information.
|
|
101
|
-
"""
|
|
102
|
-
if Utility._cached_version is None:
|
|
103
|
-
try:
|
|
104
|
-
Utility._cached_version = version("microsoft-agents-a365-runtime")
|
|
105
|
-
except PackageNotFoundError:
|
|
106
|
-
Utility._cached_version = "unknown"
|
|
107
|
-
|
|
108
|
-
orchestrator_part = f"; {orchestrator}" if orchestrator else ""
|
|
109
|
-
os_type = platform.system()
|
|
110
|
-
python_version = platform.python_version()
|
|
111
|
-
return f"Agent365SDK/{Utility._cached_version} ({os_type}; Python {python_version}{orchestrator_part})"
|
{microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/setup.cfg
RENAMED
|
File without changes
|
{microsoft_agents_a365_runtime-0.2.1.dev13 → microsoft_agents_a365_runtime-0.2.1.dev15}/setup.py
RENAMED
|
File without changes
|