bedrock-agentcore-starter-toolkit 0.1.12__py3-none-any.whl → 0.1.14__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.

Potentially problematic release.


This version of bedrock-agentcore-starter-toolkit might be problematic. Click here for more details.

Files changed (25) hide show
  1. bedrock_agentcore_starter_toolkit/cli/import_agent/agent_info.py +6 -1
  2. bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +88 -1
  3. bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +53 -0
  4. bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +3 -0
  5. bedrock_agentcore_starter_toolkit/operations/memory/__init__.py +1 -0
  6. bedrock_agentcore_starter_toolkit/operations/memory/constants.py +98 -0
  7. bedrock_agentcore_starter_toolkit/operations/memory/manager.py +890 -0
  8. bedrock_agentcore_starter_toolkit/operations/memory/models/DictWrapper.py +51 -0
  9. bedrock_agentcore_starter_toolkit/operations/memory/models/Memory.py +17 -0
  10. bedrock_agentcore_starter_toolkit/operations/memory/models/MemoryStrategy.py +17 -0
  11. bedrock_agentcore_starter_toolkit/operations/memory/models/MemorySummary.py +17 -0
  12. bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +28 -0
  13. bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py +3 -2
  14. bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +8 -2
  15. bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +1 -0
  16. bedrock_agentcore_starter_toolkit/services/codebuild.py +17 -6
  17. bedrock_agentcore_starter_toolkit/services/runtime.py +71 -5
  18. bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +1 -0
  19. bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +25 -11
  20. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.14.dist-info}/METADATA +3 -4
  21. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.14.dist-info}/RECORD +25 -18
  22. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.14.dist-info}/WHEEL +0 -0
  23. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.14.dist-info}/entry_points.txt +0 -0
  24. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.14.dist-info}/licenses/LICENSE.txt +0 -0
  25. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.14.dist-info}/licenses/NOTICE.txt +0 -0
