commandmate 0.42.1 → 0.43.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 +144 -144
- package/.next/app-path-routes-manifest.json +23 -23
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_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/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.js +1 -1
- package/.next/server/app/api/daily-summary/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/daily-summary/status/route.js.nft.json +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js +103 -1
- package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js +2 -2
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route.js +1 -1
- package/.next/server/app/api/relays/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route.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.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +3 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.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 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -103
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +3 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js.nft.json +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.js +3 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route.js.nft.json +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.js +3 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -103
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +104 -2
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +9 -9
- 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 +23 -23
- package/.next/server/chunks/1068.js +1 -1
- package/.next/server/chunks/1210.js +1 -1
- package/.next/server/chunks/1501.js +4 -4
- package/.next/server/chunks/1990.js +1 -1
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/22.js +1 -1
- package/.next/server/chunks/2328.js +1 -1
- package/.next/server/chunks/2404.js +4 -3
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/252.js +1 -1
- package/.next/server/chunks/2816.js +1 -1
- package/.next/server/chunks/3543.js +1 -1
- package/.next/server/chunks/3730.js +1 -1
- package/.next/server/chunks/3945.js +1 -1
- package/.next/server/chunks/4207.js +3 -3
- package/.next/server/chunks/434.js +1 -1
- package/.next/server/chunks/5064.js +1 -1
- package/.next/server/chunks/5571.js +1 -1
- package/.next/server/chunks/5742.js +1 -1
- package/.next/server/chunks/5775.js +1 -1
- package/.next/server/chunks/611.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6285.js +1 -1
- package/.next/server/chunks/6491.js +103 -0
- package/.next/server/chunks/6661.js +1 -1
- package/.next/server/chunks/6781.js +1 -1
- package/.next/server/chunks/6793.js +1 -1
- package/.next/server/chunks/7491.js +3 -0
- package/.next/server/chunks/7685.js +1 -1
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8581.js +2 -2
- package/.next/server/chunks/8583.js +1 -1
- package/.next/server/chunks/8673.js +6 -6
- package/.next/server/chunks/8728.js +2 -2
- package/.next/server/chunks/8791.js +1 -1
- package/.next/server/chunks/8800.js +4 -3
- package/.next/server/chunks/8918.js +5 -5
- package/.next/server/chunks/9130.js +3 -3
- package/.next/server/chunks/{8052.js → 9353.js} +4 -2
- package/.next/server/chunks/9390.js +3 -0
- package/.next/server/chunks/9627.js +4 -4
- package/.next/server/chunks/964.js +1 -1
- package/.next/server/chunks/967.js +1 -0
- package/.next/server/chunks/9731.js +3 -3
- package/.next/server/chunks/9882.js +3 -3
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{1642-fb2e176aaadbd137.js → 1642-767aed8e7c61ef7f.js} +1 -1
- package/.next/static/chunks/{2407-21fbf12d753b8d4d.js → 2407-940df9b11fadafc6.js} +1 -1
- package/.next/static/chunks/{2619-04bc32f026a0d946.js → 2619-a305ad4810f1393a.js} +1 -1
- package/.next/static/chunks/5449-6cb80bab2bd06b79.js +1 -0
- package/.next/static/chunks/7705-1f58d4c03d991fcf.js +1 -0
- package/.next/static/chunks/8165-3525fb42f1b4976e.js +1 -0
- package/.next/static/chunks/app/{layout-8ff4c7c08ef1f621.js → layout-057c850cd0df978e.js} +1 -1
- package/.next/static/chunks/app/review/page-ff9efae3f86eb840.js +1 -0
- package/.next/static/chunks/app/skills/[skillId]/page-74aac15286504ef4.js +1 -0
- package/.next/static/chunks/app/skills/page-182e16168b2fc098.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-eab0bf7564d3f9e2.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/{page-373ea37b16d930c7.js → page-c601f3b3ece6557e.js} +1 -1
- package/.next/trace +13 -13
- package/README.md +3 -3
- package/dist/cli/commands/ask.d.ts.map +1 -1
- package/dist/cli/commands/ask.js +23 -149
- package/dist/cli/commands/reply.d.ts +40 -0
- package/dist/cli/commands/reply.d.ts.map +1 -0
- package/dist/cli/commands/reply.js +133 -0
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +9 -0
- package/dist/cli/commands/wait.d.ts +5 -0
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +10 -1
- package/dist/cli/docs/agent-operations.d.ts +2 -2
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +13 -0
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/program.js +4 -0
- package/dist/cli/types/index.d.ts +2 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/reply-ledger.d.ts +95 -0
- package/dist/cli/utils/reply-ledger.d.ts.map +1 -0
- package/dist/cli/utils/reply-ledger.js +187 -0
- package/dist/lib/cli-tools/copilot-executable.d.ts +9 -0
- package/dist/lib/cli-tools/copilot-executable.d.ts.map +1 -1
- package/dist/lib/cli-tools/copilot-executable.js +21 -5
- package/dist/lib/cli-tools/opencode-executable.d.ts +103 -0
- package/dist/lib/cli-tools/opencode-executable.d.ts.map +1 -0
- package/dist/lib/cli-tools/opencode-executable.js +179 -0
- package/dist/lib/cli-tools/types.d.ts +1 -1
- package/dist/lib/cli-tools/types.d.ts.map +1 -1
- package/dist/lib/cli-tools/types.js +5 -1
- package/dist/lib/detection/tools/verified-against.d.ts +15 -0
- package/dist/lib/detection/tools/verified-against.d.ts.map +1 -1
- package/dist/lib/detection/tools/verified-against.js +17 -1
- package/dist/lib/detection/version-probes.d.ts.map +1 -1
- package/dist/lib/detection/version-probes.js +14 -1
- package/dist/lib/session/tmux-session-surface.d.ts.map +1 -1
- package/dist/lib/session/tmux-session-surface.js +1 -1
- package/dist/server/locales/en/worktree.json +44 -14
- package/dist/server/locales/ja/worktree.json +44 -14
- package/dist/server/server.js +23 -0
- package/dist/server/src/config/cli-tool-timing-config.js +19 -1
- package/dist/server/src/config/detection-evidence-config.js +3 -0
- package/dist/server/src/config/schedule-config.js +55 -1
- package/dist/server/src/lib/auto-yes-poller.js +47 -4
- package/dist/server/src/lib/cli-tools/agent-mode-spec.js +54 -2
- package/dist/server/src/lib/cli-tools/base.js +6 -2
- package/dist/server/src/lib/cli-tools/command-code.js +55 -1
- package/dist/server/src/lib/cli-tools/composer-spec.js +10 -0
- package/dist/server/src/lib/cli-tools/copilot-executable.js +21 -5
- package/dist/server/src/lib/cli-tools/graceful-exit.js +15 -1
- package/dist/server/src/lib/cli-tools/index.js +3 -1
- package/dist/server/src/lib/cli-tools/install-hints.js +9 -3
- package/dist/server/src/lib/cli-tools/liveness-spec.js +19 -0
- package/dist/server/src/lib/cli-tools/manager.js +2 -0
- package/dist/server/src/lib/cli-tools/opencode-executable.js +179 -0
- package/dist/server/src/lib/cli-tools/opencode-v2.js +369 -0
- package/dist/server/src/lib/cli-tools/opencode.js +29 -5
- package/dist/server/src/lib/cli-tools/types.js +5 -1
- package/dist/server/src/lib/cmate-cli-tool-parser.js +65 -7
- package/dist/server/src/lib/cmate-parser.js +12 -0
- package/dist/server/src/lib/cmate-validator.js +13 -1
- package/dist/server/src/lib/db/worktree-db.js +1 -1
- package/dist/server/src/lib/detection/cli-patterns.js +78 -2
- package/dist/server/src/lib/detection/model-info-extractor.js +79 -7
- package/dist/server/src/lib/detection/status-detector.js +10 -1
- package/dist/server/src/lib/detection/tools/codex/cli-patterns.js +2 -1
- package/dist/server/src/lib/detection/tools/opencode-v2/detect.js +253 -0
- package/dist/server/src/lib/detection/tools/opencode-v2/dialog-title.js +48 -0
- package/dist/server/src/lib/detection/tools/registry.js +2 -0
- package/dist/server/src/lib/detection/tools/run-detection.js +9 -0
- package/dist/server/src/lib/detection/tools/verified-against.js +17 -1
- package/dist/server/src/lib/detection/upstream-faults.js +8 -0
- package/dist/server/src/lib/detection/version-probes.js +14 -1
- package/dist/server/src/lib/hooks/agent-session-telemetry.js +36 -1
- package/dist/server/src/lib/hooks/sources/opencode/log-diagnosis.js +105 -0
- package/dist/server/src/lib/hooks/sources/opencode/source.js +7 -1
- package/dist/server/src/lib/hooks/sources/opencode-v2/client.js +422 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/decision-labels.js +24 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/history.js +421 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/ingest.js +271 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/mappers.js +165 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/payloads.js +351 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/ports.js +230 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/reattach.js +149 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/runtime.js +180 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/secrets.js +182 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/source.js +295 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/subscription.js +455 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/tool-id.js +18 -0
- package/dist/server/src/lib/hooks/sources/opencode-v2/usage.js +214 -0
- package/dist/server/src/lib/hooks/sources/registry.js +2 -0
- package/dist/server/src/lib/hooks/structured-decision-response.js +14 -5
- package/dist/server/src/lib/job-executor.js +2 -1
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +15 -1
- package/dist/server/src/lib/polling/structured-history-gate.js +19 -5
- package/dist/server/src/lib/relay/relay-delivery.js +4 -1
- package/dist/server/src/lib/session/agent-event-state.js +24 -0
- package/dist/server/src/lib/session/claude-executor.js +184 -10
- package/dist/server/src/lib/session/current-output-builder.js +27 -3
- package/dist/server/src/lib/session/send-user-message.js +4 -0
- package/dist/server/src/lib/session/structured-prompt.js +54 -0
- package/dist/server/src/lib/session/tmux-session-surface.js +1 -1
- package/dist/server/src/lib/skills/compatibility-matrix.js +65 -0
- package/dist/server/src/lib/tmux/tmux.js +48 -2
- package/dist/server/src/lib/verification/env-clean-gate.js +228 -9
- package/dist/server/src/lib/verification/env-snapshot.js +65 -1
- package/dist/server/src/types/cli-tool-contracts.js +10 -2
- package/dist/types/cli-tool-contracts.d.ts +16 -5
- package/dist/types/cli-tool-contracts.d.ts.map +1 -1
- package/dist/types/cli-tool-contracts.js +10 -2
- package/package.json +2 -1
- package/release-notes/0.42.2.json +20 -0
- package/release-notes/0.43.0.json +130 -0
- package/scripts/hooks/cmate-agent-event.sh +14 -0
- package/scripts/opencode-v2/launch.sh +226 -0
- package/.next/server/chunks/4443.js +0 -3
- package/.next/server/chunks/5164.js +0 -1
- package/.next/server/chunks/650.js +0 -1
- package/.next/static/chunks/5449-550bd273dccc3ae4.js +0 -1
- package/.next/static/chunks/7705-2a2936551b0cf9a3.js +0 -1
- package/.next/static/chunks/8165-934ee7ff3b4ddfd4.js +0 -1
- package/.next/static/chunks/app/review/page-30616daa1dc595c2.js +0 -1
- package/.next/static/chunks/app/skills/[skillId]/page-ab7e432b69fccb3b.js +0 -1
- package/.next/static/chunks/app/skills/page-56b4fa11223e0cbd.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-3c946bd2875391fd.js +0 -1
- /package/.next/static/{3M_S0B7dhEDdVE2XSJz1Y → BVkBYdceLRvMROqQIqeBx}/_buildManifest.js +0 -0
- /package/.next/static/{3M_S0B7dhEDdVE2XSJz1Y → BVkBYdceLRvMROqQIqeBx}/_ssgManifest.js +0 -0
|
@@ -35,12 +35,15 @@
|
|
|
35
35
|
*/
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.MAX_STRUCTURED_PROMPT_MESSAGE_LENGTH = exports.STRUCTURED_DECISION_OPTIONS = void 0;
|
|
38
|
+
exports.structuredDecisionOptionsFor = structuredDecisionOptionsFor;
|
|
38
39
|
exports.isAddressableDecision = isAddressableDecision;
|
|
40
|
+
exports.hasApiAnswerableDecision = hasApiAnswerableDecision;
|
|
39
41
|
exports.buildStructuredPromptQuestion = buildStructuredPromptQuestion;
|
|
40
42
|
exports.buildStructuredPromptData = buildStructuredPromptData;
|
|
41
43
|
exports.buildStructuredPromptHistoryRecord = buildStructuredPromptHistoryRecord;
|
|
42
44
|
exports.isStructuredPromptWaitingData = isStructuredPromptWaitingData;
|
|
43
45
|
const models_1 = require("../../types/models");
|
|
46
|
+
const decision_labels_1 = require("../../lib/hooks/sources/opencode-v2/decision-labels");
|
|
44
47
|
/**
|
|
45
48
|
* The three verdicts an approval dialog accepts, in the order `respond` numbers
|
|
46
49
|
* them (Issue #1898).
|
|
@@ -67,6 +70,32 @@ exports.STRUCTURED_DECISION_OPTIONS = [
|
|
|
67
70
|
// eslint-disable-next-line no-restricted-syntax -- CLI answer vocabulary, not display text
|
|
68
71
|
{ number: 3, label: 'Reject', reply: 'reject' },
|
|
69
72
|
];
|
|
73
|
+
/**
|
|
74
|
+
* A tool's own words for the approval verdicts, keyed by wire reply
|
|
75
|
+
* (Issue #2951). Only tools whose dialog words differ from
|
|
76
|
+
* {@link STRUCTURED_DECISION_OPTIONS} are here: OpenCode V2 draws `Always allow`
|
|
77
|
+
* where v1 draws `Allow always`.
|
|
78
|
+
*/
|
|
79
|
+
const DECISION_LABELS_BY_TOOL = {
|
|
80
|
+
'opencode-v2': decision_labels_1.OPENCODE_V2_DECISION_LABELS,
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* The approval verdicts in the words of the tool that asked (Issue #2951).
|
|
84
|
+
*
|
|
85
|
+
* What `capture --json` publishes as `decisionOptions` and what `respond`
|
|
86
|
+
* matches a label against, so both say what the tool's own dialog says. Only
|
|
87
|
+
* the LABEL differs per tool: the number and the wire reply are the shared
|
|
88
|
+
* list's, so `2` still means `always` everywhere. Every tool without its own
|
|
89
|
+
* words gets {@link STRUCTURED_DECISION_OPTIONS} itself (the same object).
|
|
90
|
+
*
|
|
91
|
+
* @param cliToolId - The tool the approval belongs to
|
|
92
|
+
*/
|
|
93
|
+
function structuredDecisionOptionsFor(cliToolId) {
|
|
94
|
+
const labels = DECISION_LABELS_BY_TOOL[cliToolId];
|
|
95
|
+
if (!labels)
|
|
96
|
+
return exports.STRUCTURED_DECISION_OPTIONS;
|
|
97
|
+
return exports.STRUCTURED_DECISION_OPTIONS.map((option) => labels[option.reply] ? { ...option, label: labels[option.reply] } : option);
|
|
98
|
+
}
|
|
70
99
|
/** Bound on the agent's `message`, which is prose and only ever displayed. */
|
|
71
100
|
exports.MAX_STRUCTURED_PROMPT_MESSAGE_LENGTH = 500;
|
|
72
101
|
/**
|
|
@@ -89,6 +118,31 @@ exports.MAX_STRUCTURED_PROMPT_MESSAGE_LENGTH = 500;
|
|
|
89
118
|
function isAddressableDecision(value) {
|
|
90
119
|
return typeof value === 'string' && value !== '';
|
|
91
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Whether the agent is holding a decision this server can answer over the
|
|
123
|
+
* agent's own API right now (Issue #2965).
|
|
124
|
+
*
|
|
125
|
+
* The gate on the two "the detection layer could not read this" rows — the
|
|
126
|
+
* unclassified-frame row and the structured-prompt row
|
|
127
|
+
* ({@link buildStructuredPromptQuestion}). Both exist to record a dialog nobody
|
|
128
|
+
* could answer; while this is true somebody can, by id, and the row is a
|
|
129
|
+
* meaningless line in the chat (OpenCode V2 approvals and questions, #2945;
|
|
130
|
+
* v1 opencode's approvals, which carry the same `permission-id`). All three
|
|
131
|
+
* conjuncts are required, and each failing one puts the row back — the safe
|
|
132
|
+
* side:
|
|
133
|
+
*
|
|
134
|
+
* - the source publishes per-decision ids (`eventIdentity: 'permission-id'`);
|
|
135
|
+
* - a pending decision carries one ({@link isAddressableDecision});
|
|
136
|
+
* - its delivery window has not expired (a verdict can still reach the agent).
|
|
137
|
+
*
|
|
138
|
+
* @param eventIdentity - `capabilities.eventIdentity` of the tool's event source
|
|
139
|
+
* @param pendingDecisions - the decisions the turn is holding, as published
|
|
140
|
+
*/
|
|
141
|
+
function hasApiAnswerableDecision(eventIdentity, pendingDecisions) {
|
|
142
|
+
if (eventIdentity !== 'permission-id')
|
|
143
|
+
return false;
|
|
144
|
+
return pendingDecisions.some((decision) => isAddressableDecision(decision.id) && !decision.deliveryExpired);
|
|
145
|
+
}
|
|
92
146
|
/** How each source reads in the one-line question. */
|
|
93
147
|
const SOURCE_LABEL = {
|
|
94
148
|
notification: 'Notification(permission_prompt)',
|
|
@@ -343,7 +343,7 @@ function isLiveAttachEligibleSession(sessionName) {
|
|
|
343
343
|
* `tests/unit/tmux/session-surface-2317.test.ts`, so a tool moving in or out of
|
|
344
344
|
* the alternate screen breaks a test rather than silently dropping the hint.
|
|
345
345
|
*/
|
|
346
|
-
exports.ALT_SCREEN_TOOLS = ['claude', 'opencode', 'copilot'];
|
|
346
|
+
exports.ALT_SCREEN_TOOLS = ['claude', 'opencode', 'copilot', 'opencode-v2'];
|
|
347
347
|
/** Whether `attach` should warn that a bare attach shows no transcript. */
|
|
348
348
|
function usesAltScreen(cliToolId) {
|
|
349
349
|
return exports.ALT_SCREEN_TOOLS.includes(cliToolId);
|
|
@@ -81,6 +81,7 @@ const NOT_MEASURED_SKIP_REASON_KEY = 'skills.compatibility.skipReason.notMeasure
|
|
|
81
81
|
exports.AGENT_RELOAD_MESSAGE_KEYS = {
|
|
82
82
|
SESSION_RESTART: 'skills.compatibility.reload.sessionRestart',
|
|
83
83
|
SESSION_RESTART_NO_SLASH: 'skills.compatibility.reload.sessionRestartNoSlash',
|
|
84
|
+
PICKED_UP_LIVE: 'skills.compatibility.reload.pickedUpLive',
|
|
84
85
|
UNKNOWN: 'skills.compatibility.reload.unknown',
|
|
85
86
|
};
|
|
86
87
|
/** Known limitation keys attached to an axis outcome. */
|
|
@@ -113,6 +114,15 @@ const MEASURED_DATE_2026_09_05 = '2026-09-05';
|
|
|
113
114
|
* deliberately *not* recorded as a row — see the `gemini` entry below.
|
|
114
115
|
*/
|
|
115
116
|
const OTHER_AGENTS_EVIDENCE_SOURCE = 'https://github.com/Kewton/CommandMate/blob/main/docs/reference/skill-agent-compatibility.md#9-gemini--copilot-probe-log-issue-2302';
|
|
117
|
+
/**
|
|
118
|
+
* Evidence reference for the OpenCode V2 (opencode2) 2.0.18 measurement
|
|
119
|
+
* (Issue #2975).
|
|
120
|
+
*
|
|
121
|
+
* The reference doc carries the harness, the controls and the raw
|
|
122
|
+
* `GET /api/skill` / message output.
|
|
123
|
+
*/
|
|
124
|
+
const OPENCODE_V2_EVIDENCE_SOURCE = 'https://github.com/Kewton/CommandMate/blob/main/docs/reference/skill-agent-compatibility.md#10-opencode-v2-probe-log-issue-2975';
|
|
125
|
+
const MEASURED_DATE_2026_09_29 = '2026-09-29';
|
|
116
126
|
function unmeasuredEntry(agent) {
|
|
117
127
|
return {
|
|
118
128
|
agent,
|
|
@@ -345,6 +355,61 @@ const AGENT_DISCOVERY_MATRIX = [
|
|
|
345
355
|
reloadKey: exports.AGENT_RELOAD_MESSAGE_KEYS.SESSION_RESTART,
|
|
346
356
|
skipReasonKey: null,
|
|
347
357
|
},
|
|
358
|
+
{
|
|
359
|
+
agent: 'opencode-v2',
|
|
360
|
+
// Issue #2975, opencode2 2.0.18. Measured on its own rather than copied off
|
|
361
|
+
// the v1 row: the values happen to match v1's, but the release, the day and
|
|
362
|
+
// the route that makes invocation work are different.
|
|
363
|
+
//
|
|
364
|
+
// Ten probe Skills were planted, one per candidate root, each told to
|
|
365
|
+
// answer a unique token. `GET /api/skill` returned eight of them with their
|
|
366
|
+
// absolute SKILL.md paths — project `.opencode/skills`, `.opencode/skill`,
|
|
367
|
+
// `.claude/skills`, `.agents/skills`; home `.claude/skills`,
|
|
368
|
+
// `.agents/skills`, `.config/opencode/skills`, `.config/opencode/skill` —
|
|
369
|
+
// and not the two negative controls (`.github/skills`,
|
|
370
|
+
// `$HOME/.opencode/skills`). Both CommandMate install roots are among the
|
|
371
|
+
// eight, and the byte-identical dual-root install is listed once.
|
|
372
|
+
discoveryRoots: [constants_1.SKILL_INSTALL_ROOT_PREFIX, constants_1.SKILL_CLAUDE_INSTALL_ROOT_PREFIX],
|
|
373
|
+
discovery: {
|
|
374
|
+
outcome: 'verified',
|
|
375
|
+
evidenceKind: 'mechanical',
|
|
376
|
+
labelKey: exports.AGENT_AXIS_OUTCOME_LABEL_KEYS.verified,
|
|
377
|
+
evidenceKindKey: exports.AGENT_EVIDENCE_KIND_LABEL_KEYS.mechanical,
|
|
378
|
+
limitationKey: null,
|
|
379
|
+
},
|
|
380
|
+
invocation: {
|
|
381
|
+
// Submitting `/probe-agents-root ` produced a `skill` tool part with
|
|
382
|
+
// `input.id: "probe-agents-root"` and the Skill's absolute directory in
|
|
383
|
+
// its metadata, and the agent answered `PROBE_OK_probe-agents-root`;
|
|
384
|
+
// `.claude/skills` did the same with its own token. Unlike v1 the TUI
|
|
385
|
+
// does not expand the Skill — the user message is the literal
|
|
386
|
+
// `/probe-agents-root ` (a markdown command, by contrast, is replaced by
|
|
387
|
+
// its template) — so it is the model calling the `skill` tool. The tool
|
|
388
|
+
// part is the discriminator, not the token: `/probe-home-dotopencode-root`
|
|
389
|
+
// (never discovered) came back with a *different* Skill's token.
|
|
390
|
+
//
|
|
391
|
+
// The limitation is the same as v1's: typing the name after `/` shows
|
|
392
|
+
// "No matching commands" (positive control `/rev` → `/review`, negative
|
|
393
|
+
// control `/zzzznotacommand` → nothing), and the `ctrl+p` palette has no
|
|
394
|
+
// row for it either. What v2 adds is an `@` completion — `@probe` lists
|
|
395
|
+
// every discovered Skill tagged `skill`, and the `/skills` picker inserts
|
|
396
|
+
// `@name` — so v2 does have its own way in, just not a slash command.
|
|
397
|
+
outcome: 'verified',
|
|
398
|
+
evidenceKind: 'mechanical',
|
|
399
|
+
labelKey: exports.AGENT_AXIS_OUTCOME_LABEL_KEYS.verified,
|
|
400
|
+
evidenceKindKey: exports.AGENT_EVIDENCE_KIND_LABEL_KEYS.mechanical,
|
|
401
|
+
limitationKey: exports.AGENT_LIMITATION_MESSAGE_KEYS.NO_SLASH_COMMAND,
|
|
402
|
+
},
|
|
403
|
+
testedVersion: '2.0.18',
|
|
404
|
+
testedDate: MEASURED_DATE_2026_09_29,
|
|
405
|
+
evidenceSource: OPENCODE_V2_EVIDENCE_SOURCE,
|
|
406
|
+
// Also measured, and the opposite of v1: the server does NOT need a
|
|
407
|
+
// restart. A Skill planted while it ran appeared in `GET /api/skill` within
|
|
408
|
+
// seconds, and one planted into `$HOME/.agents/skills` mid-session was run
|
|
409
|
+
// in that same session — so the guidance is "no new session needed".
|
|
410
|
+
reloadKey: exports.AGENT_RELOAD_MESSAGE_KEYS.PICKED_UP_LIVE,
|
|
411
|
+
skipReasonKey: null,
|
|
412
|
+
},
|
|
348
413
|
];
|
|
349
414
|
/** The measured matrix, in CLI-tool declaration order. */
|
|
350
415
|
function getSkillAgentMatrix() {
|
|
@@ -254,6 +254,33 @@ async function listSessions() {
|
|
|
254
254
|
return [];
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Issue #2958: what window 0 of a new session runs until `createSession`
|
|
259
|
+
* replaces it with the login shell. Two words so tmux execs it directly (a
|
|
260
|
+
* single word would be run through `default-shell -c`); it never exits on its
|
|
261
|
+
* own and dies with the SIGHUP the replacement sends.
|
|
262
|
+
*/
|
|
263
|
+
const PLACEHOLDER_PANE_COMMAND = ['sleep', '2147483647'];
|
|
264
|
+
/**
|
|
265
|
+
* Fallback for a failed window rebuild: swap the placeholder in window 0 for
|
|
266
|
+
* the login shell tmux would have started (`default-shell`, as a login shell).
|
|
267
|
+
*
|
|
268
|
+
* A bare `respawn-pane` would re-run the pane's creation command — the
|
|
269
|
+
* placeholder — so the shell is named explicitly.
|
|
270
|
+
*/
|
|
271
|
+
async function respawnLoginShell(sessionName, workingDirectory) {
|
|
272
|
+
let shell = '';
|
|
273
|
+
try {
|
|
274
|
+
const { stdout } = await execFileAsync('tmux', ['show-options', '-gv', 'default-shell'], { timeout: DEFAULT_TIMEOUT });
|
|
275
|
+
shell = stdout.trim();
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// Fall through to the environment's shell.
|
|
279
|
+
}
|
|
280
|
+
if (!shell)
|
|
281
|
+
shell = process.env.SHELL || '/bin/sh';
|
|
282
|
+
await execFileAsync('tmux', ['respawn-pane', '-k', '-t', `${exactTarget(sessionName)}0`, '-c', workingDirectory, shell, '-l'], { timeout: DEFAULT_TIMEOUT });
|
|
283
|
+
}
|
|
257
284
|
/**
|
|
258
285
|
* Create a new tmux session
|
|
259
286
|
*
|
|
@@ -300,7 +327,23 @@ async function createSession(sessionNameOrOptions, cwd) {
|
|
|
300
327
|
try {
|
|
301
328
|
// Create session with explicit window size to avoid 80x24 default
|
|
302
329
|
// This is critical for TUI tools (Copilot, OpenCode) that use alternate screen
|
|
303
|
-
|
|
330
|
+
//
|
|
331
|
+
// Issue #2958: window 0 runs a PLACEHOLDER, not the login shell, because it
|
|
332
|
+
// is thrown away by the `new-window -k` below a few milliseconds later. A
|
|
333
|
+
// shell killed that early is still inside its rc files, and pyenv's
|
|
334
|
+
// `pyenv init -` (`command pyenv rehash`) can die holding its lock file
|
|
335
|
+
// (`$PYENV_ROOT/shims/.pyenv-shim`). The NEXT shell then spins in
|
|
336
|
+
// `pyenv rehash` for its 60s lock timeout before running the launch command,
|
|
337
|
+
// so the agent missed the 60s initialization wait (exit 99). Measured on tmux
|
|
338
|
+
// 3.5a / pyenv 2.6.0: 7 of 40 shell-first creations left the lock behind and
|
|
339
|
+
// the replacement pane ran its first command after 61s; 0 of 40 with the
|
|
340
|
+
// placeholder. Two argv words make tmux exec it directly, so no shell (and
|
|
341
|
+
// no rc file) is started for window 0 at all.
|
|
342
|
+
await execFileAsync('tmux', [
|
|
343
|
+
'new-session', '-d', '-s', sessionName, '-c', workingDirectory,
|
|
344
|
+
'-x', String(windowWidth), '-y', String(windowHeight),
|
|
345
|
+
...PLACEHOLDER_PANE_COMMAND,
|
|
346
|
+
], { timeout: DEFAULT_TIMEOUT });
|
|
304
347
|
// Issue #1624: `history-limit` must be set BEFORE the pane that uses it exists.
|
|
305
348
|
//
|
|
306
349
|
// It is a session option, but a pane sizes its scrollback buffer ONCE, from
|
|
@@ -324,7 +367,9 @@ async function createSession(sessionNameOrOptions, cwd) {
|
|
|
324
367
|
// cwd instead). Omitting it silently launches every agent in the wrong repo.
|
|
325
368
|
//
|
|
326
369
|
// Best-effort, matching the geometry step below: if this fails the session is
|
|
327
|
-
// still usable, just with tmux's default 2000-line scrollback.
|
|
370
|
+
// still usable, just with tmux's default 2000-line scrollback. Window 0 is
|
|
371
|
+
// only a placeholder (Issue #2958), so the fallback must still put the login
|
|
372
|
+
// shell into it — see respawnLoginShell().
|
|
328
373
|
try {
|
|
329
374
|
await execFileAsync('tmux', ['new-window', '-k', '-t', `${exactTarget(sessionName)}0`, '-c', workingDirectory], { timeout: DEFAULT_TIMEOUT });
|
|
330
375
|
}
|
|
@@ -334,6 +379,7 @@ async function createSession(sessionNameOrOptions, cwd) {
|
|
|
334
379
|
historyLimit,
|
|
335
380
|
error: error instanceof Error ? error.message : String(error),
|
|
336
381
|
});
|
|
382
|
+
await respawnLoginShell(sessionName, workingDirectory);
|
|
337
383
|
}
|
|
338
384
|
// Issue #1163: Pin the pane to a fixed height so alternate-screen TUIs
|
|
339
385
|
// (Claude/Codex/etc.) keep enough visible rows for capture-pane.
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* counted, and it is the single failure this gate must not reproduce.
|
|
18
18
|
* 2. **Additions and removals are judged asymmetrically.** Everything that
|
|
19
19
|
* existed at task start must still exist, whoever it belonged to — that is
|
|
20
|
-
* the pkill (#1739) and `kill-server` (#1624) case
|
|
20
|
+
* the pkill (#1739) and `kill-server` (#1624) case — with one exception: a
|
|
21
|
+
* tmux session the baseline recorded as *another CommandMate server's*
|
|
22
|
+
* (#2627), which that server may close whenever it likes. New things are a
|
|
21
23
|
* violation *unless they are attributable to another worker*, because
|
|
22
24
|
* parallel delegations legitimately start their own sessions and servers
|
|
23
25
|
* inside each other's measurement windows — or unless they are the one
|
|
@@ -29,17 +31,25 @@
|
|
|
29
31
|
* @module lib/verification/env-clean-gate
|
|
30
32
|
*/
|
|
31
33
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.ENV_CLEAN_GUIDANCE = exports.MAX_REPORTED_ENV_CHANGES = exports.REQUIRE_ENV_CLEAN_SOURCE_CONTRACT = exports.REQUIRE_ENV_CLEAN_SOURCE_CONFIG = void 0;
|
|
34
|
+
exports.ENV_CLEAN_GUIDANCE = exports.MAX_REPORTED_ENV_CHANGES = exports.readLivePidCwd = exports.REQUIRE_ENV_CLEAN_SOURCE_CONTRACT = exports.REQUIRE_ENV_CLEAN_SOURCE_CONFIG = void 0;
|
|
33
35
|
exports.resolveRequireEnvClean = resolveRequireEnvClean;
|
|
34
36
|
exports.attributeSessionName = attributeSessionName;
|
|
35
37
|
exports.attributeAnchor = attributeAnchor;
|
|
38
|
+
exports.demoVitestPid = demoVitestPid;
|
|
39
|
+
exports.attributeHomeEntry = attributeHomeEntry;
|
|
36
40
|
exports.resolveTaskSessionName = resolveTaskSessionName;
|
|
37
41
|
exports.recordTaskSession = recordTaskSession;
|
|
38
42
|
exports.readTaskSession = readTaskSession;
|
|
43
|
+
exports.currentSessionServer = currentSessionServer;
|
|
44
|
+
exports.isOtherServerSession = isOtherServerSession;
|
|
45
|
+
exports.recordOtherServerSessions = recordOtherServerSessions;
|
|
46
|
+
exports.readOtherServerSessions = readOtherServerSessions;
|
|
39
47
|
exports.diffEnvSnapshots = diffEnvSnapshots;
|
|
40
48
|
exports.formatEnvCleanReport = formatEnvCleanReport;
|
|
41
49
|
exports.envCleanNoBaseline = envCleanNoBaseline;
|
|
42
50
|
exports.evaluateEnvClean = evaluateEnvClean;
|
|
51
|
+
const child_process_1 = require("child_process");
|
|
52
|
+
const fs_1 = require("fs");
|
|
43
53
|
const path_1 = require("path");
|
|
44
54
|
const session_name_1 = require("../../lib/cli-tools/session-name");
|
|
45
55
|
const types_1 = require("../../lib/cli-tools/types");
|
|
@@ -132,16 +142,92 @@ function attributeAnchor(anchor, worktreePath) {
|
|
|
132
142
|
return 'other';
|
|
133
143
|
return 'unattributed';
|
|
134
144
|
}
|
|
135
|
-
|
|
145
|
+
/** Bounded like the snapshot probes: a hung `lsof` must not hold a run open. */
|
|
146
|
+
const PID_CWD_TIMEOUT_MS = 5_000;
|
|
147
|
+
/**
|
|
148
|
+
* Read a process's cwd from the machine: `/proc/<pid>/cwd` on Linux, `lsof` on
|
|
149
|
+
* macOS (which has no procfs). A dead pid, a process owned by someone this user
|
|
150
|
+
* cannot inspect, or a missing `lsof` all answer null — which leaves the entry
|
|
151
|
+
* unattributed, the strict verdict.
|
|
152
|
+
*/
|
|
153
|
+
const readLivePidCwd = (pid) => {
|
|
154
|
+
try {
|
|
155
|
+
process.kill(pid, 0);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
// EPERM means the process exists but belongs to another user.
|
|
159
|
+
if (error.code !== 'EPERM')
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
if (process.platform === 'linux') {
|
|
163
|
+
try {
|
|
164
|
+
return (0, fs_1.readlinkSync)(`/proc/${pid}/cwd`);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const out = (0, child_process_1.execFileSync)('lsof', ['-a', '-d', 'cwd', '-p', String(pid), '-F', 'n'], {
|
|
172
|
+
encoding: 'utf8',
|
|
173
|
+
timeout: PID_CWD_TIMEOUT_MS,
|
|
174
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
175
|
+
});
|
|
176
|
+
const line = out.split('\n').find((field) => field.startsWith('n'));
|
|
177
|
+
return line ? line.slice(1) : null;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
exports.readLivePidCwd = readLivePidCwd;
|
|
184
|
+
/**
|
|
185
|
+
* `tests/unit/skills/demo-video/env-scripts.test.ts` creates
|
|
186
|
+
* `~/.commandmate-demo-vitest-<pid>` for the length of its run — in the real
|
|
187
|
+
* `$HOME`, because `env-up.sh` refuses `/tmp` and `/var` (#2954). The name
|
|
188
|
+
* carries the pid of the test process that made it, which is the one `$HOME`
|
|
189
|
+
* entry whose owner can be established.
|
|
190
|
+
*/
|
|
191
|
+
const DEMO_VITEST_HOME_ENTRY = /^\.commandmate-demo-vitest-([1-9]\d*)$/;
|
|
192
|
+
/** The pid a `.commandmate-demo-vitest-<pid>` name carries; null for any other name. */
|
|
193
|
+
function demoVitestPid(name) {
|
|
194
|
+
const match = DEMO_VITEST_HOME_ENTRY.exec(name);
|
|
195
|
+
if (!match)
|
|
196
|
+
return null;
|
|
197
|
+
const pid = Number(match[1]);
|
|
198
|
+
return Number.isSafeInteger(pid) ? pid : null;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Attribute a `$HOME` entry (#2954).
|
|
202
|
+
*
|
|
203
|
+
* Only `.commandmate-demo-vitest-<pid>` can be attributed, and only while its
|
|
204
|
+
* pid is alive: the test process's cwd is then judged exactly as a listener's
|
|
205
|
+
* is ({@link attributeAnchor}), so a parallel worktree's test run is `other`.
|
|
206
|
+
* A dead pid, an unreadable cwd, or a cwd inside this worktree leave it
|
|
207
|
+
* unattributed — a directory this worker's own tests failed to remove is still
|
|
208
|
+
* caught.
|
|
209
|
+
*/
|
|
210
|
+
function attributeHomeEntry(name, worktreePath, resolvePidCwd = exports.readLivePidCwd) {
|
|
211
|
+
const pid = demoVitestPid(name);
|
|
212
|
+
if (pid === null)
|
|
213
|
+
return 'unattributed';
|
|
214
|
+
const owner = attributeAnchor(resolvePidCwd(pid), worktreePath);
|
|
215
|
+
// `self` is not an excuse for an addition, and a file has no self to report.
|
|
216
|
+
return owner === 'other' ? 'other' : 'unattributed';
|
|
217
|
+
}
|
|
218
|
+
function attributeEntry(probeId, entry, context, resolvePidCwd) {
|
|
136
219
|
switch (probeId) {
|
|
137
220
|
case 'tmux-sessions':
|
|
138
221
|
return attributeSessionName(entry.key, context.worktreeId);
|
|
139
222
|
case 'listeners':
|
|
140
223
|
return attributeAnchor(entry.anchor, context.worktreePath);
|
|
224
|
+
case 'home-entries':
|
|
225
|
+
return attributeHomeEntry(entry.key, context.worktreePath, resolvePidCwd);
|
|
141
226
|
default:
|
|
142
227
|
// A file has no owner. `$HOME` and `~/.commandmate` are shared, so an
|
|
143
228
|
// entry appearing there is a violation for whoever is being judged — the
|
|
144
|
-
// rule the Issue's incident list is made of.
|
|
229
|
+
// rule the Issue's incident list is made of. The one `$HOME` exception is
|
|
230
|
+
// above (#2954).
|
|
145
231
|
return 'unattributed';
|
|
146
232
|
}
|
|
147
233
|
}
|
|
@@ -166,8 +252,12 @@ function resolveTaskSessionName(task) {
|
|
|
166
252
|
}
|
|
167
253
|
}
|
|
168
254
|
/** A freshly captured baseline, stamped with its task's own agent session. */
|
|
169
|
-
function recordTaskSession(snapshot, task) {
|
|
170
|
-
return {
|
|
255
|
+
function recordTaskSession(snapshot, task, server = currentSessionServer()) {
|
|
256
|
+
return {
|
|
257
|
+
...snapshot,
|
|
258
|
+
taskSession: resolveTaskSessionName(task),
|
|
259
|
+
otherServerSessions: recordOtherServerSessions(snapshot, server),
|
|
260
|
+
};
|
|
171
261
|
}
|
|
172
262
|
/**
|
|
173
263
|
* The session a baseline excuses: its name, `null` when it was recorded as
|
|
@@ -180,6 +270,106 @@ function readTaskSession(baseline) {
|
|
|
180
270
|
const recorded = baseline.taskSession;
|
|
181
271
|
return typeof recorded === 'string' && recorded !== '' ? recorded : null;
|
|
182
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* The identity of the server process this module runs in.
|
|
275
|
+
*
|
|
276
|
+
* The adoption table is read through `resolveSessionName`, which returns the
|
|
277
|
+
* adopted legacy name in place of the new-format one — this module may not
|
|
278
|
+
* reach into `lib/tmux` (#1922).
|
|
279
|
+
*/
|
|
280
|
+
function currentSessionServer() {
|
|
281
|
+
return {
|
|
282
|
+
namespace: (0, session_name_1.getActiveSessionNamespace)(),
|
|
283
|
+
legacyAliasOf: (newName) => {
|
|
284
|
+
const parsed = (0, session_name_1.parseSessionName)(newName);
|
|
285
|
+
if (!parsed)
|
|
286
|
+
return undefined;
|
|
287
|
+
try {
|
|
288
|
+
const resolved = (0, session_name_1.resolveSessionName)(parsed.cliToolId, parsed.rest);
|
|
289
|
+
return resolved !== newName ? resolved : undefined;
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Whether a session name positively belongs to a CommandMate server other than
|
|
299
|
+
* `server`.
|
|
300
|
+
*
|
|
301
|
+
* False whenever that cannot be shown: this server has no namespace (then every
|
|
302
|
+
* name could be its own), or the name is not a CommandMate session name at all.
|
|
303
|
+
*/
|
|
304
|
+
function isOtherServerSession(name, server) {
|
|
305
|
+
if (server.namespace === null)
|
|
306
|
+
return false;
|
|
307
|
+
const parsed = (0, session_name_1.parseSessionName)(name);
|
|
308
|
+
if (!parsed)
|
|
309
|
+
return false;
|
|
310
|
+
if (parsed.namespace !== null)
|
|
311
|
+
return parsed.namespace !== server.namespace;
|
|
312
|
+
let ownName;
|
|
313
|
+
try {
|
|
314
|
+
ownName = (0, session_name_1.resolveNamespacedSessionName)(server.namespace, parsed.cliToolId, parsed.rest);
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
// A name this server could never have built is not one of its adoptions.
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
return server.legacyAliasOf(ownName) !== name;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* The baseline's `mcbd-*` sessions that belonged to another CommandMate server
|
|
324
|
+
* at task creation.
|
|
325
|
+
*
|
|
326
|
+
* Recorded then, because only then is it knowable: this server's legacy
|
|
327
|
+
* adoptions are dropped when their session goes away, so by verification time
|
|
328
|
+
* a vanished adopted session would look like a stranger's.
|
|
329
|
+
*
|
|
330
|
+
* Why this is the line removals are excused on — the design note for #2627:
|
|
331
|
+
*
|
|
332
|
+
* - A removal is a violation because a worker may have killed someone else's
|
|
333
|
+
* session (#1624) or server (#1739). A session *of this server* — the
|
|
334
|
+
* sibling worktree in #1624, whose name carries this server's namespace —
|
|
335
|
+
* stays guarded exactly as before.
|
|
336
|
+
* - A session of *another server* is closed by that server's own lifecycle:
|
|
337
|
+
* the incident of 2026-09-17 was a global-install server's orchestrate
|
|
338
|
+
* finishing issue-107 and moving on to issue-108, with no kill of any kind
|
|
339
|
+
* in the three workers' transcripts. This task has no way to tell that
|
|
340
|
+
* from a worker's kill, and that server — not this gate — owns the fact.
|
|
341
|
+
* - The namespace is the designed marker of "which server" (#2866); nothing
|
|
342
|
+
* else here needs this server's database or a guess at the other
|
|
343
|
+
* repository's paths.
|
|
344
|
+
*
|
|
345
|
+
* Rejected: counting (`one other session gone is fine`) — that is the #1624
|
|
346
|
+
* shape, a sibling session disappearing while this worktree's stays; ignoring
|
|
347
|
+
* every `other` removal — that drops #1624 outright; the database's worktree
|
|
348
|
+
* table — the other server has a different database and the same worktree ids
|
|
349
|
+
* can exist in both, which is why #2866 exists; timing — the two snapshots do
|
|
350
|
+
* not say *when* inside the window something went away; the worker's command
|
|
351
|
+
* log — not recorded in a form the gate reads, and a kill run through a script
|
|
352
|
+
* would not name the session anyway.
|
|
353
|
+
*
|
|
354
|
+
* @returns the names, or null when `server` has no namespace to judge by
|
|
355
|
+
*/
|
|
356
|
+
function recordOtherServerSessions(snapshot, server) {
|
|
357
|
+
if (server.namespace === null)
|
|
358
|
+
return null;
|
|
359
|
+
const sessions = snapshot.probes['tmux-sessions'];
|
|
360
|
+
if (!sessions || sessions.status !== 'ok')
|
|
361
|
+
return [];
|
|
362
|
+
return sessions.entries
|
|
363
|
+
.map((entry) => entry.key)
|
|
364
|
+
.filter((name) => isOtherServerSession(name, server));
|
|
365
|
+
}
|
|
366
|
+
/** The recorded other-server sessions of a baseline; empty when none was recorded. */
|
|
367
|
+
function readOtherServerSessions(baseline) {
|
|
368
|
+
const recorded = baseline.otherServerSessions;
|
|
369
|
+
if (!Array.isArray(recorded))
|
|
370
|
+
return new Set();
|
|
371
|
+
return new Set(recorded.filter((name) => typeof name === 'string'));
|
|
372
|
+
}
|
|
183
373
|
function toChange(entry, owner) {
|
|
184
374
|
return { key: entry.key, detail: entry.detail, owner };
|
|
185
375
|
}
|
|
@@ -193,7 +383,9 @@ function toChange(entry, owner) {
|
|
|
193
383
|
*/
|
|
194
384
|
function diffEnvSnapshots(baseline, final, context, options = {}) {
|
|
195
385
|
const taskSession = readTaskSession(baseline);
|
|
386
|
+
const otherServerSessions = readOtherServerSessions(baseline);
|
|
196
387
|
const ignoredHomeEntries = new Set(options.ignoreHomeEntries ?? []);
|
|
388
|
+
const resolvePidCwd = options.resolvePidCwd ?? exports.readLivePidCwd;
|
|
197
389
|
const probes = env_snapshot_1.ENV_PROBE_IDS.map((probeId) => {
|
|
198
390
|
const before = baseline.probes[probeId];
|
|
199
391
|
const after = final.probes[probeId];
|
|
@@ -206,6 +398,7 @@ function diffEnvSnapshots(baseline, final, context, options = {}) {
|
|
|
206
398
|
ignoredAdded: [],
|
|
207
399
|
taskSessionAdded: [],
|
|
208
400
|
removed: [],
|
|
401
|
+
removedByOtherServer: [],
|
|
209
402
|
ignoredByConfig: [],
|
|
210
403
|
};
|
|
211
404
|
}
|
|
@@ -218,6 +411,7 @@ function diffEnvSnapshots(baseline, final, context, options = {}) {
|
|
|
218
411
|
ignoredAdded: [],
|
|
219
412
|
taskSessionAdded: [],
|
|
220
413
|
removed: [],
|
|
414
|
+
removedByOtherServer: [],
|
|
221
415
|
ignoredByConfig: [],
|
|
222
416
|
};
|
|
223
417
|
}
|
|
@@ -233,7 +427,7 @@ function diffEnvSnapshots(baseline, final, context, options = {}) {
|
|
|
233
427
|
for (const entry of after.entries) {
|
|
234
428
|
if (beforeKeys.has(entry.key))
|
|
235
429
|
continue;
|
|
236
|
-
const owner = attributeEntry(probeId, entry, context);
|
|
430
|
+
const owner = attributeEntry(probeId, entry, context, resolvePidCwd);
|
|
237
431
|
if (isIgnoredByConfig(entry)) {
|
|
238
432
|
ignoredByConfig.push(toChange(entry, owner));
|
|
239
433
|
continue;
|
|
@@ -246,12 +440,29 @@ function diffEnvSnapshots(baseline, final, context, options = {}) {
|
|
|
246
440
|
}
|
|
247
441
|
(owner === 'other' ? ignoredAdded : added).push(toChange(entry, owner));
|
|
248
442
|
}
|
|
443
|
+
// An excuse for another server's session presumes the tmux server lived
|
|
444
|
+
// through the task. When nothing from the baseline survived and the task's
|
|
445
|
+
// own session is gone too, the likeliest story is `kill-server` (#1624), and
|
|
446
|
+
// every removal is counted.
|
|
447
|
+
const tmuxServerSurvived = probeId === 'tmux-sessions' &&
|
|
448
|
+
after.entries.some((entry) => beforeKeys.has(entry.key) || (taskSession !== null && entry.key === taskSession));
|
|
249
449
|
const removed = [];
|
|
450
|
+
const removedByOtherServer = [];
|
|
250
451
|
for (const entry of before.entries) {
|
|
251
452
|
if (afterKeys.has(entry.key))
|
|
252
453
|
continue;
|
|
253
|
-
const change = toChange(entry, attributeEntry(probeId, entry, context));
|
|
254
|
-
(isIgnoredByConfig(entry)
|
|
454
|
+
const change = toChange(entry, attributeEntry(probeId, entry, context, resolvePidCwd));
|
|
455
|
+
if (isIgnoredByConfig(entry)) {
|
|
456
|
+
ignoredByConfig.push(change);
|
|
457
|
+
}
|
|
458
|
+
else if (tmuxServerSurvived &&
|
|
459
|
+
change.owner !== 'self' &&
|
|
460
|
+
otherServerSessions.has(entry.key)) {
|
|
461
|
+
removedByOtherServer.push(change);
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
removed.push(change);
|
|
465
|
+
}
|
|
255
466
|
}
|
|
256
467
|
return {
|
|
257
468
|
probeId,
|
|
@@ -261,6 +472,7 @@ function diffEnvSnapshots(baseline, final, context, options = {}) {
|
|
|
261
472
|
ignoredAdded,
|
|
262
473
|
taskSessionAdded,
|
|
263
474
|
removed,
|
|
475
|
+
removedByOtherServer,
|
|
264
476
|
ignoredByConfig,
|
|
265
477
|
};
|
|
266
478
|
});
|
|
@@ -318,6 +530,10 @@ function formatTaskSessionChanges(changes) {
|
|
|
318
530
|
* session is: a verdict that silently discounted an entry would read exactly
|
|
319
531
|
* like one that never saw it.
|
|
320
532
|
*/
|
|
533
|
+
/** Removals excused as another CommandMate server's sessions (#2627), one line each. */
|
|
534
|
+
function formatRemovedByOtherServer(changes) {
|
|
535
|
+
return changes.map((change) => ` · - ${change.key} (ignored: another CommandMate server's session, recorded at task start)`);
|
|
536
|
+
}
|
|
321
537
|
function formatIgnoredByConfig(changes) {
|
|
322
538
|
if (changes.length === 0)
|
|
323
539
|
return [];
|
|
@@ -350,6 +566,7 @@ function formatEnvCleanReport(diff) {
|
|
|
350
566
|
: '';
|
|
351
567
|
lines.push(` ${probe.probeId} clean (${label})${excused}`);
|
|
352
568
|
lines.push(...formatTaskSessionChanges(probe.taskSessionAdded));
|
|
569
|
+
lines.push(...formatRemovedByOtherServer(probe.removedByOtherServer));
|
|
353
570
|
lines.push(...formatIgnoredByConfig(probe.ignoredByConfig));
|
|
354
571
|
continue;
|
|
355
572
|
}
|
|
@@ -357,6 +574,7 @@ function formatEnvCleanReport(diff) {
|
|
|
357
574
|
lines.push(...formatChanges('+', probe.added));
|
|
358
575
|
lines.push(...formatChanges('-', probe.removed));
|
|
359
576
|
lines.push(...formatTaskSessionChanges(probe.taskSessionAdded));
|
|
577
|
+
lines.push(...formatRemovedByOtherServer(probe.removedByOtherServer));
|
|
360
578
|
lines.push(...formatIgnoredByConfig(probe.ignoredByConfig));
|
|
361
579
|
for (const excused of probe.ignoredAdded) {
|
|
362
580
|
lines.push(` · ${excused.key} (ignored: belongs to another worktree)`);
|
|
@@ -415,6 +633,7 @@ async function evaluateEnvClean(input) {
|
|
|
415
633
|
}
|
|
416
634
|
const diff = diffEnvSnapshots(input.baseline, final, input, {
|
|
417
635
|
ignoreHomeEntries: input.ignoreHomeEntries,
|
|
636
|
+
resolvePidCwd: input.resolvePidCwd,
|
|
418
637
|
});
|
|
419
638
|
const header = `${verify_config_1.ENV_CLEAN_GATE_ID}: baseline=${new Date(input.baseline.capturedAt).toISOString()} ` +
|
|
420
639
|
`status=${diff.status} task-session=${describeTaskSession(diff.taskSession)}`;
|