mainsequence 9.0.26.dev70__py3-none-any.whl → 9.0.27.dev71__py3-none-any.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.
@@ -199,10 +199,8 @@ def publish_report(team_uid: str) -> dict[str, str]:
199
199
  Call it from a `def` handler, or from an `async def` handler with
200
200
  `await asyncio.to_thread(caller.in_teams, team_uids)`, which keeps the
201
201
  request's identity.
202
- - `team_uids` on the identity is deprecated and warns when read. In a signed
203
- HTTP request it raises `RequestIdentityError` when the assertion carried no
204
- team list, as every assertion will once the platform stops signing them.
205
- Replace every check that reads it with `in_teams()`.
202
+ - The identity has no `team_uids`; the platform signs no team list. Code
203
+ written for releases that had it reads `in_teams()` instead.
206
204
  - Membership is not team administration and not a grant on an object: check
207
205
  the exact object and action as well.
208
206
 
@@ -137,7 +137,7 @@ Keep these separate:
137
137
  - administration of the team itself
138
138
 
139
139
  An application that checks whether its caller or requester belongs to a team
140
- uses `in_teams()`, never the deprecated `team_uids`; see
140
+ uses `in_teams()`; the identity carries no team list. See
141
141
  `.agents/skills/mainsequence/application_surfaces/api_surfaces/SKILL.md`.
142
142
 
143
143
  ### 5. `Constant` vs `Secret` is a security boundary
@@ -308,9 +308,9 @@ When reviewing an access-control task, look for:
308
308
  - using direct user grants where a team should be used
309
309
  - using a team when the access is clearly one-off
310
310
  - treating team membership as team administration
311
- - team checks that read the deprecated `team_uids` instead of `in_teams()`,
312
- that read an `in_teams()` error as "not a member", or that reuse an older
313
- answer (no `max_age_seconds=0`) before a write that must see a removal
311
+ - team checks that do not use `in_teams()`, that read an `in_teams()` error
312
+ as "not a member", or that reuse an older answer (no `max_age_seconds=0`)
313
+ before a write that must see a removal
314
314
  - storing sensitive data in a `Constant`
315
315
  - creating a `Constant` or `Secret` blindly without resolving whether the name already exists
316
316
  - weak or unverified claims about who can access a resource
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  import datetime
4
4
  import math
5
- import warnings
6
5
  from collections.abc import Iterable
7
6
  from typing import Any, ClassVar, Literal
8
7
  from uuid import UUID
@@ -43,7 +42,7 @@ class RequestUserIdentity(BaseModel):
43
42
  ``in_teams()`` asks the platform which teams either of them belongs to.
