commandmate 0.33.1 → 0.33.2

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 (291) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +290 -290
  3. package/.next/app-path-routes-manifest.json +29 -29
  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 +3 -3
  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_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
  15. package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
  17. package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
  18. package/.next/server/app/api/assistant/messages/route.js +1 -1
  19. package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
  20. package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/assistant/session/route.js +1 -1
  22. package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
  23. package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
  24. package/.next/server/app/api/assistant/start/route.js +32 -1
  25. package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
  26. package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/assistant/terminal/route.js +33 -2
  28. package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
  29. package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
  31. package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/hooks/agent-event/route.js +1 -1
  44. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  45. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/hooks/claude-done/route.js +2 -2
  47. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  48. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/hooks/permission-request/route.js +1 -1
  50. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  51. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/push/escalation/route.js +1 -1
  55. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/push/vapid/route.js +1 -1
  58. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
  60. package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
  61. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/relays/route.js +1 -1
  63. package/.next/server/app/api/relays/route.js.nft.json +1 -1
  64. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  70. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  71. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/repositories/clone/route.js +1 -1
  73. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  74. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/repositories/restore/route.js +1 -1
  77. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  78. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/repositories/route.js +1 -1
  80. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  81. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/repositories/scan/route.js +1 -1
  83. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  84. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/repositories/sync/route.js +1 -1
  86. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  87. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
  90. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
  104. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  108. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  110. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
  113. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  119. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
  149. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  151. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  153. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  154. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  155. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  157. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  159. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  160. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  163. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  164. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  165. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  166. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  167. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  169. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  170. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  173. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  176. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  177. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  179. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  180. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  181. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  182. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  184. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  185. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  186. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  187. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  188. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  189. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  190. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  191. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  192. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  193. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  194. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/terminal/route.js +103 -1
  196. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  197. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  198. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  199. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  200. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  201. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  202. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  203. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  204. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  205. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  206. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  207. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  208. package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
  209. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  210. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
  211. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  212. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  213. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  214. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  215. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  216. package/.next/server/app/api/worktrees/route.js +1 -1
  217. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  218. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  219. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  220. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  221. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  222. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  223. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  224. package/.next/server/app/page_client-reference-manifest.js +1 -1
  225. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  226. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  227. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  228. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  229. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  230. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  231. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  232. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  233. package/.next/server/app/worktrees/[id]/page.js +8 -8
  234. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  235. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  236. package/.next/server/app-paths-manifest.json +29 -29
  237. package/.next/server/chunks/1068.js +1 -0
  238. package/.next/server/chunks/1210.js +1 -1
  239. package/.next/server/chunks/1331.js +2 -2
  240. package/.next/server/chunks/1366.js +5 -3
  241. package/.next/server/chunks/1886.js +2 -2
  242. package/.next/server/chunks/2839.js +1 -0
  243. package/.next/server/chunks/3241.js +1 -1
  244. package/.next/server/chunks/3323.js +1 -0
  245. package/.next/server/chunks/3707.js +1 -1
  246. package/.next/server/chunks/3708.js +1 -1
  247. package/.next/server/chunks/4443.js +1 -1
  248. package/.next/server/chunks/5220.js +1 -1
  249. package/.next/server/chunks/595.js +2 -2
  250. package/.next/server/chunks/650.js +1 -0
  251. package/.next/server/chunks/6781.js +1 -0
  252. package/.next/server/chunks/6808.js +1 -0
  253. package/.next/server/chunks/6838.js +1 -1
  254. package/.next/server/chunks/7875.js +3 -3
  255. package/.next/server/chunks/8918.js +107 -5
  256. package/.next/server/middleware-build-manifest.js +1 -1
  257. package/.next/server/middleware-manifest.json +5 -5
  258. package/.next/server/pages/500.html +1 -1
  259. package/.next/server/server-reference-manifest.json +1 -1
  260. package/.next/server/src/middleware.js.map +1 -1
  261. package/.next/static/chunks/1255-7e5873f06b25e129.js +1 -0
  262. package/.next/static/chunks/{4718-2bc5cdf06573bf54.js → 4718-441a2af91f0374e9.js} +1 -1
  263. package/.next/static/chunks/app/worktrees/[id]/page-38edcd53e51f53b0.js +1 -0
  264. package/.next/static/chunks/{main-6092074a0dc78280.js → main-ac91ed6e71380841.js} +1 -1
  265. package/.next/trace +13 -14
  266. package/dist/server/src/lib/assistant-response-saver.js +168 -12
  267. package/dist/server/src/lib/hooks/sources/antigravity/history.js +204 -9
  268. package/dist/server/src/lib/hooks/sources/claude/history.js +29 -3
  269. package/dist/server/src/lib/hooks/sources/codex/history.js +29 -3
  270. package/dist/server/src/lib/hooks/sources/command-code/history.js +29 -3
  271. package/dist/server/src/lib/hooks/stop-history-capture.js +30 -4
  272. package/dist/server/src/lib/polling/response-checker.js +258 -9
  273. package/dist/server/src/lib/polling/response-poller-core.js +20 -0
  274. package/dist/server/src/lib/polling/structured-history-gate.js +26 -6
  275. package/dist/server/src/lib/response-cleaner.js +84 -0
  276. package/package.json +2 -2
  277. package/.next/server/chunks/21.js +0 -1
  278. package/.next/server/chunks/3086.js +0 -1
  279. package/.next/server/chunks/3270.js +0 -1
  280. package/.next/server/chunks/3925.js +0 -1
  281. package/.next/server/chunks/5043.js +0 -1
  282. package/.next/server/chunks/6306.js +0 -1
  283. package/.next/server/chunks/6363.js +0 -1
  284. package/.next/server/chunks/6491.js +0 -103
  285. package/.next/server/chunks/7890.js +0 -1
  286. package/.next/server/chunks/8063.js +0 -32
  287. package/.next/server/chunks/8687.js +0 -1
  288. package/.next/static/chunks/1255-8cb587d9170b45ed.js +0 -1
  289. package/.next/static/chunks/app/worktrees/[id]/page-13ca0ba551fe5cfb.js +0 -1
  290. /package/.next/static/{9lgw9hA727UMyFhXryGWG → 0a4qrU8WdUwQrlO4mfLMu}/_buildManifest.js +0 -0
  291. /package/.next/static/{9lgw9hA727UMyFhXryGWG → 0a4qrU8WdUwQrlO4mfLMu}/_ssgManifest.js +0 -0
