bedrock-agentcore-starter-toolkit 0.1.12__py3-none-any.whl → 0.1.13__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 (24) hide show
  1. bedrock_agentcore_starter_toolkit/cli/import_agent/agent_info.py +6 -1
  2. bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +86 -1
  3. bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +53 -0
  4. bedrock_agentcore_starter_toolkit/operations/memory/__init__.py +1 -0
  5. bedrock_agentcore_starter_toolkit/operations/memory/constants.py +98 -0
  6. bedrock_agentcore_starter_toolkit/operations/memory/manager.py +890 -0
  7. bedrock_agentcore_starter_toolkit/operations/memory/models/DictWrapper.py +51 -0
  8. bedrock_agentcore_starter_toolkit/operations/memory/models/Memory.py +17 -0
  9. bedrock_agentcore_starter_toolkit/operations/memory/models/MemoryStrategy.py +17 -0
  10. bedrock_agentcore_starter_toolkit/operations/memory/models/MemorySummary.py +17 -0
  11. bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +4 -0
  12. bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py +3 -2
  13. bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +8 -2
  14. bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +1 -0
  15. bedrock_agentcore_starter_toolkit/services/codebuild.py +17 -6
  16. bedrock_agentcore_starter_toolkit/services/runtime.py +71 -5
  17. bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +1 -0
  18. bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +25 -11
  19. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/METADATA +3 -4
  20. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/RECORD +24 -17
  21. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/WHEEL +0 -0
  22. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/entry_points.txt +0 -0
  23. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.dist-info}/licenses/LICENSE.txt +0 -0
  24. {bedrock_agentcore_starter_toolkit-0.1.12.dist-info → bedrock_agentcore_starter_toolkit-0.1.13.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)
