commandmate 0.33.3 → 0.34.1

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 (355) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +146 -146
  3. package/.next/app-path-routes-manifest.json +26 -26
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/prerender-manifest.json +15 -15
  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/agents/update/route_client-reference-manifest.js +1 -1
  9. package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
  10. package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/app/update-check/route.js +1 -1
  12. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/assistant/conversation/route.js +1 -1
  14. package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
  15. package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/assistant/current-output/route.js +1 -1
  17. package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
  18. package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/assistant/messages/route.js +1 -1
  20. package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
  21. package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
  22. package/.next/server/app/api/assistant/session/route.js +1 -1
  23. package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
  24. package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/assistant/start/route.js +2 -2
  26. package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
  27. package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
  28. package/.next/server/app/api/assistant/terminal/route.js +3 -3
  29. package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
  30. package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/assistant/tools/route.js +1 -1
  32. package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
  33. package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/capabilities/route.js +1 -1
  38. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/hooks/agent-event/route.js +1 -1
  47. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  48. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/hooks/claude-done/route.js +2 -2
  50. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  51. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/hooks/permission-request/route.js +1 -1
  53. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  54. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  58. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
  61. package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
  62. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/relays/route.js +1 -1
  64. package/.next/server/app/api/relays/route.js.nft.json +1 -1
  65. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  71. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  72. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/repositories/clone/route.js +1 -1
  74. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  75. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/repositories/restore/route.js +1 -1
  78. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  79. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/repositories/route.js +1 -1
  81. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  82. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/repositories/scan/route.js +1 -1
  84. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  85. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/repositories/sync/route.js +1 -1
  87. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  88. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/settings/default-agents/route.js +1 -1
  91. package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
  92. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  104. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  110. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  113. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
  116. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  122. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
  153. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  155. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  156. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  157. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  159. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  160. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  163. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  164. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  165. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  166. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  167. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  169. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  170. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  171. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/resolve-target/route.js +1 -1
  173. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  176. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  177. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  178. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  179. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  180. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -1
  181. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  182. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/schedules/route.js +1 -1
  184. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  185. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  186. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  187. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  188. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  189. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +2 -2
  190. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  191. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  192. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  193. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  194. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  196. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  197. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  198. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  199. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  200. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  201. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  202. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  203. package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
  204. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  205. package/.next/server/app/api/worktrees/[id]/terminal/route.js +2 -2
  206. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  207. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  208. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  209. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  210. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  211. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  212. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  213. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  214. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  215. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  216. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  217. package/.next/server/app/api/worktrees/[id]/verify/config/route.js +1 -1
  218. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  219. package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
  220. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  221. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
  222. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  223. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  224. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  225. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  226. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  227. package/.next/server/app/api/worktrees/route.js +1 -1
  228. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  229. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  230. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  231. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  232. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  233. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  234. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  235. package/.next/server/app/page.js +1 -1
  236. package/.next/server/app/page_client-reference-manifest.js +1 -1
  237. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  238. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  239. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  240. package/.next/server/app/sessions/page.js +1 -1
  241. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  242. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  243. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  244. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  245. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  246. package/.next/server/app/worktrees/[id]/page.js +8 -8
  247. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  248. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  249. package/.next/server/app-paths-manifest.json +26 -26
  250. package/.next/server/chunks/1501.js +4 -4
  251. package/.next/server/chunks/1886.js +2 -2
  252. package/.next/server/chunks/2119.js +1 -1
  253. package/.next/server/chunks/2404.js +1 -1
  254. package/.next/server/chunks/2445.js +1 -1
  255. package/.next/server/chunks/3077.js +1 -1
  256. package/.next/server/chunks/3241.js +1 -1
  257. package/.next/server/chunks/3323.js +1 -1
  258. package/.next/server/chunks/3480.js +2 -2
  259. package/.next/server/chunks/434.js +1 -0
  260. package/.next/server/chunks/4443.js +1 -1
  261. package/.next/server/chunks/4802.js +1 -1
  262. package/.next/server/chunks/5164.js +1 -1
  263. package/.next/server/chunks/5571.js +1 -1
  264. package/.next/server/chunks/595.js +3 -3
  265. package/.next/server/chunks/6130.js +1 -1
  266. package/.next/server/chunks/6285.js +1 -1
  267. package/.next/server/chunks/6286.js +7 -7
  268. package/.next/server/chunks/6337.js +1 -1
  269. package/.next/server/chunks/650.js +1 -1
  270. package/.next/server/chunks/663.js +1 -1
  271. package/.next/server/chunks/6677.js +25 -0
  272. package/.next/server/chunks/7075.js +1 -0
  273. package/.next/server/chunks/7905.js +1 -1
  274. package/.next/server/chunks/8583.js +1 -0
  275. package/.next/server/chunks/8807.js +10 -10
  276. package/.next/server/chunks/8918.js +4 -2
  277. package/.next/server/chunks/9080.js +1 -0
  278. package/.next/server/chunks/964.js +1 -0
  279. package/.next/server/middleware-manifest.json +5 -5
  280. package/.next/server/pages/500.html +1 -1
  281. package/.next/server/server-reference-manifest.json +1 -1
  282. package/.next/server/src/middleware.js +1 -1
  283. package/.next/server/src/middleware.js.map +1 -1
  284. package/.next/static/chunks/2053-9ac860ab4e92ac89.js +1 -0
  285. package/.next/static/chunks/2652-18b8fea64413467e.js +1 -0
  286. package/.next/static/chunks/{4718-814f4ecfd7fcc513.js → 4718-29cfcfb806724d9a.js} +1 -1
  287. package/.next/static/chunks/{8361-9bb887203c456edb.js → 8361-0142beb59be2abf3.js} +1 -1
  288. package/.next/static/chunks/app/{page-022090bf6f932818.js → page-26a42881c8648cd3.js} +1 -1
  289. package/.next/static/chunks/app/sessions/page-a3ab6f122ec60d5a.js +1 -0
  290. package/.next/static/chunks/app/worktrees/[id]/page-60eab4e24c37cb77.js +1 -0
  291. package/.next/static/css/b09bede85744e1cf.css +1 -0
  292. package/.next/trace +13 -13
  293. package/README.md +141 -24
  294. package/dist/cli/commands/ask.d.ts.map +1 -1
  295. package/dist/cli/commands/ask.js +11 -2
  296. package/dist/cli/commands/remote.d.ts +93 -3
  297. package/dist/cli/commands/remote.d.ts.map +1 -1
  298. package/dist/cli/commands/remote.js +232 -9
  299. package/dist/cli/commands/respond.d.ts.map +1 -1
  300. package/dist/cli/commands/respond.js +8 -4
  301. package/dist/cli/commands/send.d.ts.map +1 -1
  302. package/dist/cli/commands/send.js +18 -0
  303. package/dist/cli/commands/wait.d.ts +18 -3
  304. package/dist/cli/commands/wait.d.ts.map +1 -1
  305. package/dist/cli/commands/wait.js +137 -2
  306. package/dist/cli/docs/agent-operations.d.ts +2 -2
  307. package/dist/cli/docs/agent-operations.d.ts.map +1 -1
  308. package/dist/cli/docs/agent-operations.js +41 -1
  309. package/dist/cli/types/index.d.ts +9 -0
  310. package/dist/cli/types/index.d.ts.map +1 -1
  311. package/dist/cli/utils/api-client.d.ts.map +1 -1
  312. package/dist/cli/utils/api-client.js +11 -1
  313. package/dist/cli/utils/remote-state.d.ts +32 -0
  314. package/dist/cli/utils/remote-state.d.ts.map +1 -1
  315. package/dist/cli/utils/remote-state.js +12 -0
  316. package/dist/cli/utils/session-target.d.ts +23 -6
  317. package/dist/cli/utils/session-target.d.ts.map +1 -1
  318. package/dist/cli/utils/session-target.js +33 -5
  319. package/dist/server/locales/en/worktree.json +21 -2
  320. package/dist/server/locales/ja/worktree.json +21 -2
  321. package/dist/server/server.js +204 -16
  322. package/dist/server/src/config/schedule-config.js +45 -3
  323. package/dist/server/src/lib/cli-tools/antigravity.js +95 -10
  324. package/dist/server/src/lib/cli-tools/submit-verified-sender.js +377 -16
  325. package/dist/server/src/lib/cmate-parser.js +8 -1
  326. package/dist/server/src/lib/db/agent-instances-db.js +11 -0
  327. package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +140 -19
  328. package/dist/server/src/lib/detection/tools/claude/detect.js +13 -2
  329. package/dist/server/src/lib/detection/tools/claude/picker-chrome.js +239 -0
  330. package/dist/server/src/lib/detection/tools/claude/prompt.js +74 -6
  331. package/dist/server/src/lib/hooks/sources/claude/history.js +391 -34
  332. package/dist/server/src/lib/hooks/sources/claude/transcript.js +91 -0
  333. package/dist/server/src/lib/hooks/sources/opencode/ports.js +6 -0
  334. package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +114 -11
  335. package/dist/server/src/lib/polling/response-checker.js +82 -3
  336. package/dist/server/src/lib/relay/relay-session-ref.js +15 -8
  337. package/dist/server/src/lib/schedule-manager.js +178 -19
  338. package/dist/server/src/lib/session/ask-user-question-prompt.js +4 -2
  339. package/dist/server/src/lib/session/claude-executor.js +33 -2
  340. package/dist/server/src/lib/session/resolve-session-target.js +37 -9
  341. package/dist/server/src/lib/verification/env-clean-gate.js +81 -4
  342. package/dist/server/src/lib/ws-server.js +74 -5
  343. package/dist/server/src/types/agent-transcript.js +143 -1
  344. package/package.json +1 -1
  345. package/.next/server/chunks/1068.js +0 -1
  346. package/.next/server/chunks/6808.js +0 -1
  347. package/.next/server/chunks/7875.js +0 -25
  348. package/.next/server/chunks/9553.js +0 -1
  349. package/.next/static/chunks/2053-3f299a7a2c7d6767.js +0 -1
  350. package/.next/static/chunks/8772-9be53a530950d804.js +0 -1
  351. package/.next/static/chunks/app/sessions/page-584adf11ae51df10.js +0 -1
  352. package/.next/static/chunks/app/worktrees/[id]/page-3af707e74a75172b.js +0 -1
  353. package/.next/static/css/acd46e25427a2080.css +0 -1
  354. /package/.next/static/{9x6U1h52hH_r6pOsGwI_p → oDWX0JcwfhUYrMnnqDWVC}/_buildManifest.js +0 -0
  355. /package/.next/static/{9x6U1h52hH_r6pOsGwI_p → oDWX0JcwfhUYrMnnqDWVC}/_ssgManifest.js +0 -0
