agent-tower 0.7.4-beta.0 → 0.7.4-beta.2

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 (233) hide show
  1. package/dist/executors/__tests__/codex-mcp-windows.test.d.ts +2 -0
  2. package/dist/executors/__tests__/codex-mcp-windows.test.d.ts.map +1 -0
  3. package/dist/executors/__tests__/codex-mcp-windows.test.js +128 -0
  4. package/dist/executors/__tests__/codex-mcp-windows.test.js.map +1 -0
  5. package/dist/executors/__tests__/codex.executor.test.js +394 -1
  6. package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
  7. package/dist/executors/codex.executor.d.ts +6 -6
  8. package/dist/executors/codex.executor.d.ts.map +1 -1
  9. package/dist/executors/codex.executor.js +118 -32
  10. package/dist/executors/codex.executor.js.map +1 -1
  11. package/dist/mcp/tools/tasks.js +3 -3
  12. package/dist/mcp/tools/tasks.js.map +1 -1
  13. package/dist/mcp/types.js +2 -2
  14. package/dist/mcp/types.js.map +1 -1
  15. package/dist/prompts/task-handoff.d.ts.map +1 -1
  16. package/dist/prompts/task-handoff.js +2 -1
  17. package/dist/prompts/task-handoff.js.map +1 -1
  18. package/dist/routes/__tests__/sessions-runtime.test.js +2 -0
  19. package/dist/routes/__tests__/sessions-runtime.test.js.map +1 -1
  20. package/dist/routes/__tests__/tasks.test.js +41 -0
  21. package/dist/routes/__tests__/tasks.test.js.map +1 -1
  22. package/dist/routes/__tests__/workspace-history.test.d.ts +2 -0
  23. package/dist/routes/__tests__/workspace-history.test.d.ts.map +1 -0
  24. package/dist/routes/__tests__/workspace-history.test.js +174 -0
  25. package/dist/routes/__tests__/workspace-history.test.js.map +1 -0
  26. package/dist/routes/sessions.d.ts.map +1 -1
  27. package/dist/routes/sessions.js +10 -0
  28. package/dist/routes/sessions.js.map +1 -1
  29. package/dist/routes/tasks.d.ts.map +1 -1
  30. package/dist/routes/tasks.js +8 -0
  31. package/dist/routes/tasks.js.map +1 -1
  32. package/dist/runtime/__tests__/acp-process-manager.test.js +65 -0
  33. package/dist/runtime/__tests__/acp-process-manager.test.js.map +1 -1
  34. package/dist/runtime/__tests__/cli-driver-lifecycle.test.js +84 -0
  35. package/dist/runtime/__tests__/cli-driver-lifecycle.test.js.map +1 -1
  36. package/dist/runtime/acp/agents/pi-coding-agent.d.ts.map +1 -1
  37. package/dist/runtime/acp/agents/pi-coding-agent.js +3 -0
  38. package/dist/runtime/acp/agents/pi-coding-agent.js.map +1 -1
  39. package/dist/runtime/cli-driver.js +11 -8
  40. package/dist/runtime/cli-driver.js.map +1 -1
  41. package/dist/services/__tests__/task.service.test.js +128 -13
  42. package/dist/services/__tests__/task.service.test.js.map +1 -1
  43. package/dist/services/__tests__/workspace.service.test.js +95 -5
  44. package/dist/services/__tests__/workspace.service.test.js.map +1 -1
  45. package/dist/services/message-delivery.service.d.ts +1 -0
  46. package/dist/services/message-delivery.service.d.ts.map +1 -1
  47. package/dist/services/session-manager.d.ts +4 -0
  48. package/dist/services/session-manager.d.ts.map +1 -1
  49. package/dist/services/session-manager.js +3 -1
  50. package/dist/services/session-manager.js.map +1 -1
  51. package/dist/services/session-resource-cleanup.d.ts +5 -0
  52. package/dist/services/session-resource-cleanup.d.ts.map +1 -1
  53. package/dist/services/session-resource-cleanup.js +7 -0
  54. package/dist/services/session-resource-cleanup.js.map +1 -1
  55. package/dist/services/task-cleanup.service.d.ts +2 -0
  56. package/dist/services/task-cleanup.service.d.ts.map +1 -1
  57. package/dist/services/task-cleanup.service.js +4 -0
  58. package/dist/services/task-cleanup.service.js.map +1 -1
  59. package/dist/services/task-display-state.d.ts +10 -0
  60. package/dist/services/task-display-state.d.ts.map +1 -0
  61. package/dist/services/task-display-state.js +45 -0
  62. package/dist/services/task-display-state.js.map +1 -0
  63. package/dist/services/task.service.d.ts +20 -1
  64. package/dist/services/task.service.d.ts.map +1 -1
  65. package/dist/services/task.service.js +134 -101
  66. package/dist/services/task.service.js.map +1 -1
  67. package/dist/services/workspace.service.d.ts +10 -7
  68. package/dist/services/workspace.service.d.ts.map +1 -1
  69. package/dist/services/workspace.service.js +162 -89
  70. package/dist/services/workspace.service.js.map +1 -1
  71. package/dist/web/assets/{AgentDemoPage-JXetlNBf.js → AgentDemoPage-CrkoVCOZ.js} +1 -1
  72. package/dist/web/assets/{AgentEnvironmentSettingsPage-CdYyUCwJ.js → AgentEnvironmentSettingsPage-BRihy3_Q.js} +1 -1
  73. package/dist/web/assets/{AttachmentPreview-DMS-PM-1.js → AttachmentPreview-B9z1Mxb-.js} +2 -2
  74. package/dist/web/assets/{BranchSelect-BAq4L2ZS.js → BranchSelect-Bo5uhKCt.js} +1 -1
  75. package/dist/web/assets/{BrandLogo--kWXXJtZ.js → BrandLogo-DEL42B23.js} +1 -1
  76. package/dist/web/assets/{ConversationPage-CvC4RyB5.js → ConversationPage-C46RuN9U.js} +1 -1
  77. package/dist/web/assets/{CreateTaskInput-DjVxCAfc.js → CreateTaskInput-4aHJpsfL.js} +1 -1
  78. package/dist/web/assets/{DemoPage-cDVTGOp8.js → DemoPage-CAZqNag-.js} +1 -1
  79. package/dist/web/assets/{DesignSystemPage-BI54IkKi.js → DesignSystemPage-CjCYC24B.js} +1 -1
  80. package/dist/web/assets/{ExecutionPermissionSelector-f1UYIm1z.js → ExecutionPermissionSelector-CJgc_EVo.js} +1 -1
  81. package/dist/web/assets/{GeneralSettingsPage-C0yto6C_.js → GeneralSettingsPage-DFT7Cubi.js} +1 -1
  82. package/dist/web/assets/{LoadingPreviewPage-BiJSaHUi.js → LoadingPreviewPage-CB4VDPAZ.js} +1 -1
  83. package/dist/web/assets/{McpSettingsPage-BbHwZtyj.js → McpSettingsPage-CgiyGvkH.js} +1 -1
  84. package/dist/web/assets/{MemberAvatar-BLezO3vS.js → MemberAvatar-Cfz8LU6k.js} +1 -1
  85. package/dist/web/assets/MobileTaskDetail-yZNyZMQt.js +3 -0
  86. package/dist/web/assets/{NotificationSettingsPage-CiKoL2Kv.js → NotificationSettingsPage-BSMJ45yx.js} +1 -1
  87. package/dist/web/assets/{ProfileSettingsPage-DCB48cPS.js → ProfileSettingsPage-BwNThOnH.js} +1 -1
  88. package/dist/web/assets/ProjectKanbanPage-DtZW0bOh.js +14 -0
  89. package/dist/web/assets/{ProjectSettingsPage-COvIVq1h.js → ProjectSettingsPage-CMBMkbgI.js} +1 -1
  90. package/dist/web/assets/{ProviderSettingsPage-CPuJcZDg.js → ProviderSettingsPage-D759BSRa.js} +2 -2
  91. package/dist/web/assets/{SettingsMasterDetail-Cv7YaRJB.js → SettingsMasterDetail-DpYUTM-Q.js} +1 -1
  92. package/dist/web/assets/TaskDetail-rRHGOvSw.js +1 -0
  93. package/dist/web/assets/{TaskHandoffPanel-CxH3Mgyd.js → TaskHandoffPanel-DBcC1xQW.js} +24 -24
  94. package/dist/web/assets/{TeamSettingsPage-D-XDefNd.js → TeamSettingsPage-BszQfKp0.js} +1 -1
  95. package/dist/web/assets/{TranslationSettingsPage-BPHI-SZW.js → TranslationSettingsPage-lLaxWCy3.js} +1 -1
  96. package/dist/web/assets/{arc-CC81aLuf.js → arc-DtgXFw4D.js} +1 -1
  97. package/dist/web/assets/{architectureDiagram-3BPJPVTR-DL08XGaN.js → architectureDiagram-3BPJPVTR-B3p3EMX1.js} +1 -1
  98. package/dist/web/assets/{at-sign-DYPIlD6D.js → at-sign-B8rXgKEE.js} +1 -1
  99. package/dist/web/assets/{blockDiagram-GPEHLZMM-B4IW5WxI.js → blockDiagram-GPEHLZMM-BmekCfr0.js} +1 -1
  100. package/dist/web/assets/{c4Diagram-AAUBKEIU-Cj4d_FqK.js → c4Diagram-AAUBKEIU-CF6hJhuL.js} +1 -1
  101. package/dist/web/assets/channel-BdK_fAel.js +1 -0
  102. package/dist/web/assets/check-DwK55QS2.js +1 -0
  103. package/dist/web/assets/{chevron-down-DNhYsNAt.js → chevron-down-Dftc5hr4.js} +1 -1
  104. package/dist/web/assets/{chevron-up-CjrLieBR.js → chevron-up-CKRxN6zX.js} +1 -1
  105. package/dist/web/assets/{chip-Cf92J-pQ.js → chip-ZTBHcXU2.js} +1 -1
  106. package/dist/web/assets/{chunk-2J33WTMH-DeCj8QSD.js → chunk-2J33WTMH-Bjelop4M.js} +1 -1
  107. package/dist/web/assets/{chunk-4BX2VUAB-k-6rzQmQ.js → chunk-4BX2VUAB-B1rJPbL6.js} +1 -1
  108. package/dist/web/assets/{chunk-55IACEB6-qMTCQ5bX.js → chunk-55IACEB6-B5qfH7Gj.js} +1 -1
  109. package/dist/web/assets/{chunk-727SXJPM-CMnu18d1.js → chunk-727SXJPM-ClFMbgrH.js} +1 -1
  110. package/dist/web/assets/{chunk-AQP2D5EJ-C0mFbUMn.js → chunk-AQP2D5EJ-BMFjTO7B.js} +1 -1
  111. package/dist/web/assets/{chunk-FMBD7UC4-u6-_M137.js → chunk-FMBD7UC4-Pr1oOFfR.js} +1 -1
  112. package/dist/web/assets/{chunk-ND2GUHAM-DHrIE_8H.js → chunk-ND2GUHAM-BPm4TRpO.js} +1 -1
  113. package/dist/web/assets/{chunk-QZHKN3VN-aTyC0wZq.js → chunk-QZHKN3VN-DCPA_U0M.js} +1 -1
  114. package/dist/web/assets/classDiagram-4FO5ZUOK-CFzCq0ml.js +1 -0
  115. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-CFzCq0ml.js +1 -0
  116. package/dist/web/assets/{code-block-OCS4YCEC-CBIOQOMH.js → code-block-OCS4YCEC-Cx53F4d7.js} +1 -1
  117. package/dist/web/assets/{confirm-dialog-DWeBZLJW.js → confirm-dialog-C_j-w4TS.js} +1 -1
  118. package/dist/web/assets/{content-JM9E1MeD.js → content-DeIfej_n.js} +2 -2
  119. package/dist/web/assets/{copy-D0gl9kF8.js → copy-B-BaLIlh.js} +1 -1
  120. package/dist/web/assets/{cose-bilkent-S5V4N54A-xLyliSgU.js → cose-bilkent-S5V4N54A-gIys-0yu.js} +1 -1
  121. package/dist/web/assets/{cssMode-DO5UnQ64.js → cssMode-xF0bjbbU.js} +1 -1
  122. package/dist/web/assets/{dagre-BM42HDAG-CbNpoO2d.js → dagre-BM42HDAG-Bwc3Ntc-.js} +1 -1
  123. package/dist/web/assets/{diagram-2AECGRRQ-CG3hKxAX.js → diagram-2AECGRRQ-zkydBsMT.js} +1 -1
  124. package/dist/web/assets/{diagram-5GNKFQAL-mQu9M28s.js → diagram-5GNKFQAL-p9As3QtI.js} +1 -1
  125. package/dist/web/assets/{diagram-KO2AKTUF-BSeEp_Uw.js → diagram-KO2AKTUF-N_pUU-iU.js} +1 -1
  126. package/dist/web/assets/{diagram-LMA3HP47-D6yRdao-.js → diagram-LMA3HP47-CA2rl4Gq.js} +1 -1
  127. package/dist/web/assets/{diagram-OG6HWLK6-BUBUW9wD.js → diagram-OG6HWLK6-BoXxBlUC.js} +1 -1
  128. package/dist/web/assets/{dialog-D1lkhqVL.js → dialog-CnjWIdWp.js} +1 -1
  129. package/dist/web/assets/{erDiagram-TEJ5UH35-BQSZgRqq.js → erDiagram-TEJ5UH35-CVBHQTCN.js} +1 -1
  130. package/dist/web/assets/{file-BijjwxRR.js → file-jMLGlXos.js} +1 -1
  131. package/dist/web/assets/{flowDiagram-I6XJVG4X-D-eqyr5Y.js → flowDiagram-I6XJVG4X-kK7iKiAv.js} +1 -1
  132. package/dist/web/assets/{folder-picker-Czxm0_S9.js → folder-picker-CR8LiSQf.js} +1 -1
  133. package/dist/web/assets/{freemarker2-B4RvEa_U.js → freemarker2-CTMcTiBd.js} +1 -1
  134. package/dist/web/assets/{ganttDiagram-6RSMTGT7-BWGkCt_x.js → ganttDiagram-6RSMTGT7-CJVvLTkW.js} +1 -1
  135. package/dist/web/assets/{gitGraphDiagram-PVQCEYII-DGVc49pR.js → gitGraphDiagram-PVQCEYII-D8uj_SCw.js} +1 -1
  136. package/dist/web/assets/{globe-Ce01-45x.js → globe-FFVKce9-.js} +1 -1
  137. package/dist/web/assets/{handlebars-oyT7drqS.js → handlebars-NHxbmOs3.js} +1 -1
  138. package/dist/web/assets/{html-CxxWf2Qk.js → html-oFqzz32q.js} +1 -1
  139. package/dist/web/assets/{htmlMode-RfAHh5Jj.js → htmlMode-sgeetciK.js} +1 -1
  140. package/dist/web/assets/index-408ws6FY.css +1 -0
  141. package/dist/web/assets/index-9KNEuwGB.js +122 -0
  142. package/dist/web/assets/{index-hpHE1jXY.js → index-BzqyZD7L.js} +1 -1
  143. package/dist/web/assets/{index-DWrSDMX2.js → index-C5lNOKJb.js} +1 -1
  144. package/dist/web/assets/{index-Disv1kQS.js → index-C9vIrQ3Q.js} +1 -1
  145. package/dist/web/assets/{index-Pt4TsHjm.js → index-Cm_7q6ju.js} +1 -1
  146. package/dist/web/assets/{index-Dh-tgeaL.js → index-CwfWGzxi.js} +4 -4
  147. package/dist/web/assets/{index-DyJfPc4O.js → index-D8TNxwWk.js} +1 -1
  148. package/dist/web/assets/{index-T38iVLEb.js → index-biTPUXAe.js} +1 -1
  149. package/dist/web/assets/{index-BMZFF1UJ.js → index-gqrRO6ZJ.js} +1 -1
  150. package/dist/web/assets/{infoDiagram-5YYISTIA-DLPs3dV_.js → infoDiagram-5YYISTIA-CExRZE3Y.js} +1 -1
  151. package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-d_OrnwDY.js → ishikawaDiagram-YF4QCWOH-CsqM2X33.js} +1 -1
  152. package/dist/web/assets/javascript-COt-61wn.js +1 -0
  153. package/dist/web/assets/{journeyDiagram-JHISSGLW-EtU1st_0.js → journeyDiagram-JHISSGLW-DX9OMhPD.js} +1 -1
  154. package/dist/web/assets/{jsonMode-Bqh5vIxx.js → jsonMode-VQg5cpLV.js} +1 -1
  155. package/dist/web/assets/{kanban-definition-UN3LZRKU-ZWzUnsFK.js → kanban-definition-UN3LZRKU-CWHMx6vc.js} +1 -1
  156. package/dist/web/assets/{kbd-IjDGsZ2B.js → kbd-GGofKfKu.js} +1 -1
  157. package/dist/web/assets/{key-round-CbnAuUxp.js → key-round-p-Fpykfj.js} +1 -1
  158. package/dist/web/assets/{layers-Pdy9vZ6K.js → layers-pbPa_eZq.js} +1 -1
  159. package/dist/web/assets/{linear-D7n23wcA.js → linear-9VwGhaEA.js} +1 -1
  160. package/dist/web/assets/{link-Bl1ZbjFh.js → link-CG4UwIS1.js} +1 -1
  161. package/dist/web/assets/{liquid-Ds47iVLN.js → liquid-SJInUaiB.js} +1 -1
  162. package/dist/web/assets/{lspLanguageFeatures-Dcch3bsp.js → lspLanguageFeatures-DJOdVyBc.js} +1 -1
  163. package/dist/web/assets/{mdx-CM7ky-2B.js → mdx-CToYr-3b.js} +1 -1
  164. package/dist/web/assets/{menu-DubntFHq.js → menu-BFQ1mKU7.js} +1 -1
  165. package/dist/web/assets/{mermaid-NOHMQCX5-nBsnjgeU.js → mermaid-NOHMQCX5-BOViobLk.js} +58 -58
  166. package/dist/web/assets/{message-square-BE-pW0m_.js → message-square-BYZsUsLz.js} +1 -1
  167. package/dist/web/assets/{mindmap-definition-RKZ34NQL-ClHORNTt.js → mindmap-definition-RKZ34NQL-BTcDnn6X.js} +1 -1
  168. package/dist/web/assets/{modal-CHLtZWvx.js → modal-BT_7Lhtw.js} +1 -1
  169. package/dist/web/assets/{monaco-BbAmalE2.js → monaco-DUftz48_.js} +3 -3
  170. package/dist/web/assets/{nav-row-DH7rNq1V.js → nav-row-BjDckaPC.js} +1 -1
  171. package/dist/web/assets/{paperclip-YP3Bk1lI.js → paperclip-CFAH5neE.js} +1 -1
  172. package/dist/web/assets/{pencil-BFDuhbtW.js → pencil-Bg9TWm_-.js} +1 -1
  173. package/dist/web/assets/{people-DEeNaaLt.js → people-BIQwajM2.js} +1 -1
  174. package/dist/web/assets/{pieDiagram-4H26LBE5-BUOaKIyP.js → pieDiagram-4H26LBE5-CRjqdnP8.js} +1 -1
  175. package/dist/web/assets/{popover-CUmBEXxL.js → popover-DpYhrrCE.js} +1 -1
  176. package/dist/web/assets/{python-BODTtU6m.js → python-DbmUucgH.js} +1 -1
  177. package/dist/web/assets/{quadrantDiagram-W4KKPZXB-Caezg-U2.js → quadrantDiagram-W4KKPZXB-uvA8TYC6.js} +1 -1
  178. package/dist/web/assets/{razor-CHm8DY1p.js → razor-B0AJhw7W.js} +1 -1
  179. package/dist/web/assets/{requirementDiagram-4Y6WPE33-BtdP3Ka6.js → requirementDiagram-4Y6WPE33-BWLugaKX.js} +1 -1
  180. package/dist/web/assets/{sankeyDiagram-5OEKKPKP-Bv74oA-n.js → sankeyDiagram-5OEKKPKP-DHrbZZv8.js} +1 -1
  181. package/dist/web/assets/{search-ClmOQd4H.js → search-BH8nze-X.js} +1 -1
  182. package/dist/web/assets/{select-BB7QG08o.js → select-BegY9RkP.js} +1 -1
  183. package/dist/web/assets/{select-CYtYPr-c.js → select-EcEr5QSS.js} +1 -1
  184. package/dist/web/assets/{sequenceDiagram-3UESZ5HK-DuNPl-dg.js → sequenceDiagram-3UESZ5HK-CKwEKcTn.js} +1 -1
  185. package/dist/web/assets/{server-bEB_DY55.js → server-BxEUjx0l.js} +1 -1
  186. package/dist/web/assets/{stateDiagram-AJRCARHV-B72HAgBJ.js → stateDiagram-AJRCARHV-vhgmSpa9.js} +1 -1
  187. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-C0z5OTY4.js +1 -0
  188. package/dist/web/assets/{textarea-BqYPvoO-.js → textarea-CvoGGROs.js} +1 -1
  189. package/dist/web/assets/{timeline-definition-PNZ67QCA-DTmHUgmQ.js → timeline-definition-PNZ67QCA-bSDKcwDZ.js} +1 -1
  190. package/dist/web/assets/{toggles-a5fkwT_D.js → toggles-CEDUD08u.js} +1 -1
  191. package/dist/web/assets/{tooltip-DuyJMci9.js → tooltip-CKaUbVZ7.js} +1 -1
  192. package/dist/web/assets/{track-DCjyrv3n.js → track-1qw2cvTy.js} +1 -1
  193. package/dist/web/assets/{trash-2-CIyBqaVy.js → trash-2-ChiXon87.js} +1 -1
  194. package/dist/web/assets/{tsMode-C1UXAkMh.js → tsMode-bnV8W2Ru.js} +1 -1
  195. package/dist/web/assets/{typescript-Biht2wHb.js → typescript-d1MHIeEx.js} +1 -1
  196. package/dist/web/assets/{upload-D05XS5-G.js → upload-CQ-cQaOz.js} +1 -1
  197. package/dist/web/assets/{use-profiles-CrWTyz1G.js → use-profiles-CJzoqarX.js} +1 -1
  198. package/dist/web/assets/{use-providers-Dv1Nk6s7.js → use-providers-S29TCag2.js} +1 -1
  199. package/dist/web/assets/{use-translation-settings-CcUnipVO.js → use-translation-settings-DifNJaCu.js} +1 -1
  200. package/dist/web/assets/{useNormalizedLogs-D9W1cT4R.js → useNormalizedLogs-BVdY8qDk.js} +1 -1
  201. package/dist/web/assets/{useTokenUsage-CZ5mDshD.js → useTokenUsage-nh_KDA3q.js} +1 -1
  202. package/dist/web/assets/{vennDiagram-CIIHVFJN-ByoNydXe.js → vennDiagram-CIIHVFJN-D7QbDYzC.js} +1 -1
  203. package/dist/web/assets/{wardley-L42UT6IY-BqdMkOLl.js → wardley-L42UT6IY-C9aHlNVa.js} +1 -1
  204. package/dist/web/assets/{wardleyDiagram-YWT4CUSO-AO22HrEF.js → wardleyDiagram-YWT4CUSO-CZhubDRC.js} +1 -1
  205. package/dist/web/assets/{xml-NLGqNFDv.js → xml-DAKKxHAr.js} +1 -1
  206. package/dist/web/assets/{xychartDiagram-2RQKCTM6-vzPgjgqW.js → xychartDiagram-2RQKCTM6-BgDlpFBh.js} +1 -1
  207. package/dist/web/assets/{yaml-AWlGS_Ig.js → yaml-D_hSVVz0.js} +1 -1
  208. package/dist/web/index.html +2 -2
  209. package/node_modules/@agent-tower/shared/dist/index.d.ts +1 -0
  210. package/node_modules/@agent-tower/shared/dist/index.d.ts.map +1 -1
  211. package/node_modules/@agent-tower/shared/dist/index.js +1 -0
  212. package/node_modules/@agent-tower/shared/dist/index.js.map +1 -1
  213. package/node_modules/@agent-tower/shared/dist/task-hierarchy.d.ts +17 -0
  214. package/node_modules/@agent-tower/shared/dist/task-hierarchy.d.ts.map +1 -0
  215. package/node_modules/@agent-tower/shared/dist/task-hierarchy.js +12 -0
  216. package/node_modules/@agent-tower/shared/dist/task-hierarchy.js.map +1 -0
  217. package/node_modules/@agent-tower/shared/dist/types.d.ts +20 -4
  218. package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
  219. package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
  220. package/package.json +1 -1
  221. package/prisma/migrations/20261027000000_preserve_workspace_history/migration.sql +2 -0
  222. package/prisma/schema.prisma +2 -0
  223. package/dist/web/assets/MobileTaskDetail-M_Kke9rL.js +0 -3
  224. package/dist/web/assets/ProjectKanbanPage-soDqevjQ.js +0 -12
  225. package/dist/web/assets/TaskDetail-CqhPxvg3.js +0 -1
  226. package/dist/web/assets/channel-CUa8oyzC.js +0 -1
  227. package/dist/web/assets/check-DaZqOL8l.js +0 -1
  228. package/dist/web/assets/classDiagram-4FO5ZUOK-BAx111NO.js +0 -1
  229. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-BAx111NO.js +0 -1
  230. package/dist/web/assets/index-BN53vGxp.css +0 -1
  231. package/dist/web/assets/index-Cc3qXMhU.js +0 -122
  232. package/dist/web/assets/javascript-BNXtViSL.js +0 -1
  233. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-Bu3l_CgB.js +0 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=codex-mcp-windows.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-mcp-windows.test.d.ts","sourceRoot":"","sources":["../../../src/executors/__tests__/codex-mcp-windows.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,128 @@
