commandmate 0.47.2 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +180 -180
  3. package/.next/app-path-routes-manifest.json +30 -30
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/prerender-manifest.json +9 -9
  6. package/.next/required-server-files.json +1 -1
  7. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  8. package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
  9. package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
  10. package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/app/update/route.js +3 -3
  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/auth/login/route_client-reference-manifest.js +1 -1
  15. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/capabilities/route.js +1 -1
  18. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
  22. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  24. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  26. package/.next/server/app/api/hooks/agent-event/route.js +2 -2
  27. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  28. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/hooks/claude-done/route.js +2 -2
  30. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  31. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/hooks/permission-request/route.js +1 -1
  33. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  34. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/push/escalation/route.js +1 -1
  38. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/push/vapid/route.js +1 -1
  41. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
  43. package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
  44. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/relays/route.js +1 -1
  46. package/.next/server/app/api/relays/route.js.nft.json +1 -1
  47. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  51. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  52. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/repositories/clone/route.js +1 -1
  54. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  55. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/repositories/restore/route.js +1 -1
  58. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  59. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/repositories/route.js +1 -1
  61. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  62. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/repositories/scan/route.js +1 -1
  64. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  65. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/repositories/sync/route.js +1 -1
  67. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  68. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/settings/default-agents/route.js +1 -1
  71. package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
  72. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  88. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  89. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
  91. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  92. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
  94. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
  95. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  100. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  101. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/worktrees/[id]/direct-input/route.js +1 -1
  103. package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
  104. package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
  135. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  138. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  141. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/opencode/session/route.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/opencode/session/route.js.nft.json +1 -1
  151. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/opencode/share/route.js +1 -1
  153. package/.next/server/app/api/worktrees/[id]/opencode/share/route.js.nft.json +1 -1
  154. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  155. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  157. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/prompt-response/strict/route.js +1 -1
  159. package/.next/server/app/api/worktrees/[id]/prompt-response/strict/route.js.nft.json +1 -1
  160. package/.next/server/app/api/worktrees/[id]/prompt-response/strict/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  163. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  164. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  165. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  166. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  167. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  169. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  170. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  173. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  174. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +3 -3
  176. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  177. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  179. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  180. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  181. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  182. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  184. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  185. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  186. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  187. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  188. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  189. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  190. package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -103
  191. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  192. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  193. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  194. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  196. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  197. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  198. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  199. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  200. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  201. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  202. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  203. package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
  204. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  205. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
  206. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  207. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  208. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  209. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  210. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  211. package/.next/server/app/api/worktrees/route.js +1 -1
  212. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  213. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  214. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  215. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  216. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  217. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  218. package/.next/server/app/page_client-reference-manifest.js +1 -1
  219. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  220. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  221. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  222. package/.next/server/app/sessions/page.js +1 -1
  223. package/.next/server/app/sessions/page.js.nft.json +1 -1
  224. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  225. package/.next/server/app/skills/[skillId]/page.js +1 -1
  226. package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
  227. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  228. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  229. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  230. package/.next/server/app/worktrees/[id]/files/[...path]/page.js +2 -2
  231. package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +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 +12 -12
  234. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  235. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  236. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  237. package/.next/server/app-paths-manifest.json +30 -30
  238. package/.next/server/chunks/1191.js +11 -4
  239. package/.next/server/chunks/1501.js +1 -1
  240. package/.next/server/chunks/1601.js +2 -2
  241. package/.next/server/chunks/1656.js +2 -2
  242. package/.next/server/chunks/1945.js +3 -3
  243. package/.next/server/chunks/1990.js +103 -0
  244. package/.next/server/chunks/2069.js +1 -0
  245. package/.next/server/chunks/2119.js +1 -1
  246. package/.next/server/chunks/2404.js +1 -1
  247. package/.next/server/chunks/2422.js +1 -1
  248. package/.next/server/chunks/2445.js +1 -1
  249. package/.next/server/chunks/2534.js +1 -1
  250. package/.next/server/chunks/255.js +1 -1
  251. package/.next/server/chunks/3241.js +1 -1
  252. package/.next/server/chunks/3505.js +2 -2
  253. package/.next/server/chunks/4243.js +1 -1
  254. package/.next/server/chunks/4248.js +1 -1
  255. package/.next/server/chunks/4280.js +1 -0
  256. package/.next/server/chunks/5209.js +1 -1
  257. package/.next/server/chunks/6119.js +1 -1
  258. package/.next/server/chunks/6130.js +1 -1
  259. package/.next/server/chunks/6401.js +1 -1
  260. package/.next/server/chunks/6486.js +9 -8
  261. package/.next/server/chunks/6781.js +1 -1
  262. package/.next/server/chunks/720.js +103 -0
  263. package/.next/server/chunks/7604.js +2 -0
  264. package/.next/server/chunks/7905.js +1 -1
  265. package/.next/server/chunks/8581.js +8 -8
  266. package/.next/server/chunks/8728.js +3 -3
  267. package/.next/server/chunks/8791.js +1 -1
  268. package/.next/server/chunks/8807.js +19 -18
  269. package/.next/server/chunks/8918.js +2 -104
  270. package/.next/server/chunks/936.js +1 -1
  271. package/.next/server/chunks/9390.js +1 -1
  272. package/.next/server/chunks/9540.js +1 -1
  273. package/.next/server/chunks/9627.js +5 -5
  274. package/.next/server/chunks/9731.js +2 -2
  275. package/.next/server/middleware-manifest.json +5 -5
  276. package/.next/server/pages/500.html +1 -1
  277. package/.next/server/server-reference-manifest.json +1 -1
  278. package/.next/static/chunks/1128-914c559913546bbc.js +1 -0
  279. package/.next/static/chunks/{2407-a6533805a86d05bd.js → 2407-ab92ae9837c39dce.js} +1 -1
  280. package/.next/static/chunks/{2863-6a07ec82fb52f4dd.js → 2863-7173dc305dffc4f6.js} +1 -1
  281. package/.next/static/chunks/6288-6fadbba6e84393b2.js +1 -0
  282. package/.next/static/chunks/7665-e9ef6e0a85b9fa49.js +1 -0
  283. package/.next/static/chunks/8407-35bbfc84793d7004.js +1 -0
  284. package/.next/static/chunks/9079-987939d231704218.js +1 -0
  285. package/.next/static/chunks/app/{layout-538d8d2746ad1588.js → layout-aa8e66164a4367af.js} +1 -1
  286. package/.next/static/chunks/app/sessions/page-cfd645305e25fd33.js +1 -0
  287. package/.next/static/chunks/app/skills/[skillId]/{page-52ba58712faf3378.js → page-cb6e74fd9a6a571d.js} +1 -1
  288. package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-704086656fb45699.js +1 -0
  289. package/.next/static/chunks/app/worktrees/[id]/page-a8d7886703ffc7b4.js +1 -0
  290. package/.next/static/css/{743f79ff19ab91e9.css → db8959555c3d9c96.css} +1 -1
  291. package/.next/trace +13 -13
  292. package/dist/cli/commands/auto-yes.d.ts +24 -0
  293. package/dist/cli/commands/auto-yes.d.ts.map +1 -1
  294. package/dist/cli/commands/auto-yes.js +76 -8
  295. package/dist/cli/commands/capture.d.ts.map +1 -1
  296. package/dist/cli/commands/capture.js +5 -0
  297. package/dist/cli/commands/instances.d.ts.map +1 -1
  298. package/dist/cli/commands/instances.js +10 -1
  299. package/dist/cli/commands/prompt.d.ts.map +1 -1
  300. package/dist/cli/commands/prompt.js +5 -3
  301. package/dist/cli/commands/send.d.ts.map +1 -1
  302. package/dist/cli/commands/send.js +19 -6
  303. package/dist/cli/commands/verify.d.ts.map +1 -1
  304. package/dist/cli/commands/verify.js +8 -1
  305. package/dist/cli/commands/wait.d.ts.map +1 -1
  306. package/dist/cli/commands/wait.js +101 -13
  307. package/dist/cli/config/duration-constants.d.ts +16 -13
  308. package/dist/cli/config/duration-constants.d.ts.map +1 -1
  309. package/dist/cli/config/duration-constants.js +21 -21
  310. package/dist/cli/docs/agent-operations.d.ts +2 -2
  311. package/dist/cli/docs/agent-operations.d.ts.map +1 -1
  312. package/dist/cli/docs/agent-operations.js +29 -1
  313. package/dist/cli/types/api-responses.d.ts +145 -3
  314. package/dist/cli/types/api-responses.d.ts.map +1 -1
  315. package/dist/cli/types/index.d.ts +4 -0
  316. package/dist/cli/types/index.d.ts.map +1 -1
  317. package/dist/cli/utils/resource-resolvers.d.ts +0 -18
  318. package/dist/cli/utils/resource-resolvers.d.ts.map +1 -1
  319. package/dist/cli/utils/resource-resolvers.js +1 -51
  320. package/dist/cli/utils/verify-runner.d.ts +24 -1
  321. package/dist/cli/utils/verify-runner.d.ts.map +1 -1
  322. package/dist/cli/utils/verify-runner.js +54 -1
  323. package/dist/config/auto-yes-duration.d.ts +56 -0
  324. package/dist/config/auto-yes-duration.d.ts.map +1 -0
  325. package/dist/config/auto-yes-duration.js +76 -0
  326. package/dist/lib/polling/auto-yes-suppression-reason.d.ts +54 -2
  327. package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -1
  328. package/dist/lib/polling/auto-yes-suppression-reason.js +33 -2
  329. package/dist/lib/prompt-observation/types.d.ts +13 -0
  330. package/dist/lib/prompt-observation/types.d.ts.map +1 -1
  331. package/dist/server/locales/en/worktree.json +2 -1
  332. package/dist/server/locales/ja/worktree.json +2 -1
  333. package/dist/server/src/config/auto-yes-config.js +14 -19
  334. package/dist/server/src/config/auto-yes-duration.js +76 -0
  335. package/dist/server/src/lib/auto-yes-poller-answer-target.js +38 -0
  336. package/dist/server/src/lib/auto-yes-poller-keys.js +35 -0
  337. package/dist/server/src/lib/auto-yes-poller-session-presence.js +26 -0
  338. package/dist/server/src/lib/auto-yes-poller-tick.js +233 -0
  339. package/dist/server/src/lib/auto-yes-poller.js +337 -376
  340. package/dist/server/src/lib/auto-yes-state.js +59 -4
  341. package/dist/server/src/lib/cli-tools/submit-verified-sender.js +235 -19
  342. package/dist/server/src/lib/db/chat-db.js +5 -1
  343. package/dist/server/src/lib/db/verification-db.js +23 -2
  344. package/dist/server/src/lib/detection/composer-text.js +215 -11
  345. package/dist/server/src/lib/detection/option-recommendation.js +120 -0
  346. package/dist/server/src/lib/detection/prompt-detect-multiple-choice-approval-context.js +290 -0
  347. package/dist/server/src/lib/detection/prompt-detect-multiple-choice-chrome.js +193 -0
  348. package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +402 -539
  349. package/dist/server/src/lib/detection/prompt-detector.js +92 -21
  350. package/dist/server/src/lib/detection/prompt-screen-key.js +60 -0
  351. package/dist/server/src/lib/detection/tools/antigravity/dialog.js +158 -35
  352. package/dist/server/src/lib/detection/tools/claude/detect.js +111 -10
  353. package/dist/server/src/lib/detection/tools/command-code/dialog.js +67 -0
  354. package/dist/server/src/lib/detection/tools/dialog-block.js +16 -0
  355. package/dist/server/src/lib/detection/upstream-faults.js +19 -2
  356. package/dist/server/src/lib/detection/waiting-screen-reading.js +98 -0
  357. package/dist/server/src/lib/hooks/permission-decision-service.js +7 -0
  358. package/dist/server/src/lib/hooks/structured-decision-response.js +1 -1
  359. package/dist/server/src/lib/polling/auto-yes-confirm-page.js +2 -2
  360. package/dist/server/src/lib/polling/auto-yes-confirmation-ledger-types.js +3 -0
  361. package/dist/server/src/lib/polling/auto-yes-confirmation-ledger.js +34 -3
  362. package/dist/server/src/lib/polling/auto-yes-decision-state.js +195 -0
  363. package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +139 -10
  364. package/dist/server/src/lib/polling/auto-yes-enter-fallback.js +14 -0
  365. package/dist/server/src/lib/polling/auto-yes-manual-answer.js +124 -0
  366. package/dist/server/src/lib/polling/auto-yes-option-effect.js +157 -0
  367. package/dist/server/src/lib/polling/auto-yes-policy.js +11 -5
  368. package/dist/server/src/lib/polling/auto-yes-poller-capture.js +15 -2
  369. package/dist/server/src/lib/polling/auto-yes-poller-generation.js +42 -1
  370. package/dist/server/src/lib/polling/auto-yes-poller-receipt-log.js +42 -5
  371. package/dist/server/src/lib/polling/auto-yes-poller-state.js +147 -49
  372. package/dist/server/src/lib/polling/auto-yes-poller-steps.js +172 -2
  373. package/dist/server/src/lib/polling/auto-yes-prompt-suppression.js +2 -2
  374. package/dist/server/src/lib/polling/auto-yes-question-choice.js +57 -0
  375. package/dist/server/src/lib/polling/auto-yes-resolver.js +96 -21
  376. package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +33 -2
  377. package/dist/server/src/lib/polling/auto-yes-suppression-state.js +25 -1
  378. package/dist/server/src/lib/polling/prompt-screen-reading.js +232 -0
  379. package/dist/server/src/lib/polling/response-checker-dialog-gate.js +39 -0
  380. package/dist/server/src/lib/polling/response-checker.js +145 -58
  381. package/dist/server/src/lib/prompt-answer-sender.js +104 -10
  382. package/dist/server/src/lib/push/notification-dedup.js +15 -2
  383. package/dist/server/src/lib/push/prompt-card-state.js +140 -19
  384. package/dist/server/src/lib/push/prompt-push-gate.js +158 -12
  385. package/dist/server/src/lib/push/push-sender.js +23 -3
  386. package/dist/server/src/lib/push/resolution-push-notifier.js +14 -0
  387. package/dist/server/src/lib/push/waiting-push-notifier.js +139 -17
  388. package/dist/server/src/lib/realtime/terminal-broadcast.js +16 -0
  389. package/dist/server/src/lib/session/agent-event-state.js +25 -5
  390. package/dist/server/src/lib/session/agent-event-turn.js +140 -46
  391. package/dist/server/src/lib/session/cli-session.js +21 -2
  392. package/dist/server/src/lib/session/composer-clear.js +56 -4
  393. package/dist/server/src/lib/session/current-output-builder.js +66 -12
  394. package/dist/server/src/lib/session/send-attempt-state.js +24 -7
  395. package/dist/server/src/lib/session/waiting-episode-binding-observation.js +289 -0
  396. package/dist/server/src/lib/session/waiting-episode-binding-resolve.js +38 -0
  397. package/dist/server/src/lib/session/waiting-episode-binding.js +276 -0
  398. package/dist/server/src/lib/session/waiting-episode-state-ops.js +152 -0
  399. package/dist/server/src/lib/session/waiting-episode-state-store.js +202 -0
  400. package/dist/server/src/lib/session/waiting-episode-state-types.js +6 -0
  401. package/dist/server/src/lib/session/waiting-episode-state.js +68 -91
  402. package/dist/server/src/lib/session/waiting-episode-verdict.js +174 -0
  403. package/dist/server/src/lib/tmux/tmux-capture-cache.js +76 -11
  404. package/dist/server/src/lib/tmux/tmux.js +79 -9
  405. package/dist/server/src/lib/verification/gate-runner.js +172 -16
  406. package/dist/server/src/lib/verification/machine-lock.js +5 -0
  407. package/dist/server/src/lib/ws-server.js +18 -3
  408. package/dist/server/src/types/auto-yes-answer-mode.js +38 -0
  409. package/dist/server/src/types/auto-yes-decision.js +61 -0
  410. package/dist/server/src/types/auto-yes.js +6 -0
  411. package/dist/server/src/types/prompt-reading.js +32 -0
  412. package/dist/types/auto-yes-answer-mode.d.ts +35 -0
  413. package/dist/types/auto-yes-answer-mode.d.ts.map +1 -0
  414. package/dist/types/auto-yes-answer-mode.js +38 -0
  415. package/docs/en/user-guide/webapp-guide.md +47 -13
  416. package/package.json +1 -1
  417. package/public/sw.js +76 -4
  418. package/release-notes/0.48.0.json +57 -0
  419. package/release-notes/0.49.0.json +45 -0
  420. package/.next/server/chunks/1559.js +0 -2
  421. package/.next/server/chunks/2460.js +0 -1
  422. package/.next/server/chunks/5595.js +0 -1
  423. package/.next/server/chunks/610.js +0 -1
  424. package/.next/server/chunks/6722.js +0 -1
  425. package/.next/static/chunks/5371-a549a0d2c2044d41.js +0 -1
  426. package/.next/static/chunks/602-1c1488959215e140.js +0 -1
  427. package/.next/static/chunks/6288-09feba367cc2c65e.js +0 -1
  428. package/.next/static/chunks/8407-155f84e5ba05f1d5.js +0 -1
  429. package/.next/static/chunks/9079-24f9fa549ed298f1.js +0 -1
  430. package/.next/static/chunks/app/sessions/page-e02f1adb1d1c1ccc.js +0 -1
  431. package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9cf1ecc59e36a004.js +0 -1
  432. package/.next/static/chunks/app/worktrees/[id]/page-46aced20b2b1c010.js +0 -1
  433. /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → 3_JtAZaKMJEDFIIfkHv4n}/_buildManifest.js +0 -0
  434. /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → 3_JtAZaKMJEDFIIfkHv4n}/_ssgManifest.js +0 -0
