@aws/agentcore 1.0.0-preview.10 → 1.0.0-preview.12

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.
Files changed (65) hide show
  1. package/dist/agent-inspector/index.css +1 -1
  2. package/dist/agent-inspector/index.js +83 -78
  3. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +452 -146
  4. package/dist/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap +2 -2
  5. package/dist/assets/cdk/bin/cdk.ts +4 -0
  6. package/dist/assets/cdk/lib/cdk-stack.ts +2 -0
  7. package/dist/assets/cdk/package.json +1 -1
  8. package/dist/assets/container/python/Dockerfile +1 -1
  9. package/dist/assets/python/a2a/googleadk/base/main.py +24 -16
  10. package/dist/assets/python/a2a/googleadk/base/pyproject.toml +1 -1
  11. package/dist/assets/python/a2a/langchain_langgraph/base/main.py +24 -16
  12. package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +1 -0
  13. package/dist/assets/python/a2a/strands/base/main.py +23 -17
  14. package/dist/assets/python/agui/googleadk/base/main.py +62 -2
  15. package/dist/assets/python/agui/googleadk/base/pyproject.toml +1 -1
  16. package/dist/assets/python/agui/langchain_langgraph/base/main.py +56 -0
  17. package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +1 -0
  18. package/dist/assets/python/agui/strands/base/main.py +63 -1
  19. package/dist/assets/python/http/autogen/base/main.py +27 -19
  20. package/dist/assets/python/http/googleadk/base/main.py +24 -16
  21. package/dist/assets/python/http/googleadk/base/pyproject.toml +1 -0
  22. package/dist/assets/python/http/langchain_langgraph/base/main.py +24 -16
  23. package/dist/assets/python/http/langchain_langgraph/base/pyproject.toml +1 -0
  24. package/dist/assets/python/http/openaiagents/base/main.py +24 -16
  25. package/dist/assets/python/http/strands/base/main.py +23 -17
  26. package/dist/assets/python/mcp/standalone/base/main.py +58 -0
  27. package/dist/cli/index.mjs +707 -634
  28. package/dist/lib/errors/types.d.ts +9 -0
  29. package/dist/lib/errors/types.d.ts.map +1 -1
  30. package/dist/lib/errors/types.js +19 -1
  31. package/dist/lib/errors/types.js.map +1 -1
  32. package/dist/lib/errors/zod.js +1 -1
  33. package/dist/lib/errors/zod.js.map +1 -1
  34. package/dist/lib/packaging/helpers.d.ts.map +1 -1
  35. package/dist/lib/packaging/helpers.js +6 -10
  36. package/dist/lib/packaging/helpers.js.map +1 -1
  37. package/dist/lib/result.d.ts +21 -4
  38. package/dist/lib/result.d.ts.map +1 -1
  39. package/dist/lib/result.js +19 -0
  40. package/dist/lib/result.js.map +1 -1
  41. package/dist/lib/schemas/io/global-config.d.ts +28 -21
  42. package/dist/lib/schemas/io/global-config.d.ts.map +1 -1
  43. package/dist/lib/schemas/io/global-config.js +57 -28
  44. package/dist/lib/schemas/io/global-config.js.map +1 -1
  45. package/dist/lib/utils/index.d.ts +1 -1
  46. package/dist/lib/utils/index.d.ts.map +1 -1
  47. package/dist/lib/utils/index.js +2 -1
  48. package/dist/lib/utils/index.js.map +1 -1
  49. package/dist/lib/utils/zod.d.ts +16 -0
  50. package/dist/lib/utils/zod.d.ts.map +1 -1
  51. package/dist/lib/utils/zod.js +45 -0
  52. package/dist/lib/utils/zod.js.map +1 -1
  53. package/dist/schema/schemas/agent-env.d.ts +50 -4
  54. package/dist/schema/schemas/agent-env.d.ts.map +1 -1
  55. package/dist/schema/schemas/agent-env.js +102 -4
  56. package/dist/schema/schemas/agent-env.js.map +1 -1
  57. package/dist/schema/schemas/agentcore-project.d.ts +12 -2
  58. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  59. package/dist/schema/schemas/primitives/harness.d.ts +8 -0
  60. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
  61. package/dist/schema/schemas/primitives/harness.js +23 -0
  62. package/dist/schema/schemas/primitives/harness.js.map +1 -1
  63. package/npm-shrinkwrap.json +281 -366
  64. package/package.json +12 -7
  65. package/scripts/bump-version.ts +1 -1
@@ -111,6 +111,8 @@ async function main() {
111
111
  codeLocation?: string;
112
112
  tools?: { type: string; name: string }[];
113
113
  apiKeyArn?: string;
114
+ efsAccessPoints?: { accessPointArn: string; mountPath: string }[];
115
+ s3AccessPoints?: { accessPointArn: string; mountPath: string }[];
114
116
  }[] = [];
115
117
  for (const entry of specAny.harnesses ?? []) {
116
118
  const harnessDir = path.resolve(projectRoot, entry.path);
@@ -127,6 +129,8 @@ async function main() {
127
129
  codeLocation: harnessSpec.dockerfile ? harnessDir : undefined,
128
130
  tools: harnessSpec.tools,
129
131
  apiKeyArn: harnessSpec.model?.apiKeyArn,
132
+ efsAccessPoints: harnessSpec.efsAccessPoints,
133
+ s3AccessPoints: harnessSpec.s3AccessPoints,
130
134
  });
