bedrock-agentcore-starter-toolkit 0.1.22__py3-none-any.whl → 0.1.24__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 (23) hide show
  1. bedrock_agentcore_starter_toolkit/cli/common.py +1 -1
  2. bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +157 -58
  3. bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +45 -16
  4. bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +27 -1
  5. bedrock_agentcore_starter_toolkit/operations/memory/manager.py +1 -1
  6. bedrock_agentcore_starter_toolkit/operations/runtime/__init__.py +12 -1
  7. bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +145 -22
  8. bedrock_agentcore_starter_toolkit/operations/runtime/destroy.py +22 -6
  9. bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +12 -3
  10. bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +18 -3
  11. bedrock_agentcore_starter_toolkit/operations/runtime/status.py +5 -4
  12. bedrock_agentcore_starter_toolkit/services/codebuild.py +2 -1
  13. bedrock_agentcore_starter_toolkit/services/ecr.py +44 -2
  14. bedrock_agentcore_starter_toolkit/utils/runtime/container.py +12 -9
  15. bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +6 -35
  16. bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +0 -6
  17. bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_policy.json.j2 +0 -13
  18. {bedrock_agentcore_starter_toolkit-0.1.22.dist-info → bedrock_agentcore_starter_toolkit-0.1.24.dist-info}/METADATA +1 -1
  19. {bedrock_agentcore_starter_toolkit-0.1.22.dist-info → bedrock_agentcore_starter_toolkit-0.1.24.dist-info}/RECORD +23 -23
  20. {bedrock_agentcore_starter_toolkit-0.1.22.dist-info → bedrock_agentcore_starter_toolkit-0.1.24.dist-info}/WHEEL +0 -0
  21. {bedrock_agentcore_starter_toolkit-0.1.22.dist-info → bedrock_agentcore_starter_toolkit-0.1.24.dist-info}/entry_points.txt +0 -0
  22. {bedrock_agentcore_starter_toolkit-0.1.22.dist-info → bedrock_agentcore_starter_toolkit-0.1.24.dist-info}/licenses/LICENSE.txt +0 -0
  23. {bedrock_agentcore_starter_toolkit-0.1.22.dist-info → bedrock_agentcore_starter_toolkit-0.1.24.dist-info}/licenses/NOTICE.txt +0 -0
@@ -1,14 +1,16 @@
1
1
  """Configure operation - creates BedrockAgentCore configuration and Dockerfile."""
2
2
 
3
3
  import logging
4
+ import os
4
5
  import re
5
6
  from pathlib import Path
6
- from typing import Any, Dict, Optional, Tuple
7
+ from typing import Any, Dict, Literal, Optional, Tuple
7
8
 
8
9
  from ...cli.runtime.configuration_manager import ConfigurationManager
9
10
  from ...services.ecr import get_account_id, get_region
10
11
  from ...utils.runtime.config import merge_agent_config, save_config
11
12
  from ...utils.runtime.container import ContainerRuntime
