managed-deepagents 0.6.2.dev43__py3-none-win_amd64.whl → 0.6.2.dev44__py3-none-win_amd64.whl
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.
- managed_deepagents/_agent_auth.py +77 -8
- managed_deepagents/_binaries/mda.exe +0 -0
- managed_deepagents/_connections.py +114 -37
- managed_deepagents/_identity_auth.py +19 -0
- managed_deepagents/_identity_runtime.py +6 -0
- managed_deepagents/_validated_token.py +2 -0
- managed_deepagents/identity/__init__.py +4 -0
- {managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/METADATA +10 -7
- {managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/RECORD +13 -13
- {managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/WHEEL +0 -0
- {managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/entry_points.txt +0 -0
- {managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/licenses/LICENSE +0 -0
- {managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/top_level.txt +0 -0
|
@@ -34,21 +34,41 @@ __all__ = [
|
|
|
34
34
|
"AgentAuthUnknownProviderError",
|
|
35
35
|
"CredentialAuthorizationRequiredError",
|
|
36
36
|
"PendingAuthorization",
|
|
37
|
+
"ResolvePrincipalIdentity",
|
|
37
38
|
"WorkspaceConnection",
|
|
39
|
+
"clear_principal_resolve_cache_for_tests",
|
|
38
40
|
]
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
@dataclass(frozen=True)
|
|
42
44
|
class AgentAuthContext:
|
|
43
45
|
base_url: str
|
|
44
|
-
#: Deployment id, passed to oauth-authorization-sessions as ``agent_id``.
|
|
45
|
-
agent_id: str
|
|
46
46
|
#: Authenticated caller, used as ``owner_id`` for user credentials.
|
|
47
47
|
principal_id: str
|
|
48
|
+
#: Deployment id when present. Required for agent-owned lookups; optional for user OAuth.
|
|
49
|
+
agent_id: str | None = None
|
|
48
50
|
api_key: str | None = None
|
|
49
51
|
workspace_id: str | None = None
|
|
50
52
|
|
|
51
53
|
|
|
54
|
+
class ResolvePrincipalIdentity(TypedDict):
|
|
55
|
+
provider: str
|
|
56
|
+
issuer: str
|
|
57
|
+
subject: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
_principal_resolve_cache: dict[str, str] = {}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def clear_principal_resolve_cache_for_tests() -> None:
|
|
64
|
+
"""Drop cached principal resolutions. Test-only."""
|
|
65
|
+
_principal_resolve_cache.clear()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _principal_resolve_cache_key(identity: ResolvePrincipalIdentity) -> str:
|
|
69
|
+
return f"{identity['provider']}\0{identity['issuer']}\0{identity['subject']}"
|
|
70
|
+
|
|
71
|
+
|
|
52
72
|
class PendingAuthorization(TypedDict):
|
|
53
73
|
"""One connection slug the caller has not granted, with its connect URL."""
|
|
54
74
|
|
|
@@ -115,6 +135,52 @@ class AgentAuth:
|
|
|
115
135
|
self._timeout = timeout
|
|
116
136
|
self._request: AgentAuthRequest = request if request is not None else self._urlopen_json
|
|
117
137
|
|
|
138
|
+
def resolve_principal(self, identity: ResolvePrincipalIdentity) -> str:
|
|
139
|
+
"""Resolve a verified external identity to an Agent Auth principal UUID."""
|
|
140
|
+
provider = identity["provider"].strip()
|
|
141
|
+
issuer = identity["issuer"].strip()
|
|
142
|
+
subject = identity["subject"].strip()
|
|
143
|
+
if not provider or not issuer or not subject:
|
|
144
|
+
raise CredentialStoreError(
|
|
145
|
+
"Agent Auth principal resolve requires provider, issuer, and subject"
|
|
146
|
+
)
|
|
147
|
+
key = _principal_resolve_cache_key(
|
|
148
|
+
{"provider": provider, "issuer": issuer, "subject": subject}
|
|
149
|
+
)
|
|
150
|
+
cached = _principal_resolve_cache.get(key)
|
|
151
|
+
if cached is not None:
|
|
152
|
+
return cached
|
|
153
|
+
try:
|
|
154
|
+
principal_id = self._resolve_principal_uncached(
|
|
155
|
+
{"provider": provider, "issuer": issuer, "subject": subject}
|
|
156
|
+
)
|
|
157
|
+
except Exception:
|
|
158
|
+
raise
|
|
159
|
+
_principal_resolve_cache[key] = principal_id
|
|
160
|
+
return principal_id
|
|
161
|
+
|
|
162
|
+
def _resolve_principal_uncached(self, identity: ResolvePrincipalIdentity) -> str:
|
|
163
|
+
status, body = self._request(
|
|
164
|
+
"POST",
|
|
165
|
+
"/v1/agent-auth/principals",
|
|
166
|
+
body={
|
|
167
|
+
"provider": identity["provider"],
|
|
168
|
+
"issuer": identity["issuer"],
|
|
169
|
+
"subject": identity["subject"],
|
|
170
|
+
},
|
|
171
|
+
)
|
|
172
|
+
if status >= _HTTP_BAD_REQUEST:
|
|
173
|
+
raise CredentialStoreError("Agent Auth resolve principal failed", status, body)
|
|
174
|
+
principal = body.get("principal")
|
|
175
|
+
principal_id = ""
|
|
176
|
+
if isinstance(principal, Mapping):
|
|
177
|
+
raw = principal.get("id")
|
|
178
|
+
if isinstance(raw, str):
|
|
179
|
+
principal_id = raw.strip()
|
|
180
|
+
if not principal_id:
|
|
181
|
+
raise CredentialStoreError("Agent Auth resolve principal returned no principal id")
|
|
182
|
+
return principal_id
|
|
183
|
+
|
|
118
184
|
def find_workspace_connection(self, slug: str) -> WorkspaceConnection | None:
|
|
119
185
|
"""List workspace connections and return the row for ``slug``, if any."""
|
|
120
186
|
if not (self.context.workspace_id or "").strip():
|
|
@@ -175,14 +241,17 @@ class AgentAuth:
|
|
|
175
241
|
"""
|
|
176
242
|
if connection.kind != "secret":
|
|
177
243
|
return None
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
244
|
+
agent_id = (self.context.agent_id or "").strip()
|
|
245
|
+
if not agent_id:
|
|
246
|
+
raise CredentialStoreError(
|
|
247
|
+
"connections are not configured: LANGSMITH_HOST_PROJECT_ID is not set"
|
|
248
|
+
)
|
|
249
|
+
from_agent = self._credential_id_for_owner(connection.connection_id, "agent", agent_id)
|
|
181
250
|
if from_agent is not None:
|
|
182
251
|
return from_agent
|
|
183
252
|
return self.find_credential_id(
|
|
184
253
|
owner_type="agent",
|
|
185
|
-
owner_id=
|
|
254
|
+
owner_id=agent_id,
|
|
186
255
|
kind="secret",
|
|
187
256
|
slug=slug,
|
|
188
257
|
)
|
|
@@ -277,8 +346,8 @@ class AgentAuth:
|
|
|
277
346
|
"/v1/agent-auth/oauth-authorization-sessions",
|
|
278
347
|
body={
|
|
279
348
|
"connection_id": connection.connection_id,
|
|
280
|
-
"
|
|
281
|
-
"
|
|
349
|
+
"owner_type": "user",
|
|
350
|
+
"owner_id": self.context.principal_id,
|
|
282
351
|
},
|
|
283
352
|
)
|
|
284
353
|
if status >= _HTTP_BAD_REQUEST:
|
|
Binary file
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
``{ type: "agent" }`` resolves material owned by the current deployment.
|
|
4
4
|
``{ type: "user" }`` resolves per-caller grants (OAuth or opaque). Resolution
|
|
5
|
-
reads Agent Auth on every await and never caches.
|
|
5
|
+
reads Agent Auth on every await and never caches material.
|
|
6
6
|
|
|
7
7
|
``mda dev`` substitutes local values for agent opaque secrets via ``MDA_DEV_*``.
|
|
8
|
-
User-owned connections
|
|
9
|
-
|
|
8
|
+
User-owned connections talk to Agent Auth in every environment (including
|
|
9
|
+
``mda dev``) after resolving the caller's Agent Auth principal.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
@@ -25,6 +25,7 @@ from managed_deepagents._agent_auth import (
|
|
|
25
25
|
AgentAuthContext,
|
|
26
26
|
CredentialAuthorizationRequiredError,
|
|
27
27
|
WorkspaceConnection,
|
|
28
|
+
clear_principal_resolve_cache_for_tests,
|
|
28
29
|
)
|
|
29
30
|
from managed_deepagents._agent_auth_http import (
|
|
30
31
|
AGENT_AUTH_TIMEOUT_SECONDS,
|
|
@@ -49,10 +50,12 @@ __all__ = [
|
|
|
49
50
|
"CredentialStoreError",
|
|
50
51
|
"InvalidAgentAuthEndpointError",
|
|
51
52
|
"agent_connection_env_key",
|
|
53
|
+
"clear_principal_resolve_cache_for_tests",
|
|
52
54
|
"connections",
|
|
53
55
|
"is_connection_reference",
|
|
54
56
|
"resolve_agent_auth_base_url",
|
|
55
57
|
"resolve_agent_auth_context",
|
|
58
|
+
"resolve_credential_principal_id",
|
|
56
59
|
"resolve_owner_id",
|
|
57
60
|
]
|
|
58
61
|
|
|
@@ -108,19 +111,7 @@ class ConnectionNotFoundError(LookupError):
|
|
|
108
111
|
_SLUG_PATTERN = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
|
|
109
112
|
_MAX_SLUG_LENGTH = 64
|
|
110
113
|
_DEV_CREDENTIAL_PREFIX = "MDA_DEV_"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def _local_user_connection_unsupported(slug: str | None = None) -> str:
|
|
114
|
-
local_hint = (
|
|
115
|
-
f"set {agent_connection_env_key(slug)}"
|
|
116
|
-
if slug
|
|
117
|
-
else "set the corresponding MDA_DEV_<slug> variable"
|
|
118
|
-
)
|
|
119
|
-
return (
|
|
120
|
-
"user-owned connections need a deployment, so they do not resolve in `mda dev`: "
|
|
121
|
-
"deploy with `mda deploy` to exercise this connection, or declare it as "
|
|
122
|
-
f'{{"type": "agent"}} and {local_hint} for local runs'
|
|
123
|
-
)
|
|
114
|
+
_TRUSTED_BACKEND_PROVIDER = "trusted_backend"
|
|
124
115
|
|
|
125
116
|
|
|
126
117
|
#: Env var naming the LangSmith platform API that serves Agent Auth.
|
|
@@ -176,15 +167,29 @@ class AgentAuthConnectionStore:
|
|
|
176
167
|
async def fetch(self, request: ConnectionRequest) -> str | None:
|
|
177
168
|
owner = request["owner"]
|
|
178
169
|
resolved = os.environ if self._env is None else self._env
|
|
179
|
-
if resolved.get("MDA_LOCAL_DEV") == "1":
|
|
180
|
-
return
|
|
170
|
+
if resolved.get("MDA_LOCAL_DEV") == "1" and owner["type"] == "agent":
|
|
171
|
+
return _fetch_local_dev_agent_connection(resolved, request["slug"])
|
|
181
172
|
config = _resolve_agent_auth_config(self._env)
|
|
182
|
-
|
|
173
|
+
if owner["type"] == "agent" and not config.deployment_id:
|
|
174
|
+
raise CredentialStoreError(
|
|
175
|
+
f"connections are not configured: {DEPLOYMENT_ID_ENV} is not set"
|
|
176
|
+
)
|
|
177
|
+
# Source-identity match still uses JWT sub / channel actor.
|
|
178
|
+
resolve_owner_id(owner, request["identity"], config.deployment_id)
|
|
179
|
+
if owner["type"] == "user":
|
|
180
|
+
principal_id = resolve_credential_principal_id(
|
|
181
|
+
request["identity"],
|
|
182
|
+
config,
|
|
183
|
+
request_fn=self._bind_request(config),
|
|
184
|
+
timeout=self._timeout,
|
|
185
|
+
)
|
|
186
|
+
else:
|
|
187
|
+
principal_id = resolve_owner_id(owner, request["identity"], config.deployment_id)
|
|
183
188
|
client = AgentAuth(
|
|
184
189
|
AgentAuthContext(
|
|
185
190
|
base_url=config.base_url,
|
|
186
|
-
agent_id=config.deployment_id
|
|
187
|
-
principal_id=
|
|
191
|
+
agent_id=config.deployment_id,
|
|
192
|
+
principal_id=principal_id,
|
|
188
193
|
api_key=config.api_key,
|
|
189
194
|
workspace_id=config.workspace_id,
|
|
190
195
|
),
|
|
@@ -208,12 +213,14 @@ class AgentAuthConnectionStore:
|
|
|
208
213
|
*,
|
|
209
214
|
body: dict[str, object] | None = None,
|
|
210
215
|
) -> tuple[int, dict[str, object]]:
|
|
211
|
-
del method, body
|
|
212
216
|
url = f"{config.base_url}{path}"
|
|
213
217
|
headers = agent_auth_headers(
|
|
214
218
|
api_key=config.api_key,
|
|
215
219
|
workspace_id=config.workspace_id,
|
|
216
220
|
)
|
|
221
|
+
# Test injects only support GET-shaped responses; path still distinguishes
|
|
222
|
+
# principals vs connections for mock routers.
|
|
223
|
+
del method, body
|
|
217
224
|
response = http_get(url, headers, timeout)
|
|
218
225
|
status = response.get("status")
|
|
219
226
|
raw_body = response.get("body", b"")
|
|
@@ -227,13 +234,7 @@ class AgentAuthConnectionStore:
|
|
|
227
234
|
return request
|
|
228
235
|
|
|
229
236
|
|
|
230
|
-
def
|
|
231
|
-
resolved: Mapping[str, str],
|
|
232
|
-
slug: str,
|
|
233
|
-
owner: ConnectionOwner,
|
|
234
|
-
) -> str:
|
|
235
|
-
if owner["type"] == "user":
|
|
236
|
-
raise CredentialStoreError(_local_user_connection_unsupported(slug))
|
|
237
|
+
def _fetch_local_dev_agent_connection(resolved: Mapping[str, str], slug: str) -> str:
|
|
237
238
|
value = resolved.get(agent_connection_env_key(slug))
|
|
238
239
|
if value is not None and value.strip():
|
|
239
240
|
return value
|
|
@@ -410,24 +411,100 @@ def resolve_owner_id(
|
|
|
410
411
|
def resolve_agent_auth_context(
|
|
411
412
|
identity: RuntimeIdentity | None,
|
|
412
413
|
env: Mapping[str, str] | None = None,
|
|
414
|
+
*,
|
|
415
|
+
request_fn: Callable[..., tuple[int, dict[str, object]]] | None = None,
|
|
416
|
+
timeout: float = AGENT_AUTH_TIMEOUT_SECONDS,
|
|
413
417
|
) -> AgentAuthContext:
|
|
414
|
-
resolved = os.environ if env is None else env
|
|
415
|
-
if resolved.get("MDA_LOCAL_DEV") == "1":
|
|
416
|
-
raise CredentialStoreError(_local_user_connection_unsupported())
|
|
417
418
|
config = _resolve_agent_auth_config(env)
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
)
|
|
419
|
+
principal_id = resolve_credential_principal_id(
|
|
420
|
+
identity, config, request_fn=request_fn, timeout=timeout
|
|
421
|
+
)
|
|
422
422
|
return AgentAuthContext(
|
|
423
423
|
base_url=config.base_url,
|
|
424
424
|
agent_id=config.deployment_id,
|
|
425
|
-
principal_id=
|
|
425
|
+
principal_id=principal_id,
|
|
426
426
|
api_key=config.api_key,
|
|
427
427
|
workspace_id=config.workspace_id,
|
|
428
428
|
)
|
|
429
429
|
|
|
430
430
|
|
|
431
|
+
def resolve_credential_principal_id(
|
|
432
|
+
identity: RuntimeIdentity | None,
|
|
433
|
+
config: _AgentAuthConfig,
|
|
434
|
+
*,
|
|
435
|
+
request_fn: Callable[..., tuple[int, dict[str, object]]] | None = None,
|
|
436
|
+
timeout: float = AGENT_AUTH_TIMEOUT_SECONDS,
|
|
437
|
+
) -> str:
|
|
438
|
+
"""Map a runtime identity to the Agent Auth principal used for user grants."""
|
|
439
|
+
if identity is None:
|
|
440
|
+
raise CredentialStoreError("user connections require an authenticated user principal")
|
|
441
|
+
user = identity.get("user") or {}
|
|
442
|
+
user_id = user.get("id") if isinstance(user, Mapping) else None
|
|
443
|
+
if not isinstance(user_id, str) or not user_id.strip():
|
|
444
|
+
raise CredentialStoreError("user connections require an authenticated user principal")
|
|
445
|
+
kind = user.get("kind") if isinstance(user, Mapping) else None
|
|
446
|
+
if kind == "service":
|
|
447
|
+
raise CredentialStoreError(
|
|
448
|
+
"user-owned connections require a person principal; "
|
|
449
|
+
"Studio and other service callers cannot own OAuth grants"
|
|
450
|
+
)
|
|
451
|
+
stamped = identity.get("agent_auth_principal_id")
|
|
452
|
+
if isinstance(stamped, str) and stamped.strip():
|
|
453
|
+
return stamped.strip()
|
|
454
|
+
|
|
455
|
+
subject = user_id.strip()
|
|
456
|
+
external = _external_identity_for_resolve(identity, config.workspace_id)
|
|
457
|
+
if external is None:
|
|
458
|
+
raise CredentialStoreError(
|
|
459
|
+
"user-owned connections need a resolvable identity "
|
|
460
|
+
"(validated-token issuer/subject, channel identity_context, or trusted backend)"
|
|
461
|
+
)
|
|
462
|
+
bootstrap = AgentAuth(
|
|
463
|
+
AgentAuthContext(
|
|
464
|
+
base_url=config.base_url,
|
|
465
|
+
principal_id=subject,
|
|
466
|
+
api_key=config.api_key,
|
|
467
|
+
workspace_id=config.workspace_id,
|
|
468
|
+
agent_id=config.deployment_id,
|
|
469
|
+
),
|
|
470
|
+
request=request_fn,
|
|
471
|
+
timeout=timeout,
|
|
472
|
+
)
|
|
473
|
+
return bootstrap.resolve_principal(external)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _external_identity_for_resolve(
|
|
477
|
+
identity: RuntimeIdentity,
|
|
478
|
+
workspace_id: str | None,
|
|
479
|
+
) -> dict[str, str] | None:
|
|
480
|
+
user = identity.get("user") or {}
|
|
481
|
+
subject = user.get("id") if isinstance(user, Mapping) else None
|
|
482
|
+
if not isinstance(subject, str) or not subject.strip():
|
|
483
|
+
return None
|
|
484
|
+
subject = subject.strip()
|
|
485
|
+
provider = identity.get("identity_provider")
|
|
486
|
+
claims = identity.get("claims")
|
|
487
|
+
issuer = None
|
|
488
|
+
if isinstance(claims, Mapping):
|
|
489
|
+
raw_iss = claims.get("iss")
|
|
490
|
+
if isinstance(raw_iss, str) and raw_iss.strip():
|
|
491
|
+
issuer = raw_iss.strip()
|
|
492
|
+
if isinstance(provider, str) and provider.strip() and issuer:
|
|
493
|
+
return {
|
|
494
|
+
"provider": provider.strip(),
|
|
495
|
+
"issuer": issuer,
|
|
496
|
+
"subject": subject,
|
|
497
|
+
}
|
|
498
|
+
workspace = (workspace_id or "").strip()
|
|
499
|
+
if not workspace:
|
|
500
|
+
return None
|
|
501
|
+
return {
|
|
502
|
+
"provider": _TRUSTED_BACKEND_PROVIDER,
|
|
503
|
+
"issuer": workspace,
|
|
504
|
+
"subject": subject,
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
|
|
431
508
|
def _resolve_agent_auth_config(env: Mapping[str, str] | None) -> _AgentAuthConfig:
|
|
432
509
|
resolved = os.environ if env is None else env
|
|
433
510
|
api_key = (resolved.get(_API_KEY_ENV) or "").strip()
|
|
@@ -16,6 +16,7 @@ import asyncio
|
|
|
16
16
|
import hmac
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
|
+
import re
|
|
19
20
|
from collections.abc import Callable, Iterable, Mapping
|
|
20
21
|
from http import HTTPStatus
|
|
21
22
|
from importlib import import_module
|
|
@@ -58,6 +59,21 @@ class ManagedUser(ManagedUserRequired, total=False):
|
|
|
58
59
|
mda_source_provider: str
|
|
59
60
|
mda_source_thread_id: str
|
|
60
61
|
mda_user_kind: Literal["user", "service"]
|
|
62
|
+
#: Validated-token provider id for Agent Auth principal resolve.
|
|
63
|
+
mda_identity_provider: str
|
|
64
|
+
#: Agent Auth principal UUID when Trigger ``identity_context`` already resolved one.
|
|
65
|
+
mda_agent_auth_principal_id: str
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
_AGENT_AUTH_PRINCIPAL_ID = re.compile(
|
|
69
|
+
r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
70
|
+
re.IGNORECASE,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def is_agent_auth_principal_id(value: str) -> bool:
|
|
75
|
+
"""True when ``value`` is shaped like an Agent Auth principal UUID."""
|
|
76
|
+
return bool(_AGENT_AUTH_PRINCIPAL_ID.fullmatch(value.strip()))
|
|
61
77
|
|
|
62
78
|
|
|
63
79
|
if TYPE_CHECKING:
|
|
@@ -477,6 +493,9 @@ def _trusted_headers_user(headers: Mapping[str, object]) -> ManagedUser:
|
|
|
477
493
|
if source_provider is not None and source_thread is not None:
|
|
478
494
|
user["mda_source_provider"] = str(source_provider)
|
|
479
495
|
user["mda_source_thread_id"] = str(source_thread)
|
|
496
|
+
# Trigger loopback stamps the identity_context principal UUID.
|
|
497
|
+
if is_agent_auth_principal_id(str(actor)):
|
|
498
|
+
user["mda_agent_auth_principal_id"] = str(actor)
|
|
480
499
|
return user
|
|
481
500
|
|
|
482
501
|
|
|
@@ -455,6 +455,12 @@ def build_runtime_identity(configurable: Mapping[str, object] | None) -> Runtime
|
|
|
455
455
|
claims = _field(user, "mda_claims")
|
|
456
456
|
if isinstance(claims, Mapping):
|
|
457
457
|
identity["claims"] = dict(cast("Mapping[str, object]", claims))
|
|
458
|
+
identity_provider = _field(user, "mda_identity_provider")
|
|
459
|
+
if isinstance(identity_provider, str) and identity_provider.strip():
|
|
460
|
+
identity["identity_provider"] = identity_provider.strip()
|
|
461
|
+
agent_auth_principal_id = _field(user, "mda_agent_auth_principal_id")
|
|
462
|
+
if isinstance(agent_auth_principal_id, str) and agent_auth_principal_id.strip():
|
|
463
|
+
identity["agent_auth_principal_id"] = agent_auth_principal_id.strip()
|
|
458
464
|
# Immutable: tools/middleware get a read-only view they cannot tamper with.
|
|
459
465
|
return cast("RuntimeIdentity", _freeze(identity))
|
|
460
466
|
|
|
@@ -263,6 +263,8 @@ def claims_to_user(provider: ValidatedTokenProvider, claims: Mapping[str, object
|
|
|
263
263
|
"permissions": [],
|
|
264
264
|
"mda_user_id": str(caller),
|
|
265
265
|
"mda_claims": dict(claims),
|
|
266
|
+
# Provider id drives Agent Auth POST /principals (provider, issuer, subject).
|
|
267
|
+
"mda_identity_provider": (provider.get("id") or "").strip() or "oidc",
|
|
266
268
|
}
|
|
267
269
|
groups = normalize_groups(claims.get(claim_map["groups"])) if claim_map.get("groups") else []
|
|
268
270
|
if groups:
|
|
@@ -177,6 +177,10 @@ class RuntimeIdentity(RuntimeIdentityRequired, total=False):
|
|
|
177
177
|
#: tools and middleware.
|
|
178
178
|
groups: tuple[str, ...]
|
|
179
179
|
claims: JsonObject
|
|
180
|
+
#: Validated-token provider id for Agent Auth principal resolve.
|
|
181
|
+
identity_provider: str
|
|
182
|
+
#: Agent Auth principal UUID when channel ingress already resolved one.
|
|
183
|
+
agent_auth_principal_id: str
|
|
180
184
|
|
|
181
185
|
|
|
182
186
|
class PrincipalRequired(TypedDict):
|
{managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: managed-deepagents
|
|
3
|
-
Version: 0.6.2.
|
|
3
|
+
Version: 0.6.2.dev44
|
|
4
4
|
Summary: Managed Deep Agents — the define_deep_agent authoring interface plus the CLI that compiles and deploys a code-first Deep Agent repository to a managed LangGraph runtime.
|
|
5
5
|
Author: LangChain
|
|
6
6
|
License: MIT
|
|
@@ -277,16 +277,19 @@ connector = connectors.mcp(
|
|
|
277
277
|
For this example, set `MDA_DEV_DOCS_TOKEN`.
|
|
278
278
|
|
|
279
279
|
A user-owned connection — `connections.get(slug, {"type": "user"})` — resolves
|
|
280
|
-
per caller (OAuth or opaque). The
|
|
281
|
-
|
|
282
|
-
a
|
|
283
|
-
|
|
280
|
+
per caller (OAuth or opaque). The runtime maps the verified ingress identity to
|
|
281
|
+
an Agent Auth principal, then the pre-run credential gate interrupts the run
|
|
282
|
+
when a grant is missing. That path works in `mda deploy` and `mda dev` when
|
|
283
|
+
`LANGSMITH_API_KEY` and `LANGSMITH_WORKSPACE_ID` are set and the workspace
|
|
284
|
+
connection rows exist — use a person principal (for example a Supabase JWT),
|
|
285
|
+
not Studio’s synthetic service user. OAuth completes on LangSmith’s platform
|
|
286
|
+
callback URL; the local UI must surface `credential_authorization_required`
|
|
287
|
+
and resume after the user connects.
|
|
284
288
|
|
|
285
289
|
Deploy the project first, then provision its connections with
|
|
286
290
|
`mda connections create`. Agent-owned opaque secrets are scoped to that
|
|
287
291
|
deployment, so the command refuses to create one before `mda deploy`.
|
|
288
|
-
`mda
|
|
289
|
-
workspace.
|
|
292
|
+
`mda connections check` verifies manifest slugs exist in the workspace.
|
|
290
293
|
|
|
291
294
|
Use `include_tools` or `exclude_tools` inside a server config to select a
|
|
292
295
|
subset. Tool names are raw MCP tool names before the managed `{server}__` prefix
|
{managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/RECORD
RENAMED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
managed_deepagents/__init__.py,sha256=RezkpgYxCcywj6_W7Nk3PN9TMBfYyFgIatxGrIeY8V4,1862
|
|
2
2
|
managed_deepagents/__main__.py,sha256=GcjmqN0Ea5v8OHKMxYToasm3eCX_doSLOlpEzAi1tAw,67
|
|
3
|
-
managed_deepagents/_agent_auth.py,sha256=
|
|
3
|
+
managed_deepagents/_agent_auth.py,sha256=u9qvBjC1FM1U4M96M_juGqR7XI_0VtGEr6GgF9lcZJ0,19886
|
|
4
4
|
managed_deepagents/_agent_auth_http.py,sha256=aCYn7Rrtw3cnK3r4ir4JWxwzFkb7cF7nBgPpNdydxDw,5108
|
|
5
5
|
managed_deepagents/_channel.py,sha256=-Og7QOM1yvV-yeTDOfv2CxXpR2HLY4xdX1bLrIsPi5k,5272
|
|
6
6
|
managed_deepagents/_cli.py,sha256=jVaw2i1ysdRoWziMdPFmRXtKry_baYGRspyzyIeGWoU,1688
|
|
7
|
-
managed_deepagents/_connections.py,sha256=
|
|
7
|
+
managed_deepagents/_connections.py,sha256=rZnt9CI5BY16N66kR-bgx4pPoAmhNgtV73ejl7IkXhc,19378
|
|
8
8
|
managed_deepagents/_connector.py,sha256=Syp0SRs1oFCUUltIUmZbSNi1Ys_PxObVdiAi-I9GBzs,8040
|
|
9
9
|
managed_deepagents/_cors.py,sha256=V96RPrqewdRWG5RpEMEBnPuRq9_zF8nf4vf99k5dkuI,7597
|
|
10
10
|
managed_deepagents/_credential_gate.py,sha256=SNxXunqmHgjBsZpiZu8yp49YkRIO0amkl0-7GHLGnIg,12280
|
|
11
11
|
managed_deepagents/_eval_filesystem.py,sha256=1nuD0yn3gh19JrWNocWyPFyeJW42E7Gse3XRWCUSTAA,1399
|
|
12
12
|
managed_deepagents/_extension.py,sha256=x2H1S8inkvTZQ_dr7kq0M8o8rre1FjzCDvMzItw4iyk,5318
|
|
13
|
-
managed_deepagents/_identity_auth.py,sha256=
|
|
13
|
+
managed_deepagents/_identity_auth.py,sha256=TvZLzab47LIkLOps3c5yrVuuoekkTc8Mn4bbWOc4zXk,33546
|
|
14
14
|
managed_deepagents/_identity_http.py,sha256=aVQfZr7UKQoXF1Dry3p9fzXIj1882sEa668rvhn37i0,16029
|
|
15
15
|
managed_deepagents/_identity_links.py,sha256=Z1H2Seo2KaI-vcdU3_PGM-F06wOz0IQqUP-28wqiQsA,9172
|
|
16
|
-
managed_deepagents/_identity_runtime.py,sha256=
|
|
16
|
+
managed_deepagents/_identity_runtime.py,sha256=FJeJkhCg0Wo8x8hwiLB-9MUS-VBsGVzBzk3JJaDqH0o,24847
|
|
17
17
|
managed_deepagents/_ingress.py,sha256=iqsGm2jZKKn0FqjNn2nJAlR7d-IqCpVKWo6NbMEhjxw,12323
|
|
18
18
|
managed_deepagents/_log.py,sha256=oYwOO0ZL4-INkge9YJvtLuCxL9zAtgjSPIggVbkcyc8,2932
|
|
19
19
|
managed_deepagents/_loopback.py,sha256=hQ1oNi77XpdUjzZURSBRoMvJ1mGCs-STobfBftGmXuE,2409
|
|
20
20
|
managed_deepagents/_managed_middleware.py,sha256=cfInVHTVjWoy_DBoFlnslfrDf2_n6llGnTGasta4AIo,10295
|
|
21
21
|
managed_deepagents/_managed_tools.py,sha256=KsFDULbv5fv0RY3Nj0zbv6z0Y80_o5YazhqeiQ9PnPk,9105
|
|
22
22
|
managed_deepagents/_memory_instructions.py,sha256=lyxKfNuXrWih574gmy3NHhIq_hIpJIM5zl3P8V69mUs,6678
|
|
23
|
-
managed_deepagents/_validated_token.py,sha256=
|
|
23
|
+
managed_deepagents/_validated_token.py,sha256=d13DiRReoxSK5Q2kIPTLeXCFx2tjCIHJOi5DZexr7fk,17397
|
|
24
24
|
managed_deepagents/define_deep_agent.py,sha256=17VfiiBINyhlqlATHrlJRPihKVlBsDGDP8K4-fxjxU8,5611
|
|
25
25
|
managed_deepagents/define_memory.py,sha256=n7Ya20dWGvwbZOkPIXoa68nTnJie8tpKEkRwfJQt_fU,5036
|
|
26
26
|
managed_deepagents/namespaces.py,sha256=GS76G19-V5Svfpbca-9xhQfJ6vel52jcJyfj6loLt_Y,4199
|
|
@@ -28,7 +28,7 @@ managed_deepagents/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
|
28
28
|
managed_deepagents/runtime.py,sha256=B4iKTJZD0zXx_MZ09pqQFOkVo07tqJDT3qvOBr-0iOk,65782
|
|
29
29
|
managed_deepagents/sandbox.py,sha256=xlpcWa6CoVX6GKip2nK-0Lw-HBCoFGFYm0r4x9eYwcY,2530
|
|
30
30
|
managed_deepagents/schedules.py,sha256=MtTQN4tlm1NizFDCjZZv_huGfbQx-dj9WP1dIBybCug,5237
|
|
31
|
-
managed_deepagents/_binaries/mda.exe,sha256=
|
|
31
|
+
managed_deepagents/_binaries/mda.exe,sha256=DAmmuhOtkqW4j92gqsKuXvseKHNVHSJR4SZTPOEeTWA,3905536
|
|
32
32
|
managed_deepagents/_channels/__init__.py,sha256=Z6eXmzaLxG09DSyJoY_CX34axcsBo9gSJ0G7pRm2D6M,1216
|
|
33
33
|
managed_deepagents/_channels/assistant_text.py,sha256=f-Z3J3PlQFXZHztgBxlRoqB2LigU1Hj1YpwoXBMTR18,2171
|
|
34
34
|
managed_deepagents/_channels/binding.py,sha256=6vAKbWpg-eyLErJ0SsozH4mZc50hA1NhquM63mVmWDQ,3423
|
|
@@ -65,13 +65,13 @@ managed_deepagents/_memory/mounts.py,sha256=b2KtaTX3apTkmoqQxG2ENtkO86avSt1Nvrv3
|
|
|
65
65
|
managed_deepagents/_memory/paths.py,sha256=gHI-9KyawUv1ftX1K0kjaBbM4IYLeP7S-_WCaS2gVBM,353
|
|
66
66
|
managed_deepagents/_memory/slices.py,sha256=ljL78ECmDZ27xc3ajzkLliOfUln_bNr-d1ycC1wlS38,3559
|
|
67
67
|
managed_deepagents/_memory/types.py,sha256=scSk6mH0k2OC59wzUCa67szvXylSjWDrfzWWRIU9tZA,4336
|
|
68
|
-
managed_deepagents/identity/__init__.py,sha256=
|
|
68
|
+
managed_deepagents/identity/__init__.py,sha256=3KHXJca8w9Xu99_QhJX9iV1xMHTa34NS1cRPKVRx408,16342
|
|
69
69
|
managed_deepagents/identity/claims.py,sha256=prby7r_8Lww3MmoBm8PX6PsSGE3GwSHkr8mVIz_08nU,1180
|
|
70
70
|
managed_deepagents/identity/options.py,sha256=x_yMbXEFR7zXyehEjDHG2qjAlukXjrRBcQLcM6IndLM,2550
|
|
71
71
|
managed_deepagents/identity/supabase.py,sha256=8uobl3WCrQL_ieSXG8KTLf1th7Qjim9liZT50HeaJE0,6026
|
|
72
|
-
managed_deepagents-0.6.2.
|
|
73
|
-
managed_deepagents-0.6.2.
|
|
74
|
-
managed_deepagents-0.6.2.
|
|
75
|
-
managed_deepagents-0.6.2.
|
|
76
|
-
managed_deepagents-0.6.2.
|
|
77
|
-
managed_deepagents-0.6.2.
|
|
72
|
+
managed_deepagents-0.6.2.dev44.dist-info/licenses/LICENSE,sha256=WnPZQQ608ojZUkXl7BW6VbKloIAibZiGm1K6cjeZOL8,1066
|
|
73
|
+
managed_deepagents-0.6.2.dev44.dist-info/METADATA,sha256=4mbNB9mNXciy5VVqWTsrBUBFC-3LtWDOiK_MJ1w37Ec,14039
|
|
74
|
+
managed_deepagents-0.6.2.dev44.dist-info/WHEEL,sha256=3I5VVWZdsFlU417aCS2bMRuerS8wsfvBq99MyatBV3A,97
|
|
75
|
+
managed_deepagents-0.6.2.dev44.dist-info/entry_points.txt,sha256=ziZqkKuaDO3qHCjKmBhrx9cm3ok8bZ9xcEDpUTj4unM,53
|
|
76
|
+
managed_deepagents-0.6.2.dev44.dist-info/top_level.txt,sha256=fdBo4O0Hlol_w022tTLB5ftZpamb4yJoQ3MUvjgTGfA,19
|
|
77
|
+
managed_deepagents-0.6.2.dev44.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{managed_deepagents-0.6.2.dev43.dist-info → managed_deepagents-0.6.2.dev44.dist-info}/top_level.txt
RENAMED
|
File without changes
|