@macaw-ai/client 0.0.0-stage → 0.9.10

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.
Files changed (142) hide show
  1. package/LICENSE.md +339 -0
  2. package/bin/install.js +278 -0
  3. package/package.json +37 -4
  4. package/payload/README.md +56 -0
  5. package/payload/secureAI/LICENSE +190 -0
  6. package/payload/secureAI/NOTICE +5 -0
  7. package/payload/secureAI/demos/policy-controls/1_uncontrolled.py +139 -0
  8. package/payload/secureAI/demos/policy-controls/2_with_macaw.py +139 -0
  9. package/payload/secureAI/demos/policy-controls/3_with_user_policies.py +162 -0
  10. package/payload/secureAI/demos/policy-controls/credentials.txt +12 -0
  11. package/payload/secureAI/demos/policy-controls/malicious_report.txt +21 -0
  12. package/payload/secureAI/demos/policy-controls/sales_report.txt +20 -0
  13. package/payload/secureAI/demos/tutorial-1-rbac/README.md +143 -0
  14. package/payload/secureAI/demos/tutorial-1-rbac/app.py +147 -0
  15. package/payload/secureAI/demos/tutorial-1-rbac/authprompts_demo.py +306 -0
  16. package/payload/secureAI/demos/tutorial-1-rbac/config/claims-config.yaml +152 -0
  17. package/payload/secureAI/demos/tutorial-1-rbac/demo_secureopenai_with_policies.py +150 -0
  18. package/payload/secureAI/demos/tutorial-1-rbac/policies/bu_Analytics.json +8 -0
  19. package/payload/secureAI/demos/tutorial-1-rbac/policies/company_FinTech_Corp.json +70 -0
  20. package/payload/secureAI/demos/tutorial-1-rbac/policies/llm-base.json +69 -0
  21. package/payload/secureAI/demos/tutorial-1-rbac/policies/team_Reporting.json +8 -0
  22. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_alice.json +32 -0
  23. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_bob.json +28 -0
  24. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_carol.json +34 -0
  25. package/payload/secureAI/demos/tutorial-1-rbac/requirements.txt +6 -0
  26. package/payload/secureAI/demos/tutorial-1-rbac/setup/auth0_complete_setup.sh +392 -0
  27. package/payload/secureAI/demos/tutorial-1-rbac/setup/keycloak_complete_setup.sh +320 -0
  28. package/payload/secureAI/demos/tutorial-2-intent/Policies/alation-remote-proxy.json +155 -0
  29. package/payload/secureAI/demos/tutorial-2-intent/Policies/bu.json +22 -0
  30. package/payload/secureAI/demos/tutorial-2-intent/Policies/company.json +17 -0
  31. package/payload/secureAI/demos/tutorial-2-intent/Policies/user_alice.json +48 -0
  32. package/payload/secureAI/demos/tutorial-2-intent/Policies/user_bob.json +83 -0
  33. package/payload/secureAI/demos/tutorial-2-intent/README.md +213 -0
  34. package/payload/secureAI/demos/tutorial-2-intent/script.py +188 -0
  35. package/payload/secureAI/demos/tutorial-2-intent/setup/alation_verifier.py +177 -0
  36. package/payload/secureAI/demos/tutorial-2-intent/setup/get_alation_user_token.py +180 -0
  37. package/payload/secureAI/examples/README.md +244 -0
  38. package/payload/secureAI/examples/anthropic/anthropic_1a_dropin_simple.py +103 -0
  39. package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind.py +190 -0
  40. package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind_streaming.py +190 -0
  41. package/payload/secureAI/examples/anthropic/anthropic_1c_a2a_invoke.py +156 -0
  42. package/payload/secureAI/examples/attestations/1a_trade_alice.py +215 -0
  43. package/payload/secureAI/examples/attestations/1a_trade_bob.py +164 -0
  44. package/payload/secureAI/examples/attestations/1b_grant_admin.py +170 -0
  45. package/payload/secureAI/examples/attestations/1b_grant_researcher.py +231 -0
  46. package/payload/secureAI/examples/attestations/README.md +128 -0
  47. package/payload/secureAI/examples/langchain/langchain_1a_dropin_simple.py +199 -0
  48. package/payload/secureAI/examples/langchain/langchain_1b_multiuser.py +262 -0
  49. package/payload/secureAI/examples/langchain/langchain_1c_orchestration.py +332 -0
  50. package/payload/secureAI/examples/langchain/langchain_1d_llm_openai.py +130 -0
  51. package/payload/secureAI/examples/langchain/langchain_1e_llm_anthropic.py +130 -0
  52. package/payload/secureAI/examples/langchain/langchain_1f_memory.py +214 -0
  53. package/payload/secureAI/examples/litellm/litellm_1a_dropin_simple.py +131 -0
  54. package/payload/secureAI/examples/mcp/1a_simple_invocation.py +115 -0
  55. package/payload/secureAI/examples/mcp/1b_discovery_and_resources.py +142 -0
  56. package/payload/secureAI/examples/mcp/1c_logging_client.py +100 -0
  57. package/payload/secureAI/examples/mcp/1d_progress_client.py +100 -0
  58. package/payload/secureAI/examples/mcp/1e_sampling_client.py +150 -0
  59. package/payload/secureAI/examples/mcp/1e_sampling_server.py +90 -0
  60. package/payload/secureAI/examples/mcp/1f_elicitation_client.py +127 -0
  61. package/payload/secureAI/examples/mcp/1f_elicitation_server.py +73 -0
  62. package/payload/secureAI/examples/mcp/1g_roots_client.py +122 -0
  63. package/payload/secureAI/examples/mcp/1g_roots_server.py +85 -0
  64. package/payload/secureAI/examples/mcp/2a_proxy_snowflake.py +212 -0
  65. package/payload/secureAI/examples/mcp/2b_proxy_databricks.py +299 -0
  66. package/payload/secureAI/examples/mcp/2c_proxy_stdio_fetch.py +91 -0
  67. package/payload/secureAI/examples/mcp/2d_proxy_stdio_salesforce.py +119 -0
  68. package/payload/secureAI/examples/mcp/2e_proxy_multiuser.py +136 -0
  69. package/payload/secureAI/examples/mcp/2f_proxy_stdio_databricks.py +144 -0
  70. package/payload/secureAI/examples/mcp/3a_native_mcp_client.py +104 -0
  71. package/payload/secureAI/examples/mcp/3b_native_mcp_proxy.py +114 -0
  72. package/payload/secureAI/examples/mcp/3c_native_mcp_sampling.py +180 -0
  73. package/payload/secureAI/examples/mcp/3d_native_mcp_2026.py +135 -0
  74. package/payload/secureAI/examples/mcp/securemcp_calculator.py +248 -0
  75. package/payload/secureAI/examples/mcp/securemcp_server.py +75 -0
  76. package/payload/secureAI/examples/openai/openai_1a_dropin_simple.py +102 -0
  77. package/payload/secureAI/examples/openai/openai_1b_multiuser_bind.py +193 -0
  78. package/payload/secureAI/examples/openai/openai_1b_multiuser_bind_streaming.py +199 -0
  79. package/payload/secureAI/examples/openai/openai_1c_a2a_invoke.py +156 -0
  80. package/payload/secureAI/examples/openai/openai_1d_invocation_patterns.py +337 -0
  81. package/payload/secureAI/examples/phishing-resistance/1a_login_alice.py +180 -0
  82. package/payload/secureAI/examples/phishing-resistance/1b_trade_alice.py +241 -0
  83. package/payload/secureAI/examples/phishing-resistance/1b_trade_bob.py +164 -0
  84. package/payload/secureAI/examples/phishing-resistance/README.md +124 -0
  85. package/payload/secureAI/examples/phishing-resistance/get_token.py +114 -0
  86. package/payload/secureAI/examples/pydantic_ai/README.md +88 -0
  87. package/payload/secureAI/examples/pydantic_ai/policies/app_pydantic-agent.json +10 -0
  88. package/payload/secureAI/examples/pydantic_ai/pydanticai_1a_dropin_simple.py +90 -0
  89. package/payload/secureAI/examples/pydantic_ai/pydanticai_1b_model_routing.py +85 -0
  90. package/payload/secureAI/examples/pydantic_ai/pydanticai_1c_native_tools.py +83 -0
  91. package/payload/secureAI/examples/pydantic_ai/pydanticai_1d_multiuser_bind.py +104 -0
  92. package/payload/secureAI/examples/pydantic_ai/pydanticai_1e_mcp_compose.py +92 -0
  93. package/payload/secureAI/macaw_adapters/__init__.py +55 -0
  94. package/payload/secureAI/macaw_adapters/anthropic/REFERENCE.md +374 -0
  95. package/payload/secureAI/macaw_adapters/anthropic/__init__.py +22 -0
  96. package/payload/secureAI/macaw_adapters/anthropic/secure_anthropic.py +792 -0
  97. package/payload/secureAI/macaw_adapters/anthropic_ext_tools/__init__.py +25 -0
  98. package/payload/secureAI/macaw_adapters/anthropic_ext_tools/secure_anthropic.py +840 -0
  99. package/payload/secureAI/macaw_adapters/langchain/REFERENCE.md +234 -0
  100. package/payload/secureAI/macaw_adapters/langchain/__init__.py +100 -0
  101. package/payload/secureAI/macaw_adapters/langchain/_utils.py +83 -0
  102. package/payload/secureAI/macaw_adapters/langchain/agents.py +296 -0
  103. package/payload/secureAI/macaw_adapters/langchain/anthropic.py +456 -0
  104. package/payload/secureAI/macaw_adapters/langchain/callbacks.py +306 -0
  105. package/payload/secureAI/macaw_adapters/langchain/memory.py +352 -0
  106. package/payload/secureAI/macaw_adapters/langchain/openai.py +410 -0
  107. package/payload/secureAI/macaw_adapters/langchain/tools.py +174 -0
  108. package/payload/secureAI/macaw_adapters/litellm/README.md +216 -0
  109. package/payload/secureAI/macaw_adapters/litellm/__init__.py +143 -0
  110. package/payload/secureAI/macaw_adapters/litellm/secure_litellm.py +686 -0
  111. package/payload/secureAI/macaw_adapters/mcp/REFERENCE.md +460 -0
  112. package/payload/secureAI/macaw_adapters/mcp/__init__.py +65 -0
  113. package/payload/secureAI/macaw_adapters/mcp/_endpoint.py +193 -0
  114. package/payload/secureAI/macaw_adapters/mcp/_endpoint2.py +225 -0
  115. package/payload/secureAI/macaw_adapters/mcp/_serve.py +25 -0
  116. package/payload/secureAI/macaw_adapters/mcp/client.py +721 -0
  117. package/payload/secureAI/macaw_adapters/mcp/decorators.py +228 -0
  118. package/payload/secureAI/macaw_adapters/mcp/fastmcp_compat.py +353 -0
  119. package/payload/secureAI/macaw_adapters/mcp/mcp.py +785 -0
  120. package/payload/secureAI/macaw_adapters/mcp/proxy.py +626 -0
  121. package/payload/secureAI/macaw_adapters/mcp/server.py +317 -0
  122. package/payload/secureAI/macaw_adapters/mcp/subscriptions.py +450 -0
  123. package/payload/secureAI/macaw_adapters/openai/REFERENCE.md +368 -0
  124. package/payload/secureAI/macaw_adapters/openai/__init__.py +22 -0
  125. package/payload/secureAI/macaw_adapters/openai/secure_openai.py +833 -0
  126. package/payload/secureAI/macaw_adapters/openai_ext_tools/REFERENCE.md +368 -0
  127. package/payload/secureAI/macaw_adapters/openai_ext_tools/__init__.py +25 -0
  128. package/payload/secureAI/macaw_adapters/openai_ext_tools/secure_openai.py +876 -0
  129. package/payload/secureAI/macaw_adapters/pydantic_ai/__init__.py +36 -0
  130. package/payload/secureAI/macaw_adapters/pydantic_ai/_core.py +257 -0
  131. package/payload/secureAI/macaw_adapters/pydantic_ai/agent.py +218 -0
  132. package/payload/secureAI/macaw_adapters/pydantic_ai/model.py +99 -0
  133. package/payload/secureAI/macaw_adapters/pydantic_ai/toolset.py +86 -0
  134. package/payload/secureAI/test_harness_quick.py +296 -0
  135. package/wheels/macaw_adapters-0.9.10.1-py3-none-any.whl +0 -0
  136. package/wheels/macaw_client-0.9.10.1-cp312-cp312-macosx_11_0_arm64.whl +0 -0
  137. package/wheels/macaw_client-0.9.10.1-cp312-cp312-manylinux_2_17_x86_64.whl +0 -0
  138. package/wheels/macaw_client-0.9.10.1-cp314-cp314-macosx_11_0_arm64.whl +0 -0
  139. package/wheels/macaw_client-0.9.10.1-cp314-cp314-manylinux_2_17_x86_64.whl +0 -0
  140. package/wheels/macaw_client-0.9.10.1-cp314-cp314-win_amd64.whl +0 -0
  141. package/wheels/macaw_client-0.9.10.1-cp39-cp39-macosx_11_0_arm64.whl +0 -0
  142. package/README.md +0 -3