44
43
  """
45
44
 
46
- model_config = ConfigDict(extra="forbid", frozen=True, populate_by_name=True)
45
+ model_config = ConfigDict(extra="forbid", frozen=True)
47
46
 
48
47
  uid: str = Field(
49
48
  ...,
@@ -58,17 +57,6 @@ class RequestUserIdentity(BaseModel):
58
57
  title="Username",
59
58
  description="Optional display name authenticated by the runtime gateway.",
60
59
  )
61
- signed_team_uids: tuple[str, ...] | None = Field(
62
- (),
63
- alias="team_uids",
64
- title="Signed team UIDs (deprecated)",
65
- description=(
66
- "Canonical UIDs of the active teams the platform signed in the caller "
67
- "assertion, None when it signed none. Deprecated: read membership with "
68
- "in_teams() (#225). Empty in local mode and on WebSockets, which carry no "
69
- "such facts."
70
- ),
71
- )
72
60
  is_organization_admin: bool = Field(
73
61
  False,
74
62
  title="Organization admin",
@@ -87,37 +75,6 @@ class RequestUserIdentity(BaseModel):
87
75
  except (AttributeError, TypeError, ValueError) as exc:
88
76
  raise ValueError("Request user UID must be a valid UUID.") from exc
89
77
 
90
- @field_validator("signed_team_uids", mode="before")
91
- @classmethod
92
- def _canonicalize_team_uids(cls, value: Any) -> tuple[str, ...] | None:
93
- if value is None:
94
- return None
95
- try:
96
- return tuple(str(UUID(str(item).strip())) for item in value)
97
- except (AttributeError, TypeError, ValueError) as exc:
98
- raise ValueError("Team UIDs must be valid UUIDs.") from exc
99
-
100
- @property
101
- def team_uids(self) -> tuple[str, ...]:
102
- """Deprecated: the team UIDs the platform signed. Use ``in_teams()``.
103
-
104
- Warns when read. When the caller assertion carried no team list it
105
- raises ``RequestIdentityError``; it never answers an empty list for
106
- that. Empty in local mode and on WebSockets, as before (#225).
107
- """
108
- warnings.warn(
109
- "RequestUserIdentity.team_uids is deprecated; check membership with "
110
- "in_teams() instead.",
111
- DeprecationWarning,
112
- stacklevel=2,
113
- )
114
- if self.signed_team_uids is None:
115
- raise RequestIdentityError(
116
- "The caller assertion carries no team list. Check team membership "
117
- "with in_teams()."
118
- )
119
- return self.signed_team_uids
120
-
121
78
  def in_teams(
122
79
  self, team_uids: Iterable[str | UUID], *, max_age_seconds: float | None = None
123
80
  ) -> frozenset[str]:
@@ -63,8 +63,6 @@ class _VerifiedRequester:
63
63
  """The person a requester-bound call works for, as the platform signed it."""
64
64
 
65
65
  user_uid: str
66
- # None when the assertion carries no team list (#225).
67
- team_uids: tuple[str, ...] | None
68
66
  is_organization_admin: bool
69
67
 
70
68
 
@@ -75,66 +73,44 @@ class AuthenticatedCaller:
75
73
  environment_uid: str
76
74
  issued_at: int
77
75
  expires_at: int
78
- # None when the assertion carries no team list (#225).
79
- team_uids: tuple[str, ...] | None
80
76
  is_organization_admin: bool
81
77
  # The person a requester-bound call works for, None on any other call. The
82
78
  # fields above stay the caller's: the acting application (ADR-0036).
83
79
  _requester: _VerifiedRequester | None = None
84
80
 
85
81
 
86
- def _team_uids(value: object, *, owner: str) -> tuple[str, ...]:
87
- if not isinstance(value, list):
88
- raise InvalidCallerAssertion(f"{owner} team UIDs are invalid.")
89
- team_uids = tuple(_canonical_uid(item) for item in value)
90
- if list(team_uids) != sorted(set(team_uids)):
91
- raise InvalidCallerAssertion(f"{owner} team UIDs must be sorted and unique.")
92
- return team_uids
93
-
94
-
95
82
  def _admin_flag(value: object, *, owner: str) -> bool:
96
83
  if type(value) is not bool:
97
84
  raise InvalidCallerAssertion(f"{owner} admin flag is invalid.")
98
85
  return value
99
86
 
100
87
 
101
- def _signed_team_uids(claims: dict, *, owner: str) -> tuple[str, ...] | None:
102
- """The deprecated ``team_uids`` while the platform still signs it, else None.
88
+ def _caller_admin_flag(payload: dict) -> bool:
89
+ """The caller's admin flag, after checking the exact claim set.
103
90
 
104
- Membership is checked on demand with ``RequestUserIdentity.in_teams()``
105
- (#225); a list that is still signed is checked as before.
91
+ The assertion names no teams: membership is checked on demand with
92
+ ``RequestUserIdentity.in_teams()`` (#225), and an assertion that lists
93
+ teams is refused.
106
94
  """
107
- if "team_uids" not in claims:
108
- return None
109
- return _team_uids(claims["team_uids"], owner=owner)
110
-
111
-
112
- def _caller_facts(payload: dict) -> tuple[tuple[str, ...] | None, bool]:
113
- """The caller's admin flag, and its team UIDs while the platform signs them."""
114
95
  # A requester-bound call also carries "requester", which _requester() checks.
115
- if set(payload) - {"requester", "team_uids"} != REQUIRED_CLAIMS:
96
+ if set(payload) - {"requester"} != REQUIRED_CLAIMS:
116
97
  raise InvalidCallerAssertion("Caller assertion claims are invalid.")
117
- return (
118
- _signed_team_uids(payload, owner="Caller"),
119
- _admin_flag(payload["is_organization_admin"], owner="Caller"),
120
- )
98
+ return _admin_flag(payload["is_organization_admin"], owner="Caller")
121
99
 
122
100
 
123
101
  def _requester(payload: dict) -> _VerifiedRequester | None:
124
102
  """The person a requester-bound call works for, or None on any other call.
125
103
 
126
104
  The claim has exactly ``sub`` and ``is_organization_admin``, the person's
127
- own User UID and Organization-admin flag, and may still carry the
128
- deprecated ``team_uids`` (#225).
105
+ own User UID and Organization-admin flag.
129
106
  """
130
107
  if "requester" not in payload:
131
108
  return None
132
109
  claim = payload["requester"]
133
- if not isinstance(claim, dict) or set(claim) - {"team_uids"} != REQUESTER_CLAIMS:
110
+ if not isinstance(claim, dict) or set(claim) != REQUESTER_CLAIMS:
134
111
  raise InvalidCallerAssertion("Caller assertion requester is invalid.")
135
112
  return _VerifiedRequester(
136
113
  user_uid=_canonical_uid(claim["sub"]),
137
- team_uids=_signed_team_uids(claim, owner="Requester"),
138
114
  is_organization_admin=_admin_flag(claim["is_organization_admin"], owner="Requester"),
139
115
  )
140
116
 
@@ -284,7 +260,7 @@ class CallerAssertionVerifier:
284
260
  audience=f"urn:mainsequence:fapi:{self.release_uid}",
285
261
  options={"require": list(REQUIRED_CLAIMS)},
286
262
  )
