managed-deepagents 0.6.2.dev48__py3-none-win_amd64.whl → 0.6.2.dev50__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.
@@ -16,6 +16,7 @@ from managed_deepagents._agent_auth_http import (
16
16
  CredentialStoreError,
17
17
  agent_auth_credential_query,
18
18
  agent_auth_headers,
19
+ missing_workspace_connection_error,
19
20
  request_agent_auth_json,
20
21
  )
21
22
 
@@ -34,48 +35,21 @@ __all__ = [
34
35
  "AgentAuthUnknownProviderError",
35
36
  "CredentialAuthorizationRequiredError",
36
37
  "PendingAuthorization",
37
- "ResolvePrincipalIdentity",
38
38
  "WorkspaceConnection",
39
- "clear_principal_resolve_cache_for_tests",
40
39
  ]
41
40
 
42
41
 
43
42
  @dataclass(frozen=True)
44
43
  class AgentAuthContext:
45
44
  base_url: str
45
+ #: Deployment id, passed to oauth-authorization-sessions as ``agent_id``.
46
+ agent_id: str
46
47
  #: Authenticated caller, used as ``owner_id`` for user credentials.
47
48
  principal_id: str
48
- #: Deployment id when present. Required for agent-owned lookups; optional for user OAuth.
49
- agent_id: str | None = None
50
49
  api_key: str | None = None
51
50
  workspace_id: str | None = None
52
51
 
53
52
 
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(
69
- context: AgentAuthContext, identity: ResolvePrincipalIdentity
70
- ) -> str:
71
- origin = context.base_url.rstrip("/")
72
- workspace = (context.workspace_id or "").strip()
73
- return (
74
- f"{origin}\0{workspace}\0"
75
- f"{identity['provider']}\0{identity['issuer']}\0{identity['subject']}"
76
- )
77
-
78
-
79
53
  class PendingAuthorization(TypedDict):
80
54
  """One connection slug the caller has not granted, with its connect URL."""
81
55
 
@@ -142,49 +116,6 @@ class AgentAuth:
142
116
  self._timeout = timeout
143
117
  self._request: AgentAuthRequest = request if request is not None else self._urlopen_json
144
118
 
145
- def resolve_principal(self, identity: ResolvePrincipalIdentity) -> str:
146
- """Resolve a verified external identity to an Agent Auth principal UUID."""
147
- provider = identity["provider"].strip()
148
- issuer = identity["issuer"].strip()
149
- subject = identity["subject"].strip()
150
- if not provider or not issuer or not subject:
151
- raise CredentialStoreError(
152
- "Agent Auth principal resolve requires provider, issuer, and subject"
153
- )
154
- key = _principal_resolve_cache_key(
155
- self.context, {"provider": provider, "issuer": issuer, "subject": subject}
156
- )
157
- cached = _principal_resolve_cache.get(key)
158
- if cached is not None:
159
- return cached
160
- principal_id = self._resolve_principal_uncached(
161
- {"provider": provider, "issuer": issuer, "subject": subject}
162
- )
163
- _principal_resolve_cache[key] = principal_id
164
- return principal_id
165
-
166
- def _resolve_principal_uncached(self, identity: ResolvePrincipalIdentity) -> str:
167
- status, body = self._request(
168
- "POST",
169
- "/v1/agent-auth/principals",
170
- body={
171
- "provider": identity["provider"],
172
- "issuer": identity["issuer"],
173
- "subject": identity["subject"],
174
- },
175
- )
176
- if status >= _HTTP_BAD_REQUEST:
177
- raise CredentialStoreError("Agent Auth resolve principal failed", status, body)
178
- principal = body.get("principal")
179
- principal_id = ""
180
- if isinstance(principal, Mapping):
181
- raw = principal.get("id")
182
- if isinstance(raw, str):
183
- principal_id = raw.strip()
184
- if not principal_id:
185
- raise CredentialStoreError("Agent Auth resolve principal returned no principal id")
186
- return principal_id
187
-
188
119
  def find_workspace_connection(self, slug: str) -> WorkspaceConnection | None:
189
120
  """List workspace connections and return the row for ``slug``, if any."""
190
121
  if not (self.context.workspace_id or "").strip():
