@runeya/runeya 2.0.120 → 2.0.122

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.
Files changed (280) hide show
  1. package/agent/index.js +17 -2
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-BSPEFZKZ.js +10 -0
  4. package/{chat-PXMMZIAY.js → chat-SE5GIT4B.js} +7 -6
  5. package/{chunk-22RJM654.js → chunk-BVBK2WII.js} +155 -157
  6. package/chunk-BVBK2WII.js.map +1 -0
  7. package/chunk-GL7WA5K3.js +92 -0
  8. package/chunk-GL7WA5K3.js.map +1 -0
  9. package/{chunk-GIHR3BGT.js → chunk-NIU3CN5A.js} +37 -17
  10. package/chunk-NIU3CN5A.js.map +1 -0
  11. package/{chunk-H6VIS4OY.js → chunk-T2BV5WZE.js} +18 -3
  12. package/chunk-T2BV5WZE.js.map +1 -0
  13. package/{chunk-NYKFDU76.js → chunk-VNLIA5PV.js} +137 -59
  14. package/chunk-VNLIA5PV.js.map +1 -0
  15. package/{chunk-2WXQIUVC.js → chunk-XTLQOYLT.js} +147 -81
  16. package/chunk-XTLQOYLT.js.map +1 -0
  17. package/{companion-4RQ3AMOL.js → companion-HEJHC3UR.js} +5 -4
  18. package/{desktop-launcher-ADGRYF4S.js → desktop-launcher-EGGVXXKV.js} +2 -2
  19. package/index.js +4 -4
  20. package/package.json +1 -1
  21. package/settings-manager-4GAINSJO.js +10 -0
  22. package/{src-NRKK27F3.js → src-YUVA6WCM.js} +259 -172
  23. package/src-YUVA6WCM.js.map +1 -0
  24. package/web/assets/AgentScalarChatInterface.vue-DrcrJYKe.js +13 -0
  25. package/web/assets/AgentTerminalPage-BonnuSSS.js +1 -0
  26. package/web/assets/AiChatPage-DzTc9-iH.js +1 -0
  27. package/web/assets/AppLayout-DRTTSl7g.js +1 -0
  28. package/web/assets/{AppLayout-kIdcdJ2n.css → AppLayout-vWoE_P8T.css} +1 -1
  29. package/web/assets/AuditPage-CBWGaY0D.js +1 -0
  30. package/web/assets/CloudAccountPage-DTrPLz-p.js +1 -0
  31. package/web/assets/CloudAccountPage-DYOvfMjD.css +1 -0
  32. package/web/assets/CloudAccountPage-DvbOBtHJ.js +1 -0
  33. package/web/assets/CloudMessagingPage-BB-_RdEk.js +2 -0
  34. package/web/assets/DashboardPage-MdYRjTwv.js +164 -0
  35. package/web/assets/DirectoryPickerDialog-DROtNZ9T.js +1 -0
  36. package/web/assets/{EditorIcon-CdapdMgo.js → EditorIcon-LCl9V_-a.js} +2 -2
  37. package/web/assets/{EnvironmentForm-Dgrt5I5N.js → EnvironmentForm-BUadxutb.js} +2 -2
  38. package/web/assets/EnvironmentFormPage-Do3NwXL5.js +1 -0
  39. package/web/assets/EnvironmentListPage-DiG8nF1p.js +1 -0
  40. package/web/assets/KanbanBoardPage-_QCZ2A2f.js +4 -0
  41. package/web/assets/KanbanListPage-Dl-FpuPX.css +1 -0
  42. package/web/assets/KanbanListPage-nyfKMn50.js +1 -0
  43. package/web/assets/LaunchProjectPage-VHAUZ-cq.js +1 -0
  44. package/web/assets/LaunchServicesPage-CRv2zJxH.js +1 -0
  45. package/web/assets/LogViewer-DcoQF_33.js +3 -0
  46. package/web/assets/OrgKeyPrompt-X9hHtpi9.js +1 -0
  47. package/web/assets/{ProcessControls-DRyD5bQg.js → ProcessControls-PKQWukd7.js} +1 -1
  48. package/web/assets/ProcessDetailPage-DhFoi4DT.js +1 -0
  49. package/web/assets/{ProcessMetrics-D5SgFgFU.js → ProcessMetrics-CMewzyxA.js} +2 -2
  50. package/web/assets/RandomJobTestPage-CErdKP1K.js +2 -0
  51. package/web/assets/ScenarioEditorPage-CpRlfU3g.js +14 -0
  52. package/web/assets/SendToChatDialog-DIp62wvw.js +1 -0
  53. package/web/assets/ServiceControlsBar-Bk4M-tKR.js +1 -0
  54. package/web/assets/ServiceDetailPage-BxAZTqFu.js +2 -0
  55. package/web/assets/ServiceFormPage-Dr_e9QMM.js +1 -0
  56. package/web/assets/SettingsPage-Dx1eYZKP.js +2063 -0
  57. package/web/assets/UnlockPage-CgynzFN3.js +1 -0
  58. package/web/assets/UnlockPage-DU3hoV78.css +1 -0
  59. package/web/assets/VaultMigrationPage-Cw--n60U.js +1 -0
  60. package/web/assets/VersionBadge-Ct-qYNDF.js +1 -0
  61. package/web/assets/WikiPage-D7N7nA2O.js +1 -0
  62. package/web/assets/WikiView-Izg2SvGt.js +1 -0
  63. package/web/assets/add-scalar-classes-BP_OZa5m.js +387 -0
  64. package/web/assets/{autocomplete-ByrzVX5h.js → autocomplete-DMfvn4sK.js} +3 -3
  65. package/web/assets/{basic-shape-diamond.svg-h2koobo1.js → basic-shape-diamond.svg-B5H1r2bp.js} +1 -1
  66. package/web/assets/{basic-shape-hexagon.svg--IMj5O6H.js → basic-shape-hexagon.svg-CYAjKF0z.js} +1 -1
  67. package/web/assets/{basic-shape-primary-circle-ellipse-round.svg-B0JqtIPK.js → basic-shape-primary-circle-ellipse-round.svg-CgleCAf-.js} +1 -1
  68. package/web/assets/{basic-shape-primary-square-rectangle.svg-nMUFcz3q.js → basic-shape-primary-square-rectangle.svg-1Oa48Hhs.js} +1 -1
  69. package/web/assets/{basic-shape-shield.svg-B01_o2Y1.js → basic-shape-shield.svg-F6dZwVUJ.js} +1 -1
  70. package/web/assets/{browser-OPkfGEHD.js → browser-CmPAWcJ7.js} +1 -1
  71. package/web/assets/{browser-DcLBGCAS.js → browser-D3KJ0m2n.js} +1 -1
  72. package/web/assets/card-Ciw7uU90.js +32 -0
  73. package/web/assets/{checkbox-DGCPu95-.js → checkbox-BCCx0Nu6.js} +3 -3
  74. package/web/assets/{chip-Chk5tAnb.js → chip-JRh6DXig.js} +2 -2
  75. package/web/assets/client-BIlYBe0s.js +24 -0
  76. package/web/assets/cloud-chat.store-BUYi4kYT.js +1 -0
  77. package/web/assets/cloud-client-CujRC6FF.js +1 -0
  78. package/web/assets/cloud-session-CSIV9Uvx.js +1 -0
  79. package/web/assets/{cloud-session-CnTOFA6T.js → cloud-session-CZWto5y3.js} +2 -2
  80. package/web/assets/column-DSBHw97f.js +763 -0
  81. package/web/assets/{computer-device-desktop-monitor.svg-CfTbcAFd.js → computer-device-desktop-monitor.svg-D7wu2cQ4.js} +1 -1
  82. package/web/assets/{computer-device-desktop.svg-Cl-xv361.js → computer-device-desktop.svg-BtVwjOEG.js} +1 -1
  83. package/web/assets/{computer-device-laptop.svg-ukRjIyMq.js → computer-device-laptop.svg-f3tFeUHj.js} +1 -1
  84. package/web/assets/{computer-device-mobile-phone-android-samsung-back.svg-CoDvI0SU.js → computer-device-mobile-phone-android-samsung-back.svg-CmJcGkrL.js} +1 -1
  85. package/web/assets/{computer-device-mobile-phone-android-samsung.svg-BQWjk4Zt.js → computer-device-mobile-phone-android-samsung.svg-C2ke6Sbg.js} +1 -1
  86. package/web/assets/{computer-device-mobile-phone-iphone-x-back.svg-DAoSG317.js → computer-device-mobile-phone-iphone-x-back.svg-DxMqUXKE.js} +1 -1
  87. package/web/assets/{computer-device-mobile-phone-iphone-x.svg-BPjvW-EA.js → computer-device-mobile-phone-iphone-x.svg-DkbKlrh8.js} +1 -1
  88. package/web/assets/{computer-device-mobile-tablet-touch.svg-BYEFFDGJ.js → computer-device-mobile-tablet-touch.svg-DP0uXMda.js} +1 -1
  89. package/web/assets/{computer-device-mobile-tablet.svg-DNLPmtgI.js → computer-device-mobile-tablet.svg-C064ZFQm.js} +1 -1
  90. package/web/assets/{computer-device-network-ethernet-cat6.svg-BLWV1j_N.js → computer-device-network-ethernet-cat6.svg-Dwzl7zBN.js} +1 -1
  91. package/web/assets/{computer-device-network-lan-www.svg-BOoU0zpo.js → computer-device-network-lan-www.svg-BgErom5w.js} +1 -1
  92. package/web/assets/{computer-device-network-wifi-connection.svg-DGwn0IjA.js → computer-device-network-wifi-connection.svg-ByqNsV6y.js} +1 -1
  93. package/web/assets/{computer-device-network-wifi-router.svg-C5g_K48A.js → computer-device-network-wifi-router.svg-AySGITbi.js} +1 -1
  94. package/web/assets/{cssMode-Cx5qrxXy.js → cssMode-BqrZ4SB-.js} +1 -1
  95. package/web/assets/{divider-BrS14lDG.js → divider-BswZTb8q.js} +2 -2
  96. package/web/assets/{docx-preview-DAPpQjxw.js → docx-preview-t_oOha_C.js} +1 -1
  97. package/web/assets/{ecology-science-erlenmeyer-flask.svg-DomVqm74.js → ecology-science-erlenmeyer-flask.svg-Keg4ZY0z.js} +1 -1
  98. package/web/assets/{fieldset-C0CmkHzJ.js → fieldset-DaWy9FsL.js} +2 -2
  99. package/web/assets/{freemarker2-183f16_T.js → freemarker2-Br0kiufp.js} +1 -1
  100. package/web/assets/{handlebars-Ck2u5MRS.js → handlebars-BrATzKUL.js} +1 -1
  101. package/web/assets/{html-RFHCet7g.js → html-LWTJcTM2.js} +1 -1
  102. package/web/assets/{htmlMode-MLvTePcj.js → htmlMode-A23pWfMP.js} +1 -1
  103. package/web/assets/{image-flash-lightning.svg-C_9EqK2Z.js → image-flash-lightning.svg-uQfoT6Ex.js} +1 -1
  104. package/web/assets/{image-picture-flower.svg-Cw0p4SDa.js → image-picture-flower.svg-XxD1bCHC.js} +1 -1
  105. package/web/assets/{index-De7vzwYE.js → index-e5dmzMc-.js} +362 -362
  106. package/web/assets/{index-qjiWPMcS.css → index-rUoTy8xc.css} +1 -1
  107. package/web/assets/{interface-alert-exclamation-diamond.svg-D_7jQp52.js → interface-alert-exclamation-diamond.svg-gQAlkILu.js} +1 -1
  108. package/web/assets/{interface-alert-exclamation-triangle-warning.svg-slrQHD7Z.js → interface-alert-exclamation-triangle-warning.svg-Ga2rsl6o.js} +1 -1
  109. package/web/assets/{interface-alert-information-circle.svg-D6HZsCxj.js → interface-alert-information-circle.svg-BS0N2R1M.js} +1 -1
  110. package/web/assets/{interface-award-crown.svg-C8KlpCo8.js → interface-award-crown.svg-CtLbK4Gl.js} +1 -1
  111. package/web/assets/{interface-bookmark.svg-CcHEFyZI.js → interface-bookmark-tag.svg-Dfd6eXAX.js} +1 -1
  112. package/web/assets/{interface-bookmark-tag.svg-4m7iWpFd.js → interface-bookmark.svg-Clt6TTov.js} +1 -1
  113. package/web/assets/{interface-calendar-date-one.svg-B-NpBZQW.js → interface-calendar-date-one.svg-BmFNLrzR.js} +1 -1
  114. package/web/assets/{interface-content-book-open-pages.svg-D6JSr0DE.js → interface-content-book-open-pages.svg-u8kJbc3N.js} +1 -1
  115. package/web/assets/{interface-content-book-page.svg-4JwHKP99.js → interface-content-book-page.svg-Csk8d0R8.js} +1 -1
  116. package/web/assets/{interface-content-file.svg-dDsVtuW0.js → interface-content-file.svg-DIoD1UhJ.js} +1 -1
  117. package/web/assets/interface-content-folder.svg-BPZbNQNK.js +1 -0
  118. package/web/assets/{interface-content-folder.svg-VlaIOWsg.js → interface-content-folder.svg-ipO7hYM2.js} +1 -1
  119. package/web/assets/{interface-copy-clipboard.svg-C8NaVJTP.js → interface-copy-clipboard.svg-BaK2fHKS.js} +1 -1
  120. package/web/assets/{interface-edit-attachment.svg-DLv3r8Ta.js → interface-edit-attachment.svg-CrpyP6c6.js} +1 -1
  121. package/web/assets/{interface-edit-binocular.svg-Dn_isC0z.js → interface-edit-binocular.svg-Cw-YeqUQ.js} +1 -1
  122. package/web/assets/{interface-edit-magic-wand.svg-D7nDtfWR.js → interface-edit-magic-wand.svg-BPa49t_4.js} +1 -1
  123. package/web/assets/{interface-edit-tool-paint-roller.svg-pvqfHBhq.js → interface-edit-tool-paint-roller.svg-Cfag7vxY.js} +1 -1
  124. package/web/assets/{interface-edit-tool-pencil.svg-65sH97q4.js → interface-edit-tool-pencil.svg-BNAmX0d_.js} +1 -1
  125. package/web/assets/{interface-favorite-award.svg-ByKr9sn6.js → interface-favorite-award.svg-CIMvocAv.js} +1 -1
  126. package/web/assets/{interface-favorite-flag.svg-Dw-AP2Bq.js → interface-favorite-flag.svg-8C3GXK06.js} +1 -1
  127. package/web/assets/{interface-favorite-heart.svg-B_rIv8ZF.js → interface-favorite-heart.svg-CEU_IALn.js} +1 -1
  128. package/web/assets/{interface-favorite-star.svg-DdSC1aGg.js → interface-favorite-star.svg-Bs0RUtgk.js} +1 -1
  129. package/web/assets/{interface-favorite-stars-sparkles.svg-B2P_fEQm.js → interface-favorite-stars-sparkles.svg-CZYZ5qkH.js} +1 -1
  130. package/web/assets/{interface-hierarchy-flowchart.svg-Dn7xVcha.js → interface-hierarchy-flowchart.svg-DOgEXHV2.js} +1 -1
  131. package/web/assets/{interface-home-house.svg-CPV7dtoL.js → interface-home-house.svg-lDERGk8q.js} +1 -1
  132. package/web/assets/{interface-hyperlink.svg-BxEpW0yS.js → interface-hyperlink.svg-CAZrs64U.js} +1 -1
  133. package/web/assets/{interface-lighting-brightness.svg-CHwx5muE.js → interface-lighting-brightness.svg-CVANBB7i.js} +1 -1
  134. package/web/assets/{interface-lock-closed.svg-Ct9YyvGT.js → interface-lock-closed.svg-qDLyceL-.js} +1 -1
  135. package/web/assets/{interface-lock-open-unlock.svg-DuuZ9seB.js → interface-lock-open-unlock.svg-Bc-cq6eU.js} +1 -1
  136. package/web/assets/{interface-login-key.svg-DwQq_1hs.js → interface-login-key.svg-D6wZBYEi.js} +1 -1
  137. package/web/assets/{interface-search.svg-Cd9FPHXP.js → interface-search.svg-BVM6kkvA.js} +1 -1
  138. package/web/assets/{interface-setting-cog.svg-C2oUvcZk.js → interface-setting-cog.svg-uJYqlmKx.js} +1 -1
  139. package/web/assets/{interface-share-megaphone-bullhorn.svg-D_sqPbcr.js → interface-share-megaphone-bullhorn.svg-CgBVBu7Z.js} +1 -1
  140. package/web/assets/{interface-share-rocket.svg-DHw9o1DN.js → interface-share-rocket.svg-C1iCXMlX.js} +1 -1
  141. package/web/assets/{interface-share-satellite.svg-CYuWbhdH.js → interface-share-satellite.svg-uH-Cvq9d.js} +1 -1
  142. package/web/assets/{interface-share-space-ship.svg-CsQbvDGw.js → interface-share-space-ship.svg-81lauyV5.js} +1 -1
  143. package/web/assets/{interface-share.svg-CxR58P36.js → interface-share.svg-DO85t-F0.js} +1 -1
  144. package/web/assets/{interface-signal-square.svg-BFCAMIlM.js → interface-signal-square.svg-CFAni6lY.js} +1 -1
  145. package/web/assets/{interface-time-clock-circle.svg-BMtmcI_Z.js → interface-time-clock-circle.svg-BUQfluh7.js} +1 -1
  146. package/web/assets/{interface-time-hour-glass.svg-F_74mbfe.js → interface-time-hour-glass.svg-BAZDU-C5.js} +1 -1
  147. package/web/assets/{interface-users-multiple.svg-GqG3hBlb.js → interface-users-multiple.svg-Bj0AvF2-.js} +1 -1
  148. package/web/assets/{interface-weather-moon.svg-By26aJ-s.js → interface-weather-moon.svg-Cxz-5thD.js} +1 -1
  149. package/web/assets/{javascript-DWG-shUz.js → javascript-CuP68vN5.js} +1 -1
  150. package/web/assets/{jsonMode-c7o5Rqeq.js → jsonMode-CdGjze9r.js} +1 -1
  151. package/web/assets/{liquid-DqJ-7gUQ.js → liquid-fJJZPdJb.js} +1 -1
  152. package/web/assets/{lspLanguageFeatures-C_r6rVue.js → lspLanguageFeatures-YzzH9q9a.js} +3 -3
  153. package/web/assets/{mail-chat-bubble-square.svg-1OECQtwZ.js → mail-chat-bubble-square.svg-DY1-sN8v.js} +1 -1
  154. package/web/assets/{mail-send-email-paper-airplane.svg-DGa9kXqg.js → mail-send-email-paper-airplane.svg-6zVEUG2-.js} +1 -1
  155. package/web/assets/{mail-send-envelope.svg-DOnW83Qn.js → mail-send-envelope.svg-DNpA7Auy.js} +1 -1
  156. package/web/assets/{mdx-BmXiyjsf.js → mdx-CphM-wvX.js} +1 -1
  157. package/web/assets/{minus-Bva6o6mu.js → minus-DD84SZSh.js} +2 -2
  158. package/web/assets/modal-CKGM8Wb3.js +5 -0
  159. package/web/assets/modal-Dd5rzDUd.js +1 -0
  160. package/web/assets/{money-cashier-receipt.svg-C9S8E9Gs.js → money-cashier-receipt.svg-ChxLUMT0.js} +1 -1
  161. package/web/assets/{money-currency-dollar-pay.svg-CXNYqgJd.js → money-currency-dollar-pay.svg-CcPdfv1E.js} +1 -1
  162. package/web/assets/{money-graph-arrow-increase.svg-DVTCnF_9.js → money-graph-arrow-increase.svg-BsmXO8yM.js} +1 -1
  163. package/web/assets/{money-graph-bar-chart-increase.svg-CIlyGCUY.js → money-graph-bar-chart-increase.svg-QVk3zD8i.js} +1 -1
  164. package/web/assets/multiselect-3cR58mLz.js +251 -0
  165. package/web/assets/{nature-ecology-leaf.svg-xc6FzauK.js → nature-ecology-leaf.svg-nzup63P6.js} +1 -1
  166. package/web/assets/{password-B_HLITFe.js → password-CCYLOu-C.js} +4 -4
  167. package/web/assets/{phone-telephone.svg-D6y6im-g.js → phone-telephone.svg-BLtgdZw-.js} +1 -1
  168. package/web/assets/{plus-DJr2QEgS.js → plus-jk73RdzY.js} +5 -5
  169. package/web/assets/process.store-jsfEBwiA.js +1 -0
  170. package/web/assets/{programming-bug.svg-jKhqPvQ0.js → programming-bug.svg-BCN6lOn1.js} +1 -1
  171. package/web/assets/{programming-cloud.svg-C1_us4-8.js → programming-cloud.svg-DHNBRNkQ.js} +1 -1
  172. package/web/assets/{programming-computer-database-server.svg-C_8mq7k2.js → programming-computer-database-server.svg-C4DheEbm.js} +1 -1
  173. package/web/assets/{programming-computer-database.svg-Cc445gb5.js → programming-computer-database.svg-cD2w2ZZ6.js} +1 -1
  174. package/web/assets/{programming-module-four-layout.svg-B5fbcAO3.js → programming-module-four-layout.svg-D6_P2rTG.js} +1 -1
  175. package/web/assets/{programming-module-three.svg-J7fncT0c.js → programming-module-three.svg-Dc076Z_f.js} +1 -1
  176. package/web/assets/{programming-module.svg-BVjzZRgZ.js → programming-module.svg-D0c9akII.js} +1 -1
  177. package/web/assets/{programming-script-code.svg-By609kb5.js → programming-script-code.svg-BrT3KZQe.js} +1 -1
  178. package/web/assets/project-launch.store-Bp7vasUu.js +1 -0
  179. package/web/assets/project.store-DuZriz9o.js +1 -0
  180. package/web/assets/{python-jVn8dPRY.js → python-DMw-TRDY.js} +1 -1
  181. package/web/assets/{radiobutton-erK19fCL.js → radiobutton-CnoEo0qc.js} +2 -2
  182. package/web/assets/{razor-BJoJ2H1P.js → razor-BTS4Lk96.js} +1 -1
  183. package/web/assets/router-BU2tqcBm.js +1 -0
  184. package/web/assets/settings-scope-Bg_X1n_Y.js +1 -0
  185. package/web/assets/{settings.store-CcYfFedF.js → settings.store-DeGpnclr.js} +1 -1
  186. package/web/assets/{shopping-cart.svg-a8Rjk8rK.js → shopping-cart.svg-pvFNKXP_.js} +1 -1
  187. package/web/assets/{shopping-gift-present.svg-BIN9jnEb.js → shopping-gift-present.svg-PYJ4_vdS.js} +1 -1
  188. package/web/assets/{shopping-shipping-box-parcel-package.svg-05MVPg3v.js → shopping-shipping-box-parcel-package.svg-KMRd9Rmg.js} +1 -1
  189. package/web/assets/src-BsadAol0.js +572 -0
  190. package/web/assets/{src-DCcw9sU3.css → src-DU1mBjF-.css} +1 -1
  191. package/web/assets/src-NXursDRf.js +1 -0
  192. package/web/assets/{tabpanel-BIAV4aKH.js → tabpanel-CKudNnqo.js} +3 -3
  193. package/web/assets/{tag-new-circle.svg-ehKpryNe.js → tag-new-circle.svg-Bf83DYRq.js} +1 -1
  194. package/web/assets/theme-D6M00FyZ.js +1 -0
  195. package/web/assets/{toggleswitch-qhHjeH7s.js → toggleswitch-Cv0supU2.js} +2 -2
  196. package/web/assets/{travel-map-earth-globe.svg-D0Dk72so.js → travel-map-earth-globe.svg-WxnFDzEH.js} +1 -1
  197. package/web/assets/trpc-mWVODCs2.js +1 -0
  198. package/web/assets/{tsMode-B-s4JVyR.js → tsMode-CfruLmor.js} +5 -5
  199. package/web/assets/{typescript-CToySho3.js → typescript-BvsjNfHT.js} +1 -1
  200. package/web/assets/useAiChatDrawer-vNey6IKe.js +1 -0
  201. package/web/assets/useCloudOrg-B0o8vN4e.js +1 -0
  202. package/web/assets/useEnvVarAutocomplete-DJMTIim7.js +1 -0
  203. package/web/assets/useProjectCatalog-B6GXBDDA.js +1 -0
  204. package/web/assets/{useProjectCloudOrg-DOTcwa9I.js → useProjectCloudOrg-BwLsTiz0.js} +1 -1
  205. package/web/assets/useServiceUrlHost-CE4Qoy-J.js +1 -0
  206. package/web/assets/{xml-BMt9wg3H.js → xml-BTID20Kj.js} +1 -1
  207. package/web/assets/{yaml-RmzWffCM.js → yaml-BtOf_8je.js} +1 -1
  208. package/web/index.html +2 -2
  209. package/agent-manager-ACPQWUZ5.js +0 -9
  210. package/chunk-22RJM654.js.map +0 -1
  211. package/chunk-2WXQIUVC.js.map +0 -1
  212. package/chunk-GIHR3BGT.js.map +0 -1
  213. package/chunk-H6VIS4OY.js.map +0 -1
  214. package/chunk-NYKFDU76.js.map +0 -1
  215. package/settings-manager-2C7RY6ZC.js +0 -9
  216. package/src-NRKK27F3.js.map +0 -1
  217. package/web/assets/AgentScalarChatInterface.vue-Lgml1vhh.js +0 -13
  218. package/web/assets/AgentTerminalPage-CwxbyQxm.js +0 -1
  219. package/web/assets/AiChatPage-CknE-NOo.js +0 -1
  220. package/web/assets/AppLayout-DT6bb__K.js +0 -1
  221. package/web/assets/AuditPage-DzK5Tp3Z.js +0 -1
  222. package/web/assets/CloudAccountPage-BDQpVEAX.js +0 -1
  223. package/web/assets/CloudAccountPage-CWdxZvAC.js +0 -1
  224. package/web/assets/CloudAccountPage-CfMY69pk.css +0 -1
  225. package/web/assets/CloudMessagingPage-BX0QPzkR.js +0 -2
  226. package/web/assets/DashboardPage-BNBzT2zb.js +0 -164
  227. package/web/assets/DirectoryPickerDialog-ZT-tL_4A.js +0 -1
  228. package/web/assets/EnvironmentFormPage-B7uerFJ0.js +0 -1
  229. package/web/assets/EnvironmentListPage-tTe_1yh-.js +0 -1
  230. package/web/assets/KanbanBoardPage-DTZVqxd6.js +0 -4
  231. package/web/assets/KanbanListPage-C5rbZ1eL.css +0 -1
  232. package/web/assets/KanbanListPage-D82HSRy7.js +0 -1
  233. package/web/assets/LaunchProjectPage-DgaslasQ.js +0 -1
  234. package/web/assets/LaunchServicesPage-I2HCqujc.js +0 -1
  235. package/web/assets/LogViewer-CYuoSJAN.js +0 -3
  236. package/web/assets/OrgKeyPrompt-Asz6FlrN.js +0 -1
  237. package/web/assets/ProcessDetailPage-DHmTCgj8.js +0 -1
  238. package/web/assets/RandomJobTestPage-COLLiJzt.js +0 -2
  239. package/web/assets/ScenarioEditorPage-rDIB08Ap.js +0 -14
  240. package/web/assets/SendToChatDialog-BvUmZhUU.js +0 -1
  241. package/web/assets/ServiceControlsBar-DGTsbA5Q.js +0 -1
  242. package/web/assets/ServiceDetailPage-Ddko3DNN.js +0 -2
  243. package/web/assets/ServiceFormPage-oVVmtWjt.js +0 -1
  244. package/web/assets/SettingsPage-BcQruhk1.js +0 -2063
  245. package/web/assets/UnlockPage-CpysPhlT.css +0 -1
  246. package/web/assets/UnlockPage-OH7UV01L.js +0 -1
  247. package/web/assets/VaultMigrationPage-B4a0WihW.js +0 -1
  248. package/web/assets/VersionBadge-URtOAd5L.js +0 -1
  249. package/web/assets/WikiPage-BotpKBAY.js +0 -1
  250. package/web/assets/WikiView-DQCs79lu.js +0 -1
  251. package/web/assets/add-scalar-classes-CnAomA8D.js +0 -387
  252. package/web/assets/card-C3Xots9M.js +0 -32
  253. package/web/assets/client-CbBJUuyt.js +0 -24
  254. package/web/assets/cloud-chat.store-COR3nnYR.js +0 -1
  255. package/web/assets/cloud-client-DZFY1sze.js +0 -1
  256. package/web/assets/cloud-session-CCJUvzk1.js +0 -1
  257. package/web/assets/column-ROcesBUs.js +0 -763
  258. package/web/assets/interface-content-folder.svg-CagI3wg0.js +0 -1
  259. package/web/assets/modal-AUS2ksVq.js +0 -5
  260. package/web/assets/modal-BR3MAJDn.js +0 -1
  261. package/web/assets/multiselect-aPow7kzy.js +0 -251
  262. package/web/assets/process.store-CVrBwiRB.js +0 -1
  263. package/web/assets/project-launch.store-DCuERm8z.js +0 -1
  264. package/web/assets/project.store-D0oo8kyl.js +0 -1
  265. package/web/assets/router-0UcfydXq.js +0 -1
  266. package/web/assets/settings-scope-DdLe151G.js +0 -1
  267. package/web/assets/src-BiwZaAz3.js +0 -1
  268. package/web/assets/src-OmBj_GBQ.js +0 -572
  269. package/web/assets/theme-DiK_jXoN.js +0 -1
  270. package/web/assets/trpc-DOYpfRgV.js +0 -1
  271. package/web/assets/useAiChatDrawer-BSlDU8P0.js +0 -1
  272. package/web/assets/useCloudOrg-_EGQ6eUY.js +0 -1
  273. package/web/assets/useEnvVarAutocomplete-B4h_6ylX.js +0 -1
  274. package/web/assets/useProjectCatalog-B3GacZkf.js +0 -1
  275. package/web/assets/useServiceUrlHost-B-38dOxY.js +0 -1
  276. /package/{agent-manager-ACPQWUZ5.js.map → agent-manager-BSPEFZKZ.js.map} +0 -0
  277. /package/{chat-PXMMZIAY.js.map → chat-SE5GIT4B.js.map} +0 -0
  278. /package/{companion-4RQ3AMOL.js.map → companion-HEJHC3UR.js.map} +0 -0
  279. /package/{desktop-launcher-ADGRYF4S.js.map → desktop-launcher-EGGVXXKV.js.map} +0 -0
  280. /package/{settings-manager-2C7RY6ZC.js.map → settings-manager-4GAINSJO.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../server/src/services/companion.ts","../../server/src/services/conversation-store.ts","../../server/src/services/project-store.ts","../../server/src/utils/ensure-data-gitignore.ts","../../server/src/services/project-path-store.ts","../../server/src/services/turn-events.ts","../../server/src/services/claude-transcript-sync.ts","../../server/src/services/codex-transcript-sync.ts","../../server/src/services/auth-token.ts","../../server/src/services/live-pty.ts","../../server/src/services/active-session-registry.ts"],"sourcesContent":["/**\n * Le compagnon — un second CLI qui relit la conversation et rappelle la première.\n *\n * À chaque fin de tour d'une conversation qui l'a activé, une seconde session\n * CLI relit ce qui vient de se passer. Si elle trouve une régression ou un\n * effet de bord, sa trouvaille est injectée dans la conversation principale,\n * qui repart dessus.\n *\n * Le prompt ne porte que le dernier échange : le compagnon est un CLI outillé,\n * il va chercher l'état du dépôt lui-même. Lui injecter le diff coûterait des\n * jetons pour un contenu qu'il lit plus précisément — et qu'il faudrait\n * tronquer, donc amputer, au-delà de quelques dizaines de milliers de\n * caractères.\n *\n * Pourquoi un second agent plutôt qu'une consigne à l'agent principal : celui\n * qui vient d'écrire le code est le pire relecteur de ce code, parce qu'il\n * relit ce qu'il VOULAIT écrire. Un contexte neuf n'a pas cet angle mort.\n *\n * Trois garde-fous, chacun contre une façon dont ce mécanisme se retourne :\n *\n * - Le compagnon travaille dans SA conversation. Mêlées au fil principal, ses\n * remarques rentreraient dans le contexte du tour suivant, et l'agent\n * finirait par se relire lui-même.\n * - Un compagnon ne s'engendre pas (`isCompanion`), sinon la fin de son propre\n * tour en lancerait un autre, indéfiniment.\n * - Un plafond de relances consécutives. Un LLM sait toujours produire une\n * remarque plausible ; sans plafond, la boucle ne se ferme jamais.\n */\nimport { EventEmitter } from 'node:events';\nimport { conversationStore, type Conversation } from './conversation-store.js';\nimport { projectStore } from './project-store.js';\nimport {\n COMPANION_RELAY_OPEN,\n COMPANION_RELAY_CLOSE,\n extractCompanionRelay,\n} from '@runeya/packages-shared';\nimport { onTurnFinished } from './turn-events.js';\nimport { syncConversationFromClaudeTranscript } from './claude-transcript-sync.js';\nimport { syncConversationFromCodexTranscript } from './codex-transcript-sync.js';\nimport { signJwt } from './auth-token.js';\nimport { agentManager } from './agent-manager.js';\nimport { findLivePty } from './live-pty.js';\nimport { activeSessionRegistry } from './active-session-registry.js';\n\n/** Au-delà, on rend la main à l'utilisateur plutôt que de relancer sans fin. */\nexport const MAX_CONSECUTIVE_RELAUNCHES = 6;\n/**\n * Idem pour l'échange relu.\n *\n * 100 000 caractères — de l'ordre de 25 000 jetons, ce qu'une fenêtre de\n * contexte moderne absorbe sans peine. La borne précédente (20 000) coupait\n * des tours entiers dès qu'un diff ou une sortie de commande entrait dans\n * l'échange, et le compagnon jugeait alors sur un extrait amputé par le haut,\n * sans que rien ne le signale.\n */\nexport const MAX_EXCHANGE_CHARS = 100_000;\n\nconst VERDICT_NOTHING = 'RAS';\n\n/**\n * Les bornes de ce que le compagnon veut transmettre.\n *\n * Sans elles, tout son dernier message partait dans la conversation : il ne\n * pouvait donc ni raisonner à voix haute, ni annoncer ce qu'il inspecte, sans\n * que ce brouillon soit relayé. Les bornes séparent son travail de son message.\n *\n * Elles restent facultatives : un modèle en oublie, et un message sans bornes\n * vaut mieux qu'un silence — on relaie alors la réponse entière, comme avant.\n *\n * Définies dans `packages/shared` : le panneau doit afficher exactement ce qui\n * est relayé, donc extraire de la même façon.\n */\nconst RELAY_OPEN = COMPANION_RELAY_OPEN;\nconst RELAY_CLOSE = COMPANION_RELAY_CLOSE;\n\n/** Relances déjà consommées, par conversation principale. */\nconst relaunches = new Map<string, number>();\n\nexport function resetRelaunches(conversationId: string): void {\n relaunches.delete(conversationId);\n}\n\n/**\n * Prendre un jeton de relance, s'il en reste.\n *\n * Le plafond doit casser une chaîne qui s'emballe, pas éteindre le compagnon\n * pour le restant de la conversation : en l'atteignant, on saute ce tour ET on\n * rend le budget. Le tour suivant — celui que l'utilisateur relance lui-même —\n * repart donc avec un compteur neuf, et la chaîne, elle, est bien coupée.\n */\nexport function takeRelaunchSlot(conversationId: string): boolean {\n const used = relaunches.get(conversationId) ?? 0;\n if (used >= MAX_CONSECUTIVE_RELAUNCHES) {\n relaunches.delete(conversationId);\n return false;\n }\n relaunches.set(conversationId, used + 1);\n return true;\n}\n\n/**\n * Ce que le compagnon veut transmettre, ou rien.\n *\n * Un seul mot est réservé : `RAS` signifie « je n'ai rien à dire », et rien ne\n * part. Tout le reste est envoyé tel quel à la conversation principale.\n *\n * Il n'y a pas deux façons de répondre. Un relecteur peut vouloir poser une\n * question, demander une précision, signaler un doute qu'il ne sait pas encore\n * localiser, ou simplement dire que c'est bon : lui imposer un mot-clé et une\n * référence `fichier:ligne` le rendait muet dans tous ces cas.\n *\n * Ce qui borne la boucle n'est donc pas la forme de la réponse mais le plafond\n * de relances consécutives (`takeRelaunchSlot`) — le seul garde-fou qui tienne\n * quelle que soit la liberté laissée au compagnon.\n */\nexport function relayableMessage(raw: string): string | null {\n const text = (raw ?? '').trim();\n if (!text) return null;\n\n // Bornes présentes : elles font autorité, le reste est le brouillon du\n // compagnon.\n const inner = extractCompanionRelay(text);\n if (inner !== null) return isNothing(inner) ? null : inner;\n if (text.includes(RELAY_OPEN)) return null; // bornes présentes mais vides\n\n return isNothing(text) ? null : text;\n}\n\n/** `RAS` seul, ou en conclusion : le compagnon a regardé et n'envoie rien. */\nfunction isNothing(text: string): boolean {\n const bare = (v: string) => v.replace(/^[\\s\"'«»([.,;:—-]+|[\\s\"'«»)\\].,;:!—-]+$/g, '');\n if (bare(text).toUpperCase() === VERDICT_NOTHING) return true;\n const lines = text.split('\\n').map(l => l.trim()).filter(Boolean);\n const last = lines[lines.length - 1] ?? '';\n return bare(last).toUpperCase() === VERDICT_NOTHING;\n}\n\n/** Jusqu'où le compagnon a déjà relu, par conversation. */\nconst reviewedUpTo = new Map<string, number>();\n\nexport function forgetReviewProgress(conversationId: string): void {\n reviewedUpTo.delete(conversationId);\n}\n\n/**\n * Les balises que le harnais du CLI pose autour d'une commande locale.\n *\n * Un `/resume`, un `/clear`, un `/cost` ne sont pas des tours d'échange : c'est\n * l'interface qui parle au terminal. Le transcript les enregistre pourtant\n * comme des messages `user` ordinaires, avec leurs balises et un « DO NOT\n * respond to these messages » qui s'adresse au harnais, pas au compagnon.\n */\nconst LOCAL_COMMAND_TAGS = /<\\/?(local-command-caveat|local-command-stdout|local-command-stderr|command-name|command-message|command-args)>/g;\n\n/**\n * Ce tour est-il une commande locale plutôt qu'un échange ?\n *\n * Deux conséquences, chacune vue à l'usage :\n * - il ne sert pas d'ancre à la relecture, sinon « Relancer maintenant » juste\n * après un `/resume` repartait sur ce seul tour, sans réponse d'assistant\n * derrière — il n'y avait rien à relire et le bouton paraissait mort ;\n * - il ne compte pas comme du neuf : lancer un tour de modèle pour relire une\n * sortie de commande, c'est payer un RAS certain.\n */\nfunction isLocalCommandTurn(content: string | undefined): boolean {\n const text = content ?? '';\n return text.includes('<command-name>')\n || text.includes('<local-command-stdout>')\n || text.includes('<local-command-stderr>');\n}\n\n/**\n * Neutraliser ces balises avant de les montrer au compagnon.\n *\n * Elles arrivaient brutes dans son prompt, où elles se lisent comme des\n * consignes venues de l'utilisateur alors qu'elles viennent du harnais. On\n * garde le texte — la commande et sa sortie disent quelque chose — on lui\n * retire seulement sa forme de balise.\n */\nfunction defuseHarnessTags(content: string): string {\n return content.replace(LOCAL_COMMAND_TAGS, (_m, name: string) => `[${name}]`);\n}\n\n/**\n * Ce que le compagnon n'a pas encore relu.\n *\n * Le compagnon garde sa session d'un tour à l'autre : ce qu'on lui envoie n'est\n * donc pas la conversation, mais son delta — les messages parus depuis sa\n * dernière relecture. C'est le sens même d'une relecture : ce qui a changé\n * entre deux tours.\n *\n * Le repère n'avance que quand une relecture a bien eu lieu. Un tour sauté —\n * compagnon occupé, plafond atteint, agent injoignable — est donc rattrapé au\n * tour suivant au lieu d'échapper à toute relecture.\n *\n * À la première relecture il n'y a pas de repère : on s'en tient au dernier\n * aller-retour, plutôt que de déverser un historique entier dont l'essentiel\n * est déjà derrière.\n */\nexport interface ReviewWindow {\n /** Où commencer à lire. */\n from: number;\n /** Y a-t-il quelque chose de neuf depuis la dernière relecture ? */\n hasNew: boolean;\n}\n\n/**\n * Ce que le compagnon doit lire, et s'il y a lieu de le lancer.\n *\n * Deux questions distinctes, et les confondre était le défaut :\n *\n * - « y a-t-il du neuf ? » se juge sur le repère de relecture ;\n * - « à partir d'où lire ? » remonte au dernier message de l'utilisateur.\n *\n * Un tour produit plusieurs messages, et un CLI peut déclarer plusieurs fins de\n * tour pour une seule demande — Codex le fait : une pour le lancement d'un\n * sous-agent, une pour la réponse finale. La première relecture consommait\n * alors la demande et avançait le repère ; la seconde ne recevait plus que des\n * réponses d'assistant, sans rien qui lui soit adressé. Le compagnon répondait\n * RAS, et ça passait pour une panne.\n *\n * La demande est donc toujours incluse, même déjà relue : c'est elle qui dit ce\n * qu'on jugeait. Ce qu'elle coûte en jetons est marginal — le compagnon garde\n * sa session, il l'a déjà vue.\n */\nexport function reviewWindow(\n messages: Pick<Conversation, 'messages'>['messages'],\n reviewed: number | undefined,\n): ReviewWindow {\n const list = messages ?? [];\n const count = list.length;\n\n let lastUser = -1;\n for (let i = count - 1; i >= 0; i--) {\n const m = list[i]!;\n if (m.role === 'user' && !isLocalCommandTurn(m.content)) { lastUser = i; break; }\n }\n\n // Ce qui n'est arrivé que sous forme de commande locale n'est pas du neuf à\n // relire : le repère peut être en retard sans qu'il y ait rien à dire.\n let lastReal = 0;\n for (let i = count - 1; i >= 0; i--) {\n const m = list[i]!;\n if (m.role !== 'user' || !isLocalCommandTurn(m.content)) { lastReal = i + 1; break; }\n }\n\n // Une conversation peut avoir rétréci (messages purgés) : ne jamais repartir\n // au-delà de ce qui existe.\n const mark = reviewed === undefined\n ? (lastUser >= 0 ? lastUser : Math.max(0, count - 2))\n : Math.min(Math.max(0, reviewed), count);\n\n return {\n from: lastUser >= 0 ? Math.min(mark, lastUser) : mark,\n hasNew: lastReal > mark,\n };\n}\n\n/** Les messages non relus, mis en forme et bornés. */\nexport function formatExchange(messages: Pick<Conversation, 'messages'>['messages'], from: number): string {\n const text = (messages ?? []).slice(from)\n // `contextNote` porte ce que l'interface a attaché au tour : le service que\n // l'utilisateur consultait, un diff joint, un contexte collé. C'est du\n // contexte que l'agent principal a EU sous les yeux ; l'omettre ferait juger\n // le compagnon sur un échange amputé.\n .map(m => [`[${m.role}]`, m.contextNote ? `(contexte : ${m.contextNote})` : '', defuseHarnessTags(m.content ?? '')]\n .filter(Boolean).join('\\n'))\n .join('\\n\\n');\n // On tronque par la TÊTE : le plus récent est le plus pertinent à relire.\n return text.length > MAX_EXCHANGE_CHARS ? text.slice(-MAX_EXCHANGE_CHARS) : text;\n}\n\n/**\n * Les consignes complètes — dites UNE FOIS, au premier tour de la session.\n *\n * Le compagnon reprend sa session d'un tour à l'autre : tout ce qu'on lui a\n * déjà écrit reste dans son contexte. Redire les consignes à chaque tour les y\n * empile, et fait payer plusieurs centaines de jetons pour un texte qu'il a\n * déjà sous les yeux.\n */\nfunction fullInstructions(): string {\n return [\n \"Tu relis le travail d'un autre agent, dans la même conversation que lui.\",\n 'Tu ne modifies rien. Ton seul produit est le message que tu renvoies. La carte',\n \"kanban, si la conversation en a une, dit ce que « terminé » veut dire ici : tu peux la\",\n 'lire, mais tu ne la déplaces pas, tu ne la commentes pas, tu ne la modifies pas.',\n '',\n \"Les images ne te sont PAS jointes : l'échange n'en porte que le chemin, sous la\",\n 'forme [Image: source: /chemin/fichier.png]. Pour en lire une, ouvre ce chemin avec ton outil de lecture de fichiers — tu en as un.',\n \"N'invente JAMAIS le contenu d'une image que tu n'as pas ouverte : dis que la\",\n 'lecture a échoué. On te croirait.',\n '',\n \"RÈGLE 1 — ON PEUT T'ADRESSER LA PAROLE, ET ALORS TU RÉPONDS.\",\n \"Si l'échange te parle, te nomme, te demande quelque chose : tu réponds, TOUJOURS,\",\n \"même si c'est trivial ou sans rapport avec la relecture. Ne réponds JAMAIS \" + VERDICT_NOTHING,\n 'dans ce cas.',\n '',\n 'RÈGLE 2 — sinon, tu relis, et tu ne parles que si tu as trouvé quelque chose :',\n \" · une régression — un comportement qui change sans que ce soit voulu ;\",\n ' · un effet de bord — ce que la modification touche sans le dire ;',\n ' · de la duplication — du code qui existe déjà ailleurs dans le dépôt ;',\n ' · une affirmation non vérifiée présentée comme acquise.',\n 'Sois sobre là, et seulement là : une remarque que personne',\n \"n'a demandée interrompt quelqu'un au travail. Pointe un fichier et une ligne.\",\n '',\n \"Ne te fie ni aux commentaires, ni aux messages de commit, ni à ce que l'agent principal\",\n \"affirme : ils disent l'intention, pas le code. Va lire le code réel. Un décompte\",\n \"ne prouve rien — « appelé quatre fois », un `grep -c` s'écrivent sans rien ouvrir ;\",\n \"cite l'extrait de chaque site d'appel, sinon c'est « non vérifié ».\",\n '',\n \"SÉCURITÉ — regarde ce qui a été RETIRÉ (validation supprimée, contrôle d'accès\",\n \"déplacé, entrée qui n'est plus échappée : rien n'en reste dans le code final) et\",\n \"ce qui n'a JAMAIS été mis — un contrôle absent d'une entrée ne figure dans aucun\",\n 'diff, alors énumère toutes les entrées qui livrent la ressource. Ne signale une',\n \"vulnérabilité que si tu peux décrire le chemin complet d'une entrée réellement\",\n \"contrôlable jusqu'au point vulnérable ; sinon tais-toi. Quatre exceptions, parce\",\n 'que le dégât est déjà fait : secret exposé, crypto maison, protection supprimée,',\n 'dépendance ajoutée sans vérification.',\n '',\n 'Classe : bloquant, risqué, mineur. Jamais de préférence de style.',\n \"Dis séparément ce que tu n'as PAS pu vérifier — fichier non lu, dépendance\",\n \"externe, comportement à l'exécution — plutôt que de supposer.\",\n '',\n \"D'un tour à l'autre tu gardes le fil : on ne te donne que ce que tu n'as pas\",\n \"encore relu. Ne resignale pas ce que tu as déjà signalé — vérifie si ç'a été\",\n 'traité, et reviens dessus seulement sinon.',\n '',\n \"Le tour a-t-il modifié du code ? S'il n'a rien changé — question, explication,\",\n \"refus — n'inspecte PAS le dépôt. Sinon va le lire toi-même : `git diff`, `git\",\n 'status`, les fichiers touchés. Ne demande rien, cherche.',\n \"Le périmètre n'est pas les lignes du diff, c'est ce dont ce tour est RESPONSABLE :\",\n \"ce qu'il engage — appelants, autres entrées d'une même ressource — et ce que le\",\n \"code neuf aurait dû prévoir. Un défaut dans du code non modifié te regarde s'il\",\n \"naît de ce tour ou si ce tour l'a rendu atteignable ; ce qui vivait sans lui, non.\",\n '',\n 'Tu vérifies dans ton propre tour : ne délègue jamais à un sous-agent — ta réponse',\n \"est lue au moment où tu la renvoies, ce qu'un sous-agent dirait après\",\n \"n'atteindrait plus personne. Lis, cherche, conclus — ou ne dis rien.\",\n '',\n 'Ces consignes valent pour toute la session : elles ne te seront pas répétées.',\n ].join('\\n');\n}\n\n/**\n * Le protocole de réponse — TOUJOURS ajouté, jamais personnalisable.\n *\n * Ce n'est pas du conseil, c'est le contrat par lequel Runeya lit le verdict :\n * `RAS` signifie « rien à transmettre », et les bornes délimitent ce qui part\n * dans la conversation. Laisser remplacer ce bloc casserait la lecture — le\n * compagnon parlerait dans le vide, ou son brouillon partirait dans le fil.\n */\nfunction responseProtocol(): string {\n return [\n 'COMMENT RÉPONDRE :',\n \" · Si tu n'as rien à dire — et seulement dans ce cas — réponds exactement :\",\n ` ${VERDICT_NOTHING}`,\n ' Rien ne sera transmis.',\n ' · Sinon, écris ce que tu veux lui dire ENTRE CES DEUX BORNES :',\n ` ${RELAY_OPEN}`,\n ' ton message',\n ` ${RELAY_CLOSE}`,\n ' Seul ce qui est entre les bornes lui est transmis, tel quel. Ce que tu',\n ' écris en dehors reste ton brouillon : tu peux y réfléchir à voix haute,',\n ' dire ce que tu inspectes, personne ne le lira.',\n '',\n ].join('\\n');\n}\n\n/**\n * Le rappel des tours suivants.\n *\n * Court, mais pas vide : un modèle dérive sur une consigne de forme qu'il n'a\n * plus sous les yeux depuis dix tours. On redit donc les deux seules choses qui\n * cassent tout si elles sont oubliées — le mot réservé et les bornes — et rien\n * d'autre.\n */\nfunction reminder(): string {\n return [\n 'Tour suivant à relire. Mêmes règles.',\n `Rien à dire → ${VERDICT_NOTHING}. Sinon, ton message entre ${RELAY_OPEN} et ${RELAY_CLOSE}.`,\n \"Si l'échange t'adresse quelque chose, tu réponds — jamais \" + VERDICT_NOTHING + ' dans ce cas.',\n ].join('\\n');\n}\n\n/** Les consignes de Runeya, gardées hors du texte modifiable par l'utilisateur. */\nexport function companionDefaultPrompt(): string {\n return fullInstructions();\n}\n\nexport function buildCompanionPrompt(exchange: string, isFirstTurn = true, custom?: string): string {\n const customInstructions = custom?.trim();\n const instructions = isFirstTurn\n ? [\n fullInstructions(),\n ...(customInstructions\n ? [\n '',\n 'SPÉCIFICITÉS AJOUTÉES PAR L’UTILISATEUR :',\n customInstructions,\n ]\n : []),\n '',\n // Le protocole, lui, s'ajoute TOUJOURS : c'est par lui que Runeya lit le\n // verdict. Remplaçable, il rendrait le compagnon illisible.\n responseProtocol(),\n ].join('\\n')\n : reminder();\n return [\n instructions,\n '',\n \"--- Ce que tu n'as pas encore relu de la conversation ---\",\n exchange || '(vide)',\n ].join('\\n');\n}\n\n/**\n * Le compagnon se nomme.\n *\n * Ce message arrive dans la conversation principale comme un tour utilisateur :\n * sans signature, l'agent le prend pour une demande de la personne devant lui\n * et lui répond comme tel — il s'excuserait, ou traiterait la remarque comme un\n * ordre plutôt que comme une hypothèse à vérifier. Le marqueur est en tête pour\n * qu'il survive à une troncature de contexte.\n */\nexport const COMPANION_SIGNATURE = '[COMPAGNON]';\n\n/**\n * Le message injecté dans la conversation principale.\n *\n * Réduit à la signature : le compagnon écrit ce qu'il veut, et l'envelopper de\n * consignes réécrirait ses mots. Ce que le destinataire doit savoir — d'où ça\n * vient, comment le traiter — lui a déjà été dit dans son contexte système\n * (`companionSystemNote`), une fois, plutôt qu'à chaque message.\n */\nexport function buildRelaunchMessage(message: string): string {\n return `${COMPANION_SIGNATURE} ${message.trim()}`;\n}\n\n/**\n * Ce que TOUTE conversation CLI apprend d'elle-même, compagnon actif ou non.\n *\n * Dit dans tous les cas parce que l'agent ne sait pas — et n'a pas à savoir —\n * si le réglage est allumé : il peut être activé en cours de conversation, et\n * un agent qui découvrirait le compagnon au moment où celui-ci le reprend\n * prendrait la reprise pour un reproche de l'utilisateur.\n */\nexport function companionSystemNote(): string {\n return [\n '## Relecture par un compagnon',\n '',\n 'Cette conversation peut être relue par un second agent, le « compagnon ».',\n \"S'il est actif, il lit chaque tour que tu produis ainsi que les\",\n 'modifications du dépôt.',\n '',\n `Quand il a quelque chose à te dire, son message t'arrive préfixé par`,\n `${COMPANION_SIGNATURE}. Il peut te signaler une régression, poser une`,\n \"question, demander une précision, ou confirmer que c'est bon — il écrit\",\n 'librement.',\n '',\n \"Ce n'est PAS un sous-agent : tu ne le lances pas, tu ne peux pas lui parler.\",\n \"Il lit tes tours de lui-même et t'écrit quand il veut. Si on te demande de le\",\n \"solliciter, ne lance rien — dis que tu laisses le tour lui parvenir.\",\n '',\n \"Un tel message ne vient PAS de l'utilisateur : c'est une relecture, à\",\n \"vérifier avant d'agir. Si elle est juste, corrige la cause ; si elle est\",\n \"fausse, dis-le en une phrase et poursuis. Ne t'excuse pas auprès de\",\n \"l'utilisateur pour une remarque du compagnon, et ne la lui attribue pas.\",\n '',\n \"Écris donc en sachant que ton travail peut être relu : dis ce que tu as vérifié\",\n \"et ce que tu n'as pas vérifié, plutôt que d'affirmer sans preuve.\",\n ].join('\\n');\n}\n\n/**\n * L'agent où lancer le compagnon : celui où tourne la conversation relue.\n *\n * Un terminal interactif se lance sur l'agent d'un service, éventuellement\n * distant. Le compagnon lancé sur l'agent par défaut inspecterait alors un\n * autre arbre de travail — et rendrait des verdicts sur le mauvais dépôt, sans\n * que rien n'échoue.\n *\n * Deux sources, dans cet ordre, parce qu'une conversation tourne de deux\n * façons : un PTY vivant (terminal) dit où elle tourne EN CE MOMENT ; sinon la\n * dernière session enregistrée dit où le tour qu'on vient de relire s'est\n * exécuté — le registre garde cette session après la fin du tour, et le\n * compagnon part précisément à ce moment-là. Le PTY passe devant : il décrit\n * l'état courant, la session décrit le passé.\n *\n * Aucune des deux ? Il n'y a rien à suivre — une session Claude lancée hors de\n * Runeya, par exemple, tourne sur la machine du serveur. L'appelant laisse\n * alors le démarreur retomber sur l'agent par défaut, comme avant.\n */\nexport function companionAgentId(\n livePtyAgentId: string | undefined,\n lastSessionAgentId: string | undefined,\n): string | undefined {\n return livePtyAgentId ?? lastSessionAgentId;\n}\n\n/**\n * Un tour vient de finir sur `conversationId` : le compagnon doit-il parler ?\n *\n * Sortie silencieuse dans tous les cas où il n'a rien à faire — cette fonction\n * est appelée à CHAQUE fin de tour, y compris sur les conversations qui\n * n'utilisent pas le compagnon.\n */\n/**\n * Une ligne par sortie, avec sa raison.\n *\n * Ce chemin a huit portes de sortie et aucune ne laissait de trace : un\n * compagnon muet était indiscernable d'un compagnon jamais déclenché, et le\n * diagnostic se faisait par hypothèses successives. Le coût est d'une ligne de\n * journal par tour relu.\n */\nfunction trace(conversationId: string, reason: string): void {\n console.log(`[companion] ${conversationId.slice(0, 8)} — ${reason}`);\n}\n\nasync function runCompanionForTurn(conversationId: string): Promise<void> {\n const conv = await conversationStore.get(conversationId);\n if (!conv) return trace(conversationId, 'conversation introuvable');\n if (conv.isCompanion) return; // un compagnon ne s'engendre pas\n if (!conv.companionEnabled) return; // le cas courant : inutile de le tracer\n\n const messageCount = conv.messages?.length ?? 0;\n const { from, hasNew } = reviewWindow(conv.messages, reviewedUpTo.get(conversationId));\n // Rien de neuf depuis la dernière relecture : lancer un CLI pour lui faire\n // lire le vide coûte un tour de modèle et rend RAS à coup sûr. Arrive quand\n // un même tour est annoncé deux fois — le flux et le transcript peuvent tous\n // deux le signaler. Vérifié AVANT de créer quoi que ce soit : sinon un tour\n // annoncé deux fois crée une conversation de compagnon pour rien.\n if (!hasNew) {\n return skip(conversationId, 'nothingNew', `rien de neuf à relire (${messageCount} messages)`);\n }\n\n // Import tardif : `chat.ts` importe le registre, qui nous annonce le tour.\n // Le résoudre au chargement fermerait le cycle.\n const { startCliSession } = await import('../trpc/routers/chat.js');\n\n const companionConv = await ensureCompanionConversation(conv);\n // Les consignes complètes au premier tour seulement : la session étant\n // reprise, le compagnon les a déjà sous les yeux ensuite.\n //\n // Ce critère DOIT rester le miroir de `isResume` dans `startClaudeCodeSession`\n // (`conv.messages.length > 0`) : c'est lui qui décide si le CLI reprend la\n // session. Les désaccorder enverrait les consignes dans une session neuve qui\n // ne les a jamais reçues — ou pas du tout. Un test le verrouille.\n const isFirstTurn = (companionConv.messages?.length ?? 0) === 0;\n const exchange = formatExchange(conv.messages, from);\n const { prompt: customPrompt } = await companionSettings();\n const prompt = buildCompanionPrompt(exchange, isFirstTurn, customPrompt);\n const jwt = await signJwt();\n\n /*\n * Le même environnement que la conversation relue.\n *\n * Les variables marquées « injecter dans l'IA » entrent dans l'environnement\n * du CLI principal. Sans elles, le compagnon travaille dans un contexte que\n * l'autre n'a pas : une commande qui marche chez l'un échoue chez lui, et il\n * signale un défaut qui n'existe pas.\n */\n const environmentId = await activeEnvironmentOf(conv);\n\n /*\n * Le même agent que la conversation relue — voir `companionAgentId`.\n */\n const agentId = companionAgentId(\n (await findLivePty(conv.id))?.agentId,\n activeSessionRegistry.get(conv.id)?.agentId,\n );\n\n // À partir d'ici seulement, le compagnon travaille pour de bon : tout ce qui\n // précède pouvait encore renoncer sans rien lancer. Le `finally` couvre les\n // quatre sorties qui suivent — une seule oubliée laisserait un badge allumé\n // sur une conversation au repos.\n const endThinking = beginThinking(conversationId);\n try {\n try {\n const { done } = await startCliSession(\n conv.companionRunner ?? conv.runner,\n companionConv.id,\n prompt,\n { jwt, environmentId, agentId },\n );\n await done;\n } catch (err) {\n // Une session déjà en cours, ou un runner indisponible.\n const detail = (err as Error).message;\n return skip(conversationId, 'startFailed', `relecture impossible : ${detail}`, detail);\n }\n\n const raw = await readCompanionAnswer(companionConv.id, conv.companionRunner ?? conv.runner);\n\n /*\n * Le CLI a rendu la main sans rien écrire : ce n'est pas un « RAS », c'est\n * une relecture qui n'a pas eu lieu.\n *\n * Le cas vient d'une session que le CLI ne sait plus reprendre — la\n * conversation du compagnon survit au serveur, son transcript non. Le\n * `--resume` échoue alors, mais l'échec est avalé en amont : la promesse de\n * session se règle normalement, et le `catch` ci-dessus ne voit rien.\n * Faute de ce contrôle, le tour était compté comme relu, le repère avançait\n * et l'échange n'était jamais rattrapé — en silence, puisque rien n'était\n * transmis non plus.\n *\n * Le repère ne bouge donc pas : le tour repasse à la prochaine occasion.\n */\n if (!raw.trim()) {\n return skip(conversationId, 'noAnswer', 'le CLI a rendu la main sans écrire un mot');\n }\n\n // La relecture a eu lieu : le repère avance jusqu'à ce qui était là quand on\n // a construit le prompt. Pas jusqu'à maintenant — l'utilisateur a pu écrire\n // pendant ce temps, et ces messages-là n'ont pas été relus.\n reviewedUpTo.set(conversationId, messageCount);\n\n const toRelay = relayableMessage(raw);\n trace(conversationId, `relecture terminée — réponse de ${raw.length} caractères, ${toRelay ? 'à transmettre' : 'rien à transmettre'}`);\n if (!toRelay) {\n // Rien à transmettre : la chaîne s'arrête, et le budget de relances repart\n // neuf pour le prochain tour. La trace de livraison s'efface aussi, sinon\n // un « transmis » d'il y a trois tours resterait affiché comme s'il venait\n // d'arriver.\n relaunches.delete(conversationId);\n forgetDelivery(conversationId);\n return;\n }\n\n // Le jeton se prend au moment de relancer, pas avant la relecture : une\n // relecture qui ne trouve rien ne doit rien consommer.\n if (!takeRelaunchSlot(conversationId)) {\n return skip(conversationId, 'relaunchCap', 'plafond de relances atteint — on rend la main');\n }\n await deliverToMainConversation(conv, buildRelaunchMessage(toRelay));\n } finally {\n endThinking();\n }\n}\n\n/** Le temps qu'il faut au CLI pour absorber la saisie avant de la valider. */\nconst PTY_SUBMIT_DELAY_MS = 120;\n\n/**\n * Un message tenant sur UNE ligne, pour une interface de terminal.\n *\n * Une TUI traite chaque saut de ligne comme une nouvelle ligne du champ de\n * saisie : un message en plusieurs paragraphes s'y étale sans jamais partir, et\n * le premier retour chariot rencontré peut même soumettre une phrase tronquée.\n * On aplatit donc, en gardant les séparations lisibles.\n */\nexport function flattenForPty(message: string): string {\n return message\n .split(/\\n{2,}/).map(block => block.replace(/\\s*\\n\\s*/g, ' ').trim())\n .filter(Boolean)\n .join(' · ')\n .replace(/\\s{2,}/g, ' ')\n .trim();\n}\n\n/**\n * « Le compagnon relit cette conversation en ce moment. »\n *\n * L'information existait déjà, mais seulement en réponse à une question posée :\n * `companionDelivery` la dérivait du registre de sessions, et l'écran la\n * redemandait toutes les 1,5 s. Un sondage rate par construction ce qui est\n * plus court que sa période, et fait payer une requête par seconde et demie à\n * un état qui ne change que deux fois par tour.\n *\n * Ici, c'est le cycle lui-même qui l'annonce — d'où la couverture plus large\n * que le registre : la relecture commence quand on lance le CLI et ne finit\n * qu'une fois sa réponse lue, y compris pendant le sondage du transcript où\n * aucune session n'est « running ».\n *\n * L'identifiant porté est celui de la conversation RELUE, pas celui de la\n * conversation du compagnon : c'est sur la première que l'écran — panneau,\n * liste, carte de kanban — a besoin d'afficher quelque chose.\n */\nconst bus = new EventEmitter();\n// Un abonné par onglet ouvert : la limite de 10 par défaut est trop basse.\nbus.setMaxListeners(0);\n\n/*\n * Un COMPTE de relectures en cours, pas un simple drapeau.\n *\n * Un même tour peut être annoncé deux fois — le flux et le transcript le\n * signalent tous les deux — et les deux passages se croisent : le second\n * échoue aussitôt sur « session déjà en cours », et son extinction éteindrait\n * le badge alors que le premier relit encore. Le compte fait que seule la\n * dernière relecture à finir éteint.\n */\nconst thinking = new Map<string, number>();\n\n/** Les conversations dont le compagnon relit, à cet instant. */\nexport function companionThinkingIds(): string[] {\n return [...thinking.keys()];\n}\n\nexport function isCompanionThinking(conversationId: string): boolean {\n return thinking.has(conversationId);\n}\n\n/** S'abonner aux changements. Rend la fonction de désabonnement. */\nexport function onCompanionThinkingChanged(listener: () => void): () => void {\n bus.on('thinking-changed', listener);\n return () => { bus.off('thinking-changed', listener); };\n}\n\n/**\n * Pourquoi la dernière relecture n'a pas eu lieu.\n *\n * Le cycle sait toujours pourquoi il renonce — il l'écrivait dans le journal du\n * serveur, hors de portée de qui regarde l'écran. Un compagnon allumé mais muet\n * y était donc indiscernable d'un compagnon en panne, et le diagnostic\n * demandait un accès au serveur.\n *\n * La raison vaut « jusqu'à la prochaine exécution » : `beginThinking` l'efface,\n * puisqu'à partir de là le compagnon travaille pour de bon. Elle ne peut donc\n * pas rester affichée sur un fait périmé.\n */\nexport type CompanionSkipReason = 'nothingNew' | 'relaunchCap' | 'startFailed' | 'noAnswer';\n\nexport interface CompanionSkip {\n reason: CompanionSkipReason;\n /** Ce que le code n'a pas su dire lui-même — un message d'erreur, surtout. */\n detail?: string;\n at: number;\n}\n\nconst skips = new Map<string, CompanionSkip>();\n\n/** Les raisons en cours, par conversation — la charge du flux poussé. */\nexport function companionSkips(): Record<string, CompanionSkip> {\n return Object.fromEntries(skips);\n}\n\nexport function lastCompanionSkip(conversationId: string): CompanionSkip | null {\n return skips.get(conversationId) ?? null;\n}\n\nexport function forgetCompanionSkip(conversationId: string): void {\n if (skips.delete(conversationId)) bus.emit('thinking-changed');\n}\n\n/**\n * Renoncer, en le disant : au journal ET à l'écran.\n *\n * Un seul geste pour les deux, sinon l'un des deux finit par manquer une porte\n * de sortie — c'est exactement ce qui rendait ces raisons invisibles.\n */\nfunction skip(conversationId: string, reason: CompanionSkipReason, note: string, detail?: string): void {\n skips.set(conversationId, { reason, detail, at: Date.now() });\n bus.emit('thinking-changed');\n trace(conversationId, note);\n}\n\n/**\n * Marquer le début d'une relecture. Rend la fonction qui la clôt.\n *\n * Rendre le « fin » plutôt que d'exposer deux fonctions symétriques force le\n * `finally` : le cycle a huit portes de sortie, et une seule oubliée laisserait\n * un badge allumé pour toujours sur une conversation au repos.\n *\n * La fonction rendue est idempotente : appelée deux fois, elle ne décompte\n * qu'une relecture — sinon un `finally` exécuté après un appel manuel\n * éteindrait celle du voisin.\n */\nfunction beginThinking(conversationId: string): () => void {\n // Le compagnon travaille : la raison de son dernier renoncement a cessé\n // d'être vraie. La garder afficherait un fait périmé sous une relecture en\n // cours.\n skips.delete(conversationId);\n const before = thinking.get(conversationId) ?? 0;\n thinking.set(conversationId, before + 1);\n if (before === 0) bus.emit('thinking-changed');\n\n let ended = false;\n return () => {\n if (ended) return;\n ended = true;\n const left = (thinking.get(conversationId) ?? 1) - 1;\n if (left > 0) { thinking.set(conversationId, left); return; }\n thinking.delete(conversationId);\n bus.emit('thinking-changed');\n };\n}\n\n/** Par où le message est passé — ce que le bouton de test rend à l'écran. */\nexport type DeliveryRoute = 'pty' | 'headless' | 'failed';\n\nexport interface CompanionDelivery {\n route: DeliveryRoute;\n at: number;\n}\n\n/**\n * La dernière livraison tentée, par conversation.\n *\n * Sans elle, un échec de transmission est indiscernable d'un compagnon qui\n * n'avait rien à dire : les deux se traduisent par « rien n'arrive ». Cette\n * trace est ce qui permet de trancher depuis l'écran, sans lire les journaux du\n * serveur.\n */\nconst lastDelivery = new Map<string, CompanionDelivery>();\n\nexport function lastCompanionDelivery(conversationId: string): CompanionDelivery | null {\n return lastDelivery.get(conversationId) ?? null;\n}\n\nexport function forgetDelivery(conversationId: string): void {\n lastDelivery.delete(conversationId);\n}\n\n/**\n * Faire parvenir un message à la conversation principale.\n *\n * Deux chemins, selon ce qui la fait tourner. En PTY interactif — le mode par\n * défaut de l'interface — un terminal vit déjà pour cette conversation. Y\n * lancer une session sans tête en parallèle donnerait deux processus sur la\n * même session Claude, et l'utilisateur ne verrait jamais le message dans son\n * terminal. On écrit donc DANS le PTY, comme s'il avait tapé le texte.\n *\n * Extrait de la relance parce que le bouton « tester le pont » emprunte\n * exactement ce chemin : un test qui passerait ailleurs ne prouverait rien.\n */\nexport async function deliverToMainConversation(\n conv: Conversation,\n message: string,\n): Promise<DeliveryRoute> {\n const route = await attemptDelivery(conv, message);\n lastDelivery.set(conv.id, { route, at: Date.now() });\n console.log(`[companion] livraison vers ${conv.id.slice(0, 8)} : ${route}`);\n return route;\n}\n\nasync function attemptDelivery(conv: Conversation, message: string): Promise<DeliveryRoute> {\n if (await writeToLivePty(conv.id, message)) return 'pty';\n\n try {\n const { startCliSession } = await import('../trpc/routers/chat.js');\n await startCliSession(conv.runner, conv.id, message, { jwt: await signJwt() });\n return 'headless';\n } catch (err) {\n // La principale tourne déjà, ou le runner est indisponible : la trouvaille\n // est perdue, mais rien n'est cassé. Le tour suivant en refera une.\n console.warn(`[companion] livraison impossible : ${(err as Error).message}`);\n return 'failed';\n }\n}\n\n/**\n * Écrit dans le PTY vivant d'une conversation, s'il y en a un.\n *\n * Rend `false` quand aucun terminal ne tourne — l'appelant retombe alors sur\n * une session sans tête, qui est le bon chemin dans ce cas.\n */\nasync function writeToLivePty(conversationId: string, message: string): Promise<boolean> {\n const pty = await findLivePty(conversationId);\n if (!pty) return false;\n const write = (data: string) =>\n agentManager.agentMutation(pty.agentId, 'ai.writePty', { spawnId: pty.spawnId, data });\n try {\n await write(flattenForPty(message));\n // Entrée à part, après un souffle.\n //\n // Collé au texte, le retour chariot arrive dans le même bloc que la saisie :\n // l'interface du CLI le lit comme une nouvelle ligne DANS le champ, et le\n // message s'installe sans jamais partir. Envoyé seul, après que le texte a\n // été absorbé, il vaut la touche Entrée. C'est aussi ce que fait un humain\n // qui colle puis valide.\n await new Promise((r) => setTimeout(r, PTY_SUBMIT_DELAY_MS));\n await write('\\r');\n return true;\n } catch {\n return false;\n }\n}\n\n/** Les réglages du compagnon, au niveau du poste. */\nexport async function companionSettings(): Promise<{ enabledByDefault: boolean; prompt?: string }> {\n try {\n const { settingsManager } = await import('./settings-manager.js');\n // Sans coffre : réglage du poste, pas d'une organisation (§9).\n const settings = await settingsManager.getSettings();\n return settings.companion ?? { enabledByDefault: false };\n } catch {\n return { enabledByDefault: false };\n }\n}\n\n/** L'environnement actif du projet d'une conversation, s'il y en a un. */\nasync function activeEnvironmentOf(conv: Conversation): Promise<string | undefined> {\n if (!conv.projectId) return undefined;\n try {\n const ref = await conversationStore.projectRefOf(conv);\n const project = ref ? await projectStore.get(ref) : null;\n return project?.activeEnvironmentId;\n } catch {\n return undefined;\n }\n}\n\n/** Au-delà, on considère que le CLI n'a rien produit. */\nconst ANSWER_TIMEOUT_MS = 15_000;\nconst ANSWER_POLL_MS = 250;\n\n/**\n * La réponse du compagnon, lue dans son transcript.\n *\n * On ne se fie plus au flux d'événements du CLI pour ça. Ce flux alimente bien\n * l'affichage en direct, mais il s'est révélé vide au moment où l'on conclut :\n * la réponse figurait dans le transcript sur disque alors que le contenu\n * accumulé en mémoire valait zéro caractère — le compagnon paraissait muet\n * alors qu'il avait répondu.\n *\n * Le transcript, lui, fait foi : c'est ce que le CLI a réellement écrit.\n * `syncConversationFromClaudeTranscript` le relit en entier et met la\n * conversation à jour ; on attend une réponse non vide plutôt qu'une durée\n * fixe, et on borne l'attente pour ne jamais rester suspendu.\n */\nasync function readCompanionAnswer(companionId: string, runner: 'claude-code' | 'codex' | undefined): Promise<string> {\n const deadline = Date.now() + ANSWER_TIMEOUT_MS;\n // Chaque CLI écrit son propre transcript : lire celui de Claude pour une\n // session Codex ne rendrait jamais rien, et le compagnon paraîtrait muet.\n const sync = runner === 'codex'\n ? syncConversationFromCodexTranscript\n : syncConversationFromClaudeTranscript;\n let last = '';\n while (Date.now() < deadline) {\n await sync(companionId).catch(() => false);\n const conv = await conversationStore.get(companionId);\n const msg = conv?.messages?.[conv.messages.length - 1];\n last = msg?.role === 'assistant' ? (msg.content ?? '') : '';\n if (last.trim()) return last;\n await new Promise((r) => setTimeout(r, ANSWER_POLL_MS));\n }\n return last;\n}\n\n/**\n * LA conversation du compagnon — une seule, gardée d'un tour à l'autre.\n *\n * Une relecture n'est pas un audit isolé : elle porte sur ce qui a changé\n * DEPUIS le tour précédent. Le compagnon a donc besoin de se souvenir de ce\n * qu'il a déjà vu et déjà dit — sinon il redécouvre le même code à chaque fois,\n * et resignale ce qu'il a déjà signalé.\n *\n * Conserver la conversation conserve l'identifiant, donc la reprise : le runner\n * passe `--resume <même id>` (`isResume = messages.length > 0`). C'est ce qui\n * donne la continuité ; ce qui reste borné, c'est ce qu'on lui ENVOIE — les\n * seuls messages qu'il n'a pas encore relus (voir `unreviewedFrom`).\n */\nasync function ensureCompanionConversation(conv: Conversation): Promise<Conversation> {\n if (conv.companionConversationId) {\n const existing = await conversationStore.get(conv.companionConversationId);\n if (existing) return existing;\n }\n\n const companion = await conversationStore.create(\n `Compagnon · ${conv.title}`,\n true,\n undefined,\n undefined,\n conv.companionRunner ?? conv.runner,\n // La carte kanban de la conversation relue, s'il y en a une.\n //\n // `buildCliSystemContext` en tire la description de la carte, ses\n // commentaires et ses pièces jointes — c'est-à-dire ce qui a été DEMANDÉ.\n // Sans elle, le compagnon peut juger qu'un code est correct, jamais qu'un\n // travail est complet : il ignore ce qu'on attendait.\n conv.kanbanBoardId,\n conv.kanbanCardId,\n undefined,\n conv.projectId,\n undefined,\n { isCompanion: true },\n );\n await conversationStore.update(conv.id, { companionConversationId: companion.id });\n return companion;\n}\n\n/**\n * Lancer une relecture à la demande, sans attendre de fin de tour.\n *\n * Le compagnon ne parle que sur un tour annoncé, et plusieurs chemins n'en\n * annoncent aucun : un tour qui ne finit pas sur `end_turn`, une session absente\n * du registre, un runner dont Runeya ne lit pas le transcript. Le compagnon\n * paraît alors éteint alors qu'il est allumé, et rien ne permettait de le\n * relancer autrement qu'en écrivant un message de plus.\n *\n * Deux états repartent à neuf avant de lancer, parce que le geste est celui de\n * l'utilisateur :\n * - le repère de relecture, sinon un clic juste après un « RAS » ne relirait\n * rien et rendrait « rien de neuf à relire » ;\n * - le budget de relances, comme le fait le tour que l'utilisateur relance\n * lui-même (voir `takeRelaunchSlot`).\n *\n * Les gardes de `runCompanionForTurn` restent en place : un compagnon coupé ou\n * une conversation de compagnon ne se relancent pas depuis ici non plus.\n */\nexport async function runCompanionNow(conversationId: string): Promise<void> {\n forgetReviewProgress(conversationId);\n resetRelaunches(conversationId);\n await runCompanionForTurn(conversationId);\n}\n\nlet unsubscribe: (() => void) | null = null;\n\n/** Branche le compagnon sur les fins de tour. Idempotent. */\nexport function initCompanion(): void {\n if (unsubscribe) return;\n unsubscribe = onTurnFinished((conversationId) => {\n runCompanionForTurn(conversationId).catch((err) => {\n console.error('[companion]', (err as Error).message);\n });\n });\n}\n","import { randomUUID } from 'node:crypto';\nimport { readFile, writeFile, rename, mkdir, chmod } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { EventEmitter } from 'node:events';\nimport { allVaultRoots, keyedFilePath, legacyMachineFilePath, vaultFor , vaultIdOfRoot, orgIdOfRoot } from './vault-resolver.js';\nimport { rememberFile, hasChangedSinceRead, wasRead } from './disk-freshness.js';\nimport { rootOfProjectAmong, refOfProjectAmong, rootOfProjectRef, ensureProjectsLoaded } from './vault-registry.js';\nimport { projectStore } from './project-store.js';\nimport { getEncryptionKey } from '../utils/encryption-key.js';\nimport { encryptValue, decryptValue } from './crypto-helpers.js';\n\nconst FILENAME = 'conversations.private.json';\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\nexport type MessagePart =\n | { type: 'thinking'; text: string }\n | { type: 'text'; text: string }\n | { type: 'tool'; id: string; name: string; input?: Record<string, unknown>; rawInput?: string; result?: unknown; isError?: boolean; pending: boolean }\n | { type: 'image'; imageId: string; url: string; mimeType: string; altText?: string };\n\nexport interface Message {\n role: 'user' | 'assistant';\n content: string;\n parts?: MessagePart[];\n contextNote?: string;\n}\n\ninterface ContextUsage {\n inputTokens: number;\n contextWindow: number;\n}\n\ntype WorkflowType = 'scenario';\n\nexport interface Conversation {\n id: string;\n /**\n * The project this conversation belongs to.\n *\n * Optional only for conversations written before the field existed, and for\n * those a migration could not attribute: they stay visible from everywhere\n * rather than being hidden under a project picked at random.\n */\n projectId?: string;\n title: string;\n messages: Message[];\n isCli: boolean;\n runner?: 'claude-code' | 'codex';\n providerId?: string;\n codexThreadId?: string;\n contextUsage?: ContextUsage;\n workflowId?: string;\n workflowType?: WorkflowType;\n kanbanBoardId?: string;\n kanbanCardId?: string;\n /** Le compagnon relit cette conversation en fin de tour (§ compagnon). */\n companionEnabled?: boolean;\n /** Runner du compagnon. Par défaut celui de la conversation qu'il relit. */\n companionRunner?: 'claude-code' | 'codex';\n /** La conversation où le compagnon travaille — son terminal en lecture seule. */\n companionConversationId?: string;\n /**\n * Cette conversation EST un compagnon.\n *\n * Sans ce drapeau, un compagnon dont la conversation porte à son tour\n * `companionEnabled` s'engendrerait lui-même à chaque fin de tour.\n */\n isCompanion?: boolean;\n archived?: boolean;\n /**\n * Rang dans la section épinglée de la barre latérale.\n *\n * Présent = épinglée. C'est le seul marqueur : un booléen à côté pourrait\n * dire « épinglée » sans qu'aucun rang ne dise où, et l'écran devrait\n * trancher. Les rangs se lisent triés, pas comme des indices : ils restent\n * ceux que la conversation portait avant un réordonnancement, seul leur\n * ordre relatif change.\n */\n pinOrder?: number;\n createdAt: number;\n updatedAt: number;\n}\n\nexport type ConversationChangeEvent =\n | { type: 'created'; conversation: Conversation }\n | { type: 'updated'; conversation: Conversation }\n | { type: 'deleted'; id: string }\n /**\n * Tout est à relire — le disque a changé sous le store (adoption d'une clé).\n *\n * Sans conversation à porter : ce n'est pas une conversation qui a bougé,\n * c'est l'ensemble qui n'est plus celui que le client détient. L'abonnement\n * répond en renvoyant un instantané complet, le chemin que le client sait\n * déjà traiter.\n */\n | { type: 'reloaded' };\n\ninterface ConversationOnDisk {\n id: string;\n projectId?: string;\n title: string;\n encryptedMessages: string;\n isCli?: boolean;\n isClaudeCode?: boolean;\n runner?: 'claude-code' | 'codex';\n providerId?: string;\n codexThreadId?: string;\n contextUsage?: ContextUsage;\n workflowId?: string;\n workflowType?: WorkflowType;\n kanbanBoardId?: string;\n kanbanCardId?: string;\n companionEnabled?: boolean;\n companionRunner?: 'claude-code' | 'codex';\n companionConversationId?: string;\n isCompanion?: boolean;\n // Legacy field from before workflowType was introduced — used only for migration in fromDisk\n scenarioId?: string;\n archived?: boolean;\n pinOrder?: number;\n createdAt: number;\n updatedAt: number;\n}\n\n/** The project of the launch directory, if it has one. */\nasync function localProjectId(): Promise<string | null> {\n const projects = await projectStore.list().catch(() => []);\n return projects.find((p) => !p.orgId)?.id ?? null;\n}\n\n// ─── Store ─────────────────────────────────────────────────────────────────\n\nclass ConversationStore {\n private conversations: Map<string, Conversation> = new Map();\n /** Which vault each conversation was read from, until its project is known. */\n private roots: Map<string, string> = new Map();\n /**\n * Les conversations qu'on n'a pas su déchiffrer, par coffre, sous leur forme\n * de disque.\n *\n * Une entrée illisible était simplement ignorée. Tant qu'aucune de ses\n * voisines ne se chargeait, le fichier n'était jamais réécrit et la donnée\n * survivait par accident ; dès qu'une seule s'ouvrait, la sauvegarde suivante\n * réécrivait le fichier avec elle seule — et effaçait les autres. Elles sont\n * donc gardées ici, telles quelles, et réémises à l'identique.\n */\n private unreadable: Map<string, ConversationOnDisk[]> = new Map();\n private loaded = false;\n private readonly events = new EventEmitter().setMaxListeners(0);\n private saveQueue: Promise<void> = Promise.resolve();\n\n private snapshotConversation(conv: Conversation): Conversation {\n return structuredClone(conv);\n }\n\n /**\n * Encrypted, so it lives beside the key that opens it — usually the machine\n * root, but a launch directory owning its own key keeps both together.\n */\n private getFilePath(): Promise<string> {\n return keyedFilePath(FILENAME);\n }\n\n /** Where older versions kept it — read only, so an older Runeya keeps working. */\n private getLegacyFilePath(): string {\n return legacyMachineFilePath(FILENAME);\n }\n\n /**\n * Read the machine file, falling back to the launch directory of old.\n *\n * The legacy file is left in place: the next save writes the new location,\n * and a directory still opened by an older Runeya keeps working meanwhile.\n */\n private async readFromDisk(): Promise<string> {\n try {\n return await readFile(await this.getFilePath(), 'utf-8');\n } catch {\n return readFile(this.getLegacyFilePath(), 'utf-8');\n }\n }\n\n private async ensureDir(): Promise<void> {\n await mkdir(dirname(await this.getFilePath()), { recursive: true });\n }\n\n private encryptMessages(messages: Message[], vault?: string): string {\n return encryptValue(JSON.stringify(messages), vault);\n }\n\n private decryptMessages(encrypted: string, vault?: string): Message[] {\n const json = decryptValue(encrypted, vault);\n return JSON.parse(json) as Message[];\n }\n\n /**\n * Refuser d'écrire dans le coffre d'une organisation sans nommer sa clé.\n *\n * `getEncryptionKey(undefined)` rend la clé machine, et c'est le bon défaut\n * pour le coffre local. Appliqué à un fichier rangé sous `orgs/<org>/`, il\n * produit un chiffré que ce coffre n'emploie pas — lisible par personne à la\n * relecture, et impossible à distinguer d'une corruption.\n *\n * Le cas a été observé plusieurs fois sans qu'on parvienne à nommer le\n * chemin fautif : les garde-fous en place traitent le coffre fermé, le\n * coffre mal résolu et la copie périmée, aucun ne couvrait « coffre non\n * précisé ». Ces écritures-là n'ont jamais fonctionné — elles échouaient en\n * silence, ce qui est pire. On les saute désormais en le disant, avec la\n * pile d'appels qui manquait pour remonter au chemin fautif.\n */\n private isVaultNamed(root: string, vault: string | undefined): boolean {\n const orgId = orgIdOfRoot(root);\n if (!orgId || vault) return true;\n // Signalé avec sa pile — c'est elle qu'on cherche — mais sans jeter : une\n // exception ici avorterait la sauvegarde entière, coffres corrects\n // compris. Une conversation mal aiguillée ne doit pas en empêcher vingt\n // autres d'être écrites.\n console.error(\n `[conversation-store] Écriture ignorée dans le coffre de l'organisation ${orgId}`\n + ` (${root}) : aucune clé d'organisation nommée, le contenu serait chiffré par la`\n + ` clé machine et illisible à la relecture.\\n${new Error('trace').stack}`,\n );\n return false;\n }\n\n private toDisk(conv: Conversation, vault?: string): ConversationOnDisk {\n return {\n id: conv.id,\n projectId: conv.projectId,\n title: conv.title,\n encryptedMessages: this.encryptMessages(conv.messages, vault),\n isCli: conv.isCli,\n runner: conv.runner,\n providerId: conv.providerId,\n codexThreadId: conv.codexThreadId,\n contextUsage: conv.contextUsage,\n workflowId: conv.workflowId,\n workflowType: conv.workflowType,\n kanbanBoardId: conv.kanbanBoardId,\n kanbanCardId: conv.kanbanCardId,\n companionEnabled: conv.companionEnabled,\n companionRunner: conv.companionRunner,\n companionConversationId: conv.companionConversationId,\n isCompanion: conv.isCompanion,\n archived: conv.archived,\n pinOrder: conv.pinOrder,\n createdAt: conv.createdAt,\n updatedAt: conv.updatedAt,\n };\n }\n\n private fromDisk(disk: ConversationOnDisk, vault?: string): Conversation {\n // Derive workflowType from legacy fields for conversations created before this field existed\n const workflowType: WorkflowType | undefined =\n (disk.workflowType === 'scenario' ? 'scenario' : undefined)\n ?? (disk.scenarioId ? 'scenario' : undefined);\n\n return {\n id: disk.id,\n projectId: disk.projectId,\n title: disk.title,\n messages: this.decryptMessages(disk.encryptedMessages, vault),\n isCli: disk.isCli ?? disk.isClaudeCode ?? false,\n runner: disk.runner,\n providerId: disk.providerId,\n codexThreadId: disk.codexThreadId,\n contextUsage: disk.contextUsage,\n workflowId: disk.workflowId ?? disk.scenarioId,\n workflowType,\n kanbanBoardId: disk.kanbanBoardId,\n kanbanCardId: disk.kanbanCardId,\n companionEnabled: disk.companionEnabled,\n companionRunner: disk.companionRunner,\n companionConversationId: disk.companionConversationId,\n isCompanion: disk.isCompanion,\n archived: disk.archived,\n pinOrder: disk.pinOrder,\n createdAt: disk.createdAt,\n updatedAt: disk.updatedAt,\n };\n }\n\n /**\n * Read every project vault, then the homes older versions used.\n *\n * A conversation found in a legacy file is attributed to the local project of\n * that directory — which is what it was about, back when one directory meant\n * one project. With no local project to point at, it is left unattributed\n * rather than filed under a project picked at random.\n */\n private async load(): Promise<void> {\n if (this.loaded) return;\n if (!getEncryptionKey()) {\n return;\n }\n this.loaded = true;\n // Ce qu'un chargement précédent n'avait pas su lire n'a pas à s'ajouter à\n // celui-ci : la liste décrit le disque tel qu'on vient de le lire, et\n // l'empiler doublait le compte à chaque relecture.\n this.unreadable.clear();\n // Les projets d'abord : tant qu'ils sont inconnus, tout se résoudrait vers\n // le coffre de lancement et y serait réécrit à la première sauvegarde —\n // hors de l'organisation propriétaire.\n await ensureProjectsLoaded();\n\n const vaults = await allVaultRoots();\n const legacyPaths = [this.getLegacyFilePath(), await keyedFilePath(FILENAME)];\n const sources: { path: string; legacy: boolean }[] = [\n ...legacyPaths.map((path) => ({ path, legacy: true })),\n ...vaults.map((root) => ({ path: join(root, FILENAME), legacy: false })),\n ];\n\n for (const { path, legacy } of sources) {\n let data: ConversationOnDisk[];\n try {\n data = JSON.parse(await readFile(path, 'utf-8')) as ConversationOnDisk[];\n } catch {\n continue;\n }\n const inherited = legacy ? await localProjectId() : null;\n const root = dirname(path);\n // L'empreinte du fichier tel qu'on vient de le lire : ce qu'on garde de\n // côté ne vaut que tant qu'il n'a pas bougé.\n await rememberFile(path);\n for (const item of data) {\n try {\n const conv = this.fromDisk(item, vaultIdOfRoot(root));\n if (!conv.projectId && inherited) conv.projectId = inherited;\n this.conversations.set(item.id, conv);\n this.roots.set(item.id, root);\n } catch {\n // Illisible ici — pas perdue pour autant. L'entrée brute est mise de\n // côté pour repartir intacte dans le fichier dont elle vient.\n const bucket = this.unreadable.get(root) ?? [];\n bucket.push(item);\n this.unreadable.set(root, bucket);\n }\n }\n }\n }\n\n /**\n * Oublier ce qui a été lu, pour tout relire sous la clé courante.\n *\n * Appelé après l'adoption d'une clé d'organisation : le disque vient d'être\n * re-chiffré sous les pieds du store, si bien que sa mémoire décrit un état\n * qui n'existe plus. La laisser en place n'affichait pas seulement des\n * comptes périmés — la sauvegarde suivante réécrivait l'ancien chiffré\n * par-dessus le nouveau, et défaisait l'adoption.\n */\n resetCache(): void {\n this.conversations.clear();\n this.roots.clear();\n this.unreadable.clear();\n this.loaded = false;\n this.events.emit('changed', { type: 'reloaded' } as ConversationChangeEvent);\n }\n\n /**\n * Combien de conversations ce coffre garde sans savoir les lire, par\n * organisation. La machine elle-même est rangée sous `null`.\n */\n async unreadableByVault(): Promise<Map<string | null, number>> {\n await this.load();\n const out = new Map<string | null, number>();\n for (const [root, items] of this.unreadable) {\n const vault = vaultIdOfRoot(root) ?? null;\n out.set(vault, (out.get(vault) ?? 0) + items.length);\n }\n return out;\n }\n\n /**\n * Write each vault that holds — or held — a conversation.\n *\n * A vault emptied by this save is still written, as an empty list: a\n * conversation that found its project must not survive in the file it was\n * read from.\n */\n private async saveInternal(): Promise<void> {\n if (!getEncryptionKey()) return;\n\n const byRoot = new Map<string, ConversationOnDisk[]>();\n for (const root of this.roots.values()) byRoot.set(root, []);\n\n for (const conv of this.conversations.values()) {\n const root = await this.rootFor(conv);\n this.roots.set(conv.id, root);\n const bucket = byRoot.get(root) ?? [];\n // Chiffré par la clé du coffre où la conversation atterrit : celle de son\n // organisation quand elle appartient à un projet synchronisé.\n const vault = vaultIdOfRoot(root);\n if (!this.isVaultNamed(root, vault)) continue;\n bucket.push(this.toDisk(conv, vault));\n byRoot.set(root, bucket);\n }\n\n // Ce qui ne s'était pas ouvert repart d'abord, inchangé : un coffre qui ne\n // contient plus que des illisibles doit être réécrit lui aussi, sans quoi\n // le vidage plus haut le laisserait à sa forme d'origine par chance et non\n // par décision.\n for (const [root, items] of this.unreadable) {\n byRoot.set(root, [...items, ...(byRoot.get(root) ?? [])]);\n }\n\n // Les coffres réparés sous nos pieds — une clé adoptée, une conversion\n // menée à la main — ne sont pas réécrits du tout. Réémettre notre copie\n // déferait la réparation ; l'omettre effacerait ce qu'elle protège. Ne pas\n // toucher au fichier est la seule issue qui ne perd rien.\n const stale = new Set<string>();\n for (const root of byRoot.keys()) {\n // Connu ET modifié : un coffre encore vierge n'a rien à protéger, et le\n // sauter reviendrait à ne jamais écrire son premier fichier.\n const path = join(root, FILENAME);\n if (wasRead(path) && await hasChangedSinceRead(path)) stale.add(root);\n }\n\n for (const [root, data] of byRoot) {\n const filePath = join(root, FILENAME);\n if (stale.has(root)) {\n console.warn(\n `[conversation-store] ${filePath} a changé depuis sa lecture —`\n + ' écriture ignorée pour ne pas écraser une réparation faite en dehors du serveur.',\n );\n continue;\n }\n await mkdir(root, { recursive: true });\n const tmpPath = `${filePath}.tmp`;\n await writeFile(tmpPath, JSON.stringify(data, null, 2), 'utf-8');\n await rename(tmpPath, filePath);\n await chmod(filePath, 0o600);\n // Notre écriture devient la référence, sinon la suivante la prendrait\n // pour une modification extérieure.\n await rememberFile(filePath);\n }\n }\n\n /**\n * The vault a conversation is written to: its project's.\n *\n * One that names no project stays where it was read, falling back to the\n * key's own directory — never lost for want of an owner.\n */\n private async rootFor(conv: Conversation): Promise<string> {\n const known = this.roots.get(conv.id) ?? null;\n // Le coffre de lecture tranche quand l'id du projet répond pour plusieurs\n // organisations : c'est le couple, écrit par l'arborescence.\n if (conv.projectId) return rootOfProjectAmong(conv.projectId, known);\n return known ?? dirname(await keyedFilePath(FILENAME));\n }\n\n /**\n * Le projet d'une conversation, organisation comprise.\n *\n * Ce que la conversation ne dit pas d'elle-même : elle ne nomme que l'id.\n * Le coffre d'où elle vient fournit l'organisation, et c'est ce couple qu'il\n * faut pour retrouver le répertoire de travail du projet.\n */\n async projectRefOf(conv: Conversation): Promise<{ id: string; orgId: string | null } | null> {\n if (!conv.projectId) return null;\n const known = this.roots.get(conv.id) ?? null;\n const ref = refOfProjectAmong(conv.projectId, known);\n return { id: ref.id, orgId: ref.orgId ?? null };\n }\n\n /**\n * Réécrire chaque coffre, en déplaçant ce qui appartient désormais ailleurs.\n *\n * Appelé quand un projet a changé de coffre : ses conversations sont écrites\n * dans le nouveau, et le fichier d'où elles viennent est réécrit sans elles.\n */\n async relocateAll(): Promise<void> {\n await this.load();\n await this.save();\n }\n\n private async save(): Promise<void> {\n const write = this.saveQueue.then(() => this.saveInternal());\n this.saveQueue = write.catch(() => {});\n return write;\n }\n\n async list(): Promise<Conversation[]> {\n await this.load();\n return Array.from(this.conversations.values())\n .sort((a, b) => b.updatedAt - a.updatedAt);\n }\n\n /**\n * Les conversations d'un projet.\n *\n * Celle qui ne nomme aucun projet — écrite avant le champ, ou qu'une\n * migration n'a pas su attribuer — n'est rendue que dans les projets de son\n * propre coffre : elle reste atteignable là où elle a été écrite, sans se\n * déverser dans toutes les organisations de la machine.\n */\n async listForProject(ref: { id: string; orgId?: string | null }): Promise<Conversation[]> {\n const belongs = await this.matcherForProject(ref);\n return (await this.list()).filter(belongs);\n }\n\n /**\n * Le test d'appartenance à un projet, préparé une fois.\n *\n * Le flux temps réel en a besoin autant que la liste : une conversation qui\n * arrive par événement doit passer le même filtre, sans quoi elle se\n * réinvite dans un projet qui ne l'a jamais demandée.\n */\n async matcherForProject(ref: { id: string; orgId?: string | null }): Promise<(conv: Conversation) => boolean> {\n await this.load();\n let projectRoot: string | null = null;\n try {\n projectRoot = ref.orgId === undefined\n ? rootOfProjectAmong(ref.id, null)\n : rootOfProjectRef({ id: ref.id, orgId: ref.orgId ?? null });\n } catch {\n // Id ambigu sans organisation : aucun coffre ne tranche, on ne cache rien.\n projectRoot = null;\n }\n return (conv: Conversation) => {\n if (conv.projectId) return conv.projectId === ref.id;\n if (!projectRoot) return true;\n return this.roots.get(conv.id) === projectRoot;\n };\n }\n\n async get(id: string): Promise<Conversation | null> {\n await this.load();\n return this.conversations.get(id) ?? null;\n }\n\n async create(title?: string, isCli = false, workflowId?: string, workflowType?: WorkflowType, runner?: 'claude-code' | 'codex', kanbanBoardId?: string, kanbanCardId?: string, providerId?: string, projectId?: string, orgId?: string | null, extra?: { isCompanion?: boolean; companionEnabled?: boolean; companionRunner?: 'claude-code' | 'codex' }): Promise<Conversation> {\n await this.load();\n const now = Date.now();\n const conv: Conversation = {\n id: randomUUID(),\n projectId,\n title: title ?? 'New conversation',\n messages: [],\n isCli,\n runner,\n providerId,\n workflowId,\n workflowType,\n kanbanBoardId,\n kanbanCardId,\n isCompanion: extra?.isCompanion,\n companionEnabled: extra?.companionEnabled,\n companionRunner: extra?.companionRunner,\n createdAt: now,\n updatedAt: now,\n };\n this.conversations.set(conv.id, conv);\n // `orgId` ne fait pas partie de la conversation : il dit dans quel coffre la\n // poser. Une conversation neuve n'a pas de provenance, et l'id du projet\n // seul peut répondre pour une organisation comme pour l'autre.\n if (projectId && orgId !== undefined) {\n this.roots.set(conv.id, vaultFor({ id: projectId, orgId }).root);\n }\n await this.save();\n this.events.emit('changed', { type: 'created', conversation: this.snapshotConversation(conv) } as ConversationChangeEvent);\n return conv;\n }\n\n async update(id: string, patch: { title?: string; messages?: Message[]; contextUsage?: ContextUsage; workflowId?: string; archived?: boolean; codexThreadId?: string; companionEnabled?: boolean; companionRunner?: 'claude-code' | 'codex'; companionConversationId?: string }, options?: { silent?: boolean; touch?: boolean }): Promise<Conversation | null> {\n await this.load();\n const conv = this.conversations.get(id);\n if (!conv) return null;\n if (patch.title !== undefined) conv.title = patch.title;\n if (patch.messages !== undefined) conv.messages = patch.messages;\n if (patch.contextUsage !== undefined) conv.contextUsage = patch.contextUsage;\n if (patch.workflowId !== undefined) conv.workflowId = patch.workflowId;\n if (patch.archived !== undefined) conv.archived = patch.archived;\n if (patch.codexThreadId !== undefined) conv.codexThreadId = patch.codexThreadId;\n if (patch.companionEnabled !== undefined) conv.companionEnabled = patch.companionEnabled;\n if (patch.companionRunner !== undefined) conv.companionRunner = patch.companionRunner;\n if (patch.companionConversationId !== undefined) conv.companionConversationId = patch.companionConversationId;\n if (options?.touch !== false) conv.updatedAt = Date.now();\n await this.save();\n if (!options?.silent) {\n this.events.emit('changed', { type: 'updated', conversation: this.snapshotConversation(conv) } as ConversationChangeEvent);\n }\n return conv;\n }\n\n /**\n * Synchronously update the last assistant message content in memory, then schedule a save.\n * Used by activeSessionRegistry.persistNow() to avoid the async get() → .then() race.\n */\n updateLastAssistantContent(id: string, content: string, parts?: MessagePart[]): void {\n const conv = this.conversations.get(id);\n if (!conv) return;\n const lastMsg = conv.messages[conv.messages.length - 1];\n if (!lastMsg || lastMsg.role !== 'assistant') return;\n lastMsg.content = content;\n if (parts !== undefined) lastMsg.parts = parts;\n conv.updatedAt = Date.now();\n this.save().catch(() => {});\n }\n\n async listByKanbanBoardId(boardId: string): Promise<Conversation[]> {\n await this.load();\n return Array.from(this.conversations.values()).filter(c => c.kanbanBoardId === boardId);\n }\n\n async listByKanbanCardId(cardId: string): Promise<Conversation[]> {\n await this.load();\n return Array.from(this.conversations.values()).filter(c => c.kanbanCardId === cardId);\n }\n\n /**\n * Épingler ou détacher une conversation.\n *\n * Épinglée, elle se pose en fin de section : ce que l'utilisateur avait\n * rangé garde sa place. Détachée, elle retombe dans la liste par récence —\n * sans que sa date d'activité bouge, sinon un simple détachement la ferait\n * remonter en tête comme si on venait d'y travailler.\n */\n async setPinned(id: string, pinned: boolean): Promise<Conversation | null> {\n await this.load();\n const conv = this.conversations.get(id);\n if (!conv) return null;\n if (pinned === (conv.pinOrder !== undefined)) return conv;\n\n if (!pinned) {\n delete conv.pinOrder;\n } else {\n const ranks = Array.from(this.conversations.values())\n .map(c => c.pinOrder)\n .filter((r): r is number => r !== undefined);\n conv.pinOrder = ranks.length > 0 ? Math.max(...ranks) + 1 : 0;\n }\n\n await this.save();\n this.events.emit('changed', { type: 'updated', conversation: this.snapshotConversation(conv) } as ConversationChangeEvent);\n return conv;\n }\n\n /**\n * Réordonner les conversations épinglées `ids`, dans cet ordre.\n *\n * Seules celles-là bougent : les rangs qu'elles occupaient déjà sont\n * redistribués entre elles. Un appel ne peut donc pas déplacer — ni\n * détacher — une épinglée absente de la liste, ce qui compte parce que\n * l'écran ne voit que les conversations du projet courant et ignore les\n * autres. Un id inconnu ou non épinglé est ignoré.\n */\n async reorderPinned(ids: string[]): Promise<void> {\n await this.load();\n // Dédupliqué : deux fois le même id ferait absorber deux places à une seule\n // conversation, et l'ordre rendu ne serait plus celui qui a été demandé.\n const targets = Array.from(new Set(ids))\n .map(id => this.conversations.get(id))\n .filter((c): c is Conversation => !!c && c.pinOrder !== undefined);\n if (targets.length === 0) return;\n\n const ranks = targets.map(c => c.pinOrder!).sort((a, b) => a - b);\n targets.forEach((conv, i) => { conv.pinOrder = ranks[i]!; });\n\n await this.save();\n for (const conv of targets) {\n this.events.emit('changed', { type: 'updated', conversation: this.snapshotConversation(conv) } as ConversationChangeEvent);\n }\n }\n\n async delete(id: string): Promise<void> {\n await this.load();\n const existed = this.conversations.delete(id);\n await this.save();\n if (existed) {\n this.events.emit('changed', { type: 'deleted', id } as ConversationChangeEvent);\n }\n }\n\n onChanged(handler: (event: ConversationChangeEvent) => void): void {\n this.events.on('changed', handler);\n }\n\n offChanged(handler: (event: ConversationChangeEvent) => void): void {\n this.events.off('changed', handler);\n }\n}\n\nexport const conversationStore = new ConversationStore();\n","import { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport { readFile, writeFile, rename, mkdir, chmod, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Project, CreateProject, UpdateProject } from '@runeya/packages-shared';\nimport { ProjectSchema } from '@runeya/packages-shared';\nimport { environmentStore } from './environment-store.js';\nimport { serviceStore } from './service-store.js';\nimport { ensureDataGitignore } from '../utils/ensure-data-gitignore.js';\nimport { localVaultRoot, listCloudVaults, vaultFor, ensureLocalVault } from './vault-resolver.js';\nimport { registerProjectLoader, setProjectVault, setProjectServices, forgetProject } from './vault-registry.js';\nimport { projectKey, AmbiguousProjectError, type ProjectRef } from './project-key.js';\nimport { projectPathStore } from './project-path-store.js';\n\n/**\n * One project per vault, so the file holds a single object — in the local vault\n * as in a cloud one.\n */\nconst FILENAME = 'project.json';\n/**\n * The local file before a vault meant one project. Read once, migrated to\n * `project.json`, and kept on disk under a `.bak` name rather than deleted:\n * nothing is thrown away on the user's behalf.\n */\nconst LEGACY_FILENAME = 'projects.json';\n\n/**\n * A project as persisted: `activeEnvironmentId` is runtime-only and never hits\n * disk. This is also exactly what the cloud mirrors — the sync shares what git\n * shares, so \"is it persisted?\" is the only question that decides it.\n */\nfunction toDiskForm(project: Project): Omit<Project, 'activeEnvironmentId'> {\n const { activeEnvironmentId: _runtimeOnly, ...rest } = project;\n return rest;\n}\n\n/**\n * What the launch directory holds: its own project, plus any cloud projects an\n * older version left in the legacy list.\n *\n * Reads `project.json`, and falls back to the `projects.json` of older versions\n * so an existing directory keeps working. A legacy file describing several\n * local projects has no valid placement under one-project-per-vault, so it is\n * refused loudly instead of being silently narrowed to its first entry — losing\n * a project quietly is worse than refusing to start.\n *\n * Cloud projects found in that list are handed back too, so they can be moved\n * into their own vault rather than quietly dropped from the machine.\n */\nasync function readLocalProject(): Promise<{ local: Project | null; strayCloud: Project[] }> {\n const root = localVaultRoot();\n let current: Project | null = null;\n try {\n const raw = await readFile(join(root, FILENAME), 'utf-8');\n const parsed = ProjectSchema.safeParse(JSON.parse(raw));\n if (parsed.success) current = parsed.data;\n } catch {\n // Fall through to the legacy list.\n }\n\n let legacy: Project[];\n try {\n legacy = JSON.parse(await readFile(join(root, LEGACY_FILENAME), 'utf-8')) as Project[];\n } catch {\n return { local: current, strayCloud: [] };\n }\n\n const strayCloud = legacy.filter((p) => p.orgId);\n if (current) return { local: current, strayCloud };\n\n // Cloud projects in the legacy list live in their own vault now; only the\n // local ones compete for this directory.\n const locals = legacy.filter((p) => !p.orgId);\n if (locals.length > 1) {\n const names = locals.map((p) => `\"${p.name}\" (${p.id})`).join(', ');\n throw new Error(\n `${join(root, LEGACY_FILENAME)} holds ${locals.length} local projects: ${names}. ` +\n 'A directory now holds a single local project — move the others to their own directory, ' +\n 'or into an organization, then start again.',\n );\n }\n return { local: locals[0] ?? null, strayCloud };\n}\n\n/**\n * The cloud projects materialized on this machine, read from their vaults.\n *\n * The id and org come from the path, not from the file: the directory layout is\n * what the rest of the system trusts, so a file whose contents disagree with\n * where it sits must not win.\n */\nasync function readCloudProjects(): Promise<{ project: Project; root: string }[]> {\n const found: { project: Project; root: string }[] = [];\n for (const { orgId, projectId, root } of await listCloudVaults()) {\n try {\n const raw = await readFile(join(root, FILENAME), 'utf-8');\n const parsed = ProjectSchema.safeParse({ ...JSON.parse(raw), id: projectId, orgId });\n if (parsed.success) found.push({ project: parsed.data, root });\n } catch {\n // Not a readable project vault — ignore it.\n }\n }\n return found;\n}\n\n/**\n * A directory already holds its local project.\n *\n * Carries a stable `code` alongside the sentence: the sentence is for logs and\n * for API consumers, the code is what the UI translates — a message built here\n * could only ever be in one language.\n */\nexport class LocalProjectExistsError extends Error {\n readonly code = 'LOCAL_PROJECT_EXISTS';\n\n constructor(readonly projectName: string) {\n super(\n `This directory already holds the local project \"${projectName}\". ` +\n 'A directory holds one local project: use another directory, or create the project in an organization.',\n );\n this.name = 'LocalProjectExistsError';\n }\n}\n\nclass ProjectStore {\n /**\n * Keyed by the (organization, project) couple, never by the project id alone:\n * the same id can name a different project in each organization, and keying\n * by id let the second one read from disk overwrite the first — then `save()`\n * wrote that survivor back into both vaults.\n */\n private projects: Map<string, Project> = new Map();\n private loaded = false;\n /** Emits `'saved'` after every persisted mutation, for the cloud sync bridge. */\n readonly events = new EventEmitter();\n\n private getFilePath(root: string = localVaultRoot()): string {\n return join(root, FILENAME);\n }\n\n private async writeAtomic(filePath: string, data: string): Promise<void> {\n const tmpPath = filePath + '.tmp';\n await writeFile(tmpPath, data, 'utf-8');\n await rename(tmpPath, filePath);\n await chmod(filePath, 0o600);\n }\n\n /** Adopt a project read from disk, remembering which vault it came from. */\n private async absorb(p: Project, root: string): Promise<void> {\n const projectEnvs = await environmentStore.getByProjectId(p.id).catch(() => []);\n const defaultEnv = projectEnvs.find(e => e.default);\n this.projects.set(projectKey(p), {\n ...p,\n environmentIds: p.environmentIds ?? [],\n groupOrder: p.groupOrder ?? [],\n // Prefer the env marked default, fall back to first env\n activeEnvironmentId: defaultEnv?.id ?? p.environmentIds?.[0],\n });\n setProjectVault(p, root);\n setProjectServices(p, p.serviceIds ?? []);\n }\n\n /**\n * Read every vault: the launch directory, then each organization's projects.\n *\n * Cloud vaults are discovered by walking `~/.runeya/orgs/*&#47;projects/*` rather\n * than from an index. An index is one more thing that can disagree with the\n * disk — and a project present on disk but missing from it is exactly the\n * \"already here, still shown as downloadable\" bug.\n */\n private async load(): Promise<void> {\n if (this.loaded) return;\n // Marked before the awaits below: absorbing a project reads environments,\n // which can come back through the registry and re-enter this method.\n this.loaded = true;\n const { local, strayCloud } = await readLocalProject();\n if (local) await this.absorb(local, localVaultRoot());\n // Cloud projects an older version kept in the launch directory. Absorbed\n // first so that a vault holding the same project — necessarily fresher —\n // overrides them just below.\n let migrated = false;\n for (const project of strayCloud) {\n try {\n await this.absorb(project, vaultFor(project).root);\n migrated = true;\n } catch {\n // An id that cannot form a vault path: leave it in the legacy file.\n }\n }\n for (const { project, root } of await readCloudProjects()) {\n // A cloud project left over in the local vault by an older version is\n // superseded by the copy in its own vault.\n await this.absorb(project, root);\n }\n\n // Carry the launch directory over in one go rather than on the next edit:\n // a cloud project half-moved — its record in the vault, its services still\n // in `.runeya` — is the confusing state this migration exists to avoid.\n if (migrated) {\n await this.save();\n await serviceStore.relocateAll();\n await environmentStore.relocateAll();\n }\n }\n\n /**\n * Persist every vault this store knows about.\n *\n * Writing all of them rather than only the one that changed keeps a project\n * that moved between vaults — a local project adopted by an org, a cloud\n * project pulled for the first time — from lingering in the vault it left.\n */\n private async save(): Promise<void> {\n const locals: Project[] = [];\n const cloud: { project: Project; root: string }[] = [];\n for (const project of this.projects.values()) {\n if (!project.orgId) locals.push(project);\n else cloud.push({ project, root: vaultFor(project).root });\n }\n\n if (locals.length > 1) {\n // Nothing here can place a second local project, and writing the first\n // while dropping the rest would lose data without a word. The refusal\n // mirrors the one at load time.\n const names = locals.map((p) => `\"${p.name}\" (${p.id})`).join(', ');\n throw new Error(\n `Refusing to save ${locals.length} local projects in one directory: ${names}. ` +\n 'A directory holds a single local project.',\n );\n }\n\n const [local] = locals;\n if (local) {\n // Writing a local project is what brings `.runeya` into existence.\n await ensureLocalVault();\n await ensureDataGitignore(localVaultRoot());\n await this.writeAtomic(this.getFilePath(), JSON.stringify(toDiskForm(local), null, 2));\n } else {\n // No local project left: the file would otherwise keep describing one.\n await rm(this.getFilePath(), { force: true }).catch(() => {});\n }\n // The legacy list has been carried over — park it rather than delete it.\n await rename(join(localVaultRoot(), LEGACY_FILENAME), join(localVaultRoot(), LEGACY_FILENAME + '.bak'))\n .catch(() => {});\n\n for (const { project, root } of cloud) {\n await mkdir(root, { recursive: true });\n await this.writeAtomic(this.getFilePath(root), JSON.stringify(toDiskForm(project), null, 2));\n setProjectVault(project, root);\n }\n\n this.events.emit('saved');\n }\n\n /**\n * The project a caller means, from a couple or from a bare id.\n *\n * A ref carrying its `orgId` — even an explicit `null` for a local project —\n * names exactly one project and is looked up directly. A bare id is what a\n * contained entity (an environment, a conversation) still carries, and it is\n * resolved only while it answers for a single project: electing one of two\n * would write into the wrong organization's vault.\n */\n private resolveKey(target: ProjectRef | string): string | null {\n if (typeof target !== 'string' && target.orgId !== undefined) return projectKey(target);\n const id = typeof target === 'string' ? target : target.id;\n const matches = Array.from(this.projects.values()).filter((p) => p.id === id);\n if (matches.length > 1) {\n throw new AmbiguousProjectError(id, matches.map((p) => p.orgId ?? null));\n }\n return matches[0] ? projectKey(matches[0]) : null;\n }\n\n private find(target: ProjectRef | string): { key: string; project: Project } | null {\n const key = this.resolveKey(target);\n const project = key ? this.projects.get(key) : undefined;\n return key && project ? { key, project } : null;\n }\n\n async list(): Promise<Project[]> {\n await this.load();\n return Array.from(this.projects.values());\n }\n\n async get(target: ProjectRef | string): Promise<Project | null> {\n await this.load();\n return this.find(target)?.project ?? null;\n }\n\n async create(input: CreateProject): Promise<Project> {\n await this.load();\n if (!input.orgId) {\n // A local vault *is* its project: the directory holds the code the\n // project describes. A second one here would have nowhere of its own to\n // live, and \"local\" would go back to meaning \"in this folder\" rather than\n // \"on this machine\" — the ambiguity vaults exist to remove.\n const existing = Array.from(this.projects.values()).find((p) => !p.orgId);\n if (existing) {\n throw new LocalProjectExistsError(existing.name);\n }\n }\n const project: Project = {\n id: randomUUID(),\n orgId: input.orgId ?? null,\n name: input.name,\n description: input.description ?? '',\n serviceIds: input.serviceIds ?? [],\n environmentIds: input.environmentIds ?? [],\n activeEnvironmentId: input.activeEnvironmentId,\n groupOrder: input.groupOrder ?? [],\n };\n this.projects.set(projectKey(project), project);\n setProjectVault(project, vaultFor(project).root);\n setProjectServices(project, project.serviceIds);\n await this.save();\n return project;\n }\n\n /**\n * `input.orgId` names which project is being updated, it does not move it:\n * a project is born local or in an organization and stays there, so treating\n * it as a new value would silently relocate a vault instead of editing it.\n */\n async update(input: UpdateProject): Promise<Project | null> {\n await this.load();\n const found = this.find(input);\n if (!found) return null;\n const { key, project: existing } = found;\n const updated: Project = {\n ...existing,\n ...(input.name !== undefined && { name: input.name }),\n ...(input.description !== undefined && { description: input.description }),\n ...(input.serviceIds !== undefined && { serviceIds: input.serviceIds }),\n ...(input.environmentIds !== undefined && { environmentIds: input.environmentIds }),\n ...(input.activeEnvironmentId !== undefined && { activeEnvironmentId: input.activeEnvironmentId }),\n ...(input.groupOrder !== undefined && { groupOrder: input.groupOrder }),\n };\n this.projects.set(key, updated);\n setProjectVault(updated, vaultFor(updated).root);\n setProjectServices(updated, updated.serviceIds);\n await this.save();\n return updated;\n }\n\n /**\n * Faire passer le projet local de ce dossier dans une organisation.\n *\n * Le seul déplacement de coffre qu'on s'autorise, et il ne va que dans ce\n * sens : le projet quitte `<cwd>/.runeya` pour\n * `~/.runeya/orgs/<org>/projects/<id>/`, en gardant son identifiant. Il n'y a\n * donc jamais deux copies à réconcilier — c'est ce qui rendait « publier »\n * dangereux tant que le projet restait aussi local.\n *\n * Refusé si l'organisation porte déjà ce projet : la copie d'ici écraserait\n * celle du cloud, et ces deux histoires ne sont pas à nous de les fusionner.\n *\n * Ce qui suit le projet est écrit par les stores eux-mêmes : `save()` réécrit\n * chaque coffre — le fichier local disparaît, celui de l'org apparaît — puis\n * les contenus sont relogés depuis le registre, qui pointe désormais ailleurs.\n */\n async moveToOrg(projectId: string, orgId: string): Promise<Project> {\n await this.load();\n const local = this.projects.get(projectKey({ id: projectId, orgId: null }));\n if (!local) throw new Error('PROJECT_NOT_LOCAL');\n if (this.projects.get(projectKey({ id: projectId, orgId }))) {\n throw new Error('PROJECT_ALREADY_IN_ORG');\n }\n\n const moved: Project = { ...local, orgId };\n this.projects.delete(projectKey(local));\n forgetProject(local);\n this.projects.set(projectKey(moved), moved);\n setProjectVault(moved, vaultFor(moved).root);\n setProjectServices(moved, moved.serviceIds ?? []);\n await this.save();\n\n // Les services nomment leur organisation ; les environnements et les\n // conversations, non — eux suivent le coffre de leur projet.\n await serviceStore.reassignOrg(moved.serviceIds ?? [], orgId);\n await environmentStore.relocateAll();\n return moved;\n }\n\n async delete(target: ProjectRef | string): Promise<boolean> {\n await this.load();\n const found = this.find(target);\n if (!found) return false;\n const { key, project } = found;\n this.projects.delete(key);\n await this.save();\n await this.discardVault(project);\n // Le répertoire cesse d'être réservé. `projectAt` s'en sert pour interdire\n // que deux projets partagent un dossier : une entrée laissée derrière un\n // projet supprimé continuait de le retenir, et le refus qui en découlait\n // nommait un projet qui n'existe plus.\n await projectPathStore.clear(project);\n return true;\n }\n\n /**\n * Remove what a deleted cloud project left behind.\n *\n * Its vault is a directory of its own, so dropping the project means dropping\n * the directory — otherwise `readCloudVaults` would resurrect it on the next\n * load. Local projects share their vault with the directory's own files and\n * are only removed from the list.\n */\n private async discardVault(project: Project): Promise<void> {\n forgetProject(project);\n if (!project.orgId) return;\n await rm(vaultFor(project).root, { recursive: true, force: true }).catch(() => {});\n }\n\n /**\n * Stop holding a project on this machine, leaving the doc alone.\n *\n * Memory only: the caller removes the vault itself. Its contents are dropped\n * too, because a service or environment surviving in memory would be written\n * back on the next save — and, its project being unknown by then, into the\n * local vault.\n */\n async forgetLocally(target: ProjectRef | string): Promise<void> {\n await this.load();\n const found = this.find(target);\n if (!found) return;\n const { key, project } = found;\n this.projects.delete(key);\n forgetProject(project);\n await serviceStore.forgetLocally(project.serviceIds ?? []);\n await environmentStore.forgetByProject(project.id);\n // Ce poste ne tient plus ce projet — son coffre vient d'être effacé — donc\n // il ne réserve plus de répertoire non plus. Le seul appelant est le\n // retrait volontaire d'un projet suivi, jamais un état transitoire.\n await projectPathStore.clear(project);\n }\n\n /**\n * Le projet qui porte un service, ou `null` s'il n'est rattaché à aucun.\n *\n * Un service n'appartient qu'à un projet : la recherche s'arrête au premier.\n */\n async ofService(serviceId: string): Promise<Project | null> {\n await this.load();\n for (const project of this.projects.values()) {\n if (project.serviceIds.includes(serviceId)) return project;\n }\n return null;\n }\n\n /** All projects linked to a given organization (for the sync bridge). */\n async listByOrg(orgId: string): Promise<Project[]> {\n await this.load();\n return Array.from(this.projects.values()).filter((p) => p.orgId === orgId);\n }\n\n /** Projects of an organization in their on-disk form (what the cloud mirrors). */\n async listDiskFormByOrg(orgId: string): Promise<Omit<Project, 'activeEnvironmentId'>[]> {\n return (await this.listByOrg(orgId)).map(toDiskForm);\n }\n\n /**\n * Apply a project received from the cloud, in its on-disk form.\n *\n * Validated rather than trusted: a peer on another version — or a tampered\n * doc — must not be able to write arbitrary shapes into `projects.json`.\n * `activeEnvironmentId` is runtime-only and stays whatever it is locally.\n * Diff-guarded: an identical project neither saves nor emits (loop-safe).\n * Returns whether a new local project was created, so the caller can\n * bootstrap project-local state (e.g. a default environment).\n */\n async applyRemoteUpsert(\n id: string,\n fields: unknown,\n ): Promise<{ project: Project; created: boolean } | null> {\n await this.load();\n const parsed = ProjectSchema.safeParse({ ...(fields as object), id });\n if (!parsed.success) {\n console.error('[project-store] Rejected remote project %s:', id, parsed.error.message);\n return null;\n }\n const incoming = toDiskForm(parsed.data);\n // The couple, taken from the validated project itself: the bridge stamps\n // the org of the connection that carried it, so this key names the vault\n // the doc actually belongs to.\n const key = projectKey(parsed.data);\n const existing = this.projects.get(key);\n if (existing) {\n if (JSON.stringify(toDiskForm(existing)) === JSON.stringify(incoming)) {\n return { project: existing, created: false };\n }\n const updated: Project = { ...incoming, activeEnvironmentId: existing.activeEnvironmentId };\n this.projects.set(key, updated);\n setProjectVault(updated, vaultFor(updated).root);\n setProjectServices(updated, updated.serviceIds);\n await this.save();\n return { project: updated, created: false };\n }\n const project: Project = { ...incoming, activeEnvironmentId: undefined };\n this.projects.set(key, project);\n setProjectVault(project, vaultFor(project).root);\n setProjectServices(project, project.serviceIds);\n await this.save();\n return { project, created: true };\n }\n\n /** Delete a project that was removed on another peer. */\n async applyRemoteDelete(target: ProjectRef | string): Promise<boolean> {\n await this.load();\n const found = this.find(target);\n if (!found) return false;\n const { key, project } = found;\n this.projects.delete(key);\n await this.save();\n await this.discardVault(project);\n return true;\n }\n\n async addService(target: ProjectRef | string, serviceId: string): Promise<Project | null> {\n await this.load();\n const found = this.find(target);\n if (!found) return null;\n const { key, project } = found;\n if (project.serviceIds.includes(serviceId)) return project;\n const updated = { ...project, serviceIds: [...project.serviceIds, serviceId] };\n this.projects.set(key, updated);\n setProjectServices(updated, updated.serviceIds);\n await this.save();\n return updated;\n }\n\n async removeService(target: ProjectRef | string, serviceId: string): Promise<Project | null> {\n await this.load();\n const found = this.find(target);\n if (!found) return null;\n const { key, project } = found;\n const updated = { ...project, serviceIds: project.serviceIds.filter(id => id !== serviceId) };\n this.projects.set(key, updated);\n setProjectServices(updated, updated.serviceIds);\n await this.save();\n return updated;\n }\n\n async addEnvironment(target: ProjectRef | string, environmentId: string): Promise<Project | null> {\n await this.load();\n const found = this.find(target);\n if (!found) return null;\n const { key, project } = found;\n if (project.environmentIds?.includes(environmentId)) return project;\n const updated = {\n ...project,\n environmentIds: [...(project.environmentIds ?? []), environmentId],\n };\n this.projects.set(key, updated);\n await this.save();\n return updated;\n }\n\n async removeEnvironment(target: ProjectRef | string, environmentId: string): Promise<Project | null> {\n await this.load();\n const found = this.find(target);\n if (!found) return null;\n const { key, project } = found;\n const updated = {\n ...project,\n environmentIds: (project.environmentIds ?? []).filter(id => id !== environmentId),\n // Clear activeEnvironmentId if it was the removed environment\n ...(project.activeEnvironmentId === environmentId && { activeEnvironmentId: undefined }),\n };\n this.projects.set(key, updated);\n await this.save();\n return updated;\n }\n\n async setActiveEnvironment(target: ProjectRef | string, environmentId: string): Promise<Project | null> {\n await this.load();\n const found = this.find(target);\n if (!found) return null;\n const { key, project } = found;\n\n // Update activeEnvironmentId in memory only (not persisted to JSON)\n // Do NOT update updatedAt since this is a runtime preference, not a project modification\n const updated: Project = {\n ...project,\n activeEnvironmentId: environmentId,\n };\n\n this.projects.set(key, updated);\n // No save() call - activeEnvironmentId is runtime-only\n return updated;\n }\n}\n\nexport const projectStore = new ProjectStore();\n\n// Content stores resolve an entity's vault through the registry, which only\n// knows what the projects say — so it must be able to make them load first.\nregisterProjectLoader(() => projectStore.list().then(() => undefined));\n","import { writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nconst GITIGNORE_CONTENT = `\n#################\n# Do not edit this file #\n#################\n\n\n# Encryption key (contains secrets — never commit)\n.encryption.key\n.encryption.key.enc\n\n\n# Key verification canary — MUST be committed so new team members can validate their encryption key\n!.runeya-verify\n\n# This machine's cloud session token (encrypted, and specific to this install)\ncloud-session.json\n\n# Backup in local only\nbackup-*\n\n# Per-user environment overrides (never commit — each dev has their own)\nenvironment-overrides.json\n\n# Private user data (conversations, personal state — never commit)\n*.private\n*.private.json*\nconversations/\nconversations.private.json\n\n\n\n# Runtime logs\n*.log\n*.log.*\n`;\n\n/**\n * Writes data/.gitignore on every startup to keep it in sync with the codebase.\n */\nexport async function ensureDataGitignore(dataDir: string): Promise<void> {\n const gitignorePath = join(dataDir, '.gitignore');\n await writeFile(gitignorePath, GITIGNORE_CONTENT, 'utf-8');\n}\n","import { readFile, writeFile, rename, mkdir, chmod, stat } from 'node:fs/promises';\nimport { isAbsolute, join, resolve } from 'node:path';\nimport { machineFilePath, machineRoot } from './vault-resolver.js';\nimport { projectKey, type ProjectRef } from './project-key.js';\n\nconst FILENAME = 'project-paths.json';\n\n/**\n * Where each cloud project lives on THIS machine.\n *\n * A cloud project is materialized from its document, which holds no path — and\n * must not: a directory of mine means nothing on a teammate's machine, and the\n * sync pushes `projects`, `services` and `environments` wholesale, so a path\n * added to any of them would travel. Hence a file of its own, machine-local and\n * outside every synced collection.\n *\n * Without it, a service whose `cwd` is relative falls back to `'.'` — the\n * directory Runeya was launched from. Every cloud project opened from the same\n * place would then run in the same directory, which is precisely what this is\n * here to prevent.\n */\n\n/**\n * `${orgId ?? 'local'}:${projectId}` → absolute directory on this machine.\n *\n * Keyed by the couple, because a project id alone can name a different project\n * in each organization — two of them would then share one directory, which is\n * exactly what this file exists to prevent. Entries written by older versions\n * are keyed by the bare id; they are still read, and rewritten under the couple\n * the next time the project is placed.\n */\ntype PathMap = Record<string, string>;\n\nclass ProjectPathStore {\n private paths: PathMap | null = null;\n\n private async load(): Promise<PathMap> {\n if (this.paths) return this.paths;\n try {\n const raw = await readFile(machineFilePath(FILENAME), 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n this.paths = isPathMap(parsed) ? parsed : {};\n } catch {\n // Absent or unreadable: no project has been placed yet.\n this.paths = {};\n }\n return this.paths;\n }\n\n private async persist(): Promise<void> {\n await mkdir(machineRoot(), { recursive: true });\n const filePath = machineFilePath(FILENAME);\n const tmpPath = `${filePath}.tmp`;\n await writeFile(tmpPath, JSON.stringify(this.paths ?? {}, null, 2), 'utf-8');\n await rename(tmpPath, filePath);\n await chmod(filePath, 0o600);\n }\n\n /** The directory chosen for this project, or null while it has none. */\n async get(ref: ProjectRef): Promise<string | null> {\n const paths = await this.load();\n return paths[projectKey(ref)] ?? paths[ref.id] ?? null;\n }\n\n async all(): Promise<PathMap> {\n return { ...(await this.load()) };\n }\n\n /**\n * The project already placed in this directory, if any other one is.\n *\n * Answers with the key as stored — the couple, or the bare id for an entry an\n * older version wrote — which is enough to say \"taken\" and to name it.\n */\n async projectAt(path: string, exclude?: ProjectRef): Promise<string | null> {\n const target = resolve(path);\n const excluded = exclude ? [projectKey(exclude), exclude.id] : [];\n for (const [key, dir] of Object.entries(await this.load())) {\n if (!excluded.includes(key) && resolve(dir) === target) return key;\n }\n return null;\n }\n\n async set(ref: ProjectRef, path: string): Promise<void> {\n if (!isAbsolute(path)) throw new Error('Le chemin du projet doit être absolu');\n const paths = await this.load();\n // The legacy entry goes with it: leaving it behind would keep answering for\n // a project that has since been placed somewhere else.\n delete paths[ref.id];\n paths[projectKey(ref)] = resolve(path);\n await this.persist();\n }\n\n /**\n * Faire suivre le répertoire quand un projet change d'organisation.\n *\n * La clé est le couple `(organisation, projet)` : publier un projet local le\n * fait passer de `local:<id>` à `<org>:<id>`, et l'entrée d'avant cesse alors\n * de répondre — `get` ne retombe que sur l'identifiant nu, forme héritée, pas\n * sur `local:<id>`. Le projet se retrouvait donc sans emplacement connu juste\n * après avoir été publié, alors que ses sources n'avaient pas bougé d'un\n * octet.\n */\n async relocate(from: ProjectRef, to: ProjectRef): Promise<void> {\n const paths = await this.load();\n const previous = paths[projectKey(from)] ?? paths[from.id];\n if (!previous) return;\n delete paths[projectKey(from)];\n delete paths[from.id];\n paths[projectKey(to)] = previous;\n await this.persist();\n }\n\n async clear(ref: ProjectRef): Promise<void> {\n const paths = await this.load();\n const keys = [projectKey(ref), ref.id].filter((k) => k in paths);\n if (keys.length === 0) return;\n for (const key of keys) delete paths[key];\n await this.persist();\n }\n\n /** Test seam: forget what was read from disk. */\n reset(): void {\n this.paths = null;\n }\n}\n\nfunction isPathMap(value: unknown): value is PathMap {\n return (\n !!value\n && typeof value === 'object'\n && !Array.isArray(value)\n && Object.values(value as Record<string, unknown>).every((v) => typeof v === 'string')\n );\n}\n\n/**\n * Whether a directory already holds a Runeya project.\n *\n * Two projects sharing a directory is the very situation that makes services\n * run against the wrong code, so it is refused rather than warned about.\n */\nexport async function holdsRuneyaProject(path: string): Promise<boolean> {\n for (const candidate of ['project.json', 'projects.json']) {\n try {\n await stat(join(path, '.runeya', candidate));\n return true;\n } catch {\n // Keep looking.\n }\n }\n return false;\n}\n\nexport const projectPathStore = new ProjectPathStore();\n","/**\n * « Un tour vient de finir sur cette conversation. »\n *\n * Deux chemins très différents produisent ce même fait, et c'est pour ça que\n * le signal vit ici plutôt que chez l'un des deux :\n *\n * - le streaming (`activeSessionRegistry.markDone`), quand Runeya pilote le\n * CLI et reçoit ses événements ;\n * - le PTY interactif, où l'utilisateur tape dans un vrai terminal : Runeya\n * n'orchestre rien, elle lit le transcript que Claude écrit sur le disque.\n * La fin de tour s'y lit sur `stop_reason: end_turn`.\n *\n * Le loger dans le registre de sessions aurait laissé le second chemin muet —\n * un PTY n'y enregistre aucune session. C'est exactement le défaut qui a rendu\n * le compagnon silencieux dans l'interface par défaut de Claude Code.\n */\nimport { EventEmitter } from 'node:events';\n\nconst bus = new EventEmitter();\n// Un abonné par service intéressé, pas par conversation : la limite de 10 par\n// défaut n'a pas lieu de s'appliquer si d'autres s'y branchent un jour.\nbus.setMaxListeners(0);\n\nexport function emitTurnFinished(conversationId: string): void {\n bus.emit('turn-finished', conversationId);\n}\n\nexport function onTurnFinished(listener: (conversationId: string) => void): () => void {\n bus.on('turn-finished', listener);\n return () => { bus.off('turn-finished', listener); };\n}\n","import { readdir, access, readFile } from 'node:fs/promises';\nimport { watch, type FSWatcher } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { emitTurnFinished } from './turn-events.js';\nimport { conversationStore, type Message, type MessagePart } from './conversation-store.js';\n\nexport interface ClaudeTranscriptEntry {\n type: string;\n uuid?: string;\n timestamp?: string;\n message?: {\n role?: string;\n content?: unknown;\n usage?: unknown;\n model?: string;\n /** `end_turn` quand Claude a fini de parler ; autre chose quand il enchaîne. */\n stop_reason?: string | null;\n };\n toolUseResult?: unknown;\n}\n\nconst PROJECTS_DIR = join(homedir(), '.claude', 'projects');\nconst DEFAULT_TITLES = new Set(['', 'New conversation', 'Nouvelle conversation']);\n\nexport async function findClaudeSessionFile(sessionId: string): Promise<string | null> {\n let entries: string[];\n try {\n entries = await readdir(PROJECTS_DIR);\n } catch {\n return null;\n }\n const filename = `${sessionId}.jsonl`;\n for (const dir of entries) {\n const candidate = join(PROJECTS_DIR, dir, filename);\n try {\n await access(candidate);\n return candidate;\n } catch {\n // try next\n }\n }\n return null;\n}\n\nexport async function claudeSessionExists(sessionId: string): Promise<boolean> {\n return (await findClaudeSessionFile(sessionId)) !== null;\n}\n\nexport async function readClaudeTranscript(sessionId: string): Promise<{\n found: boolean;\n path: string | null;\n entries: ClaudeTranscriptEntry[];\n}> {\n const path = await findClaudeSessionFile(sessionId);\n if (!path) return { found: false, path: null, entries: [] };\n const raw = await readFile(path, 'utf-8');\n const entries: ClaudeTranscriptEntry[] = [];\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n try {\n entries.push(JSON.parse(line) as ClaudeTranscriptEntry);\n } catch {\n // skip malformed line\n }\n }\n return { found: true, path, entries };\n}\n\nfunction claudeEntriesToMessages(entries: ClaudeTranscriptEntry[]): Message[] {\n const messages: Message[] = [];\n const toolPartById = new Map<string, MessagePart & { type: 'tool' }>();\n\n for (const entry of entries) {\n if (entry.type !== 'user' && entry.type !== 'assistant') continue;\n const msg = entry.message;\n if (!msg) continue;\n const role: 'user' | 'assistant' =\n msg.role === 'user' || msg.role === 'assistant'\n ? msg.role\n : (entry.type as 'user' | 'assistant');\n const content = msg.content;\n\n const texts: string[] = [];\n const parts: MessagePart[] = [];\n let consumedByToolResult = false;\n\n if (typeof content === 'string') {\n texts.push(content);\n } else if (Array.isArray(content)) {\n for (const block of content) {\n if (typeof block === 'string') {\n texts.push(block);\n continue;\n }\n if (!block || typeof block !== 'object') continue;\n const b = block as {\n type?: string;\n text?: string;\n id?: string;\n name?: string;\n input?: Record<string, unknown>;\n tool_use_id?: string;\n content?: unknown;\n is_error?: boolean;\n };\n if (b.type === 'text' && typeof b.text === 'string') {\n texts.push(b.text);\n parts.push({ type: 'text', text: b.text });\n } else if (b.type === 'tool_use' && b.id) {\n const part: MessagePart & { type: 'tool' } = {\n type: 'tool',\n id: b.id,\n name: b.name ?? 'tool',\n input: b.input,\n pending: true,\n };\n parts.push(part);\n toolPartById.set(b.id, part);\n } else if (b.type === 'tool_result' && b.tool_use_id) {\n const target = toolPartById.get(b.tool_use_id);\n if (target) {\n target.result = b.content;\n target.isError = b.is_error;\n target.pending = false;\n }\n consumedByToolResult = true;\n }\n }\n }\n\n if (role === 'user' && consumedByToolResult && texts.length === 0) continue;\n\n // Merge into the previous message when it has the same role — Claude\n // emits one transcript entry per assistant turn (text / tool_use /\n // tool_result), so consecutive assistant entries are part of the same\n // logical reply and should render as one bubble.\n const last = messages[messages.length - 1];\n if (last && last.role === role) {\n const joinedText = [last.content, texts.join('\\n').trim()]\n .filter(Boolean)\n .join('\\n');\n last.content = joinedText;\n if (parts.length > 0) {\n last.parts = [...(last.parts ?? []), ...parts];\n }\n continue;\n }\n\n messages.push({\n role,\n content: texts.join('\\n').trim(),\n parts: parts.length > 0 ? parts : undefined,\n });\n }\n\n return messages;\n}\n\n\n/**\n * L'identité du dernier tour achevé du transcript, s'il y en a un.\n *\n * En PTY interactif, Runeya n'orchestre rien : elle relit un fichier que Claude\n * réécrit à chaque delta. La fin d'un tour ne s'y déduit pas de « le fichier a\n * changé » — seulement de `stop_reason: end_turn` sur la dernière entrée\n * d'assistant. L'uuid sert ensuite à ne l'annoncer qu'une fois : sans lui, un\n * seul tour déclencherait autant de relectures que d'écritures du fichier.\n */\nexport function lastFinishedTurnId(entries: ClaudeTranscriptEntry[]): string | null {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry.type !== 'assistant') continue;\n // La première entrée d'assistant rencontrée en remontant est la dernière du\n // tour : si elle n'a pas fini, c'est qu'aucun tour ne vient de s'achever.\n if (entry.message?.stop_reason !== 'end_turn') return null;\n return entry.uuid ?? `${entry.timestamp ?? ''}#${i}`;\n }\n return null;\n}\n\n/** Dernier tour déjà annoncé, par conversation. */\nconst announcedTurns = new Map<string, string>();\n\n/** Le tour `turnId` est-il nouveau pour cette conversation ? */\nexport function shouldAnnounceTurn(conversationId: string, turnId: string | null): boolean {\n if (!turnId) return false;\n if (announcedTurns.get(conversationId) === turnId) return false;\n announcedTurns.set(conversationId, turnId);\n return true;\n}\n\nexport function forgetAnnouncedTurns(conversationId: string): void {\n announcedTurns.delete(conversationId);\n}\n\n/**\n * Sync a Runeya conversation with its on-disk Claude JSONL transcript.\n * Skips if nothing changed; derives title from first user message on first sync.\n * Returns true when the conversation was actually updated.\n */\nexport async function syncConversationFromClaudeTranscript(conversationId: string): Promise<boolean> {\n const conv = await conversationStore.get(conversationId);\n if (!conv) return false;\n const transcript = await readClaudeTranscript(conversationId);\n if (!transcript.found) return false;\n\n const messages = claudeEntriesToMessages(transcript.entries);\n const isDefaultTitle = !conv.title || DEFAULT_TITLES.has(conv.title);\n\n const sameLength = messages.length === conv.messages.length;\n const sameLastContent =\n sameLength && (messages.at(-1)?.content ?? '') === (conv.messages.at(-1)?.content ?? '');\n if (sameLength && sameLastContent && !isDefaultTitle) {\n return false;\n }\n\n const patch: { messages: Message[]; title?: string } = { messages };\n if (isDefaultTitle) {\n const firstUserMsg = messages.find((m) => m.role === 'user' && m.content.trim().length > 0);\n if (firstUserMsg) {\n const firstLine = firstUserMsg.content.split('\\n')[0].trim();\n patch.title = firstLine.length > 80 ? firstLine.slice(0, 80) + '…' : firstLine;\n }\n }\n\n await conversationStore.update(conversationId, patch, { touch: false });\n\n // Après l'écriture : ce que le compagnon relira doit être en base, pas\n // encore en cours d'écriture.\n if (shouldAnnounceTurn(conversationId, lastFinishedTurnId(transcript.entries))) {\n emitTurnFinished(conversationId);\n }\n return true;\n}\n\n// ─── Activity tracking ─────────────────────────────────────────────────────\n\n/** Last time we observed a write to a given session's JSONL. */\nconst lastTranscriptActivity = new Map<string, number>();\n\nexport function getLastTranscriptActivity(sessionId: string): number | null {\n return lastTranscriptActivity.get(sessionId) ?? null;\n}\n\n// ─── Realtime watcher ──────────────────────────────────────────────────────\n\nlet started = false;\nconst watchers: FSWatcher[] = [];\nconst pendingResyncs = new Map<string, NodeJS.Timeout>();\nconst RESYNC_DEBOUNCE_MS = 250;\n\nfunction scheduleResync(sessionId: string): void {\n const existing = pendingResyncs.get(sessionId);\n if (existing) clearTimeout(existing);\n pendingResyncs.set(\n sessionId,\n setTimeout(() => {\n pendingResyncs.delete(sessionId);\n syncConversationFromClaudeTranscript(sessionId).catch((err) => {\n console.warn('[claude-transcript-sync] resync failed for', sessionId, err);\n });\n }, RESYNC_DEBOUNCE_MS),\n );\n}\n\n/**\n * L'identifiant de session porté par un chemin rendu par `fs.watch`, ou `null`.\n *\n * Le chemin est relatif au répertoire surveillé et utilise le séparateur de la\n * plateforme : « <projectDir>/<sessionId>.jsonl » sur POSIX, « \\ » sur Windows —\n * ne couper que sur « / » y laissait le nom du projet collé devant l'identifiant :\n * le contrôle d'UUID échouait, plus aucune session externe n'était resynchronisée,\n * donc aucune fin de tour annoncée — et pas de compagnon.\n */\nexport function sessionIdFromWatchedFilename(filename: string | null): string | null {\n if (!filename) return null;\n if (!filename.endsWith('.jsonl')) return null;\n const base = filename.split(/[\\\\/]/).pop()!;\n const sessionId = base.slice(0, -'.jsonl'.length);\n if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId)) return null;\n return sessionId;\n}\n\nfunction handleFsEvent(filename: string | null): void {\n const sessionId = sessionIdFromWatchedFilename(filename);\n if (!sessionId) return;\n lastTranscriptActivity.set(sessionId, Date.now());\n scheduleResync(sessionId);\n}\n\n/**\n * Watch ~/.claude/projects/ recursively. Whenever a `<sessionId>.jsonl` file\n * is touched, re-sync the matching Runeya conversation (if any) so the\n * non-PTY message view stays live.\n */\nexport async function startClaudeTranscriptWatcher(): Promise<void> {\n if (started) return;\n started = true;\n\n let topLevel: string[];\n try {\n topLevel = await readdir(PROJECTS_DIR);\n } catch {\n // ~/.claude/projects doesn't exist yet — watch parent for creation.\n return;\n }\n\n // Prefer a single recursive watcher when supported, otherwise watch each\n // project dir individually.\n try {\n const w = watch(PROJECTS_DIR, { recursive: true }, (_event, filename) => {\n handleFsEvent(filename ? filename.toString() : null);\n });\n w.on('error', (err) => console.warn('[claude-transcript-sync] watcher error:', err));\n watchers.push(w);\n return;\n } catch {\n // Fallback below.\n }\n\n for (const dir of topLevel) {\n try {\n const w = watch(join(PROJECTS_DIR, dir), (_event, filename) => {\n handleFsEvent(filename ? filename.toString() : null);\n });\n w.on('error', () => {});\n watchers.push(w);\n } catch {\n // ignore\n }\n }\n}\n\nexport function stopClaudeTranscriptWatcher(): void {\n for (const w of watchers) {\n try {\n w.close();\n } catch {\n // ignore\n }\n }\n watchers.length = 0;\n for (const t of pendingResyncs.values()) clearTimeout(t);\n pendingResyncs.clear();\n started = false;\n}\n","/**\n * Le pendant Codex de `claude-transcript-sync`.\n *\n * Sans lui, une conversation Codex en terminal n'existait pour Runeya que comme\n * un terminal : ses messages n'entraient jamais dans la conversation, et rien\n * n'annonçait la fin d'un tour. Le compagnon ne se déclenchait donc jamais sur\n * Codex — pas par un défaut de sa logique, mais faute de signal.\n *\n * Codex écrit un « rollout » par session :\n * ~/.codex/thread_history_<n>.sqlite (tables thread_turns / thread_items)\n *\n * Deux formes d'entrées nous intéressent :\n * - `response_item` / `message` : le fil, avec son rôle ;\n * - `event_msg` / `task_complete` : la FIN d'un tour — et elle porte même le\n * dernier message de l'agent, ce qui évite de le reconstituer.\n */\nimport { watch, type FSWatcher } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { conversationStore } from './conversation-store.js';\nimport { emitTurnFinished } from './turn-events.js';\nimport { findThreadDb, threadMessages, lastTurn, threadsStartedAfter } from './codex-thread-db.js';\n\nconst announced = new Map<string, string>();\n\nexport function forgetCodexTurns(conversationId: string): void {\n announced.delete(conversationId);\n}\n\n/**\n * Aligne une conversation Runeya sur son rollout Codex.\n *\n * Rend `true` quand elle a changé. Annonce la fin de tour au passage, une seule\n * fois par tour — c'est ce signal que le compagnon écoute.\n */\nexport async function syncConversationFromCodexTranscript(conversationId: string): Promise<boolean> {\n const conv = await conversationStore.get(conversationId);\n if (!conv?.codexThreadId) return false;\n\n const messages = threadMessages(conv.codexThreadId);\n if (messages.length === 0) return false;\n\n const same = messages.length === conv.messages.length\n && (messages.at(-1)?.content ?? '') === (conv.messages.at(-1)?.content ?? '');\n if (!same) await conversationStore.update(conversationId, { messages }, { touch: false });\n\n // `completed_at` est déclaré par Codex : contrairement aux rollouts, il n'y a\n // rien à déduire de « le fichier a changé ».\n const turn = lastTurn(conv.codexThreadId);\n if (turn?.completedAt && announced.get(conversationId) !== turn.turnId) {\n announced.set(conversationId, turn.turnId);\n emitTurnFinished(conversationId);\n }\n return !same;\n}\n\n// ─── Observateur ────────────────────────────────────────────────────────────\n\nlet watcher: FSWatcher | null = null;\nlet debounce: NodeJS.Timeout | null = null;\n\n/**\n * Toutes les conversations Codex rattachées, resynchronisées ensemble.\n *\n * La base ne dit pas quel fil vient de changer — et le savoir coûterait plus\n * cher que de relire : un fil fait quelques dizaines de lignes, et seules les\n * conversations déjà rattachées sont concernées.\n */\nasync function resyncAll(): Promise<void> {\n const convs = await conversationStore.list();\n await bindPendingSpawns(convs.map(c => c.codexThreadId).filter((x): x is string => !!x));\n for (const c of await conversationStore.list()) {\n if (!c.codexThreadId) continue;\n await syncConversationFromCodexTranscript(c.id).catch(() => false);\n }\n}\n\n/**\n * Rattacher les terminaux Codex encore sans fil.\n *\n * Codex ne dit pas quel fil il vient d'ouvrir. Il l'annonce parfois dans son\n * terminal — mais seulement au renommage d'une session, donc pas toujours. Ce\n * qu'on sait en revanche toujours, c'est QUAND on a lancé le terminal : un fil\n * dont le premier tour démarre après ce lancement est le nôtre.\n *\n * Une marge de quelques secondes absorbe l'écart entre le spawn et l'écriture\n * du premier tour. En cas d'ambiguïté — deux terminaux ouverts coup sur coup —\n * on renonce : rattacher au petit bonheur ferait relire au compagnon les tours\n * d'une autre conversation.\n */\nconst BIND_MARGIN_SECONDS = 10;\n\nasync function bindPendingSpawns(boundThreadIds: string[]): Promise<void> {\n for (const [spawnId, spawn] of spawnConversations) {\n const conv = await conversationStore.get(spawn.conversationId);\n if (!conv) { spawnConversations.delete(spawnId); continue; }\n if (conv.codexThreadId) { spawnConversations.delete(spawnId); continue; }\n if (conv.runner !== 'codex') continue;\n\n const candidates = threadsStartedAfter(spawn.startedAt - BIND_MARGIN_SECONDS)\n .filter(id => !boundThreadIds.includes(id));\n if (candidates.length !== 1) continue;\n\n await conversationStore.update(conv.id, { codexThreadId: candidates[0] });\n boundThreadIds.push(candidates[0]!);\n spawnConversations.delete(spawnId);\n }\n}\n\nexport function startCodexTranscriptWatcher(): void {\n if (watcher) return;\n const db = findThreadDb();\n if (!db) return; // Codex absent, ou base d'un schéma qu'on ne connaît pas\n\n try {\n // On observe le DOSSIER, pas le fichier : SQLite écrit par `-wal` et\n // remplace ses fichiers, ce qu'un observateur posé sur un seul fichier\n // cesse de voir dès la première rotation.\n watcher = watch(dirname(db), (_event, filename) => {\n if (!filename || !/^thread_history_\\d+\\.sqlite/.test(filename.toString())) return;\n if (debounce) clearTimeout(debounce);\n debounce = setTimeout(() => { debounce = null; void resyncAll(); }, 400);\n });\n watcher.on('error', (err) => console.warn('[codex-thread-sync]', err));\n } catch {\n // Dossier illisible : Codex n'est pas installé, rien à observer.\n }\n}\n\nexport function stopCodexTranscriptWatcher(): void {\n watcher?.close();\n watcher = null;\n if (debounce) { clearTimeout(debounce); debounce = null; }\n tails.clear();\n spawnConversations.clear();\n}\n\n// ─── Rattachement par l'annonce de Codex ────────────────────────────────────\n\n/**\n * Codex choisit lui-même l'identifiant de sa session, et rien ne le pose sur la\n * conversation quand elle tourne dans un terminal — `codexThreadId` n'est\n * renseigné que par le chemin sans terminal. Sans rattachement, un tour Codex\n * interactif n'annonce jamais sa fin, et le compagnon ne part pas.\n *\n * Mais Codex l'annonce lui-même, dans son terminal :\n * « To resume this session run `codex resume`, then select … (01a066bd-…) »\n *\n * Le serveur relaie chaque octet du PTY : il suffit de le lire. C'est\n * déterministe — l'identifiant vient de la session elle-même — là où deviner\n * par le répertoire de travail confondrait deux terminaux ouverts au même\n * endroit, et ferait relire au compagnon les tours d'une autre conversation.\n */\nconst CODEX_RESUME_RE = /codex resume[\\s\\S]{0,300}?\\(([0-9a-fA-F]{8}-[0-9a-fA-F-]{27})\\)/;\n\n/** Le flux arrive par fragments : l'annonce peut être coupée en deux. */\nconst MAX_TAIL = 4_000;\nconst tails = new Map<string, string>();\n/** À quelle conversation appartient un spawn, appris au démarrage du PTY. */\nconst spawnConversations = new Map<string, { conversationId: string; startedAt: number }>();\n\nexport function noteSpawnConversation(spawnId: string, conversationId?: string): void {\n // Un PTY peut être lancé sans conversation (terminal libre) : il n'y a alors\n // rien à rattacher, et retenir un lien vide ferait fuir la carte.\n if (!conversationId) return;\n // L'instant sert au rattachement par le temps : un fil né après ce spawn est\n // le nôtre. C'est le seul repère toujours disponible — l'annonce de Codex,\n // elle, ne paraît qu'au renommage d'une session.\n spawnConversations.set(spawnId, { conversationId, startedAt: Math.floor(Date.now() / 1000) });\n}\n\n/** L'identifiant de session annoncé par Codex dans ce texte, s'il y en a un. */\nexport function codexSessionIdIn(text: string): string | null {\n return CODEX_RESUME_RE.exec(text)?.[1] ?? null;\n}\n\n/**\n * Observer un fragment de terminal, et rattacher la session s'il l'annonce.\n *\n * Sans effet — et sans coût — pour tout ce qui n'est pas une conversation Codex\n * en attente de rattachement.\n */\nexport function observePtyChunk(spawnId: string, data: string): void {\n const spawn = spawnConversations.get(spawnId);\n if (!spawn) return;\n const conversationId = spawn.conversationId;\n\n const tail = (tails.get(spawnId) ?? '') + data;\n const sessionId = codexSessionIdIn(tail);\n if (!sessionId) {\n tails.set(spawnId, tail.slice(-MAX_TAIL));\n return;\n }\n\n // Trouvé : plus rien à surveiller pour ce spawn.\n tails.delete(spawnId);\n spawnConversations.delete(spawnId);\n\n void (async () => {\n const conv = await conversationStore.get(conversationId);\n if (!conv || conv.codexThreadId === sessionId) return;\n await conversationStore.update(conversationId, { codexThreadId: sessionId });\n await syncConversationFromCodexTranscript(conversationId).catch(() => false);\n })();\n}\n\nexport function forgetSpawn(spawnId: string): void {\n tails.delete(spawnId);\n spawnConversations.delete(spawnId);\n}\n","import { randomBytes } from 'node:crypto';\nimport { SignJWT, jwtVerify } from 'jose';\n\nlet jwtSecret: Uint8Array | null = null;\nlet jwtSecretGeneratedAt: number = 0; // Unix timestamp seconds\n\nexport function generateJwtSecret(): void {\n jwtSecret = new Uint8Array(randomBytes(32));\n jwtSecretGeneratedAt = Math.floor(Date.now() / 1000);\n}\n\nexport function getJwtSecretGeneratedAt(): number {\n return jwtSecretGeneratedAt;\n}\n\nfunction getJwtSecret(): Uint8Array {\n if (!jwtSecret) throw new Error('JWT secret not yet generated');\n return jwtSecret;\n}\n\n/** Sign a JWT valid for 8 hours */\nexport async function signJwt(): Promise<string> {\n const secret = getJwtSecret();\n return new SignJWT({ sub: 'app' })\n .setProtectedHeader({ alg: 'HS256' })\n .setIssuedAt()\n .setExpirationTime('8h')\n .sign(secret);\n}\n\nexport interface JwtPayload {\n exp: number;\n iat: number;\n}\n\n/** Verify a JWT — throws if invalid/expired */\nexport async function verifyJwt(token: string): Promise<JwtPayload> {\n const secret = getJwtSecret();\n const { payload } = await jwtVerify(token, secret);\n return { exp: payload.exp as number, iat: payload.iat as number };\n}\n","/**\n * Retrouver le PTY vivant d'une conversation, sans savoir quel agent l'héberge.\n *\n * La conversation ne porte pas l'agent où tourne son terminal : d'où le\n * balayage, et l'arrêt au premier qui répond « running ».\n *\n * `chat.ts` interroge aussi `ai.findPtyBySession` à trois endroits, et ce n'est\n * PAS la même chose — ne pas les fusionner ici sans y regarder : la suppression\n * d'une conversation balaye tous les agents pour tuer chaque PTY (elle n'en\n * cherche pas un), tandis que la reprise de session et la mesure d'activité\n * partent d'un agentId déjà connu et lisent des champs que celle-ci ne rend pas\n * (`lastChunkAt`, le besoin de redimensionner). Trois besoins voisins, trois\n * formes distinctes.\n */\nimport { agentManager } from './agent-manager.js';\n\nexport interface LivePty {\n agentId: string;\n spawnId: string;\n}\n\nexport async function findLivePty(sessionId: string): Promise<LivePty | null> {\n for (const agent of agentManager.listAgents()) {\n const agentId = agent.config.id;\n try {\n const pty = await agentManager.agentQuery<{ spawnId: string; status: string } | null>(\n agentId,\n 'ai.findPtyBySession',\n { sessionId },\n );\n if (pty?.status === 'running') return { agentId, spawnId: pty.spawnId };\n } catch {\n // Agent injoignable : les autres peuvent encore répondre.\n }\n }\n return null;\n}\n","import type { ChildProcess } from 'node:child_process';\nimport { conversationStore } from './conversation-store.js';\nimport { agentManager } from './agent-manager.js';\nimport { emitTurnFinished } from './turn-events.js';\n\ntype SessionPart =\n | { type: 'thinking'; text: string }\n | { type: 'text'; text: string }\n | { type: 'tool'; id: string; name: string; input?: Record<string, unknown>; rawInput?: string; result?: unknown; isError?: boolean; pending: boolean };\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\nexport type QueueItem =\n | { type: 'delta'; text: string }\n | { type: 'status'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'context_usage'; inputTokens: number; contextWindow: number }\n | { type: 'tool_call'; id: string; name: string; input: Record<string, unknown>; rawInput?: string }\n | { type: 'tool_result'; id: string; name: string; result: unknown; isError: boolean }\n | { type: 'done'; text: '' }\n | { type: 'error'; message: string };\n\ntype AiRunnerType = 'claude-code' | 'provider-api';\n\nexport interface ActiveSession {\n proc: ChildProcess | null;\n abortController: AbortController | null;\n sessionType: AiRunnerType;\n conversationId: string;\n buffer: QueueItem[];\n totalProduced: number;\n fullContent: string;\n parts: SessionPart[];\n lastStatus: string;\n status: 'running' | 'done';\n persistTimer: ReturnType<typeof setTimeout> | null;\n cleanupTimer: ReturnType<typeof setTimeout> | null;\n notify: Set<() => void>;\n /** Agent ID when the AI CLI runs on a remote agent. */\n agentId?: string;\n /** Spawn ID on the remote agent (for kill / reconnect). */\n agentSpawnId?: string;\n}\n\n// ─── Constants ─────────────────────────────────────────────────────────────\n\nconst MAX_BUFFER = 5_000;\nconst PERSIST_DEBOUNCE_MS = 100;\n\n// ─── Registry ──────────────────────────────────────────────────────────────\n\nclass ActiveSessionRegistry {\n private sessions = new Map<string, ActiveSession>();\n registerClaudeCodeSession(conversationId: string, proc: ChildProcess): ActiveSession {\n this.assertNoActiveSession(conversationId);\n\n const session: ActiveSession = {\n proc,\n abortController: null,\n sessionType: 'claude-code',\n conversationId,\n buffer: [],\n totalProduced: 0,\n fullContent: '',\n parts: [],\n lastStatus: '',\n status: 'running',\n persistTimer: null,\n cleanupTimer: null,\n notify: new Set(),\n };\n\n this.sessions.set(conversationId, session);\n return session;\n }\n\n registerProviderApiSession(conversationId: string, ac: AbortController): ActiveSession {\n this.assertNoActiveSession(conversationId);\n\n const session: ActiveSession = {\n proc: null,\n abortController: ac,\n sessionType: 'provider-api',\n conversationId,\n buffer: [],\n totalProduced: 0,\n fullContent: '',\n parts: [],\n lastStatus: '',\n status: 'running',\n persistTimer: null,\n cleanupTimer: null,\n notify: new Set(),\n };\n\n this.sessions.set(conversationId, session);\n return session;\n }\n\n registerDirectProviderApiSession(conversationId: string): ActiveSession {\n this.assertNoActiveSession(conversationId);\n\n const session: ActiveSession = {\n proc: null,\n abortController: new AbortController(),\n sessionType: 'provider-api',\n conversationId,\n buffer: [],\n totalProduced: 0,\n fullContent: '',\n parts: [],\n lastStatus: '',\n status: 'running',\n persistTimer: null,\n cleanupTimer: null,\n notify: new Set(),\n };\n\n this.sessions.set(conversationId, session);\n return session;\n }\n\n private assertNoActiveSession(conversationId: string): void {\n const existing = this.sessions.get(conversationId);\n if (existing && existing.status === 'running') {\n throw new Error(`A session is already running for conversation ${conversationId}. Stop it first.`);\n }\n }\n\n pushEvent(conversationId: string, event: QueueItem): void {\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: event - ', event.type)\n const session = this.sessions.get(conversationId);\n if (!session) return;\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: event - ', event.type, 'session', session.conversationId)\n\n // Accumulate parts for replay/persist\n if (event.type === 'delta') {\n session.fullContent += event.text;\n const last = session.parts[session.parts.length - 1];\n if (last?.type === 'text') last.text += event.text;\n else session.parts.push({ type: 'text', text: event.text });\n this.schedulePersist(session);\n }\n if (event.type === 'tool_call') {\n const existing = session.parts.find(p => p.type === 'tool' && p.id === event.id) as Extract<SessionPart, { type: 'tool' }> | undefined;\n if (existing) { existing.name = event.name; existing.input = event.input; }\n else session.parts.push({ type: 'tool', id: event.id, name: event.name, input: event.input, rawInput: event.rawInput, pending: true });\n this.schedulePersist(session);\n }\n if (event.type === 'tool_result') {\n const te = session.parts.find(p => p.type === 'tool' && p.id === event.id) as Extract<SessionPart, { type: 'tool' }> | undefined;\n if (te) {\n te.result = event.result;\n te.isError = event.isError;\n te.pending = false;\n } else {\n session.parts.push({ type: 'tool', id: event.id, name: event.name, result: event.result, isError: event.isError, pending: false });\n }\n this.schedulePersist(session);\n }\n if (event.type === 'thinking') {\n const last = session.parts[session.parts.length - 1];\n if (last?.type === 'thinking') last.text += event.text;\n else session.parts.push({ type: 'thinking', text: event.text });\n }\n if (event.type === 'status') {\n session.lastStatus = event.text;\n }\n\n // Cap buffer size — drop oldest if full\n if (session.buffer.length >= MAX_BUFFER) {\n session.buffer.shift();\n }\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: event - ', event.type, 'push buffer')\n session.buffer.push(event);\n session.totalProduced++;\n\n // Wake all consumers (multiple tabs/windows)\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: event - ', event.type, 'notify', session.notify.size)\n for (const cb of session.notify) cb();\n session.notify.clear();\n }\n\n markDone(conversationId: string): void {\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: markdone....', conversationId)\n const session = this.sessions.get(conversationId);\n if (!session) return;\n\n const wasRunning = session.status === 'running';\n session.status = 'done';\n\n // Persist final content immediately\n this.persistNow(session);\n\n // Après la persistance : ce que le compagnon relira doit être sur le\n // disque, pas encore dans un tampon. `markDone` est parfois rappelé sur une\n // session déjà terminée — n'annoncer que la transition évite un second\n // compagnon sur le même tour.\n if (wasRunning) emitTurnFinished(conversationId);\n }\n\n get(conversationId: string): ActiveSession | undefined {\n return this.sessions.get(conversationId);\n }\n\n listActive(): { conversationId: string; status: 'running' | 'done'; sessionType: AiRunnerType }[] {\n const result: { conversationId: string; status: 'running' | 'done'; sessionType: AiRunnerType }[] = [];\n for (const session of this.sessions.values()) {\n result.push({ conversationId: session.conversationId, status: session.status, sessionType: session.sessionType });\n }\n return result;\n }\n\n kill(conversationId: string): boolean {\n const session = this.sessions.get(conversationId);\n if (!session) return false;\n\n if (session.status === 'running') {\n // Remote agent kill\n if (session.agentId && session.agentSpawnId) {\n agentManager.agentMutation(session.agentId, 'ai.kill', { spawnId: session.agentSpawnId }).catch(() => { /* best-effort */ });\n }\n if (session.proc) {\n try { session.proc.kill(); } catch { /* already dead */ }\n }\n if (session.abortController) {\n session.abortController.abort();\n }\n }\n return true;\n }\n\n // ── Internal helpers ────────────────────────────────────────────────────\n\n private schedulePersist(session: ActiveSession): void {\n if (session.persistTimer) return;\n session.persistTimer = setTimeout(() => {\n session.persistTimer = null;\n this.persistNow(session);\n }, PERSIST_DEBOUNCE_MS);\n }\n\n private persistNow(session: ActiveSession): void {\n if (session.persistTimer) {\n clearTimeout(session.persistTimer);\n session.persistTimer = null;\n }\n\n const fullContent = session.fullContent;\n const parts = session.parts.length > 0 ? [...session.parts] : undefined;\n // Synchronous mutation + async save — avoids the async get()→.then() race where a\n // debounced partial-persist .then() could run after a markDone full-persist .then()\n // and overwrite the complete content with stale partial content.\n conversationStore.updateLastAssistantContent(session.conversationId, fullContent, parts);\n }\n\n private cleanupTimers(session: ActiveSession): void {\n if (session.persistTimer) {\n clearTimeout(session.persistTimer);\n session.persistTimer = null;\n }\n if (session.cleanupTimer) {\n clearTimeout(session.cleanupTimer);\n session.cleanupTimer = null;\n }\n }\n}\n\nexport const activeSessionRegistry = new ActiveSessionRegistry();\n"],"mappings":"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","names":["EventEmitter","randomUUID","readFile","writeFile","rename","mkdir","chmod","join","EventEmitter","readFile","writeFile","rename","mkdir","chmod","join","writeFile","join","writeFile","join","FILENAME","readFile","join","writeFile","rename","chmod","mkdir","FILENAME","EventEmitter","readFile","mkdir","join","writeFile","rename","chmod","randomUUID","EventEmitter","readFile","join","join","readFile","watch","dirname","watch","dirname","bus","EventEmitter"]}
@@ -1,9 +0,0 @@
1
- import {
2
- settingsManager
3
- } from "./chunk-GIHR3BGT.js";
4
- import "./chunk-H6VIS4OY.js";
5
- import "./chunk-ZFELQG4B.js";
6
- export {
7
- settingsManager
8
- };
9
- //# sourceMappingURL=settings-manager-2C7RY6ZC.js.map