commandmate 0.45.1 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +319 -315
- package/.next/app-path-routes-manifest.json +15 -15
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +3 -3
- package/.next/react-loadable-manifest.json +4 -4
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.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/release-notes/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_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.js.nft.json +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.js +9 -9
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +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.js.nft.json +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/relays/[relayId]/cancel/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/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/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +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.js.nft.json +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/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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +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.js +3 -3
- 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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +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]/direct-input/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/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.js +3 -3
- 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_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/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +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.js +9 -9
- 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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +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.js +3 -3
- 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.js +3 -3
- 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.js +3 -3
- 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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page.js.nft.json +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.js +1 -1
- package/.next/server/app/more/page.js.nft.json +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page.js +1 -1
- package/.next/server/app/offline/page.js.nft.json +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +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.js +2 -2
- package/.next/server/app/repositories/page.js.nft.json +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page.js +1 -1
- package/.next/server/app/review/page.js.nft.json +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page.js +1 -1
- package/.next/server/app/skills/installed/page.js.nft.json +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page.js +1 -1
- package/.next/server/app/skills/page.js.nft.json +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +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 +12 -9
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +15 -15
- package/.next/server/chunks/1341.js +1 -1
- package/.next/server/chunks/1501.js +2 -2
- package/.next/server/chunks/1559.js +1 -1
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2283.js +15 -13
- package/.next/server/chunks/2404.js +4 -4
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2460.js +1 -1
- package/.next/server/chunks/2560.js +1 -1
- package/.next/server/chunks/2908.js +1 -1
- package/.next/server/chunks/2984.js +25 -0
- package/.next/server/chunks/3543.js +1 -1
- package/.next/server/chunks/3945.js +1 -1
- package/.next/server/chunks/4072.js +15 -0
- package/.next/server/chunks/4207.js +1 -1
- package/.next/server/chunks/4718.js +3 -3
- package/.next/server/chunks/4837.js +3 -3
- package/.next/server/chunks/5209.js +1 -1
- package/.next/server/chunks/5292.js +2 -2
- package/.next/server/chunks/5845.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6345.js +1 -0
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6722.js +1 -0
- package/.next/server/chunks/8098.js +1 -1
- package/.next/server/chunks/8356.js +1 -1
- package/.next/server/chunks/8581.js +25 -29
- package/.next/server/chunks/8728.js +2 -2
- package/.next/server/chunks/8791.js +1 -1
- package/.next/server/chunks/8800.js +4 -4
- package/.next/server/chunks/8918.js +5 -5
- package/.next/server/chunks/9390.js +1 -1
- package/.next/server/chunks/9627.js +2 -1
- package/.next/server/chunks/9731.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages/_error.js +2 -2
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{2407-86890b00eeecf391.js → 2407-9703c569370b8a9d.js} +1 -1
- package/.next/static/chunks/{2619-a305ad4810f1393a.js → 2619-04bc32f026a0d946.js} +1 -1
- package/.next/static/chunks/2717.1583400c272dc1d9.js +1 -0
- package/.next/static/chunks/2770-db80aedf6384d594.js +1 -0
- package/.next/static/chunks/2863-6a07ec82fb52f4dd.js +1 -0
- package/.next/static/chunks/3201-c01f1feed69020e6.js +1 -0
- package/.next/static/chunks/4768-ad6f82f034f21eae.js +1 -0
- package/.next/static/chunks/5371-0dc947683e46e78c.js +1 -0
- package/.next/static/chunks/5794-4eee51ff1e878aa6.js +1 -0
- package/.next/static/chunks/5927-54315d21f323e204.js +1 -0
- package/.next/static/chunks/602-25e45a2422d2a3ea.js +1 -0
- package/.next/static/chunks/6606-02ddc006686a6b7d.js +1 -0
- package/.next/static/chunks/6654-f3534f35f16644bc.js +1 -0
- package/.next/static/chunks/7730-011e137be64403ae.js +1 -0
- package/.next/static/chunks/8165-263172c17ae8e8c3.js +1 -0
- package/.next/static/chunks/{8407-9a72e6f07c5d5458.js → 8407-155f84e5ba05f1d5.js} +1 -1
- package/.next/static/chunks/8606-cf6bdbff44a764cf.js +1 -0
- package/.next/static/chunks/934-a37509667baa86ee.js +1 -0
- package/.next/static/chunks/9465-e5a351baa941f31b.js +1 -0
- package/.next/static/chunks/app/layout-3e357690446cd2da.js +1 -0
- package/.next/static/chunks/app/repositories/page-71f5a5ac29b0370d.js +1 -0
- package/.next/static/chunks/app/review/page-e2fd4a590646a2d8.js +1 -0
- package/.next/static/chunks/app/sessions/page-9859ed61e93e6890.js +1 -0
- package/.next/static/chunks/app/skills/[skillId]/page-52ba58712faf3378.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-effbf0796881dd5a.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/{page-c601f3b3ece6557e.js → page-373ea37b16d930c7.js} +1 -1
- package/.next/static/chunks/{webpack-ee3ccbe378c4aa06.js → webpack-468b7b33030234b3.js} +1 -1
- package/.next/static/css/743f79ff19ab91e9.css +1 -0
- package/.next/trace +13 -13
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +6 -1
- package/dist/cli/types/api-responses.d.ts +8 -6
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts +13 -1
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -1
- package/dist/lib/polling/auto-yes-suppression-reason.js +3 -0
- package/dist/server/locales/en/worktree.json +46 -16
- package/dist/server/locales/ja/worktree.json +46 -16
- package/dist/server/src/config/activity-bar-config.js +56 -9
- package/dist/server/src/lib/assistant-response-saver.js +13 -0
- package/dist/server/src/lib/auto-yes-poller.js +252 -54
- package/dist/server/src/lib/conversation-logger.js +14 -2
- package/dist/server/src/lib/db/chat-db.js +2 -1
- package/dist/server/src/lib/history/user-turn-recorder.js +14 -0
- package/dist/server/src/lib/hooks/sources/antigravity/history.js +28 -0
- package/dist/server/src/lib/hooks/sources/claude/history.js +7 -0
- package/dist/server/src/lib/hooks/sources/codex/history.js +7 -0
- package/dist/server/src/lib/hooks/sources/command-code/history.js +7 -0
- package/dist/server/src/lib/hooks/sources/transcript-history.js +34 -0
- package/dist/server/src/lib/log-manager.js +7 -2
- package/dist/server/src/lib/polling/auto-yes-enter-fallback-state.js +22 -3
- package/dist/server/src/lib/polling/auto-yes-poller-state.js +234 -0
- package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +3 -0
- package/dist/server/src/lib/polling/pending-scraped-response.js +119 -3
- package/dist/server/src/lib/polling/poller-owner.js +110 -0
- package/dist/server/src/lib/polling/prompt-dedup-state.js +26 -7
- package/dist/server/src/lib/polling/prompt-dedup.js +50 -22
- package/dist/server/src/lib/polling/response-checker.js +212 -36
- package/dist/server/src/lib/polling/response-dedup.js +112 -20
- package/dist/server/src/lib/polling/response-poller-core.js +178 -38
- package/dist/server/src/lib/polling/structured-history-gate.js +20 -1
- package/dist/server/src/lib/prompt-answer-sender.js +59 -12
- package/dist/server/src/lib/realtime/terminal-broadcast.js +14 -0
- package/dist/server/src/lib/session/chat-turn-progress.js +57 -6
- package/dist/server/src/lib/session/current-output-builder.js +19 -0
- package/dist/server/src/lib/session/next-action-helper.js +2 -15
- package/dist/server/src/lib/session/worktree-session-reconcile.js +83 -10
- package/dist/server/src/lib/timer-manager.js +7 -0
- package/dist/server/src/lib/tmux/tmux.js +24 -2
- package/dist/server/src/lib/verification/env-clean-gate.js +41 -0
- package/dist/server/src/lib/verification/gate-runner.js +17 -11
- package/dist/server/src/lib/verification/scope-gate.js +169 -3
- package/dist/server/src/types/sidebar.js +1 -0
- package/docs/en/architecture.md +26 -14
- package/docs/en/user-guide/webapp-guide.md +31 -18
- package/package.json +1 -1
- package/release-notes/0.46.0.json +110 -0
- package/release-notes/0.46.1.json +40 -0
- package/.next/server/chunks/1674.js +0 -15
- package/.next/server/chunks/5625.js +0 -25
- package/.next/server/chunks/6927.js +0 -1
- package/.next/server/chunks/9355.js +0 -1
- package/.next/static/chunks/1642-0f1bf8fb8161e10a.js +0 -1
- package/.next/static/chunks/2770-827a64760bab8a3a.js +0 -1
- package/.next/static/chunks/3922.0bc2e4e168d2b522.js +0 -1
- package/.next/static/chunks/4727-4c69d1aa4118cc09.js +0 -1
- package/.next/static/chunks/4768-fc0aa1d0891cac08.js +0 -1
- package/.next/static/chunks/5371-af1c2b68e983da61.js +0 -1
- package/.next/static/chunks/5521-c358c31f60e5e689.js +0 -1
- package/.next/static/chunks/6192-a67df090838b9aad.js +0 -1
- package/.next/static/chunks/6322-f1b30ed0ee43518f.js +0 -1
- package/.next/static/chunks/6654-ac431270fac1a283.js +0 -1
- package/.next/static/chunks/8165-3525fb42f1b4976e.js +0 -1
- package/.next/static/chunks/8318-01e7d1e5ad0a15a3.js +0 -1
- package/.next/static/chunks/8606-79c36d4c204bbd38.js +0 -1
- package/.next/static/chunks/9619-6c17a271f7f98fbb.js +0 -1
- package/.next/static/chunks/998-2eb5883186abd0bd.js +0 -1
- package/.next/static/chunks/app/layout-eaa6365f8f942e88.js +0 -1
- package/.next/static/chunks/app/repositories/page-6f7511f4ac8449e2.js +0 -1
- package/.next/static/chunks/app/review/page-95f749caae382f46.js +0 -1
- package/.next/static/chunks/app/sessions/page-ebfed2c80e916fb8.js +0 -1
- package/.next/static/chunks/app/skills/[skillId]/page-e4b14031f946c007.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-3372b78fde232b1f.js +0 -1
- package/.next/static/css/6faeb04a0b6819d4.css +0 -1
- /package/.next/static/{vYIpFRCU_Mt0wtG8F_3-6 → QxDf7lm-JJkWoj-TyWiSB}/_buildManifest.js +0 -0
- /package/.next/static/{vYIpFRCU_Mt0wtG8F_3-6 → QxDf7lm-JJkWoj-TyWiSB}/_ssgManifest.js +0 -0
|
@@ -49,6 +49,7 @@ const session_name_1 = require("../../lib/cli-tools/session-name");
|
|
|
49
49
|
const geometry_delegation_1 = require("../../lib/tmux/geometry-delegation");
|
|
50
50
|
const tmux_session_surface_1 = require("../../lib/session/tmux-session-surface");
|
|
51
51
|
const response_poller_core_1 = require("./response-poller-core");
|
|
52
|
+
const poller_owner_1 = require("./poller-owner");
|
|
52
53
|
const push_1 = require("../../lib/push");
|
|
53
54
|
// Issue #1790: imported by deep path, not through `@/lib/push`. Suites that
|
|
54
55
|
// replace the barrel to count notifications (e.g. the #1547 escalation test)
|
|
@@ -449,6 +450,48 @@ function markOrSettleStructuredHistoryRecheck(pollerKey, recorded) {
|
|
|
449
450
|
(0, response_dedup_1.markStructuredHistoryRecheckPending)(pollerKey);
|
|
450
451
|
}
|
|
451
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Whether this tick may still act for `pollerKey` after the `await` it has just
|
|
455
|
+
* resumed from, logging the refusal when it may not (Issue #3705).
|
|
456
|
+
*
|
|
457
|
+
* #3663, #3684 and #3701 asked {@link mayActForPollerKey} at the writes that had
|
|
458
|
+
* been reported, one at a time. A tick resumes from an `await` with no idea
|
|
459
|
+
* whether its chain was stopped, cut loose past `STALLED_TICK_TIMEOUT` or moved
|
|
460
|
+
* by a worktree-ID migration in the meantime, and every write after that
|
|
461
|
+
* `await` — the prompt sweep, the waiting edge, the completion push, the cursor,
|
|
462
|
+
* the fault notifier, the TUI accumulator, the recheck and skip-streak state —
|
|
463
|
+
* lands under a key it no longer owns, on whatever the successor has put there.
|
|
464
|
+
* So the question is asked after every `await` of the tick, named by the point
|
|
465
|
+
* it resumed from, and the writes below each point run only on a yes.
|
|
466
|
+
*
|
|
467
|
+
* Not asked where a self-stop hands back its verdict (`return await
|
|
468
|
+
* recordCompletedResponse(…)`): `runPollTick` settles the pause from that
|
|
469
|
+
* value, and a stop the tick raised itself must not lose what it recorded.
|
|
470
|
+
*
|
|
471
|
+
* @param ctx - What this tick is keyed by
|
|
472
|
+
* @param after - The `await` the tick has just resumed from, for the log line
|
|
473
|
+
*/
|
|
474
|
+
function mayStillActAfter(ctx, after) {
|
|
475
|
+
if ((0, poller_owner_1.mayActForPollerKey)(ctx.pollerKey))
|
|
476
|
+
return true;
|
|
477
|
+
// One line per retired tick, at the first point that turned it away: a tick
|
|
478
|
+
// refused at the capture is asked again after the reader and the log.
|
|
479
|
+
const owner = (0, poller_owner_1.currentPollerOwner)();
|
|
480
|
+
if (owner === undefined || !refusalLogged.has(owner)) {
|
|
481
|
+
if (owner)
|
|
482
|
+
refusalLogged.add(owner);
|
|
483
|
+
logger.info('retired-tick-refused', {
|
|
484
|
+
worktreeId: ctx.worktreeId,
|
|
485
|
+
cliToolId: ctx.cliToolId,
|
|
486
|
+
instanceId: ctx.resolvedInstanceId,
|
|
487
|
+
generation: owner?.generation,
|
|
488
|
+
after,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
/** The retired chains {@link mayStillActAfter} has already logged (Issue #3705). */
|
|
494
|
+
const refusalLogged = new WeakSet();
|
|
452
495
|
/**
|
|
453
496
|
* Read the frame this tick captured: is there a finished turn on it that the
|
|
454
497
|
* cursor has not already counted?
|
|
@@ -469,17 +512,25 @@ function markOrSettleStructuredHistoryRecheck(pollerKey, recorded) {
|
|
|
469
512
|
* @param ctx - What this tick is keyed by
|
|
470
513
|
* @param output - The capture this tick made
|
|
471
514
|
* @param lastCapturedLine - Its cursor, 0 when there is none
|
|
515
|
+
* @param owns - Whether the tick still owns its key (Issue #3705); false reads
|
|
516
|
+
* the frame and writes nothing
|
|
472
517
|
* @returns What the tick goes on with, or `false` when the tick ends here
|
|
473
518
|
*/
|
|
474
|
-
function extractCompletedTurn(ctx, output, lastCapturedLine) {
|
|
519
|
+
function extractCompletedTurn(ctx, output, lastCapturedLine, owns) {
|
|
475
520
|
const { db, worktreeId, cliToolId, instanceId, resolvedInstanceId, pollerKey } = ctx;
|
|
476
521
|
// Issue #2000: the frame is in hand, so this is the cheapest place to ask
|
|
477
522
|
// whether the model API has stalled the session. Level in, edge out — see
|
|
478
523
|
// the helper.
|
|
479
|
-
|
|
524
|
+
//
|
|
525
|
+
// Issue #3705: every write in this function is the caller's `owns` — asked
|
|
526
|
+
// right after the capture, with no `await` since. A retired tick's frame is
|
|
527
|
+
// read, and none of it lands on the fault episode, the accumulator, the skip
|
|
528
|
+
// streaks or the pending prompts of the key it lost.
|
|
529
|
+
if (owns)
|
|
530
|
+
observeUpstreamFaultForPush(worktreeId, cliToolId, instanceId, output);
|
|
480
531
|
// Layer 2: Accumulate TUI content for full-screen TUI tools, so a turn that
|
|
481
532
|
// outgrows the alternate-screen pane keeps the head that has scrolled away.
|
|
482
|
-
if (cliToolId === 'opencode' || cliToolId === 'copilot') {
|
|
533
|
+
if (owns && (cliToolId === 'opencode' || cliToolId === 'copilot')) {
|
|
483
534
|
// Issue #1911: opencode is accumulated from the CURRENT TURN'S REGION
|
|
484
535
|
// rather than the whole frame. Feeding the raw pane seeded the accumulator
|
|
485
536
|
// with the previous turn's transcript, the echoed prompt and the bottom
|
|
@@ -498,13 +549,18 @@ function extractCompletedTurn(ctx, output, lastCapturedLine) {
|
|
|
498
549
|
// skipped on the line count ("no new output"), which says nothing about
|
|
499
550
|
// whether the prompt is still on screen, and neither do the cursor returns
|
|
500
551
|
// further down.
|
|
501
|
-
|
|
552
|
+
//
|
|
553
|
+
// Issue #3552: the same frame ends a run of duplicate response ticks for
|
|
554
|
+
// the same reason — an unfinished frame is not the saved reply.
|
|
555
|
+
if (owns && result) {
|
|
502
556
|
(0, prompt_dedup_1.resetDuplicatePromptSkipStreak)(pollerKey);
|
|
557
|
+
(0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
|
|
558
|
+
}
|
|
503
559
|
// DR-004 windowing: Only check tail lines
|
|
504
560
|
const { thinkingPattern } = (0, cli_patterns_1.getCliToolPatterns)(cliToolId);
|
|
505
561
|
const cleanOutput = (0, cli_patterns_1.stripAnsi)(output);
|
|
506
562
|
const tailLines = cleanOutput.split('\n').slice(-thinking_constants_1.THINKING_TAIL_LINE_COUNT).join('\n');
|
|
507
|
-
if (thinkingPattern.test(tailLines)) {
|
|
563
|
+
if (owns && thinkingPattern.test(tailLines)) {
|
|
508
564
|
// Intended twin of the sweep in recordCompletedResponse: this one runs when
|
|
509
565
|
// working is visible on an unfinished frame, that one when a response is
|
|
510
566
|
// recorded (Issue #31).
|
|
@@ -565,6 +621,21 @@ function extractCompletedTurn(ctx, output, lastCapturedLine) {
|
|
|
565
621
|
*/
|
|
566
622
|
function savePromptMessage(ctx, promptDetection, result, isFullScreenTui) {
|
|
567
623
|
const { db, worktree, worktreeId, cliToolId, instanceId, resolvedInstanceId, pollerKey } = ctx;
|
|
624
|
+
// Issue #3701: the question #3663 / #3684 ask, for the prompt a tick retired
|
|
625
|
+
// during the capture's `await` then finds on the pane — no row, broadcast,
|
|
626
|
+
// task event, push or waiting episode under the key it lost. Asked BEFORE the
|
|
627
|
+
// dedup below, which registers the hash before the save: a refused tick that
|
|
628
|
+
// registered it would make the successor drop the same prompt as a
|
|
629
|
+
// duplicate. No `await` from here to the end of this function.
|
|
630
|
+
if (!(0, poller_owner_1.mayActForPollerKey)(pollerKey)) {
|
|
631
|
+
logger.info('prompt-save-refused', {
|
|
632
|
+
worktreeId,
|
|
633
|
+
cliToolId,
|
|
634
|
+
instanceId: resolvedInstanceId,
|
|
635
|
+
generation: (0, poller_owner_1.currentPollerOwner)()?.generation,
|
|
636
|
+
});
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
568
639
|
// Issue #565: Content hash-based duplicate prompt prevention
|
|
569
640
|
const promptContent = promptDetection.rawContent || promptDetection.cleanContent;
|
|
570
641
|
const normalizedForDedup = (0, prompt_dedup_1.normalizePromptForDedup)(promptContent, cliToolId);
|
|
@@ -681,9 +752,10 @@ function savePromptMessage(ctx, promptDetection, result, isFullScreenTui) {
|
|
|
681
752
|
* @param cliToolId - CLI tool identifier
|
|
682
753
|
* @param result - The complete extraction this tick made
|
|
683
754
|
* @param pollerKey - Poller key ("worktreeId:instanceId")
|
|
755
|
+
* @param owns - Whether the tick still owns its key (Issue #3705)
|
|
684
756
|
* @returns The cleaned response
|
|
685
757
|
*/
|
|
686
|
-
function cleanCompletedResponse(cliToolId, result, pollerKey) {
|
|
758
|
+
function cleanCompletedResponse(cliToolId, result, pollerKey, owns) {
|
|
687
759
|
// Clean up responses
|
|
688
760
|
let cleanedResponse = result.response;
|
|
689
761
|
if (cliToolId === 'gemini') {
|
|
@@ -697,7 +769,10 @@ function cleanCompletedResponse(cliToolId, result, pollerKey) {
|
|
|
697
769
|
const sourceContent = accumulatedContent || result.response;
|
|
698
770
|
cleanedResponse = (0, response_cleaner_1.cleanCopilotResponse)(sourceContent);
|
|
699
771
|
cleanedResponse = (0, response_cleaner_1.truncateMessage)(cleanedResponse, copilot_constants_1.COPILOT_MAX_MESSAGE_LENGTH, copilot_constants_1.COPILOT_TRUNCATION_MARKER);
|
|
700
|
-
|
|
772
|
+
// Issue #3705: read, but not cleared, by a tick that lost the key — the
|
|
773
|
+
// successor may already be accumulating its own turn under it.
|
|
774
|
+
if (owns)
|
|
775
|
+
(0, tui_accumulator_1.clearTuiAccumulator)(pollerKey);
|
|
701
776
|
}
|
|
702
777
|
else if (cliToolId === 'opencode') {
|
|
703
778
|
// Issue #1911 defect 3: opencode wrote to the Layer-2 accumulator but never
|
|
@@ -719,7 +794,8 @@ function cleanCompletedResponse(cliToolId, result, pollerKey) {
|
|
|
719
794
|
? accumulatedContent
|
|
720
795
|
: result.response;
|
|
721
796
|
cleanedResponse = (0, response_cleaner_1.cleanOpenCodeResponse)(sourceContent);
|
|
722
|
-
|
|
797
|
+
if (owns)
|
|
798
|
+
(0, tui_accumulator_1.clearTuiAccumulator)(pollerKey);
|
|
723
799
|
}
|
|
724
800
|
return cleanedResponse;
|
|
725
801
|
}
|
|
@@ -739,6 +815,15 @@ function cleanCompletedResponse(cliToolId, result, pollerKey) {
|
|
|
739
815
|
*/
|
|
740
816
|
async function recheckDuplicateResponse(ctx, result, claudeMetadata) {
|
|
741
817
|
const { db, worktree, worktreeId, cliToolId, instanceId, resolvedInstanceId, pollerKey } = ctx;
|
|
818
|
+
// Issue #3705 (review): a retired tick reaches this function too. The dedup
|
|
819
|
+
// answers "duplicate" on a hash match BEFORE it asks about ownership (it only
|
|
820
|
+
// refuses to REGISTER a hash), and the hash it matches may be the successor's
|
|
821
|
+
// — the same reply, saved by the chain that replaced this one while this
|
|
822
|
+
// tick's capture was in flight. Everything below writes under the key (the
|
|
823
|
+
// skip-log claim, the cursor, the recheck claim, then the reader), so the
|
|
824
|
+
// question is asked first, with no `await` between it and those writes.
|
|
825
|
+
if (!mayStillActAfter(ctx, 'capture'))
|
|
826
|
+
return false;
|
|
742
827
|
// Issue #1695: this branch used to drop the response silently — the
|
|
743
828
|
// prompt-side guard above has logged its skip since #565, this one
|
|
744
829
|
// logged nothing at all, so a reply that never reached History left no
|
|
@@ -816,6 +901,11 @@ async function recheckDuplicateResponse(ctx, result, claudeMetadata) {
|
|
|
816
901
|
worktreePath: worktree.path,
|
|
817
902
|
transcriptPathHint: claudeMetadata?.logFilePath ?? null,
|
|
818
903
|
});
|
|
904
|
+
// Issue #3705: the recheck the successor is owed is not settled, and its
|
|
905
|
+
// hold not dropped, by a tick retired while the reader ran. What the reader
|
|
906
|
+
// answered is still the answer (it asks the same question before it writes).
|
|
907
|
+
if (!mayStillActAfter(ctx, 'recheck-reader'))
|
|
908
|
+
return recaptured;
|
|
819
909
|
if (recaptured) {
|
|
820
910
|
(0, response_dedup_1.settleStructuredHistoryRecheck)(pollerKey);
|
|
821
911
|
// Issue #2436: the turn is now the agent's own Markdown, so a
|
|
@@ -863,7 +953,10 @@ function recordOrHoldScrapedResponse(ctx, cleanedResponse, claudeMetadata, decis
|
|
|
863
953
|
// width and gutter-prefixed, so no content check could ever recognise them
|
|
864
954
|
// as the same reply.
|
|
865
955
|
if (holdScrapedHistory) {
|
|
866
|
-
(
|
|
956
|
+
// Issue #3663: refused (false) to a tick whose chain was retired while it
|
|
957
|
+
// awaited the reader — a stop, a stalled-tick restart or a worktree-ID
|
|
958
|
+
// migration. The store logs that, so this tick does not claim a hold.
|
|
959
|
+
const held = (0, pending_scraped_response_1.holdScrapedResponse)(pollerKey, {
|
|
867
960
|
worktreeId,
|
|
868
961
|
cliToolId,
|
|
869
962
|
instanceId: resolvedInstanceId,
|
|
@@ -879,19 +972,37 @@ function recordOrHoldScrapedResponse(ctx, cleanedResponse, claudeMetadata, decis
|
|
|
879
972
|
transcriptPathHint: claudeMetadata?.logFilePath ?? null,
|
|
880
973
|
expiresAt: Date.now() + pending_scraped_response_1.PENDING_SCRAPE_HOLD_MS,
|
|
881
974
|
});
|
|
882
|
-
|
|
975
|
+
if (held) {
|
|
976
|
+
logger.info('structured-history-scrape-held', {
|
|
977
|
+
worktreeId,
|
|
978
|
+
cliToolId,
|
|
979
|
+
instanceId: resolvedInstanceId,
|
|
980
|
+
scrapedLength: cleanedResponse.length,
|
|
981
|
+
holdMs: pending_scraped_response_1.PENDING_SCRAPE_HOLD_MS,
|
|
982
|
+
});
|
|
983
|
+
// The completion edge is announced HERE and not at the flush, because the
|
|
984
|
+
// turn finished now. A relay waiting on this session gets #2401's grace
|
|
985
|
+
// window to find a turn-keyed row, which is exactly the row the hold is
|
|
986
|
+
// waiting for; delaying the announcement by the hold would delay every
|
|
987
|
+
// delivery by it too. Issue #3684: only for a hold that was taken — a
|
|
988
|
+
// refused one is a retired tick, which announces nothing under the key
|
|
989
|
+
// it lost, as in the immediate-save branch below.
|
|
990
|
+
(0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, false);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
else if (!suppressScrapedHistory && !(0, poller_owner_1.mayActForPollerKey)(pollerKey)) {
|
|
994
|
+
// Issue #3684: the same question the hold asks (#3663), for the reply saved
|
|
995
|
+
// at once — a tick retired while it awaited the reader or the log writes no
|
|
996
|
+
// row, broadcast or relay edge under the key it lost. No `await` between
|
|
997
|
+
// this check and the three below. Issue #3705: the caller now asks the
|
|
998
|
+
// same question for the rest of the tick's writes and the Markdown log for
|
|
999
|
+
// its own, so a retired tick reaches this branch only to be refused here.
|
|
1000
|
+
logger.info('scraped-response-save-refused', {
|
|
883
1001
|
worktreeId,
|
|
884
1002
|
cliToolId,
|
|
885
1003
|
instanceId: resolvedInstanceId,
|
|
886
|
-
|
|
887
|
-
holdMs: pending_scraped_response_1.PENDING_SCRAPE_HOLD_MS,
|
|
1004
|
+
generation: (0, poller_owner_1.currentPollerOwner)()?.generation,
|
|
888
1005
|
});
|
|
889
|
-
// The completion edge is announced HERE and not at the flush, because the
|
|
890
|
-
// turn finished now. A relay waiting on this session gets #2401's grace
|
|
891
|
-
// window to find a turn-keyed row, which is exactly the row the hold is
|
|
892
|
-
// waiting for; delaying the announcement by the hold would delay every
|
|
893
|
-
// delivery by it too.
|
|
894
|
-
(0, relay_triggers_1.onRelayTurnCompleted)(worktreeId, cliToolId, resolvedInstanceId, false);
|
|
895
1006
|
}
|
|
896
1007
|
else if (!suppressScrapedHistory) {
|
|
897
1008
|
// Create new CLI tool message in database
|
|
@@ -994,7 +1105,13 @@ async function recordCompletedResponse(ctx, turn, claudeMetadata, cleanedRespons
|
|
|
994
1105
|
// a return — so unless the fact is written down now, the ask never happens
|
|
995
1106
|
// again. A `true` settles it: the turn is recorded and there is nothing left
|
|
996
1107
|
// to re-ask about.
|
|
997
|
-
|
|
1108
|
+
//
|
|
1109
|
+
// Issue #3705: by a tick that still owns the key. A retired one goes on to
|
|
1110
|
+
// the scrape decision below only so that the store's and the save branch's
|
|
1111
|
+
// own refusals (#3663, #3684) speak for it; nothing else it would write does.
|
|
1112
|
+
if (mayStillActAfter(ctx, 'reader')) {
|
|
1113
|
+
markOrSettleStructuredHistoryRecheck(pollerKey, structuredHistoryLive);
|
|
1114
|
+
}
|
|
998
1115
|
// Issue #2317 Phase D: the scrape is dropped while the geometry is
|
|
999
1116
|
// delegated, and the transcript capture above is what makes that safe.
|
|
1000
1117
|
//
|
|
@@ -1025,8 +1142,29 @@ async function recordCompletedResponse(ctx, turn, claudeMetadata, cleanedRespons
|
|
|
1025
1142
|
// out. Only the two writes that RECORD THE REPLY wait.
|
|
1026
1143
|
const holdScrapedHistory = !suppressScrapedHistory && captureReport.outcome === 'not_yet_closed';
|
|
1027
1144
|
// Create Markdown log file for the conversation pair
|
|
1145
|
+
//
|
|
1146
|
+
// Issue #3705: keyed, so the log asks before its own write (after its own
|
|
1147
|
+
// awaits) whether this tick still owns the key. A held reply confirmed by a
|
|
1148
|
+
// stop is logged by the store with no key (`pending-scraped-response`), and
|
|
1149
|
+
// that log is kept.
|
|
1028
1150
|
if (cleanedResponse && !suppressScrapedHistory && !holdScrapedHistory) {
|
|
1029
|
-
await (0, conversation_logger_1.recordClaudeConversation)(db, worktreeId, cleanedResponse, cliToolId);
|
|
1151
|
+
await (0, conversation_logger_1.recordClaudeConversation)(db, worktreeId, cleanedResponse, cliToolId, { pollerKey });
|
|
1152
|
+
}
|
|
1153
|
+
// Issue #3705: everything from here to the end of the function runs without
|
|
1154
|
+
// an `await`, so this is the tick's last chance to find out that it was
|
|
1155
|
+
// retired while the reader or the log ran. A retired tick sweeps no prompt to
|
|
1156
|
+
// "answered", closes no waiting episode, sends no completion push and moves
|
|
1157
|
+
// no cursor under the key it lost — the successor's, possibly. The scrape
|
|
1158
|
+
// decision still runs: every branch of it asks the same question itself
|
|
1159
|
+
// (#3663 the hold, #3684 the immediate save, the store the discard), and its
|
|
1160
|
+
// refusal lines are what the earlier suites read.
|
|
1161
|
+
if (!mayStillActAfter(ctx, 'conversation-log')) {
|
|
1162
|
+
recordOrHoldScrapedResponse(ctx, cleanedResponse, claudeMetadata, {
|
|
1163
|
+
structuredHistoryLive,
|
|
1164
|
+
suppressScrapedHistory,
|
|
1165
|
+
holdScrapedHistory,
|
|
1166
|
+
});
|
|
1167
|
+
return false;
|
|
1030
1168
|
}
|
|
1031
1169
|
// Mark any pending prompts as answered. Intended twin of the sweep in
|
|
1032
1170
|
// extractCompletedTurn (working visible on an unfinished frame); this one runs
|
|
@@ -1098,8 +1236,23 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
|
|
|
1098
1236
|
(0, response_poller_core_1.stopPolling)(worktreeId, cliToolId, instanceId);
|
|
1099
1237
|
return false;
|
|
1100
1238
|
}
|
|
1239
|
+
const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
|
|
1240
|
+
// Issue #3213: what the steps split out of this function are keyed by.
|
|
1241
|
+
const ctx = {
|
|
1242
|
+
db,
|
|
1243
|
+
worktree,
|
|
1244
|
+
worktreeId,
|
|
1245
|
+
cliToolId,
|
|
1246
|
+
instanceId,
|
|
1247
|
+
resolvedInstanceId,
|
|
1248
|
+
pollerKey,
|
|
1249
|
+
};
|
|
1101
1250
|
// Check if CLI tool session is running
|
|
1102
1251
|
const running = await (0, cli_session_1.isSessionRunning)(worktreeId, cliToolId, instanceId);
|
|
1252
|
+
// Issue #3705: the first of the tick's awaits. Every one of them is followed
|
|
1253
|
+
// by the same question; see `mayStillActAfter`.
|
|
1254
|
+
if (!mayStillActAfter(ctx, 'session-check'))
|
|
1255
|
+
return false;
|
|
1103
1256
|
if (!running) {
|
|
1104
1257
|
logger.info('session-not-running');
|
|
1105
1258
|
// `stopPolling` is what confirms a held scrape here; see
|
|
@@ -1111,23 +1264,14 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
|
|
|
1111
1264
|
(0, response_poller_core_1.stopPolling)(worktreeId, cliToolId, instanceId);
|
|
1112
1265
|
return false;
|
|
1113
1266
|
}
|
|
1114
|
-
const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
|
|
1115
|
-
// Issue #3213: what the steps split out of this function are keyed by.
|
|
1116
|
-
const ctx = {
|
|
1117
|
-
db,
|
|
1118
|
-
worktree,
|
|
1119
|
-
worktreeId,
|
|
1120
|
-
cliToolId,
|
|
1121
|
-
instanceId,
|
|
1122
|
-
resolvedInstanceId,
|
|
1123
|
-
pollerKey,
|
|
1124
|
-
};
|
|
1125
1267
|
// Issue #2436: a scrape held by an earlier tick, whose transcript has now
|
|
1126
1268
|
// had its whole budget to close. Ahead of everything below because most
|
|
1127
1269
|
// ticks of a finished turn never reach the save path — a static frame is a
|
|
1128
1270
|
// duplicate for an alternate-screen tool and yields no new lines for a
|
|
1129
1271
|
// scrollback one, and both of those return early.
|
|
1130
1272
|
await (0, pending_scraped_response_1.settleExpiredPendingScrapedResponse)(pollerKey);
|
|
1273
|
+
if (!mayStillActAfter(ctx, 'pending-scrape-settle'))
|
|
1274
|
+
return false;
|
|
1131
1275
|
// Issue #2317 Phase D: is a human reading this pane at their own terminal
|
|
1132
1276
|
// size right now, and did they just stop?
|
|
1133
1277
|
//
|
|
@@ -1139,6 +1283,8 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
|
|
|
1139
1283
|
const delegation = (0, tmux_session_surface_1.isLiveAttachEligibleSession)(sessionName)
|
|
1140
1284
|
? await (0, geometry_delegation_1.probeGeometryDelegation)(sessionName)
|
|
1141
1285
|
: { delegated: false, released: false };
|
|
1286
|
+
if (!mayStillActAfter(ctx, 'geometry-probe'))
|
|
1287
|
+
return false;
|
|
1142
1288
|
if (delegation.released) {
|
|
1143
1289
|
// The canvas is 1000 rows again, so a cursor recorded against a 44-row
|
|
1144
1290
|
// frame indexes into a pane that no longer exists. Zero is the only value
|
|
@@ -1165,7 +1311,16 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
|
|
|
1165
1311
|
// taken from the same constant extractResponse() measures saturation against
|
|
1166
1312
|
// (Issue #1670) — a literal here would silently decouple the two.
|
|
1167
1313
|
const output = await (0, cli_session_1.captureSessionOutput)(worktreeId, cliToolId, tmux_capture_cache_1.CACHE_MAX_CAPTURE_LINES, instanceId);
|
|
1168
|
-
|
|
1314
|
+
// Issue #3705: there is no `await` from here to the transcript reader
|
|
1315
|
+
// inside the save path, so this one answer covers every write in between —
|
|
1316
|
+
// the fault notifier, the TUI accumulator, the prompt sweep, the skip
|
|
1317
|
+
// streaks, the cursor, the in-progress id. A retired tick still READS the
|
|
1318
|
+
// frame and goes on, because the writes that already ask for themselves
|
|
1319
|
+
// (the prompt row #3701, the response hash #3663, the reader and the
|
|
1320
|
+
// scrape decision) are where its refusal is recorded; it writes none of
|
|
1321
|
+
// the rest.
|
|
1322
|
+
const owns = mayStillActAfter(ctx, 'capture');
|
|
1323
|
+
const turn = extractCompletedTurn(ctx, output, lastCapturedLine, owns);
|
|
1169
1324
|
if (!turn) {
|
|
1170
1325
|
return false;
|
|
1171
1326
|
}
|
|
@@ -1189,24 +1344,41 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
|
|
|
1189
1344
|
// has lost the position `detectDialog` judges by.
|
|
1190
1345
|
const promptIsLive = promptDetection.isPrompt && (0, response_checker_dialog_gate_1.isNumberedDialogVouched)(cliToolId, promptDetection, output);
|
|
1191
1346
|
if (promptIsLive) {
|
|
1347
|
+
// Issue #3552: a live prompt is not the saved reply, so a run of
|
|
1348
|
+
// duplicate response ticks is over; the response hash is kept (a resume
|
|
1349
|
+
// after the answer must not re-save the reply underneath, #2230).
|
|
1350
|
+
if (owns)
|
|
1351
|
+
(0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
|
|
1352
|
+
// Asks for itself (#3701), before the prompt dedup.
|
|
1192
1353
|
return savePromptMessage(ctx, promptDetection, result, isFullScreenTui);
|
|
1193
1354
|
}
|
|
1194
1355
|
// Issue #3538: a finished frame without a live prompt ends the run of
|
|
1195
1356
|
// duplicate prompt ticks, as an unfinished one does in `extractCompletedTurn`.
|
|
1196
|
-
|
|
1357
|
+
if (owns)
|
|
1358
|
+
(0, prompt_dedup_1.resetDuplicatePromptSkipStreak)(pollerKey);
|
|
1197
1359
|
// Validate response content is not empty
|
|
1198
1360
|
if (!result.response || result.response.trim() === '') {
|
|
1199
|
-
|
|
1361
|
+
// Issue #3552: an empty response is not the saved reply either.
|
|
1362
|
+
if (owns) {
|
|
1363
|
+
(0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
|
|
1364
|
+
(0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
|
|
1365
|
+
}
|
|
1200
1366
|
return false;
|
|
1201
1367
|
}
|
|
1202
1368
|
// Parse Claude-specific metadata
|
|
1203
1369
|
const claudeMetadata = cliToolId === 'claude'
|
|
1204
1370
|
? (0, claude_output_1.parseClaudeOutput)(result.response)
|
|
1205
1371
|
: undefined;
|
|
1206
|
-
const cleanedResponse = cleanCompletedResponse(cliToolId, result, pollerKey);
|
|
1372
|
+
const cleanedResponse = cleanCompletedResponse(cliToolId, result, pollerKey, owns);
|
|
1207
1373
|
// If cleaned response is empty or just "[No content]", skip saving
|
|
1208
1374
|
if (!cleanedResponse || cleanedResponse.trim() === '' || cleanedResponse === '[No content]') {
|
|
1209
|
-
|
|
1375
|
+
// Issue #3552: nor is one that cleans down to nothing.
|
|
1376
|
+
if (owns) {
|
|
1377
|
+
(0, response_dedup_1.resetDuplicateResponseSkipStreak)(pollerKey);
|
|
1378
|
+
(0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
|
|
1379
|
+
}
|
|
1380
|
+
// Left as it was (Issue #3705 sets it aside as harmless): nothing sets the
|
|
1381
|
+
// in-progress id to a non-null value or reads it today.
|
|
1210
1382
|
(0, db_1.clearInProgressMessageId)(db, worktreeId, cliToolId, resolvedInstanceId);
|
|
1211
1383
|
return false;
|
|
1212
1384
|
}
|
|
@@ -1221,6 +1393,10 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
|
|
|
1221
1393
|
// Without this the disabled cursor would leave nothing suppressing re-saves and
|
|
1222
1394
|
// the poller would append the same finished reply every 2 s.
|
|
1223
1395
|
if (!lineCountIsCursor) {
|
|
1396
|
+
// A retired tick registers no hash here (#3663, `./response-dedup`), but
|
|
1397
|
+
// it can still MATCH one — the successor's, for the same reply. The
|
|
1398
|
+
// recheck below therefore opens on the ownership question itself before
|
|
1399
|
+
// its claim, its cursor and its read (Issue #3705 review).
|
|
1224
1400
|
if ((0, response_dedup_1.isDuplicateResponse)(pollerKey, cleanedResponse)) {
|
|
1225
1401
|
// `await`, so that a rejection still lands in the catch below.
|
|
1226
1402
|
return await recheckDuplicateResponse(ctx, result, claudeMetadata);
|
|
@@ -55,13 +55,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
55
55
|
exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL = exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = void 0;
|
|
56
56
|
exports.isDuplicateResponse = isDuplicateResponse;
|
|
57
57
|
exports.claimDuplicateResponseSkipLog = claimDuplicateResponseSkipLog;
|
|
58
|
+
exports.resetDuplicateResponseSkipStreak = resetDuplicateResponseSkipStreak;
|
|
58
59
|
exports.markStructuredHistoryRecheckPending = markStructuredHistoryRecheckPending;
|
|
59
60
|
exports.settleStructuredHistoryRecheck = settleStructuredHistoryRecheck;
|
|
60
61
|
exports.claimStructuredHistoryRecheck = claimStructuredHistoryRecheck;
|
|
61
62
|
exports.clearResponseHashCache = clearResponseHashCache;
|
|
62
63
|
exports.renameResponseHashCacheKey = renameResponseHashCacheKey;
|
|
64
|
+
exports.renameResponseHashCacheKeys = renameResponseHashCacheKeys;
|
|
63
65
|
const crypto_1 = require("crypto");
|
|
64
66
|
const global_state_1 = require("../global-state");
|
|
67
|
+
const poller_owner_1 = require("./poller-owner");
|
|
65
68
|
const responseHashCache = (0, global_state_1.getOrInitGlobal)('__responseHashCache', () => new Map());
|
|
66
69
|
/**
|
|
67
70
|
* In-memory cache: pollerKey -> ticks still to wait before the transcript reader
|
|
@@ -97,7 +100,9 @@ exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = 3;
|
|
|
97
100
|
*
|
|
98
101
|
* On `globalThis` for the same reason `responseHashCache` is, and cleared and
|
|
99
102
|
* moved at exactly the same moments — see the module comment. A new
|
|
100
|
-
* (non-duplicate) response restarts it in {@link isDuplicateResponse}
|
|
103
|
+
* (non-duplicate) response restarts it in {@link isDuplicateResponse}, and a
|
|
104
|
+
* tick that reads the frame and finds no reply on it ends the run without
|
|
105
|
+
* touching the hash — see {@link resetDuplicateResponseSkipStreak} (#3552).
|
|
101
106
|
*/
|
|
102
107
|
const duplicateResponseSkipStreak = (0, global_state_1.getOrInitGlobal)('__duplicateResponseSkipStreak', () => new Map());
|
|
103
108
|
/**
|
|
@@ -123,6 +128,13 @@ function isDuplicateResponse(pollerKey, content) {
|
|
|
123
128
|
if (responseHashCache.get(pollerKey) === hash) {
|
|
124
129
|
return true;
|
|
125
130
|
}
|
|
131
|
+
// Issue #3663: a tick of a stopped chain no longer owns its key, so it does
|
|
132
|
+
// not register a hash there — the same line the held scrape draws. Left
|
|
133
|
+
// behind, the hash of a reply that tick never saves (its hold is refused)
|
|
134
|
+
// would be inherited by the next chain on the key, or moved by a migration,
|
|
135
|
+
// and make the successor skip it.
|
|
136
|
+
if (!(0, poller_owner_1.mayActForPollerKey)(pollerKey))
|
|
137
|
+
return false;
|
|
126
138
|
responseHashCache.set(pollerKey, hash);
|
|
127
139
|
// Issue #3519: a new response starts a new run of duplicate ticks.
|
|
128
140
|
duplicateResponseSkipStreak.delete(pollerKey);
|
|
@@ -148,6 +160,28 @@ function claimDuplicateResponseSkipLog(pollerKey) {
|
|
|
148
160
|
const suppressed = consecutive === 1 ? 0 : exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL - 1;
|
|
149
161
|
return { log, consecutive, suppressed };
|
|
150
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* End the current run of duplicate response ticks for this pollerKey without
|
|
165
|
+
* touching the cached hash (Issue #3552).
|
|
166
|
+
*
|
|
167
|
+
* Called by `response-checker` on the ticks that read the frame and decide it
|
|
168
|
+
* is not a duplicate of the saved reply: an unfinished frame
|
|
169
|
+
* (`extractCompletedTurn`), a finished frame with a live prompt on it, and a
|
|
170
|
+
* finished frame whose response is empty. The reply is no longer on screen, so
|
|
171
|
+
* if the same reply comes back the next duplicate is the first of a new run and
|
|
172
|
+
* logs. The hash stays: whether the returning reply is saved again is the
|
|
173
|
+
* guard's question (#1268), and this only thins the log line (#3519).
|
|
174
|
+
*
|
|
175
|
+
* Not called at the entrance of a finished frame — that would make every
|
|
176
|
+
* duplicate tick the first of its run — nor on a tick that skipped extraction
|
|
177
|
+
* on the line count (null) or returned on the cursor: neither says the reply
|
|
178
|
+
* has left the screen (the same judgement as #3538).
|
|
179
|
+
*
|
|
180
|
+
* @param pollerKey - Poller key ("worktreeId:instanceId")
|
|
181
|
+
*/
|
|
182
|
+
function resetDuplicateResponseSkipStreak(pollerKey) {
|
|
183
|
+
duplicateResponseSkipStreak.delete(pollerKey);
|
|
184
|
+
}
|
|
151
185
|
/**
|
|
152
186
|
* Note that the transcript reader has unfinished business for this pollerKey
|
|
153
187
|
* (Issue #2399).
|
|
@@ -233,6 +267,55 @@ function clearResponseHashCache(pollerKey) {
|
|
|
233
267
|
// Issue #3519: the skip count is about the same hash.
|
|
234
268
|
duplicateResponseSkipStreak.delete(pollerKey);
|
|
235
269
|
}
|
|
270
|
+
/** Remove everything held under `key` and return it. */
|
|
271
|
+
function takeResponseDedupState(key) {
|
|
272
|
+
const state = {
|
|
273
|
+
hash: responseHashCache.get(key),
|
|
274
|
+
recheck: structuredHistoryRecheckCache.get(key),
|
|
275
|
+
streak: duplicateResponseSkipStreak.get(key),
|
|
276
|
+
};
|
|
277
|
+
responseHashCache.delete(key);
|
|
278
|
+
structuredHistoryRecheckCache.delete(key);
|
|
279
|
+
duplicateResponseSkipStreak.delete(key);
|
|
280
|
+
return state;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Write state lifted by {@link takeResponseDedupState} under `key`.
|
|
284
|
+
*
|
|
285
|
+
* `replaceRecheckWithHash` is the one difference between the batch and the
|
|
286
|
+
* single rename (Issue #3553): when a hash lands, the batch also replaces the
|
|
287
|
+
* recheck under `key` — deleting it when the source had none — so a recheck
|
|
288
|
+
* owed for a different turn is not attached to the hash that just arrived. The
|
|
289
|
+
* single rename keeps its earlier behaviour and leaves such a recheck alone.
|
|
290
|
+
*/
|
|
291
|
+
function putResponseDedupState(key, state, replaceRecheckWithHash) {
|
|
292
|
+
if (state.hash !== undefined)
|
|
293
|
+
responseHashCache.set(key, state.hash);
|
|
294
|
+
// Issue #2399: the recheck caveat moves with the hash it is about. Leaving it
|
|
295
|
+
// behind would strand a turn whose transcript is still open — the new key
|
|
296
|
+
// starts owing nothing, so every later tick returns at the dedup guard and the
|
|
297
|
+
// Markdown row is never written. It moves without a hash too: the
|
|
298
|
+
// `lineCountIsCursor` path marks it with no hash cached.
|
|
299
|
+
if (state.recheck !== undefined) {
|
|
300
|
+
structuredHistoryRecheckCache.set(key, state.recheck);
|
|
301
|
+
}
|
|
302
|
+
else if (replaceRecheckWithHash && state.hash !== undefined) {
|
|
303
|
+
structuredHistoryRecheckCache.delete(key);
|
|
304
|
+
}
|
|
305
|
+
// Issue #3519: the skip count moves with the hash too, so a rename mid-run
|
|
306
|
+
// does not restart the thinning and log as if a new run had begun.
|
|
307
|
+
// Issue #3552: whenever the hash under `newKey` is replaced, so is its count
|
|
308
|
+
// — a count left over from the hash that was there before is about a
|
|
309
|
+
// different reply (the same rule as `renamePromptHashCacheKey`, #3538).
|
|
310
|
+
if (state.hash === undefined)
|
|
311
|
+
return;
|
|
312
|
+
if (state.streak !== undefined) {
|
|
313
|
+
duplicateResponseSkipStreak.set(key, state.streak);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
duplicateResponseSkipStreak.delete(key);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
236
319
|
/**
|
|
237
320
|
* Move the cached hash from one pollerKey to another.
|
|
238
321
|
*
|
|
@@ -241,7 +324,8 @@ function clearResponseHashCache(pollerKey) {
|
|
|
241
324
|
* during THIS turn?", and the turn does not end just because the ID moved —
|
|
242
325
|
* dropping it would re-save the screen currently on display.
|
|
243
326
|
*
|
|
244
|
-
* No-op when nothing is cached under `oldKey`.
|
|
327
|
+
* No-op when nothing is cached under `oldKey`. A batch of renames whose keys
|
|
328
|
+
* overlap must go through {@link renameResponseHashCacheKeys} instead.
|
|
245
329
|
*
|
|
246
330
|
* @param oldKey - Poller key the cache entry lives under
|
|
247
331
|
* @param newKey - Poller key it should live under
|
|
@@ -249,22 +333,30 @@ function clearResponseHashCache(pollerKey) {
|
|
|
249
333
|
function renameResponseHashCacheKey(oldKey, newKey) {
|
|
250
334
|
if (oldKey === newKey)
|
|
251
335
|
return;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
336
|
+
putResponseDedupState(newKey, takeResponseDedupState(oldKey), false);
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Move the response dedup state of a whole batch of pollerKeys (Issue #3553).
|
|
340
|
+
*
|
|
341
|
+
* Every source key is lifted before any destination is written, so a swap
|
|
342
|
+
* (A→B, B→A) or a chain (A→B→C) cannot overwrite a source that has not moved
|
|
343
|
+
* yet — calling {@link renameResponseHashCacheKey} once per pair would leave
|
|
344
|
+
* A with its own state and B empty after a swap. Each destination ends up with
|
|
345
|
+
* exactly what its source held: hash, skip count and recheck, the recheck
|
|
346
|
+
* moving even without a hash. A recheck already at the destination is
|
|
347
|
+
* replaced along with the hash, never kept beside the moved one.
|
|
348
|
+
*
|
|
349
|
+
* Pairs with `oldKey === newKey` are ignored.
|
|
350
|
+
*
|
|
351
|
+
* @param renames - Poller key moves applied together
|
|
352
|
+
*/
|
|
353
|
+
function renameResponseHashCacheKeys(renames) {
|
|
354
|
+
const lifted = [];
|
|
355
|
+
for (const { oldKey, newKey } of renames) {
|
|
356
|
+
if (oldKey === newKey)
|
|
357
|
+
continue;
|
|
358
|
+
lifted.push({ newKey, state: takeResponseDedupState(oldKey) });
|
|
359
|
+
}
|
|
360
|
+
for (const { newKey, state } of lifted)
|
|
361
|
+
putResponseDedupState(newKey, state, true);
|
|
270
362
|
}
|