commandmate 0.31.1 → 0.31.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +149 -149
- package/.next/app-path-routes-manifest.json +27 -27
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +12 -12
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +9 -9
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +27 -27
- package/.next/server/chunks/1191.js +1 -1
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/552.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6894.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{4718-46e1d2f07c8423ba.js → 4718-0d4d93c4c75f336e.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-7a0915e32a2af4d3.js +1 -0
- package/.next/trace +13 -13
- package/dist/server/src/lib/polling/response-poller-core.js +143 -9
- package/dist/server/src/lib/push/notification-dedup.js +2 -1
- package/package.json +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-64d4ec3051971760.js +0 -1
- /package/.next/static/{60mm4_QBuFtFTfo5UMyKU → GiOqe8rfQGQ0NvzB5qr3q}/_buildManifest.js +0 -0
- /package/.next/static/{60mm4_QBuFtFTfo5UMyKU → GiOqe8rfQGQ0NvzB5qr3q}/_ssgManifest.js +0 -0
|
@@ -61,7 +61,14 @@ const coordinator = globalThis.__responsePollerCoordinator ??
|
|
|
61
61
|
pendingRestart: new Map(),
|
|
62
62
|
ownerStorage: new async_hooks_1.AsyncLocalStorage(),
|
|
63
63
|
generationCounter: 0,
|
|
64
|
+
pausedOnPrompt: new Set(),
|
|
64
65
|
});
|
|
66
|
+
// The coordinator object outlives a module reload (that is the point of putting
|
|
67
|
+
// it on `globalThis`), so a field added after the object was first built has to
|
|
68
|
+
// be filled in on an object an older evaluation created — `next dev` keeps
|
|
69
|
+
// `globalThis` across HMR, and a poller started before the reload would
|
|
70
|
+
// otherwise crash the first tick after it.
|
|
71
|
+
coordinator.pausedOnPrompt ??= new Set();
|
|
65
72
|
/**
|
|
66
73
|
* Active pollers map: "worktreeId:instanceId" -> NodeJS.Timeout
|
|
67
74
|
*
|
|
@@ -107,10 +114,23 @@ function takeOwnership(pollerKey) {
|
|
|
107
114
|
generation: ++coordinator.generationCounter,
|
|
108
115
|
superseded: false,
|
|
109
116
|
stopped: false,
|
|
117
|
+
selfStopped: false,
|
|
110
118
|
};
|
|
111
119
|
coordinator.owners.set(pollerKey, owner);
|
|
112
120
|
return owner;
|
|
113
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* The tools whose chain ends the moment a reply is saved (opencode, copilot).
|
|
124
|
+
*
|
|
125
|
+
* `checkForResponse()` stops the poller on exactly two "something was recorded"
|
|
126
|
+
* paths, and they are split by this predicate: a full-screen TUI's chain stops
|
|
127
|
+
* AFTER ITS REPLY, every other tool's chain stops ON A PROMPT and expects
|
|
128
|
+
* `/respond` to start it again. The same two tools are the ones that need the
|
|
129
|
+
* Layer-2 accumulator, so this is the single place the pair is spelled out.
|
|
130
|
+
*/
|
|
131
|
+
function isFullScreenTui(cliToolId) {
|
|
132
|
+
return cliToolId === 'opencode' || cliToolId === 'copilot';
|
|
133
|
+
}
|
|
114
134
|
// ============================================================================
|
|
115
135
|
// Polling lifecycle (public API)
|
|
116
136
|
// ============================================================================
|
|
@@ -122,6 +142,17 @@ function takeOwnership(pollerKey) {
|
|
|
122
142
|
* `checkForResponse()` does **not** run the two side by side — the restart is
|
|
123
143
|
* handed to that tick's epilogue and applied when it settles.
|
|
124
144
|
*
|
|
145
|
+
* Issue #2230: two different callers reach this function, and they mean two
|
|
146
|
+
* different things. `sendUserMessage` starts a NEW TURN — the previous chain is
|
|
147
|
+
* still ticking over the finished screen, and the restart drops its response
|
|
148
|
+
* hash so an identical reply in the new turn is still recorded (#1268).
|
|
149
|
+
* `/respond`, `/prompt-response` and the Auto-Yes poller RESUME the turn the
|
|
150
|
+
* chain paused on when it recorded a prompt — the chain has already stopped
|
|
151
|
+
* itself, the screen may be exactly the finished turn the prompt was drawn
|
|
152
|
+
* over, and the hash is kept so that screen is not saved a second time. The
|
|
153
|
+
* two are told apart by the state the previous chain left behind
|
|
154
|
+
* ({@link PollerCoordinator.pausedOnPrompt}), not by who is calling.
|
|
155
|
+
*
|
|
125
156
|
* @param worktreeId - Worktree ID
|
|
126
157
|
* @param cliToolId - CLI tool ID (claude, codex, gemini)
|
|
127
158
|
* @param instanceId - Optional agent instance ID (defaults to primary)
|
|
@@ -151,8 +182,22 @@ function startPolling(worktreeId, cliToolId, instanceId) {
|
|
|
151
182
|
});
|
|
152
183
|
coordinator.running.delete(pollerKey);
|
|
153
184
|
}
|
|
154
|
-
|
|
155
|
-
|
|
185
|
+
restartChain(pollerKey, target);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Retire whatever chain holds `pollerKey` and start a fresh one for `target`.
|
|
189
|
+
*
|
|
190
|
+
* Shared by the direct path of {@link startPolling} and the queued restart a
|
|
191
|
+
* tick applies in its epilogue, so both make the same decision about the
|
|
192
|
+
* response hash (Issue #2230): kept when the previous chain paused itself on a
|
|
193
|
+
* prompt and this is the resume, dropped otherwise.
|
|
194
|
+
*/
|
|
195
|
+
function restartChain(pollerKey, target) {
|
|
196
|
+
const resume = coordinator.pausedOnPrompt.has(pollerKey);
|
|
197
|
+
stopPollingByKey(pollerKey, { resume });
|
|
198
|
+
if (resume) {
|
|
199
|
+
logger.info('poller:resumed-after-prompt', { pollerKey });
|
|
200
|
+
}
|
|
156
201
|
beginPolling(target);
|
|
157
202
|
}
|
|
158
203
|
/** Seed a fresh polling cycle (start time, TUI accumulator) and schedule its first tick. */
|
|
@@ -161,7 +206,7 @@ function beginPolling(target) {
|
|
|
161
206
|
// Record start time
|
|
162
207
|
coordinator.pollingStartTimes.set(pollerKey, Date.now());
|
|
163
208
|
// Initialize TUI accumulator for full-screen TUI tools (Layer 2 safety net)
|
|
164
|
-
if (target.cliToolId
|
|
209
|
+
if (isFullScreenTui(target.cliToolId)) {
|
|
165
210
|
(0, tui_accumulator_1.initTuiAccumulator)(pollerKey);
|
|
166
211
|
}
|
|
167
212
|
// Start polling with setTimeout chain to prevent race conditions
|
|
@@ -212,12 +257,18 @@ async function runPollTick(target, owner) {
|
|
|
212
257
|
return;
|
|
213
258
|
}
|
|
214
259
|
// Check for response
|
|
260
|
+
let recorded = false;
|
|
215
261
|
try {
|
|
216
|
-
await coordinator.ownerStorage.run(owner, () => (0, response_checker_1.checkForResponse)(target.worktreeId, target.cliToolId, target.instanceId));
|
|
262
|
+
recorded = await coordinator.ownerStorage.run(owner, () => (0, response_checker_1.checkForResponse)(target.worktreeId, target.cliToolId, target.instanceId));
|
|
217
263
|
}
|
|
218
264
|
catch (error) {
|
|
219
265
|
logger.error('error:', { error: error instanceof Error ? error.message : String(error) });
|
|
220
266
|
}
|
|
267
|
+
// Issue #2230: the chain stopped itself inside that call. Now that its
|
|
268
|
+
// verdict is known, decide whether the stop ended the turn or paused it.
|
|
269
|
+
if (owner.selfStopped) {
|
|
270
|
+
settleSelfStop(owner, target, recorded);
|
|
271
|
+
}
|
|
221
272
|
// Issue #1120: push the current terminal snapshot to WS subscribers so the
|
|
222
273
|
// output streams during generation. No-op (and no tmux capture) when nobody
|
|
223
274
|
// is subscribed to the worktree room. Best-effort; polling is the fallback.
|
|
@@ -244,16 +295,63 @@ async function runPollTick(target, owner) {
|
|
|
244
295
|
const pending = coordinator.pendingRestart.get(pollerKey);
|
|
245
296
|
if (pending) {
|
|
246
297
|
coordinator.pendingRestart.delete(pollerKey);
|
|
247
|
-
|
|
248
|
-
beginPolling(pending);
|
|
298
|
+
restartChain(pollerKey, pending);
|
|
249
299
|
}
|
|
250
300
|
}
|
|
251
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Finish a stop that `checkForResponse()` raised from inside its own tick
|
|
304
|
+
* (Issue #2230).
|
|
305
|
+
*
|
|
306
|
+
* `checkForResponse()` ends its chain on four paths, and `stopPollingByKey()`
|
|
307
|
+
* cannot tell them apart while the call is still on the stack — so it leaves
|
|
308
|
+
* the response hash alone and this runs once the verdict is in:
|
|
309
|
+
*
|
|
310
|
+
* - **recorded, not a full-screen TUI** — the prompt path. The turn is NOT
|
|
311
|
+
* over: the agent is waiting for an answer, and the screen under the dialog
|
|
312
|
+
* is very often the previous reply, already saved. The hash stays and the
|
|
313
|
+
* key is marked paused, so the `/respond` (or Auto-Yes) restart that follows
|
|
314
|
+
* resumes the cycle instead of opening one over the same screen. Measured
|
|
315
|
+
* live on 2026-09-01: claude drew `Teach auto mode about your environment?`
|
|
316
|
+
* AFTER a finished turn, and answering it re-saved the reply.
|
|
317
|
+
* - **recorded, full-screen TUI** — the reply itself was just saved and the
|
|
318
|
+
* chain is done. The cycle ends here exactly as before.
|
|
319
|
+
* - **nothing recorded** — worktree gone or session not running. There is no
|
|
320
|
+
* turn left to protect, and a hash kept here would suppress the first reply
|
|
321
|
+
* of the session that replaces it.
|
|
322
|
+
*
|
|
323
|
+
* The prompt hash is NOT kept in any of these. If the answer did not take and
|
|
324
|
+
* the same dialog is still up on the first resumed tick, the operator has to
|
|
325
|
+
* get a new card rather than silence.
|
|
326
|
+
*/
|
|
327
|
+
function settleSelfStop(owner, target, recorded) {
|
|
328
|
+
if (recorded && !isFullScreenTui(target.cliToolId)) {
|
|
329
|
+
coordinator.pausedOnPrompt.add(owner.pollerKey);
|
|
330
|
+
logger.info('poller:paused-on-prompt', {
|
|
331
|
+
pollerKey: owner.pollerKey,
|
|
332
|
+
generation: owner.generation,
|
|
333
|
+
});
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
(0, response_dedup_1.clearResponseHashCache)(owner.pollerKey);
|
|
337
|
+
}
|
|
252
338
|
/**
|
|
253
339
|
* Stop the poller identified by an already-computed poller key.
|
|
254
340
|
* Shared by stopPolling() and stopAllPolling() so cleanup logic stays in one place.
|
|
341
|
+
*
|
|
342
|
+
* Issue #2230: this is where the per-turn dedup caches end, and the response
|
|
343
|
+
* hash is the one whose ending has to be timed. It is dropped here for an
|
|
344
|
+
* explicit stop (a route, `session-cleanup`, `kill-session`, the 30-minute
|
|
345
|
+
* budget) and for the restart that opens a new turn. It is **kept** in two
|
|
346
|
+
* cases: `resume` — the restart is picking up a chain that paused itself on a
|
|
347
|
+
* prompt — and a stop raised by the chain's own tick, whose meaning is settled
|
|
348
|
+
* by {@link settleSelfStop} once `checkForResponse()` has returned. The prompt
|
|
349
|
+
* hash and the TUI accumulator are dropped unconditionally, as before.
|
|
350
|
+
*
|
|
351
|
+
* @param pollerKey - Poller key ("worktreeId:instanceId")
|
|
352
|
+
* @param options.resume - This stop precedes a restart of a paused chain.
|
|
255
353
|
*/
|
|
256
|
-
function stopPollingByKey(pollerKey) {
|
|
354
|
+
function stopPollingByKey(pollerKey, options = {}) {
|
|
257
355
|
// Issue #2223: a tick that has been superseded still runs to completion, and
|
|
258
356
|
// `checkForResponse()` ends the turn with `stopPolling()` on four paths
|
|
259
357
|
// (worktree gone, session not running, prompt detected, TUI reply saved).
|
|
@@ -268,13 +366,20 @@ function stopPollingByKey(pollerKey) {
|
|
|
268
366
|
return;
|
|
269
367
|
}
|
|
270
368
|
const owner = coordinator.owners.get(pollerKey);
|
|
369
|
+
// The live owner stopping itself from inside its own tick. Identity, not key
|
|
370
|
+
// equality: a stale generation with the same key was turned away above.
|
|
371
|
+
const selfStop = owner !== undefined && caller === owner;
|
|
271
372
|
if (owner) {
|
|
272
373
|
owner.stopped = true;
|
|
374
|
+
owner.selfStopped = selfStop;
|
|
273
375
|
coordinator.owners.delete(pollerKey);
|
|
274
376
|
}
|
|
275
377
|
// A restart queued behind an in-flight tick is cancelled by an explicit stop:
|
|
276
378
|
// "start then stop" must leave the session stopped.
|
|
277
379
|
coordinator.pendingRestart.delete(pollerKey);
|
|
380
|
+
// Whatever this stop is, the pause it may be ending is over: a resume consumes
|
|
381
|
+
// it, an explicit stop cancels it.
|
|
382
|
+
coordinator.pausedOnPrompt.delete(pollerKey);
|
|
278
383
|
const timerId = coordinator.activePollers.get(pollerKey);
|
|
279
384
|
if (timerId) {
|
|
280
385
|
clearTimeout(timerId);
|
|
@@ -287,7 +392,12 @@ function stopPollingByKey(pollerKey) {
|
|
|
287
392
|
(0, prompt_dedup_1.clearPromptHashCache)(pollerKey);
|
|
288
393
|
// Issue #1268: Clear response hash cache too, so the next turn can save a
|
|
289
394
|
// response even when its content is identical to the previous turn's.
|
|
290
|
-
|
|
395
|
+
//
|
|
396
|
+
// Issue #2230: unless this turn is not actually over. A self-stop is settled
|
|
397
|
+
// by the tick once its verdict is known; a resume is continuing a paused turn.
|
|
398
|
+
if (!selfStop && !options.resume) {
|
|
399
|
+
(0, response_dedup_1.clearResponseHashCache)(pollerKey);
|
|
400
|
+
}
|
|
291
401
|
}
|
|
292
402
|
/**
|
|
293
403
|
* Stop polling for a worktree and CLI tool / instance combination
|
|
@@ -312,11 +422,15 @@ function stopAllPolling() {
|
|
|
312
422
|
const keys = new Set([
|
|
313
423
|
...Array.from(coordinator.activePollers.keys()),
|
|
314
424
|
...Array.from(coordinator.owners.keys()),
|
|
425
|
+
// Issue #2230: a paused chain has no timer and no owner, but it is still
|
|
426
|
+
// holding a response hash for the turn it paused on.
|
|
427
|
+
...Array.from(coordinator.pausedOnPrompt),
|
|
315
428
|
]);
|
|
316
429
|
for (const pollerKey of keys) {
|
|
317
430
|
stopPollingByKey(pollerKey);
|
|
318
431
|
}
|
|
319
432
|
coordinator.pendingRestart.clear();
|
|
433
|
+
coordinator.pausedOnPrompt.clear();
|
|
320
434
|
}
|
|
321
435
|
/**
|
|
322
436
|
* Get list of active pollers
|
|
@@ -448,10 +562,30 @@ function migrateResponsePollerWorktreeIds(renames, resolveCliToolId) {
|
|
|
448
562
|
for (const move of moves) {
|
|
449
563
|
const startedAt = carried.get(move.newKey)?.startedAt;
|
|
450
564
|
exports.pollingStartTimes.set(move.newKey, startedAt ?? Date.now());
|
|
451
|
-
if (move.cliToolId
|
|
565
|
+
if (isFullScreenTui(move.cliToolId)) {
|
|
452
566
|
(0, tui_accumulator_1.initTuiAccumulator)(move.newKey);
|
|
453
567
|
}
|
|
454
568
|
scheduleNextResponsePoll(move.newWorktreeId, move.cliToolId, move.instanceId);
|
|
455
569
|
}
|
|
570
|
+
// Issue #2230: a chain paused on a prompt has no timer, so the loop above
|
|
571
|
+
// never sees it — but it is holding the response hash for the turn it paused
|
|
572
|
+
// on, and `/respond` will resume it under the NEW ID. Move the hash and the
|
|
573
|
+
// pause mark with it; the chain itself stays paused until that resume.
|
|
574
|
+
const pausedMoves = [];
|
|
575
|
+
for (const pollerKey of Array.from(coordinator.pausedOnPrompt)) {
|
|
576
|
+
const parts = splitPollerKey(pollerKey);
|
|
577
|
+
if (!parts)
|
|
578
|
+
continue;
|
|
579
|
+
const newWorktreeId = targets.get(parts.worktreeId);
|
|
580
|
+
if (!newWorktreeId)
|
|
581
|
+
continue;
|
|
582
|
+
pausedMoves.push({ oldKey: pollerKey, newKey: `${newWorktreeId}:${parts.instanceId}` });
|
|
583
|
+
}
|
|
584
|
+
for (const move of pausedMoves)
|
|
585
|
+
coordinator.pausedOnPrompt.delete(move.oldKey);
|
|
586
|
+
for (const move of pausedMoves) {
|
|
587
|
+
(0, response_dedup_1.renameResponseHashCacheKey)(move.oldKey, move.newKey);
|
|
588
|
+
coordinator.pausedOnPrompt.add(move.newKey);
|
|
589
|
+
}
|
|
456
590
|
return moves;
|
|
457
591
|
}
|
|
@@ -20,7 +20,8 @@ exports.resetWaitingPushDedup = resetWaitingPushDedup;
|
|
|
20
20
|
const crypto_1 = require("crypto");
|
|
21
21
|
/** Default suppression window: repeats of identical content within this are dropped. */
|
|
22
22
|
exports.DEFAULT_DEDUP_WINDOW_MS = 30_000;
|
|
23
|
-
const lastSent =
|
|
23
|
+
const lastSent = globalThis.__notificationDedupLastSent ??
|
|
24
|
+
(globalThis.__notificationDedupLastSent = new Map());
|
|
24
25
|
function contentHash(content) {
|
|
25
26
|
return (0, crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
26
27
|
}
|
package/package.json
CHANGED