managed-deepagents 0.6.2.dev20__py3-none-win_amd64.whl → 0.6.2.dev22__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/__init__.py +6 -8
- managed_deepagents/_agent_auth.py +119 -16
- managed_deepagents/_agent_auth_http.py +17 -5
- managed_deepagents/_binaries/mda.exe +0 -0
- managed_deepagents/_channels/manifest.py +2 -0
- managed_deepagents/{_credentials.py → _connections.py} +141 -145
- managed_deepagents/_connectors/mcp/__init__.py +49 -20
- managed_deepagents/_connectors/mcp/load.py +50 -43
- managed_deepagents/_credential_gate.py +86 -89
- {managed_deepagents-0.6.2.dev20.dist-info → managed_deepagents-0.6.2.dev22.dist-info}/METADATA +13 -10
- {managed_deepagents-0.6.2.dev20.dist-info → managed_deepagents-0.6.2.dev22.dist-info}/RECORD +15 -15
- {managed_deepagents-0.6.2.dev20.dist-info → managed_deepagents-0.6.2.dev22.dist-info}/WHEEL +0 -0
- {managed_deepagents-0.6.2.dev20.dist-info → managed_deepagents-0.6.2.dev22.dist-info}/entry_points.txt +0 -0
- {managed_deepagents-0.6.2.dev20.dist-info → managed_deepagents-0.6.2.dev22.dist-info}/licenses/LICENSE +0 -0
- {managed_deepagents-0.6.2.dev20.dist-info → managed_deepagents-0.6.2.dev22.dist-info}/top_level.txt +0 -0
managed_deepagents/__init__.py
CHANGED
|
@@ -9,18 +9,17 @@ This package provides two things:
|
|
|
9
9
|
|
|
10
10
|
Resources use ``define_*`` factories while provider collections use namespaces:
|
|
11
11
|
``define_sandbox()``, ``channels.slack(...)``, ``connectors.mcp(...)``,
|
|
12
|
-
``
|
|
12
|
+
``connections.get(...)``, and ``auth.supabase(...)``. Contributing a *new*
|
|
13
13
|
primitive is not part of the public surface.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
|
-
from managed_deepagents.
|
|
18
|
+
from managed_deepagents._connections import (
|
|
19
|
+
ConnectionNotFoundError,
|
|
19
20
|
CredentialAuthorizationRequiredError,
|
|
20
|
-
CredentialNotFoundError,
|
|
21
|
-
CredentialOwner,
|
|
22
21
|
CredentialStoreError,
|
|
23
|
-
|
|
22
|
+
connections,
|
|
24
23
|
)
|
|
25
24
|
from managed_deepagents._managed_tools import (
|
|
26
25
|
ManagedDeepAgentRuntime,
|
|
@@ -38,9 +37,8 @@ from managed_deepagents.sandbox import SandboxDefinition
|
|
|
38
37
|
from managed_deepagents.schedules import define_schedule
|
|
39
38
|
|
|
40
39
|
__all__ = [
|
|
40
|
+
"ConnectionNotFoundError",
|
|
41
41
|
"CredentialAuthorizationRequiredError",
|
|
42
|
-
"CredentialNotFoundError",
|
|
43
|
-
"CredentialOwner",
|
|
44
42
|
"CredentialStoreError",
|
|
45
43
|
"DeepAgentDefinition",
|
|
46
44
|
"ManagedDeepAgentRuntime",
|
|
@@ -49,8 +47,8 @@ __all__ = [
|
|
|
49
47
|
"SandboxDefinition",
|
|
50
48
|
"auth",
|
|
51
49
|
"channels",
|
|
50
|
+
"connections",
|
|
52
51
|
"connectors",
|
|
53
|
-
"credentials",
|
|
54
52
|
"define_deep_agent",
|
|
55
53
|
"define_identity",
|
|
56
54
|
"define_memory",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Agent Auth client used by ``
|
|
1
|
+
"""Agent Auth client used by ``connections.get`` and the pre-run credential gate.
|
|
2
2
|
|
|
3
3
|
Talks to the shipped Agent Auth APIs (credentials + oauth-authorization-sessions),
|
|
4
4
|
not the aspirational ``credentials/mint`` endpoint.
|
|
@@ -33,6 +33,7 @@ __all__ = [
|
|
|
33
33
|
"AgentAuthUnknownProviderError",
|
|
34
34
|
"CredentialAuthorizationRequiredError",
|
|
35
35
|
"PendingAuthorization",
|
|
36
|
+
"WorkspaceConnection",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
|
|
@@ -48,17 +49,24 @@ class AgentAuthContext:
|
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
class PendingAuthorization(TypedDict):
|
|
51
|
-
"""One
|
|
52
|
+
"""One connection slug the caller has not granted, with its connect URL."""
|
|
52
53
|
|
|
53
54
|
slug: str
|
|
54
55
|
connect_url: str
|
|
55
56
|
auth_id: str
|
|
56
57
|
|
|
57
58
|
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
|
+
class WorkspaceConnection:
|
|
61
|
+
connection_id: str
|
|
62
|
+
kind: str
|
|
63
|
+
owner_policy: str
|
|
64
|
+
|
|
65
|
+
|
|
58
66
|
class CredentialAuthorizationRequiredError(RuntimeError):
|
|
59
67
|
def __init__(self, slugs: list[str]) -> None:
|
|
60
68
|
super().__init__(
|
|
61
|
-
f"the caller has not connected: {', '.join(slugs)}. Declare the
|
|
69
|
+
f"the caller has not connected: {', '.join(slugs)}. Declare the connection "
|
|
62
70
|
"on a connector so the pre-run gate can request it."
|
|
63
71
|
)
|
|
64
72
|
self.slugs = slugs
|
|
@@ -106,6 +114,80 @@ class AgentAuth:
|
|
|
106
114
|
self._timeout = timeout
|
|
107
115
|
self._request: AgentAuthRequest = request if request is not None else self._urlopen_json
|
|
108
116
|
|
|
117
|
+
def find_workspace_connection(self, slug: str) -> WorkspaceConnection | None:
|
|
118
|
+
"""List workspace connections and return the row for ``slug``, if any."""
|
|
119
|
+
if not (self.context.workspace_id or "").strip():
|
|
120
|
+
raise CredentialStoreError(
|
|
121
|
+
"connections are not configured: LANGSMITH_WORKSPACE_ID is not set"
|
|
122
|
+
)
|
|
123
|
+
cursor: str | None = None
|
|
124
|
+
for _ in range(AGENT_AUTH_MAX_PROVIDER_PAGES):
|
|
125
|
+
query: dict[str, str] = {
|
|
126
|
+
"page_size": str(AGENT_AUTH_PROVIDER_PAGE_SIZE),
|
|
127
|
+
}
|
|
128
|
+
if cursor:
|
|
129
|
+
query["cursor"] = cursor
|
|
130
|
+
status, body = self._request(
|
|
131
|
+
"GET",
|
|
132
|
+
f"/v1/agent-auth/connections?{urllib.parse.urlencode(query)}",
|
|
133
|
+
)
|
|
134
|
+
if status >= _HTTP_BAD_REQUEST:
|
|
135
|
+
raise CredentialStoreError("Agent Auth list connections failed", status)
|
|
136
|
+
raw_items = body.get("items")
|
|
137
|
+
items = raw_items if isinstance(raw_items, list) else []
|
|
138
|
+
for item in items:
|
|
139
|
+
if not isinstance(item, Mapping) or item.get("slug") != slug:
|
|
140
|
+
continue
|
|
141
|
+
connection_id = (
|
|
142
|
+
item.get("id") or item.get("connection_id") or item.get("credential_id")
|
|
143
|
+
)
|
|
144
|
+
kind = item.get("kind")
|
|
145
|
+
owner_policy = item.get("owner_policy")
|
|
146
|
+
if not isinstance(connection_id, str) or not connection_id:
|
|
147
|
+
raise CredentialStoreError("Agent Auth connection row missing id or kind")
|
|
148
|
+
if not isinstance(kind, str) or not kind:
|
|
149
|
+
raise CredentialStoreError("Agent Auth connection row missing id or kind")
|
|
150
|
+
return WorkspaceConnection(
|
|
151
|
+
connection_id=connection_id,
|
|
152
|
+
kind=kind,
|
|
153
|
+
owner_policy=str(owner_policy) if isinstance(owner_policy, str) else "",
|
|
154
|
+
)
|
|
155
|
+
next_cursor = body.get("next_cursor")
|
|
156
|
+
if not isinstance(next_cursor, str) or not next_cursor or next_cursor == cursor:
|
|
157
|
+
return None
|
|
158
|
+
cursor = next_cursor
|
|
159
|
+
raise CredentialStoreError("Agent Auth connection list returned too many pages")
|
|
160
|
+
|
|
161
|
+
def find_workspace_credential_id(self, slug: str) -> str | None:
|
|
162
|
+
"""Workspace credential id for an agent-owned opaque secret grant."""
|
|
163
|
+
workspace_id = (self.context.workspace_id or "").strip()
|
|
164
|
+
if not workspace_id:
|
|
165
|
+
raise CredentialStoreError(
|
|
166
|
+
"connections are not configured: LANGSMITH_WORKSPACE_ID is not set"
|
|
167
|
+
)
|
|
168
|
+
return self.find_credential_id(
|
|
169
|
+
owner_type="workspace",
|
|
170
|
+
owner_id=workspace_id,
|
|
171
|
+
kind="secret",
|
|
172
|
+
slug=slug,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
def read_user_secret(self, slug: str) -> str | None:
|
|
176
|
+
"""Read a caller-owned opaque secret grant for a workspace connection slug."""
|
|
177
|
+
credential_id = self.find_credential_id(
|
|
178
|
+
owner_type="user",
|
|
179
|
+
owner_id=self.context.principal_id,
|
|
180
|
+
kind="secret",
|
|
181
|
+
slug=slug,
|
|
182
|
+
)
|
|
183
|
+
if credential_id is None:
|
|
184
|
+
return None
|
|
185
|
+
return self.read_secret(credential_id)
|
|
186
|
+
|
|
187
|
+
def read_connection_access_token(self, connection_id: str) -> str | None:
|
|
188
|
+
"""Read an OAuth access token stored on a workspace connection row."""
|
|
189
|
+
return self._fetch_access_token(connection_id)
|
|
190
|
+
|
|
109
191
|
def read_user_access_token(self, slug: str) -> str | None:
|
|
110
192
|
"""Read the caller's OAuth access token without starting a session.
|
|
111
193
|
|
|
@@ -128,12 +210,16 @@ class AgentAuth:
|
|
|
128
210
|
|
|
129
211
|
def start_authorization_session(self, slug: str) -> PendingAuthorization:
|
|
130
212
|
"""Start an OAuth session for a slug the caller has not granted."""
|
|
131
|
-
|
|
213
|
+
connection = self.find_workspace_connection(slug)
|
|
214
|
+
if connection is None:
|
|
215
|
+
raise CredentialStoreError(f"workspace connection {slug!r} is not configured")
|
|
216
|
+
if connection.kind != "oauth2":
|
|
217
|
+
raise CredentialStoreError(f"connection {slug!r} is not an OAuth connection")
|
|
132
218
|
status, body = self._request(
|
|
133
219
|
"POST",
|
|
134
220
|
"/v1/agent-auth/oauth-authorization-sessions",
|
|
135
221
|
body={
|
|
136
|
-
"
|
|
222
|
+
"connection_id": connection.connection_id,
|
|
137
223
|
"principal_id": self.context.principal_id,
|
|
138
224
|
"agent_id": self.context.agent_id,
|
|
139
225
|
},
|
|
@@ -182,18 +268,35 @@ class AgentAuth:
|
|
|
182
268
|
) -> str | None:
|
|
183
269
|
"""Find a credential id by slug.
|
|
184
270
|
|
|
185
|
-
The
|
|
271
|
+
The shipped credentials list filters by owner and kind only, so the
|
|
272
|
+
slug match is made client-side across pages. The credential key is
|
|
273
|
+
``credential_id``, never ``id``.
|
|
186
274
|
"""
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
275
|
+
cursor: str | None = None
|
|
276
|
+
for _ in range(AGENT_AUTH_MAX_PROVIDER_PAGES):
|
|
277
|
+
query = agent_auth_credential_query(
|
|
278
|
+
owner_type=owner_type,
|
|
279
|
+
owner_id=owner_id,
|
|
280
|
+
kind=kind,
|
|
281
|
+
page_size=AGENT_AUTH_PROVIDER_PAGE_SIZE,
|
|
282
|
+
cursor=cursor,
|
|
283
|
+
)
|
|
284
|
+
status, body = self._request("GET", f"/v1/agent-auth/credentials?{query}")
|
|
285
|
+
if status >= _HTTP_BAD_REQUEST:
|
|
286
|
+
raise CredentialStoreError("Agent Auth list credentials failed", status)
|
|
287
|
+
raw_items = body.get("items")
|
|
288
|
+
items = raw_items if isinstance(raw_items, list) else []
|
|
289
|
+
for item in items:
|
|
290
|
+
if not isinstance(item, Mapping) or item.get("slug") != slug:
|
|
291
|
+
continue
|
|
292
|
+
credential_id = item.get("credential_id")
|
|
293
|
+
if isinstance(credential_id, str) and credential_id:
|
|
294
|
+
return credential_id
|
|
295
|
+
next_cursor = body.get("next_cursor")
|
|
296
|
+
if not isinstance(next_cursor, str) or not next_cursor or next_cursor == cursor:
|
|
297
|
+
return None
|
|
298
|
+
cursor = next_cursor
|
|
299
|
+
raise CredentialStoreError("Agent Auth credential list returned too many pages")
|
|
197
300
|
|
|
198
301
|
def read_secret(self, credential_id: str) -> str | None:
|
|
199
302
|
status, body = self._request(
|
|
@@ -46,12 +46,24 @@ def agent_auth_credential_query(
|
|
|
46
46
|
owner_type: str,
|
|
47
47
|
owner_id: str,
|
|
48
48
|
kind: str,
|
|
49
|
-
|
|
49
|
+
page_size: int | None = None,
|
|
50
|
+
cursor: str | None = None,
|
|
50
51
|
) -> str:
|
|
51
|
-
"""Query string
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
"""Query string for listing credentials filtered by owner and kind.
|
|
53
|
+
|
|
54
|
+
The shipped list endpoint does not filter by slug; callers match ``slug``
|
|
55
|
+
client-side across pages.
|
|
56
|
+
"""
|
|
57
|
+
params: dict[str, str] = {
|
|
58
|
+
"owner_type": owner_type,
|
|
59
|
+
"owner_id": owner_id,
|
|
60
|
+
"kind": kind,
|
|
61
|
+
}
|
|
62
|
+
if page_size is not None:
|
|
63
|
+
params["page_size"] = str(page_size)
|
|
64
|
+
if cursor:
|
|
65
|
+
params["cursor"] = cursor
|
|
66
|
+
return urllib.parse.urlencode(params)
|
|
55
67
|
|
|
56
68
|
|
|
57
69
|
def agent_auth_headers(
|
|
Binary file
|
|
@@ -17,6 +17,8 @@ JsonObject = dict[str, JsonValue]
|
|
|
17
17
|
class _OptionalChannelManifest(TypedDict, total=False):
|
|
18
18
|
transport: Literal["trigger_server"]
|
|
19
19
|
userCredentials: list[str]
|
|
20
|
+
userConnections: list[str]
|
|
21
|
+
agentConnections: list[str]
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
class ChannelManifest(_OptionalChannelManifest):
|