commandmate 0.45.0 → 0.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +265 -264
  3. package/.next/app-path-routes-manifest.json +18 -18
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/prerender-manifest.json +9 -9
  6. package/.next/react-loadable-manifest.json +4 -4
  7. package/.next/required-server-files.json +1 -1
  8. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
  10. package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/app/release-notes/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/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_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  28. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  58. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  62. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  64. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  101. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  104. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/route.js +1 -1
  148. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  150. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  152. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  153. package/.next/server/app/page_client-reference-manifest.js +1 -1
  154. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  155. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  156. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  157. package/.next/server/app/sessions/page.js +1 -1
  158. package/.next/server/app/sessions/page.js.nft.json +1 -1
  159. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  160. package/.next/server/app/skills/[skillId]/page.js +2 -2
  161. package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
  162. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  163. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  164. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  165. package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
  166. package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
  167. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  168. package/.next/server/app/worktrees/[id]/page.js +4 -4
  169. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  170. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  171. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  172. package/.next/server/app-paths-manifest.json +18 -18
  173. package/.next/server/chunks/2119.js +1 -1
  174. package/.next/server/chunks/2335.js +1 -0
  175. package/.next/server/chunks/2404.js +1 -1
  176. package/.next/server/chunks/2445.js +1 -1
  177. package/.next/server/chunks/2460.js +1 -1
  178. package/.next/server/chunks/{4802.js → 4243.js} +1 -1
  179. package/.next/server/chunks/4837.js +3 -3
  180. package/.next/server/chunks/5209.js +1 -1
  181. package/.next/server/chunks/5625.js +2 -2
  182. package/.next/server/chunks/6130.js +1 -1
  183. package/.next/server/chunks/6401.js +1 -1
  184. package/.next/server/chunks/6927.js +1 -1
  185. package/.next/server/chunks/8728.js +1 -1
  186. package/.next/server/chunks/9355.js +1 -0
  187. package/.next/server/chunks/9862.js +1 -0
  188. package/.next/server/middleware-build-manifest.js +1 -1
  189. package/.next/server/middleware-manifest.json +5 -5
  190. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  191. package/.next/server/pages/500.html +1 -1
  192. package/.next/server/server-reference-manifest.json +1 -1
  193. package/.next/static/chunks/{7485.8a2a9d7c5396aa26.js → 1111.5ab4f0ffb0d944e5.js} +2 -2
  194. package/.next/static/chunks/{1642-767aed8e7c61ef7f.js → 1642-0f1bf8fb8161e10a.js} +1 -1
  195. package/.next/static/chunks/{2407-dd4a0f04f1349221.js → 2407-86890b00eeecf391.js} +1 -1
  196. package/.next/static/chunks/3662-5a27ee39a3ecba49.js +1 -0
  197. package/.next/static/chunks/3922.0bc2e4e168d2b522.js +1 -0
  198. package/.next/static/chunks/4541-b0ce0bf829ae79b7.js +1 -0
  199. package/.next/static/chunks/4727-4c69d1aa4118cc09.js +1 -0
  200. package/.next/static/chunks/5371-af1c2b68e983da61.js +1 -0
  201. package/.next/static/chunks/6288-09feba367cc2c65e.js +1 -0
  202. package/.next/static/chunks/953.31c52a251deda22d.js +1 -0
  203. package/.next/static/chunks/app/{layout-07d040cb3d3ea657.js → layout-eaa6365f8f942e88.js} +1 -1
  204. package/.next/static/chunks/app/sessions/{page-2af68c63211c751f.js → page-ebfed2c80e916fb8.js} +1 -1
  205. package/.next/static/chunks/app/skills/[skillId]/page-e4b14031f946c007.js +1 -0
  206. package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9cf1ecc59e36a004.js +1 -0
  207. package/.next/static/chunks/app/worktrees/[id]/page-3372b78fde232b1f.js +1 -0
  208. package/.next/static/chunks/{webpack-165d8df9acd1c5d4.js → webpack-ee3ccbe378c4aa06.js} +1 -1
  209. package/.next/static/css/{057f64a65726c439.css → 6faeb04a0b6819d4.css} +1 -1
  210. package/.next/trace +13 -13
  211. package/dist/server/locales/en/worktree.json +2 -1
  212. package/dist/server/locales/ja/worktree.json +2 -1
  213. package/dist/server/src/lib/auto-yes-poller.js +16 -163
  214. package/dist/server/src/lib/polling/auto-yes-enter-fallback-state.js +1 -1
  215. package/dist/server/src/lib/polling/auto-yes-poller-state.js +52 -0
  216. package/dist/server/src/lib/polling/auto-yes-prompt-suppression.js +125 -0
  217. package/dist/server/src/lib/polling/prompt-dedup.js +93 -2
  218. package/dist/server/src/lib/polling/response-checker.js +40 -2
  219. package/dist/server/src/lib/polling/response-dedup.js +62 -1
  220. package/package.json +1 -1
  221. package/release-notes/0.45.1.json +38 -0
  222. package/.next/server/chunks/364.js +0 -1
  223. package/.next/server/chunks/488.js +0 -1
  224. package/.next/server/chunks/5326.js +0 -1
  225. package/.next/static/chunks/2053-390e77fce43b555c.js +0 -1
  226. package/.next/static/chunks/3922.f45dcfc1e7da552a.js +0 -1
  227. package/.next/static/chunks/4727-5d695d71e3fdf97b.js +0 -1
  228. package/.next/static/chunks/8283-26ad31a4670ea52d.js +0 -1
  229. package/.next/static/chunks/8566-aeda747542072ace.js +0 -1
  230. package/.next/static/chunks/8811-29eaa24237f48558.js +0 -1
  231. package/.next/static/chunks/953.19e4c8178c9eac4c.js +0 -1
  232. package/.next/static/chunks/app/skills/[skillId]/page-9ee7a381636e60cb.js +0 -1
  233. package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-5445205c8dd80109.js +0 -1
  234. package/.next/static/chunks/app/worktrees/[id]/page-35f0e31a725d1c89.js +0 -1
  235. /package/.next/static/{TPWbZB3zBp3Zv_eFEOlmt → vYIpFRCU_Mt0wtG8F_3-6}/_buildManifest.js +0 -0
  236. /package/.next/static/{TPWbZB3zBp3Zv_eFEOlmt → vYIpFRCU_Mt0wtG8F_3-6}/_ssgManifest.js +0 -0
