commandmate 0.47.2 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +180 -180
- package/.next/app-path-routes-manifest.json +30 -30
- 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/release-notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route.js +3 -3
- 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 +2 -2
- 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 +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.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 +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 +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 +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 +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]/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_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 +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 +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route.js.nft.json +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]/prompt-response/strict/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/strict/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/strict/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 +3 -3
- 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 -103
- 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.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.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_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.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_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.js +2 -2
- 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 -12
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +30 -30
- package/.next/server/chunks/1191.js +11 -4
- package/.next/server/chunks/1501.js +1 -1
- package/.next/server/chunks/1601.js +2 -2
- package/.next/server/chunks/1656.js +2 -2
- package/.next/server/chunks/1945.js +3 -3
- package/.next/server/chunks/1990.js +103 -0
- package/.next/server/chunks/2069.js +1 -0
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2422.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2534.js +1 -1
- package/.next/server/chunks/255.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3505.js +2 -2
- package/.next/server/chunks/4243.js +1 -1
- package/.next/server/chunks/4248.js +1 -1
- package/.next/server/chunks/4280.js +1 -0
- package/.next/server/chunks/5209.js +1 -1
- package/.next/server/chunks/6119.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6486.js +9 -8
- package/.next/server/chunks/6781.js +1 -1
- package/.next/server/chunks/720.js +103 -0
- package/.next/server/chunks/7604.js +2 -0
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8581.js +8 -8
- package/.next/server/chunks/8728.js +3 -3
- package/.next/server/chunks/8791.js +1 -1
- package/.next/server/chunks/8807.js +19 -18
- package/.next/server/chunks/8918.js +2 -104
- package/.next/server/chunks/936.js +1 -1
- package/.next/server/chunks/9390.js +1 -1
- package/.next/server/chunks/9540.js +1 -1
- package/.next/server/chunks/9627.js +5 -5
- package/.next/server/chunks/9731.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/1128-914c559913546bbc.js +1 -0
- package/.next/static/chunks/{2407-a6533805a86d05bd.js → 2407-ab92ae9837c39dce.js} +1 -1
- package/.next/static/chunks/{2863-6a07ec82fb52f4dd.js → 2863-7173dc305dffc4f6.js} +1 -1
- package/.next/static/chunks/6288-6fadbba6e84393b2.js +1 -0
- package/.next/static/chunks/7665-e9ef6e0a85b9fa49.js +1 -0
- package/.next/static/chunks/8407-35bbfc84793d7004.js +1 -0
- package/.next/static/chunks/9079-987939d231704218.js +1 -0
- package/.next/static/chunks/app/{layout-538d8d2746ad1588.js → layout-aa8e66164a4367af.js} +1 -1
- package/.next/static/chunks/app/sessions/page-cfd645305e25fd33.js +1 -0
- package/.next/static/chunks/app/skills/[skillId]/{page-52ba58712faf3378.js → page-cb6e74fd9a6a571d.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-704086656fb45699.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-a8d7886703ffc7b4.js +1 -0
- package/.next/static/css/{743f79ff19ab91e9.css → db8959555c3d9c96.css} +1 -1
- package/.next/trace +13 -13
- package/dist/cli/commands/auto-yes.d.ts +24 -0
- package/dist/cli/commands/auto-yes.d.ts.map +1 -1
- package/dist/cli/commands/auto-yes.js +76 -8
- package/dist/cli/commands/capture.d.ts.map +1 -1
- package/dist/cli/commands/capture.js +5 -0
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +10 -1
- package/dist/cli/commands/prompt.d.ts.map +1 -1
- package/dist/cli/commands/prompt.js +5 -3
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +19 -6
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +8 -1
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +101 -13
- package/dist/cli/config/duration-constants.d.ts +16 -13
- package/dist/cli/config/duration-constants.d.ts.map +1 -1
- package/dist/cli/config/duration-constants.js +21 -21
- package/dist/cli/docs/agent-operations.d.ts +2 -2
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +29 -1
- package/dist/cli/types/api-responses.d.ts +145 -3
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +4 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/resource-resolvers.d.ts +0 -18
- package/dist/cli/utils/resource-resolvers.d.ts.map +1 -1
- package/dist/cli/utils/resource-resolvers.js +1 -51
- package/dist/cli/utils/verify-runner.d.ts +24 -1
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +54 -1
- package/dist/config/auto-yes-duration.d.ts +56 -0
- package/dist/config/auto-yes-duration.d.ts.map +1 -0
- package/dist/config/auto-yes-duration.js +76 -0
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts +54 -2
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -1
- package/dist/lib/polling/auto-yes-suppression-reason.js +33 -2
- package/dist/lib/prompt-observation/types.d.ts +13 -0
- package/dist/lib/prompt-observation/types.d.ts.map +1 -1
- package/dist/server/locales/en/worktree.json +2 -1
- package/dist/server/locales/ja/worktree.json +2 -1
- package/dist/server/src/config/auto-yes-config.js +14 -19
- package/dist/server/src/config/auto-yes-duration.js +76 -0
- package/dist/server/src/lib/auto-yes-poller-answer-target.js +38 -0
- package/dist/server/src/lib/auto-yes-poller-keys.js +35 -0
- package/dist/server/src/lib/auto-yes-poller-session-presence.js +26 -0
- package/dist/server/src/lib/auto-yes-poller-tick.js +233 -0
- package/dist/server/src/lib/auto-yes-poller.js +337 -376
- package/dist/server/src/lib/auto-yes-state.js +59 -4
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +235 -19
- package/dist/server/src/lib/db/chat-db.js +5 -1
- package/dist/server/src/lib/db/verification-db.js +23 -2
- package/dist/server/src/lib/detection/composer-text.js +215 -11
- package/dist/server/src/lib/detection/option-recommendation.js +120 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice-approval-context.js +290 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice-chrome.js +193 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +402 -539
- package/dist/server/src/lib/detection/prompt-detector.js +92 -21
- package/dist/server/src/lib/detection/prompt-screen-key.js +60 -0
- package/dist/server/src/lib/detection/tools/antigravity/dialog.js +158 -35
- package/dist/server/src/lib/detection/tools/claude/detect.js +111 -10
- package/dist/server/src/lib/detection/tools/command-code/dialog.js +67 -0
- package/dist/server/src/lib/detection/tools/dialog-block.js +16 -0
- package/dist/server/src/lib/detection/upstream-faults.js +19 -2
- package/dist/server/src/lib/detection/waiting-screen-reading.js +98 -0
- package/dist/server/src/lib/hooks/permission-decision-service.js +7 -0
- package/dist/server/src/lib/hooks/structured-decision-response.js +1 -1
- package/dist/server/src/lib/polling/auto-yes-confirm-page.js +2 -2
- package/dist/server/src/lib/polling/auto-yes-confirmation-ledger-types.js +3 -0
- package/dist/server/src/lib/polling/auto-yes-confirmation-ledger.js +34 -3
- package/dist/server/src/lib/polling/auto-yes-decision-state.js +195 -0
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +139 -10
- package/dist/server/src/lib/polling/auto-yes-enter-fallback.js +14 -0
- package/dist/server/src/lib/polling/auto-yes-manual-answer.js +124 -0
- package/dist/server/src/lib/polling/auto-yes-option-effect.js +157 -0
- package/dist/server/src/lib/polling/auto-yes-policy.js +11 -5
- package/dist/server/src/lib/polling/auto-yes-poller-capture.js +15 -2
- package/dist/server/src/lib/polling/auto-yes-poller-generation.js +42 -1
- package/dist/server/src/lib/polling/auto-yes-poller-receipt-log.js +42 -5
- package/dist/server/src/lib/polling/auto-yes-poller-state.js +147 -49
- package/dist/server/src/lib/polling/auto-yes-poller-steps.js +172 -2
- package/dist/server/src/lib/polling/auto-yes-prompt-suppression.js +2 -2
- package/dist/server/src/lib/polling/auto-yes-question-choice.js +57 -0
- package/dist/server/src/lib/polling/auto-yes-resolver.js +96 -21
- package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +33 -2
- package/dist/server/src/lib/polling/auto-yes-suppression-state.js +25 -1
- package/dist/server/src/lib/polling/prompt-screen-reading.js +232 -0
- package/dist/server/src/lib/polling/response-checker-dialog-gate.js +39 -0
- package/dist/server/src/lib/polling/response-checker.js +145 -58
- package/dist/server/src/lib/prompt-answer-sender.js +104 -10
- package/dist/server/src/lib/push/notification-dedup.js +15 -2
- package/dist/server/src/lib/push/prompt-card-state.js +140 -19
- package/dist/server/src/lib/push/prompt-push-gate.js +158 -12
- package/dist/server/src/lib/push/push-sender.js +23 -3
- package/dist/server/src/lib/push/resolution-push-notifier.js +14 -0
- package/dist/server/src/lib/push/waiting-push-notifier.js +139 -17
- package/dist/server/src/lib/realtime/terminal-broadcast.js +16 -0
- package/dist/server/src/lib/session/agent-event-state.js +25 -5
- package/dist/server/src/lib/session/agent-event-turn.js +140 -46
- package/dist/server/src/lib/session/cli-session.js +21 -2
- package/dist/server/src/lib/session/composer-clear.js +56 -4
- package/dist/server/src/lib/session/current-output-builder.js +66 -12
- package/dist/server/src/lib/session/send-attempt-state.js +24 -7
- package/dist/server/src/lib/session/waiting-episode-binding-observation.js +289 -0
- package/dist/server/src/lib/session/waiting-episode-binding-resolve.js +38 -0
- package/dist/server/src/lib/session/waiting-episode-binding.js +276 -0
- package/dist/server/src/lib/session/waiting-episode-state-ops.js +152 -0
- package/dist/server/src/lib/session/waiting-episode-state-store.js +202 -0
- package/dist/server/src/lib/session/waiting-episode-state-types.js +6 -0
- package/dist/server/src/lib/session/waiting-episode-state.js +68 -91
- package/dist/server/src/lib/session/waiting-episode-verdict.js +174 -0
- package/dist/server/src/lib/tmux/tmux-capture-cache.js +76 -11
- package/dist/server/src/lib/tmux/tmux.js +79 -9
- package/dist/server/src/lib/verification/gate-runner.js +172 -16
- package/dist/server/src/lib/verification/machine-lock.js +5 -0
- package/dist/server/src/lib/ws-server.js +18 -3
- package/dist/server/src/types/auto-yes-answer-mode.js +38 -0
- package/dist/server/src/types/auto-yes-decision.js +61 -0
- package/dist/server/src/types/auto-yes.js +6 -0
- package/dist/server/src/types/prompt-reading.js +32 -0
- package/dist/types/auto-yes-answer-mode.d.ts +35 -0
- package/dist/types/auto-yes-answer-mode.d.ts.map +1 -0
- package/dist/types/auto-yes-answer-mode.js +38 -0
- package/docs/en/user-guide/webapp-guide.md +47 -13
- package/package.json +1 -1
- package/public/sw.js +76 -4
- package/release-notes/0.48.0.json +57 -0
- package/release-notes/0.49.0.json +45 -0
- package/.next/server/chunks/1559.js +0 -2
- package/.next/server/chunks/2460.js +0 -1
- package/.next/server/chunks/5595.js +0 -1
- package/.next/server/chunks/610.js +0 -1
- package/.next/server/chunks/6722.js +0 -1
- package/.next/static/chunks/5371-a549a0d2c2044d41.js +0 -1
- package/.next/static/chunks/602-1c1488959215e140.js +0 -1
- package/.next/static/chunks/6288-09feba367cc2c65e.js +0 -1
- package/.next/static/chunks/8407-155f84e5ba05f1d5.js +0 -1
- package/.next/static/chunks/9079-24f9fa549ed298f1.js +0 -1
- package/.next/static/chunks/app/sessions/page-e02f1adb1d1c1ccc.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9cf1ecc59e36a004.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-46aced20b2b1c010.js +0 -1
- /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → 3_JtAZaKMJEDFIIfkHv4n}/_buildManifest.js +0 -0
- /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → 3_JtAZaKMJEDFIIfkHv4n}/_ssgManifest.js +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides functions to manage tmux sessions for Claude CLI integration
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.DIRECT_INPUT_TEXT_CHUNK_BYTES = exports.REPAINT_INVALIDATE_DELAY_MS = exports.NAVIGATION_KEY_VALUES = exports.SPECIAL_KEY_VALUES = exports.SpecialKeysAbortedError = void 0;
|
|
7
|
+
exports.DIRECT_INPUT_TEXT_CHUNK_BYTES = exports.REPAINT_INVALIDATE_DELAY_MS = exports.NAVIGATION_KEY_VALUES = exports.SPECIAL_KEY_VALUES = exports.SpecialKeysAbortedError = exports.TmuxSendError = void 0;
|
|
8
8
|
exports.exactTarget = exactTarget;
|
|
9
9
|
exports.reconcileSessionGeometry = reconcileSessionGeometry;
|
|
10
10
|
exports.hasSession = hasSession;
|
|
@@ -14,6 +14,7 @@ exports.getPaneCurrentCommand = getPaneCurrentCommand;
|
|
|
14
14
|
exports.listSessions = listSessions;
|
|
15
15
|
exports.createSession = createSession;
|
|
16
16
|
exports.sendKeys = sendKeys;
|
|
17
|
+
exports.deliveryOfExecFailure = deliveryOfExecFailure;
|
|
17
18
|
exports.isSendableSpecialKey = isSendableSpecialKey;
|
|
18
19
|
exports.sendSpecialKeys = sendSpecialKeys;
|
|
19
20
|
exports.capturePane = capturePane;
|
|
@@ -23,6 +24,7 @@ exports.ensureSession = ensureSession;
|
|
|
23
24
|
exports.sendSpecialKey = sendSpecialKey;
|
|
24
25
|
exports.clearInputLine = clearInputLine;
|
|
25
26
|
exports.clearComposerLine = clearComposerLine;
|
|
27
|
+
exports.clearComposerPasteToken = clearComposerPasteToken;
|
|
26
28
|
exports.isAllowedSpecialKey = isAllowedSpecialKey;
|
|
27
29
|
exports.sendSpecialKeysAndInvalidate = sendSpecialKeysAndInvalidate;
|
|
28
30
|
exports.sendDirectInputAndInvalidate = sendDirectInputAndInvalidate;
|
|
@@ -462,8 +464,9 @@ async function createSession(sessionNameOrOptions, cwd) {
|
|
|
462
464
|
* @param sendEnter - Whether to send Enter key after the command (default: true)
|
|
463
465
|
* @param options - {@link SendKeysOptions}; pass `{ literal: true }` for text
|
|
464
466
|
*
|
|
465
|
-
* @throws {
|
|
466
|
-
* combined with `sendEnter`
|
|
467
|
+
* @throws {TmuxSendError} If session doesn't exist, command fails, or `literal` is
|
|
468
|
+
* combined with `sendEnter`; its `delivery` says whether the keys may be on
|
|
469
|
+
* the pane (Issue #3880). A normal return is `delivered`: tmux exited 0.
|
|
467
470
|
*
|
|
468
471
|
* @example
|
|
469
472
|
* ```typescript
|
|
@@ -476,7 +479,7 @@ async function createSession(sessionNameOrOptions, cwd) {
|
|
|
476
479
|
*/
|
|
477
480
|
async function sendKeys(sessionName, keys, sendEnter = true, options) {
|
|
478
481
|
if (options?.literal && sendEnter) {
|
|
479
|
-
throw new
|
|
482
|
+
throw new TmuxSendError('sendKeys: { literal: true } cannot be combined with sendEnter — a trailing C-m would be typed as text. Send Enter as a separate key.', 'not-started', 0);
|
|
480
483
|
}
|
|
481
484
|
// execFile() passes arguments directly without shell interpretation,
|
|
482
485
|
// so no shell-level escaping is needed
|
|
@@ -496,9 +499,45 @@ async function sendKeys(sessionName, keys, sendEnter = true, options) {
|
|
|
496
499
|
}
|
|
497
500
|
catch (error) {
|
|
498
501
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
499
|
-
throw new
|
|
502
|
+
throw new TmuxSendError(`Failed to send keys to tmux session: ${errorMessage}`, deliveryOfExecFailure(error), 0);
|
|
500
503
|
}
|
|
501
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* A `sendKeys` / `sendSpecialKeys` that failed, with how far it got
|
|
507
|
+
* (Issue #3880). The message is the one these functions always threw.
|
|
508
|
+
*/
|
|
509
|
+
class TmuxSendError extends Error {
|
|
510
|
+
delivery;
|
|
511
|
+
keysDelivered;
|
|
512
|
+
constructor(message,
|
|
513
|
+
/** What the call that failed did; a failed call is never `delivered`. */
|
|
514
|
+
delivery,
|
|
515
|
+
/** How many keys before it tmux was seen to take (`sendSpecialKeys`; 0 for `sendKeys`). */
|
|
516
|
+
keysDelivered) {
|
|
517
|
+
super(message);
|
|
518
|
+
this.delivery = delivery;
|
|
519
|
+
this.keysDelivered = keysDelivered;
|
|
520
|
+
this.name = 'TmuxSendError';
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
exports.TmuxSendError = TmuxSendError;
|
|
524
|
+
/**
|
|
525
|
+
* Which {@link KeyDeliveryState} a rejected `execFile` of `tmux send-keys` is
|
|
526
|
+
* (Issue #3880). Read off the error's shape, never its wording: an exit status
|
|
527
|
+
* (`code` a number) is tmux refusing the command, an errno string (`ENOENT`)
|
|
528
|
+
* is a process that never ran, and anything else — `killed` by the timeout, a
|
|
529
|
+
* `signal`, an error of no known shape — may have left the key on the pane.
|
|
530
|
+
*/
|
|
531
|
+
function deliveryOfExecFailure(error) {
|
|
532
|
+
if (typeof error !== 'object' || error === null)
|
|
533
|
+
return 'indeterminate';
|
|
534
|
+
const { code, killed, signal } = error;
|
|
535
|
+
if (killed === true || (typeof signal === 'string' && signal !== ''))
|
|
536
|
+
return 'indeterminate';
|
|
537
|
+
if (typeof code === 'number' || typeof code === 'string')
|
|
538
|
+
return 'not-started';
|
|
539
|
+
return 'indeterminate';
|
|
540
|
+
}
|
|
502
541
|
/**
|
|
503
542
|
* Allowed tmux special key names for sendSpecialKeys() (multi-key TUI navigation).
|
|
504
543
|
* Used for cursor-based navigation sequences (e.g., ['Down', 'Down', 'Enter']).
|
|
@@ -607,7 +646,9 @@ exports.SpecialKeysAbortedError = SpecialKeysAbortedError;
|
|
|
607
646
|
* @param sessionName - Target session name
|
|
608
647
|
* @param keys - Array of tmux special key names (e.g., ['Down', 'Down', 'Space', 'Enter'])
|
|
609
648
|
* @param options - Optional `shouldContinue`, asked after each delay (Issue #3706)
|
|
610
|
-
* @throws {
|
|
649
|
+
* @throws {TmuxSendError} If any key name is not in the allowed set (`not-started`,
|
|
650
|
+
* nothing sent), or if a tmux command fails — with the keys before it that
|
|
651
|
+
* tmux took and what the failing call did (Issue #3880)
|
|
611
652
|
* @throws {SpecialKeysAbortedError} If `shouldContinue` returned false between two keys
|
|
612
653
|
*/
|
|
613
654
|
async function sendSpecialKeys(sessionName, keys, options) {
|
|
@@ -616,12 +657,15 @@ async function sendSpecialKeys(sessionName, keys, options) {
|
|
|
616
657
|
// Validate all keys are in the allowed set (command injection prevention)
|
|
617
658
|
for (const key of keys) {
|
|
618
659
|
if (!ALLOWED_SPECIAL_KEYS.has(key)) {
|
|
619
|
-
throw new
|
|
660
|
+
throw new TmuxSendError(`Invalid special key: ${key}`, 'not-started', 0);
|
|
620
661
|
}
|
|
621
662
|
}
|
|
663
|
+
// Issue #3880: the keys tmux was seen to take, for the error of a later one.
|
|
664
|
+
let delivered = 0;
|
|
622
665
|
try {
|
|
623
666
|
for (let i = 0; i < keys.length; i++) {
|
|
624
667
|
await execFileAsync('tmux', ['send-keys', '-t', exactTarget(sessionName), keys[i]], { timeout: DEFAULT_TIMEOUT });
|
|
668
|
+
delivered = i + 1;
|
|
625
669
|
// Delay between key presses (skip after the last key)
|
|
626
670
|
if (i < keys.length - 1) {
|
|
627
671
|
await new Promise(resolve => setTimeout(resolve, SPECIAL_KEY_DELAY_MS));
|
|
@@ -635,7 +679,7 @@ async function sendSpecialKeys(sessionName, keys, options) {
|
|
|
635
679
|
if (error instanceof SpecialKeysAbortedError)
|
|
636
680
|
throw error;
|
|
637
681
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
638
|
-
throw new
|
|
682
|
+
throw new TmuxSendError(`Failed to send special keys to tmux session: ${errorMessage}`, deliveryOfExecFailure(error), delivered);
|
|
639
683
|
}
|
|
640
684
|
}
|
|
641
685
|
/**
|
|
@@ -903,6 +947,30 @@ async function clearComposerLine(sessionName) {
|
|
|
903
947
|
throw new Error(`Failed to clear tmux composer line: ${errorMessage}`);
|
|
904
948
|
}
|
|
905
949
|
}
|
|
950
|
+
/**
|
|
951
|
+
* Send one `C-e` + `BSpace` pass: take a collapsed paste placeholder out of the
|
|
952
|
+
* composer (Issue #3937).
|
|
953
|
+
*
|
|
954
|
+
* codex folds a pasted body into one `[Pasted Content N chars]` placeholder.
|
|
955
|
+
* `C-e`+`C-u` left one in place for all 12 passes (`composer-clear:not-cleared`,
|
|
956
|
+
* 2026-10-25), and one `BSpace` sent by hand removed it whole. The caller
|
|
957
|
+
* ({@link module:lib/session/composer-clear}) sends this only when the composer
|
|
958
|
+
* shows that placeholder and nothing else — after `C-e` the cursor sits right
|
|
959
|
+
* behind it — and reads the frame back afterwards like any other pass. Same
|
|
960
|
+
* literal-keys / one-invocation rules as {@link clearComposerLine}.
|
|
961
|
+
*
|
|
962
|
+
* @param sessionName - Target session name
|
|
963
|
+
* @throws {Error} If the tmux command fails
|
|
964
|
+
*/
|
|
965
|
+
async function clearComposerPasteToken(sessionName) {
|
|
966
|
+
try {
|
|
967
|
+
await execFileAsync('tmux', ['send-keys', '-t', exactTarget(sessionName), 'C-e', 'BSpace'], { timeout: DEFAULT_TIMEOUT });
|
|
968
|
+
}
|
|
969
|
+
catch (error) {
|
|
970
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
971
|
+
throw new Error(`Failed to clear tmux composer paste placeholder: ${errorMessage}`);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
906
974
|
/**
|
|
907
975
|
* Type guard for navigation key validation (special-keys API).
|
|
908
976
|
* Returns true if the key is in the NAVIGATION_KEY_VALUES set AND sendSpecialKeys()
|
|
@@ -965,7 +1033,9 @@ exports.REPAINT_INVALIDATE_DELAY_MS = 250;
|
|
|
965
1033
|
* Drop the cached frame again once the TUI has had time to repaint.
|
|
966
1034
|
*
|
|
967
1035
|
* Fire-and-forget on purpose: the route must not wait 250 ms to answer, and the
|
|
968
|
-
* work is
|
|
1036
|
+
* work is synchronous and small — `invalidateCache` drops the cached frame and
|
|
1037
|
+
* the inflight capture and advances the session's generation (Issue #3734), so
|
|
1038
|
+
* a capture started before the repaint is not written back either. The timer is `unref()`ed where the runtime supports
|
|
969
1039
|
* it so a pending invalidation can never hold a process (or a test runner) open.
|
|
970
1040
|
*/
|
|
971
1041
|
function scheduleRepaintInvalidation(sessionName) {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* @module lib/verification/gate-runner
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.VerificationConflictError = exports.FLAKY_LOG_PREFIX = exports.MUTEX_WAIT_SKIP_REASON = exports.MUTEX_LOG_PREFIX = exports.WORKTREE_INDEX_ENV = exports.WORKTREE_ID_ENV = exports.MAX_CONCURRENT_VERIFICATIONS = exports.CONFIG_GATE_ID = exports.ENV_CLEAN_GATE_ID = exports.SCOPE_GATE_ID = exports.WORK_EVIDENCE_GATE_ID = void 0;
|
|
24
|
+
exports.VerificationConflictError = exports.TIMING_LOG_PREFIX = exports.FLAKY_LOG_PREFIX = exports.MUTEX_WAIT_SKIP_REASON = exports.MUTEX_LOG_PREFIX = exports.WORKTREE_INDEX_ENV = exports.WORKTREE_ID_ENV = exports.MAX_CONCURRENT_VERIFICATIONS = exports.CONFIG_GATE_ID = exports.ENV_CLEAN_GATE_ID = exports.SCOPE_GATE_ID = exports.WORK_EVIDENCE_GATE_ID = void 0;
|
|
25
25
|
exports.waitForVerification = waitForVerification;
|
|
26
26
|
exports.cancelVerification = cancelVerification;
|
|
27
27
|
exports.startVerification = startVerification;
|
|
@@ -87,6 +87,9 @@ const ENV_CLEAN_GATE_COMMAND = 'lsof -iTCP -sTCP:LISTEN / tmux list-sessions / $
|
|
|
87
87
|
* once turns a verification request into a fork bomb on a developer laptop.
|
|
88
88
|
* Excess runs queue rather than fail: the run row already exists and its
|
|
89
89
|
* caller has the id, so a queued run is honest about what will happen.
|
|
90
|
+
*
|
|
91
|
+
* A bound on gates *executing*, not on runs open: a run waiting for a gate's
|
|
92
|
+
* `mutex` gives its slot back for the wait (Issue #3884, see RunSlot).
|
|
90
93
|
*/
|
|
91
94
|
exports.MAX_CONCURRENT_VERIFICATIONS = 2;
|
|
92
95
|
/** Grace period between SIGTERM and SIGKILL for a gate that overran. */
|
|
@@ -144,6 +147,33 @@ exports.MUTEX_WAIT_SKIP_REASON = 'reason=mutex-wait';
|
|
|
144
147
|
* cannot import this module — `tsconfig.cli.json` compiles `src/cli/**` alone.
|
|
145
148
|
*/
|
|
146
149
|
exports.FLAKY_LOG_PREFIX = '[flaky]';
|
|
150
|
+
/**
|
|
151
|
+
* Marker line carrying a command gate's waits and its execution, apart, into
|
|
152
|
+
* `log_tail` (Issue #3884).
|
|
153
|
+
*
|
|
154
|
+
* `[timing] queue=<s> mutex=<s> run=<s>`: `queue` is the time the run waited
|
|
155
|
+
* for a {@link MAX_CONCURRENT_VERIFICATIONS} slot before and during this gate
|
|
156
|
+
* (the first command gate also carries the run's initial queue), `mutex` the
|
|
157
|
+
* time it waited for the gate's machine-wide lock, and `run` the gate's own
|
|
158
|
+
* command — `duration_ms`, which still never includes either wait (#1771).
|
|
159
|
+
* Three numbers because "this run took 40 minutes" has three different fixes:
|
|
160
|
+
* fewer runs at once, fewer heavy gates, or a faster suite.
|
|
161
|
+
*
|
|
162
|
+
* The last line, so the first line keeps meaning what it meant (the skip
|
|
163
|
+
* reason a reader of a `skipped` gate takes, the `[mutex]` / `[flaky]` marker
|
|
164
|
+
* the CLI reads). Written only for a gate that has a `mutex` or that waited for
|
|
165
|
+
* a slot: a gate that did neither has no wait to report, and its log stays
|
|
166
|
+
* byte-identical to before the marker existed. Travels on `log_tail` like
|
|
167
|
+
* `waited=` does, so it reaches `verify --json` and `verify show` without a
|
|
168
|
+
* migration; the history listing, which carries no log, reads this one line
|
|
169
|
+
* into numbers (verification-db.ts parseGateTiming, the `timing` of a gate
|
|
170
|
+
* summary — keep the two formats in step).
|
|
171
|
+
*/
|
|
172
|
+
exports.TIMING_LOG_PREFIX = '[timing]';
|
|
173
|
+
function timingMarker(waits, runMs) {
|
|
174
|
+
return (`${exports.TIMING_LOG_PREFIX} queue=${formatSeconds(waits.queueMs)} ` +
|
|
175
|
+
`mutex=${formatSeconds(waits.mutexMs)} run=${formatSeconds(runMs)}`);
|
|
176
|
+
}
|
|
147
177
|
/**
|
|
148
178
|
* Runs a gate is allowed. Not derived from `retryOnFail` at the call site: the
|
|
149
179
|
* marker states the number so a reader of stored history never has to look up
|
|
@@ -204,13 +234,43 @@ function notRun(logTail) {
|
|
|
204
234
|
// =============================================================================
|
|
205
235
|
let activeVerifications = 0;
|
|
206
236
|
const slotWaiters = [];
|
|
207
|
-
|
|
237
|
+
/**
|
|
238
|
+
* @param priority queue ahead of every run still waiting to start (Issue #3884).
|
|
239
|
+
* Used by a run that gave its slot back while it waited for a gate's
|
|
240
|
+
* `mutex` and now holds that machine-wide lock: every second it spends
|
|
241
|
+
* here is a second the lock is held with nothing running under it.
|
|
242
|
+
*/
|
|
243
|
+
function acquireSlot(priority = false, cancellation) {
|
|
208
244
|
if (activeVerifications < exports.MAX_CONCURRENT_VERIFICATIONS) {
|
|
209
245
|
activeVerifications += 1;
|
|
210
|
-
return Promise.resolve();
|
|
246
|
+
return Promise.resolve(true);
|
|
211
247
|
}
|
|
212
248
|
return new Promise((resolve) => {
|
|
213
|
-
|
|
249
|
+
let settled = false;
|
|
250
|
+
let unregister = () => { };
|
|
251
|
+
const grant = () => {
|
|
252
|
+
settled = true;
|
|
253
|
+
unregister();
|
|
254
|
+
resolve(true);
|
|
255
|
+
};
|
|
256
|
+
if (priority)
|
|
257
|
+
slotWaiters.unshift(grant);
|
|
258
|
+
else
|
|
259
|
+
slotWaiters.push(grant);
|
|
260
|
+
// Issue #3884 N3: a cancelled wait leaves the queue instead of waiting for
|
|
261
|
+
// whatever the other slots are executing. Leaving takes nothing, so there
|
|
262
|
+
// is nothing to hand back.
|
|
263
|
+
if (cancellation) {
|
|
264
|
+
unregister = cancellation.register(() => {
|
|
265
|
+
if (settled)
|
|
266
|
+
return;
|
|
267
|
+
settled = true;
|
|
268
|
+
const at = slotWaiters.indexOf(grant);
|
|
269
|
+
if (at >= 0)
|
|
270
|
+
slotWaiters.splice(at, 1);
|
|
271
|
+
resolve(false);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
214
274
|
});
|
|
215
275
|
}
|
|
216
276
|
function releaseSlot() {
|
|
@@ -223,6 +283,63 @@ function releaseSlot() {
|
|
|
223
283
|
}
|
|
224
284
|
activeVerifications -= 1;
|
|
225
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* One run's hold on a {@link MAX_CONCURRENT_VERIFICATIONS} slot (Issue #3884 H-11).
|
|
288
|
+
*
|
|
289
|
+
* The slot bounds what *executes*, not what waits. A run used to take its slot
|
|
290
|
+
* up front and keep it while a gate queued for its `mutex` (`cpu.heavy`, up to
|
|
291
|
+
* the gate's whole `timeoutSec`), so one run holding the lock and one waiting
|
|
292
|
+
* for it filled both slots, and a third run could not start even its 5-second
|
|
293
|
+
* `lint`. {@link runGateAttempt} now hands the slot back while it waits for the
|
|
294
|
+
* lock and takes it again — ahead of the runs still waiting to start — once the
|
|
295
|
+
* lock is held. At most {@link MAX_CONCURRENT_VERIFICATIONS} gates still
|
|
296
|
+
* execute at once, and the heavy gates stay one per machine under their mutex,
|
|
297
|
+
* so the load a gate is judged under does not grow.
|
|
298
|
+
*
|
|
299
|
+
* No deadlock: a run waiting for a lock holds no slot, so every slot holder is
|
|
300
|
+
* executing a gate, and each gate ends within its own `timeoutSec`.
|
|
301
|
+
*
|
|
302
|
+
* Also the clock for the `queue=` of {@link TIMING_LOG_PREFIX}: the time spent
|
|
303
|
+
* waiting for a slot since the last {@link RunSlot.takeQueuedMs}.
|
|
304
|
+
*/
|
|
305
|
+
class RunSlot {
|
|
306
|
+
held = false;
|
|
307
|
+
queuedMs = 0;
|
|
308
|
+
get isHeld() {
|
|
309
|
+
return this.held;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* @param cancellation when given, a cancel abandons the wait (Issue #3884 N3)
|
|
313
|
+
* @returns false when the wait was abandoned: no slot is held
|
|
314
|
+
*/
|
|
315
|
+
async acquire(priority = false, cancellation) {
|
|
316
|
+
if (this.held)
|
|
317
|
+
return true;
|
|
318
|
+
// Only a real queue counts: a free slot is taken without a wait, and a
|
|
319
|
+
// gate that never waited must not grow a `queue=0.0s` line (its log stays
|
|
320
|
+
// byte-identical to before #3884).
|
|
321
|
+
const queued = activeVerifications >= exports.MAX_CONCURRENT_VERIFICATIONS;
|
|
322
|
+
const startedAt = Date.now();
|
|
323
|
+
const taken = await acquireSlot(priority, cancellation);
|
|
324
|
+
this.held = taken;
|
|
325
|
+
if (queued)
|
|
326
|
+
this.queuedMs += Math.max(1, Date.now() - startedAt);
|
|
327
|
+
return taken;
|
|
328
|
+
}
|
|
329
|
+
/** Idempotent: a slot already handed back is not released twice. */
|
|
330
|
+
release() {
|
|
331
|
+
if (!this.held)
|
|
332
|
+
return;
|
|
333
|
+
this.held = false;
|
|
334
|
+
releaseSlot();
|
|
335
|
+
}
|
|
336
|
+
/** The slot wait accumulated since the previous call, and reset it. */
|
|
337
|
+
takeQueuedMs() {
|
|
338
|
+
const queued = this.queuedMs;
|
|
339
|
+
this.queuedMs = 0;
|
|
340
|
+
return queued;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
226
343
|
/**
|
|
227
344
|
* In-flight runs, so callers (tests, and the CLI in Phase 1-4) can await a run
|
|
228
345
|
* that {@link startVerification} deliberately did not wait for.
|
|
@@ -310,9 +427,10 @@ const cancellations = new Map();
|
|
|
310
427
|
* stopped something.
|
|
311
428
|
*
|
|
312
429
|
* A run cancelled while it is still queued behind
|
|
313
|
-
* {@link MAX_CONCURRENT_VERIFICATIONS}
|
|
314
|
-
*
|
|
315
|
-
* it.
|
|
430
|
+
* {@link MAX_CONCURRENT_VERIFICATIONS} leaves the queue at once and closes
|
|
431
|
+
* `cancelled` (Issue #3909); it used to answer `requested` and close only once
|
|
432
|
+
* a slot reached it. `requested` is now only a run whose gate is still winding
|
|
433
|
+
* down when the budget runs out.
|
|
316
434
|
*/
|
|
317
435
|
async function cancelVerification(runId) {
|
|
318
436
|
const cancellation = cancellations.get(runId);
|
|
@@ -578,7 +696,7 @@ function machineLockPathFor(name) {
|
|
|
578
696
|
* across both, so a retry never keeps a machine-wide resource out of another
|
|
579
697
|
* worktree's hands for a run that has already failed once.
|
|
580
698
|
*/
|
|
581
|
-
async function runGateAttempt(gate, worktreePath, maxLogTailBytes, gateEnv, cancellation) {
|
|
699
|
+
async function runGateAttempt(gate, worktreePath, maxLogTailBytes, gateEnv, cancellation, slot, waits) {
|
|
582
700
|
const mutex = gate.mutex;
|
|
583
701
|
if (!mutex) {
|
|
584
702
|
return runCommand(gate.command, worktreePath, gate.timeoutSec, maxLogTailBytes, gateEnv, cancellation);
|
|
@@ -591,10 +709,25 @@ async function runGateAttempt(gate, worktreePath, maxLogTailBytes, gateEnv, canc
|
|
|
591
709
|
// cancelled while queued behind the lock used to stay `running` for up to
|
|
592
710
|
// half an hour — holding the worktree against the very endpoint that exists
|
|
593
711
|
// to release it.
|
|
712
|
+
//
|
|
713
|
+
// Issue #3884 H-11: the run's slot is handed back for as long as the lock is
|
|
714
|
+
// busy (never when it is free — `onWait` fires only once a claim has failed),
|
|
715
|
+
// and taken back ahead of the runs still waiting to start. See RunSlot.
|
|
594
716
|
const lock = await (0, machine_lock_1.acquireMachineLock)(mutex, {
|
|
595
717
|
timeoutMs: gate.timeoutSec * 1000,
|
|
596
718
|
shouldAbandon: () => cancellation.cancelled,
|
|
719
|
+
onWait: () => slot.release(),
|
|
597
720
|
});
|
|
721
|
+
waits.mutexMs += lock.waitedMs;
|
|
722
|
+
// Taken back even when the lock was not: the run goes on to record (or run)
|
|
723
|
+
// the gates after this one, and those are under the same bound. Not by a
|
|
724
|
+
// cancelled run (#3884 N3): it executes nothing more, and waiting for the
|
|
725
|
+
// gates the other slots are running would keep its worktree held past the
|
|
726
|
+
// cancel endpoint's budget. The wait for the slot is cancellable too — the
|
|
727
|
+
// latch is read below, and the lock released, once it returns.
|
|
728
|
+
if (!cancellation.cancelled)
|
|
729
|
+
await slot.acquire(true, cancellation);
|
|
730
|
+
waits.queueMs += slot.takeQueuedMs();
|
|
598
731
|
if (!lock.acquired) {
|
|
599
732
|
const recordedAt = Date.now();
|
|
600
733
|
return {
|
|
@@ -646,14 +779,28 @@ async function runGateAttempt(gate, worktreePath, maxLogTailBytes, gateEnv, canc
|
|
|
646
779
|
* came free) and an `error` (the command could not be spawned) never started a
|
|
647
780
|
* command, so there is no verdict to seek a second opinion on.
|
|
648
781
|
*/
|
|
649
|
-
async function runCommandGate(gate, worktreePath, maxLogTailBytes, gateEnv, cancellation) {
|
|
650
|
-
|
|
782
|
+
async function runCommandGate(gate, worktreePath, maxLogTailBytes, gateEnv, cancellation, slot) {
|
|
783
|
+
// The slot wait since the previous command gate (for the first one, since
|
|
784
|
+
// the run was opened) belongs to this gate: it is what kept it from starting.
|
|
785
|
+
const waits = { queueMs: slot.takeQueuedMs(), mutexMs: 0 };
|
|
786
|
+
const outcome = await runCommandGateAttempts(gate, worktreePath, maxLogTailBytes, gateEnv, cancellation, slot, waits);
|
|
787
|
+
if (!gate.mutex && waits.queueMs === 0)
|
|
788
|
+
return outcome;
|
|
789
|
+
return {
|
|
790
|
+
...outcome,
|
|
791
|
+
logTail: `${outcome.logTail ? `${outcome.logTail}\n` : ''}${timingMarker(waits, outcome.durationMs)}`,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
/** {@link runCommandGate}'s attempts, with the waits they accumulate. */
|
|
795
|
+
async function runCommandGateAttempts(gate, worktreePath, maxLogTailBytes, gateEnv, cancellation, slot, waits) {
|
|
796
|
+
const attempt = () => runGateAttempt(gate, worktreePath, maxLogTailBytes, gateEnv, cancellation, slot, waits);
|
|
797
|
+
const first = await attempt();
|
|
651
798
|
// A cancelled attempt reports `skipped`, so the retry below is already ruled
|
|
652
799
|
// out by the status check: an operator's Stop must not buy a second run of
|
|
653
800
|
// the very command they stopped.
|
|
654
801
|
if (gate.retryOnFail !== 1 || first.status !== 'failed')
|
|
655
802
|
return first;
|
|
656
|
-
const second = await
|
|
803
|
+
const second = await attempt();
|
|
657
804
|
if (second.status !== 'passed' && second.status !== 'failed') {
|
|
658
805
|
// The retry reached no verdict of its own, so there is nothing to compare
|
|
659
806
|
// the first run against. The first run's FAIL stands unchanged — reporting
|
|
@@ -1024,7 +1171,9 @@ async function executeRun(db, runId, worktreeId, worktreePath,
|
|
|
1024
1171
|
/** Issue #2043: which agent instance the run is judging, for the second witness. */
|
|
1025
1172
|
instanceId, config, selection, baseRef, task, detachedContract, envClean,
|
|
1026
1173
|
/** Issue #2063: the operator's Stop switch for this run. */
|
|
1027
|
-
cancellation
|
|
1174
|
+
cancellation,
|
|
1175
|
+
/** Issue #3884: handed back while a gate waits for its mutex. */
|
|
1176
|
+
slot) {
|
|
1028
1177
|
const { maxLogTailBytes, skipInPrimaryCheckout } = config.options;
|
|
1029
1178
|
const { runWorkEvidence, gates } = selection;
|
|
1030
1179
|
// Issue #2043. A thunk rather than a number so the store is read at the
|
|
@@ -1209,7 +1358,7 @@ cancellation) {
|
|
|
1209
1358
|
statuses.push(recordNotRun(gate.id, gate.command, gate.source, run_verdict_vocabulary_1.PRIMARY_CHECKOUT_SKIP_LOG).status);
|
|
1210
1359
|
continue;
|
|
1211
1360
|
}
|
|
1212
|
-
const outcome = await runGate(gate.id, gate.command, gate.source, () => runCommandGate(gate, worktreePath, maxLogTailBytes, gateEnv(), cancellation));
|
|
1361
|
+
const outcome = await runGate(gate.id, gate.command, gate.source, () => runCommandGate(gate, worktreePath, maxLogTailBytes, gateEnv(), cancellation, slot));
|
|
1213
1362
|
statuses.push(outcome.status);
|
|
1214
1363
|
}
|
|
1215
1364
|
// The loop's own check cannot see a cancel that landed during the LAST gate:
|
|
@@ -1487,8 +1636,15 @@ async function startVerification(input) {
|
|
|
1487
1636
|
baseline: resolvedSelection.runEnvClean && taskId ? (0, env_snapshot_1.loadEnvSnapshot)(taskId) : null,
|
|
1488
1637
|
decision: envCleanDecision,
|
|
1489
1638
|
};
|
|
1639
|
+
const slot = new RunSlot();
|
|
1490
1640
|
const completion = (async () => {
|
|
1491
|
-
|
|
1641
|
+
// Issue #3909: the run's first wait for a slot is abandonable too, as the
|
|
1642
|
+
// re-take after a mutex wait has been since #3884 N3. A run cancelled here
|
|
1643
|
+
// used to stay queued (and `running`, holding its worktree) until one of
|
|
1644
|
+
// the two executing gates ended, so the cancel endpoint answered
|
|
1645
|
+
// `requested`. An abandoned wait holds no slot; `slot.release()` below is
|
|
1646
|
+
// idempotent, so the `finally` hands nothing back for it.
|
|
1647
|
+
await slot.acquire(false, cancellation);
|
|
1492
1648
|
let terminalStatus = 'error';
|
|
1493
1649
|
try {
|
|
1494
1650
|
// Cancelled while queued behind MAX_CONCURRENT_VERIFICATIONS: nothing was
|
|
@@ -1499,7 +1655,7 @@ async function startVerification(input) {
|
|
|
1499
1655
|
(0, db_1.finishVerificationRun)(db, run.id, 'cancelled');
|
|
1500
1656
|
return 'cancelled';
|
|
1501
1657
|
}
|
|
1502
|
-
const status = await executeRun(db, run.id, input.worktreeId, input.worktreePath, input.instanceId, resolvedConfig, resolvedSelection, baseRef, task, detachedContract, envCleanContext, cancellation);
|
|
1658
|
+
const status = await executeRun(db, run.id, input.worktreeId, input.worktreePath, input.instanceId, resolvedConfig, resolvedSelection, baseRef, task, detachedContract, envCleanContext, cancellation, slot);
|
|
1503
1659
|
terminalStatus = status;
|
|
1504
1660
|
(0, db_1.finishVerificationRun)(db, run.id, status);
|
|
1505
1661
|
return status;
|
|
@@ -1526,7 +1682,7 @@ async function startVerification(input) {
|
|
|
1526
1682
|
// after the task transition so the run's own history is complete before
|
|
1527
1683
|
// anything is said about it.
|
|
1528
1684
|
notifyRunFailure(db, input, run.id, taskId, terminalStatus);
|
|
1529
|
-
|
|
1685
|
+
slot.release();
|
|
1530
1686
|
inFlight.delete(run.id);
|
|
1531
1687
|
cancellations.delete(run.id);
|
|
1532
1688
|
}
|
|
@@ -161,6 +161,7 @@ async function acquireMachineLock(name, options) {
|
|
|
161
161
|
// A stale record is broken at most once per token, so a directory nobody can
|
|
162
162
|
// remove degrades into ordinary waiting instead of a spin loop.
|
|
163
163
|
const broken = new Set();
|
|
164
|
+
let waiting = false;
|
|
164
165
|
for (;;) {
|
|
165
166
|
// Issue #2063. Checked before the claim as well as after the sleep, so a
|
|
166
167
|
// caller that was already cancelled when it got here never takes a lock it
|
|
@@ -210,6 +211,10 @@ async function acquireMachineLock(name, options) {
|
|
|
210
211
|
if (now >= deadline) {
|
|
211
212
|
return { acquired: false, waitedMs: now - startedAt, heldBy: describeOwner(holder) };
|
|
212
213
|
}
|
|
214
|
+
if (!waiting) {
|
|
215
|
+
waiting = true;
|
|
216
|
+
options.onWait?.();
|
|
217
|
+
}
|
|
213
218
|
await sleep(Math.min(pollIntervalMs, deadline - now));
|
|
214
219
|
}
|
|
215
220
|
}
|
|
@@ -738,7 +738,11 @@ function handleDisconnect(ws) {
|
|
|
738
738
|
// what it obtains instead of registering it on a socket that is gone.
|
|
739
739
|
clientInfo.terminalSubscriptionSeq += 1;
|
|
740
740
|
if (clientInfo.terminalSubscription) {
|
|
741
|
-
|
|
741
|
+
// Issue #3749: not awaited, so a rejection would otherwise surface as an
|
|
742
|
+
// unhandled rejection.
|
|
743
|
+
clientInfo.terminalSubscription.unsubscribe().catch(() => {
|
|
744
|
+
// Best-effort cleanup on disconnect.
|
|
745
|
+
});
|
|
742
746
|
clientInfo.terminalSubscription = null;
|
|
743
747
|
}
|
|
744
748
|
// Remove client from clients map
|
|
@@ -792,7 +796,13 @@ async function handleTerminalSubscribe(ws, message) {
|
|
|
792
796
|
// Detached before the await, so a request arriving meanwhile neither
|
|
793
797
|
// releases it a second time nor has its own registration cleared by ours.
|
|
794
798
|
clientInfo.terminalSubscription = null;
|
|
795
|
-
|
|
799
|
+
try {
|
|
800
|
+
await previous.unsubscribe();
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
// Issue #3749: best-effort, like the other teardowns. Letting it throw
|
|
804
|
+
// ended this handler here and the new subscribe silently never happened.
|
|
805
|
+
}
|
|
796
806
|
}
|
|
797
807
|
if (!isCurrent())
|
|
798
808
|
return;
|
|
@@ -972,7 +982,7 @@ async function handleTerminalUnsubscribe(ws) {
|
|
|
972
982
|
return;
|
|
973
983
|
// Issue #3733: also withdraws a subscribe still in flight, which would
|
|
974
984
|
// otherwise register after this and keep streaming.
|
|
975
|
-
clientInfo.terminalSubscriptionSeq
|
|
985
|
+
const seq = ++clientInfo.terminalSubscriptionSeq;
|
|
976
986
|
const subscription = clientInfo.terminalSubscription;
|
|
977
987
|
if (!subscription) {
|
|
978
988
|
return;
|
|
@@ -985,6 +995,11 @@ async function handleTerminalUnsubscribe(ws) {
|
|
|
985
995
|
catch {
|
|
986
996
|
// Best-effort cleanup during terminal unsubscribe.
|
|
987
997
|
}
|
|
998
|
+
// Issue #3749: a subscribe that started while the teardown was awaited may
|
|
999
|
+
// already have said `connected:true`; a `connected:false` sent after it
|
|
1000
|
+
// would contradict the subscription that is now live.
|
|
1001
|
+
if (clients.get(ws) !== clientInfo || clientInfo.terminalSubscriptionSeq !== seq)
|
|
1002
|
+
return;
|
|
988
1003
|
sendTerminalEvent(ws, { type: 'terminal_status', connected: false });
|
|
989
1004
|
}
|
|
990
1005
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* How Auto-Yes answers a question the agent asks (Issue #3863).
|
|
4
|
+
*
|
|
5
|
+
* - `recommended` (おまかせ, the default) — a single-select question on the
|
|
6
|
+
* screen is answered too: the option whose title ends in a recommendation
|
|
7
|
+
* marker (`(Recommended)` / `(推奨)` / `(推奨)`), or the default (the
|
|
8
|
+
* cursor, else option 1) when none does. A question it cannot read that far
|
|
9
|
+
* (two markers, a marker on a free-text or multi-select row, a title it
|
|
10
|
+
* cannot tell from its description) is left to a human.
|
|
11
|
+
* - `careful` (慎重) — a question is never answered; it is left to a human
|
|
12
|
+
* (`question-left-to-human`).
|
|
13
|
+
*
|
|
14
|
+
* The two differ only there. Permissions are answered alike (the one "allow
|
|
15
|
+
* this call once" row, #3822), and what a contract's `autoYes` block withholds
|
|
16
|
+
* stays withheld under either (the contract is the ceiling; the answer mode is
|
|
17
|
+
* below it).
|
|
18
|
+
*
|
|
19
|
+
* Set each time Auto-Yes is turned on (worktree × instance). Turning it on
|
|
20
|
+
* from off — or after it ran out — starts at `recommended` unless a mode is
|
|
21
|
+
* given; turning it on again while it is on keeps the mode it has.
|
|
22
|
+
*
|
|
23
|
+
* Dependency-free so the CLI build (`tsconfig.cli.json`, no path aliases) can
|
|
24
|
+
* import it by a relative path.
|
|
25
|
+
*
|
|
26
|
+
* @module types/auto-yes-answer-mode
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.DEFAULT_AUTO_YES_ANSWER_MODE = exports.AUTO_YES_ANSWER_MODES = void 0;
|
|
30
|
+
exports.isAutoYesAnswerMode = isAutoYesAnswerMode;
|
|
31
|
+
/** Every {@link AutoYesAnswerMode}, the default first. */
|
|
32
|
+
exports.AUTO_YES_ANSWER_MODES = ['recommended', 'careful'];
|
|
33
|
+
/** The mode of a grant that names none: one turned on without a mode, or kept from before the field. */
|
|
34
|
+
exports.DEFAULT_AUTO_YES_ANSWER_MODE = 'recommended';
|
|
35
|
+
/** Type guard for a value read off a request body or the wire. */
|
|
36
|
+
function isAutoYesAnswerMode(value) {
|
|
37
|
+
return value === 'recommended' || value === 'careful';
|
|
38
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Auto-Yes's decision about the confirmation on screen, as published
|
|
4
|
+
* (Issue #3821).
|
|
5
|
+
*
|
|
6
|
+
* The server's Auto-Yes poller is the only thing that answers a screen
|
|
7
|
+
* confirmation automatically (the browser no longer does, and the Enter on an
|
|
8
|
+
* unreadable screen is gone). What it decided for the confirmation it last
|
|
9
|
+
* judged is published next to the prompt it was judged for, so the answer
|
|
10
|
+
* surfaces know whether a human has to answer it:
|
|
11
|
+
*
|
|
12
|
+
* - `answering` — an automatic answer to this confirmation is under way (being
|
|
13
|
+
* sent, or sent and the screen not redrawn yet);
|
|
14
|
+
* - `withheld` — Auto-Yes has left this confirmation to a human, with a
|
|
15
|
+
* `reason` (the contract, a screen it cannot read, a question, no poller
|
|
16
|
+
* running for the instance, …);
|
|
17
|
+
* - `none` — no confirmation, or one not judged yet. Never read as answering.
|
|
18
|
+
*
|
|
19
|
+
* Client-safe: no imports but the dependency-free answer mode (Issue #3863),
|
|
20
|
+
* so the browser hooks and the server read one shape.
|
|
21
|
+
*
|
|
22
|
+
* @module types/auto-yes-decision
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.NO_AUTO_ANSWER_DECISION_REASON = exports.SERVER_POLLER_INACTIVE_DECISION_REASON = void 0;
|
|
26
|
+
exports.isAutoYesAnswering = isAutoYesAnswering;
|
|
27
|
+
exports.laterAutoYesDecision = laterAutoYesDecision;
|
|
28
|
+
/** The reasons this record adds to the poller's suppression reasons. */
|
|
29
|
+
exports.SERVER_POLLER_INACTIVE_DECISION_REASON = 'server-poller-inactive';
|
|
30
|
+
exports.NO_AUTO_ANSWER_DECISION_REASON = 'no-auto-answer';
|
|
31
|
+
/**
|
|
32
|
+
* Whether the answer panel may stay hidden under Auto-Yes for a prompt with
|
|
33
|
+
* this decision: only while an automatic answer is under way. A missing
|
|
34
|
+
* decision (an older server, a delivery path that did not carry it) is not
|
|
35
|
+
* `answering`.
|
|
36
|
+
*/
|
|
37
|
+
function isAutoYesAnswering(decision) {
|
|
38
|
+
return decision?.state === 'answering';
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Of two decisions about the SAME prompt window, the one observed later
|
|
42
|
+
* (Issue #3821). The HTTP poll and the push deliver the same payload over two
|
|
43
|
+
* paths whose arrival order is not their build order; a decision observed
|
|
44
|
+
* before the one already applied is not applied over it.
|
|
45
|
+
*
|
|
46
|
+
* Only for the same confirmation (`confirmation`): a window with the same
|
|
47
|
+
* question and options may approve something else, and a decision about
|
|
48
|
+
* another confirmation — or one that does not say which (`confirmation`
|
|
49
|
+
* absent on one side only) — is dropped for the one this delivery carries,
|
|
50
|
+
* whatever their times. Two without the field are compared by time.
|
|
51
|
+
*
|
|
52
|
+
* @param previous - What is applied now for this window, if anything
|
|
53
|
+
* @param next - What this delivery carries
|
|
54
|
+
*/
|
|
55
|
+
function laterAutoYesDecision(previous, next) {
|
|
56
|
+
if (previous === undefined || next === undefined)
|
|
57
|
+
return next;
|
|
58
|
+
if ((previous.confirmation ?? null) !== (next.confirmation ?? null))
|
|
59
|
+
return next;
|
|
60
|
+
return next.observedAt < previous.observedAt ? previous : next;
|
|
61
|
+
}
|
|
@@ -10,3 +10,9 @@
|
|
|
10
10
|
* `AutoYesToggleParams` from the component keep working unchanged.
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.isAutoYesAnswerMode = exports.DEFAULT_AUTO_YES_ANSWER_MODE = exports.AUTO_YES_ANSWER_MODES = void 0;
|
|
14
|
+
// Issue #3863: the answer mode, declared dependency-free for the CLI build.
|
|
15
|
+
var auto_yes_answer_mode_1 = require("./auto-yes-answer-mode");
|
|
16
|
+
Object.defineProperty(exports, "AUTO_YES_ANSWER_MODES", { enumerable: true, get: function () { return auto_yes_answer_mode_1.AUTO_YES_ANSWER_MODES; } });
|
|
17
|
+
Object.defineProperty(exports, "DEFAULT_AUTO_YES_ANSWER_MODE", { enumerable: true, get: function () { return auto_yes_answer_mode_1.DEFAULT_AUTO_YES_ANSWER_MODE; } });
|
|
18
|
+
Object.defineProperty(exports, "isAutoYesAnswerMode", { enumerable: true, get: function () { return auto_yes_answer_mode_1.isAutoYesAnswerMode; } });
|