commandmate 0.45.1 → 0.46.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 (388) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +319 -315
  3. package/.next/app-path-routes-manifest.json +15 -15
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/react-loadable-manifest.json +4 -4
  7. package/.next/required-server-files.json +1 -1
  8. package/.next/server/app/_not-found/page.js +1 -1
  9. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  10. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
  12. package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
  15. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  18. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/capabilities/route.js +1 -1
  20. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  22. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
  24. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  26. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  28. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  29. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/hooks/claude-done/route.js +9 -9
  31. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  32. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  34. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
  41. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/relays/route.js.nft.json +1 -1
  43. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  47. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  48. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/repositories/clone/route.js +1 -1
  50. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  51. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/repositories/restore/route.js +1 -1
  54. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  55. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/repositories/route.js +1 -1
  57. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  58. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/repositories/scan/route.js +1 -1
  60. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  61. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/repositories/sync/route.js +1 -1
  63. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  64. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
  67. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  83. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  84. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  86. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
  88. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
  89. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +3 -3
  93. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  95. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  96. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/worktrees/[id]/direct-input/route.js +1 -1
  98. package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
  99. package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +3 -3
  103. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  104. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
  130. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  132. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  135. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/logs/route.js +9 -9
  138. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  148. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  152. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  153. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  154. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  155. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +3 -3
  157. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/schedules/active/route.js +3 -3
  159. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  160. package/.next/server/app/api/worktrees/[id]/schedules/route.js +3 -3
  161. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  163. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  164. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  165. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  166. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  167. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  169. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  170. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  173. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  176. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  177. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  178. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  179. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  180. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  181. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  182. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  184. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  185. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  186. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  187. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  188. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  189. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  190. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  191. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  192. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  193. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  194. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  196. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  197. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  198. package/.next/server/app/api/worktrees/route.js +1 -1
  199. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  200. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  201. package/.next/server/app/login/page.js +1 -1
  202. package/.next/server/app/login/page.js.nft.json +1 -1
  203. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  204. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  205. package/.next/server/app/more/page.js +1 -1
  206. package/.next/server/app/more/page.js.nft.json +1 -1
  207. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  208. package/.next/server/app/offline/page.js +1 -1
  209. package/.next/server/app/offline/page.js.nft.json +1 -1
  210. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  211. package/.next/server/app/page.js +1 -1
  212. package/.next/server/app/page.js.nft.json +1 -1
  213. package/.next/server/app/page_client-reference-manifest.js +1 -1
  214. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  215. package/.next/server/app/repositories/page.js +2 -2
  216. package/.next/server/app/repositories/page.js.nft.json +1 -1
  217. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  218. package/.next/server/app/review/page.js +1 -1
  219. package/.next/server/app/review/page.js.nft.json +1 -1
  220. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  221. package/.next/server/app/sessions/page.js +1 -1
  222. package/.next/server/app/sessions/page.js.nft.json +1 -1
  223. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  224. package/.next/server/app/skills/[skillId]/page.js +1 -1
  225. package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
  226. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  227. package/.next/server/app/skills/installed/page.js +1 -1
  228. package/.next/server/app/skills/installed/page.js.nft.json +1 -1
  229. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  230. package/.next/server/app/skills/page.js +1 -1
  231. package/.next/server/app/skills/page.js.nft.json +1 -1
  232. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  233. package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
  234. package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
  235. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  236. package/.next/server/app/worktrees/[id]/page.js +12 -9
  237. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  238. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  239. package/.next/server/app/worktrees/[id]/terminal/page.js +1 -1
  240. package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
  241. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  242. package/.next/server/app-paths-manifest.json +15 -15
  243. package/.next/server/chunks/1341.js +1 -1
  244. package/.next/server/chunks/1501.js +2 -2
  245. package/.next/server/chunks/1559.js +1 -1
  246. package/.next/server/chunks/2119.js +1 -1
  247. package/.next/server/chunks/2283.js +15 -13
  248. package/.next/server/chunks/2404.js +4 -4
  249. package/.next/server/chunks/2445.js +1 -1
  250. package/.next/server/chunks/2460.js +1 -1
  251. package/.next/server/chunks/2560.js +1 -1
  252. package/.next/server/chunks/2908.js +1 -1
  253. package/.next/server/chunks/2984.js +25 -0
  254. package/.next/server/chunks/3543.js +1 -1
  255. package/.next/server/chunks/3945.js +1 -1
  256. package/.next/server/chunks/4072.js +15 -0
  257. package/.next/server/chunks/4207.js +1 -1
  258. package/.next/server/chunks/4718.js +3 -3
  259. package/.next/server/chunks/4837.js +3 -3
  260. package/.next/server/chunks/5209.js +1 -1
  261. package/.next/server/chunks/5292.js +2 -2
  262. package/.next/server/chunks/5845.js +1 -1
  263. package/.next/server/chunks/6130.js +1 -1
  264. package/.next/server/chunks/6345.js +1 -0
  265. package/.next/server/chunks/6401.js +1 -1
  266. package/.next/server/chunks/6722.js +1 -0
  267. package/.next/server/chunks/8098.js +1 -1
  268. package/.next/server/chunks/8356.js +1 -1
  269. package/.next/server/chunks/8581.js +25 -29
  270. package/.next/server/chunks/8728.js +2 -2
  271. package/.next/server/chunks/8791.js +1 -1
  272. package/.next/server/chunks/8800.js +4 -4
  273. package/.next/server/chunks/8918.js +5 -5
  274. package/.next/server/chunks/9390.js +1 -1
  275. package/.next/server/chunks/9627.js +2 -1
  276. package/.next/server/chunks/9731.js +1 -1
  277. package/.next/server/middleware-build-manifest.js +1 -1
  278. package/.next/server/middleware-manifest.json +5 -5
  279. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  280. package/.next/server/pages/500.html +1 -1
  281. package/.next/server/pages/_error.js +2 -2
  282. package/.next/server/server-reference-manifest.json +1 -1
  283. package/.next/static/chunks/{2407-86890b00eeecf391.js → 2407-9703c569370b8a9d.js} +1 -1
  284. package/.next/static/chunks/{2619-a305ad4810f1393a.js → 2619-04bc32f026a0d946.js} +1 -1
  285. package/.next/static/chunks/2717.1583400c272dc1d9.js +1 -0
  286. package/.next/static/chunks/2770-db80aedf6384d594.js +1 -0
  287. package/.next/static/chunks/2863-6a07ec82fb52f4dd.js +1 -0
  288. package/.next/static/chunks/3201-c01f1feed69020e6.js +1 -0
  289. package/.next/static/chunks/4768-ad6f82f034f21eae.js +1 -0
  290. package/.next/static/chunks/5371-0dc947683e46e78c.js +1 -0
  291. package/.next/static/chunks/5794-4eee51ff1e878aa6.js +1 -0
  292. package/.next/static/chunks/5927-54315d21f323e204.js +1 -0
  293. package/.next/static/chunks/602-25e45a2422d2a3ea.js +1 -0
  294. package/.next/static/chunks/6606-02ddc006686a6b7d.js +1 -0
  295. package/.next/static/chunks/6654-f3534f35f16644bc.js +1 -0
  296. package/.next/static/chunks/7730-011e137be64403ae.js +1 -0
  297. package/.next/static/chunks/8165-263172c17ae8e8c3.js +1 -0
  298. package/.next/static/chunks/{8407-9a72e6f07c5d5458.js → 8407-155f84e5ba05f1d5.js} +1 -1
  299. package/.next/static/chunks/8606-cf6bdbff44a764cf.js +1 -0
  300. package/.next/static/chunks/934-a37509667baa86ee.js +1 -0
  301. package/.next/static/chunks/9465-e5a351baa941f31b.js +1 -0
  302. package/.next/static/chunks/app/layout-3e357690446cd2da.js +1 -0
  303. package/.next/static/chunks/app/repositories/page-71f5a5ac29b0370d.js +1 -0
  304. package/.next/static/chunks/app/review/page-e2fd4a590646a2d8.js +1 -0
  305. package/.next/static/chunks/app/sessions/page-9859ed61e93e6890.js +1 -0
  306. package/.next/static/chunks/app/skills/[skillId]/page-52ba58712faf3378.js +1 -0
  307. package/.next/static/chunks/app/worktrees/[id]/page-effbf0796881dd5a.js +1 -0
  308. package/.next/static/chunks/app/worktrees/[id]/terminal/{page-c601f3b3ece6557e.js → page-373ea37b16d930c7.js} +1 -1
  309. package/.next/static/chunks/{webpack-ee3ccbe378c4aa06.js → webpack-468b7b33030234b3.js} +1 -1
  310. package/.next/static/css/743f79ff19ab91e9.css +1 -0
  311. package/.next/trace +13 -13
  312. package/dist/cli/commands/wait.d.ts.map +1 -1
  313. package/dist/cli/commands/wait.js +6 -1
  314. package/dist/cli/types/api-responses.d.ts +8 -6
  315. package/dist/cli/types/api-responses.d.ts.map +1 -1
  316. package/dist/lib/polling/auto-yes-suppression-reason.d.ts +13 -1
  317. package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -1
  318. package/dist/lib/polling/auto-yes-suppression-reason.js +3 -0
  319. package/dist/server/locales/en/worktree.json +46 -16
  320. package/dist/server/locales/ja/worktree.json +46 -16
  321. package/dist/server/src/config/activity-bar-config.js +56 -9
  322. package/dist/server/src/lib/assistant-response-saver.js +13 -0
  323. package/dist/server/src/lib/auto-yes-poller.js +252 -54
  324. package/dist/server/src/lib/conversation-logger.js +14 -2
  325. package/dist/server/src/lib/db/chat-db.js +2 -1
  326. package/dist/server/src/lib/history/user-turn-recorder.js +14 -0
  327. package/dist/server/src/lib/hooks/sources/antigravity/history.js +28 -0
  328. package/dist/server/src/lib/hooks/sources/claude/history.js +7 -0
  329. package/dist/server/src/lib/hooks/sources/codex/history.js +7 -0
  330. package/dist/server/src/lib/hooks/sources/command-code/history.js +7 -0
  331. package/dist/server/src/lib/hooks/sources/transcript-history.js +34 -0
  332. package/dist/server/src/lib/log-manager.js +7 -2
  333. package/dist/server/src/lib/polling/auto-yes-enter-fallback-state.js +22 -3
  334. package/dist/server/src/lib/polling/auto-yes-poller-state.js +234 -0
  335. package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +3 -0
  336. package/dist/server/src/lib/polling/pending-scraped-response.js +119 -3
  337. package/dist/server/src/lib/polling/poller-owner.js +110 -0
  338. package/dist/server/src/lib/polling/prompt-dedup-state.js +26 -7
  339. package/dist/server/src/lib/polling/prompt-dedup.js +50 -22
  340. package/dist/server/src/lib/polling/response-checker.js +212 -36
  341. package/dist/server/src/lib/polling/response-dedup.js +112 -20
  342. package/dist/server/src/lib/polling/response-poller-core.js +178 -38
  343. package/dist/server/src/lib/polling/structured-history-gate.js +20 -1
  344. package/dist/server/src/lib/prompt-answer-sender.js +59 -12
  345. package/dist/server/src/lib/realtime/terminal-broadcast.js +14 -0
  346. package/dist/server/src/lib/session/chat-turn-progress.js +57 -6
  347. package/dist/server/src/lib/session/current-output-builder.js +19 -0
  348. package/dist/server/src/lib/session/next-action-helper.js +2 -15
  349. package/dist/server/src/lib/session/worktree-session-reconcile.js +83 -10
  350. package/dist/server/src/lib/timer-manager.js +7 -0
  351. package/dist/server/src/lib/tmux/tmux.js +24 -2
  352. package/dist/server/src/lib/verification/env-clean-gate.js +41 -0
  353. package/dist/server/src/lib/verification/gate-runner.js +17 -11
  354. package/dist/server/src/lib/verification/scope-gate.js +169 -3
  355. package/dist/server/src/types/sidebar.js +1 -0
  356. package/docs/en/architecture.md +26 -14
  357. package/docs/en/user-guide/webapp-guide.md +31 -18
  358. package/package.json +1 -1
  359. package/release-notes/0.46.0.json +110 -0
  360. package/release-notes/0.46.1.json +40 -0
  361. package/.next/server/chunks/1674.js +0 -15
  362. package/.next/server/chunks/5625.js +0 -25
  363. package/.next/server/chunks/6927.js +0 -1
  364. package/.next/server/chunks/9355.js +0 -1
  365. package/.next/static/chunks/1642-0f1bf8fb8161e10a.js +0 -1
  366. package/.next/static/chunks/2770-827a64760bab8a3a.js +0 -1
  367. package/.next/static/chunks/3922.0bc2e4e168d2b522.js +0 -1
  368. package/.next/static/chunks/4727-4c69d1aa4118cc09.js +0 -1
  369. package/.next/static/chunks/4768-fc0aa1d0891cac08.js +0 -1
  370. package/.next/static/chunks/5371-af1c2b68e983da61.js +0 -1
  371. package/.next/static/chunks/5521-c358c31f60e5e689.js +0 -1
  372. package/.next/static/chunks/6192-a67df090838b9aad.js +0 -1
  373. package/.next/static/chunks/6322-f1b30ed0ee43518f.js +0 -1
  374. package/.next/static/chunks/6654-ac431270fac1a283.js +0 -1
  375. package/.next/static/chunks/8165-3525fb42f1b4976e.js +0 -1
  376. package/.next/static/chunks/8318-01e7d1e5ad0a15a3.js +0 -1
  377. package/.next/static/chunks/8606-79c36d4c204bbd38.js +0 -1
  378. package/.next/static/chunks/9619-6c17a271f7f98fbb.js +0 -1
  379. package/.next/static/chunks/998-2eb5883186abd0bd.js +0 -1
  380. package/.next/static/chunks/app/layout-eaa6365f8f942e88.js +0 -1
  381. package/.next/static/chunks/app/repositories/page-6f7511f4ac8449e2.js +0 -1
  382. package/.next/static/chunks/app/review/page-95f749caae382f46.js +0 -1
  383. package/.next/static/chunks/app/sessions/page-ebfed2c80e916fb8.js +0 -1
  384. package/.next/static/chunks/app/skills/[skillId]/page-e4b14031f946c007.js +0 -1
  385. package/.next/static/chunks/app/worktrees/[id]/page-3372b78fde232b1f.js +0 -1
  386. package/.next/static/css/6faeb04a0b6819d4.css +0 -1
  387. /package/.next/static/{vYIpFRCU_Mt0wtG8F_3-6 → QxDf7lm-JJkWoj-TyWiSB}/_buildManifest.js +0 -0
  388. /package/.next/static/{vYIpFRCU_Mt0wtG8F_3-6 → QxDf7lm-JJkWoj-TyWiSB}/_ssgManifest.js +0 -0