@@ -176,6 +176,13 @@ def configure(
176
176
  authorizer_config: Optional[str] = typer.Option(
177
177
  None, "--authorizer-config", "-ac", help="OAuth authorizer configuration as JSON string"
178
178
  ),
179
+ request_header_allowlist: Optional[str] = typer.Option(
180
+ None,
181
+ "--request-header-allowlist",
182
+ "-rha",
183
+ help="Comma-separated list of allowed request headers "
184
+ "(Authorization or X-Amzn-Bedrock-AgentCore-Runtime-Custom-*)",
185
+ ),
179
186
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
180
187
  region: Optional[str] = typer.Option(None, "--region", "-r"),
181
188
  protocol: Optional[str] = typer.Option(None, "--protocol", "-p", help="Server protocol (HTTP or MCP)"),
@@ -243,6 +250,19 @@ def configure(
243
250
  else:
244
251
  oauth_config = config_manager.prompt_oauth_config()
245
252
 
253
+ # Handle request header allowlist configuration
254
+ request_header_config = None
255
+ if request_header_allowlist:
256
+ # Parse comma-separated headers and create configuration
257
+ headers = [header.strip() for header in request_header_allowlist.split(",") if header.strip()]
258
+ if headers:
259
+ request_header_config = {"requestHeaderAllowlist": headers}
260
+ _print_success(f"Configured request header allowlist with {len(headers)} headers")
261
+ else:
262
+ _handle_error("Empty request header allowlist provided")
263
+ else:
264
+ request_header_config = config_manager.prompt_request_header_allowlist()
265
+
246
266
  try:
247
267
  result = configure_bedrock_agentcore(
248
268
  agent_name=agent_name,
@@ -254,6 +274,7 @@ def configure(
254
274
  enable_observability=not disable_otel,
255
275
  requirements_file=final_requirements_file,
256
276
  authorizer_configuration=oauth_config,
277
+ request_header_configuration=request_header_config,
257
278
  verbose=verbose,
258
279
  region=region,
259
280
  protocol=protocol.upper() if protocol else None,
@@ -264,6 +285,12 @@ def configure(
264
285
  if oauth_config:
265
286
  auth_info = "OAuth (customJWTAuthorizer)"
266
287
 
288
+ # Prepare request headers info for summary
289
+ headers_info = ""
290
+ if request_header_config:
291
+ headers = request_header_config.get("requestHeaderAllowlist", [])
292
+ headers_info = f"Request Headers Allowlist: [dim]{len(headers)} headers configured[/dim]\n"
293
+
267
294
  console.print(
268
295
  Panel(
269
296
  f"[green]Configuration Complete[/green]\n\n"
@@ -277,7 +304,8 @@ def configure(
277
304
  f"ECR Repository: [dim]"
278
305
  f"{'Auto-create' if result.auto_create_ecr else result.ecr_repository or 'N/A'}"
279
306
  f"[/dim]\n"
280
- f"Authorization: [dim]{auth_info}[/dim]\n\n"
307
+ f"Authorization: [dim]{auth_info}[/dim]\n"
308
+ f"{headers_info}\n"
281
309
  f"📄 Config saved to: [dim]{result.config_path}[/dim]\n\n"
282
310
  f"[bold]Next Steps:[/bold]\n"
283
311
  f" [cyan]agentcore launch[/cyan]",
@@ -582,6 +610,45 @@ def _show_error_response(error_msg: str):
582
610
  console.print(f"\n[red]{error_msg}[/red]")
583
611
 
584
612
 
613
+ def _parse_custom_headers(headers_str: str) -> dict:
614
+ """Parse custom headers string and apply prefix logic.
615
+
616
+ Args:
617
+ headers_str: String in format "Header1:value,Header2:value2"
618
+
619
+ Returns:
620
+ dict: Dictionary of processed headers with proper prefixes
621
+
622
+ Raises:
623
+ ValueError: If header format is invalid
624
+ """
625
+ if not headers_str or not headers_str.strip():
626
+ return {}
627
+
628
+ headers = {}
629
+ header_pairs = [pair.strip() for pair in headers_str.split(",")]
630
+
631
+ for pair in header_pairs:
632
+ if ":" not in pair:
633
+ raise ValueError(f"Invalid header format: '{pair}'. Expected format: 'Header:value'")
634
+
635
+ header_name, header_value = pair.split(":", 1)
636
+ header_name = header_name.strip()
637
+ header_value = header_value.strip()
638
+
639
+ if not header_name:
640
+ raise ValueError(f"Empty header name in: '{pair}'")
641
+
642
+ # Apply prefix logic: if header doesn't start with the custom prefix, add it
643
+ prefix = "X-Amzn-Bedrock-AgentCore-Runtime-Custom-"
644
+ if not header_name.startswith(prefix):
645
+ header_name = prefix + header_name
646
+
647
+ headers[header_name] = header_value
648
+
649
+ return headers
650
+
651
+
585
652
  def invoke(
586
653
  payload: str = typer.Argument(..., help="JSON payload to send"),
587
654
  agent: Optional[str] = typer.Option(
@@ -593,6 +660,12 @@ def invoke(
593
660
  ),
594
661
  local_mode: Optional[bool] = typer.Option(False, "--local", "-l", help="Send request to a running local container"),
595
662
  user_id: Optional[str] = typer.Option(None, "--user-id", "-u", help="User id for authorization flows"),
663
+ headers: Optional[str] = typer.Option(
664
+ None,
665
+ "--headers",
666
+ help="Custom headers (format: 'Header1:value,Header2:value2'). "
667
+ "Headers will be auto-prefixed with 'X-Amzn-Bedrock-AgentCore-Runtime-Custom-' if not already present.",
668
+ ),
596
669
  ):
597
670
  """Invoke Bedrock AgentCore endpoint."""
598
671
  config_path = Path.cwd() / ".bedrock_agentcore.yaml"
@@ -625,6 +698,17 @@ def invoke(
625
698
  "[yellow]Warning: Bearer token provided but OAuth is not configured in .bedrock_agentcore.yaml[/yellow]"
626
699
  )
627
700
 
701
+ # Process custom headers
702
+ custom_headers = {}
703
+ if headers:
704
+ try:
705
+ custom_headers = _parse_custom_headers(headers)
706
+ if custom_headers:
707
+ header_names = list(custom_headers.keys())
708
+ console.print(f"[dim]Using custom headers: {', '.join(header_names)}[/dim]")
709
+ except ValueError as e:
710
+ _handle_error(f"Invalid headers format: {e}")
711
+
628
712
  # Invoke
629
713
  result = invoke_bedrock_agentcore(
630
714
  config_path=config_path,
@@ -634,6 +718,7 @@ def invoke(
634
718
  bearer_token=final_bearer_token,
635
719
  user_id=user_id,
636
720
  local_mode=local_mode,
721
+ custom_headers=custom_headers,
637
722
  )
638
723
  agent_display = config.name if config else (agent or "unknown")
639
724
  _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}
@@ -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
+ }