@@ -245,17 +176,14 @@ class AgentAuth:
245
176
  """
246
177
  if connection.kind != "secret":
247
178
  return None
248
- agent_id = (self.context.agent_id or "").strip()
249
- if not agent_id:
250
- raise CredentialStoreError(
251
- "connections are not configured: LANGSMITH_HOST_PROJECT_ID is not set"
252
- )
253
- from_agent = self._credential_id_for_owner(connection.connection_id, "agent", agent_id)
179
+ from_agent = self._credential_id_for_owner(
180
+ connection.connection_id, "agent", self.context.agent_id
181
+ )
254
182
  if from_agent is not None:
255
183
  return from_agent
256
184
  return self.find_credential_id(
257
185
  owner_type="agent",
258
- owner_id=agent_id,
186
+ owner_id=self.context.agent_id,
259
187
  kind="secret",
260
188
  slug=slug,
261
189
  )
@@ -342,7 +270,7 @@ class AgentAuth:
342
270
  """Start an OAuth session for a slug the caller has not granted."""
343
271
  connection = self.find_workspace_connection(slug)
344
272
  if connection is None:
345
- raise CredentialStoreError(f"workspace connection {slug!r} is not configured")
273
+ raise missing_workspace_connection_error(slug)
346
274
  if connection.kind != "oauth2":
347
275
  raise CredentialStoreError(f"connection {slug!r} is not an OAuth connection")
348
276
  status, body = self._request(
@@ -350,8 +278,8 @@ class AgentAuth:
350
278
  "/v1/agent-auth/oauth-authorization-sessions",
351
279
  body={
352
280
  "connection_id": connection.connection_id,
353
- "owner_type": "user",
354
- "owner_id": self.context.principal_id,
281
+ "principal_id": self.context.principal_id,
282
+ "agent_id": self.context.agent_id,
355
283
  },
356
284
  )
357
285
  if status >= _HTTP_BAD_REQUEST:
@@ -7,6 +7,7 @@ API with the same headers, redirect policy, and page walker.
7
7
  from __future__ import annotations
8
8
 
9
9
  import json
10
+ import re
10
11
  import urllib.error
11
12
  import urllib.parse
12
13
  import urllib.request
@@ -66,6 +67,27 @@ class CredentialStoreError(RuntimeError):
66
67
  super().__init__(with_status if hint is None else f"{with_status}: {hint}")
67
68
 
68
69
 
70
+ # Each runtime quotes the slug its own way: repr() here, JSON.stringify in npm.
71
+ _MISSING_WORKSPACE_CONNECTION = re.compile(
72
+ r"""workspace connection ['"]([A-Za-z0-9][A-Za-z0-9._-]*)['"] is not configured"""
73
+ )
74
+
75
+
76
+ def missing_workspace_connection_error(slug: str) -> CredentialStoreError:
77
+ """Return the error for a declared slug that has no workspace connection row."""
78
+ return CredentialStoreError(f"workspace connection {slug!r} is not configured")
79
+
80
+
81
+ def missing_workspace_connection_slug(text: str) -> str | None:
82
+ """Read the slug back out of a missing-connection message.
83
+
84
+ A run that fails in the gate reaches a channel as a plain string, so this is
85
+ the only way back to the slug. It must move with the message it parses.
86
+ """
87
+ match = _MISSING_WORKSPACE_CONNECTION.search(text)
88
+ return None if match is None else match.group(1)
89
+
90
+
69
91
  __all__ = [
70
92
  "AGENT_AUTH_PROBLEM_HINT_MAX_CHARS",
71
93
  "AGENT_AUTH_TIMEOUT_SECONDS",
@@ -73,6 +95,8 @@ __all__ = [
73
95
  "agent_auth_credential_query",
74
96
  "agent_auth_headers",
75
97
  "agent_auth_problem_hint",
98
+ "missing_workspace_connection_error",
99
+ "missing_workspace_connection_slug",
76
100
  "request_agent_auth_json",
77
101
  ]
78
102
 
Binary file
@@ -9,6 +9,7 @@ from collections.abc import Awaitable, Callable, Mapping
9
9
  from importlib import import_module
10
10
  from typing import Protocol, TypeVar
11
11
 
12
+ from managed_deepagents._agent_auth_http import missing_workspace_connection_slug
12
13
  from managed_deepagents._channels.assistant_text import extract_assistant_text
13
14
  from managed_deepagents._channels.trigger.client import (
14
15
  LANGSMITH_MDA_TRIGGER_SERVER_URL_ENV,
@@ -242,6 +243,7 @@ async def _observe_accepted_run(
242
243
  async def lifecycle(
243
244
  event_type: TriggerChannelRuntimeEventType,
244
245
  interrupts: list[TriggerChannelInterrupt] | None = None,
246
+ message: str | None = None,
245
247
  ) -> None:
246
248
  logger.info(
247
249
  "Channel outgoing event (type=%s, delivery_id=%s, channel_id=%s, run_id=%s)",
@@ -253,7 +255,7 @@ async def _observe_accepted_run(
253
255
  await _best_effort(
254
256
  client.post_runtime_event(
255
257
  event["channel_id"],
256
- _runtime_event(event_type, event, started, interrupts),
258
+ _runtime_event(event_type, event, started, interrupts, message),
257
259
  )
258
260
  )
259
261
 
@@ -266,8 +268,8 @@ async def _observe_accepted_run(
266
268
  source_thread_key=source_thread_key,
267
269
  user_id=user_id,
268
270
  )
269
- except Exception: # noqa: BLE001 - observation details stay internal
270
- await lifecycle("run.failed")
271
+ except Exception as error: # noqa: BLE001 - only known-safe details leave runtime
272
+ await lifecycle("run.failed", message=_missing_connection_failure_message(error))
271
273
  return
272
274
 
273
275
  if _is_interrupted_run(observed):
@@ -391,6 +393,7 @@ def _runtime_event(
391
393
  event: TriggerChannelEvent,
392
394
  started: StartedIngressRun,
393
395
  interrupts: list[TriggerChannelInterrupt] | None = None,
396
+ message: str | None = None,
394
397
  ) -> TriggerChannelRuntimeEventRequest:
395
398
  request: TriggerChannelRuntimeEventRequest = {
396
399
  "id": str(uuid.uuid4()),
@@ -401,11 +404,24 @@ def _runtime_event(
401
404
  "run_thread_id": started["thread_id"],
402
405
  "run_id": started["run_id"],
403
406
  }
407
+ if message:
408
+ request["message"] = {"text": message}
404
409
  if interrupts:
405
410
  request["interrupts"] = interrupts
406
411
  return request
407
412
 
408
413
 
414
+ def _missing_connection_failure_message(error: Exception) -> str | None:
415
+ slug = missing_workspace_connection_slug(str(error))
416
+ if slug is None:
417
+ return None
418
+ return (
419
+ f"This agent can't respond because the `{slug}` connection isn't configured. "
420
+ "An agent developer can configure it with "
421
+ f"`mda connections create {slug} --project <project>`, then try again."
422
+ )
423
+
424
+
409
425
  def _thread_id(source_thread_key: str) -> str:
410
426
  return str(uuid.uuid5(uuid.NAMESPACE_URL, source_thread_key))
411
427
 
@@ -119,6 +119,8 @@ class TriggerChannelRuntimeEventRequestRequired(TypedDict):
119
119
 
120
120
 
121
121
  class TriggerChannelRuntimeEventRequest(TriggerChannelRuntimeEventRequestRequired, total=False):
122
+ # Safe, user-facing status text. Raw runtime exceptions must never be sent.
123
+ message: TriggerChannelActionMessage
122
124
  # Present on `run.interrupted`. The Trigger Server cannot read the
123
125
  # deployment's threads, so pending interrupts travel on the event itself.
124
126
  interrupts: list[TriggerChannelInterrupt]
@@ -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 material.
5
+ reads Agent Auth on every await and never caches.
6
6
 
7
7
  ``mda dev`` substitutes local values for agent opaque secrets via ``MDA_DEV_*``.
8
- User-owned connections talk to Agent Auth in every environment (including
9
- ``mda dev``) after resolving the caller's Agent Auth principal.
8
+ User-owned connections need a deployment; opaque user grants use the same
9
+ pre-run gate as OAuth.
10
10
  """
11
11
 
12
12
  from __future__ import annotations
@@ -24,9 +24,7 @@ from managed_deepagents._agent_auth import (
24
24
  AgentAuth,
25
25
  AgentAuthContext,
26
26
  CredentialAuthorizationRequiredError,
27
- ResolvePrincipalIdentity,
28
27
  WorkspaceConnection,
29
- clear_principal_resolve_cache_for_tests,
30
28
  )
31
29
  from managed_deepagents._agent_auth_http import (
32
30
  AGENT_AUTH_TIMEOUT_SECONDS,
@@ -51,12 +49,10 @@ __all__ = [
51
49
  "CredentialStoreError",
52
50
  "InvalidAgentAuthEndpointError",
53
51
  "agent_connection_env_key",
54
- "clear_principal_resolve_cache_for_tests",
55
52
  "connections",
56
53
  "is_connection_reference",
57
54
  "resolve_agent_auth_base_url",
58
55
  "resolve_agent_auth_context",
59
- "resolve_credential_principal_id",
60
56
  "resolve_owner_id",
61
57
  ]
62
58
 
@@ -112,7 +108,19 @@ class ConnectionNotFoundError(LookupError):
112
108
  _SLUG_PATTERN = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
113
109
  _MAX_SLUG_LENGTH = 64
114
110
  _DEV_CREDENTIAL_PREFIX = "MDA_DEV_"
115
- _TRUSTED_BACKEND_PROVIDER = "trusted_backend"
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
+ )
116
124
 
117
125
 
118
126
  #: Env var naming the LangSmith platform API that serves Agent Auth.
@@ -123,8 +131,6 @@ _DEFAULT_LANGSMITH_ENDPOINT = "https://api.smith.langchain.com"
123
131
  DEPLOYMENT_ID_ENV = "LANGSMITH_HOST_PROJECT_ID"
124
132
  _API_KEY_ENV = "LANGSMITH_API_KEY"
125
133
  _WORKSPACE_ID_ENV = "LANGSMITH_WORKSPACE_ID"
126
- _LOCAL_DEV_USER = "mda:local-dev"
127
- _LOCAL_DEV_PRINCIPAL_ENV = "MDA_AGENT_AUTH_PRINCIPAL_ID"
128
134
 
129
135
 
130
136
  class InvalidAgentAuthEndpointError(CredentialStoreError):
@@ -151,7 +157,6 @@ class _AgentAuthConfig:
151
157
  api_key: str
152
158
  deployment_id: str | None
153
159
  workspace_id: str
154
- local_dev_principal_id: str | None
155
160
 
156
161
 
157
162
  class AgentAuthConnectionStore:
@@ -171,29 +176,15 @@ class AgentAuthConnectionStore:
171
176
  async def fetch(self, request: ConnectionRequest) -> str | None:
172
177
  owner = request["owner"]
173
178
  resolved = os.environ if self._env is None else self._env
174
- if resolved.get("MDA_LOCAL_DEV") == "1" and owner["type"] == "agent":
175
- return _fetch_local_dev_agent_connection(resolved, request["slug"])
179
+ if resolved.get("MDA_LOCAL_DEV") == "1":
180
+ return _fetch_local_dev_connection(resolved, request["slug"], owner)
176
181
  config = _resolve_agent_auth_config(self._env)
177
- if owner["type"] == "agent" and not config.deployment_id:
178
- raise CredentialStoreError(
179
- f"connections are not configured: {DEPLOYMENT_ID_ENV} is not set"
180
- )
181
- # Source-identity match still uses JWT sub / channel actor.
182
- resolve_owner_id(owner, request["identity"], config.deployment_id)
183
- if owner["type"] == "user":
184
- principal_id = resolve_credential_principal_id(
185
- request["identity"],
186
- config,
187
- request_fn=self._bind_request(config),
188
- timeout=self._timeout,
189
- )
190
- else:
191
- principal_id = resolve_owner_id(owner, request["identity"], config.deployment_id)
182
+ owner_id = resolve_owner_id(owner, request["identity"], config.deployment_id)
192
183
  client = AgentAuth(
193
184
  AgentAuthContext(
194
185
  base_url=config.base_url,
195
- agent_id=config.deployment_id,
196
- principal_id=principal_id,
186
+ agent_id=config.deployment_id or owner_id,
187
+ principal_id=owner_id,
197
188
  api_key=config.api_key,
198
189
  workspace_id=config.workspace_id,
199
190
  ),
@@ -217,14 +208,12 @@ class AgentAuthConnectionStore:
217
208
  *,
218
209
  body: dict[str, object] | None = None,
219
210
  ) -> tuple[int, dict[str, object]]:
211
+ del method, body
220
212
  url = f"{config.base_url}{path}"
221
213
  headers = agent_auth_headers(
222
214
  api_key=config.api_key,
223
215
  workspace_id=config.workspace_id,
224
216
  )
225
- # Test injects only support GET-shaped responses; path still distinguishes
226
- # principals vs connections for mock routers.
227
- del method, body
228
217
  response = http_get(url, headers, timeout)
229
218
  status = response.get("status")
230
219
  raw_body = response.get("body", b"")
@@ -238,7 +227,13 @@ class AgentAuthConnectionStore:
238
227
  return request
239
228
 
240
229
 
241
- def _fetch_local_dev_agent_connection(resolved: Mapping[str, str], slug: str) -> str:
230
+ def _fetch_local_dev_connection(
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))
242
237
  value = resolved.get(agent_connection_env_key(slug))
243
238
  if value is not None and value.strip():
244
239
  return value
@@ -415,105 +410,24 @@ def resolve_owner_id(
415
410
  def resolve_agent_auth_context(
416
411
  identity: RuntimeIdentity | None,
417
412
  env: Mapping[str, str] | None = None,
418
- *,
419
- request_fn: Callable[..., tuple[int, dict[str, object]]] | None = None,
420
- timeout: float = AGENT_AUTH_TIMEOUT_SECONDS,
421
413
  ) -> 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())
422
417
  config = _resolve_agent_auth_config(env)
423
- principal_id = resolve_credential_principal_id(
424
- identity, config, request_fn=request_fn, timeout=timeout
425
- )
418
+ if not config.deployment_id:
419
+ raise CredentialStoreError(
420
+ f"connections are not configured: {DEPLOYMENT_ID_ENV} is not set"
421
+ )
426
422
  return AgentAuthContext(
427
423
  base_url=config.base_url,
428
424
  agent_id=config.deployment_id,
429
- principal_id=principal_id,
425
+ principal_id=resolve_owner_id({"type": "user"}, identity, config.deployment_id),
430
426
  api_key=config.api_key,
431
427
  workspace_id=config.workspace_id,
432
428
  )
433
429
 
434
430
 
435
- def resolve_credential_principal_id(
436
- identity: RuntimeIdentity | None,
437
- config: _AgentAuthConfig,
438
- *,
439
- request_fn: Callable[..., tuple[int, dict[str, object]]] | None = None,
440
- timeout: float = AGENT_AUTH_TIMEOUT_SECONDS,
441
- ) -> str:
442
- """Map a runtime identity to the Agent Auth principal used for user grants."""
443
- if identity is None:
444
- raise CredentialStoreError("user connections require an authenticated user principal")
445
- user = identity.get("user") or {}
446
- user_id = user.get("id") if isinstance(user, Mapping) else None
447
- if not isinstance(user_id, str) or not user_id.strip():
448
- raise CredentialStoreError("user connections require an authenticated user principal")
449
- kind = user.get("kind") if isinstance(user, Mapping) else None
450
- if kind == "service":
451
- if (
452
- user_id.strip() == _LOCAL_DEV_USER
453
- and config.local_dev_principal_id is not None
454
- ):
455
- return config.local_dev_principal_id
456
- raise CredentialStoreError(
457
- "user-owned connections require a person principal; "
458
- "Studio and other service callers cannot own OAuth grants"
459
- )
460
- stamped = identity.get("agent_auth_principal_id")
461
- if isinstance(stamped, str) and stamped.strip():
462
- return stamped.strip()
463
-
464
- subject = user_id.strip()
465
- external = _external_identity_for_resolve(identity, config.workspace_id)
466
- if external is None:
467
- raise CredentialStoreError(
468
- "user-owned connections need a resolvable identity "
469
- "(validated-token issuer/subject, channel identity_context, or trusted backend)"
470
- )
471
- bootstrap = AgentAuth(
472
- AgentAuthContext(
473
- base_url=config.base_url,
474
- principal_id=subject,
475
- api_key=config.api_key,
476
- workspace_id=config.workspace_id,
477
- agent_id=config.deployment_id,
478
- ),
479
- request=request_fn,
480
- timeout=timeout,
481
- )
482
- return bootstrap.resolve_principal(external)
483
-
484
-
485
- def _external_identity_for_resolve(
486
- identity: RuntimeIdentity,
487
- workspace_id: str | None,
488
- ) -> ResolvePrincipalIdentity | None:
489
- user = identity.get("user") or {}
490
- subject = user.get("id") if isinstance(user, Mapping) else None
491
- if not isinstance(subject, str) or not subject.strip():
492
- return None
493
- subject = subject.strip()
494
- provider = identity.get("identity_provider")
495
- claims = identity.get("claims")
496
- issuer = None
497
- if isinstance(claims, Mapping):
498
- raw_iss = claims.get("iss")
499
- if isinstance(raw_iss, str) and raw_iss.strip():
500
- issuer = raw_iss.strip()
501
- if isinstance(provider, str) and provider.strip() and issuer:
502
- return {
503
- "provider": provider.strip(),
504
- "issuer": issuer,
505
- "subject": subject,
506
- }
507
- workspace = (workspace_id or "").strip()
508
- if not workspace:
509
- return None
510
- return {
511
- "provider": _TRUSTED_BACKEND_PROVIDER,
512
- "issuer": workspace,
513
- "subject": subject,
514
- }
515
-
516
-
517
431
  def _resolve_agent_auth_config(env: Mapping[str, str] | None) -> _AgentAuthConfig:
518
432
  resolved = os.environ if env is None else env
519
433
  api_key = (resolved.get(_API_KEY_ENV) or "").strip()
@@ -529,11 +443,6 @@ def _resolve_agent_auth_config(env: Mapping[str, str] | None) -> _AgentAuthConfi
529
443
  api_key=api_key,
530
444
  deployment_id=(resolved.get(DEPLOYMENT_ID_ENV) or "").strip() or None,
531
445
  workspace_id=workspace_id,
532
- local_dev_principal_id=(
533
- (resolved.get(_LOCAL_DEV_PRINCIPAL_ENV) or "").strip() or None
534
- if resolved.get("MDA_LOCAL_DEV") == "1"
535
- else None
536
- ),
537
446
  )
538
447
 
539
448
 
@@ -20,8 +20,8 @@ from managed_deepagents._agent_auth import (
20
20
  AgentAuth,
21
21
  AgentAuthContext,
22
22
  CredentialAuthorizationRequiredError,
23
- CredentialStoreError,
24
23
  PendingAuthorization,
24
+ missing_workspace_connection_error,
25
25
  )
26
26
  from managed_deepagents._connections import (
27
27
  ConnectionReference,
@@ -187,7 +187,7 @@ def _read_user_connection_grants(
187
187
  slug = reference.slug
188
188
  row = agent_auth.find_workspace_connection(slug)
189
189
  if row is None:
190
- raise CredentialStoreError(f"workspace connection {slug!r} is not configured")
190
+ raise missing_workspace_connection_error(slug)
191
191
  kind: Literal["oauth2", "secret"] = "secret" if row.kind == "secret" else "oauth2"
192
192
  token = (
193
193
  agent_auth.read_user_secret_for_connection(row)
@@ -58,10 +58,6 @@ class ManagedUser(ManagedUserRequired, total=False):
58
58
  mda_source_provider: str
59
59
  mda_source_thread_id: str
60
60
  mda_user_kind: Literal["user", "service"]
61
- #: Validated-token provider id for Agent Auth principal resolve.
62
- mda_identity_provider: str
63
- #: Agent Auth principal UUID when Trigger ``identity_context`` already resolved one.
64
- mda_agent_auth_principal_id: str
65
61
 
66
62
 
67
63
  if TYPE_CHECKING:
@@ -92,8 +88,6 @@ INGRESS_SECRET_HEADER = "x-mda-ingress-secret" # noqa: S105 - header name, not
92
88
  #: Internal handoff headers — accepted after a server API key or legacy ingress secret validates.
93
89
  SOURCE_PROVIDER_HEADER = "x-mda-source-provider"
94
90
  SOURCE_THREAD_HEADER = "x-mda-source-thread-id"
95
- #: Trigger loopback stamp for a verified identity_context principal UUID.
96
- AGENT_AUTH_PRINCIPAL_HEADER = "x-mda-agent-auth-principal-id"
97
91
 
98
92
  #: Deployment env var carrying the shared secret the trusted backend presents.
99
93
  INGRESS_SECRET_ENV = "MDA_INGRESS_SECRET" # noqa: S105 - env var name, not a secret
@@ -483,9 +477,6 @@ def _trusted_headers_user(headers: Mapping[str, object]) -> ManagedUser:
483
477
  if source_provider is not None and source_thread is not None:
484
478
  user["mda_source_provider"] = str(source_provider)
485
479
  user["mda_source_thread_id"] = str(source_thread)
486
- principal = str(headers.get(AGENT_AUTH_PRINCIPAL_HEADER) or "").strip()
487
- if principal:
488
- user["mda_agent_auth_principal_id"] = principal
489
480
  return user
490
481
 
491
482
 
@@ -506,13 +497,7 @@ def _has_valid_handoff_secret(headers: Mapping[str, object]) -> bool:
506
497
 
507
498
  def _has_handoff_headers(headers: Mapping[str, object]) -> bool:
508
499
  return any(
509
- header in headers
510
- for header in (
511
- USER_HEADER,
512
- SOURCE_PROVIDER_HEADER,
513
- SOURCE_THREAD_HEADER,
514
- AGENT_AUTH_PRINCIPAL_HEADER,
515
- )
500
+ header in headers for header in (USER_HEADER, SOURCE_PROVIDER_HEADER, SOURCE_THREAD_HEADER)
516
501
  )
517
502
 
518
503
 
@@ -455,12 +455,6 @@ 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()
464
458
  # Immutable: tools/middleware get a read-only view they cannot tamper with.
465
459
  return cast("RuntimeIdentity", _freeze(identity))
466
460
 
@@ -19,7 +19,6 @@ from managed_deepagents._channels.assistant_text import extract_assistant_text
19
19
  from managed_deepagents._channels.binding import CHANNEL_BINDING_KEY, ChannelBinding
20
20
  from managed_deepagents._channels.types import ChannelError
21
21
  from managed_deepagents._identity_auth import (
22
- AGENT_AUTH_PRINCIPAL_HEADER,
23
22
  HANDOFF_SECRET_ENV,
24
23
  HANDOFF_SECRET_HEADER,
25
24
  INGRESS_SECRET_ENV,
@@ -356,7 +355,6 @@ def _create_trigger_ingress_loopback_client(
356
355
  )
357
356
  headers = {
358
357
  USER_HEADER: user_id,
359
- AGENT_AUTH_PRINCIPAL_HEADER: user_id,
360
358
  SOURCE_PROVIDER_HEADER: source["provider"],
361
359
  SOURCE_THREAD_HEADER: source["thread_key"],
362
360
  }
@@ -263,8 +263,6 @@ 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",
268
266
  }
269
267
  groups = normalize_groups(claims.get(claim_map["groups"])) if claim_map.get("groups") else []
270
268
  if groups:
@@ -177,10 +177,6 @@ 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
184
180
 
185
181
 
186
182
  class PrincipalRequired(TypedDict):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: managed-deepagents
3
- Version: 0.6.2.dev48
3
+ Version: 0.6.2.dev50
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,19 +277,16 @@ 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 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.
280
+ per caller (OAuth or opaque). The pre-run credential gate interrupts the run
281
+ when a grant is missing. `mda dev` has no deployment to hold user grants, so
282
+ a user-owned connection fails there with an actionable error; deploy the agent
283
+ to exercise one.
288
284
 
289
285
  Deploy the project first, then provision its connections with
290
286
  `mda connections create`. Agent-owned opaque secrets are scoped to that
291
287
  deployment, so the command refuses to create one before `mda deploy`.
292
- `mda connections check` verifies manifest slugs exist in the workspace.
288
+ `mda deploy` fails when a slug a project declares is missing from the
289
+ workspace.
293
290
 
294
291
  Use `include_tools` or `exclude_tools` inside a server config to select a
295
292
  subset. Tool names are raw MCP tool names before the managed `{server}__` prefix
@@ -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=TUZJgc1TyjEFNZX3E97VV30igM_bC9rDZ-pmKpUSExA,20010
4
- managed_deepagents/_agent_auth_http.py,sha256=aCYn7Rrtw3cnK3r4ir4JWxwzFkb7cF7nBgPpNdydxDw,5108
3
+ managed_deepagents/_agent_auth.py,sha256=DCGZNfMUpUEwLQT7tIODywmtzO3XjF4a1dixuDR_Fzs,17266
4
+ managed_deepagents/_agent_auth_http.py,sha256=d-Fg62m-_5qBXj-d9xgfA3KGiPRfs7T2g6Q2nrhCetc,6065
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=MlrmkVJTogZdDmh_vbj0aBzlZqliysTVTBKsYfEpqnU,19919
7
+ managed_deepagents/_connections.py,sha256=VwVBwM4T0rp1MgYu9D8QjGj1ireYeEuP3VIAFN5urFY,16246
8
8
  managed_deepagents/_connector.py,sha256=Syp0SRs1oFCUUltIUmZbSNi1Ys_PxObVdiAi-I9GBzs,8040
9
9
  managed_deepagents/_cors.py,sha256=V96RPrqewdRWG5RpEMEBnPuRq9_zF8nf4vf99k5dkuI,7597
10
- managed_deepagents/_credential_gate.py,sha256=SNxXunqmHgjBsZpiZu8yp49YkRIO0amkl0-7GHLGnIg,12280
10
+ managed_deepagents/_credential_gate.py,sha256=thc9dqWphPKbNqWYEZ1P63mpMDyUPDTw4uPA_qqnW28,12262
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=sykxsL2wzcJI7I0MFs4xkcoviQhfNQGIBFQ0rfP3cXU,33418
13
+ managed_deepagents/_identity_auth.py,sha256=l0t0Xx-J1-z_Zw4K5D-B2yV6lNdeJgOw7UvxODxUxZQ,32798
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=FJeJkhCg0Wo8x8hwiLB-9MUS-VBsGVzBzk3JJaDqH0o,24847
17
- managed_deepagents/_ingress.py,sha256=8vJqIuyKI3dxC5wbyRPL9ES71xsXgxiTWJqYPpG7Wt0,12402
16
+ managed_deepagents/_identity_runtime.py,sha256=vsshj0TNWd-rXUhVsPAqXIkANunQT7jI94CWgzq-1cY,24409
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=d13DiRReoxSK5Q2kIPTLeXCFx2tjCIHJOi5DZexr7fk,17397
23
+ managed_deepagents/_validated_token.py,sha256=BY-66bgAOxhhRS3tfkDWVVU2l9HFHAuYXXCKV1pZ-nE,17232
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=4aHz0or2mqRkLdwT4W6RoaWnUoQ16_NLQE-Tbz5vbbg,3912704
31
+ managed_deepagents/_binaries/mda.exe,sha256=MKHDoU4yrPlrylogVcQ8MmsJKXs7r6MzRI3OPV6z5fg,3904512
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
@@ -50,9 +50,9 @@ managed_deepagents/_channels/slack/transport.py,sha256=_xQmzK2hBpPOoY3zKFhzc3vvl
50
50
  managed_deepagents/_channels/trigger/__init__.py,sha256=bWj66XoPbDdrztPUCKBuSJBBS2pBocn4EA8FDE3WeBA,48
51
51
  managed_deepagents/_channels/trigger/channel.py,sha256=rKSvOABOZX58ZIvZ3hKcYxvTx9npQ0YXPQWk9JEy6XE,5108
52
52
  managed_deepagents/_channels/trigger/client.py,sha256=cM97gzYWUTFC9o3uoveFe9-HxZc6_VqiqC8u4fKqjXc,8572
53
- managed_deepagents/_channels/trigger/http.py,sha256=O8yMleioXj3s5QXj7jK-HpUpiSFppjfRjHJ2tdfX0Is,16205
53
+ managed_deepagents/_channels/trigger/http.py,sha256=75ROtdT7PacKAFa-KFFRPATkHDgC7Pu48AUcipTYXY0,16903
54
54
  managed_deepagents/_channels/trigger/identity_context.py,sha256=oo6sgtNDLGGkZCzrkrifMHkkAqxj7ui2HgUUdjNA1iU,7701
55
- managed_deepagents/_channels/trigger/types.py,sha256=42JMOFXP9MfqwQ66SFlAiwC7q_AnNIJXBts3sAujxac,6625
55
+ managed_deepagents/_channels/trigger/types.py,sha256=9nlArhLf-KgYidxwzrtBVjcpPlV6ytriPX3lJvPR3fE,6746
56
56
  managed_deepagents/_connectors/__init__.py,sha256=c_SdH3T7T6bgX7-I5jxAqDzHojmqVUCX0AXRI7g5Ovs,575
57
57
  managed_deepagents/_connectors/tool_selection.py,sha256=xMvohEvg_rguQvziUXzxKn4V140mmearZHel97F8tVs,2978
58
58
  managed_deepagents/_connectors/mcp/__init__.py,sha256=3AL429g5djYEYuiuSqsxj2VESpTkyU2NeBIJsHlWVk4,11162
@@ -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=3KHXJca8w9Xu99_QhJX9iV1xMHTa34NS1cRPKVRx408,16342
68
+ managed_deepagents/identity/__init__.py,sha256=Slm-oEqKxbaZEQsMqHfD3LbG5M4vH2lKUmralVPB_Zs,16137
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.dev48.dist-info/licenses/LICENSE,sha256=WnPZQQ608ojZUkXl7BW6VbKloIAibZiGm1K6cjeZOL8,1066
73
- managed_deepagents-0.6.2.dev48.dist-info/METADATA,sha256=anU1lZ3Ci1d9Afm2DO_vN-Si7XTAJUHSc6v6P6YN6M0,14039
74
- managed_deepagents-0.6.2.dev48.dist-info/WHEEL,sha256=3I5VVWZdsFlU417aCS2bMRuerS8wsfvBq99MyatBV3A,97
75
- managed_deepagents-0.6.2.dev48.dist-info/entry_points.txt,sha256=ziZqkKuaDO3qHCjKmBhrx9cm3ok8bZ9xcEDpUTj4unM,53
76
- managed_deepagents-0.6.2.dev48.dist-info/top_level.txt,sha256=fdBo4O0Hlol_w022tTLB5ftZpamb4yJoQ3MUvjgTGfA,19
77
- managed_deepagents-0.6.2.dev48.dist-info/RECORD,,
72
+ managed_deepagents-0.6.2.dev50.dist-info/licenses/LICENSE,sha256=WnPZQQ608ojZUkXl7BW6VbKloIAibZiGm1K6cjeZOL8,1066
73
+ managed_deepagents-0.6.2.dev50.dist-info/METADATA,sha256=jGDCHaAI4JMsFMrZ_fhKDqt6PVYObwGB3vD_HoY_fxU,13718
74
+ managed_deepagents-0.6.2.dev50.dist-info/WHEEL,sha256=3I5VVWZdsFlU417aCS2bMRuerS8wsfvBq99MyatBV3A,97
75
+ managed_deepagents-0.6.2.dev50.dist-info/entry_points.txt,sha256=ziZqkKuaDO3qHCjKmBhrx9cm3ok8bZ9xcEDpUTj4unM,53
76
+ managed_deepagents-0.6.2.dev50.dist-info/top_level.txt,sha256=fdBo4O0Hlol_w022tTLB5ftZpamb4yJoQ3MUvjgTGfA,19
77
+ managed_deepagents-0.6.2.dev50.dist-info/RECORD,,