Compare commits
681 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48f0a83191 | |||
| f5dce650a4 | |||
| 99267012bc | |||
| 53bbc2b339 | |||
| a31412ba83 | |||
| cf4114062b | |||
| eb03bd16b9 | |||
| 9f3a154014 | |||
| 137c4cd59c | |||
| 15298b28c2 | |||
| 7311e242ec | |||
| 21a3925f99 | |||
| ec35ebbe57 | |||
| 65024d4538 | |||
| 4fb3790051 | |||
| 4a040931ef | |||
| e74efc5c0b | |||
| d5a5995281 | |||
| 893ae4d29a | |||
| 370c45de67 | |||
| a93a1ebd65 | |||
| 3f12c1d7c7 | |||
| 61719a3926 | |||
| beaa2a968b | |||
| ca0e6f9bd9 | |||
| 7a70ab9a5d | |||
| e54eecca51 | |||
| 7de3e4dcf9 | |||
| 8c215c7a88 | |||
| fe4fcf89d9 | |||
| 1f5d7014c6 | |||
| 1ae0499e5d | |||
| 9d741abdba | |||
| f54bccfe03 | |||
| 1db169bc66 | |||
| b9b7bba48d | |||
| 29caa52bb7 | |||
| e5adeaca80 | |||
| 6fb58bd31f | |||
| 7c3469713d | |||
| 64c928fce7 | |||
| 61b21e3d63 | |||
| 77027dff82 | |||
| c0dfa1aec3 | |||
| 06173c0885 | |||
| 08b926796c | |||
| b80234aa3e | |||
| efbddcbf98 | |||
| 63cc363606 | |||
| e0044f2868 | |||
| beff8c91aa | |||
| f510394721 | |||
| 811a383d50 | |||
| 8d171e0200 | |||
| 7b6ab50138 | |||
| 44c8b43328 | |||
| 242afd49a1 | |||
| 66c07d7296 | |||
| 40b73c9447 | |||
| 973092df50 | |||
| e446733b53 | |||
| 3344f6849c | |||
| 84936dc85d | |||
| 18cdbbf81a | |||
| 13ccc16457 | |||
| ca78a0f177 | |||
| cb7f7d6c72 | |||
| 0d7e778e08 | |||
| b5f568fefe | |||
| d00b43733c | |||
| 55f5d3923c | |||
| 2d432c1489 | |||
| 597778e55f | |||
| 4e21e5b8a3 | |||
| d9d51ba15b | |||
| aed85725b6 | |||
| 43cf63e189 | |||
| bda4491616 | |||
| 6b303a13eb | |||
| 16468a855d | |||
| 377e834e03 | |||
| 3a32d9723e | |||
| bf3ac20da0 | |||
| a3ef87e6e2 | |||
| 4c4d8bc411 | |||
| f938a3f51d | |||
| c61506bbc1 | |||
| 6a8a0d4faa | |||
| 066da2a1d1 | |||
| 7d1848d578 | |||
| eb95e99b3d | |||
| ca43f8c291 | |||
| dcf5afafda | |||
| 9f76f34049 | |||
| 7c5cd693ce | |||
| c9ed5e41b1 | |||
| 8ae5b56b5b | |||
| 1b265f343f | |||
| fd5c103f99 | |||
| cdc602edd7 | |||
| 782bb4e4bd | |||
| 94f9480a3a | |||
| 9364dd8a49 | |||
| 6d48a12efe | |||
| beab7cc053 | |||
| 6cade3eaec | |||
| c8d18eb2ac | |||
| 91400c5b0b | |||
| 554a5a36a3 | |||
| 7e117ac0ac | |||
| 6f053dd8b9 | |||
| 25af91e4df | |||
| 1edc542b55 | |||
| 119dff3b73 | |||
| 7c4b497a84 | |||
| 65ee6171e7 | |||
| 9cb09ccf8e | |||
| a79f2f81ae | |||
| 140c2b9914 | |||
| 6805e818f7 | |||
| 07e2053e12 | |||
| f9a93a1337 | |||
| 3982a252bb | |||
| e293424bb4 | |||
| 21ad42f677 | |||
| 561418c554 | |||
| d0d3639e16 | |||
| 2f7f967189 | |||
| 46d6b119b6 | |||
| 80929c48c5 | |||
| 35efdfc409 | |||
| bea57a2f3d | |||
| 1df5c98b33 | |||
| 3e95b8ec59 | |||
| 5b5f87abc7 | |||
| 991dca4c40 | |||
| dfba0e91e2 | |||
| 117a2d3844 | |||
| 4b20d93e1d | |||
| 44cdb3e376 | |||
| 9cf410478c | |||
| a255529c6b | |||
| d9d2ce36f2 | |||
| da8c841ef4 | |||
| b7c86b5497 | |||
| 3eebb75b7a | |||
| 07ab16dbbe | |||
| afc1d50c20 | |||
| ae123c547c | |||
| c2705e8332 | |||
| 9574855435 | |||
| bf6dae4690 | |||
| b5529c2475 | |||
| 9e74a7ec18 | |||
| 4034c030e7 | |||
| 765fb67011 | |||
| 97c99f263a | |||
| f1a3c35dee | |||
| ebe98fdee9 | |||
| ba71ffa736 | |||
| 320c8aba4c | |||
| e7dccabf14 | |||
| a84d4d876e | |||
| 29031ea7cf | |||
| f3977392e6 | |||
| 535667baf6 | |||
| 33cf2da1df | |||
| 104587bae8 | |||
| aca8e1af05 | |||
| 6f92642524 | |||
| 8413dd62ef | |||
| 750dec5d8d | |||
| 335b36893b | |||
| 25a20f8e4e | |||
| b5ba88b001 | |||
| 8868b34c75 | |||
| 73dd7328df | |||
| d25ce0e143 | |||
| 30397816da | |||
| 84f1c19265 | |||
| d33170931c | |||
| 1d230dbfbf | |||
| c92ae8a359 | |||
| bf03543bf6 | |||
| 1d2fbbf9c3 | |||
| 9762bf2965 | |||
| c888da5f73 | |||
| aa4d9316a9 | |||
| 5755ec2dcf | |||
| a3c1c659fd | |||
| 49534209f2 | |||
| 9e7f52b8f5 | |||
| 30e0ab102a | |||
| 2e03e3aed5 | |||
| c1e4dbd157 | |||
| ae3dbab38a | |||
| a86935b6de | |||
| b91758bf6b | |||
| 20fd405f9c | |||
| 8595b07f6d | |||
| 7b710a2790 | |||
| 124d5cfb9e | |||
| 3ada29fb51 | |||
| 012740b68f | |||
| fd0893c346 | |||
| a6a3689298 | |||
| 20aa695ac4 | |||
| 6d3437badb | |||
| 86111c4d54 | |||
| fe92a43e31 | |||
| 830f7dec61 | |||
| 0bb6c25dc7 | |||
| f618da15d6 | |||
| 1b052df52f | |||
| f11bd3d079 | |||
| c06794b3c6 | |||
| ec953426db | |||
| 028d0368d5 | |||
| 6deee11449 | |||
| bbf5c2fe95 | |||
| e667739c04 | |||
| 109a7dc531 | |||
| 5e186bfb22 | |||
| 0c919857fa | |||
| d78f54a08a | |||
| 46aa3fd193 | |||
| 73526416cf | |||
| 5b7f7b30a7 | |||
| a6c7affedb | |||
| 578d656de9 | |||
| f74f2b0780 | |||
| 0552464eed | |||
| bbdd8457df | |||
| 71a9131709 | |||
| 397ff84b0e | |||
| 1f07efb5d8 | |||
| ee425aefa6 | |||
| 466671eed4 | |||
| 11dc33eab7 | |||
| e591b51919 | |||
| 055ff92276 | |||
| 84f40768a1 | |||
| bf80e27dbc | |||
| 122b8d43ca | |||
| 999ae7827d | |||
| 1c3d397782 | |||
| 6b7dc4d822 | |||
| 139cc7b97c | |||
| 91d756f391 | |||
| fc18768155 | |||
| 893c7d3880 | |||
| dcedc42979 | |||
| 46fd7b4864 | |||
| 36e6445dba | |||
| 37c8de3c06 | |||
| a833d350a4 | |||
| 1c1416678d | |||
| 271908dc94 | |||
| 1560131f94 | |||
| a1f9af3fa7 | |||
| 89ca78837e | |||
| 57a66f5f0d | |||
| 84caf00cd4 | |||
| a7bed2cc4c | |||
| a7aa6bd6cf | |||
| fcd190b8b1 | |||
| f784e192fa | |||
| b35c12d8d0 | |||
| 2a18e78611 | |||
| 4728028512 | |||
| 00bda50d0b | |||
| 919e5da581 | |||
| db14cdf92b | |||
| fbf38361ad | |||
| b10bcf49b9 | |||
| 15f8026983 | |||
| 37857ab956 | |||
| 42a673a52c | |||
| 153f2630b9 | |||
| d5b78dbeea | |||
| 139ef0d5bd | |||
| b2d6dc4e32 | |||
| ac95282758 | |||
| efeb9f7e7b | |||
| 5a65610fa6 | |||
| 447a854ad9 | |||
| b58d79c517 | |||
| daf3691841 | |||
| 517961b2eb | |||
| ec0161ad37 | |||
| cdf077da56 | |||
| 99e5164c82 | |||
| d0ebc81c28 | |||
| c67817f1a9 | |||
| c7d44e339b | |||
| 6055c47079 | |||
| 4c533b1249 | |||
| 4a3d9414ef | |||
| 0df9498674 | |||
| 8f391585ab | |||
| e7b6326cfa | |||
| 1a70fdd364 | |||
| b316fcc44d | |||
| d1dc4902fd | |||
| d3766875f8 | |||
| 28935d1e6b | |||
| 244a608186 | |||
| fc03891a11 | |||
| 974d29128f | |||
| 3382e0413e | |||
| 992c04e768 | |||
| fbb17ebf58 | |||
| e8fe43bd69 | |||
| 11ec4ac2f8 | |||
| 8eb419a47a | |||
| 4e80f01fda | |||
| 6c78eb7a39 | |||
| 86a3a913b5 | |||
| 05e4ea80ee | |||
| cca595971d | |||
| 26b9af1cdc | |||
| b459e1a108 | |||
| 62cb14fa52 | |||
| 7a65c1e91d | |||
| 5a3c7154df | |||
| b9c87c14a2 | |||
| 52250c162d | |||
| b52641de0d | |||
| 8615315711 | |||
| c9a336976b | |||
| 06a7873c51 | |||
| 0e66f545ca | |||
| 98d1bec99f | |||
| 08063d7b0a | |||
| 2ebcd48a4e | |||
| 36dbaa8462 | |||
| 4fc059beb5 | |||
| 46ec71bf0e | |||
| 33f630111f | |||
| b3ebab308e | |||
| 4e5dfd0cb7 | |||
| 524b1e39a5 | |||
| 32a123fc54 | |||
| 23264ced9a | |||
| 7de0616229 | |||
| 39d3b0e28c | |||
| 5acaacad96 | |||
| a921bcd9ef | |||
| e9171fd792 | |||
| 2009fbbd92 | |||
| 8db2948361 | |||
| 5fa14dbe42 | |||
| e6cd5d208c | |||
| c9d48026c4 | |||
| 34f271504a | |||
| d68100e6bc | |||
| 94e6bf8591 | |||
| fd44718bfe | |||
| f6e21f50fd | |||
| 1725ec346b | |||
| c12fc340c1 | |||
| b6d5374fb7 | |||
| 0082e1ec97 | |||
| 0ed9f1e7f5 | |||
| adf21df71e | |||
| 4dcca1ca10 | |||
| a5a461c234 | |||
| fac3661980 | |||
| d7dfcf7f99 | |||
| 81a97e78f1 | |||
| 1311e8c480 | |||
| 4ecb4bb24b | |||
| be7c7bb83d | |||
| 7bfe6ac418 | |||
| bd34a42ec3 | |||
| e2658ccda8 | |||
| b8719bcd47 | |||
| e1eefffcf1 | |||
| 7ae39fd622 | |||
| fb9264bf80 | |||
| d4397dbfc5 | |||
| 5fb0d1f01d | |||
| 95bca2c3b3 | |||
| e0be1b2afd | |||
| 5d4e4c2814 | |||
| 27a50191e3 | |||
| 77ca3c0e13 | |||
| 2504105a1c | |||
| a361a84708 | |||
| ff196fbe6f | |||
| 2f90b46537 | |||
| 82d8680dcc | |||
| 1f3f7247b1 | |||
| 69e2d8c7ae | |||
| 77a874cf65 | |||
| b211f30d95 | |||
| fc51e50bc6 | |||
| 695bcaea0d | |||
| abe83fce0b | |||
| 61fd71dc29 | |||
| cddd9e7148 | |||
| 30271d64eb | |||
| 605432ea70 | |||
| 990d010ecf | |||
| b6c6da3618 | |||
| 8f22ffd2b1 | |||
| 44ce90d76c | |||
| b91f75cd6d | |||
| dfe22aae21 | |||
| bba9c07541 | |||
| 05fda0cf01 | |||
| d43ec6c8f3 | |||
| 56e0865a7b | |||
| cd2096ca80 | |||
| 48130ebd25 | |||
| ef5627eece | |||
| e3df87cf1a | |||
| 8bad5823a9 | |||
| fd62938945 | |||
| fad032d466 | |||
| 366aa84395 | |||
| 17b37144a9 | |||
| 5ddb517593 | |||
| abd9e23337 | |||
| 6061d8cac7 | |||
| bd590bbde6 | |||
| 0bf7ea60c5 | |||
| 8f2697c2e5 | |||
| c5502b2dc5 | |||
| 9f7691fd88 | |||
| 3682842a5d | |||
| 64c50d32ac | |||
| 604d4ded8d | |||
| f75bdba568 | |||
| 8d68ece8d6 | |||
| b49fc8122d | |||
| b0d151bd65 | |||
| fe8d93c75a | |||
| 24933a90d0 | |||
| fa024133e6 | |||
| 24adacdbc2 | |||
| aa23da67af | |||
| bfbd3c40a7 | |||
| dd8d4c98b3 | |||
| d368997ca3 | |||
| bbd80c9393 | |||
| 3b601b3d90 | |||
| b4bcd1a015 | |||
| aa000d7d30 | |||
| 2a7e602356 | |||
| 8d0b2d7f1b | |||
| c156bac5f7 | |||
| 263b8cd3b3 | |||
| 2d05396dd2 | |||
| 7b4a822b0e | |||
| d44615ac2f | |||
| de656f01d7 | |||
| 1d2585dba6 | |||
| 97bc3f28c5 | |||
| 3038fdce2e | |||
| bb060d7a98 | |||
| 9a73aa4072 | |||
| d7d53981f3 | |||
| 19e0b1ff7d | |||
| 4d393f9dca | |||
| 5abc170b08 | |||
| ceb4c5f6a7 | |||
| b6beab9480 | |||
| c2691f44b6 | |||
| 663d9c0537 | |||
| 4863816b81 | |||
| 829c532703 | |||
| c68303c553 | |||
| 7242d71c01 | |||
| 391715c33c | |||
| 8a537d85e9 | |||
| cd7dced951 | |||
| 73c589f9e3 | |||
| e700a9220b | |||
| 4b76fe0061 | |||
| 867dc0fdda | |||
| 7506b00488 | |||
| 34709dc62d | |||
| 8432bcee75 | |||
| a38aaa47fb | |||
| 18e8dd768a | |||
| 45faf4d31b | |||
| 333475c41f | |||
| 41d4f59f5e | |||
| 10ab958378 | |||
| 949e85ca55 | |||
| f090736ebc | |||
| 35bf746e62 | |||
| 3da1563c30 | |||
| 7380424782 | |||
| 90b53f9a82 | |||
| f368e80baf | |||
| 4a6d1fad9d | |||
| 1a7f50661a | |||
| 8bfa5b5054 | |||
| e3b3b71c14 | |||
| 926dddf0bf | |||
| 352bbc36c0 | |||
| 3bf4f885d8 | |||
| 775bcbf3a6 | |||
| be16caece2 | |||
| e802776c96 | |||
| 45a4a7054e | |||
| b7578eba7d | |||
| b87718d1ff | |||
| 067e09a40b | |||
| 36ce2ba96e | |||
| 08e174a05c | |||
| df8b399bb4 | |||
| 41f1ea4672 | |||
| 92e0da3ecb | |||
| 6cc2f8d06e | |||
| 58557ba786 | |||
| 016d5d8eb6 | |||
| 6900fe5527 | |||
| 7e9e196793 | |||
| 50384ab3c9 | |||
| eaf6e8bbb1 | |||
| b804fe9662 | |||
| 99bbbc2170 | |||
| 3e3d04877b | |||
| 88638c14f7 | |||
| c2d38bac54 | |||
| f16f13fba8 | |||
| e54c450bc1 | |||
| 075e0b1a81 | |||
| f8ad3a200a | |||
| 8b09ccc288 | |||
| 5020d8fa57 | |||
| 3a0c9a0d39 | |||
| 20a226a5ab | |||
| daf3701194 | |||
| 657f19c1f3 | |||
| 524679d23c | |||
| 7c4570339e | |||
| e22d9917b7 | |||
| d63053cb59 | |||
| a9500d6a6c | |||
| acbd914d5e | |||
| 300929a32b | |||
| 519c75f410 | |||
| 3ff68a9e55 | |||
| 5b8ad9cd65 | |||
| 5caa192cfc | |||
| 9eae91a489 | |||
| 1b69637032 | |||
| e5615f47c4 | |||
| bc75a61982 | |||
| 5d213764fb | |||
| be67470432 | |||
| 00a39b3da9 | |||
| 077c1a1e2d | |||
| 0d60d68cf9 | |||
| b404fc02e7 | |||
| b00d7c88ad | |||
| 2dd037682c | |||
| 7aae5435fa | |||
| 13f78bd9eb | |||
| 4da0366eed | |||
| 18112c474e | |||
| 80cf2fe444 | |||
| 0b78de9601 | |||
| a220874281 | |||
| 49ea9b0457 | |||
| 556825f81c | |||
| b158c96465 | |||
| e91f86c248 | |||
| 47e4f6b13f | |||
| 94ab449e65 | |||
| 0486a1675a | |||
| f9fc83089c | |||
| 5dd2dab189 | |||
| a6b95897ad | |||
| 6ae6c810ba | |||
| 02d4451e77 | |||
| c25ff94608 | |||
| 14412c3a72 | |||
| ec7773eb7b | |||
| 4653b126f3 | |||
| 1e1e7e349d | |||
| 43eb74ac59 | |||
| 215f8f3f15 | |||
| b89944c3a3 | |||
| f88488d1f9 | |||
| 1fd42802be | |||
| ab64b15d51 | |||
| e406dcc249 | |||
| 680077631d | |||
| 9fc03a0c12 | |||
| 4f4431e4e1 | |||
| 527074b50a | |||
| a17691f0fc | |||
| d246315cea | |||
| e92ccec6c8 | |||
| b68d7bc0f9 | |||
| 4c9f9bb3e2 | |||
| e7b20c49ac | |||
| 743d05b37f | |||
| 95074a1a84 | |||
| 759575faa8 | |||
| d485e08606 | |||
| 0f1258305a | |||
| 09e99824d4 | |||
| 96b939f63a | |||
| 7837194ab5 | |||
| 35ee2a841a | |||
| a253938ac5 | |||
| 37ffd608fd | |||
| 0f019122b0 | |||
| 936f6240dd | |||
| f8dd6f4f4c | |||
| ca7ac00030 | |||
| d41735d6a9 | |||
| d012929a28 | |||
| 97dfbd4598 | |||
| 237864eb63 | |||
| dc6741097c | |||
| dac3735626 | |||
| 245b68e9f1 | |||
| e89cf5d86e | |||
| 54b0344fc5 | |||
| e5d58c2b5a | |||
| 9455ecd78c | |||
| 0fd09e0150 | |||
| 9a7427197b | |||
| 33be30ab04 | |||
| 6c3a90645a | |||
| 32e61529f5 | |||
| 931e668b47 | |||
| 7989c28d2e | |||
| 2965c115f1 | |||
| 7d31d5f4ec | |||
| 1a7d4184cd | |||
| 7dce23e5d9 | |||
| 06a176e33e | |||
| 42d367d72f | |||
| 6f579934db | |||
| 6d607a5953 | |||
| 4669148a4c | |||
| 82316ef6e4 | |||
| 337e4bc8c6 | |||
| d97eaf3420 | |||
| 0452f787b2 | |||
| c1317aafb3 | |||
| f17f05373e | |||
| 6691fac50e | |||
| 35c1999341 | |||
| 5575c5ff66 | |||
| 0833aca64b | |||
| 509d4ae0a9 | |||
| 4d310dda68 | |||
| e7be0d7bcc | |||
| a8f507352b | |||
| 6aa6630137 | |||
| 7ec477d40d | |||
| 19c9508fd1 | |||
| e8bc7bea44 | |||
| 1ba9d71284 | |||
| 1aa4a32066 | |||
| 22d962e761 | |||
| 0135b03c8a | |||
| 9773a084c9 | |||
| a830858f91 | |||
| f47cb3c136 | |||
| 291639633f | |||
| c7e2dbe0cf | |||
| 31d65f40bd | |||
| 93d9adc03c | |||
| d2726d4a43 | |||
| 1c3c7a343f | |||
| f20e45175e | |||
| 0228c2b9f0 | |||
| ddafd79661 | |||
| 09259a8ec4 | |||
| 173376ba67 | |||
| 013d5dfbb0 |
@@ -0,0 +1,89 @@
|
||||
# --- STAGE 1: Base Runtime ---
|
||||
FROM docker.io/library/node:20-slim AS base
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
curl \
|
||||
dnsutils \
|
||||
less \
|
||||
jq \
|
||||
ca-certificates \
|
||||
git \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# --- STAGE 2: Builder (Compile Main) ---
|
||||
FROM base AS builder
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
RUN npm ci --ignore-scripts
|
||||
RUN npm run bundle
|
||||
# Run the official release preparation script to move the bundle and assets into packages/cli
|
||||
RUN node scripts/prepare-npm-release.js
|
||||
|
||||
# --- STAGE 3: Development Environment ---
|
||||
FROM base AS development
|
||||
|
||||
WORKDIR /home/node/dev/main
|
||||
|
||||
# Set up npm global package folder
|
||||
RUN mkdir -p /usr/local/share/npm-global \
|
||||
&& chown -R node:node /usr/local/share/npm-global
|
||||
ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
|
||||
ENV PATH=$PATH:/usr/local/share/npm-global/bin
|
||||
|
||||
# Copy package.json to extract versions for global tools
|
||||
COPY package.json /tmp/package.json
|
||||
|
||||
# Install Build Tools, Global Dev Tools (pinned), and Linters
|
||||
ARG ACTIONLINT_VER=1.7.7
|
||||
ARG SHELLCHECK_VER=0.11.0
|
||||
ARG YAMLLINT_VER=1.35.1
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
make \
|
||||
g++ \
|
||||
gh \
|
||||
git \
|
||||
unzip \
|
||||
rsync \
|
||||
ripgrep \
|
||||
procps \
|
||||
psmisc \
|
||||
lsof \
|
||||
socat \
|
||||
tmux \
|
||||
docker.io \
|
||||
build-essential \
|
||||
libsecret-1-dev \
|
||||
libkrb5-dev \
|
||||
file \
|
||||
&& curl -sSLo /tmp/actionlint.tar.gz https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VER}/actionlint_${ACTIONLINT_VER}_linux_amd64.tar.gz \
|
||||
&& tar -xzf /tmp/actionlint.tar.gz -C /usr/local/bin actionlint \
|
||||
&& curl -sSLo /tmp/shellcheck.tar.xz https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VER}/shellcheck-v${SHELLCHECK_VER}.linux.x86_64.tar.xz \
|
||||
&& tar -xf /tmp/shellcheck.tar.xz -C /usr/local/bin --strip-components=1 shellcheck-v${SHELLCHECK_VER}/shellcheck \
|
||||
&& pip3 install --break-system-packages yamllint==${YAMLLINT_VER} \
|
||||
&& export TSX_VER=$(node -p "require('/tmp/package.json').devDependencies.tsx") \
|
||||
&& export VITEST_VER=$(node -p "require('/tmp/package.json').devDependencies.vitest") \
|
||||
&& export PRETTIER_VER=$(node -p "require('/tmp/package.json').devDependencies.prettier") \
|
||||
&& export ESLINT_VER=$(node -p "require('/tmp/package.json').devDependencies.eslint") \
|
||||
&& export CROSS_ENV_VER=$(node -p "require('/tmp/package.json').devDependencies['cross-env']") \
|
||||
&& npm install -g tsx@$TSX_VER vitest@$VITEST_VER prettier@$PRETTIER_VER eslint@$ESLINT_VER cross-env@$CROSS_ENV_VER typescript@5.3.3 \
|
||||
&& npm install -g @google/gemini-cli@nightly && mv /usr/local/share/npm-global/bin/gemini /usr/local/share/npm-global/bin/g-nightly \
|
||||
&& npm install -g @google/gemini-cli@preview && mv /usr/local/share/npm-global/bin/gemini /usr/local/share/npm-global/bin/g-preview \
|
||||
&& npm install -g @google/gemini-cli@latest && mv /usr/local/share/npm-global/bin/gemini /usr/local/share/npm-global/bin/g-stable \
|
||||
&& apt-get purge -y build-essential libsecret-1-dev libkrb5-dev \
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /root/.npm
|
||||
|
||||
# Copy the bundled CLI package to a permanent location and install it
|
||||
# We MUST not delete this source folder as 'npm install -g <folder>'
|
||||
# often symlinks to it for local folder installs.
|
||||
COPY --from=builder /build/packages/cli /usr/local/lib/gemini-cli
|
||||
RUN npm install -g /usr/local/lib/gemini-cli
|
||||
|
||||
USER node
|
||||
CMD ["/bin/bash"]
|
||||
@@ -0,0 +1,10 @@
|
||||
node_modules
|
||||
.git
|
||||
.gemini/workspaces
|
||||
dist
|
||||
!packages/*/dist/*.tgz
|
||||
bundle
|
||||
out
|
||||
*.log
|
||||
.env
|
||||
.DS_Store
|
||||
@@ -0,0 +1,58 @@
|
||||
substitutions:
|
||||
_IMAGE_NAME: 'development'
|
||||
_ARTIFACT_REGISTRY_REPO: 'us-docker.pkg.dev/gemini-code-dev/gemini-cli'
|
||||
|
||||
steps:
|
||||
# Step 1: Install root dependencies
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Install Dependencies'
|
||||
entrypoint: 'npm'
|
||||
args: ['install']
|
||||
|
||||
# Step 2: Authenticate for Docker
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Authenticate docker'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'auth']
|
||||
|
||||
# Step 3: Build workspace packages
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Build packages'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'build:packages']
|
||||
|
||||
# Step 4: Build Development Image
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Build Development Image'
|
||||
entrypoint: 'bash'
|
||||
env:
|
||||
- 'RAW_BRANCH_VALUE=${BRANCH_NAME}'
|
||||
args:
|
||||
- '-c'
|
||||
- |-
|
||||
IMAGE_BASE="${_ARTIFACT_REGISTRY_REPO}/${_IMAGE_NAME}"
|
||||
|
||||
# Determine the primary tag (branch name or 'latest' for main)
|
||||
# Use $$ for shell variables to avoid Cloud Build attempting premature substitution
|
||||
RAW_BRANCH="$$RAW_BRANCH_VALUE"
|
||||
if [ "$${RAW_BRANCH}" == "main" ]; then
|
||||
TAG_PRIMARY="latest"
|
||||
else
|
||||
TAG_PRIMARY=$$(echo "$${RAW_BRANCH}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]')
|
||||
fi
|
||||
|
||||
# Use SHORT_SHA if available (Cloud Build) or fallback to latest-dev
|
||||
TAG_SHA="$${SHORT_SHA:-latest-dev}"
|
||||
|
||||
echo "📦 Building Development Image for: $${RAW_BRANCH} -> $${TAG_PRIMARY} ($${TAG_SHA})"
|
||||
|
||||
docker build -f .gcp/Dockerfile.development \
|
||||
-t "$${IMAGE_BASE}:$${TAG_SHA}" \
|
||||
-t "$${IMAGE_BASE}:$${TAG_PRIMARY}" .
|
||||
|
||||
docker push "$${IMAGE_BASE}:$${TAG_SHA}"
|
||||
docker push "$${IMAGE_BASE}:$${TAG_PRIMARY}"
|
||||
|
||||
options:
|
||||
defaultLogsBucketBehavior: 'REGIONAL_USER_OWNED_BUCKET'
|
||||
dynamicSubstitutions: true
|
||||
@@ -1,60 +0,0 @@
|
||||
description = "Check status of nightly evals, fix failures for key models, and re-run."
|
||||
prompt = """
|
||||
You are an expert at fixing behavioral evaluations.
|
||||
|
||||
1. **Investigate**:
|
||||
- Use 'gh' cli to fetch the results from the latest run from the main branch: https://github.com/google-gemini/gemini-cli/actions/workflows/evals-nightly.yml.
|
||||
- DO NOT push any changes or start any runs. The rest of your evaluation will be local.
|
||||
- Evals are in evals/ directory and are documented by evals/README.md.
|
||||
- The test case trajectory logs will be logged to evals/logs.
|
||||
- You should also enable and review the verbose agent logs by setting the GEMINI_DEBUG_LOG_FILE environment variable.
|
||||
- Identify the relevant test. Confine your investigation and validation to just this test.
|
||||
- Proactively add logging that will aid in gathering information or validating your hypotheses.
|
||||
|
||||
2. **Fix**:
|
||||
- If a relevant test is failing, locate the test file and the corresponding prompt/code.
|
||||
- It's often helpful to make an extreme, brute force change to see if you are changing the right place to make an improvement and then scope it back iteratively.
|
||||
- Your **final** change should be **minimal and targeted**.
|
||||
- Keep in mind the following:
|
||||
- The prompt has multiple configurations and pieces. Take care that your changes
|
||||
end up in the final prompt for the selected model and configuration.
|
||||
- The prompt chosen for the eval is intentional. It's often vague or indirect
|
||||
to see how the agent performs with ambiguous instructions. Changing it should
|
||||
be a last resort.
|
||||
- When changing the test prompt, carefully consider whether the prompt still tests
|
||||
the same scenario. We don't want to lose test fidelity by making the prompts too
|
||||
direct (i.e.: easy).
|
||||
- Your primary mechanism for improving the agent's behavior is to make changes to
|
||||
tool instructions, system prompt (snippets.ts), and/or modules that contribute to the prompt.
|
||||
- If prompt and description changes are unsuccessful, use logs and debugging to
|
||||
confirm that everything is working as expected.
|
||||
- If unable to fix the test, you can make recommendations for architecture changes
|
||||
that might help stablize the test. Be sure to THINK DEEPLY if offering architecture guidance.
|
||||
Some facts that might help with this are:
|
||||
- Agents may be composed of one or more agent loops.
|
||||
- AgentLoop == 'context + toolset + prompt'. Subagents are one type of agent loop.
|
||||
- Agent loops perform better when:
|
||||
- They have direct, unambiguous, and non-contradictory prompts.
|
||||
- They have fewer irrelevant tools.
|
||||
- They have fewer goals or steps to perform.
|
||||
- They have less low value or irrelevant context.
|
||||
- You may suggest compositions of existing primitives, like subagents, or
|
||||
propose a new one.
|
||||
- These recommendations should be high confidence and should be grounded
|
||||
in observed deficient behaviors rather than just parroting the facts above.
|
||||
Investigate as needed to ground your recommendations.
|
||||
|
||||
3. **Verify**:
|
||||
- Run just that one test if needed to validate that it is fixed. Be sure to run vitest in non-interactive mode.
|
||||
- Running the tests can take a long time, so consider whether you can diagnose via other means or log diagnostics before committing the time. You must minimize the number of test runs needed to diagnose the failure.
|
||||
- After the test completes, check whether it seems to have improved.
|
||||
- You will need to run the test 3 times for Gemini 3.0, Gemini 3 flash, and Gemini 2.5 pro to ensure that it is truly stable. Run these runs in parallel, using scripts if needed.
|
||||
- Some flakiness is expected; if it looks like a transient issue or the test is inherently unstable but passes 2/3 times, you might decide it cannot be improved.
|
||||
|
||||
4. **Report**:
|
||||
- Provide a summary of the test success rate for each of the tested models.
|
||||
- Success rate is calculated based on 3 runs per model (e.g., 3/3 = 100%).
|
||||
- If you couldn't fix it due to persistent flakiness, explain why.
|
||||
|
||||
{{args}}
|
||||
"""
|
||||
@@ -1,29 +0,0 @@
|
||||
description = "Promote behavioral evals that have a 100% success rate over the last 7 nightly runs."
|
||||
prompt = """
|
||||
You are an expert at analyzing and promoting behavioral evaluations.
|
||||
|
||||
1. **Investigate**:
|
||||
- Use 'gh' cli to fetch the results from the most recent run from the main branch: https://github.com/google-gemini/gemini-cli/actions/workflows/evals-nightly.yml.
|
||||
- DO NOT push any changes or start any runs. The rest of your evaluation will be local.
|
||||
- Evals are in evals/ directory and are documented by evals/README.md.
|
||||
- Identify tests that have passed 100% of the time for ALL enabled models across the past 7 runs in a row.
|
||||
- NOTE: the results summary from the most recent run contains the last 7 runs test results. 100% means the test passed 3/3 times for that model and run.
|
||||
- If a test meets this criteria, it is a candidate for promotion.
|
||||
|
||||
2. **Promote**:
|
||||
- For each candidate test, locate the test file in the evals/ directory.
|
||||
- Promote the test according to the project's standard promotion process (e.g., moving it to a stable suite, updating its tags, or removing skip/flaky annotations).
|
||||
- Ensure you follow any guidelines in evals/README.md for stable tests.
|
||||
- Your **final** change should be **minimal and targeted** to just promoting the test status.
|
||||
|
||||
3. **Verify**:
|
||||
- Run the promoted tests locally to validate that they still execute correctly. Be sure to run vitest in non-interactive mode.
|
||||
- Check that the test is now part of the expected standard or stable test suites.
|
||||
|
||||
4. **Report**:
|
||||
- Provide a summary of the tests that were promoted.
|
||||
- Include the success rate evidence (7/7 runs passed for all models) for each promoted test.
|
||||
- If no tests met the criteria for promotion, clearly state that and summarize the closest candidates.
|
||||
|
||||
{{args}}
|
||||
"""
|
||||
@@ -107,7 +107,7 @@ Gemini CLI project.
|
||||
set.
|
||||
- **Logging**: Use `debugLogger` for rethrown errors to avoid duplicate logging.
|
||||
- **Keyboard Shortcuts**: Define all new keyboard shortcuts in
|
||||
`packages/cli/src/config/keyBindings.ts` and document them in
|
||||
`packages/cli/src/ui/key/keyBindings.ts` and document them in
|
||||
`docs/cli/keyboard-shortcuts.md`. Be careful of keybindings that require the
|
||||
`Meta` key, as only certain meta key shortcuts are supported on Mac. Avoid
|
||||
function keys and shortcuts commonly bound in VSCode.
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"experimental": {
|
||||
"plan": true,
|
||||
"extensionReloading": true,
|
||||
"modelSteering": true
|
||||
"modelSteering": true,
|
||||
"memoryManager": false,
|
||||
"topicUpdateNarration": true
|
||||
},
|
||||
"general": {
|
||||
"devtools": true
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
name: async-pr-review
|
||||
description: Trigger this skill when the user wants to start an asynchronous PR review, run background checks on a PR, or check the status of a previously started async PR review.
|
||||
---
|
||||
|
||||
# Async PR Review
|
||||
|
||||
This skill provides a set of tools to asynchronously review a Pull Request. It will create a background job to run the project's preflight checks, execute Gemini-powered test plans, and perform a comprehensive code review using custom prompts.
|
||||
|
||||
This skill is designed to showcase an advanced "Agentic Asynchronous Pattern":
|
||||
1. **Native Background Shells vs Headless Inference**: While Gemini CLI can natively spawn and detach background shell commands (using the `run_shell_command` tool with `is_background: true`), a standard bash background job cannot perform LLM inference. To conduct AI-driven code reviews and test generation in the background, the shell script *must* invoke the `gemini` executable headlessly using `-p`. This offloads the AI tasks to independent worker agents.
|
||||
2. **Dynamic Git Scoping**: The review scripts avoid hardcoded paths. They use `git rev-parse --show-toplevel` to automatically resolve the root of the user's current project.
|
||||
3. **Ephemeral Worktrees**: Instead of checking out branches in the user's main workspace, the skill provisions temporary git worktrees in `.gemini/tmp/async-reviews/pr-<number>`. This prevents git lock conflicts and namespace pollution.
|
||||
4. **Agentic Evaluation (`check-async-review.sh`)**: The check script outputs clean JSON/text statuses for the main agent to parse. The interactive agent itself synthesizes the final assessment dynamically from the generated log files.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Determine Action**: Establish whether the user wants to start a new async review or check the status of an existing one.
|
||||
* If the user says "start an async review for PR #123" or similar, proceed to **Start Review**.
|
||||
* If the user says "check the status of my async review for PR #123" or similar, proceed to **Check Status**.
|
||||
|
||||
### Start Review
|
||||
|
||||
If the user wants to start a new async PR review:
|
||||
|
||||
1. Ask the user for the PR number if they haven't provided it.
|
||||
2. Execute the `async-review.sh` script, passing the PR number as the first argument. Be sure to run it with the `is_background` flag set to true to ensure it immediately detaches.
|
||||
```bash
|
||||
.gemini/skills/async-pr-review/scripts/async-review.sh <PR_NUMBER>
|
||||
```
|
||||
3. Inform the user that the tasks have started successfully and they can check the status later.
|
||||
|
||||
### Check Status
|
||||
|
||||
If the user wants to check the status or view the final assessment of a previously started async review:
|
||||
|
||||
1. Ask the user for the PR number if they haven't provided it.
|
||||
2. Execute the `check-async-review.sh` script, passing the PR number as the first argument:
|
||||
```bash
|
||||
.gemini/skills/async-pr-review/scripts/check-async-review.sh <PR_NUMBER>
|
||||
```
|
||||
3. **Evaluate Output**: Read the output from the script.
|
||||
* If the output contains `STATUS: IN_PROGRESS`, tell the user which tasks are still running.
|
||||
* If the output contains `STATUS: COMPLETE`, use your file reading tools (`read_file`) to retrieve the contents of `final-assessment.md`, `review.md`, `pr-diff.diff`, `npm-test.log`, and `test-execution.log` files from the `LOG_DIR` specified in the output.
|
||||
* **Final Assessment**: Read those files, synthesize their results, and give the user a concise recommendation on whether the PR builds successfully, passes tests, and if you recommend they approve it based on the review.
|
||||
@@ -0,0 +1,148 @@
|
||||
# --- CORE TOOLS ---
|
||||
[[rule]]
|
||||
toolName = "read_file"
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
[[rule]]
|
||||
toolName = "write_file"
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
[[rule]]
|
||||
toolName = "grep_search"
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
[[rule]]
|
||||
toolName = "glob"
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
[[rule]]
|
||||
toolName = "list_directory"
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
[[rule]]
|
||||
toolName = "codebase_investigator"
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
# --- SHELL COMMANDS ---
|
||||
|
||||
# Git (Safe/Read-only)
|
||||
[[rule]]
|
||||
toolName = "run_shell_command"
|
||||
commandPrefix = [
|
||||
"git blame",
|
||||
"git show",
|
||||
"git grep",
|
||||
"git show-ref",
|
||||
"git ls-tree",
|
||||
"git ls-remote",
|
||||
"git reflog",
|
||||
"git remote -v",
|
||||
"git diff",
|
||||
"git rev-list",
|
||||
"git rev-parse",
|
||||
"git merge-base",
|
||||
"git cherry",
|
||||
"git fetch",
|
||||
"git status",
|
||||
"git st",
|
||||
"git branch",
|
||||
"git br",
|
||||
"git log",
|
||||
"git --version"
|
||||
]
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
# GitHub CLI (Read-only)
|
||||
[[rule]]
|
||||
toolName = "run_shell_command"
|
||||
commandPrefix = [
|
||||
"gh workflow list",
|
||||
"gh auth status",
|
||||
"gh checkout view",
|
||||
"gh run view",
|
||||
"gh run job view",
|
||||
"gh run list",
|
||||
"gh run --help",
|
||||
"gh issue view",
|
||||
"gh issue list",
|
||||
"gh label list",
|
||||
"gh pr diff",
|
||||
"gh pr check",
|
||||
"gh pr checks",
|
||||
"gh pr view",
|
||||
"gh pr list",
|
||||
"gh pr status",
|
||||
"gh repo view",
|
||||
"gh job view",
|
||||
"gh api",
|
||||
"gh log"
|
||||
]
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
# Node.js/NPM (Generic Tests, Checks, and Build)
|
||||
[[rule]]
|
||||
toolName = "run_shell_command"
|
||||
commandPrefix = [
|
||||
"npm run start",
|
||||
"npm install",
|
||||
"npm run",
|
||||
"npm test",
|
||||
"npm ci",
|
||||
"npm list",
|
||||
"npm --version"
|
||||
]
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
|
||||
# Core Utilities (Safe)
|
||||
[[rule]]
|
||||
toolName = "run_shell_command"
|
||||
commandPrefix = [
|
||||
"sleep",
|
||||
"env",
|
||||
"break",
|
||||
"xargs",
|
||||
"base64",
|
||||
"uniq",
|
||||
"sort",
|
||||
"echo",
|
||||
"which",
|
||||
"ls",
|
||||
"find",
|
||||
"tail",
|
||||
"head",
|
||||
"cat",
|
||||
"cd",
|
||||
"grep",
|
||||
"ps",
|
||||
"pwd",
|
||||
"wc",
|
||||
"file",
|
||||
"stat",
|
||||
"diff",
|
||||
"lsof",
|
||||
"date",
|
||||
"whoami",
|
||||
"uname",
|
||||
"du",
|
||||
"cut",
|
||||
"true",
|
||||
"false",
|
||||
"readlink",
|
||||
"awk",
|
||||
"jq",
|
||||
"rg",
|
||||
"less",
|
||||
"more",
|
||||
"tree"
|
||||
]
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
@@ -0,0 +1,245 @@
|
||||
#!/bin/bash
|
||||
|
||||
notify() {
|
||||
local title="${1}"
|
||||
local message="${2}"
|
||||
local pr="${3}"
|
||||
# Terminal escape sequence
|
||||
printf "\e]9;%s | PR #%s | %s\a" "${title}" "${pr}" "${message}"
|
||||
# Native macOS notification
|
||||
os_type="$(uname || true)"
|
||||
if [[ "${os_type}" == "Darwin" ]]; then
|
||||
osascript -e "display notification \"${message}\" with title \"${title}\" subtitle \"PR #${pr}\""
|
||||
fi
|
||||
}
|
||||
|
||||
pr_number="${1}"
|
||||
if [[ -z "${pr_number}" ]]; then
|
||||
echo "Usage: async-review <pr_number>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
base_dir="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
if [[ -z "${base_dir}" ]]; then
|
||||
echo "❌ Must be run from within a git repository."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use the repository's local .gemini/tmp directory for ephemeral worktrees and logs
|
||||
pr_dir="${base_dir}/.gemini/tmp/async-reviews/pr-${pr_number}"
|
||||
target_dir="${pr_dir}/worktree"
|
||||
log_dir="${pr_dir}/logs"
|
||||
|
||||
cd "${base_dir}" || exit 1
|
||||
|
||||
mkdir -p "${log_dir}"
|
||||
rm -f "${log_dir}/setup.exit" "${log_dir}/final-assessment.exit" "${log_dir}/final-assessment.md"
|
||||
|
||||
echo "🧹 Cleaning up previous worktree if it exists..." | tee -a "${log_dir}/setup.log"
|
||||
git worktree remove -f "${target_dir}" >> "${log_dir}/setup.log" 2>&1 || true
|
||||
git branch -D "gemini-async-pr-${pr_number}" >> "${log_dir}/setup.log" 2>&1 || true
|
||||
git worktree prune >> "${log_dir}/setup.log" 2>&1 || true
|
||||
|
||||
echo "📡 Fetching PR #${pr_number}..." | tee -a "${log_dir}/setup.log"
|
||||
if ! git fetch origin -f "pull/${pr_number}/head:gemini-async-pr-${pr_number}" >> "${log_dir}/setup.log" 2>&1; then
|
||||
echo 1 > "${log_dir}/setup.exit"
|
||||
echo "❌ Fetch failed. Check ${log_dir}/setup.log"
|
||||
notify "Async Review Failed" "Fetch failed." "${pr_number}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "${target_dir}" ]]; then
|
||||
echo "🧹 Pruning missing worktrees..." | tee -a "${log_dir}/setup.log"
|
||||
git worktree prune >> "${log_dir}/setup.log" 2>&1
|
||||
echo "🌿 Creating worktree in ${target_dir}..." | tee -a "${log_dir}/setup.log"
|
||||
if ! git worktree add "${target_dir}" "gemini-async-pr-${pr_number}" >> "${log_dir}/setup.log" 2>&1; then
|
||||
echo 1 > "${log_dir}/setup.exit"
|
||||
echo "❌ Worktree creation failed. Check ${log_dir}/setup.log"
|
||||
notify "Async Review Failed" "Worktree creation failed." "${pr_number}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "🌿 Worktree already exists." | tee -a "${log_dir}/setup.log"
|
||||
fi
|
||||
echo 0 > "${log_dir}/setup.exit"
|
||||
|
||||
cd "${target_dir}" || exit 1
|
||||
|
||||
echo "🚀 Launching background tasks. Logs saving to: ${log_dir}"
|
||||
|
||||
echo " ↳ [1/5] Grabbing PR diff..."
|
||||
rm -f "${log_dir}/pr-diff.exit"
|
||||
{ gh pr diff "${pr_number}" > "${log_dir}/pr-diff.diff" 2>&1; echo $? > "${log_dir}/pr-diff.exit"; } &
|
||||
|
||||
echo " ↳ [2/5] Starting build and lint..."
|
||||
rm -f "${log_dir}/build-and-lint.exit"
|
||||
{ { npm run clean && npm ci && npm run format && npm run build && npm run lint:ci && npm run typecheck; } > "${log_dir}/build-and-lint.log" 2>&1; echo $? > "${log_dir}/build-and-lint.exit"; } &
|
||||
|
||||
# Dynamically resolve gemini binary (fallback to your nightly path)
|
||||
GEMINI_CMD="$(command -v gemini || echo "${HOME}/.gcli/nightly/node_modules/.bin/gemini")"
|
||||
# shellcheck disable=SC2312
|
||||
POLICY_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/policy.toml"
|
||||
|
||||
echo " ↳ [3/5] Starting Gemini code review..."
|
||||
rm -f "${log_dir}/review.exit"
|
||||
{ "${GEMINI_CMD}" --policy "${POLICY_PATH}" -p "/review-frontend ${pr_number}" > "${log_dir}/review.md" 2>&1; echo $? > "${log_dir}/review.exit"; } &
|
||||
|
||||
echo " ↳ [4/5] Starting automated tests (waiting for build and lint)..."
|
||||
rm -f "${log_dir}/npm-test.exit"
|
||||
{
|
||||
while [[ ! -f "${log_dir}/build-and-lint.exit" ]]; do sleep 1; done
|
||||
read -r build_exit < "${log_dir}/build-and-lint.exit" || build_exit=""
|
||||
if [[ "${build_exit}" == "0" ]]; then
|
||||
gh pr checks "${pr_number}" > "${log_dir}/ci-checks.log" 2>&1
|
||||
ci_status=$?
|
||||
|
||||
if [[ "${ci_status}" -eq 0 ]]; then
|
||||
echo "CI checks passed. Skipping local npm tests." > "${log_dir}/npm-test.log"
|
||||
echo 0 > "${log_dir}/npm-test.exit"
|
||||
elif [[ "${ci_status}" -eq 8 ]]; then
|
||||
echo "CI checks are still pending. Skipping local npm tests to avoid duplicate work. Please check GitHub for final results." > "${log_dir}/npm-test.log"
|
||||
echo 0 > "${log_dir}/npm-test.exit"
|
||||
else
|
||||
echo "CI checks failed. Failing checks:" > "${log_dir}/npm-test.log"
|
||||
gh pr checks "${pr_number}" --json name,bucket -q '.[] | select(.bucket=="fail") | .name' >> "${log_dir}/npm-test.log" 2>&1
|
||||
|
||||
echo "Attempting to extract failing test files from CI logs..." >> "${log_dir}/npm-test.log"
|
||||
pr_branch="$(gh pr view "${pr_number}" --json headRefName -q '.headRefName' 2>/dev/null || true)"
|
||||
run_id="$(gh run list --branch "${pr_branch}" --workflow ci.yml --json databaseId -q '.[0].databaseId' 2>/dev/null || true)"
|
||||
|
||||
failed_files=""
|
||||
if [[ -n "${run_id}" ]]; then
|
||||
failed_files="$(gh run view "${run_id}" --log-failed 2>/dev/null | grep -o -E '(packages/[a-zA-Z0-9_-]+|integration-tests|evals)/[a-zA-Z0-9_/-]+\.test\.ts(x)?' | sort | uniq || true)"
|
||||
fi
|
||||
|
||||
if [[ -n "${failed_files}" ]]; then
|
||||
echo "Found failing test files from CI:" >> "${log_dir}/npm-test.log"
|
||||
for f in ${failed_files}; do echo " - ${f}" >> "${log_dir}/npm-test.log"; done
|
||||
echo "Running ONLY failing tests locally..." >> "${log_dir}/npm-test.log"
|
||||
|
||||
exit_code=0
|
||||
for file in ${failed_files}; do
|
||||
if [[ "${file}" == packages/* ]]; then
|
||||
ws_dir="$(echo "${file}" | cut -d'/' -f1,2)"
|
||||
else
|
||||
ws_dir="$(echo "${file}" | cut -d'/' -f1)"
|
||||
fi
|
||||
rel_file="${file#"${ws_dir}"/}"
|
||||
|
||||
echo "--- Running ${rel_file} in workspace ${ws_dir} ---" >> "${log_dir}/npm-test.log"
|
||||
if ! npm run test:ci -w "${ws_dir}" -- "${rel_file}" >> "${log_dir}/npm-test.log" 2>&1; then
|
||||
exit_code=1
|
||||
fi
|
||||
done
|
||||
echo "${exit_code}" > "${log_dir}/npm-test.exit"
|
||||
else
|
||||
echo "Could not extract specific failing files. Skipping full local test suite as it takes too long. Please check CI logs manually." >> "${log_dir}/npm-test.log"
|
||||
echo 1 > "${log_dir}/npm-test.exit"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Skipped due to build-and-lint failure" > "${log_dir}/npm-test.log"
|
||||
echo 1 > "${log_dir}/npm-test.exit"
|
||||
fi
|
||||
} &
|
||||
|
||||
echo " ↳ [5/5] Starting Gemini test execution (waiting for build and lint)..."
|
||||
rm -f "${log_dir}/test-execution.exit"
|
||||
{
|
||||
while [[ ! -f "${log_dir}/build-and-lint.exit" ]]; do sleep 1; done
|
||||
read -r build_exit < "${log_dir}/build-and-lint.exit" || build_exit=""
|
||||
if [[ "${build_exit}" == "0" ]]; then
|
||||
"${GEMINI_CMD}" --policy "${POLICY_PATH}" -p "Analyze the diff for PR ${pr_number} using 'gh pr diff ${pr_number}'. Instead of running the project's automated test suite (like 'npm test'), physically exercise the newly changed code in the terminal (e.g., by writing a temporary script to call the new functions, or testing the CLI command directly). Verify the feature's behavior works as expected. IMPORTANT: Do NOT modify any source code to fix errors. Just exercise the code and log the results, reporting any failures clearly. Do not ask for user confirmation." > "${log_dir}/test-execution.log" 2>&1; echo $? > "${log_dir}/test-execution.exit"
|
||||
else
|
||||
echo "Skipped due to build-and-lint failure" > "${log_dir}/test-execution.log"
|
||||
echo 1 > "${log_dir}/test-execution.exit"
|
||||
fi
|
||||
} &
|
||||
|
||||
echo "✅ All tasks dispatched!"
|
||||
echo "You can monitor progress with: tail -f ${log_dir}/*.log"
|
||||
echo "Read your review later at: ${log_dir}/review.md"
|
||||
|
||||
# Polling loop to wait for all background tasks to finish
|
||||
tasks=("pr-diff" "build-and-lint" "review" "npm-test" "test-execution")
|
||||
log_files=("pr-diff.diff" "build-and-lint.log" "review.md" "npm-test.log" "test-execution.log")
|
||||
|
||||
declare -A task_done
|
||||
for t in "${tasks[@]}"; do task_done[${t}]=0; done
|
||||
|
||||
all_done=0
|
||||
while [[ "${all_done}" -eq 0 ]]; do
|
||||
clear
|
||||
echo "=================================================="
|
||||
echo "🚀 Async PR Review Status for PR #${pr_number}"
|
||||
echo "=================================================="
|
||||
echo ""
|
||||
|
||||
all_done=1
|
||||
for i in "${!tasks[@]}"; do
|
||||
t="${tasks[${i}]}"
|
||||
|
||||
if [[ -f "${log_dir}/${t}.exit" ]]; then
|
||||
read -r task_exit < "${log_dir}/${t}.exit" || task_exit=""
|
||||
if [[ "${task_exit}" == "0" ]]; then
|
||||
echo " ✅ ${t}: SUCCESS"
|
||||
else
|
||||
echo " ❌ ${t}: FAILED (exit code ${task_exit})"
|
||||
fi
|
||||
task_done[${t}]=1
|
||||
else
|
||||
echo " ⏳ ${t}: RUNNING"
|
||||
all_done=0
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=================================================="
|
||||
echo "📝 Live Logs (Last 5 lines of running tasks)"
|
||||
echo "=================================================="
|
||||
|
||||
for i in "${!tasks[@]}"; do
|
||||
t="${tasks[${i}]}"
|
||||
log_file="${log_files[${i}]}"
|
||||
|
||||
if [[ "${task_done[${t}]}" -eq 0 ]]; then
|
||||
if [[ -f "${log_dir}/${log_file}" ]]; then
|
||||
echo ""
|
||||
echo "--- ${t} ---"
|
||||
tail -n 5 "${log_dir}/${log_file}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "${all_done}" -eq 0 ]]; then
|
||||
sleep 3
|
||||
fi
|
||||
done
|
||||
|
||||
clear
|
||||
echo "=================================================="
|
||||
echo "🚀 Async PR Review Status for PR #${pr_number}"
|
||||
echo "=================================================="
|
||||
echo ""
|
||||
for t in "${tasks[@]}"; do
|
||||
read -r task_exit < "${log_dir}/${t}.exit" || task_exit=""
|
||||
if [[ "${task_exit}" == "0" ]]; then
|
||||
echo " ✅ ${t}: SUCCESS"
|
||||
else
|
||||
echo " ❌ ${t}: FAILED (exit code ${task_exit})"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
echo "⏳ Tasks complete! Synthesizing final assessment..."
|
||||
if ! "${GEMINI_CMD}" --policy "${POLICY_PATH}" -p "Read the review at ${log_dir}/review.md, the automated test logs at ${log_dir}/npm-test.log, and the manual test execution logs at ${log_dir}/test-execution.log. Summarize the results, state whether the build and tests passed based on ${log_dir}/build-and-lint.exit and ${log_dir}/npm-test.exit, and give a final recommendation for PR ${pr_number}." > "${log_dir}/final-assessment.md" 2>&1; then
|
||||
echo $? > "${log_dir}/final-assessment.exit"
|
||||
echo "❌ Final assessment synthesis failed!"
|
||||
echo "Check ${log_dir}/final-assessment.md for details."
|
||||
notify "Async Review Failed" "Final assessment synthesis failed." "${pr_number}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 0 > "${log_dir}/final-assessment.exit"
|
||||
echo "✅ Final assessment complete! Check ${log_dir}/final-assessment.md"
|
||||
notify "Async Review Complete" "Review and test execution finished successfully." "${pr_number}"
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
pr_number="${1}"
|
||||
|
||||
if [[ -z "${pr_number}" ]]; then
|
||||
echo "Usage: check-async-review <pr_number>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
base_dir="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
if [[ -z "${base_dir}" ]]; then
|
||||
echo "❌ Must be run from within a git repository."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_dir="${base_dir}/.gemini/tmp/async-reviews/pr-${pr_number}/logs"
|
||||
|
||||
if [[ ! -d "${log_dir}" ]]; then
|
||||
echo "STATUS: NOT_FOUND"
|
||||
echo "❌ No logs found for PR #${pr_number} in ${log_dir}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
tasks=(
|
||||
"setup|setup.log"
|
||||
"pr-diff|pr-diff.diff"
|
||||
"build-and-lint|build-and-lint.log"
|
||||
"review|review.md"
|
||||
"npm-test|npm-test.log"
|
||||
"test-execution|test-execution.log"
|
||||
"final-assessment|final-assessment.md"
|
||||
)
|
||||
|
||||
all_done=true
|
||||
echo "STATUS: CHECKING"
|
||||
|
||||
for task_info in "${tasks[@]}"; do
|
||||
IFS="|" read -r task_name log_file <<< "${task_info}"
|
||||
|
||||
file_path="${log_dir}/${log_file}"
|
||||
exit_file="${log_dir}/${task_name}.exit"
|
||||
|
||||
if [[ -f "${exit_file}" ]]; then
|
||||
read -r exit_code < "${exit_file}" || exit_code=""
|
||||
if [[ "${exit_code}" == "0" ]]; then
|
||||
echo "✅ ${task_name}: SUCCESS"
|
||||
else
|
||||
echo "❌ ${task_name}: FAILED (exit code ${exit_code})"
|
||||
echo " Last lines of ${file_path}:"
|
||||
tail -n 3 "${file_path}" | sed 's/^/ /' || true
|
||||
fi
|
||||
elif [[ -f "${file_path}" ]]; then
|
||||
echo "⏳ ${task_name}: RUNNING"
|
||||
all_done=false
|
||||
else
|
||||
echo "➖ ${task_name}: NOT STARTED"
|
||||
all_done=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "${all_done}" == "true" ]]; then
|
||||
echo "STATUS: COMPLETE"
|
||||
echo "LOG_DIR: ${log_dir}"
|
||||
else
|
||||
echo "STATUS: IN_PROGRESS"
|
||||
fi
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: behavioral-evals
|
||||
description: Guidance for creating, running, fixing, and promoting behavioral evaluations. Use when verifying agent decision logic, debugging failures, debugging prompt steering, or adding workspace regression tests.
|
||||
---
|
||||
|
||||
# Behavioral Evals
|
||||
|
||||
## Overview
|
||||
|
||||
Behavioral evaluations (evals) are tests that validate the **agent's decision-making** (e.g., tool choice) rather than pure functionality. They are critical for verifying prompt changes, debugging steerability, and preventing regressions.
|
||||
|
||||
> [!NOTE]
|
||||
> **Single Source of Truth**: For core concepts, policies, running tests, and general best practices, always refer to **[evals/README.md](file:///Users/abhipatel/code/gemini-cli/docs/evals/README.md)**.
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Workflow Decision Tree
|
||||
|
||||
1. **Does a prompt/tool change need validation?**
|
||||
* *No* -> Normal integration tests.
|
||||
* *Yes* -> Continue below.
|
||||
2. **Is it UI/Interaction heavy?**
|
||||
* *Yes* -> Use `appEvalTest` (`AppRig`). See **[creating.md](references/creating.md)**.
|
||||
* *No* -> Use `evalTest` (`TestRig`). See **[creating.md](references/creating.md)**.
|
||||
3. **Is it a new test?**
|
||||
* *Yes* -> Set policy to `USUALLY_PASSES`.
|
||||
* *No* -> `ALWAYS_PASSES` (locks in regression).
|
||||
4. **Are you fixing a failure or promoting a test?**
|
||||
* *Fixing* -> See **[fixing.md](references/fixing.md)**.
|
||||
* *Promoting* -> See **[promoting.md](references/promoting.md)**.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Quick Checklist
|
||||
|
||||
### 1. Setup Workspace
|
||||
Seed the workspace with necessary files using the `files` object to simulate a realistic scenario (e.g., NodeJS project with `package.json`).
|
||||
* *Details in **[creating.md](references/creating.md)***
|
||||
|
||||
### 2. Write Assertions
|
||||
Audit agent decisions using `rig.setBreakpoint()` (AppRig only) or index verification on `rig.readToolLogs()`.
|
||||
* *Details in **[creating.md](references/creating.md)***
|
||||
|
||||
### 3. Verify
|
||||
Run single tests locally with Vitest. Confirm stability locally before relying on CI workflows.
|
||||
* *See **[evals/README.md](file:///Users/abhipatel/code/gemini-cli/docs/evals/README.md)** for running commands.*
|
||||
|
||||
---
|
||||
|
||||
## 📦 Bundled Resources
|
||||
|
||||
Detailed procedural guides:
|
||||
* **[creating.md](references/creating.md)**: Assertion strategies, Rig selection, Mock MCPs.
|
||||
* **[fixing.md](references/fixing.md)**: Step-by-step automated investigation, architecture diagnosis guidelines.
|
||||
* **[promoting.md](references/promoting.md)**: Candidate identification criteria and threshold guidelines.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { describe, expect } from 'vitest';
|
||||
import { appEvalTest } from './app-test-helper.js';
|
||||
|
||||
describe('interactive_feature', () => {
|
||||
// New tests MUST start as USUALLY_PASSES
|
||||
appEvalTest('USUALLY_PASSES', {
|
||||
name: 'should pause for user confirmation',
|
||||
files: {
|
||||
'package.json': JSON.stringify({ name: 'app' })
|
||||
},
|
||||
prompt: 'Task description here requiring approval',
|
||||
timeout: 60000,
|
||||
setup: async (rig) => {
|
||||
// ⚠️ Breakpoints are ONLY safe in appEvalTest
|
||||
rig.setBreakpoint(['ask_user']);
|
||||
},
|
||||
assert: async (rig) => {
|
||||
// 1. Wait for the breakpoint to trigger
|
||||
const confirmation = await rig.waitForPendingConfirmation('ask_user');
|
||||
expect(confirmation).toBeDefined();
|
||||
|
||||
// 2. Resolve it so the test can finish
|
||||
await rig.resolveTool(confirmation);
|
||||
await rig.waitForIdle();
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import { describe, expect } from 'vitest';
|
||||
import { evalTest } from './test-helper.js';
|
||||
|
||||
describe('core_feature', () => {
|
||||
// New tests MUST start as USUALLY_PASSES
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: 'should perform expected agent action',
|
||||
setup: async (rig) => {
|
||||
// For mocking offline MCP:
|
||||
// rig.addMockMcpServer('workspace-server', 'google-workspace');
|
||||
},
|
||||
files: {
|
||||
'src/app.ts': '// some code',
|
||||
},
|
||||
prompt: 'Task description here',
|
||||
timeout: 60000, // 1 minute safety limit
|
||||
assert: async (rig, result) => {
|
||||
// 1. Audit the trajectory (Safe for standard evalTest)
|
||||
const logs = rig.readToolLogs();
|
||||
const hasTool = logs.some((l) => l.toolRequest.name === 'read_file');
|
||||
expect(hasTool, 'Agent should have read the file').toBe(true);
|
||||
|
||||
// 2. Assert efficiency (Cost/Turn)
|
||||
expect(logs.length).toBeLessThan(5);
|
||||
|
||||
// 3. Assert final output
|
||||
expect(result).toContain('Expected Keyword');
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,151 @@
|
||||
# Creating Behavioral Evals
|
||||
|
||||
## 🔬 Rig Selection
|
||||
|
||||
| Rig Type | Import From | Architecture | Use When |
|
||||
| :---------------- | :--------------------- | :------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- |
|
||||
| **`evalTest`** | `./test-helper.js` | **Subprocess**. Runs the CLI in a separate process + waits for exit. | Standard workspace tests. **Do not use `setBreakpoint`**; auditing history (`readToolLogs`) is safer. |
|
||||
| **`appEvalTest`** | `./app-test-helper.js` | **In-Process**. Runs directly inside the runner loop. | UI/Ink rendering. Safe for `setBreakpoint` triggers. |
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Scenario Design
|
||||
|
||||
Evals must simulate realistic agent environments to effectively test
|
||||
decision-making.
|
||||
|
||||
- **Workspace State**: Seed with standard project anchors if testing general
|
||||
capabilities:
|
||||
- `package.json` for NodeJS environments.
|
||||
- Minimal configuration files (`tsconfig.json`, `GEMINI.md`).
|
||||
- **Structural Complexity**: Provide enough files to force the agent to _search_
|
||||
or _navigate_, rather than giving the answer directly. Avoid trivial one-file
|
||||
tests unless testing exact prompt steering.
|
||||
|
||||
---
|
||||
|
||||
## ❌ Fail First Principle
|
||||
|
||||
Before asserting a new capability or locking in a fix, **verify that the test
|
||||
fails first**.
|
||||
|
||||
- It is easy to accidentally write an eval that asserts behaviors that are
|
||||
already met or pass by default.
|
||||
- **Process**: reproduce failure with test -> apply fix (prompt/tool) -> verify
|
||||
test passes.
|
||||
|
||||
---
|
||||
|
||||
## ✋ Testing Patterns
|
||||
|
||||
### 1. Breakpoints
|
||||
|
||||
Verifies the agent _intends_ to use a tool BEFORE executing it. Useful for
|
||||
interactive prompts or safety checks.
|
||||
|
||||
```typescript
|
||||
// ⚠️ Only works with appEvalTest (AppRig)
|
||||
setup: async (rig) => {
|
||||
rig.setBreakpoint(['ask_user']);
|
||||
},
|
||||
assert: async (rig) => {
|
||||
const confirmation = await rig.waitForPendingConfirmation('ask_user');
|
||||
expect(confirmation).toBeDefined();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Tool Confirmation Race
|
||||
|
||||
When asserting multiple triggers (e.g., "enters plan mode then asks question"):
|
||||
|
||||
```typescript
|
||||
assert: async (rig) => {
|
||||
let confirmation = await rig.waitForPendingConfirmation([
|
||||
'enter_plan_mode',
|
||||
'ask_user',
|
||||
]);
|
||||
|
||||
if (confirmation?.name === 'enter_plan_mode') {
|
||||
rig.acceptConfirmation('enter_plan_mode');
|
||||
confirmation = await rig.waitForPendingConfirmation('ask_user');
|
||||
}
|
||||
expect(confirmation?.toolName).toBe('ask_user');
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Audit Tool Logs
|
||||
|
||||
Audit exact operations to ensure efficiency (e.g., no redundant reads).
|
||||
|
||||
```typescript
|
||||
assert: async (rig, result) => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const toolLogs = rig.readToolLogs();
|
||||
|
||||
const writeCall = toolLogs.find(
|
||||
(log) => log.toolRequest.name === 'write_file',
|
||||
);
|
||||
expect(writeCall).toBeDefined();
|
||||
};
|
||||
```
|
||||
|
||||
### 4. Mock MCP Facades
|
||||
|
||||
To evaluate tools connected via MCP without hitting live endpoints, load a mock
|
||||
server configuration in the `setup` hook.
|
||||
|
||||
```typescript
|
||||
setup: async (rig) => {
|
||||
rig.addMockMcpServer('workspace-server', 'google-workspace');
|
||||
},
|
||||
assert: async (rig) => {
|
||||
await rig.waitForTelemetryReady();
|
||||
const toolLogs = rig.readToolLogs();
|
||||
const workspaceCall = toolLogs.find(
|
||||
(log) => log.toolRequest.name === 'mcp_workspace-server_docs.getText'
|
||||
);
|
||||
expect(workspaceCall).toBeDefined();
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Safety & Efficiency Guardrails
|
||||
|
||||
### 1. Breakpoint Deadlocks
|
||||
|
||||
Breakpoints (`setBreakpoint`) pause execution. In standard `evalTest`,
|
||||
`rig.run()` waits for the process to exit _before_ assertions run. **This will
|
||||
hang indefinitely.**
|
||||
|
||||
- **Use Breakpoints** for `appEvalTest` or interactive simulations.
|
||||
- **Use Audit Tool Logs** (above) for standard trajectory tests.
|
||||
|
||||
### 2. Runaway Timeout
|
||||
|
||||
Always set a budget boundary in the `EvalCase` to prevent runaway loops on
|
||||
quota:
|
||||
|
||||
```typescript
|
||||
evalTest('USUALLY_PASSES', {
|
||||
name: '...',
|
||||
timeout: 60000, // 1 minute safety limit
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Efficiency Assertion (Turn limits)
|
||||
|
||||
Check if a tool is called _early_ using index checks:
|
||||
|
||||
```typescript
|
||||
assert: async (rig) => {
|
||||
const toolLogs = rig.readToolLogs();
|
||||
const toolCallIndex = toolLogs.findIndex(
|
||||
(log) => log.toolRequest.name === 'cli_help',
|
||||
);
|
||||
|
||||
expect(toolCallIndex).toBeGreaterThan(-1);
|
||||
expect(toolCallIndex).toBeLessThan(5); // Called within first 5 turns
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# Fixing Behavioral Evals
|
||||
|
||||
Use this guide when asked to debug, troubleshoot, or fix a failing behavioral
|
||||
evaluation.
|
||||
|
||||
---
|
||||
|
||||
## 1. 🔍 Investigate
|
||||
|
||||
1. **Fetch Nightly Results**: Use the `gh` CLI to inspect the latest run from
|
||||
`evals-nightly.yml` if applicable.
|
||||
- _Example view URL_:
|
||||
`https://github.com/google-gemini/gemini-cli/actions/workflows/evals-nightly.yml`
|
||||
2. **Isolate**: DO NOT push changes or start remote runs. Confine investigation
|
||||
to the local workspace.
|
||||
3. **Read Logs**:
|
||||
- Eval logs live in `evals/logs/<test_name>.log`.
|
||||
- Enable verbose debugging via `export GEMINI_DEBUG_LOG_FILE="debug.log"`.
|
||||
4. **Diagnose**: Audit tool logs and telemetry. Note if due to setup/assert.
|
||||
- **Tip**: Proactively add custom logging/diagnostics to check hypotheses.
|
||||
|
||||
---
|
||||
|
||||
## 2. 🛠️ Fix Strategy
|
||||
|
||||
1. **Targeted Location**: Locate the test case and the corresponding
|
||||
prompt/code.
|
||||
2. **Iterative Scope**: Make extreme change first to verify scope, then refine
|
||||
to a minimal, targeted change.
|
||||
3. **Assertion Fidelity**:
|
||||
- Changing the test prompt is a **last resort** (prompts are often vague by
|
||||
design).
|
||||
- **Warning**: Do not lose test fidelity by making prompts too direct/easy.
|
||||
- **Primary Fix Trigger**: Adjust tool descriptions, system prompts
|
||||
(`snippets.ts`), or **modules that contribute to the prompt template**.
|
||||
- **Warning**: Prompts have multiple configurations; ensure your fix targets
|
||||
the correct config for the model in question.
|
||||
4. **Architecture Options**: If prompt or instruction tuning triggers no
|
||||
improvement, analyze loop composition.
|
||||
- **AgentLoop**: Defined by `context + toolset + prompt`.
|
||||
- **Enhancements**: Loops perform best with direct prompts, fewer irrelevant
|
||||
tools, low goal density, and minimal low-value/irrelevant context.
|
||||
- **Modifications**: Compose subagents or isolate tools. Ground in observed
|
||||
traces.
|
||||
- **Warning**: Think deeply before offering recommendations; avoid parroting
|
||||
abstract design guidelines.
|
||||
|
||||
---
|
||||
|
||||
## 3. ✅ Verify
|
||||
|
||||
1. **Run Local**: Run Vitest in non-interactive mode on just the file.
|
||||
2. **Log Audit**: Prioritize diagnosing failures via log comparison before
|
||||
triggering heavy test runs.
|
||||
3. **Stability Limit**: Run the test **3 times** locally on key models (can use
|
||||
scripts to run in parallel for speed):
|
||||
- **Gemini 3.0**
|
||||
- **Gemini 3 Flash**
|
||||
- **Gemini 2.5 Pro**
|
||||
4. **Flakiness Rule**: If it passes 2/3 times, it may be inherent noise
|
||||
difficult to improve without a structural split.
|
||||
|
||||
---
|
||||
|
||||
## 4. 📊 Report
|
||||
|
||||
Provide a summary of:
|
||||
|
||||
- Test success rate for each tested model (e.g., 3/3 = 100%).
|
||||
- Root cause identification and fix explanation.
|
||||
- If unfixed, provide high-confidence architecture recommendations.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Promoting Behavioral Evals
|
||||
|
||||
Use this guide when asked to analyze nightly results and promote incubated tests
|
||||
to stable suites.
|
||||
|
||||
---
|
||||
|
||||
## 1. 🔍 Investigate candidates
|
||||
|
||||
1. **Audit Nightly Logs**: Use the `gh` CLI to fetch results from
|
||||
`evals-nightly.yml` (Direct URL:
|
||||
`https://github.com/google-gemini/gemini-cli/actions/workflows/evals-nightly.yml`).
|
||||
- **Tip**: The aggregate summary from the most recent run integrates the
|
||||
last 7 runs of history automatically.
|
||||
- **Safety**: DO NOT push changes or start remote runs. All verification is
|
||||
local.
|
||||
2. **Assess Stability**: Identify tests that pass **100% of the time** across
|
||||
ALL enabled models over the **last 7 nightly runs** in a row.
|
||||
- _100% means the test passed 3/3 times for every model and run._
|
||||
3. **Promotion Targets**: Tests meeting this criteria are candidates for
|
||||
promotion from `USUALLY_PASSES` to `ALWAYS_PASSES`.
|
||||
|
||||
---
|
||||
|
||||
## 2. 🚥 Promotion Steps
|
||||
|
||||
1. **Locate File**: Locate the eval file in the `evals/` directory.
|
||||
2. **Update Policy**: Modify the policy argument to `ALWAYS_PASSES`.
|
||||
```typescript
|
||||
evalTest('ALWAYS_PASSES', { ... })
|
||||
```
|
||||
3. **Targeting**: Follow guidelines in `evals/README.md` regarding stable suite
|
||||
organization.
|
||||
4. **Constraint**: Your final change must be **minimal and targeted** strictly
|
||||
to promoting the test status. Do not refactor the test or setup fixtures.
|
||||
|
||||
---
|
||||
|
||||
## 3. ✅ Verify
|
||||
|
||||
1. **Run Prompted Tests**: Run the promoted test locally using non-interactive
|
||||
Vitest to confirm structure validity.
|
||||
2. **Verify Suite Inclusion**: Check that the test is successfully picked up by
|
||||
standard runnable ranges.
|
||||
|
||||
---
|
||||
|
||||
## 4. 📊 Report
|
||||
|
||||
Provide a summary of:
|
||||
|
||||
- Which tests were promoted.
|
||||
- Provide the success rate evidence (e.g., 7/7 runs passed for all models).
|
||||
- If no candidates qualified, list the next closest candidates and their current
|
||||
pass rate.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Running & Promoting Evals
|
||||
|
||||
## 🛠️ Prerequisites
|
||||
|
||||
Behavioral evals run against the compiled binary. You **must** build and bundle
|
||||
the project first after making changes:
|
||||
|
||||
```bash
|
||||
npm run build && npm run bundle
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏃♂️ Running Tests
|
||||
|
||||
### 1. Configure Environment Variables
|
||||
|
||||
Evals require a standard API key. If your `.env` file has multiple keys or
|
||||
comments, use this precise extraction setup:
|
||||
|
||||
```bash
|
||||
export GEMINI_API_KEY=$(grep '^GEMINI_API_KEY=' .env | cut -d '=' -f2) && RUN_EVALS=1 npx vitest run --config evals/vitest.config.ts <file_name>
|
||||
```
|
||||
|
||||
### 2. Commands
|
||||
|
||||
| Command | Scope | Description |
|
||||
| :---------------------------------- | :-------------- | :------------------------------------------------- |
|
||||
| `npm run test:always_passing_evals` | `ALWAYS_PASSES` | Fast feedback, runs in CI. |
|
||||
| `npm run test:all_evals` | All | Runs nightly incubation tests. Sets `RUN_EVALS=1`. |
|
||||
|
||||
### Target Specific File
|
||||
|
||||
_Note: `RUN_EVALS=1` is required for incubated (`USUALLY_PASSES`) tests._
|
||||
|
||||
```bash
|
||||
RUN_EVALS=1 npx vitest run --config evals/vitest.config.ts my_feature.eval.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐞 Debugging and Logs
|
||||
|
||||
If a test fails, verify:
|
||||
|
||||
- **Tool Trajectory Logs**:序列 of calls in `evals/logs/<test_name>.log`.
|
||||
- **Verbose Reasoning**: Capture raw buffer traces by setting
|
||||
`GEMINI_DEBUG_LOG_FILE`:
|
||||
```bash
|
||||
export GEMINI_DEBUG_LOG_FILE="debug.log"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🎯 Verify Model Targeting
|
||||
|
||||
- **Tip:** Standard evals benchmark against model variations. If a test passes
|
||||
on Flash but fails on Pro (or vice versa), the issue is usually in the **tool
|
||||
description**, not the prompt definition. Flash is sensitive to "instruction
|
||||
bloat," while Pro is sensitive to "ambiguous intent."
|
||||
|
||||
---
|
||||
|
||||
## 🚥 deflaking & Promotion
|
||||
|
||||
To maintain CI stability, all new evals follow a strict incubation period.
|
||||
|
||||
### 1. Incubation (`USUALLY_PASSES`)
|
||||
|
||||
New tests must be created with the `USUALLY_PASSES` policy.
|
||||
|
||||
```typescript
|
||||
evalTest('USUALLY_PASSES', { ... })
|
||||
```
|
||||
|
||||
They run in **Evals: Nightly** workflows and do not block PR merges.
|
||||
|
||||
### 2. Investigate Failures
|
||||
|
||||
If a nightly eval regresses, investigate via agent:
|
||||
|
||||
```bash
|
||||
gemini /fix-behavioral-eval [optional-run-uri]
|
||||
```
|
||||
|
||||
### 3. Promotion (`ALWAYS_PASSES`)
|
||||
|
||||
Once a test scores 100% consistency over multiple nightly cycles:
|
||||
|
||||
```bash
|
||||
gemini /promote-behavioral-eval
|
||||
```
|
||||
|
||||
_Do not promote manually._ The command verifies trajectory logs before updating
|
||||
the file policy.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: ci
|
||||
description:
|
||||
A specialized skill for Gemini CLI that provides high-performance, fail-fast
|
||||
monitoring of GitHub Actions workflows and automated local verification of CI
|
||||
failures. It handles run discovery automatically—simply provide the branch name.
|
||||
---
|
||||
|
||||
# CI Replicate & Status
|
||||
|
||||
This skill enables the agent to efficiently monitor GitHub Actions, triage
|
||||
failures, and bridge remote CI errors to local development. It defaults to
|
||||
**automatic replication** of failures to streamline the fix cycle.
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
- **Automatic Replication**: Automatically monitors CI and immediately executes
|
||||
suggested test or lint commands locally upon failure.
|
||||
- **Real-time Monitoring**: Aggregated status line for all concurrent workflows
|
||||
on the current branch.
|
||||
- **Fail-Fast Triage**: Immediately stops on the first job failure to provide a
|
||||
structured report.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. CI Replicate (`replicate`) - DEFAULT
|
||||
Use this as the primary path to monitor CI and **automatically** replicate
|
||||
failures locally for immediate triage and fixing.
|
||||
- **Behavior**: When this workflow is triggered, the agent will monitor the CI
|
||||
and **immediately and automatically execute** all suggested test or lint
|
||||
commands (marked with 🚀) as soon as a failure is detected.
|
||||
- **Tool**: `node .gemini/skills/ci/scripts/ci.mjs [branch]`
|
||||
- **Discovery**: The script **automatically** finds the latest active or recent
|
||||
run for the branch. Do NOT manually search for run IDs.
|
||||
- **Goal**: Reproduce the failure locally without manual intervention, then
|
||||
proceed to analyze and fix the code.
|
||||
|
||||
### 1. CI Status (`status`)
|
||||
Use this when you have pushed changes and need to monitor the CI and reproduce
|
||||
any failures locally.
|
||||
- **Tool**: `node .gemini/skills/ci/scripts/ci.mjs [branch] [run_id]`
|
||||
- **Discovery**: The script **automatically** finds the latest active or recent
|
||||
run for the branch. You should NOT manually search for \`run_id\` using \`gh run list\`
|
||||
unless a specific historical run is requested. Simply provide the branch name.
|
||||
- **Step 1 (Monitor)**: Execute the tool with the branch name.
|
||||
- **Step 2 (Extract)**: Extract suggested \`npm test\` or \`npm run lint\` commands
|
||||
from the output (marked with 🚀).
|
||||
- **Step 3 (Reproduce)**: Execute those commands locally to confirm the failure.
|
||||
- **Behavior**: It will poll every 15 seconds. If it detects a failure, it will
|
||||
exit with a structured report and provide the exact commands to run locally.
|
||||
|
||||
## Failure Categories & Actions
|
||||
|
||||
- **Test Failures**: Agent should run the specific `npm test -w <pkg> -- <path>`
|
||||
command suggested.
|
||||
- **Lint Errors**: Agent should run `npm run lint:all` or the specific package
|
||||
lint command.
|
||||
- **Build Errors**: Agent should check `tsc` output or build logs to resolve
|
||||
compilation issues.
|
||||
- **Job Errors**: Investigate `gh run view --job <job_id> --log` for
|
||||
infrastructure or setup failures.
|
||||
|
||||
## Noise Filtering
|
||||
The underlying scripts automatically filter noise (Git logs, NPM warnings, stack
|
||||
trace overhead). The agent should focus on the "Structured Failure Report"
|
||||
provided by the tool.
|
||||
@@ -0,0 +1,281 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
const BRANCH =
|
||||
process.argv[2] || execSync('git branch --show-current').toString().trim();
|
||||
const RUN_ID_OVERRIDE = process.argv[3];
|
||||
|
||||
let REPO;
|
||||
try {
|
||||
const remoteUrl = execSync('git remote get-url origin').toString().trim();
|
||||
REPO = remoteUrl
|
||||
.replace(/.*github\.com[\/:]/, '')
|
||||
.replace(/\.git$/, '')
|
||||
.trim();
|
||||
} catch (e) {
|
||||
REPO = 'google-gemini/gemini-cli';
|
||||
}
|
||||
|
||||
const FAILED_FILES = new Set();
|
||||
|
||||
function runGh(args) {
|
||||
try {
|
||||
return execSync(`gh ${args}`, {
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
}).toString();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function fetchFailuresViaApi(jobId) {
|
||||
try {
|
||||
const cmd = `gh api repos/${REPO}/actions/jobs/${jobId}/logs | grep -iE " FAIL |❌|ERROR|Lint failed|Build failed|Exception|failed with exit code"`;
|
||||
return execSync(cmd, {
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
}).toString();
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function isNoise(line) {
|
||||
const lower = line.toLowerCase();
|
||||
return (
|
||||
lower.includes('* [new branch]') ||
|
||||
lower.includes('npm warn') ||
|
||||
lower.includes('fetching updates') ||
|
||||
lower.includes('node:internal/errors') ||
|
||||
lower.includes('at ') || // Stack traces
|
||||
lower.includes('checkexecsyncerror') ||
|
||||
lower.includes('node_modules')
|
||||
);
|
||||
}
|
||||
|
||||
function extractTestFile(failureText) {
|
||||
const cleanLine = failureText
|
||||
.replace(/[|#\[\]()]/g, ' ')
|
||||
.replace(/<[^>]*>/g, ' ')
|
||||
.trim();
|
||||
const fileMatch = cleanLine.match(/([\w\/._-]+\.test\.[jt]sx?)/);
|
||||
if (fileMatch) return fileMatch[1];
|
||||
return null;
|
||||
}
|
||||
|
||||
function generateTestCommand(failedFilesMap) {
|
||||
const workspaceToFiles = new Map();
|
||||
for (const [file, info] of failedFilesMap.entries()) {
|
||||
if (
|
||||
['Job Error', 'Unknown File', 'Build Error', 'Lint Error'].includes(file)
|
||||
)
|
||||
continue;
|
||||
let workspace = '@google/gemini-cli';
|
||||
let relPath = file;
|
||||
if (file.startsWith('packages/core/')) {
|
||||
workspace = '@google/gemini-cli-core';
|
||||
relPath = file.replace('packages/core/', '');
|
||||
} else if (file.startsWith('packages/cli/')) {
|
||||
workspace = '@google/gemini-cli';
|
||||
relPath = file.replace('packages/cli/', '');
|
||||
}
|
||||
relPath = relPath.replace(/^.*packages\/[^\/]+\//, '');
|
||||
if (!workspaceToFiles.has(workspace))
|
||||
workspaceToFiles.set(workspace, new Set());
|
||||
workspaceToFiles.get(workspace).add(relPath);
|
||||
}
|
||||
const commands = [];
|
||||
for (const [workspace, files] of workspaceToFiles.entries()) {
|
||||
commands.push(`npm test -w ${workspace} -- ${Array.from(files).join(' ')}`);
|
||||
}
|
||||
return commands.join(' && ');
|
||||
}
|
||||
|
||||
async function monitor() {
|
||||
let targetRunIds = [];
|
||||
if (RUN_ID_OVERRIDE) {
|
||||
targetRunIds = [RUN_ID_OVERRIDE];
|
||||
} else {
|
||||
// 1. Get runs directly associated with the branch
|
||||
const runListOutput = runGh(
|
||||
`run list --branch "${BRANCH}" --limit 10 --json databaseId,status,workflowName,createdAt`,
|
||||
);
|
||||
if (runListOutput) {
|
||||
const runs = JSON.parse(runListOutput);
|
||||
const activeRuns = runs.filter((r) => r.status !== 'completed');
|
||||
if (activeRuns.length > 0) {
|
||||
targetRunIds = activeRuns.map((r) => r.databaseId);
|
||||
} else if (runs.length > 0) {
|
||||
const latestTime = new Date(runs[0].createdAt).getTime();
|
||||
targetRunIds = runs
|
||||
.filter((r) => latestTime - new Date(r.createdAt).getTime() < 60000)
|
||||
.map((r) => r.databaseId);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Get runs associated with commit statuses (handles chained/indirect runs)
|
||||
try {
|
||||
const headSha = execSync(`git rev-parse "${BRANCH}"`).toString().trim();
|
||||
const statusOutput = runGh(
|
||||
`api repos/${REPO}/commits/${headSha}/status -q '.statuses[] | select(.target_url | contains("actions/runs/")) | .target_url'`,
|
||||
);
|
||||
if (statusOutput) {
|
||||
const statusRunIds = statusOutput
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
.map((url) => {
|
||||
const match = url.match(/actions\/runs\/(\d+)/);
|
||||
return match ? parseInt(match[1], 10) : null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
for (const runId of statusRunIds) {
|
||||
if (!targetRunIds.includes(runId)) {
|
||||
targetRunIds.push(runId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore if branch/SHA not found or API fails
|
||||
}
|
||||
|
||||
if (targetRunIds.length > 0) {
|
||||
const runNames = [];
|
||||
for (const runId of targetRunIds) {
|
||||
const runInfo = runGh(`run view "${runId}" --json workflowName`);
|
||||
if (runInfo) {
|
||||
runNames.push(JSON.parse(runInfo).workflowName);
|
||||
}
|
||||
}
|
||||
console.log(`Monitoring workflows: ${[...new Set(runNames)].join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetRunIds.length === 0) {
|
||||
console.log(`No runs found for branch ${BRANCH}.`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let allPassed = 0,
|
||||
allFailed = 0,
|
||||
allRunning = 0,
|
||||
allQueued = 0,
|
||||
totalJobs = 0;
|
||||
let anyRunInProgress = false;
|
||||
const fileToTests = new Map();
|
||||
let failuresFoundInLoop = false;
|
||||
|
||||
for (const runId of targetRunIds) {
|
||||
const runOutput = runGh(
|
||||
`run view "${runId}" --json databaseId,status,conclusion,workflowName`,
|
||||
);
|
||||
if (!runOutput) continue;
|
||||
const run = JSON.parse(runOutput);
|
||||
if (run.status !== 'completed') anyRunInProgress = true;
|
||||
|
||||
const jobsOutput = runGh(`run view "${runId}" --json jobs`);
|
||||
if (jobsOutput) {
|
||||
const { jobs } = JSON.parse(jobsOutput);
|
||||
totalJobs += jobs.length;
|
||||
const failedJobs = jobs.filter((j) => j.conclusion === 'failure');
|
||||
if (failedJobs.length > 0) {
|
||||
failuresFoundInLoop = true;
|
||||
for (const job of failedJobs) {
|
||||
const failures = fetchFailuresViaApi(job.databaseId);
|
||||
if (failures.trim()) {
|
||||
failures.split('\n').forEach((line) => {
|
||||
if (!line.trim() || isNoise(line)) return;
|
||||
const file = extractTestFile(line);
|
||||
const filePath =
|
||||
file ||
|
||||
(line.toLowerCase().includes('lint')
|
||||
? 'Lint Error'
|
||||
: line.toLowerCase().includes('build')
|
||||
? 'Build Error'
|
||||
: 'Unknown File');
|
||||
let testName = line;
|
||||
if (line.includes(' > ')) {
|
||||
testName = line.split(' > ').slice(1).join(' > ').trim();
|
||||
}
|
||||
if (!fileToTests.has(filePath))
|
||||
fileToTests.set(filePath, new Set());
|
||||
fileToTests.get(filePath).add(testName);
|
||||
});
|
||||
} else {
|
||||
const step =
|
||||
job.steps?.find((s) => s.conclusion === 'failure')?.name ||
|
||||
'unknown';
|
||||
const category = step.toLowerCase().includes('lint')
|
||||
? 'Lint Error'
|
||||
: step.toLowerCase().includes('build')
|
||||
? 'Build Error'
|
||||
: 'Job Error';
|
||||
if (!fileToTests.has(category))
|
||||
fileToTests.set(category, new Set());
|
||||
fileToTests
|
||||
.get(category)
|
||||
.add(`${job.name}: Failed at step "${step}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const job of jobs) {
|
||||
if (job.status === 'in_progress') allRunning++;
|
||||
else if (job.status === 'queued') allQueued++;
|
||||
else if (job.conclusion === 'success') allPassed++;
|
||||
else if (job.conclusion === 'failure') allFailed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failuresFoundInLoop) {
|
||||
console.log(
|
||||
`\n\n❌ Failures detected across ${allFailed} job(s). Stopping monitor...`,
|
||||
);
|
||||
console.log('\n--- Structured Failure Report (Noise Filtered) ---');
|
||||
for (const [file, tests] of fileToTests.entries()) {
|
||||
console.log(`\nCategory/File: ${file}`);
|
||||
// Limit output per file if it's too large
|
||||
const testsArr = Array.from(tests).map((t) =>
|
||||
t.length > 500 ? t.substring(0, 500) + '... [TRUNCATED]' : t,
|
||||
);
|
||||
testsArr.slice(0, 10).forEach((t) => console.log(` - ${t}`));
|
||||
if (testsArr.length > 10)
|
||||
console.log(` ... and ${testsArr.length - 10} more`);
|
||||
}
|
||||
const testCmd = generateTestCommand(fileToTests);
|
||||
if (testCmd) {
|
||||
console.log('\n🚀 Run this to verify fixes:');
|
||||
console.log(testCmd);
|
||||
} else if (
|
||||
Array.from(fileToTests.keys()).some((k) => k.includes('Lint'))
|
||||
) {
|
||||
console.log('\n🚀 Run this to verify lint fixes:\nnpm run lint:all');
|
||||
}
|
||||
console.log('---------------------------------');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const completed = allPassed + allFailed;
|
||||
process.stdout.write(
|
||||
`\r⏳ Monitoring ${targetRunIds.length} runs... ${completed}/${totalJobs} jobs (${allPassed} passed, ${allFailed} failed, ${allRunning} running, ${allQueued} queued) `,
|
||||
);
|
||||
if (!anyRunInProgress) {
|
||||
console.log('\n✅ All workflows passed!');
|
||||
process.exit(0);
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 15000));
|
||||
}
|
||||
}
|
||||
|
||||
monitor().catch((err) => {
|
||||
console.error('\nMonitor error:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -65,18 +65,60 @@ accessible.
|
||||
- **UI and code:** Use **bold** for UI elements and `code font` for filenames,
|
||||
snippets, commands, and API elements. Focus on the task when discussing
|
||||
interaction.
|
||||
- **Links:** Use descriptive anchor text; avoid "click here." Ensure the link
|
||||
makes sense out of context.
|
||||
- **Accessibility:** Use semantic HTML elements correctly (headings, lists,
|
||||
tables).
|
||||
- **Media:** Use lowercase hyphenated filenames. Provide descriptive alt text
|
||||
for all images.
|
||||
- **Details section:** Use the `<details>` tag to create a collapsible section.
|
||||
This is useful for supplementary or data-heavy information that isn't critical
|
||||
to the main flow.
|
||||
|
||||
Example:
|
||||
|
||||
<details>
|
||||
<summary>Title</summary>
|
||||
|
||||
- First entry
|
||||
- Second entry
|
||||
|
||||
</details>
|
||||
|
||||
- **Callouts**: Use GitHub-flavored markdown alerts to highlight important
|
||||
information. To ensure the formatting is preserved by `npm run format`, place
|
||||
an empty line, then the `<!-- prettier-ignore -->` comment directly before
|
||||
the callout block. The callout type (`[!TYPE]`) should be on the first line,
|
||||
followed by a newline, and then the content, with each subsequent line of
|
||||
content starting with `>`. Available types are `NOTE`, `TIP`, `IMPORTANT`,
|
||||
`WARNING`, and `CAUTION`.
|
||||
|
||||
Example:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an example of a multi-line note that will be preserved
|
||||
> by Prettier.
|
||||
|
||||
### Links
|
||||
- **Accessibility:** Use descriptive anchor text; avoid "click here." Ensure the
|
||||
link makes sense out of context, such as when being read by a screen reader.
|
||||
- **Use relative links in docs:** Use relative links in documentation (`/docs/`)
|
||||
to ensure portability. Use paths relative to the current file's directory
|
||||
(for example, `../tools/` from `docs/cli/`). Do not include the `/docs/`
|
||||
section of a path, but do verify that the resulting relative link exists. This
|
||||
does not apply to meta files such as README.MD and CONTRIBUTING.MD.
|
||||
- **When changing headings, check for deep links:** If a user is changing a
|
||||
heading, check for deep links to that heading in other pages and update
|
||||
accordingly.
|
||||
|
||||
### Structure
|
||||
- **BLUF:** Start with an introduction explaining what to expect.
|
||||
- **Experimental features:** If a feature is clearly noted as experimental,
|
||||
add the following note immediately after the introductory paragraph:
|
||||
`> **Note:** This is a preview feature currently under active development.`
|
||||
add the following note immediately after the introductory paragraph:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development.
|
||||
|
||||
- **Headings:** Use hierarchical headings to support the user journey.
|
||||
- **Procedures:**
|
||||
- Introduce lists of steps with a complete sentence.
|
||||
@@ -85,8 +127,7 @@ add the following note immediately after the introductory paragraph:
|
||||
- Put conditions before instructions (e.g., "On the Settings page, click...").
|
||||
- Provide clear context for where the action takes place.
|
||||
- Indicate optional steps clearly (e.g., "Optional: ...").
|
||||
- **Elements:** Use bullet lists, tables, notes (`> **Note:**`), and warnings
|
||||
(`> **Warning:**`).
|
||||
- **Elements:** Use bullet lists, tables, details, and callouts.
|
||||
- **Avoid using a table of contents:** If a table of contents is present, remove
|
||||
it.
|
||||
- **Next steps:** Conclude with a "Next steps" section if applicable.
|
||||
@@ -118,13 +159,14 @@ documentation.
|
||||
reflects existing code.
|
||||
- **Structure:** Apply "Structure (New Docs)" rules (BLUF, headings, etc.) when
|
||||
adding new sections to existing pages.
|
||||
- **Headers**: If you change a header, you must check for links that lead to
|
||||
that header and update them.
|
||||
- **Tone:** Ensure the tone is active and engaging. Use "you" and contractions.
|
||||
- **Clarity:** Correct awkward wording, spelling, and grammar. Rephrase
|
||||
sentences to make them easier for users to understand.
|
||||
- **Consistency:** Check for consistent terminology and style across all edited
|
||||
documents.
|
||||
|
||||
|
||||
## Phase 4: Verification and finalization
|
||||
Perform a final quality check to ensure that all changes are correctly formatted
|
||||
and that all links are functional.
|
||||
@@ -133,7 +175,8 @@ and that all links are functional.
|
||||
technical behavior.
|
||||
2. **Self-review:** Re-read changes for formatting, correctness, and flow.
|
||||
3. **Link check:** Verify all new and existing links leading to or from modified
|
||||
pages.
|
||||
pages. If you changed a header, ensure that any links that lead to it are
|
||||
updated.
|
||||
4. **Format:** Once all changes are complete, ask to execute `npm run format`
|
||||
to ensure consistent formatting across the project. If the user confirms,
|
||||
execute the command.
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: review-duplication
|
||||
description: Use this skill during code reviews to proactively investigate the codebase for duplicated functionality, reinvented wheels, or failure to reuse existing project best practices and shared utilities.
|
||||
---
|
||||
|
||||
# Review Duplication
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides a structured workflow for investigating a codebase during a code review to identify duplicated logic, reinvented utilities, and missed opportunities to reuse established patterns. By executing this workflow, you ensure that new code integrates seamlessly with the existing project architecture.
|
||||
|
||||
## Workflow: Investigating for Duplication
|
||||
|
||||
When reviewing code, perform the following steps before finalizing your review:
|
||||
|
||||
### 1. Extract Core Logic
|
||||
Analyze the new code to identify the core algorithms, utility functions, generic data structures, or UI components being introduced. Look beyond the specific business logic to see the underlying mechanics.
|
||||
|
||||
### 2. Hypothesize Existing Locations & Trace Dependencies
|
||||
Think about where this type of code *would* live if it already existed in the project. Provide absolute paths from the repo root to disambiguate.
|
||||
- **Utilities:** `packages/core/src/utils/`, `packages/cli/src/utils/`
|
||||
- **UI Components:** `packages/cli/src/ui/components/`, `packages/cli/src/ui/`
|
||||
- **Services:** `packages/core/src/services/`, `packages/cli/src/services/`
|
||||
- **Configuration:** `packages/core/src/config/`, `packages/cli/src/config/`
|
||||
- **Core Logic:** Call out `packages/core/` if functionality does not appear React UI specific.
|
||||
|
||||
**Trace Third-Party Dependencies:** If the PR introduces a new import for a utility library (e.g., `lodash.merge`, `date-fns`), trace how and where the project currently uses that library. There is likely an existing wrapper or shared utility.
|
||||
|
||||
**Check Package Files:** Before flagging a custom implementation of a complex algorithm, check `package.json` to see if a standard library (like `lodash` or `uuid`) is already installed that provides this functionality.
|
||||
|
||||
### 3. Investigate the Codebase (Sub-Agent Delegation)
|
||||
Delegate the heavy lifting of codebase investigation to specialized sub-agents. They are optimized to perform deep searches and semantic mapping without bloating your session history.
|
||||
|
||||
To ensure a comprehensive review, you MUST formulate highly specific objectives for the sub-agents, providing them with the "scents" you discovered in Step 1.
|
||||
|
||||
- **Codebase Investigator:** Use the `codebase_investigator` as your primary researcher. When delegating, formulate an objective that asks specific, investigative questions about the codebase, explicitly including these search vectors:
|
||||
- **Structural Similarity:** Ask if existing code uses the same underlying APIs (e.g., "Does any existing code use `Intl.DateTimeFormat` or `setTimeout` for similar purposes?").
|
||||
- **Naming Conventions:** Ask if there are existing symbols with similar naming patterns (e.g., "Are there existing symbols with naming patterns like `*Format*` or `*Debounce*`?").
|
||||
- **Comments & Documentation:** Ask if keywords from the PR's comments or JSDoc exist in describing similar behavior elsewhere.
|
||||
- **Architectural Fit:** Ask where this type of logic is currently centralized (e.g., "Where is centralized date formatting logic located?").
|
||||
- **Refactoring Guidance:** Crucially, ask the sub-agent to explain *how* the new code could be refactored to use any existing logic it finds.
|
||||
- **Generalist Agent:** Use the `generalist` for detailed, turn-intensive comparisons. For example: "Review the implementation of `MyNewComponent` in the PR and compare it semantically against all components in `packages/ui/src`. Are there any existing components that could be extended or used instead?"
|
||||
- **Retain Fast Path for Simple Searches:** For extremely simple, unambiguous checks (e.g., "Does `package.json` include `lodash`?"), perform a direct search to save time. Default to delegation for any open-ended "investigations."
|
||||
|
||||
### 4. Evaluate Best Practices
|
||||
Check if the new code aligns with the project's established conventions.
|
||||
- **Error Handling:** Does it use the project's standard error classes or logging mechanisms?
|
||||
- **State Management:** Does it bypass established stores or contexts?
|
||||
- **Styling:** Does it hardcode colors or spacing instead of using theme variables?
|
||||
If the PR introduces a new pattern, compare it against the documented standards and explicitly confirm if an existing project pattern should have been used instead.
|
||||
|
||||
### 5. Formulate Constructive Feedback
|
||||
If you discover that the PR duplicates existing functionality or ignores a best practice:
|
||||
- Provide a clear review comment.
|
||||
- **Identify the Source:** Explicitly mention the absolute or project-relative file path and the specific symbol (function, component, class) that should be reused.
|
||||
- **Implementation Guidance:** Provide a brief code snippet or a clear explanation showing **how** to integrate the existing code to fulfill the task's requirements.
|
||||
- **Explain the Value:** Briefly explain why reusing the existing code is beneficial (e.g., maintainability, consistency, built-in edge case handling).
|
||||
|
||||
Example comment:
|
||||
> "It looks like this PR introduces a new `formatDate` utility. We already have a robust, tested `formatDate` function in `src/utils/dateHelpers.ts`.
|
||||
>
|
||||
> You can replace your implementation by importing it like this:
|
||||
> ```typescript
|
||||
> import { formatDate } from '../utils/dateHelpers';
|
||||
>
|
||||
> // Then use it here:
|
||||
> const displayDate = formatDate(userDate, 'MMM Do, YYYY');
|
||||
> ```
|
||||
> Reusing this ensures that the date formatting remains consistent with the rest of the application and handles timezone conversions correctly."
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
name: string-reviewer
|
||||
description: >
|
||||
Use this skill when asked to review text and user-facing strings within the codebase. It ensures that these strings follow rules on clarity,
|
||||
usefulness, brevity and style.
|
||||
---
|
||||
|
||||
# String Reviewer
|
||||
|
||||
## Instructions
|
||||
|
||||
Act as a Senior UX Writer. Look for user-facing strings that are too long,
|
||||
unclear, or inconsistent. This includes inline text, error messages, and other
|
||||
user-facing text.
|
||||
|
||||
Do NOT automatically change strings without user approval. You must only suggest
|
||||
changes and do not attempt to rewrite them directly unless the user explicitly
|
||||
asks you to do so.
|
||||
|
||||
## Core voice principles
|
||||
|
||||
The system prioritizes deterministic clarity over conversational fluff. We
|
||||
provide telemetry, not etiquette, ensuring the user retains absolute agency..
|
||||
|
||||
1. **Deterministic clarity:** Distinguish between certain system/service states
|
||||
(Cloud Billing, IAM, the System) and probabilistic AI analysis (Gemini).
|
||||
2. **System transparency:** Replace "Loading..." with active technical telemetry
|
||||
(e.g., Tracing stack traces...). Keep status updates under 5 words.
|
||||
3. **Front-loaded actionability:** Always use the [Goal] + [Action] pattern.
|
||||
Lead with intent so users can scan left-to-right.
|
||||
4. **Agentic error recovery:** Every error must be a pivot point. Pair failures
|
||||
with one-click recovery commands or suggested prompts.
|
||||
5. **Contextual humility:** Reserve disclaimers and "be careful" warnings for P0
|
||||
(destructive/irreversible) tasks only. Stop warning-fatigue.
|
||||
|
||||
## The writing checklist
|
||||
|
||||
Use this checklist to audit UI strings and AI responses.
|
||||
|
||||
### Identity and voice
|
||||
- **Eliminate the "I":** Remove all first-person pronouns (I, me, my, mine).
|
||||
- **Subject attribution:** Refer to the AI as Gemini and the infrastructure as
|
||||
the - system or the CLI.
|
||||
- **Active voice:** Ensure the subject (Gemini or the system) is clearly
|
||||
performing the action.
|
||||
- **Ownership rule:** Use the system for execution (doing) and Gemini for
|
||||
analysis (thinking)
|
||||
|
||||
### Structural scannability
|
||||
- **The skip test:** Do the first 3 words describe the user’s intent? If not,
|
||||
rewrite.
|
||||
- **Goal-first sequence:** Use the template: [To Accomplish X] + [Do Y].
|
||||
- **The 5-word rule:** Keep status updates and loading states under 5 words.
|
||||
- **Telemetry over etiquette:** Remove polite filler (Please wait, Thank you,
|
||||
Certainly). Replace with raw data or progress indicators.
|
||||
- **Micro-state cycles:** For tasks $> 3$ seconds, cycle through specific
|
||||
sub-states (e.g., Parsing logs... ➔ Identifying patterns...) to show momentum.
|
||||
|
||||
|
||||
### Technical accuracy and humility
|
||||
- **Verb signal check:** Use deterministic verbs (is, will, must) for system
|
||||
state/infrastructure.
|
||||
- Use probabilistic verbs (suggests, appears, may, identifies) for AI output.
|
||||
- **No 100% certainty:** Never attribute absolute certainty to model-generated
|
||||
content.
|
||||
- **Precision over fuzziness:** Use technical metrics (latency, tokens, compute) instead of "speed" or "cost."
|
||||
- **Instructional warnings:** Every warning must include a specific corrective action (e.g., "Perform a dry-run first" or "Review line 42").
|
||||
|
||||
### Agentic error recovery
|
||||
- **The one-step rule:** Pair every error message with exactly one immediate
|
||||
path to a fix (command, link, or prompt).
|
||||
- **Human-first:** Provide a human-readable explanation before machine error
|
||||
codes (e.g., 404, 500).
|
||||
- **Suggested prompts:** Offer specific text for the user to copy/click like
|
||||
“Ask Gemini: 'Explain this port error.'”
|
||||
|
||||
### Use consistent terminology
|
||||
|
||||
Ensure all terminology aligns with the project [word
|
||||
list](./references/word-list.md).
|
||||
|
||||
If a string uses a term marked "do not use" or "use with caution," provide a
|
||||
correction based on the preferred terms.
|
||||
|
||||
## Ensure consistent style for settings
|
||||
|
||||
If `packages/cli/src/config/settingsSchema.ts` is modified, confirm labels and
|
||||
descriptions specifically follow the unique [Settings
|
||||
guidelines](./references/settings.md).
|
||||
|
||||
## Output format
|
||||
When suggesting changes, always present your review using the following list
|
||||
format. Do not provide suggestions outside of this list..
|
||||
|
||||
```
|
||||
1. **{Rationale/Principle Violated}**
|
||||
- ❌ "{incorrect phrase}"
|
||||
- ✅ `"{corrected phrase}"`
|
||||
```
|
||||
@@ -0,0 +1,28 @@
|
||||
# Settings
|
||||
|
||||
## Noun-First Labeling (Scannability)
|
||||
|
||||
Labels must start with the subject of the setting, not the action. This allows
|
||||
users to scan for the feature they want to change.
|
||||
|
||||
- **Rule:** `[Noun]` `[Attribute/Action]`
|
||||
- **Example:** `Show line numbers` becomes simply `Line numbers`
|
||||
|
||||
## Positive Boolean Logic (Cognitive Ease)
|
||||
|
||||
Eliminate "double negatives." Booleans should represent the presence of a
|
||||
feature, not its absence.
|
||||
|
||||
- **Rule:** Replace `Disable {feature}` or `Hide {Feature}` with
|
||||
`{Feature} enabled` or simply `{Feature}`.
|
||||
- **Example:** Change "Disable auto update" to "Auto update".
|
||||
- **Implementation:** Invert the boolean value in your config loader so true
|
||||
always equals `On`
|
||||
|
||||
## Verb Stripping (Brevity)
|
||||
|
||||
Remove redundant leading verbs like "Enable," "Use," "Display," or "Show" unless
|
||||
they are part of a specific technical term.
|
||||
|
||||
- **Rule**: If the label works without the verb, remove it
|
||||
- **Example**: Change `Enable prompt completion` to `Prompt completion`
|
||||
@@ -0,0 +1,61 @@
|
||||
## Terms
|
||||
|
||||
### Preferred
|
||||
|
||||
- Use **create** when a user is creating or setting up something.
|
||||
- Use **allow** instead of **may** to indicate that permission has been granted
|
||||
to perform some action.
|
||||
- Use **canceled**, not **cancelled**.
|
||||
- Use **configure** to refer to the process of changing the attributes of a
|
||||
feature, even if that includes turning on or off the feature.
|
||||
- Use **delete** when the action being performed is destructive.
|
||||
- Use **enable** for binary operations that turn a feature or API on. Use "turn
|
||||
on" and "turn off" instead of "enable" and "disable" for other situations.
|
||||
- Use **key combination** to refer to pressing multiple keys simultaneously.
|
||||
- Use **key sequence** to refer to pressing multiple keys separately in order.
|
||||
- Use **modify** to refer to something that has changed vs obtaining the latest
|
||||
version of something.
|
||||
- Use **remove** when the action being performed takes an item out of a larger
|
||||
whole, but doesn't destroy the item itself.
|
||||
- Use **set up** as a verb. Use **setup** as a noun or adjective.
|
||||
- Use **show**. In general, use paired with **hide**.
|
||||
- Use **sign in**, **sign out** as a verb. Use **sign-in** or **sign-out** as a
|
||||
noun or adjective.
|
||||
- Use **update** when you mean to obtain the latest version of something.
|
||||
- Use **want** instead of **like** or **would like**.
|
||||
|
||||
#### Don't use
|
||||
|
||||
- Don't use **etc.** It's redundant. To convey that a series is incomplete,
|
||||
introduce it with "such as" instead.
|
||||
- Don't use **hostname**, use "host name" instead.
|
||||
- Don't use **in order to**. It's too formal. "Before you can" is usually better
|
||||
in UI text.
|
||||
- Don't use **one or more**. Specify the quantity where possible. Use "at least
|
||||
one" when the quantity is 1+ but you can't be sure of the number. Likewise,
|
||||
use "at least one" when the user must choose a quantity of 1+.
|
||||
- Don't use the terms **log in**, **log on**, **login**, **logout** or **log
|
||||
out**.
|
||||
- Don't use **like** or **would you like**. Use **want** instead. Better yet,
|
||||
rephrase so that it's not referring to the user's emotional state, but rather
|
||||
what is required.
|
||||
|
||||
#### Use with caution
|
||||
|
||||
- Avoid using **leverage**, especially as a verb. "Leverage" is considered a
|
||||
buzzword largely devoid of meaning apart from the simpler "use".
|
||||
- Avoid using **once** as a synonym for "after". Typically, when "once" is used
|
||||
in this way, it is followed by a verb in the perfect tense.
|
||||
- Don't use **e.g.** Use "example", "such as", "like", or "for example". The
|
||||
phrase is always followed by a comma.
|
||||
- Don't use **i.e.** unless absolutely essential to make text fit. Use "that is"
|
||||
instead.
|
||||
- Use **disable** for binary operations that turn a feature or API off. Use
|
||||
"turn on" and "turn off" instead of "enable" and "disable" for other
|
||||
situations. For UI elements that are not available, use "dimmed" instead of
|
||||
"disabled".
|
||||
- Use **please** only when you're asking the user to do something inconvenient,
|
||||
not just following the instructions in a typical flow.
|
||||
- Use **really** sparingly in such constructions as "Do you really want to..."
|
||||
Because of the weight it puts on the decision, it should be used to confirm
|
||||
actions that the user is extremely unlikely to make.
|
||||
@@ -0,0 +1 @@
|
||||
packages/core/src/services/scripts/*.exe
|
||||
@@ -1,7 +1,9 @@
|
||||
name: 'Website issue'
|
||||
description: 'Report an issue with the Gemini CLI Website and Gemini CLI Extensions Gallery'
|
||||
title: 'GeminiCLI.com Feedback: [ISSUE]'
|
||||
labels:
|
||||
- 'area/extensions'
|
||||
- 'area/documentation'
|
||||
body:
|
||||
- type: 'markdown'
|
||||
attributes:
|
||||
|
||||
@@ -175,7 +175,9 @@ runs:
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_CORE_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${INPUTS_CORE_PACKAGE_NAME} false --silent
|
||||
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
|
||||
npm dist-tag rm ${INPUTS_CORE_PACKAGE_NAME} false
|
||||
fi
|
||||
|
||||
- name: '🔗 Install latest core package'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
@@ -221,7 +223,9 @@ runs:
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_CLI_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${INPUTS_CLI_PACKAGE_NAME} false --silent
|
||||
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
|
||||
npm dist-tag rm ${INPUTS_CLI_PACKAGE_NAME} false
|
||||
fi
|
||||
|
||||
- name: 'Get a2a-server Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
@@ -246,7 +250,9 @@ runs:
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${INPUTS_A2A_PACKAGE_NAME} false --silent
|
||||
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
|
||||
npm dist-tag rm ${INPUTS_A2A_PACKAGE_NAME} false
|
||||
fi
|
||||
|
||||
- name: '🔬 Verify NPM release by version'
|
||||
uses: './.github/actions/verify-release'
|
||||
@@ -285,8 +291,25 @@ runs:
|
||||
INPUTS_PREVIOUS_TAG: '${{ inputs.previous-tag }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
rm -f gemini-cli-bundle.zip
|
||||
(cd bundle && chmod +x gemini.js && zip -r ../gemini-cli-bundle.zip .)
|
||||
|
||||
echo "Testing the generated bundle archive..."
|
||||
rm -rf test-bundle
|
||||
mkdir -p test-bundle
|
||||
unzip -q gemini-cli-bundle.zip -d test-bundle
|
||||
|
||||
# Verify it runs and outputs a version
|
||||
BUNDLE_VERSION=$(node test-bundle/gemini.js --version | xargs)
|
||||
echo "Bundle version output: ${BUNDLE_VERSION}"
|
||||
if [[ -z "${BUNDLE_VERSION}" ]]; then
|
||||
echo "Error: Bundle failed to execute or return version."
|
||||
exit 1
|
||||
fi
|
||||
rm -rf test-bundle
|
||||
|
||||
gh release create "${INPUTS_RELEASE_TAG}" \
|
||||
bundle/gemini.js \
|
||||
gemini-cli-bundle.zip \
|
||||
--target "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}" \
|
||||
--title "Release ${INPUTS_RELEASE_TAG}" \
|
||||
--notes-start-tag "${INPUTS_PREVIOUS_TAG}" \
|
||||
|
||||
@@ -34,7 +34,7 @@ runs:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@v4'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
ref: '${{ inputs.github-sha }}'
|
||||
fetch-depth: 0
|
||||
@@ -45,11 +45,11 @@ runs:
|
||||
shell: 'bash'
|
||||
run: 'npm run build'
|
||||
- name: 'Set up QEMU'
|
||||
uses: 'docker/setup-qemu-action@v3'
|
||||
uses: 'docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130' # ratchet:docker/setup-qemu-action@v3
|
||||
- name: 'Set up Docker Buildx'
|
||||
uses: 'docker/setup-buildx-action@v3'
|
||||
uses: 'docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f' # ratchet:docker/setup-buildx-action@v3
|
||||
- name: 'Log in to GitHub Container Registry'
|
||||
uses: 'docker/login-action@v3'
|
||||
uses: 'docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9' # ratchet:docker/login-action@v3
|
||||
with:
|
||||
registry: 'docker.io'
|
||||
username: '${{ inputs.dockerhub-username }}'
|
||||
|
||||
@@ -18,6 +18,13 @@ runs:
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
- name: 'Install system dependencies'
|
||||
if: "runner.os == 'Linux'"
|
||||
run: |
|
||||
sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq bubblewrap
|
||||
# Ubuntu 24.04+ requires this to allow bwrap to function in CI
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
|
||||
shell: 'bash'
|
||||
- name: 'Run Tests'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ inputs.gemini_api_key }}'
|
||||
|
||||
@@ -36,7 +36,7 @@ runs:
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: 'setup node'
|
||||
uses: 'actions/setup-node@v4'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
|
||||
@@ -347,6 +347,36 @@ async function run() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Remove status/need-triage from maintainer-only issues since they
|
||||
// don't need community triage. We always attempt removal rather than
|
||||
// checking the (potentially stale) label snapshot, because the
|
||||
// issue-opened-labeler workflow runs concurrently and may add the
|
||||
// label after our snapshot was taken.
|
||||
if (isDryRun) {
|
||||
console.log(
|
||||
`[DRY RUN] Would remove status/need-triage from ${issueKey}`,
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await octokit.rest.issues.removeLabel({
|
||||
owner: issueInfo.owner,
|
||||
repo: issueInfo.repo,
|
||||
issue_number: issueInfo.number,
|
||||
name: 'status/need-triage',
|
||||
});
|
||||
console.log(`Removed status/need-triage from ${issueKey}`);
|
||||
} catch (removeError) {
|
||||
// 404 means the label wasn't present — that's fine.
|
||||
if (removeError.status === 404) {
|
||||
console.log(
|
||||
`status/need-triage not present on ${issueKey}, skipping.`,
|
||||
);
|
||||
} else {
|
||||
throw removeError;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error processing label for ${issueKey}: ${error.message}`);
|
||||
}
|
||||
|
||||
@@ -264,6 +264,27 @@ jobs:
|
||||
run: 'npm run build'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Ensure Chrome is available'
|
||||
shell: 'pwsh'
|
||||
run: |
|
||||
$chromePaths = @(
|
||||
"${env:ProgramFiles}\Google\Chrome\Application\chrome.exe",
|
||||
"${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe"
|
||||
)
|
||||
$chromeExists = $chromePaths | Where-Object { Test-Path $_ } | Select-Object -First 1
|
||||
if (-not $chromeExists) {
|
||||
Write-Host 'Chrome not found, installing via Chocolatey...'
|
||||
choco install googlechrome -y --no-progress --ignore-checksums
|
||||
}
|
||||
$installed = $chromePaths | Where-Object { Test-Path $_ } | Select-Object -First 1
|
||||
if ($installed) {
|
||||
Write-Host "Chrome found at: $installed"
|
||||
& $installed --version
|
||||
} else {
|
||||
Write-Error 'Chrome installation failed'
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: 'Run E2E tests'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
@@ -313,8 +334,20 @@ jobs:
|
||||
if: "${{ steps.check_evals.outputs.should_run == 'true' }}"
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
GEMINI_MODEL: 'gemini-3-pro-preview'
|
||||
# Disable Vitest internal retries to avoid double-retrying;
|
||||
# custom retry logic is handled in evals/test-helper.ts
|
||||
VITEST_RETRY: 0
|
||||
run: 'npm run test:always_passing_evals'
|
||||
|
||||
- name: 'Upload Reliability Logs'
|
||||
if: "always() && steps.check_evals.outputs.should_run == 'true'"
|
||||
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'eval-logs-${{ github.run_id }}-${{ github.run_attempt }}'
|
||||
path: 'evals/logs/api-reliability.jsonl'
|
||||
retention-days: 7
|
||||
|
||||
e2e:
|
||||
name: 'E2E'
|
||||
if: |
|
||||
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Cache Linters'
|
||||
uses: 'actions/cache@v4'
|
||||
uses: 'actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830' # ratchet:actions/cache@v4
|
||||
with:
|
||||
path: '${{ env.GEMINI_LINT_TEMP_DIR }}'
|
||||
key: "${{ runner.os }}-${{ runner.arch }}-linters-${{ hashFiles('scripts/lint.js') }}"
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Cache ESLint'
|
||||
uses: 'actions/cache@v4'
|
||||
uses: 'actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830' # ratchet:actions/cache@v4
|
||||
with:
|
||||
path: '.eslintcache'
|
||||
key: "${{ runner.os }}-eslint-${{ hashFiles('package-lock.json', 'eslint.config.js') }}"
|
||||
@@ -114,6 +114,9 @@ jobs:
|
||||
- name: 'Run sensitive keyword linter'
|
||||
run: 'node scripts/lint.js --sensitive-keywords'
|
||||
|
||||
- name: 'Run GitHub Actions pinning linter'
|
||||
run: 'node scripts/lint.js --check-github-actions-pinning'
|
||||
|
||||
link_checker:
|
||||
name: 'Link Checker'
|
||||
runs-on: 'ubuntu-latest'
|
||||
@@ -158,6 +161,12 @@ jobs:
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Install system dependencies'
|
||||
run: |
|
||||
sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq bubblewrap
|
||||
# Ubuntu 24.04+ requires this to allow bwrap to function in CI
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
|
||||
|
||||
- name: 'Install dependencies for testing'
|
||||
run: 'npm ci'
|
||||
|
||||
@@ -166,10 +175,10 @@ jobs:
|
||||
NO_COLOR: true
|
||||
run: |
|
||||
if [[ "${{ matrix.shard }}" == "cli" ]]; then
|
||||
npm run test:ci --workspace @google/gemini-cli
|
||||
npm run test:ci --workspace "@google/gemini-cli"
|
||||
else
|
||||
# Explicitly list non-cli packages to ensure they are sharded correctly
|
||||
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present
|
||||
npm run test:ci --workspace "@google/gemini-cli-core" --workspace "@google/gemini-cli-a2a-server" --workspace "gemini-cli-vscode-ide-companion" --workspace "@google/gemini-cli-test-utils" --if-present -- --coverage.enabled=false
|
||||
npm run test:scripts
|
||||
fi
|
||||
|
||||
@@ -254,10 +263,10 @@ jobs:
|
||||
NO_COLOR: true
|
||||
run: |
|
||||
if [[ "${{ matrix.shard }}" == "cli" ]]; then
|
||||
npm run test:ci --workspace @google/gemini-cli -- --coverage.enabled=false
|
||||
npm run test:ci --workspace "@google/gemini-cli" -- --coverage.enabled=false
|
||||
else
|
||||
# Explicitly list non-cli packages to ensure they are sharded correctly
|
||||
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false
|
||||
npm run test:ci --workspace "@google/gemini-cli-core" --workspace "@google/gemini-cli-a2a-server" --workspace "gemini-cli-vscode-ide-companion" --workspace "@google/gemini-cli-test-utils" --if-present -- --coverage.enabled=false
|
||||
npm run test:scripts
|
||||
fi
|
||||
|
||||
@@ -420,11 +429,14 @@ jobs:
|
||||
NODE_ENV: 'test'
|
||||
run: |
|
||||
if ("${{ matrix.shard }}" -eq "cli") {
|
||||
npm run test:ci --workspace @google/gemini-cli -- --coverage.enabled=false
|
||||
npm run test:ci --workspace "@google/gemini-cli" -- --coverage.enabled=false
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
} else {
|
||||
# Explicitly list non-cli packages to ensure they are sharded correctly
|
||||
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false
|
||||
npm run test:ci --workspace "@google/gemini-cli-core" --workspace "@google/gemini-cli-a2a-server" --workspace "gemini-cli-vscode-ide-companion" --workspace "@google/gemini-cli-test-utils" --if-present -- --coverage.enabled=false
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
npm run test:scripts
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
}
|
||||
shell: 'pwsh'
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
name: 'Evals: PR Evaluation & Regression'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: ['opened', 'synchronize', 'reopened', 'ready_for_review']
|
||||
paths:
|
||||
- 'packages/core/src/prompts/**'
|
||||
- 'packages/core/src/tools/**'
|
||||
- 'packages/core/src/agents/**'
|
||||
- 'evals/**'
|
||||
- '!**/*.test.ts'
|
||||
- '!**/*.test.tsx'
|
||||
workflow_dispatch:
|
||||
|
||||
# Prevents multiple runs for the same PR simultaneously (saves tokens)
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
pull-requests: 'write'
|
||||
contents: 'read'
|
||||
actions: 'read'
|
||||
|
||||
jobs:
|
||||
pr-evaluation:
|
||||
name: 'Evaluate Steering & Regressions'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
if: "github.repository == 'google-gemini/gemini-cli' && (github.event_name != 'pull_request' || (github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository))"
|
||||
# External contributors' PRs will wait for approval in this environment
|
||||
environment: |-
|
||||
${{ (github.event.pull_request.head.repo.full_name == github.repository) && 'internal' || 'external-evals' }}
|
||||
env:
|
||||
# CENTRALIZED MODEL LIST
|
||||
MODEL_LIST: 'gemini-3-flash-preview'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Detect Steering Changes'
|
||||
id: 'detect'
|
||||
run: |
|
||||
SHOULD_RUN=$(node scripts/changed_prompt.js)
|
||||
STEERING_DETECTED=$(node scripts/changed_prompt.js --steering-only)
|
||||
echo "SHOULD_RUN=$SHOULD_RUN" >> "$GITHUB_OUTPUT"
|
||||
echo "STEERING_DETECTED=$STEERING_DETECTED" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Install dependencies'
|
||||
if: "steps.detect.outputs.SHOULD_RUN == 'true'"
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Build project'
|
||||
if: "steps.detect.outputs.SHOULD_RUN == 'true'"
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Analyze PR Content (Guidance)'
|
||||
if: "steps.detect.outputs.STEERING_DETECTED == 'true'"
|
||||
id: 'analysis'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: |
|
||||
# Check for behavioral eval changes
|
||||
EVAL_CHANGES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep "^evals/" || true)
|
||||
if [ -z "$EVAL_CHANGES" ]; then
|
||||
echo "MISSING_EVALS=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# Check if user is a maintainer
|
||||
USER_PERMISSION=$(gh api repos/${{ github.repository }}/collaborators/${{ github.actor }}/permission --jq '.permission')
|
||||
if [[ "$USER_PERMISSION" == "admin" || "$USER_PERMISSION" == "write" ]]; then
|
||||
echo "IS_MAINTAINER=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: 'Execute Regression Check'
|
||||
if: "steps.detect.outputs.SHOULD_RUN == 'true'"
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
MODEL_LIST: '${{ env.MODEL_LIST }}'
|
||||
run: |
|
||||
# Run the regression check loop. The script saves the report to a file.
|
||||
node scripts/run_eval_regression.js
|
||||
|
||||
# Use the generated report file if it exists
|
||||
if [[ -f eval_regression_report.md ]]; then
|
||||
echo "REPORT_FILE=eval_regression_report.md" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: 'Post or Update PR Comment'
|
||||
if: "always() && (steps.detect.outputs.STEERING_DETECTED == 'true' || env.REPORT_FILE != '')"
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: |
|
||||
# 1. Build the full comment body
|
||||
{
|
||||
if [[ -f eval_regression_report.md ]]; then
|
||||
cat eval_regression_report.md
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ "${{ steps.detect.outputs.STEERING_DETECTED }}" == "true" ]]; then
|
||||
echo "### 🧠 Model Steering Guidance"
|
||||
echo ""
|
||||
echo "This PR modifies files that affect the model's behavior (prompts, tools, or instructions)."
|
||||
echo ""
|
||||
|
||||
if [[ "${{ steps.analysis.outputs.MISSING_EVALS }}" == "true" ]]; then
|
||||
echo "- ⚠️ **Consider adding Evals:** No behavioral evaluations (\`evals/*.eval.ts\`) were added or updated in this PR. Consider [adding a test case](https://github.com/google-gemini/gemini-cli/blob/main/evals/README.md#creating-an-evaluation) to verify the new behavior and prevent regressions."
|
||||
fi
|
||||
|
||||
if [[ "${{ steps.analysis.outputs.IS_MAINTAINER }}" == "true" ]]; then
|
||||
echo "- 🚀 **Maintainer Reminder:** Please ensure that these changes do not regress results on benchmark evals before merging."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "---"
|
||||
echo "*This is an automated guidance message triggered by steering logic signatures.*"
|
||||
echo "<!-- eval-pr-report -->"
|
||||
} > full_comment.md
|
||||
|
||||
# 2. Find if a comment with our unique tag already exists
|
||||
# We extract the numeric ID from the URL to ensure compatibility with the REST API
|
||||
COMMENT_ID=$(gh pr view ${{ github.event.pull_request.number }} --json comments --jq '.comments[] | select(.body | contains("<!-- eval-pr-report -->")) | .url' | grep -oE "[0-9]+$" | head -n 1)
|
||||
|
||||
# 3. Update or Create the comment
|
||||
if [ -n "$COMMENT_ID" ]; then
|
||||
echo "Updating existing comment $COMMENT_ID via API..."
|
||||
gh api -X PATCH "repos/${{ github.repository }}/issues/comments/$COMMENT_ID" -F body=@full_comment.md
|
||||
else
|
||||
echo "Creating new PR comment..."
|
||||
gh pr comment ${{ github.event.pull_request.number }} --body-file full_comment.md
|
||||
fi
|
||||
@@ -61,6 +61,9 @@ jobs:
|
||||
GEMINI_MODEL: '${{ matrix.model }}'
|
||||
RUN_EVALS: "${{ github.event.inputs.run_all != 'false' }}"
|
||||
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
|
||||
# Disable Vitest internal retries to avoid double-retrying;
|
||||
# custom retry logic is handled in evals/test-helper.ts
|
||||
VITEST_RETRY: 0
|
||||
run: |
|
||||
CMD="npm run test:all_evals"
|
||||
PATTERN="${TEST_NAME_PATTERN}"
|
||||
|
||||
@@ -121,6 +121,7 @@ jobs:
|
||||
'area/security',
|
||||
'area/platform',
|
||||
'area/extensions',
|
||||
'area/documentation',
|
||||
'area/unknown'
|
||||
];
|
||||
const labelNames = labels.map(label => label.name).filter(name => allowedLabels.includes(name));
|
||||
@@ -255,6 +256,14 @@ jobs:
|
||||
"Issues with a specific extension."
|
||||
"Feature request for the extension ecosystem."
|
||||
|
||||
area/documentation
|
||||
- Description: Issues related to user-facing documentation and other content on the documentation website.
|
||||
- Example Issues:
|
||||
"A typo in a README file."
|
||||
"DOCS: A command is not working as described in the documentation."
|
||||
"A request for a new documentation page."
|
||||
"Instructions missing for skills feature"
|
||||
|
||||
area/unknown
|
||||
- Description: Issues that do not clearly fit into any other defined area/ category, or where information is too limited to make a determination. Use this when no other area is appropriate.
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
echo '🔍 Finding issues missing area labels...'
|
||||
NO_AREA_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
--search 'is:open is:issue -label:area/core -label:area/agent -label:area/enterprise -label:area/non-interactive -label:area/security -label:area/platform -label:area/extensions -label:area/unknown' --limit 100 --json number,title,body)"
|
||||
--search 'is:open is:issue -label:area/core -label:area/agent -label:area/enterprise -label:area/non-interactive -label:area/security -label:area/platform -label:area/extensions -label:area/documentation -label:area/unknown' --limit 100 --json number,title,body)"
|
||||
|
||||
echo '🔍 Finding issues missing kind labels...'
|
||||
NO_KIND_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
@@ -204,6 +204,7 @@ jobs:
|
||||
Categorization Guidelines (Area):
|
||||
area/agent: Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality
|
||||
area/core: User Interface, OS Support, Core Functionality
|
||||
area/documentation: End-user and contributor-facing documentation, website-related
|
||||
area/enterprise: Telemetry, Policy, Quota / Licensing
|
||||
area/extensions: Gemini CLI extensions capability
|
||||
area/non-interactive: GitHub Actions, SDK, 3P Integrations, Shell Scripting, Command line automation
|
||||
|
||||
@@ -28,14 +28,14 @@ jobs:
|
||||
steps:
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
uses: 'actions/create-github-app-token@v2'
|
||||
uses: 'actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
permission-issues: 'write'
|
||||
|
||||
- name: 'Process Stale Issues'
|
||||
uses: 'actions/github-script@v7'
|
||||
uses: 'actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b' # ratchet:actions/github-script@v7
|
||||
env:
|
||||
DRY_RUN: '${{ inputs.dry_run }}'
|
||||
with:
|
||||
|
||||
@@ -27,19 +27,21 @@ jobs:
|
||||
APP_ID: '${{ secrets.APP_ID }}'
|
||||
if: |-
|
||||
${{ env.APP_ID != '' }}
|
||||
uses: 'actions/create-github-app-token@v2'
|
||||
uses: 'actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
|
||||
- name: 'Process Stale PRs'
|
||||
uses: 'actions/github-script@v7'
|
||||
uses: 'actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b' # ratchet:actions/github-script@v7
|
||||
env:
|
||||
DRY_RUN: '${{ inputs.dry_run }}'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
||||
script: |
|
||||
const dryRun = process.env.DRY_RUN === 'true';
|
||||
const fourteenDaysAgo = new Date();
|
||||
fourteenDaysAgo.setDate(fourteenDaysAgo.getDate() - 14);
|
||||
const thirtyDaysAgo = new Date();
|
||||
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
||||
|
||||
@@ -56,48 +58,38 @@ jobs:
|
||||
for (const m of members) maintainerLogins.add(m.login.toLowerCase());
|
||||
core.info(`Successfully fetched ${members.length} team members from ${team_slug}`);
|
||||
} catch (e) {
|
||||
core.warning(`Failed to fetch team members from ${team_slug}: ${e.message}`);
|
||||
// Silently skip if permissions are insufficient; we will rely on author_association
|
||||
core.debug(`Skipped team fetch for ${team_slug}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
const isGooglerCache = new Map();
|
||||
const isGoogler = async (login) => {
|
||||
if (isGooglerCache.has(login)) return isGooglerCache.get(login);
|
||||
const isMaintainer = async (login, assoc) => {
|
||||
// Reliably identify maintainers using authorAssociation (provided by GitHub)
|
||||
// and organization membership (if available).
|
||||
const isTeamMember = maintainerLogins.has(login.toLowerCase());
|
||||
const isRepoMaintainer = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(assoc);
|
||||
|
||||
if (isTeamMember || isRepoMaintainer) return true;
|
||||
|
||||
// Fallback: Check if user belongs to the 'google' or 'googlers' orgs (requires permission)
|
||||
try {
|
||||
// Check membership in 'googlers' or 'google' orgs
|
||||
const orgs = ['googlers', 'google'];
|
||||
for (const org of orgs) {
|
||||
try {
|
||||
await github.rest.orgs.checkMembershipForUser({
|
||||
org: org,
|
||||
username: login
|
||||
});
|
||||
core.info(`User ${login} is a member of ${org} organization.`);
|
||||
isGooglerCache.set(login, true);
|
||||
await github.rest.orgs.checkMembershipForUser({ org: org, username: login });
|
||||
return true;
|
||||
} catch (e) {
|
||||
// 404 just means they aren't a member, which is fine
|
||||
if (e.status !== 404) throw e;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
core.warning(`Failed to check org membership for ${login}: ${e.message}`);
|
||||
// Gracefully ignore failures here
|
||||
}
|
||||
|
||||
isGooglerCache.set(login, false);
|
||||
return false;
|
||||
};
|
||||
|
||||
const isMaintainer = async (login, assoc) => {
|
||||
const isTeamMember = maintainerLogins.has(login.toLowerCase());
|
||||
const isRepoMaintainer = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(assoc);
|
||||
if (isTeamMember || isRepoMaintainer) return true;
|
||||
|
||||
return await isGoogler(login);
|
||||
};
|
||||
|
||||
// 2. Determine which PRs to check
|
||||
// 2. Fetch all open PRs
|
||||
let prs = [];
|
||||
if (context.eventName === 'pull_request') {
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
@@ -118,64 +110,77 @@ jobs:
|
||||
for (const pr of prs) {
|
||||
const maintainerPr = await isMaintainer(pr.user.login, pr.author_association);
|
||||
const isBot = pr.user.type === 'Bot' || pr.user.login.endsWith('[bot]');
|
||||
if (maintainerPr || isBot) continue;
|
||||
|
||||
// Detection Logic for Linked Issues
|
||||
// Check 1: Official GitHub "Closing Issue" link (GraphQL)
|
||||
const linkedIssueQuery = `query($owner:String!, $repo:String!, $number:Int!) {
|
||||
// Helper: Fetch labels and linked issues via GraphQL
|
||||
const prDetailsQuery = `query($owner:String!, $repo:String!, $number:Int!) {
|
||||
repository(owner:$owner, name:$repo) {
|
||||
pullRequest(number:$number) {
|
||||
closingIssuesReferences(first: 1) { totalCount }
|
||||
closingIssuesReferences(first: 10) {
|
||||
nodes {
|
||||
number
|
||||
labels(first: 20) {
|
||||
nodes { name }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
let hasClosingLink = false;
|
||||
let linkedIssues = [];
|
||||
try {
|
||||
const res = await github.graphql(linkedIssueQuery, {
|
||||
const res = await github.graphql(prDetailsQuery, {
|
||||
owner: context.repo.owner, repo: context.repo.repo, number: pr.number
|
||||
});
|
||||
hasClosingLink = res.repository.pullRequest.closingIssuesReferences.totalCount > 0;
|
||||
} catch (e) {}
|
||||
|
||||
// Check 2: Regex for mentions (e.g., "Related to #123", "Part of #123", "#123")
|
||||
// We check for # followed by numbers or direct URLs to issues.
|
||||
const body = pr.body || '';
|
||||
const mentionRegex = /(?:#|https:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/)(\d+)/i;
|
||||
const hasMentionLink = mentionRegex.test(body);
|
||||
|
||||
const hasLinkedIssue = hasClosingLink || hasMentionLink;
|
||||
|
||||
// Logic for Closed PRs (Auto-Reopen)
|
||||
if (pr.state === 'closed' && context.eventName === 'pull_request' && context.payload.action === 'edited') {
|
||||
if (hasLinkedIssue) {
|
||||
core.info(`PR #${pr.number} now has a linked issue. Reopening.`);
|
||||
if (!dryRun) {
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr.number,
|
||||
state: 'open'
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body: "Thank you for linking an issue! This pull request has been automatically reopened."
|
||||
});
|
||||
}
|
||||
}
|
||||
continue;
|
||||
linkedIssues = res.repository.pullRequest.closingIssuesReferences.nodes;
|
||||
} catch (e) {
|
||||
core.warning(`GraphQL fetch failed for PR #${pr.number}: ${e.message}`);
|
||||
}
|
||||
|
||||
// Logic for Open PRs (Immediate Closure)
|
||||
if (pr.state === 'open' && !maintainerPr && !hasLinkedIssue && !isBot) {
|
||||
core.info(`PR #${pr.number} is missing a linked issue. Closing.`);
|
||||
// Check for mentions in body as fallback (regex)
|
||||
const body = pr.body || '';
|
||||
const mentionRegex = /(?:#|https:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/)(\d+)/i;
|
||||
const matches = body.match(mentionRegex);
|
||||
if (matches && linkedIssues.length === 0) {
|
||||
const issueNumber = parseInt(matches[1]);
|
||||
try {
|
||||
const { data: issue } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber
|
||||
});
|
||||
linkedIssues = [{ number: issueNumber, labels: { nodes: issue.labels.map(l => ({ name: l.name })) } }];
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 3. Enforcement Logic
|
||||
const prLabels = pr.labels.map(l => l.name.toLowerCase());
|
||||
const hasHelpWanted = prLabels.includes('help wanted') ||
|
||||
linkedIssues.some(issue => issue.labels.nodes.some(l => l.name.toLowerCase() === 'help wanted'));
|
||||
|
||||
const hasMaintainerOnly = prLabels.includes('🔒 maintainer only') ||
|
||||
linkedIssues.some(issue => issue.labels.nodes.some(l => l.name.toLowerCase() === '🔒 maintainer only'));
|
||||
|
||||
const hasLinkedIssue = linkedIssues.length > 0;
|
||||
|
||||
// Closure Policy: No help-wanted label = Close after 14 days
|
||||
if (pr.state === 'open' && !hasHelpWanted && !hasMaintainerOnly) {
|
||||
const prCreatedAt = new Date(pr.created_at);
|
||||
|
||||
// We give a 14-day grace period for non-help-wanted PRs to be manually reviewed/labeled by an EM
|
||||
if (prCreatedAt > fourteenDaysAgo) {
|
||||
core.info(`PR #${pr.number} is new and lacks 'help wanted'. Giving 14-day grace period for EM review.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
core.info(`PR #${pr.number} is older than 14 days and lacks 'help wanted' association. Closing.`);
|
||||
if (!dryRun) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body: "Hi there! Thank you for your contribution to Gemini CLI. \n\nTo improve our contribution process and better track changes, we now require all pull requests to be associated with an existing issue, as announced in our [recent discussion](https://github.com/google-gemini/gemini-cli/discussions/16706) and as detailed in our [CONTRIBUTING.md](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md#1-link-to-an-existing-issue).\n\nThis pull request is being closed because it is not currently linked to an issue. **Once you have updated the description of this PR to link an issue (e.g., by adding `Fixes #123` or `Related to #123`), it will be automatically reopened.**\n\n**How to link an issue:**\nAdd a keyword followed by the issue number (e.g., `Fixes #123`) in the description of your pull request. For more details on supported keywords and how linking works, please refer to the [GitHub Documentation on linking pull requests to issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).\n\nThank you for your understanding and for being a part of our community!"
|
||||
body: "Hi there! Thank you for your interest in contributing to Gemini CLI. \n\nTo ensure we maintain high code quality and focus on our prioritized roadmap, we have updated our contribution policy (see [Discussion #17383](https://github.com/google-gemini/gemini-cli/discussions/17383)). \n\n**We only *guarantee* review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'.** All other community pull requests are subject to closure after 14 days if they do not align with our current focus areas. For this reason, we strongly recommend that contributors only submit pull requests against issues explicitly labeled as **'help-wanted'**. \n\nThis pull request is being closed as it has been open for 14 days without a 'help wanted' designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding and for being part of our community!"
|
||||
});
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
@@ -187,27 +192,22 @@ jobs:
|
||||
continue;
|
||||
}
|
||||
|
||||
// Staleness check (Scheduled runs only)
|
||||
if (pr.state === 'open' && context.eventName !== 'pull_request') {
|
||||
const labels = pr.labels.map(l => l.name.toLowerCase());
|
||||
if (labels.includes('help wanted') || labels.includes('🔒 maintainer only')) continue;
|
||||
// Also check for linked issue even if it has help wanted (redundant but safe)
|
||||
if (pr.state === 'open' && !hasLinkedIssue) {
|
||||
// Already covered by hasHelpWanted check above, but good for future-proofing
|
||||
continue;
|
||||
}
|
||||
|
||||
// 4. Staleness Check (Scheduled only)
|
||||
if (pr.state === 'open' && context.eventName !== 'pull_request') {
|
||||
// Skip PRs that were created less than 30 days ago - they cannot be stale yet
|
||||
const prCreatedAt = new Date(pr.created_at);
|
||||
if (prCreatedAt > thirtyDaysAgo) {
|
||||
const daysOld = Math.floor((Date.now() - prCreatedAt.getTime()) / (1000 * 60 * 60 * 24));
|
||||
core.info(`PR #${pr.number} was created ${daysOld} days ago. Skipping staleness check.`);
|
||||
continue;
|
||||
}
|
||||
if (prCreatedAt > thirtyDaysAgo) continue;
|
||||
|
||||
// Initialize lastActivity to PR creation date (not epoch) as a safety baseline.
|
||||
// This ensures we never incorrectly mark a PR as stale due to failed activity lookups.
|
||||
let lastActivity = new Date(pr.created_at);
|
||||
try {
|
||||
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr.number
|
||||
owner: context.repo.owner, repo: context.repo.repo, pull_number: pr.number
|
||||
});
|
||||
for (const r of reviews) {
|
||||
if (await isMaintainer(r.user.login, r.author_association)) {
|
||||
@@ -216,9 +216,7 @@ jobs:
|
||||
}
|
||||
}
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number
|
||||
owner: context.repo.owner, repo: context.repo.repo, issue_number: pr.number
|
||||
});
|
||||
for (const c of comments) {
|
||||
if (await isMaintainer(c.user.login, c.author_association)) {
|
||||
@@ -226,25 +224,23 @@ jobs:
|
||||
if (d > lastActivity) lastActivity = d;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
core.warning(`Failed to fetch reviews/comments for PR #${pr.number}: ${e.message}`);
|
||||
}
|
||||
|
||||
// For maintainer PRs, the PR creation itself counts as maintainer activity.
|
||||
// (Now redundant since we initialize to pr.created_at, but kept for clarity)
|
||||
if (maintainerPr) {
|
||||
const d = new Date(pr.created_at);
|
||||
if (d > lastActivity) lastActivity = d;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (lastActivity < thirtyDaysAgo) {
|
||||
core.info(`PR #${pr.number} is stale.`);
|
||||
const labels = pr.labels.map(l => l.name.toLowerCase());
|
||||
const isProtected = labels.includes('help wanted') || labels.includes('🔒 maintainer only');
|
||||
if (isProtected) {
|
||||
core.info(`PR #${pr.number} is stale but has a protected label. Skipping closure.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
core.info(`PR #${pr.number} is stale (no maintainer activity for 30+ days). Closing.`);
|
||||
if (!dryRun) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body: "Hi there! Thank you for your contribution to Gemini CLI. We really appreciate the time and effort you've put into this pull request.\n\nTo keep our backlog manageable and ensure we're focusing on current priorities, we are closing pull requests that haven't seen maintainer activity for 30 days. Currently, the team is prioritizing work associated with **🔒 maintainer only** or **help wanted** issues.\n\nIf you believe this change is still critical, please feel free to comment with updated details. Otherwise, we encourage contributors to focus on open issues labeled as **help wanted**. Thank you for your understanding!"
|
||||
body: "Hi there! Thank you for your contribution. To keep our backlog manageable, we are closing pull requests that haven't seen maintainer activity for 30 days. If you're still working on this, please let us know!"
|
||||
});
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
|
||||
@@ -18,10 +18,10 @@ jobs:
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@v4'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@v4'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
@@ -40,10 +40,10 @@ jobs:
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@v4'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@v4'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Check for Parent Workstream and Apply Label'
|
||||
uses: 'actions/github-script@v7'
|
||||
uses: 'actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b' # ratchet:actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const labelToAdd = 'workstream-rollup';
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
APP_ID: '${{ secrets.APP_ID }}'
|
||||
if: |-
|
||||
${{ env.APP_ID != '' }}
|
||||
uses: 'actions/create-github-app-token@v2'
|
||||
uses: 'actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Checkout repository'
|
||||
uses: 'actions/checkout@v4'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
ref: '${{ github.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -29,14 +29,14 @@ jobs:
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- name: 'Checkout repository'
|
||||
uses: 'actions/checkout@v4'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
# The user-level skills need to be available to the workflow
|
||||
fetch-depth: 0
|
||||
ref: 'main'
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@v4'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
|
||||
- name: 'Create Pull Request'
|
||||
if: "steps.validate_version.outputs.CONTINUE == 'true'"
|
||||
uses: 'peter-evans/create-pull-request@v6'
|
||||
uses: 'peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c' # ratchet:peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
commit-message: 'docs(changelog): update for ${{ steps.release_info.outputs.VERSION }}'
|
||||
@@ -95,6 +95,8 @@ jobs:
|
||||
This PR contains the auto-generated changelog for the ${{ steps.release_info.outputs.VERSION }} release.
|
||||
|
||||
Please review and merge.
|
||||
|
||||
Related to #18505
|
||||
branch: 'changelog-${{ steps.release_info.outputs.VERSION }}'
|
||||
base: 'main'
|
||||
team-reviewers: 'gemini-cli-docs, gemini-cli-maintainers'
|
||||
|
||||
@@ -120,6 +120,9 @@ jobs:
|
||||
if (recentRuns.length > 0) {
|
||||
core.setOutput('dispatched_run_urls', recentRuns.map(r => r.html_url).join(','));
|
||||
core.setOutput('dispatched_run_ids', recentRuns.map(r => r.id).join(','));
|
||||
|
||||
const markdownLinks = recentRuns.map(r => `- [View dispatched workflow run](${r.html_url})`).join('\n');
|
||||
core.setOutput('dispatched_run_links', markdownLinks);
|
||||
}
|
||||
|
||||
- name: 'Comment on Failure'
|
||||
@@ -138,16 +141,19 @@ jobs:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
issue-number: '${{ github.event.issue.number }}'
|
||||
body: |
|
||||
✅ **Patch workflow(s) dispatched successfully!**
|
||||
🚀 **[Step 1/4] Patch workflow(s) waiting for approval!**
|
||||
|
||||
**📋 Details:**
|
||||
- **Channels**: `${{ steps.dispatch_patch.outputs.dispatched_channels }}`
|
||||
- **Commit**: `${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}`
|
||||
- **Workflows Created**: ${{ steps.dispatch_patch.outputs.dispatched_run_count }}
|
||||
|
||||
**⏳ Status:** The patch creation workflow has been triggered and is waiting for deployment approval. Please visit the specific workflow links below and approve the runs.
|
||||
|
||||
**🔗 Track Progress:**
|
||||
- [View patch workflows](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
|
||||
- [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
${{ steps.dispatch_patch.outputs.dispatched_run_links }}
|
||||
- [View patch workflow history](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
|
||||
- [This trigger workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
- name: 'Final Status Comment - Dispatch Success (No URL)'
|
||||
if: "always() && startsWith(github.event.comment.body, '/patch') && steps.dispatch_patch.outcome == 'success' && !steps.dispatch_patch.outputs.dispatched_run_urls"
|
||||
@@ -156,16 +162,18 @@ jobs:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
issue-number: '${{ github.event.issue.number }}'
|
||||
body: |
|
||||
✅ **Patch workflow(s) dispatched successfully!**
|
||||
🚀 **[Step 1/4] Patch workflow(s) waiting for approval!**
|
||||
|
||||
**📋 Details:**
|
||||
- **Channels**: `${{ steps.dispatch_patch.outputs.dispatched_channels }}`
|
||||
- **Commit**: `${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}`
|
||||
- **Workflows Created**: ${{ steps.dispatch_patch.outputs.dispatched_run_count }}
|
||||
|
||||
**⏳ Status:** The patch creation workflow has been triggered and is waiting for deployment approval. Please visit the workflow history link below and approve the runs.
|
||||
|
||||
**🔗 Track Progress:**
|
||||
- [View patch workflows](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
|
||||
- [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
- [View patch workflow history](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
|
||||
- [This trigger workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
- name: 'Final Status Comment - Failure'
|
||||
if: "always() && startsWith(github.event.comment.body, '/patch') && (steps.dispatch_patch.outcome == 'failure' || steps.dispatch_patch.outcome == 'cancelled')"
|
||||
@@ -174,7 +182,7 @@ jobs:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
issue-number: '${{ github.event.issue.number }}'
|
||||
body: |
|
||||
❌ **Patch workflow dispatch failed!**
|
||||
❌ **[Step 1/4] Patch workflow dispatch failed!**
|
||||
|
||||
There was an error dispatching the patch creation workflow.
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@v4'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
|
||||
- name: 'Optimize Windows Performance'
|
||||
if: "matrix.os == 'windows-latest'"
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
shell: 'powershell'
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@v4'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
architecture: '${{ matrix.arch }}'
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- name: 'Setup Windows SDK (Windows)'
|
||||
if: "matrix.os == 'windows-latest'"
|
||||
uses: 'microsoft/setup-msbuild@v2'
|
||||
uses: 'microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce' # ratchet:microsoft/setup-msbuild@v2
|
||||
|
||||
- name: 'Add Signtool to Path (Windows)'
|
||||
if: "matrix.os == 'windows-latest'"
|
||||
@@ -153,7 +153,7 @@ jobs:
|
||||
npm run test:integration:sandbox:none -- --testTimeout=600000
|
||||
|
||||
- name: 'Upload Artifact'
|
||||
uses: 'actions/upload-artifact@v4'
|
||||
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'gemini-cli-${{ matrix.platform_name }}'
|
||||
path: 'dist/${{ matrix.platform_name }}/'
|
||||
|
||||
@@ -40,13 +40,13 @@ jobs:
|
||||
steps:
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
uses: 'actions/create-github-app-token@v2'
|
||||
uses: 'actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
|
||||
- name: 'Unassign inactive assignees'
|
||||
uses: 'actions/github-script@v7'
|
||||
uses: 'actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b' # ratchet:actions/github-script@v7
|
||||
env:
|
||||
DRY_RUN: '${{ inputs.dry_run }}'
|
||||
with:
|
||||
|
||||
@@ -62,3 +62,5 @@ gemini-debug.log
|
||||
.gemini-clipboard/
|
||||
.eslintcache
|
||||
evals/logs/
|
||||
|
||||
temp_agents/
|
||||
|
||||
@@ -60,20 +60,45 @@ All submissions, including submissions by project members, require review. We
|
||||
use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
|
||||
for this purpose.
|
||||
|
||||
If your pull request involves changes to `packages/cli` (the frontend), we
|
||||
recommend running our automated frontend review tool. **Note: This tool is
|
||||
currently experimental.** It helps detect common React anti-patterns, testing
|
||||
issues, and other frontend-specific best practices that are easy to miss.
|
||||
To assist with the review process, we provide an automated review tool that
|
||||
helps detect common anti-patterns, testing issues, and other best practices that
|
||||
are easy to miss.
|
||||
|
||||
To run the review tool, enter the following command from within Gemini CLI:
|
||||
#### Using the automated review tool
|
||||
|
||||
```text
|
||||
/review-frontend <PR_NUMBER>
|
||||
```
|
||||
You can run the review tool in two ways:
|
||||
|
||||
Replace `<PR_NUMBER>` with your pull request number. Authors are encouraged to
|
||||
run this on their own PRs for self-review, and reviewers should use it to
|
||||
augment their manual review process.
|
||||
1. **Using the helper script (Recommended):** We provide a script that
|
||||
automatically handles checking out the PR into a separate worktree,
|
||||
installing dependencies, building the project, and launching the review
|
||||
tool.
|
||||
|
||||
```bash
|
||||
./scripts/review.sh <PR_NUMBER> [model]
|
||||
```
|
||||
|
||||
**Warning:** If you run `scripts/review.sh`, you must have first verified
|
||||
that the code for the PR being reviewed is safe to run and does not contain
|
||||
data exfiltration attacks.
|
||||
|
||||
**Authors are strongly encouraged to run this script on their own PRs**
|
||||
immediately after creation. This allows you to catch and fix simple issues
|
||||
locally before a maintainer performs a full review.
|
||||
|
||||
**Note on Models:** By default, the script uses the latest Pro model
|
||||
(`gemini-3.1-pro-preview`). If you do not have enough Pro quota, you can run
|
||||
it with the latest Flash model instead:
|
||||
`./scripts/review.sh <PR_NUMBER> gemini-3-flash-preview`.
|
||||
|
||||
2. **Manually from within Gemini CLI:** If you already have the PR checked out
|
||||
and built, you can run the tool directly from the CLI prompt:
|
||||
|
||||
```text
|
||||
/review-frontend <PR_NUMBER>
|
||||
```
|
||||
|
||||
Replace `<PR_NUMBER>` with your pull request number. Reviewers should use this
|
||||
tool to augment, not replace, their manual review process.
|
||||
|
||||
### Self-assigning and unassigning issues
|
||||
|
||||
@@ -267,7 +292,8 @@ npm run test:e2e
|
||||
```
|
||||
|
||||
For more detailed information on the integration testing framework, please see
|
||||
the [Integration Tests documentation](/docs/integration-tests.md).
|
||||
the
|
||||
[Integration Tests documentation](https://geminicli.com/docs/integration-tests).
|
||||
|
||||
### Linting and preflight checks
|
||||
|
||||
@@ -297,8 +323,8 @@ fi
|
||||
|
||||
#### Formatting
|
||||
|
||||
To separately format the code in this project by running the following command
|
||||
from the root directory:
|
||||
To separately format the code in this project, run the following command from
|
||||
the root directory:
|
||||
|
||||
```bash
|
||||
npm run format
|
||||
@@ -328,21 +354,6 @@ npm run lint
|
||||
- **Imports:** Pay special attention to import paths. The project uses ESLint to
|
||||
enforce restrictions on relative imports between packages.
|
||||
|
||||
### Project structure
|
||||
|
||||
- `packages/`: Contains the individual sub-packages of the project.
|
||||
- `a2a-server`: A2A server implementation for the Gemini CLI. (Experimental)
|
||||
- `cli/`: The command-line interface.
|
||||
- `core/`: The core backend logic for the Gemini CLI.
|
||||
- `test-utils` Utilities for creating and cleaning temporary file systems for
|
||||
testing.
|
||||
- `vscode-ide-companion/`: The Gemini CLI Companion extension pairs with
|
||||
Gemini CLI.
|
||||
- `docs/`: Contains all project documentation.
|
||||
- `scripts/`: Utility scripts for building, testing, and development tasks.
|
||||
|
||||
For more detailed architecture, see `docs/architecture.md`.
|
||||
|
||||
### Debugging
|
||||
|
||||
#### VS Code
|
||||
@@ -496,8 +507,9 @@ code.
|
||||
|
||||
### Documentation structure
|
||||
|
||||
Our documentation is organized using [sidebar.json](/docs/sidebar.json) as the
|
||||
table of contents. When adding new documentation:
|
||||
Our documentation is organized using
|
||||
[sidebar.json](https://github.com/google-gemini/gemini-cli/blob/main/docs/sidebar.json)
|
||||
as the table of contents. When adding new documentation:
|
||||
|
||||
1. Create your markdown file **in the appropriate directory** under `/docs`.
|
||||
2. Add an entry to `sidebar.json` in the relevant section.
|
||||
@@ -548,7 +560,7 @@ Before submitting your documentation pull request, please:
|
||||
|
||||
If you have questions about contributing documentation:
|
||||
|
||||
- Check our [FAQ](/docs/resources/faq.md).
|
||||
- Check our [FAQ](https://geminicli.com/docs/resources/faq).
|
||||
- Review existing documentation for examples.
|
||||
- Open [an issue](https://github.com/google-gemini/gemini-cli/issues) to discuss
|
||||
your proposed changes.
|
||||
|
||||
@@ -22,9 +22,10 @@ powerful tool for developers.
|
||||
rendering.
|
||||
- `packages/core`: Backend logic, Gemini API orchestration, prompt
|
||||
construction, and tool execution.
|
||||
- `packages/core/src/tools/`: Built-in tools for file system, shell, and web
|
||||
operations.
|
||||
- `packages/a2a-server`: Experimental Agent-to-Agent server.
|
||||
- `packages/sdk`: Programmatic SDK for embedding Gemini CLI capabilities.
|
||||
- `packages/devtools`: Integrated developer tools (Network/Console inspector).
|
||||
- `packages/test-utils`: Shared test utilities and test rig.
|
||||
- `packages/vscode-ide-companion`: VS Code extension pairing with the CLI.
|
||||
|
||||
## Building and Running
|
||||
@@ -58,10 +59,6 @@ powerful tool for developers.
|
||||
|
||||
## Development Conventions
|
||||
|
||||
- **Legacy Snippets:** `packages/core/src/prompts/snippets.legacy.ts` is a
|
||||
snapshot of an older system prompt. Avoid changing the prompting verbiage to
|
||||
preserve its historical behavior; however, structural changes to ensure
|
||||
compilation or simplify the code are permitted.
|
||||
- **Contributions:** Follow the process outlined in `CONTRIBUTING.md`. Requires
|
||||
signing the Google CLA.
|
||||
- **Pull Requests:** Keep PRs small, focused, and linked to an existing issue.
|
||||
@@ -69,8 +66,6 @@ powerful tool for developers.
|
||||
`gh` CLI.
|
||||
- **Commit Messages:** Follow the
|
||||
[Conventional Commits](https://www.conventionalcommits.org/) standard.
|
||||
- **Coding Style:** Adhere to existing patterns in `packages/cli` (React/Ink)
|
||||
and `packages/core` (Backend logic).
|
||||
- **Imports:** Use specific imports and avoid restricted relative imports
|
||||
between packages (enforced by ESLint).
|
||||
- **License Headers:** For all new source code files (`.ts`, `.tsx`, `.js`),
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[](https://github.com/google-gemini/gemini-cli/blob/main/LICENSE)
|
||||
[](https://codewiki.google/github.com/google-gemini/gemini-cli?utm_source=badge&utm_medium=github&utm_campaign=github.com/google-gemini/gemini-cli)
|
||||
|
||||

|
||||

|
||||
|
||||
Gemini CLI is an open-source AI agent that brings the power of Gemini directly
|
||||
into your terminal. It provides lightweight access to Gemini, giving you the
|
||||
@@ -30,7 +30,7 @@ Learn all about Gemini CLI in our [documentation](https://geminicli.com/docs/).
|
||||
## 📦 Installation
|
||||
|
||||
See
|
||||
[Gemini CLI installation, execution, and releases](./docs/get-started/installation.md)
|
||||
[Gemini CLI installation, execution, and releases](https://www.geminicli.com/docs/get-started/installation)
|
||||
for recommended system specifications and a detailed installation guide.
|
||||
|
||||
### Quick Install
|
||||
@@ -71,13 +71,13 @@ conda activate gemini_env
|
||||
npm install -g @google/gemini-cli
|
||||
```
|
||||
|
||||
## Release Cadence and Tags
|
||||
## Release Channels
|
||||
|
||||
See [Releases](./docs/releases.md) for more details.
|
||||
See [Releases](https://www.geminicli.com/docs/changelogs) for more details.
|
||||
|
||||
### Preview
|
||||
|
||||
New preview releases will be published each week at UTC 2359 on Tuesdays. These
|
||||
New preview releases will be published each week at UTC 23:59 on Tuesdays. These
|
||||
releases will not have been fully vetted and may contain regressions or other
|
||||
outstanding issues. Please help us test and install with `preview` tag.
|
||||
|
||||
@@ -87,7 +87,7 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
### Stable
|
||||
|
||||
- New stable releases will be published each week at UTC 2000 on Tuesdays, this
|
||||
- New stable releases will be published each week at UTC 20:00 on Tuesdays, this
|
||||
will be the full promotion of last week's `preview` release + any bug fixes
|
||||
and validations. Use `latest` tag.
|
||||
|
||||
@@ -97,7 +97,7 @@ npm install -g @google/gemini-cli@latest
|
||||
|
||||
### Nightly
|
||||
|
||||
- New releases will be published each day at UTC 0000. This will be all changes
|
||||
- New releases will be published each day at UTC 00:00. This will be all changes
|
||||
from the main branch as represented at time of release. It should be assumed
|
||||
there are pending validations and issues. Use `nightly` tag.
|
||||
|
||||
@@ -147,7 +147,7 @@ Integrate Gemini CLI directly into your GitHub workflows with
|
||||
|
||||
Choose the authentication method that best fits your needs:
|
||||
|
||||
### Option 1: Login with Google (OAuth login using your Google Account)
|
||||
### Option 1: Sign in with Google (OAuth login using your Google Account)
|
||||
|
||||
**✨ Best for:** Individual developers as well as anyone who has a Gemini Code
|
||||
Assist License. (see
|
||||
@@ -161,7 +161,7 @@ for details)
|
||||
- **No API key management** - just sign in with your Google account
|
||||
- **Automatic updates** to latest models
|
||||
|
||||
#### Start Gemini CLI, then choose _Login with Google_ and follow the browser authentication flow when prompted
|
||||
#### Start Gemini CLI, then choose _Sign in with Google_ and follow the browser authentication flow when prompted
|
||||
|
||||
```bash
|
||||
gemini
|
||||
@@ -209,7 +209,7 @@ gemini
|
||||
```
|
||||
|
||||
For Google Workspace accounts and other authentication methods, see the
|
||||
[authentication guide](./docs/get-started/authentication.md).
|
||||
[authentication guide](https://www.geminicli.com/docs/get-started/authentication).
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
@@ -278,60 +278,64 @@ gemini
|
||||
|
||||
### Getting Started
|
||||
|
||||
- [**Quickstart Guide**](./docs/get-started/index.md) - Get up and running
|
||||
quickly.
|
||||
- [**Authentication Setup**](./docs/get-started/authentication.md) - Detailed
|
||||
auth configuration.
|
||||
- [**Configuration Guide**](./docs/reference/configuration.md) - Settings and
|
||||
customization.
|
||||
- [**Keyboard Shortcuts**](./docs/reference/keyboard-shortcuts.md) -
|
||||
- [**Quickstart Guide**](https://www.geminicli.com/docs/get-started) - Get up
|
||||
and running quickly.
|
||||
- [**Authentication Setup**](https://www.geminicli.com/docs/get-started/authentication) -
|
||||
Detailed auth configuration.
|
||||
- [**Configuration Guide**](https://www.geminicli.com/docs/reference/configuration) -
|
||||
Settings and customization.
|
||||
- [**Keyboard Shortcuts**](https://www.geminicli.com/docs/reference/keyboard-shortcuts) -
|
||||
Productivity tips.
|
||||
|
||||
### Core Features
|
||||
|
||||
- [**Commands Reference**](./docs/reference/commands.md) - All slash commands
|
||||
(`/help`, `/chat`, etc).
|
||||
- [**Custom Commands**](./docs/cli/custom-commands.md) - Create your own
|
||||
reusable commands.
|
||||
- [**Context Files (GEMINI.md)**](./docs/cli/gemini-md.md) - Provide persistent
|
||||
context to Gemini CLI.
|
||||
- [**Checkpointing**](./docs/cli/checkpointing.md) - Save and resume
|
||||
conversations.
|
||||
- [**Token Caching**](./docs/cli/token-caching.md) - Optimize token usage.
|
||||
- [**Commands Reference**](https://www.geminicli.com/docs/reference/commands) -
|
||||
All slash commands (`/help`, `/chat`, etc).
|
||||
- [**Custom Commands**](https://www.geminicli.com/docs/cli/custom-commands) -
|
||||
Create your own reusable commands.
|
||||
- [**Context Files (GEMINI.md)**](https://www.geminicli.com/docs/cli/gemini-md) -
|
||||
Provide persistent context to Gemini CLI.
|
||||
- [**Checkpointing**](https://www.geminicli.com/docs/cli/checkpointing) - Save
|
||||
and resume conversations.
|
||||
- [**Token Caching**](https://www.geminicli.com/docs/cli/token-caching) -
|
||||
Optimize token usage.
|
||||
|
||||
### Tools & Extensions
|
||||
|
||||
- [**Built-in Tools Overview**](./docs/reference/tools.md)
|
||||
- [File System Operations](./docs/tools/file-system.md)
|
||||
- [Shell Commands](./docs/tools/shell.md)
|
||||
- [Web Fetch & Search](./docs/tools/web-fetch.md)
|
||||
- [**MCP Server Integration**](./docs/tools/mcp-server.md) - Extend with custom
|
||||
tools.
|
||||
- [**Custom Extensions**](./docs/extensions/index.md) - Build and share your own
|
||||
commands.
|
||||
- [**Built-in Tools Overview**](https://www.geminicli.com/docs/reference/tools)
|
||||
- [File System Operations](https://www.geminicli.com/docs/tools/file-system)
|
||||
- [Shell Commands](https://www.geminicli.com/docs/tools/shell)
|
||||
- [Web Fetch & Search](https://www.geminicli.com/docs/tools/web-fetch)
|
||||
- [**MCP Server Integration**](https://www.geminicli.com/docs/tools/mcp-server) -
|
||||
Extend with custom tools.
|
||||
- [**Custom Extensions**](https://geminicli.com/docs/extensions/writing-extensions) -
|
||||
Build and share your own commands.
|
||||
|
||||
### Advanced Topics
|
||||
|
||||
- [**Headless Mode (Scripting)**](./docs/cli/headless.md) - Use Gemini CLI in
|
||||
automated workflows.
|
||||
- [**Architecture Overview**](./docs/architecture.md) - How Gemini CLI works.
|
||||
- [**IDE Integration**](./docs/ide-integration/index.md) - VS Code companion.
|
||||
- [**Sandboxing & Security**](./docs/cli/sandbox.md) - Safe execution
|
||||
environments.
|
||||
- [**Trusted Folders**](./docs/cli/trusted-folders.md) - Control execution
|
||||
policies by folder.
|
||||
- [**Enterprise Guide**](./docs/cli/enterprise.md) - Deploy and manage in a
|
||||
corporate environment.
|
||||
- [**Telemetry & Monitoring**](./docs/cli/telemetry.md) - Usage tracking.
|
||||
- [**Tools reference**](./docs/reference/tools.md) - Built-in tools overview.
|
||||
- [**Local development**](./docs/local-development.md) - Local development
|
||||
tooling.
|
||||
- [**Headless Mode (Scripting)**](https://www.geminicli.com/docs/cli/headless) -
|
||||
Use Gemini CLI in automated workflows.
|
||||
- [**IDE Integration**](https://www.geminicli.com/docs/ide-integration) - VS
|
||||
Code companion.
|
||||
- [**Sandboxing & Security**](https://www.geminicli.com/docs/cli/sandbox) - Safe
|
||||
execution environments.
|
||||
- [**Trusted Folders**](https://www.geminicli.com/docs/cli/trusted-folders) -
|
||||
Control execution policies by folder.
|
||||
- [**Enterprise Guide**](https://www.geminicli.com/docs/cli/enterprise) - Deploy
|
||||
and manage in a corporate environment.
|
||||
- [**Telemetry & Monitoring**](https://www.geminicli.com/docs/cli/telemetry) -
|
||||
Usage tracking.
|
||||
- [**Tools reference**](https://www.geminicli.com/docs/reference/tools) -
|
||||
Built-in tools overview.
|
||||
- [**Local development**](https://www.geminicli.com/docs/local-development) -
|
||||
Local development tooling.
|
||||
|
||||
### Troubleshooting & Support
|
||||
|
||||
- [**Troubleshooting Guide**](./docs/resources/troubleshooting.md) - Common
|
||||
issues and solutions.
|
||||
- [**FAQ**](./docs/resources/faq.md) - Frequently asked questions.
|
||||
- [**Troubleshooting Guide**](https://www.geminicli.com/docs/resources/troubleshooting) -
|
||||
Common issues and solutions.
|
||||
- [**FAQ**](https://www.geminicli.com/docs/resources/faq) - Frequently asked
|
||||
questions.
|
||||
- Use `/bug` command to report issues directly from the CLI.
|
||||
|
||||
### Using MCP Servers
|
||||
@@ -345,8 +349,9 @@ custom tools:
|
||||
> @database Run a query to find inactive users
|
||||
```
|
||||
|
||||
See the [MCP Server Integration guide](./docs/tools/mcp-server.md) for setup
|
||||
instructions.
|
||||
See the
|
||||
[MCP Server Integration guide](https://www.geminicli.com/docs/tools/mcp-server)
|
||||
for setup instructions.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
@@ -367,7 +372,8 @@ for planned features and priorities.
|
||||
## 📖 Resources
|
||||
|
||||
- **[Official Roadmap](./ROADMAP.md)** - See what's coming next.
|
||||
- **[Changelog](./docs/changelogs/index.md)** - See recent notable updates.
|
||||
- **[Changelog](https://www.geminicli.com/docs/changelogs)** - See recent
|
||||
notable updates.
|
||||
- **[NPM Package](https://www.npmjs.com/package/@google/gemini-cli)** - Package
|
||||
registry.
|
||||
- **[GitHub Issues](https://github.com/google-gemini/gemini-cli/issues)** -
|
||||
@@ -377,13 +383,14 @@ for planned features and priorities.
|
||||
|
||||
### Uninstall
|
||||
|
||||
See the [Uninstall Guide](./docs/resources/uninstall.md) for removal
|
||||
instructions.
|
||||
See the [Uninstall Guide](https://www.geminicli.com/docs/resources/uninstall)
|
||||
for removal instructions.
|
||||
|
||||
## 📄 Legal
|
||||
|
||||
- **License**: [Apache License 2.0](LICENSE)
|
||||
- **Terms of Service**: [Terms & Privacy](./docs/resources/tos-privacy.md)
|
||||
- **Terms of Service**:
|
||||
[Terms & Privacy](https://www.geminicli.com/docs/resources/tos-privacy)
|
||||
- **Security**: [Security Policy](SECURITY.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
# ADK-TS Alignment Pass
|
||||
|
||||
Every interface in our outline must map cleanly to ADK-TS. This document
|
||||
verifies that mapping field-by-field, identifies gaps, and confirms
|
||||
HITL/plugin/transfer patterns work.
|
||||
|
||||
Source: ADK-TS v0.4.0 at `/Users/adamfweidman/Desktop/adk-int/adk-js/core/src/`
|
||||
|
||||
---
|
||||
|
||||
## 1. AgentDescriptor ↔ ADK Agent Hierarchy
|
||||
|
||||
### Field-by-field mapping
|
||||
|
||||
| AgentDescriptor field | ADK-TS source | Notes |
|
||||
| ---------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `name` | `BaseAgent.name` | Direct. ADK validates it's a valid JS identifier. |
|
||||
| `displayName` | — | ADK doesn't have this. No conflict. |
|
||||
| `description` | `BaseAgent.description` (optional in ADK) | Direct. Used for model routing in AgentTool. |
|
||||
| `executor` | — | New concept. ADK agents are always 'adk'. Adapter sets this. |
|
||||
| `inputSchema` | `LlmAgent.inputSchema` (Zod or JSON Schema) | Direct. ADK's AgentTool uses this for tool parameter generation. |
|
||||
| `outputSchema` | `LlmAgent.outputSchema` (Zod or JSON Schema) | Direct. ADK uses for structured output + AgentTool response. |
|
||||
| `capabilities` | — | New concept. Adapter infers from agent type: LlmAgent gets `['elicitation', 'streaming', 'host_tool_execution']`, LoopAgent gets `['composition']`, etc. |
|
||||
| `ownTools` | `LlmAgent.tools: ToolUnion[]` | Maps via ToolDescriptor adapter. ADK tools have `name`, `description`, `_getDeclaration()` which returns JSON Schema. |
|
||||
| `requiredTools` | — | New concept. ADK agents don't declare required host tools. Adapter can infer from tool references. |
|
||||
| `subAgents` | `BaseAgent.subAgents: BaseAgent[]` | Recursive. Each sub-agent becomes a nested AgentDescriptor. |
|
||||
| `constraints.maxTurns` | `RunConfig.maxLlmCalls` (default 500) | Maps, though semantics differ slightly (LLM calls vs turns). |
|
||||
| `constraints.maxTimeMinutes` | — | ADK doesn't have time limits. No conflict — host enforces. |
|
||||
| `constraints.maxBudgetUsd` | — | ADK doesn't have budget. No conflict — host enforces. |
|
||||
| `metadata` | — | New concept. Adapter can populate from agent registration context. |
|
||||
|
||||
### ADK-specific fields NOT in AgentDescriptor
|
||||
|
||||
| ADK field | Where it lives | Our approach |
|
||||
| ----------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `instruction` / `globalInstruction` | LlmAgent | Executor-internal. Not in descriptor (it's runtime config, not identity). |
|
||||
| `model` | LlmAgent | Goes in ExecutionOptions.model or executor-internal config. |
|
||||
| `generateContentConfig` | LlmAgent | Executor-internal. |
|
||||
| `disallowTransferToParent/Peers` | LlmAgent | Could be `constraints` or `_meta`. Transfer policy is host-enforced. |
|
||||
| `includeContents` | LlmAgent | Executor-internal (context management). |
|
||||
| `outputKey` | LlmAgent | Executor-internal (state management). |
|
||||
| `beforeModelCallback`, etc. | LlmAgent | Executor-internal. These are ADK's callback system — our LifecycleInterceptor is the interface equivalent. |
|
||||
|
||||
### Verdict: CLEAN MAPPING
|
||||
|
||||
AgentDescriptor captures everything needed to describe an ADK agent externally.
|
||||
ADK-specific runtime config (instruction, model, callbacks) stays inside the
|
||||
executor — exactly right for the descriptor/executor separation.
|
||||
|
||||
**Key ADK pattern preserved:** AgentTool wraps an agent as a tool using
|
||||
`inputSchema` for parameters and `description` for the tool description. Our
|
||||
AgentDescriptor has both, so SubagentTool can do the same thing.
|
||||
|
||||
---
|
||||
|
||||
## 2. AgentSession ↔ ADK Runner
|
||||
|
||||
### Method mapping
|
||||
|
||||
| AgentSession method | ADK-TS equivalent | How adapter works |
|
||||
| ----------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `stream(data, options)` | `Runner.runAsync({ userId, sessionId, newMessage, runConfig })` | Adapter creates/loads session, maps data+options → runAsync params, wraps Event generator → AgentEvent generator. Each `stream()` call triggers a new `runAsync()`. |
|
||||
| `update(config)` | No direct equivalent | ADK doesn't support mid-stream config changes. Adapter queues updates for next `runAsync()` call. |
|
||||
| `steer(data)` | No direct equivalent | ADK doesn't support mid-stream intervention. Adapter can queue for next invocation or ignore. |
|
||||
| `abort()` | No direct equivalent | ADK uses `invocationContext.endInvocation = true`. Adapter sets this flag. Could also use AbortController. |
|
||||
|
||||
### ExecutionRequest → Runner.runAsync mapping
|
||||
|
||||
| ExecutionRequest field | ADK mapping |
|
||||
| --------------------------- | ---------------------------------------------------------------- |
|
||||
| `descriptor` | Used to find/create the BaseAgent instance |
|
||||
| `input` | → `newMessage: Content` (converted from ContentPart[] → Content) |
|
||||
| `sessionRef` | → `sessionId` (string) or creates session from SessionSnapshot |
|
||||
| `forkSession` | Adapter clones session before running |
|
||||
| `options.tools` | → merged into agent's `tools` config |
|
||||
| `options.model` | → `LlmAgent.model` override |
|
||||
| `options.hostToolExecution` | → `RunConfig.pauseOnToolCalls: true` |
|
||||
| `options.streaming` | → `RunConfig.streamingMode` |
|
||||
| `options.permissionMode` | → SecurityPlugin config |
|
||||
| `signal` | → wired to `invocationContext.endInvocation` |
|
||||
|
||||
### HITL: How pauseOnToolCalls works end-to-end
|
||||
|
||||
This is the critical path. Here's the full flow:
|
||||
|
||||
```
|
||||
1. LLM returns tool call (FunctionCall in Event)
|
||||
2. ADK checks RunConfig.pauseOnToolCalls === true
|
||||
3. ADK sets invocationContext.endInvocation = true
|
||||
4. ADK yields the Event (with FunctionCall) and stops
|
||||
5. Runner.runAsync() generator completes
|
||||
|
||||
--- OUR INTERFACE BOUNDARY ---
|
||||
|
||||
6. Adapter translates ADK Event → ToolRequestEvent
|
||||
7. Host receives ToolRequestEvent from session.stream() generator
|
||||
8. Host runs policy check (PolicyEvaluator.evaluate())
|
||||
9. Host fires hooks (LifecycleInterceptor.fire('before_tool', ...))
|
||||
10. If policy allows → Host executes tool → gets ToolResultData
|
||||
11. Host calls session.stream({ kind: 'tool_result', ... }) to get next stream
|
||||
|
||||
--- BACK INTO ADK ---
|
||||
|
||||
12. Adapter receives tool result
|
||||
13. Adapter creates FunctionResponse Content
|
||||
14. Adapter calls Runner.runAsync() again with FunctionResponse as newMessage
|
||||
15. ADK loads session (has prior tool call event)
|
||||
16. ADK resumes agent with tool response
|
||||
17. Loop continues from step 1
|
||||
```
|
||||
|
||||
**Why this works:** ADK's `pauseOnToolCalls` was designed exactly for this
|
||||
pattern — external tool execution by a host. The adapter translates between
|
||||
ADK's "end invocation + resume with FunctionResponse" pattern and our
|
||||
"ToolRequestEvent + send(tool_result)" pattern.
|
||||
|
||||
**Key insight:** Each `session.stream()` call triggers a new `Runner.runAsync()`
|
||||
call. This means each ADK "invocation" maps to one `stream()` call. The session
|
||||
persists state across invocations. Mid-stream `update()` and `steer()` calls are
|
||||
queued for the next invocation since ADK doesn't support mid-turn changes.
|
||||
|
||||
### HITL: ToolConfirmation flow
|
||||
|
||||
ADK also has a separate ToolConfirmation pattern (via
|
||||
`context.requestConfirmation()`):
|
||||
|
||||
```
|
||||
1. beforeToolCallback calls context.requestConfirmation({ hint: '...' })
|
||||
2. This sets eventActions.requestedToolConfirmations[functionCallId]
|
||||
3. ADK yields event with requestedToolConfirmations populated
|
||||
4. Runner completes (invocation ends)
|
||||
|
||||
--- OUR INTERFACE BOUNDARY ---
|
||||
|
||||
5. Adapter sees requestedToolConfirmations in event
|
||||
6. Adapter translates → ElicitationRequest { kind: 'tool_confirmation', ... }
|
||||
7. Host renders confirmation UI
|
||||
8. User responds → ElicitationResponse { action: 'accept' | 'decline' }
|
||||
|
||||
--- BACK INTO ADK ---
|
||||
|
||||
9. Adapter receives elicitation response
|
||||
10. If accepted: Adapter creates FunctionResponse with confirmed=true
|
||||
11. Calls Runner.runAsync() with FunctionResponse
|
||||
12. ADK's SecurityPlugin or callback reads confirmation from session
|
||||
13. Tool executes
|
||||
```
|
||||
|
||||
**Maps to our ElicitationRequest:** ADK's `ToolConfirmation.hint` →
|
||||
`ElicitationRequest.message`. ADK's `ToolConfirmation.payload` →
|
||||
`ElicitationRequest.context`. The `kind: 'tool_confirmation'` is the
|
||||
discriminator.
|
||||
|
||||
### HITL: Auth request flow
|
||||
|
||||
```
|
||||
1. Tool or callback calls context.requestCredential(authConfig)
|
||||
2. Sets eventActions.requestedAuthConfigs[functionCallId]
|
||||
3. Event yields, invocation ends
|
||||
|
||||
--- OUR INTERFACE BOUNDARY ---
|
||||
|
||||
4. Adapter sees requestedAuthConfigs
|
||||
5. Translates → ElicitationRequest { kind: 'auth_required', context: authConfig }
|
||||
6. User provides credentials
|
||||
7. ElicitationResponse { action: 'accept', content: { credential: ... } }
|
||||
|
||||
--- BACK INTO ADK ---
|
||||
|
||||
8. Adapter stores credential via CredentialService
|
||||
9. Calls Runner.runAsync() again
|
||||
10. Tool calls context.getAuthResponse() → gets credential
|
||||
```
|
||||
|
||||
**Maps to our ElicitationRequest:** ADK's auth pattern is just another
|
||||
elicitation kind. This validates our generic elicitation design — it handles
|
||||
tool confirmation, auth, and any future interaction type.
|
||||
|
||||
---
|
||||
|
||||
## 3. AgentEvent ↔ ADK Event
|
||||
|
||||
### Event type mapping
|
||||
|
||||
| Our AgentEvent | ADK Event pattern | Adapter translation |
|
||||
| --------------------- | --------------------------------------------------------------------------------- | --------------------------------------------- |
|
||||
| `InitializeEvent` | First event from Runner.runAsync() | Adapter emits on first stream() call |
|
||||
| `SessionUpdateEvent` | `eventActions.stateDelta` | Adapter emits when stateDelta is non-empty |
|
||||
| `MessageEvent` | `event.content` with text Parts | Filter text/thought parts from Content |
|
||||
| `ToolRequestEvent` | `getFunctionCalls(event)` returns FunctionCall[] | Each FunctionCall → one ToolRequestEvent |
|
||||
| `ToolUpdateEvent` | `event.longRunningToolIds` | Adapter emits progress for long-running tools |
|
||||
| `ToolResponseEvent` | `getFunctionResponses(event)` returns FunctionResponse[] | Each FunctionResponse → one ToolResponseEvent |
|
||||
| `ElicitationRequest` | `eventActions.requestedToolConfirmations` or `requestedAuthConfigs` | Map to generic elicitation |
|
||||
| `ElicitationResponse` | User input → FunctionResponse in next runAsync call | Reverse of above |
|
||||
| `UsageEvent` | `event.usageMetadata` (GenerateContentResponseUsageMetadata) | Map token counts |
|
||||
| `ErrorEvent` | `event.errorCode` + `event.errorMessage` | Map error fields |
|
||||
| `stream_end` | `isFinalResponse(event)`, `eventActions.transferToAgent`, `eventActions.escalate` | Derive `stream_end` reason from ADK signals |
|
||||
| `CustomEvent` | `event.customMetadata` | Pass through |
|
||||
|
||||
### ADK EventActions → Our events
|
||||
|
||||
| EventActions field | Our event | Notes |
|
||||
| ---------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `stateDelta` | SessionUpdate or embedded in other events | Delta state is a core ADK pattern |
|
||||
| `artifactDelta` | `CustomEvent { kind: 'artifact_delta' }` | Artifacts not in our core events |
|
||||
| `transferToAgent` | Tool call (`transfer_to_agent`) + `stream_end` `reason: 'completed'` | Handoff is a tool call. Host intercepts the tool request, mediates the handoff, originating agent completes. |
|
||||
| `escalate` | `stream_end` `reason: 'completed'` with `data: { escalateReason: '...' }` | LoopAgent exit signal. ADK's escalate = "I'm done, pass control back up" |
|
||||
| `requestedToolConfirmations` | `ElicitationRequest { kind: 'tool_confirmation' }` | Per function call ID |
|
||||
| `requestedAuthConfigs` | `ElicitationRequest { kind: 'auth_required' }` | Per function call ID |
|
||||
| `skipSummarization` | `_meta: { skipSummarization: true }` | ADK-specific, goes in metadata |
|
||||
|
||||
### AgentEventBase mapping
|
||||
|
||||
| AgentEventBase field | ADK Event field | Notes |
|
||||
| -------------------- | ---------------------------------------- | ------------------------------------------------- |
|
||||
| `id` | `event.id` | Direct |
|
||||
| `timestamp` | `event.timestamp` (number) | Convert to ISO 8601 string |
|
||||
| `type` | Derived from content analysis | ADK doesn't have event types — adapter classifies |
|
||||
| `agentId` | `event.author` (agent name) or context | **New field** — which agent emitted this event |
|
||||
| `threadId` | `event.branch` (e.g., "agent_1.agent_2") | Direct mapping |
|
||||
| `source` | `event.author` ("user" or agent name) | Direct |
|
||||
| `_meta` | `event.customMetadata` | Direct |
|
||||
|
||||
### Verdict: CLEAN MAPPING
|
||||
|
||||
Every ADK event pattern maps to our event types. The adapter classifies ADK's
|
||||
untyped events into our typed event taxonomy. Key insight: ADK events are richer
|
||||
(they carry EventActions, function calls, auth requests all in one event), so
|
||||
the adapter may fan out one ADK Event into multiple AgentEvents (e.g., one
|
||||
Message + one ToolRequest + one ElicitationRequest). The new `agentId` field
|
||||
maps directly from ADK's `event.author`.
|
||||
|
||||
---
|
||||
|
||||
## 4. ToolContract ↔ ADK Tool System
|
||||
|
||||
### ToolDescriptor ↔ BaseTool
|
||||
|
||||
| ToolDescriptor field | ADK source | Notes |
|
||||
| ------------------------- | ------------------------------------------------------------- | --------------------------------- |
|
||||
| `name` | `BaseTool.name` | Direct |
|
||||
| `displayName` | — | ADK doesn't have this |
|
||||
| `description` | `BaseTool.description` | Direct |
|
||||
| `parametersSchema` | `BaseTool._getDeclaration()` → FunctionDeclaration.parameters | JSON Schema from declaration |
|
||||
| `annotations.readOnly` | Inferred from tool type | FunctionTool with no side effects |
|
||||
| `annotations.longRunning` | `BaseTool.isLongRunning` | Direct |
|
||||
|
||||
### ToolCallRequest ↔ FunctionCall
|
||||
|
||||
| ToolCallRequest | ADK FunctionCall | Notes |
|
||||
| --------------- | ------------------- | ------ |
|
||||
| `requestId` | `functionCall.id` | Direct |
|
||||
| `name` | `functionCall.name` | Direct |
|
||||
| `args` | `functionCall.args` | Direct |
|
||||
|
||||
### ToolResultData ↔ FunctionResponse + tool return
|
||||
|
||||
| ToolResultData | ADK | Notes |
|
||||
| ---------------- | ------------------------------ | ------------------------------------------------ |
|
||||
| `llmContent` | `FunctionResponse.response` | Adapter wraps into ContentPart[] |
|
||||
| `displayContent` | — | ADK doesn't separate display from model content |
|
||||
| `isError` | Error thrown from `runAsync()` | Adapter catches and sets flag |
|
||||
| `tailCalls` | — | ADK doesn't have tail calls (gemini-cli concept) |
|
||||
|
||||
### AgentTool pattern
|
||||
|
||||
ADK's `AgentTool` wraps a `BaseAgent` as a `BaseTool`:
|
||||
|
||||
- Uses `agent.inputSchema` for tool parameters
|
||||
- Uses `agent.description` for tool description
|
||||
- Creates internal Runner with isolated session
|
||||
- Returns agent output as tool result
|
||||
- Merges state deltas back to parent
|
||||
|
||||
**Our equivalent:** `SubagentTool` wraps `AgentDescriptor` as a tool:
|
||||
|
||||
- Uses `descriptor.inputSchema` for tool parameters
|
||||
- Uses `descriptor.description` for tool description
|
||||
- Creates executor via `SessionFactory.create(descriptor, context)`
|
||||
- Returns execution result as tool result
|
||||
|
||||
**Mapping is 1:1.** The only difference is ADK does it with concrete agent
|
||||
instances; we do it with descriptors + factory.
|
||||
|
||||
---
|
||||
|
||||
## 5. LifecycleInterceptor ↔ ADK Plugin System
|
||||
|
||||
### Hook point mapping
|
||||
|
||||
| Our hook point string | ADK Plugin callback | Mapping |
|
||||
| --------------------- | ----------------------- | ------------------------------------------ |
|
||||
| `'before_agent'` | `beforeAgentCallback` | `payload: { agent, context }` |
|
||||
| `'after_agent'` | `afterAgentCallback` | `payload: { agent, context }` |
|
||||
| `'before_model'` | `beforeModelCallback` | `payload: { context, llmRequest }` |
|
||||
| `'after_model'` | `afterModelCallback` | `payload: { context, llmResponse }` |
|
||||
| `'before_tool'` | `beforeToolCallback` | `payload: { tool, args, context }` |
|
||||
| `'after_tool'` | `afterToolCallback` | `payload: { tool, args, context, result }` |
|
||||
| `'on_event'` | `onEventCallback` | `payload: { event }` |
|
||||
| `'on_user_message'` | `onUserMessageCallback` | `payload: { userMessage }` |
|
||||
| `'before_run'` | `beforeRunCallback` | `payload: { context }` |
|
||||
| `'after_run'` | `afterRunCallback` | `payload: { context }` |
|
||||
| `'on_model_error'` | `onModelErrorCallback` | `payload: { request, error }` |
|
||||
| `'on_tool_error'` | `onToolErrorCallback` | `payload: { tool, args, error }` |
|
||||
|
||||
### HookResult ↔ ADK callback return
|
||||
|
||||
| HookResult field | ADK pattern | Notes |
|
||||
| ------------------- | ----------------------------------------------- | ----------------------------------- |
|
||||
| `action: 'proceed'` | Return `undefined` | Plugin returns nothing → continue |
|
||||
| `action: 'block'` | Return `Content` (for agent/model) or throw | Non-undefined return short-circuits |
|
||||
| `modifications` | Return modified `LlmRequest`/`LlmResponse`/args | Plugin returns modified version |
|
||||
|
||||
### ADK's early-exit pattern
|
||||
|
||||
ADK plugins use "first non-undefined return wins":
|
||||
|
||||
- `beforeModelCallback` returns `LlmResponse` → skips LLM call entirely (cache
|
||||
hit)
|
||||
- `beforeToolCallback` returns modified `args` → tool runs with new args
|
||||
- `beforeAgentCallback` returns `Content` → skips agent run entirely
|
||||
|
||||
Our `HookResult.modifications` carries the same data. The `action: 'block'` +
|
||||
return value pattern maps cleanly.
|
||||
|
||||
### gemini-cli hooks NOT in ADK
|
||||
|
||||
| gemini-cli hook | ADK equivalent | Notes |
|
||||
| --------------------- | ------------------------------------ | ------------------------------------------------------------- |
|
||||
| `BeforeToolSelection` | — | ADK doesn't let you modify which tools are available mid-turn |
|
||||
| `Notification` | — | ADK doesn't have notification hooks |
|
||||
| `SessionStart` | `onUserMessageCallback` (first call) | Close enough |
|
||||
| `SessionEnd` | `afterRunCallback` | Close enough |
|
||||
| `PreCompress` | — | ADK doesn't have context compression hooks |
|
||||
|
||||
These gaps are fine — they're gemini-cli-specific hook points. Our generic
|
||||
`fire(hookPoint, payload)` handles them because the hook point is an open
|
||||
string. ADK executors simply don't fire these hook points, and
|
||||
`supportedHookPoints()` reflects that.
|
||||
|
||||
---
|
||||
|
||||
## 6. PolicyEvaluator ↔ ADK SecurityPlugin
|
||||
|
||||
### ADK SecurityPlugin
|
||||
|
||||
```typescript
|
||||
class SecurityPlugin extends BasePlugin {
|
||||
policyEngine: BasePolicyEngine;
|
||||
|
||||
// In beforeToolCallback:
|
||||
async beforeToolCallback({ tool, args, context }) {
|
||||
const outcome = await this.policyEngine.evaluate(tool.name, args);
|
||||
switch (outcome) {
|
||||
case PolicyOutcome.DENY:
|
||||
throw error;
|
||||
case PolicyOutcome.CONFIRM:
|
||||
context.requestConfirmation({ hint });
|
||||
case PolicyOutcome.ALLOW:
|
||||
return undefined; // proceed
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mapping
|
||||
|
||||
| Our PolicyEvaluator | ADK SecurityPlugin | Notes |
|
||||
| ------------------------- | --------------------------------------------------------- | ------------------------------------------ |
|
||||
| `evaluate(request)` | `policyEngine.evaluate(toolName, args)` | ADK is simpler — tool name + args only |
|
||||
| `PolicyDecision.allow` | `PolicyOutcome.ALLOW` | Direct |
|
||||
| `PolicyDecision.deny` | `PolicyOutcome.DENY` | Direct |
|
||||
| `PolicyDecision.ask_user` | `PolicyOutcome.CONFIRM` → `context.requestConfirmation()` | ADK chains to ToolConfirmation |
|
||||
| `getExcluded()` | — | ADK doesn't pre-filter tools |
|
||||
| `request.principal` | — | ADK doesn't track who's calling |
|
||||
| `request.principalPath` | Could use `context.agentName` + branch | For hierarchical policy |
|
||||
| `request.context` | — | Our extension point for host-specific data |
|
||||
|
||||
### How ADK policy maps when host controls execution
|
||||
|
||||
With `pauseOnToolCalls: true`, the flow is:
|
||||
|
||||
1. ADK yields tool call → adapter converts to ToolRequestEvent
|
||||
2. **Host** runs PolicyEvaluator.evaluate() — NOT ADK's SecurityPlugin
|
||||
3. Host decides allow/deny/ask_user
|
||||
4. If allowed, host executes tool and sends result via `session.stream()`
|
||||
|
||||
This means **ADK's SecurityPlugin is bypassed when the host controls tool
|
||||
execution** — which is correct! The host's PolicyEvaluator is the authority.
|
||||
ADK's SecurityPlugin only matters when ADK executes tools internally
|
||||
(`pauseOnToolCalls: false`).
|
||||
|
||||
---
|
||||
|
||||
## 7. SessionContract ↔ ADK Session
|
||||
|
||||
### Session mapping
|
||||
|
||||
| Our SessionHandle | ADK Session | Notes |
|
||||
| ----------------- | ---------------------------------------- | ----------------------------------------- |
|
||||
| `id` | `Session.id` | Direct |
|
||||
| `agentName` | `Session.appName` | ADK uses appName, not agent name |
|
||||
| `events` | `Session.events: Event[]` | Direct (but ADK Events → our AgentEvents) |
|
||||
| `state` | `Session.state: Record<string, unknown>` | Direct |
|
||||
| `lastUpdateTime` | `Session.lastUpdateTime` | Direct |
|
||||
|
||||
### SessionProvider ↔ BaseSessionService
|
||||
|
||||
| Our SessionProvider | ADK BaseSessionService | Notes |
|
||||
| ----------------------------- | ----------------------------------------------- | -------------------------- |
|
||||
| `create(agentName, metadata)` | `createSession({ appName, userId })` | ADK requires userId |
|
||||
| `load(sessionId)` | `getSession({ appName, userId, sessionId })` | ADK requires all three IDs |
|
||||
| `list(agentName)` | `listSessions({ appName, userId })` | ADK scopes by userId |
|
||||
| `delete(sessionId)` | `deleteSession({ appName, userId, sessionId })` | Same pattern |
|
||||
|
||||
### Gap: ADK requires userId
|
||||
|
||||
ADK sessions are scoped by `(appName, userId, sessionId)`. Our interface uses
|
||||
just `sessionId`. The adapter can embed userId in the session metadata or derive
|
||||
it from HostContext.
|
||||
|
||||
### State prefixes (ADK-specific)
|
||||
|
||||
ADK uses prefixed state keys:
|
||||
|
||||
- `app:` — app-scoped, persisted
|
||||
- `user:` — user-scoped, persisted
|
||||
- `temp:` — temporary, stripped before persistence
|
||||
|
||||
Our `SessionHandle.state` is a flat `Record<string, unknown>`. The adapter
|
||||
preserves prefixes as-is — they're just string keys. No conflict.
|
||||
|
||||
---
|
||||
|
||||
## 8. ContentPart ↔ ADK Content/Part
|
||||
|
||||
### ADK uses Google GenAI types
|
||||
|
||||
ADK's `Content` and `Part` come from `@google/genai`:
|
||||
|
||||
```typescript
|
||||
interface Content {
|
||||
role?: string; // 'user' | 'model'
|
||||
parts: Part[];
|
||||
}
|
||||
|
||||
type Part = TextPart | InlineDataPart | FunctionCallPart | FunctionResponsePart | ...
|
||||
```
|
||||
|
||||
### Mapping
|
||||
|
||||
| Our ContentPart | ADK/GenAI Part | Notes |
|
||||
| --------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------ |
|
||||
| `{ type: 'text', text }` | `{ text: string }` | Direct |
|
||||
| `{ type: 'thought', thought }` | `{ thought: true, text: string }` | ADK uses `thought` boolean flag on TextPart |
|
||||
| `{ type: 'media', mimeType, data }` | `{ inlineData: { mimeType, data } }` | Restructure |
|
||||
| `{ type: 'reference', text, uri }` | `{ fileData: { fileUri, mimeType } }` | Map fileData → reference |
|
||||
| `{ type: 'refusal', text }` | — | Not in ADK/GenAI. Adapter would map from finishReason. |
|
||||
| `{ type: 'function_call', name, args, id }` | `{ functionCall: { name, args, id } }` | Unwrap |
|
||||
| `{ type: 'function_response', name, response, id }` | `{ functionResponse: { name, response, id } }` | Unwrap |
|
||||
|
||||
### Verdict: CLEAN MAPPING
|
||||
|
||||
The adapter converts between our flat discriminated union and ADK's nested Part
|
||||
structure. No information loss in either direction.
|
||||
|
||||
---
|
||||
|
||||
## 9. Composition ↔ ADK Agent Patterns
|
||||
|
||||
| Our CompositionConfig.pattern | ADK Agent type | Notes |
|
||||
| ----------------------------- | -------------------------------------- | ------------------------------------------------ |
|
||||
| `'hierarchical'` | Any agent with `subAgents` | Default — parent calls sub-agents as tools |
|
||||
| `'sequential'` | `SequentialAgent` | Runs children in order |
|
||||
| `'parallel'` | `ParallelAgent` | Runs children concurrently, branch isolation |
|
||||
| `'loop'` | `LoopAgent` | Repeats children until escalate or maxIterations |
|
||||
| `'transfer'` | LlmAgent with `transfer_to_agent` tool | Peer-to-peer handoff |
|
||||
|
||||
### Branch isolation
|
||||
|
||||
ADK's `ParallelAgent` gives each child an isolated `branch` context:
|
||||
|
||||
- Children don't see peer events
|
||||
- Each gets unique branch path: `"parent.child_0"`, `"parent.child_1"`
|
||||
- Results merged after all complete
|
||||
|
||||
Maps to our `threadId` — each parallel branch gets a unique threadId. Events
|
||||
from different branches are interleaved by the host.
|
||||
|
||||
---
|
||||
|
||||
## 10. Summary: Gaps and Resolutions
|
||||
|
||||
### No gaps blocking ADK integration:
|
||||
|
||||
| Concern | Status | Resolution |
|
||||
| ----------------------- | --------- | ------------------------------------------------------------------------- |
|
||||
| pauseOnToolCalls HITL | **Works** | Adapter maps to stream() cycle (§2) |
|
||||
| ToolConfirmation | **Works** | Maps to ElicitationRequest (§2) |
|
||||
| Auth requests | **Works** | Maps to ElicitationRequest (§2) |
|
||||
| Plugin hooks (12 types) | **Works** | Maps to LifecycleInterceptor.fire() (§5) |
|
||||
| Agent transfers | **Works** | Tool call (`transfer_to_agent`) + `stream_end` `reason: 'completed'` (§3) |
|
||||
| State delta pattern | **Works** | SessionUpdateEvent or \_meta (§3) |
|
||||
| Branch isolation | **Works** | threadId mapping (§9) |
|
||||
| AgentTool pattern | **Works** | SubagentTool with descriptor + factory (§4) |
|
||||
| Session management | **Works** | Adapter maps userId into session (§7) |
|
||||
|
||||
### Minor adapter complexity:
|
||||
|
||||
1. **Event fan-out:** One ADK Event may become multiple AgentEvents (message +
|
||||
tool call + elicitation). Adapter logic needed but straightforward.
|
||||
2. **userId scoping:** ADK sessions require userId; our interface doesn't.
|
||||
Adapter derives from HostContext.
|
||||
3. **Timestamp format:** ADK uses `number` (epoch ms); we use ISO 8601 string.
|
||||
Simple conversion.
|
||||
4. **Content structure:** ADK uses nested Part types; we use flat discriminated
|
||||
union. Adapter converts bidirectionally.
|
||||
|
||||
### ADK features our interface supports that gemini-cli doesn't have yet:
|
||||
|
||||
- `LoopAgent` / `ParallelAgent` / `SequentialAgent` composition → our
|
||||
CompositionConfig
|
||||
- `eventActions.stateDelta` → our SessionUpdateEvent
|
||||
- `eventActions.transferToAgent` → tool call (`transfer_to_agent`) +
|
||||
`stream_end` `reason: 'completed'`
|
||||
- `eventActions.escalate` → `stream_end` `reason: 'completed'` with
|
||||
`data: { escalateReason }`
|
||||
- Long-running tools → our ToolUpdateEvent
|
||||
- Auth credential flow → our ElicitationRequest with kind: 'auth_required'
|
||||
@@ -0,0 +1,274 @@
|
||||
# ADK-TS (Agent Development Kit - TypeScript) Architecture Notes
|
||||
|
||||
## Package: `@google/adk` v0.4.0
|
||||
|
||||
**Location:** `/Users/adamfweidman/Desktop/adk-int/adk-js/core/`
|
||||
|
||||
## Agent Hierarchy
|
||||
|
||||
```
|
||||
BaseAgent (abstract)
|
||||
├── LlmAgent - Model-driven agent with tools (the main one)
|
||||
├── LoopAgent - Runs sub-agents in a loop (maxIterations, escalate to exit)
|
||||
├── ParallelAgent - Runs sub-agents concurrently (isolated branches)
|
||||
└── SequentialAgent - Runs sub-agents sequentially
|
||||
```
|
||||
|
||||
### BaseAgent Config
|
||||
|
||||
- `name: string` - Unique identifier (must be valid JS identifier)
|
||||
- `description?: string` - One-line capability for model routing
|
||||
- `parentAgent?: BaseAgent` - Parent in agent tree
|
||||
- `subAgents?: BaseAgent[]` - Child agents
|
||||
- `beforeAgentCallback / afterAgentCallback` - Pre/post execution hooks
|
||||
|
||||
### LlmAgent Config (extends BaseAgent)
|
||||
|
||||
- `model?: string | BaseLlm` - LLM to use
|
||||
- `instruction?: string | InstructionProvider` - Agent-specific instructions
|
||||
- `globalInstruction?: string | InstructionProvider` - Tree-wide (root only)
|
||||
- `tools?: ToolUnion[]` - Available tools
|
||||
- `generateContentConfig?: GenerateContentConfig` - LLM params
|
||||
- `disallowTransferToParent / disallowTransferToPeers` - Transfer controls
|
||||
- `includeContents?: 'default' | 'none'` - Context history inclusion
|
||||
- `inputSchema / outputSchema` - Validation schemas
|
||||
- `outputKey?: string` - Session state key for output storage
|
||||
- `beforeModelCallback / afterModelCallback` - LLM hooks
|
||||
- `beforeToolCallback / afterToolCallback` - Tool hooks
|
||||
- `requestProcessors / responseProcessors` - LLM request/response processors
|
||||
- `codeExecutor?: BaseCodeExecutor`
|
||||
|
||||
## Event System
|
||||
|
||||
### Event Interface
|
||||
|
||||
```typescript
|
||||
interface Event extends LlmResponse {
|
||||
id: string;
|
||||
invocationId: string;
|
||||
author?: string; // "user" or agent name
|
||||
actions: EventActions; // State/artifact/auth/transfer operations
|
||||
longRunningToolIds?: string[];
|
||||
branch?: string; // Hierarchical agent path
|
||||
timestamp: number;
|
||||
content?: Content;
|
||||
partial?: boolean; // Streaming indicator
|
||||
}
|
||||
```
|
||||
|
||||
### EventActions
|
||||
|
||||
```typescript
|
||||
interface EventActions {
|
||||
skipSummarization?: boolean;
|
||||
stateDelta: Record<string, unknown>;
|
||||
artifactDelta: Record<string, number>;
|
||||
transferToAgent?: string;
|
||||
escalate?: boolean;
|
||||
requestedAuthConfigs: Record<string, AuthConfig>;
|
||||
requestedToolConfirmations: Record<string, ToolConfirmation>;
|
||||
}
|
||||
```
|
||||
|
||||
### Structured Events (utility layer)
|
||||
|
||||
Converts raw Event to discriminated union:
|
||||
|
||||
```
|
||||
EventType: THOUGHT | CONTENT | TOOL_CALL | TOOL_RESULT | CALL_CODE |
|
||||
CODE_RESULT | ERROR | ACTIVITY | TOOL_CONFIRMATION | FINISHED
|
||||
```
|
||||
|
||||
## Tool System
|
||||
|
||||
### BaseTool (abstract)
|
||||
|
||||
- `name, description, isLongRunning`
|
||||
- `_getDeclaration(): FunctionDeclaration` - OpenAPI schema for LLM
|
||||
- `runAsync(request): Promise<unknown>` - Execute tool
|
||||
- `processLlmRequest(request): Promise<void>` - Preprocessing
|
||||
|
||||
### Concrete Tool Types
|
||||
|
||||
1. **FunctionTool** - Generic typed tools (Zod schema support)
|
||||
2. **AgentTool** - Wrap agents as tools (for hierarchical composition)
|
||||
3. **MCPTool** - Model Context Protocol server tools
|
||||
4. **GoogleSearchTool** - Built-in web search
|
||||
5. **ExitLoopTool** - Signal loop exit
|
||||
6. **LongRunningFunctionTool** - Async long-running operations
|
||||
|
||||
### BaseToolset
|
||||
|
||||
- Filter tools by predicate or string list
|
||||
- `getTools(context)`, `close()`, `isToolSelected()`
|
||||
- **MCPToolset** - Toolset for MCP server connections
|
||||
|
||||
## Session Management
|
||||
|
||||
### Session Interface
|
||||
|
||||
```typescript
|
||||
interface Session {
|
||||
id: string;
|
||||
appName: string;
|
||||
userId: string;
|
||||
state: Record<string, unknown>; // Mutable key-value store
|
||||
events: Event[]; // Complete conversation history
|
||||
lastUpdateTime: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Session Services
|
||||
|
||||
- `BaseSessionService` (abstract) - createSession, getSession, listSessions,
|
||||
deleteSession, appendEvent
|
||||
- `InMemorySessionService` - In-process storage
|
||||
- `DatabaseSessionService` - Mikro-ORM backed (SQL)
|
||||
|
||||
### State Management
|
||||
|
||||
- `State` class wraps base state + delta
|
||||
- `get()` returns from delta if present, else base
|
||||
- `set()` updates delta only
|
||||
- `hasDelta()` checks if changes made
|
||||
|
||||
## Human-in-the-Loop (HITL)
|
||||
|
||||
### Tool Confirmation
|
||||
|
||||
```typescript
|
||||
class ToolConfirmation {
|
||||
hint?: string; // Guidance for user
|
||||
confirmed: boolean; // User approval
|
||||
payload?: unknown; // Additional context
|
||||
}
|
||||
```
|
||||
|
||||
### Security Plugin
|
||||
|
||||
- `beforeToolCallback` - Evaluates policy before tool execution
|
||||
- `BasePolicyEngine` interface with `evaluate()` method
|
||||
- `PolicyOutcome`: DENY | CONFIRM | ALLOW
|
||||
|
||||
### Auth Requests
|
||||
|
||||
- `context.requestCredential(authConfig)` - Request auth from user
|
||||
- `context.getAuthResponse(authConfig)` - Check for auth response
|
||||
- Sets `eventActions.requestedAuthConfigs[functionCallId]`
|
||||
|
||||
## Multi-Agent Patterns
|
||||
|
||||
### Agent Transfer
|
||||
|
||||
- LlmAgent injects `transfer_to_agent(agentName)` tool
|
||||
- Sets `eventActions.transferToAgent = targetAgentName`
|
||||
- Runner resolves target and continues
|
||||
- Can transfer to: sub-agents, parent (if not disabled), peers (if not disabled)
|
||||
|
||||
### Parallel Agent
|
||||
|
||||
- Runs all subAgents concurrently
|
||||
- Isolates each via `branch` context
|
||||
- Sub-agents don't see peer history
|
||||
- Merges event streams with fair ordering
|
||||
|
||||
### Loop Agent
|
||||
|
||||
- Repeatedly runs subAgents
|
||||
- `maxIterations` caps loop count
|
||||
- Exits on `event.actions.escalate === true`
|
||||
|
||||
## Plugin System
|
||||
|
||||
### BasePlugin Lifecycle Hooks (14 hooks!)
|
||||
|
||||
- `onUserMessageCallback` - Preprocess user messages
|
||||
- `beforeRunCallback` - Before agent run (can short-circuit)
|
||||
- `onEventCallback` - Per-event (can modify events)
|
||||
- `afterRunCallback` - Final cleanup
|
||||
- `beforeAgentCallback / afterAgentCallback` - Agent lifecycle
|
||||
- `beforeModelCallback / afterModelCallback` - LLM lifecycle
|
||||
- `onModelErrorCallback` - Model error handling
|
||||
- `beforeToolCallback / afterToolCallback` - Tool lifecycle
|
||||
- `onToolErrorCallback` - Tool error handling
|
||||
|
||||
### Built-in Plugins
|
||||
|
||||
- **LoggingPlugin** - Debug logging
|
||||
- **SecurityPlugin** - Policy enforcement + tool confirmation
|
||||
- **PluginManager** - Plugin orchestration
|
||||
|
||||
## Runner
|
||||
|
||||
### Runner Config
|
||||
|
||||
```typescript
|
||||
interface RunnerConfig {
|
||||
appName: string;
|
||||
agent: BaseAgent; // Root agent
|
||||
plugins?: BasePlugin[];
|
||||
artifactService?: BaseArtifactService;
|
||||
sessionService: BaseSessionService; // Required
|
||||
memoryService?: BaseMemoryService;
|
||||
credentialService?: BaseCredentialService;
|
||||
}
|
||||
```
|
||||
|
||||
### RunConfig (per-run options)
|
||||
|
||||
```typescript
|
||||
interface RunConfig {
|
||||
speechConfig?: SpeechConfig;
|
||||
responseModalities?: Modality[];
|
||||
maxLlmCalls?: number; // Default 500
|
||||
pauseOnToolCalls?: boolean; // Client-side tool execution
|
||||
streamingMode?: StreamingMode; // NONE | SSE | BIDI
|
||||
// ... audio/live configs
|
||||
}
|
||||
```
|
||||
|
||||
### Execution Pipeline
|
||||
|
||||
1. Load or create session
|
||||
2. Create InvocationContext
|
||||
3. Run pluginManager.runOnUserMessageCallback()
|
||||
4. Append user message to session
|
||||
5. Run agent.runAsync(invocationContext) → yields events
|
||||
6. For each non-partial event: append to session
|
||||
7. Run pluginManager.runOnEventCallback()
|
||||
8. Run pluginManager.runAfterRunCallback()
|
||||
|
||||
## Model Layer
|
||||
|
||||
### BaseLlm (abstract)
|
||||
|
||||
- `generateContentAsync(llmRequest, stream?): AsyncGenerator<LlmResponse>`
|
||||
- `connect(llmRequest): Promise<BaseLlmConnection>` - For live/streaming
|
||||
|
||||
### Implementations
|
||||
|
||||
- `Gemini` - Google Gemini API
|
||||
- `ApigeeLlm` - Apigee-wrapped models
|
||||
- `LLMRegistry` - Static registry for model lookup
|
||||
|
||||
## Service Adapters (all abstract base + implementations)
|
||||
|
||||
| Service | Implementations |
|
||||
| --------------------- | ------------------------------ |
|
||||
| BaseSessionService | InMemory, Database (Mikro-ORM) |
|
||||
| BaseArtifactService | InMemory, File, GCS |
|
||||
| BaseMemoryService | InMemory |
|
||||
| BaseCredentialService | InMemory |
|
||||
| BaseCodeExecutor | BuiltIn |
|
||||
|
||||
## Design Patterns
|
||||
|
||||
1. **Symbol-based type guards** - Every class uses `Symbol.for()` + `isXxx()`
|
||||
2. **Abstract base classes** - Service interfaces via abstract classes
|
||||
3. **Async generators** - All agent execution yields events
|
||||
4. **Context objects** - Rich context passed to callbacks/tools
|
||||
5. **Delta state** - Session state + event action deltas
|
||||
6. **Plugin middleware** - 14 hooks at multiple execution points
|
||||
7. **Tree-based hierarchy** - Parent-child agents with root traversal
|
||||
8. **Branch isolation** - Parallel agents use branch paths
|
||||
9. **Callback chains** - Multiple callbacks per stage with early termination
|
||||
@@ -0,0 +1,587 @@
|
||||
# Cross-SDK Comparison: Events, Agents, and Interface Superset
|
||||
|
||||
## 1. AgentEvents: Our Outline vs Michael's
|
||||
|
||||
Our outline and Michael's `Gemini CLI Agents.txt` are **nearly identical** in
|
||||
event taxonomy. The only difference is we added a `stream_end` event type:
|
||||
|
||||
| # | Michael's Events | Our Outline | Delta |
|
||||
| --- | ---------------------- | --------------------- | ------------------------------------------------------------------------------- |
|
||||
| 1 | `initialize` | `InitializeEvent` | Same |
|
||||
| 2 | `session_update` | `SessionUpdateEvent` | Same |
|
||||
| 3 | `message` | `MessageEvent` | Same — streaming handled by AsyncGenerator |
|
||||
| 4 | `tool_request` | `ToolRequestEvent` | Same |
|
||||
| 5 | `tool_update` | `ToolUpdateEvent` | Same |
|
||||
| 6 | `tool_response` | `ToolResponseEvent` | Same |
|
||||
| 7 | `elicitation_request` | `ElicitationRequest` | Same |
|
||||
| 8 | `elicitation_response` | `ElicitationResponse` | Same |
|
||||
| 9 | `usage` | `UsageEvent` | Same |
|
||||
| 10 | `error` | `ErrorEvent` | Same |
|
||||
| 11 | `custom` | `CustomEvent` | Same |
|
||||
| 12 | — | **StreamEnd** | **Added**: completed, failed, aborted, max_turns, max_budget, max_time, refusal |
|
||||
|
||||
### Minor structural differences:
|
||||
|
||||
| Aspect | Michael | Our Outline |
|
||||
| ---------------------- | --------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||
| **Base type** | `AgentEventCommon` with `type: string` (fully open) | `AgentEventBase` with `type: AgentEventType` (`'known' \| (string & {})`) |
|
||||
| **Agent ID** | — | `agentId` on event base (which agent emitted this event) |
|
||||
| **Event map** | Generic `interface AgentEvents` + mapped type | Same — adopted Michael's pattern for declaration merging extensibility |
|
||||
| **ContentPart.\_meta** | Required (`_meta: Record<string, unknown>`) | Optional (`_meta?: Record<string, unknown>`) |
|
||||
| **ErrorData.status** | Google RPC codes (`'RESOURCE_EXHAUSTED' \| '...'`) | Open string (per our generic philosophy) |
|
||||
| **Message.role** | `'user' \| 'agent' \| 'developer'` | Same |
|
||||
| **Stream end** | Only `initialize` | `stream_end` with `reason` field + open `data` bag |
|
||||
| **Handoff** | Not covered | Tool call (`transfer_to_agent`) — no dedicated event |
|
||||
| **Pausing** | Implicit (elicitation/tool events) | Same — no explicit pause/resume events |
|
||||
|
||||
### Design decisions adopted from Michael
|
||||
|
||||
1. **`interface AgentEvents` + mapped type** — Michael's pattern enables
|
||||
declaration merging, letting any module add new event types without modifying
|
||||
the base definition. Strictly better than an explicit union type.
|
||||
2. **`_meta` on ContentPart** — More extensible. We adopted it (as optional).
|
||||
3. **Implicit pausing** — No separate pause/resume events. When the agent emits
|
||||
an `elicitation_request` or `tool_request`, the stream naturally pauses. The
|
||||
host calls `stream()` to resume.
|
||||
|
||||
---
|
||||
|
||||
## 2. Claude Agent SDK — Key Interfaces
|
||||
|
||||
Source: `@anthropic-ai/claude-agent-sdk`
|
||||
|
||||
### Agent Execution Model
|
||||
|
||||
```typescript
|
||||
// Entry point — not an interface, a function
|
||||
function query({
|
||||
prompt: string | AsyncIterable<SDKUserMessage>,
|
||||
options?: Options
|
||||
}): Query // extends AsyncGenerator<SDKMessage, void>
|
||||
```
|
||||
|
||||
### Message Types (Event Stream)
|
||||
|
||||
```typescript
|
||||
type SDKMessage =
|
||||
| SystemMessage // subtype: "init" | "compact_boundary"
|
||||
| AssistantMessage // Claude's response with tool calls
|
||||
| UserMessage // Tool results fed back
|
||||
| StreamEvent // Raw API stream events (opt-in)
|
||||
| ResultMessage // Final: success | error_max_turns | error_max_budget_usd | error_during_execution
|
||||
| CompactBoundaryMessage; // Context compaction marker
|
||||
```
|
||||
|
||||
### Tool Approval (HITL)
|
||||
|
||||
```typescript
|
||||
canUseTool: async (toolName: string, input: Record<string, any>) =>
|
||||
Promise<
|
||||
| { behavior: 'allow'; updatedInput: Record<string, any> }
|
||||
| { behavior: 'deny'; message: string }
|
||||
>;
|
||||
```
|
||||
|
||||
### Subagent Definition
|
||||
|
||||
```typescript
|
||||
interface AgentDefinition {
|
||||
description: string; // When to invoke
|
||||
prompt: string; // System prompt
|
||||
tools?: string[]; // Available tools (defaults to all)
|
||||
model?: 'sonnet' | 'opus' | 'haiku' | 'inherit';
|
||||
}
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
```typescript
|
||||
interface Options {
|
||||
continue?: boolean; // Resume most recent session
|
||||
resume?: string; // Resume by session ID
|
||||
forkSession?: boolean; // Branch from resume point
|
||||
persistSession?: boolean; // Default: true
|
||||
maxTurns?: number;
|
||||
maxBudgetUsd?: number; // Spend limit
|
||||
permissionMode?: 'default' | 'acceptEdits' | 'plan' | 'dontAsk' | 'bypassPermissions';
|
||||
structuredOutput?: { type: "json_schema", ... };
|
||||
}
|
||||
```
|
||||
|
||||
### Result (Termination)
|
||||
|
||||
```typescript
|
||||
interface SDKResultMessage {
|
||||
type: 'result';
|
||||
subtype:
|
||||
| 'success'
|
||||
| 'error_max_turns'
|
||||
| 'error_max_budget_usd'
|
||||
| 'error_during_execution'
|
||||
| 'error_max_structured_output_retries';
|
||||
result?: string;
|
||||
total_cost_usd: number;
|
||||
usage: { input_tokens: number; output_tokens: number };
|
||||
num_turns: number;
|
||||
session_id: string;
|
||||
stop_reason: string | null; // "end_turn", "max_tokens", "refusal"
|
||||
}
|
||||
```
|
||||
|
||||
### V2 Preview (Simpler API)
|
||||
|
||||
```typescript
|
||||
await using session = unstable_v2_createSession({ model: "..." });
|
||||
await session.send("Hello!");
|
||||
for await (const msg of session.stream()) { ... }
|
||||
await session.send("Follow-up");
|
||||
for await (const msg of session.stream()) { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. OpenAI Codex SDK / Responses API — Key Interfaces
|
||||
|
||||
### Codex SDK (TypeScript)
|
||||
|
||||
```typescript
|
||||
// Client
|
||||
const codex = new Codex({ env?, config? });
|
||||
const thread = codex.startThread({ workingDirectory?, skipGitRepoCheck? });
|
||||
const thread = codex.resumeThread(threadId);
|
||||
|
||||
// Execution
|
||||
const turn = await thread.run(prompt: string | InputEntry[], options?);
|
||||
const { events } = await thread.runStreamed(prompt);
|
||||
|
||||
// Streaming
|
||||
for await (const event of events) {
|
||||
switch (event.type) {
|
||||
case "item.completed": // event.item
|
||||
case "turn.completed": // event.usage
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Responses API Streaming Events (53 types)
|
||||
|
||||
Organized hierarchically:
|
||||
|
||||
**Response Lifecycle (7):**
|
||||
|
||||
- `response.queued`, `response.created`, `response.in_progress`
|
||||
- `response.completed`, `response.incomplete`, `response.failed`
|
||||
- `error`
|
||||
|
||||
**Content Streaming (8):**
|
||||
|
||||
- `response.output_item.added`, `response.output_item.done`
|
||||
- `response.content_part.added`, `response.content_part.done`
|
||||
- `response.output_text.delta`, `response.output_text.done`
|
||||
- `response.refusal.delta`, `response.refusal.done`
|
||||
|
||||
**Reasoning (6):**
|
||||
|
||||
- `response.reasoning_text.delta`, `response.reasoning_text.done`
|
||||
- `response.reasoning_summary_part.added`,
|
||||
`response.reasoning_summary_part.done`
|
||||
- `response.reasoning_summary_text.delta`,
|
||||
`response.reasoning_summary_text.done`
|
||||
|
||||
**Function Calls (2):**
|
||||
|
||||
- `response.function_call_arguments.delta`,
|
||||
`response.function_call_arguments.done`
|
||||
|
||||
**MCP (8):**
|
||||
|
||||
- `response.mcp_call_arguments.delta`, `response.mcp_call_arguments.done`
|
||||
- `response.mcp_call.in_progress`, `response.mcp_call.completed`,
|
||||
`response.mcp_call.failed`
|
||||
- `response.mcp_list_tools.in_progress`, `response.mcp_list_tools.completed`,
|
||||
`response.mcp_list_tools.failed`
|
||||
|
||||
**Built-in Tools (15):**
|
||||
|
||||
- File search: `in_progress`, `searching`, `completed`
|
||||
- Web search: `in_progress`, `searching`, `completed`
|
||||
- Code interpreter: `in_progress`, `interpreting`, `code.delta`, `code.done`,
|
||||
`completed`
|
||||
- Image gen: `in_progress`, `generating`, `partial_image`, `completed`
|
||||
|
||||
**Audio (4):**
|
||||
|
||||
- `response.audio.delta`, `response.audio.done`
|
||||
- `response.audio.transcript.delta`, `response.audio.transcript.done`
|
||||
|
||||
**Annotations (1):**
|
||||
|
||||
- `response.output_text.annotation.added`
|
||||
|
||||
### OpenAI Agents SDK (higher-level)
|
||||
|
||||
```python
|
||||
# Python-first, but patterns apply
|
||||
class RunItemStreamEvent:
|
||||
name: Literal[
|
||||
"message_output_created",
|
||||
"handoff_requested",
|
||||
"handoff_occurred",
|
||||
"tool_called",
|
||||
"tool_output",
|
||||
"tool_search_called",
|
||||
"tool_search_output_created",
|
||||
"reasoning_item_created",
|
||||
"mcp_approval_requested",
|
||||
"mcp_approval_response",
|
||||
"mcp_list_tools",
|
||||
]
|
||||
|
||||
class AgentUpdatedStreamEvent:
|
||||
# Fires when current agent changes (handoff)
|
||||
new_agent: Agent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Superset Analysis — What Changes Our Interfaces?
|
||||
|
||||
### Concepts Present in ALL Systems
|
||||
|
||||
| Concept | gemini-cli | ADK-TS | Claude SDK | Codex/OpenAI | Our Interfaces |
|
||||
| --------------------- | ---------- | ------ | ------------- | -------------- | ----------------------- |
|
||||
| Text streaming | ✅ | ✅ | ✅ | ✅ | ✅ MessageEvent |
|
||||
| Tool request/response | ✅ | ✅ | ✅ | ✅ | ✅ ToolRequest/Response |
|
||||
| Thinking/reasoning | ✅ | ✅ | ✅ (thinking) | ✅ (reasoning) | ✅ ContentPart.thought |
|
||||
| Error events | ✅ | ✅ | ✅ | ✅ | ✅ ErrorEvent |
|
||||
| Token usage | ✅ | ✅ | ✅ | ✅ | ✅ UsageEvent |
|
||||
| Tool progress | ✅ | ✅ | — | ✅ | ✅ ToolUpdateEvent |
|
||||
| Session resume | ✅ | ✅ | ✅ | ✅ | ✅ sessionRef |
|
||||
| Subagents | ✅ | ✅ | ✅ | — | ✅ threadId |
|
||||
| Abort/cancel | ✅ | ✅ | ✅ | ✅ | ✅ abort() |
|
||||
| Metadata escape hatch | — | ✅ | — | — | ✅ \_meta |
|
||||
|
||||
### NEW Concepts From Claude/Codex That We Should Incorporate
|
||||
|
||||
#### 4.1 Structured Stream End Reasons (HIGH PRIORITY)
|
||||
|
||||
**What:** Claude SDK has typed termination:
|
||||
`success | error_max_turns | error_max_budget_usd | error_during_execution`.
|
||||
OpenAI has `completed | incomplete | failed`.
|
||||
|
||||
**Why it matters:** We need a `stream_end` event that captures why the stream
|
||||
ended — the one signal not covered by other event types.
|
||||
|
||||
**Final design — `stream_end` with `reason` + open `data` bag:**
|
||||
|
||||
```typescript
|
||||
type StreamEndReason =
|
||||
| 'completed'
|
||||
| 'failed'
|
||||
| 'aborted'
|
||||
| 'max_turns'
|
||||
| 'max_budget'
|
||||
| 'max_time'
|
||||
| 'refusal'
|
||||
| (string & {});
|
||||
|
||||
interface StreamEnd {
|
||||
reason: StreamEndReason;
|
||||
data?: Record<string, unknown>; // { result?, cost?, usage?, numTurns?, error?, ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Design rationale:**
|
||||
|
||||
- Start is covered by `initialize`. Pausing is implicit (elicitation/tool
|
||||
request events). Handoff is a tool call (`transfer_to_agent`).
|
||||
- End-of-stream details go in `data` as an open bag, not fixed fields.
|
||||
|
||||
#### 4.2 Budget Constraints (MEDIUM PRIORITY)
|
||||
|
||||
**What:** Claude SDK has `maxBudgetUsd`. Neither gemini-cli nor ADK has this
|
||||
today.
|
||||
|
||||
**Why it matters:** Cost control is critical for production deployments.
|
||||
|
||||
**Proposed change to AgentConstraints:**
|
||||
|
||||
```typescript
|
||||
interface AgentConstraints {
|
||||
maxTurns?: number;
|
||||
maxTimeMinutes?: number;
|
||||
maxLlmCalls?: number;
|
||||
maxBudgetUsd?: number; // NEW: from Claude SDK
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 Session Forking (MEDIUM PRIORITY)
|
||||
|
||||
**What:** Claude SDK supports `forkSession: boolean` — branch from a resume
|
||||
point to explore alternatives.
|
||||
|
||||
**Why it matters:** Enables "what if" exploration without destroying history.
|
||||
Useful for plan mode.
|
||||
|
||||
**Proposed change to ExecutionRequest:**
|
||||
|
||||
```typescript
|
||||
interface ExecutionRequest {
|
||||
// ... existing fields ...
|
||||
sessionRef?: string | SessionSnapshot;
|
||||
forkSession?: boolean; // NEW: branch from sessionRef instead of continuing
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.4 Permission Modes on Execution (MEDIUM PRIORITY)
|
||||
|
||||
**What:** Claude has 5 permission modes:
|
||||
`default | acceptEdits | plan | dontAsk | bypassPermissions`. gemini-cli has 4
|
||||
approval modes: `default | autoEdit | yolo | plan`.
|
||||
|
||||
**Why it matters:** Both systems have this concept. It should be in
|
||||
ExecutionOptions, not hard-coded.
|
||||
|
||||
**Proposed change to ExecutionOptions:**
|
||||
|
||||
```typescript
|
||||
interface ExecutionOptions {
|
||||
// ... existing fields ...
|
||||
permissionMode?: string; // Open string. Conventions: 'default' | 'auto_edit' | 'autonomous' | 'plan' | string
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.5 Agent Handoff (MEDIUM PRIORITY)
|
||||
|
||||
**What:** OpenAI Agents SDK has explicit `handoff_requested` /
|
||||
`handoff_occurred` events plus `AgentUpdatedStreamEvent`. ADK has
|
||||
`transfer_to_agent` tool + `eventActions.transferToAgent`. Claude SDK has
|
||||
subagent invocation via Agent tool.
|
||||
|
||||
**Why it matters:** When agent A delegates to agent B, the host/UI needs to
|
||||
know.
|
||||
|
||||
**Design decision: Handoff is a tool call, not a separate event type.**
|
||||
|
||||
The agent calls `transfer_to_agent` as a tool (ToolRequest event). The host
|
||||
intercepts this tool call (since host controls tool execution), looks up the
|
||||
target agent, creates a new executor via the factory, and mediates the handoff.
|
||||
The originating agent's stream ends with `stream_end` reason `'completed'`.
|
||||
|
||||
```typescript
|
||||
// 1. Agent emits tool request:
|
||||
{ type: 'tool_request', name: 'transfer_to_agent', args: { target: 'coder', reason: '...' } }
|
||||
|
||||
// 2. Host mediates handoff, originating agent completes:
|
||||
{ type: 'stream_end', reason: 'completed', agentId: 'planner', data: { handoffTarget: 'coder' } }
|
||||
```
|
||||
|
||||
This avoids duplicating routing logic between stream_end events and tool calls.
|
||||
Matches ADK's `transfer_to_agent` tool pattern.
|
||||
|
||||
#### 4.6 Refusal as Distinct Signal (LOW PRIORITY)
|
||||
|
||||
**What:** OpenAI has explicit `response.refusal.delta/done` events. Claude has
|
||||
`stop_reason: "refusal"`.
|
||||
|
||||
**Why it matters:** Model refusals are operationally important (safety, policy).
|
||||
|
||||
**Proposed:** No new event type. Handle via `MessageEvent` with a `refusal`
|
||||
content part type, or via `ErrorEvent` with specific error code. ContentPart can
|
||||
be extended:
|
||||
|
||||
```typescript
|
||||
| { type: 'refusal'; text: string }
|
||||
```
|
||||
|
||||
#### 4.7 Content Annotations (LOW PRIORITY)
|
||||
|
||||
**What:** OpenAI has `response.output_text.annotation.added` for citations, file
|
||||
paths.
|
||||
|
||||
**Why it matters:** Citations and source attribution are increasingly important.
|
||||
|
||||
**Proposed:** Michael's `reference` ContentPart already covers this. No change
|
||||
needed — `reference` with `uri` and `text` handles citations.
|
||||
|
||||
#### 4.8 Context Compaction Events (LOW PRIORITY)
|
||||
|
||||
**What:** Claude SDK has `CompactBoundaryMessage` marking when context was
|
||||
compressed.
|
||||
|
||||
**Why it matters:** For long sessions, knowing when context was compressed helps
|
||||
with debugging and UI.
|
||||
|
||||
**Proposed:** `CustomEvent` with `kind: 'compact_boundary'`. No new event type
|
||||
needed.
|
||||
|
||||
#### 4.9 Structured Output Schema (ALREADY COVERED)
|
||||
|
||||
**What:** Both Claude (`structuredOutput`) and OpenAI support JSON Schema output
|
||||
constraints.
|
||||
|
||||
**Status:** Already covered by `AgentDescriptor.outputSchema: JsonSchema`. No
|
||||
change needed.
|
||||
|
||||
### Concepts We DON'T Need to Adopt
|
||||
|
||||
| Concept | Why Skip |
|
||||
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| OpenAI's 53 granular streaming events | Too coupled to Responses API internals. Our `ToolUpdateEvent` + `MessageEvent` via AsyncGenerator abstracts over this. |
|
||||
| OpenAI's per-tool-type events (file_search, web_search, code_interpreter) | Tool-specific progress belongs in `ToolUpdateEvent.data`, not in the event taxonomy. |
|
||||
| Audio/Image streaming events | Handle via `ToolUpdateEvent` with media ContentParts. When needed, add as ContentPart types, not event types. |
|
||||
| Claude's raw `StreamEvent` wrapper | Implementation detail of the Claude API client. Our adapters consume these internally. |
|
||||
| MCP-specific events (mcp_call, mcp_list_tools) | MCP tools are just tools. Use generic `ToolRequestEvent/ToolResponseEvent`. MCP approval is an `ElicitationRequest`. |
|
||||
|
||||
---
|
||||
|
||||
## 5. Updated Event Type Comparison (Full Superset)
|
||||
|
||||
| # | Event Type | Michael | Our Outline | Claude SDK | OpenAI | Verdict |
|
||||
| --- | -------------------- | ------- | ----------- | ----------------------------- | --------------------------------- | ---------------------------------------------- |
|
||||
| 1 | Initialize | ✅ | ✅ | SystemMessage(init) | — | **Keep** |
|
||||
| 2 | Session Update | ✅ | ✅ | — | — | **Keep** |
|
||||
| 3 | Message | ✅ | ✅ | AssistantMessage | output_text.delta/done | **Keep** |
|
||||
| 4 | Tool Request | ✅ | ✅ | AssistantMessage.tool_use | function_call_arguments | **Keep** |
|
||||
| 5 | Tool Update | ✅ | ✅ | — | per-tool progress events | **Keep** |
|
||||
| 6 | Tool Response | ✅ | ✅ | UserMessage | — | **Keep** |
|
||||
| 7 | Elicitation Request | ✅ | ✅ | canUseTool callback | mcp_approval_requested | **Keep** |
|
||||
| 8 | Elicitation Response | ✅ | ✅ | canUseTool return | mcp_approval_response | **Keep** |
|
||||
| 9 | Usage | ✅ | ✅ | ResultMessage.usage | response.completed | **Keep** |
|
||||
| 10 | Error | ✅ | ✅ | ResultMessage(error\_\*) | response.failed | **Keep** |
|
||||
| 11 | Custom | ✅ | ✅ | — | — | **Keep** |
|
||||
| 12 | StreamEnd | — | ✅ | ResultMessage + SystemMessage | response.created/completed/failed | **Keep — `stream_end` with `reason` + `data`** |
|
||||
|
||||
**Result: Our 12 event types are the right abstraction level.** Claude and
|
||||
OpenAI validate every category. The granularity differences (OpenAI's 53 vs
|
||||
our 12) are implementation details that adapters handle internally. `stream_end`
|
||||
uses a single `reason` field with an open `data` bag. Handoff is a tool call.
|
||||
Pausing is implicit.
|
||||
|
||||
---
|
||||
|
||||
## 6. Updated ContentPart Types (Superset)
|
||||
|
||||
```typescript
|
||||
type ContentPart = (
|
||||
| { type: 'text'; text: string }
|
||||
| { type: 'thought'; thought: string; thoughtSignature?: string }
|
||||
| { type: 'media'; data?: string; uri?: string; mimeType?: string }
|
||||
| {
|
||||
type: 'reference';
|
||||
text: string;
|
||||
data?: string;
|
||||
uri?: string;
|
||||
mimeType?: string;
|
||||
}
|
||||
| { type: 'refusal'; text: string } // NEW: from OpenAI
|
||||
) &
|
||||
// Future: type: string for unknown types from new SDKs
|
||||
{ _meta?: Record<string, unknown> };
|
||||
```
|
||||
|
||||
Adding `refusal` as a ContentPart type (rather than a new event) keeps the event
|
||||
taxonomy stable while supporting model refusals from both Claude and OpenAI.
|
||||
|
||||
---
|
||||
|
||||
## 7. Key Architectural Patterns Across SDKs
|
||||
|
||||
### Pattern: Execution Entry Points
|
||||
|
||||
| SDK | Entry Point | Multi-turn Pattern |
|
||||
| ----------- | ------------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| Michael | `agent.send(trajectory, data)` / `session.send()` + `session.update()` | Same method / three-method session |
|
||||
| Our Outline | `session.stream(data)` + `session.update(config)` + `session.steer(data)` | Four-method session (stream/update/steer/abort) |
|
||||
| Claude SDK | `query({ prompt, options })` | New `query()` call with `resume: sessionId` |
|
||||
| Claude V2 | `session.send()` + `session.stream()` | Separate send/stream |
|
||||
| Codex SDK | `thread.run(prompt)` / `thread.runStreamed(prompt)` | Same thread object |
|
||||
|
||||
**Observation:** Claude V2 and Codex both use a stateful session/thread object
|
||||
with send+stream. Michael uses a single `send()` method. Our `stream()` method
|
||||
is the unified version — the first call starts, subsequent calls continue (like
|
||||
ADK's `runAsync()`).
|
||||
|
||||
### Pattern: Tool Approval
|
||||
|
||||
| SDK | Pattern | Sync/Async |
|
||||
| ----------- | -------------------------------------------------------- | ------------------------ |
|
||||
| gemini-cli | PolicyEngine + ConfirmationBus | Async (message bus) |
|
||||
| ADK-TS | SecurityPlugin.policyCheck() | Async (plugin callback) |
|
||||
| Claude SDK | `canUseTool()` callback | Async (callback) |
|
||||
| OpenAI | `mcp_approval_requested` event | Event-based |
|
||||
| Our Outline | `ElicitationRequest` event + `PolicyEvaluator` interface | Both (event + interface) |
|
||||
|
||||
**Observation:** Our approach covers both patterns — the `ElicitationRequest`
|
||||
event for event-based approval (like OpenAI), and the `PolicyEvaluator`
|
||||
interface for synchronous policy checks (like gemini-cli/ADK/Claude). This is
|
||||
the right superset.
|
||||
|
||||
### Pattern: Subagent Definition
|
||||
|
||||
| SDK | Pattern | Key Fields |
|
||||
| ------------- | -------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| gemini-cli | `AgentDefinition` (local/remote) | name, description, kind, tools, model |
|
||||
| ADK-TS | `BaseAgentConfig` | name, description, subAgents, tools |
|
||||
| Claude SDK | `AgentDefinition` | description, prompt, tools, model |
|
||||
| OpenAI Agents | `Agent` class | name, instructions, tools, handoffs, model |
|
||||
| Our Outline | `AgentDescriptor` | name, description, executor, inputSchema, capabilities, ownTools, requiredTools, subAgents |
|
||||
|
||||
**Observation:** Our `AgentDescriptor` is the most complete. Claude's `prompt`
|
||||
field and OpenAI's `instructions` are executor-level concerns (system prompt),
|
||||
not descriptor-level. The descriptor declares identity; the executor uses the
|
||||
prompt. This separation is correct.
|
||||
|
||||
One gap: **handoffs**. OpenAI Agents has an explicit `handoffs` field listing
|
||||
which agents can be delegated to. Our `subAgents` field serves the same purpose
|
||||
but the naming implies hierarchy rather than peer delegation. Consider whether
|
||||
`subAgents` should be renamed to `delegateAgents` or kept as-is with
|
||||
documentation clarifying it covers both hierarchical and peer delegation.
|
||||
|
||||
---
|
||||
|
||||
## 8. Concrete Changes to outline.md
|
||||
|
||||
Based on this analysis, the following changes should be made:
|
||||
|
||||
### Applied (validated by multiple SDKs):
|
||||
|
||||
1. ✅ **`type: AgentEventType`** with known values + `(string & {})`
|
||||
(autocomplete + extensibility)
|
||||
2. ✅ **`interface AgentEvents` + mapped type** (adopted from Michael for
|
||||
declaration merging)
|
||||
3. ✅ **`agentId` on event base** (which agent emitted this event)
|
||||
4. ✅ **`_meta` on ContentPart** (aligned with Michael)
|
||||
5. ✅ **`stream_end` event** — signals why the stream ended, with `reason`
|
||||
field + open `data` bag
|
||||
6. ✅ **Handoff as tool call** — `transfer_to_agent` tool, not a separate event
|
||||
7. ✅ **`maxBudgetUsd` in AgentConstraints** (Claude SDK, increasingly standard)
|
||||
8. ✅ **`refusal` ContentPart type** (both Claude and OpenAI surface refusals)
|
||||
9. ✅ **`forkSession` in ExecutionRequest** (Claude SDK, valuable for
|
||||
exploration)
|
||||
10. ✅ **`permissionMode` in ExecutionOptions** (both gemini-cli and Claude SDK)
|
||||
11. ✅ **`cost` field on Usage** (Claude SDK tracks total_cost_usd)
|
||||
|
||||
### Correctly abstracted (no change needed):
|
||||
|
||||
- Event taxonomy (12 types) — validated as right abstraction level
|
||||
- `AgentDescriptor` shape — most complete across all SDKs
|
||||
- `AgentSession.stream/update/steer/abort` — covers all SDK patterns
|
||||
- ToolUpdate — correctly abstracts over OpenAI's 15+ tool-specific progress
|
||||
events
|
||||
- `ElicitationRequest/Response` — covers both callback and event patterns
|
||||
- `ContentPart` types — text/thought/media/reference/refusal
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
- [Claude Agent SDK TypeScript Reference](https://platform.claude.com/docs/en/agent-sdk/typescript)
|
||||
- [Claude Agent SDK Streaming](https://platform.claude.com/docs/en/agent-sdk/streaming-output)
|
||||
- [Claude Agent SDK Sessions](https://platform.claude.com/docs/en/agent-sdk/sessions)
|
||||
- [Claude Agent SDK Subagents](https://platform.claude.com/docs/en/agent-sdk/subagents)
|
||||
- [OpenAI Codex SDK TypeScript](https://github.com/openai/codex/tree/main/sdk/typescript)
|
||||
- [OpenAI Codex SDK Docs](https://developers.openai.com/codex/sdk/)
|
||||
- [OpenAI Responses API Streaming Events](https://developers.openai.com/api/reference/resources/responses/streaming-events/)
|
||||
- [OpenAI Agents SDK Streaming](https://openai.github.io/openai-agents-python/streaming/)
|
||||
- [Responses API Streaming Guide (Community)](https://community.openai.com/t/responses-api-streaming-the-simple-guide-to-events/1363122)
|
||||
@@ -0,0 +1,259 @@
|
||||
# Gemini CLI Architecture Notes
|
||||
|
||||
## Project Structure
|
||||
|
||||
**Monorepo packages:**
|
||||
|
||||
- `packages/core/` - Main execution engine (the big one)
|
||||
- `packages/cli/` - CLI frontend
|
||||
- `packages/sdk/` - SDK for extensions
|
||||
- `packages/a2a-server/` - Agent-to-agent server
|
||||
- `packages/devtools/` - Dev utilities
|
||||
- `packages/vscode-ide-companion/` - VS Code extension
|
||||
|
||||
## Core Execution Loop
|
||||
|
||||
### GeminiClient (`core/src/core/client.ts` ~38KB)
|
||||
|
||||
- **Primary orchestrator** for user interactions
|
||||
- Manages session lifecycle, message routing, model selection
|
||||
- Coordinates hooks, context management, error recovery
|
||||
- Enforces `MAX_TURNS = 100` per session
|
||||
- Tracks `currentSequenceModel` for multi-turn stickiness
|
||||
- Handles history compression when context grows
|
||||
|
||||
### GeminiChat (`core/src/core/geminiChat.ts` ~34KB)
|
||||
|
||||
- Bidirectional LLM communication
|
||||
- Maintains `history[]` alternating user/model turns
|
||||
- Retry logic: max 2 attempts, 500ms delay for invalid responses
|
||||
- Fires `BeforeModel` and `AfterModel` hooks
|
||||
- Integrates ChatRecordingService for persistence
|
||||
|
||||
### Scheduler (`core/src/scheduler/scheduler.ts` ~23KB)
|
||||
|
||||
- **Three-phase event-driven**: Ingestion → Processing → Completion
|
||||
- Tool call state machine:
|
||||
`Validating → AwaitingApproval → Scheduled → Executing → Terminal`
|
||||
- Terminal states: `Success`, `Error`, `Cancelled`
|
||||
- Parallel execution for read-only and agent-type tools
|
||||
- Yields to event loop for user approval
|
||||
- Publishes state changes via MessageBus
|
||||
|
||||
### CoreToolScheduler (`core/src/core/coreToolScheduler.ts` ~38KB)
|
||||
|
||||
- Sequential, queue-based tool processing
|
||||
- Validates policy via PolicyEngine
|
||||
- Confirmation handling via ToolModificationHandler (editor integration)
|
||||
- Uses MessageBus for async confirmation responses
|
||||
|
||||
## Tool System
|
||||
|
||||
### DeclarativeTool Pattern
|
||||
|
||||
- **Separation of concerns**: build() → validate → createInvocation() →
|
||||
execute()
|
||||
- `ToolBuilder` defines metadata (name, displayName, description, kind) + schema
|
||||
via `getSchema()`
|
||||
- `ToolInvocation` has: `getDescription()`, `toolLocations()`,
|
||||
`shouldConfirmExecute()`, `execute()`
|
||||
- `ToolResult` contains: `llmContent` (for LLM), `returnDisplay` (for UI), error
|
||||
details, tail calls
|
||||
|
||||
### BaseToolInvocation
|
||||
|
||||
- Abstract base with MessageBus integration for policy/confirmation
|
||||
- Three decision paths: ALLOW, DENY, ASK_USER via `getMessageBusDecision()`
|
||||
|
||||
### ToolRegistry (`core/src/tools/tool-registry.ts`)
|
||||
|
||||
- Registers tools via `registerTool()`
|
||||
- MCP tools with fully qualified names: `mcp_serverName_toolName`
|
||||
- Priority sorting: built-in → discovered → MCP (by server name)
|
||||
- Filters by active status based on configuration
|
||||
|
||||
### Confirmation System
|
||||
|
||||
- `ToolCallConfirmationDetails` union: edit, execute, MCP, info, ask_user,
|
||||
exit_plan_mode
|
||||
- `ToolConfirmationOutcome` enum: ProceedOnce, ProceedAlways, etc.
|
||||
- Async confirmation via MessageBus pub/sub
|
||||
|
||||
## Hooks System
|
||||
|
||||
### Hook Types (11 hook points)
|
||||
|
||||
| Hook | Trigger | Key Capability |
|
||||
| --------------------- | ----------------------- | --------------------------------- |
|
||||
| `BeforeTool` | Before tool execution | Modify tool_input |
|
||||
| `AfterTool` | After tool completion | Context injection, tail calls |
|
||||
| `BeforeAgent` | Before agent prompt | Additional context |
|
||||
| `AfterAgent` | After agent response | Clear context flag |
|
||||
| `BeforeModel` | Before LLM request | Modify request or inject response |
|
||||
| `AfterModel` | After LLM response | Modify response |
|
||||
| `BeforeToolSelection` | Before tool selection | Modify toolConfig |
|
||||
| `Notification` | When notifications fire | Suppress/modify message |
|
||||
| `SessionStart` | Session begins | Additional context |
|
||||
| `SessionEnd` | Session terminates | Cleanup |
|
||||
| `PreCompress` | Before compression | Suppress/modify |
|
||||
|
||||
### Hook Output Fields (common to all hooks)
|
||||
|
||||
- `continue` - Whether execution proceeds
|
||||
- `stopReason` - Reason to halt
|
||||
- `suppressOutput` - Hide from user
|
||||
- `systemMessage` - Add to system context
|
||||
- `decision` - ask/block/deny/approve/allow
|
||||
|
||||
### Hook System Components
|
||||
|
||||
- `HookSystem` - Main coordinator
|
||||
- `HookRegistry` - Stores/manages configurations
|
||||
- `HookRunner` - Executes registered hooks
|
||||
- `HookAggregator` - Combines multiple hook results
|
||||
- `HookPlanner` - Determines execution order
|
||||
- `HookEventHandler` - Orchestrates event firing
|
||||
- `HookTranslator` - Converts between formats
|
||||
|
||||
## Policy Engine
|
||||
|
||||
### Rule Structure
|
||||
|
||||
```
|
||||
PolicyRule {
|
||||
toolName: string; // wildcards supported
|
||||
decision: PolicyDecision; // ALLOW | DENY | ASK_USER
|
||||
priority: number;
|
||||
argsPattern?: RegExp; // conditional on args
|
||||
mcpName?: string;
|
||||
source: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Tier Hierarchy (lowest → highest priority)
|
||||
|
||||
1. Default (1) - Core built-in policies
|
||||
2. Extension (2) - Extension contributions
|
||||
3. Workspace (3) - Project-scoped (.gemini/)
|
||||
4. User (4) - User-provided (~/.gemini/)
|
||||
5. Admin (5) - System-level policies
|
||||
|
||||
### Dynamic Rule Priorities (within User Tier)
|
||||
|
||||
- 4.9 - MCP_EXCLUDED (persistent server blocks)
|
||||
- 4.4 - EXCLUDE_TOOLS_FLAG (CLI exclusions)
|
||||
- 4.3 - ALLOWED_TOOLS_FLAG (CLI allows)
|
||||
- 4.2 - TRUSTED_MCP_SERVER
|
||||
- 4.1 - ALLOWED_MCP_SERVER
|
||||
- 3.95 - ALWAYS_ALLOW (interactive selections)
|
||||
|
||||
### Security Constraint
|
||||
|
||||
- Extensions CANNOT contribute ALLOW rules or YOLO mode
|
||||
|
||||
## Agent System
|
||||
|
||||
### Agent Registry (`core/src/agents/registry.ts`)
|
||||
|
||||
Discovery sources:
|
||||
|
||||
1. Built-in: CodebaseInvestigator, CliHelp, Generalist, Browser
|
||||
2. User-level: `~/.gemini/agents/`
|
||||
3. Project-level: `.gemini/agents/` (requires folder trust)
|
||||
4. Extension-based: From active extensions
|
||||
|
||||
### LocalAgentExecutor (`core/src/agents/local-executor.ts`)
|
||||
|
||||
- Prompt processing: input augmentation → template expansion → system prompt
|
||||
construction
|
||||
- Uses GeminiChat for accumulating conversation
|
||||
- ChatCompressionService for history management
|
||||
- Turn loop: invoke model → extract function calls → check auth → append results
|
||||
- Termination: complete_task tool, max turns, timeout
|
||||
|
||||
### SubagentTool (`core/src/agents/subagent-tool.ts`)
|
||||
|
||||
- Extends BaseDeclarativeTool - agents invoked like standard tools
|
||||
- Read-only status checking, user hint propagation
|
||||
- Execution: validate → optional confirmation → parameter enrichment →
|
||||
SubagentToolWrapper
|
||||
|
||||
### Remote Agents
|
||||
|
||||
- A2A client manager for agent-to-agent protocol
|
||||
- Remote invocation for external agents
|
||||
- Agent acknowledgement system (security for project agents)
|
||||
|
||||
## Model System
|
||||
|
||||
### ModelConfigService
|
||||
|
||||
- **Hierarchical alias system**: children override parents
|
||||
- Resolution: alias chain → level assignment → apply overrides
|
||||
- Deep merging with array override capability
|
||||
- Fallback to `chat-base` alias for unknown models
|
||||
|
||||
### ModelRouterService
|
||||
|
||||
Sequential strategy pattern:
|
||||
|
||||
1. Fallback & Override
|
||||
2. Approval Mode Strategy
|
||||
3. Gemma Classifier (if enabled)
|
||||
4. Generic Classifier
|
||||
5. Numerical Classifier
|
||||
6. Default Strategy
|
||||
|
||||
### ModelAvailabilityService
|
||||
|
||||
Health states:
|
||||
|
||||
- **Terminal** - permanently unavailable
|
||||
- **Sticky Retry** - failed once, can retry once per turn
|
||||
- **Healthy** - no issues
|
||||
|
||||
## Services
|
||||
|
||||
| Service | Purpose |
|
||||
| --------------------------- | --------------------------------------- |
|
||||
| ChatRecordingService | Session persistence (JSON files) |
|
||||
| ChatCompressionService | History summarization for token budgets |
|
||||
| ModelConfigService | Hierarchical model config with aliases |
|
||||
| ModelAvailabilityService | Model health tracking |
|
||||
| ModelRouterService | Model selection via strategies |
|
||||
| FolderTrustDiscoveryService | Workspace security scanning |
|
||||
| KeychainService | Credential storage |
|
||||
| LoopDetectionService | Detect repetitive agent loops |
|
||||
|
||||
## UI + Core Separation
|
||||
|
||||
### IDE Client (`core/src/ide/ide-client.ts`)
|
||||
|
||||
- Singleton managing CLI ↔ IDE communication via MCP
|
||||
- **Outbound** (CLI → IDE): `openDiff`, `closeDiff`
|
||||
- **Inbound** (IDE → CLI): `ide/contextUpdate`, `ide/diffAccepted`,
|
||||
`ide/diffRejected`
|
||||
|
||||
### Event Contract
|
||||
|
||||
```typescript
|
||||
interface IdeContextNotification {
|
||||
method: 'ide/contextUpdate';
|
||||
params: { workspaceState: { openFiles: string[]; isTrusted: boolean } };
|
||||
}
|
||||
```
|
||||
|
||||
### Confirmation Bus
|
||||
|
||||
- `TOOL_CONFIRMATION_REQUEST` / `TOOL_CONFIRMATION_RESPONSE`
|
||||
- Detail types: edit, execute, MCP, info, ask_user, exit_plan_mode
|
||||
- Async pub/sub via MessageBus
|
||||
|
||||
## Configuration (`core/src/config/config.ts` ~95KB!)
|
||||
|
||||
- Tool config: core tools, allowed/excluded, MCP servers
|
||||
- File filtering: git ignore, fuzzy search, max counts, timeouts
|
||||
- Approval modes: policy engine config
|
||||
- Experiments: feature flags (GEMINI_3_1_PRO_LAUNCHED, ENABLE_ADMIN_CONTROLS,
|
||||
etc.)
|
||||
- FolderTrust: discovery scans for commands, skills, settings, MCP, hooks
|
||||
@@ -0,0 +1,296 @@
|
||||
# Deep Dive: Key Gemini-CLI Systems
|
||||
|
||||
## Hooks System (Complete)
|
||||
|
||||
### 11 Hook Points
|
||||
|
||||
| Hook | Input | Key Output Capabilities |
|
||||
| ------------------- | -------------------------------------- | --------------------------------------------- |
|
||||
| BeforeTool | toolName, toolInput, mcpContext | Modify tool_input, block/allow, systemMessage |
|
||||
| AfterTool | toolName, toolInput, toolResponse | additionalContext, tailToolCallRequest |
|
||||
| BeforeAgent | prompt | Additional context |
|
||||
| AfterAgent | prompt, response, stopHookActive | Clear context |
|
||||
| BeforeModel | llmRequest (GenerateContentParameters) | Modify llm_request OR inject llm_response |
|
||||
| AfterModel | llmRequest, llmResponse | Modify llm_response |
|
||||
| BeforeToolSelection | llmRequest | Modify toolConfig (function list, mode) |
|
||||
| Notification | type, message, details | Suppress/modify |
|
||||
| SessionStart | source (Startup/Resume/Clear) | Additional context |
|
||||
| SessionEnd | reason (Exit/Clear/Logout/etc) | Cleanup |
|
||||
| PreCompress | trigger (Manual/Auto) | Suppress/modify |
|
||||
|
||||
### Hook Configuration Types
|
||||
|
||||
- **Runtime hooks** (HookType.Runtime): JS/TS functions, registered
|
||||
programmatically
|
||||
- **Command hooks** (HookType.Command): External shell commands with JSON I/O
|
||||
|
||||
### Exit Code Semantics (Command Hooks)
|
||||
|
||||
- 0 = Success (allowed with system message)
|
||||
- 1 = Non-blocking error (warning, continues)
|
||||
- 2+ = Blocking failure (denied, stderr as reason)
|
||||
|
||||
### Hook Decision Values
|
||||
|
||||
`'ask' | 'block' | 'deny' | 'approve' | 'allow' | undefined`
|
||||
|
||||
### Execution Strategies
|
||||
|
||||
- **Parallel** (default): Promise.all(), independent
|
||||
- **Sequential** (opt-in per hook): Chained, output→input cascading
|
||||
|
||||
### Aggregation
|
||||
|
||||
- Blocking decisions: OR logic (any block → all block)
|
||||
- Field replacement: later overrides earlier
|
||||
- Tool selection: union of allowed functions, mode precedence NONE > ANY > AUTO
|
||||
|
||||
### Trust Model
|
||||
|
||||
- Project hooks require folder trust verification
|
||||
- TrustedHooksManager at `~/.gemini/trusted-hooks.json`
|
||||
- Environment sanitized for command hooks (sensitive vars removed)
|
||||
- `GEMINI_PROJECT_DIR` injected
|
||||
|
||||
### Key Insight for Abstraction
|
||||
|
||||
Hooks fire inside gemini-cli's execution loop. When ADK controls the model:
|
||||
|
||||
- BeforeModel/AfterModel still fire because AdkGeminiModel wraps GeminiChat
|
||||
- BeforeTool/AfterTool still fire because AdkToolAdapter wraps DeclarativeTool
|
||||
- This is dewitt's solution: adapters preserve hook injection points
|
||||
|
||||
**For OpenRouter or opaque agents, hooks CANNOT fire unless the agent delegates
|
||||
model/tool calls back to gemini-cli.**
|
||||
|
||||
---
|
||||
|
||||
## Policy Engine (Complete)
|
||||
|
||||
### TOML Rule Format
|
||||
|
||||
```toml
|
||||
[[rules]]
|
||||
decision = "allow" | "deny" | "ask_user"
|
||||
priority = 0-999
|
||||
toolName = "tool_name" # wildcards: *, mcp_*, mcp_server_*
|
||||
mcpName = "server_name" # MCP server filter
|
||||
argsPattern = "regex" # matches JSON-stringified args
|
||||
commandPrefix = "cmd" # shell command prefix match
|
||||
commandRegex = "regex" # shell command regex (mutually exclusive with prefix)
|
||||
modes = ["default", "autoEdit", "yolo", "plan"]
|
||||
annotations = ["read-only", "experimental"]
|
||||
allowRedirection = true # for shell commands
|
||||
allowMessage = "..." # user-facing message on allow
|
||||
denyMessage = "..." # user-facing message on deny
|
||||
```
|
||||
|
||||
### 5-Tier Priority System
|
||||
|
||||
- Tier 5 (Admin): 5.000-5.999
|
||||
- Tier 4 (User): 4.000-4.999
|
||||
- Tier 3 (Workspace): 3.000-3.999
|
||||
- Tier 2 (Extension): 2.000-2.999
|
||||
- Tier 1 (Default): 1.000-1.999
|
||||
|
||||
Formula: `tier + (priority / 1000)`
|
||||
|
||||
### 4 Approval Modes
|
||||
|
||||
1. **default** — ASK_USER decisions prompt user
|
||||
2. **autoEdit** — File writes auto-approved with safety checking (conseca)
|
||||
3. **yolo** — All auto-approved except explicit ask_user rules
|
||||
4. **plan** — Read-only, blocks modifications, allows planning docs
|
||||
|
||||
### Shell Command Safety
|
||||
|
||||
- Parses multi-command sequences (&&, ;, ||)
|
||||
- Detects injection: $(...), `...`, <(...), >(...), --flag=$(...)
|
||||
- Each subcommand evaluated independently
|
||||
- DENY overrides everything; ASK_USER escalates; ALLOW only if all pass
|
||||
- Redirections (>) downgrade ALLOW → ASK_USER unless allowRedirection=true
|
||||
|
||||
### Security Constraints
|
||||
|
||||
- Extensions cannot contribute ALLOW rules or YOLO mode
|
||||
- Regex patterns validated for ReDoS
|
||||
- Tool name typos detected via Levenshtein distance ≤3
|
||||
- Policy file integrity: SHA-256 hash checking
|
||||
|
||||
### Key Insight for Abstraction
|
||||
|
||||
Policy is evaluated at the tool execution boundary. For the interface layer:
|
||||
|
||||
- If CLI controls tool execution → policy naturally applies
|
||||
- If agent controls tool execution internally → policy bypassed (danger!)
|
||||
- This reinforces the `pauseOnToolCalls: true` approach for ADK
|
||||
- Need a `PolicyEvaluator` interface that any executor can call
|
||||
|
||||
---
|
||||
|
||||
## Tool System (Complete)
|
||||
|
||||
### Core Abstraction Chain
|
||||
|
||||
```
|
||||
ToolBuilder (metadata + schema)
|
||||
→ build(params) validates → ToolInvocation (ready to execute)
|
||||
→ shouldConfirmExecute() → execute(signal) → ToolResult
|
||||
```
|
||||
|
||||
### DeclarativeTool Pattern
|
||||
|
||||
- `build(params)` — Validate and create invocation
|
||||
- `buildAndExecute(params)` — One-step convenience
|
||||
- `validateBuildAndExecute(params)` — Non-throwing variant
|
||||
|
||||
### BaseToolInvocation
|
||||
|
||||
- Message bus integration for policy decisions
|
||||
- Three decision paths: ALLOW → execute, DENY → reject, ASK_USER → confirm
|
||||
|
||||
### ToolResult Structure
|
||||
|
||||
- `llmContent` — For LLM conversation history
|
||||
- `returnDisplay` — For UI presentation
|
||||
- `displayContent` — Additional display formatting
|
||||
- `errorDetails` — Optional error info
|
||||
- `result` — Structured data payload
|
||||
- `tailCall` — Optional chaining requests
|
||||
|
||||
### Confirmation System (6 types)
|
||||
|
||||
1. **edit** — File modification with diff
|
||||
2. **execute** — Command execution
|
||||
3. **mcp** — MCP tool with allowlist mgmt
|
||||
4. **info** — Information-only
|
||||
5. **ask_user** — General user approval
|
||||
6. **exit_plan_mode** — Plan exit notification
|
||||
|
||||
### Confirmation Outcomes (7 values)
|
||||
|
||||
ProceedOnce, ProceedAlways, ProceedAlwaysAndSave, ProceedAlwaysServer,
|
||||
ProceedAlwaysTool, ModifyWithEditor, Cancel
|
||||
|
||||
### Tool Kinds
|
||||
|
||||
- **Mutator**: Edit, Delete, Move, Execute
|
||||
- **Read-Only**: Read, Search, Fetch
|
||||
- **Other**: Think, Agent, Communicate, Plan, SwitchMode, Other
|
||||
|
||||
### MCP Tools
|
||||
|
||||
- Naming: `mcp_<server>_<toolname>` (64-char limit)
|
||||
- Schema validation via LenientJsonSchemaValidator
|
||||
- Response types: McpTextBlock, McpMediaBlock, McpResourceBlock,
|
||||
McpResourceLinkBlock
|
||||
- Transform to GenAI Parts format
|
||||
|
||||
### Error Types (20+)
|
||||
|
||||
- **Recoverable**: INVALID_TOOL_PARAMS, FILE_NOT_FOUND,
|
||||
EDIT_NO_OCCURRENCE_FOUND, SHELL_TIMEOUT, MCP_TOOL_ERROR...
|
||||
- **Fatal**: NO_SPACE_LEFT (only one!)
|
||||
|
||||
### ModifiableTool
|
||||
|
||||
- Extends DeclarativeTool with external editor support
|
||||
- `getModifyContext()` → temp files → editor opens → `getUpdatedParams()` → diff
|
||||
|
||||
---
|
||||
|
||||
## Execution Loop (Complete)
|
||||
|
||||
### LocalAgentExecutor Flow
|
||||
|
||||
1. Collect user hints, setup deadline timer
|
||||
2. **Turn loop**: executeTurn() repeatedly until completion
|
||||
3. Per-turn: compress chat → callModel() → processFunctionCalls()
|
||||
4. On limit hit: executeFinalWarningTurn() with 60s grace period
|
||||
5. Return OutputObject { result, terminate_reason }
|
||||
|
||||
### AgentTerminateMode
|
||||
|
||||
GOAL | TIMEOUT | MAX_TURNS | ABORTED | ERROR | ERROR_NO_COMPLETE_TASK_CALL
|
||||
|
||||
### SubagentTool Architecture
|
||||
|
||||
```
|
||||
Parent Agent
|
||||
└─ SubagentTool (wraps AgentDefinition as DeclarativeTool)
|
||||
└─ SubagentToolWrapper (routes by agent kind)
|
||||
├─ LocalSubagentInvocation → LocalAgentExecutor
|
||||
├─ RemoteAgentInvocation → A2AClientManager
|
||||
└─ BrowserAgentInvocation
|
||||
```
|
||||
|
||||
### Agent Types
|
||||
|
||||
- `LocalAgentDefinition` — kind: 'local', has promptConfig, modelConfig,
|
||||
runConfig, toolConfig
|
||||
- `RemoteAgentDefinition` — kind: 'remote', has agentCardUrl, auth config
|
||||
|
||||
### Key Defaults
|
||||
|
||||
- DEFAULT_MAX_TURNS = 15
|
||||
- DEFAULT_MAX_TIME_MINUTES = 5
|
||||
- A2A_TIMEOUT = 1800000 (30 min for remote agents)
|
||||
|
||||
---
|
||||
|
||||
## Services/Config (Complete)
|
||||
|
||||
### ModelConfigService
|
||||
|
||||
- **Alias chains**: Inheritance with `extends`, merged root-to-leaf
|
||||
- **Overrides**: Contextual (model, scope, retry, isChatModel), sorted by
|
||||
specificity
|
||||
- **Runtime registration**: Dynamic aliases and overrides
|
||||
- **Deep merge**: Objects merged, arrays replaced entirely
|
||||
|
||||
### ModelRouterService (Strategy Chain)
|
||||
|
||||
1. Fallback & Override → 2. Approval Mode → 3. Gemma Classifier → 4. Generic
|
||||
Classifier → 5. Numerical Classifier → 6. Default
|
||||
|
||||
### ModelAvailabilityService
|
||||
|
||||
- Terminal (permanent), Sticky_retry (one retry per turn), Healthy
|
||||
- `selectFirstAvailable()` iterates fallback chain
|
||||
- `resetTurn()` at turn boundaries enables fresh retries
|
||||
|
||||
### Config (~95KB!)
|
||||
|
||||
Central dependency injection. Initializes: ModelAvailabilityService →
|
||||
ModelConfigService → FolderTrustDiscoveryService → PolicyEngine →
|
||||
FileDiscoveryService → GitService → ToolRegistry → MCP → GeminiClient →
|
||||
HookSystem
|
||||
|
||||
### CoreEventEmitter (UI Events)
|
||||
|
||||
Event types: UserFeedback, ModelChanged, ConsoleLog, Output, RetryAttempt,
|
||||
ConsentRequest, McpProgress, Hook, QuotaChanged
|
||||
|
||||
Backlog buffering (max 10,000) with head-pointer eviction and auto-compaction.
|
||||
|
||||
### Scheduler Types
|
||||
|
||||
```typescript
|
||||
ToolCallRequestInfo {
|
||||
callId, name, args, originalRequestName,
|
||||
isClientInitiated, prompt_id, checkpoint, traceId,
|
||||
parentCallId, schedulerId
|
||||
}
|
||||
ToolCallResponseInfo {
|
||||
callId, responseParts, resultDisplay, error, errorType,
|
||||
outputFile, contentLength, data
|
||||
}
|
||||
CoreToolCallStatus: Validating → AwaitingApproval → Scheduled → Executing → Success|Error|Cancelled
|
||||
```
|
||||
|
||||
### FolderTrust
|
||||
|
||||
Scans: commands (.toml), skills (SKILL.md), settings.json, MCP servers, hooks
|
||||
Security warnings: auto-approved tools, autonomous agents, disabled trust,
|
||||
disabled sandbox Pattern: discovery → review → execution (no code runs during
|
||||
scan)
|
||||
@@ -0,0 +1,349 @@
|
||||
# Interface Priority Analysis & Open Questions
|
||||
|
||||
## The Big Picture
|
||||
|
||||
We're defining **framework-agnostic interfaces** that allow gemini-cli to:
|
||||
|
||||
1. Keep its existing execution loop working unchanged (Legacy path)
|
||||
2. Swap in ADK as an alternative runtime via config flag
|
||||
3. Eventually support OpenRouter or other agent backends
|
||||
4. Maintain all existing CLI behavior: hooks, policies, confirmations, UI events
|
||||
|
||||
## Proposed Interface Layers (Priority Order)
|
||||
|
||||
---
|
||||
|
||||
### P0 (Critical Path - Must Define First)
|
||||
|
||||
#### 1. AgentEvent / Event Stream Contract
|
||||
|
||||
**Why first:** Everything else consumes or produces these events. The UI renders
|
||||
them. The hooks intercept them. The adapters translate to/from them.
|
||||
|
||||
**Key decision:** Merge Dewitt's simpler model with Coworker's richer model?
|
||||
|
||||
**Recommendation:** Coworker's approach is more complete. Key additions:
|
||||
|
||||
- `threadId` for sub-agent tracking (AG-UI has `parentRunId`)
|
||||
- `tool_update` for progress on long-running tools
|
||||
- `elicitation_request/response` as first-class (not just tool_confirmation)
|
||||
- `usage` event for token tracking
|
||||
- `_meta` escape hatch (matches AG-UI's extensibility philosophy)
|
||||
- `initialize` event (matches AG-UI's RunStarted)
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- Do we need AG-UI's start/content/end triple pattern for streaming? Or is
|
||||
yielding partial events sufficient?
|
||||
- How do ContentPart types map to existing gemini-cli Part types?
|
||||
- Should events carry a `source` field? (useful for hook attribution)
|
||||
|
||||
#### 2. Agent Interface
|
||||
|
||||
**Why second:** This is the primary abstraction that LocalAgentExecutor, ADK
|
||||
adapters, and future OpenRouter adapters all implement.
|
||||
|
||||
**Key decision:** Dewitt's `runAsync/runEphemeral` vs Coworker's
|
||||
`send(Trajectory|string)`
|
||||
|
||||
**Recommendation:** Hybrid approach:
|
||||
|
||||
- Dewitt's `runAsync/runEphemeral` split is ADK-aligned and cleaner for the
|
||||
factory pattern
|
||||
- BUT add Coworker's elicitation support via AgentSend union type
|
||||
- The Trajectory concept is powerful but may be too opinionated for Phase 2
|
||||
|
||||
```
|
||||
Agent<TInput, TOutput>
|
||||
name: string
|
||||
description: string
|
||||
runAsync(input, options) → AsyncGenerator<AgentEvent, TOutput>
|
||||
runEphemeral(input, options) → AsyncGenerator<AgentEvent, TOutput>
|
||||
```
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- Should Agent also support `send()` for mid-stream interactions (elicitations)?
|
||||
- How does AbortSignal propagate through the adapter boundary?
|
||||
- Do we need a `capabilities` field (supports elicitation? supports HITL? etc.)?
|
||||
|
||||
#### 3. Tool Execution Contract
|
||||
|
||||
**Why third:** Tools are the primary action mechanism. Both the policy engine
|
||||
and hooks system wrap tool execution.
|
||||
|
||||
**What needs abstracting:**
|
||||
|
||||
- Tool declaration (name, schema) — already somewhat generic via JSON Schema
|
||||
- Tool execution (args → result)
|
||||
- Tool confirmation flow (ASK_USER → user decision → proceed/deny)
|
||||
- Tool result shape (llmContent + displayContent + error + tailCalls)
|
||||
|
||||
**Key decision:** Keep DeclarativeTool pattern or flatten to a simpler
|
||||
interface?
|
||||
|
||||
**Recommendation:** Define a minimal `ToolExecutor` interface:
|
||||
|
||||
```
|
||||
ToolExecutor {
|
||||
name: string
|
||||
description: string
|
||||
schema: JSONSchema
|
||||
execute(args, context): Promise<ToolResult>
|
||||
requiresConfirmation?(args, context): Promise<boolean>
|
||||
}
|
||||
```
|
||||
|
||||
DeclarativeTool remains the concrete implementation. ADK's BaseTool adapts to
|
||||
this.
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- How do MCP tools fit? They already have their own protocol.
|
||||
- Tool annotations (destructive hints) — should these be in the interface?
|
||||
- Long-running tools need progress reporting — how does this interact with
|
||||
tool_update events?
|
||||
|
||||
---
|
||||
|
||||
### P1 (Important - Define After P0)
|
||||
|
||||
#### 4. Policy / Permission Interface
|
||||
|
||||
**Why important:** Every tool call goes through policy. External agents need
|
||||
policy enforcement too.
|
||||
|
||||
**Current state:** gemini-cli has a sophisticated TOML-based policy engine with
|
||||
tiered priorities. ADK-TS has a simpler SecurityPlugin with PolicyOutcome
|
||||
(DENY/CONFIRM/ALLOW).
|
||||
|
||||
**What needs abstracting:**
|
||||
|
||||
```
|
||||
PolicyEngine {
|
||||
evaluate(toolName, args, context): PolicyDecision // ALLOW | DENY | ASK_USER
|
||||
getExcludedTools(): string[] // Tools statically denied
|
||||
}
|
||||
```
|
||||
|
||||
**Key decision:** Do external agents (OpenRouter, etc.) get the same policy
|
||||
enforcement?
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- If an ADK agent calls a tool internally, does gemini-cli's policy apply?
|
||||
- With `pauseOnToolCalls: true` in ADK, the CLI controls execution — but what
|
||||
about headless mode?
|
||||
- How do agent-level policies work? (allow/deny entire agents, not just tools)
|
||||
- Should policy be a middleware (AG-UI pattern) or a callback (ADK plugin
|
||||
pattern)?
|
||||
|
||||
#### 5. Hooks Interface
|
||||
|
||||
**Why important:** Hooks are a major gemini-cli feature. They need to work
|
||||
regardless of which agent backend runs.
|
||||
|
||||
**Current state:** 11 hook types firing at specific lifecycle points.
|
||||
|
||||
**What needs abstracting:**
|
||||
|
||||
- Hook lifecycle must be backend-agnostic
|
||||
- BeforeModel/AfterModel hooks need to work even when ADK controls the model
|
||||
- BeforeTool/AfterTool hooks need to intercept regardless of who executes the
|
||||
tool
|
||||
|
||||
**Key challenge:** When ADK runs the model internally, gemini-cli hooks can't
|
||||
easily intercept. **Dewitt's solution:** ADK uses gemini-cli's model via
|
||||
AdkGeminiModel adapter — hooks fire inside GeminiChat.
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- If OpenRouter runs the model, how do BeforeModel/AfterModel hooks work?
|
||||
- Do we need a "model steering" abstraction (injecting context mid-stream)?
|
||||
- Can hooks be expressed as AG-UI middleware? (intercept event stream)
|
||||
|
||||
#### 6. Model / LLM Interface
|
||||
|
||||
**Why important:** Model abstraction enables swapping LLM providers.
|
||||
|
||||
**Dewitt's approach:** Exposes Model interface, ADK uses it via AdkGeminiModel
|
||||
adapter. **Coworker's approach:** Model is internal to Agent (no separate Model
|
||||
interface).
|
||||
|
||||
**Recommendation:** Keep Dewitt's separate Model interface BUT make it
|
||||
provider-agnostic:
|
||||
|
||||
- Remove `@google/genai` types from the interface signature
|
||||
- Define generic Message/Content types
|
||||
- Model interface is an implementation detail, not part of the Agent contract
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- Can we define a truly provider-agnostic Model interface?
|
||||
- Or is the Model always tied to the agent backend? (ADK uses Gemini, OpenRouter
|
||||
uses whatever)
|
||||
- Model routing (choosing which model) — is this a concern of the Model
|
||||
interface or a separate service?
|
||||
|
||||
---
|
||||
|
||||
### P2 (Important but Can Follow)
|
||||
|
||||
#### 7. Session / State Interface
|
||||
|
||||
**Current state:** gemini-cli uses ChatRecordingService (JSON files). ADK uses
|
||||
Session with BaseSessionService.
|
||||
|
||||
**What needs abstracting:**
|
||||
|
||||
- Session creation/retrieval
|
||||
- State persistence across turns
|
||||
- History/trajectory management
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- Does the trajectory (coworker's concept) replace gemini-cli's chat recording?
|
||||
- Should session state be shared between gemini-cli and the agent backend?
|
||||
|
||||
#### 8. Elicitation / User Interaction Interface
|
||||
|
||||
**What it covers:** Model fallback dialogs, tool confirmations, Ctrl+B
|
||||
interrupts, user questions
|
||||
|
||||
**Current state:** gemini-cli uses ConfirmationBus + MessageBus. AG-UI uses
|
||||
frontend tools.
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- Is elicitation just a special case of tool calls (AG-UI approach)?
|
||||
- Or is it a first-class event type (coworker's approach)?
|
||||
- How does Ctrl+B (cancel/interrupt) propagate through the agent boundary?
|
||||
|
||||
#### 9. Configuration / Capability Discovery
|
||||
|
||||
**What it covers:** Feature flags, experiment settings, agent capabilities
|
||||
|
||||
**Open questions:**
|
||||
|
||||
- How does an external agent declare its capabilities?
|
||||
- Does OpenRouter support HITL? Elicitation? Tool confirmation? Each agent may
|
||||
differ.
|
||||
- Need a `capabilities` negotiation at connection time?
|
||||
|
||||
---
|
||||
|
||||
### P3 (Future / Can Defer)
|
||||
|
||||
#### 10. A2UI / Rich UI Interface
|
||||
|
||||
- Declarative UI generation from agents
|
||||
- Not critical for Phase 2 but important for differentiation
|
||||
|
||||
#### 11. Memory / Artifact Interface
|
||||
|
||||
- ADK has memory/artifact services
|
||||
- gemini-cli has ChatRecordingService + memory tools
|
||||
- Can standardize later
|
||||
|
||||
#### 12. Telemetry / Observability Interface
|
||||
|
||||
- Both systems have telemetry
|
||||
- Can standardize later
|
||||
|
||||
---
|
||||
|
||||
## Critical Open Questions (Need Team Discussion)
|
||||
|
||||
### 1. OpenRouter Integration Model
|
||||
|
||||
**Question:** When OpenRouter (or any external agent) is used, what does the
|
||||
integration look like?
|
||||
|
||||
**Option A: Full Agent Interface** — OpenRouter implements the Agent interface
|
||||
directly
|
||||
|
||||
- Pro: Clean, uniform
|
||||
- Con: OpenRouter doesn't support HITL, hooks, policies natively
|
||||
|
||||
**Option B: ACP Shim** — Agent Communication Protocol between CLI and external
|
||||
agents
|
||||
|
||||
- Pro: Standards-based
|
||||
- Con: Additional protocol layer, may be premature
|
||||
|
||||
**Option C: Model-only Integration** — OpenRouter is just an alternative Model,
|
||||
not Agent
|
||||
|
||||
- Pro: Simpler, leverages existing agent loop
|
||||
- Con: Doesn't support OpenRouter-specific features
|
||||
|
||||
**Recommendation:** Start with Option C (model-only). OpenRouter provides an LLM
|
||||
endpoint. Gemini-cli's own agent loop handles tools, policies, hooks. This means
|
||||
defining a provider-agnostic Model interface is the key enabler.
|
||||
|
||||
### 2. Tool Execution: Client-side vs Agent-side
|
||||
|
||||
**Question:** Who executes tools — the CLI or the agent backend?
|
||||
|
||||
**Option A: Always client-side** (CLI executes, agent suspends)
|
||||
|
||||
- ADK: `pauseOnToolCalls: true`
|
||||
- Pro: CLI maintains control, policies enforced, hooks fire
|
||||
- Con: Higher latency, more round-trips
|
||||
|
||||
**Option B: Agent-side execution** (agent runs tools internally)
|
||||
|
||||
- Pro: Faster, simpler
|
||||
- Con: Bypasses CLI policies, hooks, confirmations
|
||||
|
||||
**Option C: Configurable** — CLI decides per-tool or per-agent
|
||||
|
||||
- Pro: Flexible
|
||||
- Con: Complex
|
||||
|
||||
**Recommendation:** Option A for safety-critical CLI use case. Option B only for
|
||||
trusted/sandboxed sub-agents.
|
||||
|
||||
### 3. Model Steering (Hooks that inject context mid-stream)
|
||||
|
||||
**Question:** How do user-local hooks (like injecting project context) work with
|
||||
external agents?
|
||||
|
||||
**Answer:** They can only work if:
|
||||
|
||||
- The CLI controls the model (via Model interface adapter) — then BeforeModel
|
||||
hook injects context
|
||||
- OR the agent supports a "system instruction update" mechanism
|
||||
|
||||
For OpenRouter: model steering works because CLI controls the model call. For
|
||||
ADK: model steering works because AdkGeminiModel wraps GeminiChat. For fully
|
||||
opaque agents: model steering **cannot work** — this is a known limitation.
|
||||
|
||||
### 4. Elicitation Flow
|
||||
|
||||
**Question:** When the agent needs user input (model fallback, clarification),
|
||||
how does it work?
|
||||
|
||||
**For CLI-controlled agents:** Agent yields an elicitation_request event → CLI
|
||||
renders prompt → user responds → CLI sends response back via session.stream({
|
||||
kind: 'elicitation_response', ... }) to resume
|
||||
|
||||
**For external agents:** Agent uses A2A protocol or similar to send elicitation
|
||||
→ CLI bridges the request to user → response sent back via protocol
|
||||
|
||||
**Key insight:** Elicitation is fundamentally about the agent SUSPENDING and
|
||||
waiting for user input. ADK already supports this via `pauseOnToolCalls`. Can we
|
||||
generalize to `pauseOnElicitation`?
|
||||
|
||||
### 5. Sub-agent Identity and Policies
|
||||
|
||||
**Question:** When a sub-agent spawns, does it inherit parent policies? Get its
|
||||
own?
|
||||
|
||||
**Current gemini-cli behavior:** Sub-agents registered as tools, go through same
|
||||
policy engine. **ADK behavior:** Sub-agents are child nodes in agent tree, get
|
||||
parent's plugins.
|
||||
|
||||
**Recommendation:** Sub-agents inherit parent policy context. Additional
|
||||
restrictions can be layered (e.g., sub-agent X cannot use shell tool). This is
|
||||
already how gemini-cli works.
|
||||
@@ -0,0 +1,438 @@
|
||||
# Architectural Design: Gemini CLI to ADK Migration
|
||||
|
||||
| Authors: [Adam Weidman](mailto:adamfweidman@google.com) Contributors: Reviewers: *See section [Status of this document](#status-of-this-document).* | Status: Draft Last revised: Apr 7, 2026 Visibility: Confidential |
|
||||
| :--- | :--- |
|
||||
|
||||
---
|
||||
|
||||
# Goal
|
||||
|
||||
To migrate the Gemini CLI backend execution engine from its legacy fragmented loop structure to the Agent Development Kit (ADK). This migration will unify how agents and subagents are orchestrated, simplify state persistence, and expose a standard `AgentSession` interface for the CLI, future SDK surfaces, and subagent execution.
|
||||
|
||||
---
|
||||
|
||||
# Context
|
||||
|
||||
Over time, Gemini CLI has accumulated complex runtime behaviors: multi-tier tool scheduling, policy-driven approvals, payload masking, dynamic routing, and fine-grained telemetry. Integrating these with ADK requires a clean boundary that preserves Gemini CLI semantics without forking ADK core behavior.
|
||||
|
||||
The key migration boundary is:
|
||||
|
||||
- ADK runtime semantics -> Gemini CLI `AgentProtocol` / `AgentSession`
|
||||
- ADK `Event` stream -> Gemini CLI `AgentEvent` stream
|
||||
|
||||
That boundary, not the model wrapper alone, is the architectural center of this design.
|
||||
|
||||
---
|
||||
|
||||
# Current State and Proposed Mappings
|
||||
|
||||
The following analysis maps existing Gemini CLI components onto ADK capabilities, citing both repositories (`gemini-cli` and `adk-js`).
|
||||
|
||||
## Core Runtime Architecture
|
||||
|
||||
The migration uses one shared ADK-backed runtime core. Every orchestrated agent, including subagents, is exposed through the same external session contract:
|
||||
|
||||
- **Top-level CLI agent** -> `AgentSession`
|
||||
- **Future SDK entry point** -> `AgentSession`
|
||||
- **Subagent execution** -> `AgentSession`
|
||||
|
||||
The runtime owns:
|
||||
|
||||
- ADK runner/session lifecycle
|
||||
- tool execution
|
||||
- policy integration
|
||||
- routing, availability, compaction, and masking hooks
|
||||
- persistence integration
|
||||
|
||||
The adapters own:
|
||||
|
||||
- `streamId` timing guarantees
|
||||
- replay / reattach behavior
|
||||
- translation from ADK `Event` to Gemini CLI `AgentEvent`
|
||||
- top-level versus subagent event projection
|
||||
- projection of a child `AgentSession` into parent-facing tool or thread events when a subagent is embedded inside another agent
|
||||
|
||||
The minimum shape is:
|
||||
|
||||
```typescript
|
||||
interface PipelineServices {
|
||||
run(
|
||||
request: LlmRequest,
|
||||
baseModel: BaseLlm,
|
||||
stream: boolean,
|
||||
): AsyncGenerator<LlmResponse, void>;
|
||||
connect(
|
||||
request: LlmRequest,
|
||||
baseModel: BaseLlm,
|
||||
): Promise<BaseLlmConnection>;
|
||||
}
|
||||
|
||||
class GcliAgentModel extends BaseLlm {
|
||||
constructor(
|
||||
private baseModel: BaseLlm,
|
||||
private pipeline: PipelineServices,
|
||||
) {
|
||||
super({model: 'gcli-consolidated'});
|
||||
}
|
||||
|
||||
async *generateContentAsync(
|
||||
request: LlmRequest,
|
||||
stream = false,
|
||||
): AsyncGenerator<LlmResponse, void> {
|
||||
yield* this.pipeline.run(request, this.baseModel, stream);
|
||||
}
|
||||
|
||||
async connect(request: LlmRequest): Promise<BaseLlmConnection> {
|
||||
return this.pipeline.connect(request, this.baseModel);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Design rule:
|
||||
|
||||
- request mutation stays in the pipeline
|
||||
- all orchestrated agents expose the same `AgentSession` contract
|
||||
- session lifecycle, replay, approvals, and subagent projection stay in the runtime/adapters
|
||||
|
||||
`AdkAgentService` is the composition root for this architecture. It creates and resumes both top-level and subagent `AgentSession`s, builds scoped registries and message-bus instances, wires policy and approval bridges, and embeds child sessions through projection adapters rather than through a separate subagent runtime. See Appendix A for the intended initialization shape.
|
||||
|
||||
Composition rule:
|
||||
|
||||
- stateful tools are cloned or reinstantiated per session when needed; stateless tools may be shared
|
||||
- MCP discovery is shared at the manager layer but registered into session-local tool, prompt, and resource registries
|
||||
- `AgentLoopContext` is decomposed into pipeline config, tool/subagent config, session services, callback bridges, and UI projection rather than passed through as a single runtime object
|
||||
- file persistence remains Gemini CLI-owned through `GcliFileSessionService extends BaseSessionService`
|
||||
|
||||
Persistence rule:
|
||||
|
||||
- persisted history is an append-only event log plus derived app/user/session state
|
||||
- the session service provides atomic append, crash-safe recovery, and single-writer enforcement
|
||||
- rewind truncates the event log, recomputes derived state, and invalidates confirmation/resumption state past the rewind point
|
||||
|
||||
## 3.1 Authentication Flexibility
|
||||
|
||||
The CLI resolves distinct authentication flows (OAuth, ADC, Compute metadata) using standard Google libraries.
|
||||
|
||||
* **Current State:** Resolved in `packages/core/src/code_assist/oauth2.ts` based on `AuthType`.
|
||||
* OAuth (`LOGIN_WITH_GOOGLE`)
|
||||
* Compute Metadata Server (`COMPUTE_ADC`)
|
||||
* **Constraint:** Standard `Gemini` construction in `adk-js/core/src/models/google_llm.ts` still selects backend from constructor-level `apiKey` or Vertex config. It does **not** natively accept Gemini CLI's `AuthClient`-driven auth shape.
|
||||
* **Proposed ADK Mapping:** Phase 1 keeps auth in `GcliAgentModel`. The pipeline resolves refreshed credentials and injects them through `request.config.httpOptions.headers` for unary requests and `request.liveConnectConfig.httpOptions.headers` for live connections.
|
||||
* **Design position:** This is a **bridge**, not native ADK auth parity. Long-term cleanup is either:
|
||||
* a `CodeAssistLlm extends BaseLlm`, or
|
||||
* an upstream ADK auth-provider abstraction.
|
||||
|
||||
```typescript
|
||||
request.config ??= {};
|
||||
request.config.httpOptions ??= {};
|
||||
request.config.httpOptions.headers = {
|
||||
...request.config.httpOptions.headers,
|
||||
Authorization: `Bearer ${await auth.getAccessToken()}`,
|
||||
};
|
||||
```
|
||||
|
||||
## 3.2 Model Steering and Mid-Stream Injection
|
||||
|
||||
User interjections (hints) course-correct the loop mid-turn.
|
||||
|
||||
* **Current State:** Steering today is tied to the legacy loop and injection services.
|
||||
* **Proposed ADK Mapping (Next-Step Steering):** Supported in Phase 1. `beforeModelCallback` can read queued hints and mutate the next outbound request.
|
||||
* **Proposed ADK Mapping (True Real-Time Interrupt):** Still blocked. TypeScript ADK live runtime is not complete yet, and input-stream semantics are not a stable dependency.
|
||||
* **Phase 1 behavior:** user interjections are queued and prepended to the next model request at tool or turn boundaries. True in-place turn interruption remains out of scope.
|
||||
|
||||
## 3.3 State Management and Token Compaction
|
||||
|
||||
The CLI truncates large tool responses and summarizes older history to protect token budgets.
|
||||
|
||||
* **Current State:** `ChatCompressionService` in `packages/core/src/context/chatCompressionService.ts` implements reverse token budgeting and a two-phase verification loop.
|
||||
* **Proposed ADK Mapping:** Compaction remains a Gemini CLI-owned history processor invoked by the request pipeline.
|
||||
* **Design position:** Phase 1 does **not** force this onto ADK `BaseContextCompactor`. Gemini CLI compression is currently an outbound-history projection with truncation plus summary/verification sub-calls, while ADK's native compactor path is event-log-centric and mutates session history.
|
||||
* **Design choice:** In Phase 1, compaction mutates **outgoing request history only**. The persisted session event log remains canonical.
|
||||
* **Utility calls:** Compaction sub-calls use the same routing, auth, and availability pipeline as primary model calls.
|
||||
|
||||
## 3.4 Model Configuration and Hierarchical Overrides
|
||||
|
||||
Dynamic aliasing (for example, temperature scoped to specific sub-commands).
|
||||
|
||||
* **Current State:** Managed by `ModelConfigService`.
|
||||
* **Proposed ADK Mapping:** Resolution stays **request-scoped**, not session-init-scoped.
|
||||
* **Design choice:** The session stores requested model and override state. The runtime resolves the concrete model and temperature on each request, including retries, subagents, and utility calls.
|
||||
|
||||
## 3.5 Universal Policy Enforcement (TOML Rules)
|
||||
|
||||
Tiered workspace restrictions (for example, read-only tools in untrusted folders).
|
||||
|
||||
* **Current State:** Intercepted at tool scheduling time in legacy scheduler loops.
|
||||
* **Proposed ADK Mapping (Container):** Standardize on ADK `SecurityPlugin`.
|
||||
* **Proposed ADK Mapping (Decision Brain):** Implement `GcliPolicyEngineAdapter implements BasePolicyEngine`.
|
||||
* **Richer context:** The adapter is fed session/mode/subagent/MCP metadata from the runtime so current Gemini CLI policy semantics are preserved as closely as possible.
|
||||
* **Phase 1 suspension flow:** current tool approvals use existing Gemini CLI callbacks. This is intentionally **non-native** and **non-resumable across process death**.
|
||||
* **Long-term target:** move the same policy decisions onto native ADK confirmation/resumption semantics once the broader live/elicitation surface is ready.
|
||||
|
||||
```typescript
|
||||
interface GcliPolicyBridge {
|
||||
evaluate(context: ToolCallPolicyContext): Promise<PolicyCheckResult>;
|
||||
}
|
||||
|
||||
export class GcliPolicyEngineAdapter implements BasePolicyEngine {
|
||||
constructor(private readonly policyBridge: GcliPolicyBridge) {}
|
||||
|
||||
async evaluate(context: ToolCallPolicyContext): Promise<PolicyCheckResult> {
|
||||
return this.policyBridge.evaluate(context);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3.6 Telemetry and Observability (Clearcut Tracking)
|
||||
|
||||
Hardware metrics, token counts, and step durations.
|
||||
|
||||
* **Current State:** `ClearcutLogger` reads system metrics and relies on deep scheduler hooks for latency accounting.
|
||||
* **Proposed ADK Mapping:** Use a combination of:
|
||||
* passive event-stream observation, and
|
||||
* explicit runtime instrumentation where passive ADK events are insufficient.
|
||||
* **Correlation rule:** tool timing is keyed by `functionCall.id`, **not** by `event.id`.
|
||||
* **Design position:** Passive stream interception alone is not assumed to provide full parity.
|
||||
|
||||
## 3.7 Dynamic Model Routing and Configurability
|
||||
|
||||
Banning a model mid-turn, auto-routing via classifiers, and falling back dynamically without reinitializing the session.
|
||||
|
||||
* **Current State:** Managed by `ModelRouterService` and a chain of `RoutingStrategy` implementations which require the full `RoutingContext` (`history`, `request`, `AbortSignal`).
|
||||
* **Proposed ADK Mapping:** Mostly implementable now, but not “100% possible today” without bridge logic.
|
||||
* **Design choice:** The runtime constructs a proper `RoutingContext` from:
|
||||
* canonical session history
|
||||
* the pending user request
|
||||
* requested model
|
||||
* abort signal
|
||||
* **Execution point:** routing remains request-scoped and runs before final dispatch.
|
||||
* **Model banning:** treated as routing/fallback selection, not as a synthetic terminal model error.
|
||||
|
||||
## 3.8 Fallbacks and Availability Management
|
||||
|
||||
Ensuring availability by retrying or switching models when rate limits (429s) or terminal faults occur.
|
||||
|
||||
* **Current State:** Managed by `ModelAvailabilityService` and `ModelPool`.
|
||||
* **Proposed ADK Mapping (Preflight):** availability and fallback selection run before dispatch and before routing commits to a concrete model.
|
||||
* **Proposed ADK Mapping (Post-failure):** handled separately by the runtime. Availability state mutation and retry decisions are not treated as pure request preprocessing.
|
||||
* **Global Application:** utility calls use the same availability/fallback services as primary model calls.
|
||||
* **Retry safety rule:** automatic full-turn replay is allowed only before any side-effecting tool has executed. After side effects, the runtime surfaces the failure and requires explicit user action.
|
||||
* **Phase 1 transition path:** approvals and fallback prompts continue to use existing Gemini CLI callbacks. The doc treats this as an internal bridge, not as an existing standard ADK elicitation API.
|
||||
|
||||
## 3.9 State-Driven Mode Switching (Plan Mode)
|
||||
|
||||
Dynamically shifting system prompts and active tools when users switch interaction tiers (for example, Chat Mode to Plan Mode).
|
||||
|
||||
* **Current State:** Toggled via `/plan`, which changes `ApprovalMode` and related legacy scheduler behavior.
|
||||
* **Proposed ADK Mapping (Dynamic Prompt):** Use `InstructionProvider` in `LlmAgentConfig.instruction`.
|
||||
* **Proposed ADK Mapping (Dynamic Tooling):** Use a custom `BaseToolset` whose filter is derived from session mode.
|
||||
* **Single source of truth:** mode is session/runtime state derived from current approval mode; prompt, toolset, policy, routing, and UI all consume the same state.
|
||||
|
||||
```typescript
|
||||
export class GcliModeAwareToolset extends BaseToolset {
|
||||
constructor(
|
||||
private readonly chatTools: BaseTool[],
|
||||
private readonly planTools: BaseTool[],
|
||||
) {
|
||||
super(() => true);
|
||||
}
|
||||
|
||||
async getTools(context?: ReadonlyContext): Promise<BaseTool[]> {
|
||||
const isPlan = context?.state.get('plan_mode') === true;
|
||||
return isPlan ? this.planTools : this.chatTools;
|
||||
}
|
||||
|
||||
async close(): Promise<void> {}
|
||||
}
|
||||
```
|
||||
|
||||
## 3.10 Tool Output Masking
|
||||
|
||||
Managing context window efficiency by offloading bulky tool outputs (for example, shell logs and large file reads) to files.
|
||||
|
||||
* **Current State:** `ToolOutputMaskingService` in `packages/core/src/context/toolOutputMaskingService.ts`.
|
||||
* **Proposed ADK Mapping:** masking runs on **outgoing request history only**, after compaction.
|
||||
* **Design choice:** persisted session history remains the canonical event log. Masking artifacts are session-scoped files referenced from the outbound request projection.
|
||||
|
||||
---
|
||||
|
||||
# 5. Known Gaps in ADK (Gating Blockers)
|
||||
|
||||
This section highlights existing gaps in standard ADK that prevent a seamless cutover without bridge logic or upstream changes.
|
||||
|
||||
## 5.1 Real-Time User Message Injections (Aborted Turns)
|
||||
|
||||
While next-step steering is possible today using `beforeModelCallback`, true real-time interruption requires:
|
||||
|
||||
- stable input-stream support, and
|
||||
- a complete TypeScript live runtime
|
||||
|
||||
Until then, the supported behavior is queued next-step steering at model boundaries, not mid-stream interruption.
|
||||
|
||||
## 5.2 Conversation Rewind and State Reversal
|
||||
|
||||
Translating manual trajectory drops to ADK runtime state is cumbersome. While Python ADK supports rollback, TypeScript ADK does not yet support it natively.
|
||||
|
||||
* **Resolution Strategy:** Gemini CLI implements rewind in `GcliFileSessionService`, but **not** as a shallow JSON edit. Rewind truncates the event log, recomputes derived state, and invalidates any confirmation/resumption state past the rewind point.
|
||||
|
||||
## 5.3 Phase 1 Non-Goals
|
||||
|
||||
To keep the migration surface bounded, Phase 1 intentionally excludes:
|
||||
|
||||
- non-interactive surfacing of `elicitation_request` / `elicitation_response`
|
||||
- true live/bidi interruption semantics
|
||||
- native ADK confirmation/resumption parity for approvals and fallback prompts
|
||||
|
||||
---
|
||||
|
||||
# Long-Term Vision: Unification of Agents and Subagents
|
||||
|
||||
The long-term vision is that subagents and the primary agent share:
|
||||
|
||||
- the same runtime core
|
||||
- the same tool definitions
|
||||
- the same policy constraints
|
||||
- the same configuration schemas
|
||||
- the same orchestration contract: `AgentSession`
|
||||
|
||||
What may differ is the **embedding adapter**:
|
||||
|
||||
- standalone/top-level agent -> consumed directly as `AgentSession`
|
||||
- subagent embedded by a parent agent -> projected from its `AgentSession` into parent-facing tool or child-thread events
|
||||
|
||||
For SDK-first unification, Gemini CLI orchestration targets `AgentSession`, not a specific ADK tool abstraction. When a subagent must be exposed to a parent model as a tool, Gemini CLI wraps that child `AgentSession` with a `FunctionTool` or custom `BaseTool` projection. `AgentTool` remains the native ADK nested-agent option if we later want full ADK-native nested-agent semantics.
|
||||
|
||||
---
|
||||
|
||||
# Migration Sequence and Unification Checklist
|
||||
|
||||
The migration remains non-sequential, but each phase has an explicit success condition:
|
||||
|
||||
- [ ] **Non-interactive session parity** `#22699`
|
||||
output/error parity, basic replay correctness, feature-flagged rollout
|
||||
- [ ] **Interactive session parity** `#22701`
|
||||
projected event parity, approval bridge wiring, no live-interrupt claim
|
||||
- [ ] **Subagent adapter parity** `#22700`
|
||||
tool isolation, activity projection, policy/routing parity for child runs
|
||||
- [ ] **ADK session conformance** `#22974`
|
||||
`AgentSession` timing/replay guarantees preserved by the adapter
|
||||
- [ ] **Skills parity** `#22966`
|
||||
skills work through the shared runtime without special legacy paths
|
||||
- [ ] **Policy and confirmation parity** `#22964`
|
||||
phase-1 callback bridge stable; native confirmation migration scoped separately
|
||||
- [ ] **Compaction quality parity** `#22979`
|
||||
comparable summarization and truncation quality to current behavior
|
||||
|
||||
---
|
||||
|
||||
# Appendix A: Initialization Sketch
|
||||
|
||||
The main agent and subagents share the same composition root. The difference is whether the resulting `AgentSession` is consumed directly or projected into a parent session.
|
||||
|
||||
```typescript
|
||||
shared = {
|
||||
baseModel,
|
||||
modelConfigService,
|
||||
availabilityService,
|
||||
router,
|
||||
authService,
|
||||
policyEngine,
|
||||
mcpClientManager,
|
||||
skillCatalog,
|
||||
baseToolCatalog,
|
||||
sessionService,
|
||||
}
|
||||
|
||||
agentService = new AdkAgentService(shared)
|
||||
|
||||
function createSession(definition, parentSessionId?) {
|
||||
sessionRecord = sessionService.create({ definition, parentSessionId })
|
||||
registries = buildScopedRegistries(definition, parentSessionId)
|
||||
pipeline = createPipeline({
|
||||
sessionId: sessionRecord.id,
|
||||
agentId: definition.name,
|
||||
parentSessionId,
|
||||
})
|
||||
|
||||
model = new GcliAgentModel(baseModel, pipeline)
|
||||
runtime = createAdkRuntime({
|
||||
definition,
|
||||
model,
|
||||
sessionRecord,
|
||||
registries,
|
||||
policyEngine,
|
||||
})
|
||||
|
||||
return new AgentSession(new AdkAgentProtocolAdapter(runtime))
|
||||
}
|
||||
|
||||
function createSubagentTool(definition) {
|
||||
return new FunctionTool(async (input, toolContext) => {
|
||||
child = createSession(definition, toolContext.sessionId)
|
||||
stream = await child.send(userMessage(input))
|
||||
|
||||
for await (event of child.stream(stream)) {
|
||||
projectChildEventToParent(toolContext, event)
|
||||
}
|
||||
|
||||
return collectFinalText()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Child session projection shape:
|
||||
|
||||
```typescript
|
||||
emit(tool_request({ requestId, name: definition.name, args: input }))
|
||||
|
||||
for await (event of childSession.stream({ streamId })) {
|
||||
if (event.type === 'message' || event.type === 'tool_update') {
|
||||
emit(
|
||||
tool_update({
|
||||
requestId,
|
||||
content: projectDisplayContent(event),
|
||||
}),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (event.type === 'error') {
|
||||
emit(
|
||||
tool_response({
|
||||
requestId,
|
||||
name: definition.name,
|
||||
isError: true,
|
||||
content: projectErrorContent(event),
|
||||
}),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (event.type === 'agent_end') {
|
||||
emit(
|
||||
tool_response({
|
||||
requestId,
|
||||
name: definition.name,
|
||||
content: collectFinalChildResult(),
|
||||
}),
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Only the final `tool_response` is returned to the parent model. `tool_update` remains a progress/UI projection surface.
|
||||
|
||||
Service lifetime split:
|
||||
|
||||
- shared across sessions: model config, availability, routing, auth, policy engine, MCP manager, skill catalog
|
||||
- scoped per agent/session: `AgentSession`, pipeline instance, tool/prompt/resource registries, derived message bus
|
||||
- scoped per invocation: shell process, MCP request, confirmation continuation, tool progress updates
|
||||
|
||||
---
|
||||
|
||||
# Status of this document approvals table {#status-of-this-document}
|
||||
|
||||
| #begin-approvals-addon-section See [go/g3a-approvals](http://goto.google.com/g3a-approvals) for instructions on adding reviewers. |
|
||||
| :---: |
|
||||
.
|
||||
@@ -0,0 +1,554 @@
|
||||
# Unified ADK CLI Design Review
|
||||
|
||||
Date: 2026-04-06
|
||||
|
||||
> **Rollout:** All ADK migration work is feature-gated behind `experimental.adk` flags. No behavioral changes ship without an explicit opt-in. This applies to both non-interactive and interactive flows.
|
||||
|
||||
Inputs merged:
|
||||
- `claude-adk-cli-design-review.md` (adversarial code-level review)
|
||||
- `codex-adk-cli-design-review.md`
|
||||
- Follow-up review discussion on subagent architecture, session semantics, and `BaseContextCompactor`
|
||||
|
||||
Scope reviewed:
|
||||
- `gemini-cli/docs/adk-replat/adk_migration_design_doc.md`
|
||||
- `gemini-cli` `AgentProtocol` / `AgentSession` / interactive migration work
|
||||
- `adk-js` runner / session / tool / security / model architecture
|
||||
- PR context for interactive migration, including `google-gemini/gemini-cli#24297`
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The design is directionally sound and the migration goal is correct. However, the document is not yet principal-review ready due to:
|
||||
|
||||
1. **Four non-compilable code samples** (C1-C4) that will immediately erode reviewer confidence
|
||||
2. **An under-specified translation boundary** — the ADK→AgentProtocol mapping is the real architecture, but the doc treats it as an afterthought
|
||||
3. **Overclaimed parity** in routing, telemetry, and approvals
|
||||
4. **Missing phased plan** with clear milestones and non-goals
|
||||
|
||||
The central architectural insight is right: the migration boundary is:
|
||||
|
||||
- `adk-js Event -> Gemini CLI AgentEvent`
|
||||
- `adk-js session/runtime semantics -> AgentProtocol / AgentSession semantics`
|
||||
|
||||
That boundary carries stream lifecycle, replay/resume, event projection, approval correlation, and persistence correctness. Until the translation architecture is explicit, parity claims remain unsupported.
|
||||
|
||||
## Compilation Blockers (Must Fix First)
|
||||
|
||||
These are binary errors — the code samples in the design doc will not compile against current ADK types.
|
||||
|
||||
### C1. `LlmRequest` has no `headers` field
|
||||
|
||||
`adk-js/core/src/models/llm_request.ts` defines: `model?`, `contents`, `config?`, `liveConnectConfig`, `toolsDict`. The design doc's `request.headers = {...}` will silently fail or not compile. **Fix:** use `llmRequest.config.httpOptions.headers` or pass headers at `Gemini` constructor time.
|
||||
|
||||
### C2. `BaseLlm.connect()` is abstract and unimplemented
|
||||
|
||||
`adk-js/core/src/models/base_llm.ts:67-78` has TWO abstract methods: `generateContentAsync` and `connect()`. `GcliAgentModel` must implement both. **Fix:** add `connect()` — stub with `throw new Error('not supported')` if live connections are out of scope.
|
||||
|
||||
### C3. `BaseToolset` constructor signature is wrong
|
||||
|
||||
`adk-js/core/src/tools/base_toolset.ts:46-49`: `constructor(readonly toolFilter: ToolPredicate | string[], readonly prefix?: string)`. The design doc's `super([])` passes an empty array as `toolFilter`, which makes `isToolSelected` return false for all tools — a silent bug. Also `close()` is abstract and must be implemented. **Fix:** match the actual signature.
|
||||
|
||||
### C4. `FunctionalTool` does not exist
|
||||
|
||||
The design doc references `FunctionalTool` for subagent wrapping. This class does not exist in adk-js. The correct classes are `FunctionTool` (wraps a plain function) and `AgentTool` (wraps an agent with isolated runner). See subagent architecture section below for the recommended approach.
|
||||
|
||||
---
|
||||
|
||||
## High-Risk Findings
|
||||
|
||||
### 1. The actual migration boundary is under-specified
|
||||
|
||||
Current `AgentProtocol` requires `send()` timing guarantees, replay/reattach semantics, `streamId`, and optional `threadId` for subagent threads in `gemini-cli/packages/core/src/agent/types.ts` and `gemini-cli/packages/core/src/agent/agent-session.ts`.
|
||||
|
||||
ADK emits a different event model in `adk-js/core/src/events/event.ts`.
|
||||
|
||||
The doc needs a first-class architecture section for:
|
||||
- `AdkSessionRuntime`
|
||||
- `AdkEventTranslator`
|
||||
- event buffering / replay
|
||||
- `threadId` mapping
|
||||
- `tool_update` mapping
|
||||
- `agent_start` / `agent_end` ownership
|
||||
|
||||
Without that, the rest of the design sits on an unstated core contract.
|
||||
|
||||
### 2. The consolidated decorator needs two concerns extracted
|
||||
|
||||
The `GcliAgentModel.generateContentAsync` pipeline is a reasonable orchestrator pattern — each concern is a separate injected service call, not a monolithic class. However, two concerns don't belong in the model layer:
|
||||
|
||||
- **Quota/availability prompting** — involves interactive UI suspension, not request mutation. Should use `beforeModelCallback` on `LlmAgent`.
|
||||
- **Policy/approval control flow** — involves blocking for user input. Should use `beforeToolCallback` or the `ApprovalBridge` (see architecture section).
|
||||
|
||||
The remaining concerns (auth header injection, routing/model rewrite, compaction, masking) are legitimate request-preprocessing and can stay in the model pipeline.
|
||||
|
||||
### 3. The design frequently conflates “possible with custom logic” with “supported by current ADK architecture”
|
||||
|
||||
This is one of the main wording risks.
|
||||
|
||||
Several statements currently read like native ADK parity when the real meaning is:
|
||||
- possible with substantial Gemini CLI-owned bridge logic
|
||||
- possible by bypassing native ADK facilities
|
||||
- possible only for phase 1 with intentionally degraded semantics
|
||||
|
||||
That distinction needs to be explicit everywhere the doc uses strong language like “validated” or “100% possible today.”
|
||||
|
||||
### 4. Auth architecture is deeper than header injection — Coast Assist requires a custom transport
|
||||
|
||||
The auth section has two distinct problems:
|
||||
|
||||
**Problem A: API surface mismatch (C1).** `LlmRequest` has no `headers` field. Per-request headers are possible via `llmRequest.config.httpOptions.headers`, and `Gemini` constructor accepts a `headers` param (`google_llm.ts:57,79`). This is fixable.
|
||||
|
||||
**Problem B: Coast Assist / `LOGIN_WITH_GOOGLE` uses a different backend entirely.** The current `CodeAssistServer` (`packages/core/src/code_assist/server.ts:407`) does NOT call the standard Gemini API. It uses `google-auth-library`'s `AuthClient.request()` to talk to a Code Assist backend endpoint. The OAuth flow (`packages/core/src/code_assist/oauth2.ts`) handles browser launch (L305), local callback server (L492-615), token exchange (L546-550), credential caching (L739-750), and automatic token refresh via `OAuth2Client`.
|
||||
|
||||
This means `GcliAgentModel` cannot wrap a standard `Gemini` BaseLlm and just inject headers. For Coast Assist auth, `GcliAgentModel` must **extend `BaseLlm` directly** and implement its own HTTP transport using `AuthClient.request()`, translating between `LlmRequest`/`LlmResponse` and the Code Assist protocol.
|
||||
|
||||
What works:
|
||||
- The OAuth dance itself (browser launch, token caching, refresh) runs before the agent session starts — no blocking inside `generateContentAsync`
|
||||
- `OAuth2Client.getAccessToken()` handles mid-session token refresh transparently
|
||||
- Per-request header injection works for API-key-based auth via `httpOptions.headers`
|
||||
|
||||
What the design doc must change:
|
||||
- Stop assuming `Gemini` as the inner model — `GcliAgentModel extends BaseLlm` directly
|
||||
- Define two transport paths: standard Gemini API (API key) and Code Assist backend (OAuth)
|
||||
- The dummy-key workaround is irrelevant for Coast Assist — you never call `GoogleGenAI.models.generateContent`
|
||||
|
||||
### 5. Approval / elicitation bridging is acceptable only as a temporary non-native bridge
|
||||
|
||||
The design’s callback-based approval approach is pragmatic for phase 1, but it bypasses native ADK confirmation semantics.
|
||||
|
||||
`adk-js/core/src/plugins/security_plugin.ts` and `adk-js/core/src/agents/processors/request_confirmation_llm_request_processor.ts` show that ADK’s model is:
|
||||
- `ALLOW | DENY | CONFIRM`
|
||||
- persisted confirmation state
|
||||
- later resumption from history
|
||||
|
||||
Blocking in callbacks may be acceptable for the first rollout, but the doc must say clearly:
|
||||
- local-only bridge
|
||||
- not resumable across process death
|
||||
- not long-term SDK behavior
|
||||
- intentionally non-native pending future elicitation/bidi work
|
||||
|
||||
### 6. Rewind is deeper than file truncation
|
||||
|
||||
The doc treats rewind as storage truncation. The storage part is straightforward — `GcliFileSessionService` wraps the existing `ChatRecordingService` and implements 4 abstract methods (`createSession`, `getSession`, `listSessions`, `deleteSession`). DB-level concerns (locking, stale-writer detection, `PESSIMISTIC_WRITE`) do not apply to a single-user CLI.
|
||||
|
||||
However, rewind itself is not just storage:
|
||||
- `adk-js/core/src/agents/processors/request_confirmation_llm_request_processor.ts` scans event history to reconstruct pending confirmations and resume tool execution
|
||||
- `adk-js/core/src/runner/runner.ts:447` has a TODO acknowledging the event log is used as a transaction log
|
||||
- Truncating events without rolling back derived state (pending confirmations, app/user state prefixes) will break resumption
|
||||
|
||||
The doc should define rewind as: event-log truncation + session state recomputation + confirmation/auth state rollback.
|
||||
|
||||
Note: `BaseSessionService.appendEvent` handles state merging with `app:`, `user:`, `temp:` prefixed keys automatically — this is inherited for free.
|
||||
|
||||
### 7. Routing, telemetry, and plan mode are all overclaimed
|
||||
|
||||
Routing:
|
||||
- current routing requires a richer `RoutingContext`
|
||||
- `contents.slice(0, -1)` / `contents.pop()` is not a sufficient or type-accurate mapping
|
||||
- model banning via simulated error is not the same as model routing
|
||||
|
||||
Telemetry:
|
||||
- **The stream-interceptor approach in section 3.6 is wrong.** The correct mechanism is ADK's `BasePlugin` system.
|
||||
- `event.id` is not a safe per-tool correlation key — use `functionCall.id` via `beforeToolCallback`/`afterToolCallback`
|
||||
- ADK can batch multiple function calls or responses in one event — stream interception cannot distinguish them, but plugin hooks fire per-tool with distinct `functionCallId`
|
||||
- **Correction:** Token usage IS available on ADK events. `llm_agent.ts:831-834` spreads `LlmResponse` (including `usageMetadata`) into the event via `createEvent({...modelResponseEvent, ...llmResponse})`. Additionally, `afterModelCallback` receives the raw `LlmResponse` with `usageMetadata` directly — providing a second capture point.
|
||||
- HTTP-level metrics (status code, request duration) are NOT on ADK events — must be captured in the `GcliAgentModel` wrapper
|
||||
- The sample APIs in the design do not match the current `ClearcutLogger` taxonomy (~195 distinct metadata keys)
|
||||
|
||||
**Recommended telemetry architecture:**
|
||||
|
||||
| Metric Category | Capture Mechanism | ADK Hook |
|
||||
|---|---|---|
|
||||
| Token counts (input, output, cached, thinking) | `afterModelCallback` → `LlmResponse.usageMetadata` | `BasePlugin.afterModelCallback` |
|
||||
| Per-tool timing | Timer keyed on `functionCallId` | `BasePlugin.beforeToolCallback` / `afterToolCallback` |
|
||||
| Agent lifecycle | Start/end tracking | `BasePlugin.beforeAgentCallback` / `afterAgentCallback` |
|
||||
| Model errors | Error classification | `BasePlugin.onModelErrorCallback` |
|
||||
| HTTP status, API duration | Captured in model wrapper | `GcliAgentModel.generateContentAsync` |
|
||||
| Routing decisions, latency | Captured in model or beforeModelCallback | `beforeModelCallback` / model wrapper |
|
||||
| Context token breakdowns (system, tools, history) | Computed in request pipeline | Model wrapper |
|
||||
| Tool approval decisions | Injected from approval layer | `ApprovalBridge` |
|
||||
| Session config, compression, rewind, IDE, extensions, billing, slash commands, hooks, plan execution, onboarding | **Unchanged** — stays in current call sites | Existing gemini-cli code |
|
||||
|
||||
~30% of Clearcut metrics map to ADK plugin hooks. ~70% remain in their current call sites unchanged.
|
||||
|
||||
Plan mode:
|
||||
- current behavior is broader than prompt + tool filtering
|
||||
- policy, approval mode, routing, and config refresh are all part of the behavior
|
||||
|
||||
### 8. Retry safety around side effects (note)
|
||||
|
||||
The “full turn reset” proposal could duplicate side effects (file writes, shell commands, MCP mutations) if the turn already executed side-effecting tools before the stream failure. This is an ADK-wide concern, not specific to this migration. A brief note acknowledging this limitation is sufficient — a full retry safety taxonomy is out of scope for this design.
|
||||
|
||||
### 9. Design ignores ADK's native `BaseContextCompactor`
|
||||
|
||||
ADK already has a compaction extension point: `BaseContextCompactor` (`adk-js/core/src/context/base_context_compactor.ts`) with `shouldCompact(invocationContext)` + `compact(invocationContext)`. It's wired into the request processor pipeline via `ContextCompactorRequestProcessor` at `llm_agent.ts:407-420`.
|
||||
|
||||
The design doc proposes running compaction inside `GcliAgentModel.generateContentAsync` instead. This works but bypasses the native slot. Recommendation: implement `BaseContextCompactor`, delegate to the existing `ChatCompressionService` internally. ADK provides the trigger point; your service provides the logic. Note that `BaseContextCompactor` returns void — failure states (COMPRESSED, NOOP, CONTENT_TRUNCATED, etc.) must be communicated via session state or custom events.
|
||||
|
||||
### 10. Existing `isStructuredError` type-guard bug
|
||||
|
||||
`gemini-cli/packages/core/src/agent/event-translator.ts:431-438`: `isStructuredError()` checks only `typeof error === 'object' && 'message' in error && typeof error.message === 'string'`. Since every `Error` instance has `message: string`, plain `Error` objects pass this guard. In `mapError` (lines 390-429), the structured branch runs before `instanceof Error`, so plain errors get incorrect HTTP→gRPC status mapping. This should be fixed before the translator becomes the long-term session boundary. **Fix:** add `'status' in error` to the guard, or check `instanceof Error` first.
|
||||
|
||||
### 11. The review should distinguish architecture defects from rollout-status evidence
|
||||
|
||||
The interactive PR findings matter:
|
||||
- stacked-branch dependency
|
||||
- hook-order risk in `#24297`
|
||||
- `_meta.legacyState` leakage into protocol events
|
||||
|
||||
But they are secondary to the core design defect, which is the missing runtime/translation architecture.
|
||||
|
||||
These findings should stay in the review, but as credibility and rollout-risk evidence rather than the centerpiece.
|
||||
|
||||
## Recommended Architecture
|
||||
|
||||
This is the cleanest merged recommendation from both reviews plus follow-up discussion.
|
||||
|
||||
### Core principle
|
||||
|
||||
Use one shared ADK-based execution/runtime core, then put different adapters on top of it.
|
||||
|
||||
Do not make `AgentSession` the innermost engine.
|
||||
|
||||
### Recommended split
|
||||
|
||||
1. `AdkRuntimeCore`
|
||||
- owns the ADK runner loop
|
||||
- owns tool execution, policy integration, routing integration, masking, compaction hooks, and persistence hooks
|
||||
- emits runtime-level activity/events
|
||||
|
||||
2. `TopLevelSessionAdapter`
|
||||
- exposes the runtime as `AgentProtocol` / `AgentSession`
|
||||
- owns replay, reattach, `streamId`, `threadId`, `agent_start` / `agent_end`, and top-level event projection
|
||||
|
||||
3. `SubagentTools` (custom `BaseTool` implementations)
|
||||
- subagents invoke the same shared runtime core as the main agent
|
||||
- the only difference is the type mapping at the boundary (projecting child activity into parent-facing `tool_update` / `tool_response` or `threadId`-scoped events)
|
||||
- `AgentTool` is explicitly rejected — it creates a fully isolated runner/session, which conflicts with the shared-core goal
|
||||
- custom `BaseTool` wrappers allow subagents to share policy, routing, tool definitions, and config with the parent while keeping the door open for future resumability and richer state sharing
|
||||
|
||||
4. `ApprovalBridge`
|
||||
- phase-1 callback bridge for approvals / elicitation using existing callbacks
|
||||
- clearly documented as temporary and non-native
|
||||
- eventual migration to full elicitation bidi format
|
||||
|
||||
5. `GcliFileSessionService`
|
||||
- file-backed persistence wrapping existing `ChatRecordingService`
|
||||
- implements 4 abstract methods from `BaseSessionService`
|
||||
- inherits state-merging (app/user/temp prefixes) for free
|
||||
- single-writer model — concurrent runs per session are forbidden
|
||||
|
||||
### Subagent architecture decision
|
||||
|
||||
`AgentTool` is not the right abstraction for this migration. It creates an isolated runner with its own `InMemorySessionService`, meaning:
|
||||
- state is NOT shared with the parent
|
||||
- events are NOT projected into the parent stream
|
||||
- policy, routing, and config are NOT inherited
|
||||
|
||||
The core requirement is that subagents use the same core loop functionality as the main agent — the only difference is how results are mapped back to the parent. Custom `BaseTool` implementations that invoke the shared `AdkRuntimeCore` achieve this. This also positions subagents for future complexity (resumability, richer state sharing, MCP-scoped tool sets) without fighting AgentTool's isolation model.
|
||||
|
||||
### Session conclusion
|
||||
|
||||
Main agent and subagents can share the same underlying runtime core.
|
||||
|
||||
But they should not necessarily share the exact same public adapter.
|
||||
|
||||
The right formulation is:
|
||||
- same core runtime
|
||||
- different event/session projections depending on embedding context
|
||||
|
||||
## Section-by-Section Unified Review
|
||||
|
||||
### 3.1 Authentication Flexibility
|
||||
|
||||
Assessment: implementable, but the design doc’s approach is wrong for Coast Assist.
|
||||
|
||||
Keep:
|
||||
- ADK `Gemini` does not natively accept the CLI’s auth shape
|
||||
|
||||
Change:
|
||||
- `GcliAgentModel` must extend `BaseLlm` directly, not wrap `Gemini` — Coast Assist uses `AuthClient.request()` against a non-standard backend, not `GoogleGenAI`
|
||||
- fix `request.headers` to `llmRequest.config.httpOptions.headers` (C1)
|
||||
- define two transport paths: API-key (standard Gemini) and OAuth (Code Assist)
|
||||
- token refresh is transparent via `OAuth2Client` — this works as-is
|
||||
- browser-based OAuth runs before agent session starts — no blocking concern
|
||||
|
||||
### 3.2 Model Steering and Mid-Stream Injection
|
||||
|
||||
Assessment: next-turn steering is plausible now; true live interruption is still blocked.
|
||||
|
||||
Required change:
|
||||
- split “next-step steering” from “true mid-turn interruption”
|
||||
- define temporary user-visible behavior until live input-stream support exists
|
||||
|
||||
### 3.3 State Management and Token Compaction
|
||||
|
||||
Assessment: plausible, but too wrapper-centric.
|
||||
|
||||
Required change:
|
||||
- specify whether compaction operates on persisted history, outgoing request history, or both
|
||||
- define recursion guards and utility-call isolation
|
||||
- define artifact ownership across sessions and subagents
|
||||
|
||||
### 3.4 Model Configuration and Hierarchical Overrides
|
||||
|
||||
Assessment: under-specified.
|
||||
|
||||
Required change:
|
||||
- model config resolution should remain request-scoped
|
||||
- preserve scoped overrides and retry-aware behavior
|
||||
- explain subagent and utility-call override behavior
|
||||
|
||||
### 3.5 Universal Policy Enforcement
|
||||
|
||||
Assessment: acceptable as a bridge, not as full parity.
|
||||
|
||||
Required change:
|
||||
- explicitly document reduced phase-1 semantics
|
||||
- list policy inputs lost unless extra context plumbing is added
|
||||
- separate short-term callback bridge from long-term native confirmation flow
|
||||
|
||||
### 3.6 Telemetry and Observability (Clearcut)
|
||||
|
||||
Assessment: fully implementable, but the stream-interceptor approach must be replaced with a `BasePlugin`.
|
||||
|
||||
Required change:
|
||||
- replace stream interception with `ClearcutTelemetryPlugin extends BasePlugin`
|
||||
- use `afterModelCallback` for token counts (`LlmResponse.usageMetadata` is available)
|
||||
- use `beforeToolCallback`/`afterToolCallback` with `functionCallId` for per-tool timing
|
||||
- capture HTTP-level metrics (status code, duration) in `GcliAgentModel` wrapper
|
||||
- ~70% of Clearcut metrics stay in their current call sites unchanged — document which
|
||||
- replace fake API examples with a telemetry parity matrix showing capture mechanism per metric
|
||||
|
||||
### 3.7 Dynamic Model Routing and Configurability
|
||||
|
||||
Assessment: partly feasible, materially overstated.
|
||||
|
||||
Required change:
|
||||
- remove “100% possible today”
|
||||
- separate alias rewrite, fallback selection, classifier routing, and banning/rejection behavior
|
||||
- define a real `RoutingContextBuilder`
|
||||
|
||||
### 3.8 Fallbacks and Availability Management
|
||||
|
||||
Assessment: incomplete.
|
||||
|
||||
Required change:
|
||||
- split preflight fallback from post-failure transition behavior
|
||||
- define retry safety
|
||||
- define main-call vs utility-call fallback ownership
|
||||
|
||||
### 3.9 State-Driven Mode Switching
|
||||
|
||||
Assessment: too narrow.
|
||||
|
||||
Required change:
|
||||
- define a single mode state source of truth
|
||||
- enumerate all consumers: prompt, toolset, policy, router, UI
|
||||
|
||||
### 3.10 Tool Output Masking
|
||||
|
||||
Assessment: one of the stronger sections.
|
||||
|
||||
Required change:
|
||||
- define whether masking mutates persisted history or only outgoing request history
|
||||
- define ordering and idempotence relative to compaction
|
||||
- define file/artifact lifecycle
|
||||
|
||||
### 4. SDK Facade / Stateful Orchestration
|
||||
|
||||
Assessment: this should become the architectural center of the document.
|
||||
|
||||
Required change:
|
||||
- expand ownership boundaries
|
||||
- describe the shared runtime plus adapter model
|
||||
- document temporary approval/elicitation limitations explicitly
|
||||
|
||||
### 4.1 Hybrid Tool Instantiation
|
||||
|
||||
Assessment: directionally good.
|
||||
|
||||
Required change:
|
||||
- include message bus derivation
|
||||
- include MCP discovery scoping
|
||||
- include recursion prevention and tool isolation details
|
||||
|
||||
### 4.2 Custom File-Based Persistence
|
||||
|
||||
Assessment: straightforward for storage, but rewind semantics need work.
|
||||
|
||||
The storage layer itself is low-risk — `GcliFileSessionService` wraps existing `ChatRecordingService` and implements 4 abstract methods. DB concerns (locking, stale-writer, crash recovery) don't apply to a single-user CLI.
|
||||
|
||||
Required change:
|
||||
- define rewind as event-log truncation + state recomputation (not just file truncation)
|
||||
- state that concurrent runs per session are forbidden (single-writer)
|
||||
- define how app/user/temp state prefixes map to existing workspace JSON
|
||||
|
||||
### 4.3 Decomposing `AgentLoopContext`
|
||||
|
||||
Assessment: right instinct, incomplete decomposition.
|
||||
|
||||
Required change:
|
||||
- account for message bus / confirmation routing
|
||||
- account for injection queues
|
||||
- account for prompt/resource registries and MCP scoping
|
||||
|
||||
### 5.1 Real-Time User Message Injections
|
||||
|
||||
Assessment: blocker analysis is incomplete.
|
||||
|
||||
Required change:
|
||||
- mention TS live runtime gaps directly
|
||||
- describe temporary UX until true interruption exists
|
||||
|
||||
### 5.2 Conversation Rewind and State Reversal
|
||||
|
||||
Assessment: too shallow today.
|
||||
|
||||
Required change:
|
||||
- define rewind as event-log truncation plus state rollback semantics
|
||||
- define confirmation/auth rollback semantics
|
||||
- define artifact/version rollback expectations
|
||||
|
||||
### 5.3 Concurrent Sessions
|
||||
|
||||
Assessment: a single-user CLI does not need DB-level locking.
|
||||
|
||||
Required change:
|
||||
- state that concurrent runs per session are forbidden (single-writer model)
|
||||
- this is sufficient for a CLI — no further locking design needed
|
||||
|
||||
## Corrections to Earlier Reviews
|
||||
|
||||
### Auth is implementable, not blocked
|
||||
API-key auth works via `httpOptions.headers`. Coast Assist / `LOGIN_WITH_GOOGLE` requires `GcliAgentModel` to extend `BaseLlm` directly with its own transport (using `AuthClient.request()`), not wrap `Gemini`. The OAuth flow, token refresh, and credential caching all work as-is — the issue was the design doc's assumption about the inner model, not auth capability.
|
||||
|
||||
### Persistence is simpler than initially claimed
|
||||
DB-level concerns (PESSIMISTIC_WRITE, stale-writer detection, concurrent append policy) come from `DatabaseSessionService` and don't apply. The file-backed service wraps existing `ChatRecordingService`. Only rewind semantics need deeper design.
|
||||
|
||||
### Interactive PR findings are rollout-risk evidence, not architecture defects
|
||||
The branch-stack dependency, hook-order risk, and `_meta.legacyState` leakage in PR #24297 are real but secondary to the core architecture gaps above.
|
||||
|
||||
## What To Change Before Principal Review
|
||||
|
||||
1. **Fix the 4 compilation blockers** (C1-C4) — these will be the first thing reviewers check
|
||||
2. **Add an explicit ADK→AgentProtocol translation architecture section** — this is the real design center
|
||||
3. **Add the shared-core subagent architecture** — custom `BaseTool` over shared runtime, not `AgentTool`
|
||||
4. **Use `BaseContextCompactor`** for compaction instead of embedding it in the model layer
|
||||
5. **Replace “100% possible today” and similar language** with explicit labels: native / bridgeable / blocked
|
||||
6. **Add a parity matrix** (feature | ADK mechanism | status | bridge workaround | long-term target)
|
||||
|
||||
## Phased Migration Plan
|
||||
|
||||
### Phase 0: Foundation (current state → near-term)
|
||||
**Goal:** Establish the shared runtime core and translation layer.
|
||||
|
||||
- [ ] Fix compilation blockers (C1-C4) in design doc code samples
|
||||
- [ ] Implement `AdkRuntimeCore` wrapping ADK `Runner` + `LlmAgent`
|
||||
- [ ] Implement `AdkEventTranslator` (ADK `Event` → gemini-cli `AgentEvent`)
|
||||
- [ ] Implement `GcliAgentModel extends BaseLlm` with dual transport (API-key via `httpOptions.headers`, Coast Assist via `AuthClient.request()`)
|
||||
- [ ] Implement `GcliFileSessionService extends BaseSessionService` wrapping `ChatRecordingService`
|
||||
- [ ] Implement `GcliContextCompactor implements BaseContextCompactor` wrapping `ChatCompressionService`
|
||||
- [ ] Wire behind `experimental.adk` feature gate
|
||||
|
||||
**Non-goals for Phase 0:**
|
||||
- No interactive flow changes
|
||||
- No subagent support
|
||||
- No live/bidi connections
|
||||
- No resumable approvals
|
||||
|
||||
**Exit criteria:** Non-interactive flow produces identical output behind feature gate.
|
||||
|
||||
### Phase 1: Non-Interactive Parity
|
||||
**Goal:** Feature-gated non-interactive flow matches legacy behavior.
|
||||
|
||||
- [ ] Tool output masking via `BaseLlmRequestProcessor`
|
||||
- [ ] Model routing via `beforeModelCallback` + `RoutingContextBuilder` adapter
|
||||
- [ ] Policy enforcement via `beforeToolCallback` using existing callbacks (temporary bridge)
|
||||
- [ ] Quota/availability handling via `beforeModelCallback`
|
||||
- [ ] `ClearcutTelemetryPlugin extends BasePlugin` for token counts (`afterModelCallback`), per-tool timing (`beforeToolCallback`/`afterToolCallback` with `functionCallId`), agent lifecycle, model errors
|
||||
- [ ] HTTP-level telemetry (status code, duration) captured in `GcliAgentModel` wrapper
|
||||
- [ ] Verify ~70% of existing Clearcut call sites work unchanged
|
||||
|
||||
**Non-goals for Phase 1:**
|
||||
- No interactive UI changes
|
||||
- No plan mode switching
|
||||
- No subagents
|
||||
- Approvals are callback-based, not resumable
|
||||
|
||||
**Exit criteria:** Non-interactive tests pass with `experimental.adk.enabled = true`. Legacy path remains default.
|
||||
|
||||
### Phase 2: Interactive Flow Migration
|
||||
**Goal:** Interactive flow uses the same ADK runtime behind `TopLevelSessionAdapter`.
|
||||
|
||||
- [ ] `TopLevelSessionAdapter` exposing runtime as `AgentProtocol` / `AgentSession`
|
||||
- [ ] Stream lifecycle (`agent_start` / `agent_end` / `streamId` ownership)
|
||||
- [ ] Replay/reattach semantics matching current `AgentSession.stream()` behavior
|
||||
- [ ] Plan mode via dynamic `BaseToolset` + `InstructionProvider` + mode state
|
||||
- [ ] `ApprovalBridge` for tool confirmations using existing UI callbacks
|
||||
- [ ] Elicitation via existing callbacks (not yet bidi)
|
||||
- [ ] `_meta.legacyState` bridge for UI rendering (documented as temporary)
|
||||
|
||||
**Non-goals for Phase 2:**
|
||||
- No live/bidi parity (`runLiveImpl` is still a stub in ADK TS)
|
||||
- No true mid-turn interruption (steering limited to once-per-LLM-call)
|
||||
- No resumable approvals across process death
|
||||
- No concurrent multi-stream sessions
|
||||
|
||||
**Exit criteria:** Interactive flow works behind feature gate. Legacy path remains default.
|
||||
|
||||
### Phase 3: Subagents and SDK Readiness
|
||||
**Goal:** Subagents share the core runtime. The architecture is SDK-reusable.
|
||||
|
||||
- [ ] Custom `BaseTool` subagent wrappers over shared `AdkRuntimeCore`
|
||||
- [ ] `threadId`-scoped event projection for child activity
|
||||
- [ ] Shared policy, routing, and config inheritance
|
||||
- [ ] Remove `_meta.legacyState` — replace with proper event/adapter separation
|
||||
- [ ] Migrate approval bridge to native ADK elicitation/bidi (when available)
|
||||
- [ ] Define SDK public API surface
|
||||
|
||||
**Non-goals for Phase 3:**
|
||||
- Full ADK Dev UI compatibility
|
||||
- Agent-to-agent transfer via ADK's native mechanism
|
||||
|
||||
**Exit criteria:** Subagent invocation works. Architecture is documented for SDK consumers.
|
||||
|
||||
## Positive Signals
|
||||
|
||||
- Feature-gating behind `experimental.adk` is the right rollout pattern
|
||||
- Tool output masking maps cleanly to request-preprocessing
|
||||
- Dynamic toolset + `InstructionProvider` for plan mode is a good ADK-native fit
|
||||
- The phased migration checklist with tracking issues shows good engineering discipline
|
||||
- The doc correctly identifies 3 known ADK gaps (sections 5.1-5.3)
|
||||
- `AgentProtocol` / `AgentSession` is the right consumer-facing boundary
|
||||
- The policy adapter pattern is directionally sound even with reduced phase-1 semantics
|
||||
|
||||
## Concern Decomposition
|
||||
|
||||
| Concern | Level | ADK Mechanism |
|
||||
|---|---|---|
|
||||
| Auth (API key) | Transport | `BaseLlm` constructor / `httpOptions.headers` |
|
||||
| Auth (Coast Assist) | Transport | `BaseLlm` direct — custom transport via `AuthClient.request()` |
|
||||
| Model rewrite | Transport | `BaseLlm.generateContentAsync` model override |
|
||||
| Model routing | Agent | `beforeModelCallback` + `RoutingContextBuilder` |
|
||||
| Token compaction | Agent | `BaseContextCompactor` (native ADK) |
|
||||
| Quota/availability | Agent | `beforeModelCallback` |
|
||||
| Tool masking | Agent | `BaseLlmRequestProcessor` |
|
||||
| Tool confirmations | Agent+Consumer | `beforeToolCallback` + existing callbacks (phase 1) |
|
||||
| Telemetry (lifecycle) | Agent | `ClearcutTelemetryPlugin` — `afterModelCallback`, `beforeToolCallback`/`afterToolCallback` |
|
||||
| Telemetry (HTTP) | Transport | `GcliAgentModel` wrapper (status code, duration) |
|
||||
| Telemetry (CLI) | Consumer | Existing call sites (~70% of Clearcut metrics, unchanged) |
|
||||
| UI rendering | Consumer | Event subscriber / adapter |
|
||||
| Subagent invocation | Agent | Custom `BaseTool` over shared runtime core |
|
||||
|
||||
## Final Verdict
|
||||
|
||||
The design is architecturally correct in its goal. To be principal-review ready:
|
||||
|
||||
1. Fix the 4 compilation blockers
|
||||
2. Add the ADK→AgentProtocol translation architecture as the design center
|
||||
3. Adopt native `BaseContextCompactor` instead of model-layer compaction
|
||||
4. Use custom `BaseTool` subagents over shared core (not `AgentTool`)
|
||||
5. Downgrade parity claims to match reality
|
||||
6. Add the phased plan with explicit non-goals per phase
|
||||
|
||||
The migration path is: shared runtime core → translation layer → adapters (top-level session, subagent tools, approval bridge). Each phase ships independently behind the feature gate.
|
||||
@@ -106,6 +106,67 @@ organization.
|
||||
ensures users maintain final control over which permitted servers are actually
|
||||
active in their environment.
|
||||
|
||||
#### Required MCP Servers (preview)
|
||||
|
||||
**Default**: empty
|
||||
|
||||
Allows administrators to define MCP servers that are **always injected** into
|
||||
the user's environment. Unlike the allowlist (which filters user-configured
|
||||
servers), required servers are automatically added regardless of the user's
|
||||
local configuration.
|
||||
|
||||
**Required Servers Format:**
|
||||
|
||||
```json
|
||||
{
|
||||
"requiredMcpServers": {
|
||||
"corp-compliance-tool": {
|
||||
"url": "https://mcp.corp/compliance",
|
||||
"type": "http",
|
||||
"trust": true,
|
||||
"description": "Corporate compliance tool"
|
||||
},
|
||||
"internal-registry": {
|
||||
"url": "https://registry.corp/mcp",
|
||||
"type": "sse",
|
||||
"authProviderType": "google_credentials",
|
||||
"oauth": {
|
||||
"scopes": ["https://www.googleapis.com/auth/scope"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Supported Fields:**
|
||||
|
||||
- `url`: (Required) The full URL of the MCP server endpoint.
|
||||
- `type`: (Required) The connection type (`sse` or `http`).
|
||||
- `trust`: (Optional) If set to `true`, tool execution will not require user
|
||||
approval. Defaults to `true` for required servers.
|
||||
- `description`: (Optional) Human-readable description of the server.
|
||||
- `authProviderType`: (Optional) Authentication provider (`dynamic_discovery`,
|
||||
`google_credentials`, or `service_account_impersonation`).
|
||||
- `oauth`: (Optional) OAuth configuration including `scopes`, `clientId`, and
|
||||
`clientSecret`.
|
||||
- `targetAudience`: (Optional) OAuth target audience for service-to-service
|
||||
auth.
|
||||
- `targetServiceAccount`: (Optional) Service account email to impersonate.
|
||||
- `headers`: (Optional) Additional HTTP headers to send with requests.
|
||||
- `includeTools` / `excludeTools`: (Optional) Tool filtering lists.
|
||||
- `timeout`: (Optional) Timeout in milliseconds for MCP requests.
|
||||
|
||||
**Client Enforcement Logic:**
|
||||
|
||||
- Required servers are injected **after** allowlist filtering, so they are
|
||||
always available even if the allowlist is active.
|
||||
- If a required server has the **same name** as a locally configured server, the
|
||||
admin configuration **completely overrides** the local one.
|
||||
- Required servers only support remote transports (`sse`, `http`). Local
|
||||
execution fields (`command`, `args`, `env`, `cwd`) are not supported.
|
||||
- Required servers can coexist with allowlisted servers — both features work
|
||||
independently.
|
||||
|
||||
### Unmanaged Capabilities
|
||||
|
||||
**Enabled/Disabled** | Default: disabled
|
||||
|
||||
|
After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 120 KiB |
@@ -18,6 +18,85 @@ on GitHub.
|
||||
| [Preview](preview.md) | Experimental features ready for early feedback. |
|
||||
| [Stable](latest.md) | Stable, recommended for general use. |
|
||||
|
||||
## Announcements: v0.36.0 - 2026-04-01
|
||||
|
||||
- **Multi-Registry Architecture and Sandboxing:** Introduced a multi-registry
|
||||
architecture and implemented native macOS Seatbelt and Windows sandboxing for
|
||||
enhanced subagent security
|
||||
([#22712](https://github.com/google-gemini/gemini-cli/pull/22712),
|
||||
[#22718](https://github.com/google-gemini/gemini-cli/pull/22718) by @akh64bit,
|
||||
[#22832](https://github.com/google-gemini/gemini-cli/pull/22832) by @ehedlund,
|
||||
[#21807](https://github.com/google-gemini/gemini-cli/pull/21807) by
|
||||
@mattKorwel).
|
||||
- **Refreshed Composer UX:** Implemented a refreshed user experience for the
|
||||
Composer layout and improved terminal interaction robustness
|
||||
([#21212](https://github.com/google-gemini/gemini-cli/pull/21212),
|
||||
[#23286](https://github.com/google-gemini/gemini-cli/pull/23286) by
|
||||
@jwhelangoog).
|
||||
- **Git Worktree Support:** Added native support for Git worktrees, allowing for
|
||||
isolated parallel sessions
|
||||
([#22973](https://github.com/google-gemini/gemini-cli/pull/22973),
|
||||
[#23265](https://github.com/google-gemini/gemini-cli/pull/23265) by @jerop).
|
||||
- **Subagent Context and Feedback:** Enhanced subagents with JIT context
|
||||
injection and resilient tool rejection with contextual feedback
|
||||
([#23032](https://github.com/google-gemini/gemini-cli/pull/23032),
|
||||
[#22951](https://github.com/google-gemini/gemini-cli/pull/22951) by
|
||||
@abhipatel12).
|
||||
|
||||
## Announcements: v0.35.0 - 2026-03-24
|
||||
|
||||
- **Customizable Keyboard Shortcuts:** Users can now customize their keyboard
|
||||
shortcuts, including support for literal character keybindings and the
|
||||
extended Kitty protocol
|
||||
([#21945](https://github.com/google-gemini/gemini-cli/pull/21945),
|
||||
[#21972](https://github.com/google-gemini/gemini-cli/pull/21972) by
|
||||
@scidomino).
|
||||
- **Vim Mode Improvements:** Added missing motions (X, ~, r, f/F/t/T) and
|
||||
yank/paste support with the unnamed register
|
||||
([#21932](https://github.com/google-gemini/gemini-cli/pull/21932),
|
||||
[#22026](https://github.com/google-gemini/gemini-cli/pull/22026) by @aanari).
|
||||
- **Tool Isolation and Sandboxing:** Introduced `SandboxManager` to isolate
|
||||
process-spawning tools and added Linux bubblewrap/seccomp sandboxing support
|
||||
([#21774](https://github.com/google-gemini/gemini-cli/pull/21774),
|
||||
[#22231](https://github.com/google-gemini/gemini-cli/pull/22231) by @galz10,
|
||||
[#22680](https://github.com/google-gemini/gemini-cli/pull/22680) by
|
||||
@DavidAPierce).
|
||||
- **JIT Context Discovery:** Implemented Just-In-Time context discovery for file
|
||||
system tools to improve model performance and accuracy
|
||||
([#22082](https://github.com/google-gemini/gemini-cli/pull/22082),
|
||||
[#22736](https://github.com/google-gemini/gemini-cli/pull/22736) by
|
||||
@SandyTao520).
|
||||
|
||||
## Announcements: v0.34.0 - 2026-03-17
|
||||
|
||||
- **Plan Mode Enabled by Default:** Plan Mode is now enabled by default to help
|
||||
you break down complex tasks and execute them systematically
|
||||
([#21713](https://github.com/google-gemini/gemini-cli/pull/21713) by @jerop).
|
||||
- **Sandboxing Enhancements:** We've added native gVisor (runsc) and
|
||||
experimental LXC container sandboxing support for safer execution environments
|
||||
([#21062](https://github.com/google-gemini/gemini-cli/pull/21062) by
|
||||
@Zheyuan-Lin, [#20735](https://github.com/google-gemini/gemini-cli/pull/20735)
|
||||
by @h30s).
|
||||
|
||||
## Announcements: v0.33.0 - 2026-03-11
|
||||
|
||||
- **Agent Architecture Enhancements:** Introduced HTTP authentication for A2A
|
||||
remote agents and authenticated A2A agent card discovery
|
||||
([#20510](https://github.com/google-gemini/gemini-cli/pull/20510) by
|
||||
@SandyTao520, [#20622](https://github.com/google-gemini/gemini-cli/pull/20622)
|
||||
by @SandyTao520).
|
||||
- **Plan Mode Updates:** Expanded Plan Mode with built-in research subagents,
|
||||
annotation support for feedback, and a new `copy` subcommand
|
||||
([#20972](https://github.com/google-gemini/gemini-cli/pull/20972) by @Adib234,
|
||||
[#20988](https://github.com/google-gemini/gemini-cli/pull/20988) by
|
||||
@ruomengz).
|
||||
- **CLI UX & Admin Controls:** Redesigned the header to be compact with an ASCII
|
||||
icon, inverted context window display to show usage, and enabled a 30-day
|
||||
default retention for chat history
|
||||
([#18713](https://github.com/google-gemini/gemini-cli/pull/18713) by
|
||||
@keithguerin, [#20853](https://github.com/google-gemini/gemini-cli/pull/20853)
|
||||
by @skeshive).
|
||||
|
||||
## Announcements: v0.32.0 - 2026-03-03
|
||||
|
||||
- **Generalist Agent:** The generalist agent is now enabled to improve task
|
||||
@@ -106,10 +185,6 @@ on GitHub.
|
||||
|
||||
## Announcements: v0.28.0 - 2026-02-10
|
||||
|
||||
- **Slash Command:** We've added a new `/prompt-suggest` slash command to help
|
||||
you generate prompt suggestions
|
||||
([#17264](https://github.com/google-gemini/gemini-cli/pull/17264) by
|
||||
@NTaylorMullen).
|
||||
- **IDE Support:** Gemini CLI now supports the Positron IDE
|
||||
([#15047](https://github.com/google-gemini/gemini-cli/pull/15047) by
|
||||
@kapsner).
|
||||
@@ -149,8 +224,8 @@ on GitHub.
|
||||
([#16638](https://github.com/google-gemini/gemini-cli/pull/16638) by
|
||||
@joshualitt).
|
||||
- **UI/UX Improvements:** You can now "Rewind" through your conversation history
|
||||
([#15717](https://github.com/google-gemini/gemini-cli/pull/15717) by @Adib234)
|
||||
and use a new `/introspect` command for debugging.
|
||||
([#15717](https://github.com/google-gemini/gemini-cli/pull/15717) by
|
||||
@Adib234).
|
||||
- **Core and Scheduler Refactoring:** The core scheduler has been significantly
|
||||
refactored to improve performance and reliability
|
||||
([#16895](https://github.com/google-gemini/gemini-cli/pull/16895) by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Latest stable release: v0.32.1
|
||||
# Latest stable release: v0.36.0
|
||||
|
||||
Released: March 4, 2026
|
||||
Released: April 1, 2026
|
||||
|
||||
For most users, our latest stable release is the recommended release. Install
|
||||
the latest stable version with:
|
||||
@@ -11,198 +11,372 @@ npm install -g @google/gemini-cli
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Plan Mode Enhancements**: Significant updates to Plan Mode, including the
|
||||
ability to open and modify plans in an external editor, adaptations for
|
||||
complex tasks with multi-select options, and integration tests for plan mode.
|
||||
- **Agent and Steering Improvements**: The generalist agent has been enabled to
|
||||
enhance task delegation, model steering is now supported directly within the
|
||||
workspace, and contiguous parallel admission is enabled for `Kind.Agent`
|
||||
tools.
|
||||
- **Interactive Shell**: Interactive shell autocompletion has been introduced,
|
||||
significantly enhancing the user experience.
|
||||
- **Core Stability and Performance**: Extensions are now loaded in parallel,
|
||||
fetch timeouts have been increased, robust A2A streaming reassembly was
|
||||
implemented, and orphaned processes when terminal closes have been prevented.
|
||||
- **Billing and Quota Handling**: Implemented G1 AI credits overage flow with
|
||||
billing telemetry and added support for quota error fallbacks across all
|
||||
authentication types.
|
||||
- **Multi-Registry Architecture and Tool Isolation:** Introduced a
|
||||
multi-registry architecture for subagents and implemented strict sandboxing
|
||||
for macOS (Seatbelt) and Windows to enhance security and isolation.
|
||||
- **Improved Subagent Coordination:** Enhanced subagents with local execution
|
||||
capabilities, JIT context injection (upward traversal capped at git root), and
|
||||
resilient tool rejection with contextual feedback.
|
||||
- **Enhanced UI and UX:** Implemented a refreshed UX for the Composer layout,
|
||||
improved terminal fallback warnings, and resolved various UI flickering and
|
||||
state persistence issues.
|
||||
- **Git Worktree Support:** Added support for Git worktrees to enable isolated
|
||||
parallel sessions within the same repository.
|
||||
- **Plan Mode Improvements:** Plan mode now supports non-interactive execution
|
||||
and includes hardened sandbox path resolution to prevent hallucinations.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- fix(patch): cherry-pick 0659ad1 to release/v0.32.0-pr-21042 to patch version
|
||||
v0.32.0 and create version 0.32.1 by @gemini-cli-robot in
|
||||
[#21048](https://github.com/google-gemini/gemini-cli/pull/21048)
|
||||
- feat(plan): add integration tests for plan mode by @Adib234 in
|
||||
[#20214](https://github.com/google-gemini/gemini-cli/pull/20214)
|
||||
- fix(acp): update auth handshake to spec by @skeshive in
|
||||
[#19725](https://github.com/google-gemini/gemini-cli/pull/19725)
|
||||
- feat(core): implement robust A2A streaming reassembly and fix task continuity
|
||||
by @adamfweidman in
|
||||
[#20091](https://github.com/google-gemini/gemini-cli/pull/20091)
|
||||
- feat(cli): load extensions in parallel by @scidomino in
|
||||
[#20229](https://github.com/google-gemini/gemini-cli/pull/20229)
|
||||
- Plumb the maxAttempts setting through Config args by @kevinjwang1 in
|
||||
[#20239](https://github.com/google-gemini/gemini-cli/pull/20239)
|
||||
- fix(cli): skip 404 errors in setup-github file downloads by @h30s in
|
||||
[#20287](https://github.com/google-gemini/gemini-cli/pull/20287)
|
||||
- fix(cli): expose model.name setting in settings dialog for persistence by
|
||||
@achaljhawar in
|
||||
[#19605](https://github.com/google-gemini/gemini-cli/pull/19605)
|
||||
- docs: remove legacy cmd examples in favor of powershell by @scidomino in
|
||||
[#20323](https://github.com/google-gemini/gemini-cli/pull/20323)
|
||||
- feat(core): Enable model steering in workspace. by @joshualitt in
|
||||
[#20343](https://github.com/google-gemini/gemini-cli/pull/20343)
|
||||
- fix: remove trailing comma in issue triage workflow settings json by @Nixxx19
|
||||
in [#20265](https://github.com/google-gemini/gemini-cli/pull/20265)
|
||||
- feat(core): implement task tracker foundation and service by @anj-s in
|
||||
[#19464](https://github.com/google-gemini/gemini-cli/pull/19464)
|
||||
- test: support tests that include color information by @jacob314 in
|
||||
[#20220](https://github.com/google-gemini/gemini-cli/pull/20220)
|
||||
- feat(core): introduce Kind.Agent for sub-agent classification by @abhipatel12
|
||||
in [#20369](https://github.com/google-gemini/gemini-cli/pull/20369)
|
||||
- Changelog for v0.30.0 by @gemini-cli-robot in
|
||||
[#20252](https://github.com/google-gemini/gemini-cli/pull/20252)
|
||||
- Update changelog workflow to reject nightly builds by @g-samroberts in
|
||||
[#20248](https://github.com/google-gemini/gemini-cli/pull/20248)
|
||||
- Changelog for v0.31.0-preview.0 by @gemini-cli-robot in
|
||||
[#20249](https://github.com/google-gemini/gemini-cli/pull/20249)
|
||||
- feat(cli): hide workspace policy update dialog and auto-accept by default by
|
||||
@Abhijit-2592 in
|
||||
[#20351](https://github.com/google-gemini/gemini-cli/pull/20351)
|
||||
- feat(core): rename grep_search include parameter to include_pattern by
|
||||
- Changelog for v0.33.2 by @gemini-cli-robot in
|
||||
[#22730](https://github.com/google-gemini/gemini-cli/pull/22730)
|
||||
- feat(core): multi-registry architecture and tool filtering for subagents by
|
||||
@akh64bit in [#22712](https://github.com/google-gemini/gemini-cli/pull/22712)
|
||||
- Changelog for v0.34.0-preview.4 by @gemini-cli-robot in
|
||||
[#22752](https://github.com/google-gemini/gemini-cli/pull/22752)
|
||||
- fix(devtools): use theme-aware text colors for console warnings and errors by
|
||||
@SandyTao520 in
|
||||
[#20328](https://github.com/google-gemini/gemini-cli/pull/20328)
|
||||
- feat(plan): support opening and modifying plan in external editor by @Adib234
|
||||
in [#20348](https://github.com/google-gemini/gemini-cli/pull/20348)
|
||||
- feat(cli): implement interactive shell autocompletion by @mrpmohiburrahman in
|
||||
[#20082](https://github.com/google-gemini/gemini-cli/pull/20082)
|
||||
- fix(core): allow /memory add to work in plan mode by @Jefftree in
|
||||
[#20353](https://github.com/google-gemini/gemini-cli/pull/20353)
|
||||
- feat(core): add HTTP 499 to retryable errors and map to RetryableQuotaError by
|
||||
@bdmorgan in [#20432](https://github.com/google-gemini/gemini-cli/pull/20432)
|
||||
- feat(core): Enable generalist agent by @joshualitt in
|
||||
[#19665](https://github.com/google-gemini/gemini-cli/pull/19665)
|
||||
- Updated tests in TableRenderer.test.tsx to use SVG snapshots by @devr0306 in
|
||||
[#20450](https://github.com/google-gemini/gemini-cli/pull/20450)
|
||||
- Refactor Github Action per b/485167538 by @google-admin in
|
||||
[#19443](https://github.com/google-gemini/gemini-cli/pull/19443)
|
||||
- fix(github): resolve actionlint and yamllint regressions from #19443 by @jerop
|
||||
in [#20467](https://github.com/google-gemini/gemini-cli/pull/20467)
|
||||
- fix: action var usage by @galz10 in
|
||||
[#20492](https://github.com/google-gemini/gemini-cli/pull/20492)
|
||||
- feat(core): improve A2A content extraction by @adamfweidman in
|
||||
[#20487](https://github.com/google-gemini/gemini-cli/pull/20487)
|
||||
- fix(cli): support quota error fallbacks for all authentication types by
|
||||
@sehoon38 in [#20475](https://github.com/google-gemini/gemini-cli/pull/20475)
|
||||
- fix(core): flush transcript for pure tool-call responses to ensure BeforeTool
|
||||
hooks see complete state by @krishdef7 in
|
||||
[#20419](https://github.com/google-gemini/gemini-cli/pull/20419)
|
||||
- feat(plan): adapt planning workflow based on complexity of task by @jerop in
|
||||
[#20465](https://github.com/google-gemini/gemini-cli/pull/20465)
|
||||
- fix: prevent orphaned processes from consuming 100% CPU when terminal closes
|
||||
by @yuvrajangadsingh in
|
||||
[#16965](https://github.com/google-gemini/gemini-cli/pull/16965)
|
||||
- feat(core): increase fetch timeout and fix [object Object] error
|
||||
stringification by @bdmorgan in
|
||||
[#20441](https://github.com/google-gemini/gemini-cli/pull/20441)
|
||||
- [Gemma x Gemini CLI] Add an Experimental Gemma Router that uses a LiteRT-LM
|
||||
shim into the Composite Model Classifier Strategy by @sidwan02 in
|
||||
[#17231](https://github.com/google-gemini/gemini-cli/pull/17231)
|
||||
- docs(plan): update documentation regarding supporting editing of plan files
|
||||
during plan approval by @Adib234 in
|
||||
[#20452](https://github.com/google-gemini/gemini-cli/pull/20452)
|
||||
- test(cli): fix flaky ToolResultDisplay overflow test by @jwhelangoog in
|
||||
[#20518](https://github.com/google-gemini/gemini-cli/pull/20518)
|
||||
- ui(cli): reduce length of Ctrl+O hint by @jwhelangoog in
|
||||
[#20490](https://github.com/google-gemini/gemini-cli/pull/20490)
|
||||
- fix(ui): correct styled table width calculations by @devr0306 in
|
||||
[#20042](https://github.com/google-gemini/gemini-cli/pull/20042)
|
||||
- Avoid overaggressive unescaping by @scidomino in
|
||||
[#20520](https://github.com/google-gemini/gemini-cli/pull/20520)
|
||||
- feat(telemetry) Instrument traces with more attributes and make them available
|
||||
to OTEL users by @heaventourist in
|
||||
[#20237](https://github.com/google-gemini/gemini-cli/pull/20237)
|
||||
- Add support for policy engine in extensions by @chrstnb in
|
||||
[#20049](https://github.com/google-gemini/gemini-cli/pull/20049)
|
||||
- Docs: Update to Terms of Service & FAQ by @jkcinouye in
|
||||
[#20488](https://github.com/google-gemini/gemini-cli/pull/20488)
|
||||
- Fix bottom border rendering for search and add a regression test. by @jacob314
|
||||
in [#20517](https://github.com/google-gemini/gemini-cli/pull/20517)
|
||||
- fix(core): apply retry logic to CodeAssistServer for all users by @bdmorgan in
|
||||
[#20507](https://github.com/google-gemini/gemini-cli/pull/20507)
|
||||
- Fix extension MCP server env var loading by @chrstnb in
|
||||
[#20374](https://github.com/google-gemini/gemini-cli/pull/20374)
|
||||
- feat(ui): add 'ctrl+o' hint to truncated content message by @jerop in
|
||||
[#20529](https://github.com/google-gemini/gemini-cli/pull/20529)
|
||||
- Fix flicker showing message to press ctrl-O again to collapse. by @jacob314 in
|
||||
[#20414](https://github.com/google-gemini/gemini-cli/pull/20414)
|
||||
- fix(cli): hide shortcuts hint while model is thinking or the user has typed a
|
||||
prompt + add debounce to avoid flicker by @jacob314 in
|
||||
[#19389](https://github.com/google-gemini/gemini-cli/pull/19389)
|
||||
- feat(plan): update planning workflow to encourage multi-select with
|
||||
descriptions of options by @Adib234 in
|
||||
[#20491](https://github.com/google-gemini/gemini-cli/pull/20491)
|
||||
- refactor(core,cli): useAlternateBuffer read from config by @psinha40898 in
|
||||
[#20346](https://github.com/google-gemini/gemini-cli/pull/20346)
|
||||
- fix(cli): ensure dialogs stay scrolled to bottom in alternate buffer mode by
|
||||
@jacob314 in [#20527](https://github.com/google-gemini/gemini-cli/pull/20527)
|
||||
- fix(core): revert auto-save of policies to user space by @Abhijit-2592 in
|
||||
[#20531](https://github.com/google-gemini/gemini-cli/pull/20531)
|
||||
- Demote unreliable test. by @gundermanc in
|
||||
[#20571](https://github.com/google-gemini/gemini-cli/pull/20571)
|
||||
- fix(core): handle optional response fields from code assist API by @sehoon38
|
||||
in [#20345](https://github.com/google-gemini/gemini-cli/pull/20345)
|
||||
- fix(cli): keep thought summary when loading phrases are off by @LyalinDotCom
|
||||
in [#20497](https://github.com/google-gemini/gemini-cli/pull/20497)
|
||||
- feat(cli): add temporary flag to disable workspace policies by @Abhijit-2592
|
||||
in [#20523](https://github.com/google-gemini/gemini-cli/pull/20523)
|
||||
- Disable expensive and scheduled workflows on personal forks by @dewitt in
|
||||
[#20449](https://github.com/google-gemini/gemini-cli/pull/20449)
|
||||
- Moved markdown parsing logic to a separate util file by @devr0306 in
|
||||
[#20526](https://github.com/google-gemini/gemini-cli/pull/20526)
|
||||
- fix(plan): prevent agent from using ask_user for shell command confirmation by
|
||||
@Adib234 in [#20504](https://github.com/google-gemini/gemini-cli/pull/20504)
|
||||
- fix(core): disable retries for code assist streaming requests by @sehoon38 in
|
||||
[#20561](https://github.com/google-gemini/gemini-cli/pull/20561)
|
||||
- feat(billing): implement G1 AI credits overage flow with billing telemetry by
|
||||
@gsquared94 in
|
||||
[#18590](https://github.com/google-gemini/gemini-cli/pull/18590)
|
||||
- feat: better error messages by @gsquared94 in
|
||||
[#20577](https://github.com/google-gemini/gemini-cli/pull/20577)
|
||||
- fix(ui): persist expansion in AskUser dialog when navigating options by @jerop
|
||||
in [#20559](https://github.com/google-gemini/gemini-cli/pull/20559)
|
||||
- fix(cli): prevent sub-agent tool calls from leaking into UI by @abhipatel12 in
|
||||
[#20580](https://github.com/google-gemini/gemini-cli/pull/20580)
|
||||
- fix(cli): Shell autocomplete polish by @jacob314 in
|
||||
[#20411](https://github.com/google-gemini/gemini-cli/pull/20411)
|
||||
- Changelog for v0.31.0-preview.1 by @gemini-cli-robot in
|
||||
[#20590](https://github.com/google-gemini/gemini-cli/pull/20590)
|
||||
- Add slash command for promoting behavioral evals to CI blocking by @gundermanc
|
||||
in [#20575](https://github.com/google-gemini/gemini-cli/pull/20575)
|
||||
- Changelog for v0.30.1 by @gemini-cli-robot in
|
||||
[#20589](https://github.com/google-gemini/gemini-cli/pull/20589)
|
||||
- Add low/full CLI error verbosity mode for cleaner UI by @LyalinDotCom in
|
||||
[#20399](https://github.com/google-gemini/gemini-cli/pull/20399)
|
||||
- Disable Gemini PR reviews on draft PRs. by @gundermanc in
|
||||
[#20362](https://github.com/google-gemini/gemini-cli/pull/20362)
|
||||
- Docs: FAQ update by @jkcinouye in
|
||||
[#20585](https://github.com/google-gemini/gemini-cli/pull/20585)
|
||||
- fix(core): reduce intrusive MCP errors and deduplicate diagnostics by
|
||||
[#22181](https://github.com/google-gemini/gemini-cli/pull/22181)
|
||||
- Add support for dynamic model Resolution to ModelConfigService by @kevinjwang1
|
||||
in [#22578](https://github.com/google-gemini/gemini-cli/pull/22578)
|
||||
- chore(release): bump version to 0.36.0-nightly.20260317.2f90b4653 by
|
||||
@gemini-cli-robot in
|
||||
[#22858](https://github.com/google-gemini/gemini-cli/pull/22858)
|
||||
- fix(cli): use active sessionId in useLogger and improve resume robustness by
|
||||
@mattKorwel in
|
||||
[#22606](https://github.com/google-gemini/gemini-cli/pull/22606)
|
||||
- fix(cli): expand tilde in policy paths from settings.json by @abhipatel12 in
|
||||
[#22772](https://github.com/google-gemini/gemini-cli/pull/22772)
|
||||
- fix(core): add actionable warnings for terminal fallbacks (#14426) by
|
||||
@spencer426 in
|
||||
[#20232](https://github.com/google-gemini/gemini-cli/pull/20232)
|
||||
- docs: fix spelling typos in installation guide by @campox747 in
|
||||
[#20579](https://github.com/google-gemini/gemini-cli/pull/20579)
|
||||
- Promote stable tests to CI blocking. by @gundermanc in
|
||||
[#20581](https://github.com/google-gemini/gemini-cli/pull/20581)
|
||||
- feat(core): enable contiguous parallel admission for Kind.Agent tools by
|
||||
[#22211](https://github.com/google-gemini/gemini-cli/pull/22211)
|
||||
- feat(tracker): integrate task tracker protocol into core system prompt by
|
||||
@anj-s in [#22442](https://github.com/google-gemini/gemini-cli/pull/22442)
|
||||
- chore: add posttest build hooks and fix missing dependencies by @NTaylorMullen
|
||||
in [#22865](https://github.com/google-gemini/gemini-cli/pull/22865)
|
||||
- feat(a2a): add agent acknowledgment command and enhance registry discovery by
|
||||
@alisa-alisa in
|
||||
[#22389](https://github.com/google-gemini/gemini-cli/pull/22389)
|
||||
- fix(cli): automatically add all VSCode workspace folders to Gemini context by
|
||||
@sakshisemalti in
|
||||
[#21380](https://github.com/google-gemini/gemini-cli/pull/21380)
|
||||
- feat: add 'blocked' status to tasks and todos by @anj-s in
|
||||
[#22735](https://github.com/google-gemini/gemini-cli/pull/22735)
|
||||
- refactor(cli): remove extra newlines in ShellToolMessage.tsx by @NTaylorMullen
|
||||
in [#22868](https://github.com/google-gemini/gemini-cli/pull/22868)
|
||||
- fix(cli): lazily load settings in onModelChange to prevent stale closure data
|
||||
loss by @KumarADITHYA123 in
|
||||
[#20403](https://github.com/google-gemini/gemini-cli/pull/20403)
|
||||
- feat(core): subagent local execution and tool isolation by @akh64bit in
|
||||
[#22718](https://github.com/google-gemini/gemini-cli/pull/22718)
|
||||
- fix(cli): resolve subagent grouping and UI state persistence by @abhipatel12
|
||||
in [#22252](https://github.com/google-gemini/gemini-cli/pull/22252)
|
||||
- refactor(ui): extract SessionBrowser search and navigation components by
|
||||
@abhipatel12 in
|
||||
[#20583](https://github.com/google-gemini/gemini-cli/pull/20583)
|
||||
- Enforce import/no-duplicates as error by @Nixxx19 in
|
||||
[#19797](https://github.com/google-gemini/gemini-cli/pull/19797)
|
||||
- fix: merge duplicate imports in sdk and test-utils packages (1/4) by @Nixxx19
|
||||
in [#19777](https://github.com/google-gemini/gemini-cli/pull/19777)
|
||||
- fix: merge duplicate imports in a2a-server package (2/4) by @Nixxx19 in
|
||||
[#19781](https://github.com/google-gemini/gemini-cli/pull/19781)
|
||||
[#22377](https://github.com/google-gemini/gemini-cli/pull/22377)
|
||||
- fix: updates Docker image reference for GitHub MCP server by @jhhornn in
|
||||
[#22938](https://github.com/google-gemini/gemini-cli/pull/22938)
|
||||
- refactor(cli): group subagent trajectory deletion and use native filesystem
|
||||
testing by @abhipatel12 in
|
||||
[#22890](https://github.com/google-gemini/gemini-cli/pull/22890)
|
||||
- refactor(cli): simplify keypress and mouse providers and update tests by
|
||||
@scidomino in [#22853](https://github.com/google-gemini/gemini-cli/pull/22853)
|
||||
- Changelog for v0.34.0 by @gemini-cli-robot in
|
||||
[#22860](https://github.com/google-gemini/gemini-cli/pull/22860)
|
||||
- test(cli): simplify createMockSettings calls by @scidomino in
|
||||
[#22952](https://github.com/google-gemini/gemini-cli/pull/22952)
|
||||
- feat(ui): format multi-line banner warnings with a bold title by @keithguerin
|
||||
in [#22955](https://github.com/google-gemini/gemini-cli/pull/22955)
|
||||
- Docs: Remove references to stale Gemini CLI file structure info by
|
||||
@g-samroberts in
|
||||
[#22976](https://github.com/google-gemini/gemini-cli/pull/22976)
|
||||
- feat(ui): remove write todo list tool from UI tips by @aniruddhaadak80 in
|
||||
[#22281](https://github.com/google-gemini/gemini-cli/pull/22281)
|
||||
- Fix issue where subagent thoughts are appended. by @gundermanc in
|
||||
[#22975](https://github.com/google-gemini/gemini-cli/pull/22975)
|
||||
- Feat/browser privacy consent by @kunal-10-cloud in
|
||||
[#21119](https://github.com/google-gemini/gemini-cli/pull/21119)
|
||||
- fix(core): explicitly map execution context in LocalAgentExecutor by @akh64bit
|
||||
in [#22949](https://github.com/google-gemini/gemini-cli/pull/22949)
|
||||
- feat(plan): support plan mode in non-interactive mode by @ruomengz in
|
||||
[#22670](https://github.com/google-gemini/gemini-cli/pull/22670)
|
||||
- feat(core): implement strict macOS sandboxing using Seatbelt allowlist by
|
||||
@ehedlund in [#22832](https://github.com/google-gemini/gemini-cli/pull/22832)
|
||||
- docs: add additional notes by @abhipatel12 in
|
||||
[#23008](https://github.com/google-gemini/gemini-cli/pull/23008)
|
||||
- fix(cli): resolve duplicate footer on tool cancel via ESC (#21743) by
|
||||
@ruomengz in [#21781](https://github.com/google-gemini/gemini-cli/pull/21781)
|
||||
- Changelog for v0.35.0-preview.1 by @gemini-cli-robot in
|
||||
[#23012](https://github.com/google-gemini/gemini-cli/pull/23012)
|
||||
- fix(ui): fix flickering on small terminal heights by @devr0306 in
|
||||
[#21416](https://github.com/google-gemini/gemini-cli/pull/21416)
|
||||
- fix(acp): provide more meta in tool_call_update by @Mervap in
|
||||
[#22663](https://github.com/google-gemini/gemini-cli/pull/22663)
|
||||
- docs: add FAQ entry for checking Gemini CLI version by @surajsahani in
|
||||
[#21271](https://github.com/google-gemini/gemini-cli/pull/21271)
|
||||
- feat(core): resilient subagent tool rejection with contextual feedback by
|
||||
@abhipatel12 in
|
||||
[#22951](https://github.com/google-gemini/gemini-cli/pull/22951)
|
||||
- fix(cli): correctly handle auto-update for standalone binaries by @bdmorgan in
|
||||
[#23038](https://github.com/google-gemini/gemini-cli/pull/23038)
|
||||
- feat(core): add content-utils by @adamfweidman in
|
||||
[#22984](https://github.com/google-gemini/gemini-cli/pull/22984)
|
||||
- fix: circumvent genai sdk requirement for api key when using gateway auth via
|
||||
ACP by @sripasg in
|
||||
[#23042](https://github.com/google-gemini/gemini-cli/pull/23042)
|
||||
- fix(core): don't persist browser consent sentinel in non-interactive mode by
|
||||
@jasonmatthewsuhari in
|
||||
[#23073](https://github.com/google-gemini/gemini-cli/pull/23073)
|
||||
- fix(core): narrow browser agent description to prevent stealing URL tasks from
|
||||
web_fetch by @gsquared94 in
|
||||
[#23086](https://github.com/google-gemini/gemini-cli/pull/23086)
|
||||
- feat(cli): Partial threading of AgentLoopContext. by @joshualitt in
|
||||
[#22978](https://github.com/google-gemini/gemini-cli/pull/22978)
|
||||
- fix(browser-agent): enable "Allow all server tools" session policy by
|
||||
@cynthialong0-0 in
|
||||
[#22343](https://github.com/google-gemini/gemini-cli/pull/22343)
|
||||
- refactor(cli): integrate real config loading into async test utils by
|
||||
@scidomino in [#23040](https://github.com/google-gemini/gemini-cli/pull/23040)
|
||||
- feat(core): inject memory and JIT context into subagents by @abhipatel12 in
|
||||
[#23032](https://github.com/google-gemini/gemini-cli/pull/23032)
|
||||
- Fix logging and virtual list. by @jacob314 in
|
||||
[#23080](https://github.com/google-gemini/gemini-cli/pull/23080)
|
||||
- feat(core): cap JIT context upward traversal at git root by @SandyTao520 in
|
||||
[#23074](https://github.com/google-gemini/gemini-cli/pull/23074)
|
||||
- Docs: Minor style updates from initial docs audit. by @g-samroberts in
|
||||
[#22872](https://github.com/google-gemini/gemini-cli/pull/22872)
|
||||
- feat(core): add experimental memory manager agent to replace save_memory tool
|
||||
by @SandyTao520 in
|
||||
[#22726](https://github.com/google-gemini/gemini-cli/pull/22726)
|
||||
- Changelog for v0.35.0-preview.2 by @gemini-cli-robot in
|
||||
[#23142](https://github.com/google-gemini/gemini-cli/pull/23142)
|
||||
- Update website issue template for label and title by @g-samroberts in
|
||||
[#23036](https://github.com/google-gemini/gemini-cli/pull/23036)
|
||||
- fix: upgrade ACP SDK from 0.12 to 0.16.1 by @sripasg in
|
||||
[#23132](https://github.com/google-gemini/gemini-cli/pull/23132)
|
||||
- Update callouts to work on github. by @g-samroberts in
|
||||
[#22245](https://github.com/google-gemini/gemini-cli/pull/22245)
|
||||
- feat: ACP: Add token usage metadata to the `send` method's return value by
|
||||
@sripasg in [#23148](https://github.com/google-gemini/gemini-cli/pull/23148)
|
||||
- fix(plan): clarify that plan mode policies are combined with normal mode by
|
||||
@ruomengz in [#23158](https://github.com/google-gemini/gemini-cli/pull/23158)
|
||||
- Add ModelChain support to ModelConfigService and make ModelDialog dynamic by
|
||||
@kevinjwang1 in
|
||||
[#22914](https://github.com/google-gemini/gemini-cli/pull/22914)
|
||||
- Ensure that copied extensions are writable in the user's local directory by
|
||||
@kevinjwang1 in
|
||||
[#23016](https://github.com/google-gemini/gemini-cli/pull/23016)
|
||||
- feat(core): implement native Windows sandboxing by @mattKorwel in
|
||||
[#21807](https://github.com/google-gemini/gemini-cli/pull/21807)
|
||||
- feat(core): add support for admin-forced MCP server installations by
|
||||
@gsquared94 in
|
||||
[#23163](https://github.com/google-gemini/gemini-cli/pull/23163)
|
||||
- chore(lint): ignore .gemini directory and recursive node_modules by
|
||||
@mattKorwel in
|
||||
[#23211](https://github.com/google-gemini/gemini-cli/pull/23211)
|
||||
- feat(cli): conditionally exclude ask_user tool in ACP mode by @nmcnamara-eng
|
||||
in [#23045](https://github.com/google-gemini/gemini-cli/pull/23045)
|
||||
- feat(core): introduce AgentSession and rename stream events to agent events by
|
||||
@mbleigh in [#23159](https://github.com/google-gemini/gemini-cli/pull/23159)
|
||||
- feat(worktree): add Git worktree support for isolated parallel sessions by
|
||||
@jerop in [#22973](https://github.com/google-gemini/gemini-cli/pull/22973)
|
||||
- Add support for linking in the extension registry by @kevinjwang1 in
|
||||
[#23153](https://github.com/google-gemini/gemini-cli/pull/23153)
|
||||
- feat(extensions): add --skip-settings flag to install command by @Ratish1 in
|
||||
[#17212](https://github.com/google-gemini/gemini-cli/pull/17212)
|
||||
- feat(telemetry): track if session is running in a Git worktree by @jerop in
|
||||
[#23265](https://github.com/google-gemini/gemini-cli/pull/23265)
|
||||
- refactor(core): use absolute paths in GEMINI.md context markers by
|
||||
@SandyTao520 in
|
||||
[#23135](https://github.com/google-gemini/gemini-cli/pull/23135)
|
||||
- fix(core): add sanitization to sub agent thoughts and centralize utilities by
|
||||
@devr0306 in [#22828](https://github.com/google-gemini/gemini-cli/pull/22828)
|
||||
- feat(core): refine User-Agent for VS Code traffic (unified format) by
|
||||
@sehoon38 in [#23256](https://github.com/google-gemini/gemini-cli/pull/23256)
|
||||
- Fix schema for ModelChains by @kevinjwang1 in
|
||||
[#23284](https://github.com/google-gemini/gemini-cli/pull/23284)
|
||||
- test(cli): refactor tests for async render utilities by @scidomino in
|
||||
[#23252](https://github.com/google-gemini/gemini-cli/pull/23252)
|
||||
- feat(core): add security prompt for browser agent by @cynthialong0-0 in
|
||||
[#23241](https://github.com/google-gemini/gemini-cli/pull/23241)
|
||||
- refactor(ide): replace dynamic undici import with static fetch import by
|
||||
@cocosheng-g in
|
||||
[#23268](https://github.com/google-gemini/gemini-cli/pull/23268)
|
||||
- test(cli): address unresolved feedback from PR #23252 by @scidomino in
|
||||
[#23303](https://github.com/google-gemini/gemini-cli/pull/23303)
|
||||
- feat(browser): add sensitive action controls and read-only noise reduction by
|
||||
@cynthialong0-0 in
|
||||
[#22867](https://github.com/google-gemini/gemini-cli/pull/22867)
|
||||
- Disabling failing test while investigating by @alisa-alisa in
|
||||
[#23311](https://github.com/google-gemini/gemini-cli/pull/23311)
|
||||
- fix broken extension link in hooks guide by @Indrapal-70 in
|
||||
[#21728](https://github.com/google-gemini/gemini-cli/pull/21728)
|
||||
- fix(core): fix agent description indentation by @abhipatel12 in
|
||||
[#23315](https://github.com/google-gemini/gemini-cli/pull/23315)
|
||||
- Wrap the text under TOML rule for easier readability in policy-engine.md… by
|
||||
@CogitationOps in
|
||||
[#23076](https://github.com/google-gemini/gemini-cli/pull/23076)
|
||||
- fix(extensions): revert broken extension removal behavior by @ehedlund in
|
||||
[#23317](https://github.com/google-gemini/gemini-cli/pull/23317)
|
||||
- feat(core): set up onboarding telemetry by @yunaseoul in
|
||||
[#23118](https://github.com/google-gemini/gemini-cli/pull/23118)
|
||||
- Retry evals on API error. by @gundermanc in
|
||||
[#23322](https://github.com/google-gemini/gemini-cli/pull/23322)
|
||||
- fix(evals): remove tool restrictions and add compile-time guards by
|
||||
@SandyTao520 in
|
||||
[#23312](https://github.com/google-gemini/gemini-cli/pull/23312)
|
||||
- fix(hooks): support 'ask' decision for BeforeTool hooks by @gundermanc in
|
||||
[#21146](https://github.com/google-gemini/gemini-cli/pull/21146)
|
||||
- feat(browser): add warning message for session mode 'existing' by
|
||||
@cynthialong0-0 in
|
||||
[#23288](https://github.com/google-gemini/gemini-cli/pull/23288)
|
||||
- chore(lint): enforce zero warnings and cleanup syntax restrictions by
|
||||
@alisa-alisa in
|
||||
[#22902](https://github.com/google-gemini/gemini-cli/pull/22902)
|
||||
- fix(cli): add Esc instruction to HooksDialog footer by @abhipatel12 in
|
||||
[#23258](https://github.com/google-gemini/gemini-cli/pull/23258)
|
||||
- Disallow and suppress misused spread operator. by @gundermanc in
|
||||
[#23294](https://github.com/google-gemini/gemini-cli/pull/23294)
|
||||
- fix(core): refine CliHelpAgent description for better delegation by
|
||||
@abhipatel12 in
|
||||
[#23310](https://github.com/google-gemini/gemini-cli/pull/23310)
|
||||
- fix(core): enable global session and persistent approval for web_fetch by
|
||||
@NTaylorMullen in
|
||||
[#23295](https://github.com/google-gemini/gemini-cli/pull/23295)
|
||||
- fix(plan): add state transition override to prevent plan mode freeze by
|
||||
@Adib234 in [#23020](https://github.com/google-gemini/gemini-cli/pull/23020)
|
||||
- fix(cli): record skill activation tool calls in chat history by @NTaylorMullen
|
||||
in [#23203](https://github.com/google-gemini/gemini-cli/pull/23203)
|
||||
- fix(core): ensure subagent tool updates apply configuration overrides
|
||||
immediately by @abhipatel12 in
|
||||
[#23161](https://github.com/google-gemini/gemini-cli/pull/23161)
|
||||
- fix(cli): resolve flicker at boundaries of list in BaseSelectionList by
|
||||
@jackwotherspoon in
|
||||
[#23298](https://github.com/google-gemini/gemini-cli/pull/23298)
|
||||
- test(cli): force generic terminal in tests to fix snapshot failures by
|
||||
@abhipatel12 in
|
||||
[#23499](https://github.com/google-gemini/gemini-cli/pull/23499)
|
||||
- Evals: PR Guidance adding workflow by @alisa-alisa in
|
||||
[#23164](https://github.com/google-gemini/gemini-cli/pull/23164)
|
||||
- feat(core): refactor SandboxManager to a stateless architecture and introduce
|
||||
explicit Deny interface by @ehedlund in
|
||||
[#23141](https://github.com/google-gemini/gemini-cli/pull/23141)
|
||||
- feat(core): add event-translator and update agent types by @adamfweidman in
|
||||
[#22985](https://github.com/google-gemini/gemini-cli/pull/22985)
|
||||
- perf(cli): parallelize and background startup cleanup tasks by @sehoon38 in
|
||||
[#23545](https://github.com/google-gemini/gemini-cli/pull/23545)
|
||||
- fix: "allow always" for commands with paths by @scidomino in
|
||||
[#23558](https://github.com/google-gemini/gemini-cli/pull/23558)
|
||||
- fix(cli): prevent terminal escape sequences from leaking on exit by
|
||||
@mattKorwel in
|
||||
[#22682](https://github.com/google-gemini/gemini-cli/pull/22682)
|
||||
- feat(cli): implement full "GEMINI CLI" logo for logged-out state by
|
||||
@keithguerin in
|
||||
[#22412](https://github.com/google-gemini/gemini-cli/pull/22412)
|
||||
- fix(plan): reserve minimum height for selection list in AskUserDialog by
|
||||
@ruomengz in [#23280](https://github.com/google-gemini/gemini-cli/pull/23280)
|
||||
- fix(core): harden AgentSession replay semantics by @adamfweidman in
|
||||
[#23548](https://github.com/google-gemini/gemini-cli/pull/23548)
|
||||
- test(core): migrate hook tests to scheduler by @abhipatel12 in
|
||||
[#23496](https://github.com/google-gemini/gemini-cli/pull/23496)
|
||||
- chore(config): disable agents by default by @abhipatel12 in
|
||||
[#23546](https://github.com/google-gemini/gemini-cli/pull/23546)
|
||||
- fix(ui): make tool confirmations take up entire terminal height by @devr0306
|
||||
in [#22366](https://github.com/google-gemini/gemini-cli/pull/22366)
|
||||
- fix(core): prevent redundant remote agent loading on model switch by
|
||||
@adamfweidman in
|
||||
[#23576](https://github.com/google-gemini/gemini-cli/pull/23576)
|
||||
- refactor(core): update production type imports from coreToolScheduler by
|
||||
@abhipatel12 in
|
||||
[#23498](https://github.com/google-gemini/gemini-cli/pull/23498)
|
||||
- feat(cli): always prefix extension skills with colon separator by
|
||||
@NTaylorMullen in
|
||||
[#23566](https://github.com/google-gemini/gemini-cli/pull/23566)
|
||||
- fix(core): properly support allowRedirect in policy engine by @scidomino in
|
||||
[#23579](https://github.com/google-gemini/gemini-cli/pull/23579)
|
||||
- fix(cli): prevent subcommand shadowing and skip auth for commands by
|
||||
@mattKorwel in
|
||||
[#23177](https://github.com/google-gemini/gemini-cli/pull/23177)
|
||||
- fix(test): move flaky tests to non-blocking suite by @mattKorwel in
|
||||
[#23259](https://github.com/google-gemini/gemini-cli/pull/23259)
|
||||
- Changelog for v0.35.0-preview.3 by @gemini-cli-robot in
|
||||
[#23574](https://github.com/google-gemini/gemini-cli/pull/23574)
|
||||
- feat(skills): add behavioral-evals skill with fixing and promoting guides by
|
||||
@abhipatel12 in
|
||||
[#23349](https://github.com/google-gemini/gemini-cli/pull/23349)
|
||||
- refactor(core): delete obsolete coreToolScheduler by @abhipatel12 in
|
||||
[#23502](https://github.com/google-gemini/gemini-cli/pull/23502)
|
||||
- Changelog for v0.35.0-preview.4 by @gemini-cli-robot in
|
||||
[#23581](https://github.com/google-gemini/gemini-cli/pull/23581)
|
||||
- feat(core): add LegacyAgentSession by @adamfweidman in
|
||||
[#22986](https://github.com/google-gemini/gemini-cli/pull/22986)
|
||||
- feat(test-utils): add TestMcpServerBuilder and support in TestRig by
|
||||
@abhipatel12 in
|
||||
[#23491](https://github.com/google-gemini/gemini-cli/pull/23491)
|
||||
- fix(core)!: Force policy config to specify toolName by @kschaab in
|
||||
[#23330](https://github.com/google-gemini/gemini-cli/pull/23330)
|
||||
- eval(save_memory): add multi-turn interactive evals for memoryManager by
|
||||
@SandyTao520 in
|
||||
[#23572](https://github.com/google-gemini/gemini-cli/pull/23572)
|
||||
- fix(telemetry): patch memory leak and enforce logPrompts privacy by
|
||||
@spencer426 in
|
||||
[#23281](https://github.com/google-gemini/gemini-cli/pull/23281)
|
||||
- perf(cli): background IDE client to speed up initialization by @sehoon38 in
|
||||
[#23603](https://github.com/google-gemini/gemini-cli/pull/23603)
|
||||
- fix(cli): prevent Ctrl+D exit when input buffer is not empty by @wtanaka in
|
||||
[#23306](https://github.com/google-gemini/gemini-cli/pull/23306)
|
||||
- fix: ACP: separate conversational text from execute tool command title by
|
||||
@sripasg in [#23179](https://github.com/google-gemini/gemini-cli/pull/23179)
|
||||
- feat(evals): add behavioral evaluations for subagent routing by @Samee24 in
|
||||
[#23272](https://github.com/google-gemini/gemini-cli/pull/23272)
|
||||
- refactor(cli,core): foundational layout, identity management, and type safety
|
||||
by @jwhelangoog in
|
||||
[#23286](https://github.com/google-gemini/gemini-cli/pull/23286)
|
||||
- fix(core): accurately reflect subagent tool failure in UI by @abhipatel12 in
|
||||
[#23187](https://github.com/google-gemini/gemini-cli/pull/23187)
|
||||
- Changelog for v0.35.0-preview.5 by @gemini-cli-robot in
|
||||
[#23606](https://github.com/google-gemini/gemini-cli/pull/23606)
|
||||
- feat(ui): implement refreshed UX for Composer layout by @jwhelangoog in
|
||||
[#21212](https://github.com/google-gemini/gemini-cli/pull/21212)
|
||||
- fix: API key input dialog user interaction when selected Gemini API Key by
|
||||
@kartikangiras in
|
||||
[#21057](https://github.com/google-gemini/gemini-cli/pull/21057)
|
||||
- docs: update `/mcp refresh` to `/mcp reload` by @adamfweidman in
|
||||
[#23631](https://github.com/google-gemini/gemini-cli/pull/23631)
|
||||
- Implementation of sandbox "Write-Protected" Governance Files by @DavidAPierce
|
||||
in [#23139](https://github.com/google-gemini/gemini-cli/pull/23139)
|
||||
- feat(sandbox): dynamic macOS sandbox expansion and worktree support by @galz10
|
||||
in [#23301](https://github.com/google-gemini/gemini-cli/pull/23301)
|
||||
- fix(acp): Pass the cwd to `AcpFileSystemService` to avoid looping failures in
|
||||
asking for perms to write plan md file by @sripasg in
|
||||
[#23612](https://github.com/google-gemini/gemini-cli/pull/23612)
|
||||
- fix(plan): sandbox path resolution in Plan Mode to prevent hallucinations by
|
||||
@Adib234 in [#22737](https://github.com/google-gemini/gemini-cli/pull/22737)
|
||||
- feat(ui): allow immediate user input during startup by @sehoon38 in
|
||||
[#23661](https://github.com/google-gemini/gemini-cli/pull/23661)
|
||||
- refactor(sandbox): reorganize Windows sandbox files by @galz10 in
|
||||
[#23645](https://github.com/google-gemini/gemini-cli/pull/23645)
|
||||
- fix(core): improve remote agent streaming UI and UX by @adamfweidman in
|
||||
[#23633](https://github.com/google-gemini/gemini-cli/pull/23633)
|
||||
- perf(cli): optimize --version startup time by @sehoon38 in
|
||||
[#23671](https://github.com/google-gemini/gemini-cli/pull/23671)
|
||||
- refactor(core): stop gemini CLI from producing unsafe casts by @gundermanc in
|
||||
[#23611](https://github.com/google-gemini/gemini-cli/pull/23611)
|
||||
- use enableAutoUpdate in test rig by @scidomino in
|
||||
[#23681](https://github.com/google-gemini/gemini-cli/pull/23681)
|
||||
- feat(core): change user-facing auth type from oauth2 to oauth by @adamfweidman
|
||||
in [#23639](https://github.com/google-gemini/gemini-cli/pull/23639)
|
||||
- chore(deps): fix npm audit vulnerabilities by @scidomino in
|
||||
[#23679](https://github.com/google-gemini/gemini-cli/pull/23679)
|
||||
- test(evals): fix overlapping act() deadlock in app-test-helper by @Adib234 in
|
||||
[#23666](https://github.com/google-gemini/gemini-cli/pull/23666)
|
||||
- fix(patch): cherry-pick 055ff92 to release/v0.36.0-preview.0-pr-23672 to patch
|
||||
version v0.36.0-preview.0 and create version 0.36.0-preview.1 by
|
||||
@gemini-cli-robot in
|
||||
[#23723](https://github.com/google-gemini/gemini-cli/pull/23723)
|
||||
- fix(patch): cherry-pick 765fb67 to release/v0.36.0-preview.5-pr-24055 to patch
|
||||
version v0.36.0-preview.5 and create version 0.36.0-preview.6 by
|
||||
@gemini-cli-robot in
|
||||
[#24061](https://github.com/google-gemini/gemini-cli/pull/24061)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.31.0...v0.32.1
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.35.3...v0.36.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Preview release: v0.33.0-preview.1
|
||||
# Preview release: v0.37.0-preview.1
|
||||
|
||||
Released: March 04, 2026
|
||||
Released: April 02, 2026
|
||||
|
||||
Our preview release includes the latest, new, and experimental features. This
|
||||
release may not be as stable as our [latest weekly release](latest.md).
|
||||
@@ -13,175 +13,410 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Plan Mode Enhancements**: Added support for annotating plans with feedback
|
||||
for iteration, enabling built-in research subagents in plan mode, and a new
|
||||
`copy` subcommand.
|
||||
- **Agent and Skill Improvements**: Introduced the new `github-issue-creator`
|
||||
skill, implemented HTTP authentication support for A2A remote agents, and
|
||||
added support for authenticated A2A agent card discovery.
|
||||
- **CLI UX/UI Updates**: Redesigned the header to be compact with an ASCII icon,
|
||||
inverted the context window display to show usage, and directly indicate auth
|
||||
required state for agents.
|
||||
- **Core and ACP Enhancements**: Implemented slash command handling in ACP (for
|
||||
`/memory`, `/init`, `/extensions`, and `/restore`), added a set models
|
||||
interface to ACP, and centralized `read_file` limits while truncating large
|
||||
MCP tool output.
|
||||
- **Plan Mode Enhancements**: Plan now includes support for untrusted folders,
|
||||
prioritized pre-approval discussions, and a resolve for sandbox-related
|
||||
deadlocks during file creation.
|
||||
- **Browser Agent Evolved**: Significant updates to the browser agent, including
|
||||
persistent session management, dynamic discovery of read-only tools,
|
||||
sandbox-aware initialization, and automated reclamation of stale snapshots to
|
||||
optimize context window usage.
|
||||
- **Advanced Sandbox Security**: Implementation of dynamic sandbox expansion for
|
||||
both Linux and Windows, alongside secret visibility lockdown for environment
|
||||
files and OS-specific forbidden path support.
|
||||
- **Unified Core Architecture**: Centralized context management and a new
|
||||
`ModelConfigService` for unified model discovery, complemented by the
|
||||
introduction of `AgentHistoryProvider` and tool-based topic grouping
|
||||
(Chapters).
|
||||
- **UI/UX & Performance Improvements**: New Tokyo Night theme, "tab to queue"
|
||||
message support, and compact tool output formatting, plus optimized build
|
||||
scripts and improved layout stability for TUI components.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- fix(patch): cherry-pick 0659ad1 to release/v0.33.0-preview.0-pr-21042 to patch
|
||||
version v0.33.0-preview.0 and create version 0.33.0-preview.1 by
|
||||
- fix(patch): cherry-pick 64c928f to release/v0.37.0-preview.0-pr-23257 to patch
|
||||
version v0.37.0-preview.0 and create version 0.37.0-preview.1 by
|
||||
@gemini-cli-robot in
|
||||
[#21047](https://github.com/google-gemini/gemini-cli/pull/21047)
|
||||
|
||||
* Docs: Update model docs to remove Preview Features. by @jkcinouye in
|
||||
[#20084](https://github.com/google-gemini/gemini-cli/pull/20084)
|
||||
* docs: fix typo in installation documentation by @AdityaSharma-Git3207 in
|
||||
[#20153](https://github.com/google-gemini/gemini-cli/pull/20153)
|
||||
* docs: add Windows PowerShell equivalents for environments and scripting by
|
||||
@scidomino in [#20333](https://github.com/google-gemini/gemini-cli/pull/20333)
|
||||
* fix(core): parse raw ASCII buffer strings in Gaxios errors by @sehoon38 in
|
||||
[#20626](https://github.com/google-gemini/gemini-cli/pull/20626)
|
||||
* chore(release): bump version to 0.33.0-nightly.20260227.ba149afa0 by @galz10
|
||||
in [#20637](https://github.com/google-gemini/gemini-cli/pull/20637)
|
||||
* fix(github): use robot PAT for automated PRs to pass CLA check by @galz10 in
|
||||
[#20641](https://github.com/google-gemini/gemini-cli/pull/20641)
|
||||
* chore/release: bump version to 0.33.0-nightly.20260228.1ca5c05d0 by
|
||||
@gemini-cli-robot in
|
||||
[#20644](https://github.com/google-gemini/gemini-cli/pull/20644)
|
||||
* Changelog for v0.31.0 by @gemini-cli-robot in
|
||||
[#20634](https://github.com/google-gemini/gemini-cli/pull/20634)
|
||||
* fix: use full paths for ACP diff payloads by @JagjeevanAK in
|
||||
[#19539](https://github.com/google-gemini/gemini-cli/pull/19539)
|
||||
* Changelog for v0.32.0-preview.0 by @gemini-cli-robot in
|
||||
[#20627](https://github.com/google-gemini/gemini-cli/pull/20627)
|
||||
* fix: acp/zed race condition between MCP initialisation and prompt by
|
||||
@kartikangiras in
|
||||
[#20205](https://github.com/google-gemini/gemini-cli/pull/20205)
|
||||
* fix(cli): reset themeManager between tests to ensure isolation by
|
||||
@NTaylorMullen in
|
||||
[#20598](https://github.com/google-gemini/gemini-cli/pull/20598)
|
||||
* refactor(core): Extract tool parameter names as constants by @SandyTao520 in
|
||||
[#20460](https://github.com/google-gemini/gemini-cli/pull/20460)
|
||||
* fix(cli): resolve autoThemeSwitching when background hasn't changed but theme
|
||||
mismatches by @sehoon38 in
|
||||
[#20706](https://github.com/google-gemini/gemini-cli/pull/20706)
|
||||
* feat(skills): add github-issue-creator skill by @sehoon38 in
|
||||
[#20709](https://github.com/google-gemini/gemini-cli/pull/20709)
|
||||
* fix(cli): allow sub-agent confirmation requests in UI while preventing
|
||||
background flicker by @abhipatel12 in
|
||||
[#20722](https://github.com/google-gemini/gemini-cli/pull/20722)
|
||||
* Merge User and Agent Card Descriptions #20849 by @adamfweidman in
|
||||
[#20850](https://github.com/google-gemini/gemini-cli/pull/20850)
|
||||
* fix(core): reduce LLM-based loop detection false positives by @SandyTao520 in
|
||||
[#20701](https://github.com/google-gemini/gemini-cli/pull/20701)
|
||||
* fix(plan): deflake plan mode integration tests by @Adib234 in
|
||||
[#20477](https://github.com/google-gemini/gemini-cli/pull/20477)
|
||||
* Add /unassign support by @scidomino in
|
||||
[#20864](https://github.com/google-gemini/gemini-cli/pull/20864)
|
||||
* feat(core): implement HTTP authentication support for A2A remote agents by
|
||||
@SandyTao520 in
|
||||
[#20510](https://github.com/google-gemini/gemini-cli/pull/20510)
|
||||
* feat(core): centralize read_file limits and update gemini-3 description by
|
||||
@aishaneeshah in
|
||||
[#20619](https://github.com/google-gemini/gemini-cli/pull/20619)
|
||||
* Do not block CI on evals by @gundermanc in
|
||||
[#20870](https://github.com/google-gemini/gemini-cli/pull/20870)
|
||||
* document node limitation for shift+tab by @scidomino in
|
||||
[#20877](https://github.com/google-gemini/gemini-cli/pull/20877)
|
||||
* Add install as an option when extension is selected. by @DavidAPierce in
|
||||
[#20358](https://github.com/google-gemini/gemini-cli/pull/20358)
|
||||
* Update CODEOWNERS for README.md reviewers by @g-samroberts in
|
||||
[#20860](https://github.com/google-gemini/gemini-cli/pull/20860)
|
||||
* feat(core): truncate large MCP tool output by @SandyTao520 in
|
||||
[#19365](https://github.com/google-gemini/gemini-cli/pull/19365)
|
||||
* Subagent activity UX. by @gundermanc in
|
||||
[#17570](https://github.com/google-gemini/gemini-cli/pull/17570)
|
||||
* style(cli) : Dialog pattern for /hooks Command by @AbdulTawabJuly in
|
||||
[#17930](https://github.com/google-gemini/gemini-cli/pull/17930)
|
||||
* feat: redesign header to be compact with ASCII icon by @keithguerin in
|
||||
[#18713](https://github.com/google-gemini/gemini-cli/pull/18713)
|
||||
* fix(core): ensure subagents use qualified MCP tool names by @abhipatel12 in
|
||||
[#20801](https://github.com/google-gemini/gemini-cli/pull/20801)
|
||||
* feat(core): support authenticated A2A agent card discovery by @SandyTao520 in
|
||||
[#20622](https://github.com/google-gemini/gemini-cli/pull/20622)
|
||||
* refactor(cli): fully remove React anti patterns, improve type safety and fix
|
||||
UX oversights in SettingsDialog.tsx by @psinha40898 in
|
||||
[#18963](https://github.com/google-gemini/gemini-cli/pull/18963)
|
||||
* Adding MCPOAuthProvider implementing the MCPSDK OAuthClientProvider by
|
||||
@Nayana-Parameswarappa in
|
||||
[#20121](https://github.com/google-gemini/gemini-cli/pull/20121)
|
||||
* feat(core): add tool name validation in TOML policy files by @allenhutchison
|
||||
in [#19281](https://github.com/google-gemini/gemini-cli/pull/19281)
|
||||
* docs: fix broken markdown links in main README.md by @Hamdanbinhashim in
|
||||
[#20300](https://github.com/google-gemini/gemini-cli/pull/20300)
|
||||
* refactor(core): replace manual syncPlanModeTools with declarative policy rules
|
||||
by @jerop in [#20596](https://github.com/google-gemini/gemini-cli/pull/20596)
|
||||
* fix(core): increase default headers timeout to 5 minutes by @gundermanc in
|
||||
[#20890](https://github.com/google-gemini/gemini-cli/pull/20890)
|
||||
* feat(admin): enable 30 day default retention for chat history & remove warning
|
||||
by @skeshive in
|
||||
[#20853](https://github.com/google-gemini/gemini-cli/pull/20853)
|
||||
* feat(plan): support annotating plans with feedback for iteration by @Adib234
|
||||
in [#20876](https://github.com/google-gemini/gemini-cli/pull/20876)
|
||||
* Add some dos and don'ts to behavioral evals README. by @gundermanc in
|
||||
[#20629](https://github.com/google-gemini/gemini-cli/pull/20629)
|
||||
* fix(core): skip telemetry logging for AbortError exceptions by @yunaseoul in
|
||||
[#19477](https://github.com/google-gemini/gemini-cli/pull/19477)
|
||||
* fix(core): restrict "System: Please continue" invalid stream retry to Gemini 2
|
||||
models by @SandyTao520 in
|
||||
[#20897](https://github.com/google-gemini/gemini-cli/pull/20897)
|
||||
* ci(evals): only run evals in CI if prompts or tools changed by @gundermanc in
|
||||
[#20898](https://github.com/google-gemini/gemini-cli/pull/20898)
|
||||
* Build binary by @aswinashok44 in
|
||||
[#18933](https://github.com/google-gemini/gemini-cli/pull/18933)
|
||||
* Code review fixes as a pr by @jacob314 in
|
||||
[#20612](https://github.com/google-gemini/gemini-cli/pull/20612)
|
||||
* fix(ci): handle empty APP_ID in stale PR closer by @bdmorgan in
|
||||
[#20919](https://github.com/google-gemini/gemini-cli/pull/20919)
|
||||
* feat(cli): invert context window display to show usage by @keithguerin in
|
||||
[#20071](https://github.com/google-gemini/gemini-cli/pull/20071)
|
||||
* fix(plan): clean up session directories and plans on deletion by @jerop in
|
||||
[#20914](https://github.com/google-gemini/gemini-cli/pull/20914)
|
||||
* fix(core): enforce optionality for API response fields in code_assist by
|
||||
@sehoon38 in [#20714](https://github.com/google-gemini/gemini-cli/pull/20714)
|
||||
* feat(extensions): add support for plan directory in extension manifest by
|
||||
@mahimashanware in
|
||||
[#20354](https://github.com/google-gemini/gemini-cli/pull/20354)
|
||||
* feat(plan): enable built-in research subagents in plan mode by @Adib234 in
|
||||
[#20972](https://github.com/google-gemini/gemini-cli/pull/20972)
|
||||
* feat(agents): directly indicate auth required state by @adamfweidman in
|
||||
[#20986](https://github.com/google-gemini/gemini-cli/pull/20986)
|
||||
* fix(cli): wait for background auto-update before relaunching by @scidomino in
|
||||
[#20904](https://github.com/google-gemini/gemini-cli/pull/20904)
|
||||
* fix: pre-load @scripts/copy_files.js references from external editor prompts
|
||||
by @kartikangiras in
|
||||
[#20963](https://github.com/google-gemini/gemini-cli/pull/20963)
|
||||
* feat(evals): add behavioral evals for ask_user tool by @Adib234 in
|
||||
[#20620](https://github.com/google-gemini/gemini-cli/pull/20620)
|
||||
* refactor common settings logic for skills,agents by @ishaanxgupta in
|
||||
[#17490](https://github.com/google-gemini/gemini-cli/pull/17490)
|
||||
* Update docs-writer skill with new resource by @g-samroberts in
|
||||
[#20917](https://github.com/google-gemini/gemini-cli/pull/20917)
|
||||
* fix(cli): pin clipboardy to ~5.2.x by @scidomino in
|
||||
[#21009](https://github.com/google-gemini/gemini-cli/pull/21009)
|
||||
* feat: Implement slash command handling in ACP for
|
||||
`/memory`,`/init`,`/extensions` and `/restore` by @sripasg in
|
||||
[#20528](https://github.com/google-gemini/gemini-cli/pull/20528)
|
||||
* Docs/add hooks reference by @AadithyaAle in
|
||||
[#20961](https://github.com/google-gemini/gemini-cli/pull/20961)
|
||||
* feat(plan): add copy subcommand to plan (#20491) by @ruomengz in
|
||||
[#20988](https://github.com/google-gemini/gemini-cli/pull/20988)
|
||||
* fix(core): sanitize and length-check MCP tool qualified names by @abhipatel12
|
||||
in [#20987](https://github.com/google-gemini/gemini-cli/pull/20987)
|
||||
* Format the quota/limit style guide. by @g-samroberts in
|
||||
[#21017](https://github.com/google-gemini/gemini-cli/pull/21017)
|
||||
* fix(core): send shell output to model on cancel by @devr0306 in
|
||||
[#20501](https://github.com/google-gemini/gemini-cli/pull/20501)
|
||||
* remove hardcoded tiername when missing tier by @sehoon38 in
|
||||
[#21022](https://github.com/google-gemini/gemini-cli/pull/21022)
|
||||
* feat(acp): add set models interface by @skeshive in
|
||||
[#20991](https://github.com/google-gemini/gemini-cli/pull/20991)
|
||||
[#24561](https://github.com/google-gemini/gemini-cli/pull/24561)
|
||||
- feat(evals): centralize test agents into test-utils for reuse by @Samee24 in
|
||||
[#23616](https://github.com/google-gemini/gemini-cli/pull/23616)
|
||||
- revert: chore(config): disable agents by default by @abhipatel12 in
|
||||
[#23672](https://github.com/google-gemini/gemini-cli/pull/23672)
|
||||
- fix(plan): update telemetry attribute keys and add timestamp by @Adib234 in
|
||||
[#23685](https://github.com/google-gemini/gemini-cli/pull/23685)
|
||||
- fix(core): prevent premature MCP discovery completion by @jackwotherspoon in
|
||||
[#23637](https://github.com/google-gemini/gemini-cli/pull/23637)
|
||||
- feat(browser): add maxActionsPerTask for browser agent setting by
|
||||
@cynthialong0-0 in
|
||||
[#23216](https://github.com/google-gemini/gemini-cli/pull/23216)
|
||||
- fix(core): improve agent loader error formatting for empty paths by
|
||||
@adamfweidman in
|
||||
[#23690](https://github.com/google-gemini/gemini-cli/pull/23690)
|
||||
- fix(cli): only show updating spinner when auto-update is in progress by
|
||||
@scidomino in [#23709](https://github.com/google-gemini/gemini-cli/pull/23709)
|
||||
- Refine onboarding metrics to log the duration explicitly and use the tier
|
||||
name. by @yunaseoul in
|
||||
[#23678](https://github.com/google-gemini/gemini-cli/pull/23678)
|
||||
- chore(tools): add toJSON to tools and invocations to reduce logging verbosity
|
||||
by @alisa-alisa in
|
||||
[#22899](https://github.com/google-gemini/gemini-cli/pull/22899)
|
||||
- fix(cli): stabilize copy mode to prevent flickering and cursor resets by
|
||||
@mattKorwel in
|
||||
[#22584](https://github.com/google-gemini/gemini-cli/pull/22584)
|
||||
- fix(test): move flaky ctrl-c-exit test to non-blocking suite by @mattKorwel in
|
||||
[#23732](https://github.com/google-gemini/gemini-cli/pull/23732)
|
||||
- feat(skills): add ci skill for automated failure replication by @mattKorwel in
|
||||
[#23720](https://github.com/google-gemini/gemini-cli/pull/23720)
|
||||
- feat(sandbox): implement forbiddenPaths for OS-specific sandbox managers by
|
||||
@ehedlund in [#23282](https://github.com/google-gemini/gemini-cli/pull/23282)
|
||||
- fix(core): conditionally expose additional_permissions in shell tool by
|
||||
@galz10 in [#23729](https://github.com/google-gemini/gemini-cli/pull/23729)
|
||||
- refactor(core): standardize OS-specific sandbox tests and extract linux helper
|
||||
methods by @ehedlund in
|
||||
[#23715](https://github.com/google-gemini/gemini-cli/pull/23715)
|
||||
- format recently added script by @scidomino in
|
||||
[#23739](https://github.com/google-gemini/gemini-cli/pull/23739)
|
||||
- fix(ui): prevent over-eager slash subcommand completion by @keithguerin in
|
||||
[#20136](https://github.com/google-gemini/gemini-cli/pull/20136)
|
||||
- Fix dynamic model routing for gemini 3.1 pro to customtools model by
|
||||
@kevinjwang1 in
|
||||
[#23641](https://github.com/google-gemini/gemini-cli/pull/23641)
|
||||
- feat(core): support inline agentCardJson for remote agents by @adamfweidman in
|
||||
[#23743](https://github.com/google-gemini/gemini-cli/pull/23743)
|
||||
- fix(cli): skip console log/info in headless mode by @cynthialong0-0 in
|
||||
[#22739](https://github.com/google-gemini/gemini-cli/pull/22739)
|
||||
- test(core): install bubblewrap on Linux CI for sandbox integration tests by
|
||||
@ehedlund in [#23583](https://github.com/google-gemini/gemini-cli/pull/23583)
|
||||
- docs(reference): split tools table into category sections by @sheikhlimon in
|
||||
[#21516](https://github.com/google-gemini/gemini-cli/pull/21516)
|
||||
- fix(browser): detect embedded URLs in query params to prevent allowedDomains
|
||||
bypass by @tony-shi in
|
||||
[#23225](https://github.com/google-gemini/gemini-cli/pull/23225)
|
||||
- fix(browser): add proxy bypass constraint to domain restriction system prompt
|
||||
by @tony-shi in
|
||||
[#23229](https://github.com/google-gemini/gemini-cli/pull/23229)
|
||||
- fix(policy): relax write_file argsPattern in plan mode to allow paths without
|
||||
session ID by @Adib234 in
|
||||
[#23695](https://github.com/google-gemini/gemini-cli/pull/23695)
|
||||
- docs: fix grammar in CONTRIBUTING and numbering in sandbox docs by
|
||||
@splint-disk-8i in
|
||||
[#23448](https://github.com/google-gemini/gemini-cli/pull/23448)
|
||||
- fix(acp): allow attachments by adding a permission prompt by @sripasg in
|
||||
[#23680](https://github.com/google-gemini/gemini-cli/pull/23680)
|
||||
- fix(core): thread AbortSignal to chat compression requests (#20405) by
|
||||
@SH20RAJ in [#20778](https://github.com/google-gemini/gemini-cli/pull/20778)
|
||||
- feat(core): implement Windows sandbox dynamic expansion Phase 1 and 2.1 by
|
||||
@scidomino in [#23691](https://github.com/google-gemini/gemini-cli/pull/23691)
|
||||
- Add note about root privileges in sandbox docs by @diodesign in
|
||||
[#23314](https://github.com/google-gemini/gemini-cli/pull/23314)
|
||||
- docs(core): document agent_card_json string literal options for remote agents
|
||||
by @adamfweidman in
|
||||
[#23797](https://github.com/google-gemini/gemini-cli/pull/23797)
|
||||
- fix(cli): resolve TTY hang on headless environments by unconditionally
|
||||
resuming process.stdin before React Ink launch by @cocosheng-g in
|
||||
[#23673](https://github.com/google-gemini/gemini-cli/pull/23673)
|
||||
- fix(ui): cleanup estimated string length hacks in composer by @keithguerin in
|
||||
[#23694](https://github.com/google-gemini/gemini-cli/pull/23694)
|
||||
- feat(browser): dynamically discover read-only tools by @cynthialong0-0 in
|
||||
[#23805](https://github.com/google-gemini/gemini-cli/pull/23805)
|
||||
- docs: clarify policy requirement for `general.plan.directory` in settings
|
||||
schema by @jerop in
|
||||
[#23784](https://github.com/google-gemini/gemini-cli/pull/23784)
|
||||
- Revert "perf(cli): optimize --version startup time (#23671)" by @scidomino in
|
||||
[#23812](https://github.com/google-gemini/gemini-cli/pull/23812)
|
||||
- don't silence errors from wombat by @scidomino in
|
||||
[#23822](https://github.com/google-gemini/gemini-cli/pull/23822)
|
||||
- fix(ui): prevent escape key from cancelling requests in shell mode by
|
||||
@PrasannaPal21 in
|
||||
[#21245](https://github.com/google-gemini/gemini-cli/pull/21245)
|
||||
- Changelog for v0.36.0-preview.0 by @gemini-cli-robot in
|
||||
[#23702](https://github.com/google-gemini/gemini-cli/pull/23702)
|
||||
- feat(core,ui): Add experiment-gated support for gemini flash 3.1 lite by
|
||||
@chrstnb in [#23794](https://github.com/google-gemini/gemini-cli/pull/23794)
|
||||
- Changelog for v0.36.0-preview.3 by @gemini-cli-robot in
|
||||
[#23827](https://github.com/google-gemini/gemini-cli/pull/23827)
|
||||
- new linting check: github-actions-pinning by @alisa-alisa in
|
||||
[#23808](https://github.com/google-gemini/gemini-cli/pull/23808)
|
||||
- fix(cli): show helpful guidance when no skills are available by @Niralisj in
|
||||
[#23785](https://github.com/google-gemini/gemini-cli/pull/23785)
|
||||
- fix: Chat logs and errors handle tail tool calls correctly by @googlestrobe in
|
||||
[#22460](https://github.com/google-gemini/gemini-cli/pull/22460)
|
||||
- Don't try removing a tag from a non-existent release. by @scidomino in
|
||||
[#23830](https://github.com/google-gemini/gemini-cli/pull/23830)
|
||||
- fix(cli): allow ask question dialog to take full window height by @jacob314 in
|
||||
[#23693](https://github.com/google-gemini/gemini-cli/pull/23693)
|
||||
- fix(core): strip leading underscores from error types in telemetry by
|
||||
@yunaseoul in [#23824](https://github.com/google-gemini/gemini-cli/pull/23824)
|
||||
- Changelog for v0.35.0 by @gemini-cli-robot in
|
||||
[#23819](https://github.com/google-gemini/gemini-cli/pull/23819)
|
||||
- feat(evals): add reliability harvester and 500/503 retry support by
|
||||
@alisa-alisa in
|
||||
[#23626](https://github.com/google-gemini/gemini-cli/pull/23626)
|
||||
- feat(sandbox): dynamic Linux sandbox expansion and worktree support by @galz10
|
||||
in [#23692](https://github.com/google-gemini/gemini-cli/pull/23692)
|
||||
- Merge examples of use into quickstart documentation by @diodesign in
|
||||
[#23319](https://github.com/google-gemini/gemini-cli/pull/23319)
|
||||
- fix(cli): prioritize primary name matches in slash command search by @sehoon38
|
||||
in [#23850](https://github.com/google-gemini/gemini-cli/pull/23850)
|
||||
- Changelog for v0.35.1 by @gemini-cli-robot in
|
||||
[#23840](https://github.com/google-gemini/gemini-cli/pull/23840)
|
||||
- fix(browser): keep input blocker active across navigations by @kunal-10-cloud
|
||||
in [#22562](https://github.com/google-gemini/gemini-cli/pull/22562)
|
||||
- feat(core): new skill to look for duplicated code while reviewing PRs by
|
||||
@devr0306 in [#23704](https://github.com/google-gemini/gemini-cli/pull/23704)
|
||||
- fix(core): replace hardcoded non-interactive ASK_USER denial with explicit
|
||||
policy rules by @ruomengz in
|
||||
[#23668](https://github.com/google-gemini/gemini-cli/pull/23668)
|
||||
- fix(plan): after exiting plan mode switches model to a flash model by @Adib234
|
||||
in [#23885](https://github.com/google-gemini/gemini-cli/pull/23885)
|
||||
- feat(gcp): add development worker infrastructure by @mattKorwel in
|
||||
[#23814](https://github.com/google-gemini/gemini-cli/pull/23814)
|
||||
- fix(a2a-server): A2A server should execute ask policies in interactive mode by
|
||||
@kschaab in [#23831](https://github.com/google-gemini/gemini-cli/pull/23831)
|
||||
- feat(core): define TrajectoryProvider interface by @sehoon38 in
|
||||
[#23050](https://github.com/google-gemini/gemini-cli/pull/23050)
|
||||
- Docs: Update quotas and pricing by @jkcinouye in
|
||||
[#23835](https://github.com/google-gemini/gemini-cli/pull/23835)
|
||||
- fix(core): allow disabling environment variable redaction by @galz10 in
|
||||
[#23927](https://github.com/google-gemini/gemini-cli/pull/23927)
|
||||
- feat(cli): enable notifications cross-platform via terminal bell fallback by
|
||||
@genneth in [#21618](https://github.com/google-gemini/gemini-cli/pull/21618)
|
||||
- feat(sandbox): implement secret visibility lockdown for env files by
|
||||
@DavidAPierce in
|
||||
[#23712](https://github.com/google-gemini/gemini-cli/pull/23712)
|
||||
- fix(core): remove shell outputChunks buffer caching to prevent memory bloat
|
||||
and sanitize prompt input by @spencer426 in
|
||||
[#23751](https://github.com/google-gemini/gemini-cli/pull/23751)
|
||||
- feat(core): implement persistent browser session management by @kunal-10-cloud
|
||||
in [#21306](https://github.com/google-gemini/gemini-cli/pull/21306)
|
||||
- refactor(core): delegate sandbox denial parsing to SandboxManager by
|
||||
@scidomino in [#23928](https://github.com/google-gemini/gemini-cli/pull/23928)
|
||||
- dep(update) Update Ink version to 6.5.0 by @jacob314 in
|
||||
[#23843](https://github.com/google-gemini/gemini-cli/pull/23843)
|
||||
- Docs: Update 'docs-writer' skill for relative links by @jkcinouye in
|
||||
[#21463](https://github.com/google-gemini/gemini-cli/pull/21463)
|
||||
- Changelog for v0.36.0-preview.4 by @gemini-cli-robot in
|
||||
[#23935](https://github.com/google-gemini/gemini-cli/pull/23935)
|
||||
- fix(acp): Update allow approval policy flow for ACP clients to fix config
|
||||
persistence and compatible with TUI by @sripasg in
|
||||
[#23818](https://github.com/google-gemini/gemini-cli/pull/23818)
|
||||
- Changelog for v0.35.2 by @gemini-cli-robot in
|
||||
[#23960](https://github.com/google-gemini/gemini-cli/pull/23960)
|
||||
- ACP integration documents by @g-samroberts in
|
||||
[#22254](https://github.com/google-gemini/gemini-cli/pull/22254)
|
||||
- fix(core): explicitly set error names to avoid bundling renaming issues by
|
||||
@yunaseoul in [#23913](https://github.com/google-gemini/gemini-cli/pull/23913)
|
||||
- feat(core): subagent isolation and cleanup hardening by @abhipatel12 in
|
||||
[#23903](https://github.com/google-gemini/gemini-cli/pull/23903)
|
||||
- disable extension-reload test by @scidomino in
|
||||
[#24018](https://github.com/google-gemini/gemini-cli/pull/24018)
|
||||
- feat(core): add forbiddenPaths to GlobalSandboxOptions and refactor
|
||||
createSandboxManager by @ehedlund in
|
||||
[#23936](https://github.com/google-gemini/gemini-cli/pull/23936)
|
||||
- refactor(core): improve ignore resolution and fix directory-matching bug by
|
||||
@ehedlund in [#23816](https://github.com/google-gemini/gemini-cli/pull/23816)
|
||||
- revert(core): support custom base URL via env vars by @spencer426 in
|
||||
[#23976](https://github.com/google-gemini/gemini-cli/pull/23976)
|
||||
- Increase memory limited for eslint. by @jacob314 in
|
||||
[#24022](https://github.com/google-gemini/gemini-cli/pull/24022)
|
||||
- fix(acp): prevent crash on empty response in ACP mode by @sripasg in
|
||||
[#23952](https://github.com/google-gemini/gemini-cli/pull/23952)
|
||||
- feat(core): Land `AgentHistoryProvider`. by @joshualitt in
|
||||
[#23978](https://github.com/google-gemini/gemini-cli/pull/23978)
|
||||
- fix(core): switch to subshells for shell tool wrapping to fix heredocs and
|
||||
edge cases by @abhipatel12 in
|
||||
[#24024](https://github.com/google-gemini/gemini-cli/pull/24024)
|
||||
- Debug command. by @jacob314 in
|
||||
[#23851](https://github.com/google-gemini/gemini-cli/pull/23851)
|
||||
- Changelog for v0.36.0-preview.5 by @gemini-cli-robot in
|
||||
[#24046](https://github.com/google-gemini/gemini-cli/pull/24046)
|
||||
- Fix test flakes by globally mocking ink-spinner by @jacob314 in
|
||||
[#24044](https://github.com/google-gemini/gemini-cli/pull/24044)
|
||||
- Enable network access in sandbox configuration by @galz10 in
|
||||
[#24055](https://github.com/google-gemini/gemini-cli/pull/24055)
|
||||
- feat(context): add configurable memoryBoundaryMarkers setting by @SandyTao520
|
||||
in [#24020](https://github.com/google-gemini/gemini-cli/pull/24020)
|
||||
- feat(core): implement windows sandbox expansion and denial detection by
|
||||
@scidomino in [#24027](https://github.com/google-gemini/gemini-cli/pull/24027)
|
||||
- fix(core): resolve ACP Operation Aborted Errors in grep_search by @ivanporty
|
||||
in [#23821](https://github.com/google-gemini/gemini-cli/pull/23821)
|
||||
- fix(hooks): prevent SessionEnd from firing twice in non-interactive mode by
|
||||
@krishdef7 in [#22139](https://github.com/google-gemini/gemini-cli/pull/22139)
|
||||
- Re-word intro to Gemini 3 page. by @g-samroberts in
|
||||
[#24069](https://github.com/google-gemini/gemini-cli/pull/24069)
|
||||
- fix(cli): resolve layout contention and flashing loop in StatusRow by
|
||||
@keithguerin in
|
||||
[#24065](https://github.com/google-gemini/gemini-cli/pull/24065)
|
||||
- fix(sandbox): implement Windows Mandatory Integrity Control for GeminiSandbox
|
||||
by @galz10 in [#24057](https://github.com/google-gemini/gemini-cli/pull/24057)
|
||||
- feat(core): implement tool-based topic grouping (Chapters) by @Abhijit-2592 in
|
||||
[#23150](https://github.com/google-gemini/gemini-cli/pull/23150)
|
||||
- feat(cli): support 'tab to queue' for messages while generating by @gundermanc
|
||||
in [#24052](https://github.com/google-gemini/gemini-cli/pull/24052)
|
||||
- feat(core): agnostic background task UI with CompletionBehavior by
|
||||
@adamfweidman in
|
||||
[#22740](https://github.com/google-gemini/gemini-cli/pull/22740)
|
||||
- UX for topic narration tool by @gundermanc in
|
||||
[#24079](https://github.com/google-gemini/gemini-cli/pull/24079)
|
||||
- fix: shellcheck warnings in scripts by @scidomino in
|
||||
[#24035](https://github.com/google-gemini/gemini-cli/pull/24035)
|
||||
- test(evals): add comprehensive subagent delegation evaluations by @abhipatel12
|
||||
in [#24132](https://github.com/google-gemini/gemini-cli/pull/24132)
|
||||
- fix(a2a-server): prioritize ADC before evaluating headless constraints for
|
||||
auth initialization by @spencer426 in
|
||||
[#23614](https://github.com/google-gemini/gemini-cli/pull/23614)
|
||||
- Text can be added after /plan command by @rambleraptor in
|
||||
[#22833](https://github.com/google-gemini/gemini-cli/pull/22833)
|
||||
- fix(cli): resolve missing F12 logs via global console store by @scidomino in
|
||||
[#24235](https://github.com/google-gemini/gemini-cli/pull/24235)
|
||||
- fix broken tests by @scidomino in
|
||||
[#24279](https://github.com/google-gemini/gemini-cli/pull/24279)
|
||||
- fix(evals): add update_topic behavioral eval by @gundermanc in
|
||||
[#24223](https://github.com/google-gemini/gemini-cli/pull/24223)
|
||||
- feat(core): Unified Context Management and Tool Distillation. by @joshualitt
|
||||
in [#24157](https://github.com/google-gemini/gemini-cli/pull/24157)
|
||||
- Default enable narration for the team. by @gundermanc in
|
||||
[#24224](https://github.com/google-gemini/gemini-cli/pull/24224)
|
||||
- fix(core): ensure default agents provide tools and use model-specific schemas
|
||||
by @abhipatel12 in
|
||||
[#24268](https://github.com/google-gemini/gemini-cli/pull/24268)
|
||||
- feat(cli): show Flash Lite Preview model regardless of user tier by @sehoon38
|
||||
in [#23904](https://github.com/google-gemini/gemini-cli/pull/23904)
|
||||
- feat(cli): implement compact tool output by @jwhelangoog in
|
||||
[#20974](https://github.com/google-gemini/gemini-cli/pull/20974)
|
||||
- Add security settings for tool sandboxing by @galz10 in
|
||||
[#23923](https://github.com/google-gemini/gemini-cli/pull/23923)
|
||||
- chore(test-utils): switch integration tests to use PREVIEW_GEMINI_MODEL by
|
||||
@sehoon38 in [#24276](https://github.com/google-gemini/gemini-cli/pull/24276)
|
||||
- feat(core): enable topic update narration for legacy models by @Abhijit-2592
|
||||
in [#24241](https://github.com/google-gemini/gemini-cli/pull/24241)
|
||||
- feat(core): add project-level memory scope to save_memory tool by @SandyTao520
|
||||
in [#24161](https://github.com/google-gemini/gemini-cli/pull/24161)
|
||||
- test(integration): fix plan mode write denial test false positive by @sehoon38
|
||||
in [#24299](https://github.com/google-gemini/gemini-cli/pull/24299)
|
||||
- feat(plan): support `Plan` mode in untrusted folders by @Adib234 in
|
||||
[#17586](https://github.com/google-gemini/gemini-cli/pull/17586)
|
||||
- fix(core): enable mid-stream retries for all models and re-enable compression
|
||||
test by @sehoon38 in
|
||||
[#24302](https://github.com/google-gemini/gemini-cli/pull/24302)
|
||||
- Changelog for v0.36.0-preview.6 by @gemini-cli-robot in
|
||||
[#24082](https://github.com/google-gemini/gemini-cli/pull/24082)
|
||||
- Changelog for v0.35.3 by @gemini-cli-robot in
|
||||
[#24083](https://github.com/google-gemini/gemini-cli/pull/24083)
|
||||
- feat(cli): add auth info to footer by @sehoon38 in
|
||||
[#24042](https://github.com/google-gemini/gemini-cli/pull/24042)
|
||||
- fix(browser): reset action counter for each agent session and let it ignore
|
||||
internal actions by @cynthialong0-0 in
|
||||
[#24228](https://github.com/google-gemini/gemini-cli/pull/24228)
|
||||
- feat(plan): promote planning feature to stable by @ruomengz in
|
||||
[#24282](https://github.com/google-gemini/gemini-cli/pull/24282)
|
||||
- fix(browser): terminate subagent immediately on domain restriction violations
|
||||
by @gsquared94 in
|
||||
[#24313](https://github.com/google-gemini/gemini-cli/pull/24313)
|
||||
- feat(cli): add UI to update extensions by @ruomengz in
|
||||
[#23682](https://github.com/google-gemini/gemini-cli/pull/23682)
|
||||
- Fix(browser): terminate immediately for "browser is already running" error by
|
||||
@cynthialong0-0 in
|
||||
[#24233](https://github.com/google-gemini/gemini-cli/pull/24233)
|
||||
- docs: Add 'plan' option to approval mode in CLI reference by @YifanRuan in
|
||||
[#24134](https://github.com/google-gemini/gemini-cli/pull/24134)
|
||||
- fix(core): batch macOS seatbelt rules into a profile file to prevent ARG_MAX
|
||||
errors by @ehedlund in
|
||||
[#24255](https://github.com/google-gemini/gemini-cli/pull/24255)
|
||||
- fix(core): fix race condition between browser agent and main closing process
|
||||
by @cynthialong0-0 in
|
||||
[#24340](https://github.com/google-gemini/gemini-cli/pull/24340)
|
||||
- perf(build): optimize build scripts for parallel execution and remove
|
||||
redundant checks by @sehoon38 in
|
||||
[#24307](https://github.com/google-gemini/gemini-cli/pull/24307)
|
||||
- ci: install bubblewrap on Linux for release workflows by @ehedlund in
|
||||
[#24347](https://github.com/google-gemini/gemini-cli/pull/24347)
|
||||
- chore(release): allow bundling for all builds, including stable by @sehoon38
|
||||
in [#24305](https://github.com/google-gemini/gemini-cli/pull/24305)
|
||||
- Revert "Add security settings for tool sandboxing" by @jerop in
|
||||
[#24357](https://github.com/google-gemini/gemini-cli/pull/24357)
|
||||
- docs: update subagents docs to not be experimental by @abhipatel12 in
|
||||
[#24343](https://github.com/google-gemini/gemini-cli/pull/24343)
|
||||
- fix(core): implement **read and **write commands in sandbox managers by
|
||||
@galz10 in [#24283](https://github.com/google-gemini/gemini-cli/pull/24283)
|
||||
- don't try to remove tags in dry run by @scidomino in
|
||||
[#24356](https://github.com/google-gemini/gemini-cli/pull/24356)
|
||||
- fix(config): disable JIT context loading by default by @SandyTao520 in
|
||||
[#24364](https://github.com/google-gemini/gemini-cli/pull/24364)
|
||||
- test(sandbox): add integration test for dynamic permission expansion by
|
||||
@galz10 in [#24359](https://github.com/google-gemini/gemini-cli/pull/24359)
|
||||
- docs(policy): remove unsupported mcpName wildcard edge case by @abhipatel12 in
|
||||
[#24133](https://github.com/google-gemini/gemini-cli/pull/24133)
|
||||
- docs: fix broken GEMINI.md link in CONTRIBUTING.md by @Panchal-Tirth in
|
||||
[#24182](https://github.com/google-gemini/gemini-cli/pull/24182)
|
||||
- feat(core): infrastructure for event-driven subagent history by @abhipatel12
|
||||
in [#23914](https://github.com/google-gemini/gemini-cli/pull/23914)
|
||||
- fix(core): resolve Plan Mode deadlock during plan file creation due to sandbox
|
||||
restrictions by @DavidAPierce in
|
||||
[#24047](https://github.com/google-gemini/gemini-cli/pull/24047)
|
||||
- fix(core): fix browser agent UX issues and improve E2E test reliability by
|
||||
@gsquared94 in
|
||||
[#24312](https://github.com/google-gemini/gemini-cli/pull/24312)
|
||||
- fix(ui): wrap topic and intent fields in TopicMessage by @jwhelangoog in
|
||||
[#24386](https://github.com/google-gemini/gemini-cli/pull/24386)
|
||||
- refactor(core): Centralize context management logic into src/context by
|
||||
@joshualitt in
|
||||
[#24380](https://github.com/google-gemini/gemini-cli/pull/24380)
|
||||
- fix(core): pin AuthType.GATEWAY to use Gemini 3.1 Pro/Flash Lite by default by
|
||||
@sripasg in [#24375](https://github.com/google-gemini/gemini-cli/pull/24375)
|
||||
- feat(ui): add Tokyo Night theme by @danrneal in
|
||||
[#24054](https://github.com/google-gemini/gemini-cli/pull/24054)
|
||||
- fix(cli): refactor test config loading and mock debugLogger in test-setup by
|
||||
@mattKorwel in
|
||||
[#24389](https://github.com/google-gemini/gemini-cli/pull/24389)
|
||||
- Set memoryManager to false in settings.json by @mattKorwel in
|
||||
[#24393](https://github.com/google-gemini/gemini-cli/pull/24393)
|
||||
- ink 6.6.3 by @jacob314 in
|
||||
[#24372](https://github.com/google-gemini/gemini-cli/pull/24372)
|
||||
- fix(core): resolve subagent chat recording gaps and directory inheritance by
|
||||
@abhipatel12 in
|
||||
[#24368](https://github.com/google-gemini/gemini-cli/pull/24368)
|
||||
- fix(cli): cap shell output at 10 MB to prevent RangeError crash by @ProthamD
|
||||
in [#24168](https://github.com/google-gemini/gemini-cli/pull/24168)
|
||||
- feat(plan): conditionally add enter/exit plan mode tools based on current mode
|
||||
by @ruomengz in
|
||||
[#24378](https://github.com/google-gemini/gemini-cli/pull/24378)
|
||||
- feat(core): prioritize discussion before formal plan approval by @jerop in
|
||||
[#24423](https://github.com/google-gemini/gemini-cli/pull/24423)
|
||||
- fix(ui): add accelerated scrolling on alternate buffer mode by @devr0306 in
|
||||
[#23940](https://github.com/google-gemini/gemini-cli/pull/23940)
|
||||
- feat(core): populate sandbox forbidden paths with project ignore file contents
|
||||
by @ehedlund in
|
||||
[#24038](https://github.com/google-gemini/gemini-cli/pull/24038)
|
||||
- fix(core): ensure blue border overlay and input blocker to act correctly
|
||||
depending on browser agent activities by @cynthialong0-0 in
|
||||
[#24385](https://github.com/google-gemini/gemini-cli/pull/24385)
|
||||
- fix(ui): removed additional vertical padding for tables by @devr0306 in
|
||||
[#24381](https://github.com/google-gemini/gemini-cli/pull/24381)
|
||||
- fix(build): upload full bundle directory archive to GitHub releases by
|
||||
@sehoon38 in [#24403](https://github.com/google-gemini/gemini-cli/pull/24403)
|
||||
- fix(build): wire bundle:browser-mcp into bundle pipeline by @gsquared94 in
|
||||
[#24424](https://github.com/google-gemini/gemini-cli/pull/24424)
|
||||
- feat(browser): add sandbox-aware browser agent initialization by @gsquared94
|
||||
in [#24419](https://github.com/google-gemini/gemini-cli/pull/24419)
|
||||
- feat(core): enhance tracker task schemas for detailed titles and descriptions
|
||||
by @anj-s in [#23902](https://github.com/google-gemini/gemini-cli/pull/23902)
|
||||
- refactor(core): Unified context management settings schema by @joshualitt in
|
||||
[#24391](https://github.com/google-gemini/gemini-cli/pull/24391)
|
||||
- feat(core): update browser agent prompt to check open pages first when
|
||||
bringing up by @cynthialong0-0 in
|
||||
[#24431](https://github.com/google-gemini/gemini-cli/pull/24431)
|
||||
- fix(acp) refactor(core,cli): centralize model discovery logic in
|
||||
ModelConfigService by @sripasg in
|
||||
[#24392](https://github.com/google-gemini/gemini-cli/pull/24392)
|
||||
- Changelog for v0.36.0-preview.7 by @gemini-cli-robot in
|
||||
[#24346](https://github.com/google-gemini/gemini-cli/pull/24346)
|
||||
- fix: update task tracker storage location in system prompt by @anj-s in
|
||||
[#24034](https://github.com/google-gemini/gemini-cli/pull/24034)
|
||||
- feat(browser): supersede stale snapshots to reclaim context-window tokens by
|
||||
@gsquared94 in
|
||||
[#24440](https://github.com/google-gemini/gemini-cli/pull/24440)
|
||||
- docs(core): add subagent tool isolation draft doc by @akh64bit in
|
||||
[#23275](https://github.com/google-gemini/gemini-cli/pull/23275)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.32.0-preview.0...v0.33.0-preview.1
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.36.0-preview.8...v0.37.0-preview.1
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
# ACP Mode
|
||||
|
||||
ACP (Agent Client Protocol) mode is a special operational mode of Gemini CLI
|
||||
designed for programmatic control, primarily for IDE and other developer tool
|
||||
integrations. It uses a JSON-RPC protocol over stdio to communicate between
|
||||
Gemini CLI agent and a client.
|
||||
|
||||
To start Gemini CLI in ACP mode, use the `--acp` flag:
|
||||
|
||||
```bash
|
||||
gemini --acp
|
||||
```
|
||||
|
||||
## Agent Client Protocol (ACP)
|
||||
|
||||
ACP is an open protocol that standardizes how AI coding agents communicate with
|
||||
code editors and IDEs. It addresses the challenge of fragmented distribution,
|
||||
where agents traditionally needed custom integrations for each client. With ACP,
|
||||
developers can implement their agent once, and it becomes compatible with any
|
||||
ACP-compliant editor.
|
||||
|
||||
For a comprehensive introduction to ACP, including its architecture and
|
||||
benefits, refer to the official
|
||||
[ACP Introduction](https://agentclientprotocol.com/get-started/introduction)
|
||||
documentation.
|
||||
|
||||
### Existing integrations using ACP
|
||||
|
||||
The ACP Agent Registry simplifies the distribution and management of
|
||||
ACP-compatible agents across various IDEs. Gemini CLI is an ACP-compatible agent
|
||||
and can be found in this registry.
|
||||
|
||||
For more general information about the registry, and how to use it with specific
|
||||
IDEs like JetBrains and Zed, refer to the
|
||||
[IDE Integration](../ide-integration/index.md) documentation.
|
||||
|
||||
You can also find more information on the official
|
||||
[ACP Agent Registry](https://agentclientprotocol.com/get-started/registry) page.
|
||||
|
||||
## Architecture and protocol basics
|
||||
|
||||
ACP mode establishes a client-server relationship between your tool (the client)
|
||||
and Gemini CLI (the server).
|
||||
|
||||
- **Communication:** The entire communication happens over standard input/output
|
||||
(stdio) using the JSON-RPC 2.0 protocol.
|
||||
- **Client's role:** The client is responsible for sending requests (e.g.,
|
||||
prompts) and handling responses and notifications from Gemini CLI.
|
||||
- **Gemini CLI's role:** In ACP mode, Gemini CLI listens for incoming JSON-RPC
|
||||
requests, processes them, and sends back responses.
|
||||
|
||||
The core of the ACP implementation can be found in
|
||||
`packages/cli/src/acp/acpClient.ts`.
|
||||
|
||||
### Extending with MCP
|
||||
|
||||
ACP can be used with the Model Context Protocol (MCP). This lets an ACP client
|
||||
(like an IDE) expose its own functionality as "tools" that the Gemini model can
|
||||
use.
|
||||
|
||||
1. The client implements an **MCP server** that advertises its tools.
|
||||
2. During the ACP `initialize` handshake, the client provides the connection
|
||||
details for its MCP server.
|
||||
3. Gemini CLI connects to the MCP server, discovers the available tools, and
|
||||
makes them available to the AI model.
|
||||
4. When the model decides to use one of these tools, Gemini CLI sends a tool
|
||||
call request to the MCP server.
|
||||
|
||||
This mechanism lets for a powerful, two-way integration where the agent can
|
||||
leverage the IDE's capabilities to perform tasks. The MCP client logic is in
|
||||
`packages/core/src/tools/mcp-client.ts`.
|
||||
|
||||
## Capabilities and supported methods
|
||||
|
||||
The ACP protocol exposes a number of methods for ACP clients (e.g. IDEs) to
|
||||
control Gemini CLI.
|
||||
|
||||
### Core methods
|
||||
|
||||
- `initialize`: Establishes the initial connection and lets the client to
|
||||
register its MCP server.
|
||||
- `authenticate`: Authenticates the user.
|
||||
- `newSession`: Starts a new chat session.
|
||||
- `loadSession`: Loads a previous session.
|
||||
- `prompt`: Sends a prompt to the agent.
|
||||
- `cancel`: Cancels an ongoing prompt.
|
||||
|
||||
### Session control
|
||||
|
||||
- `setSessionMode`: Allows changing the approval level for tool calls (e.g., to
|
||||
`auto-approve`).
|
||||
- `unstable_setSessionModel`: Changes the model for the current session.
|
||||
|
||||
### File system proxy
|
||||
|
||||
ACP includes a proxied file system service. This means that when the agent needs
|
||||
to read or write files, it does so through the ACP client. This is a security
|
||||
feature that ensures the agent only has access to the files that the client (and
|
||||
by extension, the user) has explicitly allowed.
|
||||
|
||||
## Debugging and telemetry
|
||||
|
||||
You can get insights into the ACP communication and the agent's behavior through
|
||||
debugging logs and telemetry.
|
||||
|
||||
### Debugging logs
|
||||
|
||||
To enable general debugging logs, start Gemini CLI with the `--debug` flag:
|
||||
|
||||
```bash
|
||||
gemini --acp --debug
|
||||
```
|
||||
|
||||
### Telemetry
|
||||
|
||||
For more detailed telemetry, you can use the following environment variables to
|
||||
capture telemetry data to a file:
|
||||
|
||||
- `GEMINI_TELEMETRY_ENABLED=true`
|
||||
- `GEMINI_TELEMETRY_TARGET=local`
|
||||
- `GEMINI_TELEMETRY_OUTFILE=/path/to/your/log.json`
|
||||
|
||||
This will write a JSON log file containing detailed information about all the
|
||||
events happening within the agent, including ACP requests and responses. The
|
||||
integration test `integration-tests/acp-telemetry.test.ts` provides a working
|
||||
example of how to set this up.
|
||||
@@ -39,7 +39,9 @@ file in your project's temporary directory, typically located at
|
||||
The Checkpointing feature is disabled by default. To enable it, you need to edit
|
||||
your `settings.json` file.
|
||||
|
||||
> **Note:** The `--checkpointing` command-line flag was removed in version
|
||||
<!-- prettier-ignore -->
|
||||
> [!CAUTION]
|
||||
> The `--checkpointing` command-line flag was removed in version
|
||||
> 0.11.0. Checkpointing can now only be enabled through the `settings.json`
|
||||
> configuration file.
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ and parameters.
|
||||
| Command | Description | Example |
|
||||
| ---------------------------------- | ---------------------------------- | ------------------------------------------------------------ |
|
||||
| `gemini` | Start interactive REPL | `gemini` |
|
||||
| `gemini "query"` | Query non-interactively, then exit | `gemini "explain this project"` |
|
||||
| `gemini -p "query"` | Query non-interactively | `gemini -p "summarize README.md"` |
|
||||
| `gemini "query"` | Query and continue interactively | `gemini "explain this project"` |
|
||||
| `cat file \| gemini` | Process piped content | `cat logs.txt \| gemini`<br>`Get-Content logs.txt \| gemini` |
|
||||
| `gemini -i "query"` | Execute and continue interactively | `gemini -i "What is the purpose of this project?"` |
|
||||
| `gemini -r "latest"` | Continue most recent session | `gemini -r "latest"` |
|
||||
@@ -20,9 +21,24 @@ and parameters.
|
||||
|
||||
### Positional arguments
|
||||
|
||||
| Argument | Type | Description |
|
||||
| -------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| `query` | string (variadic) | Positional prompt. Defaults to one-shot mode. Use `-i/--prompt-interactive` to execute and continue interactively. |
|
||||
| Argument | Type | Description |
|
||||
| -------- | ----------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `query` | string (variadic) | Positional prompt. Defaults to interactive mode in a TTY. Use `-p/--prompt` for non-interactive execution. |
|
||||
|
||||
## Interactive commands
|
||||
|
||||
These commands are available within the interactive REPL.
|
||||
|
||||
| Command | Description |
|
||||
| -------------------- | ---------------------------------------- |
|
||||
| `/skills reload` | Reload discovered skills from disk |
|
||||
| `/agents reload` | Reload the agent registry |
|
||||
| `/commands reload` | Reload custom slash commands |
|
||||
| `/memory reload` | Reload context files (e.g., `GEMINI.md`) |
|
||||
| `/mcp reload` | Restart and reload MCP servers |
|
||||
| `/extensions reload` | Reload all active extensions |
|
||||
| `/help` | Show help for all commands |
|
||||
| `/quit` | Exit the interactive session |
|
||||
|
||||
## CLI Options
|
||||
|
||||
@@ -32,10 +48,11 @@ and parameters.
|
||||
| `--version` | `-v` | - | - | Show CLI version number and exit |
|
||||
| `--help` | `-h` | - | - | Show help information |
|
||||
| `--model` | `-m` | string | `auto` | Model to use. See [Model Selection](#model-selection) for available values. |
|
||||
| `--prompt` | `-p` | string | - | Prompt text. Appended to stdin input if provided. **Deprecated:** Use positional arguments instead. |
|
||||
| `--prompt` | `-p` | string | - | Prompt text. Appended to stdin input if provided. Forces non-interactive mode. |
|
||||
| `--prompt-interactive` | `-i` | string | - | Execute prompt and continue in interactive mode |
|
||||
| `--worktree` | `-w` | string | - | Start Gemini in a new git worktree. If no name is provided, one is generated automatically. Requires `experimental.worktrees: true` in settings. |
|
||||
| `--sandbox` | `-s` | boolean | `false` | Run in a sandboxed environment for safer execution |
|
||||
| `--approval-mode` | - | string | `default` | Approval mode for tool execution. Choices: `default`, `auto_edit`, `yolo` |
|
||||
| `--approval-mode` | - | string | `default` | Approval mode for tool execution. Choices: `default`, `auto_edit`, `yolo`, `plan` |
|
||||
| `--yolo` | `-y` | boolean | `false` | **Deprecated.** Auto-approve all actions. Use `--approval-mode=yolo` instead. |
|
||||
| `--experimental-acp` | - | boolean | - | Start in ACP (Agent Code Pilot) mode. **Experimental feature.** |
|
||||
| `--experimental-zed-integration` | - | boolean | - | Run in Zed editor integration mode. **Experimental feature.** |
|
||||
|
||||
@@ -30,7 +30,9 @@ separator (`/` or `\`) being converted to a colon (`:`).
|
||||
- A file at `<project>/.gemini/commands/git/commit.toml` becomes the namespaced
|
||||
command `/git:commit`.
|
||||
|
||||
> [!TIP] After creating or modifying `.toml` command files, run
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> After creating or modifying `.toml` command files, run
|
||||
> `/commands reload` to pick up your changes without restarting the CLI.
|
||||
|
||||
## TOML file format (v1)
|
||||
@@ -177,10 +179,10 @@ ensure that only intended commands can be run.
|
||||
automatically shell-escaped (see
|
||||
[Context-Aware Injection](#1-context-aware-injection-with-args) above).
|
||||
3. **Robust parsing:** The parser correctly handles complex shell commands that
|
||||
include nested braces, such as JSON payloads. **Note:** The content inside
|
||||
`!{...}` must have balanced braces (`{` and `}`). If you need to execute a
|
||||
command containing unbalanced braces, consider wrapping it in an external
|
||||
script file and calling the script within the `!{...}` block.
|
||||
include nested braces, such as JSON payloads. The content inside `!{...}`
|
||||
must have balanced braces (`{` and `}`). If you need to execute a command
|
||||
containing unbalanced braces, consider wrapping it in an external script
|
||||
file and calling the script within the `!{...}` block.
|
||||
4. **Security check and confirmation:** The CLI performs a security check on
|
||||
the final, resolved command (after arguments are escaped and substituted). A
|
||||
dialog will appear showing the exact command(s) to be executed.
|
||||
|
||||
@@ -5,9 +5,11 @@ and managing Gemini CLI in an enterprise environment. By leveraging system-level
|
||||
settings, administrators can enforce security policies, manage tool access, and
|
||||
ensure a consistent experience for all users.
|
||||
|
||||
> **A note on security:** The patterns described in this document are intended
|
||||
> to help administrators create a more controlled and secure environment for
|
||||
> using Gemini CLI. However, they should not be considered a foolproof security
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> The patterns described in this document are intended to help
|
||||
> administrators create a more controlled and secure environment for using
|
||||
> Gemini CLI. However, they should not be considered a foolproof security
|
||||
> boundary. A determined user with sufficient privileges on their local machine
|
||||
> may still be able to circumvent these configurations. These measures are
|
||||
> designed to prevent accidental misuse and enforce corporate policy in a
|
||||
@@ -280,10 +282,12 @@ environment to a blocklist.
|
||||
}
|
||||
```
|
||||
|
||||
**Security note:** Blocklisting with `excludeTools` is less secure than
|
||||
allowlisting with `coreTools`, as it relies on blocking known-bad commands, and
|
||||
clever users may find ways to bypass simple string-based blocks. **Allowlisting
|
||||
is the recommended approach.**
|
||||
<!-- prettier-ignore -->
|
||||
> [!WARNING]
|
||||
> Blocklisting with `excludeTools` is less secure than
|
||||
> allowlisting with `coreTools`, as it relies on blocking known-bad commands,
|
||||
> and clever users may find ways to bypass simple string-based blocks.
|
||||
> **Allowlisting is the recommended approach.**
|
||||
|
||||
### Disabling YOLO mode
|
||||
|
||||
@@ -494,8 +498,10 @@ other events. For more information, see the
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Ensure that `logPrompts` is set to `false` in an enterprise setting to
|
||||
avoid collecting potentially sensitive information from user prompts.
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Ensure that `logPrompts` is set to `false` in an enterprise setting to
|
||||
> avoid collecting potentially sensitive information from user prompts.
|
||||
|
||||
## Authentication
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ You can interact with the loaded context files by using the `/memory` command.
|
||||
- **`/memory show`**: Displays the full, concatenated content of the current
|
||||
hierarchical memory. This lets you inspect the exact instructional context
|
||||
being provided to the model.
|
||||
- **`/memory refresh`**: Forces a re-scan and reload of all `GEMINI.md` files
|
||||
- **`/memory reload`**: Forces a re-scan and reload of all `GEMINI.md` files
|
||||
from all configured locations.
|
||||
- **`/memory add <text>`**: Appends your text to your global
|
||||
`~/.gemini/GEMINI.md` file. This lets you add persistent memories on the fly.
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# Git Worktrees (experimental)
|
||||
|
||||
When working on multiple tasks at once, you can use Git worktrees to give each
|
||||
Gemini session its own copy of the codebase. Git worktrees create separate
|
||||
working directories that each have their own files and branch while sharing the
|
||||
same repository history. This prevents changes in one session from colliding
|
||||
with another.
|
||||
|
||||
Learn more about [session management](./session-management.md).
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development. Your
|
||||
> feedback is invaluable as we refine this feature. If you have ideas,
|
||||
> suggestions, or encounter issues:
|
||||
>
|
||||
> - [Open an issue](https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml) on GitHub.
|
||||
> - Use the **/bug** command within Gemini CLI to file an issue.
|
||||
|
||||
Learn more in the official Git worktree
|
||||
[documentation](https://git-scm.com/docs/git-worktree).
|
||||
|
||||
## How to enable Git worktrees
|
||||
|
||||
Git worktrees are an experimental feature. You must enable them in your settings
|
||||
using the `/settings` command or by manually editing your `settings.json` file.
|
||||
|
||||
1. Use the `/settings` command.
|
||||
2. Search for and set **Enable Git Worktrees** to `true`.
|
||||
|
||||
Alternatively, add the following to your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"worktrees": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How to use Git worktrees
|
||||
|
||||
Use the `--worktree` (`-w`) flag to create an isolated worktree and start Gemini
|
||||
CLI in it.
|
||||
|
||||
- **Start with a specific name:** The value you pass becomes both the directory
|
||||
name (within `.gemini/worktrees/`) and the branch name.
|
||||
|
||||
```bash
|
||||
gemini --worktree feature-search
|
||||
```
|
||||
|
||||
- **Start with a random name:** If you omit the name, Gemini generates a random
|
||||
one automatically (for example, `worktree-a1b2c3d4`).
|
||||
|
||||
```bash
|
||||
gemini --worktree
|
||||
```
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> Remember to initialize your development environment in each new
|
||||
> worktree according to your project's setup. Depending on your stack, this
|
||||
> might include running dependency installation (`npm install`, `yarn`), setting
|
||||
> up virtual environments, or following your project's standard build process.
|
||||
|
||||
## How to exit a Git worktree session
|
||||
|
||||
When you exit a worktree session (using `/quit` or `Ctrl+C`), Gemini leaves the
|
||||
worktree intact so your work is not lost. This includes your uncommitted changes
|
||||
(modified files, staged changes, or untracked files) and any new commits you
|
||||
have made.
|
||||
|
||||
Gemini prioritizes a fast and safe exit: it **does not automatically delete**
|
||||
your worktree or branch. You are responsible for cleaning up your worktrees
|
||||
manually once you are finished with them.
|
||||
|
||||
When you exit, Gemini displays instructions on how to resume your work or how to
|
||||
manually remove the worktree if you no longer need it.
|
||||
|
||||
## Resuming work in a Git worktree
|
||||
|
||||
To resume a session in a worktree, navigate to the worktree directory and start
|
||||
Gemini CLI with the `--resume` flag and the session ID:
|
||||
|
||||
```bash
|
||||
cd .gemini/worktrees/feature-search
|
||||
gemini --resume <session_id>
|
||||
```
|
||||
|
||||
## Managing Git worktrees manually
|
||||
|
||||
For more control over worktree location and branch configuration, or to clean up
|
||||
a preserved worktree, you can use Git directly:
|
||||
|
||||
- **Clean up a preserved Git worktree:**
|
||||
```bash
|
||||
git worktree remove .gemini/worktrees/feature-search --force
|
||||
git branch -D worktree-feature-search
|
||||
```
|
||||
- **Create a Git worktree manually:**
|
||||
```bash
|
||||
git worktree add ../project-feature-search -b feature-search
|
||||
cd ../project-feature-search && gemini
|
||||
```
|
||||
|
||||
[Open an issue]: https://github.com/google-gemini/gemini-cli/issues
|
||||
@@ -6,7 +6,7 @@ structured text or JSON output without an interactive terminal UI.
|
||||
## Technical reference
|
||||
|
||||
Headless mode is triggered when the CLI is run in a non-TTY environment or when
|
||||
providing a query as a positional argument without the interactive flag.
|
||||
providing a query with the `-p` (or `--prompt`) flag.
|
||||
|
||||
### Output formats
|
||||
|
||||
@@ -31,7 +31,8 @@ Returns a stream of newline-delimited JSON (JSONL) events.
|
||||
- `tool_use`: Tool call requests with arguments.
|
||||
- `tool_result`: Output from executed tools.
|
||||
- `error`: Non-fatal warnings and system errors.
|
||||
- `result`: Final outcome with aggregated statistics.
|
||||
- `result`: Final outcome with aggregated statistics and per-model token usage
|
||||
breakdowns.
|
||||
|
||||
## Exit codes
|
||||
|
||||
|
||||
@@ -26,6 +26,20 @@ policies.
|
||||
the CLI will use an available fallback model for the current turn or the
|
||||
remainder of the session.
|
||||
|
||||
### Local Model Routing (Experimental)
|
||||
|
||||
Gemini CLI supports using a local model for routing decisions. When configured,
|
||||
Gemini CLI will use a locally-running **Gemma** model to make routing decisions
|
||||
(instead of sending routing decisions to a hosted model). This feature can help
|
||||
reduce costs associated with hosted model usage while offering similar routing
|
||||
decision latency and quality.
|
||||
|
||||
In order to use this feature, the local Gemma model **must** be served behind a
|
||||
Gemini API and accessible via HTTP at an endpoint configured in `settings.json`.
|
||||
|
||||
For more details on how to configure local model routing, see
|
||||
[Local Model Routing](../core/local-model-routing.md).
|
||||
|
||||
### Model selection precedence
|
||||
|
||||
The model used by Gemini CLI is determined by the following order of precedence:
|
||||
@@ -38,5 +52,8 @@ The model used by Gemini CLI is determined by the following order of precedence:
|
||||
3. **`model.name` in `settings.json`:** If neither of the above are set, the
|
||||
model specified in the `model.name` property of your `settings.json` file
|
||||
will be used.
|
||||
4. **Default model:** If none of the above are set, the default model will be
|
||||
4. **Local model (experimental):** If the Gemma local model router is enabled
|
||||
in your `settings.json` file, the CLI will use the local Gemma model
|
||||
(instead of Gemini models) to route the request to an appropriate model.
|
||||
5. **Default model:** If none of the above are set, the default model will be
|
||||
used. The default model is `auto`
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# Model steering (experimental)
|
||||
|
||||
Model steering lets you provide real-time guidance and feedback to Gemini CLI
|
||||
while it is actively executing a task. This lets you correct course, add missing
|
||||
context, or skip unnecessary steps without having to stop and restart the agent.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development and
|
||||
> may need to be enabled under `/settings`.
|
||||
|
||||
Model steering is particularly useful during complex [Plan Mode](./plan-mode.md)
|
||||
workflows or long-running subagent executions where you want to ensure the agent
|
||||
stays on the right track.
|
||||
|
||||
## Enabling model steering
|
||||
|
||||
Model steering is an experimental feature and is disabled by default. You can
|
||||
enable it using the `/settings` command or by updating your `settings.json`
|
||||
file.
|
||||
|
||||
1. Type `/settings` in the Gemini CLI.
|
||||
2. Search for **Model Steering**.
|
||||
3. Set the value to **true**.
|
||||
|
||||
Alternatively, add the following to your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"modelSteering": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using model steering
|
||||
|
||||
When model steering is enabled, Gemini CLI treats any text you type while the
|
||||
agent is working as a steering hint.
|
||||
|
||||
1. Start a task (for example, "Refactor the database service").
|
||||
2. While the agent is working (the spinner is visible), type your feedback in
|
||||
the input box.
|
||||
3. Press **Enter**.
|
||||
|
||||
Gemini CLI acknowledges your hint with a brief message and injects it directly
|
||||
into the model's context for the very next turn. The model then re-evaluates its
|
||||
current plan and adjusts its actions accordingly.
|
||||
|
||||
### Common use cases
|
||||
|
||||
You can use steering hints to guide the model in several ways:
|
||||
|
||||
- **Correcting a path:** "Actually, the utilities are in `src/common/utils`."
|
||||
- **Skipping a step:** "Skip the unit tests for now and just focus on the
|
||||
implementation."
|
||||
- **Adding context:** "The `User` type is defined in `packages/core/types.ts`."
|
||||
- **Redirecting the effort:** "Stop searching the codebase and start drafting
|
||||
the plan now."
|
||||
- **Handling ambiguity:** "Use the existing `Logger` class instead of creating a
|
||||
new one."
|
||||
|
||||
## How it works
|
||||
|
||||
When you submit a steering hint, Gemini CLI performs the following actions:
|
||||
|
||||
1. **Immediate acknowledgment:** It uses a small, fast model to generate a
|
||||
one-sentence acknowledgment so you know your hint was received.
|
||||
2. **Context injection:** It prepends an internal instruction to your hint that
|
||||
tells the main agent to:
|
||||
- Re-evaluate the active plan.
|
||||
- Classify the update (for example, as a new task or extra context).
|
||||
- Apply minimal-diff changes to affected tasks.
|
||||
3. **Real-time update:** The hint is delivered to the agent at the beginning of
|
||||
its next turn, ensuring the most immediate course correction possible.
|
||||
|
||||
## Next steps
|
||||
|
||||
- Tackle complex tasks with [Plan Mode](./plan-mode.md).
|
||||
- Build custom [Agent Skills](./skills.md).
|
||||
@@ -5,7 +5,9 @@ used by Gemini CLI, giving you more control over your results. Use **Pro**
|
||||
models for complex tasks and reasoning, **Flash** models for high speed results,
|
||||
or the (recommended) **Auto** setting to choose the best model for your tasks.
|
||||
|
||||
> **Note:** The `/model` command (and the `--model` flag) does not override the
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> The `/model` command (and the `--model` flag) does not override the
|
||||
> model used by sub-agents. Consequently, even when using the `/model` flag you
|
||||
> may see other models used in your model usage reports.
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Notifications (experimental)
|
||||
|
||||
Gemini CLI can send system notifications to alert you when a session completes
|
||||
or when it needs your attention, such as when it's waiting for you to approve a
|
||||
tool call.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> This is an experimental feature currently under active development and
|
||||
> may need to be enabled under `/settings`.
|
||||
|
||||
Notifications are particularly useful when running long-running tasks or using
|
||||
[Plan Mode](./plan-mode.md), letting you switch to other windows while Gemini
|
||||
CLI works in the background.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Terminal support
|
||||
|
||||
The CLI uses the OSC 9 terminal escape sequence to trigger system notifications.
|
||||
This is supported by several modern terminal emulators including iTerm2,
|
||||
WezTerm, Ghostty, and Kitty. If your terminal does not support OSC 9
|
||||
notifications, Gemini CLI falls back to a terminal bell (BEL) to get your
|
||||
attention. Most terminals respond to BEL with a taskbar flash or system alert
|
||||
sound.
|
||||
|
||||
## Enable notifications
|
||||
|
||||
Notifications are disabled by default. You can enable them using the `/settings`
|
||||
command or by updating your `settings.json` file.
|
||||
|
||||
1. Open the settings dialog by typing `/settings` in an interactive session.
|
||||
2. Navigate to the **General** category.
|
||||
3. Toggle the **Enable Notifications** setting to **On**.
|
||||
|
||||
Alternatively, add the following to your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"enableNotifications": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Types of notifications
|
||||
|
||||
Gemini CLI sends notifications for the following events:
|
||||
|
||||
- **Action required:** Triggered when the model is waiting for user input or
|
||||
tool approval. This helps you know when the CLI has paused and needs you to
|
||||
intervene.
|
||||
- **Session complete:** Triggered when a session finishes successfully. This is
|
||||
useful for tracking the completion of automated tasks.
|
||||
|
||||
## Next steps
|
||||
|
||||
- Start planning with [Plan Mode](./plan-mode.md).
|
||||
- Configure your experience with other [settings](./settings.md).
|
||||
@@ -1,4 +1,4 @@
|
||||
# Plan Mode (experimental)
|
||||
# Plan Mode
|
||||
|
||||
Plan Mode is a read-only environment for architecting robust solutions before
|
||||
implementation. With Plan Mode, you can:
|
||||
@@ -8,27 +8,8 @@ implementation. With Plan Mode, you can:
|
||||
- **Design:** Understand problems, evaluate trade-offs, and choose a solution.
|
||||
- **Plan:** Align on an execution strategy before any code is modified.
|
||||
|
||||
> **Note:** This is a preview feature currently under active development. Your
|
||||
> feedback is invaluable as we refine this feature. If you have ideas,
|
||||
> suggestions, or encounter issues:
|
||||
>
|
||||
> - [Open an issue] on GitHub.
|
||||
> - Use the **/bug** command within Gemini CLI to file an issue.
|
||||
|
||||
## How to enable Plan Mode
|
||||
|
||||
Enable Plan Mode in **Settings** or by editing your configuration file.
|
||||
|
||||
- **Settings:** Use the `/settings` command and set **Plan** to `true`.
|
||||
- **Configuration:** Add the following to your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": {
|
||||
"plan": true
|
||||
}
|
||||
}
|
||||
```
|
||||
Plan Mode is enabled by default. You can manage this setting using the
|
||||
`/settings` command.
|
||||
|
||||
## How to enter Plan Mode
|
||||
|
||||
@@ -54,16 +35,19 @@ To launch Gemini CLI in Plan Mode once:
|
||||
To start Plan Mode while using Gemini CLI:
|
||||
|
||||
- **Keyboard shortcut:** Press `Shift+Tab` to cycle through approval modes
|
||||
(`Default` -> `Auto-Edit` -> `Plan`).
|
||||
(`Default` -> `Auto-Edit` -> `Plan`). Plan Mode is automatically removed from
|
||||
the rotation when Gemini CLI is actively processing or showing confirmation
|
||||
dialogs.
|
||||
|
||||
> **Note:** Plan Mode is automatically removed from the rotation when Gemini
|
||||
> CLI is actively processing or showing confirmation dialogs.
|
||||
|
||||
- **Command:** Type `/plan` in the input box.
|
||||
- **Command:** Type `/plan [goal]` in the input box. The `[goal]` is optional;
|
||||
for example, `/plan implement authentication` will switch to Plan Mode and
|
||||
immediately submit the prompt to the model.
|
||||
|
||||
- **Natural Language:** Ask Gemini CLI to "start a plan for...". Gemini CLI
|
||||
calls the [`enter_plan_mode`] tool to switch modes.
|
||||
> **Note:** This tool is not available when Gemini CLI is in [YOLO mode].
|
||||
calls the
|
||||
[`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode) tool
|
||||
to switch modes. This tool is not available when Gemini CLI is in
|
||||
[YOLO mode](../reference/configuration.md#command-line-arguments).
|
||||
|
||||
## How to use Plan Mode
|
||||
|
||||
@@ -72,24 +56,51 @@ Gemini CLI takes action.
|
||||
|
||||
1. **Provide a goal:** Start by describing what you want to achieve. Gemini CLI
|
||||
will then enter Plan Mode (if it's not already) to research the task.
|
||||
2. **Review research and provide input:** As Gemini CLI analyzes your codebase,
|
||||
it may ask you questions or present different implementation options using
|
||||
[`ask_user`]. Provide your preferences to help guide the design.
|
||||
3. **Review the plan:** Once Gemini CLI has a proposed strategy, it creates a
|
||||
detailed implementation plan as a Markdown file in your plans directory. You
|
||||
can open and read this file to understand the proposed changes.
|
||||
2. **Discuss and agree on strategy:** As Gemini CLI analyzes your codebase, it
|
||||
will discuss its findings and proposed strategy with you to ensure
|
||||
alignment. It may ask you questions or present different implementation
|
||||
options using [`ask_user`](../tools/ask-user.md). **Gemini CLI will stop and
|
||||
wait for your confirmation** before drafting the formal plan. You should
|
||||
reach an informal agreement on the approach before proceeding.
|
||||
3. **Review the plan:** Once you've agreed on the strategy, Gemini CLI creates
|
||||
a detailed implementation plan as a Markdown file in your plans directory.
|
||||
- **View:** You can open and read this file to understand the proposed
|
||||
changes.
|
||||
- **Edit:** Press `Ctrl+X` to open the plan directly in your configured
|
||||
external editor.
|
||||
|
||||
4. **Approve or iterate:** Gemini CLI will present the finalized plan for your
|
||||
approval.
|
||||
formal approval.
|
||||
- **Approve:** If you're satisfied with the plan, approve it to start the
|
||||
implementation immediately: **Yes, automatically accept edits** or **Yes,
|
||||
manually accept edits**.
|
||||
- **Iterate:** If the plan needs adjustments, provide feedback. Gemini CLI
|
||||
will refine the strategy and update the plan.
|
||||
- **Iterate:** If the plan needs adjustments, provide feedback in the input
|
||||
box or [edit the plan file directly](#collaborative-plan-editing). Gemini
|
||||
CLI will refine the strategy and update the plan.
|
||||
- **Cancel:** You can cancel your plan with `Esc`.
|
||||
|
||||
For more complex or specialized planning tasks, you can
|
||||
[customize the planning workflow with skills](#custom-planning-with-skills).
|
||||
|
||||
### Collaborative plan editing
|
||||
|
||||
You can collaborate with Gemini CLI by making direct changes or leaving comments
|
||||
in the implementation plan. This is often faster and more precise than
|
||||
describing complex changes in natural language.
|
||||
|
||||
1. **Open the plan:** Press `Ctrl+X` when Gemini CLI presents a plan for
|
||||
review.
|
||||
2. **Edit or comment:** The plan opens in your configured external editor (for
|
||||
example, VS Code or Vim). You can:
|
||||
- **Modify steps:** Directly reorder, delete, or rewrite implementation
|
||||
steps.
|
||||
- **Leave comments:** Add inline questions or feedback (for example, "Wait,
|
||||
shouldn't we use the existing `Logger` class here?").
|
||||
3. **Save and close:** Save your changes and close the editor.
|
||||
4. **Review and refine:** Gemini CLI automatically detects the changes, reviews
|
||||
your comments, and adjusts the implementation strategy. It then presents the
|
||||
refined plan for your final approval.
|
||||
|
||||
## How to exit Plan Mode
|
||||
|
||||
You can exit Plan Mode at any time, whether you have finalized a plan or want to
|
||||
@@ -100,41 +111,47 @@ switch back to another mode.
|
||||
- **Keyboard shortcut:** Press `Shift+Tab` to cycle to the desired mode.
|
||||
- **Natural language:** Ask Gemini CLI to "exit plan mode" or "stop planning."
|
||||
|
||||
## Customization and best practices
|
||||
|
||||
Plan Mode is secure by default, but you can adapt it to fit your specific
|
||||
workflows. You can customize how Gemini CLI plans by using skills, adjusting
|
||||
safety policies, or changing where plans are stored.
|
||||
|
||||
## Commands
|
||||
|
||||
- **`/plan copy`**: Copy the currently approved plan to your clipboard.
|
||||
|
||||
## Tool Restrictions
|
||||
|
||||
Plan Mode enforces strict safety policies to prevent accidental changes.
|
||||
|
||||
These are the only allowed tools:
|
||||
|
||||
- **FileSystem (Read):** [`read_file`], [`list_directory`], [`glob`]
|
||||
- **Search:** [`grep_search`], [`google_web_search`]
|
||||
- **Research Subagents:** [`codebase_investigator`], [`cli_help`]
|
||||
- **Interaction:** [`ask_user`]
|
||||
- **MCP tools (Read):** Read-only [MCP tools] (for example, `github_read_issue`,
|
||||
`postgres_read_schema`) are allowed.
|
||||
- **Planning (Write):** [`write_file`] and [`replace`] only allowed for `.md`
|
||||
- **FileSystem (Read):**
|
||||
[`read_file`](../tools/file-system.md#2-read_file-readfile),
|
||||
[`list_directory`](../tools/file-system.md#1-list_directory-readfolder),
|
||||
[`glob`](../tools/file-system.md#4-glob-findfiles)
|
||||
- **Search:** [`grep_search`](../tools/file-system.md#5-grep_search-searchtext),
|
||||
[`google_web_search`](../tools/web-search.md),
|
||||
[`web_fetch`](../tools/web-fetch.md) (requires explicit confirmation),
|
||||
[`get_internal_docs`](../tools/internal-docs.md)
|
||||
- **Research Subagents:**
|
||||
[`codebase_investigator`](../core/subagents.md#codebase-investigator),
|
||||
[`cli_help`](../core/subagents.md#cli-help-agent)
|
||||
- **Interaction:** [`ask_user`](../tools/ask-user.md)
|
||||
- **MCP tools (Read):** Read-only [MCP tools](../tools/mcp-server.md) (for
|
||||
example, `github_read_issue`, `postgres_read_schema`) are allowed.
|
||||
- **Planning (Write):**
|
||||
[`write_file`](../tools/file-system.md#3-write_file-writefile) and
|
||||
[`replace`](../tools/file-system.md#6-replace-edit) only allowed for `.md`
|
||||
files in the `~/.gemini/tmp/<project>/<session-id>/plans/` directory or your
|
||||
[custom plans directory](#custom-plan-directory-and-policies).
|
||||
- **Memory:** [`save_memory`]
|
||||
- **Skills:** [`activate_skill`] (allows loading specialized instructions and
|
||||
resources in a read-only manner)
|
||||
- **Memory:** [`save_memory`](../tools/memory.md)
|
||||
- **Skills:** [`activate_skill`](../cli/skills.md) (allows loading specialized
|
||||
instructions and resources in a read-only manner)
|
||||
|
||||
## Customization and best practices
|
||||
|
||||
Plan Mode is secure by default, but you can adapt it to fit your specific
|
||||
workflows. You can customize how Gemini CLI plans by using skills, adjusting
|
||||
safety policies, changing where plans are stored, or adding hooks.
|
||||
|
||||
### Custom planning with skills
|
||||
|
||||
You can use [Agent Skills] to customize how Gemini CLI approaches planning for
|
||||
specific types of tasks. When a skill is activated during Plan Mode, its
|
||||
specialized instructions and procedural workflows will guide the research,
|
||||
design, and planning phases.
|
||||
You can use [Agent Skills](../cli/skills.md) to customize how Gemini CLI
|
||||
approaches planning for specific types of tasks. When a skill is activated
|
||||
during Plan Mode, its specialized instructions and procedural workflows will
|
||||
guide the research, design, and planning phases.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -151,10 +168,39 @@ based on the task description.
|
||||
|
||||
### Custom policies
|
||||
|
||||
Plan Mode's default tool restrictions are managed by the [policy engine] and
|
||||
defined in the built-in [`plan.toml`] file. The built-in policy (Tier 1)
|
||||
enforces the read-only state, but you can customize these rules by creating your
|
||||
own policies in your `~/.gemini/policies/` directory (Tier 2).
|
||||
Plan Mode's default tool restrictions are managed by the
|
||||
[policy engine](../reference/policy-engine.md) and defined in the built-in
|
||||
[`plan.toml`] file. The built-in policy (Tier 1) enforces the read-only state,
|
||||
but you can customize these rules by creating your own policies in your
|
||||
`~/.gemini/policies/` directory (Tier 2).
|
||||
|
||||
#### Global vs. mode-specific rules
|
||||
|
||||
As described in the
|
||||
[policy engine documentation](../reference/policy-engine.md#approval-modes), any
|
||||
rule that does not explicitly specify `modes` is considered "always active" and
|
||||
will apply to Plan Mode as well.
|
||||
|
||||
To maintain the integrity of Plan Mode as a safe research environment,
|
||||
persistent tool approvals are context-aware. Approvals granted in modes like
|
||||
Default or Auto-Edit do not apply to Plan Mode, ensuring that tools trusted for
|
||||
implementation don't automatically execute while you're researching. However,
|
||||
approvals granted while in Plan Mode are treated as intentional choices for
|
||||
global trust and apply to all modes.
|
||||
|
||||
If you want to manually restrict a rule to other modes but _not_ to Plan Mode,
|
||||
you must explicitly specify the target modes. For example, to allow `npm test`
|
||||
in default and Auto-Edit modes but not in Plan Mode:
|
||||
|
||||
```toml
|
||||
[[rule]]
|
||||
toolName = "run_shell_command"
|
||||
commandPrefix = "npm test"
|
||||
decision = "allow"
|
||||
priority = 100
|
||||
# By omitting "plan", this rule will not be active in Plan Mode.
|
||||
modes = ["default", "autoEdit"]
|
||||
```
|
||||
|
||||
#### Example: Automatically approve read-only MCP tools
|
||||
|
||||
@@ -166,6 +212,7 @@ your specific environment.
|
||||
|
||||
```toml
|
||||
[[rule]]
|
||||
toolName = "*"
|
||||
mcpName = "*"
|
||||
toolAnnotations = { readOnlyHint = true }
|
||||
decision = "allow"
|
||||
@@ -173,8 +220,8 @@ priority = 100
|
||||
modes = ["plan"]
|
||||
```
|
||||
|
||||
For more information on how the policy engine works, see the [policy engine]
|
||||
docs.
|
||||
For more information on how the policy engine works, see the
|
||||
[policy engine](../reference/policy-engine.md) docs.
|
||||
|
||||
#### Example: Allow git commands in Plan Mode
|
||||
|
||||
@@ -194,9 +241,12 @@ modes = ["plan"]
|
||||
|
||||
#### Example: Enable custom subagents in Plan Mode
|
||||
|
||||
Built-in research [subagents] like [`codebase_investigator`] and [`cli_help`]
|
||||
are enabled by default in Plan Mode. You can enable additional [custom
|
||||
subagents] by adding a rule to your policy.
|
||||
Built-in research [subagents](../core/subagents.md) like
|
||||
[`codebase_investigator`](../core/subagents.md#codebase-investigator) and
|
||||
[`cli_help`](../core/subagents.md#cli-help-agent) are enabled by default in Plan
|
||||
Mode. You can enable additional
|
||||
[custom subagents](../core/subagents.md#creating-custom-subagents) by adding a
|
||||
rule to your policy.
|
||||
|
||||
`~/.gemini/policies/research-subagents.toml`
|
||||
|
||||
@@ -235,10 +285,11 @@ locations defined within a project's workspace cannot be used to escape and
|
||||
overwrite sensitive files elsewhere. Any user-configured directory must reside
|
||||
within the project boundary.
|
||||
|
||||
Using a custom directory requires updating your [policy engine] configurations
|
||||
to allow `write_file` and `replace` in that specific location. For example, to
|
||||
allow writing to the `.gemini/plans` directory within your project, create a
|
||||
policy file at `~/.gemini/policies/plan-custom-directory.toml`:
|
||||
Using a custom directory requires updating your
|
||||
[policy engine](../reference/policy-engine.md) configurations to allow
|
||||
`write_file` and `replace` in that specific location. For example, to allow
|
||||
writing to the `.gemini/plans` directory within your project, create a policy
|
||||
file at `~/.gemini/policies/plan-custom-directory.toml`:
|
||||
|
||||
```toml
|
||||
[[rule]]
|
||||
@@ -251,16 +302,84 @@ modes = ["plan"]
|
||||
argsPattern = "\"file_path\":\"[^\"]+[\\\\/]+\\.gemini[\\\\/]+plans[\\\\/]+[\\w-]+\\.md\""
|
||||
```
|
||||
|
||||
### Using hooks with Plan Mode
|
||||
|
||||
You can use the [hook system](../hooks/writing-hooks.md) to automate parts of
|
||||
the planning workflow or enforce additional checks when Gemini CLI transitions
|
||||
into or out of Plan Mode.
|
||||
|
||||
Hooks such as `BeforeTool` or `AfterTool` can be configured to intercept the
|
||||
`enter_plan_mode` and `exit_plan_mode` tool calls.
|
||||
|
||||
> [!WARNING] When hooks are triggered by **tool executions**, they do **not**
|
||||
> run when you manually toggle Plan Mode using the `/plan` command or the
|
||||
> `Shift+Tab` keyboard shortcut. If you need hooks to execute on mode changes,
|
||||
> ensure the transition is initiated by the agent (e.g., by asking "start a plan
|
||||
> for...").
|
||||
|
||||
#### Example: Archive approved plans to GCS (`AfterTool`)
|
||||
|
||||
If your organizational policy requires a record of all execution plans, you can
|
||||
use an `AfterTool` hook to securely copy the plan artifact to Google Cloud
|
||||
Storage whenever Gemini CLI exits Plan Mode to start the implementation.
|
||||
|
||||
**`.gemini/hooks/archive-plan.sh`:**
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Extract the plan path from the tool input JSON
|
||||
plan_path=$(jq -r '.tool_input.plan_path // empty')
|
||||
|
||||
if [ -f "$plan_path" ]; then
|
||||
# Generate a unique filename using a timestamp
|
||||
filename="$(date +%s)_$(basename "$plan_path")"
|
||||
|
||||
# Upload the plan to GCS in the background so it doesn't block the CLI
|
||||
gsutil cp "$plan_path" "gs://my-audit-bucket/gemini-plans/$filename" > /dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
# AfterTool hooks should generally allow the flow to continue
|
||||
echo '{"decision": "allow"}'
|
||||
```
|
||||
|
||||
To register this `AfterTool` hook, add it to your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"AfterTool": [
|
||||
{
|
||||
"matcher": "exit_plan_mode",
|
||||
"hooks": [
|
||||
{
|
||||
"name": "archive-plan",
|
||||
"type": "command",
|
||||
"command": "./.gemini/hooks/archive-plan.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
- **`/plan copy`**: Copy the currently approved plan to your clipboard.
|
||||
|
||||
## Planning workflows
|
||||
|
||||
Plan Mode provides building blocks for structured research and design. These are
|
||||
implemented as [extensions] using core planning tools like [`enter_plan_mode`],
|
||||
[`exit_plan_mode`], and [`ask_user`].
|
||||
implemented as [extensions](../extensions/index.md) using core planning tools
|
||||
like [`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode),
|
||||
[`exit_plan_mode`](../tools/planning.md#2-exit_plan_mode-exitplanmode), and
|
||||
[`ask_user`](../tools/ask-user.md).
|
||||
|
||||
### Built-in planning workflow
|
||||
|
||||
The built-in planner uses an adaptive workflow to analyze your project, consult
|
||||
you on trade-offs via [`ask_user`], and draft a plan for your approval.
|
||||
you on trade-offs via [`ask_user`](../tools/ask-user.md), and draft a plan for
|
||||
your approval.
|
||||
|
||||
### Custom planning workflows
|
||||
|
||||
@@ -272,28 +391,36 @@ You can install or create specialized planners to suit your workflow.
|
||||
"tracks" and stores persistent artifacts in your project's `conductor/`
|
||||
directory:
|
||||
|
||||
- **Automate transitions:** Switches to read-only mode via [`enter_plan_mode`].
|
||||
- **Streamline decisions:** Uses [`ask_user`] for architectural choices.
|
||||
- **Automate transitions:** Switches to read-only mode via
|
||||
[`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode).
|
||||
- **Streamline decisions:** Uses [`ask_user`](../tools/ask-user.md) for
|
||||
architectural choices.
|
||||
- **Maintain project context:** Stores artifacts in the project directory using
|
||||
[custom plan directory and policies](#custom-plan-directory-and-policies).
|
||||
- **Handoff execution:** Transitions to implementation via [`exit_plan_mode`].
|
||||
- **Handoff execution:** Transitions to implementation via
|
||||
[`exit_plan_mode`](../tools/planning.md#2-exit_plan_mode-exitplanmode).
|
||||
|
||||
#### Build your own
|
||||
|
||||
Since Plan Mode is built on modular building blocks, you can develop your own
|
||||
custom planning workflow as an [extensions]. By leveraging core tools and
|
||||
[custom policies](#custom-policies), you can define how Gemini CLI researches
|
||||
and stores plans for your specific domain.
|
||||
custom planning workflow as an [extensions](../extensions/index.md). By
|
||||
leveraging core tools and [custom policies](#custom-policies), you can define
|
||||
how Gemini CLI researches and stores plans for your specific domain.
|
||||
|
||||
To build a custom planning workflow, you can use:
|
||||
|
||||
- **Tool usage:** Use core tools like [`enter_plan_mode`], [`ask_user`], and
|
||||
[`exit_plan_mode`] to manage the research and design process.
|
||||
- **Tool usage:** Use core tools like
|
||||
[`enter_plan_mode`](../tools/planning.md#1-enter_plan_mode-enterplanmode),
|
||||
[`ask_user`](../tools/ask-user.md), and
|
||||
[`exit_plan_mode`](../tools/planning.md#2-exit_plan_mode-exitplanmode) to
|
||||
manage the research and design process.
|
||||
- **Customization:** Set your own storage locations and policy rules using
|
||||
[custom plan directories](#custom-plan-directory-and-policies) and
|
||||
[custom policies](#custom-policies).
|
||||
|
||||
> **Note:** Use [Conductor] as a reference when building your own custom
|
||||
<!-- prettier-ignore -->
|
||||
> [!TIP]
|
||||
> Use [Conductor] as a reference when building your own custom
|
||||
> planning workflow.
|
||||
|
||||
By using Plan Mode as its execution environment, your custom methodology can
|
||||
@@ -302,8 +429,9 @@ high-reasoning model routing.
|
||||
|
||||
## Automatic Model Routing
|
||||
|
||||
When using an [auto model], Gemini CLI automatically optimizes [model routing]
|
||||
based on the current phase of your task:
|
||||
When using an [auto model](../reference/configuration.md#model), Gemini CLI
|
||||
automatically optimizes [model routing](../cli/telemetry.md#model-routing) based
|
||||
on the current phase of your task:
|
||||
|
||||
1. **Planning Phase:** While in Plan Mode, the CLI routes requests to a
|
||||
high-reasoning **Pro** model to ensure robust architectural decisions and
|
||||
@@ -334,7 +462,8 @@ associated plan files and task trackers.
|
||||
- **Default behavior:** Sessions (and their plans) are retained for **30 days**.
|
||||
- **Configuration:** You can customize this behavior via the `/settings` command
|
||||
(search for **Session Retention**) or in your `settings.json` file. See
|
||||
[session retention] for more details.
|
||||
[session retention](../cli/session-management.md#session-retention) for more
|
||||
details.
|
||||
|
||||
Manual deletion also removes all associated artifacts:
|
||||
|
||||
@@ -344,32 +473,27 @@ Manual deletion also removes all associated artifacts:
|
||||
If you use a [custom plans directory](#custom-plan-directory-and-policies),
|
||||
those files are not automatically deleted and must be managed manually.
|
||||
|
||||
[`list_directory`]: ../tools/file-system.md#1-list_directory-readfolder
|
||||
[`read_file`]: ../tools/file-system.md#2-read_file-readfile
|
||||
[`grep_search`]: ../tools/file-system.md#5-grep_search-searchtext
|
||||
[`write_file`]: ../tools/file-system.md#3-write_file-writefile
|
||||
[`glob`]: ../tools/file-system.md#4-glob-findfiles
|
||||
[`google_web_search`]: ../tools/web-search.md
|
||||
[`replace`]: ../tools/file-system.md#6-replace-edit
|
||||
[MCP tools]: ../tools/mcp-server.md
|
||||
[`save_memory`]: ../tools/memory.md
|
||||
[`activate_skill`]: ./skills.md
|
||||
[`codebase_investigator`]: ../core/subagents.md#codebase_investigator
|
||||
[`cli_help`]: ../core/subagents.md#cli_help
|
||||
[subagents]: ../core/subagents.md
|
||||
[custom subagents]: ../core/subagents.md#creating-custom-subagents
|
||||
[policy engine]: ../reference/policy-engine.md
|
||||
[`enter_plan_mode`]: ../tools/planning.md#1-enter_plan_mode-enterplanmode
|
||||
[`exit_plan_mode`]: ../tools/planning.md#2-exit_plan_mode-exitplanmode
|
||||
[`ask_user`]: ../tools/ask-user.md
|
||||
[YOLO mode]: ../reference/configuration.md#command-line-arguments
|
||||
## Non-interactive execution
|
||||
|
||||
When running Gemini CLI in non-interactive environments (such as headless
|
||||
scripts or CI/CD pipelines), Plan Mode optimizes for automated workflows:
|
||||
|
||||
- **Automatic transitions:** The policy engine automatically approves the
|
||||
`enter_plan_mode` and `exit_plan_mode` tools without prompting for user
|
||||
confirmation.
|
||||
- **Automated implementation:** When exiting Plan Mode to execute the plan,
|
||||
Gemini CLI automatically switches to
|
||||
[YOLO mode](../reference/policy-engine.md#approval-modes) instead of the
|
||||
standard Default mode. This allows the CLI to execute the implementation steps
|
||||
automatically without hanging on interactive tool approvals.
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
gemini --approval-mode plan -p "Analyze telemetry and suggest improvements"
|
||||
```
|
||||
|
||||
[`plan.toml`]:
|
||||
https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/policy/policies/plan.toml
|
||||
[auto model]: ../reference/configuration.md#model-settings
|
||||
[model routing]: ./telemetry.md#model-routing
|
||||
[preferred external editor]: ../reference/configuration.md#general
|
||||
[session retention]: ./session-management.md#session-retention
|
||||
[extensions]: ../extensions/index.md
|
||||
[Conductor]: https://github.com/gemini-cli-extensions/conductor
|
||||
[open an issue]: https://github.com/google-gemini/gemini-cli/issues
|
||||
[Agent Skills]: ./skills.md
|
||||
|
||||