commandmate 0.33.2 → 0.34.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 (370) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +160 -160
  3. package/.next/app-path-routes-manifest.json +28 -28
  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/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.js.nft.json +1 -1
  13. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/assistant/conversation/route.js +1 -1
  15. package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
  16. package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/assistant/current-output/route.js +1 -1
  18. package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
  19. package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/assistant/messages/route.js +1 -1
  21. package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
  22. package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/assistant/session/route.js +1 -1
  24. package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
  25. package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
  26. package/.next/server/app/api/assistant/start/route.js +2 -2
  27. package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
  28. package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/assistant/terminal/route.js +3 -3
  30. package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
  31. package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/assistant/tools/route.js +1 -1
  33. package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
  34. package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/capabilities/route.js +1 -1
  39. package/.next/server/app/api/capabilities/route.js.nft.json +1 -1
  40. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/hooks/agent-event/route.js +1 -1
  49. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  50. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/hooks/claude-done/route.js +2 -2
  52. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  53. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/hooks/permission-request/route.js +1 -1
  55. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  56. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  58. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/push/escalation/route.js +1 -1
  60. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/push/vapid/route.js +1 -1
  63. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  64. package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
  65. package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
  66. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/relays/route.js +1 -1
  68. package/.next/server/app/api/relays/route.js.nft.json +1 -1
  69. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  75. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  76. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/repositories/clone/route.js +1 -1
  78. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  79. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/repositories/restore/route.js +1 -1
  82. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  83. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/repositories/route.js +1 -1
  85. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  86. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/repositories/scan/route.js +1 -1
  88. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  89. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/repositories/sync/route.js +1 -1
  91. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  92. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/settings/default-agents/route.js +1 -1
  95. package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
  96. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/skills/[id]/route.js +1 -1
  100. package/.next/server/app/api/skills/[id]/route.js.nft.json +1 -1
  101. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/skills/installations/route.js +1 -1
  103. package/.next/server/app/api/skills/installations/route.js.nft.json +1 -1
  104. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/skills/route.js +1 -1
  108. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  109. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  120. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  123. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
  126. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  132. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  153. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  155. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  157. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  159. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  160. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
  163. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  164. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  165. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  166. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  167. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  169. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  170. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  173. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  176. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  177. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  179. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  180. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  181. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  182. package/.next/server/app/api/worktrees/[id]/resolve-target/route.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  184. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  185. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  186. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  187. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  188. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  189. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  190. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -1
  191. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  192. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  193. package/.next/server/app/api/worktrees/[id]/schedules/route.js +1 -1
  194. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  196. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  197. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  198. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  199. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +2 -2
  200. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  201. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  202. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  203. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js +1 -1
  204. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js.nft.json +1 -1
  205. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  206. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  207. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  208. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  209. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js +1 -1
  210. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js.nft.json +1 -1
  211. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  212. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  213. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  214. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  215. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  216. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  217. package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
  218. package/.next/server/app/api/worktrees/[id]/tasks/route.js.nft.json +1 -1
  219. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  220. package/.next/server/app/api/worktrees/[id]/terminal/route.js +2 -2
  221. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  222. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  223. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  224. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  225. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  226. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  227. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  228. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  229. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  230. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  231. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  232. package/.next/server/app/api/worktrees/[id]/verify/config/route.js +1 -1
  233. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  234. package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
  235. package/.next/server/app/api/worktrees/[id]/verify/route.js.nft.json +1 -1
  236. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  237. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
  238. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js.nft.json +1 -1
  239. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  240. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  241. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  242. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  243. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  244. package/.next/server/app/api/worktrees/route.js +1 -1
  245. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  246. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  247. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  248. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  249. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  250. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  251. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  252. package/.next/server/app/page.js +1 -1
  253. package/.next/server/app/page_client-reference-manifest.js +1 -1
  254. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  255. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  256. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  257. package/.next/server/app/sessions/page.js +1 -1
  258. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  259. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  260. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  261. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  262. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  263. package/.next/server/app/worktrees/[id]/page.js +10 -10
  264. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  265. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  266. package/.next/server/app-paths-manifest.json +28 -28
  267. package/.next/server/chunks/1049.js +1 -0
  268. package/.next/server/chunks/1501.js +6 -2
  269. package/.next/server/chunks/1886.js +3 -3
  270. package/.next/server/chunks/2119.js +1 -1
  271. package/.next/server/chunks/2404.js +1 -1
  272. package/.next/server/chunks/2445.js +1 -1
  273. package/.next/server/chunks/3123.js +1 -1
  274. package/.next/server/chunks/3241.js +1 -1
  275. package/.next/server/chunks/3323.js +1 -1
  276. package/.next/server/chunks/3480.js +2 -2
  277. package/.next/server/chunks/4443.js +1 -1
  278. package/.next/server/chunks/4802.js +1 -1
  279. package/.next/server/chunks/5164.js +1 -1
  280. package/.next/server/chunks/5571.js +1 -1
  281. package/.next/server/chunks/595.js +2 -2
  282. package/.next/server/chunks/6130.js +1 -0
  283. package/.next/server/chunks/6285.js +1 -1
  284. package/.next/server/chunks/6286.js +7 -7
  285. package/.next/server/chunks/6337.js +1 -1
  286. package/.next/server/chunks/650.js +1 -1
  287. package/.next/server/chunks/663.js +1 -1
  288. package/.next/server/chunks/6661.js +1 -1
  289. package/.next/server/chunks/6677.js +25 -0
  290. package/.next/server/chunks/6781.js +1 -1
  291. package/.next/server/chunks/7075.js +1 -0
  292. package/.next/server/chunks/7905.js +1 -1
  293. package/.next/server/chunks/8583.js +1 -0
  294. package/.next/server/chunks/8918.js +7 -5
  295. package/.next/server/chunks/9080.js +1 -0
  296. package/.next/server/chunks/9627.js +1 -1
  297. package/.next/server/chunks/964.js +1 -0
  298. package/.next/server/middleware-manifest.json +5 -5
  299. package/.next/server/pages/500.html +1 -1
  300. package/.next/server/server-reference-manifest.json +1 -1
  301. package/.next/static/chunks/2053-9ac860ab4e92ac89.js +1 -0
  302. package/.next/static/chunks/2652-18b8fea64413467e.js +1 -0
  303. package/.next/static/chunks/{4718-441a2af91f0374e9.js → 4718-2d9f0397a8f931c4.js} +1 -1
  304. package/.next/static/chunks/6022-7a7d1760fc9814c7.js +1 -0
  305. package/.next/static/chunks/{8361-9bb887203c456edb.js → 8361-0142beb59be2abf3.js} +1 -1
  306. package/.next/static/chunks/app/{page-022090bf6f932818.js → page-26a42881c8648cd3.js} +1 -1
  307. package/.next/static/chunks/app/sessions/page-a3ab6f122ec60d5a.js +1 -0
  308. package/.next/static/chunks/app/worktrees/[id]/page-a39ec2a0732d11a6.js +1 -0
  309. package/.next/static/css/b09bede85744e1cf.css +1 -0
  310. package/.next/trace +13 -13
  311. package/dist/cli/commands/ask.d.ts.map +1 -1
  312. package/dist/cli/commands/ask.js +11 -2
  313. package/dist/cli/commands/respond.d.ts.map +1 -1
  314. package/dist/cli/commands/respond.js +8 -4
  315. package/dist/cli/commands/send.d.ts.map +1 -1
  316. package/dist/cli/commands/send.js +18 -0
  317. package/dist/cli/commands/wait.d.ts +18 -3
  318. package/dist/cli/commands/wait.d.ts.map +1 -1
  319. package/dist/cli/commands/wait.js +137 -2
  320. package/dist/cli/docs/agent-operations.d.ts +2 -2
  321. package/dist/cli/docs/agent-operations.d.ts.map +1 -1
  322. package/dist/cli/docs/agent-operations.js +41 -1
  323. package/dist/cli/utils/session-target.d.ts +23 -6
  324. package/dist/cli/utils/session-target.d.ts.map +1 -1
  325. package/dist/cli/utils/session-target.js +33 -5
  326. package/dist/server/locales/en/worktree.json +30 -4
  327. package/dist/server/locales/ja/worktree.json +30 -4
  328. package/dist/server/src/config/schedule-config.js +45 -3
  329. package/dist/server/src/lib/cli-tools/antigravity.js +95 -10
  330. package/dist/server/src/lib/cli-tools/submit-verified-sender.js +377 -16
  331. package/dist/server/src/lib/cli-tools/vibe-local.js +10 -0
  332. package/dist/server/src/lib/cmate-parser.js +8 -1
  333. package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +140 -19
  334. package/dist/server/src/lib/detection/tools/claude/detect.js +13 -2
  335. package/dist/server/src/lib/detection/tools/claude/picker-chrome.js +239 -0
  336. package/dist/server/src/lib/detection/tools/claude/prompt.js +74 -6
  337. package/dist/server/src/lib/hooks/hook-settings-generator.js +61 -1
  338. package/dist/server/src/lib/hooks/sources/antigravity/history.js +302 -27
  339. package/dist/server/src/lib/hooks/sources/antigravity/transcript.js +103 -0
  340. package/dist/server/src/lib/hooks/sources/claude/history.js +391 -34
  341. package/dist/server/src/lib/hooks/sources/claude/transcript.js +91 -0
  342. package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +114 -11
  343. package/dist/server/src/lib/polling/response-checker.js +82 -3
  344. package/dist/server/src/lib/polling/structured-history-gate.js +68 -4
  345. package/dist/server/src/lib/schedule-manager.js +178 -19
  346. package/dist/server/src/lib/session/agent-session-lifecycle.js +31 -1
  347. package/dist/server/src/lib/session/ask-user-question-prompt.js +4 -2
  348. package/dist/server/src/lib/session/claude-executor.js +33 -2
  349. package/dist/server/src/lib/session/resolve-session-target.js +37 -9
  350. package/dist/server/src/lib/session/session-generation-archive.js +194 -0
  351. package/dist/server/src/lib/tasks/contract-message.js +83 -9
  352. package/dist/server/src/lib/tasks/contract-parser.js +2 -0
  353. package/dist/server/src/lib/verification/env-clean-gate.js +81 -4
  354. package/dist/server/src/types/agent-transcript.js +143 -1
  355. package/package.json +1 -1
  356. package/.next/server/chunks/1068.js +0 -1
  357. package/.next/server/chunks/153.js +0 -1
  358. package/.next/server/chunks/4299.js +0 -1
  359. package/.next/server/chunks/6808.js +0 -1
  360. package/.next/server/chunks/6838.js +0 -1
  361. package/.next/server/chunks/7366.js +0 -5
  362. package/.next/server/chunks/7875.js +0 -25
  363. package/.next/static/chunks/2053-3f299a7a2c7d6767.js +0 -1
  364. package/.next/static/chunks/6022-2fa0b5a82d4ce7b6.js +0 -1
  365. package/.next/static/chunks/8772-9be53a530950d804.js +0 -1
  366. package/.next/static/chunks/app/sessions/page-584adf11ae51df10.js +0 -1
  367. package/.next/static/chunks/app/worktrees/[id]/page-38edcd53e51f53b0.js +0 -1
  368. package/.next/static/css/acd46e25427a2080.css +0 -1
  369. /package/.next/static/{0a4qrU8WdUwQrlO4mfLMu → gniPBCKoXtbIOQey8kNcx}/_buildManifest.js +0 -0
  370. /package/.next/static/{0a4qrU8WdUwQrlO4mfLMu → gniPBCKoXtbIOQey8kNcx}/_ssgManifest.js +0 -0
