commandmate 0.22.2 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (476) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +108 -108
  3. package/.next/app-path-routes-manifest.json +21 -21
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/prerender-manifest.json +12 -12
  6. package/.next/required-server-files.json +1 -1
  7. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  8. package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
  9. package/.next/server/app/api/app/update-check/route.js +1 -1
  10. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/assistant/conversation/route.js +1 -1
  12. package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
  13. package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/assistant/current-output/route.js +1 -1
  15. package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
  16. package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/assistant/messages/route.js +1 -1
  18. package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
  19. package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/assistant/session/route.js +1 -1
  21. package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
  22. package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/assistant/start/route.js +1 -1
  24. package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
  25. package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
  26. package/.next/server/app/api/assistant/terminal/route.js +3 -3
  27. package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
  28. package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/assistant/tools/route.js +1 -1
  30. package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
  31. package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/daily-summary/route.js +1 -1
  36. package/.next/server/app/api/daily-summary/route.js.nft.json +1 -1
  37. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/daily-summary/status/route.js +1 -1
  39. package/.next/server/app/api/daily-summary/status/route.js.nft.json +1 -1
  40. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/external-apps/[id]/health/route.js +1 -1
  42. package/.next/server/app/api/external-apps/[id]/health/route.js.nft.json +1 -1
  43. package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/external-apps/[id]/route.js +1 -1
  45. package/.next/server/app/api/external-apps/[id]/route.js.nft.json +1 -1
  46. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/external-apps/route.js +1 -1
  48. package/.next/server/app/api/external-apps/route.js.nft.json +1 -1
  49. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/fs/browse/route.js +1 -1
  51. package/.next/server/app/api/fs/browse/route.js.nft.json +1 -1
  52. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/fs/recent-paths/route.js +1 -1
  54. package/.next/server/app/api/fs/recent-paths/route.js.nft.json +1 -1
  55. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/hooks/agent-event/route.js +1 -1
  57. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  58. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/hooks/claude-done/route.js +1 -1
  60. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  61. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/hooks/permission-request/route.js +1 -1
  63. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  64. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/metrics/vibe/route.js +1 -1
  66. package/.next/server/app/api/metrics/vibe/route.js.nft.json +1 -1
  67. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/push/subscriptions/route.js +1 -1
  70. package/.next/server/app/api/push/subscriptions/route.js.nft.json +1 -1
  71. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/push/vapid/route.js +1 -1
  73. package/.next/server/app/api/push/vapid/route.js.nft.json +1 -1
  74. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/repositories/[id]/route.js +1 -1
  76. package/.next/server/app/api/repositories/[id]/route.js.nft.json +1 -1
  77. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route.js +1 -1
  79. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route.js.nft.json +1 -1
  80. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/repositories/[id]/todos/route.js +1 -1
  82. package/.next/server/app/api/repositories/[id]/todos/route.js.nft.json +1 -1
  83. package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  85. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  86. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/repositories/clone/route.js +1 -1
  88. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  89. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/repositories/excluded/route.js +1 -1
  91. package/.next/server/app/api/repositories/excluded/route.js.nft.json +1 -1
  92. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/repositories/restore/route.js +1 -1
  94. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  95. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/repositories/route.js +1 -1
  97. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  98. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/repositories/scan/route.js +1 -1
  100. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  101. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/repositories/sync/route.js +1 -1
  103. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  104. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/repositories/validate-path/route.js +1 -1
  106. package/.next/server/app/api/repositories/validate-path/route.js.nft.json +1 -1
  107. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/sidebar/group-order/route.js +1 -1
  109. package/.next/server/app/api/sidebar/group-order/route.js.nft.json +1 -1
  110. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/skills/installations/route.js +1 -1
  113. package/.next/server/app/api/skills/installations/route.js.nft.json +1 -1
  114. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/skills/operations/route.js +1 -1
  116. package/.next/server/app/api/skills/operations/route.js.nft.json +1 -1
  117. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/skills/reindex/route.js +1 -1
  119. package/.next/server/app/api/skills/reindex/route.js.nft.json +1 -1
  120. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/tasks/[taskId]/cancel/route.js +1 -4
  124. package/.next/server/app/api/tasks/[taskId]/cancel/route.js.nft.json +1 -1
  125. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/tasks/[taskId]/route.js +1 -4
  127. package/.next/server/app/api/tasks/[taskId]/route.js.nft.json +1 -1
  128. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/templates/[id]/route.js +1 -1
  130. package/.next/server/app/api/templates/[id]/route.js.nft.json +1 -1
  131. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/templates/route.js +1 -1
  133. package/.next/server/app/api/templates/route.js.nft.json +1 -1
  134. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/todos/route.js +1 -1
  136. package/.next/server/app/api/todos/route.js.nft.json +1 -1
  137. package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/verification/runs/[runId]/route.js +1 -1
  139. package/.next/server/app/api/verification/runs/[runId]/route.js.nft.json +1 -1
  140. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/verification/runs/route.js +1 -1
  142. package/.next/server/app/api/verification/runs/route.js.nft.json +1 -1
  143. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  146. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  149. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/cli-tool/route.js.nft.json +1 -1
  152. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  153. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +2 -2
  154. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js.nft.json +1 -1
  155. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  157. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  158. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  159. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js +1 -1
  160. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js.nft.json +1 -1
  161. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +2 -2
  163. package/.next/server/app/api/worktrees/[id]/execution-logs/route.js.nft.json +1 -1
  164. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  165. package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
  166. package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js.nft.json +1 -1
  167. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/git/branch/create/route.js +1 -1
  169. package/.next/server/app/api/worktrees/[id]/git/branch/create/route.js.nft.json +1 -1
  170. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route.js.nft.json +1 -1
  173. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/[id]/git/branches/route.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/git/branches/route.js.nft.json +1 -1
  176. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  177. package/.next/server/app/api/worktrees/[id]/git/checkout/route.js +1 -1
  178. package/.next/server/app/api/worktrees/[id]/git/checkout/route.js.nft.json +1 -1
  179. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  180. package/.next/server/app/api/worktrees/[id]/git/commit/route.js +1 -1
  181. package/.next/server/app/api/worktrees/[id]/git/commit/route.js.nft.json +1 -1
  182. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/git/diff/route.js +1 -1
  184. package/.next/server/app/api/worktrees/[id]/git/diff/route.js.nft.json +1 -1
  185. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  186. package/.next/server/app/api/worktrees/[id]/git/fetch/route.js +1 -1
  187. package/.next/server/app/api/worktrees/[id]/git/fetch/route.js.nft.json +1 -1
  188. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  189. package/.next/server/app/api/worktrees/[id]/git/log/route.js +1 -1
  190. package/.next/server/app/api/worktrees/[id]/git/log/route.js.nft.json +1 -1
  191. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  192. package/.next/server/app/api/worktrees/[id]/git/pull/route.js +1 -1
  193. package/.next/server/app/api/worktrees/[id]/git/pull/route.js.nft.json +1 -1
  194. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/git/push/route.js +1 -1
  196. package/.next/server/app/api/worktrees/[id]/git/push/route.js.nft.json +1 -1
  197. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  198. package/.next/server/app/api/worktrees/[id]/git/reset/route.js +1 -1
  199. package/.next/server/app/api/worktrees/[id]/git/reset/route.js.nft.json +1 -1
  200. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  201. package/.next/server/app/api/worktrees/[id]/git/revert/route.js +1 -1
  202. package/.next/server/app/api/worktrees/[id]/git/revert/route.js.nft.json +1 -1
  203. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  204. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route.js +1 -1
  205. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route.js.nft.json +1 -1
  206. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  207. package/.next/server/app/api/worktrees/[id]/git/stage/route.js +1 -1
  208. package/.next/server/app/api/worktrees/[id]/git/stage/route.js.nft.json +1 -1
  209. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  210. package/.next/server/app/api/worktrees/[id]/git/staged/route.js +1 -1
  211. package/.next/server/app/api/worktrees/[id]/git/staged/route.js.nft.json +1 -1
  212. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  213. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route.js +1 -1
  214. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route.js.nft.json +1 -1
  215. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  216. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route.js +1 -1
  217. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route.js.nft.json +1 -1
  218. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  219. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route.js +1 -1
  220. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route.js.nft.json +1 -1
  221. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  222. package/.next/server/app/api/worktrees/[id]/git/stash/push/route.js +1 -1
  223. package/.next/server/app/api/worktrees/[id]/git/stash/push/route.js.nft.json +1 -1
  224. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  225. package/.next/server/app/api/worktrees/[id]/git/stash/route.js +1 -1
  226. package/.next/server/app/api/worktrees/[id]/git/stash/route.js.nft.json +1 -1
  227. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  228. package/.next/server/app/api/worktrees/[id]/git/status/route.js +1 -1
  229. package/.next/server/app/api/worktrees/[id]/git/status/route.js.nft.json +1 -1
  230. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  231. package/.next/server/app/api/worktrees/[id]/git/unstage/route.js +1 -1
  232. package/.next/server/app/api/worktrees/[id]/git/unstage/route.js.nft.json +1 -1
  233. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  234. package/.next/server/app/api/worktrees/[id]/git/working-diff/route.js +1 -1
  235. package/.next/server/app/api/worktrees/[id]/git/working-diff/route.js.nft.json +1 -1
  236. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  237. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  238. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  239. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  240. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  241. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  242. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  243. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
  244. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js.nft.json +1 -1
  245. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  246. package/.next/server/app/api/worktrees/[id]/logs/route.js +1 -1
  247. package/.next/server/app/api/worktrees/[id]/logs/route.js.nft.json +1 -1
  248. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  249. package/.next/server/app/api/worktrees/[id]/marp-render/route.js +3 -3
  250. package/.next/server/app/api/worktrees/[id]/marp-render/route.js.nft.json +1 -1
  251. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  252. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
  253. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js.nft.json +1 -1
  254. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  255. package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
  256. package/.next/server/app/api/worktrees/[id]/memos/route.js.nft.json +1 -1
  257. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  258. package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
  259. package/.next/server/app/api/worktrees/[id]/messages/route.js.nft.json +1 -1
  260. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  261. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  262. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  263. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  264. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  265. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  266. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  267. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  268. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  269. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  270. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +2 -2
  271. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js.nft.json +1 -1
  272. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  273. package/.next/server/app/api/worktrees/[id]/schedules/active/route.js +1 -1
  274. package/.next/server/app/api/worktrees/[id]/schedules/active/route.js.nft.json +1 -1
  275. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  276. package/.next/server/app/api/worktrees/[id]/schedules/route.js +3 -3
  277. package/.next/server/app/api/worktrees/[id]/schedules/route.js.nft.json +1 -1
  278. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  279. package/.next/server/app/api/worktrees/[id]/search/route.js +1 -1
  280. package/.next/server/app/api/worktrees/[id]/search/route.js.nft.json +1 -1
  281. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  282. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  283. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  284. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  285. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +1 -1
  286. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  287. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  288. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route.js +1 -1
  289. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route.js.nft.json +1 -1
  290. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  291. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js +1 -1
  292. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js.nft.json +1 -1
  293. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  294. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route.js +1 -1
  295. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route.js.nft.json +1 -1
  296. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  297. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route.js +1 -1
  298. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route.js.nft.json +1 -1
  299. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  300. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route.js +1 -1
  301. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route.js.nft.json +1 -1
  302. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  303. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js +1 -1
  304. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js.nft.json +1 -1
  305. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  306. package/.next/server/app/api/worktrees/[id]/skills/route.js +1 -1
  307. package/.next/server/app/api/worktrees/[id]/skills/route.js.nft.json +1 -1
  308. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  309. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
  310. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
  311. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  312. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  313. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  314. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  315. package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
  316. package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
  317. package/.next/server/app/api/worktrees/[id]/start-polling/route_client-reference-manifest.js +1 -1
  318. package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
  319. package/.next/server/app/api/worktrees/[id]/tasks/route.js.nft.json +1 -1
  320. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  321. package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -1
  322. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  323. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  324. package/.next/server/app/api/worktrees/[id]/timers/history/route.js +1 -1
  325. package/.next/server/app/api/worktrees/[id]/timers/history/route.js.nft.json +1 -1
  326. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  327. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  328. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  329. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  330. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route.js +1 -1
  331. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route.js.nft.json +1 -1
  332. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  333. package/.next/server/app/api/worktrees/[id]/todos/route.js +1 -1
  334. package/.next/server/app/api/worktrees/[id]/todos/route.js.nft.json +1 -1
  335. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  336. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -1
  337. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js.nft.json +1 -1
  338. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  339. package/.next/server/app/api/worktrees/[id]/tree/route.js +1 -1
  340. package/.next/server/app/api/worktrees/[id]/tree/route.js.nft.json +1 -1
  341. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  342. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -1
  343. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js.nft.json +1 -1
  344. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  345. package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -4
  346. package/.next/server/app/api/worktrees/[id]/verify/route.js.nft.json +1 -1
  347. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  348. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route.js +1 -1
  349. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route.js.nft.json +1 -1
  350. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  351. package/.next/server/app/api/worktrees/[id]/verify/runs/route.js +1 -1
  352. package/.next/server/app/api/worktrees/[id]/verify/runs/route.js.nft.json +1 -1
  353. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  354. package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -1
  355. package/.next/server/app/api/worktrees/[id]/viewed/route.js.nft.json +1 -1
  356. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  357. package/.next/server/app/api/worktrees/route.js +1 -1
  358. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  359. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  360. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  361. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  362. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  363. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  364. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  365. package/.next/server/app/page_client-reference-manifest.js +1 -1
  366. package/.next/server/app/proxy/[...path]/route.js +1 -1
  367. package/.next/server/app/proxy/[...path]/route.js.nft.json +1 -1
  368. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  369. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  370. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  371. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  372. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  373. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  374. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  375. package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
  376. package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
  377. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  378. package/.next/server/app/worktrees/[id]/page.js +2 -2
  379. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  380. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  381. package/.next/server/app/worktrees/[id]/terminal/page.js +1 -1
  382. package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
  383. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  384. package/.next/server/app-paths-manifest.json +21 -21
  385. package/.next/server/chunks/1029.js +1 -1
  386. package/.next/server/chunks/1068.js +1 -1
  387. package/.next/server/chunks/2119.js +1 -1
  388. package/.next/server/chunks/2404.js +1 -1
  389. package/.next/server/chunks/2445.js +1 -1
  390. package/.next/server/chunks/2546.js +1 -1
  391. package/.next/server/chunks/3241.js +1 -0
  392. package/.next/server/chunks/3417.js +1 -1
  393. package/.next/server/chunks/406.js +10 -6
  394. package/.next/server/chunks/4444.js +1 -1
  395. package/.next/server/chunks/5959.js +4 -5
  396. package/.next/server/chunks/5998.js +1 -12
  397. package/.next/server/chunks/6130.js +1 -1
  398. package/.next/server/chunks/6274.js +3 -3
  399. package/.next/server/chunks/6401.js +1 -1
  400. package/.next/server/chunks/6781.js +1 -0
  401. package/.next/server/chunks/7458.js +6 -0
  402. package/.next/server/chunks/{3753.js → 7970.js} +57 -32
  403. package/.next/server/chunks/8536.js +1 -1
  404. package/.next/server/chunks/8807.js +180 -127
  405. package/.next/server/chunks/9058.js +1 -1
  406. package/.next/server/chunks/9067.js +1 -1
  407. package/.next/server/chunks/9360.js +1 -1
  408. package/.next/server/chunks/9687.js +2 -2
  409. package/.next/server/middleware-manifest.json +5 -5
  410. package/.next/server/pages/500.html +1 -1
  411. package/.next/server/server-reference-manifest.json +1 -1
  412. package/.next/server/src/middleware.js.map +1 -1
  413. package/.next/static/chunks/{6488-53a3e1d78e4fe8f7.js → 6488-95ba9d24a76f0cc5.js} +1 -1
  414. package/.next/static/chunks/app/worktrees/[id]/{page-6dc0b8c4e808ea3e.js → page-966a451ae7b0903a.js} +1 -1
  415. package/.next/trace +12 -12
  416. package/dist/server/src/config/log-config.js +10 -1
  417. package/dist/server/src/config/safe-directory.js +103 -0
  418. package/dist/server/src/config/system-directories.js +74 -9
  419. package/dist/server/src/lib/cli-tools/antigravity.js +19 -2
  420. package/dist/server/src/lib/cli-tools/codex.js +95 -1
  421. package/dist/server/src/lib/cli-tools/copilot.js +30 -1
  422. package/dist/server/src/lib/cli-tools/gemini.js +28 -2
  423. package/dist/server/src/lib/cli-tools/opencode.js +51 -2
  424. package/dist/server/src/lib/git/git-default-branch.js +59 -0
  425. package/dist/server/src/lib/hooks/agent-event-service.js +205 -0
  426. package/dist/server/src/lib/hooks/agent-event-types.js +7 -86
  427. package/dist/server/src/lib/hooks/hook-settings-generator.js +26 -11
  428. package/dist/server/src/lib/hooks/permission-decision-service.js +269 -0
  429. package/dist/server/src/lib/hooks/sources/abstain.js +78 -0
  430. package/dist/server/src/lib/hooks/sources/antigravity/hooks-config.js +372 -0
  431. package/dist/server/src/lib/hooks/sources/antigravity/source.js +225 -0
  432. package/dist/server/src/lib/hooks/sources/antigravity/tool-id.js +16 -0
  433. package/dist/server/src/lib/hooks/sources/claude/source.js +143 -0
  434. package/dist/server/src/lib/hooks/sources/claude/tool-id.js +18 -0
  435. package/dist/server/src/lib/hooks/sources/codex/hooks-config.js +465 -0
  436. package/dist/server/src/lib/hooks/sources/codex/source.js +138 -0
  437. package/dist/server/src/lib/hooks/sources/codex/tool-id.js +15 -0
  438. package/dist/server/src/lib/hooks/sources/copilot/hook-settings.js +473 -0
  439. package/dist/server/src/lib/hooks/sources/copilot/source.js +210 -0
  440. package/dist/server/src/lib/hooks/sources/copilot/tool-id.js +15 -0
  441. package/dist/server/src/lib/hooks/sources/define-source.js +169 -0
  442. package/dist/server/src/lib/hooks/sources/event-mapper.js +157 -0
  443. package/dist/server/src/lib/hooks/sources/gemini/event-vocabulary.js +102 -0
  444. package/dist/server/src/lib/hooks/sources/gemini/settings-generator.js +315 -0
  445. package/dist/server/src/lib/hooks/sources/gemini/shared-config-tree.js +103 -0
  446. package/dist/server/src/lib/hooks/sources/gemini/source.js +151 -0
  447. package/dist/server/src/lib/hooks/sources/gemini/tool-id.js +17 -0
  448. package/dist/server/src/lib/hooks/sources/hook-event-vocabulary.js +111 -0
  449. package/dist/server/src/lib/hooks/sources/index.js +62 -0
  450. package/dist/server/src/lib/hooks/sources/legacy-relay.js +84 -0
  451. package/dist/server/src/lib/hooks/sources/opencode/client.js +299 -0
  452. package/dist/server/src/lib/hooks/sources/opencode/ingest.js +261 -0
  453. package/dist/server/src/lib/hooks/sources/opencode/mappers.js +154 -0
  454. package/dist/server/src/lib/hooks/sources/opencode/payloads.js +189 -0
  455. package/dist/server/src/lib/hooks/sources/opencode/ports.js +295 -0
  456. package/dist/server/src/lib/hooks/sources/opencode/runtime.js +159 -0
  457. package/dist/server/src/lib/hooks/sources/opencode/source.js +250 -0
  458. package/dist/server/src/lib/hooks/sources/opencode/subscription.js +422 -0
  459. package/dist/server/src/lib/hooks/sources/opencode/tool-id.js +14 -0
  460. package/dist/server/src/lib/hooks/sources/opencode/turn-gate.js +193 -0
  461. package/dist/server/src/lib/hooks/sources/pending-decisions.js +127 -0
  462. package/dist/server/src/lib/hooks/sources/registry.js +111 -0
  463. package/dist/server/src/lib/hooks/sources/types.js +46 -0
  464. package/dist/server/src/lib/session/agent-session-lifecycle.js +82 -0
  465. package/dist/server/src/lib/session/claude-session.js +10 -3
  466. package/dist/server/src/lib/tasks/contract-message.js +175 -0
  467. package/dist/server/src/lib/verification/env-clean-gate.js +320 -0
  468. package/dist/server/src/lib/verification/env-snapshot.js +474 -0
  469. package/dist/server/src/lib/verification/gate-runner.js +802 -0
  470. package/dist/server/src/lib/verification/scope-gate.js +402 -0
  471. package/dist/server/src/lib/verification/verify-config.js +281 -0
  472. package/package.json +1 -1
  473. package/scripts/hooks/cmate-agent-event.sh +73 -3
  474. package/.next/server/chunks/8856.js +0 -4
  475. /package/.next/static/{XpKfn5x6H-mtTaXI9yNmb → wH6K6QNPHSdATFkVoIcy3}/_buildManifest.js +0 -0
  476. /package/.next/static/{XpKfn5x6H-mtTaXI9yNmb → wH6K6QNPHSdATFkVoIcy3}/_ssgManifest.js +0 -0
