@runeya/runeya 2.0.117 → 2.0.118

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 (343) hide show
  1. package/agent/index.js +32 -1
  2. package/agent/index.js.map +1 -1
  3. package/agent-manager-HOL7ZG4V.js +9 -0
  4. package/{chat-QLIFNGA2.js → chat-XUIXGKSR.js} +6 -6
  5. package/{chunk-77UQ2UKW.js → chunk-I2AZCZMR.js} +2 -2
  6. package/{chunk-OGCEFBSS.js → chunk-P47X3LT3.js} +7 -2
  7. package/chunk-P47X3LT3.js.map +1 -0
  8. package/{chunk-6T7R2JZR.js → chunk-RN4X65D3.js} +35 -2
  9. package/chunk-RN4X65D3.js.map +1 -0
  10. package/{chunk-K3H5WJRQ.js → chunk-WE6EOX4C.js} +331 -215
  11. package/chunk-WE6EOX4C.js.map +1 -0
  12. package/{chunk-F7A7XH3N.js → chunk-XIRMRWVU.js} +18 -6
  13. package/{chunk-F7A7XH3N.js.map → chunk-XIRMRWVU.js.map} +1 -1
  14. package/{companion-RLSRXTNO.js → companion-POUL5OGO.js} +4 -4
  15. package/{desktop-launcher-Q6J2LSVP.js → desktop-launcher-QVYFCE56.js} +2 -2
  16. package/index.js +4 -4
  17. package/package.json +1 -1
  18. package/settings-manager-KKSPWSM6.js +9 -0
  19. package/{src-Q2AWI57U.js → src-VCZQLW2Q.js} +92 -11
  20. package/src-VCZQLW2Q.js.map +1 -0
  21. package/web/assets/AgentScalarChatInterface.vue-BrMvIukr.js +13 -0
  22. package/web/assets/AgentTerminalPage-CXWlEDYr.js +1 -0
  23. package/web/assets/AiChatPage-CemkKI5f.js +1 -0
  24. package/web/assets/AppLayout-Kc2JJjh1.js +1 -0
  25. package/web/assets/AuditPage-Da3_Dw6e.js +1 -0
  26. package/web/assets/CloudAccountPage-C6zqdsH7.js +1 -0
  27. package/web/assets/CloudAccountPage-DPs87dbs.js +1 -0
  28. package/web/assets/CloudMessagingPage-DTuqXSPI.js +2 -0
  29. package/web/assets/DashboardPage-BGRnC6VN.js +164 -0
  30. package/web/assets/DirectoryPickerDialog-DEoqmzoh.js +1 -0
  31. package/web/assets/{EditorIcon-DoAjQCrb.js → EditorIcon-CtWlXuLo.js} +2 -2
  32. package/web/assets/{EnvironmentForm-CgsN_8EO.js → EnvironmentForm-7rQeTN-8.js} +2 -2
  33. package/web/assets/EnvironmentFormPage-YcYUsjhT.js +1 -0
  34. package/web/assets/EnvironmentListPage-CLD1-AAY.js +1 -0
  35. package/web/assets/KanbanBoardPage-B7VdWhZB.js +4 -0
  36. package/web/assets/KanbanListPage-BEHtcwKs.js +1 -0
  37. package/web/assets/LaunchProjectPage-exfyMjFt.js +1 -0
  38. package/web/assets/LaunchServicesPage-YVkLRJqQ.js +1 -0
  39. package/web/assets/LogViewer-D1RcM2A4.js +3 -0
  40. package/web/assets/OrgKeyPrompt-C7LMf-Zk.js +1 -0
  41. package/web/assets/{ProcessControls-BAThUaVo.js → ProcessControls-58Ablgi0.js} +1 -1
  42. package/web/assets/ProcessDetailPage-BUjh1O5a.js +1 -0
  43. package/web/assets/{ProcessMetrics-BnfF7Esx.js → ProcessMetrics-BEzFNP7J.js} +2 -2
  44. package/web/assets/RandomJobTestPage-DuPQQhVb.js +2 -0
  45. package/web/assets/ScenarioEditorPage-6-CuJOmJ.js +14 -0
  46. package/web/assets/SendToChatDialog-CxuunSwf.js +1 -0
  47. package/web/assets/ServiceControlsBar-Cu9vxEOQ.js +1 -0
  48. package/web/assets/ServiceDetailPage-B4plJ5bz.js +2 -0
  49. package/web/assets/ServiceFormPage-7O2k1FOl.js +1 -0
  50. package/web/assets/SettingsPage-Hwj6MTiF.js +2063 -0
  51. package/web/assets/UnlockPage-Dcv0Z9re.js +1 -0
  52. package/web/assets/VaultMigrationPage-B25lyJg7.js +1 -0
  53. package/web/assets/VersionBadge-HrSfhQYh.js +1 -0
  54. package/web/assets/WikiPage-EBD94ewJ.js +1 -0
  55. package/web/assets/WikiView-nfJY79hF.js +1 -0
  56. package/web/assets/add-scalar-classes-H19oYTsX.js +387 -0
  57. package/web/assets/{autocomplete-DVfF8-mq.js → autocomplete-DJxf2wqE.js} +3 -3
  58. package/web/assets/{basic-shape-diamond.svg-D8aU5xwY.js → basic-shape-diamond.svg-COr16MGG.js} +1 -1
  59. package/web/assets/{basic-shape-hexagon.svg-C48LyQUE.js → basic-shape-hexagon.svg-BzYcuOkI.js} +1 -1
  60. package/web/assets/{basic-shape-primary-circle-ellipse-round.svg-Cn4csfd0.js → basic-shape-primary-circle-ellipse-round.svg-Bw1gV38D.js} +1 -1
  61. package/web/assets/{basic-shape-primary-square-rectangle.svg-C9Hhhoy0.js → basic-shape-primary-square-rectangle.svg-Cn_NwW2C.js} +1 -1
  62. package/web/assets/{basic-shape-shield.svg-BtyWy6Hb.js → basic-shape-shield.svg-DMZviUvu.js} +1 -1
  63. package/web/assets/{browser-SQJu0kqP.js → browser-72MoXpHU.js} +1 -1
  64. package/web/assets/{browser-D-xywlrw.js → browser-APehRX_-.js} +1 -1
  65. package/web/assets/card-DWOaM9R5.js +32 -0
  66. package/web/assets/{checkbox-bUKoFar-.js → checkbox-3MTmcxWS.js} +3 -3
  67. package/web/assets/{chip-FS8CF7nQ.js → chip-DX0PHdQT.js} +2 -2
  68. package/web/assets/client-CmMSqZpF.js +24 -0
  69. package/web/assets/cloud-chat.store-BcZHJL7V.js +1 -0
  70. package/web/assets/cloud-client-DSbRz1wz.js +1 -0
  71. package/web/assets/{cloud-session-D9RcOxiL.js → cloud-session-CMcFsrn6.js} +2 -2
  72. package/web/assets/cloud-session-NnwumoiU.js +1 -0
  73. package/web/assets/column-CDX5Db_i.js +763 -0
  74. package/web/assets/{computer-device-desktop-monitor.svg-MSBAl_IT.js → computer-device-desktop-monitor.svg-BLXia0GM.js} +1 -1
  75. package/web/assets/{computer-device-desktop.svg-CEWAl-1r.js → computer-device-desktop.svg-gunjcHFA.js} +1 -1
  76. package/web/assets/{computer-device-laptop.svg-DQ1d2ADo.js → computer-device-laptop.svg-Bw0yugEp.js} +1 -1
  77. package/web/assets/{computer-device-mobile-phone-android-samsung-back.svg-Dzz2XEW6.js → computer-device-mobile-phone-android-samsung-back.svg-C0WpuMdy.js} +1 -1
  78. package/web/assets/{computer-device-mobile-phone-android-samsung.svg-BfiSZRYt.js → computer-device-mobile-phone-android-samsung.svg-CNHYgja0.js} +1 -1
  79. package/web/assets/{computer-device-mobile-phone-iphone-x-back.svg-BKDNa4ua.js → computer-device-mobile-phone-iphone-x-back.svg-DggESCcp.js} +1 -1
  80. package/web/assets/{computer-device-mobile-phone-iphone-x.svg-CxwOtpMv.js → computer-device-mobile-phone-iphone-x.svg-CDSSQDCC.js} +1 -1
  81. package/web/assets/{computer-device-mobile-tablet-touch.svg-D-d6m_kv.js → computer-device-mobile-tablet-touch.svg-DY9TWMUB.js} +1 -1
  82. package/web/assets/{computer-device-mobile-tablet.svg-D8b5on89.js → computer-device-mobile-tablet.svg-D_hfbqaw.js} +1 -1
  83. package/web/assets/{computer-device-network-ethernet-cat6.svg-c-FuyKH3.js → computer-device-network-ethernet-cat6.svg-CCoszOYr.js} +1 -1
  84. package/web/assets/{computer-device-network-lan-www.svg-DVIwP-3w.js → computer-device-network-lan-www.svg-CClZA2cg.js} +1 -1
  85. package/web/assets/{computer-device-network-wifi-connection.svg-BXtM5IP8.js → computer-device-network-wifi-connection.svg-DHLQJkPy.js} +1 -1
  86. package/web/assets/{computer-device-network-wifi-router.svg-D0MA5S5K.js → computer-device-network-wifi-router.svg-xActAcrc.js} +1 -1
  87. package/web/assets/{cssMode-C1ocy9fG.js → cssMode-UiVbv5-d.js} +1 -1
  88. package/web/assets/{divider-DaQ67Uqu.js → divider-CHpP62xs.js} +2 -2
  89. package/web/assets/{docx-preview-D2pXpGJL.js → docx-preview-CZnedVKH.js} +1 -1
  90. package/web/assets/{ecology-science-erlenmeyer-flask.svg-BC5h7SBw.js → ecology-science-erlenmeyer-flask.svg-BNQkfPKh.js} +1 -1
  91. package/web/assets/{fieldset-DASecQzo.js → fieldset-BEeYMjpD.js} +2 -2
  92. package/web/assets/{freemarker2-Co24PBKb.js → freemarker2-DEBZcua_.js} +1 -1
  93. package/web/assets/{handlebars-BiGhhRTT.js → handlebars-BNH-tM3s.js} +1 -1
  94. package/web/assets/{html-jVmuH_kg.js → html-BFhZuj1f.js} +1 -1
  95. package/web/assets/{htmlMode-DfGryePh.js → htmlMode-DsWwMsD5.js} +1 -1
  96. package/web/assets/{image-flash-lightning.svg-pmP3Tybf.js → image-flash-lightning.svg-CaDxrzvs.js} +1 -1
  97. package/web/assets/{image-picture-flower.svg-D5oOhzzg.js → image-picture-flower.svg-Cm83ljlo.js} +1 -1
  98. package/web/assets/index-Bk2hTx0g.css +1 -0
  99. package/web/assets/{index-DBUGVNfV.js → index-D1xU-p3z.js} +570 -374
  100. package/web/assets/{interface-alert-exclamation-diamond.svg-H0YNQW-M.js → interface-alert-exclamation-diamond.svg-CXdlYd1V.js} +1 -1
  101. package/web/assets/{interface-alert-exclamation-triangle-warning.svg-MmLyfLsu.js → interface-alert-exclamation-triangle-warning.svg-CeyAEw_C.js} +1 -1
  102. package/web/assets/{interface-alert-information-circle.svg-DPtcCWUk.js → interface-alert-information-circle.svg-DUbwIFHR.js} +1 -1
  103. package/web/assets/{interface-award-crown.svg-D4TDd5uG.js → interface-award-crown.svg-BI3VqkS7.js} +1 -1
  104. package/web/assets/{interface-bookmark.svg-BOJKIiPZ.js → interface-bookmark-tag.svg-BNP52up1.js} +1 -1
  105. package/web/assets/{interface-bookmark-tag.svg-DY2HHX7j.js → interface-bookmark.svg-C_zoDBZC.js} +1 -1
  106. package/web/assets/{interface-calendar-date-one.svg-Ml9gQeRQ.js → interface-calendar-date-one.svg-DC0tGASA.js} +1 -1
  107. package/web/assets/{interface-content-book-open-pages.svg-DjrtXzto.js → interface-content-book-open-pages.svg-l-5VHr-g.js} +1 -1
  108. package/web/assets/{interface-content-book-page.svg-C7WDuE63.js → interface-content-book-page.svg-CagVpKh4.js} +1 -1
  109. package/web/assets/{interface-content-file.svg-Bj1fDi8J.js → interface-content-file.svg-B0aGvqPx.js} +1 -1
  110. package/web/assets/interface-content-folder.svg-BQN0Hvq-.js +1 -0
  111. package/web/assets/{interface-content-folder.svg-CtoSNYlE.js → interface-content-folder.svg-CVxNoyJ7.js} +1 -1
  112. package/web/assets/{interface-copy-clipboard.svg-CndmGYGb.js → interface-copy-clipboard.svg-D78-91bx.js} +1 -1
  113. package/web/assets/{interface-edit-attachment.svg-C8GKdk_Y.js → interface-edit-attachment.svg-D6pWV5bC.js} +1 -1
  114. package/web/assets/{interface-edit-binocular.svg-JwEBvvNd.js → interface-edit-binocular.svg-C6wbX04C.js} +1 -1
  115. package/web/assets/{interface-edit-magic-wand.svg-dhVstxFv.js → interface-edit-magic-wand.svg-CoZhFtfQ.js} +1 -1
  116. package/web/assets/{interface-edit-tool-paint-roller.svg-BOT-wlXa.js → interface-edit-tool-paint-roller.svg-DTQGU9t3.js} +1 -1
  117. package/web/assets/{interface-edit-tool-pencil.svg-Bkzz_y5T.js → interface-edit-tool-pencil.svg-ByQHy9tx.js} +1 -1
  118. package/web/assets/{interface-favorite-award.svg-CUsXllHu.js → interface-favorite-award.svg-0CNheH17.js} +1 -1
  119. package/web/assets/{interface-favorite-flag.svg-D4tuSHHG.js → interface-favorite-flag.svg-CCmC-2ES.js} +1 -1
  120. package/web/assets/{interface-favorite-heart.svg-D-HpTcun.js → interface-favorite-heart.svg-BVpuv5ru.js} +1 -1
  121. package/web/assets/{interface-favorite-star.svg-Cud3tgeY.js → interface-favorite-star.svg-D7-x2gpZ.js} +1 -1
  122. package/web/assets/{interface-favorite-stars-sparkles.svg-CSCGE6jh.js → interface-favorite-stars-sparkles.svg-BReo0WNX.js} +1 -1
  123. package/web/assets/{interface-hierarchy-flowchart.svg-qBM4FMpY.js → interface-hierarchy-flowchart.svg-D6044jiD.js} +1 -1
  124. package/web/assets/{interface-home-house.svg-YhTW7PqZ.js → interface-home-house.svg-BDbbqQ7y.js} +1 -1
  125. package/web/assets/{interface-hyperlink.svg-SzbaajGz.js → interface-hyperlink.svg-jMR6mBTh.js} +1 -1
  126. package/web/assets/{interface-lighting-brightness.svg-C2-5tyeD.js → interface-lighting-brightness.svg-DxAyAplK.js} +1 -1
  127. package/web/assets/{interface-lock-closed.svg-gcAhT36X.js → interface-lock-closed.svg-KHv3R3lv.js} +1 -1
  128. package/web/assets/{interface-lock-open-unlock.svg-BC3s82xO.js → interface-lock-open-unlock.svg-DTVS6Ld3.js} +1 -1
  129. package/web/assets/{interface-login-key.svg-C-2XbaU2.js → interface-login-key.svg-CmrsGePn.js} +1 -1
  130. package/web/assets/{interface-search.svg-B0QE3jtv.js → interface-search.svg-D68gdm71.js} +1 -1
  131. package/web/assets/{interface-setting-cog.svg-D3xvk8YU.js → interface-setting-cog.svg-BpmjacTl.js} +1 -1
  132. package/web/assets/{interface-share-megaphone-bullhorn.svg-CRa9esm0.js → interface-share-megaphone-bullhorn.svg-B1Qopu7g.js} +1 -1
  133. package/web/assets/{interface-share-rocket.svg-TdCeEZOl.js → interface-share-rocket.svg-xe-sFt3v.js} +1 -1
  134. package/web/assets/{interface-share-satellite.svg-DZTk90vq.js → interface-share-satellite.svg-DKB0wpfg.js} +1 -1
  135. package/web/assets/{interface-share-space-ship.svg-C_8R9-Nb.js → interface-share-space-ship.svg-BXUyLcC9.js} +1 -1
  136. package/web/assets/{interface-share.svg-CUenxjcS.js → interface-share.svg--X4Sy8OU.js} +1 -1
  137. package/web/assets/{interface-signal-square.svg-CM6pbneT.js → interface-signal-square.svg-SVcBtlCh.js} +1 -1
  138. package/web/assets/{interface-time-clock-circle.svg-DaKkRSPs.js → interface-time-clock-circle.svg-MMicUy7s.js} +1 -1
  139. package/web/assets/{interface-time-hour-glass.svg-BupwUgts.js → interface-time-hour-glass.svg-CqNSWB1s.js} +1 -1
  140. package/web/assets/{interface-users-multiple.svg-C3UEzidB.js → interface-users-multiple.svg-Cb-ACWeS.js} +1 -1
  141. package/web/assets/{interface-weather-moon.svg-DuiN-Lpe.js → interface-weather-moon.svg-BTBYfL8w.js} +1 -1
  142. package/web/assets/{javascript-cyCZQpCs.js → javascript-DW5dr95z.js} +1 -1
  143. package/web/assets/{jsonMode-C7bOJkxl.js → jsonMode-D3gEKJlR.js} +2 -2
  144. package/web/assets/{liquid-wNd-_zNC.js → liquid-DsB3REL_.js} +1 -1
  145. package/web/assets/{lspLanguageFeatures-BLqGpyw3.js → lspLanguageFeatures-Bo6XRGjF.js} +3 -3
  146. package/web/assets/{mail-chat-bubble-square.svg-DYRNn-F8.js → mail-chat-bubble-square.svg-DQPaE_l8.js} +1 -1
  147. package/web/assets/{mail-send-email-paper-airplane.svg-C52yHOX7.js → mail-send-email-paper-airplane.svg-SeNV9WPa.js} +1 -1
  148. package/web/assets/{mail-send-envelope.svg-0-Xc7oiy.js → mail-send-envelope.svg-DZr1S3ec.js} +1 -1
  149. package/web/assets/{mdx-Q8jGyneU.js → mdx-bfiZxOWu.js} +1 -1
  150. package/web/assets/{minus-DytRiHhn.js → minus-DDv3xj8k.js} +2 -2
  151. package/web/assets/modal-Cg41N0d3.js +5 -0
  152. package/web/assets/modal-hmS0I-a3.js +1 -0
  153. package/web/assets/{money-cashier-receipt.svg-vbdCrl5f.js → money-cashier-receipt.svg-CMGSkzy4.js} +1 -1
  154. package/web/assets/{money-currency-dollar-pay.svg-B0WPo5zD.js → money-currency-dollar-pay.svg-BqBhV1Bu.js} +1 -1
  155. package/web/assets/{money-graph-arrow-increase.svg-u1MYvAWb.js → money-graph-arrow-increase.svg-Jb3id3iX.js} +1 -1
  156. package/web/assets/{money-graph-bar-chart-increase.svg--vnvg38g.js → money-graph-bar-chart-increase.svg-C1_S5Hbg.js} +1 -1
  157. package/web/assets/multiselect-D6SVauJ0.js +251 -0
  158. package/web/assets/{nature-ecology-leaf.svg-Bm86NNsZ.js → nature-ecology-leaf.svg-Cv_Ub0f3.js} +1 -1
  159. package/web/assets/{password-D4dFyeDf.js → password-BnTuhvvF.js} +4 -4
  160. package/web/assets/{phone-telephone.svg-CDu0gfrF.js → phone-telephone.svg-C8m8L7Af.js} +1 -1
  161. package/web/assets/{plus-DyqgWY-p.js → plus-CwKOeZC2.js} +6 -6
  162. package/web/assets/process.store-CgBhdhXY.js +1 -0
  163. package/web/assets/{programming-bug.svg-GOtIFzyq.js → programming-bug.svg-BMbf-v1F.js} +1 -1
  164. package/web/assets/{programming-cloud.svg-Dob7Yyhx.js → programming-cloud.svg-DoETzNlz.js} +1 -1
  165. package/web/assets/{programming-computer-database-server.svg-BL9a2tMm.js → programming-computer-database-server.svg-DFsTW5pZ.js} +1 -1
  166. package/web/assets/{programming-computer-database.svg-CvSRjqHa.js → programming-computer-database.svg-Dap1d6pH.js} +1 -1
  167. package/web/assets/{programming-module-four-layout.svg-HWf5upwd.js → programming-module-four-layout.svg-Dlte_L9w.js} +1 -1
  168. package/web/assets/{programming-module-three.svg-v9W9DdQq.js → programming-module-three.svg-CF2uP4yd.js} +1 -1
  169. package/web/assets/{programming-module.svg-BHJLgt2p.js → programming-module.svg-DSm70VcV.js} +1 -1
  170. package/web/assets/{programming-script-code.svg-BDIXsnT6.js → programming-script-code.svg-C7LrjNTC.js} +1 -1
  171. package/web/assets/project-launch.store-HM4tWJyO.js +1 -0
  172. package/web/assets/project.store-BUbzqDJl.js +1 -0
  173. package/web/assets/{python-C_Cyv2aj.js → python-DGNKYf98.js} +1 -1
  174. package/web/assets/{radiobutton-F_E8D321.js → radiobutton-Drb-6bSf.js} +2 -2
  175. package/web/assets/{razor-DustIlxk.js → razor-CLJyE3n7.js} +1 -1
  176. package/web/assets/router-DoDaan68.js +1 -0
  177. package/web/assets/settings-scope-CA1gM0Eg.js +1 -0
  178. package/web/assets/{settings.store-CaQ6QeSL.js → settings.store-BJ7H5w4n.js} +1 -1
  179. package/web/assets/{shopping-cart.svg-DyoeDwG3.js → shopping-cart.svg-DqX4V0Z9.js} +1 -1
  180. package/web/assets/{shopping-gift-present.svg-BKPtii5v.js → shopping-gift-present.svg-BO9ncUGX.js} +1 -1
  181. package/web/assets/{shopping-shipping-box-parcel-package.svg-BvBFvRrd.js → shopping-shipping-box-parcel-package.svg-CEjca0LH.js} +1 -1
  182. package/web/assets/src-BKjJciGN.js +572 -0
  183. package/web/assets/src-F0BzIGAP.js +1 -0
  184. package/web/assets/{tabpanel-BbQfLmF1.js → tabpanel-BX_29Z1p.js} +3 -3
  185. package/web/assets/{tag-new-circle.svg-hTnv3ssK.js → tag-new-circle.svg-DEFm057e.js} +1 -1
  186. package/web/assets/theme-CXbG8xGR.js +1 -0
  187. package/web/assets/{toggleswitch-Ct_pe9Y7.js → toggleswitch-BCY7RhMa.js} +2 -2
  188. package/web/assets/{travel-map-earth-globe.svg-CpkewKvu.js → travel-map-earth-globe.svg-bobKX3Td.js} +1 -1
  189. package/web/assets/trpc-D953ojvb.js +1 -0
  190. package/web/assets/{tsMode-WejVCjTD.js → tsMode-HrRwEBdZ.js} +5 -5
  191. package/web/assets/{typescript-Bhy-yTki.js → typescript-CM93Igq2.js} +1 -1
  192. package/web/assets/useAiChatDrawer-uMew_avv.js +1 -0
  193. package/web/assets/{useCloudOrg-DyonGJWX.js → useCloudOrg-Dd4m5ej-.js} +1 -1
  194. package/web/assets/{useEnvVarAutocomplete-DFNddwqp.css → useEnvVarAutocomplete-ChKhQI_8.css} +1 -1
  195. package/web/assets/useEnvVarAutocomplete-CwYnoQY2.js +1 -0
  196. package/web/assets/useProjectCatalog-Ct2H50rw.js +1 -0
  197. package/web/assets/{useProjectCloudOrg-hi8bDROZ.js → useProjectCloudOrg-By2M8LTk.js} +1 -1
  198. package/web/assets/useServiceUrlHost-ZVKBplG8.js +1 -0
  199. package/web/assets/{xml-DhE3CKSm.js → xml-CJGov3Ex.js} +1 -1
  200. package/web/assets/{yaml-BnNl--CF.js → yaml-xY3BFPUE.js} +1 -1
  201. package/web/index.html +2 -2
  202. package/agent-manager-APY7SZ75.js +0 -9
  203. package/chunk-6T7R2JZR.js.map +0 -1
  204. package/chunk-K3H5WJRQ.js.map +0 -1
  205. package/chunk-OGCEFBSS.js.map +0 -1
  206. package/settings-manager-MQBUCUIO.js +0 -9
  207. package/src-Q2AWI57U.js.map +0 -1
  208. package/web/assets/AgentScalarChatInterface.vue-DVTbGHHm.js +0 -13
  209. package/web/assets/AgentTerminalPage-BK1JBjF7.js +0 -1
  210. package/web/assets/AiChatPage-CzLTf0Dh.js +0 -1
  211. package/web/assets/AppLayout-DIpd73gZ.js +0 -1
  212. package/web/assets/AuditPage-DDemuQXR.js +0 -1
  213. package/web/assets/CloudAccountPage-BW4UfXnM.js +0 -1
  214. package/web/assets/CloudAccountPage-f9nRZeNR.js +0 -1
  215. package/web/assets/CloudMessagingPage-2gjGcJwo.js +0 -2
  216. package/web/assets/DashboardPage-2qCxx4Sm.js +0 -164
  217. package/web/assets/DirectoryPickerDialog-DxxSRtWi.js +0 -1
  218. package/web/assets/EnvironmentFormPage-Cg6oojrW.js +0 -1
  219. package/web/assets/EnvironmentListPage-DgQaxPct.js +0 -1
  220. package/web/assets/KanbanBoardPage-C2zkv8-3.js +0 -4
  221. package/web/assets/KanbanListPage-nH2wjGBV.js +0 -1
  222. package/web/assets/LaunchProjectPage-CGr9lWsC.js +0 -1
  223. package/web/assets/LaunchServicesPage-CLGbYznW.js +0 -1
  224. package/web/assets/LogViewer-CxPBFVR1.js +0 -3
  225. package/web/assets/OrgKeyPrompt-bIIIdk_5.js +0 -1
  226. package/web/assets/ProcessDetailPage-Uh2TiAAJ.js +0 -1
  227. package/web/assets/RandomJobTestPage-D7j9GCJM.js +0 -2
  228. package/web/assets/ScenarioEditorPage-Ct8pvptW.js +0 -14
  229. package/web/assets/SendToChatDialog-Byzw5hfV.js +0 -1
  230. package/web/assets/ServiceControlsBar-Dht6GO3P.js +0 -1
  231. package/web/assets/ServiceDetailPage-CbeFd4R4.js +0 -2
  232. package/web/assets/ServiceFormPage-BNbDJB1o.js +0 -1
  233. package/web/assets/SettingsPage-Dxhmpet-.js +0 -2063
  234. package/web/assets/UnlockPage-DJfS33Iu.js +0 -1
  235. package/web/assets/VaultMigrationPage-2bVNCDJk.js +0 -1
  236. package/web/assets/VersionBadge-1RvUznxK.js +0 -1
  237. package/web/assets/WikiPage-Ck-HfGMo.js +0 -1
  238. package/web/assets/WikiView-DnZi2CXf.js +0 -1
  239. package/web/assets/add-scalar-classes-DZeMJUzs.js +0 -387
  240. package/web/assets/card-C0WVIEBg.js +0 -32
  241. package/web/assets/client-DKfN77U3.js +0 -24
  242. package/web/assets/cloud-chat.store-C0O-wJT9.js +0 -1
  243. package/web/assets/cloud-client-D4Mr6l0A.js +0 -1
  244. package/web/assets/cloud-session-kXf1tl4W.js +0 -1
  245. package/web/assets/column-BGKAKQS9.js +0 -763
  246. package/web/assets/index-nUGeXteT.css +0 -1
  247. package/web/assets/interface-content-folder.svg-DGDMeUjj.js +0 -1
  248. package/web/assets/modal-DIpZHaX_.js +0 -1
  249. package/web/assets/modal-Dx2t6N7O.js +0 -5
  250. package/web/assets/multiselect-0FlwwPJ2.js +0 -251
  251. package/web/assets/process.store-Dy_a775l.js +0 -1
  252. package/web/assets/project-launch.store-BFSsFFjl.js +0 -1
  253. package/web/assets/project.store-CbypuRJa.js +0 -1
  254. package/web/assets/router-BujVDJWr.js +0 -1
  255. package/web/assets/settings-scope-hbAQWfja.js +0 -1
  256. package/web/assets/src-40AEWC6e.js +0 -1
  257. package/web/assets/src-BsM0Obsp.css +0 -1
  258. package/web/assets/src-CagkHqfs.js +0 -197
  259. package/web/assets/src-XBWUvtlx.js +0 -572
  260. package/web/assets/theme-CXVscC2X.js +0 -1
  261. package/web/assets/trpc-C5MKhb-3.js +0 -1
  262. package/web/assets/useAiChatDrawer-tRwj5DTE.js +0 -1
  263. package/web/assets/useEnvVarAutocomplete-wZvdJ--F.js +0 -1
  264. package/web/assets/useProjectCatalog-tOf78QC4.js +0 -1
  265. package/web/assets/useServiceUrlHost-B9WSS4Mi.js +0 -1
  266. package/web/assets/w3c-keyname-tfhvQ9CV.js +0 -1
  267. /package/{agent-manager-APY7SZ75.js.map → agent-manager-HOL7ZG4V.js.map} +0 -0
  268. /package/{chat-QLIFNGA2.js.map → chat-XUIXGKSR.js.map} +0 -0
  269. /package/{chunk-77UQ2UKW.js.map → chunk-I2AZCZMR.js.map} +0 -0
  270. /package/{companion-RLSRXTNO.js.map → companion-POUL5OGO.js.map} +0 -0
  271. /package/{desktop-launcher-Q6J2LSVP.js.map → desktop-launcher-QVYFCE56.js.map} +0 -0
  272. /package/{settings-manager-MQBUCUIO.js.map → settings-manager-KKSPWSM6.js.map} +0 -0
  273. /package/web/assets/{abap-DblxFMgX.js → abap-cDlgWO6H.js} +0 -0
  274. /package/web/assets/{apex-C_fmPlQy.js → apex-DxzSGotK.js} +0 -0
  275. /package/web/assets/{azcli-D73HZyav.js → azcli-CUNfZLtQ.js} +0 -0
  276. /package/web/assets/{bat-Br4Biw6V.js → bat-C-oKGifY.js} +0 -0
  277. /package/web/assets/{bicep-Cwg6_V-m.js → bicep-C4xTl9QV.js} +0 -0
  278. /package/web/assets/{browser-D6yPejPt.js → browser-CE5P0iHW.js} +0 -0
  279. /package/web/assets/{cameligo-Dq6Izwzi.js → cameligo-COpO1ZvP.js} +0 -0
  280. /package/web/assets/{clojure-U0exYVUx.js → clojure-D8gp5eAN.js} +0 -0
  281. /package/web/assets/{coffee-mPGPozSE.js → coffee-De9HMN-q.js} +0 -0
  282. /package/web/assets/{cpp-lpr_PQMA.js → cpp-C4pJyeKy.js} +0 -0
  283. /package/web/assets/{csharp-saQQwY3s.js → csharp-B4wLqqiU.js} +0 -0
  284. /package/web/assets/{csp-CcDpUBZN.js → csp-Do0Gg4qE.js} +0 -0
  285. /package/web/assets/{css-D6QFYBbL.js → css-BLq1nXK0.js} +0 -0
  286. /package/web/assets/{cypher-B_4KCq-w.js → cypher-CDQzi0TV.js} +0 -0
  287. /package/web/assets/{dart-CgLuovz1.js → dart-bTPYn7bY.js} +0 -0
  288. /package/web/assets/{dockerfile-DObNIc52.js → dockerfile-yM7ZnQpo.js} +0 -0
  289. /package/web/assets/{ecl-BpCwjSsH.js → ecl-BgTpp_c4.js} +0 -0
  290. /package/web/assets/{elixir-CebZWKus.js → elixir-DO7wJ1ib.js} +0 -0
  291. /package/web/assets/{flow9-DhoDs9JD.js → flow9-B2Ko_py3.js} +0 -0
  292. /package/web/assets/{fsharp-YDWqgr0W.js → fsharp-DNdx3wKW.js} +0 -0
  293. /package/web/assets/{go-CaPX4mu5.js → go-D7LajbkE.js} +0 -0
  294. /package/web/assets/{graphql-DOSo46Ed.js → graphql-BCQSwUPo.js} +0 -0
  295. /package/web/assets/{hcl-nSYpx4qj.js → hcl-DR3uNUXT.js} +0 -0
  296. /package/web/assets/{ini-Ca22qQIM.js → ini-zkzQPitL.js} +0 -0
  297. /package/web/assets/{java-KrQduvTo.js → java-YFwuCa4S.js} +0 -0
  298. /package/web/assets/{julia-Bb2MDsY_.js → julia-WWwv4jLR.js} +0 -0
  299. /package/web/assets/{kotlin-DB7e82Vg.js → kotlin-FwlJkapy.js} +0 -0
  300. /package/web/assets/{less-K5fiQqgY.js → less-CyhHW1nH.js} +0 -0
  301. /package/web/assets/{lexon-Do9EcqkA.js → lexon-BScN_v1c.js} +0 -0
  302. /package/web/assets/{lua-DU9Dpzvs.js → lua-eWb9GpZJ.js} +0 -0
  303. /package/web/assets/{m3-pF6PmPkL.js → m3-C4NDU793.js} +0 -0
  304. /package/web/assets/{markdown-BNRSTD3D.js → markdown-DWWpXAtL.js} +0 -0
  305. /package/web/assets/{mips-OIXxWbNl.js → mips-BpJKGw00.js} +0 -0
  306. /package/web/assets/{msdax-Blzk7oHr.js → msdax-BiuvyZwP.js} +0 -0
  307. /package/web/assets/{mysql-CISO3r5e.js → mysql-DJW9Aa9q.js} +0 -0
  308. /package/web/assets/{objective-c-CfnC_OAj.js → objective-c-YouNnWOW.js} +0 -0
  309. /package/web/assets/{pascal-BUF-IB2S.js → pascal-jQsRnBre.js} +0 -0
  310. /package/web/assets/{pascaligo-BKce2a7P.js → pascaligo-DZI84_dt.js} +0 -0
  311. /package/web/assets/{perl-BSNQJqG3.js → perl-D9TKt-s6.js} +0 -0
  312. /package/web/assets/{pgsql-B6gnZ9Pz.js → pgsql-DXgI6ehC.js} +0 -0
  313. /package/web/assets/{php-D3DRjlE-.js → php-Cgqd4wfW.js} +0 -0
  314. /package/web/assets/{pla-DMkpEwWz.js → pla-DRDkvVUU.js} +0 -0
  315. /package/web/assets/{postiats-CS9vJAK8.js → postiats-BOLeCCau.js} +0 -0
  316. /package/web/assets/{powerquery-5wdkUpGP.js → powerquery-DCDtPNBU.js} +0 -0
  317. /package/web/assets/{powershell-CFy-OLZK.js → powershell-WnZ4e5ia.js} +0 -0
  318. /package/web/assets/{protobuf-BexdMU0z.js → protobuf-C9Dg-C9o.js} +0 -0
  319. /package/web/assets/{pug-D7XxQLO5.js → pug-Dr8RhWun.js} +0 -0
  320. /package/web/assets/{qsharp-Cbf2FpNP.js → qsharp-DukGWvWZ.js} +0 -0
  321. /package/web/assets/{r-DF7k9FbU.js → r-BC41pTgC.js} +0 -0
  322. /package/web/assets/{redis-CsS7YEaP.js → redis-D-gYIUku.js} +0 -0
  323. /package/web/assets/{redshift-ELXdujA0.js → redshift-r0XbF8XS.js} +0 -0
  324. /package/web/assets/{restructuredtext-xoFIa5lj.js → restructuredtext-DsJDS7Eu.js} +0 -0
  325. /package/web/assets/{ruby-BqZs-MXO.js → ruby-JPrm3ODz.js} +0 -0
  326. /package/web/assets/{rust-Bg6ZO_et.js → rust-C8IjEXni.js} +0 -0
  327. /package/web/assets/{sb-DiB12F6v.js → sb-D8vNiFl9.js} +0 -0
  328. /package/web/assets/{scala-DElpnKpP.js → scala-DThahuqf.js} +0 -0
  329. /package/web/assets/{scheme-1ahRBKOq.js → scheme-CvEQUYYf.js} +0 -0
  330. /package/web/assets/{scss-DEMPy3QV.js → scss-C6obpl8K.js} +0 -0
  331. /package/web/assets/{shell-DPy86lBu.js → shell-BnVmd31t.js} +0 -0
  332. /package/web/assets/{solidity-BJ26fCkf.js → solidity-Bo6zEybc.js} +0 -0
  333. /package/web/assets/{sophia-BisqpnJK.js → sophia-DuB3Px8n.js} +0 -0
  334. /package/web/assets/{sparql-C7Lhhim4.js → sparql-CW9HqMW2.js} +0 -0
  335. /package/web/assets/{sql-BXVElqbr.js → sql-CPCPCQON.js} +0 -0
  336. /package/web/assets/{st-C3Kk-cML.js → st-AxC3n-1-.js} +0 -0
  337. /package/web/assets/{swift-D8IWJydR.js → swift-CgZOS_MZ.js} +0 -0
  338. /package/web/assets/{systemverilog-524SrI2Q.js → systemverilog-DYR-yx39.js} +0 -0
  339. /package/web/assets/{tcl-MYNTx-d-.js → tcl-4hAvWv0d.js} +0 -0
  340. /package/web/assets/{twig-BAVZN0tW.js → twig-Cf_07aFW.js} +0 -0
  341. /package/web/assets/{typespec-BhgMHLJQ.js → typespec-2tXj8s11.js} +0 -0
  342. /package/web/assets/{vb-BLauMmY8.js → vb-DLpM4f4s.js} +0 -0
  343. /package/web/assets/{wgsl-ybvhdvmz.js → wgsl-BcECae_z.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../server/src/trpc/routers/chat.ts","../../../packages/mcp-server/src/launch.ts","../../server/src/services/codex-sessions.ts","../../server/src/trpc/trpc.ts","../../server/src/services/api-key-store.ts","../../server/src/trpc/shared-schemas.ts","../../server/src/services/workspace-cwd.ts","../../server/src/services/environment-overrides-store.ts","../../server/src/services/environment-resolver.ts","../../../packages/ai-capabilities/src/capabilities.ts","../../../packages/ai-capabilities/src/adapters/anthropic.ts","../../../packages/ai-capabilities/src/adapters/openai.ts","../../../packages/ai-capabilities/src/adapters/prompt.ts","../../../packages/ai-capabilities/src/trpc-caller.ts","../../server/src/services/llms-txt-generator.ts","../../server/src/services/chat-tools.ts","../../server/src/trpc/routers/service.ts","../../server/src/services/audit-service.ts","../../server/src/services/log-parser-store.ts","../../server/src/services/service-builtins.ts","../../server/src/services/claude-usage.ts","../../server/src/services/codex-usage.ts","../../server/src/services/scenario-store.ts","../../server/src/services/scenario-run-store.ts","../../server/src/services/run-store-utils.ts","../../server/src/services/ai-provider-utils.ts","../../server/src/services/settings-scope.ts","../../server/src/services/image-storage.service.ts","../../server/src/services/message-images.ts","../../server/src/trpc/routers/cloud-auth.ts","../../server/src/services/cloud-session-store.ts","../../server/src/services/lan-mode.ts","../../server/src/config/app-version.ts","../../server/src/services/cloud-api.ts","../../server/src/services/kanban-store.ts","../../server/src/services/kanban-commands.ts","../../server/src/services/ai-runners/claude-code-runner.ts","../../server/src/services/ai-runners/scenario-tokens.ts","../../server/src/services/ai-runners/claude-pty-runner.ts","../../server/src/services/ai-runners/claude-pty-tail.ts","../../server/src/services/ai-runners/codex-app-server-client.ts","../../server/src/services/ai-runners/codex-runner.ts"],"sourcesContent":["import { z } from 'zod';\nimport Anthropic from '@anthropic-ai/sdk';\nimport OpenAI from 'openai';\nimport { fileURLToPath } from 'node:url';\nimport { runeyaMcpConfig } from '@runeya/packages-mcp-server/launch';\nimport { isCliAvailable } from '@runeya/packages-shared/node';\nimport {\n claudeSessionExists,\n readClaudeTranscript,\n syncConversationFromClaudeTranscript,\n getLastTranscriptActivity,\n forgetAnnouncedTurns,\n} from '../../services/claude-transcript-sync.js';\nimport { resolveCodexResumeId } from '../../services/codex-sessions.js';\nimport {\n forgetCodexTurns,\n noteSpawnConversation,\n observePtyChunk,\n forgetSpawn,\n} from '../../services/codex-transcript-sync.js';\n\nimport { TRPCError } from '@trpc/server';\nimport { router, protectedProcedure } from '../trpc.js';\nimport type { AuthContext } from '../trpc.js';\nimport {\n ConversationIdSchema,\n Id128Schema,\n ShortString200Schema,\n} from '../shared-schemas.js';\nimport { projectStore } from '../../services/project-store.js';\nimport { serviceStore } from '../../services/service-store.js';\nimport { resolveWorkspaceCwd, resolveAgentCwd, resolveConversationCwd } from '../../services/workspace-cwd.js';\nimport { environmentResolver } from '../../services/environment-resolver.js';\nimport { generateLlmsTxt, generateAdminLlmsTxt } from '../../services/llms-txt-generator.js';\nimport { CHAT_TOOLS, CHAT_TOOLS_OPENAI, TOOL_SAFETY_INSTRUCTION, executeTool } from '../../services/chat-tools.js';\nimport { env } from '../../config/env.js';\nimport { resolveServiceVariables } from './service.js';\nimport { conversationStore } from '../../services/conversation-store.js';\nimport type { ConversationChangeEvent, MessagePart } from '../../services/conversation-store.js';\nimport type { ApiKey } from '@runeya/packages-shared';\nimport { getClaudeUsageEntries } from '../../services/claude-usage.js';\nimport { getCodexUsageEntries } from '../../services/codex-usage.js';\nimport { activeSessionRegistry } from '../../services/active-session-registry.js';\nimport type { ActiveSession, QueueItem } from '../../services/active-session-registry.js';\nimport { scenarioStore } from '../../services/scenario-store.js';\nimport { scenarioRunStore } from '../../services/scenario-run-store.js';\nimport {\n DEFAULT_MODELS,\n OPENAI_COMPAT_BASE_URLS,\n fetchProviderModels,\n getActiveProvider,\n} from '../../services/ai-provider-utils.js';\nimport { vaultOfProject } from '../../services/settings-scope.js';\nimport { settingsManager } from '../../services/settings-manager.js';\nimport { resolveImagesToBase64 } from '../../services/message-images.js';\nimport { imageStorageService } from '../../services/image-storage.service.js';\nimport { buildKanbanCardPrompt, generateKanbanPopulatePrompt } from '../../services/kanban-commands.js';\nimport { kanbanStore } from '../../services/kanban-store.js';\nimport { ClaudeCodeRunner } from '../../services/ai-runners/claude-code-runner.js';\nimport { CodexRunner } from '../../services/ai-runners/codex-runner.js';\nimport { agentManager } from '../../services/agent-manager.js';\nimport {\n resetRelaunches,\n companionSystemNote,\n forgetReviewProgress,\n deliverToMainConversation,\n COMPANION_SIGNATURE,\n lastCompanionDelivery,\n forgetDelivery,\n companionThinkingIds,\n isCompanionThinking,\n onCompanionThinkingChanged,\n runCompanionNow,\n companionSkips,\n forgetCompanionSkip,\n} from '../../services/companion.js';\n\nconst MessageSchema = z.object({\n role: z.enum(['user', 'assistant']),\n content: z.string().max(100_000),\n});\n\nconst ImagePartSchema = z.object({\n type: z.literal('image'),\n imageId: z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),\n url: z.string().max(500),\n mimeType: z.string().max(100),\n altText: z.string().max(500).optional(),\n});\nconst ImagePartsSchema = z.array(ImagePartSchema).max(5).optional();\n\nconst LANGUAGE_INSTRUCTION = 'Always respond in the same language the user writes in.';\n\n\nasync function buildSystemPrompt(apiKey: ApiKey | null): Promise<string> {\n const allServices = await serviceStore.list();\n const resolvedServices = await Promise.all(allServices.map(resolveServiceVariables));\n const context = apiKey\n ? generateLlmsTxt(apiKey, resolvedServices.filter(s => apiKey.scopes.services.some(p => p.serviceId === s.id)))\n : generateAdminLlmsTxt(resolvedServices);\n const runeya_ai_vars = Object.entries(process.env)\n .filter(([k]) => k.startsWith('RUNEYA_AI_'))\n .map(([k, v]) => `${k}=${v}`)\n .join('\\n');\n const envSection = runeya_ai_vars ? `\\n\\n## Environment\\n\\n${runeya_ai_vars}` : '';\n return `${LANGUAGE_INSTRUCTION}\\n\\n${context}${envSection}`;\n}\n\n// ─── Event types yielded by the subscription ─────────────────────────────────\n\ntype ChatEvent =\n | { type: 'delta'; text: string }\n | { type: 'tool_call'; id: string; name: string; input: Record<string, unknown> }\n | { type: 'tool_result'; id: string; name: string; result: unknown; isError: boolean }\n | { type: 'done'; text: '' };\n\n// ─── Anthropic tool-use loop ──────────────────────────────────────────────────\n\nasync function* runAnthropic(\n client: Anthropic,\n model: string,\n system: string,\n initialMessages: Anthropic.MessageParam[],\n apiKey: ApiKey | null,\n authToken: string,\n signal?: AbortSignal,\n): AsyncGenerator<ChatEvent> {\n const messages: Anthropic.MessageParam[] = [...initialMessages];\n const MAX_TOOL_ROUNDS = 10;\n\n for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {\n // Buffer tool_use blocks; stream text deltas live\n const toolUseBlocks: Anthropic.ToolUseBlock[] = [];\n // Accumulate partial JSON for each tool use block (keyed by block index)\n const partialJson = new Map<number, string>();\n let assistantText = '';\n\n if (signal?.aborted) return;\n const stream = client.messages.stream({\n model,\n max_tokens: 16384,\n system,\n tools: CHAT_TOOLS as Anthropic.Tool[],\n messages,\n }, { signal });\n\n for await (const event of stream) {\n if (event.type === 'content_block_start' && event.content_block.type === 'tool_use') {\n toolUseBlocks.push({ ...event.content_block, input: {} } as Anthropic.ToolUseBlock);\n } else if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n assistantText += event.delta.text;\n yield { type: 'delta', text: event.delta.text };\n } else if (event.delta.type === 'input_json_delta') {\n const idx = toolUseBlocks.length - 1;\n if (idx >= 0) {\n partialJson.set(idx, (partialJson.get(idx) ?? '') + event.delta.partial_json);\n }\n }\n } else if (event.type === 'content_block_stop') {\n const idx = toolUseBlocks.length - 1;\n const last = toolUseBlocks[idx];\n const partial = partialJson.get(idx);\n if (last && partial !== undefined) {\n try { last.input = JSON.parse(partial); } catch { last.input = {}; }\n partialJson.delete(idx);\n }\n }\n }\n\n const finalMsg = await stream.finalMessage();\n\n // No tool calls → done\n if (finalMsg.stop_reason !== 'tool_use' || toolUseBlocks.length === 0) {\n yield { type: 'done', text: '' };\n return;\n }\n\n // Build assistant message with all content blocks\n const assistantContent: Anthropic.MessageParam['content'] = [];\n if (assistantText) assistantContent.push({ type: 'text', text: assistantText } as Anthropic.TextBlockParam);\n assistantContent.push(...toolUseBlocks);\n messages.push({ role: 'assistant', content: assistantContent });\n\n // Execute each tool and collect results\n const toolResults: Anthropic.ToolResultBlockParam[] = [];\n for (const block of toolUseBlocks) {\n yield { type: 'tool_call', id: block.id, name: block.name, input: block.input as Record<string, unknown> };\n let result: unknown;\n let isError = false;\n try {\n result = await executeTool(block.name, block.input as Record<string, unknown>, apiKey, authToken);\n } catch (err) {\n result = (err as Error).message;\n isError = true;\n }\n yield { type: 'tool_result', id: block.id, name: block.name, result, isError };\n toolResults.push({\n type: 'tool_result',\n tool_use_id: block.id,\n content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),\n is_error: isError,\n });\n }\n\n messages.push({ role: 'user', content: toolResults });\n // Loop: send tool results back to AI\n }\n yield { type: 'done', text: '' };\n}\n\n// ─── OpenAI tool-use loop ─────────────────────────────────────────────────────\n\nasync function* runOpenAI(\n client: OpenAI,\n model: string,\n initialMessages: OpenAI.Chat.ChatCompletionMessageParam[],\n apiKey: ApiKey | null,\n authToken: string,\n signal?: AbortSignal,\n): AsyncGenerator<ChatEvent> {\n const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [...initialMessages];\n const MAX_TOOL_ROUNDS = 10;\n\n for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {\n type ToolCallAcc = { id: string; name: string; args: string };\n const toolCallsAcc: Record<number, ToolCallAcc> = {};\n\n if (signal?.aborted) return;\n const stream = await client.chat.completions.create({\n model,\n messages,\n tools: CHAT_TOOLS_OPENAI,\n stream: true,\n max_tokens: 16384,\n temperature: 0,\n }, { signal });\n\n for await (const chunk of stream) {\n const choice = chunk.choices[0];\n if (!choice) continue;\n\n if (choice.delta?.content) {\n yield { type: 'delta', text: choice.delta.content };\n }\n\n // Accumulate tool call deltas\n for (const tc of choice.delta?.tool_calls ?? []) {\n const acc = toolCallsAcc[tc.index] ?? { id: '', name: '', args: '' };\n if (tc.id) acc.id = tc.id;\n if (tc.function?.name) acc.name = tc.function.name;\n if (tc.function?.arguments) acc.args += tc.function.arguments;\n toolCallsAcc[tc.index] = acc;\n }\n }\n\n // Some Ollama models return finish_reason 'stop' even when they made tool calls.\n // Fall back to checking if any tool calls were accumulated.\n if (Object.keys(toolCallsAcc).length === 0) {\n yield { type: 'done', text: '' };\n return;\n }\n\n // Build assistant message with tool calls\n const toolCallsList = Object.values(toolCallsAcc);\n messages.push({\n role: 'assistant',\n tool_calls: toolCallsList.map(tc => ({\n id: tc.id,\n type: 'function' as const,\n function: { name: tc.name, arguments: tc.args },\n })),\n });\n\n // Execute tools\n for (const tc of toolCallsList) {\n let input: Record<string, unknown> = {};\n try { input = JSON.parse(tc.args); } catch { /* empty */ }\n\n yield { type: 'tool_call', id: tc.id, name: tc.name, input };\n\n let result: unknown;\n let isError = false;\n try {\n result = await executeTool(tc.name, input, apiKey, authToken);\n } catch (err) {\n result = (err as Error).message;\n isError = true;\n }\n\n yield { type: 'tool_result', id: tc.id, name: tc.name, result, isError };\n messages.push({\n role: 'tool',\n tool_call_id: tc.id,\n content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),\n });\n }\n // Loop\n }\n yield { type: 'done', text: '' };\n}\n\n// ─── Session drainer (shared by new-message and reconnect) ───────────────────\n\nasync function* drainSession(\n session: ActiveSession,\n signal: AbortSignal | undefined,\n isReconnect: boolean,\n): AsyncGenerator<\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> }\n | { type: 'tool_result'; id: string; name: string; result: unknown; isError: boolean }\n | { type: 'done'; text: '' }\n> {\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: drainSession')\n // On reconnect, replay accumulated parts + current status\n if (isReconnect) {\n for (const part of session.parts) {\n if (part.type === 'thinking') {\n yield { type: 'thinking', text: part.text };\n } else if (part.type === 'text') {\n yield { type: 'delta', text: part.text };\n } else if (part.type === 'tool') {\n yield { type: 'tool_call', id: part.id, name: part.name, input: part.input ?? {} };\n if (!part.pending) yield { type: 'tool_result', id: part.id, name: part.name, result: part.result, isError: part.isError ?? false };\n }\n }\n if (session.lastStatus) {\n yield { type: 'status', text: session.lastStatus };\n }\n // If session already finished, yield done immediately\n if (session.status === 'done') {\n yield { type: 'done', text: '' };\n return;\n }\n }\n\n // Consumer cursor: skip buffered items, start receiving only NEW events\n // On reconnect we already replayed fullContent, so skip all existing buffer items\n let cursor = isReconnect ? session.totalProduced : (session.totalProduced - session.buffer.length);\n\n while (true) {\n // Drain any pending items\n while (cursor < session.totalProduced) {\n const idx = cursor - (session.totalProduced - session.buffer.length);\n const item: QueueItem | undefined = idx >= 0 ? session.buffer[idx] : undefined;\n cursor++;\n if (!item) continue;\n\n if (item.type === 'done') {\n yield { type: 'done', text: '' };\n return;\n }\n if (item.type === 'error') {\n yield { type: 'delta', text: `\\n\\n⚠️ ${item.message}` };\n continue;\n }\n if (item.type === 'status') {\n yield { type: 'status', text: item.text };\n continue;\n }\n if (item.type === 'thinking') {\n yield { type: 'thinking', text: item.text };\n continue;\n }\n if (item.type === 'context_usage') {\n yield { type: 'context_usage', inputTokens: item.inputTokens, contextWindow: item.contextWindow };\n continue;\n }\n if (item.type === 'tool_call') {\n yield { type: 'tool_call', id: item.id, name: item.name, input: item.input };\n continue;\n }\n if (item.type === 'tool_result') {\n yield { type: 'tool_result', id: item.id, name: item.name, result: item.result, isError: item.isError };\n continue;\n }\n yield item;\n }\n\n // If signal aborted, just stop yielding — process survives\n if (signal?.aborted) return;\n\n // Wait for next event from the registry, or signal abort\n await new Promise<void>(r => {\n if (cursor < session.totalProduced) { r(); return; }\n session.notify.add(r);\n const cleanup = () => { session.notify.delete(r); r(); };\n signal?.addEventListener('abort', cleanup, { once: true });\n });\n if (signal?.aborted) return;\n }\n}\n\n// ─── Shared CLI session starters (used by tRPC handlers + kanban queue) ──────\n\n/**\n * Le contexte kanban d'une conversation, ou rien si elle n'est liée à aucun\n * tableau.\n *\n * Ce bloc était recopié mot pour mot à quatre endroits. Le tenir ici est ce qui\n * garantit qu'une carte reçoit le même contexte, qu'elle parte du chat, d'un\n * CLI ou de la file.\n */\nasync function kanbanContextFor(\n conv: { kanbanBoardId?: string; kanbanCardId?: string } | null | undefined,\n): Promise<string> {\n if (!conv?.kanbanBoardId) return '';\n const kanbanBoard = await kanbanStore.get(conv.kanbanBoardId);\n if (!kanbanBoard) return '';\n return '\\n\\n' + (conv.kanbanCardId\n ? await buildKanbanCardPrompt(kanbanBoard, conv.kanbanCardId)\n : generateKanbanPopulatePrompt(kanbanBoard));\n}\n\nasync function buildCliSystemContext(conv: Awaited<ReturnType<typeof conversationStore.get>>, jwt: string | undefined): Promise<string> {\n // Les services du projet de la conversation, pas ceux de la machine. Un CLI\n // lancé sur une carte du projet A recevait la description de tous les\n // services de tous les projets — à commencer par ceux de Runeya lui-même,\n // dont l'URL d'API — et travaillait donc avec le mauvais inventaire.\n const allServices = await serviceStore.list();\n const ref = conv ? await conversationStore.projectRefOf(conv) : null;\n const project = ref ? await projectStore.get(ref) : null;\n const ownServices = project\n ? allServices.filter((svc) => project.serviceIds.includes(svc.id))\n : allServices;\n const resolvedServices = await Promise.all(ownServices.map(resolveServiceVariables));\n // Le projet est passé tel quel : c'est lui qui a servi à filtrer les services\n // ci-dessus, et l'agent a besoin de son id pour demander la même chose aux\n // tools — sans quoi `list_services` lui rend toute la machine.\n let systemContext = generateAdminLlmsTxt(resolvedServices, jwt, project);\n\n // Dans toute conversation CLI, que le compagnon soit actif ou non : le\n // réglage peut s'allumer en cours de route, et l'agent doit déjà savoir\n // reconnaître une remarque de relecture quand elle arrive. Jamais gardé par\n // `companionEnabled`, donc.\n //\n // Sauf pour le compagnon lui-même : il ne peut pas en avoir un (la route\n // `setCompanion` le refuse), et lui annoncer le contraire serait lui dire\n // quelque chose de faux sur sa propre situation.\n if (!conv?.isCompanion) systemContext += '\\n\\n' + companionSystemNote();\n\n systemContext += await kanbanContextFor(conv);\n return systemContext;\n}\n\n/**\n * Resolve the variables flagged `injectIntoAi` for the given active environment\n * (with inheritance) into a flat env dict. Returns undefined when nothing to inject.\n */\nasync function resolveInjectedEnv(environmentId?: string): Promise<Record<string, string> | undefined> {\n if (!environmentId) return undefined;\n try {\n const injected = await environmentResolver.resolveAiInjectedEnv(environmentId);\n return Object.keys(injected).length > 0 ? injected : undefined;\n } catch (err) {\n console.warn('[chat] resolveInjectedEnv failed:', (err as Error).message);\n return undefined;\n }\n}\n\nexport interface StartCliSessionOpts {\n /**\n * L'agent où lancer le CLI. Par défaut, l'agent par défaut.\n *\n * Explicite parce qu'une conversation peut tourner ailleurs : un terminal\n * interactif se lance avec l'agent d'un service, éventuellement distant. Une\n * session lancée sur l'agent par défaut travaillerait alors sur un autre\n * arbre — sans rien signaler, en rendant des verdicts sur le mauvais dépôt.\n */\n agentId?: string;\n model?: string;\n effort?: 'low' | 'medium' | 'high' | 'normal';\n jwt?: string;\n /** Extra metadata stored on the user message (for UI display) */\n userMessageParts?: MessagePart[];\n userMessageContextNote?: string;\n /** Active environment id: variables flagged `injectIntoAi` are injected into the process env. */\n environmentId?: string;\n}\n\n/**\n * Start a Claude Code session for a conversation.\n * Registers the session in activeSessionRegistry (so the frontend can subscribe),\n * builds the system context (services + kanban), and fires the runner.\n * Returns `{ session, done }` where `done` resolves when the runner finishes.\n * Throws if a session is already running for this conversation.\n */\nexport async function startClaudeCodeSession(\n convId: string,\n prompt: string,\n opts: StartCliSessionOpts = {},\n): Promise<{ session: ActiveSession; done: Promise<void> }> {\n const conv = await conversationStore.get(convId);\n if (!conv) throw new Error(`Conversation ${convId} not found`);\n\n const isResume = conv.messages.length > 0;\n conv.messages.push({\n role: 'user',\n content: prompt,\n parts: opts.userMessageParts,\n contextNote: opts.userMessageContextNote,\n });\n conv.messages.push({ role: 'assistant', content: '' });\n conversationStore.update(conv.id, { messages: conv.messages }, { silent: true }).catch(() => {});\n\n const systemContext = await buildCliSystemContext(conv, opts.jwt);\n\n // Le projet de la conversation ancre les tools : sans lui, `list_services`\n // rend tous les projets de la machine alors que le prompt n'en décrit qu'un.\n const mcpRef = conv ? await conversationStore.projectRefOf(conv) : null;\n const mcpConfig = runeyaMcpConfig({\n RUNEYA_BASE_URL: `http://localhost:${env.PORT}`,\n ...(opts.jwt ? { RUNEYA_API_TOKEN: opts.jwt } : {}),\n ...(mcpRef?.id ? { RUNEYA_PROJECT_ID: mcpRef.id } : {}),\n ...(mcpRef?.orgId ? { RUNEYA_ORG_ID: mcpRef.orgId } : {}),\n });\n\n const ac = new AbortController();\n const session = activeSessionRegistry.registerProviderApiSession(convId, ac);\n let capturedFinalResult = '';\n\n const resolvedAgentId = opts.agentId ?? agentManager.getDefaultAgentId();\n if (resolvedAgentId) {\n session.agentId = resolvedAgentId;\n }\n\n const injectedEnv = await resolveInjectedEnv(opts.environmentId);\n\n const done = new ClaudeCodeRunner().run({\n agentId: resolvedAgentId ?? undefined,\n // The conversation's project, not the server's launch directory.\n cwd: await conversationCwd(conv),\n sessionId: conv.id,\n prompt,\n isResume,\n systemContext,\n apiBaseUrl: `http://localhost:${env.PORT}`,\n apiToken: opts.jwt ?? '',\n ...(mcpRef?.id ? { projectId: mcpRef.id } : {}),\n ...(mcpRef?.orgId ? { orgId: mcpRef.orgId } : {}),\n model: opts.model ?? 'claude-opus-4-6',\n effort: opts.effort ?? 'normal',\n signal: ac.signal,\n skipTokenInstructions: true,\n mcpConfig,\n injectedEnv,\n onDelta: (text) => activeSessionRegistry.pushEvent(convId, { type: 'delta', text }),\n onStatus: (text) => activeSessionRegistry.pushEvent(convId, { type: 'status', text }),\n onThinking: (text) => activeSessionRegistry.pushEvent(convId, { type: 'thinking', text }),\n onToolCall: (id, name, input_) => activeSessionRegistry.pushEvent(convId, { type: 'tool_call', id, name, input: input_ }),\n onToolResult: (id, result, isError) => activeSessionRegistry.pushEvent(convId, { type: 'tool_result', id, name: '', result, isError }),\n onContextUsage: (inputTokens, contextWindow) => activeSessionRegistry.pushEvent(convId, { type: 'context_usage', inputTokens, contextWindow }),\n onResult: (result) => { if (result) capturedFinalResult = result; },\n }).then(() => {\n if (process.env.RUNEYA_DEBUG) console.log('[chat]: claude end...');\n if (activeSessionRegistry.get(convId) !== session) return;\n if (capturedFinalResult && capturedFinalResult.length > session.fullContent.length) {\n const missing = capturedFinalResult.slice(session.fullContent.length);\n activeSessionRegistry.pushEvent(convId, { type: 'delta', text: missing });\n }\n activeSessionRegistry.pushEvent(convId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(convId);\n }).catch((err: Error) => {\n if (process.env.RUNEYA_DEBUG) console.error('[chat]: error...', err?.message, err);\n if (activeSessionRegistry.get(convId) !== session) return;\n activeSessionRegistry.pushEvent(convId, { type: 'error', message: `Claude Code error: ${err.message}` });\n activeSessionRegistry.pushEvent(convId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(convId);\n });\n\n return { session, done };\n}\n\nexport interface StartCodexSessionOpts {\n /**\n * L'agent où lancer le CLI. Par défaut, l'agent par défaut.\n *\n * Explicite parce qu'une conversation peut tourner ailleurs : un terminal\n * interactif se lance avec l'agent d'un service, éventuellement distant. Une\n * session lancée sur l'agent par défaut travaillerait alors sur un autre\n * arbre — sans rien signaler, en rendant des verdicts sur le mauvais dépôt.\n */\n agentId?: string;\n model?: string;\n jwt?: string;\n images?: { imageId: string; base64: string; mimeType: string; altText?: string }[];\n userMessageParts?: MessagePart[];\n userMessageContextNote?: string;\n /** Active environment id: variables flagged `injectIntoAi` are injected into the process env. */\n environmentId?: string;\n}\n\n/**\n * Start a Codex session for a conversation.\n * Same contract as startClaudeCodeSession.\n */\nexport async function startCodexSession(\n convId: string,\n prompt: string,\n opts: StartCodexSessionOpts = {},\n): Promise<{ session: ActiveSession; done: Promise<void> }> {\n const conv = await conversationStore.get(convId);\n if (!conv) throw new Error(`Conversation ${convId} not found`);\n\n const isResume = !!conv.codexThreadId;\n conv.messages.push({\n role: 'user',\n content: prompt,\n parts: opts.userMessageParts,\n contextNote: opts.userMessageContextNote,\n });\n conv.messages.push({ role: 'assistant', content: '' });\n conversationStore.update(conv.id, { messages: conv.messages }, { silent: true }).catch(() => {});\n\n const systemContext = await buildCliSystemContext(conv, opts.jwt);\n\n const ac = new AbortController();\n const session = activeSessionRegistry.registerProviderApiSession(convId, ac);\n\n const resolvedAgentId = opts.agentId ?? agentManager.getDefaultAgentId();\n if (resolvedAgentId) {\n session.agentId = resolvedAgentId;\n }\n\n const injectedEnv = await resolveInjectedEnv(opts.environmentId);\n\n const done = new CodexRunner().run({\n agentId: resolvedAgentId ?? undefined,\n cwd: await conversationCwd(conv),\n sessionId: conv.codexThreadId ?? '',\n prompt,\n isResume,\n systemContext,\n apiBaseUrl: `http://localhost:${env.PORT}`,\n apiToken: opts.jwt ?? '',\n model: opts.model ?? '',\n effort: 'normal',\n signal: ac.signal,\n skipTokenInstructions: true,\n images: opts.images,\n injectedEnv,\n onThreadId: (threadId) => conversationStore.update(conv.id, { codexThreadId: threadId }).catch(() => {}),\n onDelta: (text) => activeSessionRegistry.pushEvent(convId, { type: 'delta', text }),\n onStatus: (text) => activeSessionRegistry.pushEvent(convId, { type: 'status', text }),\n onThinking: (text) => activeSessionRegistry.pushEvent(convId, { type: 'thinking', text }),\n onToolCall: (id, name, input_) => activeSessionRegistry.pushEvent(convId, { type: 'tool_call', id, name, input: input_ }),\n onToolResult: (id, result, isError) => activeSessionRegistry.pushEvent(convId, { type: 'tool_result', id, name: '', result, isError }),\n }).then(() => {\n if (activeSessionRegistry.get(convId) !== session) return;\n activeSessionRegistry.pushEvent(convId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(convId);\n }).catch((err: Error) => {\n if (activeSessionRegistry.get(convId) !== session) return;\n activeSessionRegistry.pushEvent(convId, { type: 'error', message: `Codex error: ${err.message}` });\n activeSessionRegistry.pushEvent(convId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(convId);\n });\n\n return { session, done };\n}\n\n// ─── Router ───────────────────────────────────────────────────────────────────\n\nconst MessagePartSchema = z.discriminatedUnion('type', [\n z.object({ type: z.literal('thinking'), text: z.string().max(100_000) }),\n z.object({ type: z.literal('text'), text: z.string().max(100_000) }),\n z.object({\n type: z.literal('tool'),\n id: Id128Schema,\n name: Id128Schema,\n input: z.record(z.string(), z.unknown()).optional(),\n rawInput: z.string().max(10_000).optional(),\n result: z.unknown().optional(),\n isError: z.boolean().optional(),\n pending: z.boolean(),\n }),\n]);\n\nconst StoredMessageSchema = z.object({\n role: z.enum(['user', 'assistant']),\n content: z.string().max(100_000),\n parts: z.array(MessagePartSchema).max(200).optional(),\n});\n\n/**\n * Le répertoire de travail d'une conversation.\n *\n * Une conversation ne nomme que l'id de son projet, et cet id peut désigner un\n * projet dans chaque organisation ; le store retrouve le couple à partir du\n * coffre d'où la conversation a été lue, et c'est ce couple qui place le\n * répertoire.\n */\nasync function conversationCwd(conv: { id: string; projectId?: string | null }): Promise<string | undefined> {\n return resolveConversationCwd(conv.id);\n}\n\n/**\n * Démarre un tour sur une conversation, quel que soit le runner.\n *\n * L'aiguillage `codex ? … : claude-code` vivait dans `kanban-queue`; le\n * compagnon en a besoin du même. Un motif à deux endroits s'extrait (§16) :\n * recopié, la prochaine famille de runner n'en corrigerait qu'un.\n */\nexport function startCliSession(\n runner: 'claude-code' | 'codex' | undefined,\n convId: string,\n prompt: string,\n opts: StartCliSessionOpts & StartCodexSessionOpts = {},\n): Promise<{ session: ActiveSession; done: Promise<void> }> {\n return runner === 'codex'\n ? startCodexSession(convId, prompt, opts)\n : startClaudeCodeSession(convId, prompt, opts);\n}\n\nexport const chatRouter = router({\n conversations: router({\n /**\n * Le flux des conversations du projet ouvert.\n *\n * Filtré comme `list` : l'instantané initial remplace la liste du client,\n * et sans le même filtre il y reversait toutes les conversations de la\n * machine, une seconde après un chargement pourtant filtré.\n */\n onChanged: protectedProcedure\n .input(z.object({\n projectId: Id128Schema.optional(),\n orgId: Id128Schema.nullable().optional(),\n }).optional())\n .subscription(async function* ({ input }) {\n const belongs = input?.projectId\n ? await conversationStore.matcherForProject({ id: input.projectId, orgId: input.orgId })\n : () => true;\n const initial = (await conversationStore.list()).filter(belongs);\n yield { type: 'initial' as const, conversations: initial };\n\n const queue: ConversationChangeEvent[] = [];\n let resolve: (() => void) | null = null;\n\n const handler = (event: ConversationChangeEvent) => {\n // Une suppression passe toujours : son id ne dit pas le projet, et\n // retirer un id inconnu du client ne coûte rien. Un rechargement\n // aussi : il ne porte aucune conversation à filtrer.\n if (event.type !== 'deleted' && event.type !== 'reloaded' && !belongs(event.conversation)) return;\n if (queue.length >= 500) queue.shift();\n queue.push(event);\n if (resolve) {\n resolve();\n resolve = null;\n }\n };\n\n conversationStore.onChanged(handler);\n\n try {\n while (true) {\n if (queue.length > 0) {\n const event = queue.shift()!;\n // Le store a tout relu : ce que le client détient est périmé dans\n // son ensemble, donc on lui renvoie l'instantané plutôt qu'un\n // delta qui ne dirait pas ce qui a disparu.\n if (event.type === 'reloaded') {\n yield { type: 'initial' as const, conversations: (await conversationStore.list()).filter(belongs) };\n continue;\n }\n yield event;\n } else {\n await new Promise<void>((r) => { resolve = r; });\n }\n }\n } finally {\n conversationStore.offChanged(handler);\n }\n }),\n\n /**\n * Conversations of one project.\n *\n * A conversation belongs to the project it was opened from, so the caller\n * says which one it is looking at. Ones that name no project — written\n * before the field existed, or that a migration could not attribute — are\n * returned only for the projects of the vault they were read from: still\n * reachable where they were written, without leaking into every other\n * organization's projects.\n */\n list: protectedProcedure\n .input(z.object({\n projectId: Id128Schema.optional(),\n orgId: Id128Schema.nullable().optional(),\n }).optional())\n .query(async ({ input }) => {\n const projectId = input?.projectId;\n if (!projectId) return conversationStore.list();\n return conversationStore.listForProject({ id: projectId, orgId: input?.orgId });\n }),\n\n get: protectedProcedure\n .input(z.object({ id: Id128Schema }))\n .query(async ({ input }) => {\n const conv = await conversationStore.get(input.id);\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n return conv;\n }),\n\n create: protectedProcedure\n .input(z.object({\n title: ShortString200Schema.optional(),\n isCli: z.boolean().optional(),\n runner: z.enum(['claude-code', 'codex']).optional(),\n providerId: Id128Schema.optional(),\n scenarioId: Id128Schema.optional(),\n scenarioInput: z.string().max(100_000).optional(),\n kanbanBoardId: Id128Schema.optional(),\n kanbanCardId: Id128Schema.optional(),\n // The project the conversation is about: only the caller knows which\n // one it is looking at.\n projectId: Id128Schema.optional(),\n orgId: z.string().max(128).nullable().optional(),\n }))\n .mutation(async ({ input }) => {\n let scenario = null;\n if (input.scenarioId) {\n scenario = await scenarioStore.get(\n input.scenarioId,\n input.orgId ?? await vaultOfProject(input.projectId),\n );\n if (!scenario) throw new TRPCError({ code: 'NOT_FOUND', message: 'Scenario not found' });\n }\n\n // Le compagnon d'office, si l'organisation l'a réglé ainsi. Décidé ici\n // et non à la première fin de tour : un réglage changé en cours de route\n // ne doit pas allumer le compagnon sur des conversations déjà ouvertes.\n // Réglage du poste, sans coffre : il décrit cette machine, pas une\n // équipe (§9).\n const machineSettings = await settingsManager.getSettings();\n const companionByDefault = machineSettings.companion?.enabledByDefault ?? false;\n\n const conv = await conversationStore.create(\n input.title,\n input.isCli ?? false,\n input.scenarioId,\n input.scenarioId ? 'scenario' : undefined,\n input.runner,\n input.kanbanBoardId,\n input.kanbanCardId,\n input.providerId,\n input.projectId,\n input.orgId,\n companionByDefault ? { companionEnabled: true } : undefined,\n );\n\n if (scenario) {\n await scenarioRunStore.initializeConversationWorkspace(\n conv.id,\n scenario,\n input.scenarioInput ?? '',\n );\n }\n\n return conv;\n }),\n\n update: protectedProcedure\n .input(z.object({\n id: Id128Schema,\n title: ShortString200Schema.optional(),\n messages: z.array(StoredMessageSchema).max(500).optional(),\n contextUsage: z.object({\n inputTokens: z.number().int().nonnegative(),\n contextWindow: z.number().int().positive(),\n }).optional(),\n }))\n .mutation(async ({ input }) => {\n const conv = await conversationStore.update(input.id, { title: input.title, messages: input.messages, contextUsage: input.contextUsage });\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n return conv;\n }),\n\n /**\n * Activer ou couper le compagnon sur une conversation.\n *\n * Route dédiée plutôt qu'un champ de plus sur `update` : celle-ci porte des\n * messages, et un réglage qui voyage avec 500 messages se perdrait dans un\n * patch dont ce n'est pas le sujet.\n */\n setCompanion: protectedProcedure\n .input(z.object({\n id: Id128Schema,\n enabled: z.boolean(),\n runner: z.enum(['claude-code', 'codex']).optional(),\n }))\n .mutation(async ({ input }) => {\n const existing = await conversationStore.get(input.id);\n if (!existing) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n if (existing.isCompanion) {\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'A companion cannot have a companion' });\n }\n const conv = await conversationStore.update(input.id, {\n companionEnabled: input.enabled,\n ...(input.runner ? { companionRunner: input.runner } : {}),\n });\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n resetRelaunches(input.id);\n // Couper puis rallumer, c'est repartir de zéro : garder la raison du\n // dernier renoncement afficherait, sur un compagnon qu'on vient\n // d'activer, un fait qui date de sa vie précédente.\n forgetCompanionSkip(input.id);\n return conv;\n }),\n\n /**\n * La dernière livraison du compagnon vers cette conversation.\n *\n * Rend visible à l'écran ce qui n'existait que dans les journaux : un\n * compagnon silencieux parce qu'il n'avait rien à dire et un message qui\n * n'a pas pu être transmis se ressemblent parfaitement sans cette trace.\n */\n companionDelivery: protectedProcedure\n .input(z.object({ id: Id128Schema }))\n .query(async ({ input }) => {\n const conv = await conversationStore.get(input.id);\n /*\n * « Relit-il en ce moment ? » ne peut venir que d'ici.\n *\n * Le front ne connaît que les flux qu'il a lui-même ouverts ; le\n * compagnon, lui, est lancé par le serveur. Sans cette réponse,\n * l'interface le croit toujours au repos — et n'affiche jamais son\n * travail en cours.\n */\n const companionId = conv?.companionConversationId;\n // Le cycle fait foi ; le registre de sessions reste en second rideau\n // pour une relecture lancée avant que ce serveur ne tienne l'état.\n const running = isCompanionThinking(input.id) || (companionId\n ? activeSessionRegistry.get(companionId)?.status === 'running'\n : false);\n return { delivery: lastCompanionDelivery(input.id), running };\n }),\n\n /**\n * Le flux des conversations dont le compagnon relit, à cet instant.\n *\n * Remplace le sondage de `companionDelivery.running` : un état qui ne\n * change que deux fois par tour n'a pas à être redemandé toutes les\n * 1,5 s — et une relecture plus courte que cette période passait\n * inaperçue. La liste entière est renvoyée à chaque fois, comme\n * `kanbanQueue.onChanged` : elle tient en quelques identifiants, et un\n * client qui se branche en cours de route reçoit l'état complet plutôt\n * que la prochaine transition.\n */\n onCompanionThinking: protectedProcedure.subscription(async function* () {\n /*\n * Le même flux porte deux faits : qui relit, et pourquoi les autres n'ont\n * pas relu. Ils changent aux mêmes instants et s'affichent au même\n * endroit — les séparer aurait donné deux abonnements dont l'un peut\n * arriver après l'autre, donc un bandeau qui annonce une raison alors que\n * la relecture a repris.\n */\n const snapshot = () => ({ thinking: companionThinkingIds(), skipped: companionSkips() });\n const queue: Array<ReturnType<typeof snapshot>> = [snapshot()];\n let resolve: (() => void) | null = null;\n\n const handler = () => {\n if (queue.length >= 50) queue.shift();\n queue.push(snapshot());\n if (resolve) { resolve(); resolve = null; }\n };\n\n const unsubscribe = onCompanionThinkingChanged(handler);\n try {\n while (true) {\n if (queue.length > 0) yield queue.shift()!;\n else await new Promise<void>((r) => { resolve = r; });\n }\n } finally {\n unsubscribe();\n }\n }),\n\n /**\n * Envoyer un message de test par le pont du compagnon.\n *\n * Le compagnon ne parle que pour une trouvaille localisée : impossible, donc,\n * de vérifier que le pont fonctionne en le lui demandant — il refusera, et\n * on ne saura pas si c'est le jugement ou la plomberie qui a bloqué. Cette\n * route emprunte exactement le chemin de livraison, sans le jugement, et\n * rend PAR OÙ le message est passé.\n */\n testCompanionBridge: protectedProcedure\n .input(z.object({ id: Id128Schema }))\n .mutation(async ({ input }) => {\n const conv = await conversationStore.get(input.id);\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n const route = await deliverToMainConversation(\n conv,\n `${COMPANION_SIGNATURE} Test du pont : si tu lis ceci, le compagnon peut t'écrire. Réponds « pont ok » en une ligne, sans rien faire d'autre.`,\n );\n return { route };\n }),\n\n /**\n * Relancer le compagnon à la main.\n *\n * Une relecture ne part que sur une fin de tour annoncée, et plusieurs\n * chemins n'en annoncent aucune (tour interrompu, session hors registre,\n * transcript non reconnu) : sans ce geste, un compagnon allumé pouvait\n * rester muet sans qu'on puisse rien y faire.\n *\n * On rend la main tout de suite : une relecture dure un tour de modèle\n * entier, bien plus qu'une requête ne doit rester ouverte. L'interface suit\n * l'avancement par l'état poussé « il relit », déjà en place.\n */\n runCompanionNow: protectedProcedure\n .input(z.object({ id: Id128Schema }))\n .mutation(async ({ input }) => {\n const conv = await conversationStore.get(input.id);\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n if (conv.isCompanion) {\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'A companion cannot review itself' });\n }\n if (!conv.companionEnabled) {\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'Companion is disabled on this conversation' });\n }\n // Deux relectures concurrentes se disputeraient la même session CLI : la\n // seconde échouerait de toute façon, autant le dire ici.\n if (isCompanionThinking(input.id)) {\n throw new TRPCError({ code: 'CONFLICT', message: 'Companion is already reading' });\n }\n void runCompanionNow(input.id).catch((err) => {\n console.error('[companion]', (err as Error).message);\n });\n return { started: true };\n }),\n\n delete: protectedProcedure\n .input(z.object({ id: Id128Schema }))\n .mutation(async ({ input }) => {\n const existing = await conversationStore.get(input.id);\n if (!existing) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n\n // L'état en mémoire attaché à cette conversation part avec elle : sinon\n // le serveur garderait indéfiniment l'identifiant de tours et de\n // relances d'une conversation qui n'existe plus.\n resetRelaunches(input.id);\n forgetAnnouncedTurns(input.id);\n forgetCodexTurns(input.id);\n forgetReviewProgress(input.id);\n forgetDelivery(input.id);\n forgetCompanionSkip(input.id);\n\n // Kill any interactive PTY still running for this conversation before we\n // drop it. PTY sessions are spawned with sessionId === conversation id and\n // never exit on their own, so without this they'd leak a concurrency slot.\n // Fire-and-forget across every connected agent (we don't track which one\n // hosts the session on the conversation record).\n for (const agent of agentManager.listAgents()) {\n const agentId = agent.config.id;\n agentManager\n .agentQuery<{ spawnId: string; status: string } | null>(\n agentId,\n 'ai.findPtyBySession',\n { sessionId: input.id },\n )\n .then((pty) => {\n if (pty?.status === 'running') {\n return agentManager.agentMutation(agentId, 'ai.kill', { spawnId: pty.spawnId });\n }\n })\n .catch(() => { /* agent unreachable or no session — best-effort */ });\n }\n\n await conversationStore.delete(input.id);\n await scenarioRunStore.deleteRunDir(input.id);\n // Fire-and-forget: delete the whole conversation images directory\n imageStorageService.deleteByContextId(input.id).catch((err) =>\n console.error('[chat.delete] Image cleanup error:', err),\n );\n }),\n\n archive: protectedProcedure\n .input(z.object({ id: Id128Schema, archived: z.boolean() }))\n .mutation(async ({ input }) => {\n const conv = await conversationStore.update(input.id, { archived: input.archived });\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n return conv;\n }),\n\n pin: protectedProcedure\n .input(z.object({ id: Id128Schema, pinned: z.boolean() }))\n .mutation(async ({ input }) => {\n const conv = await conversationStore.setPinned(input.id, input.pinned);\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n return conv;\n }),\n\n // L'ordre ne vaut que pour les ids donnés : l'écran ne connaît que les\n // conversations du projet courant, et les épinglées des autres projets\n // n'ont pas à bouger parce qu'on a rangé celles-ci.\n reorderPinned: protectedProcedure\n .input(z.object({ ids: z.array(Id128Schema).max(500) }))\n .mutation(async ({ input }) => {\n await conversationStore.reorderPinned(input.ids);\n }),\n }),\n\n listModels: protectedProcedure\n .input(z.object({\n providerId: Id128Schema.optional(),\n // Le projet dit de quelle organisation lire les fournisseurs ; absent,\n // ce sont ceux du poste.\n projectId: Id128Schema.optional(),\n orgId: z.string().max(128).nullable().optional(),\n }).optional())\n .query(async ({ input }) => {\n const active = await getActiveProvider(\n input?.providerId,\n input?.orgId ?? await vaultOfProject(input?.projectId),\n );\n if (!active) return [];\n try {\n return await fetchProviderModels(active.provider, active.plaintextKey);\n } catch {\n return [];\n }\n }),\n\n checkCliAvailability: protectedProcedure\n .input(z.object({ agentId: Id128Schema.optional() }).optional())\n .query(async ({ input }) => {\n if (input?.agentId) {\n try {\n const res = await agentManager.agentQuery<{ claudeAvailable: boolean; codexAvailable: boolean }>(\n input.agentId,\n 'health.checkCli',\n );\n return { available: res.claudeAvailable, codexAvailable: res.codexAvailable };\n } catch {\n return { available: false, codexAvailable: false };\n }\n }\n const [claudeOk, codexOk] = await Promise.all([\n isCliAvailable('claude'),\n isCliAvailable('codex'),\n ]);\n return { available: claudeOk, codexAvailable: codexOk };\n }),\n\n enhanceScenarioPrompt: protectedProcedure\n .input(z.object({\n text: z.string().max(10_000),\n providerId: Id128Schema.optional(),\n model: z.string().max(200).optional(),\n projectId: Id128Schema.optional(),\n orgId: z.string().max(128).nullable().optional(),\n }))\n .mutation(async ({ input }) => {\n const active = await getActiveProvider(\n input.providerId,\n input.orgId ?? await vaultOfProject(input.projectId),\n );\n if (!active) throw new TRPCError({ code: 'BAD_REQUEST', message: 'No AI provider configured' });\n\n const systemPrompt = `You are an expert at writing clear, concise user request briefs for AI-powered workflows.\nWhen given a rough user request, rewrite it to be:\n- Clear and well-structured\n- Specific about the expected outcome\n- Written in the same language as the input\n- Concise but complete (no fluff)\nReturn ONLY the improved text, no explanation, no preamble.`;\n\n const { provider, plaintextKey } = active;\n const model = input.model || provider.model || DEFAULT_MODELS[provider.provider] || 'claude-opus-4-6';\n\n if (provider.provider === 'anthropic') {\n const client = new Anthropic({ apiKey: plaintextKey });\n const res = await client.messages.create({\n model,\n max_tokens: 2048,\n system: systemPrompt,\n messages: [{ role: 'user', content: input.text }],\n });\n const block = res.content[0];\n return { enhanced: block?.type === 'text' ? block.text : input.text };\n }\n\n if (provider.provider in OPENAI_COMPAT_BASE_URLS) {\n const baseURL = OPENAI_COMPAT_BASE_URLS[provider.provider]!;\n const client = new OpenAI({ apiKey: plaintextKey || 'ollama', baseURL });\n const res = await client.chat.completions.create({\n model,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: input.text },\n ],\n });\n return { enhanced: res.choices[0]?.message?.content ?? input.text };\n }\n\n return { enhanced: input.text };\n }),\n\n listClaudeCodeModels: protectedProcedure\n .query(() => ['opus', 'sonnet', 'haiku']),\n\n getClaudeUsage: protectedProcedure\n .query(async () => {\n try {\n return await getClaudeUsageEntries();\n } catch {\n return null;\n }\n }),\n\n getCodexUsage: protectedProcedure\n .query(async () => {\n try {\n return await getCodexUsageEntries();\n } catch {\n return null;\n }\n }),\n\n getStreamingStatus: protectedProcedure\n .query(() => activeSessionRegistry.listActive()),\n\n stopSession: protectedProcedure\n .input(z.object({ conversationId: ConversationIdSchema }))\n .mutation(({ input }) => {\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: stopSession')\n const killed = activeSessionRegistry.kill(input.conversationId);\n if (!killed) throw new TRPCError({ code: 'NOT_FOUND', message: 'No active session for this conversation' });\n return { ok: true };\n }),\n\n streamViaClaudeCode: protectedProcedure\n .input(z.object({\n conversationId: ConversationIdSchema,\n prompt: z.string().max(100_000),\n contextNote: z.string().max(200_000).optional(),\n model: ShortString200Schema.optional(),\n effort: z.enum(['low', 'medium', 'high']).optional(),\n reconnect: z.boolean().optional(),\n agentId: Id128Schema.optional(),\n environmentId: z.string().max(128).optional(),\n imageIds: z.array(z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),\n imageParts: ImagePartsSchema,\n }))\n .subscription(async function* ({ input, ctx, signal }) {\n // ── Reconnect path: attach to an existing session ───────────────────\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: Reconnect')\n const existingSession = activeSessionRegistry.get(input.conversationId);\n if (input.reconnect && existingSession) {\n yield* drainSession(existingSession, signal, true);\n return;\n }\n\n // ── New message path ────────────────────────────────────────────────\n if (!input.prompt) throw new TRPCError({ code: 'BAD_REQUEST', message: 'Prompt is required for new messages' });\n\n const conv = await conversationStore.get(input.conversationId);\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n\n console.log(`[kanban-debug] streamViaClaudeCode start — convId=${conv.id} kanbanBoardId=${conv.kanbanBoardId ?? 'null'}`);\n\n const isResume = conv.messages.length > 0;\n\n const userParts = input.imageParts && input.imageParts.length > 0\n ? [...(input.prompt.trim() ? [{ type: 'text' as const, text: input.prompt }] : []), ...input.imageParts]\n : undefined;\n conv.messages.push({ role: 'user', content: input.prompt, parts: userParts, contextNote: input.contextNote });\n conv.messages.push({ role: 'assistant', content: '' });\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: update')\n conversationStore.update(conv.id, { messages: conv.messages }, { silent: true }).catch(() => { });\n\n const cliPrompt = input.contextNote ? `${input.contextNote}\\n\\n${input.prompt}` : input.prompt;\n\n const jwt = ctx.authorization?.startsWith('Bearer ') ? ctx.authorization.slice(7) : undefined;\n // Le même constructeur que partout ailleurs. Ce chemin listait les\n // services du poste entier au lieu de ceux du projet de la conversation,\n // et n'annonçait pas le compagnon : deux corrections faites une fois, ici\n // perdues parce que le prompt y était réassemblé à la main.\n const systemContext = await buildCliSystemContext(conv, jwt);\n\n const mcpRef = await conversationStore.projectRefOf(conv);\n const mcpConfig = runeyaMcpConfig({\n RUNEYA_BASE_URL: `http://localhost:${env.PORT}`,\n ...(jwt ? { RUNEYA_API_TOKEN: jwt } : {}),\n ...(mcpRef?.id ? { RUNEYA_PROJECT_ID: mcpRef.id } : {}),\n ...(mcpRef?.orgId ? { RUNEYA_ORG_ID: mcpRef.orgId } : {}),\n });\n\n // Claude Code has filesystem access — pass image paths directly in the prompt\n const imagePaths: string[] = [];\n if (input.imageIds && input.imageIds.length > 0) {\n for (const id of input.imageIds) {\n // Scopé à la conversation : sinon un imageId d'une autre conversation\n // se glisse dans le prompt et fuit par ce chemin.\n const result = await imageStorageService.getImagePath(id, input.conversationId);\n if (result) imagePaths.push(result.filePath);\n }\n }\n const promptWithImages = imagePaths.length > 0\n ? `${cliPrompt}\\n\\n${imagePaths.map(p => `Image: ${p}`).join('\\n')}`\n : cliPrompt;\n\n const convId = input.conversationId;\n const ac = new AbortController();\n let session: ActiveSession;\n try {\n session = activeSessionRegistry.registerProviderApiSession(convId, ac);\n } catch {\n throw new TRPCError({ code: 'CONFLICT', message: 'A session is already running for this conversation. Stop it first.' });\n }\n let capturedFinalResult = '';\n\n const resolvedAgentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (resolvedAgentId) {\n session.agentId = resolvedAgentId;\n }\n\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: claude run...')\n const injectedEnv = await resolveInjectedEnv(input.environmentId);\n new ClaudeCodeRunner().run({\n agentId: resolvedAgentId ?? undefined,\n cwd: await conversationCwd(conv),\n sessionId: conv.id,\n prompt: promptWithImages,\n isResume,\n systemContext,\n apiBaseUrl: `http://localhost:${env.PORT}`,\n apiToken: jwt ?? '',\n ...(mcpRef?.id ? { projectId: mcpRef.id } : {}),\n ...(mcpRef?.orgId ? { orgId: mcpRef.orgId } : {}),\n model: input.model ?? 'claude-opus-4-6',\n effort: input.effort ?? 'normal',\n signal: ac.signal,\n skipTokenInstructions: true,\n mcpConfig,\n injectedEnv,\n onDelta: (text) => {\n activeSessionRegistry.pushEvent(convId, { type: 'delta', text });\n },\n onStatus: (text) => activeSessionRegistry.pushEvent(convId, { type: 'status', text }),\n onThinking: (text) => activeSessionRegistry.pushEvent(convId, { type: 'thinking', text }),\n onToolCall: (id, name, input_) => activeSessionRegistry.pushEvent(convId, { type: 'tool_call', id, name, input: input_ }),\n onToolResult: (id, result, isError) => activeSessionRegistry.pushEvent(convId, { type: 'tool_result', id, name: '', result, isError }),\n onContextUsage: (inputTokens: number, contextWindow: number) => activeSessionRegistry.pushEvent(convId, { type: 'context_usage', inputTokens, contextWindow }),\n onResult: (result: string) => { if (result) capturedFinalResult = result; },\n }).then(() => {\n if (activeSessionRegistry.get(convId) !== session) return;\n\n // Reconcile: push any content captured in the result event but missed by text_delta\n if (capturedFinalResult && capturedFinalResult.length > session.fullContent.length) {\n const missing = capturedFinalResult.slice(session.fullContent.length);\n activeSessionRegistry.pushEvent(convId, { type: 'delta', text: missing });\n }\n\n activeSessionRegistry.pushEvent(convId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(convId);\n }).catch((err: Error) => {\n if (activeSessionRegistry.get(convId) !== session) return;\n activeSessionRegistry.pushEvent(convId, { type: 'error', message: `Claude Code error: ${err.message}` });\n activeSessionRegistry.pushEvent(convId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(convId);\n });\n\n if(process.env.RUNEYA_DEBUG) console.log('[chat]: drainSession...')\n yield* drainSession(session, signal, false);\n }),\n\n streamViaCodex: protectedProcedure\n .input(z.object({\n conversationId: ConversationIdSchema,\n prompt: z.string().max(100_000),\n contextNote: z.string().max(200_000).optional(),\n model: ShortString200Schema.optional(),\n reconnect: z.boolean().optional(),\n agentId: Id128Schema.optional(),\n environmentId: z.string().max(128).optional(),\n imageIds: z.array(z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),\n imageParts: ImagePartsSchema,\n }))\n .subscription(async function* ({ input, ctx, signal }) {\n // ── Reconnect path ───────────────────────────────────────────────────\n const existingSession = activeSessionRegistry.get(input.conversationId);\n if (input.reconnect && existingSession) {\n yield* drainSession(existingSession, signal, true);\n return;\n }\n\n if (!input.prompt) throw new TRPCError({ code: 'BAD_REQUEST', message: 'Prompt is required' });\n\n const conv = await conversationStore.get(input.conversationId);\n if (!conv) throw new TRPCError({ code: 'NOT_FOUND', message: 'Conversation not found' });\n\n const isResume = !!conv.codexThreadId;\n\n const codexUserParts = input.imageParts && input.imageParts.length > 0\n ? [...(input.prompt.trim() ? [{ type: 'text' as const, text: input.prompt }] : []), ...input.imageParts]\n : undefined;\n conv.messages.push({ role: 'user', content: input.prompt, parts: codexUserParts, contextNote: input.contextNote });\n conv.messages.push({ role: 'assistant', content: '' });\n conversationStore.update(conv.id, { messages: conv.messages }, { silent: true }).catch(() => {});\n\n const cliPrompt = input.contextNote ? `${input.contextNote}\\n\\n${input.prompt}` : input.prompt;\n\n const jwt = ctx.authorization?.startsWith('Bearer ') ? ctx.authorization.slice(7) : undefined;\n // Le même constructeur que partout ailleurs. Ce chemin listait les\n // services du poste entier au lieu de ceux du projet de la conversation,\n // et n'annonçait pas le compagnon : deux corrections faites une fois, ici\n // perdues parce que le prompt y était réassemblé à la main.\n const systemContext = await buildCliSystemContext(conv, jwt);\n\n const resolvedImages = input.imageIds && input.imageIds.length > 0\n ? await resolveImagesToBase64(input.imageIds.map(id => ({ imageId: id, mimeType: '' })), input.conversationId)\n : [];\n\n const convId = input.conversationId;\n const ac = new AbortController();\n let session: ActiveSession;\n try {\n session = activeSessionRegistry.registerProviderApiSession(convId, ac);\n } catch {\n throw new TRPCError({ code: 'CONFLICT', message: 'A session is already running for this conversation. Stop it first.' });\n }\n\n const resolvedAgentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (resolvedAgentId) {\n session.agentId = resolvedAgentId;\n }\n\n const injectedEnv = await resolveInjectedEnv(input.environmentId);\n new CodexRunner().run({\n agentId: resolvedAgentId ?? undefined,\n cwd: await conversationCwd(conv),\n sessionId: conv.codexThreadId ?? '',\n prompt: cliPrompt,\n isResume,\n systemContext,\n apiBaseUrl: `http://localhost:${env.PORT}`,\n apiToken: jwt ?? '',\n model: input.model ?? '',\n effort: 'normal',\n signal: ac.signal,\n skipTokenInstructions: true,\n images: resolvedImages,\n injectedEnv,\n onThreadId: (threadId) => conversationStore.update(conv.id, { codexThreadId: threadId }).catch(() => {}),\n onDelta: (text) => activeSessionRegistry.pushEvent(convId, { type: 'delta', text }),\n onStatus: (text) => activeSessionRegistry.pushEvent(convId, { type: 'status', text }),\n onThinking: (text) => activeSessionRegistry.pushEvent(convId, { type: 'thinking', text }),\n onToolCall: (id, name, input_) => activeSessionRegistry.pushEvent(convId, { type: 'tool_call', id, name, input: input_ }),\n onToolResult: (id, result, isError) => activeSessionRegistry.pushEvent(convId, { type: 'tool_result', id, name: '', result, isError }),\n }).then(() => {\n if (activeSessionRegistry.get(convId) !== session) return;\n activeSessionRegistry.pushEvent(convId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(convId);\n }).catch((err: Error) => {\n if (activeSessionRegistry.get(convId) !== session) return;\n activeSessionRegistry.pushEvent(convId, { type: 'error', message: `Codex error: ${err.message}` });\n activeSessionRegistry.pushEvent(convId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(convId);\n });\n\n yield* drainSession(session, signal, false);\n }),\n\n send: protectedProcedure\n .input(z.object({\n conversationId: ConversationIdSchema,\n messages: z.array(MessageSchema).min(0).max(100),\n model: ShortString200Schema.optional(),\n contextNote: z.string().max(200_000).optional(),\n reconnect: z.boolean().optional(),\n imageIds: z.array(z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)).max(5).optional(),\n imageParts: ImagePartsSchema,\n }))\n .subscription(async function* ({ input, ctx, signal }) {\n // ── Reconnect path: attach to an existing session ───────────────────\n const existingSession = activeSessionRegistry.get(input.conversationId);\n if (input.reconnect && existingSession) {\n yield* drainSession(existingSession, signal, true);\n return;\n }\n\n // ── New message path ────────────────────────────────────────────────\n if (input.messages.length === 0) {\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'Messages are required for new messages' });\n }\n\n // Persist user message immediately — don't wait for the frontend\n const conv = await conversationStore.get(input.conversationId);\n\n // La conversation nomme son projet, donc son organisation : les\n // fournisseurs lus sont ceux de l'équipe à qui appartient ce travail.\n const convRef = conv ? await conversationStore.projectRefOf(conv) : null;\n const active = await getActiveProvider(conv?.providerId, convRef?.orgId ?? undefined);\n\n if (!active) {\n throw new TRPCError({\n code: 'PRECONDITION_FAILED',\n message: 'No AI provider configured for this conversation. Please select one when starting a new chat.',\n });\n }\n if (conv) {\n const lastUserMsg = input.messages[input.messages.length - 1];\n if (lastUserMsg) {\n const sendUserParts = input.imageParts && input.imageParts.length > 0\n ? [...(lastUserMsg.content.trim() ? [{ type: 'text' as const, text: lastUserMsg.content }] : []), ...input.imageParts]\n : undefined;\n conv.messages.push({ role: lastUserMsg.role, content: lastUserMsg.content, parts: sendUserParts, contextNote: input.contextNote });\n conv.messages.push({ role: 'assistant', content: '' });\n await conversationStore.update(conv.id, { messages: conv.messages }, { silent: true });\n }\n }\n\n const { provider, plaintextKey } = active;\n const model = input.model ?? provider.model ?? DEFAULT_MODELS[provider.provider] ?? 'gpt-4o';\n const apiKey = (ctx as AuthContext).apiKey ?? null;\n const authToken = ctx.authorization?.replace(/^Bearer\\s+/i, '') ?? '';\n let system = await buildSystemPrompt(apiKey);\n system += '\\n\\n' + TOOL_SAFETY_INSTRUCTION;\n\n system += await kanbanContextFor(conv);\n\n // Resolve images if any imageIds provided\n const resolvedImages = input.imageIds && input.imageIds.length > 0\n ? await resolveImagesToBase64(input.imageIds.map(id => ({ imageId: id, mimeType: '' })), input.conversationId)\n : [];\n\n // Register in the session registry — stream survives WS disconnection\n let session: ActiveSession;\n try {\n session = activeSessionRegistry.registerDirectProviderApiSession(input.conversationId);\n } catch {\n throw new TRPCError({ code: 'CONFLICT', message: 'A session is already running for this conversation. Stop it first.' });\n }\n const apiSignal = session.abortController!.signal;\n\n // Fire-and-forget: run the API stream, pushing events into the registry\n const runStream = async () => {\n try {\n let gen: AsyncGenerator<ChatEvent>;\n\n if (provider.provider === 'anthropic') {\n const client = new Anthropic({ apiKey: plaintextKey });\n const msgs: Anthropic.MessageParam[] = input.messages.map((m, i) => {\n const isLast = i === input.messages.length - 1;\n if (isLast && resolvedImages.length > 0) {\n const content: Anthropic.ContentBlockParam[] = resolvedImages.map(img => ({\n type: 'image' as const,\n source: { type: 'base64' as const, media_type: img.mimeType as Anthropic.Base64ImageSource['media_type'], data: img.base64 },\n }));\n content.push({ type: 'text', text: m.content });\n return { role: m.role, content };\n }\n return { role: m.role, content: m.content };\n });\n gen = runAnthropic(client, model, system, msgs, apiKey, authToken, apiSignal);\n } else if (provider.provider in OPENAI_COMPAT_BASE_URLS) {\n const baseURL = OPENAI_COMPAT_BASE_URLS[provider.provider]!;\n const client = new OpenAI({ apiKey: plaintextKey || 'ollama', baseURL });\n const msgs: OpenAI.Chat.ChatCompletionMessageParam[] = [\n { role: 'system', content: system },\n ...input.messages.map((m, i) => {\n const isLast = i === input.messages.length - 1;\n if (isLast && resolvedImages.length > 0) {\n const content: OpenAI.Chat.ChatCompletionContentPart[] = resolvedImages.map(img => ({\n type: 'image_url' as const,\n image_url: { url: `data:${img.mimeType};base64,${img.base64}` },\n }));\n content.push({ type: 'text', text: m.content });\n return { role: m.role, content } as OpenAI.Chat.ChatCompletionMessageParam;\n }\n return { role: m.role, content: m.content } as OpenAI.Chat.ChatCompletionMessageParam;\n }),\n ];\n gen = runOpenAI(client, model, msgs, apiKey, authToken, apiSignal);\n } else {\n activeSessionRegistry.pushEvent(input.conversationId, { type: 'error', message: `Provider \"${provider.provider}\" is not supported.` });\n activeSessionRegistry.pushEvent(input.conversationId, { type: 'done', text: '' });\n activeSessionRegistry.markDone(input.conversationId);\n return;\n }\n\n for await (const event of gen) {\n if (event.type === 'delta') {\n activeSessionRegistry.pushEvent(input.conversationId, { type: 'delta', text: event.text });\n } else if (event.type === 'tool_call') {\n activeSessionRegistry.pushEvent(input.conversationId, { type: 'tool_call', id: event.id, name: event.name, input: event.input });\n } else if (event.type === 'tool_result') {\n activeSessionRegistry.pushEvent(input.conversationId, { type: 'tool_result', id: event.id, name: event.name, result: event.result, isError: event.isError });\n } else if (event.type === 'done') {\n activeSessionRegistry.pushEvent(input.conversationId, { type: 'done', text: '' });\n }\n }\n } catch (err) {\n if (!apiSignal.aborted) {\n activeSessionRegistry.pushEvent(input.conversationId, { type: 'error', message: (err as Error).message });\n activeSessionRegistry.pushEvent(input.conversationId, { type: 'done', text: '' });\n }\n } finally {\n activeSessionRegistry.markDone(input.conversationId);\n }\n };\n\n // Start the stream in the background — it won't be cancelled by tRPC signal\n void runStream();\n\n // Drain from registry (WS disconnection stops yielding but stream continues)\n yield* drainSession(session, signal, false);\n }),\n\n // ── PTY passthrough (interactive Claude Code session) ────────────────────\n pty: router({\n /** Start a PTY session running `claude --resume <sessionId>` (or fresh). */\n start: protectedProcedure\n .input(z.object({\n agentId: z.string().max(128).optional(),\n sessionId: z.string().max(128).optional(),\n runner: z.enum(['claude-code', 'codex']).default('claude-code'),\n resume: z.boolean().default(false),\n model: z.string().max(128).optional(),\n cwd: z.string().max(4096).optional(),\n /** Anchors the session when no explicit cwd is given. */\n serviceId: z.string().max(128).optional(),\n projectId: Id128Schema.optional(),\n orgId: z.string().max(128).nullable().optional(),\n cols: z.number().int().min(1).max(1000).default(120),\n rows: z.number().int().min(1).max(1000).default(30),\n enableMcp: z.boolean().default(false),\n apiBaseUrl: z.string().max(512).optional(),\n environmentId: z.string().max(128).optional(),\n attachedContexts: z.array(z.object({\n label: z.string().max(200),\n text: z.string().max(1_000_000),\n })).max(20).optional(),\n }))\n .mutation(async ({ input, ctx }) => {\n // Where the terminal's startup time actually goes. Kept behind the debug\n // flag but written once, here: the browser only sees one long mutation,\n // so without these laps \"the terminal takes forever\" cannot be pinned on\n // the agent lookup, the system prompt, or the spawn itself.\n const t0 = Date.now();\n const lap = (label: string) => {\n if (process.env.RUNEYA_DEBUG) console.log('[chat.pty.start] %s +%dms', label, Date.now() - t0);\n };\n const agentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (!agentId) throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'No agent available' });\n\n // If a PTY is already alive for this sessionId, re-attach to it instead\n // of spawning a fresh `claude` (so the process survives navigation).\n if (input.sessionId) {\n try {\n const existing = await agentManager.agentQuery<{ spawnId: string; status: string } | null>(\n agentId,\n 'ai.findPtyBySession',\n { sessionId: input.sessionId },\n );\n if (existing && existing.status === 'running') {\n // Resize to current viewport so the reattached terminal looks right.\n await agentManager.agentMutation(agentId, 'ai.resizePty', {\n spawnId: existing.spawnId,\n cols: input.cols,\n rows: input.rows,\n }).catch(() => {});\n lap('reattached to existing pty');\n noteSpawnConversation(existing.spawnId, input.sessionId);\n return { spawnId: existing.spawnId, agentId, reattached: true };\n }\n } catch {\n // best-effort — fall back to spawning a fresh PTY\n }\n }\n\n lap('findPtyBySession (no reattach)');\n\n // La conversation que ce terminal poursuit, lue une seule fois : elle\n // sert au fil Codex à reprendre, au prompt système ET à l'ancrage des\n // tools MCP.\n const ptyConv = input.sessionId\n ? await conversationStore.get(input.sessionId).catch(() => null)\n : null;\n\n const isCodex = input.runner === 'codex';\n const cli = isCodex ? 'codex' : 'claude';\n const args: string[] = [];\n if (isCodex) {\n // Codex resume is a positional subcommand: `codex resume <id>`. Only\n // attempt it when the thread actually exists, otherwise the CLI errors\n // out instead of starting fresh. C'est `codexThreadId` qui porte le\n // fil — Codex le nomme lui-même, l'id de conversation ne désigne rien\n // chez lui : le chercher, lui, revenait à ne jamais reprendre.\n const resumeId = await resolveCodexResumeId(input.sessionId, ptyConv?.codexThreadId);\n if (resumeId) {\n args.push('resume', resumeId);\n }\n if (input.model) args.push('--model', input.model);\n args.push('--dangerously-bypass-approvals-and-sandbox');\n } else {\n if (input.sessionId) {\n // Auto-detect whether the session already exists on disk: Claude refuses\n // `--session-id <id>` when the ID is already known. Override the caller's\n // hint so a returning conversation always resumes cleanly.\n const sessionExists = await claudeSessionExists(input.sessionId);\n if (input.resume || sessionExists) {\n args.push('--resume', input.sessionId);\n } else {\n args.push('--session-id', input.sessionId);\n }\n }\n if (input.model) args.push('--model', input.model);\n args.push('--dangerously-skip-permissions');\n }\n\n // Même priorité que le `cwd` plus bas : le projet de la conversation\n // prime sur celui de l'onglet. Ouvrir le terminal d'une carte kanban\n // depuis ailleurs ne doit pas ancrer les tools sur le projet affiché.\n const ptyRef = (ptyConv ? await conversationStore.projectRefOf(ptyConv) : null)\n ?? (input.projectId ? { id: input.projectId, orgId: input.orgId ?? null } : null);\n\n // Build the system prompt (services llms.txt + kanban) and inject it via\n // `--append-system-prompt` so Claude treats it as a real system prompt\n // instead of a user message. Codex has no equivalent\n // append-system-prompt flag, so this is skipped for the codex runner\n // (only the attached-context bracketed paste below applies to it).\n const jwt = ctx.authorization?.startsWith('Bearer ') ? ctx.authorization.slice(7) : undefined;\n if (!isCodex && ptyConv) {\n try {\n const systemContext = await buildCliSystemContext(ptyConv, jwt);\n if (systemContext.trim().length > 0) {\n args.push('--append-system-prompt', systemContext);\n }\n } catch (err) {\n console.warn('[chat.pty.start] buildCliSystemContext failed:', (err as Error).message);\n }\n lap('buildCliSystemContext');\n }\n\n // Inject flagged variables from the active environment into the AI\n // process env (opt-in per variable via `injectIntoAi`).\n const injectedEnv = await resolveInjectedEnv(input.environmentId);\n lap('resolveInjectedEnv');\n\n const res = await agentManager.agentMutation<{ spawnId: string }>(\n agentId,\n 'ai.spawnPty',\n {\n cli,\n args,\n env: injectedEnv,\n // An explicit cwd wins; then le projet de la conversation, s'il y en\n // a une : c'est elle que ce terminal poursuit, et son projet n'est\n // pas forcément celui ouvert dans l'onglet — ouvrir le terminal\n // d'une carte kanban depuis ailleurs lançait le CLI dans le\n // répertoire de lancement de Runeya. Sinon le service, puis le\n // projet actif — jamais le répertoire du serveur.\n cwd: input.cwd\n ?? (input.sessionId ? await resolveConversationCwd(input.sessionId) : undefined)\n ?? await resolveAgentCwd({\n serviceId: input.serviceId, projectId: input.projectId, orgId: input.orgId,\n }),\n cols: input.cols,\n rows: input.rows,\n sessionId: input.sessionId,\n enableMcp: input.enableMcp,\n mcpApiBaseUrl: input.apiBaseUrl ?? `http://localhost:${env.PORT}`,\n mcpApiToken: jwt,\n ...(ptyRef?.id ? { mcpProjectId: ptyRef.id } : {}),\n ...(ptyRef?.orgId ? { mcpOrgId: ptyRef.orgId } : {}),\n },\n );\n\n lap('agent ai.spawnPty');\n const contexts = input.attachedContexts ?? [];\n if (contexts.length > 0) {\n const body = contexts\n .map((c) => `### ${c.label}\\n${c.text}`)\n .join('\\n\\n');\n // Bracketed paste: Claude's TUI collapses this to \"[Pasted text #N +X lines]\"\n // but keeps the full content in the message it sends.\n const injection = `\\x1b[200~---------\\nContext:\\n\\x1b[201~\\x1b[200~${body}\\x1b[201~\\x1b[200~\\n---------\\n\\x1b[201~`;\n\n const doInject = (): void => {\n agentManager\n .agentMutation(agentId, 'ai.writePty', { spawnId: res.spawnId, data: injection })\n .catch((err) => {\n console.warn('[chat.pty.start] deferred context writePty failed:', (err as Error).message);\n });\n };\n\n // There is no explicit \"ready for input\" signal from Claude Code or\n // Codex, so we combine a floor with output-quiescence detection:\n // - MIN_MS: never inject before this — the known-good fixed delay that\n // guarantees the TUI has reached its input prompt (a plain quiescence\n // check alone can fire during an early boot pause and drop the paste,\n // which is what broke larger contexts like logs).\n // - then wait for a quiet gap (no new bytes) so a slow boot pushes the\n // injection later rather than earlier.\n // - MAX_MS: hard safety net if output never settles (blinking cursor).\n const startedAt = Date.now();\n const MIN_MS = input.runner === 'codex' ? 500 : 1200;\n const QUIET_MS = 400;\n const MAX_MS = 8000;\n let injected = false;\n let quietTimer: ReturnType<typeof setTimeout> | null = null;\n let maxTimer: ReturnType<typeof setTimeout> | null = null;\n let unsub: (() => void) | null = null;\n const cleanup = (): void => {\n if (quietTimer) clearTimeout(quietTimer);\n if (maxTimer) clearTimeout(maxTimer);\n unsub?.();\n };\n const inject = (): void => {\n if (injected) return;\n injected = true;\n cleanup();\n doInject();\n };\n const scheduleQuiet = (): void => {\n if (quietTimer) clearTimeout(quietTimer);\n const wait = Math.max(QUIET_MS, MIN_MS - (Date.now() - startedAt));\n quietTimer = setTimeout(inject, wait);\n };\n maxTimer = setTimeout(inject, MAX_MS);\n unsub = agentManager.agentSubscribe(\n agentId,\n 'ai.stream',\n { spawnId: res.spawnId, fromOffset: 0 },\n (data: unknown) => {\n const chunk = (data as { chunk?: { type?: string } }).chunk;\n if (chunk?.type === 'exit') { injected = true; cleanup(); return; } // process gone\n scheduleQuiet();\n },\n () => { /* stream ended before ready — nothing to inject into */ },\n );\n }\n\n // Idem au démarrage : sans ce lien, l'annonce de session Codex lue plus\n // tard dans le flux ne saurait pas quelle conversation rattacher.\n noteSpawnConversation(res.spawnId, input.sessionId);\n return { spawnId: res.spawnId, agentId, reattached: false };\n }),\n\n /** Subscribe to PTY output stream (raw text with ANSI). */\n stream: protectedProcedure\n .input(z.object({\n agentId: z.string().max(128).optional(),\n spawnId: z.string().max(128),\n fromOffset: z.number().int().min(0).default(0),\n }))\n .subscription(async function* ({ input, signal }) {\n const agentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (!agentId) throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'No agent available' });\n\n // Time to the terminal's first visible byte: the gap people describe as\n // \"the PTY does not even show claude\" lives between this subscribe and\n // the agent's first chunk.\n const tSub = Date.now();\n let firstChunk = true;\n const queue: Array<{ offset: number; chunk: { type: string; data: string; code?: number } }> = [];\n let resolve: (() => void) | null = null;\n let stopped = false;\n\n const unsub = agentManager.agentSubscribe(\n agentId,\n 'ai.stream',\n { spawnId: input.spawnId, fromOffset: input.fromOffset },\n (data: unknown) => {\n if (firstChunk) {\n firstChunk = false;\n if (process.env.RUNEYA_DEBUG) console.log('[chat.pty.stream] first chunk +%dms', Date.now() - tSub);\n }\n const item = data as { offset: number; chunk: { type: string; data: string; code?: number } };\n // Codex annonce l'identifiant de sa session dans son terminal : le\n // serveur relaie déjà ces octets, autant les lire. C'est ce qui\n // rattache un rollout à sa conversation (voir `codex-transcript-sync`).\n if (typeof item.chunk?.data === 'string') observePtyChunk(input.spawnId, item.chunk.data);\n queue.push(item);\n if (resolve) { resolve(); resolve = null; }\n },\n () => { stopped = true; if (resolve) { resolve(); resolve = null; } },\n );\n\n try {\n while (!signal?.aborted && !stopped) {\n while (queue.length > 0) {\n const item = queue.shift()!;\n yield item;\n if (item.chunk.type === 'exit') {\n // Le PTY est mort : plus rien à surveiller pour ce spawn.\n forgetSpawn(input.spawnId);\n stopped = true;\n break;\n }\n }\n if (stopped) break;\n await new Promise<void>((r) => { resolve = r; });\n }\n } finally {\n unsub();\n }\n }),\n\n /** Write input to a PTY session (keystrokes from xterm). */\n write: protectedProcedure\n .input(z.object({\n agentId: z.string().max(128).optional(),\n spawnId: z.string().max(128),\n data: z.string().max(1_000_000),\n }))\n .mutation(async ({ input }) => {\n const agentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (!agentId) throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'No agent available' });\n return agentManager.agentMutation<{ ok: boolean }>(agentId, 'ai.writePty', {\n spawnId: input.spawnId,\n data: input.data,\n });\n }),\n\n /** Resize a PTY session. */\n resize: protectedProcedure\n .input(z.object({\n agentId: z.string().max(128).optional(),\n spawnId: z.string().max(128),\n cols: z.number().int().min(1).max(1000),\n rows: z.number().int().min(1).max(1000),\n }))\n .mutation(async ({ input }) => {\n const agentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (!agentId) throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'No agent available' });\n return agentManager.agentMutation<{ ok: boolean }>(agentId, 'ai.resizePty', {\n spawnId: input.spawnId,\n cols: input.cols,\n rows: input.rows,\n });\n }),\n\n /**\n * Upload a pasted image to the agent. The agent saves it to a tmp file and\n * tries to populate its system clipboard so Claude's native paste detection\n * picks it up. Returns the tmp path and which clipboard method succeeded\n * (null if none — caller should inject `@<path>` instead of Ctrl+V).\n */\n pasteImage: protectedProcedure\n .input(z.object({\n agentId: z.string().max(128).optional(),\n sessionId: z.string().max(128).optional(),\n mimeType: z.string().max(64),\n base64: z.string().max(15_000_000),\n }))\n .mutation(async ({ input }) => {\n const agentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (!agentId) throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'No agent available' });\n const res = await agentManager.agentMutation<{\n tmpPath: string;\n clipboardMethod: 'xclip' | 'wl-copy' | 'pbcopy' | 'powershell' | null;\n clipboardError?: string;\n }>(agentId, 'ai.pasteImage', {\n sessionId: input.sessionId,\n mimeType: input.mimeType,\n base64: input.base64,\n });\n if (!res.clipboardMethod) {\n console.warn('[chat.pty.pasteImage] clipboard spoof failed, using @path fallback:', res.clipboardError);\n }\n return res;\n }),\n\n /**\n * Read the transcript Claude Code stores on disk for a given session.\n * Returns parsed JSONL entries (user / assistant messages, tool uses, etc.).\n */\n transcript: protectedProcedure\n .input(z.object({ sessionId: z.string().max(128) }))\n .query(async ({ input }) => {\n return readClaudeTranscript(input.sessionId);\n }),\n\n /**\n * Lightweight liveness probe: is a PTY running for this session, and\n * when was the transcript JSONL last touched? Used by the non-PTY view\n * to show a \"Claude is thinking…\" indicator while output streams.\n */\n activity: protectedProcedure\n .input(z.object({\n agentId: z.string().max(128).optional(),\n sessionId: z.string().max(128),\n }))\n .query(async ({ input }) => {\n const agentId = input.agentId ?? agentManager.getDefaultAgentId();\n let running = false;\n let lastChunkAt: number | null = null;\n if (agentId) {\n try {\n const existing = await agentManager.agentQuery<{ spawnId: string; status: string; lastChunkAt: number | null } | null>(\n agentId,\n 'ai.findPtyBySession',\n { sessionId: input.sessionId },\n );\n running = existing?.status === 'running';\n lastChunkAt = existing?.lastChunkAt ?? null;\n } catch {\n // agent unreachable — treat as not running\n }\n }\n // PTY data (every byte streamed by the agent) is the most accurate\n // signal; JSONL writes are a coarser fallback if the agent didn't\n // surface a chunk timestamp yet.\n const transcriptActivityAt = getLastTranscriptActivity(input.sessionId);\n const lastActivityAt = lastChunkAt && transcriptActivityAt\n ? Math.max(lastChunkAt, transcriptActivityAt)\n : (lastChunkAt ?? transcriptActivityAt);\n return { running, lastActivityAt };\n }),\n\n /**\n * Convert the on-disk Claude transcript into Runeya Message[] and persist\n * them on the conversation, so the non-PTY chat view stays in sync.\n * Uses the conversation id as session id (PTY sessions are spawned that way).\n */\n syncTranscript: protectedProcedure\n .input(z.object({ conversationId: ConversationIdSchema }))\n .mutation(async ({ input }) => {\n const conv = await conversationStore.get(input.conversationId);\n if (!conv) {\n throw new TRPCError({ code: 'NOT_FOUND', message: `Conversation ${input.conversationId} not found` });\n }\n const synced = await syncConversationFromClaudeTranscript(conv.id);\n const after = await conversationStore.get(conv.id);\n return { synced, messageCount: after?.messages.length ?? 0 };\n }),\n\n /**\n * Debug-only: spawn a PTY that emits random data at a chosen rate, tagged\n * with the given sessionId. Used to benchmark the PTY → tRPC → xterm.js\n * pipeline. Open the conversation in PTY mode to attach to it.\n */\n debugStress: protectedProcedure\n .input(z.object({\n sessionId: z.string().max(128),\n agentId: z.string().max(128).optional(),\n bytesPerSec: z.number().int().min(100).max(10_000_000).default(50_000),\n chunkSize: z.number().int().min(1).max(65_536).default(256),\n durationMs: z.number().int().min(0).max(3_600_000).default(0),\n cols: z.number().int().min(1).max(1000).default(120),\n rows: z.number().int().min(1).max(1000).default(30),\n }))\n .mutation(async ({ input }) => {\n const agentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (!agentId) throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'No agent available' });\n\n // Use a synthetic sessionId so we don't collide with the conversation's\n // real Claude PTY — that would make the overlay re-attach to Claude\n // instead of the stress generator.\n const stressSessionId = `${input.sessionId}-stress-${Date.now().toString(36)}`;\n\n // Resolve the script. Try a few likely locations because import.meta.url\n // depth depends on whether the server runs from src (dev) or dist (prod\n // bundle) and tsup can split into chunks.\n const { existsSync } = await import('node:fs');\n const candidates = [\n new URL('../../../../scripts/pty-stress-gen.mjs', import.meta.url),\n new URL('../../../../../scripts/pty-stress-gen.mjs', import.meta.url),\n new URL('../../../scripts/pty-stress-gen.mjs', import.meta.url),\n ];\n let scriptPath: string | null = null;\n for (const c of candidates) {\n const p = fileURLToPath(c);\n if (existsSync(p)) { scriptPath = p; break; }\n }\n if (!scriptPath) {\n throw new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: `pty-stress-gen.mjs not found near ${fileURLToPath(import.meta.url)}`,\n });\n }\n const res = await agentManager.agentMutation<{ spawnId: string }>(\n agentId,\n 'ai.spawnPty',\n {\n cli: 'node',\n args: [scriptPath],\n // Was `process.cwd()` — the SERVER's directory, handed to an agent\n // that may not even run on this machine.\n cwd: await resolveWorkspaceCwd({}),\n cols: input.cols,\n rows: input.rows,\n sessionId: stressSessionId,\n env: {\n BYTES_PER_SEC: String(input.bytesPerSec),\n CHUNK_SIZE: String(input.chunkSize),\n DURATION_MS: String(input.durationMs),\n },\n },\n );\n return { spawnId: res.spawnId, agentId, stressSessionId };\n }),\n\n /** Kill a PTY session. */\n kill: protectedProcedure\n .input(z.object({\n agentId: z.string().max(128).optional(),\n spawnId: z.string().max(128),\n }))\n .mutation(async ({ input }) => {\n const agentId = input.agentId ?? agentManager.getDefaultAgentId();\n if (!agentId) throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'No agent available' });\n return agentManager.agentMutation<{ ok: boolean }>(agentId, 'ai.kill', {\n spawnId: input.spawnId,\n });\n }),\n }),\n});\n","import { fileURLToPath } from 'node:url';\nimport { createRequire } from 'node:module';\nimport { existsSync } from 'node:fs';\n\n/**\n * Comment brancher le serveur MCP Runeya sur un CLI d'IA.\n *\n * Runeya n'installe rien dans la configuration de la machine : il passe sa\n * config MCP en ligne de commande, au moment où il lance lui-même le CLI. Le\n * `~/.claude.json` ou le `~/.codex/config.toml` de l'utilisateur reste intact,\n * et un CLI lancé à la main depuis un terminal ne voit pas ce serveur.\n *\n * Chaque CLI a sa syntaxe — d'où ce module, seule source de vérité partagée par\n * les deux points de lancement (l'agent, et le serveur en mode local).\n */\n\n/** Variables d'environnement que le serveur MCP attend pour joindre Runeya. */\nexport interface RuneyaMcpEnv {\n RUNEYA_BASE_URL?: string;\n RUNEYA_API_TOKEN?: string;\n /**\n * Le projet de la conversation qui lance ce CLI, s'il y en a un.\n *\n * Le serveur MCP en fait le défaut des tools qui acceptent un `projectId` :\n * sans lui, `list_services` rend tous les projets de la machine, quand\n * l'écran et le prompt n'en montrent qu'un.\n */\n RUNEYA_PROJECT_ID?: string;\n RUNEYA_ORG_ID?: string;\n}\n\n/**\n * Chemin du point d'entrée du serveur MCP, à lancer avec `node`.\n *\n * Ne PAS se fier à `import.meta.url` pour viser `./index.js` : ce module est\n * bundlé dans l'agent et dans le serveur (tsup y bundle les paquets @runeya\n * via `noExternal`), donc\n * `import.meta.url` y désigne le bundle hôte, pas ce paquet. On résout donc le\n * paquet, puis on sonde les emplacements de la version packagée, où les liens\n * symboliques de l'espace de travail n'existent plus.\n */\nexport function resolveMcpServerPath(): string {\n try {\n const req = createRequire(import.meta.url);\n return req.resolve('@runeya/packages-mcp-server');\n } catch { /* pas de node_modules résolvable : on sonde le disque */ }\n\n const candidates = [\n // Build packagé du CLI : dist/agent/index.js et dist/index.js côtoient\n // dist/mcp-server/, copié là par le post-build.\n new URL('../mcp-server/index.js', import.meta.url),\n new URL('./mcp-server/index.js', import.meta.url),\n // Monorepo, depuis un dist d'app.\n new URL('../../../packages/mcp-server/dist/index.js', import.meta.url),\n new URL('../../../../packages/mcp-server/dist/index.js', import.meta.url),\n ];\n for (const c of candidates) {\n const p = fileURLToPath(c);\n if (existsSync(p)) return p;\n }\n throw new Error('Could not locate @runeya/packages-mcp-server (built dist not found)');\n}\n\n/** Ne garde que les variables renseignées : une valeur vide n'aide personne. */\nfunction pickEnv(env: RuneyaMcpEnv): Record<string, string> {\n return {\n ...(env.RUNEYA_BASE_URL ? { RUNEYA_BASE_URL: env.RUNEYA_BASE_URL } : {}),\n ...(env.RUNEYA_API_TOKEN ? { RUNEYA_API_TOKEN: env.RUNEYA_API_TOKEN } : {}),\n ...(env.RUNEYA_PROJECT_ID ? { RUNEYA_PROJECT_ID: env.RUNEYA_PROJECT_ID } : {}),\n ...(env.RUNEYA_ORG_ID ? { RUNEYA_ORG_ID: env.RUNEYA_ORG_ID } : {}),\n };\n}\n\n/**\n * La config MCP de Runeya, au format `mcpServers` attendu par `--mcp-config`.\n *\n * Seul endroit du dépôt où ce JSON se fabrique. `chat.ts` en tenait deux copies\n * qui résolvaient le chemin avec `import.meta.url` — le piège que\n * `resolveMcpServerPath()` documente juste au-dessus : bundlées dans l'app\n * hôte, elles visaient un fichier qui n'existe pas, et le CLI démarrait sans\n * les tools Runeya, en silence.\n */\nexport function runeyaMcpConfig(env: RuneyaMcpEnv): string {\n return JSON.stringify({\n mcpServers: {\n runeya: {\n command: 'node',\n args: [resolveMcpServerPath()],\n env: pickEnv(env),\n },\n },\n });\n}\n\n/**\n * Arguments `--mcp-config` pour le CLI `claude`, qui attend du JSON.\n *\n * Volontairement non strict : le CLI démarre aussi les serveurs MCP de la\n * machine. Cela coûte quelques secondes par lancement, mais l'utilisateur garde\n * ses propres outils.\n */\nexport function claudeMcpArgs(env: RuneyaMcpEnv): string[] {\n return ['--mcp-config', runeyaMcpConfig(env)];\n}\n\n/**\n * Arguments `-c` pour le CLI `codex`, qui attend du TOML.\n *\n * Codex n'a pas d'équivalent de `--mcp-config` : il expose des surcharges de\n * configuration en TOML (`-c chemin.pointé=valeur`), à placer AVANT la\n * sous-commande. Une table inline décrit le serveur en une seule surcharge.\n */\nexport function codexMcpArgs(env: RuneyaMcpEnv): string[] {\n const fields = [\n `command=${tomlString('node')}`,\n `args=[${tomlString(resolveMcpServerPath())}]`,\n ];\n\n const entries = Object.entries(pickEnv(env));\n if (entries.length > 0) {\n const inline = entries.map(([k, v]) => `${k}=${tomlString(v)}`).join(',');\n fields.push(`env={${inline}}`);\n }\n\n return ['-c', `mcp_servers.runeya={${fields.join(',')}}`];\n}\n\n/**\n * Chaîne TOML de base : seuls la contre-oblique et le guillemet doivent être\n * échappés. Les caractères de contrôle, eux, sont interdits par le format — un\n * chemin ou un jeton qui en contiendrait casserait la config en silence.\n */\nfunction tomlString(value: string): string {\n const escaped = value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/[\\u0000-\\u001f\\u007f]/g, (c) => `\\\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);\n return `\"${escaped}\"`;\n}\n","import { readdir } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst CODEX_SESSIONS_DIR = join(homedir(), '.codex', 'sessions');\n\n/**\n * Codex persists each interactive session as a rollout file:\n * ~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<ISO>-<sessionId>.jsonl\n *\n * The app-server reuses the same session id as the rollout file, so a\n * conversation's `codexThreadId` can be passed to `codex resume <id>` — but\n * only if the rollout actually exists on disk, otherwise the CLI errors out.\n */\nasync function findCodexSessionFile(sessionId: string): Promise<string | null> {\n // Filenames end with `-<sessionId>.jsonl`; scan the date-bucketed tree.\n const suffix = `-${sessionId}.jsonl`;\n async function walk(dir: string): Promise<string | null> {\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n const found = await walk(full);\n if (found) return found;\n } else if (entry.name.endsWith(suffix)) {\n return full;\n }\n }\n return null;\n }\n return walk(CODEX_SESSIONS_DIR);\n}\n\n/**\n * Cette session Codex existe-t-elle ?\n *\n * Codex a migré des rollouts JSONL vers une base SQLite : `~/.codex/sessions/`\n * n'est plus alimenté. Se fier au seul fichier faisait répondre « non » pour\n * toute session récente — et `codex resume` n'était donc jamais tenté, la\n * conversation repartant de zéro sans erreur ni trace.\n *\n * On interroge donc la base d'abord, et le disque ensuite : une installation\n * antérieure à la migration garde ses rollouts.\n */\nasync function codexSessionExists(sessionId: string): Promise<boolean> {\n const { threadExists } = await import('./codex-thread-db.js');\n if (threadExists(sessionId)) return true;\n return (await findCodexSessionFile(sessionId)) !== null;\n}\n\n/**\n * L'id à passer à `codex resume` pour poursuivre une conversation, ou `null`\n * s'il faut repartir d'une session neuve.\n *\n * Codex choisit lui-même l'id de son fil — contrairement à Claude, à qui on\n * impose `--session-id`. Le fil réel est donc découvert après coup et rangé\n * dans `codexThreadId` ; l'id de conversation, lui, ne correspond à aucun\n * rollout. Ne tester que ce dernier faisait répondre « non » à chaque fois :\n * après un redémarrage, la conversation repartait de zéro au lieu de reprendre.\n */\nexport async function resolveCodexResumeId(\n sessionId: string | undefined,\n codexThreadId: string | undefined,\n): Promise<string | null> {\n for (const candidate of [codexThreadId, sessionId]) {\n if (candidate && await codexSessionExists(candidate)) return candidate;\n }\n return null;\n}\n","import { initTRPC, TRPCError } from '@trpc/server';\nimport type { OpenApiMeta } from 'trpc-to-openapi';\nimport type { CreateExpressContextOptions } from '@trpc/server/adapters/express';\nimport { verifyJwt } from '../services/auth-token.js';\nimport { hasEncryptionKey, isCiMode } from '../utils/encryption-key.js';\nimport { apiKeyStore } from '../services/api-key-store.js';\nimport type { ApiKey, ApiServiceAction, ApiEnvAction, MissingVariable } from '@runeya/packages-shared';\nimport { MissingVariableSchema } from '@runeya/packages-shared';\nimport { z } from 'zod';\n\ntype CallerType = 'app' | 'api';\n\nexport function createContext({ req }: CreateExpressContextOptions) {\n return {\n authorization: req.headers.authorization ?? null,\n // L'adresse de la socket, et non `req.ip` : celle-ci ne dépend d'aucun\n // en-tête, donc d'aucune valeur qu'un appelant distant puisse forger.\n remoteAddress: req.socket.remoteAddress ?? null,\n };\n}\n\n// `remoteAddress` est optionnel : le contexte WebSocket, lui, n'en fournit pas,\n// et aucune route servie par socket ne demande la boucle locale.\ntype Context = Omit<Awaited<ReturnType<typeof createContext>>, 'remoteAddress'> & {\n remoteAddress?: string | null;\n};\n\nexport type AuthContext = Context & {\n callerType: CallerType;\n apiKey: ApiKey | null;\n};\n\n/**\n * Le seul morceau de `cause` autorisé à rejoindre le client : la liste des\n * variables d'environnement qu'il reste à remplir. Tout le reste est écarté.\n */\nfunction extractMissingVariables(cause: unknown): { missingVariables?: MissingVariable[]; environmentId?: string } {\n if (!cause || typeof cause !== 'object') return {};\n const parsed = z.object({\n missingVariables: z.array(MissingVariableSchema).max(500),\n environmentId: z.string().max(128),\n }).safeParse(cause);\n return parsed.success ? parsed.data : {};\n}\n\nconst t = initTRPC.context<Context>().meta<OpenApiMeta>().create({\n errorFormatter({ shape, error }) {\n return {\n ...shape,\n data: {\n ...shape.data,\n message: error.message,\n // La cause d'une erreur n'est PAS transportée par défaut, et c'est\n // voulu : elle porte souvent la pile ou l'objet interne qui a échoué.\n // Un seul champ traverse, nommé et validé — la liste des variables à\n // remplir, dont le client a besoin pour ouvrir l'assistant sur ce qui\n // vient exactement d'être refusé.\n ...extractMissingVariables(error.cause),\n },\n };\n },\n});\n\nexport const router = t.router;\n/** Appeler une route sans passer par HTTP — les tests s'en servent. */\nexport const createCallerFactory = t.createCallerFactory;\nexport const publicProcedure = t.procedure;\n\nconst API_KEY_REGEX = /^[0-9a-f]{64}$/i;\n\nconst authMiddleware = t.middleware(async ({ ctx, next }) => {\n const { authorization } = ctx;\n\n // API API key: 64 hex chars — checked before the JWT/encryption-key bypass\n if (authorization?.startsWith('Bearer ') && API_KEY_REGEX.test(authorization.slice(7))) {\n if (!hasEncryptionKey()) {\n throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'Server is locked — master password must be unlocked before using API API keys' });\n }\n const token = authorization.slice(7);\n const apiKey = await apiKeyStore.validateKey(token);\n if (!apiKey) {\n throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Invalid API API key' });\n }\n return next({ ctx: { ...ctx, callerType: 'api' as CallerType, apiKey } });\n }\n\n // CI mode: key loaded from RUNEYA_ENCRYPTION_KEY env var — no JWT in circulation, bypass auth.\n if (isCiMode()) {\n return next({ ctx: { ...ctx, callerType: 'app' as CallerType, apiKey: null } });\n }\n\n if (!authorization?.startsWith('Bearer ')) {\n throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Missing or invalid Authorization header' });\n }\n\n const token = authorization.slice(7);\n\n try {\n await verifyJwt(token);\n } catch {\n throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Invalid or expired token' });\n }\n\n return next({ ctx: { ...ctx, callerType: 'app' as CallerType, apiKey: null } });\n});\n\nexport const protectedProcedure = t.procedure.use(authMiddleware);\n\nconst LOOPBACK = new Set(['127.0.0.1', '::1', '::ffff:127.0.0.1']);\n\n/**\n * Refuse tout appel qui ne vient pas de la machine elle-même.\n *\n * Pour le peu de routes qui doivent répondre AVANT le déverrouillage — il n'y a\n * alors pas de jeton en circulation, et exiger l'en-tête reviendrait à les\n * rendre inatteignables depuis l'écran de déverrouillage, précisément là où\n * elles servent. Le mode LAN ouvre l'écoute à d'autres postes : la boucle\n * locale reste ce qui distingue l'utilisateur assis devant la machine.\n */\nexport function requireLoopback(ctx: Context): void {\n if (!ctx.remoteAddress || !LOOPBACK.has(ctx.remoteAddress)) {\n throw new TRPCError({ code: 'FORBIDDEN', message: 'This route only answers the local machine.' });\n }\n}\n\n/**\n * La machine elle-même, OU un appelant qui a déjà passé le déverrouillage.\n *\n * `requireLoopback` seul convient à ce qui doit répondre AVANT tout jeton. Mais\n * une route qui doit aussi rester joignable une fois l'application ouverte n'a\n * pas à être réservée au poste : en mode LAN, l'onglet qui la pilote est sur une\n * autre machine, et il détient un jeton — c'est précisément la preuve que la\n * boucle locale servait à remplacer.\n *\n * Le jeton d'application seulement : une clé d'API porte des droits sur des\n * services et des environnements, jamais sur l'installation elle-même. Élargir\n * ici reviendrait à donner à une clé de service un pouvoir qu'aucune de ses\n * portées ne nomme.\n */\nexport async function requireLoopbackOrToken(ctx: Context): Promise<void> {\n if (ctx.remoteAddress && LOOPBACK.has(ctx.remoteAddress)) return;\n // Le mode CI n'a pas de jeton en circulation, comme pour `protectedProcedure`.\n if (isCiMode()) return;\n\n const authorization = ctx.authorization;\n if (!authorization?.startsWith('Bearer ')) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: 'This route answers the local machine, or a signed-in app.',\n });\n }\n try {\n await verifyJwt(authorization.slice(7));\n } catch {\n throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Invalid or expired token' });\n }\n}\n\n// ---------------------------------------------------------------------------\n// API scope enforcement helpers\n// Call these at the top of route handlers that API callers can reach.\n// App callers (callerType === 'app') always pass through.\n// ---------------------------------------------------------------------------\n\n/** Throw FORBIDDEN if an API caller lacks `action` on `serviceId`. */\nexport function requireServiceAction(\n ctx: AuthContext,\n serviceId: string,\n action: ApiServiceAction,\n): void {\n if (ctx.callerType !== 'api') return;\n const perm = ctx.apiKey!.scopes.services.find(p => p.serviceId === serviceId);\n if (!perm?.actions.includes(action)) {\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: `API key lacks '${action}' permission on service ${serviceId}`,\n });\n }\n}\n\n/**\n * Return the set of service IDs an API caller is allowed to see.\n * Returns null for app callers (= all services allowed).\n */\nexport function getAllowedServiceIds(ctx: AuthContext): Set<string> | null {\n if (ctx.callerType !== 'api') return null;\n return new Set(ctx.apiKey!.scopes.services.map(p => p.serviceId));\n}\n\n/**\n * Throw FORBIDDEN if an API caller lacks `action` on `environmentId`.\n * Returns whether secrets are allowed (always true for app callers).\n *\n * @param serviceId - when provided, the check is restricted to the permission entry for that\n * specific service. This prevents cross-service escalation: a global env ID shared across\n * multiple services cannot grant access via a different service's permission entry.\n */\n/**\n * Le droit d'un appelant sur un environnement, ou `null` s'il ne l'a pas.\n *\n * Une seule lecture de la règle, pour les deux façons de s'en servir :\n * `requireEnvAction` refuse, `canEnvAction` trie. Les écrire séparément, ce\n * serait deux définitions du même droit — et une seule corrigée le jour où\n * elle change.\n */\nfunction findEnvPermission(\n ctx: AuthContext,\n environmentId: string,\n action: ApiEnvAction,\n serviceId?: string,\n): { includeSecrets: boolean } | null {\n if (ctx.callerType !== 'api') return { includeSecrets: true };\n const permsToCheck = serviceId\n ? ctx.apiKey!.scopes.services.filter(p => p.serviceId === serviceId)\n : ctx.apiKey!.scopes.services;\n for (const svcPerm of permsToCheck) {\n const envPerm = svcPerm.environments.find(e => e.environmentId === environmentId);\n if (envPerm?.actions.includes(action)) {\n return { includeSecrets: envPerm.includeSecrets };\n }\n }\n return null;\n}\n\nexport function requireEnvAction(\n ctx: AuthContext,\n environmentId: string,\n action: ApiEnvAction,\n serviceId?: string,\n): { includeSecrets: boolean } {\n const permission = findEnvPermission(ctx, environmentId, action, serviceId);\n if (permission) return permission;\n throw new TRPCError({\n code: 'FORBIDDEN',\n message: `API key lacks '${action}' permission on environment ${environmentId}`,\n });\n}\n\n/**\n * Le même droit, en question plutôt qu'en refus.\n *\n * Pour les réponses qui rassemblent plusieurs environnements : le contrôle\n * d'entrée ne porte que sur celui qu'on a demandé, alors que le contenu peut\n * venir d'un parent ou d'un environnement de service. Sans ce tri, une clé\n * bornée à un environnement apprendrait les noms et les descriptions des\n * variables des autres — jamais leurs valeurs, mais c'est déjà trop.\n */\nexport function canEnvAction(\n ctx: AuthContext,\n environmentId: string,\n action: ApiEnvAction,\n): boolean {\n return findEnvPermission(ctx, environmentId, action) !== null;\n}\n","import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';\nimport { readFile, writeFile, rename, mkdir, chmod } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { keyedFilePath, orgVaultRoot, listOrgVaults } from './vault-resolver.js';\nimport { ApiKeysFileSchema, type ApiKey, type ApiKeyScope, type MaskedApiKey } from '@runeya/packages-shared';\nimport { encryptValue, decryptValue, isEncryptedValue } from './crypto-helpers.js';\nimport { rememberFile, hasChangedSinceRead, wasRead } from './disk-freshness.js';\n\n/** Strips the secret from a key before it leaves the store. */\nexport function maskApiKey(apiKey: ApiKey): MaskedApiKey {\n const { key, ...rest } = apiKey;\n return { ...rest, keyPreview: key.slice(-4) };\n}\n\nconst FILE_PUBLIC = 'api-keys.json';\nconst FILE_PRIVATE = 'api-keys.private.json';\n\n/**\n * À qui appartient une clé d'API : une organisation, ou la machine.\n *\n * Une clé nomme des services et des environnements dans ses portées — donc des\n * choses qui appartiennent à un projet, donc à une organisation. La ranger au\n * niveau du poste faisait décrire l'hôte à un fait qui décrit la chose\n * partagée. `null` reste la machine, pour une clé qui ne vise que des projets\n * locaux.\n */\nexport type ApiKeyOwner = string | null;\n\nclass ApiKeyStore {\n private keys: Map<string, ApiKey> = new Map();\n /** Le propriétaire de chaque clé, d'après le coffre où elle a été lue. */\n private owners: Map<string, ApiKeyOwner> = new Map();\n /** Tous les coffres rencontrés — voir `AgentStore.knownOwners`, même raison. */\n private knownOwners: Set<ApiKeyOwner> = new Set([null]);\n /**\n * Les clés que ce poste n'a pas su déchiffrer, gardées sous leur forme de\n * disque.\n *\n * Le store tient le secret en clair en mémoire et le re-chiffre à chaque\n * écriture : une clé absente de la mémoire est donc une clé effacée du\n * fichier à la première sauvegarde. Or « je ne sais pas l'ouvrir » n'est pas\n * « elle n'existe plus » — le chiffré d'origine repart tel quel, et redevient\n * lisible le jour où la bonne clé est adoptée.\n */\n private unreadable: Map<ApiKeyOwner, Map<string, unknown>> = new Map();\n private loaded = false;\n\n /**\n * Encrypted at rest, so the file sits beside the key that opens it — read\n * under a different key it would decrypt to blanks, then be saved back blank.\n */\n /**\n * Le fichier d'un propriétaire. Côté machine il reste collé à la clé qui\n * l'ouvre (`keyedFilePath`) ; côté organisation, c'est son coffre.\n */\n private filePath(isPublic: boolean, owner: ApiKeyOwner): Promise<string> {\n const name = isPublic ? FILE_PUBLIC : FILE_PRIVATE;\n if (owner === null) return keyedFilePath(name);\n return Promise.resolve(join(orgVaultRoot(owner), name));\n }\n\n private async persistFile(isPublic: boolean, owner: ApiKeyOwner): Promise<void> {\n const path = await this.filePath(isPublic, owner);\n // Réparé en dehors du serveur depuis qu'on l'a lu : on n'y touche pas.\n // Réécrire notre copie déferait la réparation, l'omettre effacerait les\n // clés qu'elle protège.\n if (this.unreadable.get(owner)?.size && wasRead(path) && await hasChangedSinceRead(path)) {\n console.warn(\n `[api-key-store] ${path} a changé depuis sa lecture — écriture ignorée`\n + ' pour ne pas écraser une réparation faite en dehors du serveur.',\n );\n return;\n }\n await mkdir(dirname(path), { recursive: true });\n // Le secret est chiffré au repos, comme partout ailleurs dans le serveur :\n // le mode 0600 protège des autres utilisateurs, pas d'une copie du dataDir.\n const keys: unknown[] = Array.from(this.keys.values())\n .filter(k => k.isPublic === isPublic && (this.owners.get(k.id) ?? null) === owner)\n // Chiffrée par la clé du coffre où la clé d'API est rangée.\n .map(k => ({ ...k, key: encryptValue(k.key, owner ?? undefined) }));\n // Puis ce qui ne s'était pas ouvert, inchangé. Rien n'est re-chiffré ici :\n // le chiffré est déjà le bon, sous une clé que ce poste n'a simplement pas.\n for (const raw of this.unreadable.get(owner)?.values() ?? []) {\n if ((raw as { isPublic?: boolean }).isPublic === isPublic) keys.push(raw);\n }\n const tmp = path + '.tmp';\n await writeFile(tmp, JSON.stringify({ keys }, null, 2), 'utf-8');\n await rename(tmp, path);\n await chmod(path, 0o600);\n await rememberFile(path);\n }\n\n /**\n * Réécrire ce fichier pour chaque propriétaire connu.\n *\n * Y compris ceux qui n'ont plus de clé : sinon une clé supprimée — ou dont le\n * propriétaire a changé — survivrait dans son ancien coffre.\n */\n private async persist(isPublic: boolean): Promise<void> {\n for (const owner of this.knownOwners) await this.persistFile(isPublic, owner);\n }\n\n private async loadFile(path: string, owner: ApiKeyOwner): Promise<void> {\n try {\n const raw = await readFile(path, 'utf-8');\n await rememberFile(path);\n const parsed = ApiKeysFileSchema.safeParse(JSON.parse(raw));\n if (parsed.success) {\n // Le coffre est connu dès que son fichier se lit, avant tout\n // déchiffrement : c'est ce qui garantit qu'une réécriture ultérieure\n // repassera bien par lui.\n this.knownOwners.add(owner);\n for (const k of parsed.data.keys) {\n // Une clé par une : le `catch` du fichier entier abandonnait toutes\n // les suivantes dès la première illisible, et la sauvegarde d'après\n // les effaçait.\n try {\n // Les fichiers écrits avant le chiffrement contiennent le secret en\n // clair : on les accepte et le prochain persist les rechiffrera.\n const key = isEncryptedValue(k.key) ? decryptValue(k.key, owner ?? undefined) : k.key;\n this.keys.set(k.id, { ...k, key });\n this.owners.set(k.id, owner);\n } catch {\n console.error(`[api-key-store] Clé d'API ${k.id} illisible sous la clé de ce coffre — conservée telle quelle sur le disque`);\n const bucket = this.unreadable.get(owner) ?? new Map<string, unknown>();\n bucket.set(k.id, k);\n this.unreadable.set(owner, bucket);\n }\n }\n }\n } catch {\n // File doesn't exist yet — start empty\n }\n }\n\n /** La machine, puis chaque organisation dont un coffre existe sur ce disque. */\n async load(): Promise<void> {\n if (this.loaded) return;\n this.loaded = true;\n // Même raison qu'ailleurs : la liste des illisibles décrit cette lecture-ci.\n this.unreadable.clear();\n const owners: ApiKeyOwner[] = [null, ...await listOrgVaults()];\n for (const owner of owners) {\n await this.loadFile(await this.filePath(true, owner), owner);\n await this.loadFile(await this.filePath(false, owner), owner);\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.keys.clear();\n this.owners.clear();\n this.knownOwners = new Set([null]);\n this.unreadable.clear();\n this.loaded = false;\n }\n\n /**\n * Combien de clés d'API ce coffre garde sans savoir les lire.\n *\n * Remonté à l'interface : une donnée conservée mais illisible n'est visible\n * nulle part autrement, et un silence se lit comme « il n'y a rien ».\n */\n async unreadableCount(owner: ApiKeyOwner): Promise<number> {\n await this.load();\n return this.unreadable.get(owner)?.size ?? 0;\n }\n\n /**\n * Keys as exposed to callers — secrets stripped. Use `reveal` to obtain one\n * deliberately; never hand the whole set of secrets out at once.\n */\n async list(): Promise<MaskedApiKey[]> {\n await this.load();\n return Array.from(this.keys.values()).map(maskApiKey);\n }\n\n /** The plaintext secret of a single key, or null when unknown. */\n async reveal(id: string): Promise<string | null> {\n await this.load();\n return this.keys.get(id)?.key ?? null;\n }\n\n async create(\n name: string,\n scopes: ApiKeyScope,\n isPublic: boolean,\n owner: ApiKeyOwner = null,\n ): Promise<ApiKey> {\n await this.load();\n const apiKey: ApiKey = {\n id: `apikey-${randomUUID()}`,\n name,\n key: randomBytes(32).toString('hex'),\n scopes,\n createdAt: new Date().toISOString(),\n isPublic,\n };\n this.keys.set(apiKey.id, apiKey);\n this.owners.set(apiKey.id, owner);\n this.knownOwners.add(owner);\n await this.persist(isPublic);\n return apiKey;\n }\n\n async update(id: string, name: string, scopes: ApiKeyScope, isPublic: boolean): Promise<ApiKey | null> {\n await this.load();\n const existing = this.keys.get(id);\n if (!existing) return null;\n const updated: ApiKey = { ...existing, name, scopes, isPublic };\n this.keys.set(id, updated);\n // If isPublic changed, persist both files (remove from old, add to new)\n if (existing.isPublic !== isPublic) {\n await this.persist(existing.isPublic);\n await this.persist(isPublic);\n } else {\n await this.persist(isPublic);\n }\n return updated;\n }\n\n async remove(id: string): Promise<boolean> {\n await this.load();\n const existing = this.keys.get(id);\n if (!existing) return false;\n this.keys.delete(id);\n this.owners.delete(id);\n await this.persist(existing.isPublic);\n return true;\n }\n\n async renewKey(id: string): Promise<ApiKey | null> {\n await this.load();\n const existing = this.keys.get(id);\n if (!existing) return null;\n const renewed: ApiKey = { ...existing, key: randomBytes(32).toString('hex') };\n this.keys.set(id, renewed);\n await this.persist(renewed.isPublic);\n return renewed;\n }\n\n async validateKey(candidateHex: string): Promise<ApiKey | null> {\n await this.load();\n if (!/^[0-9a-f]{64}$/i.test(candidateHex)) return null;\n const candidate = Buffer.from(candidateHex, 'hex');\n for (const apiKey of this.keys.values()) {\n const stored = Buffer.from(apiKey.key, 'hex');\n if (candidate.length === stored.length && timingSafeEqual(candidate, stored)) {\n return apiKey;\n }\n }\n return null;\n }\n}\n\nexport const apiKeyStore = new ApiKeyStore();\n","import { z } from 'zod';\n\nexport const Id128Schema = z.string().max(128);\nexport const ConversationIdSchema = z.string().max(128);\nexport const NameSchema = z.string().min(1).max(200);\nexport const ShortString200Schema = z.string().max(200);\nexport const DescriptionSchema = z.string().max(2000);\nexport const OptionalProviderIdSchema = Id128Schema.optional();\n","import { isAbsolute, join, dirname } from 'node:path';\nimport { projectStore } from './project-store.js';\nimport { serviceStore } from './service-store.js';\nimport { projectPathStore } from './project-path-store.js';\nimport type { ProjectRef } from './project-key.js';\nimport { conversationStore } from './conversation-store.js';\nimport { localVaultRoot } from './vault-resolver.js';\n\n/**\n * The one place that answers \"which directory does this work happen in?\".\n *\n * Terminals, the AI chat, git and the editor each used to answer it themselves,\n * and each fell back to `'.'` or `process.cwd()` — the directory Runeya was\n * launched from. For a cloud project, materialized from a document that holds no\n * path, that meant every project ran against whatever code happened to sit\n * there. Four copies of the same fallback are four chances to forget one, so\n * they now share this.\n *\n * Deliberately NOT a process-wide `chdir`: Runeya orchestrates several projects\n * in one process and hands execution to an agent that may not even be on this\n * machine. A working directory belongs to an operation, not to the server.\n *\n * The order is narrowest-first: a service knows better than its project, and a\n * project better than wherever the server was started.\n */\n\nexport interface WorkspaceTarget {\n serviceId?: string | undefined;\n projectId?: string | undefined;\n /**\n * L'organisation du projet visé. Sans elle, un id partagé par plusieurs\n * organisations désignerait un projet au hasard — et donc un répertoire de\n * travail au hasard.\n */\n orgId?: string | null | undefined;\n /**\n * A directory to use instead of the service's own `cwd`, anchored the same way.\n *\n * For paths a service carries besides its working directory — the wiki's\n * `docsPath`, say. Relative ones anchor on the project; absolute ones, and\n * those the agent expands, pass through untouched.\n */\n override?: string | undefined;\n}\n\n/**\n * Where a project lives on this machine, or null when nothing places it.\n *\n * Only cloud projects carry a path: a local project IS its directory, so the\n * launch directory is already the right answer for it.\n */\nasync function projectRoot(ref: ProjectRef): Promise<string | null> {\n const project = await projectStore.get(ref);\n if (!project?.orgId) return null;\n return projectPathStore.get(project);\n}\n\n/**\n * The absolute directory an operation should run in.\n *\n * A cwd that is already absolute, or that the agent expands itself (`~`, `$VAR`),\n * is returned untouched — prefixing those would produce a path that exists\n * nowhere.\n */\nexport async function resolveWorkspaceCwd(target: WorkspaceTarget): Promise<string> {\n const launchDir = localVaultRootParent();\n\n if (target.serviceId) {\n const service = await serviceStore.get(target.serviceId);\n if (service) {\n const raw = (target.override ?? service.cwd ?? '').trim();\n const projects = await projectStore.list();\n const owner = projects.find((p) => p.serviceIds.includes(service.id));\n const root = owner ? await projectRoot(owner) : null;\n // No project directory: hand back what the service holds, untouched. The\n // agent resolves relative paths against ITS OWN directory, and it may not\n // even be on this machine — absolutising here would send it somewhere that\n // does not exist.\n return root ? anchor(root, raw) : (raw || '.');\n }\n }\n\n if (target.projectId) {\n const root = await projectRoot({ id: target.projectId, orgId: target.orgId });\n const raw = (target.override ?? '').trim();\n return root ? anchor(root, raw) : (raw || launchDir);\n }\n\n return anchor(launchDir, (target.override ?? '').trim());\n}\n\n/** Join a relative path onto its base, leaving absolute and agent-expanded ones alone. */\nfunction anchor(base: string, raw: string): string {\n if (!raw || raw === '.') return base;\n if (raw.startsWith('~') || raw.startsWith('$') || isAbsolute(raw)) return raw;\n return join(base, raw);\n}\n\n/**\n * The directory Runeya was launched in — the vault's parent.\n *\n * Derived from the vault rather than read from `process.cwd()`, so it stays put\n * even if something changes the process's directory.\n */\nfunction localVaultRootParent(): string {\n return dirname(localVaultRoot());\n}\n\n/**\n * The directory to hand an AGENT, or undefined when there is nothing to anchor.\n *\n * Distinct from `resolveWorkspaceCwd` on purpose. That one always answers with an\n * absolute path, which is right for work the SERVER does on its own disk. An\n * agent is another process — possibly on another machine — and resolves relative\n * paths against its own directory: handing it this machine's launch directory\n * would send it somewhere that need not exist there.\n *\n * So it only speaks when a project actually places the work somewhere; otherwise\n * it stays silent and the caller keeps whatever default it had.\n */\nexport async function resolveAgentCwd(target: WorkspaceTarget): Promise<string | undefined> {\n if (target.serviceId) {\n const service = await serviceStore.get(target.serviceId);\n if (service) {\n const projects = await projectStore.list();\n const owner = projects.find((p) => p.serviceIds.includes(service.id));\n const root = owner ? await projectRoot(owner) : null;\n if (!root) return undefined;\n return anchor(root, (target.override ?? service.cwd ?? '').trim());\n }\n }\n\n if (target.projectId) {\n const root = await projectRoot({ id: target.projectId, orgId: target.orgId });\n if (!root) return undefined;\n return anchor(root, (target.override ?? '').trim());\n }\n\n return undefined;\n}\n\n/**\n * Le répertoire d'une conversation — celui du projet à qui elle appartient.\n *\n * Une conversation porte déjà son projet : le chat et les scénarios n'ont donc\n * rien à se faire passer de main en main jusqu'au lancement du CLI. Chacun le\n * demandait pourtant à sa façon, et la file kanban, qui lance un scénario hors\n * de toute requête, ne transmettait rien : le CLI travaillait alors dans le\n * répertoire de lancement de Runeya, pas dans celui du projet de la carte.\n *\n * Muette quand rien ne place la conversation, comme `resolveAgentCwd`.\n */\nexport async function resolveConversationCwd(conversationId: string): Promise<string | undefined> {\n const conv = await conversationStore.get(conversationId);\n const ref = conv ? await conversationStore.projectRefOf(conv) : null;\n if (!ref) return resolveAgentCwd({});\n return resolveAgentCwd({ projectId: ref.id, orgId: ref.orgId });\n}\n","import { readFile, writeFile, rename, mkdir, chmod } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { getEncryptionKey } from '../utils/encryption-key.js';\nimport { allVaultRoots, keyedFilePath, legacyMachineFilePath , vaultIdOfRoot } from './vault-resolver.js';\nimport { environmentStore } from './environment-store.js';\nimport { ensureProjectsLoaded } from './vault-registry.js';\nimport {\n encryptVariableSlots,\n decryptVariableSlots,\n extractEncryptedVariableSlots,\n} from './crypto-helpers.js';\nimport type { VariableOverride, VariableSlot } from '@runeya/packages-shared';\n\nconst FILENAME = 'environment-overrides.json';\n\n// On disk: flat Record<variableId, VariableOverride>\n// An override holds only a value slot — the env var name always comes from the\n// variable definition, resolved by variable ID.\ntype OverridesData = Record<string, VariableOverride>;\n\nclass EnvironmentOverridesStore {\n private data: OverridesData = {};\n private loaded = false;\n private diskEncryptedSlots: Record<string, string> = {};\n /**\n * Les surcharges dont le secret ne s'est pas ouvert.\n *\n * Vides en mémoire, elles doivent repartir chiffrées : sans cela, la\n * sauvegarde suivante remplacerait par du blanc une valeur dont il ne\n * manquait que la clé.\n */\n private unreadableSecrets: Set<string> = new Set();\n private saveQueue: Promise<void> = Promise.resolve();\n /** Which vault each override was read from, until its project is known. */\n private roots: Map<string, string> = new Map();\n\n /**\n * An override belongs to the project whose variable it answers.\n *\n * Nothing in an override says so — they are keyed by variable id alone — so\n * the owning project is resolved through the environment that defines the\n * variable. Overrides are never synced: they stay in the project's vault on\n * this machine, and no other machine sees them.\n *\n * An override whose variable no longer exists (a deleted environment, a row\n * left behind) has no project to belong to. It stays wherever it was read,\n * falling back to the key's own directory — never dropped, since the variable\n * may simply not be loaded yet.\n */\n private async rootFor(variableId: string): Promise<string> {\n // Le coffre de l'environnement qui définit la variable, plutôt que l'id de\n // son projet : cet id peut nommer un projet dans chaque organisation, le\n // coffre en désigne un seul.\n const root = await environmentStore.rootOfVariable(variableId);\n if (root) return root;\n return this.roots.get(variableId) ?? dirname(await keyedFilePath(FILENAME));\n }\n\n /** Where earlier versions kept them: read once, rewritten to their vaults. */\n private getLegacyFilePath(): string {\n return legacyMachineFilePath(FILENAME);\n }\n\n /**\n * Read every vault, then the places older versions used.\n *\n * Variable ids are unique, so merging the files cannot collide; a vault read\n * later simply wins over a legacy copy of the same override.\n */\n private async load(): Promise<void> {\n if (this.loaded) return;\n if (!getEncryptionKey()) return;\n this.loaded = true;\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 roots = await allVaultRoots();\n const sources = [\n this.getLegacyFilePath(),\n await keyedFilePath(FILENAME),\n ...roots.map((root) => join(root, FILENAME)),\n ];\n\n for (const path of sources) {\n let parsed: OverridesData;\n try {\n parsed = JSON.parse(await readFile(path, 'utf-8')) as OverridesData;\n } catch {\n continue;\n }\n\n // Cache encrypted slots then decrypt\n Object.assign(\n this.diskEncryptedSlots,\n extractEncryptedVariableSlots(parsed as Record<string, { value: VariableSlot }>),\n );\n const decrypted = decryptVariableSlots(\n parsed as Record<string, { value: VariableSlot }>,\n 'environment-overrides',\n (variableId) => this.unreadableSecrets.add(variableId),\n vaultIdOfRoot(dirname(path)),\n ) as Record<string, { value: VariableSlot }>;\n\n for (const [id, override] of Object.entries(decrypted)) {\n // Drop any legacy `key` field written by older versions: it has no\n // authority and goes stale when the variable is renamed.\n this.data[id] = { value: override.value };\n this.roots.set(id, dirname(path));\n }\n }\n }\n\n private save(): Promise<void> {\n this.saveQueue = this.saveQueue.then(() => this.doSave()).catch(() => this.doSave());\n return this.saveQueue;\n }\n\n /**\n * Write each vault that holds — or held — an override.\n *\n * A vault emptied by this save is still written, as an empty object: an\n * override that moved to its project's vault must not survive in the one it\n * came from.\n */\n private async doSave(): Promise<void> {\n const byRoot = new Map<string, OverridesData>();\n for (const root of this.roots.values()) byRoot.set(root, {});\n\n for (const [variableId, override] of Object.entries(this.data)) {\n const root = await this.rootFor(variableId);\n this.roots.set(variableId, root);\n const bucket = byRoot.get(root) ?? {};\n bucket[variableId] = override;\n byRoot.set(root, bucket);\n }\n\n for (const [root, overrides] of byRoot) {\n const encrypted = this.keepUnreadableSecrets(encryptVariableSlots(\n overrides as Record<string, { value: VariableSlot }>,\n this.diskEncryptedSlots,\n vaultIdOfRoot(root),\n ) as OverridesData);\n Object.assign(\n this.diskEncryptedSlots,\n extractEncryptedVariableSlots(encrypted as Record<string, { value: VariableSlot }>),\n );\n\n await mkdir(root, { recursive: true });\n const path = join(root, FILENAME);\n const tmpPath = path + '.tmp';\n await writeFile(tmpPath, JSON.stringify(encrypted, null, 2), 'utf-8');\n await rename(tmpPath, path);\n await chmod(path, 0o600);\n }\n }\n\n /**\n * Rendre au disque le chiffré d'une surcharge illisible, plutôt qu'un blanc.\n *\n * Pendant du garde-fou du store des environnements : tant que la valeur en\n * mémoire est vide, c'est que personne n'a retouché la variable et que son\n * chiffré fait toujours foi.\n */\n private keepUnreadableSecrets(overrides: OverridesData): OverridesData {\n if (this.unreadableSecrets.size === 0) return overrides;\n const result = { ...overrides };\n for (const variableId of this.unreadableSecrets) {\n const ciphertext = this.diskEncryptedSlots[variableId];\n const override = result[variableId];\n if (!ciphertext || !override || override.value.value) continue;\n result[variableId] = { ...override, value: { ...override.value, value: ciphertext } };\n }\n return result;\n }\n\n async getOverride(variableId: string): Promise<VariableOverride | null> {\n await this.load();\n return this.data[variableId] ?? null;\n }\n\n async setOverride(variableId: string, override: VariableOverride): Promise<void> {\n await this.load();\n this.data[variableId] = { value: override.value };\n await this.save();\n }\n\n async removeOverride(variableId: string): Promise<void> {\n await this.load();\n if (!this.data[variableId]) return;\n delete this.data[variableId];\n this.unreadableSecrets.delete(variableId);\n await this.save();\n }\n\n async getAll(): Promise<Record<string, VariableOverride>> {\n await this.load();\n return { ...this.data };\n }\n\n async removeOverridesForVariables(variableIds: string[]): Promise<void> {\n await this.load();\n let changed = false;\n for (const id of variableIds) {\n if (this.data[id]) {\n delete this.data[id];\n this.unreadableSecrets.delete(id);\n changed = true;\n }\n }\n if (changed) await this.save();\n }\n}\n\nexport const environmentOverridesStore = new EnvironmentOverridesStore();\n","import type { Variable, VariableOverride, ResolvedEnvironment, MissingVariable } from '@runeya/packages-shared';\nimport { environmentStore } from './environment-store.js';\nimport { environmentOverridesStore } from './environment-overrides-store.js';\n\ninterface VariableSource {\n environmentId: string;\n environmentName: string;\n overridden: boolean;\n resolvedFrom?: { environmentId: string; environmentName: string };\n /** Environment carrying the machine-local override that won for this key */\n overriddenBy?: { environmentId: string; environmentName: string };\n}\n\n/** Winning override for a variable key, plus the environment it comes from */\ninterface WinningOverride {\n variable: Variable;\n source?: { environmentId: string; environmentName: string };\n}\n\ninterface DagResult {\n variables: Record<string, Variable>;\n sources: Record<string, VariableSource>;\n /** Concrete parent values keyed by variable key (pre-dedup), used for interpolation context */\n parentEnv: Record<string, string>;\n /** Keys in parentEnv that are secret */\n parentSecretKeys: Set<string>;\n /** Maps variable key → source environment info for parent variables (before child override) */\n parentKeySources: Record<string, { environmentId: string; environmentName: string }>;\n}\n\n/**\n * Interpolate {{environment.KEY}} references in a value string.\n * envContext is the flat dict of resolved environment values.\n * Returns the interpolated string.\n * Missing keys are left as-is ({{environment.MISSING}} -> unchanged).\n */\nfunction interpolateValue(raw: string, envContext: Record<string, string>): string {\n return raw.replace(/\\{\\{environment\\.([^}]+)\\}\\}/g, (match, key) => {\n return envContext[key] !== undefined ? envContext[key] : match;\n });\n}\n\n/**\n * Resolve a Variable to its final string value.\n * Uses variable.value slot, computes interpolate(pre + value + post).\n *\n * The three parts are interpolated, not just `value`: a prefix like\n * `postgres://{{environment.DB_USER}}@` is as legitimate a reference as the\n * value itself, and the parts are concatenated into one string anyway. They\n * are interpolated separately so that a reference cannot be formed by the\n * seam between two parts (a `pre` ending in `{{environment.` completed by a\n * `value` starting with `KEY}}`), which would let a prefix reach a variable\n * the author never named.\n */\nfunction resolveVariable(\n variable: Variable,\n envContext: Record<string, string>,\n): { value: string; isSecret: boolean } {\n const slot = variable.value;\n const finalValue = interpolateValue(slot.pre, envContext)\n + interpolateValue(slot.value, envContext)\n + interpolateValue(slot.post, envContext);\n return { value: finalValue, isSecret: slot.isSecret };\n}\n\n/** Every part of a slot that may carry `{{environment.KEY}}` references. */\nfunction slotReferenceText(variable: Variable): string {\n const slot = variable.value;\n // Séparateur : deux parties voisines ne doivent pas former une référence\n // qu'aucune ne contient — même raison que l'interpolation partie par partie.\n return `${slot.pre}\\n${slot.value}\\n${slot.post}`;\n}\n\nclass EnvironmentResolver {\n async resolveForService(ctx: { serviceId: string; activeEnvironmentId: string }): Promise<{ env: Record<string, string>; envSecrets: string[] }> {\n if (!ctx.activeEnvironmentId) {\n return { env: {}, envSecrets: [] };\n }\n\n // Find the service-scoped environment for this service + active global env\n const serviceEnv = await environmentStore.findServiceEnvironment(ctx.serviceId, ctx.activeEnvironmentId);\n\n if (!serviceEnv) {\n // No service env: resolve just the global env\n return this.resolveEnvironment(ctx.activeEnvironmentId);\n }\n\n // Resolve the service env (which inherits from global via parentIds)\n return this.resolveEnvironment(serviceEnv.id);\n }\n\n async resolveEnvironment(environmentId: string): Promise<{ env: Record<string, string>; envSecrets: string[] }> {\n const visited = new Set<string>();\n const result = await this.resolveDag(environmentId, visited);\n\n // Apply overrides, resolved as their own chain (see resolveOverridesByKey)\n const overridesByKey = await this.resolveOverridesByKey(environmentId);\n for (const [varId, variable] of Object.entries(result.variables)) {\n const override = overridesByKey[variable.key];\n if (override) {\n result.variables[varId] = { ...variable, value: override.variable.value };\n }\n }\n\n // First pass: build raw env dict (no interpolation yet)\n const rawEnv: Record<string, string> = {};\n for (const [_varId, variable] of Object.entries(result.variables)) {\n const slot = variable.value;\n rawEnv[variable.key] = slot.pre + slot.value + slot.post;\n }\n\n // Build interpolation context:\n // Start from parent concrete values, then overlay with current concrete values.\n // This ensures {{environment.KEY}} resolves against parent values when the child\n // redefines KEY as an interpolation reference.\n const { ctx: interpolationCtx, secretKeys: ctxSecretKeys } = await this.buildInterpolationContext(environmentId, rawEnv, result.parentEnv, result.parentSecretKeys, result.parentKeySources);\n const secretKeys = this.collectSecretKeys(result.variables, ctxSecretKeys);\n\n // Second pass: interpolate and build final env dict\n const env: Record<string, string> = {};\n const envSecrets: string[] = [];\n for (const [_varId, variable] of Object.entries(result.variables)) {\n const { value, isSecret } = resolveVariable(variable, interpolationCtx);\n env[variable.key] = value;\n if (isSecret || secretKeys.has(variable.key)) {\n envSecrets.push(variable.key);\n }\n }\n\n return { env, envSecrets };\n }\n\n /**\n * Resolve an environment (with inheritance) and return only the variables\n * flagged `injectIntoAi`, as a flat KEY→value dict ready to be injected into\n * the process environment of an AI agent spawn.\n */\n async resolveAiInjectedEnv(environmentId: string): Promise<Record<string, string>> {\n if (!environmentId) return {};\n\n const visited = new Set<string>();\n const result = await this.resolveDag(environmentId, visited);\n\n // Apply overrides (same as resolveEnvironment)\n const overridesByKey = await this.resolveOverridesByKey(environmentId);\n for (const [varId, variable] of Object.entries(result.variables)) {\n const override = overridesByKey[variable.key];\n if (override) {\n result.variables[varId] = { ...variable, value: override.variable.value };\n }\n }\n\n // Build raw env for interpolation context\n const rawEnv: Record<string, string> = {};\n for (const variable of Object.values(result.variables)) {\n const slot = variable.value;\n rawEnv[variable.key] = slot.pre + slot.value + slot.post;\n }\n const { ctx: interpolationCtx } = await this.buildInterpolationContext(\n environmentId,\n rawEnv,\n result.parentEnv,\n result.parentSecretKeys,\n result.parentKeySources,\n );\n\n // Keep only flagged variables, interpolating their final value.\n const injected: Record<string, string> = {};\n for (const variable of Object.values(result.variables)) {\n if (!variable.value.injectIntoAi) continue;\n const { value } = resolveVariable(variable, interpolationCtx);\n injected[variable.key] = value;\n }\n return injected;\n }\n\n /**\n * Les variables qu'il faut remplir avant qu'un service de cet environnement\n * puisse démarrer.\n *\n * Deux motifs, et ils ne se mesurent pas au même endroit :\n *\n * - `required` porte sur la valeur **finale**, interpolation et surcharge\n * comprises : une variable obligatoire dont la valeur ne tient qu'à une\n * référence morte est aussi vide qu'une case blanche.\n * - `overrideOnly` porte sur l'**existence d'une surcharge machine**. Elle\n * n'a par construction pas de valeur partagée ; ce qui manque n'est pas un\n * contenu, c'est la réponse de ce poste-ci.\n *\n * Les drapeaux se lisent sur la définition, jamais sur le slot résolu : une\n * surcharge remplace le slot entier et ne porte aucun drapeau — les y lire\n * rendrait toute variable surchargée silencieusement facultative.\n */\n async findMissingVariables(environmentId: string): Promise<MissingVariable[]> {\n if (!environmentId) return [];\n\n const [{ env }, definitions, overridesByKey] = await Promise.all([\n this.resolveEnvironment(environmentId),\n this.resolveDag(environmentId, new Set()),\n this.resolveOverridesByKey(environmentId),\n ]);\n\n const missing: MissingVariable[] = [];\n for (const [variableId, variable] of Object.entries(definitions.variables)) {\n const slot = variable.value;\n const source = definitions.sources[variableId];\n const base = {\n variableId,\n key: variable.key,\n description: slot.description,\n isSecret: slot.isSecret,\n environmentId: source?.environmentId ?? environmentId,\n environmentName: source?.environmentName ?? '',\n };\n\n if (slot.overrideOnly && !overridesByKey[variable.key]) {\n missing.push({ ...base, reason: 'overrideOnly' });\n continue;\n }\n if (slot.required && (env[variable.key] ?? '') === '') {\n missing.push({ ...base, reason: 'required' });\n }\n }\n return missing;\n }\n\n /**\n * Tout ce qu'il reste à remplir sous l'environnement courant.\n *\n * Deux périmètres, et il faut les deux :\n *\n * 1. **L'environnement courant lui-même**, héritage compris —\n * `findMissingVariables` remonte ses `parentIds`. C'est là que vivent les\n * variables globales du projet, et c'est la première branche balayée.\n * 2. **Les environnements de service qui s'activent avec lui**\n * (`activateOn`). Ceux-là ne sont pas des ancêtres du global : ils en\n * héritent, dans l'autre sens. Ne pas y descendre revenait à ne rien\n * réclamer d'un service jusqu'à ce qu'on essaie de le démarrer.\n *\n * Dédoublonné par identifiant de variable : une variable du global remonte\n * dans chaque branche de service qui en hérite, et l'assistant ne doit pas\n * demander deux fois la même chose.\n */\n async findMissingForActiveEnvironment(globalEnvId: string): Promise<MissingVariable[]> {\n if (!globalEnvId) return [];\n\n const serviceEnvs = await environmentStore.listByActivateOn(globalEnvId);\n const branches = await Promise.all([\n this.findMissingVariables(globalEnvId),\n ...serviceEnvs.map((env) => this.findMissingVariables(env.id)),\n ]);\n\n const seen = new Set<string>();\n const missing: MissingVariable[] = [];\n for (const branch of branches) {\n for (const item of branch) {\n if (seen.has(item.variableId)) continue;\n seen.add(item.variableId);\n missing.push(item);\n }\n }\n return missing;\n }\n\n /**\n * Idem, pour ce qu'un service verra réellement : son environnement dédié\n * quand il en a un, l'environnement global sinon. Miroir de\n * `resolveForService`, dont il partage la règle de sélection.\n */\n async findMissingForService(ctx: { serviceId: string; activeEnvironmentId: string }): Promise<MissingVariable[]> {\n if (!ctx.activeEnvironmentId) return [];\n const serviceEnv = await environmentStore.findServiceEnvironment(ctx.serviceId, ctx.activeEnvironmentId);\n return this.findMissingVariables(serviceEnv ? serviceEnv.id : ctx.activeEnvironmentId);\n }\n\n async resolveEnvironmentWithSources(environmentId: string): Promise<ResolvedEnvironment> {\n const visited = new Set<string>();\n const result = await this.resolveDag(environmentId, visited);\n\n // Apply overrides, resolved as their own chain (see resolveOverridesByKey)\n const overridesByKey = await this.resolveOverridesByKey(environmentId);\n for (const [varId, variable] of Object.entries(result.variables)) {\n const override = overridesByKey[variable.key];\n if (override) {\n result.variables[varId] = { ...variable, value: override.variable.value };\n result.sources[varId] = {\n ...result.sources[varId],\n overridden: true,\n ...(override.source ? { overriddenBy: override.source } : {}),\n };\n }\n }\n\n // First pass: build raw env dict (no interpolation yet)\n const rawEnv: Record<string, string> = {};\n for (const [_varId, variable] of Object.entries(result.variables)) {\n const slot = variable.value;\n rawEnv[variable.key] = slot.pre + slot.value + slot.post;\n }\n\n // Build interpolation context\n const { ctx: interpolationCtx, secretKeys: ctxSecretKeys, keySources } = await this.buildInterpolationContext(environmentId, rawEnv, result.parentEnv, result.parentSecretKeys, result.parentKeySources);\n const secretKeys = this.collectSecretKeys(result.variables, ctxSecretKeys);\n\n // Second pass: interpolate and build final env dict\n const env: Record<string, string> = {};\n const envSecrets: string[] = [];\n for (const [_varId, variable] of Object.entries(result.variables)) {\n const { value, isSecret } = resolveVariable(variable, interpolationCtx);\n env[variable.key] = value;\n if (isSecret || secretKeys.has(variable.key)) {\n envSecrets.push(variable.key);\n }\n }\n\n // Post-process: for pure interpolation references, annotate `resolvedFrom`\n // so the frontend knows where the resolved value actually comes from,\n // while keeping the source pointing to where the variable is defined.\n const refPattern = /^\\{\\{environment\\.([^}]+)\\}\\}$/;\n for (const [varId, variable] of Object.entries(result.variables)) {\n const match = variable.value.value.match(refPattern);\n if (match) {\n const refSource = keySources[match[1]];\n if (refSource && result.sources[varId]?.environmentId !== refSource.environmentId) {\n result.sources[varId] = {\n ...result.sources[varId],\n resolvedFrom: refSource,\n };\n }\n }\n }\n\n return {\n variables: result.variables,\n env,\n envSecrets,\n sources: result.sources,\n };\n }\n\n /** Check if a raw value string interpolates any key that is secret in the context */\n /**\n * Toutes les clés dont la valeur est, directement ou par interpolation, un\n * secret.\n *\n * Le contexte d'interpolation ne connaît que les secrets *hérités* : les\n * secrets définis dans le même environnement n'y figurent pas, si bien qu'un\n * `AUTH='Bearer {{environment.TOKEN}}'` voisin de son propre `TOKEN` secret\n * ressortait non marqué — et donc non masqué par toute la chaîne en aval.\n *\n * On part des secrets hérités plus ceux déclarés ici, puis on propage\n * jusqu'au point fixe : une valeur qui référence une clé secrète devient\n * secrète à son tour, y compris en chaîne (A secret → B cite A → C cite B).\n */\n private collectSecretKeys(\n variables: Record<string, Variable>,\n parentSecretKeys: Set<string>,\n ): Set<string> {\n const secretKeys = new Set(parentSecretKeys);\n for (const variable of Object.values(variables)) {\n if (variable.value.isSecret) secretKeys.add(variable.key);\n }\n let changed = true;\n while (changed) {\n changed = false;\n for (const variable of Object.values(variables)) {\n if (secretKeys.has(variable.key)) continue;\n if (this.interpolatesSecretKey(slotReferenceText(variable), secretKeys)) {\n secretKeys.add(variable.key);\n changed = true;\n }\n }\n }\n return secretKeys;\n }\n\n private interpolatesSecretKey(rawValue: string, ctxSecretKeys: Set<string>): boolean {\n const refs = [...rawValue.matchAll(/\\{\\{environment\\.([^}]+)\\}\\}/g)].map(m => m[1]);\n return refs.some(ref => ctxSecretKeys.has(ref));\n }\n\n /**\n * Build the interpolation context for an environment.\n *\n * For inheriting envs (has parents): start from parent concrete values,\n * then overlay with current concrete values. This ensures that\n * {{environment.KEY}} resolves against the parent's value when the child\n * redefines KEY as an interpolation reference.\n *\n * For isolated service envs (scope=service, activateOn set, no parents):\n * the global env is resolved and used as fallback.\n */\n private async buildInterpolationContext(\n environmentId: string,\n rawEnv: Record<string, string>,\n parentEnv: Record<string, string>,\n parentSecretKeys: Set<string>,\n parentKeySources: Record<string, { environmentId: string; environmentName: string }>,\n ): Promise<{ ctx: Record<string, string>; secretKeys: Set<string>; keySources: Record<string, { environmentId: string; environmentName: string }> }> {\n const environment = await environmentStore.get(environmentId);\n\n // Isolated service env: resolve global env for interpolation context\n if (\n environment\n && environment.scope === 'service'\n && environment.activateOn\n && (!environment.parentIds || environment.parentIds.length === 0)\n ) {\n const globalResolved = await this.resolveEnvironmentWithSources(environment.activateOn);\n const secretKeys = new Set(globalResolved.envSecrets);\n // Build keySources from global env variables\n const keySources: Record<string, { environmentId: string; environmentName: string }> = {};\n for (const [varId, variable] of Object.entries(globalResolved.variables)) {\n const source = globalResolved.sources[varId];\n if (source) {\n keySources[variable.key] = { environmentId: source.environmentId, environmentName: source.environmentName };\n }\n }\n // Start with global values, then override with local values that are\n // concrete (not themselves interpolation references)\n const ctx = { ...globalResolved.env };\n for (const [key, value] of Object.entries(rawEnv)) {\n if (!/\\{\\{environment\\.[^}]+\\}\\}/.test(value)) {\n ctx[key] = value;\n }\n }\n return { ctx, secretKeys, keySources };\n }\n\n // Inheriting env: start from parent concrete values, overlay with concrete rawEnv values\n const ctx = { ...parentEnv };\n for (const [key, value] of Object.entries(rawEnv)) {\n if (!/\\{\\{environment\\.[^}]+\\}\\}/.test(value)) {\n ctx[key] = value;\n }\n }\n return { ctx, secretKeys: parentSecretKeys, keySources: parentKeySources };\n }\n\n /**\n * For service-scoped envs, compute effective parentIds by also including\n * the service-scoped envs of ancestor global environments.\n *\n * Example: \"Server (test)\" has parentIds=[test-global]. test-global has\n * parentIds=[Local-global]. This method adds \"Server (Local)\" to the\n * effective parents so service-specific variables from ancestor envs\n * are inherited correctly.\n */\n private async getEffectiveParentIds(environment: { scope?: string; reference?: string; activateOn?: string; parentIds: string[] }): Promise<string[]> {\n if (environment.scope !== 'service' || !environment.reference || !environment.activateOn) {\n return environment.parentIds;\n }\n\n // Walk each global parent and find the service-scoped env for the same service\n const additionalIds: string[] = [];\n for (const parentId of environment.parentIds) {\n const parentEnv = await environmentStore.get(parentId);\n if (!parentEnv || parentEnv.scope !== 'global') continue;\n\n for (const grandparentGlobalId of parentEnv.parentIds) {\n const ancestorServiceEnv = await environmentStore.findServiceEnvironment(environment.reference, grandparentGlobalId);\n if (ancestorServiceEnv && !environment.parentIds.includes(ancestorServiceEnv.id)) {\n additionalIds.push(ancestorServiceEnv.id);\n }\n }\n }\n\n if (additionalIds.length === 0) return environment.parentIds;\n return [...environment.parentIds, ...additionalIds];\n }\n\n /**\n * Project an environment's variables onto their overrides: keep only the\n * variables that have one, and swap in the override's value slot. The key\n * always comes from the definition.\n *\n * Feeding this projection back through `resolveDag` resolves the override\n * chain with the exact same inheritance and dedup-by-key rules as the value\n * chain — so an override set on a parent env is no longer swallowed when a\n * child (or a service env) redeclares the same key.\n */\n private projectOverrides(\n variables: Record<string, Variable>,\n overrides: Record<string, VariableOverride>,\n ): Record<string, Variable> {\n const projected: Record<string, Variable> = {};\n for (const [varId, variable] of Object.entries(variables)) {\n const override = overrides[varId];\n if (override) {\n projected[varId] = { key: variable.key, value: override.value };\n }\n }\n return projected;\n }\n\n /**\n * Resolve the override chain independently of the value chain and return the\n * winning override per variable key (empty when nothing is overridden).\n */\n private async resolveOverridesByKey(environmentId: string): Promise<Record<string, WinningOverride>> {\n const allOverrides = await environmentOverridesStore.getAll();\n if (Object.keys(allOverrides).length === 0) return {};\n\n const result = await this.resolveDag(environmentId, new Set(), (variables) =>\n this.projectOverrides(variables, allOverrides),\n );\n\n const byKey: Record<string, WinningOverride> = {};\n for (const [varId, variable] of Object.entries(result.variables)) {\n const source = result.sources[varId];\n byKey[variable.key] = {\n variable,\n ...(source ? { source: { environmentId: source.environmentId, environmentName: source.environmentName } } : {}),\n };\n }\n return byKey;\n }\n\n private async resolveDag(\n environmentId: string,\n visited: Set<string>,\n project?: (variables: Record<string, Variable>) => Record<string, Variable>,\n ): Promise<DagResult> {\n if (visited.has(environmentId)) {\n throw new Error(`Circular dependency detected in environment inheritance for ${environmentId}`);\n }\n\n const environment = await environmentStore.get(environmentId);\n if (!environment) {\n throw new Error(`Environment \"${environmentId}\" not found`);\n }\n\n visited.add(environmentId);\n\n // For service-scoped envs: ensure we also inherit from ancestor service-scoped envs.\n // When a service env's global parent itself has parent globals, we need to include\n // the service-scoped envs of those ancestor globals in the resolution chain.\n const effectiveParentIds = await this.getEffectiveParentIds(environment);\n\n // Resolve all parents first\n const parentResults: DagResult[] = [];\n for (const parentId of effectiveParentIds) {\n const parentVisited = new Set(visited);\n const parentResult = await this.resolveDag(parentId, parentVisited, project);\n parentResults.push(parentResult);\n }\n\n // Merge parent variables (left-to-right, later parents override earlier)\n const mergedVariables: Record<string, Variable> = {};\n const sources: Record<string, VariableSource> = {};\n\n for (const result of parentResults) {\n // Dedup by key: if a later parent has a variable with the same key, remove the earlier one\n for (const [varId, variable] of Object.entries(result.variables)) {\n for (const [existingId, existingVar] of Object.entries(mergedVariables)) {\n if (existingVar.key === variable.key && existingId !== varId) {\n delete mergedVariables[existingId];\n delete sources[existingId];\n }\n }\n mergedVariables[varId] = variable;\n sources[varId] = result.sources[varId] ?? sources[varId];\n }\n }\n\n // Build parentEnv: concrete values from parents BEFORE child dedup/override\n // Used as interpolation context so {{environment.KEY}} resolves against parent values\n const parentEnv: Record<string, string> = {};\n const parentSecretKeys = new Set<string>();\n const parentKeySources: Record<string, { environmentId: string; environmentName: string }> = {};\n for (const [varId, variable] of Object.entries(mergedVariables)) {\n const slot = variable.value;\n parentEnv[variable.key] = slot.pre + slot.value + slot.post;\n if (slot.isSecret) {\n parentSecretKeys.add(variable.key);\n }\n const source = sources[varId];\n if (source) {\n parentKeySources[variable.key] = { environmentId: source.environmentId, environmentName: source.environmentName };\n }\n }\n // Also include parent-of-parent values for deeper chains.\n // Prefer concrete values over interpolation references: if parentEnv already has a\n // raw {{environment.KEY}} ref for a key, replace it with a concrete value if found.\n const isInterpRef = (v: string) => /\\{\\{environment\\.[^}]+\\}\\}/.test(v);\n for (const result of parentResults) {\n for (const [key, value] of Object.entries(result.parentEnv)) {\n const existingIsRef = key in parentEnv && isInterpRef(parentEnv[key]);\n const newIsConcrete = !isInterpRef(value);\n if (!(key in parentEnv) || (existingIsRef && newIsConcrete)) {\n parentEnv[key] = value;\n }\n }\n for (const key of result.parentSecretKeys) {\n parentSecretKeys.add(key);\n }\n for (const [key, source] of Object.entries(result.parentKeySources)) {\n if (!(key in parentKeySources)) {\n parentKeySources[key] = source;\n }\n }\n }\n // Une variable héritée qui interpole un secret est elle-même un secret, et\n // doit le rester pour les descendants — sans ça le caractère secret se perd\n // dès qu'on descend d'un niveau.\n for (const key of this.collectSecretKeys(mergedVariables, parentSecretKeys)) {\n parentSecretKeys.add(key);\n }\n\n // Apply current environment's own variables\n const ownVariables = project ? project(environment.variables) : environment.variables;\n for (const [varId, variable] of Object.entries(ownVariables)) {\n // Remove any parent variable with the same key but different variableId (dedup by key)\n let wasInherited = varId in mergedVariables;\n for (const [existingId, existingVar] of Object.entries(mergedVariables)) {\n if (existingVar.key === variable.key && existingId !== varId) {\n delete mergedVariables[existingId];\n delete sources[existingId];\n wasInherited = true;\n }\n }\n mergedVariables[varId] = variable;\n sources[varId] = {\n environmentId: environment.id,\n environmentName: environment.name,\n overridden: wasInherited,\n };\n }\n\n return {\n variables: mergedVariables,\n sources,\n parentEnv,\n parentSecretKeys,\n parentKeySources,\n };\n }\n}\n\nexport const environmentResolver = new EnvironmentResolver();\n","// ─── Runeya AI Capabilities — Single Source of Truth ─────────────────────────\n//\n// Adding a new capability:\n// 1. Add an entry to RUNEYA_CAPABILITIES below.\n// 2. Done. Anthropic tools, OpenAI tools, runner prompt docs, and MCP routes\n// are all generated automatically from this list.\n\nexport interface CapabilityParam {\n name: string;\n description: string;\n required: boolean;\n}\n\nexport interface RuneyaCapability {\n /** Tool name (snake_case, used as function name by LLMs). */\n name: string;\n /** Human-readable description shown to the LLM. */\n description: string;\n /** tRPC route segment, e.g. \"service.list\" or \"process.start\". */\n route: string;\n /** HTTP method for the tRPC call. */\n method: 'GET' | 'POST';\n /** Input parameters exposed to the LLM. */\n params: CapabilityParam[];\n /** Build the tRPC input object from raw LLM params. */\n buildInput: (params: Record<string, string | undefined>) => Record<string, unknown> | undefined;\n /** If true, safety instructions will warn the LLM to require explicit user intent. */\n destructive?: boolean;\n /**\n * Optional usage hint injected into the system prompt so the LLM knows when to use this tool.\n * Example: '\"wait 5s\" or \"pause for 10 seconds\" → call wait'\n */\n usageHint?: string;\n}\n\n/**\n * Une liste, telle qu'un modèle l'écrit.\n *\n * Il envoie du JSON quand il y pense, une énumération séparée par des virgules\n * quand il n'y pense pas. Refuser la seconde ferait échouer l'appel sans que\n * rien n'explique pourquoi.\n */\nfunction toList(raw: string | undefined): string[] {\n if (!raw) return [];\n const text = raw.trim();\n if (text.startsWith('[')) {\n try {\n const parsed: unknown = JSON.parse(text);\n if (Array.isArray(parsed)) return parsed.map((v) => String(v).trim()).filter(Boolean);\n } catch {\n // Un JSON malformé se lit comme une énumération : mieux vaut retenir les\n // noms qu'il contient que de tout perdre sur une virgule en trop.\n }\n }\n return text.split(',').map((v) => v.trim().replace(/^[\"'[]+|[\"'\\]]+$/g, '')).filter(Boolean);\n}\n\n/**\n * Un champ qu'on peut soit changer, soit effacer, soit laisser tranquille.\n *\n * Absent : on n'y touche pas. `null` ou vide : on l'efface — c'est ce que le\n * cloud attend, et la seule façon pour un agent de retirer une échéance.\n */\nfunction clearable(\n raw: string | undefined,\n parse: (value: string) => unknown,\n key: string,\n): Record<string, unknown> {\n if (raw === undefined) return {};\n const text = String(raw).trim();\n if (!text || text === 'null') return { [key]: null };\n return { [key]: parse(text) };\n}\n\n/**\n * La cible d'une commande git : un service, ou à défaut un projet.\n *\n * Les quatre champs sont facultatifs côté route (`gitTargetShape`), qui résout\n * elle-même l'agent et le répertoire. On ne transmet que ce que le modèle a\n * réellement donné : un `serviceId` vide ferait échouer la résolution.\n */\nfunction gitTarget(p: Record<string, string | undefined>): Record<string, unknown> {\n return {\n ...(p.serviceId ? { serviceId: p.serviceId } : {}),\n ...(p.projectId ? { projectId: p.projectId } : {}),\n ...(p.agentId ? { agentId: p.agentId } : {}),\n ...(p.orgId ? { orgId: p.orgId } : {}),\n };\n}\n\nexport const RUNEYA_CAPABILITIES: RuneyaCapability[] = [\n {\n name: 'list_services',\n description: \"List services, with the serviceId and agentId to use in other calls. Pass the projectId your context names to stay on the project the user is looking at, or another one from list_projects to look elsewhere. Called with no project, this can answer for every project of every organization on the machine — far more than the interface shows.\",\n route: 'service.list',\n method: 'GET',\n params: [\n { name: 'projectId', description: 'Restrict to this project, from list_projects. Omit to list every service on the machine.', required: false },\n { name: 'orgId', description: 'Its organization. Needed only when the same project id exists in several.', required: false },\n ],\n buildInput: (p) => (p.projectId\n ? { projectId: p.projectId, ...(p.orgId ? { orgId: p.orgId } : {}) }\n : undefined),\n },\n {\n name: 'get_service_status',\n description: \"Get the current status (running, stopped, etc.) of a service. A service that has never been started on this agent has no process yet, and this answers 404 Process not found — that means STOPPED, not forbidden and not missing. Start it, or read its configuration with get_service.\",\n route: 'process.get',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'The service ID', required: true },\n { name: 'agentId', description: 'The agent ID hosting this service', required: true },\n ],\n buildInput: (p) => ({ agentId: p.agentId, id: p.serviceId }),\n },\n {\n name: 'get_logs',\n description: 'Get the recent buffered logs for a service.',\n route: 'process.logs',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'The service ID', required: true },\n { name: 'agentId', description: 'The agent ID hosting this service', required: true },\n ],\n buildInput: (p) => ({ agentId: p.agentId, processId: p.serviceId }),\n },\n {\n name: 'start_service',\n description: 'Start a service on an agent.',\n route: 'process.start',\n method: 'POST',\n params: [\n { name: 'serviceId', description: 'The service ID to start', required: true },\n { name: 'agentId', description: 'The agent ID hosting this service', required: true },\n ],\n buildInput: (p) => ({ agentId: p.agentId, processId: p.serviceId }),\n destructive: true,\n },\n {\n name: 'stop_service',\n description: 'Stop a running service on an agent.',\n route: 'process.stop',\n method: 'POST',\n params: [\n { name: 'serviceId', description: 'The service ID to stop', required: true },\n { name: 'agentId', description: 'The agent ID hosting this service', required: true },\n ],\n buildInput: (p) => ({ agentId: p.agentId, processId: p.serviceId }),\n destructive: true,\n },\n {\n name: 'restart_service',\n description: 'Restart a service on an agent (stop → redeploy → start).',\n route: 'process.restart',\n method: 'POST',\n params: [\n { name: 'serviceId', description: 'The service ID to restart', required: true },\n { name: 'agentId', description: 'The agent ID hosting this service', required: true },\n ],\n buildInput: (p) => ({ agentId: p.agentId, processId: p.serviceId }),\n destructive: true,\n },\n\n // ─── Utility ─────────────────────────────────────────────────────────────\n\n {\n name: 'wait',\n description: 'Wait for a given number of milliseconds before continuing.',\n route: 'utility.wait',\n method: 'POST',\n params: [\n { name: 'ms', description: 'Number of MILLISECONDS to wait. Convert from seconds: 1s=1000, 10s=10000, 1min=60000. Max 300000.', required: true },\n ],\n buildInput: (p) => ({ ms: Number(p.ms) }),\n usageHint: '\"wait 5s\" → wait(ms=5000), \"pause 10 seconds\" → wait(ms=10000), \"attends 3 secondes\" → wait(ms=3000). Always convert to milliseconds.',\n },\n\n // ─── Kanban ───────────────────────────────────────────────────────────────\n\n {\n name: 'kanban_list_boards',\n description: 'List the kanban boards of the organization. Returns the organizationId actually queried, the boardId to use in the other kanban tools, plus column and card counts. Cards themselves are not included — use kanban_get_board for those. An empty list means that organization really has no board: a failure to reach the cloud is reported as an error instead, never as an empty list.',\n route: 'kanban.listBoards',\n method: 'GET',\n params: [\n { name: 'includeArchived', description: 'Set to \"true\" to include archived boards. Default: false.', required: false },\n { name: 'organizationId', description: 'Which organization to read. Optional: needed only when the account belongs to several and the error message asks for it.', required: false },\n ],\n buildInput: (p) => ({\n includeArchived: p.includeArchived === 'true',\n ...(p.organizationId ? { organizationId: p.organizationId } : {}),\n }),\n },\n {\n name: 'kanban_get_board',\n description: 'Read one kanban board: its columns, and the cards in each (id, title, markdown body, assignees, comment count, and attachment metadata — use kanban_download_attachment to fetch the bytes of a file). Use the exact column names it returns when moving or adding a card.',\n route: 'kanban.getBoard',\n method: 'GET',\n params: [\n { name: 'boardId', description: 'The kanban board ID, from kanban_list_boards', required: true },\n ],\n buildInput: (p) => ({ boardId: p.boardId }),\n },\n {\n name: 'kanban_get_card_comments',\n description: \"Read the comment thread of one kanban card, newest last: who wrote each comment (ai or user, with the person's name), when, its text, and its attachments. The comments in your task context are a snapshot taken when the task started — call this to see anything written since, whenever someone tells you a comment was added or kanban_get_board reports a higher commentCount than you have.\",\n route: 'kanban.getCardComments',\n method: 'GET',\n params: [\n { name: 'boardId', description: 'The kanban board ID', required: true },\n { name: 'cardId', description: 'The card ID whose comments to read', required: true },\n { name: 'limit', description: 'How many of the most recent comments to return (1-200). Default: 50.', required: false },\n ],\n buildInput: (p) => ({\n boardId: p.boardId,\n cardId: p.cardId,\n ...(p.limit ? { limit: Number(p.limit) } : {}),\n }),\n usageHint: '\"le support a ajouté un commentaire\" / \"regarde les nouveaux commentaires\" → kanban_get_card_comments(boardId, cardId). Never answer from the snapshot in your context when fresh comments are mentioned.',\n },\n {\n name: 'kanban_search_cards',\n description: 'Search kanban cards by free text, matched against their title, body and comments (accent- and case-insensitive). Archived cards are included. Searches every board of the organization unless boardId narrows it down.',\n route: 'kanban.searchCards',\n method: 'GET',\n params: [\n { name: 'query', description: 'Text to look for in card titles, bodies and comments', required: true },\n { name: 'boardId', description: 'Restrict the search to this board. Omit to search all boards.', required: false },\n { name: 'limit', description: 'Maximum number of cards to return (1-100). Default: 25.', required: false },\n { name: 'organizationId', description: 'Which organization to search. Optional: needed only when the account belongs to several and the error message asks for it.', required: false },\n ],\n buildInput: (p) => ({\n query: p.query,\n ...(p.boardId ? { boardId: p.boardId } : {}),\n ...(p.limit ? { limit: Number(p.limit) } : {}),\n ...(p.organizationId ? { organizationId: p.organizationId } : {}),\n }),\n },\n {\n name: 'kanban_download_attachment',\n description: 'Download a kanban card attachment (image, PDF, any file) onto this machine and return its local path, so it can be opened with file-reading tools. Attachment ids come from the card context, from kanban_get_board, or from kanban_get_card_comments — a file attached to a comment downloads exactly like one attached to the card.',\n route: 'kanban.downloadAttachment',\n method: 'POST',\n params: [\n { name: 'attachmentId', description: 'The attachment ID, as listed on the card', required: true },\n { name: 'organizationId', description: 'Which organization the attachment belongs to. Optional: needed only when the account belongs to several and the error message asks for it.', required: false },\n ],\n buildInput: (p) => ({\n attachmentId: p.attachmentId,\n ...(p.organizationId ? { orgId: p.organizationId } : {}),\n }),\n },\n {\n name: 'kanban_move_card',\n description: 'Move a kanban card to another column. Use the exact column name.',\n route: 'kanban.moveCard',\n method: 'POST',\n params: [\n { name: 'boardId', description: 'The kanban board ID', required: true },\n { name: 'cardId', description: 'The card ID to move', required: true },\n { name: 'targetColumn', description: 'Exact name of the target column', required: true },\n ],\n buildInput: (p) => ({ boardId: p.boardId, cardId: p.cardId, targetColumn: p.targetColumn }),\n },\n {\n name: 'kanban_add_comment',\n description: 'Add a comment to a kanban card.',\n route: 'kanban.addComment',\n method: 'POST',\n params: [\n { name: 'boardId', description: 'The kanban board ID', required: true },\n { name: 'cardId', description: 'The card ID', required: true },\n { name: 'comment', description: 'The comment text (markdown supported)', required: true },\n ],\n buildInput: (p) => ({ boardId: p.boardId, cardId: p.cardId, comment: p.comment }),\n },\n {\n name: 'kanban_get_card_links',\n description: 'Read the filiation of a board: which card depends on which. Returns one entry per link (child card ↔ parent card), plus the title, board, column, assignees and comment count of any linked card living on another board. Use it when a card context says it has a parent or sub-cards and you need their ids to read them.',\n route: 'kanban.getCardLinks',\n method: 'GET',\n params: [\n { name: 'boardId', description: 'The kanban board ID whose links to read', required: true },\n ],\n buildInput: (p) => ({ boardId: p.boardId }),\n },\n {\n name: 'kanban_set_card_parent',\n description: 'Link a kanban card to a parent card, or detach it. The parent is the card the other one depends on — an epic and its tasks, a bug and its follow-ups. A card has at most one parent, and setting a new one replaces it. Omit parentCardId to detach. kanban_get_board reports the current filiation as parentCardId / childCardIds on each card.',\n route: 'kanban.setCardParent',\n method: 'POST',\n params: [\n { name: 'boardId', description: 'The kanban board ID of the card being linked', required: true },\n { name: 'cardId', description: 'The card that depends on the parent (the child)', required: true },\n { name: 'parentCardId', description: 'The parent card ID. Omit to detach the card from its parent.', required: false },\n { name: 'parentBoardId', description: \"The parent's board, when it lives on another board of the same organization. Defaults to boardId.\", required: false },\n ],\n buildInput: (p) => ({\n boardId: p.boardId,\n cardId: p.cardId,\n ...(p.parentCardId ? { parentCardId: p.parentCardId } : {}),\n ...(p.parentBoardId ? { parentBoardId: p.parentBoardId } : {}),\n }),\n },\n {\n name: 'kanban_add_card',\n description: 'Add a new card to a kanban column.',\n route: 'kanban.addCard',\n method: 'POST',\n params: [\n { name: 'boardId', description: 'The kanban board ID', required: true },\n { name: 'column', description: 'Exact name of the target column', required: true },\n { name: 'title', description: 'Card title', required: true },\n { name: 'content', description: 'Card content in markdown', required: false },\n ],\n buildInput: (p) => ({ boardId: p.boardId, column: p.column, title: p.title, content: p.content }),\n },\n {\n name: 'kanban_get_card',\n description: 'Read one kanban card by id, including archived ones. Cheaper than fetching the whole board, and the only way to read a card that has been archived.',\n route: 'kanban.getCard',\n method: 'GET',\n params: [\n { name: 'boardId', description: 'The kanban board ID', required: true },\n { name: 'cardId', description: 'The card ID', required: true },\n ],\n buildInput: (p) => ({ boardId: p.boardId, cardId: p.cardId }),\n },\n {\n name: 'kanban_update_card',\n description: 'Update an existing kanban card. Only the fields you pass are changed; passing null to priority, color or deadline clears it. Prefer this over creating a second card when something needs correcting.',\n route: 'kanban.updateCard',\n method: 'POST',\n params: [\n { name: 'boardId', description: 'The kanban board ID', required: true },\n { name: 'cardId', description: 'The card ID', required: true },\n { name: 'title', description: 'New title', required: false },\n { name: 'content', description: 'New card content, in markdown', required: false },\n { name: 'priority', description: 'Priority from 1 to 10, or null to clear it', required: false },\n { name: 'color', description: 'Card colour, or null to clear it', required: false },\n { name: 'groups', description: 'Labels, as a list of strings. Replaces the current ones.', required: false },\n { name: 'deadline', description: 'Due date as YYYY-MM-DD, or null to clear it', required: false },\n { name: 'assignees', description: 'User ids to assign, as a list. Replaces the current ones; use kanban_list_members to find the ids.', required: false },\n ],\n buildInput: (p) => ({\n boardId: p.boardId,\n cardId: p.cardId,\n ...(p.title !== undefined ? { title: p.title } : {}),\n ...(p.content !== undefined ? { content: p.content } : {}),\n // Tout arrive en chaîne : un modèle n'écrit pas des nombres, des listes\n // ni des `null` typés. `clearable` distingue « efface » de « ne touche\n // pas » — sans quoi vider une couleur serait impossible à exprimer, et\n // changer un titre risquerait de vider le reste.\n ...(clearable(p.priority, (v) => Number(v), 'priority')),\n ...(clearable(p.color, (v) => v, 'color')),\n ...(clearable(p.deadline, (v) => v, 'deadline')),\n ...(p.groups !== undefined ? { groups: toList(p.groups) } : {}),\n ...(p.assignees !== undefined ? { assignees: toList(p.assignees) } : {}),\n }),\n },\n {\n name: 'kanban_set_card_archived',\n description: 'Archive a kanban card (it leaves the board but is not deleted) or bring it back. An unarchived card returns to the column it left.',\n route: 'kanban.setCardArchived',\n method: 'POST',\n params: [\n { name: 'boardId', description: 'The kanban board ID', required: true },\n { name: 'cardId', description: 'The card ID', required: true },\n { name: 'archived', description: 'true to archive, false to bring the card back', required: true },\n ],\n buildInput: (p) => ({ boardId: p.boardId, cardId: p.cardId, archived: p.archived === 'true' }),\n },\n {\n name: 'kanban_list_members',\n description: 'List the people of the organization who can be assigned to a card, with their ids. Needed before assigning anyone with kanban_update_card.',\n route: 'kanban.listMembers',\n method: 'GET',\n params: [\n { name: 'organizationId', description: 'Which organization. Optional: needed only when the account belongs to several and the error message asks for it.', required: false },\n ],\n buildInput: (p) => (p.organizationId ? { organizationId: p.organizationId } : {}),\n },\n\n // ─── Lecture seule ───────────────────────────────────────────────────────\n //\n // Tout ce qui se consulte sans rien changer. La création, la mise à jour et\n // la suppression restent hors MCP : elles s'ajouteront au cas par cas.\n //\n // Volontairement absentes, et à ne pas ajouter sans y regarder à deux fois,\n // parce qu'elles rendent un secret : `environment.list` / `get` / `resolve` /\n // `resolveFlat` (un `Environment` porte ses variables, valeurs comprises),\n // `apiKeys.list` / `reveal`, `setup.getEncryptionKeyHex` et `getOrgKeys`\n // (clés de chiffrement), `settings.get` et `settings.listAIProviders` (clés\n // de fournisseurs d'IA — `settings.getPaths` et `getNetwork`, eux, ne\n // rendent que des chemins et une adresse, d'où leur présence ci-dessous),\n // `cloudAuth.session`, `agent.getPassthroughEnv`. Un agent n'a pas besoin de\n // ces valeurs pour travailler, et ce qu'il lit finit dans une transcription.\n {\n name: 'list_processes',\n description: \"List every process across all agents with its current state (running, stopped, exit code, uptime). Use this to see the whole machine at once, rather than calling get_service_status service by service. An agent only learns about a service when it is first started, so this lists what is deployed, not what is configured: an empty list means nothing has been started, not that you cannot see them — list_services shows what exists.\",\n route: 'process.list',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'get_service',\n description: 'Read the full configuration of one service: its commands, ports, groups, runner, log sources and restart policy. list_services gives the ids.',\n route: 'service.get',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'The service ID', required: true },\n ],\n buildInput: (p) => ({ id: p.serviceId }),\n },\n {\n name: 'get_service_metrics',\n description: 'Read the CPU and memory currently used by a running service.',\n route: 'process.metrics',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'The service ID', required: true },\n { name: 'agentId', description: 'The agent ID hosting this service', required: true },\n ],\n buildInput: (p) => ({ agentId: p.agentId, processId: p.serviceId }),\n },\n {\n name: 'list_agents',\n description: 'List the agents connected to this Runeya, with their status and whether they run on this machine. Passphrases are never returned.',\n route: 'agent.list',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'list_projects',\n description: 'List the projects known to this Runeya, with their ids and the services they hold.',\n route: 'project.list',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'get_project',\n description: 'Read one project: its services, its active environment, its organization.',\n route: 'project.get',\n method: 'GET',\n params: [\n { name: 'projectId', description: 'The project ID, from list_projects', required: true },\n { name: 'orgId', description: 'Its organization. Needed only when the same project id exists in several.', required: false },\n ],\n buildInput: (p) => ({ id: p.projectId, ...(p.orgId ? { orgId: p.orgId } : {}) }),\n },\n {\n name: 'list_project_paths',\n description: 'Where each project lives on this machine. Use it to find the directory to work in before reading or editing files.',\n route: 'project.localPaths',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'git_status',\n description: 'Read the git working tree of a service or a project: staged, modified and untracked files, current branch, ahead/behind counts.',\n route: 'git.status',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'Target the repository of this service', required: false },\n { name: 'projectId', description: 'Target the repository of this project, when no service is given', required: false },\n { name: 'agentId', description: 'The agent hosting the repository', required: false },\n { name: 'orgId', description: 'Its organization, when a project id alone is ambiguous', required: false },\n ],\n buildInput: (p) => gitTarget(p),\n },\n {\n name: 'git_log',\n description: 'Read the recent commits of a service or project repository: hash, author, date, message.',\n route: 'git.log',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'Target the repository of this service', required: false },\n { name: 'projectId', description: 'Target the repository of this project, when no service is given', required: false },\n { name: 'agentId', description: 'The agent hosting the repository', required: false },\n { name: 'orgId', description: 'Its organization, when a project id alone is ambiguous', required: false },\n { name: 'limit', description: 'How many commits to return (1-200). Default: 50.', required: false },\n ],\n buildInput: (p) => ({ ...gitTarget(p), ...(p.limit ? { limit: Number(p.limit) } : {}) }),\n },\n {\n name: 'git_branches',\n description: 'List the local and remote branches of a service or project repository.',\n route: 'git.branches',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'Target the repository of this service', required: false },\n { name: 'projectId', description: 'Target the repository of this project, when no service is given', required: false },\n { name: 'agentId', description: 'The agent hosting the repository', required: false },\n { name: 'orgId', description: 'Its organization, when a project id alone is ambiguous', required: false },\n ],\n buildInput: (p) => gitTarget(p),\n },\n {\n name: 'git_diff',\n description: 'Read the diff of a service or project repository — everything, or one file. Use it to see what changed before describing or reviewing it.',\n route: 'git.diff',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'Target the repository of this service', required: false },\n { name: 'projectId', description: 'Target the repository of this project, when no service is given', required: false },\n { name: 'agentId', description: 'The agent hosting the repository', required: false },\n { name: 'orgId', description: 'Its organization, when a project id alone is ambiguous', required: false },\n { name: 'path', description: 'Restrict the diff to this file path', required: false },\n { name: 'staged', description: 'Set to \"true\" to read the staged diff instead of the working tree', required: false },\n { name: 'untracked', description: 'Set to \"true\" to include untracked files', required: false },\n ],\n buildInput: (p) => ({\n ...gitTarget(p),\n ...(p.path ? { path: p.path } : {}),\n ...(p.staged !== undefined ? { staged: p.staged === 'true' } : {}),\n ...(p.untracked !== undefined ? { untracked: p.untracked === 'true' } : {}),\n }),\n },\n {\n name: 'list_docs',\n description: 'List the markdown documentation files of a scope, as a tree. Pair it with read_doc to answer from the project\\'s own docs rather than from guesswork.',\n route: 'wiki.list',\n method: 'GET',\n params: [\n { name: 'scope', description: '\"global\" for this Runeya, or \"service:<serviceId>\" for one service\\'s repository', required: true },\n ],\n buildInput: (p) => ({ scope: p.scope }),\n },\n {\n name: 'read_doc',\n description: 'Read one markdown documentation file. Paths come from list_docs; only .md and .markdown can be read.',\n route: 'wiki.read',\n method: 'GET',\n params: [\n { name: 'scope', description: '\"global\" for this Runeya, or \"service:<serviceId>\" for one service\\'s repository', required: true },\n { name: 'path', description: 'Relative path of the file, as returned by list_docs', required: true },\n ],\n buildInput: (p) => ({ scope: p.scope, path: p.path }),\n },\n {\n name: 'get_server_logs',\n description: \"Read Runeya's own recent log entries. Use it to explain why Runeya itself misbehaved — not to read a service's output, which is get_logs.\",\n route: 'diagnostics.recentLogs',\n method: 'GET',\n params: [\n { name: 'limit', description: 'How many entries to return (1-2000). Default: 500.', required: false },\n ],\n buildInput: (p) => (p.limit ? { limit: Number(p.limit) } : undefined),\n },\n {\n name: 'get_kanban_queue_state',\n description: 'Read the state of the kanban task queue: what is running, what is waiting. Use it before queuing work, or to explain why a card has not started.',\n route: 'kanbanQueue.getState',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'export_project',\n description: \"Read a project and every service it holds in one call, with secrets stripped out. Cheaper and safer than calling get_service for each service — prefer it whenever you need the whole picture of a project.\",\n route: 'project.export',\n method: 'GET',\n params: [\n { name: 'projectId', description: 'The project ID, from list_projects', required: true },\n { name: 'orgId', description: 'Its organization. Needed only when the same project id exists in several.', required: false },\n ],\n buildInput: (p) => ({ id: p.projectId, ...(p.orgId ? { orgId: p.orgId } : {}) }),\n },\n {\n name: 'git_remotes',\n description: 'List the git remotes of a service or project repository, with their URLs.',\n route: 'git.remotes',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'Target the repository of this service', required: false },\n { name: 'projectId', description: 'Target the repository of this project, when no service is given', required: false },\n { name: 'agentId', description: 'The agent hosting the repository', required: false },\n { name: 'orgId', description: 'Its organization, when a project id alone is ambiguous', required: false },\n ],\n buildInput: (p) => gitTarget(p),\n },\n {\n name: 'git_stash_list',\n description: 'List the stashes of a service or project repository. Useful before assuming work was lost: it may simply be stashed.',\n route: 'git.stashList',\n method: 'GET',\n params: [\n { name: 'serviceId', description: 'Target the repository of this service', required: false },\n { name: 'projectId', description: 'Target the repository of this project, when no service is given', required: false },\n { name: 'agentId', description: 'The agent hosting the repository', required: false },\n { name: 'orgId', description: 'Its organization, when a project id alone is ambiguous', required: false },\n ],\n buildInput: (p) => gitTarget(p),\n },\n {\n name: 'get_runeya_version',\n description: 'Read the version of Runeya running here, the latest published one, and whether this install is up to date. Use it before blaming a bug on the code: the machine may simply be behind.',\n route: 'health.versionInfo',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'get_runeya_paths',\n description: \"Read where Runeya keeps its things on this machine: its launch directory, its data directory, and the machine-wide directory holding cloud projects.\",\n route: 'settings.getPaths',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'get_network_info',\n description: 'Read where this Runeya listens (host and port) and whether it is reachable from the local network, with the URLs to use. Use it to give someone a working link instead of guessing one.',\n route: 'settings.getNetwork',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'list_runeya_instances',\n description: 'List every Runeya answering on this machine, this one included, with its port and directory. Use it when a port is taken or when two instances seem to disagree.',\n route: 'diagnostics.instances',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n {\n name: 'get_cloud_status',\n description: 'Say whether this Runeya is linked to a cloud account, and to which cloud. A kanban tool failing for lack of a cloud link is explained here.',\n route: 'cloudAuth.status',\n method: 'GET',\n params: [],\n buildInput: () => undefined,\n },\n];\n","import type { RuneyaCapability } from '../capabilities.js';\n\nexport interface AnthropicTool {\n name: string;\n description: string;\n input_schema: {\n type: 'object';\n properties: Record<string, { type: string; description: string }>;\n required: string[];\n [k: string]: unknown;\n };\n}\n\nexport function buildAnthropicTools(capabilities: RuneyaCapability[]): AnthropicTool[] {\n return capabilities.map(cap => ({\n name: cap.name,\n description: cap.description,\n input_schema: {\n type: 'object' as const,\n properties: Object.fromEntries(\n cap.params.map(p => [p.name, { type: 'string', description: p.description }]),\n ),\n required: cap.params.filter(p => p.required).map(p => p.name),\n },\n }));\n}\n","import type { RuneyaCapability } from '../capabilities.js';\n\nexport interface OpenAITool {\n type: 'function';\n function: {\n name: string;\n description: string;\n parameters: {\n type: 'object';\n properties: Record<string, { type: string; description: string }>;\n required: string[];\n };\n };\n}\n\nexport function buildOpenAITools(capabilities: RuneyaCapability[]): OpenAITool[] {\n return capabilities.map(cap => ({\n type: 'function' as const,\n function: {\n name: cap.name,\n description: cap.description,\n parameters: {\n type: 'object' as const,\n properties: Object.fromEntries(\n cap.params.map(p => [p.name, { type: 'string', description: p.description }]),\n ),\n required: cap.params.filter(p => p.required).map(p => p.name),\n },\n },\n }));\n}\n","import type { RuneyaCapability } from '../capabilities.js';\nexport type { RuneyaCapability };\n\n/**\n * Build the tool safety instruction block injected into cloud provider system prompts.\n * Generated from capabilities so it stays in sync automatically.\n */\nexport function buildToolSafetyInstruction(capabilities: RuneyaCapability[]): string {\n const destructive = capabilities.filter(c => c.destructive).map(c => c.name);\n const withHints = capabilities.filter(c => c.usageHint);\n\n const lines = ['## Tool use safety rules'];\n\n if (destructive.length > 0) {\n lines.push(`CRITICAL: Never call ${destructive.join(', ')} unless the user has EXPLICITLY and UNAMBIGUOUSLY requested it.`);\n lines.push(`- Only call destructive tools (${destructive.join(', ')}) when the user explicitly uses action words for a specific service.`);\n }\n\n if (withHints.length > 0) {\n lines.push('');\n lines.push('## Tool usage hints');\n for (const cap of withHints) {\n lines.push(`- ${cap.usageHint}`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * Shared tRPC HTTP caller used by:\n * - Server-side cloud tool executor (proxies cloud LLM tool calls to local tRPC)\n * - MCP server (calls tRPC on behalf of CLI runners)\n */\nexport async function callTrpc(\n baseUrl: string,\n route: string,\n method: 'GET' | 'POST',\n input: Record<string, unknown> | undefined,\n authToken: string,\n): Promise<unknown> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${authToken}`,\n };\n\n const url = method === 'GET' && input\n ? `${baseUrl}/api/trpc/${route}?input=${encodeURIComponent(JSON.stringify(input))}`\n : `${baseUrl}/api/trpc/${route}`;\n\n const res = await fetch(url, {\n method,\n headers,\n ...(method === 'POST' ? { body: JSON.stringify(input ?? {}) } : {}),\n signal: AbortSignal.timeout(15_000),\n });\n\n const json = await res.json() as Record<string, unknown>;\n if (json['error']) throw new Error(JSON.stringify(json['error']));\n const result = json['result'] as Record<string, unknown> | undefined;\n return result?.['data'] ?? result ?? json;\n}\n","import { env } from '../config/env.js';\nimport { RUNEYA_CAPABILITIES } from '@runeya/packages-ai-capabilities';\nimport type { ApiKey, ApiServicePerm, ServiceConfig } from '@runeya/packages-shared';\n\n/**\n * Generate a llms.txt for a JWT-authenticated admin caller (full access, all services).\n *\n * Ne documente QUE les tools MCP : plus aucune route HTTP, plus d'exemple curl.\n * Le repli HTTP qu'on décrivait ici était le chemin réellement emprunté — plus\n * concret que la consigne « prefer MCP tools » qui le précédait.\n */\nexport function generateAdminLlmsTxt(\n services: ServiceConfig[],\n jwt?: string,\n project?: { id: string; name: string; orgId?: string | null } | null,\n): string {\n const lines: string[] = [];\n\n lines.push('# Runeya — Service Management');\n lines.push('');\n lines.push('> You are operating as an authenticated admin with full access to all services.');\n lines.push('> Everything below is driven through the Runeya MCP tools.');\n lines.push('');\n\n // Sans cet ancrage, le prompt et les tools se contredisent : la liste\n // ci-dessous ne tient que les services de ce projet, alors que\n // `list_services` rend toute la machine — tous les projets, toutes les\n // organisations. L'agent citait donc des services que l'utilisateur n'a\n // jamais vus à l'écran, et concluait à un problème de permissions.\n //\n // Le serveur MCP applique bien ce projet par défaut quand Runeya le lui pose\n // (`RUNEYA_PROJECT_ID`), mais on ne le PROMET pas ici : le codex headless\n // partage un `codex app-server` unique, dont l'environnement est figé au\n // premier lancement. Lui annoncer un ancrage que son process n'a pas serait\n // lui dire quelque chose de faux — d'où une consigne vraie dans tous les\n // cas : nommer le projet.\n if (project) {\n lines.push(`## Current project`);\n lines.push('');\n lines.push(`You are working on **${project.name}** (projectId: \\`${project.id}\\`${project.orgId ? `, orgId: \\`${project.orgId}\\`` : ''}).`);\n lines.push(`The services listed below are its own. Name this project — \\`projectId=${project.id}\\` — on any tool that takes one, and you will stay on what the user actually sees.`);\n lines.push('A call that names no project may answer for every project of the machine, which is far more than this screen shows: if a result mentions services you do not recognize, that is why.');\n lines.push('');\n }\n\n if (services.length === 0) {\n lines.push('No services configured.');\n return lines.join('\\n');\n }\n\n lines.push('## Services');\n lines.push('');\n\n for (const svc of services) {\n lines.push(`### ${svc.name}`);\n lines.push(`- **ID**: \\`${svc.id}\\``);\n if (svc.agentId) lines.push(`- **Agent ID**: \\`${svc.agentId}\\``);\n if (svc.description) lines.push(`- **Description**: ${svc.description}`);\n if (svc.cwd) lines.push(`- **Working directory**: \\`${svc.cwd}\\``);\n lines.push('- **Permitted actions**: all');\n lines.push('');\n }\n\n lines.push('## MCP Tools');\n lines.push('');\n lines.push('Runeya is driven exclusively through the MCP tools below. There is no HTTP API to fall back on.');\n lines.push('');\n // Les noms seulement : le protocole MCP transporte déjà la description de\n // chaque tool. Les répéter ici coûterait ~2500 jetons par prompt pour un\n // texte que le CLI a déjà. Générée depuis les capabilities, et jamais écrite\n // à la main : la liste en dur n'en nommait que six sur vingt et une, aucune\n // du kanban — retirer le repli HTTP sans la corriger laissait le kanban sans\n // aucun chemin nommé.\n lines.push(RUNEYA_CAPABILITIES.map(c => `\\`${c.name}\\``).join(', ') + '.');\n lines.push('');\n\n // Portées jusqu'ici par le bloc HTTP qu'on vient de retirer : sans elles,\n // `wait` reprend des secondes pour des millisecondes, et le kanban répond\n // depuis l'instantané du prompt au lieu de relire le fil de commentaires.\n const hints = RUNEYA_CAPABILITIES.filter(c => c.usageHint);\n if (hints.length > 0) {\n lines.push('### Usage hints');\n lines.push('');\n for (const cap of hints) lines.push(`- ${cap.usageHint}`);\n lines.push('');\n }\n\n if (jwt) {\n lines.push('**Important**: Pass the `authToken` parameter with value `$RUNEYA_API_TOKEN` (from environment) on every MCP tool call for authentication.');\n lines.push('');\n }\n lines.push('Use the service IDs and agent IDs listed above when calling these tools.');\n lines.push('');\n lines.push('If a tool you need does not exist, say so instead of improvising another route.');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a llms.txt document describing the available API endpoints\n * for this key's scopes. Services are pre-filtered by the caller.\n */\nexport function generateLlmsTxt(\n apiKey: ApiKey,\n services: ServiceConfig[],\n): string {\n const { scopes } = apiKey;\n const baseUrl = `http://localhost:${env.PORT}`;\n const trpcBase = `${baseUrl}/api/trpc`;\n\n const lines: string[] = [];\n\n lines.push('# Runeya — Service Management API');\n lines.push('');\n lines.push('> This file describes the API surface available for this key.');\n lines.push(`> Base URL: ${trpcBase}`);\n lines.push(`> Authentication: pass this key as \\`Authorization: Bearer ${apiKey.key}\\` on all tRPC requests.`);\n lines.push('');\n\n if (services.length === 0) {\n lines.push('No services accessible with this key.');\n return lines.join('\\n');\n }\n\n // Build a quick lookup: serviceId → permission\n const permByServiceId = new Map<string, ApiServicePerm>(\n scopes.services.map(p => [p.serviceId, p]),\n );\n\n // --- Services ---\n lines.push('## Services');\n lines.push('');\n\n for (const svc of services) {\n const perm = permByServiceId.get(svc.id);\n const actions = perm?.actions ?? [];\n\n lines.push(`### ${svc.name}`);\n lines.push(`- **ID**: \\`${svc.id}\\``);\n if (svc.agentId) lines.push(`- **Agent ID**: \\`${svc.agentId}\\``);\n if (svc.description) lines.push(`- **Description**: ${svc.description}`);\n if (svc.cwd) lines.push(`- **Working directory**: \\`${svc.cwd}\\``);\n\n lines.push('- **Permitted actions**:');\n if (actions.length === 0) {\n lines.push(' - _(none)_');\n } else {\n for (const a of actions) lines.push(` - \\`${a}\\``);\n }\n\n const envPerms = perm?.environments ?? [];\n if (envPerms.length > 0) {\n lines.push('- **Environment access**:');\n for (const ep of envPerms) {\n const envActions = ep.actions.join(', ');\n const secrets = ep.includeSecrets ? ' _(includes secrets)_' : '';\n lines.push(` - \\`${ep.environmentId}\\`: ${envActions}${secrets}`);\n }\n }\n\n lines.push('');\n }\n\n // --- API Reference ---\n lines.push('## API Reference');\n lines.push('');\n lines.push('All endpoints use tRPC v11 over HTTP (`Content-Type: application/json`).');\n lines.push('');\n lines.push('**Important — tRPC v11 request format:**');\n lines.push('- **GET queries**: pass input as a plain JSON-encoded string in the `input` query param (no `{\"json\":...}` wrapper).');\n lines.push(' Example: `?input={\"id\":\"abc\"}`');\n lines.push('- **POST mutations**: send the input object as a plain JSON body (no `{\"json\":...}` wrapper).');\n lines.push(' Example body: `{\"agentId\":\"<agentId>\",\"processId\":\"<serviceId>\"}`');\n lines.push('- The `agentId` is listed per service above.');\n lines.push('- The `processId` is the same as the service `ID` listed above.');\n lines.push('');\n\n // Collect union of actions across all services for the reference section\n const allActions = new Set(scopes.services.flatMap(p => p.actions));\n const hasEnvRead = scopes.services.some(p => p.environments.some(e => e.actions.includes('env:read')));\n const hasEnvWrite = scopes.services.some(p => p.environments.some(e => e.actions.includes('env:write')));\n\n if (allActions.has('service:read')) {\n lines.push('### List services');\n lines.push(`\\`GET ${trpcBase}/service.list\\``);\n lines.push('');\n lines.push('### Get service');\n lines.push(`\\`GET ${trpcBase}/service.get?input={\"agentId\":\"<agentId>\",\"id\":\"<serviceId>\"}\\``);\n lines.push('');\n }\n\n if (allActions.has('logs:read')) {\n lines.push('### Read logs');\n lines.push(`\\`GET ${trpcBase}/process.logs?input={\"agentId\":\"<agentId>\",\"processId\":\"<serviceId>\"}\\``);\n lines.push('');\n }\n\n if (allActions.has('service:start')) {\n lines.push('### Start service');\n lines.push(`\\`POST ${trpcBase}/process.start\\``);\n lines.push('Body: `{\"agentId\":\"<agentId>\",\"processId\":\"<serviceId>\"}`');\n lines.push('');\n }\n\n if (allActions.has('service:stop')) {\n lines.push('### Stop service');\n lines.push(`\\`POST ${trpcBase}/process.stop\\``);\n lines.push('Body: `{\"agentId\":\"<agentId>\",\"processId\":\"<serviceId>\"}`');\n lines.push('');\n }\n\n if (allActions.has('service:restart')) {\n lines.push('### Restart service');\n lines.push(`\\`POST ${trpcBase}/process.restart\\``);\n lines.push('Body: `{\"agentId\":\"<agentId>\",\"processId\":\"<serviceId>\"}`');\n lines.push('');\n }\n\n if (allActions.has('service:status')) {\n lines.push('### Get service status');\n lines.push(`\\`GET ${trpcBase}/process.status?input={\"agentId\":\"<agentId>\",\"processId\":\"<serviceId>\"}\\``);\n lines.push('');\n }\n\n if (allActions.has('service:healthcheck')) {\n lines.push('### Get service health');\n lines.push(`\\`GET ${trpcBase}/process.health?input={\"agentId\":\"<agentId>\",\"processId\":\"<serviceId>\"}\\``);\n lines.push('');\n }\n\n if (hasEnvRead) {\n lines.push('### Read environment variables (flat KEY=VALUE — secrets shown as *****)');\n lines.push(`\\`GET ${trpcBase}/environment.resolveFlat?input={\"environmentId\":\"<environmentId>\"}\\``);\n lines.push('Returns a flat `Record<string, string>`. Secret variables you are not authorized to see are shown as `*****`.');\n lines.push('');\n }\n\n if (hasEnvWrite) {\n lines.push('### Write environment variable');\n lines.push(`\\`POST ${trpcBase}/environment.upsertVariable\\``);\n lines.push('Body: `{\"environmentId\":\"<environmentId>\",\"key\":\"<key>\",\"value\":\"<value>\"}`');\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n","import {\n RUNEYA_CAPABILITIES,\n buildAnthropicTools,\n buildOpenAITools,\n buildToolSafetyInstruction,\n callTrpc,\n} from '@runeya/packages-ai-capabilities';\nimport type { ApiKey, ApiServiceAction } from '@runeya/packages-shared';\nimport { env } from '../config/env.js';\n\n// ─── Tool definitions (generated from capabilities) ───────────────────────────\n\nexport const CHAT_TOOLS = buildAnthropicTools(RUNEYA_CAPABILITIES);\nexport const CHAT_TOOLS_OPENAI = buildOpenAITools(RUNEYA_CAPABILITIES);\nexport const TOOL_SAFETY_INSTRUCTION = buildToolSafetyInstruction(RUNEYA_CAPABILITIES);\n\n// ─── Scope check ──────────────────────────────────────────────────────────────\n\nfunction hasServiceAction(apiKey: ApiKey | null, serviceId: string, action: ApiServiceAction): boolean {\n if (!apiKey) return true; // JWT = full access\n const perm = apiKey.scopes.services.find(p => p.serviceId === serviceId);\n return perm?.actions.includes(action) ?? false;\n}\n\n// ─── Tool name normalization ─────────────────────────────────────────────────\n// Some models (Groq, Ollama, …) hallucinate suffixes like `<|channel|>commentary`\n// after the real tool name. Match against known names to recover gracefully.\n\nconst KNOWN_TOOL_NAMES = RUNEYA_CAPABILITIES.map(c => c.name);\n\nfunction normalizeToolName(raw: string): string {\n if (KNOWN_TOOL_NAMES.includes(raw)) return raw;\n const match = KNOWN_TOOL_NAMES.find(t => raw.startsWith(t));\n return match ?? raw;\n}\n\n// ─── Tool executor — proxies to local tRPC API ────────────────────────────────\n//\n// Cloud LLMs cannot reach localhost directly, so the server acts as a proxy:\n// cloud tool_call → executeTool() → HTTP → /api/trpc/...\n//\n// This keeps the execution path identical to what runners (ClaudeCode, Codex)\n// do when they call the API themselves.\n\nexport async function executeTool(\n rawName: string,\n input: Record<string, unknown>,\n apiKey: ApiKey | null,\n authToken: string,\n): Promise<unknown> {\n const name = normalizeToolName(rawName);\n const cap = RUNEYA_CAPABILITIES.find(c => c.name === name);\n if (!cap) throw new Error(`Unknown tool: ${name}`);\n\n // Scope check for service-level actions\n const serviceId = (input['serviceId'] ?? input['id'] ?? input['processId']) as string | undefined;\n if (serviceId) {\n const actionMap: Record<string, ApiServiceAction> = {\n get_service_status: 'service:status',\n get_logs: 'logs:read',\n start_service: 'service:start',\n stop_service: 'service:stop',\n restart_service: 'service:restart',\n };\n const action = actionMap[name];\n if (action && !hasServiceAction(apiKey, serviceId, action)) {\n throw new Error(`No '${action}' permission for service ${serviceId}`);\n }\n }\n\n const params = Object.fromEntries(\n Object.entries(input).map(([k, v]) => [\n k,\n v != null ? (typeof v === 'object' ? JSON.stringify(v) : String(v as string | number | boolean | bigint)) : undefined,\n ]),\n ) as Record<string, string | undefined>;\n\n const baseUrl = `http://localhost:${env.PORT}`;\n return callTrpc(baseUrl, cap.route, cap.method, cap.buildInput(params), authToken);\n}\n","import { z } from 'zod';\nimport { TRPCError } from '@trpc/server';\nimport { HealthCheckSchema, RestartStrategySchema } from '@runeya/packages-shared';\nimport type { ProcessStatus } from '@runeya/packages-shared';\nimport { router, protectedProcedure, requireServiceAction, getAllowedServiceIds } from '../trpc.js';\nimport type { AuthContext } from '../trpc.js';\nimport { serviceStore } from '../../services/service-store.js';\nimport { agentManager } from '../../services/agent-manager.js';\nimport { auditService } from '../../services/audit-service.js';\nimport { projectStore } from '../../services/project-store.js';\nimport { environmentResolver } from '../../services/environment-resolver.js';\nimport { environmentStore } from '../../services/environment-store.js';\nimport { logParserStore } from '../../services/log-parser-store.js';\nimport { nativeParsers } from '@runeya/packages-shared';\nimport { ServiceShortcutSchema, ServiceCommandSchema, DockerConfigSchema, LogSourceConfigSchema, AGENT_STOP_FETCH_TIMEOUT } from '@runeya/packages-shared';\nimport type { ServiceConfig } from '@runeya/packages-shared';\nimport { interpolateBuiltinsInEnv } from '@runeya/packages-shared';\nimport { buildServerBuiltins } from '../../services/service-builtins.js';\nimport { env } from '../../config/env.js';\nimport { isAbsolute } from 'node:path';\nimport { resolveWorkspaceCwd } from '../../services/workspace-cwd.js';\nimport { agentStore } from '../../services/agent-store.js';\n\n\n/**\n * Resolve the effective agentId for a service.\n * Priority: service.agentId (explicit override) → activeEnvironment.agentId →\n * l'agent local de ce poste.\n *\n * Le dernier recours n'est pas une commodité, c'est ce qui rend un projet\n * récupéré du cloud utilisable. Un service synchronisé porte l'agent de la\n * machine qui l'a publié — un identifiant qui ne désigne rien ici — et les\n * environnements, eux, ne voyagent pas : ils portent des secrets et restent\n * locaux. Le nouvel arrivant se retrouvait donc devant « aucun agent assigné »\n * sur chacun de ses services, sans rien avoir fait de travers.\n *\n * Un agent nommé mais inconnu de ce poste vaut donc absence : le garder\n * reviendrait à déployer vers un agent qui n'existe pas, pour une erreur plus\n * obscure encore.\n */\nexport async function resolveEffectiveAgentId(service: ServiceConfig): Promise<string | null> {\n // Les agents connectés, pas ceux persistés : l'agent local d'un poste est\n // enregistré « en mémoire seulement » (voir `agentManager.registerLocal`) et\n // n'apparaît donc jamais dans `agentStore`. S'y fier revenait à ignorer le\n // seul agent que la machine possède à coup sûr.\n const known = new Set([\n ...agentManager.listAgents().map((a) => a.config.id),\n ...(await agentStore.list()).map((a) => a.id),\n ]);\n if (service.agentId && known.has(service.agentId)) return service.agentId;\n\n const projects = await projectStore.list();\n const project = projects.find(p => p.serviceIds.includes(service.id));\n if (project?.activeEnvironmentId) {\n const activeEnv = await environmentStore.get(project.activeEnvironmentId);\n if (activeEnv?.agentId && known.has(activeEnv.agentId)) return activeEnv.agentId;\n }\n\n // L'agent local du poste, ou à défaut le premier connecté. Nommer un autre\n // agent reste un choix explicite, jamais une supposition.\n return agentManager.getDefaultAgentId();\n}\n\n/**\n * Helper to resolve service variables to env dict before deployment.\n * Finds the project containing this service and uses its activeEnvironmentId.\n * Also resolves parserIds to full parser definitions.\n */\nexport async function resolveServiceVariables(service: ServiceConfig): Promise<ServiceConfig> {\n // Find the project containing this service\n const projects = await projectStore.list();\n const project = projects.find(p => p.serviceIds.includes(service.id));\n\n let resolvedService: ServiceConfig = service;\n\n // Anchor a cloud project's services to where that project lives on this\n // machine. A cloud project is materialized from a document that holds no\n // path, so without this a relative cwd falls back to '.' — the directory\n // Runeya was launched from — and every cloud project opened from the same\n // place would run against whatever code happens to be there.\n // Only set when the project actually places the service somewhere: an\n // untouched `cwd` must keep reaching the agent as-is, since a relative one is\n // resolved against the agent's own directory — possibly on another machine.\n const anchored = await resolveWorkspaceCwd({ serviceId: service.id });\n if (anchored !== (service.cwd ?? '').trim() && isAbsolute(anchored)) {\n resolvedService = { ...resolvedService, resolvedCwd: anchored };\n }\n\n // Resolve parser IDs to full parser definitions (always, regardless of environment)\n try {\n if (service.parserIds && service.parserIds.length > 0) {\n // Dans le coffre du projet : un service d'organisation référence des\n // parseurs qui appartiennent à cette organisation. Les chercher au niveau\n // du poste rendrait une liste vide chez tout membre qui ne les a pas\n // écrits lui-même — et le service partirait à l'agent sans parseur, sans\n // que rien ne le signale.\n const vault = project?.orgId ?? undefined;\n const parsers = await logParserStore.list(vault);\n const allParsers = [...nativeParsers, ...parsers];\n const resolvedParsers = service.parserIds\n .map(id => allParsers.find(p => p.id === id))\n .filter((p) => p !== undefined);\n resolvedService = { ...resolvedService, resolvedLogParsers: resolvedParsers };\n }\n } catch (err) {\n console.error('[service-router] Failed to resolve parsers for service', service.id + ':', (err as Error).message);\n }\n\n if (!project || !project.activeEnvironmentId) {\n // No project or no active environment — nothing more to resolve\n return resolvedService;\n }\n\n // Resolve variables to env dict\n try {\n const resolved = await environmentResolver.resolveForService({\n serviceId: service.id,\n activeEnvironmentId: project.activeEnvironmentId,\n });\n\n // Interpolate the server-side builtins the environment resolver left alone.\n // The agent-side ones ({{agent.*}}, {{service.CWD}}) are deliberately NOT\n // resolved here: they belong to the machine that runs the service, not to\n // the one that deploys it — see `BUILTIN_VARIABLES`.\n const [environment, agentId, siblings] = await Promise.all([\n environmentStore.get(project.activeEnvironmentId),\n resolveEffectiveAgentId(service),\n Promise.all(project.serviceIds.filter((id) => id !== service.id).map((id) => serviceStore.get(id))),\n ]);\n const agent = agentId ? await agentStore.get(agentId) : null;\n\n const builtins = buildServerBuiltins({\n service,\n siblings: siblings.filter((sibling) => sibling !== null),\n projectName: project.name,\n environmentName: environment?.name,\n agentName: agent?.name,\n host: env.HOST,\n });\n\n resolvedService = {\n ...resolvedService,\n env: interpolateBuiltinsInEnv(resolved.env, builtins),\n envSecrets: [...resolved.envSecrets],\n };\n } catch (err) {\n console.error('[service-router] Failed to resolve variables for service', service.id + ':', (err as Error).message);\n }\n\n return resolvedService;\n}\n\n\nconst DockerConfigInput = DockerConfigSchema;\n\nexport const serviceRouter = router({\n /**\n * Sans filtre, la réponse couvre TOUT le poste : `serviceStore.list()` rend\n * chaque coffre, donc chaque organisation et chaque projet de la machine.\n * Deux portées permettent de s'y borner, de la plus étroite à la plus large :\n *\n * - `projectId` (+ `orgId` pour le désigner) : les services de ce projet.\n * - `orgId` seul : les services de cette organisation — `null` désigne le\n * coffre local, ce qui n'est pas la même chose que l'absence de filtre.\n * C'est la portée d'un écran qui doit proposer des services hors du projet\n * ouvert sans pour autant montrer ceux des autres organisations.\n *\n * Un projet introuvable rend une liste vide, jamais tout le poste : un repli\n * silencieux ferait croire au filtrage tout en rendant les services des\n * autres organisations.\n */\n list: protectedProcedure\n .meta({ openapi: { method: 'GET', path: '/services', tags: ['services'], summary: 'List services (every project of the machine unless narrowed by project or organization)', protect: true } })\n .input(z.object({\n projectId: z.string().max(128).optional(),\n orgId: z.string().max(128).nullable().optional(),\n }).optional())\n .output(z.any())\n .query(async ({ input, ctx }) => {\n const all = await serviceStore.list();\n const allowedIds = getAllowedServiceIds(ctx as AuthContext);\n let filtered = allowedIds ? all.filter(s => allowedIds.has(s.id)) : all;\n\n if (input?.projectId) {\n // `orgId` n'est transmis que s'il a été donné : `resolveKey` traite un\n // `null` explicite comme « le coffre local », ce qui rendrait introuvable\n // un projet appartenant à une organisation.\n const project = await projectStore.get({\n id: input.projectId,\n ...(input.orgId !== undefined ? { orgId: input.orgId } : {}),\n });\n const ids = new Set(project?.serviceIds ?? []);\n filtered = filtered.filter(s => ids.has(s.id));\n } else if (input && 'orgId' in input && input.orgId !== undefined) {\n // Portée organisation : un service porte son `orgId` en propre, et\n // l'absence d'organisation s'écrit `null` des deux côtés.\n const orgId = input.orgId ?? null;\n filtered = filtered.filter(s => (s.orgId ?? null) === orgId);\n }\n\n return Promise.all(filtered.map(async (s) => ({\n ...s,\n effectiveAgentId: await resolveEffectiveAgentId(s),\n })));\n }),\n\n get: protectedProcedure\n .meta({ openapi: { method: 'GET', path: '/services/{id}', tags: ['services'], summary: 'Get a service by ID', protect: true } })\n .input(z.object({ id: z.string().max(128) }))\n .output(z.any())\n .query(async ({ input, ctx }) => {\n requireServiceAction(ctx as AuthContext, input.id, 'service:read');\n const service = await serviceStore.get(input.id);\n if (!service) {\n throw new TRPCError({ code: 'NOT_FOUND', message: `Service ${input.id} not found` });\n }\n // Comme `list` : l'agent effectif est calculé, pas stocké. Sans lui, un\n // écran qui a chargé son service à l'unité — parce qu'il n'appartient pas\n // au projet ouvert — n'a plus de quoi le démarrer.\n return { ...service, effectiveAgentId: await resolveEffectiveAgentId(service) };\n }),\n\n create: protectedProcedure\n .meta({ openapi: { method: 'POST', path: '/services', tags: ['services'], summary: 'Create a new service', protect: true } })\n .output(z.any())\n .input(\n z.object({\n agentId: z.string().max(128).optional(),\n name: z.string().min(1).max(200),\n commands: z.array(ServiceCommandSchema).max(50).optional(),\n cwd: z.string().max(4096).optional(),\n ports: z.array(z.number().int().min(1).max(65535)).max(20).optional(),\n groups: z.array(z.string().max(200)).max(20).optional(),\n description: z.string().max(2000).optional(),\n docsPath: z.string().max(4096).optional(),\n url: z.string().max(2048).optional(),\n localUrls: z.array(z.object({\n url: z.string().max(253),\n port: z.number().int().min(1).max(65535),\n })).max(20).optional(),\n openapiUrl: z.string().max(2048).optional(),\n git: z.object({ remote: z.string().max(2048).optional(), home: z.string().max(2048).optional() }).optional(),\n meta: z.record(z.string().max(256), z.string().max(2000)).optional()\n .refine((obj) => !obj || Object.keys(obj).length <= 100, { message: 'Too many meta keys (max 100)' }),\n runner: z.enum(['native', 'docker']).optional().default('native'),\n dockerConfig: DockerConfigInput.optional(),\n healthCheck: HealthCheckSchema.optional(),\n autoRestart: z.boolean().optional().default(false),\n restartStrategy: RestartStrategySchema.optional(),\n maxRestarts: z.number().int().nonnegative().optional(),\n restartBackoffMs: z.number().int().positive().optional().default(1000),\n orgId: z.string().max(128).nullable().optional(),\n shortcuts: z.array(ServiceShortcutSchema).max(50).optional(),\n parserIds: z.array(z.string().max(128)).max(50).optional(),\n }),\n )\n .mutation(async ({ input }) => {\n // Validate agent exists (only if explicitly provided)\n if (input.agentId) {\n const status = agentManager.getStatus(input.agentId);\n if (!status) {\n throw new TRPCError({\n code: 'NOT_FOUND',\n message: `Agent ${input.agentId} not found`\n });\n }\n }\n\n if (input.runner === 'docker') {\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'Docker runner is not yet supported. Use \"native\" runner.' });\n }\n const service = await serviceStore.create({\n ...input,\n agentId: input.agentId,\n });\n await auditService.log({ userId: 'local', action: 'create', entityType: 'service', entityId: service.id, metadata: { name: input.name } });\n return service;\n }),\n\n update: protectedProcedure\n .meta({ openapi: { method: 'PATCH', path: '/services/{id}', tags: ['services'], summary: 'Update a service', protect: true } })\n .output(z.any())\n .input(\n z.object({\n id: z.string().max(128),\n agentId: z.string().max(128).nullable().optional(), // null = clear (inherit from env)\n name: z.string().min(1).max(200).optional(),\n commands: z.array(ServiceCommandSchema).max(50).optional(),\n cwd: z.string().max(4096).nullable().optional(),\n ports: z.array(z.number().int().min(1).max(65535)).max(20).optional(),\n groups: z.array(z.string().max(200)).max(20).optional(),\n description: z.string().max(2000).optional(),\n docsPath: z.string().max(4096).optional(),\n url: z.string().max(2048).optional(),\n localUrls: z.array(z.object({\n url: z.string().max(253),\n port: z.number().int().min(1).max(65535),\n })).max(20).optional(),\n openapiUrl: z.string().max(2048).optional(),\n git: z.object({ remote: z.string().max(2048).optional(), home: z.string().max(2048).optional() }).nullable().optional(),\n meta: z.record(z.string().max(256), z.string().max(2000)).nullable().optional()\n .refine((obj) => !obj || Object.keys(obj).length <= 100, { message: 'Too many meta keys (max 100)' }),\n runner: z.enum(['native', 'docker']).optional(),\n dockerConfig: DockerConfigInput.optional(),\n healthCheck: HealthCheckSchema.nullable().optional(),\n autoRestart: z.boolean().optional(),\n clearLogsOnStart: z.boolean().optional(),\n restartStrategy: RestartStrategySchema.optional(),\n maxRestarts: z.number().int().nonnegative().optional(),\n restartBackoffMs: z.number().int().positive().optional(),\n orgId: z.string().max(128).nullable().optional(),\n shortcuts: z.array(ServiceShortcutSchema).max(50).optional(),\n parserIds: z.array(z.string().max(128)).max(50).optional(),\n logSources: z.array(LogSourceConfigSchema).max(20).optional(),\n }),\n )\n .mutation(async ({ input }) => {\n const existing = await serviceStore.get(input.id);\n if (!existing) {\n throw new TRPCError({ code: 'NOT_FOUND', message: `Service ${input.id} not found` });\n }\n\n // If changing agentId (including clearing it), stop the service on the old agent if running\n let stoppedForAgentChange = false;\n if (input.agentId !== undefined && input.agentId !== existing.agentId && existing.agentId) {\n const oldAgentStatus = agentManager.getStatus(existing.agentId);\n if (oldAgentStatus?.connected) {\n try {\n const processes = await agentManager.agentQuery<ProcessStatus[]>(existing.agentId, 'process.list');\n const proc = processes.find((p) => p.serviceId === input.id);\n if (proc && (proc.state === 'running' || proc.state === 'starting')) {\n await agentManager.agentMutation(existing.agentId, 'process.stop', { id: input.id }, AGENT_STOP_FETCH_TIMEOUT);\n stoppedForAgentChange = true;\n }\n } catch {\n // Agent unreachable or stop failed — allow the config change anyway\n }\n }\n\n // Validate new agent exists (only when setting a non-null agentId)\n if (input.agentId) {\n const newAgentStatus = agentManager.getStatus(input.agentId);\n if (!newAgentStatus) {\n throw new TRPCError({\n code: 'NOT_FOUND',\n message: `Agent ${input.agentId} not found`\n });\n }\n }\n }\n\n const service = await serviceStore.update(input);\n if (!service) {\n throw new TRPCError({ code: 'NOT_FOUND', message: `Service ${input.id} not found` });\n }\n\n // If service was renamed, cascade to service-scoped environment names\n if (input.name && input.name !== existing.name) {\n await environmentStore.renameByReference(input.id, input.name);\n }\n\n // Propagate config to assigned agent (only if service has an explicit agentId)\n if (service.agentId) {\n const agentStatus = agentManager.getStatus(service.agentId);\n if (agentStatus?.connected) {\n try {\n const resolvedService = await resolveServiceVariables(service);\n await agentManager.agentMutation(service.agentId, 'process.updateConfig', {\n serviceId: service.id,\n config: resolvedService,\n });\n } catch (err) {\n console.debug('[service-router] Failed to propagate config to agent', service.agentId + ':', (err as Error).message);\n }\n }\n }\n\n await auditService.log({ userId: 'local', action: 'update', entityType: 'service', entityId: input.id });\n return { ...service, effectiveAgentId: await resolveEffectiveAgentId(service), stoppedForAgentChange };\n }),\n\n delete: protectedProcedure\n .meta({ openapi: { method: 'DELETE', path: '/services/{id}', tags: ['services'], summary: 'Delete a service', protect: true } })\n .input(z.object({ id: z.string().max(128) }))\n .output(z.object({ success: z.literal(true) }))\n .mutation(async ({ input }) => {\n const deleted = await serviceStore.delete(input.id);\n if (!deleted) {\n throw new TRPCError({ code: 'NOT_FOUND', message: `Service ${input.id} not found` });\n }\n await auditService.log({ userId: 'local', action: 'delete', entityType: 'service', entityId: input.id });\n return { success: true as const };\n }),\n\n checkCwd: protectedProcedure\n .input(z.object({ id: z.string().max(128), checkReachability: z.boolean().optional() }))\n .query(async ({ input }) => {\n const service = await serviceStore.get(input.id);\n if (!service) {\n throw new TRPCError({ code: 'NOT_FOUND', message: `Service ${input.id} not found` });\n }\n const effectiveAgentId = await resolveEffectiveAgentId(service);\n if (!effectiveAgentId) {\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'No agent configured for this service' });\n }\n\n // Delegate entirely to the agent — it resolves $VAR from its own process.env\n const agentResult = await agentManager.agentQuery<{\n exists: boolean;\n actualRemote: string | null;\n remoteMatches?: boolean;\n unreachable: boolean;\n resolvedCwd: string;\n unresolvedVars: string[];\n }>(effectiveAgentId, 'git.checkCwd', {\n // Anchored on the project, so the check answers about the directory the\n // service will actually run in.\n cwd: await resolveWorkspaceCwd({ serviceId: service.id }),\n remote: service.git?.remote,\n checkReachability: input.checkReachability,\n });\n\n const { exists, actualRemote, remoteMatches, unreachable, resolvedCwd, unresolvedVars } = agentResult;\n\n if (unresolvedVars.length > 0) {\n return { exists: false, canClone: false, unresolvedVars, resolvedCwd, gitMismatch: null };\n }\n\n const canClone = !!service.git?.remote && (!exists || actualRemote === null);\n\n let gitMismatch: { actual: string | null; configured: string; unreachable?: boolean } | null = null;\n if (exists && service.git?.remote && !canClone) {\n if (actualRemote === null) {\n gitMismatch = { actual: null, configured: service.git.remote };\n // `remoteMatches` absent : agent plus ancien, on retombe sur l'égalité stricte.\n } else if (remoteMatches === undefined ? actualRemote !== service.git.remote : !remoteMatches) {\n gitMismatch = { actual: actualRemote, configured: service.git.remote };\n } else if (unreachable) {\n gitMismatch = { actual: actualRemote, configured: service.git.remote, unreachable: true };\n }\n }\n\n return { exists, canClone, unresolvedVars: [], resolvedCwd, gitMismatch };\n }),\n\n cloneRepository: protectedProcedure\n .input(z.object({ id: z.string().max(128) }))\n .mutation(async ({ input }) => {\n const service = await serviceStore.get(input.id);\n if (!service) {\n throw new TRPCError({ code: 'NOT_FOUND', message: `Service ${input.id} not found` });\n }\n if (!service.git?.remote) {\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'Service has no git remote configured' });\n }\n const effectiveAgentId = await resolveEffectiveAgentId(service);\n if (!effectiveAgentId) {\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'No agent configured for this service' });\n }\n\n // Delegate entirely to the agent — it resolves $VAR from its own process.env\n await agentManager.agentMutation(effectiveAgentId, 'git.clone', {\n remote: service.git.remote,\n // Clones WRITE: landing this in the launch directory instead of the\n // project's is how a repository ends up in the wrong place.\n cwd: await resolveWorkspaceCwd({ serviceId: service.id }),\n }, 5 * 60 * 1000);\n\n return { success: true as const };\n }),\n});\n","import { appendFile, readFile, stat, rename, unlink, readdir, mkdir } from 'node:fs/promises';\nimport { machineFilePath, machineRoot } from './vault-resolver.js';\n\nexport interface AuditEntry {\n timestamp: string;\n userId: string;\n action: string;\n entityType: string;\n entityId: string;\n metadata?: Record<string, unknown>;\n}\n\nconst AUDIT_FILENAME = 'audit.log';\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\nconst MAX_AGE_DAYS = 30;\n\nclass AuditService {\n private getFilePath(): string {\n return machineFilePath(AUDIT_FILENAME);\n }\n\n private getRotatedPath(): string {\n return machineFilePath(`audit.log.${Date.now()}.bak`);\n }\n\n /** Append an audit entry (newline-delimited JSON) */\n async log(entry: Omit<AuditEntry, 'timestamp'>): Promise<void> {\n const record: AuditEntry = {\n ...entry,\n timestamp: new Date().toISOString(),\n };\n const line = JSON.stringify(record) + '\\n';\n const filepath = this.getFilePath();\n\n try {\n await mkdir(machineRoot(), { recursive: true });\n await appendFile(filepath, line, 'utf-8');\n await this.rotateIfNeeded();\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n console.error('[audit] Failed to write audit log:', message);\n }\n }\n\n /** Read audit entries, optionally filtered */\n async list(options?: {\n action?: string;\n entityType?: string;\n entityId?: string;\n limit?: number;\n offset?: number;\n }): Promise<{ entries: AuditEntry[]; total: number }> {\n const filepath = this.getFilePath();\n let lines: string[];\n try {\n const content = await readFile(filepath, 'utf-8');\n lines = content.trim().split('\\n').filter(Boolean);\n } catch {\n return { entries: [], total: 0 };\n }\n\n let entries: AuditEntry[] = lines\n .map((line) => {\n try { return JSON.parse(line) as AuditEntry; } catch { return null; }\n })\n .filter((e): e is AuditEntry => e !== null)\n .reverse(); // newest first\n\n // Apply filters\n if (options?.action) {\n entries = entries.filter((e) => e.action === options.action);\n }\n if (options?.entityType) {\n entries = entries.filter((e) => e.entityType === options.entityType);\n }\n if (options?.entityId) {\n entries = entries.filter((e) => e.entityId === options.entityId);\n }\n\n const total = entries.length;\n const offset = options?.offset ?? 0;\n const limit = options?.limit ?? 50;\n entries = entries.slice(offset, offset + limit);\n\n return { entries, total };\n }\n\n /** Rotate log file if it exceeds MAX_FILE_SIZE */\n private async rotateIfNeeded(): Promise<void> {\n const filepath = this.getFilePath();\n try {\n const stats = await stat(filepath);\n if (stats.size > MAX_FILE_SIZE) {\n await rename(filepath, this.getRotatedPath());\n }\n } catch {\n // File doesn't exist, nothing to rotate\n }\n }\n\n /** Clean up old rotated logs (older than MAX_AGE_DAYS) */\n async cleanup(): Promise<void> {\n const files = await readdir(machineRoot());\n const cutoff = Date.now() - MAX_AGE_DAYS * 24 * 60 * 60 * 1000;\n\n for (const file of files) {\n if (file.startsWith('audit.log.') && file.endsWith('.bak')) {\n const tsStr = file.replace('audit.log.', '').replace('.bak', '');\n const ts = Number.parseInt(tsStr, 10);\n if (!Number.isNaN(ts) && ts < cutoff) {\n try {\n await unlink(machineFilePath(file));\n } catch {\n // Ignore\n }\n }\n }\n }\n }\n}\n\nexport const auditService = new AuditService();\n","import { randomUUID } from 'node:crypto';\nimport { JSLogParserSchema, type JSLogParserDef } from '@runeya/packages-shared';\nimport { settingsManager } from './settings-manager.js';\n\n/**\n * Les parseurs de logs, rangés dans le coffre de ce qu'ils décrivent.\n *\n * Un parseur décrit le format des logs d'un service, et les services\n * appartiennent à des organisations : le parseur qui lit « nos logs Symfony »\n * décrit le travail d'une équipe, pas la machine d'une personne. Il vit donc\n * dans le coffre de l'organisation, d'où la synchronisation le porte aux autres\n * membres — sans quoi un service partagé arrive chez un collègue en référençant\n * (`ServiceConfig.parserIds`) un parseur qu'il n'a pas, et ses logs cessent\n * d'être lus sans que rien ne le dise.\n *\n * Le coffre sans organisation reste possible : une machine qui ne travaille\n * pour personne garde ses parseurs pour elle.\n *\n * Ce store existe pour une seconde raison : donner un point d'écoute. Les\n * réglages n'en avaient pas, et la synchronisation a besoin de savoir qu'un\n * parseur a changé pour le porter dans le document de l'organisation.\n */\n\ntype Listener = () => void;\n/** Reçoit le coffre touché : l'interface n'écoute que celui qu'elle affiche. */\ntype VaultListener = (vault: string | undefined) => void;\n\n/**\n * Un parseur natif ne se partage pas.\n *\n * Les natifs sont livrés avec l'application : chaque poste a déjà les siens,\n * dans la version de son binaire. En pousser une copie dans le document d'une\n * organisation en ferait un doublon qui vieillit — figé au jour où il est\n * parti, pendant que le natif, lui, suit les mises à jour — et il reviendrait\n * s'ajouter à la liste de tout le monde à côté de son original.\n *\n * Le garde porte sur les deux marques, parce qu'elles ne disent pas la même\n * chose : `builtin` est ce que le schéma expose, le préfixe d'identifiant est\n * ce que `nativeParsers` produit, et le routeur refuse déjà de toucher au\n * second. Un parseur qui porte l'une ou l'autre reste ici.\n */\nfunction isShareable(parser: { id: string; builtin?: boolean }): boolean {\n return parser.builtin !== true && !parser.id.startsWith('native:');\n}\n\n/** Champs qu'un appelant fournit ; l'id et les dates appartiennent au store. */\nexport type LogParserInput = Omit<JSLogParserDef, 'id' | 'createdAt' | 'updatedAt'>;\n\nclass LogParserStore {\n private readonly localListeners = new Set<Listener>();\n private readonly changeListeners = new Set<VaultListener>();\n\n /**\n * S'abonner aux écritures *locales* — celles nées ici.\n *\n * C'est ce qu'écoute la synchronisation : réagir aussi à ce qui arrive du\n * document la ferait repartir aussitôt vers ce document, en boucle.\n */\n onLocalChange(fn: Listener): () => void {\n this.localListeners.add(fn);\n return () => this.localListeners.delete(fn);\n }\n\n /**\n * S'abonner à toute écriture, d'où qu'elle vienne.\n *\n * C'est ce qu'écoute l'interface : un parseur écrit par un collègue arrive\n * par le document, donc silencieusement pour la synchronisation — mais un\n * écran qui ne l'apprend qu'au prochain montage montre une liste fausse\n * pendant tout ce temps, alors même que les services, eux, se rafraîchissent.\n */\n onChanged(fn: VaultListener): () => void {\n this.changeListeners.add(fn);\n return () => this.changeListeners.delete(fn);\n }\n\n private notify(vault: string | undefined, local: boolean): void {\n if (local) {\n for (const fn of this.localListeners) {\n try {\n fn();\n } catch (err) {\n console.error('[log-parser-store] listener failed:', (err as Error).message);\n }\n }\n }\n for (const fn of this.changeListeners) {\n try {\n fn(vault);\n } catch (err) {\n console.error('[log-parser-store] listener failed:', (err as Error).message);\n }\n }\n }\n\n /** Les parseurs d'un coffre — ceux de l'organisation, ou ceux du poste. */\n async list(vault?: string): Promise<JSLogParserDef[]> {\n const settings = await settingsManager.getSettings(vault);\n return settings.logParsers ?? [];\n }\n\n async get(id: string, vault?: string): Promise<JSLogParserDef | null> {\n return (await this.list(vault)).find((p) => p.id === id) ?? null;\n }\n\n private async write(parsers: JSLogParserDef[], vault?: string, silent = false): Promise<void> {\n const settings = await settingsManager.getSettings(vault);\n await settingsManager.saveSettings({ ...settings, logParsers: parsers }, vault);\n this.notify(vault, !silent);\n }\n\n async create(input: LogParserInput, vault?: string): Promise<JSLogParserDef> {\n const now = new Date();\n if (input.builtin) {\n throw new Error('A parser created here is never a built-in one');\n }\n const parser = JSLogParserSchema.parse({\n ...input,\n id: randomUUID(),\n createdAt: now,\n updatedAt: now,\n });\n await this.write([...(await this.list(vault)), parser], vault);\n return parser;\n }\n\n async update(\n id: string,\n patch: Partial<LogParserInput>,\n vault?: string,\n ): Promise<JSLogParserDef | null> {\n const parsers = await this.list(vault);\n const index = parsers.findIndex((p) => p.id === id);\n if (index === -1) return null;\n const updated = JSLogParserSchema.parse({\n ...parsers[index],\n ...patch,\n id,\n updatedAt: new Date(),\n });\n const next = [...parsers];\n next[index] = updated;\n await this.write(next, vault);\n return updated;\n }\n\n async delete(id: string, vault?: string): Promise<boolean> {\n const parsers = await this.list(vault);\n const next = parsers.filter((p) => p.id !== id);\n if (next.length === parsers.length) return false;\n await this.write(next, vault);\n return true;\n }\n\n // ---- Synchronisation --------------------------------------------------\n\n /**\n * Écrire ce que le document de l'organisation porte.\n *\n * Silencieux et gardé par une comparaison : réveiller les écoutants ici\n * relancerait la réconciliation qui vient de produire cette écriture, et un\n * document identique au disque ne mérite pas un fichier réécrit.\n */\n async applyRemoteUpsert(id: string, fields: unknown, orgId: string): Promise<void> {\n const parsed = JSLogParserSchema.safeParse({ ...(fields as object), id });\n if (!parsed.success) {\n console.warn('[log-parser-store] rejected a remote parser %s: %s', id, parsed.error.message);\n return;\n }\n // Un natif arrivé par le document vient d'une version qui ne gardait pas\n // encore la porte ; le laisser entrer doublerait la liste locale.\n if (!isShareable(parsed.data)) return;\n const parsers = await this.list(orgId);\n const index = parsers.findIndex((p) => p.id === id);\n if (index !== -1 && sameParser(parsers[index]!, parsed.data)) return;\n const next = [...parsers];\n if (index === -1) next.push(parsed.data);\n else next[index] = parsed.data;\n await this.write(next, orgId, true);\n }\n\n async applyRemoteDelete(id: string, orgId: string): Promise<void> {\n const parsers = await this.list(orgId);\n const next = parsers.filter((p) => p.id !== id);\n if (next.length === parsers.length) return;\n await this.write(next, orgId, true);\n }\n\n /**\n * Remonter les parseurs du poste dans les organisations de cette machine.\n *\n * Un déplacement, pas une copie : le même parseur des deux côtés ferait deux\n * vérités qui divergent au premier changement, et la liste du poste\n * ressusciterait à chaque connexion celui qu'un membre vient de supprimer\n * pour toute l'équipe. Une fois la machine vidée, il n'y a plus rien à\n * remonter — l'opération peut donc être rejouée sans dégât.\n *\n * Vers *toutes* les organisations liées, chacune recevant sa copie : les\n * coffres ne communiquent pas, et rien ne dit laquelle des deux était visée.\n */\n async migrateMachineParsersInto(orgIds: string[]): Promise<void> {\n if (orgIds.length === 0) return;\n const all = await this.list();\n if (all.length === 0) return;\n const machineParsers = all.filter(isShareable);\n\n for (const orgId of orgIds) {\n const existing = await this.list(orgId);\n const known = new Set(existing.map((p) => p.id));\n const added = machineParsers.filter((p) => !known.has(p.id));\n if (added.length === 0) continue;\n await this.write([...existing, ...added], orgId, true);\n }\n\n // Ce qui n'était pas partageable reste sur le poste : il n'a pas été\n // remonté, le vider l'effacerait.\n await this.write(all.filter((p) => !isShareable(p)), undefined, true);\n if (machineParsers.length > 0) {\n console.log(\n '[log-parser-store] moved %d machine parser(s) into organization(s): %s',\n machineParsers.length,\n orgIds.join(', '),\n );\n }\n }\n}\n\n/** Deux parseurs portent-ils la même chose ? Les dates comptent, l'ordre non. */\nfunction sameParser(a: JSLogParserDef, b: JSLogParserDef): boolean {\n return (\n a.name === b.name\n && a.code === b.code\n && a.enabled === b.enabled\n && (a.description ?? '') === (b.description ?? '')\n && a.updatedAt.getTime() === b.updatedAt.getTime()\n );\n}\n\nexport const logParserStore = new LogParserStore();\nexport { isShareable as isShareableParser };\n","import {\n ambiguousServiceSlugs,\n crossServiceValues,\n servicePortValues,\n serviceSlug,\n serviceUrlValues,\n urlHostFor,\n withDeprecatedAliases,\n} from '@runeya/packages-shared';\nimport type { ServiceConfig } from '@runeya/packages-shared';\n\nexport interface ServerBuiltinsInput {\n service: ServiceConfig;\n /** Les autres services du même projet, tels que chargés par l'appelant */\n siblings: ServiceConfig[];\n projectName?: string;\n /** Nom de l'environnement actif */\n environmentName?: string;\n /** Nom de l'agent qui exécutera le service */\n agentName?: string;\n /** Adresse d'écoute du serveur (`env.HOST`) */\n host: string;\n}\n\n/**\n * Les builtins que le serveur sait produire, au moment où il prépare la\n * configuration à déployer : ce qui vient de la configuration elle-même.\n *\n * Volontairement pur — aucun store, aucun accès disque — pour que la règle qui\n * compte ici (quel nom rend quelle valeur) se teste sans monter un serveur.\n * Les builtins de portée `agent` ne sont PAS produits ici : ils décrivent la\n * machine d'exécution, et le faire à sa place graverait celle qui déploie.\n */\nexport function buildServerBuiltins(input: ServerBuiltinsInput): Record<string, string> {\n const { service, siblings, projectName, environmentName, agentName, host } = input;\n\n // `runeya.HOST` reste l'adresse d'écoute telle quelle — c'est ce qu'un service\n // met dans son propre bind, `0.0.0.0` compris. Une URL, elle, doit être\n // joignable et lisible : la boucle locale s'y écrit `localhost`, et en mode\n // LAN on laisse une référence que l'agent résoudra en son IP locale, au\n // lancement, plutôt que d'écrire `http://0.0.0.0:3000`.\n const urlHost = urlHostFor(host) ?? '{{agent.HOST_LAN}}';\n\n const values: Record<string, string> = {\n 'service.NAME': service.name,\n 'service.ID': service.id,\n 'runeya.HOST': host,\n ...servicePortValues(service.ports),\n ...serviceUrlValues(service, urlHost),\n };\n\n // L'« URL de base » saisie par l'utilisateur : ni un port, ni un domaine\n // local — souvent l'adresse publique du service.\n if (service.url) values['service.BASE_URL'] = service.url;\n if (projectName !== undefined) values['project.NAME'] = projectName;\n if (environmentName !== undefined) values['runeya.ENV_NAME'] = environmentName;\n if (agentName !== undefined) values['agent.NAME'] = agentName;\n\n // Un slug ambigu — deux services du projet portant le même nom — n'est\n // exposé pour aucun des deux : rendre l'un d'eux arbitrairement serait pire\n // qu'une référence qui reste visible telle quelle dans la valeur.\n //\n // Le service courant est dans la table au même titre que ses voisins. Une\n // variable d'environnement globale est partagée par tout le projet et ne\n // connaît pas qui la lit : l'exclure de son propre slug faisait rendre à la\n // même variable une URL pour tous les services sauf un — celui qu'elle\n // nomme, qui recevait `{{services.<son-slug>.URL}}` en clair. Son ambiguïté\n // se compte donc avec les autres.\n const addressable = [service, ...siblings];\n const ambiguous = ambiguousServiceSlugs(addressable.map((svc) => svc.name));\n for (const svc of addressable) {\n const slug = serviceSlug(svc.name);\n if (!slug || ambiguous.has(slug)) continue;\n Object.assign(values, crossServiceValues({\n name: svc.name,\n id: svc.id,\n ports: svc.ports,\n localUrls: svc.localUrls,\n url: svc.url,\n }, urlHost));\n }\n\n return withDeprecatedAliases(values);\n}\n","import pty from '@lydell/node-pty';\nimport { execSync } from 'child_process';\nimport { resolveCliInvocation } from '@runeya/packages-shared/node';\n\nexport interface CliUsageEntry {\n label: string;\n percentage: number | null;\n next_reset: string | null;\n}\n\ninterface UsageEntry {\n percent: number | null;\n resetsAt: string | null;\n}\n\ninterface ExtraUsage extends UsageEntry {\n spent: number | null;\n limit: number | null;\n}\n\ninterface ClaudeUsage {\n current_session?: UsageEntry;\n current_week_all?: UsageEntry;\n current_week_sonnet?: UsageEntry;\n extra_usage?: ExtraUsage;\n}\n\nconst MONTHS: Record<string, number> = {\n jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5,\n jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11,\n};\n\nfunction parseResetsAt(str: string): string {\n const tzMatch = str.match(/\\(([^)]+)\\)/);\n const tz = tzMatch?.[1] ?? 'UTC';\n const dateStr = str.replace(/\\([^)]+\\)/, '').replaceAll('·', '').trim();\n\n const withTime = dateStr.match(/^([A-Za-z]{3})(\\d+),(\\d+)(am|pm)$/i);\n const withoutTime = dateStr.match(/^([A-Za-z]{3})(\\d+)$/i);\n // Time-only format: \"7:59am\" or \"10am\" — means today/tomorrow in the given tz\n const timeOnly = dateStr.match(/^(\\d+)(?::(\\d+))?(am|pm)$/i);\n\n let month: number, day: number, hour = 0, minute = 0; // minute used for timeOnly format\n\n const year = new Date().getFullYear();\n\n if (withTime) {\n month = MONTHS[withTime[1].toLowerCase()] ?? 0;\n day = Number.parseInt(withTime[2]);\n hour = Number.parseInt(withTime[3]);\n if (withTime[4].toLowerCase() === 'pm' && hour !== 12) hour += 12;\n if (withTime[4].toLowerCase() === 'am' && hour === 12) hour = 0;\n } else if (withoutTime) {\n month = MONTHS[withoutTime[1].toLowerCase()] ?? 0;\n day = Number.parseInt(withoutTime[2]);\n } else if (timeOnly) {\n hour = Number.parseInt(timeOnly[1]);\n minute = timeOnly[2] ? Number.parseInt(timeOnly[2]) : 0;\n if (timeOnly[3].toLowerCase() === 'pm' && hour !== 12) hour += 12;\n if (timeOnly[3].toLowerCase() === 'am' && hour === 12) hour = 0;\n // Use today's local date in the target timezone\n const now = new Date();\n const parts = new Intl.DateTimeFormat('en', { timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(now);\n month = Number.parseInt(parts.find(p => p.type === 'month')!.value) - 1;\n day = Number.parseInt(parts.find(p => p.type === 'day')!.value);\n } else {\n return str;\n }\n const approxUtc = new Date(Date.UTC(year, month, day, hour, 0));\n const offsetStr = new Intl.DateTimeFormat('en', {\n timeZone: tz,\n timeZoneName: 'shortOffset',\n }).formatToParts(approxUtc).find(p => p.type === 'timeZoneName')?.value ?? 'GMT+0';\n\n const offsetMatch = offsetStr.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\n const sign = offsetMatch?.[1] === '-' ? -1 : 1;\n const offsetMinutes = sign * (Number.parseInt(offsetMatch?.[2] ?? '0') * 60 + Number.parseInt(offsetMatch?.[3] ?? '0'));\n\n const utc = new Date(Date.UTC(year, month, day, hour, minute) - offsetMinutes * 60000);\n return utc.toISOString();\n}\n\nfunction extractSection(text: string, startPattern: RegExp, endPattern: RegExp | null): string | null {\n const start = text.search(startPattern);\n if (start === -1) return null;\n const after = text.slice(start);\n const end = endPattern ? after.search(endPattern) : after.length;\n return end === -1 ? after : after.slice(0, end);\n}\n\nfunction extractPercent(section: string | null): number | null {\n const m = section?.match(/(\\d+)%\\s*used/i);\n return m ? Number.parseInt(m[1]) : null;\n}\n\nfunction extractResets(section: string | null): string | null {\n const m = section?.match(/Rese[ts]+\\s*(.+?)(?=Current|Extra|$)/is);\n return m ? m[1].replaceAll(/\\s+/g, ' ').trim() : null;\n}\n\nfunction parse(text: string): ClaudeUsage {\n // Flatten to single line for cross-line regex matching (handles Windows ConPTY output)\n const flat = text.replaceAll('\\n', ' ').replaceAll(/\\s+/g, ' ');\n const result: ClaudeUsage = {};\n\n const sessionSec = extractSection(flat, /Current\\s*session/i, /Current\\s*week|Extra\\s*usage/i);\n if (sessionSec) {\n const resets = extractResets(sessionSec);\n result.current_session = {\n percent: extractPercent(sessionSec),\n resetsAt: resets ? parseResetsAt(resets) : null,\n };\n }\n\n const weekAllSec = extractSection(flat, /Current\\s*week\\s*\\(all\\s*models?\\)/i, /Current\\s*week\\s*\\(Sonnet/i);\n if (weekAllSec) {\n const resets = extractResets(weekAllSec);\n result.current_week_all = {\n percent: extractPercent(weekAllSec),\n resetsAt: resets ? parseResetsAt(resets) : null,\n };\n }\n\n const weekSonnetSec = extractSection(flat, /Current\\s*week\\s*\\(Sonnet\\s*only\\)/i, /Extra\\s*usage/i);\n if (weekSonnetSec) {\n const resets = extractResets(weekSonnetSec);\n result.current_week_sonnet = {\n percent: extractPercent(weekSonnetSec),\n resetsAt: resets ? parseResetsAt(resets) : null,\n };\n }\n\n const extraSec = extractSection(flat, /Extra\\s*usage(?!\\s*not)/i, null);\n if (extraSec && !/not\\s*enabled/i.test(extraSec.slice(0, 40))) {\n const spent = extraSec.match(/\\$([\\d.]+)\\s*\\/\\s*\\$([\\d.]+)\\s*spent/i);\n const resets = extractResets(extraSec);\n result.extra_usage = {\n percent: extractPercent(extraSec),\n spent: spent ? Number.parseFloat(spent[1]) : null,\n limit: spent ? Number.parseFloat(spent[2]) : null,\n resetsAt: resets ? parseResetsAt(resets) : null,\n };\n }\n\n return result;\n}\n\nfunction getClaudeUsage(): Promise<ClaudeUsage> {\n return new Promise((resolve, reject) => {\n const env = { ...process.env };\n delete env['CLAUDECODE'];\n\n // Le chemin absolu résolu par `which` reste ce qu'on passait ici sur POSIX ;\n // sur Windows, `resolveCliInvocation` prend le relais — node-pty ne sait pas\n // ouvrir un shim `.cmd`, et `where` y est déjà fait par le helper.\n let claudeBin = 'claude';\n if (process.platform !== 'win32') {\n try {\n claudeBin = execSync('which claude', { encoding: 'utf8' }).trim().split('\\n')[0].trim() || 'claude';\n } catch { /* fallback to 'claude' */ }\n }\n const invocation = resolveCliInvocation(claudeBin, ['/usage']);\n\n let term: ReturnType<typeof pty.spawn> | null = null;\n try {\n term = pty.spawn(invocation.command, invocation.args, {\n name: 'xterm-256color',\n cols: 200,\n rows: 40,\n env,\n });\n } catch (err) {\n reject(err);\n return;\n }\n\n let buffer = '';\n let done = false;\n let debounce: ReturnType<typeof setTimeout> | null = null;\n\n function finish() {\n if (done) return;\n done = true;\n if (debounce) clearTimeout(debounce);\n clearTimeout(hardTimeout);\n term?.kill();\n const clean = buffer\n .replaceAll(/\\x1b\\[[0-9;]*X/g, '\\n') // ECH separators on Windows ConPTY\n .replaceAll(/\\x1b\\[[0-9;]*[mGKHFABCDJsu]/g, '')\n .replaceAll(/\\x1b[^a-zA-Z]*[a-zA-Z]/g, '')\n .replaceAll('\\r', '');\n resolve(parse(clean));\n }\n\n const hardTimeout = setTimeout(() => {\n if (!done) finish();\n }, 15_000);\n\n term.onData((data: string) => {\n buffer += data;\n // Start debounce only after actual usage data has arrived\n if (buffer.includes('%')) {\n if (debounce) clearTimeout(debounce);\n debounce = setTimeout(finish, 800);\n }\n });\n\n term.onExit(() => {\n finish();\n });\n });\n}\n\nexport async function getClaudeUsageEntries(): Promise<CliUsageEntry[]> {\n const usage = await getClaudeUsage();\n const entries: CliUsageEntry[] = [];\n\n if (usage.current_session) {\n entries.push({\n label: 'Session',\n percentage: usage.current_session.percent,\n next_reset: usage.current_session.resetsAt,\n });\n }\n\n if (usage.current_week_all) {\n entries.push({\n label: 'Week',\n percentage: usage.current_week_all.percent,\n next_reset: usage.current_week_all.resetsAt,\n });\n }\n\n if (usage.current_week_sonnet) {\n entries.push({\n label: 'Sonnet',\n percentage: usage.current_week_sonnet.percent,\n next_reset: usage.current_week_sonnet.resetsAt,\n });\n }\n\n if (usage.extra_usage) {\n entries.push({\n label: 'Extra',\n percentage: usage.extra_usage.percent,\n next_reset: usage.extra_usage.resetsAt,\n });\n }\n\n return entries;\n}\n","import pty from '@lydell/node-pty';\nimport { resolveCliInvocation } from '@runeya/packages-shared/node';\nimport type { CliUsageEntry } from './claude-usage.js';\n\nconst MONTHS: Record<string, number> = {\n jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5,\n jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11,\n};\n\nfunction parseResetToIso(raw: string): string | null {\n const value = raw\n .replace(/^\\(*/, '')\n .replace(/\\)*$/, '')\n .replace(/^resets?\\s*/i, '')\n .trim();\n\n const now = new Date();\n\n const timeOnly = value.match(/^(\\d{1,2}):(\\d{2})$/);\n if (timeOnly) {\n const hour = Number.parseInt(timeOnly[1], 10);\n const minute = Number.parseInt(timeOnly[2], 10);\n const date = new Date(now);\n date.setHours(hour, minute, 0, 0);\n if (date.getTime() <= now.getTime()) {\n date.setDate(date.getDate() + 1);\n }\n return date.toISOString();\n }\n\n const withDate = value.match(/^(\\d{1,2}):(\\d{2})\\s+on\\s+(\\d{1,2})\\s+([A-Za-z]{3})$/i);\n if (withDate) {\n const hour = Number.parseInt(withDate[1], 10);\n const minute = Number.parseInt(withDate[2], 10);\n const day = Number.parseInt(withDate[3], 10);\n const month = MONTHS[withDate[4].toLowerCase()];\n if (month === undefined) return null;\n\n let year = now.getFullYear();\n let date = new Date(year, month, day, hour, minute, 0, 0);\n if (date.getTime() <= now.getTime()) {\n year += 1;\n date = new Date(year, month, day, hour, minute, 0, 0);\n }\n return date.toISOString();\n }\n\n return null;\n}\n\nfunction stripAnsi(text: string): string {\n return text\n .replace(/[\\u001b\\u009b][[\\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '')\n .replaceAll('\\r', '');\n}\n\nexport function parseCodexStatusText(text: string): CliUsageEntry[] {\n const clean = stripAnsi(text);\n const normalizedLines = clean\n .replace(/[│╭╰╮╯─]/g, ' ')\n .split('\\n')\n .map((line) => line.replace(/\\s+/g, ' ').trim())\n .filter(Boolean);\n\n const entries: CliUsageEntry[] = [];\n let pendingIndex: number | null = null;\n\n for (const line of normalizedLines) {\n const sameLine = line.match(/^(.+?limit)\\s*:\\s*(?:\\[[^\\]]*\\]\\s*)?(\\d+)%\\s*left(?:\\s*\\((?:resets?\\s+)?([^)]+)\\))?$/i);\n if (sameLine) {\n const label = sameLine[1].trim();\n const left = Number.parseInt(sameLine[2], 10);\n const used = Number.isNaN(left) ? null : Math.max(0, Math.min(100, 100 - left));\n const nextReset = sameLine[3] ? (parseResetToIso(sameLine[3]) ?? null) : null;\n entries.push({ label, percentage: used, next_reset: nextReset });\n pendingIndex = nextReset ? null : entries.length - 1;\n continue;\n }\n\n const splitReset = line.match(/^\\(?\\s*resets?\\s+([^)]+)\\)?$/i);\n if (splitReset && pendingIndex !== null) {\n entries[pendingIndex].next_reset = parseResetToIso(splitReset[1]) ?? null;\n pendingIndex = null;\n }\n }\n\n if (entries.length === 0) {\n // Fallback: parse statusline format \"· X% left\" emitted by codex bottom bar\n // e.g. \"gpt-5.4 default · 43% left · ~/Projects/runeya-rust\"\n const statuslineMatch = clean.match(/·\\s*(\\d+)%\\s*left/i);\n if (statuslineMatch) {\n const left = Number.parseInt(statuslineMatch[1], 10);\n const used = Number.isNaN(left) ? null : Math.max(0, Math.min(100, 100 - left));\n entries.push({ label: 'Usage', percentage: used, next_reset: null });\n }\n }\n\n return entries;\n}\n\nexport function getCodexUsageEntries(): Promise<CliUsageEntry[]> {\n return new Promise((resolve, reject) => {\n let term: ReturnType<typeof pty.spawn> | null = null;\n try {\n const invocation = resolveCliInvocation('codex', []);\n term = pty.spawn(invocation.command, invocation.args, {\n name: 'xterm-256color',\n cols: 220,\n rows: 40,\n env: process.env,\n });\n } catch (err) {\n reject(err);\n return;\n }\n\n let buffer = '';\n let done = false;\n const queuedInputs = ['/status', '/status', '/status', '/exit'];\n let queuedIndex = 0;\n let inputDrainDebounce: ReturnType<typeof setTimeout> | null = null;\n let promptSeen = false;\n\n const finish = () => {\n if (done) return;\n done = true;\n if (inputDrainDebounce) clearTimeout(inputDrainDebounce);\n clearTimeout(initialTrigger);\n clearTimeout(hardTimeout);\n term?.kill();\n if (process.env['CODEX_USAGE_DEBUG'] === '1') {\n const debugClean = stripAnsi(buffer).slice(-6000);\n process.stdout.write('[codex-usage] raw tail:\\n' + debugClean + '\\n');\n }\n resolve(parseCodexStatusText(buffer));\n };\n\n const hardTimeout = setTimeout(() => {\n finish();\n }, 25_000);\n\n const sendNextInputIfReady = () => {\n if (done) return;\n if (!promptSeen) return;\n if (queuedIndex >= queuedInputs.length) return;\n const value = queuedInputs[queuedIndex];\n queuedIndex += 1;\n promptSeen = false;\n term?.write(`${value}\\r`);\n };\n\n const initialTrigger = setTimeout(() => {\n promptSeen = true;\n sendNextInputIfReady();\n }, 6_000);\n\n term.onData((data: string) => {\n buffer += data;\n if (data.includes('\\x1b[6n')) {\n // Answer DSR (cursor position report), matching pty-runner behavior.\n term?.write('\\x1b[25;1R');\n }\n\n if (data.includes('›')) {\n promptSeen = true;\n }\n\n if (inputDrainDebounce) clearTimeout(inputDrainDebounce);\n inputDrainDebounce = setTimeout(sendNextInputIfReady, 120);\n\n if (/(\\d+)\\s*h\\s+limit\\s*:/i.test(buffer) && /weekly\\s+limit\\s*:/i.test(buffer)) {\n setTimeout(finish, 800);\n }\n\n // Codex hasn't loaded limits yet — no point waiting the full 25s\n if (/data not available yet/i.test(buffer) && queuedIndex >= queuedInputs.length - 1) {\n setTimeout(finish, 400);\n }\n });\n\n term.onExit(() => {\n clearTimeout(initialTrigger);\n finish();\n });\n });\n}\n","import { settingsManager } from './settings-manager.js';\nimport { ScenarioSchema } from '@runeya/packages-shared';\nimport type { Scenario, ScenarioNode } from '@runeya/packages-shared';\n\nfunction normalizeNode(raw: ScenarioNode & { parentId?: string | null }): ScenarioNode {\n const aiCli = raw.aiCli ?? 'claude-code';\n const defaultModel = aiCli === 'codex' ? '' : 'sonnet';\n // Migrate legacy parentId → parentIds\n let parentIds = raw.parentIds ?? [];\n if (raw.parentId && !parentIds.includes(raw.parentId)) {\n parentIds = [raw.parentId, ...parentIds];\n }\n return {\n ...raw,\n parentIds,\n groupId: raw.groupId ?? null,\n label: raw.label?.trim() ?? '',\n prompt: raw.prompt ?? '',\n autoGenerated: raw.autoGenerated ?? true,\n canAddNode: raw.canAddNode ?? false,\n aiCli,\n model: raw.model?.trim() || defaultModel,\n };\n}\n\n/**\n * `vault` désigne l'organisation dont on lit les scénarios — ils décrivent ses\n * services, donc ils lui appartiennent. Absent : ceux du poste, pour un projet\n * local qui n'est l'affaire de personne d'autre.\n */\nclass ScenarioStore {\n async list(vault?: string): Promise<Scenario[]> {\n const [settings, priv] = await Promise.all([\n settingsManager.getSettings(vault),\n settingsManager.getPrivateSettings(vault),\n ]);\n return [...(settings.scenarios ?? []), ...(priv.scenarios ?? [])];\n }\n\n async get(id: string, vault?: string): Promise<Scenario | null> {\n const list = await this.list(vault);\n return list.find((e) => e.id === id) ?? null;\n }\n\n async create(scenario: Scenario, vault?: string): Promise<Scenario> {\n const normalized = this.normalizeScenario(scenario);\n const parsed = ScenarioSchema.parse(normalized);\n this.validateHierarchy(parsed);\n\n if (parsed.isPublished) {\n const settings = await settingsManager.getSettings(vault);\n await settingsManager.saveSettings({ ...settings, scenarios: [...(settings.scenarios ?? []), parsed] }, vault);\n } else {\n const priv = await settingsManager.getPrivateSettings(vault);\n await settingsManager.savePrivateSettings({ ...priv, scenarios: [...(priv.scenarios ?? []), parsed] }, vault);\n }\n\n return parsed;\n }\n\n async update(id: string, patch: Partial<Scenario>, vault?: string): Promise<Scenario | null> {\n const [settings, priv] = await Promise.all([\n settingsManager.getSettings(vault),\n settingsManager.getPrivateSettings(vault),\n ]);\n\n const publicScenarios = [...(settings.scenarios ?? [])];\n const privateScenarios = [...(priv.scenarios ?? [])];\n\n const publicIdx = publicScenarios.findIndex((e) => e.id === id);\n const privateIdx = privateScenarios.findIndex((e) => e.id === id);\n\n const existing = publicIdx !== -1 ? publicScenarios[publicIdx] : privateIdx !== -1 ? privateScenarios[privateIdx] : null;\n if (!existing) return null;\n\n const merged = { ...existing, ...patch, id, updatedAt: Date.now() } as Scenario;\n const normalized = this.normalizeScenario(merged);\n const parsed = ScenarioSchema.parse(normalized);\n this.validateHierarchy(parsed);\n\n const wasPublic = publicIdx !== -1;\n const isNowPublic = parsed.isPublished;\n\n if (wasPublic && isNowPublic) {\n publicScenarios[publicIdx] = parsed;\n await settingsManager.saveSettings({ ...settings, scenarios: publicScenarios }, vault);\n } else if (!wasPublic && !isNowPublic) {\n privateScenarios[privateIdx] = parsed;\n await settingsManager.savePrivateSettings({ ...priv, scenarios: privateScenarios }, vault);\n } else if (wasPublic && !isNowPublic) {\n // moved public → private\n publicScenarios.splice(publicIdx, 1);\n await Promise.all([\n settingsManager.saveSettings({ ...settings, scenarios: publicScenarios }, vault),\n settingsManager.savePrivateSettings({ ...priv, scenarios: [...privateScenarios, parsed] }, vault),\n ]);\n } else {\n // moved private → public\n privateScenarios.splice(privateIdx, 1);\n await Promise.all([\n settingsManager.savePrivateSettings({ ...priv, scenarios: privateScenarios }, vault),\n settingsManager.saveSettings({ ...settings, scenarios: [...publicScenarios, parsed] }, vault),\n ]);\n }\n\n return parsed;\n }\n\n async delete(id: string, vault?: string): Promise<boolean> {\n const [settings, priv] = await Promise.all([\n settingsManager.getSettings(vault),\n settingsManager.getPrivateSettings(vault),\n ]);\n\n const publicFiltered = (settings.scenarios ?? []).filter((e) => e.id !== id);\n const privateFiltered = (priv.scenarios ?? []).filter((e) => e.id !== id);\n\n const removedFromPublic = publicFiltered.length < (settings.scenarios ?? []).length;\n const removedFromPrivate = privateFiltered.length < (priv.scenarios ?? []).length;\n\n if (!removedFromPublic && !removedFromPrivate) return false;\n\n await Promise.all([\n removedFromPublic ? settingsManager.saveSettings({ ...settings, scenarios: publicFiltered }, vault) : Promise.resolve(),\n removedFromPrivate ? settingsManager.savePrivateSettings({ ...priv, scenarios: privateFiltered }, vault) : Promise.resolve(),\n ]);\n\n return true;\n }\n\n private normalizeScenario(scenario: Scenario): Scenario {\n const nodes = scenario.nodes.map((p) => normalizeNode(p));\n let leaderId = scenario.leaderId;\n if (!nodes.some((p) => p.id === leaderId)) {\n leaderId = nodes[0]?.id ?? leaderId;\n }\n\n return {\n ...scenario,\n leaderId,\n groups: scenario.groups ?? [],\n nodes,\n };\n }\n\n private validateHierarchy(scenario: Scenario): void {\n const groupIds = new Set<string>();\n for (const group of scenario.groups) {\n if (groupIds.has(group.id)) {\n throw new Error(`Duplicate group id: ${group.id}`);\n }\n groupIds.add(group.id);\n }\n\n const nodeIds = new Set<string>();\n for (const node of scenario.nodes) {\n if (nodeIds.has(node.id)) {\n throw new Error(`Duplicate node id: ${node.id}`);\n }\n nodeIds.add(node.id);\n }\n\n const nodesById = new Map(scenario.nodes.map((p) => [p.id, p]));\n if (!nodesById.has(scenario.leaderId)) {\n throw new Error('Leader must be a valid node in the scenario');\n }\n\n for (const node of scenario.nodes) {\n for (const pid of (node.parentIds ?? [])) {\n if (!nodesById.has(pid)) {\n throw new Error(`Node ${node.id} references unknown parent ${pid}`);\n }\n if (node.id === pid) {\n throw new Error(`Node ${node.id} cannot be their own parent`);\n }\n }\n if (node.groupId && !groupIds.has(node.groupId)) {\n throw new Error(`Node ${node.id} references unknown group ${node.groupId}`);\n }\n }\n\n const visited = new Set<string>();\n const inPath = new Set<string>();\n\n const walk = (nodeId: string): void => {\n if (inPath.has(nodeId)) throw new Error('Scenario hierarchy contains a cycle');\n if (visited.has(nodeId)) return;\n inPath.add(nodeId);\n const node = nodesById.get(nodeId);\n for (const pid of (node?.parentIds ?? [])) walk(pid);\n inPath.delete(nodeId);\n visited.add(nodeId);\n };\n\n for (const p of scenario.nodes) walk(p.id);\n }\n}\n\nexport const scenarioStore = new ScenarioStore();\n","import { mkdir, rm, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Scenario } from '@runeya/packages-shared';\nimport {\n atomicWriteJsonSecure,\n atomicWriteSecure,\n readJsonFileSafe,\n resolveConversationRunDir,\n} from './run-store-utils.js';\n\nfunction sanitizeSlug(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60) || 'member';\n}\n\nfunction sanitizeFilePart(value: string, fallback: string): string {\n const sanitized = value\n .toLowerCase()\n .replace(/[^a-z0-9_-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80);\n return sanitized || fallback;\n}\n\nexport type NodeState = 'idle' | 'running' | 'waiting' | 'waiting_input' | 'done' | 'synthesized' | 'error';\n\nexport interface RunState {\n nodeStates: Record<string, NodeState>;\n nodeOutputs: Record<string, string>;\n /** Claude Code session ID per node — used by the debug PTY drawer (--resume). */\n nodeSessionIds: Record<string, string>;\n /** Agent that ran each node (needed to spawn the resume PTY on the right host). */\n nodeAgentIds: Record<string, string>;\n activeNodeId?: string;\n waitingInputs: Record<string, string>;\n}\n\nexport interface PersistedRunState {\n nodeStates: Record<string, NodeState>;\n nodeOutputs: Record<string, string>;\n nodeSessionIds?: Record<string, string>;\n nodeAgentIds?: Record<string, string>;\n waitingInputs?: Record<string, string>;\n}\n\nclass ScenarioRunStore {\n private static readonly SCENARIO_SNAPSHOT_FILE = 'SCENARIO.json';\n private activeRuns = new Map<string, RunState>();\n\n private getRunDir(conversationId: string): string {\n return resolveConversationRunDir('scenario-runs', conversationId);\n }\n\n async initializeConversationWorkspace(conversationId: string, scenario: Scenario, userInput: string): Promise<void> {\n const dir = this.getRunDir(conversationId);\n await mkdir(dir, { recursive: true });\n await this.saveScenarioSnapshot(conversationId, scenario);\n\n const nodesById = new Map(scenario.nodes.map((p) => [p.id, p]));\n\n const teamMap = new Map(scenario.groups.map((t) => [t.id, t.name]));\n const leader = nodesById.get(scenario.leaderId) ?? scenario.nodes[0];\n\n const overview = [\n `# Scenario Conversation Workspace`,\n '',\n `- Scenario: ${scenario.name}`,\n `- Conversation ID: ${conversationId}`,\n `- Leader: ${leader?.label ?? ''}`,\n '',\n '## Members',\n ...scenario.nodes.map((p) => {\n const parent = p.parentIds[0] ? nodesById.get(p.parentIds[0]) : null;\n const group = p.groupId ? (teamMap.get(p.groupId) ?? p.groupId) : 'No group';\n return `- ${p.label} (${p.id}) | Group: ${group} | Reports to: ${parent ? parent.label : 'None'}`;\n }),\n '',\n '## Rules',\n '- Every node writes in their own markdown file.',\n '- Responses should be sent back to the requester.',\n '- Use read-only references from linked upstream files as context.',\n '',\n ].join('\\n');\n\n await atomicWriteSecure(join(dir, 'README.md'), overview);\n await atomicWriteSecure(join(dir, 'USER_REQUEST.md'), `# User request\\n\\n${userInput.trim() || '(empty)'}\\n`);\n\n for (const node of scenario.nodes) {\n const nodeName = node.label.trim();\n const safeNodeId = sanitizeFilePart(node.id, 'node');\n const fileName = `${sanitizeSlug(nodeName || node.id)}_${safeNodeId}.md`;\n const parent = node.parentIds[0] ? nodesById.get(node.parentIds[0]) : null;\n const reports = scenario.nodes.filter((p) => p.parentIds[0] === node.id);\n const teamName = node.groupId ? (teamMap.get(node.groupId) ?? node.groupId) : 'No group';\n const content = [\n `# ${nodeName || node.id}`,\n '',\n `- ID: ${node.id}`,\n `- Group: ${teamName}`,\n `- Reports to: ${parent ? parent.label : 'None'}`,\n `- Manages: ${reports.length ? reports.map((r) => r.label).join(', ') : 'No direct reports'}`,\n '',\n '## Prompt',\n node.prompt || '(No prompt defined)',\n '',\n '## Work Log',\n '- Waiting for assignment.',\n '',\n ].join('\\n');\n await atomicWriteSecure(join(dir, fileName), content);\n }\n }\n\n async deleteRunDir(conversationId: string): Promise<void> {\n await rm(this.getRunDir(conversationId), { recursive: true, force: true });\n this.activeRuns.delete(conversationId);\n }\n\n private ensureRunState(conversationId: string): RunState {\n let runState = this.activeRuns.get(conversationId);\n if (!runState) {\n runState = { nodeStates: {}, nodeOutputs: {}, nodeSessionIds: {}, nodeAgentIds: {}, waitingInputs: {} };\n this.activeRuns.set(conversationId, runState);\n }\n return runState;\n }\n\n setNodeSession(conversationId: string, nodeId: string, sessionId: string, agentId: string): void {\n const rs = this.ensureRunState(conversationId);\n rs.nodeSessionIds[nodeId] = sessionId;\n rs.nodeAgentIds[nodeId] = agentId;\n }\n\n getNodeSession(conversationId: string, nodeId: string): { sessionId: string; agentId: string } | null {\n const rs = this.activeRuns.get(conversationId);\n const sessionId = rs?.nodeSessionIds[nodeId];\n const agentId = rs?.nodeAgentIds[nodeId];\n if (sessionId && agentId) return { sessionId, agentId };\n return null;\n }\n\n async getNodeSessionPersistent(conversationId: string, nodeId: string): Promise<{ sessionId: string; agentId: string } | null> {\n const live = this.getNodeSession(conversationId, nodeId);\n if (live) return live;\n const persisted = await this.loadPersistedRunState(conversationId);\n const sessionId = persisted?.nodeSessionIds?.[nodeId];\n const agentId = persisted?.nodeAgentIds?.[nodeId];\n if (sessionId && agentId) return { sessionId, agentId };\n return null;\n }\n\n setNodeState(conversationId: string, nodeId: string, state: NodeState): void {\n this.ensureRunState(conversationId).nodeStates[nodeId] = state;\n }\n\n setNodeOutput(conversationId: string, nodeId: string, output: string): void {\n this.ensureRunState(conversationId).nodeOutputs[nodeId] = output;\n }\n\n setActiveNodeId(conversationId: string, nodeId?: string): void {\n this.ensureRunState(conversationId).activeNodeId = nodeId;\n }\n\n setWaitingInput(conversationId: string, nodeId: string, question: string): void {\n this.ensureRunState(conversationId).waitingInputs[nodeId] = question;\n }\n\n clearWaitingInput(conversationId: string, nodeId: string): void {\n const runState = this.activeRuns.get(conversationId);\n if (runState?.waitingInputs) delete runState.waitingInputs[nodeId];\n }\n\n getRunState(conversationId: string): RunState | null {\n return this.activeRuns.get(conversationId) ?? null;\n }\n\n async persistRunState(conversationId: string): Promise<void> {\n const runState = this.activeRuns.get(conversationId);\n if (!runState) return;\n try {\n const dir = this.getRunDir(conversationId);\n await mkdir(dir, { recursive: true });\n const data: PersistedRunState = {\n nodeStates: { ...runState.nodeStates },\n nodeOutputs: { ...runState.nodeOutputs },\n ...(Object.keys(runState.nodeSessionIds).length > 0 ? { nodeSessionIds: { ...runState.nodeSessionIds } } : {}),\n ...(Object.keys(runState.nodeAgentIds).length > 0 ? { nodeAgentIds: { ...runState.nodeAgentIds } } : {}),\n ...(Object.keys(runState.waitingInputs).length > 0 ? { waitingInputs: { ...runState.waitingInputs } } : {}),\n };\n await atomicWriteJsonSecure(join(dir, 'run-state.json'), data);\n } catch {\n // Non-fatal\n }\n }\n\n async loadPersistedRunState(conversationId: string): Promise<PersistedRunState | null> {\n const dir = this.getRunDir(conversationId);\n return readJsonFileSafe<PersistedRunState>(join(dir, 'run-state.json'));\n }\n\n async readUserRequest(conversationId: string): Promise<string | null> {\n try {\n const dir = this.getRunDir(conversationId);\n const content = await readFile(join(dir, 'USER_REQUEST.md'), 'utf-8');\n return content.replace(/^#\\s*User request\\s*\\n+/, '').trim() || null;\n } catch {\n return null;\n }\n }\n\n async readReadme(conversationId: string): Promise<string | null> {\n try {\n const dir = this.getRunDir(conversationId);\n return await readFile(join(dir, 'README.md'), 'utf-8');\n } catch {\n return null;\n }\n }\n\n async saveScenarioSnapshot(conversationId: string, scenario: Scenario): Promise<void> {\n const dir = this.getRunDir(conversationId);\n await mkdir(dir, { recursive: true });\n await atomicWriteJsonSecure(\n join(dir, ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),\n scenario,\n true,\n );\n }\n\n async loadScenarioSnapshot(conversationId: string): Promise<Scenario | null> {\n const dir = this.getRunDir(conversationId);\n return readJsonFileSafe<Scenario>(join(dir, ScenarioRunStore.SCENARIO_SNAPSHOT_FILE));\n }\n}\n\nexport const scenarioRunStore = new ScenarioRunStore();\n","import { chmod, mkdir, rename, writeFile, readFile } from 'node:fs/promises';\nimport { dirname, resolve, sep } from 'node:path';\nimport { machineRoot } from './vault-resolver.js';\n\nconst SAFE_PATH_SEGMENT_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;\n\nexport function resolveConversationRunDir(rootDirName: string, conversationId: string): string {\n if (!SAFE_PATH_SEGMENT_RE.test(conversationId)) {\n throw new Error('Invalid conversationId');\n }\n // Machine-wide, like the conversations these runs belong to: using the launch\n // directory would put a `.runeya` in a repository just for asking the AI a\n // question about a cloud project.\n const base = resolve(machineRoot(), rootDirName);\n const dir = resolve(base, conversationId);\n if (!dir.startsWith(base + sep)) {\n throw new Error('Invalid conversationId');\n }\n return dir;\n}\n\nexport async function atomicWriteSecure(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp`;\n await writeFile(tmpPath, content, 'utf-8');\n await rename(tmpPath, filePath);\n await chmod(filePath, 0o600);\n}\n\nexport async function atomicWriteJsonSecure(filePath: string, data: unknown, pretty = false): Promise<void> {\n await atomicWriteSecure(filePath, JSON.stringify(data, null, pretty ? 2 : 0));\n}\n\nexport async function readJsonFileSafe<T>(filePath: string): Promise<T | null> {\n try {\n const raw = await readFile(filePath, 'utf-8');\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n}\n","import Anthropic from '@anthropic-ai/sdk';\nimport OpenAI from 'openai';\nimport type { AIProvider } from '@runeya/packages-shared';\nimport { settingsManager } from './settings-manager.js';\nimport { decryptValue } from './crypto-helpers.js';\n\nexport const OPENAI_COMPAT_BASE_URLS: Record<string, string> = {\n openai: 'https://api.openai.com/v1',\n groq: 'https://api.groq.com/openai/v1',\n mistral: 'https://api.mistral.ai/v1',\n ollama: 'http://localhost:11434/v1',\n};\n\nexport const DEFAULT_MODELS: Record<string, string> = {\n anthropic: 'claude-opus-4-6',\n openai: 'gpt-4o',\n groq: 'llama-3.3-70b-versatile',\n mistral: 'mistral-large-latest',\n ollama: 'llama3',\n};\n\n/**\n * Le fournisseur actif de l'organisation concernée.\n *\n * `vault` est son identifiant, ou l'absence pour les réglages du poste — un\n * projet local n'appartenant à personne d'autre. La clé d'API est déchiffrée\n * par la clé de ce coffre : c'est ce qui permet à une équipe de partager le\n * fournisseur qu'elle paie sans partager la clé de chacune de ses machines.\n */\nexport async function getActiveProvider(\n providerId?: string,\n vault?: string,\n): Promise<{ provider: AIProvider; plaintextKey: string } | null> {\n const priv = await settingsManager.getPrivateSettings(vault);\n const providers = priv.aiProviders ?? [];\n const selected = providerId\n ? providers.find((p: AIProvider) => p.id === providerId)\n : providers.find((p: AIProvider) => p.active);\n if (!selected) return null;\n const plaintextKey = selected.encryptedApiKey ? decryptValue(selected.encryptedApiKey, vault) : '';\n return { provider: selected, plaintextKey };\n}\n\nexport async function fetchProviderModels(provider: AIProvider, plaintextKey: string): Promise<string[]> {\n if (provider.provider === 'anthropic') {\n const client = new Anthropic({ apiKey: plaintextKey });\n const res = await client.models.list();\n return res.data.map(m => m.id).sort((a, b) => a.localeCompare(b));\n }\n\n if (provider.provider in OPENAI_COMPAT_BASE_URLS) {\n const baseURL = OPENAI_COMPAT_BASE_URLS[provider.provider]!;\n const client = new OpenAI({ apiKey: plaintextKey || 'ollama', baseURL });\n const res = await client.models.list();\n return res.data.map(m => m.id).sort((a, b) => a.localeCompare(b));\n }\n\n return [];\n}\n","import { ensureProjectsLoaded, refsOfProjectId } from './vault-registry.js';\n\n/**\n * À quelle organisation appartiennent les réglages qui s'appliquent ici.\n *\n * Les fournisseurs d'IA, les scénarios et les préférences de notification\n * décrivent le travail d'une organisation, pas le poste : il faut donc, à\n * chaque lecture, savoir de laquelle on parle. La réponse ne vient jamais d'un\n * « projet courant » global — il n'en existe pas, et un état partagé de ce\n * genre se désynchronise dès que deux requêtes se croisent — mais du projet que\n * l'action concerne, qui voyage déjà dans les paramètres de chaque appel.\n *\n * `undefined` désigne le poste : un projet local n'appartient à aucune\n * organisation, et ses réglages restent là où personne d'autre ne les lit.\n */\nexport async function vaultOfProject(projectId?: string | null): Promise<string | undefined> {\n if (!projectId) return undefined;\n await ensureProjectsLoaded();\n const refs = refsOfProjectId(projectId);\n // Le même id peut répondre pour un projet dans plusieurs organisations. Sans\n // quoi trancher, on ne choisit pas : mieux vaut lire les réglages du poste\n // que ceux d'une organisation prise au hasard.\n if (refs.length !== 1) return undefined;\n return refs[0]?.orgId ?? undefined;\n}\n","import { randomUUID } from 'node:crypto';\nimport { mkdir, writeFile, readFile, unlink, readdir } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join, extname, resolve, sep } from 'node:path';\nimport { machineRoot } from './vault-resolver.js';\n\nexport const ALLOWED_MIME_TYPES = new Set([\n 'image/png',\n 'image/jpeg',\n 'image/webp',\n 'image/gif',\n]);\n\nconst ALLOWED_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);\nexport const MAX_IMAGE_SIZE_BYTES = 5 * 1024 * 1024; // 5 MB\nexport const IMAGE_TTL_MS = 24 * 60 * 60 * 1000; // 24h\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport interface StoredImage {\n imageId: string;\n filePath: string;\n mimeType: string;\n originalName: string;\n createdAt: number;\n /** Optional tag for grouping (e.g. conversationId, kanban cardId) */\n contextId?: string;\n}\n\nexport class ImageStorageService {\n private dir: string;\n\n constructor(dir: string) {\n this.dir = dir;\n }\n\n async ensureTmpDir(): Promise<void> {\n await mkdir(this.dir, { recursive: true, mode: 0o700 });\n }\n\n async saveImage(buffer: Buffer, originalName: string, mimeType: string, contextId?: string): Promise<StoredImage> {\n if (!ALLOWED_MIME_TYPES.has(mimeType)) {\n throw new Error(`Unsupported MIME type: ${mimeType}. Allowed: ${[...ALLOWED_MIME_TYPES].join(', ')}`);\n }\n if (buffer.length > MAX_IMAGE_SIZE_BYTES) {\n throw new Error(`Image too large: ${buffer.length} bytes (max ${MAX_IMAGE_SIZE_BYTES})`);\n }\n if (!checkMagicBytes(buffer, mimeType)) {\n throw new Error('File content does not match declared MIME type');\n }\n\n const ext = extname(originalName).toLowerCase() || mimeToExt(mimeType);\n if (!ALLOWED_EXTENSIONS.has(ext)) {\n throw new Error(`Unsupported extension: ${ext}`);\n }\n\n await this.ensureTmpDir();\n\n const imageId = randomUUID();\n const safeExt = ext.replace(/[^a-z.]/g, '');\n const fileName = `${imageId}${safeExt}`;\n const filePath = join(this.dir, fileName);\n const resolvedPath = resolve(filePath);\n const resolvedDir = resolve(this.dir);\n if (resolvedPath !== resolvedDir && !resolvedPath.startsWith(resolvedDir + sep)) {\n throw new Error('Invalid file path');\n }\n\n await writeFile(filePath, buffer, { mode: 0o600 });\n const meta: StoredImage = { imageId, filePath, mimeType, originalName, createdAt: Date.now(), contextId };\n await writeFile(`${filePath}.meta.json`, JSON.stringify(meta), { encoding: 'utf-8', mode: 0o600 });\n return meta;\n }\n\n /**\n * Resolve an image on disk.\n *\n * `expectedContextId` scopes the lookup: when given, an image stored under a\n * different context (or under none at all) is reported as missing. Callers\n * that serve an image through a context-scoped URL MUST pass it — the id\n * alone is not an authorization, and without the check any imageId could be\n * fetched under any conversation.\n */\n async getImagePath(\n imageId: string,\n expectedContextId?: string,\n ): Promise<{ filePath: string; mimeType: string; contextId?: string } | null> {\n if (!UUID_RE.test(imageId)) return null;\n try {\n const files = await readdir(this.dir);\n const metaFile = files.find(f => f.startsWith(imageId) && f.endsWith('.meta.json'));\n if (!metaFile) return null;\n const raw = JSON.parse(await readFile(join(this.dir, metaFile), 'utf-8')) as StoredImage;\n if (expectedContextId !== undefined && raw.contextId !== expectedContextId) return null;\n if (!existsSync(raw.filePath)) return null;\n return { filePath: raw.filePath, mimeType: raw.mimeType, contextId: raw.contextId };\n } catch {\n return null;\n }\n }\n\n async deleteImage(imageId: string): Promise<void> {\n const result = await this.getImagePath(imageId);\n if (!result) return;\n await unlink(result.filePath).catch(() => {});\n await unlink(`${result.filePath}.meta.json`).catch(() => {});\n }\n\n async deleteByContextId(contextId: string): Promise<void> {\n let files: string[];\n try {\n files = await readdir(this.dir);\n } catch {\n return;\n }\n await Promise.all(\n files\n .filter(f => f.endsWith('.meta.json'))\n .map(async (file) => {\n try {\n const raw = JSON.parse(await readFile(join(this.dir, file), 'utf-8')) as StoredImage;\n if (raw.contextId === contextId) {\n await unlink(raw.filePath).catch(() => {});\n await unlink(`${raw.filePath}.meta.json`).catch(() => {});\n }\n } catch {\n // skip malformed\n }\n }),\n );\n }\n\n async readImageAsBase64(imageId: string, expectedContextId?: string): Promise<{ base64: string; mimeType: string } | null> {\n const result = await this.getImagePath(imageId, expectedContextId);\n if (!result) return null;\n const buffer = await readFile(result.filePath);\n return { base64: buffer.toString('base64'), mimeType: result.mimeType };\n }\n\n async listExpiredImages(ttlMs = IMAGE_TTL_MS): Promise<StoredImage[]> {\n const expired: StoredImage[] = [];\n let files: string[];\n try {\n files = await readdir(this.dir);\n } catch {\n return [];\n }\n const now = Date.now();\n for (const file of files) {\n if (!file.endsWith('.meta.json')) continue;\n try {\n const raw = JSON.parse(await readFile(join(this.dir, file), 'utf-8')) as StoredImage;\n if (now - raw.createdAt > ttlMs) expired.push(raw);\n } catch {\n // skip malformed\n }\n }\n return expired;\n }\n}\n\nlet _singleton: ImageStorageService | undefined;\nfunction getSingleton(): ImageStorageService {\n if (!_singleton) {\n // Images pasted into a chat belong to the machine, not to a project: the\n // conversations they illustrate are machine-wide, and a cloud-only session\n // must not grow a `.runeya` in the directory it was started from.\n _singleton = new ImageStorageService(resolve(machineRoot(), 'images'));\n }\n return _singleton;\n}\nexport const imageStorageService = new Proxy({} as ImageStorageService, {\n get(_target, prop: string | symbol) {\n const svc = getSingleton();\n const val = (svc as unknown as Record<string | symbol, unknown>)[prop];\n return typeof val === 'function' ? (val as (...args: unknown[]) => unknown).bind(svc) : val;\n },\n});\n\nfunction checkMagicBytes(buf: Buffer, mime: string): boolean {\n if (buf.length < 12) return false;\n switch (mime) {\n case 'image/png':\n return buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4E && buf[3] === 0x47;\n case 'image/jpeg':\n return buf[0] === 0xFF && buf[1] === 0xD8 && buf[2] === 0xFF;\n case 'image/gif':\n return buf.slice(0, 6).toString('ascii') === 'GIF87a' || buf.slice(0, 6).toString('ascii') === 'GIF89a';\n case 'image/webp':\n return buf.slice(0, 4).toString('ascii') === 'RIFF' && buf.slice(8, 12).toString('ascii') === 'WEBP';\n default:\n return false;\n }\n}\n\nfunction mimeToExt(mime: string): string {\n const map: Record<string, string> = {\n 'image/png': '.png',\n 'image/jpeg': '.jpg',\n 'image/webp': '.webp',\n 'image/gif': '.gif',\n };\n return map[mime] ?? '.bin';\n}\n","import { imageStorageService } from './image-storage.service.js';\n\nexport interface MessageImageRef {\n imageId: string;\n mimeType: string;\n altText?: string;\n}\n\n/**\n * Extracts all image parts from a message's parts array.\n * Parts come from the stored conversation messages.\n */\nexport function extractImagesFromMessage(parts: Array<{ type: string; [key: string]: unknown }>): MessageImageRef[] {\n const images: MessageImageRef[] = [];\n for (const part of parts) {\n if (\n part['type'] === 'image' &&\n typeof part['imageId'] === 'string' &&\n typeof part['mimeType'] === 'string'\n ) {\n images.push({\n imageId: part['imageId'] as string,\n mimeType: part['mimeType'] as string,\n altText: typeof part['altText'] === 'string' ? part['altText'] : undefined,\n });\n }\n }\n return images;\n}\n\n/**\n * Resolves a single image ref to base64, returning null if not found.\n *\n * `contextId` scopes the lookup to the conversation the image was uploaded\n * under; ids that belong elsewhere resolve to null.\n */\nexport async function resolveImageToBase64(\n imageId: string,\n contextId?: string,\n): Promise<{ base64: string; mimeType: string } | null> {\n return imageStorageService.readImageAsBase64(imageId, contextId);\n}\n\n/**\n * Resolves all image refs to base64, skipping missing ones (with a warning).\n *\n * `contextId` scopes the lookup — see `resolveImageToBase64`.\n */\nexport async function resolveImagesToBase64(\n images: MessageImageRef[],\n contextId?: string,\n): Promise<Array<{ imageId: string; base64: string; mimeType: string; altText?: string }>> {\n const results = await Promise.all(\n images.map(async (img) => {\n const resolved = await imageStorageService.readImageAsBase64(img.imageId, contextId);\n if (!resolved) {\n console.warn('[message-images] Image not found (imageId redacted from log)');\n return null;\n }\n return { imageId: img.imageId, base64: resolved.base64, mimeType: resolved.mimeType, altText: img.altText };\n }),\n );\n return results.filter((r): r is NonNullable<typeof r> => r !== null);\n}\n","import { z } from 'zod';\nimport { hostname } from 'node:os';\nimport { basename } from 'node:path';\nimport { TRPCError } from '@trpc/server';\nimport { router, protectedProcedure } from '../trpc.js';\nimport { cloudSessionStore, type CloudAccount } from '../../services/cloud-session-store.js';\nimport { env } from '../../config/env.js';\nimport { lanIps } from '../../services/lan-mode.js';\nimport {\n getCloudApiUrl,\n getCloudAppUrl,\n cloudFetch as callCloud,\n CloudNotLinkedError,\n accountForOrg,\n accountForOrgOrThrow,\n soleAccountOrThrow,\n CloudAmbiguousAccountError,\n readCloudIdentity,\n type CloudCallScope,\n} from '../../services/cloud-api.js';\n\n// Le bridge de sync en a besoin aussi, et un service ne doit pas importer un\n// routeur : l'implémentation vit dans `services/cloud-api.ts`, réexportée ici\n// pour tout ce qui appelait déjà depuis les routeurs.\nexport { getCloudApiUrl };\n\n/**\n * The label this machine carries in the cloud's \"linked machines\" screen.\n *\n * The hostname alone is ambiguous once a host runs several instances — which is\n * the whole point of separate data dirs — so a non-default data dir is appended.\n * `RUNEYA_DEVICE_NAME` overrides both.\n */\nexport function getDeviceName(): string {\n if (env.DEVICE_NAME) return env.DEVICE_NAME.slice(0, 64);\n const host = hostname() || 'runeya';\n const dir = basename(env.DATA_DIR);\n const suffix = dir && dir !== '.runeya' ? ` · ${dir}` : '';\n return `${host}${suffix}`.slice(0, 64);\n}\n\n/**\n * Où le navigateur a le droit de revenir — et donc où poser le callback.\n *\n * `returnTo` vient de l'interface : sans liste close, cette route deviendrait\n * une redirection ouverte. La boucle locale est toujours acceptée.\n *\n * En mode LAN, l'onglet qui pilote Runeya est sur une IP de cette machine, pas\n * sur `localhost` : refuser cette origine revenait à exposer toute l'app au\n * réseau sauf l'écran de connexion. On accepte donc aussi les adresses des\n * interfaces de CETTE machine, sur le port du serveur — la même règle que le\n * CSRF applique déjà. La liste reste calculée ici, jamais déduite de ce que le\n * client annonce.\n *\n * Le callback suit l'origine retenue : figé sur `127.0.0.1`, il renvoyait la\n * tablette vers sa propre boucle locale, où il n'y a rien. RFC 8252 §7.3\n * n'ignore le port que pour les IP littérales, jamais pour le nom « localhost » —\n * d'où le port explicite dans les deux cas.\n */\nexport function callbackForReturnTo(url: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return null;\n\n const { hostname, port } = parsed;\n if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]') {\n return `http://127.0.0.1:${env.PORT}/api/cloud/callback`;\n }\n if (!env.RUNEYA_LAN_PROXY) return null;\n\n const effectivePort = port || (parsed.protocol === 'https:' ? '443' : '80');\n if (effectivePort !== String(env.PORT)) return null;\n if (!lanIps().includes(hostname)) return null;\n return `${parsed.origin}/api/cloud/callback`;\n}\n\n/**\n * Calls the cloud API on behalf of this machine's stored session token.\n *\n * This is the only place a cloud call is authenticated: the token never leaves\n * the server, so the browser holds no cloud credential at all.\n */\nexport async function cloudFetch(\n path: string,\n init: { method?: string; body?: unknown; timeoutMs?: number } & CloudCallScope,\n): Promise<Response> {\n try {\n return await callCloud(path, init);\n } catch (err) {\n if (!(err instanceof CloudNotLinkedError)) throw err;\n // Deliberately NOT UNAUTHORIZED: tRPC would turn that into a 401, which the\n // web client reads as \"the local session died\" — it would then drop a\n // perfectly valid JWT and bounce the user back to the unlock page. Having no\n // cloud link is a state of this machine, not a verdict on the caller.\n throw new TRPCError({ code: 'PRECONDITION_FAILED', message: err.message });\n }\n}\n\n/**\n * Le compte d'une organisation, avec la traduction d'erreur de `cloudFetch`.\n *\n * Les routeurs qui ne connaissent que l'organisation résolvent le compte ici,\n * et non par l'appel brut : ne rien avoir à quoi la rattacher est un état de\n * cette machine, et doit se lire comme tel jusque dans l'interface — pas comme\n * une erreur interne parce que la résolution est sortie du `try`.\n */\nexport async function accountForOrgScope(orgId: string): Promise<string> {\n try {\n return await accountForOrgOrThrow(orgId);\n } catch (err) {\n if (!(err instanceof CloudNotLinkedError)) throw err;\n throw new TRPCError({ code: 'PRECONDITION_FAILED', message: err.message });\n }\n}\n\n/**\n * L'unique compte du poste, traduit comme `cloudFetch` traduit ses erreurs.\n *\n * Pour le seul appel qui ne peut pas nommer d'organisation. Avec plusieurs\n * comptes, l'utilisateur reçoit une demande de précision, pas un « non lié ».\n */\nexport async function soleAccountScope(): Promise<string> {\n try {\n return await soleAccountOrThrow();\n } catch (err) {\n if (err instanceof CloudAmbiguousAccountError) {\n throw new TRPCError({ code: 'BAD_REQUEST', message: err.message });\n }\n if (!(err instanceof CloudNotLinkedError)) throw err;\n throw new TRPCError({ code: 'PRECONDITION_FAILED', message: err.message });\n }\n}\n\n/**\n * Cloud endpoints the web UI may reach through this proxy.\n *\n * An allowlist, not a passthrough: the proxy speaks with this machine's cloud\n * token, so an unconstrained `path` would let any caller drive the whole cloud\n * API (including sign-out or account changes) with our credential.\n */\nconst PROXY_PREFIXES = ['/organization/', '/project/', '/chat/', '/kanban/', '/user/prefs'];\n\nfunction isAllowedProxyPath(path: string): boolean {\n if (!path.startsWith('/') || path.includes('..')) return false;\n const [pathname] = path.split('?');\n return PROXY_PREFIXES.some((p) => pathname === p || pathname.startsWith(p));\n}\n\n\n/**\n * De quel compte parle un appel de l'interface.\n *\n * `accountId`, et lui seul. L'interface le connaît : elle suit le projet\n * ouvert, dont l'organisation désigne le compte, et elle garde cette réponse\n * (`cloud-session.ts`). Elle envoyait auparavant l'organisation et laissait le\n * serveur choisir — mais rien ne l'obligeait à envoyer quoi que ce soit, et les\n * composables partagées n'envoyaient rien du tout.\n */\nconst ScopeSchema = z.object({\n accountId: z.string().min(1).max(64),\n});\n\n/** La portée telle que `cloudFetch` l'attend. */\nfunction scopeOf(input: { accountId: string }): CloudCallScope {\n return { accountId: input.accountId };\n}\n\n/** One row of the \"linked machines\" list, as the cloud returns it. */\nexport interface CloudDevice {\n id: string;\n name: string;\n kind: 'device' | 'browser';\n current: boolean;\n createdAt: string;\n lastSeenAt: string;\n expiresAt: string;\n ipAddress: string | null;\n}\n\n/**\n * Cloud account link for this machine.\n *\n * The browser authorizes on the cloud origin (its session cookie is same-site\n * there, so it just works), and hands back a single-use token that this server\n * exchanges for a session token. Nothing cloud-related is ever authenticated\n * from the browser, which is why the cloud can refuse localhost origins.\n */\nexport const cloudAuthRouter = router({\n /** Build the cloud authorization URL to open in the browser. */\n startLogin: protectedProcedure\n .input(z.object({ returnTo: z.string().url().max(2048) }))\n .mutation(async ({ input }) => {\n const callback = callbackForReturnTo(input.returnTo);\n if (!callback) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: env.RUNEYA_LAN_PROXY\n ? 'returnTo must be this machine, on the server port.'\n : 'returnTo must be a local origin.',\n });\n }\n const state = cloudSessionStore.createState(input.returnTo);\n const url = `${getCloudAppUrl()}/link-local`\n + `?callback=${encodeURIComponent(callback)}`\n + `&state=${encodeURIComponent(state)}`;\n return { url };\n }),\n\n /**\n * Whether this machine holds any cloud session, and against which cloud.\n *\n * `cloudUrl` n'a de sens qu'avec un seul compte : au-delà, il n'y a plus « le »\n * cloud du poste, et c'est `accounts` qu'il faut lire.\n */\n status: protectedProcedure.query(async () => {\n const accounts = await cloudSessionStore.list();\n return {\n linked: accounts.length > 0,\n cloudUrl: accounts.length === 1 ? accounts[0]!.cloudUrl : null,\n accountCount: accounts.length,\n };\n }),\n\n\n /**\n * Le compte dont relève une organisation.\n *\n * La question que l'interface pose à chaque changement de projet : le projet\n * ouvert donne son organisation, l'organisation donne son compte. Répondue\n * ici plutôt que déduite d'une carte tenue dans le navigateur — une carte se\n * périme dès qu'un coffre est tamponné, et l'interface restait alors sur le\n * compte précédent sans que rien ne le dise.\n *\n * `null` quand rien ne rattache cette organisation à un compte de ce poste :\n * l'interface l'affiche comme déconnectée, ce qu'elle est.\n */\n accountForOrg: protectedProcedure\n .input(z.object({ orgId: z.string().min(1).max(128) }))\n .query(async ({ input }) => ({ accountId: await accountForOrg(input.orgId) })),\n /**\n * Les comptes liés à ce poste, et l'organisation dont chacun répond.\n *\n * Aucun token n'en sort : de quoi peupler la page Compte et afficher, à côté\n * du projet ouvert, sous quel compte il se synchronise.\n */\n accounts: protectedProcedure.query(async (): Promise<{\n accounts: CloudAccount[];\n orgAccounts: Record<string, string>;\n }> => {\n const { readOrgAccounts } = await import('../../services/vault-resolver.js');\n return { accounts: await cloudSessionStore.list(), orgAccounts: await readOrgAccounts() };\n }),\n\n /** The cloud session (user + org), fetched with the stored token. */\n session: protectedProcedure.input(ScopeSchema).query(async ({ input }) => {\n const { accountId } = input;\n if (!(await cloudSessionStore.isAccountLinked(accountId))) return null;\n const res = await cloudFetch('/api/auth/get-session', { accountId });\n if (!res.ok) {\n // 401 means the session backing this account is gone — revoked from\n // another machine, or expired. Forget that one, so the UI offers a fresh\n // link instead of a link that silently fails on every call. Les autres\n // comptes ne sont pas concernés : un seul token vient d'être refusé.\n if (res.status === 401) await cloudSessionStore.remove(accountId);\n return null;\n }\n const payload = (await res.json()) as unknown;\n // Un compte lié avant le multicompte n'a pas d'identité : c'est ici qu'elle\n // arrive, et c'est ici qu'il prend son id définitif.\n const account = await cloudSessionStore.get(accountId);\n if (account && !account.userId) {\n const identity = await readCloudIdentity(account.cloudUrl, (await cloudSessionStore.getToken(accountId)) ?? '');\n if (identity) {\n const settled = await cloudSessionStore.describe(accountId, identity);\n // Les coffres tamponnés avec l'identifiant provisoire doivent suivre :\n // sinon ils désignent un compte qui vient de cesser d'exister.\n const { renameOrgAccount } = await import('../../services/vault-resolver.js');\n await renameOrgAccount(accountId, settled);\n }\n }\n return payload;\n }),\n\n /**\n * Every session of the cloud account: the machines linked to it and the\n * browsers signed into it. No token material — the cloud only ever returns\n * ids and labels here.\n */\n devices: protectedProcedure.input(ScopeSchema).query(async ({ input }) => {\n const { accountId } = input;\n if (!accountId || !(await cloudSessionStore.isAccountLinked(accountId))) {\n return { devices: [] as CloudDevice[] };\n }\n const res = await cloudFetch('/api/auth/device/list', { accountId });\n if (!res.ok) {\n if (res.status === 401) await cloudSessionStore.remove(accountId);\n return { devices: [] as CloudDevice[] };\n }\n return (await res.json()) as { devices: CloudDevice[] };\n }),\n\n /**\n * Revoke one session of the cloud account.\n *\n * Revoking THIS machine is allowed and is the honest way to unlink: the cloud\n * drops the session, and the local token is dropped here too, so nothing is\n * left holding a dead credential.\n */\n revokeDevice: protectedProcedure\n .input(ScopeSchema.extend({ id: z.string().min(1).max(128) }))\n .mutation(async ({ input }) => {\n const { accountId } = input;\n if (!accountId) throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'No cloud account for this scope.' });\n const res = await cloudFetch('/api/auth/device/revoke', {\n method: 'POST', body: { id: input.id }, accountId,\n });\n if (!res.ok) {\n if (res.status === 401) await cloudSessionStore.remove(accountId);\n throw new TRPCError({ code: 'BAD_REQUEST', message: 'Could not revoke this session.' });\n }\n const { self } = (await res.json()) as { self?: boolean };\n if (self) await cloudSessionStore.remove(accountId);\n return { success: true as const, self: self === true };\n }),\n\n /**\n * Forward a cloud API call, authenticated with this machine's token.\n *\n * This is what replaces the browser's direct, cookie-authenticated calls to\n * the cloud: the web UI asks its own server, which holds the credential.\n */\n proxy: protectedProcedure\n .input(\n z.object({\n path: z.string().min(1).max(512),\n method: z.enum(['GET', 'POST']).default('GET'),\n query: z.record(z.string().max(128), z.string().max(2048)).optional(),\n body: z.unknown().optional(),\n // Obligatoire, et seul : c'est la porte par laquelle passent tous les\n // appels cloud de l'interface. La laisser optionnelle est ce qui a\n // permis aux composables partagées de n'envoyer aucune portée pendant\n // trente routes, et au serveur de retomber sur « le compte unique du\n // poste » — muet dès qu'il y en a eu deux.\n accountId: z.string().min(1).max(64),\n }),\n )\n .mutation(async ({ input }) => {\n if (!isAllowedProxyPath(input.path)) {\n throw new TRPCError({ code: 'FORBIDDEN', message: `Path not allowed: ${input.path}` });\n }\n const qs = input.query ? `?${new URLSearchParams(input.query).toString()}` : '';\n const res = await cloudFetch(`/api/auth${input.path}${qs}`, {\n method: input.method,\n ...(input.method === 'POST' ? { body: input.body ?? {} } : {}),\n ...scopeOf(input),\n });\n const text = await res.text();\n let data: unknown = null;\n try {\n data = text ? JSON.parse(text) : null;\n } catch {\n data = text;\n }\n // Mirrors Better Auth's client shape ({ data } / { error }) so callers can\n // keep the same handling they had when they talked to the cloud directly.\n if (!res.ok) return { data: null, error: { status: res.status, ...(data as object) } };\n return { data, error: null };\n }),\n\n /**\n * Unlink this machine: drop the session here and on the cloud.\n *\n * Since the machine holds a session of its own, this no longer touches the\n * browser's session — signing out here leaves the web app signed in, and vice\n * versa.\n */\n signOut: protectedProcedure.input(ScopeSchema).mutation(async ({ input }) => {\n const { accountId } = input;\n // Le compte est nommé, toujours : « lequel ? » n'est plus une question que\n // ce serveur puisse se poser. Un compte déjà oublié n'est pas une erreur,\n // c'est l'état voulu — `remove` plus bas est idempotent.\n if (await cloudSessionStore.isAccountLinked(accountId)) {\n try {\n await cloudFetch('/api/auth/sign-out', { method: 'POST', body: {}, accountId });\n } catch {\n // Revoking server-side is best-effort — forget the token regardless.\n }\n }\n await cloudSessionStore.remove(accountId);\n return { success: true as const };\n }),\n});\n","import { readFile, writeFile, rename, mkdir, chmod, unlink } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { randomBytes, createHash, timingSafeEqual } from 'node:crypto';\nimport { keyedFilePath, legacyMachineFilePath } from './vault-resolver.js';\nimport { encryptValue, decryptValue } from './crypto-helpers.js';\n\nconst FILENAME = 'cloud-session.json';\n\n/** How long a pending login may sit between `start` and the browser callback. */\nconst STATE_TTL_MS = 5 * 60 * 1000;\n\n/** Ce que le poste retient d'un compte, une fois le token mis de côté. */\nexport interface CloudAccountIdentity {\n userId: string;\n email: string;\n name: string;\n image: string | null;\n}\n\n/** Un compte lié, tel qu'il sort d'ici — jamais son token. */\nexport interface CloudAccount extends CloudAccountIdentity {\n id: string;\n cloudUrl: string;\n}\n\ninterface StoredAccount extends CloudAccountIdentity {\n id: string;\n cloudUrl: string;\n encryptedToken: string;\n}\n\n/** La forme d'avant le multicompte : un seul token, sans identité ni id. */\ninterface LegacySession {\n cloudUrl: string;\n encryptedToken: string;\n}\n\ninterface StoredFile {\n version: 2;\n accounts: StoredAccount[];\n}\n\n/**\n * L'identifiant d'un compte : le cloud d'où il vient et l'utilisateur qu'il est.\n *\n * Dérivé plutôt que tiré au sort, pour que relier deux fois le même compte le\n * remplace au lieu de le doubler — ce qui laisserait deux tokens vivre côte à\n * côte, dont un que plus rien ne rafraîchit. Le hash ne protège rien, il\n * normalise : un `userId` de cloud n'a pas de forme garantie, et cet id sert de\n * nom de champ dans les tampons d'organisation.\n */\nexport function accountIdFor(cloudUrl: string, userId: string): string {\n return createHash('sha256')\n .update(`${cloudUrl.replace(/\\/+$/, '')}|${userId}`)\n .digest('hex')\n .slice(0, 32);\n}\n\n/**\n * Holds the cloud sessions this machine is signed into — one per account.\n *\n * The token is obtained through the local-login flow: the browser authorizes on\n * the cloud origin (where its session cookie is same-site and works), receives a\n * single-use token, and hands it to this server, which exchanges it for a\n * session token. The token then lives here — never in the browser — and every\n * cloud call is proxied through this server with `Authorization: Bearer`.\n *\n * That is what lets the cloud refuse localhost origins entirely: no page served\n * on any local port can act on the cloud session, and an XSS in the web UI has\n * no token to steal.\n *\n * Plusieurs comptes cohabitent parce que les organisations, elles, cohabitaient\n * déjà : un poste tient les coffres de plusieurs organisations, éventuellement\n * sur plusieurs clouds, et un token unique condamnait tout ce qui n'appartenait\n * pas au dernier compte connecté à ne plus jamais se synchroniser. Le compte à\n * employer se déduit de l'organisation visée (`readOrgAccounts`), jamais d'un\n * « compte courant » que deux appels concurrents se disputeraient.\n *\n * Encrypted at rest with the data-dir key, same mechanism as agents.json.\n */\nclass CloudSessionStore {\n private accounts: StoredAccount[] = [];\n private loaded = false;\n /** Pending login states (CSRF binding between `start` and the callback). */\n private pending = new Map<string, { expiresAt: number; returnTo: string }>();\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 /**\n * Relire le fichier, quelle que soit sa version.\n *\n * La forme d'avant n'est pas convertie sur disque ici : elle est lue telle\n * quelle et prend un id dérivé de son seul cloud. L'identité manque — le\n * compte n'était pas décrit — et se remplira au premier appel qui la\n * connaîtra. Réécrire le fichier au chargement le rendrait illisible à une\n * Runeya plus ancienne pour rien.\n */\n private async load(): Promise<void> {\n if (this.loaded) return;\n try {\n const parsed = JSON.parse(await this.readFromDisk()) as StoredFile | LegacySession;\n if ('accounts' in parsed && Array.isArray(parsed.accounts)) {\n this.accounts = parsed.accounts.filter(\n (a): a is StoredAccount => typeof a?.id === 'string' && typeof a?.encryptedToken === 'string',\n );\n } else if (typeof (parsed as LegacySession).encryptedToken === 'string') {\n const legacy = parsed as LegacySession;\n this.accounts = [{\n // Pas d'utilisateur connu : le cloud seul fonde l'id, et la première\n // identité lue le remplacera par le définitif.\n id: accountIdFor(legacy.cloudUrl, ''),\n cloudUrl: legacy.cloudUrl,\n encryptedToken: legacy.encryptedToken,\n userId: '',\n email: '',\n name: '',\n image: null,\n }];\n }\n } catch {\n // No file yet — not logged in.\n }\n this.loaded = true;\n }\n\n /**\n * Mint a state value binding a login attempt to this server. Returned to the\n * cloud page and echoed back on the callback, so a redirect we never\n * initiated cannot install a token.\n */\n createState(returnTo: string): string {\n this.sweepStates();\n const state = randomBytes(32).toString('hex');\n this.pending.set(state, { expiresAt: Date.now() + STATE_TTL_MS, returnTo });\n return state;\n }\n\n /**\n * Consume a state — single use, constant-time compare, TTL enforced. Returns\n * the URL to send the browser back to, or null when the state is unknown or\n * expired (i.e. a callback this server never initiated).\n */\n consumeState(candidate: string): string | null {\n this.sweepStates();\n for (const [state, entry] of this.pending) {\n const a = Buffer.from(state);\n const b = Buffer.from(candidate);\n if (a.length === b.length && timingSafeEqual(a, b)) {\n this.pending.delete(state);\n return entry.expiresAt > Date.now() ? entry.returnTo : null;\n }\n }\n return null;\n }\n\n private sweepStates(): void {\n const now = Date.now();\n for (const [state, entry] of this.pending) {\n if (entry.expiresAt <= now) this.pending.delete(state);\n }\n }\n\n private async save(): Promise<void> {\n const filePath = await this.getFilePath();\n await mkdir(dirname(filePath), { recursive: true });\n const payload: StoredFile = { version: 2, accounts: this.accounts };\n const tmpPath = filePath + '.tmp';\n await writeFile(tmpPath, JSON.stringify(payload, null, 2), 'utf-8');\n await rename(tmpPath, filePath);\n await chmod(filePath, 0o600);\n }\n\n /**\n * Ranger le token obtenu par l'échange de jeton à usage unique.\n *\n * L'identité est fournie par l'appelant, qui vient de la lire avec ce token :\n * c'est elle qui donne son id au compte, et donc ce qui fait qu'une seconde\n * connexion du même utilisateur remplace la première au lieu de s'ajouter.\n */\n async add(cloudUrl: string, token: string, identity: CloudAccountIdentity): Promise<CloudAccount> {\n await this.load();\n const account: StoredAccount = {\n ...identity,\n id: accountIdFor(cloudUrl, identity.userId),\n cloudUrl,\n encryptedToken: encryptValue(token),\n };\n const at = this.accounts.findIndex((a) => a.id === account.id);\n if (at >= 0) this.accounts[at] = account;\n else this.accounts.push(account);\n await this.save();\n return this.publicView(account);\n }\n\n private publicView(account: StoredAccount): CloudAccount {\n const { encryptedToken: _token, ...rest } = account;\n return rest;\n }\n\n /** Les comptes liés à ce poste, sans rien de secret. */\n async list(): Promise<CloudAccount[]> {\n await this.load();\n return this.accounts.map((a) => this.publicView(a));\n }\n\n /** Un compte par son id, ou null. */\n async get(accountId: string): Promise<CloudAccount | null> {\n await this.load();\n const found = this.accounts.find((a) => a.id === accountId);\n return found ? this.publicView(found) : null;\n }\n\n /**\n * Compléter ce qu'on sait d'un compte, une fois le cloud interrogé.\n *\n * Sert à la forme héritée, liée avant que l'identité soit retenue : son id\n * provisoire (fondé sur le seul cloud) devient le définitif, et les tampons\n * d'organisation qui le désignaient sont suivis par l'appelant.\n */\n async describe(accountId: string, identity: CloudAccountIdentity): Promise<string> {\n await this.load();\n const at = this.accounts.findIndex((a) => a.id === accountId);\n if (at < 0) return accountId;\n const current = this.accounts[at]!;\n const nextId = accountIdFor(current.cloudUrl, identity.userId);\n if (current.id === nextId && current.email === identity.email && current.name === identity.name) {\n return current.id;\n }\n // Un compte déjà présent sous l'id définitif l'emporte : deux entrées pour\n // un même utilisateur laisseraient un token orphelin.\n const duplicate = this.accounts.findIndex((a) => a.id === nextId && a.id !== current.id);\n if (duplicate >= 0) this.accounts.splice(duplicate, 1);\n this.accounts[this.accounts.findIndex((a) => a.id === accountId)] = {\n ...current, ...identity, id: nextId,\n };\n await this.save();\n return nextId;\n }\n\n /**\n * Le token déchiffré d'un compte, ou null.\n *\n * Sans id : le compte unique du poste, et rien du tout dès qu'il y en a\n * plusieurs — deviner lequel reviendrait à parler au cloud d'un autre\n * utilisateur, ce qu'aucun appel ne veut jamais.\n */\n async getToken(accountId?: string): Promise<string | null> {\n await this.load();\n const account = accountId\n ? this.accounts.find((a) => a.id === accountId)\n : (this.accounts.length === 1 ? this.accounts[0] : undefined);\n if (!account) return null;\n try {\n return decryptValue(account.encryptedToken);\n } catch {\n // Key rotated or file tampered with — treat as logged out.\n return null;\n }\n }\n\n /**\n * The cloud a given account is signed into, or null when unknown.\n *\n * Needed to tell \"an org I am not a member of\" apart from \"an org of another\n * cloud instance entirely\" — the ids of two clouds share no namespace, so\n * comparing them without this would call every foreign-cloud project a\n * permission problem.\n */\n async getCloudUrl(accountId?: string): Promise<string | null> {\n await this.load();\n const account = accountId\n ? this.accounts.find((a) => a.id === accountId)\n : (this.accounts.length === 1 ? this.accounts[0] : undefined);\n return account?.cloudUrl ?? null;\n }\n\n /** Au moins un compte lié — l'état du poste, pas celui d'un compte précis. */\n async isLinked(): Promise<boolean> {\n await this.load();\n return this.accounts.length > 0;\n }\n\n /** Ce compte-là est-il utilisable ? */\n async isAccountLinked(accountId: string): Promise<boolean> {\n return (await this.getToken(accountId)) !== null;\n }\n\n /**\n * Oublier un compte, en laissant les autres tranquilles.\n *\n * Les organisations qui en dépendaient passent hors ligne d'elles-mêmes : leur\n * tampon désigne un compte qui n'est plus là, et la résolution rend « non\n * lié » plutôt que d'aller frapper au cloud avec le token du voisin.\n */\n async remove(accountId: string): Promise<void> {\n await this.load();\n const before = this.accounts.length;\n this.accounts = this.accounts.filter((a) => a.id !== accountId);\n if (this.accounts.length === before) return;\n if (this.accounts.length === 0) {\n await this.clear();\n return;\n }\n await this.save();\n }\n\n /** Forget every session (full sign-out of this machine). */\n async clear(): Promise<void> {\n await this.load();\n this.accounts = [];\n this.pending.clear();\n try {\n await unlink(await this.getFilePath());\n } catch {\n // Already gone.\n }\n }\n}\n\nexport const cloudSessionStore = new CloudSessionStore();\n","import type { Server } from 'node:http';\nimport { networkInterfaces } from 'node:os';\nimport { readFile, writeFile, rename, mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { z } from 'zod';\nimport { env } from '../config/env.js';\nimport { machineRoot } from './vault-resolver.js';\n\n/**\n * Exposer Runeya sur le réseau local, depuis l'interface.\n *\n * C'est ce que fait `--lan` au démarrage : écouter sur `0.0.0.0` au lieu de la\n * seule boucle locale, et desserrer CORS/CSRF pour les origines qui visent le\n * port du serveur. Le drapeau reste la voie du lancement ; ce module est la\n * même bascule, à chaud, mémorisée dans les réglages.\n *\n * Le réglage appartient à CETTE instance, pas au poste. Deux stacks ouvertes\n * côte à côte n'ont aucune raison d'être exposées ensemble — l'exposition\n * réseau est précisément ce qu'on ne veut pas voir s'étendre par héritage.\n *\n * Il est pourtant écrit dans la racine machine, et non dans le dossier de\n * données de l'instance : celui-ci peut très bien ne jamais exister (une Runeya\n * qui ne travaille que sur des projets cloud n'écrit rien dans son répertoire\n * de lancement, et ce n'est pas une bascule réseau qui doit l'y forcer). Le\n * fichier est donc un dictionnaire indexé par dossier de données — ce qui\n * distingue une instance d'une autre — et non un réglage unique pour le poste.\n *\n * Rien n'est recalculé ailleurs : `env.HOST` et `env.RUNEYA_LAN_PROXY` sont lus\n * à l'usage (CORS, CSRF, `{{service.HOST}}`), donc les modifier suffit — reste\n * à rouvrir la socket d'écoute, ce que `rebind` fait sans couper les\n * connexions en cours (l'onglet qui a demandé la bascule survit).\n */\n\nconst LOOPBACK = '127.0.0.1';\nconst ANY = '0.0.0.0';\n\nconst NETWORK_FILE = 'network.json';\nconst NetworkFileSchema = z.object({\n instances: z.record(z.string(), z.object({ lanEnabled: z.boolean().default(false) })).default({}),\n});\ntype NetworkFile = z.infer<typeof NetworkFileSchema>;\n\nlet httpServer: Server | null = null;\n\n/** L'instance, c'est son dossier de données — résolu en absolu dès le démarrage. */\nfunction instanceKey(): string {\n return env.DATA_DIR;\n}\n\nfunction networkFilePath(): string {\n return join(machineRoot(), NETWORK_FILE);\n}\n\nasync function readFileState(): Promise<NetworkFile> {\n try {\n return NetworkFileSchema.parse(JSON.parse(await readFile(networkFilePath(), 'utf-8')));\n } catch {\n return { instances: {} };\n }\n}\n\nasync function readPersisted(): Promise<boolean> {\n const state = await readFileState();\n return state.instances[instanceKey()]?.lanEnabled ?? false;\n}\n\n/**\n * Écriture atomique, en relisant d'abord : plusieurs Runeya partagent ce\n * fichier, et écraser l'entrée d'une voisine reviendrait à la remettre en\n * écoute locale à son prochain démarrage sans que personne ne l'ait demandé.\n */\nasync function writePersisted(lanEnabled: boolean): Promise<void> {\n const root = machineRoot();\n await mkdir(root, { recursive: true });\n const state = await readFileState();\n state.instances[instanceKey()] = { lanEnabled };\n const filePath = networkFilePath();\n const tmpPath = filePath + '.tmp';\n await writeFile(tmpPath, JSON.stringify(state, null, 2), 'utf-8');\n await rename(tmpPath, filePath);\n}\n\n/** Appelé une fois par `createLocalServer`, avant l'écoute. */\nexport function registerHttpServer(server: Server): void {\n httpServer = server;\n}\n\nexport interface NetworkInfo {\n /** Adresse sur laquelle le serveur écoute. */\n host: string;\n port: number;\n lanEnabled: boolean;\n /** URLs joignables depuis les autres machines, vides hors mode LAN. */\n lanUrls: string[];\n}\n\n/** Adresses IPv4 non locales de cette machine. */\nexport function lanIps(): string[] {\n const found: string[] = [];\n for (const nets of Object.values(networkInterfaces())) {\n for (const net of nets ?? []) {\n if (net.family === 'IPv4' && !net.internal) found.push(net.address);\n }\n }\n return found;\n}\n\nfunction currentPort(): number {\n const address = httpServer?.address();\n return typeof address === 'object' && address ? address.port : env.PORT;\n}\n\nexport function networkInfo(): NetworkInfo {\n const port = currentPort();\n return {\n host: env.HOST,\n port,\n lanEnabled: env.RUNEYA_LAN_PROXY,\n lanUrls: env.RUNEYA_LAN_PROXY ? lanIps().map((ip) => `http://${ip}:${port}`) : [],\n };\n}\n\n/** Un seul endroit met à jour l'hôte : `env` et `process.env` ne peuvent pas diverger. */\nfunction setHost(host: string, enabled: boolean): void {\n env.HOST = host;\n env.RUNEYA_HOST = host;\n env.RUNEYA_LAN_PROXY = enabled;\n process.env.RUNEYA_HOST = host;\n process.env.RUNEYA_LAN_PROXY = enabled ? 'true' : 'false';\n}\n\n/**\n * Rouvrir la socket d'écoute sur une autre adresse.\n *\n * `close()` ferme le descripteur d'écoute tout de suite et ne rappelle qu'une\n * fois les connexions terminées — on ne l'attend donc pas, sans quoi la requête\n * qui a demandé la bascule s'attendrait elle-même. En cas d'échec, on revient à\n * l'adresse précédente : un serveur qui n'écoute plus nulle part serait pire\n * que la bascule refusée.\n */\nasync function rebind(host: string, previousHost: string): Promise<void> {\n const server = httpServer;\n if (!server?.listening) return;\n const port = currentPort();\n\n const listenOn = (target: string) =>\n new Promise<void>((resolve, reject) => {\n const onError = (err: Error) => {\n server.removeListener('listening', onListening);\n reject(err);\n };\n const onListening = () => {\n server.removeListener('error', onError);\n resolve();\n };\n server.once('error', onError);\n server.once('listening', onListening);\n server.listen(port, target);\n });\n\n server.close(() => { /* les connexions en cours finissent d'elles-mêmes */ });\n\n try {\n await listenOn(host);\n } catch (err) {\n setHost(previousHost, previousHost === ANY);\n await listenOn(previousHost).catch(() => { /* plus rien à sauver ici */ });\n throw err;\n }\n}\n\n/**\n * Réglage mémorisé, appliqué au démarrage — avant que le serveur n'écoute.\n *\n * Ce qui a été demandé au lancement passe devant : `--lan` (ou `--host`) est un\n * choix explicite de cette session, le réglage n'est qu'un défaut.\n */\nexport async function applyPersistedLanMode(): Promise<void> {\n const onLoopback = env.HOST === LOOPBACK || env.HOST === 'localhost';\n\n // Le mode LAN et l'adresse d'écoute ne peuvent pas se contredire : annoncé\n // sur la boucle locale, il ferait proposer à l'écran des URL de réseau que\n // rien ne sert. Le cas arrive sans que personne ne l'ait demandé — une\n // Runeya lancée par une autre hérite de son `RUNEYA_LAN_PROXY` ET de son\n // `RUNEYA_HOST`, et le second empêchait alors le premier de prendre effet.\n // Un hôte explicitement non local, lui, reste celui qu'on a choisi.\n if (env.RUNEYA_LAN_PROXY) {\n if (onLoopback) {\n setHost(ANY, true);\n console.log('[server] LAN mode: hérité sur la boucle locale — écoute rétablie sur 0.0.0.0');\n }\n return;\n }\n\n if (!onLoopback) return;\n if (!(await readPersisted())) return;\n setHost(ANY, true);\n console.log('[server] LAN mode: activé par les réglages — écoute sur 0.0.0.0');\n}\n\n/** Bascule à chaud, mémorisée pour les démarrages suivants. */\nexport async function setLanMode(enabled: boolean): Promise<NetworkInfo> {\n await writePersisted(enabled);\n\n const previousHost = env.HOST;\n const host = enabled ? ANY : LOOPBACK;\n setHost(host, enabled);\n if (host !== previousHost) await rebind(host, previousHost);\n\n console.log(`[server] LAN mode: ${enabled ? 'activé' : 'désactivé'} — écoute sur ${host}`);\n return networkInfo();\n}\n","declare const __RUNEYA_VERSION__: string;\n\n/**\n * Version de cette app, telle qu'annoncée au cloud et affichée dans l'UI.\n *\n * Injectée au build par tsup (`define`) ; vaut `dev` depuis le monorepo, ce que\n * le cloud accepte toujours.\n */\nexport const appVersion = process.env['RUNEYA_VERSION']\n ?? (typeof __RUNEYA_VERSION__ !== 'undefined' ? __RUNEYA_VERSION__ : 'dev');\n\n/** En-tête où l'app annonce sa version à chaque appel au cloud. */\nexport const APP_VERSION_HEADER = 'X-Runeya-Version';\n","import { env } from '../config/env.js';\nimport { APP_VERSION_HEADER, appVersion } from '../config/app-version.js';\nimport { cloudSessionStore, type CloudAccountIdentity } from './cloud-session-store.js';\n\n/**\n * Where this installation's cloud lives, and how to call it.\n *\n * One cloud per installation: the URL comes from the environment, not from\n * anything stored per organization. It used to be carried alongside each sync\n * link, which made two records of one fact — and the stored one could outlive\n * the truth.\n *\n * Lives in a service rather than in the cloud-auth router because the sync\n * bridge needs it too, and a service must not import a router.\n */\n\nconst DEFAULT_CLOUD_API_URL = 'https://api.runeya.dev';\nconst DEFAULT_CLOUD_APP_URL = 'https://runeya.dev';\n\nexport function getCloudApiUrl(): string {\n return (env.CLOUD_URL ?? DEFAULT_CLOUD_API_URL).replace(/\\/+$/, '');\n}\n\n/**\n * Le cloud d'une organisation donnée.\n *\n * Une organisation peut être hébergée ailleurs que sur le cloud par défaut :\n * le poste garde l'adresse de celui où elle a été liée. S'adresser au cloud\n * global pour toutes revient à ne rien trouver de celles qui vivent ailleurs.\n */\nexport async function orgCloudUrl(orgId: string): Promise<string> {\n const { readOrgClouds } = await import('./vault-resolver.js');\n const clouds = await readOrgClouds();\n return (clouds[orgId] ?? getCloudApiUrl()).replace(/\\/+$/, '');\n}\n\n/**\n * Le compte au nom duquel parler d'une organisation donnée.\n *\n * Le tampon du coffre tranche ; à défaut — coffre écrit avant le multicompte —\n * le compte unique du poste, qui est exactement ce que ce tampon décrivait.\n * Rien n'est deviné au-delà : avec plusieurs comptes et aucun tampon, répondre\n * `null` fait dire « non lié » plutôt que d'aller frapper au cloud d'un tiers\n * avec le token de quelqu'un d'autre.\n */\nexport async function accountForOrg(orgId: string): Promise<string | null> {\n const { readOrgAccounts, readOrgClouds } = await import('./vault-resolver.js');\n const [stampedAccounts, stampedClouds, accounts] = await Promise.all([\n readOrgAccounts(), readOrgClouds(), cloudSessionStore.list(),\n ]);\n const orgCloud = stampedClouds[orgId]?.replace(/\\/+$/, '');\n // Le coffre dit à quel cloud cette organisation appartient : seuls les\n // comptes de CE cloud peuvent en répondre. Les identifiants de deux clouds ne\n // partagent aucun espace de noms — demander à l'un des nouvelles d'une\n // organisation de l'autre ne rend pas un désaccord, mais un refus qui se lit\n // comme un problème de droits. Sans tampon d'adresse, tous les comptes\n // restent candidats : c'est ce que valait un coffre écrit avant les tampons.\n const candidates = orgCloud\n ? accounts.filter((a) => a.cloudUrl.replace(/\\/+$/, '') === orgCloud)\n : accounts;\n const stamped = stampedAccounts[orgId];\n // Le tampon de compte tranche, tant qu'il désigne un candidat encore lié :\n // un compte délié depuis ne vaut plus rien, et s'y tenir rendrait\n // l'organisation muette à jamais.\n if (stamped && candidates.some((a) => a.id === stamped)) return stamped;\n return candidates.length === 1 ? candidates[0]!.id : null;\n}\n\nexport function getCloudAppUrl(): string {\n return (env.CLOUD_APP_URL ?? DEFAULT_CLOUD_APP_URL).replace(/\\/+$/, '');\n}\n\n/**\n * Plusieurs comptes, et rien pour choisir.\n *\n * Distinct de `CloudNotLinkedError` à dessein : « aucun compte lié » et « je ne\n * sais pas lequel » demandent deux gestes différents de l'utilisateur, et les\n * confondre est ce qui a envoyé chercher une déconnexion là où il y avait une\n * portée manquante.\n */\nexport class CloudAmbiguousAccountError extends Error {\n readonly code = 'CLOUD_AMBIGUOUS_ACCOUNT';\n constructor() {\n super('Several cloud accounts are linked; name the organization this call belongs to.');\n this.name = 'CloudAmbiguousAccountError';\n }\n}\n\n/** No cloud session on this machine — a state of the machine, not a verdict on the caller. */\nexport class CloudNotLinkedError extends Error {\n readonly code = 'CLOUD_NOT_LINKED';\n constructor() {\n super('Not linked to the Runeya cloud.');\n this.name = 'CloudNotLinkedError';\n }\n}\n\n/**\n * De quel compte relève un appel.\n *\n * Un seul champ, et il est obligatoire. Le compte porte son cloud dans son\n * identifiant même (`accountIdFor(cloudUrl, userId)`), si bien qu'il donne les\n * deux choses dont un appel a besoin : le jeton, et l'adresse où frapper.\n *\n * Il n'y a plus de repli. La portée était optionnelle, et `cloudFetch({})`\n * retombait sur « le compte unique du poste, sinon rien » : le jour où un\n * second compte a été lié, trente appels du kanban se sont mis à répondre\n * « non lié » sans que rien ne le dise. Ce que le type n'exige pas, un\n * appelant finit par l'omettre.\n */\nexport interface CloudCallScope {\n accountId: string;\n}\n\n/**\n * Le compte d'une organisation, ou un refus net.\n *\n * Pour les appelants internes qui ne connaissent que l'organisation visée : le\n * tampon du coffre tranche, relu à chaque appel plutôt que recopié quelque\n * part. Rien ne la rattache à un compte de ce poste — on le dit, au lieu de\n * partir avec le jeton du voisin.\n */\nexport async function accountForOrgOrThrow(orgId: string): Promise<string> {\n const accountId = await accountForOrg(orgId);\n if (!accountId) throw new CloudNotLinkedError();\n return accountId;\n}\n\n/**\n * L'unique compte de ce poste, ou un refus net.\n *\n * Le seul repli qui subsiste, et il porte un nom pour cela. Réservé aux appels\n * qui ne PEUVENT pas nommer d'organisation — le téléchargement d'une pièce\n * jointe, où un agent n'a qu'un identifiant de fichier. Partout ailleurs, la\n * portée est connue et doit être dite.\n *\n * Avec plusieurs comptes il refuse, et il le dit : c'est exactement ce que\n * l'ancien repli faisait en silence, en rendant `null` qui ressortait en\n * « non lié » — une panne de routage déguisée en déconnexion.\n */\nexport async function soleAccountOrThrow(): Promise<string> {\n const accounts = await cloudSessionStore.list();\n if (accounts.length === 1) return accounts[0]!.id;\n if (accounts.length === 0) throw new CloudNotLinkedError();\n throw new CloudAmbiguousAccountError();\n}\n\n/**\n * Call the cloud with one of this machine's sessions, read fresh on every request\n * so signing back in resumes everything without a restart.\n */\nexport async function cloudFetch(\n path: string,\n // Pas de valeur par défaut : un `cloudFetch(path)` sans portée ne doit pas\n // compiler, sans quoi le repli supprimé plus haut se reformerait tout seul.\n init: { method?: string; body?: unknown; timeoutMs?: number } & CloudCallScope,\n): Promise<Response> {\n const { accountId } = init;\n const token = await cloudSessionStore.getToken(accountId);\n if (!token) throw new CloudNotLinkedError();\n // L'adresse vient du COMPTE, et elle en est indissociable : son identifiant\n // est dérivé de son cloud, donc deux clouds ne peuvent pas partager un\n // compte. Nommer le compte, c'est donc avoir déjà nommé l'adresse — il n'y a\n // pas de couple à tenir cohérent, ni de second aller au disque pour le lire.\n const base = (await cloudSessionStore.getCloudUrl(accountId)) ?? getCloudApiUrl();\n return fetch(`${base.replace(/\\/+$/, '')}${path}`, {\n method: init.method ?? 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n // Le cloud refuse (426) les apps trop anciennes pour son schéma courant.\n [APP_VERSION_HEADER]: appVersion,\n ...(init.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n ...(init.body !== undefined ? { body: JSON.stringify(init.body) } : {}),\n // 15s suits a JSON call; attachment downloads pass a longer budget.\n signal: AbortSignal.timeout(init.timeoutMs ?? 15_000),\n });\n}\n\n/**\n * Qui est l'utilisateur derrière un token, demandé au cloud.\n *\n * Appelé juste après la liaison, avec le token tout neuf : c'est cette identité\n * qui donne son id au compte, et donc ce qui fait qu'une seconde connexion du\n * même utilisateur remplace la première au lieu d'en empiler une seconde. Rend\n * `null` si le cloud ne répond pas — la liaison échoue alors franchement,\n * plutôt que de ranger un token que rien ne saurait plus désigner.\n */\nexport async function readCloudIdentity(\n cloudUrl: string,\n token: string,\n): Promise<CloudAccountIdentity | null> {\n try {\n const res = await fetch(`${cloudUrl.replace(/\\/+$/, '')}/api/auth/get-session`, {\n headers: { Authorization: `Bearer ${token}`, [APP_VERSION_HEADER]: appVersion },\n signal: AbortSignal.timeout(15_000),\n });\n if (!res.ok) return null;\n const body = (await res.json()) as { user?: { id?: unknown; email?: unknown; name?: unknown; image?: unknown } };\n const id = body.user?.id;\n if (typeof id !== 'string' || !id) return null;\n return {\n userId: id,\n email: typeof body.user?.email === 'string' ? body.user.email : '',\n name: typeof body.user?.name === 'string' ? body.user.name : '',\n image: typeof body.user?.image === 'string' ? body.user.image : null,\n };\n } catch {\n return null;\n }\n}\n","import type {\n KanbanBoard,\n KanbanCard,\n KanbanCardEvent,\n KanbanCardLink,\n KanbanCardRef,\n CreateKanbanBoardInput,\n UpdateKanbanBoardInput,\n} from '@runeya/packages-shared';\nimport { cloudFetch, accountForOrgScope, soleAccountScope } from '../trpc/routers/cloud-auth.js';\nimport { cloudSessionStore } from './cloud-session-store.js';\n\n/**\n * Kanban boards, read and written on the Runeya cloud.\n *\n * Boards belong to an organization, not to a machine: they are shared with the\n * team and gated on a paid plan, so there is no local copy and no local file.\n * This module keeps the store-shaped API its callers (the MCP kanban commands,\n * the AI queue, the chat router) were already written against — only the\n * backing store moved.\n *\n * Every call goes through `cloudFetch`, which attaches this machine's cloud\n * token. A machine that is not linked to the cloud therefore has no boards at\n * all: reads come back empty and writes report failure, rather than silently\n * accumulating work nobody else will ever see.\n */\n\n/** A cloud board, as the API serializes it. */\ninterface CloudBoard extends KanbanBoard {\n organizationId: string;\n}\n\ninterface CloudCall<T> {\n ok: boolean;\n data: T | null;\n /** Pourquoi l'appel a échoué — vide quand il a réussi. */\n reason: string | null;\n /**\n * Le code HTTP, quand il y en a eu un. Ce qui permet de distinguer « ce cloud\n * ne connaît pas cette route » (404) d'un refus — un cloud auto-hébergé plus\n * ancien que cette app n'a pas forcément les routes qu'elle a apprises.\n */\n status: number | null;\n}\n\n/**\n * Un tableau introuvable et un cloud qui refuse de répondre ne se ressemblent\n * pas, mais rendaient tous deux une liste vide.\n *\n * C'est ce qui a coûté le plus cher : un agent annonçait « aucun tableau » avec\n * aplomb alors que la question n'avait jamais atteint le cloud. Une lecture qui\n * échoue lève désormais, avec la raison ; seule une vraie absence rend du vide.\n */\nexport class KanbanUnavailableError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KanbanUnavailableError';\n }\n}\n\nfunction planGateMessage(status: number, path: string): string {\n if (status === 402 || status === 403) {\n return `Le cloud a refusé l'accès aux tableaux (HTTP ${status}) : le plan de l'organisation ne le permet pas, ou ce compte n'y a pas droit.`;\n }\n if (status === 426) {\n return 'Le cloud refuse cette version de Runeya (HTTP 426) : mettez l\\'application à jour.';\n }\n return `Le cloud a répondu HTTP ${status} sur ${path}.`;\n}\n\nasync function callCloud<T>(\n path: string,\n // `accountId` obligatoire : chaque appel de ce magasin sait de qui il parle\n // — par l'organisation, par le tableau, ou par le mémo. Le laisser facultatif\n // laissait passer les appels sans portée, qui échouaient ensuite en « non\n // lié » alors que le compte existait.\n init: { method?: string; body?: unknown; accountId: string },\n): Promise<CloudCall<T>> {\n try {\n const res = await cloudFetch(`/api/auth${path}`, init);\n if (!res.ok) {\n // 402/403 here is the plan gate (Solo is read-only) — worth surfacing,\n // since the caller is usually an AI agent whose write just vanished.\n console.error('[kanban-store] cloud call failed: %s %s', res.status, path);\n return { ok: false, data: null, reason: planGateMessage(res.status, path), status: res.status };\n }\n return { ok: true, data: (await res.json()) as T, reason: null, status: res.status };\n } catch (err) {\n console.error('[kanban-store] cloud call errored:', err);\n return { ok: false, data: null, reason: `Appel au cloud impossible : ${(err as Error).message}`, status: null };\n }\n}\n\n/** Lecture obligatoire : ce qui n'a pas abouti ne doit pas passer pour du vide. */\nfunction must<T>(res: CloudCall<T>): T {\n if (!res.ok || res.data === null) {\n throw new KanbanUnavailableError(res.reason ?? 'Le cloud n\\'a pas répondu.');\n }\n return res.data;\n}\n\n\n/**\n * Sous quel compte ce magasin parle au cloud.\n *\n * Les appels du kanban sont adressés par tableau, pas par organisation : rien\n * dans leur signature ne dit de quel compte ils relèvent. Trois sources, dans\n * l'ordre :\n *\n * 1. l'organisation quand l'appelant la nomme — son tampon donne le compte ;\n * 2. le tableau, s'il a déjà répondu ici : on retient qui l'a servi ;\n * 3. le compte unique du poste, ce qui est toute la situation d'avant.\n *\n * Au-delà, on refuse de choisir plutôt que d'aller lire les tableaux d'un\n * homonyme avec le token du voisin.\n */\nconst boardAccounts = new Map<string, string>();\n\nasync function accountOfOrg(orgId: string): Promise<string> {\n // La variante du routeur, comme `cloudFetch` juste au-dessus : ce magasin\n // répond à des routes tRPC, et un refus doit garder ici la forme qu'il avait\n // — sans quoi l'appelant reçoit une erreur interne là où il lisait « pas de\n // compte lié ».\n return accountForOrgScope(orgId);\n}\n\n/** Retenir qui a servi un tableau, pour les appels suivants qui n'ont que son id. */\nfunction rememberBoardAccount(boardId: string, accountId: string): void {\n boardAccounts.set(boardId, accountId);\n}\n\n/**\n * Le compte à employer pour un tableau donné.\n *\n * Un tableau jamais vu et plusieurs comptes liés : on balaie les organisations\n * de chacun pour trouver celui qui le porte. C'est un appel réseau par compte,\n * fait une seule fois par tableau — ensuite le mémo répond.\n */\nasync function accountOfBoard(boardId: string): Promise<string> {\n const memo = boardAccounts.get(boardId);\n if (memo) return memo;\n const accounts = await cloudSessionStore.list();\n if (accounts.length <= 1) return soleAccountScope();\n // Tous les comptes en même temps, pas l'un après l'autre : un identifiant de\n // tableau inexistant — ce qu'une IA produit régulièrement — coûtait sinon un\n // aller-retour par compte avant de pouvoir dire qu'il n'existe nulle part.\n const owners = await Promise.all(accounts.map(async (account) => {\n const res = await callCloud<{ board: CloudBoard | null }>(\n `/kanban/board/get?id=${encodeURIComponent(boardId)}`,\n { accountId: account.id },\n );\n return res.ok && res.data?.board ? account.id : null;\n }));\n const owner = owners.find((id): id is string => id !== null);\n if (owner) {\n boardAccounts.set(boardId, owner);\n return owner;\n }\n throw new KanbanUnavailableError(\n `Aucun des ${accounts.length} comptes liés à ce poste ne connaît le tableau ${boardId}.`,\n );\n}\n\n/** La portée d'un appel adressé par tableau, prête pour `callCloud`. */\nasync function scopeOfBoard(boardId: string): Promise<{ accountId: string }> {\n return { accountId: await accountOfBoard(boardId) };\n}\n\n/**\n * L'organisation dont on lit les tableaux.\n *\n * Dans l'ordre : celle que l'appelant nomme, puis celle que porte la session\n * cloud de cette machine, puis — et seulement si le compte n'en a qu'une — la\n * sienne. Au-delà, on refuse de choisir.\n *\n * L'ancien repli prenait « la première de la liste ». Le sélecteur du web agit\n * sur la session du NAVIGATEUR, pas sur celle de la machine : les deux vues\n * divergeaient donc en silence, et un compte à plusieurs organisations voyait\n * les tableaux d'une autre — ou rien du tout, sans que rien ne le dise.\n *\n * Résolu à chaque appel plutôt que mis en cache : changer d'organisation prend\n * effet aussitôt.\n */\nasync function resolveOrganizationId(explicit?: string): Promise<string> {\n if (explicit) return explicit;\n\n // Sans organisation nommée, il faut un compte pour poser la question — et il\n // n'y en a un d'évident que s'il n'y en a qu'un.\n const accounts = await cloudSessionStore.list();\n if (accounts.length > 1) {\n throw new KanbanUnavailableError(\n `Ce poste est lié à ${accounts.length} comptes cloud : précisez organizationId, `\n + 'sans quoi rien ne dit lequel interroger.',\n );\n }\n const accountId = await soleAccountScope();\n\n const session = await callCloud<{ session?: { activeOrganizationId?: string | null } }>(\n '/get-session', { accountId },\n );\n const active = session.data?.session?.activeOrganizationId;\n if (active) return active;\n\n const orgs = must(await callCloud<{ id: string; name?: string }[]>(\n '/organization/list', { accountId },\n ));\n if (orgs.length === 1) return orgs[0]!.id;\n if (orgs.length === 0) {\n throw new KanbanUnavailableError(\n \"Ce compte cloud n'appartient à aucune organisation : il n'y a aucun tableau à lire.\",\n );\n }\n\n const listed = orgs.map((o) => `${o.name ?? '(sans nom)'} — ${o.id}`).join(', ');\n throw new KanbanUnavailableError(\n `Ce compte appartient à ${orgs.length} organisations et la session de cette machine n'en désigne aucune comme active. `\n + `Précisez organizationId parmi : ${listed}.`,\n );\n}\n\nclass KanbanStore {\n /**\n * Les tableaux d'une organisation, et laquelle a répondu.\n *\n * L'organisation fait partie du résultat parce qu'elle faisait partie du\n * problème : une liste vide ne disait pas à qui la question avait été posée.\n */\n async list(\n includeArchived = false,\n organizationId?: string,\n ): Promise<{ organizationId: string; boards: KanbanBoard[] }> {\n const orgId = await resolveOrganizationId(organizationId);\n const query = `?organizationId=${encodeURIComponent(orgId)}`\n + (includeArchived ? '&includeArchived=true' : '');\n const accountId = await accountOfOrg(orgId);\n const res = must(await callCloud<{ boards: KanbanBoard[] }>(\n `/kanban/board/list${query}`, { accountId },\n ));\n // Les tableaux de cette organisation viennent de ce compte : les appels\n // suivants, qui n'auront que leur id, sauront à qui s'adresser.\n for (const board of res.boards ?? []) rememberBoardAccount(board.id, accountId);\n return { organizationId: orgId, boards: res.boards ?? [] };\n }\n\n /**\n * Un tableau. Sans les journaux de ses cartes par défaut : c'est la moitié\n * lourde du document, et presque aucun appelant n'en a besoin — un agent qui\n * liste des cartes ne lit pas les commentaires. `withHistory` les redemande\n * pour les rares cas qui les veulent tous d'un coup.\n */\n async get(id: string, opts: { withHistory?: boolean } = {}): Promise<KanbanBoard | null> {\n const query = `?id=${encodeURIComponent(id)}${opts.withHistory ? '&withHistory=true' : ''}`;\n // `must` : un cloud muet lève, alors qu'un `board` absent de la réponse est\n // une vraie réponse — ce tableau n'existe pas.\n const accountId = await accountOfBoard(id);\n const res = must(await callCloud<{ board: CloudBoard | null }>(\n `/kanban/board/get${query}`, { accountId },\n ));\n return res.board ?? null;\n }\n\n /** Le journal d'une seule carte — ce que le contexte d'un agent demande. */\n async cardHistory(boardId: string, cardId: string): Promise<KanbanCardEvent[]> {\n const res = await callCloud<{ history: KanbanCardEvent[] }>(\n `/kanban/card/history?boardId=${encodeURIComponent(boardId)}&cardId=${encodeURIComponent(cardId)}`,\n await scopeOfBoard(boardId),\n );\n return res.data?.history ?? [];\n }\n\n /**\n * Commenter une carte, sans réécrire le tableau.\n *\n * C'est le geste que l'IA répète le plus, et souvent avec de longs textes :\n * le faire passer par une sauvegarde de tableau faisait transiter tout le\n * graphe de cartes à chaque phrase.\n */\n async addComment(\n boardId: string,\n cardId: string,\n detail: string,\n conversationId?: string,\n ): Promise<boolean> {\n const res = await callCloud<{ event: KanbanCardEvent }>('/kanban/card/comment/add', {\n method: 'POST',\n body: { boardId, cardId, detail, author: 'ai', ...(conversationId ? { conversationId } : {}) },\n ...(await scopeOfBoard(boardId)),\n });\n return res.ok;\n }\n\n /** Poser une entrée de journal non-commentaire (une conversation qui s'ouvre). */\n async addCardEvent(\n boardId: string,\n cardId: string,\n type: 'conversation_started' | 'scenario_started',\n detail?: string,\n ): Promise<boolean> {\n const res = await callCloud<{ event: KanbanCardEvent }>('/kanban/card/event/add', {\n method: 'POST',\n body: { boardId, cardId, type, detail },\n ...(await scopeOfBoard(boardId)),\n });\n return res.ok;\n }\n\n /**\n * Ajouter une carte, sans réécrire le tableau.\n *\n * La colonne se nomme par son titre : c'est ce que connaît une IA qui lit un\n * tableau, et le cloud le résout. Deux ajouts qui se croisent se rangent tous\n * les deux, ce qu'une sauvegarde de tableau entier ne garantissait pas.\n */\n async addCard(\n boardId: string,\n input: { column: string; title: string; markdown?: string },\n ): Promise<{ card: KanbanCard | null; unsupported: boolean }> {\n const res = await callCloud<{ card: KanbanCard }>('/kanban/card/add', {\n method: 'POST',\n body: { boardId, ...input },\n ...(await scopeOfBoard(boardId)),\n });\n // Un cloud qui ne connaît pas la route n'est pas un cloud qui refuse : il est\n // plus ancien que cette app. L'appelant a de quoi se rabattre, et le dire est\n // ce qui l'en rend capable.\n return { card: res.data?.card ?? null, unsupported: res.status === 404 };\n }\n\n /** Une carte, par son identifiant — archivée comprise. */\n async card(boardId: string, cardId: string): Promise<{ card: KanbanCard; columnId: string } | null> {\n const res = await callCloud<{ card: KanbanCard; columnId: string }>(\n `/kanban/card/get?boardId=${encodeURIComponent(boardId)}&cardId=${encodeURIComponent(cardId)}`,\n await scopeOfBoard(boardId),\n );\n return res.data ?? null;\n }\n\n /**\n * Modifier une carte.\n *\n * L'absence d'un champ ne le touche pas ; `null` l'efface. C'est le contrat du\n * cloud, et le distinguer ici évite qu'un agent vide une couleur en voulant\n * simplement changer un titre.\n */\n async updateCard(\n boardId: string,\n cardId: string,\n patch: Record<string, unknown>,\n ): Promise<KanbanCard | null> {\n const res = await callCloud<{ card: KanbanCard }>('/kanban/card/update', {\n method: 'POST',\n body: { boardId, cardId, ...patch },\n ...(await scopeOfBoard(boardId)),\n });\n return res.data?.card ?? null;\n }\n\n /** Ranger une carte, ou la reprendre. */\n async setCardArchived(boardId: string, cardId: string, archived: boolean): Promise<boolean> {\n const res = await callCloud(`/kanban/card/${archived ? 'archive' : 'unarchive'}`, {\n method: 'POST',\n body: { boardId, cardId },\n ...(await scopeOfBoard(boardId)),\n });\n return res.ok;\n }\n\n /**\n * Les membres de l'organisation — sans quoi assigner reviendrait à deviner\n * un identifiant.\n */\n async members(organizationId?: string): Promise<{ id: string; name: string; email: string }[]> {\n const orgId = await resolveOrganizationId(organizationId);\n // Better Auth n'a pas de route « les membres » : c'est l'organisation\n // entière qui les porte. Les écrans du cloud lisent la même.\n const res = must(\n await callCloud<{ members?: { userId: string; user?: { name?: string; email?: string } }[] }>(\n `/organization/get-full-organization?organizationId=${encodeURIComponent(orgId)}`,\n { accountId: await accountOfOrg(orgId) },\n ),\n );\n return (res.members ?? []).map((m) => ({\n id: m.userId,\n name: m.user?.name?.trim() || m.user?.email || m.userId,\n email: m.user?.email ?? '',\n }));\n }\n\n /**\n * Chercher des cartes — dans le cloud, pas ici.\n *\n * Cette recherche téléchargeait autrefois **tous les tableaux** de\n * l'organisation, entiers, pour filtrer ensuite en mémoire sur le titre et le\n * corps. Elle coûtait donc le poids du kanban complet à chaque appel, ne\n * voyait pas les commentaires — ils ne voyagent plus avec le tableau — et\n * ignorait les cartes rangées, absentes des colonnes.\n *\n * Le cloud fait la même recherche que ses propres écrans : titre, corps et\n * commentaires, sans casse ni accents.\n */\n async searchCards(input: {\n query: string;\n boardId?: string;\n limit?: number;\n organizationId?: string;\n }): Promise<{ cards: Record<string, unknown>[]; total: number; truncated: boolean }> {\n const orgId = await resolveOrganizationId(input.organizationId);\n const query = new URLSearchParams({ organizationId: orgId, q: input.query });\n if (input.boardId) query.set('boardId', input.boardId);\n if (input.limit) query.set('limit', String(input.limit));\n const res = must(\n await callCloud<{ cards: Record<string, unknown>[]; total: number; truncated: boolean }>(\n `/kanban/card/search?${query.toString()}`,\n { accountId: await accountOfOrg(orgId) },\n ),\n );\n return { cards: res.cards ?? [], total: res.total ?? 0, truncated: Boolean(res.truncated) };\n }\n\n /** Déplacer une carte, sans réécrire le tableau. */\n async moveCard(boardId: string, cardId: string, toColumnId: string): Promise<boolean> {\n const res = await callCloud<{ board: CloudBoard }>('/kanban/card/move', {\n method: 'POST',\n body: { boardId, cardId, toColumnId },\n ...(await scopeOfBoard(boardId)),\n });\n return res.ok;\n }\n\n /**\n * La filiation des cartes d'un tableau, dans les deux sens.\n *\n * Elle vit hors du tableau — une sauvegarde de tableau ne la transporte pas —\n * donc elle se demande à part. `cards` nomme les cartes liées qui vivent sur\n * un autre tableau ; celles d'ici sont déjà dans le tableau.\n */\n async cardLinks(boardId: string): Promise<{ links: KanbanCardLink[]; cards: KanbanCardRef[] }> {\n const res = await callCloud<{ links: KanbanCardLink[]; cards: KanbanCardRef[] }>(\n `/kanban/card-link/list?boardId=${encodeURIComponent(boardId)}`,\n await scopeOfBoard(boardId),\n );\n return { links: res.data?.links ?? [], cards: res.data?.cards ?? [] };\n }\n\n /**\n * Rattacher une carte à un parent, ou l'en détacher avec `parent: null`.\n *\n * Le parent peut vivre sur un autre tableau de la même organisation. Le cloud\n * refuse un parent qui descendrait déjà de la carte : l'arbre reste un arbre.\n */\n async setCardParent(\n boardId: string,\n cardId: string,\n parent: { boardId: string; cardId: string } | null,\n ): Promise<boolean> {\n const res = await callCloud<{ link: KanbanCardLink | null }>('/kanban/card-link/set', {\n method: 'POST',\n body: {\n boardId,\n cardId,\n parentBoardId: parent?.boardId ?? null,\n parentCardId: parent?.cardId ?? null,\n },\n ...(await scopeOfBoard(boardId)),\n });\n return res.ok;\n }\n\n async create(input: CreateKanbanBoardInput): Promise<KanbanBoard | null> {\n const organizationId = await resolveOrganizationId();\n if (!organizationId) return null;\n const res = await callCloud<{ board: CloudBoard }>('/kanban/board/create', {\n method: 'POST',\n accountId: await accountOfOrg(organizationId),\n body: {\n organizationId,\n name: input.name,\n description: input.description ?? '',\n instructions: input.instructions ?? '',\n },\n });\n return res.data?.board ?? null;\n }\n\n async update(input: UpdateKanbanBoardInput): Promise<KanbanBoard | null> {\n const res = await callCloud<{ board: CloudBoard }>('/kanban/board/update', {\n method: 'POST',\n body: input,\n ...(await scopeOfBoard(input.id)),\n });\n return res.data?.board ?? null;\n }\n\n async delete(id: string): Promise<boolean> {\n const res = await callCloud<{ success: boolean }>('/kanban/board/delete', {\n method: 'POST',\n body: { id },\n ...(await scopeOfBoard(id)),\n });\n return res.data?.success ?? false;\n }\n}\n\nexport const kanbanStore = new KanbanStore();\n","import { kanbanStore } from './kanban-store.js';\nimport type { KanbanBoard, KanbanCardEvent, KanbanCardLink } from '@runeya/packages-shared';\n\n// ─── Command parsing ────────────────────────────────────────────────────────\n\nexport interface KanbanCommand {\n type: 'move_card' | 'add_comment' | 'add_card';\n /** Target column title (for move_card, add_card) */\n columnTitle?: string;\n /** Comment text (for add_comment) */\n comment?: string;\n /** Card title (for add_card) */\n cardTitle?: string;\n /** Card content / markdown (for add_card) */\n cardContent?: string;\n}\n\n// ─── Command execution ──────────────────────────────────────────────────────\n\nexport async function executeKanbanCommands(\n boardId: string,\n cardId: string,\n commands: KanbanCommand[],\n conversationId?: string,\n): Promise<void> {\n if (commands.length === 0) return;\n\n // Retry loading the board a few times — the card may not have been persisted yet\n // when the conversation was launched before the card was fully saved.\n let board = await kanbanStore.get(boardId);\n if (!board) {\n console.warn(`[kanban-commands] Board ${boardId} not found, skipping ${commands.length} command(s)`);\n return;\n }\n\n // Check if card exists in the board; if not, retry a few times with a short delay\n const cardExistsInBoard = () => board!.columns.some(col => col.cards.some(c => c.id === cardId));\n if (!cardExistsInBoard()) {\n for (let attempt = 1; attempt <= 5; attempt++) {\n console.warn(`[kanban-commands] Card ${cardId} not found in board, retry ${attempt}/5...`);\n await new Promise(r => setTimeout(r, 500 * attempt));\n board = await kanbanStore.get(boardId);\n if (!board) return;\n if (cardExistsInBoard()) break;\n }\n if (!cardExistsInBoard()) {\n console.error(`[kanban-commands] Card ${cardId} still not found after retries, skipping ${commands.length} command(s)`);\n return;\n }\n }\n\n // Plus aucune commande ne réécrit le tableau : chacune passe par sa route\n // ciblée, qui ne fait pas circuler le graphe — et ne l'écrase donc pas.\n for (const cmd of commands) {\n if (cmd.type === 'move_card' && cmd.columnTitle) {\n const target = board.columns.find(\n (c) => c.title.toLowerCase() === cmd.columnTitle!.toLowerCase(),\n );\n if (!target) {\n console.warn(`[kanban-commands] Target column \"${cmd.columnTitle}\" not found. Available: ${board.columns.map(c => c.title).join(', ')}`);\n continue;\n }\n // Route ciblée : trois identifiants montent, le graphe ne bouge pas.\n const ok = await kanbanStore.moveCard(board.id, cardId, target.id);\n if (!ok) console.warn(`[kanban-commands] move_card failed: card=${cardId} target=\"${cmd.columnTitle}\"`);\n } else if (cmd.type === 'add_comment' && cmd.comment) {\n const ok = await kanbanStore.addComment(board.id, cardId, cmd.comment, conversationId);\n if (!ok) console.warn(`[kanban-commands] add_comment failed: card=${cardId}`);\n } else if (cmd.type === 'add_card' && cmd.columnTitle && cmd.cardTitle) {\n // Route ciblée, comme le déplacement : la carte s'ajoute seule, sans que\n // le tableau que cette conversation a lu au début reparte l'écraser.\n await addCardOrFallback(board, cmd.columnTitle, cmd.cardTitle, cmd.cardContent);\n }\n }\n}\n\n/**\n * Execute board-level commands (no card context needed).\n * Used by the AI populate feature.\n */\nexport async function executeKanbanBoardCommands(\n boardId: string,\n commands: KanbanCommand[],\n): Promise<void> {\n if (commands.length === 0) return;\n\n const board = await kanbanStore.get(boardId);\n if (!board) return;\n\n for (const cmd of commands) {\n if (cmd.type === 'add_card' && cmd.columnTitle && cmd.cardTitle) {\n await addCardOrFallback(board, cmd.columnTitle, cmd.cardTitle, cmd.cardContent);\n }\n }\n}\n\n\n/**\n * Poser une carte demandée par l'IA.\n *\n * Il y avait ici un repli pour un cloud sans `/kanban/card/add` : réécrire le\n * tableau entier avec la carte en plus. Il ne pouvait déjà plus rien faire — le\n * cloud n'accepte plus `columns` sur `board/update`, et Zod le retirait en\n * silence. Il est retiré pour de bon maintenant que les cartes vivent dans leur\n * propre collection : ce chemin n'écrirait plus dans le vide, il écrirait à\n * côté, dans un champ que plus aucune lecture ne regarde.\n *\n * Un cloud trop ancien pour la route ciblée n'est donc plus rattrapable ici, et\n * c'est honnête : il faut le mettre à jour, pas le contourner.\n */\nasync function addCardOrFallback(\n board: KanbanBoard,\n columnTitle: string,\n cardTitle: string,\n cardContent?: string,\n): Promise<void> {\n const { card, unsupported } = await kanbanStore.addCard(board.id, {\n column: columnTitle,\n title: cardTitle,\n markdown: cardContent,\n });\n if (card) return;\n console.warn(\n unsupported\n ? `[kanban-commands] cloud sans /kanban/card/add — carte « ${cardTitle} » non créée`\n : `[kanban-commands] add_card failed: column=\"${columnTitle}\"`,\n );\n}\n\n// ─── System prompt for kanban context ───────────────────────────────────────\n\n/** \"240 Ko\" / \"1,2 Mo\" — the agent only needs the order of magnitude. */\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} o`;\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} Ko`;\n return `${(bytes / (1024 * 1024)).toFixed(1).replace('.', ',')} Mo`;\n}\n\nexport function generateKanbanSystemPrompt(\n board: KanbanBoard,\n cardId: string,\n /**\n * Le journal de la carte, lu à part. Les lectures de tableau ne le portent\n * plus — il grossit sans fin — alors l'appelant le demande pour la seule\n * carte dont il parle. Absent, on retombe sur celui que la carte porte, ce\n * qui garde les anciens appels honnêtes.\n */\n history?: KanbanCardEvent[],\n /**\n * Les liens de filiation du tableau. Seul le *nombre* entre dans le prompt :\n * un agent doit savoir qu'une carte en attend une autre, pas relire l'arbre\n * — s'il en a besoin, il le demande avec `kanban_get_card_links`.\n */\n links?: KanbanCardLink[],\n): string {\n let currentColTitle = '';\n let card = null;\n for (const col of board.columns) {\n const found = col.cards.find(c => c.id === cardId);\n if (found) {\n currentColTitle = col.title;\n card = found;\n break;\n }\n }\n\n const sortedColumns = [...board.columns].sort((a, b) => a.order - b.order);\n\n const lines: string[] = [];\n lines.push('## Kanban Board Context');\n lines.push('');\n lines.push(`Tu travailles dans le contexte d'une carte Kanban.`);\n lines.push(`- **Board** : ${board.name}`);\n if (card) {\n lines.push(`- **Carte** : ${card.title}`);\n lines.push(`- **Colonne actuelle** : \\`${currentColTitle}\\``);\n }\n lines.push('');\n\n // Le mode d'emploi écrit par l'équipe. Il passe avant les règles génériques\n // plus bas : c'est lui qui dit ce que veulent dire les colonnes de *ce*\n // tableau, et quand une carte a le droit d'avancer.\n if (board.description) {\n lines.push(`### Description du tableau`);\n lines.push('');\n lines.push(board.description);\n lines.push('');\n }\n if (board.instructions) {\n lines.push('### Fonctionnement de ce tableau (consignes de l\\'équipe)');\n lines.push('');\n lines.push('> Ces consignes priment sur les règles générales plus bas en cas de conflit.');\n lines.push('');\n lines.push(board.instructions);\n lines.push('');\n }\n\n // List columns explicitly so the AI knows the exact names to use\n lines.push('### Colonnes disponibles (noms exacts à utiliser) :');\n for (const col of sortedColumns) {\n const cardCount = col.cards.length;\n const isCurrent = card && col.cards.some(c => c.id === cardId);\n lines.push(`- \\`${col.title}\\` (${cardCount} carte${cardCount !== 1 ? 's' : ''})${isCurrent ? ' ← **colonne actuelle**' : ''}`);\n }\n lines.push('');\n\n // Inject existing comments\n if (card) {\n const events = history ?? card.history ?? [];\n const comments = events.filter(e => e.type === 'comment');\n if (comments.length > 0) {\n lines.push('### Commentaires existants sur cette carte :');\n lines.push('');\n lines.push('> Instantané pris au lancement de cette tâche. Si on te dit qu\\'un commentaire a été ajouté depuis, relis le fil avec `kanban_get_card_comments` — ne réponds pas depuis cette liste.');\n lines.push('');\n for (const c of comments.slice(-20)) { // Last 20 comments max\n const who = c.author === 'ai' ? 'AI' : 'Utilisateur';\n const date = new Date(c.timestamp).toLocaleString('fr-FR', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });\n const files = (c.attachments ?? []).map(f => `\\`${f.id}\\` ${f.name}`).join(', ');\n lines.push(`- **${who}** (${date}) : ${c.detail}${files ? ` — pièces jointes : ${files}` : ''}`);\n }\n lines.push('');\n }\n }\n\n // Attachments are stored out of band: the prompt lists what exists, and the\n // agent pulls the bytes itself with `kanban_download_attachment`.\n if (card) {\n const files = card.attachments ?? [];\n const commentFiles = (history ?? card.history ?? []).flatMap(e => e.attachments ?? []);\n const all = [...files, ...commentFiles];\n if (all.length > 0) {\n lines.push(`### Pièces jointes disponibles (${all.length})`);\n lines.push('');\n for (const f of all) {\n lines.push(`- \\`${f.id}\\` — ${f.name} (${f.contentType}, ${formatSize(f.size)})`);\n }\n lines.push('');\n lines.push('> Ces fichiers ne sont **pas** dans ce prompt. Appelle `kanban_download_attachment(attachmentId)` pour en récupérer un sur le disque, puis ouvre le chemin renvoyé avec tes outils de lecture de fichiers.');\n lines.push('');\n }\n }\n\n // La filiation : annoncée, jamais dépliée. Un parent et ses sous-cartes\n // peuvent porter des pages entières ; les recopier ici gonflerait le prompt\n // de tout ce que l'agent n'a pas demandé. Le compte suffit à lui faire lever\n // la tête, et l'outil est là pour la suite.\n if (card && links?.length) {\n const parent = links.find(l => l.childCardId === card.id);\n const children = links.filter(l => l.parentCardId === card.id);\n if (parent || children.length) {\n lines.push('### Cartes liées');\n lines.push('');\n if (parent) lines.push('- Cette carte **dépend d\\'une carte parente**.');\n if (children.length) {\n lines.push(`- **${children.length} sous-carte${children.length !== 1 ? 's' : ''}** dépend${children.length !== 1 ? 'ent' : ''} de cette carte.`);\n }\n lines.push('');\n lines.push('> Leur contenu n\\'est **pas** dans ce prompt. Appelle `kanban_get_card_links(boardId)` pour obtenir les identifiants des cartes liées, puis lis-les avec `kanban_get_board` ou `kanban_get_card_comments`.');\n lines.push('');\n }\n }\n\n lines.push('### Outils Kanban disponibles');\n lines.push('');\n lines.push(`- **boardId** (à passer dans tous les outils) : \\`${board.id}\\``);\n if (card) lines.push(`- **cardId** (carte actuelle) : \\`${card.id}\\``);\n lines.push('');\n lines.push('Tu disposes des outils suivants pour interagir avec le board :');\n lines.push('- `kanban_move_card(boardId, cardId, targetColumn)` — Déplacer la carte vers une autre colonne');\n lines.push('- `kanban_add_comment(boardId, cardId, comment)` — Ajouter un commentaire sur la carte');\n lines.push('- `kanban_get_card_comments(boardId, cardId)` — **Relire** le fil de commentaires à jour');\n lines.push('- `kanban_download_attachment(attachmentId)` — Télécharger une pièce jointe et obtenir son chemin local');\n lines.push('- `kanban_get_card_links(boardId)` — Lire la filiation des cartes du tableau (parent ↔ sous-cartes)');\n lines.push('- `kanban_set_card_parent(boardId, cardId, parentCardId?)` — Rattacher une carte à une carte parente, ou l\\'en détacher');\n lines.push('');\n lines.push('### OBLIGATOIRE — Décision Kanban');\n lines.push('');\n lines.push('À chaque réponse, tu DOIS obligatoirement faire les deux actions suivantes :');\n lines.push('');\n lines.push('**1. Évaluer et déplacer la carte si nécessaire** via `kanban_move_card` :');\n lines.push(' - **Au début** si tu sais déjà que tu vas travailler activement (ex. tu passes en `In progress`).');\n lines.push(' - **À la fin** pour une décision forte après analyse complète (ex. tu conclus que c\\'est `Done`).');\n lines.push(' - Si tu ne fais qu\\'analyser ou poser des questions → **ne pas déplacer**.');\n lines.push('');\n lines.push(' > ⚠️ **CRITIQUE** : En fin de réponse, tu DOIS TOUJOURS évaluer si la colonne actuelle est encore correcte.');\n lines.push('');\n lines.push('**2. Appeler `kanban_add_comment`** résumant ce que tu as fait ou constaté (**à la fin** de ta réponse).');\n lines.push('');\n lines.push('> 🚫 **INTERDIT** : Terminer une réponse sans appeler `kanban_add_comment`. C\\'est non négociable.');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Le contexte d'une carte, tel que l'agent le reçoit.\n *\n * Rassemble ici ce que le tableau ne transporte plus — le journal de la carte,\n * la filiation — pour que les quatre endroits qui lancent un agent n'aient pas\n * à se souvenir chacun de les demander (ni à oublier l'un des deux).\n */\nexport async function buildKanbanCardPrompt(board: KanbanBoard, cardId: string): Promise<string> {\n const [history, links] = await Promise.all([\n kanbanStore.cardHistory(board.id, cardId),\n kanbanStore.cardLinks(board.id).then(r => r.links),\n ]);\n return generateKanbanSystemPrompt(board, cardId, history, links);\n}\n\n// ─── Board-level populate prompt ─────────────────────────────────────────────\n\nexport function generateKanbanPopulatePrompt(board: KanbanBoard): string {\n const lines: string[] = [];\n lines.push('## Kanban Board — Mode Populate');\n lines.push('');\n lines.push(`Tu es en mode **populate** sur le tableau Kanban **\"${board.name}\"**.`);\n lines.push('Tu es un outil de gestion de projet. Quand l\\'utilisateur dit \"créer une card\", \"ajouter une tâche\", \"créer une carte\", ou toute demande similaire, il parle EXCLUSIVEMENT d\\'une carte Kanban à ajouter au tableau — PAS d\\'un composant UI, PAS de code HTML ou CSS, PAS d\\'un composant React/Vue/Angular.');\n lines.push('');\n lines.push('Ton UNIQUE rôle est de créer des cartes Kanban pertinentes en réponse à la demande de l\\'utilisateur.');\n lines.push('Tu ne dois PAS répondre à la question, ni donner d\\'explication, ni écrire du code, ni montrer de composant.');\n lines.push('Tu dois UNIQUEMENT produire des tags XML `<kanban_add_card>` pour créer des cartes dans le tableau.');\n if (board.description) {\n lines.push('');\n lines.push(`Description du board : ${board.description}`);\n }\n if (board.instructions) {\n lines.push('');\n lines.push('### Fonctionnement de ce tableau (consignes de l\\'équipe)');\n lines.push('');\n lines.push('> Ces consignes priment sur les règles générales en cas de conflit — sauf sur l\\'interdiction de répondre autrement que par des cartes.');\n lines.push('');\n lines.push(board.instructions);\n }\n lines.push('');\n\n const sortedCols = [...board.columns].sort((a, b) => a.order - b.order);\n\n lines.push('### Colonnes existantes (noms exacts à utiliser) :');\n for (const col of sortedCols) {\n const cardCount = col.cards.length;\n lines.push(`- \\`${col.title}\\` (${cardCount} carte${cardCount !== 1 ? 's' : ''})`);\n for (const card of col.cards) {\n lines.push(` - ${card.title}`);\n }\n }\n lines.push('');\n\n lines.push('### Outil disponible');\n lines.push('');\n lines.push(`- **boardId** (à passer dans tous les outils) : \\`${board.id}\\``);\n lines.push('');\n lines.push('Pour chaque carte à créer, appelle l\\'outil `kanban_add_card(boardId, column, title, content)` :');\n lines.push('- `column` : nom exact de la colonne cible');\n lines.push('- `title` : titre concis de la carte');\n lines.push('- `content` : contenu markdown détaillé et actionnable');\n lines.push('');\n lines.push('### Règles STRICTES');\n lines.push('');\n lines.push('1. **NE RÉPONDS PAS** à la question de l\\'utilisateur. Ne donne aucune explication, aucun texte libre.');\n lines.push('2. **Appelle uniquement** `kanban_add_card`, rien d\\'autre.');\n lines.push('3. Tu ne peux PAS créer de nouvelles colonnes. Dispatch les cartes dans les colonnes existantes listées ci-dessus.');\n lines.push('4. Utilise les noms **exacts** des colonnes.');\n lines.push('5. Chaque carte doit avoir un titre concis et un contenu markdown détaillé et actionnable.');\n lines.push('6. Décompose la demande de l\\'utilisateur en tâches concrètes, chacune étant une carte.');\n lines.push('7. Dispatch chaque carte dans la colonne la plus appropriée selon sa nature.');\n lines.push('');\n\n return lines.join('\\n');\n}\n","import { spawn } from 'node:child_process';\nimport { parseEndToken, TOKEN_INSTRUCTIONS } from './scenario-tokens.js';\nimport { runClaudeViaPty } from './claude-pty-runner.js';\nimport { resolveCliInvocation } from '@runeya/packages-shared/node';\nimport type { AiRunner, AiRunnerParams, NodeResult } from './ai-runner.js';\n\n// ─── Claude stream parser ──────────────────────────────────────────────────\n\ninterface ClaudeStreamState {\n fullContent: string;\n thinkingEmitted: boolean;\n textDeltaReceived: boolean;\n currentToolName: string;\n currentToolId: string;\n currentToolInputJson: string;\n toolInputAccumulator: Map<string, { name: string; json: string }>;\n actualSessionId: string | undefined;\n}\n\nfunction createClaudeStreamState(): ClaudeStreamState {\n return {\n fullContent: '',\n thinkingEmitted: false,\n textDeltaReceived: false,\n currentToolName: '',\n currentToolId: '',\n currentToolInputJson: '',\n toolInputAccumulator: new Map(),\n actualSessionId: undefined,\n };\n}\n\ninterface ClaudeStreamCallbacks {\n onDelta: (text: string) => void;\n onStatus: (text: string) => void;\n onThinking?: (text: string) => void;\n onToolCall?: (id: string, name: string, input: Record<string, unknown>) => void;\n onToolResult?: (id: string, name: string, result: string, isError: boolean) => void;\n onContextUsage?: (inputTokens: number, contextWindow: number) => void;\n onResult?: (result: string, sessionId: string | undefined, isError: boolean) => void;\n}\n\nfunction parseClaudeStreamLine(\n line: string,\n state: ClaudeStreamState,\n cb: ClaudeStreamCallbacks,\n): Record<string, unknown> | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n let event: Record<string, unknown>;\n try { event = JSON.parse(trimmed) as Record<string, unknown>; } catch { return null; }\n\n if (event['type'] === 'stream_event') {\n const ev = event['event'] as Record<string, unknown> | undefined;\n const evType = ev?.['type'];\n const delta = ev?.['delta'] as Record<string, unknown> | undefined;\n const contentBlock = ev?.['content_block'] as Record<string, unknown> | undefined;\n\n if (delta?.['type'] === 'thinking_delta' && typeof delta['thinking'] === 'string') {\n if (!state.thinkingEmitted) {\n state.thinkingEmitted = true;\n cb.onStatus('💭 En réflexion…');\n }\n cb.onThinking?.(delta['thinking'] as string);\n }\n\n if (evType === 'content_block_start' && contentBlock?.['type'] === 'tool_use') {\n state.currentToolName = typeof contentBlock['name'] === 'string' ? contentBlock['name'] : 'tool';\n state.currentToolId = typeof contentBlock['id'] === 'string' ? contentBlock['id'] : '';\n state.currentToolInputJson = '';\n if (state.currentToolId) {\n state.toolInputAccumulator.set(state.currentToolId, { name: state.currentToolName, json: '' });\n }\n cb.onStatus(`🔧 ${state.currentToolName}`);\n }\n\n if (delta?.['type'] === 'input_json_delta' && typeof delta['partial_json'] === 'string') {\n state.currentToolInputJson += delta['partial_json'] as string;\n if (state.currentToolId) {\n const acc = state.toolInputAccumulator.get(state.currentToolId);\n if (acc) acc.json = state.currentToolInputJson;\n }\n }\n\n if (evType === 'content_block_stop' && state.currentToolId && state.toolInputAccumulator.has(state.currentToolId)) {\n const acc = state.toolInputAccumulator.get(state.currentToolId)!;\n let parsedInput: Record<string, unknown> = {};\n try { parsedInput = JSON.parse(acc.json || '{}') as Record<string, unknown>; } catch { /* keep empty */ }\n cb.onToolCall?.(state.currentToolId, acc.name, parsedInput);\n state.currentToolId = '';\n state.currentToolInputJson = '';\n }\n\n if (delta?.['type'] === 'text_delta' && typeof delta['text'] === 'string') {\n state.textDeltaReceived = true;\n state.thinkingEmitted = false;\n state.fullContent += delta['text'];\n cb.onStatus('');\n cb.onDelta(delta['text']);\n }\n\n if (evType === 'message_start') {\n const msg = ev?.['message'] as Record<string, unknown> | undefined;\n const usage = msg?.['usage'] as Record<string, unknown> | undefined;\n if (usage && typeof usage['input_tokens'] === 'number' && cb.onContextUsage) {\n const inputTokens = (usage['input_tokens'] as number)\n + (typeof usage['cache_read_input_tokens'] === 'number' ? usage['cache_read_input_tokens'] : 0)\n + (typeof usage['cache_creation_input_tokens'] === 'number' ? usage['cache_creation_input_tokens'] : 0);\n const modelName = typeof msg?.['model'] === 'string' ? msg['model'] : '';\n const contextWindow = /claude-instant/i.test(modelName) ? 100_000 : 200_000;\n cb.onContextUsage(inputTokens, contextWindow);\n }\n }\n }\n\n if (event['type'] === 'user') {\n const msg = event['message'] as Record<string, unknown> | undefined;\n const content = msg?.['content'];\n const blocks = Array.isArray(content) ? content as Record<string, unknown>[] : [];\n for (const block of blocks) {\n if (block['type'] === 'tool_result' && typeof block['tool_use_id'] === 'string') {\n const id = block['tool_use_id'] as string;\n const isError = block['is_error'] === true;\n const blockContent = block['content'];\n let result = '';\n if (typeof blockContent === 'string') {\n result = blockContent;\n } else if (Array.isArray(blockContent)) {\n result = (blockContent as Record<string, unknown>[])\n .filter(b => b['type'] === 'text')\n .map(b => b['text'] as string)\n .join('');\n }\n const toolName = state.toolInputAccumulator.get(id)?.name ?? '';\n cb.onToolResult?.(id, toolName, result, isError);\n }\n }\n }\n\n if (event['type'] === 'result') {\n if (typeof event['session_id'] === 'string' && event['session_id']) {\n state.actualSessionId = event['session_id'];\n }\n const isError = event['is_error'] === true;\n const resultText = typeof event['result'] === 'string' ? event['result'] : '';\n if (!state.textDeltaReceived && resultText) {\n state.fullContent += resultText;\n cb.onStatus('');\n cb.onDelta(resultText);\n }\n cb.onResult?.(resultText, state.actualSessionId, isError);\n }\n\n return event;\n}\n\nconst EFFORT_THINKING_BUDGET: Record<string, number> = {\n low: 0,\n normal: 5000,\n high: 10000,\n};\n\n/** Build CLI args, env, and stdinData from runner params. Shared by runLocal and runViaAgent.\n * When `excludeMcp` is true, omit --mcp-config from args (agent builds it locally). */\nfunction buildClaudeSpawnConfig(params: AiRunnerParams, opts?: { excludeMcp?: boolean }) {\n const { sessionId, prompt, isResume, systemContext, apiToken, model, effort, images, mcpConfig, skipTokenInstructions, injectedEnv } = params;\n\n const hasImages = images && images.length > 0;\n const sessionArg = isResume\n ? ['--resume', sessionId]\n : (sessionId ? ['--session-id', sessionId] : []);\n\n const thinkingBudget = EFFORT_THINKING_BUDGET[effort] ?? 0;\n const thinkingArgs = thinkingBudget > 0 ? ['--thinking', 'enabled'] : [];\n\n const systemPrompt = skipTokenInstructions\n ? systemContext\n : `${systemContext}\\n\\n${TOKEN_INSTRUCTIONS}`;\n\n const inputArgs = hasImages\n ? ['--input-format', 'stream-json']\n : ['-p', prompt];\n\n // Runeya declares its own MCP server; the CLI additionally loads whatever the\n // user has in ~/.claude.json. That costs a few seconds of boot per message on\n // a machine with many servers, but keeping the user's tools available is worth\n // it — do NOT add `--strict-mcp-config` here without a way to opt out.\n const mcpArgs = (mcpConfig && !opts?.excludeMcp) ? ['--mcp-config', mcpConfig] : [];\n\n // Modèle vide = on laisse le CLI utiliser celui configuré par l'utilisateur.\n const modelArgs = model ? ['--model', model] : [];\n\n const args = [\n ...sessionArg,\n '--output-format', 'stream-json',\n '--verbose',\n '--include-partial-messages',\n '--dangerously-skip-permissions',\n ...modelArgs,\n ...thinkingArgs,\n '--append-system-prompt', systemPrompt,\n ...mcpArgs,\n ...inputArgs,\n ];\n\n // Env: unset CLAUDECODE, pass apiToken via env var\n const env: Record<string, string> = {\n ...Object.fromEntries(Object.entries(process.env).filter(([k]) => k !== 'CLAUDECODE')) as Record<string, string>,\n ...injectedEnv,\n ...(apiToken ? { RUNEYA_API_TOKEN: apiToken } : {}),\n };\n\n // Build stdin data for images\n let stdinData: string | undefined;\n if (hasImages) {\n const contentBlocks: unknown[] = [{ type: 'text', text: prompt }];\n for (const img of images!) {\n contentBlocks.push({\n type: 'image',\n source: { type: 'base64', media_type: img.mimeType, data: img.base64 },\n });\n }\n stdinData = JSON.stringify({ role: 'user', content: contentBlocks }) + '\\n';\n }\n\n return { args, env, stdinData, sessionArg, hasImages };\n}\n\n/** Mutable state shared across stdout processing calls. */\ninterface ClaudeProcessingCtx {\n settled: boolean;\n earlyResult: NodeResult | null;\n lastResultError: string;\n rawStdoutLines: string[];\n}\n\n/** Process raw stdout lines from the claude CLI stream. Shared by runLocal and runViaAgent. */\nfunction processClaudeStdoutLines(\n lines: string[],\n streamState: ClaudeStreamState,\n params: AiRunnerParams,\n ctx: ClaudeProcessingCtx,\n): void {\n const { onDelta, onStatus, onThinking, onToolCall, onToolResult, skipTokenInstructions } = params;\n\n const settle = (result: NodeResult) => {\n if (ctx.settled) return;\n ctx.settled = true;\n ctx.earlyResult = result;\n // For local runs, don't remove abort listener here — let the process finish\n // to capture actualSessionId from the result event.\n };\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n ctx.rawStdoutLines.push(trimmed);\n if (ctx.rawStdoutLines.length > 50) ctx.rawStdoutLines.shift();\n\n if (ctx.settled) {\n try {\n const ev = JSON.parse(trimmed) as Record<string, unknown>;\n if (ev['type'] === 'result' && typeof ev['session_id'] === 'string') {\n streamState.actualSessionId = ev['session_id'];\n }\n if (ev['type'] === 'result' && ev['is_error'] === true) {\n const errMsg = typeof ev['error'] === 'string' ? ev['error']\n : typeof (ev['error'] as Record<string, unknown>)?.['message'] === 'string' ? (ev['error'] as Record<string, unknown>)['message'] as string : '';\n if (errMsg) ctx.lastResultError = errMsg.slice(-2000);\n }\n } catch { /* ignore */ }\n continue;\n }\n\n const event = parseClaudeStreamLine(line, streamState, {\n onDelta: (text) => {\n onDelta(text);\n if (!skipTokenInstructions) {\n const { status, message, branchChoice } = parseEndToken(streamState.fullContent);\n if (status !== 'none') {\n settle({ output: streamState.fullContent, tokenStatus: status, tokenMessage: message, branchChoice, actualSessionId: streamState.actualSessionId });\n }\n }\n },\n onStatus,\n onThinking,\n onToolCall: (id, name, input) => onToolCall?.(id, name, input),\n onToolResult: (id, _name, result, isError) => onToolResult?.(id, result, isError),\n onContextUsage: params.onContextUsage,\n onResult: (result, _sessionId, isError) => {\n if (isError) {\n const errMsg = typeof result === 'string' ? result : '';\n if (errMsg) ctx.lastResultError = errMsg.slice(-2000);\n }\n if (result) params.onResult?.(result);\n },\n });\n\n if (!event) {\n if (!ctx.lastResultError) ctx.lastResultError = trimmed.slice(-2000);\n }\n }\n}\n\nexport class ClaudeCodeRunner implements AiRunner {\n run(params: AiRunnerParams): Promise<NodeResult> {\n // The CLI always runs in an interactive PTY — there is no headless `-p`\n // mode anymore (it would switch billing from the subscription to the API).\n // The PTY lives on the agent (it owns node-pty); without an agent we can\n // only fall back to the local spawn path.\n if (params.agentId) {\n return runClaudeViaPty(params);\n }\n return this.runLocal(params);\n }\n\n // ── Run locally (original implementation) ───────────────────────────────\n\n private runLocal(params: AiRunnerParams): Promise<NodeResult> {\n const { signal, skipTokenInstructions } = params;\n const { args, env, stdinData, sessionArg, hasImages: _hasImages } = buildClaudeSpawnConfig(params);\n\n return new Promise<NodeResult>((resolve, reject) => {\n const totalArgSize = args.reduce((s, a) => s + a.length + 1, 0);\n console.log(`[claude-code-runner] arg size: ${totalArgSize} bytes (${(totalArgSize / 1024 / 1024).toFixed(2)} MB), session: ${sessionArg.join(' ')}, images: ${params.images?.length ?? 0}`);\n const displayArgs = args.map(a => {\n if (a === params.prompt) return '<prompt>';\n if (a.includes(TOKEN_INSTRUCTIONS)) return '<system-context>';\n return a;\n });\n params.onCommand?.(`claude ${displayArgs.join(' ')}`);\n // Windows : `claude` est un shim `.cmd`, que `shell: false` ne sait pas\n // lancer. Le helper partagé rend la commande inchangée sur POSIX.\n const invocation = resolveCliInvocation('claude', args);\n const proc = spawn(invocation.command, invocation.args, {\n windowsHide: true,\n stdio: ['pipe', 'pipe', 'pipe'],\n shell: false,\n env,\n });\n\n if (stdinData) {\n proc.stdin!.write(stdinData);\n proc.stdin!.end();\n } else {\n proc.stdin!.end();\n }\n\n const streamState = createClaudeStreamState();\n const ctx: ClaudeProcessingCtx = { settled: false, earlyResult: null, lastResultError: '', rawStdoutLines: [] };\n let stdoutBuffer = '';\n\n const onAbort = () => {\n try { proc.kill('SIGTERM'); } catch { /* ignore */ }\n };\n signal.addEventListener('abort', onAbort, { once: true });\n\n let stderrBuffer = '';\n\n proc.stdout!.on('data', (chunk: Buffer) => {\n stdoutBuffer += chunk.toString('utf-8');\n const lines = stdoutBuffer.split('\\n');\n stdoutBuffer = lines.pop() ?? '';\n processClaudeStdoutLines(lines, streamState, params, ctx);\n });\n\n proc.stderr!.on('data', (chunk: Buffer) => { stderrBuffer += chunk.toString('utf-8'); });\n\n proc.on('close', (code) => {\n if (ctx.earlyResult) { resolve({ ...ctx.earlyResult, actualSessionId: streamState.actualSessionId }); return; }\n\n signal.removeEventListener('abort', onAbort);\n if (signal.aborted) { reject(new Error('Aborted')); return; }\n if (code !== 0 && !streamState.fullContent) {\n const stderr = stderrBuffer.trim().slice(-2000);\n const rawStdout = ctx.rawStdoutLines.slice(-10).join('\\n').trim();\n console.error(`[claude-code-runner] exit code ${code} | stderr: ${stderr || '(empty)'} | last stdout lines: ${rawStdout || '(empty)'}`);\n const detail = ctx.lastResultError || stderr || rawStdout;\n reject(new Error(`Claude Code exited with code ${code}${detail ? `: ${detail}` : ''}`));\n return;\n }\n const { status, message, branchChoice } = skipTokenInstructions\n ? { status: 'none' as const, message: '', branchChoice: undefined }\n : parseEndToken(streamState.fullContent);\n resolve({ output: streamState.fullContent, tokenStatus: status, tokenMessage: message, branchChoice, actualSessionId: streamState.actualSessionId });\n });\n\n proc.on('error', (err) => {\n if (ctx.settled) return;\n signal.removeEventListener('abort', onAbort);\n reject(err);\n });\n });\n }\n}\n\n","// ─── Token-based node completion signals ──────────────────────────────────\n//\n// Shared between all runners (ClaudeCodeRunner, CodexRunner) and the scenario\n// runner orchestration layer. Not used in chat mode.\n\nexport function parseEndToken(output: string): { status: 'pass' | 'error' | 'ask_user' | 'none'; message: string; branchChoice?: number } {\n const tail = output.trimEnd().slice(-2000);\n\n // <pass branch=\"N\" />\n const branchMatch = tail.match(/<pass\\s+branch=\"(\\d+)\"\\s*\\/>/);\n if (branchMatch) {\n return { status: 'pass', message: '', branchChoice: Number.parseInt(branchMatch[1], 10) };\n }\n\n // <pass />\n if (/<pass\\s*\\/>/.test(tail)) {\n return { status: 'pass', message: '' };\n }\n\n // <error>message</error>\n const errorMatch = tail.match(/<error>([\\s\\S]*?)<\\/error>/);\n if (errorMatch) {\n return { status: 'error', message: errorMatch[1].trim() };\n }\n\n // <ask_user>question</ask_user>\n const askMatch = tail.match(/<ask_user>([\\s\\S]*?)<\\/ask_user>/);\n if (askMatch) {\n return { status: 'ask_user', message: askMatch[1].trim() };\n }\n\n return { status: 'none', message: '' };\n}\n\nexport const TOKEN_INSTRUCTIONS = `\n## Instructions de signalisation (OBLIGATOIRE)\n\nÀ la fin de CHAQUE réponse, tu DOIS terminer par exactement l'un de ces tags XML :\n\n- Si tu as terminé avec succès (chemin par défaut) : \\`<pass />\\`\n- Si le nœud propose plusieurs branches de sortie numérotées : \\`<pass branch=\"1\" />\\`, \\`<pass branch=\"2\" />\\`, etc. (choisis la branche qui correspond le mieux au résultat)\n- Si tu as rencontré une erreur bloquante : \\`<error>description courte</error>\\`\n- Si tu es bloqué par une information que personne dans l'équipe ne peut fournir (secret, décision business, credential externe) : \\`<ask_user>ta question</ask_user>\\` — NE PAS utiliser pour demander une confirmation ou un accord (\"es-tu prêt ?\", \"puis-je continuer ?\")\n\nNe jamais omettre ce tag. Il doit être la toute dernière ligne de ta réponse.\n`.trim();\n","import { randomUUID } from 'node:crypto';\nimport { parseEndToken, TOKEN_INSTRUCTIONS } from './scenario-tokens.js';\nimport { tailTranscript, type TranscriptEntry } from './claude-pty-tail.js';\nimport type { AiRunnerParams, NodeResult } from './ai-runner.js';\n\nasync function getAgentManager() {\n const { agentManager } = await import('../agent-manager.js');\n return agentManager;\n}\n\ninterface ToolAcc { id: string; name: string }\n\n/**\n * Build claude argv for interactive (PTY) mode. The prompt is passed as a\n * positional argument so claude opens the TUI with it as the first message.\n * `-p` is intentionally omitted: that's the whole point of this runner —\n * stay on the subscription pricing tier.\n */\nfunction buildPtyArgs(params: AiRunnerParams, sessionId: string, opts: { excludeMcp: boolean }) {\n const { isResume, systemContext, model, mcpConfig, skipTokenInstructions, prompt } = params;\n\n const sessionArg = isResume ? ['--resume', sessionId] : ['--session-id', sessionId];\n\n const systemPrompt = skipTokenInstructions\n ? systemContext\n : `${systemContext}\\n\\n${TOKEN_INSTRUCTIONS}`;\n\n // Runeya declares its own MCP server; the CLI additionally loads whatever the\n // user has in ~/.claude.json. That costs a few seconds of boot per message on\n // a machine with many servers, but keeping the user's tools available is worth\n // it — do NOT add `--strict-mcp-config` here without a way to opt out.\n const mcpArgs = (mcpConfig && !opts.excludeMcp) ? ['--mcp-config', mcpConfig] : [];\n\n // Modèle vide = on laisse le CLI utiliser celui configuré par l'utilisateur.\n const modelArgs = model ? ['--model', model] : [];\n\n const args = [\n ...sessionArg,\n '--dangerously-skip-permissions',\n ...modelArgs,\n '--append-system-prompt', systemPrompt,\n ...mcpArgs,\n prompt,\n ];\n\n return args;\n}\n\n/**\n * Extract a delta of text/tool/thinking events from a JSONL entry written by\n * claude (interactive mode). Each entry is either a user / assistant message\n * or a system event. We fire callbacks similar to the headless stream-json\n * parser but at message granularity instead of token granularity.\n */\nfunction dispatchEntry(\n entry: TranscriptEntry,\n state: { assistantText: string; tools: Map<string, ToolAcc> },\n cbs: {\n onDelta: (text: string) => void;\n onStatus: (text: string) => void;\n onThinking?: (text: string) => void;\n onToolCall?: (id: string, name: string, input: Record<string, unknown>) => void;\n onToolResult?: (id: string, result: string, isError: boolean) => void;\n },\n): void {\n if (entry.type !== 'user' && entry.type !== 'assistant') return;\n const msg = entry.message;\n if (!msg) return;\n const role = msg.role ?? entry.type;\n const content = msg.content;\n\n if (typeof content === 'string') {\n if (role === 'assistant' && content) {\n state.assistantText += content;\n cbs.onStatus('');\n cbs.onDelta(content);\n }\n return;\n }\n if (!Array.isArray(content)) return;\n\n for (const raw of content) {\n if (!raw || typeof raw !== 'object') continue;\n const block = raw as {\n type?: string;\n text?: string;\n thinking?: 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 (block.type === 'text' && typeof block.text === 'string' && role === 'assistant') {\n state.assistantText += block.text;\n cbs.onStatus('');\n cbs.onDelta(block.text);\n } else if (block.type === 'thinking' && typeof block.thinking === 'string') {\n cbs.onStatus('💭 En réflexion…');\n cbs.onThinking?.(block.thinking);\n } else if (block.type === 'tool_use' && block.id) {\n const name = block.name ?? 'tool';\n state.tools.set(block.id, { id: block.id, name });\n cbs.onStatus(`🔧 ${name}`);\n cbs.onToolCall?.(block.id, name, block.input ?? {});\n } else if (block.type === 'tool_result' && typeof block.tool_use_id === 'string') {\n const c = block.content;\n let result = '';\n if (typeof c === 'string') result = c;\n else if (Array.isArray(c)) {\n result = (c as Array<Record<string, unknown>>)\n .filter((b) => b['type'] === 'text')\n .map((b) => (b['text'] as string) ?? '')\n .join('');\n }\n cbs.onToolResult?.(block.tool_use_id, result, block.is_error === true);\n }\n }\n}\n\n/**\n * Run a single node turn via an interactive claude PTY.\n *\n * Flow:\n * 1. Generate a fresh session-id (or reuse params.sessionId on resume).\n * 2. Spawn `claude <args> \"<prompt>\"` in a PTY on the agent.\n * 3. Tail the JSONL transcript Claude writes to ~/.claude/projects/.\n * 4. Re-emit each new entry as onDelta/onThinking/onToolCall/onToolResult.\n * 5. parseEndToken() on accumulated assistant text. On token → kill PTY,\n * resolve. Without TOKEN_INSTRUCTIONS, resolve on transcript settling.\n */\nexport async function runClaudeViaPty(params: AiRunnerParams): Promise<NodeResult> {\n const { agentId, signal, skipTokenInstructions } = params;\n if (!agentId) throw new Error('runClaudeViaPty requires an agentId');\n\n const sessionId = params.sessionId && params.sessionId.length > 0\n ? params.sessionId\n : randomUUID();\n const args = buildPtyArgs(params, sessionId, { excludeMcp: true });\n\n const agentMgr = await getAgentManager();\n\n const displayArgs = args.map((a) => {\n if (a === params.prompt) return '<prompt>';\n if (a.includes(TOKEN_INSTRUCTIONS)) return '<system-context>';\n return a;\n });\n params.onCommand?.(`claude ${displayArgs.join(' ')}`);\n\n return new Promise<NodeResult>((resolve, reject) => {\n let settled = false;\n let spawnId: string | null = null;\n const state = { assistantText: '', tools: new Map<string, ToolAcc>() };\n\n const innerAbort = new AbortController();\n const onParentAbort = () => innerAbort.abort();\n signal.addEventListener('abort', onParentAbort, { once: true });\n\n const cleanup = () => {\n signal.removeEventListener('abort', onParentAbort);\n innerAbort.abort();\n };\n\n const killSpawn = async () => {\n if (!spawnId) return;\n try { await agentMgr.agentMutation(agentId, 'ai.kill', { spawnId }); } catch { /* best-effort */ }\n };\n\n const settle = (result: NodeResult) => {\n if (settled) return;\n settled = true;\n cleanup();\n void killSpawn();\n resolve(result);\n };\n\n const fail = (err: Error) => {\n if (settled) return;\n settled = true;\n cleanup();\n void killSpawn();\n reject(err);\n };\n\n // Tail JSONL → dispatch entries → detect end-token.\n const tail = tailTranscript({\n sessionId,\n signal: innerAbort.signal,\n // Sur une reprise, le transcript porte déjà les tours précédents : les\n // relire ferait conclure ce tour-ci sur le `end_turn` d'un ancien.\n startAtEnd: params.isResume,\n onEntry: (entry) => {\n try {\n dispatchEntry(entry, state, {\n onDelta: params.onDelta,\n onStatus: params.onStatus,\n onThinking: params.onThinking,\n onToolCall: params.onToolCall,\n onToolResult: params.onToolResult,\n });\n } catch { /* never break the tail on a malformed event */ }\n\n // Sans token de fin, rien ne disait qu'un tour était terminé : le PTY\n // reste ouvert en attente d'une autre instruction, `whenDone` ne se\n // résout que sur abandon, et l'appelant attendait pour toujours — une\n // carte de la file kanban restait « en cours » indéfiniment, bloquant\n // les suivantes. Le transcript, lui, le dit : `end_turn`.\n if (skipTokenInstructions\n && entry.type === 'assistant'\n && entry.message?.stop_reason === 'end_turn') {\n settle({\n output: state.assistantText,\n tokenStatus: 'none',\n tokenMessage: '',\n actualSessionId: sessionId,\n });\n return;\n }\n\n if (!skipTokenInstructions) {\n const { status, message, branchChoice } = parseEndToken(state.assistantText);\n if (status !== 'none') {\n settle({\n output: state.assistantText,\n tokenStatus: status,\n tokenMessage: message,\n branchChoice,\n actualSessionId: sessionId,\n });\n }\n }\n },\n });\n\n // Spawn PTY on the agent.\n agentMgr.agentMutation<{ spawnId: string }>(agentId, 'ai.spawnPty', {\n cli: 'claude',\n args,\n env: params.injectedEnv && Object.keys(params.injectedEnv).length > 0 ? params.injectedEnv : undefined,\n // Le répertoire que l'appelant a résolu — celui du projet de la\n // conversation. Sans lui, le PTY naissait dans le répertoire de l'agent,\n // c'est-à-dire là où Runeya a été lancé : une carte kanban du projet A\n // faisait travailler le CLI dans le code de Runeya.\n cwd: params.cwd || undefined,\n cols: 120,\n rows: 30,\n sessionId,\n enableMcp: !!params.mcpConfig,\n mcpApiBaseUrl: params.apiBaseUrl || undefined,\n // Sans lui, l'agent lance le CLI et son serveur MCP sans RUNEYA_API_TOKEN :\n // toutes les tools répondent « No auth token available », et `$RUNEYA_API_TOKEN`\n // ne résout à rien dans le shell du CLI. Le jeton est déjà là — la file le\n // signe elle-même, faute de requête HTTP d'où le tirer.\n mcpApiToken: params.apiToken || undefined,\n mcpProjectId: params.projectId || undefined,\n mcpOrgId: params.orgId || undefined,\n }).then(({ spawnId: id }) => {\n spawnId = id;\n if (signal.aborted) { fail(new Error('Aborted')); return; }\n }).catch((err) => {\n fail(new Error(`Failed to spawn claude PTY: ${(err as Error).message}`));\n });\n\n // Abort handling: parent signal → stop tail + kill.\n signal.addEventListener('abort', () => fail(new Error('Aborted')), { once: true });\n\n // If skipTokenInstructions, we have no end-token; rely on tail completion\n // (which only happens on abort). For scenarios we always use the token.\n void tail.whenDone.then(() => {\n if (settled) return;\n // Tail ended without a token — likely the PTY exited or user aborted.\n if (skipTokenInstructions) {\n settle({\n output: state.assistantText,\n tokenStatus: 'none',\n tokenMessage: '',\n actualSessionId: sessionId,\n });\n } else {\n fail(new Error('Claude PTY ended before producing an end token'));\n }\n });\n });\n}\n","import { open, type FileHandle } from 'node:fs/promises';\nimport { watch, type FSWatcher } from 'node:fs';\nimport { findClaudeSessionFile } from '../claude-transcript-sync.js';\n\nexport interface TranscriptEntry {\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 ; `tool_use` quand il enchaîne. */\n stop_reason?: string | null;\n };\n toolUseResult?: unknown;\n}\n\nexport interface TailOptions {\n sessionId: string;\n signal: AbortSignal;\n /** Max wait for the JSONL file to appear on disk before yielding control. */\n fileAppearTimeoutMs?: number;\n /**\n * Ne lire que ce qui sera écrit à partir de maintenant.\n *\n * Indispensable sur une session REPRISE : le transcript contient déjà les\n * tours précédents, et les relire n'est pas seulement inutile — le premier\n * `end_turn` de cet historique fait croire à l'appelant que le tour courant\n * est terminé. Il se conclut alors sur une réponse vide, avant même que le\n * CLI ait répondu.\n */\n startAtEnd?: boolean;\n onEntry: (entry: TranscriptEntry) => void;\n}\n\n/** Wait until the JSONL transcript file for `sessionId` exists on disk. */\nasync function waitForFile(sessionId: string, signal: AbortSignal, timeoutMs: number): Promise<string> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (signal.aborted) throw new Error('Aborted');\n const path = await findClaudeSessionFile(sessionId);\n if (path) return path;\n await new Promise((r) => setTimeout(r, 150));\n }\n throw new Error(`Transcript file for session ${sessionId} never appeared (timeout ${timeoutMs}ms)`);\n}\n\n/**\n * Tail a Claude JSONL transcript: yield each new entry as it is appended.\n * Returns a `stop()` function and a `whenDone` promise that resolves on stop.\n *\n * NOTE: Reads `~/.claude/projects/<slug>/<sessionId>.jsonl` from the local\n * filesystem. This assumes the agent runs on the same host as the server\n * (local-simple mode). For remote agents, expose a tail route on the agent.\n */\nexport function tailTranscript(opts: TailOptions): { stop: () => void; whenDone: Promise<void> } {\n const { sessionId, signal, onEntry, fileAppearTimeoutMs = 30_000, startAtEnd = false } = opts;\n let stopped = false;\n let watcher: FSWatcher | null = null;\n let fh: FileHandle | null = null;\n let offset = 0;\n let pendingBuf = '';\n let reading = false;\n let queuedRead = false;\n\n const cleanup = async () => {\n stopped = true;\n if (watcher) { try { watcher.close(); } catch { /* ignore */ } watcher = null; }\n if (fh) { try { await fh.close(); } catch { /* ignore */ } fh = null; }\n };\n\n const drainBuffer = () => {\n let idx;\n while ((idx = pendingBuf.indexOf('\\n')) >= 0) {\n const line = pendingBuf.slice(0, idx).trim();\n pendingBuf = pendingBuf.slice(idx + 1);\n if (!line) continue;\n try {\n const entry = JSON.parse(line) as TranscriptEntry;\n onEntry(entry);\n } catch { /* skip malformed line */ }\n }\n };\n\n const readMore = async () => {\n if (!fh || stopped) return;\n if (reading) { queuedRead = true; return; }\n reading = true;\n try {\n while (!stopped) {\n const st = await fh.stat();\n if (st.size <= offset) break;\n const len = st.size - offset;\n const buf = Buffer.alloc(len);\n const { bytesRead } = await fh.read(buf, 0, len, offset);\n if (bytesRead <= 0) break;\n offset += bytesRead;\n pendingBuf += buf.slice(0, bytesRead).toString('utf-8');\n drainBuffer();\n }\n } finally {\n reading = false;\n if (queuedRead && !stopped) { queuedRead = false; void readMore(); }\n }\n };\n\n const whenDone = (async () => {\n const onAbort = () => { void cleanup(); };\n signal.addEventListener('abort', onAbort, { once: true });\n try {\n const path = await waitForFile(sessionId, signal, fileAppearTimeoutMs);\n if (stopped) return;\n fh = await open(path, 'r');\n if (startAtEnd) {\n // On se place à la fin AVANT la première lecture : tout ce qui précède\n // appartient aux tours déjà joués.\n offset = (await fh.stat()).size;\n }\n // Initial drain (handles content already written between spawn and watcher).\n await readMore();\n if (stopped) return;\n watcher = watch(path, { persistent: false }, () => { void readMore(); });\n watcher.on('error', () => { /* best-effort */ });\n // Polling fallback at low cadence — fs.watch can miss events on some FS.\n const poll = setInterval(() => { void readMore(); }, 500);\n // Keep alive until stopped.\n await new Promise<void>((resolve) => {\n const tick = setInterval(() => { if (stopped) { clearInterval(tick); clearInterval(poll); resolve(); } }, 100);\n });\n } finally {\n signal.removeEventListener('abort', onAbort);\n await cleanup();\n }\n })();\n\n return {\n stop: () => { void cleanup(); },\n whenDone,\n };\n}\n","import { spawn, type ChildProcess } from 'node:child_process';\nimport { createInterface, type Interface as ReadlineInterface } from 'node:readline';\nimport { EventEmitter } from 'node:events';\nimport { codexMcpArgs } from '@runeya/packages-mcp-server/launch';\nimport { resolveCliInvocation } from '@runeya/packages-shared/node';\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\ntype NotificationHandler = (method: string, params: Record<string, unknown>) => void;\n\ninterface PendingRequest {\n resolve: (result: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n// ─── Client ─────────────────────────────────────────────────────────────────\n\n/**\n * Surcharges `-c` qui branchent le serveur MCP Runeya sur `codex app-server`.\n *\n * Le pendant de ce que `ai-spawn-manager` fait pour `claude` : rien n'est écrit\n * dans le `~/.codex/config.toml` de l'utilisateur, la config ne vit que le temps\n * du process. Les surcharges doivent précéder la sous-commande.\n *\n * Sans jeton Runeya, le serveur MCP ne pourrait rien appeler : on n'injecte donc\n * rien plutôt que de faire démarrer un serveur inutile à chaque lancement.\n */\nfunction resolveMcpArgs(env: NodeJS.ProcessEnv): string[] {\n if (!env['RUNEYA_API_TOKEN']) return [];\n try {\n return codexMcpArgs({\n RUNEYA_BASE_URL: env['RUNEYA_BASE_URL'],\n RUNEYA_API_TOKEN: env['RUNEYA_API_TOKEN'],\n });\n } catch (err) {\n console.warn('[codex-app-server] Failed to resolve MCP server path:', (err as Error).message);\n return [];\n }\n}\n\nexport class CodexAppServerClient extends EventEmitter {\n private proc: ChildProcess | null = null;\n private rl: ReadlineInterface | null = null;\n private nextId = 1;\n private pending = new Map<number, PendingRequest>();\n private notificationHandlers = new Map<string, NotificationHandler[]>();\n private catchAllHandlers: NotificationHandler[] = [];\n private requestTimeoutMs: number;\n\n constructor(opts?: { requestTimeoutMs?: number }) {\n super();\n this.requestTimeoutMs = opts?.requestTimeoutMs ?? 120_000;\n }\n\n /**\n * Spawn codex app-server, perform initialize handshake, return server info.\n *\n * `runeyaEnv` porte les identifiants Runeya : ils vont à la fois dans l'env du\n * process (le CLI y résout `$RUNEYA_API_TOKEN`) et dans la config du serveur\n * MCP dérivée de cet env — un seul canal, comme côté agent.\n */\n async connect(runeyaEnv?: Record<string, string>): Promise<Record<string, unknown>> {\n const env = { ...process.env, ...runeyaEnv };\n\n // Windows : shim `.cmd` — inchangé sur POSIX (voir resolveCliInvocation).\n const invocation = resolveCliInvocation('codex', [...resolveMcpArgs(env), 'app-server']);\n this.proc = spawn(invocation.command, invocation.args, {\n windowsHide: true,\n stdio: ['pipe', 'pipe', 'pipe'],\n env,\n });\n\n this.rl = createInterface({ input: this.proc.stdout! });\n this.rl.on('line', (line) => this.handleLine(line));\n\n this.proc.on('exit', (code, signal) => {\n this.rejectAllPending(new Error(`codex app-server exited (code=${code}, signal=${signal})`));\n this.emit('exit', code, signal);\n });\n\n this.proc.on('error', (err) => {\n this.rejectAllPending(err);\n this.emit('error', err);\n });\n\n // Initialize handshake\n const result = await this.sendRequest('initialize', {\n clientInfo: { name: 'runeya', title: 'Runeya', version: '1.0.0' },\n capabilities: { experimentalApi: false },\n }, 0) as Record<string, unknown>;\n\n // Send initialized notification\n this.sendNotification('initialized', {});\n\n return result;\n }\n\n /** Send a JSON-RPC request and return a promise for the result. */\n sendRequest(method: string, params?: Record<string, unknown>, forceId?: number): Promise<unknown> {\n const id = forceId ?? this.nextId++;\n // If forceId was used and equals nextId range, bump to avoid collision\n if (forceId !== undefined && forceId >= this.nextId) {\n this.nextId = forceId + 1;\n }\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`Request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`));\n }, this.requestTimeoutMs);\n\n this.pending.set(id, { resolve, reject, timer });\n this.writeLine({ jsonrpc: '2.0', method, id, params: params ?? {} });\n });\n }\n\n /** Send a JSON-RPC notification (no id, no response expected). */\n sendNotification(method: string, params: Record<string, unknown>): void {\n this.writeLine({ jsonrpc: '2.0', method, params });\n }\n\n /** Respond to a server-initiated request (e.g. approval workflow). */\n respond(id: number, result: unknown): void {\n this.writeLine({ jsonrpc: '2.0', id, result });\n }\n\n respondError(id: number, code: number, message: string): void {\n this.writeLine({ jsonrpc: '2.0', id, error: { code, message } });\n }\n\n /** Register a handler for a specific notification method. */\n onNotification(method: string, handler: NotificationHandler): void {\n const list = this.notificationHandlers.get(method) ?? [];\n list.push(handler);\n this.notificationHandlers.set(method, list);\n }\n\n /** Register a catch-all notification handler. */\n onAnyNotification(handler: NotificationHandler): void {\n this.catchAllHandlers.push(handler);\n }\n\n /** Gracefully shut down the child process. */\n async shutdown(): Promise<void> {\n if (!this.proc) return;\n this.rl?.close();\n this.rl = null;\n\n const proc = this.proc;\n this.proc = null;\n\n return new Promise<void>((resolve) => {\n proc.on('exit', () => resolve());\n proc.kill('SIGTERM');\n // Force kill after 5s\n const forceTimer = setTimeout(() => {\n proc.kill('SIGKILL');\n }, 5000);\n proc.on('exit', () => clearTimeout(forceTimer));\n });\n }\n\n // ─── Internal ───────────────────────────────────────────────────────────\n\n private writeLine(obj: Record<string, unknown>): void {\n if (!this.proc?.stdin?.writable) {\n throw new Error('codex app-server stdin not writable');\n }\n this.proc.stdin.write(JSON.stringify(obj) + '\\n');\n }\n\n private handleLine(line: string): void {\n const trimmed = line.trim();\n if (!trimmed) return;\n\n let msg: Record<string, unknown>;\n try {\n msg = JSON.parse(trimmed);\n } catch {\n return; // ignore malformed lines\n }\n\n // Response to a pending request\n if ('id' in msg && typeof msg['id'] === 'number' && ('result' in msg || 'error' in msg)) {\n const pending = this.pending.get(msg['id'] as number);\n if (pending) {\n this.pending.delete(msg['id'] as number);\n clearTimeout(pending.timer);\n if ('error' in msg && msg['error']) {\n const err = msg['error'] as { code: number; message: string };\n pending.reject(new Error(`JSON-RPC error ${err.code}: ${err.message}`));\n } else {\n pending.resolve(msg['result']);\n }\n }\n return;\n }\n\n // Server-initiated request (has id + method, no result/error)\n if ('id' in msg && 'method' in msg && typeof msg['method'] === 'string') {\n this.emit('server-request', msg);\n return;\n }\n\n // Notification (method, no id)\n if ('method' in msg && typeof msg['method'] === 'string' && !('id' in msg)) {\n const method = msg['method'] as string;\n const params = (msg['params'] ?? {}) as Record<string, unknown>;\n\n const handlers = this.notificationHandlers.get(method);\n if (handlers) {\n for (const h of handlers) h(method, params);\n }\n for (const h of this.catchAllHandlers) h(method, params);\n }\n }\n\n private rejectAllPending(error: Error): void {\n for (const [id, pending] of this.pending) {\n clearTimeout(pending.timer);\n pending.reject(error);\n this.pending.delete(id);\n }\n }\n}\n","import { parseEndToken, TOKEN_INSTRUCTIONS } from './scenario-tokens.js';\nimport type { AiRunner, AiRunnerParams, NodeResult } from './ai-runner.js';\nimport { CodexAppServerClient } from './codex-app-server-client.js';\n\n// ─── Singleton app-server client ───────────────────────────────────────────\n\nlet appServerClient: CodexAppServerClient | null = null;\nlet appServerConnecting: Promise<void> | null = null;\n\n/**\n * Le client est un singleton : les identifiants du PREMIER appel sont ceux du\n * process `codex app-server`, y compris pour son serveur MCP. Même contrat que\n * le manager côté agent, dont l'`extraEnv` n'est lu qu'au spawn.\n */\nasync function getAppServerClient(runeyaEnv?: Record<string, string>): Promise<CodexAppServerClient> {\n if (appServerClient) return appServerClient;\n if (appServerConnecting) {\n await appServerConnecting;\n return appServerClient!;\n }\n const client = new CodexAppServerClient({ requestTimeoutMs: 300_000 });\n appServerConnecting = client.connect(runeyaEnv).then(() => {\n appServerClient = client;\n appServerConnecting = null;\n }).catch((err) => {\n appServerConnecting = null;\n throw err;\n });\n // Handle unexpected exit — reset singleton so next call reconnects\n client.on('exit', () => {\n if (appServerClient === client) {\n appServerClient = null;\n }\n });\n await appServerConnecting;\n return appServerClient!;\n}\n\n/** Gracefully shut down the singleton codex app-server process. */\nexport async function shutdownCodexAppServer(): Promise<void> {\n if (appServerClient) {\n const client = appServerClient;\n appServerClient = null;\n await client.shutdown();\n }\n}\n\nasync function getAgentManager() {\n const { agentManager } = await import('../agent-manager.js');\n return agentManager;\n}\n\n// ─── Codex CLI runner ──────────────────────────────────────────────────────\n//\n// Local mode: uses `codex app-server` (persistent JSON-RPC 2.0 process).\n// Agent mode: uses agent-side `codex app-server` via ai.codexRequest/ai.codexNotifications tRPC routes.\n//\n// Both modes use the app-server protocol for thread management (start/resume)\n// and turn execution with structured notifications.\n\n/** Extract route name + parsed input from a curl command targeting the Runeya tRPC API. */\nfunction parseRuneyaCurlCommand(command: string): { route: string; input: Record<string, unknown> } | null {\n // Match /api/trpc/<route> in URL\n const routeMatch = command.match(/\\/api\\/trpc\\/([\\w.]+)/);\n if (!routeMatch) return null;\n const route = routeMatch[1];\n // Try to extract JSON body from -d '...' or -d \"...\"\n let input: Record<string, unknown> = {};\n const bodyMatch = command.match(/-d\\s+'([^']+)'/) ?? command.match(/-d\\s+\"((?:[^\"\\\\]|\\\\.)*)\"/);\n if (bodyMatch) {\n try { input = JSON.parse(bodyMatch[1]) as Record<string, unknown>; } catch { /* keep empty */ }\n }\n // Also check ?input= for GET requests\n const inputQsMatch = command.match(/[?&]input=([^&\\s'\"]+)/);\n if (inputQsMatch) {\n try { input = JSON.parse(decodeURIComponent(inputQsMatch[1])) as Record<string, unknown>; } catch { /* keep empty */ }\n }\n return { route, input };\n}\n\nfunction getCommandExecutionInput(item: Record<string, unknown> | undefined): Record<string, unknown> {\n if (!item) return {};\n const command = typeof item['command'] === 'string' ? item['command'] : '';\n const parsed = command ? parseRuneyaCurlCommand(command) : null;\n if (parsed) return parsed.input;\n const input: Record<string, unknown> = {};\n if (command) input.command = command;\n if (typeof item['status'] === 'string' && item['status']) input.status = item['status'];\n if (typeof item['exit_code'] === 'number') input.exit_code = item['exit_code'];\n return input;\n}\n\nfunction getCommandExecutionName(item: Record<string, unknown> | undefined): string {\n if (!item) return 'command_execution';\n const command = typeof item['command'] === 'string' ? item['command'] : '';\n const parsed = command ? parseRuneyaCurlCommand(command) : null;\n return parsed ? parsed.route : 'command_execution';\n}\n\nfunction getCommandExecutionResult(item: Record<string, unknown> | undefined): string {\n if (!item) return '';\n if (typeof item['aggregated_output'] === 'string' && item['aggregated_output']) {\n return item['aggregated_output'] as string;\n }\n const fallback = {\n command: typeof item['command'] === 'string' ? item['command'] : undefined,\n status: typeof item['status'] === 'string' ? item['status'] : undefined,\n exit_code: typeof item['exit_code'] === 'number' ? item['exit_code'] : undefined,\n };\n return JSON.stringify(fallback, null, 2);\n}\n\n/** Map Runeya effort levels to Codex app-server effort values. */\nfunction mapEffort(effort: string): string | undefined {\n const map: Record<string, string> = { low: 'low', normal: 'medium', medium: 'medium', high: 'high' };\n return map[effort];\n}\n\nexport class CodexRunner implements AiRunner {\n /** Codex thread_id captured from `thread.started`, used for resume. */\n private lastThreadId: string | null = null;\n\n run(params: AiRunnerParams): Promise<NodeResult> {\n if (params.agentId) {\n return this.runViaAgent(params);\n }\n return this.runLocal(params);\n }\n\n // ── Run via agent (app-server JSON-RPC via agent tRPC routes) ───────────\n\n private async runViaAgent(params: AiRunnerParams): Promise<NodeResult> {\n const { agentId, prompt, systemContext, signal, model, effort, isResume, images, skipTokenInstructions, apiBaseUrl, apiToken, injectedEnv } = params;\n const agentMgr = await getAgentManager();\n\n if (isResume && params.sessionId && !this.lastThreadId) {\n this.lastThreadId = params.sessionId;\n }\n\n params.onStatus('🔧 Codex en cours…');\n params.onCommand?.('codex app-server (agent JSON-RPC)');\n\n // Env vars to inject into the agent-side app-server process:\n // active-environment variables flagged `injectIntoAi` + the Runeya API token.\n // `RUNEYA_BASE_URL` accompagne le jeton : le serveur MCP lancé à côté de\n // l'app-server en a besoin pour savoir QUI appeler, pas seulement avec quoi.\n const codexEnv = (injectedEnv && Object.keys(injectedEnv).length > 0) || apiToken\n ? {\n ...injectedEnv,\n ...(apiToken ? { RUNEYA_API_TOKEN: apiToken } : {}),\n ...(apiToken && apiBaseUrl ? { RUNEYA_BASE_URL: apiBaseUrl } : {}),\n }\n : undefined;\n\n // 1. Start or resume thread on agent-side app-server\n let threadId: string;\n if (isResume && this.lastThreadId) {\n const res = await agentMgr.agentMutation<{ result: { thread: { id: string } } }>(agentId!, 'ai.codexRequest', {\n method: 'thread/resume',\n params: { threadId: this.lastThreadId },\n env: codexEnv,\n });\n threadId = res.result.thread.id;\n } else {\n const res = await agentMgr.agentMutation<{ result: { thread: { id: string } } }>(agentId!, 'ai.codexRequest', {\n method: 'thread/start',\n params: {\n model: model || undefined,\n cwd: params.cwd ?? process.cwd(),\n approvalPolicy: 'never',\n sandbox: 'danger-full-access',\n },\n env: codexEnv,\n });\n threadId = res.result.thread.id;\n this.lastThreadId = threadId;\n params.onThreadId?.(threadId);\n }\n\n // 2. Build turn input (same as runLocal)\n const systemParts = [systemContext, ...(skipTokenInstructions ? [] : [TOKEN_INSTRUCTIONS])].filter(Boolean);\n const fullSystemContext = systemParts.join('\\n\\n');\n const fullPrompt = (isResume && this.lastThreadId)\n ? prompt\n : `${fullSystemContext}\\n\\n---\\n\\n${prompt}`;\n\n const input: Array<Record<string, unknown>> = [];\n if (images && images.length > 0) {\n for (const img of images) {\n input.push({ type: 'image', url: `data:${img.mimeType};base64,${img.base64}` });\n }\n }\n input.push({ type: 'text', text: fullPrompt });\n\n // 3. Subscribe to notifications BEFORE starting the turn\n // Mutable ref so the notification handler can access turnId set after turn/start\n const turnRef: { id: string | null } = { id: null };\n\n return new Promise<NodeResult>((resolve, reject) => {\n let settled = false;\n let fullContent = '';\n let thinkingEmitted = false;\n const startedCommandExecutions = new Set<string>();\n const completedCommandExecutions = new Set<string>();\n\n const settle = (fn: () => void) => {\n if (settled) return;\n settled = true;\n signal.removeEventListener('abort', onAbort);\n if (unsubscribe) { unsubscribe(); unsubscribe = null; }\n fn();\n };\n\n const onAbort = () => {\n if (settled) return;\n if (turnRef.id) {\n agentMgr.agentMutation(agentId!, 'ai.codexRequest', {\n method: 'turn/interrupt',\n params: { threadId, turnId: turnRef.id },\n }).catch(() => { /* best-effort */ });\n }\n settle(() => reject(new Error('Aborted')));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n\n let unsubscribe: (() => void) | null = agentMgr.agentSubscribe(\n agentId!,\n 'ai.codexNotifications',\n { threadId },\n (data: unknown) => {\n if (settled) return;\n const notif = data as { method: string; params: Record<string, unknown> };\n const method = notif.method;\n const p = notif.params;\n\n // Filter by threadId\n const notifThreadId = p['threadId'] as string | undefined;\n if (notifThreadId && notifThreadId !== threadId) return;\n\n // Filter by turnId if present\n const notifTurnId = (p['turnId'] ?? (p['turn'] as Record<string, unknown> | undefined)?.['id']) as string | undefined;\n if (notifTurnId && turnRef.id && notifTurnId !== turnRef.id) return;\n\n if (method === 'item/agentMessage/delta') {\n const delta = p['delta'] as string;\n if (delta) {\n thinkingEmitted = false;\n fullContent += delta;\n params.onDelta(delta);\n const { status, message, branchChoice } = parseEndToken(fullContent);\n if (status !== 'none') {\n settle(() => resolve({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));\n }\n }\n } else if (method === 'item/reasoning/summaryTextDelta' || method === 'item/reasoning/textDelta') {\n const delta = p['delta'] as string;\n if (delta) {\n if (!thinkingEmitted) {\n thinkingEmitted = true;\n params.onStatus('💭 En réflexion…');\n }\n params.onThinking?.(delta);\n }\n } else if (method === 'item/started') {\n const item = p['item'] as Record<string, unknown> | undefined;\n if (!item) return;\n const itemType = item['type'] as string;\n const itemId = item['id'] as string ?? '';\n\n if (itemType === 'commandExecution') {\n const command = typeof item['command'] === 'string' ? item['command'] as string : '';\n const cmdName = getCommandExecutionName(item);\n const effectiveId = itemId || (command ? `cmd-${command.slice(0, 80)}` : '');\n params.onStatus(`🔧 ${cmdName}`);\n if (effectiveId && !startedCommandExecutions.has(effectiveId)) {\n startedCommandExecutions.add(effectiveId);\n params.onToolCall?.(effectiveId, cmdName, getCommandExecutionInput(item));\n }\n } else if (itemType === 'mcpToolCall' || itemType === 'tool_call') {\n const rawName = typeof item['tool'] === 'string' ? item['tool'] as string\n : typeof item['name'] === 'string' ? item['name'] as string : 'tool';\n const server = typeof item['server'] === 'string' ? item['server'] as string : '';\n const displayName = server ? `${server}.${rawName}` : rawName;\n params.onStatus(`🔧 ${displayName}`);\n if (itemId) {\n let toolInput: Record<string, unknown> = {};\n const rawArgs = item['arguments'];\n if (typeof rawArgs === 'string') {\n try { toolInput = JSON.parse(rawArgs) as Record<string, unknown>; } catch { /* keep empty */ }\n } else if (rawArgs && typeof rawArgs === 'object') {\n toolInput = rawArgs as Record<string, unknown>;\n }\n params.onToolCall?.(itemId, displayName, toolInput);\n }\n }\n } else if (method === 'item/completed') {\n const item = p['item'] as Record<string, unknown> | undefined;\n if (!item) return;\n const itemType = item['type'] as string;\n const itemId = item['id'] as string ?? '';\n\n if (itemType === 'commandExecution') {\n const command = typeof item['command'] === 'string' ? item['command'] as string : '';\n const effectiveId = itemId || (command ? `cmd-${command.slice(0, 80)}` : '');\n if (effectiveId && !completedCommandExecutions.has(effectiveId)) {\n completedCommandExecutions.add(effectiveId);\n const exitCode = typeof item['exit_code'] === 'number' ? item['exit_code'] as number : 0;\n params.onToolResult?.(effectiveId, getCommandExecutionResult(item), exitCode !== 0);\n }\n } else if (itemType === 'mcpToolCall' || itemType === 'tool_call') {\n if (itemId) {\n const output = item['output'];\n const result = typeof output === 'string' ? output : (output ? JSON.stringify(output) : '');\n params.onToolResult?.(itemId, result, false);\n }\n }\n } else if (method === 'turn/completed') {\n const turn = p['turn'] as Record<string, unknown> | undefined;\n const error = turn?.['error'] as Record<string, unknown> | undefined;\n if (error) {\n const msg = typeof error['message'] === 'string' ? error['message'] as string : 'Codex turn failed';\n settle(() => reject(new Error(`Codex: ${msg}`)));\n return;\n }\n const { status, message, branchChoice } = parseEndToken(fullContent);\n settle(() => resolve({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));\n } else if (method === 'turn/errored') {\n const error = p['error'] as Record<string, unknown> | undefined;\n const msg = typeof error?.['message'] === 'string' ? error['message'] as string : 'Codex turn errored';\n settle(() => reject(new Error(`Codex: ${msg}`)));\n }\n },\n () => {\n // onStop — WS disconnect\n },\n );\n\n // 4. Start turn on agent-side app-server\n agentMgr.agentMutation<{ result: { turn: { id: string; status: string } } }>(agentId!, 'ai.codexRequest', {\n method: 'turn/start',\n params: {\n threadId,\n input,\n model: model || undefined,\n effort: mapEffort(effort),\n approvalPolicy: 'never',\n sandboxPolicy: { type: 'dangerFullAccess' },\n },\n }).then((turnRes) => {\n turnRef.id = turnRes.result.turn.id;\n }).catch((err) => {\n settle(() => reject(new Error(`Failed to start codex turn on agent: ${(err as Error).message}`)));\n });\n });\n }\n\n // ── Run locally via app-server client ────────────────────────────────────\n\n private async runLocal(params: AiRunnerParams): Promise<NodeResult> {\n const { prompt, systemContext, signal, model, effort, isResume, images, skipTokenInstructions, apiBaseUrl, apiToken } = params;\n\n if (isResume && params.sessionId && !this.lastThreadId) {\n this.lastThreadId = params.sessionId;\n }\n\n params.onStatus('🔧 Codex en cours…');\n params.onCommand?.('codex app-server (JSON-RPC)');\n\n // 1. Get or create the singleton client\n const client = await getAppServerClient({\n ...(apiBaseUrl ? { RUNEYA_BASE_URL: apiBaseUrl } : {}),\n ...(apiToken ? { RUNEYA_API_TOKEN: apiToken } : {}),\n });\n\n // 2. Start or resume thread\n let threadId: string;\n if (isResume && this.lastThreadId) {\n const res = await client.sendRequest('thread/resume', { threadId: this.lastThreadId }) as { thread: { id: string } };\n threadId = res.thread.id;\n } else {\n const res = await client.sendRequest('thread/start', {\n model: model || undefined,\n cwd: params.cwd ?? process.cwd(),\n approvalPolicy: 'never',\n sandbox: 'danger-full-access',\n }) as { thread: { id: string } };\n threadId = res.thread.id;\n this.lastThreadId = threadId;\n params.onThreadId?.(threadId);\n }\n\n // 3. Build turn input\n const systemParts = [systemContext, ...(skipTokenInstructions ? [] : [TOKEN_INSTRUCTIONS])].filter(Boolean);\n const fullSystemContext = systemParts.join('\\n\\n');\n const fullPrompt = (isResume && this.lastThreadId)\n ? prompt\n : `${fullSystemContext}\\n\\n---\\n\\n${prompt}`;\n\n const input: Array<Record<string, unknown>> = [];\n if (images && images.length > 0) {\n for (const img of images) {\n input.push({ type: 'image', url: `data:${img.mimeType};base64,${img.base64}` });\n }\n }\n input.push({ type: 'text', text: fullPrompt });\n\n // 4. Start turn\n const turnRes = await client.sendRequest('turn/start', {\n threadId,\n input,\n model: model || undefined,\n effort: mapEffort(effort),\n approvalPolicy: 'never',\n sandboxPolicy: { type: 'dangerFullAccess' },\n }) as { turn: { id: string; status: string } };\n const turnId = turnRes.turn.id;\n\n // 5. Listen for notifications and resolve on turn/completed\n return new Promise<NodeResult>((resolve, reject) => {\n let settled = false;\n let fullContent = '';\n let thinkingEmitted = false;\n const startedCommandExecutions = new Set<string>();\n const completedCommandExecutions = new Set<string>();\n\n const settle = (fn: () => void) => {\n if (settled) return;\n settled = true;\n signal.removeEventListener('abort', onAbort);\n fn();\n };\n\n const handler = (method: string, p: Record<string, unknown>) => {\n if (settled) return;\n\n // Filter by threadId to scope to this conversation\n const notifThreadId = p['threadId'] as string | undefined;\n if (notifThreadId && notifThreadId !== threadId) return;\n\n // Also filter by turnId if present\n const notifTurnId = (p['turnId'] ?? (p['turn'] as Record<string, unknown> | undefined)?.['id']) as string | undefined;\n if (notifTurnId && notifTurnId !== turnId) return;\n\n if (method === 'item/agentMessage/delta') {\n const delta = p['delta'] as string;\n if (delta) {\n thinkingEmitted = false;\n fullContent += delta;\n params.onDelta(delta);\n const { status, message, branchChoice } = parseEndToken(fullContent);\n if (status !== 'none') {\n settle(() => resolve({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));\n }\n }\n } else if (method === 'item/reasoning/summaryTextDelta' || method === 'item/reasoning/textDelta') {\n const delta = p['delta'] as string;\n if (delta) {\n if (!thinkingEmitted) {\n thinkingEmitted = true;\n params.onStatus('💭 En réflexion…');\n }\n params.onThinking?.(delta);\n }\n } else if (method === 'item/started') {\n const item = p['item'] as Record<string, unknown> | undefined;\n if (!item) return;\n const itemType = item['type'] as string;\n const itemId = item['id'] as string ?? '';\n\n if (itemType === 'commandExecution') {\n const command = typeof item['command'] === 'string' ? item['command'] as string : '';\n const cmdName = getCommandExecutionName(item);\n const effectiveId = itemId || (command ? `cmd-${command.slice(0, 80)}` : '');\n params.onStatus(`🔧 ${cmdName}`);\n if (effectiveId && !startedCommandExecutions.has(effectiveId)) {\n startedCommandExecutions.add(effectiveId);\n params.onToolCall?.(effectiveId, cmdName, getCommandExecutionInput(item));\n }\n } else if (itemType === 'mcpToolCall' || itemType === 'tool_call') {\n const rawName = typeof item['tool'] === 'string' ? item['tool'] as string\n : typeof item['name'] === 'string' ? item['name'] as string : 'tool';\n const server = typeof item['server'] === 'string' ? item['server'] as string : '';\n const displayName = server ? `${server}.${rawName}` : rawName;\n params.onStatus(`🔧 ${displayName}`);\n if (itemId) {\n let toolInput: Record<string, unknown> = {};\n const rawArgs = item['arguments'];\n if (typeof rawArgs === 'string') {\n try { toolInput = JSON.parse(rawArgs) as Record<string, unknown>; } catch { /* keep empty */ }\n } else if (rawArgs && typeof rawArgs === 'object') {\n toolInput = rawArgs as Record<string, unknown>;\n }\n params.onToolCall?.(itemId, displayName, toolInput);\n }\n }\n } else if (method === 'item/completed') {\n const item = p['item'] as Record<string, unknown> | undefined;\n if (!item) return;\n const itemType = item['type'] as string;\n const itemId = item['id'] as string ?? '';\n\n if (itemType === 'commandExecution') {\n const command = typeof item['command'] === 'string' ? item['command'] as string : '';\n const effectiveId = itemId || (command ? `cmd-${command.slice(0, 80)}` : '');\n if (effectiveId && !completedCommandExecutions.has(effectiveId)) {\n completedCommandExecutions.add(effectiveId);\n const exitCode = typeof item['exit_code'] === 'number' ? item['exit_code'] as number : 0;\n params.onToolResult?.(effectiveId, getCommandExecutionResult(item), exitCode !== 0);\n }\n } else if (itemType === 'mcpToolCall' || itemType === 'tool_call') {\n if (itemId) {\n const output = item['output'];\n const result = typeof output === 'string' ? output : (output ? JSON.stringify(output) : '');\n params.onToolResult?.(itemId, result, false);\n }\n }\n } else if (method === 'turn/completed') {\n const turn = p['turn'] as Record<string, unknown> | undefined;\n const error = turn?.['error'] as Record<string, unknown> | undefined;\n if (error) {\n const msg = typeof error['message'] === 'string' ? error['message'] as string : 'Codex turn failed';\n settle(() => reject(new Error(`Codex: ${msg}`)));\n return;\n }\n // Turn finished normally — resolve with accumulated content\n const { status, message, branchChoice } = parseEndToken(fullContent);\n settle(() => resolve({ output: fullContent, tokenStatus: status, tokenMessage: message, branchChoice }));\n } else if (method === 'turn/errored') {\n const error = p['error'] as Record<string, unknown> | undefined;\n const msg = typeof error?.['message'] === 'string' ? error['message'] as string : 'Codex turn errored';\n settle(() => reject(new Error(`Codex: ${msg}`)));\n }\n };\n\n client.onAnyNotification(handler);\n\n const onAbort = () => {\n if (settled) return;\n client.sendRequest('turn/interrupt', { threadId, turnId }).catch(() => { /* best-effort */ });\n settle(() => reject(new Error('Aborted')));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n });\n }\n}\n"],"mappings":"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","names":["z","Anthropic","OpenAI","fileURLToPath","env","env","TRPCError","join","join","token","z","join","dirname","join","dirname","readFile","writeFile","rename","mkdir","chmod","dirname","join","dirname","join","readFile","mkdir","writeFile","rename","chmod","env","ctx","parse","t","z","TRPCError","readFile","rename","readdir","mkdir","mkdir","readFile","rename","readdir","randomUUID","randomUUID","isAbsolute","isAbsolute","z","TRPCError","resolve","env","pty","MONTHS","resolve","pty","mkdir","readFile","join","chmod","mkdir","rename","writeFile","readFile","dirname","mkdir","dirname","writeFile","rename","chmod","readFile","mkdir","t","join","readFile","randomUUID","mkdir","writeFile","readFile","unlink","readdir","existsSync","join","resolve","sep","mkdir","randomUUID","join","resolve","sep","writeFile","readdir","readFile","existsSync","unlink","z","TRPCError","readFile","writeFile","rename","mkdir","chmod","unlink","dirname","randomBytes","timingSafeEqual","FILENAME","readFile","randomBytes","timingSafeEqual","mkdir","dirname","writeFile","rename","chmod","unlink","readFile","writeFile","rename","mkdir","join","z","LOOPBACK","z","join","readFile","mkdir","writeFile","rename","resolve","hostname","cloudFetch","TRPCError","z","cloudFetch","randomUUID","resolve","agentManager","randomUUID","resolve","env","resolve","spawn","env","spawn","resolve","getAgentManager","agentManager","resolve","z","resolve","TRPCError","pty","Anthropic","OpenAI","existsSync","fileURLToPath"]}