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
@@ -20,7 +20,7 @@ def _handle_error(message: str, exception: Optional[Exception] = None) -> NoRetu
20
20
 
21
21
  def _handle_warn(message: str) -> None:
22
22
  """Handle errors with consistent formatting and exit."""
23
- console.print(f"⚠️ {message}", new_line_start=True, style="bold yellow underline")
23
+ console.print(f"⚠️ {message}", new_line_start=True, style="yellow")
24
24
 
25
25
 
26
26
  def _print_success(message: str) -> None:
@@ -15,13 +15,16 @@ from rich.syntax import Syntax
15
15
  from ...operations.runtime import (
16
16
  configure_bedrock_agentcore,
17
17
  destroy_bedrock_agentcore,
18
+ detect_entrypoint,
19
+ detect_requirements,
20
+ get_relative_path,
18
21
  get_status,
22
+ infer_agent_name,
19
23
  invoke_bedrock_agentcore,
20
24
  launch_bedrock_agentcore,
21
25
  validate_agent_name,
22
26
  )
23
27
  from ...utils.runtime.config import load_config
24
- from ...utils.runtime.entrypoint import parse_entrypoint
25
28
  from ...utils.runtime.logs import get_agent_log_paths, get_aws_tail_commands, get_genai_observability_url
26
29
  from ..common import _handle_error, _print_success, console
27
30
  from .configuration_manager import ConfigurationManager
@@ -47,22 +50,50 @@ def _show_configuration_not_found_panel():
47
50
 
48
51
 
49
52
  def _validate_requirements_file(file_path: str) -> str:
50
- """Validate requirements file and return the path."""
53
+ """Validate requirements file and return the absolute path."""
51
54
  from ...utils.runtime.entrypoint import validate_requirements_file
52
55
 
53
56
  try:
54
57
  deps = validate_requirements_file(Path.cwd(), file_path)
55
- _print_success(f"Using requirements file: [dim]{deps.resolved_path}[/dim]")
56
- return file_path
58
+ rel_path = get_relative_path(Path(deps.resolved_path))
59
+ _print_success(f"Using requirements file: [dim]{rel_path}[/dim]")
60
+ # Return absolute path for consistency with entrypoint handling
61
+ return str(Path(deps.resolved_path).resolve())
57
62
  except (FileNotFoundError, ValueError) as e:
58
63
  _handle_error(str(e), e)
59
64
 
60
65
 
61
- def _prompt_for_requirements_file(prompt_text: str, default: str = "") -> Optional[str]:
62
- """Prompt user for requirements file path with validation."""
63
- response = prompt(prompt_text, completer=PathCompleter(), default=default)
66
+ def _prompt_for_requirements_file(prompt_text: str, source_path: str, default: str = "") -> Optional[str]:
67
+ """Prompt user for requirements file path with validation.
68
+
69
+ Args:
70
+ prompt_text: Prompt message to display
71
+ source_path: Source directory path for validation
72
+ default: Default path to pre-populate
73
+ """
74
+ # Pre-populate with relative source directory path if no default provided
75
+ if not default:
76
+ rel_source = get_relative_path(Path(source_path))
77
+ default = f"{rel_source}/"
78
+
79
+ # Use PathCompleter without filter - allow navigation anywhere
80
+ response = prompt(prompt_text, completer=PathCompleter(), complete_while_typing=True, default=default)
64
81
 
65
82
  if response.strip():
83
+ # Validate file exists and is in source directory
84
+ req_file = Path(response.strip()).resolve()
85
+ source_dir = Path(source_path).resolve()
86
+
87
+ # Check if requirements file is within source directory
88
+ try:
89
+ if not req_file.is_relative_to(source_dir):
90
+ rel_source = get_relative_path(source_dir)
91
+ console.print(f"[red]Error: Requirements file must be in source directory: {rel_source}[/red]")
92
+ return _prompt_for_requirements_file(prompt_text, source_path, default)
93
+ except (ValueError, AttributeError):
94
+ # is_relative_to not available or other error - skip validation
95
+ pass
96
+
66
97
  return _validate_requirements_file(response.strip())
