bedrock-agentcore-starter-toolkit 0.1.26__py3-none-any.whl → 0.1.28__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/cli.py +9 -1
  2. bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +263 -12
  3. bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +31 -7
  4. bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +240 -2
  5. bedrock_agentcore_starter_toolkit/operations/memory/manager.py +20 -33
  6. bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/base.py +2 -0
  7. bedrock_agentcore_starter_toolkit/operations/memory/models/strategies/self_managed.py +107 -0
  8. bedrock_agentcore_starter_toolkit/operations/runtime/__init__.py +4 -0
  9. bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +130 -11
  10. bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +0 -53
  11. bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +213 -16
  12. bedrock_agentcore_starter_toolkit/operations/runtime/models.py +19 -0
  13. bedrock_agentcore_starter_toolkit/operations/runtime/status.py +30 -0
  14. bedrock_agentcore_starter_toolkit/operations/runtime/stop_session.py +123 -0
  15. bedrock_agentcore_starter_toolkit/operations/runtime/vpc_validation.py +196 -0
  16. bedrock_agentcore_starter_toolkit/services/runtime.py +43 -1
  17. bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +44 -1
  18. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.28.dist-info}/METADATA +8 -8
  19. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.28.dist-info}/RECORD +23 -20
  20. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.28.dist-info}/WHEEL +0 -0
  21. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.28.dist-info}/entry_points.txt +0 -0
  22. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.28.dist-info}/licenses/LICENSE.txt +0 -0
  23. {bedrock_agentcore_starter_toolkit-0.1.26.dist-info → bedrock_agentcore_starter_toolkit-0.1.28.dist-info}/licenses/NOTICE.txt +0 -0
@@ -22,6 +22,10 @@ class ConfigureResult(BaseModel):
22
22
  ecr_repository: Optional[str] = Field(None, description="ECR repository URI")
23
23
  auto_create_ecr: bool = Field(False, description="Whether ECR will be auto-created")
24
24
  memory_id: Optional[str] = Field(default=None, description="Memory resource ID if created")
25
+ network_mode: Optional[str] = Field(None, description="Network mode (PUBLIC or VPC)")
26
+ network_subnets: Optional[List[str]] = Field(None, description="VPC subnet IDs")
27
+ network_security_groups: Optional[List[str]] = Field(None, description="VPC security group IDs")
28
+ network_vpc_id: Optional[str] = Field(None, description="VPC ID")
25
29
 
26
30
 
27
31
  # Launch operation models
@@ -70,12 +74,18 @@ class StatusConfigInfo(BaseModel):
70
74
  ecr_repository: Optional[str] = Field(None, description="ECR repository URI")
71
75
  agent_id: Optional[str] = Field(None, description="BedrockAgentCore agent ID")
72
76
  agent_arn: Optional[str] = Field(None, description="BedrockAgentCore agent ARN")
77
+ network_mode: Optional[str] = None
78
+ network_subnets: Optional[List[str]] = None
79
+ network_security_groups: Optional[List[str]] = None
80
+ network_vpc_id: Optional[str] = None
73
81
  memory_id: Optional[str] = Field(None, description="Memory resource ID")
74
82
  memory_status: Optional[str] = Field(None, description="Memory provisioning status (CREATING/ACTIVE/FAILED)")
75
83
  memory_type: Optional[str] = Field(None, description="Memory type (STM or STM+LTM)")
76
84
  memory_enabled: Optional[bool] = Field(None, description="Whether memory is enabled")
77
85
  memory_strategies: Optional[List[str]] = Field(None, description="Active memory strategies")
78
86
  memory_details: Optional[Dict[str, Any]] = Field(None, description="Detailed memory resource information")
87
+ idle_timeout: Optional[int] = Field(None, description="Idle runtime session timeout in seconds")
88
+ max_lifetime: Optional[int] = Field(None, description="Maximum instance lifetime in seconds")
79
89
 
80
90
 
81
91
  class StatusResult(BaseModel):
@@ -94,3 +104,12 @@ class DestroyResult(BaseModel):
94
104
  warnings: List[str] = Field(default_factory=list, description="List of warnings during destruction")
95
105
  errors: List[str] = Field(default_factory=list, description="List of errors during destruction")