@@ -18,6 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.getLogDir = getLogDir;
19
19
  const path_1 = __importDefault(require("path"));
20
20
  const env_1 = require("../lib/env");
21
+ const safe_directory_1 = require("./safe-directory");
21
22
  /**
22
23
  * Get the log directory path.
23
24
  *
@@ -25,6 +26,13 @@ const env_1 = require("../lib/env");
25
26
  * 1. CM_LOG_DIR environment variable (with MCBD_LOG_DIR fallback via getEnvByKey)
26
27
  * 2. Default: `${process.cwd()}/data/logs`
27
28
  *
29
+ * Issue #1774: a CM_LOG_DIR inside `/proc`, `/sys` or `/dev` falls back to the
30
+ * default with a warning. `log-manager.ensureLogDirectory()` calls
31
+ * `fs.mkdir(dir, {recursive:true})` on the result, and on Linux that promise
32
+ * never settles for such a path — it holds a libuv threadpool thread for the
33
+ * life of the process instead. Throwing is not an option here: the log
34
+ * directory is where the complaint would have to go.
35
+ *
28
36
  * @returns Absolute path to the log directory
29
37
  *
30
38
  * @example
@@ -37,5 +45,6 @@ const env_1 = require("../lib/env");
37
45
  * ```
38
46
  */
