@shareai-lab/kode 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +649 -25
- package/README.zh-CN.md +579 -0
- package/cli-acp.js +3 -17
- package/cli.js +5 -7
- package/dist/chunks/Doctor-M3J7GRTJ.js +12 -0
- package/dist/chunks/LogList-ISWZ6DDD.js +121 -0
- package/dist/chunks/LogList-ISWZ6DDD.js.map +7 -0
- package/dist/chunks/REPL-RQ6LO6S7.js +56 -0
- package/dist/chunks/ResumeConversation-6DMVBEGH.js +56 -0
- package/dist/chunks/agentLoader-FCRG3TFJ.js +31 -0
- package/dist/{agentsValidate-7LH4HTNR.js → chunks/agentsValidate-PEWMYN4Q.js} +97 -69
- package/dist/chunks/agentsValidate-PEWMYN4Q.js.map +7 -0
- package/dist/{ask-3NHFFUQG.js → chunks/ask-D7SOHJ6Z.js} +36 -44
- package/dist/chunks/ask-D7SOHJ6Z.js.map +7 -0
- package/dist/chunks/autoUpdater-CNESBOKO.js +19 -0
- package/dist/{chunk-AFFSCMYS.js → chunks/chunk-2JN5MY67.js} +12 -14
- package/dist/chunks/chunk-2JN5MY67.js.map +7 -0
- package/dist/chunks/chunk-2QONJ5MG.js +14 -0
- package/dist/chunks/chunk-2QONJ5MG.js.map +7 -0
- package/dist/chunks/chunk-2WEXPKHH.js +903 -0
- package/dist/chunks/chunk-2WEXPKHH.js.map +7 -0
- package/dist/{chunk-ARZSBOAO.js → chunks/chunk-3BYE3ME6.js} +717 -792
- package/dist/chunks/chunk-3BYE3ME6.js.map +7 -0
- package/dist/chunks/chunk-3JDNWX7W.js +1264 -0
- package/dist/chunks/chunk-3JDNWX7W.js.map +7 -0
- package/dist/chunks/chunk-3OEJVB5A.js +906 -0
- package/dist/chunks/chunk-3OEJVB5A.js.map +7 -0
- package/dist/chunks/chunk-3TNIOEBO.js +369 -0
- package/dist/chunks/chunk-3TNIOEBO.js.map +7 -0
- package/dist/chunks/chunk-4A46ZXMJ.js +67 -0
- package/dist/chunks/chunk-4A46ZXMJ.js.map +7 -0
- package/dist/{chunk-UHYRLID6.js → chunks/chunk-4ATBQOFO.js} +107 -55
- package/dist/chunks/chunk-4ATBQOFO.js.map +7 -0
- package/dist/chunks/chunk-4CRUCZR4.js +0 -0
- package/dist/{chunk-YC6LJCDE.js → chunks/chunk-4EO6SIQY.js} +32 -75
- package/dist/chunks/chunk-4EO6SIQY.js.map +7 -0
- package/dist/chunks/chunk-53M46S5I.js +64 -0
- package/dist/chunks/chunk-53M46S5I.js.map +7 -0
- package/dist/{chunk-JC6NCUG5.js → chunks/chunk-54KOYG5C.js} +0 -2
- package/dist/{chunk-EZXMVTDU.js → chunks/chunk-6BAS4WY6.js} +29 -45
- package/dist/chunks/chunk-6BAS4WY6.js.map +7 -0
- package/dist/{chunk-3IN27HA5.js → chunks/chunk-6KRRFSDN.js} +4 -6
- package/dist/chunks/chunk-6KRRFSDN.js.map +7 -0
- package/dist/chunks/chunk-6LJNZK4K.js +39 -0
- package/dist/chunks/chunk-6LJNZK4K.js.map +7 -0
- package/dist/chunks/chunk-6ZWEOSEI.js +666 -0
- package/dist/chunks/chunk-6ZWEOSEI.js.map +7 -0
- package/dist/chunks/chunk-77XDJMBP.js +3326 -0
- package/dist/chunks/chunk-77XDJMBP.js.map +7 -0
- package/dist/chunks/chunk-7RRW4NTB.js +6454 -0
- package/dist/chunks/chunk-7RRW4NTB.js.map +7 -0
- package/dist/chunks/chunk-7X3TW4JB.js +4520 -0
- package/dist/chunks/chunk-7X3TW4JB.js.map +7 -0
- package/dist/chunks/chunk-B3MW3YGY.js +1409 -0
- package/dist/chunks/chunk-B3MW3YGY.js.map +7 -0
- package/dist/chunks/chunk-BBJFHTBC.js +28 -0
- package/dist/chunks/chunk-BBJFHTBC.js.map +7 -0
- package/dist/chunks/chunk-BHDHXOXB.js +24 -0
- package/dist/chunks/chunk-BHDHXOXB.js.map +7 -0
- package/dist/{chunk-73WGVYLQ.js → chunks/chunk-BTA7SZ26.js} +152 -223
- package/dist/chunks/chunk-BTA7SZ26.js.map +7 -0
- package/dist/chunks/chunk-CDGRYGPZ.js +103 -0
- package/dist/chunks/chunk-CDGRYGPZ.js.map +7 -0
- package/dist/{chunk-S6HRABTA.js → chunks/chunk-CP6E5UG6.js} +1 -4
- package/dist/chunks/chunk-CP6E5UG6.js.map +7 -0
- package/dist/{chunk-QVLYOPO5.js → chunks/chunk-DQ4JHXMT.js} +462 -424
- package/dist/chunks/chunk-DQ4JHXMT.js.map +7 -0
- package/dist/chunks/chunk-DXD76CMV.js +208 -0
- package/dist/chunks/chunk-DXD76CMV.js.map +7 -0
- package/dist/chunks/chunk-GCQCAXJZ.js +0 -0
- package/dist/chunks/chunk-GELCZWMB.js +42 -0
- package/dist/chunks/chunk-GELCZWMB.js.map +7 -0
- package/dist/{chunk-K2CWOTI2.js → chunks/chunk-HJYOH4HC.js} +23 -18
- package/dist/chunks/chunk-HJYOH4HC.js.map +7 -0
- package/dist/chunks/chunk-HPYNW6TT.js +744 -0
- package/dist/chunks/chunk-HPYNW6TT.js.map +7 -0
- package/dist/{chunk-RZWOUA25.js → chunks/chunk-HRJ3ICQK.js} +59 -55
- package/dist/chunks/chunk-HRJ3ICQK.js.map +7 -0
- package/dist/{chunk-DZE5YA7L.js → chunks/chunk-IFCIADS3.js} +571 -573
- package/dist/chunks/chunk-IFCIADS3.js.map +7 -0
- package/dist/chunks/chunk-IN7XZ7BC.js +27 -0
- package/dist/chunks/chunk-IN7XZ7BC.js.map +7 -0
- package/dist/chunks/chunk-L7P4M4KW.js +193 -0
- package/dist/chunks/chunk-L7P4M4KW.js.map +7 -0
- package/dist/chunks/chunk-LB6TCPDI.js +0 -0
- package/dist/{chunk-3RUXVV4S.js → chunks/chunk-LOCXPQNJ.js} +1 -4
- package/dist/{chunk-3RUXVV4S.js.map → chunks/chunk-LOCXPQNJ.js.map} +2 -2
- package/dist/{chunk-7M2YN6TU.js → chunks/chunk-LOD5ZHCI.js} +213 -208
- package/dist/chunks/chunk-LOD5ZHCI.js.map +7 -0
- package/dist/{chunk-S3J2TLV6.js → chunks/chunk-M7P3QNRU.js} +1 -4
- package/dist/{chunk-S3J2TLV6.js.map → chunks/chunk-M7P3QNRU.js.map} +2 -2
- package/dist/chunks/chunk-PPHLQVL7.js +4234 -0
- package/dist/chunks/chunk-PPHLQVL7.js.map +7 -0
- package/dist/{chunk-ABLVTESJ.js → chunks/chunk-QAXE37B5.js} +1 -4
- package/dist/chunks/chunk-QAXE37B5.js.map +7 -0
- package/dist/chunks/chunk-QHQOBUF6.js +60 -0
- package/dist/chunks/chunk-QHQOBUF6.js.map +7 -0
- package/dist/{chunk-W7GRKO7Q.js → chunks/chunk-RPJXO7GG.js} +241 -214
- package/dist/chunks/chunk-RPJXO7GG.js.map +7 -0
- package/dist/{chunk-NPFOMITO.js → chunks/chunk-SWQV4KSY.js} +1 -4
- package/dist/{chunk-NPFOMITO.js.map → chunks/chunk-SWQV4KSY.js.map} +2 -2
- package/dist/chunks/chunk-SZLAPULP.js +28 -0
- package/dist/chunks/chunk-SZLAPULP.js.map +7 -0
- package/dist/{chunk-7U7L4NMD.js → chunks/chunk-T7RB5V5J.js} +23 -25
- package/dist/chunks/chunk-T7RB5V5J.js.map +7 -0
- package/dist/{chunk-HN4E4UUQ.js → chunks/chunk-TI2CTTMA.js} +25 -17
- package/dist/chunks/chunk-TI2CTTMA.js.map +7 -0
- package/dist/{chunk-ZVDRDPII.js → chunks/chunk-TNGVRTO5.js} +45 -20
- package/dist/chunks/chunk-TNGVRTO5.js.map +7 -0
- package/dist/chunks/chunk-TNWB3U5Y.js +2077 -0
- package/dist/chunks/chunk-TNWB3U5Y.js.map +7 -0
- package/dist/chunks/chunk-U2IHWPCU.js +12 -0
- package/dist/chunks/chunk-U2IHWPCU.js.map +7 -0
- package/dist/{chunk-KAA5BGMQ.js → chunks/chunk-UNOY3VJ2.js} +1 -4
- package/dist/{chunk-KAA5BGMQ.js.map → chunks/chunk-UNOY3VJ2.js.map} +2 -2
- package/dist/{chunk-MWRSY4X6.js → chunks/chunk-UVDJL6ZZ.js} +97 -58
- package/dist/chunks/chunk-UVDJL6ZZ.js.map +7 -0
- package/dist/chunks/chunk-VNCW4C2Z.js +13452 -0
- package/dist/chunks/chunk-VNCW4C2Z.js.map +7 -0
- package/dist/chunks/chunk-W5EGGA44.js +15 -0
- package/dist/chunks/chunk-W5EGGA44.js.map +7 -0
- package/dist/chunks/chunk-XR2W3MAM.js +1533 -0
- package/dist/chunks/chunk-XR2W3MAM.js.map +7 -0
- package/dist/{chunk-STSX7GIX.js → chunks/chunk-YIO5EBMQ.js} +423 -377
- package/dist/chunks/chunk-YIO5EBMQ.js.map +7 -0
- package/dist/chunks/chunk-ZBVLKZ5V.js +1062 -0
- package/dist/chunks/chunk-ZBVLKZ5V.js.map +7 -0
- package/dist/{chunk-E6YNABER.js → chunks/chunk-ZCLTZIVP.js} +1 -4
- package/dist/chunks/chunk-ZCLTZIVP.js.map +7 -0
- package/dist/chunks/client-SILZNM5N.js +42 -0
- package/dist/{config-RUSD6G5Y.js → chunks/config-25HRTPSP.js} +48 -10
- package/dist/chunks/cost-tracker-Z2UZT2J5.js +28 -0
- package/dist/{customCommands-TOIJFZAL.js → chunks/customCommands-TYMYZRG5.js} +11 -8
- package/dist/chunks/engine-MRVF6FK6.js +39 -0
- package/dist/{env-XGKBLU3D.js → chunks/env-TJ5NOBEB.js} +7 -5
- package/dist/{kodeAgentSessionId-X6XWQW7B.js → chunks/kodeAgentSessionId-VTNISJ2L.js} +2 -4
- package/dist/chunks/kodeAgentSessionLoad-YB2RKBGJ.js +15 -0
- package/dist/chunks/kodeAgentSessionResume-DZSIVKVA.js +13 -0
- package/dist/chunks/kodeAgentStreamJson-X5PLS2S6.js +11 -0
- package/dist/{kodeAgentStreamJsonSession-UGEZJJEB.js → chunks/kodeAgentStreamJsonSession-RDXM4XYF.js} +38 -24
- package/dist/chunks/kodeAgentStreamJsonSession-RDXM4XYF.js.map +7 -0
- package/dist/{chunk-4RTX4AG4.js → chunks/kodeAgentStructuredStdio-SVGDSB4P.js} +14 -9
- package/dist/chunks/kodeAgentStructuredStdio-SVGDSB4P.js.map +7 -0
- package/dist/{kodeHooks-QWM36A3D.js → chunks/kodeHooks-RVKYRJHG.js} +11 -9
- package/dist/{llm-ZUQC4WYM.js → chunks/llm-62N6T5ZT.js} +1734 -1526
- package/dist/chunks/llm-62N6T5ZT.js.map +7 -0
- package/dist/chunks/llmLazy-ZUSSE3ZA.js +13 -0
- package/dist/{mentionProcessor-EE3XFHCJ.js → chunks/mentionProcessor-RJW5UPJD.js} +46 -16
- package/dist/chunks/mentionProcessor-RJW5UPJD.js.map +7 -0
- package/dist/{messages-EOYQKPGM.js → chunks/messages-EEWWLPHN.js} +2 -6
- package/dist/chunks/model-5TIEKQPD.js +37 -0
- package/dist/{openai-RRCWW33N.js → chunks/openai-XXK3YZG4.js} +13 -10
- package/dist/{outputStyles-62Q3VH2J.js → chunks/outputStyles-FAJTXN2A.js} +6 -9
- package/dist/chunks/permissions-HO7INPWM.js +27 -0
- package/dist/{pluginRuntime-6ETCZ2LL.js → chunks/pluginRuntime-C7K5ULK2.js} +31 -48
- package/dist/chunks/pluginRuntime-C7K5ULK2.js.map +7 -0
- package/dist/chunks/pluginValidation-DAM7WRTC.js +20 -0
- package/dist/chunks/registry-XYJXMOA5.js +60 -0
- package/dist/chunks/responsesStreaming-JNGE2P3D.js +8 -0
- package/dist/chunks/runNonTextPrintMode-SVBLCZQX.js +577 -0
- package/dist/chunks/runNonTextPrintMode-SVBLCZQX.js.map +7 -0
- package/dist/chunks/server-REXXF5IK.js +46 -0
- package/dist/{skillMarketplace-3RXQBVOL.js → chunks/skillMarketplace-N4HVHNST.js} +8 -6
- package/dist/chunks/src-OROQIWP3.js +44 -0
- package/dist/chunks/src-QXLGGMUW.js +1647 -0
- package/dist/chunks/src-QXLGGMUW.js.map +7 -0
- package/dist/{cli-DOPVY2CW.js → chunks/src-SSDT6MVP.js} +2659 -3384
- package/dist/chunks/src-SSDT6MVP.js.map +7 -0
- package/dist/chunks/theme-YBJUIMWK.js +10 -0
- package/dist/{toolPermissionContext-65L65VEZ.js → chunks/toolPermissionContext-MOCTRR7N.js} +2 -4
- package/dist/chunks/toolPermissionSettings-EV2EJAXL.js +18 -0
- package/dist/chunks/toolPermissionSettings-EV2EJAXL.js.map +7 -0
- package/dist/chunks/uuid-6577SO6X.js +7 -0
- package/dist/chunks/uuid-6577SO6X.js.map +7 -0
- package/dist/chunks/webOnlyMode-ALXX7UQY.js +66 -0
- package/dist/chunks/webOnlyMode-ALXX7UQY.js.map +7 -0
- package/dist/entrypoints/cli.js +10 -0
- package/dist/entrypoints/cli.js.map +7 -0
- package/dist/entrypoints/daemon.js +10 -0
- package/dist/entrypoints/daemon.js.map +7 -0
- package/dist/entrypoints/mcp.js +71 -0
- package/dist/entrypoints/mcp.js.map +7 -0
- package/dist/index.js +6 -7
- package/dist/index.js.map +3 -3
- package/dist/sdk/client.cjs +391 -0
- package/dist/sdk/client.cjs.map +7 -0
- package/dist/sdk/client.js +364 -0
- package/dist/sdk/client.js.map +7 -0
- package/dist/sdk/core.cjs +19932 -0
- package/dist/sdk/core.cjs.map +7 -0
- package/dist/sdk/core.js +19893 -0
- package/dist/sdk/core.js.map +7 -0
- package/dist/sdk/daemon-client.cjs +257 -0
- package/dist/sdk/daemon-client.cjs.map +7 -0
- package/dist/sdk/daemon-client.js +221 -0
- package/dist/sdk/daemon-client.js.map +7 -0
- package/dist/sdk/protocol.cjs +170 -0
- package/dist/sdk/protocol.cjs.map +7 -0
- package/dist/sdk/protocol.js +140 -0
- package/dist/sdk/protocol.js.map +7 -0
- package/dist/sdk/runtime-node.cjs +236 -0
- package/dist/sdk/runtime-node.cjs.map +7 -0
- package/dist/sdk/runtime-node.js +222 -0
- package/dist/sdk/runtime-node.js.map +7 -0
- package/dist/sdk/runtime.cjs +17 -0
- package/dist/sdk/runtime.cjs.map +7 -0
- package/dist/sdk/runtime.js +0 -0
- package/dist/sdk/runtime.js.map +7 -0
- package/dist/sdk/tools.cjs +30300 -0
- package/dist/sdk/tools.cjs.map +7 -0
- package/dist/sdk/tools.js +30282 -0
- package/dist/sdk/tools.js.map +7 -0
- package/dist/webui/assets/index-5hlfByVS.css +1 -0
- package/dist/webui/assets/index-BR9lm1lA.js +82 -0
- package/dist/webui/index.html +28 -0
- package/package.json +93 -22
- package/scripts/binary-utils.cjs +12 -4
- package/scripts/cli-acp-wrapper.cjs +3 -17
- package/scripts/cli-wrapper.cjs +5 -7
- package/scripts/postinstall.js +8 -4
- package/dist/REPL-CW7AYLVL.js +0 -42
- package/dist/acp-VEPJ74LT.js +0 -1357
- package/dist/acp-VEPJ74LT.js.map +0 -7
- package/dist/agentsValidate-7LH4HTNR.js.map +0 -7
- package/dist/ask-3NHFFUQG.js.map +0 -7
- package/dist/autoUpdater-ITPIHCOI.js +0 -17
- package/dist/chunk-3IN27HA5.js.map +0 -7
- package/dist/chunk-4FX3IVPT.js +0 -164
- package/dist/chunk-4FX3IVPT.js.map +0 -7
- package/dist/chunk-4RTX4AG4.js.map +0 -7
- package/dist/chunk-5PDP7R6N.js +0 -515
- package/dist/chunk-5PDP7R6N.js.map +0 -7
- package/dist/chunk-73WGVYLQ.js.map +0 -7
- package/dist/chunk-7M2YN6TU.js.map +0 -7
- package/dist/chunk-7U7L4NMD.js.map +0 -7
- package/dist/chunk-ABLVTESJ.js.map +0 -7
- package/dist/chunk-AFFSCMYS.js.map +0 -7
- package/dist/chunk-ARZSBOAO.js.map +0 -7
- package/dist/chunk-CIG63V4E.js +0 -72
- package/dist/chunk-CIG63V4E.js.map +0 -7
- package/dist/chunk-CM3EGTG6.js +0 -1609
- package/dist/chunk-CM3EGTG6.js.map +0 -7
- package/dist/chunk-DZE5YA7L.js.map +0 -7
- package/dist/chunk-E6YNABER.js.map +0 -7
- package/dist/chunk-EZXMVTDU.js.map +0 -7
- package/dist/chunk-F2SJXUDI.js +0 -148
- package/dist/chunk-F2SJXUDI.js.map +0 -7
- package/dist/chunk-FC5ZCKBI.js +0 -30167
- package/dist/chunk-FC5ZCKBI.js.map +0 -7
- package/dist/chunk-HCBELH4J.js +0 -145
- package/dist/chunk-HCBELH4J.js.map +0 -7
- package/dist/chunk-HN4E4UUQ.js.map +0 -7
- package/dist/chunk-IZVMU4S2.js +0 -654
- package/dist/chunk-IZVMU4S2.js.map +0 -7
- package/dist/chunk-K2CWOTI2.js.map +0 -7
- package/dist/chunk-LC4TVOCZ.js +0 -835
- package/dist/chunk-LC4TVOCZ.js.map +0 -7
- package/dist/chunk-MIW7N2MY.js +0 -2613
- package/dist/chunk-MIW7N2MY.js.map +0 -7
- package/dist/chunk-MWRSY4X6.js.map +0 -7
- package/dist/chunk-ND3XWFO6.js +0 -34
- package/dist/chunk-ND3XWFO6.js.map +0 -7
- package/dist/chunk-QVLYOPO5.js.map +0 -7
- package/dist/chunk-RZWOUA25.js.map +0 -7
- package/dist/chunk-S6HRABTA.js.map +0 -7
- package/dist/chunk-STSX7GIX.js.map +0 -7
- package/dist/chunk-UHYRLID6.js.map +0 -7
- package/dist/chunk-UKHTVRJM.js +0 -47
- package/dist/chunk-UKHTVRJM.js.map +0 -7
- package/dist/chunk-UYXEDKOZ.js +0 -24
- package/dist/chunk-UYXEDKOZ.js.map +0 -7
- package/dist/chunk-W7GRKO7Q.js.map +0 -7
- package/dist/chunk-WVHORZQ5.js +0 -17
- package/dist/chunk-WVHORZQ5.js.map +0 -7
- package/dist/chunk-WWUWDNWW.js +0 -49
- package/dist/chunk-WWUWDNWW.js.map +0 -7
- package/dist/chunk-YC6LJCDE.js.map +0 -7
- package/dist/chunk-YXYYDIMI.js +0 -2931
- package/dist/chunk-YXYYDIMI.js.map +0 -7
- package/dist/chunk-ZVDRDPII.js.map +0 -7
- package/dist/cli-DOPVY2CW.js.map +0 -7
- package/dist/commands-2BF2CJ3A.js +0 -46
- package/dist/context-6FXPETYH.js +0 -30
- package/dist/costTracker-6SL26FDB.js +0 -19
- package/dist/kodeAgentSessionLoad-MITZADPB.js +0 -18
- package/dist/kodeAgentSessionResume-GVRWB4WO.js +0 -16
- package/dist/kodeAgentStreamJson-NXFN7TXH.js +0 -13
- package/dist/kodeAgentStreamJsonSession-UGEZJJEB.js.map +0 -7
- package/dist/kodeAgentStructuredStdio-HGWJT7CU.js +0 -10
- package/dist/llm-ZUQC4WYM.js.map +0 -7
- package/dist/llmLazy-54QQHA54.js +0 -15
- package/dist/loader-FYHJQES5.js +0 -28
- package/dist/mcp-J332IKT3.js +0 -49
- package/dist/mentionProcessor-EE3XFHCJ.js.map +0 -7
- package/dist/model-FV3JDJKH.js +0 -30
- package/dist/pluginRuntime-6ETCZ2LL.js.map +0 -7
- package/dist/pluginValidation-I4YKUWGS.js +0 -17
- package/dist/prompts-ZLEKDD77.js +0 -48
- package/dist/query-VFRJPBGD.js +0 -50
- package/dist/responsesStreaming-AW344PQO.js +0 -10
- package/dist/ripgrep-3NTIKQYW.js +0 -17
- package/dist/state-P5G6CO5V.js +0 -16
- package/dist/theme-3LWP3BG7.js +0 -14
- package/dist/toolPermissionSettings-3ROBVTUK.js +0 -18
- package/dist/tools-RO7HSSE5.js +0 -47
- package/dist/userInput-JSBJRFSK.js +0 -311
- package/dist/userInput-JSBJRFSK.js.map +0 -7
- package/dist/uuid-QN2CNKKN.js +0 -9
- /package/dist/{REPL-CW7AYLVL.js.map → chunks/Doctor-M3J7GRTJ.js.map} +0 -0
- /package/dist/{autoUpdater-ITPIHCOI.js.map → chunks/REPL-RQ6LO6S7.js.map} +0 -0
- /package/dist/{chunk-JC6NCUG5.js.map → chunks/ResumeConversation-6DMVBEGH.js.map} +0 -0
- /package/dist/{commands-2BF2CJ3A.js.map → chunks/agentLoader-FCRG3TFJ.js.map} +0 -0
- /package/dist/{config-RUSD6G5Y.js.map → chunks/autoUpdater-CNESBOKO.js.map} +0 -0
- /package/dist/{context-6FXPETYH.js.map → chunks/chunk-4CRUCZR4.js.map} +0 -0
- /package/dist/{costTracker-6SL26FDB.js.map → chunks/chunk-54KOYG5C.js.map} +0 -0
- /package/dist/{customCommands-TOIJFZAL.js.map → chunks/chunk-GCQCAXJZ.js.map} +0 -0
- /package/dist/{env-XGKBLU3D.js.map → chunks/chunk-LB6TCPDI.js.map} +0 -0
- /package/dist/{kodeAgentSessionId-X6XWQW7B.js.map → chunks/client-SILZNM5N.js.map} +0 -0
- /package/dist/{kodeAgentSessionLoad-MITZADPB.js.map → chunks/config-25HRTPSP.js.map} +0 -0
- /package/dist/{kodeAgentSessionResume-GVRWB4WO.js.map → chunks/cost-tracker-Z2UZT2J5.js.map} +0 -0
- /package/dist/{kodeAgentStreamJson-NXFN7TXH.js.map → chunks/customCommands-TYMYZRG5.js.map} +0 -0
- /package/dist/{kodeAgentStructuredStdio-HGWJT7CU.js.map → chunks/engine-MRVF6FK6.js.map} +0 -0
- /package/dist/{kodeHooks-QWM36A3D.js.map → chunks/env-TJ5NOBEB.js.map} +0 -0
- /package/dist/{llmLazy-54QQHA54.js.map → chunks/kodeAgentSessionId-VTNISJ2L.js.map} +0 -0
- /package/dist/{loader-FYHJQES5.js.map → chunks/kodeAgentSessionLoad-YB2RKBGJ.js.map} +0 -0
- /package/dist/{mcp-J332IKT3.js.map → chunks/kodeAgentSessionResume-DZSIVKVA.js.map} +0 -0
- /package/dist/{messages-EOYQKPGM.js.map → chunks/kodeAgentStreamJson-X5PLS2S6.js.map} +0 -0
- /package/dist/{model-FV3JDJKH.js.map → chunks/kodeHooks-RVKYRJHG.js.map} +0 -0
- /package/dist/{openai-RRCWW33N.js.map → chunks/llmLazy-ZUSSE3ZA.js.map} +0 -0
- /package/dist/{outputStyles-62Q3VH2J.js.map → chunks/messages-EEWWLPHN.js.map} +0 -0
- /package/dist/{pluginValidation-I4YKUWGS.js.map → chunks/model-5TIEKQPD.js.map} +0 -0
- /package/dist/{prompts-ZLEKDD77.js.map → chunks/openai-XXK3YZG4.js.map} +0 -0
- /package/dist/{query-VFRJPBGD.js.map → chunks/outputStyles-FAJTXN2A.js.map} +0 -0
- /package/dist/{responsesStreaming-AW344PQO.js.map → chunks/permissions-HO7INPWM.js.map} +0 -0
- /package/dist/{ripgrep-3NTIKQYW.js.map → chunks/pluginValidation-DAM7WRTC.js.map} +0 -0
- /package/dist/{skillMarketplace-3RXQBVOL.js.map → chunks/registry-XYJXMOA5.js.map} +0 -0
- /package/dist/{state-P5G6CO5V.js.map → chunks/responsesStreaming-JNGE2P3D.js.map} +0 -0
- /package/dist/{theme-3LWP3BG7.js.map → chunks/server-REXXF5IK.js.map} +0 -0
- /package/dist/{toolPermissionContext-65L65VEZ.js.map → chunks/skillMarketplace-N4HVHNST.js.map} +0 -0
- /package/dist/{toolPermissionSettings-3ROBVTUK.js.map → chunks/src-OROQIWP3.js.map} +0 -0
- /package/dist/{tools-RO7HSSE5.js.map → chunks/theme-YBJUIMWK.js.map} +0 -0
- /package/dist/{uuid-QN2CNKKN.js.map → chunks/toolPermissionContext-MOCTRR7N.js.map} +0 -0
|
@@ -0,0 +1,3326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AskUserQuestionTool,
|
|
3
|
+
BashTool,
|
|
4
|
+
EnterPlanModeTool,
|
|
5
|
+
ExitPlanModeTool,
|
|
6
|
+
FileEditTool,
|
|
7
|
+
FileReadTool,
|
|
8
|
+
FileWriteTool,
|
|
9
|
+
GlobTool,
|
|
10
|
+
GrepTool,
|
|
11
|
+
NotebookEditTool,
|
|
12
|
+
OutputLine,
|
|
13
|
+
SkillTool,
|
|
14
|
+
SlashCommandTool,
|
|
15
|
+
WebFetchTool,
|
|
16
|
+
applyMarkdown,
|
|
17
|
+
maybeTruncateVerboseToolOutput
|
|
18
|
+
} from "./chunk-7RRW4NTB.js";
|
|
19
|
+
import {
|
|
20
|
+
getClients,
|
|
21
|
+
getMCPTools
|
|
22
|
+
} from "./chunk-ZBVLKZ5V.js";
|
|
23
|
+
import {
|
|
24
|
+
getActiveAgents,
|
|
25
|
+
getAgentByType,
|
|
26
|
+
getAvailableAgentTypes
|
|
27
|
+
} from "./chunk-BTA7SZ26.js";
|
|
28
|
+
import {
|
|
29
|
+
hasPermissionsToUseTool,
|
|
30
|
+
hasReadPermission
|
|
31
|
+
} from "./chunk-PPHLQVL7.js";
|
|
32
|
+
import {
|
|
33
|
+
countTokens,
|
|
34
|
+
getAbsolutePath,
|
|
35
|
+
query,
|
|
36
|
+
startWatchingTodoFile
|
|
37
|
+
} from "./chunk-TNWB3U5Y.js";
|
|
38
|
+
import {
|
|
39
|
+
getTheme
|
|
40
|
+
} from "./chunk-T7RB5V5J.js";
|
|
41
|
+
import {
|
|
42
|
+
getMaxThinkingTokens
|
|
43
|
+
} from "./chunk-53M46S5I.js";
|
|
44
|
+
import {
|
|
45
|
+
getAgentPrompt,
|
|
46
|
+
getContext
|
|
47
|
+
} from "./chunk-HPYNW6TT.js";
|
|
48
|
+
import {
|
|
49
|
+
emitReminderEvent,
|
|
50
|
+
generateAgentId,
|
|
51
|
+
getTodos,
|
|
52
|
+
setTodos
|
|
53
|
+
} from "./chunk-LOD5ZHCI.js";
|
|
54
|
+
import {
|
|
55
|
+
formatDuration,
|
|
56
|
+
formatNumber
|
|
57
|
+
} from "./chunk-GELCZWMB.js";
|
|
58
|
+
import {
|
|
59
|
+
queryLLM
|
|
60
|
+
} from "./chunk-BHDHXOXB.js";
|
|
61
|
+
import {
|
|
62
|
+
INTERRUPT_MESSAGE,
|
|
63
|
+
createAssistantMessage,
|
|
64
|
+
createUserMessage,
|
|
65
|
+
getLastAssistantMessageId
|
|
66
|
+
} from "./chunk-HRJ3ICQK.js";
|
|
67
|
+
import {
|
|
68
|
+
getModelManager
|
|
69
|
+
} from "./chunk-6ZWEOSEI.js";
|
|
70
|
+
import {
|
|
71
|
+
debug
|
|
72
|
+
} from "./chunk-YIO5EBMQ.js";
|
|
73
|
+
import {
|
|
74
|
+
getMessagesPath,
|
|
75
|
+
getNextAvailableLogSidechainNumber,
|
|
76
|
+
logError,
|
|
77
|
+
overwriteLog
|
|
78
|
+
} from "./chunk-3OEJVB5A.js";
|
|
79
|
+
import {
|
|
80
|
+
getCwd
|
|
81
|
+
} from "./chunk-BBJFHTBC.js";
|
|
82
|
+
import {
|
|
83
|
+
BunShell,
|
|
84
|
+
readTaskOutput
|
|
85
|
+
} from "./chunk-B3MW3YGY.js";
|
|
86
|
+
|
|
87
|
+
// packages/tools/src/registry.ts
|
|
88
|
+
import { memoize as memoize2 } from "lodash-es";
|
|
89
|
+
|
|
90
|
+
// packages/tools/src/tools/ai/AskExpertModelTool/AskExpertModelTool.tsx
|
|
91
|
+
import { Box, Text } from "ink";
|
|
92
|
+
import React from "react";
|
|
93
|
+
import { z } from "zod";
|
|
94
|
+
|
|
95
|
+
// packages/core/src/utils/expertChatStorage.ts
|
|
96
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
97
|
+
import { join } from "path";
|
|
98
|
+
import { homedir } from "os";
|
|
99
|
+
import { randomUUID } from "crypto";
|
|
100
|
+
function getExpertChatDirectory() {
|
|
101
|
+
const configDir = process.env.KODE_CONFIG_DIR ?? process.env.ANYKODE_CONFIG_DIR ?? join(homedir(), ".kode");
|
|
102
|
+
const expertChatDir = join(configDir, "expert-chats");
|
|
103
|
+
if (!existsSync(expertChatDir)) {
|
|
104
|
+
mkdirSync(expertChatDir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
return expertChatDir;
|
|
107
|
+
}
|
|
108
|
+
function getSessionFilePath(sessionId) {
|
|
109
|
+
return join(getExpertChatDirectory(), `${sessionId}.json`);
|
|
110
|
+
}
|
|
111
|
+
function createExpertChatSession(expertModel) {
|
|
112
|
+
const sessionId = randomUUID().slice(0, 5);
|
|
113
|
+
const session = {
|
|
114
|
+
sessionId,
|
|
115
|
+
expertModel,
|
|
116
|
+
messages: [],
|
|
117
|
+
createdAt: Date.now(),
|
|
118
|
+
lastUpdated: Date.now()
|
|
119
|
+
};
|
|
120
|
+
saveExpertChatSession(session);
|
|
121
|
+
return session;
|
|
122
|
+
}
|
|
123
|
+
function loadExpertChatSession(sessionId) {
|
|
124
|
+
const filePath = getSessionFilePath(sessionId);
|
|
125
|
+
if (!existsSync(filePath)) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const content = readFileSync(filePath, "utf-8");
|
|
130
|
+
return JSON.parse(content);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logError(error);
|
|
133
|
+
debug.warn("EXPERT_CHAT_SESSION_LOAD_FAILED", {
|
|
134
|
+
sessionId,
|
|
135
|
+
error: error instanceof Error ? error.message : String(error)
|
|
136
|
+
});
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function saveExpertChatSession(session) {
|
|
141
|
+
const filePath = getSessionFilePath(session.sessionId);
|
|
142
|
+
try {
|
|
143
|
+
session.lastUpdated = Date.now();
|
|
144
|
+
writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
|
|
145
|
+
} catch (error) {
|
|
146
|
+
logError(error);
|
|
147
|
+
debug.warn("EXPERT_CHAT_SESSION_SAVE_FAILED", {
|
|
148
|
+
sessionId: session.sessionId,
|
|
149
|
+
error: error instanceof Error ? error.message : String(error)
|
|
150
|
+
});
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function addMessageToSession(sessionId, role, content) {
|
|
155
|
+
const session = loadExpertChatSession(sessionId);
|
|
156
|
+
if (!session) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
session.messages.push({ role, content });
|
|
160
|
+
saveExpertChatSession(session);
|
|
161
|
+
return session;
|
|
162
|
+
}
|
|
163
|
+
function getSessionMessages(sessionId) {
|
|
164
|
+
const session = loadExpertChatSession(sessionId);
|
|
165
|
+
return session?.messages || [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// packages/tools/src/tools/ai/AskExpertModelTool/call.ts
|
|
169
|
+
function isRecord(value) {
|
|
170
|
+
return !!value && typeof value === "object";
|
|
171
|
+
}
|
|
172
|
+
function extractAssistantText(message) {
|
|
173
|
+
const content = message?.message?.content;
|
|
174
|
+
if (typeof content === "string") return content;
|
|
175
|
+
if (!Array.isArray(content)) return "";
|
|
176
|
+
const parts = [];
|
|
177
|
+
for (const block of content) {
|
|
178
|
+
if (!isRecord(block)) continue;
|
|
179
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
180
|
+
parts.push(block.text);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return parts.join("\n");
|
|
184
|
+
}
|
|
185
|
+
function isInterrupted(error, abortController, interruptedFlag) {
|
|
186
|
+
if (interruptedFlag) return true;
|
|
187
|
+
if (abortController.signal.aborted) return true;
|
|
188
|
+
if (!isRecord(error)) return false;
|
|
189
|
+
return error.name === "AbortError";
|
|
190
|
+
}
|
|
191
|
+
async function withTimeout(promise, timeoutMs) {
|
|
192
|
+
return new Promise((resolve, reject) => {
|
|
193
|
+
const timeoutId = setTimeout(() => {
|
|
194
|
+
reject(new Error(`Expert model query timed out after ${timeoutMs}ms`));
|
|
195
|
+
}, timeoutMs);
|
|
196
|
+
promise.then((value) => resolve(value)).catch((err) => reject(err)).finally(() => clearTimeout(timeoutId));
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function interruptResult(expertModelName) {
|
|
200
|
+
return {
|
|
201
|
+
type: "result",
|
|
202
|
+
data: {
|
|
203
|
+
chatSessionId: "interrupted",
|
|
204
|
+
expertModelName,
|
|
205
|
+
expertAnswer: INTERRUPT_MESSAGE
|
|
206
|
+
},
|
|
207
|
+
resultForAssistant: INTERRUPT_MESSAGE
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
async function* callAskExpertModelTool(input, context, renderResultForAssistant) {
|
|
211
|
+
const { question, expert_model, chat_session_id } = input;
|
|
212
|
+
const { abortController } = context;
|
|
213
|
+
const expertModel = expert_model;
|
|
214
|
+
let sessionId = "";
|
|
215
|
+
let interrupted = false;
|
|
216
|
+
const abortListener = () => {
|
|
217
|
+
interrupted = true;
|
|
218
|
+
};
|
|
219
|
+
abortController.signal.addEventListener("abort", abortListener);
|
|
220
|
+
try {
|
|
221
|
+
if (abortController.signal.aborted) {
|
|
222
|
+
yield interruptResult(expertModel);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (chat_session_id === "new") {
|
|
226
|
+
sessionId = createExpertChatSession(expertModel).sessionId;
|
|
227
|
+
} else {
|
|
228
|
+
sessionId = chat_session_id;
|
|
229
|
+
const session = loadExpertChatSession(sessionId);
|
|
230
|
+
if (!session) {
|
|
231
|
+
sessionId = createExpertChatSession(expertModel).sessionId;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (interrupted || abortController.signal.aborted) {
|
|
235
|
+
yield interruptResult(expertModel);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const history = (() => {
|
|
239
|
+
try {
|
|
240
|
+
return getSessionMessages(sessionId);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
logError(error);
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
})();
|
|
246
|
+
const conversation = [...history, { role: "user", content: question }];
|
|
247
|
+
const llmMessages = conversation.map(
|
|
248
|
+
(msg) => msg.role === "user" ? createUserMessage(msg.content) : createAssistantMessage(msg.content)
|
|
249
|
+
);
|
|
250
|
+
if (interrupted || abortController.signal.aborted) {
|
|
251
|
+
yield interruptResult(expertModel);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
yield {
|
|
255
|
+
type: "progress",
|
|
256
|
+
content: createAssistantMessage(
|
|
257
|
+
`Connecting to ${expertModel}... (timeout: 5 minutes)`
|
|
258
|
+
)
|
|
259
|
+
};
|
|
260
|
+
const modelManager = getModelManager();
|
|
261
|
+
const modelResolution = modelManager.resolveModelWithInfo(expertModel);
|
|
262
|
+
debug.api("EXPERT_MODEL_RESOLUTION", {
|
|
263
|
+
requestedModel: expertModel,
|
|
264
|
+
success: modelResolution.success,
|
|
265
|
+
profileName: modelResolution.profile?.name,
|
|
266
|
+
profileModelName: modelResolution.profile?.modelName,
|
|
267
|
+
provider: modelResolution.profile?.provider,
|
|
268
|
+
isActive: modelResolution.profile?.isActive,
|
|
269
|
+
error: modelResolution.error
|
|
270
|
+
});
|
|
271
|
+
const timeoutMs = 3e5;
|
|
272
|
+
const response = await withTimeout(
|
|
273
|
+
queryLLM(llmMessages, [], 0, [], abortController.signal, {
|
|
274
|
+
safeMode: false,
|
|
275
|
+
model: expertModel,
|
|
276
|
+
prependCLISysprompt: false
|
|
277
|
+
}),
|
|
278
|
+
timeoutMs
|
|
279
|
+
);
|
|
280
|
+
if (interrupted || abortController.signal.aborted) {
|
|
281
|
+
yield interruptResult(expertModel);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const expertAnswer = extractAssistantText(response).trim();
|
|
285
|
+
if (!expertAnswer) {
|
|
286
|
+
throw new Error("Expert response was empty");
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
addMessageToSession(sessionId, "user", question);
|
|
290
|
+
addMessageToSession(sessionId, "assistant", expertAnswer);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
logError(error);
|
|
293
|
+
}
|
|
294
|
+
const result = {
|
|
295
|
+
chatSessionId: sessionId,
|
|
296
|
+
expertModelName: expertModel,
|
|
297
|
+
expertAnswer
|
|
298
|
+
};
|
|
299
|
+
yield {
|
|
300
|
+
type: "result",
|
|
301
|
+
data: result,
|
|
302
|
+
resultForAssistant: renderResultForAssistant(result)
|
|
303
|
+
};
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (isInterrupted(error, abortController, interrupted)) {
|
|
306
|
+
yield interruptResult(expertModel);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
logError(error);
|
|
310
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
311
|
+
const result = {
|
|
312
|
+
chatSessionId: sessionId || "error-session",
|
|
313
|
+
expertModelName: expertModel,
|
|
314
|
+
expertAnswer: `\u274C ${errorMessage || "Expert consultation failed with unknown error"}`
|
|
315
|
+
};
|
|
316
|
+
yield {
|
|
317
|
+
type: "result",
|
|
318
|
+
data: result,
|
|
319
|
+
resultForAssistant: renderResultForAssistant(result)
|
|
320
|
+
};
|
|
321
|
+
} finally {
|
|
322
|
+
abortController.signal.removeEventListener("abort", abortListener);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// packages/tools/src/tools/ai/AskExpertModelTool/prompt.ts
|
|
327
|
+
var DESCRIPTION = "Consult an external AI model for a second opinion or specialized analysis.";
|
|
328
|
+
var PROMPT = `Ask a question to a specific external AI model for expert analysis.
|
|
329
|
+
|
|
330
|
+
CRITICAL: The expert model receives ONLY your \`question\` (plus the prior messages in the same \`chat_session_id\`).
|
|
331
|
+
It does NOT have access to the user\u2019s current repository context unless you include it in the question.
|
|
332
|
+
|
|
333
|
+
The \`question\` MUST be self-contained:
|
|
334
|
+
1) Background / context
|
|
335
|
+
2) Current situation / constraints
|
|
336
|
+
3) A clear, independent question
|
|
337
|
+
|
|
338
|
+
Use this tool when you want a different model\u2019s perspective, not for task execution.`;
|
|
339
|
+
|
|
340
|
+
// packages/tools/src/tools/ai/AskExpertModelTool/AskExpertModelTool.tsx
|
|
341
|
+
var inputSchema = z.strictObject({
|
|
342
|
+
question: z.string().describe(
|
|
343
|
+
"A fully self-contained question (include all background context, constraints, and a clear ask)."
|
|
344
|
+
),
|
|
345
|
+
expert_model: z.string().describe(
|
|
346
|
+
"The expert model to use (e.g., gpt-5, claude-3-5-sonnet-20241022)"
|
|
347
|
+
),
|
|
348
|
+
chat_session_id: z.string().describe('Use "new" for a new session, or an existing session ID.')
|
|
349
|
+
});
|
|
350
|
+
function normalizeModelName(modelName) {
|
|
351
|
+
return modelName.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
352
|
+
}
|
|
353
|
+
function getCurrentModelName(context) {
|
|
354
|
+
if (typeof context?.options?.model === "string") return context.options.model;
|
|
355
|
+
const modelName = getModelManager().getModelName("main");
|
|
356
|
+
return modelName ?? "";
|
|
357
|
+
}
|
|
358
|
+
async function validateInput(input, context) {
|
|
359
|
+
const question = input.question.trim();
|
|
360
|
+
const expertModel = input.expert_model.trim();
|
|
361
|
+
const sessionId = input.chat_session_id.trim();
|
|
362
|
+
if (!question) return { result: false, message: "Question cannot be empty" };
|
|
363
|
+
if (!expertModel)
|
|
364
|
+
return { result: false, message: "Expert model must be specified" };
|
|
365
|
+
if (!sessionId) {
|
|
366
|
+
return {
|
|
367
|
+
result: false,
|
|
368
|
+
message: 'Chat session ID must be specified (use "new" for new session)'
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const currentModel = getCurrentModelName(context);
|
|
372
|
+
if (currentModel && normalizeModelName(currentModel) === normalizeModelName(expertModel)) {
|
|
373
|
+
return {
|
|
374
|
+
result: false,
|
|
375
|
+
message: `You are already running as ${currentModel}. Please choose a different model to consult.`
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
const modelManager = getModelManager();
|
|
379
|
+
const resolved = modelManager.resolveModelWithInfo(expertModel);
|
|
380
|
+
if (!resolved.success) {
|
|
381
|
+
const available = modelManager.getAllAvailableModelNames();
|
|
382
|
+
return {
|
|
383
|
+
result: false,
|
|
384
|
+
message: available.length > 0 ? `Model '${expertModel}' is not configured. Available models: ${available.join(", ")}. Configure it via /model.` : `Model '${expertModel}' is not configured and no models are currently available. Configure a model via /model first.`
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return { result: true };
|
|
388
|
+
}
|
|
389
|
+
var AskExpertModelTool = {
|
|
390
|
+
name: "AskExpertModel",
|
|
391
|
+
async description() {
|
|
392
|
+
return DESCRIPTION;
|
|
393
|
+
},
|
|
394
|
+
async prompt() {
|
|
395
|
+
return PROMPT;
|
|
396
|
+
},
|
|
397
|
+
inputSchema,
|
|
398
|
+
userFacingName() {
|
|
399
|
+
return "AskExpertModel";
|
|
400
|
+
},
|
|
401
|
+
async isEnabled() {
|
|
402
|
+
return true;
|
|
403
|
+
},
|
|
404
|
+
isReadOnly() {
|
|
405
|
+
return true;
|
|
406
|
+
},
|
|
407
|
+
isConcurrencySafe() {
|
|
408
|
+
return true;
|
|
409
|
+
},
|
|
410
|
+
needsPermissions() {
|
|
411
|
+
return false;
|
|
412
|
+
},
|
|
413
|
+
validateInput,
|
|
414
|
+
renderToolUseMessage({ question, expert_model, chat_session_id }, { verbose }) {
|
|
415
|
+
if (!expert_model) return null;
|
|
416
|
+
const isNewSession = chat_session_id === "new";
|
|
417
|
+
const sessionLabel = isNewSession ? "new session" : `session ${chat_session_id.slice(0, 8)}\u2026`;
|
|
418
|
+
const theme = getTheme();
|
|
419
|
+
if (!verbose) {
|
|
420
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "yellow" }, expert_model, " "), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText, dimColor: true }, "(", sessionLabel, ")"));
|
|
421
|
+
}
|
|
422
|
+
const preview = question.length > 300 ? `${question.slice(0, 300)}\u2026` : question;
|
|
423
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "yellow" }, expert_model), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, sessionLabel), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.text }, preview)));
|
|
424
|
+
},
|
|
425
|
+
renderToolResultMessage(output, { verbose }) {
|
|
426
|
+
const theme = getTheme();
|
|
427
|
+
const answer = (output.expertAnswer ?? "").trim();
|
|
428
|
+
const shown = verbose ? answer : answer.length > 800 ? `${answer.slice(0, 800)}\u2026` : answer;
|
|
429
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.text }, "Response from ", output.expertModelName, ":"), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.text }, applyMarkdown(shown))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText, dimColor: true }, "Session: ", output.chatSessionId.slice(0, 8))));
|
|
430
|
+
},
|
|
431
|
+
renderToolUseRejectedMessage() {
|
|
432
|
+
const theme = getTheme();
|
|
433
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText, dimColor: true }, "Expert consultation cancelled"));
|
|
434
|
+
},
|
|
435
|
+
renderResultForAssistant(output) {
|
|
436
|
+
return `[Expert consultation completed]
|
|
437
|
+
Expert Model: ${output.expertModelName}
|
|
438
|
+
Session ID: ${output.chatSessionId}
|
|
439
|
+
To continue this conversation, reuse this Session ID in the next AskExpertModel call.
|
|
440
|
+
|
|
441
|
+
${output.expertAnswer}`;
|
|
442
|
+
},
|
|
443
|
+
async *call(input, { abortController, readFileTimestamps }) {
|
|
444
|
+
const normalizedInput = {
|
|
445
|
+
question: String(input.question ?? ""),
|
|
446
|
+
expert_model: String(input.expert_model ?? ""),
|
|
447
|
+
chat_session_id: String(input.chat_session_id ?? "")
|
|
448
|
+
};
|
|
449
|
+
yield* callAskExpertModelTool(
|
|
450
|
+
normalizedInput,
|
|
451
|
+
{ abortController, readFileTimestamps },
|
|
452
|
+
(output) => this.renderResultForAssistant(output)
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// packages/tools/src/tools/system/TaskOutputTool/TaskOutputTool.tsx
|
|
458
|
+
import { z as z2 } from "zod";
|
|
459
|
+
|
|
460
|
+
// packages/core/src/utils/backgroundTasks.ts
|
|
461
|
+
var backgroundTasks = /* @__PURE__ */ new Map();
|
|
462
|
+
function getBackgroundAgentTaskSnapshot(agentId) {
|
|
463
|
+
const task = backgroundTasks.get(agentId);
|
|
464
|
+
if (!task) return void 0;
|
|
465
|
+
const { abortController: _abortController, done: _done, ...snapshot } = task;
|
|
466
|
+
return snapshot;
|
|
467
|
+
}
|
|
468
|
+
function upsertBackgroundAgentTask(task) {
|
|
469
|
+
backgroundTasks.set(task.agentId, task);
|
|
470
|
+
}
|
|
471
|
+
async function waitForBackgroundAgentTask(agentId, waitUpToMs, signal) {
|
|
472
|
+
const task = backgroundTasks.get(agentId);
|
|
473
|
+
if (!task) return void 0;
|
|
474
|
+
if (task.status !== "running") return task;
|
|
475
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
476
|
+
const timeoutId = setTimeout(() => {
|
|
477
|
+
reject(new Error("Request timed out"));
|
|
478
|
+
}, waitUpToMs);
|
|
479
|
+
timeoutId.unref?.();
|
|
480
|
+
});
|
|
481
|
+
const abortPromise = new Promise((_, reject) => {
|
|
482
|
+
if (signal.aborted) {
|
|
483
|
+
reject(new Error("Request aborted"));
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const onAbort = () => reject(new Error("Request aborted"));
|
|
487
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
488
|
+
});
|
|
489
|
+
await Promise.race([task.done, timeoutPromise, abortPromise]);
|
|
490
|
+
return backgroundTasks.get(agentId);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// packages/tools/src/tools/system/TaskOutputTool/prompt.ts
|
|
494
|
+
var TOOL_NAME_FOR_PROMPT = "TaskOutput";
|
|
495
|
+
var DESCRIPTION2 = "Retrieves output from a running or completed task";
|
|
496
|
+
var PROMPT2 = `- Retrieves output from a running or completed task (background shell, agent, or remote session)
|
|
497
|
+
- Takes a task_id parameter identifying the task
|
|
498
|
+
- Returns the task output along with status information
|
|
499
|
+
- Use block=true (default) to wait for task completion
|
|
500
|
+
- Use block=false for non-blocking check of current status
|
|
501
|
+
- Task IDs can be found using the /tasks command
|
|
502
|
+
- Works with all task types: background shells, async agents, and remote sessions`;
|
|
503
|
+
|
|
504
|
+
// packages/tools/src/tools/system/TaskOutputTool/TaskOutputTool.tsx
|
|
505
|
+
var inputSchema2 = z2.strictObject({
|
|
506
|
+
task_id: z2.string().describe("The task ID to get output from"),
|
|
507
|
+
block: z2.boolean().optional().default(true).describe("Whether to wait for completion"),
|
|
508
|
+
timeout: z2.number().min(0).max(6e5).optional().default(3e4).describe("Max wait time in ms")
|
|
509
|
+
});
|
|
510
|
+
function normalizeTaskOutputInput(input) {
|
|
511
|
+
const task_id = typeof input.task_id === "string" && input.task_id || typeof input.agentId === "string" && input.agentId || typeof input.bash_id === "string" && input.bash_id || "";
|
|
512
|
+
const block = typeof input.block === "boolean" ? input.block : true;
|
|
513
|
+
const timeout = typeof input.timeout === "number" ? input.timeout : typeof input.wait_up_to === "number" ? input.wait_up_to * 1e3 : 3e4;
|
|
514
|
+
return { task_id, block, timeout };
|
|
515
|
+
}
|
|
516
|
+
function taskStatusFromBash(bg) {
|
|
517
|
+
if (!bg) return "failed";
|
|
518
|
+
if (bg.killed) return "killed";
|
|
519
|
+
if (bg.code === null) return "running";
|
|
520
|
+
return bg.code === 0 ? "completed" : "failed";
|
|
521
|
+
}
|
|
522
|
+
function buildTaskSummary(taskId) {
|
|
523
|
+
const bg = BunShell.getInstance().getBackgroundOutput(taskId);
|
|
524
|
+
if (bg) {
|
|
525
|
+
return {
|
|
526
|
+
task_id: taskId,
|
|
527
|
+
task_type: "local_bash",
|
|
528
|
+
status: taskStatusFromBash(bg),
|
|
529
|
+
description: bg.command,
|
|
530
|
+
output: readTaskOutput(taskId),
|
|
531
|
+
exitCode: bg.code
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
const agent = getBackgroundAgentTaskSnapshot(taskId);
|
|
535
|
+
if (agent) {
|
|
536
|
+
const output = readTaskOutput(taskId) || agent.resultText || "";
|
|
537
|
+
return {
|
|
538
|
+
task_id: taskId,
|
|
539
|
+
task_type: "local_agent",
|
|
540
|
+
status: agent.status,
|
|
541
|
+
description: agent.description,
|
|
542
|
+
output,
|
|
543
|
+
prompt: agent.prompt,
|
|
544
|
+
result: output,
|
|
545
|
+
error: agent.error
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
async function waitForBashTaskCompletion(args) {
|
|
551
|
+
const { taskId, timeoutMs, signal } = args;
|
|
552
|
+
const startedAt = Date.now();
|
|
553
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
554
|
+
if (signal.aborted) return null;
|
|
555
|
+
const summary = buildTaskSummary(taskId);
|
|
556
|
+
if (!summary) return null;
|
|
557
|
+
if (summary.status !== "running" && summary.status !== "pending")
|
|
558
|
+
return summary;
|
|
559
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
560
|
+
}
|
|
561
|
+
return buildTaskSummary(taskId);
|
|
562
|
+
}
|
|
563
|
+
var TaskOutputTool = {
|
|
564
|
+
name: TOOL_NAME_FOR_PROMPT,
|
|
565
|
+
async description() {
|
|
566
|
+
return DESCRIPTION2;
|
|
567
|
+
},
|
|
568
|
+
userFacingName() {
|
|
569
|
+
return "Task Output";
|
|
570
|
+
},
|
|
571
|
+
inputSchema: inputSchema2,
|
|
572
|
+
isReadOnly() {
|
|
573
|
+
return true;
|
|
574
|
+
},
|
|
575
|
+
isConcurrencySafe() {
|
|
576
|
+
return true;
|
|
577
|
+
},
|
|
578
|
+
async isEnabled() {
|
|
579
|
+
return true;
|
|
580
|
+
},
|
|
581
|
+
needsPermissions() {
|
|
582
|
+
return false;
|
|
583
|
+
},
|
|
584
|
+
async prompt() {
|
|
585
|
+
return PROMPT2;
|
|
586
|
+
},
|
|
587
|
+
renderToolUseMessage(input) {
|
|
588
|
+
const normalized = normalizeTaskOutputInput(input);
|
|
589
|
+
if (normalized.block === false) return "non-blocking";
|
|
590
|
+
return "";
|
|
591
|
+
},
|
|
592
|
+
renderToolUseRejectedMessage() {
|
|
593
|
+
return null;
|
|
594
|
+
},
|
|
595
|
+
renderResultForAssistant(output) {
|
|
596
|
+
const parts = [];
|
|
597
|
+
parts.push(
|
|
598
|
+
`<retrieval_status>${output.retrieval_status}</retrieval_status>`
|
|
599
|
+
);
|
|
600
|
+
if (output.task) {
|
|
601
|
+
parts.push(`<task_id>${output.task.task_id}</task_id>`);
|
|
602
|
+
parts.push(`<task_type>${output.task.task_type}</task_type>`);
|
|
603
|
+
parts.push(`<status>${output.task.status}</status>`);
|
|
604
|
+
if (output.task.exitCode !== void 0 && output.task.exitCode !== null) {
|
|
605
|
+
parts.push(`<exit_code>${output.task.exitCode}</exit_code>`);
|
|
606
|
+
}
|
|
607
|
+
if (output.task.output?.trim()) {
|
|
608
|
+
parts.push(`<output>
|
|
609
|
+
${output.task.output.trimEnd()}
|
|
610
|
+
</output>`);
|
|
611
|
+
}
|
|
612
|
+
if (output.task.error) {
|
|
613
|
+
parts.push(`<error>${output.task.error}</error>`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return parts.join("\n\n");
|
|
617
|
+
},
|
|
618
|
+
async validateInput(input) {
|
|
619
|
+
if (!input.task_id) {
|
|
620
|
+
return { result: false, message: "Task ID is required", errorCode: 1 };
|
|
621
|
+
}
|
|
622
|
+
const task = buildTaskSummary(input.task_id);
|
|
623
|
+
if (!task) {
|
|
624
|
+
return {
|
|
625
|
+
result: false,
|
|
626
|
+
message: `No task found with ID: ${input.task_id}`,
|
|
627
|
+
errorCode: 2
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
return { result: true };
|
|
631
|
+
},
|
|
632
|
+
async *call(input, context) {
|
|
633
|
+
const normalized = normalizeTaskOutputInput(input);
|
|
634
|
+
const taskId = normalized.task_id;
|
|
635
|
+
const block = normalized.block;
|
|
636
|
+
const timeoutMs = normalized.timeout;
|
|
637
|
+
const initial = buildTaskSummary(taskId);
|
|
638
|
+
if (!initial) {
|
|
639
|
+
throw new Error(`No task found with ID: ${taskId}`);
|
|
640
|
+
}
|
|
641
|
+
if (!block) {
|
|
642
|
+
const isDone = initial.status !== "running" && initial.status !== "pending";
|
|
643
|
+
const out2 = {
|
|
644
|
+
retrieval_status: isDone ? "success" : "not_ready",
|
|
645
|
+
task: initial
|
|
646
|
+
};
|
|
647
|
+
yield {
|
|
648
|
+
type: "result",
|
|
649
|
+
data: out2,
|
|
650
|
+
resultForAssistant: this.renderResultForAssistant(out2)
|
|
651
|
+
};
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
yield {
|
|
655
|
+
type: "progress",
|
|
656
|
+
content: createAssistantMessage(
|
|
657
|
+
`<tool-progress>${initial.description ? ` ${initial.description}
|
|
658
|
+
` : ""} Waiting for task (esc to give additional instructions)</tool-progress>`
|
|
659
|
+
)
|
|
660
|
+
};
|
|
661
|
+
let finalTask = null;
|
|
662
|
+
if (initial.task_type === "local_agent") {
|
|
663
|
+
try {
|
|
664
|
+
const task = await waitForBackgroundAgentTask(
|
|
665
|
+
taskId,
|
|
666
|
+
timeoutMs,
|
|
667
|
+
context.abortController.signal
|
|
668
|
+
);
|
|
669
|
+
finalTask = task ? buildTaskSummary(taskId) : null;
|
|
670
|
+
} catch {
|
|
671
|
+
finalTask = buildTaskSummary(taskId);
|
|
672
|
+
}
|
|
673
|
+
} else {
|
|
674
|
+
finalTask = await waitForBashTaskCompletion({
|
|
675
|
+
taskId,
|
|
676
|
+
timeoutMs,
|
|
677
|
+
signal: context.abortController.signal
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
if (!finalTask) {
|
|
681
|
+
const out2 = { retrieval_status: "timeout", task: null };
|
|
682
|
+
yield {
|
|
683
|
+
type: "result",
|
|
684
|
+
data: out2,
|
|
685
|
+
resultForAssistant: this.renderResultForAssistant(out2)
|
|
686
|
+
};
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (finalTask.status === "running" || finalTask.status === "pending") {
|
|
690
|
+
const out2 = { retrieval_status: "timeout", task: finalTask };
|
|
691
|
+
yield {
|
|
692
|
+
type: "result",
|
|
693
|
+
data: out2,
|
|
694
|
+
resultForAssistant: this.renderResultForAssistant(out2)
|
|
695
|
+
};
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
const out = { retrieval_status: "success", task: finalTask };
|
|
699
|
+
yield {
|
|
700
|
+
type: "result",
|
|
701
|
+
data: out,
|
|
702
|
+
resultForAssistant: this.renderResultForAssistant(out)
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
// packages/tools/src/tools/system/KillShellTool/KillShellTool.tsx
|
|
708
|
+
import { z as z3 } from "zod";
|
|
709
|
+
|
|
710
|
+
// packages/tools/src/tools/system/KillShellTool/prompt.ts
|
|
711
|
+
var TOOL_NAME_FOR_PROMPT2 = "KillShell";
|
|
712
|
+
var DESCRIPTION3 = "Kill a background bash shell by ID";
|
|
713
|
+
var PROMPT3 = `
|
|
714
|
+
- Kills a running background bash shell by its ID
|
|
715
|
+
- Takes a shell_id parameter identifying the shell to kill
|
|
716
|
+
- Returns a success or failure status
|
|
717
|
+
- Use this tool when you need to terminate a long-running shell
|
|
718
|
+
- Shell IDs can be found using the /tasks command
|
|
719
|
+
`;
|
|
720
|
+
|
|
721
|
+
// packages/tools/src/tools/system/KillShellTool/KillShellTool.tsx
|
|
722
|
+
var inputSchema3 = z3.strictObject({
|
|
723
|
+
shell_id: z3.string().describe("The ID of the background shell to kill")
|
|
724
|
+
});
|
|
725
|
+
var KillShellTool = {
|
|
726
|
+
name: TOOL_NAME_FOR_PROMPT2,
|
|
727
|
+
async description() {
|
|
728
|
+
return DESCRIPTION3;
|
|
729
|
+
},
|
|
730
|
+
userFacingName() {
|
|
731
|
+
return "Kill Shell";
|
|
732
|
+
},
|
|
733
|
+
inputSchema: inputSchema3,
|
|
734
|
+
isReadOnly() {
|
|
735
|
+
return false;
|
|
736
|
+
},
|
|
737
|
+
isConcurrencySafe() {
|
|
738
|
+
return true;
|
|
739
|
+
},
|
|
740
|
+
async isEnabled() {
|
|
741
|
+
return true;
|
|
742
|
+
},
|
|
743
|
+
needsPermissions() {
|
|
744
|
+
return false;
|
|
745
|
+
},
|
|
746
|
+
async prompt() {
|
|
747
|
+
return PROMPT3;
|
|
748
|
+
},
|
|
749
|
+
renderToolUseMessage({ shell_id }) {
|
|
750
|
+
return `Kill shell: ${shell_id}`;
|
|
751
|
+
},
|
|
752
|
+
renderResultForAssistant(output) {
|
|
753
|
+
return JSON.stringify(output);
|
|
754
|
+
},
|
|
755
|
+
async validateInput({ shell_id }) {
|
|
756
|
+
const bg = BunShell.getInstance().getBackgroundOutput(shell_id);
|
|
757
|
+
if (!bg) {
|
|
758
|
+
return {
|
|
759
|
+
result: false,
|
|
760
|
+
message: `No shell found with ID: ${shell_id}`,
|
|
761
|
+
errorCode: 1
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
return { result: true };
|
|
765
|
+
},
|
|
766
|
+
async *call({ shell_id }) {
|
|
767
|
+
const bg = BunShell.getInstance().getBackgroundOutput(shell_id);
|
|
768
|
+
if (!bg) {
|
|
769
|
+
throw new Error(`No shell found with ID: ${shell_id}`);
|
|
770
|
+
}
|
|
771
|
+
const status = bg.killed ? "killed" : bg.code === null ? "running" : bg.code === 0 ? "completed" : "failed";
|
|
772
|
+
if (status !== "running") {
|
|
773
|
+
throw new Error(
|
|
774
|
+
`Shell ${shell_id} is not running, so cannot be killed (status: ${status})`
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
const killed = BunShell.getInstance().killBackgroundShell(shell_id);
|
|
778
|
+
const output = {
|
|
779
|
+
message: killed ? `Successfully killed shell: ${shell_id} (${bg.command})` : `No shell found with ID: ${shell_id}`,
|
|
780
|
+
shell_id
|
|
781
|
+
};
|
|
782
|
+
yield {
|
|
783
|
+
type: "result",
|
|
784
|
+
data: output,
|
|
785
|
+
resultForAssistant: this.renderResultForAssistant(output)
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
// packages/tools/src/tools/mcp/ListMcpResourcesTool/ListMcpResourcesTool.tsx
|
|
791
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
792
|
+
import React2 from "react";
|
|
793
|
+
import { z as z4 } from "zod";
|
|
794
|
+
import { ListResourcesResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
795
|
+
|
|
796
|
+
// packages/tools/src/tools/mcp/ListMcpResourcesTool/prompt.ts
|
|
797
|
+
var TOOL_NAME = "ListMcpResourcesTool";
|
|
798
|
+
var DESCRIPTION4 = `Lists available resources from configured MCP servers.
|
|
799
|
+
Each resource object includes a 'server' field indicating which server it's from.
|
|
800
|
+
|
|
801
|
+
Usage examples:
|
|
802
|
+
- List all resources from all servers: \`listMcpResources\`
|
|
803
|
+
- List resources from a specific server: \`listMcpResources({ server: "myserver" })\``;
|
|
804
|
+
var PROMPT4 = `List available resources from configured MCP servers.
|
|
805
|
+
Each returned resource will include all standard MCP resource fields plus a 'server' field
|
|
806
|
+
indicating which server the resource belongs to.
|
|
807
|
+
|
|
808
|
+
Parameters:
|
|
809
|
+
- server (optional): The name of a specific MCP server to get resources from. If not provided,
|
|
810
|
+
resources from all servers will be returned.`;
|
|
811
|
+
|
|
812
|
+
// packages/tools/src/tools/mcp/ListMcpResourcesTool/ListMcpResourcesTool.tsx
|
|
813
|
+
var inputSchema4 = z4.strictObject({
|
|
814
|
+
server: z4.string().optional().describe("Optional server name to filter resources by")
|
|
815
|
+
});
|
|
816
|
+
function isWrappedClient(value) {
|
|
817
|
+
if (!value || typeof value !== "object") return false;
|
|
818
|
+
const record = value;
|
|
819
|
+
if (typeof record.name !== "string") return false;
|
|
820
|
+
if (record.type !== "connected" && record.type !== "failed") return false;
|
|
821
|
+
if (record.type === "connected") {
|
|
822
|
+
return typeof record.client === "object" && record.client !== null;
|
|
823
|
+
}
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
826
|
+
async function getMcpClients(context) {
|
|
827
|
+
const override = context?.options?.mcpClients;
|
|
828
|
+
if (Array.isArray(override) && override.every(isWrappedClient)) {
|
|
829
|
+
return override;
|
|
830
|
+
}
|
|
831
|
+
return await getClients();
|
|
832
|
+
}
|
|
833
|
+
var ListMcpResourcesTool = {
|
|
834
|
+
name: TOOL_NAME,
|
|
835
|
+
async description() {
|
|
836
|
+
return DESCRIPTION4;
|
|
837
|
+
},
|
|
838
|
+
async prompt() {
|
|
839
|
+
return PROMPT4;
|
|
840
|
+
},
|
|
841
|
+
inputSchema: inputSchema4,
|
|
842
|
+
userFacingName() {
|
|
843
|
+
return "listMcpResources";
|
|
844
|
+
},
|
|
845
|
+
async isEnabled() {
|
|
846
|
+
return true;
|
|
847
|
+
},
|
|
848
|
+
isReadOnly() {
|
|
849
|
+
return true;
|
|
850
|
+
},
|
|
851
|
+
isConcurrencySafe() {
|
|
852
|
+
return true;
|
|
853
|
+
},
|
|
854
|
+
needsPermissions() {
|
|
855
|
+
return false;
|
|
856
|
+
},
|
|
857
|
+
async validateInput({ server }, context) {
|
|
858
|
+
if (!server) return { result: true };
|
|
859
|
+
const clients = await getMcpClients(context);
|
|
860
|
+
const found = clients.some((c) => c.name === server);
|
|
861
|
+
if (!found) {
|
|
862
|
+
return {
|
|
863
|
+
result: false,
|
|
864
|
+
message: `Server "${server}" not found. Available servers: ${clients.map((c) => c.name).join(", ")}`,
|
|
865
|
+
errorCode: 1
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
return { result: true };
|
|
869
|
+
},
|
|
870
|
+
renderToolUseMessage({ server }) {
|
|
871
|
+
return server ? `List MCP resources from server "${server}"` : "List all MCP resources";
|
|
872
|
+
},
|
|
873
|
+
renderToolResultMessage(output) {
|
|
874
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "row" }, /* @__PURE__ */ React2.createElement(Text2, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React2.createElement(Text2, { bold: true }, output.length), /* @__PURE__ */ React2.createElement(Text2, null, " resources"));
|
|
875
|
+
},
|
|
876
|
+
renderResultForAssistant(output) {
|
|
877
|
+
return JSON.stringify(output);
|
|
878
|
+
},
|
|
879
|
+
async *call({ server }, context) {
|
|
880
|
+
const clients = await getMcpClients(context);
|
|
881
|
+
const selected = server ? clients.filter((c) => c.name === server) : clients;
|
|
882
|
+
if (server && selected.length === 0) {
|
|
883
|
+
throw new Error(
|
|
884
|
+
`Server "${server}" not found. Available servers: ${clients.map((c) => c.name).join(", ")}`
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
const resources = [];
|
|
888
|
+
for (const wrapped of selected) {
|
|
889
|
+
if (wrapped.type !== "connected") continue;
|
|
890
|
+
try {
|
|
891
|
+
let capabilities = wrapped.capabilities ?? null;
|
|
892
|
+
if (!capabilities) {
|
|
893
|
+
try {
|
|
894
|
+
capabilities = wrapped.client.getServerCapabilities() ?? null;
|
|
895
|
+
} catch {
|
|
896
|
+
capabilities = null;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (!capabilities?.resources) continue;
|
|
900
|
+
const result = await wrapped.client.request(
|
|
901
|
+
{ method: "resources/list" },
|
|
902
|
+
ListResourcesResultSchema
|
|
903
|
+
);
|
|
904
|
+
if (!result.resources) continue;
|
|
905
|
+
resources.push(
|
|
906
|
+
...result.resources.map((r) => ({
|
|
907
|
+
...r,
|
|
908
|
+
server: wrapped.name
|
|
909
|
+
}))
|
|
910
|
+
);
|
|
911
|
+
} catch {
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
yield {
|
|
915
|
+
type: "result",
|
|
916
|
+
data: resources,
|
|
917
|
+
resultForAssistant: this.renderResultForAssistant(resources)
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// packages/tools/src/tools/system/LspTool/LspTool.tsx
|
|
923
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
924
|
+
import { Box as Box3, Text as Text4 } from "ink";
|
|
925
|
+
import React4 from "react";
|
|
926
|
+
import { z as z5 } from "zod";
|
|
927
|
+
|
|
928
|
+
// packages/tools/src/tools/system/LspTool/constants.ts
|
|
929
|
+
var OPERATIONS = [
|
|
930
|
+
"goToDefinition",
|
|
931
|
+
"findReferences",
|
|
932
|
+
"hover",
|
|
933
|
+
"documentSymbol",
|
|
934
|
+
"workspaceSymbol",
|
|
935
|
+
"goToImplementation",
|
|
936
|
+
"prepareCallHierarchy",
|
|
937
|
+
"incomingCalls",
|
|
938
|
+
"outgoingCalls"
|
|
939
|
+
];
|
|
940
|
+
var OPERATION_LABELS = {
|
|
941
|
+
goToDefinition: { singular: "definition", plural: "definitions" },
|
|
942
|
+
findReferences: { singular: "reference", plural: "references" },
|
|
943
|
+
documentSymbol: { singular: "symbol", plural: "symbols" },
|
|
944
|
+
workspaceSymbol: { singular: "symbol", plural: "symbols" },
|
|
945
|
+
hover: { singular: "hover info", plural: "hover info", special: "available" },
|
|
946
|
+
goToImplementation: { singular: "implementation", plural: "implementations" },
|
|
947
|
+
prepareCallHierarchy: { singular: "call item", plural: "call items" },
|
|
948
|
+
incomingCalls: { singular: "caller", plural: "callers" },
|
|
949
|
+
outgoingCalls: { singular: "callee", plural: "callees" }
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
// packages/tools/src/tools/system/LspTool/format.ts
|
|
953
|
+
import { relative } from "path";
|
|
954
|
+
function extractSymbolAtPosition(lines, zeroBasedLine, zeroBasedCharacter) {
|
|
955
|
+
try {
|
|
956
|
+
if (zeroBasedLine < 0 || zeroBasedLine >= lines.length) return null;
|
|
957
|
+
const line = lines[zeroBasedLine];
|
|
958
|
+
if (zeroBasedCharacter < 0 || zeroBasedCharacter >= line.length) return null;
|
|
959
|
+
const tokenRe = /[\w$'!]+|[+\-*/%&|^~<>=]+/g;
|
|
960
|
+
let match;
|
|
961
|
+
while ((match = tokenRe.exec(line)) !== null) {
|
|
962
|
+
const start = match.index;
|
|
963
|
+
const end = start + match[0].length;
|
|
964
|
+
if (zeroBasedCharacter >= start && zeroBasedCharacter < end) {
|
|
965
|
+
const token = match[0];
|
|
966
|
+
return token.length > 30 ? `${token.slice(0, 27)}...` : token;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return null;
|
|
970
|
+
} catch {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
function toProjectRelativeIfPossible(filePath) {
|
|
975
|
+
const cwd = getCwd();
|
|
976
|
+
try {
|
|
977
|
+
const rel = relative(cwd, filePath);
|
|
978
|
+
if (!rel || rel === "") return filePath;
|
|
979
|
+
if (rel.startsWith("..")) return filePath;
|
|
980
|
+
return rel;
|
|
981
|
+
} catch {
|
|
982
|
+
return filePath;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
function formatLocation(fileName, line0, character0) {
|
|
986
|
+
return `${toProjectRelativeIfPossible(fileName)}:${line0 + 1}:${character0 + 1}`;
|
|
987
|
+
}
|
|
988
|
+
function formatGoToDefinitionResult(locations) {
|
|
989
|
+
if (!locations || locations.length === 0) {
|
|
990
|
+
return {
|
|
991
|
+
formatted: "No definition found. This may occur if the cursor is not on a symbol, or if the definition is in an external library not indexed by the LSP server.",
|
|
992
|
+
resultCount: 0,
|
|
993
|
+
fileCount: 0
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
const fileCount = new Set(locations.map((l) => l.fileName)).size;
|
|
997
|
+
if (locations.length === 1) {
|
|
998
|
+
const loc = locations[0];
|
|
999
|
+
return {
|
|
1000
|
+
formatted: `Defined in ${formatLocation(loc.fileName, loc.line0, loc.character0)}`,
|
|
1001
|
+
resultCount: 1,
|
|
1002
|
+
fileCount
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
return {
|
|
1006
|
+
formatted: `Found ${locations.length} definitions:
|
|
1007
|
+
${locations.map(
|
|
1008
|
+
(loc) => ` ${formatLocation(loc.fileName, loc.line0, loc.character0)}`
|
|
1009
|
+
).join("\n")}`,
|
|
1010
|
+
resultCount: locations.length,
|
|
1011
|
+
fileCount
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
function groupLocationsByFile(items) {
|
|
1015
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1016
|
+
for (const item of items) {
|
|
1017
|
+
const key = toProjectRelativeIfPossible(item.fileName);
|
|
1018
|
+
const existing = grouped.get(key);
|
|
1019
|
+
if (existing) existing.push(item);
|
|
1020
|
+
else grouped.set(key, [item]);
|
|
1021
|
+
}
|
|
1022
|
+
return grouped;
|
|
1023
|
+
}
|
|
1024
|
+
function formatFindReferencesResult(references) {
|
|
1025
|
+
if (!references || references.length === 0) {
|
|
1026
|
+
return {
|
|
1027
|
+
formatted: "No references found. This may occur if the symbol has no usages, or if the LSP server has not fully indexed the workspace.",
|
|
1028
|
+
resultCount: 0,
|
|
1029
|
+
fileCount: 0
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
if (references.length === 1) {
|
|
1033
|
+
const ref = references[0];
|
|
1034
|
+
return {
|
|
1035
|
+
formatted: `Found 1 reference:
|
|
1036
|
+
${formatLocation(ref.fileName, ref.line0, ref.character0)}`,
|
|
1037
|
+
resultCount: 1,
|
|
1038
|
+
fileCount: 1
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
const grouped = groupLocationsByFile(references);
|
|
1042
|
+
const lines = [
|
|
1043
|
+
`Found ${references.length} references across ${grouped.size} files:`
|
|
1044
|
+
];
|
|
1045
|
+
for (const [file, refs] of grouped) {
|
|
1046
|
+
lines.push(`
|
|
1047
|
+
${file}:`);
|
|
1048
|
+
for (const ref of refs) {
|
|
1049
|
+
lines.push(` Line ${ref.line0 + 1}:${ref.character0 + 1}`);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return {
|
|
1053
|
+
formatted: lines.join("\n"),
|
|
1054
|
+
resultCount: references.length,
|
|
1055
|
+
fileCount: grouped.size
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
function formatHoverResult(hoverText, line0, character0) {
|
|
1059
|
+
if (!hoverText || hoverText.trim() === "") {
|
|
1060
|
+
return {
|
|
1061
|
+
formatted: "No hover information available. This may occur if the cursor is not on a symbol, or if the LSP server has not fully indexed the file.",
|
|
1062
|
+
resultCount: 0,
|
|
1063
|
+
fileCount: 0
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
return {
|
|
1067
|
+
formatted: `Hover info at ${line0 + 1}:${character0 + 1}:
|
|
1068
|
+
|
|
1069
|
+
${hoverText}`,
|
|
1070
|
+
resultCount: 1,
|
|
1071
|
+
fileCount: 1
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
function formatDocumentSymbolsResult(lines, symbolCount) {
|
|
1075
|
+
if (symbolCount === 0) {
|
|
1076
|
+
return {
|
|
1077
|
+
formatted: "No symbols found in document. This may occur if the file is empty, not supported by the LSP server, or if the server has not fully indexed the file.",
|
|
1078
|
+
resultCount: 0,
|
|
1079
|
+
fileCount: 0
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
return {
|
|
1083
|
+
formatted: ["Document symbols:", ...lines].join("\n"),
|
|
1084
|
+
resultCount: symbolCount,
|
|
1085
|
+
fileCount: 1
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// packages/tools/src/tools/system/LspTool/summary.tsx
|
|
1090
|
+
import { Text as Text3 } from "ink";
|
|
1091
|
+
import React3 from "react";
|
|
1092
|
+
function summarizeToolResult(operation, resultCount, fileCount) {
|
|
1093
|
+
const label = OPERATION_LABELS[operation] ?? {
|
|
1094
|
+
singular: "result",
|
|
1095
|
+
plural: "results"
|
|
1096
|
+
};
|
|
1097
|
+
const noun = resultCount === 1 ? label.singular : label.plural;
|
|
1098
|
+
if (operation === "hover" && resultCount > 0 && label.special) {
|
|
1099
|
+
return /* @__PURE__ */ React3.createElement(Text3, null, "Hover info ", label.special);
|
|
1100
|
+
}
|
|
1101
|
+
return /* @__PURE__ */ React3.createElement(Text3, null, "Found ", /* @__PURE__ */ React3.createElement(Text3, { bold: true }, resultCount), " ", noun, fileCount > 1 ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, " ", "across ", /* @__PURE__ */ React3.createElement(Text3, { bold: true }, fileCount), " files") : null);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// packages/tools/src/tools/system/LspTool/tsProject.ts
|
|
1105
|
+
import { statSync } from "fs";
|
|
1106
|
+
import { createRequire } from "node:module";
|
|
1107
|
+
import { extname, join as join2 } from "path";
|
|
1108
|
+
import { pathToFileURL } from "url";
|
|
1109
|
+
var cachedTypeScript = null;
|
|
1110
|
+
function tryLoadTypeScriptModule(projectCwd) {
|
|
1111
|
+
if (cachedTypeScript?.cwd === projectCwd) return cachedTypeScript.module;
|
|
1112
|
+
try {
|
|
1113
|
+
const requireFromCwd = createRequire(
|
|
1114
|
+
pathToFileURL(join2(projectCwd, "__kode_lsp__.js"))
|
|
1115
|
+
);
|
|
1116
|
+
const mod = requireFromCwd("typescript");
|
|
1117
|
+
cachedTypeScript = { cwd: projectCwd, module: mod };
|
|
1118
|
+
return mod;
|
|
1119
|
+
} catch {
|
|
1120
|
+
cachedTypeScript = { cwd: projectCwd, module: null };
|
|
1121
|
+
return null;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
var projectCache = /* @__PURE__ */ new Map();
|
|
1125
|
+
function getOrCreateTsProject(projectCwd) {
|
|
1126
|
+
const ts = tryLoadTypeScriptModule(projectCwd);
|
|
1127
|
+
if (!ts) return null;
|
|
1128
|
+
const existing = projectCache.get(projectCwd);
|
|
1129
|
+
if (existing) return existing;
|
|
1130
|
+
let compilerOptions = {
|
|
1131
|
+
allowJs: true,
|
|
1132
|
+
checkJs: false,
|
|
1133
|
+
jsx: ts.JsxEmit.ReactJSX,
|
|
1134
|
+
target: ts.ScriptTarget.ESNext,
|
|
1135
|
+
module: ts.ModuleKind.ESNext,
|
|
1136
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext
|
|
1137
|
+
};
|
|
1138
|
+
let rootFileNames = [];
|
|
1139
|
+
try {
|
|
1140
|
+
const configPath = ts.findConfigFile(
|
|
1141
|
+
projectCwd,
|
|
1142
|
+
ts.sys.fileExists,
|
|
1143
|
+
"tsconfig.json"
|
|
1144
|
+
);
|
|
1145
|
+
if (configPath) {
|
|
1146
|
+
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
1147
|
+
if (!configFile.error) {
|
|
1148
|
+
const parsed = ts.parseJsonConfigFileContent(
|
|
1149
|
+
configFile.config,
|
|
1150
|
+
ts.sys,
|
|
1151
|
+
projectCwd
|
|
1152
|
+
);
|
|
1153
|
+
compilerOptions = { ...compilerOptions, ...parsed.options };
|
|
1154
|
+
rootFileNames = parsed.fileNames;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
} catch {
|
|
1158
|
+
}
|
|
1159
|
+
const rootFiles = new Set(rootFileNames);
|
|
1160
|
+
const versions = /* @__PURE__ */ new Map();
|
|
1161
|
+
const host = {
|
|
1162
|
+
getCompilationSettings: () => compilerOptions,
|
|
1163
|
+
getScriptFileNames: () => Array.from(rootFiles),
|
|
1164
|
+
getScriptVersion: (fileName) => {
|
|
1165
|
+
try {
|
|
1166
|
+
const stat = statSync(fileName);
|
|
1167
|
+
const version = String(stat.mtimeMs ?? Date.now());
|
|
1168
|
+
versions.set(fileName, version);
|
|
1169
|
+
return version;
|
|
1170
|
+
} catch {
|
|
1171
|
+
return versions.get(fileName) ?? "0";
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
getScriptSnapshot: (fileName) => {
|
|
1175
|
+
try {
|
|
1176
|
+
if (!ts.sys.fileExists(fileName)) return void 0;
|
|
1177
|
+
const content = ts.sys.readFile(fileName);
|
|
1178
|
+
if (content === void 0) return void 0;
|
|
1179
|
+
const stat = statSync(fileName);
|
|
1180
|
+
versions.set(fileName, String(stat.mtimeMs ?? Date.now()));
|
|
1181
|
+
return ts.ScriptSnapshot.fromString(content);
|
|
1182
|
+
} catch {
|
|
1183
|
+
return void 0;
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
getCurrentDirectory: () => projectCwd,
|
|
1187
|
+
getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options),
|
|
1188
|
+
fileExists: ts.sys.fileExists,
|
|
1189
|
+
readFile: ts.sys.readFile,
|
|
1190
|
+
readDirectory: ts.sys.readDirectory,
|
|
1191
|
+
directoryExists: ts.sys.directoryExists,
|
|
1192
|
+
getDirectories: ts.sys.getDirectories,
|
|
1193
|
+
useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
|
|
1194
|
+
getCanonicalFileName: (fileName) => ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(),
|
|
1195
|
+
getNewLine: () => ts.sys.newLine
|
|
1196
|
+
};
|
|
1197
|
+
const languageService = ts.createLanguageService(
|
|
1198
|
+
host,
|
|
1199
|
+
ts.createDocumentRegistry()
|
|
1200
|
+
);
|
|
1201
|
+
const state = {
|
|
1202
|
+
ts,
|
|
1203
|
+
cwd: projectCwd,
|
|
1204
|
+
rootFiles,
|
|
1205
|
+
compilerOptions,
|
|
1206
|
+
languageService,
|
|
1207
|
+
versions
|
|
1208
|
+
};
|
|
1209
|
+
projectCache.set(projectCwd, state);
|
|
1210
|
+
return state;
|
|
1211
|
+
}
|
|
1212
|
+
function isFileTypeSupportedByTypescriptBackend(filePath) {
|
|
1213
|
+
const ext = extname(filePath).toLowerCase();
|
|
1214
|
+
return ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".jsx" || ext === ".mts" || ext === ".cts" || ext === ".mjs" || ext === ".cjs";
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// packages/tools/src/tools/system/LspTool/call.ts
|
|
1218
|
+
import { extname as extname2 } from "path";
|
|
1219
|
+
|
|
1220
|
+
// packages/tools/src/tools/system/LspTool/operations.ts
|
|
1221
|
+
function runLspOperation({
|
|
1222
|
+
input,
|
|
1223
|
+
absPath,
|
|
1224
|
+
pos,
|
|
1225
|
+
ts,
|
|
1226
|
+
program,
|
|
1227
|
+
service,
|
|
1228
|
+
sourceFile
|
|
1229
|
+
}) {
|
|
1230
|
+
let formatted;
|
|
1231
|
+
let resultCount = 0;
|
|
1232
|
+
let fileCount = 0;
|
|
1233
|
+
switch (input.operation) {
|
|
1234
|
+
case "goToDefinition": {
|
|
1235
|
+
const defs = service.getDefinitionAtPosition?.(absPath, pos) ?? [];
|
|
1236
|
+
const locations = defs.map((d) => {
|
|
1237
|
+
const defSourceFile = program.getSourceFile(d.fileName);
|
|
1238
|
+
if (!defSourceFile) return null;
|
|
1239
|
+
const lc = ts.getLineAndCharacterOfPosition(
|
|
1240
|
+
defSourceFile,
|
|
1241
|
+
d.textSpan.start
|
|
1242
|
+
);
|
|
1243
|
+
return {
|
|
1244
|
+
fileName: d.fileName,
|
|
1245
|
+
line0: lc.line,
|
|
1246
|
+
character0: lc.character
|
|
1247
|
+
};
|
|
1248
|
+
}).filter(Boolean);
|
|
1249
|
+
const res = formatGoToDefinitionResult(locations);
|
|
1250
|
+
formatted = res.formatted;
|
|
1251
|
+
resultCount = res.resultCount;
|
|
1252
|
+
fileCount = res.fileCount;
|
|
1253
|
+
break;
|
|
1254
|
+
}
|
|
1255
|
+
case "goToImplementation": {
|
|
1256
|
+
const impls = service.getImplementationAtPosition?.(absPath, pos) ?? [];
|
|
1257
|
+
const locations = impls.map((d) => {
|
|
1258
|
+
const defSourceFile = program.getSourceFile(d.fileName);
|
|
1259
|
+
if (!defSourceFile) return null;
|
|
1260
|
+
const lc = ts.getLineAndCharacterOfPosition(
|
|
1261
|
+
defSourceFile,
|
|
1262
|
+
d.textSpan.start
|
|
1263
|
+
);
|
|
1264
|
+
return {
|
|
1265
|
+
fileName: d.fileName,
|
|
1266
|
+
line0: lc.line,
|
|
1267
|
+
character0: lc.character
|
|
1268
|
+
};
|
|
1269
|
+
}).filter(Boolean);
|
|
1270
|
+
const res = formatGoToDefinitionResult(locations);
|
|
1271
|
+
formatted = res.formatted;
|
|
1272
|
+
resultCount = res.resultCount;
|
|
1273
|
+
fileCount = res.fileCount;
|
|
1274
|
+
break;
|
|
1275
|
+
}
|
|
1276
|
+
case "findReferences": {
|
|
1277
|
+
const referencedSymbols = service.findReferences?.(absPath, pos) ?? [];
|
|
1278
|
+
const refs = [];
|
|
1279
|
+
for (const sym of referencedSymbols) {
|
|
1280
|
+
for (const ref of sym.references ?? []) {
|
|
1281
|
+
const refSource = program.getSourceFile(ref.fileName);
|
|
1282
|
+
if (!refSource) continue;
|
|
1283
|
+
const lc = ts.getLineAndCharacterOfPosition(
|
|
1284
|
+
refSource,
|
|
1285
|
+
ref.textSpan.start
|
|
1286
|
+
);
|
|
1287
|
+
refs.push({
|
|
1288
|
+
fileName: ref.fileName,
|
|
1289
|
+
line0: lc.line,
|
|
1290
|
+
character0: lc.character
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
const res = formatFindReferencesResult(refs);
|
|
1295
|
+
formatted = res.formatted;
|
|
1296
|
+
resultCount = res.resultCount;
|
|
1297
|
+
fileCount = res.fileCount;
|
|
1298
|
+
break;
|
|
1299
|
+
}
|
|
1300
|
+
case "hover": {
|
|
1301
|
+
const info = service.getQuickInfoAtPosition?.(absPath, pos);
|
|
1302
|
+
let text = null;
|
|
1303
|
+
let hoverLine0 = input.line - 1;
|
|
1304
|
+
let hoverCharacter0 = input.character - 1;
|
|
1305
|
+
if (info) {
|
|
1306
|
+
const parts = [];
|
|
1307
|
+
const signature = ts.displayPartsToString(info.displayParts ?? []);
|
|
1308
|
+
if (signature) parts.push(signature);
|
|
1309
|
+
const doc = ts.displayPartsToString(info.documentation ?? []);
|
|
1310
|
+
if (doc) parts.push(doc);
|
|
1311
|
+
if (info.tags && info.tags.length > 0) {
|
|
1312
|
+
for (const tag of info.tags) {
|
|
1313
|
+
const tagText = ts.displayPartsToString(tag.text ?? []);
|
|
1314
|
+
parts.push(`@${tag.name}${tagText ? ` ${tagText}` : ""}`);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
text = parts.filter(Boolean).join("\\n\\n");
|
|
1318
|
+
const lc = ts.getLineAndCharacterOfPosition(
|
|
1319
|
+
sourceFile,
|
|
1320
|
+
info.textSpan.start
|
|
1321
|
+
);
|
|
1322
|
+
hoverLine0 = lc.line;
|
|
1323
|
+
hoverCharacter0 = lc.character;
|
|
1324
|
+
}
|
|
1325
|
+
const res = formatHoverResult(text, hoverLine0, hoverCharacter0);
|
|
1326
|
+
formatted = res.formatted;
|
|
1327
|
+
resultCount = res.resultCount;
|
|
1328
|
+
fileCount = res.fileCount;
|
|
1329
|
+
break;
|
|
1330
|
+
}
|
|
1331
|
+
case "documentSymbol": {
|
|
1332
|
+
const tree = service.getNavigationTree?.(absPath);
|
|
1333
|
+
const lines = [];
|
|
1334
|
+
let count = 0;
|
|
1335
|
+
const kindLabel = (kind) => {
|
|
1336
|
+
const m = {
|
|
1337
|
+
class: "Class",
|
|
1338
|
+
interface: "Interface",
|
|
1339
|
+
enum: "Enum",
|
|
1340
|
+
function: "Function",
|
|
1341
|
+
method: "Method",
|
|
1342
|
+
property: "Property",
|
|
1343
|
+
var: "Variable",
|
|
1344
|
+
let: "Variable",
|
|
1345
|
+
const: "Constant",
|
|
1346
|
+
module: "Module",
|
|
1347
|
+
alias: "Alias",
|
|
1348
|
+
type: "Type"
|
|
1349
|
+
};
|
|
1350
|
+
return m[kind] ?? (kind ? kind[0].toUpperCase() + kind.slice(1) : "Unknown");
|
|
1351
|
+
};
|
|
1352
|
+
const walk = (node, depth) => {
|
|
1353
|
+
const children = node?.childItems ?? [];
|
|
1354
|
+
for (const child of children) {
|
|
1355
|
+
const span = child.spans?.[0];
|
|
1356
|
+
if (!span) continue;
|
|
1357
|
+
const lc = ts.getLineAndCharacterOfPosition(sourceFile, span.start);
|
|
1358
|
+
const indent = " ".repeat(depth);
|
|
1359
|
+
const label = kindLabel(child.kind);
|
|
1360
|
+
const detail = child.kindModifiers ? ` ${child.kindModifiers}` : "";
|
|
1361
|
+
lines.push(
|
|
1362
|
+
`${indent}${child.text} (${label})${detail} - Line ${lc.line + 1}`
|
|
1363
|
+
);
|
|
1364
|
+
count += 1;
|
|
1365
|
+
if (child.childItems && child.childItems.length > 0) {
|
|
1366
|
+
walk(child, depth + 1);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
walk(tree, 0);
|
|
1371
|
+
const res = formatDocumentSymbolsResult(lines, count);
|
|
1372
|
+
formatted = res.formatted;
|
|
1373
|
+
resultCount = res.resultCount;
|
|
1374
|
+
fileCount = res.fileCount;
|
|
1375
|
+
break;
|
|
1376
|
+
}
|
|
1377
|
+
case "workspaceSymbol": {
|
|
1378
|
+
const items = service.getNavigateToItems?.("", 100, void 0, true, true) ?? [];
|
|
1379
|
+
if (!items || items.length === 0) {
|
|
1380
|
+
formatted = "No symbols found in workspace. This may occur if the workspace is empty, or if the LSP server has not finished indexing the project.";
|
|
1381
|
+
resultCount = 0;
|
|
1382
|
+
fileCount = 0;
|
|
1383
|
+
break;
|
|
1384
|
+
}
|
|
1385
|
+
const lines = [
|
|
1386
|
+
`Found ${items.length} symbol${items.length === 1 ? "" : "s"} in workspace:`
|
|
1387
|
+
];
|
|
1388
|
+
const wrappedItems = items.map(
|
|
1389
|
+
(it) => ({
|
|
1390
|
+
fileName: it.fileName,
|
|
1391
|
+
item: it
|
|
1392
|
+
})
|
|
1393
|
+
);
|
|
1394
|
+
const grouped = groupLocationsByFile(wrappedItems);
|
|
1395
|
+
for (const [file, itemsInFile] of grouped) {
|
|
1396
|
+
lines.push(`\\n${file}:`);
|
|
1397
|
+
for (const wrapper of itemsInFile) {
|
|
1398
|
+
const it = wrapper.item;
|
|
1399
|
+
const sf = program.getSourceFile(it.fileName);
|
|
1400
|
+
if (!sf) continue;
|
|
1401
|
+
const span = it.textSpan;
|
|
1402
|
+
const lc = span ? ts.getLineAndCharacterOfPosition(sf, span.start) : { line: 0, character: 0 };
|
|
1403
|
+
const label = it.kind ? String(it.kind)[0].toUpperCase() + String(it.kind).slice(1) : "Symbol";
|
|
1404
|
+
let line = ` ${it.name} (${label}) - Line ${lc.line + 1}`;
|
|
1405
|
+
if (it.containerName) line += ` in ${it.containerName}`;
|
|
1406
|
+
lines.push(line);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
formatted = lines.join("\\n");
|
|
1410
|
+
resultCount = items.length;
|
|
1411
|
+
fileCount = grouped.size;
|
|
1412
|
+
break;
|
|
1413
|
+
}
|
|
1414
|
+
case "prepareCallHierarchy":
|
|
1415
|
+
case "incomingCalls":
|
|
1416
|
+
case "outgoingCalls": {
|
|
1417
|
+
const opLabel = input.operation;
|
|
1418
|
+
formatted = `Error performing ${opLabel}: Call hierarchy is not supported by the TypeScript backend`;
|
|
1419
|
+
resultCount = 0;
|
|
1420
|
+
fileCount = 0;
|
|
1421
|
+
break;
|
|
1422
|
+
}
|
|
1423
|
+
default: {
|
|
1424
|
+
formatted = `Error performing ${input.operation}: Unsupported operation`;
|
|
1425
|
+
resultCount = 0;
|
|
1426
|
+
fileCount = 0;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return { formatted, resultCount, fileCount };
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// packages/tools/src/tools/system/LspTool/call.ts
|
|
1433
|
+
async function* callLspTool(input, _context) {
|
|
1434
|
+
const absPath = getAbsolutePath(input.filePath) ?? input.filePath;
|
|
1435
|
+
if (!isFileTypeSupportedByTypescriptBackend(absPath)) {
|
|
1436
|
+
const ext = extname2(absPath);
|
|
1437
|
+
const out = {
|
|
1438
|
+
operation: input.operation,
|
|
1439
|
+
result: `No LSP server available for file type: ${ext}`,
|
|
1440
|
+
filePath: input.filePath,
|
|
1441
|
+
resultCount: 0,
|
|
1442
|
+
fileCount: 0
|
|
1443
|
+
};
|
|
1444
|
+
yield { type: "result", data: out, resultForAssistant: out.result };
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
const project = getOrCreateTsProject(getCwd());
|
|
1448
|
+
if (!project) {
|
|
1449
|
+
const out = {
|
|
1450
|
+
operation: input.operation,
|
|
1451
|
+
result: "LSP server manager not initialized. This may indicate a startup issue.",
|
|
1452
|
+
filePath: input.filePath,
|
|
1453
|
+
resultCount: 0,
|
|
1454
|
+
fileCount: 0
|
|
1455
|
+
};
|
|
1456
|
+
yield { type: "result", data: out, resultForAssistant: out.result };
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
project.rootFiles.add(absPath);
|
|
1460
|
+
const ts = project.ts;
|
|
1461
|
+
const service = project.languageService;
|
|
1462
|
+
const program = service.getProgram?.();
|
|
1463
|
+
if (!program) {
|
|
1464
|
+
const out = {
|
|
1465
|
+
operation: input.operation,
|
|
1466
|
+
result: `Error performing ${input.operation}: TypeScript program not available`,
|
|
1467
|
+
filePath: input.filePath,
|
|
1468
|
+
resultCount: 0,
|
|
1469
|
+
fileCount: 0
|
|
1470
|
+
};
|
|
1471
|
+
yield { type: "result", data: out, resultForAssistant: out.result };
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
const sourceFile = program.getSourceFile(absPath);
|
|
1475
|
+
if (!sourceFile) {
|
|
1476
|
+
const out = {
|
|
1477
|
+
operation: input.operation,
|
|
1478
|
+
result: `Error performing ${input.operation}: File is not part of the TypeScript program`,
|
|
1479
|
+
filePath: input.filePath,
|
|
1480
|
+
resultCount: 0,
|
|
1481
|
+
fileCount: 0
|
|
1482
|
+
};
|
|
1483
|
+
yield { type: "result", data: out, resultForAssistant: out.result };
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
const pos = ts.getPositionOfLineAndCharacter(
|
|
1487
|
+
sourceFile,
|
|
1488
|
+
input.line - 1,
|
|
1489
|
+
input.character - 1
|
|
1490
|
+
);
|
|
1491
|
+
try {
|
|
1492
|
+
const { formatted, resultCount, fileCount } = runLspOperation({
|
|
1493
|
+
input,
|
|
1494
|
+
absPath,
|
|
1495
|
+
pos,
|
|
1496
|
+
program,
|
|
1497
|
+
service,
|
|
1498
|
+
ts,
|
|
1499
|
+
sourceFile
|
|
1500
|
+
});
|
|
1501
|
+
const out = {
|
|
1502
|
+
operation: input.operation,
|
|
1503
|
+
result: formatted,
|
|
1504
|
+
filePath: input.filePath,
|
|
1505
|
+
resultCount,
|
|
1506
|
+
fileCount
|
|
1507
|
+
};
|
|
1508
|
+
yield { type: "result", data: out, resultForAssistant: out.result };
|
|
1509
|
+
} catch (err) {
|
|
1510
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1511
|
+
const out = {
|
|
1512
|
+
operation: input.operation,
|
|
1513
|
+
result: `Error performing ${input.operation}: ${message}`,
|
|
1514
|
+
filePath: input.filePath
|
|
1515
|
+
};
|
|
1516
|
+
yield { type: "result", data: out, resultForAssistant: out.result };
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// packages/tools/src/tools/system/LspTool/prompt.ts
|
|
1521
|
+
var TOOL_NAME_FOR_PROMPT3 = "LSP";
|
|
1522
|
+
var PROMPT5 = `Interact with Language Server Protocol (LSP) servers to get code intelligence features.
|
|
1523
|
+
|
|
1524
|
+
Supported operations:
|
|
1525
|
+
- goToDefinition: Find where a symbol is defined
|
|
1526
|
+
- findReferences: Find all references to a symbol
|
|
1527
|
+
- hover: Get hover information (documentation, type info) for a symbol
|
|
1528
|
+
- documentSymbol: Get all symbols (functions, classes, variables) in a document
|
|
1529
|
+
- workspaceSymbol: Search for symbols across the entire workspace
|
|
1530
|
+
- goToImplementation: Find implementations of an interface or abstract method
|
|
1531
|
+
- prepareCallHierarchy: Get call hierarchy item at a position (functions/methods)
|
|
1532
|
+
- incomingCalls: Find all functions/methods that call the function at a position
|
|
1533
|
+
- outgoingCalls: Find all functions/methods called by the function at a position
|
|
1534
|
+
|
|
1535
|
+
All operations require:
|
|
1536
|
+
- filePath: The file to operate on
|
|
1537
|
+
- line: The line number (1-based, as shown in editors)
|
|
1538
|
+
- character: The character offset (1-based, as shown in editors)
|
|
1539
|
+
|
|
1540
|
+
Note: LSP servers must be configured for the file type. If no server is available, an error will be returned.`;
|
|
1541
|
+
var DESCRIPTION5 = PROMPT5;
|
|
1542
|
+
|
|
1543
|
+
// packages/tools/src/tools/system/LspTool/LspTool.tsx
|
|
1544
|
+
var inputSchema5 = z5.strictObject({
|
|
1545
|
+
operation: z5.enum(OPERATIONS).describe("The LSP operation to perform"),
|
|
1546
|
+
filePath: z5.string().describe("The absolute or relative path to the file"),
|
|
1547
|
+
line: z5.number().int().positive().describe("The line number (1-based, as shown in editors)"),
|
|
1548
|
+
character: z5.number().int().positive().describe("The character offset (1-based, as shown in editors)")
|
|
1549
|
+
});
|
|
1550
|
+
var outputSchema = z5.object({
|
|
1551
|
+
operation: z5.enum(OPERATIONS).describe("The LSP operation that was performed"),
|
|
1552
|
+
result: z5.string().describe("The formatted result of the LSP operation"),
|
|
1553
|
+
filePath: z5.string().describe("The file path the operation was performed on"),
|
|
1554
|
+
resultCount: z5.number().int().nonnegative().optional().describe("Number of results (definitions, references, symbols)"),
|
|
1555
|
+
fileCount: z5.number().int().nonnegative().optional().describe("Number of files containing results")
|
|
1556
|
+
});
|
|
1557
|
+
var LspTool = {
|
|
1558
|
+
name: TOOL_NAME_FOR_PROMPT3,
|
|
1559
|
+
async description() {
|
|
1560
|
+
return DESCRIPTION5;
|
|
1561
|
+
},
|
|
1562
|
+
async prompt() {
|
|
1563
|
+
return PROMPT5;
|
|
1564
|
+
},
|
|
1565
|
+
inputSchema: inputSchema5,
|
|
1566
|
+
userFacingName() {
|
|
1567
|
+
return "LSP";
|
|
1568
|
+
},
|
|
1569
|
+
async isEnabled() {
|
|
1570
|
+
return tryLoadTypeScriptModule(getCwd()) !== null;
|
|
1571
|
+
},
|
|
1572
|
+
isReadOnly() {
|
|
1573
|
+
return true;
|
|
1574
|
+
},
|
|
1575
|
+
isConcurrencySafe() {
|
|
1576
|
+
return true;
|
|
1577
|
+
},
|
|
1578
|
+
needsPermissions({ filePath }) {
|
|
1579
|
+
const abs = getAbsolutePath(filePath) ?? filePath;
|
|
1580
|
+
return !hasReadPermission(abs || getCwd());
|
|
1581
|
+
},
|
|
1582
|
+
async validateInput(input) {
|
|
1583
|
+
const parsed = inputSchema5.safeParse(input);
|
|
1584
|
+
if (!parsed.success) {
|
|
1585
|
+
return {
|
|
1586
|
+
result: false,
|
|
1587
|
+
message: `Invalid input: ${parsed.error.message}`,
|
|
1588
|
+
errorCode: 3
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
const absPath = getAbsolutePath(input.filePath) ?? input.filePath;
|
|
1592
|
+
if (!existsSync2(absPath)) {
|
|
1593
|
+
return {
|
|
1594
|
+
result: false,
|
|
1595
|
+
message: `File does not exist: ${input.filePath}`,
|
|
1596
|
+
errorCode: 1
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
try {
|
|
1600
|
+
if (!statSync2(absPath).isFile()) {
|
|
1601
|
+
return {
|
|
1602
|
+
result: false,
|
|
1603
|
+
message: `Path is not a file: ${input.filePath}`,
|
|
1604
|
+
errorCode: 2
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
} catch (err) {
|
|
1608
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
1609
|
+
return {
|
|
1610
|
+
result: false,
|
|
1611
|
+
message: `Cannot access file: ${input.filePath}. ${e.message}`,
|
|
1612
|
+
errorCode: 4
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
return { result: true };
|
|
1616
|
+
},
|
|
1617
|
+
renderToolUseMessage(input, { verbose }) {
|
|
1618
|
+
const abs = getAbsolutePath(input.filePath) ?? input.filePath;
|
|
1619
|
+
const filePathForDisplay = verbose ? abs : toProjectRelativeIfPossible(abs);
|
|
1620
|
+
const parts = [];
|
|
1621
|
+
if ((input.operation === "goToDefinition" || input.operation === "findReferences" || input.operation === "hover" || input.operation === "goToImplementation") && input.filePath && input.line !== void 0 && input.character !== void 0) {
|
|
1622
|
+
try {
|
|
1623
|
+
const content = readFileSync2(abs, "utf8");
|
|
1624
|
+
const symbol = extractSymbolAtPosition(
|
|
1625
|
+
content.split("\n"),
|
|
1626
|
+
input.line - 1,
|
|
1627
|
+
input.character - 1
|
|
1628
|
+
);
|
|
1629
|
+
if (symbol) {
|
|
1630
|
+
parts.push(`operation: "${input.operation}"`);
|
|
1631
|
+
parts.push(`symbol: "${symbol}"`);
|
|
1632
|
+
parts.push(`in: "${filePathForDisplay}"`);
|
|
1633
|
+
return parts.join(", ");
|
|
1634
|
+
}
|
|
1635
|
+
} catch {
|
|
1636
|
+
}
|
|
1637
|
+
parts.push(`operation: "${input.operation}"`);
|
|
1638
|
+
parts.push(`file: "${filePathForDisplay}"`);
|
|
1639
|
+
parts.push(`position: ${input.line}:${input.character}`);
|
|
1640
|
+
return parts.join(", ");
|
|
1641
|
+
}
|
|
1642
|
+
parts.push(`operation: "${input.operation}"`);
|
|
1643
|
+
if (input.filePath) parts.push(`file: "${filePathForDisplay}"`);
|
|
1644
|
+
return parts.join(", ");
|
|
1645
|
+
},
|
|
1646
|
+
renderToolResultMessage(output, { verbose }) {
|
|
1647
|
+
if (output.resultCount !== void 0 && output.fileCount !== void 0) {
|
|
1648
|
+
const display = verbose ? maybeTruncateVerboseToolOutput(output.result, {
|
|
1649
|
+
maxLines: 120,
|
|
1650
|
+
maxChars: 2e4
|
|
1651
|
+
}) : null;
|
|
1652
|
+
return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "row" }, /* @__PURE__ */ React4.createElement(Text4, null, "\xA0\xA0\u23BF \xA0"), summarizeToolResult(
|
|
1653
|
+
output.operation,
|
|
1654
|
+
output.resultCount,
|
|
1655
|
+
output.fileCount
|
|
1656
|
+
)), display ? /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 5 }, /* @__PURE__ */ React4.createElement(Text4, null, display.text)) : null);
|
|
1657
|
+
}
|
|
1658
|
+
return /* @__PURE__ */ React4.createElement(Box3, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "row" }, /* @__PURE__ */ React4.createElement(Text4, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React4.createElement(Text4, null, output.result)));
|
|
1659
|
+
},
|
|
1660
|
+
renderResultForAssistant(output) {
|
|
1661
|
+
return output.result;
|
|
1662
|
+
},
|
|
1663
|
+
async *call(input, context) {
|
|
1664
|
+
yield* callLspTool(input, context);
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
|
|
1668
|
+
// packages/tools/src/tools/mcp/MCPTool/MCPTool.tsx
|
|
1669
|
+
import { Box as Box4, Text as Text5 } from "ink";
|
|
1670
|
+
import * as React5 from "react";
|
|
1671
|
+
import { z as z6 } from "zod";
|
|
1672
|
+
|
|
1673
|
+
// packages/tools/src/tools/mcp/MCPTool/prompt.ts
|
|
1674
|
+
var PROMPT6 = "";
|
|
1675
|
+
var DESCRIPTION6 = "";
|
|
1676
|
+
|
|
1677
|
+
// packages/tools/src/tools/mcp/MCPTool/MCPTool.tsx
|
|
1678
|
+
var inputSchema6 = z6.object({}).passthrough();
|
|
1679
|
+
var MCPTool = {
|
|
1680
|
+
async isEnabled() {
|
|
1681
|
+
return true;
|
|
1682
|
+
},
|
|
1683
|
+
isReadOnly() {
|
|
1684
|
+
return false;
|
|
1685
|
+
},
|
|
1686
|
+
isConcurrencySafe() {
|
|
1687
|
+
return false;
|
|
1688
|
+
},
|
|
1689
|
+
// Overridden in mcpClient.ts
|
|
1690
|
+
name: "mcp",
|
|
1691
|
+
// Overridden in mcpClient.ts
|
|
1692
|
+
async description() {
|
|
1693
|
+
return DESCRIPTION6;
|
|
1694
|
+
},
|
|
1695
|
+
// Overridden in mcpClient.ts
|
|
1696
|
+
async prompt() {
|
|
1697
|
+
return PROMPT6;
|
|
1698
|
+
},
|
|
1699
|
+
inputSchema: inputSchema6,
|
|
1700
|
+
// Overridden in mcpClient.ts
|
|
1701
|
+
async *call() {
|
|
1702
|
+
yield {
|
|
1703
|
+
type: "result",
|
|
1704
|
+
data: "",
|
|
1705
|
+
resultForAssistant: ""
|
|
1706
|
+
};
|
|
1707
|
+
},
|
|
1708
|
+
needsPermissions() {
|
|
1709
|
+
return true;
|
|
1710
|
+
},
|
|
1711
|
+
renderToolUseMessage(input) {
|
|
1712
|
+
return Object.entries(input).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(", ");
|
|
1713
|
+
},
|
|
1714
|
+
// Overridden in mcpClient.ts
|
|
1715
|
+
userFacingName: () => "mcp",
|
|
1716
|
+
renderToolResultMessage(output) {
|
|
1717
|
+
const verbose = false;
|
|
1718
|
+
if (Array.isArray(output)) {
|
|
1719
|
+
return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column" }, output.map((item, i) => {
|
|
1720
|
+
if (item.type === "image") {
|
|
1721
|
+
return /* @__PURE__ */ React5.createElement(
|
|
1722
|
+
Box4,
|
|
1723
|
+
{
|
|
1724
|
+
key: i,
|
|
1725
|
+
justifyContent: "space-between",
|
|
1726
|
+
overflowX: "hidden",
|
|
1727
|
+
width: "100%"
|
|
1728
|
+
},
|
|
1729
|
+
/* @__PURE__ */ React5.createElement(Box4, { flexDirection: "row" }, /* @__PURE__ */ React5.createElement(Text5, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React5.createElement(Text5, null, "[Image]"))
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
const lines2 = item.text.split("\n").length;
|
|
1733
|
+
return /* @__PURE__ */ React5.createElement(
|
|
1734
|
+
OutputLine,
|
|
1735
|
+
{
|
|
1736
|
+
key: i,
|
|
1737
|
+
content: item.text,
|
|
1738
|
+
lines: lines2,
|
|
1739
|
+
verbose
|
|
1740
|
+
}
|
|
1741
|
+
);
|
|
1742
|
+
}));
|
|
1743
|
+
}
|
|
1744
|
+
if (!output) {
|
|
1745
|
+
return /* @__PURE__ */ React5.createElement(Box4, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "row" }, /* @__PURE__ */ React5.createElement(Text5, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React5.createElement(Text5, { color: getTheme().secondaryText }, "(No content)")));
|
|
1746
|
+
}
|
|
1747
|
+
const lines = output.split("\n").length;
|
|
1748
|
+
return /* @__PURE__ */ React5.createElement(OutputLine, { content: output, lines, verbose });
|
|
1749
|
+
},
|
|
1750
|
+
renderResultForAssistant(content) {
|
|
1751
|
+
return content;
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
|
|
1755
|
+
// packages/tools/src/tools/mcp/ReadMcpResourceTool/ReadMcpResourceTool.tsx
|
|
1756
|
+
import { Box as Box5, Text as Text6 } from "ink";
|
|
1757
|
+
import React6 from "react";
|
|
1758
|
+
import { z as z7 } from "zod";
|
|
1759
|
+
import { ReadResourceResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
1760
|
+
|
|
1761
|
+
// packages/tools/src/tools/mcp/ReadMcpResourceTool/prompt.ts
|
|
1762
|
+
var TOOL_NAME2 = "ReadMcpResourceTool";
|
|
1763
|
+
var DESCRIPTION7 = `Reads a specific resource from an MCP server.
|
|
1764
|
+
- server: The name of the MCP server to read from
|
|
1765
|
+
- uri: The URI of the resource to read
|
|
1766
|
+
|
|
1767
|
+
Usage examples:
|
|
1768
|
+
- Read a resource from a server: \`readMcpResource({ server: "myserver", uri: "my-resource-uri" })\``;
|
|
1769
|
+
var PROMPT7 = `Reads a specific resource from an MCP server, identified by server name and resource URI.
|
|
1770
|
+
|
|
1771
|
+
Parameters:
|
|
1772
|
+
- server (required): The name of the MCP server from which to read the resource
|
|
1773
|
+
- uri (required): The URI of the resource to read`;
|
|
1774
|
+
|
|
1775
|
+
// packages/tools/src/tools/mcp/ReadMcpResourceTool/ReadMcpResourceTool.tsx
|
|
1776
|
+
var inputSchema7 = z7.strictObject({
|
|
1777
|
+
server: z7.string().describe("The MCP server name"),
|
|
1778
|
+
uri: z7.string().describe("The resource URI to read")
|
|
1779
|
+
});
|
|
1780
|
+
function isWrappedClient2(value) {
|
|
1781
|
+
if (!value || typeof value !== "object") return false;
|
|
1782
|
+
const record = value;
|
|
1783
|
+
if (typeof record.name !== "string") return false;
|
|
1784
|
+
if (record.type !== "connected" && record.type !== "failed") return false;
|
|
1785
|
+
if (record.type === "connected") {
|
|
1786
|
+
return typeof record.client === "object" && record.client !== null;
|
|
1787
|
+
}
|
|
1788
|
+
return true;
|
|
1789
|
+
}
|
|
1790
|
+
async function getMcpClients2(context) {
|
|
1791
|
+
const override = context?.options?.mcpClients;
|
|
1792
|
+
if (Array.isArray(override) && override.every(isWrappedClient2)) {
|
|
1793
|
+
return override;
|
|
1794
|
+
}
|
|
1795
|
+
return await getClients();
|
|
1796
|
+
}
|
|
1797
|
+
var ReadMcpResourceTool = {
|
|
1798
|
+
name: TOOL_NAME2,
|
|
1799
|
+
async description() {
|
|
1800
|
+
return DESCRIPTION7;
|
|
1801
|
+
},
|
|
1802
|
+
async prompt() {
|
|
1803
|
+
return PROMPT7;
|
|
1804
|
+
},
|
|
1805
|
+
inputSchema: inputSchema7,
|
|
1806
|
+
userFacingName() {
|
|
1807
|
+
return "readMcpResource";
|
|
1808
|
+
},
|
|
1809
|
+
async isEnabled() {
|
|
1810
|
+
return true;
|
|
1811
|
+
},
|
|
1812
|
+
isReadOnly() {
|
|
1813
|
+
return true;
|
|
1814
|
+
},
|
|
1815
|
+
isConcurrencySafe() {
|
|
1816
|
+
return true;
|
|
1817
|
+
},
|
|
1818
|
+
needsPermissions() {
|
|
1819
|
+
return false;
|
|
1820
|
+
},
|
|
1821
|
+
async validateInput({ server }, context) {
|
|
1822
|
+
const clients = await getMcpClients2(context);
|
|
1823
|
+
const match = clients.find((c) => c.name === server);
|
|
1824
|
+
if (!match) {
|
|
1825
|
+
return {
|
|
1826
|
+
result: false,
|
|
1827
|
+
message: `Server "${server}" not found. Available servers: ${clients.map((c) => c.name).join(", ")}`,
|
|
1828
|
+
errorCode: 1
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
if (match.type !== "connected") {
|
|
1832
|
+
return {
|
|
1833
|
+
result: false,
|
|
1834
|
+
message: `Server "${server}" is not connected`,
|
|
1835
|
+
errorCode: 2
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
let capabilities = match.capabilities ?? null;
|
|
1839
|
+
if (!capabilities) {
|
|
1840
|
+
try {
|
|
1841
|
+
capabilities = match.client.getServerCapabilities() ?? null;
|
|
1842
|
+
} catch {
|
|
1843
|
+
capabilities = null;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
if (!capabilities?.resources) {
|
|
1847
|
+
return {
|
|
1848
|
+
result: false,
|
|
1849
|
+
message: `Server "${server}" does not support resources`,
|
|
1850
|
+
errorCode: 3
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
return { result: true };
|
|
1854
|
+
},
|
|
1855
|
+
renderToolUseMessage({ server, uri }) {
|
|
1856
|
+
if (!server || !uri) return null;
|
|
1857
|
+
return `Read resource "${uri}" from server "${server}"`;
|
|
1858
|
+
},
|
|
1859
|
+
renderToolResultMessage(output) {
|
|
1860
|
+
const count = output.contents?.length ?? 0;
|
|
1861
|
+
return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "row" }, /* @__PURE__ */ React6.createElement(Text6, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Read MCP resource"), /* @__PURE__ */ React6.createElement(Text6, null, count ? ` (${count} part${count === 1 ? "" : "s"})` : ""));
|
|
1862
|
+
},
|
|
1863
|
+
renderResultForAssistant(output) {
|
|
1864
|
+
return JSON.stringify(output);
|
|
1865
|
+
},
|
|
1866
|
+
async *call({ server, uri }, context) {
|
|
1867
|
+
const clients = await getMcpClients2(context);
|
|
1868
|
+
const match = clients.find((c) => c.name === server);
|
|
1869
|
+
if (!match) {
|
|
1870
|
+
throw new Error(
|
|
1871
|
+
`Server "${server}" not found. Available servers: ${clients.map((c) => c.name).join(", ")}`
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1874
|
+
if (match.type !== "connected") {
|
|
1875
|
+
throw new Error(`Server "${server}" is not connected`);
|
|
1876
|
+
}
|
|
1877
|
+
let capabilities = match.capabilities ?? null;
|
|
1878
|
+
if (!capabilities) {
|
|
1879
|
+
try {
|
|
1880
|
+
capabilities = match.client.getServerCapabilities() ?? null;
|
|
1881
|
+
} catch {
|
|
1882
|
+
capabilities = null;
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
if (!capabilities?.resources) {
|
|
1886
|
+
throw new Error(`Server "${server}" does not support resources`);
|
|
1887
|
+
}
|
|
1888
|
+
const result = await match.client.request(
|
|
1889
|
+
{ method: "resources/read", params: { uri } },
|
|
1890
|
+
ReadResourceResultSchema
|
|
1891
|
+
);
|
|
1892
|
+
yield {
|
|
1893
|
+
type: "result",
|
|
1894
|
+
data: result,
|
|
1895
|
+
resultForAssistant: this.renderResultForAssistant(result)
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
|
|
1900
|
+
// packages/core/src/utils/agentTranscripts.ts
|
|
1901
|
+
var transcripts = /* @__PURE__ */ new Map();
|
|
1902
|
+
function saveAgentTranscript(agentId, messages) {
|
|
1903
|
+
transcripts.set(agentId, messages);
|
|
1904
|
+
}
|
|
1905
|
+
function getAgentTranscript(agentId) {
|
|
1906
|
+
return transcripts.get(agentId);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
// packages/tools/src/tools/ai/TaskTool/constants.ts
|
|
1910
|
+
var TOOL_NAME3 = "Task";
|
|
1911
|
+
|
|
1912
|
+
// packages/tools/src/tools/ai/TaskTool/prompt.ts
|
|
1913
|
+
var SUBAGENT_DISALLOWED_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
1914
|
+
"Task",
|
|
1915
|
+
"TaskOutput",
|
|
1916
|
+
"KillShell",
|
|
1917
|
+
"EnterPlanMode",
|
|
1918
|
+
"ExitPlanMode",
|
|
1919
|
+
"AskUserQuestion"
|
|
1920
|
+
]);
|
|
1921
|
+
async function getTaskTools(safeMode) {
|
|
1922
|
+
return (await (!safeMode ? getTools() : getReadOnlyTools())).filter(
|
|
1923
|
+
(tool) => !SUBAGENT_DISALLOWED_TOOL_NAMES.has(tool.name)
|
|
1924
|
+
);
|
|
1925
|
+
}
|
|
1926
|
+
async function getPrompt(safeMode) {
|
|
1927
|
+
const agents = await getActiveAgents();
|
|
1928
|
+
const agentDescriptions = agents.map((agent) => {
|
|
1929
|
+
const toolsStr = Array.isArray(agent.tools) ? agent.tools.join(", ") : "*";
|
|
1930
|
+
return `- ${agent.agentType}: ${agent.whenToUse} (Tools: ${toolsStr})`;
|
|
1931
|
+
}).join("\n");
|
|
1932
|
+
return `Launch a new agent to handle complex, multi-step tasks autonomously.
|
|
1933
|
+
|
|
1934
|
+
Available agent types and the tools they have access to:
|
|
1935
|
+
${agentDescriptions}
|
|
1936
|
+
|
|
1937
|
+
When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.
|
|
1938
|
+
|
|
1939
|
+
When to use the Agent tool:
|
|
1940
|
+
- When you are instructed to execute custom slash commands. Use the Agent tool with the slash command invocation as the entire prompt. The slash command can take arguments. For example: Task(description="Check the file", prompt="/check-file path/to/file.py")
|
|
1941
|
+
|
|
1942
|
+
When NOT to use the Agent tool:
|
|
1943
|
+
- If you want to read a specific file path, use the ${FileReadTool.name} or ${GlobTool.name} tool instead of the Agent tool, to find the match more quickly
|
|
1944
|
+
- If you are searching for a specific class definition like "class Foo", use the ${GlobTool.name} tool instead, to find the match more quickly
|
|
1945
|
+
- If you are searching for code within a specific file or set of 2-3 files, use the ${FileReadTool.name} tool instead of the Agent tool, to find the match more quickly
|
|
1946
|
+
- Other tasks that are not related to the agent descriptions above
|
|
1947
|
+
|
|
1948
|
+
Usage notes:
|
|
1949
|
+
1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
|
|
1950
|
+
2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
|
|
1951
|
+
3. Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
|
|
1952
|
+
4. The agent's outputs should generally be trusted
|
|
1953
|
+
5. Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent
|
|
1954
|
+
6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
|
|
1955
|
+
|
|
1956
|
+
Example usage:
|
|
1957
|
+
|
|
1958
|
+
<example_agent_descriptions>
|
|
1959
|
+
"code-reviewer": use this agent after you are done writing a signficant piece of code
|
|
1960
|
+
"greeting-responder": use this agent when to respond to user greetings with a friendly joke
|
|
1961
|
+
</example_agent_description>
|
|
1962
|
+
|
|
1963
|
+
<example>
|
|
1964
|
+
user: "Please write a function that checks if a number is prime"
|
|
1965
|
+
assistant: Sure let me write a function that checks if a number is prime
|
|
1966
|
+
assistant: First let me use the ${FileWriteTool.name} tool to write a function that checks if a number is prime
|
|
1967
|
+
assistant: I'm going to use the ${FileWriteTool.name} tool to write the following code:
|
|
1968
|
+
<code>
|
|
1969
|
+
function isPrime(n) {
|
|
1970
|
+
if (n <= 1) return false
|
|
1971
|
+
for (let i = 2; i * i <= n; i++) {
|
|
1972
|
+
if (n % i === 0) return false
|
|
1973
|
+
}
|
|
1974
|
+
return true
|
|
1975
|
+
}
|
|
1976
|
+
</code>
|
|
1977
|
+
<commentary>
|
|
1978
|
+
Since a signficant piece of code was written and the task was completed, now use the code-reviewer agent to review the code
|
|
1979
|
+
</commentary>
|
|
1980
|
+
assistant: Now let me use the code-reviewer agent to review the code
|
|
1981
|
+
assistant: Uses the Task tool to launch the with the code-reviewer agent
|
|
1982
|
+
</example>
|
|
1983
|
+
|
|
1984
|
+
<example>
|
|
1985
|
+
user: "Hello"
|
|
1986
|
+
<commentary>
|
|
1987
|
+
Since the user is greeting, use the greeting-responder agent to respond with a friendly joke
|
|
1988
|
+
</commentary>
|
|
1989
|
+
assistant: "I'm going to use the Task tool to launch the with the greeting-responder agent"
|
|
1990
|
+
</example>`;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// packages/tools/src/tools/ai/TaskTool/forkContext.ts
|
|
1994
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1995
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
1996
|
+
var FORK_CONTEXT_TOOL_RESULT_TEXT = `### FORKING CONVERSATION CONTEXT ###
|
|
1997
|
+
### ENTERING SUB-AGENT ROUTINE ###
|
|
1998
|
+
Entered sub-agent context
|
|
1999
|
+
|
|
2000
|
+
PLEASE NOTE:
|
|
2001
|
+
- The messages above this point are from the main thread prior to sub-agent execution. They are provided as context only.
|
|
2002
|
+
- Context messages may include tool_use blocks for tools that are not available in the sub-agent context. You should only use the tools specifically provided to you in the system prompt.
|
|
2003
|
+
- Only complete the specific sub-agent task you have been assigned below.`;
|
|
2004
|
+
function isToolUseLikeBlock(block) {
|
|
2005
|
+
if (!block || typeof block !== "object") return false;
|
|
2006
|
+
const type = block.type;
|
|
2007
|
+
if (type !== "tool_use" && type !== "server_tool_use" && type !== "mcp_tool_use") {
|
|
2008
|
+
return false;
|
|
2009
|
+
}
|
|
2010
|
+
const id = block.id;
|
|
2011
|
+
return typeof id === "string" && id.length > 0;
|
|
2012
|
+
}
|
|
2013
|
+
function isConversationMessage(value) {
|
|
2014
|
+
if (!value || typeof value !== "object") return false;
|
|
2015
|
+
const type = value.type;
|
|
2016
|
+
return type === "assistant" || type === "user" || type === "progress";
|
|
2017
|
+
}
|
|
2018
|
+
function readJsonArrayFile(path) {
|
|
2019
|
+
if (!existsSync3(path)) return null;
|
|
2020
|
+
try {
|
|
2021
|
+
const raw = readFileSync3(path, "utf8");
|
|
2022
|
+
const parsed = JSON.parse(raw);
|
|
2023
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
2024
|
+
} catch {
|
|
2025
|
+
return null;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
function createForkContextToolResult(options) {
|
|
2029
|
+
const toolResultBlock = {
|
|
2030
|
+
type: "tool_result",
|
|
2031
|
+
tool_use_id: options.toolUseId,
|
|
2032
|
+
content: FORK_CONTEXT_TOOL_RESULT_TEXT
|
|
2033
|
+
};
|
|
2034
|
+
const toolUseResult = {
|
|
2035
|
+
data: {
|
|
2036
|
+
status: "sub_agent_entered",
|
|
2037
|
+
description: "Entered sub-agent context",
|
|
2038
|
+
message: FORK_CONTEXT_TOOL_RESULT_TEXT
|
|
2039
|
+
},
|
|
2040
|
+
resultForAssistant: FORK_CONTEXT_TOOL_RESULT_TEXT
|
|
2041
|
+
};
|
|
2042
|
+
return createUserMessage([toolResultBlock], toolUseResult);
|
|
2043
|
+
}
|
|
2044
|
+
function createToolUseOnlyAssistantMessage(options) {
|
|
2045
|
+
return {
|
|
2046
|
+
...options.message,
|
|
2047
|
+
uuid: randomUUID2(),
|
|
2048
|
+
message: {
|
|
2049
|
+
...options.message.message,
|
|
2050
|
+
content: [options.toolUseBlock]
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
function buildForkContextForAgent(options) {
|
|
2055
|
+
const userPromptMessage = createUserMessage(options.prompt);
|
|
2056
|
+
if (!options.enabled || !options.toolUseId) {
|
|
2057
|
+
return {
|
|
2058
|
+
forkContextMessages: [],
|
|
2059
|
+
promptMessages: [userPromptMessage]
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
const mainPath = getMessagesPath(
|
|
2063
|
+
options.messageLogName,
|
|
2064
|
+
options.forkNumber,
|
|
2065
|
+
0
|
|
2066
|
+
);
|
|
2067
|
+
const raw = readJsonArrayFile(mainPath);
|
|
2068
|
+
const mainMessages = (raw ?? []).filter(isConversationMessage);
|
|
2069
|
+
if (mainMessages.length === 0) {
|
|
2070
|
+
return {
|
|
2071
|
+
forkContextMessages: [],
|
|
2072
|
+
promptMessages: [userPromptMessage]
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
let toolUseMessageIndex = -1;
|
|
2076
|
+
let toolUseMessage = null;
|
|
2077
|
+
let taskToolUseBlock = null;
|
|
2078
|
+
for (let i = 0; i < mainMessages.length; i++) {
|
|
2079
|
+
const msg = mainMessages[i];
|
|
2080
|
+
if (msg.type !== "assistant") continue;
|
|
2081
|
+
const blocks = Array.isArray(msg.message?.content) ? msg.message.content : [];
|
|
2082
|
+
const match = blocks.find(
|
|
2083
|
+
(b) => isToolUseLikeBlock(b) && b.id === options.toolUseId
|
|
2084
|
+
);
|
|
2085
|
+
if (!match) continue;
|
|
2086
|
+
toolUseMessageIndex = i;
|
|
2087
|
+
toolUseMessage = msg;
|
|
2088
|
+
taskToolUseBlock = match;
|
|
2089
|
+
break;
|
|
2090
|
+
}
|
|
2091
|
+
if (toolUseMessageIndex === -1 || !toolUseMessage || !taskToolUseBlock) {
|
|
2092
|
+
return {
|
|
2093
|
+
forkContextMessages: [],
|
|
2094
|
+
promptMessages: [userPromptMessage]
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
const forkContextMessages = mainMessages.slice(0, toolUseMessageIndex) ?? [];
|
|
2098
|
+
const toolUseOnlyAssistant = createToolUseOnlyAssistantMessage({
|
|
2099
|
+
message: toolUseMessage,
|
|
2100
|
+
toolUseBlock: taskToolUseBlock
|
|
2101
|
+
});
|
|
2102
|
+
const forkContextToolResult = createForkContextToolResult({
|
|
2103
|
+
toolUseId: taskToolUseBlock.id
|
|
2104
|
+
});
|
|
2105
|
+
return {
|
|
2106
|
+
forkContextMessages,
|
|
2107
|
+
promptMessages: [
|
|
2108
|
+
toolUseOnlyAssistant,
|
|
2109
|
+
forkContextToolResult,
|
|
2110
|
+
userPromptMessage
|
|
2111
|
+
]
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
// packages/tools/src/tools/ai/TaskTool/models.ts
|
|
2116
|
+
function modelEnumToPointer(model) {
|
|
2117
|
+
if (!model) return void 0;
|
|
2118
|
+
switch (model) {
|
|
2119
|
+
case "haiku":
|
|
2120
|
+
return "quick";
|
|
2121
|
+
case "sonnet":
|
|
2122
|
+
return "task";
|
|
2123
|
+
case "opus":
|
|
2124
|
+
return "main";
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
function normalizeAgentModelName(model) {
|
|
2128
|
+
if (!model) return void 0;
|
|
2129
|
+
if (model === "inherit") return "inherit";
|
|
2130
|
+
if (model === "haiku" || model === "sonnet" || model === "opus") {
|
|
2131
|
+
return modelEnumToPointer(model);
|
|
2132
|
+
}
|
|
2133
|
+
return model;
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// packages/tools/src/tools/ai/TaskTool/toolSpec.ts
|
|
2137
|
+
function getToolNameFromSpec(spec) {
|
|
2138
|
+
const trimmed = spec.trim();
|
|
2139
|
+
if (!trimmed) return trimmed;
|
|
2140
|
+
const match = trimmed.match(/^([^(]+)\(([^)]+)\)$/);
|
|
2141
|
+
if (!match) return trimmed;
|
|
2142
|
+
const toolName = match[1]?.trim();
|
|
2143
|
+
const ruleContent = match[2]?.trim();
|
|
2144
|
+
if (!toolName || !ruleContent) return trimmed;
|
|
2145
|
+
return toolName;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
// packages/tools/src/tools/ai/TaskTool/permissions.ts
|
|
2149
|
+
function normalizeAgentPermissionMode(mode) {
|
|
2150
|
+
if (!mode) return void 0;
|
|
2151
|
+
if (mode === "delegate") return "default";
|
|
2152
|
+
if (mode === "default" || mode === "acceptEdits" || mode === "plan" || mode === "bypassPermissions" || mode === "dontAsk") {
|
|
2153
|
+
return mode;
|
|
2154
|
+
}
|
|
2155
|
+
return void 0;
|
|
2156
|
+
}
|
|
2157
|
+
function applyAgentPermissionMode(base, options) {
|
|
2158
|
+
if (!base) return base;
|
|
2159
|
+
if (!options.agentPermissionMode) return base;
|
|
2160
|
+
if (options.agentPermissionMode === "bypassPermissions" && (options.safeMode || base.isBypassPermissionsModeAvailable !== true)) {
|
|
2161
|
+
return { ...base, mode: "default" };
|
|
2162
|
+
}
|
|
2163
|
+
return { ...base, mode: options.agentPermissionMode };
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
// packages/tools/src/tools/ai/TaskTool/callBackground.ts
|
|
2167
|
+
import { last } from "lodash-es";
|
|
2168
|
+
|
|
2169
|
+
// packages/tools/src/tools/ai/TaskTool/assistantText.ts
|
|
2170
|
+
function asyncLaunchMessage(agentId) {
|
|
2171
|
+
const toolName = "TaskOutput";
|
|
2172
|
+
return `Async agent launched successfully.
|
|
2173
|
+
agentId: ${agentId} (This is an internal ID for your use, do not mention it to the user. Use this ID to retrieve results with ${toolName} when the agent finishes).
|
|
2174
|
+
The agent is currently working in the background. If you have other tasks you you should continue working on them now. Wait to call ${toolName} until either:
|
|
2175
|
+
- If you want to check on the agent's progress - call ${toolName} with block=false to get an immediate update on the agent's status
|
|
2176
|
+
- If you run out of things to do and the agent is still running - call ${toolName} with block=true to idle and wait for the agent's result (do not use block=true unless you completely run out of things to do as it will waste time).`;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
// packages/tools/src/tools/ai/TaskTool/callBackground.ts
|
|
2180
|
+
function isTextBlock(block) {
|
|
2181
|
+
return Boolean(block) && typeof block === "object" && block.type === "text" && typeof block.text === "string";
|
|
2182
|
+
}
|
|
2183
|
+
async function* callTaskToolBackground(input, prepared) {
|
|
2184
|
+
const bgAbortController = new AbortController();
|
|
2185
|
+
const taskRecord = {
|
|
2186
|
+
type: "async_agent",
|
|
2187
|
+
agentId: prepared.agentId,
|
|
2188
|
+
description: input.description,
|
|
2189
|
+
prompt: prepared.effectivePrompt,
|
|
2190
|
+
status: "running",
|
|
2191
|
+
startedAt: Date.now(),
|
|
2192
|
+
messages: [...prepared.transcriptMessages],
|
|
2193
|
+
abortController: bgAbortController,
|
|
2194
|
+
done: Promise.resolve()
|
|
2195
|
+
};
|
|
2196
|
+
taskRecord.done = (async () => {
|
|
2197
|
+
try {
|
|
2198
|
+
const bgMessages = [...prepared.messagesForQuery];
|
|
2199
|
+
const bgTranscriptMessages = [
|
|
2200
|
+
...prepared.transcriptMessages
|
|
2201
|
+
];
|
|
2202
|
+
for await (const msg of prepared.queryFn(
|
|
2203
|
+
bgMessages,
|
|
2204
|
+
prepared.systemPrompt,
|
|
2205
|
+
prepared.context,
|
|
2206
|
+
hasPermissionsToUseTool,
|
|
2207
|
+
{
|
|
2208
|
+
abortController: bgAbortController,
|
|
2209
|
+
options: prepared.queryOptions,
|
|
2210
|
+
messageId: getLastAssistantMessageId(bgMessages),
|
|
2211
|
+
agentId: prepared.agentId,
|
|
2212
|
+
readFileTimestamps: prepared.readFileTimestamps,
|
|
2213
|
+
setToolJSX: () => {
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
)) {
|
|
2217
|
+
bgMessages.push(msg);
|
|
2218
|
+
bgTranscriptMessages.push(msg);
|
|
2219
|
+
taskRecord.messages = [...bgTranscriptMessages];
|
|
2220
|
+
upsertBackgroundAgentTask(taskRecord);
|
|
2221
|
+
}
|
|
2222
|
+
const lastAssistant = last(
|
|
2223
|
+
bgTranscriptMessages.filter((m) => m.type === "assistant")
|
|
2224
|
+
);
|
|
2225
|
+
const content = lastAssistant?.type === "assistant" ? lastAssistant.message.content.filter(isTextBlock) : [];
|
|
2226
|
+
taskRecord.status = "completed";
|
|
2227
|
+
taskRecord.completedAt = Date.now();
|
|
2228
|
+
taskRecord.resultText = content.map((b) => b.text).join("\n");
|
|
2229
|
+
taskRecord.messages = [...bgTranscriptMessages];
|
|
2230
|
+
upsertBackgroundAgentTask(taskRecord);
|
|
2231
|
+
saveAgentTranscript(prepared.agentId, bgTranscriptMessages);
|
|
2232
|
+
} catch (e) {
|
|
2233
|
+
taskRecord.status = "failed";
|
|
2234
|
+
taskRecord.completedAt = Date.now();
|
|
2235
|
+
taskRecord.error = e instanceof Error ? e.message : String(e);
|
|
2236
|
+
upsertBackgroundAgentTask(taskRecord);
|
|
2237
|
+
}
|
|
2238
|
+
})();
|
|
2239
|
+
upsertBackgroundAgentTask(taskRecord);
|
|
2240
|
+
const output = {
|
|
2241
|
+
status: "async_launched",
|
|
2242
|
+
agentId: prepared.agentId,
|
|
2243
|
+
description: input.description,
|
|
2244
|
+
prompt: prepared.effectivePrompt
|
|
2245
|
+
};
|
|
2246
|
+
yield {
|
|
2247
|
+
type: "result",
|
|
2248
|
+
data: output,
|
|
2249
|
+
resultForAssistant: asyncLaunchMessage(prepared.agentId)
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
// packages/tools/src/tools/ai/TaskTool/callForeground.ts
|
|
2254
|
+
import { last as last2, memoize } from "lodash-es";
|
|
2255
|
+
function isTextBlock2(block) {
|
|
2256
|
+
return Boolean(block) && typeof block === "object" && block.type === "text" && typeof block.text === "string";
|
|
2257
|
+
}
|
|
2258
|
+
function isToolUseLikeBlock2(block) {
|
|
2259
|
+
if (!block || typeof block !== "object") return false;
|
|
2260
|
+
const type = block.type;
|
|
2261
|
+
if (type !== "tool_use" && type !== "server_tool_use" && type !== "mcp_tool_use") {
|
|
2262
|
+
return false;
|
|
2263
|
+
}
|
|
2264
|
+
const name = block.name;
|
|
2265
|
+
return typeof name === "string";
|
|
2266
|
+
}
|
|
2267
|
+
function truncate(text, maxLen) {
|
|
2268
|
+
const normalized = text.replace(/\\s+/g, " ").trim();
|
|
2269
|
+
if (normalized.length <= maxLen) return normalized;
|
|
2270
|
+
return `${normalized.slice(0, maxLen - 1)}\u2026`;
|
|
2271
|
+
}
|
|
2272
|
+
function summarizeToolUse(name, rawInput) {
|
|
2273
|
+
const input = rawInput && typeof rawInput === "object" ? rawInput : {};
|
|
2274
|
+
switch (name) {
|
|
2275
|
+
case "Read": {
|
|
2276
|
+
const filePath = typeof input.file_path === "string" && input.file_path || typeof input.path === "string" && input.path || "";
|
|
2277
|
+
return filePath ? `Read ${filePath}` : "Read";
|
|
2278
|
+
}
|
|
2279
|
+
case "Write": {
|
|
2280
|
+
const filePath = typeof input.file_path === "string" && input.file_path || typeof input.path === "string" && input.path || "";
|
|
2281
|
+
return filePath ? `Write ${filePath}` : "Write";
|
|
2282
|
+
}
|
|
2283
|
+
case "Edit":
|
|
2284
|
+
case "MultiEdit": {
|
|
2285
|
+
const filePath = typeof input.file_path === "string" && input.file_path || typeof input.path === "string" && input.path || "";
|
|
2286
|
+
return filePath ? `${name} ${filePath}` : name;
|
|
2287
|
+
}
|
|
2288
|
+
case "Grep": {
|
|
2289
|
+
const pattern = typeof input.pattern === "string" ? input.pattern : "";
|
|
2290
|
+
return pattern ? `Grep ${truncate(pattern, 80)}` : "Grep";
|
|
2291
|
+
}
|
|
2292
|
+
case "Glob": {
|
|
2293
|
+
const pattern = typeof input.pattern === "string" && input.pattern || typeof input.glob === "string" && input.glob || "";
|
|
2294
|
+
return pattern ? `Glob ${truncate(pattern, 80)}` : "Glob";
|
|
2295
|
+
}
|
|
2296
|
+
case "Bash": {
|
|
2297
|
+
const command = typeof input.command === "string" ? input.command : "";
|
|
2298
|
+
return command ? `Bash ${truncate(command, 80)}` : "Bash";
|
|
2299
|
+
}
|
|
2300
|
+
case "WebFetch":
|
|
2301
|
+
case "WebSearch": {
|
|
2302
|
+
const url = typeof input.url === "string" ? input.url : "";
|
|
2303
|
+
const query2 = typeof input.query === "string" ? input.query : "";
|
|
2304
|
+
if (url) return `${name} ${truncate(url, 100)}`;
|
|
2305
|
+
if (query2) return `${name} ${truncate(query2, 100)}`;
|
|
2306
|
+
return name;
|
|
2307
|
+
}
|
|
2308
|
+
default:
|
|
2309
|
+
return name;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
async function* callTaskToolForeground(input, prepared) {
|
|
2313
|
+
const getSidechainNumber = memoize(
|
|
2314
|
+
() => getNextAvailableLogSidechainNumber(
|
|
2315
|
+
prepared.messageLogName,
|
|
2316
|
+
prepared.forkNumber
|
|
2317
|
+
)
|
|
2318
|
+
);
|
|
2319
|
+
const PROGRESS_THROTTLE_MS = 200;
|
|
2320
|
+
const MAX_RECENT_ACTIONS = 6;
|
|
2321
|
+
let lastProgressEmitAt = 0;
|
|
2322
|
+
let lastEmittedToolUseCount = 0;
|
|
2323
|
+
const recentActions = [];
|
|
2324
|
+
const addRecentAction = (action) => {
|
|
2325
|
+
const trimmed = action.trim();
|
|
2326
|
+
if (!trimmed) return;
|
|
2327
|
+
recentActions.push(trimmed);
|
|
2328
|
+
if (recentActions.length > MAX_RECENT_ACTIONS) {
|
|
2329
|
+
recentActions.splice(0, recentActions.length - MAX_RECENT_ACTIONS);
|
|
2330
|
+
}
|
|
2331
|
+
};
|
|
2332
|
+
const renderProgressText = (toolUseCount2) => {
|
|
2333
|
+
const header = `${input.description || "Task"}\u2026 (${toolUseCount2} tool${toolUseCount2 === 1 ? "" : "s"})`;
|
|
2334
|
+
if (recentActions.length === 0) return header;
|
|
2335
|
+
const lines = recentActions.map((a) => `- ${a}`);
|
|
2336
|
+
return [header, ...lines].join("\\n");
|
|
2337
|
+
};
|
|
2338
|
+
yield {
|
|
2339
|
+
type: "progress",
|
|
2340
|
+
content: createAssistantMessage(
|
|
2341
|
+
`<tool-progress>${renderProgressText(0)}</tool-progress>`
|
|
2342
|
+
)
|
|
2343
|
+
};
|
|
2344
|
+
lastProgressEmitAt = Date.now();
|
|
2345
|
+
let toolUseCount = 0;
|
|
2346
|
+
for await (const message of prepared.queryFn(
|
|
2347
|
+
prepared.messagesForQuery,
|
|
2348
|
+
prepared.systemPrompt,
|
|
2349
|
+
prepared.context,
|
|
2350
|
+
hasPermissionsToUseTool,
|
|
2351
|
+
{
|
|
2352
|
+
abortController: prepared.abortController,
|
|
2353
|
+
options: prepared.queryOptions,
|
|
2354
|
+
messageId: getLastAssistantMessageId(prepared.messagesForQuery),
|
|
2355
|
+
agentId: prepared.agentId,
|
|
2356
|
+
readFileTimestamps: prepared.readFileTimestamps,
|
|
2357
|
+
setToolJSX: () => {
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
)) {
|
|
2361
|
+
prepared.messagesForQuery.push(message);
|
|
2362
|
+
prepared.transcriptMessages.push(message);
|
|
2363
|
+
overwriteLog(
|
|
2364
|
+
getMessagesPath(
|
|
2365
|
+
prepared.messageLogName,
|
|
2366
|
+
prepared.forkNumber,
|
|
2367
|
+
getSidechainNumber()
|
|
2368
|
+
),
|
|
2369
|
+
prepared.transcriptMessages.filter((m) => m.type !== "progress"),
|
|
2370
|
+
{ conversationKey: `${prepared.messageLogName}:${prepared.forkNumber}` }
|
|
2371
|
+
);
|
|
2372
|
+
if (message.type === "assistant") {
|
|
2373
|
+
for (const block of message.message.content) {
|
|
2374
|
+
if (!isToolUseLikeBlock2(block)) continue;
|
|
2375
|
+
toolUseCount += 1;
|
|
2376
|
+
addRecentAction(summarizeToolUse(block.name, block.input));
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
const now = Date.now();
|
|
2380
|
+
const hasNewToolUses = toolUseCount > lastEmittedToolUseCount;
|
|
2381
|
+
const shouldEmit = hasNewToolUses && (lastEmittedToolUseCount === 0 || now - lastProgressEmitAt >= PROGRESS_THROTTLE_MS);
|
|
2382
|
+
if (shouldEmit) {
|
|
2383
|
+
yield {
|
|
2384
|
+
type: "progress",
|
|
2385
|
+
content: createAssistantMessage(
|
|
2386
|
+
`<tool-progress>${renderProgressText(toolUseCount)}</tool-progress>`
|
|
2387
|
+
)
|
|
2388
|
+
};
|
|
2389
|
+
lastEmittedToolUseCount = toolUseCount;
|
|
2390
|
+
lastProgressEmitAt = now;
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
const lastAssistant = last2(
|
|
2394
|
+
prepared.transcriptMessages.filter((m) => m.type === "assistant")
|
|
2395
|
+
);
|
|
2396
|
+
if (!lastAssistant || lastAssistant.type !== "assistant") {
|
|
2397
|
+
throw new Error("No assistant messages found");
|
|
2398
|
+
}
|
|
2399
|
+
const content = lastAssistant.message.content.filter(isTextBlock2);
|
|
2400
|
+
const totalDurationMs = Date.now() - prepared.startTime;
|
|
2401
|
+
const totalTokens = countTokens(prepared.transcriptMessages);
|
|
2402
|
+
const usage = lastAssistant.message.usage;
|
|
2403
|
+
const output = {
|
|
2404
|
+
status: "completed",
|
|
2405
|
+
agentId: prepared.agentId,
|
|
2406
|
+
prompt: prepared.effectivePrompt,
|
|
2407
|
+
content,
|
|
2408
|
+
totalToolUseCount: toolUseCount,
|
|
2409
|
+
totalDurationMs,
|
|
2410
|
+
totalTokens,
|
|
2411
|
+
usage
|
|
2412
|
+
};
|
|
2413
|
+
const agentIdBlock = {
|
|
2414
|
+
type: "text",
|
|
2415
|
+
text: `agentId: ${prepared.agentId} (for resuming to continue this agent's work if needed)`,
|
|
2416
|
+
citations: []
|
|
2417
|
+
};
|
|
2418
|
+
yield {
|
|
2419
|
+
type: "result",
|
|
2420
|
+
data: output,
|
|
2421
|
+
resultForAssistant: [...content, agentIdBlock]
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
// packages/tools/src/tools/ai/TaskTool/call.ts
|
|
2426
|
+
async function* callTaskTool(input, toolUseContext) {
|
|
2427
|
+
const startTime = Date.now();
|
|
2428
|
+
const options = toolUseContext.options ?? {};
|
|
2429
|
+
const safeMode = options.safeMode ?? false;
|
|
2430
|
+
const forkNumber = options.forkNumber ?? 0;
|
|
2431
|
+
const messageLogName = options.messageLogName ?? "default";
|
|
2432
|
+
const verbose = options.verbose ?? false;
|
|
2433
|
+
const parentModel = options.model;
|
|
2434
|
+
const queryFn = typeof toolUseContext.__testQuery === "function" ? toolUseContext.__testQuery : query;
|
|
2435
|
+
const agentConfig = await getAgentByType(input.subagent_type);
|
|
2436
|
+
if (!agentConfig) {
|
|
2437
|
+
const available = await getAvailableAgentTypes();
|
|
2438
|
+
throw new Error(
|
|
2439
|
+
`Agent type '${input.subagent_type}' not found. Available agents: ${available.join(", ")}`
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
const effectivePrompt = input.prompt;
|
|
2443
|
+
const normalizedAgentModel = normalizeAgentModelName(agentConfig.model);
|
|
2444
|
+
const defaultSubagentModel = "task";
|
|
2445
|
+
const envSubagentModel = process.env.KODE_SUBAGENT_MODEL ?? process.env.CLAUDE_CODE_SUBAGENT_MODEL;
|
|
2446
|
+
const modelToUse = (typeof envSubagentModel === "string" && envSubagentModel.trim() ? envSubagentModel.trim() : void 0) || modelEnumToPointer(input.model) || (normalizedAgentModel === "inherit" ? parentModel || defaultSubagentModel : normalizedAgentModel) || defaultSubagentModel;
|
|
2447
|
+
const toolFilter = agentConfig.tools;
|
|
2448
|
+
let tools = await getTaskTools(safeMode);
|
|
2449
|
+
if (toolFilter) {
|
|
2450
|
+
const isAllArray = Array.isArray(toolFilter) && toolFilter.length === 1 && toolFilter[0] === "*";
|
|
2451
|
+
if (toolFilter === "*" || isAllArray) {
|
|
2452
|
+
} else if (Array.isArray(toolFilter)) {
|
|
2453
|
+
const allowedToolNames = new Set(
|
|
2454
|
+
toolFilter.map(getToolNameFromSpec).filter(Boolean)
|
|
2455
|
+
);
|
|
2456
|
+
tools = tools.filter((t) => allowedToolNames.has(t.name));
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
const disallowedTools = Array.isArray(agentConfig.disallowedTools) ? agentConfig.disallowedTools : [];
|
|
2460
|
+
if (disallowedTools.length > 0) {
|
|
2461
|
+
const disallowedToolNames = new Set(
|
|
2462
|
+
disallowedTools.map(getToolNameFromSpec).filter(Boolean)
|
|
2463
|
+
);
|
|
2464
|
+
tools = tools.filter((t) => !disallowedToolNames.has(t.name));
|
|
2465
|
+
}
|
|
2466
|
+
const agentId = input.resume || generateAgentId();
|
|
2467
|
+
const baseTranscript = input.resume ? getAgentTranscript(input.resume)?.filter((m) => m.type !== "progress") ?? null : [];
|
|
2468
|
+
if (input.resume && baseTranscript === null) {
|
|
2469
|
+
throw new Error(`No transcript found for agent ID: ${input.resume}`);
|
|
2470
|
+
}
|
|
2471
|
+
const { forkContextMessages, promptMessages } = buildForkContextForAgent({
|
|
2472
|
+
enabled: agentConfig.forkContext === true,
|
|
2473
|
+
prompt: effectivePrompt,
|
|
2474
|
+
toolUseId: toolUseContext.toolUseId,
|
|
2475
|
+
messageLogName,
|
|
2476
|
+
forkNumber
|
|
2477
|
+
});
|
|
2478
|
+
const transcriptMessages = [...baseTranscript || [], ...promptMessages];
|
|
2479
|
+
const messagesForQuery = [...forkContextMessages, ...transcriptMessages];
|
|
2480
|
+
const [baseSystemPrompt, context, maxThinkingTokens] = await Promise.all([
|
|
2481
|
+
getAgentPrompt(),
|
|
2482
|
+
getContext(),
|
|
2483
|
+
getMaxThinkingTokens(messagesForQuery)
|
|
2484
|
+
]);
|
|
2485
|
+
const systemPrompt = agentConfig.systemPrompt && agentConfig.systemPrompt.length > 0 ? [...baseSystemPrompt, agentConfig.systemPrompt] : baseSystemPrompt;
|
|
2486
|
+
const agentPermissionMode = normalizeAgentPermissionMode(
|
|
2487
|
+
agentConfig.permissionMode
|
|
2488
|
+
);
|
|
2489
|
+
const toolPermissionContext = applyAgentPermissionMode(
|
|
2490
|
+
options.toolPermissionContext,
|
|
2491
|
+
{
|
|
2492
|
+
agentPermissionMode,
|
|
2493
|
+
safeMode
|
|
2494
|
+
}
|
|
2495
|
+
);
|
|
2496
|
+
const queryOptions = {
|
|
2497
|
+
safeMode,
|
|
2498
|
+
forkNumber,
|
|
2499
|
+
messageLogName,
|
|
2500
|
+
tools,
|
|
2501
|
+
commands: [],
|
|
2502
|
+
verbose,
|
|
2503
|
+
permissionMode: "dontAsk",
|
|
2504
|
+
toolPermissionContext,
|
|
2505
|
+
maxThinkingTokens,
|
|
2506
|
+
model: modelToUse,
|
|
2507
|
+
mcpClients: options.mcpClients
|
|
2508
|
+
};
|
|
2509
|
+
const prepared = {
|
|
2510
|
+
queryFn,
|
|
2511
|
+
agentId,
|
|
2512
|
+
effectivePrompt,
|
|
2513
|
+
systemPrompt,
|
|
2514
|
+
context,
|
|
2515
|
+
messagesForQuery,
|
|
2516
|
+
transcriptMessages,
|
|
2517
|
+
queryOptions,
|
|
2518
|
+
messageLogName,
|
|
2519
|
+
forkNumber,
|
|
2520
|
+
abortController: toolUseContext.abortController,
|
|
2521
|
+
readFileTimestamps: toolUseContext.readFileTimestamps,
|
|
2522
|
+
startTime
|
|
2523
|
+
};
|
|
2524
|
+
if (input.run_in_background) {
|
|
2525
|
+
yield* callTaskToolBackground(input, prepared);
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
for await (const chunk of callTaskToolForeground(input, prepared)) {
|
|
2529
|
+
if (chunk.type === "result") {
|
|
2530
|
+
saveAgentTranscript(prepared.agentId, prepared.transcriptMessages);
|
|
2531
|
+
}
|
|
2532
|
+
yield chunk;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
// packages/tools/src/tools/ai/TaskTool/schema.ts
|
|
2537
|
+
import { z as z8 } from "zod";
|
|
2538
|
+
var inputSchema8 = z8.object({
|
|
2539
|
+
description: z8.string().describe("A short (3-5 word) description of the task"),
|
|
2540
|
+
prompt: z8.string().describe("The task for the agent to perform"),
|
|
2541
|
+
subagent_type: z8.string().describe("The type of specialized agent to use for this task"),
|
|
2542
|
+
model: z8.enum(["sonnet", "opus", "haiku"]).optional().describe(
|
|
2543
|
+
"Optional model to use for this agent. If not specified, inherits from parent. Prefer haiku for quick, straightforward tasks to minimize cost and latency."
|
|
2544
|
+
),
|
|
2545
|
+
resume: z8.string().optional().describe(
|
|
2546
|
+
"Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript."
|
|
2547
|
+
),
|
|
2548
|
+
run_in_background: z8.boolean().optional().describe(
|
|
2549
|
+
"Set to true to run this agent in the background. Use TaskOutput to read the output later."
|
|
2550
|
+
)
|
|
2551
|
+
});
|
|
2552
|
+
|
|
2553
|
+
// packages/tools/src/tools/ai/TaskTool/render.tsx
|
|
2554
|
+
import React7 from "react";
|
|
2555
|
+
import { Box as Box6, Text as Text7 } from "ink";
|
|
2556
|
+
function renderTaskToolUseMessage(input) {
|
|
2557
|
+
if (!input.description || !input.prompt) return "";
|
|
2558
|
+
return input.description;
|
|
2559
|
+
}
|
|
2560
|
+
function renderTaskToolResultMessage(output, options) {
|
|
2561
|
+
const theme = getTheme();
|
|
2562
|
+
if (output.status === "async_launched") {
|
|
2563
|
+
const hint = output.prompt ? " (down arrow \u2193 to manage \xB7 ctrl+o to expand)" : " (down arrow \u2193 to manage)";
|
|
2564
|
+
return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "row" }, /* @__PURE__ */ React7.createElement(Text7, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React7.createElement(Text7, null, "Backgrounded agent", !options.verbose && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, hint))), options.verbose && output.prompt && /* @__PURE__ */ React7.createElement(
|
|
2565
|
+
Box6,
|
|
2566
|
+
{
|
|
2567
|
+
paddingLeft: 2,
|
|
2568
|
+
borderStyle: "single",
|
|
2569
|
+
borderLeft: true,
|
|
2570
|
+
borderTop: false,
|
|
2571
|
+
borderRight: false,
|
|
2572
|
+
borderBottom: false,
|
|
2573
|
+
borderLeftColor: theme.secondaryBorder
|
|
2574
|
+
},
|
|
2575
|
+
/* @__PURE__ */ React7.createElement(Text7, { color: theme.secondaryText, wrap: "wrap" }, output.prompt)
|
|
2576
|
+
));
|
|
2577
|
+
}
|
|
2578
|
+
const summary = [
|
|
2579
|
+
output.totalToolUseCount === 1 ? "1 tool use" : `${output.totalToolUseCount} tool uses`,
|
|
2580
|
+
`${formatNumber(output.totalTokens)} tokens`,
|
|
2581
|
+
formatDuration(output.totalDurationMs)
|
|
2582
|
+
];
|
|
2583
|
+
return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column" }, options.verbose && output.prompt && /* @__PURE__ */ React7.createElement(
|
|
2584
|
+
Box6,
|
|
2585
|
+
{
|
|
2586
|
+
paddingLeft: 2,
|
|
2587
|
+
borderStyle: "single",
|
|
2588
|
+
borderLeft: true,
|
|
2589
|
+
borderTop: false,
|
|
2590
|
+
borderRight: false,
|
|
2591
|
+
borderBottom: false,
|
|
2592
|
+
borderLeftColor: theme.secondaryBorder
|
|
2593
|
+
},
|
|
2594
|
+
/* @__PURE__ */ React7.createElement(Text7, { color: theme.secondaryText, wrap: "wrap" }, maybeTruncateVerboseToolOutput(output.prompt, {
|
|
2595
|
+
maxLines: 120,
|
|
2596
|
+
maxChars: 2e4
|
|
2597
|
+
}).text)
|
|
2598
|
+
), options.verbose && output.content.length > 0 && /* @__PURE__ */ React7.createElement(
|
|
2599
|
+
Box6,
|
|
2600
|
+
{
|
|
2601
|
+
paddingLeft: 2,
|
|
2602
|
+
borderStyle: "single",
|
|
2603
|
+
borderLeft: true,
|
|
2604
|
+
borderTop: false,
|
|
2605
|
+
borderRight: false,
|
|
2606
|
+
borderBottom: false,
|
|
2607
|
+
borderLeftColor: theme.secondaryBorder
|
|
2608
|
+
},
|
|
2609
|
+
/* @__PURE__ */ React7.createElement(Text7, { wrap: "wrap" }, maybeTruncateVerboseToolOutput(
|
|
2610
|
+
output.content.map((b) => b.text).join("\n"),
|
|
2611
|
+
{ maxLines: 200, maxChars: 4e4 }
|
|
2612
|
+
).text)
|
|
2613
|
+
), /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "row" }, /* @__PURE__ */ React7.createElement(Text7, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Done (", summary.join(" \xB7 "), ")")));
|
|
2614
|
+
}
|
|
2615
|
+
function renderTaskToolResultForAssistant(output) {
|
|
2616
|
+
if (output.status === "async_launched")
|
|
2617
|
+
return asyncLaunchMessage(output.agentId);
|
|
2618
|
+
return output.content.map((b) => b.text).join("\n");
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
// packages/tools/src/tools/ai/TaskTool/TaskTool.tsx
|
|
2622
|
+
var TaskTool = {
|
|
2623
|
+
name: TOOL_NAME3,
|
|
2624
|
+
inputSchema: inputSchema8,
|
|
2625
|
+
async description() {
|
|
2626
|
+
return "Launch a new task";
|
|
2627
|
+
},
|
|
2628
|
+
async prompt({ safeMode }) {
|
|
2629
|
+
return await getPrompt(safeMode);
|
|
2630
|
+
},
|
|
2631
|
+
userFacingName(input) {
|
|
2632
|
+
if (input?.subagent_type && input.subagent_type !== "general-purpose") {
|
|
2633
|
+
return input.subagent_type;
|
|
2634
|
+
}
|
|
2635
|
+
return "Task";
|
|
2636
|
+
},
|
|
2637
|
+
async isEnabled() {
|
|
2638
|
+
return true;
|
|
2639
|
+
},
|
|
2640
|
+
isReadOnly() {
|
|
2641
|
+
return true;
|
|
2642
|
+
},
|
|
2643
|
+
isConcurrencySafe() {
|
|
2644
|
+
return true;
|
|
2645
|
+
},
|
|
2646
|
+
needsPermissions() {
|
|
2647
|
+
return false;
|
|
2648
|
+
},
|
|
2649
|
+
async validateInput(input) {
|
|
2650
|
+
if (!input.description || typeof input.description !== "string") {
|
|
2651
|
+
return {
|
|
2652
|
+
result: false,
|
|
2653
|
+
message: "Description is required and must be a string"
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
if (!input.prompt || typeof input.prompt !== "string") {
|
|
2657
|
+
return {
|
|
2658
|
+
result: false,
|
|
2659
|
+
message: "Prompt is required and must be a string"
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
const availableTypes = await getAvailableAgentTypes();
|
|
2663
|
+
if (!availableTypes.includes(input.subagent_type)) {
|
|
2664
|
+
return {
|
|
2665
|
+
result: false,
|
|
2666
|
+
message: `Agent type '${input.subagent_type}' not found. Available agents: ${availableTypes.join(", ")}`,
|
|
2667
|
+
meta: { subagent_type: input.subagent_type, availableTypes }
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2670
|
+
if (input.resume) {
|
|
2671
|
+
const transcript = getAgentTranscript(input.resume);
|
|
2672
|
+
if (!transcript) {
|
|
2673
|
+
return {
|
|
2674
|
+
result: false,
|
|
2675
|
+
message: `No transcript found for agent ID: ${input.resume}`,
|
|
2676
|
+
meta: { resume: input.resume }
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
return { result: true };
|
|
2681
|
+
},
|
|
2682
|
+
renderToolUseMessage: renderTaskToolUseMessage,
|
|
2683
|
+
renderToolResultMessage: renderTaskToolResultMessage,
|
|
2684
|
+
renderResultForAssistant: renderTaskToolResultForAssistant,
|
|
2685
|
+
call: callTaskTool
|
|
2686
|
+
};
|
|
2687
|
+
|
|
2688
|
+
// packages/tools/src/tools/interaction/TodoWriteTool/TodoWriteTool.tsx
|
|
2689
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2690
|
+
import { z as z9 } from "zod";
|
|
2691
|
+
|
|
2692
|
+
// packages/tools/src/tools/interaction/TodoWriteTool/prompt.ts
|
|
2693
|
+
var DESCRIPTION8 = "Update the todo list for the current session. To be used proactively and often to track progress and pending tasks. Make sure that at least one task is in_progress at all times. Always provide both content (imperative) and activeForm (present continuous) for each task.";
|
|
2694
|
+
var PROMPT8 = `Use this tool to create and manage a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
|
|
2695
|
+
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
2696
|
+
|
|
2697
|
+
## When to Use This Tool
|
|
2698
|
+
Use this tool proactively in these scenarios:
|
|
2699
|
+
|
|
2700
|
+
1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
2701
|
+
2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
|
|
2702
|
+
3. User explicitly requests todo list - When the user directly asks you to use the todo list
|
|
2703
|
+
4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
2704
|
+
5. After receiving new instructions - Immediately capture user requirements as todos
|
|
2705
|
+
6. When you start working on a task - Mark it as in_progress BEFORE beginning work. Ideally you should only have one todo as in_progress at a time
|
|
2706
|
+
7. After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
|
|
2707
|
+
|
|
2708
|
+
## When NOT to Use This Tool
|
|
2709
|
+
|
|
2710
|
+
Skip using this tool when:
|
|
2711
|
+
1. There is only a single, straightforward task
|
|
2712
|
+
2. The task is trivial and tracking it provides no organizational benefit
|
|
2713
|
+
3. The task can be completed in less than 3 trivial steps
|
|
2714
|
+
4. The task is purely conversational or informational
|
|
2715
|
+
|
|
2716
|
+
NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.
|
|
2717
|
+
|
|
2718
|
+
## Examples of When to Use the Todo List
|
|
2719
|
+
|
|
2720
|
+
<example>
|
|
2721
|
+
User: I want to add a dark mode toggle to the application settings. Make sure you run the tests and build when you're done!
|
|
2722
|
+
Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.
|
|
2723
|
+
*Creates todo list with the following items:*
|
|
2724
|
+
1. Creating dark mode toggle component in Settings page
|
|
2725
|
+
2. Adding dark mode state management (context/store)
|
|
2726
|
+
3. Implementing CSS-in-JS styles for dark theme
|
|
2727
|
+
4. Updating existing components to support theme switching
|
|
2728
|
+
5. Running tests and build process, addressing any failures or errors that occur
|
|
2729
|
+
*Begins working on the first task*
|
|
2730
|
+
|
|
2731
|
+
<reasoning>
|
|
2732
|
+
The assistant used the todo list because:
|
|
2733
|
+
1. Adding dark mode is a multi-step feature requiring UI, state management, and styling changes
|
|
2734
|
+
2. The user explicitly requested tests and build be run afterward
|
|
2735
|
+
3. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task
|
|
2736
|
+
</reasoning>
|
|
2737
|
+
</example>
|
|
2738
|
+
|
|
2739
|
+
<example>
|
|
2740
|
+
User: Help me rename the function getCwd to getCurrentWorkingDirectory across my project
|
|
2741
|
+
Assistant: Let me first search through your codebase to find all occurrences of 'getCwd'.
|
|
2742
|
+
*Uses grep or search tools to locate all instances of getCwd in the codebase*
|
|
2743
|
+
Assistant: I've found 15 instances of 'getCwd' across 8 different files. Let me create a todo list to track these changes.
|
|
2744
|
+
*Creates todo list with specific items for each file that needs updating*
|
|
2745
|
+
|
|
2746
|
+
<reasoning>
|
|
2747
|
+
The assistant used the todo list because:
|
|
2748
|
+
1. First, the assistant searched to understand the scope of the task
|
|
2749
|
+
2. Upon finding multiple occurrences across different files, it determined this was a complex task with multiple steps
|
|
2750
|
+
3. The todo list helps ensure every instance is tracked and updated systematically
|
|
2751
|
+
4. This approach prevents missing any occurrences and maintains code consistency
|
|
2752
|
+
</reasoning>
|
|
2753
|
+
</example>
|
|
2754
|
+
|
|
2755
|
+
|
|
2756
|
+
<example>
|
|
2757
|
+
User: I need to implement these features for my e-commerce site: user registration, product catalog, shopping cart, and checkout flow.
|
|
2758
|
+
Assistant: I'll help implement these features. First, let's add all the features to the todo list.
|
|
2759
|
+
*Creates a todo list breaking down each feature into specific tasks based on the project architecture*
|
|
2760
|
+
Assistant: Let's start with user registration. This will involve creating database models, API endpoints, and frontend forms that integrate with your existing authentication system.
|
|
2761
|
+
|
|
2762
|
+
<reasoning>
|
|
2763
|
+
The assistant used the todo list because:
|
|
2764
|
+
1. The user provided multiple complex features to implement in a comma separated list
|
|
2765
|
+
2. The todo list helps organize these large features into manageable tasks
|
|
2766
|
+
3. This approach allows for tracking progress across the entire implementation
|
|
2767
|
+
</reasoning>
|
|
2768
|
+
</example>
|
|
2769
|
+
|
|
2770
|
+
<example>
|
|
2771
|
+
User: Can you help optimize my React application? It's rendering slowly and has performance issues.</user>
|
|
2772
|
+
Assistant: I'll help optimize your React application. First, let me examine your codebase to identify potential performance bottlenecks.
|
|
2773
|
+
*Reviews component structure, render patterns, state management, and data fetching*
|
|
2774
|
+
Assistant: After analyzing your codebase, I've identified several performance issues. Let me create a todo list to track our optimization efforts.
|
|
2775
|
+
*Creates todo list with items like: 1) Implementing memoization for expensive calculations in ProductList, 2) Adding virtualization for long lists in Dashboard, 3) Optimizing image loading in Gallery component, 4) Fixing state update loops in ShoppingCart, 5) Reviewing bundle size and implementing code splitting*
|
|
2776
|
+
Let's start by implementing memoization for the expensive calculations in your ProductList component.</assistant>
|
|
2777
|
+
|
|
2778
|
+
<reasoning>
|
|
2779
|
+
The assistant used the todo list because:
|
|
2780
|
+
1. First, the assistant examined the codebase to identify specific performance issues
|
|
2781
|
+
2. Based on this analysis, it identified multiple optimization opportunities
|
|
2782
|
+
3. Performance optimization is a non-trivial task requiring multiple steps
|
|
2783
|
+
4. The todo list helps methodically track improvements across different components
|
|
2784
|
+
5. This systematic approach ensures all performance bottlenecks are addressed
|
|
2785
|
+
</reasoning>
|
|
2786
|
+
</example>
|
|
2787
|
+
|
|
2788
|
+
## Examples of When NOT to Use the Todo List
|
|
2789
|
+
|
|
2790
|
+
<example>
|
|
2791
|
+
User: How do I print 'Hello World' in Python?
|
|
2792
|
+
Assistant: In Python, you can print "Hello World" with this simple code:
|
|
2793
|
+
|
|
2794
|
+
python
|
|
2795
|
+
print("Hello World")
|
|
2796
|
+
|
|
2797
|
+
This will output the text "Hello World" to the console when executed.</assistant>
|
|
2798
|
+
|
|
2799
|
+
<reasoning>
|
|
2800
|
+
The assistant did not use the todo list because this is a single, trivial task that can be completed in one step. There's no need to track multiple tasks or steps for such a straightforward request.
|
|
2801
|
+
</reasoning>
|
|
2802
|
+
</example>
|
|
2803
|
+
|
|
2804
|
+
<example>
|
|
2805
|
+
User: What does the git status command do?
|
|
2806
|
+
Assistant: The git status command shows the current state of your working directory and staging area. It displays which changes have been staged, which haven't, and which files aren't being tracked by Git. This is a fundamental command for understanding the current state of your repository before making commits.
|
|
2807
|
+
|
|
2808
|
+
<reasoning>
|
|
2809
|
+
The assistant did not use the todo list because this is an informational request with no actual coding task to complete. The user is simply asking for an explanation, not for the assistant to perform multiple steps or tasks.
|
|
2810
|
+
</reasoning>
|
|
2811
|
+
</example>
|
|
2812
|
+
|
|
2813
|
+
<example>
|
|
2814
|
+
User: Can you add a comment to the calculateTotal function to explain what it does?
|
|
2815
|
+
Assistant: Sure, let me add a comment to the calculateTotal function to explain what it does.
|
|
2816
|
+
* Uses the Edit tool to add a comment to the calculateTotal function *
|
|
2817
|
+
|
|
2818
|
+
<reasoning>
|
|
2819
|
+
The assistant did not use the todo list because this is a single, straightforward task confined to one location in the code. Adding a comment doesn't require tracking multiple steps or systematic organization.
|
|
2820
|
+
</reasoning>
|
|
2821
|
+
</example>
|
|
2822
|
+
|
|
2823
|
+
<example>
|
|
2824
|
+
User: Run npm install for me and tell me what happens.
|
|
2825
|
+
Assistant: I'll run the npm install command for you.
|
|
2826
|
+
|
|
2827
|
+
*Executes: npm install*
|
|
2828
|
+
|
|
2829
|
+
The command completed successfully. Here's the output:
|
|
2830
|
+
[Output of npm install command]
|
|
2831
|
+
|
|
2832
|
+
All dependencies have been installed according to your package.json file.
|
|
2833
|
+
|
|
2834
|
+
<reasoning>
|
|
2835
|
+
The assistant did not use the todo list because this is a single command execution with immediate results. There are no multiple steps to track or organize, making the todo list unnecessary for this straightforward task.
|
|
2836
|
+
</reasoning>
|
|
2837
|
+
</example>
|
|
2838
|
+
|
|
2839
|
+
## Task States and Management
|
|
2840
|
+
|
|
2841
|
+
1. **Task States**: Use these states to track progress:
|
|
2842
|
+
- pending: Task not yet started
|
|
2843
|
+
- in_progress: Currently working on (limit to ONE task at a time)
|
|
2844
|
+
- completed: Task finished successfully
|
|
2845
|
+
|
|
2846
|
+
**IMPORTANT**: Task descriptions must have two forms:
|
|
2847
|
+
- content: The imperative form describing what needs to be done (e.g., "Run tests", "Build the project")
|
|
2848
|
+
- activeForm: The present continuous form shown during execution (e.g., "Running tests", "Building the project")
|
|
2849
|
+
|
|
2850
|
+
2. **Task Management**:
|
|
2851
|
+
- Update task status in real-time as you work
|
|
2852
|
+
- Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
|
|
2853
|
+
- Exactly ONE task must be in_progress at any time (not less, not more)
|
|
2854
|
+
- Complete current tasks before starting new ones
|
|
2855
|
+
- Remove tasks that are no longer relevant from the list entirely
|
|
2856
|
+
|
|
2857
|
+
3. **Task Completion Requirements**:
|
|
2858
|
+
- ONLY mark a task as completed when you have FULLY accomplished it
|
|
2859
|
+
- If you encounter errors, blockers, or cannot finish, keep the task as in_progress
|
|
2860
|
+
- When blocked, create a new task describing what needs to be resolved
|
|
2861
|
+
- Never mark a task as completed if:
|
|
2862
|
+
- Tests are failing
|
|
2863
|
+
- Implementation is partial
|
|
2864
|
+
- You encountered unresolved errors
|
|
2865
|
+
- You couldn't find necessary files or dependencies
|
|
2866
|
+
|
|
2867
|
+
4. **Task Breakdown**:
|
|
2868
|
+
- Create specific, actionable items
|
|
2869
|
+
- Break complex tasks into smaller, manageable steps
|
|
2870
|
+
- Use clear, descriptive task names
|
|
2871
|
+
- Always provide both forms:
|
|
2872
|
+
- content: "Fix authentication bug"
|
|
2873
|
+
- activeForm: "Fixing authentication bug"
|
|
2874
|
+
|
|
2875
|
+
When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully.
|
|
2876
|
+
`;
|
|
2877
|
+
|
|
2878
|
+
// packages/tools/src/tools/interaction/TodoWriteTool/TodoWriteTool.tsx
|
|
2879
|
+
var TodoItemSchema = z9.object({
|
|
2880
|
+
content: z9.string().min(1, "Content cannot be empty").describe("The task description or content"),
|
|
2881
|
+
status: z9.enum(["pending", "in_progress", "completed"]).describe("Current status of the task"),
|
|
2882
|
+
activeForm: z9.string().min(1, "Active form cannot be empty").describe('The active form of the task (e.g., "Writing tests")')
|
|
2883
|
+
});
|
|
2884
|
+
var inputSchema9 = z9.strictObject({
|
|
2885
|
+
todos: z9.array(TodoItemSchema).describe("The updated todo list")
|
|
2886
|
+
});
|
|
2887
|
+
function validateTodos(todos) {
|
|
2888
|
+
const inProgressTasks = todos.filter((todo) => todo.status === "in_progress");
|
|
2889
|
+
if (inProgressTasks.length > 1) {
|
|
2890
|
+
return {
|
|
2891
|
+
result: false,
|
|
2892
|
+
errorCode: 2,
|
|
2893
|
+
message: "Only one task can be in_progress at a time",
|
|
2894
|
+
meta: { inProgressTasks: inProgressTasks.map((t) => t.content) }
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
for (const todo of todos) {
|
|
2898
|
+
if (!todo.content?.trim()) {
|
|
2899
|
+
return {
|
|
2900
|
+
result: false,
|
|
2901
|
+
errorCode: 3,
|
|
2902
|
+
message: "Todo has empty content"
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
if (!["pending", "in_progress", "completed"].includes(todo.status)) {
|
|
2906
|
+
return {
|
|
2907
|
+
result: false,
|
|
2908
|
+
errorCode: 4,
|
|
2909
|
+
message: `Invalid status "${todo.status}" for todo "${todo.content}"`,
|
|
2910
|
+
meta: { invalidStatus: todo.status }
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
if (!todo.activeForm?.trim()) {
|
|
2914
|
+
return {
|
|
2915
|
+
result: false,
|
|
2916
|
+
errorCode: 5,
|
|
2917
|
+
message: "Todo has empty activeForm",
|
|
2918
|
+
meta: { todoContent: todo.content }
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
return { result: true };
|
|
2923
|
+
}
|
|
2924
|
+
var TodoWriteTool = {
|
|
2925
|
+
name: "TodoWrite",
|
|
2926
|
+
async description() {
|
|
2927
|
+
return DESCRIPTION8;
|
|
2928
|
+
},
|
|
2929
|
+
async prompt() {
|
|
2930
|
+
return PROMPT8;
|
|
2931
|
+
},
|
|
2932
|
+
inputSchema: inputSchema9,
|
|
2933
|
+
userFacingName() {
|
|
2934
|
+
return "";
|
|
2935
|
+
},
|
|
2936
|
+
async isEnabled() {
|
|
2937
|
+
return true;
|
|
2938
|
+
},
|
|
2939
|
+
isReadOnly() {
|
|
2940
|
+
return false;
|
|
2941
|
+
},
|
|
2942
|
+
isConcurrencySafe() {
|
|
2943
|
+
return false;
|
|
2944
|
+
},
|
|
2945
|
+
needsPermissions() {
|
|
2946
|
+
return false;
|
|
2947
|
+
},
|
|
2948
|
+
renderResultForAssistant() {
|
|
2949
|
+
return "Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable";
|
|
2950
|
+
},
|
|
2951
|
+
renderToolUseMessage(input, { verbose }) {
|
|
2952
|
+
return null;
|
|
2953
|
+
},
|
|
2954
|
+
renderToolResultMessage(_output, _options) {
|
|
2955
|
+
return null;
|
|
2956
|
+
},
|
|
2957
|
+
async validateInput({ todos }) {
|
|
2958
|
+
const validation = validateTodos(todos);
|
|
2959
|
+
if (!validation.result) {
|
|
2960
|
+
return validation;
|
|
2961
|
+
}
|
|
2962
|
+
return { result: true };
|
|
2963
|
+
},
|
|
2964
|
+
async *call({ todos }, context) {
|
|
2965
|
+
const agentId = context?.agentId;
|
|
2966
|
+
if (agentId) {
|
|
2967
|
+
startWatchingTodoFile(agentId);
|
|
2968
|
+
}
|
|
2969
|
+
const previousTodos = getTodos(agentId);
|
|
2970
|
+
const oldTodos = previousTodos.map((todo) => ({
|
|
2971
|
+
content: todo.content,
|
|
2972
|
+
status: todo.status,
|
|
2973
|
+
activeForm: todo.activeForm || todo.content
|
|
2974
|
+
}));
|
|
2975
|
+
const shouldClear = todos.length > 0 && todos.every((todo) => todo.status === "completed");
|
|
2976
|
+
const reusable = /* @__PURE__ */ new Map();
|
|
2977
|
+
for (const todo of previousTodos) {
|
|
2978
|
+
const key = `${todo.content}|||${todo.activeForm || todo.content}`;
|
|
2979
|
+
const list = reusable.get(key) ?? [];
|
|
2980
|
+
list.push(todo);
|
|
2981
|
+
reusable.set(key, list);
|
|
2982
|
+
}
|
|
2983
|
+
const todoItems = shouldClear ? [] : todos.map((todo) => {
|
|
2984
|
+
const key = `${todo.content}|||${todo.activeForm}`;
|
|
2985
|
+
const list = reusable.get(key);
|
|
2986
|
+
const reused = list && list.length > 0 ? list.shift() : void 0;
|
|
2987
|
+
return {
|
|
2988
|
+
id: reused?.id ?? randomUUID3(),
|
|
2989
|
+
content: todo.content,
|
|
2990
|
+
status: todo.status,
|
|
2991
|
+
activeForm: todo.activeForm,
|
|
2992
|
+
priority: reused?.priority ?? "medium",
|
|
2993
|
+
...reused?.createdAt ? { createdAt: reused.createdAt } : {}
|
|
2994
|
+
};
|
|
2995
|
+
});
|
|
2996
|
+
try {
|
|
2997
|
+
setTodos(todoItems, agentId);
|
|
2998
|
+
} catch (error) {
|
|
2999
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
3000
|
+
emitReminderEvent("todo:error", {
|
|
3001
|
+
error: errorMessage,
|
|
3002
|
+
timestamp: Date.now(),
|
|
3003
|
+
agentId: context?.agentId || "default",
|
|
3004
|
+
context: "TodoWriteTool.call"
|
|
3005
|
+
});
|
|
3006
|
+
throw error instanceof Error ? error : new Error(errorMessage);
|
|
3007
|
+
}
|
|
3008
|
+
const hasChanged = JSON.stringify(previousTodos) !== JSON.stringify(todoItems);
|
|
3009
|
+
if (hasChanged) {
|
|
3010
|
+
emitReminderEvent("todo:changed", {
|
|
3011
|
+
previousTodos,
|
|
3012
|
+
newTodos: todoItems,
|
|
3013
|
+
timestamp: Date.now(),
|
|
3014
|
+
agentId: agentId || "default",
|
|
3015
|
+
changeType: todoItems.length > previousTodos.length ? "added" : todoItems.length < previousTodos.length ? "removed" : "modified"
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
yield {
|
|
3019
|
+
type: "result",
|
|
3020
|
+
data: {
|
|
3021
|
+
oldTodos,
|
|
3022
|
+
newTodos: todos,
|
|
3023
|
+
agentId: agentId || void 0
|
|
3024
|
+
},
|
|
3025
|
+
resultForAssistant: this.renderResultForAssistant()
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
3028
|
+
};
|
|
3029
|
+
|
|
3030
|
+
// packages/tools/src/tools/search/WebSearchTool/WebSearchTool.tsx
|
|
3031
|
+
import { Box as Box7, Text as Text8 } from "ink";
|
|
3032
|
+
import React8 from "react";
|
|
3033
|
+
import { z as z10 } from "zod";
|
|
3034
|
+
|
|
3035
|
+
// packages/tools/src/tools/search/WebSearchTool/prompt.ts
|
|
3036
|
+
var TOOL_NAME_FOR_PROMPT4 = "WebSearch";
|
|
3037
|
+
function todayISO() {
|
|
3038
|
+
const now = /* @__PURE__ */ new Date();
|
|
3039
|
+
const year = now.getFullYear();
|
|
3040
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
3041
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
3042
|
+
return `${year}-${month}-${day}`;
|
|
3043
|
+
}
|
|
3044
|
+
var PROMPT9 = `
|
|
3045
|
+
- Allows the assistant to search the web and use the results to inform responses
|
|
3046
|
+
- Provides up-to-date information for current events and recent data
|
|
3047
|
+
- Returns search result information formatted as search result blocks, including links as markdown hyperlinks
|
|
3048
|
+
- Use this tool for accessing information beyond the model's knowledge cutoff
|
|
3049
|
+
- Searches are performed automatically within a single API call
|
|
3050
|
+
|
|
3051
|
+
CRITICAL REQUIREMENT - You MUST follow this:
|
|
3052
|
+
- After answering the user's question, you MUST include a "Sources:" section at the end of your response
|
|
3053
|
+
- In the Sources section, list all relevant URLs from the search results as markdown hyperlinks: [Title](URL)
|
|
3054
|
+
- This is MANDATORY - never skip including sources in your response
|
|
3055
|
+
- Example format:
|
|
3056
|
+
|
|
3057
|
+
[Your answer here]
|
|
3058
|
+
|
|
3059
|
+
Sources:
|
|
3060
|
+
- [Source Title 1](https://example.com/1)
|
|
3061
|
+
- [Source Title 2](https://example.com/2)
|
|
3062
|
+
|
|
3063
|
+
Usage notes:
|
|
3064
|
+
- Domain filtering is supported to include or block specific websites
|
|
3065
|
+
- Web search is only available in the US
|
|
3066
|
+
|
|
3067
|
+
IMPORTANT - Use the correct year in search queries:
|
|
3068
|
+
- Today's date is ${todayISO()}. You MUST use this year when searching for recent information, documentation, or current events.
|
|
3069
|
+
- Example: If today is 2025-07-15 and the user asks for "latest React docs", search for "React documentation 2025", NOT "React documentation 2024"
|
|
3070
|
+
`.trim();
|
|
3071
|
+
|
|
3072
|
+
// packages/tools/src/tools/search/WebSearchTool/searchProviders.ts
|
|
3073
|
+
import { parse } from "node-html-parser";
|
|
3074
|
+
var duckDuckGoSearchProvider = {
|
|
3075
|
+
isEnabled: () => true,
|
|
3076
|
+
search: async (query2) => {
|
|
3077
|
+
const response = await fetch(
|
|
3078
|
+
`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query2)}`,
|
|
3079
|
+
{
|
|
3080
|
+
headers: {
|
|
3081
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
);
|
|
3085
|
+
if (!response.ok) {
|
|
3086
|
+
throw new Error(
|
|
3087
|
+
`DuckDuckGo search failed with status: ${response.status}`
|
|
3088
|
+
);
|
|
3089
|
+
}
|
|
3090
|
+
const html = await response.text();
|
|
3091
|
+
const root = parse(html);
|
|
3092
|
+
const results = [];
|
|
3093
|
+
const resultNodes = root.querySelectorAll(".result.web-result");
|
|
3094
|
+
for (const node of resultNodes) {
|
|
3095
|
+
const titleNode = node.querySelector(".result__a");
|
|
3096
|
+
const snippetNode = node.querySelector(".result__snippet");
|
|
3097
|
+
if (titleNode && snippetNode) {
|
|
3098
|
+
const title = titleNode.text;
|
|
3099
|
+
const link = titleNode.getAttribute("href");
|
|
3100
|
+
const snippet = snippetNode.text;
|
|
3101
|
+
if (title && link && snippet) {
|
|
3102
|
+
let cleanLink = link;
|
|
3103
|
+
if (link.startsWith("https://duckduckgo.com/l/?uddg=")) {
|
|
3104
|
+
try {
|
|
3105
|
+
const url = new URL(link);
|
|
3106
|
+
cleanLink = url.searchParams.get("uddg") || link;
|
|
3107
|
+
} catch {
|
|
3108
|
+
cleanLink = link;
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
results.push({
|
|
3112
|
+
title: title.trim(),
|
|
3113
|
+
snippet: snippet.trim(),
|
|
3114
|
+
link: cleanLink
|
|
3115
|
+
});
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
return results;
|
|
3120
|
+
}
|
|
3121
|
+
};
|
|
3122
|
+
var searchProviders = {
|
|
3123
|
+
duckduckgo: duckDuckGoSearchProvider
|
|
3124
|
+
};
|
|
3125
|
+
|
|
3126
|
+
// packages/tools/src/tools/search/WebSearchTool/WebSearchTool.tsx
|
|
3127
|
+
var inputSchema10 = z10.strictObject({
|
|
3128
|
+
query: z10.string().min(2).describe("The search query to use"),
|
|
3129
|
+
allowed_domains: z10.array(z10.string()).optional().describe("Only include search results from these domains"),
|
|
3130
|
+
blocked_domains: z10.array(z10.string()).optional().describe("Never include search results from these domains")
|
|
3131
|
+
});
|
|
3132
|
+
function hostnameForUrl(url) {
|
|
3133
|
+
try {
|
|
3134
|
+
return new URL(url).hostname;
|
|
3135
|
+
} catch {
|
|
3136
|
+
return null;
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
function summarizeResults(results) {
|
|
3140
|
+
let searchCount = 0;
|
|
3141
|
+
let totalResultCount = 0;
|
|
3142
|
+
for (const item of results) {
|
|
3143
|
+
if (typeof item === "string") continue;
|
|
3144
|
+
searchCount += 1;
|
|
3145
|
+
totalResultCount += item.content.length;
|
|
3146
|
+
}
|
|
3147
|
+
return { searchCount, totalResultCount };
|
|
3148
|
+
}
|
|
3149
|
+
var WebSearchTool = {
|
|
3150
|
+
name: TOOL_NAME_FOR_PROMPT4,
|
|
3151
|
+
async description(input) {
|
|
3152
|
+
const query2 = input?.query ?? "";
|
|
3153
|
+
return `Requesting web search for: ${query2}`;
|
|
3154
|
+
},
|
|
3155
|
+
userFacingName: () => "Web Search",
|
|
3156
|
+
inputSchema: inputSchema10,
|
|
3157
|
+
isReadOnly: () => true,
|
|
3158
|
+
isConcurrencySafe: () => true,
|
|
3159
|
+
async isEnabled() {
|
|
3160
|
+
return true;
|
|
3161
|
+
},
|
|
3162
|
+
needsPermissions() {
|
|
3163
|
+
return true;
|
|
3164
|
+
},
|
|
3165
|
+
async prompt() {
|
|
3166
|
+
return PROMPT9;
|
|
3167
|
+
},
|
|
3168
|
+
renderToolUseMessage({ query: query2, allowed_domains, blocked_domains }, { verbose }) {
|
|
3169
|
+
let summary = `"${query2}"`;
|
|
3170
|
+
if (verbose) {
|
|
3171
|
+
if (allowed_domains && allowed_domains.length > 0) {
|
|
3172
|
+
summary += `, only allowing domains: ${allowed_domains.join(", ")}`;
|
|
3173
|
+
}
|
|
3174
|
+
if (blocked_domains && blocked_domains.length > 0) {
|
|
3175
|
+
summary += `, blocking domains: ${blocked_domains.join(", ")}`;
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
return summary;
|
|
3179
|
+
},
|
|
3180
|
+
renderToolResultMessage(output) {
|
|
3181
|
+
const { searchCount } = summarizeResults(output.results);
|
|
3182
|
+
const duration = output.durationSeconds >= 1 ? `${Math.round(output.durationSeconds)}s` : `${Math.round(output.durationSeconds * 1e3)}ms`;
|
|
3183
|
+
return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "row" }, /* @__PURE__ */ React8.createElement(Text8, null, "\xA0\xA0\u23BF \xA0Did "), /* @__PURE__ */ React8.createElement(Text8, { bold: true }, searchCount, " "), /* @__PURE__ */ React8.createElement(Text8, null, "search", searchCount === 1 ? "" : "es", " in ", duration));
|
|
3184
|
+
},
|
|
3185
|
+
renderResultForAssistant(output) {
|
|
3186
|
+
let result = `Web search results for query: "${output.query}"
|
|
3187
|
+
|
|
3188
|
+
`;
|
|
3189
|
+
for (const item of output.results) {
|
|
3190
|
+
if (typeof item === "string") {
|
|
3191
|
+
result += `${item}
|
|
3192
|
+
|
|
3193
|
+
`;
|
|
3194
|
+
continue;
|
|
3195
|
+
}
|
|
3196
|
+
if (item.content.length > 0) {
|
|
3197
|
+
result += `Links: ${JSON.stringify(item.content)}
|
|
3198
|
+
|
|
3199
|
+
`;
|
|
3200
|
+
} else {
|
|
3201
|
+
result += `No links found.
|
|
3202
|
+
|
|
3203
|
+
`;
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
result += "\nREMINDER: You MUST include the sources above in your response to the user using markdown hyperlinks.";
|
|
3207
|
+
return result.trim();
|
|
3208
|
+
},
|
|
3209
|
+
async validateInput(input) {
|
|
3210
|
+
if (!input.query || !input.query.length) {
|
|
3211
|
+
return {
|
|
3212
|
+
result: false,
|
|
3213
|
+
message: "Error: Missing query",
|
|
3214
|
+
errorCode: 1
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
if (input.allowed_domains?.length && input.blocked_domains?.length) {
|
|
3218
|
+
return {
|
|
3219
|
+
result: false,
|
|
3220
|
+
message: "Error: Cannot specify both allowed_domains and blocked_domains in the same request",
|
|
3221
|
+
errorCode: 2
|
|
3222
|
+
};
|
|
3223
|
+
}
|
|
3224
|
+
return { result: true };
|
|
3225
|
+
},
|
|
3226
|
+
async *call({ query: query2, allowed_domains, blocked_domains }, {}) {
|
|
3227
|
+
const start = Date.now();
|
|
3228
|
+
try {
|
|
3229
|
+
const rawResults = await searchProviders.duckduckgo.search(query2);
|
|
3230
|
+
const allowed = allowed_domains?.map((d) => d.toLowerCase()) ?? null;
|
|
3231
|
+
const blocked = blocked_domains?.map((d) => d.toLowerCase()) ?? null;
|
|
3232
|
+
const results = rawResults.filter((result) => {
|
|
3233
|
+
const host = hostnameForUrl(result.link)?.toLowerCase();
|
|
3234
|
+
if (!host) return false;
|
|
3235
|
+
if (allowed && allowed.length > 0) {
|
|
3236
|
+
return allowed.some(
|
|
3237
|
+
(domain) => host === domain || host.endsWith(`.${domain}`)
|
|
3238
|
+
);
|
|
3239
|
+
}
|
|
3240
|
+
if (blocked && blocked.length > 0) {
|
|
3241
|
+
return !blocked.some(
|
|
3242
|
+
(domain) => host === domain || host.endsWith(`.${domain}`)
|
|
3243
|
+
);
|
|
3244
|
+
}
|
|
3245
|
+
return true;
|
|
3246
|
+
});
|
|
3247
|
+
const hits = results.map((item) => ({
|
|
3248
|
+
title: item.title,
|
|
3249
|
+
url: item.link
|
|
3250
|
+
}));
|
|
3251
|
+
const output = {
|
|
3252
|
+
query: query2,
|
|
3253
|
+
results: [
|
|
3254
|
+
{
|
|
3255
|
+
tool_use_id: "duckduckgo",
|
|
3256
|
+
content: hits
|
|
3257
|
+
}
|
|
3258
|
+
],
|
|
3259
|
+
durationSeconds: (Date.now() - start) / 1e3
|
|
3260
|
+
};
|
|
3261
|
+
yield {
|
|
3262
|
+
type: "result",
|
|
3263
|
+
resultForAssistant: this.renderResultForAssistant(output),
|
|
3264
|
+
data: output
|
|
3265
|
+
};
|
|
3266
|
+
} catch (error) {
|
|
3267
|
+
const output = {
|
|
3268
|
+
query: query2,
|
|
3269
|
+
results: [
|
|
3270
|
+
`Web search error: ${error instanceof Error ? error.message : String(error)}`
|
|
3271
|
+
],
|
|
3272
|
+
durationSeconds: (Date.now() - start) / 1e3
|
|
3273
|
+
};
|
|
3274
|
+
yield {
|
|
3275
|
+
type: "result",
|
|
3276
|
+
resultForAssistant: this.renderResultForAssistant(output),
|
|
3277
|
+
data: output
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
};
|
|
3282
|
+
|
|
3283
|
+
// packages/tools/src/registry.ts
|
|
3284
|
+
var getAllTools = () => [
|
|
3285
|
+
TaskTool,
|
|
3286
|
+
AskExpertModelTool,
|
|
3287
|
+
BashTool,
|
|
3288
|
+
TaskOutputTool,
|
|
3289
|
+
KillShellTool,
|
|
3290
|
+
GlobTool,
|
|
3291
|
+
GrepTool,
|
|
3292
|
+
LspTool,
|
|
3293
|
+
FileReadTool,
|
|
3294
|
+
FileEditTool,
|
|
3295
|
+
FileWriteTool,
|
|
3296
|
+
NotebookEditTool,
|
|
3297
|
+
TodoWriteTool,
|
|
3298
|
+
WebSearchTool,
|
|
3299
|
+
WebFetchTool,
|
|
3300
|
+
AskUserQuestionTool,
|
|
3301
|
+
EnterPlanModeTool,
|
|
3302
|
+
ExitPlanModeTool,
|
|
3303
|
+
SlashCommandTool,
|
|
3304
|
+
SkillTool,
|
|
3305
|
+
ListMcpResourcesTool,
|
|
3306
|
+
ReadMcpResourceTool,
|
|
3307
|
+
MCPTool
|
|
3308
|
+
];
|
|
3309
|
+
var getTools = memoize2(
|
|
3310
|
+
async (_includeOptional) => {
|
|
3311
|
+
const tools = [...getAllTools(), ...await getMCPTools()];
|
|
3312
|
+
const isEnabled = await Promise.all(tools.map((tool) => tool.isEnabled()));
|
|
3313
|
+
return tools.filter((_, i) => isEnabled[i]);
|
|
3314
|
+
}
|
|
3315
|
+
);
|
|
3316
|
+
var getReadOnlyTools = memoize2(async () => {
|
|
3317
|
+
const tools = getAllTools().filter((tool) => tool.isReadOnly());
|
|
3318
|
+
const isEnabled = await Promise.all(tools.map((tool) => tool.isEnabled()));
|
|
3319
|
+
return tools.filter((_, index) => isEnabled[index]);
|
|
3320
|
+
});
|
|
3321
|
+
|
|
3322
|
+
export {
|
|
3323
|
+
getAllTools,
|
|
3324
|
+
getTools,
|
|
3325
|
+
getReadOnlyTools
|
|
3326
|
+
};
|