96
106
  dry_run: bool = Field(default=False, description="Whether this was a dry run")
107
+
108
+
109
+ class StopSessionResult(BaseModel):
110
+ """Result of stop session operation."""
111
+
112
+ session_id: str = Field(..., description="Session ID that was stopped")
113
+ agent_name: str = Field(..., description="Name of the agent")
114
+ status_code: int = Field(..., description="HTTP status code of the operation")
115
+ message: str = Field(default="Session stopped successfully", description="Result message")
@@ -26,6 +26,24 @@ def get_status(config_path: Path, agent_name: Optional[str] = None) -> StatusRes
26
26
  project_config = load_config(config_path)
27
27
  agent_config = project_config.get_agent_config(agent_name)
28
28
 
29
+ # ADD NETWORK CONFIGURATION EXTRACTION
30
+ network_mode = agent_config.aws.network_configuration.network_mode
31
+ vpc_id = None
32
+
33
+ if network_mode == "VPC" and agent_config.aws.network_configuration.network_mode_config:
34
+ network_config = agent_config.aws.network_configuration.network_mode_config
35
+
36
+ # Try to get VPC ID from subnets (best effort - don't fail if can't retrieve)
37
+ try:
38
+ import boto3
39
+
40
+ ec2_client = boto3.client("ec2", region_name=agent_config.aws.region)
41
+ subnet_response = ec2_client.describe_subnets(SubnetIds=network_config.subnets[:1])
42
+ if subnet_response["Subnets"]:
43
+ vpc_id = subnet_response["Subnets"][0]["VpcId"]
44
+ except Exception:
45
+ pass # nosec B110 # Ignore errors - VPC ID is nice-to-have
46
+
29
47
  # Build config info
30
48
  config_info = StatusConfigInfo(
31
49
  name=agent_config.name,
@@ -36,8 +54,20 @@ def get_status(config_path: Path, agent_name: Optional[str] = None) -> StatusRes
36
54
  ecr_repository=agent_config.aws.ecr_repository,
37
55
  agent_id=agent_config.bedrock_agentcore.agent_id,
38
56
  agent_arn=agent_config.bedrock_agentcore.agent_arn,
57
+ network_mode=agent_config.aws.network_configuration.network_mode,
58
+ network_subnets=agent_config.aws.network_configuration.network_mode_config.subnets
59
+ if agent_config.aws.network_configuration.network_mode_config
60
+ else None,
61
+ network_security_groups=agent_config.aws.network_configuration.network_mode_config.security_groups
62
+ if agent_config.aws.network_configuration.network_mode_config
63
+ else None,
64
+ network_vpc_id=vpc_id,
39
65
  )
40
66
 
67
+ if agent_config.aws.lifecycle_configuration.has_custom_settings:
68
+ config_info.idle_timeout = agent_config.aws.lifecycle_configuration.idle_runtime_session_timeout
69
+ config_info.max_lifetime = agent_config.aws.lifecycle_configuration.max_lifetime
70
+
41
71
  # Check if memory is disabled first
42
72
  if agent_config.memory and agent_config.memory.mode == "NO_MEMORY":
43
73
  config_info.memory_type = "Disabled"