67
98
 
68
99
  return None
@@ -76,49 +107,45 @@ def _handle_requirements_file_display(
76
107
  Args:
77
108
  requirements_file: Explicit requirements file path
78
109
  non_interactive: Whether to skip interactive prompts
79
- source_path: Optional source code directory (checks here first, then falls back to project root)
110
+ source_path: Optional source code directory
80
111
  """
81
- from ...utils.runtime.entrypoint import detect_dependencies
82
-
83
112
  if requirements_file:
84
113
  # User provided file - validate and show confirmation
85
114
  return _validate_requirements_file(requirements_file)
86
115
 
87
- # Detect dependencies:
88
- # - If source_path provided: check source_path only
89
- # - Otherwise: check project root (Path.cwd())
90
- if source_path:
91
- source_dir = Path(source_path)
92
- deps = detect_dependencies(source_dir)
93
- else:
94
- # No source_path, check project root
95
- deps = detect_dependencies(Path.cwd())
116
+ # Use operations layer for detection - source_path is always provided
117
+ deps = detect_requirements(Path(source_path))
96
118
 
97
119
  if non_interactive:
98
120
  # Auto-detection for non-interactive mode
99
121
  if deps.found:
100
- _print_success(f"Using detected file: [dim]{deps.file}[/dim]")
122
+ rel_deps_path = get_relative_path(Path(deps.resolved_path))
123
+ _print_success(f"Using detected requirements file: [cyan]{rel_deps_path}[/cyan]")
101
124
  return None # Use detected file
102
125
  else:
103
126
  _handle_error("No requirements file specified and none found automatically")
104
127
 
105
128
  # Auto-detection with interactive prompt
106
129
  if deps.found:
107
- console.print(f"\n🔍 [cyan]Detected dependency file:[/cyan] [bold]{deps.file}[/bold]")
130
+ rel_deps_path = get_relative_path(Path(deps.resolved_path))
131
+
132
+ console.print(f"\n🔍 [cyan]Detected dependency file:[/cyan] [bold]{rel_deps_path}[/bold]")
108
133
  console.print("[dim]Press Enter to use this file, or type a different path (use Tab for autocomplete):[/dim]")
109
134
 
110
- result = _prompt_for_requirements_file("Path or Press Enter to use detected dependency file: ", default="")
135
+ result = _prompt_for_requirements_file(
136
+ "Path or Press Enter to use detected dependency file: ", source_path=source_path, default=rel_deps_path
137
+ )
111
138
 
112
139
  if result is None:
113
140
  # Use detected file
114
- _print_success(f"Using detected file: [dim]{deps.file}[/dim]")
141
+ _print_success(f"Using detected requirements file: [cyan]{rel_deps_path}[/cyan]")
115
142
 
116
143
  return result
117
144
  else:
118
145
  console.print("\n[yellow]⚠️ No dependency file found (requirements.txt or pyproject.toml)[/yellow]")
119
146
  console.print("[dim]Enter path to requirements file (use Tab for autocomplete), or press Enter to skip:[/dim]")
120
147
 
121
- result = _prompt_for_requirements_file("Path: ")
148
+ result = _prompt_for_requirements_file("Path: ", source_path=source_path)
122
149
 
123
150
  if result is None:
124
151
  _handle_error("No requirements file specified and none found automatically")
@@ -126,6 +153,29 @@ def _handle_requirements_file_display(
126
153
  return result
127
154
 
128
155
 
156
+ def _detect_entrypoint_in_source(source_path: str, non_interactive: bool = False) -> str:
157
+ """Detect entrypoint file in source directory with CLI display."""
158
+ source_dir = Path(source_path)
159
+
160
+ # Use operations layer for detection
161
+ detected = detect_entrypoint(source_dir)
162
+
163
+ if not detected:
164
+ # No fallback prompt - fail with clear error message
165
+ rel_source = get_relative_path(source_dir)
166
+ _handle_error(
167
+ f"No entrypoint file found in {rel_source}\n"
168
+ f"Expected one of: main.py, agent.py, app.py, __main__.py\n"
169
+ f"Please specify full file path (e.g., {rel_source}/your_agent.py)"
170
+ )
171
+
172
+ # Show detection and confirm
173
+ rel_entrypoint = get_relative_path(detected)
174
+
175
+ _print_success(f"Using entrypoint file: [cyan]{rel_entrypoint}[/cyan]")
176
+ return str(detected)
177
+
178
+
129
179
  # Define options at module level to avoid B008
130
180
  ENV_OPTION = typer.Option(None, "--env", "-env", help="Environment variables for local mode (format: KEY=VALUE)")
131
181
 
@@ -179,7 +229,12 @@ def set_default(name: str = typer.Argument(...)):
179
229
  @configure_app.callback(invoke_without_command=True)
180
230
  def configure(
181
231
  ctx: typer.Context,
182
- entrypoint: Optional[str] = typer.Option(None, "--entrypoint", "-e", help="Python file with BedrockAgentCoreApp"),
232
+ entrypoint: Optional[str] = typer.Option(
233
+ None,
234
+ "--entrypoint",
235
+ "-e",
236
+ help="Entry point: file path (e.g., agent.py) or directory path (auto-detects main.py, agent.py, app.py)",
237
+ ),
183
238
  agent_name: Optional[str] = typer.Option(None, "--name", "-n"),
184
239
  execution_role: Optional[str] = typer.Option(None, "--execution-role", "-er"),
185
240
  code_build_execution_role: Optional[str] = typer.Option(None, "--code-build-execution-role", "-cber"),
@@ -189,6 +244,7 @@ def configure(
189
244
  None, "--requirements-file", "-rf", help="Path to requirements file"
190
245
  ),
191
246
  disable_otel: bool = typer.Option(False, "--disable-otel", "-do", help="Disable OpenTelemetry"),
247
+ disable_memory: bool = typer.Option(False, "--disable-memory", "-dm", help="Disable memory"),
192
248
  authorizer_config: Optional[str] = typer.Option(
193
249
  None, "--authorizer-config", "-ac", help="OAuth authorizer configuration as JSON string"
194
250
  ),
@@ -205,35 +261,75 @@ def configure(
205
261
  non_interactive: bool = typer.Option(
206
262
  False, "--non-interactive", "-ni", help="Skip prompts; use defaults unless overridden"
207
263
  ),
208
- source_path: Optional[str] = typer.Option(None, "--source-path", "-sp", help="Path to agent source code directory"),
209
264
  ):
210
- """Configure a Bedrock AgentCore agent. The agent name defaults to your Python file name."""
265
+ """Configure a Bedrock AgentCore agent interactively or with parameters.
266
+
267
+ Examples:
268
+ agentcore configure # Fully interactive (current directory)
269
+ agentcore configure --entrypoint writer/ # Directory (auto-detect entrypoint)
270
+ agentcore configure --entrypoint agent.py # File (use as entrypoint)
271
+ """
211
272
  if ctx.invoked_subcommand is not None:
212
273
  return
213
274
 
214
- if not entrypoint:
215
- _handle_error("--entrypoint is required")
216
-
217
275
  if protocol and protocol.upper() not in ["HTTP", "MCP", "A2A"]:
218
276
  _handle_error("Error: --protocol must be either HTTP or MCP or A2A")
219
277
 
220
278
  console.print("[cyan]Configuring Bedrock AgentCore...[/cyan]")
221
- try:
222
- _, file_name = parse_entrypoint(entrypoint)
223
- agent_name = agent_name or file_name
224
-
225
- valid, error = validate_agent_name(agent_name)
226
- if not valid:
227
- _handle_error(error)
228
-
229
- console.print(f"[dim]Agent name: {agent_name}[/dim]")
230
- except ValueError as e:
231
- _handle_error(f"Error: {e}", e)
232
279
 
233
- # Create configuration manager for clean, elegant prompting
280
+ # Create configuration manager early for consistent prompting
234
281
  config_path = Path.cwd() / ".bedrock_agentcore.yaml"
235
282
  config_manager = ConfigurationManager(config_path, non_interactive)
236
283
 
284
+ # Interactive entrypoint selection
285
+ if not entrypoint:
286
+ if non_interactive:
287
+ entrypoint_input = "."
288
+ else:
289
+ console.print("\n📂 [cyan]Entrypoint Selection[/cyan]")
290
+ console.print("[dim]Specify the entry point (use Tab for autocomplete):[/dim]")
291
+ console.print("[dim] • File path: weather/agent.py[/dim]")
292
+ console.print("[dim] • Directory: weather/ (auto-detects main.py, agent.py, app.py)[/dim]")
293
+ console.print("[dim] • Current directory: press Enter[/dim]")
294
+
295
+ entrypoint_input = (
296
+ prompt("Entrypoint: ", completer=PathCompleter(), complete_while_typing=True, default="").strip() or "."
297
+ )
298
+ else:
299
+ entrypoint_input = entrypoint
300
+
301
+ # Resolve the entrypoint_input (handles both file and directory)
302
+ entrypoint_path = Path(entrypoint_input).resolve()
303
+
304
+ if entrypoint_path.is_file():
305
+ # It's a file - use directly as entrypoint
306
+ entrypoint = str(entrypoint_path)
307
+ source_path = str(entrypoint_path.parent)
308
+ if not non_interactive:
309
+ rel_path = get_relative_path(entrypoint_path)
310
+ _print_success(f"Using file: {rel_path}")
311
+ elif entrypoint_path.is_dir():
312
+ # It's a directory - detect entrypoint within it
313
+ source_path = str(entrypoint_path)
314
+ entrypoint = _detect_entrypoint_in_source(source_path, non_interactive)
315
+ else:
316
+ _handle_error(f"Path not found: {entrypoint_input}")
317
+
318
+ # Process agent name
319
+ entrypoint_path = Path(entrypoint)
320
+
321
+ # Infer agent name from full entrypoint path (e.g., agents/writer/main.py -> agents_writer_main)
322
+ if not agent_name:
323
+ suggested_name = infer_agent_name(entrypoint_path)
324
+ agent_name = config_manager.prompt_agent_name(suggested_name)
325
+
326
+ valid, error = validate_agent_name(agent_name)
327
+ if not valid:
328
+ _handle_error(error)
329
+
330
+ # Handle dependency file selection with simplified logic
331
+ final_requirements_file = _handle_requirements_file_display(requirements_file, non_interactive, source_path)
332
+
237
333
  # Interactive prompts for missing values - clean and elegant
238
334
  if not execution_role:
239
335
  execution_role = config_manager.prompt_execution_role()
@@ -252,9 +348,6 @@ def configure(
252
348
  auto_create_ecr = False
253
349
  _print_success(f"Using existing ECR repository: [dim]{ecr_repository}[/dim]")
254
350
 
255
- # Handle dependency file selection with simplified logic
256
- final_requirements_file = _handle_requirements_file_display(requirements_file, non_interactive, source_path)
257
-
258
351
  # Handle OAuth authorization configuration
259
352
  oauth_config = None
260
353
  if authorizer_config:
@@ -280,6 +373,11 @@ def configure(
280
373
  else:
281
374
  request_header_config = config_manager.prompt_request_header_allowlist()
282
375
 
376
+ if disable_memory:
377
+ memory_mode_value = "NO_MEMORY"
378
+ else:
379
+ memory_mode_value = "STM_ONLY"
380
+
283
381
  try:
284
382
  result = configure_bedrock_agentcore(
285
383
  agent_name=agent_name,
@@ -290,6 +388,7 @@ def configure(
290
388
  container_runtime=container_runtime,
291
389
  auto_create_ecr=auto_create_ecr,
292
390
  enable_observability=not disable_otel,
391
+ memory_mode=memory_mode_value,
293
392
  requirements_file=final_requirements_file,
294
393
  authorizer_configuration=oauth_config,
295
394
  request_header_configuration=request_header_config,
@@ -311,22 +410,26 @@ def configure(
311
410
  headers = request_header_config.get("requestHeaderAllowlist", [])
312
411
  headers_info = f"Request Headers Allowlist: [dim]{len(headers)} headers configured[/dim]\n"
313
412
 
413
+ execution_role_display = "Auto-create" if not result.execution_role else result.execution_role
414
+ memory_info = "Short-term memory (30-day retention)"
415
+ if disable_memory:
416
+ memory_info = "Disabled"
417
+
314
418
  console.print(
315
419
  Panel(
316
- f"[green]Configuration Complete[/green]\n\n"
317
- f"[bold]Agent Details:[/bold]\n"
420
+ f"[bold]Agent Details[/bold]\n"
318
421
  f"Agent Name: [cyan]{agent_name}[/cyan]\n"
319
422
  f"Runtime: [cyan]{result.runtime}[/cyan]\n"
320
423
  f"Region: [cyan]{result.region}[/cyan]\n"
321
- f"Account: [dim]{result.account_id}[/dim]\n\n"
322
- f"[bold]Configuration:[/bold]\n"
323
- f"Execution Role: [dim]{result.execution_role}[/dim]\n"
324
- f"ECR Repository: [dim]"
424
+ f"Account: [cyan]{result.account_id}[/cyan]\n\n"
425
+ f"[bold]Configuration[/bold]\n"
426
+ f"Execution Role: [cyan]{execution_role_display}[/cyan]\n"
427
+ f"ECR Repository: [cyan]"
325
428
  f"{'Auto-create' if result.auto_create_ecr else result.ecr_repository or 'N/A'}"
326
- f"[/dim]\n"
327
- f"Authorization: [dim]{auth_info}[/dim]\n\n"
429
+ f"[/cyan]\n"
430
+ f"Authorization: [cyan]{auth_info}[/cyan]\n\n"
328
431
  f"{headers_info}\n"
329
- f"Memory: [dim]Short-term memory (30-day retention)[/dim]\n\n"
432
+ f"Memory: [cyan]{memory_info}[/cyan]\n\n"
330
433
  f"📄 Config saved to: [dim]{result.config_path}[/dim]\n\n"
331
434
  f"[bold]Next Steps:[/bold]\n"
332
435
  f" [cyan]agentcore launch[/cyan]",
@@ -490,7 +593,6 @@ def launch(
490
593
  region = agent_config.aws.region if agent_config else "us-east-1"
491
594
 
492
595
  deploy_panel = (
493
- f"✅ [green]CodeBuild Deployment Successful![/green]\n\n"
494
596
  f"[bold]Agent Details:[/bold]\n"
495
597
  f"Agent Name: [cyan]{agent_name}[/cyan]\n"
496
598
  f"Agent ARN: [cyan]{result.agent_arn}[/cyan]\n"
@@ -530,15 +632,12 @@ def launch(
530
632
 
531
633
  if local_build:
532
634
  title = "Local Build Success"
533
- deployment_type = "✅ [green]Local Build Deployment Successful![/green]"
534
635
  icon = "🔧"
535
636
  else:
536
637
  title = "Deployment Success"
537
- deployment_type = "✅ [green]Deployment Successful![/green]"
538
638
  icon = "🚀"
539
639
 
540
640
  deploy_panel = (
541
- f"{deployment_type}\n\n"
542
641
  f"[bold]Agent Details:[/bold]\n"
543
642
  f"Agent Name: [cyan]{agent_name}[/cyan]\n"
544
643
  f"Agent ARN: [cyan]{result.agent_arn}[/cyan]\n"
@@ -10,18 +10,43 @@ from ..common import _handle_error, _print_success, _prompt_with_default, consol
10
10
  class ConfigurationManager:
11
11
  """Manages interactive configuration prompts with existing configuration defaults."""
12
12
 
13
- def __init__(self, config_path: Path, non_interactive: bool = False):
13
+ def __init__(self, config_path: Path, non_interactive: bool = False, region: Optional[str] = None):
14
14
  """Initialize the ConfigPrompt with a configuration path.
15
15
 
16
16
  Args:
17
17
  config_path: Path to the configuration file
18
18
  non_interactive: If True, use defaults without prompting
19
+ region: AWS region for checking existing memories (optional, from configure operation)
19
20
  """
20
21
  from ...utils.runtime.config import load_config_if_exists
21
22
 
22
23
  project_config = load_config_if_exists(config_path)
23
24
  self.existing_config = project_config.get_agent_config() if project_config else None
24
25
  self.non_interactive = non_interactive
26
+ self.region = region
27
+
28
+ def prompt_agent_name(self, suggested_name: str) -> str:
29
+ """Prompt for agent name with a suggested default.
30
+
31
+ Args:
32
+ suggested_name: The suggested agent name based on entrypoint path
33
+
34
+ Returns:
35
+ The selected or entered agent name
36
+ """
37
+ if self.non_interactive:
38
+ _print_success(f"Agent name (inferred): {suggested_name}")
39
+ return suggested_name
40
+
41
+ console.print(f"\n🏷️ [cyan]Inferred agent name[/cyan]: {suggested_name}")
42
+ console.print("[dim]Press Enter to use this name, or type a different one (alphanumeric without '-')[/dim]")
43
+ agent_name = _prompt_with_default("Agent name", suggested_name)
44
+
45
+ if not agent_name:
46
+ agent_name = suggested_name
47
+
48
+ _print_success(f"Using agent name: [cyan]{agent_name}[/cyan]")
49
+ return agent_name
25
50
 
26
51
  def prompt_execution_role(self) -> Optional[str]:
27
52
  """Prompt for execution role. Returns role name/ARN or None for auto-creation."""
@@ -205,7 +230,7 @@ class ConfigurationManager:
205
230
  Returns:
206
231
  Tuple of (enable_memory, enable_ltm)
207
232
  """
208
- console.print("\n🧠 [cyan]Memory Configuration[/cyan]")
233
+ console.print("\n[cyan]Memory Configuration[/cyan]")
209
234
  console.print("Short-term memory stores conversation within sessions.")
210
235
  console.print("Long-term memory extracts preferences and facts across sessions.")
211
236
  console.print()
@@ -238,7 +263,7 @@ class ConfigurationManager:
238
263
  return enable_memory, enable_ltm
239
264
 
240
265
  def prompt_memory_selection(self) -> Tuple[str, str]:
241
- """Prompt user to select existing memory or create new.
266
+ """Prompt user to select existing memory or create new (no skip option).
242
267
 
243
268
  Returns:
244
269
  Tuple of (action, value) where:
@@ -249,23 +274,26 @@ class ConfigurationManager:
249
274
  # In non-interactive mode, default to creating new STM
250
275
  return ("CREATE_NEW", "STM_ONLY")
251
276
 
252
- console.print("\n🧠 [cyan]Memory Configuration[/cyan]")
277
+ console.print("\n[cyan]Memory Configuration[/cyan]")
278
+ console.print("[dim]Tip: Use --disable-memory flag to skip memory entirely[/dim]\n")
253
279
 
254
280
  # Try to list existing memories
255
281
  try:
256
282
  from ...operations.memory.manager import MemoryManager
257
283
 
258
- # Need region - will be passed from configure.py
259
- region = self.existing_config.aws.region if self.existing_config else None
284
+ # Get region from passed parameter OR existing config
285
+ region = self.region or (self.existing_config.aws.region if self.existing_config else None)
286
+
260
287
  if not region:
261
- # Fall back to new memory creation if no region
288
+ # No region available - skip to new memory creation
289
+ console.print("[dim]No region configured yet, proceeding with new memory creation[/dim]")
262
290
  return self._prompt_new_memory_config()
263
291
 
264
292
  memory_manager = MemoryManager(region_name=region)
265
293
  existing_memories = memory_manager.list_memories(max_results=10)
266
294
 
267
295
  if existing_memories:
268
- console.print("\n[cyan]Existing memory resources found:[/cyan]")
296
+ console.print("[cyan]Existing memory resources found:[/cyan]")
269
297
  for i, mem in enumerate(existing_memories, 1):
270
298
  # Display memory summary
271
299
  mem_id = mem.get("id", "unknown")
@@ -283,7 +311,6 @@ class ConfigurationManager:
283
311
  console.print("\n[dim]Options:[/dim]")
284
312
  console.print("[dim] • Enter a number to use existing memory[/dim]")
285
313
  console.print("[dim] • Press Enter to create new memory[/dim]")
286
- console.print("[dim] • Type 's' to skip memory setup[/dim]")
287
314
 
288
315
  response = _prompt_with_default("Your choice", "").strip().lower()
289
316
 
@@ -293,9 +320,11 @@ class ConfigurationManager:
293
320
  selected = existing_memories[idx]
294
321
  _print_success(f"Using existing memory: {selected.get('name', selected.get('id'))}")
295
322
  return ("USE_EXISTING", selected.get("id"))
296
- elif response == "s":
297
- _print_success("Skipping memory configuration")
298
- return ("SKIP", None)
323
+ else:
324
+ # No existing memories found
325
+ console.print("[yellow]No existing memory resources found in your account[/yellow]")
326
+ console.print("[dim]Proceeding with new memory creation...[/dim]\n")
327
+
299
328
  except Exception as e:
300
329
  console.print(f"[dim]Could not list existing memories: {e}[/dim]")
301
330
 
@@ -303,8 +332,8 @@ class ConfigurationManager:
303
332
  return self._prompt_new_memory_config()
304
333
 
305
334
  def _prompt_new_memory_config(self) -> Tuple[str, str]:
306
- """Prompt for new memory configuration."""
307
- console.print("[green]✓ Short-term memory is enabled by default[/green]")
335
+ """Prompt for new memory configuration (no skip option)."""
336
+ console.print("[green]✓ Short-term memory will be enabled (default)[/green]")
308
337
  console.print(" • Stores conversations within sessions")
309
338
  console.print(" • Provides immediate context recall")
310
339
  console.print()
@@ -312,10 +341,10 @@ class ConfigurationManager:
312
341
  console.print(" • Extracts user preferences across sessions")
313
342
  console.print(" • Remembers facts and patterns")
314
343
  console.print(" • Creates session summaries")
315
- console.print(" • [dim]Note: Takes 60-90 seconds to process[/dim]")
344
+ console.print(" • [dim]Note: Takes 120-180 seconds to process[/dim]")
316
345
  console.print()
317
346
 
318
- response = _prompt_with_default("Enable long-term memory extraction? (yes/no)", "no").strip().lower()
347
+ response = _prompt_with_default("Enable long-term memory? (yes/no)", "no").strip().lower()
319
348
 
320
349
  if response in ["yes", "y"]:
321
350
  _print_success("Configuring short-term + long-term memory")
@@ -48,6 +48,7 @@ class Runtime:
48
48
  region: Optional[str] = None,
49
49
  protocol: Optional[Literal["HTTP", "MCP", "A2A"]] = None,
50
50
  disable_otel: bool = False,
51
+ memory_mode: Literal["NO_MEMORY", "STM_ONLY", "STM_AND_LTM"] = "STM_ONLY",
51
52
  non_interactive: bool = True,
52
53
  ) -> ConfigureResult:
