commandmate 0.47.2 → 0.48.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 (396) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +215 -215
  3. package/.next/app-path-routes-manifest.json +41 -41
  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_client-reference-manifest.js +1 -1
  224. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  225. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  226. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  227. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  228. package/.next/server/app/worktrees/[id]/page.js +12 -12
  229. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  230. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  231. package/.next/server/app-paths-manifest.json +41 -41
  232. package/.next/server/chunks/1191.js +11 -4
  233. package/.next/server/chunks/1501.js +1 -1
  234. package/.next/server/chunks/1601.js +2 -2
  235. package/.next/server/chunks/1656.js +2 -2
  236. package/.next/server/chunks/1945.js +3 -3
  237. package/.next/server/chunks/1990.js +103 -0
  238. package/.next/server/chunks/2119.js +1 -1
  239. package/.next/server/chunks/2404.js +1 -1
  240. package/.next/server/chunks/2422.js +1 -1
  241. package/.next/server/chunks/2445.js +1 -1
  242. package/.next/server/chunks/2534.js +1 -1
  243. package/.next/server/chunks/255.js +1 -1
  244. package/.next/server/chunks/3241.js +1 -1
  245. package/.next/server/chunks/3505.js +2 -2
  246. package/.next/server/chunks/4248.js +1 -1
  247. package/.next/server/chunks/4280.js +1 -0
  248. package/.next/server/chunks/5209.js +1 -1
  249. package/.next/server/chunks/6119.js +1 -1
  250. package/.next/server/chunks/6130.js +1 -1
  251. package/.next/server/chunks/6401.js +1 -1
  252. package/.next/server/chunks/6486.js +9 -8
  253. package/.next/server/chunks/6781.js +1 -1
  254. package/.next/server/chunks/720.js +103 -0
  255. package/.next/server/chunks/7604.js +2 -0
  256. package/.next/server/chunks/7905.js +1 -1
  257. package/.next/server/chunks/8581.js +7 -7
  258. package/.next/server/chunks/8728.js +3 -3
  259. package/.next/server/chunks/8791.js +1 -1
  260. package/.next/server/chunks/8807.js +19 -18
  261. package/.next/server/chunks/8918.js +2 -104
  262. package/.next/server/chunks/936.js +1 -1
  263. package/.next/server/chunks/9390.js +1 -1
  264. package/.next/server/chunks/9540.js +1 -1
  265. package/.next/server/chunks/9627.js +3 -3
  266. package/.next/server/chunks/9731.js +2 -2
  267. package/.next/server/middleware-manifest.json +5 -5
  268. package/.next/server/pages/500.html +1 -1
  269. package/.next/server/server-reference-manifest.json +1 -1
  270. package/.next/static/chunks/2099-c3e0ab70a1b95dd3.js +1 -0
  271. package/.next/static/chunks/{2407-a6533805a86d05bd.js → 2407-26139a8a2f1adbe8.js} +1 -1
  272. package/.next/static/chunks/{2619-04bc32f026a0d946.js → 2619-a305ad4810f1393a.js} +1 -1
  273. package/.next/static/chunks/{2863-6a07ec82fb52f4dd.js → 2863-f0121af1cbbedb3b.js} +1 -1
  274. package/.next/static/chunks/{8165-263172c17ae8e8c3.js → 8165-c68b3dc844210e6a.js} +1 -1
  275. package/.next/static/chunks/8407-35bbfc84793d7004.js +1 -0
  276. package/.next/static/chunks/9079-406b84d3cc891a87.js +1 -0
  277. package/.next/static/chunks/app/{layout-538d8d2746ad1588.js → layout-46b4cbf07520d9b6.js} +1 -1
  278. package/.next/static/chunks/app/sessions/page-fb241bed579c67aa.js +1 -0
  279. package/.next/static/chunks/app/worktrees/[id]/page-cbc7749a33450cc5.js +1 -0
  280. package/.next/static/chunks/app/worktrees/[id]/terminal/{page-373ea37b16d930c7.js → page-c601f3b3ece6557e.js} +1 -1
  281. package/.next/trace +13 -13
  282. package/dist/cli/commands/auto-yes.d.ts +24 -0
  283. package/dist/cli/commands/auto-yes.d.ts.map +1 -1
  284. package/dist/cli/commands/auto-yes.js +73 -3
  285. package/dist/cli/commands/instances.d.ts.map +1 -1
  286. package/dist/cli/commands/instances.js +10 -1
  287. package/dist/cli/commands/prompt.d.ts.map +1 -1
  288. package/dist/cli/commands/prompt.js +5 -3
  289. package/dist/cli/commands/send.d.ts.map +1 -1
  290. package/dist/cli/commands/send.js +15 -0
  291. package/dist/cli/commands/wait.d.ts.map +1 -1
  292. package/dist/cli/commands/wait.js +101 -13
  293. package/dist/cli/docs/agent-operations.d.ts +2 -2
  294. package/dist/cli/docs/agent-operations.d.ts.map +1 -1
  295. package/dist/cli/docs/agent-operations.js +27 -0
  296. package/dist/cli/types/api-responses.d.ts +116 -2
  297. package/dist/cli/types/api-responses.d.ts.map +1 -1
  298. package/dist/cli/types/index.d.ts +4 -0
  299. package/dist/cli/types/index.d.ts.map +1 -1
  300. package/dist/cli/utils/resource-resolvers.d.ts +0 -18
  301. package/dist/cli/utils/resource-resolvers.d.ts.map +1 -1
  302. package/dist/cli/utils/resource-resolvers.js +1 -51
  303. package/dist/lib/polling/auto-yes-suppression-reason.d.ts +54 -2
  304. package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -1
  305. package/dist/lib/polling/auto-yes-suppression-reason.js +33 -2
  306. package/dist/lib/prompt-observation/types.d.ts +13 -0
  307. package/dist/lib/prompt-observation/types.d.ts.map +1 -1
  308. package/dist/server/locales/en/worktree.json +2 -1
  309. package/dist/server/locales/ja/worktree.json +2 -1
  310. package/dist/server/src/lib/auto-yes-poller-answer-target.js +38 -0
  311. package/dist/server/src/lib/auto-yes-poller-keys.js +35 -0
  312. package/dist/server/src/lib/auto-yes-poller-session-presence.js +26 -0
  313. package/dist/server/src/lib/auto-yes-poller-tick.js +233 -0
  314. package/dist/server/src/lib/auto-yes-poller.js +337 -376
  315. package/dist/server/src/lib/auto-yes-state.js +50 -2
  316. package/dist/server/src/lib/cli-tools/submit-verified-sender.js +107 -14
  317. package/dist/server/src/lib/db/chat-db.js +5 -1
  318. package/dist/server/src/lib/db/verification-db.js +23 -2
  319. package/dist/server/src/lib/detection/option-recommendation.js +120 -0
  320. package/dist/server/src/lib/detection/prompt-detect-multiple-choice-approval-context.js +290 -0
  321. package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +396 -350
  322. package/dist/server/src/lib/detection/prompt-detector.js +92 -21
  323. package/dist/server/src/lib/detection/prompt-screen-key.js +60 -0
  324. package/dist/server/src/lib/detection/tools/antigravity/dialog.js +158 -35
  325. package/dist/server/src/lib/detection/tools/claude/detect.js +111 -10
  326. package/dist/server/src/lib/detection/tools/command-code/dialog.js +67 -0
  327. package/dist/server/src/lib/detection/tools/dialog-block.js +16 -0
  328. package/dist/server/src/lib/detection/waiting-screen-reading.js +98 -0
  329. package/dist/server/src/lib/hooks/permission-decision-service.js +7 -0
  330. package/dist/server/src/lib/polling/auto-yes-confirm-page.js +2 -2
  331. package/dist/server/src/lib/polling/auto-yes-confirmation-ledger-types.js +3 -0
  332. package/dist/server/src/lib/polling/auto-yes-confirmation-ledger.js +34 -3
  333. package/dist/server/src/lib/polling/auto-yes-decision-state.js +195 -0
  334. package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +139 -10
  335. package/dist/server/src/lib/polling/auto-yes-enter-fallback.js +14 -0
  336. package/dist/server/src/lib/polling/auto-yes-manual-answer.js +122 -0
  337. package/dist/server/src/lib/polling/auto-yes-option-effect.js +157 -0
  338. package/dist/server/src/lib/polling/auto-yes-policy.js +11 -5
  339. package/dist/server/src/lib/polling/auto-yes-poller-capture.js +15 -2
  340. package/dist/server/src/lib/polling/auto-yes-poller-generation.js +42 -1
  341. package/dist/server/src/lib/polling/auto-yes-poller-receipt-log.js +42 -5
  342. package/dist/server/src/lib/polling/auto-yes-poller-state.js +147 -49
  343. package/dist/server/src/lib/polling/auto-yes-poller-steps.js +172 -2
  344. package/dist/server/src/lib/polling/auto-yes-prompt-suppression.js +2 -2
  345. package/dist/server/src/lib/polling/auto-yes-question-choice.js +57 -0
  346. package/dist/server/src/lib/polling/auto-yes-resolver.js +96 -21
  347. package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +33 -2
  348. package/dist/server/src/lib/polling/auto-yes-suppression-state.js +25 -1
  349. package/dist/server/src/lib/polling/prompt-screen-reading.js +232 -0
  350. package/dist/server/src/lib/polling/response-checker-dialog-gate.js +39 -0
  351. package/dist/server/src/lib/polling/response-checker.js +133 -43
  352. package/dist/server/src/lib/prompt-answer-sender.js +104 -10
  353. package/dist/server/src/lib/push/notification-dedup.js +15 -2
  354. package/dist/server/src/lib/push/prompt-card-state.js +140 -19
  355. package/dist/server/src/lib/push/prompt-push-gate.js +158 -12
  356. package/dist/server/src/lib/push/push-sender.js +23 -3
  357. package/dist/server/src/lib/push/resolution-push-notifier.js +14 -0
  358. package/dist/server/src/lib/push/waiting-push-notifier.js +139 -17
  359. package/dist/server/src/lib/realtime/terminal-broadcast.js +16 -0
  360. package/dist/server/src/lib/session/agent-event-state.js +25 -5
  361. package/dist/server/src/lib/session/agent-event-turn.js +140 -46
  362. package/dist/server/src/lib/session/cli-session.js +21 -2
  363. package/dist/server/src/lib/session/current-output-builder.js +64 -11
  364. package/dist/server/src/lib/session/send-attempt-state.js +23 -7
  365. package/dist/server/src/lib/session/waiting-episode-binding-observation.js +289 -0
  366. package/dist/server/src/lib/session/waiting-episode-binding-resolve.js +38 -0
  367. package/dist/server/src/lib/session/waiting-episode-binding.js +276 -0
  368. package/dist/server/src/lib/session/waiting-episode-state.js +307 -17
  369. package/dist/server/src/lib/session/waiting-episode-verdict.js +174 -0
  370. package/dist/server/src/lib/tmux/tmux-capture-cache.js +76 -11
  371. package/dist/server/src/lib/tmux/tmux.js +54 -9
  372. package/dist/server/src/lib/verification/gate-runner.js +162 -13
  373. package/dist/server/src/lib/verification/machine-lock.js +5 -0
  374. package/dist/server/src/lib/ws-server.js +18 -3
  375. package/dist/server/src/types/auto-yes-answer-mode.js +38 -0
  376. package/dist/server/src/types/auto-yes-decision.js +61 -0
  377. package/dist/server/src/types/auto-yes.js +6 -0
  378. package/dist/server/src/types/prompt-reading.js +32 -0
  379. package/dist/types/auto-yes-answer-mode.d.ts +35 -0
  380. package/dist/types/auto-yes-answer-mode.d.ts.map +1 -0
  381. package/dist/types/auto-yes-answer-mode.js +38 -0
  382. package/docs/en/user-guide/webapp-guide.md +33 -4
  383. package/package.json +1 -1
  384. package/public/sw.js +76 -4
  385. package/release-notes/0.48.0.json +57 -0
  386. package/.next/server/chunks/1559.js +0 -2
  387. package/.next/server/chunks/2460.js +0 -1
  388. package/.next/server/chunks/5595.js +0 -1
  389. package/.next/server/chunks/610.js +0 -1
  390. package/.next/static/chunks/602-1c1488959215e140.js +0 -1
  391. package/.next/static/chunks/8407-155f84e5ba05f1d5.js +0 -1
  392. package/.next/static/chunks/9079-24f9fa549ed298f1.js +0 -1
  393. package/.next/static/chunks/app/sessions/page-e02f1adb1d1c1ccc.js +0 -1
  394. package/.next/static/chunks/app/worktrees/[id]/page-46aced20b2b1c010.js +0 -1
  395. /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → PTlAswXLq8PAe2vUJ8dVP}/_buildManifest.js +0 -0
  396. /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → PTlAswXLq8PAe2vUJ8dVP}/_ssgManifest.js +0 -0
@@ -88,12 +88,27 @@
88
88
  * this module's; it is written up as a remaining limitation in §6.2 of the
89
89
  * design note and pinned by `tests/unit/push/restart-card-state-2057.test.ts`.
90
90
  *
91
+ * ## Which episode the card is about (Issue #3798)
92
+ *
93
+ * The mark also keeps the id of the waiting episode the card was last sent
94
+ * for, so the resolution can name it (`resolvedEpisodeId`) and the Service
95
+ * Worker can leave a newer episode's card alone. The id lives in a second row,
96
+ * {@link PROMPT_CARD_EPISODE_KEY_PREFIX}`<worktreeId>`, written in the same
97
+ * statement as the time row — one write, so a reader never sees a new time
98
+ * with an old id — and its value repeats the time (`<at>:<episodeId>`): an id
99
+ * whose time is not the time row's is not this card's and reads as none. The
100
+ * time row's value stays the bare timestamp it was before #3798, so a mark
101
+ * written by an older server (time row only) reads as a card with no id, and
102
+ * an older server reading a newer mark sees exactly what it wrote.
103
+ *
91
104
  * @module lib/push/prompt-card-state
92
105
  */
93
106
  Object.defineProperty(exports, "__esModule", { value: true });
94
- exports.PROMPT_CARD_MAX_AGE_MS = exports.PROMPT_CARD_KEY_PREFIX = void 0;
107
+ exports.PROMPT_CARD_MAX_AGE_MS = exports.PROMPT_ORDER_KEY_PREFIX = exports.PROMPT_CARD_EPISODE_KEY_PREFIX = exports.PROMPT_CARD_KEY_PREFIX = void 0;
95
108
  exports.markPromptCardShown = markPromptCardShown;