287
- team_uids, is_organization_admin = _caller_facts(payload)
263
+ is_organization_admin = _caller_admin_flag(payload)
288
264
  requester = _requester(payload)
289
265
  if (
290
266
  payload["aud"] != f"urn:mainsequence:fapi:{self.release_uid}"
@@ -304,7 +280,6 @@ class CallerAssertionVerifier:
304
280
  environment_uid=self.environment_uid,
305
281
  issued_at=iat,
306
282
  expires_at=exp,
307
- team_uids=team_uids,
308
283
  is_organization_admin=is_organization_admin,
309
284
  _requester=requester,
310
285
  )
@@ -229,7 +229,6 @@ class _RequestIdentityMiddleware:
229
229
  caller = await asyncio.to_thread(self.verifier.verify, assertion)
230
230
  user = RequestUserIdentity(
231
231
  uid=caller.user_uid,
232
- team_uids=caller.team_uids,
233
232
  is_organization_admin=caller.is_organization_admin,
234
233
  )
235
234
  state["resource_release_uid"] = caller.release_uid
@@ -244,7 +243,6 @@ class _RequestIdentityMiddleware:
244
243
  # The person this call works for, with the person's own facts.
245
244
  context.requester = RequestUserIdentity(
246
245
  uid=caller._requester.user_uid,
247
- team_uids=caller._requester.team_uids,
248
246
  is_organization_admin=caller._requester.is_organization_admin,
249
247
  )
250
248
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 9.0.26.dev70
3
+ Version: 9.0.27.dev71
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License-Expression: MIT
@@ -1,10 +1,10 @@
1
1
  agent_scaffold/skills/a2a_sdk_execution/SKILL.md,sha256=NcwIQ1QgD4Ku0mG2Hb-Izee8p9fm9LTPHa6gDEWTOSQ,13200
2
- agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md,sha256=f6Mn4VJsMz6DQgJgv4Wzi7spiPNXvO-eOKJBqj2ik58,26969
2
+ agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md,sha256=V2YW1oNlkKCr0_WwieVIZStl7rRfXlk02IdvkzyuneU,26820
3
3
  agent_scaffold/skills/mainsequence-inference/SKILL.md,sha256=yv8aAHLQ2FBBSWPIFadY7pGNTwtR-yiR_-UzaGRN9-g,12502
4
4
  agent_scaffold/skills/maintenance/bug_auditor/SKILL.md,sha256=8RRZNmC3omhZoxNbmu50MdhBFTQ2CU2THeyNu07Ax18,8659
5
5
  agent_scaffold/skills/maintenance/code_repository_maintenance/SKILL.md,sha256=pNQICoVW6nRr0HfvyB0Q64TdsqCXTWQUuImpBL-4XUQ,18575
6
6
  agent_scaffold/skills/maintenance/metatables_transition/SKILL.md,sha256=PisgpytcksJnfZMS6aiPCmahsT3fm8DIwk_cefXA2qU,3759
7
- agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md,sha256=Nw2ptUO70BJNUFrGlmzPscGrSKu61EHQgGKg45vBSDc,15280
7
+ agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md,sha256=sgAe4buJaMoatRRwBVWZvDcONY9mxJyqElUFX5swoqA,15249
8
8
  agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md,sha256=KfTptbOMO-b8hiVFPgKxRMjmQWZCLB9Sr_kli7kp4hs,16920
9
9
  agent_scaffold/skills/sdk_code_repository_execution/SKILL.md,sha256=g4C0Cp5oqQg2LBjT-piWXunmt_hNd5yTK33RMYPAE8I,7313
10
10
  mainsequence/__init__.py,sha256=70CbIo7ZG1mzi5uwkqDtVESunBE0RanHjeFg4ptfsA0,385
@@ -45,22 +45,22 @@ mainsequence/client/github_issues.py,sha256=pSTUw3UPrmE6wqnpoJctartTaNSq6iu6PQRr
45
45
  mainsequence/client/inference.py,sha256=tYKHRxLR6mA5iLmgd3qURwweWc2OPQuw0__tD3LQgSI,7748
46
46
  mainsequence/client/models_foundry.py,sha256=JwQdPLDSO4dZI-KC0SUdmN6izmNIKMb9IzZlYXy0Mig,36488
47
47
  mainsequence/client/models_helpers.py,sha256=CQRjBjx3DsREJLRk0vqFsC-Dcll5lUP2ofu6e5UMvhk,49051
