commandmate 0.31.1 → 0.31.3
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 +170 -170
- package/.next/app-path-routes-manifest.json +29 -29
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- 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.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route.js +1 -1
- package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route.js +2 -2
- package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js +2 -2
- package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js.nft.json +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.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 +2 -2
- 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 +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.js +2 -2
- package/.next/server/app/api/push/escalation/route.js.nft.json +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route.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.js +2 -2
- package/.next/server/app/api/push/vapid/route.js.nft.json +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.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/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.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_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]/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.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_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_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.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 +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/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 +10 -10
- 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 +29 -29
- package/.next/server/chunks/1058.js +1 -1
- package/.next/server/chunks/1068.js +1 -1
- package/.next/server/chunks/1191.js +2 -2
- package/.next/server/chunks/1886.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/261.js +1 -1
- package/.next/server/chunks/2739.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3484.js +1 -0
- package/.next/server/chunks/358.js +1 -0
- package/.next/server/chunks/4325.js +1 -1
- package/.next/server/chunks/4393.js +1 -1
- package/.next/server/chunks/4443.js +1 -1
- package/.next/server/chunks/5017.js +1 -1
- package/.next/server/chunks/5120.js +1 -1
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/552.js +3 -3
- package/.next/server/chunks/595.js +8 -0
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/8800.js +1 -1
- package/.next/server/chunks/8918.js +3 -3
- package/.next/server/chunks/9123.js +1 -1
- package/.next/server/chunks/9130.js +2 -2
- package/.next/server/chunks/9627.js +3 -3
- package/.next/server/chunks/9696.js +2 -2
- package/.next/server/chunks/9798.js +2 -2
- 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/{1783-11c9b4d8a52ebd91.js → 2813-de885de8a4b11910.js} +1 -1
- package/.next/static/chunks/4718-07ec4e10186928db.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-91b639c90795baa9.js +1 -0
- package/.next/static/css/acc4b9c3feb04287.css +1 -0
- package/.next/trace +13 -13
- package/dist/lib/detection/tools/verified-against.d.ts +24 -24
- package/dist/lib/detection/tools/verified-against.js +24 -24
- package/dist/server/locales/en/worktree.json +1402 -0
- package/dist/server/locales/ja/worktree.json +1402 -0
- package/dist/server/src/lib/db/chat-db.js +44 -1
- package/dist/server/src/lib/detection/cli-patterns.js +157 -23
- package/dist/server/src/lib/detection/model-info-extractor.js +185 -4
- package/dist/server/src/lib/detection/tools/antigravity/detect.js +105 -25
- package/dist/server/src/lib/detection/tools/antigravity/dialog.js +233 -0
- package/dist/server/src/lib/detection/tools/verified-against.js +24 -24
- package/dist/server/src/lib/hooks/hook-settings-generator.js +16 -0
- package/dist/server/src/lib/hooks/sources/claude/model-switch.js +169 -0
- package/dist/server/src/lib/hooks/sources/claude/source.js +14 -1
- package/dist/server/src/lib/polling/response-checker.js +17 -1
- package/dist/server/src/lib/polling/response-poller-core.js +143 -9
- package/dist/server/src/lib/push/index.js +13 -1
- package/dist/server/src/lib/push/model-change-push-notifier.js +174 -0
- package/dist/server/src/lib/push/notification-dedup.js +2 -1
- package/dist/server/src/lib/push/push-sender.js +48 -20
- package/dist/server/src/lib/realtime/model-change-broadcast.js +171 -0
- package/dist/server/src/lib/realtime/types.js +19 -1
- package/dist/server/src/lib/realtime/waiting-broadcast.js +13 -0
- package/dist/server/src/lib/session/agent-event-state.js +344 -12
- package/package.json +1 -1
- package/.next/server/chunks/6894.js +0 -8
- package/.next/server/chunks/7187.js +0 -1
- package/.next/server/chunks/7472.js +0 -1
- package/.next/static/chunks/4718-46e1d2f07c8423ba.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-64d4ec3051971760.js +0 -1
- package/.next/static/css/5c13ea243dfdcb66.css +0 -1
- /package/.next/static/{60mm4_QBuFtFTfo5UMyKU → 0hTIackGXw6v7diXJS6yN}/_buildManifest.js +0 -0
- /package/.next/static/{60mm4_QBuFtFTfo5UMyKU → 0hTIackGXw6v7diXJS6yN}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code's `PostModelSwitch` hook, as CommandMate reads it (Issue #2363).
|
|
4
|
+
*
|
|
5
|
+
* #2361 detected a `/model` switch off the pane, because the seven events
|
|
6
|
+
* CommandMate registered never mentioned it: Claude names its model on
|
|
7
|
+
* `SessionStart` and on nothing else. The same probe found the build emitting
|
|
8
|
+
* a pair of dedicated hooks, and this module is the structured half of the
|
|
9
|
+
* answer — the frame reader stays as the fallback (see
|
|
10
|
+
* `lib/session/agent-event-state`'s `FRAME_OVERTAKES_HOOK_MODEL_TOOLS`).
|
|
11
|
+
*
|
|
12
|
+
* ## Measured on claude 2.1.263 (2026-09-06, isolated `CLAUDE_CONFIG_DIR`)
|
|
13
|
+
*
|
|
14
|
+
* Re-taken for this Issue rather than inherited from #2361, whose numbers were
|
|
15
|
+
* a by-product. Payload, delivered verbatim by a `type: "http"` hook injected
|
|
16
|
+
* through `--settings` — the same channel every other event here uses:
|
|
17
|
+
*
|
|
18
|
+
* ```json
|
|
19
|
+
* {
|
|
20
|
+
* "session_id": "…", "transcript_path": "…", "cwd": "…", "prompt_id": "…",
|
|
21
|
+
* "hook_event_name": "PostModelSwitch",
|
|
22
|
+
* "from_model": "claude-haiku-4-5-20251001",
|
|
23
|
+
* "to_model": "claude-sonnet-5",
|
|
24
|
+
* "requested_model": "sonnet",
|
|
25
|
+
* "source": "command",
|
|
26
|
+
* "context_tokens": 0, "prompt_cache_warm": false, "cache_ttl": "5m",
|
|
27
|
+
* "estimated_cache_write_usd": 0, "pricing": "catalog"
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* - **`to_model` is the model id in the `SessionStart` spelling** —
|
|
32
|
+
* `claude-sonnet-5`, `claude-haiku-4-5-20251001`, `claude-opus-5`,
|
|
33
|
+
* `claude-opus-5[1m]` — so the latch in `agent-event-state` compares like
|
|
34
|
+
* with like, and the `[1m]` suffix travels with it.
|
|
35
|
+
* - **`/fast` is a switch**: ON from Haiku reported `to_model:
|
|
36
|
+
* "claude-opus-5[1m]"`, `requested_model: null`, `source: "command"`. That
|
|
37
|
+
* is the fix for the spelling drift #2361 refused to read (the confirmation
|
|
38
|
+
* line says `Opus 5`, the banner `Opus 5 (1M context)`): the hook spells it
|
|
39
|
+
* the one way. `/fast` OFF fires nothing and the model stays on Opus.
|
|
40
|
+
* - **`requested_model` is null** for `/model default` (→ `claude-opus-5[1m]`)
|
|
41
|
+
* and for `/fast`, and is the user's alias (`sonnet`) otherwise. Never read.
|
|
42
|
+
* - **`source`** is `command` for `/model <arg>` and `/fast`, `picker` for the
|
|
43
|
+
* interactive list (measured with `s`). The schema also names `sdk`, `auto`
|
|
44
|
+
* and `resume`; none were reproducible. Not read.
|
|
45
|
+
* - **Nothing fires when the model does not change**: `/model haiku` while on
|
|
46
|
+
* Haiku, `/fast` while already on Opus 1M, the picker's `Esc` (`Kept model
|
|
47
|
+
* as`), and `/effort` all stay silent. `/clear` emits its usual
|
|
48
|
+
* `SessionEnd` + `SessionStart` (no `model` key) and no switch.
|
|
49
|
+
* - **There is no `model` key on this event.** The generic `modelFields:
|
|
50
|
+
* ['model']` lookup answers null for it, which is why the extraction below
|
|
51
|
+
* exists rather than a second entry in that list.
|
|
52
|
+
*
|
|
53
|
+
* ## Why `from_model` is never read
|
|
54
|
+
*
|
|
55
|
+
* It is the model the session was on BEFORE the switch. Reading it as "the
|
|
56
|
+
* model" would move the latch backwards on the very event that reports a
|
|
57
|
+
* forward move. It is also the field the paired `PreModelSwitch` got wrong:
|
|
58
|
+
* in the probe's second session that hook fired **twice per switch**, and on
|
|
59
|
+
* `/fast` the second of the two named `from_model: "claude-sonnet-5"` for a
|
|
60
|
+
* session that was on Haiku (`PostModelSwitch` fired once, and agreed with
|
|
61
|
+
* the screen, every time).
|
|
62
|
+
*
|
|
63
|
+
* ## Why `PreModelSwitch` is not registered
|
|
64
|
+
*
|
|
65
|
+
* Three measured reasons. It is a decision hook — its response body can block
|
|
66
|
+
* or redirect the switch — and the event receiver answers a fixed 202 that
|
|
67
|
+
* decides nothing, so registering it would put a blocking round-trip (5 s at
|
|
68
|
+
* worst, on a wedged server) in front of every `/model` for no information.
|
|
69
|
+
* It fired twice per switch with inconsistent values (above). And its
|
|
70
|
+
* `to_model` is the request, not the outcome — the second of that pair on
|
|
71
|
+
* `/model haiku` said `to_model: "claude-sonnet-5"`; `PostModelSwitch` alone
|
|
72
|
+
* is the event that says what happened. The Issue leaves this to
|
|
73
|
+
* implementation judgement, and this is it.
|
|
74
|
+
*
|
|
75
|
+
* ## Which of the seven words
|
|
76
|
+
*
|
|
77
|
+
* `notification`, with a subtype of its own ({@link modelSwitchDetail}). A
|
|
78
|
+
* model switch is the agent telling the human something and is neither a turn
|
|
79
|
+
* boundary nor a tool call; `agent-event-state` treats a `notification` whose
|
|
80
|
+
* subtype it does not recognise as evidence of nothing (no status, no dialog,
|
|
81
|
+
* no turn), and `status-mapping` answers null for it — measured behaviour, not
|
|
82
|
+
* a hope. The one thing every event does is run the model latch, and that is
|
|
83
|
+
* the whole point of delivering this one.
|
|
84
|
+
*
|
|
85
|
+
* @module lib/hooks/sources/claude/model-switch
|
|
86
|
+
*/
|
|
87
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
88
|
+
exports.claudeModelSwitchMapper = exports.MODEL_SWITCH_DETAIL = exports.CLAUDE_PRE_MODEL_SWITCH_EVENT_NAME = exports.CLAUDE_POST_MODEL_SWITCH_EVENT_NAME = void 0;
|
|
89
|
+
exports.modelSwitchDetail = modelSwitchDetail;
|
|
90
|
+
exports.isModelSwitchDetail = isModelSwitchDetail;
|
|
91
|
+
exports.isClaudeModelSwitchPayload = isClaudeModelSwitchPayload;
|
|
92
|
+
exports.extractClaudeSwitchedModel = extractClaudeSwitchedModel;
|
|
93
|
+
const event_mapper_1 = require("../event-mapper");
|
|
94
|
+
/** Claude's own name for the event; the key written into `--settings`. */
|
|
95
|
+
exports.CLAUDE_POST_MODEL_SWITCH_EVENT_NAME = 'PostModelSwitch';
|
|
96
|
+
/**
|
|
97
|
+
* The paired decision hook, which is deliberately NOT registered — see the
|
|
98
|
+
* module comment. Named here so the test that pins its absence from the
|
|
99
|
+
* injected settings spells it the way this module does.
|
|
100
|
+
*/
|
|
101
|
+
exports.CLAUDE_PRE_MODEL_SWITCH_EVENT_NAME = 'PreModelSwitch';
|
|
102
|
+
/**
|
|
103
|
+
* The subtype a switch is filed under (Issue #2363).
|
|
104
|
+
*
|
|
105
|
+
* A word of CommandMate's own, beside Claude's `permission_prompt` /
|
|
106
|
+
* `idle_prompt` on the same event. Nothing branches on it — the model rides on
|
|
107
|
+
* the normalised event's `model`, not on the subtype — but two things read it:
|
|
108
|
+
* `structuredEvents.lastEventDetail` shows it, and the receiver's
|
|
109
|
+
* de-duplication key carries it. See {@link modelSwitchDetail} for why the
|
|
110
|
+
* target model is appended.
|
|
111
|
+
*/
|
|
112
|
+
exports.MODEL_SWITCH_DETAIL = 'model_switch';
|
|
113
|
+
/** The payload key that names the model the session is now on. */
|
|
114
|
+
const TO_MODEL_FIELD = 'to_model';
|
|
115
|
+
/**
|
|
116
|
+
* `model_switch:<to_model>`, or the bare word when the switch names no target.
|
|
117
|
+
*
|
|
118
|
+
* The target is part of the subtype because the subtype is part of the
|
|
119
|
+
* receiver's de-duplication key: `(instance, event, detail, session_id)` within
|
|
120
|
+
* a 3 s window (`isDuplicateAgentEvent`). With a fixed word, two switches to
|
|
121
|
+
* DIFFERENT models inside that window — the picker's `s` followed by `/fast`,
|
|
122
|
+
* a `/model` typed twice to correct itself — would collapse into one and the
|
|
123
|
+
* second target would be lost until the frame caught up. With the target in
|
|
124
|
+
* the key, only a second delivery of the SAME switch collapses, which is the
|
|
125
|
+
* case the window exists for (a user's own `PostModelSwitch` hook is
|
|
126
|
+
* concatenated with the injected one, and both name the same `to_model`).
|
|
127
|
+
*/
|
|
128
|
+
function modelSwitchDetail(toModel) {
|
|
129
|
+
return toModel === null ? exports.MODEL_SWITCH_DETAIL : `${exports.MODEL_SWITCH_DETAIL}:${toModel}`;
|
|
130
|
+
}
|
|
131
|
+
/** Whether a recorded `detail` is one this module wrote, target or no target. */
|
|
132
|
+
function isModelSwitchDetail(detail) {
|
|
133
|
+
return detail === exports.MODEL_SWITCH_DETAIL || (detail?.startsWith(`${exports.MODEL_SWITCH_DETAIL}:`) ?? false);
|
|
134
|
+
}
|
|
135
|
+
/** True for the one native event this module speaks for. */
|
|
136
|
+
function isClaudeModelSwitchPayload(payload) {
|
|
137
|
+
return (0, event_mapper_1.readStringField)(payload, 'hook_event_name') === exports.CLAUDE_POST_MODEL_SWITCH_EVENT_NAME;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The rule that maps `PostModelSwitch` onto `notification(model_switch:<to_model>)`.
|
|
141
|
+
*
|
|
142
|
+
* Appended after the shared CamelCase table in `./source`, so every spelling
|
|
143
|
+
* that table already claims keeps its word.
|
|
144
|
+
*/
|
|
145
|
+
const claudeModelSwitchMapper = (nativeEventName, payload) => nativeEventName === exports.CLAUDE_POST_MODEL_SWITCH_EVENT_NAME
|
|
146
|
+
? {
|
|
147
|
+
event: 'notification',
|
|
148
|
+
detail: modelSwitchDetail((0, event_mapper_1.readStringField)(payload, TO_MODEL_FIELD)),
|
|
149
|
+
}
|
|
150
|
+
: null;
|
|
151
|
+
exports.claudeModelSwitchMapper = claudeModelSwitchMapper;
|
|
152
|
+
/**
|
|
153
|
+
* The model a `PostModelSwitch` reports, or null for any other payload.
|
|
154
|
+
*
|
|
155
|
+
* `to_model` and only `to_model`: `from_model` is the value being left behind
|
|
156
|
+
* and `requested_model` is an alias that is null for `/model default` and
|
|
157
|
+
* `/fast`. Answers null for a switch that somehow carries no `to_model`, which
|
|
158
|
+
* the caller (`buildNormalizedEvent`) turns into "this event names no model"
|
|
159
|
+
* — the latch is left where it was rather than blanked or reversed.
|
|
160
|
+
*
|
|
161
|
+
* Declared as the source's `extractModel`, which runs before the flat
|
|
162
|
+
* `modelFields` lookup; for every other event it answers null and the lookup
|
|
163
|
+
* proceeds exactly as it did before this Issue.
|
|
164
|
+
*/
|
|
165
|
+
function extractClaudeSwitchedModel(payload) {
|
|
166
|
+
if (!isClaudeModelSwitchPayload(payload))
|
|
167
|
+
return null;
|
|
168
|
+
return (0, event_mapper_1.readStringField)(payload, TO_MODEL_FIELD);
|
|
169
|
+
}
|
|
@@ -41,6 +41,7 @@ const permission_request_payload_1 = require("../../../../lib/hooks/permission-r
|
|
|
41
41
|
const define_source_1 = require("../define-source");
|
|
42
42
|
const event_mapper_1 = require("../event-mapper");
|
|
43
43
|
const hook_event_vocabulary_1 = require("../hook-event-vocabulary");
|
|
44
|
+
const model_switch_1 = require("./model-switch");
|
|
44
45
|
const tool_id_1 = require("./tool-id");
|
|
45
46
|
/**
|
|
46
47
|
* The no-decision body, byte for byte what the #1721 spike returned for D5.
|
|
@@ -129,7 +130,12 @@ exports.claudeAgentEventSource = (0, define_source_1.definePushHookSource)({
|
|
|
129
130
|
// word, no predicates — so it is expressed as rules built from the table
|
|
130
131
|
// rather than as a table, and a source that needs conditions (opencode) adds
|
|
131
132
|
// them to the same list.
|
|
132
|
-
|
|
133
|
+
//
|
|
134
|
+
// Issue #2363: plus the one spelling that is Claude's alone. `PostModelSwitch`
|
|
135
|
+
// is filed as `notification(model_switch:<to_model>)` — see `./model-switch`
|
|
136
|
+
// for the measurement and for why its `Pre` twin is not here. Appended after
|
|
137
|
+
// the shared table so nothing that table claims changes its word.
|
|
138
|
+
mappers: [...(0, event_mapper_1.fromNameTable)(hook_event_vocabulary_1.CAMEL_CASE_HOOK_EVENT_NAMES), model_switch_1.claudeModelSwitchMapper],
|
|
133
139
|
nativeEventNameFields: ['hook_event_name'],
|
|
134
140
|
conversationIdFields: hook_event_vocabulary_1.SESSION_ID_FIELDS,
|
|
135
141
|
toolCallIdFields: hook_event_vocabulary_1.TOOL_CALL_ID_FIELDS,
|
|
@@ -140,6 +146,13 @@ exports.claudeAgentEventSource = (0, define_source_1.definePushHookSource)({
|
|
|
140
146
|
// does that. The value arrives with its own suffix (`claude-opus-5[1m]`) and
|
|
141
147
|
// is not parsed here: a display string the tool chose is the tool's to spell.
|
|
142
148
|
modelFields: ['model'],
|
|
149
|
+
// Issue #2363. The second and only other event that names a model, and it
|
|
150
|
+
// names it under a different key: `PostModelSwitch` carries `to_model` (the
|
|
151
|
+
// model the session is now on) and no `model` at all. Read there, and only
|
|
152
|
+
// there — `from_model` is the value being left behind and must never reach
|
|
153
|
+
// the latch. `extractModel` runs before `modelFields`, and answers null for
|
|
154
|
+
// every other event so the flat lookup above is unchanged for them.
|
|
155
|
+
extractModel: model_switch_1.extractClaudeSwitchedModel,
|
|
143
156
|
// S2.
|
|
144
157
|
extractDetail: hook_event_vocabulary_1.extractSnakeCaseEventDetail,
|
|
145
158
|
// S7. Both parsers are strict and answer null for anything they cannot vouch
|
|
@@ -11,6 +11,7 @@ const db_instance_1 = require("../../lib/db/db-instance");
|
|
|
11
11
|
const db_1 = require("../../lib/db");
|
|
12
12
|
const ws_server_1 = require("../../lib/ws-server");
|
|
13
13
|
const prompt_detector_1 = require("../../lib/detection/prompt-detector");
|
|
14
|
+
const dialog_1 = require("../../lib/detection/tools/antigravity/dialog");
|
|
14
15
|
const conversation_logger_1 = require("../../lib/conversation-logger");
|
|
15
16
|
const types_1 = require("../../lib/cli-tools/types");
|
|
16
17
|
const claude_output_1 = require("../../lib/claude-output");
|
|
@@ -190,8 +191,23 @@ function buildPromptExtractionResult(lines, lastCapturedLine, totalLines, buffer
|
|
|
190
191
|
* @returns PromptDetectionResult with isPrompt, promptData, and cleanContent
|
|
191
192
|
*/
|
|
192
193
|
function detectPromptWithOptions(output, cliToolId) {
|
|
194
|
+
const clean = (0, cli_patterns_1.stripBoxDrawing)((0, cli_patterns_1.stripAnsi)(output));
|
|
195
|
+
// Issue #2364: agy's `↑/↓ Navigate` dialogs are read by agy's own reader
|
|
196
|
+
// before the generic pass, on the same spelling `tools/antigravity/detect.ts`
|
|
197
|
+
// reads them on. The generic multiple-choice parser takes one row per option
|
|
198
|
+
// and agy wraps a long command across several rows of one label, so without
|
|
199
|
+
// this the poller stored nothing for the frame the status API published as a
|
|
200
|
+
// prompt — and, on the file-creation menu, stored a question with the diff
|
|
201
|
+
// preview joined into it. One reader for both producers is what makes the
|
|
202
|
+
// stored `prompt` row, the push notification's excerpt and `/current-output`
|
|
203
|
+
// agree about one screen.
|
|
204
|
+
if (cliToolId === 'antigravity') {
|
|
205
|
+
const dialog = (0, dialog_1.detectAntigravityNumberedDialogPrompt)(clean);
|
|
206
|
+
if (dialog !== null)
|
|
207
|
+
return dialog;
|
|
208
|
+
}
|
|
193
209
|
const promptOptions = (0, cli_patterns_1.buildDetectPromptOptions)(cliToolId);
|
|
194
|
-
return (0, prompt_detector_1.detectPrompt)(
|
|
210
|
+
return (0, prompt_detector_1.detectPrompt)(clean, promptOptions);
|
|
195
211
|
}
|
|
196
212
|
// ============================================================================
|
|
197
213
|
// extractResponse (internal)
|
|
@@ -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
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** Web Push module barrel (Issue #1125). */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.notifyPromptResolved = exports.decidePromptResolution = exports.MAX_ESCALATION_THRESHOLD_MINUTES = exports.MIN_ESCALATION_THRESHOLD_MINUTES = exports.ESCALATION_SETTINGS_KEY = exports.ESCALATION_THRESHOLD_CHOICES = exports.DEFAULT_ESCALATION_SETTINGS = exports.normalizeEscalationSettings = exports.setPushEscalationSettings = exports.getPushEscalationSettings = exports.ESCALATION_TICK_MS = exports.pendingEscalationCount = exports.runEscalationTick = exports.handleWaitingTransition = exports.isWaitingPushNotifierActive = exports.stopWaitingPushNotifier = exports.startWaitingPushNotifier = exports.UPSTREAM_FAULT_COOLDOWN_MS = exports.clearUpstreamFaultEpisodes = exports.observeUpstreamFaultEdge = exports.notifySessionStartFailurePush = exports.notifyUpstreamFaultPush = exports.notifyVerificationFailurePush = exports.isPromptPushSuppressed = exports.decidePromptPush = exports.resetWaitingPushDedup = exports.shouldSendWaitingPush = exports.DEFAULT_DEDUP_WINDOW_MS = exports.resetNotificationDedup = exports.shouldSendNotification = exports.resolvePushLocale = exports.buildExcerpt = exports.buildPushPayload = exports.notifyPushSubscribers = exports.PUSH_DELIVERY_HEALTH_MAX_AGE_MS = exports.PUSH_DELIVERY_HEALTH_KEY_PREFIX = exports.clearAllPushDeliveryHealth = exports.clearPushDeliveryHealth = exports.recordPushDeliveryFailure = exports.getPushDeliveryHealth = exports.VAPID_ENV_KEYS = exports.VAPID_DEFAULT_SUBJECT = exports.extractVapidSubjectHost = exports.classifyVapidSubject = exports.runVapidSelfCheck = exports.formatVapidReportLines = exports.inspectVapidConfig = exports.getVapidPublicKey = exports.isPushConfigured = exports.getVapidConfig = void 0;
|
|
5
|
-
exports.PROMPT_CARD_MAX_AGE_MS = exports.PROMPT_CARD_KEY_PREFIX = exports.promptCardCount = exports.forgetPromptCardMemory = exports.clearAllPromptCards = exports.clearPromptCard = exports.hasPromptCard = exports.markPromptCardShown = exports.MIN_DEVICES_FOR_CROSS_DEVICE_CLEAR = void 0;
|
|
5
|
+
exports.MODEL_CHANGE_TAG_SUFFIX = exports.resolveReadersLocale = exports.buildModelChangedBodies = exports.buildModelChangedSentence = exports.notifyModelChangePush = exports.PROMPT_CARD_MAX_AGE_MS = exports.PROMPT_CARD_KEY_PREFIX = exports.promptCardCount = exports.forgetPromptCardMemory = exports.clearAllPromptCards = exports.clearPromptCard = exports.hasPromptCard = exports.markPromptCardShown = exports.MIN_DEVICES_FOR_CROSS_DEVICE_CLEAR = void 0;
|
|
6
6
|
var vapid_1 = require("./vapid");
|
|
7
7
|
Object.defineProperty(exports, "getVapidConfig", { enumerable: true, get: function () { return vapid_1.getVapidConfig; } });
|
|
8
8
|
Object.defineProperty(exports, "isPushConfigured", { enumerable: true, get: function () { return vapid_1.isPushConfigured; } });
|
|
@@ -93,3 +93,15 @@ Object.defineProperty(exports, "forgetPromptCardMemory", { enumerable: true, get
|
|
|
93
93
|
Object.defineProperty(exports, "promptCardCount", { enumerable: true, get: function () { return prompt_card_state_1.promptCardCount; } });
|
|
94
94
|
Object.defineProperty(exports, "PROMPT_CARD_KEY_PREFIX", { enumerable: true, get: function () { return prompt_card_state_1.PROMPT_CARD_KEY_PREFIX; } });
|
|
95
95
|
Object.defineProperty(exports, "PROMPT_CARD_MAX_AGE_MS", { enumerable: true, get: function () { return prompt_card_state_1.PROMPT_CARD_MAX_AGE_MS; } });
|
|
96
|
+
// The model edge's push (Issue #2357).
|
|
97
|
+
// NOTE: `realtime/model-change-broadcast` imports these from
|
|
98
|
+
// './model-change-push-notifier' directly, for the reason every block above
|
|
99
|
+
// gives — a suite that stubs the whole '@/lib/push' barrel would otherwise
|
|
100
|
+
// leave the notifier undefined on the path it guards.
|
|
101
|
+
var model_change_push_notifier_1 = require("./model-change-push-notifier");
|
|
102
|
+
Object.defineProperty(exports, "notifyModelChangePush", { enumerable: true, get: function () { return model_change_push_notifier_1.notifyModelChangePush; } });
|
|
103
|
+
Object.defineProperty(exports, "buildModelChangedSentence", { enumerable: true, get: function () { return model_change_push_notifier_1.buildModelChangedSentence; } });
|
|
104
|
+
Object.defineProperty(exports, "buildModelChangedBodies", { enumerable: true, get: function () { return model_change_push_notifier_1.buildModelChangedBodies; } });
|
|
105
|
+
Object.defineProperty(exports, "resolveReadersLocale", { enumerable: true, get: function () { return model_change_push_notifier_1.resolveReadersLocale; } });
|
|
106
|
+
var push_sender_2 = require("./push-sender");
|
|
107
|
+
Object.defineProperty(exports, "MODEL_CHANGE_TAG_SUFFIX", { enumerable: true, get: function () { return push_sender_2.MODEL_CHANGE_TAG_SUFFIX; } });
|