@@ -818,7 +818,8 @@
818
818
  "loading": "Loading diagram...",
819
819
  "errorTitle": "Diagram Error",
820
820
  "emptyCode": "Diagram code is empty",
821
- "renderError": "Failed to render diagram"
821
+ "renderError": "Failed to render diagram",
822
+ "source": "Source"
822
823
  },
823
824
  "imageViewer": {
824
825
  "loadError": "Failed to load image"
@@ -818,7 +818,8 @@
818
818
  "loading": "図を読み込み中...",
819
819
  "errorTitle": "図のエラー",
820
820
  "emptyCode": "図のコードが空です",
821
- "renderError": "図の描画に失敗しました"
821
+ "renderError": "図の描画に失敗しました",
822
+ "source": "ソース"
822
823
  },
823
824
  "imageViewer": {
824
825
  "loadError": "画像の読み込みに失敗しました"
@@ -7,8 +7,9 @@
7
7
  *
8
8
  * Issue #525: Composite key migration (worktreeId:cliToolId) for per-agent auto-yes.
9
9
  *
10
- * Dependencies: auto-yes-state.ts (one-way dependency).
11
- * auto-yes-poller.ts -> auto-yes-state.ts
10
+ * Dependencies: auto-yes-state.ts (composite keys / state), polling/* (prompt
11
+ * detection, resolver, policy, dialog gate, enter fallback, poller state),
12
+ * cli-tools/*, session/cli-session.ts, tmux/*, detection/*, db/*, tasks/*.
12
13
  */
13
14
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
15
  if (k2 === undefined) k2 = k;
@@ -67,7 +68,6 @@ const auto_yes_resolver_1 = require("./polling/auto-yes-resolver");
67
68
  const auto_yes_policy_1 = require("./polling/auto-yes-policy");
68
69
  const detect_1 = require("./detection/tools/codex/detect");
69
70
  const frame_1 = require("./detection/tools/frame");
70
- const auto_yes_suppression_state_1 = require("./polling/auto-yes-suppression-state");
71
71
  const auto_yes_dialog_gate_1 = require("./polling/auto-yes-dialog-gate");
72
72
  const auto_yes_enter_fallback_1 = require("./polling/auto-yes-enter-fallback");
73
73
  const task_transition_service_1 = require("./tasks/task-transition-service");
@@ -88,6 +88,8 @@ const logger = (0, logger_1.createLogger)('auto-yes-poller');
88
88
  const path_validator_1 = require("./security/path-validator");
89
89
  const auto_yes_state_1 = require("./auto-yes-state");
90
90
  const global_state_1 = require("./global-state");
91
+ const auto_yes_poller_state_1 = require("./polling/auto-yes-poller-state");
92
+ const auto_yes_prompt_suppression_1 = require("./polling/auto-yes-prompt-suppression");
91
93
  /** In-memory storage for poller states (globalThis for hot reload persistence) */
92
94
  const autoYesPollerStates = (0, global_state_1.getOrInitGlobal)('__autoYesPollerStates', () => new Map());
93
95
  /**
@@ -221,45 +223,6 @@ function isDuplicatePrompt(pollerState, promptKey) {
221
223
  return false;
222
224
  return (Date.now() - pollerState.lastAnsweredAt) < auto_yes_state_1.DUPLICATE_RETRY_EXPIRY_MS;
223
225
  }
224
- /**
225
- * Issue #2995: print a "did not answer" line as WARN once per frame.
226
- *
227
- * The poller re-reads a static pane every 2s, so a frame Auto-Yes leaves alone
228
- * (a reply quoting `1. Yes / 2. No`, a launch dialog, a policy-withheld prompt,
229
- * a foreign session) would otherwise print the same WARN on every tick. The
230
- * first one stays WARN -- it is what tells a silently stalled worker apart --
231
- * and repeats for the same frame drop to `debug`. The frame is identified by
232
- * the event, `promptFrameKey()` and the event's own detail; a different
233
- * prompt, a different reason, or a tick with no prompt / an answer sent (both
234
- * clear the key) makes the next line WARN again.
235
- * `recordPolicySuppression()` is not throttled: `capture --json` and `wait`
236
- * read the latest record, not the log.
237
- */
238
- /**
239
- * Issue #2995: what makes two ticks "the same prompt" for `warnOncePerFrame`.
240
- *
241
- * `generatePromptKey` alone is type + question, and the question the detector
242
- * extracts from an agent's reply is often a fixed line above the list, so two
243
- * different lists would share it. The option rows (and where the cursor sits)
244
- * plus `approvalTarget` -- the current prompt's own panel, which leaves out
245
- * scrollback and footers that change while the prompt does not -- separate them.
246
- * Not the whole pane: a status bar that ticks every second would defeat it.
247
- */
248
- function promptFrameKey(promptData) {
249
- const options = promptData.type === 'multiple_choice'
250
- ? promptData.options.map((o) => `${o.isDefault ? '>' : ''}${o.number}.${o.label}`).join('\n')
251
- : '';
252
- return [(0, prompt_key_1.generatePromptKey)(promptData), options, promptData.approvalTarget ?? ''].join('\u0000');
253
- }
254
- function warnOncePerFrame(pollerState, frameKey, event, fields) {
255
- const key = `${event}\u0000${frameKey}`;
256
- if (pollerState.lastSkipWarnKey === key) {
257
- logger.debug(event, { ...fields, repeated: true });
258
- return;
259
- }
260
- pollerState.lastSkipWarnKey = key;
261
- logger.warn(event, fields);
262
- }
263
226
  // =============================================================================
264
227
  // Extracted Functions for pollAutoYes (Issue #323: SRP decomposition)
265
228
  // =============================================================================
@@ -395,116 +358,6 @@ function logIfWithheldForWantOfReceipt(worktreeId, instanceId, compositeKey, rea
395
358
  hint: 'hook の受信記録が直近に無いため Auto-Yes は答えなかった。~/.gemini/config/hooks.json の向き先(#2622)を確認',
396
359
  });