@@ -16,6 +16,7 @@ exports.buildCompositeKey = buildCompositeKey;
16
16
  exports.extractWorktreeId = extractWorktreeId;
17
17
  exports.extractCliToolId = extractCliToolId;
18
18
  exports.extractInstanceId = extractInstanceId;
19
+ exports.autoYesAnswerModeOf = autoYesAnswerModeOf;
19
20
  exports.isAutoYesExpired = isAutoYesExpired;
20
21
  exports.getAutoYesState = getAutoYesState;
21
22
  exports.setAutoYesEnabled = setAutoYesEnabled;
@@ -36,12 +37,14 @@ exports.getCompositeKeysByWorktree = getCompositeKeysByWorktree;
36
37
  exports.deleteAutoYesStateByWorktree = deleteAutoYesStateByWorktree;
37
38
  exports.getEnabledAutoYesByWorktree = getEnabledAutoYesByWorktree;
38
39
  exports.getRestartReleasedAutoYesByWorktree = getRestartReleasedAutoYesByWorktree;
40
+ exports.getEnabledAutoYesAnswerModeByWorktree = getEnabledAutoYesAnswerModeByWorktree;
39
41
  exports.migrateAutoYesStateWorktreeIds = migrateAutoYesStateWorktreeIds;
40
42
  exports.calculateBackoffInterval = calculateBackoffInterval;