13
+ from ...utils.runtime.entrypoint import detect_dependencies
12
14
  from ...utils.runtime.schema import (
13
15
  AWSConfig,
14
16
  BedrockAgentCoreAgentSchema,
@@ -24,6 +26,103 @@ from .models import ConfigureResult
24
26
  log = logging.getLogger(__name__)
25
27
 
26
28
 
29
+ def get_relative_path(path: Path, base: Optional[Path] = None) -> str:
30
+ """Convert path to relative format with OS-native separators.
31
+
32
+ Args:
33
+ path: Absolute or relative path
34
+ base: Base directory (defaults to current working directory)
35
+
36
+ Returns:
37
+ Path relative to base with OS-native separators
38
+
39
+ Raises:
40
+ ValueError: If path is empty or invalid
41
+ """
42
+ # Validate input
43
+ if not path or str(path).strip() == "":
44
+ raise ValueError("Path cannot be empty")
45
+
46
+ # Ensure path is a Path object
47
+ path_obj = Path(path) if not isinstance(path, Path) else path
48
+ base = base or Path.cwd()
49
+
50
+ try:
51
+ rel_path = path_obj.relative_to(base)
52
+ return str(rel_path)
53
+ except ValueError:
54
+ # Path is outside base - keep full path for clarity
55
+ # Don't lose directory structure by showing just the filename
56
+ return str(path_obj)
57
+
58
+
59
+ def detect_entrypoint(source_path: Path) -> Optional[Path]:
60
+ """Detect entrypoint file in source directory.
61
+
62
+ Args:
63
+ source_path: Directory to search for entrypoint
64
+
65
+ Returns:
66
+ Path to detected entrypoint file, or None if not found
67
+ """
68
+ ENTRYPOINT_CANDIDATES = ["agent.py", "app.py", "main.py", "__main__.py"]
69
+
70
+ source_dir = Path(source_path)
71
+ for candidate in ENTRYPOINT_CANDIDATES:
72
+ candidate_path = source_dir / candidate
73
+ if candidate_path.exists():
74
+ log.debug("Detected entrypoint: %s", candidate_path)
75
+ return candidate_path
76
+
77
+ log.debug("No entrypoint found in %s", source_path)
78
+ return None
79
+
80
+
81
+ def detect_requirements(source_path: Path):
82
+ """Detect requirements file in the source directory.
83
+
84
+ Args:
85
+ source_path: Source directory (where entrypoint is located)
86
+
87
+ Returns:
88
+ DependencyInfo object with detection results
89
+ """
90
+ # Resolve to absolute path for consistent behavior
91
+ source_path_resolved = Path(source_path).resolve()
92
+ log.debug("Checking for requirements in source directory: %s", source_path_resolved)
93
+
94
+ deps = detect_dependencies(source_path_resolved)
95
+ if deps.found:
96
+ log.debug("Found requirements in source directory: %s", deps.resolved_path)
97
+ else:
98
+ log.debug("No requirements file found in source directory: %s", source_path_resolved)
99
+
100
+ return deps
101
+
102
+
103
+ def infer_agent_name(entrypoint_path: Path, base: Optional[Path] = None) -> str:
104
+ """Infer agent name from entrypoint path.
105
+
106
+ Args:
107
+ entrypoint_path: Path to agent entrypoint file
108
+ base: Base directory for relative path (defaults to cwd)
109
+
110
+ Returns:
111
+ Suggested agent name (e.g., 'agents_writer_main' from 'agents/writer/main.py')
112
+ """
113
+ rel_entrypoint = get_relative_path(entrypoint_path, base)
114
+
115
+ # Remove .py extension if present (only at the end)
116
+ if rel_entrypoint.endswith(".py"):
117
+ rel_entrypoint = rel_entrypoint[:-3]
118
+
119
+ # Replace spaces and OS path separators with underscores
120
+ suggested_name = rel_entrypoint.replace(" ", "_").replace(os.sep, "_")
121
+
122
+ log.debug("Inferred agent name: %s from %s", suggested_name, get_relative_path(entrypoint_path, base))
123
+ return suggested_name
124
+
125
+
27
126
  def configure_bedrock_agentcore(
28
127
  agent_name: str,
29
128
  entrypoint_path: Path,
@@ -34,6 +133,7 @@ def configure_bedrock_agentcore(
34
133
  auto_create_ecr: bool = True,
35
134
  auto_create_execution_role: bool = True,
36
135
  enable_observability: bool = True,
136
+ memory_mode: Literal["NO_MEMORY", "STM_ONLY", "STM_AND_LTM"] = "STM_ONLY",
37
137
  requirements_file: Optional[str] = None,
38
138
  authorizer_configuration: Optional[Dict[str, Any]] = None,
39
139
  request_header_configuration: Optional[Dict[str, Any]] = None,
@@ -55,6 +155,7 @@ def configure_bedrock_agentcore(
55
155
  auto_create_ecr: Whether to auto-create ECR repository
56
156
  auto_create_execution_role: Whether to auto-create execution role if not provided
57
157
  enable_observability: Whether to enable observability
158
+ memory_mode: Memory configuration mode - "NO_MEMORY", "STM_ONLY" (default), or "STM_AND_LTM"
58
159
  requirements_file: Path to requirements file
59
160
  authorizer_configuration: JWT authorizer configuration dictionary
60
161
  request_header_configuration: Request header configuration dictionary
@@ -121,32 +222,52 @@ def configure_bedrock_agentcore(
121
222
  else:
122
223
  log.debug("No execution role provided and auto-create disabled")
123
224
 
124
- if verbose:
125
- log.debug("Prompting for memory configuration")
126
-
225
+ # Pass region to ConfigurationManager so it can check for existing memories
127
226
  config_manager = ConfigurationManager(build_dir / ".bedrock_agentcore.yaml", non_interactive)
128
227
 
129
- # New memory selection flow
130
- action, value = config_manager.prompt_memory_selection()
131
-
228
+ # Handle memory configuration
132
229
  memory_config = MemoryConfig()
133
- if action == "USE_EXISTING":
134
- # Using existing memory - just store the ID
135
- memory_config.memory_id = value
136
- memory_config.mode = "STM_AND_LTM" # Assume existing has strategies
137
- memory_config.memory_name = f"{agent_name}_memory"
138
- log.info("Using existing memory resource: %s", value)
139
- elif action == "CREATE_NEW":
140
- # Create new with specified mode
141
- memory_config.mode = value # This is the mode (STM_ONLY, STM_AND_LTM, NO_MEMORY)
230
+
231
+ # Check if memory is explicitly disabled FIRST (works in both interactive and non-interactive modes)
232
+ if memory_mode == "NO_MEMORY":
233
+ memory_config.mode = "NO_MEMORY"
234
+ log.info("Memory disabled")
235
+ elif non_interactive:
236
+ # Non-interactive mode: use explicit memory_mode parameter
237
+ memory_config.mode = memory_mode
142
238
  memory_config.event_expiry_days = 30
143
239
  memory_config.memory_name = f"{agent_name}_memory"
144
- log.info("Will create new memory with mode: %s", value)
240
+ log.info("Will create new memory with mode: %s", memory_mode)
145
241
 
146
- if memory_config.mode == "STM_AND_LTM":
147
- log.info("Memory configuration: Short-term + Long-term memory enabled")
148
- elif memory_config.mode == "STM_ONLY":
149
- log.info("Memory configuration: Short-term memory only")
242
+ if memory_mode == "STM_AND_LTM":
243
+ log.info("Memory configuration: Short-term + Long-term memory enabled")
244
+ else: # STM_ONLY
245
+ log.info("Memory configuration: Short-term memory only")
246
+ else:
247
+ # Interactive mode: prompt user (only if memory not explicitly disabled)
248
+ action, value = config_manager.prompt_memory_selection()
249
+
250
+ if action == "USE_EXISTING":
251
+ # Using existing memory - just store the ID
252
+ memory_config.memory_id = value
253
+ memory_config.mode = "STM_AND_LTM" # Assume existing has strategies
254
+ memory_config.memory_name = f"{agent_name}_memory"
255
+ log.info("Using existing memory resource: %s", value)
256
+ elif action == "CREATE_NEW":
257
+ # Create new with specified mode
258
+ memory_config.mode = value
259
+ memory_config.event_expiry_days = 30
260
+ memory_config.memory_name = f"{agent_name}_memory"
261
+ log.info("Will create new memory with mode: %s", value)
262
+
263
+ if value == "STM_AND_LTM":
264
+ log.info("Memory configuration: Short-term + Long-term memory enabled")
265
+ else: # STM_ONLY
266
+ log.info("Memory configuration: Short-term memory only")
267
+ elif action == "SKIP":
268
+ # User chose to skip memory setup
269
+ memory_config.mode = "NO_MEMORY"
270
+ log.info("Memory disabled by user choice")
150
271
 
151
272
  # Check for existing memory configuration from previous launch
152
273
  config_path = build_dir / ".bedrock_agentcore.yaml"
@@ -224,7 +345,9 @@ def configure_bedrock_agentcore(
224
345
  source_path,
225
346
  protocol,
226
347
  )
227
- log.info("Generated Dockerfile: %s", dockerfile_path)
348
+ # Log with relative path for better readability
349
+ rel_dockerfile_path = get_relative_path(Path(dockerfile_path))
350
+ log.info("Generated Dockerfile: %s", rel_dockerfile_path)
228
351
 
229
352
  # Ensure .dockerignore exists at Docker build context location
230
353
  if source_path:
@@ -80,7 +80,16 @@ def destroy_bedrock_agentcore(
80
80
  _destroy_codebuild_project(session, agent_config, result, dry_run)
81
81
 
82
82
  # 5. Remove memory resource
83
- _destroy_memory(session, agent_config, result, dry_run)
83
+ if agent_config.memory and agent_config.memory.memory_id and agent_config.memory.mode != "NO_MEMORY":
84
+ if agent_config.memory.was_created_by_toolkit:
85
+ # Memory was created by toolkit during configure/launch - delete it
86
+ _destroy_memory(session, agent_config, result, dry_run)
87
+ if not dry_run:
88
+ log.info("Deleted memory (was created by toolkit): %s", agent_config.memory.memory_id)
89
+ else:
90
+ # Memory was pre-existing - preserve it
91
+ result.warnings.append(f"Memory {agent_config.memory.memory_id} preserved (was pre-existing)")
92
+ log.info("Preserving pre-existing memory: %s", agent_config.memory.memory_id)
84
93
 
85
94
  # 6. Remove CodeBuild IAM Role
86
95
  _destroy_codebuild_iam_role(session, agent_config, result, dry_run)
@@ -130,10 +139,9 @@ def _destroy_agentcore_endpoint(
130
139
  endpoint_name = endpoint_response.get("name", "DEFAULT")
131
140
  endpoint_arn = endpoint_response.get("agentRuntimeEndpointArn")
132
141
 
133
- # Special case: DEFAULT endpoint cannot be explicitly deleted
142
+ # DEFAULT endpoint is automatically deleted when agent is deleted
134
143
  if endpoint_name == "DEFAULT":
135
- result.warnings.append("DEFAULT endpoint cannot be explicitly deleted, skipping")
136
- log.info("Skipping deletion of DEFAULT endpoint")
144
+ log.info("DEFAULT endpoint will be automatically deleted with agent")
137
145
  return
138
146
 
139
147
  if dry_run:
@@ -147,7 +155,13 @@ def _destroy_agentcore_endpoint(
147
155
  result.resources_removed.append(f"AgentCore endpoint: {endpoint_arn}")
148
156
  log.info("Deleted AgentCore endpoint: %s", endpoint_arn)
149
157
  except ClientError as delete_error:
150
- if delete_error.response["Error"]["Code"] not in ["ResourceNotFoundException", "NotFound"]:
158
+ error_code = delete_error.response["Error"]["Code"]
159
+
160
+ # Handle ConflictException for DEFAULT endpoint gracefully
161
+ if error_code == "ConflictException":
162
+ log.info("DEFAULT endpoint will be automatically deleted with agent")
163
+ return
164
+ elif error_code not in ["ResourceNotFoundException", "NotFound"]:
151
165
  result.errors.append(f"Failed to delete endpoint {endpoint_arn}: {delete_error}")
152
166
  log.error("Failed to delete endpoint: %s", delete_error)
153
167
  else:
@@ -369,7 +383,9 @@ def _destroy_codebuild_project(
369
383
  """Remove CodeBuild project for this agent."""
370
384
  try:
371
385
  codebuild_client = session.client("codebuild", region_name=agent_config.aws.region)
372
- project_name = f"bedrock-agentcore-{agent_config.name}-builder"
386
+ from ...services.ecr import sanitize_ecr_repo_name
387
+
388
+ project_name = f"bedrock-agentcore-{sanitize_ecr_repo_name(agent_config.name)}-builder"
373
389
 
374
390
  if dry_run:
375
391
  result.resources_removed.append(f"CodeBuild project: {project_name} (DRY RUN)")
@@ -30,10 +30,10 @@ def invoke_bedrock_agentcore(
30
30
  project_config = load_config(config_path)
31
31
  agent_config = project_config.get_agent_config(agent_name)
32
32
 
33
- # Check memory status on first invoke if LTM is enabled
33
+ # Check memory status on first invoke if memory is enabled (STM or LTM)
34
34
  if (
35
35
  agent_config.memory
36
- and agent_config.memory.has_ltm
36
+ and agent_config.memory.mode != "NO_MEMORY"
37
37
  and agent_config.memory.memory_id
38
38
  and not agent_config.memory.first_invoke_memory_check_done
39
39
  ):
@@ -45,10 +45,19 @@ def invoke_bedrock_agentcore(
45
45
  memory_status = memory_manager.get_memory_status(agent_config.memory.memory_id)
46
46
 
47
47
  if memory_status != MemoryStatus.ACTIVE.value:
48
+ # Determine memory type for better messaging
49
+ memory_type = "Memory"
50
+ if agent_config.memory.has_ltm:
51
+ memory_type = "Long-term memory"
52
+ time_estimate = "60-180 seconds"
53
+ else:
54
+ memory_type = "Short-term memory"
55
+ time_estimate = "30-90 seconds"
56
+
48
57
  # Provide graceful error message
49
58
  error_message = (
50
59
  f"Memory is still provisioning (current status: {memory_status}). "
51
- f"Long-term memory extraction takes 60-180 seconds to activate.\n\n"
60
+ f"{memory_type} takes {time_estimate} to activate.\n\n"
52
61
  f"Please wait and check status with:\n"
53
62
  f" agentcore status{f' --agent {agent_name}' if agent_name else ''}"
54
63
  )
@@ -140,7 +140,13 @@ def _ensure_memory_for_agent(
140
140
  """Ensure memory resource exists for agent. Returns memory_id or None.
141
141
 
142
142
  This function is idempotent - it creates memory if needed or reuses existing.
143
+ CRITICAL: Never overwrites was_created_by_toolkit flag - that's set by configure.
143
144
  """
145
+ # Check if memory is disabled
146
+ if agent_config.memory and agent_config.memory.mode == "NO_MEMORY":
147
+ log.info("Memory disabled - skipping memory creation")
148
+ return None
149
+
144
150
  # If memory already exists, return it
145
151
  if agent_config.memory and agent_config.memory.memory_id:
146
152
  log.info("Using existing memory: %s", agent_config.memory.memory_id)
@@ -158,14 +164,16 @@ def _ensure_memory_for_agent(
158
164
  memory_manager = MemoryManager(region_name=agent_config.aws.region)
159
165
  memory_name = f"{agent_name}_mem" # Short name under 48 char limit
160
166
 
161
- # Check if memory already exists
167
+ # Check if memory already exists in cloud
162
168
  existing_memory = None
163
169
  try:
164
170
  memories = memory_manager.list_memories()
165
171
  for m in memories:
166
172
  if m.id.startswith(memory_name):
167
173
  existing_memory = memory_manager.get_memory(m.id)
168
- log.info("Found existing memory: %s", m.id)
174
+ log.info("Found existing memory in cloud: %s", m.id)
175
+ # DO NOT OVERWRITE was_created_by_toolkit flag
176
+ # The flag from configure tells us the user's intent
169
177
  break
170
178
  except Exception as e:
171
179
  log.debug("Error checking for existing memory: %s", e)
@@ -251,9 +259,16 @@ def _ensure_memory_for_agent(
251
259
  event_expiry_days=agent_config.memory.event_expiry_days or 30,
252
260
  memory_execution_role_arn=None,
253
261
  )
262
+
263
+ # Ensure was_created_by_toolkit is True since we just created it
264
+ # (Should already be True from configure if user chose CREATE_NEW)
265
+ if not agent_config.memory.was_created_by_toolkit:
266
+ log.warning("Memory created but flag was False - correcting to True")
267
+ agent_config.memory.was_created_by_toolkit = True
268
+
254
269
  log.info("✅ New memory created: %s (provisioning in background)", memory.id)
255
270
 
256
- # Save memory configuration
271
+ # Save memory configuration (preserving was_created_by_toolkit flag)
257
272
  agent_config.memory.memory_id = memory.id
258
273
  agent_config.memory.memory_arn = memory.arn
259
274
  agent_config.memory.memory_name = memory_name
@@ -38,7 +38,11 @@ def get_status(config_path: Path, agent_name: Optional[str] = None) -> StatusRes
38
38
  agent_arn=agent_config.bedrock_agentcore.agent_arn,
39
39
  )
40
40
 
41
- if agent_config.memory and agent_config.memory.memory_id:
41
+ # Check if memory is disabled first
42
+ if agent_config.memory and agent_config.memory.mode == "NO_MEMORY":
43
+ config_info.memory_type = "Disabled"
44
+ config_info.memory_enabled = False
45
+ elif agent_config.memory and agent_config.memory.memory_id:
42
46
  try:
43
47
  from ...operations.memory.manager import MemoryManager
44
48
 
@@ -91,9 +95,6 @@ def get_status(config_path: Path, agent_name: Optional[str] = None) -> StatusRes
91
95
 
92
96
  config_info.memory_id = agent_config.memory.memory_id
93
97
  config_info.memory_status = memory_status
94
-
95
- # Add the detailed memory info to config_info
96
- # You'll need to add this field to StatusConfigInfo model
97
98
  config_info.memory_details = memory_details
98
99
 
99
100
  except Exception as e:
@@ -14,6 +14,7 @@ import boto3
14
14
  from botocore.exceptions import ClientError
15
15
 
16
16
  from ..operations.runtime.create_role import get_or_create_codebuild_execution_role
17
+ from .ecr import sanitize_ecr_repo_name
17
18
 
18
19
 
19
20
  class CodeBuildService:
@@ -159,7 +160,7 @@ class CodeBuildService:
159
160
  self, agent_name: str, ecr_repository_uri: str, execution_role: str, source_location: str
160
161
  ) -> str:
161
162
  """Create or update CodeBuild project for ARM64 builds."""
162
- project_name = f"bedrock-agentcore-{agent_name}-builder"
163
+ project_name = f"bedrock-agentcore-{sanitize_ecr_repo_name(agent_name)}-builder"
163
164
 
164
165
  buildspec = self._get_arm64_buildspec(ecr_repository_uri)
165
166
 
@@ -1,12 +1,54 @@
1
1
  """ECR (Elastic Container Registry) service integration."""
2
2
 
3
3
  import base64
4
+ import re
4
5
 
5
6
  import boto3
6
7
 
7
8
  from ..utils.runtime.container import ContainerRuntime
8
9
 
9
10
 
11
+ def sanitize_ecr_repo_name(name: str) -> str:
12
+ """Sanitize agent name for ECR repository naming requirements.
13
+
14
+ ECR repository names must:
15
+ - Contain only lowercase letters, numbers, hyphens (-), underscores (_), and forward slashes (/)
16
+ - Start with a lowercase letter or number
17
+ - Be between 2 and 256 characters
18
+
19
+ Args:
20
+ name: Agent name to sanitize
21
+
22
+ Returns:
23
+ Sanitized repository name component
24
+ """
25
+ # Convert to lowercase
26
+ name = name.lower()
27
+
28
+ # Replace invalid characters with hyphens
29
+ name = re.sub(r"[^a-z0-9_\-/]", "-", name)
30
+
31
+ # Ensure starts with alphanumeric
32
+ if name and not name[0].isalnum():
33
+ name = "a" + name # Prefix with 'a' if starts with non-alphanumeric
34
+
35
+ # Remove consecutive hyphens/underscores
36
+ name = re.sub(r"[-_]{2,}", "-", name)
37
+
38
+ # Strip trailing hyphens/underscores
39
+ name = name.rstrip("-_")
40
+
41
+ # Ensure minimum length
42
+ if len(name) < 2:
43
+ name = name + "-agent"
44
+
45
+ # Truncate if too long (leave room for prefix)
46
+ if len(name) > 200:
47
+ name = name[:200].rstrip("-_")
48
+
49
+ return name
50
+
51
+
10
52
  def get_account_id() -> str:
11
53
  """Get AWS account ID."""
12
54
  return boto3.client("sts").get_caller_identity()["Account"]
@@ -38,8 +80,8 @@ def get_or_create_ecr_repository(agent_name: str, region: str) -> str:
38
80
  Returns:
39
81
  ECR repository URI
40
82
  """
41
- # Generate deterministic repository name based on agent name
42
- repo_name = f"bedrock-agentcore-{agent_name}"
83
+ # Generate deterministic repository name based on agent name (sanitized for ECR requirements)
84
+ repo_name = f"bedrock-agentcore-{sanitize_ecr_repo_name(agent_name)}"
43
85
 
44
86
  ecr = boto3.client("ecr", region_name=region)
45
87
 
@@ -44,8 +44,10 @@ class ContainerRuntime:
44
44
  else:
45
45
  # Informational message - default CodeBuild deployment works fine
46
46
  console.print("\n💡 [cyan]No container engine found (Docker/Finch/Podman not installed)[/cyan]")
47
- _print_success("Default deployment uses CodeBuild (no container engine needed)")
48
- console.print("[dim]For local builds, install Docker, Finch, or Podman[/dim]")
47
+ _print_success(
48
+ "Default deployment uses CodeBuild (no container engine needed), "
49
+ "For local builds, install Docker, Finch, or Podman"
50
+ )
49
51
  self.runtime = "none"
50
52
  self.has_local_runtime = False
51
53
  elif runtime_type in self.available_runtimes:
@@ -131,9 +133,10 @@ class ContainerRuntime:
131
133
 
132
134
  if current_platform != required_platform:
133
135
  _handle_warn(
134
- f"[WARNING] Platform mismatch: Current system is '{current_platform}' "
135
- f"but Bedrock AgentCore requires '{required_platform}'.\n"
136
- "For deployment options and workarounds, see: "
136
+ f"Platform mismatch: Current system is '{current_platform}' "
137
+ f"but Bedrock AgentCore requires '{required_platform}', so local builds won't work.\n"
138
+ "Please use default launch command which will do a remote cross-platform build using code build."
139
+ "For deployment other options and workarounds, see: "
137
140
  "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html\n"
138
141
  )
139
142
 
@@ -150,9 +153,8 @@ class ContainerRuntime:
150
153
  # If source_path provided: module path relative to source_path (Docker build context)
151
154
  # Otherwise: module path relative to project root
152
155
  build_context_root = Path(source_path) if source_path else output_dir
153
-
154
156
  # Generate .dockerignore if it doesn't exist
155
- self._ensure_dockerignore(output_dir)
157
+ self._ensure_dockerignore(build_context_root)
156
158
 
157
159
  # Validate module path against build context root
158
160
  self._validate_module_path(agent_path, build_context_root)
@@ -167,11 +169,12 @@ class ContainerRuntime:
167
169
  # - Otherwise: check project root (output_dir)
168
170
  # - If explicit requirements_file provided: use that regardless
169
171
  if source_path and not requirements_file:
170
- # Check source_path directory for dependencies
171
172
  source_dir = Path(source_path)
172
173
  deps = detect_dependencies(source_dir, explicit_file=None)
174
+ if source_path:
175
+ source_dir = Path(source_path)
176
+ deps = detect_dependencies(source_dir, explicit_file=requirements_file)
173
177
  else:
174
- # Check project root for dependencies (or use explicit file)
175
178
  deps = detect_dependencies(output_dir, explicit_file=requirements_file)
176
179
 
177
180
  # Add logic to avoid duplicate installation
@@ -1,6 +1,5 @@
1
1
  """Typed configuration schema for Bedrock AgentCore SDK."""
2
2
 
3
- from pathlib import Path
4
3
  from typing import Dict, List, Literal, Optional
5
4
 
6
5
  from pydantic import BaseModel, Field, field_validator
@@ -16,7 +15,7 @@ class NetworkModeConfig(BaseModel):
16
15
  class MemoryConfig(BaseModel):
17
16
  """Memory configuration for BedrockAgentCore."""
18
17
 
19
- mode: Literal["STM_ONLY", "STM_AND_LTM"] = Field(
18
+ mode: Literal["STM_ONLY", "STM_AND_LTM", "NO_MEMORY"] = Field(
20
19
  default="STM_ONLY", description="Memory mode - always has STM, optionally adds LTM"
21
20
  )
22
21
  memory_id: Optional[str] = Field(default=None, description="Memory resource ID")
@@ -26,11 +25,14 @@ class MemoryConfig(BaseModel):
26
25
  first_invoke_memory_check_done: bool = Field(
27
26
  default=False, description="Whether first invoke memory check has been performed"
28
27
  )
28
+ was_created_by_toolkit: bool = Field(
29
+ default=False, description="Whether memory was created by toolkit (vs reused existing)"
30
+ )
29
31
 
30
32
  @property
31
33
  def is_enabled(self) -> bool:
32
- """Check if memory is enabled (always true now)."""
33
- return True
34
+ """Check if memory is enabled."""
35
+ return self.mode != "NO_MEMORY"
34
36
 
35
37
  @property
36
38
  def has_ltm(self) -> bool:
@@ -158,37 +160,6 @@ class BedrockAgentCoreAgentSchema(BaseModel):
158
160
  request_header_configuration: Optional[dict] = Field(default=None, description="Request header configuration")
159
161
  oauth_configuration: Optional[dict] = Field(default=None, description="Oauth configuration")
160
162
 
161
- @field_validator("source_path")
162
- @classmethod
163
- def validate_source_path(cls, v: Optional[str]) -> Optional[str]:
164
- """Validate source path if provided.
165
-
166
- Args:
167
- v: Source path value
168
-
169
- Returns:
170
- Validated source path or None
171
-
172
- Raises:
173
- ValueError: If source path is invalid
174
- """
175
- if v is None:
176
- return v
177
-
178
- # Convert to Path for validation
179
- source_path = Path(v)
180
-
181
- # Check if path exists
182
- if not source_path.exists():
183
- raise ValueError(f"Source path does not exist: {v}")
184
-
185
- # Check if it's a directory
186
- if not source_path.is_dir():
187
- raise ValueError(f"Source path must be a directory: {v}")
188
-
189
- # Return absolute path string
190
- return str(source_path.resolve())
191
-
192
163
  def get_authorizer_configuration(self) -> Optional[dict]:
193
164
  """Get the authorizer configuration."""
194
165
  return self.authorizer_configuration
@@ -28,12 +28,6 @@ RUN uv pip install -r {{ dependencies_file }}
28
28
  RUN uv pip install aws-opentelemetry-distro>=0.10.1
29
29
  {% endif %}
30
30
 
31
- # Set AWS region environment variable
32
- {% if aws_region %}
33
- ENV AWS_REGION={{ aws_region }}
34
- ENV AWS_DEFAULT_REGION={{ aws_region }}
35
- {% endif %}
36
-
37
31
  # Signal that this is running in Docker for host binding logic
38
32
  ENV DOCKER_CONTAINER=1
39
33
 
@@ -143,19 +143,6 @@
143
143
  "arn:aws:bedrock-agentcore:{{ region }}:{{ account_id }}:workload-identity-directory/default/workload-identity/{{ agent_name }}-*"
144
144
  ]
145
145
  },
146
- {
147
- "Sid": "BedrockAgentCoreIdentityGetWorkloadAccessToken",
148
- "Effect": "Allow",
149
- "Action": [
150
- "bedrock-agentcore:GetWorkloadAccessToken",
151
- "bedrock-agentcore:GetWorkloadAccessTokenForJWT",
152
- "bedrock-agentcore:GetWorkloadAccessTokenForUserId"
153
- ],
154
- "Resource": [
155
- "arn:aws:bedrock-agentcore:{{ region }}:{{ account_id }}:workload-identity-directory/default",
156
- "arn:aws:bedrock-agentcore:{{ region }}:{{ account_id }}:workload-identity-directory/default/workload-identity/{{ agent_name }}-*"
157
- ]
158
- },
159
146
  {
160
147
  "Sid": "BedrockModelInvocation",
161
148
  "Effect": "Allow",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bedrock-agentcore-starter-toolkit
3
- Version: 0.1.22
3
+ Version: 0.1.24
4
4
  Summary: A starter toolkit for using Bedrock AgentCore
5
5
  Project-URL: Homepage, https://github.com/aws/bedrock-agentcore-starter-toolkit
6
6
  Project-URL: Bug Tracker, https://github.com/aws/bedrock-agentcore-starter-toolkit/issues