397
360
  }
398
- /**
399
- * Issue #3214: the form the four suppression exits of `detectAndRespondToPrompt`
400
- * share. Records why Auto-Yes left the prompt alone (`recordPolicySuppression`,
401
- * not throttled), then prints the WARN once per frame (`warnOncePerFrame`).
402
- *
403
- * @param suppression - Handed to `recordPolicySuppression` as is
404
- * @param warnFrameKey - Handed to `warnOncePerFrame` as is: the prompt's
405
- * `frameKey` plus the exit's own detail
406
- * @param event - The WARN's log name
407
- * @param detail - The WARN's fields after `worktreeId`, `cliToolId` and `instanceId`
408
- */
409
- function suppressAndWarnOnce(prompt, suppression, warnFrameKey, event, detail) {
410
- const { worktreeId, cliToolId, instanceId } = prompt;
411
- (0, auto_yes_suppression_state_1.recordPolicySuppression)(worktreeId, cliToolId, instanceId, suppression);
412
- warnOncePerFrame(prompt.pollerState, warnFrameKey, event, {
413
- worktreeId,
414
- cliToolId,
415
- instanceId,
416
- ...detail,
417
- });
418
- }
419
- /**
420
- * Issue #3214: steps 3, 3.2 and 3.5 of `detectAndRespondToPrompt` -- the frames
421
- * that read as a prompt and are still not Auto-Yes's to answer, judged in the
422
- * order they always were. "The detection above" in the comments below is that
423
- * function's step 1.
424
- *
425
- * Issue #3397: step 3.5 no longer records its refusal here. It hands the gate's
426
- * verdict back so `tryEnterFallback` can look at the frame first, and records
427
- * through {@link suppressUnclassifiedFrame} when that sends nothing. Steps 3 and
428
- * 3.2 still win: an Enter never reaches codex's launch dialogs or its `/model`
429
- * picker.
430
- *
431
- * @param frame - The tick's one normalised frame (Issue #3183)
432
- */
433
- function suppressIfNotOursToAnswer(prompt, frame) {
434
- const { cliToolId, promptData, frameKey } = prompt;
435
- // 3. Issue #1829: codex's own launch dialogs are CodexTool.waitForReady()'s
436
- // to answer, not the poller's. Every one of them defaults to option 1, and
437
- // the base rules answer the default:
438
- //
439
- // "Hooks need review" -> 1. Review hooks (undoes Issue #1760; the pane
440
- // then sits two screens deep in a review UI that
441
- // only `t`/`esc` leave, reported as `running`)
442
- // "Update available" -> 1. Update now (undoes Issue #890; runs
443
- // `npm install -g @openai/codex`, killing codex)
444
- // "Do you trust …" -> 1. Yes, continue
445
- //
446
- // waitForReady answers the same screens deliberately and differently ('3',
447
- // '2', '1' — each without a trailing Enter), but only during startSession,
448
- // while this poller runs on its own 2s phase for the life of the session.
449
- // Whichever sees the dialog first decides, so the fix is to leave them all
450
- // to the tool. Auto-answer layer only: the detection above still reports the
451
- // prompt, so the human keeps seeing the screen and the response poller
452
- // still notifies them about it.
453
- const launchDialog = cliToolId === 'codex' ? (0, cli_patterns_1.getCodexLifecycleDialog)(frame) : null;
454
- if (launchDialog) {
455
- // Recorded through the #1684 channel so `capture --json` and `cmate wait`
456
- // can name the reason instead of showing a worker that went quiet.
457
- suppressAndWarnOnce(prompt, { reason: 'agent-launch-dialog', mode: null, promptType: promptData.type }, `${frameKey}\u0000${launchDialog}`, 'poller:auto-yes-skipped-launch-dialog', { dialog: launchDialog, promptType: promptData.type });
458
- return { kind: 'left-alone' };
459
- }
460
- // 3.2. Issue #3062: codex's `/model` picker (both stages) is not ours to
461
- // answer. A digit there is an immediate decision, so the base rules'
462
- // default would choose the model and effort for the operator. Judged by the
463
- // picker's own footer, not by `kind: picker`, which the hooks screens share.
464
- // The tool's `detectPrompt` still reports the prompt, so `/prompt-response`
465
- // (the human's answer) is unaffected. Reuses `unclassified-frame`: the tool
466
- // recognised the frame and deliberately declined it.
467
- if (cliToolId === 'codex' && (0, detect_1.isCodexModelPickerFrame)(frame)) {
468
- suppressAndWarnOnce(prompt, { reason: 'unclassified-frame', mode: null, promptType: promptData.type }, `${frameKey}\u0000codex-model-picker`, 'poller:auto-yes-skipped-model-picker', { promptType: promptData.type });
469
- return { kind: 'left-alone' };
470
- }
471
- // 3.5. Issue #1928 (§4 D1 decision 4): the generic numbered-list inference is
472
- // not enough to send an answer. The detection above judges the ROWS, and the
473
- // rows of an agent's own reply can be indistinguishable from a dialog's --
474
- // opencode 1.18 answering "list three options and ask which one" is the
475
- // reported case (#1896), and the `1` this poller sent in reply was not
476
- // answering anything, it was SENT AS A USER UTTERANCE. What separates the two
477
- // is position and chrome, which only the tool's own module knows, so the
478
- // decision is delegated to `detectDialog` (the seam #1927 declared).
479
- //
480
- // Per tool, and only for tools whose dialogs were measured from their own
481
- // live captures -- see AUTO_YES_DIALOG_GATE_DEFAULT_MODE. An ungated tool
482
- // reaches `allowed: true` without being judged, which is the pre-#1928
483
- // behaviour and the right one where nobody has measured anything.
484
- //
485
- // Recorded through the #1684 channel with the reason code #1924 landed for
486
- // exactly this position, so `capture --json` and `cmate wait` can name the
487
- // gap instead of showing a worker that silently went quiet.
488
- const dialogGate = (0, auto_yes_dialog_gate_1.evaluateAutoYesDialogGate)(cliToolId, promptData.type, frame);
489
- if (!dialogGate.allowed) {
490
- return { kind: 'dialog-gate-refused', dialogGate };
491
- }
492
- return { kind: 'ours' };
493
- }
494
- /**
495
- * Step 3.5's record: the tool's dialog detector did not vouch for the frame and
496
- * nothing was sent (Issue #1928). Split out of `suppressIfNotOursToAnswer` by
497
- * Issue #3397, unchanged, so it runs after `tryEnterFallback` declined.
498
- */
499
- function suppressUnclassifiedFrame(prompt, dialogGate) {
500
- const { promptData, frameKey } = prompt;
501
- suppressAndWarnOnce(prompt, { reason: 'unclassified-frame', mode: null, promptType: promptData.type }, `${frameKey}\u0000${dialogGate.dialog?.kind ?? ''}\u0000${dialogGate.mode}`, 'poller:auto-yes-skipped-unclassified-frame', {
502
- promptType: promptData.type,
503
- dialogKind: dialogGate.dialog?.kind ?? null,
504
- answerMode: dialogGate.dialog?.answerMode ?? null,
505
- gateMode: dialogGate.mode,
506
- });
507
- }
508
361
  /**
509
362
  * The audit row's `answer` for the Enter (Issue #3397): not a digit, so the
510
363
  * History reads as "Auto-Yes pressed Enter" rather than as a chosen option.
@@ -525,7 +378,7 @@ function isStillEnterFallbackScreen(prompt, fresh) {
525
378
  const detection = (0, response_checker_1.detectPromptOnCleanFrame)(clean, cliToolId, clean.split('\n'), fresh, { worktreeId, instanceId }, frame);
526
379
  if (!detection.isPrompt || !detection.promptData)
527
380
  return false;
528
- if (promptFrameKey(detection.promptData) !== frameKey)
381
+ if ((0, auto_yes_poller_state_1.promptFrameKey)(detection.promptData) !== frameKey)
529
382
  return false;
530
383
  if (cliToolId === 'codex' && ((0, cli_patterns_1.getCodexLifecycleDialog)(frame) || (0, detect_1.isCodexModelPickerFrame)(frame))) {
531
384
  return false;
@@ -563,7 +416,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
563
416
  const judgement = dialogGate.dialog === null ? (0, auto_yes_enter_fallback_1.judgeEnterFallback)(cliToolId, rawOutput) : null;
564
417
  if (judgement === null || !judgement.eligible) {
565
418
  pollerState.enterFallbackCandidateKey = null;
566
- suppressUnclassifiedFrame(prompt, dialogGate);
419
+ (0, auto_yes_prompt_suppression_1.suppressUnclassifiedFrame)(prompt, dialogGate);
567
420
  return false;
568
421
  }
569
422
  const screenKey = (0, auto_yes_enter_fallback_1.enterFallbackScreenKey)(promptData);
@@ -571,8 +424,8 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
571
424
  if (pollerState.enterFallbackSentKey === frameKey) {
572
425
  pollerState.enterFallbackCandidateKey = null;
573
426
  (0, auto_yes_enter_fallback_1.recordEnterFallbackNoEffect)(worktreeId, cliToolId, instanceId, screenKey);
574
- suppressUnclassifiedFrame(prompt, dialogGate);
575
- warnOncePerFrame(pollerState, `${frameKey}\u0000enter-fallback-no-effect`, 'poller:auto-yes-enter-fallback-no-effect', {
427
+ (0, auto_yes_prompt_suppression_1.suppressUnclassifiedFrame)(prompt, dialogGate);
428
+ (0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000enter-fallback-no-effect`, 'poller:auto-yes-enter-fallback-no-effect', {
576
429
  worktreeId,
577
430
  cliToolId,
578
431
  instanceId,
@@ -586,7 +439,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
586
439
  const resolution = (0, auto_yes_resolver_1.resolveAutoAnswerWithPolicy)(promptData, policy);
587
440
  if (resolution.suppressedBy) {
588
441
  pollerState.enterFallbackCandidateKey = null;
589
- suppressAndWarnOnce(prompt, {
442
+ (0, auto_yes_prompt_suppression_1.suppressAndWarnOnce)(prompt, {
590
443
  reason: resolution.suppressedBy,
591
444
  mode: policy?.mode ?? null,
592
445
  promptType: promptData.type,
@@ -608,7 +461,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
608
461
  if (resolution.answer === null ||
609
462
  (promptData.type === 'multiple_choice' && (0, prompt_answer_semantic_1.isMultiSelectPrompt)(promptData))) {
610
463
  pollerState.enterFallbackCandidateKey = null;
611
- suppressUnclassifiedFrame(prompt, dialogGate);
464
+ (0, auto_yes_prompt_suppression_1.suppressUnclassifiedFrame)(prompt, dialogGate);
612
465
  return false;
613
466
  }
614
467
  // 4. Seen once: wait for the next tick to see the same screen again.
@@ -629,7 +482,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
629
482
  const sessionName = manager_1.CLIToolManager.getInstance().getTool(cliToolId).getSessionName(worktreeId, instanceId);
630
483
  const ownership = await (0, worktree_session_ownership_1.checkWorktreeSessionOwnership)(worktreeId, sessionName);
631
484
  if (ownership === null || ownership.verdict === 'foreign') {
632
- warnOncePerFrame(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
485
+ (0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
633
486
  worktreeId,
634
487
  cliToolId,
635
488
  instanceId,
@@ -841,7 +694,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
841
694
  }
842
695
  // 2. Check for duplicate prompt (Issue #306)
843
696
  const promptKey = (0, prompt_key_1.generatePromptKey)(promptDetection.promptData);
844
- const frameKey = promptFrameKey(promptDetection.promptData);
697
+ const frameKey = (0, auto_yes_poller_state_1.promptFrameKey)(promptDetection.promptData);
845
698
  if (isDuplicatePrompt(pollerState, promptKey)) {
846
699
  return 'duplicate';
847
700
  }
@@ -857,7 +710,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
857
710
  };
858
711
  // 3., 3.2., 3.5. Frames that are not Auto-Yes's to answer: see
859
712
  // `suppressIfNotOursToAnswer`.
860
- const notOurs = suppressIfNotOursToAnswer(judged, frame);
713
+ const notOurs = (0, auto_yes_prompt_suppression_1.suppressIfNotOursToAnswer)(judged, frame);
861
714
  if (notOurs.kind !== 'dialog-gate-refused') {
862
715
  pollerState.enterFallbackCandidateKey = null;
863
716
  }
@@ -883,7 +736,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
883
736
  // Issue #1684: the log line alone leaves a CLI-driven pipeline blind to
884
737
  // why its worker stalled. Record the suppression so buildCurrentOutput
885
738
  // can publish it (`autoYes.lastSuppression` in capture --json).
886
- suppressAndWarnOnce(judged, {
739
+ (0, auto_yes_prompt_suppression_1.suppressAndWarnOnce)(judged, {
887
740
  reason: resolution.suppressedBy,
888
741
  mode: policy?.mode ?? null,
889
742
  promptType: promptDetection.promptData.type,
@@ -907,7 +760,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
907
760
  // server created. The poller keeps running; it just does not type.
908
761
  const ownership = await (0, worktree_session_ownership_1.checkWorktreeSessionOwnership)(worktreeId, sessionName);
909
762
  if (ownership === null || ownership.verdict === 'foreign') {
910
- warnOncePerFrame(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
763
+ (0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
911
764
  worktreeId,
912
765
  cliToolId,
913
766
  instanceId,
@@ -30,7 +30,7 @@ const global_state_1 = require("../global-state");
30
30
  * What tells two prompts apart for the published record: the type, the
31
31
  * question and the option labels.
32
32
  *
33
- * Not the poller's `promptFrameKey`: that one also carries the cursor and
33
+ * Not the poller's `promptFrameKey` (in `auto-yes-poller-state.ts`): that one also carries the cursor and
34
34
  * `approvalTarget`, and the status API reads a different number of rows than
35
35
  * the poller does, so `approvalTarget` can differ between the two readings of
36
36
  * one screen. The record is matched against the status API's prompt (to say
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ /**
3
+ * Auto-Yes poller state and its once-per-frame WARN (Issue #3411).
4
+ *
5
+ * Moved out of auto-yes-poller.ts unchanged; the poller re-exports
6
+ * `AutoYesPollerState`. Must not import auto-yes-poller.ts (cycle).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.promptFrameKey = promptFrameKey;
10
+ exports.warnOncePerFrame = warnOncePerFrame;
11
+ const prompt_key_1 = require("../detection/prompt-key");
12
+ const logger_1 = require("../../lib/logger");
13
+ const logger = (0, logger_1.createLogger)('auto-yes-poller');
14
+ /**
15
+ * Issue #2995: what makes two ticks "the same prompt" for `warnOncePerFrame`.
16
+ *
17
+ * `generatePromptKey` alone is type + question, and the question the detector
18
+ * extracts from an agent's reply is often a fixed line above the list, so two
19
+ * different lists would share it. The option rows (and where the cursor sits)
20
+ * plus `approvalTarget` -- the current prompt's own panel, which leaves out
21
+ * scrollback and footers that change while the prompt does not -- separate them.
22
+ * Not the whole pane: a status bar that ticks every second would defeat it.
23
+ */
24
+ function promptFrameKey(promptData) {
25
+ const options = promptData.type === 'multiple_choice'
26
+ ? promptData.options.map((o) => `${o.isDefault ? '>' : ''}${o.number}.${o.label}`).join('\n')
27
+ : '';
28
+ return [(0, prompt_key_1.generatePromptKey)(promptData), options, promptData.approvalTarget ?? ''].join('\u0000');
29
+ }
30
+ /**
31
+ * Issue #2995: print a "did not answer" line as WARN once per frame.
32
+ *
33
+ * The poller re-reads a static pane every 2s, so a frame Auto-Yes leaves alone
34
+ * (a reply quoting `1. Yes / 2. No`, a launch dialog, a policy-withheld prompt,
35
+ * a foreign session) would otherwise print the same WARN on every tick. The
36
+ * first one stays WARN -- it is what tells a silently stalled worker apart --
37
+ * and repeats for the same frame drop to `debug`. The frame is identified by
38
+ * the event, `promptFrameKey()` and the event's own detail; a different
39
+ * prompt, a different reason, or a tick with no prompt / an answer sent (both
40
+ * clear the key) makes the next line WARN again.
41
+ * `recordPolicySuppression()` is not throttled: `capture --json` and `wait`
42
+ * read the latest record, not the log.
43
+ */
44
+ function warnOncePerFrame(pollerState, frameKey, event, fields) {
45
+ const key = `${event}\u0000${frameKey}`;
46
+ if (pollerState.lastSkipWarnKey === key) {
47
+ logger.debug(event, { ...fields, repeated: true });
48
+ return;
49
+ }
50
+ pollerState.lastSkipWarnKey = key;
51
+ logger.warn(event, fields);
52
+ }
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ /**
3
+ * The suppression exits of `detectAndRespondToPrompt` (Issue #3214), moved out
4
+ * of auto-yes-poller.ts unchanged (Issue #3411). Must not import
5
+ * auto-yes-poller.ts (cycle) or `../tmux/tmux` (tmux import allowlist).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.suppressAndWarnOnce = suppressAndWarnOnce;
9
+ exports.suppressIfNotOursToAnswer = suppressIfNotOursToAnswer;
10
+ exports.suppressUnclassifiedFrame = suppressUnclassifiedFrame;
11
+ const detect_1 = require("../detection/tools/codex/detect");
12
+ const cli_patterns_1 = require("../detection/cli-patterns");
13
+ const auto_yes_suppression_state_1 = require("./auto-yes-suppression-state");
14
+ const auto_yes_dialog_gate_1 = require("./auto-yes-dialog-gate");
15
+ const auto_yes_poller_state_1 = require("./auto-yes-poller-state");
16
+ /**
17
+ * Issue #3214: the form the four suppression exits of `detectAndRespondToPrompt`
18
+ * share. Records why Auto-Yes left the prompt alone (`recordPolicySuppression`,
19
+ * not throttled), then prints the WARN once per frame (`warnOncePerFrame`).
20
+ *
21
+ * @param suppression - Handed to `recordPolicySuppression` as is
22
+ * @param warnFrameKey - Handed to `warnOncePerFrame` as is: the prompt's
23
+ * `frameKey` plus the exit's own detail
24
+ * @param event - The WARN's log name
25
+ * @param detail - The WARN's fields after `worktreeId`, `cliToolId` and `instanceId`
26
+ */
27
+ function suppressAndWarnOnce(prompt, suppression, warnFrameKey, event, detail) {
28
+ const { worktreeId, cliToolId, instanceId } = prompt;
29
+ (0, auto_yes_suppression_state_1.recordPolicySuppression)(worktreeId, cliToolId, instanceId, suppression);
30
+ (0, auto_yes_poller_state_1.warnOncePerFrame)(prompt.pollerState, warnFrameKey, event, {
31
+ worktreeId,
32
+ cliToolId,
33
+ instanceId,
34
+ ...detail,
35
+ });
36
+ }
37
+ /**
38
+ * Issue #3214: steps 3, 3.2 and 3.5 of `detectAndRespondToPrompt` -- the frames
39
+ * that read as a prompt and are still not Auto-Yes's to answer, judged in the
40
+ * order they always were. "The detection above" in the comments below is that
41
+ * function's step 1.
42
+ *
43
+ * Issue #3397: step 3.5 no longer records its refusal here. It hands the gate's
44
+ * verdict back so `tryEnterFallback` can look at the frame first, and records
45
+ * through {@link suppressUnclassifiedFrame} when that sends nothing. Steps 3 and
46
+ * 3.2 still win: an Enter never reaches codex's launch dialogs or its `/model`
47
+ * picker.
48
+ *
49
+ * @param frame - The tick's one normalised frame (Issue #3183)
50
+ */
51
+ function suppressIfNotOursToAnswer(prompt, frame) {
52
+ const { cliToolId, promptData, frameKey } = prompt;
53
+ // 3. Issue #1829: codex's own launch dialogs are CodexTool.waitForReady()'s
54
+ // to answer, not the poller's. Every one of them defaults to option 1, and
55
+ // the base rules answer the default:
56
+ //
57
+ // "Hooks need review" -> 1. Review hooks (undoes Issue #1760; the pane
58
+ // then sits two screens deep in a review UI that
59
+ // only `t`/`esc` leave, reported as `running`)
60
+ // "Update available" -> 1. Update now (undoes Issue #890; runs
61
+ // `npm install -g @openai/codex`, killing codex)
62
+ // "Do you trust …" -> 1. Yes, continue
63
+ //
64
+ // waitForReady answers the same screens deliberately and differently ('3',
65
+ // '2', '1' — each without a trailing Enter), but only during startSession,
66
+ // while this poller runs on its own 2s phase for the life of the session.
67
+ // Whichever sees the dialog first decides, so the fix is to leave them all
68
+ // to the tool. Auto-answer layer only: the detection above still reports the
69
+ // prompt, so the human keeps seeing the screen and the response poller
70
+ // still notifies them about it.
71
+ const launchDialog = cliToolId === 'codex' ? (0, cli_patterns_1.getCodexLifecycleDialog)(frame) : null;
72
+ if (launchDialog) {
73
+ // Recorded through the #1684 channel so `capture --json` and `cmate wait`
74
+ // can name the reason instead of showing a worker that went quiet.
75
+ suppressAndWarnOnce(prompt, { reason: 'agent-launch-dialog', mode: null, promptType: promptData.type }, `${frameKey}\u0000${launchDialog}`, 'poller:auto-yes-skipped-launch-dialog', { dialog: launchDialog, promptType: promptData.type });
76
+ return { kind: 'left-alone' };
77
+ }
78
+ // 3.2. Issue #3062: codex's `/model` picker (both stages) is not ours to
79
+ // answer. A digit there is an immediate decision, so the base rules'
80
+ // default would choose the model and effort for the operator. Judged by the
81
+ // picker's own footer, not by `kind: picker`, which the hooks screens share.
82
+ // The tool's `detectPrompt` still reports the prompt, so `/prompt-response`
83
+ // (the human's answer) is unaffected. Reuses `unclassified-frame`: the tool
84
+ // recognised the frame and deliberately declined it.
85
+ if (cliToolId === 'codex' && (0, detect_1.isCodexModelPickerFrame)(frame)) {
86
+ suppressAndWarnOnce(prompt, { reason: 'unclassified-frame', mode: null, promptType: promptData.type }, `${frameKey}\u0000codex-model-picker`, 'poller:auto-yes-skipped-model-picker', { promptType: promptData.type });
87
+ return { kind: 'left-alone' };
88
+ }
89
+ // 3.5. Issue #1928 (§4 D1 decision 4): the generic numbered-list inference is
90
+ // not enough to send an answer. The detection above judges the ROWS, and the
91
+ // rows of an agent's own reply can be indistinguishable from a dialog's --
92
+ // opencode 1.18 answering "list three options and ask which one" is the
93
+ // reported case (#1896), and the `1` this poller sent in reply was not
94
+ // answering anything, it was SENT AS A USER UTTERANCE. What separates the two
95
+ // is position and chrome, which only the tool's own module knows, so the
96
+ // decision is delegated to `detectDialog` (the seam #1927 declared).
97
+ //
98
+ // Per tool, and only for tools whose dialogs were measured from their own
99
+ // live captures -- see AUTO_YES_DIALOG_GATE_DEFAULT_MODE. An ungated tool
100
+ // reaches `allowed: true` without being judged, which is the pre-#1928
101
+ // behaviour and the right one where nobody has measured anything.
102
+ //
103
+ // Recorded through the #1684 channel with the reason code #1924 landed for
104
+ // exactly this position, so `capture --json` and `cmate wait` can name the
105
+ // gap instead of showing a worker that silently went quiet.
106
+ const dialogGate = (0, auto_yes_dialog_gate_1.evaluateAutoYesDialogGate)(cliToolId, promptData.type, frame);
107
+ if (!dialogGate.allowed) {
108
+ return { kind: 'dialog-gate-refused', dialogGate };
109
+ }
110
+ return { kind: 'ours' };
111
+ }
112
+ /**
113
+ * Step 3.5's record: the tool's dialog detector did not vouch for the frame and
114
+ * nothing was sent (Issue #1928). Split out of `suppressIfNotOursToAnswer` by
115
+ * Issue #3397, unchanged, so it runs after `tryEnterFallback` declined.
116
+ */
117
+ function suppressUnclassifiedFrame(prompt, dialogGate) {
118
+ const { promptData, frameKey } = prompt;
119
+ suppressAndWarnOnce(prompt, { reason: 'unclassified-frame', mode: null, promptType: promptData.type }, `${frameKey}\u0000${dialogGate.dialog?.kind ?? ''}\u0000${dialogGate.mode}`, 'poller:auto-yes-skipped-unclassified-frame', {
120
+ promptType: promptData.type,
121
+ dialogKind: dialogGate.dialog?.kind ?? null,
122
+ answerMode: dialogGate.dialog?.answerMode ?? null,
123
+ gateMode: dialogGate.mode,
124
+ });
125
+ }
@@ -6,15 +6,53 @@
6
6
  * Uses SHA-256 content hash with an in-memory cache (Map<pollerKey, hash>).
