@aws/agentcore 1.0.0-preview.3 → 1.0.0-preview.31

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 (330) hide show
  1. package/README.md +193 -33
  2. package/dist/agent-inspector/index.css +1 -1
  3. package/dist/agent-inspector/index.js +99 -84
  4. package/dist/assets/README.md +6 -2
  5. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +3344 -366
  6. package/dist/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap +6 -2
  7. package/dist/assets/__tests__/assets.snapshot.test.ts +10 -24
  8. package/dist/assets/__tests__/googleadk-observability.test.ts +23 -0
  9. package/dist/assets/__tests__/googleadk-session-eviction.test.ts +87 -0
  10. package/dist/assets/__tests__/input-validation.test.ts +44 -0
  11. package/dist/assets/__tests__/summarization-namespace.test.ts +43 -0
  12. package/dist/assets/agents/AGENTS.md +91 -14
  13. package/dist/assets/cdk/bin/cdk.ts +109 -24
  14. package/dist/assets/cdk/lib/cdk-stack.ts +227 -19
  15. package/dist/assets/cdk/package.json +9 -9
  16. package/dist/assets/cdk/test/cdk.test.ts +4 -0
  17. package/dist/assets/container/python/Dockerfile +7 -1
  18. package/dist/assets/container/typescript/Dockerfile +25 -0
  19. package/dist/assets/container/typescript/dockerignore.template +24 -0
  20. package/dist/assets/datasets/predefined-v1.jsonl +3 -0
  21. package/dist/assets/datasets/simulated-v1.jsonl +3 -0
  22. package/dist/assets/evaluators/autoevals-lambda/execution-role-policy.json +15 -0
  23. package/dist/assets/evaluators/autoevals-lambda/lambda_function.py +37 -0
  24. package/dist/assets/evaluators/autoevals-lambda/pyproject.toml +22 -0
  25. package/dist/assets/evaluators/deepeval-lambda/execution-role-policy.json +15 -0
  26. package/dist/assets/evaluators/deepeval-lambda/lambda_function.py +29 -0
  27. package/dist/assets/evaluators/deepeval-lambda/pyproject.toml +19 -0
  28. package/dist/assets/mcp/python/pyproject.toml +1 -1
  29. package/dist/assets/python/a2a/googleadk/base/main.py +32 -18
  30. package/dist/assets/python/a2a/googleadk/base/pyproject.toml +6 -4
  31. package/dist/assets/python/a2a/langchain_langgraph/base/main.py +35 -21
  32. package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +4 -3
  33. package/dist/assets/python/a2a/strands/base/main.py +23 -17
  34. package/dist/assets/python/a2a/strands/base/pyproject.toml +3 -3
  35. package/dist/assets/python/a2a/strands/capabilities/memory/session.py +4 -1
  36. package/dist/assets/python/agui/googleadk/base/main.py +62 -2
  37. package/dist/assets/python/agui/googleadk/base/pyproject.toml +6 -5
  38. package/dist/assets/python/agui/langchain_langgraph/base/main.py +56 -0
  39. package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +3 -2
  40. package/dist/assets/python/agui/strands/base/main.py +63 -1
  41. package/dist/assets/python/agui/strands/base/pyproject.toml +2 -2
  42. package/dist/assets/python/agui/strands/capabilities/memory/session.py +4 -1
  43. package/dist/assets/python/http/autogen/base/README.md +7 -1
  44. package/dist/assets/python/http/autogen/base/main.py +62 -29
  45. package/dist/assets/python/http/autogen/base/pyproject.toml +1 -1
  46. package/dist/assets/python/http/googleadk/base/README.md +7 -1
  47. package/dist/assets/python/http/googleadk/base/main.py +71 -24
  48. package/dist/assets/python/http/googleadk/base/mcp_client/client.py +7 -7
  49. package/dist/assets/python/http/googleadk/base/pyproject.toml +5 -3
  50. package/dist/assets/python/http/langchain_langgraph/base/README.md +7 -1
  51. package/dist/assets/python/http/langchain_langgraph/base/main.py +119 -24
  52. package/dist/assets/python/http/langchain_langgraph/base/mcp_client/client.py +10 -8
  53. package/dist/assets/python/http/langchain_langgraph/base/pyproject.toml +5 -4
  54. package/dist/assets/python/http/openaiagents/base/README.md +7 -1
  55. package/dist/assets/python/http/openaiagents/base/main.py +63 -32
  56. package/dist/assets/python/http/openaiagents/base/mcp_client/client.py +4 -4
  57. package/dist/assets/python/http/openaiagents/base/pyproject.toml +3 -3
  58. package/dist/assets/python/http/strands/base/README.md +8 -1
  59. package/dist/assets/python/http/strands/base/main.py +607 -47
  60. package/dist/assets/python/http/strands/base/mcp_client/client.py +49 -6
  61. package/dist/assets/python/http/strands/base/model/load.py +119 -3
  62. package/dist/assets/python/http/strands/base/model/mantle_compat.py +21 -0
  63. package/dist/assets/python/http/strands/base/pyproject.toml +11 -5
  64. package/dist/assets/python/http/strands/base/skills/fetcher.py +279 -0
  65. package/dist/assets/python/http/strands/capabilities/execution-limits/hooks/execution_limits.py +54 -0
  66. package/dist/assets/python/http/strands/capabilities/memory/session.py +4 -1
  67. package/dist/assets/python/http/strands/capabilities/payments/__init__.py +4 -0
  68. package/dist/assets/python/http/strands/capabilities/payments/payments.py +142 -0
  69. package/dist/assets/python/mcp/standalone/base/main.py +58 -0
  70. package/dist/assets/python/mcp/standalone/base/pyproject.toml +1 -1
  71. package/dist/assets/typescript/http/strands/base/README.md +42 -0
  72. package/dist/assets/typescript/http/strands/base/gitignore.template +22 -0
  73. package/dist/assets/typescript/http/strands/base/main.ts +149 -0
  74. package/dist/assets/typescript/http/strands/base/mcp_client/client.ts +11 -0
  75. package/dist/assets/typescript/http/strands/base/model/load.ts +102 -0
  76. package/dist/assets/typescript/http/strands/base/package.json +38 -0
  77. package/dist/assets/typescript/http/strands/base/tsconfig.json +19 -0
  78. package/dist/assets/typescript/http/strands/capabilities/memory/memory.ts +52 -0
  79. package/dist/assets/typescript/http/vercelai/base/README.md +42 -0
  80. package/dist/assets/typescript/http/vercelai/base/gitignore.template +22 -0
  81. package/dist/assets/typescript/http/vercelai/base/main.ts +70 -0
  82. package/dist/assets/typescript/http/vercelai/base/model/load.ts +121 -0
  83. package/dist/assets/typescript/http/vercelai/base/package.json +35 -0
  84. package/dist/assets/typescript/http/vercelai/base/tsconfig.json +19 -0
  85. package/dist/cli/index.mjs +1743 -627
  86. package/dist/lib/constants.d.ts +8 -4
  87. package/dist/lib/constants.d.ts.map +1 -1
  88. package/dist/lib/constants.js +13 -7
  89. package/dist/lib/constants.js.map +1 -1
  90. package/dist/lib/dependency-management/index.d.ts +5 -0
  91. package/dist/lib/dependency-management/index.d.ts.map +1 -0
  92. package/dist/lib/dependency-management/index.js +8 -0
  93. package/dist/lib/dependency-management/index.js.map +1 -0
  94. package/dist/lib/dependency-management/messages.d.ts +30 -0
  95. package/dist/lib/dependency-management/messages.d.ts.map +1 -0
  96. package/dist/lib/dependency-management/messages.js +86 -0
  97. package/dist/lib/dependency-management/messages.js.map +1 -0
  98. package/dist/lib/dependency-management/plan.d.ts +25 -0
  99. package/dist/lib/dependency-management/plan.d.ts.map +1 -0
  100. package/dist/lib/dependency-management/plan.js +67 -0
  101. package/dist/lib/dependency-management/plan.js.map +1 -0
  102. package/dist/lib/dependency-management/semver.d.ts +21 -0
  103. package/dist/lib/dependency-management/semver.d.ts.map +1 -0
  104. package/dist/lib/dependency-management/semver.js +51 -0
  105. package/dist/lib/dependency-management/semver.js.map +1 -0
  106. package/dist/lib/dependency-management/sync.d.ts +26 -0
  107. package/dist/lib/dependency-management/sync.d.ts.map +1 -0
  108. package/dist/lib/dependency-management/sync.js +141 -0
  109. package/dist/lib/dependency-management/sync.js.map +1 -0
  110. package/dist/lib/dependency-management/types.d.ts +85 -0
  111. package/dist/lib/dependency-management/types.d.ts.map +1 -0
  112. package/dist/lib/dependency-management/types.js +3 -0
  113. package/dist/lib/dependency-management/types.js.map +1 -0
  114. package/dist/lib/errors/index.d.ts +1 -1
  115. package/dist/lib/errors/index.d.ts.map +1 -1
  116. package/dist/lib/errors/index.js +1 -1
  117. package/dist/lib/errors/index.js.map +1 -1
  118. package/dist/lib/errors/types.d.ts +274 -0
  119. package/dist/lib/errors/types.d.ts.map +1 -0
  120. package/dist/lib/errors/types.js +423 -0
  121. package/dist/lib/errors/types.js.map +1 -0
  122. package/dist/lib/errors/zod.d.ts +8 -0
  123. package/dist/lib/errors/zod.d.ts.map +1 -0
  124. package/dist/lib/errors/{config.js → zod.js} +8 -81
  125. package/dist/lib/errors/zod.js.map +1 -0
  126. package/dist/lib/index.d.ts +3 -0
  127. package/dist/lib/index.d.ts.map +1 -1
  128. package/dist/lib/index.js +4 -1
  129. package/dist/lib/index.js.map +1 -1
  130. package/dist/lib/packaging/build-args.d.ts +6 -0
  131. package/dist/lib/packaging/build-args.d.ts.map +1 -1
  132. package/dist/lib/packaging/build-args.js +11 -2
  133. package/dist/lib/packaging/build-args.js.map +1 -1
  134. package/dist/lib/packaging/build-context-dockerignore.d.ts +23 -0
  135. package/dist/lib/packaging/build-context-dockerignore.d.ts.map +1 -0
  136. package/dist/lib/packaging/build-context-dockerignore.js +63 -0
  137. package/dist/lib/packaging/build-context-dockerignore.js.map +1 -0
  138. package/dist/lib/packaging/build-context.d.ts +21 -0
  139. package/dist/lib/packaging/build-context.d.ts.map +1 -0
  140. package/dist/lib/packaging/build-context.js +21 -0
  141. package/dist/lib/packaging/build-context.js.map +1 -0
  142. package/dist/lib/packaging/container.d.ts.map +1 -1
  143. package/dist/lib/packaging/container.js +20 -9
  144. package/dist/lib/packaging/container.js.map +1 -1
  145. package/dist/lib/packaging/helpers.d.ts +16 -0
  146. package/dist/lib/packaging/helpers.d.ts.map +1 -1
  147. package/dist/lib/packaging/helpers.js +127 -29
  148. package/dist/lib/packaging/helpers.js.map +1 -1
  149. package/dist/lib/packaging/index.d.ts +2 -1
  150. package/dist/lib/packaging/index.d.ts.map +1 -1
  151. package/dist/lib/packaging/index.js +9 -20
  152. package/dist/lib/packaging/index.js.map +1 -1
  153. package/dist/lib/packaging/node.d.ts +2 -2
  154. package/dist/lib/packaging/node.d.ts.map +1 -1
  155. package/dist/lib/packaging/node.js +83 -38
  156. package/dist/lib/packaging/node.js.map +1 -1
  157. package/dist/lib/packaging/python.js +12 -12
  158. package/dist/lib/packaging/python.js.map +1 -1
  159. package/dist/lib/result.d.ts +57 -0
  160. package/dist/lib/result.d.ts.map +1 -0
  161. package/dist/lib/result.js +62 -0
  162. package/dist/lib/result.js.map +1 -0
  163. package/dist/lib/schemas/io/config-io.d.ts +14 -0
  164. package/dist/lib/schemas/io/config-io.d.ts.map +1 -1
  165. package/dist/lib/schemas/io/config-io.js +89 -5
  166. package/dist/lib/schemas/io/config-io.js.map +1 -1
  167. package/dist/lib/schemas/io/global-config.d.ts +41 -0
  168. package/dist/lib/schemas/io/global-config.d.ts.map +1 -0
  169. package/dist/lib/schemas/io/global-config.js +119 -0
  170. package/dist/lib/schemas/io/global-config.js.map +1 -0
  171. package/dist/lib/schemas/io/index.d.ts +1 -2
  172. package/dist/lib/schemas/io/index.d.ts.map +1 -1
  173. package/dist/lib/schemas/io/index.js +1 -4
  174. package/dist/lib/schemas/io/index.js.map +1 -1
  175. package/dist/lib/schemas/io/path-resolver.d.ts +0 -6
  176. package/dist/lib/schemas/io/path-resolver.d.ts.map +1 -1
  177. package/dist/lib/schemas/io/path-resolver.js +3 -13
  178. package/dist/lib/schemas/io/path-resolver.js.map +1 -1
  179. package/dist/lib/secrets/cipher.d.ts +12 -0
  180. package/dist/lib/secrets/cipher.d.ts.map +1 -0
  181. package/dist/lib/secrets/cipher.js +54 -0
  182. package/dist/lib/secrets/cipher.js.map +1 -0
  183. package/dist/lib/secrets/index.d.ts +4 -0
  184. package/dist/lib/secrets/index.d.ts.map +1 -0
  185. package/dist/lib/secrets/index.js +15 -0
  186. package/dist/lib/secrets/index.js.map +1 -0
  187. package/dist/lib/secrets/key-provider.d.ts +16 -0
  188. package/dist/lib/secrets/key-provider.d.ts.map +1 -0
  189. package/dist/lib/secrets/key-provider.js +191 -0
  190. package/dist/lib/secrets/key-provider.js.map +1 -0
  191. package/dist/lib/secrets/sensitive-keys.d.ts +21 -0
  192. package/dist/lib/secrets/sensitive-keys.d.ts.map +1 -0
  193. package/dist/lib/secrets/sensitive-keys.js +67 -0
  194. package/dist/lib/secrets/sensitive-keys.js.map +1 -0
  195. package/dist/lib/time-constants.d.ts +5 -0
  196. package/dist/lib/time-constants.d.ts.map +1 -0
  197. package/dist/lib/time-constants.js +8 -0
  198. package/dist/lib/time-constants.js.map +1 -0
  199. package/dist/lib/utils/env.d.ts +8 -2
  200. package/dist/lib/utils/env.d.ts.map +1 -1
  201. package/dist/lib/utils/env.js +65 -17
  202. package/dist/lib/utils/env.js.map +1 -1
  203. package/dist/lib/utils/index.d.ts +3 -2
  204. package/dist/lib/utils/index.d.ts.map +1 -1
  205. package/dist/lib/utils/index.js +6 -1
  206. package/dist/lib/utils/index.js.map +1 -1
  207. package/dist/lib/utils/polling.d.ts +31 -0
  208. package/dist/lib/utils/polling.d.ts.map +1 -0
  209. package/dist/lib/utils/polling.js +61 -0
  210. package/dist/lib/utils/polling.js.map +1 -0
  211. package/dist/lib/utils/subprocess.d.ts +4 -1
  212. package/dist/lib/utils/subprocess.d.ts.map +1 -1
  213. package/dist/lib/utils/subprocess.js +11 -57
  214. package/dist/lib/utils/subprocess.js.map +1 -1
  215. package/dist/lib/utils/zod.d.ts +16 -0
  216. package/dist/lib/utils/zod.d.ts.map +1 -1
  217. package/dist/lib/utils/zod.js +45 -0
  218. package/dist/lib/utils/zod.js.map +1 -1
  219. package/dist/schema/constants.d.ts +56 -2
  220. package/dist/schema/constants.d.ts.map +1 -1
  221. package/dist/schema/constants.js +82 -7
  222. package/dist/schema/constants.js.map +1 -1
  223. package/dist/schema/schemas/agent-env.d.ts +187 -7
  224. package/dist/schema/schemas/agent-env.d.ts.map +1 -1
  225. package/dist/schema/schemas/agent-env.js +323 -27
  226. package/dist/schema/schemas/agent-env.js.map +1 -1
  227. package/dist/schema/schemas/agentcore-project.d.ts +519 -12
  228. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  229. package/dist/schema/schemas/agentcore-project.js +342 -13
  230. package/dist/schema/schemas/agentcore-project.js.map +1 -1
  231. package/dist/schema/schemas/auth.d.ts +174 -0
  232. package/dist/schema/schemas/auth.d.ts.map +1 -1
  233. package/dist/schema/schemas/auth.js +127 -3
  234. package/dist/schema/schemas/auth.js.map +1 -1
  235. package/dist/schema/schemas/aws-targets.d.ts +18 -0
  236. package/dist/schema/schemas/aws-targets.d.ts.map +1 -1
  237. package/dist/schema/schemas/aws-targets.js +6 -0
  238. package/dist/schema/schemas/aws-targets.js.map +1 -1
  239. package/dist/schema/schemas/connections.d.ts +185 -0
  240. package/dist/schema/schemas/connections.d.ts.map +1 -0
  241. package/dist/schema/schemas/connections.js +176 -0
  242. package/dist/schema/schemas/connections.js.map +1 -0
  243. package/dist/schema/schemas/deployed-state.d.ts +392 -5
  244. package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
  245. package/dist/schema/schemas/deployed-state.js +112 -2
  246. package/dist/schema/schemas/deployed-state.js.map +1 -1
  247. package/dist/schema/schemas/index.d.ts +3 -0
  248. package/dist/schema/schemas/index.d.ts.map +1 -1
  249. package/dist/schema/schemas/index.js +5 -0
  250. package/dist/schema/schemas/index.js.map +1 -1
  251. package/dist/schema/schemas/mcp-defs.d.ts +2 -1
  252. package/dist/schema/schemas/mcp-defs.d.ts.map +1 -1
  253. package/dist/schema/schemas/mcp-defs.js +3 -2
  254. package/dist/schema/schemas/mcp-defs.js.map +1 -1
  255. package/dist/schema/schemas/mcp.d.ts +197 -0
  256. package/dist/schema/schemas/mcp.d.ts.map +1 -1
  257. package/dist/schema/schemas/mcp.js +334 -5
  258. package/dist/schema/schemas/mcp.js.map +1 -1
  259. package/dist/schema/schemas/primitives/ab-test.d.ts +135 -0
  260. package/dist/schema/schemas/primitives/ab-test.d.ts.map +1 -0
  261. package/dist/schema/schemas/primitives/ab-test.js +95 -0
  262. package/dist/schema/schemas/primitives/ab-test.js.map +1 -0
  263. package/dist/schema/schemas/primitives/capacity-provider.d.ts +237 -0
  264. package/dist/schema/schemas/primitives/capacity-provider.d.ts.map +1 -0
  265. package/dist/schema/schemas/primitives/capacity-provider.js +220 -0
  266. package/dist/schema/schemas/primitives/capacity-provider.js.map +1 -0
  267. package/dist/schema/schemas/primitives/config-bundle.d.ts +32 -0
  268. package/dist/schema/schemas/primitives/config-bundle.d.ts.map +1 -0
  269. package/dist/schema/schemas/primitives/config-bundle.js +41 -0
  270. package/dist/schema/schemas/primitives/config-bundle.js.map +1 -0
  271. package/dist/schema/schemas/primitives/dataset.d.ts +48 -0
  272. package/dist/schema/schemas/primitives/dataset.d.ts.map +1 -0
  273. package/dist/schema/schemas/primitives/dataset.js +58 -0
  274. package/dist/schema/schemas/primitives/dataset.js.map +1 -0
  275. package/dist/schema/schemas/primitives/evaluator.d.ts +22 -0
  276. package/dist/schema/schemas/primitives/evaluator.d.ts.map +1 -1
  277. package/dist/schema/schemas/primitives/evaluator.js +28 -3
  278. package/dist/schema/schemas/primitives/evaluator.js.map +1 -1
  279. package/dist/schema/schemas/primitives/harness.d.ts +377 -19
  280. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
  281. package/dist/schema/schemas/primitives/harness.js +379 -24
  282. package/dist/schema/schemas/primitives/harness.js.map +1 -1
  283. package/dist/schema/schemas/primitives/index.d.ts +17 -9
  284. package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
  285. package/dist/schema/schemas/primitives/index.js +62 -1
  286. package/dist/schema/schemas/primitives/index.js.map +1 -1
  287. package/dist/schema/schemas/primitives/knowledge-base.d.ts +104 -0
  288. package/dist/schema/schemas/primitives/knowledge-base.d.ts.map +1 -0
  289. package/dist/schema/schemas/primitives/knowledge-base.js +99 -0
  290. package/dist/schema/schemas/primitives/knowledge-base.js.map +1 -0
  291. package/dist/schema/schemas/primitives/memory.d.ts +22 -4
  292. package/dist/schema/schemas/primitives/memory.d.ts.map +1 -1
  293. package/dist/schema/schemas/primitives/memory.js +57 -16
  294. package/dist/schema/schemas/primitives/memory.js.map +1 -1
  295. package/dist/schema/schemas/primitives/online-eval-config.d.ts +21 -2
  296. package/dist/schema/schemas/primitives/online-eval-config.d.ts.map +1 -1
  297. package/dist/schema/schemas/primitives/online-eval-config.js +45 -5
  298. package/dist/schema/schemas/primitives/online-eval-config.js.map +1 -1
  299. package/dist/schema/schemas/primitives/payment.d.ts +86 -0
  300. package/dist/schema/schemas/primitives/payment.d.ts.map +1 -0
  301. package/dist/schema/schemas/primitives/payment.js +88 -0
  302. package/dist/schema/schemas/primitives/payment.js.map +1 -0
  303. package/dist/schema/schemas/primitives/policy.d.ts +26 -0
  304. package/dist/schema/schemas/primitives/policy.d.ts.map +1 -1
  305. package/dist/schema/schemas/primitives/policy.js +5 -1
  306. package/dist/schema/schemas/primitives/policy.js.map +1 -1
  307. package/npm-shrinkwrap.json +16874 -0
  308. package/package.json +49 -21
  309. package/scripts/bump-version.ts +4 -4
  310. package/scripts/bundle.mjs +70 -41
  311. package/scripts/extract-cli-model.mjs +269 -0
  312. package/scripts/extract-cli-model.test.mjs +43 -0
  313. package/scripts/render_adoc.py +350 -0
  314. package/scripts/run-e2e-dev.sh +56 -0
  315. package/scripts/run-e2e-local.sh +145 -0
  316. package/scripts/safe-junit-reporter.ts +7 -0
  317. package/scripts/sanitize-test-artifacts.ts +65 -0
  318. package/scripts/sync-vended-cdk.ts +49 -0
  319. package/scripts/test_render_adoc.py +38 -0
  320. package/dist/lib/errors/config.d.ts +0 -49
  321. package/dist/lib/errors/config.d.ts.map +0 -1
  322. package/dist/lib/errors/config.js.map +0 -1
  323. package/dist/lib/packaging/errors.d.ts +0 -16
  324. package/dist/lib/packaging/errors.d.ts.map +0 -1
  325. package/dist/lib/packaging/errors.js +0 -36
  326. package/dist/lib/packaging/errors.js.map +0 -1
  327. package/dist/lib/schemas/io/cli-config.d.ts +0 -12
  328. package/dist/lib/schemas/io/cli-config.d.ts.map +0 -1
  329. package/dist/lib/schemas/io/cli-config.js +0 -35
  330. package/dist/lib/schemas/io/cli-config.js.map +0 -1
