mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-22 02:54:31 -07:00
feat: Add A2A Client Manager and tests (#15485)
This commit is contained in:
Generated
+45
-20
@@ -2500,6 +2500,7 @@
|
|||||||
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
|
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-token": "^6.0.0",
|
"@octokit/auth-token": "^6.0.0",
|
||||||
"@octokit/graphql": "^9.0.2",
|
"@octokit/graphql": "^9.0.2",
|
||||||
@@ -2680,6 +2681,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
@@ -2713,6 +2715,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz",
|
||||||
"integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==",
|
"integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||||
},
|
},
|
||||||
@@ -3081,6 +3084,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz",
|
||||||
"integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==",
|
"integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/core": "2.0.1",
|
"@opentelemetry/core": "2.0.1",
|
||||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||||
@@ -3114,6 +3118,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz",
|
||||||
"integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==",
|
"integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/core": "2.0.1",
|
"@opentelemetry/core": "2.0.1",
|
||||||
"@opentelemetry/resources": "2.0.1"
|
"@opentelemetry/resources": "2.0.1"
|
||||||
@@ -3166,6 +3171,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz",
|
||||||
"integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==",
|
"integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/core": "2.0.1",
|
"@opentelemetry/core": "2.0.1",
|
||||||
"@opentelemetry/resources": "2.0.1",
|
"@opentelemetry/resources": "2.0.1",
|
||||||
@@ -4396,6 +4402,7 @@
|
|||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@@ -4673,6 +4680,7 @@
|
|||||||
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
|
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.35.0",
|
"@typescript-eslint/scope-manager": "8.35.0",
|
||||||
"@typescript-eslint/types": "8.35.0",
|
"@typescript-eslint/types": "8.35.0",
|
||||||
@@ -5684,6 +5692,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -6129,8 +6138,7 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/array-includes": {
|
"node_modules/array-includes": {
|
||||||
"version": "3.1.9",
|
"version": "3.1.9",
|
||||||
@@ -7420,7 +7428,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safe-buffer": "5.2.1"
|
"safe-buffer": "5.2.1"
|
||||||
},
|
},
|
||||||
@@ -8744,6 +8751,7 @@
|
|||||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -9346,7 +9354,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -9356,7 +9363,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
@@ -9366,7 +9372,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -9620,7 +9625,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"encodeurl": "~2.0.0",
|
"encodeurl": "~2.0.0",
|
||||||
@@ -9639,7 +9643,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
@@ -9648,15 +9651,13 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/finalhandler/node_modules/statuses": {
|
"node_modules/finalhandler/node_modules/statuses": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
@@ -10942,6 +10943,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.6.tgz",
|
||||||
"integrity": "sha512-QHl6l1cl3zPCaRMzt9TUbTX6Q5SzvkGEZDDad0DmSf5SPmT1/90k6pGPejEvDCJprkitwObXpPaTWGHItqsy4g==",
|
"integrity": "sha512-QHl6l1cl3zPCaRMzt9TUbTX6Q5SzvkGEZDDad0DmSf5SPmT1/90k6pGPejEvDCJprkitwObXpPaTWGHItqsy4g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alcalzone/ansi-tokenize": "^0.2.1",
|
"@alcalzone/ansi-tokenize": "^0.2.1",
|
||||||
"ansi-escapes": "^7.0.0",
|
"ansi-escapes": "^7.0.0",
|
||||||
@@ -14136,8 +14138,7 @@
|
|||||||
"version": "0.1.12",
|
"version": "0.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@@ -14718,6 +14719,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -14728,6 +14730,7 @@
|
|||||||
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
|
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"shell-quote": "^1.6.1",
|
"shell-quote": "^1.6.1",
|
||||||
"ws": "^7"
|
"ws": "^7"
|
||||||
@@ -16987,6 +16990,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -17213,7 +17217,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "0BSD"
|
"license": "0BSD",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.20.3",
|
"version": "4.20.3",
|
||||||
@@ -17221,6 +17226,7 @@
|
|||||||
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "~0.25.0",
|
"esbuild": "~0.25.0",
|
||||||
"get-tsconfig": "^4.7.5"
|
"get-tsconfig": "^4.7.5"
|
||||||
@@ -17405,6 +17411,7 @@
|
|||||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -17567,7 +17574,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
@@ -17623,6 +17629,7 @@
|
|||||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -17739,6 +17746,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -17752,6 +17760,7 @@
|
|||||||
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/chai": "^5.2.2",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/expect": "3.2.4",
|
"@vitest/expect": "3.2.4",
|
||||||
@@ -18458,6 +18467,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
@@ -18481,7 +18491,7 @@
|
|||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"fs-extra": "^11.3.0",
|
"fs-extra": "^11.3.0",
|
||||||
"tar": "^7.5.2",
|
"tar": "^7.5.2",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^13.0.0",
|
||||||
"winston": "^3.17.0"
|
"winston": "^3.17.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -18769,16 +18779,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/a2a-server/node_modules/uuid": {
|
"packages/a2a-server/node_modules/uuid": {
|
||||||
"version": "11.1.0",
|
"version": "13.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||||
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://github.com/sponsors/broofa",
|
"https://github.com/sponsors/broofa",
|
||||||
"https://github.com/sponsors/ctavan"
|
"https://github.com/sponsors/ctavan"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"uuid": "dist/esm/bin/uuid"
|
"uuid": "dist-node/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/cli": {
|
"packages/cli": {
|
||||||
@@ -18932,6 +18942,7 @@
|
|||||||
"strip-ansi": "^7.1.0",
|
"strip-ansi": "^7.1.0",
|
||||||
"tree-sitter-bash": "^0.25.0",
|
"tree-sitter-bash": "^0.25.0",
|
||||||
"undici": "^7.10.0",
|
"undici": "^7.10.0",
|
||||||
|
"uuid": "^13.0.0",
|
||||||
"web-tree-sitter": "^0.25.10",
|
"web-tree-sitter": "^0.25.10",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
@@ -19017,6 +19028,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -19024,6 +19036,19 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/core/node_modules/uuid": {
|
||||||
|
"version": "13.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||||
|
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist-node/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"packages/test-utils": {
|
"packages/test-utils": {
|
||||||
"name": "@google/gemini-cli-test-utils",
|
"name": "@google/gemini-cli-test-utils",
|
||||||
"version": "0.21.0-nightly.20251220.41a1a3eed",
|
"version": "0.21.0-nightly.20251220.41a1a3eed",
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"fs-extra": "^11.3.0",
|
"fs-extra": "^11.3.0",
|
||||||
"tar": "^7.5.2",
|
"tar": "^7.5.2",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^13.0.0",
|
||||||
"winston": "^3.17.0"
|
"winston": "^3.17.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
"strip-ansi": "^7.1.0",
|
"strip-ansi": "^7.1.0",
|
||||||
"tree-sitter-bash": "^0.25.0",
|
"tree-sitter-bash": "^0.25.0",
|
||||||
"undici": "^7.10.0",
|
"undici": "^7.10.0",
|
||||||
|
"uuid": "^13.0.0",
|
||||||
"web-tree-sitter": "^0.25.10",
|
"web-tree-sitter": "^0.25.10",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,305 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
|
import {
|
||||||
|
A2AClientManager,
|
||||||
|
type SendMessageResult,
|
||||||
|
} from './a2a-client-manager.js';
|
||||||
|
import type { AgentCard, Task } from '@a2a-js/sdk';
|
||||||
|
import type { AuthenticationHandler, Client } from '@a2a-js/sdk/client';
|
||||||
|
import { ClientFactory, DefaultAgentCardResolver } from '@a2a-js/sdk/client';
|
||||||
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
|
import {
|
||||||
|
createAuthenticatingFetchWithRetry,
|
||||||
|
ClientFactoryOptions,
|
||||||
|
} from '@a2a-js/sdk/client';
|
||||||
|
|
||||||
|
vi.mock('../utils/debugLogger.js', () => ({
|
||||||
|
debugLogger: {
|
||||||
|
debug: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@a2a-js/sdk/client', () => {
|
||||||
|
const ClientFactory = vi.fn();
|
||||||
|
const DefaultAgentCardResolver = vi.fn();
|
||||||
|
const RestTransportFactory = vi.fn();
|
||||||
|
const JsonRpcTransportFactory = vi.fn();
|
||||||
|
const ClientFactoryOptions = {
|
||||||
|
default: {},
|
||||||
|
createFrom: vi.fn(),
|
||||||
|
};
|
||||||
|
const createAuthenticatingFetchWithRetry = vi.fn();
|
||||||
|
|
||||||
|
DefaultAgentCardResolver.prototype.resolve = vi.fn();
|
||||||
|
ClientFactory.prototype.createFromUrl = vi.fn();
|
||||||
|
|
||||||
|
return {
|
||||||
|
ClientFactory,
|
||||||
|
ClientFactoryOptions,
|
||||||
|
DefaultAgentCardResolver,
|
||||||
|
RestTransportFactory,
|
||||||
|
JsonRpcTransportFactory,
|
||||||
|
createAuthenticatingFetchWithRetry,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('A2AClientManager', () => {
|
||||||
|
let manager: A2AClientManager;
|
||||||
|
|
||||||
|
// Stable mocks initialized once
|
||||||
|
const sendMessageMock = vi.fn();
|
||||||
|
const getTaskMock = vi.fn();
|
||||||
|
const cancelTaskMock = vi.fn();
|
||||||
|
const getAgentCardMock = vi.fn();
|
||||||
|
const authFetchMock = vi.fn();
|
||||||
|
|
||||||
|
const mockClient = {
|
||||||
|
sendMessage: sendMessageMock,
|
||||||
|
getTask: getTaskMock,
|
||||||
|
cancelTask: cancelTaskMock,
|
||||||
|
getAgentCard: getAgentCardMock,
|
||||||
|
} as unknown as Client;
|
||||||
|
|
||||||
|
const mockAgentCard: Partial<AgentCard> = { name: 'TestAgent' };
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
A2AClientManager.resetInstanceForTesting();
|
||||||
|
manager = A2AClientManager.getInstance();
|
||||||
|
|
||||||
|
// Default mock implementations
|
||||||
|
getAgentCardMock.mockResolvedValue({
|
||||||
|
...mockAgentCard,
|
||||||
|
url: 'http://test.agent/real/endpoint',
|
||||||
|
} as AgentCard);
|
||||||
|
|
||||||
|
vi.mocked(ClientFactory.prototype.createFromUrl).mockResolvedValue(
|
||||||
|
mockClient,
|
||||||
|
);
|
||||||
|
|
||||||
|
vi.mocked(DefaultAgentCardResolver.prototype.resolve).mockResolvedValue({
|
||||||
|
...mockAgentCard,
|
||||||
|
url: 'http://test.agent/real/endpoint',
|
||||||
|
} as AgentCard);
|
||||||
|
|
||||||
|
vi.mocked(ClientFactoryOptions.createFrom).mockImplementation(
|
||||||
|
(_defaults, overrides) => overrides as ClientFactoryOptions,
|
||||||
|
);
|
||||||
|
|
||||||
|
vi.mocked(createAuthenticatingFetchWithRetry).mockReturnValue(
|
||||||
|
authFetchMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
vi.stubGlobal(
|
||||||
|
'fetch',
|
||||||
|
vi.fn().mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({}),
|
||||||
|
} as Response),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enforce the singleton pattern', () => {
|
||||||
|
const instance1 = A2AClientManager.getInstance();
|
||||||
|
const instance2 = A2AClientManager.getInstance();
|
||||||
|
expect(instance1).toBe(instance2);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadAgent', () => {
|
||||||
|
it('should create and cache an A2AClient', async () => {
|
||||||
|
const agentCard = await manager.loadAgent(
|
||||||
|
'TestAgent',
|
||||||
|
'http://test.agent/card',
|
||||||
|
);
|
||||||
|
expect(agentCard).toMatchObject(mockAgentCard);
|
||||||
|
expect(manager.getAgentCard('TestAgent')).toBe(agentCard);
|
||||||
|
expect(manager.getClient('TestAgent')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if an agent with the same name is already loaded', async () => {
|
||||||
|
await manager.loadAgent('TestAgent', 'http://test.agent/card');
|
||||||
|
await expect(
|
||||||
|
manager.loadAgent('TestAgent', 'http://another.agent/card'),
|
||||||
|
).rejects.toThrow("Agent with name 'TestAgent' is already loaded.");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use native fetch by default', async () => {
|
||||||
|
await manager.loadAgent('TestAgent', 'http://test.agent/card');
|
||||||
|
expect(createAuthenticatingFetchWithRetry).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use provided custom authentication handler', async () => {
|
||||||
|
const customAuthHandler = {
|
||||||
|
headers: vi.fn(),
|
||||||
|
shouldRetryWithHeaders: vi.fn(),
|
||||||
|
};
|
||||||
|
await manager.loadAgent(
|
||||||
|
'CustomAuthAgent',
|
||||||
|
'http://custom.agent/card',
|
||||||
|
customAuthHandler as unknown as AuthenticationHandler,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(createAuthenticatingFetchWithRetry).toHaveBeenCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
customAuthHandler,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log a debug message upon loading an agent', async () => {
|
||||||
|
await manager.loadAgent('TestAgent', 'http://test.agent/card');
|
||||||
|
expect(debugLogger.debug).toHaveBeenCalledWith(
|
||||||
|
"[A2AClientManager] Loaded agent 'TestAgent' from http://test.agent/card",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sendMessage', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await manager.loadAgent('TestAgent', 'http://test.agent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send a message to the correct agent', async () => {
|
||||||
|
sendMessageMock.mockResolvedValue({
|
||||||
|
kind: 'message',
|
||||||
|
messageId: 'a',
|
||||||
|
parts: [],
|
||||||
|
role: 'agent',
|
||||||
|
} as SendMessageResult);
|
||||||
|
|
||||||
|
await manager.sendMessage('TestAgent', 'Hello');
|
||||||
|
expect(sendMessageMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
message: expect.anything(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use contextId and taskId when provided', async () => {
|
||||||
|
sendMessageMock.mockResolvedValue({
|
||||||
|
kind: 'message',
|
||||||
|
messageId: 'a',
|
||||||
|
parts: [],
|
||||||
|
role: 'agent',
|
||||||
|
} as SendMessageResult);
|
||||||
|
|
||||||
|
const expectedContextId = 'user-context-id';
|
||||||
|
const expectedTaskId = 'user-task-id';
|
||||||
|
|
||||||
|
await manager.sendMessage('TestAgent', 'Hello', {
|
||||||
|
contextId: expectedContextId,
|
||||||
|
taskId: expectedTaskId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const call = sendMessageMock.mock.calls[0][0];
|
||||||
|
expect(call.message.contextId).toBe(expectedContextId);
|
||||||
|
expect(call.message.taskId).toBe(expectedTaskId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return result from client', async () => {
|
||||||
|
const mockResult = {
|
||||||
|
contextId: 'server-context-id',
|
||||||
|
id: 'ctx-1',
|
||||||
|
kind: 'task',
|
||||||
|
status: { state: 'working' },
|
||||||
|
};
|
||||||
|
|
||||||
|
sendMessageMock.mockResolvedValueOnce(mockResult as SendMessageResult);
|
||||||
|
|
||||||
|
const response = await manager.sendMessage('TestAgent', 'Hello');
|
||||||
|
|
||||||
|
expect(response).toEqual(mockResult);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw prefixed error on failure', async () => {
|
||||||
|
sendMessageMock.mockRejectedValueOnce(new Error('Network error'));
|
||||||
|
|
||||||
|
await expect(manager.sendMessage('TestAgent', 'Hello')).rejects.toThrow(
|
||||||
|
'A2AClient SendMessage Error [TestAgent]: Network error',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the agent is not found', async () => {
|
||||||
|
await expect(
|
||||||
|
manager.sendMessage('NonExistentAgent', 'Hello'),
|
||||||
|
).rejects.toThrow("Agent 'NonExistentAgent' not found.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTask', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await manager.loadAgent('TestAgent', 'http://test.agent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get a task from the correct agent', async () => {
|
||||||
|
getTaskMock.mockResolvedValue({
|
||||||
|
id: 'task123',
|
||||||
|
contextId: 'a',
|
||||||
|
kind: 'task',
|
||||||
|
status: { state: 'completed' },
|
||||||
|
} as Task);
|
||||||
|
|
||||||
|
await manager.getTask('TestAgent', 'task123');
|
||||||
|
expect(getTaskMock).toHaveBeenCalledWith({
|
||||||
|
id: 'task123',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw prefixed error on failure', async () => {
|
||||||
|
getTaskMock.mockRejectedValueOnce(new Error('Network error'));
|
||||||
|
|
||||||
|
await expect(manager.getTask('TestAgent', 'task123')).rejects.toThrow(
|
||||||
|
'A2AClient getTask Error [TestAgent]: Network error',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the agent is not found', async () => {
|
||||||
|
await expect(
|
||||||
|
manager.getTask('NonExistentAgent', 'task123'),
|
||||||
|
).rejects.toThrow("Agent 'NonExistentAgent' not found.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cancelTask', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await manager.loadAgent('TestAgent', 'http://test.agent');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cancel a task on the correct agent', async () => {
|
||||||
|
cancelTaskMock.mockResolvedValue({
|
||||||
|
id: 'task123',
|
||||||
|
contextId: 'a',
|
||||||
|
kind: 'task',
|
||||||
|
status: { state: 'canceled' },
|
||||||
|
} as Task);
|
||||||
|
|
||||||
|
await manager.cancelTask('TestAgent', 'task123');
|
||||||
|
expect(cancelTaskMock).toHaveBeenCalledWith({
|
||||||
|
id: 'task123',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw prefixed error on failure', async () => {
|
||||||
|
cancelTaskMock.mockRejectedValueOnce(new Error('Network error'));
|
||||||
|
|
||||||
|
await expect(manager.cancelTask('TestAgent', 'task123')).rejects.toThrow(
|
||||||
|
'A2AClient cancelTask Error [TestAgent]: Network error',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the agent is not found', async () => {
|
||||||
|
await expect(
|
||||||
|
manager.cancelTask('NonExistentAgent', 'task123'),
|
||||||
|
).rejects.toThrow("Agent 'NonExistentAgent' not found.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { AgentCard, Message, MessageSendParams, Task } from '@a2a-js/sdk';
|
||||||
|
import {
|
||||||
|
type Client,
|
||||||
|
ClientFactory,
|
||||||
|
ClientFactoryOptions,
|
||||||
|
DefaultAgentCardResolver,
|
||||||
|
RestTransportFactory,
|
||||||
|
JsonRpcTransportFactory,
|
||||||
|
type AuthenticationHandler,
|
||||||
|
createAuthenticatingFetchWithRetry,
|
||||||
|
} from '@a2a-js/sdk/client';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
|
|
||||||
|
export type SendMessageResult = Message | Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages A2A clients and caches loaded agent information.
|
||||||
|
* Follows a singleton pattern to ensure a single client instance.
|
||||||
|
*/
|
||||||
|
export class A2AClientManager {
|
||||||
|
private static instance: A2AClientManager;
|
||||||
|
|
||||||
|
// Each agent should manage their own context/taskIds/card/etc
|
||||||
|
private clients = new Map<string, Client>();
|
||||||
|
private agentCards = new Map<string, AgentCard>();
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the singleton instance of the A2AClientManager.
|
||||||
|
*/
|
||||||
|
static getInstance(): A2AClientManager {
|
||||||
|
if (!A2AClientManager.instance) {
|
||||||
|
A2AClientManager.instance = new A2AClientManager();
|
||||||
|
}
|
||||||
|
return A2AClientManager.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the singleton instance. Only for testing purposes.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
static resetInstanceForTesting() {
|
||||||
|
// @ts-expect-error - Resetting singleton for testing
|
||||||
|
A2AClientManager.instance = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an agent by fetching its AgentCard and caches the client.
|
||||||
|
* @param name The name to assign to the agent.
|
||||||
|
* @param agentCardUrl The full URL to the agent's card.
|
||||||
|
* @param authHandler Optional authentication handler to use for this agent.
|
||||||
|
* @returns The loaded AgentCard.
|
||||||
|
*/
|
||||||
|
async loadAgent(
|
||||||
|
name: string,
|
||||||
|
agentCardUrl: string,
|
||||||
|
authHandler?: AuthenticationHandler,
|
||||||
|
): Promise<AgentCard> {
|
||||||
|
if (this.clients.has(name)) {
|
||||||
|
throw new Error(`Agent with name '${name}' is already loaded.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fetchImpl = fetch;
|
||||||
|
if (authHandler) {
|
||||||
|
fetchImpl = createAuthenticatingFetchWithRetry(fetch, authHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolver = new DefaultAgentCardResolver({ fetchImpl });
|
||||||
|
|
||||||
|
const options = ClientFactoryOptions.createFrom(
|
||||||
|
ClientFactoryOptions.default,
|
||||||
|
{
|
||||||
|
transports: [
|
||||||
|
new RestTransportFactory({ fetchImpl }),
|
||||||
|
new JsonRpcTransportFactory({ fetchImpl }),
|
||||||
|
],
|
||||||
|
cardResolver: resolver,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const factory = new ClientFactory(options);
|
||||||
|
const client = await factory.createFromUrl(agentCardUrl, '');
|
||||||
|
const agentCard = await client.getAgentCard();
|
||||||
|
|
||||||
|
this.clients.set(name, client);
|
||||||
|
this.agentCards.set(name, agentCard);
|
||||||
|
|
||||||
|
debugLogger.debug(
|
||||||
|
`[A2AClientManager] Loaded agent '${name}' from ${agentCardUrl}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return agentCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to a loaded agent.
|
||||||
|
* @param agentName The name of the agent to send the message to.
|
||||||
|
* @param message The message content.
|
||||||
|
* @param options Optional context and task IDs to maintain conversation state.
|
||||||
|
* @returns The response from the agent (Message or Task).
|
||||||
|
* @throws Error if the agent returns an error response.
|
||||||
|
*/
|
||||||
|
async sendMessage(
|
||||||
|
agentName: string,
|
||||||
|
message: string,
|
||||||
|
options?: { contextId?: string; taskId?: string },
|
||||||
|
): Promise<SendMessageResult> {
|
||||||
|
const client = this.clients.get(agentName);
|
||||||
|
if (!client) {
|
||||||
|
throw new Error(`Agent '${agentName}' not found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageParams: MessageSendParams = {
|
||||||
|
message: {
|
||||||
|
kind: 'message',
|
||||||
|
role: 'user',
|
||||||
|
messageId: uuidv4(),
|
||||||
|
parts: [{ kind: 'text', text: message }],
|
||||||
|
contextId: options?.contextId,
|
||||||
|
taskId: options?.taskId,
|
||||||
|
},
|
||||||
|
configuration: {
|
||||||
|
blocking: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await client.sendMessage(messageParams);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const prefix = `A2AClient SendMessage Error [${agentName}]`;
|
||||||
|
if (error instanceof Error) {
|
||||||
|
throw new Error(`${prefix}: ${error.message}`, { cause: error });
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`${prefix}: Unexpected error during sendMessage: ${String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a loaded agent card.
|
||||||
|
* @param name The name of the agent.
|
||||||
|
* @returns The agent card, or undefined if not found.
|
||||||
|
*/
|
||||||
|
getAgentCard(name: string): AgentCard | undefined {
|
||||||
|
return this.agentCards.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a loaded client.
|
||||||
|
* @param name The name of the agent.
|
||||||
|
* @returns The client, or undefined if not found.
|
||||||
|
*/
|
||||||
|
getClient(name: string): Client | undefined {
|
||||||
|
return this.clients.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a task from an agent.
|
||||||
|
* @param agentName The name of the agent.
|
||||||
|
* @param taskId The ID of the task to retrieve.
|
||||||
|
* @returns The task details.
|
||||||
|
*/
|
||||||
|
async getTask(agentName: string, taskId: string): Promise<Task> {
|
||||||
|
const client = this.clients.get(agentName);
|
||||||
|
if (!client) {
|
||||||
|
throw new Error(`Agent '${agentName}' not found.`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await client.getTask({ id: taskId });
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const prefix = `A2AClient getTask Error [${agentName}]`;
|
||||||
|
if (error instanceof Error) {
|
||||||
|
throw new Error(`${prefix}: ${error.message}`, { cause: error });
|
||||||
|
}
|
||||||
|
throw new Error(`${prefix}: Unexpected error: ${String(error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels a task on an agent.
|
||||||
|
* @param agentName The name of the agent.
|
||||||
|
* @param taskId The ID of the task to cancel.
|
||||||
|
* @returns The cancellation response.
|
||||||
|
*/
|
||||||
|
async cancelTask(agentName: string, taskId: string): Promise<Task> {
|
||||||
|
const client = this.clients.get(agentName);
|
||||||
|
if (!client) {
|
||||||
|
throw new Error(`Agent '${agentName}' not found.`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await client.cancelTask({ id: taskId });
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const prefix = `A2AClient cancelTask Error [${agentName}]`;
|
||||||
|
if (error instanceof Error) {
|
||||||
|
throw new Error(`${prefix}: ${error.message}`, { cause: error });
|
||||||
|
}
|
||||||
|
throw new Error(`${prefix}: Unexpected error: ${String(error)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user