7
7
  * Design decision [DR1-006]: In-memory only (no DB layer).
8
8
  * Process restart may cause a single duplicate, which is acceptable (KISS).
9
+ *
10
+ * ## How often the skip says so (Issue #3538)
11
+ *
12
+ * A full-screen TUI (copilot, opencode) keeps polling while it sits on a
13
+ * prompt, so the same prompt is a duplicate on every 2 s tick until it is
14
+ * answered, and `duplicate-prompt-skipped` used to be logged on each of them.
15
+ * The run of duplicate ticks is counted here, the same way `response-dedup`
16
+ * counts `duplicate-response-skipped` (#3519): the line is written on the first
17
+ * tick of a run and then once per {@link DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL}
18
+ * ticks. The count is about one particular cached hash, so it never outlives
19
+ * it — the functions below that drop or move the hash drop or move the count
20
+ * too. The one exception runs the other way: a tick that reads a frame with no
21
+ * live prompt on it ends the run without touching the hash
22
+ * ({@link resetDuplicatePromptSkipStreak}), because the prompt left the screen
23
+ * while the guard still has to keep it from being saved twice. A returning
24
+ * prompt is then still suppressed, but its first duplicate logs as the first
25
+ * of a new run.
26
+ *
27
+ * Only the log line is thinned. `recordPromptDedupSkip` (#1695) is still called
28
+ * on every duplicate tick; its tally is what `capture --json` reports.
9
29
  */
10
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL = void 0;
11
32
  exports.isDuplicatePrompt = isDuplicatePrompt;
33
+ exports.claimDuplicatePromptSkipLog = claimDuplicatePromptSkipLog;
34
+ exports.resetDuplicatePromptSkipStreak = resetDuplicatePromptSkipStreak;
12
35
  exports.clearPromptHashCache = clearPromptHashCache;
13
36
  exports.renamePromptHashCacheKey = renamePromptHashCacheKey;
14
37
  exports.normalizePromptForDedup = normalizePromptForDedup;
15
38
  const crypto_1 = require("crypto");
16
39
  const global_state_1 = require("../global-state");
40
+ const response_dedup_1 = require("./response-dedup");
17
41
  const promptHashCache = (0, global_state_1.getOrInitGlobal)('__promptHashCache', () => new Map());
42
+ /**
43
+ * In-memory cache: pollerKey -> how many duplicate ticks in a row the current
44
+ * cached prompt hash has been skipped (Issue #3538). Absence means none yet.
45
+ *
46
+ * On `globalThis` for the same reason `promptHashCache` is, and cleared and
47
+ * moved at exactly the same moments — see the module comment.
48
+ */
49
+ const duplicatePromptSkipStreak = (0, global_state_1.getOrInitGlobal)('__duplicatePromptSkipStreak', () => new Map());
50
+ /**
51
+ * How many duplicate ticks separate two `duplicate-prompt-skipped` lines
52
+ * (Issue #3538). Shared with `duplicate-response-skipped` (#3519): one line a
53
+ * minute at the poller's 2 s tick.
54
+ */
55
+ exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL = response_dedup_1.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL;
18
56
  /**
19
57
  * Check if the given prompt content is a duplicate of the last saved prompt
20
58
  * for the same pollerKey. If not a duplicate, updates the cache.
@@ -29,8 +67,46 @@ function isDuplicatePrompt(pollerKey, content) {
29
67
  return true;
30
68
  }
31
69
  promptHashCache.set(pollerKey, hash);
70
+ // Issue #3538: a new prompt starts a new run of duplicate ticks.
71
+ duplicatePromptSkipStreak.delete(pollerKey);
32
72
  return false;
33
73
  }
74
+ /**
75
+ * Count one duplicate prompt tick for this pollerKey and say whether it should
76
+ * be logged (Issue #3538).
77
+ *
78
+ * A claim: it advances the count, so call it exactly once per duplicate tick.
79
+ * Logs on the first tick of a run and then on every
80
+ * {@link DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL}th tick after it (ticks 1,
81
+ * 1+N, 1+2N, …).
82
+ *
83
+ * @param pollerKey - Poller key ("worktreeId:cliToolId")
84
+ * @returns Whether to log, the run length so far, and the ticks left unlogged
85
+ * since the previous line
86
+ */
87
+ function claimDuplicatePromptSkipLog(pollerKey) {
88
+ const consecutive = (duplicatePromptSkipStreak.get(pollerKey) ?? 0) + 1;
89
+ duplicatePromptSkipStreak.set(pollerKey, consecutive);
90
+ const log = (consecutive - 1) % exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL === 0;
91
+ const suppressed = consecutive === 1 ? 0 : exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL - 1;
92
+ return { log, consecutive, suppressed };
93
+ }
94
+ /**
95
+ * End the current run of duplicate prompt ticks for this pollerKey without
96
+ * touching the cached hash (Issue #3538).
97
+ *
98
+ * Called by `response-checker` on every tick that does not reach the prompt
99
+ * guard with a live prompt — a working screen, an unfinished frame, a reply.
100
+ * The prompt is no longer on screen, so if the same prompt comes back the next
101
+ * duplicate is the first of a new run and logs. The hash stays: whether the
102
+ * returning prompt is saved again is the guard's question (#565), and this
103
+ * Issue only thins the log line.
104
+ *
105
+ * @param pollerKey - Poller key ("worktreeId:cliToolId")
106
+ */
107
+ function resetDuplicatePromptSkipStreak(pollerKey) {
108
+ duplicatePromptSkipStreak.delete(pollerKey);
109
+ }
34
110
  /**
35
111
  * Clear the prompt hash cache for a specific pollerKey.
36
112
  * Called during session cleanup / stopPolling.
@@ -39,6 +115,8 @@ function isDuplicatePrompt(pollerKey, content) {
39
115
  */
40
116
  function clearPromptHashCache(pollerKey) {
41
117
  promptHashCache.delete(pollerKey);
118
+ // Issue #3538: the skip count is about the same hash.
119
+ duplicatePromptSkipStreak.delete(pollerKey);
42
120
  }
43
121
  /**
44
122
  * Move the cached hash from one pollerKey to another.
@@ -57,9 +135,22 @@ function renamePromptHashCacheKey(oldKey, newKey) {
57
135
  if (oldKey === newKey)
58
136
  return;
59
137
  const hash = promptHashCache.get(oldKey);
138
+ const streak = duplicatePromptSkipStreak.get(oldKey);
60
139
  promptHashCache.delete(oldKey);
61
- if (hash !== undefined)
62
- promptHashCache.set(newKey, hash);
140
+ duplicatePromptSkipStreak.delete(oldKey);
141
+ if (hash === undefined)
142
+ return;
143
+ promptHashCache.set(newKey, hash);
144
+ // Issue #3538: the skip count moves with the hash, so a rename mid-run does
145
+ // not restart the thinning and log as if a new run had begun. Whenever the
146
+ // hash under `newKey` is replaced, so is its count — a count left over from
147
+ // the hash that was there before is about a different prompt.
148
+ if (streak !== undefined) {
149
+ duplicatePromptSkipStreak.set(newKey, streak);
150
+ }
151
+ else {
152
+ duplicatePromptSkipStreak.delete(newKey);
153
+ }
63
154
  }
64
155
  /**
65
156
  * Normalize prompt content before deduplication check.