109
+ exports.nextPromptOrder = nextPromptOrder;
96
110
  exports.hasPromptCard = hasPromptCard;
111
+ exports.getPromptCardEpisodeId = getPromptCardEpisodeId;
97
112
  exports.clearPromptCard = clearPromptCard;
98
113
  exports.clearAllPromptCards = clearAllPromptCards;
99
114
  exports.forgetPromptCardMemory = forgetPromptCardMemory;
@@ -111,6 +126,20 @@ const logger = (0, logger_1.createLogger)('push/prompt-card-state');
111
126
  * spell another id's key.
112
127
  */
113
128
  exports.PROMPT_CARD_KEY_PREFIX = 'push_prompt_card:';
129
+ /**
130
+ * `app_settings.key` prefix for the episode id beside one worktree's card mark
131
+ * (Issue #3798). Not matched by `${PROMPT_CARD_KEY_PREFIX}*`: the character
132
+ * after `push_prompt_card` is `_` here and `:` there.
133
+ */
134
+ exports.PROMPT_CARD_EPISODE_KEY_PREFIX = 'push_prompt_card_episode:';
135
+ /**
136
+ * `app_settings.key` prefix for one worktree's prompt push order (Issue #3798):
137
+ * a counter that moves once per episode-named waiting card and once per
138
+ * resolution that names its episode, so the Service Worker can tell a card
139
+ * sent after a resolution was decided from one sent before it. Not a card row:
140
+ * no card GLOB matches it and nothing clears it, so the order never goes back.
141
+ */
142
+ exports.PROMPT_ORDER_KEY_PREFIX = 'push_prompt_order:';
114
143
  /**
115
144
  * How long a persisted mark is believed.
116
145
  *
@@ -144,25 +173,51 @@ exports.PROMPT_CARD_KEY_PREFIX = 'push_prompt_card:';
144
173
  exports.PROMPT_CARD_MAX_AGE_MS = 24 * 60 * 60 * 1000;
145
174
  /** worktreeId -> epoch ms the most recent prompt card was fanned out. */
146
175
  const cards = (0, global_state_1.getOrInitGlobal)('__promptPushCards', () => new Map());
176
+ /**
177
+ * worktreeId -> the episode id that card was sent for, or null (Issue #3798).
178
+ * Set and dropped together with {@link cards}; a worktree in `cards` with no
179
+ * entry here reads as id-less.
180
+ */
181
+ const cardEpisodes = (0, global_state_1.getOrInitGlobal)('__promptPushCardEpisodes', () => new Map());
147
182
  function keyFor(worktreeId) {
148
183
  return `${exports.PROMPT_CARD_KEY_PREFIX}${worktreeId}`;
149
184
  }
185
+ function episodeKeyFor(worktreeId) {
186
+ return `${exports.PROMPT_CARD_EPISODE_KEY_PREFIX}${worktreeId}`;
187
+ }
188
+ /**
189
+ * The episode id from its row, if that row belongs to the mark written at `at`.
190
+ * `<at>:<id>`; an empty id, a different time or a malformed value is no id.
191
+ */
192
+ function parseEpisodeValue(value, at) {
193
+ if (value === undefined)
194
+ return null;
195
+ const sep = value.indexOf(':');
196
+ if (sep < 0 || value.slice(0, sep) !== String(at))
197
+ return null;
198
+ const id = value.slice(sep + 1);
199
+ return id === '' ? null : id;
200
+ }
150
201
  function isExpired(at, now) {
151
202
  return now - at >= exports.PROMPT_CARD_MAX_AGE_MS;
152
203
  }
153
204
  /** The persisted mark for one worktree, or null. Never throws. */