1
+ import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { buildAgentTowerMcpConfigOverrides, getCodexMcpServerNamesForLaunch, resetCodexMcpServerCache, } from '../codex.executor.js';
6
+ import { ExecutionEnv } from '../execution-env.js';
7
+ const mocks = vi.hoisted(() => ({ execFile: vi.fn() }));
8
+ vi.mock('child_process', async (importOriginal) => ({
9
+ ...await importOriginal(),
10
+ execFile: mocks.execFile,
11
+ }));
12
+ const nativePlatform = process.platform;
13
+ const platformDescriptor = Object.getOwnPropertyDescriptor(process, 'platform');
14
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-mcp-windows-'));
15
+ const workingDir = path.join(tmpDir, 'project with spaces');
16
+ const codexHome = path.join(tmpDir, 'codex-home');
17
+ fs.mkdirSync(path.join(workingDir, '.codex'), { recursive: true });
18
+ fs.mkdirSync(codexHome);
19
+ fs.writeFileSync(path.join(workingDir, '.codex', 'config.toml'), '[mcp_servers.agent-tower]\ncommand="old-tower"');
20
+ beforeEach(() => {
21
+ Object.defineProperty(process, 'platform', { ...platformDescriptor, value: 'win32' });
22
+ resetCodexMcpServerCache();
23
+ mocks.execFile.mockReset();
24
+ mocks.execFile.mockImplementation((_command, _args, _options, callback) => {
25
+ callback(null, JSON.stringify([{ name: 'agent-tower' }, { name: 'project-only' }]));
26
+ });
27
+ });
28
+ afterEach(() => {
29
+ Object.defineProperty(process, 'platform', platformDescriptor);
30
+ resetCodexMcpServerCache();
31
+ });
32
+ afterAll(() => { fs.rmSync(tmpDir, { recursive: true, force: true }); });
33
+ function environment(overrides = {}) {
34
+ return { CODEX_HOME: codexHome, PROVIDER_SENTINEL: 'provider-value', ...overrides };
35
+ }
36
+ describe('Windows Codex MCP launch discovery', () => {
37
+ it.each(['cmd', 'BAT'])('loads project MCPs through a .%s shim with cwd, env and config arguments', async (extension) => {
38
+ const executable = path.join(tmpDir, `npm directory/codex.${extension}`);
39
+ const env = environment({ ComSpec: 'custom-cmd.exe' });
40
+ const overrides = ['--profile', 'work profile', '-c', 'model="test model"'];
41
+ const names = await getCodexMcpServerNamesForLaunch(overrides, undefined, { program: executable, args: overrides }, env, workingDir);
42
+ expect(names).toEqual(new Set(['agent-tower', 'project-only']));
43
+ expect(mocks.execFile).toHaveBeenCalledOnce();
44
+ const [command, args, options] = mocks.execFile.mock.calls[0];
45
+ expect(command).toBe('custom-cmd.exe');
46
+ expect(args.slice(0, 3)).toEqual(['/d', '/s', '/c']);
47
+ expect(args[3]).toContain('codex.' + extension);
48
+ expect(args[3]).toContain('--profile "work profile"');
49
+ expect(args[3]).toContain('-c "model=\\"test model\\""');
50
+ expect(args[3]).toMatch(/ mcp list --json"$/);
51
+ expect(options).toMatchObject({
52
+ cwd: fs.realpathSync(workingDir), env, timeout: 3000, windowsHide: true, windowsVerbatimArguments: true,
53
+ });
54
+ expect(options.shell).not.toBe(true);
55
+ const sessionEnv = ExecutionEnv.default(workingDir).merge({
56
+ AGENT_TOWER_AGENT_CREDENTIAL: 'windows-session-credential',
57
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
58
+ AGENT_TOWER_SESSION_ID: 'windows-mcp-session',
59
+ });
60
+ expect(buildAgentTowerMcpConfigOverrides(sessionEnv, names)).toContain('mcp_servers.agent-tower.enabled=false');
61
+ });
62
+ it.each(['ComSpec', 'COMSPEC', undefined])('selects the configured command interpreter (%s)', async (key) => {
63
+ const env = environment(key ? { [key]: 'selected-cmd.exe' } : {});
64
+ await getCodexMcpServerNamesForLaunch([], undefined, { program: path.join(tmpDir, 'codex.cmd'), args: [] }, env, workingDir);
65
+ expect(mocks.execFile.mock.calls[0][0]).toBe(key ? 'selected-cmd.exe' : 'cmd.exe');
66
+ });
67
+ it('resolves an npm shim from a Windows Path variable', async () => {
68
+ const binDir = path.join(tmpDir, 'npm-bin');
69
+ fs.mkdirSync(binDir, { recursive: true });
70
+ fs.writeFileSync(path.join(binDir, 'codex.CMD'), '');
71
+ const env = environment({ Path: binDir, PATHEXT: '.EXE;.CMD;.BAT' });
72
+ await getCodexMcpServerNamesForLaunch([], undefined, { program: 'codex', args: [] }, env, workingDir);
73
+ expect(mocks.execFile.mock.calls[0][0]).toBe('cmd.exe');
74
+ expect(mocks.execFile.mock.calls[0][1][3]).toContain('codex.CMD');
75
+ });
76
+ it('retains a relative custom shim and resolves it from the target workspace', async () => {
77
+ await getCodexMcpServerNamesForLaunch([], undefined, { program: './tools/custom-codex.cmd', args: ['--profile', 'custom'] }, environment(), workingDir);
78
+ const args = mocks.execFile.mock.calls[0][1];
79
+ expect(args[3]).toContain('custom-codex.cmd');
80
+ // cmd quoting escapes Windows path separators; normalize only for this assertion.
81
+ expect(args[3].replace(/\\\\/g, '\\')).toContain(path.join(workingDir, 'tools', 'custom-codex.cmd'));
82
+ expect(args[3]).toContain('--profile custom mcp list --json');
83
+ });
84
+ it('keeps native executables and missing custom commands on the direct path', async () => {
85
+ for (const program of [path.join(tmpDir, 'native codex.exe'), 'custom-missing-codex']) {
86
+ const args = ['-c', 'model="test model"'];
87
+ await getCodexMcpServerNamesForLaunch(args, undefined, { program, args }, environment(), workingDir);
88
+ expect(mocks.execFile).toHaveBeenLastCalledWith(program, [...args, 'mcp', 'list', '--json'], expect.not.objectContaining({ windowsVerbatimArguments: true }), expect.any(Function));
89
+ }
90
+ });
91
+ it('retains the existing global fallback if the command interpreter fails', async () => {
92
+ const configPath = path.join(tmpDir, 'fallback.toml');
93
+ fs.writeFileSync(configPath, '[mcp_servers.global-only]\ncommand="fallback-mcp"');
94
+ mocks.execFile.mockImplementation((_command, _args, _options, callback) => callback(new Error('interpreter failed'), ''));
95
+ const names = await getCodexMcpServerNamesForLaunch([], configPath, { program: path.join(tmpDir, 'codex.cmd'), args: [] }, environment(), workingDir);
96
+ expect(names).toEqual(new Set(['global-only']));
97
+ });
98
+ it('does not wrap a .cmd filename on Unix', async () => {
99
+ Object.defineProperty(process, 'platform', { ...platformDescriptor, value: 'linux' });
100
+ const executable = path.join(tmpDir, 'unix-codex.cmd');
101
+ await getCodexMcpServerNamesForLaunch([], undefined, { program: executable, args: [] }, environment(), workingDir);
102
+ expect(mocks.execFile).toHaveBeenCalledWith(executable, ['mcp', 'list', '--json'], expect.not.objectContaining({ windowsVerbatimArguments: true }), expect.any(Function));
103
+ });
104
+ });
105
+ describe.skipIf(nativePlatform !== 'win32')('native Windows npm shim discovery', () => {
106
+ it.each(['cmd', 'bat'])('round-trips project configuration and arguments through a real .%s shim', async (extension) => {
107
+ const nativeChildProcess = await vi.importActual('child_process');
108
+ mocks.execFile.mockImplementation(nativeChildProcess.execFile);
109
+ const script = path.join(tmpDir, 'native-probe.cjs');
110
+ const captured = path.join(tmpDir, `native-args-${extension}.json`);
111
+ fs.writeFileSync(script, [
112
+ 'const fs = require("node:fs");',
113
+ `fs.writeFileSync(${JSON.stringify(captured)}, JSON.stringify({args: process.argv.slice(2), cwd: process.cwd(), sentinel: process.env.PROVIDER_SENTINEL}));`,
114
+ 'const config = fs.readFileSync(".codex/config.toml", "utf8");',
115
+ 'process.stdout.write(JSON.stringify(config.includes("mcp_servers.agent-tower") ? [{name:"agent-tower"}] : []));',
116
+ ].join('\n'));
117
+ const shim = path.join(tmpDir, `native codex.${extension}`);
118
+ fs.writeFileSync(shim, `@echo off\r\n"${process.execPath}" "${script}" %*\r\n`);
119
+ const args = ['-c', 'model="test model"'];
120
+ const env = environment({ ...process.env, CODEX_HOME: codexHome });
121
+ const names = await getCodexMcpServerNamesForLaunch(args, undefined, { program: shim, args }, env, workingDir);
122
+ expect(names).toEqual(new Set(['agent-tower']));
123
+ expect(JSON.parse(fs.readFileSync(captured, 'utf8'))).toEqual({
124
+ args: [...args, 'mcp', 'list', '--json'], cwd: fs.realpathSync(workingDir), sentinel: 'provider-value',
125
+ });
126
+ });
127
+ });
128
+ //# sourceMappingURL=codex-mcp-windows.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-mcp-windows.test.js","sourceRoot":"","sources":["../../../src/executors/__tests__/codex-mcp-windows.test.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,8 @@ import { describe, expect, it, afterAll, afterEach, beforeEach, vi } from 'vites
2
2
  import * as fs from 'fs';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
- import { buildAgentTowerMcpEnvConfigOverrides, getCodexDeclaredMcpServerNames, queryCodexMcpServerNames, detectDeclaredMcpServers, getCachedCodexMcpServerNames, resetCodexMcpServerCache, waitForCodexMcpServerCacheRefresh, CODEX_HTTP_ONLY_OPENAI_PROVIDER_ID, getCodexPermissionMode, CodexExecutor, } from '../codex.executor.js';
5
+ import { parse as parseToml } from 'smol-toml';
6
+ import { buildAgentTowerMcpConfigOverrides, buildAgentTowerMcpEnvConfigOverrides, getCodexDeclaredMcpServerNames, queryCodexMcpServerNames, detectDeclaredMcpServers, getCachedCodexMcpServerNames, getCodexMcpServerNamesForLaunch, resetCodexMcpServerCache, waitForCodexMcpServerCacheRefresh, CODEX_HTTP_ONLY_OPENAI_PROVIDER_ID, getCodexPermissionMode, CodexExecutor, } from '../codex.executor.js';
6
7
  import { AGENT_SUBPROCESS_BLOCKED_ENV_KEYS, AGENT_TOWER_MCP_IDENTITY_ENV_KEYS, AGENT_TOWER_MCP_SERVICE_ENV_KEYS, ExecutionEnv, } from '../execution-env.js';
7
8
  import { getExecutorByProvider, createExecutorFromProvider } from '../index.js';
8
9
  import { createProvider as createRawProvider, reloadProviders, updateProvider } from '../providers.js';
@@ -637,6 +638,7 @@ function createAsyncProbeCodex(name, stdout, options = {}) {
637
638
  const script = [
638
639
  '#!/bin/sh',
639
640
  `printf '1\\n' >> "${countFile}"`,
641
+ `pwd >> "${path.join(binDir, 'probe-cwds')}"`,
640
642
  `sleep ${delaySeconds}`,
641
643
  'cat <<\'FAKE_EOF\'',
642
644
  stdout,
@@ -653,6 +655,34 @@ function probeCount(binDir) {
653
655
  return 0;
654
656
  return fs.readFileSync(file, 'utf-8').trim().split('\n').filter(Boolean).length;
655
657
  }
658
+ function createWorkspaceProbeCodex(name, options = {}) {
659
+ const binDir = path.join(tmpDir, `workspace-probe-${name}`);
660
+ fs.mkdirSync(binDir, { recursive: true });
661
+ const script = `#!${process.execPath}
662
+ const fs = require('node:fs');
663
+ const path = require('node:path');
664
+ fs.appendFileSync(${JSON.stringify(path.join(binDir, 'probe-count'))}, '1\\n');
665
+ fs.appendFileSync(${JSON.stringify(path.join(binDir, 'probe-cwds'))}, process.cwd() + '\\n');
666
+ setTimeout(() => {
667
+ const files = [path.join(process.env.CODEX_HOME, 'config.toml')];
668
+ if (!${Boolean(options.ignoreProject)}) {
669
+ for (let dir = process.cwd(); ; dir = path.dirname(dir)) {
670
+ files.push(path.join(dir, '.codex', 'config.toml'));
671
+ if (path.dirname(dir) === dir) break;
672
+ }
673
+ }
674
+ const names = new Set();
675
+ for (const file of files) {
676
+ try {
677
+ for (const match of fs.readFileSync(file, 'utf8').matchAll(/^\\[mcp_servers\\.([a-zA-Z0-9_-]+)\\]/gm)) names.add(match[1]);
678
+ } catch {}
679
+ }
680
+ process.stdout.write(JSON.stringify([...names].map(name => ({ name }))));
681
+ }, ${options.delayMs ?? 0});
682
+ `;
683
+ fs.writeFileSync(path.join(binDir, 'codex'), script, { mode: 0o755 });
684
+ return binDir;
685
+ }
656
686
  describe('getCachedCodexMcpServerNames', () => {
657
687
  it('returns fallback names immediately and probes without blocking the event loop', async () => {
658
688
  const binDir = createAsyncProbeCodex('nonblocking', JSON.stringify([{ name: 'agent-tower' }]), { delayMs: 500 });
@@ -724,6 +754,126 @@ describe('getCachedCodexMcpServerNames', () => {
724
754
  await waitForCodexMcpServerCacheRefresh();
725
755
  expect(probeCount(binDir)).toBe(1);
726
756
  });
757
+ it('awaits effective workspace and ancestor declarations on the first launch without blocking the event loop', async () => {
758
+ const binDir = createWorkspaceProbeCodex('workspace-fallback', { delayMs: 100 });
759
+ const home = path.join(tmpDir, 'workspace-fallback-home');
760
+ const project = path.join(tmpDir, 'workspace-fallback-project');
761
+ const workingDir = path.join(project, 'packages', 'app');
762
+ fs.mkdirSync(home, { recursive: true });
763
+ fs.mkdirSync(path.join(project, '.codex'), { recursive: true });
764
+ fs.mkdirSync(path.join(workingDir, '.codex'), { recursive: true });
765
+ fs.writeFileSync(path.join(home, 'config.toml'), '[mcp_servers.global-tool]\ncommand="global-mcp"');
766
+ fs.writeFileSync(path.join(project, '.codex', 'config.toml'), '[mcp_servers.agent-tower]\nurl="https://old.example/mcp"');
767
+ const sessionEnv = ExecutionEnv.default(workingDir).merge({
768
+ AGENT_TOWER_AGENT_CREDENTIAL: 'project-session-credential',
769
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
770
+ AGENT_TOWER_SESSION_ID: 'project-fallback-session',
771
+ });
772
+ const generatedName = buildAgentTowerMcpConfigOverrides(sessionEnv, new Set())
773
+ .find(arg => /^mcp_servers\.agent-tower-[a-f0-9]{8}=/.test(arg))
774
+ .split('=')[0].slice('mcp_servers.'.length);
775
+ fs.writeFileSync(path.join(workingDir, '.codex', 'config.toml'), [
776
+ '[mcp_servers.agent-tower-dev]', 'command="old-dev-mcp"',
777
+ `[mcp_servers.${generatedName}]`, 'url="https://old-name.example/mcp"',
778
+ ].join('\n'));
779
+ const environment = { ...process.env, PATH: `${binDir}:${savedPath}`, CODEX_HOME: home };
780
+ const probe = { program: 'codex', args: [] };
781
+ const expected = new Set(['global-tool', 'agent-tower', 'agent-tower-dev', generatedName]);
782
+ let eventLoopAdvanced = false;
783
+ const tick = new Promise(resolve => setTimeout(() => { eventLoopAdvanced = true; resolve(); }, 0));
784
+ const initial = await getCodexMcpServerNamesForLaunch([], undefined, probe, environment, workingDir);
785
+ expect(eventLoopAdvanced).toBe(true);
786
+ await tick;
787
+ expect(initial).toEqual(expected);
788
+ const overrides = buildAgentTowerMcpConfigOverrides(sessionEnv, initial);
789
+ expect(overrides).toContain('mcp_servers.agent-tower.enabled=false');
790
+ expect(overrides).toContain('mcp_servers.agent-tower-dev.enabled=false');
791
+ expect(overrides.some(arg => arg.startsWith(`mcp_servers.${generatedName}-2=`))).toBe(true);
792
+ await waitForCodexMcpServerCacheRefresh();
793
+ expect(getCachedCodexMcpServerNames([], undefined, probe, environment, workingDir)).toEqual(expected);
794
+ expect(fs.readFileSync(path.join(binDir, 'probe-cwds'), 'utf8').trim()).toBe(fs.realpathSync(workingDir));
795
+ });
796
+ it('isolates cached probes for workspaces sharing the same executable and CODEX_HOME', async () => {
797
+ const binDir = createWorkspaceProbeCodex('workspace-isolation');
798
+ const home = path.join(tmpDir, 'workspace-isolation-home');
799
+ fs.mkdirSync(home, { recursive: true });
800
+ const environment = { ...process.env, PATH: `${binDir}:${savedPath}`, CODEX_HOME: home };
801
+ const probe = { program: 'codex', args: [] };
802
+ const workspaces = ['first', 'second'].map(name => {
803
+ const workspace = path.join(tmpDir, `isolated-${name}`);
804
+ fs.mkdirSync(path.join(workspace, '.codex'), { recursive: true });
805
+ fs.writeFileSync(path.join(workspace, '.codex', 'config.toml'), `[mcp_servers.${name}-tool]\ncommand="${name}-mcp"`);
806
+ return workspace;
807
+ });
808
+ const [first, second] = await Promise.all(workspaces.map(workspace => getCodexMcpServerNamesForLaunch([], undefined, probe, environment, workspace)));
809
+ expect(first).toEqual(new Set(['first-tool']));
810
+ expect(second).toEqual(new Set(['second-tool']));
811
+ expect(probeCount(binDir)).toBe(2);
812
+ expect(new Set(fs.readFileSync(path.join(binDir, 'probe-cwds'), 'utf8').trim().split('\n'))).toEqual(new Set(workspaces.map(workspace => fs.realpathSync(workspace))));
813
+ expect(getCachedCodexMcpServerNames([], undefined, probe, environment, workspaces[0])).toEqual(new Set(['first-tool']));
814
+ expect(getCachedCodexMcpServerNames([], undefined, probe, environment, workspaces[1])).toEqual(new Set(['second-tool']));
815
+ });
816
+ it.each(['workspace', 'ancestor'])('invalidates the cache when a %s config is created, changed or removed', async (scope) => {
817
+ const binDir = createWorkspaceProbeCodex(`workspace-config-change-${scope}`);
818
+ const home = path.join(tmpDir, `project-config-change-home-${scope}`);
819
+ const project = path.join(tmpDir, `project-config-change-${scope}`);
820
+ const workingDir = path.join(project, 'nested', 'app');
821
+ fs.mkdirSync(home, { recursive: true });
822
+ fs.mkdirSync(workingDir, { recursive: true });
823
+ const environment = { ...process.env, PATH: `${binDir}:${savedPath}`, CODEX_HOME: home };
824
+ const probe = { program: 'codex', args: [] };
825
+ const configPath = path.join(scope === 'workspace' ? workingDir : project, '.codex', 'config.toml');
826
+ const lookup = () => getCodexMcpServerNamesForLaunch([], undefined, probe, environment, workingDir);
827
+ expect(await lookup()).toEqual(new Set());
828
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
829
+ fs.writeFileSync(configPath, '[mcp_servers.agent-tower]\ncommand="old-mcp"');
830
+ expect(await lookup()).toEqual(new Set(['agent-tower']));
831
+ fs.writeFileSync(configPath, '[mcp_servers.agent-tower-dev]\ncommand="another-mcp"');
832
+ expect(await lookup()).toEqual(new Set(['agent-tower-dev']));
833
+ fs.unlinkSync(configPath);
834
+ expect(await lookup()).toEqual(new Set());
835
+ expect(probeCount(binDir)).toBeGreaterThanOrEqual(3);
836
+ });
837
+ it('does not fabricate disabled aliases from a project ignored by the CLI trust rules', async () => {
838
+ const binDir = createWorkspaceProbeCodex('untrusted-project', { ignoreProject: true });
839
+ const home = path.join(tmpDir, 'untrusted-project-home');
840
+ const workingDir = path.join(tmpDir, 'untrusted-project-workspace');
841
+ fs.mkdirSync(home, { recursive: true });
842
+ fs.mkdirSync(path.join(workingDir, '.codex'), { recursive: true });
843
+ fs.writeFileSync(path.join(workingDir, '.codex', 'config.toml'), '[mcp_servers.agent-tower]\ncommand="ignored-project-mcp"');
844
+ const environment = { ...process.env, PATH: `${binDir}:${savedPath}`, CODEX_HOME: home };
845
+ const names = await getCodexMcpServerNamesForLaunch([], undefined, { program: 'codex', args: [] }, environment, workingDir);
846
+ const sessionEnv = ExecutionEnv.default(workingDir).merge({
847
+ AGENT_TOWER_AGENT_CREDENTIAL: 'untrusted-session-credential',
848
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
849
+ AGENT_TOWER_SESSION_ID: 'untrusted-project-session',
850
+ });
851
+ expect(names).toEqual(new Set());
852
+ const overrides = buildAgentTowerMcpConfigOverrides(sessionEnv, names);
853
+ expect(overrides).not.toContain('mcp_servers.agent-tower.enabled=false');
854
+ expect(managedMcpServer(overrides).enabled).toBe(true);
855
+ });
856
+ it('waits for its shared refresh when a launch cache expires', async () => {
857
+ const name = 'launch-expired-cache';
858
+ const binDir = createAsyncProbeCodex(name, '[{"name":"previous-tool"}]');
859
+ const home = path.join(tmpDir, 'launch-expired-home');
860
+ fs.mkdirSync(home, { recursive: true });
861
+ const environment = { ...process.env, PATH: `${binDir}:${savedPath}`, CODEX_HOME: home };
862
+ const probe = { program: 'codex', args: [] };
863
+ const date = vi.spyOn(Date, 'now').mockReturnValue(1_000_000);
864
+ try {
865
+ expect(await getCodexMcpServerNamesForLaunch([], undefined, probe, environment, tmpDir)).toEqual(new Set(['previous-tool']));
866
+ createAsyncProbeCodex(name, '[{"name":"current-tool"}]', { delayMs: 50 });
867
+ date.mockReturnValue(1_030_001);
868
+ const results = await Promise.all(Array.from({ length: 3 }, () => getCodexMcpServerNamesForLaunch([], undefined, probe, environment, tmpDir)));
869
+ expect(results).toEqual(Array.from({ length: 3 }, () => new Set(['current-tool'])));
870
+ expect(probeCount(binDir)).toBe(2);
871
+ }
872
+ finally {
873
+ await waitForCodexMcpServerCacheRefresh();
874
+ date.mockRestore();
875
+ }
876
+ });
727
877
  });
728
878
  // ─── getCodexDeclaredMcpServerNames (config.toml fallback) ───────
729
879
  describe('getCodexDeclaredMcpServerNames', () => {
@@ -1107,4 +1257,247 @@ describe('buildAgentTowerMcpEnvConfigOverrides', () => {
1107
1257
  ]);
1108
1258
  });
1109
1259
  });
1260
+ function managedMcpServer(args) {
1261
+ const assignment = args.find(arg => /^mcp_servers\.agent-tower-[a-f0-9]{8}(?:-\d+)?=/.test(arg));
1262
+ expect(assignment).toBeDefined();
1263
+ const parsed = parseToml(assignment);
1264
+ const name = Object.keys(parsed.mcp_servers)[0];
1265
+ const server = parsed.mcp_servers[name];
1266
+ server.env = {};
1267
+ for (const arg of args.filter(arg => arg.startsWith(`mcp_servers.${name}.env.`))) {
1268
+ const projected = parseToml(arg);
1269
+ Object.assign(server.env, projected.mcp_servers[name].env);
1270
+ }
1271
+ return server;
1272
+ }
1273
+ describe('managed Codex CLI MCP registration', () => {
1274
+ beforeEach(() => {
1275
+ for (const key of [
1276
+ ...AGENT_TOWER_MCP_IDENTITY_ENV_KEYS, ...AGENT_TOWER_MCP_SERVICE_ENV_KEYS,
1277
+ 'AGENT_TOWER_MCP_ENTRY', 'AGENT_TOWER_DESKTOP_RUNTIME_MODE', 'AGENT_TOWER_NODE_RUNTIME',
1278
+ 'AGENT_TOWER_DESKTOP_NODE', 'ELECTRON_RUN_AS_NODE',
1279
+ ])
1280
+ vi.stubEnv(key, undefined);
1281
+ });
1282
+ afterEach(() => { vi.unstubAllEnvs(); });
1283
+ it('registers the source MCP without user configuration and scopes its environment to the current session', () => {
1284
+ vi.stubEnv('AGENT_TOWER_AGENT_CREDENTIAL', 'parent-agent-secret');
1285
+ vi.stubEnv('AGENT_TOWER_INTERNAL_TOKEN', 'parent-internal-secret');
1286
+ vi.stubEnv('AGENT_TOWER_SESSION_ID', 'parent-session');
1287
+ vi.stubEnv('AGENT_TOWER_TEAM_RUN_ID', 'parent-team');
1288
+ const env = ExecutionEnv.default(tmpDir).merge({
1289
+ AGENT_TOWER_AGENT_CREDENTIAL: 'current-agent-credential',
1290
+ AGENT_TOWER_INTERNAL_TOKEN: 'must-not-forward-internal',
1291
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
1292
+ AGENT_TOWER_PORT: '42001',
1293
+ AGENT_TOWER_SESSION_ID: 'current-session',
1294
+ AGENT_TOWER_INVOCATION_ID: 'current-invocation',
1295
+ AGENT_TOWER_TEAM_RUN_ID: 'current-team',
1296
+ AGENT_TOWER_MEMBER_ID: 'current-member',
1297
+ OPENAI_API_KEY: 'provider-secret',
1298
+ });
1299
+ const args = buildAgentTowerMcpConfigOverrides(env, new Set());
1300
+ const server = managedMcpServer(args);
1301
+ expect(server).toEqual({
1302
+ command: process.execPath,
1303
+ args: ['--import', expect.stringMatching(/[\\/]tsx[\\/]dist[\\/]loader\.mjs$/), path.resolve('packages/server/src/mcp/index.ts')],
1304
+ enabled: true,
1305
+ env: {
1306
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
1307
+ AGENT_TOWER_PORT: '42001',
1308
+ AGENT_TOWER_AGENT_CREDENTIAL: 'current-agent-credential',
1309
+ AGENT_TOWER_SESSION_ID: 'current-session',
1310
+ AGENT_TOWER_INVOCATION_ID: 'current-invocation',
1311
+ AGENT_TOWER_TEAM_RUN_ID: 'current-team',
1312
+ AGENT_TOWER_MEMBER_ID: 'current-member',
1313
+ },
1314
+ });
1315
+ expect(args.join(' ')).not.toMatch(/parent-|must-not-forward-internal|provider-secret/);
1316
+ const registration = args.find(arg => /^mcp_servers\.agent-tower-[a-f0-9]{8}=/.test(arg));
1317
+ expect(registration).not.toContain('current-agent-credential');
1318
+ expect(registration).not.toContain('env');
1319
+ expect(args.some(arg => arg.startsWith('mcp_servers.agent-tower.env.'))).toBe(false);
1320
+ });
1321
+ it('uses a distinct backend and credential for each session without inheriting a previous team identity', () => {
1322
+ vi.stubEnv('AGENT_TOWER_TEAM_RUN_ID', 'stale-parent-team');
1323
+ const first = ExecutionEnv.default(tmpDir).merge({
1324
+ AGENT_TOWER_AGENT_CREDENTIAL: 'first-credential', AGENT_TOWER_PORT: '42001',
1325
+ AGENT_TOWER_SESSION_ID: 'first-session', AGENT_TOWER_TEAM_RUN_ID: 'first-team',
1326
+ });
1327
+ const second = ExecutionEnv.default(tmpDir).merge({
1328
+ AGENT_TOWER_AGENT_CREDENTIAL: 'second-credential', AGENT_TOWER_URL: 'http://127.0.0.1:42002',
1329
+ AGENT_TOWER_SESSION_ID: 'second-session',
1330
+ });
1331
+ const firstServer = managedMcpServer(buildAgentTowerMcpConfigOverrides(first, new Set()));
1332
+ const secondServer = managedMcpServer(buildAgentTowerMcpConfigOverrides(second, new Set()));
1333
+ expect(firstServer.env.AGENT_TOWER_URL).toBe('http://127.0.0.1:42001');
1334
+ expect(firstServer.env.AGENT_TOWER_TEAM_RUN_ID).toBe('first-team');
1335
+ expect(secondServer.env).toEqual({
1336
+ AGENT_TOWER_URL: 'http://127.0.0.1:42002',
1337
+ AGENT_TOWER_AGENT_CREDENTIAL: 'second-credential',
1338
+ AGENT_TOWER_SESSION_ID: 'second-session',
1339
+ });
1340
+ });
1341
+ it.each(['workspace', 'packaged'])('uses the trusted %s runtime entry and keeps its Node environment', mode => {
1342
+ const entry = path.join(tmpDir, `${mode} install`, 'dist', 'mcp', 'index.js');
1343
+ fs.mkdirSync(path.dirname(entry), { recursive: true });
1344
+ fs.writeFileSync(entry, '');
1345
+ vi.stubEnv('AGENT_TOWER_MCP_ENTRY', entry);
1346
+ vi.stubEnv('AGENT_TOWER_DESKTOP_RUNTIME_MODE', mode);
1347
+ vi.stubEnv('AGENT_TOWER_NODE_RUNTIME', 'C:\\Program Files\\Agent Tower\\node.exe');
1348
+ vi.stubEnv('ELECTRON_RUN_AS_NODE', '1');
1349
+ const env = ExecutionEnv.default(tmpDir).merge({
1350
+ AGENT_TOWER_URL: 'http://127.0.0.1:12580',
1351
+ AGENT_TOWER_INTERNAL_TOKEN: 'trusted-internal-token',
1352
+ });
1353
+ const server = managedMcpServer(buildAgentTowerMcpConfigOverrides(env, new Set()));
1354
+ expect(server.command).toBe(mode === 'packaged' ? 'C:\\Program Files\\Agent Tower\\node.exe' : process.execPath);
1355
+ expect(server.args).toEqual([entry]);
1356
+ expect(server.env).toEqual({
1357
+ AGENT_TOWER_URL: 'http://127.0.0.1:12580',
1358
+ AGENT_TOWER_INTERNAL_TOKEN: 'trusted-internal-token',
1359
+ ...(mode === 'packaged' ? { ELECTRON_RUN_AS_NODE: '1' } : {}),
1360
+ });
1361
+ });
1362
+ it('disables declared Tower endpoints without sharing credentials and leaves unrelated MCP servers alone', () => {
1363
+ const env = ExecutionEnv.default(tmpDir).merge({
1364
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
1365
+ AGENT_TOWER_AGENT_CREDENTIAL: 'session-credential',
1366
+ AGENT_TOWER_INTERNAL_TOKEN: 'must-not-forward-internal',
1367
+ AGENT_TOWER_SESSION_ID: 'session',
1368
+ });
1369
+ const args = buildAgentTowerMcpConfigOverrides(env, new Set(['agent-tower', 'agent-tower-dev', 'other-server']));
1370
+ expect(managedMcpServer(args).env.AGENT_TOWER_AGENT_CREDENTIAL).toBe('session-credential');
1371
+ expect(args).toContain('mcp_servers.agent-tower.enabled=false');
1372
+ expect(args).toContain('mcp_servers.agent-tower-dev.enabled=false');
1373
+ expect(args.some(arg => /^mcp_servers\.(agent-tower|agent-tower-dev)\.env\./.test(arg))).toBe(false);
1374
+ expect(args.join(' ')).not.toContain('must-not-forward-internal');
1375
+ expect(args.some(arg => arg.includes('other-server'))).toBe(false);
1376
+ });
1377
+ it('does not register MCP from credentials belonging to a parent process', () => {
1378
+ vi.stubEnv('AGENT_TOWER_AGENT_CREDENTIAL', 'parent-agent-secret');
1379
+ vi.stubEnv('AGENT_TOWER_INTERNAL_TOKEN', 'parent-internal-secret');
1380
+ const env = ExecutionEnv.default(tmpDir).set('AGENT_TOWER_SESSION_ID', 'session');
1381
+ expect(buildAgentTowerMcpConfigOverrides(env, new Set())).toEqual([]);
1382
+ expect(buildAgentTowerMcpConfigOverrides(env, new Set(['agent-tower']))).toEqual([
1383
+ '-c', 'mcp_servers.agent-tower.env.AGENT_TOWER_SESSION_ID="session"',
1384
+ ]);
1385
+ });
1386
+ it('chooses an unused managed server name without changing an existing server at the generated name', () => {
1387
+ const env = ExecutionEnv.default(tmpDir).merge({
1388
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
1389
+ AGENT_TOWER_AGENT_CREDENTIAL: 'session-credential',
1390
+ AGENT_TOWER_SESSION_ID: 'session-with-existing-server',
1391
+ });
1392
+ const initial = buildAgentTowerMcpConfigOverrides(env, new Set());
1393
+ const assignment = initial.find(arg => /^mcp_servers\.agent-tower-[a-f0-9]{8}=/.test(arg));
1394
+ const existingName = assignment.slice('mcp_servers.'.length, assignment.indexOf('='));
1395
+ const args = buildAgentTowerMcpConfigOverrides(env, new Set([existingName, `${existingName}-2`]));
1396
+ expect(args.some(arg => arg.startsWith(`mcp_servers.${existingName}-3=`))).toBe(true);
1397
+ expect(args.some(arg => arg.startsWith(`mcp_servers.${existingName}.`))).toBe(false);
1398
+ expect(args.some(arg => arg.startsWith(`mcp_servers.${existingName}-2.`))).toBe(false);
1399
+ expect(managedMcpServer(args).env.AGENT_TOWER_AGENT_CREDENTIAL).toBe('session-credential');
1400
+ });
1401
+ it.each(['FULL_ACCESS', 'ASK', 'AUTO_SANDBOX'])('disables project-level Tower aliases on the first new and resumed %s launch', async (executionPermission) => {
1402
+ const home = path.join(tmpDir, `project-launch-home-${executionPermission}`);
1403
+ const workingDir = path.join(tmpDir, `project-launch-workspace-${executionPermission}`);
1404
+ fs.mkdirSync(home, { recursive: true });
1405
+ fs.mkdirSync(path.join(workingDir, '.codex'), { recursive: true });
1406
+ const projectConfig = '[mcp_servers.agent-tower]\nurl="https://old-project.example/mcp"\n[mcp_servers.agent-tower-dev]\ncommand="old-dev-mcp"\n[mcp_servers.project-tool]\ncommand="project-mcp"';
1407
+ const configPath = path.join(workingDir, '.codex', 'config.toml');
1408
+ fs.writeFileSync(configPath, projectConfig);
1409
+ vi.stubEnv('CODEX_HOME', home);
1410
+ const binDir = createWorkspaceProbeCodex(`project-launch-${executionPermission}`, { delayMs: 50 });
1411
+ const executable = path.join(binDir, 'codex');
1412
+ const refresh = vi.spyOn(codexAskCapabilities, 'refreshCodexCliSupport').mockResolvedValue({ supported: true, executable });
1413
+ const support = vi.spyOn(codexAskCapabilities, 'cachedCodexCliSupport').mockReturnValue({ supported: true, executable });
1414
+ const executor = new CodexExecutor({ executionPermission, cmd: { baseCommandOverride: executable } });
1415
+ const stdin = vi.spyOn(executor, 'spawnWithStdin').mockResolvedValue({});
1416
+ const duplex = vi.spyOn(executor, 'spawnWithDuplex').mockResolvedValue({});
1417
+ try {
1418
+ const config = {
1419
+ workingDir, prompt: 'task',
1420
+ env: ExecutionEnv.default(workingDir).merge({
1421
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
1422
+ AGENT_TOWER_AGENT_CREDENTIAL: 'project-bound-credential',
1423
+ AGENT_TOWER_SESSION_ID: 'project-launch-session',
1424
+ }),
1425
+ };
1426
+ await executor.spawn(config);
1427
+ await executor.spawnFollowUp(config, 'native-thread');
1428
+ const spawn = executionPermission === 'FULL_ACCESS' ? stdin : duplex;
1429
+ expect(spawn).toHaveBeenCalledTimes(2);
1430
+ for (const [, parts] of spawn.mock.calls) {
1431
+ expect(parts.args).toContain('mcp_servers.agent-tower.enabled=false');
1432
+ expect(parts.args).toContain('mcp_servers.agent-tower-dev.enabled=false');
1433
+ expect(parts.args.some(arg => arg.includes('mcp_servers.project-tool'))).toBe(false);
1434
+ expect(managedMcpServer(parts.args).env.AGENT_TOWER_SESSION_ID).toBe('project-launch-session');
1435
+ }
1436
+ await waitForCodexMcpServerCacheRefresh();
1437
+ expect(fs.readFileSync(path.join(binDir, 'probe-cwds'), 'utf8').trim()).toBe(fs.realpathSync(workingDir));
1438
+ expect(fs.readFileSync(configPath, 'utf8')).toBe(projectConfig);
1439
+ expect(fs.readdirSync(home)).toEqual([]);
1440
+ }
1441
+ finally {
1442
+ await waitForCodexMcpServerCacheRefresh();
1443
+ stdin.mockRestore();
1444
+ duplex.mockRestore();
1445
+ refresh.mockRestore();
1446
+ support.mockRestore();
1447
+ }
1448
+ });
1449
+ it.each(['FULL_ACCESS', 'ASK', 'AUTO_SANDBOX'])('isolates managed MCP from stale user settings on new and resumed %s launches without writing user config', async (executionPermission) => {
1450
+ const home = path.join(tmpDir, `managed-codex-home-${executionPermission}`);
1451
+ fs.mkdirSync(home, { recursive: true });
1452
+ const userConfig = '[mcp_servers.agent-tower]\nurl="https://stale.example/mcp"\nenabled=false\ndisabled_tools=["get_task_handoff"]\n[mcp_servers.other-server]\ncommand="other-mcp"';
1453
+ fs.writeFileSync(path.join(home, 'config.toml'), userConfig);
1454
+ vi.stubEnv('CODEX_HOME', home);
1455
+ const refresh = vi.spyOn(codexAskCapabilities, 'refreshCodexCliSupport').mockResolvedValue({ supported: true, executable: '/verified/codex' });
1456
+ const support = vi.spyOn(codexAskCapabilities, 'cachedCodexCliSupport').mockReturnValue({ supported: true, executable: '/verified/codex' });
1457
+ const executor = new CodexExecutor({
1458
+ executionPermission,
1459
+ settings: userConfig,
1460
+ cmd: { baseCommandOverride: '/verified/codex', env: { AGENT_TOWER_DESKTOP_NODE: '/profile/node' } },
1461
+ });
1462
+ const stdin = vi.spyOn(executor, 'spawnWithStdin').mockResolvedValue({});
1463
+ const duplex = vi.spyOn(executor, 'spawnWithDuplex').mockResolvedValue({});
1464
+ try {
1465
+ const config = {
1466
+ workingDir: tmpDir, prompt: 'task',
1467
+ env: ExecutionEnv.default(tmpDir).merge({
1468
+ AGENT_TOWER_URL: 'http://127.0.0.1:42001',
1469
+ AGENT_TOWER_AGENT_CREDENTIAL: 'bound-session-credential',
1470
+ AGENT_TOWER_SESSION_ID: 'current-session',
1471
+ }),
1472
+ };
1473
+ await executor.spawn(config);
1474
+ await executor.spawnFollowUp(config, 'native-thread');
1475
+ const spawn = executionPermission === 'FULL_ACCESS' ? stdin : duplex;
1476
+ expect(spawn).toHaveBeenCalledTimes(2);
1477
+ const serverNames = new Set(spawn.mock.calls.map(([, parts]) => parts.args.find(arg => /^mcp_servers\.agent-tower-[a-f0-9]{8}(?:-\d+)?=/.test(arg))?.split('=')[0]));
1478
+ expect(serverNames.size).toBe(1);
1479
+ for (const [, parts] of spawn.mock.calls) {
1480
+ const server = managedMcpServer(parts.args);
1481
+ expect(server.command).toBe('/profile/node');
1482
+ expect(server.enabled).toBe(true);
1483
+ expect(server.env.AGENT_TOWER_AGENT_CREDENTIAL).toBe('bound-session-credential');
1484
+ expect(server.env.AGENT_TOWER_SESSION_ID).toBe('current-session');
1485
+ expect(server).not.toHaveProperty('url');
1486
+ expect(server).not.toHaveProperty('disabled_tools');
1487
+ expect(parts.args).toContain('mcp_servers.other-server.command="other-mcp"');
1488
+ expect(parts.args.findIndex(arg => /^mcp_servers\.agent-tower-[a-f0-9]{8}(?:-\d+)?=/.test(arg)))
1489
+ .toBeGreaterThan(parts.args.indexOf('mcp_servers.agent-tower.enabled=false'));
1490
+ }
1491
+ expect(fs.readdirSync(home)).toEqual(['config.toml']);
1492
+ expect(fs.readFileSync(path.join(home, 'config.toml'), 'utf8')).toBe(userConfig);
1493
+ }
1494
+ finally {
1495
+ await waitForCodexMcpServerCacheRefresh();
1496
+ stdin.mockRestore();
1497
+ duplex.mockRestore();
1498
+ refresh.mockRestore();
1499
+ support.mockRestore();
1500
+ }
1501
+ });
1502
+ });
1110
1503
  //# sourceMappingURL=codex.executor.test.js.map