@aws/agentcore 1.0.0-preview.11 → 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 (41) 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 +446 -144
  4. package/dist/assets/cdk/bin/cdk.ts +4 -0
  5. package/dist/assets/cdk/lib/cdk-stack.ts +2 -0
  6. package/dist/assets/cdk/package.json +1 -1
  7. package/dist/assets/python/a2a/googleadk/base/main.py +24 -16
  8. package/dist/assets/python/a2a/langchain_langgraph/base/main.py +24 -16
  9. package/dist/assets/python/a2a/strands/base/main.py +23 -17
  10. package/dist/assets/python/agui/googleadk/base/main.py +62 -2
  11. package/dist/assets/python/agui/langchain_langgraph/base/main.py +56 -0
  12. package/dist/assets/python/agui/strands/base/main.py +63 -1
  13. package/dist/assets/python/http/autogen/base/main.py +27 -19
  14. package/dist/assets/python/http/googleadk/base/main.py +24 -16
  15. package/dist/assets/python/http/langchain_langgraph/base/main.py +24 -16
  16. package/dist/assets/python/http/openaiagents/base/main.py +24 -16
  17. package/dist/assets/python/http/strands/base/main.py +23 -17
  18. package/dist/assets/python/mcp/standalone/base/main.py +58 -0
  19. package/dist/cli/index.mjs +685 -639
  20. package/dist/lib/errors/types.d.ts +3 -0
  21. package/dist/lib/errors/types.d.ts.map +1 -1
  22. package/dist/lib/errors/types.js +10 -1
  23. package/dist/lib/errors/types.js.map +1 -1
  24. package/dist/lib/errors/zod.js +1 -1
  25. package/dist/lib/errors/zod.js.map +1 -1
  26. package/dist/lib/result.d.ts +6 -4
  27. package/dist/lib/result.d.ts.map +1 -1
  28. package/dist/lib/result.js +7 -0
  29. package/dist/lib/result.js.map +1 -1
  30. package/dist/schema/schemas/agent-env.d.ts +50 -4
  31. package/dist/schema/schemas/agent-env.d.ts.map +1 -1
  32. package/dist/schema/schemas/agent-env.js +102 -4
  33. package/dist/schema/schemas/agent-env.js.map +1 -1
  34. package/dist/schema/schemas/agentcore-project.d.ts +12 -2
  35. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  36. package/dist/schema/schemas/primitives/harness.d.ts +8 -0
  37. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
  38. package/dist/schema/schemas/primitives/harness.js +23 -0
  39. package/dist/schema/schemas/primitives/harness.js.map +1 -1
  40. package/npm-shrinkwrap.json +281 -366
  41. package/package.json +12 -7