48
- mainsequence/client/models_user.py,sha256=FESX_RYmY_qXXJ-VuZUo_C9Hwx0MrdHP_4b8wb4HycU,52256
48
+ mainsequence/client/models_user.py,sha256=Z3h6MX5aWKFfEvtwHPk1adhfPbG3RZCKbp3Aanor4lo,50502
49
49
  mainsequence/client/observability.py,sha256=_WrDKCaNk_TXmKElKCXC1inork6Exxxe8FRY4m1GI9s,16008
50
50
  mainsequence/client/utils.py,sha256=Rk2z-78swO6GepibLlQNdxL1eqYy8vHGrjiZdV3G5CM,31641
51
51
  mainsequence/client/value_sets.py,sha256=7TA-mpuAd3hOduNgO1WTQQTuNOGaFgue-VqCsDWKaC8,6181
52
52
  mainsequence/instrumentation/__init__.py,sha256=J_9XmfjprElvz0u2t9rTyJT0kFdyKpxdbkdQiaw28_U,316
53
53
  mainsequence/instrumentation/utils.py,sha256=b-ihjSioAepeLfMpudM2SqCtvnxMqIXileHx3klBCQU,3377
54
54
  mainsequence/server/__init__.py,sha256=BHUAhtdWPDRdxX07NYO_6wrBv5mf0GsXflecO52PmUc,73
55
- mainsequence/server/caller_assertions.py,sha256=AcE9PgkIAu8kMUILSRs6P--wN68DUw5a5D1PC7NQqIs,13116
56
- mainsequence/server/fastapi.py,sha256=zVPvtlkAZuuD1EgKk_9jPVjfBxOlFjf9aXjDyFlTC54,20250
55
+ mainsequence/server/caller_assertions.py,sha256=IUQ74UL3VqCAAuujTGk3DzCPogRYhWRD9rVoWezdWdU,11979
56
+ mainsequence/server/fastapi.py,sha256=M5zDoFIu9MzuQZdLOXvQPU9Yyqyf_GyXWVn_aravHH4,20123
57
57
  mainsequence/server/mcp_skills.py,sha256=-krRwAHCAMipScgGJ19uVkzznhTop1Nz4C_wRY1GUpw,16690
58
58
  mainsequence/server/_mcp_skills_upstream/__init__.py,sha256=px283qBMf3z1nxCfN3WHrH9v8Y8jOb1xoBqZMRZcaD8,2694
59
59
  mainsequence/server/_mcp_skills_upstream/server.py,sha256=laHsfpoaYX3LcwQ04-GN7r0_C63fG6CzyXDci2rmbHE,7695
60
60
  mainsequence/server/_mcp_skills_upstream/shared.py,sha256=JmnX9PoLoyi93nBYnUEPWCdPXor_nQHjHptQDXpFzPs,11978
61
- mainsequence-9.0.26.dev70.dist-info/licenses/LICENSE,sha256=b6Ecsk8wjsEby3lbSzdljR6hHiFGHBizt9iWV2p1c_I,1074
62
- mainsequence-9.0.26.dev70.dist-info/METADATA,sha256=GDRoS2ymodPcySLWPHDt1dYbh2tB2LxZFBO2fWs1XQc,7531
63
- mainsequence-9.0.26.dev70.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
64
- mainsequence-9.0.26.dev70.dist-info/entry_points.txt,sha256=XGAOq3-mF2lWWpeRJvoaWyMarRcK167mH1g124aGdUs,88
65
- mainsequence-9.0.26.dev70.dist-info/top_level.txt,sha256=BFeblM_Q9q0cUWBvTxnfWcWQEHtf5XZi9nOS-Gdfwus,28
66
- mainsequence-9.0.26.dev70.dist-info/RECORD,,
61
+ mainsequence-9.0.27.dev71.dist-info/licenses/LICENSE,sha256=b6Ecsk8wjsEby3lbSzdljR6hHiFGHBizt9iWV2p1c_I,1074
62
+ mainsequence-9.0.27.dev71.dist-info/METADATA,sha256=fsXFdkTMGMsdDs7W1h_085WrAvjdsZrLr35ldgLPRpw,7531
63
+ mainsequence-9.0.27.dev71.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
64
+ mainsequence-9.0.27.dev71.dist-info/entry_points.txt,sha256=XGAOq3-mF2lWWpeRJvoaWyMarRcK167mH1g124aGdUs,88
65
+ mainsequence-9.0.27.dev71.dist-info/top_level.txt,sha256=BFeblM_Q9q0cUWBvTxnfWcWQEHtf5XZi9nOS-Gdfwus,28
66
+ mainsequence-9.0.27.dev71.dist-info/RECORD,,