@@ -49,6 +49,7 @@ const session_name_1 = require("../../lib/cli-tools/session-name");
49
49
  const geometry_delegation_1 = require("../../lib/tmux/geometry-delegation");
50
50
  const tmux_session_surface_1 = require("../../lib/session/tmux-session-surface");
51
51
  const response_poller_core_1 = require("./response-poller-core");
52
+ const poller_owner_1 = require("./poller-owner");
52
53
  const push_1 = require("../../lib/push");
53
54
  // Issue #1790: imported by deep path, not through `@/lib/push`. Suites that
54
55
  // replace the barrel to count notifications (e.g. the #1547 escalation test)
@@ -449,6 +450,48 @@ function markOrSettleStructuredHistoryRecheck(pollerKey, recorded) {
449
450
  (0, response_dedup_1.markStructuredHistoryRecheckPending)(pollerKey);
450
451
  }
451
452
  }
453
+ /**
454
+ * Whether this tick may still act for `pollerKey` after the `await` it has just
455
+ * resumed from, logging the refusal when it may not (Issue #3705).
456
+ *
457
+ * #3663, #3684 and #3701 asked {@link mayActForPollerKey} at the writes that had
458
+ * been reported, one at a time. A tick resumes from an `await` with no idea
459
+ * whether its chain was stopped, cut loose past `STALLED_TICK_TIMEOUT` or moved
460
+ * by a worktree-ID migration in the meantime, and every write after that
461
+ * `await` — the prompt sweep, the waiting edge, the completion push, the cursor,
462
+ * the fault notifier, the TUI accumulator, the recheck and skip-streak state —
463
+ * lands under a key it no longer owns, on whatever the successor has put there.
464
+ * So the question is asked after every `await` of the tick, named by the point
465
+ * it resumed from, and the writes below each point run only on a yes.
466
+ *
467
+ * Not asked where a self-stop hands back its verdict (`return await
468
+ * recordCompletedResponse(…)`): `runPollTick` settles the pause from that
469
+ * value, and a stop the tick raised itself must not lose what it recorded.
470
+ *
471
+ * @param ctx - What this tick is keyed by
472
+ * @param after - The `await` the tick has just resumed from, for the log line
473
+ */
474
+ function mayStillActAfter(ctx, after) {
475
+ if ((0, poller_owner_1.mayActForPollerKey)(ctx.pollerKey))
476
+ return true;
477
+ // One line per retired tick, at the first point that turned it away: a tick
478
+ // refused at the capture is asked again after the reader and the log.
479
+ const owner = (0, poller_owner_1.currentPollerOwner)();
480
+ if (owner === undefined || !refusalLogged.has(owner)) {
481
+ if (owner)
482
+ refusalLogged.add(owner);
483
+ logger.info('retired-tick-refused', {
484
+ worktreeId: ctx.worktreeId,
485
+ cliToolId: ctx.cliToolId,
486
+ instanceId: ctx.resolvedInstanceId,
487
+ generation: owner?.generation,
488
+ after,
489
+ });
490
+ }
491
+ return false;
492
+ }
493
+ /** The retired chains {@link mayStillActAfter} has already logged (Issue #3705). */
494
+ const refusalLogged = new WeakSet();
452
495
  /**
453
496
  * Read the frame this tick captured: is there a finished turn on it that the
454
497
  * cursor has not already counted?
@@ -469,17 +512,25 @@ function markOrSettleStructuredHistoryRecheck(pollerKey, recorded) {
469
512
  * @param ctx - What this tick is keyed by
470
513
  * @param output - The capture this tick made
471
514
  * @param lastCapturedLine - Its cursor, 0 when there is none
515
+ * @param owns - Whether the tick still owns its key (Issue #3705); false reads
516
+ * the frame and writes nothing
472
517
  * @returns What the tick goes on with, or `false` when the tick ends here
473
518
  */