53
54
  """Configure Bedrock AgentCore from notebook using an entrypoint file.
@@ -69,10 +70,27 @@ class Runtime:
69
70
  region: AWS region for deployment
70
71
  protocol: agent server protocol, must be either HTTP or MCP or A2A
71
72
  disable_otel: Whether to disable OpenTelemetry observability (default: False)
73
+ memory_mode: Memory configuration mode (default: "STM_ONLY")
74
+ - "NO_MEMORY": Disable memory entirely (stateless agent)
75
+ - "STM_ONLY": Short-term memory only (default)
76
+ - "STM_AND_LTM": Short-term + long-term memory with strategy extraction
72
77
  non_interactive: Skip interactive prompts and use defaults (default: True)
73
78
 
74
79
  Returns:
75
80
  ConfigureResult with configuration details
81
+
82
+ Example:
83
+ # Default: STM only (backward compatible)
84
+ runtime.configure(entrypoint='handler.py')
85
+
86
+ # Explicitly enable LTM
87
+ runtime.configure(entrypoint='handler.py', memory_mode='STM_AND_LTM')
88
+
89
+ # Disable memory entirely
90
+ runtime.configure(entrypoint='handler.py', memory_mode='NO_MEMORY')
91
+
92
+ # Invalid - raises error
93
+ runtime.configure(entrypoint='handler.py', disable_memory=True, memory_mode='STM_AND_LTM')
76
94
  """