@@ -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(
@@ -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()
@@ -1696,7 +1718,7 @@ from model.load import load_model
1696
1718
  {{#if hasMemory}}
1697
1719
  from memory.session import get_memory_session_manager
1698
1720
  {{/if}}
1699
- {{#if sessionStorageMountPath}}
1721
+ {{#if needsOs}}
1700
1722
  import os
1701
1723
  {{/if}}
1702
1724
 
@@ -1709,19 +1731,22 @@ def add_numbers(a: int, b: int) -> int:
1709
1731
 
1710
1732
  tools = [add_numbers]
1711
1733
 
1712
- {{#if sessionStorageMountPath}}
1713
- 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
+ ]
1714
1740
 
1715
1741
  def _safe_resolve(path: str) -> str:
1716
- """Resolve path safely within the storage boundary."""
1717
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1718
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1719
- 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)})")
1720
1745
  return resolved
1721
1746
 
1722
1747
  @tool
1723
1748
  def file_read(path: str) -> str:
1724
- """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)."""
1725
1750
  try:
1726
1751
  full_path = _safe_resolve(path)
1727
1752
  with open(full_path) as f:
@@ -1733,7 +1758,7 @@ def file_read(path: str) -> str:
1733
1758
 
1734
1759
  @tool
1735
1760
  def file_write(path: str, content: str) -> str:
1736
- """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)."""
1737
1762
  try:
1738
1763
  full_path = _safe_resolve(path)
1739
1764
  parent = os.path.dirname(full_path)
@@ -1748,25 +1773,28 @@ def file_write(path: str, content: str) -> str:
1748
1773
  return f"Error writing '{path}': {e.strerror}"
1749
1774
 
1750
1775
  @tool
1751
- def list_files(directory: str = "") -> str:
1752
- """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)."""
1753
1778
  try:
1754
- target = _safe_resolve(directory)
1755
- entries = os.listdir(target)
1779
+ full_path = _safe_resolve(path)
1780
+ entries = os.listdir(full_path)
1756
1781
  return "\\n".join(entries) if entries else "(empty directory)"
1757
1782
  except ValueError as e:
1758
1783
  return str(e)
1759
1784
  except OSError as e:
1760
- return f"Error listing '{directory}': {e.strerror}"
1785
+ return f"Error listing '{path}': {e.strerror}"
1761
1786
 
1762
1787
  tools.extend([file_read, file_write, list_files])
1763
1788
  {{/if}}
1764
1789
 
1765
1790
  SYSTEM_PROMPT = """
1766
1791
  You are a helpful assistant. Use tools when appropriate.
1767
- {{#if sessionStorageMountPath}}
1768
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1769
- {{/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}}
1770
1798
  """
1771
1799
 
1772
1800
  {{#if hasMemory}}
@@ -2093,16 +2121,76 @@ exports[`Assets Directory Snapshots > Python framework assets > python/python/ag
2093
2121
  "import os
2094
2122
  import uvicorn
2095
2123
  from google.adk.agents import LlmAgent
2124
+ from google.adk.tools import FunctionTool
2096
2125
  from ag_ui_adk import ADKAgent, AGUIToolset, create_adk_app
2097
2126
  from model.load import load_model
2098
2127
 
2099
2128
  load_model()
2100
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
+
2101
2183
  agent = LlmAgent(
2102
2184
  name="{{ name }}",
2103
2185
  model="gemini-2.5-flash",
2104
- instruction="You are a helpful assistant.",
2105
- 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}}],
2106
2194
  )
2107
2195
 
2108
2196
  adk_agent = ADKAgent(
@@ -2301,6 +2389,62 @@ def add_numbers(a: int, b: int) -> int:
2301
2389
 
2302
2390
 
2303
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}}
2304
2448
  model = load_model()
2305
2449
 
2306
2450
 
@@ -2613,9 +2757,71 @@ def add_numbers(a: int, b: int) -> int:
2613
2757
 
2614
2758
  tools = [add_numbers]
2615
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
+
2616
2816
  agent = Agent(
2617
2817
  model=load_model(),
2618
- 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}}""",
2619
2825
  tools=tools,
2620
2826
  )
2621
2827
 
@@ -2940,7 +3146,9 @@ Thumbs.db
2940
3146
  `;
2941
3147
 
2942
3148
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/autogen/base/main.py should match snapshot 1`] = `
2943
- "import os
3149
+ "{{#if needsOs}}
3150
+ import os
3151
+ {{/if}}
2944
3152
  from autogen_agentchat.agents import AssistantAgent
2945
3153
  from autogen_core.tools import FunctionTool
2946
3154
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
@@ -2964,18 +3172,21 @@ add_numbers_tool = FunctionTool(
2964
3172
  # Define a collection of tools used by the model
2965
3173
  tools = [add_numbers_tool]
2966
3174
 
2967
- {{#if sessionStorageMountPath}}
2968
- 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
+ ]
2969
3181
 
2970
3182
  def _safe_resolve(path: str) -> str:
2971
- """Resolve path safely within the storage boundary."""
2972
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
2973
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
2974
- 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)})")
2975
3186
  return resolved
2976
3187
 
2977
3188
  def file_read(path: str) -> str:
2978
- """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)."""
2979
3190
  try:
2980
3191
  full_path = _safe_resolve(path)
2981
3192
  with open(full_path) as f:
@@ -2986,7 +3197,7 @@ def file_read(path: str) -> str:
2986
3197
  return f"Error reading '{path}': {e.strerror}"
2987
3198
 
2988
3199
  def file_write(path: str, content: str) -> str:
2989
- """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)."""
2990
3201
  try:
2991
3202
  full_path = _safe_resolve(path)
2992
3203
  parent = os.path.dirname(full_path)
@@ -3000,29 +3211,32 @@ def file_write(path: str, content: str) -> str:
3000
3211
  except OSError as e:
3001
3212
  return f"Error writing '{path}': {e.strerror}"
3002
3213
 
3003
- def list_files(directory: str = "") -> str:
3004
- """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)."""
3005
3216
  try:
3006
- target = _safe_resolve(directory)
3007
- entries = os.listdir(target)
3217
+ full_path = _safe_resolve(path)
3218
+ entries = os.listdir(full_path)
3008
3219
  return "\\n".join(entries) if entries else "(empty directory)"
3009
3220
  except ValueError as e:
3010
3221
  return str(e)
3011
3222
  except OSError as e:
3012
- return f"Error listing '{directory}': {e.strerror}"
3223
+ return f"Error listing '{path}': {e.strerror}"
3013
3224
 
3014
3225
  tools.extend([
3015
- FunctionTool(file_read, description="Read a file from persistent storage. The path is relative to the storage root."),
3016
- FunctionTool(file_write, description="Write content to a file in persistent storage. The path is relative to the storage root."),
3017
- 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)."),
3018
3229
  ])
3019
3230
  {{/if}}
3020
3231
 
3021
3232
  SYSTEM_MESSAGE = """
3022
3233
  You are a helpful assistant. Use tools when appropriate.
3023
- {{#if sessionStorageMountPath}}
3024
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3025
- {{/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}}
3026
3240
  """
3027
3241
 
3028
3242
  @app.entrypoint
@@ -3364,7 +3578,9 @@ Thumbs.db
3364
3578
  `;
3365
3579
 
3366
3580
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/googleadk/base/main.py should match snapshot 1`] = `
3367
- "import os
3581
+ "{{#if needsOs}}
3582
+ import os
3583
+ {{/if}}
3368
3584
  from google.adk.agents import Agent
3369
3585
  from google.adk.runners import Runner
3370
3586
  from google.adk.sessions import InMemorySessionService
@@ -3395,18 +3611,21 @@ def add_numbers(a: int, b: int) -> int:
3395
3611
  # Define a collection of tools used by the model
3396
3612
  tools = [add_numbers]
3397
3613
 
3398
- {{#if sessionStorageMountPath}}
3399
- 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
+ ]
3400
3620
 
3401
3621
  def _safe_resolve(path: str) -> str:
3402
- """Resolve path safely within the storage boundary."""
3403
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
3404
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
3405
- 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)})")
3406
3625
  return resolved
3407
3626
 
3408
3627
  def file_read(path: str) -> str:
3409
- """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)."""
3410
3629
  try:
3411
3630
  full_path = _safe_resolve(path)
3412
3631
  with open(full_path) as f:
@@ -3417,7 +3636,7 @@ def file_read(path: str) -> str:
3417
3636
  return f"Error reading '{path}': {e.strerror}"
3418
3637
 
3419
3638
  def file_write(path: str, content: str) -> str:
3420
- """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)."""
3421
3640
  try:
3422
3641
  full_path = _safe_resolve(path)
3423
3642
  parent = os.path.dirname(full_path)
@@ -3431,25 +3650,28 @@ def file_write(path: str, content: str) -> str:
3431
3650
  except OSError as e:
3432
3651
  return f"Error writing '{path}': {e.strerror}"
3433
3652
 
3434
- def list_files(directory: str = "") -> str:
3435
- """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)."""
3436
3655
  try:
3437
- target = _safe_resolve(directory)
3438
- entries = os.listdir(target)
3656
+ full_path = _safe_resolve(path)
3657
+ entries = os.listdir(full_path)
3439
3658
  return "\\n".join(entries) if entries else "(empty directory)"
3440
3659
  except ValueError as e:
3441
3660
  return str(e)
3442
3661
  except OSError as e:
3443
- return f"Error listing '{directory}': {e.strerror}"
3662
+ return f"Error listing '{path}': {e.strerror}"
3444
3663
 
3445
3664
  tools.extend([file_read, file_write, list_files])
3446
3665
  {{/if}}
3447
3666
 
3448
3667
  AGENT_INSTRUCTION = """
3449
3668
  I can answer your questions using the knowledge I have!
3450
- {{#if sessionStorageMountPath}}
3451
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3452
- {{/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}}
3453
3675
  """
3454
3676
 
3455
3677
  # Get MCP Toolset
@@ -3777,7 +3999,9 @@ Thumbs.db
3777
3999
  `;
3778
4000
 
3779
4001
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/langchain_langgraph/base/main.py should match snapshot 1`] = `
3780
- "import os
4002
+ "{{#if needsOs}}
4003
+ import os
4004
+ {{/if}}
3781
4005
  from typing import Any
3782
4006
 
3783
4007
  from langchain_core.messages import HumanMessage{{#if hasConfigBundle}}, SystemMessage{{/if}}
@@ -3812,9 +4036,12 @@ def get_or_create_model():
3812
4036
 
3813
4037
  DEFAULT_SYSTEM_PROMPT = """
3814
4038
  You are a helpful assistant. Use tools when appropriate.
3815
- {{#if sessionStorageMountPath}}
3816
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3817
- {{/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}}
3818
4045
  """
3819
4046
 
3820
4047
 
@@ -3828,19 +4055,22 @@ def add_numbers(a: int, b: int) -> int:
3828
4055
  # Define a collection of tools used by the model
3829
4056
  tools = [add_numbers]
3830
4057
 
3831
- {{#if sessionStorageMountPath}}
3832
- 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
+ ]
3833
4064
 
3834
4065
  def _safe_resolve(path: str) -> str:
3835
- """Resolve path safely within the storage boundary."""
3836
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
3837
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
3838
- 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)})")
3839
4069
  return resolved
3840
4070
 
3841
4071
  @tool
3842
4072
  def file_read(path: str) -> str:
3843
- """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)."""
3844
4074
  try:
3845
4075
  full_path = _safe_resolve(path)
3846
4076
  with open(full_path) as f:
@@ -3852,7 +4082,7 @@ def file_read(path: str) -> str:
3852
4082
 
3853
4083
  @tool
3854
4084
  def file_write(path: str, content: str) -> str:
3855
- """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)."""
3856
4086
  try:
3857
4087
  full_path = _safe_resolve(path)
3858
4088
  parent = os.path.dirname(full_path)
@@ -3867,16 +4097,16 @@ def file_write(path: str, content: str) -> str:
3867
4097
  return f"Error writing '{path}': {e.strerror}"
3868
4098
 
3869
4099
  @tool
3870
- def list_files(directory: str = "") -> str:
3871
- """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)."""
3872
4102
  try:
3873
- target = _safe_resolve(directory)
3874
- entries = os.listdir(target)
4103
+ full_path = _safe_resolve(path)
4104
+ entries = os.listdir(full_path)
3875
4105
  return "\\n".join(entries) if entries else "(empty directory)"
3876
4106
  except ValueError as e:
3877
4107
  return str(e)
3878
4108
  except OSError as e:
3879
- return f"Error listing '{directory}': {e.strerror}"
4109
+ return f"Error listing '{path}': {e.strerror}"
3880
4110
 
3881
4111
  tools.extend([file_read, file_write, list_files])
3882
4112
  {{/if}}
@@ -4305,7 +4535,9 @@ Thumbs.db
4305
4535
  `;
4306
4536
 
4307
4537
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/openaiagents/base/main.py should match snapshot 1`] = `
4308
- "import os
4538
+ "{{#if needsOs}}
4539
+ import os
4540
+ {{/if}}
4309
4541
  from agents import Agent, Runner, function_tool
4310
4542
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
4311
4543
  from model.load import load_model
@@ -4344,19 +4576,22 @@ def add_numbers(a: int, b: int) -> int:
4344
4576
 
4345
4577
  tools = [add_numbers]
4346
4578
 
4347
- {{#if sessionStorageMountPath}}
4348
- 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
+ ]
4349
4585
 
4350
4586
  def _safe_resolve(path: str) -> str:
4351
- """Resolve path safely within the storage boundary."""
4352
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
4353
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
4354
- 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)})")
4355
4590
  return resolved
4356
4591
 
4357
4592
  @function_tool
4358
4593
  def file_read(path: str) -> str:
4359
- """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)."""
4360
4595
  try:
4361
4596
  full_path = _safe_resolve(path)
4362
4597
  with open(full_path) as f:
@@ -4368,7 +4603,7 @@ def file_read(path: str) -> str:
4368
4603
 
4369
4604
  @function_tool
4370
4605
  def file_write(path: str, content: str) -> str:
4371
- """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)."""
4372
4607
  try:
4373
4608
  full_path = _safe_resolve(path)
4374
4609
  parent = os.path.dirname(full_path)
@@ -4383,25 +4618,28 @@ def file_write(path: str, content: str) -> str:
4383
4618
  return f"Error writing '{path}': {e.strerror}"
4384
4619
 
4385
4620
  @function_tool
4386
- def list_files(directory: str = "") -> str:
4387
- """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)."""
4388
4623
  try:
4389
- target = _safe_resolve(directory)
4390
- entries = os.listdir(target)
4624
+ full_path = _safe_resolve(path)
4625
+ entries = os.listdir(full_path)
4391
4626
  return "\\n".join(entries) if entries else "(empty directory)"
4392
4627
  except ValueError as e:
4393
4628
  return str(e)
4394
4629
  except OSError as e:
4395
- return f"Error listing '{directory}': {e.strerror}"
4630
+ return f"Error listing '{path}': {e.strerror}"
4396
4631
 
4397
4632
  tools.extend([file_read, file_write, list_files])
4398
4633
  {{/if}}
4399
4634
 
4400
4635
  INSTRUCTIONS = """
4401
4636
  You are a helpful assistant. Use tools when appropriate.
4402
- {{#if sessionStorageMountPath}}
4403
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
4404
- {{/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}}
4405
4643
  """
4406
4644
 
4407
4645
  # Define the agent execution
@@ -4736,7 +4974,7 @@ from mcp_client.client import get_streamable_http_mcp_client
4736
4974
  {{#if hasMemory}}
4737
4975
  from memory.session import get_memory_session_manager
4738
4976
  {{/if}}
4739
- {{#if sessionStorageMountPath}}
4977
+ {{#if needsOs}}
4740
4978
  import os
4741
4979
  {{/if}}
4742
4980
 
@@ -4752,9 +4990,12 @@ mcp_clients = [get_streamable_http_mcp_client()]
4752
4990
 
4753
4991
  DEFAULT_SYSTEM_PROMPT = """
4754
4992
  You are a helpful assistant. Use tools when appropriate.
4755
- {{#if sessionStorageMountPath}}
4756
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
4757
- {{/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}}
4758
4999
  """
4759
5000
 
4760
5001
  {{#if hasConfigBundle}}
@@ -4775,19 +5016,22 @@ def add_numbers(a: int, b: int) -> int:
4775
5016
  return a+b
4776
5017
  tools.append(add_numbers)
4777
5018
 
4778
- {{#if sessionStorageMountPath}}
4779
- 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
+ ]
4780
5025
 
4781
5026
  def _safe_resolve(path: str) -> str:
4782
- """Resolve path safely within the storage boundary."""
4783
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
4784
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
4785
- 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)})")
4786
5030
  return resolved
4787
5031
 
4788
5032
  @tool
4789
5033
  def file_read(path: str) -> str:
4790
- """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)."""
4791
5035
  try:
4792
5036
  full_path = _safe_resolve(path)
4793
5037
  with open(full_path) as f:
@@ -4799,7 +5043,7 @@ def file_read(path: str) -> str:
4799
5043
 
4800
5044
  @tool
4801
5045
  def file_write(path: str, content: str) -> str:
4802
- """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)."""
4803
5047
  try:
4804
5048
  full_path = _safe_resolve(path)
4805
5049
  parent = os.path.dirname(full_path)
@@ -4814,16 +5058,16 @@ def file_write(path: str, content: str) -> str:
4814
5058
  return f"Error writing '{path}': {e.strerror}"
4815
5059
 
4816
5060
  @tool
4817
- def list_files(directory: str = "") -> str:
4818
- """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)."""
4819
5063
  try:
4820
- target = _safe_resolve(directory)
4821
- entries = os.listdir(target)
5064
+ full_path = _safe_resolve(path)
5065
+ entries = os.listdir(full_path)
4822
5066
  return "\\n".join(entries) if entries else "(empty directory)"
4823
5067
  except ValueError as e:
4824
5068
  return str(e)
4825
5069
  except OSError as e:
4826
- return f"Error listing '{directory}': {e.strerror}"
5070
+ return f"Error listing '{path}': {e.strerror}"
4827
5071
 
4828
5072
  tools.extend([file_read, file_write, list_files])
4829
5073
  {{/if}}
@@ -5321,10 +5565,68 @@ Thumbs.db
5321
5565
  `;
5322
5566
 
5323
5567
  exports[`Assets Directory Snapshots > Python framework assets > python/python/mcp/standalone/base/main.py should match snapshot 1`] = `
5324
- "from mcp.server.fastmcp import FastMCP
5568
+ "{{#if needsOs}}
5569
+ import os
5570
+ {{/if}}
5571
+ from mcp.server.fastmcp import FastMCP
5325
5572
 
5326
5573
  mcp = FastMCP("{{ name }}", host="0.0.0.0", stateless_http=True)
5327
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
+
5328
5630
 
5329
5631
  @mcp.tool()
5330
5632
  def add_numbers(a: int, b: int) -> int: