commandmate 0.45.1 → 0.46.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 +302 -298
- package/.next/app-path-routes-manifest.json +15 -15
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +9 -9
- package/.next/react-loadable-manifest.json +4 -4
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +3 -3
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +3 -3
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +3 -3
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route.js +3 -3
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route.js +3 -3
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page.js +1 -1
- package/.next/server/app/more/page.js.nft.json +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page.js +1 -1
- package/.next/server/app/offline/page.js.nft.json +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page.js +2 -2
- package/.next/server/app/repositories/page.js.nft.json +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page.js +1 -1
- package/.next/server/app/review/page.js.nft.json +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page.js +1 -1
- package/.next/server/app/skills/installed/page.js.nft.json +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page.js +1 -1
- package/.next/server/app/skills/page.js.nft.json +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +12 -9
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +15 -15
- package/.next/server/chunks/1341.js +1 -1
- package/.next/server/chunks/1501.js +2 -2
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2283.js +15 -13
- package/.next/server/chunks/2404.js +4 -4
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2560.js +1 -1
- package/.next/server/chunks/3543.js +1 -1
- package/.next/server/chunks/3945.js +1 -1
- package/.next/server/chunks/4072.js +15 -0
- package/.next/server/chunks/4207.js +1 -1
- package/.next/server/chunks/4718.js +3 -3
- package/.next/server/chunks/4837.js +3 -3
- package/.next/server/chunks/5625.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6345.js +1 -0
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6722.js +1 -0
- package/.next/server/chunks/8098.js +1 -1
- package/.next/server/chunks/8356.js +1 -1
- package/.next/server/chunks/8581.js +25 -29
- package/.next/server/chunks/8728.js +1 -1
- package/.next/server/chunks/8800.js +4 -4
- package/.next/server/chunks/9731.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages/_error.js +2 -2
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{2407-86890b00eeecf391.js → 2407-acb644c5487e1720.js} +1 -1
- package/.next/static/chunks/{2619-a305ad4810f1393a.js → 2619-04bc32f026a0d946.js} +1 -1
- package/.next/static/chunks/2717.21abf5b6bbd45520.js +1 -0
- package/.next/static/chunks/2770-db80aedf6384d594.js +1 -0
- package/.next/static/chunks/2863-6a07ec82fb52f4dd.js +1 -0
- package/.next/static/chunks/3201-c01f1feed69020e6.js +1 -0
- package/.next/static/chunks/4768-ad6f82f034f21eae.js +1 -0
- package/.next/static/chunks/5371-a94ed618148287a0.js +1 -0
- package/.next/static/chunks/5794-947fb4934d8be193.js +1 -0
- package/.next/static/chunks/5927-54315d21f323e204.js +1 -0
- package/.next/static/chunks/602-25e45a2422d2a3ea.js +1 -0
- package/.next/static/chunks/6606-02ddc006686a6b7d.js +1 -0
- package/.next/static/chunks/6654-f3534f35f16644bc.js +1 -0
- package/.next/static/chunks/7730-011e137be64403ae.js +1 -0
- package/.next/static/chunks/8165-263172c17ae8e8c3.js +1 -0
- package/.next/static/chunks/8606-cf6bdbff44a764cf.js +1 -0
- package/.next/static/chunks/934-a37509667baa86ee.js +1 -0
- package/.next/static/chunks/9465-e5a351baa941f31b.js +1 -0
- package/.next/static/chunks/app/layout-ae30ce63569c22be.js +1 -0
- package/.next/static/chunks/app/repositories/page-71f5a5ac29b0370d.js +1 -0
- package/.next/static/chunks/app/review/page-e2fd4a590646a2d8.js +1 -0
- package/.next/static/chunks/app/sessions/page-9859ed61e93e6890.js +1 -0
- package/.next/static/chunks/app/skills/[skillId]/page-52ba58712faf3378.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-2eb1d0fd0a077b7e.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/{page-c601f3b3ece6557e.js → page-373ea37b16d930c7.js} +1 -1
- package/.next/static/chunks/{webpack-ee3ccbe378c4aa06.js → webpack-c0b821f340afdebf.js} +1 -1
- package/.next/static/css/743f79ff19ab91e9.css +1 -0
- package/.next/trace +13 -13
- package/dist/server/locales/en/worktree.json +46 -16
- package/dist/server/locales/ja/worktree.json +46 -16
- package/dist/server/src/config/activity-bar-config.js +56 -9
- package/dist/server/src/lib/polling/prompt-dedup-state.js +26 -7
- package/dist/server/src/lib/polling/prompt-dedup.js +50 -22
- package/dist/server/src/lib/polling/response-checker.js +14 -1
- package/dist/server/src/lib/polling/response-dedup.js +104 -20
- package/dist/server/src/lib/polling/response-poller-core.js +47 -26
- package/dist/server/src/lib/session/next-action-helper.js +2 -15
- package/dist/server/src/lib/verification/env-clean-gate.js +14 -1
- package/dist/server/src/lib/verification/gate-runner.js +17 -11
- package/dist/server/src/lib/verification/scope-gate.js +169 -3
- package/dist/server/src/types/sidebar.js +1 -0
- package/docs/en/architecture.md +26 -14
- package/docs/en/user-guide/webapp-guide.md +31 -18
- package/package.json +1 -1
- package/release-notes/0.46.0.json +110 -0
- package/.next/server/chunks/1674.js +0 -15
- package/.next/server/chunks/6927.js +0 -1
- package/.next/server/chunks/9355.js +0 -1
- package/.next/static/chunks/1642-0f1bf8fb8161e10a.js +0 -1
- package/.next/static/chunks/2770-827a64760bab8a3a.js +0 -1
- package/.next/static/chunks/3922.0bc2e4e168d2b522.js +0 -1
- package/.next/static/chunks/4727-4c69d1aa4118cc09.js +0 -1
- package/.next/static/chunks/4768-fc0aa1d0891cac08.js +0 -1
- package/.next/static/chunks/5371-af1c2b68e983da61.js +0 -1
- package/.next/static/chunks/5521-c358c31f60e5e689.js +0 -1
- package/.next/static/chunks/6192-a67df090838b9aad.js +0 -1
- package/.next/static/chunks/6322-f1b30ed0ee43518f.js +0 -1
- package/.next/static/chunks/6654-ac431270fac1a283.js +0 -1
- package/.next/static/chunks/8165-3525fb42f1b4976e.js +0 -1
- package/.next/static/chunks/8318-01e7d1e5ad0a15a3.js +0 -1
- package/.next/static/chunks/8606-79c36d4c204bbd38.js +0 -1
- package/.next/static/chunks/9619-6c17a271f7f98fbb.js +0 -1
- package/.next/static/chunks/998-2eb5883186abd0bd.js +0 -1
- package/.next/static/chunks/app/layout-eaa6365f8f942e88.js +0 -1
- package/.next/static/chunks/app/repositories/page-6f7511f4ac8449e2.js +0 -1
- package/.next/static/chunks/app/review/page-95f749caae382f46.js +0 -1
- package/.next/static/chunks/app/sessions/page-ebfed2c80e916fb8.js +0 -1
- package/.next/static/chunks/app/skills/[skillId]/page-e4b14031f946c007.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-3372b78fde232b1f.js +0 -1
- package/.next/static/css/6faeb04a0b6819d4.css +0 -1
- /package/.next/static/{vYIpFRCU_Mt0wtG8F_3-6 → zI2nQ6WKboSbFbMF4EtrJ}/_buildManifest.js +0 -0
- /package/.next/static/{vYIpFRCU_Mt0wtG8F_3-6 → zI2nQ6WKboSbFbMF4EtrJ}/_ssgManifest.js +0 -0
|
@@ -55,11 +55,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
55
55
|
exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL = exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = void 0;
|
|
56
56
|
exports.isDuplicateResponse = isDuplicateResponse;
|
|
57
57
|
exports.claimDuplicateResponseSkipLog = claimDuplicateResponseSkipLog;
|
|
58
|
+
exports.resetDuplicateResponseSkipStreak = resetDuplicateResponseSkipStreak;
|
|
58
59
|
exports.markStructuredHistoryRecheckPending = markStructuredHistoryRecheckPending;
|
|
59
60
|
exports.settleStructuredHistoryRecheck = settleStructuredHistoryRecheck;
|
|
60
61
|
exports.claimStructuredHistoryRecheck = claimStructuredHistoryRecheck;
|
|
61
62
|
exports.clearResponseHashCache = clearResponseHashCache;
|
|
62
63
|
exports.renameResponseHashCacheKey = renameResponseHashCacheKey;
|
|
64
|
+
exports.renameResponseHashCacheKeys = renameResponseHashCacheKeys;
|
|
63
65
|
const crypto_1 = require("crypto");
|
|
64
66
|
const global_state_1 = require("../global-state");
|
|
65
67
|
const responseHashCache = (0, global_state_1.getOrInitGlobal)('__responseHashCache', () => new Map());
|
|
@@ -97,7 +99,9 @@ exports.STRUCTURED_HISTORY_RECHECK_TICK_INTERVAL = 3;
|
|
|
97
99
|
*
|
|
98
100
|
* On `globalThis` for the same reason `responseHashCache` is, and cleared and
|
|
99
101
|
* moved at exactly the same moments — see the module comment. A new
|
|
100
|
-
* (non-duplicate) response restarts it in {@link isDuplicateResponse}
|
|
102
|
+
* (non-duplicate) response restarts it in {@link isDuplicateResponse}, and a
|
|
103
|
+
* tick that reads the frame and finds no reply on it ends the run without
|
|
104
|
+
* touching the hash — see {@link resetDuplicateResponseSkipStreak} (#3552).
|
|
101
105
|
*/
|
|
102
106
|
const duplicateResponseSkipStreak = (0, global_state_1.getOrInitGlobal)('__duplicateResponseSkipStreak', () => new Map());
|
|
103
107
|
/**
|
|
@@ -148,6 +152,28 @@ function claimDuplicateResponseSkipLog(pollerKey) {
|
|
|
148
152
|
const suppressed = consecutive === 1 ? 0 : exports.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL - 1;
|
|
149
153
|
return { log, consecutive, suppressed };
|
|
150
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* End the current run of duplicate response ticks for this pollerKey without
|
|
157
|
+
* touching the cached hash (Issue #3552).
|
|
158
|
+
*
|
|
159
|
+
* Called by `response-checker` on the ticks that read the frame and decide it
|
|
160
|
+
* is not a duplicate of the saved reply: an unfinished frame
|
|
161
|
+
* (`extractCompletedTurn`), a finished frame with a live prompt on it, and a
|
|
162
|
+
* finished frame whose response is empty. The reply is no longer on screen, so
|
|
163
|
+
* if the same reply comes back the next duplicate is the first of a new run and
|
|
164
|
+
* logs. The hash stays: whether the returning reply is saved again is the
|
|
165
|
+
* guard's question (#1268), and this only thins the log line (#3519).
|
|
166
|
+
*
|
|
167
|
+
* Not called at the entrance of a finished frame — that would make every
|
|
168
|
+
* duplicate tick the first of its run — nor on a tick that skipped extraction
|
|
169
|
+
* on the line count (null) or returned on the cursor: neither says the reply
|
|
170
|
+
* has left the screen (the same judgement as #3538).
|
|
171
|
+
*
|
|
172
|
+
* @param pollerKey - Poller key ("worktreeId:instanceId")
|
|
173
|
+
*/
|
|
174
|
+
function resetDuplicateResponseSkipStreak(pollerKey) {
|
|
175
|
+
duplicateResponseSkipStreak.delete(pollerKey);
|
|
176
|
+
}
|
|
151
177
|
/**
|
|
152
178
|
* Note that the transcript reader has unfinished business for this pollerKey
|
|
153
179
|
* (Issue #2399).
|
|
@@ -233,6 +259,55 @@ function clearResponseHashCache(pollerKey) {
|
|
|
233
259
|
// Issue #3519: the skip count is about the same hash.
|
|
234
260
|
duplicateResponseSkipStreak.delete(pollerKey);
|
|
235
261
|
}
|
|
262
|
+
/** Remove everything held under `key` and return it. */
|
|
263
|
+
function takeResponseDedupState(key) {
|
|
264
|
+
const state = {
|
|
265
|
+
hash: responseHashCache.get(key),
|
|
266
|
+
recheck: structuredHistoryRecheckCache.get(key),
|
|
267
|
+
streak: duplicateResponseSkipStreak.get(key),
|
|
268
|
+
};
|
|
269
|
+
responseHashCache.delete(key);
|
|
270
|
+
structuredHistoryRecheckCache.delete(key);
|
|
271
|
+
duplicateResponseSkipStreak.delete(key);
|
|
272
|
+
return state;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Write state lifted by {@link takeResponseDedupState} under `key`.
|
|
276
|
+
*
|
|
277
|
+
* `replaceRecheckWithHash` is the one difference between the batch and the
|
|
278
|
+
* single rename (Issue #3553): when a hash lands, the batch also replaces the
|
|
279
|
+
* recheck under `key` — deleting it when the source had none — so a recheck
|
|
280
|
+
* owed for a different turn is not attached to the hash that just arrived. The
|
|
281
|
+
* single rename keeps its earlier behaviour and leaves such a recheck alone.
|
|
282
|
+
*/
|
|
283
|
+
function putResponseDedupState(key, state, replaceRecheckWithHash) {
|
|
284
|
+
if (state.hash !== undefined)
|
|
285
|
+
responseHashCache.set(key, state.hash);
|
|
286
|
+
// Issue #2399: the recheck caveat moves with the hash it is about. Leaving it
|
|
287
|
+
// behind would strand a turn whose transcript is still open — the new key
|
|
288
|
+
// starts owing nothing, so every later tick returns at the dedup guard and the
|
|
289
|
+
// Markdown row is never written. It moves without a hash too: the
|
|
290
|
+
// `lineCountIsCursor` path marks it with no hash cached.
|
|
291
|
+
if (state.recheck !== undefined) {
|
|
292
|
+
structuredHistoryRecheckCache.set(key, state.recheck);
|
|
293
|
+
}
|
|
294
|
+
else if (replaceRecheckWithHash && state.hash !== undefined) {
|
|
295
|
+
structuredHistoryRecheckCache.delete(key);
|
|
296
|
+
}
|
|
297
|
+
// Issue #3519: the skip count moves with the hash too, so a rename mid-run
|
|
298
|
+
// does not restart the thinning and log as if a new run had begun.
|
|
299
|
+
// Issue #3552: whenever the hash under `newKey` is replaced, so is its count
|
|
300
|
+
// — a count left over from the hash that was there before is about a
|
|
301
|
+
// different reply (the same rule as `renamePromptHashCacheKey`, #3538).
|
|
302
|
+
if (state.hash === undefined)
|
|
303
|
+
return;
|
|
304
|
+
if (state.streak !== undefined) {
|
|
305
|
+
duplicateResponseSkipStreak.set(key, state.streak);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
duplicateResponseSkipStreak.delete(key);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
236
311
|
/**
|
|
237
312
|
* Move the cached hash from one pollerKey to another.
|
|
238
313
|
*
|
|
@@ -241,7 +316,8 @@ function clearResponseHashCache(pollerKey) {
|
|
|
241
316
|
* during THIS turn?", and the turn does not end just because the ID moved —
|
|
242
317
|
* dropping it would re-save the screen currently on display.
|
|
243
318
|
*
|
|
244
|
-
* No-op when nothing is cached under `oldKey`.
|
|
319
|
+
* No-op when nothing is cached under `oldKey`. A batch of renames whose keys
|
|
320
|
+
* overlap must go through {@link renameResponseHashCacheKeys} instead.
|
|
245
321
|
*
|
|
246
322
|
* @param oldKey - Poller key the cache entry lives under
|
|
247
323
|
* @param newKey - Poller key it should live under
|
|
@@ -249,22 +325,30 @@ function clearResponseHashCache(pollerKey) {
|
|
|
249
325
|
function renameResponseHashCacheKey(oldKey, newKey) {
|
|
250
326
|
if (oldKey === newKey)
|
|
251
327
|
return;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
328
|
+
putResponseDedupState(newKey, takeResponseDedupState(oldKey), false);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Move the response dedup state of a whole batch of pollerKeys (Issue #3553).
|
|
332
|
+
*
|
|
333
|
+
* Every source key is lifted before any destination is written, so a swap
|
|
334
|
+
* (A→B, B→A) or a chain (A→B→C) cannot overwrite a source that has not moved
|
|
335
|
+
* yet — calling {@link renameResponseHashCacheKey} once per pair would leave
|
|
336
|
+
* A with its own state and B empty after a swap. Each destination ends up with
|
|
337
|
+
* exactly what its source held: hash, skip count and recheck, the recheck
|
|
338
|
+
* moving even without a hash. A recheck already at the destination is
|
|
339
|
+
* replaced along with the hash, never kept beside the moved one.
|
|
340
|
+
*
|
|
341
|
+
* Pairs with `oldKey === newKey` are ignored.
|
|
342
|
+
*
|
|
343
|
+
* @param renames - Poller key moves applied together
|
|
344
|
+
*/
|
|
345
|
+
function renameResponseHashCacheKeys(renames) {
|
|
346
|
+
const lifted = [];
|
|
347
|
+
for (const { oldKey, newKey } of renames) {
|
|
348
|
+
if (oldKey === newKey)
|
|
349
|
+
continue;
|
|
350
|
+
lifted.push({ newKey, state: takeResponseDedupState(oldKey) });
|
|
351
|
+
}
|
|
352
|
+
for (const { newKey, state } of lifted)
|
|
353
|
+
putResponseDedupState(newKey, state, true);
|
|
270
354
|
}
|
|
@@ -532,8 +532,11 @@ function splitPollerKey(pollerKey) {
|
|
|
532
532
|
* one in-flight TUI response may be truncated at the rename boundary; the next
|
|
533
533
|
* turn accumulates normally.
|
|
534
534
|
*
|
|
535
|
-
* Takes the whole batch because IDs can swap (A→B, B→A)
|
|
536
|
-
* removed before any destination key is written.
|
|
535
|
+
* Takes the whole batch because IDs can swap (A→B, B→A) or chain (A→B→C):
|
|
536
|
+
* every source key is removed before any destination key is written. That
|
|
537
|
+
* includes the dedup state (Issue #3553) — of running pollers and paused chains
|
|
538
|
+
* alike — which is lifted off every source key in one pass and only then
|
|
539
|
+
* written, so a pair handled early cannot overwrite a source handled later.
|
|
537
540
|
*
|
|
538
541
|
* @param renames - Worktree ID pairs being applied
|
|
539
542
|
* @param resolveCliToolId - Maps a poller's (worktree, instance) back to its CLI
|
|
@@ -596,33 +599,21 @@ function migrateResponsePollerWorktreeIds(renames, resolveCliToolId) {
|
|
|
596
599
|
coordinator.pendingRestart.delete(move.oldKey);
|
|
597
600
|
carried.set(move.newKey, { startedAt: exports.pollingStartTimes.get(move.oldKey) });
|
|
598
601
|
exports.pollingStartTimes.delete(move.oldKey);
|
|
599
|
-
(0, prompt_dedup_1.renamePromptHashCacheKey)(move.oldKey, move.newKey);
|
|
600
|
-
(0, response_dedup_1.renameResponseHashCacheKey)(move.oldKey, move.newKey);
|
|
601
|
-
// Issue #1695: the prompt hash moved on the line above, so the guard keeps
|
|
602
|
-
// suppressing the prompt on screen under the new ID. Its tally has to move
|
|
603
|
-
// with it, or `capture --json` reports zero skips for a session that is
|
|
604
|
-
// still skipping. Old worktree ID comes back out of the key we just left.
|
|
605
|
-
const movedFrom = splitPollerKey(move.oldKey);
|
|
606
|
-
if (movedFrom) {
|
|
607
|
-
(0, prompt_dedup_state_1.renamePromptDedupSkips)(movedFrom.worktreeId, move.newWorktreeId, move.cliToolId, move.instanceId);
|
|
608
|
-
}
|
|
609
602
|
(0, tui_accumulator_1.clearTuiAccumulator)(move.oldKey);
|
|
610
603
|
}
|
|
604
|
+
// Stop the abandoned pollers before any dedup state is written: the stop
|
|
605
|
+
// clears every cache under its key, and that key can be the destination of
|
|
606
|
+
// another move in this batch (A:x paused → B:x while B:x is abandoned).
|
|
607
|
+
// Stopped afterwards, it erased the state that had just arrived (#3553).
|
|
608
|
+
// An abandoned key is never a move source, so nothing still to be moved is
|
|
609
|
+
// lost here; and the paused chains are collected after it, as before, so a
|
|
610
|
+
// pause the stop ends is not carried.
|
|
611
611
|
for (const pollerKey of abandoned) {
|
|
612
612
|
stopPollingByKey(pollerKey);
|
|
613
613
|
logger.warn('poller:migrate-abandoned', { pollerKey });
|
|
614
614
|
}
|
|
615
|
-
//
|
|
616
|
-
for
|
|
617
|
-
const startedAt = carried.get(move.newKey)?.startedAt;
|
|
618
|
-
exports.pollingStartTimes.set(move.newKey, startedAt ?? Date.now());
|
|
619
|
-
if (isFullScreenTui(move.cliToolId)) {
|
|
620
|
-
(0, tui_accumulator_1.initTuiAccumulator)(move.newKey);
|
|
621
|
-
}
|
|
622
|
-
scheduleNextResponsePoll(move.newWorktreeId, move.cliToolId, move.instanceId);
|
|
623
|
-
}
|
|
624
|
-
// Issue #2230: a chain paused on a prompt has no timer, so the loop above
|
|
625
|
-
// never sees it — but it is holding the response hash for the turn it paused
|
|
615
|
+
// Issue #2230: a chain paused on a prompt has no timer, so the loops here
|
|
616
|
+
// never see it — but it is holding the response hash for the turn it paused
|
|
626
617
|
// on, and `/respond` will resume it under the NEW ID. Move the hash and the
|
|
627
618
|
// pause mark with it; the chain itself stays paused until that resume.
|
|
628
619
|
const pausedMoves = [];
|
|
@@ -635,11 +626,41 @@ function migrateResponsePollerWorktreeIds(renames, resolveCliToolId) {
|
|
|
635
626
|
continue;
|
|
636
627
|
pausedMoves.push({ oldKey: pollerKey, newKey: `${newWorktreeId}:${parts.instanceId}` });
|
|
637
628
|
}
|
|
629
|
+
// Issue #3553: the dedup state of the whole batch moves in one call per map
|
|
630
|
+
// family, each lifting every source before writing any destination. Re-keyed
|
|
631
|
+
// pair by pair, a swap left A with its own state and B empty.
|
|
632
|
+
(0, prompt_dedup_1.renamePromptHashCacheKeys)(moves);
|
|
633
|
+
(0, response_dedup_1.renameResponseHashCacheKeys)([...moves, ...pausedMoves]);
|
|
634
|
+
// Issue #1695: the prompt hash moved above, so the guard keeps suppressing
|
|
635
|
+
// the prompt on screen under the new ID. Its tally has to move with it, or
|
|
636
|
+
// `capture --json` reports zero skips for a session that is still skipping.
|
|
637
|
+
// Old worktree ID comes back out of the key being left.
|
|
638
|
+
(0, prompt_dedup_state_1.renamePromptDedupSkipsBatch)(moves.flatMap((move) => {
|
|
639
|
+
const movedFrom = splitPollerKey(move.oldKey);
|
|
640
|
+
return movedFrom
|
|
641
|
+
? [{
|
|
642
|
+
oldWorktreeId: movedFrom.worktreeId,
|
|
643
|
+
newWorktreeId: move.newWorktreeId,
|
|
644
|
+
cliToolId: move.cliToolId,
|
|
645
|
+
instanceId: move.instanceId,
|
|
646
|
+
}]
|
|
647
|
+
: [];
|
|
648
|
+
}));
|
|
649
|
+
// Phase 2: restart each poller under its new identity.
|
|
650
|
+
for (const move of moves) {
|
|
651
|
+
const startedAt = carried.get(move.newKey)?.startedAt;
|
|
652
|
+
exports.pollingStartTimes.set(move.newKey, startedAt ?? Date.now());
|
|
653
|
+
if (isFullScreenTui(move.cliToolId)) {
|
|
654
|
+
(0, tui_accumulator_1.initTuiAccumulator)(move.newKey);
|
|
655
|
+
}
|
|
656
|
+
scheduleNextResponsePoll(move.newWorktreeId, move.cliToolId, move.instanceId);
|
|
657
|
+
}
|
|
658
|
+
// Issue #2230: the pause marks of the chains collected above follow their
|
|
659
|
+
// hashes (moved with the batch). All removed before any is added, for the
|
|
660
|
+
// same swap reason.
|
|
638
661
|
for (const move of pausedMoves)
|
|
639
662
|
coordinator.pausedOnPrompt.delete(move.oldKey);
|
|
640
|
-
for (const move of pausedMoves)
|
|
641
|
-
(0, response_dedup_1.renameResponseHashCacheKey)(move.oldKey, move.newKey);
|
|
663
|
+
for (const move of pausedMoves)
|
|
642
664
|
coordinator.pausedOnPrompt.add(move.newKey);
|
|
643
|
-
}
|
|
644
665
|
return moves;
|
|
645
666
|
}
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* Next Action Helper - Derived state for worktree next actions and review status.
|
|
4
4
|
*
|
|
5
5
|
* Issue #600: UX refresh - provides getNextAction() and getReviewStatus()
|
|
6
|
-
* for Review screen and
|
|
6
|
+
* for the Review screen (via `/api/worktrees`) and the sidebar row
|
|
7
|
+
* (`toBranchItem` in `src/types/sidebar.ts`).
|
|
7
8
|
*
|
|
8
9
|
* Uses exhaustive check (satisfies never) pattern to catch future
|
|
9
10
|
* SessionStatus extensions at compile time [DR2-005].
|
|
10
11
|
*/
|
|
11
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
13
|
exports.NEXT_ACTION_KEYS = void 0;
|
|
13
|
-
exports.isNextActionKey = isNextActionKey;
|
|
14
14
|
exports.getNextAction = getNextAction;
|
|
15
15
|
exports.getReviewStatus = getReviewStatus;
|
|
16
16
|
/**
|
|
@@ -31,19 +31,6 @@ exports.NEXT_ACTION_KEYS = {
|
|
|
31
31
|
checkStalled: 'nextAction.checkStalled',
|
|
32
32
|
running: 'nextAction.running',
|
|
33
33
|
};
|
|
34
|
-
const NEXT_ACTION_KEY_SET = new Set(Object.values(exports.NEXT_ACTION_KEYS));
|
|
35
|
-
/**
|
|
36
|
-
* Whether a value is a key this module produces (Issue #1787).
|
|
37
|
-
*
|
|
38
|
-
* `Worktree.nextAction` is a plain `string` on the wire, and a server that
|
|
39
|
-
* predates this Issue still sends the old English literal. Callers must gate
|
|
40
|
-
* `t()` on this: next-intl reports a missing key by rendering the key path, so
|
|
41
|
-
* feeding it `"Approve / Reject"` would put `worktree.Approve / Reject` on
|
|
42
|
-
* screen. Anything that fails this test is rendered verbatim instead.
|
|
43
|
-
*/
|
|
44
|
-
function isNextActionKey(value) {
|
|
45
|
-
return NEXT_ACTION_KEY_SET.has(value);
|
|
46
|
-
}
|
|
47
34
|
/**
|
|
48
35
|
* Determine the next action for a given session state.
|
|
49
36
|
*
|
|
@@ -39,6 +39,7 @@ exports.ENV_CLEAN_GUIDANCE = exports.MAX_REPORTED_ENV_CHANGES = exports.isLivePi
|
|
|
39
39
|
exports.resolveRequireEnvClean = resolveRequireEnvClean;
|
|
40
40
|
exports.attributeSessionName = attributeSessionName;
|
|
41
41
|
exports.attributeAnchor = attributeAnchor;
|
|
42
|
+
exports.isZcompdumpEntry = isZcompdumpEntry;
|
|
42
43
|
exports.demoVitestPid = demoVitestPid;
|
|
43
44
|
exports.attributeHomeEntry = attributeHomeEntry;
|
|
44
45
|
exports.resolveTaskSessionName = resolveTaskSessionName;
|
|
@@ -200,6 +201,17 @@ exports.readLivePidCwd = readLivePidCwd;
|
|
|
200
201
|
* established.
|
|
201
202
|
*/
|
|
202
203
|
const DEMO_VITEST_HOME_ENTRY = /^\.commandmate-demo-vitest-([1-9]\d*)$/;
|
|
204
|
+
/**
|
|
205
|
+
* zsh's `compinit` cache: `~/.zcompdump` itself, and the `~/.zcompdump.<host>.<pid>`
|
|
206
|
+
* scratch file it writes before renaming (#3664). A worker's shell start makes
|
|
207
|
+
* it; the pid in it is already gone by the time the gate looks. Only these two
|
|
208
|
+
* shapes — any other `.zcompdump*` name stays a violation.
|
|
209
|
+
*/
|
|
210
|
+
const ZCOMPDUMP_HOME_ENTRY = /^\.zcompdump(?:\.[A-Za-z0-9._-]+\.[1-9]\d*)?$/;
|
|
211
|
+
/** Whether a `$HOME` entry name is a zsh compinit cache file (#3664). */
|
|
212
|
+
function isZcompdumpEntry(name) {
|
|
213
|
+
return ZCOMPDUMP_HOME_ENTRY.test(name);
|
|
214
|
+
}
|
|
203
215
|
const isLivePid = (pid) => {
|
|
204
216
|
try {
|
|
205
217
|
process.kill(pid, 0);
|
|
@@ -581,7 +593,8 @@ function diffEnvSnapshots(baseline, final, context, options = {}) {
|
|
|
581
593
|
const afterKeys = new Set(after.entries.map((entry) => entry.key));
|
|
582
594
|
// Only `home-entries`, and only by the whole name: the same string under
|
|
583
595
|
// `~/.commandmate` is a different directory and stays counted.
|
|
584
|
-
const isIgnoredByConfig = (entry) => probeId === 'home-entries' &&
|
|
596
|
+
const isIgnoredByConfig = (entry) => probeId === 'home-entries' &&
|
|
597
|
+
(ignoredHomeEntries.has(entry.key) || isZcompdumpEntry(entry.key));
|
|
585
598
|
const added = [];
|
|
586
599
|
const ignoredAdded = [];
|
|
587
600
|
const taskSessionAdded = [];
|
|
@@ -751,7 +751,10 @@ function namesOpencodeInstance(db, worktreeId, instanceId) {
|
|
|
751
751
|
* gates — the precise failure `verify-completion.sh` was written to catch.
|
|
752
752
|
*
|
|
753
753
|
* Contract files are evidence of the *orchestrator*, not of the agent, so
|
|
754
|
-
* neither side counts them (see {@link CONTRACT_DIR_PREFIX}).
|
|
754
|
+
* neither side counts them (see {@link CONTRACT_DIR_PREFIX}). Nor is the
|
|
755
|
+
* orchestrator's module-reference fold commit (orchestrate 6-4, judged by
|
|
756
|
+
* scope-gate's `collectBranchCommitChanges`): a branch that holds only a fold
|
|
757
|
+
* has no work of the worker's in it (#3633).
|
|
755
758
|
*
|
|
756
759
|
* ## The opencode second witness (Issue #2043)
|
|
757
760
|
*
|
|
@@ -796,18 +799,20 @@ async function evaluateWorkEvidence(worktreePath, baseRef, requireCommit, agentE
|
|
|
796
799
|
// pathspec is not exclusions alone, and it anchors both patterns at the root
|
|
797
800
|
// rather than at cwd. A setup commit that only carries the contract must not
|
|
798
801
|
// read as a commit's worth of work.
|
|
799
|
-
const revList = await runGit([
|
|
800
|
-
'rev-list',
|
|
801
|
-
'--count',
|
|
802
|
-
`${base}..HEAD`,
|
|
803
|
-
'--',
|
|
804
|
-
':(top)',
|
|
805
|
-
`:(exclude,top)${scope_gate_1.CONTRACT_DIR_PREFIX}`,
|
|
806
|
-
], worktreePath);
|
|
802
|
+
const revList = await runGit(['rev-list', `${base}..HEAD`, '--', ':(top)', `:(exclude,top)${scope_gate_1.CONTRACT_DIR_PREFIX}`], worktreePath);
|
|
807
803
|
if (revList.code !== 0) {
|
|
808
|
-
return done('error', `work-evidence: 'git rev-list
|
|
804
|
+
return done('error', `work-evidence: 'git rev-list ${base}..HEAD' failed.`, null);
|
|
809
805
|
}
|
|
810
|
-
|
|
806
|
+
// The orchestrator's fold commit is not the worker's (#3633), by the same
|
|
807
|
+
// rule the scope gate drops what only folds touched (#3588).
|
|
808
|
+
const branchChanges = await (0, scope_gate_1.collectBranchCommitChanges)(worktreePath, base);
|
|
809
|
+
if ('error' in branchChanges) {
|
|
810
|
+
return done('error', `work-evidence: ${branchChanges.error}`, null);
|
|
811
|
+
}
|
|
812
|
+
const foldShas = new Set(branchChanges.changes.filter((c) => c.fold).map((c) => c.sha));
|
|
813
|
+
const branchShas = revList.stdout.split('\n').filter((sha) => sha.trim() !== '');
|
|
814
|
+
const foldCount = branchShas.filter((sha) => foldShas.has(sha)).length;
|
|
815
|
+
const commitCount = branchShas.length - foldCount;
|
|
811
816
|
// -z -uall for the same reasons scope-gate uses them: the human format
|
|
812
817
|
// C-quotes paths with spaces and joins renames with ` -> `, and the default
|
|
813
818
|
// untracked mode collapses a fresh `.commandmate/tasks/` to one directory
|
|
@@ -841,6 +846,7 @@ async function evaluateWorkEvidence(worktreePath, baseRef, requireCommit, agentE
|
|
|
841
846
|
const summary = `work-evidence: baseRef=${baseRef} commits=${commitCount} uncommitted=${uncommittedCount}` +
|
|
842
847
|
(requireCommit.required ? ' requireCommit=true' : '') +
|
|
843
848
|
' (contract files excluded)' +
|
|
849
|
+
(foldCount > 0 ? ` (${foldCount} orchestrator fold commit(s) excluded)` : '') +
|
|
844
850
|
(skillInstalledCount > 0
|
|
845
851
|
? ` (${skillInstalledCount} CommandMate-installed Skill file(s) excluded)`
|
|
846
852
|
: '') +
|
|
@@ -30,11 +30,16 @@
|
|
|
30
30
|
* @module lib/verification/scope-gate
|
|
31
31
|
*/
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.SCOPE_SKIP_NOT_REQUIRED = exports.SCOPE_SKIP_NO_CONTRACT = exports.ScopeMatcher = exports.CONTRACT_DIR_PREFIX = exports.SCOPE_EXEMPT_CONTRACT_PATH = exports.SCOPE_EXEMPT_ALWAYS_ALLOWED = exports.SCOPE_PATTERN_ARROW = exports.ALWAYS_ALLOWED_PREFIX = exports.SCOPE_ALLOW_GUIDANCE = exports.MAX_REPORTED_VIOLATIONS = void 0;
|
|
33
|
+
exports.SCOPE_SKIP_NOT_REQUIRED = exports.SCOPE_SKIP_NO_CONTRACT = exports.ScopeMatcher = exports.ORCHESTRATOR_FOLD_TRAILER_VALUE = exports.ORCHESTRATOR_FOLD_TRAILER_KEY = exports.ORCHESTRATOR_FILES = exports.CONTRACT_DIR_PREFIX = exports.SCOPE_EXEMPT_CONTRACT_PATH = exports.SCOPE_EXEMPT_ALWAYS_ALLOWED = exports.SCOPE_PATTERN_ARROW = exports.ALWAYS_ALLOWED_PREFIX = exports.SCOPE_ALLOW_GUIDANCE = exports.MAX_REPORTED_VIOLATIONS = void 0;
|
|
34
|
+
exports.isOrchestratorOnlyCommit = isOrchestratorOnlyCommit;
|
|
35
|
+
exports.parseBranchCommits = parseBranchCommits;
|
|
36
|
+
exports.isFoldCommit = isFoldCommit;
|
|
37
|
+
exports.orchestratorOnlyPaths = orchestratorOnlyPaths;
|
|
34
38
|
exports.isContractPath = isContractPath;
|
|
35
39
|
exports.parsePorcelainEntries = parsePorcelainEntries;
|
|
36
40
|
exports.parsePorcelainStatusEntries = parsePorcelainStatusEntries;
|
|
37
41
|
exports.isSkillInstalledUntrackedEntry = isSkillInstalledUntrackedEntry;
|
|
42
|
+
exports.collectBranchCommitChanges = collectBranchCommitChanges;
|
|
38
43
|
exports.collectChangedPaths = collectChangedPaths;
|
|
39
44
|
exports.scopeSkipDetachedContract = scopeSkipDetachedContract;
|
|
40
45
|
exports.evaluateScope = evaluateScope;
|
|
@@ -117,6 +122,88 @@ exports.SCOPE_EXEMPT_CONTRACT_PATH = '(exempt: contract path)';
|
|
|
117
122
|
* explicit `deny` can still catch an agent weakening its own gates.
|
|
118
123
|
*/
|
|
119
124
|
exports.CONTRACT_DIR_PREFIX = '.commandmate/tasks/';
|
|
125
|
+
/**
|
|
126
|
+
* Files the orchestrator commits on a PR branch after the worker's checks
|
|
127
|
+
* (orchestrate 6-4: the module-reference fold) (#3588).
|
|
128
|
+
*
|
|
129
|
+
* Copy of `ORCHESTRATOR_FILES` in `scripts/orchestrate/pr-common.mjs`, where
|
|
130
|
+
* `merge-pr.mjs` uses the same file rule to look past a fold commit to the
|
|
131
|
+
* worker's HEAD. A test imports the .mjs and compares the two arrays, so the
|
|
132
|
+
* merge side and this gate cannot drift apart.
|
|
133
|
+
*/
|
|
134
|
+
exports.ORCHESTRATOR_FILES = ['docs/module-reference.md'];
|
|
135
|
+
/**
|
|
136
|
+
* The trailer that marks a fold commit, read the way `git interpret-trailers`
|
|
137
|
+
* reads it — from the message's trailer block only, so the same words in the
|
|
138
|
+
* middle of a body do not count (#3588).
|
|
139
|
+
*
|
|
140
|
+
* The file set alone cannot tell the orchestrator's fold from a worker that
|
|
141
|
+
* committed module-reference on its own, so this gate also requires the
|
|
142
|
+
* trailer. `workHeads` in pr-common.mjs deliberately does not: the merge side
|
|
143
|
+
* only decides which HEAD's records to read, and stays permissive.
|
|
144
|
+
*/
|
|
145
|
+
exports.ORCHESTRATOR_FOLD_TRAILER_KEY = 'Orchestrator-Fold';
|
|
146
|
+
exports.ORCHESTRATOR_FOLD_TRAILER_VALUE = 'module-reference';
|
|
147
|
+
/**
|
|
148
|
+
* Whether a commit's file list is the orchestrator's: at least one file, and
|
|
149
|
+
* every file in {@link ORCHESTRATOR_FILES} — the file rule of `workHeads` in
|
|
150
|
+
* `scripts/orchestrate/pr-common.mjs` (#3588).
|
|
151
|
+
*/
|
|
152
|
+
function isOrchestratorOnlyCommit(files) {
|
|
153
|
+
return files.length > 0 && files.every((file) => exports.ORCHESTRATOR_FILES.includes(file));
|
|
154
|
+
}
|
|
155
|
+
const FIELD_SEPARATOR = '\x1f';
|
|
156
|
+
const TRAILER_SEPARATOR = '\x1e';
|
|
157
|
+
/**
|
|
158
|
+
* Per commit: sha, parents, fold trailer values. With `-z` each record ends in
|
|
159
|
+
* NUL, which no commit message can contain; the sha and parent fields are hex,
|
|
160
|
+
* so only the trailer field is free text, and it is last.
|
|
161
|
+
*/
|
|
162
|
+
const BRANCH_COMMITS_FORMAT = `--format=%H%x1f%P%x1f%(trailers:key=${exports.ORCHESTRATOR_FOLD_TRAILER_KEY},` +
|
|
163
|
+
'valueonly,unfold,separator=%x1e)';
|
|
164
|
+
/** Parse `git log -z` output in {@link BRANCH_COMMITS_FORMAT} (#3588). */
|
|
165
|
+
function parseBranchCommits(output) {
|
|
166
|
+
const commits = [];
|
|
167
|
+
for (const record of output.split('\0')) {
|
|
168
|
+
const first = record.indexOf(FIELD_SEPARATOR);
|
|
169
|
+
const second = record.indexOf(FIELD_SEPARATOR, first + 1);
|
|
170
|
+
if (first <= 0 || second < 0)
|
|
171
|
+
continue;
|
|
172
|
+
const trailers = record.slice(second + 1);
|
|
173
|
+
commits.push({
|
|
174
|
+
sha: record.slice(0, first),
|
|
175
|
+
parents: record.slice(first + 1, second).split(' ').filter(Boolean),
|
|
176
|
+
foldTrailers: trailers === '' ? [] : trailers.split(TRAILER_SEPARATOR),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return commits;
|
|
180
|
+
}
|
|
181
|
+
/** Whether a non-merge commit is a fold (#3588): the trailer and the file rule both hold. */
|
|
182
|
+
function isFoldCommit(commit, files) {
|
|
183
|
+
return (commit.parents.length === 1 &&
|
|
184
|
+
commit.foldTrailers.includes(exports.ORCHESTRATOR_FOLD_TRAILER_VALUE) &&
|
|
185
|
+
isOrchestratorOnlyCommit(files));
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Paths that only fold commits touched on the branch (#3588).
|
|
189
|
+
*
|
|
190
|
+
* A path any other non-merge commit also touched is not returned: a worker
|
|
191
|
+
* that committed `docs/module-reference.md` itself — alone or with other
|
|
192
|
+
* files — is still judged on it. Merges are not passed in
|
|
193
|
+
* ({@link collectBranchCommitChanges} skips them): a merge's own edit inside
|
|
194
|
+
* {@link ORCHESTRATOR_FILES} is the approved integration of a refresh and
|
|
195
|
+
* leaves a fold-only path excluded, while its edits elsewhere stay in the
|
|
196
|
+
* branch diff and are judged as the worker's.
|
|
197
|
+
*/
|
|
198
|
+
function orchestratorOnlyPaths(commits) {
|
|
199
|
+
const byOrchestrator = new Set();
|
|
200
|
+
const byWorker = new Set();
|
|
201
|
+
for (const commit of commits) {
|
|
202
|
+
const target = commit.fold ? byOrchestrator : byWorker;
|
|
203
|
+
commit.files.forEach((file) => target.add(file));
|
|
204
|
+
}
|
|
205
|
+
return new Set([...byOrchestrator].filter((path) => !byWorker.has(path)));
|
|
206
|
+
}
|
|
120
207
|
/** Whether git reported a path that {@link CONTRACT_DIR_PREFIX} excludes. */
|
|
121
208
|
function isContractPath(path) {
|
|
122
209
|
return path.startsWith(exports.CONTRACT_DIR_PREFIX);
|
|
@@ -352,6 +439,61 @@ function parsePorcelainStatusEntries(output) {
|
|
|
352
439
|
function isSkillInstalledUntrackedEntry(entry, owned) {
|
|
353
440
|
return entry.status === '??' && entry.paths.every((path) => owned.has(path));
|
|
354
441
|
}
|
|
442
|
+
/**
|
|
443
|
+
* The non-merge commits of `<base>..HEAD` of a worktree, each judged by
|
|
444
|
+
* {@link isFoldCommit} (#3588). The scope gate drops what only folds touched
|
|
445
|
+
* ({@link orchestratorOnlyPaths}); the work-evidence gate does not count a
|
|
446
|
+
* fold as the worker's commit (#3633). Empty when no commit carries the fold
|
|
447
|
+
* trailer: none can be a fold, so the per-commit calls are skipped.
|
|
448
|
+
*
|
|
449
|
+
* File lists come from one `git diff-tree -z` per commit rather than from one
|
|
450
|
+
* `git log --name-only -z` stream: in that stream a commit header and a path
|
|
451
|
+
* are both NUL-terminated fields, and a path may start with a newline or a
|
|
452
|
+
* control character, so no parse of it can tell them apart. diff-tree prints
|
|
453
|
+
* paths and nothing else.
|
|
454
|
+
*
|
|
455
|
+
* A merge's own edits are split by path. Inside {@link ORCHESTRATOR_FILES}
|
|
456
|
+
* they are the approved integration — orchestrate lets the orchestrator
|
|
457
|
+
* resolve a refresh conflict in shared files such as module-reference by
|
|
458
|
+
* machine — so a fold-only path stays excluded. Every other path a merge edits
|
|
459
|
+
* is never a candidate (a fold changes only ORCHESTRATOR_FILES), so it stays in
|
|
460
|
+
* the branch diff and scope judges it as the worker's. Merges are therefore
|
|
461
|
+
* skipped here without asking `git diff-tree --cc`.
|
|
462
|
+
*/
|
|
463
|
+
async function collectBranchCommitChanges(worktreePath, base) {
|
|
464
|
+
const log = await runGit(['log', '-z', BRANCH_COMMITS_FORMAT, `${base}..HEAD`], worktreePath);
|
|
465
|
+
if (log.code !== 0) {
|
|
466
|
+
return {
|
|
467
|
+
error: `'git log ${base}..HEAD' failed.` + (log.stderr.trim() ? `\n${log.stderr.trim()}` : ''),
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const commits = parseBranchCommits(log.stdout);
|
|
471
|
+
// Nothing to exclude without a trailer; skip the per-commit calls.
|
|
472
|
+
if (!commits.some((commit) => commit.foldTrailers.length > 0))
|
|
473
|
+
return { changes: [] };
|
|
474
|
+
const changes = [];
|
|
475
|
+
for (const commit of commits) {
|
|
476
|
+
if (commit.parents.length !== 1)
|
|
477
|
+
continue;
|
|
478
|
+
const tree = await runGit(['diff-tree', '--no-commit-id', '--name-only', '-z', '--no-renames', '-r', commit.sha], worktreePath);
|
|
479
|
+
if (tree.code !== 0) {
|
|
480
|
+
return {
|
|
481
|
+
error: `'git diff-tree ${commit.sha}' failed.` +
|
|
482
|
+
(tree.stderr.trim() ? `\n${tree.stderr.trim()}` : ''),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
const files = splitNul(tree.stdout);
|
|
486
|
+
changes.push({ sha: commit.sha, files, fold: isFoldCommit(commit, files) });
|
|
487
|
+
}
|
|
488
|
+
return { changes };
|
|
489
|
+
}
|
|
490
|
+
/** {@link orchestratorOnlyPaths} for `<base>..HEAD` of a worktree (#3588). */
|
|
491
|
+
async function collectOrchestratorOnlyPaths(worktreePath, base) {
|
|
492
|
+
const result = await collectBranchCommitChanges(worktreePath, base);
|
|
493
|
+
if ('error' in result)
|
|
494
|
+
return result;
|
|
495
|
+
return { paths: orchestratorOnlyPaths(result.changes) };
|
|
496
|
+
}
|
|
355
497
|
/**
|
|
356
498
|
* Every path this worktree changed relative to `baseRef`.
|
|
357
499
|
*
|
|
@@ -379,6 +521,18 @@ async function collectChangedPaths(worktreePath, baseRef) {
|
|
|
379
521
|
(diff.stderr.trim() ? `\n${diff.stderr.trim()}` : ''),
|
|
380
522
|
};
|
|
381
523
|
}
|
|
524
|
+
// The orchestrator's fold commit (6-4) can sit under a re-instructed worker's
|
|
525
|
+
// commits; what only it touched is not the worker's change (#3588).
|
|
526
|
+
const orchestratorOnly = await collectOrchestratorOnlyPaths(worktreePath, base);
|
|
527
|
+
if ('error' in orchestratorOnly)
|
|
528
|
+
return orchestratorOnly;
|
|
529
|
+
const orchestratorExcluded = new Set();
|
|
530
|
+
const committed = splitNul(diff.stdout).filter((path) => {
|
|
531
|
+
if (!orchestratorOnly.paths.has(path))
|
|
532
|
+
return true;
|
|
533
|
+
orchestratorExcluded.add(path);
|
|
534
|
+
return false;
|
|
535
|
+
});
|
|
382
536
|
// -uall: without it a new directory collapses to a single `?? dir/` entry, and
|
|
383
537
|
// the gate would judge a directory name instead of the files inside it.
|
|
384
538
|
const status = await runGit(['status', '--porcelain', '-z', '--untracked-files=all'], worktreePath);
|
|
@@ -405,12 +559,14 @@ async function collectChangedPaths(worktreePath, baseRef) {
|
|
|
405
559
|
return true;
|
|
406
560
|
});
|
|
407
561
|
// Both sides are filtered, not just the working tree: an orchestrator may
|
|
408
|
-
// also have committed the contract as a setup commit (#1580).
|
|
409
|
-
|
|
562
|
+
// also have committed the contract as a setup commit (#1580). The working
|
|
563
|
+
// tree keeps an uncommitted module-reference edit: no fold commit made it.
|
|
564
|
+
const paths = new Set([...committed, ...workingTree.flatMap((entry) => entry.paths)].filter((path) => !isContractPath(path)));
|
|
410
565
|
return {
|
|
411
566
|
paths: [...paths].sort(),
|
|
412
567
|
mergeBase: base,
|
|
413
568
|
agentStateExcluded: [...agentStateExcluded].sort(),
|
|
569
|
+
orchestratorExcluded: [...orchestratorExcluded].filter((path) => !paths.has(path)).sort(),
|
|
414
570
|
};
|
|
415
571
|
}
|
|
416
572
|
// =============================================================================
|
|
@@ -470,6 +626,15 @@ function formatAgentStateExcluded(excluded) {
|
|
|
470
626
|
...(remainder > 0 ? [` ... (+${remainder} more)`] : []),
|
|
471
627
|
];
|
|
472
628
|
}
|
|
629
|
+
/** The `excluded:` section naming orchestrator-only committed paths (#3588). */
|
|
630
|
+
function formatOrchestratorExcluded(excluded) {
|
|
631
|
+
if (excluded.length === 0)
|
|
632
|
+
return [];
|
|
633
|
+
return [
|
|
634
|
+
'excluded (changed only by orchestrator commits, not counted as change):',
|
|
635
|
+
...excluded.map((path) => ` ~ ${path}`),
|
|
636
|
+
];
|
|
637
|
+
}
|
|
473
638
|
/**
|
|
474
639
|
* Reasons the gate has nothing to judge, phrased for `log_tail`.
|
|
475
640
|
*
|
|
@@ -546,6 +711,7 @@ async function evaluateScope(worktreePath, scope, requireScopeClean, baseRef, co
|
|
|
546
711
|
`deny: ${formatPatterns(scope.deny)}`,
|
|
547
712
|
...formatAdmitted(admitted),
|
|
548
713
|
...formatAgentStateExcluded(changed.agentStateExcluded),
|
|
714
|
+
...formatOrchestratorExcluded(changed.orchestratorExcluded),
|
|
549
715
|
].join('\n');
|
|
550
716
|
if (violations.length === 0) {
|
|
551
717
|
// An empty change set passes: work-evidence is the gate that judges "nothing
|
|
@@ -337,6 +337,7 @@ function toBranchItem(worktree) {
|
|
|
337
337
|
exitedInstanceIds,
|
|
338
338
|
unclassifiedInstanceIds,
|
|
339
339
|
worktreePath: worktree.path,
|
|
340
|
+
worktreeStatus: worktree.status ?? null,
|
|
340
341
|
waitingKind,
|
|
341
342
|
awaitingInstruction,
|
|
342
343
|
// `promptType` mirrors what `/api/worktrees` does with the same data
|