@namzu/cli 35.0.0 → 36.1.0
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/CHANGELOG.md +764 -0
- package/README.md +160 -394
- package/assets/pal-blender-mcp.py +123 -0
- package/assets/pal-godot-mcp-pin.json +135 -0
- package/assets/pal-godot-mcp.cjs +418 -0
- package/dist/bin-main.d.ts +3 -0
- package/dist/bin-main.d.ts.map +1 -0
- package/dist/bin.d.ts +2 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +85 -23
- package/dist/bin.js.map +6 -1
- package/dist/boot-narrative.d.ts +16 -0
- package/dist/boot-narrative.d.ts.map +1 -0
- package/dist/checkpoints/manifest.d.ts +107 -0
- package/dist/checkpoints/manifest.d.ts.map +1 -0
- package/dist/checkpoints/store.d.ts +280 -40
- package/dist/checkpoints/store.d.ts.map +1 -1
- package/dist/checkpoints/undo-plan.d.ts +83 -0
- package/dist/checkpoints/undo-plan.d.ts.map +1 -0
- package/dist/checkpoints/wrap.d.ts +4 -0
- package/dist/checkpoints/wrap.d.ts.map +1 -1
- package/dist/chunks/acp-5CWS2GLJ.js +13448 -0
- package/dist/chunks/acp-5CWS2GLJ.js.map +6 -0
- package/dist/chunks/activity-command-HK7YJTXV.js +262 -0
- package/dist/chunks/activity-command-HK7YJTXV.js.map +6 -0
- package/dist/chunks/add-ULH5DUNX.js +712 -0
- package/dist/chunks/add-ULH5DUNX.js.map +6 -0
- package/dist/chunks/agent-UEK2U3RE.js +87 -0
- package/dist/chunks/agent-UEK2U3RE.js.map +6 -0
- package/dist/chunks/agent-session-V7JRTODS.js +46 -0
- package/dist/chunks/agent-session-V7JRTODS.js.map +6 -0
- package/dist/chunks/archive-C4A3HPLF.js +84 -0
- package/dist/chunks/archive-C4A3HPLF.js.map +6 -0
- package/dist/chunks/bin-main-EEJHRDIC.js +126 -0
- package/dist/chunks/bin-main-EEJHRDIC.js.map +6 -0
- package/dist/chunks/browser-CRX4HM4C.js +31 -0
- package/dist/chunks/browser-CRX4HM4C.js.map +6 -0
- package/dist/chunks/chat-FVBATC4E.js +101 -0
- package/dist/chunks/chat-FVBATC4E.js.map +6 -0
- package/dist/chunks/chunk-2A37JQKY.js +1355 -0
- package/dist/chunks/chunk-2A37JQKY.js.map +6 -0
- package/dist/chunks/chunk-2NJ2XUXJ.js +283 -0
- package/dist/chunks/chunk-2NJ2XUXJ.js.map +6 -0
- package/dist/chunks/chunk-4HAE7PYX.js +74 -0
- package/dist/chunks/chunk-4HAE7PYX.js.map +6 -0
- package/dist/chunks/chunk-5GRV6P5C.js +69 -0
- package/dist/chunks/chunk-5GRV6P5C.js.map +6 -0
- package/dist/chunks/chunk-5PQVJBIO.js +168 -0
- package/dist/chunks/chunk-5PQVJBIO.js.map +6 -0
- package/dist/chunks/chunk-62I7HLAP.js +1142 -0
- package/dist/chunks/chunk-62I7HLAP.js.map +6 -0
- package/dist/chunks/chunk-63CGV3XY.js +161 -0
- package/dist/chunks/chunk-63CGV3XY.js.map +6 -0
- package/dist/chunks/chunk-6LLJQ3KY.js +225 -0
- package/dist/chunks/chunk-6LLJQ3KY.js.map +6 -0
- package/dist/chunks/chunk-6YG7D7U4.js +159 -0
- package/dist/chunks/chunk-6YG7D7U4.js.map +6 -0
- package/dist/chunks/chunk-76FLAFCG.js +51 -0
- package/dist/chunks/chunk-76FLAFCG.js.map +6 -0
- package/dist/chunks/chunk-7WJOQ6EA.js +106 -0
- package/dist/chunks/chunk-7WJOQ6EA.js.map +6 -0
- package/dist/chunks/chunk-A22MBIYU.js +42 -0
- package/dist/chunks/chunk-A22MBIYU.js.map +6 -0
- package/dist/chunks/chunk-A5DWGNGR.js +73 -0
- package/dist/chunks/chunk-A5DWGNGR.js.map +6 -0
- package/dist/chunks/chunk-A5UXODFL.js +76 -0
- package/dist/chunks/chunk-A5UXODFL.js.map +6 -0
- package/dist/chunks/chunk-AHIDYIRB.js +329 -0
- package/dist/chunks/chunk-AHIDYIRB.js.map +6 -0
- package/dist/chunks/chunk-AIY4PCDR.js +164 -0
- package/dist/chunks/chunk-AIY4PCDR.js.map +6 -0
- package/dist/chunks/chunk-AK2U3N2A.js +31 -0
- package/dist/chunks/chunk-AK2U3N2A.js.map +6 -0
- package/dist/chunks/chunk-AN7FO7NS.js +247 -0
- package/dist/chunks/chunk-AN7FO7NS.js.map +6 -0
- package/dist/chunks/chunk-AXIGFKJF.js +34 -0
- package/dist/chunks/chunk-AXIGFKJF.js.map +6 -0
- package/dist/chunks/chunk-C5WBEO3X.js +46 -0
- package/dist/chunks/chunk-C5WBEO3X.js.map +6 -0
- package/dist/chunks/chunk-C5XYHYMX.js +675 -0
- package/dist/chunks/chunk-C5XYHYMX.js.map +6 -0
- package/dist/chunks/chunk-CG65NUSJ.js +115 -0
- package/dist/chunks/chunk-CG65NUSJ.js.map +6 -0
- package/dist/chunks/chunk-CHIEVVQ2.js +32 -0
- package/dist/chunks/chunk-CHIEVVQ2.js.map +6 -0
- package/dist/chunks/chunk-E5VLANCO.js +101 -0
- package/dist/chunks/chunk-E5VLANCO.js.map +6 -0
- package/dist/chunks/chunk-EBOHVFOB.js +648 -0
- package/dist/chunks/chunk-EBOHVFOB.js.map +6 -0
- package/dist/chunks/chunk-EEDZOGV4.js +47 -0
- package/dist/chunks/chunk-EEDZOGV4.js.map +6 -0
- package/dist/chunks/chunk-EIYM6TY7.js +1479 -0
- package/dist/chunks/chunk-EIYM6TY7.js.map +6 -0
- package/dist/chunks/chunk-EWKJGHRO.js +82559 -0
- package/dist/chunks/chunk-EWKJGHRO.js.map +6 -0
- package/dist/chunks/chunk-EY7UNVER.js +311 -0
- package/dist/chunks/chunk-EY7UNVER.js.map +6 -0
- package/dist/chunks/chunk-FTMTO72X.js +95 -0
- package/dist/chunks/chunk-FTMTO72X.js.map +6 -0
- package/dist/chunks/chunk-FXS3AG6B.js +25326 -0
- package/dist/chunks/chunk-FXS3AG6B.js.map +6 -0
- package/dist/chunks/chunk-GIXSS6KW.js +2228 -0
- package/dist/chunks/chunk-GIXSS6KW.js.map +6 -0
- package/dist/chunks/chunk-GMCOZ3CU.js +245 -0
- package/dist/chunks/chunk-GMCOZ3CU.js.map +6 -0
- package/dist/chunks/chunk-GNBMKISG.js +19 -0
- package/dist/chunks/chunk-GNBMKISG.js.map +6 -0
- package/dist/chunks/chunk-GWQEMJFS.js +28 -0
- package/dist/chunks/chunk-GWQEMJFS.js.map +6 -0
- package/dist/chunks/chunk-H6B6AROX.js +151 -0
- package/dist/chunks/chunk-H6B6AROX.js.map +6 -0
- package/dist/chunks/chunk-HBBB2CAC.js +75 -0
- package/dist/chunks/chunk-HBBB2CAC.js.map +6 -0
- package/dist/chunks/chunk-HE7WUOX2.js +26 -0
- package/dist/chunks/chunk-HE7WUOX2.js.map +6 -0
- package/dist/chunks/chunk-HFJHHCTY.js +303 -0
- package/dist/chunks/chunk-HFJHHCTY.js.map +6 -0
- package/dist/chunks/chunk-HPMWYIBY.js +773 -0
- package/dist/chunks/chunk-HPMWYIBY.js.map +6 -0
- package/dist/chunks/chunk-HQM2WSQT.js +106 -0
- package/dist/chunks/chunk-HQM2WSQT.js.map +6 -0
- package/dist/chunks/chunk-ITZK4B66.js +787 -0
- package/dist/chunks/chunk-ITZK4B66.js.map +6 -0
- package/dist/chunks/chunk-J72CRUNX.js +154 -0
- package/dist/chunks/chunk-J72CRUNX.js.map +6 -0
- package/dist/chunks/chunk-JORONMD3.js +18 -0
- package/dist/chunks/chunk-JORONMD3.js.map +6 -0
- package/dist/chunks/chunk-JPPC4LJF.js +271 -0
- package/dist/chunks/chunk-JPPC4LJF.js.map +6 -0
- package/dist/chunks/chunk-JV3LONAU.js +76 -0
- package/dist/chunks/chunk-JV3LONAU.js.map +6 -0
- package/dist/chunks/chunk-K76WV7TN.js +71 -0
- package/dist/chunks/chunk-K76WV7TN.js.map +6 -0
- package/dist/chunks/chunk-KCWUQRZV.js +828 -0
- package/dist/chunks/chunk-KCWUQRZV.js.map +6 -0
- package/dist/chunks/chunk-KONYGNG6.js +7661 -0
- package/dist/chunks/chunk-KONYGNG6.js.map +6 -0
- package/dist/chunks/chunk-KU3W6IHV.js +109 -0
- package/dist/chunks/chunk-KU3W6IHV.js.map +6 -0
- package/dist/chunks/chunk-LQMHASKR.js +171 -0
- package/dist/chunks/chunk-LQMHASKR.js.map +6 -0
- package/dist/chunks/chunk-LXB3S4IC.js +19 -0
- package/dist/chunks/chunk-LXB3S4IC.js.map +6 -0
- package/dist/chunks/chunk-MZP6SH3M.js +3497 -0
- package/dist/chunks/chunk-MZP6SH3M.js.map +6 -0
- package/dist/chunks/chunk-N5IF64K3.js +1858 -0
- package/dist/chunks/chunk-N5IF64K3.js.map +6 -0
- package/dist/chunks/chunk-NADQQMMG.js +46 -0
- package/dist/chunks/chunk-NADQQMMG.js.map +6 -0
- package/dist/chunks/chunk-NQY5P2EB.js +20 -0
- package/dist/chunks/chunk-NQY5P2EB.js.map +6 -0
- package/dist/chunks/chunk-NUGPLXVK.js +228 -0
- package/dist/chunks/chunk-NUGPLXVK.js.map +6 -0
- package/dist/chunks/chunk-O7DBCVNY.js +110 -0
- package/dist/chunks/chunk-O7DBCVNY.js.map +6 -0
- package/dist/chunks/chunk-OEW7CJKK.js +140 -0
- package/dist/chunks/chunk-OEW7CJKK.js.map +6 -0
- package/dist/chunks/chunk-OH6TB27S.js +111 -0
- package/dist/chunks/chunk-OH6TB27S.js.map +6 -0
- package/dist/chunks/chunk-OLPOM3DR.js +127 -0
- package/dist/chunks/chunk-OLPOM3DR.js.map +6 -0
- package/dist/chunks/chunk-PYG7DZ2D.js +380 -0
- package/dist/chunks/chunk-PYG7DZ2D.js.map +6 -0
- package/dist/chunks/chunk-Q24K7536.js +661 -0
- package/dist/chunks/chunk-Q24K7536.js.map +6 -0
- package/dist/chunks/chunk-Q5YM2GCX.js +283 -0
- package/dist/chunks/chunk-Q5YM2GCX.js.map +6 -0
- package/dist/chunks/chunk-R7DCDBRN.js +106 -0
- package/dist/chunks/chunk-R7DCDBRN.js.map +6 -0
- package/dist/chunks/chunk-RC6X3ZY6.js +1439 -0
- package/dist/chunks/chunk-RC6X3ZY6.js.map +6 -0
- package/dist/chunks/chunk-SGS435PR.js +11111 -0
- package/dist/chunks/chunk-SGS435PR.js.map +6 -0
- package/dist/chunks/chunk-SKKWMMFP.js +474 -0
- package/dist/chunks/chunk-SKKWMMFP.js.map +6 -0
- package/dist/chunks/chunk-SRU3MDHZ.js +75 -0
- package/dist/chunks/chunk-SRU3MDHZ.js.map +6 -0
- package/dist/chunks/chunk-TBIX6JRE.js +24 -0
- package/dist/chunks/chunk-TBIX6JRE.js.map +6 -0
- package/dist/chunks/chunk-THNCQJ33.js +11265 -0
- package/dist/chunks/chunk-THNCQJ33.js.map +6 -0
- package/dist/chunks/chunk-THZXXVHM.js +2953 -0
- package/dist/chunks/chunk-THZXXVHM.js.map +6 -0
- package/dist/chunks/chunk-TIGWWAJI.js +765 -0
- package/dist/chunks/chunk-TIGWWAJI.js.map +6 -0
- package/dist/chunks/chunk-UDBEU2FR.js +97 -0
- package/dist/chunks/chunk-UDBEU2FR.js.map +6 -0
- package/dist/chunks/chunk-UOOQPODR.js +257 -0
- package/dist/chunks/chunk-UOOQPODR.js.map +6 -0
- package/dist/chunks/chunk-US5ZFPAU.js +400 -0
- package/dist/chunks/chunk-US5ZFPAU.js.map +6 -0
- package/dist/chunks/chunk-UWWGSDZ5.js +134 -0
- package/dist/chunks/chunk-UWWGSDZ5.js.map +6 -0
- package/dist/chunks/chunk-UXVYJWN5.js +1373 -0
- package/dist/chunks/chunk-UXVYJWN5.js.map +6 -0
- package/dist/chunks/chunk-V7TQR6AR.js +818 -0
- package/dist/chunks/chunk-V7TQR6AR.js.map +6 -0
- package/dist/chunks/chunk-VRL2WNQN.js +7534 -0
- package/dist/chunks/chunk-VRL2WNQN.js.map +6 -0
- package/dist/chunks/chunk-WATMFOHB.js +319 -0
- package/dist/chunks/chunk-WATMFOHB.js.map +6 -0
- package/dist/chunks/chunk-WHODOMXA.js +132 -0
- package/dist/chunks/chunk-WHODOMXA.js.map +6 -0
- package/dist/chunks/chunk-WM2JEUGV.js +207 -0
- package/dist/chunks/chunk-WM2JEUGV.js.map +6 -0
- package/dist/chunks/chunk-XEUQN4JC.js +54 -0
- package/dist/chunks/chunk-XEUQN4JC.js.map +6 -0
- package/dist/chunks/chunk-XHOD3RDN.js +517 -0
- package/dist/chunks/chunk-XHOD3RDN.js.map +6 -0
- package/dist/chunks/chunk-Y7B67G22.js +26 -0
- package/dist/chunks/chunk-Y7B67G22.js.map +6 -0
- package/dist/chunks/chunk-ZKEWE6K4.js +399 -0
- package/dist/chunks/chunk-ZKEWE6K4.js.map +6 -0
- package/dist/chunks/chunk-ZPR4IMRT.js +36 -0
- package/dist/chunks/chunk-ZPR4IMRT.js.map +6 -0
- package/dist/chunks/chunk-ZWA2JRJV.js +45 -0
- package/dist/chunks/chunk-ZWA2JRJV.js.map +6 -0
- package/dist/chunks/cli-runtime-7YAOEJGS.js +129 -0
- package/dist/chunks/cli-runtime-7YAOEJGS.js.map +6 -0
- package/dist/chunks/commands-QPVRB3RK.js +21 -0
- package/dist/chunks/commands-QPVRB3RK.js.map +6 -0
- package/dist/chunks/computer-GXDY4H35.js +493 -0
- package/dist/chunks/computer-GXDY4H35.js.map +6 -0
- package/dist/chunks/crash-report-S3BTEPYR.js +21 -0
- package/dist/chunks/crash-report-S3BTEPYR.js.map +6 -0
- package/dist/chunks/devtools-LBE4PAQN.js +3851 -0
- package/dist/chunks/devtools-LBE4PAQN.js.map +6 -0
- package/dist/chunks/dist-3GJFRS2X.js +520 -0
- package/dist/chunks/dist-3GJFRS2X.js.map +6 -0
- package/dist/chunks/dist-BFC7XUCT.js +555 -0
- package/dist/chunks/dist-BFC7XUCT.js.map +6 -0
- package/dist/chunks/dist-ERN5DRKK.js +1415 -0
- package/dist/chunks/dist-ERN5DRKK.js.map +6 -0
- package/dist/chunks/dist-EY3OJBQH.js +1339 -0
- package/dist/chunks/dist-EY3OJBQH.js.map +6 -0
- package/dist/chunks/dist-G42HPNIE.js +514 -0
- package/dist/chunks/dist-G42HPNIE.js.map +6 -0
- package/dist/chunks/dist-SKRHHPUR.js +1018 -0
- package/dist/chunks/dist-SKRHHPUR.js.map +6 -0
- package/dist/chunks/dist-Z47L7ZAJ.js +40654 -0
- package/dist/chunks/dist-Z47L7ZAJ.js.map +6 -0
- package/dist/chunks/doctor-IE3PLXZK.js +28 -0
- package/dist/chunks/doctor-IE3PLXZK.js.map +6 -0
- package/dist/chunks/drain-ESXUKOE6.js +471 -0
- package/dist/chunks/drain-ESXUKOE6.js.map +6 -0
- package/dist/chunks/endpoint-M2DDENPP.js +15 -0
- package/dist/chunks/endpoint-M2DDENPP.js.map +6 -0
- package/dist/chunks/eval-53UONRWR.js +187 -0
- package/dist/chunks/eval-53UONRWR.js.map +6 -0
- package/dist/chunks/exec-COV235L5.js +1284 -0
- package/dist/chunks/exec-COV235L5.js.map +6 -0
- package/dist/chunks/home-QS7RBP34.js +16 -0
- package/dist/chunks/home-QS7RBP34.js.map +6 -0
- package/dist/chunks/host-queries-LDEBHKBX.js +152 -0
- package/dist/chunks/host-queries-LDEBHKBX.js.map +6 -0
- package/dist/chunks/lib-KXZZJJG2.js +2657 -0
- package/dist/chunks/lib-KXZZJJG2.js.map +6 -0
- package/dist/chunks/lifecycle-6NF6NJNV.js +63 -0
- package/dist/chunks/lifecycle-6NF6NJNV.js.map +6 -0
- package/dist/chunks/list-DQZU7NEF.js +293 -0
- package/dist/chunks/list-DQZU7NEF.js.map +6 -0
- package/dist/chunks/login-IQGNP5XY.js +29 -0
- package/dist/chunks/login-IQGNP5XY.js.map +6 -0
- package/dist/chunks/logs-KSG7SFAB.js +116 -0
- package/dist/chunks/logs-KSG7SFAB.js.map +6 -0
- package/dist/chunks/mcp-WFXKXGNM.js +763 -0
- package/dist/chunks/mcp-WFXKXGNM.js.map +6 -0
- package/dist/chunks/message-command-O7ZH4I4C.js +170 -0
- package/dist/chunks/message-command-O7ZH4I4C.js.map +6 -0
- package/dist/chunks/node-QB2FA2ZB.js +916 -0
- package/dist/chunks/node-QB2FA2ZB.js.map +6 -0
- package/dist/chunks/operator-tools-LTPV3CH5.js +115 -0
- package/dist/chunks/operator-tools-LTPV3CH5.js.map +6 -0
- package/dist/chunks/output-schema-UM3P5AOR.js +11 -0
- package/dist/chunks/output-schema-UM3P5AOR.js.map +6 -0
- package/dist/chunks/pal-GBYJHBP3.js +200 -0
- package/dist/chunks/pal-GBYJHBP3.js.map +6 -0
- package/dist/chunks/paths-DXYFKIJU.js +10 -0
- package/dist/chunks/paths-DXYFKIJU.js.map +6 -0
- package/dist/chunks/pica-WNHDDIFX.js +1736 -0
- package/dist/chunks/pica-WNHDDIFX.js.map +6 -0
- package/dist/chunks/providers-J42J2PDA.js +160 -0
- package/dist/chunks/providers-J42J2PDA.js.map +6 -0
- package/dist/chunks/resident-7D6IM5LP.js +1314 -0
- package/dist/chunks/resident-7D6IM5LP.js.map +6 -0
- package/dist/chunks/resume-command-XZWGUXRK.js +22 -0
- package/dist/chunks/resume-command-XZWGUXRK.js.map +6 -0
- package/dist/chunks/schedule-JLIOF3MS.js +143 -0
- package/dist/chunks/schedule-JLIOF3MS.js.map +6 -0
- package/dist/chunks/serve-S7UWNIR3.js +21 -0
- package/dist/chunks/serve-S7UWNIR3.js.map +6 -0
- package/dist/chunks/service-YBU5VTKQ.js +1633 -0
- package/dist/chunks/service-YBU5VTKQ.js.map +6 -0
- package/dist/chunks/session-step-2HJSIJNN.js +26 -0
- package/dist/chunks/session-step-2HJSIJNN.js.map +6 -0
- package/dist/chunks/skills-HV2MOBEW.js +282 -0
- package/dist/chunks/skills-HV2MOBEW.js.map +6 -0
- package/dist/chunks/state-6HV3D3O4.js +717 -0
- package/dist/chunks/state-6HV3D3O4.js.map +6 -0
- package/dist/chunks/store-6J2MUGON.js +82 -0
- package/dist/chunks/store-6J2MUGON.js.map +6 -0
- package/dist/chunks/store-GDP2EW27.js +34 -0
- package/dist/chunks/store-GDP2EW27.js.map +6 -0
- package/dist/chunks/store-VXNKMMNW.js +42 -0
- package/dist/chunks/store-VXNKMMNW.js.map +6 -0
- package/dist/chunks/tui-DDWX2WYH.js +56903 -0
- package/dist/chunks/tui-DDWX2WYH.js.map +6 -0
- package/dist/chunks/upgrade-MC5JB7DK.js +315 -0
- package/dist/chunks/upgrade-MC5JB7DK.js.map +6 -0
- package/dist/chunks/webapi-6HUHZUAH.js +2715 -0
- package/dist/chunks/webapi-6HUHZUAH.js.map +6 -0
- package/dist/chunks/worktree-6YPRX6SU.js +116 -0
- package/dist/chunks/worktree-6YPRX6SU.js.map +6 -0
- package/dist/cli-runtime.d.ts +19 -0
- package/dist/cli-runtime.d.ts.map +1 -0
- package/dist/cli.d.ts +0 -15
- package/dist/cli.d.ts.map +1 -1
- package/dist/commands/acp-harness.d.ts +64 -0
- package/dist/commands/acp-harness.d.ts.map +1 -0
- package/dist/commands/acp-provider-retry.d.ts +27 -0
- package/dist/commands/acp-provider-retry.d.ts.map +1 -0
- package/dist/commands/acp.d.ts +134 -3
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/computer.d.ts +3 -0
- package/dist/commands/computer.d.ts.map +1 -0
- package/dist/commands/desktop-host-changes.d.ts +65 -0
- package/dist/commands/desktop-host-changes.d.ts.map +1 -0
- package/dist/commands/desktop-host-header.d.ts +30 -0
- package/dist/commands/desktop-host-header.d.ts.map +1 -0
- package/dist/commands/desktop-host.d.ts +354 -0
- package/dist/commands/desktop-host.d.ts.map +1 -0
- package/dist/commands/desktop-model-catalogue.d.ts +25 -0
- package/dist/commands/desktop-model-catalogue.d.ts.map +1 -0
- package/dist/commands/effort-levels.d.ts +3 -0
- package/dist/commands/effort-levels.d.ts.map +1 -0
- package/dist/commands/exec-flags.d.ts +4 -0
- package/dist/commands/exec-flags.d.ts.map +1 -1
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/lazy.d.ts +20 -0
- package/dist/commands/lazy.d.ts.map +1 -0
- package/dist/commands/manifest.d.ts +33 -0
- package/dist/commands/manifest.d.ts.map +1 -0
- package/dist/commands/pal.d.ts +5 -0
- package/dist/commands/pal.d.ts.map +1 -0
- package/dist/commands/permission-preview.d.ts +38 -0
- package/dist/commands/permission-preview.d.ts.map +1 -0
- package/dist/commands/registry.d.ts +10 -0
- package/dist/commands/registry.d.ts.map +1 -1
- package/dist/commands/serve.d.ts +1 -12
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/stdin.d.ts.map +1 -1
- package/dist/commands/upgrade.d.ts +14 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/compile-cache-enable.d.ts +2 -0
- package/dist/compile-cache-enable.d.ts.map +1 -0
- package/dist/compile-cache.d.ts +53 -0
- package/dist/compile-cache.d.ts.map +1 -0
- package/dist/computer/bundle.d.ts +18 -0
- package/dist/computer/bundle.d.ts.map +1 -0
- package/dist/computer/run.d.ts +26 -0
- package/dist/computer/run.d.ts.map +1 -0
- package/dist/computer/sentences.d.ts +30 -0
- package/dist/computer/sentences.d.ts.map +1 -0
- package/dist/config/load.d.ts +10 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/context/capabilities.d.ts +25 -2
- package/dist/context/capabilities.d.ts.map +1 -1
- package/dist/context/runtime-message.d.ts.map +1 -1
- package/dist/crash-report.d.ts +22 -0
- package/dist/crash-report.d.ts.map +1 -0
- package/dist/debug-requested.d.ts +7 -0
- package/dist/debug-requested.d.ts.map +1 -0
- package/dist/doctor/checks/credentials.d.ts.map +1 -1
- package/dist/exit-codes.d.ts +9 -1
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/index.js +105 -17
- package/dist/index.js.map +6 -1
- package/dist/integrations/harness/claude-adapter.d.ts +20 -0
- package/dist/integrations/harness/claude-adapter.d.ts.map +1 -0
- package/dist/integrations/harness/claude-protocol.d.ts +67 -0
- package/dist/integrations/harness/claude-protocol.d.ts.map +1 -0
- package/dist/integrations/harness/codex-adapter.d.ts +80 -0
- package/dist/integrations/harness/codex-adapter.d.ts.map +1 -0
- package/dist/integrations/harness/codex-protocol.d.ts +22 -0
- package/dist/integrations/harness/codex-protocol.d.ts.map +1 -0
- package/dist/integrations/harness/model-cache.d.ts +16 -0
- package/dist/integrations/harness/model-cache.d.ts.map +1 -0
- package/dist/integrations/harness/native-executable.d.ts +8 -0
- package/dist/integrations/harness/native-executable.d.ts.map +1 -0
- package/dist/integrations/harness/process.d.ts +25 -0
- package/dist/integrations/harness/process.d.ts.map +1 -0
- package/dist/integrations/peers/runtime.d.ts +64 -0
- package/dist/integrations/peers/runtime.d.ts.map +1 -0
- package/dist/integrations/plugins/inventory.d.ts +27 -0
- package/dist/integrations/plugins/inventory.d.ts.map +1 -0
- package/dist/integrations/providers/composer-settings.d.ts +33 -0
- package/dist/integrations/providers/composer-settings.d.ts.map +1 -0
- package/dist/integrations/providers/connections.d.ts +37 -0
- package/dist/integrations/providers/connections.d.ts.map +1 -0
- package/dist/integrations/providers/credential-store.d.ts +35 -0
- package/dist/integrations/providers/credential-store.d.ts.map +1 -1
- package/dist/integrations/providers/discover.d.ts +27 -1
- package/dist/integrations/providers/discover.d.ts.map +1 -1
- package/dist/integrations/providers/index.d.ts +2 -2
- package/dist/integrations/providers/index.d.ts.map +1 -1
- package/dist/integrations/providers/picker-operation.d.ts +6 -0
- package/dist/integrations/providers/picker-operation.d.ts.map +1 -0
- package/dist/integrations/providers/setup.d.ts +16 -1
- package/dist/integrations/providers/setup.d.ts.map +1 -1
- package/dist/integrations/resident/runner-worker.js +190 -161
- package/dist/integrations/resident/runner-worker.js.map +6 -1
- package/dist/integrations/sessions/display-title.d.ts +8 -0
- package/dist/integrations/sessions/display-title.d.ts.map +1 -1
- package/dist/integrations/sessions/store.d.ts +23 -9
- package/dist/integrations/sessions/store.d.ts.map +1 -1
- package/dist/integrations/sessions/task-context.d.ts +1 -1
- package/dist/integrations/sessions/task-context.d.ts.map +1 -1
- package/dist/integrations/sessions/transcript-export.d.ts.map +1 -1
- package/dist/integrations/subagents/activity.d.ts +9 -6
- package/dist/integrations/subagents/activity.d.ts.map +1 -1
- package/dist/integrations/subagents/operator-journal.d.ts +20 -0
- package/dist/integrations/subagents/operator-journal.d.ts.map +1 -0
- package/dist/integrations/subagents/runtime.d.ts +11 -0
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/trust/store.d.ts +14 -0
- package/dist/integrations/trust/store.d.ts.map +1 -1
- package/dist/integrations/web/search.d.ts +1 -1
- package/dist/pals/actions.d.ts +57 -0
- package/dist/pals/actions.d.ts.map +1 -0
- package/dist/pals/activity-command.d.ts +3 -0
- package/dist/pals/activity-command.d.ts.map +1 -0
- package/dist/pals/activity.d.ts +18 -0
- package/dist/pals/activity.d.ts.map +1 -0
- package/dist/pals/agent-session.d.ts +19 -0
- package/dist/pals/agent-session.d.ts.map +1 -0
- package/dist/pals/channel-actions.d.ts +5 -0
- package/dist/pals/channel-actions.d.ts.map +1 -0
- package/dist/pals/channel-local.d.ts +7 -0
- package/dist/pals/channel-local.d.ts.map +1 -0
- package/dist/pals/channel.d.ts +20 -0
- package/dist/pals/channel.d.ts.map +1 -0
- package/dist/pals/chat.d.ts +6 -0
- package/dist/pals/chat.d.ts.map +1 -0
- package/dist/pals/communication.d.ts +47 -0
- package/dist/pals/communication.d.ts.map +1 -0
- package/dist/pals/conversations.d.ts +26 -0
- package/dist/pals/conversations.d.ts.map +1 -0
- package/dist/pals/desktop-communication.d.ts +111 -0
- package/dist/pals/desktop-communication.d.ts.map +1 -0
- package/dist/pals/dispatch.d.ts +13 -0
- package/dist/pals/dispatch.d.ts.map +1 -0
- package/dist/pals/environment.d.ts +103 -0
- package/dist/pals/environment.d.ts.map +1 -0
- package/dist/pals/guest-mcp-transport.d.ts +28 -0
- package/dist/pals/guest-mcp-transport.d.ts.map +1 -0
- package/dist/pals/guest-mcp.d.ts +22 -0
- package/dist/pals/guest-mcp.d.ts.map +1 -0
- package/dist/pals/inbox-start.d.ts +35 -0
- package/dist/pals/inbox-start.d.ts.map +1 -0
- package/dist/pals/message-command.d.ts +3 -0
- package/dist/pals/message-command.d.ts.map +1 -0
- package/dist/pals/operator-tools.d.ts +23 -0
- package/dist/pals/operator-tools.d.ts.map +1 -0
- package/dist/pals/public-transcript.d.ts +4 -0
- package/dist/pals/public-transcript.d.ts.map +1 -0
- package/dist/pals/review.d.ts +16 -0
- package/dist/pals/review.d.ts.map +1 -0
- package/dist/pals/store.d.ts +16 -0
- package/dist/pals/store.d.ts.map +1 -0
- package/dist/pals/tui-session.d.ts +11 -0
- package/dist/pals/tui-session.d.ts.map +1 -0
- package/dist/permissions/mode-names.d.ts +2 -0
- package/dist/permissions/mode-names.d.ts.map +1 -0
- package/dist/resume-invocation.d.ts +1 -1
- package/dist/resume-invocation.d.ts.map +1 -1
- package/dist/resume-shell.d.ts +8 -0
- package/dist/resume-shell.d.ts.map +1 -0
- package/dist/schedule/build.d.ts +12 -3
- package/dist/schedule/build.d.ts.map +1 -1
- package/dist/schedule/commands/add.d.ts +1 -1
- package/dist/schedule/commands/add.d.ts.map +1 -1
- package/dist/schedule/commands/lifecycle.d.ts.map +1 -1
- package/dist/schedule/commands/list.d.ts +3 -1
- package/dist/schedule/commands/list.d.ts.map +1 -1
- package/dist/schedule/daemon/daemon.d.ts +2 -0
- package/dist/schedule/daemon/daemon.d.ts.map +1 -1
- package/dist/schedule/delivery.d.ts +66 -0
- package/dist/schedule/delivery.d.ts.map +1 -0
- package/dist/schedule/fire/fire.d.ts +1 -1
- package/dist/schedule/fire/fire.d.ts.map +1 -1
- package/dist/schedule/fire/run-script.d.ts +2 -2
- package/dist/schedule/fire/run-script.d.ts.map +1 -1
- package/dist/schedule/fire/script-report.d.ts +31 -0
- package/dist/schedule/fire/script-report.d.ts.map +1 -0
- package/dist/schedule/floor.d.ts +1 -1
- package/dist/schedule/floor.d.ts.map +1 -1
- package/dist/schedule/folder.d.ts +6 -0
- package/dist/schedule/folder.d.ts.map +1 -1
- package/dist/schedule/resume-command.d.ts +3 -3
- package/dist/schedule/resume-command.d.ts.map +1 -1
- package/dist/schedule/store/history.d.ts.map +1 -1
- package/dist/schedule/store/jobs.d.ts +6 -6
- package/dist/schedule/store/jobs.d.ts.map +1 -1
- package/dist/schedule/store/script-state.d.ts +31 -0
- package/dist/schedule/store/script-state.d.ts.map +1 -0
- package/dist/schedule/store/state.d.ts +4 -2
- package/dist/schedule/store/state.d.ts.map +1 -1
- package/dist/schedule/types.d.ts +61 -16
- package/dist/schedule/types.d.ts.map +1 -1
- package/dist/skills/store.d.ts.map +1 -1
- package/dist/terminal/host.d.ts +22 -0
- package/dist/terminal/host.d.ts.map +1 -0
- package/dist/terminal/manager.d.ts +124 -0
- package/dist/terminal/manager.d.ts.map +1 -0
- package/dist/terminal/protocol.d.ts +162 -0
- package/dist/terminal/protocol.d.ts.map +1 -0
- package/dist/terminal/pty.d.ts +85 -0
- package/dist/terminal/pty.d.ts.map +1 -0
- package/dist/terminal/ring.d.ts +23 -0
- package/dist/terminal/ring.d.ts.map +1 -0
- package/dist/terminal/screen.d.ts +31 -0
- package/dist/terminal/screen.d.ts.map +1 -0
- package/dist/termination-state.d.ts +19 -0
- package/dist/termination-state.d.ts.map +1 -0
- package/dist/termination.d.ts +2 -9
- package/dist/termination.d.ts.map +1 -1
- package/dist/tui/AgentExplorer.d.ts +4 -1
- package/dist/tui/AgentExplorer.d.ts.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/BackgroundJobsPanel.d.ts.map +1 -1
- package/dist/tui/BrandHeader.d.ts.map +1 -1
- package/dist/tui/ChildComposer.d.ts +22 -0
- package/dist/tui/ChildComposer.d.ts.map +1 -0
- package/dist/tui/ChoicePicker.d.ts.map +1 -1
- package/dist/tui/Composer.d.ts +21 -3
- package/dist/tui/Composer.d.ts.map +1 -1
- package/dist/tui/ComposerFrame.d.ts.map +1 -1
- package/dist/tui/CopyPicker.d.ts.map +1 -1
- package/dist/tui/EditPromptPicker.d.ts.map +1 -1
- package/dist/tui/EffortSlider.d.ts.map +1 -1
- package/dist/tui/LiveActivity.d.ts +7 -1
- package/dist/tui/LiveActivity.d.ts.map +1 -1
- package/dist/tui/Markdown.d.ts.map +1 -1
- package/dist/tui/PendingInputPanel.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/Picker.d.ts.map +1 -1
- package/dist/tui/ProviderSetup.d.ts.map +1 -1
- package/dist/tui/ResumePicker.d.ts.map +1 -1
- package/dist/tui/SaveSkillOverlay.d.ts.map +1 -1
- package/dist/tui/ScheduleReviewOverlay.d.ts +1 -1
- package/dist/tui/ScheduleReviewOverlay.d.ts.map +1 -1
- package/dist/tui/StatusBar.d.ts +7 -1
- package/dist/tui/StatusBar.d.ts.map +1 -1
- package/dist/tui/StatusPanel.d.ts.map +1 -1
- package/dist/tui/TextPrompt.d.ts.map +1 -1
- package/dist/tui/ToolOutputViewer.d.ts.map +1 -1
- package/dist/tui/Transcript.d.ts +3 -0
- package/dist/tui/Transcript.d.ts.map +1 -1
- package/dist/tui/TranscriptScrollView.d.ts +23 -0
- package/dist/tui/TranscriptScrollView.d.ts.map +1 -0
- package/dist/tui/TrustPrompt.d.ts +8 -0
- package/dist/tui/TrustPrompt.d.ts.map +1 -1
- package/dist/tui/agent.d.ts +125 -3
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/browser-notices.d.ts.map +1 -1
- package/dist/tui/capability-notice.d.ts +38 -0
- package/dist/tui/capability-notice.d.ts.map +1 -0
- package/dist/tui/console-input.d.ts +31 -0
- package/dist/tui/console-input.d.ts.map +1 -0
- package/dist/tui/conversation-evidence-view.d.ts.map +1 -1
- package/dist/tui/credential-entry.d.ts.map +1 -1
- package/dist/tui/exit-summary.d.ts +9 -1
- package/dist/tui/exit-summary.d.ts.map +1 -1
- package/dist/tui/external-editor.d.ts.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/key-hints.d.ts +51 -0
- package/dist/tui/key-hints.d.ts.map +1 -0
- package/dist/tui/launch-settings.d.ts +34 -0
- package/dist/tui/launch-settings.d.ts.map +1 -0
- package/dist/tui/log-pane.d.ts.map +1 -1
- package/dist/tui/model-choices.d.ts +39 -1
- package/dist/tui/model-choices.d.ts.map +1 -1
- package/dist/tui/model-search.d.ts +4 -1
- package/dist/tui/model-search.d.ts.map +1 -1
- package/dist/tui/output-size-watch.d.ts +32 -0
- package/dist/tui/output-size-watch.d.ts.map +1 -0
- package/dist/tui/permission-review.d.ts.map +1 -1
- package/dist/tui/person-terms.d.ts +58 -0
- package/dist/tui/person-terms.d.ts.map +1 -0
- package/dist/tui/plain-failure.d.ts +27 -0
- package/dist/tui/plain-failure.d.ts.map +1 -0
- package/dist/tui/plain-interruption.d.ts +46 -0
- package/dist/tui/plain-interruption.d.ts.map +1 -0
- package/dist/tui/provider-list.d.ts.map +1 -1
- package/dist/tui/render-mode.d.ts +19 -0
- package/dist/tui/render-mode.d.ts.map +1 -0
- package/dist/tui/schedule/host-commands.d.ts +2 -9
- package/dist/tui/schedule/host-commands.d.ts.map +1 -1
- package/dist/tui/schedule/integration.d.ts +8 -0
- package/dist/tui/schedule/integration.d.ts.map +1 -1
- package/dist/tui/schedule/source-deliveries.d.ts +37 -0
- package/dist/tui/schedule/source-deliveries.d.ts.map +1 -0
- package/dist/tui/schedule/startup.d.ts.map +1 -1
- package/dist/tui/schedule/tool-host.d.ts +3 -1
- package/dist/tui/schedule/tool-host.d.ts.map +1 -1
- package/dist/tui/setup-text.d.ts +14 -0
- package/dist/tui/setup-text.d.ts.map +1 -0
- package/dist/tui/shell-escape.d.ts.map +1 -1
- package/dist/tui/skills/suggest-setting.d.ts.map +1 -1
- package/dist/tui/slashCommands.d.ts +4 -1
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/terminal-choice-text.d.ts +7 -0
- package/dist/tui/terminal-choice-text.d.ts.map +1 -1
- package/dist/tui/tidy-tool-output.d.ts +83 -0
- package/dist/tui/tidy-tool-output.d.ts.map +1 -0
- package/dist/tui/tool-presentation.d.ts +6 -0
- package/dist/tui/tool-presentation.d.ts.map +1 -0
- package/dist/tui/transcript-scroll.d.ts +73 -0
- package/dist/tui/transcript-scroll.d.ts.map +1 -0
- package/dist/tui/triggers/copy.d.ts.map +1 -1
- package/dist/tui/triggers/setting.d.ts.map +1 -1
- package/dist/tui/turn-interruption.d.ts +1 -1
- package/dist/tui/turn-interruption.d.ts.map +1 -1
- package/dist/tui/turn-limits-settings.d.ts +4 -4
- package/dist/tui/turn-limits-settings.d.ts.map +1 -1
- package/dist/tui/types.d.ts +10 -0
- package/dist/tui/types.d.ts.map +1 -1
- package/dist/tui/web-activity.d.ts +3 -2
- package/dist/tui/web-activity.d.ts.map +1 -1
- package/dist/tui/zen-free-refusal.d.ts +17 -0
- package/dist/tui/zen-free-refusal.d.ts.map +1 -0
- package/dist/usage-error.d.ts +15 -0
- package/dist/usage-error.d.ts.map +1 -0
- package/dist/warning-filter.d.ts +30 -0
- package/dist/warning-filter.d.ts.map +1 -0
- package/package.json +21 -10
- package/skills/schedule-task/SKILL.md +74 -20
- package/dist/browser/commands.js +0 -365
- package/dist/browser/commands.js.map +0 -1
- package/dist/browser/control.js +0 -140
- package/dist/browser/control.js.map +0 -1
- package/dist/browser/describe.js +0 -27
- package/dist/browser/describe.js.map +0 -1
- package/dist/checkpoints/store.js +0 -192
- package/dist/checkpoints/store.js.map +0 -1
- package/dist/checkpoints/wrap.js +0 -37
- package/dist/checkpoints/wrap.js.map +0 -1
- package/dist/cli.js +0 -602
- package/dist/cli.js.map +0 -1
- package/dist/commands/acp.js +0 -388
- package/dist/commands/acp.js.map +0 -1
- package/dist/commands/archive.js +0 -65
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/browser.js +0 -16
- package/dist/commands/browser.js.map +0 -1
- package/dist/commands/doctor.js +0 -267
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/drain.js +0 -498
- package/dist/commands/drain.js.map +0 -1
- package/dist/commands/eval.js +0 -237
- package/dist/commands/eval.js.map +0 -1
- package/dist/commands/exec-flags.js +0 -334
- package/dist/commands/exec-flags.js.map +0 -1
- package/dist/commands/exec-json.js +0 -497
- package/dist/commands/exec-json.js.map +0 -1
- package/dist/commands/exec.js +0 -688
- package/dist/commands/exec.js.map +0 -1
- package/dist/commands/host-queries.js +0 -156
- package/dist/commands/host-queries.js.map +0 -1
- package/dist/commands/login.js +0 -315
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/mcp-oauth-login.js +0 -213
- package/dist/commands/mcp-oauth-login.js.map +0 -1
- package/dist/commands/mcp.js +0 -461
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/prior-messages.js +0 -329
- package/dist/commands/prior-messages.js.map +0 -1
- package/dist/commands/provider-wait.js +0 -65
- package/dist/commands/provider-wait.js.map +0 -1
- package/dist/commands/registry.js +0 -40
- package/dist/commands/registry.js.map +0 -1
- package/dist/commands/resident-flags.js +0 -193
- package/dist/commands/resident-flags.js.map +0 -1
- package/dist/commands/resident-learning.js +0 -293
- package/dist/commands/resident-learning.js.map +0 -1
- package/dist/commands/resident.js +0 -419
- package/dist/commands/resident.js.map +0 -1
- package/dist/commands/resume.js +0 -109
- package/dist/commands/resume.js.map +0 -1
- package/dist/commands/schedule.js +0 -131
- package/dist/commands/schedule.js.map +0 -1
- package/dist/commands/serve.js +0 -21
- package/dist/commands/serve.js.map +0 -1
- package/dist/commands/skills.js +0 -178
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/state.js +0 -110
- package/dist/commands/state.js.map +0 -1
- package/dist/commands/stdin.js +0 -69
- package/dist/commands/stdin.js.map +0 -1
- package/dist/commands/types.js +0 -2
- package/dist/commands/types.js.map +0 -1
- package/dist/commands/upgrade-progress.js +0 -54
- package/dist/commands/upgrade-progress.js.map +0 -1
- package/dist/commands/upgrade.js +0 -232
- package/dist/commands/upgrade.js.map +0 -1
- package/dist/commands/worktree.js +0 -86
- package/dist/commands/worktree.js.map +0 -1
- package/dist/config/composer-triggers.js +0 -80
- package/dist/config/composer-triggers.js.map +0 -1
- package/dist/config/debug.js +0 -126
- package/dist/config/debug.js.map +0 -1
- package/dist/config/load.js +0 -1208
- package/dist/config/load.js.map +0 -1
- package/dist/config/schema.js +0 -13
- package/dist/config/schema.js.map +0 -1
- package/dist/config/tool-result-screens.js +0 -104
- package/dist/config/tool-result-screens.js.map +0 -1
- package/dist/config/trusted-project-context.js +0 -28
- package/dist/config/trusted-project-context.js.map +0 -1
- package/dist/config/turn-guards.js +0 -46
- package/dist/config/turn-guards.js.map +0 -1
- package/dist/config/user-config.js +0 -118
- package/dist/config/user-config.js.map +0 -1
- package/dist/context/capabilities.js +0 -164
- package/dist/context/capabilities.js.map +0 -1
- package/dist/context/directories.js +0 -69
- package/dist/context/directories.js.map +0 -1
- package/dist/context/doctrine.js +0 -24
- package/dist/context/doctrine.js.map +0 -1
- package/dist/context/environment.js +0 -255
- package/dist/context/environment.js.map +0 -1
- package/dist/context/project-path.js +0 -16
- package/dist/context/project-path.js.map +0 -1
- package/dist/context/project-tracker.js +0 -217
- package/dist/context/project-tracker.js.map +0 -1
- package/dist/context/project.js +0 -256
- package/dist/context/project.js.map +0 -1
- package/dist/context/runtime-message.js +0 -30
- package/dist/context/runtime-message.js.map +0 -1
- package/dist/context/sandbox.js +0 -114
- package/dist/context/sandbox.js.map +0 -1
- package/dist/context/turn-snapshot.js +0 -122
- package/dist/context/turn-snapshot.js.map +0 -1
- package/dist/doctor/checks/browser.js +0 -63
- package/dist/doctor/checks/browser.js.map +0 -1
- package/dist/doctor/checks/chain.js +0 -211
- package/dist/doctor/checks/chain.js.map +0 -1
- package/dist/doctor/checks/credentials.js +0 -98
- package/dist/doctor/checks/credentials.js.map +0 -1
- package/dist/doctor/checks/index.js +0 -72
- package/dist/doctor/checks/index.js.map +0 -1
- package/dist/doctor/checks/invariants.js +0 -79
- package/dist/doctor/checks/invariants.js.map +0 -1
- package/dist/doctor/checks/logging.js +0 -69
- package/dist/doctor/checks/logging.js.map +0 -1
- package/dist/doctor/checks/providers.js +0 -30
- package/dist/doctor/checks/providers.js.map +0 -1
- package/dist/doctor/checks/runtime.js +0 -40
- package/dist/doctor/checks/runtime.js.map +0 -1
- package/dist/doctor/checks/sandbox.js +0 -76
- package/dist/doctor/checks/sandbox.js.map +0 -1
- package/dist/doctor/checks/scheduler.js +0 -74
- package/dist/doctor/checks/scheduler.js.map +0 -1
- package/dist/doctor/checks/session-export.js +0 -62
- package/dist/doctor/checks/session-export.js.map +0 -1
- package/dist/doctor/checks/telemetry.js +0 -58
- package/dist/doctor/checks/telemetry.js.map +0 -1
- package/dist/doctor/checks/vault.js +0 -67
- package/dist/doctor/checks/vault.js.map +0 -1
- package/dist/doctor/index.js +0 -3
- package/dist/doctor/index.js.map +0 -1
- package/dist/doctor/registry.js +0 -230
- package/dist/doctor/registry.js.map +0 -1
- package/dist/exit-codes.js +0 -62
- package/dist/exit-codes.js.map +0 -1
- package/dist/installation.js +0 -48
- package/dist/installation.js.map +0 -1
- package/dist/integrations/clipboard/image.js +0 -119
- package/dist/integrations/clipboard/image.js.map +0 -1
- package/dist/integrations/clipboard/text.js +0 -43
- package/dist/integrations/clipboard/text.js.map +0 -1
- package/dist/integrations/files/attachment-store.js +0 -86
- package/dist/integrations/files/attachment-store.js.map +0 -1
- package/dist/integrations/mcp/oauth-store.js +0 -549
- package/dist/integrations/mcp/oauth-store.js.map +0 -1
- package/dist/integrations/mcp/servers.js +0 -611
- package/dist/integrations/mcp/servers.js.map +0 -1
- package/dist/integrations/mcp/user-config.js +0 -92
- package/dist/integrations/mcp/user-config.js.map +0 -1
- package/dist/integrations/notifications/desktop/freedesktop.js +0 -33
- package/dist/integrations/notifications/desktop/freedesktop.js.map +0 -1
- package/dist/integrations/notifications/desktop/macos.js +0 -17
- package/dist/integrations/notifications/desktop/macos.js.map +0 -1
- package/dist/integrations/notifications/desktop/sanitize.js +0 -49
- package/dist/integrations/notifications/desktop/sanitize.js.map +0 -1
- package/dist/integrations/notifications/desktop/throttle.js +0 -39
- package/dist/integrations/notifications/desktop/throttle.js.map +0 -1
- package/dist/integrations/notifications/desktop/windows-toast.js +0 -56
- package/dist/integrations/notifications/desktop/windows-toast.js.map +0 -1
- package/dist/integrations/notifications/desktop.js +0 -199
- package/dist/integrations/notifications/desktop.js.map +0 -1
- package/dist/integrations/notifications/terminal.js +0 -46
- package/dist/integrations/notifications/terminal.js.map +0 -1
- package/dist/integrations/npm-invocation.js +0 -23
- package/dist/integrations/npm-invocation.js.map +0 -1
- package/dist/integrations/plugins/runtime.js +0 -189
- package/dist/integrations/plugins/runtime.js.map +0 -1
- package/dist/integrations/plugins/settings.js +0 -106
- package/dist/integrations/plugins/settings.js.map +0 -1
- package/dist/integrations/providers/access.js +0 -23
- package/dist/integrations/providers/access.js.map +0 -1
- package/dist/integrations/providers/chain-capabilities.js +0 -211
- package/dist/integrations/providers/chain-capabilities.js.map +0 -1
- package/dist/integrations/providers/codex-device-login.js +0 -195
- package/dist/integrations/providers/codex-device-login.js.map +0 -1
- package/dist/integrations/providers/codex-oauth.js +0 -95
- package/dist/integrations/providers/codex-oauth.js.map +0 -1
- package/dist/integrations/providers/credential-provider.js +0 -74
- package/dist/integrations/providers/credential-provider.js.map +0 -1
- package/dist/integrations/providers/credential-store.js +0 -657
- package/dist/integrations/providers/credential-store.js.map +0 -1
- package/dist/integrations/providers/current-credential.js +0 -45
- package/dist/integrations/providers/current-credential.js.map +0 -1
- package/dist/integrations/providers/discover.js +0 -313
- package/dist/integrations/providers/discover.js.map +0 -1
- package/dist/integrations/providers/gemini-credentials.js +0 -148
- package/dist/integrations/providers/gemini-credentials.js.map +0 -1
- package/dist/integrations/providers/harness-credentials.js +0 -344
- package/dist/integrations/providers/harness-credentials.js.map +0 -1
- package/dist/integrations/providers/identity.js +0 -64
- package/dist/integrations/providers/identity.js.map +0 -1
- package/dist/integrations/providers/index.js +0 -14
- package/dist/integrations/providers/index.js.map +0 -1
- package/dist/integrations/providers/keychain.js +0 -78
- package/dist/integrations/providers/keychain.js.map +0 -1
- package/dist/integrations/providers/model-reasoning.js +0 -51
- package/dist/integrations/providers/model-reasoning.js.map +0 -1
- package/dist/integrations/providers/oauth.js +0 -302
- package/dist/integrations/providers/oauth.js.map +0 -1
- package/dist/integrations/providers/preferences.js +0 -287
- package/dist/integrations/providers/preferences.js.map +0 -1
- package/dist/integrations/providers/register.js +0 -154
- package/dist/integrations/providers/register.js.map +0 -1
- package/dist/integrations/providers/registry.js +0 -289
- package/dist/integrations/providers/registry.js.map +0 -1
- package/dist/integrations/providers/setup.js +0 -158
- package/dist/integrations/providers/setup.js.map +0 -1
- package/dist/integrations/providers/subscription-login.js +0 -328
- package/dist/integrations/providers/subscription-login.js.map +0 -1
- package/dist/integrations/providers/zen-catalogue.js +0 -196
- package/dist/integrations/providers/zen-catalogue.js.map +0 -1
- package/dist/integrations/resident/foreground.js +0 -95
- package/dist/integrations/resident/foreground.js.map +0 -1
- package/dist/integrations/resident/inspection.js +0 -282
- package/dist/integrations/resident/inspection.js.map +0 -1
- package/dist/integrations/resident/learning-sources.js +0 -79
- package/dist/integrations/resident/learning-sources.js.map +0 -1
- package/dist/integrations/resident/learning-storage.js +0 -55
- package/dist/integrations/resident/learning-storage.js.map +0 -1
- package/dist/integrations/resident/lifecycle-errors.js +0 -15
- package/dist/integrations/resident/lifecycle-errors.js.map +0 -1
- package/dist/integrations/resident/owned-runner.js +0 -91
- package/dist/integrations/resident/owned-runner.js.map +0 -1
- package/dist/integrations/resident/runner-control.js +0 -238
- package/dist/integrations/resident/runner-control.js.map +0 -1
- package/dist/integrations/resident/runner-launch.js +0 -142
- package/dist/integrations/resident/runner-launch.js.map +0 -1
- package/dist/integrations/resident/runner-store.js +0 -284
- package/dist/integrations/resident/runner-store.js.map +0 -1
- package/dist/integrations/resident/session-log-reads.js +0 -125
- package/dist/integrations/resident/session-log-reads.js.map +0 -1
- package/dist/integrations/resident/session-step.js +0 -476
- package/dist/integrations/resident/session-step.js.map +0 -1
- package/dist/integrations/resident/storage.js +0 -183
- package/dist/integrations/resident/storage.js.map +0 -1
- package/dist/integrations/resident/tool-evidence.js +0 -123
- package/dist/integrations/resident/tool-evidence.js.map +0 -1
- package/dist/integrations/resident/verification.js +0 -109
- package/dist/integrations/resident/verification.js.map +0 -1
- package/dist/integrations/sessions/checkpoint-view.js +0 -48
- package/dist/integrations/sessions/checkpoint-view.js.map +0 -1
- package/dist/integrations/sessions/context-inventory.js +0 -44
- package/dist/integrations/sessions/context-inventory.js.map +0 -1
- package/dist/integrations/sessions/conversation-search.js +0 -572
- package/dist/integrations/sessions/conversation-search.js.map +0 -1
- package/dist/integrations/sessions/display-title.js +0 -5
- package/dist/integrations/sessions/display-title.js.map +0 -1
- package/dist/integrations/sessions/evidence-page-validation.js +0 -74
- package/dist/integrations/sessions/evidence-page-validation.js.map +0 -1
- package/dist/integrations/sessions/evidence-recall.js +0 -117
- package/dist/integrations/sessions/evidence-recall.js.map +0 -1
- package/dist/integrations/sessions/store.js +0 -780
- package/dist/integrations/sessions/store.js.map +0 -1
- package/dist/integrations/sessions/task-context.js +0 -96
- package/dist/integrations/sessions/task-context.js.map +0 -1
- package/dist/integrations/sessions/transcript-export.js +0 -342
- package/dist/integrations/sessions/transcript-export.js.map +0 -1
- package/dist/integrations/state/home.js +0 -17
- package/dist/integrations/state/home.js.map +0 -1
- package/dist/integrations/state/identity.js +0 -49
- package/dist/integrations/state/identity.js.map +0 -1
- package/dist/integrations/state/immutable-json.js +0 -38
- package/dist/integrations/state/immutable-json.js.map +0 -1
- package/dist/integrations/state/private-directory.js +0 -43
- package/dist/integrations/state/private-directory.js.map +0 -1
- package/dist/integrations/state/project.js +0 -13
- package/dist/integrations/state/project.js.map +0 -1
- package/dist/integrations/state/report.js +0 -626
- package/dist/integrations/state/report.js.map +0 -1
- package/dist/integrations/state/retention.js +0 -18
- package/dist/integrations/state/retention.js.map +0 -1
- package/dist/integrations/subagents/NamzuCliAgent.js +0 -9
- package/dist/integrations/subagents/NamzuCliAgent.js.map +0 -1
- package/dist/integrations/subagents/activity.js +0 -759
- package/dist/integrations/subagents/activity.js.map +0 -1
- package/dist/integrations/subagents/batches.js +0 -172
- package/dist/integrations/subagents/batches.js.map +0 -1
- package/dist/integrations/subagents/definitions.js +0 -26
- package/dist/integrations/subagents/definitions.js.map +0 -1
- package/dist/integrations/subagents/model-effort.js +0 -12
- package/dist/integrations/subagents/model-effort.js.map +0 -1
- package/dist/integrations/subagents/parent.js +0 -47
- package/dist/integrations/subagents/parent.js.map +0 -1
- package/dist/integrations/subagents/policy.js +0 -3
- package/dist/integrations/subagents/policy.js.map +0 -1
- package/dist/integrations/subagents/replay.js +0 -295
- package/dist/integrations/subagents/replay.js.map +0 -1
- package/dist/integrations/subagents/runtime.js +0 -1317
- package/dist/integrations/subagents/runtime.js.map +0 -1
- package/dist/integrations/subagents/saved-agents.js +0 -102
- package/dist/integrations/subagents/saved-agents.js.map +0 -1
- package/dist/integrations/subagents/slash.js +0 -45
- package/dist/integrations/subagents/slash.js.map +0 -1
- package/dist/integrations/subagents/worktree-driver.js +0 -72
- package/dist/integrations/subagents/worktree-driver.js.map +0 -1
- package/dist/integrations/telemetry/session-export.js +0 -119
- package/dist/integrations/telemetry/session-export.js.map +0 -1
- package/dist/integrations/trust/store.js +0 -73
- package/dist/integrations/trust/store.js.map +0 -1
- package/dist/integrations/updates.js +0 -115
- package/dist/integrations/updates.js.map +0 -1
- package/dist/integrations/web/exa-search.js +0 -154
- package/dist/integrations/web/exa-search.js.map +0 -1
- package/dist/integrations/web/open-url.js +0 -73
- package/dist/integrations/web/open-url.js.map +0 -1
- package/dist/integrations/web/search.js +0 -68
- package/dist/integrations/web/search.js.map +0 -1
- package/dist/integrations/worktrees/managed.js +0 -231
- package/dist/integrations/worktrees/managed.js.map +0 -1
- package/dist/logging.js +0 -115
- package/dist/logging.js.map +0 -1
- package/dist/memory/io.js +0 -177
- package/dist/memory/io.js.map +0 -1
- package/dist/memory/presentation.js +0 -76
- package/dist/memory/presentation.js.map +0 -1
- package/dist/memory/store.js +0 -180
- package/dist/memory/store.js.map +0 -1
- package/dist/memory/typed.js +0 -336
- package/dist/memory/typed.js.map +0 -1
- package/dist/output/formatter.js +0 -16
- package/dist/output/formatter.js.map +0 -1
- package/dist/output/index.js +0 -22
- package/dist/output/index.js.map +0 -1
- package/dist/output/json.js +0 -51
- package/dist/output/json.js.map +0 -1
- package/dist/output/text.js +0 -50
- package/dist/output/text.js.map +0 -1
- package/dist/output/yaml.js +0 -26
- package/dist/output/yaml.js.map +0 -1
- package/dist/output-schema.js +0 -18
- package/dist/output-schema.js.map +0 -1
- package/dist/permissions/browser-sites.js +0 -270
- package/dist/permissions/browser-sites.js.map +0 -1
- package/dist/permissions/canonical-project.js +0 -13
- package/dist/permissions/canonical-project.js.map +0 -1
- package/dist/permissions/checks.js +0 -128
- package/dist/permissions/checks.js.map +0 -1
- package/dist/permissions/headless-trust.js +0 -86
- package/dist/permissions/headless-trust.js.map +0 -1
- package/dist/permissions/live-mode.js +0 -62
- package/dist/permissions/live-mode.js.map +0 -1
- package/dist/permissions/mode.js +0 -65
- package/dist/permissions/mode.js.map +0 -1
- package/dist/permissions/rules.js +0 -358
- package/dist/permissions/rules.js.map +0 -1
- package/dist/release-notes.js +0 -64
- package/dist/release-notes.js.map +0 -1
- package/dist/resume-invocation.js +0 -24
- package/dist/resume-invocation.js.map +0 -1
- package/dist/schedule/build.js +0 -374
- package/dist/schedule/build.js.map +0 -1
- package/dist/schedule/changes.js +0 -78
- package/dist/schedule/changes.js.map +0 -1
- package/dist/schedule/commands/add.js +0 -603
- package/dist/schedule/commands/add.js.map +0 -1
- package/dist/schedule/commands/args.js +0 -80
- package/dist/schedule/commands/args.js.map +0 -1
- package/dist/schedule/commands/confirm-prompt.js +0 -21
- package/dist/schedule/commands/confirm-prompt.js.map +0 -1
- package/dist/schedule/commands/lifecycle.js +0 -586
- package/dist/schedule/commands/lifecycle.js.map +0 -1
- package/dist/schedule/commands/list.js +0 -260
- package/dist/schedule/commands/list.js.map +0 -1
- package/dist/schedule/commands/logs.js +0 -106
- package/dist/schedule/commands/logs.js.map +0 -1
- package/dist/schedule/commands/service.js +0 -458
- package/dist/schedule/commands/service.js.map +0 -1
- package/dist/schedule/daemon/daemon.js +0 -1265
- package/dist/schedule/daemon/daemon.js.map +0 -1
- package/dist/schedule/daemon/endpoint.js +0 -158
- package/dist/schedule/daemon/endpoint.js.map +0 -1
- package/dist/schedule/daemon/exit.js +0 -8
- package/dist/schedule/daemon/exit.js.map +0 -1
- package/dist/schedule/daemon/log.js +0 -53
- package/dist/schedule/daemon/log.js.map +0 -1
- package/dist/schedule/daemon/notify.js +0 -158
- package/dist/schedule/daemon/notify.js.map +0 -1
- package/dist/schedule/daemon/retention.js +0 -46
- package/dist/schedule/daemon/retention.js.map +0 -1
- package/dist/schedule/daemon/sessions.js +0 -52
- package/dist/schedule/daemon/sessions.js.map +0 -1
- package/dist/schedule/env.js +0 -106
- package/dist/schedule/env.js.map +0 -1
- package/dist/schedule/fire/browser-preflight.js +0 -73
- package/dist/schedule/fire/browser-preflight.js.map +0 -1
- package/dist/schedule/fire/calls.js +0 -114
- package/dist/schedule/fire/calls.js.map +0 -1
- package/dist/schedule/fire/fire.js +0 -681
- package/dist/schedule/fire/fire.js.map +0 -1
- package/dist/schedule/fire/result.js +0 -19
- package/dist/schedule/fire/result.js.map +0 -1
- package/dist/schedule/fire/run-script.js +0 -81
- package/dist/schedule/fire/run-script.js.map +0 -1
- package/dist/schedule/fire/unattended-note.js +0 -51
- package/dist/schedule/fire/unattended-note.js.map +0 -1
- package/dist/schedule/fire/wake-gate.js +0 -90
- package/dist/schedule/fire/wake-gate.js.map +0 -1
- package/dist/schedule/floor.js +0 -1561
- package/dist/schedule/floor.js.map +0 -1
- package/dist/schedule/folder.js +0 -84
- package/dist/schedule/folder.js.map +0 -1
- package/dist/schedule/paths.js +0 -53
- package/dist/schedule/paths.js.map +0 -1
- package/dist/schedule/policy.js +0 -414
- package/dist/schedule/policy.js.map +0 -1
- package/dist/schedule/resume-command.js +0 -72
- package/dist/schedule/resume-command.js.map +0 -1
- package/dist/schedule/script-check.js +0 -146
- package/dist/schedule/script-check.js.map +0 -1
- package/dist/schedule/script-shell.js +0 -5
- package/dist/schedule/script-shell.js.map +0 -1
- package/dist/schedule/service/detect.js +0 -29
- package/dist/schedule/service/detect.js.map +0 -1
- package/dist/schedule/service/index.js +0 -361
- package/dist/schedule/service/index.js.map +0 -1
- package/dist/schedule/service/launchd.js +0 -84
- package/dist/schedule/service/launchd.js.map +0 -1
- package/dist/schedule/service/manifest.js +0 -16
- package/dist/schedule/service/manifest.js.map +0 -1
- package/dist/schedule/service/names.js +0 -22
- package/dist/schedule/service/names.js.map +0 -1
- package/dist/schedule/service/quote.js +0 -64
- package/dist/schedule/service/quote.js.map +0 -1
- package/dist/schedule/service/runner.js +0 -31
- package/dist/schedule/service/runner.js.map +0 -1
- package/dist/schedule/service/systemd.js +0 -96
- package/dist/schedule/service/systemd.js.map +0 -1
- package/dist/schedule/service/windows-task.js +0 -199
- package/dist/schedule/service/windows-task.js.map +0 -1
- package/dist/schedule/service/wsl.js +0 -121
- package/dist/schedule/service/wsl.js.map +0 -1
- package/dist/schedule/store/atomic.js +0 -138
- package/dist/schedule/store/atomic.js.map +0 -1
- package/dist/schedule/store/claims.js +0 -57
- package/dist/schedule/store/claims.js.map +0 -1
- package/dist/schedule/store/digest.js +0 -139
- package/dist/schedule/store/digest.js.map +0 -1
- package/dist/schedule/store/history.js +0 -117
- package/dist/schedule/store/history.js.map +0 -1
- package/dist/schedule/store/jobs.js +0 -277
- package/dist/schedule/store/jobs.js.map +0 -1
- package/dist/schedule/store/state.js +0 -38
- package/dist/schedule/store/state.js.map +0 -1
- package/dist/schedule/types.js +0 -33
- package/dist/schedule/types.js.map +0 -1
- package/dist/schedule/visible-source.js +0 -59
- package/dist/schedule/visible-source.js.map +0 -1
- package/dist/skills/audit.js +0 -75
- package/dist/skills/audit.js.map +0 -1
- package/dist/skills/catalog.js +0 -226
- package/dist/skills/catalog.js.map +0 -1
- package/dist/skills/directory.js +0 -51
- package/dist/skills/directory.js.map +0 -1
- package/dist/skills/save.js +0 -344
- package/dist/skills/save.js.map +0 -1
- package/dist/skills/store.js +0 -351
- package/dist/skills/store.js.map +0 -1
- package/dist/termination.js +0 -143
- package/dist/termination.js.map +0 -1
- package/dist/test-support/toolset.js +0 -27
- package/dist/test-support/toolset.js.map +0 -1
- package/dist/tui/AgentExplorer.js +0 -789
- package/dist/tui/AgentExplorer.js.map +0 -1
- package/dist/tui/App.js +0 -8499
- package/dist/tui/App.js.map +0 -1
- package/dist/tui/BackgroundJobsPanel.js +0 -129
- package/dist/tui/BackgroundJobsPanel.js.map +0 -1
- package/dist/tui/BrandHeader.js +0 -14
- package/dist/tui/BrandHeader.js.map +0 -1
- package/dist/tui/Checklist.js +0 -90
- package/dist/tui/Checklist.js.map +0 -1
- package/dist/tui/ChoicePicker.js +0 -64
- package/dist/tui/ChoicePicker.js.map +0 -1
- package/dist/tui/Composer.js +0 -939
- package/dist/tui/Composer.js.map +0 -1
- package/dist/tui/ComposerFrame.js +0 -79
- package/dist/tui/ComposerFrame.js.map +0 -1
- package/dist/tui/CopyPicker.js +0 -19
- package/dist/tui/CopyPicker.js.map +0 -1
- package/dist/tui/EditPromptPicker.js +0 -17
- package/dist/tui/EditPromptPicker.js.map +0 -1
- package/dist/tui/EffortSlider.js +0 -134
- package/dist/tui/EffortSlider.js.map +0 -1
- package/dist/tui/LiveActivity.js +0 -89
- package/dist/tui/LiveActivity.js.map +0 -1
- package/dist/tui/Markdown.js +0 -137
- package/dist/tui/Markdown.js.map +0 -1
- package/dist/tui/PendingInputPanel.js +0 -108
- package/dist/tui/PendingInputPanel.js.map +0 -1
- package/dist/tui/PermissionOverlay.js +0 -278
- package/dist/tui/PermissionOverlay.js.map +0 -1
- package/dist/tui/Picker.js +0 -1310
- package/dist/tui/Picker.js.map +0 -1
- package/dist/tui/ProviderSetup.js +0 -125
- package/dist/tui/ProviderSetup.js.map +0 -1
- package/dist/tui/ResumePicker.js +0 -34
- package/dist/tui/ResumePicker.js.map +0 -1
- package/dist/tui/SaveSkillOverlay.js +0 -118
- package/dist/tui/SaveSkillOverlay.js.map +0 -1
- package/dist/tui/ScheduleReviewOverlay.js +0 -166
- package/dist/tui/ScheduleReviewOverlay.js.map +0 -1
- package/dist/tui/StatusBar.js +0 -182
- package/dist/tui/StatusBar.js.map +0 -1
- package/dist/tui/StatusPanel.js +0 -11
- package/dist/tui/StatusPanel.js.map +0 -1
- package/dist/tui/TaskList.js +0 -43
- package/dist/tui/TaskList.js.map +0 -1
- package/dist/tui/TextPrompt.js +0 -185
- package/dist/tui/TextPrompt.js.map +0 -1
- package/dist/tui/ToolOutputViewer.js +0 -39
- package/dist/tui/ToolOutputViewer.js.map +0 -1
- package/dist/tui/Transcript.js +0 -235
- package/dist/tui/Transcript.js.map +0 -1
- package/dist/tui/TrustPrompt.js +0 -13
- package/dist/tui/TrustPrompt.js.map +0 -1
- package/dist/tui/ViewportBound.js +0 -55
- package/dist/tui/ViewportBound.js.map +0 -1
- package/dist/tui/agent-transcript-rows.js +0 -115
- package/dist/tui/agent-transcript-rows.js.map +0 -1
- package/dist/tui/agent.js +0 -4301
- package/dist/tui/agent.js.map +0 -1
- package/dist/tui/browser-notices.js +0 -107
- package/dist/tui/browser-notices.js.map +0 -1
- package/dist/tui/choice-selection.js +0 -38
- package/dist/tui/choice-selection.js.map +0 -1
- package/dist/tui/compact-transcript.js +0 -46
- package/dist/tui/compact-transcript.js.map +0 -1
- package/dist/tui/consent-timing.js +0 -48
- package/dist/tui/consent-timing.js.map +0 -1
- package/dist/tui/conversation-evidence-view.js +0 -96
- package/dist/tui/conversation-evidence-view.js.map +0 -1
- package/dist/tui/conversation-history.js +0 -33
- package/dist/tui/conversation-history.js.map +0 -1
- package/dist/tui/copy-targets.js +0 -189
- package/dist/tui/copy-targets.js.map +0 -1
- package/dist/tui/credential-entry.js +0 -134
- package/dist/tui/credential-entry.js.map +0 -1
- package/dist/tui/edit-prompts.js +0 -35
- package/dist/tui/edit-prompts.js.map +0 -1
- package/dist/tui/exit-summary.js +0 -30
- package/dist/tui/exit-summary.js.map +0 -1
- package/dist/tui/external-editor.js +0 -169
- package/dist/tui/external-editor.js.map +0 -1
- package/dist/tui/hypermode.js +0 -58
- package/dist/tui/hypermode.js.map +0 -1
- package/dist/tui/index.js +0 -81
- package/dist/tui/index.js.map +0 -1
- package/dist/tui/live-window.js +0 -281
- package/dist/tui/live-window.js.map +0 -1
- package/dist/tui/log-pane.js +0 -100
- package/dist/tui/log-pane.js.map +0 -1
- package/dist/tui/login-prompt.js +0 -105
- package/dist/tui/login-prompt.js.map +0 -1
- package/dist/tui/logo.js +0 -9
- package/dist/tui/logo.js.map +0 -1
- package/dist/tui/markdown-block-cache.js +0 -76
- package/dist/tui/markdown-block-cache.js.map +0 -1
- package/dist/tui/markdown-table.js +0 -175
- package/dist/tui/markdown-table.js.map +0 -1
- package/dist/tui/markdown-wrap.js +0 -189
- package/dist/tui/markdown-wrap.js.map +0 -1
- package/dist/tui/markdownParser.js +0 -237
- package/dist/tui/markdownParser.js.map +0 -1
- package/dist/tui/mentions.js +0 -150
- package/dist/tui/mentions.js.map +0 -1
- package/dist/tui/model-catalogue-view.js +0 -75
- package/dist/tui/model-catalogue-view.js.map +0 -1
- package/dist/tui/model-choices.js +0 -157
- package/dist/tui/model-choices.js.map +0 -1
- package/dist/tui/model-search.js +0 -28
- package/dist/tui/model-search.js.map +0 -1
- package/dist/tui/model-selection-intent.js +0 -96
- package/dist/tui/model-selection-intent.js.map +0 -1
- package/dist/tui/model-switch-tool.js +0 -74
- package/dist/tui/model-switch-tool.js.map +0 -1
- package/dist/tui/model-switch.js +0 -106
- package/dist/tui/model-switch.js.map +0 -1
- package/dist/tui/notices.js +0 -27
- package/dist/tui/notices.js.map +0 -1
- package/dist/tui/open-browser.js +0 -147
- package/dist/tui/open-browser.js.map +0 -1
- package/dist/tui/permission-review.js +0 -858
- package/dist/tui/permission-review.js.map +0 -1
- package/dist/tui/plugin-view.js +0 -59
- package/dist/tui/plugin-view.js.map +0 -1
- package/dist/tui/provider-list.js +0 -327
- package/dist/tui/provider-list.js.map +0 -1
- package/dist/tui/provider-selection.js +0 -16
- package/dist/tui/provider-selection.js.map +0 -1
- package/dist/tui/schedule/host-commands.js +0 -428
- package/dist/tui/schedule/host-commands.js.map +0 -1
- package/dist/tui/schedule/integration.js +0 -150
- package/dist/tui/schedule/integration.js.map +0 -1
- package/dist/tui/schedule/loop-host.js +0 -153
- package/dist/tui/schedule/loop-host.js.map +0 -1
- package/dist/tui/schedule/resume.js +0 -333
- package/dist/tui/schedule/resume.js.map +0 -1
- package/dist/tui/schedule/startup.js +0 -121
- package/dist/tui/schedule/startup.js.map +0 -1
- package/dist/tui/schedule/tool-host.js +0 -619
- package/dist/tui/schedule/tool-host.js.map +0 -1
- package/dist/tui/selection-window.js +0 -37
- package/dist/tui/selection-window.js.map +0 -1
- package/dist/tui/shell-escape.js +0 -110
- package/dist/tui/shell-escape.js.map +0 -1
- package/dist/tui/skills/learning.js +0 -131
- package/dist/tui/skills/learning.js.map +0 -1
- package/dist/tui/skills/suggest-setting.js +0 -55
- package/dist/tui/skills/suggest-setting.js.map +0 -1
- package/dist/tui/skills/suggestion-ledger.js +0 -52
- package/dist/tui/skills/suggestion-ledger.js.map +0 -1
- package/dist/tui/slashCommands.js +0 -1588
- package/dist/tui/slashCommands.js.map +0 -1
- package/dist/tui/status-card.js +0 -30
- package/dist/tui/status-card.js.map +0 -1
- package/dist/tui/status-panel-layout.js +0 -42
- package/dist/tui/status-panel-layout.js.map +0 -1
- package/dist/tui/stream-blocks.js +0 -187
- package/dist/tui/stream-blocks.js.map +0 -1
- package/dist/tui/task-activity.js +0 -157
- package/dist/tui/task-activity.js.map +0 -1
- package/dist/tui/terminal-choice-text.js +0 -118
- package/dist/tui/terminal-choice-text.js.map +0 -1
- package/dist/tui/terminal-display.js +0 -65
- package/dist/tui/terminal-display.js.map +0 -1
- package/dist/tui/terminal-hyperlinks.js +0 -70
- package/dist/tui/terminal-hyperlinks.js.map +0 -1
- package/dist/tui/theme.js +0 -34
- package/dist/tui/theme.js.map +0 -1
- package/dist/tui/triggers/analyze.js +0 -221
- package/dist/tui/triggers/analyze.js.map +0 -1
- package/dist/tui/triggers/context-text.js +0 -44
- package/dist/tui/triggers/context-text.js.map +0 -1
- package/dist/tui/triggers/copy.js +0 -163
- package/dist/tui/triggers/copy.js.map +0 -1
- package/dist/tui/triggers/detect.js +0 -295
- package/dist/tui/triggers/detect.js.map +0 -1
- package/dist/tui/triggers/fold.js +0 -44
- package/dist/tui/triggers/fold.js.map +0 -1
- package/dist/tui/triggers/pattern.js +0 -243
- package/dist/tui/triggers/pattern.js.map +0 -1
- package/dist/tui/triggers/provenance.js +0 -98
- package/dist/tui/triggers/provenance.js.map +0 -1
- package/dist/tui/triggers/registry.js +0 -164
- package/dist/tui/triggers/registry.js.map +0 -1
- package/dist/tui/triggers/setting.js +0 -61
- package/dist/tui/triggers/setting.js.map +0 -1
- package/dist/tui/triggers/verbs.js +0 -140
- package/dist/tui/triggers/verbs.js.map +0 -1
- package/dist/tui/turn-interruption.js +0 -160
- package/dist/tui/turn-interruption.js.map +0 -1
- package/dist/tui/turn-limits-settings.js +0 -88
- package/dist/tui/turn-limits-settings.js.map +0 -1
- package/dist/tui/types.js +0 -7
- package/dist/tui/types.js.map +0 -1
- package/dist/tui/units.js +0 -20
- package/dist/tui/units.js.map +0 -1
- package/dist/tui/use-selection-index.js +0 -13
- package/dist/tui/use-selection-index.js.map +0 -1
- package/dist/tui/web-activity.js +0 -105
- package/dist/tui/web-activity.js.map +0 -1
- package/dist/tui/workspace-diff.js +0 -101
- package/dist/tui/workspace-diff.js.map +0 -1
- package/dist/tui/workspace-review.js +0 -67
- package/dist/tui/workspace-review.js.map +0 -1
- package/dist/user-commands/store.js +0 -235
- package/dist/user-commands/store.js.map +0 -1
- package/dist/version.js +0 -23
- package/dist/version.js.map +0 -1
|
@@ -0,0 +1,2953 @@
|
|
|
1
|
+
import { createRequire as __namzuCreateRequire } from 'node:module';
|
|
2
|
+
const require = __namzuCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_WAKE_GATE_CONTEXT_CHARS,
|
|
5
|
+
checkJobFolder,
|
|
6
|
+
checkScratchFolder,
|
|
7
|
+
computeProjectDigest,
|
|
8
|
+
folderReadable,
|
|
9
|
+
plannedScratchFolder,
|
|
10
|
+
projectDigestChanges,
|
|
11
|
+
scriptShellUnavailableReason,
|
|
12
|
+
verifyScheduledScript
|
|
13
|
+
} from "./chunk-KCWUQRZV.js";
|
|
14
|
+
import {
|
|
15
|
+
CallTally,
|
|
16
|
+
callsWords
|
|
17
|
+
} from "./chunk-E5VLANCO.js";
|
|
18
|
+
import {
|
|
19
|
+
allowsWrites,
|
|
20
|
+
compileJobPolicy,
|
|
21
|
+
compileScriptCheckPolicy,
|
|
22
|
+
withheldTools
|
|
23
|
+
} from "./chunk-EIYM6TY7.js";
|
|
24
|
+
import {
|
|
25
|
+
resumeCommand
|
|
26
|
+
} from "./chunk-HQM2WSQT.js";
|
|
27
|
+
import {
|
|
28
|
+
appendHistory,
|
|
29
|
+
compactHistory,
|
|
30
|
+
foldHistory,
|
|
31
|
+
readHistory,
|
|
32
|
+
readState,
|
|
33
|
+
withoutUndefined,
|
|
34
|
+
writeState
|
|
35
|
+
} from "./chunk-J72CRUNX.js";
|
|
36
|
+
import {
|
|
37
|
+
startEndpoint
|
|
38
|
+
} from "./chunk-6YG7D7U4.js";
|
|
39
|
+
import {
|
|
40
|
+
SCHEDULE_FORMAT_VERSION,
|
|
41
|
+
confirmationHolds,
|
|
42
|
+
jobSecurityDigest,
|
|
43
|
+
listJobs,
|
|
44
|
+
parseScriptReport,
|
|
45
|
+
readJob,
|
|
46
|
+
readScriptState,
|
|
47
|
+
runResultVersion,
|
|
48
|
+
sanitizeLine,
|
|
49
|
+
summaryOf,
|
|
50
|
+
updateJob,
|
|
51
|
+
writeScriptState
|
|
52
|
+
} from "./chunk-SKKWMMFP.js";
|
|
53
|
+
import {
|
|
54
|
+
PRIVATE_FILE_MODE,
|
|
55
|
+
ensureDir,
|
|
56
|
+
publishExclusive,
|
|
57
|
+
readVersioned,
|
|
58
|
+
writeJsonAtomic
|
|
59
|
+
} from "./chunk-OEW7CJKK.js";
|
|
60
|
+
import {
|
|
61
|
+
readPermissionLayers
|
|
62
|
+
} from "./chunk-RC6X3ZY6.js";
|
|
63
|
+
import {
|
|
64
|
+
resolveTrustedProjectContext
|
|
65
|
+
} from "./chunk-CHIEVVQ2.js";
|
|
66
|
+
import {
|
|
67
|
+
readWslMountRoot
|
|
68
|
+
} from "./chunk-2A37JQKY.js";
|
|
69
|
+
import {
|
|
70
|
+
pauseWait
|
|
71
|
+
} from "./chunk-XEUQN4JC.js";
|
|
72
|
+
import {
|
|
73
|
+
describeTurnInterruption,
|
|
74
|
+
retryAfterMs,
|
|
75
|
+
unattendedNote
|
|
76
|
+
} from "./chunk-WM2JEUGV.js";
|
|
77
|
+
import {
|
|
78
|
+
cliLogger,
|
|
79
|
+
createStderrSink,
|
|
80
|
+
installCliLogging
|
|
81
|
+
} from "./chunk-HBBB2CAC.js";
|
|
82
|
+
import {
|
|
83
|
+
archiveConversation,
|
|
84
|
+
closeSessions,
|
|
85
|
+
openSessions,
|
|
86
|
+
readConversationFacts,
|
|
87
|
+
readIdentity,
|
|
88
|
+
setTitle,
|
|
89
|
+
startConversation
|
|
90
|
+
} from "./chunk-TIGWWAJI.js";
|
|
91
|
+
import {
|
|
92
|
+
DiskSessionLeaseStore,
|
|
93
|
+
DiskSessionLog,
|
|
94
|
+
SessionPaths,
|
|
95
|
+
TurnCancelled,
|
|
96
|
+
abandonTurn,
|
|
97
|
+
asSessionId,
|
|
98
|
+
evaluateJob,
|
|
99
|
+
execHostShell,
|
|
100
|
+
generateScheduleRunId,
|
|
101
|
+
hostTimeZone,
|
|
102
|
+
installedCommandShellForDialect,
|
|
103
|
+
isEntityId,
|
|
104
|
+
isLeaseLive,
|
|
105
|
+
jsonLinesSink,
|
|
106
|
+
readSessionLease,
|
|
107
|
+
releaseHeldSessionLeases,
|
|
108
|
+
revealHiddenCharacters,
|
|
109
|
+
scanSchedulePrompt
|
|
110
|
+
} from "./chunk-EWKJGHRO.js";
|
|
111
|
+
import {
|
|
112
|
+
__name
|
|
113
|
+
} from "./chunk-76FLAFCG.js";
|
|
114
|
+
|
|
115
|
+
// src/schedule/fire/result.ts
|
|
116
|
+
function writeRunResult(paths, result) {
|
|
117
|
+
writeJsonAtomic(paths.runResult(result.jobId, result.runId), result);
|
|
118
|
+
}
|
|
119
|
+
__name(writeRunResult, "writeRunResult");
|
|
120
|
+
function readRunResult(paths, jobId, runId) {
|
|
121
|
+
return readVersioned(
|
|
122
|
+
paths.runResult(jobId, runId),
|
|
123
|
+
"schedule-run-result",
|
|
124
|
+
SCHEDULE_FORMAT_VERSION
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
__name(readRunResult, "readRunResult");
|
|
128
|
+
function isFinal(result) {
|
|
129
|
+
return result !== void 0 && result.status !== "running";
|
|
130
|
+
}
|
|
131
|
+
__name(isFinal, "isFinal");
|
|
132
|
+
|
|
133
|
+
// src/schedule/store/claims.ts
|
|
134
|
+
import { readdirSync, rmSync, statSync } from "node:fs";
|
|
135
|
+
import { join } from "node:path";
|
|
136
|
+
function claimOccurrence(paths, claim) {
|
|
137
|
+
if (!/^(\d{1,16}|manual-[0-9a-f-]{8,64})$/.test(claim.key)) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Refusing an occurrence key that is not an instant or a manual run: ${claim.key}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return publishExclusive(paths.claim(claim.jobId, claim.key), {
|
|
143
|
+
v: 1,
|
|
144
|
+
kind: "schedule-claim",
|
|
145
|
+
...claim
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
__name(claimOccurrence, "claimOccurrence");
|
|
149
|
+
function isClaimed(paths, jobId, key) {
|
|
150
|
+
try {
|
|
151
|
+
statSync(paths.claim(jobId, key));
|
|
152
|
+
return true;
|
|
153
|
+
} catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
__name(isClaimed, "isClaimed");
|
|
158
|
+
function pruneClaims(paths, jobId, maxAgeMs, now = Date.now()) {
|
|
159
|
+
let removed = 0;
|
|
160
|
+
let names;
|
|
161
|
+
try {
|
|
162
|
+
names = readdirSync(paths.claimsOf(jobId));
|
|
163
|
+
} catch {
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
for (const name of names) {
|
|
167
|
+
const match = /^(\d{1,16})\.json$/.exec(name);
|
|
168
|
+
if (!match) continue;
|
|
169
|
+
if (now - Number(match[1]) > maxAgeMs) {
|
|
170
|
+
rmSync(join(paths.claimsOf(jobId), name), { force: true });
|
|
171
|
+
removed++;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return removed;
|
|
175
|
+
}
|
|
176
|
+
__name(pruneClaims, "pruneClaims");
|
|
177
|
+
|
|
178
|
+
// src/schedule/daemon/exit.ts
|
|
179
|
+
var EXIT_STOP_REQUESTED = 80;
|
|
180
|
+
|
|
181
|
+
// src/schedule/daemon/log.ts
|
|
182
|
+
import { appendFileSync, readdirSync as readdirSync2, rmSync as rmSync2 } from "node:fs";
|
|
183
|
+
import { join as join2 } from "node:path";
|
|
184
|
+
import { Writable } from "node:stream";
|
|
185
|
+
var KEEP_DAYS = 14;
|
|
186
|
+
function day(now) {
|
|
187
|
+
return now.toISOString().slice(0, 10);
|
|
188
|
+
}
|
|
189
|
+
__name(day, "day");
|
|
190
|
+
function daemonLogPath(dir, now = /* @__PURE__ */ new Date()) {
|
|
191
|
+
return join2(dir, `daemon-${day(now)}.jsonl`);
|
|
192
|
+
}
|
|
193
|
+
__name(daemonLogPath, "daemonLogPath");
|
|
194
|
+
function pruneDaemonLogs(dir, now = /* @__PURE__ */ new Date()) {
|
|
195
|
+
let names;
|
|
196
|
+
try {
|
|
197
|
+
names = readdirSync2(dir);
|
|
198
|
+
} catch {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const cutoff = new Date(now.getTime() - KEEP_DAYS * 24 * 60 * 60 * 1e3).toISOString().slice(0, 10);
|
|
202
|
+
for (const name of names) {
|
|
203
|
+
const match = /^daemon-(\d{4}-\d{2}-\d{2})\.jsonl$/.exec(name);
|
|
204
|
+
if (match && match[1] < cutoff) rmSync2(join2(dir, name), { force: true });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
__name(pruneDaemonLogs, "pruneDaemonLogs");
|
|
208
|
+
function daemonLogSink(dir, now = () => /* @__PURE__ */ new Date()) {
|
|
209
|
+
ensureDir(dir);
|
|
210
|
+
const stream = new Writable({
|
|
211
|
+
write(chunk, _encoding, callback) {
|
|
212
|
+
try {
|
|
213
|
+
appendFileSync(daemonLogPath(dir, now()), chunk, { mode: PRIVATE_FILE_MODE });
|
|
214
|
+
callback();
|
|
215
|
+
} catch (error) {
|
|
216
|
+
callback(error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
stream.on("error", () => {
|
|
221
|
+
});
|
|
222
|
+
return jsonLinesSink(stream);
|
|
223
|
+
}
|
|
224
|
+
__name(daemonLogSink, "daemonLogSink");
|
|
225
|
+
|
|
226
|
+
// src/schedule/daemon/sessions.ts
|
|
227
|
+
function pathsOf(ref) {
|
|
228
|
+
return new SessionPaths({ home: ref.home, slug: ref.projectSlug });
|
|
229
|
+
}
|
|
230
|
+
__name(pathsOf, "pathsOf");
|
|
231
|
+
async function sessionLeaseLive(ref, now = Date.now()) {
|
|
232
|
+
const dir = pathsOf(ref).sessionDir({ sessionId: asSessionId(ref.sessionId) });
|
|
233
|
+
try {
|
|
234
|
+
return isLeaseLive(await readSessionLease(dir), now);
|
|
235
|
+
} catch {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
__name(sessionLeaseLive, "sessionLeaseLive");
|
|
240
|
+
async function sessionFacts(ref) {
|
|
241
|
+
try {
|
|
242
|
+
return await readConversationFacts(
|
|
243
|
+
{ paths: pathsOf(ref) },
|
|
244
|
+
asSessionId(ref.sessionId),
|
|
245
|
+
"tolerant"
|
|
246
|
+
);
|
|
247
|
+
} catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
__name(sessionFacts, "sessionFacts");
|
|
252
|
+
function turnOutcome(facts, turnId) {
|
|
253
|
+
for (let i = facts.records.length - 1; i >= 0; i--) {
|
|
254
|
+
const record = facts.records[i];
|
|
255
|
+
if (turnId && record.turnId !== turnId) continue;
|
|
256
|
+
if (record.type === "turn_completed") return { status: "completed" };
|
|
257
|
+
if (record.type === "turn_failed") {
|
|
258
|
+
return {
|
|
259
|
+
status: "failed",
|
|
260
|
+
...record.error?.message ? { reason: record.error.message } : {}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return void 0;
|
|
265
|
+
}
|
|
266
|
+
__name(turnOutcome, "turnOutcome");
|
|
267
|
+
async function abandonParkedTurn(ref, turnId, reason) {
|
|
268
|
+
const log = DiskSessionLog.at(pathsOf(ref), { sessionId: asSessionId(ref.sessionId) });
|
|
269
|
+
await abandonTurn(asSessionId(ref.sessionId), turnId, reason, { log });
|
|
270
|
+
}
|
|
271
|
+
__name(abandonParkedTurn, "abandonParkedTurn");
|
|
272
|
+
|
|
273
|
+
// src/schedule/daemon/daemon.ts
|
|
274
|
+
import { spawn } from "node:child_process";
|
|
275
|
+
import {
|
|
276
|
+
closeSync,
|
|
277
|
+
existsSync,
|
|
278
|
+
openSync,
|
|
279
|
+
rmSync as rmSync3,
|
|
280
|
+
statSync as statSync4,
|
|
281
|
+
watch,
|
|
282
|
+
writeFileSync
|
|
283
|
+
} from "node:fs";
|
|
284
|
+
import { dirname, join as join4 } from "node:path";
|
|
285
|
+
|
|
286
|
+
// src/integrations/notifications/desktop/throttle.ts
|
|
287
|
+
import { readFileSync } from "node:fs";
|
|
288
|
+
var PER_JOB_MS = 10 * 6e4;
|
|
289
|
+
var PER_DAY = 20;
|
|
290
|
+
var DAY_MS = 24 * 60 * 6e4;
|
|
291
|
+
function read(path) {
|
|
292
|
+
try {
|
|
293
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
294
|
+
if (parsed.kind === "schedule-notify" && Array.isArray(parsed.sent)) return parsed;
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
return { v: 1, kind: "schedule-notify", sent: [], suppressed: 0 };
|
|
298
|
+
}
|
|
299
|
+
__name(read, "read");
|
|
300
|
+
function admitNotification(path, jobId, now = Date.now()) {
|
|
301
|
+
const file = read(path);
|
|
302
|
+
const recent = file.sent.filter((s) => now - s.at < DAY_MS);
|
|
303
|
+
const jobRecent = recent.some((s) => s.jobId === jobId && now - s.at < PER_JOB_MS);
|
|
304
|
+
if (jobRecent || recent.length >= PER_DAY) {
|
|
305
|
+
writeJsonAtomic(path, { ...file, sent: recent, suppressed: file.suppressed + 1 });
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
writeJsonAtomic(path, { ...file, sent: [...recent, { jobId, at: now }] });
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
__name(admitNotification, "admitNotification");
|
|
312
|
+
function takeSuppressedCount(path) {
|
|
313
|
+
const file = read(path);
|
|
314
|
+
if (file.suppressed > 0) writeJsonAtomic(path, { ...file, suppressed: 0 });
|
|
315
|
+
return file.suppressed;
|
|
316
|
+
}
|
|
317
|
+
__name(takeSuppressedCount, "takeSuppressedCount");
|
|
318
|
+
|
|
319
|
+
// src/schedule/delivery.ts
|
|
320
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
321
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
322
|
+
import { join as join3 } from "node:path";
|
|
323
|
+
var DELIVERY_SUMMARY_CHARS = 600;
|
|
324
|
+
var DELIVERY_REASON_CHARS = 300;
|
|
325
|
+
var DELIVERY_READ_LIMIT = 100;
|
|
326
|
+
var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
|
|
327
|
+
var FINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
328
|
+
"completed",
|
|
329
|
+
"failed",
|
|
330
|
+
"awaiting-approval",
|
|
331
|
+
"approval-expired",
|
|
332
|
+
"interrupted",
|
|
333
|
+
"timed-out",
|
|
334
|
+
"blocked-config",
|
|
335
|
+
"cancelled",
|
|
336
|
+
"check-failed"
|
|
337
|
+
]);
|
|
338
|
+
function id(value) {
|
|
339
|
+
return UUID.test(value);
|
|
340
|
+
}
|
|
341
|
+
__name(id, "id");
|
|
342
|
+
function sourcePaths(paths, source) {
|
|
343
|
+
return new SessionPaths({ home: paths.home, slug: source.projectSlug });
|
|
344
|
+
}
|
|
345
|
+
__name(sourcePaths, "sourcePaths");
|
|
346
|
+
function deliveryDir(paths, source) {
|
|
347
|
+
if (!isEntityId(source.sessionId, "session")) throw new Error("invalid source session id");
|
|
348
|
+
return join3(paths.root, "deliveries", source.sessionId);
|
|
349
|
+
}
|
|
350
|
+
__name(deliveryDir, "deliveryDir");
|
|
351
|
+
function eventKey(delivery) {
|
|
352
|
+
return createHash("sha256").update(JSON.stringify([delivery.jobId, delivery.runId, delivery.status, delivery.at])).digest("hex");
|
|
353
|
+
}
|
|
354
|
+
__name(eventKey, "eventKey");
|
|
355
|
+
function eventFile(paths, delivery) {
|
|
356
|
+
return join3(deliveryDir(paths, delivery.source), `${eventKey(delivery)}.json`);
|
|
357
|
+
}
|
|
358
|
+
__name(eventFile, "eventFile");
|
|
359
|
+
function sameSource(a, b) {
|
|
360
|
+
return a.kind === b.kind && a.sessionId === b.sessionId && a.projectSlug === b.projectSlug && a.projectId === b.projectId && a.tenantId === b.tenantId;
|
|
361
|
+
}
|
|
362
|
+
__name(sameSource, "sameSource");
|
|
363
|
+
async function sourceProblem(paths, source) {
|
|
364
|
+
if (source.kind !== "source-conversation" || !isEntityId(source.sessionId, "session") || !isEntityId(source.projectId, "project") || !isEntityId(source.tenantId, "tenant"))
|
|
365
|
+
return "the source conversation binding is invalid";
|
|
366
|
+
let sessionPaths;
|
|
367
|
+
try {
|
|
368
|
+
sessionPaths = sourcePaths(paths, source);
|
|
369
|
+
} catch {
|
|
370
|
+
return "the source project slug is invalid";
|
|
371
|
+
}
|
|
372
|
+
const identity = readIdentity(paths.home);
|
|
373
|
+
if (!identity || identity.tenantId !== source.tenantId)
|
|
374
|
+
return "the source conversation belongs to another installation";
|
|
375
|
+
let project;
|
|
376
|
+
try {
|
|
377
|
+
project = JSON.parse(readFileSync2(sessionPaths.projectFile(), "utf8"));
|
|
378
|
+
} catch {
|
|
379
|
+
return "the source project is missing or unreadable";
|
|
380
|
+
}
|
|
381
|
+
if (!project || typeof project !== "object" || project.projectId !== source.projectId || project.slug !== source.projectSlug)
|
|
382
|
+
return "the source project no longer matches its binding";
|
|
383
|
+
const facts = await readConversationFacts({ paths: sessionPaths }, asSessionId(source.sessionId));
|
|
384
|
+
if (!facts) return "the source conversation is missing";
|
|
385
|
+
if (facts.started.projectId !== source.projectId || // Legacy session_started records had no tenant. The installation identity
|
|
386
|
+
// and exact project were already checked above; an explicit different
|
|
387
|
+
// tenant in the log still rejects the binding.
|
|
388
|
+
facts.started.tenantId !== void 0 && facts.started.tenantId !== source.tenantId || facts.started.cwd !== project.cwd)
|
|
389
|
+
return "the source conversation no longer matches its binding";
|
|
390
|
+
if (facts.archived) return "the source conversation is archived";
|
|
391
|
+
return void 0;
|
|
392
|
+
}
|
|
393
|
+
__name(sourceProblem, "sourceProblem");
|
|
394
|
+
function scriptSummary(result) {
|
|
395
|
+
const stdout = result.scriptOutput?.stdout;
|
|
396
|
+
if (!stdout) return "";
|
|
397
|
+
const lines = stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
398
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
399
|
+
const line = lines[i];
|
|
400
|
+
if (!(line.startsWith("{") && line.endsWith("}"))) return line;
|
|
401
|
+
}
|
|
402
|
+
return "";
|
|
403
|
+
}
|
|
404
|
+
__name(scriptSummary, "scriptSummary");
|
|
405
|
+
function deliveryFor(job, result) {
|
|
406
|
+
const source = job.delivery;
|
|
407
|
+
if (!source || result.status === "running" || result.status === "completed" && result.scriptReport?.state === "quiet" || result.status === "completed" && result.gateResult?.wake === false)
|
|
408
|
+
return void 0;
|
|
409
|
+
const summary = sanitizeLine(
|
|
410
|
+
result.summary || result.scriptReport?.summary || scriptSummary(result),
|
|
411
|
+
DELIVERY_SUMMARY_CHARS
|
|
412
|
+
);
|
|
413
|
+
const calls = result.refusedCalls ?? result.failedCalls;
|
|
414
|
+
const callNote = calls ? `${calls.count} ${result.refusedCalls ? "refused" : "failed"} call${calls.count === 1 ? "" : "s"}; first: ${calls.first.tool}: ${calls.first.reason}` : "";
|
|
415
|
+
const reason = sanitizeLine(result.reason ?? callNote, DELIVERY_REASON_CHARS);
|
|
416
|
+
const noteworthy = result.status !== "completed" || Boolean(summary || reason || result.refusedCalls || result.failedCalls);
|
|
417
|
+
if (!noteworthy) return void 0;
|
|
418
|
+
const at = result.endedAt ?? result.startedAt;
|
|
419
|
+
return {
|
|
420
|
+
v: 1,
|
|
421
|
+
kind: "schedule-source-delivery",
|
|
422
|
+
source: { ...source },
|
|
423
|
+
jobId: job.id,
|
|
424
|
+
jobName: sanitizeLine(job.name, 80),
|
|
425
|
+
runId: result.runId,
|
|
426
|
+
...result.sessionId ? { runSessionId: result.sessionId } : {},
|
|
427
|
+
status: result.status,
|
|
428
|
+
at,
|
|
429
|
+
...summary ? { summary } : {},
|
|
430
|
+
...reason ? { reason } : {}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
__name(deliveryFor, "deliveryFor");
|
|
434
|
+
function validDelivery(value, source) {
|
|
435
|
+
return value.v === 1 && value.kind === "schedule-source-delivery" && value.source !== null && typeof value.source === "object" && sameSource(value.source, source) && id(value.jobId) && id(value.runId) && typeof value.jobName === "string" && [...value.jobName].length <= 80 && FINAL_STATUSES.has(value.status) && Number.isFinite(Date.parse(value.at)) && (value.publishedOrder === void 0 || Number.isSafeInteger(value.publishedOrder) && value.publishedOrder >= 0) && (value.runSessionId === void 0 || isEntityId(value.runSessionId, "session")) && (value.summary === void 0 || typeof value.summary === "string" && [...value.summary].length <= DELIVERY_SUMMARY_CHARS) && (value.reason === void 0 || typeof value.reason === "string" && [...value.reason].length <= DELIVERY_REASON_CHARS);
|
|
436
|
+
}
|
|
437
|
+
__name(validDelivery, "validDelivery");
|
|
438
|
+
function inboxFiles(paths, source) {
|
|
439
|
+
const dir = deliveryDir(paths, source);
|
|
440
|
+
try {
|
|
441
|
+
return {
|
|
442
|
+
dir,
|
|
443
|
+
names: readdirSync3(dir).filter((name) => /^[a-f0-9]{64}\.json$/.test(name))
|
|
444
|
+
};
|
|
445
|
+
} catch (error) {
|
|
446
|
+
if (error.code === "ENOENT") return { dir, names: [] };
|
|
447
|
+
throw error;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
__name(inboxFiles, "inboxFiles");
|
|
451
|
+
function inboxRecords(paths, source) {
|
|
452
|
+
const { dir, names } = inboxFiles(paths, source);
|
|
453
|
+
return names.map((name) => {
|
|
454
|
+
const file = join3(dir, name);
|
|
455
|
+
const entry = readVersioned(file, "schedule-source-delivery", 1);
|
|
456
|
+
if (!entry || !validDelivery(entry, source) || `${eventKey(entry)}.json` !== name)
|
|
457
|
+
throw new Error(`${file} is not a delivery for this source conversation`);
|
|
458
|
+
const age = entry.publishedOrder === void 0 ? statSync2(file) : void 0;
|
|
459
|
+
const order = entry.publishedOrder ?? (age && age.birthtimeMs > 0 ? age.birthtimeMs : age?.mtimeMs ?? 0);
|
|
460
|
+
return { entry, name, order };
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
__name(inboxRecords, "inboxRecords");
|
|
464
|
+
function compareRecord(a, b) {
|
|
465
|
+
return a.order - b.order || a.name.localeCompare(b.file);
|
|
466
|
+
}
|
|
467
|
+
__name(compareRecord, "compareRecord");
|
|
468
|
+
function nextPublicationOrder(paths, source) {
|
|
469
|
+
const { dir, names } = inboxFiles(paths, source);
|
|
470
|
+
let latest = 0;
|
|
471
|
+
for (const name of names) {
|
|
472
|
+
const file = join3(dir, name);
|
|
473
|
+
const age = statSync2(file);
|
|
474
|
+
let order = age.birthtimeMs > 0 ? age.birthtimeMs : age.mtimeMs;
|
|
475
|
+
let parsed;
|
|
476
|
+
try {
|
|
477
|
+
parsed = JSON.parse(readFileSync2(file, "utf8"));
|
|
478
|
+
} catch (error) {
|
|
479
|
+
if (error.code) throw error;
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
const entry = parsed;
|
|
483
|
+
if (entry && validDelivery(entry, source) && `${eventKey(entry)}.json` === name && entry.publishedOrder !== void 0)
|
|
484
|
+
order = entry.publishedOrder;
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
latest = Math.max(latest, order);
|
|
488
|
+
}
|
|
489
|
+
const next = Math.max(Date.now(), Math.floor(latest) + 1);
|
|
490
|
+
if (!Number.isSafeInteger(next)) throw new Error("source delivery publication order is exhausted");
|
|
491
|
+
return next;
|
|
492
|
+
}
|
|
493
|
+
__name(nextPublicationOrder, "nextPublicationOrder");
|
|
494
|
+
async function deliverRunToSource(paths, job, result) {
|
|
495
|
+
if (!job.delivery) return { kind: "quiet" };
|
|
496
|
+
if (result.jobId !== job.id || !id(job.id) || !id(result.runId))
|
|
497
|
+
return { kind: "rejected", reason: "the run does not match its source job" };
|
|
498
|
+
const delivery = deliveryFor(job, result);
|
|
499
|
+
if (!delivery) return { kind: "quiet" };
|
|
500
|
+
if (!Number.isFinite(Date.parse(delivery.at)))
|
|
501
|
+
return { kind: "rejected", reason: "the run result has no valid time" };
|
|
502
|
+
let file;
|
|
503
|
+
try {
|
|
504
|
+
file = eventFile(paths, delivery);
|
|
505
|
+
const existing = readVersioned(file, "schedule-source-delivery", 1);
|
|
506
|
+
if (existing) {
|
|
507
|
+
return validDelivery(existing, job.delivery) && eventKey(existing) === eventKey(delivery) ? { kind: "already-delivered" } : { kind: "rejected", reason: "a different delivery already owns this run event" };
|
|
508
|
+
}
|
|
509
|
+
} catch (error) {
|
|
510
|
+
return { kind: "rejected", reason: error instanceof Error ? error.message : String(error) };
|
|
511
|
+
}
|
|
512
|
+
let sessionPaths;
|
|
513
|
+
try {
|
|
514
|
+
sessionPaths = sourcePaths(paths, job.delivery);
|
|
515
|
+
const problem = await sourceProblem(paths, job.delivery);
|
|
516
|
+
if (problem) return { kind: "rejected", reason: problem };
|
|
517
|
+
} catch (error) {
|
|
518
|
+
return { kind: "rejected", reason: error instanceof Error ? error.message : String(error) };
|
|
519
|
+
}
|
|
520
|
+
const log = DiskSessionLog.at(sessionPaths, { sessionId: asSessionId(job.delivery.sessionId) });
|
|
521
|
+
let lease;
|
|
522
|
+
try {
|
|
523
|
+
lease = await log.claim({
|
|
524
|
+
holder: `namzu-schedule-delivery:${process.pid}:${randomUUID()}`,
|
|
525
|
+
ttlMs: 3e4,
|
|
526
|
+
repairTornTail: false
|
|
527
|
+
});
|
|
528
|
+
} catch (error) {
|
|
529
|
+
return { kind: "retry", reason: error instanceof Error ? error.message : String(error) };
|
|
530
|
+
}
|
|
531
|
+
if (!lease) return { kind: "retry", reason: "the source conversation is busy" };
|
|
532
|
+
try {
|
|
533
|
+
const problem = await sourceProblem(paths, job.delivery);
|
|
534
|
+
if (problem) return { kind: "rejected", reason: problem };
|
|
535
|
+
const published = {
|
|
536
|
+
...delivery,
|
|
537
|
+
publishedOrder: nextPublicationOrder(paths, job.delivery)
|
|
538
|
+
};
|
|
539
|
+
const renewed = await log.claim({
|
|
540
|
+
holder: lease.holder,
|
|
541
|
+
ttlMs: 3e4,
|
|
542
|
+
repairTornTail: false
|
|
543
|
+
});
|
|
544
|
+
if (!renewed || renewed.fence !== lease.fence)
|
|
545
|
+
return { kind: "retry", reason: "the source conversation lease changed" };
|
|
546
|
+
if (!publishExclusive(file, published)) {
|
|
547
|
+
const existing = readVersioned(file, "schedule-source-delivery", 1);
|
|
548
|
+
if (!existing || !validDelivery(existing, job.delivery) || eventKey(existing) !== eventKey(delivery))
|
|
549
|
+
return { kind: "rejected", reason: "a different delivery already owns this run event" };
|
|
550
|
+
return { kind: "already-delivered" };
|
|
551
|
+
}
|
|
552
|
+
return { kind: "delivered" };
|
|
553
|
+
} catch (error) {
|
|
554
|
+
return { kind: "retry", reason: error instanceof Error ? error.message : String(error) };
|
|
555
|
+
} finally {
|
|
556
|
+
await log.release(lease);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
__name(deliverRunToSource, "deliverRunToSource");
|
|
560
|
+
async function readSourceDeliveryPage(paths, source, after) {
|
|
561
|
+
const problem = await sourceProblem(paths, source);
|
|
562
|
+
if (problem) throw new Error(problem);
|
|
563
|
+
const records = inboxRecords(paths, source).sort((a, b) => a.order - b.order || a.name.localeCompare(b.name)).filter((record) => after === void 0 || compareRecord(record, after) > 0);
|
|
564
|
+
const page = records.slice(0, DELIVERY_READ_LIMIT);
|
|
565
|
+
const last = page.at(-1);
|
|
566
|
+
return {
|
|
567
|
+
entries: page.map((record) => record.entry),
|
|
568
|
+
...last ? { cursor: { order: last.order, file: last.name } } : after ? { cursor: after } : {},
|
|
569
|
+
hasMore: records.length > DELIVERY_READ_LIMIT
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
__name(readSourceDeliveryPage, "readSourceDeliveryPage");
|
|
573
|
+
|
|
574
|
+
// src/schedule/env.ts
|
|
575
|
+
import { readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
576
|
+
var CHILD_ENV_ALLOWLIST = [
|
|
577
|
+
"HOME",
|
|
578
|
+
"USER",
|
|
579
|
+
"LOGNAME",
|
|
580
|
+
"LANG",
|
|
581
|
+
"LC_ALL",
|
|
582
|
+
"TZ",
|
|
583
|
+
"TMPDIR",
|
|
584
|
+
"SHELL",
|
|
585
|
+
"XDG_RUNTIME_DIR",
|
|
586
|
+
"XDG_CONFIG_HOME",
|
|
587
|
+
"DBUS_SESSION_BUS_ADDRESS",
|
|
588
|
+
"WSL_DISTRO_NAME",
|
|
589
|
+
"WSL_INTEROP",
|
|
590
|
+
"SystemRoot",
|
|
591
|
+
"USERPROFILE",
|
|
592
|
+
"APPDATA",
|
|
593
|
+
"LOCALAPPDATA",
|
|
594
|
+
// A job with `browser.headed` opens a window: X11, Wayland (WSLg sets
|
|
595
|
+
// both), and the X authority file a display may need.
|
|
596
|
+
"DISPLAY",
|
|
597
|
+
"WAYLAND_DISPLAY",
|
|
598
|
+
"XAUTHORITY"
|
|
599
|
+
];
|
|
600
|
+
function stripWindowsMounts(path, mountRoot = readWslMountRoot()) {
|
|
601
|
+
const root = mountRoot.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
602
|
+
const drive = new RegExp(`^${root}[a-z](/|$)`, "i");
|
|
603
|
+
return (path ?? "").split(":").filter((entry) => entry !== "" && !drive.test(entry)).join(":");
|
|
604
|
+
}
|
|
605
|
+
__name(stripWindowsMounts, "stripWindowsMounts");
|
|
606
|
+
function childEnvironment(source, namzuHome, mountRoot) {
|
|
607
|
+
const env = {};
|
|
608
|
+
for (const key of CHILD_ENV_ALLOWLIST) if (source[key] !== void 0) env[key] = source[key];
|
|
609
|
+
env.PATH = process.platform === "win32" ? source.PATH ?? source.Path ?? "" : stripWindowsMounts(source.PATH, mountRoot);
|
|
610
|
+
if (!env.PATH) env.PATH = "/usr/local/bin:/usr/bin:/bin";
|
|
611
|
+
env.NAMZU_HOME = namzuHome;
|
|
612
|
+
return env;
|
|
613
|
+
}
|
|
614
|
+
__name(childEnvironment, "childEnvironment");
|
|
615
|
+
function readDaemonEnv(path) {
|
|
616
|
+
let text;
|
|
617
|
+
try {
|
|
618
|
+
text = readFileSync3(path, "utf8");
|
|
619
|
+
} catch {
|
|
620
|
+
return { values: {}, warnings: [] };
|
|
621
|
+
}
|
|
622
|
+
const warnings = [];
|
|
623
|
+
try {
|
|
624
|
+
const mode = statSync3(path).mode & 63;
|
|
625
|
+
if (mode !== 0 && process.platform !== "win32") {
|
|
626
|
+
warnings.push(`${path} can be read by other users; run chmod 600 on it`);
|
|
627
|
+
}
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
const values = {};
|
|
631
|
+
text.split(/\r?\n/).forEach((raw, index) => {
|
|
632
|
+
const line = raw.trim();
|
|
633
|
+
if (!line || line.startsWith("#")) return;
|
|
634
|
+
const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
|
|
635
|
+
if (!match) {
|
|
636
|
+
warnings.push(`${path}:${index + 1} is not KEY=value; ignored`);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
let value = match[2] ?? "";
|
|
640
|
+
if (value.length >= 2 && (value[0] === '"' || value[0] === "'") && value.at(-1) === value[0]) {
|
|
641
|
+
value = value.slice(1, -1);
|
|
642
|
+
}
|
|
643
|
+
values[match[1]] = value;
|
|
644
|
+
});
|
|
645
|
+
return { values, warnings };
|
|
646
|
+
}
|
|
647
|
+
__name(readDaemonEnv, "readDaemonEnv");
|
|
648
|
+
|
|
649
|
+
// src/schedule/daemon/notify.ts
|
|
650
|
+
function clock(at) {
|
|
651
|
+
return new Intl.DateTimeFormat("en-GB", {
|
|
652
|
+
weekday: "short",
|
|
653
|
+
hour: "2-digit",
|
|
654
|
+
minute: "2-digit"
|
|
655
|
+
}).format(at);
|
|
656
|
+
}
|
|
657
|
+
__name(clock, "clock");
|
|
658
|
+
var NOTICE_BODY_MAX = 200;
|
|
659
|
+
function noticeText(kind, job, extra) {
|
|
660
|
+
const title = `namzu: ${job.name}`;
|
|
661
|
+
const when = clock(extra.at);
|
|
662
|
+
const summary = job.notify.includeSummary && extra.summary ? ` \u2014 ${extra.summary}` : "";
|
|
663
|
+
switch (kind) {
|
|
664
|
+
case "finished": {
|
|
665
|
+
const refused = extra.refused;
|
|
666
|
+
if (!refused) return { title, body: `finished at ${when}${summary}` };
|
|
667
|
+
const done = `done at ${when}, but ${callsWords(refused, "refused")}`;
|
|
668
|
+
if (!job.notify.includeSummary)
|
|
669
|
+
return {
|
|
670
|
+
title,
|
|
671
|
+
body: fit(
|
|
672
|
+
`${done} (${refused.first.tool}); namzu schedule show ${job.name} says why`,
|
|
673
|
+
`${done} (${refused.first.tool})`
|
|
674
|
+
)
|
|
675
|
+
};
|
|
676
|
+
const head = `${done}: `;
|
|
677
|
+
const room = NOTICE_BODY_MAX - [...head].length - [...summary].length;
|
|
678
|
+
const reason = [...refused.first.reason];
|
|
679
|
+
const said = reason.length <= room ? refused.first.reason : `${reason.slice(0, Math.max(room - 1, 0)).join("")}\u2026`;
|
|
680
|
+
return { title, body: fit(`${head}${said}${summary}`, `${head}${said}`) };
|
|
681
|
+
}
|
|
682
|
+
case "failed":
|
|
683
|
+
return { title, body: `failed at ${when}${summary}` };
|
|
684
|
+
case "check-failed": {
|
|
685
|
+
if (!job.notify.includeSummary || !extra.reason) {
|
|
686
|
+
return {
|
|
687
|
+
title,
|
|
688
|
+
body: `check failed at ${when}; namzu schedule show ${job.name} says why`
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
const head = `check failed at ${when}: `;
|
|
692
|
+
const room = NOTICE_BODY_MAX - [...head].length;
|
|
693
|
+
const safeReason = sanitizeLine(extra.reason, 1e3);
|
|
694
|
+
const reason = [...safeReason];
|
|
695
|
+
const said = reason.length <= room ? safeReason : `${reason.slice(0, Math.max(room - 1, 0)).join("")}\u2026`;
|
|
696
|
+
return { title, body: fit(`${head}${said}`, `check failed at ${when}`) };
|
|
697
|
+
}
|
|
698
|
+
case "timed-out":
|
|
699
|
+
return { title, body: `stopped at its time limit at ${when}` };
|
|
700
|
+
case "interrupted":
|
|
701
|
+
return { title, body: `was interrupted at ${when}` };
|
|
702
|
+
case "blocked-config":
|
|
703
|
+
return {
|
|
704
|
+
title,
|
|
705
|
+
body: `could not start at ${when}: check it with namzu schedule show ${job.name}`
|
|
706
|
+
};
|
|
707
|
+
case "awaiting-approval": {
|
|
708
|
+
if (extra.handoff) {
|
|
709
|
+
const needs = `needs you (since ${when}): ${extra.handoff}`;
|
|
710
|
+
const candidates = [
|
|
711
|
+
...extra.resumeCommand ? [`${needs}; continue it: ${extra.resumeCommand}`] : [],
|
|
712
|
+
`${needs}; namzu schedule show ${job.name} says how to continue it`,
|
|
713
|
+
needs
|
|
714
|
+
];
|
|
715
|
+
const body = candidates.find((text) => [...text].length <= NOTICE_BODY_MAX);
|
|
716
|
+
return {
|
|
717
|
+
title,
|
|
718
|
+
body: body ?? `${[...needs].slice(0, NOTICE_BODY_MAX - 1).join("")}\u2026`
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
const waiting = `is waiting for your approval (since ${when})`;
|
|
722
|
+
const answer = extra.resumeCommand ? `${waiting}; answer it: ${extra.resumeCommand}` : void 0;
|
|
723
|
+
return {
|
|
724
|
+
title,
|
|
725
|
+
body: answer && [...answer].length <= NOTICE_BODY_MAX ? answer : `${waiting}; namzu schedule show ${job.name} says how to answer it`
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
case "approval-expired":
|
|
729
|
+
return { title, body: `approval expired at ${when}; the run was abandoned` };
|
|
730
|
+
case "catch-up":
|
|
731
|
+
return {
|
|
732
|
+
title,
|
|
733
|
+
body: `catch-up run for ${extra.scheduledFor ? clock(extra.scheduledFor) : "a missed time"}${extra.missed ? `, ${extra.missed} earlier runs missed` : ""}`
|
|
734
|
+
};
|
|
735
|
+
case "held":
|
|
736
|
+
return { title, body: "changed outside namzu and is on hold; confirm it with /schedule" };
|
|
737
|
+
case "needs-confirmation":
|
|
738
|
+
return { title, body: "needs confirmation before it runs; open /schedule" };
|
|
739
|
+
case "auto-paused":
|
|
740
|
+
return { title, body: `paused after ${extra.failures ?? "several"} failed runs in a row` };
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
__name(noticeText, "noticeText");
|
|
744
|
+
function fit(...candidates) {
|
|
745
|
+
const whole = candidates.find((text) => [...text].length <= NOTICE_BODY_MAX);
|
|
746
|
+
if (whole !== void 0) return whole;
|
|
747
|
+
const last = [...candidates.at(-1) ?? ""];
|
|
748
|
+
return `${last.slice(0, NOTICE_BODY_MAX - 1).join("")}\u2026`;
|
|
749
|
+
}
|
|
750
|
+
__name(fit, "fit");
|
|
751
|
+
function scriptSummaryOf(result) {
|
|
752
|
+
const stdout = result.scriptOutput?.stdout;
|
|
753
|
+
if (!stdout) return void 0;
|
|
754
|
+
const lines = stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
755
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
756
|
+
const line = lines[i];
|
|
757
|
+
const looksLikeContract = line.startsWith("{") && line.endsWith("}");
|
|
758
|
+
if (!looksLikeContract) return sanitizeLine(line, 200);
|
|
759
|
+
}
|
|
760
|
+
return void 0;
|
|
761
|
+
}
|
|
762
|
+
__name(scriptSummaryOf, "scriptSummaryOf");
|
|
763
|
+
function failureSignature(status, reason) {
|
|
764
|
+
const normal = (reason ?? "").toLowerCase().replace(/[0-9a-f]{8}-[0-9a-f-]{27,}/g, "<id>").replace(/\d+/g, "<n>").replace(/\s+/g, " ").slice(0, 160);
|
|
765
|
+
return `${status}:${normal}`;
|
|
766
|
+
}
|
|
767
|
+
__name(failureSignature, "failureSignature");
|
|
768
|
+
|
|
769
|
+
// src/schedule/daemon/retention.ts
|
|
770
|
+
function sessionsToArchive(paths, job, state) {
|
|
771
|
+
const done = new Set(state.archivedSessions ?? []);
|
|
772
|
+
const completed = foldHistory(readHistory(paths, job.id)).filter(
|
|
773
|
+
(r) => r.kind === "run" && r.status === "completed" && typeof r.sessionId === "string"
|
|
774
|
+
);
|
|
775
|
+
return completed.slice(Math.max(0, job.retention.keepSessions)).map((r) => r.sessionId).filter((id2) => !done.has(id2));
|
|
776
|
+
}
|
|
777
|
+
__name(sessionsToArchive, "sessionsToArchive");
|
|
778
|
+
async function archiveOldRuns(paths, job, state) {
|
|
779
|
+
const ids = sessionsToArchive(paths, job, state);
|
|
780
|
+
if (ids.length === 0) return [];
|
|
781
|
+
const sessions = await openSessions(job.folder.canonical, { stateRoot: paths.home });
|
|
782
|
+
const archived = [];
|
|
783
|
+
try {
|
|
784
|
+
for (const id2 of ids) {
|
|
785
|
+
try {
|
|
786
|
+
await archiveConversation(sessions, asSessionId(id2));
|
|
787
|
+
archived.push(id2);
|
|
788
|
+
} catch (error) {
|
|
789
|
+
if (error instanceof Error && /already archived/.test(error.message)) archived.push(id2);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
} finally {
|
|
793
|
+
closeSessions(sessions);
|
|
794
|
+
}
|
|
795
|
+
return archived;
|
|
796
|
+
}
|
|
797
|
+
__name(archiveOldRuns, "archiveOldRuns");
|
|
798
|
+
|
|
799
|
+
// src/schedule/daemon/daemon.ts
|
|
800
|
+
var LEASE_TTL_MS = 9e4;
|
|
801
|
+
var LEASE_RENEW_MS = 3e4;
|
|
802
|
+
var TICK_MS = 3e4;
|
|
803
|
+
var START_GRACE_MS = 12e4;
|
|
804
|
+
var SCRIPT_EXIT_MARGIN_MS = 6e4;
|
|
805
|
+
var CLAIM_RETENTION_EXTRA_MS = 24 * 60 * 60 * 1e3;
|
|
806
|
+
var HOUSEKEEPING_MS = 24 * 60 * 60 * 1e3;
|
|
807
|
+
var GAP_MS = 6e4;
|
|
808
|
+
function pendingResultKey(item) {
|
|
809
|
+
return JSON.stringify([
|
|
810
|
+
item.result.runId,
|
|
811
|
+
item.result.status,
|
|
812
|
+
item.result.endedAt,
|
|
813
|
+
item.delivery?.sessionId
|
|
814
|
+
]);
|
|
815
|
+
}
|
|
816
|
+
__name(pendingResultKey, "pendingResultKey");
|
|
817
|
+
function noSessionStartGraceMs(job) {
|
|
818
|
+
if (job.runKind !== "script" && job.runKind !== "script+agent") return START_GRACE_MS;
|
|
819
|
+
const timeout = job.script?.timeoutMs;
|
|
820
|
+
if (!timeout || !Number.isSafeInteger(timeout) || timeout <= 0) return START_GRACE_MS;
|
|
821
|
+
return Math.max(
|
|
822
|
+
START_GRACE_MS,
|
|
823
|
+
Math.min(timeout, Number.MAX_SAFE_INTEGER - SCRIPT_EXIT_MARGIN_MS) + SCRIPT_EXIT_MARGIN_MS
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
__name(noSessionStartGraceMs, "noSessionStartGraceMs");
|
|
827
|
+
var ScheduleDaemon = class {
|
|
828
|
+
static {
|
|
829
|
+
__name(this, "ScheduleDaemon");
|
|
830
|
+
}
|
|
831
|
+
#o;
|
|
832
|
+
#now;
|
|
833
|
+
#mono;
|
|
834
|
+
#leaseStore;
|
|
835
|
+
#lease = null;
|
|
836
|
+
#leaseRenewedAt = 0;
|
|
837
|
+
#endpoint;
|
|
838
|
+
#watcher;
|
|
839
|
+
/** Runs in progress: ours (with a handle) and adopted ones (without). */
|
|
840
|
+
#running = /* @__PURE__ */ new Map();
|
|
841
|
+
#stopping = false;
|
|
842
|
+
/** The stop came from `schedule stop`'s file, not a signal or the endpoint. */
|
|
843
|
+
#stopRequestedByFile = false;
|
|
844
|
+
#draining = false;
|
|
845
|
+
#standby = false;
|
|
846
|
+
#wake;
|
|
847
|
+
#lastWall = 0;
|
|
848
|
+
#lastMono = 0;
|
|
849
|
+
#startedAt = 0;
|
|
850
|
+
#observedGap;
|
|
851
|
+
#startFingerprint = "";
|
|
852
|
+
#lastHousekeeping = 0;
|
|
853
|
+
#manual = [];
|
|
854
|
+
/** Why a queued run is waiting, for its run record once it starts. */
|
|
855
|
+
#delayReasons = /* @__PURE__ */ new Map();
|
|
856
|
+
#finalizing = [];
|
|
857
|
+
/** Keep exit finalization behind a tick that still holds an older state snapshot. */
|
|
858
|
+
#stateTail = Promise.resolve();
|
|
859
|
+
constructor(options) {
|
|
860
|
+
this.#o = options;
|
|
861
|
+
this.#now = options.now ?? Date.now;
|
|
862
|
+
this.#mono = options.monotonic ?? (() => performance.now());
|
|
863
|
+
this.#leaseStore = new DiskSessionLeaseStore(options.paths.daemon);
|
|
864
|
+
}
|
|
865
|
+
get standby() {
|
|
866
|
+
return this.#standby;
|
|
867
|
+
}
|
|
868
|
+
get lease() {
|
|
869
|
+
return this.#lease;
|
|
870
|
+
}
|
|
871
|
+
/** Ask the loop to stop at its next wake. Running children are left running. */
|
|
872
|
+
stop() {
|
|
873
|
+
this.#stopping = true;
|
|
874
|
+
this.#wake?.();
|
|
875
|
+
}
|
|
876
|
+
/** Stop dispatching, wait for our own runs, then stop (an upgrade restart). */
|
|
877
|
+
drainAndRestart() {
|
|
878
|
+
this.#draining = true;
|
|
879
|
+
this.#persistManual();
|
|
880
|
+
this.#wake?.();
|
|
881
|
+
}
|
|
882
|
+
wake() {
|
|
883
|
+
this.#wake?.();
|
|
884
|
+
}
|
|
885
|
+
/** Try once to own the home (for a caller driving {@link tick} itself). */
|
|
886
|
+
async claimOwnership() {
|
|
887
|
+
this.#startFingerprint ||= this.#o.fingerprint();
|
|
888
|
+
this.#startedAt ||= this.#now();
|
|
889
|
+
const lease = await this.#leaseStore.claim({
|
|
890
|
+
holder: this.#o.epoch,
|
|
891
|
+
ttlMs: this.#o.leaseTtlMs ?? LEASE_TTL_MS,
|
|
892
|
+
now: this.#now()
|
|
893
|
+
});
|
|
894
|
+
if (lease) {
|
|
895
|
+
this.#lease = lease;
|
|
896
|
+
this.#leaseRenewedAt = this.#now();
|
|
897
|
+
}
|
|
898
|
+
return lease !== null;
|
|
899
|
+
}
|
|
900
|
+
/** Give the home up (a caller driving {@link tick} itself). */
|
|
901
|
+
async releaseOwnership() {
|
|
902
|
+
if (this.#lease) await this.#leaseStore.release(this.#lease);
|
|
903
|
+
this.#lease = null;
|
|
904
|
+
}
|
|
905
|
+
/** Run until stopped. Returns the process exit code. */
|
|
906
|
+
async run() {
|
|
907
|
+
ensureDir(this.#o.paths.daemon);
|
|
908
|
+
this.#startedAt = this.#now();
|
|
909
|
+
this.#startFingerprint = this.#o.fingerprint();
|
|
910
|
+
for (; ; ) {
|
|
911
|
+
const acquired = await this.#acquire();
|
|
912
|
+
if (acquired === "exit75") return 75;
|
|
913
|
+
if (acquired === "stopped") return this.#stopRequestedByFile ? EXIT_STOP_REQUESTED : 0;
|
|
914
|
+
const outcome = await this.#own();
|
|
915
|
+
if (outcome === "standby") continue;
|
|
916
|
+
return this.#stopRequestedByFile ? EXIT_STOP_REQUESTED : 0;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
async #acquire() {
|
|
920
|
+
let announced = false;
|
|
921
|
+
for (; ; ) {
|
|
922
|
+
if (this.#stopRequested()) this.#stopping = true;
|
|
923
|
+
if (this.#stopping) return "stopped";
|
|
924
|
+
const lease = await this.#leaseStore.claim({
|
|
925
|
+
holder: this.#o.epoch,
|
|
926
|
+
ttlMs: this.#o.leaseTtlMs ?? LEASE_TTL_MS,
|
|
927
|
+
now: this.#now()
|
|
928
|
+
});
|
|
929
|
+
if (lease) {
|
|
930
|
+
this.#lease = lease;
|
|
931
|
+
this.#leaseRenewedAt = this.#now();
|
|
932
|
+
this.#standby = false;
|
|
933
|
+
this.#o.log.info("scheduler owns this home", {
|
|
934
|
+
"namzu.schedule.epoch": this.#o.epoch,
|
|
935
|
+
"namzu.schedule.fence": lease.fence
|
|
936
|
+
});
|
|
937
|
+
return "owned";
|
|
938
|
+
}
|
|
939
|
+
if (this.#o.onceOrExit) {
|
|
940
|
+
this.#o.log.warn("another scheduler owns this home", {
|
|
941
|
+
"namzu.schedule.epoch": this.#o.epoch
|
|
942
|
+
});
|
|
943
|
+
return "exit75";
|
|
944
|
+
}
|
|
945
|
+
this.#standby = true;
|
|
946
|
+
if (!announced) {
|
|
947
|
+
announced = true;
|
|
948
|
+
this.#o.log.info("scheduler on standby", { "namzu.schedule.epoch": this.#o.epoch });
|
|
949
|
+
}
|
|
950
|
+
await this.#sleep(this.#o.standbyPollMs ?? LEASE_RENEW_MS);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
async #own() {
|
|
954
|
+
this.#endpoint = await startEndpoint(this.#o.paths.endpoint, this.#o.epoch, {
|
|
955
|
+
status: /* @__PURE__ */ __name(() => this.status(), "status"),
|
|
956
|
+
reload: /* @__PURE__ */ __name(() => this.wake(), "reload"),
|
|
957
|
+
runNow: /* @__PURE__ */ __name((jobId) => this.requestRunNow(jobId), "runNow"),
|
|
958
|
+
stop: /* @__PURE__ */ __name(() => this.stop(), "stop"),
|
|
959
|
+
drainAndRestart: /* @__PURE__ */ __name(() => this.drainAndRestart(), "drainAndRestart")
|
|
960
|
+
});
|
|
961
|
+
if (this.#o.watchJobs !== false) {
|
|
962
|
+
try {
|
|
963
|
+
ensureDir(this.#o.paths.jobs);
|
|
964
|
+
this.#watcher = watch(this.#o.paths.jobs, () => this.wake());
|
|
965
|
+
this.#watcher.on("error", () => {
|
|
966
|
+
});
|
|
967
|
+
} catch {
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
this.#lastWall = this.#now();
|
|
971
|
+
this.#lastMono = this.#mono();
|
|
972
|
+
try {
|
|
973
|
+
for (; ; ) {
|
|
974
|
+
if (this.#stopRequested()) this.#stopping = true;
|
|
975
|
+
if (this.#stopping) return "stopped";
|
|
976
|
+
if (!await this.#holdLease()) {
|
|
977
|
+
this.#o.log.warn("scheduler lost its lease; standing by", {
|
|
978
|
+
"namzu.schedule.epoch": this.#o.epoch
|
|
979
|
+
});
|
|
980
|
+
return "standby";
|
|
981
|
+
}
|
|
982
|
+
await this.tick();
|
|
983
|
+
if (this.#draining && this.#ownRuns() === 0) {
|
|
984
|
+
this.#o.log.info("scheduler drained for a restart", {
|
|
985
|
+
"namzu.schedule.epoch": this.#o.epoch
|
|
986
|
+
});
|
|
987
|
+
this.#stopping = true;
|
|
988
|
+
return "stopped";
|
|
989
|
+
}
|
|
990
|
+
await this.#sleep(this.#nextSleep());
|
|
991
|
+
}
|
|
992
|
+
} finally {
|
|
993
|
+
this.#watcher?.close();
|
|
994
|
+
await this.#endpoint?.close();
|
|
995
|
+
this.#endpoint = void 0;
|
|
996
|
+
await this.#stateTail;
|
|
997
|
+
await Promise.allSettled(this.#finalizing);
|
|
998
|
+
if (this.#stopping) this.#persistManual();
|
|
999
|
+
if (this.#lease && this.#stopping) {
|
|
1000
|
+
await this.#leaseStore.release(this.#lease).catch(() => void 0);
|
|
1001
|
+
this.#lease = null;
|
|
1002
|
+
}
|
|
1003
|
+
this.#heartbeat();
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* `schedule stop` and `uninstall` leave `daemon/stop.json`; `start` and
|
|
1008
|
+
* `install` remove it. It reaches a daemon the service manager cannot: one
|
|
1009
|
+
* on standby (it has no endpoint), or one under WSL, whose Windows task
|
|
1010
|
+
* ending does not end the Linux process. A daemon that finds it exits
|
|
1011
|
+
* {@link EXIT_STOP_REQUESTED}.
|
|
1012
|
+
*/
|
|
1013
|
+
#stopRequested() {
|
|
1014
|
+
if (!existsSync(stopRequestPath(this.#o.paths))) return false;
|
|
1015
|
+
this.#stopRequestedByFile = true;
|
|
1016
|
+
this.#o.log.info("scheduler stop requested", { "namzu.schedule.epoch": this.#o.epoch });
|
|
1017
|
+
return true;
|
|
1018
|
+
}
|
|
1019
|
+
#ownRuns() {
|
|
1020
|
+
let n = 0;
|
|
1021
|
+
for (const t of this.#running.values()) if (t.run) n++;
|
|
1022
|
+
return n;
|
|
1023
|
+
}
|
|
1024
|
+
/** Renew when due; true while this daemon's fence is still the highest. */
|
|
1025
|
+
async #holdLease() {
|
|
1026
|
+
const lease = this.#lease;
|
|
1027
|
+
if (!lease) return false;
|
|
1028
|
+
if (this.#now() - this.#leaseRenewedAt >= LEASE_RENEW_MS) {
|
|
1029
|
+
const renewed = await this.#leaseStore.claim(
|
|
1030
|
+
{ holder: this.#o.epoch, ttlMs: this.#o.leaseTtlMs ?? LEASE_TTL_MS, now: this.#now() },
|
|
1031
|
+
{ renew: lease }
|
|
1032
|
+
);
|
|
1033
|
+
if (!renewed) {
|
|
1034
|
+
this.#lease = null;
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
this.#lease = renewed;
|
|
1038
|
+
this.#leaseRenewedAt = this.#now();
|
|
1039
|
+
}
|
|
1040
|
+
return this.#stillOwner();
|
|
1041
|
+
}
|
|
1042
|
+
async #stillOwner() {
|
|
1043
|
+
const lease = this.#lease;
|
|
1044
|
+
return lease !== null && await this.#leaseStore.fence() === lease.fence;
|
|
1045
|
+
}
|
|
1046
|
+
#sleep(ms) {
|
|
1047
|
+
return new Promise((resolve) => {
|
|
1048
|
+
const timer = setTimeout(
|
|
1049
|
+
() => {
|
|
1050
|
+
this.#wake = void 0;
|
|
1051
|
+
resolve();
|
|
1052
|
+
},
|
|
1053
|
+
Math.max(10, ms)
|
|
1054
|
+
);
|
|
1055
|
+
this.#wake = () => {
|
|
1056
|
+
clearTimeout(timer);
|
|
1057
|
+
this.#wake = void 0;
|
|
1058
|
+
resolve();
|
|
1059
|
+
};
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
#nextSleep() {
|
|
1063
|
+
const tick = this.#o.tickMs ?? TICK_MS;
|
|
1064
|
+
let soonest = this.#now() + tick;
|
|
1065
|
+
for (const job of listJobs(this.#o.paths).jobs) {
|
|
1066
|
+
try {
|
|
1067
|
+
const next = readState(this.#o.paths, job.id).nextFireAt;
|
|
1068
|
+
if (next) soonest = Math.min(soonest, Date.parse(next) + 50);
|
|
1069
|
+
} catch {
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return Math.max(50, Math.min(tick, soonest - this.#now()));
|
|
1073
|
+
}
|
|
1074
|
+
/** What the endpoint's `status` reports. */
|
|
1075
|
+
status() {
|
|
1076
|
+
return {
|
|
1077
|
+
epoch: this.#o.epoch,
|
|
1078
|
+
pid: process.pid,
|
|
1079
|
+
version: this.#o.version,
|
|
1080
|
+
fence: this.#lease?.fence,
|
|
1081
|
+
standby: this.#standby,
|
|
1082
|
+
draining: this.#draining,
|
|
1083
|
+
runs: [...this.#running.values()].map((t) => ({
|
|
1084
|
+
jobId: t.jobId,
|
|
1085
|
+
runId: t.runId,
|
|
1086
|
+
adopted: t.run === void 0
|
|
1087
|
+
})),
|
|
1088
|
+
notifications: this.#o.notificationBackend ?? "unknown"
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
/** Queue a manual run (`schedule run-now`). Refused while the job has a run. */
|
|
1092
|
+
requestRunNow(jobId) {
|
|
1093
|
+
const job = readJob(this.#o.paths, jobId);
|
|
1094
|
+
if (!job) return { ok: false, message: `no job ${jobId}` };
|
|
1095
|
+
if (job.state !== "active" || !confirmationHolds(job)) {
|
|
1096
|
+
return {
|
|
1097
|
+
ok: false,
|
|
1098
|
+
message: `${job.name} is ${job.state === "active" ? "awaiting confirmation" : job.state}`
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
const state = readState(this.#o.paths, job.id);
|
|
1102
|
+
if (state.activeRun || state.queued || this.#manual.some((m) => m.jobId === job.id)) {
|
|
1103
|
+
return { ok: false, message: `${job.name} already has a run` };
|
|
1104
|
+
}
|
|
1105
|
+
this.#manual.push({ jobId: job.id, runId: generateScheduleRunId() });
|
|
1106
|
+
if (this.#draining) {
|
|
1107
|
+
this.#persistManual();
|
|
1108
|
+
return {
|
|
1109
|
+
ok: true,
|
|
1110
|
+
message: `${job.name} queued; the scheduler is restarting for an upgrade and starts it once it is back`
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
this.wake();
|
|
1114
|
+
return { ok: true, message: `${job.name} queued` };
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Move every manual run still waiting in memory (deferred by the cap or a
|
|
1118
|
+
* busy folder lane) to `state.queued` on disk. A daemon that stops
|
|
1119
|
+
* dispatching (a drain) or exits keeps no memory, and the CLI has already
|
|
1120
|
+
* told the operator the run is queued; the daemon that takes over starts
|
|
1121
|
+
* it from disk under the same run id.
|
|
1122
|
+
*/
|
|
1123
|
+
#persistManual() {
|
|
1124
|
+
for (const manual of this.#manual.splice(0)) {
|
|
1125
|
+
try {
|
|
1126
|
+
const state = readState(this.#o.paths, manual.jobId);
|
|
1127
|
+
if (state.activeRun || state.queued) {
|
|
1128
|
+
this.#o.log.warn("manual run dropped; the job already has a run", {
|
|
1129
|
+
"namzu.schedule.job_id": manual.jobId,
|
|
1130
|
+
"namzu.schedule.run_id": manual.runId
|
|
1131
|
+
});
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
const at = new Date(this.#now()).toISOString();
|
|
1135
|
+
writeState(this.#o.paths, {
|
|
1136
|
+
...state,
|
|
1137
|
+
queued: {
|
|
1138
|
+
key: `${MANUAL_KEY_PREFIX}${manual.runId}`,
|
|
1139
|
+
scheduledFor: at,
|
|
1140
|
+
trigger: "manual",
|
|
1141
|
+
queuedAt: at
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
this.#o.log.error("manual run could not be queued on disk", {
|
|
1146
|
+
"namzu.schedule.job_id": manual.jobId,
|
|
1147
|
+
"namzu.schedule.run_id": manual.runId,
|
|
1148
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
#heartbeat() {
|
|
1154
|
+
try {
|
|
1155
|
+
writeJsonAtomic(this.#o.paths.heartbeat, {
|
|
1156
|
+
v: 1,
|
|
1157
|
+
kind: "schedule-heartbeat",
|
|
1158
|
+
at: new Date(this.#now()).toISOString(),
|
|
1159
|
+
pid: process.pid,
|
|
1160
|
+
epoch: this.#o.epoch,
|
|
1161
|
+
version: this.#o.version,
|
|
1162
|
+
standby: this.#standby,
|
|
1163
|
+
draining: this.#draining,
|
|
1164
|
+
runsInFlight: this.#running.size,
|
|
1165
|
+
notifications: this.#o.notificationBackend ?? "unknown"
|
|
1166
|
+
});
|
|
1167
|
+
} catch {
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
#observeClock() {
|
|
1171
|
+
const wall = this.#now();
|
|
1172
|
+
const mono = this.#mono();
|
|
1173
|
+
const wallDelta = wall - this.#lastWall;
|
|
1174
|
+
const monoDelta = mono - this.#lastMono;
|
|
1175
|
+
if (wallDelta - monoDelta > GAP_MS) {
|
|
1176
|
+
this.#observedGap = {
|
|
1177
|
+
from: new Date(this.#lastWall),
|
|
1178
|
+
to: new Date(wall),
|
|
1179
|
+
kind: monoDelta < wallDelta / 2 ? "asleep" : "clock-forward"
|
|
1180
|
+
};
|
|
1181
|
+
this.#o.log.info("scheduler observed a clock gap", {
|
|
1182
|
+
"namzu.schedule.gap_ms": wallDelta - monoDelta,
|
|
1183
|
+
"namzu.schedule.gap_kind": this.#observedGap.kind
|
|
1184
|
+
});
|
|
1185
|
+
} else if (monoDelta - wallDelta > GAP_MS) {
|
|
1186
|
+
this.#observedGap = {
|
|
1187
|
+
from: new Date(this.#lastWall),
|
|
1188
|
+
to: new Date(wall),
|
|
1189
|
+
kind: "clock-backward"
|
|
1190
|
+
};
|
|
1191
|
+
this.#o.log.info("scheduler observed the clock going back", {
|
|
1192
|
+
"namzu.schedule.gap_ms": monoDelta - wallDelta
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
this.#lastWall = wall;
|
|
1196
|
+
this.#lastMono = mono;
|
|
1197
|
+
}
|
|
1198
|
+
/** One pass: every job evaluated, the queue dispatched. Public for tests. */
|
|
1199
|
+
tick() {
|
|
1200
|
+
return this.#serializeState(() => this.#tick());
|
|
1201
|
+
}
|
|
1202
|
+
#serializeState(operation) {
|
|
1203
|
+
const result = this.#stateTail.then(operation);
|
|
1204
|
+
this.#stateTail = result.then(
|
|
1205
|
+
() => void 0,
|
|
1206
|
+
() => void 0
|
|
1207
|
+
);
|
|
1208
|
+
return result;
|
|
1209
|
+
}
|
|
1210
|
+
async #tick() {
|
|
1211
|
+
this.#observeClock();
|
|
1212
|
+
if (!this.#draining && this.#o.fingerprint() !== this.#startFingerprint) {
|
|
1213
|
+
this.#o.log.info("installed CLI changed; draining for a restart", {
|
|
1214
|
+
"namzu.schedule.epoch": this.#o.epoch
|
|
1215
|
+
});
|
|
1216
|
+
this.#draining = true;
|
|
1217
|
+
this.#persistManual();
|
|
1218
|
+
}
|
|
1219
|
+
const now = this.#now();
|
|
1220
|
+
const { jobs, errors } = listJobs(this.#o.paths);
|
|
1221
|
+
for (const error of errors) {
|
|
1222
|
+
this.#o.log.warn("scheduled job file unreadable", {
|
|
1223
|
+
"namzu.schedule.path": error.path,
|
|
1224
|
+
"exception.message": error.message
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
for (const job of jobs) {
|
|
1228
|
+
try {
|
|
1229
|
+
await this.#tickJob(job, now);
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
this.#o.log.error("scheduled job evaluation failed", {
|
|
1232
|
+
"namzu.schedule.job_id": job.id,
|
|
1233
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
this.#observedGap = void 0;
|
|
1238
|
+
if (!this.#draining) await this.#dispatch(now);
|
|
1239
|
+
if (this.#draining) this.#persistManual();
|
|
1240
|
+
if (now - this.#lastHousekeeping > HOUSEKEEPING_MS) {
|
|
1241
|
+
this.#lastHousekeeping = now;
|
|
1242
|
+
for (const job of jobs) {
|
|
1243
|
+
try {
|
|
1244
|
+
compactHistory(this.#o.paths, job.id, now);
|
|
1245
|
+
pruneClaims(this.#o.paths, job.id, job.catchUp.windowMs + CLAIM_RETENTION_EXTRA_MS, now);
|
|
1246
|
+
} catch {
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
pruneDaemonLogs(this.#o.paths.daemonLog, new Date(now));
|
|
1250
|
+
}
|
|
1251
|
+
this.#heartbeat();
|
|
1252
|
+
}
|
|
1253
|
+
async #notice(kind, job, extra) {
|
|
1254
|
+
if (!this.#o.notifications) return;
|
|
1255
|
+
const flags = job.notify;
|
|
1256
|
+
const wanted = kind === "finished" ? flags.finished : kind === "awaiting-approval" ? flags.awaitingApproval : kind === "catch-up" ? flags.finished : flags.failed || kind === "held" || kind === "needs-confirmation";
|
|
1257
|
+
if (!wanted) return;
|
|
1258
|
+
const always = kind === "held" || kind === "needs-confirmation" || kind === "auto-paused" || kind === "awaiting-approval" || kind === "approval-expired";
|
|
1259
|
+
if (!always && !admitNotification(join4(this.#o.paths.daemon, "notify.json"), job.id, this.#now()))
|
|
1260
|
+
return;
|
|
1261
|
+
const text = noticeText(kind, job, extra);
|
|
1262
|
+
await this.#o.notify({ jobId: job.id, ...text });
|
|
1263
|
+
this.#o.log.info("scheduled run notification", {
|
|
1264
|
+
"namzu.schedule.job_id": job.id,
|
|
1265
|
+
"namzu.schedule.notice": kind
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
async #tickJob(loaded, now) {
|
|
1269
|
+
const paths = this.#o.paths;
|
|
1270
|
+
let job = loaded;
|
|
1271
|
+
let state = readState(paths, job.id);
|
|
1272
|
+
if (job.state === "active" && !confirmationHolds(job)) {
|
|
1273
|
+
job = updateJob(paths, job.id, job.revision, (j) => ({ ...j, state: "pending-confirmation" }));
|
|
1274
|
+
appendHistory(paths, job.id, {
|
|
1275
|
+
v: 1,
|
|
1276
|
+
kind: "job",
|
|
1277
|
+
at: new Date(now).toISOString(),
|
|
1278
|
+
action: "tampered",
|
|
1279
|
+
by: "daemon",
|
|
1280
|
+
detail: "the job file changed outside namzu; it waits for confirmation"
|
|
1281
|
+
});
|
|
1282
|
+
state = { ...state, lastHoldNotice: jobSecurityDigest(job) };
|
|
1283
|
+
writeState(paths, state);
|
|
1284
|
+
await this.#notice("held", job, { at: new Date(now) });
|
|
1285
|
+
} else if (job.state === "pending-confirmation") {
|
|
1286
|
+
const digest = jobSecurityDigest(job);
|
|
1287
|
+
if (state.lastHoldNotice !== digest) {
|
|
1288
|
+
state = { ...state, lastHoldNotice: digest };
|
|
1289
|
+
writeState(paths, state);
|
|
1290
|
+
await this.#notice("needs-confirmation", job, { at: new Date(now) });
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
state = await this.#reconcile(job, state, now);
|
|
1294
|
+
job = readJob(paths, job.id) ?? job;
|
|
1295
|
+
state = await this.#deliverPending(job, state);
|
|
1296
|
+
if (this.#draining) return;
|
|
1297
|
+
const evaluationState = {
|
|
1298
|
+
...state.lastEvaluatedAt ? { lastEvaluatedAt: state.lastEvaluatedAt } : {},
|
|
1299
|
+
...state.jobRevision !== void 0 ? { jobRevision: state.jobRevision } : {},
|
|
1300
|
+
...state.activeRun ? { activeRun: { status: state.activeRun.status } } : state.queued || this.#manual.some((m) => m.jobId === job.id) ? { activeRun: { status: "queued" } } : {},
|
|
1301
|
+
...state.quotaHoldUntil ? { quotaHoldUntil: state.quotaHoldUntil } : {}
|
|
1302
|
+
};
|
|
1303
|
+
const decision = evaluateJob({
|
|
1304
|
+
job: {
|
|
1305
|
+
spec: job.schedule,
|
|
1306
|
+
state: job.state,
|
|
1307
|
+
catchUp: job.catchUp,
|
|
1308
|
+
createdAt: job.createdAt,
|
|
1309
|
+
updatedAt: job.updatedAt,
|
|
1310
|
+
revision: job.revision
|
|
1311
|
+
},
|
|
1312
|
+
state: evaluationState,
|
|
1313
|
+
now: new Date(now),
|
|
1314
|
+
daemon: {
|
|
1315
|
+
startedAt: new Date(this.#startedAt),
|
|
1316
|
+
...this.#observedGap ? { observedGap: this.#observedGap } : {}
|
|
1317
|
+
},
|
|
1318
|
+
isClaimed: /* @__PURE__ */ __name((key) => isClaimed(paths, job.id, key), "isClaimed")
|
|
1319
|
+
});
|
|
1320
|
+
const at = new Date(now).toISOString();
|
|
1321
|
+
for (const skip of decision.skip) {
|
|
1322
|
+
appendHistory(paths, job.id, {
|
|
1323
|
+
v: 1,
|
|
1324
|
+
kind: "skip",
|
|
1325
|
+
at,
|
|
1326
|
+
scheduledFor: skip.scheduledFor.toISOString(),
|
|
1327
|
+
reason: skip.reason,
|
|
1328
|
+
count: skip.count
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
if (decision.missed) {
|
|
1332
|
+
appendHistory(paths, job.id, {
|
|
1333
|
+
v: 1,
|
|
1334
|
+
kind: "missed",
|
|
1335
|
+
at,
|
|
1336
|
+
from: decision.missed.from.toISOString(),
|
|
1337
|
+
to: decision.missed.to.toISOString(),
|
|
1338
|
+
count: decision.missed.count,
|
|
1339
|
+
...decision.missed.capped ? { capped: true } : {},
|
|
1340
|
+
reason: decision.missed.reason,
|
|
1341
|
+
...decision.fire ? { caughtUpBy: decision.fire.key } : {}
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
let next = withoutUndefined({
|
|
1345
|
+
...state,
|
|
1346
|
+
lastEvaluatedAt: decision.nextState.lastEvaluatedAt.toISOString(),
|
|
1347
|
+
jobRevision: decision.nextState.jobRevision,
|
|
1348
|
+
nextFireAt: decision.nextState.nextFireAt?.toISOString(),
|
|
1349
|
+
quotaHoldUntil: state.quotaHoldUntil && Date.parse(state.quotaHoldUntil) > now ? state.quotaHoldUntil : void 0
|
|
1350
|
+
});
|
|
1351
|
+
if (decision.fire) {
|
|
1352
|
+
next = {
|
|
1353
|
+
...next,
|
|
1354
|
+
queued: {
|
|
1355
|
+
key: decision.fire.key,
|
|
1356
|
+
scheduledFor: decision.fire.scheduledFor.toISOString(),
|
|
1357
|
+
trigger: decision.fire.trigger,
|
|
1358
|
+
queuedAt: at
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
if (decision.fire.trigger === "catch-up") {
|
|
1362
|
+
await this.#notice("catch-up", job, {
|
|
1363
|
+
at: new Date(now),
|
|
1364
|
+
scheduledFor: decision.fire.scheduledFor,
|
|
1365
|
+
...decision.missed ? { missed: decision.missed.count } : {}
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
writeState(paths, next);
|
|
1370
|
+
if (decision.jobTransition && decision.jobTransition !== job.state) {
|
|
1371
|
+
const transition = decision.jobTransition;
|
|
1372
|
+
if (transition === "expired" || !decision.fire) {
|
|
1373
|
+
updateJob(paths, job.id, job.revision, (j) => ({ ...j, state: transition }));
|
|
1374
|
+
appendHistory(paths, job.id, { v: 1, kind: "job", at, action: transition, by: "daemon" });
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Record how a run this daemon did not start has ended — a foreground
|
|
1380
|
+
* `schedule run-now` — exactly as a run it started is recorded. False,
|
|
1381
|
+
* and nothing written, when the job's state no longer names that run (a
|
|
1382
|
+
* daemon that adopted it recorded it first).
|
|
1383
|
+
*/
|
|
1384
|
+
async finalizeRun(jobId, runId, result) {
|
|
1385
|
+
const recorded = await this.#serializeState(async () => {
|
|
1386
|
+
const job = readJob(this.#o.paths, jobId);
|
|
1387
|
+
const run = readState(this.#o.paths, jobId).activeRun;
|
|
1388
|
+
if (!job || run?.runId !== runId) return false;
|
|
1389
|
+
this.#finalize(job, run, result, this.#now());
|
|
1390
|
+
await this.#deliverPending(job, readState(this.#o.paths, job.id));
|
|
1391
|
+
return true;
|
|
1392
|
+
});
|
|
1393
|
+
await this.settled();
|
|
1394
|
+
return recorded;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Settle the job's run in progress if its process is gone, as a tick
|
|
1398
|
+
* would, without evaluating the schedule. For a foreground `run-now`
|
|
1399
|
+
* finding a run no scheduler is left to settle.
|
|
1400
|
+
*/
|
|
1401
|
+
async reconcileJob(jobId) {
|
|
1402
|
+
const state = await this.#serializeState(async () => {
|
|
1403
|
+
const job = readJob(this.#o.paths, jobId);
|
|
1404
|
+
if (!job) return void 0;
|
|
1405
|
+
const reconciled = await this.#reconcile(job, readState(this.#o.paths, jobId), this.#now());
|
|
1406
|
+
return this.#deliverPending(job, reconciled);
|
|
1407
|
+
});
|
|
1408
|
+
await this.settled();
|
|
1409
|
+
return state;
|
|
1410
|
+
}
|
|
1411
|
+
/** Retry durable source deliveries after a busy source turn or daemon restart. */
|
|
1412
|
+
async #deliverPending(job, state) {
|
|
1413
|
+
const pending = state.deliveryPending ?? [];
|
|
1414
|
+
if (pending.length === 0) return state;
|
|
1415
|
+
const handled = /* @__PURE__ */ new Set();
|
|
1416
|
+
for (const item of pending.slice(0, 20)) {
|
|
1417
|
+
const waived = Boolean(
|
|
1418
|
+
item.delivery && !job.delivery && confirmationHolds(job) && job.deliveryWaiverRunIds?.includes(item.result.runId)
|
|
1419
|
+
);
|
|
1420
|
+
const outcome = waived ? { kind: "quiet" } : item.delivery ? await deliverRunToSource(
|
|
1421
|
+
this.#o.paths,
|
|
1422
|
+
{ ...job, delivery: item.delivery },
|
|
1423
|
+
item.result
|
|
1424
|
+
) : { kind: "quiet" };
|
|
1425
|
+
if (outcome.kind === "retry") continue;
|
|
1426
|
+
if (outcome.kind === "rejected") {
|
|
1427
|
+
const latest2 = readState(this.#o.paths, job.id);
|
|
1428
|
+
const key = pendingResultKey(item);
|
|
1429
|
+
if (latest2.lastDeliveryIssue?.key !== key) {
|
|
1430
|
+
this.#o.log.warn("scheduled result could not reach its source conversation", {
|
|
1431
|
+
"namzu.schedule.job_id": job.id,
|
|
1432
|
+
"namzu.schedule.run_id": item.result.runId,
|
|
1433
|
+
"exception.message": outcome.reason
|
|
1434
|
+
});
|
|
1435
|
+
writeState(this.#o.paths, {
|
|
1436
|
+
...latest2,
|
|
1437
|
+
lastDeliveryIssue: { key, reason: outcome.reason }
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
continue;
|
|
1441
|
+
}
|
|
1442
|
+
const nextState = item.result.scriptReport?.nextState;
|
|
1443
|
+
if (nextState !== void 0) {
|
|
1444
|
+
const revision = item.result.scriptStateRevision;
|
|
1445
|
+
if (revision === void 0) {
|
|
1446
|
+
this.#o.log.error("scheduled script state revision is missing", {
|
|
1447
|
+
"namzu.schedule.job_id": job.id,
|
|
1448
|
+
"namzu.schedule.run_id": item.result.runId
|
|
1449
|
+
});
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
try {
|
|
1453
|
+
const written = writeScriptState(
|
|
1454
|
+
this.#o.paths,
|
|
1455
|
+
job.id,
|
|
1456
|
+
revision,
|
|
1457
|
+
item.result.runId,
|
|
1458
|
+
nextState
|
|
1459
|
+
);
|
|
1460
|
+
if (written.kind === "conflict") {
|
|
1461
|
+
this.#o.log.error("scheduled script state changed before its result settled", {
|
|
1462
|
+
"namzu.schedule.job_id": job.id,
|
|
1463
|
+
"namzu.schedule.run_id": item.result.runId
|
|
1464
|
+
});
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1467
|
+
} catch (error) {
|
|
1468
|
+
this.#o.log.error("scheduled script state could not be saved", {
|
|
1469
|
+
"namzu.schedule.job_id": job.id,
|
|
1470
|
+
"namzu.schedule.run_id": item.result.runId,
|
|
1471
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
1472
|
+
});
|
|
1473
|
+
continue;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
if (waived && !readHistory(this.#o.paths, job.id).some(
|
|
1477
|
+
(record) => record.kind === "job" && record.action === "delivery-waived" && record.detail === item.result.runId
|
|
1478
|
+
)) {
|
|
1479
|
+
appendHistory(this.#o.paths, job.id, {
|
|
1480
|
+
v: 1,
|
|
1481
|
+
kind: "job",
|
|
1482
|
+
at: new Date(this.#now()).toISOString(),
|
|
1483
|
+
action: "delivery-waived",
|
|
1484
|
+
by: "operator",
|
|
1485
|
+
detail: item.result.runId
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
handled.add(pendingResultKey(item));
|
|
1489
|
+
}
|
|
1490
|
+
if (handled.size === 0) return readState(this.#o.paths, job.id);
|
|
1491
|
+
const latest = readState(this.#o.paths, job.id);
|
|
1492
|
+
const remaining = (latest.deliveryPending ?? []).filter(
|
|
1493
|
+
(item) => !handled.has(pendingResultKey(item))
|
|
1494
|
+
);
|
|
1495
|
+
const next = withoutUndefined({
|
|
1496
|
+
...latest,
|
|
1497
|
+
deliveryPending: remaining.length > 0 ? remaining : void 0,
|
|
1498
|
+
lastDeliveryIssue: latest.lastDeliveryIssue && handled.has(latest.lastDeliveryIssue.key) ? void 0 : latest.lastDeliveryIssue
|
|
1499
|
+
});
|
|
1500
|
+
writeState(this.#o.paths, next);
|
|
1501
|
+
return next;
|
|
1502
|
+
}
|
|
1503
|
+
/** Finalise or adopt the job's run in progress, if it has one. */
|
|
1504
|
+
async #reconcile(job, state, now) {
|
|
1505
|
+
for (const [runId, tracked2] of this.#running) {
|
|
1506
|
+
if (tracked2.jobId === job.id && !tracked2.run && state.activeRun?.runId !== runId)
|
|
1507
|
+
this.#running.delete(runId);
|
|
1508
|
+
}
|
|
1509
|
+
const run = state.activeRun;
|
|
1510
|
+
if (!run) return state;
|
|
1511
|
+
const tracked = this.#running.get(run.runId);
|
|
1512
|
+
if (tracked?.run) return state;
|
|
1513
|
+
const result = readRunResult(this.#o.paths, job.id, run.runId);
|
|
1514
|
+
if (run.status === "running") {
|
|
1515
|
+
if (isFinal(result)) return this.#finalize(job, run, result, now);
|
|
1516
|
+
const slug = result?.projectSlug ?? run.projectSlug;
|
|
1517
|
+
const sessionId = result?.sessionId ?? run.sessionId;
|
|
1518
|
+
const ref2 = slug && sessionId ? { home: this.#o.paths.home, projectSlug: slug, sessionId } : void 0;
|
|
1519
|
+
if (ref2 && await sessionLeaseLive(ref2, now)) {
|
|
1520
|
+
this.#running.set(run.runId, {
|
|
1521
|
+
jobId: job.id,
|
|
1522
|
+
runId: run.runId,
|
|
1523
|
+
folder: job.folder.canonical,
|
|
1524
|
+
writes: allowsWrites(job.permissions)
|
|
1525
|
+
});
|
|
1526
|
+
return state;
|
|
1527
|
+
}
|
|
1528
|
+
if (!ref2 && now - Date.parse(run.startedAt) < (run.noSessionGraceMs ?? noSessionStartGraceMs(job))) {
|
|
1529
|
+
this.#running.set(run.runId, {
|
|
1530
|
+
jobId: job.id,
|
|
1531
|
+
runId: run.runId,
|
|
1532
|
+
folder: job.folder.canonical,
|
|
1533
|
+
writes: allowsWrites(job.permissions)
|
|
1534
|
+
});
|
|
1535
|
+
return state;
|
|
1536
|
+
}
|
|
1537
|
+
this.#running.delete(run.runId);
|
|
1538
|
+
const facts2 = ref2 ? await sessionFacts(ref2) : null;
|
|
1539
|
+
if (facts2?.activeTurn?.paused) {
|
|
1540
|
+
return this.#finalize(
|
|
1541
|
+
job,
|
|
1542
|
+
run,
|
|
1543
|
+
{
|
|
1544
|
+
...result ?? this.#emptyResult(job, run),
|
|
1545
|
+
status: "awaiting-approval",
|
|
1546
|
+
exitCode: 0,
|
|
1547
|
+
turnId: facts2.activeTurn.turnId,
|
|
1548
|
+
...ref2 ? { sessionId: ref2.sessionId, projectSlug: ref2.projectSlug } : {}
|
|
1549
|
+
},
|
|
1550
|
+
now
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
const outcome2 = facts2 && !facts2.activeTurn ? turnOutcome(facts2, run.turnId ?? result?.turnId) : void 0;
|
|
1554
|
+
return this.#finalize(
|
|
1555
|
+
job,
|
|
1556
|
+
run,
|
|
1557
|
+
{
|
|
1558
|
+
...result ?? this.#emptyResult(job, run),
|
|
1559
|
+
status: outcome2?.status ?? "interrupted",
|
|
1560
|
+
exitCode: outcome2?.status === "completed" ? 0 : 1,
|
|
1561
|
+
reason: outcome2?.reason ?? "the run stopped without recording a result (its process ended)"
|
|
1562
|
+
},
|
|
1563
|
+
now
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
if (!run.sessionId || !run.projectSlug) {
|
|
1567
|
+
return this.#finalize(
|
|
1568
|
+
job,
|
|
1569
|
+
run,
|
|
1570
|
+
{ ...this.#emptyResult(job, run), status: "interrupted", exitCode: 1 },
|
|
1571
|
+
now
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
const ref = { home: this.#o.paths.home, projectSlug: run.projectSlug, sessionId: run.sessionId };
|
|
1575
|
+
const facts = await sessionFacts(ref);
|
|
1576
|
+
if (!facts) {
|
|
1577
|
+
return this.#finalize(
|
|
1578
|
+
job,
|
|
1579
|
+
run,
|
|
1580
|
+
{
|
|
1581
|
+
...this.#emptyResult(job, run),
|
|
1582
|
+
status: "interrupted",
|
|
1583
|
+
exitCode: 1,
|
|
1584
|
+
reason: "the parked session is gone"
|
|
1585
|
+
},
|
|
1586
|
+
now
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
const active = facts.activeTurn;
|
|
1590
|
+
if (active && active.turnId === run.turnId) {
|
|
1591
|
+
if (!active.paused) return state;
|
|
1592
|
+
const parkedAt = Date.parse(run.parkedAt ?? run.startedAt);
|
|
1593
|
+
if (now - parkedAt > job.approvalTtlMs && !await sessionLeaseLive(ref, now)) {
|
|
1594
|
+
try {
|
|
1595
|
+
await abandonParkedTurn(ref, active.turnId, "Scheduled run: approval expired");
|
|
1596
|
+
} catch (error) {
|
|
1597
|
+
this.#o.log.warn("could not abandon an expired park", {
|
|
1598
|
+
"namzu.schedule.job_id": job.id,
|
|
1599
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
1600
|
+
});
|
|
1601
|
+
return state;
|
|
1602
|
+
}
|
|
1603
|
+
const expired = this.#finalize(
|
|
1604
|
+
job,
|
|
1605
|
+
run,
|
|
1606
|
+
{
|
|
1607
|
+
...this.#emptyResult(job, run),
|
|
1608
|
+
status: "approval-expired",
|
|
1609
|
+
exitCode: 1,
|
|
1610
|
+
reason: "nobody answered the approval in time; the turn was abandoned"
|
|
1611
|
+
},
|
|
1612
|
+
now
|
|
1613
|
+
);
|
|
1614
|
+
await this.#notice("approval-expired", job, { at: new Date(now) });
|
|
1615
|
+
return expired;
|
|
1616
|
+
}
|
|
1617
|
+
return state;
|
|
1618
|
+
}
|
|
1619
|
+
if (active) return state;
|
|
1620
|
+
const outcome = turnOutcome(facts, run.turnId);
|
|
1621
|
+
if (!outcome) return state;
|
|
1622
|
+
return this.#finalize(
|
|
1623
|
+
job,
|
|
1624
|
+
run,
|
|
1625
|
+
{
|
|
1626
|
+
...this.#emptyResult(job, run),
|
|
1627
|
+
status: outcome.status,
|
|
1628
|
+
exitCode: outcome.status === "completed" ? 0 : 1,
|
|
1629
|
+
...outcome.reason ? { reason: outcome.reason } : {}
|
|
1630
|
+
},
|
|
1631
|
+
now
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
#emptyResult(job, run) {
|
|
1635
|
+
return {
|
|
1636
|
+
v: 1,
|
|
1637
|
+
kind: "schedule-run-result",
|
|
1638
|
+
runId: run.runId,
|
|
1639
|
+
jobId: job.id,
|
|
1640
|
+
status: "interrupted",
|
|
1641
|
+
exitCode: 1,
|
|
1642
|
+
startedAt: run.startedAt,
|
|
1643
|
+
...run.sessionId ? { sessionId: run.sessionId } : {},
|
|
1644
|
+
...run.projectSlug ? { projectSlug: run.projectSlug } : {},
|
|
1645
|
+
...run.turnId ? { turnId: run.turnId } : {}
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
/** Record how a run ended, update counters, notify. Returns the new state (already written). */
|
|
1649
|
+
#finalize(job, run, result, now) {
|
|
1650
|
+
const paths = this.#o.paths;
|
|
1651
|
+
const at = new Date(now).toISOString();
|
|
1652
|
+
const status = result.status === "running" ? "interrupted" : result.status;
|
|
1653
|
+
this.#running.delete(run.runId);
|
|
1654
|
+
appendRunRecord(paths, job.id, run, result, at);
|
|
1655
|
+
const current = readState(paths, job.id);
|
|
1656
|
+
const failed = status === "failed" || status === "blocked-config" || status === "timed-out" || status === "interrupted" || status === "approval-expired" || status === "check-failed";
|
|
1657
|
+
const counters = {
|
|
1658
|
+
runs: current.counters.runs + (status === "awaiting-approval" ? 0 : 1),
|
|
1659
|
+
failures: current.counters.failures + (failed ? 1 : 0),
|
|
1660
|
+
failureStreak: failed ? current.counters.failureStreak + 1 : status === "completed" ? 0 : current.counters.failureStreak
|
|
1661
|
+
};
|
|
1662
|
+
const quotaHold = status === "failed" && result.retryAfterMs !== void 0 && result.retryAfterMs > job.budget.waitForProviderMs ? new Date(now + result.retryAfterMs).toISOString() : current.quotaHoldUntil;
|
|
1663
|
+
let next;
|
|
1664
|
+
if (status === "awaiting-approval") {
|
|
1665
|
+
next = withoutUndefined({
|
|
1666
|
+
...current,
|
|
1667
|
+
counters,
|
|
1668
|
+
activeRun: withoutUndefined({
|
|
1669
|
+
...run,
|
|
1670
|
+
status: "awaiting-approval",
|
|
1671
|
+
sessionId: result.sessionId ?? run.sessionId,
|
|
1672
|
+
projectSlug: result.projectSlug ?? run.projectSlug,
|
|
1673
|
+
turnId: result.turnId ?? run.turnId,
|
|
1674
|
+
parkedAt: run.parkedAt ?? at,
|
|
1675
|
+
handoff: result.handoff ? { reason: result.handoff.reason } : void 0
|
|
1676
|
+
})
|
|
1677
|
+
});
|
|
1678
|
+
} else {
|
|
1679
|
+
next = withoutUndefined({
|
|
1680
|
+
...current,
|
|
1681
|
+
counters,
|
|
1682
|
+
activeRun: void 0,
|
|
1683
|
+
quotaHoldUntil: quotaHold,
|
|
1684
|
+
lastRun: withoutUndefined({
|
|
1685
|
+
runId: run.runId,
|
|
1686
|
+
scheduledFor: run.scheduledFor,
|
|
1687
|
+
status,
|
|
1688
|
+
endedAt: result.endedAt ?? at,
|
|
1689
|
+
sessionId: result.sessionId,
|
|
1690
|
+
refusedCalls: result.refusedCalls?.count,
|
|
1691
|
+
failedCalls: result.failedCalls?.count
|
|
1692
|
+
})
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
if (run.delivery || result.scriptReport?.nextState !== void 0) {
|
|
1696
|
+
const snapshot = {
|
|
1697
|
+
...result,
|
|
1698
|
+
status,
|
|
1699
|
+
endedAt: result.endedAt ?? at
|
|
1700
|
+
};
|
|
1701
|
+
next = {
|
|
1702
|
+
...next,
|
|
1703
|
+
deliveryPending: [
|
|
1704
|
+
...current.deliveryPending ?? [],
|
|
1705
|
+
{ result: snapshot, ...run.delivery ? { delivery: run.delivery } : {} }
|
|
1706
|
+
]
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
let incident = next.lastIncident;
|
|
1710
|
+
let tell;
|
|
1711
|
+
if (status === "completed") {
|
|
1712
|
+
incident = void 0;
|
|
1713
|
+
if (result.scriptReport?.state !== "quiet") tell = "finished";
|
|
1714
|
+
} else if (status === "awaiting-approval") {
|
|
1715
|
+
tell = "awaiting-approval";
|
|
1716
|
+
} else if (failed && status !== "approval-expired") {
|
|
1717
|
+
const signature = failureSignature(status, result.reason);
|
|
1718
|
+
if (incident?.signature !== signature) {
|
|
1719
|
+
tell = status === "blocked-config" ? "blocked-config" : status === "timed-out" ? "timed-out" : status === "interrupted" ? "interrupted" : status === "check-failed" ? "check-failed" : "failed";
|
|
1720
|
+
incident = { signature, at };
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
next = withoutUndefined({ ...next, lastIncident: incident });
|
|
1724
|
+
writeState(paths, next);
|
|
1725
|
+
const pauseAfter = job.failurePolicy.pauseAfterFailures;
|
|
1726
|
+
let autoPaused = false;
|
|
1727
|
+
if (failed && pauseAfter > 0 && counters.failureStreak >= pauseAfter) {
|
|
1728
|
+
const latest = readJob(paths, job.id);
|
|
1729
|
+
if (latest && latest.state === "active") {
|
|
1730
|
+
updateJob(paths, job.id, latest.revision, (j) => ({
|
|
1731
|
+
...j,
|
|
1732
|
+
state: "paused",
|
|
1733
|
+
pausedAt: at,
|
|
1734
|
+
pausedBy: "auto-failure-streak"
|
|
1735
|
+
}));
|
|
1736
|
+
appendHistory(paths, job.id, {
|
|
1737
|
+
v: 1,
|
|
1738
|
+
kind: "job",
|
|
1739
|
+
at,
|
|
1740
|
+
action: "paused",
|
|
1741
|
+
by: "auto-failure-streak",
|
|
1742
|
+
detail: `${counters.failureStreak} failed runs in a row`
|
|
1743
|
+
});
|
|
1744
|
+
autoPaused = true;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
if (job.schedule.kind === "at" && status !== "awaiting-approval" && run.trigger !== "manual") {
|
|
1748
|
+
const latest = readJob(paths, job.id);
|
|
1749
|
+
if (latest && latest.state === "active") {
|
|
1750
|
+
updateJob(paths, job.id, latest.revision, (j) => ({ ...j, state: "completed" }));
|
|
1751
|
+
appendHistory(paths, job.id, { v: 1, kind: "job", at, action: "completed", by: "daemon" });
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
const notices = [];
|
|
1755
|
+
if (tell) {
|
|
1756
|
+
const summary = result.summary ?? scriptSummaryOf(result);
|
|
1757
|
+
notices.push(
|
|
1758
|
+
this.#notice(tell, job, {
|
|
1759
|
+
at: new Date(now),
|
|
1760
|
+
...summary ? { summary } : {},
|
|
1761
|
+
...result.refusedCalls ? { refused: result.refusedCalls } : {},
|
|
1762
|
+
...tell === "check-failed" && result.reason ? { reason: result.reason } : {},
|
|
1763
|
+
...tell === "awaiting-approval" && next.activeRun?.sessionId ? { resumeCommand: resumeCommand(job, next.activeRun.sessionId) } : {},
|
|
1764
|
+
...tell === "awaiting-approval" && result.handoff ? { handoff: result.handoff.reason } : {}
|
|
1765
|
+
})
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
if (autoPaused)
|
|
1769
|
+
notices.push(
|
|
1770
|
+
this.#notice("auto-paused", job, { at: new Date(now), failures: counters.failureStreak })
|
|
1771
|
+
);
|
|
1772
|
+
if (status === "completed") {
|
|
1773
|
+
notices.push(
|
|
1774
|
+
archiveOldRuns(paths, job, next).then(
|
|
1775
|
+
(archived) => {
|
|
1776
|
+
if (archived.length > 0) {
|
|
1777
|
+
const latest = readState(paths, job.id);
|
|
1778
|
+
writeState(paths, {
|
|
1779
|
+
...latest,
|
|
1780
|
+
archivedSessions: [...latest.archivedSessions ?? [], ...archived].slice(-200)
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
},
|
|
1784
|
+
() => void 0
|
|
1785
|
+
)
|
|
1786
|
+
);
|
|
1787
|
+
}
|
|
1788
|
+
this.#finalizing.push(Promise.allSettled(notices).then(() => void 0));
|
|
1789
|
+
this.#o.log.info("scheduled run recorded", {
|
|
1790
|
+
"namzu.schedule.job_id": job.id,
|
|
1791
|
+
"namzu.schedule.run_id": run.runId,
|
|
1792
|
+
"namzu.schedule.status": status
|
|
1793
|
+
});
|
|
1794
|
+
return next;
|
|
1795
|
+
}
|
|
1796
|
+
/** Start queued runs, oldest first, within the cap and the folder lanes. */
|
|
1797
|
+
async #dispatch(now) {
|
|
1798
|
+
const paths = this.#o.paths;
|
|
1799
|
+
const queue = [];
|
|
1800
|
+
for (const job of listJobs(paths).jobs) {
|
|
1801
|
+
const state = readState(paths, job.id);
|
|
1802
|
+
if (state.queued) queue.push({ job, state });
|
|
1803
|
+
}
|
|
1804
|
+
queue.sort(
|
|
1805
|
+
(a, b) => (a.state.queued?.queuedAt ?? "").localeCompare(b.state.queued?.queuedAt ?? "")
|
|
1806
|
+
);
|
|
1807
|
+
for (const manual of this.#manual.splice(0)) {
|
|
1808
|
+
const job = readJob(paths, manual.jobId);
|
|
1809
|
+
if (job) queue.push({ job, state: readState(paths, job.id), manual: manual.runId });
|
|
1810
|
+
}
|
|
1811
|
+
for (const entry of queue) {
|
|
1812
|
+
const { job } = entry;
|
|
1813
|
+
const queued = entry.state.queued;
|
|
1814
|
+
if (!await this.#stillOwner()) {
|
|
1815
|
+
this.#o.log.warn("scheduler fenced out; not dispatching", {
|
|
1816
|
+
"namzu.schedule.epoch": this.#o.epoch
|
|
1817
|
+
});
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
const latest = readJob(paths, job.id);
|
|
1821
|
+
if (!latest || latest.state !== "active" || !confirmationHolds(latest)) {
|
|
1822
|
+
if (queued) writeState(paths, withoutUndefined({ ...entry.state, queued: void 0 }));
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
if (readState(paths, job.id).deliveryPending?.length) {
|
|
1826
|
+
if (entry.manual) this.#manual.push({ jobId: job.id, runId: entry.manual });
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
const writes = allowsWrites(latest.permissions);
|
|
1830
|
+
const busyLane = writes && [...this.#running.values()].some((t) => t.writes && t.folder === latest.folder.canonical);
|
|
1831
|
+
if (this.#running.size >= this.#o.maxConcurrentRuns || busyLane) {
|
|
1832
|
+
this.#delayReasons.set(job.id, busyLane ? "folder-busy" : "concurrency-cap");
|
|
1833
|
+
if (entry.manual) this.#manual.push({ jobId: job.id, runId: entry.manual });
|
|
1834
|
+
continue;
|
|
1835
|
+
}
|
|
1836
|
+
const delayReason = this.#delayReasons.get(job.id);
|
|
1837
|
+
this.#delayReasons.delete(job.id);
|
|
1838
|
+
const queuedManual = !entry.manual && queued?.trigger === "manual" && queued.key.startsWith(MANUAL_KEY_PREFIX) ? queued.key.slice(MANUAL_KEY_PREFIX.length) : void 0;
|
|
1839
|
+
const runId = entry.manual ?? queuedManual ?? generateScheduleRunId();
|
|
1840
|
+
const key = entry.manual ? `${MANUAL_KEY_PREFIX}${runId}` : queued?.key;
|
|
1841
|
+
const trigger = entry.manual ? "manual" : queued?.trigger ?? "scheduled";
|
|
1842
|
+
if (!claimOccurrence(paths, {
|
|
1843
|
+
jobId: job.id,
|
|
1844
|
+
key,
|
|
1845
|
+
runId,
|
|
1846
|
+
daemonEpoch: this.#o.epoch,
|
|
1847
|
+
at: new Date(now).toISOString()
|
|
1848
|
+
})) {
|
|
1849
|
+
if (queued)
|
|
1850
|
+
writeState(paths, withoutUndefined({ ...readState(paths, job.id), queued: void 0 }));
|
|
1851
|
+
continue;
|
|
1852
|
+
}
|
|
1853
|
+
const delayedMs = queued && trigger !== "manual" ? Math.max(0, now - Date.parse(queued.queuedAt)) : 0;
|
|
1854
|
+
const run = withoutUndefined({
|
|
1855
|
+
runId,
|
|
1856
|
+
key,
|
|
1857
|
+
trigger,
|
|
1858
|
+
scheduledFor: trigger === "manual" ? void 0 : queued?.scheduledFor,
|
|
1859
|
+
startedAt: new Date(now).toISOString(),
|
|
1860
|
+
daemonEpoch: this.#o.epoch,
|
|
1861
|
+
status: "running",
|
|
1862
|
+
noSessionGraceMs: noSessionStartGraceMs(latest),
|
|
1863
|
+
delivery: latest.delivery,
|
|
1864
|
+
delayedMs: delayedMs >= 1e3 ? delayedMs : void 0,
|
|
1865
|
+
delayReason: delayedMs >= 1e3 ? delayReason ?? "concurrency-cap" : void 0
|
|
1866
|
+
});
|
|
1867
|
+
writeState(
|
|
1868
|
+
paths,
|
|
1869
|
+
withoutUndefined({ ...readState(paths, job.id), queued: void 0, activeRun: run })
|
|
1870
|
+
);
|
|
1871
|
+
appendHistory(paths, job.id, {
|
|
1872
|
+
v: 1,
|
|
1873
|
+
kind: "run",
|
|
1874
|
+
at: run.startedAt,
|
|
1875
|
+
runId,
|
|
1876
|
+
key,
|
|
1877
|
+
trigger,
|
|
1878
|
+
...run.scheduledFor ? { scheduledFor: run.scheduledFor } : {},
|
|
1879
|
+
startedAt: run.startedAt,
|
|
1880
|
+
...run.delayedMs ? { delayedMs: run.delayedMs, delayReason: run.delayReason ?? "concurrency-cap" } : {},
|
|
1881
|
+
status: "running"
|
|
1882
|
+
});
|
|
1883
|
+
let spawned;
|
|
1884
|
+
try {
|
|
1885
|
+
spawned = this.#o.spawnFire({
|
|
1886
|
+
job: latest,
|
|
1887
|
+
runId,
|
|
1888
|
+
key,
|
|
1889
|
+
trigger,
|
|
1890
|
+
...run.scheduledFor ? { scheduledFor: run.scheduledFor } : {},
|
|
1891
|
+
epoch: this.#o.epoch
|
|
1892
|
+
});
|
|
1893
|
+
} catch (error) {
|
|
1894
|
+
this.#finalize(
|
|
1895
|
+
latest,
|
|
1896
|
+
run,
|
|
1897
|
+
{
|
|
1898
|
+
...this.#emptyResult(latest, run),
|
|
1899
|
+
status: "failed",
|
|
1900
|
+
exitCode: 1,
|
|
1901
|
+
reason: `the run could not be started: ${error instanceof Error ? error.message : String(error)}`
|
|
1902
|
+
},
|
|
1903
|
+
now
|
|
1904
|
+
);
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
this.#running.set(runId, {
|
|
1908
|
+
jobId: job.id,
|
|
1909
|
+
runId,
|
|
1910
|
+
folder: latest.folder.canonical,
|
|
1911
|
+
writes,
|
|
1912
|
+
run: spawned
|
|
1913
|
+
});
|
|
1914
|
+
this.#o.log.info("scheduled run started", {
|
|
1915
|
+
"namzu.schedule.job_id": job.id,
|
|
1916
|
+
"namzu.schedule.run_id": runId,
|
|
1917
|
+
"namzu.schedule.trigger": trigger
|
|
1918
|
+
});
|
|
1919
|
+
void spawned.exited.then(
|
|
1920
|
+
() => this.#serializeState(() => {
|
|
1921
|
+
const tracked = this.#running.get(runId);
|
|
1922
|
+
if (tracked) this.#running.set(runId, { ...tracked, run: void 0 });
|
|
1923
|
+
const jobNow = readJob(paths, job.id) ?? latest;
|
|
1924
|
+
const state = readState(paths, job.id);
|
|
1925
|
+
if (state.activeRun?.runId === runId) {
|
|
1926
|
+
const result = readRunResult(paths, job.id, runId);
|
|
1927
|
+
this.#finalize(
|
|
1928
|
+
jobNow,
|
|
1929
|
+
state.activeRun,
|
|
1930
|
+
isFinal(result) ? result : {
|
|
1931
|
+
...this.#emptyResult(jobNow, state.activeRun),
|
|
1932
|
+
...result ?? {},
|
|
1933
|
+
status: "interrupted",
|
|
1934
|
+
exitCode: 1,
|
|
1935
|
+
reason: "the run ended without recording a result"
|
|
1936
|
+
},
|
|
1937
|
+
this.#now()
|
|
1938
|
+
);
|
|
1939
|
+
} else {
|
|
1940
|
+
this.#running.delete(runId);
|
|
1941
|
+
if (!readJob(paths, job.id)) {
|
|
1942
|
+
const result = readRunResult(paths, job.id, runId);
|
|
1943
|
+
appendRunRecord(
|
|
1944
|
+
paths,
|
|
1945
|
+
job.id,
|
|
1946
|
+
run,
|
|
1947
|
+
isFinal(result) ? result : {
|
|
1948
|
+
...this.#emptyResult(latest, run),
|
|
1949
|
+
...result ?? {},
|
|
1950
|
+
status: "interrupted",
|
|
1951
|
+
exitCode: 1,
|
|
1952
|
+
reason: "the run ended without recording a result"
|
|
1953
|
+
},
|
|
1954
|
+
new Date(this.#now()).toISOString()
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
this.wake();
|
|
1959
|
+
})
|
|
1960
|
+
).catch((error) => {
|
|
1961
|
+
this.#o.log.error("scheduled run exit could not be recorded", {
|
|
1962
|
+
"namzu.schedule.job_id": job.id,
|
|
1963
|
+
"namzu.schedule.run_id": runId,
|
|
1964
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
1965
|
+
});
|
|
1966
|
+
this.wake();
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
/** For tests: wait until notifications and archiving started by finalisation are done. */
|
|
1971
|
+
async settled() {
|
|
1972
|
+
await this.#stateTail;
|
|
1973
|
+
await Promise.allSettled(this.#finalizing.splice(0));
|
|
1974
|
+
}
|
|
1975
|
+
};
|
|
1976
|
+
var MANUAL_KEY_PREFIX = "manual-";
|
|
1977
|
+
function appendRunRecord(paths, jobId, run, result, at) {
|
|
1978
|
+
const status = result.status === "running" ? "interrupted" : result.status;
|
|
1979
|
+
appendHistory(paths, jobId, {
|
|
1980
|
+
v: runResultVersion({
|
|
1981
|
+
status,
|
|
1982
|
+
gateResult: result.gateResult,
|
|
1983
|
+
scriptOutput: result.scriptOutput
|
|
1984
|
+
}),
|
|
1985
|
+
kind: "run",
|
|
1986
|
+
at,
|
|
1987
|
+
runId: run.runId,
|
|
1988
|
+
key: run.key,
|
|
1989
|
+
trigger: run.trigger,
|
|
1990
|
+
...run.scheduledFor ? { scheduledFor: run.scheduledFor } : {},
|
|
1991
|
+
startedAt: run.startedAt,
|
|
1992
|
+
...status === "awaiting-approval" ? {} : { endedAt: result.endedAt ?? at },
|
|
1993
|
+
...run.delayedMs ? { delayedMs: run.delayedMs, delayReason: run.delayReason ?? "concurrency-cap" } : {},
|
|
1994
|
+
...result.sessionId ? { sessionId: result.sessionId } : {},
|
|
1995
|
+
status,
|
|
1996
|
+
...result.reason ? { reason: result.reason } : {},
|
|
1997
|
+
exitCode: result.exitCode,
|
|
1998
|
+
...result.summary ? { summary: result.summary } : {},
|
|
1999
|
+
...result.usage ? { usage: result.usage } : {},
|
|
2000
|
+
...result.warnings ? { warnings: result.warnings } : {},
|
|
2001
|
+
...result.refusedCalls ? { refusedCalls: result.refusedCalls } : {},
|
|
2002
|
+
...result.failedCalls ? { failedCalls: result.failedCalls } : {},
|
|
2003
|
+
...result.gateResult ? { gateResult: result.gateResult } : {},
|
|
2004
|
+
...result.scriptOutput ? { scriptOutput: result.scriptOutput } : {}
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
__name(appendRunRecord, "appendRunRecord");
|
|
2008
|
+
function stopRequestPath(paths) {
|
|
2009
|
+
return join4(paths.daemon, "stop.json");
|
|
2010
|
+
}
|
|
2011
|
+
__name(stopRequestPath, "stopRequestPath");
|
|
2012
|
+
function requestStop(paths, stop) {
|
|
2013
|
+
if (stop)
|
|
2014
|
+
writeJsonAtomic(stopRequestPath(paths), {
|
|
2015
|
+
v: 1,
|
|
2016
|
+
kind: "schedule-stop",
|
|
2017
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2018
|
+
});
|
|
2019
|
+
else rmSync3(stopRequestPath(paths), { force: true });
|
|
2020
|
+
}
|
|
2021
|
+
__name(requestStop, "requestStop");
|
|
2022
|
+
function spawnFireProcess(options) {
|
|
2023
|
+
return (request) => {
|
|
2024
|
+
const logPath = options.paths.runLog(request.job.id, request.runId);
|
|
2025
|
+
ensureDir(dirname(logPath));
|
|
2026
|
+
writeFileSync(logPath, "", { flag: "a", mode: PRIVATE_FILE_MODE });
|
|
2027
|
+
const fd = openSync(logPath, "a", PRIVATE_FILE_MODE);
|
|
2028
|
+
let child;
|
|
2029
|
+
try {
|
|
2030
|
+
child = spawn(
|
|
2031
|
+
options.node,
|
|
2032
|
+
[
|
|
2033
|
+
options.bin,
|
|
2034
|
+
"schedule",
|
|
2035
|
+
"__fire",
|
|
2036
|
+
"--home",
|
|
2037
|
+
options.paths.home,
|
|
2038
|
+
"--job",
|
|
2039
|
+
request.job.id,
|
|
2040
|
+
"--run",
|
|
2041
|
+
request.runId,
|
|
2042
|
+
"--key",
|
|
2043
|
+
request.key,
|
|
2044
|
+
"--revision",
|
|
2045
|
+
String(request.job.revision),
|
|
2046
|
+
"--trigger",
|
|
2047
|
+
request.trigger,
|
|
2048
|
+
...request.scheduledFor ? ["--scheduled-for", request.scheduledFor] : [],
|
|
2049
|
+
"--epoch",
|
|
2050
|
+
request.epoch
|
|
2051
|
+
],
|
|
2052
|
+
{
|
|
2053
|
+
cwd: request.job.folder.canonical,
|
|
2054
|
+
env: childEnvironment(options.env, options.paths.home),
|
|
2055
|
+
stdio: ["ignore", fd, fd],
|
|
2056
|
+
detached: process.platform !== "win32",
|
|
2057
|
+
windowsHide: true
|
|
2058
|
+
}
|
|
2059
|
+
);
|
|
2060
|
+
} finally {
|
|
2061
|
+
closeSync(fd);
|
|
2062
|
+
}
|
|
2063
|
+
const exited = new Promise((resolve) => {
|
|
2064
|
+
child.once("exit", (code) => resolve(code));
|
|
2065
|
+
child.once("error", () => resolve(null));
|
|
2066
|
+
});
|
|
2067
|
+
child.unref();
|
|
2068
|
+
return {
|
|
2069
|
+
exited,
|
|
2070
|
+
terminate: /* @__PURE__ */ __name(() => {
|
|
2071
|
+
try {
|
|
2072
|
+
child.kill("SIGTERM");
|
|
2073
|
+
} catch {
|
|
2074
|
+
}
|
|
2075
|
+
}, "terminate")
|
|
2076
|
+
};
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
__name(spawnFireProcess, "spawnFireProcess");
|
|
2080
|
+
function installedFingerprint(bin) {
|
|
2081
|
+
const dist = dirname(bin);
|
|
2082
|
+
const watched = [
|
|
2083
|
+
join4(dist, "..", "package.json"),
|
|
2084
|
+
bin,
|
|
2085
|
+
join4(dist, "schedule", "daemon", "daemon.js"),
|
|
2086
|
+
join4(dist, "schedule", "fire", "fire.js")
|
|
2087
|
+
];
|
|
2088
|
+
return () => {
|
|
2089
|
+
const parts = [];
|
|
2090
|
+
for (const path of watched) {
|
|
2091
|
+
try {
|
|
2092
|
+
const st = statSync4(path);
|
|
2093
|
+
parts.push(`${st.size}:${st.mtimeMs}`);
|
|
2094
|
+
} catch {
|
|
2095
|
+
parts.push("missing");
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
return parts.join("|");
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
__name(installedFingerprint, "installedFingerprint");
|
|
2102
|
+
|
|
2103
|
+
// src/schedule/fire/browser-preflight.ts
|
|
2104
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
2105
|
+
async function browserPreflight(grant, home, deps) {
|
|
2106
|
+
let browser;
|
|
2107
|
+
try {
|
|
2108
|
+
browser = await (deps.load ?? (() => import("@namzu/browser")))();
|
|
2109
|
+
} catch (error) {
|
|
2110
|
+
return {
|
|
2111
|
+
ok: false,
|
|
2112
|
+
reason: `the job uses the browser, but @namzu/browser cannot be loaded (${error instanceof Error ? error.message : String(error)}); run namzu doctor`
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
const login = `namzu browser login ${grant.profile} ${Object.keys(grant.sites)[0] ?? "<url>"}`;
|
|
2116
|
+
let descriptor;
|
|
2117
|
+
try {
|
|
2118
|
+
descriptor = new browser.BrowserProfileStore(home).get(grant.profile);
|
|
2119
|
+
} catch (error) {
|
|
2120
|
+
return {
|
|
2121
|
+
ok: false,
|
|
2122
|
+
reason: `browser profile ${grant.profile} cannot be read: ${error instanceof Error ? error.message : String(error)}`
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
if (!descriptor) {
|
|
2126
|
+
return {
|
|
2127
|
+
ok: false,
|
|
2128
|
+
reason: `browser profile ${grant.profile} does not exist; sign in once with ${login}`
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
const engine = descriptor.engine === "windows-cdp" ? "windows" : "local";
|
|
2132
|
+
const plan = browser.runnableBrowserPlan(
|
|
2133
|
+
browser.detectBrowserEnvironment(
|
|
2134
|
+
deps.env,
|
|
2135
|
+
deps.platform ?? process.platform,
|
|
2136
|
+
deps.probes ?? browser.nodeBrowserProbes,
|
|
2137
|
+
{ engine, headless: grant.headed ? "never" : "always", mode: "unattended" }
|
|
2138
|
+
)
|
|
2139
|
+
);
|
|
2140
|
+
if (plan.unavailableReason !== void 0) {
|
|
2141
|
+
return {
|
|
2142
|
+
ok: false,
|
|
2143
|
+
reason: `the browser for profile ${grant.profile} cannot start here: ${plan.unavailableReason}`
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
if (plan.engine !== descriptor.engine) {
|
|
2147
|
+
return {
|
|
2148
|
+
ok: false,
|
|
2149
|
+
reason: `browser profile ${grant.profile} was signed in with ${descriptor.engine === "windows-cdp" ? "the Windows browser" : "a browser inside Linux"}, and this run would use ${plan.engine === "windows-cdp" ? "the Windows browser" : "a browser inside Linux"}`
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
const exists = deps.exists ?? existsSync2;
|
|
2153
|
+
const dataDir = plan.engine === "windows-cdp" ? browser.windowsPathToWsl(descriptor.userDataDir, plan.mountRoot) : descriptor.userDataDir;
|
|
2154
|
+
if (dataDir === void 0 || !exists(dataDir)) {
|
|
2155
|
+
return {
|
|
2156
|
+
ok: false,
|
|
2157
|
+
reason: `browser profile ${grant.profile} has no data at ${descriptor.userDataDir}; sign in again with ${login}`
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
return { ok: true, engine, warnings: plan.warnings };
|
|
2161
|
+
}
|
|
2162
|
+
__name(browserPreflight, "browserPreflight");
|
|
2163
|
+
|
|
2164
|
+
// src/schedule/fire/run-script.ts
|
|
2165
|
+
import { constants, accessSync } from "node:fs";
|
|
2166
|
+
var MAX_SCRIPT_OUTPUT_CHARS = 8e3;
|
|
2167
|
+
function capOutput(text, max = MAX_SCRIPT_OUTPUT_CHARS) {
|
|
2168
|
+
return text.length <= max ? text : `${text.slice(0, max)}
|
|
2169
|
+
...(truncated)`;
|
|
2170
|
+
}
|
|
2171
|
+
__name(capOutput, "capOutput");
|
|
2172
|
+
function isExecutable(path) {
|
|
2173
|
+
try {
|
|
2174
|
+
accessSync(path, constants.X_OK);
|
|
2175
|
+
return true;
|
|
2176
|
+
} catch {
|
|
2177
|
+
return false;
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
__name(isExecutable, "isExecutable");
|
|
2181
|
+
async function runScript(body, shell, options) {
|
|
2182
|
+
const selected = options.resolvedShell ?? installedCommandShellForDialect(shell);
|
|
2183
|
+
if (!selected) {
|
|
2184
|
+
return { stdout: "", stderr: "", exitCode: null, timedOut: false, shellUnavailable: shell };
|
|
2185
|
+
}
|
|
2186
|
+
if (selected.dialect !== shell) {
|
|
2187
|
+
return {
|
|
2188
|
+
stdout: "",
|
|
2189
|
+
stderr: "",
|
|
2190
|
+
exitCode: null,
|
|
2191
|
+
timedOut: false,
|
|
2192
|
+
dialectMismatch: { expected: shell, actual: selected.dialect }
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
try {
|
|
2196
|
+
const { stdout, stderr } = await execHostShell(body, {
|
|
2197
|
+
cwd: options.cwd,
|
|
2198
|
+
env: options.env,
|
|
2199
|
+
timeout: options.timeoutMs,
|
|
2200
|
+
maxBuffer: options.maxOutputBytes ?? 10 * 1024 * 1024,
|
|
2201
|
+
shell: selected,
|
|
2202
|
+
...options.onOutput ? { onOutput: options.onOutput } : {}
|
|
2203
|
+
});
|
|
2204
|
+
return { stdout, stderr, exitCode: 0, timedOut: false };
|
|
2205
|
+
} catch (error) {
|
|
2206
|
+
const failure = error;
|
|
2207
|
+
if ((failure.code === "ENOENT" || failure.code === "EACCES") && selected.path !== void 0 && !isExecutable(selected.path)) {
|
|
2208
|
+
return {
|
|
2209
|
+
stdout: failure.stdout ?? "",
|
|
2210
|
+
stderr: failure.stderr ?? "",
|
|
2211
|
+
exitCode: null,
|
|
2212
|
+
timedOut: false,
|
|
2213
|
+
shellUnavailable: shell
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
return {
|
|
2217
|
+
stdout: failure.stdout ?? "",
|
|
2218
|
+
stderr: failure.stderr ?? (failure.message ? `${failure.message}
|
|
2219
|
+
` : ""),
|
|
2220
|
+
exitCode: typeof failure.code === "number" ? failure.code : null,
|
|
2221
|
+
timedOut: failure.timedOut === true
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
__name(runScript, "runScript");
|
|
2226
|
+
|
|
2227
|
+
// src/schedule/fire/wake-gate.ts
|
|
2228
|
+
function shown(text, max = 200) {
|
|
2229
|
+
const cut = sanitizeLine(text, max);
|
|
2230
|
+
return cut.includes("`") ? `\`\` ${cut} \`\`` : `\`${cut}\``;
|
|
2231
|
+
}
|
|
2232
|
+
__name(shown, "shown");
|
|
2233
|
+
function isJson(line) {
|
|
2234
|
+
try {
|
|
2235
|
+
JSON.parse(line);
|
|
2236
|
+
return true;
|
|
2237
|
+
} catch {
|
|
2238
|
+
return false;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
__name(isJson, "isJson");
|
|
2242
|
+
function parseWakeGateOutput(stdout, maxContextChars) {
|
|
2243
|
+
const lines = stdout.split(/\r?\n/);
|
|
2244
|
+
while (lines.length > 0 && lines.at(-1).trim() === "") lines.pop();
|
|
2245
|
+
if (lines.length === 0) {
|
|
2246
|
+
return {
|
|
2247
|
+
ok: false,
|
|
2248
|
+
reason: 'the wake-gate script produced no output; it must print exactly one JSON line, {"wake": boolean, "context": string}'
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
const last = lines.at(-1);
|
|
2252
|
+
if (!isJson(last)) {
|
|
2253
|
+
return {
|
|
2254
|
+
ok: false,
|
|
2255
|
+
reason: `the wake-gate script\u2019s stdout does not end with JSON: ${shown(last)}`
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
const earlierJsonLines = lines.slice(0, -1).filter((line) => line.trim() !== "" && isJson(line));
|
|
2259
|
+
if (earlierJsonLines.length > 0) {
|
|
2260
|
+
return {
|
|
2261
|
+
ok: false,
|
|
2262
|
+
reason: "the wake-gate script printed more than one line of JSON stdout; it must print exactly one, as its last line"
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
const value = JSON.parse(last);
|
|
2266
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2267
|
+
return {
|
|
2268
|
+
ok: false,
|
|
2269
|
+
reason: `the wake-gate script\u2019s JSON line is not an object: ${shown(last)}`
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
const record = value;
|
|
2273
|
+
if (typeof record.wake !== "boolean") {
|
|
2274
|
+
return {
|
|
2275
|
+
ok: false,
|
|
2276
|
+
reason: `the wake-gate script\u2019s JSON has no boolean "wake": ${shown(last)}`
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
if (typeof record.context !== "string") {
|
|
2280
|
+
return {
|
|
2281
|
+
ok: false,
|
|
2282
|
+
reason: `the wake-gate script\u2019s JSON has no string "context": ${shown(last)}`
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
const context = record.context.length > maxContextChars ? `${record.context.slice(0, maxContextChars)}
|
|
2286
|
+
...(truncated)` : record.context;
|
|
2287
|
+
return { ok: true, result: { wake: record.wake, context } };
|
|
2288
|
+
}
|
|
2289
|
+
__name(parseWakeGateOutput, "parseWakeGateOutput");
|
|
2290
|
+
|
|
2291
|
+
// src/schedule/fire/fire.ts
|
|
2292
|
+
var HOLD_REASON = "Scheduled run: waiting for the operator to approve";
|
|
2293
|
+
var WATCHDOG_GRACE_MS = 6e4;
|
|
2294
|
+
function parseFireArgs(argv) {
|
|
2295
|
+
const get = /* @__PURE__ */ __name((name) => {
|
|
2296
|
+
const at = argv.indexOf(`--${name}`);
|
|
2297
|
+
return at >= 0 ? argv[at + 1] : void 0;
|
|
2298
|
+
}, "get");
|
|
2299
|
+
const jobId = get("job");
|
|
2300
|
+
const runId = get("run");
|
|
2301
|
+
const key = get("key");
|
|
2302
|
+
const revision = Number(get("revision"));
|
|
2303
|
+
const trigger = get("trigger") ?? "scheduled";
|
|
2304
|
+
if (!jobId || !runId || !key || !Number.isSafeInteger(revision)) {
|
|
2305
|
+
return { error: "__fire needs --job, --run, --key and --revision" };
|
|
2306
|
+
}
|
|
2307
|
+
if (!["scheduled", "late", "catch-up", "manual"].includes(trigger)) {
|
|
2308
|
+
return { error: `__fire: unknown trigger ${trigger}` };
|
|
2309
|
+
}
|
|
2310
|
+
const scheduledFor = get("scheduled-for");
|
|
2311
|
+
const daemonEpoch = get("epoch");
|
|
2312
|
+
return {
|
|
2313
|
+
jobId,
|
|
2314
|
+
runId,
|
|
2315
|
+
key,
|
|
2316
|
+
revision,
|
|
2317
|
+
trigger,
|
|
2318
|
+
...scheduledFor ? { scheduledFor } : {},
|
|
2319
|
+
...daemonEpoch ? { daemonEpoch } : {}
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
__name(parseFireArgs, "parseFireArgs");
|
|
2323
|
+
function credentialSource(source) {
|
|
2324
|
+
switch (source.kind) {
|
|
2325
|
+
case "env":
|
|
2326
|
+
return {
|
|
2327
|
+
text: `environment variable ${source.envName ?? ""}`.trim(),
|
|
2328
|
+
borrowed: false
|
|
2329
|
+
};
|
|
2330
|
+
case "claude-file":
|
|
2331
|
+
return {
|
|
2332
|
+
text: `Claude Code\u2019s sign-in (${source.path ?? ""})`,
|
|
2333
|
+
borrowed: true
|
|
2334
|
+
};
|
|
2335
|
+
case "codex-file":
|
|
2336
|
+
return { text: `Codex\u2019s sign-in (${source.path ?? ""})`, borrowed: true };
|
|
2337
|
+
case "gemini-file":
|
|
2338
|
+
return {
|
|
2339
|
+
text: `Gemini CLI\u2019s sign-in (${source.path ?? ""})`,
|
|
2340
|
+
borrowed: true
|
|
2341
|
+
};
|
|
2342
|
+
case "stored-gemini-key":
|
|
2343
|
+
return {
|
|
2344
|
+
text: `namzu\u2019s saved Gemini API key (${source.path ?? ""})`,
|
|
2345
|
+
borrowed: false
|
|
2346
|
+
};
|
|
2347
|
+
case "stored-api-key":
|
|
2348
|
+
return {
|
|
2349
|
+
text: `namzu\u2019s saved API key (${source.path ?? ""})`,
|
|
2350
|
+
borrowed: false
|
|
2351
|
+
};
|
|
2352
|
+
case "stored":
|
|
2353
|
+
return {
|
|
2354
|
+
text: `namzu\u2019s credential store (${source.path ?? ""})`,
|
|
2355
|
+
borrowed: false
|
|
2356
|
+
};
|
|
2357
|
+
case "keychain":
|
|
2358
|
+
return { text: "the system keychain", borrowed: true };
|
|
2359
|
+
default:
|
|
2360
|
+
return { text: source.kind, borrowed: false };
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
__name(credentialSource, "credentialSource");
|
|
2364
|
+
var BORROWED_CREDENTIAL_WARNING = "This run used another program\u2019s sign-in. Refreshing it here can race that program\u2019s own refresh; store a credential with `namzu login` or daemon.env for unattended runs.";
|
|
2365
|
+
async function runFire(ctx, paths, args, deps = {}) {
|
|
2366
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
2367
|
+
const startedAt = now().toISOString();
|
|
2368
|
+
if (!deps.keepLogging) installCliLogging(createStderrSink("json"), "info");
|
|
2369
|
+
const log = cliLogger();
|
|
2370
|
+
let sessionId;
|
|
2371
|
+
let projectSlug;
|
|
2372
|
+
let turnId;
|
|
2373
|
+
const warnings = [];
|
|
2374
|
+
const found = {};
|
|
2375
|
+
let calls = new CallTally();
|
|
2376
|
+
let gateResult;
|
|
2377
|
+
let scriptOutput;
|
|
2378
|
+
const base = /* @__PURE__ */ __name(() => ({
|
|
2379
|
+
v: 1,
|
|
2380
|
+
kind: "schedule-run-result",
|
|
2381
|
+
runId: args.runId,
|
|
2382
|
+
jobId: args.jobId,
|
|
2383
|
+
startedAt,
|
|
2384
|
+
...sessionId ? { sessionId } : {},
|
|
2385
|
+
...projectSlug ? { projectSlug } : {},
|
|
2386
|
+
...turnId ? { turnId } : {},
|
|
2387
|
+
...found.credential ? { credentialSource: found.credential } : {},
|
|
2388
|
+
...warnings.length > 0 ? { warnings: [...warnings] } : {},
|
|
2389
|
+
...gateResult ? { gateResult } : {},
|
|
2390
|
+
...scriptOutput ? { scriptOutput } : {},
|
|
2391
|
+
...calls.tallies()
|
|
2392
|
+
}), "base");
|
|
2393
|
+
let settledByWatchdog = false;
|
|
2394
|
+
const finish = /* @__PURE__ */ __name((status, exitCode, extra = {}) => {
|
|
2395
|
+
if (settledByWatchdog) return 1;
|
|
2396
|
+
const result = {
|
|
2397
|
+
...base(),
|
|
2398
|
+
status,
|
|
2399
|
+
exitCode,
|
|
2400
|
+
endedAt: now().toISOString(),
|
|
2401
|
+
...extra
|
|
2402
|
+
};
|
|
2403
|
+
writeRunResult(paths, { ...result, v: runResultVersion(result) });
|
|
2404
|
+
log.info("scheduled run finished", {
|
|
2405
|
+
"namzu.schedule.job_id": args.jobId,
|
|
2406
|
+
"namzu.schedule.run_id": args.runId,
|
|
2407
|
+
"namzu.schedule.status": status
|
|
2408
|
+
});
|
|
2409
|
+
return exitCode;
|
|
2410
|
+
}, "finish");
|
|
2411
|
+
const blocked = /* @__PURE__ */ __name((reason, exitCode = 1) => finish("blocked-config", exitCode, { reason }), "blocked");
|
|
2412
|
+
writeRunResult(paths, { ...base(), status: "running", exitCode: 0 });
|
|
2413
|
+
let job;
|
|
2414
|
+
try {
|
|
2415
|
+
job = readJob(paths, args.jobId);
|
|
2416
|
+
} catch (error) {
|
|
2417
|
+
return blocked(error instanceof Error ? error.message : String(error));
|
|
2418
|
+
}
|
|
2419
|
+
if (!job) return blocked("the job was removed before the run started");
|
|
2420
|
+
if (job.revision !== args.revision) {
|
|
2421
|
+
return blocked(
|
|
2422
|
+
`job changed: revision ${job.revision}, the run was claimed for ${args.revision}`
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
if (job.state !== "active") return blocked(`the job is ${job.state}`);
|
|
2426
|
+
if (!confirmationHolds(job)) {
|
|
2427
|
+
return blocked(
|
|
2428
|
+
`job changed outside namzu since it was confirmed; run namzu schedule confirm ${job.name}`
|
|
2429
|
+
);
|
|
2430
|
+
}
|
|
2431
|
+
const runKind = job.runKind ?? "agent";
|
|
2432
|
+
if (job.workspace !== void 0 && job.workspace !== "none")
|
|
2433
|
+
return blocked(`the job has an unknown workspace mode: ${String(job.workspace)}`);
|
|
2434
|
+
if (runKind !== "agent" && runKind !== "script" && runKind !== "script+agent") {
|
|
2435
|
+
return blocked(`the job has an unknown run kind: ${String(runKind)}`);
|
|
2436
|
+
}
|
|
2437
|
+
if (runKind !== "agent" && !job.script) {
|
|
2438
|
+
return blocked(`the ${runKind} job has no script recorded`);
|
|
2439
|
+
}
|
|
2440
|
+
if (job.script?.report !== void 0 && (runKind !== "script" || job.script.report !== "json-v1"))
|
|
2441
|
+
return blocked("the script report mode is not valid for this job");
|
|
2442
|
+
const model = job.model;
|
|
2443
|
+
if (runKind !== "script" && !model)
|
|
2444
|
+
return blocked("an agent job has no model; edit the job and confirm it again");
|
|
2445
|
+
if (job.workspace === "none") {
|
|
2446
|
+
if (runKind !== "script")
|
|
2447
|
+
return blocked("a no-project workspace is only valid for a pure script job");
|
|
2448
|
+
let expected;
|
|
2449
|
+
try {
|
|
2450
|
+
expected = plannedScratchFolder(paths.home, job.id);
|
|
2451
|
+
} catch (error) {
|
|
2452
|
+
return blocked(
|
|
2453
|
+
`scratch folder cannot be located: ${error instanceof Error ? error.message : String(error)}`,
|
|
2454
|
+
77
|
|
2455
|
+
);
|
|
2456
|
+
}
|
|
2457
|
+
if (job.folder.path !== expected || job.folder.canonical !== expected)
|
|
2458
|
+
return blocked("scratch folder differs from the job id and NAMZU_HOME", 77);
|
|
2459
|
+
}
|
|
2460
|
+
const folder = job.workspace === "none" ? checkScratchFolder(paths.home, job.id) : checkJobFolder(job.folder.path, { namzuHome: paths.home });
|
|
2461
|
+
if (!folder.ok) return blocked(folder.reason, 77);
|
|
2462
|
+
if (folder.canonical !== job.folder.canonical || job.trust?.canonical !== folder.canonical) {
|
|
2463
|
+
return blocked(
|
|
2464
|
+
`folder moved or replaced: ${job.folder.path} is now ${folder.canonical}, the job was confirmed for ${job.folder.canonical}`,
|
|
2465
|
+
77
|
|
2466
|
+
);
|
|
2467
|
+
}
|
|
2468
|
+
const readable = folderReadable(folder.canonical);
|
|
2469
|
+
if (!readable.ok) return blocked(readable.reason);
|
|
2470
|
+
const changed = job.workspace === "none" ? [] : projectDigestChanges(job.projectDigest, computeProjectDigest(folder.canonical));
|
|
2471
|
+
if (changed.length > 0) {
|
|
2472
|
+
return blocked(
|
|
2473
|
+
`project config changed since the job was confirmed (${changed.join(", ")}); run namzu schedule confirm ${job.name}`
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2476
|
+
let projectCtx;
|
|
2477
|
+
let layers;
|
|
2478
|
+
try {
|
|
2479
|
+
projectCtx = job.workspace === "none" ? ctx : resolveTrustedProjectContext(ctx, folder.canonical);
|
|
2480
|
+
layers = readPermissionLayers({
|
|
2481
|
+
cwd: folder.canonical,
|
|
2482
|
+
env: { ...deps.env ?? process.env, NAMZU_HOME: paths.home },
|
|
2483
|
+
...job.workspace === "none" ? { includeProject: false } : {}
|
|
2484
|
+
});
|
|
2485
|
+
} catch (error) {
|
|
2486
|
+
return blocked(
|
|
2487
|
+
`config cannot be read: ${error instanceof Error ? error.message : String(error)}`
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
const policy = compileJobPolicy(job.permissions, {
|
|
2491
|
+
layers,
|
|
2492
|
+
namzuHome: paths.home,
|
|
2493
|
+
folder: job.folder
|
|
2494
|
+
});
|
|
2495
|
+
if (policy.diagnostics.length > 0) {
|
|
2496
|
+
return blocked(`permission rules do not compile: ${policy.diagnostics.join("; ")}`);
|
|
2497
|
+
}
|
|
2498
|
+
if (runKind !== "agent" && process.platform === "win32") {
|
|
2499
|
+
return blocked(
|
|
2500
|
+
`a ${runKind} job cannot run on native Windows: its script would run through cmd.exe, read only as a loose approximation the floor cannot fully verify. Use WSL, where a job\u2019s script runs on the Linux side, or an agent job.`
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
const selectedScriptShell = runKind !== "agent" && job.script ? installedCommandShellForDialect(job.script.shell) : void 0;
|
|
2504
|
+
if (runKind !== "agent" && job.script && !selectedScriptShell)
|
|
2505
|
+
return blocked(scriptShellUnavailableReason(job.script.shell));
|
|
2506
|
+
if (runKind !== "agent" && job.script && selectedScriptShell) {
|
|
2507
|
+
const scriptPolicy = compileScriptCheckPolicy(job.permissions, {
|
|
2508
|
+
layers,
|
|
2509
|
+
namzuHome: paths.home,
|
|
2510
|
+
folder: job.folder
|
|
2511
|
+
});
|
|
2512
|
+
const checked = verifyScheduledScript(
|
|
2513
|
+
job.script.body,
|
|
2514
|
+
selectedScriptShell.dialect,
|
|
2515
|
+
scriptPolicy
|
|
2516
|
+
);
|
|
2517
|
+
if (!checked.ok) {
|
|
2518
|
+
return blocked(
|
|
2519
|
+
`the ${runKind === "script" ? "script" : "wake-gate script"} is no longer allowed: ${checked.reason}; run namzu schedule confirm ${job.name} once it is fixed`
|
|
2520
|
+
);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
if (runKind === "script") {
|
|
2524
|
+
return runScriptJob(
|
|
2525
|
+
job,
|
|
2526
|
+
folder.canonical,
|
|
2527
|
+
paths,
|
|
2528
|
+
deps,
|
|
2529
|
+
selectedScriptShell,
|
|
2530
|
+
finish,
|
|
2531
|
+
blocked
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
if (!model) return blocked("an agent job has no model; edit the job and confirm it again");
|
|
2535
|
+
let wakeGateContext;
|
|
2536
|
+
if (runKind === "script+agent") {
|
|
2537
|
+
const script = job.script;
|
|
2538
|
+
if (!script) return blocked(`${job.name} is a script+agent job with no wake-gate recorded`);
|
|
2539
|
+
const gateEnv = { ...deps.env ?? process.env, NAMZU_HOME: paths.home };
|
|
2540
|
+
const gate = await runScript(script.body, script.shell, {
|
|
2541
|
+
cwd: folder.canonical,
|
|
2542
|
+
env: gateEnv,
|
|
2543
|
+
timeoutMs: script.timeoutMs,
|
|
2544
|
+
resolvedShell: selectedScriptShell
|
|
2545
|
+
});
|
|
2546
|
+
if (gate.shellUnavailable) return blocked(scriptShellUnavailableReason(gate.shellUnavailable));
|
|
2547
|
+
if (gate.dialectMismatch) {
|
|
2548
|
+
return blocked(
|
|
2549
|
+
`the wake-gate script was confirmed for ${gate.dialectMismatch.expected}, but this host now runs commands as ${gate.dialectMismatch.actual}; run namzu schedule confirm ${job.name} to verify it in the shell that will actually run it`
|
|
2550
|
+
);
|
|
2551
|
+
}
|
|
2552
|
+
scriptOutput = {
|
|
2553
|
+
stdout: capOutput(gate.stdout),
|
|
2554
|
+
stderr: capOutput(gate.stderr)
|
|
2555
|
+
};
|
|
2556
|
+
if (gate.timedOut) {
|
|
2557
|
+
return finish("check-failed", 1, {
|
|
2558
|
+
reason: `the wake-gate script exceeded its ${script.timeoutMs} ms timeout`
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
if (gate.exitCode !== 0) {
|
|
2562
|
+
return finish("check-failed", 1, {
|
|
2563
|
+
reason: `the wake-gate script exited ${gate.exitCode ?? "without a code"}`
|
|
2564
|
+
});
|
|
2565
|
+
}
|
|
2566
|
+
const parsed = parseWakeGateOutput(
|
|
2567
|
+
gate.stdout,
|
|
2568
|
+
job.wakeGate?.maxContextChars ?? DEFAULT_WAKE_GATE_CONTEXT_CHARS
|
|
2569
|
+
);
|
|
2570
|
+
if (!parsed.ok) return finish("check-failed", 1, { reason: parsed.reason });
|
|
2571
|
+
gateResult = {
|
|
2572
|
+
wake: parsed.result.wake,
|
|
2573
|
+
contextChars: parsed.result.context.length
|
|
2574
|
+
};
|
|
2575
|
+
if (!parsed.result.wake) return finish("completed", 0, {});
|
|
2576
|
+
const findings = scanSchedulePrompt(parsed.result.context);
|
|
2577
|
+
if (findings.length > 0) warnings.push(...findings.map((f) => `wake-gate context: ${f}`));
|
|
2578
|
+
wakeGateContext = revealHiddenCharacters(parsed.result.context);
|
|
2579
|
+
}
|
|
2580
|
+
const grant = job.permissions.browser;
|
|
2581
|
+
let browser;
|
|
2582
|
+
if (grant) {
|
|
2583
|
+
const checked = await (deps.browserPreflight ?? browserPreflight)(grant, paths.home, {
|
|
2584
|
+
env: deps.env ?? process.env
|
|
2585
|
+
});
|
|
2586
|
+
if (!checked.ok) return blocked(checked.reason);
|
|
2587
|
+
browser = checked;
|
|
2588
|
+
warnings.push(...checked.warnings);
|
|
2589
|
+
}
|
|
2590
|
+
const daemonEnv = readDaemonEnv(paths.daemonEnv);
|
|
2591
|
+
warnings.push(...daemonEnv.warnings);
|
|
2592
|
+
const discoveryEnv = { ...deps.env ?? process.env, ...daemonEnv.values };
|
|
2593
|
+
const agent = deps.agent ?? await import("./agent-UEK2U3RE.js");
|
|
2594
|
+
const probe = await agent.probeAgentSession({ env: discoveryEnv });
|
|
2595
|
+
const detected = probe.detected.find((d) => d.entry.id === model.provider);
|
|
2596
|
+
if (!detected) {
|
|
2597
|
+
return blocked(
|
|
2598
|
+
`no credential for ${model.provider} in the scheduler\u2019s environment; run namzu login, or put the key in ${paths.daemonEnv}`
|
|
2599
|
+
);
|
|
2600
|
+
}
|
|
2601
|
+
const source = credentialSource(detected.source);
|
|
2602
|
+
found.credential = source.text;
|
|
2603
|
+
if (source.borrowed) warnings.push(BORROWED_CREDENTIAL_WARNING);
|
|
2604
|
+
const prefs = {
|
|
2605
|
+
version: 3,
|
|
2606
|
+
providers: [
|
|
2607
|
+
{
|
|
2608
|
+
id: model.provider,
|
|
2609
|
+
...model.model ? { model: model.model } : {}
|
|
2610
|
+
}
|
|
2611
|
+
],
|
|
2612
|
+
subagents: { active: [] }
|
|
2613
|
+
};
|
|
2614
|
+
const withheld = withheldTools(job.permissions, policy);
|
|
2615
|
+
calls = new CallTally(withheld);
|
|
2616
|
+
let sessions;
|
|
2617
|
+
try {
|
|
2618
|
+
sessions = await openSessions(folder.canonical, { stateRoot: paths.home });
|
|
2619
|
+
projectSlug = sessions.slug;
|
|
2620
|
+
sessionId = await startConversation(sessions, {
|
|
2621
|
+
origin: { protocol: "cli", kind: "prompt" }
|
|
2622
|
+
});
|
|
2623
|
+
const when = args.scheduledFor ?? startedAt;
|
|
2624
|
+
const tz = job.schedule.kind === "cron" ? job.schedule.tz : void 0;
|
|
2625
|
+
await setTitle(
|
|
2626
|
+
sessions,
|
|
2627
|
+
asSessionId(sessionId),
|
|
2628
|
+
`Scheduled: ${job.name} \xB7 ${describeWhen(when, tz)}`
|
|
2629
|
+
);
|
|
2630
|
+
} catch (error) {
|
|
2631
|
+
return finish("failed", 1, {
|
|
2632
|
+
reason: `the conversation could not be started: ${error instanceof Error ? error.message : String(error)}`
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
writeRunResult(paths, { ...base(), status: "running", exitCode: 0 });
|
|
2636
|
+
const config = projectCtx.config;
|
|
2637
|
+
const session = await agent.createAgentSession(prefs, [detected], {
|
|
2638
|
+
cwd: folder.canonical,
|
|
2639
|
+
stateRoot: paths.home,
|
|
2640
|
+
conversationSessions: sessions,
|
|
2641
|
+
scope: {
|
|
2642
|
+
sessionId: asSessionId(sessionId),
|
|
2643
|
+
topicId: sessions.topicId,
|
|
2644
|
+
projectId: sessions.projectId,
|
|
2645
|
+
tenantId: sessions.tenantId
|
|
2646
|
+
},
|
|
2647
|
+
rules: policy.rules,
|
|
2648
|
+
permissionMode: policy.mode,
|
|
2649
|
+
withheldTools: withheld,
|
|
2650
|
+
...config.mcpServers ? { mcpServers: config.mcpServers } : {},
|
|
2651
|
+
...config.plugins ? { plugins: config.plugins } : {},
|
|
2652
|
+
...config.skills ? { skills: config.skills } : {},
|
|
2653
|
+
...config.hooks ? { hooks: config.hooks } : {},
|
|
2654
|
+
...policy.network && config.web ? { web: config.web } : {},
|
|
2655
|
+
...config.compaction ? { compaction: config.compaction } : {},
|
|
2656
|
+
...config.memory ? { memory: config.memory } : {},
|
|
2657
|
+
sandbox: job.permissions.execution === "sandbox" ? { ...config.sandbox ?? {}, enabled: true } : { enabled: false },
|
|
2658
|
+
...job.permissions.additionalDirectories ? { additionalDirectories: [...job.permissions.additionalDirectories] } : {},
|
|
2659
|
+
...grant && browser ? {
|
|
2660
|
+
browser: {
|
|
2661
|
+
profile: grant.profile,
|
|
2662
|
+
engine: browser.engine,
|
|
2663
|
+
headless: grant.headed ? "never" : "always",
|
|
2664
|
+
// The host checks every landing against the same sites the
|
|
2665
|
+
// gate was given: the grant, what a config file denies, and
|
|
2666
|
+
// nothing else.
|
|
2667
|
+
sites: {
|
|
2668
|
+
...grant.sites,
|
|
2669
|
+
...Object.fromEntries(
|
|
2670
|
+
layers.flatMap((layer) => layer.browserDenies ?? []).map((site) => [site, "deny"])
|
|
2671
|
+
),
|
|
2672
|
+
"*": "deny"
|
|
2673
|
+
},
|
|
2674
|
+
home: paths.home,
|
|
2675
|
+
mode: "unattended"
|
|
2676
|
+
}
|
|
2677
|
+
} : {},
|
|
2678
|
+
limits: {
|
|
2679
|
+
maxIterations: job.budget.maxIterations,
|
|
2680
|
+
tokenBudget: job.budget.tokenBudget,
|
|
2681
|
+
timeoutMs: job.budget.timeoutMs
|
|
2682
|
+
}
|
|
2683
|
+
});
|
|
2684
|
+
const closeAll = /* @__PURE__ */ __name(async () => {
|
|
2685
|
+
await session.close().catch(() => void 0);
|
|
2686
|
+
closeSessions(sessions);
|
|
2687
|
+
}, "closeAll");
|
|
2688
|
+
if (!session.hasProvider) {
|
|
2689
|
+
await closeAll();
|
|
2690
|
+
return blocked(session.errorHint ?? "the provider could not be started");
|
|
2691
|
+
}
|
|
2692
|
+
if (session.mcpFailed.length > 0) {
|
|
2693
|
+
await closeAll();
|
|
2694
|
+
return blocked(
|
|
2695
|
+
`tool servers unavailable: ${session.mcpFailed.map((f) => `${f.name} (${f.reason})`).join(", ")}`
|
|
2696
|
+
);
|
|
2697
|
+
}
|
|
2698
|
+
const abort = new AbortController();
|
|
2699
|
+
const exit = deps.exit ?? ((code) => process.exit(code));
|
|
2700
|
+
const watchdog = setTimeout(
|
|
2701
|
+
() => {
|
|
2702
|
+
abort.abort(new TurnCancelled("user"));
|
|
2703
|
+
void (async () => {
|
|
2704
|
+
finish("timed-out", 1, {
|
|
2705
|
+
reason: `the run exceeded its ${job.budget.timeoutMs} ms wall clock`
|
|
2706
|
+
});
|
|
2707
|
+
settledByWatchdog = true;
|
|
2708
|
+
await releaseHeldSessionLeases().catch(() => void 0);
|
|
2709
|
+
exit(1);
|
|
2710
|
+
})();
|
|
2711
|
+
},
|
|
2712
|
+
job.budget.timeoutMs + (deps.graceMs ?? WATCHDOG_GRACE_MS)
|
|
2713
|
+
);
|
|
2714
|
+
watchdog.unref();
|
|
2715
|
+
let text = "";
|
|
2716
|
+
let totalTokens;
|
|
2717
|
+
let costUsd;
|
|
2718
|
+
let paused = null;
|
|
2719
|
+
let failed = null;
|
|
2720
|
+
let stopReason;
|
|
2721
|
+
const consume = /* @__PURE__ */ __name(async (stream) => {
|
|
2722
|
+
paused = null;
|
|
2723
|
+
failed = null;
|
|
2724
|
+
for await (const event of stream) {
|
|
2725
|
+
switch (event.kind) {
|
|
2726
|
+
case "delta":
|
|
2727
|
+
text += event.text;
|
|
2728
|
+
break;
|
|
2729
|
+
case "usage":
|
|
2730
|
+
totalTokens = event.totalTokens;
|
|
2731
|
+
costUsd = event.cost?.totalCost;
|
|
2732
|
+
break;
|
|
2733
|
+
case "paused":
|
|
2734
|
+
paused = event;
|
|
2735
|
+
turnId = event.turnId;
|
|
2736
|
+
failed = describeTurnInterruption(event);
|
|
2737
|
+
break;
|
|
2738
|
+
case "error":
|
|
2739
|
+
failed = describeTurnInterruption(event);
|
|
2740
|
+
break;
|
|
2741
|
+
case "done":
|
|
2742
|
+
if (event.text !== void 0) text = event.text;
|
|
2743
|
+
stopReason = event.stopReason;
|
|
2744
|
+
if (event.turnId) turnId = event.turnId;
|
|
2745
|
+
break;
|
|
2746
|
+
case "tool-start":
|
|
2747
|
+
log.info("scheduled run tool call", {
|
|
2748
|
+
"namzu.schedule.run_id": args.runId,
|
|
2749
|
+
"namzu.tool.name": event.toolName
|
|
2750
|
+
});
|
|
2751
|
+
break;
|
|
2752
|
+
case "tool-end":
|
|
2753
|
+
calls.observe(event);
|
|
2754
|
+
break;
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
}, "consume");
|
|
2758
|
+
try {
|
|
2759
|
+
await consume(
|
|
2760
|
+
session.send([{ role: "user", content: job.prompt, timestamp: Date.now() }], {
|
|
2761
|
+
signal: abort.signal,
|
|
2762
|
+
permissionMode: policy.mode,
|
|
2763
|
+
reviewHold: { reason: HOLD_REASON },
|
|
2764
|
+
systemNote: unattendedNote(job.name, {
|
|
2765
|
+
browser: grant !== void 0,
|
|
2766
|
+
now: now(),
|
|
2767
|
+
tz: job.schedule.kind === "cron" ? job.schedule.tz : hostTimeZone(),
|
|
2768
|
+
...wakeGateContext !== void 0 ? { wakeGateContext } : {}
|
|
2769
|
+
}),
|
|
2770
|
+
...model.effort ? { effort: model.effort } : {}
|
|
2771
|
+
})
|
|
2772
|
+
);
|
|
2773
|
+
let waits = 0;
|
|
2774
|
+
let waitedMs = 0;
|
|
2775
|
+
for (; ; ) {
|
|
2776
|
+
const current = paused;
|
|
2777
|
+
if (!current || !(current.providerError || current.failure)) break;
|
|
2778
|
+
const asked = retryAfterMs(current);
|
|
2779
|
+
const decision = pauseWait({
|
|
2780
|
+
waited: waits,
|
|
2781
|
+
...asked !== void 0 ? { retryAfterMs: asked } : {},
|
|
2782
|
+
waitedMs,
|
|
2783
|
+
budgetMs: job.budget.waitForProviderMs
|
|
2784
|
+
});
|
|
2785
|
+
if (decision.kind === "stop") {
|
|
2786
|
+
await closeAll();
|
|
2787
|
+
clearTimeout(watchdog);
|
|
2788
|
+
return finish("failed", 75, {
|
|
2789
|
+
reason: `${failed ?? "the provider paused the turn"}; not resumed: ${decision.reason}`,
|
|
2790
|
+
...asked !== void 0 ? { retryAfterMs: asked } : {}
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
waits++;
|
|
2794
|
+
await new Promise((resolve) => setTimeout(resolve, decision.delayMs));
|
|
2795
|
+
waitedMs += decision.delayMs;
|
|
2796
|
+
await consume(
|
|
2797
|
+
session.resumePaused({
|
|
2798
|
+
turnId: current.turnId,
|
|
2799
|
+
checkpointId: current.checkpointId,
|
|
2800
|
+
signal: abort.signal,
|
|
2801
|
+
permissionMode: policy.mode,
|
|
2802
|
+
reviewHold: { reason: HOLD_REASON }
|
|
2803
|
+
})
|
|
2804
|
+
);
|
|
2805
|
+
}
|
|
2806
|
+
} finally {
|
|
2807
|
+
clearTimeout(watchdog);
|
|
2808
|
+
}
|
|
2809
|
+
await closeAll();
|
|
2810
|
+
const usage = {
|
|
2811
|
+
...totalTokens !== void 0 ? { totalTokens } : {},
|
|
2812
|
+
...costUsd !== void 0 ? { costUsd } : {}
|
|
2813
|
+
};
|
|
2814
|
+
const withUsage = Object.keys(usage).length > 0 ? { usage } : {};
|
|
2815
|
+
const summary = summaryOf(text);
|
|
2816
|
+
const parked = paused;
|
|
2817
|
+
if (parked) {
|
|
2818
|
+
const handoff = parked.handoff ? sanitizeLine(handoffReason(parked.handoff), 300) : "";
|
|
2819
|
+
return finish("awaiting-approval", 0, {
|
|
2820
|
+
reason: handoff || parked.reason,
|
|
2821
|
+
...handoff ? { handoff: { reason: handoff } } : {},
|
|
2822
|
+
...summary ? { summary } : {},
|
|
2823
|
+
...withUsage
|
|
2824
|
+
});
|
|
2825
|
+
}
|
|
2826
|
+
if (failed)
|
|
2827
|
+
return finish("failed", 1, {
|
|
2828
|
+
reason: failed,
|
|
2829
|
+
...summary ? { summary } : {},
|
|
2830
|
+
...withUsage
|
|
2831
|
+
});
|
|
2832
|
+
if (stopReason === "timeout") {
|
|
2833
|
+
return finish("timed-out", 1, {
|
|
2834
|
+
reason: `the turn reached its ${job.budget.timeoutMs} ms time limit`,
|
|
2835
|
+
...summary ? { summary } : {},
|
|
2836
|
+
...withUsage
|
|
2837
|
+
});
|
|
2838
|
+
}
|
|
2839
|
+
if (stopReason && stopReason !== "end_turn") {
|
|
2840
|
+
return finish("failed", 1, {
|
|
2841
|
+
reason: `the turn did not finish normally: ${stopReason}`,
|
|
2842
|
+
...summary ? { summary } : {},
|
|
2843
|
+
...withUsage
|
|
2844
|
+
});
|
|
2845
|
+
}
|
|
2846
|
+
return finish("completed", 0, {
|
|
2847
|
+
...summary ? { summary } : {},
|
|
2848
|
+
...withUsage
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
__name(runFire, "runFire");
|
|
2852
|
+
async function runScriptJob(job, cwd, paths, deps, selectedShell, finish, blocked) {
|
|
2853
|
+
const script = job.script;
|
|
2854
|
+
if (!script) return blocked(`${job.name} is a script job with no script recorded`);
|
|
2855
|
+
const env = { ...deps.env ?? process.env, NAMZU_HOME: paths.home };
|
|
2856
|
+
let scriptStateRevision;
|
|
2857
|
+
if (script.report === "json-v1") {
|
|
2858
|
+
try {
|
|
2859
|
+
const snapshot = readScriptState(paths, job.id);
|
|
2860
|
+
env.NAMZU_SCHEDULE_STATE = snapshot.state;
|
|
2861
|
+
env.NAMZU_SCHEDULE_STATE_REVISION = String(snapshot.revision);
|
|
2862
|
+
scriptStateRevision = snapshot.revision;
|
|
2863
|
+
} catch (error) {
|
|
2864
|
+
return blocked(
|
|
2865
|
+
`script state cannot be read: ${error instanceof Error ? error.message : String(error)}`
|
|
2866
|
+
);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
const result = await runScript(script.body, script.shell, {
|
|
2870
|
+
cwd,
|
|
2871
|
+
env,
|
|
2872
|
+
timeoutMs: script.timeoutMs,
|
|
2873
|
+
resolvedShell: selectedShell
|
|
2874
|
+
});
|
|
2875
|
+
if (result.shellUnavailable) return blocked(scriptShellUnavailableReason(result.shellUnavailable));
|
|
2876
|
+
if (result.dialectMismatch) {
|
|
2877
|
+
return blocked(
|
|
2878
|
+
`the script was confirmed for ${result.dialectMismatch.expected}, but this host now runs commands as ${result.dialectMismatch.actual}; run namzu schedule confirm ${job.name} to verify it in the shell that will actually run it`
|
|
2879
|
+
);
|
|
2880
|
+
}
|
|
2881
|
+
const scriptOutput = {
|
|
2882
|
+
stdout: capOutput(result.stdout),
|
|
2883
|
+
stderr: capOutput(result.stderr)
|
|
2884
|
+
};
|
|
2885
|
+
if (result.timedOut) {
|
|
2886
|
+
return finish("timed-out", 1, {
|
|
2887
|
+
reason: `the script exceeded its ${script.timeoutMs} ms timeout`,
|
|
2888
|
+
scriptOutput
|
|
2889
|
+
});
|
|
2890
|
+
}
|
|
2891
|
+
if (result.exitCode === 0) {
|
|
2892
|
+
if (script.report === "json-v1") {
|
|
2893
|
+
const parsed = parseScriptReport(result.stdout);
|
|
2894
|
+
if (!parsed.ok) return finish("check-failed", 1, { reason: parsed.reason, scriptOutput });
|
|
2895
|
+
return finish("completed", 0, {
|
|
2896
|
+
scriptOutput,
|
|
2897
|
+
scriptReport: parsed.result,
|
|
2898
|
+
scriptStateRevision,
|
|
2899
|
+
...parsed.result.state === "changed" ? { summary: parsed.result.summary } : {}
|
|
2900
|
+
});
|
|
2901
|
+
}
|
|
2902
|
+
return finish("completed", 0, { scriptOutput });
|
|
2903
|
+
}
|
|
2904
|
+
return finish("failed", result.exitCode ?? 1, {
|
|
2905
|
+
reason: `the script exited ${result.exitCode ?? "without a code"}`,
|
|
2906
|
+
scriptOutput
|
|
2907
|
+
});
|
|
2908
|
+
}
|
|
2909
|
+
__name(runScriptJob, "runScriptJob");
|
|
2910
|
+
function handoffReason(handoff) {
|
|
2911
|
+
const login = handoff.detail?.tool === "browser" ? handoff.detail.loginCommand : void 0;
|
|
2912
|
+
return login ? `${handoff.reason}; sign in again with ${login}` : handoff.reason;
|
|
2913
|
+
}
|
|
2914
|
+
__name(handoffReason, "handoffReason");
|
|
2915
|
+
function describeWhen(iso, tz) {
|
|
2916
|
+
try {
|
|
2917
|
+
return new Intl.DateTimeFormat("en-GB", {
|
|
2918
|
+
dateStyle: "medium",
|
|
2919
|
+
timeStyle: "short",
|
|
2920
|
+
...tz ? { timeZone: tz } : {}
|
|
2921
|
+
}).format(new Date(iso));
|
|
2922
|
+
} catch {
|
|
2923
|
+
return iso;
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
__name(describeWhen, "describeWhen");
|
|
2927
|
+
|
|
2928
|
+
export {
|
|
2929
|
+
takeSuppressedCount,
|
|
2930
|
+
DELIVERY_READ_LIMIT,
|
|
2931
|
+
readSourceDeliveryPage,
|
|
2932
|
+
writeRunResult,
|
|
2933
|
+
readRunResult,
|
|
2934
|
+
isFinal,
|
|
2935
|
+
claimOccurrence,
|
|
2936
|
+
EXIT_STOP_REQUESTED,
|
|
2937
|
+
daemonLogPath,
|
|
2938
|
+
daemonLogSink,
|
|
2939
|
+
sessionLeaseLive,
|
|
2940
|
+
sessionFacts,
|
|
2941
|
+
abandonParkedTurn,
|
|
2942
|
+
noSessionStartGraceMs,
|
|
2943
|
+
ScheduleDaemon,
|
|
2944
|
+
MANUAL_KEY_PREFIX,
|
|
2945
|
+
appendRunRecord,
|
|
2946
|
+
stopRequestPath,
|
|
2947
|
+
requestStop,
|
|
2948
|
+
spawnFireProcess,
|
|
2949
|
+
installedFingerprint,
|
|
2950
|
+
parseFireArgs,
|
|
2951
|
+
runFire
|
|
2952
|
+
};
|
|
2953
|
+
//# sourceMappingURL=chunk-THZXXVHM.js.map
|