commandmate 0.45.0 → 0.46.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 (322) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +333 -328
  3. package/.next/app-path-routes-manifest.json +24 -24
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/react-loadable-manifest.json +6 -6
  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_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  58. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  64. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +3 -3
  70. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +3 -3
  76. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  104. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  105. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +3 -3
  121. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/schedules/active/route.js +3 -3
  123. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/schedules/route.js +3 -3
  125. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  153. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/route.js +1 -1
  155. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/login/page.js +1 -1
  157. package/.next/server/app/login/page.js.nft.json +1 -1
  158. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  159. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  160. package/.next/server/app/more/page.js +1 -1
  161. package/.next/server/app/more/page.js.nft.json +1 -1
  162. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  163. package/.next/server/app/offline/page.js +1 -1
  164. package/.next/server/app/offline/page.js.nft.json +1 -1
  165. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  166. package/.next/server/app/page.js +1 -1
  167. package/.next/server/app/page.js.nft.json +1 -1
  168. package/.next/server/app/page_client-reference-manifest.js +1 -1
  169. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  170. package/.next/server/app/repositories/page.js +2 -2
  171. package/.next/server/app/repositories/page.js.nft.json +1 -1
  172. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  173. package/.next/server/app/review/page.js +1 -1
  174. package/.next/server/app/review/page.js.nft.json +1 -1
  175. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  176. package/.next/server/app/sessions/page.js +1 -1
  177. package/.next/server/app/sessions/page.js.nft.json +1 -1
  178. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  179. package/.next/server/app/skills/[skillId]/page.js +2 -2
  180. package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
  181. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  182. package/.next/server/app/skills/installed/page.js +1 -1
  183. package/.next/server/app/skills/installed/page.js.nft.json +1 -1
  184. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  185. package/.next/server/app/skills/page.js +1 -1
  186. package/.next/server/app/skills/page.js.nft.json +1 -1
  187. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  188. package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
  189. package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
  190. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  191. package/.next/server/app/worktrees/[id]/page.js +12 -9
  192. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  193. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  194. package/.next/server/app/worktrees/[id]/terminal/page.js +1 -1
  195. package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
  196. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  197. package/.next/server/app-paths-manifest.json +24 -24
  198. package/.next/server/chunks/1341.js +1 -1
  199. package/.next/server/chunks/1501.js +2 -2
  200. package/.next/server/chunks/2119.js +1 -1
  201. package/.next/server/chunks/2283.js +15 -13
  202. package/.next/server/chunks/2335.js +1 -0
  203. package/.next/server/chunks/2404.js +4 -4
  204. package/.next/server/chunks/2445.js +1 -1
  205. package/.next/server/chunks/2460.js +1 -1
  206. package/.next/server/chunks/2560.js +1 -1
  207. package/.next/server/chunks/3543.js +1 -1
  208. package/.next/server/chunks/3945.js +1 -1
  209. package/.next/server/chunks/4072.js +15 -0
  210. package/.next/server/chunks/4207.js +1 -1
  211. package/.next/server/chunks/{4802.js → 4243.js} +1 -1
  212. package/.next/server/chunks/4718.js +3 -3
  213. package/.next/server/chunks/4837.js +3 -3
  214. package/.next/server/chunks/5209.js +1 -1
  215. package/.next/server/chunks/5625.js +2 -2
  216. package/.next/server/chunks/6130.js +1 -1
  217. package/.next/server/chunks/6345.js +1 -0
  218. package/.next/server/chunks/6401.js +1 -1
  219. package/.next/server/chunks/6722.js +1 -0
  220. package/.next/server/chunks/8098.js +1 -1
  221. package/.next/server/chunks/8356.js +1 -1
  222. package/.next/server/chunks/8581.js +25 -29
  223. package/.next/server/chunks/8728.js +1 -1
  224. package/.next/server/chunks/8800.js +4 -4
  225. package/.next/server/chunks/9731.js +1 -1
  226. package/.next/server/chunks/9862.js +1 -0
  227. package/.next/server/middleware-build-manifest.js +1 -1
  228. package/.next/server/middleware-manifest.json +5 -5
  229. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  230. package/.next/server/pages/500.html +1 -1
  231. package/.next/server/pages/_error.js +2 -2
  232. package/.next/server/server-reference-manifest.json +1 -1
  233. package/.next/static/chunks/{7485.8a2a9d7c5396aa26.js → 1111.5ab4f0ffb0d944e5.js} +2 -2
  234. package/.next/static/chunks/{2407-dd4a0f04f1349221.js → 2407-acb644c5487e1720.js} +1 -1
  235. package/.next/static/chunks/{2619-a305ad4810f1393a.js → 2619-04bc32f026a0d946.js} +1 -1
  236. package/.next/static/chunks/2717.21abf5b6bbd45520.js +1 -0
  237. package/.next/static/chunks/2770-db80aedf6384d594.js +1 -0
  238. package/.next/static/chunks/2863-6a07ec82fb52f4dd.js +1 -0
  239. package/.next/static/chunks/3201-c01f1feed69020e6.js +1 -0
  240. package/.next/static/chunks/3662-5a27ee39a3ecba49.js +1 -0
  241. package/.next/static/chunks/4541-b0ce0bf829ae79b7.js +1 -0
  242. package/.next/static/chunks/4768-ad6f82f034f21eae.js +1 -0
  243. package/.next/static/chunks/5371-a94ed618148287a0.js +1 -0
  244. package/.next/static/chunks/5794-947fb4934d8be193.js +1 -0
  245. package/.next/static/chunks/5927-54315d21f323e204.js +1 -0
  246. package/.next/static/chunks/602-25e45a2422d2a3ea.js +1 -0
  247. package/.next/static/chunks/6288-09feba367cc2c65e.js +1 -0
  248. package/.next/static/chunks/6606-02ddc006686a6b7d.js +1 -0
  249. package/.next/static/chunks/6654-f3534f35f16644bc.js +1 -0
  250. package/.next/static/chunks/7730-011e137be64403ae.js +1 -0
  251. package/.next/static/chunks/8165-263172c17ae8e8c3.js +1 -0
  252. package/.next/static/chunks/8606-cf6bdbff44a764cf.js +1 -0
  253. package/.next/static/chunks/934-a37509667baa86ee.js +1 -0
  254. package/.next/static/chunks/9465-e5a351baa941f31b.js +1 -0
  255. package/.next/static/chunks/953.31c52a251deda22d.js +1 -0
  256. package/.next/static/chunks/app/layout-ae30ce63569c22be.js +1 -0
  257. package/.next/static/chunks/app/repositories/page-71f5a5ac29b0370d.js +1 -0
  258. package/.next/static/chunks/app/review/page-e2fd4a590646a2d8.js +1 -0
  259. package/.next/static/chunks/app/sessions/page-9859ed61e93e6890.js +1 -0
  260. package/.next/static/chunks/app/skills/[skillId]/page-52ba58712faf3378.js +1 -0
  261. package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9cf1ecc59e36a004.js +1 -0
  262. package/.next/static/chunks/app/worktrees/[id]/page-2eb1d0fd0a077b7e.js +1 -0
  263. package/.next/static/chunks/app/worktrees/[id]/terminal/{page-c601f3b3ece6557e.js → page-373ea37b16d930c7.js} +1 -1
  264. package/.next/static/chunks/{webpack-165d8df9acd1c5d4.js → webpack-c0b821f340afdebf.js} +1 -1
  265. package/.next/static/css/743f79ff19ab91e9.css +1 -0
  266. package/.next/trace +13 -13
  267. package/dist/server/locales/en/worktree.json +48 -17
  268. package/dist/server/locales/ja/worktree.json +48 -17
  269. package/dist/server/src/config/activity-bar-config.js +56 -9
  270. package/dist/server/src/lib/auto-yes-poller.js +16 -163
  271. package/dist/server/src/lib/polling/auto-yes-enter-fallback-state.js +1 -1
  272. package/dist/server/src/lib/polling/auto-yes-poller-state.js +52 -0
  273. package/dist/server/src/lib/polling/auto-yes-prompt-suppression.js +125 -0
  274. package/dist/server/src/lib/polling/prompt-dedup-state.js +26 -7
  275. package/dist/server/src/lib/polling/prompt-dedup.js +125 -6
  276. package/dist/server/src/lib/polling/response-checker.js +53 -2
  277. package/dist/server/src/lib/polling/response-dedup.js +159 -14
  278. package/dist/server/src/lib/polling/response-poller-core.js +47 -26
  279. package/dist/server/src/lib/session/next-action-helper.js +2 -15
  280. package/dist/server/src/lib/verification/env-clean-gate.js +14 -1
  281. package/dist/server/src/lib/verification/gate-runner.js +17 -11
  282. package/dist/server/src/lib/verification/scope-gate.js +169 -3
  283. package/dist/server/src/types/sidebar.js +1 -0
  284. package/docs/en/architecture.md +26 -14
  285. package/docs/en/user-guide/webapp-guide.md +31 -18
  286. package/package.json +1 -1
  287. package/release-notes/0.45.1.json +38 -0
  288. package/release-notes/0.46.0.json +110 -0
  289. package/.next/server/chunks/1674.js +0 -15
  290. package/.next/server/chunks/364.js +0 -1
  291. package/.next/server/chunks/488.js +0 -1
  292. package/.next/server/chunks/5326.js +0 -1
  293. package/.next/server/chunks/6927.js +0 -1
  294. package/.next/static/chunks/1642-767aed8e7c61ef7f.js +0 -1
  295. package/.next/static/chunks/2053-390e77fce43b555c.js +0 -1
  296. package/.next/static/chunks/2770-827a64760bab8a3a.js +0 -1
  297. package/.next/static/chunks/3922.f45dcfc1e7da552a.js +0 -1
  298. package/.next/static/chunks/4727-5d695d71e3fdf97b.js +0 -1
  299. package/.next/static/chunks/4768-fc0aa1d0891cac08.js +0 -1
  300. package/.next/static/chunks/5521-c358c31f60e5e689.js +0 -1
  301. package/.next/static/chunks/6192-a67df090838b9aad.js +0 -1
  302. package/.next/static/chunks/6322-f1b30ed0ee43518f.js +0 -1
  303. package/.next/static/chunks/6654-ac431270fac1a283.js +0 -1
  304. package/.next/static/chunks/8165-3525fb42f1b4976e.js +0 -1
  305. package/.next/static/chunks/8283-26ad31a4670ea52d.js +0 -1
  306. package/.next/static/chunks/8318-01e7d1e5ad0a15a3.js +0 -1
  307. package/.next/static/chunks/8566-aeda747542072ace.js +0 -1
  308. package/.next/static/chunks/8606-79c36d4c204bbd38.js +0 -1
  309. package/.next/static/chunks/8811-29eaa24237f48558.js +0 -1
  310. package/.next/static/chunks/953.19e4c8178c9eac4c.js +0 -1
  311. package/.next/static/chunks/9619-6c17a271f7f98fbb.js +0 -1
  312. package/.next/static/chunks/998-2eb5883186abd0bd.js +0 -1
  313. package/.next/static/chunks/app/layout-07d040cb3d3ea657.js +0 -1
  314. package/.next/static/chunks/app/repositories/page-6f7511f4ac8449e2.js +0 -1
  315. package/.next/static/chunks/app/review/page-95f749caae382f46.js +0 -1
  316. package/.next/static/chunks/app/sessions/page-2af68c63211c751f.js +0 -1
  317. package/.next/static/chunks/app/skills/[skillId]/page-9ee7a381636e60cb.js +0 -1
  318. package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-5445205c8dd80109.js +0 -1
  319. package/.next/static/chunks/app/worktrees/[id]/page-35f0e31a725d1c89.js +0 -1
  320. package/.next/static/css/057f64a65726c439.css +0 -1
  321. /package/.next/static/{TPWbZB3zBp3Zv_eFEOlmt → zI2nQ6WKboSbFbMF4EtrJ}/_buildManifest.js +0 -0
  322. /package/.next/static/{TPWbZB3zBp3Zv_eFEOlmt → zI2nQ6WKboSbFbMF4EtrJ}/_ssgManifest.js +0 -0