@@ -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);
@@ -118,9 +118,41 @@ const logger = (0, logger_1.createLogger)('lib/polling/structured-history-gate')
118
118
  * can see is not a lock.
119
119
  */
120
120
  const captureQueue = (globalThis.__structuredHistoryCaptureQueue ??= new Map());
121
- /** Forget every instance's in-flight capture. Test seam. */
121
+ /**
122
+ * The last completion each instance was announced for (Issue #2443).
123
+ *
124
+ * Keyed by the same triple as {@link captureQueue}, holding whatever
125
+ * `StructuredHistoryCaptureReport.completionKey` the reader minted. Its only job
126
+ * is to make the completion edge an *edge*: the poller re-reads a finished turn
127
+ * every couple of seconds, and #2443 forbids adding a path that re-delivers a
128
+ * body somebody already has.
129
+ *
130
+ * On `globalThis` for the reason {@link captureQueue} is, and in memory for the
131
+ * reason `session/agent-event-state` gives: the value describes a live session,
132
+ * and a restart that forgets it costs at most one extra announcement into a
133
+ * relay whose own stash guard already refuses a second payload.
134
+ */
135
+ const announcedCompletions = (globalThis.__structuredHistoryAnnouncedCompletions ??= new Map());
136
+ /** Forget every instance's in-flight capture and announced completion. Test seam. */
122
137
  function resetStructuredHistoryCaptureQueue() {
123
138
  captureQueue.clear();
139
+ announcedCompletions.clear();
140
+ }
141
+ /**
142
+ * Whether this completion has already been announced for this instance (#2443).
143
+ *
144
+ * A reader that minted no key is always announced — the pre-#2443 behaviour, and
145
+ * the behaviour claude, codex and command-code keep. Records the key as a side
146
+ * effect when the answer is "no", because the two steps must not be separable:
147
+ * an announcement that forgot to record would repeat on the next poll.
148
+ */
149
+ function claimCompletionAnnouncement(key, completionKey) {
150
+ if (completionKey === undefined)
151
+ return true;
152
+ if (announcedCompletions.get(key) === completionKey)
153
+ return false;
154
+ announcedCompletions.set(key, completionKey);
155
+ return true;
124
156
  }
