commandmate 0.40.1 → 0.41.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 (199) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +134 -134
  3. package/.next/app-path-routes-manifest.json +21 -21
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/prerender-manifest.json +12 -12
  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_client-reference-manifest.js +1 -1
  12. package/.next/server/app/api/app/update-check/route.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.js +1 -1
  29. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  58. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/verification/runs/route_client-reference-manifest.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.js +1 -1
  69. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.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_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/route.js +1 -1
  147. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  149. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  151. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  152. package/.next/server/app/page_client-reference-manifest.js +1 -1
  153. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  155. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  156. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  157. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  158. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  159. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  160. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  161. package/.next/server/app/worktrees/[id]/page.js +1 -1
  162. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  163. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  164. package/.next/server/app-paths-manifest.json +21 -21
  165. package/.next/server/chunks/4443.js +1 -1
  166. package/.next/server/chunks/5164.js +1 -1
  167. package/.next/server/chunks/6130.js +1 -1
  168. package/.next/server/chunks/650.js +1 -1
  169. package/.next/server/chunks/8581.js +2 -2
  170. package/.next/server/chunks/8728.js +1 -1
  171. package/.next/server/chunks/9627.js +30 -24
  172. package/.next/server/chunks/9882.js +3 -3
  173. package/.next/server/middleware-manifest.json +5 -5
  174. package/.next/server/pages/500.html +1 -1
  175. package/.next/server/server-reference-manifest.json +1 -1
  176. package/.next/static/chunks/{2407-d750620d0511e94f.js → 2407-99799e3c2ff03bae.js} +1 -1
  177. package/.next/static/chunks/{4768-979c8a03bed218e7.js → 4768-fc0aa1d0891cac08.js} +1 -1
  178. package/.next/static/chunks/{8606-9a381b996f9b6834.js → 8606-79c36d4c204bbd38.js} +1 -1
  179. package/.next/static/chunks/app/{layout-3d3fcc8aac7b3bb5.js → layout-1fb647d6df55c498.js} +1 -1
  180. package/.next/static/chunks/app/worktrees/[id]/{page-a91e812ad9d94697.js → page-f24da37453224716.js} +1 -1
  181. package/.next/trace +13 -13
  182. package/dist/server/src/lib/auto-yes-poller.js +72 -1
  183. package/dist/server/src/lib/db/chat-db.js +38 -0
  184. package/dist/server/src/lib/detection/cli-patterns.js +11 -0
  185. package/dist/server/src/lib/detection/selection-shape.js +76 -0
  186. package/dist/server/src/lib/detection/tools/antigravity/detect.js +64 -0
  187. package/dist/server/src/lib/detection/tools/claude/detect.js +28 -1
  188. package/dist/server/src/lib/detection/tools/codex/cli-patterns.js +51 -0
  189. package/dist/server/src/lib/detection/tools/codex/detect.js +21 -2
  190. package/dist/server/src/lib/detection/tools/command-code/detect.js +14 -3
  191. package/dist/server/src/lib/polling/antigravity-permission-receipts.js +113 -0
  192. package/dist/server/src/lib/polling/response-checker.js +73 -4
  193. package/dist/server/src/lib/session/current-output-builder.js +25 -0
  194. package/dist/server/src/lib/session/layer-disagreement.js +108 -0
  195. package/dist/server/src/types/sidebar.js +1 -0
  196. package/package.json +1 -1
  197. package/release-notes/0.41.0.json +29 -0
  198. /package/.next/static/{JfzIPPhL-ikSVcJYOKgfD → UoGuvKnR_kGftU9RHtebq}/_buildManifest.js +0 -0
  199. /package/.next/static/{JfzIPPhL-ikSVcJYOKgfD → UoGuvKnR_kGftU9RHtebq}/_ssgManifest.js +0 -0
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ /**
3
+ * Short-lived record of the `PreToolUse` questions agy asked CommandMate
4
+ * (Issue #2849).
5
+ *
6
+ * ## Why this exists
7
+ *
8
+ * agy registers its permission hook on every tool call and waits for the reply
9
+ * before it either draws an approval dialog or runs the tool (#2848). A dialog
10
+ * that is really open on the pane therefore always follows a request that
11
+ * reached `POST /api/hooks/permission-request` a moment earlier. A dialog that
12
+ * agy only QUOTED in a reply — `Do you want to proceed?` and its four options
13
+ * in the model's prose — follows nothing. That asymmetry is the whole signal:
14
+ * "did agy ask us about a tool call just now" is a fact the screen cannot fake.
15
+ *
16
+ * ## Why no existing record can be used instead
17
+ *
18
+ * - `reportPermissionRequestPending` is not called for antigravity when
19
+ * `permissionHookPredictsDialog` is false, which is the ordinary case;
20
+ * - `pendingByInstance` is emptied by `closeDecisionSlot` the moment CommandMate
21
+ * answers, and the answer and the dialog belong to one causal chain — by the
22
+ * time the dialog is on the pane the slot is already gone.
23
+ *
24
+ * ## What is kept
25
+ *
26
+ * ONE entry per instance: the tool name and the arrival time of the most recent
27
+ * question. A second question replaces the first, because the only thing asked
28
+ * of this record is "was there one lately".
29
+ *
30
+ * The screen carries no tool name (only the option labels), so the caller that
31
+ * reads a frame asks {@link hasRecentAntigravityPermissionReceipt} without one
32
+ * and gets a time-only answer. The name is still stored and comparable, for a
33
+ * caller that does know it.
34
+ *
35
+ * In-memory and per-process, like the Auto-Yes state it is read beside: a server
36
+ * restart empties it, and the cost is that a dialog opened within
37
+ * {@link ANTIGRAVITY_PERMISSION_RECEIPT_WINDOW_MS} of the restart is left to a
38
+ * human — never that a quotation is answered.
39
+ */
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.ANTIGRAVITY_PERMISSION_RECEIPT_WINDOW_MS = void 0;
42
+ exports.recordAntigravityPermissionReceipt = recordAntigravityPermissionReceipt;
43
+ exports.hasRecentAntigravityPermissionReceipt = hasRecentAntigravityPermissionReceipt;
44
+ exports.resetAntigravityPermissionReceiptsForTests = resetAntigravityPermissionReceiptsForTests;
45
+ const auto_yes_state_1 = require("../../lib/auto-yes-state");
46
+ /**
47
+ * How long after a `PreToolUse` question a dialog still counts as its dialog.
48
+ *
49
+ * The hook's own timeout is 5 seconds, and the frame the poller reads can lag the
50
+ * pane by a capture interval on top, so 8 seconds is the hook timeout plus the
51
+ * margin for that lag. A `PreToolUse` that timed out never produced a dialog at
52
+ * all (agy proceeds without one), so the window does not need to be longer to
53
+ * cover it.
54
+ */
55
+ exports.ANTIGRAVITY_PERMISSION_RECEIPT_WINDOW_MS = 8_000;
56
+ const receipts = globalThis.__antigravityPermissionReceipts ??
57
+ (globalThis.__antigravityPermissionReceipts = new Map());
58
+ /**
59
+ * How long an entry is kept before it is dropped on the next write.
60
+ *
61
+ * Far beyond the window on purpose: an entry older than the window is already
62
+ * useless to the reader, so this only bounds the Map for sessions nobody asks
63
+ * about again (a removed worktree, a killed instance).
64
+ */
65
+ const RECEIPT_IDLE_TTL_MS = 60 * 60 * 1000;
66
+ function pruneExpiredReceipts(now) {
67
+ for (const [key, receipt] of receipts) {
68
+ if (now - receipt.at > RECEIPT_IDLE_TTL_MS)
69
+ receipts.delete(key);
70
+ }
71
+ }
72
+ /**
73
+ * Record that agy asked CommandMate about a tool call.
74
+ *
75
+ * A null `toolName` is not recorded: the payload could not be read, so there is
76
+ * nothing to say the request was a `PreToolUse` at all.
77
+ *
78
+ * @param worktreeId - Worktree the request resolved to
79
+ * @param cliToolId - CLI tool the request came from
80
+ * @param instanceId - Agent instance id (the tool id itself for the primary)
81
+ * @param toolName - Tool the request named, or null when the payload had none
82
+ * @param at - Epoch ms the request arrived
83
+ */
84
+ function recordAntigravityPermissionReceipt(worktreeId, cliToolId, instanceId, toolName, at = Date.now()) {
85
+ if (toolName === null)
86
+ return;
87
+ pruneExpiredReceipts(at);
88
+ receipts.set((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId), { toolName, at });
89
+ }
90
+ /**
91
+ * Did agy ask CommandMate about a tool call, for this instance, within
92
+ * {@link ANTIGRAVITY_PERMISSION_RECEIPT_WINDOW_MS}?
93
+ *
94
+ * @param worktreeId - Worktree the frame belongs to
95
+ * @param cliToolId - CLI tool the frame belongs to
96
+ * @param instanceId - Agent instance id; the tool id itself or `undefined` for
97
+ * the primary (the spelling the Auto-Yes poller carries)
98
+ * @param toolName - When given, the question must have named this tool. Omit it
99
+ * to ask only "was there one lately" — a frame carries no tool name.
100
+ * @param now - Epoch ms to measure against
101
+ */
102
+ function hasRecentAntigravityPermissionReceipt(worktreeId, cliToolId, instanceId, toolName, now = Date.now()) {
103
+ const receipt = receipts.get((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId));
104
+ if (!receipt)
105
+ return false;
106
+ if (toolName !== undefined && receipt.toolName !== toolName)
107
+ return false;
108
+ return now - receipt.at <= exports.ANTIGRAVITY_PERMISSION_RECEIPT_WINDOW_MS;
109
+ }
110
+ /** Empty the record. Tests only: production entries age out on their own. */
111
+ function resetAntigravityPermissionReceiptsForTests() {
112
+ receipts.clear();
113
+ }
@@ -18,6 +18,8 @@ const db_1 = require("../../lib/db");
18
18
  const ws_server_1 = require("../../lib/ws-server");
