@maycombhq/atticus 2.0.0-beta.2 → 2.0.0-beta.22
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/dist/bin.js +8 -4
- package/dist/chunk-23XVSEV5.js +49 -0
- package/dist/{chunk-ZDPAS56H.js → chunk-24OXGB3P.js} +8 -2
- package/dist/{chunk-7EOBHUMC.js → chunk-24XLDU72.js} +4 -3
- package/dist/{chunk-CR3MTH32.js → chunk-2F4CYZKJ.js} +86 -11
- package/dist/{chunk-6IU67AEK.js → chunk-2GF7OLF3.js} +23 -15
- package/dist/chunk-2IIMFOPM.js +41 -0
- package/dist/chunk-2LGRMVCA.js +617 -0
- package/dist/{chunk-XPCM366L.js → chunk-2MMCFP5C.js} +10 -8
- package/dist/{chunk-JBTK5OP2.js → chunk-2Q56PQPS.js} +6 -4
- package/dist/{chunk-Z6LF7HQN.js → chunk-2Q5EDGEO.js} +7 -5
- package/dist/{chunk-3USYZGRF.js → chunk-2RLKFUUL.js} +38 -27
- package/dist/chunk-2SKB4O2E.js +59 -0
- package/dist/{chunk-SP2I63OO.js → chunk-34S2WGOB.js} +87 -4
- package/dist/{chunk-GQ4N4WKF.js → chunk-3NFFJL5D.js} +33 -27
- package/dist/{chunk-SJRGMG6A.js → chunk-3NWDLGG6.js} +28 -25
- package/dist/{chunk-4WBVUHXN.js → chunk-3RM4UK6W.js} +20 -18
- package/dist/chunk-42SIDKEU.js +583 -0
- package/dist/{chunk-GLUX4EEP.js → chunk-4EURQWQD.js} +20 -15
- package/dist/chunk-4U276ICE.js +39 -0
- package/dist/{chunk-ZMFPW66A.js → chunk-53T4O55J.js} +4 -3
- package/dist/{chunk-6J5OKA4Y.js → chunk-57TDNMI3.js} +17 -15
- package/dist/chunk-5BD2KYLE.js +57 -0
- package/dist/{chunk-7EFILTCI.js → chunk-5F7GFN2F.js} +28 -0
- package/dist/chunk-5NMKONLP.js +95 -0
- package/dist/{chunk-7ZUIMTOU.js → chunk-5SJZW52K.js} +26 -24
- package/dist/{chunk-4RS5KZQD.js → chunk-63YRMR6J.js} +6 -5
- package/dist/chunk-66PPNOON.js +62 -0
- package/dist/chunk-6H3Z5JHH.js +64 -0
- package/dist/{chunk-CEXSU4S6.js → chunk-6MUGR4LS.js} +775 -89
- package/dist/{chunk-QSXK2RGH.js → chunk-6OJQOPUN.js} +5 -3
- package/dist/{chunk-MVZVGYXH.js → chunk-6PPOWY5E.js} +84 -14
- package/dist/{chunk-7EKXJZ2Y.js → chunk-6ZGLAACW.js} +299 -38
- package/dist/chunk-6ZJ5A7AF.js +222 -0
- package/dist/{chunk-L24C55VN.js → chunk-745DAZ4L.js} +142 -30
- package/dist/{chunk-Q4D5X7QB.js → chunk-7ASNL4Q4.js} +46 -24
- package/dist/chunk-7BR7IPOH.js +35 -0
- package/dist/{chunk-7WD63HU2.js → chunk-7DOR27HL.js} +6 -6
- package/dist/chunk-7FTTA3HD.js +341 -0
- package/dist/{chunk-OLQJTJJU.js → chunk-7GJS6DNX.js} +12 -3
- package/dist/{chunk-7JUKDRSZ.js → chunk-7IGKHZ4Z.js} +347 -226
- package/dist/{chunk-G2B4ZQGH.js → chunk-7IOWA5D5.js} +11 -6
- package/dist/{chunk-EBEW3XXQ.js → chunk-7JITZFJC.js} +7 -5
- package/dist/chunk-7MJSQTL5.js +174 -0
- package/dist/{chunk-I4JRCD6M.js → chunk-7UOMVMFG.js} +25 -17
- package/dist/{chunk-YY6ZQQBP.js → chunk-AFWUDXEM.js} +19 -7
- package/dist/chunk-AFZ3QA6W.js +47 -0
- package/dist/chunk-AJQ72EBM.js +420 -0
- package/dist/chunk-AJYXNTNZ.js +317 -0
- package/dist/chunk-AVTBS4OW.js +49 -0
- package/dist/{chunk-FHH4I4ZV.js → chunk-BDKC6H5P.js} +12 -6
- package/dist/{chunk-IPKHE5D6.js → chunk-BEVTIBRH.js} +1526 -875
- package/dist/chunk-BHT7QK4G.js +162 -0
- package/dist/{chunk-6JUBZBMW.js → chunk-BIGDQDHD.js} +6 -4
- package/dist/{chunk-2BFCZ3RK.js → chunk-BO4KX37X.js} +20 -18
- package/dist/chunk-BPK5P77O.js +115 -0
- package/dist/chunk-BQ4SQQC6.js +59 -0
- package/dist/chunk-BQGDB6ZL.js +7342 -0
- package/dist/{chunk-2ANOROX3.js → chunk-BTGPE6CR.js} +11 -9
- package/dist/chunk-BTY3HZBO.js +93 -0
- package/dist/{chunk-DPZHZQKE.js → chunk-BXYMYEJ4.js} +44 -10
- package/dist/{chunk-2RMYG36Q.js → chunk-C3RY6KU6.js} +309 -346
- package/dist/chunk-C4R4JLHE.js +40 -0
- package/dist/chunk-CADWLE2T.js +18 -0
- package/dist/chunk-CBGGOQWU.js +82 -0
- package/dist/chunk-CMDXGQTS.js +47 -0
- package/dist/{chunk-MC3H7736.js → chunk-CQDGOPGH.js} +58 -18
- package/dist/{chunk-HFQCFSJR.js → chunk-CX4RZ2KS.js} +4 -3
- package/dist/{chunk-PAMPPUR6.js → chunk-CXJ6FUKN.js} +1852 -207
- package/dist/{chunk-ABWHYWNI.js → chunk-D73UEXYW.js} +3 -3
- package/dist/chunk-DCZWQLYT.js +1205 -0
- package/dist/chunk-DLOBCJL6.js +48 -0
- package/dist/{chunk-4BN53TG3.js → chunk-DOBQKHIF.js} +31 -23
- package/dist/chunk-E3ISYXO3.js +423 -0
- package/dist/{chunk-7SW2QWLU.js → chunk-E6ZIEVOI.js} +16 -13
- package/dist/chunk-EBL2R3YG.js +43 -0
- package/dist/{chunk-AIOQPLA5.js → chunk-EGQRREQH.js} +41 -15
- package/dist/chunk-EHA7RMZ3.js +58 -0
- package/dist/{chunk-M766ZJ6W.js → chunk-EIPTLPFC.js} +38 -27
- package/dist/chunk-EMRIQUND.js +517 -0
- package/dist/{chunk-DFBCSHVT.js → chunk-EPCJQQWD.js} +6 -4
- package/dist/{chunk-RZPYKDWS.js → chunk-F3P7N7SF.js} +1 -1
- package/dist/{chunk-R77KLRB3.js → chunk-FFLNS6VP.js} +21 -18
- package/dist/chunk-FFTRTL6R.js +26 -0
- package/dist/{chunk-IHMLFRVV.js → chunk-FPJPLKDW.js} +62 -16
- package/dist/{chunk-JCZIKA4C.js → chunk-FSMSFDLD.js} +5 -4
- package/dist/{chunk-AVJDSJ2F.js → chunk-FUJM6RTF.js} +8 -7
- package/dist/chunk-FZOJFH7Q.js +132 -0
- package/dist/{chunk-QRAOANID.js → chunk-G6X77UJZ.js} +23 -21
- package/dist/{chunk-3ALD2W2A.js → chunk-GCWRWSU7.js} +10 -4
- package/dist/chunk-GIW4ZVDB.js +39 -0
- package/dist/{chunk-B2WLAWNJ.js → chunk-GUX3PTYM.js} +3 -3
- package/dist/{chunk-DPUYDDQG.js → chunk-GZY6RUZ6.js} +25 -3
- package/dist/chunk-H4VO6WG3.js +10 -0
- package/dist/{chunk-MMOSZPWJ.js → chunk-H5HBGNXU.js} +5 -4
- package/dist/chunk-H7KM7LZE.js +229 -0
- package/dist/{chunk-AA6YULYB.js → chunk-HNM2YQT2.js} +7 -7
- package/dist/{chunk-3C7EYRFZ.js → chunk-HYU5VLGA.js} +1 -1
- package/dist/{chunk-XY3P7JW7.js → chunk-IDKCWSM5.js} +1032 -172
- package/dist/chunk-IFKEI4SA.js +237 -0
- package/dist/{chunk-F7HYOUFF.js → chunk-IGOE6KPG.js} +67 -22
- package/dist/{chunk-TDD55CJH.js → chunk-IH2XURVQ.js} +6 -5
- package/dist/chunk-INQHESFB.js +38 -0
- package/dist/chunk-IP74LCOQ.js +57 -0
- package/dist/chunk-IPHHDDC6.js +45 -0
- package/dist/{chunk-T6AIP7H5.js → chunk-ISUAAMGS.js} +110 -153
- package/dist/{chunk-A7UPUQFY.js → chunk-IUPG3UKI.js} +624 -319
- package/dist/{chunk-L6SGQIR3.js → chunk-IVBOQAGB.js} +7 -3
- package/dist/{chunk-27NKUS3X.js → chunk-J2JAKCIW.js} +2205 -796
- package/dist/{chunk-V2YTAIQO.js → chunk-J3IOLXWM.js} +24 -7
- package/dist/chunk-JA5FI3QV.js +142 -0
- package/dist/chunk-JAVI7JSP.js +49 -0
- package/dist/{chunk-HLJAXMM4.js → chunk-JB5LINEM.js} +6 -2
- package/dist/{chunk-2VHF7OXM.js → chunk-JENSEMXF.js} +1 -1
- package/dist/{chunk-6BVONWJ7.js → chunk-JJF6HALY.js} +4 -3
- package/dist/chunk-JLUCKAEL.js +38 -0
- package/dist/chunk-JRBFANFE.js +221 -0
- package/dist/{chunk-R6XF6UR3.js → chunk-KDNTAPLX.js} +4 -4
- package/dist/chunk-KFAVQZSX.js +33 -0
- package/dist/chunk-KJXD4PNL.js +50 -0
- package/dist/chunk-KN5BXMWN.js +44 -0
- package/dist/{chunk-PCVDV3UZ.js → chunk-KQGURXFU.js} +9 -7
- package/dist/{chunk-WY6PP6AA.js → chunk-KXITGYLF.js} +40 -6
- package/dist/{chunk-66YVAGGM.js → chunk-L27SV425.js} +22 -20
- package/dist/{chunk-NE2W3OO7.js → chunk-LDLL7LB3.js} +24 -22
- package/dist/chunk-LFGTYX5U.js +379 -0
- package/dist/{chunk-EYWIDJFU.js → chunk-LGBMTVNT.js} +7 -5
- package/dist/{chunk-Q5AMFEWD.js → chunk-LGFL54FP.js} +3 -2
- package/dist/{chunk-6TACQN3N.js → chunk-LJXMYKZJ.js} +7 -5
- package/dist/{chunk-BKVWTZ6A.js → chunk-LKOR5UET.js} +29 -26
- package/dist/chunk-LNBQOP7D.js +294 -0
- package/dist/{chunk-O3WI5KWW.js → chunk-LO6P2VYG.js} +2 -2
- package/dist/chunk-LQJPN536.js +122 -0
- package/dist/{chunk-YEDDOYSC.js → chunk-LYBNHMVZ.js} +29 -5
- package/dist/{chunk-A3GY6SOA.js → chunk-LZ7NUC62.js} +20 -18
- package/dist/chunk-M32BLWDH.js +37 -0
- package/dist/{chunk-VYDBJ73M.js → chunk-M3NANSDZ.js} +17 -15
- package/dist/chunk-MFHL5URL.js +31 -0
- package/dist/chunk-MFR5OPBD.js +109 -0
- package/dist/{chunk-DE76JROX.js → chunk-MG4ZL5RW.js} +32 -20
- package/dist/{chunk-N7IXEHK3.js → chunk-MO6P2JZH.js} +10 -8
- package/dist/{chunk-65NYPGRC.js → chunk-MUWDB4R4.js} +1 -1
- package/dist/chunk-MZCGSXVR.js +34 -0
- package/dist/{chunk-MGYZM27B.js → chunk-N3XIYPU3.js} +51 -23
- package/dist/chunk-NDCF4R6S.js +172 -0
- package/dist/{chunk-DHPACDG7.js → chunk-NR2UJWOC.js} +10 -8
- package/dist/chunk-NXJ5URP6.js +51 -0
- package/dist/{chunk-UNM6ZOKP.js → chunk-NY2XSR5K.js} +147 -18
- package/dist/{chunk-5GDRCDQB.js → chunk-O45HFTFS.js} +3 -2
- package/dist/{chunk-ONLS5QA5.js → chunk-O4GXGGBX.js} +14 -12
- package/dist/chunk-O5PXQR67.js +249 -0
- package/dist/{chunk-PRPPGYZG.js → chunk-O66DM6DJ.js} +6 -4
- package/dist/{chunk-KEM4SDDP.js → chunk-O7FDLW7L.js} +11 -8
- package/dist/{chunk-T2UYZSCQ.js → chunk-O7XLYEFA.js} +169 -29
- package/dist/{chunk-FITWGOJ2.js → chunk-OHNCWBKF.js} +4 -4
- package/dist/{chunk-652QWTLZ.js → chunk-OHVTTOPS.js} +3 -3
- package/dist/chunk-OMI6FPPN.js +43 -0
- package/dist/chunk-ONUPY3H6.js +53 -0
- package/dist/chunk-OTLDSZDV.js +69 -0
- package/dist/chunk-OUBGGKDU.js +22 -0
- package/dist/chunk-OWNCM4ZN.js +330 -0
- package/dist/{chunk-YISXLPHN.js → chunk-P2V7QW4D.js} +235 -35
- package/dist/{chunk-CNUKCS2T.js → chunk-P32RUW3B.js} +1632 -93
- package/dist/{chunk-BWZ77RQT.js → chunk-PAQ2AS5E.js} +1 -1
- package/dist/{chunk-ZMAINWBW.js → chunk-PBPBMP2B.js} +4 -3
- package/dist/{chunk-ZVM6AKKM.js → chunk-PJSJPMJI.js} +3 -3
- package/dist/{chunk-EFHQ3TWP.js → chunk-PKBHS4AA.js} +233 -56
- package/dist/{chunk-GT4I4ZWM.js → chunk-PMUAJGLI.js} +20 -16
- package/dist/chunk-PPD7HTDP.js +152 -0
- package/dist/chunk-PSOFBQWB.js +42 -0
- package/dist/{chunk-S2A7YC3Q.js → chunk-Q2TECDIT.js} +10694 -8896
- package/dist/chunk-Q4K5WB2W.js +42 -0
- package/dist/{chunk-UGLN7QHV.js → chunk-Q6DFNVRJ.js} +14 -12
- package/dist/chunk-QA4O2GGC.js +265 -0
- package/dist/chunk-QAXW2N3B.js +1216 -0
- package/dist/chunk-QBNWF5ZU.js +19 -0
- package/dist/chunk-QFWA2DPH.js +224 -0
- package/dist/{chunk-23W7VWKR.js → chunk-QKUJMING.js} +1363 -114
- package/dist/{chunk-NXFPS2LR.js → chunk-R44CYFPV.js} +16 -13
- package/dist/{chunk-CKSITVS4.js → chunk-R7NYHMUU.js} +2687 -786
- package/dist/chunk-REWWDVQB.js +162 -0
- package/dist/{chunk-JMSCMUCA.js → chunk-RGERPOIV.js} +145 -21
- package/dist/{chunk-6JCPI55B.js → chunk-RLDGWLQ7.js} +49 -7
- package/dist/chunk-RUGXBPWI.js +384 -0
- package/dist/{chunk-CPA6HPVR.js → chunk-RXPJMEOW.js} +18 -12
- package/dist/{chunk-3HI6Z55Y.js → chunk-S4PMJU6H.js} +339 -43
- package/dist/chunk-S5LSDXV4.js +40 -0
- package/dist/chunk-S7FCV7S5.js +51 -0
- package/dist/{chunk-5GGOLAKC.js → chunk-SDKLKB6I.js} +28 -26
- package/dist/chunk-SFSPGFCV.js +62 -0
- package/dist/{chunk-ZNCEKR4J.js → chunk-SFUP7TTO.js} +12 -8
- package/dist/{chunk-64EQYWCA.js → chunk-SLXZOMA6.js} +69 -19
- package/dist/chunk-SQV37XO2.js +273 -0
- package/dist/{chunk-K65BJIMT.js → chunk-SU74NDMQ.js} +19 -17
- package/dist/chunk-SYV5LHML.js +165 -0
- package/dist/chunk-T4YPZIQN.js +36 -0
- package/dist/chunk-T6DQR5TV.js +323 -0
- package/dist/{chunk-JIITQXFB.js → chunk-THDMSZ3R.js} +3075 -802
- package/dist/{chunk-JGZCG5VM.js → chunk-TNR75OXT.js} +7 -7
- package/dist/{chunk-D3PO4DOZ.js → chunk-TUULXQRW.js} +174 -15
- package/dist/chunk-TYQBFTBR.js +516 -0
- package/dist/chunk-U76OWS3Q.js +49 -0
- package/dist/{chunk-YYQN46FY.js → chunk-UCROWG2D.js} +27 -24
- package/dist/{chunk-6M5NK3K3.js → chunk-UF4AZOZB.js} +50 -10
- package/dist/chunk-UF4GLFBX.js +345 -0
- package/dist/{chunk-OT2O76OS.js → chunk-UGUP3ZM6.js} +240 -39
- package/dist/chunk-UNAGJYET.js +45 -0
- package/dist/{chunk-5WG5YP4T.js → chunk-UOR2DTHH.js} +16 -14
- package/dist/{chunk-S3QNRWYR.js → chunk-UPOY4YSQ.js} +14 -12
- package/dist/{chunk-EPTBZ3GP.js → chunk-UPS2DGFC.js} +1 -1
- package/dist/{chunk-4AK7IBVP.js → chunk-UUU4CLJG.js} +4 -2
- package/dist/{chunk-W3BQAZ3Q.js → chunk-UWMJKX5U.js} +14 -12
- package/dist/{chunk-QZSX3F7C.js → chunk-UWOJLHFT.js} +27 -23
- package/dist/chunk-VBTP56CE.js +535 -0
- package/dist/chunk-VCTWPLAO.js +211 -0
- package/dist/chunk-VNU26RCO.js +1215 -0
- package/dist/{chunk-CSZ4C5N6.js → chunk-VTZMAJ3W.js} +93 -10
- package/dist/{chunk-TXLH6WHQ.js → chunk-VW62D6QE.js} +14 -12
- package/dist/{chunk-CAQZUNOH.js → chunk-VY72R2XZ.js} +610 -3
- package/dist/{chunk-2DIQQJCB.js → chunk-VZALIKIF.js} +18 -15
- package/dist/{chunk-TYAID7B7.js → chunk-W62H2XSO.js} +25 -23
- package/dist/{chunk-HVUPKCGJ.js → chunk-W7VWI2YH.js} +9 -6
- package/dist/{chunk-TANFV3MZ.js → chunk-WERM6KPJ.js} +7 -4
- package/dist/{chunk-BOHJZTEQ.js → chunk-WF7B67YE.js} +208 -87
- package/dist/{chunk-7FBGJFEE.js → chunk-WG5U3F5V.js} +14 -12
- package/dist/{chunk-4NDAAFQT.js → chunk-WJF2CS6M.js} +8 -5
- package/dist/{chunk-XAKWMCXI.js → chunk-WKUJ3KPV.js} +4 -4
- package/dist/chunk-WMTRHUI5.js +103 -0
- package/dist/chunk-WNKPRSGI.js +36 -0
- package/dist/chunk-WXFLMQSR.js +223 -0
- package/dist/{chunk-RRTKKSLQ.js → chunk-X3WY6MN3.js} +8 -6
- package/dist/{chunk-ZCJDOK2S.js → chunk-X4CXA2IL.js} +47 -23
- package/dist/{chunk-UBBC2XSG.js → chunk-X5F3Q5LV.js} +560 -105
- package/dist/{chunk-C6EKFH7J.js → chunk-XD75VZIH.js} +9 -7
- package/dist/chunk-XERYFDB5.js +297 -0
- package/dist/chunk-XI2LCCYQ.js +420 -0
- package/dist/chunk-Y2EQNWKV.js +933 -0
- package/dist/{chunk-LR3R6Z2A.js → chunk-YHCMZC2J.js} +7 -7
- package/dist/chunk-YYY3AZFE.js +122 -0
- package/dist/{chunk-GDLULHTC.js → chunk-Z5IA3WDR.js} +19 -31
- package/dist/{chunk-T6XQL73F.js → chunk-ZFFYNRIO.js} +63 -23
- package/dist/{chunk-EDS2L2LD.js → chunk-ZFL7NY4F.js} +56 -27
- package/dist/{chunk-YMZ3TNYB.js → chunk-ZGULQAPZ.js} +7 -4
- package/dist/{chunk-4SKZDI62.js → chunk-ZIUFM5HT.js} +3 -1
- package/dist/{chunk-DKJA76CJ.js → chunk-ZJFRMSJT.js} +15 -13
- package/dist/chunk-ZJXLNDH4.js +239 -0
- package/dist/chunk-ZQ3ON2VQ.js +252 -0
- package/dist/{chunk-HITVBIJS.js → chunk-ZQ7SKESZ.js} +150 -415
- package/dist/{chunk-6QSERWXH.js → chunk-ZQXZ2SES.js} +24 -21
- package/dist/chunk-ZX57KSEQ.js +39 -0
- package/dist/{chunk-I3E7CX4H.js → chunk-ZZK56BLP.js} +5 -3
- package/dist/files.json +664 -368
- package/dist/packages/naysayer/constitution/constitution.yaml +19 -0
- package/dist/packages/sdk/npm/index.js +6194 -2542
- package/dist/packages/ui/dist/assets/{elk-api-tma0MCuh.js → elk-api-CIf-0gz5.js} +2 -2
- package/dist/packages/ui/dist/assets/index-CoK_Dl0D.js +47 -0
- package/dist/packages/ui/dist/assets/index-Dw20IRhQ.css +1 -0
- package/dist/packages/ui/dist/index.html +3 -2
- package/package.json +5 -5
- package/dist/chunk-2BF2EYJV.js +0 -174
- package/dist/chunk-42EVSKME.js +0 -37
- package/dist/chunk-4BKGMUIK.js +0 -53
- package/dist/chunk-5KLGBXG5.js +0 -48
- package/dist/chunk-6BNC6EQ4.js +0 -41
- package/dist/chunk-6D2CQ5II.js +0 -41
- package/dist/chunk-7AVHLZGD.js +0 -45
- package/dist/chunk-D46WUV3T.js +0 -33
- package/dist/chunk-E36QOJMD.js +0 -47
- package/dist/chunk-FQXYPF7E.js +0 -147
- package/dist/chunk-GBRFVT63.js +0 -45
- package/dist/chunk-ICY36XCH.js +0 -34
- package/dist/chunk-J4K3KVLD.js +0 -131
- package/dist/chunk-JZU3H6UQ.js +0 -54
- package/dist/chunk-NN2ZQEB4.js +0 -38
- package/dist/chunk-PCOCACY3.js +0 -385
- package/dist/chunk-RTEYOSJY.js +0 -29
- package/dist/chunk-RUVFXE2L.js +0 -666
- package/dist/chunk-RVH5SUW5.js +0 -69
- package/dist/chunk-VN3DP4LQ.js +0 -43
- package/dist/chunk-W5BVQF44.js +0 -36
- package/dist/chunk-WVPJSRRJ.js +0 -52
- package/dist/chunk-XPTOBS2D.js +0 -48
- package/dist/packages/ui/dist/assets/index-B54fm9FQ.js +0 -29
- package/dist/packages/ui/dist/assets/index-DD_hufQz.css +0 -1
|
@@ -3,6 +3,9 @@ import { fileURLToPath as __atticusFileURLToPath } from "node:url";
|
|
|
3
3
|
var require = __atticusCreateRequire(import.meta.url);
|
|
4
4
|
var __atticusUrl = (p) => new URL("./" + p, import.meta.url).href;
|
|
5
5
|
var __atticusPath = (p) => __atticusFileURLToPath(__atticusUrl(p));
|
|
6
|
+
import {
|
|
7
|
+
commandWords
|
|
8
|
+
} from "./chunk-QA4O2GGC.js";
|
|
6
9
|
import {
|
|
7
10
|
ANSWERED_BY,
|
|
8
11
|
AgentDefinition,
|
|
@@ -13,8 +16,10 @@ import {
|
|
|
13
16
|
STEP_CLASSES,
|
|
14
17
|
changesDirOf,
|
|
15
18
|
changesRelOf,
|
|
19
|
+
copilotAcpOf,
|
|
16
20
|
currentSignal,
|
|
17
21
|
differentThings,
|
|
22
|
+
estimateTokens,
|
|
18
23
|
identifierWords,
|
|
19
24
|
judgmentKey,
|
|
20
25
|
loadOrgPack,
|
|
@@ -22,19 +27,122 @@ import {
|
|
|
22
27
|
namedBeyondAnswer,
|
|
23
28
|
orgPackDirOf,
|
|
24
29
|
quotedIn,
|
|
25
|
-
require_dist,
|
|
26
30
|
runsDirOf,
|
|
27
31
|
runsRelOf,
|
|
28
32
|
stateDirOf,
|
|
29
|
-
stepModelsOf
|
|
30
|
-
|
|
33
|
+
stepModelsOf,
|
|
34
|
+
tokenUnits
|
|
35
|
+
} from "./chunk-Q2TECDIT.js";
|
|
31
36
|
import {
|
|
32
37
|
writeFileAtomic
|
|
33
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-TUULXQRW.js";
|
|
39
|
+
import {
|
|
40
|
+
require_dist
|
|
41
|
+
} from "./chunk-BQGDB6ZL.js";
|
|
34
42
|
import {
|
|
35
43
|
__toESM
|
|
36
44
|
} from "./chunk-TV45EUGM.js";
|
|
37
45
|
|
|
46
|
+
// packages/ai/dist/person-only.js
|
|
47
|
+
var flag = (a, ...names) => a.some((x) => names.some((n) => x === n || x.startsWith(`${n}=`)));
|
|
48
|
+
var switchOn = (a, name) => a.some((x) => x === name || x.startsWith(`${name}=`) && /^(true)?$/i.test(x.slice(name.length + 1)));
|
|
49
|
+
var RULES = [
|
|
50
|
+
{
|
|
51
|
+
test: (w, a) => w[0] === "change" && w[1] === "release-prs" && flag(a, "--approve"),
|
|
52
|
+
why: "a person raises the release PRs into each repo's default branch (ADR 0184); a session never approves a push or a PR"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
test: (w, a) => w[0] === "task" && w[1] === "run-ready" && flag(a, "--approve"),
|
|
56
|
+
why: "a person approves each ready task's push and PR into the change branch (ADR 0181); a session never approves a push or a PR"
|
|
57
|
+
},
|
|
58
|
+
{ test: (w) => w[0] === "questions" && w[1] === "answer", why: "a person answers questions" },
|
|
59
|
+
{
|
|
60
|
+
// ADR 0181: the AI decides as itself, through Atticus; correcting it, spreading the
|
|
61
|
+
// correction and undoing a batch are a person's word.
|
|
62
|
+
test: (w) => w[0] === "questions" && (w[1] === "correct" || w[1] === "undo"),
|
|
63
|
+
why: "a person corrects an AI decision and undoes a batch of decisions (ADR 0181); the AI decides only as itself, through Atticus"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
test: (w, a) => w[0] === "questions" && w[1] === "sheet" && flag(a, "--apply"),
|
|
67
|
+
why: "a person answers questions (questions sheet --apply answers as them)"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
test: (w, a) => w[0] === "verify" && w[1] === "audit" && flag(a, "--right", "--wrong", "--unsure", "--answers"),
|
|
71
|
+
why: "a person answers an audit: its record says who confirmed each fact"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
test: (w) => w[0] === "knowledge" && ["verify", "correct", "reject"].includes(w[1] ?? ""),
|
|
75
|
+
why: "a person verifies, corrects or rejects a knowledge item"
|
|
76
|
+
},
|
|
77
|
+
{ test: (w) => w[1] === "accept", why: "a person accepts what is proposed" },
|
|
78
|
+
{ test: (w) => w[0] === "baseline", why: "a person accepts the catalog as the baseline" },
|
|
79
|
+
{ test: (w) => w[0] === "triage" && w[1] === "decide", why: "a person decides a triage item" },
|
|
80
|
+
{ test: (w) => w[0] === "finding", why: "a person acts on a finding (it is recorded with them)" },
|
|
81
|
+
{
|
|
82
|
+
test: (w) => w[0] === "feature" && ["name", "link"].includes(w[1] ?? ""),
|
|
83
|
+
why: "a person names a feature or links it to work"
|
|
84
|
+
},
|
|
85
|
+
{ test: (w) => w[0] === "todo" && w[1] === "done", why: "a person records a manual step done" },
|
|
86
|
+
{
|
|
87
|
+
test: (w) => {
|
|
88
|
+
const n = w[0] === "change" && w[1] === "needs" ? w.slice(1) : w;
|
|
89
|
+
return n[0] === "needs" && ["done", "skip"].includes(n[1] ?? "");
|
|
90
|
+
},
|
|
91
|
+
why: "a person records a need done or not needed"
|
|
92
|
+
},
|
|
93
|
+
{ test: (_w, a) => flag(a, "--by"), why: "--by names a person; a session acts as no one" },
|
|
94
|
+
{
|
|
95
|
+
test: (_w, a) => flag(a, "--approve", "--send"),
|
|
96
|
+
why: "--approve and --send are a person's: a human approves every write to shared systems"
|
|
97
|
+
},
|
|
98
|
+
{ test: (_w, a) => flag(a, "--as"), why: "--as names a person; a session acts as no one" },
|
|
99
|
+
{
|
|
100
|
+
test: (w, a) => w[0] === "rules" && (w[1] === "activate" || w[1] === "off" || w[1] === "add" && !switchOn(a, "--draft")),
|
|
101
|
+
why: "a person sets a reading rule active or off (a session drafts)"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
test: (w) => w[0] === "initiative" && ["commit", "new", "plan", "request"].includes(w[1] ?? ""),
|
|
105
|
+
why: "a person starts, plans and records an initiative"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
test: (w) => w[0] === "questions" && w[1] === "owner",
|
|
109
|
+
why: "an owner answers a knowledge gap's question"
|
|
110
|
+
},
|
|
111
|
+
{ test: (w) => w[0] === "outcome" && w[1] === "record", why: "a person records an outcome" },
|
|
112
|
+
{
|
|
113
|
+
test: (_w, a) => flag(a, "--accept", "--accept-proposal"),
|
|
114
|
+
why: "a person accepts what is proposed (--accept is theirs)"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
test: (w, a) => w[0] === "repo" && (w[1] === "add" || w[1] === "describe" && flag(a, "--description", "--reject")),
|
|
118
|
+
why: "a person adds a repo and writes or drops its description (a session drafts with --drafted)"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
test: (w) => w[0] === "knowledge" && w[1] === "keep",
|
|
122
|
+
why: "a person keeps a knowledge item (it is recorded as verified by them)"
|
|
123
|
+
},
|
|
124
|
+
{ test: (w) => w[0] === "alias", why: "a person decides what a name means" },
|
|
125
|
+
{
|
|
126
|
+
test: (w) => w[0] === "glossary" && ["define", "link", "alias"].includes(w[1] ?? ""),
|
|
127
|
+
why: "a person defines, links or aliases a glossary term"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
// Seventh UX walk: shown by anyone; set (a handle given) only by a person, as in the app.
|
|
131
|
+
test: (w) => w[0] === "orgpack" && w[1] === "owner" && !!w[2],
|
|
132
|
+
why: "a person says who looks after the org pack"
|
|
133
|
+
}
|
|
134
|
+
];
|
|
135
|
+
function withoutLegacy(argv) {
|
|
136
|
+
return argv[0] === "legacy" && argv[1] !== void 0 && !argv[1].startsWith("-") ? argv.slice(1) : argv;
|
|
137
|
+
}
|
|
138
|
+
function personOnlyWhy(argv) {
|
|
139
|
+
argv = withoutLegacy(argv);
|
|
140
|
+
const words2 = commandWords(argv);
|
|
141
|
+
return RULES.find((r) => r.test(words2, argv))?.why;
|
|
142
|
+
}
|
|
143
|
+
var isPersonsFactsFile = (name) => /\.audit\.yaml$|\.corrections\.jsonl$/i.test(windowsName(name));
|
|
144
|
+
var windowsName = (name) => name.replace(/:.*$/, "").replace(/[. ]+$/, "");
|
|
145
|
+
|
|
38
146
|
// packages/ai/dist/context.js
|
|
39
147
|
import { createHash as createHash2 } from "node:crypto";
|
|
40
148
|
|
|
@@ -855,9 +963,16 @@ var PromptTooLong = class extends Error {
|
|
|
855
963
|
|
|
856
964
|
// packages/ai/dist/context.js
|
|
857
965
|
var sha2562 = (s) => createHash2("sha256").update(s, "utf8").digest("hex");
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
966
|
+
var RequiredSourceLeftOut = class extends Error {
|
|
967
|
+
step;
|
|
968
|
+
left;
|
|
969
|
+
constructor(step, left) {
|
|
970
|
+
super(`atticus ${step} can't go without ${left.map((l) => `${l.origin}: ${l.reason}`).join("; ")}.`);
|
|
971
|
+
this.step = step;
|
|
972
|
+
this.left = left;
|
|
973
|
+
this.name = "RequiredSourceLeftOut";
|
|
974
|
+
}
|
|
975
|
+
};
|
|
861
976
|
function wrapData(source, text) {
|
|
862
977
|
const safe = escapeDelimiters(text).text;
|
|
863
978
|
return `<atticus-data id="${attr(source.id)}" kind="${attr(source.kind)}" origin="${attr(source.origin)}">
|
|
@@ -870,10 +985,10 @@ function buildContextPack(given) {
|
|
|
870
985
|
const maxTokens = given.maxTokens ?? currentPromptLimit();
|
|
871
986
|
const o = { ...given, budget: fitBudget(given.budget, maxTokens) };
|
|
872
987
|
if (maxTokens !== void 0) {
|
|
873
|
-
const
|
|
874
|
-
const needed = o.sources.filter((s) => s.trusted ||
|
|
988
|
+
const required2 = new Set(o.required ?? []);
|
|
989
|
+
const needed = o.sources.filter((s) => s.trusted || required2.has(s.id)).reduce((n, s) => n + estimateTokens(s.trusted ? s.text : wrapData(s, s.text)), estimateTokens(DATA_RULE));
|
|
875
990
|
if (needed > o.budget)
|
|
876
|
-
throw new PromptTooLong(o.step ?? o.agent, needed, o.budget, maxTokens, o.sources.filter((s) => !s.trusted &&
|
|
991
|
+
throw new PromptTooLong(o.step ?? o.agent, needed, o.budget, maxTokens, o.sources.filter((s) => !s.trusted && required2.has(s.id)).map((s) => s.id), o.smaller);
|
|
877
992
|
}
|
|
878
993
|
const maxLevel = o.levels.indexOf(o.maxClassification);
|
|
879
994
|
if (maxLevel === -1)
|
|
@@ -929,6 +1044,14 @@ function buildContextPack(given) {
|
|
|
929
1044
|
}
|
|
930
1045
|
if (total > o.budget)
|
|
931
1046
|
throw new Error(`instructions alone (${total} tokens) exceed the ${o.budget}-token budget`);
|
|
1047
|
+
const required = new Set(o.required ?? []);
|
|
1048
|
+
const left = dropped.filter((d) => required.has(d.id));
|
|
1049
|
+
if (left.length)
|
|
1050
|
+
throw new RequiredSourceLeftOut(o.step ?? o.agent, left.map((d) => ({
|
|
1051
|
+
id: d.id,
|
|
1052
|
+
origin: o.sources.find((s) => s.id === d.id)?.origin ?? d.id,
|
|
1053
|
+
reason: d.reason
|
|
1054
|
+
})));
|
|
932
1055
|
const prompt = parts.join("\n\n") + "\n";
|
|
933
1056
|
return {
|
|
934
1057
|
prompt,
|
|
@@ -947,6 +1070,11 @@ function buildContextPack(given) {
|
|
|
947
1070
|
};
|
|
948
1071
|
}
|
|
949
1072
|
|
|
1073
|
+
// packages/ai/dist/runtime.js
|
|
1074
|
+
import { randomBytes } from "node:crypto";
|
|
1075
|
+
import { mkdir } from "node:fs/promises";
|
|
1076
|
+
import { join } from "node:path";
|
|
1077
|
+
|
|
950
1078
|
// packages/ai/dist/step-class.js
|
|
951
1079
|
var STEP_CLASS_BY_AGENT = {
|
|
952
1080
|
// read: the repo read into facts (atticus map, and its fix round for the facts rejected).
|
|
@@ -1018,6 +1146,10 @@ var NEXT_CLASS = {
|
|
|
1018
1146
|
survey: "draft",
|
|
1019
1147
|
verify: "read",
|
|
1020
1148
|
map: "read",
|
|
1149
|
+
// ADR 0141: a mapped repo read again where its clone moved on.
|
|
1150
|
+
reread: "read",
|
|
1151
|
+
// ADR 0167: a read repo topped up for reading rules added since.
|
|
1152
|
+
rules: "read",
|
|
1021
1153
|
spec: "draft",
|
|
1022
1154
|
knowledge: "knowledge",
|
|
1023
1155
|
feature: "draft",
|
|
@@ -1107,8 +1239,21 @@ function checkAllowed(kind, agent) {
|
|
|
1107
1239
|
}
|
|
1108
1240
|
function makeRunId(now, seq) {
|
|
1109
1241
|
const stamp = now.toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
|
1110
|
-
const
|
|
1111
|
-
return `run-${stamp}-${
|
|
1242
|
+
const tail2 = seq !== void 0 ? String(seq).padStart(4, "0") : randomBytes(3).toString("hex");
|
|
1243
|
+
return `run-${stamp}-${tail2}`;
|
|
1244
|
+
}
|
|
1245
|
+
async function claimRunDir(dir, next) {
|
|
1246
|
+
await mkdir(dir, { recursive: true });
|
|
1247
|
+
for (let i = 0; ; i++) {
|
|
1248
|
+
const runId = next();
|
|
1249
|
+
try {
|
|
1250
|
+
await mkdir(join(dir, runId));
|
|
1251
|
+
return runId;
|
|
1252
|
+
} catch (e) {
|
|
1253
|
+
if (e.code !== "EEXIST" || i >= 9999)
|
|
1254
|
+
throw e;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1112
1257
|
}
|
|
1113
1258
|
function toRunSummary(run) {
|
|
1114
1259
|
return {
|
|
@@ -1128,7 +1273,10 @@ function toRunSummary(run) {
|
|
|
1128
1273
|
...run.tools ? { tools: run.tools } : {},
|
|
1129
1274
|
...run.request.retrieval ? { retrieval: run.request.retrieval } : {},
|
|
1130
1275
|
stepClass: run.stepClass ?? stepClassOf(run.request),
|
|
1131
|
-
...run.model ? { model: run.model } : {}
|
|
1276
|
+
...run.model ? { model: run.model } : {},
|
|
1277
|
+
...run.assistant ? { assistant: run.assistant } : {},
|
|
1278
|
+
...run.batch ? { batch: run.batch } : {},
|
|
1279
|
+
...run.parts?.length ? { parts: run.parts } : {}
|
|
1132
1280
|
};
|
|
1133
1281
|
}
|
|
1134
1282
|
var OneShotRuntime = class {
|
|
@@ -1145,17 +1293,32 @@ var OneShotRuntime = class {
|
|
|
1145
1293
|
modelFor(_request) {
|
|
1146
1294
|
return void 0;
|
|
1147
1295
|
}
|
|
1296
|
+
/** The kind an agent's allowedRuntimes must name for this runtime (copilot-acp: hand-off too). */
|
|
1297
|
+
allowedAs(_agent) {
|
|
1298
|
+
return this.kind;
|
|
1299
|
+
}
|
|
1148
1300
|
/** Keep a finished run where Model runs reads it (the model runtime, given a root). */
|
|
1149
1301
|
async keep(_run) {
|
|
1150
1302
|
}
|
|
1303
|
+
/**
|
|
1304
|
+
* The next run id: counted from 1 on a test's clock (deterministic), with a random tail on the
|
|
1305
|
+
* real one, so two commands' runtimes in one second don't share an id (round 46).
|
|
1306
|
+
*/
|
|
1307
|
+
nextRunId(now) {
|
|
1308
|
+
return makeRunId(now, this.opts.now ? ++this.seq : void 0);
|
|
1309
|
+
}
|
|
1310
|
+
/** A new run's id; a runtime that keeps its runs in folders claims the folder (`claimRunDir`). */
|
|
1311
|
+
async newRunId(_request, now) {
|
|
1312
|
+
return this.nextRunId(now);
|
|
1313
|
+
}
|
|
1151
1314
|
async prepare(request) {
|
|
1152
|
-
checkAllowed(this.
|
|
1315
|
+
checkAllowed(this.allowedAs(request.agent), request.agent);
|
|
1153
1316
|
if (request.pack.manifest.totalTokens > request.agent.tokenBudget) {
|
|
1154
1317
|
throw new RuntimeError(`context pack (${request.pack.manifest.totalTokens} tokens) is over agent ${request.agent.id}'s budget of ${request.agent.tokenBudget}`);
|
|
1155
1318
|
}
|
|
1156
1319
|
const now = this.now();
|
|
1157
1320
|
const run = {
|
|
1158
|
-
runId:
|
|
1321
|
+
runId: await this.newRunId(request, now),
|
|
1159
1322
|
request,
|
|
1160
1323
|
runtime: this.kind,
|
|
1161
1324
|
state: "prepared",
|
|
@@ -1177,8 +1340,14 @@ var OneShotRuntime = class {
|
|
|
1177
1340
|
return run;
|
|
1178
1341
|
}
|
|
1179
1342
|
try {
|
|
1180
|
-
const { output, usage, tools } = await this.produce(run, signal);
|
|
1181
|
-
run = transition(run, "produced", this.now(), {
|
|
1343
|
+
const { output, usage, tools, model, assistant } = await this.produce(run, signal);
|
|
1344
|
+
run = transition(run, "produced", this.now(), {
|
|
1345
|
+
output,
|
|
1346
|
+
usage,
|
|
1347
|
+
...tools ? { tools } : {},
|
|
1348
|
+
...model ? { model } : {},
|
|
1349
|
+
...assistant ? { assistant } : {}
|
|
1350
|
+
});
|
|
1182
1351
|
const problems = await this.opts.validate?.(output, run.request) ?? [];
|
|
1183
1352
|
run = problems.length === 0 ? transition(run, "validated", this.now()) : transition(run, "failed", this.now(), { errors: problems });
|
|
1184
1353
|
} catch (e) {
|
|
@@ -1204,12 +1373,12 @@ var OneShotRuntime = class {
|
|
|
1204
1373
|
};
|
|
1205
1374
|
|
|
1206
1375
|
// packages/ai/dist/runtimes.js
|
|
1207
|
-
import { mkdir as
|
|
1208
|
-
import { join as
|
|
1376
|
+
import { mkdir as mkdir3, readdir as readdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1377
|
+
import { join as join3 } from "node:path";
|
|
1209
1378
|
|
|
1210
1379
|
// packages/ai/dist/tools.js
|
|
1211
|
-
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
1212
|
-
import { join } from "node:path";
|
|
1380
|
+
import { mkdir as mkdir2, readdir, readFile, writeFile } from "node:fs/promises";
|
|
1381
|
+
import { join as join2 } from "node:path";
|
|
1213
1382
|
var ToolError = class extends Error {
|
|
1214
1383
|
constructor(message) {
|
|
1215
1384
|
super(message);
|
|
@@ -1291,8 +1460,18 @@ function guardToolText(name, raw, room) {
|
|
|
1291
1460
|
if (estimateTokens(wrap(text)) > room) {
|
|
1292
1461
|
const note = `
|
|
1293
1462
|
\u2026 cut at ${room} tokens; ask for less (a smaller limit, fewer hops, one id).`;
|
|
1294
|
-
const
|
|
1295
|
-
|
|
1463
|
+
const units = Math.max(0, room * 4 - tokenUnits(wrap("")) - tokenUnits(note));
|
|
1464
|
+
let end = 0;
|
|
1465
|
+
for (let used = 0; end < text.length; end++) {
|
|
1466
|
+
const c = text.charCodeAt(end);
|
|
1467
|
+
used += c < 128 ? 1 : c >= 56320 && c <= 57343 ? 0 : 4;
|
|
1468
|
+
if (used > units)
|
|
1469
|
+
break;
|
|
1470
|
+
}
|
|
1471
|
+
const last = text.charCodeAt(end - 1);
|
|
1472
|
+
if (end > 0 && last >= 55296 && last <= 56319)
|
|
1473
|
+
end--;
|
|
1474
|
+
text = text.slice(0, end) + note;
|
|
1296
1475
|
}
|
|
1297
1476
|
return wrap(text);
|
|
1298
1477
|
}
|
|
@@ -1339,7 +1518,7 @@ async function recordToolCalls(root, runId, update) {
|
|
|
1339
1518
|
if (!found)
|
|
1340
1519
|
return void 0;
|
|
1341
1520
|
const tools = update(found.run.tools);
|
|
1342
|
-
await
|
|
1521
|
+
await mkdir2(join2(found.file, ".."), { recursive: true });
|
|
1343
1522
|
await writeFile(found.file, JSON.stringify({ ...found.run, tools }, null, 2) + "\n", "utf8");
|
|
1344
1523
|
return tools;
|
|
1345
1524
|
}
|
|
@@ -1354,7 +1533,7 @@ async function runFile(root, runId) {
|
|
|
1354
1533
|
for (const change of await readdir(runs).catch(() => [])) {
|
|
1355
1534
|
if (change.includes(".."))
|
|
1356
1535
|
continue;
|
|
1357
|
-
const file =
|
|
1536
|
+
const file = join2(runs, change, runId, "run.json");
|
|
1358
1537
|
const text = await readFile(file, "utf8").catch(() => void 0);
|
|
1359
1538
|
if (text)
|
|
1360
1539
|
return { file, run: JSON.parse(text) };
|
|
@@ -1398,8 +1577,8 @@ var ReplayRuntime = class _ReplayRuntime extends OneShotRuntime {
|
|
|
1398
1577
|
} catch {
|
|
1399
1578
|
}
|
|
1400
1579
|
for (const a of agents.sort()) {
|
|
1401
|
-
for (const f of (await readdir2(
|
|
1402
|
-
out.push(Recording.parse(JSON.parse(await readFile2(
|
|
1580
|
+
for (const f of (await readdir2(join3(dir, a)).catch(() => [])).filter((x) => x.endsWith(".json")).sort()) {
|
|
1581
|
+
out.push(Recording.parse(JSON.parse(await readFile2(join3(dir, a, f), "utf8"))));
|
|
1403
1582
|
}
|
|
1404
1583
|
}
|
|
1405
1584
|
return new _ReplayRuntime(out, opts);
|
|
@@ -1427,8 +1606,8 @@ async function recordRun(live, request, dir, model, now = /* @__PURE__ */ new Da
|
|
|
1427
1606
|
model,
|
|
1428
1607
|
recordedAt: now.toISOString()
|
|
1429
1608
|
};
|
|
1430
|
-
await
|
|
1431
|
-
await writeFile2(
|
|
1609
|
+
await mkdir3(join3(dir, rec.agent), { recursive: true });
|
|
1610
|
+
await writeFile2(join3(dir, rec.agent, `${rec.promptSha256}.json`), JSON.stringify(rec, null, 2) + "\n", "utf8");
|
|
1432
1611
|
return rec;
|
|
1433
1612
|
}
|
|
1434
1613
|
var MockModelProvider = class {
|
|
@@ -1527,17 +1706,56 @@ ${DATA_RULE}`;
|
|
|
1527
1706
|
async keep(run) {
|
|
1528
1707
|
if (!this.mo.root)
|
|
1529
1708
|
return;
|
|
1530
|
-
const dir =
|
|
1531
|
-
await
|
|
1532
|
-
await writeFile2(
|
|
1533
|
-
await writeFile2(
|
|
1534
|
-
await writeFile2(
|
|
1709
|
+
const dir = join3(runsDirOf(this.mo.root), run.request.change, run.runId);
|
|
1710
|
+
await mkdir3(dir, { recursive: true });
|
|
1711
|
+
await writeFile2(join3(dir, "prompt.md"), run.request.pack.prompt, "utf8");
|
|
1712
|
+
await writeFile2(join3(dir, "context-manifest.json"), JSON.stringify(run.request.pack.manifest, null, 2) + "\n", "utf8");
|
|
1713
|
+
await writeFile2(join3(dir, "run.json"), JSON.stringify(run, null, 2) + "\n", "utf8");
|
|
1714
|
+
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Round 46: given a root, the run's folder is claimed with its id, so another command's run in
|
|
1717
|
+
* the same second (its runtime counts from 1 too) never gets this id and overwrites it.
|
|
1718
|
+
*/
|
|
1719
|
+
async newRunId(request, now) {
|
|
1720
|
+
if (!this.mo.root)
|
|
1721
|
+
return super.newRunId(request, now);
|
|
1722
|
+
return claimRunDir(join3(runsDirOf(this.mo.root), request.change), () => this.nextRunId(now));
|
|
1535
1723
|
}
|
|
1536
1724
|
};
|
|
1537
1725
|
|
|
1538
1726
|
// packages/ai/dist/handoff.js
|
|
1539
|
-
import { mkdir as
|
|
1540
|
-
import { join as
|
|
1727
|
+
import { mkdir as mkdir4, readdir as readdir3, readFile as readFile3, rm, stat, writeFile as writeFile3 } from "node:fs/promises";
|
|
1728
|
+
import { join as join4 } from "node:path";
|
|
1729
|
+
var BATCH_OUTPUT = "answers.md";
|
|
1730
|
+
var DEFAULT_BATCH_MAX_TOKENS = 6e4;
|
|
1731
|
+
var SECTION = /^===[ \t]*([a-z][a-z0-9-]*)[ \t]*$/;
|
|
1732
|
+
function batchSections(text) {
|
|
1733
|
+
const out = /* @__PURE__ */ new Map();
|
|
1734
|
+
let name;
|
|
1735
|
+
let lines = [];
|
|
1736
|
+
const end = () => {
|
|
1737
|
+
if (name === void 0)
|
|
1738
|
+
return;
|
|
1739
|
+
while (lines.length && !lines[0].trim())
|
|
1740
|
+
lines.shift();
|
|
1741
|
+
while (lines.length && !lines.at(-1).trim())
|
|
1742
|
+
lines.pop();
|
|
1743
|
+
const body = lines.length ? `${lines.join("\n")}
|
|
1744
|
+
` : "";
|
|
1745
|
+
out.set(name, out.has(name) ? { text: body, twice: true } : { text: body });
|
|
1746
|
+
};
|
|
1747
|
+
for (const line of text.replace(/\r\n/g, "\n").split("\n")) {
|
|
1748
|
+
const m = SECTION.exec(line);
|
|
1749
|
+
if (m) {
|
|
1750
|
+
end();
|
|
1751
|
+
name = m[1];
|
|
1752
|
+
lines = [];
|
|
1753
|
+
} else
|
|
1754
|
+
lines.push(line);
|
|
1755
|
+
}
|
|
1756
|
+
end();
|
|
1757
|
+
return out;
|
|
1758
|
+
}
|
|
1541
1759
|
var HandoffRuntime = class _HandoffRuntime {
|
|
1542
1760
|
o;
|
|
1543
1761
|
kind = "hand-off";
|
|
@@ -1546,12 +1764,12 @@ var HandoffRuntime = class _HandoffRuntime {
|
|
|
1546
1764
|
this.o = o;
|
|
1547
1765
|
}
|
|
1548
1766
|
now = () => this.o.now?.() ?? /* @__PURE__ */ new Date();
|
|
1549
|
-
runDir = (change, runId) =>
|
|
1550
|
-
outputFile = (r) => r.request.outputIn === "run" ?
|
|
1767
|
+
runDir = (change, runId) => join4(runsDirOf(this.o.root), change, runId);
|
|
1768
|
+
outputFile = (r) => r.request.outputIn === "run" ? join4(this.runDir(r.request.change, r.runId), r.request.outputPath) : join4(changesDirOf(this.o.root), r.request.change, r.request.outputPath);
|
|
1551
1769
|
// Atomic: a step reading run.json while `handoff done` writes it never sees half a file.
|
|
1552
1770
|
async save(run) {
|
|
1553
1771
|
const dir = this.runDir(run.request.change, run.runId);
|
|
1554
|
-
await writeFileAtomic(
|
|
1772
|
+
await writeFileAtomic(join4(dir, "run.json"), JSON.stringify(run, null, 2) + "\n");
|
|
1555
1773
|
return run;
|
|
1556
1774
|
}
|
|
1557
1775
|
/** Where a run's prompt is, relative to the root (`atticus <step> --handoff` prints it). */
|
|
@@ -1563,6 +1781,39 @@ var HandoffRuntime = class _HandoffRuntime {
|
|
|
1563
1781
|
const { outputPath, change } = run.request;
|
|
1564
1782
|
return run.request.outputIn === "run" ? `${runsRel}/${change}/${run.runId}/${outputPath}` : `${changesRel}/${change}/${outputPath}`;
|
|
1565
1783
|
}
|
|
1784
|
+
/**
|
|
1785
|
+
* A batched hand-off's prompt (ADR 0185): one section to write per part, then each part's own
|
|
1786
|
+
* prompt, whole and in order, under its name. It ends with the completion command.
|
|
1787
|
+
*/
|
|
1788
|
+
static promptForBatch(run, parts, changesRel = "openspec/changes", runsRel = ".runs") {
|
|
1789
|
+
const { agent, change } = run.request;
|
|
1790
|
+
const where = _HandoffRuntime.outputRel(run, changesRel, runsRel);
|
|
1791
|
+
const n = parts.length;
|
|
1792
|
+
return [
|
|
1793
|
+
`# ${agent.id}: ${n} parts for change ${change}, in one answer`,
|
|
1794
|
+
"",
|
|
1795
|
+
agent.description,
|
|
1796
|
+
"",
|
|
1797
|
+
`Write the result to ${where}. Change no other files.`,
|
|
1798
|
+
"",
|
|
1799
|
+
`The file holds one section per part below. Start each section with a line that holds only "=== " and the part's name, then write that part's answer exactly as the part asks for it, and nothing else. Answer each part from its own instructions and its own data only: the parts are separate. Leave no part out. Atticus checks each section on its own, as if that part were answered alone: a section that is missing or fails its check is asked for again, and the others are kept.`,
|
|
1800
|
+
"",
|
|
1801
|
+
...parts.flatMap((p) => [`=== ${p.name}`, `<the ${p.name} part's answer>`]),
|
|
1802
|
+
"",
|
|
1803
|
+
...parts.flatMap((p, i) => [
|
|
1804
|
+
"---",
|
|
1805
|
+
"",
|
|
1806
|
+
`Part ${i + 1} of ${n}: ${p.name}. Its answer is the section "=== ${p.name}".`,
|
|
1807
|
+
"",
|
|
1808
|
+
p.prompt.trimEnd(),
|
|
1809
|
+
""
|
|
1810
|
+
]),
|
|
1811
|
+
"---",
|
|
1812
|
+
"",
|
|
1813
|
+
`When the file is complete, run: atticus handoff done ${run.runId}`,
|
|
1814
|
+
""
|
|
1815
|
+
].join("\n");
|
|
1816
|
+
}
|
|
1566
1817
|
/** The prompt the person's assistant gets. It ends with the completion command. */
|
|
1567
1818
|
static promptFor(run, changesRel = "openspec/changes", runsRel = ".runs") {
|
|
1568
1819
|
const { agent, pack, outputPath, change } = run.request;
|
|
@@ -1580,15 +1831,93 @@ var HandoffRuntime = class _HandoffRuntime {
|
|
|
1580
1831
|
""
|
|
1581
1832
|
].join("\n");
|
|
1582
1833
|
}
|
|
1834
|
+
/** A run id no run folder of the change has yet, its folder made (claimed) with it. */
|
|
1835
|
+
async freshId(change, now) {
|
|
1836
|
+
return claimRunDir(join4(runsDirOf(this.o.root), change), () => makeRunId(now, ++this.seq));
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Whether `parts` runs that wait together, `tokens` in all, are asked in one prompt (ADR 0185):
|
|
1840
|
+
* only when this runtime batches, for two parts or more, within the size a batch may hold.
|
|
1841
|
+
*/
|
|
1842
|
+
canBatch(parts, tokens) {
|
|
1843
|
+
return !!this.o.batch && parts >= 2 && tokens <= (this.o.batchMaxTokens ?? DEFAULT_BATCH_MAX_TOKENS);
|
|
1844
|
+
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Ask several waiting runs of one change in one prompt (ADR 0185). Each part keeps its own run,
|
|
1847
|
+
* prompt, pack and answer file, and can still be answered alone; the batch links them, and each
|
|
1848
|
+
* part names the batch. The batch's pack is its parts' packs together: their sources, each id
|
|
1849
|
+
* under its part's name, and their sizes added up. Run it (`run`) to hand it over.
|
|
1850
|
+
*/
|
|
1851
|
+
async prepareBatch(o) {
|
|
1852
|
+
checkAllowed(this.kind, o.agent);
|
|
1853
|
+
if (o.parts.length < 2)
|
|
1854
|
+
throw new RuntimeError("a batched hand-off needs two parts or more");
|
|
1855
|
+
const twice = o.parts.find((p, i) => o.parts.findIndex((x) => x.name === p.name) !== i);
|
|
1856
|
+
if (twice)
|
|
1857
|
+
throw new RuntimeError(`a batched hand-off can't have two parts named ${twice.name}`);
|
|
1858
|
+
const parts = [];
|
|
1859
|
+
for (const p of o.parts) {
|
|
1860
|
+
const run2 = await this.load(p.runId);
|
|
1861
|
+
if (run2.request.change !== o.change)
|
|
1862
|
+
throw new RuntimeError(`run ${p.runId} isn't a run of ${o.change}`);
|
|
1863
|
+
if (run2.state !== "waiting" && run2.state !== "prepared")
|
|
1864
|
+
throw new RuntimeError(`run ${p.runId} is ${run2.state}: only a run still waiting for its answer can be batched`);
|
|
1865
|
+
if (run2.batch)
|
|
1866
|
+
throw new RuntimeError(`run ${p.runId} is already asked in ${run2.batch}`);
|
|
1867
|
+
parts.push({ name: p.name, run: run2 });
|
|
1868
|
+
}
|
|
1869
|
+
const now = this.now();
|
|
1870
|
+
const runId = await this.freshId(o.change, now);
|
|
1871
|
+
const manifests = parts.map((p) => ({ name: p.name, m: p.run.request.pack.manifest }));
|
|
1872
|
+
const totalTokens = manifests.reduce((n, x) => n + x.m.totalTokens, 0);
|
|
1873
|
+
const agent = { ...o.agent, tokenBudget: Math.max(o.agent.tokenBudget, totalTokens) };
|
|
1874
|
+
const run = {
|
|
1875
|
+
runId,
|
|
1876
|
+
request: {
|
|
1877
|
+
change: o.change,
|
|
1878
|
+
agent,
|
|
1879
|
+
pack: { prompt: "", manifest: manifests[0].m },
|
|
1880
|
+
outputPath: BATCH_OUTPUT,
|
|
1881
|
+
outputIn: "run",
|
|
1882
|
+
...o.stepClass ? { stepClass: o.stepClass } : {}
|
|
1883
|
+
},
|
|
1884
|
+
runtime: this.kind,
|
|
1885
|
+
state: "prepared",
|
|
1886
|
+
attempt: 1,
|
|
1887
|
+
startedAt: now.toISOString(),
|
|
1888
|
+
errors: [],
|
|
1889
|
+
stepClass: o.stepClass ?? stepClassOf({ agent }),
|
|
1890
|
+
parts: parts.map((p) => ({ name: p.name, runId: p.run.runId }))
|
|
1891
|
+
};
|
|
1892
|
+
const prompt = _HandoffRuntime.promptForBatch(run, parts.map((p) => ({ name: p.name, prompt: p.run.request.pack.prompt })), changesRelOf(this.o.root), runsRelOf(this.o.root));
|
|
1893
|
+
const manifest = {
|
|
1894
|
+
schemaVersion: 1,
|
|
1895
|
+
change: o.change,
|
|
1896
|
+
agent: o.agent.id,
|
|
1897
|
+
createdAt: now.toISOString(),
|
|
1898
|
+
budget: manifests.reduce((n, x) => n + x.m.budget, 0),
|
|
1899
|
+
totalTokens,
|
|
1900
|
+
sources: manifests.flatMap((x) => x.m.sources.map((s) => ({ ...s, id: `${x.name}/${s.id}` }))),
|
|
1901
|
+
dropped: manifests.flatMap((x) => x.m.dropped.map((d) => ({ ...d, id: `${x.name}/${d.id}` }))),
|
|
1902
|
+
promptSha256: sha2562(prompt)
|
|
1903
|
+
};
|
|
1904
|
+
run.request.pack = { prompt, manifest };
|
|
1905
|
+
const dir = this.runDir(o.change, runId);
|
|
1906
|
+
await mkdir4(dir, { recursive: true });
|
|
1907
|
+
await writeFile3(join4(dir, "prompt.md"), prompt, "utf8");
|
|
1908
|
+
await writeFile3(join4(dir, "context-manifest.json"), JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
1909
|
+
await this.save(run);
|
|
1910
|
+
for (const p of parts)
|
|
1911
|
+
await this.save({ ...p.run, batch: runId });
|
|
1912
|
+
return run;
|
|
1913
|
+
}
|
|
1583
1914
|
async prepare(request) {
|
|
1584
1915
|
checkAllowed(this.kind, request.agent);
|
|
1585
1916
|
if (request.pack.manifest.totalTokens > request.agent.tokenBudget) {
|
|
1586
1917
|
throw new RuntimeError(`context pack (${request.pack.manifest.totalTokens} tokens) is over agent ${request.agent.id}'s budget of ${request.agent.tokenBudget}`);
|
|
1587
1918
|
}
|
|
1588
1919
|
const now = this.now();
|
|
1589
|
-
|
|
1590
|
-
while (await stat(this.runDir(request.change, runId)).catch(() => void 0))
|
|
1591
|
-
runId = makeRunId(now, ++this.seq);
|
|
1920
|
+
const runId = await this.freshId(request.change, now);
|
|
1592
1921
|
const run = {
|
|
1593
1922
|
runId,
|
|
1594
1923
|
request,
|
|
@@ -1601,9 +1930,9 @@ var HandoffRuntime = class _HandoffRuntime {
|
|
|
1601
1930
|
stepClass: stepClassOf(request)
|
|
1602
1931
|
};
|
|
1603
1932
|
const dir = this.runDir(request.change, run.runId);
|
|
1604
|
-
await
|
|
1605
|
-
await writeFile3(
|
|
1606
|
-
await writeFile3(
|
|
1933
|
+
await mkdir4(dir, { recursive: true });
|
|
1934
|
+
await writeFile3(join4(dir, "prompt.md"), _HandoffRuntime.promptFor(run, changesRelOf(this.o.root), runsRelOf(this.o.root)), "utf8");
|
|
1935
|
+
await writeFile3(join4(dir, "context-manifest.json"), JSON.stringify(request.pack.manifest, null, 2) + "\n", "utf8");
|
|
1607
1936
|
return this.save(run);
|
|
1608
1937
|
}
|
|
1609
1938
|
/** Hand the prompt to the assistant; Atticus now waits. */
|
|
@@ -1622,6 +1951,8 @@ var HandoffRuntime = class _HandoffRuntime {
|
|
|
1622
1951
|
async done(runId, o = {}) {
|
|
1623
1952
|
let run = await this.load(runId);
|
|
1624
1953
|
const model = o.model?.trim();
|
|
1954
|
+
if (run.parts?.length)
|
|
1955
|
+
return this.doneBatch(run, model);
|
|
1625
1956
|
if (model)
|
|
1626
1957
|
run = { ...run, model };
|
|
1627
1958
|
if (run.state === "failed") {
|
|
@@ -1645,8 +1976,61 @@ var HandoffRuntime = class _HandoffRuntime {
|
|
|
1645
1976
|
return run;
|
|
1646
1977
|
if (this.now().getTime() - info.mtimeMs < (this.o.stableMs ?? 3e4))
|
|
1647
1978
|
return run;
|
|
1979
|
+
if (run.parts?.length)
|
|
1980
|
+
return this.doneBatch(run);
|
|
1648
1981
|
return this.validate(transition(run, "produced", this.now()));
|
|
1649
1982
|
}
|
|
1983
|
+
/**
|
|
1984
|
+
* A batched hand-off's Done (ADR 0185): its answer is split into its parts' sections, each
|
|
1985
|
+
* written to its part's own answer file and checked there as that part's Done would check it.
|
|
1986
|
+
* A part that passes is in; a missing, doubled or failing section fails only its part, which can
|
|
1987
|
+
* be fixed in the batch (Done again checks only the parts still out) or answered alone. The
|
|
1988
|
+
* batch passes once every part is in; until then it fails, naming each part that is out and why.
|
|
1989
|
+
*/
|
|
1990
|
+
async doneBatch(start, model) {
|
|
1991
|
+
let run = start;
|
|
1992
|
+
if (run.state === "validated" || run.state === "cancelled")
|
|
1993
|
+
return run;
|
|
1994
|
+
if (run.state === "failed")
|
|
1995
|
+
run = transition(transition(run, "prepared", this.now(), { attempt: run.attempt + 1, errors: [] }), "waiting", this.now());
|
|
1996
|
+
if (model)
|
|
1997
|
+
run = { ...run, model };
|
|
1998
|
+
const output = await readFile3(this.outputFile(run), "utf8").catch(() => void 0);
|
|
1999
|
+
if (output === void 0)
|
|
2000
|
+
return this.save(transition(run, "failed", this.now(), {
|
|
2001
|
+
errors: [`${run.request.outputPath} was not written`]
|
|
2002
|
+
}));
|
|
2003
|
+
const names = run.parts.map((p) => p.name);
|
|
2004
|
+
const sections = batchSections(output);
|
|
2005
|
+
if (!names.some((n) => sections.has(n)))
|
|
2006
|
+
return this.save(transition(run, "failed", this.now(), {
|
|
2007
|
+
output,
|
|
2008
|
+
errors: [
|
|
2009
|
+
`${run.request.outputPath} has no section of any part. Start each part's answer with its own line "=== <part>": ${names.map((n) => `=== ${n}`).join(", ")}.`
|
|
2010
|
+
]
|
|
2011
|
+
}));
|
|
2012
|
+
run = transition(run, "produced", this.now(), { output });
|
|
2013
|
+
const errors = [];
|
|
2014
|
+
const runsRel = runsRelOf(this.o.root);
|
|
2015
|
+
for (const p of run.parts) {
|
|
2016
|
+
const part = await this.load(p.runId);
|
|
2017
|
+
if (part.state === "validated" || part.state === "cancelled")
|
|
2018
|
+
continue;
|
|
2019
|
+
const s = sections.get(p.name);
|
|
2020
|
+
const alone = `answer that part alone: its prompt is ${_HandoffRuntime.promptRel(part, runsRel)}.`;
|
|
2021
|
+
if (!s || s.twice) {
|
|
2022
|
+
errors.push(s ? `${p.name}: the answer has two "=== ${p.name}" sections. Keep one, or ${alone}` : `${p.name}: the answer has no "=== ${p.name}" section. Add it, or ${alone}`);
|
|
2023
|
+
const why = `The batched answer (run ${run.runId}) has ${s ? 'two "=== ' + p.name + '" sections' : 'no "=== ' + p.name + '" section'}. Answer this part here.`;
|
|
2024
|
+
await this.save(part.state === "failed" ? { ...part, errors: [why] } : transition(part, "failed", this.now(), { errors: [why] }));
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
2027
|
+
await writeFile3(this.outputFile(part), s.text, "utf8");
|
|
2028
|
+
const r = await this.done(p.runId, model ? { model } : {});
|
|
2029
|
+
if (r.state !== "validated")
|
|
2030
|
+
errors.push(...r.errors.map((e) => `${p.name}: ${e}`));
|
|
2031
|
+
}
|
|
2032
|
+
return this.save(errors.length ? transition(run, "failed", this.now(), { errors }) : transition(run, "validated", this.now()));
|
|
2033
|
+
}
|
|
1650
2034
|
/** Retry after a failure: a fresh attempt with the same request. */
|
|
1651
2035
|
async retry(runId) {
|
|
1652
2036
|
const run = await this.load(runId);
|
|
@@ -1667,7 +2051,7 @@ var HandoffRuntime = class _HandoffRuntime {
|
|
|
1667
2051
|
throw new RuntimeError(`not a run id: ${runId}`);
|
|
1668
2052
|
const runs = runsDirOf(this.o.root);
|
|
1669
2053
|
for (const change of await readdir3(runs).catch(() => [])) {
|
|
1670
|
-
const f =
|
|
2054
|
+
const f = join4(runs, change, runId, "run.json");
|
|
1671
2055
|
const text = await readFile3(f, "utf8").catch(() => void 0);
|
|
1672
2056
|
if (text)
|
|
1673
2057
|
return JSON.parse(text);
|
|
@@ -1681,12 +2065,12 @@ async function pruneRuns(root, days, now = /* @__PURE__ */ new Date(), only) {
|
|
|
1681
2065
|
const cutoff = now.getTime() - days * 864e5;
|
|
1682
2066
|
const changes = only ? [only] : await readdir3(runs).catch(() => []);
|
|
1683
2067
|
for (const change of changes) {
|
|
1684
|
-
for (const runId of await readdir3(
|
|
1685
|
-
const f =
|
|
2068
|
+
for (const runId of await readdir3(join4(runs, change)).catch(() => [])) {
|
|
2069
|
+
const f = join4(runs, change, runId, "run.json");
|
|
1686
2070
|
const text = await readFile3(f, "utf8").catch(() => void 0);
|
|
1687
2071
|
const started = text ? Date.parse(JSON.parse(text).startedAt) : NaN;
|
|
1688
2072
|
if (!Number.isNaN(started) && started < cutoff) {
|
|
1689
|
-
await rm(
|
|
2073
|
+
await rm(join4(runs, change, runId), { recursive: true, force: true });
|
|
1690
2074
|
removed.push(`${change}/${runId}`);
|
|
1691
2075
|
}
|
|
1692
2076
|
}
|
|
@@ -1695,12 +2079,1070 @@ async function pruneRuns(root, days, now = /* @__PURE__ */ new Date(), only) {
|
|
|
1695
2079
|
}
|
|
1696
2080
|
|
|
1697
2081
|
// packages/ai/dist/factory.js
|
|
1698
|
-
import {
|
|
1699
|
-
import {
|
|
2082
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
2083
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2084
|
+
import { mkdir as mkdir6, mkdtemp, readFile as readFile5, rm as rm2, stat as stat3, writeFile as writeFile5 } from "node:fs/promises";
|
|
1700
2085
|
import { tmpdir } from "node:os";
|
|
1701
|
-
import { dirname, join as
|
|
2086
|
+
import { dirname as dirname2, join as join6 } from "node:path";
|
|
2087
|
+
|
|
2088
|
+
// packages/ai/dist/copilot-acp.js
|
|
2089
|
+
import { spawn } from "node:child_process";
|
|
2090
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
2091
|
+
import { mkdir as mkdir5, readFile as readFile4, stat as stat2, writeFile as writeFile4 } from "node:fs/promises";
|
|
2092
|
+
import { basename, dirname, isAbsolute, join as join5, relative, resolve, sep } from "node:path";
|
|
2093
|
+
import { createInterface } from "node:readline";
|
|
2094
|
+
|
|
2095
|
+
// packages/ai/dist/acp.js
|
|
2096
|
+
var ACP_PROTOCOL_VERSION = 1;
|
|
2097
|
+
var ACP_METHODS = {
|
|
2098
|
+
initialize: "initialize",
|
|
2099
|
+
authenticate: "authenticate",
|
|
2100
|
+
sessionNew: "session/new",
|
|
2101
|
+
sessionSetModel: "session/set_model",
|
|
2102
|
+
sessionPrompt: "session/prompt",
|
|
2103
|
+
sessionCancel: "session/cancel",
|
|
2104
|
+
sessionUpdate: "session/update",
|
|
2105
|
+
requestPermission: "session/request_permission",
|
|
2106
|
+
readTextFile: "fs/read_text_file",
|
|
2107
|
+
writeTextFile: "fs/write_text_file",
|
|
2108
|
+
terminalCreate: "terminal/create",
|
|
2109
|
+
terminalOutput: "terminal/output",
|
|
2110
|
+
terminalWaitForExit: "terminal/wait_for_exit",
|
|
2111
|
+
terminalKill: "terminal/kill",
|
|
2112
|
+
terminalRelease: "terminal/release"
|
|
2113
|
+
};
|
|
2114
|
+
var ACP_ERRORS = {
|
|
2115
|
+
invalidParams: -32602,
|
|
2116
|
+
methodNotFound: -32601,
|
|
2117
|
+
internal: -32603,
|
|
2118
|
+
authRequired: -32e3
|
|
2119
|
+
};
|
|
2120
|
+
var AcpRpcError = class extends Error {
|
|
2121
|
+
code;
|
|
2122
|
+
data;
|
|
2123
|
+
constructor(code, message, data) {
|
|
2124
|
+
super(message);
|
|
2125
|
+
this.code = code;
|
|
2126
|
+
this.data = data;
|
|
2127
|
+
this.name = "AcpRpcError";
|
|
2128
|
+
}
|
|
2129
|
+
};
|
|
2130
|
+
var AcpClient = class {
|
|
2131
|
+
t;
|
|
2132
|
+
h;
|
|
2133
|
+
tap;
|
|
2134
|
+
next = 1;
|
|
2135
|
+
pending = /* @__PURE__ */ new Map();
|
|
2136
|
+
closed;
|
|
2137
|
+
constructor(t, h, tap = () => {
|
|
2138
|
+
}) {
|
|
2139
|
+
this.t = t;
|
|
2140
|
+
this.h = h;
|
|
2141
|
+
this.tap = tap;
|
|
2142
|
+
t.onLine((line) => this.receive(line));
|
|
2143
|
+
t.onClose((why) => {
|
|
2144
|
+
this.closed = why;
|
|
2145
|
+
for (const p of this.pending.values())
|
|
2146
|
+
p.reject(new AcpRpcError(ACP_ERRORS.internal, why));
|
|
2147
|
+
this.pending.clear();
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
out(message) {
|
|
2151
|
+
const m = { jsonrpc: "2.0", ...message };
|
|
2152
|
+
this.tap("out", m);
|
|
2153
|
+
this.t.send(JSON.stringify(m));
|
|
2154
|
+
}
|
|
2155
|
+
request(method, params) {
|
|
2156
|
+
if (this.closed)
|
|
2157
|
+
return Promise.reject(new AcpRpcError(ACP_ERRORS.internal, this.closed));
|
|
2158
|
+
const id = this.next++;
|
|
2159
|
+
return new Promise((resolve2, reject) => {
|
|
2160
|
+
this.pending.set(id, { resolve: resolve2, reject });
|
|
2161
|
+
this.out({ id, method, params });
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
notify(method, params) {
|
|
2165
|
+
if (!this.closed)
|
|
2166
|
+
this.out({ method, params });
|
|
2167
|
+
}
|
|
2168
|
+
receive(line) {
|
|
2169
|
+
if (!line.trim())
|
|
2170
|
+
return;
|
|
2171
|
+
let m;
|
|
2172
|
+
try {
|
|
2173
|
+
m = JSON.parse(line);
|
|
2174
|
+
} catch {
|
|
2175
|
+
this.tap("in", { notJson: line.slice(0, 500) });
|
|
2176
|
+
return;
|
|
2177
|
+
}
|
|
2178
|
+
this.tap("in", m);
|
|
2179
|
+
if (m.method === void 0 && m.id !== void 0) {
|
|
2180
|
+
const p = this.pending.get(Number(m.id));
|
|
2181
|
+
if (!p)
|
|
2182
|
+
return;
|
|
2183
|
+
this.pending.delete(Number(m.id));
|
|
2184
|
+
if (m.error)
|
|
2185
|
+
p.reject(new AcpRpcError(m.error.code, m.error.message, m.error.data));
|
|
2186
|
+
else
|
|
2187
|
+
p.resolve(m.result ?? null);
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
if (m.method === ACP_METHODS.sessionUpdate) {
|
|
2191
|
+
const u = m.params?.update;
|
|
2192
|
+
if (u && typeof u === "object")
|
|
2193
|
+
this.h.update(u);
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
if (m.id === void 0)
|
|
2197
|
+
return;
|
|
2198
|
+
const id = m.id;
|
|
2199
|
+
void this.answer(m.method ?? "", m.params ?? {}).then((result) => this.out({ id, result }), (e) => this.out({
|
|
2200
|
+
id,
|
|
2201
|
+
error: {
|
|
2202
|
+
code: e instanceof AcpRpcError ? e.code : ACP_ERRORS.invalidParams,
|
|
2203
|
+
message: e.message
|
|
2204
|
+
}
|
|
2205
|
+
}));
|
|
2206
|
+
}
|
|
2207
|
+
answer(method, p) {
|
|
2208
|
+
const s = (k) => typeof p[k] === "string" ? p[k] : "";
|
|
2209
|
+
switch (method) {
|
|
2210
|
+
case ACP_METHODS.readTextFile:
|
|
2211
|
+
return this.h.readTextFile({
|
|
2212
|
+
path: s("path"),
|
|
2213
|
+
...typeof p.line === "number" ? { line: p.line } : {},
|
|
2214
|
+
...typeof p.limit === "number" ? { limit: p.limit } : {}
|
|
2215
|
+
});
|
|
2216
|
+
case ACP_METHODS.writeTextFile:
|
|
2217
|
+
return this.h.writeTextFile({ path: s("path"), content: s("content") });
|
|
2218
|
+
case ACP_METHODS.requestPermission:
|
|
2219
|
+
return this.h.requestPermission({
|
|
2220
|
+
toolCall: p.toolCall ?? {},
|
|
2221
|
+
options: Array.isArray(p.options) ? p.options : []
|
|
2222
|
+
});
|
|
2223
|
+
case ACP_METHODS.terminalCreate:
|
|
2224
|
+
return this.h.terminalCreate({
|
|
2225
|
+
command: s("command"),
|
|
2226
|
+
...Array.isArray(p.args) ? { args: p.args.map(String) } : {},
|
|
2227
|
+
...typeof p.cwd === "string" ? { cwd: p.cwd } : {},
|
|
2228
|
+
...Array.isArray(p.env) ? { env: p.env } : {},
|
|
2229
|
+
...typeof p.outputByteLimit === "number" ? { outputByteLimit: p.outputByteLimit } : {}
|
|
2230
|
+
});
|
|
2231
|
+
case ACP_METHODS.terminalOutput:
|
|
2232
|
+
return this.h.terminalOutput({ terminalId: s("terminalId") });
|
|
2233
|
+
case ACP_METHODS.terminalWaitForExit:
|
|
2234
|
+
return this.h.terminalWaitForExit({ terminalId: s("terminalId") });
|
|
2235
|
+
case ACP_METHODS.terminalKill:
|
|
2236
|
+
return this.h.terminalKill({ terminalId: s("terminalId") });
|
|
2237
|
+
case ACP_METHODS.terminalRelease:
|
|
2238
|
+
return this.h.terminalRelease({ terminalId: s("terminalId") });
|
|
2239
|
+
default:
|
|
2240
|
+
return Promise.reject(new AcpRpcError(ACP_ERRORS.methodNotFound, `Atticus doesn't offer ${method}`));
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
// ------------------------------------------------------------ the agent's methods
|
|
2244
|
+
initialize(clientInfo) {
|
|
2245
|
+
return this.request(ACP_METHODS.initialize, {
|
|
2246
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
2247
|
+
clientCapabilities: { fs: { readTextFile: true, writeTextFile: true }, terminal: true },
|
|
2248
|
+
clientInfo
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
newSession(cwd) {
|
|
2252
|
+
return this.request(ACP_METHODS.sessionNew, { cwd, mcpServers: [] });
|
|
2253
|
+
}
|
|
2254
|
+
setModel(sessionId, modelId) {
|
|
2255
|
+
return this.request(ACP_METHODS.sessionSetModel, { sessionId, modelId });
|
|
2256
|
+
}
|
|
2257
|
+
prompt(sessionId, text) {
|
|
2258
|
+
return this.request(ACP_METHODS.sessionPrompt, {
|
|
2259
|
+
sessionId,
|
|
2260
|
+
prompt: [{ type: "text", text }]
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
cancel(sessionId) {
|
|
2264
|
+
this.notify(ACP_METHODS.sessionCancel, { sessionId });
|
|
2265
|
+
}
|
|
2266
|
+
};
|
|
2267
|
+
function chunkText(u) {
|
|
2268
|
+
const c = u.content;
|
|
2269
|
+
return c?.type === "text" && typeof c.text === "string" ? c.text : void 0;
|
|
2270
|
+
}
|
|
2271
|
+
var UPDATE = {
|
|
2272
|
+
message: "agent_message_chunk",
|
|
2273
|
+
thought: "agent_thought_chunk",
|
|
2274
|
+
toolCall: "tool_call",
|
|
2275
|
+
toolCallUpdate: "tool_call_update",
|
|
2276
|
+
plan: "plan"
|
|
2277
|
+
};
|
|
2278
|
+
function usageOf(u) {
|
|
2279
|
+
if (!u)
|
|
2280
|
+
return void 0;
|
|
2281
|
+
const n = (...ks) => {
|
|
2282
|
+
for (const k of ks)
|
|
2283
|
+
if (typeof u[k] === "number")
|
|
2284
|
+
return u[k];
|
|
2285
|
+
return void 0;
|
|
2286
|
+
};
|
|
2287
|
+
const i = n("inputTokens", "input_tokens", "promptTokens");
|
|
2288
|
+
const o = n("outputTokens", "output_tokens", "completionTokens");
|
|
2289
|
+
return i === void 0 && o === void 0 ? void 0 : {
|
|
2290
|
+
inputTokens: Math.max(0, Math.round(i ?? 0)),
|
|
2291
|
+
outputTokens: Math.max(0, Math.round(o ?? 0))
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
var isAuthRequired = (e) => e instanceof AcpRpcError && (e.code === ACP_ERRORS.authRequired || /auth|sign.?in|log.?in/i.test(e.message));
|
|
2295
|
+
|
|
2296
|
+
// packages/ai/dist/copilot-acp.js
|
|
2297
|
+
function words(line) {
|
|
2298
|
+
const out = [];
|
|
2299
|
+
let cur = "";
|
|
2300
|
+
let quote;
|
|
2301
|
+
let any = false;
|
|
2302
|
+
let meta = false;
|
|
2303
|
+
for (const ch of line) {
|
|
2304
|
+
if (quote) {
|
|
2305
|
+
if (ch === quote)
|
|
2306
|
+
quote = void 0;
|
|
2307
|
+
else
|
|
2308
|
+
cur += ch;
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2311
|
+
if (ch === '"' || ch === "'") {
|
|
2312
|
+
quote = ch;
|
|
2313
|
+
any = true;
|
|
2314
|
+
continue;
|
|
2315
|
+
}
|
|
2316
|
+
if (/\s/.test(ch)) {
|
|
2317
|
+
if (cur || any)
|
|
2318
|
+
out.push(cur);
|
|
2319
|
+
cur = "";
|
|
2320
|
+
any = false;
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
2323
|
+
if ("|&;<>`$(){}\n".includes(ch))
|
|
2324
|
+
meta = true;
|
|
2325
|
+
cur += ch;
|
|
2326
|
+
}
|
|
2327
|
+
if (cur || any)
|
|
2328
|
+
out.push(cur);
|
|
2329
|
+
return { words: out, meta: meta || quote !== void 0 };
|
|
2330
|
+
}
|
|
2331
|
+
function commandLineOf(line) {
|
|
2332
|
+
const w = words(line.trim()).words;
|
|
2333
|
+
if (!w.length)
|
|
2334
|
+
throw new RuntimeConfigLikeError("copilotAcp.command is empty");
|
|
2335
|
+
const args = w.slice(1);
|
|
2336
|
+
return { command: w[0], args: args.includes("--acp") ? args : [...args, "--acp"] };
|
|
2337
|
+
}
|
|
2338
|
+
var RuntimeConfigLikeError = class extends RuntimeError {
|
|
2339
|
+
};
|
|
2340
|
+
var PERSONS = [
|
|
2341
|
+
// What only a person does is personOnlyWhy's list (person-only.ts), shared with the CLI.
|
|
2342
|
+
{
|
|
2343
|
+
test: (a) => a[0] === "map" && a.includes("--all"),
|
|
2344
|
+
why: "this session is one step of atticus map --all; Atticus runs the loop, not the session"
|
|
2345
|
+
},
|
|
2346
|
+
{
|
|
2347
|
+
test: (a) => ["ui", "serve", "mcp"].includes(a[0] ?? ""),
|
|
2348
|
+
why: "a server that keeps running isn't a step's command"
|
|
2349
|
+
},
|
|
2350
|
+
{
|
|
2351
|
+
test: (a) => a.some((x) => x === "--root" || x.startsWith("--root=")),
|
|
2352
|
+
why: "the session works in this metarepo; --root points elsewhere"
|
|
2353
|
+
}
|
|
2354
|
+
];
|
|
2355
|
+
var ATTICUS_NAMES = /* @__PURE__ */ new Set(["atticus", "atticus.cmd", "atticus.exe"]);
|
|
2356
|
+
function atticusArgvOf(command, args = []) {
|
|
2357
|
+
const head = words(command.trim());
|
|
2358
|
+
if (head.meta)
|
|
2359
|
+
return {
|
|
2360
|
+
refused: `Atticus runs no shell here, so a command line with | & ; < > $ or backquotes is refused: ${short(command)}. Run one atticus command at a time.`
|
|
2361
|
+
};
|
|
2362
|
+
const all = [...head.words, ...args];
|
|
2363
|
+
const program = all[0] ?? "";
|
|
2364
|
+
if (!ATTICUS_NAMES.has(program.toLowerCase()))
|
|
2365
|
+
return {
|
|
2366
|
+
refused: `Atticus runs only atticus commands in this session (atticus <command> <arguments>); ${short([program, ...all.slice(1)].join(" "))} is refused. Read files with your file tools; write only under facts/ or the step's result file.`
|
|
2367
|
+
};
|
|
2368
|
+
const argv = all.slice(1);
|
|
2369
|
+
if (!argv.length)
|
|
2370
|
+
return { refused: "atticus needs a command (atticus next, atticus verify <repo>, ...)" };
|
|
2371
|
+
const named = [...withoutLegacy(argv)];
|
|
2372
|
+
for (const p of PERSONS)
|
|
2373
|
+
if (p.test(named))
|
|
2374
|
+
return { refused: `Refused: ${p.why}.` };
|
|
2375
|
+
const hidden = argv[0] === "legacy" && argv[1]?.startsWith("-") && argv.slice(2).some((x) => !x.startsWith("-"));
|
|
2376
|
+
if (hidden || argv[0].startsWith("-") && !["--help", "--version", "-h"].includes(argv[0]))
|
|
2377
|
+
return {
|
|
2378
|
+
refused: `Put the command first: atticus <command> [arguments] (got ${short(argv.join(" "))})`
|
|
2379
|
+
};
|
|
2380
|
+
const person = personOnlyWhy(argv);
|
|
2381
|
+
if (person)
|
|
2382
|
+
return { refused: `Refused: ${person}; Atticus lets a session act as no one.` };
|
|
2383
|
+
return { argv };
|
|
2384
|
+
}
|
|
2385
|
+
var short = (s, n = 120) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
|
|
2386
|
+
function real(p) {
|
|
2387
|
+
let cur = resolve(p);
|
|
2388
|
+
const rest = [];
|
|
2389
|
+
for (; ; ) {
|
|
2390
|
+
try {
|
|
2391
|
+
return join5(realpathSync.native(cur), ...rest.reverse());
|
|
2392
|
+
} catch {
|
|
2393
|
+
const up = dirname(cur);
|
|
2394
|
+
if (up === cur)
|
|
2395
|
+
return resolve(p);
|
|
2396
|
+
rest.push(basename(cur));
|
|
2397
|
+
cur = up;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
var sameOrUnder = (root, p) => {
|
|
2402
|
+
const r = relative(root, p);
|
|
2403
|
+
return r === "" || !r.startsWith("..") && !isAbsolute(r);
|
|
2404
|
+
};
|
|
2405
|
+
var caseOf = (p) => process.platform === "win32" ? p.toLowerCase() : p;
|
|
2406
|
+
var Paths = class {
|
|
2407
|
+
p;
|
|
2408
|
+
reads;
|
|
2409
|
+
writeDirs;
|
|
2410
|
+
files;
|
|
2411
|
+
constructor(p) {
|
|
2412
|
+
this.p = p;
|
|
2413
|
+
this.reads = p.readRoots.map((r) => caseOf(real(r)));
|
|
2414
|
+
this.writeDirs = p.writeRoots.map((r) => caseOf(real(r)));
|
|
2415
|
+
this.files = (p.writeFiles ?? []).map((f) => caseOf(real(f)));
|
|
2416
|
+
}
|
|
2417
|
+
readable(path) {
|
|
2418
|
+
if (!path || !isAbsolute(path))
|
|
2419
|
+
return `the path must be absolute (got ${short(path)})`;
|
|
2420
|
+
const r = caseOf(real(path));
|
|
2421
|
+
return this.reads.some((x) => sameOrUnder(x, r)) ? void 0 : `Atticus lets this session read only under the metarepo and its clones; ${short(path)} is outside`;
|
|
2422
|
+
}
|
|
2423
|
+
writable(path) {
|
|
2424
|
+
if (!path || !isAbsolute(path))
|
|
2425
|
+
return `the path must be absolute (got ${short(path)})`;
|
|
2426
|
+
const r = caseOf(real(path));
|
|
2427
|
+
if (r.split(sep).includes(".git"))
|
|
2428
|
+
return `Atticus never lets a session write in .git (${short(path)})`;
|
|
2429
|
+
const parts = r.split(sep).slice(/^[A-Za-z]:$/.test(r.split(sep)[0] ?? "") ? 1 : 0);
|
|
2430
|
+
if (parts.some((x) => x !== "." && x !== ".." && (/[. ]$/.test(x) || x.includes(":"))))
|
|
2431
|
+
return `Atticus never lets a session write a name that ends in a dot or space or names a stream (${short(path)})`;
|
|
2432
|
+
if (isPersonsFactsFile(basename(r)))
|
|
2433
|
+
return `Atticus never lets a session write a person's record (${short(path)}: an audit's answers or a person's corrections)`;
|
|
2434
|
+
if (this.files.includes(r) || this.writeDirs.some((x) => sameOrUnder(x, r)))
|
|
2435
|
+
return void 0;
|
|
2436
|
+
const base = this.p.readRoots[0] ?? "";
|
|
2437
|
+
const rel = (x) => {
|
|
2438
|
+
const r2 = relative(base, x);
|
|
2439
|
+
return r2 && !r2.startsWith("..") && !isAbsolute(r2) ? r2.split(sep).join("/") : x;
|
|
2440
|
+
};
|
|
2441
|
+
const where = [
|
|
2442
|
+
...this.p.writeRoots.map((d) => `${rel(d)}/`),
|
|
2443
|
+
...(this.p.writeFiles ?? []).map(rel)
|
|
2444
|
+
].join(", ");
|
|
2445
|
+
return `Atticus lets this session write only its step's result: it writes only under ${where || "nothing"}; ${short(path)} is refused`;
|
|
2446
|
+
}
|
|
2447
|
+
};
|
|
2448
|
+
var acpPathsOf = (p) => new Paths(p);
|
|
2449
|
+
var factsOnly = (root, factsDir) => ({
|
|
2450
|
+
readRoots: readRootsOf(root),
|
|
2451
|
+
writeRoots: [factsDir]
|
|
2452
|
+
});
|
|
2453
|
+
function readRootsOf(root) {
|
|
2454
|
+
const out = [root];
|
|
2455
|
+
const state = join5(stateDirOf(root), "workspace");
|
|
2456
|
+
if (!sameOrUnder(resolve(root), resolve(state)))
|
|
2457
|
+
out.push(state);
|
|
2458
|
+
return out;
|
|
2459
|
+
}
|
|
2460
|
+
var needsShell = (command, platform = process.platform) => platform === "win32" && !/\.(exe|com)$/i.test(command);
|
|
2461
|
+
var quoteForCmd = (a) => /[\s"]/.test(a) ? `"${a.replace(/"/g, '""')}"` : a;
|
|
2462
|
+
function killTree(child, platform = process.platform, run = spawn) {
|
|
2463
|
+
if (platform === "win32" && child.pid) {
|
|
2464
|
+
try {
|
|
2465
|
+
const k = run("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
2466
|
+
windowsHide: true,
|
|
2467
|
+
stdio: "ignore"
|
|
2468
|
+
});
|
|
2469
|
+
k.on("error", () => child.kill());
|
|
2470
|
+
return;
|
|
2471
|
+
} catch {
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
child.kill();
|
|
2475
|
+
}
|
|
2476
|
+
var spawnAcpServer = (o) => {
|
|
2477
|
+
const shell = needsShell(o.command);
|
|
2478
|
+
let child;
|
|
2479
|
+
let err = "";
|
|
2480
|
+
let failed = () => {
|
|
2481
|
+
};
|
|
2482
|
+
let ok = () => {
|
|
2483
|
+
};
|
|
2484
|
+
const started = new Promise((res, rej) => {
|
|
2485
|
+
ok = res;
|
|
2486
|
+
failed = rej;
|
|
2487
|
+
});
|
|
2488
|
+
started.catch(() => void 0);
|
|
2489
|
+
try {
|
|
2490
|
+
child = spawn(shell ? quoteForCmd(o.command) : o.command, shell ? o.args.map(quoteForCmd) : o.args, { cwd: o.cwd, env: o.env, stdio: ["pipe", "pipe", "pipe"], windowsHide: true, shell });
|
|
2491
|
+
} catch (e) {
|
|
2492
|
+
failed(e);
|
|
2493
|
+
const dead = { send: () => {
|
|
2494
|
+
}, onLine: () => {
|
|
2495
|
+
}, onClose: (fn) => fn(String(e)) };
|
|
2496
|
+
return { transport: dead, stderr: () => "", close: async () => {
|
|
2497
|
+
}, started };
|
|
2498
|
+
}
|
|
2499
|
+
const lines = [];
|
|
2500
|
+
const closes = [];
|
|
2501
|
+
let closed;
|
|
2502
|
+
const close = (why) => {
|
|
2503
|
+
if (closed)
|
|
2504
|
+
return;
|
|
2505
|
+
closed = why;
|
|
2506
|
+
for (const f of closes)
|
|
2507
|
+
f(why);
|
|
2508
|
+
};
|
|
2509
|
+
child.on("spawn", () => ok());
|
|
2510
|
+
child.on("error", (e) => {
|
|
2511
|
+
failed(e);
|
|
2512
|
+
close(`the Copilot language server couldn't be run: ${e.message}`);
|
|
2513
|
+
});
|
|
2514
|
+
child.on("exit", (code, signal) => close(`the Copilot language server ended (${code ?? signal ?? "no code"})`));
|
|
2515
|
+
for (const s of [child.stdin, child.stdout, child.stderr])
|
|
2516
|
+
s?.on("error", () => void 0);
|
|
2517
|
+
child.stderr?.setEncoding("utf8").on("data", (d) => {
|
|
2518
|
+
err = (err + d).slice(-64e3);
|
|
2519
|
+
});
|
|
2520
|
+
createInterface({ input: child.stdout }).on("line", (l) => {
|
|
2521
|
+
for (const f of lines)
|
|
2522
|
+
f(l);
|
|
2523
|
+
});
|
|
2524
|
+
return {
|
|
2525
|
+
started,
|
|
2526
|
+
stderr: () => err,
|
|
2527
|
+
transport: {
|
|
2528
|
+
send: (t) => {
|
|
2529
|
+
if (!closed)
|
|
2530
|
+
child.stdin?.write(`${t}
|
|
2531
|
+
`);
|
|
2532
|
+
},
|
|
2533
|
+
onLine: (f) => lines.push(f),
|
|
2534
|
+
onClose: (f) => closed ? f(closed) : closes.push(f)
|
|
2535
|
+
},
|
|
2536
|
+
close: async () => {
|
|
2537
|
+
if (closed)
|
|
2538
|
+
return;
|
|
2539
|
+
child.stdin?.end();
|
|
2540
|
+
await new Promise((res) => {
|
|
2541
|
+
const t = setTimeout(() => {
|
|
2542
|
+
killTree(child);
|
|
2543
|
+
res();
|
|
2544
|
+
}, 2e3);
|
|
2545
|
+
closes.push(() => {
|
|
2546
|
+
clearTimeout(t);
|
|
2547
|
+
res();
|
|
2548
|
+
});
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
};
|
|
2553
|
+
function atticusExec(entry, node = process.execPath) {
|
|
2554
|
+
return (argv, o) => new Promise((res) => {
|
|
2555
|
+
let output = "";
|
|
2556
|
+
const child = spawn(node, [entry, ...argv], {
|
|
2557
|
+
cwd: o.cwd,
|
|
2558
|
+
env: { ...process.env, ATTICUS_ACP_SESSION: "1" },
|
|
2559
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2560
|
+
windowsHide: true,
|
|
2561
|
+
signal: o.signal
|
|
2562
|
+
});
|
|
2563
|
+
const take = (d) => {
|
|
2564
|
+
const t = d.toString("utf8");
|
|
2565
|
+
output = (output + t).slice(-1e6);
|
|
2566
|
+
o.onOutput?.(t);
|
|
2567
|
+
};
|
|
2568
|
+
child.stdout.on("data", take);
|
|
2569
|
+
child.stderr.on("data", take);
|
|
2570
|
+
child.on("error", (e) => res({ exitCode: 1, output: `${output}${e.message}
|
|
2571
|
+
` }));
|
|
2572
|
+
child.on("close", (code, signal) => res({ exitCode: code, output, signal }));
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
var AcpSignInError = class extends RuntimeError {
|
|
2576
|
+
constructor(message) {
|
|
2577
|
+
super(message);
|
|
2578
|
+
this.name = "AcpSignInError";
|
|
2579
|
+
}
|
|
2580
|
+
};
|
|
2581
|
+
var AcpSetupError = class extends RuntimeError {
|
|
2582
|
+
constructor(message) {
|
|
2583
|
+
super(message);
|
|
2584
|
+
this.name = "AcpSetupError";
|
|
2585
|
+
}
|
|
2586
|
+
};
|
|
2587
|
+
var MAX_NUDGES = 3;
|
|
2588
|
+
function carryOnPrompt(refusals) {
|
|
2589
|
+
return [
|
|
2590
|
+
"Atticus refused this, and you are not finished yet:",
|
|
2591
|
+
...refusals.slice(-3).map((r) => `- ${oneLine(r, 300)}`),
|
|
2592
|
+
"",
|
|
2593
|
+
"Carry on with the step. Use your own file tools to find, view and search files: they are allowed and need no shell. Don't run shell commands (cd, dir, Get-ChildItem, Select-String, Get-Content, grep, find); the only command Atticus runs is one `atticus ...` command at a time, with nothing piped or chained. The step says where its files are. Finish what the step asks for (the facts, or the result file it names), then stop."
|
|
2594
|
+
].join("\n");
|
|
2595
|
+
}
|
|
2596
|
+
function sumUsage(us) {
|
|
2597
|
+
if (!us.length)
|
|
2598
|
+
return void 0;
|
|
2599
|
+
const out = {};
|
|
2600
|
+
for (const u of us)
|
|
2601
|
+
for (const [k, v] of Object.entries(u))
|
|
2602
|
+
out[k] = typeof v === "number" ? (out[k] ?? 0) + v : out[k] ?? v;
|
|
2603
|
+
return out;
|
|
2604
|
+
}
|
|
2605
|
+
var ACP_OFFERED = [
|
|
2606
|
+
"fs/read_text_file",
|
|
2607
|
+
"fs/write_text_file",
|
|
2608
|
+
"terminal/create (atticus only)",
|
|
2609
|
+
"session/request_permission"
|
|
2610
|
+
];
|
|
2611
|
+
var withTimeout = (p, ms, what) => new Promise((res, rej) => {
|
|
2612
|
+
const t = setTimeout(() => rej(new RuntimeError(`${what} took over ${Math.round(ms / 1e3)}s`)), ms);
|
|
2613
|
+
p.then((v) => {
|
|
2614
|
+
clearTimeout(t);
|
|
2615
|
+
res(v);
|
|
2616
|
+
}, (e) => {
|
|
2617
|
+
clearTimeout(t);
|
|
2618
|
+
rej(e);
|
|
2619
|
+
});
|
|
2620
|
+
});
|
|
2621
|
+
var oneLine = (s, n = 150) => short(s.replace(/\s+/g, " ").trim(), n);
|
|
2622
|
+
var signInLines = (stderr) => stderr.split(/\r?\n/).map((l) => l.trim()).filter((l) => /https?:\/\/|code|sign|log.?in|auth/i.test(l)).slice(-5);
|
|
2623
|
+
async function runAcpSession(o) {
|
|
2624
|
+
const { command, args } = commandLineOf(o.command);
|
|
2625
|
+
const server = (o.spawn ?? spawnAcpServer)({
|
|
2626
|
+
command,
|
|
2627
|
+
args,
|
|
2628
|
+
cwd: o.cwd,
|
|
2629
|
+
// beta.17: set on the server too, so an atticus command Copilot runs in its own shell (not
|
|
2630
|
+
// through Atticus's terminal) knows it is inside a session and hands off instead of starting
|
|
2631
|
+
// a Copilot session of its own (a session within a session, waiting on itself).
|
|
2632
|
+
env: { ...process.env, ...o.env ?? {}, ATTICUS_ACP_SESSION: "1", ATTICUS_ROOT: o.cwd }
|
|
2633
|
+
});
|
|
2634
|
+
const transcript = [];
|
|
2635
|
+
const paths = new Paths(o.policy);
|
|
2636
|
+
const calls = [];
|
|
2637
|
+
const refused = [];
|
|
2638
|
+
const say = (l) => o.onProgress?.(short(l, 160));
|
|
2639
|
+
let text = "";
|
|
2640
|
+
let pendingText = "";
|
|
2641
|
+
let agentCalls = 0;
|
|
2642
|
+
let textAtRefusal = 0;
|
|
2643
|
+
let cancelWhy;
|
|
2644
|
+
let sessionId;
|
|
2645
|
+
const abort = new AbortController();
|
|
2646
|
+
const flush = () => {
|
|
2647
|
+
if (pendingText.trim())
|
|
2648
|
+
say(` Copilot: ${oneLine(pendingText)}`);
|
|
2649
|
+
pendingText = "";
|
|
2650
|
+
};
|
|
2651
|
+
const record = (name, input, output, error) => {
|
|
2652
|
+
calls.push({
|
|
2653
|
+
round: 1,
|
|
2654
|
+
name,
|
|
2655
|
+
input,
|
|
2656
|
+
output: short(output, 2e3),
|
|
2657
|
+
tokens: estimateTokens(output),
|
|
2658
|
+
...error ? { error: true } : {}
|
|
2659
|
+
});
|
|
2660
|
+
if (error) {
|
|
2661
|
+
textAtRefusal = text.length;
|
|
2662
|
+
refused.push(output);
|
|
2663
|
+
flush();
|
|
2664
|
+
say(` Refused: ${oneLine(output, 140)}`);
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
2667
|
+
const refuse = (name, input, why) => {
|
|
2668
|
+
record(name, input, why, true);
|
|
2669
|
+
throw new AcpRpcError(ACP_ERRORS.invalidParams, why);
|
|
2670
|
+
};
|
|
2671
|
+
const terminals = /* @__PURE__ */ new Map();
|
|
2672
|
+
let tn = 0;
|
|
2673
|
+
const box = {};
|
|
2674
|
+
const cancel = (why) => {
|
|
2675
|
+
if (cancelWhy)
|
|
2676
|
+
return;
|
|
2677
|
+
cancelWhy = why;
|
|
2678
|
+
flush();
|
|
2679
|
+
say(` Stopping the session: ${why}.`);
|
|
2680
|
+
if (sessionId)
|
|
2681
|
+
box.client?.cancel(sessionId);
|
|
2682
|
+
for (const t of terminals.values())
|
|
2683
|
+
t.kill.abort();
|
|
2684
|
+
};
|
|
2685
|
+
const handlers = {
|
|
2686
|
+
async readTextFile(p) {
|
|
2687
|
+
const why = paths.readable(p.path);
|
|
2688
|
+
if (why)
|
|
2689
|
+
return refuse("fs/read_text_file", { path: p.path }, why);
|
|
2690
|
+
const all = await readFile4(p.path, "utf8").catch((e) => {
|
|
2691
|
+
throw new AcpRpcError(ACP_ERRORS.invalidParams, `${p.path} can't be read: ${e.code ?? e.message}`);
|
|
2692
|
+
});
|
|
2693
|
+
let content = all;
|
|
2694
|
+
if (p.line !== void 0 || p.limit !== void 0) {
|
|
2695
|
+
const lines = all.split("\n");
|
|
2696
|
+
const from = Math.max(0, (p.line ?? 1) - 1);
|
|
2697
|
+
content = lines.slice(from, p.limit !== void 0 ? from + p.limit : void 0).join("\n");
|
|
2698
|
+
}
|
|
2699
|
+
record("fs/read_text_file", { path: p.path }, `${content.length} characters`);
|
|
2700
|
+
return { content };
|
|
2701
|
+
},
|
|
2702
|
+
async writeTextFile(p) {
|
|
2703
|
+
const why = paths.writable(p.path);
|
|
2704
|
+
if (why)
|
|
2705
|
+
return refuse("fs/write_text_file", { path: p.path }, why);
|
|
2706
|
+
await mkdir5(dirname(p.path), { recursive: true });
|
|
2707
|
+
await writeFile4(p.path, p.content, "utf8");
|
|
2708
|
+
record("fs/write_text_file", { path: p.path }, `${p.content.length} characters written`);
|
|
2709
|
+
return null;
|
|
2710
|
+
},
|
|
2711
|
+
async requestPermission(p) {
|
|
2712
|
+
const tc = p.toolCall;
|
|
2713
|
+
const why = permissionRefused(tc, paths);
|
|
2714
|
+
const pick = (kinds) => kinds.map((k) => p.options.find((x) => x.kind === k)).find(Boolean);
|
|
2715
|
+
const allow = why ? void 0 : pick(["allow_once"]);
|
|
2716
|
+
const input = {
|
|
2717
|
+
kind: tc.kind ?? "",
|
|
2718
|
+
title: tc.title ?? "",
|
|
2719
|
+
...tc.locations?.length ? { locations: tc.locations.map((l) => l.path) } : {}
|
|
2720
|
+
};
|
|
2721
|
+
if (allow) {
|
|
2722
|
+
record(`permission:${tc.kind ?? "other"}`, input, `allowed once: ${tc.title ?? ""}`);
|
|
2723
|
+
return { outcome: { outcome: "selected", optionId: allow.optionId } };
|
|
2724
|
+
}
|
|
2725
|
+
record(`permission:${tc.kind ?? "other"}`, input, why ?? "Refused: the server offered only to allow it always; Atticus allows once at a time", true);
|
|
2726
|
+
const reject = pick(["reject_once", "reject_always"]);
|
|
2727
|
+
return reject ? { outcome: { outcome: "selected", optionId: reject.optionId } } : { outcome: { outcome: "cancelled" } };
|
|
2728
|
+
},
|
|
2729
|
+
async terminalCreate(p) {
|
|
2730
|
+
const a = atticusArgvOf(p.command, p.args ?? []);
|
|
2731
|
+
const input = { command: p.command, args: p.args ?? [] };
|
|
2732
|
+
if (a.refused !== void 0)
|
|
2733
|
+
return refuse("terminal/create", input, a.refused);
|
|
2734
|
+
const cwd = o.cwd;
|
|
2735
|
+
const id = `atticus-${++tn}`;
|
|
2736
|
+
const kill = new AbortController();
|
|
2737
|
+
flush();
|
|
2738
|
+
say(` atticus ${oneLine(a.argv.join(" "), 140)}`);
|
|
2739
|
+
const t = {
|
|
2740
|
+
output: "",
|
|
2741
|
+
kill,
|
|
2742
|
+
...p.outputByteLimit ? { limit: p.outputByteLimit } : {}
|
|
2743
|
+
};
|
|
2744
|
+
t.done = o.exec(a.argv, {
|
|
2745
|
+
cwd,
|
|
2746
|
+
signal: kill.signal,
|
|
2747
|
+
onOutput: (s) => {
|
|
2748
|
+
t.output += s;
|
|
2749
|
+
if (t.limit && Buffer.byteLength(t.output) > t.limit)
|
|
2750
|
+
t.output = t.output.slice(-t.limit);
|
|
2751
|
+
}
|
|
2752
|
+
}).then((r) => {
|
|
2753
|
+
if (!t.output && r.output)
|
|
2754
|
+
t.output = r.output;
|
|
2755
|
+
t.exit = { exitCode: r.exitCode, signal: r.signal ?? null };
|
|
2756
|
+
record("terminal/create", input, `exit ${r.exitCode ?? "none"}: ${short(t.output, 1500)}`);
|
|
2757
|
+
return t.exit;
|
|
2758
|
+
});
|
|
2759
|
+
terminals.set(id, t);
|
|
2760
|
+
return { terminalId: id };
|
|
2761
|
+
},
|
|
2762
|
+
async terminalOutput(p) {
|
|
2763
|
+
const t = terminals.get(p.terminalId);
|
|
2764
|
+
if (!t)
|
|
2765
|
+
throw new AcpRpcError(ACP_ERRORS.invalidParams, `no terminal ${p.terminalId}`);
|
|
2766
|
+
return { output: t.output, truncated: false, ...t.exit ? { exitStatus: t.exit } : {} };
|
|
2767
|
+
},
|
|
2768
|
+
async terminalWaitForExit(p) {
|
|
2769
|
+
const t = terminals.get(p.terminalId);
|
|
2770
|
+
if (!t)
|
|
2771
|
+
throw new AcpRpcError(ACP_ERRORS.invalidParams, `no terminal ${p.terminalId}`);
|
|
2772
|
+
return t.done;
|
|
2773
|
+
},
|
|
2774
|
+
async terminalKill(p) {
|
|
2775
|
+
terminals.get(p.terminalId)?.kill.abort();
|
|
2776
|
+
return null;
|
|
2777
|
+
},
|
|
2778
|
+
async terminalRelease(p) {
|
|
2779
|
+
const t = terminals.get(p.terminalId);
|
|
2780
|
+
t?.kill.abort();
|
|
2781
|
+
terminals.delete(p.terminalId);
|
|
2782
|
+
return null;
|
|
2783
|
+
},
|
|
2784
|
+
update(u) {
|
|
2785
|
+
const kind = u.sessionUpdate;
|
|
2786
|
+
if (kind === UPDATE.message) {
|
|
2787
|
+
const t = chunkText(u);
|
|
2788
|
+
if (t) {
|
|
2789
|
+
text += t;
|
|
2790
|
+
pendingText += t;
|
|
2791
|
+
}
|
|
2792
|
+
} else if (kind === UPDATE.toolCall) {
|
|
2793
|
+
agentCalls++;
|
|
2794
|
+
flush();
|
|
2795
|
+
say(` \xB7 ${oneLine(String(u.title ?? u.kind ?? "a tool"), 140)}`);
|
|
2796
|
+
if (o.maxTurns !== void 0 && agentCalls > o.maxTurns)
|
|
2797
|
+
cancel(`maxTurns ${o.maxTurns}`);
|
|
2798
|
+
} else if (kind === UPDATE.plan) {
|
|
2799
|
+
const n = Array.isArray(u.entries) ? u.entries.length : 0;
|
|
2800
|
+
if (n)
|
|
2801
|
+
say(` Plan: ${n} steps`);
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
const client = new AcpClient(server.transport, handlers, (dir, m) => transcript.push(JSON.stringify({ dir, m })));
|
|
2806
|
+
box.client = client;
|
|
2807
|
+
const onAbort = () => cancel("stopped (Ctrl+C)");
|
|
2808
|
+
o.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2809
|
+
let timer;
|
|
2810
|
+
try {
|
|
2811
|
+
let init;
|
|
2812
|
+
try {
|
|
2813
|
+
await server.started;
|
|
2814
|
+
init = await withTimeout(client.initialize({ name: "atticus", version: o.clientVersion ?? "2" }), 6e4, "The Copilot language server's initialize");
|
|
2815
|
+
} catch (e) {
|
|
2816
|
+
throw new AcpSetupError(`Atticus couldn't start the Copilot language server (${o.command}): ${firstLine(e)}${tail(server.stderr())}. Check copilotAcp.command in runtime.yaml, and that ${command} --help lists --acp.`);
|
|
2817
|
+
}
|
|
2818
|
+
const assistant = {
|
|
2819
|
+
name: init.agentInfo?.name ?? basename(command).replace(/\.(cmd|exe)$/i, ""),
|
|
2820
|
+
...init.agentInfo?.version ? { version: init.agentInfo.version } : {},
|
|
2821
|
+
...typeof init.protocolVersion === "number" ? { protocolVersion: init.protocolVersion } : {}
|
|
2822
|
+
};
|
|
2823
|
+
let ns;
|
|
2824
|
+
try {
|
|
2825
|
+
ns = await withTimeout(client.newSession(o.cwd), 12e4, "Opening a Copilot session");
|
|
2826
|
+
} catch (e) {
|
|
2827
|
+
if (isAuthRequired(e)) {
|
|
2828
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
2829
|
+
throw signInError(e, init, server.stderr());
|
|
2830
|
+
}
|
|
2831
|
+
throw new AcpSetupError(`The Copilot language server didn't open a session: ${firstLine(e)}`);
|
|
2832
|
+
}
|
|
2833
|
+
sessionId = ns.sessionId;
|
|
2834
|
+
let model = ns.models?.currentModelId;
|
|
2835
|
+
if (o.model && o.model !== model) {
|
|
2836
|
+
const offered = (ns.models?.availableModels ?? []).map((m) => m.modelId);
|
|
2837
|
+
if (!offered.includes(o.model))
|
|
2838
|
+
throw new AcpSetupError(`The Copilot language server doesn't offer the model ${o.model} (it offers ${offered.join(", ") || "no list"}): change copilotAcp.model in runtime.yaml, or leave it out for Copilot's own.`);
|
|
2839
|
+
await client.setModel(sessionId, o.model);
|
|
2840
|
+
model = o.model;
|
|
2841
|
+
}
|
|
2842
|
+
say(` Copilot session open (${assistant.name}${assistant.version ? ` ${assistant.version}` : ""}${model ? `, ${model}` : ""}).`);
|
|
2843
|
+
if (o.signal?.aborted)
|
|
2844
|
+
cancel("stopped (Ctrl+C)");
|
|
2845
|
+
timer = setTimeout(() => cancel("timeout"), o.timeoutMs ?? 30 * 6e4);
|
|
2846
|
+
let prompt = o.prompt;
|
|
2847
|
+
let answer = {
|
|
2848
|
+
stopReason: "end_turn"
|
|
2849
|
+
};
|
|
2850
|
+
const usages = [];
|
|
2851
|
+
for (let nudge = 0; ; nudge++) {
|
|
2852
|
+
const before = refused.length;
|
|
2853
|
+
const asked = client.prompt(sessionId, prompt);
|
|
2854
|
+
try {
|
|
2855
|
+
answer = await new Promise((res, rej) => {
|
|
2856
|
+
asked.then(res, rej);
|
|
2857
|
+
const watch = setInterval(() => {
|
|
2858
|
+
if (!cancelWhy)
|
|
2859
|
+
return;
|
|
2860
|
+
clearInterval(watch);
|
|
2861
|
+
setTimeout(() => res({ stopReason: "cancelled" }), 1e4);
|
|
2862
|
+
}, 50);
|
|
2863
|
+
asked.finally(() => clearInterval(watch)).catch(() => void 0);
|
|
2864
|
+
});
|
|
2865
|
+
} catch (e) {
|
|
2866
|
+
if (isAuthRequired(e))
|
|
2867
|
+
throw signInError(e, init, server.stderr());
|
|
2868
|
+
throw new RuntimeError(`The Copilot session failed: ${firstLine(e)}${tail(server.stderr())}`);
|
|
2869
|
+
}
|
|
2870
|
+
flush();
|
|
2871
|
+
if (answer.usage)
|
|
2872
|
+
usages.push(answer.usage);
|
|
2873
|
+
const fresh = refused.slice(before);
|
|
2874
|
+
const after = text.slice(textAtRefusal);
|
|
2875
|
+
const overIt = fresh.length > 0 && !!calls.at(-1)?.error && (!after.trim() || /cancel|refused|not allowed|can'?t|cannot|couldn'?t|unable/i.test(after));
|
|
2876
|
+
if (cancelWhy || !overIt || nudge >= MAX_NUDGES || !["end_turn", "cancelled"].includes(answer.stopReason))
|
|
2877
|
+
break;
|
|
2878
|
+
say(` Atticus: told Copilot why that was refused and to carry on (${nudge + 1} of ${MAX_NUDGES}).`);
|
|
2879
|
+
prompt = carryOnPrompt(fresh);
|
|
2880
|
+
}
|
|
2881
|
+
const usage = usageOf(sumUsage(usages));
|
|
2882
|
+
return {
|
|
2883
|
+
stopReason: cancelWhy ? "cancelled" : answer.stopReason,
|
|
2884
|
+
...cancelWhy ? { stopped: cancelWhy } : {},
|
|
2885
|
+
...model ? { model } : {},
|
|
2886
|
+
assistant,
|
|
2887
|
+
...usage ? { usage } : {},
|
|
2888
|
+
text,
|
|
2889
|
+
toolCalls: calls,
|
|
2890
|
+
refused,
|
|
2891
|
+
agentToolCalls: agentCalls
|
|
2892
|
+
};
|
|
2893
|
+
} finally {
|
|
2894
|
+
clearTimeout(timer);
|
|
2895
|
+
o.signal?.removeEventListener("abort", onAbort);
|
|
2896
|
+
abort.abort();
|
|
2897
|
+
for (const t of terminals.values())
|
|
2898
|
+
t.kill.abort();
|
|
2899
|
+
await server.close().catch(() => void 0);
|
|
2900
|
+
if (o.transcript) {
|
|
2901
|
+
await mkdir5(dirname(o.transcript), { recursive: true });
|
|
2902
|
+
await writeFile4(o.transcript, transcript.join("\n") + (transcript.length ? "\n" : ""), "utf8");
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
var firstLine = (e) => String(e?.message ?? e).split("\n")[0];
|
|
2907
|
+
var tail = (stderr) => {
|
|
2908
|
+
const l = stderr.trim().split(/\r?\n/).slice(-2).join(" ").trim();
|
|
2909
|
+
return l ? ` (it said: ${short(l, 300)})` : "";
|
|
2910
|
+
};
|
|
2911
|
+
function signInError(e, init, stderr) {
|
|
2912
|
+
const how = (init.authMethods ?? []).map((m) => m.description || m.name).filter(Boolean).join("; ");
|
|
2913
|
+
const lines = signInLines(stderr);
|
|
2914
|
+
return new AcpSignInError([
|
|
2915
|
+
`GitHub Copilot isn't signed in here, so the run stopped. The Copilot language server said: ${firstLine(e)}`,
|
|
2916
|
+
...lines.length ? [`It also said: ${lines.join(" ")}`] : [],
|
|
2917
|
+
...how ? [`Its ways to sign in: ${how}.`] : [],
|
|
2918
|
+
"Sign in yourself (Copilot's sign-in in your editor, or the link and code above), then run the command again. Atticus never enters or keeps your credentials."
|
|
2919
|
+
].join("\n"));
|
|
2920
|
+
}
|
|
2921
|
+
function permissionRefused(tc, paths) {
|
|
2922
|
+
const locs = (tc.locations ?? []).map((l) => l.path);
|
|
2923
|
+
switch (tc.kind) {
|
|
2924
|
+
case "think":
|
|
2925
|
+
return void 0;
|
|
2926
|
+
case "read":
|
|
2927
|
+
case "search": {
|
|
2928
|
+
if (!locs.length)
|
|
2929
|
+
return tc.kind === "search" ? void 0 : "Refused: a read must say which file (use fs/read_text_file)";
|
|
2930
|
+
for (const l of locs) {
|
|
2931
|
+
const why = paths.readable(l);
|
|
2932
|
+
if (why)
|
|
2933
|
+
return why;
|
|
2934
|
+
}
|
|
2935
|
+
return void 0;
|
|
2936
|
+
}
|
|
2937
|
+
case "edit": {
|
|
2938
|
+
if (!locs.length)
|
|
2939
|
+
return "Refused: an edit must say which file it changes";
|
|
2940
|
+
for (const l of locs) {
|
|
2941
|
+
const why = paths.writable(l);
|
|
2942
|
+
if (why)
|
|
2943
|
+
return why;
|
|
2944
|
+
}
|
|
2945
|
+
return void 0;
|
|
2946
|
+
}
|
|
2947
|
+
case "execute": {
|
|
2948
|
+
const raw = tc.rawInput ?? {};
|
|
2949
|
+
const cmd = typeof raw.command === "string" ? raw.command : typeof raw.commandLine === "string" ? raw.commandLine : tc.title ?? "";
|
|
2950
|
+
const args = Array.isArray(raw.args) ? raw.args.map(String) : [];
|
|
2951
|
+
const a = atticusArgvOf(cmd, args);
|
|
2952
|
+
return a.refused;
|
|
2953
|
+
}
|
|
2954
|
+
default:
|
|
2955
|
+
return `Refused: Atticus doesn't let this session ${tc.kind ?? "do"} ${short(tc.title ?? "that")} (it may read the metarepo, write its step's result and run atticus commands)`;
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
async function copilotAcpProbe(o) {
|
|
2959
|
+
const { command, args } = commandLineOf(o.command);
|
|
2960
|
+
const plain = args.filter((a) => a !== "--acp");
|
|
2961
|
+
const env = { ...process.env, ...o.env ?? {} };
|
|
2962
|
+
const version = await new Promise((res) => {
|
|
2963
|
+
const shell = needsShell(command);
|
|
2964
|
+
let out = "";
|
|
2965
|
+
let child;
|
|
2966
|
+
try {
|
|
2967
|
+
child = spawn(shell ? quoteForCmd(command) : command, [...shell ? plain.map(quoteForCmd) : plain, "--version"], {
|
|
2968
|
+
cwd: o.cwd,
|
|
2969
|
+
env,
|
|
2970
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2971
|
+
windowsHide: true,
|
|
2972
|
+
shell
|
|
2973
|
+
});
|
|
2974
|
+
} catch (e) {
|
|
2975
|
+
res({ ok: false, text: e.message });
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
const t = setTimeout(() => killTree(child), o.timeoutMs ?? 6e4);
|
|
2979
|
+
child.stdout?.on("data", (d) => out += d.toString("utf8"));
|
|
2980
|
+
child.stderr?.on("data", (d) => out += d.toString("utf8"));
|
|
2981
|
+
child.on("error", (e) => {
|
|
2982
|
+
clearTimeout(t);
|
|
2983
|
+
res({ ok: false, text: e.message });
|
|
2984
|
+
});
|
|
2985
|
+
child.on("close", (code) => {
|
|
2986
|
+
clearTimeout(t);
|
|
2987
|
+
res({ ok: code === 0, text: out.trim() });
|
|
2988
|
+
});
|
|
2989
|
+
});
|
|
2990
|
+
if (!version.ok)
|
|
2991
|
+
return { found: false, said: oneLine(version.text, 200) };
|
|
2992
|
+
const v = version.text.split(/\r?\n/).find((l) => l.trim())?.trim();
|
|
2993
|
+
const server = (o.spawn ?? spawnAcpServer)({ command, args, cwd: o.cwd, env });
|
|
2994
|
+
const client = new AcpClient(server.transport, {
|
|
2995
|
+
readTextFile: () => Promise.reject(new AcpRpcError(ACP_ERRORS.invalidParams, "doctor reads nothing")),
|
|
2996
|
+
writeTextFile: () => Promise.reject(new AcpRpcError(ACP_ERRORS.invalidParams, "doctor writes nothing")),
|
|
2997
|
+
requestPermission: async () => ({ outcome: { outcome: "cancelled" } }),
|
|
2998
|
+
terminalCreate: () => Promise.reject(new AcpRpcError(ACP_ERRORS.invalidParams, "doctor runs nothing")),
|
|
2999
|
+
terminalOutput: () => Promise.reject(new AcpRpcError(ACP_ERRORS.invalidParams, "no terminal")),
|
|
3000
|
+
terminalWaitForExit: () => Promise.reject(new AcpRpcError(ACP_ERRORS.invalidParams, "no terminal")),
|
|
3001
|
+
terminalKill: async () => null,
|
|
3002
|
+
terminalRelease: async () => null,
|
|
3003
|
+
update: () => {
|
|
3004
|
+
}
|
|
3005
|
+
});
|
|
3006
|
+
const base = { found: true, ...v ? { version: v } : {} };
|
|
3007
|
+
try {
|
|
3008
|
+
await server.started;
|
|
3009
|
+
await withTimeout(client.initialize({ name: "atticus-doctor", version: "2" }), o.timeoutMs ?? 6e4, "initialize");
|
|
3010
|
+
try {
|
|
3011
|
+
await withTimeout(client.newSession(o.cwd), o.timeoutMs ?? 6e4, "a new session");
|
|
3012
|
+
return { ...base, signedIn: true };
|
|
3013
|
+
} catch (e) {
|
|
3014
|
+
if (isAuthRequired(e)) {
|
|
3015
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
3016
|
+
const lines = signInLines(server.stderr());
|
|
3017
|
+
return { ...base, signedIn: false, said: [firstLine(e), ...lines].join(" ") };
|
|
3018
|
+
}
|
|
3019
|
+
return { ...base, said: firstLine(e) };
|
|
3020
|
+
}
|
|
3021
|
+
} catch (e) {
|
|
3022
|
+
return { ...base, said: `the --acp handshake failed: ${firstLine(e)}` };
|
|
3023
|
+
} finally {
|
|
3024
|
+
await server.close().catch(() => void 0);
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
var CopilotAcpRuntime = class extends OneShotRuntime {
|
|
3028
|
+
c;
|
|
3029
|
+
kind = "copilot-acp";
|
|
3030
|
+
constructor(c) {
|
|
3031
|
+
super({ ...c.now ? { now: c.now } : {}, ...c.validate ? { validate: c.validate } : {} });
|
|
3032
|
+
this.c = c;
|
|
3033
|
+
}
|
|
3034
|
+
allowedAs(agent) {
|
|
3035
|
+
return agent.allowedRuntimes.includes("copilot-acp") ? "copilot-acp" : "hand-off";
|
|
3036
|
+
}
|
|
3037
|
+
modelFor() {
|
|
3038
|
+
return this.c.model;
|
|
3039
|
+
}
|
|
3040
|
+
/** Review round 48: the run's folder is claimed, as a model run's is (claimRunDir). */
|
|
3041
|
+
async newRunId(request, now) {
|
|
3042
|
+
return claimRunDir(join5(runsDirOf(this.c.root), request.change), () => this.nextRunId(now));
|
|
3043
|
+
}
|
|
3044
|
+
async produce(run, signal) {
|
|
3045
|
+
const { request } = run;
|
|
3046
|
+
const root = this.c.root;
|
|
3047
|
+
const dir = join5(runsDirOf(root), request.change, run.runId);
|
|
3048
|
+
await mkdir5(dir, { recursive: true });
|
|
3049
|
+
const outRel = HandoffRuntime.outputRel(run, changesRelOf(root), runsRelOf(root));
|
|
3050
|
+
const prompt = HandoffRuntime.promptFor(run, changesRelOf(root), runsRelOf(root)).replace(/\nWhen the file is complete.*\n$/s, "\n") + (request.selfCheck ? `
|
|
3051
|
+
Before you finish, check the file: run ${request.selfCheck.command} ${outRel}. Fix each problem it lists and run it again, until it says the file is clean (at most ${request.selfCheck.limit} times).
|
|
3052
|
+
` : "") + `
|
|
3053
|
+
${DATA_RULE}
|
|
3054
|
+
`;
|
|
3055
|
+
await writeFile4(join5(dir, "prompt.md"), prompt, "utf8");
|
|
3056
|
+
await writeFile4(join5(dir, "context-manifest.json"), JSON.stringify(request.pack.manifest, null, 2) + "\n", "utf8");
|
|
3057
|
+
const outputFile = request.outputIn === "run" ? join5(dir, request.outputPath) : join5(changesDirOf(root), request.change, request.outputPath);
|
|
3058
|
+
const before = (await stat2(outputFile).catch(() => void 0))?.mtimeMs;
|
|
3059
|
+
const r = await runAcpSession({
|
|
3060
|
+
command: this.c.command,
|
|
3061
|
+
cwd: root,
|
|
3062
|
+
prompt,
|
|
3063
|
+
policy: { readRoots: readRootsOf(root), writeRoots: [], writeFiles: [outputFile] },
|
|
3064
|
+
exec: this.c.exec ?? noExec,
|
|
3065
|
+
...this.c.model ? { model: this.c.model } : {},
|
|
3066
|
+
...this.c.maxTurns ? { maxTurns: this.c.maxTurns } : {},
|
|
3067
|
+
...this.c.timeoutMs ? { timeoutMs: this.c.timeoutMs } : {},
|
|
3068
|
+
...signal ? { signal } : {},
|
|
3069
|
+
...this.c.onProgress ? { onProgress: this.c.onProgress } : {},
|
|
3070
|
+
...this.c.env ? { env: this.c.env } : {},
|
|
3071
|
+
...this.c.spawn ? { spawn: this.c.spawn } : {},
|
|
3072
|
+
...this.c.clientVersion ? { clientVersion: this.c.clientVersion } : {},
|
|
3073
|
+
transcript: join5(dir, "transcript.jsonl")
|
|
3074
|
+
});
|
|
3075
|
+
const after = (await stat2(outputFile).catch(() => void 0))?.mtimeMs;
|
|
3076
|
+
const written = after !== void 0 && after !== before ? await readFile4(outputFile, "utf8") : void 0;
|
|
3077
|
+
const tools = {
|
|
3078
|
+
offered: ACP_OFFERED,
|
|
3079
|
+
calls: r.toolCalls,
|
|
3080
|
+
...r.stopped ? { stopped: r.stopped } : {}
|
|
3081
|
+
};
|
|
3082
|
+
if (written === void 0 && r.stopReason !== "end_turn")
|
|
3083
|
+
throw Object.assign(new RuntimeError(`The Copilot session ended (${r.stopped ?? r.stopReason}) without writing ${outRel}.`), { tools });
|
|
3084
|
+
return {
|
|
3085
|
+
output: written ?? r.text,
|
|
3086
|
+
usage: r.usage ?? {
|
|
3087
|
+
inputTokens: request.pack.manifest.totalTokens,
|
|
3088
|
+
outputTokens: estimateTokens(r.text)
|
|
3089
|
+
},
|
|
3090
|
+
tools,
|
|
3091
|
+
...r.model ? { model: r.model } : {},
|
|
3092
|
+
assistant: r.assistant
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
3095
|
+
/** Kept as a hand-off run is: run.json beside the prompt, so Model runs shows it. */
|
|
3096
|
+
async keep(run) {
|
|
3097
|
+
const dir = join5(runsDirOf(this.c.root), run.request.change, run.runId);
|
|
3098
|
+
await mkdir5(dir, { recursive: true });
|
|
3099
|
+
await writeFile4(join5(dir, "run.json"), JSON.stringify(run, null, 2) + "\n", "utf8");
|
|
3100
|
+
}
|
|
3101
|
+
};
|
|
3102
|
+
var noExec = async (argv) => ({
|
|
3103
|
+
exitCode: 127,
|
|
3104
|
+
output: `atticus ${argv.join(" ")}: this program can't run atticus commands for the session; run the step with the atticus command line.
|
|
3105
|
+
`
|
|
3106
|
+
});
|
|
3107
|
+
async function keepAcpRun(o) {
|
|
3108
|
+
const dir = join5(runsDirOf(o.root), o.change, o.runId);
|
|
3109
|
+
await mkdir5(dir, { recursive: true });
|
|
3110
|
+
if (!existsSync(join5(dir, "prompt.md")))
|
|
3111
|
+
await writeFile4(join5(dir, "prompt.md"), o.prompt, "utf8");
|
|
3112
|
+
const r = o.result;
|
|
3113
|
+
const run = {
|
|
3114
|
+
runId: o.runId,
|
|
3115
|
+
request: { change: o.change, agent: o.agent, outputPath: o.outputPath },
|
|
3116
|
+
runtime: "copilot-acp",
|
|
3117
|
+
state: o.state,
|
|
3118
|
+
attempt: 1,
|
|
3119
|
+
startedAt: o.startedAt,
|
|
3120
|
+
finishedAt: o.finishedAt,
|
|
3121
|
+
errors: o.errors,
|
|
3122
|
+
...r?.usage ? { usage: r.usage } : {},
|
|
3123
|
+
...r ? {
|
|
3124
|
+
tools: {
|
|
3125
|
+
offered: ACP_OFFERED,
|
|
3126
|
+
calls: r.toolCalls,
|
|
3127
|
+
...r.stopped ? { stopped: r.stopped } : {}
|
|
3128
|
+
}
|
|
3129
|
+
} : {},
|
|
3130
|
+
stepClass: o.stepClass ?? stepClassOf({ agent: o.agent }),
|
|
3131
|
+
...r?.model ? { model: r.model } : {},
|
|
3132
|
+
...r ? { assistant: r.assistant } : {}
|
|
3133
|
+
};
|
|
3134
|
+
await writeFile4(join5(dir, "run.json"), JSON.stringify(run, null, 2) + "\n", "utf8");
|
|
3135
|
+
return dir;
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
// packages/ai/dist/factory.js
|
|
1702
3139
|
var RUNTIME_MODES = RuntimeKind.options;
|
|
1703
|
-
var CONFIGURED = [
|
|
3140
|
+
var CONFIGURED = [
|
|
3141
|
+
"hand-off",
|
|
3142
|
+
"model",
|
|
3143
|
+
"headless-claude-code",
|
|
3144
|
+
"copilot-acp"
|
|
3145
|
+
];
|
|
1704
3146
|
var RuntimeConfigError = class extends RuntimeError {
|
|
1705
3147
|
constructor(message) {
|
|
1706
3148
|
super(message);
|
|
@@ -1713,16 +3155,49 @@ var AiOffError = class extends RuntimeConfigError {
|
|
|
1713
3155
|
}
|
|
1714
3156
|
};
|
|
1715
3157
|
function claudeCodeCliRunner(o = {}) {
|
|
1716
|
-
return (i) =>
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
|
|
3158
|
+
return (i) => {
|
|
3159
|
+
const command = resolveWindowsCommand(o.command ?? "claude");
|
|
3160
|
+
const shell = isShim(command);
|
|
3161
|
+
let started;
|
|
3162
|
+
const end = () => {
|
|
3163
|
+
if (started)
|
|
3164
|
+
killTree(started);
|
|
3165
|
+
};
|
|
3166
|
+
if (shell && i.signal) {
|
|
3167
|
+
if (i.signal.aborted)
|
|
3168
|
+
queueMicrotask(end);
|
|
3169
|
+
else
|
|
3170
|
+
i.signal.addEventListener("abort", end, { once: true });
|
|
3171
|
+
}
|
|
3172
|
+
return collectChild(() => {
|
|
3173
|
+
started = spawn2(shell ? quoteForCmdAlways(command) : command, shell ? i.args.map(quoteForCmdAlways) : i.args, {
|
|
3174
|
+
cwd: i.cwd,
|
|
3175
|
+
env: { ...process.env, ...i.env },
|
|
3176
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
3177
|
+
windowsHide: true,
|
|
3178
|
+
shell,
|
|
3179
|
+
...i.signal && !shell ? { signal: i.signal } : {}
|
|
3180
|
+
});
|
|
3181
|
+
return started;
|
|
3182
|
+
}, i.prompt, i.timeoutMs, i.cwd, process.platform, shell ? (c) => killTree(c) : void 0);
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
var isShim = (command, platform = process.platform) => platform === "win32" && /\.(cmd|bat)$/i.test(command);
|
|
3186
|
+
function resolveWindowsCommand(command, env = process.env, platform = process.platform, exists = existsSync2) {
|
|
3187
|
+
if (platform !== "win32" || /[\\/]/.test(command) || /\.[a-z0-9]+$/i.test(command))
|
|
3188
|
+
return command;
|
|
3189
|
+
const exts = (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
3190
|
+
for (const dir of (env.Path ?? env.PATH ?? "").split(";").map((d) => d.trim().replace(/^"(.*)"$/, "$1")).filter(Boolean))
|
|
3191
|
+
for (const ext of exts) {
|
|
3192
|
+
const full = `${dir.replace(/[\\/]+$/, "")}\\${command}${ext.toLowerCase()}`;
|
|
3193
|
+
if (exists(full))
|
|
3194
|
+
return full;
|
|
3195
|
+
}
|
|
3196
|
+
return command;
|
|
3197
|
+
}
|
|
3198
|
+
var quoteForCmdAlways = (a) => `"${a.replace(/"/g, '""')}"`;
|
|
3199
|
+
function collectChild(start, prompt, timeoutMs, cwd, platform = process.platform, stop) {
|
|
3200
|
+
return new Promise((resolve2, reject) => {
|
|
1726
3201
|
let done = false;
|
|
1727
3202
|
const timer = {};
|
|
1728
3203
|
const fail = (e) => {
|
|
@@ -1743,7 +3218,7 @@ function collectChild(start, prompt, timeoutMs, cwd, platform = process.platform
|
|
|
1743
3218
|
}
|
|
1744
3219
|
let stdout = "";
|
|
1745
3220
|
let stderr = "";
|
|
1746
|
-
timer.id = setTimeout(() => child.kill("SIGINT"), timeoutMs);
|
|
3221
|
+
timer.id = setTimeout(() => stop ? stop(child) : child.kill("SIGINT"), timeoutMs);
|
|
1747
3222
|
for (const s of [child.stdin, child.stdout, child.stderr])
|
|
1748
3223
|
s.on("error", fail);
|
|
1749
3224
|
child.stdout.setEncoding("utf8").on("data", (d) => stdout += d);
|
|
@@ -1754,16 +3229,16 @@ function collectChild(start, prompt, timeoutMs, cwd, platform = process.platform
|
|
|
1754
3229
|
return;
|
|
1755
3230
|
done = true;
|
|
1756
3231
|
clearTimeout(timer.id);
|
|
1757
|
-
|
|
3232
|
+
resolve2({ exitCode: code, stdout, stderr });
|
|
1758
3233
|
});
|
|
1759
3234
|
child.stdin.end(prompt, "utf8");
|
|
1760
3235
|
});
|
|
1761
3236
|
}
|
|
1762
|
-
var cloneDirs = (root) => [
|
|
3237
|
+
var cloneDirs = (root) => [join6(root, "workspace"), join6(stateDirOf(root), "workspace")];
|
|
1763
3238
|
async function existingDirs(dirs) {
|
|
1764
3239
|
const out = [];
|
|
1765
3240
|
for (const d of [...new Set(dirs)])
|
|
1766
|
-
if ((await
|
|
3241
|
+
if ((await stat3(d).catch(() => void 0))?.isDirectory())
|
|
1767
3242
|
out.push(d);
|
|
1768
3243
|
return out;
|
|
1769
3244
|
}
|
|
@@ -1800,9 +3275,9 @@ var HeadlessClaudeCodeRuntime = class _HeadlessClaudeCodeRuntime extends OneShot
|
|
|
1800
3275
|
}
|
|
1801
3276
|
async produce(run, signal) {
|
|
1802
3277
|
const { request } = run;
|
|
1803
|
-
const dir =
|
|
3278
|
+
const dir = join6(runsDirOf(this.h.root), request.change, run.runId);
|
|
1804
3279
|
const reviewer = request.agent.outputArtifact === "findings" && request.outputIn === "run";
|
|
1805
|
-
const work = reviewer ? await mkdtemp(
|
|
3280
|
+
const work = reviewer ? await mkdtemp(join6(tmpdir(), "atticus-review-")) : void 0;
|
|
1806
3281
|
const clones = work ? await existingDirs(cloneDirs(this.h.root)) : [];
|
|
1807
3282
|
const base = work ? {
|
|
1808
3283
|
cwd: work,
|
|
@@ -1816,27 +3291,27 @@ var HeadlessClaudeCodeRuntime = class _HeadlessClaudeCodeRuntime extends OneShot
|
|
|
1816
3291
|
`Bash(${request.selfCheck.command}:*)`
|
|
1817
3292
|
]
|
|
1818
3293
|
} : base;
|
|
1819
|
-
await
|
|
1820
|
-
const promptFile =
|
|
3294
|
+
await mkdir6(dir, { recursive: true });
|
|
3295
|
+
const promptFile = join6(dir, "prompt.md");
|
|
1821
3296
|
const self = reviewer ? void 0 : request.selfCheck;
|
|
1822
3297
|
const handoff = HandoffRuntime.promptFor(run, changesRelOf(this.h.root), runsRelOf(this.h.root)).replace(/\nWhen the file is complete.*\n$/s, "\n") + // W3a: the drafter checks its own file with the stage's checks before it finishes.
|
|
1823
3298
|
(self ? `
|
|
1824
3299
|
Before you finish, check the file: run ${self.command} ${HandoffRuntime.outputRel(run, changesRelOf(this.h.root), runsRelOf(this.h.root))}. Fix each problem it lists and run it again, until it says the file is clean (at most ${self.limit} times).
|
|
1825
3300
|
` : "");
|
|
1826
3301
|
const prompt = reviewer ? handoff.replace(`Write the result to ${HandoffRuntime.outputRel(run, changesRelOf(this.h.root), runsRelOf(this.h.root))}.`, `Write the result to ${request.outputPath}.${clones.length ? ` The metarepo is at ${this.h.root}: a folder named relative to the metarepo (the repos' clones under workspace/) is there, to read, never to edit.` : ""}`) : handoff;
|
|
1827
|
-
await
|
|
1828
|
-
await
|
|
1829
|
-
if (!await
|
|
3302
|
+
await writeFile5(promptFile, prompt, "utf8");
|
|
3303
|
+
await writeFile5(join6(dir, "context-manifest.json"), JSON.stringify(request.pack.manifest, null, 2) + "\n", "utf8");
|
|
3304
|
+
if (!await stat3(inv.cwd).catch(() => void 0))
|
|
1830
3305
|
throw new RuntimeError(`the worktree ${inv.cwd} doesn't exist`);
|
|
1831
|
-
const outputFile = work ?
|
|
3306
|
+
const outputFile = work ? join6(work, request.outputPath) : request.outputIn === "run" ? join6(dir, request.outputPath) : join6(changesDirOf(this.h.root), request.change, request.outputPath);
|
|
1832
3307
|
try {
|
|
1833
3308
|
return await this.runIn(run, dir, inv, prompt, promptFile, outputFile, signal);
|
|
1834
3309
|
} finally {
|
|
1835
3310
|
if (work) {
|
|
1836
|
-
const kept = await
|
|
3311
|
+
const kept = await readFile5(outputFile, "utf8").catch(() => void 0);
|
|
1837
3312
|
if (kept !== void 0) {
|
|
1838
|
-
await
|
|
1839
|
-
await
|
|
3313
|
+
await mkdir6(dirname2(join6(dir, request.outputPath)), { recursive: true });
|
|
3314
|
+
await writeFile5(join6(dir, request.outputPath), kept, "utf8");
|
|
1840
3315
|
}
|
|
1841
3316
|
await rm2(work, { recursive: true, force: true, maxRetries: 5 }).catch(() => void 0);
|
|
1842
3317
|
}
|
|
@@ -1844,7 +3319,7 @@ Before you finish, check the file: run ${self.command} ${HandoffRuntime.outputRe
|
|
|
1844
3319
|
}
|
|
1845
3320
|
async runIn(run, dir, inv, prompt, promptFile, outputFile, signal) {
|
|
1846
3321
|
const { request } = run;
|
|
1847
|
-
const before = (await
|
|
3322
|
+
const before = (await stat3(outputFile).catch(() => void 0))?.mtimeMs;
|
|
1848
3323
|
const res = await this.runner({
|
|
1849
3324
|
cwd: inv.cwd,
|
|
1850
3325
|
prompt,
|
|
@@ -1860,7 +3335,7 @@ Before you finish, check the file: run ${self.command} ${HandoffRuntime.outputRe
|
|
|
1860
3335
|
timeoutMs: this.h.timeoutMs ?? 30 * 6e4,
|
|
1861
3336
|
...signal ? { signal } : {}
|
|
1862
3337
|
});
|
|
1863
|
-
await
|
|
3338
|
+
await writeFile5(join6(dir, "claude-output.json"), res.stdout, "utf8");
|
|
1864
3339
|
let parsed;
|
|
1865
3340
|
try {
|
|
1866
3341
|
parsed = JSON.parse(res.stdout);
|
|
@@ -1870,8 +3345,8 @@ Before you finish, check the file: run ${self.command} ${HandoffRuntime.outputRe
|
|
|
1870
3345
|
const text = typeof parsed.result === "string" ? parsed.result : "";
|
|
1871
3346
|
if (res.exitCode !== 0 || parsed.is_error === true)
|
|
1872
3347
|
throw new RuntimeError(`Claude Code reported a failure: ${text || res.stderr.trim()}`);
|
|
1873
|
-
const after = (await
|
|
1874
|
-
const written = after !== void 0 && after !== before ? await
|
|
3348
|
+
const after = (await stat3(outputFile).catch(() => void 0))?.mtimeMs;
|
|
3349
|
+
const written = after !== void 0 && after !== before ? await readFile5(outputFile, "utf8") : void 0;
|
|
1875
3350
|
return {
|
|
1876
3351
|
output: written ?? text,
|
|
1877
3352
|
usage: {
|
|
@@ -1916,6 +3391,10 @@ var toolSource;
|
|
|
1916
3391
|
function registerToolSource(source) {
|
|
1917
3392
|
toolSource = source;
|
|
1918
3393
|
}
|
|
3394
|
+
var acpRunner;
|
|
3395
|
+
function registerCopilotAcp(o) {
|
|
3396
|
+
acpRunner = o;
|
|
3397
|
+
}
|
|
1919
3398
|
function classModels(rt, o) {
|
|
1920
3399
|
const by = modelsByClass(rt, o.env ?? {});
|
|
1921
3400
|
const out = {};
|
|
@@ -1937,17 +3416,19 @@ function classModels(rt, o) {
|
|
|
1937
3416
|
}
|
|
1938
3417
|
var told = /* @__PURE__ */ new WeakMap();
|
|
1939
3418
|
var nonEmpty = async (file, request) => {
|
|
1940
|
-
const text = await
|
|
3419
|
+
const text = await readFile5(file, "utf8").catch(() => "");
|
|
1941
3420
|
return text.trim() ? [] : [`${request.outputPath} is empty`];
|
|
1942
3421
|
};
|
|
1943
3422
|
async function createRuntime(orgPack, o) {
|
|
1944
3423
|
const rt = orgPack.runtime;
|
|
1945
|
-
const
|
|
3424
|
+
const asked = o.mode ?? rt.modes.default;
|
|
3425
|
+
const inSession = !!(o.env ?? process.env).ATTICUS_ACP_SESSION;
|
|
3426
|
+
const mode = asked === "copilot-acp" && inSession ? "hand-off" : asked;
|
|
1946
3427
|
if (!RUNTIME_MODES.includes(mode))
|
|
1947
3428
|
throw new RuntimeConfigError(`There is no AI mode called ${String(mode)}. Use one of: ${RUNTIME_MODES.join(", ")}.`);
|
|
1948
3429
|
if (rt.ai === "off" && CONFIGURED.includes(mode))
|
|
1949
3430
|
throw new AiOffError();
|
|
1950
|
-
if (CONFIGURED.includes(mode) && !rt.modes.allowed.includes(mode))
|
|
3431
|
+
if (CONFIGURED.includes(mode) && !rt.modes.allowed.includes(mode) && !(inSession && asked === "copilot-acp"))
|
|
1951
3432
|
throw new RuntimeConfigError(`runtime.yaml doesn't allow ${mode} mode (allowed: ${rt.modes.allowed.join(", ")}). Change modes.allowed in a pull request, or pick an allowed mode.`);
|
|
1952
3433
|
const one = { ...o.now ? { now: o.now } : {}, ...o.validate ? { validate: o.validate } : {} };
|
|
1953
3434
|
switch (mode) {
|
|
@@ -1955,7 +3436,9 @@ async function createRuntime(orgPack, o) {
|
|
|
1955
3436
|
return new HandoffRuntime({
|
|
1956
3437
|
root: o.root,
|
|
1957
3438
|
validate: o.handoffValidate ?? nonEmpty,
|
|
1958
|
-
...o.now ? { now: o.now } : {}
|
|
3439
|
+
...o.now ? { now: o.now } : {},
|
|
3440
|
+
// ADR 0185: a stage's critic lenses go to the person's assistant in one prompt.
|
|
3441
|
+
batch: true
|
|
1959
3442
|
});
|
|
1960
3443
|
case "model": {
|
|
1961
3444
|
const cfg = rt.adapters.model;
|
|
@@ -1995,6 +3478,22 @@ async function createRuntime(orgPack, o) {
|
|
|
1995
3478
|
...o.validate ? { validate: o.validate } : {}
|
|
1996
3479
|
});
|
|
1997
3480
|
}
|
|
3481
|
+
case "copilot-acp": {
|
|
3482
|
+
if (!rt.assistants.allowed.includes("copilot"))
|
|
3483
|
+
throw new RuntimeConfigError("copilot-acp drives GitHub Copilot, which runtime.yaml doesn't list under assistants.allowed.");
|
|
3484
|
+
const c = copilotAcpOf(rt);
|
|
3485
|
+
return new CopilotAcpRuntime({
|
|
3486
|
+
root: o.root,
|
|
3487
|
+
command: c.command,
|
|
3488
|
+
...c.model ? { model: c.model } : {},
|
|
3489
|
+
maxTurns: c.maxTurns,
|
|
3490
|
+
timeoutMs: c.timeoutMs,
|
|
3491
|
+
...acpRunner,
|
|
3492
|
+
...o.copilotAcp,
|
|
3493
|
+
...o.now ? { now: o.now } : {},
|
|
3494
|
+
...o.validate ? { validate: o.validate } : {}
|
|
3495
|
+
});
|
|
3496
|
+
}
|
|
1998
3497
|
case "replay":
|
|
1999
3498
|
if (o.recordings)
|
|
2000
3499
|
return new ReplayRuntime(o.recordings, one);
|
|
@@ -2011,13 +3510,13 @@ var sayOnce = (line) => process.stderr.write(`${line}
|
|
|
2011
3510
|
`);
|
|
2012
3511
|
async function runtimeForCli(root, o = {}) {
|
|
2013
3512
|
const env = o.env ?? process.env;
|
|
2014
|
-
const op = await loadOrgPack(
|
|
3513
|
+
const op = await loadOrgPack(join6(orgPackDirOf(root)));
|
|
2015
3514
|
const bad = op.problems.filter((p) => p.file === "runtime.yaml");
|
|
2016
3515
|
if (bad.length)
|
|
2017
3516
|
throw new RuntimeConfigError(`org-pack/runtime.yaml has problems: ${bad.map((p) => `${p.path ? `${p.path}: ` : ""}${p.message}`).join("; ")}`);
|
|
2018
3517
|
const asked = o.mode ?? env.ATTICUS_RUNTIME ?? op.runtime.modes.default;
|
|
2019
3518
|
if (asked === "mock")
|
|
2020
|
-
throw new RuntimeConfigError("Mock mode is for tests; use hand-off, model, headless-claude-code or replay.");
|
|
3519
|
+
throw new RuntimeConfigError("Mock mode is for tests; use hand-off, model, headless-claude-code, copilot-acp or replay.");
|
|
2021
3520
|
const mode = asked;
|
|
2022
3521
|
const runtime = await createRuntime(op, {
|
|
2023
3522
|
root,
|
|
@@ -2025,13 +3524,14 @@ async function runtimeForCli(root, o = {}) {
|
|
|
2025
3524
|
...env.ATTICUS_REPLAY_DIR ? { replayDir: env.ATTICUS_REPLAY_DIR } : {},
|
|
2026
3525
|
...o.providers ? { providers: o.providers } : {},
|
|
2027
3526
|
...o.headless ? { headless: o.headless } : {},
|
|
3527
|
+
...o.copilotAcp ? { copilotAcp: o.copilotAcp } : {},
|
|
2028
3528
|
...o.handoffValidate ? { handoffValidate: o.handoffValidate } : {},
|
|
2029
3529
|
// ADR 0121 addendum: the person's model per kind of step, and a model passed over said once.
|
|
2030
3530
|
env,
|
|
2031
3531
|
onNote: o.onNote ?? sayOnce
|
|
2032
3532
|
});
|
|
2033
|
-
const describe =
|
|
2034
|
-
return { runtime, mode, describe };
|
|
3533
|
+
const describe = runtime.kind === "hand-off" ? "AI steps are handed to your assistant: Atticus writes the prompt and waits for atticus handoff done." : mode === "model" ? `AI steps call the ${op.runtime.adapters.model?.type ?? "configured"} model directly.` : mode === "headless-claude-code" ? "AI steps run in headless Claude Code." : mode === "copilot-acp" ? "AI steps run in GitHub Copilot, unattended, one new session each (copilot-acp)." : "AI steps replay recorded answers.";
|
|
3534
|
+
return { runtime, mode: runtime.kind === "hand-off" ? "hand-off" : mode, describe };
|
|
2035
3535
|
}
|
|
2036
3536
|
|
|
2037
3537
|
// packages/ai/dist/attempts.js
|
|
@@ -2127,8 +3627,8 @@ var HashEmbedder = class {
|
|
|
2127
3627
|
return { i: h.readUInt32BE(0) % this.dim, sign: h[4] & 1 ? 1 : -1 };
|
|
2128
3628
|
};
|
|
2129
3629
|
for (const m of text.matchAll(/[A-Za-z0-9]+/g)) {
|
|
2130
|
-
const
|
|
2131
|
-
for (const w of new Set(
|
|
3630
|
+
const words2 = [m[0].toLowerCase(), ...identifierWords(m[0])];
|
|
3631
|
+
for (const w of new Set(words2)) {
|
|
2132
3632
|
if (w.length < 3)
|
|
2133
3633
|
continue;
|
|
2134
3634
|
const b = bucket(`w:${w}`);
|
|
@@ -2618,6 +4118,10 @@ function recordedJudge(byTexts, o = {}) {
|
|
|
2618
4118
|
}
|
|
2619
4119
|
|
|
2620
4120
|
export {
|
|
4121
|
+
withoutLegacy,
|
|
4122
|
+
personOnlyWhy,
|
|
4123
|
+
isPersonsFactsFile,
|
|
4124
|
+
windowsName,
|
|
2621
4125
|
DEFENCES_VERSION,
|
|
2622
4126
|
INSTRUCTION_RULES,
|
|
2623
4127
|
riskAtLeast,
|
|
@@ -2651,7 +4155,7 @@ export {
|
|
|
2651
4155
|
BIGGER_MODEL,
|
|
2652
4156
|
PromptTooLong,
|
|
2653
4157
|
sha2562 as sha256,
|
|
2654
|
-
|
|
4158
|
+
RequiredSourceLeftOut,
|
|
2655
4159
|
wrapData,
|
|
2656
4160
|
DATA_RULE,
|
|
2657
4161
|
buildContextPack,
|
|
@@ -2667,6 +4171,7 @@ export {
|
|
|
2667
4171
|
RuntimeError,
|
|
2668
4172
|
checkAllowed,
|
|
2669
4173
|
makeRunId,
|
|
4174
|
+
claimRunDir,
|
|
2670
4175
|
toRunSummary,
|
|
2671
4176
|
OneShotRuntime,
|
|
2672
4177
|
ToolError,
|
|
@@ -2683,12 +4188,45 @@ export {
|
|
|
2683
4188
|
recordRun,
|
|
2684
4189
|
MockModelProvider,
|
|
2685
4190
|
ModelRuntime,
|
|
4191
|
+
BATCH_OUTPUT,
|
|
4192
|
+
DEFAULT_BATCH_MAX_TOKENS,
|
|
4193
|
+
batchSections,
|
|
2686
4194
|
HandoffRuntime,
|
|
2687
4195
|
pruneRuns,
|
|
4196
|
+
ACP_PROTOCOL_VERSION,
|
|
4197
|
+
ACP_METHODS,
|
|
4198
|
+
ACP_ERRORS,
|
|
4199
|
+
AcpRpcError,
|
|
4200
|
+
AcpClient,
|
|
4201
|
+
chunkText,
|
|
4202
|
+
UPDATE,
|
|
4203
|
+
usageOf,
|
|
4204
|
+
isAuthRequired,
|
|
4205
|
+
commandLineOf,
|
|
4206
|
+
atticusArgvOf,
|
|
4207
|
+
acpPathsOf,
|
|
4208
|
+
factsOnly,
|
|
4209
|
+
readRootsOf,
|
|
4210
|
+
needsShell,
|
|
4211
|
+
quoteForCmd,
|
|
4212
|
+
killTree,
|
|
4213
|
+
spawnAcpServer,
|
|
4214
|
+
atticusExec,
|
|
4215
|
+
AcpSignInError,
|
|
4216
|
+
AcpSetupError,
|
|
4217
|
+
MAX_NUDGES,
|
|
4218
|
+
carryOnPrompt,
|
|
4219
|
+
ACP_OFFERED,
|
|
4220
|
+
runAcpSession,
|
|
4221
|
+
copilotAcpProbe,
|
|
4222
|
+
CopilotAcpRuntime,
|
|
4223
|
+
keepAcpRun,
|
|
2688
4224
|
RUNTIME_MODES,
|
|
2689
4225
|
RuntimeConfigError,
|
|
2690
4226
|
AiOffError,
|
|
2691
4227
|
claudeCodeCliRunner,
|
|
4228
|
+
resolveWindowsCommand,
|
|
4229
|
+
quoteForCmdAlways,
|
|
2692
4230
|
collectChild,
|
|
2693
4231
|
posixPath,
|
|
2694
4232
|
HeadlessClaudeCodeRuntime,
|
|
@@ -2699,6 +4237,7 @@ export {
|
|
|
2699
4237
|
registeredModelProviders,
|
|
2700
4238
|
clearModelProviders,
|
|
2701
4239
|
registerToolSource,
|
|
4240
|
+
registerCopilotAcp,
|
|
2702
4241
|
createRuntime,
|
|
2703
4242
|
runtimeForCli,
|
|
2704
4243
|
resumedAttempt,
|