131
135
  } catch (err) {
132
136
  throw new Error(
@@ -312,6 +316,8 @@ export interface HarnessConfig {
312
316
  codeLocation?: string;
313
317
  tools?: { type: string; name: string }[];
314
318
  apiKeyArn?: string;
319
+ efsAccessPoints?: { accessPointArn: string; mountPath: string }[];
320
+ s3AccessPoints?: { accessPointArn: string; mountPath: string }[];
315
321
  }
316
322
 
317
323
  export interface AgentCoreStackProps extends StackProps {
@@ -408,7 +414,7 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/package.json should m
408
414
  "@types/node": "^24.10.1",
409
415
  "jest": "^29.7.0",
410
416
  "ts-jest": "^29.2.5",
411
- "aws-cdk": "2.1100.1",
417
+ "aws-cdk": "2.1126.0",
412
418
  "prettier": "^3.4.2",
413
419
  "typescript": "~5.9.3"
414
420
  },
@@ -1063,7 +1069,9 @@ Thumbs.db
1063
1069
  `;
1064
1070
 
1065
1071
  exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/main.py should match snapshot 1`] = `
1066
- "import os
1072
+ "{{#if needsOs}}
1073
+ import os
1074
+ {{/if}}
1067
1075
  from google.adk.agents import Agent
1068
1076
  from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
1069
1077
  from google.adk.runners import Runner
@@ -1082,18 +1090,21 @@ def add_numbers(a: int, b: int) -> int:
1082
1090
 
1083
1091
  tools = [add_numbers]
1084
1092
 
1085
- {{#if sessionStorageMountPath}}
1086
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1093
+ {{#if needsOs}}
1094
+ _MOUNT_PATHS = [
1095
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
1096
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
1097
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
1098
+ ]
1087
1099
 
1088
1100
  def _safe_resolve(path: str) -> str:
1089
- """Resolve path safely within the storage boundary."""
1090
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1091
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1092
- raise ValueError(f"Path '{path}' is outside the storage boundary")
1101
+ resolved = os.path.realpath(path)
1102
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
1103
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
1093
1104
  return resolved
1094
1105
 
1095
1106
  def file_read(path: str) -> str:
1096
- """Read a file from persistent storage. The path is relative to the storage root."""
1107
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
1097
1108
  try:
1098
1109
  full_path = _safe_resolve(path)
1099
1110
  with open(full_path) as f:
@@ -1104,7 +1115,7 @@ def file_read(path: str) -> str:
1104
1115
  return f"Error reading '{path}': {e.strerror}"
1105
1116
 
1106
1117
  def file_write(path: str, content: str) -> str:
1107
- """Write content to a file in persistent storage. The path is relative to the storage root."""
1118
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
1108
1119
  try:
1109
1120
  full_path = _safe_resolve(path)
1110
1121
  parent = os.path.dirname(full_path)
@@ -1118,25 +1129,28 @@ def file_write(path: str, content: str) -> str:
1118
1129
  except OSError as e:
1119
1130
  return f"Error writing '{path}': {e.strerror}"
1120
1131
 
1121
- def list_files(directory: str = "") -> str:
1122
- """List files in persistent storage. The directory is relative to the storage root."""
1132
+ def list_files(path: str) -> str:
1133
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
1123
1134
  try:
1124
- target = _safe_resolve(directory)
1125
- entries = os.listdir(target)
1135
+ full_path = _safe_resolve(path)
1136
+ entries = os.listdir(full_path)
1126
1137
  return "\\n".join(entries) if entries else "(empty directory)"
1127
1138
  except ValueError as e:
1128
1139
  return str(e)
1129
1140
  except OSError as e:
1130
- return f"Error listing '{directory}': {e.strerror}"
1141
+ return f"Error listing '{path}': {e.strerror}"
1131
1142
 
1132
1143
  tools.extend([file_read, file_write, list_files])
1133
1144
  {{/if}}
1134
1145
 
1135
1146
  AGENT_INSTRUCTION = """
1136
1147
  You are a helpful assistant. Use tools when appropriate.
1137
- {{#if sessionStorageMountPath}}
1138
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1139
- {{/if}}
1148
+ {{#if needsOs}}
1149
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
1150
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
1151
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
1152
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
1153
+ {{/each}}{{/if}}
1140
1154
  """
1141
1155
 
1142
1156
  agent = Agent(
@@ -1242,7 +1256,7 @@ dependencies = [
1242
1256
  "aws-opentelemetry-distro",
1243
1257
  "bedrock-agentcore[a2a] >= 1.0.3",
1244
1258
  "google-adk >= 1.0.0, < 2.0.0",
1245
- "google-genai >= 1.0.0",
1259
+ "google-genai >= 1.0.0, < 2.0.0",
1246
1260
  ]
1247
1261
 
1248
1262
  [tool.hatch.build.targets.wheel]
@@ -1322,7 +1336,9 @@ Thumbs.db
1322
1336
  `;
1323
1337
 
1324
1338
  exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/main.py should match snapshot 1`] = `
1325
- "import os
1339
+ "{{#if needsOs}}
1340
+ import os
1341
+ {{/if}}
1326
1342
  from langchain_core.tools import tool
1327
1343
  from langgraph.prebuilt import create_react_agent
1328
1344
  from opentelemetry.instrumentation.langchain import LangchainInstrumentor
@@ -1345,19 +1361,22 @@ def add_numbers(a: int, b: int) -> int:
1345
1361
 
1346
1362
  tools = [add_numbers]
1347
1363
 
1348
- {{#if sessionStorageMountPath}}
1349
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1364
+ {{#if needsOs}}
1365
+ _MOUNT_PATHS = [
1366
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
1367
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
1368
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
1369
+ ]
1350
1370
 
1351
1371
  def _safe_resolve(path: str) -> str:
1352
- """Resolve path safely within the storage boundary."""
1353
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1354
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1355
- raise ValueError(f"Path '{path}' is outside the storage boundary")
1372
+ resolved = os.path.realpath(path)
1373
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
1374
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
1356
1375
  return resolved
1357
1376
 
1358
1377
  @tool
1359
1378
  def file_read(path: str) -> str:
1360
- """Read a file from persistent storage. The path is relative to the storage root."""
1379
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
1361
1380
  try:
1362
1381
  full_path = _safe_resolve(path)
1363
1382
  with open(full_path) as f:
@@ -1369,7 +1388,7 @@ def file_read(path: str) -> str:
1369
1388
 
1370
1389
  @tool
1371
1390
  def file_write(path: str, content: str) -> str:
1372
- """Write content to a file in persistent storage. The path is relative to the storage root."""
1391
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
1373
1392
  try:
1374
1393
  full_path = _safe_resolve(path)
1375
1394
  parent = os.path.dirname(full_path)
@@ -1384,25 +1403,28 @@ def file_write(path: str, content: str) -> str:
1384
1403
  return f"Error writing '{path}': {e.strerror}"
1385
1404
 
1386
1405
  @tool
1387
- def list_files(directory: str = "") -> str:
1388
- """List files in persistent storage. The directory is relative to the storage root."""
1406
+ def list_files(path: str) -> str:
1407
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
1389
1408
  try:
1390
- target = _safe_resolve(directory)
1391
- entries = os.listdir(target)
1409
+ full_path = _safe_resolve(path)
1410
+ entries = os.listdir(full_path)
1392
1411
  return "\\n".join(entries) if entries else "(empty directory)"
1393
1412
  except ValueError as e:
1394
1413
  return str(e)
1395
1414
  except OSError as e:
1396
- return f"Error listing '{directory}': {e.strerror}"
1415
+ return f"Error listing '{path}': {e.strerror}"
1397
1416
 
1398
1417
  tools.extend([file_read, file_write, list_files])
1399
1418
  {{/if}}
1400
1419
 
1401
1420
  SYSTEM_PROMPT = """
1402
1421
  You are a helpful assistant. Use tools when appropriate.
1403
- {{#if sessionStorageMountPath}}
1404
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1405
- {{/if}}
1422
+ {{#if needsOs}}
1423
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
1424
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
1425
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
1426
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
1427
+ {{/each}}{{/if}}
1406
1428
  """
1407
1429
 
1408
1430
  model = load_model()
@@ -1603,6 +1625,7 @@ dependencies = [
1603
1625
  {{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
1604
1626
  {{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
1605
1627
  {{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
1628
+ "google-genai >= 1.0.0, < 2.0.0",
1606
1629
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
1607
1630
  {{/if}}"aws-opentelemetry-distro",
1608
1631
  "opentelemetry-instrumentation-langchain >= 0.59.0",
@@ -1695,7 +1718,7 @@ from model.load import load_model
1695
1718
  {{#if hasMemory}}
1696
1719
  from memory.session import get_memory_session_manager
1697
1720
  {{/if}}
1698
- {{#if sessionStorageMountPath}}
1721
+ {{#if needsOs}}
1699
1722
  import os
1700
1723
  {{/if}}
1701
1724
 
@@ -1708,19 +1731,22 @@ def add_numbers(a: int, b: int) -> int:
1708
1731
 
1709
1732
  tools = [add_numbers]
1710
1733
 
1711
- {{#if sessionStorageMountPath}}
1712
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1734
+ {{#if needsOs}}
1735
+ _MOUNT_PATHS = [
1736
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
1737
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
1738
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
1739
+ ]
1713
1740
 
1714
1741
  def _safe_resolve(path: str) -> str:
1715
- """Resolve path safely within the storage boundary."""
1716
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1717
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1718
- raise ValueError(f"Path '{path}' is outside the storage boundary")
1742
+ resolved = os.path.realpath(path)
1743
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
1744
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
1719
1745
  return resolved
1720
1746
 
1721
1747
  @tool
1722
1748
  def file_read(path: str) -> str:
1723
- """Read a file from persistent storage. The path is relative to the storage root."""
1749
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
1724
1750
  try:
1725
1751
  full_path = _safe_resolve(path)
1726
1752
  with open(full_path) as f:
@@ -1732,7 +1758,7 @@ def file_read(path: str) -> str:
1732
1758
 
1733
1759
  @tool
1734
1760
  def file_write(path: str, content: str) -> str:
1735
- """Write content to a file in persistent storage. The path is relative to the storage root."""
1761
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
1736
1762
  try:
1737
1763
  full_path = _safe_resolve(path)
1738
1764
  parent = os.path.dirname(full_path)
@@ -1747,25 +1773,28 @@ def file_write(path: str, content: str) -> str:
1747
1773
  return f"Error writing '{path}': {e.strerror}"
1748
1774
 
1749
1775
  @tool
1750
- def list_files(directory: str = "") -> str:
1751
- """List files in persistent storage. The directory is relative to the storage root."""
1776
+ def list_files(path: str) -> str:
1777
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
1752
1778
  try:
1753
- target = _safe_resolve(directory)
1754
- entries = os.listdir(target)
1779
+ full_path = _safe_resolve(path)
1780
+ entries = os.listdir(full_path)
1755
1781
  return "\\n".join(entries) if entries else "(empty directory)"
1756
1782
  except ValueError as e:
1757
1783
  return str(e)
1758
1784
  except OSError as e:
1759
- return f"Error listing '{directory}': {e.strerror}"
1785
+ return f"Error listing '{path}': {e.strerror}"
1760
1786
 
1761
1787
  tools.extend([file_read, file_write, list_files])
1762
1788
  {{/if}}
1763
1789
 
1764
1790
  SYSTEM_PROMPT = """
1765
1791
  You are a helpful assistant. Use tools when appropriate.
1766
- {{#if sessionStorageMountPath}}
1767
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1768
- {{/if}}
1792
+ {{#if needsOs}}
1793
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
1794
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
1795
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
1796
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
1797
+ {{/each}}{{/if}}
1769
1798
  """
1770
1799
 
1771
1800
  {{#if hasMemory}}
@@ -2092,16 +2121,76 @@ exports[`Assets Directory Snapshots > Python framework assets > python/python/ag
2092
2121
  "import os
2093
2122
  import uvicorn
2094
2123
  from google.adk.agents import LlmAgent
2124
+ from google.adk.tools import FunctionTool
2095
2125
  from ag_ui_adk import ADKAgent, AGUIToolset, create_adk_app
2096
2126
  from model.load import load_model
2097
2127
 
2098
2128
  load_model()
2099
2129
 
2130
+ {{#if needsOs}}
2131
+ _MOUNT_PATHS = [
2132
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
2133
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
2134
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
2135
+ ]
2136
+
2137
+ def _safe_resolve(path: str) -> str:
2138
+ resolved = os.path.realpath(path)
2139
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
2140
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
2141
+ return resolved
2142
+
2143
+ def file_read(path: str) -> str:
2144
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
2145
+ try:
2146
+ full_path = _safe_resolve(path)
2147
+ with open(full_path) as f:
2148
+ return f.read()
2149
+ except ValueError as e:
2150
+ return str(e)
2151
+ except OSError as e:
2152
+ return f"Error reading '{path}': {e.strerror}"
2153
+
2154
+ def file_write(path: str, content: str) -> str:
2155
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
2156
+ try:
2157
+ full_path = _safe_resolve(path)
2158
+ parent = os.path.dirname(full_path)
2159
+ if parent:
2160
+ os.makedirs(parent, exist_ok=True)
2161
+ with open(full_path, "w") as f:
2162
+ f.write(content)
2163
+ return f"Written to {path}"
2164
+ except ValueError as e:
2165
+ return str(e)
2166
+ except OSError as e:
2167
+ return f"Error writing '{path}': {e.strerror}"
2168
+
2169
+ def list_files(path: str) -> str:
2170
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
2171
+ try:
2172
+ full_path = _safe_resolve(path)
2173
+ entries = os.listdir(full_path)
2174
+ return "\\n".join(entries) if entries else "(empty directory)"
2175
+ except ValueError as e:
2176
+ return str(e)
2177
+ except OSError as e:
2178
+ return f"Error listing '{path}': {e.strerror}"
2179
+
2180
+ _fs_tools = [FunctionTool(file_read), FunctionTool(file_write), FunctionTool(list_files)]
2181
+ {{/if}}
2182
+
2100
2183
  agent = LlmAgent(
2101
2184
  name="{{ name }}",
2102
2185
  model="gemini-2.5-flash",
2103
- instruction="You are a helpful assistant.",
2104
- tools=[AGUIToolset()],
2186
+ instruction="""You are a helpful assistant.
2187
+ {{#if needsOs}}
2188
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
2189
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
2190
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
2191
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
2192
+ {{/each}}{{/if}}""",
2193
+ tools=[AGUIToolset(), {{#if needsOs}}*_fs_tools{{/if}}],
2105
2194
  )
2106
2195
 
2107
2196
  adk_agent = ADKAgent(
@@ -2190,7 +2279,7 @@ dependencies = [
2190
2279
  "bedrock-agentcore >= 1.0.3",
2191
2280
  "fastapi >= 0.115.12",
2192
2281
  "google-adk >= 1.16.0, < 2.0.0",
2193
- "google-genai >= 1.0.0",
2282
+ "google-genai >= 1.0.0, < 2.0.0",
2194
2283
  "opentelemetry-distro",
2195
2284
  "opentelemetry-exporter-otlp",
2196
2285
  "uvicorn >= 0.34.3",
@@ -2300,6 +2389,62 @@ def add_numbers(a: int, b: int) -> int:
2300
2389
 
2301
2390
 
2302
2391
  backend_tools = [add_numbers]
2392
+
2393
+ {{#if needsOs}}
2394
+ _MOUNT_PATHS = [
2395
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
2396
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
2397
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
2398
+ ]
2399
+
2400
+ def _safe_resolve(path: str) -> str:
2401
+ resolved = os.path.realpath(path)
2402
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
2403
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
2404
+ return resolved
2405
+
2406
+ @tool
2407
+ def file_read(path: str) -> str:
2408
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
2409
+ try:
2410
+ full_path = _safe_resolve(path)
2411
+ with open(full_path) as f:
2412
+ return f.read()
2413
+ except ValueError as e:
2414
+ return str(e)
2415
+ except OSError as e:
2416
+ return f"Error reading '{path}': {e.strerror}"
2417
+
2418
+ @tool
2419
+ def file_write(path: str, content: str) -> str:
2420
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
2421
+ try:
2422
+ full_path = _safe_resolve(path)
2423
+ parent = os.path.dirname(full_path)
2424
+ if parent:
2425
+ os.makedirs(parent, exist_ok=True)
2426
+ with open(full_path, "w") as f:
2427
+ f.write(content)
2428
+ return f"Written to {path}"
2429
+ except ValueError as e:
2430
+ return str(e)
2431
+ except OSError as e:
2432
+ return f"Error writing '{path}': {e.strerror}"
2433
+
2434
+ @tool
2435
+ def list_files(path: str) -> str:
2436
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
2437
+ try:
2438
+ full_path = _safe_resolve(path)
2439
+ entries = os.listdir(full_path)
2440
+ return "\\n".join(entries) if entries else "(empty directory)"
2441
+ except ValueError as e:
2442
+ return str(e)
2443
+ except OSError as e:
2444
+ return f"Error listing '{path}': {e.strerror}"
2445
+
2446
+ backend_tools.extend([file_read, file_write, list_files])
2447
+ {{/if}}
2303
2448
  model = load_model()
2304
2449
 
2305
2450
 
@@ -2499,6 +2644,7 @@ dependencies = [
2499
2644
  {{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
2500
2645
  {{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
2501
2646
  {{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
2647
+ "google-genai >= 1.0.0, < 2.0.0",
2502
2648
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
2503
2649
  {{/if}}"aws-opentelemetry-distro",
2504
2650
  "opentelemetry-instrumentation-langchain >= 0.59.0",
@@ -2611,9 +2757,71 @@ def add_numbers(a: int, b: int) -> int:
2611
2757
 
2612
2758
  tools = [add_numbers]
2613
2759
 
2760
+ {{#if needsOs}}
2761
+ _MOUNT_PATHS = [
2762
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
2763
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
2764
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
2765
+ ]
2766
+
2767
+ def _safe_resolve(path: str) -> str:
2768
+ resolved = os.path.realpath(path)
2769
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
2770
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
2771
+ return resolved
2772
+
2773
+ @tool
2774
+ def file_read(path: str) -> str:
2775
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
2776
+ try:
2777
+ full_path = _safe_resolve(path)
2778
+ with open(full_path) as f:
2779
+ return f.read()
2780
+ except ValueError as e:
2781
+ return str(e)
2782
+ except OSError as e:
2783
+ return f"Error reading '{path}': {e.strerror}"
2784
+
2785
+ @tool
2786
+ def file_write(path: str, content: str) -> str:
2787
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
2788
+ try:
2789
+ full_path = _safe_resolve(path)
2790
+ parent = os.path.dirname(full_path)
2791
+ if parent:
2792
+ os.makedirs(parent, exist_ok=True)
2793
+ with open(full_path, "w") as f:
2794
+ f.write(content)
2795
+ return f"Written to {path}"
2796
+ except ValueError as e:
2797
+ return str(e)
2798
+ except OSError as e:
2799
+ return f"Error writing '{path}': {e.strerror}"
2800
+
2801
+ @tool
2802
+ def list_files(path: str) -> str:
2803
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
2804
+ try:
2805
+ full_path = _safe_resolve(path)
2806
+ entries = os.listdir(full_path)
2807
+ return "\\n".join(entries) if entries else "(empty directory)"
2808
+ except ValueError as e:
2809
+ return str(e)
2810
+ except OSError as e:
2811
+ return f"Error listing '{path}': {e.strerror}"
2812
+
2813
+ tools.extend([file_read, file_write, list_files])
2814
+ {{/if}}
2815
+
2614
2816
  agent = Agent(
2615
2817
  model=load_model(),
2616
- system_prompt="You are a helpful assistant. Use tools when appropriate.",
2818
+ system_prompt="""You are a helpful assistant. Use tools when appropriate.
2819
+ {{#if needsOs}}
2820
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
2821
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
2822
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
2823
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
2824
+ {{/each}}{{/if}}""",
2617
2825
  tools=tools,
2618
2826
  )
2619
2827
 
@@ -2938,7 +3146,9 @@ Thumbs.db
2938
3146
  `;
2939
3147
 
2940
3148
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/autogen/base/main.py should match snapshot 1`] = `
2941
- "import os
3149
+ "{{#if needsOs}}
3150
+ import os
3151
+ {{/if}}
2942
3152
  from autogen_agentchat.agents import AssistantAgent
2943
3153
  from autogen_core.tools import FunctionTool
2944
3154
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
@@ -2962,18 +3172,21 @@ add_numbers_tool = FunctionTool(
2962
3172
  # Define a collection of tools used by the model
2963
3173
  tools = [add_numbers_tool]
2964
3174
 
2965
- {{#if sessionStorageMountPath}}
2966
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
3175
+ {{#if needsOs}}
3176
+ _MOUNT_PATHS = [
3177
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
3178
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
3179
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
3180
+ ]
2967
3181
 
2968
3182
  def _safe_resolve(path: str) -> str:
2969
- """Resolve path safely within the storage boundary."""
2970
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
2971
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
2972
- raise ValueError(f"Path '{path}' is outside the storage boundary")
3183
+ resolved = os.path.realpath(path)
3184
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
3185
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
2973
3186
  return resolved
2974
3187
 
2975
3188
  def file_read(path: str) -> str:
2976
- """Read a file from persistent storage. The path is relative to the storage root."""
3189
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
2977
3190
  try:
2978
3191
  full_path = _safe_resolve(path)
2979
3192
  with open(full_path) as f:
@@ -2984,7 +3197,7 @@ def file_read(path: str) -> str:
2984
3197
  return f"Error reading '{path}': {e.strerror}"
2985
3198
 
2986
3199
  def file_write(path: str, content: str) -> str:
2987
- """Write content to a file in persistent storage. The path is relative to the storage root."""
3200
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
2988
3201
  try:
2989
3202
  full_path = _safe_resolve(path)
2990
3203
  parent = os.path.dirname(full_path)
@@ -2998,29 +3211,32 @@ def file_write(path: str, content: str) -> str:
2998
3211
  except OSError as e:
2999
3212
  return f"Error writing '{path}': {e.strerror}"
3000
3213
 
3001
- def list_files(directory: str = "") -> str:
3002
- """List files in persistent storage. The directory is relative to the storage root."""
3214
+ def list_files(path: str) -> str:
3215
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
3003
3216
  try:
3004
- target = _safe_resolve(directory)
3005
- entries = os.listdir(target)
3217
+ full_path = _safe_resolve(path)
3218
+ entries = os.listdir(full_path)
3006
3219
  return "\\n".join(entries) if entries else "(empty directory)"
3007
3220
  except ValueError as e:
3008
3221
  return str(e)
3009
3222
  except OSError as e:
3010
- return f"Error listing '{directory}': {e.strerror}"
3223
+ return f"Error listing '{path}': {e.strerror}"
3011
3224
 
3012
3225
  tools.extend([
3013
- FunctionTool(file_read, description="Read a file from persistent storage. The path is relative to the storage root."),
3014
- FunctionTool(file_write, description="Write content to a file in persistent storage. The path is relative to the storage root."),
3015
- FunctionTool(list_files, description="List files in persistent storage. The directory is relative to the storage root."),
3226
+ FunctionTool(file_read, description="Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."),
3227
+ FunctionTool(file_write, description="Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."),
3228
+ FunctionTool(list_files, description="List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."),
3016
3229
  ])
3017
3230
  {{/if}}
3018
3231
 
3019
3232
  SYSTEM_MESSAGE = """
3020
3233
  You are a helpful assistant. Use tools when appropriate.
3021
- {{#if sessionStorageMountPath}}
3022
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3023
- {{/if}}
3234
+ {{#if needsOs}}
3235
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
3236
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
3237
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
3238
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
3239
+ {{/each}}{{/if}}
3024
3240
  """
3025
3241
 
3026
3242
  @app.entrypoint
@@ -3362,7 +3578,9 @@ Thumbs.db
3362
3578
  `;
3363
3579
 
3364
3580
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/googleadk/base/main.py should match snapshot 1`] = `
3365
- "import os
3581
+ "{{#if needsOs}}
3582
+ import os
3583
+ {{/if}}
3366
3584
  from google.adk.agents import Agent
3367
3585
  from google.adk.runners import Runner
3368
3586
  from google.adk.sessions import InMemorySessionService
@@ -3393,18 +3611,21 @@ def add_numbers(a: int, b: int) -> int:
3393
3611
  # Define a collection of tools used by the model
3394
3612
  tools = [add_numbers]
3395
3613
 
3396
- {{#if sessionStorageMountPath}}
3397
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
3614
+ {{#if needsOs}}
3615
+ _MOUNT_PATHS = [
3616
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
3617
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
3618
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
3619
+ ]
3398
3620
 
3399
3621
  def _safe_resolve(path: str) -> str:
3400
- """Resolve path safely within the storage boundary."""
3401
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
3402
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
3403
- raise ValueError(f"Path '{path}' is outside the storage boundary")
3622
+ resolved = os.path.realpath(path)
3623
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
3624
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
3404
3625
  return resolved
3405
3626
 
3406
3627
  def file_read(path: str) -> str:
3407
- """Read a file from persistent storage. The path is relative to the storage root."""
3628
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
3408
3629
  try:
3409
3630
  full_path = _safe_resolve(path)
3410
3631
  with open(full_path) as f:
@@ -3415,7 +3636,7 @@ def file_read(path: str) -> str:
3415
3636
  return f"Error reading '{path}': {e.strerror}"
3416
3637
 
3417
3638
  def file_write(path: str, content: str) -> str:
3418
- """Write content to a file in persistent storage. The path is relative to the storage root."""
3639
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
3419
3640
  try:
3420
3641
  full_path = _safe_resolve(path)
3421
3642
  parent = os.path.dirname(full_path)
@@ -3429,25 +3650,28 @@ def file_write(path: str, content: str) -> str:
3429
3650
  except OSError as e:
3430
3651
  return f"Error writing '{path}': {e.strerror}"
3431
3652
 
3432
- def list_files(directory: str = "") -> str:
3433
- """List files in persistent storage. The directory is relative to the storage root."""
3653
+ def list_files(path: str) -> str:
3654
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
3434
3655
  try:
3435
- target = _safe_resolve(directory)
3436
- entries = os.listdir(target)
3656
+ full_path = _safe_resolve(path)
3657
+ entries = os.listdir(full_path)
3437
3658
  return "\\n".join(entries) if entries else "(empty directory)"
3438
3659
  except ValueError as e:
3439
3660
  return str(e)
3440
3661
  except OSError as e:
3441
- return f"Error listing '{directory}': {e.strerror}"
3662
+ return f"Error listing '{path}': {e.strerror}"
3442
3663
 
3443
3664
  tools.extend([file_read, file_write, list_files])
3444
3665
  {{/if}}
3445
3666
 
3446
3667
  AGENT_INSTRUCTION = """
3447
3668
  I can answer your questions using the knowledge I have!
3448
- {{#if sessionStorageMountPath}}
3449
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3450
- {{/if}}
3669
+ {{#if needsOs}}
3670
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
3671
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
3672
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
3673
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
3674
+ {{/each}}{{/if}}
3451
3675
  """
3452
3676
 
3453
3677
  # Get MCP Toolset
@@ -3674,6 +3898,7 @@ dependencies = [
3674
3898
  "opentelemetry-distro",
3675
3899
  "opentelemetry-exporter-otlp",
3676
3900
  "google-adk >= 1.17.0, < 2.0.0",
3901
+ "google-genai >= 1.0.0, < 2.0.0",
3677
3902
  "bedrock-agentcore >= 1.0.3",
3678
3903
  "botocore[crt] >= 1.35.0",
3679
3904
  {{#if hasGateway}}{{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0",
@@ -3774,7 +3999,9 @@ Thumbs.db
3774
3999
  `;
3775
4000
 
3776
4001
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/langchain_langgraph/base/main.py should match snapshot 1`] = `
3777
- "import os
4002
+ "{{#if needsOs}}
4003
+ import os
4004
+ {{/if}}
3778
4005
  from typing import Any
3779
4006
 
3780
4007
  from langchain_core.messages import HumanMessage{{#if hasConfigBundle}}, SystemMessage{{/if}}
@@ -3809,9 +4036,12 @@ def get_or_create_model():
3809
4036
 
3810
4037
  DEFAULT_SYSTEM_PROMPT = """
3811
4038
  You are a helpful assistant. Use tools when appropriate.
3812
- {{#if sessionStorageMountPath}}
3813
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3814
- {{/if}}
4039
+ {{#if needsOs}}
4040
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
4041
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
4042
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
4043
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
4044
+ {{/each}}{{/if}}
3815
4045
  """
3816
4046
 
3817
4047
 
@@ -3825,19 +4055,22 @@ def add_numbers(a: int, b: int) -> int:
3825
4055
  # Define a collection of tools used by the model
3826
4056
  tools = [add_numbers]
3827
4057
 
3828
- {{#if sessionStorageMountPath}}
3829
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
4058
+ {{#if needsOs}}
4059
+ _MOUNT_PATHS = [
4060
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
4061
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
4062
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
4063
+ ]
3830
4064
 
3831
4065
  def _safe_resolve(path: str) -> str:
3832
- """Resolve path safely within the storage boundary."""
3833
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
3834
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
3835
- raise ValueError(f"Path '{path}' is outside the storage boundary")
4066
+ resolved = os.path.realpath(path)
4067
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
4068
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
3836
4069
  return resolved
3837
4070
 
3838
4071
  @tool
3839
4072
  def file_read(path: str) -> str:
3840
- """Read a file from persistent storage. The path is relative to the storage root."""
4073
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
3841
4074
  try:
3842
4075
  full_path = _safe_resolve(path)
3843
4076
  with open(full_path) as f:
@@ -3849,7 +4082,7 @@ def file_read(path: str) -> str:
3849
4082
 
3850
4083
  @tool
3851
4084
  def file_write(path: str, content: str) -> str:
3852
- """Write content to a file in persistent storage. The path is relative to the storage root."""
4085
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
3853
4086
  try:
3854
4087
  full_path = _safe_resolve(path)
3855
4088
  parent = os.path.dirname(full_path)
@@ -3864,16 +4097,16 @@ def file_write(path: str, content: str) -> str:
3864
4097
  return f"Error writing '{path}': {e.strerror}"
3865
4098
 
3866
4099
  @tool
3867
- def list_files(directory: str = "") -> str:
3868
- """List files in persistent storage. The directory is relative to the storage root."""
4100
+ def list_files(path: str) -> str:
4101
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
3869
4102
  try:
3870
- target = _safe_resolve(directory)
3871
- entries = os.listdir(target)
4103
+ full_path = _safe_resolve(path)
4104
+ entries = os.listdir(full_path)
3872
4105
  return "\\n".join(entries) if entries else "(empty directory)"
3873
4106
  except ValueError as e:
3874
4107
  return str(e)
3875
4108
  except OSError as e:
3876
- return f"Error listing '{directory}': {e.strerror}"
4109
+ return f"Error listing '{path}': {e.strerror}"
3877
4110
 
3878
4111
  tools.extend([file_read, file_write, list_files])
3879
4112
  {{/if}}
@@ -4202,6 +4435,7 @@ dependencies = [
4202
4435
  {{/if}}
4203
4436
  {{#if (eq modelProvider "Gemini")}}
4204
4437
  "langchain-google-genai >= 3.0.3",
4438
+ "google-genai >= 1.0.0, < 2.0.0",
4205
4439
  {{/if}}
4206
4440
  {{#if hasGateway}}{{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0",
4207
4441
  {{/if}}{{/if}}
@@ -4301,7 +4535,9 @@ Thumbs.db
4301
4535
  `;
4302
4536
 
4303
4537
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/openaiagents/base/main.py should match snapshot 1`] = `
4304
- "import os
4538
+ "{{#if needsOs}}
4539
+ import os
4540
+ {{/if}}
4305
4541
  from agents import Agent, Runner, function_tool
4306
4542
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
4307
4543
  from model.load import load_model
@@ -4340,19 +4576,22 @@ def add_numbers(a: int, b: int) -> int:
4340
4576
 
4341
4577
  tools = [add_numbers]
4342
4578
 
4343
- {{#if sessionStorageMountPath}}
4344
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
4579
+ {{#if needsOs}}
4580
+ _MOUNT_PATHS = [
4581
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
4582
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
4583
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
4584
+ ]
4345
4585
 
4346
4586
  def _safe_resolve(path: str) -> str:
4347
- """Resolve path safely within the storage boundary."""
4348
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
4349
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
4350
- raise ValueError(f"Path '{path}' is outside the storage boundary")
4587
+ resolved = os.path.realpath(path)
4588
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
4589
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
4351
4590
  return resolved
4352
4591
 
4353
4592
  @function_tool
4354
4593
  def file_read(path: str) -> str:
4355
- """Read a file from persistent storage. The path is relative to the storage root."""
4594
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
4356
4595
  try:
4357
4596
  full_path = _safe_resolve(path)
4358
4597
  with open(full_path) as f:
@@ -4364,7 +4603,7 @@ def file_read(path: str) -> str:
4364
4603
 
4365
4604
  @function_tool
4366
4605
  def file_write(path: str, content: str) -> str:
4367
- """Write content to a file in persistent storage. The path is relative to the storage root."""
4606
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
4368
4607
  try:
4369
4608
  full_path = _safe_resolve(path)
4370
4609
  parent = os.path.dirname(full_path)
@@ -4379,25 +4618,28 @@ def file_write(path: str, content: str) -> str:
4379
4618
  return f"Error writing '{path}': {e.strerror}"
4380
4619
 
4381
4620
  @function_tool
4382
- def list_files(directory: str = "") -> str:
4383
- """List files in persistent storage. The directory is relative to the storage root."""
4621
+ def list_files(path: str) -> str:
4622
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
4384
4623
  try:
4385
- target = _safe_resolve(directory)
4386
- entries = os.listdir(target)
4624
+ full_path = _safe_resolve(path)
4625
+ entries = os.listdir(full_path)
4387
4626
  return "\\n".join(entries) if entries else "(empty directory)"
4388
4627
  except ValueError as e:
4389
4628
  return str(e)
4390
4629
  except OSError as e:
4391
- return f"Error listing '{directory}': {e.strerror}"
4630
+ return f"Error listing '{path}': {e.strerror}"
4392
4631
 
4393
4632
  tools.extend([file_read, file_write, list_files])
4394
4633
  {{/if}}
4395
4634
 
4396
4635
  INSTRUCTIONS = """
4397
4636
  You are a helpful assistant. Use tools when appropriate.
4398
- {{#if sessionStorageMountPath}}
4399
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
4400
- {{/if}}
4637
+ {{#if needsOs}}
4638
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
4639
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
4640
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
4641
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
4642
+ {{/each}}{{/if}}
4401
4643
  """
4402
4644
 
4403
4645
  # Define the agent execution
@@ -4732,7 +4974,7 @@ from mcp_client.client import get_streamable_http_mcp_client
4732
4974
  {{#if hasMemory}}
4733
4975
  from memory.session import get_memory_session_manager
4734
4976
  {{/if}}
4735
- {{#if sessionStorageMountPath}}
4977
+ {{#if needsOs}}
4736
4978
  import os
4737
4979
  {{/if}}
4738
4980
 
@@ -4748,9 +4990,12 @@ mcp_clients = [get_streamable_http_mcp_client()]
4748
4990
 
4749
4991
  DEFAULT_SYSTEM_PROMPT = """
4750
4992
  You are a helpful assistant. Use tools when appropriate.
4751
- {{#if sessionStorageMountPath}}
4752
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
4753
- {{/if}}
4993
+ {{#if needsOs}}
4994
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
4995
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
4996
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
4997
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
4998
+ {{/each}}{{/if}}
4754
4999
  """
4755
5000
 
4756
5001
  {{#if hasConfigBundle}}
@@ -4771,19 +5016,22 @@ def add_numbers(a: int, b: int) -> int:
4771
5016
  return a+b
4772
5017
  tools.append(add_numbers)
4773
5018
 
4774
- {{#if sessionStorageMountPath}}
4775
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
5019
+ {{#if needsOs}}
5020
+ _MOUNT_PATHS = [
5021
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
5022
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
5023
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
5024
+ ]
4776
5025
 
4777
5026
  def _safe_resolve(path: str) -> str:
4778
- """Resolve path safely within the storage boundary."""
4779
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
4780
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
4781
- raise ValueError(f"Path '{path}' is outside the storage boundary")
5027
+ resolved = os.path.realpath(path)
5028
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
5029
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
4782
5030
  return resolved
4783
5031
 
4784
5032
  @tool
4785
5033
  def file_read(path: str) -> str:
4786
- """Read a file from persistent storage. The path is relative to the storage root."""
5034
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
4787
5035
  try:
4788
5036
  full_path = _safe_resolve(path)
4789
5037
  with open(full_path) as f:
@@ -4795,7 +5043,7 @@ def file_read(path: str) -> str:
4795
5043
 
4796
5044
  @tool
4797
5045
  def file_write(path: str, content: str) -> str:
4798
- """Write content to a file in persistent storage. The path is relative to the storage root."""
5046
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
4799
5047
  try:
4800
5048
  full_path = _safe_resolve(path)
4801
5049
  parent = os.path.dirname(full_path)
@@ -4810,16 +5058,16 @@ def file_write(path: str, content: str) -> str:
4810
5058
  return f"Error writing '{path}': {e.strerror}"
4811
5059
 
4812
5060
  @tool
4813
- def list_files(directory: str = "") -> str:
4814
- """List files in persistent storage. The directory is relative to the storage root."""
5061
+ def list_files(path: str) -> str:
5062
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
4815
5063
  try:
4816
- target = _safe_resolve(directory)
4817
- entries = os.listdir(target)
5064
+ full_path = _safe_resolve(path)
5065
+ entries = os.listdir(full_path)
4818
5066
  return "\\n".join(entries) if entries else "(empty directory)"
4819
5067
  except ValueError as e:
4820
5068
  return str(e)
4821
5069
  except OSError as e:
4822
- return f"Error listing '{directory}': {e.strerror}"
5070
+ return f"Error listing '{path}': {e.strerror}"
4823
5071
 
4824
5072
  tools.extend([file_read, file_write, list_files])
4825
5073
  {{/if}}
@@ -5317,10 +5565,68 @@ Thumbs.db
5317
5565
  `;
5318
5566
 
5319
5567
  exports[`Assets Directory Snapshots > Python framework assets > python/python/mcp/standalone/base/main.py should match snapshot 1`] = `
5320
- "from mcp.server.fastmcp import FastMCP
5568
+ "{{#if needsOs}}
5569
+ import os
5570
+ {{/if}}
5571
+ from mcp.server.fastmcp import FastMCP
5321
5572
 
5322
5573
  mcp = FastMCP("{{ name }}", host="0.0.0.0", stateless_http=True)
5323
5574
 
5575
+ {{#if needsOs}}
5576
+ _MOUNT_PATHS = [
5577
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
5578
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
5579
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
5580
+ ]
5581
+
5582
+ def _safe_resolve(path: str) -> str:
5583
+ resolved = os.path.realpath(path)
5584
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
5585
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
5586
+ return resolved
5587
+
5588
+ @mcp.tool()
5589
+ def file_read(path: str) -> str:
5590
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
5591
+ try:
5592
+ full_path = _safe_resolve(path)
5593
+ with open(full_path) as f:
5594
+ return f.read()
5595
+ except ValueError as e:
5596
+ return str(e)
5597
+ except OSError as e:
5598
+ return f"Error reading '{path}': {e.strerror}"
5599
+
5600
+ @mcp.tool()
5601
+ def file_write(path: str, content: str) -> str:
5602
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
5603
+ try:
5604
+ full_path = _safe_resolve(path)
5605
+ parent = os.path.dirname(full_path)
5606
+ if parent:
5607
+ os.makedirs(parent, exist_ok=True)
5608
+ with open(full_path, "w") as f:
5609
+ f.write(content)
5610
+ return f"Written to {path}"
5611
+ except ValueError as e:
5612
+ return str(e)
5613
+ except OSError as e:
5614
+ return f"Error writing '{path}': {e.strerror}"
5615
+
5616
+ @mcp.tool()
5617
+ def list_files(path: str) -> str:
5618
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
5619
+ try:
5620
+ full_path = _safe_resolve(path)
5621
+ entries = os.listdir(full_path)
5622
+ return "\\n".join(entries) if entries else "(empty directory)"
5623
+ except ValueError as e:
5624
+ return str(e)
5625
+ except OSError as e:
5626
+ return f"Error listing '{path}': {e.strerror}"
5627
+
5628
+ {{/if}}
5629
+
5324
5630
 
5325
5631
  @mcp.tool()
5326
5632
  def add_numbers(a: int, b: int) -> int: