commandmate 0.33.1 → 0.33.3

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 (337) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +307 -307
  3. package/.next/app-path-routes-manifest.json +28 -28
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/diagnostics/framework.json +1 -1
  6. package/.next/next-server.js.nft.json +1 -1
  7. package/.next/prerender-manifest.json +15 -15
  8. package/.next/required-server-files.json +1 -2
  9. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  10. package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
  12. package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/app/update-check/route.js +1 -1
  14. package/.next/server/app/api/app/update-check/route.js.nft.json +1 -1
  15. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/assistant/conversation/route.js +1 -1
  17. package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
  18. package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/assistant/current-output/route.js +1 -1
  20. package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
  21. package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
  22. package/.next/server/app/api/assistant/messages/route.js +1 -1
  23. package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
  24. package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/assistant/session/route.js +1 -1
  26. package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
  27. package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
  28. package/.next/server/app/api/assistant/start/route.js +32 -1
  29. package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
  30. package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/assistant/terminal/route.js +33 -2
  32. package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
  33. package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/assistant/tools/route.js +1 -1
  35. package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
  36. package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/capabilities/route.js +1 -1
  41. package/.next/server/app/api/capabilities/route.js.nft.json +1 -1
  42. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/hooks/agent-event/route.js +1 -1
  51. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  52. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/hooks/claude-done/route.js +2 -2
  54. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  55. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/hooks/permission-request/route.js +1 -1
  57. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  58. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  64. package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
  65. package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
  66. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/relays/route.js +1 -1
  68. package/.next/server/app/api/relays/route.js.nft.json +1 -1
  69. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  75. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  76. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/repositories/clone/route.js +1 -1
  78. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  79. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/repositories/restore/route.js +1 -1
  82. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  83. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/repositories/route.js +1 -1
  85. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  86. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/repositories/scan/route.js +1 -1
  88. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  89. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/repositories/sync/route.js +1 -1
  91. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  92. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/settings/default-agents/route.js +1 -1
  95. package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
  96. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/skills/[id]/route.js +1 -1
  100. package/.next/server/app/api/skills/[id]/route.js.nft.json +1 -1
  101. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/skills/installations/route.js +1 -1
  103. package/.next/server/app/api/skills/installations/route.js.nft.json +1 -1
  104. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/skills/route.js +1 -1
  108. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  109. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  120. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  123. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
  126. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  132. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  153. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  155. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  157. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  159. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  160. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
  162. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  163. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  164. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  165. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  166. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  167. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  168. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  169. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  170. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  173. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  176. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  177. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  179. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  180. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  181. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  182. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  184. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  185. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  186. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  187. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  188. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  189. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  190. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  191. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  192. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  193. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  194. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +1 -1
  196. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  197. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  198. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  199. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js +1 -1
  200. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js.nft.json +1 -1
  201. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  202. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  203. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  204. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  205. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js +1 -1
  206. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js.nft.json +1 -1
  207. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  208. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  209. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  210. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  211. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  212. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  213. package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
  214. package/.next/server/app/api/worktrees/[id]/tasks/route.js.nft.json +1 -1
  215. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  216. package/.next/server/app/api/worktrees/[id]/terminal/route.js +103 -1
  217. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  218. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  219. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  220. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  221. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  222. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  223. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  224. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  225. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  226. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  227. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  228. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  229. package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
  230. package/.next/server/app/api/worktrees/[id]/verify/route.js.nft.json +1 -1
  231. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  232. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
  233. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js.nft.json +1 -1
  234. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  235. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  236. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  237. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  238. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  239. package/.next/server/app/api/worktrees/route.js +1 -1
  240. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  241. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  242. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  243. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  244. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  245. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  246. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  247. package/.next/server/app/page_client-reference-manifest.js +1 -1
  248. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  249. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  250. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  251. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  252. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  253. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  254. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  255. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  256. package/.next/server/app/worktrees/[id]/page.js +10 -10
  257. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  258. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  259. package/.next/server/app-paths-manifest.json +28 -28
  260. package/.next/server/chunks/1068.js +1 -0
  261. package/.next/server/chunks/1210.js +1 -1
  262. package/.next/server/chunks/1331.js +2 -2
  263. package/.next/server/chunks/1366.js +5 -3
  264. package/.next/server/chunks/1501.js +6 -2
  265. package/.next/server/chunks/1886.js +2 -2
  266. package/.next/server/chunks/2119.js +1 -1
  267. package/.next/server/chunks/2445.js +1 -1
  268. package/.next/server/chunks/2839.js +1 -0
  269. package/.next/server/chunks/3123.js +1 -1
  270. package/.next/server/chunks/3241.js +1 -1
  271. package/.next/server/chunks/3323.js +1 -0
  272. package/.next/server/chunks/3707.js +1 -1
  273. package/.next/server/chunks/3708.js +1 -1
  274. package/.next/server/chunks/4443.js +1 -1
  275. package/.next/server/chunks/5220.js +1 -1
  276. package/.next/server/chunks/595.js +2 -2
  277. package/.next/server/chunks/6130.js +1 -0
  278. package/.next/server/chunks/650.js +1 -0
  279. package/.next/server/chunks/6661.js +1 -1
  280. package/.next/server/chunks/6781.js +1 -0
  281. package/.next/server/chunks/6808.js +1 -0
  282. package/.next/server/chunks/7875.js +3 -3
  283. package/.next/server/chunks/7905.js +1 -1
  284. package/.next/server/chunks/8918.js +107 -5
  285. package/.next/server/chunks/9553.js +1 -0
  286. package/.next/server/chunks/9627.js +1 -1
  287. package/.next/server/middleware-build-manifest.js +1 -1
  288. package/.next/server/middleware-manifest.json +5 -5
  289. package/.next/server/pages/500.html +1 -1
  290. package/.next/server/server-reference-manifest.json +1 -1
  291. package/.next/server/src/middleware.js.map +1 -1
  292. package/.next/static/chunks/1255-7e5873f06b25e129.js +1 -0
  293. package/.next/static/chunks/{4718-2bc5cdf06573bf54.js → 4718-814f4ecfd7fcc513.js} +1 -1
  294. package/.next/static/chunks/6022-7a7d1760fc9814c7.js +1 -0
  295. package/.next/static/chunks/app/worktrees/[id]/page-3af707e74a75172b.js +1 -0
  296. package/.next/static/chunks/{main-6092074a0dc78280.js → main-ac91ed6e71380841.js} +1 -1
  297. package/.next/trace +13 -14
  298. package/dist/server/locales/en/worktree.json +9 -2
  299. package/dist/server/locales/ja/worktree.json +9 -2
  300. package/dist/server/src/lib/assistant-response-saver.js +168 -12
  301. package/dist/server/src/lib/cli-tools/vibe-local.js +10 -0
  302. package/dist/server/src/lib/hooks/hook-settings-generator.js +61 -1
  303. package/dist/server/src/lib/hooks/sources/antigravity/history.js +497 -27
  304. package/dist/server/src/lib/hooks/sources/antigravity/transcript.js +103 -0
  305. package/dist/server/src/lib/hooks/sources/claude/history.js +29 -3
  306. package/dist/server/src/lib/hooks/sources/codex/history.js +29 -3
  307. package/dist/server/src/lib/hooks/sources/command-code/history.js +29 -3
  308. package/dist/server/src/lib/hooks/stop-history-capture.js +30 -4
  309. package/dist/server/src/lib/polling/response-checker.js +258 -9
  310. package/dist/server/src/lib/polling/response-poller-core.js +20 -0
  311. package/dist/server/src/lib/polling/structured-history-gate.js +94 -10
  312. package/dist/server/src/lib/response-cleaner.js +84 -0
  313. package/dist/server/src/lib/session/agent-session-lifecycle.js +31 -1
  314. package/dist/server/src/lib/session/session-generation-archive.js +194 -0
  315. package/dist/server/src/lib/tasks/contract-message.js +83 -9
  316. package/dist/server/src/lib/tasks/contract-parser.js +2 -0
  317. package/package.json +2 -2
  318. package/.next/server/chunks/153.js +0 -1
  319. package/.next/server/chunks/21.js +0 -1
  320. package/.next/server/chunks/3086.js +0 -1
  321. package/.next/server/chunks/3270.js +0 -1
  322. package/.next/server/chunks/3925.js +0 -1
  323. package/.next/server/chunks/4299.js +0 -1
  324. package/.next/server/chunks/5043.js +0 -1
  325. package/.next/server/chunks/6306.js +0 -1
  326. package/.next/server/chunks/6363.js +0 -1
  327. package/.next/server/chunks/6491.js +0 -103
  328. package/.next/server/chunks/6838.js +0 -1
  329. package/.next/server/chunks/7366.js +0 -5
  330. package/.next/server/chunks/7890.js +0 -1
  331. package/.next/server/chunks/8063.js +0 -32
  332. package/.next/server/chunks/8687.js +0 -1
  333. package/.next/static/chunks/1255-8cb587d9170b45ed.js +0 -1
  334. package/.next/static/chunks/6022-2fa0b5a82d4ce7b6.js +0 -1
  335. package/.next/static/chunks/app/worktrees/[id]/page-13ca0ba551fe5cfb.js +0 -1
  336. /package/.next/static/{9lgw9hA727UMyFhXryGWG → 9x6U1h52hH_r6pOsGwI_p}/_buildManifest.js +0 -0
  337. /package/.next/static/{9lgw9hA727UMyFhXryGWG → 9x6U1h52hH_r6pOsGwI_p}/_ssgManifest.js +0 -0
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
2
  /** Response checking and extraction logic for CLI tool polling (Issue #575 split from response-poller.ts). */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.PENDING_SCRAPE_HOLD_MS = void 0;
4
5
  exports.incompleteResult = incompleteResult;
5
6
  exports.buildPromptExtractionResult = buildPromptExtractionResult;
6
7
  exports.detectPromptOnCleanFrame = detectPromptOnCleanFrame;
7
8
  exports.detectPromptWithOptions = detectPromptWithOptions;
8
9
  exports.extractResponse = extractResponse;
10
+ exports.resetPendingScrapedResponses = resetPendingScrapedResponses;
11
+ exports.hasPendingScrapedResponse = hasPendingScrapedResponse;
12
+ exports.flushPendingScrapedResponse = flushPendingScrapedResponse;
9
13
  exports.checkForResponse = checkForResponse;
10
14
  const cli_session_1 = require("../../lib/session/cli-session");
11
15
  const db_instance_1 = require("../../lib/db/db-instance");
@@ -30,6 +34,7 @@ const prompt_dedup_1 = require("./prompt-dedup");
30
34
  const prompt_dedup_state_1 = require("./prompt-dedup-state");
31
35
  const response_dedup_1 = require("./response-dedup");
32
36
  const structured_history_gate_1 = require("./structured-history-gate");
37
+ const stop_history_capture_1 = require("../../lib/hooks/stop-history-capture");
33
38
  const relay_triggers_1 = require("../../lib/relay/relay-triggers");
34
39
  // Issue #2317 Phase D: while a human holds the pane's geometry, the frame is
35
40
  // their terminal (44 rows), not the 1000-row canvas every rule below was
@@ -735,6 +740,176 @@ function extractResponse(output, lastCapturedLine, cliToolId, captureWindowLines
735
740
  // ============================================================================
736
741
  // checkForResponse (exported for response-poller-core.ts)
737
742
  // ============================================================================
743
+ // ============================================================================
744
+ // The held scrape (Issue #2436)
745
+ // ============================================================================
746
+ /**
747
+ * How long a scraped reply is held while its transcript finishes closing.
748
+ *
749
+ * Derived from `STOP_TRANSCRIPT_DEFERRED_DELAYS_MS`, not spelled again: those
750
+ * are the instants the Stop receiver re-reads the transcript at after it has
751
+ * answered the agent (#2398), so their SUM is the moment after which nobody is
752
+ * still trying. Holding past it would be holding for a row that has no producer
753
+ * left; stopping short of it would race the producer that is still running.
754
+ *
755
+ * The measured gap this covers is under a second — codex 2026-09-08 appended
756
+ * `task_complete` ~700 ms after the frame went quiet — so the budget is roughly
757
+ * ten times the case it exists for, spent only on turns that ask for it.
758
+ */
759
+ exports.PENDING_SCRAPE_HOLD_MS = stop_history_capture_1.STOP_TRANSCRIPT_DEFERRED_DELAYS_MS.reduce((total, delay) => total + delay, 0);
760
+ /**
761
+ * Held scrapes, by poller key.
762
+ *
763
+ * **Deliberately not in `./response-dedup`.** That module's two caches are
764
+ * cleared by `stopPollingByKey` — `clearResponseHashCache` is called from
765
+ * inside it — so a held reply parked beside them would be dropped by the very
766
+ * event that has to write it (Issue #2436, requirement B). It lives here, next
767
+ * to the code that fills it and the code that writes it out, and
768
+ * `flushPendingScrapedResponse` is what the poller calls before it clears
769
+ * anything.
770
+ *
771
+ * On `globalThis` for the reason every shared map in this subsystem is (#1736):
772
+ * under `next dev` the poller's bundle and each route's bundle would otherwise
773
+ * hold a private copy, and a held reply only one bundle can see is a lost one.
774
+ */
775
+ const pendingScrapedResponses = (globalThis.__pendingScrapedResponses ??= new Map());
776
+ /** Forget every held scrape. Test seam. */
777
+ function resetPendingScrapedResponses() {
778
+ pendingScrapedResponses.clear();
779
+ }
780
+ /** Whether a scrape is being held for this poller key. Test seam / diagnostics. */
781
+ function hasPendingScrapedResponse(pollerKey) {
782
+ return pendingScrapedResponses.has(pollerKey);
783
+ }
784
+ /**
785
+ * Hold this tick's scraped reply instead of writing it (Issue #2436).
786
+ *
787
+ * Called when the transcript reader has said `not_yet_closed`: the agent's own
788
+ * Markdown for this turn is coming, and writing the pane's copy now is what put
789
+ * 234,323 characters of prompt echo, intermediate output and footer into
790
+ * History beside the real answer.
791
+ *
792
+ * A second hold for the same key REPLACES the content and keeps the original
793
+ * deadline. Replaces, because a frame that moved on is a better copy of the
794
+ * same turn; keeps, because a pane that redraws every tick would otherwise push
795
+ * its own deadline forward forever and the hold would stop being bounded.
796
+ */
797
+ function holdScrapedResponse(pollerKey, pending) {
798
+ const existing = pendingScrapedResponses.get(pollerKey);
799
+ if (existing) {
800
+ existing.content = pending.content;
801
+ return;
802
+ }
803
+ pendingScrapedResponses.set(pollerKey, pending);
804
+ }
805
+ /**
806
+ * Drop a held scrape without writing it (Issue #2436).
807
+ *
808
+ * The one thing that justifies dropping it: the transcript reader has since
809
+ * written the turn as the agent's own Markdown, so the pane's copy is the
810
+ * duplicate this Issue exists to stop.
811
+ */
812
+ function discardPendingScrapedResponse(pollerKey) {
813
+ pendingScrapedResponses.delete(pollerKey);
814
+ }
815
+ /**
816
+ * Write a held scrape now, whatever the clock says (Issue #2436).
817
+ *
818
+ * Exported because `response-poller-core` calls it from `stopPollingByKey`,
819
+ * which is every way a polling cycle ends: an explicit stop, the session going
820
+ * away, `MAX_POLLING_DURATION`, and the restart that opens the NEXT turn. A
821
+ * held reply must not be able to outlive the cycle that holds it — the caches
822
+ * that key it are cleared in that same function, and a reply nobody writes is
823
+ * strictly worse than the duplicate row this Issue is trading against.
824
+ *
825
+ * **Deliberately bypasses `isDuplicateResponse`.** The hash for this content was
826
+ * registered by the tick that decided to hold it (the dedup guard's check is
827
+ * also its write), so a re-check here would answer "duplicate" for the reply
828
+ * that has never been saved. That is requirement A of the Issue in one line.
829
+ *
830
+ * Synchronous through the row: `better-sqlite3` is, and this runs on shutdown
831
+ * paths where an awaited continuation may never be reached. The Markdown
832
+ * conversation log is fired afterwards and not waited for, because it is a
833
+ * secondary record and the row is the one History reads.
834
+ *
835
+ * @param pollerKey - Poller key ("worktreeId:instanceId")
836
+ * @param reason - What ended the hold; logged, for the operator reading back
837
+ * @returns Whether a held reply was written
838
+ */
839
+ function flushPendingScrapedResponse(pollerKey, reason) {
840
+ const pending = pendingScrapedResponses.get(pollerKey);
841
+ if (!pending)
842
+ return false;
843
+ pendingScrapedResponses.delete(pollerKey);
844
+ try {
845
+ const db = (0, db_instance_1.getDbInstance)();
846
+ const message = (0, db_1.createMessage)(db, {
847
+ worktreeId: pending.worktreeId,
848
+ role: 'assistant',
849
+ content: pending.content,
850
+ messageType: 'normal',
851
+ timestamp: pending.timestamp,
852
+ cliToolId: pending.cliToolId,
853
+ instanceId: pending.instanceId,
854
+ summary: pending.summary,
855
+ logFileName: pending.logFileName,
856
+ requestId: pending.requestId,
857
+ });
858
+ (0, ws_server_1.broadcastMessage)('message', { worktreeId: pending.worktreeId, message });
859
+ logger.info('pending-scrape-flushed', {
860
+ worktreeId: pending.worktreeId,
861
+ cliToolId: pending.cliToolId,
862
+ instanceId: pending.instanceId,
863
+ reason,
864
+ scrapedLength: pending.content.length,
865
+ heldForMs: Date.now() - pending.timestamp.getTime(),
866
+ });
867
+ void (0, conversation_logger_1.recordClaudeConversation)(db, pending.worktreeId, pending.content, pending.cliToolId).catch(() => { });
868
+ return true;
869
+ }
870
+ catch (error) {
871
+ logger.warn('pending-scrape-flush-failed', {
872
+ worktreeId: pending.worktreeId,
873
+ cliToolId: pending.cliToolId,
874
+ instanceId: pending.instanceId,
875
+ reason,
876
+ error: error instanceof Error ? error.message : String(error),
877
+ });
878
+ return false;
879
+ }
880
+ }
881
+ /**
882
+ * End a hold whose deadline has passed, one way or the other (Issue #2436).
883
+ *
884
+ * Run at the top of every tick, because the tick that has to notice an expiry
885
+ * is very often one that returns early — a static frame yields no new lines for
886
+ * a scrollback tool and is a duplicate for an alternate-screen one, and neither
887
+ * of those paths reaches the save block at the bottom of `checkForResponse`.
888
+ *
889
+ * The reader is asked ONE more time before the row is written. By the deadline
890
+ * the throttled recheck (#2399) has usually already captured the turn and
891
+ * dropped the hold, but the two are not in step — the recheck is every third
892
+ * duplicate tick and this is a wall clock — and a last read costs one tail
893
+ * parse against writing a pane dump that is about to be superseded.
894
+ */
895
+ async function settleExpiredPendingScrapedResponse(pollerKey) {
896
+ const pending = pendingScrapedResponses.get(pollerKey);
897
+ if (!pending || Date.now() < pending.expiresAt)
898
+ return;
899
+ const captured = await (0, structured_history_gate_1.captureStructuredHistoryTurn)(pending.worktreeId, pending.cliToolId, pending.instanceId, { worktreePath: pending.worktreePath, transcriptPathHint: pending.transcriptPathHint });
900
+ if (captured) {
901
+ discardPendingScrapedResponse(pollerKey);
902
+ (0, response_dedup_1.settleStructuredHistoryRecheck)(pollerKey);
903
+ logger.info('pending-scrape-superseded', {
904
+ worktreeId: pending.worktreeId,
905
+ cliToolId: pending.cliToolId,
906
+ instanceId: pending.instanceId,
907
+ heldForMs: Date.now() - pending.timestamp.getTime(),
908
+ });
909
+ return;
910
+ }
911
+ flushPendingScrapedResponse(pollerKey, 'hold-expired');
912
+ }
738
913
  /**
739
914
  * Record what the structured writers said about this turn, for the ticks that
740
915
  * will not get to ask (Issue #2399).
@@ -784,9 +959,20 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
784
959
  const running = await (0, cli_session_1.isSessionRunning)(worktreeId, cliToolId, instanceId);
785
960
  if (!running) {
786
961
  logger.info('session-not-running');
962
+ // `stopPolling` is what confirms a held scrape here; see
963
+ // `flushPendingScrapedResponse` and `stopPollingByKey`. Requirement B of
964
+ // Issue #2436: this return is ~300 lines above the save path, so a hold
965
+ // released only down there would never be released at all.
787
966
  (0, response_poller_core_1.stopPolling)(worktreeId, cliToolId, instanceId);
788
967
  return false;
789
968
  }
969
+ const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
970
+ // Issue #2436: a scrape held by an earlier tick, whose transcript has now
971
+ // had its whole budget to close. Ahead of everything below because most
972
+ // ticks of a finished turn never reach the save path — a static frame is a
973
+ // duplicate for an alternate-screen tool and yields no new lines for a
974
+ // scrollback one, and both of those return early.
975
+ await settleExpiredPendingScrapedResponse(pollerKey);
790
976
  // Issue #2317 Phase D: is a human reading this pane at their own terminal
791
977
  // size right now, and did they just stop?
792
978
  //
@@ -831,7 +1017,6 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
831
1017
  // Layer 2: Accumulate TUI content for full-screen TUI tools, so a turn that
832
1018
  // outgrows the alternate-screen pane keeps the head that has scrolled away.
833
1019
  if (cliToolId === 'opencode' || cliToolId === 'copilot') {
834
- const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
835
1020
  // Issue #1911: opencode is accumulated from the CURRENT TURN'S REGION
836
1021
  // rather than the whole frame. Feeding the raw pane seeded the accumulator
837
1022
  // with the previous turn's transcript, the echoed prompt and the bottom
@@ -885,7 +1070,6 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
885
1070
  if (promptDetection.isPrompt) {
886
1071
  // Issue #565: Content hash-based duplicate prompt prevention
887
1072
  const promptContent = promptDetection.rawContent || promptDetection.cleanContent;
888
- const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
889
1073
  const normalizedForDedup = (0, prompt_dedup_1.normalizePromptForDedup)(promptContent, cliToolId);
890
1074
  if ((0, prompt_dedup_1.isDuplicatePrompt)(pollerKey, normalizedForDedup)) {
891
1075
  // Issue #1695: the log line below is invisible to `commandmate capture
@@ -995,7 +1179,6 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
995
1179
  cleanedResponse = (0, response_cleaner_1.cleanClaudeResponse)(result.response);
996
1180
  }
997
1181
  else if (cliToolId === 'copilot') {
998
- const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
999
1182
  const accumulatedContent = (0, tui_accumulator_1.getAccumulatedContent)(pollerKey);
1000
1183
  const sourceContent = accumulatedContent || result.response;
1001
1184
  cleanedResponse = (0, response_cleaner_1.cleanCopilotResponse)(sourceContent);
@@ -1003,7 +1186,6 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1003
1186
  (0, tui_accumulator_1.clearTuiAccumulator)(pollerKey);
1004
1187
  }
1005
1188
  else if (cliToolId === 'opencode') {
1006
- const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
1007
1189
  // Issue #1911 defect 3: opencode wrote to the Layer-2 accumulator but never
1008
1190
  // read it, so any turn longer than the pane was saved without its head.
1009
1191
  //
@@ -1042,7 +1224,6 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1042
1224
  // Without this the disabled cursor would leave nothing suppressing re-saves and
1043
1225
  // the poller would append the same finished reply every 2 s.
1044
1226
  if (!lineCountIsCursor) {
1045
- const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
1046
1227
  if ((0, response_dedup_1.isDuplicateResponse)(pollerKey, cleanedResponse)) {
1047
1228
  // Issue #1695: this branch used to drop the response silently — the
1048
1229
  // prompt-side guard above has logged its skip since #565, this one
@@ -1092,6 +1273,16 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1092
1273
  // is neither. Two rows for one turn is the failure this trades for, and
1093
1274
  // #2401 has already stopped the junk one being picked as a relay's
1094
1275
  // answer.
1276
+ //
1277
+ // Issue #2436 narrowed what that trade costs, without changing the
1278
+ // decision above. The row this skip cannot retract is now only ever one
1279
+ // the poller had no reason to hold: a turn whose reader said
1280
+ // `not_yet_closed` is held at the save path below rather than written,
1281
+ // so on the ordinary codex turn there is no earlier row here to regret.
1282
+ // What remains is the case the three reasons above are actually about —
1283
+ // a scrape written when the reader could tell us nothing, and a
1284
+ // transcript that closed later anyway — and for that the row stays,
1285
+ // folded rather than deleted on the chat surface (`ChatMessageBubble`).
1095
1286
  if ((0, response_dedup_1.claimStructuredHistoryRecheck)(pollerKey)) {
1096
1287
  const recaptured = await (0, structured_history_gate_1.captureStructuredHistoryTurn)(worktreeId, cliToolId, instanceId, {
1097
1288
  worktreePath: worktree.path,
@@ -1099,6 +1290,11 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1099
1290
  });
1100
1291
  if (recaptured) {
1101
1292
  (0, response_dedup_1.settleStructuredHistoryRecheck)(pollerKey);
1293
+ // Issue #2436: the turn is now the agent's own Markdown, so a
1294
+ // scrape held for it is exactly the second row this Issue exists
1295
+ // to stop. Dropped, not written — the only case where dropping a
1296
+ // held reply loses nothing.
1297
+ discardPendingScrapedResponse(pollerKey);
1102
1298
  logger.info('structured-history-recheck-captured', {
1103
1299
  worktreeId,
1104
1300
  cliToolId,
@@ -1132,18 +1328,24 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1132
1328
  // Markdown and answers true, or answers false and leaves the scrape below to
1133
1329
  // be the only record. `||` and not `&&`: the two are different tools'
1134
1330
  // answers to the same question, and each one is false for the other's tool.
1331
+ //
1332
+ // Issue #2436 adds the third answer. `captureReport.outcome` distinguishes
1333
+ // "the agent has not closed this turn yet" from "there is nothing to read
1334
+ // here", which the boolean could not: both arrived as `false`, and `false`
1335
+ // meant "save the pane's copy". See {@link StructuredHistoryCaptureOutcome}.
1336
+ const captureReport = {};
1135
1337
  const structuredHistoryLive = (0, structured_history_gate_1.isStructuredHistoryWriterLive)(worktreeId, cliToolId, instanceId) ||
1136
1338
  (await (0, structured_history_gate_1.captureStructuredHistoryTurn)(worktreeId, cliToolId, instanceId, {
1137
1339
  worktreePath: worktree.path,
1138
1340
  transcriptPathHint: claudeMetadata?.logFilePath ?? null,
1139
- }));
1341
+ }, captureReport));
1140
1342
  // Issue #2399: remember which way that went, because the next tick may not
1141
1343
  // get here. A `false` is the reader saying "not yet, or not mine", and the
1142
1344
  // dedup guard above turns every following poll of the same static frame into
1143
1345
  // a return — so unless the fact is written down now, the ask never happens
1144
1346
  // again. A `true` settles it: the turn is recorded and there is nothing left
1145
1347
  // to re-ask about.
1146
- markOrSettleStructuredHistoryRecheck((0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId), structuredHistoryLive);
1348
+ markOrSettleStructuredHistoryRecheck(pollerKey, structuredHistoryLive);
1147
1349
  // Issue #2317 Phase D: the scrape is dropped while the geometry is
1148
1350
  // delegated, and the transcript capture above is what makes that safe.
1149
1351
  //
@@ -1161,8 +1363,20 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1161
1363
  // deliberate trade: a missing row is recoverable, a truncated reply saved as
1162
1364
  // the agent's answer is not.
1163
1365
  const suppressScrapedHistory = structuredHistoryLive || delegation.delegated;
1366
+ // Issue #2436: not suppressed — HELD. The reader has read the transcript,
1367
+ // found the newest turn still open and said so, which means the agent's own
1368
+ // Markdown for this turn is on its way. Writing the pane's copy now is what
1369
+ // put a 234,323-character dump of prompt echo, intermediate output and
1370
+ // footer into History beside the real answer, and #2399 explicitly accepted
1371
+ // that trade because the boolean it had could not tell "not yet" from
1372
+ // "never". It can now.
1373
+ //
1374
+ // Everything else this tick does still happens: the cursor advances, the
1375
+ // prompts are marked answered, the waiting episode closes, the push goes
1376
+ // out. Only the two writes that RECORD THE REPLY wait.
1377
+ const holdScrapedHistory = !suppressScrapedHistory && captureReport.outcome === 'not_yet_closed';
1164
1378
  // Create Markdown log file for the conversation pair
1165
- if (cleanedResponse && !suppressScrapedHistory) {
1379
+ if (cleanedResponse && !suppressScrapedHistory && !holdScrapedHistory) {
1166
1380
  await (0, conversation_logger_1.recordClaudeConversation)(db, worktreeId, cleanedResponse, cliToolId);
1167
1381
  }
1168
1382
  // Mark any pending prompts as answered
@@ -1183,7 +1397,38 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1183
1397
  // are not byte-comparable — the pane's copy is hard-wrapped at the pane
1184
1398
  // width and gutter-prefixed, so no content check could ever recognise them
1185
1399
  // as the same reply.
1186
- if (!suppressScrapedHistory) {
1400
+ if (holdScrapedHistory) {
1401
+ holdScrapedResponse(pollerKey, {
1402
+ worktreeId,
1403
+ cliToolId,
1404
+ instanceId: resolvedInstanceId,
1405
+ content: cleanedResponse,
1406
+ // The instant the turn was JUDGED finished, not the instant the row is
1407
+ // written. History sorts on this, and a row dated at the end of the
1408
+ // hold would sort under the NEXT turn's prompt.
1409
+ timestamp: new Date(),
1410
+ summary: claudeMetadata?.summary,
1411
+ logFileName: claudeMetadata?.logFileName,
1412
+ requestId: claudeMetadata?.requestId,
1413
+ worktreePath: worktree.path,
1414
+ transcriptPathHint: claudeMetadata?.logFilePath ?? null,
1415
+ expiresAt: Date.now() + exports.PENDING_SCRAPE_HOLD_MS,
1416
+ });
1417
+ logger.info('structured-history-scrape-held', {
1418
+ worktreeId,
1419
+ cliToolId,
1420
+ instanceId: resolvedInstanceId,
1421
+ scrapedLength: cleanedResponse.length,
1422
+ holdMs: exports.PENDING_SCRAPE_HOLD_MS,
1423
+ });
1424
+ // The completion edge is announced HERE and not at the flush, because the
1425
+ // turn finished now. A relay waiting on this session gets #2401's grace
1426
+ // window to find a turn-keyed row, which is exactly the row the hold is
1427
+ // waiting for; delaying the announcement by the hold would delay every
1428
+ // delivery by it too.
1429
+ (0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, false);
1430
+ }
1431
+ else if (!suppressScrapedHistory) {
1187
1432
  // Create new CLI tool message in database
1188
1433
  const message = (0, db_1.createMessage)(db, {
1189
1434
  worktreeId,
@@ -1209,6 +1454,10 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
1209
1454
  (0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, false);
1210
1455
  }
1211
1456
  else {
1457
+ // Issue #2436: a hold from an earlier tick of this same turn is now moot
1458
+ // — the row it was waiting for exists.
1459
+ if (structuredHistoryLive)
1460
+ discardPendingScrapedResponse(pollerKey);
1212
1461
  logger.info('structured-history-scrape-suppressed', {
1213
1462
  worktreeId,
1214
1463
  cliToolId,
@@ -365,6 +365,26 @@ function stopPollingByKey(pollerKey, options = {}) {
365
365
  });
366
366
  return;
367
367
  }
368
+ // Issue #2436: a scraped reply this cycle decided to HOLD — because the
369
+ // agent's transcript had not closed the turn yet — is confirmed here, before
370
+ // anything that keys it is dropped. Every way a cycle ends comes through this
371
+ // function: an explicit stop, `checkForResponse` finding the session gone,
372
+ // `MAX_POLLING_DURATION`, and the restart that opens the next turn. A hold
373
+ // that outlived its cycle would be a reply nobody ever writes, which is worse
374
+ // than the duplicate row the hold is trading against.
375
+ //
376
+ // Ahead of `clearResponseHashCache` below, and that ordering is the whole
377
+ // reason the held reply does not live in `./response-dedup` beside the hash:
378
+ // it would be cleared by the same call that has to write it.
379
+ //
380
+ // `resume` is the one exception, and for the same reason the response hash
381
+ // survives it: the cycle is not ending, it is being picked up where a prompt
382
+ // paused it, and the turn the hold belongs to is still the current one. The
383
+ // hold stays bounded regardless — the first tick of the resumed chain runs
384
+ // `settleExpiredPendingScrapedResponse`, and by then the deadline has almost
385
+ // certainly passed.
386
+ if (!options.resume)
387
+ (0, response_checker_1.flushPendingScrapedResponse)(pollerKey, 'poller-stopped');
368
388
  const owner = coordinator.owners.get(pollerKey);
369
389
  // The live owner stopping itself from inside its own tick. Identity, not key
370
390
  // equality: a stale generation with the same key was turned away above.
@@ -118,9 +118,41 @@ const logger = (0, logger_1.createLogger)('lib/polling/structured-history-gate')
118
118
  * can see is not a lock.
119
119
  */
120
120
  const captureQueue = (globalThis.__structuredHistoryCaptureQueue ??= new Map());
121
- /** Forget every instance's in-flight capture. Test seam. */
121
+ /**
122
+ * The last completion each instance was announced for (Issue #2443).
123
+ *
124
+ * Keyed by the same triple as {@link captureQueue}, holding whatever
125
+ * `StructuredHistoryCaptureReport.completionKey` the reader minted. Its only job
126
+ * is to make the completion edge an *edge*: the poller re-reads a finished turn
127
+ * every couple of seconds, and #2443 forbids adding a path that re-delivers a
128
+ * body somebody already has.
129
+ *
130
+ * On `globalThis` for the reason {@link captureQueue} is, and in memory for the
131
+ * reason `session/agent-event-state` gives: the value describes a live session,
132
+ * and a restart that forgets it costs at most one extra announcement into a
133
+ * relay whose own stash guard already refuses a second payload.
134
+ */
135
+ const announcedCompletions = (globalThis.__structuredHistoryAnnouncedCompletions ??= new Map());
136
+ /** Forget every instance's in-flight capture and announced completion. Test seam. */
122
137
  function resetStructuredHistoryCaptureQueue() {
123
138
  captureQueue.clear();
139
+ announcedCompletions.clear();
140
+ }
141
+ /**
142
+ * Whether this completion has already been announced for this instance (#2443).
143
+ *
144
+ * A reader that minted no key is always announced — the pre-#2443 behaviour, and
145
+ * the behaviour claude, codex and command-code keep. Records the key as a side
146
+ * effect when the answer is "no", because the two steps must not be separable:
147
+ * an announcement that forgot to record would repeat on the next poll.
148
+ */
149
+ function claimCompletionAnnouncement(key, completionKey) {
150
+ if (completionKey === undefined)
151
+ return true;
152
+ if (announcedCompletions.get(key) === completionKey)
153
+ return false;
154
+ announcedCompletions.set(key, completionKey);
155
+ return true;
124
156
  }
125
157
  /** The queue key; the same triple `buildCompositeKey` spells everywhere else. */
126
158
  function captureKeyOf(worktreeId, cliToolId, instanceId) {
@@ -161,19 +193,19 @@ function serializePerInstance(key, work) {
161
193
  */
162
194
  const PULL_TRANSCRIPT_READERS = {
163
195
  [tool_id_1.CLAUDE_CLI_TOOL_ID]: {
164
- capture: (target, capture) => (0, history_1.captureClaudeTranscriptTurn)(target, capture),
196
+ capture: (target, capture, report) => (0, history_1.captureClaudeTranscriptTurn)(target, capture, report),
165
197
  locate: (target, capture) => (0, history_1.resolveClaudeTranscriptPath)(target, capture),
166
198
  },
167
199
  [tool_id_2.CODEX_CLI_TOOL_ID]: {
168
- capture: (target, capture) => (0, history_2.captureCodexTranscriptTurn)(target, capture),
200
+ capture: (target, capture, report) => (0, history_2.captureCodexTranscriptTurn)(target, capture, report),
169
201
  locate: (target, capture) => (0, history_2.resolveCodexTranscriptPath)(target, capture),
170
202
  },
171
203
  [tool_id_3.ANTIGRAVITY_CLI_TOOL_ID]: {
172
- capture: (target, capture) => (0, history_3.captureAntigravityTranscriptTurn)(target, capture),
204
+ capture: (target, capture, report) => (0, history_3.captureAntigravityTranscriptTurn)(target, capture, report),
173
205
  locate: (target, capture) => (0, history_3.resolveAntigravityTranscriptPath)(target, capture),
174
206
  },
175
207
  [tool_id_4.COMMAND_CODE_CLI_TOOL_ID]: {
176
- capture: (target, capture) => (0, history_4.captureCommandCodeTranscriptTurn)(target, capture),
208
+ capture: (target, capture, report) => (0, history_4.captureCommandCodeTranscriptTurn)(target, capture, report),
177
209
  locate: (target, capture) => (0, history_4.resolveCommandCodeTranscriptPath)(target, capture),
178
210
  },
179
211
  };
@@ -267,12 +299,23 @@ function isStructuredHistoryWriterLive(worktreeId, cliToolId, instanceId) {
267
299
  * not: this runs inside the poller's save path, and an exception here would cost
268
300
  * the scraped reply as well as the structured one.
269
301
  *
302
+ * Since Issue #2436 the `false` can be asked to explain itself: pass a
303
+ * {@link StructuredHistoryCaptureReport} and `outcome` says whether the turn is
304
+ * merely still open — the row is coming, and a caller holding the pane's copy
305
+ * should wait — or whether nothing will ever be written for it. The boolean is
306
+ * unchanged in meaning and in value; see
307
+ * {@link StructuredHistoryCaptureOutcome} for why the third value is reported
308
+ * beside it rather than in it.
309
+ *
270
310
  * @param worktreeId - The worktree
271
311
  * @param cliToolId - The tool driving the pane
272
312
  * @param instanceId - The agent instance; defaults to the primary
273
313
  * @param capture - Where to look; see {@link StructuredHistoryCapture}
314
+ * @param report - Optional out-parameter; see {@link StructuredHistoryCaptureReport}
274
315
  */
275
- async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, capture) {
316
+ async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, capture, report) {
317
+ if (report)
318
+ report.outcome = 'unavailable';
276
319
  if (!isPullTranscriptHistory(cliToolId))
277
320
  return false;
278
321
  const reader = PULL_TRANSCRIPT_READERS[cliToolId];
@@ -285,9 +328,25 @@ async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, c
285
328
  return false;
286
329
  }
287
330
  const resolvedInstanceId = instanceId ?? cliToolId;
288
- return serializePerInstance(captureKeyOf(worktreeId, cliToolId, resolvedInstanceId), async () => {
331
+ const captureKey = captureKeyOf(worktreeId, cliToolId, resolvedInstanceId);
332
+ return serializePerInstance(captureKey, async () => {
333
+ // The reader's own report, kept local and copied out below: `report` is
334
+ // the caller's object and must end up holding one verdict, not whatever
335
+ // the last reader to touch it happened to leave there.
336
+ const readerReport = {};
289
337
  try {
290
- const captured = await reader.capture({ worktreeId, cliToolId, instanceId: resolvedInstanceId }, capture);
338
+ const captured = await reader.capture({ worktreeId, cliToolId, instanceId: resolvedInstanceId }, capture, readerReport);
339
+ // Issue #2443: absent means `settled`, which is what leaves the three
340
+ // readers that have a turn-closing record exactly where they were.
341
+ const completion = readerReport.completion ?? 'settled';
342
+ if (report) {
343
+ // A reader that says nothing is read as `unavailable` — the pre-#2436
344
+ // behaviour, and the safe direction: the scraper keeps the turn.
345
+ report.outcome = captured ? 'captured' : readerReport.outcome ?? 'unavailable';
346
+ report.completion = completion;
347
+ report.completionKey = readerReport.completionKey;
348
+ report.completionReason = readerReport.completionReason;
349
+ }
291
350
  // Issue #2377: the moment a relay is waiting for. This is the single
292
351
  // point BOTH triggers of a pull capture pass through — the poller's save
293
352
  // path and the Stop hook receiver — which is why the relay needs one
@@ -297,8 +356,33 @@ async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, c
297
356
  // Announced only when the capture actually wrote something: a `false`
298
357
  // means the scraper is still the writer for this turn, and the poller's
299
358
  // own call below will announce it.
300
- if (captured)
301
- (0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, true);
359
+ //
360
+ // Issue #2443 adds the second condition, and it is the whole Issue.
361
+ // `captured` means History holds the newest turn; it does **not** mean
362
+ // the agent is finished with it, because antigravity has no record that
363
+ // closes a turn and an interim report is saved under the same key the
364
+ // conclusion will land on. Announcing here would hand a relay the
365
+ // interim body as the answer, and `broadcastMessage('message_updated',
366
+ // …)` — the repair #2438 added — does not take a delivered message
367
+ // back. So a provisional capture announces nothing at all; the relay
368
+ // keeps waiting on the deadline it already has, and the settled capture
369
+ // that follows announces the finished body.
370
+ if (captured && completion === 'settled') {
371
+ if (claimCompletionAnnouncement(captureKey, readerReport.completionKey)) {
372
+ (0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, true);
373
+ }
374
+ }
375
+ else if (captured) {
376
+ logger.info('structured-history-capture-provisional', {
377
+ worktreeId,
378
+ cliToolId,
379
+ instanceId: resolvedInstanceId,
380
+ // The reader's own word for what is missing; see
381
+ // `AntigravityTurnCompletionReason`. This is the line #2443 asks for
382
+ // — the reason left on the relay's waiting path.
383
+ reason: readerReport.completionReason ?? 'unknown',
384
+ });
385
+ }
302
386
  return captured;
303
387
  }
304
388
  catch (error) {