commandmate 0.24.0 → 0.26.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 +266 -266
- package/.next/app-path-routes-manifest.json +15 -15
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +3 -3
- package/.next/react-loadable-manifest.json +3 -3
- 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/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/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_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]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/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/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page.js +2 -2
- package/.next/server/app/chat/page.js.nft.json +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page.js +2 -2
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page.js +2 -2
- package/.next/server/app/more/page.js.nft.json +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page.js +2 -2
- package/.next/server/app/repositories/page.js.nft.json +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page.js +1 -1
- package/.next/server/app/review/page.js.nft.json +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js +2 -2
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page.js +2 -2
- package/.next/server/app/skills/installed/page.js.nft.json +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page.js +2 -2
- package/.next/server/app/skills/page.js.nft.json +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +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 +6 -6
- 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 +15 -15
- 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/3241.js +1 -1
- package/.next/server/chunks/3346.js +1 -1
- package/.next/server/chunks/4455.js +1 -1
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/5571.js +1 -1
- package/.next/server/chunks/5959.js +4 -4
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6274.js +12 -9
- package/.next/server/chunks/6285.js +1 -1
- package/.next/server/chunks/7458.js +6 -6
- package/.next/server/chunks/848.js +1 -1
- package/.next/server/chunks/8730.js +1 -0
- package/.next/server/chunks/880.js +1 -1
- package/.next/server/chunks/9328.js +1 -1
- package/.next/server/chunks/9373.js +4 -3
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/2053-dfac2d49227cc942.js +1 -0
- package/.next/static/chunks/3898-ef488bfa804507d9.js +1 -0
- package/.next/static/chunks/5229-35f9acab97a553c5.js +1 -0
- package/.next/static/chunks/{6381-18a6a02a94d00d76.js → 6381-9303430933084b56.js} +1 -1
- package/.next/static/chunks/{9599-d29d2f8eeaf7c249.js → 8649-f551b0010dba4986.js} +1 -1
- package/.next/static/chunks/9263.f2034ec525abcf72.js +1 -0
- package/.next/static/chunks/app/chat/page-439b35c9cb90dbe0.js +1 -0
- package/.next/static/chunks/app/login/page-eefd515d8c37e5ee.js +1 -0
- package/.next/static/chunks/app/more/page-889b7546a8a553dc.js +1 -0
- package/.next/static/chunks/app/page-a8d04ac0e3e4c9fc.js +1 -0
- package/.next/static/chunks/app/repositories/page-f3c4d8839b1ca203.js +1 -0
- package/.next/static/chunks/app/review/page-6dc88649fe88ac16.js +1 -0
- package/.next/static/chunks/app/sessions/page-2c9ed9e969097688.js +1 -0
- package/.next/static/chunks/app/skills/[skillId]/page-19beef6121c4c982.js +1 -0
- package/.next/static/chunks/app/skills/installed/page-d5b9cef6d21fabda.js +1 -0
- package/.next/static/chunks/app/skills/page-06ef637e07dd9377.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/{page-9ddcb224594f620f.js → page-5fed3984fafcd910.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-fbdf4621a2911ac1.js +1 -0
- package/.next/static/chunks/{webpack-3534d858912a6c92.js → webpack-9d892a9ff07c289d.js} +1 -1
- package/.next/static/css/e865c4979a41aa03.css +1 -0
- package/.next/trace +12 -12
- package/README.md +96 -45
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +2 -1
- package/dist/cli/commands/verify.d.ts +14 -0
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +109 -3
- package/dist/cli/commands/wait.d.ts +2 -0
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +184 -4
- package/dist/cli/types/api-responses.d.ts +172 -0
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +31 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/types/index.js +26 -0
- package/dist/cli/utils/verify-runner.d.ts +36 -1
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +114 -6
- package/dist/server/src/config/activity-bar-config.js +5 -1
- package/dist/server/src/lib/auto-yes-poller.js +40 -4
- package/dist/server/src/lib/auto-yes-state.js +108 -3
- package/dist/server/src/lib/cli-tools/antigravity.js +8 -4
- package/dist/server/src/lib/cli-tools/codex.js +63 -30
- package/dist/server/src/lib/cli-tools/copilot.js +5 -1
- package/dist/server/src/lib/cli-tools/gemini.js +15 -15
- package/dist/server/src/lib/cli-tools/opencode.js +9 -3
- package/dist/server/src/lib/detection/cli-patterns.js +148 -6
- package/dist/server/src/lib/detection/status-detector.js +28 -0
- package/dist/server/src/lib/detection/upstream-faults.js +120 -0
- package/dist/server/src/lib/hooks/permission-decision-service.js +13 -5
- package/dist/server/src/lib/hooks/sources/antigravity/hooks-config.js +18 -11
- package/dist/server/src/lib/hooks/sources/antigravity/source.js +10 -9
- package/dist/server/src/lib/hooks/sources/claude/source.js +5 -2
- package/dist/server/src/lib/hooks/sources/codex/hooks-config.js +26 -18
- package/dist/server/src/lib/hooks/sources/codex/source.js +6 -9
- package/dist/server/src/lib/hooks/sources/copilot/hook-settings.js +22 -12
- package/dist/server/src/lib/hooks/sources/copilot/source.js +1 -1
- package/dist/server/src/lib/hooks/sources/define-source.js +6 -4
- package/dist/server/src/lib/hooks/sources/gemini/settings-generator.js +14 -8
- package/dist/server/src/lib/hooks/sources/gemini/source.js +29 -29
- package/dist/server/src/lib/hooks/sources/index.js +4 -1
- package/dist/server/src/lib/hooks/sources/launch-command.js +61 -0
- package/dist/server/src/lib/hooks/sources/legacy-relay.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode/source.js +10 -7
- package/dist/server/src/lib/polling/prompt-dedup-state.js +95 -0
- package/dist/server/src/lib/polling/response-checker.js +12 -0
- package/dist/server/src/lib/polling/response-poller-core.js +11 -1
- package/dist/server/src/lib/session/agent-session-lifecycle.js +28 -7
- package/dist/server/src/lib/session/claude-session.js +5 -1
- package/dist/server/src/lib/session/current-output-builder.js +34 -0
- package/dist/server/src/lib/verification/env-snapshot.js +21 -3
- package/dist/server/src/lib/verification/gate-runner.js +258 -7
- package/dist/server/src/lib/verification/machine-lock.js +204 -0
- package/dist/server/src/lib/verification/scope-gate.js +126 -13
- package/dist/server/src/lib/verification/verify-config.js +85 -3
- package/dist/server/src/lib/verification/worktree-index.js +140 -0
- package/package.json +1 -1
- package/.next/server/chunks/3451.js +0 -1
- package/.next/static/chunks/2053-52d3c82014e1cd54.js +0 -1
- package/.next/static/chunks/5229-0610b4e3288e777d.js +0 -1
- package/.next/static/chunks/744-3ad06d8c8ca482fc.js +0 -1
- package/.next/static/chunks/9263.5259f73f89220d64.js +0 -1
- package/.next/static/chunks/app/chat/page-15d13fc024d78f5d.js +0 -1
- package/.next/static/chunks/app/login/page-0247a3fb569bb22b.js +0 -1
- package/.next/static/chunks/app/more/page-b8ba537baeac8d5b.js +0 -1
- package/.next/static/chunks/app/page-afc64708362f31e5.js +0 -1
- package/.next/static/chunks/app/repositories/page-3469dacb6be7842f.js +0 -1
- package/.next/static/chunks/app/review/page-2d9cb7e068f8f35a.js +0 -1
- package/.next/static/chunks/app/sessions/page-7470c2bfd89e0df9.js +0 -1
- package/.next/static/chunks/app/skills/[skillId]/page-5e24481162f7ea78.js +0 -1
- package/.next/static/chunks/app/skills/installed/page-efd2d710a27f9735.js +0 -1
- package/.next/static/chunks/app/skills/page-0498adfc6e48684b.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-e611d30e47989e5d.js +0 -1
- package/.next/static/css/3ac44cfc082d02ad.css +0 -1
- /package/.next/static/{smu4mnL7qMf8w38n9dQJE → hNdNiFes2xLls68SWhAU9}/_buildManifest.js +0 -0
- /package/.next/static/{smu4mnL7qMf8w38n9dQJE → hNdNiFes2xLls68SWhAU9}/_ssgManifest.js +0 -0
|
@@ -13,6 +13,7 @@ const logger_1 = require("../../lib/logger");
|
|
|
13
13
|
const tui_accumulator_1 = require("../tui-accumulator");
|
|
14
14
|
const prompt_dedup_1 = require("./prompt-dedup");
|
|
15
15
|
const response_dedup_1 = require("./response-dedup");
|
|
16
|
+
const prompt_dedup_state_1 = require("./prompt-dedup-state");
|
|
16
17
|
const response_checker_1 = require("./response-checker");
|
|
17
18
|
const terminal_broadcast_1 = require("../../lib/realtime/terminal-broadcast");
|
|
18
19
|
const logger = (0, logger_1.createLogger)('response-poller');
|
|
@@ -205,7 +206,8 @@ function splitPollerKey(pollerKey) {
|
|
|
205
206
|
* - `pollingStartTimes`, so MAX_POLLING_DURATION still measures from when the
|
|
206
207
|
* turn actually began rather than restarting the 30-minute budget;
|
|
207
208
|
* - the prompt and response dedup hashes, so the screen currently on display is
|
|
208
|
-
* not saved a second time as a new message
|
|
209
|
+
* not saved a second time as a new message, together with the prompt-skip
|
|
210
|
+
* tally that explains those suppressions to `capture --json` (Issue #1695).
|
|
209
211
|
*
|
|
210
212
|
* The TUI accumulator (opencode / copilot only) is re-initialised empty at the
|
|
211
213
|
* new key instead of carried over: `tui-accumulator.ts` exposes no way to write
|
|
@@ -266,6 +268,14 @@ function migrateResponsePollerWorktreeIds(renames, resolveCliToolId) {
|
|
|
266
268
|
exports.pollingStartTimes.delete(move.oldKey);
|
|
267
269
|
(0, prompt_dedup_1.renamePromptHashCacheKey)(move.oldKey, move.newKey);
|
|
268
270
|
(0, response_dedup_1.renameResponseHashCacheKey)(move.oldKey, move.newKey);
|
|
271
|
+
// Issue #1695: the prompt hash moved on the line above, so the guard keeps
|
|
272
|
+
// suppressing the prompt on screen under the new ID. Its tally has to move
|
|
273
|
+
// with it, or `capture --json` reports zero skips for a session that is
|
|
274
|
+
// still skipping. Old worktree ID comes back out of the key we just left.
|
|
275
|
+
const movedFrom = splitPollerKey(move.oldKey);
|
|
276
|
+
if (movedFrom) {
|
|
277
|
+
(0, prompt_dedup_state_1.renamePromptDedupSkips)(movedFrom.worktreeId, move.newWorktreeId, move.cliToolId, move.instanceId);
|
|
278
|
+
}
|
|
269
279
|
(0, tui_accumulator_1.clearTuiAccumulator)(move.oldKey);
|
|
270
280
|
}
|
|
271
281
|
for (const pollerKey of abandoned) {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.beginAgentSession = beginAgentSession;
|
|
41
41
|
exports.prepareAgentLaunch = prepareAgentLaunch;
|
|
42
|
+
exports.buildAgentLaunchCommandLine = buildAgentLaunchCommandLine;
|
|
42
43
|
const sources_1 = require("../../lib/hooks/sources");
|
|
43
44
|
const agent_event_state_1 = require("../../lib/session/agent-event-state");
|
|
44
45
|
const logger_1 = require("../../lib/logger");
|
|
@@ -70,13 +71,33 @@ function beginAgentSession(target, at = Date.now()) {
|
|
|
70
71
|
* does not import a settings generator: which file gets written, and whether
|
|
71
72
|
* one gets written at all, is the source's business.
|
|
72
73
|
*
|
|
73
|
-
* Never throws — see
|
|
74
|
-
*
|
|
74
|
+
* Never throws — see `AgentEventSource.prepareLaunch`. A tool whose config
|
|
75
|
+
* could not be written starts bare, which is the pre-#1722 status quo.
|
|
75
76
|
*
|
|
76
|
-
* @param
|
|
77
|
-
* @
|
|
78
|
-
* @returns The command line, and the config file when one was written
|
|
77
|
+
* @param context - The instance, its executable, and the worktree it runs in
|
|
78
|
+
* @returns The command, its environment, and the config file when one landed
|
|
79
79
|
*/
|
|
80
|
-
function prepareAgentLaunch(
|
|
81
|
-
return (0, sources_1.getAgentEventSource)(target.cliToolId).prepareLaunch(
|
|
80
|
+
function prepareAgentLaunch(context) {
|
|
81
|
+
return (0, sources_1.getAgentEventSource)(context.target.cliToolId).prepareLaunch(context);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The single line a `startSession` types into its pane (Issue #1846).
|
|
85
|
+
*
|
|
86
|
+
* `prepareAgentLaunch` returns a plan whose environment is *data*; this is the
|
|
87
|
+
* one function that turns that data into shell assignments. Before #1846 four
|
|
88
|
+
* sources each wrote their own `NAME=value ` prefix onto `plan.command`, so
|
|
89
|
+
* "does this launcher apply the environment?" was answered four times, by
|
|
90
|
+
* accident, and a fifth source that forgot would have produced a session whose
|
|
91
|
+
* hooks fire and cannot be attributed — no error, events landing on the wrong
|
|
92
|
+
* instance.
|
|
93
|
+
*
|
|
94
|
+
* Callers that need the settings path as well should call
|
|
95
|
+
* {@link prepareAgentLaunch} and render the plan themselves; every caller in
|
|
96
|
+
* `src/lib/cli-tools/` wants only the line.
|
|
97
|
+
*
|
|
98
|
+
* @param context - The instance, its executable, and the worktree it runs in
|
|
99
|
+
* @returns `NAME='value' … command`, ready for `sendKeys`
|
|
100
|
+
*/
|
|
101
|
+
function buildAgentLaunchCommandLine(context) {
|
|
102
|
+
return (0, sources_1.renderAgentLaunchCommand)(prepareAgentLaunch(context));
|
|
82
103
|
}
|
|
@@ -601,7 +601,11 @@ async function startClaudeSession(options) {
|
|
|
601
601
|
// Issue #1759: which config file gets written, and whether one is written
|
|
602
602
|
// at all, belongs to the tool's `AgentEventSource` (S3/S4). Claude's
|
|
603
603
|
// delegates to `buildClaudeLaunchCommand`, unchanged.
|
|
604
|
-
const launchCommand = (0, agent_session_lifecycle_1.
|
|
604
|
+
const launchCommand = (0, agent_session_lifecycle_1.buildAgentLaunchCommandLine)({
|
|
605
|
+
target: { worktreeId, cliToolId: sources_1.CLAUDE_CLI_TOOL_ID, instanceId },
|
|
606
|
+
executablePath: claudePath,
|
|
607
|
+
worktreePath,
|
|
608
|
+
});
|
|
605
609
|
// Start Claude CLI in interactive mode using dynamically resolved path
|
|
606
610
|
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
607
611
|
// Wait for Claude to initialize with dynamic detection (OCP-001)
|
|
@@ -12,6 +12,7 @@ exports.mergeStructuredStatus = mergeStructuredStatus;
|
|
|
12
12
|
exports.buildCurrentOutput = buildCurrentOutput;
|
|
13
13
|
const db_1 = require("../../lib/db");
|
|
14
14
|
const unclassified_frame_tracker_1 = require("../../lib/detection/unclassified-frame-tracker");
|
|
15
|
+
const upstream_faults_1 = require("../../lib/detection/upstream-faults");
|
|
15
16
|
const models_1 = require("../../types/models");
|
|
16
17
|
const logger_1 = require("../../lib/logger");
|
|
17
18
|
const manager_1 = require("../../lib/cli-tools/manager");
|
|
@@ -19,6 +20,7 @@ const cli_session_1 = require("../../lib/session/cli-session");
|
|
|
19
20
|
const status_detector_1 = require("../../lib/detection/status-detector");
|
|
20
21
|
const auto_yes_manager_1 = require("../../lib/polling/auto-yes-manager");
|
|
21
22
|
const auto_yes_suppression_state_1 = require("../../lib/polling/auto-yes-suppression-state");
|
|
23
|
+
const prompt_dedup_state_1 = require("../../lib/polling/prompt-dedup-state");
|
|
22
24
|
const status_capture_config_1 = require("../../config/status-capture-config");
|
|
23
25
|
const tmux_capture_cache_1 = require("../../lib/tmux/tmux-capture-cache");
|
|
24
26
|
const agent_event_state_1 = require("../../lib/session/agent-event-state");
|
|
@@ -300,6 +302,18 @@ async function buildCurrentOutput(db, worktreeId, cliToolId, instanceId) {
|
|
|
300
302
|
// the server's job, done here and in exactly one place.
|
|
301
303
|
model: null,
|
|
302
304
|
reasoningEffort: null,
|
|
305
|
+
// Issue #1695: the real tally, not zeros — and deliberately unlike the two
|
|
306
|
+
// fields above. A model latch describes a process, so on a dead session it
|
|
307
|
+
// would assert something false; a skip count describes what already
|
|
308
|
+
// happened, and `lastSkippedAt` dates it. Zeroing it here would erase the
|
|
309
|
+
// evidence at exactly the moment an operator goes looking for it — the
|
|
310
|
+
// session ended and the prompt they were waiting on was never saved.
|
|
311
|
+
promptDedup: (0, prompt_dedup_state_1.getPromptDedupSkips)(worktreeId, cliToolId, instanceId),
|
|
312
|
+
// Issue #1839: there is no frame to read, so there is nothing to report.
|
|
313
|
+
// Unlike `promptDedup` this is not a tally of what already happened — it
|
|
314
|
+
// is a claim about what is on screen right now, and a dead session has no
|
|
315
|
+
// screen.
|
|
316
|
+
upstreamFault: null,
|
|
303
317
|
};
|
|
304
318
|
}
|
|
305
319
|
const sessionState = (0, db_1.getSessionState)(db, worktreeId, resolvedInstanceId);
|
|
@@ -455,6 +469,10 @@ async function buildCurrentOutput(db, worktreeId, cliToolId, instanceId) {
|
|
|
455
469
|
});
|
|
456
470
|
}
|
|
457
471
|
const realtimeSnippet = lines.slice(-100).join('\n');
|
|
472
|
+
// Issue #1839: judged on exactly what is published as `realtimeSnippet`, not
|
|
473
|
+
// on `output`. The wider capture keeps a banner from an hour ago in scope, and
|
|
474
|
+
// a fault the operator cannot see in the payload next to it is unverifiable.
|
|
475
|
+
const upstreamFaultMatch = (0, upstream_faults_1.matchUpstreamFault)(realtimeSnippet);
|
|
458
476
|
const autoYesState = (0, auto_yes_manager_1.getAutoYesState)(worktreeId, cliToolId, instanceId);
|
|
459
477
|
// Issue #1785 + #1784: ONE resolution for both halves, not two readers.
|
|
460
478
|
//
|
|
@@ -489,6 +507,9 @@ async function buildCurrentOutput(db, worktreeId, cliToolId, instanceId) {
|
|
|
489
507
|
expiresAt: autoYesState?.enabled ? autoYesState.expiresAt : null,
|
|
490
508
|
stopReason: autoYesState?.stopReason,
|
|
491
509
|
lastSuppression: (0, auto_yes_suppression_state_1.getLastPolicySuppression)(worktreeId, cliToolId, instanceId),
|
|
510
|
+
// Issue #1694: undefined (so the key is absent from the JSON) unless a
|
|
511
|
+
// stop pattern actually fired — the state clears it on every other path.
|
|
512
|
+
stopMatchedText: autoYesState?.stopMatchedText,
|
|
492
513
|
},
|
|
493
514
|
isSelectionListActive,
|
|
494
515
|
isPagerActive,
|
|
@@ -501,5 +522,18 @@ async function buildCurrentOutput(db, worktreeId, cliToolId, instanceId) {
|
|
|
501
522
|
// docs on CurrentOutputPayload for why nothing is normalised on the way out.
|
|
502
523
|
model,
|
|
503
524
|
reasoningEffort: effort,
|
|
525
|
+
// Issue #1695: appended last on purpose — every field above is a published
|
|
526
|
+
// CLI contract and reordering them churns the diff for no reader's benefit.
|
|
527
|
+
promptDedup: (0, prompt_dedup_state_1.getPromptDedupSkips)(worktreeId, cliToolId, instanceId),
|
|
528
|
+
// Issue #1839: read from `realtimeSnippet`, the same rows an operator sees
|
|
529
|
+
// in `capture --json`, so what the field claims can be checked against what
|
|
530
|
+
// is printed next to it.
|
|
531
|
+
upstreamFault: upstreamFaultMatch
|
|
532
|
+
? {
|
|
533
|
+
id: upstreamFaultMatch.fault.id,
|
|
534
|
+
matchedText: upstreamFaultMatch.matchedText,
|
|
535
|
+
at: Date.now(),
|
|
536
|
+
}
|
|
537
|
+
: null,
|
|
504
538
|
};
|
|
505
539
|
}
|
|
@@ -38,6 +38,8 @@ const os_1 = require("os");
|
|
|
38
38
|
const path_1 = require("path");
|
|
39
39
|
const env_1 = require("../../lib/env");
|
|
40
40
|
const logger_1 = require("../../lib/logger");
|
|
41
|
+
const machine_lock_1 = require("./machine-lock");
|
|
42
|
+
const worktree_index_1 = require("./worktree-index");
|
|
41
43
|
const logger = (0, logger_1.createLogger)('lib/verification/env-snapshot');
|
|
42
44
|
/** Bumped only when a stored snapshot stops being readable by this module. */
|
|
43
45
|
exports.ENV_SNAPSHOT_VERSION = 1;
|
|
@@ -299,12 +301,25 @@ async function probeTmuxSessions(deps) {
|
|
|
299
301
|
exports.VOLATILE_ENTRY_PATTERNS = [/-wal$/, /-shm$/, /-journal$/, /^\.DS_Store$/];
|
|
300
302
|
/** This module's own storage, which must not read as something a task created. */
|
|
301
303
|
exports.ENV_SNAPSHOT_DIR_NAME = 'env-snapshots';
|
|
304
|
+
/**
|
|
305
|
+
* Coordination directories the verification runner itself creates under
|
|
306
|
+
* `~/.commandmate` (Issue #1771): the gate mutex locks and the worktree index
|
|
307
|
+
* registry. Neither is a side effect of the delegation — the first mutexed gate
|
|
308
|
+
* on a machine creates them — so counting them would report the runner's own
|
|
309
|
+
* bookkeeping as pollution the agent left behind.
|
|
310
|
+
*
|
|
311
|
+
* Scoped to the `~/.commandmate` probe rather than added to
|
|
312
|
+
* {@link isIgnoredEntry}: a `~/locks` directory in `$HOME` is not ours, and
|
|
313
|
+
* blanket-ignoring the name would put a hole in the probe that exists to catch
|
|
314
|
+
* exactly that.
|
|
315
|
+
*/
|
|
316
|
+
const COMMANDMATE_RUNTIME_ENTRY_NAMES = [machine_lock_1.MACHINE_LOCK_DIR_NAME, worktree_index_1.WORKTREE_INDEX_DIR_NAME];
|
|
302
317
|
function isIgnoredEntry(name) {
|
|
303
318
|
if (name === exports.ENV_SNAPSHOT_DIR_NAME)
|
|
304
319
|
return true;
|
|
305
320
|
return exports.VOLATILE_ENTRY_PATTERNS.some((pattern) => pattern.test(name));
|
|
306
321
|
}
|
|
307
|
-
function probeDirectory(deps, path, { missingIsEmpty }) {
|
|
322
|
+
function probeDirectory(deps, path, { missingIsEmpty, alsoIgnored = [] }) {
|
|
308
323
|
let names;
|
|
309
324
|
try {
|
|
310
325
|
names = deps.readDir(path);
|
|
@@ -318,7 +333,7 @@ function probeDirectory(deps, path, { missingIsEmpty }) {
|
|
|
318
333
|
return unavailable(`could not list ${path}: ${error.message}`);
|
|
319
334
|
}
|
|
320
335
|
return ok(names
|
|
321
|
-
.filter((name) => !isIgnoredEntry(name))
|
|
336
|
+
.filter((name) => !isIgnoredEntry(name) && !alsoIgnored.includes(name))
|
|
322
337
|
.map((name) => ({ key: name, detail: null, anchor: null })));
|
|
323
338
|
}
|
|
324
339
|
/** Entries directly under `$HOME`. A worker leaving `~/.commandmate-uat-1726` shows up here. */
|
|
@@ -327,7 +342,10 @@ function probeHomeEntries(deps) {
|
|
|
327
342
|
}
|
|
328
343
|
/** Entries directly under `~/.commandmate` — the state directory #1722 polluted. */
|
|
329
344
|
function probeCommandmateEntries(deps) {
|
|
330
|
-
return probeDirectory(deps, (0, path_1.join)(deps.homeDir(), '.commandmate'), {
|
|
345
|
+
return probeDirectory(deps, (0, path_1.join)(deps.homeDir(), '.commandmate'), {
|
|
346
|
+
missingIsEmpty: true,
|
|
347
|
+
alsoIgnored: COMMANDMATE_RUNTIME_ENTRY_NAMES,
|
|
348
|
+
});
|
|
331
349
|
}
|
|
332
350
|
/**
|
|
333
351
|
* Take one snapshot of all four probes.
|
|
@@ -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.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.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.startVerification = startVerification;
|
|
27
27
|
const child_process_1 = require("child_process");
|
|
@@ -39,6 +39,8 @@ const contract_message_1 = require("../../lib/tasks/contract-message");
|
|
|
39
39
|
const task_transition_service_1 = require("../../lib/tasks/task-transition-service");
|
|
40
40
|
const env_clean_gate_1 = require("./env-clean-gate");
|
|
41
41
|
const env_snapshot_1 = require("./env-snapshot");
|
|
42
|
+
const machine_lock_1 = require("./machine-lock");
|
|
43
|
+
const worktree_index_1 = require("./worktree-index");
|
|
42
44
|
const scope_gate_1 = require("./scope-gate");
|
|
43
45
|
const verify_config_1 = require("./verify-config");
|
|
44
46
|
Object.defineProperty(exports, "ENV_CLEAN_GATE_ID", { enumerable: true, get: function () { return verify_config_1.ENV_CLEAN_GATE_ID; } });
|
|
@@ -74,6 +76,85 @@ const ENV_CLEAN_GATE_COMMAND = 'lsof -iTCP -sTCP:LISTEN / tmux list-sessions / $
|
|
|
74
76
|
exports.MAX_CONCURRENT_VERIFICATIONS = 2;
|
|
75
77
|
/** Grace period between SIGTERM and SIGKILL for a gate that overran. */
|
|
76
78
|
const SIGKILL_GRACE_MS = 5000;
|
|
79
|
+
/**
|
|
80
|
+
* Per-worktree environment every command gate is given (Issue #1771).
|
|
81
|
+
*
|
|
82
|
+
* `CM_WORKTREE_INDEX` is what lets a repository write
|
|
83
|
+
* `E2E_PORT=$((60400+CM_WORKTREE_INDEX))` and stop N parallel worktrees from
|
|
84
|
+
* fighting over one port — removing the collision instead of serializing around
|
|
85
|
+
* it, which is the only option that keeps the parallelism. `CM_WORKTREE_ID` is
|
|
86
|
+
* for everything a number cannot name: a container name, a database prefix, a
|
|
87
|
+
* log directory.
|
|
88
|
+
*/
|
|
89
|
+
exports.WORKTREE_ID_ENV = 'CM_WORKTREE_ID';
|
|
90
|
+
exports.WORKTREE_INDEX_ENV = 'CM_WORKTREE_INDEX';
|
|
91
|
+
/**
|
|
92
|
+
* Marker line carrying a mutexed gate's wait into `log_tail` (Issue #1771).
|
|
93
|
+
*
|
|
94
|
+
* `verification_gate_results` has no column for the wait, and it must not be
|
|
95
|
+
* folded into `duration_ms`: the duration is what the gate's own command took,
|
|
96
|
+
* and adding another run's queueing to it corrupts the number every timeout
|
|
97
|
+
* budget and every "this gate got slower" judgement is made from. The log tail
|
|
98
|
+
* is the same channel work-evidence's counts and the scope gate's evidence
|
|
99
|
+
* already travel on (`src/cli/commands/verify.ts:60-99`).
|
|
100
|
+
*
|
|
101
|
+
* Mirrored by `MUTEX_LOG_PREFIX` in `src/cli/utils/verify-runner.ts`, which
|
|
102
|
+
* cannot import this module — `tsconfig.cli.json` compiles `src/cli/**` alone.
|
|
103
|
+
*/
|
|
104
|
+
exports.MUTEX_LOG_PREFIX = '[mutex]';
|
|
105
|
+
/**
|
|
106
|
+
* First line of a gate that never ran because the lock stayed held.
|
|
107
|
+
*
|
|
108
|
+
* Deliberately not `timeout`: the gate's command was never started, so there is
|
|
109
|
+
* nothing to say it ran long. Deliberately not `failed` either — the whole point
|
|
110
|
+
* of Issue #1771 is that a resource conflict and a broken change must stop
|
|
111
|
+
* reading the same. `skipped` aggregates the run to `error` (exit 99), which is
|
|
112
|
+
* "no verdict was reached", not "the work is bad".
|
|
113
|
+
*/
|
|
114
|
+
exports.MUTEX_WAIT_SKIP_REASON = 'reason=mutex-wait';
|
|
115
|
+
/**
|
|
116
|
+
* Marker line carrying a retried gate's two runs into `log_tail` (Issue #1772).
|
|
117
|
+
*
|
|
118
|
+
* `verification_gate_results` stores one status, one exit code and one duration
|
|
119
|
+
* per gate, and a DB migration was out of scope for #1772, so the second run's
|
|
120
|
+
* numbers travel the same way #1771's wait does: a line-anchored, machine
|
|
121
|
+
* readable first line the CLI reads back.
|
|
122
|
+
*
|
|
123
|
+
* Written whenever a retry actually ran — for `outcome=fail` too, not only for
|
|
124
|
+
* `outcome=flaky`. A gate that failed twice is evidence *against* flakiness,
|
|
125
|
+
* and a flake advisor mining history needs both halves of that ratio; a marker
|
|
126
|
+
* present only on the flaky half would make every retried gate look flaky.
|
|
127
|
+
*
|
|
128
|
+
* Mirrored by `FLAKY_LOG_PREFIX` in `src/cli/utils/verify-runner.ts`, which
|
|
129
|
+
* cannot import this module — `tsconfig.cli.json` compiles `src/cli/**` alone.
|
|
130
|
+
*/
|
|
131
|
+
exports.FLAKY_LOG_PREFIX = '[flaky]';
|
|
132
|
+
/**
|
|
133
|
+
* Runs a gate is allowed. Not derived from `retryOnFail` at the call site: the
|
|
134
|
+
* marker states the number so a reader of stored history never has to look up
|
|
135
|
+
* the verify.yaml that was in effect months ago.
|
|
136
|
+
*/
|
|
137
|
+
const RETRY_TOTAL_RUNS = 2;
|
|
138
|
+
/**
|
|
139
|
+
* Separator between the two runs' outputs inside the composed log tail.
|
|
140
|
+
*
|
|
141
|
+
* Both are kept. Keeping only one makes the single question this feature exists
|
|
142
|
+
* to answer — *what differed between the two runs* — unanswerable from the
|
|
143
|
+
* record. `maxLogTailBytes` is applied per run, because it caps what one gate
|
|
144
|
+
* command may contribute and there were two commands.
|
|
145
|
+
*/
|
|
146
|
+
function flakyRunHeader(index, outcome) {
|
|
147
|
+
return (`--- ${exports.FLAKY_LOG_PREFIX} run ${index}/${RETRY_TOTAL_RUNS}: ${outcome.status} ` +
|
|
148
|
+
`exit=${formatExit(outcome.exitCode)} duration=${formatSeconds(outcome.durationMs)} ---`);
|
|
149
|
+
}
|
|
150
|
+
/** One decimal second, the spelling both `waited=` and `duration=` use. */
|
|
151
|
+
function formatSeconds(ms) {
|
|
152
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
153
|
+
}
|
|
154
|
+
/** `exit_code` is null for a gate killed by a signal; say so rather than lie. */
|
|
155
|
+
function formatExit(exitCode) {
|
|
156
|
+
return exitCode === null ? 'n/a' : String(exitCode);
|
|
157
|
+
}
|
|
77
158
|
/** A run is already in flight for this worktree. Surfaces as HTTP 409. */
|
|
78
159
|
class VerificationConflictError extends Error {
|
|
79
160
|
worktreeId;
|
|
@@ -191,7 +272,7 @@ class TailBuffer {
|
|
|
191
272
|
* process group — killing only the shell leaves the `npm`/`vitest` grandchildren
|
|
192
273
|
* running and holding the worktree.
|
|
193
274
|
*/
|
|
194
|
-
function runCommand(command, cwd, timeoutSec, maxLogTailBytes) {
|
|
275
|
+
function runCommand(command, cwd, timeoutSec, maxLogTailBytes, gateEnv) {
|
|
195
276
|
return new Promise((resolve) => {
|
|
196
277
|
const startedAt = Date.now();
|
|
197
278
|
const tail = new TailBuffer(maxLogTailBytes);
|
|
@@ -203,8 +284,9 @@ function runCommand(command, cwd, timeoutSec, maxLogTailBytes) {
|
|
|
203
284
|
cwd,
|
|
204
285
|
// CI=true matches how the same commands run in the pipeline; vitest picks
|
|
205
286
|
// a different fileParallelism otherwise, and a gate that passes here but
|
|
206
|
-
// fails in CI is worse than no gate.
|
|
207
|
-
|
|
287
|
+
// fails in CI is worse than no gate. `gateEnv` carries the per-worktree
|
|
288
|
+
// identity (#1771) and is code-supplied, never user input.
|
|
289
|
+
env: { ...process.env, CI: 'true', ...gateEnv },
|
|
208
290
|
detached: true,
|
|
209
291
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
210
292
|
});
|
|
@@ -272,6 +354,158 @@ function runCommand(command, cwd, timeoutSec, maxLogTailBytes) {
|
|
|
272
354
|
});
|
|
273
355
|
});
|
|
274
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Prefix a gate's log with what it waited for, so the wait is on the record
|
|
359
|
+
* even when the command itself printed nothing.
|
|
360
|
+
*
|
|
361
|
+
* First line rather than last: the reader's question is "why did this gate take
|
|
362
|
+
* so long", and the answer belongs before the output it is about. The GATE line
|
|
363
|
+
* is built from the whole stored tail, so placement never decides whether
|
|
364
|
+
* `waited=` reaches the terminal.
|
|
365
|
+
*/
|
|
366
|
+
function withMutexMarker(name, waitedMs, lockPath, logTail) {
|
|
367
|
+
const marker = `${exports.MUTEX_LOG_PREFIX} name=${name} waited=${formatSeconds(waitedMs)} lock=${lockPath}`;
|
|
368
|
+
return prefixLogTail(marker, logTail);
|
|
369
|
+
}
|
|
370
|
+
/** The log of a gate whose lock never came free. */
|
|
371
|
+
function mutexWaitTimeoutLog(gate, name, result) {
|
|
372
|
+
if (result.acquired)
|
|
373
|
+
throw new Error('mutexWaitTimeoutLog called for an acquired lock');
|
|
374
|
+
const held = result.heldBy ? ` (held by ${result.heldBy})` : '';
|
|
375
|
+
return [
|
|
376
|
+
`${exports.MUTEX_WAIT_SKIP_REASON} waited=${formatSeconds(result.waitedMs)}`,
|
|
377
|
+
`${exports.MUTEX_LOG_PREFIX} name=${name} lock=${machineLockPathFor(name)}${held}`,
|
|
378
|
+
`Gate '${gate.id}' declares mutex '${name}' and the machine-wide lock stayed held for its ` +
|
|
379
|
+
`whole ${gate.timeoutSec}s budget, so the command was never started. This is a resource ` +
|
|
380
|
+
'conflict, not a verdict on the work: re-run once the other run finishes, or raise ' +
|
|
381
|
+
"the gate's timeoutSec.",
|
|
382
|
+
].join('\n');
|
|
383
|
+
}
|
|
384
|
+
/** Resolved separately from the acquisition so the message can name it either way. */
|
|
385
|
+
function machineLockPathFor(name) {
|
|
386
|
+
return (0, machine_lock_1.machineLockPath)(name, (0, machine_lock_1.resolveMachineLockRoot)());
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Run one attempt at a command gate, holding its `mutex` for the duration
|
|
390
|
+
* (Issue #1771).
|
|
391
|
+
*
|
|
392
|
+
* The wait happens *outside* the measured interval: `startedAt`/`durationMs`
|
|
393
|
+
* come from {@link runCommand} and describe the command alone, so a gate that
|
|
394
|
+
* queued for 42s and then ran for 190s records 190s and reports the 42s beside
|
|
395
|
+
* it. Merging them would inflate every duration by however busy the machine was.
|
|
396
|
+
*
|
|
397
|
+
* One *attempt*, not one gate, since #1772: a gate declaring `retryOnFail: 1`
|
|
398
|
+
* calls this twice. The lock is taken and released per attempt rather than held
|
|
399
|
+
* across both, so a retry never keeps a machine-wide resource out of another
|
|
400
|
+
* worktree's hands for a run that has already failed once.
|
|
401
|
+
*/
|
|
402
|
+
async function runGateAttempt(gate, worktreePath, maxLogTailBytes, gateEnv) {
|
|
403
|
+
const mutex = gate.mutex;
|
|
404
|
+
if (!mutex) {
|
|
405
|
+
return runCommand(gate.command, worktreePath, gate.timeoutSec, maxLogTailBytes, gateEnv);
|
|
406
|
+
}
|
|
407
|
+
// The gate's own timeout is the wait budget: a gate allowed 600s of execution
|
|
408
|
+
// has already declared how long this run may spend on it, and a second knob
|
|
409
|
+
// would only let the two disagree.
|
|
410
|
+
const lock = await (0, machine_lock_1.acquireMachineLock)(mutex, { timeoutMs: gate.timeoutSec * 1000 });
|
|
411
|
+
if (!lock.acquired) {
|
|
412
|
+
const recordedAt = Date.now();
|
|
413
|
+
return {
|
|
414
|
+
status: 'skipped',
|
|
415
|
+
exitCode: null,
|
|
416
|
+
startedAt: recordedAt,
|
|
417
|
+
durationMs: 0,
|
|
418
|
+
logTail: mutexWaitTimeoutLog(gate, mutex, lock),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const outcome = await runCommand(gate.command, worktreePath, gate.timeoutSec, maxLogTailBytes, gateEnv);
|
|
423
|
+
return {
|
|
424
|
+
...outcome,
|
|
425
|
+
logTail: withMutexMarker(mutex, lock.waitedMs, lock.handle.path, outcome.logTail),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
finally {
|
|
429
|
+
lock.handle.release();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Run one command gate, re-running it once when it fails and the gate asked for
|
|
434
|
+
* that (Issue #1772).
|
|
435
|
+
*
|
|
436
|
+
* A gate that fails on the machine's luck — a random UUID that happens to
|
|
437
|
+
* contain a forbidden substring, a port that was still in TIME_WAIT — is
|
|
438
|
+
* indistinguishable in the record from a gate that fails on the work, and
|
|
439
|
+
* "re-run the one red gate before believing it" has been tribal knowledge
|
|
440
|
+
* rather than something the runner does. This makes it a declaration.
|
|
441
|
+
*
|
|
442
|
+
* Only a `failed` attempt is retried. A `timeout` is not: the gate already
|
|
443
|
+
* spent its whole budget, and a second attempt would double the wall clock of
|
|
444
|
+
* exactly the gates whose budgets are largest. A `skipped` (the mutex never
|
|
445
|
+
* came free) and an `error` (the command could not be spawned) never started a
|
|
446
|
+
* command, so there is no verdict to seek a second opinion on.
|
|
447
|
+
*/
|
|
448
|
+
async function runCommandGate(gate, worktreePath, maxLogTailBytes, gateEnv) {
|
|
449
|
+
const first = await runGateAttempt(gate, worktreePath, maxLogTailBytes, gateEnv);
|
|
450
|
+
if (gate.retryOnFail !== 1 || first.status !== 'failed')
|
|
451
|
+
return first;
|
|
452
|
+
const second = await runGateAttempt(gate, worktreePath, maxLogTailBytes, gateEnv);
|
|
453
|
+
if (second.status !== 'passed' && second.status !== 'failed') {
|
|
454
|
+
// The retry reached no verdict of its own, so there is nothing to compare
|
|
455
|
+
// the first run against. The first run's FAIL stands unchanged — reporting
|
|
456
|
+
// the retry's `skipped`/`timeout` instead would turn a gate that judged the
|
|
457
|
+
// work into a run with no verdict (exit 99), which is strictly weaker.
|
|
458
|
+
return {
|
|
459
|
+
...first,
|
|
460
|
+
logTail: prefixLogTail(`${exports.FLAKY_LOG_PREFIX} retry reached no verdict (${second.status}); ` +
|
|
461
|
+
"the first run's result stands. Retry log:\n" +
|
|
462
|
+
(second.logTail ?? '(no output)'), first.logTail),
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
return composeRetriedGate(gate, first, second);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Fold two runs of one gate into the single row the schema has (Issue #1772).
|
|
469
|
+
*
|
|
470
|
+
* `status` and `exitCode` come from the run whose verdict is being reported, so
|
|
471
|
+
* the row never says `failed` beside `exit=0`: a FLAKY gate counted as a
|
|
472
|
+
* failure reports the failing run, one counted as a pass reports the passing
|
|
473
|
+
* one, and a gate that failed twice reports the later failure. `durationMs` is
|
|
474
|
+
* the sum, because both runs were this gate's own command executing — the same
|
|
475
|
+
* rule #1771 applied when it kept the lock *wait* out of the number.
|
|
476
|
+
*
|
|
477
|
+
* Both runs' numbers reach the terminal through the {@link FLAKY_LOG_PREFIX}
|
|
478
|
+
* marker, which the CLI reads to print `GATE <id> FLAKY (exit=1,0, 45.0s,44.0s)`.
|
|
479
|
+
*/
|
|
480
|
+
function composeRetriedGate(gate, first, second) {
|
|
481
|
+
const outcome = second.status === 'passed' ? 'flaky' : 'fail';
|
|
482
|
+
const passes = outcome === 'flaky' && gate.flakyIsPass === true;
|
|
483
|
+
const reported = passes ? second : outcome === 'flaky' ? first : second;
|
|
484
|
+
const marker = `${exports.FLAKY_LOG_PREFIX} runs=${RETRY_TOTAL_RUNS} outcome=${outcome} ` +
|
|
485
|
+
`exit=${formatExit(first.exitCode)},${formatExit(second.exitCode)} ` +
|
|
486
|
+
`duration=${formatSeconds(first.durationMs)},${formatSeconds(second.durationMs)} ` +
|
|
487
|
+
`verdict=${passes ? 'pass' : 'fail'}`;
|
|
488
|
+
const body = [
|
|
489
|
+
flakyRunHeader(1, first),
|
|
490
|
+
first.logTail ?? '',
|
|
491
|
+
flakyRunHeader(2, second),
|
|
492
|
+
second.logTail ?? '',
|
|
493
|
+
].join('\n');
|
|
494
|
+
return {
|
|
495
|
+
status: passes ? 'passed' : 'failed',
|
|
496
|
+
exitCode: reported.exitCode,
|
|
497
|
+
// The window opens where the first run opened and closes `durationMs`
|
|
498
|
+
// later, so #1625's `finished_at - started_at === duration_ms` still holds
|
|
499
|
+
// and the pair still describes execution rather than the database write.
|
|
500
|
+
startedAt: first.startedAt,
|
|
501
|
+
durationMs: first.durationMs + second.durationMs,
|
|
502
|
+
logTail: `${marker}\n${body}`,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
/** Put a machine-readable line ahead of a gate's output, keeping both. */
|
|
506
|
+
function prefixLogTail(marker, logTail) {
|
|
507
|
+
return logTail ? `${marker}\n${logTail}` : marker;
|
|
508
|
+
}
|
|
275
509
|
/** Run a git plumbing command. No shell: every argument here is code-supplied. */
|
|
276
510
|
function runGit(args, cwd) {
|
|
277
511
|
return new Promise((resolve) => {
|
|
@@ -483,12 +717,29 @@ function aggregateRunStatus(gateStatuses) {
|
|
|
483
717
|
return 'error';
|
|
484
718
|
return 'passed';
|
|
485
719
|
}
|
|
486
|
-
async function executeRun(db, runId, worktreePath, config, selection, baseRef, task, detachedContract, envClean) {
|
|
720
|
+
async function executeRun(db, runId, worktreeId, worktreePath, config, selection, baseRef, task, detachedContract, envClean) {
|
|
487
721
|
const { maxLogTailBytes, skipInPrimaryCheckout } = config.options;
|
|
488
722
|
const { runWorkEvidence, gates } = selection;
|
|
489
723
|
// ORed with the repository-wide switch rather than replacing it (#1642): a
|
|
490
724
|
// delegation may tighten the rule, never relax one the repository declared.
|
|
491
725
|
const requireCommit = (0, contract_message_1.resolveRequireCommit)(task?.contract ?? null, config);
|
|
726
|
+
/**
|
|
727
|
+
* The per-worktree environment, resolved on first use (#1771).
|
|
728
|
+
*
|
|
729
|
+
* Lazy because claiming an index writes to `~/.commandmate/worktree-index`,
|
|
730
|
+
* and a run that reaches no command gate — work-evidence failed, the primary
|
|
731
|
+
* checkout guard skipped everything — must leave nothing behind. It also has
|
|
732
|
+
* to stay behind the env-clean gate below, which lists `~/.commandmate` and
|
|
733
|
+
* would otherwise diff against a directory this very run had just created.
|
|
734
|
+
*/
|
|
735
|
+
let gateEnvCache = null;
|
|
736
|
+
const gateEnv = () => {
|
|
737
|
+
gateEnvCache ??= {
|
|
738
|
+
[exports.WORKTREE_ID_ENV]: worktreeId,
|
|
739
|
+
[exports.WORKTREE_INDEX_ENV]: String((0, worktree_index_1.resolveWorktreeIndex)(worktreeId)),
|
|
740
|
+
};
|
|
741
|
+
return gateEnvCache;
|
|
742
|
+
};
|
|
492
743
|
/**
|
|
493
744
|
* Close an open gate row with the interval its evaluator measured.
|
|
494
745
|
*
|
|
@@ -595,7 +846,7 @@ async function executeRun(db, runId, worktreePath, config, selection, baseRef, t
|
|
|
595
846
|
'options.skipInPrimaryCheckout is true.').status);
|
|
596
847
|
continue;
|
|
597
848
|
}
|
|
598
|
-
const outcome = await runGate(gate.id, gate.command, gate.source, () =>
|
|
849
|
+
const outcome = await runGate(gate.id, gate.command, gate.source, () => runCommandGate(gate, worktreePath, maxLogTailBytes, gateEnv()));
|
|
599
850
|
statuses.push(outcome.status);
|
|
600
851
|
}
|
|
601
852
|
return aggregateRunStatus(statuses);
|
|
@@ -813,7 +1064,7 @@ async function startVerification(input) {
|
|
|
813
1064
|
await acquireSlot();
|
|
814
1065
|
let terminalStatus = 'error';
|
|
815
1066
|
try {
|
|
816
|
-
const status = await executeRun(db, run.id, input.worktreePath, resolvedConfig, resolvedSelection, baseRef, task, detachedContract, envCleanContext);
|
|
1067
|
+
const status = await executeRun(db, run.id, input.worktreeId, input.worktreePath, resolvedConfig, resolvedSelection, baseRef, task, detachedContract, envCleanContext);
|
|
817
1068
|
terminalStatus = status;
|
|
818
1069
|
(0, db_1.finishVerificationRun)(db, run.id, status);
|
|
819
1070
|
return status;
|