@@ -135,6 +135,7 @@ const path_1 = require("path");
135
135
  const auto_yes_state_1 = require("../../../../lib/auto-yes-state");
136
136
  const transcript_tail_1 = require("../../../../lib/history/transcript-tail");
137
137
  const user_turn_recorder_1 = require("../../../../lib/history/user-turn-recorder");
138
+ const assistant_response_saver_1 = require("../../../../lib/assistant-response-saver");
138
139
  const logger_1 = require("../../../../lib/logger");
139
140
  const agent_transcript_1 = require("../../../../types/agent-transcript");
140
141
  const transcript_1 = require("./transcript");
@@ -325,10 +326,18 @@ async function findCommandCodeTranscriptPath(homeDir, sessionId) {
325
326
  *
326
327
  * Never throws.
327
328
  *
329
+ * Since Issue #2436 the false can explain itself: pass a
330
+ * `StructuredHistoryCaptureReport` and `outcome` is set to `'not_yet_closed'`
331
+ * when the newest turn is one the agent has not finished writing — the case the
332
+ * `-turn-open` line below reports, and the one where the caller's scraped copy
333
+ * is worth holding rather than saving. Every other false leaves it unset, which
334
+ * the gate reads as `'unavailable'`.
335
+ *
328
336
  * @param target - The instance whose turn just ended
337
+ * @param report - Optional out-parameter; see `StructuredHistoryCaptureReport`
329
338
  * @returns Whether History now holds this instance's newest turn as Markdown
330
339
  */
331
- async function captureCommandCodeTranscriptTurn(target, capture) {
340
+ async function captureCommandCodeTranscriptTurn(target, capture, report) {
332
341
  const instanceId = target.instanceId ?? target.cliToolId;
333
342
  try {
334
343
  const homeDir = capture.commandCodeHome ?? (0, os_1.homedir)();
@@ -394,6 +403,9 @@ async function captureCommandCodeTranscriptTurn(target, capture) {
394
403
  instanceId,
395
404
  turnsInWindow: built.turns.length,
396
405
  });
406
+ // Issue #2437: this turn is already History's Markdown, so the pane rows
407
+ // behind it must stop being "unsaved output" the pre-send flush can pick up.
408
+ await (0, assistant_response_saver_1.advanceCapturedLineForTranscriptTurn)(target);
397
409
  return true;
398
410
  }
399
411
  if (pending.turns.length > 1) {
@@ -426,7 +438,14 @@ async function captureCommandCodeTranscriptTurn(target, capture) {
426
438
  let captured = false;
427
439
  for (let index = 0; index < pending.turns.length; index += 1) {
428
440
  const turn = pending.turns[index];
429
- captured = await writeCommandCodeTurn(target, turn, (0, transcript_1.renderCommandCodeTurn)(turn), resolveAssistantTimestampMs(turn, userRows[index], lastRecordAt.get(turn.promptId) ?? 0, nextTurnOpensAt(pending.turns, userRows, index)), path);
441
+ captured = await writeCommandCodeTurn(target, turn, (0, transcript_1.renderCommandCodeTurn)(turn), resolveAssistantTimestampMs(turn, userRows[index], lastRecordAt.get(turn.promptId) ?? 0, nextTurnOpensAt(pending.turns, userRows, index)), path, report);
442
+ }
443
+ if (captured) {
444
+ // Issue #2437: History now holds this turn as the agent's own Markdown.
445
+ // Park the pre-send flush's cursor past the pane rows it covers, or a
446
+ // `/send` arriving before the next poll tick saves the whole finished turn
447
+ // a second time.
448
+ await (0, assistant_response_saver_1.advanceCapturedLineForTranscriptTurn)(target);
430
449
  }
431
450
  return captured;
432
451
  }
@@ -773,8 +792,13 @@ async function refreshCommandCodeTurnRows(target, candidates, path) {
773
792
  *
774
793
  * @returns Whether History holds this turn as the agent's own Markdown
775
794
  */
776
- async function writeCommandCodeTurn(target, turn, rendered, timestampMs, path) {
795
+ async function writeCommandCodeTurn(target, turn, rendered, timestampMs, path, report) {
777
796
  const instanceId = target.instanceId ?? target.cliToolId;
797
+ // Issue #2436: the caller's report is about the turn THIS call is deciding.
798
+ // The loop above walks oldest-first, so a verdict left by an earlier turn has
799
+ // to be cleared before this one's is written.
800
+ if (report)
801
+ report.outcome = undefined;
778
802
  if (!(0, transcript_1.isCommandCodeTurnWritable)(turn)) {
779
803
  // The agent's last word in this turn reached for a tool and no later prompt
780
804
  // has taken over, so what is in the file is a turn in progress. Writing it
@@ -789,6 +813,8 @@ async function writeCommandCodeTurn(target, turn, rendered, timestampMs, path) {
789
813
  // Handing it back to the scraper costs the Markdown rendering for this turn
790
814
  // and nothing else: the Stop receiver asks again after a short delay, and
791
815
  // the poller asks again when the pane returns to the composer.
816
+ if (report)
817
+ report.outcome = 'not_yet_closed';
792
818
  logger.info('command-code-transcript-turn-open', {
793
819
  worktreeId: target.worktreeId,
794
820
  instanceId,
@@ -248,7 +248,12 @@ async function readAfterResponding(gate, worktree, cliToolId, instanceId, captur
248
248
  try {
249
249
  for (const [index, delayMs] of delaysMs.entries()) {
250
250
  await sleep(delayMs);
251
- const captured = await gate.captureStructuredHistoryTurn(worktree.id, cliToolId, instanceId, capture);
251
+ // Issue #2436: the boolean is the verdict, the report says why. Read
252
+ // explicitly rather than as truthiness — an open turn answers `false`
253
+ // here and must keep the loop going, which is exactly the misreading the
254
+ // Issue's requirement C is about.
255
+ const report = {};
256
+ const captured = await gate.captureStructuredHistoryTurn(worktree.id, cliToolId, instanceId, capture, report);
252
257
  if (captured) {
253
258
  // The line an operator greps when a codex reply did reach the chat
254
259
  // surface: the receiver's own `agent-event-stop-applied` cannot carry
@@ -269,6 +274,10 @@ async function readAfterResponding(gate, worktree, cliToolId, instanceId, captur
269
274
  instanceId,
270
275
  attempt: index + 1,
271
276
  attempts: delaysMs.length,
277
+ // Issue #2436: `not_yet_closed` here means the next read is worth
278
+ // taking; anything else means these delays are being spent on a turn
279
+ // that will never be written by this reader.
280
+ outcome: report.outcome ?? 'unavailable',
272
281
  elapsedMs: Date.now() - startedAt,
273
282
  });
274
283
  }
@@ -324,7 +333,15 @@ async function resolveStopTranscriptCapture(worktree, cliToolId, instanceId, opt
324
333
  // found that way has no session pointer either, and therefore sends no stop
325
334
  // event this path could act on.
326
335
  const capture = { worktreePath: worktree.path, transcriptPathHint: null };
327
- if (await gate.captureStructuredHistoryTurn(worktree.id, cliToolId, instanceId, capture)) {
336
+ // Issue #2436: `captureStructuredHistoryTurn` still answers a boolean, and
337
+ // `true` still means — and only means — "the row is written NOW". That is
338
+ // load-bearing here: the three statuses below are decided by reading this
339
+ // value as a boolean, and a tri-state RETURN would have made every one of
340
+ // them truthy, turning an open turn into `captured` and cancelling the very
341
+ // reads that go on to write it. The third value arrives beside the boolean
342
+ // instead, in `firstReport`, and is used explicitly.
343
+ const firstReport = {};
344
+ if (await gate.captureStructuredHistoryTurn(worktree.id, cliToolId, instanceId, capture, firstReport)) {
328
345
  return { status: 'captured' };
329
346
  }
330
347
  // Asked once, before any waiting, on either path: every failure other than
@@ -333,7 +350,14 @@ async function resolveStopTranscriptCapture(worktree, cliToolId, instanceId, opt
333
350
  // nor deferring can fix, and an instance with no transcript will not have
334
351
  // one in 150 ms either. A transcript that exists does not stop existing
335
352
  // between attempts, so this is not re-asked.
336
- const hasTranscript = () => gate.hasStructuredHistoryTranscript(worktree.id, cliToolId, instanceId, capture);
353
+ //
354
+ // Issue #2436: a reader that answered `not_yet_closed` HAS just read the
355
+ // file — that is what "the turn in it is still open" is a statement about —
356
+ // so the probe is skipped and the answer taken from the report. The
357
+ // fallback is the pre-#2436 round-trip, for a reader that reported nothing.
358
+ const hasTranscript = () => firstReport.outcome === 'not_yet_closed'
359
+ ? Promise.resolve(true)
360
+ : gate.hasStructuredHistoryTranscript(worktree.id, cliToolId, instanceId, capture);
337
361
  if (exports.STOP_HOOK_BLOCKS_TRANSCRIPT_CLOSE.has(cliToolId)) {
338
362
  const delaysMs = options.deferredDelaysMs ?? exports.STOP_TRANSCRIPT_DEFERRED_DELAYS_MS;
339
363
  if (delaysMs.length === 0)
@@ -357,7 +381,8 @@ async function resolveStopTranscriptCapture(worktree, cliToolId, instanceId, opt
357
381
  const maxAttempts = Math.max(1, options.maxAttempts ?? exports.STOP_TRANSCRIPT_MAX_ATTEMPTS);
358
382
  for (let attempt = 2; attempt <= maxAttempts; attempt += 1) {
359
383
  await sleep(retryDelayMs);
360
- const captured = await gate.captureStructuredHistoryTurn(worktree.id, cliToolId, instanceId, capture);
384
+ const report = {};
385
+ const captured = await gate.captureStructuredHistoryTurn(worktree.id, cliToolId, instanceId, capture, report);
361
386
  logger.debug('stop-history-capture-retried', {
362
387
  worktreeId: worktree.id,
363
388
  cliToolId,
@@ -365,6 +390,7 @@ async function resolveStopTranscriptCapture(worktree, cliToolId, instanceId, opt
365
390
  attempt,
366
391
  maxAttempts,
367
392
  captured,
393
+ outcome: report.outcome ?? 'unavailable',
368
394
  });
369
395
  if (captured)
370
396
  return { status: 'captured' };
@@ -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.
@@ -161,19 +161,19 @@ function serializePerInstance(key, work) {
161
161
  */
162
162
  const PULL_TRANSCRIPT_READERS = {
163
163
  [tool_id_1.CLAUDE_CLI_TOOL_ID]: {
164
- capture: (target, capture) => (0, history_1.captureClaudeTranscriptTurn)(target, capture),
164
+ capture: (target, capture, report) => (0, history_1.captureClaudeTranscriptTurn)(target, capture, report),
165
165
  locate: (target, capture) => (0, history_1.resolveClaudeTranscriptPath)(target, capture),
166
166
  },
167
167
  [tool_id_2.CODEX_CLI_TOOL_ID]: {
168
- capture: (target, capture) => (0, history_2.captureCodexTranscriptTurn)(target, capture),
168
+ capture: (target, capture, report) => (0, history_2.captureCodexTranscriptTurn)(target, capture, report),
169
169
  locate: (target, capture) => (0, history_2.resolveCodexTranscriptPath)(target, capture),
170
170
  },
171
171
  [tool_id_3.ANTIGRAVITY_CLI_TOOL_ID]: {
172
- capture: (target, capture) => (0, history_3.captureAntigravityTranscriptTurn)(target, capture),
172
+ capture: (target, capture, report) => (0, history_3.captureAntigravityTranscriptTurn)(target, capture, report),
173
173
  locate: (target, capture) => (0, history_3.resolveAntigravityTranscriptPath)(target, capture),
174
174
  },
175
175
  [tool_id_4.COMMAND_CODE_CLI_TOOL_ID]: {
176
- capture: (target, capture) => (0, history_4.captureCommandCodeTranscriptTurn)(target, capture),
176
+ capture: (target, capture, report) => (0, history_4.captureCommandCodeTranscriptTurn)(target, capture, report),
177
177
  locate: (target, capture) => (0, history_4.resolveCommandCodeTranscriptPath)(target, capture),
178
178
  },
179
179
  };
@@ -267,12 +267,23 @@ function isStructuredHistoryWriterLive(worktreeId, cliToolId, instanceId) {
267
267
  * not: this runs inside the poller's save path, and an exception here would cost
268
268
  * the scraped reply as well as the structured one.
269
269
  *
270
+ * Since Issue #2436 the `false` can be asked to explain itself: pass a
271
+ * {@link StructuredHistoryCaptureReport} and `outcome` says whether the turn is
272
+ * merely still open — the row is coming, and a caller holding the pane's copy
273
+ * should wait — or whether nothing will ever be written for it. The boolean is
274
+ * unchanged in meaning and in value; see
275
+ * {@link StructuredHistoryCaptureOutcome} for why the third value is reported
276
+ * beside it rather than in it.
277
+ *
270
278
  * @param worktreeId - The worktree
271
279
  * @param cliToolId - The tool driving the pane
272
280
  * @param instanceId - The agent instance; defaults to the primary
273
281
  * @param capture - Where to look; see {@link StructuredHistoryCapture}
282
+ * @param report - Optional out-parameter; see {@link StructuredHistoryCaptureReport}
274
283
  */
275
- async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, capture) {
284
+ async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, capture, report) {
285
+ if (report)
286
+ report.outcome = 'unavailable';
276
287
  if (!isPullTranscriptHistory(cliToolId))
277
288
  return false;
278
289
  const reader = PULL_TRANSCRIPT_READERS[cliToolId];
@@ -286,8 +297,17 @@ async function captureStructuredHistoryTurn(worktreeId, cliToolId, instanceId, c
286
297
  }
287
298
  const resolvedInstanceId = instanceId ?? cliToolId;
288
299
  return serializePerInstance(captureKeyOf(worktreeId, cliToolId, resolvedInstanceId), async () => {
300
+ // The reader's own report, kept local and copied out below: `report` is
301
+ // the caller's object and must end up holding one verdict, not whatever
302
+ // the last reader to touch it happened to leave there.
303
+ const readerReport = {};
289
304
  try {
290
- const captured = await reader.capture({ worktreeId, cliToolId, instanceId: resolvedInstanceId }, capture);
305
+ const captured = await reader.capture({ worktreeId, cliToolId, instanceId: resolvedInstanceId }, capture, readerReport);
306
+ if (report) {
307
+ // A reader that says nothing is read as `unavailable` — the pre-#2436
308
+ // behaviour, and the safe direction: the scraper keeps the turn.
309
+ report.outcome = captured ? 'captured' : readerReport.outcome ?? 'unavailable';
310
+ }
291
311
  // Issue #2377: the moment a relay is waiting for. This is the single
292
312
  // point BOTH triggers of a pull capture pass through — the poller's save
293
313
  // path and the Stop hook receiver — which is why the relay needs one