77
95
  if protocol and protocol.upper() not in ["HTTP", "MCP", "A2A"]:
78
96
  raise ValueError("protocol must be either HTTP or MCP or A2A")
@@ -101,7 +119,7 @@ class Runtime:
101
119
  handler_dir = Path(file_path).parent
102
120
  req_file_path = handler_dir / "requirements.txt"
103
121
 
104
- all_requirements = [] # "bedrock_agentcore" # Always include bedrock_agentcore
122
+ all_requirements = []
105
123
  all_requirements.extend(requirements)
106
124
 
107
125
  req_file_path.write_text("\n".join(all_requirements))
@@ -109,6 +127,13 @@ class Runtime:
109
127
 
110
128
  final_requirements_file = str(req_file_path)
111
129
 
130
+ if memory_mode == "NO_MEMORY":
131
+ log.info("Memory disabled - agent will be stateless")
132
+ elif memory_mode == "STM_AND_LTM":
133
+ log.info("Memory configured with STM + LTM")
134
+ else: # STM_ONLY
135
+ log.info("Memory configured with STM only")
136
+
112
137
  # Configure using the operations module
113
138
  result = configure_bedrock_agentcore(
114
139
  agent_name=agent_name,
@@ -120,6 +145,7 @@ class Runtime:
120
145
  container_runtime=container_runtime,
121
146
  auto_create_ecr=auto_create_ecr,
122
147
  enable_observability=not disable_otel,
148
+ memory_mode=memory_mode,
123
149
  requirements_file=final_requirements_file,
124
150
  authorizer_configuration=authorizer_configuration,
125
151
  request_header_configuration=request_header_configuration,
@@ -451,7 +451,7 @@ class MemoryManager:
451
451
  logger.info("🔎 Retrieving memory resource with ID: %s...", memory_id)
452
452
  try:
453
453
  response = self._control_plane_client.get_memory(memoryId=memory_id).get("memory", {})
454
- logger.info(" Found memory: %s", memory_id)
454
+ logger.info(" Found memory: %s", memory_id)
455
455
  return Memory(response)
456
456
  except ClientError as e:
457
457
  logger.error(" ❌ Error retrieving memory: %s", e)
@@ -1,6 +1,13 @@
1
1
  """Bedrock AgentCore operations - shared business logic for CLI and notebook interfaces."""
2
2
 
3
- from .configure import configure_bedrock_agentcore, validate_agent_name
3
+ from .configure import (
4
+ configure_bedrock_agentcore,
5
+ detect_entrypoint,
6
+ detect_requirements,
7
+ get_relative_path,
8
+ infer_agent_name,
9
+ validate_agent_name,
10
+ )
4
11
  from .destroy import destroy_bedrock_agentcore
5
12
  from .invoke import invoke_bedrock_agentcore
6
13
  from .launch import launch_bedrock_agentcore
@@ -18,6 +25,10 @@ __all__ = [
18
25
  "configure_bedrock_agentcore",
19
26
  "destroy_bedrock_agentcore",
20
27
  "validate_agent_name",
28
+ "detect_entrypoint",
29
+ "detect_requirements",
30
+ "get_relative_path",
31
+ "infer_agent_name",
21
32
  "launch_bedrock_agentcore",
22
33
  "invoke_bedrock_agentcore",
23
34
  "get_status",