@@ -148,7 +148,12 @@ def get_agent_info(agent_id: str, agent_alias_id: str, bedrock_client, bedrock_a
148
148
  s3_object_key = action_group["apiSchema"]["s3"]["s3ObjectKey"]
149
149
 
150
150
  s3_client = boto3.client("s3")
151
- response = s3_client.get_object(Bucket=s3_bucket_name, Key=s3_object_key)
151
+ # Get account ID for bucket ownership verification
152
+ sts_client = boto3.client("sts")
153
+ account_id = sts_client.get_caller_identity()["Account"]
154
+ response = s3_client.get_object(
155
+ Bucket=s3_bucket_name, Key=s3_object_key, ExpectedBucketOwner=account_id
156
+ )
152
157
  yaml_content = response["Body"].read().decode("utf-8")
153
158
  yaml = YAML(typ="safe")
154
159
  action_group["apiSchema"]["payload"] = yaml.load(yaml_content)
@@ -167,6 +167,7 @@ def configure(
167
167
  entrypoint: Optional[str] = typer.Option(None, "--entrypoint", "-e", help="Python file with BedrockAgentCoreApp"),
168
168
  agent_name: Optional[str] = typer.Option(None, "--name", "-n"),
169
169
  execution_role: Optional[str] = typer.Option(None, "--execution-role", "-er"),
170
+ code_build_execution_role: Optional[str] = typer.Option(None, "--code-build-execution-role", "-cber"),
170
171
  ecr_repository: Optional[str] = typer.Option(None, "--ecr", "-ecr"),
171
172
  container_runtime: Optional[str] = typer.Option(None, "--container-runtime", "-ctr"),
172
173
  requirements_file: Optional[str] = typer.Option(
@@ -176,6 +177,13 @@ def configure(
176
177
  authorizer_config: Optional[str] = typer.Option(
177
178
  None, "--authorizer-config", "-ac", help="OAuth authorizer configuration as JSON string"
178
179
  ),
180
+ request_header_allowlist: Optional[str] = typer.Option(
181
+ None,
182
+ "--request-header-allowlist",
183
+ "-rha",
184
+ help="Comma-separated list of allowed request headers "
185
+ "(Authorization or X-Amzn-Bedrock-AgentCore-Runtime-Custom-*)",
186
+ ),
179
187
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
180
188
  region: Optional[str] = typer.Option(None, "--region", "-r"),
181
189
  protocol: Optional[str] = typer.Option(None, "--protocol", "-p", help="Server protocol (HTTP or MCP)"),
@@ -243,17 +251,32 @@ def configure(
243
251
  else:
244
252
  oauth_config = config_manager.prompt_oauth_config()
245
253
 
254
+ # Handle request header allowlist configuration
255
+ request_header_config = None
256
+ if request_header_allowlist:
257
+ # Parse comma-separated headers and create configuration
258
+ headers = [header.strip() for header in request_header_allowlist.split(",") if header.strip()]
259
+ if headers:
260
+ request_header_config = {"requestHeaderAllowlist": headers}
261
+ _print_success(f"Configured request header allowlist with {len(headers)} headers")
262
+ else:
263
+ _handle_error("Empty request header allowlist provided")
264
+ else:
265
+ request_header_config = config_manager.prompt_request_header_allowlist()
266
+
246
267
  try:
247
268
  result = configure_bedrock_agentcore(
248
269
  agent_name=agent_name,
249
270
  entrypoint_path=Path(entrypoint),
250
271
  execution_role=execution_role,
272
+ code_build_execution_role=code_build_execution_role,
251
273
  ecr_repository=ecr_repository,
252
274
  container_runtime=container_runtime,
253
275
  auto_create_ecr=auto_create_ecr,
254
276
  enable_observability=not disable_otel,
255
277
  requirements_file=final_requirements_file,
256
278
  authorizer_configuration=oauth_config,
279
+ request_header_configuration=request_header_config,
257
280
  verbose=verbose,
258
281
  region=region,
259
282
  protocol=protocol.upper() if protocol else None,
@@ -264,6 +287,12 @@ def configure(
264
287
  if oauth_config:
265
288
  auth_info = "OAuth (customJWTAuthorizer)"
266
289
 
290
+ # Prepare request headers info for summary
291
+ headers_info = ""
292
+ if request_header_config:
293
+ headers = request_header_config.get("requestHeaderAllowlist", [])
294
+ headers_info = f"Request Headers Allowlist: [dim]{len(headers)} headers configured[/dim]\n"
295
+
267
296
  console.print(
268
297
  Panel(
269
298
  f"[green]Configuration Complete[/green]\n\n"
@@ -277,7 +306,8 @@ def configure(
277
306
  f"ECR Repository: [dim]"
278
307
  f"{'Auto-create' if result.auto_create_ecr else result.ecr_repository or 'N/A'}"
279
308
  f"[/dim]\n"
280
- f"Authorization: [dim]{auth_info}[/dim]\n\n"
309
+ f"Authorization: [dim]{auth_info}[/dim]\n"
310
+ f"{headers_info}\n"
281
311
  f"📄 Config saved to: [dim]{result.config_path}[/dim]\n\n"
282
312
  f"[bold]Next Steps:[/bold]\n"
283
313
  f" [cyan]agentcore launch[/cyan]",
@@ -582,6 +612,45 @@ def _show_error_response(error_msg: str):
582
612
  console.print(f"\n[red]{error_msg}[/red]")
583
613
 
584
614
 
615
+ def _parse_custom_headers(headers_str: str) -> dict:
616
+ """Parse custom headers string and apply prefix logic.
617
+
618
+ Args:
619
+ headers_str: String in format "Header1:value,Header2:value2"
620
+
621
+ Returns:
622
+ dict: Dictionary of processed headers with proper prefixes
623
+
624
+ Raises:
625
+ ValueError: If header format is invalid
626
+ """
627
+ if not headers_str or not headers_str.strip():
628
+ return {}
629
+
630
+ headers = {}
631
+ header_pairs = [pair.strip() for pair in headers_str.split(",")]
632
+
633
+ for pair in header_pairs:
634
+ if ":" not in pair:
635
+ raise ValueError(f"Invalid header format: '{pair}'. Expected format: 'Header:value'")
636
+
637
+ header_name, header_value = pair.split(":", 1)
638
+ header_name = header_name.strip()
639
+ header_value = header_value.strip()
640
+
641
+ if not header_name:
642
+ raise ValueError(f"Empty header name in: '{pair}'")
643
+
644
+ # Apply prefix logic: if header doesn't start with the custom prefix, add it
645
+ prefix = "X-Amzn-Bedrock-AgentCore-Runtime-Custom-"
646
+ if not header_name.startswith(prefix):
647
+ header_name = prefix + header_name
648
+
649
+ headers[header_name] = header_value
650
+
651
+ return headers
652
+
653
+
585
654
  def invoke(
586
655
  payload: str = typer.Argument(..., help="JSON payload to send"),
587
656
  agent: Optional[str] = typer.Option(
@@ -593,6 +662,12 @@ def invoke(
593
662
  ),
594
663
  local_mode: Optional[bool] = typer.Option(False, "--local", "-l", help="Send request to a running local container"),
595
664
  user_id: Optional[str] = typer.Option(None, "--user-id", "-u", help="User id for authorization flows"),
665
+ headers: Optional[str] = typer.Option(
666
+ None,
667
+ "--headers",
668
+ help="Custom headers (format: 'Header1:value,Header2:value2'). "
669
+ "Headers will be auto-prefixed with 'X-Amzn-Bedrock-AgentCore-Runtime-Custom-' if not already present.",
670
+ ),
596
671
  ):
597
672
  """Invoke Bedrock AgentCore endpoint."""
598
673
  config_path = Path.cwd() / ".bedrock_agentcore.yaml"
@@ -625,6 +700,17 @@ def invoke(
625
700
  "[yellow]Warning: Bearer token provided but OAuth is not configured in .bedrock_agentcore.yaml[/yellow]"
626
701
  )
627
702
 
703
+ # Process custom headers
704
+ custom_headers = {}
705
+ if headers:
706
+ try:
707
+ custom_headers = _parse_custom_headers(headers)
708
+ if custom_headers:
709
+ header_names = list(custom_headers.keys())
710
+ console.print(f"[dim]Using custom headers: {', '.join(header_names)}[/dim]")
711
+ except ValueError as e:
712
+ _handle_error(f"Invalid headers format: {e}")
713
+
628
714
  # Invoke
629
715
  result = invoke_bedrock_agentcore(
630
716
  config_path=config_path,
@@ -634,6 +720,7 @@ def invoke(
634
720
  bearer_token=final_bearer_token,
635
721
  user_id=user_id,
636
722
  local_mode=local_mode,
723
+ custom_headers=custom_headers,
637
724
  )
638
725
  agent_display = config.name if config else (agent or "unknown")
639
726
  _show_invoke_info_panel(agent_display, result, config)
@@ -145,3 +145,56 @@ class ConfigurationManager:
145
145
 
146
146
  _print_success("OAuth authorizer configuration created")
147
147
  return config
148
+
149
+ def prompt_request_header_allowlist(self) -> Optional[dict]:
150
+ """Prompt for request header allowlist configuration. Returns allowlist config dict or None."""
151
+ if self.non_interactive:
152
+ _print_success("Using default request header configuration")
153
+ return None
154
+
155
+ console.print("\n🔒 [cyan]Request Header Allowlist[/cyan]")
156
+ console.print("[dim]Configure which request headers are allowed to pass through to your agent.[/dim]")
157
+ console.print("[dim]Common headers: Authorization, X-Amzn-Bedrock-AgentCore-Runtime-Custom-*[/dim]")
158
+
159
+ # Get existing allowlist values
160
+ existing_headers = ""
161
+ if (
162
+ self.existing_config
163
+ and self.existing_config.request_header_configuration
164
+ and "requestHeaderAllowlist" in self.existing_config.request_header_configuration
165
+ ):
166
+ existing_headers = ",".join(self.existing_config.request_header_configuration["requestHeaderAllowlist"])
167
+
168
+ allowlist_default = "yes" if existing_headers else "no"
169
+ response = _prompt_with_default("Configure request header allowlist? (yes/no)", allowlist_default)
170
+
171
+ if response.lower() in ["yes", "y"]:
172
+ return self._configure_request_header_allowlist(existing_headers)
173
+ else:
174
+ _print_success("Using default request header configuration")
175
+ return None
176
+
177
+ def _configure_request_header_allowlist(self, existing_headers: str = "") -> dict:
178
+ """Configure request header allowlist and return config dict."""
179
+ console.print("\n📋 [cyan]Request Header Allowlist Configuration[/cyan]")
180
+
181
+ # Show existing config if available
182
+ if existing_headers:
183
+ console.print(f"[dim]Previously configured: {existing_headers}[/dim]")
184
+
185
+ # Prompt for headers
186
+ default_headers = existing_headers or "Authorization,X-Amzn-Bedrock-AgentCore-Runtime-Custom-*"
187
+ headers_input = _prompt_with_default("Enter allowed request headers (comma-separated)", default_headers)
188
+
189
+ if not headers_input:
190
+ _handle_error("At least one request header must be specified for allowlist configuration")
191
+
192
+ # Parse and validate headers
193
+ headers = [header.strip() for header in headers_input.split(",") if header.strip()]
194
+
195
+ if not headers:
196
+ _handle_error("Empty request header allowlist provided")
197
+
198
+ _print_success(f"Request header allowlist configured with {len(headers)} headers")
199
+
200
+ return {"requestHeaderAllowlist": headers}
@@ -35,6 +35,7 @@ class Runtime:
35
35
  self,
36
36
  entrypoint: str,
37
37
  execution_role: Optional[str] = None,
38
+ code_build_execution_role: Optional[str] = None,
38
39
  agent_name: Optional[str] = None,
39
40
  requirements: Optional[List[str]] = None,
40
41
  requirements_file: Optional[str] = None,
@@ -53,6 +54,7 @@ class Runtime:
53
54
  entrypoint: Path to Python file with optional Bedrock AgentCore name
54
55
  (e.g., "handler.py" or "handler.py:bedrock_agentcore")
55
56
  execution_role: AWS IAM execution role ARN or name (optional if auto_create_execution_role=True)
57
+ code_build_execution_role: Optional separate CodeBuild execution role ARN or name
56
58
  agent_name: name of the agent
57
59
  requirements: Optional list of requirements to generate requirements.txt
58
60
  requirements_file: Optional path to existing requirements file
@@ -109,6 +111,7 @@ class Runtime:
109
111
  entrypoint_path=Path(file_path),
110
112
  auto_create_execution_role=auto_create_execution_role,
111
113
  execution_role=execution_role,
114
+ code_build_execution_role=code_build_execution_role,
112
115
  ecr_repository=ecr_repository,
113
116
  container_runtime=container_runtime,
114
117
  auto_create_ecr=auto_create_ecr,
@@ -0,0 +1 @@
1
+ """BedrockAgentCore Starter Toolkit cli memory package."""
@@ -0,0 +1,98 @@
1
+ """Constants for Bedrock AgentCore Memory SDK."""
2
+
3
+ from enum import Enum
4
+ from typing import Dict, List, Optional
5
+
6
+
7
+ class StrategyType(Enum):
8
+ """Memory strategy types with integrated wrapper key and type methods."""
9
+
10
+ SEMANTIC = "semanticMemoryStrategy"
11
+ SUMMARY = "summaryMemoryStrategy"
12
+ USER_PREFERENCE = "userPreferenceMemoryStrategy"
13
+ CUSTOM = "customMemoryStrategy"
14
+
15
+ def extraction_wrapper_key(self) -> Optional[str]:
16
+ """Get the extraction wrapper key for this strategy type."""
17
+ extraction_keys = {
18
+ StrategyType.SEMANTIC: "semanticExtractionConfiguration",
19
+ StrategyType.USER_PREFERENCE: "userPreferenceExtractionConfiguration",
20
+ }
21
+ return extraction_keys.get(self)
22
+
23
+ def consolidation_wrapper_key(self) -> Optional[str]:
24
+ """Get the consolidation wrapper key for this strategy type."""
25
+ # Only SUMMARY strategy has a consolidation wrapper key
26
+ if self == StrategyType.SUMMARY:
27
+ return "summaryConsolidationConfiguration"
28
+ return None
29
+
30
+ def get_memory_strategy(self) -> str:
31
+ """Get the internal memory strategy type string."""
32
+ strategy_mapping = {
33
+ StrategyType.SEMANTIC: "SEMANTIC",
34
+ StrategyType.SUMMARY: "SUMMARIZATION",
35
+ StrategyType.USER_PREFERENCE: "USER_PREFERENCE",
36
+ StrategyType.CUSTOM: "CUSTOM",
37
+ }
38
+ return strategy_mapping[self]
39
+
40
+ def get_override_type(self) -> Optional[str]:
41
+ """Get the override type for custom strategies."""
42
+ # This method is primarily for CUSTOM strategy type
43
+ # The actual override type would be determined by context
44
+ if self == StrategyType.CUSTOM:
45
+ return "CUSTOM_OVERRIDE" # Base type, specific override determined by usage
46
+ return None
47
+
48
+
49
+ class OverrideType(Enum):
50
+ """Custom strategy override types."""
51
+
52
+ SEMANTIC_OVERRIDE = "SEMANTIC_OVERRIDE"
53
+ SUMMARY_OVERRIDE = "SUMMARY_OVERRIDE"
54
+ USER_PREFERENCE_OVERRIDE = "USER_PREFERENCE_OVERRIDE"
55
+
56
+ def extraction_wrapper_key(self) -> Optional[str]:
57
+ """Get the extraction wrapper key for this override type."""
58
+ extraction_keys = {
59
+ OverrideType.SEMANTIC_OVERRIDE: "semanticExtractionOverride",
60
+ OverrideType.USER_PREFERENCE_OVERRIDE: "userPreferenceExtractionOverride",
61
+ }
62
+ return extraction_keys.get(self)
63
+
64
+ def consolidation_wrapper_key(self) -> Optional[str]:
65
+ """Get the consolidation wrapper key for this override type."""
66
+ consolidation_keys = {
67
+ OverrideType.SEMANTIC_OVERRIDE: "semanticConsolidationOverride",
68
+ OverrideType.SUMMARY_OVERRIDE: "summaryConsolidationOverride",
69
+ OverrideType.USER_PREFERENCE_OVERRIDE: "userPreferenceConsolidationOverride",
70
+ }
71
+ return consolidation_keys.get(self)
72
+
73
+
74
+ class MemoryStatus(Enum):
75
+ """Memory resource statuses."""
76
+
77
+ CREATING = "CREATING"
78
+ ACTIVE = "ACTIVE"
79
+ FAILED = "FAILED"
80
+ UPDATING = "UPDATING"
81
+ DELETING = "DELETING"
82
+
83
+
84
+ class MemoryStrategyStatus(Enum):
85
+ """Memory strategy statuses (new from API update)."""
86
+
87
+ CREATING = "CREATING"
88
+ ACTIVE = "ACTIVE"
89
+ DELETING = "DELETING"
90
+ FAILED = "FAILED"
91
+
92
+
93
+ # Default namespaces for each strategy type
94
+ DEFAULT_NAMESPACES: Dict[StrategyType, List[str]] = {
95
+ StrategyType.SEMANTIC: ["/actor/{actorId}/strategy/{strategyId}/{sessionId}"],
96
+ StrategyType.SUMMARY: ["/actor/{actorId}/strategy/{strategyId}/{sessionId}"],
97
+ StrategyType.USER_PREFERENCE: ["/actor/{actorId}/strategy/{strategyId}"],
98
+ }