@aws/nx-plugin 1.0.0-rc.60 → 1.0.0-rc.62

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 (75) hide show
  1. package/migrations.json +36 -26
  2. package/package.json +1 -1
  3. package/src/mcp-server/tools/general-guidance.js +1 -1
  4. package/src/mcp-server/tools/general-guidance.js.map +1 -1
  5. package/src/migrations/latest/py-agent-lifespan-construction/metadata.json +3 -0
  6. package/src/migrations/latest/py-agent-lifespan-construction/migration.js +412 -0
  7. package/src/migrations/latest/py-agent-lifespan-construction/migration.js.map +1 -0
  8. package/src/migrations/latest/rename-load-runtime-config-target/metadata.json +3 -0
  9. package/src/migrations/latest/rename-load-runtime-config-target/migration.js +33 -0
  10. package/src/migrations/latest/rename-load-runtime-config-target/migration.js.map +1 -0
  11. package/src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/migration.js.map +1 -0
  12. package/src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/migration.js.map +1 -0
  13. package/src/migrations/v1.0.0-rc.50/0003-terraform-bootstrap-adopt-existing-bucket/migration.d.ts +6 -0
  14. package/src/migrations/v1.0.0-rc.50/0003-terraform-bootstrap-adopt-existing-bucket/migration.js.map +1 -0
  15. package/src/migrations/v1.0.0-rc.52/0001-user-identity-waf-allow-localhost-callback/migration.js.map +1 -0
  16. package/src/migrations/v1.0.0-rc.54/0001-order-access-log-delivery-after-bucket-policy/migration.js.map +1 -0
  17. package/src/migrations/v1.0.0-rc.55/0001-backfill-generator-metadata/migration.js.map +1 -0
  18. package/src/migrations/v1.0.0-rc.56/0001-dynamodb-local-remove-optimize-flag/migration.js.map +1 -0
  19. package/src/migrations/v1.0.0-rc.57/0001-ts-agent-session-management-support/migration.d.ts +6 -0
  20. package/src/migrations/v1.0.0-rc.57/0001-ts-agent-session-management-support/migration.js.map +1 -0
  21. package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +2 -10
  22. package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +12 -8
  23. package/src/py/agent/files/{ag-ui-langchain → langchain/ag-ui}/main.py.template +12 -8
  24. package/src/py/agent/files/{http → langchain/http}/init.py.template +10 -0
  25. package/src/py/agent/files/{http-langchain → langchain/http}/main.py.template +1 -4
  26. package/src/py/agent/files/strands/ag-ui/main.py.template +89 -0
  27. package/src/py/agent/files/strands/http/init.py.template +97 -0
  28. package/src/py/agent/files/{http → strands/http}/main.py.template +2 -10
  29. package/src/py/agent/generator.js +9 -19
  30. package/src/py/agent/generator.js.map +1 -1
  31. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +6 -6
  32. package/src/ts/react-website/app/files/app/common/README.md.template +1 -1
  33. package/src/ts/react-website/app/generator.js +4 -4
  34. package/src/ts/react-website/app/generator.js.map +1 -1
  35. package/src/utils/migration-versions.d.ts +31 -2
  36. package/src/utils/migration-versions.js +74 -10
  37. package/src/utils/migration-versions.js.map +1 -1
  38. package/src/migrations/v1.0.0-rc.50/modernize-function-props-cast/migration.js.map +0 -1
  39. package/src/migrations/v1.0.0-rc.50/restrict-cors-to-custom-domains/migration.js.map +0 -1
  40. package/src/migrations/v1.0.0-rc.50/terraform-bootstrap-adopt-existing-bucket/migration.js.map +0 -1
  41. package/src/migrations/v1.0.0-rc.52/user-identity-waf-allow-localhost-callback/migration.js.map +0 -1
  42. package/src/migrations/v1.0.0-rc.54/order-access-log-delivery-after-bucket-policy/migration.js.map +0 -1
  43. package/src/migrations/v1.0.0-rc.55/backfill-generator-metadata/migration.js.map +0 -1
  44. package/src/migrations/v1.0.0-rc.56/dynamodb-local-remove-optimize-flag/migration.js.map +0 -1
  45. package/src/migrations/v1.0.0-rc.57/ts-agent-session-management-support/migration.js.map +0 -1
  46. package/src/py/agent/files/ag-ui/main.py.template +0 -37
  47. /package/src/migrations/{v1.0.0-rc.50/modernize-function-props-cast → latest/py-agent-lifespan-construction}/migration.d.ts +0 -0
  48. /package/src/migrations/{v1.0.0-rc.50/restrict-cors-to-custom-domains → latest/rename-load-runtime-config-target}/migration.d.ts +0 -0
  49. /package/src/migrations/v1.0.0-rc.50/{modernize-function-props-cast → 0001-modernize-function-props-cast}/metadata.json +0 -0
  50. /package/src/migrations/v1.0.0-rc.50/{terraform-bootstrap-adopt-existing-bucket → 0001-modernize-function-props-cast}/migration.d.ts +0 -0
  51. /package/src/migrations/v1.0.0-rc.50/{modernize-function-props-cast → 0001-modernize-function-props-cast}/migration.js +0 -0
  52. /package/src/migrations/v1.0.0-rc.50/{restrict-cors-to-custom-domains → 0002-restrict-cors-to-custom-domains}/metadata.json +0 -0
  53. /package/src/migrations/{v1.0.0-rc.57/ts-agent-session-management-support → v1.0.0-rc.50/0002-restrict-cors-to-custom-domains}/migration.d.ts +0 -0
  54. /package/src/migrations/v1.0.0-rc.50/{restrict-cors-to-custom-domains → 0002-restrict-cors-to-custom-domains}/migration.js +0 -0
  55. /package/src/migrations/v1.0.0-rc.50/{terraform-bootstrap-adopt-existing-bucket → 0003-terraform-bootstrap-adopt-existing-bucket}/metadata.json +0 -0
  56. /package/src/migrations/v1.0.0-rc.50/{terraform-bootstrap-adopt-existing-bucket → 0003-terraform-bootstrap-adopt-existing-bucket}/migration.js +0 -0
  57. /package/src/migrations/v1.0.0-rc.52/{user-identity-waf-allow-localhost-callback → 0001-user-identity-waf-allow-localhost-callback}/metadata.json +0 -0
  58. /package/src/migrations/v1.0.0-rc.52/{user-identity-waf-allow-localhost-callback → 0001-user-identity-waf-allow-localhost-callback}/migration.d.ts +0 -0
  59. /package/src/migrations/v1.0.0-rc.52/{user-identity-waf-allow-localhost-callback → 0001-user-identity-waf-allow-localhost-callback}/migration.js +0 -0
  60. /package/src/migrations/v1.0.0-rc.54/{order-access-log-delivery-after-bucket-policy → 0001-order-access-log-delivery-after-bucket-policy}/metadata.json +0 -0
  61. /package/src/migrations/v1.0.0-rc.54/{order-access-log-delivery-after-bucket-policy → 0001-order-access-log-delivery-after-bucket-policy}/migration.d.ts +0 -0
  62. /package/src/migrations/v1.0.0-rc.54/{order-access-log-delivery-after-bucket-policy → 0001-order-access-log-delivery-after-bucket-policy}/migration.js +0 -0
  63. /package/src/migrations/v1.0.0-rc.55/{backfill-generator-metadata → 0001-backfill-generator-metadata}/metadata.json +0 -0
  64. /package/src/migrations/v1.0.0-rc.55/{backfill-generator-metadata → 0001-backfill-generator-metadata}/migration.d.ts +0 -0
  65. /package/src/migrations/v1.0.0-rc.55/{backfill-generator-metadata → 0001-backfill-generator-metadata}/migration.js +0 -0
  66. /package/src/migrations/v1.0.0-rc.56/{dynamodb-local-remove-optimize-flag → 0001-dynamodb-local-remove-optimize-flag}/metadata.json +0 -0
  67. /package/src/migrations/v1.0.0-rc.56/{dynamodb-local-remove-optimize-flag → 0001-dynamodb-local-remove-optimize-flag}/migration.d.ts +0 -0
  68. /package/src/migrations/v1.0.0-rc.56/{dynamodb-local-remove-optimize-flag → 0001-dynamodb-local-remove-optimize-flag}/migration.js +0 -0
  69. /package/src/migrations/v1.0.0-rc.57/{ts-agent-session-management-support → 0001-ts-agent-session-management-support}/metadata.json +0 -0
  70. /package/src/migrations/v1.0.0-rc.57/{ts-agent-session-management-support → 0001-ts-agent-session-management-support}/migration.js +0 -0
  71. /package/src/py/agent/files/{a2a-langchain → langchain/a2a}/main.py.template +0 -0
  72. /package/src/py/agent/files/{common-langchain → langchain/common}/agent.py.template +0 -0
  73. /package/src/py/agent/files/{a2a → strands/a2a}/main.py.template +0 -0
  74. /package/src/py/agent/files/{common → strands/common}/__init__.py.template +0 -0
  75. /package/src/py/agent/files/{common → strands/common}/agent.py.template +0 -0