41
43
  const auto_yes_config_1 = require("../config/auto-yes-config");
42
44
  const path_validator_1 = require("./security/path-validator");
43
45
  const types_1 = require("./cli-tools/types");
44
46
  const logger_1 = require("../lib/logger");
47
+ const auto_yes_answer_mode_1 = require("../types/auto-yes-answer-mode");
45
48
  const global_state_1 = require("./global-state");
46
49
  const logger = (0, logger_1.createLogger)('auto-yes-state');
47
50
  // =============================================================================
@@ -124,6 +127,14 @@ function extractInstanceId(compositeKey) {
124
127
  // parts[2] = alias instanceId; fall back to parts[1] (cliToolId) for primary keys.
125
128
  return parts[2] ?? parts[1] ?? null;
126
129
  }
130
+ /**
131
+ * The answer mode a state stands for (Issue #3863): its own, or
132
+ * `recommended` for a state that predates the field. Not meaningful on a
133
+ * disabled state, whose mode a later enable does not inherit.
134
+ */
135
+ function autoYesAnswerModeOf(state) {
136
+ return state.answerMode ?? auto_yes_answer_mode_1.DEFAULT_AUTO_YES_ANSWER_MODE;
137
+ }
127
138
  /** In-memory storage for auto-yes states (globalThis for hot reload persistence) */