39
47
  function getLogDir() {
40
- return (0, env_1.getEnvByKey)('CM_LOG_DIR') || path_1.default.join(process.cwd(), 'data', 'logs');
48
+ const fallback = path_1.default.join(process.cwd(), 'data', 'logs');
49
+ return (0, safe_directory_1.resolveSafeDirectory)((0, env_1.getEnvByKey)('CM_LOG_DIR'), fallback, 'CM_LOG_DIR');
41
50
  }
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * The one place a configured directory becomes a directory this process is
4
+ * willing to create (Issue #1774).
5
+ *
6
+ * ## What this is defending against
7
+ *
8
+ * Pointing a configured path at `/proc`, `/sys` or `/dev` does not make
9
+ * CommandMate fail — it makes it **stop**. procfs answers a `mkdir` for a child
10
+ * that cannot exist with ENOENT rather than EPERM, and Node's recursive mkdir
11
+ * reads ENOENT as "the parent is missing", creates the parent, and retries; the
12
+ * parent is created with EEXIST, so one level is enough to loop. Measured in a
13
+ * container: `mkdirSync('/proc/x/y', {recursive:true})` had not returned after
14
+ * 25s at 100.4% CPU, and `fs.promises.mkdir` on the same path never settled and
15
+ * held a libuv threadpool thread (there are four) forever. The synchronous form
16
+ * stops the event loop outright, which is also why the `try/catch` these call
17
+ * sites already have does nothing: control never reaches it. There is no error
18
+ * log, no OOM and no timeout — the process simply goes quiet. PR #1773's CI job
19
+ * ran 5h31m that way.
20
+ *
21
+ * On macOS none of the three paths exist, so the same call throws instantly and
22
+ * everything looks fine. The failure is Linux- and container-only.
23
+ *
24
+ * ## Why a resolver rather than a check at each mkdir
25
+ *
26
+ * Five settings reach a recursive mkdir this way (`CM_AGENT_HOOKS_DIR`,
27
+ * `CM_LOG_DIR`, `CODEX_HOME`, `COPILOT_HOME`, `CM_OPENCODE_PORT_FILE`), and
28
+ * three of the five arrived with Phase 4 (#1760 / #1761 / #1763) as copies of
29
+ * the first. Guarding the mkdir sites individually invites a sixth copy; putting
30
+ * the guard on the *resolution* of the value means a new tool is protected by
31
+ * calling the resolver its siblings already call.
32
+ *
33
+ * ## Why it never throws
34
+ *
35
+ * A bad log directory must not take out logging, and hook injection is
36
+ * fail-open by design (`hook-settings-generator.buildClaudeLaunchCommand`).
37
+ * Falling back to the built-in default and saying so keeps a misconfigured
38
+ * server running with its events intact.
39
+ *
40
+ * @module config/safe-directory
41
+ */
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.resetSafeDirectoryWarnings = resetSafeDirectoryWarnings;
44
+ exports.resolveSafeDirectory = resolveSafeDirectory;
45
+ const logger_1 = require("../lib/logger");
46
+ const system_directories_1 = require("./system-directories");
47
+ const logger = (0, logger_1.createLogger)('safe-directory');
48
+ /**
49
+ * Settings already warned about, so a rejection is reported once rather than on
50
+ * every call.
51
+ *
52
+ * `getLogDir()` runs on the path of every log write; an unconditional warning
53
+ * would turn one misconfigured variable into a log flood, in the log directory
54
+ * that is already not the one the operator asked for.
55
+ */
56
+ const warnedRejections = new Set();
57
+ /** Forget which rejections have been warned about. For tests. */
58
+ function resetSafeDirectoryWarnings() {
59
+ warnedRejections.clear();
60
+ }
61
+ /**
62
+ * The directory to actually use for `candidate`, which is `candidate` unless
63
+ * using it would hang the process.
64
+ *
65
+ * Pure with respect to the filesystem in the sense that matters here: it reads
66
+ * (symlink resolution) but creates nothing, so it is safe to call — and to test
67
+ * — with a path that no `mkdir` could survive.
68
+ *
69
+ * A *file* path is a valid candidate. The check is prefix-based, so a file
70
+ * inside a virtual filesystem is rejected exactly as its directory would be;
71
+ * `CM_OPENCODE_PORT_FILE` relies on that.
72
+ *
73
+ * @param candidate - The configured path. Empty or undefined means "unset"
74
+ * @param fallback - The built-in default to use when the candidate is refused
75
+ * @param source - What supplied the candidate, e.g. `'CM_LOG_DIR'`. Appears in
76
+ * the warning, because the operator's next question is which setting to fix
77
+ * @returns `candidate`, or `fallback` when the candidate is inside `/proc`,
78
+ * `/sys` or `/dev`
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const fallback = join(homedir(), '.commandmate', 'hooks');
83
+ * return resolveSafeDirectory(process.env.CM_AGENT_HOOKS_DIR, fallback, 'CM_AGENT_HOOKS_DIR');
84
+ * ```
85
+ */
86
+ function resolveSafeDirectory(candidate, fallback, source) {
87
+ if (!candidate)
88
+ return fallback;
89
+ if (!(0, system_directories_1.isVirtualFilesystemPath)(candidate))
90
+ return candidate;
91
+ const key = `${source}|${candidate}`;
92
+ if (!warnedRejections.has(key)) {
93
+ warnedRejections.add(key);
94
+ logger.warn('virtual-filesystem-path-rejected', {
95
+ source,
96
+ candidate,
97
+ fallback,
98
+ reason: 'A recursive mkdir under /proc, /sys or /dev never returns on Linux; ' +
99
+ 'the built-in default is used instead (Issue #1774).',
100
+ });
101
+ }
102
+ return fallback;
103
+ }
@@ -13,9 +13,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
14
  };
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.SYSTEM_DIRECTORIES = void 0;
16
+ exports.VIRTUAL_FILESYSTEM_ROOTS = exports.SYSTEM_DIRECTORIES = void 0;
17
17
  exports.isPathWithin = isPathWithin;
18
18
  exports.isSystemDirectory = isSystemDirectory;
19
+ exports.isVirtualFilesystemPath = isVirtualFilesystemPath;
19
20
  const fs_1 = __importDefault(require("fs"));
20
21
  const path_1 = __importDefault(require("path"));
21
22
  /**
@@ -36,6 +37,35 @@ exports.SYSTEM_DIRECTORIES = [
36
37
  '/sys',
37
38
  '/proc',
38
39
  ];
40
+ /**
41
+ * The subset of {@link SYSTEM_DIRECTORIES} that is not a filesystem at all.
42
+ *
43
+ * Issue #1774: these three are kernel-backed namespaces, and their `mkdir`
44
+ * error codes are not the ones `fs` callers assume. procfs answers a `mkdir`
45
+ * for a child that cannot exist with **ENOENT rather than EPERM**, and Node's
46
+ * recursive mkdir reads ENOENT as "the parent is missing", so it creates the
47
+ * parent and retries — forever. Measured in a container: `mkdirSync('/proc/x/y',
48
+ * {recursive:true})` had not returned after 25s at 100.4% CPU; the promise from
49
+ * `fs.promises.mkdir` never settled and held a libuv threadpool thread (4 by
50
+ * default) for the life of the process. Neither logs anything, and the
51
+ * synchronous form stops the event loop, so a surrounding `try/catch` is never
52
+ * reached. On macOS the paths do not exist and the call throws at once, which
53
+ * is why this is invisible outside Linux and containers.
54
+ *
55
+ * Kept as a named subset rather than reusing the whole list because the two
56
+ * lists answer different questions. {@link isSystemDirectory} answers "may a
57
+ * database live here", and rejects `/tmp` and `/var` for that; but `/tmp` and
58
+ * `/var/log` are ordinary writable directories that a log or hook directory may
59
+ * legitimately be pointed at — `os.tmpdir()` is *inside* one on both platforms
60
+ * (`/tmp` on Linux, `/var/folders/…` on macOS), which is where every isolated
61
+ * test directory in this repository lives. Rejecting those would not prevent a
62
+ * hang; it would redirect isolated test runs and container deployments back
63
+ * onto the real `~/.codex` and `~/.commandmate`.
64
+ *
65
+ * `SYSTEM_DIRECTORIES` remains the superset — `tests/unit/config/system-directories.test.ts`
66
+ * pins that, so the two cannot drift apart.
67
+ */
68
+ exports.VIRTUAL_FILESYSTEM_ROOTS = ['/proc', '/sys', '/dev'];
39
69
  /**
40
70
  * POSIX separator. SYSTEM_DIRECTORIES are POSIX absolute paths and this guard
41
71
  * protects POSIX platforms, so the boundary is always '/' rather than path.sep.
@@ -66,13 +96,14 @@ function isPathWithin(target, dir) {
66
96
  *
67
97
  * Mount layout does not change while the process runs, so this is computed once.
68
98
  */
69
- let resolvedSystemDirectoriesCache = null;
70
- function getSystemDirectoryCandidates() {
71
- if (resolvedSystemDirectoriesCache !== null) {
72
- return resolvedSystemDirectoriesCache;
99
+ const resolvedDirectoryCandidatesCache = new Map();
100
+ function getDirectoryCandidates(directories) {
101
+ const cached = resolvedDirectoryCandidatesCache.get(directories);
102
+ if (cached !== undefined) {
103
+ return cached;
73
104
  }
74
105
  const candidates = new Set();
75
- for (const dir of exports.SYSTEM_DIRECTORIES) {
106
+ for (const dir of directories) {
76
107
  // Always keep the literal form: it must stay enforced even when the
77
108
  // directory does not exist on this platform (e.g. /proc and /sys on macOS).
78
109
  candidates.add(dir);
@@ -83,8 +114,9 @@ function getSystemDirectoryCandidates() {
83
114
  // Not present on this platform; the literal form still applies.
84
115
  }
85
116
  }
86
- resolvedSystemDirectoriesCache = Array.from(candidates);
87
- return resolvedSystemDirectoriesCache;
117
+ const resolved = Array.from(candidates);
118
+ resolvedDirectoryCandidatesCache.set(directories, resolved);
119
+ return resolved;
88
120
  }
89
121
  /**
90
122
  * Resolve symlinks in an absolute path, tolerating components that do not exist.
@@ -140,7 +172,40 @@ function resolvePhysicalPath(absolutePath) {
140
172
  * @returns true if the path is within a system directory
141
173
  */
142
174
  function isSystemDirectory(inputPath) {
175
+ return matchesAnyDirectory(inputPath, exports.SYSTEM_DIRECTORIES);
176
+ }
177
+ /**
178
+ * Check if a path is inside a virtual filesystem ({@link VIRTUAL_FILESYSTEM_ROOTS}).
179
+ *
180
+ * Issue #1774: the question `isSystemDirectory` cannot answer for a directory
181
+ * that is about to be created. A recursive `mkdir` under one of these roots does
182
+ * not fail — it hangs the process, unrecoverably and without a log line — so
183
+ * every path that reaches a `mkdir(…, {recursive:true})` has to be filtered
184
+ * through this first. See {@link VIRTUAL_FILESYSTEM_ROOTS} for the measurements
185
+ * and for why this is a subset of `SYSTEM_DIRECTORIES` rather than all of it.
186
+ *
187
+ * Matching is the same as `isSystemDirectory`: lexical *and* physical form, so a
188
+ * symlink that points into `/proc` is rejected along with a literal `/proc/…`.
189
+ * That is the direction that matters — a missed one hangs the server.
190
+ *
191
+ * Reads the filesystem (symlink resolution); it never creates anything, and
192
+ * `realpath` on a procfs path returns ENOENT immediately rather than spinning.
193
+ *
194
+ * @param inputPath - The path to check (relative paths are resolved against cwd)
195
+ * @returns true if a recursive mkdir for this path could spin
196
+ */
197
+ function isVirtualFilesystemPath(inputPath) {
198
+ return matchesAnyDirectory(inputPath, exports.VIRTUAL_FILESYSTEM_ROOTS);
199
+ }
200
+ /**
201
+ * Shared matcher for {@link isSystemDirectory} and {@link isVirtualFilesystemPath}.
202
+ *
203
+ * @param inputPath - The path to check
204
+ * @param directories - Absolute directory prefixes to test against
205
+ * @returns true if either the lexical or the physical form lands in one of them
206
+ */
207
+ function matchesAnyDirectory(inputPath, directories) {
143
208
  const lexicalPath = path_1.default.resolve(inputPath);
144
209
  const physicalPath = resolvePhysicalPath(lexicalPath);
145
- return getSystemDirectoryCandidates().some((dir) => isPathWithin(lexicalPath, dir) || isPathWithin(physicalPath, dir));
210
+ return getDirectoryCandidates(directories).some((dir) => isPathWithin(lexicalPath, dir) || isPathWithin(physicalPath, dir));
146
211
  }
@@ -16,6 +16,8 @@ const tmux_1 = require("../tmux/tmux");
16
16
  const submit_verified_sender_1 = require("./submit-verified-sender");
17
17
  const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
18
18
  const cli_patterns_1 = require("../detection/cli-patterns");
19
+ const sources_1 = require("../../lib/hooks/sources");
20
+ const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
19
21
  const logger_1 = require("../../lib/logger");
20
22
  const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
21
23
  const logger = (0, logger_1.createLogger)('cli-tools/antigravity');
@@ -116,6 +118,11 @@ class AntigravityTool extends base_1.BaseCLITool {
116
118
  logger.info('antigravity-session-exists');
117
119
  return;
118
120
  }
121
+ // Issue #1762: fence this instance's structured events off from the process
122
+ // that used to hold the same (worktree, tool, instance) key. Creation path
123
+ // only — the reuse branch above has already returned — and before the pane
124
+ // exists. Bumped even if the launch below then fails.
125
+ (0, agent_session_lifecycle_1.beginAgentSession)({ worktreeId, cliToolId: sources_1.ANTIGRAVITY_CLI_TOOL_ID, instanceId });
119
126
  try {
120
127
  // Create tmux session with large history buffer for agy output
121
128
  // (agy is inline-rendered and retains scrollback, like Codex)
@@ -127,8 +134,18 @@ class AntigravityTool extends base_1.BaseCLITool {
127
134
  });
128
135
  // Wait a moment for the session to be created
129
136
  await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
130
- // Start agy in interactive mode, optionally pinned to a model
131
- const launchCommand = model ? `agy --model ${shellSingleQuote(model)}` : 'agy';
137
+ // Start agy in interactive mode, optionally pinned to a model.
138
+ //
139
+ // Issue #1762: `prepareAgentLaunch` merges CommandMate's named hook into
140
+ // `~/.gemini/config/hooks.json` — agy's single global config, shared with
141
+ // gemini's tree and with whatever the user has in it — and prefixes
142
+ // `CM_HOOK_URL`. That variable is the *only* correlation channel agy has:
143
+ // its payloads carry no `cwd`, its hooks run in `~/.gemini/config`, and
144
+ // one file serves every worktree on the machine. `--model` is appended
145
+ // after, so the env prefix stays in front of the command.
146
+ // `CM_AGENT_HOOKS_INJECT=0` returns bare `agy`, unchanged from before.
147
+ const base = (0, agent_session_lifecycle_1.prepareAgentLaunch)({ worktreeId, cliToolId: sources_1.ANTIGRAVITY_CLI_TOOL_ID, instanceId }, this.command).command;
148
+ const launchCommand = model ? `${base} --model ${shellSingleQuote(model)}` : base;
132
149
  await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
133
150
  // Wait for agy to initialize
134
151
  await new Promise((resolve) => setTimeout(resolve, ANTIGRAVITY_INIT_WAIT_MS));
@@ -5,12 +5,15 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.CodexTool = void 0;
8
+ exports.isCodexHooksReviewDialog = isCodexHooksReviewDialog;
8
9
  const base_1 = require("./base");
9
10
  const tmux_1 = require("../tmux/tmux");
10
11
  const submit_verified_sender_1 = require("./submit-verified-sender");
11
12
  const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
12
13
  const cli_patterns_1 = require("../detection/cli-patterns");
13
14
  const logger_1 = require("../../lib/logger");
15
+ const sources_1 = require("../../lib/hooks/sources");
16
+ const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
14
17
  const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
15
18
  const logger = (0, logger_1.createLogger)('cli-tools/codex');
16
19
  /**
@@ -28,6 +31,63 @@ const CODEX_POLL_INTERVAL_MS = 1000;
28
31
  const CODEX_INIT_MAX_ATTEMPTS = 30;
29
32
  /** Timeout for waiting for prompt before sending a message */
30
33
  const CODEX_PROMPT_WAIT_TIMEOUT_MS = 15000;
34
+ /**
35
+ * Anchors of codex's "Hooks need review" dialog (Issue #1760, measured on
36
+ * codex-cli 0.147.0):
37
+ *
38
+ * ```
39
+ * Hooks need review
40
+ * 5 hooks are new or changed.
41
+ * Hooks can run outside the sandbox after you trust them.
42
+ *
43
+ * > 1. Review hooks
44
+ * 2. Trust all and continue
45
+ * 3. Continue without trusting (hooks won't run)
46
+ * ```
47
+ *
48
+ * It has to be handled here rather than in `getCodexActiveDialog`, which
49
+ * classifies it as `null`: its wording matches none of that function's three
50
+ * anchors (`Skip until next version` / `Do you trust` / `Press enter to
51
+ * continue`). Measured consequence of leaving it alone, on a pane with the
52
+ * generated `hooks.json` present: `isCodexPromptReady` stays false for the full
53
+ * 30-attempt window and the session is then handed to `sendMessage` still
54
+ * sitting on the dialog. Every launch, because "continue without trusting" is
55
+ * not remembered — verified by relaunching and getting the same screen.
56
+ *
57
+ * Both strings are required so a "hooks" mention elsewhere cannot select an
58
+ * option on a live prompt.
59
+ */
60
+ const CODEX_HOOKS_REVIEW_ANCHORS = ['Hooks need review', 'Continue without trusting'];
61
+ /**
62
+ * Option 3, "Continue without trusting (hooks won't run)".
63
+ *
64
+ * Not option 2. Trusting writes `[hooks.state."<file>:<event>:0:0"]` entries
65
+ * into the operator's own `~/.codex/config.toml` — the file that carries, among
66
+ * other things, the `notify` command a Computer Use integration runs — and
67
+ * granting that on someone's behalf is not this server's call. So a session
68
+ * starts with the hooks inert and the screen scraper doing exactly what it did
69
+ * before Issue #1760; the human enables them once through codex's own review
70
+ * screen, or an operator sets `CM_CODEX_HOOK_TRUST=bypass`.
71
+ *
72
+ * Sent alone, like the other numbered dialogs (Issue #890): codex confirms a
73
+ * numbered selection instantly, and a trailing Enter would land on the next
74
+ * screen. Verified live — the prompt was ready on the following poll.
75
+ */
76
+ const CODEX_HOOKS_REVIEW_DECLINE_KEY = '3';
77
+ /**
78
+ * Whether the pane is sitting on the hooks review dialog.
79
+ *
80
+ * Position-independent on purpose, unlike `getCodexActiveDialog`: the only
81
+ * caller checks `isCodexPromptReady` first and returns when a genuine prompt
82
+ * exists, so residual dialog text above a live prompt is never reached, and a
83
+ * one-shot guard stops the key being sent twice.
84
+ *
85
+ * @param output - ANSI-stripped pane capture
86
+ * @returns True when both anchors of the dialog are present
87
+ */
88
+ function isCodexHooksReviewDialog(output) {
89
+ return CODEX_HOOKS_REVIEW_ANCHORS.every((anchor) => output.includes(anchor));
90
+ }
31
91
  /**
32
92
  * Codex CLI tool implementation
33
93
  * Manages Codex sessions using tmux
@@ -66,6 +126,18 @@ class CodexTool extends base_1.BaseCLITool {
66
126
  logger.info('codex-session-sessionname');
67
127
  return;
68
128
  }
129
+ // Issue #1760: everything the previous codex process reported through this
130
+ // (worktreeId, instanceId) belongs to a session that no longer exists, and
131
+ // the key is reused verbatim by the one about to be created. Without this
132
+ // fence the old process's last `user_prompt_submit` reads as the new one's
133
+ // and a brand-new session publishes `running` before anyone has typed into
134
+ // it (#1723).
135
+ //
136
+ // Creation path only — the reuse branch above has already returned — and
137
+ // before `createSession`, so no live pane is ever matched against a stale
138
+ // generation. Bumped even when the launch then fails: falling back to the
139
+ // scraper is always safe, trusting a dead session's events is not.
140
+ (0, agent_session_lifecycle_1.beginAgentSession)({ worktreeId, cliToolId: sources_1.CODEX_CLI_TOOL_ID, instanceId });
69
141
  try {
70
142
  // Create tmux session. Codex is inline-rendered, so its transcript lives in
71
143
  // the pane scrollback — depth comes from the shared TMUX_HISTORY_LIMIT
@@ -76,8 +148,18 @@ class CodexTool extends base_1.BaseCLITool {
76
148
  });
77
149
  // Wait a moment for the session to be created
78
150
  await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
151
+ // Issue #1760: hand this session its correlation keys, writing codex's
152
+ // hooks config first if it is not already there. codex has no
153
+ // `--settings`, so the keys ride in environment assignments on the launch
154
+ // line and the hook commands read them back — which is what tells `codex`
155
+ // from `codex-2` in one worktree, since `cwd` is identical for both.
156
+ //
157
+ // Falls back to the bare command on any failure and when
158
+ // `CM_AGENT_HOOKS_INJECT=0`; a session that starts without hooks is the
159
+ // pre-#1760 status quo, and a session that fails to start is not.
160
+ const launchCommand = (0, agent_session_lifecycle_1.prepareAgentLaunch)({ worktreeId, cliToolId: sources_1.CODEX_CLI_TOOL_ID, instanceId }, this.command).command;
79
161
  // Start Codex CLI in interactive mode
80
- await (0, tmux_1.sendKeys)(sessionName, 'codex', true);
162
+ await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
81
163
  // Wait for Codex to initialize
82
164
  await new Promise((resolve) => setTimeout(resolve, CODEX_INIT_WAIT_MS));
83
165
  // Poll until Codex interactive prompt is ready
@@ -102,6 +184,7 @@ class CodexTool extends base_1.BaseCLITool {
102
184
  // update branch re-sends "2" every poll and the live prompt gets "222...".
103
185
  let updateDialogHandled = false;
104
186
  let trustDialogHandled = false;
187
+ let hooksReviewHandled = false;
105
188
  for (let i = 0; i < CODEX_INIT_MAX_ATTEMPTS; i++) {
106
189
  try {
107
190
  const rawOutput = await (0, tmux_1.capturePane)(sessionName, 50);
@@ -114,6 +197,17 @@ class CodexTool extends base_1.BaseCLITool {
114
197
  logger.info('codex-prompt-detected');
115
198
  return;
116
199
  }
200
+ // Issue #1760: "Hooks need review". Reached only when no genuine prompt
201
+ // exists (the check above returned), so this cannot fire on a live
202
+ // prompt, and the one-shot guard stops a second key on a re-render.
203
+ // Declines: granting trust would write the operator's config.toml.
204
+ if (!hooksReviewHandled && isCodexHooksReviewDialog(output)) {
205
+ await (0, tmux_1.sendKeys)(sessionName, CODEX_HOOKS_REVIEW_DECLINE_KEY, false);
206
+ hooksReviewHandled = true;
207
+ logger.info('codex-hooks-review-declined');
208
+ await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.CODEX_DIALOG_SETTLE_MS));
209
+ continue;
210
+ }
117
211
  // Issue #892: classify the bottom-most ACTIVE dialog by position. A dialog
118
212
  // whose text is only residual scrollback above a genuine prompt returns
119
213
  // null here, so no stray key is sent after it has been dismissed.
@@ -17,6 +17,9 @@ const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
17
17
  const cli_patterns_1 = require("../detection/cli-patterns");
18
18
  const copilot_constants_1 = require("../../config/copilot-constants");
19
19
  const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
20
+ const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
21
+ const hook_settings_1 = require("../../lib/hooks/sources/copilot/hook-settings");
22
+ const tool_id_1 = require("../../lib/hooks/sources/copilot/tool-id");
20
23
  const errors_1 = require("../../lib/errors");
21
24
  const logger_1 = require("../../lib/logger");
22
25
  const logger = (0, logger_1.createLogger)('cli-tools/copilot');
@@ -112,6 +115,18 @@ class CopilotTool extends base_1.BaseCLITool {
112
115
  logger.info('copilot-session-exists');
113
116
  return;
114
117
  }
118
+ // Issue #1761: fence this session off from the previous copilot process's
119
+ // events. The state is keyed by (worktree, tool, instance), a key the new
120
+ // session reuses verbatim, so without this the old process's last
121
+ // `user_prompt_submit` reads as the new one's and a session publishes
122
+ // `running` before anybody has typed into it (#1723).
123
+ //
124
+ // Creation path only — the reuse branch above has already returned — and
125
+ // before the pane exists, so no live pane is ever judged against a stale
126
+ // generation. Outside the try, so a launch that then fails is still fenced:
127
+ // falling back to the scraper is always safe, trusting a dead session's
128
+ // events is not.
129
+ (0, agent_session_lifecycle_1.beginAgentSession)({ worktreeId, cliToolId: tool_id_1.COPILOT_CLI_TOOL_ID, instanceId });
115
130
  try {
116
131
  // Create tmux session. Scrollback depth comes from the shared
117
132
  // TMUX_HISTORY_LIMIT default (Issue #1624) — do not re-hardcode it here.
@@ -121,8 +136,22 @@ class CopilotTool extends base_1.BaseCLITool {
121
136
  });
122
137
  // Wait a moment for the session to be created
123
138
  await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
139
+ // Issue #1761: hand this session its hook configuration, so structured
140
+ // lifecycle events and Auto-Yes adjudication exist without the operator
141
+ // having edited ~/.copilot/settings.json by hand.
142
+ //
143
+ // The returned command is `gh copilot` with two environment assignments
144
+ // in front of it. Copilot's config is one file for the whole machine, so
145
+ // unlike Claude's `--settings` it cannot carry the correlation keys —
146
+ // they ride in the environment and the hook reads them when it fires.
147
+ // See `lib/hooks/sources/copilot/hook-settings`.
148
+ //
149
+ // Fails open in every branch: with `CM_AGENT_HOOKS_INJECT=0`, or with a
150
+ // settings file that cannot be read or written, this is byte-for-byte the
151
+ // `gh copilot` this line has always sent.
152
+ const launchCommand = (0, agent_session_lifecycle_1.prepareAgentLaunch)({ worktreeId, cliToolId: tool_id_1.COPILOT_CLI_TOOL_ID, instanceId }, hook_settings_1.COPILOT_LAUNCH_COMMAND).command;
124
153
  // Start Copilot CLI in interactive mode
125
- await (0, tmux_1.sendKeys)(sessionName, 'gh copilot', true);
154
+ await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
126
155
  // Wait for Copilot to initialize
127
156
  await new Promise((resolve) => setTimeout(resolve, COPILOT_INIT_WAIT_MS));
128
157
  // Poll until Copilot interactive prompt is ready
@@ -15,6 +15,9 @@ const tmux_1 = require("../tmux/tmux");
15
15
  const submit_verified_sender_1 = require("./submit-verified-sender");
16
16
  const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
17
17
  const cli_patterns_1 = require("../detection/cli-patterns");
18
+ const sources_1 = require("../../lib/hooks/sources");
19
+ const source_1 = require("../../lib/hooks/sources/gemini/source");
20
+ const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
18
21
  const logger_1 = require("../../lib/logger");
19
22
  const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
20
23
  const logger = (0, logger_1.createLogger)('cli-tools/gemini');
@@ -73,6 +76,13 @@ class GeminiTool extends base_1.BaseCLITool {
73
76
  logger.info('gemini-session-sessionname');
74
77
  return;
75
78
  }
79
+ // Issue #1762: fence this instance's structured events off from the process
80
+ // that used to hold the same (worktree, tool, instance) key. On the creation
81
+ // path only — the reuse branch above has already returned — and before the
82
+ // pane exists, so no live pane is ever judged against a stale generation.
83
+ // Bumped even if the launch below then fails: falling back to the screen
84
+ // scraper is always safe, trusting a dead session's events is not.
85
+ (0, agent_session_lifecycle_1.beginAgentSession)({ worktreeId, cliToolId: sources_1.GEMINI_CLI_TOOL_ID, instanceId });
76
86
  try {
77
87
  // Create tmux session. Scrollback depth comes from the shared
78
88
  // TMUX_HISTORY_LIMIT default (Issue #1624) — do not re-hardcode it here.
@@ -82,8 +92,24 @@ class GeminiTool extends base_1.BaseCLITool {
82
92
  });
83
93
  // Wait a moment for the session to be created
84
94
  await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
85
- // Start Gemini CLI in interactive mode (no flags = interactive REPL)
86
- await (0, tmux_1.sendKeys)(sessionName, 'gemini', true);
95
+ // Issue #1762: hand this worktree its own hooks config, merged into any
96
+ // `.gemini/settings.json` that is already there. Written from here rather
97
+ // than from the source's `prepareLaunch` because the worktree path is the
98
+ // one input `AgentInstanceRef` does not carry, and gemini is the only tool
99
+ // whose config is scoped to a worktree. Fail-open: a config that cannot be
100
+ // written costs the events and nothing else.
101
+ (0, source_1.injectGeminiHookSettings)(worktreePath, {
102
+ worktreeId,
103
+ cliToolId: sources_1.GEMINI_CLI_TOOL_ID,
104
+ instanceId,
105
+ });
106
+ // Start Gemini CLI in interactive mode (no flags = interactive REPL).
107
+ // `prepareAgentLaunch` prefixes `CM_HOOK_URL`, which is what tells the
108
+ // receiver which instance an event came from — `.gemini/settings.json` is
109
+ // per worktree and cannot. `CM_AGENT_HOOKS_INJECT=0` returns the bare
110
+ // command, unchanged from before this Issue.
111
+ const launchCommand = (0, agent_session_lifecycle_1.prepareAgentLaunch)({ worktreeId, cliToolId: sources_1.GEMINI_CLI_TOOL_ID, instanceId }, this.command).command;
112
+ await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
87
113
  // Wait for Gemini to initialize (minimum wait for banner/auth)
88
114
  await new Promise((resolve) => setTimeout(resolve, GEMINI_INIT_WAIT_MS));
89
115
  // Poll until Gemini interactive prompt is ready
@@ -8,6 +8,22 @@
8
8
  * - sendMessage: sends text via tmux send-keys + Enter
9
9
  * - killSession: sends `/exit` command then falls back to tmux kill-session
10
10
  * - interrupt(): inherits BaseCLITool default (Escape key) [D2-008]
11
+ *
12
+ * ## Structured events (Issue #1763, Epic #1720 Phase 4-5)
13
+ *
14
+ * opencode is the one supported agent that does not push lifecycle events at
15
+ * CommandMate: it is *subscribed to*. #1758 §5.1.2 measured that the plain TUI
16
+ * serves the same HTTP API `opencode serve` does once it is given `--port`, so
17
+ * the whole of the launch change is that flag — no second process, no pid to
18
+ * track, no orphan to reap, and `killSession`'s `/exit`, the init wait and
19
+ * `reconcileExistingSession` are all untouched.
20
+ *
21
+ * What is added around it is three calls into
22
+ * `@/lib/hooks/sources/opencode/runtime`: reserve a port before the launch
23
+ * command is built, attach the event stream once the TUI is up, and release
24
+ * both when the pane is killed. Every one of them is fail-open — a session that
25
+ * starts without structured events is the pre-#1763 status quo, and the screen
26
+ * scraper keeps deciding for it exactly as before.
11
27
  */
12
28
  Object.defineProperty(exports, "__esModule", { value: true });
13
29
  exports.OpenCodeTool = exports.OPENCODE_INIT_WAIT_MS = exports.OPENCODE_PANE_HEIGHT = exports.OPENCODE_EXIT_COMMAND = void 0;
@@ -19,6 +35,8 @@ const opencode_config_1 = require("./opencode-config");
19
35
  const child_process_1 = require("child_process");
20
36
  const util_1 = require("util");
21
37
  const logger_1 = require("../../lib/logger");
38
+ const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
39
+ const runtime_1 = require("../../lib/hooks/sources/opencode/runtime");
22
40
  const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
23
41
  const logger = (0, logger_1.createLogger)('cli-tools/opencode');
24
42
  const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
@@ -77,15 +95,29 @@ class OpenCodeTool extends base_1.BaseCLITool {
77
95
  throw new Error('OpenCode is not installed or not in PATH');
78
96
  }
79
97
  const sessionName = this.getSessionName(worktreeId, instanceId);
98
+ const target = (0, runtime_1.opencodeTarget)(worktreeId, instanceId);
80
99
  const exists = await (0, tmux_1.hasSession)(sessionName);
81
100
  if (exists) {
82
101
  await this.reconcileExistingSession(sessionName, {
83
102
  windowWidth: 80,
84
103
  windowHeight: exports.OPENCODE_PANE_HEIGHT,
85
104
  });
105
+ // Issue #1763: the pane outlived this process (a CommandMate restart), so
106
+ // the subscription that used to watch it is gone while its server is
107
+ // still listening. Recovered from the recorded port, health-checked, and
108
+ // skipped silently when there is nothing there. NOT a new generation —
109
+ // the pane is the same one, and fencing here would discard a still-valid
110
+ // verdict on every reconnect.
111
+ await (0, runtime_1.resumeOpencodeEventStream)(target, worktreePath);
86
112
  logger.info('opencode-session-sessionname');
87
113
  return;
88
114
  }
115
+ // Issue #1759 (S8) / #1763: everything the previous opencode process
116
+ // reported through this (worktreeId, instanceId) belongs to a session that
117
+ // no longer exists, and the key is reused verbatim by the one about to be
118
+ // created. On the creation path only, before the pane exists, and even if
119
+ // the launch then fails — falling back to the scraper is always safe.
120
+ (0, agent_session_lifecycle_1.beginAgentSession)(target);
89
121
  try {
90
122
  // Generate opencode.json if not present (non-fatal on failure)
91
123
  await (0, opencode_config_1.ensureOpencodeConfig)(worktreePath);
@@ -109,10 +141,23 @@ class OpenCodeTool extends base_1.BaseCLITool {
109
141
  catch {
110
142
  // Non-fatal: resize may fail in some environments
111
143
  }
112
- // Start OpenCode TUI
113
- await (0, tmux_1.sendKeys)(sessionName, 'opencode', true);
144
+ // Issue #1763: pick the port before the command is built. The TUI is its
145
+ // own HTTP server once `--port` is passed (#1758 §5.1.2), and CommandMate
146
+ // assigns the number because opencode's `--port 0` is not an OS-assigned
147
+ // port — it tries 4096 first and only then falls back to an ephemeral one
148
+ // that nothing can read back (§5.9). Null means "no structured events",
149
+ // and the launch below is then the pre-#1763 bare command.
150
+ await (0, runtime_1.reserveOpencodeServerPort)(target, worktreePath);
151
+ // Start OpenCode TUI. `prepareAgentLaunch` asks the tool's own
152
+ // `AgentEventSource` for the command line (S3/S4/S5) and never throws.
153
+ const launchCommand = (0, agent_session_lifecycle_1.prepareAgentLaunch)(target, this.command).command;
154
+ await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
114
155
  // Wait for OpenCode to initialize (GPU model loading via Ollama)
115
156
  await new Promise((resolve) => setTimeout(resolve, exports.OPENCODE_INIT_WAIT_MS));
157
+ // Issue #1763: subscribe once the server has had the same time to come up
158
+ // that the TUI has. Health-checked first, so an opencode too old to know
159
+ // `--port` costs one probe and nothing else.
160
+ await (0, runtime_1.attachOpencodeEventStream)(target);
116
161
  logger.info('started-opencode-session:sessionname');
117
162
  }
118
163
  catch (error) {
@@ -164,6 +209,10 @@ class OpenCodeTool extends base_1.BaseCLITool {
164
209
  */
165
210
  async killSession(worktreeId, instanceId) {
166
211
  const sessionName = this.getSessionName(worktreeId, instanceId);
212
+ // Issue #1763: stop watching before the pane goes, so the stream is not
213
+ // reconnecting to a server that is being shut down. Also gives the port
214
+ // back — the pane is what held it. Never throws.
215
+ await (0, runtime_1.releaseOpencodeEventStream)((0, runtime_1.opencodeTarget)(worktreeId, instanceId));
167
216
  try {
168
217
  // Step 1: Check if the tmux session currently exists
169
218
  const exists = await (0, tmux_1.hasSession)(sessionName);