package/migrations.json CHANGED
@@ -7,75 +7,85 @@
7
7
  "description": "Add agent runtime target support to the vended AgentCoreGateway construct and an agentName member to vended agent constructs",
8
8
  "implementation": "./src/migrations/latest/gateway-agent-targets/migration"
9
9
  },
10
- "latest-gateway-cognito-passthrough": {
11
- "version": "1.0.0-rc.60",
12
- "description": "Front Cognito agents with an AgentCore Gateway via JWT passthrough: addAgent picks the target credential from the agent's auth, agent constructs gain an auth member, and Cognito runtimes allowlist the Authorization header",
13
- "implementation": "./src/migrations/latest/gateway-cognito-passthrough/migration"
14
- },
15
10
  "latest-smithy-ssdk-bundle-pins": {
16
11
  "version": "1.0.0-rc.58",
17
12
  "description": "Bump the rolldown and dts plugin pins in the Smithy build.Dockerfile so the bundled SSDK type declaration resolves",
18
13
  "implementation": "./src/migrations/latest/smithy-ssdk-bundle-pins/migration"
19
14
  },
20
- "latest-strip-pip-from-python-images": {
15
+ "latest-gateway-cognito-passthrough": {
21
16
  "version": "1.0.0-rc.60",
22
- "description": "Remove pip from vended Python agent / MCP server images so its vulnerable vendored packages are not present at runtime",
23
- "implementation": "./src/migrations/latest/strip-pip-from-python-images/migration"
17
+ "description": "Front Cognito agents with an AgentCore Gateway via JWT passthrough: addAgent picks the target credential from the agent's auth, agent constructs gain an auth member, and Cognito runtimes allowlist the Authorization header",
18
+ "implementation": "./src/migrations/latest/gateway-cognito-passthrough/migration"
24
19
  },
25
20
  "latest-waf-log-group-removal": {
26
21
  "version": "1.0.0-rc.60",
27
22
  "description": "Set RemovalPolicy.DESTROY on WAF log groups in the vended AgentCoreGateway and REST API constructs so a rolled-back deploy does not orphan them",
28
23
  "implementation": "./src/migrations/latest/waf-log-group-removal/migration"
29
24
  },
30
- "v1.0.0-rc.50-modernize-function-props-cast": {
25
+ "latest-strip-pip-from-python-images": {
26
+ "version": "1.0.0-rc.60",
27
+ "description": "Remove pip from vended Python agent / MCP server images so its vulnerable vendored packages are not present at runtime",
28
+ "implementation": "./src/migrations/latest/strip-pip-from-python-images/migration"
29
+ },
30
+ "latest-py-agent-lifespan-construction": {
31
+ "version": "1.0.0-rc.61",
32
+ "description": "Move py#agent HTTP/AG-UI agent construction out of module import time and into a FastAPI lifespan handler, stored on app.state",
33
+ "implementation": "./src/migrations/latest/py-agent-lifespan-construction/migration"
34
+ },
35
+ "latest-rename-load-runtime-config-target": {
36
+ "version": "1.0.0-rc.62",
37
+ "description": "Clone the react website 'load:runtime-config' target to a 'load-runtime-config' target so it can be run with the verb syntax (nx load-runtime-config <project>)",
38
+ "implementation": "./src/migrations/latest/rename-load-runtime-config-target/migration"
39
+ },
40
+ "v1.0.0-rc.50-0001-modernize-function-props-cast": {
31
41
  "version": "1.0.0-rc.50",
32
42
  "description": "Replace the legacy angle-bracket FunctionProps type assertion with the modern as syntax in generated API constructs",
33
- "implementation": "./src/migrations/v1.0.0-rc.50/modernize-function-props-cast/migration"
43
+ "implementation": "./src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/migration"
34
44
  },
35
- "v1.0.0-rc.50-restrict-cors-to-custom-domains": {
45
+ "v1.0.0-rc.50-0002-restrict-cors-to-custom-domains": {
36
46
  "version": "1.0.0-rc.50",
37
47
  "description": "Include CloudFront custom domain aliases in restrictCorsTo and UserIdentity callback URLs",
38
- "implementation": "./src/migrations/v1.0.0-rc.50/restrict-cors-to-custom-domains/migration"
48
+ "implementation": "./src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/migration"
39
49
  },
40
- "v1.0.0-rc.50-terraform-bootstrap-adopt-existing-bucket": {
50
+ "v1.0.0-rc.50-0003-terraform-bootstrap-adopt-existing-bucket": {
41
51
  "version": "1.0.0-rc.50",
42
52
  "description": "Adopt an existing Terraform state bucket in the vended bootstrap script when its state object is missing",
43
- "implementation": "./src/migrations/v1.0.0-rc.50/terraform-bootstrap-adopt-existing-bucket/migration"
53
+ "implementation": "./src/migrations/v1.0.0-rc.50/0003-terraform-bootstrap-adopt-existing-bucket/migration"
44
54
  },
45
- "v1.0.0-rc.52-user-identity-waf-allow-localhost-callback": {
55
+ "v1.0.0-rc.52-0001-user-identity-waf-allow-localhost-callback": {
46
56
  "version": "1.0.0-rc.52",
47
57
  "description": "Count the EC2MetaDataSSRF_QUERYARGUMENTS WAF rule on the UserIdentity Web ACL so sign-in from a local dev server is not blocked",
48
- "implementation": "./src/migrations/v1.0.0-rc.52/user-identity-waf-allow-localhost-callback/migration"
58
+ "implementation": "./src/migrations/v1.0.0-rc.52/0001-user-identity-waf-allow-localhost-callback/migration"
49
59
  },
50
- "v1.0.0-rc.54-order-access-log-delivery-after-bucket-policy": {
60
+ "v1.0.0-rc.54-0001-order-access-log-delivery-after-bucket-policy": {
51
61
  "version": "1.0.0-rc.54",
52
62
  "description": "Order the S3 server access log delivery source after the bucket policy to avoid a 409 from concurrent bucket configuration writes",
53
- "implementation": "./src/migrations/v1.0.0-rc.54/order-access-log-delivery-after-bucket-policy/migration"
63
+ "implementation": "./src/migrations/v1.0.0-rc.54/0001-order-access-log-delivery-after-bucket-policy/migration"
54
64
  },
55
- "v1.0.0-rc.55-backfill-generator-metadata": {
65
+ "v1.0.0-rc.55-0001-backfill-generator-metadata": {
56
66
  "version": "1.0.0-rc.55",
57
67
  "description": "Backfill the project metadata the version sync reads, recovered from the files the generators left behind",
58
- "implementation": "./src/migrations/v1.0.0-rc.55/backfill-generator-metadata/migration"
68
+ "implementation": "./src/migrations/v1.0.0-rc.55/0001-backfill-generator-metadata/migration"
59
69
  },
60
- "v1.0.0-rc.56-dynamodb-local-remove-optimize-flag": {
70
+ "v1.0.0-rc.56-0001-dynamodb-local-remove-optimize-flag": {
61
71
  "version": "1.0.0-rc.56",
62
72
  "description": "Drop -optimizeDbBeforeStartup from the vended DynamoDB Local container script, which can corrupt shared-local-instance.db",
63
- "implementation": "./src/migrations/v1.0.0-rc.56/dynamodb-local-remove-optimize-flag/migration"
73
+ "implementation": "./src/migrations/v1.0.0-rc.56/0001-dynamodb-local-remove-optimize-flag/migration"
64
74
  },
65
- "v1.0.0-rc.57-ts-agent-session-management-support": {
75
+ "v1.0.0-rc.57-0001-ts-agent-session-management-support": {
66
76
  "version": "1.0.0-rc.57",
67
77
  "description": "Add session management support to ts#agent",
68
- "implementation": "./src/migrations/v1.0.0-rc.57/ts-agent-session-management-support/migration"
78
+ "implementation": "./src/migrations/v1.0.0-rc.57/0001-ts-agent-session-management-support/migration"
69
79
  },
70
80
  "sync-vended-versions": {
71
- "version": "1.0.0-rc.60",
81
+ "version": "1.0.0-rc.62",
72
82
  "description": "Sync vended dependency versions and the tracked plugin version to those vended by this release",
73
83
  "implementation": "./src/utils/version-upgrade-migration/migration"
74
84
  }
75
85
  },
76
86
  "packageJsonUpdates": {
77
87
  "nx-23.1.1-nx-packages": {
78
- "version": "1.0.0-rc.60",
88
+ "version": "1.0.0-rc.62",
79
89
  "packages": {
80
90
  "nx": {
81
91
  "version": "23.1.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws/nx-plugin",
3
- "version": "1.0.0-rc.60",
3
+ "version": "1.0.0-rc.62",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/awslabs/nx-plugin-for-aws.git",
@@ -64,7 +64,7 @@ ${PACKAGE_MANAGERS.map((pm)=>buildNxCommand('<options>', pm)).join(' - \n')}
64
64
  - Carefully examine the files that have been generated and always refer back to the generator guide when working in a generated project
65
65
  - Generate all projects into the \`packages/\` directory
66
66
  - After making changes to your projects, fix linting issues, then run a full build
67
- - When it's time to start testing a project, suggest to the user that infrastructure is deployed to AWS. For websites, if a runtime-config.json is needed, use the load:runtime-config target after a deployment to point a local website at a sandbox stack.
67
+ - When it's time to start testing a project, suggest to the user that infrastructure is deployed to AWS. For websites, if a runtime-config.json is needed, use the load-runtime-config target after a deployment to point a local website at a sandbox stack.
68
68
 
69
69
  ## Batching Generators
70
70
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../packages/nx-plugin/src/mcp-server/tools/general-guidance.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { NxGeneratorInfo } from '../../utils/generators';\nimport { IAC_PROVIDERS } from '../../utils/iac-providers';\nimport { buildNxCommand, fetchGuidePages } from '../generator-info';\nimport { PACKAGE_MANAGERS } from '../schema';\n\nexport const TOOL_SELECTION_GUIDE = `## Tool Selection Guide\n\n- Use the \\`general-guidance\\` tool for guidance and best practices for working with Nx and the Nx Plugin for AWS.\n- Use the \\`create-workspace-command\\` tool to discover how to create a workspace to start a new project.\n- Use the \\`add-to-existing-project\\` tool when adding the plugin to an existing Nx workspace or non-Nx monorepo, or when troubleshooting configuration errors in such a workspace.\n- Use the \\`upgrade-workspace\\` tool when upgrading Nx or the Nx Plugin for AWS to a newer version.\n- Use the \\`list-generators\\` tool to discover the available generators and how to run them.\n- Use the \\`generator-guide\\` tool to retrieve detailed information about a specific generator.`;\n\n/**\n * Add a tool which provides general guidance for using Nx and the Nx Plugin for AWS\n */\nexport const addGeneralGuidanceTool = (\n server: McpServer,\n generators: NxGeneratorInfo[],\n) => {\n server.registerTool(\n 'general-guidance',\n {\n title: 'General Guidance',\n description:\n 'Tool for guidance and best practices for working with Nx and the Nx Plugin for AWS',\n },\n async () => ({\n content: [\n {\n type: 'text' as const,\n text: `# Nx Plugin for AWS Guidance\n\n${TOOL_SELECTION_GUIDE}\n\n## Getting Started\n\n- Choose a package manager first. You can choose between ${PACKAGE_MANAGERS.join(', ')}. It's recommended to use \"pnpm\" if the user has no preference\n- Choose an infrastructure as code (IaC) provider next. You can choose between ${IAC_PROVIDERS.join(', ')}. It's recommended to use CDK if the user has no preference\n- Next, you must create an Nx workspace. Use the \\`create-workspace-command\\` tool for more details, and provide it with your chosen package manager\n- After this, you can start scaffolding the main components of your application using generators. Use the \\`list-generators\\` tool to discover available generators, and the \\`generator-guide\\` tool for more detailed information about a specific generator\n\n## Nx Primer\n\n- Prefix nx commands with the appropriate prefix for your package manager, for example:\n${PACKAGE_MANAGERS.map((pm) => buildNxCommand('<options>', pm)).join(' - \\n')}\n- Each project in your workspace has a file named \\`project.json\\` which contains important project information such as its name, and defines the \"targets\" which can be run for that project, for example building or testing the project\n- Use the command \\`nx reset\\` to reset the Nx daemon when unexpected issues arise\n- After adding dependencies between TypeScript projects, use \\`nx sync\\` to ensure project references are set up correctly\n\n## General Instructions\n\n- Each project declares the third-party runtime dependencies its source imports in its own \\`package.json\\`; shared build/test tooling (test runners, bundlers, etc.) is declared in the root \\`package.json\\` \\`devDependencies\\`. Versions follow a single version policy, recorded in the package manager's dependency catalog where supported (referenced as \\`catalog:\\`). Install with the appropriate command for your package manager:\n - Add a project runtime dependency: \\`pnpm add <package> --filter <project>\\` / \\`yarn workspace <project> add <package>\\` / \\`npm install <package> -w <project>\\` / \\`bun add <package> --cwd <project-directory>\\`\n - Add a shared dev tool at the root: \\`pnpm add -Dw <package>\\` / \\`yarn add -D <package>\\` / \\`npm install --legacy-peer-deps -D <package>\\` / \\`bun install -D <package>\\`\n- The \\`noUndeclaredDependencies\\` lint rule enforces that a project's source only imports packages declared in its own \\`package.json\\`\n- Projects in the same workspace can import one another directly by package name (eg \\`@workspace-name/project-name\\`) without declaring a dependency in \\`package.json\\` — TypeScript path aliases in \\`tsconfig.base.json\\` resolve these imports\n- When specifying project names as arguments to generators, prefer the _fully qualified_ project name, for example \\`@workspace-name/project-name\\`. Check the \\`project.json\\` file for the specific package to find its fully qualified name\n- When no generator exists for a specific framework required, use the base \\`ts#project\\` and \\`py#project\\` generators and build on top.\n- Leave the \\`--infra\\` option at its default value unless the user has explicitly instructed otherwise. Generators choose a sensible default type of infrastructure to deploy the project with, so only override \\`--infra\\` when the user has specified a particular requirement.\n\n## Useful Commands\n\n- Fix lint issues with \\`nx run-many --target lint --configuration=fix --all --output-style=stream\\`\n- Build all projects with \\`nx run-many --target build --all --output-style=stream\\`\n- Prefer importing the CDK constructs vended by generators in \\`packages/common/constructs\\` over writing your own\n\n## Best Practices\n\n- After running a generator, use the \\`nx show projects\\` command to check which projects have been added (if any)\n- Carefully examine the files that have been generated and always refer back to the generator guide when working in a generated project\n- Generate all projects into the \\`packages/\\` directory\n- After making changes to your projects, fix linting issues, then run a full build\n- When it's time to start testing a project, suggest to the user that infrastructure is deployed to AWS. For websites, if a runtime-config.json is needed, use the load:runtime-config target after a deployment to point a local website at a sandbox stack.\n\n## Batching Generators\n\nWhen scaffolding several projects in one go, chain generators to avoid a slow dependency install after every generator:\n\n- **Chain generators** with \\`&&\\` in a single command. Pass \\`--prefer-install-dependencies=false\\` on each generator except the last so dependencies install once at the end, for example:\n\n${PACKAGE_MANAGERS.map(\n (pm) => ` \\`\\`\\`bash\n ${buildNxCommand('g @aws/nx-plugin:ts#trpc-api --no-interactive --name=my-app-api --auth=IAM --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:ts#react-website --no-interactive --name=my-app-website --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:connection --no-interactive --sourceProject=@my-app/my-app-website --targetProject=@my-app/my-app-api --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:ts#infra --no-interactive --name=infra', pm)} && \\\\\n ${buildNxCommand('sync', pm)}\n \\`\\`\\``,\n).join('\\n')}\n\n- **\\`--prefer-install-dependencies=false\\`** asks a generator to defer its dependency install so the batch installs once at the end (the final generator above omits the flag and installs everything).\n- **\\`nx sync\\`** is required before building — generators modify TypeScript project references.\n\n## Detailed Guides\n\nPlease refer to the below documentation for important details regarding workspaces and working with TypeScript or Python projects.\n\n${await fetchGuidePages(['workspace', 'typescript-project', 'python-project'], generators)}\n\n `,\n },\n ],\n }),\n );\n};\n"],"names":["IAC_PROVIDERS","buildNxCommand","fetchGuidePages","PACKAGE_MANAGERS","TOOL_SELECTION_GUIDE","addGeneralGuidanceTool","server","generators","registerTool","title","description","content","type","text","join","map","pm"],"mappings":"AAAA;;;CAGC,GAGD,SAASA,aAAa,QAAQ,+BAA4B;AAC1D,SAASC,cAAc,EAAEC,eAAe,QAAQ,uBAAoB;AACpE,SAASC,gBAAgB,QAAQ,eAAY;AAE7C,OAAO,MAAMC,uBAAuB,CAAC;;;;;;;+FAO0D,CAAC,CAAC;AAEjG;;CAEC,GACD,OAAO,MAAMC,yBAAyB,CACpCC,QACAC;IAEAD,OAAOE,YAAY,CACjB,oBACA;QACEC,OAAO;QACPC,aACE;IACJ,GACA,UAAa,CAAA;YACXC,SAAS;gBACP;oBACEC,MAAM;oBACNC,MAAM,CAAC;;AAEjB,EAAET,qBAAqB;;;;yDAIkC,EAAED,iBAAiBW,IAAI,CAAC,MAAM;+EACR,EAAEd,cAAcc,IAAI,CAAC,MAAM;;;;;;;AAO1G,EAAEX,iBAAiBY,GAAG,CAAC,CAACC,KAAOf,eAAe,aAAae,KAAKF,IAAI,CAAC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoC9E,EAAEX,iBAAiBY,GAAG,CACpB,CAACC,KAAO,CAAC;EACT,EAAEf,eAAe,kHAAkHe,IAAI;IACrI,EAAEf,eAAe,gHAAgHe,IAAI;IACrI,EAAEf,eAAe,8JAA8Je,IAAI;IACnL,EAAEf,eAAe,2DAA2De,IAAI;IAChF,EAAEf,eAAe,QAAQe,IAAI;QACzB,CAAC,EACPF,IAAI,CAAC,MAAM;;;;;;;;;AASb,EAAE,MAAMZ,gBAAgB;wBAAC;wBAAa;wBAAsB;qBAAiB,EAAEK,YAAY;;IAEvF,CAAC;gBACG;aACD;QACH,CAAA;AAEJ,EAAE"}
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/mcp-server/tools/general-guidance.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { NxGeneratorInfo } from '../../utils/generators';\nimport { IAC_PROVIDERS } from '../../utils/iac-providers';\nimport { buildNxCommand, fetchGuidePages } from '../generator-info';\nimport { PACKAGE_MANAGERS } from '../schema';\n\nexport const TOOL_SELECTION_GUIDE = `## Tool Selection Guide\n\n- Use the \\`general-guidance\\` tool for guidance and best practices for working with Nx and the Nx Plugin for AWS.\n- Use the \\`create-workspace-command\\` tool to discover how to create a workspace to start a new project.\n- Use the \\`add-to-existing-project\\` tool when adding the plugin to an existing Nx workspace or non-Nx monorepo, or when troubleshooting configuration errors in such a workspace.\n- Use the \\`upgrade-workspace\\` tool when upgrading Nx or the Nx Plugin for AWS to a newer version.\n- Use the \\`list-generators\\` tool to discover the available generators and how to run them.\n- Use the \\`generator-guide\\` tool to retrieve detailed information about a specific generator.`;\n\n/**\n * Add a tool which provides general guidance for using Nx and the Nx Plugin for AWS\n */\nexport const addGeneralGuidanceTool = (\n server: McpServer,\n generators: NxGeneratorInfo[],\n) => {\n server.registerTool(\n 'general-guidance',\n {\n title: 'General Guidance',\n description:\n 'Tool for guidance and best practices for working with Nx and the Nx Plugin for AWS',\n },\n async () => ({\n content: [\n {\n type: 'text' as const,\n text: `# Nx Plugin for AWS Guidance\n\n${TOOL_SELECTION_GUIDE}\n\n## Getting Started\n\n- Choose a package manager first. You can choose between ${PACKAGE_MANAGERS.join(', ')}. It's recommended to use \"pnpm\" if the user has no preference\n- Choose an infrastructure as code (IaC) provider next. You can choose between ${IAC_PROVIDERS.join(', ')}. It's recommended to use CDK if the user has no preference\n- Next, you must create an Nx workspace. Use the \\`create-workspace-command\\` tool for more details, and provide it with your chosen package manager\n- After this, you can start scaffolding the main components of your application using generators. Use the \\`list-generators\\` tool to discover available generators, and the \\`generator-guide\\` tool for more detailed information about a specific generator\n\n## Nx Primer\n\n- Prefix nx commands with the appropriate prefix for your package manager, for example:\n${PACKAGE_MANAGERS.map((pm) => buildNxCommand('<options>', pm)).join(' - \\n')}\n- Each project in your workspace has a file named \\`project.json\\` which contains important project information such as its name, and defines the \"targets\" which can be run for that project, for example building or testing the project\n- Use the command \\`nx reset\\` to reset the Nx daemon when unexpected issues arise\n- After adding dependencies between TypeScript projects, use \\`nx sync\\` to ensure project references are set up correctly\n\n## General Instructions\n\n- Each project declares the third-party runtime dependencies its source imports in its own \\`package.json\\`; shared build/test tooling (test runners, bundlers, etc.) is declared in the root \\`package.json\\` \\`devDependencies\\`. Versions follow a single version policy, recorded in the package manager's dependency catalog where supported (referenced as \\`catalog:\\`). Install with the appropriate command for your package manager:\n - Add a project runtime dependency: \\`pnpm add <package> --filter <project>\\` / \\`yarn workspace <project> add <package>\\` / \\`npm install <package> -w <project>\\` / \\`bun add <package> --cwd <project-directory>\\`\n - Add a shared dev tool at the root: \\`pnpm add -Dw <package>\\` / \\`yarn add -D <package>\\` / \\`npm install --legacy-peer-deps -D <package>\\` / \\`bun install -D <package>\\`\n- The \\`noUndeclaredDependencies\\` lint rule enforces that a project's source only imports packages declared in its own \\`package.json\\`\n- Projects in the same workspace can import one another directly by package name (eg \\`@workspace-name/project-name\\`) without declaring a dependency in \\`package.json\\` — TypeScript path aliases in \\`tsconfig.base.json\\` resolve these imports\n- When specifying project names as arguments to generators, prefer the _fully qualified_ project name, for example \\`@workspace-name/project-name\\`. Check the \\`project.json\\` file for the specific package to find its fully qualified name\n- When no generator exists for a specific framework required, use the base \\`ts#project\\` and \\`py#project\\` generators and build on top.\n- Leave the \\`--infra\\` option at its default value unless the user has explicitly instructed otherwise. Generators choose a sensible default type of infrastructure to deploy the project with, so only override \\`--infra\\` when the user has specified a particular requirement.\n\n## Useful Commands\n\n- Fix lint issues with \\`nx run-many --target lint --configuration=fix --all --output-style=stream\\`\n- Build all projects with \\`nx run-many --target build --all --output-style=stream\\`\n- Prefer importing the CDK constructs vended by generators in \\`packages/common/constructs\\` over writing your own\n\n## Best Practices\n\n- After running a generator, use the \\`nx show projects\\` command to check which projects have been added (if any)\n- Carefully examine the files that have been generated and always refer back to the generator guide when working in a generated project\n- Generate all projects into the \\`packages/\\` directory\n- After making changes to your projects, fix linting issues, then run a full build\n- When it's time to start testing a project, suggest to the user that infrastructure is deployed to AWS. For websites, if a runtime-config.json is needed, use the load-runtime-config target after a deployment to point a local website at a sandbox stack.\n\n## Batching Generators\n\nWhen scaffolding several projects in one go, chain generators to avoid a slow dependency install after every generator:\n\n- **Chain generators** with \\`&&\\` in a single command. Pass \\`--prefer-install-dependencies=false\\` on each generator except the last so dependencies install once at the end, for example:\n\n${PACKAGE_MANAGERS.map(\n (pm) => ` \\`\\`\\`bash\n ${buildNxCommand('g @aws/nx-plugin:ts#trpc-api --no-interactive --name=my-app-api --auth=IAM --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:ts#react-website --no-interactive --name=my-app-website --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:connection --no-interactive --sourceProject=@my-app/my-app-website --targetProject=@my-app/my-app-api --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:ts#infra --no-interactive --name=infra', pm)} && \\\\\n ${buildNxCommand('sync', pm)}\n \\`\\`\\``,\n).join('\\n')}\n\n- **\\`--prefer-install-dependencies=false\\`** asks a generator to defer its dependency install so the batch installs once at the end (the final generator above omits the flag and installs everything).\n- **\\`nx sync\\`** is required before building — generators modify TypeScript project references.\n\n## Detailed Guides\n\nPlease refer to the below documentation for important details regarding workspaces and working with TypeScript or Python projects.\n\n${await fetchGuidePages(['workspace', 'typescript-project', 'python-project'], generators)}\n\n `,\n },\n ],\n }),\n );\n};\n"],"names":["IAC_PROVIDERS","buildNxCommand","fetchGuidePages","PACKAGE_MANAGERS","TOOL_SELECTION_GUIDE","addGeneralGuidanceTool","server","generators","registerTool","title","description","content","type","text","join","map","pm"],"mappings":"AAAA;;;CAGC,GAGD,SAASA,aAAa,QAAQ,+BAA4B;AAC1D,SAASC,cAAc,EAAEC,eAAe,QAAQ,uBAAoB;AACpE,SAASC,gBAAgB,QAAQ,eAAY;AAE7C,OAAO,MAAMC,uBAAuB,CAAC;;;;;;;+FAO0D,CAAC,CAAC;AAEjG;;CAEC,GACD,OAAO,MAAMC,yBAAyB,CACpCC,QACAC;IAEAD,OAAOE,YAAY,CACjB,oBACA;QACEC,OAAO;QACPC,aACE;IACJ,GACA,UAAa,CAAA;YACXC,SAAS;gBACP;oBACEC,MAAM;oBACNC,MAAM,CAAC;;AAEjB,EAAET,qBAAqB;;;;yDAIkC,EAAED,iBAAiBW,IAAI,CAAC,MAAM;+EACR,EAAEd,cAAcc,IAAI,CAAC,MAAM;;;;;;;AAO1G,EAAEX,iBAAiBY,GAAG,CAAC,CAACC,KAAOf,eAAe,aAAae,KAAKF,IAAI,CAAC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoC9E,EAAEX,iBAAiBY,GAAG,CACpB,CAACC,KAAO,CAAC;EACT,EAAEf,eAAe,kHAAkHe,IAAI;IACrI,EAAEf,eAAe,gHAAgHe,IAAI;IACrI,EAAEf,eAAe,8JAA8Je,IAAI;IACnL,EAAEf,eAAe,2DAA2De,IAAI;IAChF,EAAEf,eAAe,QAAQe,IAAI;QACzB,CAAC,EACPF,IAAI,CAAC,MAAM;;;;;;;;;AASb,EAAE,MAAMZ,gBAAgB;wBAAC;wBAAa;wBAAsB;qBAAiB,EAAEK,YAAY;;IAEvF,CAAC;gBACG;aACD;QACH,CAAA;AAEJ,EAAE"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "Move py#agent HTTP/AG-UI agent construction out of module import time and into a FastAPI lifespan handler, stored on app.state"
3
+ }
@@ -0,0 +1,412 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */ import { getProjects, joinPathFragments } from "@nx/devkit";
5
+ import { PY_AGENT_GENERATOR_INFO } from "../../../py/agent/generator.js";
6
+ import { addPythonDestructuredImport, applyGritQL, captureGritQLVariable, matchGritQL } from "../../../utils/ast.js";
7
+ import { formatFilesInSubtree } from "../../../utils/format.js";
8
+ /**
9
+ * Move py#agent HTTP/AG-UI agent construction out of module import time and
10
+ * into a FastAPI `lifespan` handler, stored on `app.state`.
11
+ *
12
+ * Building the agent eagerly at import time breaks any codepath that imports
13
+ * the module before `RUNTIME_CONFIG_APP_ID`/AgentCore env vars are set (e.g.
14
+ * OpenAPI spec generation, tests) - and, for AgentCore, construction belongs
15
+ * at container/app startup rather than import, since AgentCore hands each
16
+ * session its own container. The A2A protocol is unaffected: `with_session_id`
17
+ * only touches the agent factory lazily on first use, so its eager
18
+ * `__enter__()` was never actually eager in practice.
19
+ *
20
+ * How to write a migration:
21
+ * - https://nx.dev/docs/kb/migration-generators
22
+ * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject
23
+ *
24
+ * Guardrails:
25
+ * - Pattern-match before writing: every rewrite this migration makes to a file
26
+ * is preceded by a read-only `matchGritQL` check of every pattern it will
27
+ * need - so a diverged file is reported via `nextSteps` and left completely
28
+ * untouched, rather than partially rewritten into a broken state.
29
+ * - Idempotent: re-running must be a no-op (each rewrite's own shape change
30
+ * means its pattern no longer matches on a second pass).
31
+ * - Format what you write: finish with `formatFilesInSubtree`.
32
+ */ /** A read-only GritQL match against a python file (no rewrite). */ const pyMatch = (snippet)=>`language python\n\`${snippet}\``;
33
+ /** A GritQL rewrite deleting the matched python snippet entirely. */ const pyDelete = (snippet)=>`${pyMatch(snippet)} => .`;
34
+ /**
35
+ * A GritQL rewrite of one python snippet into another. Multi-line
36
+ * replacements go through `raw` so GritQL's own reformatting doesn't fight
37
+ * the exact shape being written (formatFilesInSubtree normalizes it after).
38
+ */ const pyRewrite = (from, to, where)=>{
39
+ const replacement = to.includes('\n') ? `raw\`${to}\`` : `\`${to}\``;
40
+ return `${pyMatch(from)} => ${replacement}${where ? ` where { ${where} }` : ''}`;
41
+ };
42
+ /** All checks must match before anything is written for that agent. */ const allMatch = async (tree, checks)=>{
43
+ for (const [filePath, pattern] of checks){
44
+ if (!await matchGritQL(tree, filePath, pattern)) return false;
45
+ }
46
+ return true;
47
+ };
48
+ const AGENT_CONNECTION_MODULE_PATTERN = (names)=>pyMatch(`from $mod import ${names}`);
49
+ const STRANDS_HTTP_MARKER = '_agent_ctx = with_session_id(';
50
+ const LANGCHAIN_HTTP_MARKER = '_graph = get_agent()';
51
+ const STRANDS_AGUI_MARKER = 'create_strands_app';
52
+ const LANGCHAIN_AGUI_MARKER = '_graph = get_agent()';
53
+ // Shared by strands/http and langchain/http - only the lifespan body differs.
54
+ const HTTP_INIT_APP_OLD = `app = FastAPI(
55
+ title="$name",
56
+ responses={500: {"model": InternalServerErrorDetails}},
57
+ generate_unique_id_function=lambda route: route.name,
58
+ )`;
59
+ // --- Strands HTTP -----------------------------------------------------------
60
+ const STRANDS_HTTP_MAIN_CTX_OLD = `_agent_ctx = with_session_id(
61
+ get_agent,
62
+ name="$name",
63
+ description="A Strands Agent exposed via HTTP streaming.",
64
+ )`;
65
+ const STRANDS_HTTP_INIT_APP_NEW = `@asynccontextmanager
66
+ async def lifespan(app: FastAPI):
67
+ with with_session_id(
68
+ get_agent,
69
+ name="$name",
70
+ description="A Strands Agent exposed via HTTP streaming.",
71
+ ) as agent:
72
+ app.state.agent = agent
73
+ yield
74
+
75
+
76
+ app = FastAPI(
77
+ title="$name",
78
+ responses={500: {"model": InternalServerErrorDetails}},
79
+ generate_unique_id_function=lambda route: route.name,
80
+ lifespan=lifespan,
81
+ )`;
82
+ const migrateStrandsHttpAgent = async (tree, dir, nextSteps)=>{
83
+ const initPath = joinPathFragments(dir, 'init.py');
84
+ const mainPath = joinPathFragments(dir, 'main.py');
85
+ if (!tree.exists(initPath) || !tree.exists(mainPath)) return;
86
+ const mainContents = tree.read(mainPath, 'utf-8') ?? '';
87
+ if (!mainContents.includes(STRANDS_HTTP_MARKER)) return;
88
+ const mod = await captureGritQLVariable(tree, mainPath, AGENT_CONNECTION_MODULE_PATTERN('session_id_context, with_session_id'), 'mod');
89
+ const ready = !!mod && await allMatch(tree, [
90
+ [
91
+ mainPath,
92
+ pyMatch(STRANDS_HTTP_MAIN_CTX_OLD)
93
+ ],
94
+ [
95
+ mainPath,
96
+ pyMatch('_agent = _agent_ctx.__enter__()')
97
+ ],
98
+ [
99
+ mainPath,
100
+ pyMatch('from .agent import get_agent')
101
+ ],
102
+ [
103
+ mainPath,
104
+ pyMatch('_agent.stream_async($prompt)')
105
+ ],
106
+ [
107
+ initPath,
108
+ pyMatch(HTTP_INIT_APP_OLD)
109
+ ]
110
+ ]);
111
+ if (!ready) {
112
+ nextSteps.push(`${mainPath}: diverged from the generated shape - left untouched, along with ${initPath}. Manually move agent construction into an \`init.py\` \`lifespan\` handler storing the agent on \`app.state.agent\` (see the py#agent generator's strands/http template).`);
113
+ return;
114
+ }
115
+ await addPythonDestructuredImport(tree, initPath, [
116
+ 'asynccontextmanager'
117
+ ], 'contextlib');
118
+ await addPythonDestructuredImport(tree, initPath, [
119
+ 'get_agent'
120
+ ], '.agent');
121
+ await addPythonDestructuredImport(tree, initPath, [
122
+ 'with_session_id'
123
+ ], mod);
124
+ await applyGritQL(tree, initPath, pyRewrite(HTTP_INIT_APP_OLD, STRANDS_HTTP_INIT_APP_NEW));
125
+ await applyGritQL(tree, mainPath, pyRewrite('from $mod import session_id_context, with_session_id', 'from $mod import session_id_context'));
126
+ await applyGritQL(tree, mainPath, pyDelete('from .agent import get_agent'));
127
+ await applyGritQL(tree, mainPath, pyDelete(STRANDS_HTTP_MAIN_CTX_OLD));
128
+ await applyGritQL(tree, mainPath, pyDelete('_agent = _agent_ctx.__enter__()'));
129
+ await applyGritQL(tree, mainPath, pyRewrite('_agent.stream_async($prompt)', 'app.state.agent.stream_async($prompt)'));
130
+ };
131
+ // --- LangChain HTTP ----------------------------------------------------------
132
+ const LANGCHAIN_HTTP_INIT_APP_NEW = `@asynccontextmanager
133
+ async def lifespan(app: FastAPI):
134
+ app.state.graph = get_agent()
135
+ yield
136
+
137
+
138
+ app = FastAPI(
139
+ title="$name",
140
+ responses={500: {"model": InternalServerErrorDetails}},
141
+ generate_unique_id_function=lambda route: route.name,
142
+ lifespan=lifespan,
143
+ )`;
144
+ const migrateLangchainHttpAgent = async (tree, dir, nextSteps)=>{
145
+ const initPath = joinPathFragments(dir, 'init.py');
146
+ const mainPath = joinPathFragments(dir, 'main.py');
147
+ if (!tree.exists(initPath) || !tree.exists(mainPath)) return;
148
+ const mainContents = tree.read(mainPath, 'utf-8') ?? '';
149
+ if (!mainContents.includes(LANGCHAIN_HTTP_MARKER)) return;
150
+ const ready = await allMatch(tree, [
151
+ [
152
+ mainPath,
153
+ pyMatch('_graph = get_agent()')
154
+ ],
155
+ [
156
+ mainPath,
157
+ pyMatch('from .agent import get_agent')
158
+ ],
159
+ [
160
+ mainPath,
161
+ pyMatch('_graph.astream($args)')
162
+ ],
163
+ [
164
+ initPath,
165
+ pyMatch(HTTP_INIT_APP_OLD)
166
+ ]
167
+ ]);
168
+ if (!ready) {
169
+ nextSteps.push(`${mainPath}: diverged from the generated shape - left untouched, along with ${initPath}. Manually wrap \`get_agent()\` in an \`init.py\` \`lifespan\` handler storing the graph on \`app.state.graph\` (see the py#agent generator's langchain/http template).`);
170
+ return;
171
+ }
172
+ await addPythonDestructuredImport(tree, initPath, [
173
+ 'asynccontextmanager'
174
+ ], 'contextlib');
175
+ await addPythonDestructuredImport(tree, initPath, [
176
+ 'get_agent'
177
+ ], '.agent');
178
+ await applyGritQL(tree, initPath, pyRewrite(HTTP_INIT_APP_OLD, LANGCHAIN_HTTP_INIT_APP_NEW));
179
+ await applyGritQL(tree, mainPath, pyDelete('from .agent import get_agent'));
180
+ await applyGritQL(tree, mainPath, pyDelete('_graph = get_agent()'));
181
+ await applyGritQL(tree, mainPath, pyRewrite('_graph.astream($args)', 'app.state.graph.astream($args)'));
182
+ };
183
+ // --- LangChain AG-UI ---------------------------------------------------------
184
+ const LANGCHAIN_AGUI_AGENT_OLD = `agui_agent = LangGraphAgent(
185
+ name="$name",
186
+ graph=_graph,
187
+ description="A LangChain/LangGraph Agent exposed via the AG-UI protocol.",
188
+ )`;
189
+ const LANGCHAIN_AGUI_AGENT_NEW = `@asynccontextmanager
190
+ async def lifespan(app: FastAPI):
191
+ graph = get_agent()
192
+ app.state.agui_agent = LangGraphAgent(
193
+ name="$name",
194
+ graph=graph,
195
+ description="A LangChain/LangGraph Agent exposed via the AG-UI protocol.",
196
+ )
197
+ yield`;
198
+ const migrateLangchainAgUiAgent = async (tree, dir, nextSteps)=>{
199
+ const mainPath = joinPathFragments(dir, 'main.py');
200
+ if (!tree.exists(mainPath)) return;
201
+ const mainContents = tree.read(mainPath, 'utf-8') ?? '';
202
+ if (!mainContents.includes(LANGCHAIN_AGUI_MARKER)) return;
203
+ const ready = await allMatch(tree, [
204
+ [
205
+ mainPath,
206
+ pyMatch('_graph = get_agent()')
207
+ ],
208
+ [
209
+ mainPath,
210
+ pyMatch(LANGCHAIN_AGUI_AGENT_OLD)
211
+ ],
212
+ [
213
+ mainPath,
214
+ pyMatch('agui_agent.run($input)')
215
+ ],
216
+ [
217
+ mainPath,
218
+ pyMatch('app = FastAPI(title="$name")')
219
+ ]
220
+ ]);
221
+ if (!ready) {
222
+ nextSteps.push(`${mainPath}: diverged from the generated shape - left untouched. Manually wrap graph + \`LangGraphAgent\` construction in a \`lifespan\` handler storing the wrapper on \`app.state.agui_agent\` (see the py#agent generator's langchain/ag-ui template).`);
223
+ return;
224
+ }
225
+ await addPythonDestructuredImport(tree, mainPath, [
226
+ 'asynccontextmanager'
227
+ ], 'contextlib');
228
+ await applyGritQL(tree, mainPath, pyDelete('_graph = get_agent()'));
229
+ await applyGritQL(tree, mainPath, pyRewrite(LANGCHAIN_AGUI_AGENT_OLD, LANGCHAIN_AGUI_AGENT_NEW));
230
+ await applyGritQL(tree, mainPath, pyRewrite('agui_agent.run($input)', 'request.app.state.agui_agent.run($input)'));
231
+ await applyGritQL(tree, mainPath, pyRewrite('app = FastAPI(title="$name")', 'app = FastAPI(title="$name", lifespan=lifespan)'));
232
+ };
233
+ // --- Strands AG-UI -----------------------------------------------------------
234
+ // The biggest of the four: `ag_ui_strands.create_strands_app` is dropped
235
+ // entirely for an inlined FastAPI app (matching upstream `ag_ui_strands`,
236
+ // plus a Content-Type-independent body-parsing fix and an explicit session
237
+ // re-bind `create_strands_app` was missing), so most of the file is new.
238
+ const STRANDS_AGUI_IMPORT_OLD = 'from ag_ui_strands import StrandsAgent, create_strands_app';
239
+ const STRANDS_AGUI_AGENT_OLD = `agui_agent = StrandsAgent(
240
+ agent=_agent,
241
+ name="$name",
242
+ description="A Strands Agent exposed via the AG-UI protocol.",
243
+ )`;
244
+ const STRANDS_AGUI_AGENT_NEW = `@asynccontextmanager
245
+ async def lifespan(app: FastAPI):
246
+ with get_agent() as agent:
247
+ app.state.agui_agent = StrandsAgent(
248
+ agent=agent,
249
+ name="$name",
250
+ description="A Strands Agent exposed via the AG-UI protocol.",
251
+ )
252
+ yield`;
253
+ const STRANDS_AGUI_APP_OLD = 'app = create_strands_app(agui_agent, path="/invocations")';
254
+ // `$name` isn't bound within this match (unlike STRANDS_AGUI_AGENT_OLD above),
255
+ // so the title is built from a name captured separately rather than reused as
256
+ // a metavariable - GritQL leaves a rewrite whose replacement references an
257
+ // unbound metavariable unapplied.
258
+ const strandsAguiAppNew = (name)=>`app = FastAPI(title="AWS Strands - ${name}", lifespan=lifespan)
259
+ app.add_middleware(
260
+ CORSMiddleware,
261
+ allow_origins=["*"],
262
+ allow_credentials=True,
263
+ allow_methods=["*"],
264
+ allow_headers=["*"],
265
+ )`;
266
+ const STRANDS_AGUI_MIDDLEWARE_OLD = 'app.add_middleware(_SessionIdMiddleware)';
267
+ const STRANDS_AGUI_ROUTES_NEW = `app.add_middleware(_SessionIdMiddleware)
268
+
269
+
270
+ @app.post("/invocations")
271
+ async def invocations(request: Request):
272
+ # Validate the body manually since AgentCore may omit Content-Type.
273
+ encoder = EventEncoder(accept=request.headers.get("accept") or "")
274
+ raw = await request.body()
275
+ try:
276
+ input_data = RunAgentInput.model_validate_json(raw)
277
+ except Exception as exc:
278
+ message = f"Invalid RunAgentInput: {str(exc)[:200]}"
279
+
280
+ async def _bad():
281
+ yield encoder.encode(RunErrorEvent(type=EventType.RUN_ERROR, message=message, code="BAD_REQUEST"))
282
+
283
+ return StreamingResponse(_bad(), media_type=encoder.get_content_type())
284
+
285
+ session_id = request.headers.get(SESSION_ID_HEADER) or get_current_session_id()
286
+
287
+ async def event_generator():
288
+ # Re-bind the session: the streaming body runs outside the middleware.
289
+ with session_id_context(session_id or str(uuid.uuid4())):
290
+ async for event in request.app.state.agui_agent.run(input_data):
291
+ try:
292
+ yield encoder.encode(event)
293
+ except Exception as e:
294
+ error_event = RunErrorEvent(
295
+ type=EventType.RUN_ERROR,
296
+ message=f"Encoding error: {e}",
297
+ code="ENCODING_ERROR",
298
+ )
299
+ yield encoder.encode(error_event)
300
+ break
301
+
302
+ return StreamingResponse(event_generator(), media_type=encoder.get_content_type())
303
+
304
+
305
+ @app.get("/ping")
306
+ async def ping():
307
+ return {"status": "healthy"}`;
308
+ const STRANDS_AGUI_COMMENTS_OLD = [
309
+ '# Create AG-UI agent wrapper',
310
+ '# Create FastAPI app with AG-UI endpoint and health check'
311
+ ];
312
+ const migrateStrandsAgUiAgent = async (tree, dir, nextSteps)=>{
313
+ const mainPath = joinPathFragments(dir, 'main.py');
314
+ if (!tree.exists(mainPath)) return;
315
+ const mainContents = tree.read(mainPath, 'utf-8') ?? '';
316
+ if (!mainContents.includes(STRANDS_AGUI_MARKER)) return;
317
+ const mod = await captureGritQLVariable(tree, mainPath, AGENT_CONNECTION_MODULE_PATTERN('session_id_context'), 'mod');
318
+ const agentClassName = await captureGritQLVariable(tree, mainPath, pyMatch(STRANDS_AGUI_AGENT_OLD), 'name');
319
+ const ready = !!mod && !!agentClassName && await allMatch(tree, [
320
+ [
321
+ mainPath,
322
+ pyMatch(STRANDS_AGUI_IMPORT_OLD)
323
+ ],
324
+ [
325
+ mainPath,
326
+ pyMatch('_agent_ctx = get_agent()')
327
+ ],
328
+ [
329
+ mainPath,
330
+ pyMatch('_agent = _agent_ctx.__enter__()')
331
+ ],
332
+ [
333
+ mainPath,
334
+ pyMatch(STRANDS_AGUI_AGENT_OLD)
335
+ ],
336
+ [
337
+ mainPath,
338
+ pyMatch(STRANDS_AGUI_APP_OLD)
339
+ ],
340
+ [
341
+ mainPath,
342
+ pyMatch(STRANDS_AGUI_MIDDLEWARE_OLD)
343
+ ]
344
+ ]);
345
+ if (!ready) {
346
+ nextSteps.push(`${mainPath}: diverged from the generated shape - left untouched. Manually rebuild this file without \`create_strands_app\`: a \`lifespan\` handler storing the wrapper on \`app.state.agui_agent\`, and inlined \`/invocations\`/\`/ping\` routes with Content-Type-independent body parsing (see the py#agent generator's strands/ag-ui template).`);
347
+ return;
348
+ }
349
+ await addPythonDestructuredImport(tree, mainPath, [
350
+ 'asynccontextmanager'
351
+ ], 'contextlib');
352
+ await addPythonDestructuredImport(tree, mainPath, [
353
+ 'EventType',
354
+ 'RunAgentInput',
355
+ 'RunErrorEvent'
356
+ ], 'ag_ui.core');
357
+ await addPythonDestructuredImport(tree, mainPath, [
358
+ 'EventEncoder'
359
+ ], 'ag_ui.encoder');
360
+ await addPythonDestructuredImport(tree, mainPath, [
361
+ 'FastAPI'
362
+ ], 'fastapi');
363
+ await addPythonDestructuredImport(tree, mainPath, [
364
+ 'CORSMiddleware'
365
+ ], 'fastapi.middleware.cors');
366
+ await addPythonDestructuredImport(tree, mainPath, [
367
+ 'StreamingResponse'
368
+ ], 'fastapi.responses');
369
+ await addPythonDestructuredImport(tree, mainPath, [
370
+ 'get_current_session_id'
371
+ ], mod);
372
+ await applyGritQL(tree, mainPath, pyRewrite(STRANDS_AGUI_IMPORT_OLD, 'from ag_ui_strands import StrandsAgent'));
373
+ await applyGritQL(tree, mainPath, pyDelete('_agent_ctx = get_agent()'));
374
+ await applyGritQL(tree, mainPath, pyDelete('_agent = _agent_ctx.__enter__()'));
375
+ await applyGritQL(tree, mainPath, pyRewrite(STRANDS_AGUI_AGENT_OLD, STRANDS_AGUI_AGENT_NEW));
376
+ for (const comment of STRANDS_AGUI_COMMENTS_OLD){
377
+ await applyGritQL(tree, mainPath, pyDelete(comment));
378
+ }
379
+ await applyGritQL(tree, mainPath, pyRewrite(STRANDS_AGUI_APP_OLD, strandsAguiAppNew(agentClassName)));
380
+ await applyGritQL(tree, mainPath, pyRewrite(STRANDS_AGUI_MIDDLEWARE_OLD, STRANDS_AGUI_ROUTES_NEW));
381
+ };
382
+ /** This agent's ComponentMetadata entries. */ const findAgentComponents = (components)=>(components ?? []).filter((component)=>component.generator === PY_AGENT_GENERATOR_INFO.id);
383
+ export default async function migration(tree) {
384
+ const nextSteps = [];
385
+ for (const project of getProjects(tree).values()){
386
+ const components = findAgentComponents(project.metadata?.components);
387
+ for (const component of components){
388
+ if (!component.path) continue;
389
+ const dir = joinPathFragments(project.root, component.path);
390
+ const framework = component.framework ?? 'strands';
391
+ if (component.protocol === 'http') {
392
+ if (framework === 'langchain') {
393
+ await migrateLangchainHttpAgent(tree, dir, nextSteps);
394
+ } else {
395
+ await migrateStrandsHttpAgent(tree, dir, nextSteps);
396
+ }
397
+ } else if (component.protocol === 'ag-ui') {
398
+ if (framework === 'langchain') {
399
+ await migrateLangchainAgUiAgent(tree, dir, nextSteps);
400
+ } else {
401
+ await migrateStrandsAgUiAgent(tree, dir, nextSteps);
402
+ }
403
+ }
404
+ }
405
+ }
406
+ await formatFilesInSubtree(tree);
407
+ return {
408
+ nextSteps
409
+ };
410
+ }
411
+
412
+ //# sourceMappingURL=migration.js.map