128
139
  const autoYesStates = (0, global_state_1.getOrInitGlobal)('__autoYesStates', () => new Map());
129
140
  // =============================================================================
@@ -192,14 +203,28 @@ function getAutoYesState(worktreeId, cliToolId = 'claude', instanceId) {
192
203
  * @param worktreeId - Worktree identifier
193
204
  * @param cliToolId - CLI tool type
194
205
  * @param enabled - Whether to enable or disable auto-yes
195
- * @param duration - Optional duration in milliseconds (must be an ALLOWED_DURATIONS value).
196
- * Defaults to DEFAULT_AUTO_YES_DURATION (1 hour) when omitted.
206
+ * @param duration - Optional duration in milliseconds (must be an AUTO_YES_DURATIONS value,
207
+ * 1〜24 whole hours). Defaults to DEFAULT_AUTO_YES_DURATION (1 hour) when omitted.
208
+ * Issue #3913: checked at run time too — an enable with any other
209
+ * value throws a RangeError and leaves the state as it was.
197
210
  * @param stopPattern - Optional regex pattern for stop condition (Issue #314).
198
211
  * @param instanceId - Optional instance identifier (Issue #896; defaults to primary).
199
- */
200
- function setAutoYesEnabled(worktreeId, cliToolId, enabled, duration, stopPattern, instanceId) {
212
+ * @param answerMode - Issue #3863: how questions are answered. Omitted, an
213
+ * enable over a LIVE grant keeps that grant's mode, and any other enable —
214
+ * from off, or over a grant that has run out — starts at `recommended`.
215
+ * Whether the grant is live is read through {@link getAutoYesState}, which
216
+ * turns an expired grant into a disabled one first, so an enable at or after
217
+ * the deadline is a new grant, not a continuation. Ignored when disabling.
218
+ */
219
+ function setAutoYesEnabled(worktreeId, cliToolId, enabled, duration, stopPattern, instanceId, answerMode) {
201
220
  const key = buildCompositeKey(worktreeId, cliToolId, instanceId);
202
221
  if (enabled) {
222
+ // Issue #3913: the type is not proof (a cast, or a caller outside the
223
+ // route's check), so the value is checked before anything is written.
224
+ if (duration !== undefined && !(0, auto_yes_config_1.isAllowedDuration)(duration)) {
225
+ throw new RangeError(`Invalid Auto-Yes duration: ${String(duration)} (must be 1-24 whole hours in ms)`);
226
+ }
227
+ const current = answerMode === undefined ? getAutoYesState(worktreeId, cliToolId, instanceId) : null;
203
228
  const now = Date.now();
204
229
  const effectiveDuration = duration ?? auto_yes_config_1.DEFAULT_AUTO_YES_DURATION;
205
230
  const state = {
@@ -207,6 +232,7 @@ function setAutoYesEnabled(worktreeId, cliToolId, enabled, duration, stopPattern
207
232
  enabledAt: now,
208
233
  expiresAt: now + effectiveDuration,
209
234
  stopPattern,
235
+ answerMode: answerMode ?? (current?.enabled ? autoYesAnswerModeOf(current) : auto_yes_answer_mode_1.DEFAULT_AUTO_YES_ANSWER_MODE),
210
236
  };
211
237
  autoYesStates.set(key, state);
212
238
  notifyChangeAfterShutdown();
@@ -681,6 +707,35 @@ function getRestartReleasedAutoYesByWorktree() {
681
707
  }
682
708
  return byWorktree;
683
709
  }
710
+ /**
711
+ * The answer mode of every armed instance, grouped by worktree and keyed by
712
+ * instance, for the worktree list (Issue #3863).
713
+ *
714
+ * Its own map rather than a field on {@link getEnabledAutoYesByWorktree}'s
715
+ * entries, whose `{ enabled, expiresAt }` shape the list pins (#2512). The same
716
+ * instances that map holds (armed, read through {@link getAutoYesState} so an
717
+ * expiry resolved at read time drops out), each with the mode it runs under.
718
+ *
719
+ * @returns worktreeId -> instanceId -> mode; worktrees with nothing armed are absent
720
+ */
721
+ function getEnabledAutoYesAnswerModeByWorktree() {
722
+ const byWorktree = new Map();
723
+ // A snapshot of the keys: an expired read below rewrites its own entry.
724
+ for (const key of Array.from(autoYesStates.keys())) {
725
+ const cliToolId = extractCliToolId(key);
726
+ if (!cliToolId)
727
+ continue;
728
+ const worktreeId = extractWorktreeId(key);
729
+ const instanceId = extractInstanceId(key) ?? cliToolId;
730
+ const state = getAutoYesState(worktreeId, cliToolId, instanceId);
731
+ if (!state?.enabled)
732
+ continue;
733
+ const entries = byWorktree.get(worktreeId) ?? {};
734
+ entries[instanceId] = autoYesAnswerModeOf(state);
735
+ byWorktree.set(worktreeId, entries);
736
+ }
737
+ return byWorktree;
738
+ }
684
739
  /**
685
740
  * Re-key every auto-yes state whose worktree ID is being renamed
686
741
  * (Issue #1621 Phase 3).
@@ -289,7 +289,7 @@ function withoutTrailingBlankRows(lines) {
289
289
  end--;
290
290
  return lines.slice(0, end);
291
291
  }
292
- /** Default bounded read-back attempts before giving up (throwing). */
292
+ /** Default bounded recovery Enters (each read back) before giving up (throwing). */
293
293
  const DEFAULT_VERIFY_ATTEMPTS = 4;
294
294
  /**
295
295
  * First non-blank line of the message, trimmed. This is what a TUI shows on the
@@ -427,12 +427,148 @@ function comparePasteToken(token, message) {
427
427
  * and its first line is the input line. Every other frame (a one-row
428
428
  * composer, an empty or ghost one, none at all, another tool) is read exactly
429
429
  * as before.
430
+ *
431
+ * Issue #3880 (H-2): this is also the reader before Enter
432
+ * ({@link classifyPasteLanded}) and in the error text ({@link describeComposer}).
433
+ * The paste check used to read the twelve-row tail with {@link readComposer}
434
+ * alone, so a codex composer taller than that window (a 20-line paste, or 600
435
+ * bytes wrapped in a narrow pane) put its `›` row above the window — `unseen`,
436
+ * and the body that was in the composer was withdrawn and reported as not
437
+ * landed — and a continuation row starting with `>` (a Markdown quote) was taken
438
+ * for the input line — `mismatch`. The same frame read after Enter was
439
+ * `pending`. One reader on both sides of Enter keeps the two verdicts about the
440
+ * same composer.
441
+ */
442
+ /**
443
+ * Everything a codex composer holds, for deciding that a pasted body arrived in
444
+ * full (Issue #3880).
445
+ *
446
+ * `extractComposerText` answers for the UI: its text stops at 2,000 characters,
447
+ * at 40 rows and at the composer's first blank row, so a composer that matches
448
+ * it may still be missing the rest of the body — or hold something after it.
449
+ * This reads the raw rows instead, from the `›` row down to the last block of
450
+ * the frame (codex's model / cwd footer), so a body with blank lines, more than
451
+ * 40 rows or more than 2,000 characters is compared whole, and anything typed
452
+ * after the body is part of what is compared.
430
453
  */
431
- function readSubmitComposer(output, cliToolId, reader, lines, windowLines) {
454
+ function codexComposerRegion(output) {
455
+ const raw = output.split('\n');
456
+ const box = (0, composer_text_1.findCodexInputBox)(raw);
457
+ if (box === null)
458
+ return null;
459
+ return codexRegionFrom(raw, box.firstRow, box.lastRow);
460
+ }
461
+ /**
462
+ * {@link codexComposerRegion} from a known `›` row (Issue #3906): the rows from
463
+ * `firstRow` down to the frame's last block (the footer), ANSI and glyph
464
+ * stripped. `lastRow` is the end of the `›` row's own block, so a composer that
465
+ * is itself the frame's last block is read to the end.
466
+ */
467
+ function codexRegionFrom(raw, firstRow, lastRow) {
468
+ const isBlank = (row) => (0, cli_patterns_1.stripAnsi)(row).trim() === '';
469
+ let end = raw.length - 1;
470
+ while (end >= 0 && isBlank(raw[end]))
471
+ end--;
472
+ let lastBlockStart = end;
473
+ while (lastBlockStart > 0 && !isBlank(raw[lastBlockStart - 1]))
474
+ lastBlockStart--;
475
+ const stop = lastBlockStart > lastRow ? lastBlockStart - 1 : end;
476
+ const rows = raw.slice(firstRow, stop + 1).map((row) => (0, cli_patterns_1.stripAnsi)(row));
477
+ rows[0] = rows[0].replace(/^\s*\u203A ?/, '');
478
+ return rows.join('\n');
479
+ }
480
+ /** End (inclusive) of the blank-row-delimited block that starts at `row`. */
481
+ function blockEndFrom(raw, row) {
482
+ let end = row;
483
+ while (end + 1 < raw.length && (0, cli_patterns_1.stripAnsi)(raw[end + 1]).trim() !== '')
484
+ end++;
485
+ return end;
486
+ }
487
+ /** The bottom-most row of `windowLines` the marker reader takes as the input line, as a frame index. */
488
+ function markerRowIndex(windowLines, windowStart) {
489
+ for (let i = windowLines.length - 1; i >= 0; i--) {
490
+ if (INPUT_LINE_MARKER.test(windowLines[i]))
491
+ return windowStart + i;
492
+ }
493
+ return -1;
494
+ }
495
+ function readSubmitComposer(output, cliToolId, reader, lines, windowLines, message) {
432
496
  if (cliToolId === 'codex' && reader === 'input-line-marker') {
497
+ const raw = output.split('\n');
498
+ const windowStart = lines.length - windowLines.length;
433
499
  const read = (0, composer_text_1.extractComposerText)(output, cliToolId);
434
- if (read.state === 'content' && read.text.includes('\n')) {
435
- return { kind: 'text', text: firstNonBlankLine(read.text), rows: read.text.split('\n') };
500
+ if (read.state === 'content') {
501
+ const box = (0, composer_text_1.findCodexInputBox)(raw);
502
+ // Issue #3906: a dialog option drawn below the composer the extractor
503
+ // located means the dialog is what the pane shows now, on every branch
504
+ // below — that composer is one left from before, not this send's.
505
+ if (box !== null && (0, composer_text_1.codexDialogBelow)(raw, box.firstRow))
506
+ return { kind: 'absent' };
507
+ const outsideWindow = box !== null && box.firstRow < windowStart;
508
+ const region = codexComposerRegion(output) ?? read.text;
509
+ if (read.text.includes('\n')) {
510
+ // Issue #3906: a composer of many rows with no blank line (20 lines) has
511
+ // its `›` row above the window too, and is marked so like the others.
512
+ return {
513
+ kind: 'text',
514
+ text: firstNonBlankLine(read.text),
515
+ rows: read.text.split('\n'),
516
+ region,
517
+ ...(outsideWindow ? { outsideWindow } : {}),
518
+ };
519
+ }
520
+ // Issue #3906 (1): the `›` row sits above the twelve-row window (a one-line
521
+ // paragraph, a blank line, then a tall one). The marker reader would see no
522
+ // input line there — `unseen` before Enter, `submitted` after it — so the
523
+ // composer `extractComposerText` located is read instead, whole.
524
+ if (outsideWindow && (0, composer_text_1.findCodexLiveComposerRow)(raw) === box.firstRow) {
525
+ return { kind: 'text', text: read.text.trim(), rows: [raw[box.firstRow]], region, outsideWindow: true };
526
+ }
527
+ // Issue #3880 (N1): a one-row excerpt is read as before for its verdict
528
+ // after Enter, but still carries the whole region — `head` with the rest of
529
+ // the body under a blank row is one excerpt row and must not pass for the
530
+ // whole body before Enter.
531
+ const single = readComposer(reader, lines, windowLines);
532
+ return single.kind === 'text' ? { ...single, region } : single;
533
+ }
534
+ if (read.state === 'no_composer') {
535
+ // Issue #3906 (2): more paragraphs than the four blocks the extractor
536
+ // searches. Whatever the marker reader takes must carry the whole region,
537
+ // so the composer's first line alone never passes for the body. The row is
538
+ // the live composer only when no dialog option is drawn below it
539
+ // ({@link findCodexLiveComposerRow}, the order `findCodexBottomGlyphRow`
540
+ // reads a frame in): a composer row left above an approval dialog is
541
+ // never this send's composer, however its text reads.
542
+ const composerRow = (0, composer_text_1.findCodexLiveComposerRow)(raw);
543
+ if (composerRow >= 0) {
544
+ const region = codexRegionFrom(raw, composerRow, blockEndFrom(raw, composerRow));
545
+ const text = stripInputMarker((0, cli_patterns_1.stripAnsi)(raw[composerRow]));
546
+ if (composerRow >= windowStart) {
547
+ const single = readComposer(reader, lines, windowLines);
548
+ if (single.kind === 'text' && markerRowIndex(windowLines, windowStart) === composerRow) {
549
+ return { ...single, region };
550
+ }
551
+ return single;
552
+ }
553
+ // Above the window as well, with no dialog below it.
554
+ if (message !== undefined && text.length > 0 && inputMatchesBody(text, message)) {
555
+ return { kind: 'text', text, rows: [raw[composerRow]], region, outsideWindow: true };
556
+ }
557
+ // Not the body's first line. A composer whose own block is empty or
558
+ // placeholder-only is empty; real text is read too — a `/…` command
559
+ // there is `replaced` as it is inside the window, and anything else is
560
+ // not proof that the message left ({@link classifySubmit}: `indeterminate`).
561
+ const held = (0, composer_text_1.readCodexComposerFrom)(raw.slice(0, blockEndFrom(raw, composerRow) + 1), composerRow);
562
+ if (held.state !== 'content')
563
+ return { kind: 'empty' };
564
+ return {
565
+ kind: 'text',
566
+ text: firstNonBlankLine(held.text),
567
+ rows: [raw[composerRow]],
568
+ region,
569
+ outsideWindow: true,
570
+ };
571
+ }
436
572
  }
437
573
  }
438
574
  return readComposer(reader, lines, windowLines);
@@ -448,7 +584,9 @@ function readSubmitComposer(output, cliToolId, reader, lines, windowLines) {
448
584
  * E. A DIFFERENT slash command is in the composer -> replaced (TUI popup
449
585
  * autocompleted the command; clear the line and throw, never resend Enter).
450
586
  * F. Any other non-empty text (idle placeholder/hint) -> submitted (unchanged
451
- * pre-#1501 permissive default, so normal sends never spuriously fail).
587
+ * pre-#1501 permissive default, so normal sends never spuriously fail) —
588
+ * except in a codex composer above the read window -> indeterminate
589
+ * (Issue #3906).
452
590
  *
453
591
  * C–F are scoped to the composer only, so the user-message echo that a TUI
454
592
  * prints into its history above the prompt never causes a false verdict.
@@ -481,7 +619,7 @@ function classifySubmit(output, cliToolId, message, composerSpec = (0, composer_
481
619
  if ((0, cli_patterns_1.detectThinking)(cliToolId, windowStr)) {
482
620
  return 'submitted';
483
621
  }
484
- const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, windowLines);
622
+ const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, windowLines, message);
485
623
  // B. No composer visible (scrolled off / dialog / still starting), or the
486
624
  // composer is empty => the message left the box => submitted.
487
625
  if (composer.kind !== 'text') {
@@ -502,8 +640,11 @@ function classifySubmit(output, cliToolId, message, composerSpec = (0, composer_
502
640
  return 'replaced';
503
641
  }
504
642
  // F. Non-empty, non-command steady-state text (idle placeholder / hint) =>
505
- // submitted, preserving the pre-#1501 permissive default.
506
- return 'submitted';
643
+ // submitted, preserving the pre-#1501 permissive default. Not for a codex
644
+ // composer above the window (Issue #3906): before #3906 that frame was
645
+ // read as no composer at all; text there that is not the body says
646
+ // neither that the message left nor that Enter would send it.
647
+ return composer.outsideWindow ? 'indeterminate' : 'submitted';
507
648
  }
508
649
  /**
509
650
  * Backward-compatible boolean view of {@link classifySubmit}: submitted vs not.
@@ -530,14 +671,53 @@ function isSubmitted(output, cliToolId, message, composerSpec = (0, composer_spe
530
671
  * 3. Anything else — the idle placeholder, a ghost suggestion, no composer —
531
672
  * shows nothing of the body. `unseen`, which the caller decides about.
532
673
  *
533
- * Reads the same tail window as {@link classifySubmit}, so a history echo of an
534
- * earlier message above the composer is never mistaken for this one.
674
+ * Reads the composer the way {@link classifySubmit} does
675
+ * ({@link readSubmitComposer}), so a history echo of an earlier message above the
676
+ * composer is never mistaken for this one, and a frame both functions see holds
677
+ * the same composer for both (Issue #3880). A multi-row codex composer is
678
+ * compared as a whole ({@link codexComposerRegion}), whitespace removed
679
+ * (wrapping and continuation indent proof): only the WHOLE body is `landed`;
680
+ * the body's beginning alone is `pasting` (never enough to press Enter on), the
681
+ * body plus anything else or text from inside it is `mismatch`. The whole body
682
+ * is checked before placeholders, so a body that itself quotes
683
+ * `[Pasted Content 600 chars]` is its own text, not codex's placeholder; a real
684
+ * placeholder is only ever the composer's first row.
685
+ *
686
+ * Issue #3906: a codex composer outside the twelve-row window, or split into
687
+ * more blocks than `extractComposerText` searches, is still read with its whole
688
+ * region ({@link readSubmitComposer}), so neither `unseen` for a body that is
689
+ * there nor `landed` on its first line alone.
535
690
  */
536
691
  function classifyPasteLanded(output, cliToolId, message, composerSpec = (0, composer_spec_1.resolveComposerSpec)(cliToolId)) {
537
692
  const lines = withoutTrailingBlankRows((0, cli_patterns_1.stripAnsi)(output).split('\n'));
538
- const composer = readComposer(composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES));
693
+ const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES), message);
539
694
  if (composer.kind !== 'text')
540
695
  return 'unseen';
696
+ if (composer.region !== undefined) {
697
+ const shown = compactText(composer.region);
698
+ const body = compactText(message);
699
+ if (shown === body)
700
+ return 'landed';
701
+ const placeholder = findPasteTokens(composer.text);
702
+ if (placeholder.tokens.length > 0) {
703
+ // A placeholder plus anything else — on its row or under it — is not the body as sent.
704
+ const below = compactText(composer.region.split('\n').slice(1).join('\n'));
705
+ if (placeholder.tokens.length > 1 || placeholder.rest !== '' || below !== '')
706
+ return 'mismatch';
707
+ return comparePasteToken(placeholder.tokens[0], message);
708
+ }
709
+ if (body.startsWith(shown))
710
+ return 'pasting';
711
+ // The body's beginning followed by something else, the body plus more, or a
712
+ // piece from inside it: the composer holds something other than the body.
713
+ if (inputMatchesBody(composer.text, message) || shown.startsWith(body) || body.includes(shown)) {
714
+ return 'mismatch';
715
+ }
716
+ return 'unseen';
717
+ }
718
+ // The whole body on one row is the body, even when it quotes a placeholder.
719
+ if (compactText(composer.text) === compactText(message))
720
+ return 'landed';
541
721
  const { tokens, rest } = findPasteTokens(composer.text);
542
722
  if (tokens.length === 0) {
543
723
  if (inputMatchesBody(composer.text, message))
@@ -591,9 +771,9 @@ async function pasteBody(sessionName, message) {
591
771
  }
592
772
  }
593
773
  /** The composer's first row, for an error message a person has to act on. */
594
- function describeComposer(output, composerSpec) {
774
+ function describeComposer(output, cliToolId, composerSpec, message) {
595
775
  const lines = withoutTrailingBlankRows((0, cli_patterns_1.stripAnsi)(output).split('\n'));
596
- const composer = readComposer(composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES));
776
+ const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES), message);
597
777
  if (composer.kind === 'absent')
598
778
  return 'no composer on screen';
599
779
  if (composer.kind === 'empty')
@@ -666,7 +846,7 @@ async function confirmPasteLanded(params) {
666
846
  }
667
847
  const bytes = Buffer.byteLength(message, 'utf8');
668
848
  const lineCount = message.split('\n').length;
669
- const shown = describeComposer(output, composer);
849
+ const shown = describeComposer(output, cliToolId, composer, message);
670
850
  await withdrawUnsentBody(sessionName, cliToolId, composer);
671
851
  (0, tmux_capture_cache_1.invalidateCache)(sessionName);
672
852
  logger.error('paste-not-landed', { sessionName, cliToolId, state, bytes, lines: lineCount, shown });
@@ -698,6 +878,12 @@ function visibleTypedBody(message) {
698
878
  const blank = lines.findIndex((line, index) => index > first && line.trim() === '');
699
879
  return lines.slice(0, blank < 0 ? lines.length : blank).join('\n');
700
880
  }
881
+ /** Whether the whole codex composer, read past the bounded readers (Issue #3906), is exactly `message`. */
882
+ function codexComposerShowsWholeBody(output, message, composerSpec) {
883
+ const lines = withoutTrailingBlankRows((0, cli_patterns_1.stripAnsi)(output).split('\n'));
884
+ const composer = readSubmitComposer(output, 'codex', composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES), message);
885
+ return composer.kind === 'text' && composer.region !== undefined && compactText(composer.region) === compactText(message);
886
+ }
701
887
  /**
702
888
  * Wait until the composer shows the typed body; only then may Enter be pressed
703
889
  * (Issue #3366).
@@ -711,6 +897,11 @@ function visibleTypedBody(message) {
711
897
  * a prefix is not enough, because codex can draw the first keystrokes before it
712
898
  * decides the rest are a burst.
713
899
  *
900
+ * On codex the verdict is the whole composer against the whole body
901
+ * ({@link codexComposerShowsWholeBody}, Issue #3906): the extractor's text is
902
+ * the body's first paragraph at most, so on its own it would let Enter go on a
903
+ * body whose later paragraphs had not been drawn.
904
+ *
714
905
  * A body that never appears is taken back out and the send THROWS without
715
906
  * Enter — nothing was submitted, so the caller must not print `Message sent.`.
716
907
  *
@@ -725,12 +916,21 @@ async function confirmTypedBodyLanded(params) {
725
916
  for (let attempt = 0; attempt < Math.max(1, attempts); attempt++) {
726
917
  await new Promise((resolve) => setTimeout(resolve, TYPED_BODY_POLL_MS));
727
918
  output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
919
+ // Issue #3906: on codex the whole composer must show the whole body. The
920
+ // extractor's excerpt stops at the first blank row, so its match is only
921
+ // the first paragraph — a five-paragraph body with one paragraph drawn
922
+ // would pass on it — and it cannot reach a body of more than four blocks.
923
+ if (cliToolId === 'codex') {
924
+ if (codexComposerShowsWholeBody(output, message, composer))
925
+ return;
926
+ continue;
927
+ }
728
928
  const read = (0, composer_text_1.extractComposerText)(output, cliToolId);
729
929
  if (read.state === 'content' && compactText(read.text) === expected)
730
930
  return;
731
931
  }
732
932
  const bytes = Buffer.byteLength(message, 'utf8');
733
- const shown = describeComposer(output, composer);
933
+ const shown = describeComposer(output, cliToolId, composer, message);
734
934
  await withdrawUnsentBody(sessionName, cliToolId, composer);
735
935
  (0, tmux_capture_cache_1.invalidateCache)(sessionName);
736
936
  logger.error('typed-body-not-landed', { sessionName, cliToolId, bytes, attempts, shown });
@@ -819,9 +1019,10 @@ async function clearComposerBeforeSend(sessionName, cliToolId, composerSpec = (0
819
1019
  *
820
1020
  * The body and Enter are always separate tmux commands (never batched), so the
821
1021
  * TUI cannot swallow the Enter inside a bracketed-paste buffer. After the
822
- * initial submit the pane is read back up to `verifyAttempts` times; each time
823
- * it is still pending an extra Enter is sent. If submit can never be confirmed
824
- * the function THROWS — callers must surface that as a failure, never as
1022
+ * initial submit the pane is read back; each time it is still pending an extra
1023
+ * Enter is sent, up to `verifyAttempts` of them, and every Enter — the last one
1024
+ * included (Issue #3880) — is read back before a verdict. If submit can never
1025
+ * be confirmed the function THROWS — callers must surface that as a failure, never as
825
1026
  * success (Issues #1469/#1470/#1471).
826
1027
  *
827
1028
  * A body longer than {@link LITERAL_SEND_MAX_BYTES} is pasted instead of typed,
@@ -884,8 +1085,14 @@ async function sendMessageWithSubmitVerification(params) {
884
1085
  }
885
1086
  // 4. Read-back verification: confirm the message left the input box, resend
886
1087
  // Enter while it is still pending, and throw if it never submits.
1088
+ //
1089
+ // Every Enter is followed by a read (Issue #3880, H-1): `attempts` resends
1090
+ // take `attempts + 1` reads. The loop used to resend on its last pending read
1091
+ // and throw straight after, so a message that only that last Enter submitted
1092
+ // was reported "could not be confirmed" — a sent message as exit 99. The
1093
+ // last read sends nothing: no Enter goes out that is not read back.
887
1094
  const attempts = Math.max(1, verifyAttempts);
888
- for (let attempt = 0; attempt < attempts; attempt++) {
1095
+ for (let attempt = 0; attempt <= attempts; attempt++) {
889
1096
  await new Promise((resolve) => setTimeout(resolve, verifyDelayMs));
890
1097
  const output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
891
1098
  const state = classifySubmit(output, cliToolId, message, composer);
@@ -913,6 +1120,15 @@ async function sendMessageWithSubmitVerification(params) {
913
1120
  logger.error('submit-replaced-by-tui-completion', { sessionName, cliToolId, attempt });
914
1121
  throw new Error(`Message was replaced by a TUI autocompletion for session ${sessionName}; the input no longer matches the sent text. Cleared the input line without submitting to avoid executing a different command.`);
915
1122
  }
1123
+ // state === 'pending' / 'indeterminate' after the final read-back: give up below.
1124
+ if (attempt === attempts)
1125
+ break;
1126
+ // state === 'indeterminate' (Issue #3906) — no Enter: the composer does not
1127
+ // show the body, so another Enter could submit whatever it does show.
1128
+ if (state === 'indeterminate') {
1129
+ logger.warn('submit-not-confirmed:composer-unreadable', { sessionName, cliToolId, attempt });
1130
+ continue;
1131
+ }
916
1132
  // state === 'pending' — still typed-but-unsent, resend a single Enter and re-check.
917
1133
  logger.warn('submit-not-confirmed:resending-enter', {
918
1134
  sessionName,
@@ -715,7 +715,11 @@ function recordAnsweredPrompt(db, params) {
715
715
  }
716
716
  const answeredAt = new Date().toISOString();
717
717
  if (target) {
718
- const updated = { ...target.parsed, status: 'answered', answer, answeredAt, answeredBy };
718
+ // Issue #3881 (review P2-2): the row keeps the reading it was saved with;
719
+ // one saved without a reading (before #3881) takes the answerer's, so a
720
+ // row's classification does not depend on which writer came first.
721
+ const reading = target.parsed.reading === undefined && promptData.reading !== undefined ? { reading: promptData.reading } : {};
722
+ const updated = { ...target.parsed, ...reading, status: 'answered', answer, answeredAt, answeredBy };
719
723
  updatePromptData(db, target.id, updated);
720
724
  return { message: getMessageById(db, target.id), created: false };
721
725
  }
@@ -21,6 +21,7 @@
21
21
  */
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
23
  exports.MAX_RUN_HISTORY_DAYS = exports.MAX_RUN_HISTORY_LIMIT = exports.DEFAULT_RUN_HISTORY_LIMIT = exports.VERIFICATION_GATE_SOURCES = exports.VERIFICATION_TRIGGERS = void 0;
24
+ exports.parseGateTiming = parseGateTiming;
24
25
  exports.createVerificationRun = createVerificationRun;
25
26
  exports.finishVerificationRun = finishVerificationRun;
26
27
  exports.createGateResult = createGateResult;
@@ -54,6 +55,20 @@ exports.VERIFICATION_TRIGGERS = ['manual', 'wait', 'api', 'task'];
54
55
  * The runtime tuple mirrors the CHECK constraint in migration v56.
55
56
  */
56
57
  exports.VERIFICATION_GATE_SOURCES = ['builtin', 'verify.yaml', 'contract'];
58
+ /**
59
+ * How much of a gate's `log_tail` the history listing reads: enough for the
60
+ * `[timing]` line, which is the last line — never the log body.
61
+ */
62
+ const TIMING_TAIL_CHARS = 200;
63
+ /** The last line of `logEnd`, when it is the runner's `[timing]` line. */
64
+ const TIMING_LINE = /(?:^|\n)\[timing\] queue=(\d+(?:\.\d+)?)s mutex=(\d+(?:\.\d+)?)s run=(\d+(?:\.\d+)?)s\s*$/;
65
+ /** The {@link VerificationGateTiming} of a gate whose `log_tail` ends with `logEnd`, or null. */
66
+ function parseGateTiming(logEnd) {
67
+ const match = TIMING_LINE.exec(logEnd ?? '');
68
+ if (!match)
69
+ return null;
70
+ return { queueSec: Number(match[1]), mutexSec: Number(match[2]), runSec: Number(match[3]) };
71
+ }
57
72
  const RUN_COLUMNS = `
58
73
  id, worktree_id, instance_id, task_id, trigger, status, base_ref, started_at, finished_at
59
74
  `;
@@ -247,7 +262,8 @@ function clampInt(value, fallback, min, max) {
247
262
  }
248
263
  /**
249
264
  * Recent runs across worktrees, newest first, each with its gate verdicts but
250
- * no log bodies (Issue #1593).
265
+ * no log bodies (Issue #1593). A gate's `[timing]` line is the one piece of its
266
+ * log that is carried, parsed into numbers (`timing`, Issue #3884).
251
267
  *
252
268
  * `worktreeId` is optional because the advisor that consumes this (#1594) looks
253
269
  * at a repository's trend, not one worktree's. Ordering follows the same
@@ -285,7 +301,8 @@ function listVerificationRunsForPeriod(db, opts = {}) {
285
301
  const placeholders = runRows.map(() => '?').join(', ');
286
302
  const gateRows = db
287
303
  .prepare(`
288
- SELECT run_id, gate_id, status, exit_code, duration_ms, source
304
+ SELECT run_id, gate_id, status, exit_code, duration_ms, source,
305
+ substr(log_tail, -${TIMING_TAIL_CHARS}) AS log_end
289
306
  FROM verification_gate_results
290
307
  WHERE run_id IN (${placeholders})
291
308
  ORDER BY started_at ASC, id ASC
@@ -300,6 +317,10 @@ function listVerificationRunsForPeriod(db, opts = {}) {
300
317
  durationMs: row.duration_ms,
301
318
  source: row.source ?? null,
302
319
  };
320
+ // Only the numbers leave this function: the log stays out of the listing (#3884).
321
+ const timing = parseGateTiming(row.log_end);
322
+ if (timing)
323
+ summary.timing = timing;
303
324
  const bucket = byRun.get(row.run_id);
304
325
  if (bucket)
305
326
  bucket.push(summary);