@@ -6,15 +6,54 @@
6
6
  * Uses SHA-256 content hash with an in-memory cache (Map<pollerKey, hash>).
7
7
  * Design decision [DR1-006]: In-memory only (no DB layer).
8
8
  * Process restart may cause a single duplicate, which is acceptable (KISS).
9
+ *
10
+ * ## How often the skip says so (Issue #3538)
11
+ *
12
+ * A full-screen TUI (copilot, opencode) keeps polling while it sits on a
13
+ * prompt, so the same prompt is a duplicate on every 2 s tick until it is
14
+ * answered, and `duplicate-prompt-skipped` used to be logged on each of them.
15
+ * The run of duplicate ticks is counted here, the same way `response-dedup`
16
+ * counts `duplicate-response-skipped` (#3519): the line is written on the first
17
+ * tick of a run and then once per {@link DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL}
18
+ * ticks. The count is about one particular cached hash, so it never outlives
19
+ * it — the functions below that drop or move the hash drop or move the count
20
+ * too. The one exception runs the other way: a tick that reads a frame with no
21
+ * live prompt on it ends the run without touching the hash
22
+ * ({@link resetDuplicatePromptSkipStreak}), because the prompt left the screen
23
+ * while the guard still has to keep it from being saved twice. A returning
24
+ * prompt is then still suppressed, but its first duplicate logs as the first
25
+ * of a new run.
26
+ *
27
+ * Only the log line is thinned. `recordPromptDedupSkip` (#1695) is still called
28
+ * on every duplicate tick; its tally is what `capture --json` reports.
9
29
  */
10
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL = void 0;
11
32
  exports.isDuplicatePrompt = isDuplicatePrompt;
33
+ exports.claimDuplicatePromptSkipLog = claimDuplicatePromptSkipLog;
34
+ exports.resetDuplicatePromptSkipStreak = resetDuplicatePromptSkipStreak;
12
35
  exports.clearPromptHashCache = clearPromptHashCache;
13
36
  exports.renamePromptHashCacheKey = renamePromptHashCacheKey;
37
+ exports.renamePromptHashCacheKeys = renamePromptHashCacheKeys;
14
38
  exports.normalizePromptForDedup = normalizePromptForDedup;
15
39
  const crypto_1 = require("crypto");
16
40
  const global_state_1 = require("../global-state");
41
+ const response_dedup_1 = require("./response-dedup");
17
42
  const promptHashCache = (0, global_state_1.getOrInitGlobal)('__promptHashCache', () => new Map());
43
+ /**
44
+ * In-memory cache: pollerKey -> how many duplicate ticks in a row the current
45
+ * cached prompt hash has been skipped (Issue #3538). Absence means none yet.
46
+ *
47
+ * On `globalThis` for the same reason `promptHashCache` is, and cleared and
48
+ * moved at exactly the same moments — see the module comment.
49
+ */
50
+ const duplicatePromptSkipStreak = (0, global_state_1.getOrInitGlobal)('__duplicatePromptSkipStreak', () => new Map());
51
+ /**
52
+ * How many duplicate ticks separate two `duplicate-prompt-skipped` lines
53
+ * (Issue #3538). Shared with `duplicate-response-skipped` (#3519): one line a
54
+ * minute at the poller's 2 s tick.
55
+ */
56
+ exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL = response_dedup_1.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL;
18
57
  /**
19
58
  * Check if the given prompt content is a duplicate of the last saved prompt
20
59
  * for the same pollerKey. If not a duplicate, updates the cache.
@@ -29,8 +68,50 @@ function isDuplicatePrompt(pollerKey, content) {
29
68
  return true;
30
69
  }
31
70
  promptHashCache.set(pollerKey, hash);
71
+ // Issue #3538: a new prompt starts a new run of duplicate ticks.
72
+ duplicatePromptSkipStreak.delete(pollerKey);
32
73
  return false;
33
74
  }
75
+ /**
76
+ * Count one duplicate prompt tick for this pollerKey and say whether it should
77
+ * be logged (Issue #3538).
78
+ *
79
+ * A claim: it advances the count, so call it exactly once per duplicate tick.
80
+ * Logs on the first tick of a run and then on every
81
+ * {@link DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL}th tick after it (ticks 1,
82
+ * 1+N, 1+2N, …).
83
+ *
84
+ * @param pollerKey - Poller key ("worktreeId:cliToolId")
85
+ * @returns Whether to log, the run length so far, and the ticks left unlogged
86
+ * since the previous line
87
+ */
88
+ function claimDuplicatePromptSkipLog(pollerKey) {
89
+ const consecutive = (duplicatePromptSkipStreak.get(pollerKey) ?? 0) + 1;
90
+ duplicatePromptSkipStreak.set(pollerKey, consecutive);
91
+ const log = (consecutive - 1) % exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL === 0;
92
+ const suppressed = consecutive === 1 ? 0 : exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL - 1;
93
+ return { log, consecutive, suppressed };
94
+ }
95
+ /**
96
+ * End the current run of duplicate prompt ticks for this pollerKey without
97
+ * touching the cached hash (Issue #3538).
98
+ *
99
+ * Called by `response-checker` in two places: `extractCompletedTurn`, when the
100
+ * frame was read and is unfinished (a working screen), and `checkForResponse`,
101
+ * when a finished frame carries no live prompt (a reply). The prompt is no
102
+ * longer on screen, so if the same prompt comes back the next duplicate is the
103
+ * first of a new run and logs. Not called on a tick that skipped extraction on
104
+ * the line count (a null result) or returned on the cursor, nor on a tick that
105
+ * ended before extraction: none of them says the prompt has left the screen,
106
+ * so the run carries on through them. The hash stays: whether the
107
+ * returning prompt is saved again is the guard's question (#565), and this
108
+ * Issue only thins the log line.
109
+ *
110
+ * @param pollerKey - Poller key ("worktreeId:cliToolId")
111
+ */
112
+ function resetDuplicatePromptSkipStreak(pollerKey) {
113
+ duplicatePromptSkipStreak.delete(pollerKey);
114
+ }
34
115
  /**
35
116
  * Clear the prompt hash cache for a specific pollerKey.
36
117
  * Called during session cleanup / stopPolling.
@@ -39,6 +120,8 @@ function isDuplicatePrompt(pollerKey, content) {
39
120
  */
40
121
  function clearPromptHashCache(pollerKey) {
41
122
  promptHashCache.delete(pollerKey);
123
+ // Issue #3538: the skip count is about the same hash.
124
+ duplicatePromptSkipStreak.delete(pollerKey);
42
125
  }
43
126
  /**
44
127
  * Move the cached hash from one pollerKey to another.
@@ -48,18 +131,54 @@ function clearPromptHashCache(pollerKey) {
48
131
  * re-save the prompt it is currently sitting on as a fresh message, which is
49
132
  * the duplicate this module exists to prevent.
50
133
  *
51
- * No-op when nothing is cached under `oldKey`.
134
+ * No-op when nothing is cached under `oldKey`. A batch of renames whose keys
135
+ * overlap must go through {@link renamePromptHashCacheKeys} instead.
52
136
  *
53
137
  * @param oldKey - Poller key the cache entry lives under
54
138
  * @param newKey - Poller key it should live under
55
139
  */
56
140
  function renamePromptHashCacheKey(oldKey, newKey) {
57
- if (oldKey === newKey)
58
- return;
59
- const hash = promptHashCache.get(oldKey);
60
- promptHashCache.delete(oldKey);
61
- if (hash !== undefined)
141
+ renamePromptHashCacheKeys([{ oldKey, newKey }]);
142
+ }
143
+ /**
144
+ * Move the prompt hash and its skip count for a whole batch of pollerKeys
145
+ * (Issue #3553).
146
+ *
147
+ * Every source key is lifted before any destination is written, so a swap
148
+ * (A→B, B→A) or a chain (A→B→C) cannot overwrite a source that has not moved
149
+ * yet. Per pair this is exactly {@link renamePromptHashCacheKey}: nothing
150
+ * happens at the destination when the source held no hash.
151
+ *
152
+ * @param renames - Poller key moves applied together; `oldKey === newKey` is ignored
153
+ */
154
+ function renamePromptHashCacheKeys(renames) {
155
+ const lifted = [];
156
+ for (const { oldKey, newKey } of renames) {
157
+ if (oldKey === newKey)
158
+ continue;
159
+ lifted.push({
160
+ newKey,
161
+ hash: promptHashCache.get(oldKey),
162
+ streak: duplicatePromptSkipStreak.get(oldKey),
163
+ });
164
+ promptHashCache.delete(oldKey);
165
+ duplicatePromptSkipStreak.delete(oldKey);
166
+ }
167
+ for (const { newKey, hash, streak } of lifted) {
168
+ if (hash === undefined)
169
+ continue;
62
170
  promptHashCache.set(newKey, hash);
171
+ // Issue #3538: the skip count moves with the hash, so a rename mid-run does
172
+ // not restart the thinning and log as if a new run had begun. Whenever the
173
+ // hash under `newKey` is replaced, so is its count — a count left over from
174
+ // the hash that was there before is about a different prompt.
175
+ if (streak !== undefined) {
176
+ duplicatePromptSkipStreak.set(newKey, streak);
177
+ }
178
+ else {
179
+ duplicatePromptSkipStreak.delete(newKey);
180
+ }
181
+ }
63
182
  }
64
183
  /**
65
184
  * Normalize prompt content before deduplication check.
@@ -491,6 +491,20 @@ function extractCompletedTurn(ctx, output, lastCapturedLine) {
491
491
  // Extract response
492
492
  const result = extractResponse(output, lastCapturedLine, cliToolId, tmux_capture_cache_1.CACHE_MAX_CAPTURE_LINES);
493
493
  if (!result || !result.isComplete) {
494
+ // Issue #3538: an unfinished frame was read and carries no live prompt (the
495
+ // early prompt check in `extractResponse` would have returned it complete),
496
+ // so a run of duplicate prompt ticks is over; the hash is kept — see the
497
+ // helper. Only when the frame WAS read: a null result is extraction being
498
+ // skipped on the line count ("no new output"), which says nothing about
499
+ // whether the prompt is still on screen, and neither do the cursor returns
500
+ // further down.
501
+ //
502
+ // Issue #3552: the same frame ends a run of duplicate response ticks for
503
+ // the same reason — an unfinished frame is not the saved reply.
504
+ if (result) {
505
+ (0, prompt_dedup_1.resetDuplicatePromptSkipStreak)(pollerKey);
506
+ (0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
507
+ }
494
508
  // DR-004 windowing: Only check tail lines
495
509
  const { thinkingPattern } = (0, cli_patterns_1.getCliToolPatterns)(cliToolId);
496
510
  const cleanOutput = (0, cli_patterns_1.stripAnsi)(output);
@@ -565,7 +579,19 @@ function savePromptMessage(ctx, promptDetection, result, isFullScreenTui) {
565
579
  // classified (#1676) look identical from the CLI — both say "nothing was
566
580
  // recorded". Count the skip so the payload can tell them apart.
567
581
  (0, prompt_dedup_state_1.recordPromptDedupSkip)(worktreeId, cliToolId, instanceId);
568
- logger.info('duplicate-prompt-skipped', { worktreeId, cliToolId });
582
+ // Issue #3538: the tally above is counted on every tick; the log line is
583
+ // not. A full-screen TUI sits on its prompt for as long as it stays
584
+ // unanswered, a duplicate every 2 s. The first tick of a run logs exactly
585
+ // as before; after that one line per `DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL`
586
+ // ticks, with the run length and how many ticks went unlogged (#3519).
587
+ const skipLog = (0, prompt_dedup_1.claimDuplicatePromptSkipLog)(pollerKey);
588
+ if (skipLog.log) {
589
+ logger.info('duplicate-prompt-skipped', {
590
+ worktreeId,
591
+ cliToolId,
592
+ ...(skipLog.consecutive > 1 ? { consecutive: skipLog.consecutive, suppressed: skipLog.suppressed } : {}),
593
+ });
594
+ }
569
595
  return false;
570
596
  }
571
597
  // Issue #571: Clean TUI decorations from Copilot prompt content before saving
@@ -723,7 +749,21 @@ async function recheckDuplicateResponse(ctx, result, claudeMetadata) {
723
749
  // logged nothing at all, so a reply that never reached History left no
724
750
  // trace anywhere. Same action name shape as its sibling so both skips
725
751
  // are found by one grep.
726
- logger.info('duplicate-response-skipped', { worktreeId, cliToolId, instanceId: resolvedInstanceId });
752
+ //
753
+ // Issue #3519: but not on every tick. A finished screen that stays up is a
754
+ // duplicate on each of the cycle's 900 ticks, and logging all of them was
755
+ // 51,933 lines a day. The first tick of a run logs exactly as before; after
756
+ // that one line per `DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL` ticks, with
757
+ // the run length and how many ticks went unlogged since the last line.
758
+ const skipLog = (0, response_dedup_1.claimDuplicateResponseSkipLog)(pollerKey);
759
+ if (skipLog.log) {
760
+ logger.info('duplicate-response-skipped', {
761
+ worktreeId,
762
+ cliToolId,
763
+ instanceId: resolvedInstanceId,
764
+ ...(skipLog.consecutive > 1 ? { consecutive: skipLog.consecutive, suppressed: skipLog.suppressed } : {}),
765
+ });
766
+ }
727
767
  (0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
728
768
  // Issue #2399: the skip above is about the SCREEN, and until this Issue
729
769
  // it also ended the tick for the TRANSCRIPT READER 100 lines below —
@@ -1154,10 +1194,19 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1154
1194
  // has lost the position `detectDialog` judges by.
1155
1195
  const promptIsLive = promptDetection.isPrompt && (0, response_checker_dialog_gate_1.isNumberedDialogVouched)(cliToolId, promptDetection, output);
1156
1196
  if (promptIsLive) {
1197
+ // Issue #3552: a live prompt is not the saved reply, so a run of
1198
+ // duplicate response ticks is over; the response hash is kept (a resume
1199
+ // after the answer must not re-save the reply underneath, #2230).
1200
+ (0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
1157
1201
  return savePromptMessage(ctx, promptDetection, result, isFullScreenTui);
1158
1202
  }
1203
+ // Issue #3538: a finished frame without a live prompt ends the run of
1204
+ // duplicate prompt ticks, as an unfinished one does in `extractCompletedTurn`.
1205
+ (0, prompt_dedup_1.resetDuplicatePromptSkipStreak)(pollerKey);
1159
1206
  // Validate response content is not empty
1160
1207
  if (!result.response || result.response.trim() === '') {
1208
+ // Issue #3552: an empty response is not the saved reply either.
1209
+ (0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
1161
1210
  (0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
1162
1211
  return false;
1163
1212
  }
@@ -1168,6 +1217,8 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1168
1217
  const cleanedResponse = cleanCompletedResponse(cliToolId, result, pollerKey);
1169
1218
  // If cleaned response is empty or just "[No content]", skip saving
1170
1219
  if (!cleanedResponse || cleanedResponse.trim() === '' || cleanedResponse === '[No content]') {
1220
+ // Issue #3552: nor is one that cleans down to nothing.
1221
+ (0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
1171
1222
  (0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
1172
1223
  (0, db_1.clearInProgressMessageId)(db, worktreeId, cliToolId, resolvedInstanceId);
1173
1224
  return false;
@@ -39,15 +39,29 @@
39
39
  * hash, and the worktree rename all have to treat the two the same, and the
40
40
  * only way to guarantee that without touching `response-poller-core` (which
41
41
  * owns those three calls) is for one function to clear both.
42
+ *
43
+ * ## The third cache: how often the skip says so (#3519)
44
+ *
45
+ * The skip logs `duplicate-response-skipped` so a reply missing from History
46
+ * leaves a grep-able trace (#1695). Logged on every tick, a screen that stays
47
+ * up writes that line every 2 s for the whole cycle — 51,933 lines in 24 h,
48
+ * 80% of the server log. The run of duplicate ticks is counted here so the line
49
+ * is written on the first tick of a run and then once per
50
+ * {@link DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL} ticks. The count is about
51
+ * one particular cached hash, so it lives and dies with it for the same reason
52
+ * the recheck does.
42
53
  */
43
54
  Object.defineProperty(exports, "__esModule", { value: true });
44
- exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = void 0;
55
+ exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL = exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = void 0;
45
56
  exports.isDuplicateResponse = isDuplicateResponse;
57
+ exports.claimDuplicateResponseSkipLog = claimDuplicateResponseSkipLog;
58
+ exports.resetDuplicateResponseSkipStreak = resetDuplicateResponseSkipStreak;
46
59
  exports.markStructuredHistoryRecheckPending = markStructuredHistoryRecheckPending;
47
60
  exports.settleStructuredHistoryRecheck = settleStructuredHistoryRecheck;
48
61
  exports.claimStructuredHistoryRecheck = claimStructuredHistoryRecheck;
49
62
  exports.clearResponseHashCache = clearResponseHashCache;
50
63
  exports.renameResponseHashCacheKey = renameResponseHashCacheKey;
64
+ exports.renameResponseHashCacheKeys = renameResponseHashCacheKeys;
51
65
  const crypto_1 = require("crypto");
52
66
  const global_state_1 = require("../global-state");
53
67
  const responseHashCache = (0, global_state_1.getOrInitGlobal)('__responseHashCache', () => new Map());
@@ -79,6 +93,27 @@ const structuredHistoryRecheckCache = (0, global_state_1.getOrInitGlobal)('__str
79
93
  * or the cycle ends, the entry is gone.
80
94
  */
81
95
  exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = 3;
96
+ /**
97
+ * In-memory cache: pollerKey -> how many duplicate ticks in a row the current
98
+ * cached hash has been skipped (Issue #3519). Absence means none yet.
99
+ *
100
+ * On `globalThis` for the same reason `responseHashCache` is, and cleared and
101
+ * moved at exactly the same moments — see the module comment. A new
102
+ * (non-duplicate) response restarts it in {@link isDuplicateResponse}, and a
103
+ * tick that reads the frame and finds no reply on it ends the run without
104
+ * touching the hash — see {@link resetDuplicateResponseSkipStreak} (#3552).
105
+ */
106
+ const duplicateResponseSkipStreak = (0, global_state_1.getOrInitGlobal)('__duplicateResponseSkipStreak', () => new Map());
107
+ /**
108
+ * How many duplicate ticks separate two `duplicate-response-skipped` lines
109
+ * (Issue #3519).
110
+ *
111
+ * At the poller's 2 s tick this is one line a minute while a finished screen
112
+ * stays up, so a 900-tick cycle writes 30 lines instead of 899. The first tick
113
+ * of a run always logs, so the #1695 trace — "this reply was skipped, at this
114
+ * time" — is still there for every run; what is dropped is the repetition.
115
+ */
116
+ exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL = 30;
82
117
  /**
83
118
  * Check whether the given response content was already saved during the current
84
119
  * polling cycle for the same pollerKey. If it is new, updates the cache.
@@ -93,8 +128,52 @@ function isDuplicateResponse(pollerKey, content) {
93
128
  return true;
94
129
  }
95
130
  responseHashCache.set(pollerKey, hash);
131
+ // Issue #3519: a new response starts a new run of duplicate ticks.
132
+ duplicateResponseSkipStreak.delete(pollerKey);
96
133
  return false;
97
134
  }
135
+ /**
136
+ * Count one duplicate tick for this pollerKey and say whether it should be
137
+ * logged (Issue #3519).
138
+ *
139
+ * A claim, like {@link claimStructuredHistoryRecheck}: it advances the count, so
140
+ * call it exactly once per duplicate tick. Logs on the first tick of a run and
141
+ * then on every {@link DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL}th tick after
142
+ * it (ticks 1, 1+N, 1+2N, …).
143
+ *
144
+ * @param pollerKey - Poller key ("worktreeId:instanceId")
145
+ * @returns Whether to log, the run length so far, and the ticks left unlogged
146
+ * since the previous line
147
+ */
148
+ function claimDuplicateResponseSkipLog(pollerKey) {
149
+ const consecutive = (duplicateResponseSkipStreak.get(pollerKey) ?? 0) + 1;
150
+ duplicateResponseSkipStreak.set(pollerKey, consecutive);
151
+ const log = (consecutive - 1) % exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL === 0;
152
+ const suppressed = consecutive === 1 ? 0 : exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL - 1;
153
+ return { log, consecutive, suppressed };
154
+ }
155
+ /**
156
+ * End the current run of duplicate response ticks for this pollerKey without
157
+ * touching the cached hash (Issue #3552).
158
+ *
159
+ * Called by `response-checker` on the ticks that read the frame and decide it
160
+ * is not a duplicate of the saved reply: an unfinished frame
161
+ * (`extractCompletedTurn`), a finished frame with a live prompt on it, and a
162
+ * finished frame whose response is empty. The reply is no longer on screen, so
163
+ * if the same reply comes back the next duplicate is the first of a new run and
164
+ * logs. The hash stays: whether the returning reply is saved again is the
165
+ * guard's question (#1268), and this only thins the log line (#3519).
166
+ *
167
+ * Not called at the entrance of a finished frame — that would make every
168
+ * duplicate tick the first of its run — nor on a tick that skipped extraction
169
+ * on the line count (null) or returned on the cursor: neither says the reply
170
+ * has left the screen (the same judgement as #3538).
171
+ *
172
+ * @param pollerKey - Poller key ("worktreeId:instanceId")
173
+ */
174
+ function resetDuplicateResponseSkipStreak(pollerKey) {
175
+ duplicateResponseSkipStreak.delete(pollerKey);
176
+ }
98
177
  /**
99
178
  * Note that the transcript reader has unfinished business for this pollerKey
100
179
  * (Issue #2399).
@@ -177,6 +256,57 @@ function claimStructuredHistoryRecheck(pollerKey) {
177
256
  function clearResponseHashCache(pollerKey) {
178
257
  responseHashCache.delete(pollerKey);
179
258
  structuredHistoryRecheckCache.delete(pollerKey);
259
+ // Issue #3519: the skip count is about the same hash.
260
+ duplicateResponseSkipStreak.delete(pollerKey);
261
+ }
262
+ /** Remove everything held under `key` and return it. */
263
+ function takeResponseDedupState(key) {
264
+ const state = {
265
+ hash: responseHashCache.get(key),
266
+ recheck: structuredHistoryRecheckCache.get(key),
267
+ streak: duplicateResponseSkipStreak.get(key),
268
+ };
269
+ responseHashCache.delete(key);
270
+ structuredHistoryRecheckCache.delete(key);
271
+ duplicateResponseSkipStreak.delete(key);
272
+ return state;
273
+ }
274
+ /**
275
+ * Write state lifted by {@link takeResponseDedupState} under `key`.
276
+ *
277
+ * `replaceRecheckWithHash` is the one difference between the batch and the
278
+ * single rename (Issue #3553): when a hash lands, the batch also replaces the
279
+ * recheck under `key` — deleting it when the source had none — so a recheck
280
+ * owed for a different turn is not attached to the hash that just arrived. The
281
+ * single rename keeps its earlier behaviour and leaves such a recheck alone.
282
+ */
283
+ function putResponseDedupState(key, state, replaceRecheckWithHash) {
284
+ if (state.hash !== undefined)
285
+ responseHashCache.set(key, state.hash);
286
+ // Issue #2399: the recheck caveat moves with the hash it is about. Leaving it
287
+ // behind would strand a turn whose transcript is still open — the new key
288
+ // starts owing nothing, so every later tick returns at the dedup guard and the
289
+ // Markdown row is never written. It moves without a hash too: the
290
+ // `lineCountIsCursor` path marks it with no hash cached.
291
+ if (state.recheck !== undefined) {
292
+ structuredHistoryRecheckCache.set(key, state.recheck);
293
+ }
294
+ else if (replaceRecheckWithHash && state.hash !== undefined) {
295
+ structuredHistoryRecheckCache.delete(key);
296
+ }
297
+ // Issue #3519: the skip count moves with the hash too, so a rename mid-run
298
+ // does not restart the thinning and log as if a new run had begun.
299
+ // Issue #3552: whenever the hash under `newKey` is replaced, so is its count
300
+ // — a count left over from the hash that was there before is about a
301
+ // different reply (the same rule as `renamePromptHashCacheKey`, #3538).
302
+ if (state.hash === undefined)
303
+ return;
304
+ if (state.streak !== undefined) {
305
+ duplicateResponseSkipStreak.set(key, state.streak);
306
+ }
307
+ else {
308
+ duplicateResponseSkipStreak.delete(key);
309
+ }
180
310
  }
181
311
  /**
182
312
  * Move the cached hash from one pollerKey to another.
@@ -186,7 +316,8 @@ function clearResponseHashCache(pollerKey) {
186
316
  * during THIS turn?", and the turn does not end just because the ID moved —
187
317
  * dropping it would re-save the screen currently on display.
188
318
  *
189
- * No-op when nothing is cached under `oldKey`.
319
+ * No-op when nothing is cached under `oldKey`. A batch of renames whose keys
320
+ * overlap must go through {@link renameResponseHashCacheKeys} instead.
190
321
  *
191
322
  * @param oldKey - Poller key the cache entry lives under
192
323
  * @param newKey - Poller key it should live under
@@ -194,16 +325,30 @@ function clearResponseHashCache(pollerKey) {
194
325
  function renameResponseHashCacheKey(oldKey, newKey) {
195
326
  if (oldKey === newKey)
196
327
  return;
197
- const hash = responseHashCache.get(oldKey);
198
- responseHashCache.delete(oldKey);
199
- if (hash !== undefined)
200
- responseHashCache.set(newKey, hash);
201
- // Issue #2399: the recheck caveat moves with the hash it is about. Leaving it
202
- // behind would strand a turn whose transcript is still open — the new key
203
- // starts owing nothing, so every later tick returns at the dedup guard and the
204
- // Markdown row is never written.
205
- const ticksToWait = structuredHistoryRecheckCache.get(oldKey);
206
- structuredHistoryRecheckCache.delete(oldKey);
207
- if (ticksToWait !== undefined)
208
- structuredHistoryRecheckCache.set(newKey, ticksToWait);
328
+ putResponseDedupState(newKey, takeResponseDedupState(oldKey), false);
329
+ }
330
+ /**
331
+ * Move the response dedup state of a whole batch of pollerKeys (Issue #3553).
332
+ *
333
+ * Every source key is lifted before any destination is written, so a swap
334
+ * (A→B, B→A) or a chain (A→B→C) cannot overwrite a source that has not moved
335
+ * yet — calling {@link renameResponseHashCacheKey} once per pair would leave
336
+ * A with its own state and B empty after a swap. Each destination ends up with
337
+ * exactly what its source held: hash, skip count and recheck, the recheck
338
+ * moving even without a hash. A recheck already at the destination is
339
+ * replaced along with the hash, never kept beside the moved one.
340
+ *
341
+ * Pairs with `oldKey === newKey` are ignored.
342
+ *
343
+ * @param renames - Poller key moves applied together
344
+ */
345
+ function renameResponseHashCacheKeys(renames) {
346
+ const lifted = [];
347
+ for (const { oldKey, newKey } of renames) {
348
+ if (oldKey === newKey)
349
+ continue;
350
+ lifted.push({ newKey, state: takeResponseDedupState(oldKey) });
351
+ }
352
+ for (const { newKey, state } of lifted)
353
+ putResponseDedupState(newKey, state, true);
209
354
  }
@@ -532,8 +532,11 @@ function splitPollerKey(pollerKey) {
532
532
  * one in-flight TUI response may be truncated at the rename boundary; the next
533
533
  * turn accumulates normally.
534
534
  *
535
- * Takes the whole batch because IDs can swap (A→B, B→A): every source key is
536
- * removed before any destination key is written.
535
+ * Takes the whole batch because IDs can swap (A→B, B→A) or chain (A→B→C):
536
+ * every source key is removed before any destination key is written. That
537
+ * includes the dedup state (Issue #3553) — of running pollers and paused chains
538
+ * alike — which is lifted off every source key in one pass and only then
539
+ * written, so a pair handled early cannot overwrite a source handled later.
537
540
  *
538
541
  * @param renames - Worktree ID pairs being applied
539
542
  * @param resolveCliToolId - Maps a poller's (worktree, instance) back to its CLI
@@ -596,33 +599,21 @@ function migrateResponsePollerWorktreeIds(renames, resolveCliToolId) {
596
599
  coordinator.pendingRestart.delete(move.oldKey);
597
600
  carried.set(move.newKey, { startedAt: exports.pollingStartTimes.get(move.oldKey) });
598
601
  exports.pollingStartTimes.delete(move.oldKey);
599
- (0, prompt_dedup_1.renamePromptHashCacheKey)(move.oldKey, move.newKey);
600
- (0, response_dedup_1.renameResponseHashCacheKey)(move.oldKey, move.newKey);
601
- // Issue #1695: the prompt hash moved on the line above, so the guard keeps
602
- // suppressing the prompt on screen under the new ID. Its tally has to move
603
- // with it, or `capture --json` reports zero skips for a session that is
604
- // still skipping. Old worktree ID comes back out of the key we just left.
605
- const movedFrom = splitPollerKey(move.oldKey);
606
- if (movedFrom) {
607
- (0, prompt_dedup_state_1.renamePromptDedupSkips)(movedFrom.worktreeId, move.newWorktreeId, move.cliToolId, move.instanceId);
608
- }
609
602
  (0, tui_accumulator_1.clearTuiAccumulator)(move.oldKey);
610
603
  }
604
+ // Stop the abandoned pollers before any dedup state is written: the stop
605
+ // clears every cache under its key, and that key can be the destination of
606
+ // another move in this batch (A:x paused → B:x while B:x is abandoned).
607
+ // Stopped afterwards, it erased the state that had just arrived (#3553).
608
+ // An abandoned key is never a move source, so nothing still to be moved is
609
+ // lost here; and the paused chains are collected after it, as before, so a
610
+ // pause the stop ends is not carried.
611
611
  for (const pollerKey of abandoned) {
612
612
  stopPollingByKey(pollerKey);
613
613
  logger.warn('poller:migrate-abandoned', { pollerKey });
614
614
  }
615
- // Phase 2: restart each poller under its new identity.
616
- for (const move of moves) {
617
- const startedAt = carried.get(move.newKey)?.startedAt;
618
- exports.pollingStartTimes.set(move.newKey, startedAt ?? Date.now());
619
- if (isFullScreenTui(move.cliToolId)) {
620
- (0, tui_accumulator_1.initTuiAccumulator)(move.newKey);
621
- }
622
- scheduleNextResponsePoll(move.newWorktreeId, move.cliToolId, move.instanceId);
623
- }
624
- // Issue #2230: a chain paused on a prompt has no timer, so the loop above
625
- // never sees it — but it is holding the response hash for the turn it paused
615
+ // Issue #2230: a chain paused on a prompt has no timer, so the loops here
616
+ // never see it — but it is holding the response hash for the turn it paused
626
617
  // on, and `/respond` will resume it under the NEW ID. Move the hash and the
627
618
  // pause mark with it; the chain itself stays paused until that resume.
628
619
  const pausedMoves = [];
@@ -635,11 +626,41 @@ function migrateResponsePollerWorktreeIds(renames, resolveCliToolId) {
635
626
  continue;
636
627
  pausedMoves.push({ oldKey: pollerKey, newKey: `${newWorktreeId}:${parts.instanceId}` });
637
628
  }
629
+ // Issue #3553: the dedup state of the whole batch moves in one call per map
630
+ // family, each lifting every source before writing any destination. Re-keyed
631
+ // pair by pair, a swap left A with its own state and B empty.
632
+ (0, prompt_dedup_1.renamePromptHashCacheKeys)(moves);
633
+ (0, response_dedup_1.renameResponseHashCacheKeys)([...moves, ...pausedMoves]);
634
+ // Issue #1695: the prompt hash moved above, so the guard keeps suppressing
635
+ // the prompt on screen under the new ID. Its tally has to move with it, or
636
+ // `capture --json` reports zero skips for a session that is still skipping.
637
+ // Old worktree ID comes back out of the key being left.
638
+ (0, prompt_dedup_state_1.renamePromptDedupSkipsBatch)(moves.flatMap((move) => {
639
+ const movedFrom = splitPollerKey(move.oldKey);
640
+ return movedFrom
641
+ ? [{
642
+ oldWorktreeId: movedFrom.worktreeId,
643
+ newWorktreeId: move.newWorktreeId,
644
+ cliToolId: move.cliToolId,
645
+ instanceId: move.instanceId,
646
+ }]
647
+ : [];
648
+ }));
649
+ // Phase 2: restart each poller under its new identity.
650
+ for (const move of moves) {
651
+ const startedAt = carried.get(move.newKey)?.startedAt;
652
+ exports.pollingStartTimes.set(move.newKey, startedAt ?? Date.now());
653
+ if (isFullScreenTui(move.cliToolId)) {
654
+ (0, tui_accumulator_1.initTuiAccumulator)(move.newKey);
655
+ }
656
+ scheduleNextResponsePoll(move.newWorktreeId, move.cliToolId, move.instanceId);
657
+ }
658
+ // Issue #2230: the pause marks of the chains collected above follow their
659
+ // hashes (moved with the batch). All removed before any is added, for the
660
+ // same swap reason.
638
661
  for (const move of pausedMoves)
639
662
  coordinator.pausedOnPrompt.delete(move.oldKey);
640
- for (const move of pausedMoves) {
641
- (0, response_dedup_1.renameResponseHashCacheKey)(move.oldKey, move.newKey);
663
+ for (const move of pausedMoves)
642
664
  coordinator.pausedOnPrompt.add(move.newKey);
643
- }
644
665
  return moves;
645
666
  }
@@ -3,14 +3,14 @@
3
3
  * Next Action Helper - Derived state for worktree next actions and review status.
4
4
  *
5
5
  * Issue #600: UX refresh - provides getNextAction() and getReviewStatus()
6
- * for Review screen and WorktreeCard display.
6
+ * for the Review screen (via `/api/worktrees`) and the sidebar row
7
+ * (`toBranchItem` in `src/types/sidebar.ts`).
7
8
  *
8
9
  * Uses exhaustive check (satisfies never) pattern to catch future
9
10
  * SessionStatus extensions at compile time [DR2-005].
10
11
  */
11
12
  Object.defineProperty(exports, "__esModule", { value: true });
12
13
  exports.NEXT_ACTION_KEYS = void 0;
13
- exports.isNextActionKey = isNextActionKey;
14
14
  exports.getNextAction = getNextAction;
15
15
  exports.getReviewStatus = getReviewStatus;
16
16
  /**
@@ -31,19 +31,6 @@ exports.NEXT_ACTION_KEYS = {
31
31
  checkStalled: 'nextAction.checkStalled',
32
32
  running: 'nextAction.running',
33
33
  };
34
- const NEXT_ACTION_KEY_SET = new Set(Object.values(exports.NEXT_ACTION_KEYS));
35
- /**
36
- * Whether a value is a key this module produces (Issue #1787).
37
- *
38
- * `Worktree.nextAction` is a plain `string` on the wire, and a server that
39
- * predates this Issue still sends the old English literal. Callers must gate
40
- * `t()` on this: next-intl reports a missing key by rendering the key path, so
41
- * feeding it `"Approve / Reject"` would put `worktree.Approve / Reject` on
42
- * screen. Anything that fails this test is rendered verbatim instead.
43
- */
44
- function isNextActionKey(value) {
45
- return NEXT_ACTION_KEY_SET.has(value);
46
- }
47
34
  /**
48
35
  * Determine the next action for a given session state.
49
36
  *
@@ -39,6 +39,7 @@ exports.ENV_CLEAN_GUIDANCE = exports.MAX_REPORTED_ENV_CHANGES = exports.isLivePi
39
39
  exports.resolveRequireEnvClean = resolveRequireEnvClean;
40
40
  exports.attributeSessionName = attributeSessionName;
41
41
  exports.attributeAnchor = attributeAnchor;
42
+ exports.isZcompdumpEntry = isZcompdumpEntry;
42
43
  exports.demoVitestPid = demoVitestPid;
43
44
  exports.attributeHomeEntry = attributeHomeEntry;
44
45
  exports.resolveTaskSessionName = resolveTaskSessionName;
@@ -200,6 +201,17 @@ exports.readLivePidCwd = readLivePidCwd;
200
201
  * established.
201
202
  */
202
203
  const DEMO_VITEST_HOME_ENTRY = /^\.commandmate-demo-vitest-([1-9]\d*)$/;
204
+ /**
205
+ * zsh's `compinit` cache: `~/.zcompdump` itself, and the `~/.zcompdump.<host>.<pid>`
206
+ * scratch file it writes before renaming (#3664). A worker's shell start makes
207
+ * it; the pid in it is already gone by the time the gate looks. Only these two
208
+ * shapes — any other `.zcompdump*` name stays a violation.
209
+ */
210
+ const ZCOMPDUMP_HOME_ENTRY = /^\.zcompdump(?:\.[A-Za-z0-9._-]+\.[1-9]\d*)?$/;
211
+ /** Whether a `$HOME` entry name is a zsh compinit cache file (#3664). */
212
+ function isZcompdumpEntry(name) {
213
+ return ZCOMPDUMP_HOME_ENTRY.test(name);
214
+ }
203
215
  const isLivePid = (pid) => {
204
216
  try {
205
217
  process.kill(pid, 0);
@@ -581,7 +593,8 @@ function diffEnvSnapshots(baseline, final, context, options = {}) {
581
593
  const afterKeys = new Set(after.entries.map((entry) => entry.key));
582
594
  // Only `home-entries`, and only by the whole name: the same string under
583
595
  // `~/.commandmate` is a different directory and stays counted.
584
- const isIgnoredByConfig = (entry) => probeId === 'home-entries' && ignoredHomeEntries.has(entry.key);
596
+ const isIgnoredByConfig = (entry) => probeId === 'home-entries' &&
597
+ (ignoredHomeEntries.has(entry.key) || isZcompdumpEntry(entry.key));
585
598
  const added = [];
586
599
  const ignoredAdded = [];
587
600
  const taskSessionAdded = [];