474
- function extractCompletedTurn(ctx, output, lastCapturedLine) {
519
+ function extractCompletedTurn(ctx, output, lastCapturedLine, owns) {
475
520
  const { db, worktreeId, cliToolId, instanceId, resolvedInstanceId, pollerKey } = ctx;
476
521
  // Issue #2000: the frame is in hand, so this is the cheapest place to ask
477
522
  // whether the model API has stalled the session. Level in, edge out — see
478
523
  // the helper.
479
- observeUpstreamFaultForPush(worktreeId, cliToolId, instanceId, output);
524
+ //
525
+ // Issue #3705: every write in this function is the caller's `owns` — asked
526
+ // right after the capture, with no `await` since. A retired tick's frame is
527
+ // read, and none of it lands on the fault episode, the accumulator, the skip
528
+ // streaks or the pending prompts of the key it lost.
529
+ if (owns)
530
+ observeUpstreamFaultForPush(worktreeId, cliToolId, instanceId, output);
480
531
  // Layer 2: Accumulate TUI content for full-screen TUI tools, so a turn that
481
532
  // outgrows the alternate-screen pane keeps the head that has scrolled away.
482
- if (cliToolId === 'opencode' || cliToolId === 'copilot') {
533
+ if (owns && (cliToolId === 'opencode' || cliToolId === 'copilot')) {
483
534
  // Issue #1911: opencode is accumulated from the CURRENT TURN'S REGION
484
535
  // rather than the whole frame. Feeding the raw pane seeded the accumulator
485
536
  // with the previous turn's transcript, the echoed prompt and the bottom
@@ -498,13 +549,18 @@ function extractCompletedTurn(ctx, output, lastCapturedLine) {
498
549
  // skipped on the line count ("no new output"), which says nothing about
499
550
  // whether the prompt is still on screen, and neither do the cursor returns
500
551
  // further down.
501
- if (result)
552
+ //
553
+ // Issue #3552: the same frame ends a run of duplicate response ticks for
554
+ // the same reason — an unfinished frame is not the saved reply.
555
+ if (owns && result) {
502
556
  (0, prompt_dedup_1.resetDuplicatePromptSkipStreak)(pollerKey);
557
+ (0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
558
+ }
503
559
  // DR-004 windowing: Only check tail lines
504
560
  const { thinkingPattern } = (0, cli_patterns_1.getCliToolPatterns)(cliToolId);
505
561
  const cleanOutput = (0, cli_patterns_1.stripAnsi)(output);
506
562
  const tailLines = cleanOutput.split('\n').slice(-thinking_constants_1.THINKING_TAIL_LINE_COUNT).join('\n');
507
- if (thinkingPattern.test(tailLines)) {
563
+ if (owns && thinkingPattern.test(tailLines)) {
508
564
  // Intended twin of the sweep in recordCompletedResponse: this one runs when
509
565
  // working is visible on an unfinished frame, that one when a response is
510
566
  // recorded (Issue #31).
@@ -565,6 +621,21 @@ function extractCompletedTurn(ctx, output, lastCapturedLine) {
565
621
  */
566
622
  function savePromptMessage(ctx, promptDetection, result, isFullScreenTui) {
567
623
  const { db, worktree, worktreeId, cliToolId, instanceId, resolvedInstanceId, pollerKey } = ctx;
624
+ // Issue #3701: the question #3663 / #3684 ask, for the prompt a tick retired
625
+ // during the capture's `await` then finds on the pane — no row, broadcast,
626
+ // task event, push or waiting episode under the key it lost. Asked BEFORE the
627
+ // dedup below, which registers the hash before the save: a refused tick that
628
+ // registered it would make the successor drop the same prompt as a
629
+ // duplicate. No `await` from here to the end of this function.
630
+ if (!(0, poller_owner_1.mayActForPollerKey)(pollerKey)) {
631
+ logger.info('prompt-save-refused', {
632
+ worktreeId,
633
+ cliToolId,
634
+ instanceId: resolvedInstanceId,
635
+ generation: (0, poller_owner_1.currentPollerOwner)()?.generation,
636
+ });
637
+ return false;
638
+ }
568
639
  // Issue #565: Content hash-based duplicate prompt prevention
569
640
  const promptContent = promptDetection.rawContent || promptDetection.cleanContent;
570
641
  const normalizedForDedup = (0, prompt_dedup_1.normalizePromptForDedup)(promptContent, cliToolId);
@@ -681,9 +752,10 @@ function savePromptMessage(ctx, promptDetection, result, isFullScreenTui) {
681
752
  * @param cliToolId - CLI tool identifier
682
753
  * @param result - The complete extraction this tick made
683
754
  * @param pollerKey - Poller key ("worktreeId:instanceId")
755
+ * @param owns - Whether the tick still owns its key (Issue #3705)
684
756
  * @returns The cleaned response
685
757
  */
686
- function cleanCompletedResponse(cliToolId, result, pollerKey) {
758
+ function cleanCompletedResponse(cliToolId, result, pollerKey, owns) {
687
759
  // Clean up responses
688
760
  let cleanedResponse = result.response;
689
761
  if (cliToolId === 'gemini') {
@@ -697,7 +769,10 @@ function cleanCompletedResponse(cliToolId, result, pollerKey) {
697
769
  const sourceContent = accumulatedContent || result.response;
698
770
  cleanedResponse = (0, response_cleaner_1.cleanCopilotResponse)(sourceContent);
699
771
  cleanedResponse = (0, response_cleaner_1.truncateMessage)(cleanedResponse, copilot_constants_1.COPILOT_MAX_MESSAGE_LENGTH, copilot_constants_1.COPILOT_TRUNCATION_MARKER);
700
- (0, tui_accumulator_1.clearTuiAccumulator)(pollerKey);
772
+ // Issue #3705: read, but not cleared, by a tick that lost the key — the
773
+ // successor may already be accumulating its own turn under it.
774
+ if (owns)
775
+ (0, tui_accumulator_1.clearTuiAccumulator)(pollerKey);
701
776
  }
702
777
  else if (cliToolId === 'opencode') {
703
778
  // Issue #1911 defect 3: opencode wrote to the Layer-2 accumulator but never
@@ -719,7 +794,8 @@ function cleanCompletedResponse(cliToolId, result, pollerKey) {
719
794
  ? accumulatedContent
720
795
  : result.response;
721
796
  cleanedResponse = (0, response_cleaner_1.cleanOpenCodeResponse)(sourceContent);
722
- (0, tui_accumulator_1.clearTuiAccumulator)(pollerKey);
797
+ if (owns)
798
+ (0, tui_accumulator_1.clearTuiAccumulator)(pollerKey);
723
799
  }
724
800
  return cleanedResponse;
725
801
  }
@@ -739,6 +815,15 @@ function cleanCompletedResponse(cliToolId, result, pollerKey) {
739
815
  */
740
816
  async function recheckDuplicateResponse(ctx, result, claudeMetadata) {
741
817
  const { db, worktree, worktreeId, cliToolId, instanceId, resolvedInstanceId, pollerKey } = ctx;
818
+ // Issue #3705 (review): a retired tick reaches this function too. The dedup
819
+ // answers "duplicate" on a hash match BEFORE it asks about ownership (it only
820
+ // refuses to REGISTER a hash), and the hash it matches may be the successor's
821
+ // — the same reply, saved by the chain that replaced this one while this
822
+ // tick's capture was in flight. Everything below writes under the key (the
823
+ // skip-log claim, the cursor, the recheck claim, then the reader), so the
824
+ // question is asked first, with no `await` between it and those writes.
825
+ if (!mayStillActAfter(ctx, 'capture'))
826
+ return false;
742
827
  // Issue #1695: this branch used to drop the response silently — the
743
828
  // prompt-side guard above has logged its skip since #565, this one
744
829
  // logged nothing at all, so a reply that never reached History left no
@@ -816,6 +901,11 @@ async function recheckDuplicateResponse(ctx, result, claudeMetadata) {
816
901
  worktreePath: worktree.path,
817
902
  transcriptPathHint: claudeMetadata?.logFilePath ?? null,
818
903
  });
904
+ // Issue #3705: the recheck the successor is owed is not settled, and its
905
+ // hold not dropped, by a tick retired while the reader ran. What the reader
906
+ // answered is still the answer (it asks the same question before it writes).
907
+ if (!mayStillActAfter(ctx, 'recheck-reader'))
908
+ return recaptured;
819
909
  if (recaptured) {
820
910
  (0, response_dedup_1.settleStructuredHistoryRecheck)(pollerKey);
821
911
  // Issue #2436: the turn is now the agent's own Markdown, so a
@@ -863,7 +953,10 @@ function recordOrHoldScrapedResponse(ctx, cleanedResponse, claudeMetadata, decis
863
953
  // width and gutter-prefixed, so no content check could ever recognise them
864
954
  // as the same reply.
865
955
  if (holdScrapedHistory) {
866
- (0, pending_scraped_response_1.holdScrapedResponse)(pollerKey, {
956
+ // Issue #3663: refused (false) to a tick whose chain was retired while it
957
+ // awaited the reader — a stop, a stalled-tick restart or a worktree-ID
958
+ // migration. The store logs that, so this tick does not claim a hold.
959
+ const held = (0, pending_scraped_response_1.holdScrapedResponse)(pollerKey, {
867
960
  worktreeId,
868
961
  cliToolId,
869
962
  instanceId: resolvedInstanceId,
@@ -879,19 +972,37 @@ function recordOrHoldScrapedResponse(ctx, cleanedResponse, claudeMetadata, decis
879
972
  transcriptPathHint: claudeMetadata?.logFilePath ?? null,
880
973
  expiresAt: Date.now() + pending_scraped_response_1.PENDING_SCRAPE_HOLD_MS,
881
974
  });
882
- logger.info('structured-history-scrape-held', {
975
+ if (held) {
976
+ logger.info('structured-history-scrape-held', {
977
+ worktreeId,
978
+ cliToolId,
979
+ instanceId: resolvedInstanceId,
980
+ scrapedLength: cleanedResponse.length,
981
+ holdMs: pending_scraped_response_1.PENDING_SCRAPE_HOLD_MS,
982
+ });
983
+ // The completion edge is announced HERE and not at the flush, because the
984
+ // turn finished now. A relay waiting on this session gets #2401's grace
985
+ // window to find a turn-keyed row, which is exactly the row the hold is
986
+ // waiting for; delaying the announcement by the hold would delay every
987
+ // delivery by it too. Issue #3684: only for a hold that was taken — a
988
+ // refused one is a retired tick, which announces nothing under the key
989
+ // it lost, as in the immediate-save branch below.
990
+ (0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, false);
991
+ }
992
+ }
993
+ else if (!suppressScrapedHistory && !(0, poller_owner_1.mayActForPollerKey)(pollerKey)) {
994
+ // Issue #3684: the same question the hold asks (#3663), for the reply saved
995
+ // at once — a tick retired while it awaited the reader or the log writes no
996
+ // row, broadcast or relay edge under the key it lost. No `await` between
997
+ // this check and the three below. Issue #3705: the caller now asks the
998
+ // same question for the rest of the tick's writes and the Markdown log for
999
+ // its own, so a retired tick reaches this branch only to be refused here.
1000
+ logger.info('scraped-response-save-refused', {
883
1001
  worktreeId,
884
1002
  cliToolId,
885
1003
  instanceId: resolvedInstanceId,
886
- scrapedLength: cleanedResponse.length,
887
- holdMs: pending_scraped_response_1.PENDING_SCRAPE_HOLD_MS,
1004
+ generation: (0, poller_owner_1.currentPollerOwner)()?.generation,
888
1005
  });
889
- // The completion edge is announced HERE and not at the flush, because the
890
- // turn finished now. A relay waiting on this session gets #2401's grace
891
- // window to find a turn-keyed row, which is exactly the row the hold is
892
- // waiting for; delaying the announcement by the hold would delay every
893
- // delivery by it too.
894
- (0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, false);
895
1006
  }
896
1007
  else if (!suppressScrapedHistory) {
897
1008
  // Create new CLI tool message in database
@@ -994,7 +1105,13 @@ async function recordCompletedResponse(ctx, turn, claudeMetadata, cleanedRespons
994
1105
  // a return — so unless the fact is written down now, the ask never happens
995
1106
  // again. A `true` settles it: the turn is recorded and there is nothing left
996
1107
  // to re-ask about.
997
- markOrSettleStructuredHistoryRecheck(pollerKey, structuredHistoryLive);
1108
+ //
1109
+ // Issue #3705: by a tick that still owns the key. A retired one goes on to
1110
+ // the scrape decision below only so that the store's and the save branch's
1111
+ // own refusals (#3663, #3684) speak for it; nothing else it would write does.
1112
+ if (mayStillActAfter(ctx, 'reader')) {
1113
+ markOrSettleStructuredHistoryRecheck(pollerKey, structuredHistoryLive);
1114
+ }
998
1115
  // Issue #2317 Phase D: the scrape is dropped while the geometry is
999
1116
  // delegated, and the transcript capture above is what makes that safe.
1000
1117
  //
@@ -1025,8 +1142,29 @@ async function recordCompletedResponse(ctx, turn, claudeMetadata, cleanedRespons
1025
1142
  // out. Only the two writes that RECORD THE REPLY wait.
1026
1143
  const holdScrapedHistory = !suppressScrapedHistory && captureReport.outcome === 'not_yet_closed';
1027
1144
  // Create Markdown log file for the conversation pair
1145
+ //
1146
+ // Issue #3705: keyed, so the log asks before its own write (after its own
1147
+ // awaits) whether this tick still owns the key. A held reply confirmed by a
1148
+ // stop is logged by the store with no key (`pending-scraped-response`), and
1149
+ // that log is kept.
1028
1150
  if (cleanedResponse && !suppressScrapedHistory && !holdScrapedHistory) {
1029
- await (0, conversation_logger_1.recordClaudeConversation)(db, worktreeId, cleanedResponse, cliToolId);
1151
+ await (0, conversation_logger_1.recordClaudeConversation)(db, worktreeId, cleanedResponse, cliToolId, { pollerKey });
1152
+ }
1153
+ // Issue #3705: everything from here to the end of the function runs without
1154
+ // an `await`, so this is the tick's last chance to find out that it was
1155
+ // retired while the reader or the log ran. A retired tick sweeps no prompt to
1156
+ // "answered", closes no waiting episode, sends no completion push and moves
1157
+ // no cursor under the key it lost — the successor's, possibly. The scrape
1158
+ // decision still runs: every branch of it asks the same question itself
1159
+ // (#3663 the hold, #3684 the immediate save, the store the discard), and its
1160
+ // refusal lines are what the earlier suites read.
1161
+ if (!mayStillActAfter(ctx, 'conversation-log')) {
1162
+ recordOrHoldScrapedResponse(ctx, cleanedResponse, claudeMetadata, {
1163
+ structuredHistoryLive,
1164
+ suppressScrapedHistory,
1165
+ holdScrapedHistory,
1166
+ });
1167
+ return false;
1030
1168
  }
1031
1169
  // Mark any pending prompts as answered. Intended twin of the sweep in
1032
1170
  // extractCompletedTurn (working visible on an unfinished frame); this one runs
@@ -1098,8 +1236,23 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1098
1236
  (0, response_poller_core_1.stopPolling)(worktreeId, cliToolId, instanceId);
1099
1237
  return false;
1100
1238
  }
1239
+ const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
1240
+ // Issue #3213: what the steps split out of this function are keyed by.
1241
+ const ctx = {
1242
+ db,
1243
+ worktree,
1244
+ worktreeId,
1245
+ cliToolId,
1246
+ instanceId,
1247
+ resolvedInstanceId,
1248
+ pollerKey,
1249
+ };
1101
1250
  // Check if CLI tool session is running
1102
1251
  const running = await (0, cli_session_1.isSessionRunning)(worktreeId, cliToolId, instanceId);
1252
+ // Issue #3705: the first of the tick's awaits. Every one of them is followed
1253
+ // by the same question; see `mayStillActAfter`.
1254
+ if (!mayStillActAfter(ctx, 'session-check'))
1255
+ return false;
1103
1256
  if (!running) {
1104
1257
  logger.info('session-not-running');
1105
1258
  // `stopPolling` is what confirms a held scrape here; see
@@ -1111,23 +1264,14 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1111
1264
  (0, response_poller_core_1.stopPolling)(worktreeId, cliToolId, instanceId);
1112
1265
  return false;
1113
1266
  }
1114
- const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
1115
- // Issue #3213: what the steps split out of this function are keyed by.
1116
- const ctx = {
1117
- db,
1118
- worktree,
1119
- worktreeId,
1120
- cliToolId,
1121
- instanceId,
1122
- resolvedInstanceId,
1123
- pollerKey,
1124
- };
1125
1267
  // Issue #2436: a scrape held by an earlier tick, whose transcript has now
1126
1268
  // had its whole budget to close. Ahead of everything below because most
1127
1269
  // ticks of a finished turn never reach the save path — a static frame is a
1128
1270
  // duplicate for an alternate-screen tool and yields no new lines for a
1129
1271
  // scrollback one, and both of those return early.
1130
1272
  await (0, pending_scraped_response_1.settleExpiredPendingScrapedResponse)(pollerKey);
1273
+ if (!mayStillActAfter(ctx, 'pending-scrape-settle'))
1274
+ return false;
1131
1275
  // Issue #2317 Phase D: is a human reading this pane at their own terminal
1132
1276
  // size right now, and did they just stop?
1133
1277
  //
@@ -1139,6 +1283,8 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1139
1283
  const delegation = (0, tmux_session_surface_1.isLiveAttachEligibleSession)(sessionName)
1140
1284
  ? await (0, geometry_delegation_1.probeGeometryDelegation)(sessionName)
1141
1285
  : { delegated: false, released: false };
1286
+ if (!mayStillActAfter(ctx, 'geometry-probe'))
1287
+ return false;
1142
1288
  if (delegation.released) {
1143
1289
  // The canvas is 1000 rows again, so a cursor recorded against a 44-row
1144
1290
  // frame indexes into a pane that no longer exists. Zero is the only value
@@ -1165,7 +1311,16 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1165
1311
  // taken from the same constant extractResponse() measures saturation against
1166
1312
  // (Issue #1670) — a literal here would silently decouple the two.
1167
1313
  const output = await (0, cli_session_1.captureSessionOutput)(worktreeId, cliToolId, tmux_capture_cache_1.CACHE_MAX_CAPTURE_LINES, instanceId);
1168
- const turn = extractCompletedTurn(ctx, output, lastCapturedLine);
1314
+ // Issue #3705: there is no `await` from here to the transcript reader
1315
+ // inside the save path, so this one answer covers every write in between —
1316
+ // the fault notifier, the TUI accumulator, the prompt sweep, the skip
1317
+ // streaks, the cursor, the in-progress id. A retired tick still READS the
1318
+ // frame and goes on, because the writes that already ask for themselves
1319
+ // (the prompt row #3701, the response hash #3663, the reader and the
1320
+ // scrape decision) are where its refusal is recorded; it writes none of
1321
+ // the rest.
1322
+ const owns = mayStillActAfter(ctx, 'capture');
1323
+ const turn = extractCompletedTurn(ctx, output, lastCapturedLine, owns);
1169
1324
  if (!turn) {
1170
1325
  return false;
1171
1326
  }
@@ -1189,24 +1344,41 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1189
1344
  // has lost the position `detectDialog` judges by.
1190
1345
  const promptIsLive = promptDetection.isPrompt && (0, response_checker_dialog_gate_1.isNumberedDialogVouched)(cliToolId, promptDetection, output);
1191
1346
  if (promptIsLive) {
1347
+ // Issue #3552: a live prompt is not the saved reply, so a run of
1348
+ // duplicate response ticks is over; the response hash is kept (a resume
1349
+ // after the answer must not re-save the reply underneath, #2230).
1350
+ if (owns)
1351
+ (0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
1352
+ // Asks for itself (#3701), before the prompt dedup.
1192
1353
  return savePromptMessage(ctx, promptDetection, result, isFullScreenTui);
1193
1354
  }
1194
1355
  // Issue #3538: a finished frame without a live prompt ends the run of
1195
1356
  // duplicate prompt ticks, as an unfinished one does in `extractCompletedTurn`.
1196
- (0, prompt_dedup_1.resetDuplicatePromptSkipStreak)(pollerKey);
1357
+ if (owns)
1358
+ (0, prompt_dedup_1.resetDuplicatePromptSkipStreak)(pollerKey);
1197
1359
  // Validate response content is not empty
1198
1360
  if (!result.response || result.response.trim() === '') {
1199
- (0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
1361
+ // Issue #3552: an empty response is not the saved reply either.
1362
+ if (owns) {
1363
+ (0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
1364
+ (0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
1365
+ }
1200
1366
  return false;
1201
1367
  }
1202
1368
  // Parse Claude-specific metadata
1203
1369
  const claudeMetadata = cliToolId === 'claude'
1204
1370
  ? (0, claude_output_1.parseClaudeOutput)(result.response)
1205
1371
  : undefined;
1206
- const cleanedResponse = cleanCompletedResponse(cliToolId, result, pollerKey);
1372
+ const cleanedResponse = cleanCompletedResponse(cliToolId, result, pollerKey, owns);
1207
1373
  // If cleaned response is empty or just "[No content]", skip saving
1208
1374
  if (!cleanedResponse || cleanedResponse.trim() === '' || cleanedResponse === '[No content]') {
1209
- (0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
1375
+ // Issue #3552: nor is one that cleans down to nothing.
1376
+ if (owns) {
1377
+ (0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
1378
+ (0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
1379
+ }
1380
+ // Left as it was (Issue #3705 sets it aside as harmless): nothing sets the
1381
+ // in-progress id to a non-null value or reads it today.
1210
1382
  (0, db_1.clearInProgressMessageId)(db, worktreeId, cliToolId, resolvedInstanceId);
1211
1383
  return false;
1212
1384
  }
@@ -1221,6 +1393,10 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1221
1393
  // Without this the disabled cursor would leave nothing suppressing re-saves and
1222
1394
  // the poller would append the same finished reply every 2 s.
1223
1395
  if (!lineCountIsCursor) {
1396
+ // A retired tick registers no hash here (#3663, `./response-dedup`), but
1397
+ // it can still MATCH one — the successor's, for the same reply. The
1398
+ // recheck below therefore opens on the ownership question itself before
1399
+ // its claim, its cursor and its read (Issue #3705 review).
1224
1400
  if ((0, response_dedup_1.isDuplicateResponse)(pollerKey, cleanedResponse)) {
1225
1401
  // `await`, so that a rejection still lands in the catch below.
1226
1402
  return await recheckDuplicateResponse(ctx, result, claudeMetadata);
@@ -55,13 +55,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
55
55
  exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL = exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = void 0;
56
56
  exports.isDuplicateResponse = isDuplicateResponse;
57
57
  exports.claimDuplicateResponseSkipLog = claimDuplicateResponseSkipLog;
58
+ exports.resetDuplicateResponseSkipStreak = resetDuplicateResponseSkipStreak;
58
59
  exports.markStructuredHistoryRecheckPending = markStructuredHistoryRecheckPending;
59
60
  exports.settleStructuredHistoryRecheck = settleStructuredHistoryRecheck;
60
61
  exports.claimStructuredHistoryRecheck = claimStructuredHistoryRecheck;
61
62
  exports.clearResponseHashCache = clearResponseHashCache;
62
63
  exports.renameResponseHashCacheKey = renameResponseHashCacheKey;
64
+ exports.renameResponseHashCacheKeys = renameResponseHashCacheKeys;
63
65
  const crypto_1 = require("crypto");
64
66
  const global_state_1 = require("../global-state");
67
+ const poller_owner_1 = require("./poller-owner");
65
68
  const responseHashCache = (0, global_state_1.getOrInitGlobal)('__responseHashCache', () => new Map());
66
69
  /**
67
70
  * In-memory cache: pollerKey -> ticks still to wait before the transcript reader
@@ -97,7 +100,9 @@ exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = 3;
97
100
  *
98
101
  * On `globalThis` for the same reason `responseHashCache` is, and cleared and
99
102
  * moved at exactly the same moments — see the module comment. A new
100
- * (non-duplicate) response restarts it in {@link isDuplicateResponse}.
103
+ * (non-duplicate) response restarts it in {@link isDuplicateResponse}, and a
104
+ * tick that reads the frame and finds no reply on it ends the run without
105
+ * touching the hash — see {@link resetDuplicateResponseSkipStreak} (#3552).
101
106
  */
102
107
  const duplicateResponseSkipStreak = (0, global_state_1.getOrInitGlobal)('__duplicateResponseSkipStreak', () => new Map());
103
108
  /**
@@ -123,6 +128,13 @@ function isDuplicateResponse(pollerKey, content) {
123
128
  if (responseHashCache.get(pollerKey) === hash) {
124
129
  return true;
125
130
  }
131
+ // Issue #3663: a tick of a stopped chain no longer owns its key, so it does
132
+ // not register a hash there — the same line the held scrape draws. Left
133
+ // behind, the hash of a reply that tick never saves (its hold is refused)
134
+ // would be inherited by the next chain on the key, or moved by a migration,
135
+ // and make the successor skip it.
136
+ if (!(0, poller_owner_1.mayActForPollerKey)(pollerKey))
137
+ return false;
126
138
  responseHashCache.set(pollerKey, hash);
127
139
  // Issue #3519: a new response starts a new run of duplicate ticks.
128
140
  duplicateResponseSkipStreak.delete(pollerKey);
@@ -148,6 +160,28 @@ function claimDuplicateResponseSkipLog(pollerKey) {
148
160
  const suppressed = consecutive === 1 ? 0 : exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL - 1;
149
161
  return { log, consecutive, suppressed };
150
162
  }
163
+ /**
164
+ * End the current run of duplicate response ticks for this pollerKey without
165
+ * touching the cached hash (Issue #3552).
166
+ *
167
+ * Called by `response-checker` on the ticks that read the frame and decide it
168
+ * is not a duplicate of the saved reply: an unfinished frame
169
+ * (`extractCompletedTurn`), a finished frame with a live prompt on it, and a
170
+ * finished frame whose response is empty. The reply is no longer on screen, so
171
+ * if the same reply comes back the next duplicate is the first of a new run and
172
+ * logs. The hash stays: whether the returning reply is saved again is the
173
+ * guard's question (#1268), and this only thins the log line (#3519).
174
+ *
175
+ * Not called at the entrance of a finished frame — that would make every
176
+ * duplicate tick the first of its run — nor on a tick that skipped extraction
177
+ * on the line count (null) or returned on the cursor: neither says the reply
178
+ * has left the screen (the same judgement as #3538).
179
+ *
180
+ * @param pollerKey - Poller key ("worktreeId:instanceId")
181
+ */
182
+ function resetDuplicateResponseSkipStreak(pollerKey) {
183
+ duplicateResponseSkipStreak.delete(pollerKey);
184
+ }
151
185
  /**
152
186
  * Note that the transcript reader has unfinished business for this pollerKey
153
187
  * (Issue #2399).
@@ -233,6 +267,55 @@ function clearResponseHashCache(pollerKey) {
233
267
  // Issue #3519: the skip count is about the same hash.
234
268
  duplicateResponseSkipStreak.delete(pollerKey);
235
269
  }
270
+ /** Remove everything held under `key` and return it. */
271
+ function takeResponseDedupState(key) {
272
+ const state = {
273
+ hash: responseHashCache.get(key),
274
+ recheck: structuredHistoryRecheckCache.get(key),
275
+ streak: duplicateResponseSkipStreak.get(key),
276
+ };
277
+ responseHashCache.delete(key);
278
+ structuredHistoryRecheckCache.delete(key);
279
+ duplicateResponseSkipStreak.delete(key);
280
+ return state;
281
+ }
282
+ /**
283
+ * Write state lifted by {@link takeResponseDedupState} under `key`.
284
+ *
285
+ * `replaceRecheckWithHash` is the one difference between the batch and the
286
+ * single rename (Issue #3553): when a hash lands, the batch also replaces the
287
+ * recheck under `key` — deleting it when the source had none — so a recheck
288
+ * owed for a different turn is not attached to the hash that just arrived. The
289
+ * single rename keeps its earlier behaviour and leaves such a recheck alone.
290
+ */
291
+ function putResponseDedupState(key, state, replaceRecheckWithHash) {
292
+ if (state.hash !== undefined)
293
+ responseHashCache.set(key, state.hash);
294
+ // Issue #2399: the recheck caveat moves with the hash it is about. Leaving it
295
+ // behind would strand a turn whose transcript is still open — the new key
296
+ // starts owing nothing, so every later tick returns at the dedup guard and the
297
+ // Markdown row is never written. It moves without a hash too: the
298
+ // `lineCountIsCursor` path marks it with no hash cached.
299
+ if (state.recheck !== undefined) {
300
+ structuredHistoryRecheckCache.set(key, state.recheck);
301
+ }
302
+ else if (replaceRecheckWithHash && state.hash !== undefined) {
303
+ structuredHistoryRecheckCache.delete(key);
304
+ }
305
+ // Issue #3519: the skip count moves with the hash too, so a rename mid-run
306
+ // does not restart the thinning and log as if a new run had begun.
307
+ // Issue #3552: whenever the hash under `newKey` is replaced, so is its count
308
+ // — a count left over from the hash that was there before is about a
309
+ // different reply (the same rule as `renamePromptHashCacheKey`, #3538).
310
+ if (state.hash === undefined)
311
+ return;
312
+ if (state.streak !== undefined) {
313
+ duplicateResponseSkipStreak.set(key, state.streak);
314
+ }
315
+ else {
316
+ duplicateResponseSkipStreak.delete(key);
317
+ }
318
+ }
236
319
  /**
237
320
  * Move the cached hash from one pollerKey to another.
238
321
  *
@@ -241,7 +324,8 @@ function clearResponseHashCache(pollerKey) {
241
324
  * during THIS turn?", and the turn does not end just because the ID moved —
242
325
  * dropping it would re-save the screen currently on display.
243
326
  *
244
- * No-op when nothing is cached under `oldKey`.
327
+ * No-op when nothing is cached under `oldKey`. A batch of renames whose keys
328
+ * overlap must go through {@link renameResponseHashCacheKeys} instead.
245
329
  *
246
330
  * @param oldKey - Poller key the cache entry lives under
247
331
  * @param newKey - Poller key it should live under
@@ -249,22 +333,30 @@ function clearResponseHashCache(pollerKey) {
249
333
  function renameResponseHashCacheKey(oldKey, newKey) {
250
334
  if (oldKey === newKey)
251
335
  return;
252
- const hash = responseHashCache.get(oldKey);
253
- responseHashCache.delete(oldKey);
254
- if (hash !== undefined)
255
- responseHashCache.set(newKey, hash);
256
- // Issue #2399: the recheck caveat moves with the hash it is about. Leaving it
257
- // behind would strand a turn whose transcript is still open — the new key
258
- // starts owing nothing, so every later tick returns at the dedup guard and the
259
- // Markdown row is never written.
260
- const ticksToWait = structuredHistoryRecheckCache.get(oldKey);
261
- structuredHistoryRecheckCache.delete(oldKey);
262
- if (ticksToWait !== undefined)
263
- structuredHistoryRecheckCache.set(newKey, ticksToWait);
264
- // Issue #3519: the skip count moves with the hash too, so a rename mid-run
265
- // does not restart the thinning and log as if a new run had begun.
266
- const streak = duplicateResponseSkipStreak.get(oldKey);
267
- duplicateResponseSkipStreak.delete(oldKey);
268
- if (streak !== undefined)
269
- duplicateResponseSkipStreak.set(newKey, streak);
336
+ putResponseDedupState(newKey, takeResponseDedupState(oldKey), false);
337
+ }
338
+ /**
339
+ * Move the response dedup state of a whole batch of pollerKeys (Issue #3553).
340
+ *
341
+ * Every source key is lifted before any destination is written, so a swap
342
+ * (A→B, B→A) or a chain (A→B→C) cannot overwrite a source that has not moved
343
+ * yet — calling {@link renameResponseHashCacheKey} once per pair would leave
344
+ * A with its own state and B empty after a swap. Each destination ends up with
345
+ * exactly what its source held: hash, skip count and recheck, the recheck
346
+ * moving even without a hash. A recheck already at the destination is
347
+ * replaced along with the hash, never kept beside the moved one.
348
+ *
349
+ * Pairs with `oldKey === newKey` are ignored.
350
+ *
351
+ * @param renames - Poller key moves applied together
352
+ */
353
+ function renameResponseHashCacheKeys(renames) {
354
+ const lifted = [];
355
+ for (const { oldKey, newKey } of renames) {
356
+ if (oldKey === newKey)
357
+ continue;
358
+ lifted.push({ newKey, state: takeResponseDedupState(oldKey) });
359
+ }
360
+ for (const { newKey, state } of lifted)
361
+ putResponseDedupState(newKey, state, true);
270
362
  }