@@ -24,8 +24,11 @@ def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Opti
24
24
  {{#if (includes memoryProviders.[0].strategies "USER_PREFERENCE")}}
25
25
  f"/users/{actor_id}/preferences": RetrievalConfig(top_k=3, relevance_score=0.5),
26
26
  {{/if}}
27
+ {{#if (includes memoryProviders.[0].strategies "EPISODIC")}}
28
+ f"/episodes/{actor_id}/{session_id}": RetrievalConfig(top_k=5, relevance_score=0.5),
29
+ {{/if}}
27
30
  {{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
28
- f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
31
+ f"/summaries/{actor_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
29
32
  {{/if}}
30
33
  }
31
34
  {{/if}}
@@ -13,6 +13,11 @@ file defines a Starlette ASGI app with the AutoGen framework running within.
13
13
 
14
14
  `model/load.py` instantiates your chosen model provider.
15
15
 
16
+ ## Input Validation
17
+
18
+ Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
19
+ only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -24,7 +29,8 @@ file defines a Starlette ASGI app with the AutoGen framework running within.
24
29
 
25
30
  If installation was successful, a virtual environment is already created with dependencies installed.
26
31
 
27
- Run `source .venv/bin/activate` before developing.
32
+ Activate the environment with `source .venv/bin/activate` on macOS/Linux, `.venv\Scripts\activate.bat` in Windows
33
+ Command Prompt, or `.\.venv\Scripts\activate.ps1` in Windows PowerShell.
28
34
 
29
35
  `agentcore dev` will start a local server on 0.0.0.0:8080.
30
36
 
@@ -1,4 +1,7 @@
1
+ {{#if needsOs}}
1
2
  import os
3
+ {{/if}}
4
+ from collections import OrderedDict
2
5
  from autogen_agentchat.agents import AssistantAgent
3
6
  from autogen_core.tools import FunctionTool
4
7
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
@@ -22,18 +25,21 @@ add_numbers_tool = FunctionTool(
22
25
  # Define a collection of tools used by the model
23
26
  tools = [add_numbers_tool]
24
27
 
25
- {{#if sessionStorageMountPath}}
26
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
28
+ {{#if needsOs}}
29
+ _MOUNT_PATHS = [
30
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
31
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
32
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
33
+ ]
27
34
 
28
35
  def _safe_resolve(path: str) -> str:
29
- """Resolve path safely within the storage boundary."""
30
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
31
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
32
- raise ValueError(f"Path '{path}' is outside the storage boundary")
36
+ resolved = os.path.realpath(path)
37
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
38
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
33
39
  return resolved
34
40
 
35
41
  def file_read(path: str) -> str:
36
- """Read a file from persistent storage. The path is relative to the storage root."""
42
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
37
43
  try:
38
44
  full_path = _safe_resolve(path)
39
45
  with open(full_path) as f:
@@ -44,7 +50,7 @@ def file_read(path: str) -> str:
44
50
  return f"Error reading '{path}': {e.strerror}"
45
51
 
46
52
  def file_write(path: str, content: str) -> str:
47
- """Write content to a file in persistent storage. The path is relative to the storage root."""
53
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
48
54
  try:
49
55
  full_path = _safe_resolve(path)
50
56
  parent = os.path.dirname(full_path)
@@ -58,48 +64,75 @@ def file_write(path: str, content: str) -> str:
58
64
  except OSError as e:
59
65
  return f"Error writing '{path}': {e.strerror}"
60
66
 
61
- def list_files(directory: str = "") -> str:
62
- """List files in persistent storage. The directory is relative to the storage root."""
67
+ def list_files(path: str) -> str:
68
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
63
69
  try:
64
- target = _safe_resolve(directory)
65
- entries = os.listdir(target)
70
+ full_path = _safe_resolve(path)
71
+ entries = os.listdir(full_path)
66
72
  return "\n".join(entries) if entries else "(empty directory)"
67
73
  except ValueError as e:
68
74
  return str(e)
69
75
  except OSError as e:
70
- return f"Error listing '{directory}': {e.strerror}"
76
+ return f"Error listing '{path}': {e.strerror}"
71
77
 
72
78
  tools.extend([
73
- FunctionTool(file_read, description="Read a file from persistent storage. The path is relative to the storage root."),
74
- FunctionTool(file_write, description="Write content to a file in persistent storage. The path is relative to the storage root."),
75
- FunctionTool(list_files, description="List files in persistent storage. The directory is relative to the storage root."),
79
+ FunctionTool(file_read, description="Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."),
80
+ FunctionTool(file_write, description="Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."),
81
+ FunctionTool(list_files, description="List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."),
76
82
  ])
77
83
  {{/if}}
78
84
 
79
85
  SYSTEM_MESSAGE = """
80
86
  You are a helpful assistant. Use tools when appropriate.
81
- {{#if sessionStorageMountPath}}
82
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
83
- {{/if}}
87
+ {{#if needsOs}}
88
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
89
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
90
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
91
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
92
+ {{/each}}{{/if}}
84
93
  """
85
94
 
86
- @app.entrypoint
87
- async def invoke(payload, context):
88
- log.info("Invoking Agent.....")
95
+ # Reuses one AssistantAgent per session_id so each session keeps its own
96
+ # in-process conversation history (best-effort; resets on cold start). Caches up
97
+ # to 128 active sessions with LRU eviction (least-recently-used is dropped and
98
+ # its history reset).
99
+ _agents = OrderedDict()
89
100
 
101
+
102
+ async def get_or_create_agent(session_id):
103
+ if session_id in _agents:
104
+ _agents.move_to_end(session_id)
105
+ return _agents[session_id]
106
+ if len(_agents) >= 128:
107
+ _agents.popitem(last=False)
90
108
  # Get MCP Tools
91
109
  mcp_tools = await get_streamable_http_mcp_tools()
110
+ # Re-check after the await: a concurrent first-invocation for the same
111
+ # session_id may have built and stored the agent while we were awaiting.
112
+ # Don't overwrite it (that would orphan the agent the other request is using).
113
+ if session_id not in _agents:
114
+ _agents[session_id] = AssistantAgent(
115
+ name="{{ name }}",
116
+ model_client=load_model(),
117
+ tools=tools + mcp_tools,
118
+ system_message=SYSTEM_MESSAGE,
119
+ )
120
+ _agents.move_to_end(session_id)
121
+ return _agents[session_id]
122
+
92
123
 
93
- # Define an AssistantAgent with the model and tools
94
- agent = AssistantAgent(
95
- name="{{ name }}",
96
- model_client=load_model(),
97
- tools=tools + mcp_tools,
98
- system_message=SYSTEM_MESSAGE,
99
- )
124
+ @app.entrypoint
125
+ async def invoke(payload, context):
126
+ log.info("Invoking Agent.....")
100
127
 
101
128
  # Process the user prompt
102
129
  prompt = payload.get("prompt", "What can you help me with?")
130
+ if not isinstance(prompt, str):
131
+ raise ValueError("prompt must be a string")
132
+ session_id = getattr(context, "session_id", "default-session")
133
+
134
+ # Reuse the per-session agent (preserves conversation history)
135
+ agent = await get_or_create_agent(session_id)
103
136
 
104
137
  # Run the agent
105
138
  result = await agent.run(task=prompt)
@@ -13,7 +13,7 @@ dependencies = [
13
13
  "autogen-ext[mcp] >= 0.7.5",
14
14
  "opentelemetry-distro",
15
15
  "opentelemetry-exporter-otlp",
16
- "bedrock-agentcore >= 1.0.3",
16
+ "bedrock-agentcore >= 1.8.0",
17
17
  "botocore[crt] >= 1.35.0",
18
18
  "tiktoken",
19
19
  {{#if (eq modelProvider "Bedrock")}}
@@ -13,6 +13,11 @@ file defines a Starlette ASGI app with the Google ADK framework running within.
13
13
 
14
14
  `model/load.py` instantiates your chosen model provider (Gemini).
15
15
 
16
+ ## Input Validation
17
+
18
+ Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
19
+ only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -24,7 +29,8 @@ file defines a Starlette ASGI app with the Google ADK framework running within.
24
29
 
25
30
  If installation was successful, a virtual environment is already created with dependencies installed.
26
31
 
27
- Run `source .venv/bin/activate` before developing.
32
+ Activate the environment with `source .venv/bin/activate` on macOS/Linux, `.venv\Scripts\activate.bat` in Windows
33
+ Command Prompt, or `.\.venv\Scripts\activate.ps1` in Windows PowerShell.
28
34
 
29
35
  `agentcore dev` will start a local server on 0.0.0.0:8080.
30
36
 
@@ -1,4 +1,7 @@
1
+ {{#if needsOs}}
1
2
  import os
3
+ {{/if}}
4
+ from collections import OrderedDict
2
5
  from google.adk.agents import Agent
3
6
  from google.adk.runners import Runner
4
7
  from google.adk.sessions import InMemorySessionService
@@ -29,18 +32,21 @@ def add_numbers(a: int, b: int) -> int:
29
32
  # Define a collection of tools used by the model
30
33
  tools = [add_numbers]
31
34
 
32
- {{#if sessionStorageMountPath}}
33
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
35
+ {{#if needsOs}}
36
+ _MOUNT_PATHS = [
37
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
38
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
39
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
40
+ ]
34
41
 
35
42
  def _safe_resolve(path: str) -> str:
36
- """Resolve path safely within the storage boundary."""
37
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
38
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
39
- raise ValueError(f"Path '{path}' is outside the storage boundary")
43
+ resolved = os.path.realpath(path)
44
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
45
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
40
46
  return resolved
41
47
 
42
48
  def file_read(path: str) -> str:
43
- """Read a file from persistent storage. The path is relative to the storage root."""
49
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
44
50
  try:
45
51
  full_path = _safe_resolve(path)
46
52
  with open(full_path) as f:
@@ -51,7 +57,7 @@ def file_read(path: str) -> str:
51
57
  return f"Error reading '{path}': {e.strerror}"
52
58
 
53
59
  def file_write(path: str, content: str) -> str:
54
- """Write content to a file in persistent storage. The path is relative to the storage root."""
60
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
55
61
  try:
56
62
  full_path = _safe_resolve(path)
57
63
  parent = os.path.dirname(full_path)
@@ -65,25 +71,28 @@ def file_write(path: str, content: str) -> str:
65
71
  except OSError as e:
66
72
  return f"Error writing '{path}': {e.strerror}"
67
73
 
68
- def list_files(directory: str = "") -> str:
69
- """List files in persistent storage. The directory is relative to the storage root."""
74
+ def list_files(path: str) -> str:
75
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
70
76
  try:
71
- target = _safe_resolve(directory)
72
- entries = os.listdir(target)
77
+ full_path = _safe_resolve(path)
78
+ entries = os.listdir(full_path)
73
79
  return "\n".join(entries) if entries else "(empty directory)"
74
80
  except ValueError as e:
75
81
  return str(e)
76
82
  except OSError as e:
77
- return f"Error listing '{directory}': {e.strerror}"
83
+ return f"Error listing '{path}': {e.strerror}"
78
84
 
79
85
  tools.extend([file_read, file_write, list_files])
80
86
  {{/if}}
81
87
 
82
88
  AGENT_INSTRUCTION = """
83
89
  I can answer your questions using the knowledge I have!
84
- {{#if sessionStorageMountPath}}
85
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
86
- {{/if}}
90
+ {{#if needsOs}}
91
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
92
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
93
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
94
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
95
+ {{/each}}{{/if}}
87
96
  """
88
97
 
89
98
  # Get MCP Toolset
@@ -113,21 +122,57 @@ agent = Agent(
113
122
  )
114
123
 
115
124
 
116
- # Session and Runner
117
- async def setup_session_and_runner(user_id, session_id):
118
- ensure_credentials_loaded()
119
- session_service = InMemorySessionService()
120
- session = await session_service.create_session(
125
+ # Module-level session service and runner preserve history across invocations.
126
+ # InMemorySessionService retains every (app_name, user_id, session_id) triple
127
+ # forever, so we bound it to 128 active sessions with LRU eviction (the
128
+ # least-recently-used session is deleted and its history reset) to keep a
129
+ # long-running process from growing without limit. For durable history, swap in
130
+ # a persistent session service (e.g. DatabaseSessionService).
131
+ _SESSION_LIMIT = 128
132
+ _session_service = InMemorySessionService()
133
+ _session_keys = OrderedDict()
134
+ _runner = None
135
+
136
+
137
+ def get_or_create_runner():
138
+ global _runner
139
+ if _runner is None:
140
+ ensure_credentials_loaded()
141
+ _runner = Runner(
142
+ agent=agent,
143
+ app_name=APP_NAME,
144
+ session_service=_session_service,
145
+ )
146
+ return _runner
147
+
148
+
149
+ async def get_or_create_session(user_id, session_id):
150
+ key = (user_id, session_id)
151
+ if key in _session_keys:
152
+ _session_keys.move_to_end(key)
153
+ else:
154
+ while len(_session_keys) >= _SESSION_LIMIT:
155
+ (old_user_id, old_session_id), _ = _session_keys.popitem(last=False)
156
+ await _session_service.delete_session(
157
+ app_name=APP_NAME, user_id=old_user_id, session_id=old_session_id
158
+ )
159
+ _session_keys[key] = True
160
+
161
+ session = await _session_service.get_session(
121
162
  app_name=APP_NAME, user_id=user_id, session_id=session_id
122
163
  )
123
- runner = Runner(agent=agent, app_name=APP_NAME, session_service=session_service)
124
- return session, runner
164
+ if session is None:
165
+ session = await _session_service.create_session(
166
+ app_name=APP_NAME, user_id=user_id, session_id=session_id
167
+ )
168
+ return session
125
169
 
126
170
 
127
171
  # Agent Interaction
128
172
  async def call_agent_async(query, user_id, session_id):
129
173
  content = types.Content(role="user", parts=[types.Part(text=query)])
130
- session, runner = await setup_session_and_runner(user_id, session_id)
174
+ runner = get_or_create_runner()
175
+ session = await get_or_create_session(user_id, session_id)
131
176
  events = runner.run_async(
132
177
  user_id=user_id, session_id=session.id, new_message=content
133
178
  )
@@ -146,6 +191,8 @@ async def invoke(payload, context):
146
191
 
147
192
  # Process the user prompt
148
193
  prompt = payload.get("prompt", "What can you help me with?")
194
+ if not isinstance(prompt, str):
195
+ raise ValueError("prompt must be a string")
149
196
  session_id = getattr(context, "session_id", "default_session")
150
197
  user_id = payload.get("user_id", "default_user")
151
198
 
@@ -34,20 +34,20 @@ def get_all_gateway_mcp_toolsets() -> list[MCPToolset]:
34
34
  {{#each gatewayProviders}}
35
35
  url = os.environ.get("{{envVarName}}")
36
36
  if url:
37
- {{#if (eq authType "AWS_IAM")}}
37
+ {{#if (eq authType "AWS_IAM")}}
38
38
  session = create_aws_session()
39
39
  auth = SigV4HTTPXAuth(session.get_credentials(), "bedrock-agentcore", session.region_name)
40
- toolsets.append(MCPToolset(connection_params=StreamableHTTPConnectionParams(
40
+ toolsets.append(MCPToolset(tool_name_prefix="{{snakeCase name}}", connection_params=StreamableHTTPConnectionParams(
41
41
  url=url,
42
42
  httpx_client_factory=lambda **kwargs: httpx.AsyncClient(auth=auth, **kwargs)
43
43
  )))
44
- {{else if (eq authType "CUSTOM_JWT")}}
44
+ {{else if (eq authType "CUSTOM_JWT")}}
45
45
  token = _get_bearer_token_{{snakeCase name}}()
46
46
  headers = {"Authorization": f"Bearer {token}"} if token else None
47
- toolsets.append(MCPToolset(connection_params=StreamableHTTPConnectionParams(url=url, headers=headers)))
48
- {{else}}
49
- toolsets.append(MCPToolset(connection_params=StreamableHTTPConnectionParams(url=url)))
50
- {{/if}}
47
+ toolsets.append(MCPToolset(tool_name_prefix="{{snakeCase name}}", connection_params=StreamableHTTPConnectionParams(url=url, headers=headers)))
48
+ {{else}}
49
+ toolsets.append(MCPToolset(tool_name_prefix="{{snakeCase name}}", connection_params=StreamableHTTPConnectionParams(url=url)))
50
+ {{/if}}
51
51
  else:
52
52
  logger.warning("{{envVarName}} not set — {{name}} gateway tools unavailable")
53
53
  {{/each}}
@@ -9,9 +9,11 @@ description = "AgentCore Runtime Application using Google ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
- "opentelemetry-distro",
13
- "opentelemetry-exporter-otlp",
14
- "google-adk >= 1.17.0",
12
+ "aws-opentelemetry-distro >= 0.17.0",
13
+ "google-adk >= 1.35.0, < 2.0.0",
14
+ "google-genai >= 1.0.0, < 2.0.0",
15
+ # 1.13.0 was yanked for broken imports.
16
+ "opentelemetry-resourcedetector-gcp >= 1.9.0a0, < 2.0.0, != 1.13.0",
15
17
  "bedrock-agentcore >= 1.0.3",
16
18
  "botocore[crt] >= 1.35.0",
17
19
  {{#if hasGateway}}{{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0",
@@ -13,6 +13,11 @@ file defines a Starlette ASGI app with the LangChain/LangGraph framework running
13
13
 
14
14
  `model/load.py` instantiates your chosen model provider.
15
15
 
16
+ ## Input Validation
17
+
18
+ Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
19
+ only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -24,7 +29,8 @@ file defines a Starlette ASGI app with the LangChain/LangGraph framework running
24
29
 
25
30
  If installation was successful, a virtual environment is already created with dependencies installed.
26
31
 
27
- Run `source .venv/bin/activate` before developing.
32
+ Activate the environment with `source .venv/bin/activate` on macOS/Linux, `.venv\Scripts\activate.bat` in Windows
33
+ Command Prompt, or `.\.venv\Scripts\activate.ps1` in Windows PowerShell.
28
34
 
29
35
  `agentcore dev` will start a local server on 0.0.0.0:8080.
30
36
 
@@ -1,7 +1,17 @@
1
+ {{#if needsOs}}
1
2
  import os
2
- from langchain_core.messages import HumanMessage
3
+ {{/if}}
4
+ from collections import OrderedDict
5
+ from typing import Any
6
+
7
+ from langchain_core.messages import HumanMessage{{#if hasConfigBundle}}, SystemMessage{{/if}}
8
+ from langgraph.checkpoint.memory import InMemorySaver
3
9
  from langgraph.prebuilt import create_react_agent
4
10
  from langchain.tools import tool
11
+ {{#if hasConfigBundle}}
12
+ from langchain_core.callbacks import BaseCallbackHandler
13
+ from bedrock_agentcore.runtime.context import BedrockAgentCoreContext
14
+ {{/if}}
5
15
  from opentelemetry.instrumentation.langchain import LangchainInstrumentor
6
16
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
7
17
  from model.load import load_model
@@ -25,6 +35,17 @@ def get_or_create_model():
25
35
  return _llm
26
36
 
27
37
 
38
+ DEFAULT_SYSTEM_PROMPT = """
39
+ You are a helpful assistant. Use tools when appropriate.
40
+ {{#if needsOs}}
41
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
42
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
43
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
44
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
45
+ {{/each}}{{/if}}
46
+ """
47
+
48
+
28
49
  # Define a simple function tool
29
50
  @tool
30
51
  def add_numbers(a: int, b: int) -> int:
@@ -35,19 +56,42 @@ def add_numbers(a: int, b: int) -> int:
35
56
  # Define a collection of tools used by the model
36
57
  tools = [add_numbers]
37
58
 
38
- {{#if sessionStorageMountPath}}
39
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
59
+ # Module-level checkpointer preserves conversation history across invocations.
60
+ # InMemorySaver keeps every thread_id (= session_id) checkpoint in memory
61
+ # forever, so we bound it to 128 active threads with LRU eviction (the
62
+ # least-recently-used thread is deleted and its history reset) to keep a
63
+ # long-running process from growing without limit. For durable history, swap in
64
+ # a persistent checkpointer (e.g. SqliteSaver/AsyncSqliteSaver with a file path).
65
+ _CHECKPOINT_LIMIT = 128
66
+ _checkpointer = InMemorySaver()
67
+ _thread_ids = OrderedDict()
68
+
69
+
70
+ def touch_thread(thread_id):
71
+ if thread_id in _thread_ids:
72
+ _thread_ids.move_to_end(thread_id)
73
+ return
74
+ while len(_thread_ids) >= _CHECKPOINT_LIMIT:
75
+ evicted, _ = _thread_ids.popitem(last=False)
76
+ _checkpointer.delete_thread(evicted)
77
+ _thread_ids[thread_id] = True
78
+
79
+ {{#if needsOs}}
80
+ _MOUNT_PATHS = [
81
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
82
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
83
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
84
+ ]
40
85
 
41
86
  def _safe_resolve(path: str) -> str:
42
- """Resolve path safely within the storage boundary."""
43
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
44
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
45
- raise ValueError(f"Path '{path}' is outside the storage boundary")
87
+ resolved = os.path.realpath(path)
88
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
89
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
46
90
  return resolved
47
91
 
48
92
  @tool
49
93
  def file_read(path: str) -> str:
50
- """Read a file from persistent storage. The path is relative to the storage root."""
94
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
51
95
  try:
52
96
  full_path = _safe_resolve(path)
53
97
  with open(full_path) as f:
@@ -59,7 +103,7 @@ def file_read(path: str) -> str:
59
103
 
60
104
  @tool
61
105
  def file_write(path: str, content: str) -> str:
62
- """Write content to a file in persistent storage. The path is relative to the storage root."""
106
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
63
107
  try:
64
108
  full_path = _safe_resolve(path)
65
109
  parent = os.path.dirname(full_path)
@@ -74,27 +118,42 @@ def file_write(path: str, content: str) -> str:
74
118
  return f"Error writing '{path}': {e.strerror}"
75
119
 
76
120
  @tool
77
- def list_files(directory: str = "") -> str:
78
- """List files in persistent storage. The directory is relative to the storage root."""
121
+ def list_files(path: str) -> str:
122
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
79
123
  try:
80
- target = _safe_resolve(directory)
81
- entries = os.listdir(target)
124
+ full_path = _safe_resolve(path)
125
+ entries = os.listdir(full_path)
82
126
  return "\n".join(entries) if entries else "(empty directory)"
83
127
  except ValueError as e:
84
128
  return str(e)
85
129
  except OSError as e:
86
- return f"Error listing '{directory}': {e.strerror}"
130
+ return f"Error listing '{path}': {e.strerror}"
87
131
 
88
132
  tools.extend([file_read, file_write, list_files])
89
133
  {{/if}}
90
134
 
91
- SYSTEM_PROMPT = """
92
- You are a helpful assistant. Use tools when appropriate.
93
- {{#if sessionStorageMountPath}}
94
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
95
- {{/if}}
96
- """
135
+ {{#if hasConfigBundle}}
136
+
137
+ class ConfigBundleCallback(BaseCallbackHandler):
138
+ """Injects config bundle values into LangGraph agent at runtime.
97
139
 
140
+ BedrockAgentCoreContext.get_config_bundle() fetches the component configuration
141
+ for the current runtime ARN from the config bundle service. The SDK caches the
142
+ result and refreshes on bundle version changes.
143
+ """
144
+
145
+ def on_chain_start(self, serialized: dict, inputs: dict, **kwargs: Any) -> None:
146
+ config = BedrockAgentCoreContext.get_config_bundle()
147
+ prompt = config.get("systemPrompt", DEFAULT_SYSTEM_PROMPT)
148
+
149
+ messages = inputs.get("messages", [])
150
+ if messages and isinstance(messages[0], SystemMessage):
151
+ messages[0] = SystemMessage(content=prompt)
152
+ else:
153
+ messages.insert(0, SystemMessage(content=prompt))
154
+ inputs["messages"] = messages
155
+
156
+ {{/if}}
98
157
 
99
158
  @app.entrypoint
100
159
  async def invoke(payload, context):
@@ -112,15 +171,51 @@ async def invoke(payload, context):
112
171
  if mcp_client:
113
172
  mcp_tools = await mcp_client.get_tools()
114
173
 
115
- # Define the agent using create_react_agent
116
- graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools, prompt=SYSTEM_PROMPT)
174
+ # Define the agent using create_react_agent (checkpointer is shared across invocations)
175
+ {{#if hasConfigBundle}}
176
+ graph = create_react_agent(
177
+ get_or_create_model(),
178
+ tools=mcp_tools + tools,
179
+ prompt=DEFAULT_SYSTEM_PROMPT,
180
+ checkpointer=_checkpointer,
181
+ )
182
+ callback = ConfigBundleCallback()
117
183
 
118
184
  # Process the user prompt
119
185
  prompt = payload.get("prompt", "What can you help me with?")
186
+ if not isinstance(prompt, str):
187
+ raise ValueError("prompt must be a string")
188
+ session_id = getattr(context, "session_id", "default-session")
189
+ touch_thread(session_id)
120
190
  log.info(f"Agent input: {prompt}")
121
191
 
122
- # Run the agent
123
- result = await graph.ainvoke({"messages": [HumanMessage(content=prompt)]})
192
+ # Run the agent with config bundle callback (checkpointer auto-loads/saves history per session)
193
+ result = await graph.ainvoke(
194
+ {"messages": [HumanMessage(content=prompt)]},
195
+ config={"callbacks": [callback], "configurable": {"thread_id": session_id}},
196
+ )
197
+ {{else}}
198
+ graph = create_react_agent(
199
+ get_or_create_model(),
200
+ tools=mcp_tools + tools,
201
+ prompt=DEFAULT_SYSTEM_PROMPT,
202
+ checkpointer=_checkpointer,
203
+ )
204
+
205
+ # Process the user prompt
206
+ prompt = payload.get("prompt", "What can you help me with?")
207
+ if not isinstance(prompt, str):
208
+ raise ValueError("prompt must be a string")
209
+ session_id = getattr(context, "session_id", "default-session")
210
+ touch_thread(session_id)
211
+ log.info(f"Agent input: {prompt}")
212
+
213
+ # Run the agent (checkpointer auto-loads/saves history per session)
214
+ result = await graph.ainvoke(
215
+ {"messages": [HumanMessage(content=prompt)]},
216
+ config={"configurable": {"thread_id": session_id}},
217
+ )
218
+ {{/if}}
124
219
 
125
220
  # Return result
126
221
  output = result["messages"][-1].content