125
157
  /** The queue key; the same triple `buildCompositeKey` spells everywhere else. */
126
158
  function captureKeyOf(worktreeId, cliToolId, instanceId) {
@@ -296,17 +328,24 @@ async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, c
296
328
  return false;
297
329
  }
298
330
  const resolvedInstanceId = instanceId ?? cliToolId;
299
- return serializePerInstance(captureKeyOf(worktreeId, cliToolId, resolvedInstanceId), async () => {
331
+ const captureKey = captureKeyOf(worktreeId, cliToolId, resolvedInstanceId);
332
+ return serializePerInstance(captureKey, async () => {
300
333
  // The reader's own report, kept local and copied out below: `report` is
301
334
  // the caller's object and must end up holding one verdict, not whatever
302
335
  // the last reader to touch it happened to leave there.
303
336
  const readerReport = {};
304
337
  try {
305
338
  const captured = await reader.capture({ worktreeId, cliToolId, instanceId: resolvedInstanceId }, capture, readerReport);
339
+ // Issue #2443: absent means `settled`, which is what leaves the three
340
+ // readers that have a turn-closing record exactly where they were.
341
+ const completion = readerReport.completion ?? 'settled';
306
342
  if (report) {
307
343
  // A reader that says nothing is read as `unavailable` — the pre-#2436
308
344
  // behaviour, and the safe direction: the scraper keeps the turn.
309
345
  report.outcome = captured ? 'captured' : readerReport.outcome ?? 'unavailable';
346
+ report.completion = completion;
347
+ report.completionKey = readerReport.completionKey;
348
+ report.completionReason = readerReport.completionReason;
310
349
  }
311
350
  // Issue #2377: the moment a relay is waiting for. This is the single
312
351
  // point BOTH triggers of a pull capture pass through — the poller's save
@@ -317,8 +356,33 @@ async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, c
317
356
  // Announced only when the capture actually wrote something: a `false`
318
357
  // means the scraper is still the writer for this turn, and the poller's
319
358
  // own call below will announce it.
320
- if (captured)
321
- (0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, true);
359
+ //
360
+ // Issue #2443 adds the second condition, and it is the whole Issue.
361
+ // `captured` means History holds the newest turn; it does **not** mean
362
+ // the agent is finished with it, because antigravity has no record that
363
+ // closes a turn and an interim report is saved under the same key the
364
+ // conclusion will land on. Announcing here would hand a relay the
365
+ // interim body as the answer, and `broadcastMessage('message_updated',
366
+ // …)` — the repair #2438 added — does not take a delivered message
367
+ // back. So a provisional capture announces nothing at all; the relay
368
+ // keeps waiting on the deadline it already has, and the settled capture
369
+ // that follows announces the finished body.
370
+ if (captured && completion === 'settled') {
371
+ if (claimCompletionAnnouncement(captureKey, readerReport.completionKey)) {
372
+ (0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, true);
373
+ }
374
+ }
375
+ else if (captured) {
376
+ logger.info('structured-history-capture-provisional', {
377
+ worktreeId,
378
+ cliToolId,
379
+ instanceId: resolvedInstanceId,
380
+ // The reader's own word for what is missing; see
381
+ // `AntigravityTurnCompletionReason`. This is the line #2443 asks for
382
+ // — the reason left on the relay's waiting path.
383
+ reason: readerReport.completionReason ?? 'unknown',
384
+ });
385
+ }
322
386
  return captured;
323
387
  }
324
388
  catch (error) {
@@ -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);