managed-deepagents 0.6.2.dev25__py3-none-win_amd64.whl → 0.6.2.dev29__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.
@@ -19,6 +19,7 @@ from managed_deepagents._connections import (
19
19
  ConnectionNotFoundError,
20
20
  CredentialAuthorizationRequiredError,
21
21
  CredentialStoreError,
22
+ InvalidAgentAuthEndpointError,
22
23
  connections,
23
24
  )
24
25
  from managed_deepagents._managed_tools import (
@@ -41,6 +42,7 @@ __all__ = [
41
42
  "CredentialAuthorizationRequiredError",
42
43
  "CredentialStoreError",
43
44
  "DeepAgentDefinition",
45
+ "InvalidAgentAuthEndpointError",
44
46
  "ManagedDeepAgentRuntime",
45
47
  "ManagedServerInfo",
46
48
  "MemoryDefinition",
@@ -46,6 +46,8 @@ class AgentAuthContext:
46
46
  principal_id: str
47
47
  api_key: str | None = None
48
48
  workspace_id: str | None = None
49
+ #: Agent Builder service JWT for workspace-owned credential material.
50
+ service_key: str | None = None
49
51
 
50
52
 
51
53
  class PendingAuthorization(TypedDict):
@@ -141,7 +143,7 @@ class AgentAuth:
141
143
  connection_id = (
142
144
  item.get("id") or item.get("connection_id") or item.get("credential_id")
143
145
  )
144
- kind = item.get("kind")
146
+ kind = _connection_credential_kind(item)
145
147
  owner_policy = item.get("owner_policy")
146
148
  if not isinstance(connection_id, str) or not connection_id:
147
149
  raise CredentialStoreError("Agent Auth connection row missing id or kind")
@@ -160,18 +162,62 @@ class AgentAuth:
160
162
 
161
163
  def find_workspace_credential_id(self, slug: str) -> str | None:
162
164
  """Workspace credential id for an agent-owned opaque secret grant."""
165
+ connection = self.find_workspace_connection(slug)
166
+ if connection is None:
167
+ return None
168
+ return self.find_secret_credential_id(connection, slug)
169
+
170
+ def find_secret_credential_id(self, connection: WorkspaceConnection, slug: str) -> str | None:
171
+ """Resolve the credential id for a secret connection row.
172
+
173
+ With an Agent Builder service key, prefer GET-for-owner / workspace list.
174
+ Personal API keys may only list agent-owned credentials.
175
+ """
176
+ if connection.kind != "secret":
177
+ return None
163
178
  workspace_id = (self.context.workspace_id or "").strip()
164
179
  if not workspace_id:
165
180
  raise CredentialStoreError(
166
181
  "connections are not configured: LANGSMITH_WORKSPACE_ID is not set"
167
182
  )
183
+ if (self.context.service_key or "").strip():
184
+ from_owner = self._credential_id_for_owner(
185
+ connection.connection_id, "workspace", workspace_id
186
+ )
187
+ if from_owner is not None:
188
+ return from_owner
189
+ return self.find_credential_id(
190
+ owner_type="workspace",
191
+ owner_id=workspace_id,
192
+ kind="secret",
193
+ slug=slug,
194
+ )
168
195
  return self.find_credential_id(
169
- owner_type="workspace",
170
- owner_id=workspace_id,
196
+ owner_type="agent",
197
+ owner_id=self.context.agent_id,
171
198
  kind="secret",
172
199
  slug=slug,
173
200
  )
174
201
 
202
+ def _credential_id_for_owner(
203
+ self, connection_id: str, owner_type: str, owner_id: str
204
+ ) -> str | None:
205
+ query = urllib.parse.urlencode(
206
+ {
207
+ "credential_owner_type": owner_type,
208
+ "credential_owner_id": owner_id,
209
+ }
210
+ )
211
+ status, body = self._request(
212
+ "GET",
213
+ f"/v1/agent-auth/connections/{_path_segment(connection_id)}?{query}",
214
+ )
215
+ if status == _HTTP_NOT_FOUND:
216
+ return None
217
+ if status >= _HTTP_BAD_REQUEST:
218
+ raise CredentialStoreError("Agent Auth get connection for owner failed", status)
219
+ return _connection_credential_id(body)
220
+
175
221
  def read_user_secret(self, slug: str) -> str | None:
176
222
  """Read a caller-owned opaque secret grant for a workspace connection slug."""
177
223
  credential_id = self.find_credential_id(
@@ -344,6 +390,7 @@ class AgentAuth:
344
390
  return agent_auth_headers(
345
391
  api_key=self.context.api_key,
346
392
  workspace_id=self.context.workspace_id,
393
+ service_key=self.context.service_key,
347
394
  )
348
395
 
349
396
 
@@ -351,6 +398,24 @@ def _path_segment(value: str) -> str:
351
398
  return urllib.parse.quote(value, safe="")
352
399
 
353
400
 
401
+ def _connection_credential_kind(item: Mapping[str, object]) -> str:
402
+ credential = item.get("credential")
403
+ if isinstance(credential, Mapping):
404
+ kind = credential.get("kind")
405
+ if isinstance(kind, str) and kind:
406
+ return kind
407
+ kind = item.get("kind")
408
+ return kind if isinstance(kind, str) else ""
409
+
410
+
411
+ def _connection_credential_id(body: Mapping[str, object]) -> str | None:
412
+ credential = body.get("credential")
413
+ if not isinstance(credential, Mapping):
414
+ return None
415
+ credential_id = credential.get("credential_id")
416
+ return credential_id if isinstance(credential_id, str) and credential_id else None
417
+
418
+
354
419
  def _is_authorization_required(status: int, body: dict[str, object]) -> bool:
355
420
  """Agent Auth answers 409 when a stored grant has lapsed and needs re-auth."""
356
421
  if status != _HTTP_CONFLICT:
@@ -70,12 +70,16 @@ def agent_auth_headers(
70
70
  *,
71
71
  api_key: str | None = None,
72
72
  workspace_id: str | None = None,
73
+ service_key: str | None = None,
73
74
  ) -> dict[str, str]:
74
75
  headers = {
75
76
  "accept": "application/json",
76
77
  "content-type": "application/json",
77
78
  "x-langsmith-product": "mda",
78
79
  }
80
+ resolved_service_key = (service_key or "").strip()
81
+ if resolved_service_key:
82
+ headers["x-service-key"] = resolved_service_key
79
83
  resolved_api_key = (api_key or "").strip()
80
84
  if resolved_api_key:
81
85
  headers["x-api-key"] = resolved_api_key
Binary file
@@ -47,6 +47,7 @@ __all__ = [
47
47
  "ConnectionRequest",
48
48
  "CredentialAuthorizationRequiredError",
49
49
  "CredentialStoreError",
50
+ "InvalidAgentAuthEndpointError",
50
51
  "agent_connection_env_key",
51
52
  "connections",
52
53
  "is_connection_reference",
@@ -132,6 +133,15 @@ _API_KEY_ENV = "LANGSMITH_API_KEY"
132
133
  _WORKSPACE_ID_ENV = "LANGSMITH_WORKSPACE_ID"
133
134
 
134
135
 
136
+ class InvalidAgentAuthEndpointError(CredentialStoreError):
137
+ """``LANGSMITH_ENDPOINT`` is not a usable Agent Auth origin."""
138
+
139
+ def __init__(self) -> None:
140
+ super().__init__(
141
+ f"LangSmith Agent Auth endpoint is not a valid URL (set {PLATFORM_API_URL_ENV})"
142
+ )
143
+
144
+
135
145
  class ConnectionHttpResponse(TypedDict):
136
146
  status: int
137
147
  body: bytes
@@ -147,6 +157,7 @@ class _AgentAuthConfig:
147
157
  api_key: str
148
158
  deployment_id: str | None
149
159
  workspace_id: str
160
+ service_key: str | None = None
150
161
 
151
162
 
152
163
  class AgentAuthConnectionStore:
@@ -177,6 +188,7 @@ class AgentAuthConnectionStore:
177
188
  principal_id=owner_id,
178
189
  api_key=config.api_key,
179
190
  workspace_id=config.workspace_id,
191
+ service_key=config.service_key,
180
192
  ),
181
193
  request=self._bind_request(config),
182
194
  timeout=self._timeout,
@@ -200,7 +212,11 @@ class AgentAuthConnectionStore:
200
212
  ) -> tuple[int, dict[str, object]]:
201
213
  del method, body
202
214
  url = f"{config.base_url}{path}"
203
- headers = agent_auth_headers(api_key=config.api_key, workspace_id=config.workspace_id)
215
+ headers = agent_auth_headers(
216
+ api_key=config.api_key,
217
+ workspace_id=config.workspace_id,
218
+ service_key=config.service_key,
219
+ )
204
220
  response = http_get(url, headers, timeout)
205
221
  status = response.get("status")
206
222
  raw_body = response.get("body", b"")
@@ -260,7 +276,7 @@ def _lookup_agent_connection_material(
260
276
  row: WorkspaceConnection,
261
277
  ) -> str | None:
262
278
  if row.kind == "secret":
263
- credential_id = client.find_workspace_credential_id(slug)
279
+ credential_id = client.find_secret_credential_id(row, slug)
264
280
  if credential_id is None:
265
281
  return None
266
282
  return client.read_secret(credential_id)
@@ -413,6 +429,7 @@ def resolve_agent_auth_context(
413
429
  principal_id=resolve_owner_id({"type": "user"}, identity, config.deployment_id),
414
430
  api_key=config.api_key,
415
431
  workspace_id=config.workspace_id,
432
+ service_key=config.service_key,
416
433
  )
417
434
 
418
435
 
@@ -426,22 +443,34 @@ def _resolve_agent_auth_config(env: Mapping[str, str] | None) -> _AgentAuthConfi
426
443
  raise CredentialStoreError(
427
444
  f"connections are not configured: {_WORKSPACE_ID_ENV} is not set"
428
445
  )
446
+ service_key = (resolved.get("MDA_AGENT_AUTH_SERVICE_KEY") or "").strip() or None
429
447
  return _AgentAuthConfig(
430
448
  base_url=resolve_agent_auth_base_url(resolved),
431
449
  api_key=api_key,
432
450
  deployment_id=(resolved.get(DEPLOYMENT_ID_ENV) or "").strip() or None,
433
451
  workspace_id=workspace_id,
452
+ service_key=service_key,
434
453
  )
435
454
 
436
455
 
437
456
  def resolve_agent_auth_base_url(env: Mapping[str, str] | None = None) -> str:
457
+ """Origin for Agent Auth HTTP calls from ``LANGSMITH_ENDPOINT``.
458
+
459
+ Cloud SaaS revisions omit that reserved name and inject
460
+ ``LANGCHAIN_ENDPOINT`` instead; read it as the same setting so we do not
461
+ default to production.
462
+ """
438
463
  resolved = os.environ if env is None else env
439
- raw = (resolved.get(PLATFORM_API_URL_ENV) or "").strip() or _DEFAULT_LANGSMITH_ENDPOINT
464
+ raw = (
465
+ (resolved.get(PLATFORM_API_URL_ENV) or "").strip()
466
+ or (resolved.get("LANGCHAIN_ENDPOINT") or "").strip()
467
+ or _DEFAULT_LANGSMITH_ENDPOINT
468
+ )
440
469
  try:
441
470
  parsed = urllib.parse.urlsplit(raw.rstrip("/"))
442
471
  _ = parsed.port
443
472
  except ValueError:
444
- raise CredentialStoreError(f"{PLATFORM_API_URL_ENV} is not a valid URL") from None
473
+ raise InvalidAgentAuthEndpointError from None
445
474
  if (
446
475
  parsed.scheme not in {"http", "https"}
447
476
  or not parsed.hostname
@@ -451,7 +480,7 @@ def resolve_agent_auth_base_url(env: Mapping[str, str] | None = None) -> str:
451
480
  or parsed.query
452
481
  or parsed.fragment
453
482
  ):
454
- raise CredentialStoreError(f"{PLATFORM_API_URL_ENV} is not a valid URL")
483
+ raise InvalidAgentAuthEndpointError
455
484
  return f"{parsed.scheme}://{parsed.netloc}"
456
485
 
457
486
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: managed-deepagents
3
- Version: 0.6.2.dev25
3
+ Version: 0.6.2.dev29
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
@@ -282,8 +282,8 @@ when a grant is missing. `mda dev` has no deployment to hold user grants, so
282
282
  a user-owned connection fails there with an actionable error; deploy the agent
283
283
  to exercise one.
284
284
 
285
- Provision workspace connections with `mda connection create` before deploy.
286
- `mda connection check` verifies manifest slugs exist in the workspace.
285
+ Provision workspace connections with `mda connections create` before deploy.
286
+ `mda connections check` verifies manifest slugs exist in the workspace.
287
287
 
288
288
  Use `include_tools` or `exclude_tools` inside a server config to select a
289
289
  subset. Tool names are raw MCP tool names before the managed `{server}__` prefix
@@ -1,10 +1,10 @@
1
- managed_deepagents/__init__.py,sha256=KSwxB3Sdu3az-TKh1r6aYWp3nus0DMJJdvPtN68Fl1w,1790
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=Pj4UJzIOoNz3cuO35jp17xvE0yBbFVeAW6FZA25APAE,14101
4
- managed_deepagents/_agent_auth_http.py,sha256=xMp6bVNrgEoR1xnUKAFSjPfvMO1o9m1CO-_vmaI5sOs,3748
3
+ managed_deepagents/_agent_auth.py,sha256=3rA39j_3PFROrtsIr5OHkAO_FgAHfASM-ee0vGhPGu8,16650
4
+ managed_deepagents/_agent_auth_http.py,sha256=DtspEFmWHTtpp3uqELw1-wuHiLbAx_ers9JW18J3Coc,3924
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=sy_ZDO6ZlgEm3NioF1i776GNcPATgb8Zn4gMBHMt158,15598
7
+ managed_deepagents/_connections.py,sha256=N6luj1YMpBPjpH-WnGc6AIGNTE9DnZoBWAlWcFRSo2I,16518
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=WlryhbWHtg1FN5IdadiOvgT3JX3G78DtWVGW0wLWvz4,12252
@@ -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=XZjv2YMdWq9BDc7DgFLJ4-kt3NUskewROt7ypb0yCj8,3855360
31
+ managed_deepagents/_binaries/mda.exe,sha256=2tNegm-g-AkSnDb_mHCMIVwJNX47E6AXbRfpM74WQag,3858432
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
@@ -68,9 +68,9 @@ managed_deepagents/identity/__init__.py,sha256=Slm-oEqKxbaZEQsMqHfD3LbG5M4vH2lKU
68
68
  managed_deepagents/identity/claims.py,sha256=prby7r_8Lww3MmoBm8PX6PsSGE3GwSHkr8mVIz_08nU,1180
69
69
  managed_deepagents/identity/options.py,sha256=x_yMbXEFR7zXyehEjDHG2qjAlukXjrRBcQLcM6IndLM,2550
70
70
  managed_deepagents/identity/supabase.py,sha256=8uobl3WCrQL_ieSXG8KTLf1th7Qjim9liZT50HeaJE0,6026
71
- managed_deepagents-0.6.2.dev25.dist-info/licenses/LICENSE,sha256=WnPZQQ608ojZUkXl7BW6VbKloIAibZiGm1K6cjeZOL8,1066
72
- managed_deepagents-0.6.2.dev25.dist-info/METADATA,sha256=yqZkx8zOkbSgz9Ydkeqjgzkx0IGBr3O_wfYezDqCNtg,13580
73
- managed_deepagents-0.6.2.dev25.dist-info/WHEEL,sha256=3I5VVWZdsFlU417aCS2bMRuerS8wsfvBq99MyatBV3A,97
74
- managed_deepagents-0.6.2.dev25.dist-info/entry_points.txt,sha256=ziZqkKuaDO3qHCjKmBhrx9cm3ok8bZ9xcEDpUTj4unM,53
75
- managed_deepagents-0.6.2.dev25.dist-info/top_level.txt,sha256=fdBo4O0Hlol_w022tTLB5ftZpamb4yJoQ3MUvjgTGfA,19
76
- managed_deepagents-0.6.2.dev25.dist-info/RECORD,,
71
+ managed_deepagents-0.6.2.dev29.dist-info/licenses/LICENSE,sha256=WnPZQQ608ojZUkXl7BW6VbKloIAibZiGm1K6cjeZOL8,1066
72
+ managed_deepagents-0.6.2.dev29.dist-info/METADATA,sha256=XkIgC0l4YyYRSgaLIuI97rr2Li4jElapfveTwg_EiZ4,13582
73
+ managed_deepagents-0.6.2.dev29.dist-info/WHEEL,sha256=3I5VVWZdsFlU417aCS2bMRuerS8wsfvBq99MyatBV3A,97
74
+ managed_deepagents-0.6.2.dev29.dist-info/entry_points.txt,sha256=ziZqkKuaDO3qHCjKmBhrx9cm3ok8bZ9xcEDpUTj4unM,53
75
+ managed_deepagents-0.6.2.dev29.dist-info/top_level.txt,sha256=fdBo4O0Hlol_w022tTLB5ftZpamb4yJoQ3MUvjgTGfA,19
76
+ managed_deepagents-0.6.2.dev29.dist-info/RECORD,,