@@ -0,0 +1,123 @@
1
+ """Stop session operation - terminates active runtime sessions."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ from botocore.exceptions import ClientError
8
+
9
+ from ...services.runtime import BedrockAgentCoreClient
10
+ from ...utils.runtime.config import load_config, save_config
11
+ from ...utils.runtime.schema import BedrockAgentCoreAgentSchema, BedrockAgentCoreConfigSchema
12
+ from .models import StopSessionResult
13
+
14
+ log = logging.getLogger(__name__)
15
+
16
+
17
+ def stop_runtime_session(
18
+ config_path: Path,
19
+ session_id: Optional[str] = None,
20
+ agent_name: Optional[str] = None,
21
+ ) -> StopSessionResult:
22
+ """Stop an active runtime session.
23
+
24
+ Args:
25
+ config_path: Path to BedrockAgentCore configuration file
26
+ session_id: Session ID to stop (if None, uses tracked session from config)
27
+ agent_name: Name of agent (for project configurations)
28
+
29
+ Returns:
30
+ StopSessionResult with operation details
31
+
32
+ Raises:
33
+ ValueError: If no session ID provided or found, or agent not deployed
34
+ FileNotFoundError: If configuration file doesn't exist
35
+ """
36
+ # Load project configuration
37
+ project_config = load_config(config_path)
38
+ agent_config = project_config.get_agent_config(agent_name)
39
+
40
+ log.info("Stopping session for agent: %s", agent_config.name)
41
+
42
+ # Check if agent is deployed
43
+ if not agent_config.bedrock_agentcore.agent_arn:
44
+ raise ValueError(
45
+ f"Agent '{agent_config.name}' is not deployed. Run 'agentcore launch' to deploy the agent first."
46
+ )
47
+
48
+ # Determine session ID to stop
49
+ target_session_id = session_id
50
+ if not target_session_id:
51
+ # Try to use tracked session from config
52
+ target_session_id = agent_config.bedrock_agentcore.agent_session_id
53
+ if not target_session_id:
54
+ raise ValueError(
55
+ "No active session found. Please provide --session-id or invoke the agent first to create a session."
56
+ )
57
+ log.info("Using tracked session ID from config: %s", target_session_id)
58
+ else:
59
+ log.info("Using provided session ID: %s", target_session_id)
60
+
61
+ region = agent_config.aws.region
62
+ agent_arn = agent_config.bedrock_agentcore.agent_arn
63
+
64
+ # Stop the session
65
+ client = BedrockAgentCoreClient(region)
66
+
67
+ try:
68
+ response = client.stop_runtime_session(
69
+ agent_arn=agent_arn,
70
+ session_id=target_session_id,
71
+ )
72
+
73
+ status_code = response.get("statusCode", 200)
74
+
75
+ # Success case
76
+ log.info("Session stopped successfully: %s", target_session_id)
77
+
78
+ # Clear the session ID from config if it matches
79
+ if agent_config.bedrock_agentcore.agent_session_id == target_session_id:
80
+ _clear_session_from_config(agent_config, project_config, config_path)
81
+
82
+ return StopSessionResult(
83
+ session_id=target_session_id,
84
+ agent_name=agent_config.name,
85
+ status_code=status_code,
86
+ message="Session stopped successfully",
87
+ )
88
+
89
+ except ClientError as e:
90
+ # Case 2: Error propagated as ClientError (defense in depth)
91
+ error_code = e.response.get("Error", {}).get("Code", "")
92
+ error_message = e.response.get("Error", {}).get("Message", "")
93
+ status_code = e.response.get("ResponseMetadata", {}).get("HTTPStatusCode", 500)
94
+
95
+ if error_code in ["ResourceNotFoundException", "NotFound"]:
96
+ log.warning("Session not found (may have already been terminated): %s", target_session_id)
97
+
98
+ # Still clear from config if it matches
99
+ if agent_config.bedrock_agentcore.agent_session_id == target_session_id:
100
+ _clear_session_from_config(agent_config, project_config, config_path)
101
+
102
+ return StopSessionResult(
103
+ session_id=target_session_id,
104
+ agent_name=agent_config.name,
105
+ status_code=404,
106
+ message="Session not found (may have already been terminated)",
107
+ )
108
+ else:
109
+ # Re-raise other client errors
110
+ log.error("Failed to stop session %s: %s - %s", target_session_id, error_code, error_message)
111
+ raise
112
+
113
+
114
+ def _clear_session_from_config(
115
+ agent_config: BedrockAgentCoreAgentSchema,
116
+ project_config: BedrockAgentCoreConfigSchema,
117
+ config_path: Path,
118
+ ) -> None:
119
+ """Clear session ID from agent configuration."""
120
+ agent_config.bedrock_agentcore.agent_session_id = None
121
+ project_config.agents[agent_config.name] = agent_config
122
+ save_config(project_config, config_path)
123
+ log.info("Cleared session ID from configuration")
@@ -0,0 +1,196 @@
1
+ """VPC networking validation utilities for AgentCore Runtime."""
2
+
3
+ import logging
4
+ from typing import List, Optional, Tuple
5
+
6
+ import boto3
7
+ from botocore.exceptions import ClientError
8
+
9
+ log = logging.getLogger(__name__)
10
+
11
+
12
+ def validate_vpc_configuration(
13
+ region: str,
14
+ subnets: List[str],
15
+ security_groups: List[str],
16
+ session: Optional[boto3.Session] = None,
17
+ ) -> Tuple[str, List[str]]:
18
+ """Validate VPC configuration and return VPC ID and any warnings.
19
+
20
+ Args:
21
+ region: AWS region
22
+ subnets: List of subnet IDs
23
+ security_groups: List of security group IDs
24
+ session: Optional boto3 session (creates new if not provided)
25
+
26
+ Returns:
27
+ Tuple of (vpc_id, warnings_list)
28
+
29
+ Raises:
30
+ ValueError: If validation fails
31
+ """
32
+ if not session:
33
+ session = boto3.Session(region_name=region)
34
+
35
+ ec2_client = session.client("ec2", region_name=region)
36
+ warnings = []
37
+
38
+ # Validate subnets
39
+ vpc_id = _validate_subnets(ec2_client, subnets, warnings)
40
+
41
+ # Validate security groups
42
+ _validate_security_groups(ec2_client, security_groups, vpc_id, warnings)
43
+
44
+ return vpc_id, warnings
45
+
46
+
47
+ def _validate_subnets(ec2_client, subnets: List[str], warnings: List[str]) -> str:
48
+ """Validate subnets and return VPC ID."""
49
+ try:
50
+ response = ec2_client.describe_subnets(SubnetIds=subnets)
51
+
52
+ if len(response["Subnets"]) != len(subnets):
53
+ found_ids = {s["SubnetId"] for s in response["Subnets"]}
54
+ missing = set(subnets) - found_ids
55
+ raise ValueError(f"Subnet IDs not found: {missing}")
56
+
57
+ # Check all subnets are in same VPC
58
+ vpc_ids = {subnet["VpcId"] for subnet in response["Subnets"]}
59
+
60
+ if len(vpc_ids) > 1:
61
+ raise ValueError(
62
+ f"All subnets must be in the same VPC. Found subnets in {len(vpc_ids)} different VPCs: {vpc_ids}"
63
+ )
64
+
65
+ vpc_id = vpc_ids.pop()
66
+ log.info("✓ Validated %d subnets in VPC %s", len(subnets), vpc_id)
67
+
68
+ # Check subnet availability zones
69
+ azs = {subnet["AvailabilityZone"] for subnet in response["Subnets"]}
70
+ if len(azs) < 2:
71
+ warnings.append(
72
+ f"Subnets are in only {len(azs)} availability zone(s). "
73
+ "For high availability, use subnets in multiple AZs."
74
+ )
75
+
76
+ return vpc_id
77
+
78
+ except ClientError as e:
79
+ error_code = e.response["Error"]["Code"]
80
+ if error_code == "InvalidSubnetID.NotFound":
81
+ raise ValueError(f"One or more subnet IDs not found: {subnets}") from e
82
+ raise ValueError(f"Failed to validate subnets: {e}") from e
83
+
84
+
85
+ def _validate_security_groups(
86
+ ec2_client, security_groups: List[str], expected_vpc_id: str, warnings: List[str]
87
+ ) -> None:
88
+ """Validate security groups are in the expected VPC."""
89
+ try:
90
+ response = ec2_client.describe_security_groups(GroupIds=security_groups)
91
+
92
+ if len(response["SecurityGroups"]) != len(security_groups):
93
+ found_ids = {sg["GroupId"] for sg in response["SecurityGroups"]}
94
+ missing = set(security_groups) - found_ids
95
+ raise ValueError(f"Security group IDs not found: {missing}")
96
+
97
+ # Check all SGs are in same VPC
98
+ sg_vpcs = {sg["VpcId"] for sg in response["SecurityGroups"]}
99
+
100
+ if len(sg_vpcs) > 1:
101
+ raise ValueError(
102
+ f"All security groups must be in the same VPC. "
103
+ f"Found security groups in {len(sg_vpcs)} different VPCs: {sg_vpcs}"
104
+ )
105
+
106
+ sg_vpc_id = sg_vpcs.pop()
107
+
108
+ # Check SGs are in same VPC as subnets
109
+ if sg_vpc_id != expected_vpc_id:
110
+ raise ValueError(
111
+ f"Security groups must be in the same VPC as subnets. "
112
+ f"Subnets are in VPC {expected_vpc_id}, "
113
+ f"but security groups are in VPC {sg_vpc_id}"
114
+ )
115
+
116
+ log.info("✓ Validated %d security groups in VPC %s", len(security_groups), sg_vpc_id)
117
+
118
+ except ClientError as e:
119
+ error_code = e.response["Error"]["Code"]
120
+ if error_code == "InvalidGroup.NotFound":
121
+ raise ValueError(f"One or more security group IDs not found: {security_groups}") from e
122
+ raise ValueError(f"Failed to validate security groups: {e}") from e
123
+
124
+
125
+ def check_network_immutability(
126
+ existing_network_mode: str,
127
+ existing_subnets: Optional[List[str]],
128
+ existing_security_groups: Optional[List[str]],
129
+ new_network_mode: str,
130
+ new_subnets: Optional[List[str]],
131
+ new_security_groups: Optional[List[str]],
132
+ ) -> Optional[str]:
133
+ """Check if network configuration is being changed (not allowed).
134
+
135
+ Returns:
136
+ Error message if change detected, None if no change
137
+ """
138
+ # Check mode change
139
+ if existing_network_mode != new_network_mode:
140
+ return (
141
+ f"Cannot change network mode from {existing_network_mode} to {new_network_mode}. "
142
+ f"Network configuration is immutable after agent creation. "
143
+ f"Create a new agent for different network settings."
144
+ )
145
+
146
+ # If both PUBLIC, no further checks needed
147
+ if existing_network_mode == "PUBLIC":
148
+ return None
149
+
150
+ # Check VPC resource changes
151
+ if set(existing_subnets or []) != set(new_subnets or []):
152
+ return (
153
+ "Cannot change VPC subnets after agent creation. "
154
+ "Network configuration is immutable. "
155
+ "Create a new agent for different network settings."
156
+ )
157
+
158
+ if set(existing_security_groups or []) != set(new_security_groups or []):
159
+ return (
160
+ "Cannot change VPC security groups after agent creation. "
161
+ "Network configuration is immutable. "
162
+ "Create a new agent for different network settings."
163
+ )
164
+
165
+ return None
166
+
167
+
168
+ def verify_subnet_azs(ec2_client, subnets: List[str], region: str) -> List[str]:
169
+ """Verify subnets are in supported AZs and return any issues."""
170
+ # Supported AZ IDs for us-west-2
171
+ SUPPORTED_AZS = {
172
+ "us-west-2": ["usw2-az1", "usw2-az2", "usw2-az3"],
173
+ "us-east-1": ["use1-az1", "use1-az2", "use1-az4"],
174
+ # Add other regions as needed
175
+ }
176
+
177
+ supported = SUPPORTED_AZS.get(region, [])
178
+
179
+ response = ec2_client.describe_subnets(SubnetIds=subnets)
180
+ issues = []
181
+
182
+ for subnet in response["Subnets"]:
183
+ subnet_id = subnet["SubnetId"]
184
+ az_id = subnet["AvailabilityZoneId"]
185
+ az_name = subnet["AvailabilityZone"]
186
+
187
+ if supported and az_id not in supported:
188
+ issues.append(
189
+ f"Subnet {subnet_id} is in AZ {az_name} (ID: {az_id}) "
190
+ f"which is NOT supported by AgentCore in {region}. "
191
+ f"Supported AZ IDs: {supported}"
192
+ )
193
+ else:
194
+ log.info("✓ Subnet %s is in supported AZ: %s (%s)", subnet_id, az_name, az_id)
195
+
196
+ return issues
@@ -128,6 +128,7 @@ class BedrockAgentCoreClient:
128
128
  protocol_config: Optional[Dict] = None,
129
129
  env_vars: Optional[Dict] = None,
130
130
  auto_update_on_conflict: bool = False,
131
+ lifecycle_config: Optional[Dict] = None,
131
132
  ) -> Dict[str, str]:
132
133
  """Create new agent."""
133
134
  self.logger.info("Creating agent '%s' with image URI: %s", agent_name, image_uri)
@@ -154,17 +155,20 @@ class BedrockAgentCoreClient:
154
155
  if env_vars is not None:
155
156
  params["environmentVariables"] = env_vars
156
157
 
158
+ if lifecycle_config is not None:
159
+ params["lifecycleConfiguration"] = lifecycle_config
160
+
157
161
  resp = self.client.create_agent_runtime(**params)
158
162
  agent_id = resp["agentRuntimeId"]
159
163
  agent_arn = resp["agentRuntimeArn"]
160
164
  self.logger.info("Successfully created agent '%s' with ID: %s, ARN: %s", agent_name, agent_id, agent_arn)
161
165
  return {"id": agent_id, "arn": agent_arn}
166
+
162
167
  except ClientError as e:
163
168
  error_code = e.response.get("Error", {}).get("Code")
164
169
  if error_code == "ConflictException":
165
170
  if not auto_update_on_conflict:
166
171
  self.logger.error("Agent '%s' already exists and auto_update_on_conflict is disabled", agent_name)
167
- # Create a more helpful error message
168
172
  raise ClientError(
169
173
  {
170
174
  "Error": {
@@ -226,6 +230,7 @@ class BedrockAgentCoreClient:
226
230
  request_header_config: Optional[Dict] = None,
227
231
  protocol_config: Optional[Dict] = None,
228
232
  env_vars: Optional[Dict] = None,
233
+ lifecycle_config: Optional[Dict] = None,
229
234
  ) -> Dict[str, str]:
230
235
  """Update existing agent."""
231
236
  self.logger.info("Updating agent ID '%s' with image URI: %s", agent_id, image_uri)
@@ -252,6 +257,9 @@ class BedrockAgentCoreClient:
252
257
  if env_vars is not None:
253
258
  params["environmentVariables"] = env_vars
254
259
 
260
+ if lifecycle_config is not None:
261
+ params["lifecycleConfiguration"] = lifecycle_config
262
+
255
263
  resp = self.client.update_agent_runtime(**params)
256
264
  agent_arn = resp["agentRuntimeArn"]
257
265
  self.logger.info("Successfully updated agent ID '%s', ARN: %s", agent_id, agent_arn)
@@ -312,6 +320,7 @@ class BedrockAgentCoreClient:
312
320
  protocol_config: Optional[Dict] = None,
313
321
  env_vars: Optional[Dict] = None,
314
322
  auto_update_on_conflict: bool = False,
323
+ lifecycle_config: Optional[Dict] = None,
315
324
  ) -> Dict[str, str]:
316
325
  """Create or update agent."""
317
326
  if agent_id:
@@ -324,6 +333,7 @@ class BedrockAgentCoreClient:
324
333
  request_header_config,
325
334
  protocol_config,
326
335
  env_vars,
336
+ lifecycle_config,
327
337
  )
328
338
  return self.create_agent(
329
339
  agent_name,
@@ -335,6 +345,7 @@ class BedrockAgentCoreClient:
335
345
  protocol_config,
336
346
  env_vars,
337
347
  auto_update_on_conflict,
348
+ lifecycle_config,
338
349
  )
339
350
 
340
351
  def wait_for_agent_endpoint_ready(self, agent_id: str, endpoint_name: str = "DEFAULT", max_wait: int = 120) -> str:
@@ -483,6 +494,37 @@ class BedrockAgentCoreClient:
483
494
  "before-sign.bedrock-agentcore.InvokeAgentRuntime", handler_id
484
495
  )
485
496
 
497
+ def stop_runtime_session(
498
+ self,
499
+ agent_arn: str,
500
+ session_id: str,
501
+ endpoint_name: str = "DEFAULT",
502
+ ) -> Dict:
503
+ """Stop a runtime session.
504
+
505
+ Args:
506
+ agent_arn: Agent ARN
507
+ session_id: Session ID to stop
508
+ endpoint_name: Endpoint name, defaults to "DEFAULT"
509
+
510
+ Returns:
511
+ Response with status code
512
+
513
+ Raises:
514
+ ClientError: If the operation fails, including ResourceNotFoundException
515
+ if the session doesn't exist
516
+ """
517
+ self.logger.info("Stopping runtime session: %s", session_id)
518
+
519
+ response = self.dataplane_client.stop_runtime_session(
520
+ agentRuntimeArn=agent_arn,
521
+ qualifier=endpoint_name,
522
+ runtimeSessionId=session_id,
523
+ )
524
+
525
+ self.logger.info("Successfully stopped session: %s", session_id)
526
+ return response
527
+
486
528
 
487
529
  class HttpBedrockAgentCoreClient:
488
530
  """Bedrock AgentCore client for agent management using HTTP requests with bearer token."""
@@ -16,7 +16,7 @@ class MemoryConfig(BaseModel):
16
16
  """Memory configuration for BedrockAgentCore."""
17
17
 
18
18
  mode: Literal["STM_ONLY", "STM_AND_LTM", "NO_MEMORY"] = Field(
19
- default="STM_ONLY", description="Memory mode - always has STM, optionally adds LTM"
19
+ default="NO_MEMORY", description="Memory mode - opt-in feature"
20
20
  )
21
21
  memory_id: Optional[str] = Field(default=None, description="Memory resource ID")
22
22
  memory_arn: Optional[str] = Field(default=None, description="Memory resource ARN")
@@ -99,6 +99,48 @@ class ProtocolConfiguration(BaseModel):
99
99
  return {"serverProtocol": self.server_protocol}
100
100
 
101
101
 
102
+ class LifecycleConfiguration(BaseModel):
103
+ """Lifecycle configuration for runtime sessions."""
104
+
105
+ idle_runtime_session_timeout: Optional[int] = Field(
106
+ default=None,
107
+ description="Timeout in seconds for idle runtime sessions (60-28800)",
108
+ ge=60,
109
+ le=28800,
110
+ )
111
+ max_lifetime: Optional[int] = Field(
112
+ default=None, description="Maximum lifetime for the instance in seconds (60-28800)", ge=60, le=28800
113
+ )
114
+
115
+ @field_validator("max_lifetime")
116
+ @classmethod
117
+ def validate_lifecycle_relationship(cls, v: Optional[int], info) -> Optional[int]:
118
+ """Validate that max_lifetime >= idle_timeout if both are set."""
119
+ if v is None:
120
+ return v
121
+
122
+ idle = info.data.get("idle_runtime_session_timeout")
123
+ if idle is not None and v < idle:
124
+ raise ValueError(
125
+ f"max_lifetime ({v}s) must be greater than or equal to idle_runtime_session_timeout ({idle}s)"
126
+ )
127
+ return v
128
+
129
+ def to_aws_dict(self) -> dict:
130
+ """Convert to AWS API format with camelCase keys."""
131
+ result = {}
132
+ if self.idle_runtime_session_timeout is not None:
133
+ result["idleRuntimeSessionTimeout"] = self.idle_runtime_session_timeout
134
+ if self.max_lifetime is not None:
135
+ result["maxLifetime"] = self.max_lifetime
136
+ return result
137
+
138
+ @property
139
+ def has_custom_settings(self) -> bool:
140
+ """Check if any custom lifecycle settings are configured."""
141
+ return self.idle_runtime_session_timeout is not None or self.max_lifetime is not None
142
+
143
+
102
144
  class ObservabilityConfig(BaseModel):
103
145
  """Observability configuration."""
104
146
 
@@ -117,6 +159,7 @@ class AWSConfig(BaseModel):
117
159
  network_configuration: NetworkConfiguration = Field(default_factory=NetworkConfiguration)
118
160
  protocol_configuration: ProtocolConfiguration = Field(default_factory=ProtocolConfiguration)
119
161
  observability: ObservabilityConfig = Field(default_factory=ObservabilityConfig)
162
+ lifecycle_configuration: LifecycleConfiguration = Field(default_factory=LifecycleConfiguration)
120
163
 
121
164
  @field_validator("account")
122
165
  @classmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bedrock-agentcore-starter-toolkit
3
- Version: 0.1.26
3
+ Version: 0.1.28
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
@@ -83,38 +83,38 @@ Amazon Bedrock AgentCore includes the following modular Services that you can us
83
83
  ## 🚀 Amazon Bedrock AgentCore Runtime
84
84
  AgentCore Runtime is a secure, serverless runtime purpose-built for deploying and scaling dynamic AI agents and tools using any open-source framework including LangGraph, CrewAI, and Strands Agents, any protocol, and any model. Runtime was built to work for agentic workloads with industry-leading extended runtime support, fast cold starts, true session isolation, built-in identity, and support for multi-modal payloads. Developers can focus on innovation while Amazon Bedrock AgentCore Runtime handles infrastructure and security -- accelerating time-to-market
85
85
 
86
- **[Runtime Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/runtime/quickstart.html)**
86
+ **[Runtime Quick Start](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-get-started-toolkit.html)**
87
87
 
88
88
  ## 🧠 Amazon Bedrock AgentCore Memory
89
89
  AgentCore Memory makes it easy for developers to build context aware agents by eliminating complex memory infrastructure management while providing full control over what the AI agent remembers. Memory provides industry-leading accuracy along with support for both short-term memory for multi-turn conversations and long-term memory that can be shared across agents and sessions.
90
90
 
91
91
 
92
- **[Memory Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/memory/quickstart.html)**
92
+ **[Memory Quick Start](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory-get-started.html)**
93
93
 
94
94
  ## 🔗 Amazon Bedrock AgentCore Gateway
95
95
  Amazon Bedrock AgentCore Gateway acts as a managed Model Context Protocol (MCP) server that converts APIs and Lambda functions into MCP tools that agents can use. Gateway manages the complexity of OAuth ingress authorization and secure egress credential exchange, making standing up remote MCP servers easier and more secure. Gateway also offers composition and built-in semantic search over tools, enabling developers to scale their agents to use hundreds or thousands of tools.
96
96
 
97
- **[Gateway Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/gateway/quickstart.html)**
97
+ **[Gateway Quick Start](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-quick-start.html)**
98
98
 
99
99
  ## 💻 Amazon Bedrock AgentCore Code Interpreter
100
100
  AgentCore Code Interpreter tool enables agents to securely execute code in isolated sandbox environments. It offers advanced configuration support and seamless integration with popular frameworks. Developers can build powerful agents for complex workflows and data analysis while meeting enterprise security requirements.
101
101
 
102
- **[Code Interpreter Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/builtin-tools/quickstart-code-interpreter.html)**
102
+ **[Code Interpreter Quick Start](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/code-interpreter-getting-started.html)**
103
103
 
104
104
  ## 🌐 Amazon Bedrock AgentCore Browser
105
105
  AgentCore Browser tool provides a fast, secure, cloud-based browser runtime to enable AI agents to interact with websites at scale. It provides enterprise-grade security, comprehensive observability features, and automatically scales— all without infrastructure management overhead.
106
106
 
107
- **[Browser Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/builtin-tools/quickstart-browser.html)**
107
+ **[Browser Quick Start](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/browser-onboarding.html)**
108
108
 
109
109
  ## 📊 Amazon Bedrock AgentCore Observability
110
110
  AgentCore Observability helps developers trace, debug, and monitor agent performance in production through unified operational dashboards. With support for OpenTelemetry compatible telemetry and detailed visualizations of each step of the agent workflow, AgentCore enables developers to easily gain visibility into agent behavior and maintain quality standards at scale.
111
111
 
112
- **[Observability Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/observability/quickstart.html)**
112
+ **[Observability Quick Start](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-get-started.html)**
113
113
 
114
114
  ## 🔐 Amazon Bedrock AgentCore Identity
115
115
  AgentCore Identity provides a secure, scalable agent identity and access management capability accelerating AI agent development. It is compatible with existing identity providers, eliminating needs for user migration or rebuilding authentication flows. AgentCore Identity's helps to minimize consent fatigue with a secure token vault and allows you to build streamlined AI agent experiences. Just-enough access and secure permission delegation allow agents to securely access AWS resources and third-party tools and services.
116
116
 
117
- **[Identity Quick Start](https://aws.github.io/bedrock-agentcore-starter-toolkit/user-guide/identity/quickstart.html)**
117
+ **[Identity Quick Start](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/identity-getting-started-cognito.html)**
118
118
 
119
119
  ## 🔐 Import Amazon Bedrock Agents to Bedrock AgentCore
120
120
  AgentCore Import-Agent enables seamless migration of existing Amazon Bedrock Agents to LangChain/LangGraph or Strands frameworks while automatically integrating AgentCore primitives like Memory, Code Interpreter, and Gateway. Developers can migrate agents in minutes with full feature parity and deploy directly to AgentCore Runtime for serverless operation.