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
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ /**
3
+ * codex CLI's {@link AgentEventSource} — Phase 4-2 of Epic #1720 (Issue #1760),
4
+ * and the first tool added through the #1759 abstraction rather than by copying
5
+ * Claude's seams.
6
+ *
7
+ * Everything below is a value measured against codex-cli **0.147.0**, either by
8
+ * the #1757 spike or by this Issue's own live runs. The four that would fail
9
+ * silently if guessed:
10
+ *
11
+ * - **`noDecision: 'proceeds'`.** Driven live: a `PermissionRequest` hook that
12
+ * answered `{}` produced codex's ordinary "Would you like to run the
13
+ * following command?" dialog, and so did a receiver that was not running.
14
+ * Abstaining costs a dialog here, which is what makes Auto-Yes safe to leave
15
+ * undecided — a sentence that is false for antigravity and for opencode (C3).
16
+ * - **No `notification`.** codex's own review screen enumerates eleven events
17
+ * and `Notification` is not one of them (#1757 §5.1.1); a hook registered for
18
+ * it is dropped without a word. Anything waiting for one would wait forever,
19
+ * so {@link AgentSourceCapabilities.supportedEvents} says it cannot come.
20
+ * - **`session_start` is not a launch signal.** It fires when the first *turn*
21
+ * starts, ~68 s after the process on the spike's machine (#1757 §5.1.8).
22
+ * Nothing may treat it as "codex is ready".
23
+ * - **`session_end` is not an exit signal.** `/quit` emits one; a `kill-session`
24
+ * does not. Process death stays tmux's job, for every tool.
25
+ *
26
+ * The payload dialect is Claude's — same CamelCase `hook_event_name`, same
27
+ * `session_id`, same `tool_use_id`, same subtype fields — so this source reuses
28
+ * the shared vocabulary instead of restating it. That is a measurement too
29
+ * (#1757 §8.1), not an assumption: gemini renames four of the seven and
30
+ * antigravity ships no event name at all, and both of those bring their own
31
+ * mappers.
32
+ *
33
+ * @module lib/hooks/sources/codex/source
34
+ */
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.codexAgentEventSource = void 0;
37
+ exports.encodeCodexVerdict = encodeCodexVerdict;
38
+ const permission_request_payload_1 = require("../../../../lib/hooks/permission-request-payload");
39
+ const define_source_1 = require("../define-source");
40
+ const event_mapper_1 = require("../event-mapper");
41
+ const hook_event_vocabulary_1 = require("../hook-event-vocabulary");
42
+ const hooks_config_1 = require("./hooks-config");
43
+ const tool_id_1 = require("./tool-id");
44
+ /** No opinion. Measured to be indistinguishable from having no hook (#1757 §5.1.6). */
45
+ const NO_DECISION_BODY = {};
46
+ /**
47
+ * codex's decision wire format (S6 / #1757 §5.1.6).
48
+ *
49
+ * Three of the verdicts collapse to no-decision, for the reasons Claude's
50
+ * source gives at length: `allowAlways` has no measured spelling (codex's
51
+ * dialog has a "don't ask again" option, but no hook body was observed to
52
+ * produce it, and approximating it would write a persistent rule nobody asked
53
+ * for); `answer` belongs to a question picker codex does not have.
54
+ *
55
+ * `deny` is spelled out rather than collapsed, because unlike on Claude it was
56
+ * measured end to end: the command did not run, the TUI printed
57
+ * `• PermissionRequest hook (blocked)`, and the `message` was handed to the
58
+ * agent verbatim, which reported being refused. Nothing in CommandMate produces
59
+ * a deny today — `lib/hooks/permission-decision-service` explains why Auto-Yes
60
+ * must not — so this is the honest encoding of a verdict that is currently
61
+ * unreachable, not a new behaviour.
62
+ */
63
+ function encodeCodexVerdict(verdict) {
64
+ if (verdict.kind === 'allowOnce') {
65
+ return {
66
+ hookSpecificOutput: {
67
+ hookEventName: hooks_config_1.CODEX_PERMISSION_REQUEST_EVENT,
68
+ decision: { behavior: 'allow' },
69
+ },
70
+ };
71
+ }
72
+ if (verdict.kind === 'deny') {
73
+ return {
74
+ hookSpecificOutput: {
75
+ hookEventName: hooks_config_1.CODEX_PERMISSION_REQUEST_EVENT,
76
+ decision: { behavior: 'deny', message: verdict.message ?? '' },
77
+ },
78
+ };
79
+ }
80
+ return NO_DECISION_BODY;
81
+ }
82
+ /**
83
+ * codex CLI as an event source.
84
+ *
85
+ * Registered in `../registry`. Nothing outside this directory imports it by
86
+ * name; callers ask the registry for the tool they are holding.
87
+ */
88
+ exports.codexAgentEventSource = (0, define_source_1.definePushHookSource)({
89
+ cliToolId: tool_id_1.CODEX_CLI_TOOL_ID,
90
+ // Measured, not inherited from Claude. See the module comment.
91
+ noDecision: { kind: 'proceeds' },
92
+ capabilities: {
93
+ // What this server's config can actually produce — see CODEX_REGISTERED_EVENTS
94
+ // for why `notification`, `pre_tool_use` and `post_tool_use` are absent.
95
+ supportedEvents: hooks_config_1.CODEX_SUPPORTED_EVENTS,
96
+ // One file for the machine: codex has no `--settings` and no per-launch
97
+ // config flag, so the correlation keys travel in the environment instead.
98
+ configScope: 'global-singleton',
99
+ // What the generated `PermissionRequest` handler writes. codex's own default
100
+ // is 600 s; the receiver decides from in-memory state, so the budget it
101
+ // needs is small and the failure it bounds is a wedged server.
102
+ decisionTimeoutSeconds: 5,
103
+ },
104
+ // Same CamelCase dialect as Claude and copilot (#1757 §8.1). The table is
105
+ // shared, the choice to use it is this source's.
106
+ mappers: (0, event_mapper_1.fromNameTable)(hook_event_vocabulary_1.CAMEL_CASE_HOOK_EVENT_NAMES),
107
+ nativeEventNameFields: ['hook_event_name'],
108
+ conversationIdFields: hook_event_vocabulary_1.SESSION_ID_FIELDS,
109
+ toolCallIdFields: hook_event_vocabulary_1.TOOL_CALL_ID_FIELDS,
110
+ extractDetail: hook_event_vocabulary_1.extractSnakeCaseEventDetail,
111
+ // The captured `PermissionRequest` payload is Claude-shaped field for field —
112
+ // `hook_event_name`, `tool_name`, `tool_input`, `session_id`,
113
+ // `permission_mode` — so the same strict parser reads it, and the fixture is
114
+ // asserted against it rather than the resemblance being assumed.
115
+ //
116
+ // `prompt_id` is absent: codex's nearest field is `turn_id`, which identifies
117
+ // a *turn* and is therefore shared by every approval inside one. Mapping it
118
+ // onto `promptId` would give two approvals the same decision id, so it is
119
+ // left null and the receiver mints a unique id per request.
120
+ parsePermissionRequest: (payload) => (0, permission_request_payload_1.parsePermissionRequestPayload)(payload),
121
+ // codex has no `AskUserQuestion` tool, so there is no structured question to
122
+ // read. Null degrades to the behaviour of a machine without hooks: the picker
123
+ // is scraped off the screen exactly as it was before this Issue.
124
+ parseQuestion: () => null,
125
+ encodeVerdict: encodeCodexVerdict,
126
+ // S3 / S4 / S5. The whole of the config generation lives in `./hooks-config`,
127
+ // which never throws — a session that starts without hooks is the status quo,
128
+ // a session that fails to start is not.
129
+ prepareLaunch: (target, executablePath) => {
130
+ const command = (0, hooks_config_1.buildCodexLaunchCommand)(executablePath, target);
131
+ // Reported only when the command actually reflects an injection. Both
132
+ // `CM_AGENT_HOOKS_INJECT=0` and a file that could not be written return the
133
+ // bare executable, and claiming a settings file then would name a file this
134
+ // launch is not using.
135
+ const settingsPath = command === executablePath ? null : (0, hooks_config_1.getCodexHooksPath)();
136
+ return { command, settingsPath };
137
+ },
138
+ });
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * codex's {@link CLIToolType} id, on its own so nothing has to import the
4
+ * source to name the tool (Issue #1760).
5
+ *
6
+ * Mirrors `../claude/tool-id`: `hook-settings-generator` needs Claude's id
7
+ * without pulling in Claude's source, and `cli-tools/codex` needs codex's id
8
+ * without pulling in codex's.
9
+ *
10
+ * @module lib/hooks/sources/codex/tool-id
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.CODEX_CLI_TOOL_ID = void 0;
14
+ /** The registry key and the `tool` parameter codex's hooks post under. */
15
+ exports.CODEX_CLI_TOOL_ID = 'codex';
@@ -0,0 +1,473 @@
1
+ "use strict";
2
+ /**
3
+ * The hook configuration CommandMate injects for GitHub Copilot CLI, and the
4
+ * command that starts a copilot session with it (Issue #1761, Epic #1720 Phase
5
+ * 4-3).
6
+ *
7
+ * Copilot's payloads are the closest of the four new tools to Claude Code's —
8
+ * same CamelCase event names, same snake_case fields, same
9
+ * `hookSpecificOutput` verdict envelope — so `copilot/source.ts` is short. The
10
+ * *delivery* side is not close at all, and everything unusual about this tool
11
+ * lives in this file.
12
+ *
13
+ * ## Five measurements this file is built on
14
+ *
15
+ * The first three come from the Issue #1757 spike
16
+ * (`docs/design/agent-hooks-phase4-live-verification.md` §5.2); the last two
17
+ * were taken for this Issue against copilot 1.0.79 and are written up in
18
+ * `docs/design/copilot-agent-hooks-injection.md`.
19
+ *
20
+ * 1. **The file is `~/.copilot/settings.json`, not `config.json`.**
21
+ * `copilot help config` documents `hooks` as a `config.json` key and it
22
+ * does fire from there — once. Copilot then rewrites `config.json` in its
23
+ * own machine-managed form (`// This file is managed automatically.`) and
24
+ * moves `hooks` into `settings.json`, so a config CommandMate wrote there
25
+ * would vanish at the next launch with nothing to show for it (P2).
26
+ * 2. **`type: "http"` does not work.** Not a single request arrived from an
27
+ * `http` handler and no error was printed (§5.2.5). `type: "command"` is
28
+ * the only delivery mechanism, which is why `scripts/hooks/cmate-agent-event.sh`
29
+ * is on the critical path here and is not for Claude.
30
+ * 3. **The decision budget is ≈10 s, not Claude's 600 s** (§5.2.3), and it is
31
+ * fail-open: a late verdict is discarded and the tool runs. Every command
32
+ * below therefore bounds its own `curl` at {@link COPILOT_HOOK_CURL_TIMEOUT_SECONDS},
33
+ * comfortably inside the window, instead of trusting the agent's timer.
34
+ * 4. **Hook commands run through a shell.** `;`, `$(…)`, `[ … ]`, redirection
35
+ * and `printf` all behave, which is what makes a self-bounding, self-gating
36
+ * one-liner possible at all.
37
+ * 5. **The copilot process's environment reaches its hooks.** A variable set
38
+ * on the launch command line is visible to every hook it spawns. This is
39
+ * load-bearing — see below.
40
+ *
41
+ * ## Why correlation travels in the environment
42
+ *
43
+ * Claude bakes `worktreeId` and `instanceId` into the hook URL, because
44
+ * `--settings <file>` is per launch: `claude` and `claude-2` get different
45
+ * files. Copilot has no `--settings`. Its only configuration is one file for
46
+ * the whole machine (`configScope: 'global-singleton'`), so a URL fixed at
47
+ * write time could only ever name one instance, and the *second* copilot
48
+ * session in a worktree would post under the first one's identity.
49
+ *
50
+ * Measurement 5 is the way out: the launch command sets
51
+ * {@link COPILOT_WORKTREE_ID_ENV} and {@link COPILOT_INSTANCE_ID_ENV}, the hook
52
+ * reads them at fire time, and one global file serves every worktree and every
53
+ * instance correctly.
54
+ *
55
+ * It also makes the file **inert for sessions CommandMate did not start**. A
56
+ * machine-global hook otherwise fires for a copilot the operator ran in their
57
+ * own terminal, and if that terminal happened to sit in a registered worktree
58
+ * its `Stop` would resolve by `cwd` and release a `commandmate wait` that
59
+ * nobody's agent had finished. Every command below opens with a guard on the
60
+ * worktree variable and exits quietly when it is unset.
61
+ *
62
+ * ## What is written into somebody else's file
63
+ *
64
+ * `~/.copilot/settings.json` belongs to the user. This module **merges**: it
65
+ * strips out entries carrying {@link COPILOT_HOOK_MARKER} (its own, from a
66
+ * previous launch), appends the current ones, and copies every other key and
67
+ * every other handler through untouched. An existing file it cannot parse is
68
+ * left alone entirely and the session starts without hooks — losing events is
69
+ * recoverable, overwriting a user's settings is not.
70
+ *
71
+ * @module lib/hooks/sources/copilot/hook-settings
72
+ */
73
+ Object.defineProperty(exports, "__esModule", { value: true });
74
+ exports.COPILOT_EVENT_HOOKS = exports.COPILOT_PERMISSION_HOOK_EVENT = exports.COPILOT_AUTH_TOKEN_ENV = exports.COPILOT_INSTANCE_ID_ENV = exports.COPILOT_WORKTREE_ID_ENV = exports.COPILOT_HOOK_CURL_TIMEOUT_SECONDS = exports.COPILOT_HOOK_TIMEOUT_SECONDS = exports.COPILOT_HOOK_MARKER = exports.COPILOT_LAUNCH_COMMAND = void 0;
75
+ exports.getCopilotHomeDirectory = getCopilotHomeDirectory;
76
+ exports.getCopilotSettingsPath = getCopilotSettingsPath;
77
+ exports.buildCopilotEventCommand = buildCopilotEventCommand;
78
+ exports.buildCopilotPermissionCommand = buildCopilotPermissionCommand;
79
+ exports.buildCopilotHookSettings = buildCopilotHookSettings;
80
+ exports.mergeCopilotHookSettings = mergeCopilotHookSettings;
81
+ exports.readCopilotSettings = readCopilotSettings;
82
+ exports.writeCopilotHookSettings = writeCopilotHookSettings;
83
+ exports.buildCopilotEnvironmentPrefix = buildCopilotEnvironmentPrefix;
84
+ exports.isCopilotHookInjectionEnabled = isCopilotHookInjectionEnabled;
85
+ exports.buildCopilotLaunchCommand = buildCopilotLaunchCommand;
86
+ const fs_1 = require("fs");
87
+ const os_1 = require("os");
88
+ const path_1 = require("path");
89
+ const safe_directory_1 = require("../../../../config/safe-directory");
90
+ const types_1 = require("../../../../lib/cli-tools/types");
91
+ const env_1 = require("../../../../lib/env");
92
+ const hook_settings_generator_1 = require("../../../../lib/hooks/hook-settings-generator");
93
+ const path_validator_1 = require("../../../../lib/security/path-validator");
94
+ const logger_1 = require("../../../../lib/logger");
95
+ const tool_id_1 = require("./tool-id");
96
+ const logger = (0, logger_1.createLogger)('lib/hooks/sources/copilot/hook-settings');
97
+ /** Loopback: the agent runs on the machine the server runs on. */
98
+ const HOOK_HOST = '127.0.0.1';
99
+ /**
100
+ * The launch command, unchanged from what `cli-tools/copilot.ts` has sent since
101
+ * Issue #545.
102
+ *
103
+ * Copilot is started through the `gh` extension wrapper rather than by an
104
+ * absolute path, so "executable path" is a two-word command here. It stays a
105
+ * bare command: `gh` warns that Copilot's own flags must be preceded by `--`,
106
+ * and this Issue adds no flags — the configuration is entirely in the settings
107
+ * file and the environment.
108
+ */
109
+ exports.COPILOT_LAUNCH_COMMAND = 'gh copilot';
110
+ /**
111
+ * Present verbatim in every command this module writes, and the only way it
112
+ * recognises its own entries when merging into the user's file.
113
+ *
114
+ * Spelled as an argument to `:` — the shell's no-op builtin — so it is inert,
115
+ * portable, and visible both to `grep` and to a human reading their settings.
116
+ */
117
+ exports.COPILOT_HOOK_MARKER = 'cmate-copilot-agent-hooks';
118
+ /**
119
+ * Seconds copilot waits for a hook before giving up, measured (#1757 §5.2.3).
120
+ *
121
+ * Two orders of magnitude below Claude's 600 s, which is the single number a
122
+ * Claude-shaped decision path gets wrong. Published as
123
+ * `capabilities.decisionTimeoutSeconds` so callers can read it instead of
124
+ * assuming.
125
+ *
126
+ * Left unwritten in the generated config: the measured *default* is this, and
127
+ * an explicit `"timeout"` did not divide the window the way its value suggested
128
+ * (`"timeout": 3` produced a 5.12 s wait). The commands bound themselves
129
+ * instead.
130
+ */
131
+ exports.COPILOT_HOOK_TIMEOUT_SECONDS = 10;
132
+ /**
133
+ * `curl --max-time` for the generated commands.
134
+ *
135
+ * Under {@link COPILOT_HOOK_TIMEOUT_SECONDS} on purpose: a wedged server should
136
+ * cost the agent four seconds and a missing event, and should never be the
137
+ * thing that decides whether the verdict arrived in time.
138
+ */
139
+ exports.COPILOT_HOOK_CURL_TIMEOUT_SECONDS = 4;
140
+ /** Carries the worktree id from the launch command line to the hook. */
141
+ exports.COPILOT_WORKTREE_ID_ENV = 'CM_AGENT_WORKTREE_ID';
142
+ /** Carries the instance id the same way; absent means the primary instance. */
143
+ exports.COPILOT_INSTANCE_ID_ENV = 'CM_AGENT_INSTANCE_ID';
144
+ /** Bearer token variable, read at fire time exactly as the relay script reads it. */
145
+ exports.COPILOT_AUTH_TOKEN_ENV = 'CM_AUTH_TOKEN';
146
+ /**
147
+ * Copilot's name for the event that gates a tool call (#1757 §5.2.4).
148
+ *
149
+ * Copilot has no `PermissionRequest`. `PreToolUse` is the approval gate *and*
150
+ * the observation point, and its response body decides whether the call runs —
151
+ * which is why this event alone is pointed at `/api/hooks/permission-request`
152
+ * and does not reach the event store. See `copilot/source.ts` for what that
153
+ * costs.
154
+ */
155
+ exports.COPILOT_PERMISSION_HOOK_EVENT = 'PreToolUse';
156
+ /**
157
+ * The events pointed at the fire-and-forget receiver, with the relay `--event`
158
+ * word each one is sent as.
159
+ *
160
+ * Exactly the payloads captured in `tests/fixtures/hooks/copilot/`, minus
161
+ * `PreToolUse`. Nothing unmeasured is registered — in particular `Notification`,
162
+ * which was configured during the spike and never fired once (README, §5.2.6).
163
+ * A hook that never fires is not free: it is a word `capabilities` would have
164
+ * to promise and a caller could wait forever for.
165
+ */
166
+ exports.COPILOT_EVENT_HOOKS = [
167
+ ['SessionStart', 'session_start'],
168
+ ['UserPromptSubmit', 'user_prompt_submit'],
169
+ ['PostToolUse', 'post_tool_use'],
170
+ ['Stop', 'stop'],
171
+ ['SessionEnd', 'session_end'],
172
+ ];
173
+ /**
174
+ * Copilot's home directory.
175
+ *
176
+ * `COPILOT_HOME` is copilot's own override and the only session-scoped
177
+ * injection point it has; honouring it means an operator who isolates copilot
178
+ * gets an isolated hook config too, and tests get a directory that is not the
179
+ * developer's.
180
+ *
181
+ * Issue #1774: `writeCopilotHookSettings` creates this directory with a
182
+ * recursive mkdir, which does not fail but *hangs* for a path inside `/proc`,
183
+ * `/sys` or `/dev`. Such a value is refused here and `~/.copilot` is used.
184
+ */
185
+ function getCopilotHomeDirectory() {
186
+ const fallback = (0, path_1.join)((0, os_1.homedir)(), '.copilot');
187
+ return (0, safe_directory_1.resolveSafeDirectory)(process.env.COPILOT_HOME, fallback, 'COPILOT_HOME');
188
+ }
189
+ /** The one file copilot reads hooks from. */
190
+ function getCopilotSettingsPath() {
191
+ return (0, path_1.join)(getCopilotHomeDirectory(), 'settings.json');
192
+ }
193
+ function receiverUrl(path, options) {
194
+ return `http://${HOOK_HOST}:${options.port ?? (0, env_1.getServerPort)()}${path}`;
195
+ }
196
+ /**
197
+ * The guard every generated command opens with.
198
+ *
199
+ * Consumes stdin before exiting rather than exiting straight away: copilot
200
+ * writes the payload to the hook's stdin, and a hook that exits first turns
201
+ * that write into an EPIPE on the agent's side.
202
+ *
203
+ * @param onSkip - Extra shell to run before exiting, e.g. printing a body
204
+ */
205
+ function guardPrelude(onSkip = '') {
206
+ return (`: ${exports.COPILOT_HOOK_MARKER}; ` +
207
+ `if [ -z "\${${exports.COPILOT_WORKTREE_ID_ENV}:-}" ]; then cat >/dev/null; ${onSkip}exit 0; fi; `);
208
+ }
209
+ /** `"$CM_AGENT_INSTANCE_ID"`, defaulting to the primary instance. */
210
+ const INSTANCE_EXPANSION = `"\${${exports.COPILOT_INSTANCE_ID_ENV}:-${tool_id_1.COPILOT_CLI_TOOL_ID}}"`;
211
+ /** `"$CM_AGENT_WORKTREE_ID"`, only reached once the guard has proved it non-empty. */
212
+ const WORKTREE_EXPANSION = `"$${exports.COPILOT_WORKTREE_ID_ENV}"`;
213
+ /**
214
+ * `set --` preamble that assembles curl's arguments, with the bearer header
215
+ * added only when the token is in the environment.
216
+ *
217
+ * Written this way rather than as `${TOKEN:+-H "…"}` because that expansion
218
+ * word-splits into four arguments and sends a header of `Authorization:`
219
+ * alone. The relay script solves it identically.
220
+ */
221
+ function curlArgumentPreamble() {
222
+ return (`set -- -sS -m ${exports.COPILOT_HOOK_CURL_TIMEOUT_SECONDS} -X POST ` +
223
+ `-H 'Content-Type: application/json'; ` +
224
+ `if [ -n "\${${exports.COPILOT_AUTH_TOKEN_ENV}:-}" ]; then ` +
225
+ `set -- "$@" -H "Authorization: Bearer $${exports.COPILOT_AUTH_TOKEN_ENV}"; fi; `);
226
+ }
227
+ /**
228
+ * A receiver URL with the correlation keys appended at fire time.
229
+ *
230
+ * The static half is single-quoted and the variables are double-quoted next to
231
+ * it, so the shell concatenates them into one word without re-splitting. Both
232
+ * ids are validated before injection ({@link buildCopilotLaunchCommand}) and
233
+ * draw from `[A-Za-z0-9_-]`, so no percent-encoding is required.
234
+ */
235
+ function runtimeCorrelatedUrl(base) {
236
+ return (`${(0, hook_settings_generator_1.shellQuote)(`${base}?tool=${tool_id_1.COPILOT_CLI_TOOL_ID}&worktreeId=`)}${WORKTREE_EXPANSION}` +
237
+ `'&instanceId='${INSTANCE_EXPANSION}`);
238
+ }
239
+ /**
240
+ * The command for one observation event.
241
+ *
242
+ * Prefers the shipped relay: it already reads copilot's payload from stdin,
243
+ * knows its CamelCase spellings, extracts the subtype and exits 0 when the POST
244
+ * fails. When the package layout puts it out of reach, an inline `curl` posts
245
+ * the agent's payload verbatim to the same receiver — which reads it, because
246
+ * `normalizeEvent` is what turns `hook_event_name` into a word (S1/S2).
247
+ */
248
+ function buildCopilotEventCommand(event, options = {}) {
249
+ const url = receiverUrl(hook_settings_generator_1.AGENT_EVENT_PATH, options);
250
+ const relay = options.relayScriptPath === undefined ? (0, hook_settings_generator_1.resolveRelayScriptPath)() : options.relayScriptPath;
251
+ if (relay) {
252
+ return (guardPrelude() +
253
+ [
254
+ (0, hook_settings_generator_1.shellQuote)(relay),
255
+ '--tool',
256
+ (0, hook_settings_generator_1.shellQuote)(tool_id_1.COPILOT_CLI_TOOL_ID),
257
+ '--event',
258
+ (0, hook_settings_generator_1.shellQuote)(event),
259
+ '--worktree-id',
260
+ WORKTREE_EXPANSION,
261
+ '--instance-id',
262
+ INSTANCE_EXPANSION,
263
+ '--url',
264
+ (0, hook_settings_generator_1.shellQuote)(url),
265
+ '--stdin-json',
266
+ ].join(' '));
267
+ }
268
+ return (guardPrelude() +
269
+ curlArgumentPreamble() +
270
+ `curl "$@" --data-binary @- ${runtimeCorrelatedUrl(url)} >/dev/null 2>&1 || true`);
271
+ }
272
+ /**
273
+ * The command for `PreToolUse`, whose stdout copilot obeys.
274
+ *
275
+ * There is no relay for this direction — `cmate-agent-event.sh` discards the
276
+ * response body, which is the whole payload here — so it is always an inline
277
+ * `curl` that pipes the receiver's answer straight to stdout. Measured end to
278
+ * end against copilot 1.0.79: a body of `{"hookSpecificOutput":{…
279
+ * "permissionDecision":"deny"}}` prints `Denied by preToolUse hook: …` and the
280
+ * command does not run; `"allow"` runs it with no prompt; `{}` falls through to
281
+ * copilot's ordinary flow.
282
+ *
283
+ * Every failure path prints `{}`: an unreachable server, a timeout and a
284
+ * session CommandMate did not start all have to look like "no opinion", which
285
+ * is the measured fail-safe.
286
+ */
287
+ function buildCopilotPermissionCommand(options = {}) {
288
+ const url = receiverUrl(hook_settings_generator_1.PERMISSION_REQUEST_PATH, options);
289
+ return (guardPrelude(`printf '{}'; `) +
290
+ curlArgumentPreamble() +
291
+ `out=$(curl "$@" --data-binary @- ${runtimeCorrelatedUrl(url)} 2>/dev/null); ` +
292
+ `[ -n "$out" ] || out='{}'; printf '%s' "$out"`);
293
+ }
294
+ /**
295
+ * The `hooks` block CommandMate owns.
296
+ *
297
+ * Deterministic for a given port and relay path, so re-launching rewrites the
298
+ * same bytes rather than accumulating entries.
299
+ */
300
+ function buildCopilotHookSettings(options = {}) {
301
+ const hooks = {};
302
+ for (const [hookEventName, relayEvent] of exports.COPILOT_EVENT_HOOKS) {
303
+ hooks[hookEventName] = [
304
+ { hooks: [{ type: 'command', command: buildCopilotEventCommand(relayEvent, options) }] },
305
+ ];
306
+ }
307
+ hooks[exports.COPILOT_PERMISSION_HOOK_EVENT] = [
308
+ { hooks: [{ type: 'command', command: buildCopilotPermissionCommand(options) }] },
309
+ ];
310
+ return { hooks };
311
+ }
312
+ function isPlainObject(value) {
313
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
314
+ }
315
+ /** Whether this handler is one this module wrote. */
316
+ function isOwnedHandler(value) {
317
+ return (isPlainObject(value) &&
318
+ typeof value.command === 'string' &&
319
+ value.command.includes(exports.COPILOT_HOOK_MARKER));
320
+ }
321
+ /**
322
+ * Drop this module's previous entries from one event's array, keeping the
323
+ * user's.
324
+ *
325
+ * Both shapes copilot accepts are handled: a grouped `{hooks: [...]}` has its
326
+ * handler list filtered and is dropped only when nothing of the user's is left
327
+ * in it, and a flat handler is dropped on its own. Anything unrecognised is
328
+ * passed through — a merge that cannot identify an entry has no business
329
+ * deleting it.
330
+ */
331
+ function stripOwnedEntries(entries) {
332
+ const kept = [];
333
+ for (const entry of entries) {
334
+ if (isOwnedHandler(entry))
335
+ continue;
336
+ if (isPlainObject(entry) && Array.isArray(entry.hooks)) {
337
+ const handlers = entry.hooks.filter((handler) => !isOwnedHandler(handler));
338
+ if (handlers.length === 0 && entry.hooks.length > 0)
339
+ continue;
340
+ kept.push({ ...entry, hooks: handlers });
341
+ continue;
342
+ }
343
+ kept.push(entry);
344
+ }
345
+ return kept;
346
+ }
347
+ /**
348
+ * Fold the generated block into whatever is already in the user's file.
349
+ *
350
+ * @param existing - The parsed contents of `settings.json`, or `{}`
351
+ * @param generated - {@link buildCopilotHookSettings}'s output
352
+ * @returns A new object; `existing` is not mutated
353
+ * @throws When `existing.hooks` is present but not an object, which means this
354
+ * file is not shaped the way copilot documents and merging into it would be
355
+ * guessing
356
+ */
357
+ function mergeCopilotHookSettings(existing, generated) {
358
+ const priorHooks = existing.hooks;
359
+ if (priorHooks !== undefined && !isPlainObject(priorHooks)) {
360
+ throw new Error('copilot settings.json has a non-object "hooks" key');
361
+ }
362
+ const merged = { ...(priorHooks ?? {}) };
363
+ for (const [event, groups] of Object.entries(generated.hooks)) {
364
+ const prior = merged[event];
365
+ if (prior !== undefined && !Array.isArray(prior)) {
366
+ // Not ours to reshape. Skipping this one event costs its structured
367
+ // signal; rewriting it would cost the operator whatever they meant by it.
368
+ logger.warn('copilot-hook-event-not-an-array', { event });
369
+ continue;
370
+ }
371
+ merged[event] = [...stripOwnedEntries(prior ?? []), ...groups];
372
+ }
373
+ return { ...existing, hooks: merged };
374
+ }
375
+ /**
376
+ * Read the user's `settings.json`.
377
+ *
378
+ * @returns The parsed object, or `{}` when the file does not exist
379
+ * @throws When the file exists but cannot be read or is not a JSON object —
380
+ * the caller turns that into "start without hooks", never into a rewrite
381
+ */
382
+ function readCopilotSettings(settingsPath) {
383
+ let raw;
384
+ try {
385
+ raw = (0, fs_1.readFileSync)(settingsPath, 'utf8');
386
+ }
387
+ catch (error) {
388
+ if (error.code === 'ENOENT')
389
+ return {};
390
+ throw error;
391
+ }
392
+ if (raw.trim() === '')
393
+ return {};
394
+ const parsed = JSON.parse(raw);
395
+ if (!isPlainObject(parsed)) {
396
+ throw new Error('copilot settings.json is not a JSON object');
397
+ }
398
+ return parsed;
399
+ }
400
+ /**
401
+ * Merge the generated hooks into copilot's settings file and return its path.
402
+ *
403
+ * @throws Anything the read, the merge or the write throws. Callers must treat
404
+ * a throw as "launch without hooks" (fail-open).
405
+ */
406
+ function writeCopilotHookSettings(options = {}) {
407
+ const settingsPath = options.settingsPath ?? getCopilotSettingsPath();
408
+ const merged = mergeCopilotHookSettings(readCopilotSettings(settingsPath), buildCopilotHookSettings(options));
409
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(settingsPath), { recursive: true, mode: 0o700 });
410
+ (0, fs_1.writeFileSync)(settingsPath, `${JSON.stringify(merged, null, 2)}\n`, { mode: 0o600 });
411
+ return settingsPath;
412
+ }
413
+ /**
414
+ * The environment assignments that tell a hook which instance fired it.
415
+ *
416
+ * A prefix on the command line rather than `tmux set-environment`: the pane is
417
+ * created before the agent starts and the assignment then travels with the one
418
+ * process that matters, out of reach of `sanitizeSessionEnvironment` and of
419
+ * anything else that edits the session's environment later.
420
+ */
421
+ function buildCopilotEnvironmentPrefix(worktreeId, instanceId) {
422
+ return (`${exports.COPILOT_WORKTREE_ID_ENV}=${(0, hook_settings_generator_1.shellQuote)(worktreeId)} ` +
423
+ `${exports.COPILOT_INSTANCE_ID_ENV}=${(0, hook_settings_generator_1.shellQuote)(instanceId)}`);
424
+ }
425
+ /**
426
+ * Whether to inject at all.
427
+ *
428
+ * Shares `CM_AGENT_HOOKS_INJECT=0` with every other tool, so one switch rolls
429
+ * the whole feature back. Re-read here rather than imported so that copilot's
430
+ * rollback cannot come to depend on Claude's module loading first.
431
+ */
432
+ function isCopilotHookInjectionEnabled() {
433
+ return process.env.CM_AGENT_HOOKS_INJECT !== '0';
434
+ }
435
+ /**
436
+ * The command that launches copilot for one instance (S3 / S4 / S5).
437
+ *
438
+ * Never throws. Injection is an enhancement to a session that has to start
439
+ * anyway, so every failure — the switch is off, an id would not survive the
440
+ * receiver's own validation, the settings file is unreadable or unwritable —
441
+ * returns the bare command and no settings path, which is byte-for-byte what
442
+ * `cli-tools/copilot.ts` sent before this Issue.
443
+ *
444
+ * @param executablePath - Normally {@link COPILOT_LAUNCH_COMMAND}
445
+ * @param target - The instance being started
446
+ */
447
+ function buildCopilotLaunchCommand(executablePath, target, options = {}) {
448
+ const bare = { command: executablePath, settingsPath: null };
449
+ if (!isCopilotHookInjectionEnabled())
450
+ return bare;
451
+ const instanceId = target.instanceId ?? tool_id_1.COPILOT_CLI_TOOL_ID;
452
+ if (!(0, types_1.isValidInstanceId)(instanceId) || !(0, path_validator_1.isValidWorktreeId)(target.worktreeId)) {
453
+ // Both become URL parameters the receivers re-validate; a value that would
454
+ // be rejected there is not worth injecting, and a rejected event is worse
455
+ // than no event because it is a 400 in the operator's log for every turn.
456
+ logger.warn('copilot-hook-invalid-correlation-key', { worktreeId: target.worktreeId });
457
+ return bare;
458
+ }
459
+ try {
460
+ const settingsPath = writeCopilotHookSettings(options);
461
+ return {
462
+ command: `${buildCopilotEnvironmentPrefix(target.worktreeId, instanceId)} ${executablePath}`,
463
+ settingsPath,
464
+ };
465
+ }
466
+ catch (error) {
467
+ logger.warn('copilot-hook-settings-write-failed', {
468
+ worktreeId: target.worktreeId,
469
+ error: error instanceof Error ? error.message : String(error),
470
+ });
471
+ return bare;
472
+ }
473
+ }