@@ -0,0 +1,296 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MACAW Quick Test Harness
4
+
5
+ Runs MCP + 1a examples only. No Identity Provider required.
6
+ For full test suite (including multi-user examples), use test_harness.py
7
+
8
+ Usage:
9
+ python3 test_harness_quick.py
10
+ python3 test_harness_quick.py --verbose
11
+
12
+ Prerequisites:
13
+ - MACAW SDK installed (pip install macaw-client macaw-adapters)
14
+ - OPENAI_API_KEY and/or ANTHROPIC_API_KEY for LLM examples
15
+ - MCP examples run without any API keys
16
+ """
17
+
18
+ import os
19
+ import sys
20
+ import subprocess
21
+ import time
22
+ import signal
23
+ from datetime import datetime
24
+ from pathlib import Path
25
+ from dataclasses import dataclass, field
26
+ from typing import Optional, List
27
+ from enum import Enum
28
+
29
+
30
+ class TestResult(Enum):
31
+ PASS = "PASS"
32
+ FAIL = "FAIL"
33
+ SKIP = "SKIP"
34
+ TIMEOUT = "TIMEOUT"
35
+
36
+
37
+ @dataclass
38
+ class TestCase:
39
+ name: str
40
+ script: Path
41
+ category: str
42
+ requires_server: Optional[Path] = None
43
+ requires_keys: List[str] = field(default_factory=list)
44
+ timeout: int = 120
45
+ result: TestResult = TestResult.SKIP
46
+ output: str = ""
47
+ error: str = ""
48
+ duration: float = 0
49
+
50
+
51
+ # Quick tests - no IdP required
52
+ QUICK_TESTS = {
53
+ # MCP examples - no API keys needed, just MACAW
54
+ "mcp/1a_simple_invocation.py": {
55
+ "requires_server": "mcp/securemcp_calculator.py",
56
+ "timeout": 120,
57
+ },
58
+ "mcp/1b_discovery_and_resources.py": {
59
+ "requires_server": "mcp/securemcp_calculator.py",
60
+ "timeout": 120,
61
+ },
62
+ "mcp/1c_logging_client.py": {
63
+ "requires_server": "mcp/securemcp_calculator.py",
64
+ "timeout": 120,
65
+ },
66
+ "mcp/1d_progress_client.py": {
67
+ "requires_server": "mcp/securemcp_calculator.py",
68
+ "timeout": 120,
69
+ },
70
+ "mcp/1e_sampling_client.py": {
71
+ "requires_server": "mcp/1e_sampling_server.py",
72
+ "timeout": 120,
73
+ },
74
+ "mcp/1g_roots_client.py": {
75
+ "requires_server": "mcp/1g_roots_server.py",
76
+ "timeout": 120,
77
+ },
78
+ # 1a examples - need API keys but no IdP
79
+ "openai/openai_1a_dropin_simple.py": {
80
+ "requires_keys": ["OPENAI_API_KEY"],
81
+ "timeout": 120,
82
+ },
83
+ "anthropic/anthropic_1a_dropin_simple.py": {
84
+ "requires_keys": ["ANTHROPIC_API_KEY"],
85
+ "timeout": 120,
86
+ },
87
+ "langchain/langchain_1a_dropin_simple.py": {
88
+ "requires_keys": ["OPENAI_API_KEY"],
89
+ "timeout": 120,
90
+ },
91
+ }
92
+
93
+
94
+ class QuickTestHarness:
95
+ def __init__(self, examples_dir: Path, verbose: bool = False):
96
+ self.examples_dir = examples_dir
97
+ self.verbose = verbose
98
+ self.python = Path(sys.executable)
99
+ self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
100
+ self.results_dir = Path(f"/tmp/macaw-quick-{self.timestamp}")
101
+ self.test_cases: List[TestCase] = []
102
+
103
+ def discover_tests(self):
104
+ """Discover quick test cases."""
105
+ for script_rel, config in QUICK_TESTS.items():
106
+ script_path = self.examples_dir / script_rel
107
+ if script_path.exists():
108
+ category = script_rel.split("/")[0]
109
+ server_path = None
110
+ if config.get("requires_server"):
111
+ server_path = self.examples_dir / config["requires_server"]
112
+
113
+ tc = TestCase(
114
+ name=script_rel,
115
+ script=script_path,
116
+ category=category,
117
+ requires_server=server_path,
118
+ requires_keys=config.get("requires_keys", []),
119
+ timeout=config.get("timeout", 60),
120
+ )
121
+ self.test_cases.append(tc)
122
+
123
+ def _check_keys(self, test: TestCase) -> tuple:
124
+ """Check if required API keys are available."""
125
+ missing = []
126
+ for key in test.requires_keys:
127
+ if not os.environ.get(key):
128
+ missing.append(key)
129
+ return len(missing) == 0, missing
130
+
131
+ def _get_env(self, test: TestCase) -> dict:
132
+ """Build environment variables for test execution."""
133
+ env = os.environ.copy()
134
+ env["PYTHONPATH"] = str(self.examples_dir.parent)
135
+ return env
136
+
137
+ def run_test(self, test: TestCase) -> TestCase:
138
+ """Run a single test case."""
139
+ # Check for required keys
140
+ has_keys, missing = self._check_keys(test)
141
+ if not has_keys:
142
+ test.result = TestResult.SKIP
143
+ test.error = f"Missing: {', '.join(missing)}"
144
+ return test
145
+
146
+ env = self._get_env(test)
147
+ start_time = time.time()
148
+ server_proc = None
149
+
150
+ try:
151
+ # Start server if required
152
+ if test.requires_server and test.requires_server.exists():
153
+ server_proc = subprocess.Popen(
154
+ [str(self.python), str(test.requires_server)],
155
+ env=env,
156
+ stdout=subprocess.PIPE,
157
+ stderr=subprocess.PIPE,
158
+ preexec_fn=os.setsid,
159
+ )
160
+ time.sleep(20) # Wait for server to register (AWS registration latency)
161
+
162
+ if server_proc.poll() is not None:
163
+ test.result = TestResult.FAIL
164
+ test.error = "Server failed to start"
165
+ return test
166
+
167
+ # Run the test
168
+ result = subprocess.run(
169
+ [str(self.python), str(test.script)],
170
+ env=env,
171
+ capture_output=True,
172
+ timeout=test.timeout,
173
+ )
174
+
175
+ test.duration = time.time() - start_time
176
+ test.output = result.stdout.decode("utf-8", errors="replace")
177
+ test.error = result.stderr.decode("utf-8", errors="replace")
178
+
179
+ if result.returncode == 0:
180
+ test.result = TestResult.PASS
181
+ else:
182
+ test.result = TestResult.FAIL
183
+
184
+ except subprocess.TimeoutExpired:
185
+ test.result = TestResult.TIMEOUT
186
+ test.duration = test.timeout
187
+ test.error = f"Timeout after {test.timeout}s"
188
+
189
+ except Exception as e:
190
+ test.result = TestResult.FAIL
191
+ test.error = str(e)
192
+ test.duration = time.time() - start_time
193
+
194
+ finally:
195
+ # Cleanup server
196
+ if server_proc:
197
+ try:
198
+ os.killpg(os.getpgid(server_proc.pid), signal.SIGTERM)
199
+ server_proc.wait(timeout=5)
200
+ except Exception:
201
+ try:
202
+ os.killpg(os.getpgid(server_proc.pid), signal.SIGKILL)
203
+ except Exception:
204
+ pass
205
+
206
+ return test
207
+
208
+ def run_all(self):
209
+ """Run all test cases."""
210
+ self.results_dir.mkdir(parents=True, exist_ok=True)
211
+
212
+ for category in ["mcp", "openai", "anthropic", "langchain"]:
213
+ category_tests = [t for t in self.test_cases if t.category == category]
214
+ if not category_tests:
215
+ continue
216
+
217
+ print(f"\n{category.upper()} ({len(category_tests)} tests)")
218
+ print("-" * 40)
219
+
220
+ for test in category_tests:
221
+ self.run_test(test)
222
+ status = test.result.value
223
+ if test.result == TestResult.PASS:
224
+ print(f" [PASS] {test.name} ({test.duration:.1f}s)")
225
+ elif test.result == TestResult.SKIP:
226
+ print(f" [SKIP] {test.name} - {test.error}")
227
+ elif test.result == TestResult.TIMEOUT:
228
+ print(f" [TIME] {test.name} - {test.error}")
229
+ else:
230
+ print(f" [FAIL] {test.name} ({test.duration:.1f}s)")
231
+ if self.verbose and test.error:
232
+ for line in test.error.strip().split("\n")[-5:]:
233
+ print(f" {line}")
234
+
235
+ def print_summary(self):
236
+ """Print test summary."""
237
+ passed = len([t for t in self.test_cases if t.result == TestResult.PASS])
238
+ failed = len([t for t in self.test_cases if t.result == TestResult.FAIL])
239
+ skipped = len([t for t in self.test_cases if t.result == TestResult.SKIP])
240
+ timeout = len([t for t in self.test_cases if t.result == TestResult.TIMEOUT])
241
+ total = len(self.test_cases)
242
+
243
+ print("\n" + "=" * 60)
244
+ print("QUICK TEST SUMMARY")
245
+ print("=" * 60)
246
+ print(f" Passed: {passed}/{total}")
247
+ print(f" Failed: {failed}")
248
+ print(f" Skipped: {skipped}")
249
+ print(f" Timeout: {timeout}")
250
+ print("=" * 60)
251
+
252
+ if skipped > 0:
253
+ print("\nSkipped tests need API keys:")
254
+ print(" export OPENAI_API_KEY=sk-...")
255
+ print(" export ANTHROPIC_API_KEY=sk-ant-...")
256
+
257
+ if failed > 0 or timeout > 0:
258
+ print("\nFor full output, run with --verbose")
259
+
260
+ print(f"\nFor multi-user examples (requires IdP), run:")
261
+ print(f" python test_harness.py")
262
+
263
+
264
+ def main():
265
+ verbose = "--verbose" in sys.argv or "-v" in sys.argv
266
+
267
+ # Find examples directory
268
+ script_dir = Path(__file__).parent
269
+ examples_dir = script_dir / "examples"
270
+
271
+ if not examples_dir.exists():
272
+ print(f"Error: examples directory not found at {examples_dir}")
273
+ sys.exit(1)
274
+
275
+ print("=" * 60)
276
+ print("MACAW Quick Test Harness")
277
+ print("=" * 60)
278
+ print(f"Examples: {examples_dir}")
279
+ print(f"Python: {sys.executable}")
280
+ print(f"Tests: MCP + 1a examples (no IdP required)")
281
+
282
+ harness = QuickTestHarness(examples_dir, verbose=verbose)
283
+ harness.discover_tests()
284
+
285
+ print(f"\nFound {len(harness.test_cases)} quick tests")
286
+
287
+ harness.run_all()
288
+ harness.print_summary()
289
+
290
+ # Exit with error if any tests failed
291
+ failed = len([t for t in harness.test_cases if t.result == TestResult.FAIL])
292
+ sys.exit(1 if failed > 0 else 0)
293
+
294
+
295
+ if __name__ == "__main__":
296
+ main()
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.