19
19
  const prompt_detector_1 = require("../../lib/detection/prompt-detector");
20
20
  const dialog_1 = require("../../lib/detection/tools/antigravity/dialog");
21
+ const detect_1 = require("../../lib/detection/tools/antigravity/detect");
22
+ const frame_1 = require("../../lib/detection/tools/frame");
21
23
  const dialog_2 = require("../../lib/detection/tools/command-code/dialog");
22
24
  const conversation_logger_1 = require("../../lib/conversation-logger");
23
25
  const types_1 = require("../../lib/cli-tools/types");
@@ -33,6 +35,7 @@ const response_cleaner_1 = require("../response-cleaner");
33
35
  const copilot_constants_1 = require("../../config/copilot-constants");
34
36
  const tui_accumulator_1 = require("../tui-accumulator");
35
37
  const prompt_dedup_1 = require("./prompt-dedup");
38
+ const antigravity_permission_receipts_1 = require("./antigravity-permission-receipts");
36
39
  // Issue #2457: the same rollout table, `hasDialogRules` cross-check and
37
40
  // `detectDialog` seam Auto-Yes reads, reached through the presence helper rather
38
41
  // than through `evaluateAutoYesDialogGate` — see `isNumberedDialogVouched`.
@@ -226,9 +229,14 @@ function buildPromptExtractionResult(lines, lastCapturedLine, totalLines, buffer
226
229
  * (Issue #499 Item 4); must be the split of THIS string, not of the raw capture
227
230
  * @param rawFrame - the SAME tick's capture with its box drawing intact (ANSI
228
231
  * optional), for the tool readers anchored on it (Issue #2522)
232
+ * @param receiptScope - whose frame this is, when the caller answers it
233
+ * automatically (Issue #2849). For agy only: a frame that reads as a prompt is
234
+ * then handed back as no prompt unless agy asked CommandMate about a tool call
235
+ * for that instance a moment ago. Omitted, the reading is not gated — which is
236
+ * what every caller that only DISPLAYS the frame wants.
229
237
  * @returns PromptDetectionResult with isPrompt, promptData, and cleanContent
230
238
  */
231
- function detectPromptOnCleanFrame(cleanOutput, cliToolId, precomputedLines, rawFrame) {
239
+ function detectPromptOnCleanFrame(cleanOutput, cliToolId, precomputedLines, rawFrame, receiptScope) {
232
240
  // Issue #2364: agy's `↑/↓ Navigate` dialogs are read by agy's own reader
233
241
  // before the generic pass, on the same spelling `tools/antigravity/detect.ts`
234
242
  // reads them on. The generic multiple-choice parser takes one row per option
@@ -246,8 +254,11 @@ function detectPromptOnCleanFrame(cleanOutput, cliToolId, precomputedLines, rawF
246
254
  // API published the very same screen as an answerable four-option prompt.
247
255
  if (cliToolId === 'antigravity') {
248
256
  const dialog = (0, dialog_1.detectAntigravityNumberedDialogPrompt)(cleanOutput);
249
- if (dialog !== null)
250
- return dialog;
257
+ if (dialog !== null) {
258
+ return isWithheldForWantOfReceipt(receiptScope)
259
+ ? { isPrompt: false, cleanContent: cleanOutput.trim() }
260
+ : dialog;
261
+ }
251
262
  }
252
263
  // Issue #2522: Command Code's footer-less `AskUserQuestion`, read by the same
253
264
  // module `tools/command-code/detect.ts` publishes its status from — so the
@@ -275,7 +286,65 @@ function detectPromptOnCleanFrame(cleanOutput, cliToolId, precomputedLines, rawF
275
286
  }
276
287
  }
277
288
  const promptOptions = (0, cli_patterns_1.buildDetectPromptOptions)(cliToolId);
278
- return (0, prompt_detector_1.detectPrompt)(cleanOutput, precomputedLines ? { ...promptOptions, precomputedLines } : promptOptions);
289
+ const result = (0, prompt_detector_1.detectPrompt)(cleanOutput, precomputedLines ? { ...promptOptions, precomputedLines } : promptOptions);
290
+ // Issue #2851: agy's reader above declines a dialog quoted in a reply (#2845),
291
+ // and this generic pass then read the quotation itself — `Do you want to
292
+ // proceed?` and its four options, above a live `>` composer — as an answerable
293
+ // `multiple_choice`. agy's Auto-Yes gate row is `legacy`, so nothing after this
294
+ // point judges the frame again and the poller answered a dialog nobody had
295
+ // opened. The status side declines the same candidate with the same predicate
296
+ // (`isStalePrompt`, #2845): agy draws its numbered screens in place of the
297
+ // composer or below it, never above it, so a composer under the list's last
298
+ // row makes the list a quotation.
299
+ //
300
+ // `rawFrame` when the caller has it, and `cleanOutput` otherwise. The status
301
+ // side normalises the capture as captured and the predicate then strips box
302
+ // drawing exactly once; handing it `cleanOutput` — already stripped once, and
303
+ // `stripBoxDrawing` is not idempotent — would strip a second time and could
304
+ // read different rows.
305
+ if (cliToolId === 'antigravity' &&
306
+ result.isPrompt &&
307
+ (0, detect_1.isAntigravityQuotedNumberedList)((0, frame_1.normalizeFrame)(rawFrame ?? cleanOutput), result)) {
308
+ return { isPrompt: false, cleanContent: cleanOutput.trim() };
309
+ }
310
+ // Issue #2849: the same withholding on this exit. agy's reader answering `null`
311
+ // does not mean the frame is no dialog — the generic pass above reads shapes
312
+ // agy's reader declines, and #2851 showed it can read a quotation — so a gate
313
+ // that covered only the reader's exit would leave this one open.
314
+ if (cliToolId === 'antigravity' && result.isPrompt && isWithheldForWantOfReceipt(receiptScope)) {
315
+ return { isPrompt: false, cleanContent: cleanOutput.trim() };
316
+ }
317
+ return result;
318
+ }
319
+ /**
320
+ * Should an agy frame that reads as a prompt be treated as no prompt because
321
+ * agy asked CommandMate about no tool call lately? (Issue #2849)
322
+ *
323
+ * agy asks (`PreToolUse`) before it draws ANY approval dialog and waits for the
324
+ * reply, so a dialog that is really open follows a question a moment before it,
325
+ * and one that is only quoted in a reply follows nothing. The frame carries no
326
+ * tool name, so the check is time-only.
327
+ *
328
+ * Not gated (false) when there is no scope: a caller that only shows the frame —
329
+ * the status API, the response poller's stored `prompt` row, the notification —
330
+ * must keep showing a real dialog on a machine whose hook is not installed or
331
+ * did not reach us. Only the caller that ANSWERS the frame opts in, because for
332
+ * it a missed answer costs a human a keystroke and a wrong one is sent as an
333
+ * utterance nobody asked for.
334
+ *
335
+ * `debug`, not `info`: the poller re-reads a static pane every 2s, so a
336
+ * quotation left on screen would print this on every tick.
337
+ */
338
+ function isWithheldForWantOfReceipt(scope) {
339
+ if (scope === undefined)
340
+ return false;
341
+ if ((0, antigravity_permission_receipts_1.hasRecentAntigravityPermissionReceipt)(scope.worktreeId, 'antigravity', scope.instanceId))
342
+ return false;
343
+ logger.debug('antigravity-dialog-no-recent-receipt', {
344
+ worktreeId: scope.worktreeId,
345
+ instanceId: scope.instanceId ?? 'antigravity',
346
+ });
347
+ return true;
279
348
  }
280
349
  /**
281
350
  * Is the generic parser's numbered-list candidate a dialog the tool vouches for?
@@ -85,6 +85,7 @@ const prompt_waiting_composition_1 = require("../../lib/session/prompt-waiting-c
85
85
  const provisional_turn_1 = require("../../lib/session/provisional-turn");
86
86
  const status_evidence_1 = require("../../lib/session/status-evidence");
87
87
  const ask_user_question_prompt_1 = require("../../lib/session/ask-user-question-prompt");
88
+ const layer_disagreement_1 = require("../../lib/session/layer-disagreement");
88
89
  const structured_prompt_1 = require("../../lib/session/structured-prompt");
89
90
  const logger = (0, logger_1.createLogger)('current-output-builder');
90
91
  /**
@@ -1093,6 +1094,30 @@ async function buildPayload(db, worktreeId, cliToolId, instanceId) {
1093
1094
  obstruction: paneObstruction,
1094
1095
  });
1095
1096
  }
1097
+ // Issue #2843: the opposite gap — the scraper reads a dialog on a turn the
1098
+ // agent's own `Stop` closed. Logged only (see `layer-disagreement.ts`); fed the
1099
+ // SCRAPER's verdict, because the merge lets a scraper `waiting` win and would
1100
+ // hide the very disagreement this records.
1101
+ const disagreement = (0, layer_disagreement_1.classifyLayerDisagreement)({
1102
+ supportedEvents: eventSource.capabilities.supportedEvents,
1103
+ scraperStatus: statusResult.status,
1104
+ scraperReason: statusResult.reason,
1105
+ hasActivePrompt: scraperPromptWaiting,
1106
+ lastEventType: structuredEvents.lastEventType,
1107
+ lastEventDetail: structuredEvents.lastEventDetail,
1108
+ });
1109
+ if (disagreement !== null) {
1110
+ (0, layer_disagreement_1.reportLayerDisagreement)({
1111
+ compositeKey,
1112
+ worktreeId,
1113
+ cliToolId,
1114
+ instanceId: resolvedInstanceId,
1115
+ turnId: structuredEvents.turnId,
1116
+ kind: disagreement,
1117
+ scraperReason: statusResult.reason,
1118
+ frame: output,
1119
+ });
1120
+ }
1096
1121
  // Issue #1725: the structured layer saw a dialog the scraper did not. That
1097
1122
  // gap is the fact worth keeping — see recordStructuredPrompt.
1098
1123
  if (promptWaiting !== null && structuredFacts !== null && !scraperPromptWaiting && !promptWaiting.recorded) {
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * The screen says "a dialog is open", the agent's own hooks say "the turn is
4
+ * over" — record it (Issue #2843).
5
+ *
6
+ * A tool's dialogs are read off the pane (`lib/detection/tools/*`), and the pane
7
+ * is free text: every few weeks a new wording, a new screen or a quoted
8
+ * paragraph was misread as a dialog, and each time the report came from a user
9
+ * who saw a card that should not have been there. The hook layer is an
10
+ * independent witness that already runs on every poll: the agent fires `Stop`
11
+ * when a turn ends, and a dialog that belongs to a turn (an approval, a
12
+ * question) only ever appears inside one. So a screen `waiting` whose newest
13
+ * hook event is `stop` is either a dialog the user opened themselves (`/model`,
14
+ * `/experimental` …) or a misreading.
15
+ *
16
+ * This module does NOT change any verdict. `mergeStructuredStatus` deliberately
17
+ * lets the screen's `waiting` win (Claude emits nothing while its picker is up,
18
+ * #1708), and overturning that is a separate decision that needs the numbers
19
+ * this module produces. What it does is leave one log line per turn, with the
20
+ * frame's tail, so the next misreading arrives as a fixture candidate instead
21
+ * of as a bug report.
22
+ *
23
+ * Which tools qualify is decided by what their hooks deliver, not by name: the
24
+ * rule needs BOTH ends of a turn. A tool whose hooks never announce the start of
25
+ * a turn (`user_prompt_submit` — antigravity, Command Code) keeps the previous
26
+ * turn's `stop` as its newest event while the next turn runs, so a genuine
27
+ * dialog in the middle of that turn would be logged as a disagreement.
28
+ * Likewise a `stop` whose detail is `self_resume_pending` says "the turn is
29
+ * closed but the agent wakes itself", not "the agent is done".
30
+ *
31
+ * The two kinds are logged separately because they mean different things:
32
+ *
33
+ * - `approval-after-stop` — the screen reads an ANSWERABLE prompt
34
+ * (`hasActivePrompt`). The agent asks for permission before it draws a
35
+ * dialog, inside a turn, so after `Stop` every line of this kind is a
36
+ * detector defect to fixture.
37
+ * - `menu-after-stop` — the screen reads a selection list / pager. A user who
38
+ * opened `/model` after the turn produces this legitimately, so these lines
39
+ * are triaged, not fixed on sight.
40
+ */
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.DISAGREEMENT_FRAME_TAIL_ROWS = void 0;
43
+ exports.classifyLayerDisagreement = classifyLayerDisagreement;
44
+ exports.reportLayerDisagreement = reportLayerDisagreement;
45
+ exports.resetLayerDisagreementForTests = resetLayerDisagreementForTests;
46
+ const status_detector_1 = require("../../lib/detection/status-detector");
47
+ const cli_patterns_1 = require("../../lib/detection/cli-patterns");
48
+ const agent_event_types_1 = require("../../lib/hooks/agent-event-types");
49
+ const logger_1 = require("../../lib/logger");
50
+ const logger = (0, logger_1.createLogger)('layer-disagreement');
51
+ /**
52
+ * Classify one poll. Pure.
53
+ *
54
+ * Only tools whose hooks deliver both `user_prompt_submit` and `stop` qualify
55
+ * (claude, codex, copilot, gemini, opencode): without the turn's start, "the
56
+ * newest event is `stop`" cannot tell a closed turn from a running one.
57
+ */
58
+ function classifyLayerDisagreement(input) {
59
+ if (!input.supportedEvents.includes('user_prompt_submit') ||
60
+ !input.supportedEvents.includes('stop')) {
61
+ return null;
62
+ }
63
+ if (input.scraperStatus !== 'waiting')
64
+ return null;
65
+ if (input.lastEventType !== 'stop')
66
+ return null;
67
+ if (input.lastEventDetail === agent_event_types_1.SELF_RESUME_PENDING_DETAIL)
68
+ return null;
69
+ if (input.hasActivePrompt)
70
+ return 'approval-after-stop';
71
+ if (status_detector_1.SELECTION_LIST_REASONS.has(input.scraperReason))
72
+ return 'menu-after-stop';
73
+ return null;
74
+ }
75
+ /** How many rows of the frame's tail go into the log line. */
76
+ exports.DISAGREEMENT_FRAME_TAIL_ROWS = 40;
77
+ const reported = globalThis.__layerDisagreementReported ??
78
+ (globalThis.__layerDisagreementReported = new Set());
79
+ /** Bound on {@link reported}; at the cap the set is cleared (worst case: one repeat line). */
80
+ const REPORTED_CAP = 512;
81
+ /**
82
+ * Log one disagreement, once per (session, turn, kind).
83
+ *
84
+ * @returns true when a line was written (for tests)
85
+ */
86
+ function reportLayerDisagreement(report) {
87
+ const key = `${report.compositeKey}|${report.turnId ?? '-'}|${report.kind}`;
88
+ if (reported.has(key))
89
+ return false;
90
+ if (reported.size >= REPORTED_CAP)
91
+ reported.clear();
92
+ reported.add(key);
93
+ const rows = (0, cli_patterns_1.stripAnsi)(report.frame).split('\n').filter((row) => row.trim() !== '');
94
+ logger.warn('layerDisagreement', {
95
+ kind: report.kind,
96
+ worktreeId: report.worktreeId,
97
+ cliToolId: report.cliToolId,
98
+ instanceId: report.instanceId,
99
+ turnId: report.turnId,
100
+ scraperReason: report.scraperReason,
101
+ frameTail: rows.slice(-exports.DISAGREEMENT_FRAME_TAIL_ROWS).join('\n'),
102
+ });
103
+ return true;
104
+ }
105
+ /** Test-only: forget what has been reported. */
106
+ function resetLayerDisagreementForTests() {
107
+ reported.clear();
108
+ }
@@ -330,6 +330,7 @@ function toBranchItem(worktree) {
330
330
  status,
331
331
  hasUnread,
332
332
  lastActivity: worktree.updatedAt,
333
+ lastActivityByInstance: worktree.lastActivityByInstance,
333
334
  description: worktree.description,
334
335
  cliStatus,
335
336
  cliStatusLabels,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commandmate",
3
- "version": "0.40.1",
3
+ "version": "0.41.0",
4
4
  "description": "A local control plane for agent CLIs — orchestration and visibility for Claude Code, Codex, Gemini CLI, and more across Git worktrees",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -0,0 +1,29 @@
1
+ {
2
+ "version": "0.41.0",
3
+ "date": "2026-09-25",
4
+ "highlight": {
5
+ "ja": "エージェントが返答の中でダイアログの文面を引用しただけなのに、ダイアログが開いていると表示される問題を直しました。",
6
+ "en": "Fixed a dialog being shown as open when the agent had only quoted a dialog's text in its reply."
7
+ },
8
+ "added": [],
9
+ "improved": [
10
+ {
11
+ "ja": "Antigravity の Auto-Yes は、本当に確認を求められたときだけ承認に答えるようになりました。",
12
+ "en": "Antigravity's Auto-Yes now answers an approval only when the agent actually asked for one."
13
+ }
14
+ ],
15
+ "fixed": [
16
+ {
17
+ "ja": "Codex・Antigravity・Command Code・Claude で、入力待ちなのに選択リストやダイアログが開いていると表示される問題を直しました。",
18
+ "en": "Fixed Codex, Antigravity, Command Code and Claude showing an open selection list or dialog while waiting for input."
19
+ },
20
+ {
21
+ "ja": "Antigravity の Auto-Yes が、返答に引用されたダイアログの文面に自動で答えてしまう問題を直しました。",
22
+ "en": "Fixed Antigravity's Auto-Yes answering dialog text that was only quoted in a reply."
23
+ },
24
+ {
25
+ "ja": "サイドバーのセッション表示で、選んだ並び順どおりに並ぶようになりました。",
26
+ "en": "The session view in the sidebar now follows the sort order you choose."
27
+ }
28
+ ]
29
+ }