@@ -82,6 +82,12 @@ function keyOf(target) {
82
82
  * not throw — it spins the event loop forever, so the `try/catch` around it
83
83
  * never runs. A file path is checked the same way a directory is (the match is
84
84
  * on the prefix), so the override is refused here and the default is used.
85
+ *
86
+ * Issue #2490: the default is also what a *test* gets when it says nothing, and
87
+ * the writers below are read-modify-write, so an unredirected suite rewrites
88
+ * the ledger a live server is using. `tests/setup.ts` pins the variable for the
89
+ * whole suite for that reason; `tests/unit/lib/hooks/sources/opencode/port-file-isolation-2490.test.ts`
90
+ * is the guard on that pin.
85
91
  */
86
92
  function getOpencodePortFilePath() {
87
93
  const fallback = (0, path_1.join)((0, os_1.homedir)(), '.commandmate', 'opencode-ports.json');
@@ -54,9 +54,11 @@
54
54
  * @module lib/polling/auto-yes-dialog-gate
55
55
  */
56
56
  Object.defineProperty(exports, "__esModule", { value: true });
57
- exports.AUTO_YES_DIALOG_GATE_ENV_VAR = exports.AUTO_YES_DIALOG_GATE_DEFAULT_MODE = exports.AUTO_YES_DIALOG_GATE_MODES = void 0;
57
+ exports.UNSUPPORTED_DIALOG_LAYOUT_REASON = exports.PROMPT_NO_LONGER_ACTIVE_REASON = exports.AUTO_YES_DIALOG_GATE_ENV_VAR = exports.AUTO_YES_DIALOG_GATE_DEFAULT_MODE = exports.AUTO_YES_DIALOG_GATE_MODES = void 0;
58
58
  exports.resolveAutoYesDialogGateMode = resolveAutoYesDialogGateMode;
59
59
  exports.evaluateAutoYesDialogGate = evaluateAutoYesDialogGate;
60
+ exports.evaluateDialogPresence = evaluateDialogPresence;
61
+ exports.judgePromptResponse = judgePromptResponse;
60
62
  const frame_1 = require("../../lib/detection/tools/frame");
61
63
  const registry_1 = require("../../lib/detection/tools/registry");
62
64
  /** Every accepted value of {@link AutoYesDialogGateMode}, for parsing and tests. */
@@ -168,23 +170,124 @@ const GATED_PROMPT_TYPE = 'multiple_choice';
168
170
  * @param env - Environment to read the kill switch from; injectable for tests
169
171
  */
170
172
  function evaluateAutoYesDialogGate(cliToolId, promptType, cleanOutput, env = process.env) {
171
- const detector = (0, registry_1.getToolStatusDetector)(cliToolId);
172
- const mode = resolveAutoYesDialogGateMode(cliToolId, env);
173
173
  // A tool nobody has measured is not gated whatever the table says. Belt and
174
174
  // braces on purpose: the failure mode of getting this wrong is Auto-Yes going
175
175
  // permanently silent for that tool, which looks exactly like a hung worker.
176
- if (promptType !== GATED_PROMPT_TYPE || !detector.hasDialogRules || mode === 'legacy') {
177
- return { allowed: true, dialog: null, mode, gated: false };
178
- }
179
- const dialog = detector.detectDialog((0, frame_1.normalizeFrame)(cleanOutput));
176
+ // `judgeDialogPresence` holds that rule; Issue #2457 split it out so the
177
+ // history-save and `/prompt-response` paths judge a frame the same way this
178
+ // one does instead of re-deriving the rollout table.
179
+ const presence = judgeDialogPresence(cliToolId, promptType, cleanOutput, resolveAutoYesDialogGateMode(cliToolId, env));
180
180
  return {
181
181
  // `keys` is a dialog and still not answerable: opencode's button strip takes
182
182
  // ←/→ + Enter, so the digit is swallowed and the Enter confirms whatever is
183
183
  // highlighted — asking to Reject would Approve (#1893). Sending nothing is
184
184
  // the only safe answer, and `wait` still stops for the frame.
185
- allowed: dialog !== null && dialog.answerMode === 'numbered',
186
- dialog,
187
- mode,
188
- gated: true,
185
+ //
186
+ // Issue #2457: this extra test is what makes `allowed` unusable as an
187
+ // "is there a dialog here?" answer, and why the save path reads
188
+ // {@link evaluateDialogPresence} instead.
189
+ allowed: presence.gated
190
+ ? presence.dialog !== null && presence.dialog.answerMode === 'numbered'
191
+ : true,
192
+ dialog: presence.dialog,
193
+ mode: presence.mode,
194
+ gated: presence.gated,
189
195
  };
190
196
  }
197
+ /** The one reading of a frame both gates share. `mode` is resolved by the caller. */
198
+ function judgeDialogPresence(cliToolId, promptType, frame, mode) {
199
+ const detector = (0, registry_1.getToolStatusDetector)(cliToolId);
200
+ if (promptType !== GATED_PROMPT_TYPE || !detector.hasDialogRules || mode === 'legacy') {
201
+ return { present: true, dialog: null, mode, gated: false };
202
+ }
203
+ const dialog = detector.detectDialog((0, frame_1.normalizeFrame)(frame));
204
+ return { present: dialog !== null, dialog, mode, gated: true };
205
+ }
206
+ /**
207
+ * Does this frame POSITIVELY carry a dialog for the numbered-list candidate the
208
+ * generic parser found on it? (Issue #2457)
209
+ *
210
+ * The same rollout table, the same `hasDialogRules` cross-check and the same
211
+ * `detectDialog` seam Auto-Yes reads — and deliberately NOT the same env var.
212
+ * `CM_AUTOYES_DIALOG_GATE` is the kill switch for *answering* prompts without a
213
+ * human: an operator who sets it because their unattended pipeline stopped
214
+ * replying is saying nothing at all about which rows belong in History, and
215
+ * letting that flag also decide what gets stored would make the chat surface's
216
+ * contents depend on an Auto-Yes setting. So this reads
217
+ * {@link AUTO_YES_DIALOG_GATE_DEFAULT_MODE} directly.
218
+ *
219
+ * Auto-Yes being enabled or disabled is likewise not a condition here: the false
220
+ * `1. / 2. / 3.` row #2457 reported was saved by the response poller, which runs
221
+ * for every session whether or not anything is auto-answering it.
222
+ *
223
+ * @param cliToolId - CLI tool the frame came from
224
+ * @param promptType - How the generic parser classified the frame; anything but
225
+ * `multiple_choice` is left alone (see the module docstring on `yes_no`)
226
+ * @param frame - The WHOLE capture the candidate was read off, **as captured**.
227
+ * Never the extracted response, the question alone or a tail window: the rules
228
+ * `detectDialog` applies are about where the block sits in the pane, so a
229
+ * fragment answers a different question from the one being asked.
230
+ *
231
+ * And never a `stripBoxDrawing`-ed string, which is the one place this differs
232
+ * from {@link evaluateAutoYesDialogGate}. That function is handed the spelling
233
+ * Auto-Yes already had in hand (`captureAndCleanOutput`); this one is handed
234
+ * the capture, because that is the spelling on which ALL FOUR gated tools have
235
+ * working rules. opencode is the proof and it is already documented in
236
+ * `tests/unit/detection/tools/dialogs.test.ts`: its permission strip is
237
+ * anchored on the input box's own gutter (#1893's anchor, which must not be
238
+ * weakened), so with the box drawing removed its live dialog reads as `null`.
239
+ * For Auto-Yes `null` and `keys` both mean "send nothing" and the difference
240
+ * is invisible; here `null` would mean "there is no prompt on this screen",
241
+ * which about an open approval is simply false. `normalizeFrame` runs
242
+ * `stripAnsi` itself and each tool's rule applies `stripBoxDrawing` where it
243
+ * needs it, so nothing is lost by handing over the capture — and
244
+ * `stripBoxDrawing` never runs twice over the same row.
245
+ */
246
+ function evaluateDialogPresence(cliToolId, promptType, frame) {
247
+ return judgeDialogPresence(cliToolId, promptType, frame, exports.AUTO_YES_DIALOG_GATE_DEFAULT_MODE[cliToolId] ?? 'legacy');
248
+ }
249
+ /** `/prompt-response`'s refusal when the prompt it was asked to answer is gone. */
250
+ exports.PROMPT_NO_LONGER_ACTIVE_REASON = 'prompt_no_longer_active';
251
+ /**
252
+ * `/prompt-response`'s refusal when an AskUserQuestion picker IS on screen but
253
+ * the tool's own dialog rule could not vouch for it (Issue #2486).
254
+ */
255
+ exports.UNSUPPORTED_DIALOG_LAYOUT_REASON = 'unsupported_dialog_layout';
256
+ /**
257
+ * Fixed text, never quoting the answer (SEC-003, as in `prompt-answer-semantic`),
258
+ * so it is safe to return to a client verbatim.
259
+ */
260
+ const UNSUPPORTED_DIALOG_LAYOUT_MESSAGE = 'An AskUserQuestion picker is on screen, but its layout could not be verified as one of the ' +
261
+ "agent's dialogs, so no key was sent. Answer it in the terminal, or retry once the screen has settled.";
262
+ /**
263
+ * May `/prompt-response` answer this frame? Null when it may; otherwise why not
264
+ * (Issue #2486).
265
+ *
266
+ * The route has refused whenever the generic parser found no prompt OR the gate
267
+ * could not vouch for the one it found (#161, #2457), and it called both
268
+ * `prompt_no_longer_active`. For the second half that was sometimes false: in
269
+ * #2486 an AskUserQuestion picker with a preview pane was open on screen, `wait`
270
+ * had just exited 10 for it, and `respond` said the prompt was gone. The two
271
+ * cases need different next steps — retry versus answer at the pane — so they
272
+ * get different reasons.
273
+ *
274
+ * The evidence for "a picker IS on screen" is the picker's own footer
275
+ * (`Enter to select · … navigate`, which sets `isAskUserQuestion`), not the
276
+ * parser's candidate alone: #2457's reply frames are candidates too, and for
277
+ * them `prompt_no_longer_active` stays the answer.
278
+ *
279
+ * @param promptCheck - What the generic parser (or agy's reader) found on the frame
280
+ * @param presence - {@link evaluateDialogPresence} for the same frame
281
+ */
282
+ function judgePromptResponse(promptCheck, presence) {
283
+ if (promptCheck.isPrompt && presence.present)
284
+ return null;
285
+ const promptData = promptCheck.promptData;
286
+ if (promptCheck.isPrompt &&
287
+ presence.gated &&
288
+ promptData?.type === 'multiple_choice' &&
289
+ promptData.isAskUserQuestion === true) {
290
+ return { reason: exports.UNSUPPORTED_DIALOG_LAYOUT_REASON, message: UNSUPPORTED_DIALOG_LAYOUT_MESSAGE };
291
+ }
292
+ return { reason: exports.PROMPT_NO_LONGER_ACTIVE_REASON };
293
+ }
@@ -5,6 +5,7 @@ exports.PENDING_SCRAPE_HOLD_MS = void 0;
5
5
  exports.incompleteResult = incompleteResult;
6
6
  exports.buildPromptExtractionResult = buildPromptExtractionResult;
7
7
  exports.detectPromptOnCleanFrame = detectPromptOnCleanFrame;
8
+ exports.isNumberedDialogVouched = isNumberedDialogVouched;
8
9
  exports.detectPromptWithOptions = detectPromptWithOptions;
9
10
  exports.extractResponse = extractResponse;
10
11
  exports.resetPendingScrapedResponses = resetPendingScrapedResponses;
@@ -31,6 +32,10 @@ const response_cleaner_1 = require("../response-cleaner");
31
32
  const copilot_constants_1 = require("../../config/copilot-constants");
32
33
  const tui_accumulator_1 = require("../tui-accumulator");
33
34
  const prompt_dedup_1 = require("./prompt-dedup");
35
+ // Issue #2457: the same rollout table, `hasDialogRules` cross-check and
36
+ // `detectDialog` seam Auto-Yes reads, reached through the presence helper rather
37
+ // than through `evaluateAutoYesDialogGate` — see `isNumberedDialogVouched`.
38
+ const auto_yes_dialog_gate_1 = require("./auto-yes-dialog-gate");
34
39
  const prompt_dedup_state_1 = require("./prompt-dedup-state");
35
40
  const response_dedup_1 = require("./response-dedup");
36
41
  const structured_history_gate_1 = require("./structured-history-gate");
@@ -230,6 +235,55 @@ function detectPromptOnCleanFrame(cleanOutput, cliToolId, precomputedLines) {
230
235
  const promptOptions = (0, cli_patterns_1.buildDetectPromptOptions)(cliToolId);
231
236
  return (0, prompt_detector_1.detectPrompt)(cleanOutput, precomputedLines ? { ...promptOptions, precomputedLines } : promptOptions);
232
237
  }
238
+ /**
239
+ * Is the generic parser's numbered-list candidate a dialog the tool vouches for?
240
+ * (Issue #2457)
241
+ *
242
+ * ## The candidate this removes
243
+ *
244
+ * `detectPrompt` classifies a frame as `multiple_choice` from the ROWS alone,
245
+ * and for Claude `buildDetectPromptOptions` returns
246
+ * `requireDefaultIndicator: false` — so a reply that happens to answer in
247
+ * Markdown ("1. … 2. … 3. …") is a candidate with no `❯` anywhere near it.
248
+ * Every one of those was stored as a `prompt` message, and the chat surface drew
249
+ * a tool-approval chip over an ordinary answer.
250
+ *
251
+ * ## Why the whole frame, and only the frame
252
+ *
253
+ * `detectDialog` decides by POSITION and CHROME — where the option block sits
254
+ * relative to the transcript tail, what (if anything) is drawn under it. None of
255
+ * that survives being handed the extracted response, the question string or a
256
+ * tail window, so the caller passes the same capture the candidate came off, as
257
+ * captured: `evaluateDialogPresence` documents why the box drawing must still be
258
+ * on it.
259
+ *
260
+ * ## What a `false` means downstream
261
+ *
262
+ * Not "there is no output here" — only "this is not a prompt". The caller falls
263
+ * through to the ordinary completion/partial reading, so a real reply is still
264
+ * saved by the normal path and a half-written one still reports
265
+ * `isComplete: false`.
266
+ *
267
+ * @param cliToolId - CLI tool the frame came from
268
+ * @param promptDetection - What the generic parser (or a tool reader) answered
269
+ * @param frame - The whole capture this tick made, as captured
270
+ * @returns True when the candidate may be treated as a live prompt
271
+ */
272
+ function isNumberedDialogVouched(cliToolId, promptDetection, frame) {
273
+ const presence = (0, auto_yes_dialog_gate_1.evaluateDialogPresence)(cliToolId, promptDetection.promptData?.type, frame);
274
+ if (presence.present)
275
+ return true;
276
+ // `debug`, not `info`: the poller re-reads a static idle pane every 2s, so a
277
+ // reply carrying a numbered list would print this on every tick for as long as
278
+ // it stays on screen. The suppression is not silent where it matters — nothing
279
+ // is stored, so History simply shows the reply the ordinary path saves.
280
+ logger.debug('prompt-candidate-not-vouched', {
281
+ cliToolId,
282
+ promptType: promptDetection.promptData?.type,
283
+ gateMode: presence.mode,
284
+ });
285
+ return false;
286
+ }
233
287
  /**
234
288
  * Internal helper: detect prompt with CLI-tool-specific options.
235
289
  *
@@ -432,7 +486,13 @@ function extractResponse(output, lastCapturedLine, cliToolId, captureWindowLines
432
486
  cliToolId === 'command-code') {
433
487
  const fullOutput = lines.join('\n');
434
488
  const promptDetection = detectPromptWithOptions(fullOutput, cliToolId);
435
- if (promptDetection.isPrompt) {
489
+ // Issue #2457: a candidate the tool's own rules cannot vouch for is not a
490
+ // prompt, and returning here would declare the turn finished ON IT — the
491
+ // early-completion half of the defect. Falling through leaves the ordinary
492
+ // completion/partial reading to answer, which is what a reply containing a
493
+ // numbered list needs. The gate is handed `fullOutput`, the capture itself,
494
+ // not the string `detectPromptWithOptions` cleaned for the parser.
495
+ if (promptDetection.isPrompt && isNumberedDialogVouched(cliToolId, promptDetection, fullOutput)) {
436
496
  return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex, promptDetection, captureWindowSaturated);
437
497
  }
438
498
  }
@@ -700,8 +760,11 @@ function extractResponse(output, lastCapturedLine, cliToolId, captureWindowLines
700
760
  // Check if this is an interactive prompt
701
761
  if (cliToolId !== 'opencode') {
702
762
  const fullOutput = lines.join('\n');
763
+ // Issue #2457: same gate as the early check above — this is the site a frame
764
+ // reaches when the completion rules said "unfinished", so a candidate
765
+ // accepted here would end the turn on it just the same.
703
766
  const promptDetection = detectPromptWithOptions(fullOutput, cliToolId);
704
- if (promptDetection.isPrompt) {
767
+ if (promptDetection.isPrompt && isNumberedDialogVouched(cliToolId, promptDetection, fullOutput)) {
705
768
  return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex, promptDetection, captureWindowSaturated);
706
769
  }
707
770
  }
@@ -1067,7 +1130,23 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1067
1130
  }
1068
1131
  // Response is complete! Check if it's a prompt.
1069
1132
  const promptDetection = result.promptDetection ?? detectPromptWithOptions(result.response, cliToolId);
1070
- if (promptDetection.isPrompt) {
1133
+ // Issue #2457: the gate is applied HERE as well as in `extractResponse`, and
1134
+ // to a carried `result.promptDetection` as well as to one derived on this
1135
+ // line. Two independent reasons, either one sufficient:
1136
+ //
1137
+ // 1. the fallback above reads `result.response` — the EXTRACTED text, with
1138
+ // the pane's chrome already cut off — so a reply's `1. / 2. / 3.` rows
1139
+ // look even more like a dialog here than they did upstream, and a tool
1140
+ // the early check never runs for (opencode) reaches the save path only
1141
+ // through this line;
1142
+ // 2. `promptDetection` carried from `extractResponse` is the one thing that
1143
+ // could walk a candidate past the gate unexamined, and this is the last
1144
+ // position before the row is written.
1145
+ //
1146
+ // The frame is the capture this tick made — never `result.response`, which
1147
+ // has lost the position `detectDialog` judges by.
1148
+ const promptIsLive = promptDetection.isPrompt && isNumberedDialogVouched(cliToolId, promptDetection, output);
1149
+ if (promptIsLive) {
1071
1150
  // Issue #565: Content hash-based duplicate prompt prevention
1072
1151
  const promptContent = promptDetection.rawContent || promptDetection.cleanContent;
1073
1152
  const normalizedForDedup = (0, prompt_dedup_1.normalizePromptForDedup)(promptContent, cliToolId);
@@ -19,6 +19,7 @@ exports.resolveRelaySession = resolveRelaySession;
19
19
  exports.relayWorktreeExists = relayWorktreeExists;
20
20
  const agent_instances_db_1 = require("../../lib/db/agent-instances-db");
21
21
  const worktree_db_1 = require("../../lib/db/worktree-db");
22
+ const resolve_session_target_1 = require("../../lib/session/resolve-session-target");
22
23
  const logger_1 = require("../../lib/logger");
23
24
  const logger = (0, logger_1.createLogger)('relay-session-ref');
24
25
  /** The tool assumed when neither the roster nor the worktree declares one. */
@@ -26,23 +27,29 @@ const FALLBACK_CLI_TOOL = 'claude';
26
27
  /**
27
28
  * Resolve one end of a relay.
28
29
  *
29
- * The tool comes from `resolveInstanceCliTool` — the same authority
30
+ * The tool comes from `resolveSessionTarget` — the same authority
30
31
  * `POST /api/worktrees/:id/send` uses, so a relay cannot address a session the
31
32
  * send route would address differently (#1629 is the record of what two
32
33
  * authorities on that cost) — falling back to the worktree's own tool and then
33
34
  * to claude.
35
+ *
36
+ * Issue #2491: that sentence was true of `resolveInstanceCliTool` when this
37
+ * module was written and stopped being true when `send` moved to the shared
38
+ * resolver. The two answer identically here — a relay never names a tool, and
39
+ * with `requestedCliTool` absent both walk roster → primary anchor → worktree
40
+ * default → claude — so this is the comment made true again, not a behaviour
41
+ * change. `resolveSessionTarget` folds in the worktree-default and claude
42
+ * stages this function used to spell out for itself; `FALLBACK_CLI_TOOL` stays
43
+ * because the catch below still needs an answer when the roster cannot be read
44
+ * at all.
34
45
  */
35
46
  function resolveRelaySession(db, endpoint) {
36
47
  let cliToolId = FALLBACK_CLI_TOOL;
37
48
  let alias = endpoint.instanceId;
38
49
  try {
39
- const resolution = (0, agent_instances_db_1.resolveInstanceCliTool)(db, endpoint.worktreeId, endpoint.instanceId);
40
- if (resolution.ok && resolution.cliToolId) {
41
- cliToolId = resolution.cliToolId;
42
- }
43
- else {
44
- cliToolId = (0, worktree_db_1.getWorktreeById)(db, endpoint.worktreeId)?.cliToolId ?? FALLBACK_CLI_TOOL;
45
- }
50
+ cliToolId = (0, resolve_session_target_1.resolveSessionTarget)(db, endpoint.worktreeId, {
51
+ instanceId: endpoint.instanceId,
52
+ }).cliToolId;
46
53
  const rosterRow = (0, agent_instances_db_1.getAgentInstance)(db, endpoint.worktreeId, endpoint.instanceId);
47
54
  if (rosterRow?.alias)
48
55
  alias = rosterRow.alias;
@@ -58,6 +58,37 @@ function isCronJobActive(cronJob) {
58
58
  }
59
59
  return true;
60
60
  }
61
+ /**
62
+ * Attach the execution callback to a cron job.
63
+ *
64
+ * The callback closes over the {@link ScheduleState}, never over the entry the
65
+ * timer was built from (Issue #2456). Two things follow, and both are load
66
+ * bearing when a timer is replaced while its schedule keeps running:
67
+ *
68
+ * - the tick executes `state.entry`, so a Message/CLI Tool/Permission edit
69
+ * picked up by a later sync is the one the next run uses;
70
+ * - the tick reads `state.isExecuting`, the single guard every timer this
71
+ * schedule ever owns shares. `protect: true` cannot stand in for it: the
72
+ * callback returns `undefined` rather than the execution's Promise, so croner
73
+ * never sees itself as blocking, and a fresh timer with its own state would
74
+ * start a second run on top of a live one.
75
+ *
76
+ * @param state - The schedule state the callback executes
77
+ * @param cronJob - The cron job to attach the callback to
78
+ */
79
+ function scheduleExecution(state, cronJob) {
80
+ cronJob.schedule(() => {
81
+ // Issue #1343: executeSchedule() rejects if its own error handling fails
82
+ // (e.g. the DB is still down when writing the 'failed' log). Without this
83
+ // catch the rejection is silently unhandled.
84
+ void (0, job_executor_1.executeSchedule)(state).catch((error) => {
85
+ logger.error('execution:unhandled', {
86
+ name: state.entry.name,
87
+ error: error instanceof Error ? error.message : String(error),
88
+ });
89
+ });
90
+ });
91
+ }
61
92
  function createScheduleState(worktreeId, scheduleId, entry) {
62
93
  const cronJob = new croner_1.Cron(entry.cronExpression, {
63
94
  paused: false,
@@ -70,18 +101,102 @@ function createScheduleState(worktreeId, scheduleId, entry) {
70
101
  isExecuting: false,
71
102
  entry,
72
103
  };
73
- cronJob.schedule(() => {
74
- // Issue #1343: executeSchedule() rejects if its own error handling fails
75
- // (e.g. the DB is still down when writing the 'failed' log). Without this
76
- // catch the rejection is silently unhandled.
77
- void (0, job_executor_1.executeSchedule)(state).catch((error) => {
78
- logger.error('execution:unhandled', {
79
- name: entry.name,
80
- error: error instanceof Error ? error.message : String(error),
81
- });
104
+ scheduleExecution(state, cronJob);
105
+ return state;
106
+ }
107
+ /**
108
+ * Move a live schedule onto a new cron expression (Issue #2456).
109
+ *
110
+ * The bug this exists for: the sync loop used to answer a changed CMATE.md row
111
+ * with `existingState.entry = entry`, which updates every field the executor
112
+ * reads at run time but not the pattern `croner` compiled into the timer at
113
+ * construction. A Cron edit was therefore accepted by the parser, written to the
114
+ * DB, shown by the API — and ignored by the thing that actually fires, for as
115
+ * long as the server stayed up.
116
+ *
117
+ * ## Order of operations
118
+ *
119
+ * The replacement is built **paused** first, so the only step that can reject a
120
+ * pattern happens while the old timer is still the one and only live timer, and
121
+ * a rejected pattern leaves the schedule exactly as it was. Only then is the old
122
+ * timer stopped and the replacement resumed — in that order, so the two are
123
+ * never runnable at the same instant. `state.cronJob` and `state.entry` are
124
+ * published last, together, so a caller that reads the state never sees a timer
125
+ * and an entry that disagree.
126
+ *
127
+ * A pause costs nothing here: `resume()` follows synchronously, so no tick can
128
+ * pass while the replacement is paused and there is nothing to "catch up" on.
129
+ * The new expression's first run is its first occurrence after now, which is
130
+ * what a schedule edit should mean.
131
+ *
132
+ * ## Failure
133
+ *
134
+ * Failure keeps the old timer and the old entry, and says so — no
135
+ * `schedule:updated`. What it must never do is leave two live timers, so the
136
+ * candidate (which has never run) is the one discarded. If the failure was
137
+ * `stop()` on the old timer, that timer is left in whatever state it reached:
138
+ * either it stopped, and the next sync's inactive-state recovery rebuilds it
139
+ * from the newest entry, or it survived on its old expression. Both are
140
+ * recoverable, and the caller invalidates this worktree's mtime cache so the
141
+ * next sync retries even though CMATE.md has not been touched again.
142
+ *
143
+ * @param state - The live schedule state (kept, so its execution guard survives)
144
+ * @param entry - The entry carrying the new cron expression
145
+ * @returns true when the schedule now runs on `entry.cronExpression`
146
+ */
147
+ function replaceCronExpression(state, entry) {
148
+ const previousCron = state.entry.cronExpression;
149
+ const failed = (phase, error) => {
150
+ logger.error('schedule:update-failed', {
151
+ scheduleId: state.scheduleId,
152
+ worktreeId: state.worktreeId,
153
+ previousCron,
154
+ cron: entry.cronExpression,
155
+ phase,
156
+ error: error instanceof Error ? error.message : String(error),
82
157
  });
158
+ return false;
159
+ };
160
+ let candidate = null;
161
+ try {
162
+ candidate = new croner_1.Cron(entry.cronExpression, { paused: true, protect: true });
163
+ scheduleExecution(state, candidate);
164
+ }
165
+ catch (error) {
166
+ if (candidate) {
167
+ try {
168
+ candidate.stop();
169
+ }
170
+ catch {
171
+ // The candidate never ran; nothing else to reclaim
172
+ }
173
+ }
174
+ return failed('prepare', error);
175
+ }
176
+ try {
177
+ state.cronJob.stop();
178
+ candidate.resume();
179
+ }
180
+ catch (error) {
181
+ try {
182
+ candidate.stop();
183
+ }
184
+ catch {
185
+ // The candidate never ran; nothing else to reclaim
186
+ }
187
+ return failed('swap', error);
188
+ }
189
+ state.cronJob = candidate;
190
+ state.entry = entry;
191
+ // Deliberately without the message: the prompt body is not an operational
192
+ // fact and execution_logs already holds it.
193
+ logger.info('schedule:updated', {
194
+ scheduleId: state.scheduleId,
195
+ worktreeId: state.worktreeId,
196
+ previousCron,
197
+ cron: entry.cronExpression,
83
198
  });
84
- return state;
199
+ return true;
85
200
  }
86
201
  /**
87
202
  * Get or initialize the global manager state.
@@ -124,6 +239,9 @@ function getLazyDbInstance() {
124
239
  * Issue #406: Async I/O for readCmateFile() to avoid event loop blocking.
125
240
  * Issue #409: Uses mtime caching to skip unchanged CMATE.md files
126
241
  * and batchUpsertSchedules() for efficient DB operations.
242
+ * Issue #2456: A changed Cron column swaps the timer (replaceCronExpression);
243
+ * everything else about an existing schedule is an entry update that leaves the
244
+ * timer alone.
127
245
  *
128
246
  * DJ-007: isSyncing guard prevents concurrent execution when async
129
247
  * operations exceed the 60-second polling interval.
@@ -188,10 +306,6 @@ async function syncSchedules() {
188
306
  for (let i = 0; i < entries.length; i++) {
189
307
  const entry = entries[i];
190
308
  const scheduleId = scheduleIds[i];
191
- if (manager.schedules.size >= exports.MAX_CONCURRENT_SCHEDULES) {
192
- logger.warn('schedule:max-concurrent-reached', { limit: exports.MAX_CONCURRENT_SCHEDULES });
193
- return;
194
- }
195
309
  activeScheduleIds.add(scheduleId);
196
310
  // Skip disabled or incomplete entries
197
311
  if (!entry.enabled || !entry.cronExpression) {
@@ -214,16 +328,61 @@ async function syncSchedules() {
214
328
  catch {
215
329
  // Ignore cleanup errors for inactive cron jobs
216
330
  }
217
- const recoveredState = createScheduleState(worktree.id, scheduleId, entry);
218
- manager.schedules.set(scheduleId, recoveredState);
219
- logger.warn('schedule:recreated-inactive', { name: entry.name, cron: entry.cronExpression });
331
+ // Issue #2456: recover onto the newest entry, and keep the same
332
+ // ScheduleState so a still-running execution's guard survives the
333
+ // exchange rather than being reset by a fresh object.
334
+ try {
335
+ const recovered = new croner_1.Cron(entry.cronExpression, { paused: false, protect: true });
336
+ scheduleExecution(existingState, recovered);
337
+ existingState.cronJob = recovered;
338
+ existingState.entry = entry;
339
+ logger.warn('schedule:recreated-inactive', { name: entry.name, cron: entry.cronExpression });
340
+ }
341
+ catch (recoverError) {
342
+ manager.cmateFileCache.delete(worktree.path);
343
+ logger.error('schedule:update-failed', {
344
+ scheduleId,
345
+ worktreeId: worktree.id,
346
+ previousCron: existingState.entry.cronExpression,
347
+ cron: entry.cronExpression,
348
+ phase: 'recover',
349
+ error: recoverError instanceof Error ? recoverError.message : String(recoverError),
350
+ });
351
+ }
352
+ continue;
353
+ }
354
+ // Issue #2456: a changed Cron column has to reach the timer, not
355
+ // just the entry the executor reads. String comparison on purpose —
356
+ // the parser has already trimmed the cell, and two spellings of the
357
+ // same schedule are a rewrite the operator asked for.
358
+ if (existingState.entry.cronExpression !== entry.cronExpression) {
359
+ if (!replaceCronExpression(existingState, entry)) {
360
+ // The old timer and the old entry are still in force. Drop this
361
+ // worktree's mtime cache so the next sync retries even though
362
+ // CMATE.md itself has not changed again — without this the file
363
+ // would have to be touched to get another attempt.
364
+ manager.cmateFileCache.delete(worktree.path);
365
+ }
220
366
  continue;
221
367
  }
222
- // Update entry if changed
368
+ // Metadata-only change (Message / CLI Tool / Permission / model):
369
+ // the timer stays, and the next tick reads this entry through the
370
+ // shared state.
223
371
  existingState.entry = entry;
224
372
  continue;
225
373
  }
226
- // Create new cron job
374
+ // Create new cron job. The cap belongs here and only here (Issue
375
+ // #2456): it bounds how many timers exist, so refusing a new one must
376
+ // not also refuse to update, disable or clean up the ones already
377
+ // running — nor abandon the rest of the sync, which is what the
378
+ // previous `return` did.
379
+ if (manager.schedules.size >= exports.MAX_CONCURRENT_SCHEDULES) {
380
+ logger.warn('schedule:max-concurrent-reached', {
381
+ limit: exports.MAX_CONCURRENT_SCHEDULES,
382
+ name: entry.name,
383
+ });
384
+ continue;
385
+ }
227
386
  try {
228
387
  const state = createScheduleState(worktree.id, scheduleId, entry);
229
388
  manager.schedules.set(scheduleId, state);
@@ -100,9 +100,11 @@ function corresponds(a, b) {
100
100
  * Containment rather than the prefix rule the labels use, because the two are
101
101
  * extracted differently. An option label is a parsed line; the question is
102
102
  * whatever the detector's upward scan swept together, and on a real capture that
103
- * is several lines glued into one — the canary frame yields
103
+ * was several lines glued into one — before Issue #2486 the canary frame yielded
104
104
  * `"⏺ I'll load the TaskCreate tool schema first. ☐ First task Which task would
105
- * you like to start with?"` for a question that is only its last clause.
105
+ * you like to start with?"` for a question that is only its last clause. The
106
+ * picker's tab row now caps that scan, but the scan still reads at most a few
107
+ * rows, so a long question arrives cut and containment is still the right test.
106
108
  *
107
109
  * The floor stops a short question ("Continue?") from matching unrelated prose;
108
110
  * the uniqueness requirement in {@link matchAskUserQuestion} does the rest. On
@@ -106,7 +106,7 @@ function truncateOutput(output) {
106
106
  * - vibe-local: [-p <message> -y] or [--model <model> -p <message> -y]
107
107
  * - opencode: run --format json [-m <model>] [--agent <a>] [--variant <v>] [-c] [--title <t>] <message>
108
108
  * - antigravity: -p <message> --dangerously-skip-permissions
109
- * - command-code: -p <message> --output-format json [--yolo | --permission-mode <permission>]
109
+ * - command-code: -p <message> --output-format json [--yolo | --permission-mode <permission>] --no-auto-update
110
110
  * - others: -p <message> (fallback)
111
111
  *
112
112
  * @param message - Prompt message
@@ -201,13 +201,44 @@ function buildCliArgs(message, cliToolId, permission, options) {
201
201
  // `--yolo` does not override `.commandcode/settings.json`
202
202
  // `permissions.deny`; a denied tool call still ends the run with exit 4,
203
203
  // which {@link describeCommandCodeExit} names in the failure reason.
204
+ //
205
+ // ## Issue #2454: `yolo` is a Permission *column* value, and the default
206
+ //
207
+ // Every `--permission-mode` value leaves print mode read-only. The 1.53.0
208
+ // bundle injects a `print-permission-gate` mod whenever `--yolo` was not
209
+ // passed, and that mod answers `edit_file` / `write_file` /
210
+ // `shell_command` / `monitor_command` / `kill_shell` with `block: true`;
211
+ // the mode flag never reaches it. A blocked call does not fail the run —
212
+ // it ends exit 0 with `subtype: "success"` — so a schedule that asked for
213
+ // `default` looked successful and changed nothing.
214
+ //
215
+ // So the column now spells `--yolo` as the value `yolo`
216
+ // ({@link COMMAND_CODE_SCHEDULE_PERMISSIONS}, copilot's shape), and the
217
+ // parser fills an empty cell with it. The `else` below used to be the
218
+ // only way to reach `--yolo` and was unreachable from a schedule, because
219
+ // `DEFAULT_PERMISSIONS['command-code']` was `'default'`.
204
220
  const args = ['-p', message, '--output-format', 'json'];
205
- if (schedule_config_1.COMMAND_CODE_PERMISSIONS.includes(permission)) {
221
+ if (permission === schedule_config_1.COMMAND_CODE_YOLO_PERMISSION) {
222
+ args.push('--yolo');
223
+ }
224
+ else if (schedule_config_1.COMMAND_CODE_PERMISSIONS.includes(permission)) {
206
225
  args.push('--permission-mode', permission);
207
226
  }
208
227
  else {
228
+ // Not reachable through the parser (it falls back to `yolo`); this is
229
+ // the direct-call guard.
209
230
  args.push('--yolo');
210
231
  }
232
+ // Issue #2454: without this, `resolveCliStartupPlan` runs an update check
233
+ // and a background self-update on every scheduled run. Spelled as a
234
+ // literal for the same reason `getCommandForTool` spells `commandcode` as
235
+ // one — importing `COMMAND_CODE_LAUNCH_FLAGS` from
236
+ // `@/lib/cli-tools/command-code` would pull the tmux transport into the
237
+ // scheduler's dependency graph. The unit test imports both and asserts
238
+ // the spelling matches, so the copy cannot drift. `--trust` /
239
+ // `--skip-onboarding` are *not* copied: print mode dispatches before the
240
+ // TUI's trust and onboarding gates, so they have nothing to answer.
241
+ args.push('--no-auto-update');
211
242
  return args;
212
243
  }
213
244
  case 'antigravity':