154
205
  function readRow(worktreeId) {
155
206
  try {
156
- const row = (0, db_instance_1.getDbInstance)()
157
- .prepare('SELECT value FROM app_settings WHERE key = ?')
158
- .get(keyFor(worktreeId));
207
+ const rows = (0, db_instance_1.getDbInstance)()
208
+ .prepare('SELECT key, value FROM app_settings WHERE key IN (?, ?)')
209
+ .all(keyFor(worktreeId), episodeKeyFor(worktreeId));
210
+ const row = rows.find((r) => r.key === keyFor(worktreeId));
159
211
  if (!row)
160
212
  return null;
161
213
  const at = Number.parseInt(row.value, 10);
162
214
  // A value nobody can read is treated as no card: it fails towards *not*
163
215
  // spending a push, the same direction `decidePromptResolution` chose for a
164
216
  // database that cannot answer.
165
- return Number.isFinite(at) ? at : null;
217
+ if (!Number.isFinite(at))
218
+ return null;
219
+ const episodeRow = rows.find((r) => r.key === episodeKeyFor(worktreeId));
220
+ return { at, episodeId: parseEpisodeValue(episodeRow?.value, at) };
166
221
  }
167
222
  catch (error) {
168
223
  logger.debug('prompt-card-read-failed', {
@@ -195,18 +250,24 @@ function readAllRows() {
195
250
  return [];
196
251
  }
197
252
  }
198
- /** Write one worktree's mark through to storage. Never throws. */
199
- function writeRow(worktreeId, at) {
253
+ /**
254
+ * Write one worktree's mark through to storage. Never throws.
255
+ *
256
+ * Issue #3798: the time row and the episode row in one statement — an id-less
257
+ * mark writes an empty id, so a newer id-less card never leaves an older
258
+ * card's id behind.
259
+ */
260
+ function writeRow(worktreeId, at, episodeId) {
200
261
  try {
201
262
  (0, db_instance_1.getDbInstance)()
202
263
  .prepare(`
203
264
  INSERT INTO app_settings (key, value, created_at, updated_at)
204
- VALUES (?, ?, ?, ?)
265
+ VALUES (?, ?, ?, ?), (?, ?, ?, ?)
205
266
  ON CONFLICT(key) DO UPDATE SET
206
267
  value = excluded.value,
207
268
  updated_at = excluded.updated_at
208
269
  `)
209
- .run(keyFor(worktreeId), String(at), at, at);
270
+ .run(keyFor(worktreeId), String(at), at, at, episodeKeyFor(worktreeId), `${at}:${episodeId ?? ''}`, at, at);
210
271
  }
211
272
  catch (error) {
212
273
  logger.debug('prompt-card-write-failed', {
@@ -218,8 +279,9 @@ function writeRow(worktreeId, at) {
218
279
  /** @returns True when a row was actually removed. Never throws. */
219
280
  function deleteRow(worktreeId) {
220
281
  try {
221
- return ((0, db_instance_1.getDbInstance)().prepare('DELETE FROM app_settings WHERE key = ?').run(keyFor(worktreeId))
222
- .changes > 0);
282
+ return ((0, db_instance_1.getDbInstance)()
283
+ .prepare('DELETE FROM app_settings WHERE key IN (?, ?)')
284
+ .run(keyFor(worktreeId), episodeKeyFor(worktreeId)).changes > 0);
223
285
  }
224
286
  catch (error) {
225
287
  logger.debug('prompt-card-delete-failed', {
@@ -235,10 +297,49 @@ function deleteRow(worktreeId) {
235
297
  * Idempotent by overwrite: a second prompt in the same worktree (another
236
298
  * instance, or the #1790 reminder) replaces the same card on the device, so it
237
299
  * refreshes the timestamp rather than adding an entry.
300
+ *
301
+ * @param episodeId Issue #3798: the waiting episode this card is about, or
302
+ * null when the producer did not name one. The last card's id replaces the
303
+ * previous one — the device shows one card per worktree.
238
304
  */
239
- function markPromptCardShown(worktreeId, at = Date.now()) {
305
+ function markPromptCardShown(worktreeId, at = Date.now(), episodeId = null) {
240
306
  cards.set(worktreeId, at);
241
- writeRow(worktreeId, at);
307
+ cardEpisodes.set(worktreeId, episodeId);
308
+ writeRow(worktreeId, at, episodeId);
309
+ }
310
+ /**
311
+ * Take this worktree's next prompt push order (Issue #3798). Never throws.
312
+ *
313
+ * One statement reads and advances the counter, so two decisions never share a
314
+ * value — not even within the same millisecond — and it survives a restart
315
+ * with the database. The episode's `since` cannot do this job: a structured
316
+ * since is reused by the next episode and can be older than the observation.
317
+ *
318
+ * @returns The new value, or null when storage cannot answer; the push then
319
+ * carries no order and the Service Worker treats it as it did before #3798.
320
+ */
321
+ function nextPromptOrder(worktreeId, at = Date.now()) {
322
+ try {
323
+ const row = (0, db_instance_1.getDbInstance)()
324
+ .prepare(`
325
+ INSERT INTO app_settings (key, value, created_at, updated_at)
326
+ VALUES (?, '1', ?, ?)
327
+ ON CONFLICT(key) DO UPDATE SET
328
+ value = CAST(app_settings.value AS INTEGER) + 1,
329
+ updated_at = excluded.updated_at
330
+ RETURNING value
331
+ `)
332
+ .get(`${exports.PROMPT_ORDER_KEY_PREFIX}${worktreeId}`, at, at);
333
+ const order = Number(row?.value);
334
+ return Number.isSafeInteger(order) && order > 0 ? order : null;
335
+ }
336
+ catch (error) {
337
+ logger.debug('prompt-order-advance-failed', {
338
+ worktreeId,
339
+ error: error instanceof Error ? error.message : String(error),
340
+ });
341
+ return null;
342
+ }
242
343
  }
243
344
  /**
244
345
  * Whether a prompt card for this worktree was fanned out and not yet cleared.
@@ -253,21 +354,38 @@ function markPromptCardShown(worktreeId, at = Date.now()) {
253
354
  * {@link PROMPT_CARD_MAX_AGE_MS} without a fake clock.
254
355
  */
255
356
  function hasPromptCard(worktreeId, now = Date.now()) {
256
- const at = cards.get(worktreeId) ?? readRow(worktreeId);
257
- if (at === null || at === undefined)
357
+ const remembered = cards.get(worktreeId);
358
+ const mark = remembered !== undefined
359
+ ? { at: remembered, episodeId: cardEpisodes.get(worktreeId) ?? null }
360
+ : readRow(worktreeId);
361
+ if (mark === null)
258
362
  return false;
259
- if (isExpired(at, now)) {
363
+ if (isExpired(mark.at, now)) {
260
364
  // Dropped as it is read rather than by a sweep: this is the only question
261
365
  // asked about a given worktree's card, so the read is the moment the
262
366
  // expiry becomes observable, and there is nothing a background pass would
263
367
  // catch earlier.
264
368
  cards.delete(worktreeId);
369
+ cardEpisodes.delete(worktreeId);
265
370
  deleteRow(worktreeId);
266
371
  return false;
267
372
  }
268
- cards.set(worktreeId, at);
373
+ cards.set(worktreeId, mark.at);
374
+ cardEpisodes.set(worktreeId, mark.episodeId);
269
375
  return true;
270
376
  }
377
+ /**
378
+ * The episode id the worktree's outstanding card was sent for (Issue #3798),
379
+ * or null — no card, or a card marked without an id (including every mark an
380
+ * older server wrote). Read the same way {@link hasPromptCard} reads the mark:
381
+ * memory first, storage after a restart. Does not expire anything; ask
382
+ * {@link hasPromptCard} first.
383
+ */
384
+ function getPromptCardEpisodeId(worktreeId) {
385
+ if (cards.has(worktreeId))
386
+ return cardEpisodes.get(worktreeId) ?? null;
387
+ return readRow(worktreeId)?.episodeId ?? null;
388
+ }
271
389
  /**
272
390
  * Forget this worktree's card, in memory and in storage.
273
391
  *
@@ -277,6 +395,7 @@ function hasPromptCard(worktreeId, now = Date.now()) {
277
395
  * from `app_settings` still reports as cleared.
278
396
  */
279
397
  function clearPromptCard(worktreeId) {
398
+ cardEpisodes.delete(worktreeId);
280
399
  const wasInMemory = cards.delete(worktreeId);
281
400
  const wasPersisted = deleteRow(worktreeId);
282
401
  return wasInMemory || wasPersisted;
@@ -290,10 +409,11 @@ function clearPromptCard(worktreeId) {
290
409
  */
291
410
  function clearAllPromptCards() {
292
411
  cards.clear();
412
+ cardEpisodes.clear();
293
413
  try {
294
414
  (0, db_instance_1.getDbInstance)()
295
- .prepare('DELETE FROM app_settings WHERE key GLOB ?')
296
- .run(`${exports.PROMPT_CARD_KEY_PREFIX}*`);
415
+ .prepare('DELETE FROM app_settings WHERE key GLOB ? OR key GLOB ?')
416
+ .run(`${exports.PROMPT_CARD_KEY_PREFIX}*`, `${exports.PROMPT_CARD_EPISODE_KEY_PREFIX}*`);
297
417
  }
298
418
  catch (error) {
299
419
  logger.debug('prompt-card-clear-all-failed', {
@@ -312,6 +432,7 @@ function clearAllPromptCards() {
312
432
  */
313
433
  function forgetPromptCardMemory() {
314
434
  cards.clear();
435
+ cardEpisodes.clear();
315
436
  }
316
437
  /**
317
438
  * How many worktrees have an outstanding, unexpired card. Test seam.
@@ -47,6 +47,12 @@
47
47
  * `at` lines up with the current wait. Here that is `at >= waitingSince` — the
48
48
  * suppression was recorded at or after this episode opened. A record from an
49
49
  * earlier episode is older than `since` and is ignored.
50
+ * Issue #3798: a record that names its episode (`episodeId`) is matched to a
51
+ * wait that names one by that id instead, since two episodes can share a
52
+ * `since`; either side without an id falls back to the time comparison. A
53
+ * record of the episode the poller then answered (`lastAnswer`), written
54
+ * before that answer, is not the wait's reason any more: the success path of
55
+ * a send does not clear the record, and the answer is the newer fact.
50
56
  *
51
57
  * The poller re-evaluates a suppressed prompt on every poll (the duplicate
52
58
  * guard only remembers *answered* prompts), so `at` stays current for as long
@@ -75,6 +81,20 @@
75
81
  * second prompt right after an answer gets a full hold of its own. Both
76
82
  * producers ask through {@link decideAndLogPromptPush} and share that origin.
77
83
  *
84
+ * ## Which hold belongs to which wait (Issue #3798)
85
+ *
86
+ * A wait is named by its episode id when the caller has one
87
+ * (`waitingEpisodeId`), and by `waitingSince` when it does not. The two are
88
+ * matched by the rule {@link isSameWait} states, the same rule the episode
89
+ * dedup in `notification-dedup` uses: both ids present and equal, or one of
90
+ * them absent and the `since`s equal. So an id-less producer (opencode's
91
+ * question push, or the response poller when its commit changed nothing —
92
+ * otherwise it names the episode it committed) and the id-bearing notifier asking about
93
+ * one wait share one hold, and the session wait's tick (#3796) is read once,
94
+ * when that hold begins. A rebind keeps the id, so it keeps the hold and its
95
+ * end; a replacement brings a new id, so it begins a new hold that reads the
96
+ * tick at its own start — even when the new wait reuses the old `since`.
97
+ *
78
98
  * ## The hold reaches the session wait's tick (Issue #3791)
79
99
  *
80
100
  * The 9 s assume the poller's next tick is at most a cooldown away. Its ticks
@@ -93,6 +113,38 @@
93
113
  * that tick forward to a poll interval, and the hold follows it — the tick is
94
114
  * only ever read earlier than it was when the hold began.
95
115
  *
116
+ * The other tick past the 9 s is the duplicate guard's (#306). A prompt with
117
+ * the type and question of the one the poller just answered — Claude's
118
+ * consecutive Bash approvals all ask `Do you want to proceed?` — is not
119
+ * answered for `DUPLICATE_RETRY_EXPIRY_MS` (10 s) after that answer, and is
120
+ * answered on the first tick after. While the poller reports that the guard
121
+ * actually held back the prompt of this wait (`duplicateRetryReservation`,
122
+ * review N1: its tick matched the guard's key, since this wait opened, and no
123
+ * tick since read another screen, no prompt, no session or failed; the tick
124
+ * sending the retry keeps it until that send is settled, review N3) the hold
125
+ * also lasts until that retry plus two ticks: {@link duplicateRetryHoldEndsAt}.
126
+ * An answer alone does not: a different screen the poller will not answer, or
127
+ * an error backoff, after an answer still notifies at the usual 9 s. Read on
128
+ * every decision, not once: the reservation appears on the poller's next tick,
129
+ * after the wait may already be held, and the notifier asks again when the 9 s
130
+ * end.
131
+ *
132
+ * ## A wait the poller answered is not "not answered" (Issue #3798)
133
+ *
134
+ * The poller records which episode its answer went to (`lastAnswer`, read off
135
+ * its state on `globalThis` like the answer's time), and closes that episode
136
+ * when the confirmation is gone. Until a frame says so — the agent works on
137
+ * over frames no rule reads — the episode stays open, and its hold can run
138
+ * out. That end is not "Auto-Yes did not answer": when the wait asked about
139
+ * names its id and it is the answered episode, still open and not a screen
140
+ * only a person drives, the gate says `auto-yes-answered` and stays quiet.
141
+ * Another confirmation in the same turn is another episode (R1-7), and a
142
+ * survey or a picker after the answer turns the episode into a `screen`: both
143
+ * notify as before. Without an id on either side nothing is matched, and the
144
+ * hold's end notifies as before. The hold itself is unchanged — its start, its
145
+ * end, the session wait's tick (#3796) and the duplicate guard's retry (#3791)
146
+ * are counted as above.
147
+ *
96
148
  * ## The reminder is never muted
97
149
  *
98
150
  * `runEscalationTick` has already established that the wait is still open — it
@@ -109,17 +161,20 @@
109
161
  */
110
162
  Object.defineProperty(exports, "__esModule", { value: true });
111
163
  exports.AUTO_YES_HOLD_MAX_AGE_MS = exports.AUTO_YES_ANSWER_AFTER_TICK_MS = exports.AUTO_YES_ANSWER_GRACE_MS = void 0;
164
+ exports.isSameWait = isSameWait;
112
165
  exports.autoYesHoldStartedAt = autoYesHoldStartedAt;
113
166
  exports.autoYesHoldEndsAt = autoYesHoldEndsAt;
114
167
  exports.forgetAutoYesHold = forgetAutoYesHold;
115
168
  exports.clearAutoYesHolds = clearAutoYesHolds;
116
169
  exports.decidePromptPush = decidePromptPush;
170
+ exports.autoYesAnsweredWait = autoYesAnsweredWait;
117
171
  exports.isPromptPushSuppressed = isPromptPushSuppressed;
118
172
  exports.decideAndLogPromptPush = decideAndLogPromptPush;
119
173
  const auto_yes_state_1 = require("../../lib/auto-yes-state");
120
174
  const logger_1 = require("../../lib/logger");
121
175
  const auto_yes_suppression_state_1 = require("../../lib/polling/auto-yes-suppression-state");
122
176
  const global_state_1 = require("../../lib/global-state");
177
+ const waiting_episode_state_1 = require("../../lib/session/waiting-episode-state");
123
178
  const logger = (0, logger_1.createLogger)('push/prompt-gate');
124
179
  /**
125
180
  * How long a wait with nothing recorded about it is held as "Auto-Yes is
@@ -132,6 +187,20 @@ const logger = (0, logger_1.createLogger)('push/prompt-gate');
132
187
  exports.AUTO_YES_ANSWER_GRACE_MS = Math.max(3 * auto_yes_state_1.POLLING_INTERVAL_MS, auto_yes_state_1.COOLDOWN_INTERVAL_MS + 2 * auto_yes_state_1.POLLING_INTERVAL_MS);
133
188
  /** compositeKey -> the hold of that session's current episode. globalThis per #1736. */
134
189
  const autoYesHolds = (0, global_state_1.getOrInitGlobal)('__promptPushAutoYesHolds', () => new Map());
190
+ /**
191
+ * Whether two names are the same wait (Issue #3798): both ids present and
192
+ * equal, or one of them absent and the `since`s equal. The id decides whenever
193
+ * both sides have one — two waits can share a `since` (the same millisecond, a
194
+ * structured `at` reused by the next prompt), and one wait keeps its id across
195
+ * a rebind.
196
+ */
197
+ function isSameWait(a, b) {
198
+ const aId = a.episodeId ?? null;
199
+ const bId = b.episodeId ?? null;
200
+ if (aId !== null && bId !== null)
201
+ return aId === bId;
202
+ return a.since === b.since;
203
+ }
135
204
  /**
136
205
  * Where this wait's `auto-yes-answering` hold is counted from (Issue #3757):
137
206
  * the later of the moment the gate first held this episode (recorded here on
@@ -146,11 +215,21 @@ const autoYesHolds = (0, global_state_1.getOrInitGlobal)('__promptPushAutoYesHol
146
215
  function autoYesHoldStartedAt(input, now) {
147
216
  const key = (0, auto_yes_state_1.buildCompositeKey)(input.worktreeId, input.cliToolId, input.instanceId);
148
217
  sweepStaleAutoYesHolds(now);
218
+ const episodeId = input.waitingEpisodeId ?? undefined;
149
219
  let hold = autoYesHolds.get(key);
150
- if (hold === undefined || hold.since !== input.waitingSince) {
151
- hold = { since: input.waitingSince, firstHeldAt: now, sessionWaitTick: pendingSessionWaitTick(key, now) };
220
+ if (hold === undefined || !isSameWait(hold, { since: input.waitingSince, episodeId })) {
221
+ hold = {
222
+ since: input.waitingSince,
223
+ ...(episodeId !== undefined ? { episodeId } : {}),
224
+ firstHeldAt: now,
225
+ sessionWaitTick: pendingSessionWaitTick(key, now),
226
+ };
152
227
  autoYesHolds.set(key, hold);
153
228
  }
229
+ else if (hold.episodeId === undefined && episodeId !== undefined) {
230
+ // Issue #3798: an id-less caller began this hold; the wait is now named.
231
+ hold.episodeId = episodeId;
232
+ }
154
233
  // Issue #3770: Auto-Yes switched off and on again inside one episode starts a
155
234
  // fresh hold; the old origin would read the poller's silence while it was off
156
235
  // as "did not answer".
@@ -199,7 +278,8 @@ function effectiveSessionWaitTickAt(tick) {
199
278
  * When this wait's `auto-yes-answering` hold ends (Issue #3791): a full
200
279
  * {@link AUTO_YES_ANSWER_GRACE_MS} after {@link autoYesHoldStartedAt}, and not
201
280
  * before {@link AUTO_YES_ANSWER_AFTER_TICK_MS} past the session wait's tick
202
- * pending when the hold began — while the poller that scheduled it still runs.
281
+ * pending when the hold began — while the poller that scheduled it still runs —
282
+ * nor past the duplicate guard's retry the poller reserved for this wait.
203
283
  *
204
284
  * @param now - Epoch ms of the decision; recorded as the start of a new hold
205
285
  */
@@ -208,14 +288,35 @@ function autoYesHoldEndsAt(input, now) {
208
288
  const key = (0, auto_yes_state_1.buildCompositeKey)(input.worktreeId, input.cliToolId, input.instanceId);
209
289
  const tick = autoYesHolds.get(key)?.sessionWaitTick;
210
290
  const graceEndsAt = startedAt + exports.AUTO_YES_ANSWER_GRACE_MS;
291
+ const holdEndsAt = Math.max(graceEndsAt, duplicateRetryHoldEndsAt(key, input.waitingSince));
211
292
  // A poller stopped or replaced since (disable, off → on) will not tick.
212
293
  if (tick === undefined || globalThis.__autoYesPollerStates?.get(key) !== tick.poller)
213
- return graceEndsAt;
294
+ return holdEndsAt;
214
295
  // Kept on the hold: an earlier tick once adopted is not lost when the poller
215
296
  // later reschedules (an ordinary tick after a failed capture, a new session
216
297
  // wait) — the hold never moves back out.
217
298
  tick.at = effectiveSessionWaitTickAt(tick);
218
- return Math.max(graceEndsAt, tick.at + exports.AUTO_YES_ANSWER_AFTER_TICK_MS);
299
+ return Math.max(holdEndsAt, tick.at + exports.AUTO_YES_ANSWER_AFTER_TICK_MS);
300
+ }
301
+ /**
302
+ * Issue #3791: when the duplicate guard (#306) lets the running poller answer
303
+ * again, plus {@link AUTO_YES_ANSWER_AFTER_TICK_MS} — or -Infinity unless the
304
+ * poller reserved that retry for this wait (review N1): a reservation on the
305
+ * live poller (`__autoYesPollerStates`, so a stopped or replaced one has none),
306
+ * made by a tick since the wait opened, for the answer the guard still counts
307
+ * from. Read off `globalThis` for the reason {@link autoYesHoldStartedAt} gives.
308
+ */
309
+ function duplicateRetryHoldEndsAt(key, waitingSince) {
310
+ const poller = globalThis.__autoYesPollerStates?.get(key);
311
+ const reservation = poller?.duplicateRetryReservation;
312
+ if (poller === undefined || reservation === undefined || reservation === null)
313
+ return -Infinity;
314
+ if (reservation.observedAt < waitingSince)
315
+ return -Infinity;
316
+ if (reservation.answeredAt !== poller.lastAnsweredAt || reservation.promptKey !== poller.lastAnsweredPromptKey) {
317
+ return -Infinity;
318
+ }
319
+ return reservation.answeredAt + auto_yes_state_1.DUPLICATE_RETRY_EXPIRY_MS + exports.AUTO_YES_ANSWER_AFTER_TICK_MS;
219
320
  }
220
321
  /** A hold older than this is dropped (Issue #3770): its episode is long over. */
221
322
  exports.AUTO_YES_HOLD_MAX_AGE_MS = 60 * 60 * 1000;
@@ -229,14 +330,21 @@ function sweepStaleAutoYesHolds(now) {
229
330
  * Drop the hold of one session (Issue #3770): Auto-Yes went off, or the
230
331
  * episode closed.
231
332
  *
232
- * @param since - Issue #3776: drop it only when it is this episode's hold. A
333
+ * @param wait - Issue #3776: drop it only when it is this episode's hold. A
233
334
  * caller retiring one episode must not take the next episode's origin with it
234
335
  * (that hold would start over, and its notification come up to a hold late).
235
- * Omitted, the session's hold goes whatever its episode.
336
+ * A number is the episode's `since`; Issue #3798 adds the episode's id, matched
337
+ * by {@link isSameWait}. Omitted, the session's hold goes whatever its episode.
236
338
  */
237
- function forgetAutoYesHold(key, since) {
238
- if (since !== undefined && autoYesHolds.get(key)?.since !== since)
239
- return;
339
+ function forgetAutoYesHold(key, wait) {
340
+ if (wait !== undefined) {
341
+ const hold = autoYesHolds.get(key);
342
+ if (hold === undefined)
343
+ return;
344
+ const identity = typeof wait === 'number' ? { since: wait } : wait;
345
+ if (!isSameWait(hold, identity))
346
+ return;
347
+ }
240
348
  autoYesHolds.delete(key);
241
349
  }
242
350
  /** Forget every hold. Test seam. */
@@ -251,13 +359,16 @@ function clearAutoYesHolds() {
251
359
  * rather than only the outcome.
252
360
  */
253
361
  function decidePromptPush(input) {
254
- const { worktreeId, cliToolId, instanceId, waitingSince } = input;
362
+ const { worktreeId, cliToolId, instanceId } = input;
255
363
  const state = (0, auto_yes_state_1.getAutoYesState)(worktreeId, cliToolId, instanceId);
256
364
  if (state === null || !state.enabled) {
257
365
  return { suppress: false, reason: 'auto-yes-inactive', stopReason: state?.stopReason };
258
366
  }
259
367
  const suppression = (0, auto_yes_suppression_state_1.getLastPolicySuppression)(worktreeId, cliToolId, instanceId);
260
- if (suppression !== null && suppression.at >= waitingSince) {
368
+ if (suppression !== null &&
369
+ suppressionBelongsToWait(suppression, input) &&
370
+ // The answer that came after the record is the newer fact (Issue #3798).
371
+ !(0, auto_yes_suppression_state_1.suppressionPredatesAnswer)(suppression, input.waitingEpisodeId, answerTo(input))) {
261
372
  return { suppress: false, reason: 'policy-withheld', suppressedBy: suppression.reason };
262
373
  }
263
374
  if (input.escalated === true) {
@@ -265,10 +376,44 @@ function decidePromptPush(input) {
265
376
  }
266
377
  const holdEndsAt = input.holdEndsAt ?? (input.holdStartedAt !== undefined ? input.holdStartedAt + exports.AUTO_YES_ANSWER_GRACE_MS : undefined);
267
378
  if (holdEndsAt !== undefined && (input.now ?? Date.now()) >= holdEndsAt) {
379
+ if (autoYesAnsweredWait(input))
380
+ return { suppress: true, reason: 'auto-yes-answered' };
268
381
  return { suppress: false, reason: 'auto-yes-not-answered' };
269
382
  }
270
383
  return { suppress: true, reason: 'auto-yes-answering' };
271
384
  }
385
+ /**
386
+ * Whether the Auto-Yes poller answered this wait (Issue #3798): the caller
387
+ * names the episode, the poller's `lastAnswer` names the same one, and that
388
+ * episode is still the one open and not a screen only a person drives (a
389
+ * survey or a picker after the answer is rebound to `screen` under the same
390
+ * id, and a person has to act on it). Read off the poller state on
391
+ * `globalThis`, as {@link autoYesHoldStartedAt} reads the answer's time.
392
+ */
393
+ function autoYesAnsweredWait(input) {
394
+ const waitId = input.waitingEpisodeId ?? null;
395
+ if (waitId === null || answerTo(input)?.episodeId !== waitId)
396
+ return false;
397
+ const episode = (0, waiting_episode_state_1.getWaitingEpisodeRecord)(input.worktreeId, input.cliToolId, input.instanceId);
398
+ return episode !== null && episode.episodeId === waitId && episode.binding.kind !== 'screen';
399
+ }
400
+ /** The poller's `lastAnswer` for this instance, read off `globalThis` (Issue #3798). */
401
+ function answerTo(input) {
402
+ const key = (0, auto_yes_state_1.buildCompositeKey)(input.worktreeId, input.cliToolId, input.instanceId);
403
+ return globalThis.__autoYesPollerStates?.get(key)?.lastAnswer ?? null;
404
+ }
405
+ /**
406
+ * Whether a policy-suppression record is about this wait (Issue #3798). By the
407
+ * episode id when both the record and the caller carry one — a record written
408
+ * for an earlier episode is not this wait's reason however recent its `at` —
409
+ * and by #1684's `at >= waitingSince` otherwise.
410
+ */
411
+ function suppressionBelongsToWait(suppression, input) {
412
+ const waitId = input.waitingEpisodeId ?? null;
413
+ if (suppression.episodeId !== undefined && waitId !== null)
414
+ return suppression.episodeId === waitId;
415
+ return suppression.at >= input.waitingSince;
416
+ }
272
417
  /**
273
418
  * {@link decidePromptPush}, logged.
274
419
  *
@@ -303,6 +448,7 @@ function decideAndLogPromptPush(input, options = {}) {
303
448
  cliToolId: input.cliToolId,
304
449
  instanceId: input.instanceId ?? input.cliToolId,
305
450
  waitingSince: input.waitingSince,
451
+ ...(input.waitingEpisodeId ? { waitingEpisodeId: input.waitingEpisodeId } : {}),
306
452
  escalated: input.escalated === true,
307
453
  reason: decision.reason,
308
454
  ...(decision.stopReason !== undefined ? { stopReason: decision.stopReason } : {}),
@@ -160,7 +160,9 @@ function buildFailureBody(event, messages, excerpt, locale) {
160
160
  : messages[keys.plain];
161
161
  }
162
162
  /** Build the minimal notification payload for an event, in the reader's language. */
163
- function buildPushPayload(event, locale = i18n_config_1.DEFAULT_LOCALE, now = Date.now()) {
163
+ function buildPushPayload(event, locale = i18n_config_1.DEFAULT_LOCALE, now = Date.now(),
164
+ /** Issue #3798: the prompt push order taken for this fan-out, if any. */
165
+ order = null) {
164
166
  const excerpt = buildExcerpt(event.excerpt);
165
167
  const agentSuffix = event.agentName ? ` (${event.agentName})` : '';
166
168
  const title = `${event.worktreeName}${agentSuffix}`;
@@ -208,6 +210,12 @@ function buildPushPayload(event, locale = i18n_config_1.DEFAULT_LOCALE, now = Da
208
210
  timestamp: now,
209
211
  ...(event.waitingKind ? { waitingKind: event.waitingKind } : {}),
210
212
  ...(event.resolved === true ? { resolved: true } : {}),
213
+ ...(event.kind === 'prompt' && event.resolved !== true && event.waitingEpisodeId
214
+ ? { episodeId: event.waitingEpisodeId, ...(typeof order === 'number' ? { order } : {}) }
215
+ : {}),
216
+ ...(event.resolved === true && event.resolvedEpisodeId
217
+ ? { resolvedEpisodeId: event.resolvedEpisodeId, ...(typeof order === 'number' ? { order } : {}) }
218
+ : {}),
211
219
  };
212
220
  }
213
221
  /**
@@ -296,6 +304,7 @@ function passesDedup(event) {
296
304
  worktreeId: event.worktreeId,
297
305
  instanceId: event.instanceId ?? event.agentName ?? '',
298
306
  since: event.waitingSince,
307
+ episodeId: event.waitingEpisodeId,
299
308
  escalated: event.escalated,
300
309
  });
301
310
  }
@@ -334,8 +343,19 @@ async function notifyPushSubscribers(event, now = Date.now()) {
334
343
  // itself does not mark; it clears, and it does so in its own module so each
335
344
  // direction has one writer.
336
345
  if (event.kind === 'prompt' && event.resolved !== true) {
337
- (0, prompt_card_state_1.markPromptCardShown)(event.worktreeId, now);
346
+ // Issue #3798: with the episode's id, so the resolution can tell its own
347
+ // card from a newer one (`resolvedEpisodeId`).
348
+ (0, prompt_card_state_1.markPromptCardShown)(event.worktreeId, now, event.waitingEpisodeId ?? null);
338
349
  }
350
+ // Issue #3798: the order is taken here, synchronously with the decision to
351
+ // send — for a resolution, in the same tick `notifyPromptResolved` found
352
+ // nothing waiting — so a card taken after it compares greater on every
353
+ // device, whichever arrives first. Only pushes the Service Worker compares
354
+ // take one: an episode-named card, and a resolution naming its episode.
355
+ const order = event.kind === 'prompt' &&
356
+ (event.resolved === true ? Boolean(event.resolvedEpisodeId) : Boolean(event.waitingEpisodeId))
357
+ ? (0, prompt_card_state_1.nextPromptOrder)(event.worktreeId, now)
358
+ : null;
339
359
  web_push_1.default.setVapidDetails(config.subject, config.publicKey, config.privateKey);
340
360
  // Devices can be registered in different languages, so the body is built per
341
361
  // distinct locale rather than once for the whole fan-out.
@@ -349,7 +369,7 @@ async function notifyPushSubscribers(event, now = Date.now()) {
349
369
  byLocale.set(locale, [sub]);
350
370
  }
351
371
  const outcomes = (await Promise.all(Array.from(byLocale, ([locale, subs]) => {
352
- const payload = JSON.stringify(buildPushPayload(event, locale, now));
372
+ const payload = JSON.stringify(buildPushPayload(event, locale, now, order));
353
373
  return Promise.all(subs.map((sub) => sendToOne(sub, payload, now)));
354
374
  }))).flat();
355
375
  // Issue #2124: a fan-out that reached nobody used to be indistinguishable
@@ -56,6 +56,16 @@
56
56
  * exist, so a single-device install sends exactly as many pushes as it did
57
57
  * before this module was written — zero extra.
58
58
  *
59
+ * ## Which card it clears (Issue #3798)
60
+ *
61
+ * The resolution names the episode the card mark was last sent for
62
+ * (`resolvedEpisodeId`). A resolution can reach a device after the card of a
63
+ * newer wait in the same worktree — the next prompt's push overtook it — and
64
+ * without the name the Service Worker would close that newer, still-true card.
65
+ * With it, the worker closes only this episode's cards (and cards that name no
66
+ * episode) and re-shows the newer one silently. A mark without an id sends a
67
+ * resolution without one, which the worker handles exactly as before.
68
+ *
59
69
  * `completion` and `failure` cards are deliberately left alone; the reasoning
60
70
  * for each is in the design note under "which kinds are cleared".
61
71
  *
@@ -170,6 +180,9 @@ async function notifyPromptResolved(input) {
170
180
  reason: decision.reason,
171
181
  ...(decision.deviceCount !== undefined ? { deviceCount: decision.deviceCount } : {}),
172
182
  };
183
+ // Issue #3798: read before the mark is cleared below; `decidePromptResolution`
184
+ // has just adopted it into memory, so this is not a second query.
185
+ const resolvedEpisodeId = decision.send ? (0, prompt_card_state_1.getPromptCardEpisodeId)(worktreeId) : null;
173
186
  if (decision.reason !== 'still-waiting') {
174
187
  (0, prompt_card_state_1.clearPromptCard)(worktreeId);
175
188
  }
@@ -193,6 +206,7 @@ async function notifyPromptResolved(input) {
193
206
  kind: 'prompt',
194
207
  agentName,
195
208
  resolved: true,
209
+ ...(resolvedEpisodeId !== null ? { resolvedEpisodeId } : {}),
196
210
  }, at);
197
211
  logger.info('resolution-push-sent', context);
198
212
  }