@maycombhq/atticus 2.0.0-beta.2 → 2.0.0-beta.21
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-265LWMOA.js +91 -0
- package/dist/chunk-26WBY34Y.js +37 -0
- package/dist/{chunk-UBBC2XSG.js → chunk-26Z7OGQW.js} +518 -93
- package/dist/chunk-2D7PWSOK.js +172 -0
- package/dist/{chunk-JCZIKA4C.js → chunk-2HH3UCZM.js} +4 -4
- package/dist/{chunk-GDLULHTC.js → chunk-2HTEMYTB.js} +12 -26
- package/dist/chunk-2UCRFXFZ.js +130 -0
- package/dist/{chunk-MVZVGYXH.js → chunk-2V2ZPQQU.js} +81 -13
- package/dist/{chunk-BOHJZTEQ.js → chunk-36RY7ZXM.js} +130 -71
- package/dist/{chunk-I3E7CX4H.js → chunk-37WE3LS2.js} +3 -3
- package/dist/{chunk-IPKHE5D6.js → chunk-3AQ2R5NL.js} +1520 -871
- package/dist/chunk-3G6S7LAA.js +43 -0
- package/dist/chunk-3J5I7ZOU.js +58 -0
- package/dist/{chunk-65NYPGRC.js → chunk-3LZ6P5EP.js} +1 -1
- package/dist/chunk-3SSTNB4E.js +191 -0
- package/dist/chunk-3TD6S4TC.js +49 -0
- package/dist/{chunk-3C7EYRFZ.js → chunk-3UTYPHN3.js} +1 -1
- package/dist/{chunk-G2B4ZQGH.js → chunk-3WON4G3M.js} +2 -2
- package/dist/{chunk-S3QNRWYR.js → chunk-42EPP7OB.js} +12 -12
- package/dist/chunk-455SYIIE.js +113 -0
- package/dist/{chunk-YISXLPHN.js → chunk-4B6BT75O.js} +166 -23
- package/dist/{chunk-C6EKFH7J.js → chunk-4CLK2RJO.js} +7 -7
- package/dist/chunk-4CYTJ5TZ.js +38 -0
- package/dist/{chunk-EYWIDJFU.js → chunk-4D62IZII.js} +5 -5
- package/dist/chunk-4FOAYLI4.js +328 -0
- package/dist/{chunk-7JUKDRSZ.js → chunk-4KNOJC6J.js} +294 -215
- package/dist/{chunk-YMZ3TNYB.js → chunk-4PYC2UGC.js} +2 -2
- package/dist/{chunk-27NKUS3X.js → chunk-4SLBCEPD.js} +2104 -726
- package/dist/chunk-4TKVU2I2.js +122 -0
- package/dist/{chunk-RUVFXE2L.js → chunk-4WXRPQDE.js} +448 -47
- package/dist/{chunk-7ZUIMTOU.js → chunk-5CMPVC3N.js} +24 -24
- package/dist/chunk-5G7ILBUG.js +66 -0
- package/dist/{chunk-HFQCFSJR.js → chunk-5PV7S2ZP.js} +3 -3
- package/dist/{chunk-FITWGOJ2.js → chunk-5QUOPNLN.js} +4 -4
- package/dist/{chunk-Z6LF7HQN.js → chunk-5TDRBOAV.js} +5 -5
- package/dist/{chunk-T6AIP7H5.js → chunk-5WERBKLE.js} +199 -63
- package/dist/{chunk-XPCM366L.js → chunk-62DLJSLV.js} +8 -8
- package/dist/{chunk-L24C55VN.js → chunk-64AADMTW.js} +99 -25
- package/dist/{chunk-K65BJIMT.js → chunk-67BP7HXL.js} +17 -17
- package/dist/chunk-6DQYZU2W.js +379 -0
- package/dist/{chunk-EDS2L2LD.js → chunk-6EXDI5GI.js} +52 -26
- package/dist/chunk-6FNAKCET.js +38 -0
- package/dist/{chunk-6IU67AEK.js → chunk-75ASLHBT.js} +21 -15
- package/dist/{chunk-A3GY6SOA.js → chunk-75ZOZFGH.js} +18 -18
- package/dist/{chunk-MGYZM27B.js → chunk-77O633L5.js} +49 -23
- package/dist/{chunk-GQ4N4WKF.js → chunk-7AB6OWTV.js} +3 -3
- package/dist/{chunk-M766ZJ6W.js → chunk-7CZHUZBL.js} +25 -25
- package/dist/{chunk-A7UPUQFY.js → chunk-7FHNFCHK.js} +643 -124
- package/dist/chunk-7FUIVIBG.js +415 -0
- package/dist/{chunk-6QSERWXH.js → chunk-7QPCGTPS.js} +21 -21
- package/dist/{chunk-LR3R6Z2A.js → chunk-A2G72UI4.js} +7 -7
- package/dist/{chunk-I4JRCD6M.js → chunk-AE4P57KK.js} +23 -17
- package/dist/{chunk-AVJDSJ2F.js → chunk-AHW3RY3X.js} +7 -7
- package/dist/chunk-AM6FP6JW.js +43 -0
- package/dist/{chunk-CSZ4C5N6.js → chunk-AOCWFA7T.js} +93 -10
- package/dist/{chunk-EPTBZ3GP.js → chunk-API5TOUB.js} +1 -1
- package/dist/{chunk-652QWTLZ.js → chunk-AZDITBCT.js} +3 -3
- package/dist/{chunk-DPUYDDQG.js → chunk-BA2HUMDN.js} +25 -3
- package/dist/chunk-BA3U7ALS.js +99 -0
- package/dist/{chunk-HVUPKCGJ.js → chunk-BC6KKZ67.js} +5 -5
- package/dist/{chunk-CKSITVS4.js → chunk-BEPCKDCP.js} +2554 -722
- package/dist/{chunk-3ALD2W2A.js → chunk-BPPU3PW7.js} +4 -4
- package/dist/{chunk-QRAOANID.js → chunk-BVWWPBKD.js} +21 -21
- package/dist/{chunk-ZDPAS56H.js → chunk-BXBCMGIQ.js} +7 -1
- package/dist/chunk-BXZ4PKIT.js +233 -0
- package/dist/chunk-C7CEK3KW.js +247 -0
- package/dist/chunk-CFH3QH26.js +29 -0
- package/dist/chunk-CG6CULP6.js +170 -0
- package/dist/chunk-CLD4RVTA.js +45 -0
- package/dist/{chunk-DKJA76CJ.js → chunk-CNPY4NTZ.js} +13 -13
- package/dist/chunk-CNU5Q2D5.js +575 -0
- package/dist/{chunk-T2UYZSCQ.js → chunk-COYN3UAN.js} +126 -23
- package/dist/{chunk-23W7VWKR.js → chunk-CPK7QSBG.js} +1360 -113
- package/dist/{chunk-4AK7IBVP.js → chunk-D2WSW4GU.js} +1 -1
- package/dist/{chunk-ABWHYWNI.js → chunk-D57MFJPP.js} +3 -3
- package/dist/chunk-D5KV5PSK.js +45 -0
- package/dist/chunk-D5MASS4I.js +48 -0
- package/dist/{chunk-7EOBHUMC.js → chunk-DCWN3HFV.js} +2 -2
- package/dist/{chunk-QSXK2RGH.js → chunk-DDJPM5YX.js} +2 -2
- package/dist/chunk-DN4A2Q2O.js +57 -0
- package/dist/{chunk-IHMLFRVV.js → chunk-DUPZPDCJ.js} +58 -16
- package/dist/{chunk-PRPPGYZG.js → chunk-E2VXBSIR.js} +4 -4
- package/dist/chunk-E57AOFYN.js +32 -0
- package/dist/{chunk-UGLN7QHV.js → chunk-ECLXZWAG.js} +12 -12
- package/dist/{chunk-5WG5YP4T.js → chunk-EDZ45WIR.js} +14 -14
- package/dist/chunk-EFCQSMXB.js +37 -0
- package/dist/{chunk-2ANOROX3.js → chunk-EZIN5E5B.js} +8 -8
- package/dist/{chunk-BWZ77RQT.js → chunk-F6H2RJ3N.js} +1 -1
- package/dist/{chunk-OLQJTJJU.js → chunk-FAJDKNSD.js} +7 -3
- package/dist/chunk-FGAGWYZB.js +47 -0
- package/dist/{chunk-4WBVUHXN.js → chunk-FLVZFTLQ.js} +18 -18
- package/dist/chunk-FQ6WH6PB.js +140 -0
- package/dist/{chunk-T6XQL73F.js → chunk-FRHD22WM.js} +61 -23
- package/dist/{chunk-XAKWMCXI.js → chunk-FVMPFCDS.js} +4 -4
- package/dist/{chunk-B2WLAWNJ.js → chunk-FY3QIRTL.js} +3 -3
- package/dist/{chunk-CPA6HPVR.js → chunk-FYBF5MB2.js} +15 -11
- package/dist/{chunk-V2YTAIQO.js → chunk-G5YJBNDZ.js} +21 -6
- package/dist/chunk-G6IQYM25.js +339 -0
- package/dist/chunk-G6X4YL4D.js +161 -0
- package/dist/{chunk-SJRGMG6A.js → chunk-GC2VK3JT.js} +25 -25
- package/dist/{chunk-SP2I63OO.js → chunk-GCS3FZOB.js} +77 -3
- package/dist/{chunk-6TACQN3N.js → chunk-GDZB57IU.js} +5 -5
- package/dist/chunk-GIFMBCTW.js +221 -0
- package/dist/{chunk-YY6ZQQBP.js → chunk-GK4PYVEC.js} +18 -8
- package/dist/{chunk-TDD55CJH.js → chunk-GNWAOVFQ.js} +6 -5
- package/dist/chunk-GS76RXQE.js +226 -0
- package/dist/{chunk-YYQN46FY.js → chunk-GSGIZ2OO.js} +23 -23
- package/dist/chunk-GUYL7RMK.js +60 -0
- package/dist/chunk-GX74YWR7.js +321 -0
- package/dist/chunk-HE3HRDNZ.js +122 -0
- package/dist/{chunk-AIOQPLA5.js → chunk-HGHRSF3E.js} +30 -12
- package/dist/{chunk-2RMYG36Q.js → chunk-HK3VOHIE.js} +198 -336
- package/dist/{chunk-QZSX3F7C.js → chunk-HS4BGB4G.js} +24 -22
- package/dist/{chunk-XY3P7JW7.js → chunk-HSZMPH5T.js} +834 -149
- package/dist/{chunk-L6SGQIR3.js → chunk-HWD4JPU7.js} +4 -2
- package/dist/{chunk-PCVDV3UZ.js → chunk-HXXP2NTE.js} +6 -6
- package/dist/{chunk-AA6YULYB.js → chunk-I2A3Z5AK.js} +7 -7
- package/dist/{chunk-N7IXEHK3.js → chunk-ICHDSWEY.js} +7 -7
- package/dist/chunk-IFFRGC3N.js +31 -0
- package/dist/{chunk-4BN53TG3.js → chunk-IFHDV6KQ.js} +31 -23
- package/dist/chunk-J2O4LFVI.js +80 -0
- package/dist/chunk-J5AAZ6OJ.js +161 -0
- package/dist/{chunk-ZMFPW66A.js → chunk-JAABT3DA.js} +3 -3
- package/dist/chunk-JITJP64K.js +34 -0
- package/dist/chunk-JKIGL3XQ.js +162 -0
- package/dist/chunk-JRVNXLZP.js +615 -0
- package/dist/chunk-JRXHON4E.js +41 -0
- package/dist/{chunk-DHPACDG7.js → chunk-JS6WMC3J.js} +8 -8
- package/dist/chunk-JU4QNJSR.js +62 -0
- package/dist/{chunk-PAMPPUR6.js → chunk-JYMIB5XY.js} +1697 -172
- package/dist/{chunk-RRTKKSLQ.js → chunk-JYTIHTIK.js} +8 -6
- package/dist/{chunk-3HI6Z55Y.js → chunk-K332XW7K.js} +324 -39
- package/dist/{chunk-GLUX4EEP.js → chunk-K6JB6XTX.js} +9 -9
- package/dist/chunk-KDAHXQIE.js +294 -0
- package/dist/chunk-KYZMRSPS.js +93 -0
- package/dist/chunk-L4CESDPB.js +22 -0
- package/dist/{chunk-NXFPS2LR.js → chunk-LEBR5NHW.js} +14 -14
- package/dist/chunk-LIJJCBQI.js +223 -0
- package/dist/{chunk-DE76JROX.js → chunk-LIRVU3HW.js} +16 -16
- package/dist/{chunk-JMSCMUCA.js → chunk-LN77DXBM.js} +145 -21
- package/dist/{chunk-JIITQXFB.js → chunk-LNWEWYCM.js} +2969 -788
- package/dist/{chunk-TANFV3MZ.js → chunk-LO3BLMVG.js} +3 -3
- package/dist/chunk-LPV6T362.js +250 -0
- package/dist/{chunk-2VHF7OXM.js → chunk-LQH4P45K.js} +1 -1
- package/dist/chunk-LU4MQVAL.js +41 -0
- package/dist/chunk-LWDNKE4X.js +273 -0
- package/dist/chunk-M4H2UG4R.js +218 -0
- package/dist/{chunk-6BVONWJ7.js → chunk-M6D2U3YM.js} +3 -3
- package/dist/chunk-MD7KUEJ3.js +514 -0
- package/dist/{chunk-66YVAGGM.js → chunk-MMZUN7ZO.js} +20 -20
- package/dist/chunk-MZGRDONP.js +916 -0
- package/dist/chunk-N3RUGTGV.js +39 -0
- package/dist/{chunk-JGZCG5VM.js → chunk-NCB6656Z.js} +7 -7
- package/dist/{chunk-4NDAAFQT.js → chunk-NRQ65LTQ.js} +7 -5
- package/dist/{chunk-UNM6ZOKP.js → chunk-NTAVOWYD.js} +145 -18
- package/dist/{chunk-3USYZGRF.js → chunk-NYOABJNK.js} +36 -27
- package/dist/{chunk-WVPJSRRJ.js → chunk-O4IKAIZ2.js} +24 -20
- package/dist/{chunk-D3PO4DOZ.js → chunk-ODMBPDDV.js} +51 -14
- package/dist/chunk-OKUB6RJA.js +533 -0
- package/dist/chunk-OMPENNWY.js +39 -0
- package/dist/chunk-OPQZISG3.js +25 -0
- package/dist/{chunk-5GGOLAKC.js → chunk-OV57AHCG.js} +26 -26
- package/dist/chunk-OZJ3JLYI.js +58 -0
- package/dist/{chunk-KEM4SDDP.js → chunk-P2VBXX7N.js} +11 -10
- package/dist/{chunk-S2A7YC3Q.js → chunk-PAINMSID.js} +10150 -1720
- package/dist/chunk-PKJAV25S.js +43 -0
- package/dist/{chunk-EBEW3XXQ.js → chunk-PNQLCRSS.js} +4 -4
- package/dist/{chunk-WY6PP6AA.js → chunk-PQH56YF6.js} +37 -5
- package/dist/chunk-PRTBPXYA.js +50 -0
- package/dist/{chunk-YEDDOYSC.js → chunk-PRW3HXM3.js} +2 -2
- package/dist/chunk-PRZ7KMWL.js +42 -0
- package/dist/{chunk-6M5NK3K3.js → chunk-PTQ3ZALZ.js} +42 -4
- package/dist/{chunk-5GDRCDQB.js → chunk-PUEWDYGP.js} +2 -2
- package/dist/chunk-Q26LDIUV.js +236 -0
- package/dist/{chunk-CR3MTH32.js → chunk-Q46QDDMF.js} +77 -7
- package/dist/chunk-QBNWF5ZU.js +19 -0
- package/dist/chunk-QE527XRY.js +342 -0
- package/dist/{chunk-CNUKCS2T.js → chunk-QHJJMZLX.js} +1518 -54
- package/dist/chunk-QJCJOMW3.js +425 -0
- package/dist/{chunk-VYDBJ73M.js → chunk-QKMRFNMA.js} +15 -15
- package/dist/chunk-QOOEYDNJ.js +317 -0
- package/dist/chunk-QSIDVFB7.js +45 -0
- package/dist/{chunk-R77KLRB3.js → chunk-RA33EYS4.js} +19 -18
- package/dist/{chunk-DFBCSHVT.js → chunk-RALY3E5U.js} +4 -4
- package/dist/{chunk-ZCJDOK2S.js → chunk-S2RY3BOD.js} +35 -21
- package/dist/chunk-S5LSDXV4.js +40 -0
- package/dist/chunk-SB5GRUFM.js +54 -0
- package/dist/chunk-SH27FYPF.js +150 -0
- package/dist/chunk-T74WQQGS.js +40 -0
- package/dist/{chunk-O3WI5KWW.js → chunk-TGVVUKTG.js} +2 -2
- package/dist/chunk-THN26IEF.js +48 -0
- package/dist/chunk-TJH2EUAZ.js +47 -0
- package/dist/{chunk-ZNCEKR4J.js → chunk-TQ4TG57V.js} +12 -8
- package/dist/{chunk-7FBGJFEE.js → chunk-TQTRWENN.js} +12 -12
- package/dist/{chunk-TYAID7B7.js → chunk-TSM6T6GQ.js} +23 -23
- package/dist/{chunk-DPZHZQKE.js → chunk-TW3KOSQK.js} +43 -10
- package/dist/{chunk-MC3H7736.js → chunk-TWQRPM76.js} +37 -4
- package/dist/chunk-TYD6I4CZ.js +109 -0
- package/dist/chunk-U3NBK4EU.js +420 -0
- package/dist/{chunk-6JCPI55B.js → chunk-UDXWIPCB.js} +47 -7
- package/dist/{chunk-MMOSZPWJ.js → chunk-UEVZZZU2.js} +4 -4
- package/dist/{chunk-JBTK5OP2.js → chunk-UF4UHCIG.js} +2 -2
- package/dist/{chunk-6JUBZBMW.js → chunk-UFE36WJH.js} +4 -4
- package/dist/{chunk-ONLS5QA5.js → chunk-URIKUID7.js} +12 -12
- package/dist/chunk-UYLBRRFK.js +1106 -0
- package/dist/chunk-UZACEG2C.js +515 -0
- package/dist/{chunk-7SW2QWLU.js → chunk-VAMHOHUU.js} +12 -12
- package/dist/{chunk-RZPYKDWS.js → chunk-VUVGX5L2.js} +1 -1
- package/dist/{chunk-Q4D5X7QB.js → chunk-VYOCPTUB.js} +44 -24
- package/dist/{chunk-CAQZUNOH.js → chunk-WAZEXEJH.js} +567 -3
- package/dist/chunk-WCEF7H6L.js +221 -0
- package/dist/{chunk-2DIQQJCB.js → chunk-WEOMJHPV.js} +12 -12
- package/dist/{chunk-FHH4I4ZV.js → chunk-WFU77T3A.js} +12 -6
- package/dist/chunk-WIWTJMMR.js +292 -0
- package/dist/{chunk-2BFCZ3RK.js → chunk-WNTIOBOG.js} +18 -18
- package/dist/{chunk-7WD63HU2.js → chunk-X2CUXTWU.js} +6 -6
- package/dist/chunk-X7EMBM5X.js +34 -0
- package/dist/chunk-XEQCQO66.js +33 -0
- package/dist/{chunk-F7HYOUFF.js → chunk-XG4WJHLQ.js} +20 -4
- package/dist/{chunk-EFHQ3TWP.js → chunk-XOL3GFW2.js} +171 -31
- package/dist/{chunk-NE2W3OO7.js → chunk-XQ3YZ4QW.js} +22 -22
- package/dist/{chunk-CEXSU4S6.js → chunk-Y6K3CJBU.js} +723 -85
- package/dist/{chunk-4RS5KZQD.js → chunk-YAH5IQAL.js} +5 -5
- package/dist/{chunk-64EQYWCA.js → chunk-YBOVNMHC.js} +51 -14
- package/dist/chunk-YF5DZCPN.js +1212 -0
- package/dist/chunk-YIGBJNH4.js +37 -0
- package/dist/{chunk-BKVWTZ6A.js → chunk-YJ3TBOTK.js} +25 -25
- package/dist/{chunk-Q5AMFEWD.js → chunk-YKTE2RU3.js} +2 -2
- package/dist/{chunk-7EKXJZ2Y.js → chunk-YO2PKTXT.js} +296 -37
- package/dist/{chunk-6J5OKA4Y.js → chunk-YTTNTQOX.js} +15 -15
- package/dist/{chunk-OT2O76OS.js → chunk-YWAKDNMH.js} +183 -33
- package/dist/{chunk-R6XF6UR3.js → chunk-YZDZOEV4.js} +4 -4
- package/dist/chunk-Z3QFH7YG.js +17 -0
- package/dist/{chunk-W5BVQF44.js → chunk-Z6VLC5X4.js} +17 -17
- package/dist/{chunk-HLJAXMM4.js → chunk-ZFQMMU5Y.js} +2 -2
- package/dist/{chunk-W3BQAZ3Q.js → chunk-ZKYRDV7A.js} +12 -12
- package/dist/{chunk-ZMAINWBW.js → chunk-ZLS6PU2O.js} +3 -3
- package/dist/{chunk-ZVM6AKKM.js → chunk-ZOSD4FXV.js} +3 -3
- package/dist/chunk-ZPDDEVQX.js +47 -0
- package/dist/{chunk-TXLH6WHQ.js → chunk-ZQLRCA2V.js} +11 -11
- package/dist/{chunk-HITVBIJS.js → chunk-ZX7235KR.js} +128 -54
- package/dist/{chunk-GT4I4ZWM.js → chunk-ZZFHLRQW.js} +18 -16
- package/dist/files.json +636 -364
- package/dist/packages/naysayer/constitution/constitution.yaml +19 -0
- package/dist/packages/sdk/npm/index.js +5614 -2343
- package/dist/packages/ui/dist/assets/{elk-api-tma0MCuh.js → elk-api-CowTnhEJ.js} +2 -2
- package/dist/packages/ui/dist/assets/index-Dw20IRhQ.css +1 -0
- package/dist/packages/ui/dist/assets/index-z2wv0d5d.js +47 -0
- package/dist/packages/ui/dist/index.html +2 -2
- package/package.json +4 -4
- 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-RVH5SUW5.js +0 -69
- package/dist/chunk-VN3DP4LQ.js +0 -43
- 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
|
@@ -0,0 +1,1106 @@
|
|
|
1
|
+
import { createRequire as __atticusCreateRequire } from "node:module";
|
|
2
|
+
import { fileURLToPath as __atticusFileURLToPath } from "node:url";
|
|
3
|
+
var require = __atticusCreateRequire(import.meta.url);
|
|
4
|
+
var __atticusUrl = (p) => new URL("./" + p, import.meta.url).href;
|
|
5
|
+
var __atticusPath = (p) => __atticusFileURLToPath(__atticusUrl(p));
|
|
6
|
+
import {
|
|
7
|
+
safeMessage,
|
|
8
|
+
withFileLock
|
|
9
|
+
} from "./chunk-THN26IEF.js";
|
|
10
|
+
import {
|
|
11
|
+
checkoutOf
|
|
12
|
+
} from "./chunk-Q46QDDMF.js";
|
|
13
|
+
import {
|
|
14
|
+
AI_LINE_BY,
|
|
15
|
+
AnswerMatch,
|
|
16
|
+
FactCorrection,
|
|
17
|
+
HOSTS_NONE,
|
|
18
|
+
JOINED_BY_AI,
|
|
19
|
+
JOINED_BY_ANSWER,
|
|
20
|
+
JOINED_BY_PERSON,
|
|
21
|
+
Question,
|
|
22
|
+
QuestionError,
|
|
23
|
+
RULE_AFTER_CORRECTIONS,
|
|
24
|
+
TARGETS_STEP_ID,
|
|
25
|
+
aiPause,
|
|
26
|
+
answerByWords,
|
|
27
|
+
answerQuestion,
|
|
28
|
+
auditPrecision,
|
|
29
|
+
batchRelOf,
|
|
30
|
+
callsOfQuestion,
|
|
31
|
+
catalogDirOf,
|
|
32
|
+
checkedProposals,
|
|
33
|
+
correctionLine,
|
|
34
|
+
decideOf,
|
|
35
|
+
deriveMatch,
|
|
36
|
+
factsDirOf,
|
|
37
|
+
factsRelOf,
|
|
38
|
+
isAiBy,
|
|
39
|
+
linkCandidates,
|
|
40
|
+
linksFor,
|
|
41
|
+
listBatches,
|
|
42
|
+
listQuestions,
|
|
43
|
+
loadOrgPack,
|
|
44
|
+
matchCovers,
|
|
45
|
+
newBatchId,
|
|
46
|
+
orgPackDirOf,
|
|
47
|
+
pairingBar,
|
|
48
|
+
parseAuditFile,
|
|
49
|
+
parseCorrections,
|
|
50
|
+
parseFacts,
|
|
51
|
+
proposalRelOf,
|
|
52
|
+
questionAiProblem,
|
|
53
|
+
questionBar,
|
|
54
|
+
questionsRelOf,
|
|
55
|
+
readBatch,
|
|
56
|
+
readQuestion,
|
|
57
|
+
ruleKindOf,
|
|
58
|
+
writeBatch,
|
|
59
|
+
writeQuestion
|
|
60
|
+
} from "./chunk-PAINMSID.js";
|
|
61
|
+
import {
|
|
62
|
+
writeFileAtomic
|
|
63
|
+
} from "./chunk-ODMBPDDV.js";
|
|
64
|
+
|
|
65
|
+
// packages/cli/dist/ai-decide.js
|
|
66
|
+
import { readdir, readFile, rm, stat } from "node:fs/promises";
|
|
67
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
68
|
+
var DecideError = class extends Error {
|
|
69
|
+
code;
|
|
70
|
+
constructor(code, message) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.code = code;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var inAcpSession = (env = process.env) => !!env.ATTICUS_ACP_SESSION;
|
|
76
|
+
var AI_RUNTIME = "copilot-acp";
|
|
77
|
+
async function factsOf(root, repo) {
|
|
78
|
+
const text = await readFile(join(factsDirOf(root), `${repo}.jsonl`), "utf8").catch(() => "");
|
|
79
|
+
if (!text.trim())
|
|
80
|
+
return [];
|
|
81
|
+
const r = parseFacts(text);
|
|
82
|
+
return r.ok ? r.facts : [];
|
|
83
|
+
}
|
|
84
|
+
var correctionsFile = (root, repo) => join(factsDirOf(root), `${repo}.corrections.jsonl`);
|
|
85
|
+
var correctionsRel = (root, repo) => `${factsRelOf(root)}/${repo}.corrections.jsonl`;
|
|
86
|
+
async function correctionsOf(root, repo) {
|
|
87
|
+
const text = await readFile(correctionsFile(root, repo), "utf8").catch(() => "");
|
|
88
|
+
return parseCorrections(text).corrections;
|
|
89
|
+
}
|
|
90
|
+
async function correctedRepos(root) {
|
|
91
|
+
return (await readdir(factsDirOf(root)).catch(() => [])).filter((n) => n.endsWith(".corrections.jsonl")).map((n) => n.slice(0, -".corrections.jsonl".length)).sort();
|
|
92
|
+
}
|
|
93
|
+
async function rewriteCorrections(root, repo, o) {
|
|
94
|
+
const file = correctionsFile(root, repo);
|
|
95
|
+
return withFileLock(file, async () => {
|
|
96
|
+
const text = await readFile(file, "utf8").catch(() => "");
|
|
97
|
+
const eol = /\r\n/.test(text) ? "\r\n" : "\n";
|
|
98
|
+
const lines = text.split(/\r?\n/).filter((l, i, all) => l !== "" || i < all.length - 1);
|
|
99
|
+
let removed = 0;
|
|
100
|
+
const gone = new Set((o.remove ?? []).map((l) => l.trim()));
|
|
101
|
+
const kept = lines.filter((l) => {
|
|
102
|
+
if (l.trim() && gone.has(l.trim())) {
|
|
103
|
+
gone.delete(l.trim());
|
|
104
|
+
removed++;
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
});
|
|
109
|
+
const have = new Set(kept.map((l) => l.trim()));
|
|
110
|
+
const wanted = o.pick ? o.pick(parseCorrections(text).corrections) : o.add ?? [];
|
|
111
|
+
const add = wanted.filter((l) => !have.has(l.trim()));
|
|
112
|
+
const out = [...kept.filter((l) => l.trim() !== ""), ...add];
|
|
113
|
+
if (!removed && !add.length)
|
|
114
|
+
return { removed: 0, added: 0, ...o.pick ? { picked: [] } : {} };
|
|
115
|
+
await writeFileAtomic(file, out.length ? out.join(eol) + eol : "");
|
|
116
|
+
return { removed, added: add.length, ...o.pick ? { picked: add } : {} };
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
var callLikeOf = (c) => ({
|
|
120
|
+
kind: "call",
|
|
121
|
+
id: c.fact,
|
|
122
|
+
cite: { file: c.target.file, line: c.target.line, value: c.target.value }
|
|
123
|
+
});
|
|
124
|
+
function standingAi(corrections) {
|
|
125
|
+
return corrections.filter((c) => c.ai && linksFor(callLikeOf(c), corrections)[0] === c);
|
|
126
|
+
}
|
|
127
|
+
function subjectOfCall(f) {
|
|
128
|
+
if (f.callType === "event-topic" || f.callType === "mq-queue")
|
|
129
|
+
return { topic: f.path };
|
|
130
|
+
if (f.path?.startsWith("/"))
|
|
131
|
+
return { path: f.path };
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
async function coversCall(match, f, facts) {
|
|
135
|
+
const s = subjectOfCall(f);
|
|
136
|
+
if (s?.topic)
|
|
137
|
+
return matchCovers(match, s);
|
|
138
|
+
const { ruleCoversCall } = await import("./chunk-BXZ4PKIT.js");
|
|
139
|
+
return ruleCoversCall(match, f, facts);
|
|
140
|
+
}
|
|
141
|
+
async function aiAuditItems(root) {
|
|
142
|
+
const out = [];
|
|
143
|
+
for (const n of (await readdir(factsDirOf(root)).catch(() => [])).sort()) {
|
|
144
|
+
if (!n.endsWith(".audit.yaml"))
|
|
145
|
+
continue;
|
|
146
|
+
const r = parseAuditFile(await readFile(join(factsDirOf(root), n), "utf8").catch(() => ""));
|
|
147
|
+
if (r.ok)
|
|
148
|
+
out.push(...r.file.items.filter((i) => i.kind === "ai"));
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
async function decideState(root) {
|
|
153
|
+
const op = await loadOrgPack(orgPackDirOf(root));
|
|
154
|
+
const configured = decideOf(op.runtime);
|
|
155
|
+
const precision = auditPrecision(await aiAuditItems(root)).ai;
|
|
156
|
+
return {
|
|
157
|
+
configured,
|
|
158
|
+
now: aiPause(configured, precision),
|
|
159
|
+
precision,
|
|
160
|
+
decideIn: decideIn(op.runtime)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function decideIn(rt) {
|
|
164
|
+
if (rt.ai === "off")
|
|
165
|
+
return [];
|
|
166
|
+
const out = [];
|
|
167
|
+
if (rt.modes.allowed.includes("copilot-acp") && rt.assistants.allowed.includes("copilot"))
|
|
168
|
+
out.push("copilot-acp");
|
|
169
|
+
if (rt.modes.allowed.includes("hand-off"))
|
|
170
|
+
out.push("hand-off");
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
var notHere = (runtime, state) => runtime === "hand-off" && !state.decideIn.includes("hand-off") ? "runtime.yaml doesn't allow hand-off here (modes.allowed): a person confirms it" : void 0;
|
|
174
|
+
async function pairingCitesProblem(root, repos, lines) {
|
|
175
|
+
if (!lines.length)
|
|
176
|
+
return "it names no line it rests on";
|
|
177
|
+
const dirs = [];
|
|
178
|
+
for (const r of repos) {
|
|
179
|
+
const d = await checkoutOf(root, r).catch(() => void 0);
|
|
180
|
+
if (d)
|
|
181
|
+
dirs.push(d);
|
|
182
|
+
}
|
|
183
|
+
if (!dirs.length)
|
|
184
|
+
return "no checkout of either repo is here to check its lines";
|
|
185
|
+
for (const l of lines) {
|
|
186
|
+
const m = /^(.+):(\d+)$/.exec(l);
|
|
187
|
+
if (!m)
|
|
188
|
+
return `${l} isn't a path:line`;
|
|
189
|
+
const rel = m[1].replace(/\\/g, "/").replace(/^\.\//, "");
|
|
190
|
+
if (isAbsolute(rel) || rel.split("/").includes(".."))
|
|
191
|
+
return `${l} leaves the repo`;
|
|
192
|
+
let found = false;
|
|
193
|
+
for (const d of dirs) {
|
|
194
|
+
const file = resolve(d, rel);
|
|
195
|
+
const back = relative(d, file);
|
|
196
|
+
if (!back || back.startsWith("..") || isAbsolute(back))
|
|
197
|
+
continue;
|
|
198
|
+
const ok = await stat(file).then((s) => s.isFile(), () => false);
|
|
199
|
+
if (!ok)
|
|
200
|
+
continue;
|
|
201
|
+
const text = (await readFile(file, "utf8").catch(() => "")).split(/\r?\n/);
|
|
202
|
+
if ((text[Number(m[2]) - 1] ?? "").trim())
|
|
203
|
+
found = true;
|
|
204
|
+
}
|
|
205
|
+
if (!found)
|
|
206
|
+
return `${l} isn't a line with words on it in either checkout`;
|
|
207
|
+
}
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
async function linkRulesOf(root, repo) {
|
|
211
|
+
return (await listBatches(root)).batches.filter((b) => b.kind === "rule" && !b.undone && !!b.correction?.rule && b.correction.item.startsWith(`link:${repo}:`)).map((b) => ({ match: b.correction.rule.match, answer: b.correction.answer, batch: b.id }));
|
|
212
|
+
}
|
|
213
|
+
async function decideLinks(root, o) {
|
|
214
|
+
if (inAcpSession(o.env) || !o.proposals.length)
|
|
215
|
+
return void 0;
|
|
216
|
+
const state = await decideState(root);
|
|
217
|
+
if (state.configured.links === "propose")
|
|
218
|
+
return void 0;
|
|
219
|
+
const runtime = o.runtime ?? AI_RUNTIME;
|
|
220
|
+
const kept = [];
|
|
221
|
+
const off = notHere(runtime, state);
|
|
222
|
+
if (off) {
|
|
223
|
+
for (const p of o.proposals)
|
|
224
|
+
kept.push({ call: p.call, api: p.api, why: off });
|
|
225
|
+
return { mode: "propose", applied: [], kept };
|
|
226
|
+
}
|
|
227
|
+
const paused = state.now.paused?.why;
|
|
228
|
+
if (state.now.links === "propose") {
|
|
229
|
+
for (const p of o.proposals)
|
|
230
|
+
kept.push({ call: p.call, api: p.api, why: "deciding is paused: a person confirms it" });
|
|
231
|
+
return { mode: "propose", applied: [], kept, ...paused ? { paused } : {} };
|
|
232
|
+
}
|
|
233
|
+
const to = o.view.to.repo;
|
|
234
|
+
if (!to) {
|
|
235
|
+
for (const p of o.proposals)
|
|
236
|
+
kept.push({
|
|
237
|
+
call: p.call,
|
|
238
|
+
api: p.api,
|
|
239
|
+
why: "the AI pairs only with a repo's entry: a pairing with an outside system is a person's"
|
|
240
|
+
});
|
|
241
|
+
return { mode: "auto", applied: [], kept };
|
|
242
|
+
}
|
|
243
|
+
const at = (o.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
244
|
+
const batch = newBatchId(o.now ?? /* @__PURE__ */ new Date());
|
|
245
|
+
const rules = await linkRulesOf(root, o.view.from);
|
|
246
|
+
const applied = [];
|
|
247
|
+
const lines = [];
|
|
248
|
+
const items = [];
|
|
249
|
+
const calls = /* @__PURE__ */ new Map();
|
|
250
|
+
for (const p of o.proposals) {
|
|
251
|
+
const call = o.view.calls.find((c) => c.fact === p.call && c.status !== "paired");
|
|
252
|
+
const f = o.facts.find((x) => x.id === p.call && x.kind === "call");
|
|
253
|
+
const api = o.view.apis.find((a) => a.id === p.api && a.kind !== "operation");
|
|
254
|
+
if (!call || !f || !api) {
|
|
255
|
+
kept.push({ call: p.call, api: p.api, why: "the call or the API isn't on record as loose" });
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
let ruleAgrees = false;
|
|
259
|
+
let against;
|
|
260
|
+
for (const r2 of rules)
|
|
261
|
+
if (await coversCall(r2.match, f, o.facts)) {
|
|
262
|
+
if (r2.answer === to)
|
|
263
|
+
ruleAgrees = true;
|
|
264
|
+
else
|
|
265
|
+
against = r2;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
if (against) {
|
|
269
|
+
kept.push({
|
|
270
|
+
call: p.call,
|
|
271
|
+
api: p.api,
|
|
272
|
+
why: `a person's rule (${against.match}) says these calls go to ${against.answer}`
|
|
273
|
+
});
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const cites = await pairingCitesProblem(root, [o.view.from, to], p.lines);
|
|
277
|
+
if (cites) {
|
|
278
|
+
kept.push({ call: p.call, api: p.api, why: `the citation check fails: ${cites}` });
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const bar = pairingBar(call, p.api, { ruleAgrees });
|
|
282
|
+
if (!bar) {
|
|
283
|
+
kept.push({
|
|
284
|
+
call: p.call,
|
|
285
|
+
api: p.api,
|
|
286
|
+
why: "the shapes don't rank that API first and no person's rule says so: a person confirms it"
|
|
287
|
+
});
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const line = correctionLine(FactCorrection.parse({
|
|
291
|
+
schemaVersion: 2,
|
|
292
|
+
fact: f.id,
|
|
293
|
+
target: { kind: "call", file: f.cite.file, line: f.cite.line, value: f.cite.value },
|
|
294
|
+
action: "link",
|
|
295
|
+
link: {
|
|
296
|
+
repo: to,
|
|
297
|
+
entry: api.id,
|
|
298
|
+
...api.method ? { method: api.method } : {},
|
|
299
|
+
path: api.path
|
|
300
|
+
},
|
|
301
|
+
by: AI_LINE_BY,
|
|
302
|
+
at,
|
|
303
|
+
ai: { runtime, run: o.run, bar },
|
|
304
|
+
batch
|
|
305
|
+
}));
|
|
306
|
+
lines.push(line);
|
|
307
|
+
items.push({ kind: "link", repo: o.view.from, fact: f.id, line });
|
|
308
|
+
applied.push({ call: p.call, api: p.api, bar });
|
|
309
|
+
calls.set(line, f);
|
|
310
|
+
}
|
|
311
|
+
if (!lines.length)
|
|
312
|
+
return { mode: "auto", applied, kept };
|
|
313
|
+
const r = await rewriteCorrections(root, o.view.from, {
|
|
314
|
+
pick: (now) => lines.filter((l) => {
|
|
315
|
+
const f = calls.get(l);
|
|
316
|
+
return !linksFor(f, now).some((c) => !c.ai);
|
|
317
|
+
})
|
|
318
|
+
});
|
|
319
|
+
const written = new Set(r.picked ?? []);
|
|
320
|
+
for (let i = applied.length - 1; i >= 0; i--) {
|
|
321
|
+
if (written.has(lines[i]))
|
|
322
|
+
continue;
|
|
323
|
+
kept.push({
|
|
324
|
+
call: applied[i].call,
|
|
325
|
+
api: applied[i].api,
|
|
326
|
+
why: "a person has said where this call goes: their word stands"
|
|
327
|
+
});
|
|
328
|
+
applied.splice(i, 1);
|
|
329
|
+
items.splice(i, 1);
|
|
330
|
+
}
|
|
331
|
+
if (!applied.length)
|
|
332
|
+
return { mode: "auto", applied, kept };
|
|
333
|
+
await writeBatch(root, {
|
|
334
|
+
schema: 1,
|
|
335
|
+
id: batch,
|
|
336
|
+
kind: "ai",
|
|
337
|
+
at,
|
|
338
|
+
by: { kind: "ai", runtime, run: o.run },
|
|
339
|
+
applied: items,
|
|
340
|
+
reverted: []
|
|
341
|
+
});
|
|
342
|
+
return { mode: "auto", batch, applied, kept };
|
|
343
|
+
}
|
|
344
|
+
async function proposalsSnapshot(root) {
|
|
345
|
+
const out = /* @__PURE__ */ new Map();
|
|
346
|
+
for (const p of await checkedProposals(root)) {
|
|
347
|
+
const text = await readFile(join(root, ...p.file.split("/")), "utf8").catch(() => void 0);
|
|
348
|
+
if (text !== void 0)
|
|
349
|
+
out.set(p.id, text);
|
|
350
|
+
}
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
async function targetRuleVerdict(root, q, label, questions) {
|
|
354
|
+
const facts = await factsOf(root, q.repo);
|
|
355
|
+
const calls = callsOfQuestion(q, /* @__PURE__ */ new Map([[q.repo, facts]]));
|
|
356
|
+
if (!calls.length)
|
|
357
|
+
return void 0;
|
|
358
|
+
const op = await loadOrgPack(orgPackDirOf(root));
|
|
359
|
+
const { estateRepos, ruledTargets } = await import("./chunk-BXZ4PKIT.js");
|
|
360
|
+
const repoType = estateRepos(op).find((r) => r.id === q.repo)?.type;
|
|
361
|
+
const ruled = ruledTargets(q.repo, facts, questions, repoType ? { repoType } : {});
|
|
362
|
+
const labels = calls.map((c) => ruled.get(c.id)?.label);
|
|
363
|
+
if (labels.some((l) => l !== void 0 && l !== label))
|
|
364
|
+
return "against";
|
|
365
|
+
return labels.every((l) => l === label) ? "agrees" : void 0;
|
|
366
|
+
}
|
|
367
|
+
async function decideAnswers(root, o) {
|
|
368
|
+
if (inAcpSession(o.env))
|
|
369
|
+
return void 0;
|
|
370
|
+
const state = await decideState(root);
|
|
371
|
+
if (state.configured.questions === "propose")
|
|
372
|
+
return void 0;
|
|
373
|
+
const runtime = o.runtime ?? AI_RUNTIME;
|
|
374
|
+
const proposals = (await checkedProposals(root)).filter((p) => p.ok && p.proposal && (!o.ids || o.ids.has(p.id)));
|
|
375
|
+
const kept = [];
|
|
376
|
+
const off = notHere(runtime, state);
|
|
377
|
+
if (off) {
|
|
378
|
+
for (const p of proposals)
|
|
379
|
+
kept.push({ id: p.id, why: off });
|
|
380
|
+
return { mode: "propose", applied: [], kept };
|
|
381
|
+
}
|
|
382
|
+
if (state.now.questions === "propose") {
|
|
383
|
+
for (const p of proposals)
|
|
384
|
+
kept.push({ id: p.id, why: "deciding is paused: a person confirms it" });
|
|
385
|
+
return {
|
|
386
|
+
mode: "propose",
|
|
387
|
+
applied: [],
|
|
388
|
+
kept,
|
|
389
|
+
...state.now.paused ? { paused: state.now.paused.why } : {}
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
const at = (o.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
393
|
+
const batch = newBatchId(o.now ?? /* @__PURE__ */ new Date());
|
|
394
|
+
const everyTarget = (await listQuestions(root, { step: TARGETS_STEP_ID })).questions;
|
|
395
|
+
const applied = [];
|
|
396
|
+
const items = [];
|
|
397
|
+
for (const p of proposals) {
|
|
398
|
+
const q = await readQuestion(root, p.id).catch(() => void 0);
|
|
399
|
+
const option = p.proposal.answer.option;
|
|
400
|
+
if (!q)
|
|
401
|
+
continue;
|
|
402
|
+
const problem = questionAiProblem(q, option);
|
|
403
|
+
if (problem) {
|
|
404
|
+
kept.push({ id: p.id, why: problem });
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const label = q.options[option - 1].label;
|
|
408
|
+
const { TARGETS_NO_EVIDENCE } = await import("./chunk-BXZ4PKIT.js");
|
|
409
|
+
const shapesFirst = option === 1 && !q.options[0].means.includes(TARGETS_NO_EVIDENCE);
|
|
410
|
+
const rule = await targetRuleVerdict(root, q, label, everyTarget);
|
|
411
|
+
if (rule === "against") {
|
|
412
|
+
kept.push({ id: p.id, why: "a person's rule covers its calls and says otherwise" });
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
const bar = questionBar(q, option, { shapesFirst, ruleAgrees: rule === "agrees" });
|
|
416
|
+
if (!bar) {
|
|
417
|
+
kept.push({
|
|
418
|
+
id: p.id,
|
|
419
|
+
why: "the shapes don't list that option first and no person's rule says so: a person confirms it"
|
|
420
|
+
});
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
const by = { kind: "ai", runtime, run: o.run, bar };
|
|
424
|
+
try {
|
|
425
|
+
await answerQuestion(root, q.id, {
|
|
426
|
+
option,
|
|
427
|
+
by,
|
|
428
|
+
at,
|
|
429
|
+
batch,
|
|
430
|
+
proposal: { by: p.proposal.by, at: p.proposal.at, cites: p.proposal.cites }
|
|
431
|
+
});
|
|
432
|
+
} catch (e) {
|
|
433
|
+
if (e instanceof QuestionError) {
|
|
434
|
+
kept.push({ id: p.id, why: e.message });
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
throw e;
|
|
438
|
+
}
|
|
439
|
+
await rm(join(root, ...proposalRelOf(root, q.id).split("/")), { force: true });
|
|
440
|
+
items.push({ kind: "question", id: q.id });
|
|
441
|
+
applied.push({ id: q.id, option, bar });
|
|
442
|
+
}
|
|
443
|
+
if (items.length)
|
|
444
|
+
await writeBatch(root, {
|
|
445
|
+
schema: 1,
|
|
446
|
+
id: batch,
|
|
447
|
+
kind: "ai",
|
|
448
|
+
at,
|
|
449
|
+
by: { kind: "ai", runtime, run: o.run },
|
|
450
|
+
applied: items,
|
|
451
|
+
reverted: []
|
|
452
|
+
});
|
|
453
|
+
return { mode: "auto", ...items.length ? { batch } : {}, applied, kept };
|
|
454
|
+
}
|
|
455
|
+
async function decisionsView(root) {
|
|
456
|
+
const state = await decideState(root);
|
|
457
|
+
const linkItems = [];
|
|
458
|
+
const lines = { byAi: 0, byPerson: 0 };
|
|
459
|
+
for (const repo of await correctedRepos(root)) {
|
|
460
|
+
const all = await correctionsOf(root, repo);
|
|
461
|
+
for (const c of all)
|
|
462
|
+
if (c.action === "link" && c.link && linksFor(callLikeOf(c), all)[0] === c) {
|
|
463
|
+
if (c.ai)
|
|
464
|
+
lines.byAi++;
|
|
465
|
+
else
|
|
466
|
+
lines.byPerson++;
|
|
467
|
+
}
|
|
468
|
+
for (const c of standingAi(all)) {
|
|
469
|
+
linkItems.push({
|
|
470
|
+
kind: "link",
|
|
471
|
+
item: `link:${repo}:${c.fact}`,
|
|
472
|
+
repo,
|
|
473
|
+
what: `${repo}'s call ${c.target.value} (${c.target.file}:${c.target.line}) \u2192 ${[c.link.method, c.link.path].filter(Boolean).join(" ")} on ${c.link.repo}`,
|
|
474
|
+
decided: c.link.entry ?? `${c.link.method ?? ""} ${c.link.path}`.trim(),
|
|
475
|
+
call: { fact: c.fact, file: c.target.file, line: c.target.line, value: c.target.value },
|
|
476
|
+
to: {
|
|
477
|
+
repo: c.link.repo,
|
|
478
|
+
...c.link.entry ? { entry: c.link.entry } : {},
|
|
479
|
+
...c.link.method ? { method: c.link.method } : {},
|
|
480
|
+
path: c.link.path
|
|
481
|
+
},
|
|
482
|
+
runtime: c.ai.runtime,
|
|
483
|
+
run: c.ai.run,
|
|
484
|
+
...c.ai.bar ? { bar: c.ai.bar } : {},
|
|
485
|
+
...c.batch ? { batch: c.batch } : {},
|
|
486
|
+
at: c.at,
|
|
487
|
+
cites: [`${repo} ${c.target.file}:${c.target.line}`]
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
const qs = (await listQuestions(root)).questions;
|
|
492
|
+
const questionItems = [];
|
|
493
|
+
const qCounts = { fromCode: 0, byPerson: 0, byAi: 0, open: 0, retired: 0 };
|
|
494
|
+
for (const q of qs) {
|
|
495
|
+
if (q.state === "open")
|
|
496
|
+
qCounts.open++;
|
|
497
|
+
else if (q.state === "retired")
|
|
498
|
+
qCounts.retired++;
|
|
499
|
+
else if (q.answer && isAiBy(q.answer.by)) {
|
|
500
|
+
qCounts.byAi++;
|
|
501
|
+
const option = q.answer.option ?? 0;
|
|
502
|
+
questionItems.push({
|
|
503
|
+
kind: "question",
|
|
504
|
+
item: q.id,
|
|
505
|
+
repo: q.repo,
|
|
506
|
+
what: `${q.question.trim()} AI: ${q.options[option - 1]?.label ?? ""}`,
|
|
507
|
+
decided: q.options[option - 1]?.label ?? "",
|
|
508
|
+
question: { text: q.question, option, options: q.options.map((x) => x.label) },
|
|
509
|
+
runtime: q.answer.by.runtime,
|
|
510
|
+
run: q.answer.by.run,
|
|
511
|
+
...q.answer.by.bar ? { bar: q.answer.by.bar } : {},
|
|
512
|
+
...q.answer.batch ? { batch: q.answer.batch } : {},
|
|
513
|
+
at: q.answer.at,
|
|
514
|
+
cites: (q.answer.proposal?.cites ?? []).map((c) => `${c.repo ?? q.repo} ${c.file}:${c.line}`)
|
|
515
|
+
});
|
|
516
|
+
} else
|
|
517
|
+
qCounts.byPerson++;
|
|
518
|
+
}
|
|
519
|
+
const linkCounts = { fromCode: 0, byPerson: 0, byAi: 0 };
|
|
520
|
+
const { readCatalog } = await import("./chunk-BXZ4PKIT.js");
|
|
521
|
+
const cat = await readCatalog(join(catalogDirOf(root), "generated")).catch(() => void 0);
|
|
522
|
+
for (const r of cat?.records ?? []) {
|
|
523
|
+
if (r.kind !== "Interface")
|
|
524
|
+
continue;
|
|
525
|
+
const flags = r.flags ?? [];
|
|
526
|
+
if (flags.includes(JOINED_BY_AI))
|
|
527
|
+
linkCounts.byAi++;
|
|
528
|
+
else if (flags.includes(JOINED_BY_PERSON) || flags.includes(JOINED_BY_ANSWER))
|
|
529
|
+
linkCounts.byPerson++;
|
|
530
|
+
else
|
|
531
|
+
linkCounts.fromCode++;
|
|
532
|
+
}
|
|
533
|
+
const { batches, problems } = await listBatches(root);
|
|
534
|
+
return {
|
|
535
|
+
decide: {
|
|
536
|
+
configured: state.configured,
|
|
537
|
+
now: { links: state.now.links, questions: state.now.questions },
|
|
538
|
+
...state.now.paused ? { paused: state.now.paused } : {},
|
|
539
|
+
precision: state.precision,
|
|
540
|
+
decideIn: state.decideIn
|
|
541
|
+
},
|
|
542
|
+
links: { counts: linkCounts, items: linkItems, lines },
|
|
543
|
+
questions: { counts: qCounts, items: questionItems },
|
|
544
|
+
batches: batches.map((b) => batchView(root, b)),
|
|
545
|
+
problems
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function batchView(root, b) {
|
|
549
|
+
return {
|
|
550
|
+
id: b.id,
|
|
551
|
+
kind: b.kind,
|
|
552
|
+
at: b.at,
|
|
553
|
+
by: answerByWords(b.by),
|
|
554
|
+
...isAiBy(b.by) ? { run: b.by.run } : b.correction?.run ? { run: b.correction.run } : {},
|
|
555
|
+
applied: b.applied.length,
|
|
556
|
+
reverted: b.reverted.length,
|
|
557
|
+
...b.correction ? {
|
|
558
|
+
correction: {
|
|
559
|
+
item: b.correction.item,
|
|
560
|
+
answer: b.correction.answer,
|
|
561
|
+
...b.correction.rule ? {
|
|
562
|
+
rule: {
|
|
563
|
+
kind: b.correction.rule.kind,
|
|
564
|
+
match: b.correction.rule.match,
|
|
565
|
+
from: b.correction.rule.from
|
|
566
|
+
}
|
|
567
|
+
} : {}
|
|
568
|
+
}
|
|
569
|
+
} : {},
|
|
570
|
+
...b.undone ? { undone: b.undone } : {},
|
|
571
|
+
file: batchRelOf(root, b.id)
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
var counted = (spread) => ({
|
|
575
|
+
applied: spread.filter((s) => s.change === "applied").length,
|
|
576
|
+
confirmed: spread.filter((s) => s.change === "confirmed").length,
|
|
577
|
+
replaced: spread.filter((s) => s.change === "replaced").length,
|
|
578
|
+
reverted: spread.filter((s) => s.change === "reverted").length
|
|
579
|
+
});
|
|
580
|
+
async function shapeCount(root, shape) {
|
|
581
|
+
return (await listBatches(root)).batches.filter((b) => b.kind === "rule" && !b.undone && b.correction?.shape === shape).length + 1;
|
|
582
|
+
}
|
|
583
|
+
async function correctDecision(root, req, o) {
|
|
584
|
+
if (inAcpSession(o.env))
|
|
585
|
+
throw new DecideError("refused", "A correction is a person's: a Copilot session acts as no one (ADR 0171, round 32).");
|
|
586
|
+
return req.item.startsWith("link:") ? correctLink(root, req, o) : correctQuestion(root, req, o);
|
|
587
|
+
}
|
|
588
|
+
async function correctQuestion(root, req, o) {
|
|
589
|
+
const q = await readQuestion(root, req.item).catch(() => void 0);
|
|
590
|
+
if (!q)
|
|
591
|
+
throw new DecideError("not-found", `There is no question ${req.item}.`);
|
|
592
|
+
if (!q.answer || !isAiBy(q.answer.by))
|
|
593
|
+
throw new DecideError("conflict", `${q.id} isn't an AI decision: answer it as usual (atticus questions answer ${q.id}).`);
|
|
594
|
+
const aiBy = q.answer.by;
|
|
595
|
+
const option = req.none ? q.options.findIndex((x) => x.label === HOSTS_NONE) + 1 : req.option ?? 0;
|
|
596
|
+
if (!option || option > q.options.length)
|
|
597
|
+
throw new DecideError("bad", req.none ? `${q.id} has no "None of these" option: pick one of its ${q.options.length} options.` : `Pick one of ${q.id}'s ${q.options.length} options (from 1), or none.`);
|
|
598
|
+
const label = q.options[option - 1].label;
|
|
599
|
+
const aiLabel = q.options[(q.answer.option ?? 0) - 1]?.label ?? "";
|
|
600
|
+
const facts = await factsOf(root, q.repo);
|
|
601
|
+
const byRepo = /* @__PURE__ */ new Map([[q.repo, facts]]);
|
|
602
|
+
const calls = callsOfQuestion(q, byRepo);
|
|
603
|
+
const everyTarget = (await listQuestions(root, { step: TARGETS_STEP_ID })).questions;
|
|
604
|
+
const similar = req.similar !== false;
|
|
605
|
+
let rule;
|
|
606
|
+
let noRule;
|
|
607
|
+
if (similar) {
|
|
608
|
+
if (req.match) {
|
|
609
|
+
if (!AnswerMatch.safeParse(req.match).success || ruleKindOf(req.match) === "topic")
|
|
610
|
+
throw new DecideError("bad", `${req.match} isn't a rule a targets answer takes: a path prefix (/forge/limits/**) or a host pattern (*.payments.forge.internal).`);
|
|
611
|
+
rule = { kind: ruleKindOf(req.match), match: req.match, derived: false };
|
|
612
|
+
} else {
|
|
613
|
+
const subject = calls.map((c) => subjectOfCall(c)).find((s) => s?.path);
|
|
614
|
+
const siblings = everyTarget.filter((x) => x.id !== q.id && x.repo === q.repo && x.state === "answered" && x.answer && isAiBy(x.answer.by) && x.options[(x.answer.option ?? 0) - 1]?.label === aiLabel).flatMap((x) => callsOfQuestion(x, byRepo)).map((c) => subjectOfCall(c)).filter((s) => !!s?.path);
|
|
615
|
+
if (!subject)
|
|
616
|
+
noRule = "its calls have no path a rule can name: give one with match";
|
|
617
|
+
else {
|
|
618
|
+
const d = deriveMatch(subject, siblings);
|
|
619
|
+
if (d.match === "/**")
|
|
620
|
+
noRule = "the calls share no path narrower than every path: give a rule with match, or correct it alone";
|
|
621
|
+
else if (!AnswerMatch.safeParse(d.match).success)
|
|
622
|
+
noRule = `${d.match} isn't a rule a targets answer takes: give one with match`;
|
|
623
|
+
else
|
|
624
|
+
rule = { ...d, derived: true };
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
const at = (o.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
629
|
+
const batch = newBatchId(o.now ?? /* @__PURE__ */ new Date());
|
|
630
|
+
const plans = [];
|
|
631
|
+
if (rule) {
|
|
632
|
+
for (const x of everyTarget) {
|
|
633
|
+
if (x.id === q.id || x.repo !== q.repo || x.scope.level !== "repo")
|
|
634
|
+
continue;
|
|
635
|
+
const ai = x.state === "answered" && x.answer && isAiBy(x.answer.by);
|
|
636
|
+
if (x.state !== "open" && !ai)
|
|
637
|
+
continue;
|
|
638
|
+
const xc = callsOfQuestion(x, byRepo);
|
|
639
|
+
if (!xc.length)
|
|
640
|
+
continue;
|
|
641
|
+
let all = true;
|
|
642
|
+
for (const c of xc)
|
|
643
|
+
if (!await coversCall(rule.match, c, facts))
|
|
644
|
+
all = false;
|
|
645
|
+
if (!all)
|
|
646
|
+
continue;
|
|
647
|
+
const idx = x.options.findIndex((y) => y.label === label);
|
|
648
|
+
const was = ai ? x.options[(x.answer.option ?? 0) - 1]?.label : void 0;
|
|
649
|
+
const what = x.question.trim();
|
|
650
|
+
if (idx >= 0) {
|
|
651
|
+
const change = !ai ? "applied" : was === label ? "confirmed" : "replaced";
|
|
652
|
+
plans.push({
|
|
653
|
+
view: {
|
|
654
|
+
item: x.id,
|
|
655
|
+
repo: x.repo,
|
|
656
|
+
what,
|
|
657
|
+
...was ? { was } : {},
|
|
658
|
+
now: label,
|
|
659
|
+
change
|
|
660
|
+
},
|
|
661
|
+
applied: { kind: "question", id: x.id, ...x.answer ? { before: x.answer } : {} },
|
|
662
|
+
write: async () => {
|
|
663
|
+
await answerQuestion(root, x.id, {
|
|
664
|
+
option: idx + 1,
|
|
665
|
+
by: o.by,
|
|
666
|
+
at,
|
|
667
|
+
batch,
|
|
668
|
+
rule: { from: q.id, match: rule.match }
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
} else if (ai)
|
|
673
|
+
plans.push({
|
|
674
|
+
view: { item: x.id, repo: x.repo, what, ...was ? { was } : {}, change: "reverted" },
|
|
675
|
+
reverted: { kind: "question", id: x.id, before: x.answer },
|
|
676
|
+
write: async () => {
|
|
677
|
+
const { answer: _a, ...rest } = x;
|
|
678
|
+
await writeQuestion(root, Question.parse({ ...rest, state: "open" }));
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
const spread = plans.map((p) => p.view);
|
|
684
|
+
const op = await loadOrgPack(orgPackDirOf(root));
|
|
685
|
+
const { estateRepos } = await import("./chunk-BXZ4PKIT.js");
|
|
686
|
+
const repoType = estateRepos(op).find((r) => r.id === q.repo)?.type;
|
|
687
|
+
const shape = `question:${repoType ?? q.repo}:${rule?.match ?? calls[0]?.path ?? q.id}`;
|
|
688
|
+
const n = await shapeCount(root, shape);
|
|
689
|
+
const files = [`${questionsRelOf(root)}/`, ...req.preview ? [] : [batchRelOf(root, batch)]];
|
|
690
|
+
const result = {
|
|
691
|
+
written: !req.preview,
|
|
692
|
+
...req.preview ? {} : { batch },
|
|
693
|
+
item: q.id,
|
|
694
|
+
answer: label,
|
|
695
|
+
...rule ? { rule } : {},
|
|
696
|
+
...similar && !rule && noRule ? { noRule } : {},
|
|
697
|
+
spread,
|
|
698
|
+
counts: counted(spread),
|
|
699
|
+
...n >= RULE_AFTER_CORRECTIONS && repoType ? {
|
|
700
|
+
makeRule: {
|
|
701
|
+
corrections: n,
|
|
702
|
+
shape,
|
|
703
|
+
repo: q.repo,
|
|
704
|
+
command: `atticus rules draft --repo ${q.repo} --about "calls under ${rule?.match ?? calls[0]?.path ?? "these paths"} go to ${label}"`
|
|
705
|
+
}
|
|
706
|
+
} : {},
|
|
707
|
+
files: [...new Set(files)],
|
|
708
|
+
verify: [`atticus verify ${q.repo} --write`],
|
|
709
|
+
commit: `git add ${questionsRelOf(root)}/ ${batchRelOf(root, batch).replace(/\/[^/]+$/, "/")}
|
|
710
|
+
git commit -m "${safeMessage(`${q.repo}: an AI answer corrected${rule ? `, and spread by ${rule.match}` : ""}`)}"`
|
|
711
|
+
};
|
|
712
|
+
if (req.preview)
|
|
713
|
+
return result;
|
|
714
|
+
const before = q.answer;
|
|
715
|
+
try {
|
|
716
|
+
await answerQuestion(root, q.id, {
|
|
717
|
+
option,
|
|
718
|
+
by: o.by,
|
|
719
|
+
at,
|
|
720
|
+
batch,
|
|
721
|
+
...rule ? { match: rule.match } : {}
|
|
722
|
+
});
|
|
723
|
+
} catch (e) {
|
|
724
|
+
if (e instanceof QuestionError)
|
|
725
|
+
throw new DecideError("bad", e.message);
|
|
726
|
+
throw e;
|
|
727
|
+
}
|
|
728
|
+
for (const p of plans)
|
|
729
|
+
await p.write();
|
|
730
|
+
await writeBatch(root, {
|
|
731
|
+
schema: 1,
|
|
732
|
+
id: batch,
|
|
733
|
+
kind: "rule",
|
|
734
|
+
at,
|
|
735
|
+
by: o.by,
|
|
736
|
+
correction: {
|
|
737
|
+
item: q.id,
|
|
738
|
+
answer: label,
|
|
739
|
+
...rule ? { rule: { from: q.id, match: rule.match, kind: rule.kind } } : {},
|
|
740
|
+
run: aiBy.run,
|
|
741
|
+
shape
|
|
742
|
+
},
|
|
743
|
+
applied: [
|
|
744
|
+
{ kind: "question", id: q.id, before },
|
|
745
|
+
...plans.flatMap((p) => p.applied ? [p.applied] : [])
|
|
746
|
+
],
|
|
747
|
+
reverted: plans.flatMap((p) => p.reverted ? [p.reverted] : [])
|
|
748
|
+
});
|
|
749
|
+
return result;
|
|
750
|
+
}
|
|
751
|
+
function parseLinkItem(item) {
|
|
752
|
+
const m = /^link:([A-Za-z0-9._-]{1,200}):(.+)$/.exec(item);
|
|
753
|
+
if (!m)
|
|
754
|
+
throw new DecideError("bad", `${item} isn't link:<repo>:<call fact id> or a question id.`);
|
|
755
|
+
return { repo: m[1], fact: m[2] };
|
|
756
|
+
}
|
|
757
|
+
async function correctLink(root, req, o) {
|
|
758
|
+
const { repo, fact } = parseLinkItem(req.item);
|
|
759
|
+
const facts = await factsOf(root, repo);
|
|
760
|
+
const f = facts.find((x) => x.id === fact && x.kind === "call");
|
|
761
|
+
if (!f)
|
|
762
|
+
throw new DecideError("not-found", `${repo} has no call ${fact} on record.`);
|
|
763
|
+
const corrections = await correctionsOf(root, repo);
|
|
764
|
+
const standing = linksFor(f, corrections)[0];
|
|
765
|
+
if (!standing?.ai || !standing.link?.repo)
|
|
766
|
+
throw new DecideError("conflict", `${repo}'s call ${fact} isn't paired by AI: correct it on the canvas (Connect to\u2026) as usual.`);
|
|
767
|
+
const aiRepo = standing.link.repo;
|
|
768
|
+
const none = req.none === true;
|
|
769
|
+
if (!none && (!req.api || !req.repo))
|
|
770
|
+
throw new DecideError("bad", "Say the right API and its repo ({ api, repo }), or none.");
|
|
771
|
+
const target = none ? void 0 : req.repo;
|
|
772
|
+
let entry;
|
|
773
|
+
if (target) {
|
|
774
|
+
entry = (await factsOf(root, target)).find((x) => x.id === req.api && x.kind === "entry");
|
|
775
|
+
if (!entry)
|
|
776
|
+
throw new DecideError("not-found", `${target} has no API ${req.api} on record.`);
|
|
777
|
+
}
|
|
778
|
+
const answer = target ?? "none";
|
|
779
|
+
const similar = req.similar !== false;
|
|
780
|
+
let rule;
|
|
781
|
+
let noRule;
|
|
782
|
+
const standingAll = standingAi(corrections);
|
|
783
|
+
if (similar) {
|
|
784
|
+
if (req.match)
|
|
785
|
+
rule = { kind: ruleKindOf(req.match, subjectOfCall(f)), match: req.match, derived: false };
|
|
786
|
+
else {
|
|
787
|
+
const subject = subjectOfCall(f);
|
|
788
|
+
const siblings = standingAll.filter((c) => c !== standing && c.link?.repo === aiRepo).map((c) => facts.find((x) => x.id === c.fact && x.kind === "call")).flatMap((x) => x ? [subjectOfCall(x)] : []).filter((s) => !!s);
|
|
789
|
+
if (!subject)
|
|
790
|
+
noRule = "the call's path is a name, not a path a rule can name: give one with match";
|
|
791
|
+
else {
|
|
792
|
+
const d = deriveMatch(subject, siblings);
|
|
793
|
+
if (d.match === "/**")
|
|
794
|
+
noRule = "the calls share no path narrower than every path: give a rule with match, or correct it alone";
|
|
795
|
+
else
|
|
796
|
+
rule = { ...d, derived: true };
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const at = (o.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
801
|
+
const batch = newBatchId(o.now ?? /* @__PURE__ */ new Date());
|
|
802
|
+
const lineFor = (c, api, own) => correctionLine(FactCorrection.parse({
|
|
803
|
+
schemaVersion: 2,
|
|
804
|
+
fact: c.id,
|
|
805
|
+
target: { kind: "call", file: c.cite.file, line: c.cite.line, value: c.cite.value },
|
|
806
|
+
action: api ? "link" : "unlink",
|
|
807
|
+
...api ? {
|
|
808
|
+
link: {
|
|
809
|
+
repo: target,
|
|
810
|
+
entry: api.id,
|
|
811
|
+
...api.method ? { method: api.method.toUpperCase() } : {},
|
|
812
|
+
path: api.path
|
|
813
|
+
}
|
|
814
|
+
} : {},
|
|
815
|
+
by: o.by,
|
|
816
|
+
at,
|
|
817
|
+
batch,
|
|
818
|
+
...own && rule ? { match: rule.match } : {},
|
|
819
|
+
...!own && rule ? { rule: { from: req.item, match: rule.match } } : {}
|
|
820
|
+
}));
|
|
821
|
+
const ownLine = lineFor(f, entry ? { id: entry.id, ...entry.method ? { method: entry.method } : {}, path: entry.path } : void 0, true);
|
|
822
|
+
const spread = [];
|
|
823
|
+
const add = [ownLine];
|
|
824
|
+
const remove = [];
|
|
825
|
+
const appliedItems = [{ kind: "link", repo, fact: f.id, line: ownLine }];
|
|
826
|
+
const revertedItems = [];
|
|
827
|
+
if (rule) {
|
|
828
|
+
const { candidatesOf, settingsOf } = await import("./chunk-PRZ7KMWL.js");
|
|
829
|
+
const box = { repo: target ?? aiRepo };
|
|
830
|
+
const cands = box.repo !== repo ? await candidatesOf(root, repo, box).catch(() => void 0) : void 0;
|
|
831
|
+
const loose = new Set((cands?.view.calls ?? []).filter((c) => c.status !== "paired").map((c) => c.fact));
|
|
832
|
+
const apis = (cands?.view.apis ?? []).filter((a) => a.kind !== "operation");
|
|
833
|
+
const settings = settingsOf(facts);
|
|
834
|
+
const bestOf = (c) => {
|
|
835
|
+
if (!target || !apis.length)
|
|
836
|
+
return void 0;
|
|
837
|
+
const s = linkCandidates({
|
|
838
|
+
from: repo,
|
|
839
|
+
facts: [c],
|
|
840
|
+
join: { joins: [], asks: [], misses: [], outside: [], left: [] },
|
|
841
|
+
apis,
|
|
842
|
+
pairs: {},
|
|
843
|
+
to: { repo: target },
|
|
844
|
+
settings
|
|
845
|
+
}).calls[0]?.suggestion?.api;
|
|
846
|
+
return s ? apis.find((a) => a.id === s) : void 0;
|
|
847
|
+
};
|
|
848
|
+
const words = (c) => `${repo}'s call ${[c.method?.toUpperCase(), c.path].filter(Boolean).join(" ")} (${c.cite.file}:${c.cite.line})`;
|
|
849
|
+
for (const c of facts) {
|
|
850
|
+
if (c.kind !== "call" || c.id === f.id)
|
|
851
|
+
continue;
|
|
852
|
+
if (!await coversCall(rule.match, c, facts))
|
|
853
|
+
continue;
|
|
854
|
+
const st = linksFor(c, corrections)[0];
|
|
855
|
+
if (st && !st.ai)
|
|
856
|
+
continue;
|
|
857
|
+
const item = `link:${repo}:${c.id}`;
|
|
858
|
+
const was = st?.ai ? `${st.link.repo} ${[st.link.method, st.link.path].filter(Boolean).join(" ")}` : void 0;
|
|
859
|
+
if (!st && !loose.has(c.id))
|
|
860
|
+
continue;
|
|
861
|
+
if (!target) {
|
|
862
|
+
const l = lineFor(c);
|
|
863
|
+
add.push(l);
|
|
864
|
+
appliedItems.push({ kind: "link", repo, fact: c.id, line: l });
|
|
865
|
+
spread.push({
|
|
866
|
+
item,
|
|
867
|
+
repo,
|
|
868
|
+
what: words(c),
|
|
869
|
+
...was ? { was } : {},
|
|
870
|
+
now: "none",
|
|
871
|
+
change: st ? "replaced" : "applied"
|
|
872
|
+
});
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
const same = st?.ai && st.link?.repo === target;
|
|
876
|
+
const api = same ? {
|
|
877
|
+
id: st.link.entry ?? "",
|
|
878
|
+
...st.link.method ? { method: st.link.method } : {},
|
|
879
|
+
path: st.link.path
|
|
880
|
+
} : bestOf(c);
|
|
881
|
+
if (api && api.id) {
|
|
882
|
+
const l = lineFor(c, api);
|
|
883
|
+
add.push(l);
|
|
884
|
+
appliedItems.push({ kind: "link", repo, fact: c.id, line: l });
|
|
885
|
+
spread.push({
|
|
886
|
+
item,
|
|
887
|
+
repo,
|
|
888
|
+
what: words(c),
|
|
889
|
+
...was ? { was } : {},
|
|
890
|
+
now: `${target} ${[api.method, api.path].filter(Boolean).join(" ")}`,
|
|
891
|
+
change: !st ? "applied" : same ? "confirmed" : "replaced"
|
|
892
|
+
});
|
|
893
|
+
} else if (st?.ai) {
|
|
894
|
+
const l = correctionLine(st);
|
|
895
|
+
remove.push(l);
|
|
896
|
+
revertedItems.push({ kind: "link", repo, fact: c.id, line: l });
|
|
897
|
+
spread.push({ item, repo, what: words(c), ...was ? { was } : {}, change: "reverted" });
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
const op = await loadOrgPack(orgPackDirOf(root));
|
|
902
|
+
const { estateRepos } = await import("./chunk-BXZ4PKIT.js");
|
|
903
|
+
const repoType = estateRepos(op).find((r) => r.id === repo)?.type;
|
|
904
|
+
const shape = `link:${repoType ?? repo}:${rule?.match ?? f.path}`;
|
|
905
|
+
const n = await shapeCount(root, shape);
|
|
906
|
+
const rel = correctionsRel(root, repo);
|
|
907
|
+
const result = {
|
|
908
|
+
written: !req.preview,
|
|
909
|
+
...req.preview ? {} : { batch },
|
|
910
|
+
item: req.item,
|
|
911
|
+
answer: entry ? `${target} ${[entry.method?.toUpperCase(), entry.path].filter(Boolean).join(" ")}` : "none",
|
|
912
|
+
...rule ? { rule } : {},
|
|
913
|
+
...similar && !rule && noRule ? { noRule } : {},
|
|
914
|
+
spread,
|
|
915
|
+
counts: counted(spread),
|
|
916
|
+
...n >= RULE_AFTER_CORRECTIONS && repoType && entry ? { makeRule: { corrections: n, shape, repo, line: ownLine } } : {},
|
|
917
|
+
files: [rel, ...req.preview ? [] : [batchRelOf(root, batch)]],
|
|
918
|
+
verify: [`atticus verify ${repo} --write`],
|
|
919
|
+
commit: `git add ${rel} ${batchRelOf(root, batch).replace(/\/[^/]+$/, "/")}
|
|
920
|
+
git commit -m "${safeMessage(`${repo}: an AI pairing corrected${rule ? `, and spread by ${rule.match}` : ""}`)}"`
|
|
921
|
+
};
|
|
922
|
+
if (req.preview)
|
|
923
|
+
return result;
|
|
924
|
+
await rewriteCorrections(root, repo, { remove, add });
|
|
925
|
+
await writeBatch(root, {
|
|
926
|
+
schema: 1,
|
|
927
|
+
id: batch,
|
|
928
|
+
kind: "rule",
|
|
929
|
+
at,
|
|
930
|
+
by: o.by,
|
|
931
|
+
correction: {
|
|
932
|
+
item: req.item,
|
|
933
|
+
answer,
|
|
934
|
+
...rule ? { rule: { from: req.item, match: rule.match, kind: rule.kind } } : {},
|
|
935
|
+
run: standing.ai.run,
|
|
936
|
+
shape
|
|
937
|
+
},
|
|
938
|
+
applied: appliedItems,
|
|
939
|
+
reverted: revertedItems
|
|
940
|
+
});
|
|
941
|
+
return result;
|
|
942
|
+
}
|
|
943
|
+
async function batchStands(root, b) {
|
|
944
|
+
for (const i of b.applied) {
|
|
945
|
+
if (i.kind === "question") {
|
|
946
|
+
const q = await readQuestion(root, i.id).catch(() => void 0);
|
|
947
|
+
if (q?.answer?.batch === b.id)
|
|
948
|
+
return true;
|
|
949
|
+
} else {
|
|
950
|
+
const text = await readFile(correctionsFile(root, i.repo), "utf8").catch(() => "");
|
|
951
|
+
if (text.split(/\r?\n/).some((l) => l.trim() === i.line.trim()))
|
|
952
|
+
return true;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
async function undoBatch(root, id, o) {
|
|
958
|
+
if (inAcpSession(o.env))
|
|
959
|
+
throw new DecideError("refused", "Undo is a person's: a Copilot session acts as no one.");
|
|
960
|
+
const b = await readBatch(root, id);
|
|
961
|
+
if (!b)
|
|
962
|
+
throw new DecideError("not-found", `There is no batch ${id}.`);
|
|
963
|
+
if (b.undone)
|
|
964
|
+
throw new DecideError("conflict", `${id} was undone by ${b.undone.by} on ${b.undone.at.slice(0, 10)}.`);
|
|
965
|
+
let removed = 0;
|
|
966
|
+
let restored = 0;
|
|
967
|
+
const skipped = [];
|
|
968
|
+
const files = /* @__PURE__ */ new Set();
|
|
969
|
+
const repos = /* @__PURE__ */ new Set();
|
|
970
|
+
const byRepo = /* @__PURE__ */ new Map();
|
|
971
|
+
const of = (r) => {
|
|
972
|
+
const x = byRepo.get(r) ?? { remove: [], add: [] };
|
|
973
|
+
byRepo.set(r, x);
|
|
974
|
+
return x;
|
|
975
|
+
};
|
|
976
|
+
const undoneBatches = /* @__PURE__ */ new Map();
|
|
977
|
+
const isUndone = async (batch) => {
|
|
978
|
+
if (!batch || batch === b.id)
|
|
979
|
+
return false;
|
|
980
|
+
if (!undoneBatches.has(batch))
|
|
981
|
+
undoneBatches.set(batch, !!(await readBatch(root, batch).catch(() => void 0))?.undone);
|
|
982
|
+
return undoneBatches.get(batch);
|
|
983
|
+
};
|
|
984
|
+
const batchOfLine = (line) => {
|
|
985
|
+
try {
|
|
986
|
+
return JSON.parse(line).batch;
|
|
987
|
+
} catch {
|
|
988
|
+
return void 0;
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
const stands = await batchStands(root, b);
|
|
992
|
+
if (!stands)
|
|
993
|
+
throw new DecideError("conflict", `Nothing of ${id} stands now: each of its lines and answers was changed since, so there is nothing to undo.`);
|
|
994
|
+
for (const i of b.applied)
|
|
995
|
+
if (i.kind === "link")
|
|
996
|
+
of(i.repo).remove.push(i.line);
|
|
997
|
+
for (const i of b.reverted)
|
|
998
|
+
if (i.kind === "link") {
|
|
999
|
+
if (await isUndone(batchOfLine(i.line))) {
|
|
1000
|
+
skipped.push(`a line of an undone batch in ${correctionsRel(root, i.repo)}: not put back`);
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
of(i.repo).add.push(i.line);
|
|
1004
|
+
}
|
|
1005
|
+
for (const [repo, x] of byRepo) {
|
|
1006
|
+
const r = await rewriteCorrections(root, repo, x);
|
|
1007
|
+
removed += r.removed;
|
|
1008
|
+
restored += r.added;
|
|
1009
|
+
if (r.removed < x.remove.length)
|
|
1010
|
+
skipped.push(`${x.remove.length - r.removed} of its lines in ${correctionsRel(root, repo)} were gone already`);
|
|
1011
|
+
files.add(correctionsRel(root, repo));
|
|
1012
|
+
repos.add(repo);
|
|
1013
|
+
}
|
|
1014
|
+
for (const i of [...b.applied].reverse()) {
|
|
1015
|
+
if (i.kind !== "question")
|
|
1016
|
+
continue;
|
|
1017
|
+
const q = await readQuestion(root, i.id).catch(() => void 0);
|
|
1018
|
+
if (!q || q.answer?.batch !== b.id) {
|
|
1019
|
+
skipped.push(`${i.id} was answered again since: left as it is`);
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
const { answer: _a, ...rest } = q;
|
|
1023
|
+
const before = i.before && !await isUndone(i.before.batch) ? i.before : void 0;
|
|
1024
|
+
if (i.before && !before)
|
|
1025
|
+
skipped.push(`${i.id}: its earlier answer was an undone batch's, so it is open`);
|
|
1026
|
+
await writeQuestion(root, Question.parse(before ? { ...rest, state: "answered", answer: before } : { ...rest, state: "open" }));
|
|
1027
|
+
removed++;
|
|
1028
|
+
repos.add(q.repo);
|
|
1029
|
+
}
|
|
1030
|
+
for (const i of b.reverted) {
|
|
1031
|
+
if (i.kind !== "question" || !i.before)
|
|
1032
|
+
continue;
|
|
1033
|
+
const q = await readQuestion(root, i.id).catch(() => void 0);
|
|
1034
|
+
if (!q || q.state !== "open") {
|
|
1035
|
+
skipped.push(`${i.id} was answered since: the AI's answer isn't put back`);
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
if (await isUndone(i.before.batch)) {
|
|
1039
|
+
skipped.push(`${i.id}: the AI's answer was an undone batch's, so it stays open`);
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
await writeQuestion(root, Question.parse({ ...q, state: "answered", answer: i.before }));
|
|
1043
|
+
restored++;
|
|
1044
|
+
repos.add(q.repo);
|
|
1045
|
+
}
|
|
1046
|
+
if (b.applied.some((i) => i.kind === "question") || b.reverted.some((i) => i.kind === "question"))
|
|
1047
|
+
files.add(`${questionsRelOf(root)}/`);
|
|
1048
|
+
const at = (o.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
1049
|
+
const file = await writeBatch(root, { ...b, undone: { by: o.by, at } });
|
|
1050
|
+
files.add(file);
|
|
1051
|
+
return {
|
|
1052
|
+
batch: b.id,
|
|
1053
|
+
removed,
|
|
1054
|
+
restored,
|
|
1055
|
+
skipped,
|
|
1056
|
+
files: [...files],
|
|
1057
|
+
verify: [...repos].sort().map((r) => `atticus verify ${r} --write`),
|
|
1058
|
+
commit: `git add ${[...files].join(" ")}
|
|
1059
|
+
git commit -m "${safeMessage(`Undo ${b.id}`)}"`
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
async function aiAuditCandidates(root, repo) {
|
|
1063
|
+
const out = [];
|
|
1064
|
+
for (const c of standingAi(await correctionsOf(root, repo)))
|
|
1065
|
+
out.push({
|
|
1066
|
+
id: `ai:link:${c.fact}`,
|
|
1067
|
+
kind: "ai",
|
|
1068
|
+
of: "link",
|
|
1069
|
+
claim: `AI paired ${c.target.value} with ${[c.link.method, c.link.path].filter(Boolean).join(" ")} on ${c.link.repo}`,
|
|
1070
|
+
cites: [`${repo} ${c.target.file}:${c.target.line}`],
|
|
1071
|
+
fact: c.fact,
|
|
1072
|
+
to: c.link.repo,
|
|
1073
|
+
decided: c.link.entry ?? c.link.path,
|
|
1074
|
+
run: c.ai.run
|
|
1075
|
+
});
|
|
1076
|
+
for (const q of (await listQuestions(root, { state: "answered" })).questions) {
|
|
1077
|
+
if (q.repo !== repo || !q.answer || !isAiBy(q.answer.by))
|
|
1078
|
+
continue;
|
|
1079
|
+
const label = q.options[(q.answer.option ?? 0) - 1]?.label ?? "";
|
|
1080
|
+
const cites = (q.answer.proposal?.cites ?? []).map((c) => `${c.repo ?? q.repo} ${c.file}:${c.line}`);
|
|
1081
|
+
out.push({
|
|
1082
|
+
id: `ai:question:${q.id}`,
|
|
1083
|
+
kind: "ai",
|
|
1084
|
+
of: "question",
|
|
1085
|
+
claim: `AI answered "${q.question.trim()}" with ${label}`,
|
|
1086
|
+
cites: cites.length ? cites : q.evidence.map((e) => `${q.repo} ${e}`),
|
|
1087
|
+
decided: label,
|
|
1088
|
+
run: q.answer.by.run
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
return out;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
export {
|
|
1095
|
+
DecideError,
|
|
1096
|
+
inAcpSession,
|
|
1097
|
+
decideState,
|
|
1098
|
+
decideIn,
|
|
1099
|
+
decideLinks,
|
|
1100
|
+
proposalsSnapshot,
|
|
1101
|
+
decideAnswers,
|
|
1102
|
+
decisionsView,
|
|
1103
|
+
correctDecision,
|
|
1104
|
+
undoBatch,
|
|
1105
|
+
aiAuditCandidates
|
|
1106
|
+
};
|