commandmate 0.22.2 → 0.23.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 +108 -108
- package/.next/app-path-routes-manifest.json +21 -21
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +12 -12
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route.js +1 -1
- package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route.js +1 -1
- package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route.js +1 -1
- package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route.js +1 -1
- package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js +3 -3
- package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/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.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/external-apps/route.js.nft.json +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route.js +1 -1
- package/.next/server/app/api/fs/browse/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route.js.nft.json +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js +1 -1
- 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.js +1 -1
- package/.next/server/app/api/metrics/vibe/route.js.nft.json +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/subscriptions/route.js +1 -1
- package/.next/server/app/api/push/subscriptions/route.js.nft.json +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route.js +1 -1
- package/.next/server/app/api/push/vapid/route.js.nft.json +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route.js +1 -1
- package/.next/server/app/api/repositories/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route.js +1 -1
- package/.next/server/app/api/repositories/excluded/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/skills/installations/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route.js +1 -1
- package/.next/server/app/api/skills/operations/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route.js +1 -1
- package/.next/server/app/api/skills/reindex/route.js.nft.json +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.js +1 -4
- package/.next/server/app/api/tasks/[taskId]/cancel/route.js.nft.json +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.js +1 -4
- package/.next/server/app/api/tasks/[taskId]/route.js.nft.json +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route.js +1 -1
- package/.next/server/app/api/templates/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route.js +1 -1
- package/.next/server/app/api/templates/route.js.nft.json +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route.js +1 -1
- package/.next/server/app/api/todos/route.js.nft.json +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/verification/runs/route.js.nft.json +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]/cli-tool/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js.nft.json +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 +2 -2
- 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]/execution-logs/[logId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js.nft.json +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 +2 -2
- 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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route.js.nft.json +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.js +3 -3
- package/.next/server/app/api/worktrees/[id]/marp-render/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.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]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +2 -2
- 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 +1 -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 -3
- 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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route.js.nft.json +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]/start-polling/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/tasks/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -4
- package/.next/server/app/api/worktrees/[id]/verify/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route.js +1 -1
- package/.next/server/app/proxy/[...path]/route.js.nft.json +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.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 +2 -2
- 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 +21 -21
- package/.next/server/chunks/1029.js +1 -1
- package/.next/server/chunks/1068.js +1 -1
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2546.js +1 -1
- package/.next/server/chunks/3241.js +1 -0
- package/.next/server/chunks/3417.js +1 -1
- package/.next/server/chunks/406.js +10 -6
- package/.next/server/chunks/4444.js +1 -1
- package/.next/server/chunks/5959.js +4 -5
- package/.next/server/chunks/5998.js +1 -12
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6274.js +3 -3
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6781.js +1 -0
- package/.next/server/chunks/7458.js +6 -0
- package/.next/server/chunks/{3753.js → 7970.js} +57 -32
- package/.next/server/chunks/8536.js +1 -1
- package/.next/server/chunks/8807.js +180 -127
- package/.next/server/chunks/9058.js +1 -1
- package/.next/server/chunks/9067.js +1 -1
- package/.next/server/chunks/9360.js +1 -1
- package/.next/server/chunks/9687.js +2 -2
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/server/src/middleware.js.map +1 -1
- package/.next/static/chunks/{6488-53a3e1d78e4fe8f7.js → 6488-95ba9d24a76f0cc5.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/{page-6dc0b8c4e808ea3e.js → page-966a451ae7b0903a.js} +1 -1
- package/.next/trace +12 -12
- package/dist/server/src/config/log-config.js +10 -1
- package/dist/server/src/config/safe-directory.js +103 -0
- package/dist/server/src/config/system-directories.js +74 -9
- package/dist/server/src/lib/cli-tools/antigravity.js +19 -2
- package/dist/server/src/lib/cli-tools/codex.js +95 -1
- package/dist/server/src/lib/cli-tools/copilot.js +30 -1
- package/dist/server/src/lib/cli-tools/gemini.js +28 -2
- package/dist/server/src/lib/cli-tools/opencode.js +51 -2
- package/dist/server/src/lib/git/git-default-branch.js +59 -0
- package/dist/server/src/lib/hooks/agent-event-service.js +205 -0
- package/dist/server/src/lib/hooks/agent-event-types.js +7 -86
- package/dist/server/src/lib/hooks/hook-settings-generator.js +26 -11
- package/dist/server/src/lib/hooks/permission-decision-service.js +269 -0
- package/dist/server/src/lib/hooks/sources/abstain.js +78 -0
- package/dist/server/src/lib/hooks/sources/antigravity/hooks-config.js +372 -0
- package/dist/server/src/lib/hooks/sources/antigravity/source.js +225 -0
- package/dist/server/src/lib/hooks/sources/antigravity/tool-id.js +16 -0
- package/dist/server/src/lib/hooks/sources/claude/source.js +143 -0
- package/dist/server/src/lib/hooks/sources/claude/tool-id.js +18 -0
- package/dist/server/src/lib/hooks/sources/codex/hooks-config.js +465 -0
- package/dist/server/src/lib/hooks/sources/codex/source.js +138 -0
- package/dist/server/src/lib/hooks/sources/codex/tool-id.js +15 -0
- package/dist/server/src/lib/hooks/sources/copilot/hook-settings.js +473 -0
- package/dist/server/src/lib/hooks/sources/copilot/source.js +210 -0
- package/dist/server/src/lib/hooks/sources/copilot/tool-id.js +15 -0
- package/dist/server/src/lib/hooks/sources/define-source.js +169 -0
- package/dist/server/src/lib/hooks/sources/event-mapper.js +157 -0
- package/dist/server/src/lib/hooks/sources/gemini/event-vocabulary.js +102 -0
- package/dist/server/src/lib/hooks/sources/gemini/settings-generator.js +315 -0
- package/dist/server/src/lib/hooks/sources/gemini/shared-config-tree.js +103 -0
- package/dist/server/src/lib/hooks/sources/gemini/source.js +151 -0
- package/dist/server/src/lib/hooks/sources/gemini/tool-id.js +17 -0
- package/dist/server/src/lib/hooks/sources/hook-event-vocabulary.js +111 -0
- package/dist/server/src/lib/hooks/sources/index.js +62 -0
- package/dist/server/src/lib/hooks/sources/legacy-relay.js +84 -0
- package/dist/server/src/lib/hooks/sources/opencode/client.js +299 -0
- package/dist/server/src/lib/hooks/sources/opencode/ingest.js +261 -0
- package/dist/server/src/lib/hooks/sources/opencode/mappers.js +154 -0
- package/dist/server/src/lib/hooks/sources/opencode/payloads.js +189 -0
- package/dist/server/src/lib/hooks/sources/opencode/ports.js +295 -0
- package/dist/server/src/lib/hooks/sources/opencode/runtime.js +159 -0
- package/dist/server/src/lib/hooks/sources/opencode/source.js +250 -0
- package/dist/server/src/lib/hooks/sources/opencode/subscription.js +422 -0
- package/dist/server/src/lib/hooks/sources/opencode/tool-id.js +14 -0
- package/dist/server/src/lib/hooks/sources/opencode/turn-gate.js +193 -0
- package/dist/server/src/lib/hooks/sources/pending-decisions.js +127 -0
- package/dist/server/src/lib/hooks/sources/registry.js +111 -0
- package/dist/server/src/lib/hooks/sources/types.js +46 -0
- package/dist/server/src/lib/session/agent-session-lifecycle.js +82 -0
- package/dist/server/src/lib/session/claude-session.js +10 -3
- package/dist/server/src/lib/tasks/contract-message.js +175 -0
- package/dist/server/src/lib/verification/env-clean-gate.js +320 -0
- package/dist/server/src/lib/verification/env-snapshot.js +474 -0
- package/dist/server/src/lib/verification/gate-runner.js +802 -0
- package/dist/server/src/lib/verification/scope-gate.js +402 -0
- package/dist/server/src/lib/verification/verify-config.js +281 -0
- package/package.json +1 -1
- package/scripts/hooks/cmate-agent-event.sh +73 -3
- package/.next/server/chunks/8856.js +0 -4
- /package/.next/static/{XpKfn5x6H-mtTaXI9yNmb → wH6K6QNPHSdATFkVoIcy3}/_buildManifest.js +0 -0
- /package/.next/static/{XpKfn5x6H-mtTaXI9yNmb → wH6K6QNPHSdATFkVoIcy3}/_ssgManifest.js +0 -0
|
@@ -18,6 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
exports.getLogDir = getLogDir;
|
|
19
19
|
const path_1 = __importDefault(require("path"));
|
|
20
20
|
const env_1 = require("../lib/env");
|
|
21
|
+
const safe_directory_1 = require("./safe-directory");
|
|
21
22
|
/**
|
|
22
23
|
* Get the log directory path.
|
|
23
24
|
*
|
|
@@ -25,6 +26,13 @@ const env_1 = require("../lib/env");
|
|
|
25
26
|
* 1. CM_LOG_DIR environment variable (with MCBD_LOG_DIR fallback via getEnvByKey)
|
|
26
27
|
* 2. Default: `${process.cwd()}/data/logs`
|
|
27
28
|
*
|
|
29
|
+
* Issue #1774: a CM_LOG_DIR inside `/proc`, `/sys` or `/dev` falls back to the
|
|
30
|
+
* default with a warning. `log-manager.ensureLogDirectory()` calls
|
|
31
|
+
* `fs.mkdir(dir, {recursive:true})` on the result, and on Linux that promise
|
|
32
|
+
* never settles for such a path — it holds a libuv threadpool thread for the
|
|
33
|
+
* life of the process instead. Throwing is not an option here: the log
|
|
34
|
+
* directory is where the complaint would have to go.
|
|
35
|
+
*
|
|
28
36
|
* @returns Absolute path to the log directory
|
|
29
37
|
*
|
|
30
38
|
* @example
|
|
@@ -37,5 +45,6 @@ const env_1 = require("../lib/env");
|
|
|
37
45
|
* ```
|
|
38
46
|
*/
|
|
39
47
|
function getLogDir() {
|
|
40
|
-
|
|
48
|
+
const fallback = path_1.default.join(process.cwd(), 'data', 'logs');
|
|
49
|
+
return (0, safe_directory_1.resolveSafeDirectory)((0, env_1.getEnvByKey)('CM_LOG_DIR'), fallback, 'CM_LOG_DIR');
|
|
41
50
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The one place a configured directory becomes a directory this process is
|
|
4
|
+
* willing to create (Issue #1774).
|
|
5
|
+
*
|
|
6
|
+
* ## What this is defending against
|
|
7
|
+
*
|
|
8
|
+
* Pointing a configured path at `/proc`, `/sys` or `/dev` does not make
|
|
9
|
+
* CommandMate fail — it makes it **stop**. procfs answers a `mkdir` for a child
|
|
10
|
+
* that cannot exist with ENOENT rather than EPERM, and Node's recursive mkdir
|
|
11
|
+
* reads ENOENT as "the parent is missing", creates the parent, and retries; the
|
|
12
|
+
* parent is created with EEXIST, so one level is enough to loop. Measured in a
|
|
13
|
+
* container: `mkdirSync('/proc/x/y', {recursive:true})` had not returned after
|
|
14
|
+
* 25s at 100.4% CPU, and `fs.promises.mkdir` on the same path never settled and
|
|
15
|
+
* held a libuv threadpool thread (there are four) forever. The synchronous form
|
|
16
|
+
* stops the event loop outright, which is also why the `try/catch` these call
|
|
17
|
+
* sites already have does nothing: control never reaches it. There is no error
|
|
18
|
+
* log, no OOM and no timeout — the process simply goes quiet. PR #1773's CI job
|
|
19
|
+
* ran 5h31m that way.
|
|
20
|
+
*
|
|
21
|
+
* On macOS none of the three paths exist, so the same call throws instantly and
|
|
22
|
+
* everything looks fine. The failure is Linux- and container-only.
|
|
23
|
+
*
|
|
24
|
+
* ## Why a resolver rather than a check at each mkdir
|
|
25
|
+
*
|
|
26
|
+
* Five settings reach a recursive mkdir this way (`CM_AGENT_HOOKS_DIR`,
|
|
27
|
+
* `CM_LOG_DIR`, `CODEX_HOME`, `COPILOT_HOME`, `CM_OPENCODE_PORT_FILE`), and
|
|
28
|
+
* three of the five arrived with Phase 4 (#1760 / #1761 / #1763) as copies of
|
|
29
|
+
* the first. Guarding the mkdir sites individually invites a sixth copy; putting
|
|
30
|
+
* the guard on the *resolution* of the value means a new tool is protected by
|
|
31
|
+
* calling the resolver its siblings already call.
|
|
32
|
+
*
|
|
33
|
+
* ## Why it never throws
|
|
34
|
+
*
|
|
35
|
+
* A bad log directory must not take out logging, and hook injection is
|
|
36
|
+
* fail-open by design (`hook-settings-generator.buildClaudeLaunchCommand`).
|
|
37
|
+
* Falling back to the built-in default and saying so keeps a misconfigured
|
|
38
|
+
* server running with its events intact.
|
|
39
|
+
*
|
|
40
|
+
* @module config/safe-directory
|
|
41
|
+
*/
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.resetSafeDirectoryWarnings = resetSafeDirectoryWarnings;
|
|
44
|
+
exports.resolveSafeDirectory = resolveSafeDirectory;
|
|
45
|
+
const logger_1 = require("../lib/logger");
|
|
46
|
+
const system_directories_1 = require("./system-directories");
|
|
47
|
+
const logger = (0, logger_1.createLogger)('safe-directory');
|
|
48
|
+
/**
|
|
49
|
+
* Settings already warned about, so a rejection is reported once rather than on
|
|
50
|
+
* every call.
|
|
51
|
+
*
|
|
52
|
+
* `getLogDir()` runs on the path of every log write; an unconditional warning
|
|
53
|
+
* would turn one misconfigured variable into a log flood, in the log directory
|
|
54
|
+
* that is already not the one the operator asked for.
|
|
55
|
+
*/
|
|
56
|
+
const warnedRejections = new Set();
|
|
57
|
+
/** Forget which rejections have been warned about. For tests. */
|
|
58
|
+
function resetSafeDirectoryWarnings() {
|
|
59
|
+
warnedRejections.clear();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The directory to actually use for `candidate`, which is `candidate` unless
|
|
63
|
+
* using it would hang the process.
|
|
64
|
+
*
|
|
65
|
+
* Pure with respect to the filesystem in the sense that matters here: it reads
|
|
66
|
+
* (symlink resolution) but creates nothing, so it is safe to call — and to test
|
|
67
|
+
* — with a path that no `mkdir` could survive.
|
|
68
|
+
*
|
|
69
|
+
* A *file* path is a valid candidate. The check is prefix-based, so a file
|
|
70
|
+
* inside a virtual filesystem is rejected exactly as its directory would be;
|
|
71
|
+
* `CM_OPENCODE_PORT_FILE` relies on that.
|
|
72
|
+
*
|
|
73
|
+
* @param candidate - The configured path. Empty or undefined means "unset"
|
|
74
|
+
* @param fallback - The built-in default to use when the candidate is refused
|
|
75
|
+
* @param source - What supplied the candidate, e.g. `'CM_LOG_DIR'`. Appears in
|
|
76
|
+
* the warning, because the operator's next question is which setting to fix
|
|
77
|
+
* @returns `candidate`, or `fallback` when the candidate is inside `/proc`,
|
|
78
|
+
* `/sys` or `/dev`
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const fallback = join(homedir(), '.commandmate', 'hooks');
|
|
83
|
+
* return resolveSafeDirectory(process.env.CM_AGENT_HOOKS_DIR, fallback, 'CM_AGENT_HOOKS_DIR');
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
function resolveSafeDirectory(candidate, fallback, source) {
|
|
87
|
+
if (!candidate)
|
|
88
|
+
return fallback;
|
|
89
|
+
if (!(0, system_directories_1.isVirtualFilesystemPath)(candidate))
|
|
90
|
+
return candidate;
|
|
91
|
+
const key = `${source}|${candidate}`;
|
|
92
|
+
if (!warnedRejections.has(key)) {
|
|
93
|
+
warnedRejections.add(key);
|
|
94
|
+
logger.warn('virtual-filesystem-path-rejected', {
|
|
95
|
+
source,
|
|
96
|
+
candidate,
|
|
97
|
+
fallback,
|
|
98
|
+
reason: 'A recursive mkdir under /proc, /sys or /dev never returns on Linux; ' +
|
|
99
|
+
'the built-in default is used instead (Issue #1774).',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return fallback;
|
|
103
|
+
}
|
|
@@ -13,9 +13,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
14
|
};
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.SYSTEM_DIRECTORIES = void 0;
|
|
16
|
+
exports.VIRTUAL_FILESYSTEM_ROOTS = exports.SYSTEM_DIRECTORIES = void 0;
|
|
17
17
|
exports.isPathWithin = isPathWithin;
|
|
18
18
|
exports.isSystemDirectory = isSystemDirectory;
|
|
19
|
+
exports.isVirtualFilesystemPath = isVirtualFilesystemPath;
|
|
19
20
|
const fs_1 = __importDefault(require("fs"));
|
|
20
21
|
const path_1 = __importDefault(require("path"));
|
|
21
22
|
/**
|
|
@@ -36,6 +37,35 @@ exports.SYSTEM_DIRECTORIES = [
|
|
|
36
37
|
'/sys',
|
|
37
38
|
'/proc',
|
|
38
39
|
];
|
|
40
|
+
/**
|
|
41
|
+
* The subset of {@link SYSTEM_DIRECTORIES} that is not a filesystem at all.
|
|
42
|
+
*
|
|
43
|
+
* Issue #1774: these three are kernel-backed namespaces, and their `mkdir`
|
|
44
|
+
* error codes are not the ones `fs` callers assume. procfs answers a `mkdir`
|
|
45
|
+
* for a child that cannot exist with **ENOENT rather than EPERM**, and Node's
|
|
46
|
+
* recursive mkdir reads ENOENT as "the parent is missing", so it creates the
|
|
47
|
+
* parent and retries — forever. Measured in a container: `mkdirSync('/proc/x/y',
|
|
48
|
+
* {recursive:true})` had not returned after 25s at 100.4% CPU; the promise from
|
|
49
|
+
* `fs.promises.mkdir` never settled and held a libuv threadpool thread (4 by
|
|
50
|
+
* default) for the life of the process. Neither logs anything, and the
|
|
51
|
+
* synchronous form stops the event loop, so a surrounding `try/catch` is never
|
|
52
|
+
* reached. On macOS the paths do not exist and the call throws at once, which
|
|
53
|
+
* is why this is invisible outside Linux and containers.
|
|
54
|
+
*
|
|
55
|
+
* Kept as a named subset rather than reusing the whole list because the two
|
|
56
|
+
* lists answer different questions. {@link isSystemDirectory} answers "may a
|
|
57
|
+
* database live here", and rejects `/tmp` and `/var` for that; but `/tmp` and
|
|
58
|
+
* `/var/log` are ordinary writable directories that a log or hook directory may
|
|
59
|
+
* legitimately be pointed at — `os.tmpdir()` is *inside* one on both platforms
|
|
60
|
+
* (`/tmp` on Linux, `/var/folders/…` on macOS), which is where every isolated
|
|
61
|
+
* test directory in this repository lives. Rejecting those would not prevent a
|
|
62
|
+
* hang; it would redirect isolated test runs and container deployments back
|
|
63
|
+
* onto the real `~/.codex` and `~/.commandmate`.
|
|
64
|
+
*
|
|
65
|
+
* `SYSTEM_DIRECTORIES` remains the superset — `tests/unit/config/system-directories.test.ts`
|
|
66
|
+
* pins that, so the two cannot drift apart.
|
|
67
|
+
*/
|
|
68
|
+
exports.VIRTUAL_FILESYSTEM_ROOTS = ['/proc', '/sys', '/dev'];
|
|
39
69
|
/**
|
|
40
70
|
* POSIX separator. SYSTEM_DIRECTORIES are POSIX absolute paths and this guard
|
|
41
71
|
* protects POSIX platforms, so the boundary is always '/' rather than path.sep.
|
|
@@ -66,13 +96,14 @@ function isPathWithin(target, dir) {
|
|
|
66
96
|
*
|
|
67
97
|
* Mount layout does not change while the process runs, so this is computed once.
|
|
68
98
|
*/
|
|
69
|
-
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
99
|
+
const resolvedDirectoryCandidatesCache = new Map();
|
|
100
|
+
function getDirectoryCandidates(directories) {
|
|
101
|
+
const cached = resolvedDirectoryCandidatesCache.get(directories);
|
|
102
|
+
if (cached !== undefined) {
|
|
103
|
+
return cached;
|
|
73
104
|
}
|
|
74
105
|
const candidates = new Set();
|
|
75
|
-
for (const dir of
|
|
106
|
+
for (const dir of directories) {
|
|
76
107
|
// Always keep the literal form: it must stay enforced even when the
|
|
77
108
|
// directory does not exist on this platform (e.g. /proc and /sys on macOS).
|
|
78
109
|
candidates.add(dir);
|
|
@@ -83,8 +114,9 @@ function getSystemDirectoryCandidates() {
|
|
|
83
114
|
// Not present on this platform; the literal form still applies.
|
|
84
115
|
}
|
|
85
116
|
}
|
|
86
|
-
|
|
87
|
-
|
|
117
|
+
const resolved = Array.from(candidates);
|
|
118
|
+
resolvedDirectoryCandidatesCache.set(directories, resolved);
|
|
119
|
+
return resolved;
|
|
88
120
|
}
|
|
89
121
|
/**
|
|
90
122
|
* Resolve symlinks in an absolute path, tolerating components that do not exist.
|
|
@@ -140,7 +172,40 @@ function resolvePhysicalPath(absolutePath) {
|
|
|
140
172
|
* @returns true if the path is within a system directory
|
|
141
173
|
*/
|
|
142
174
|
function isSystemDirectory(inputPath) {
|
|
175
|
+
return matchesAnyDirectory(inputPath, exports.SYSTEM_DIRECTORIES);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Check if a path is inside a virtual filesystem ({@link VIRTUAL_FILESYSTEM_ROOTS}).
|
|
179
|
+
*
|
|
180
|
+
* Issue #1774: the question `isSystemDirectory` cannot answer for a directory
|
|
181
|
+
* that is about to be created. A recursive `mkdir` under one of these roots does
|
|
182
|
+
* not fail — it hangs the process, unrecoverably and without a log line — so
|
|
183
|
+
* every path that reaches a `mkdir(…, {recursive:true})` has to be filtered
|
|
184
|
+
* through this first. See {@link VIRTUAL_FILESYSTEM_ROOTS} for the measurements
|
|
185
|
+
* and for why this is a subset of `SYSTEM_DIRECTORIES` rather than all of it.
|
|
186
|
+
*
|
|
187
|
+
* Matching is the same as `isSystemDirectory`: lexical *and* physical form, so a
|
|
188
|
+
* symlink that points into `/proc` is rejected along with a literal `/proc/…`.
|
|
189
|
+
* That is the direction that matters — a missed one hangs the server.
|
|
190
|
+
*
|
|
191
|
+
* Reads the filesystem (symlink resolution); it never creates anything, and
|
|
192
|
+
* `realpath` on a procfs path returns ENOENT immediately rather than spinning.
|
|
193
|
+
*
|
|
194
|
+
* @param inputPath - The path to check (relative paths are resolved against cwd)
|
|
195
|
+
* @returns true if a recursive mkdir for this path could spin
|
|
196
|
+
*/
|
|
197
|
+
function isVirtualFilesystemPath(inputPath) {
|
|
198
|
+
return matchesAnyDirectory(inputPath, exports.VIRTUAL_FILESYSTEM_ROOTS);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Shared matcher for {@link isSystemDirectory} and {@link isVirtualFilesystemPath}.
|
|
202
|
+
*
|
|
203
|
+
* @param inputPath - The path to check
|
|
204
|
+
* @param directories - Absolute directory prefixes to test against
|
|
205
|
+
* @returns true if either the lexical or the physical form lands in one of them
|
|
206
|
+
*/
|
|
207
|
+
function matchesAnyDirectory(inputPath, directories) {
|
|
143
208
|
const lexicalPath = path_1.default.resolve(inputPath);
|
|
144
209
|
const physicalPath = resolvePhysicalPath(lexicalPath);
|
|
145
|
-
return
|
|
210
|
+
return getDirectoryCandidates(directories).some((dir) => isPathWithin(lexicalPath, dir) || isPathWithin(physicalPath, dir));
|
|
146
211
|
}
|
|
@@ -16,6 +16,8 @@ const tmux_1 = require("../tmux/tmux");
|
|
|
16
16
|
const submit_verified_sender_1 = require("./submit-verified-sender");
|
|
17
17
|
const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
|
|
18
18
|
const cli_patterns_1 = require("../detection/cli-patterns");
|
|
19
|
+
const sources_1 = require("../../lib/hooks/sources");
|
|
20
|
+
const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
|
|
19
21
|
const logger_1 = require("../../lib/logger");
|
|
20
22
|
const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
|
|
21
23
|
const logger = (0, logger_1.createLogger)('cli-tools/antigravity');
|
|
@@ -116,6 +118,11 @@ class AntigravityTool extends base_1.BaseCLITool {
|
|
|
116
118
|
logger.info('antigravity-session-exists');
|
|
117
119
|
return;
|
|
118
120
|
}
|
|
121
|
+
// Issue #1762: fence this instance's structured events off from the process
|
|
122
|
+
// that used to hold the same (worktree, tool, instance) key. Creation path
|
|
123
|
+
// only — the reuse branch above has already returned — and before the pane
|
|
124
|
+
// exists. Bumped even if the launch below then fails.
|
|
125
|
+
(0, agent_session_lifecycle_1.beginAgentSession)({ worktreeId, cliToolId: sources_1.ANTIGRAVITY_CLI_TOOL_ID, instanceId });
|
|
119
126
|
try {
|
|
120
127
|
// Create tmux session with large history buffer for agy output
|
|
121
128
|
// (agy is inline-rendered and retains scrollback, like Codex)
|
|
@@ -127,8 +134,18 @@ class AntigravityTool extends base_1.BaseCLITool {
|
|
|
127
134
|
});
|
|
128
135
|
// Wait a moment for the session to be created
|
|
129
136
|
await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
|
|
130
|
-
// Start agy in interactive mode, optionally pinned to a model
|
|
131
|
-
|
|
137
|
+
// Start agy in interactive mode, optionally pinned to a model.
|
|
138
|
+
//
|
|
139
|
+
// Issue #1762: `prepareAgentLaunch` merges CommandMate's named hook into
|
|
140
|
+
// `~/.gemini/config/hooks.json` — agy's single global config, shared with
|
|
141
|
+
// gemini's tree and with whatever the user has in it — and prefixes
|
|
142
|
+
// `CM_HOOK_URL`. That variable is the *only* correlation channel agy has:
|
|
143
|
+
// its payloads carry no `cwd`, its hooks run in `~/.gemini/config`, and
|
|
144
|
+
// one file serves every worktree on the machine. `--model` is appended
|
|
145
|
+
// after, so the env prefix stays in front of the command.
|
|
146
|
+
// `CM_AGENT_HOOKS_INJECT=0` returns bare `agy`, unchanged from before.
|
|
147
|
+
const base = (0, agent_session_lifecycle_1.prepareAgentLaunch)({ worktreeId, cliToolId: sources_1.ANTIGRAVITY_CLI_TOOL_ID, instanceId }, this.command).command;
|
|
148
|
+
const launchCommand = model ? `${base} --model ${shellSingleQuote(model)}` : base;
|
|
132
149
|
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
133
150
|
// Wait for agy to initialize
|
|
134
151
|
await new Promise((resolve) => setTimeout(resolve, ANTIGRAVITY_INIT_WAIT_MS));
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.CodexTool = void 0;
|
|
8
|
+
exports.isCodexHooksReviewDialog = isCodexHooksReviewDialog;
|
|
8
9
|
const base_1 = require("./base");
|
|
9
10
|
const tmux_1 = require("../tmux/tmux");
|
|
10
11
|
const submit_verified_sender_1 = require("./submit-verified-sender");
|
|
11
12
|
const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
|
|
12
13
|
const cli_patterns_1 = require("../detection/cli-patterns");
|
|
13
14
|
const logger_1 = require("../../lib/logger");
|
|
15
|
+
const sources_1 = require("../../lib/hooks/sources");
|
|
16
|
+
const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
|
|
14
17
|
const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
|
|
15
18
|
const logger = (0, logger_1.createLogger)('cli-tools/codex');
|
|
16
19
|
/**
|
|
@@ -28,6 +31,63 @@ const CODEX_POLL_INTERVAL_MS = 1000;
|
|
|
28
31
|
const CODEX_INIT_MAX_ATTEMPTS = 30;
|
|
29
32
|
/** Timeout for waiting for prompt before sending a message */
|
|
30
33
|
const CODEX_PROMPT_WAIT_TIMEOUT_MS = 15000;
|
|
34
|
+
/**
|
|
35
|
+
* Anchors of codex's "Hooks need review" dialog (Issue #1760, measured on
|
|
36
|
+
* codex-cli 0.147.0):
|
|
37
|
+
*
|
|
38
|
+
* ```
|
|
39
|
+
* Hooks need review
|
|
40
|
+
* 5 hooks are new or changed.
|
|
41
|
+
* Hooks can run outside the sandbox after you trust them.
|
|
42
|
+
*
|
|
43
|
+
* > 1. Review hooks
|
|
44
|
+
* 2. Trust all and continue
|
|
45
|
+
* 3. Continue without trusting (hooks won't run)
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* It has to be handled here rather than in `getCodexActiveDialog`, which
|
|
49
|
+
* classifies it as `null`: its wording matches none of that function's three
|
|
50
|
+
* anchors (`Skip until next version` / `Do you trust` / `Press enter to
|
|
51
|
+
* continue`). Measured consequence of leaving it alone, on a pane with the
|
|
52
|
+
* generated `hooks.json` present: `isCodexPromptReady` stays false for the full
|
|
53
|
+
* 30-attempt window and the session is then handed to `sendMessage` still
|
|
54
|
+
* sitting on the dialog. Every launch, because "continue without trusting" is
|
|
55
|
+
* not remembered — verified by relaunching and getting the same screen.
|
|
56
|
+
*
|
|
57
|
+
* Both strings are required so a "hooks" mention elsewhere cannot select an
|
|
58
|
+
* option on a live prompt.
|
|
59
|
+
*/
|
|
60
|
+
const CODEX_HOOKS_REVIEW_ANCHORS = ['Hooks need review', 'Continue without trusting'];
|
|
61
|
+
/**
|
|
62
|
+
* Option 3, "Continue without trusting (hooks won't run)".
|
|
63
|
+
*
|
|
64
|
+
* Not option 2. Trusting writes `[hooks.state."<file>:<event>:0:0"]` entries
|
|
65
|
+
* into the operator's own `~/.codex/config.toml` — the file that carries, among
|
|
66
|
+
* other things, the `notify` command a Computer Use integration runs — and
|
|
67
|
+
* granting that on someone's behalf is not this server's call. So a session
|
|
68
|
+
* starts with the hooks inert and the screen scraper doing exactly what it did
|
|
69
|
+
* before Issue #1760; the human enables them once through codex's own review
|
|
70
|
+
* screen, or an operator sets `CM_CODEX_HOOK_TRUST=bypass`.
|
|
71
|
+
*
|
|
72
|
+
* Sent alone, like the other numbered dialogs (Issue #890): codex confirms a
|
|
73
|
+
* numbered selection instantly, and a trailing Enter would land on the next
|
|
74
|
+
* screen. Verified live — the prompt was ready on the following poll.
|
|
75
|
+
*/
|
|
76
|
+
const CODEX_HOOKS_REVIEW_DECLINE_KEY = '3';
|
|
77
|
+
/**
|
|
78
|
+
* Whether the pane is sitting on the hooks review dialog.
|
|
79
|
+
*
|
|
80
|
+
* Position-independent on purpose, unlike `getCodexActiveDialog`: the only
|
|
81
|
+
* caller checks `isCodexPromptReady` first and returns when a genuine prompt
|
|
82
|
+
* exists, so residual dialog text above a live prompt is never reached, and a
|
|
83
|
+
* one-shot guard stops the key being sent twice.
|
|
84
|
+
*
|
|
85
|
+
* @param output - ANSI-stripped pane capture
|
|
86
|
+
* @returns True when both anchors of the dialog are present
|
|
87
|
+
*/
|
|
88
|
+
function isCodexHooksReviewDialog(output) {
|
|
89
|
+
return CODEX_HOOKS_REVIEW_ANCHORS.every((anchor) => output.includes(anchor));
|
|
90
|
+
}
|
|
31
91
|
/**
|
|
32
92
|
* Codex CLI tool implementation
|
|
33
93
|
* Manages Codex sessions using tmux
|
|
@@ -66,6 +126,18 @@ class CodexTool extends base_1.BaseCLITool {
|
|
|
66
126
|
logger.info('codex-session-sessionname');
|
|
67
127
|
return;
|
|
68
128
|
}
|
|
129
|
+
// Issue #1760: everything the previous codex process reported through this
|
|
130
|
+
// (worktreeId, instanceId) belongs to a session that no longer exists, and
|
|
131
|
+
// the key is reused verbatim by the one about to be created. Without this
|
|
132
|
+
// fence the old process's last `user_prompt_submit` reads as the new one's
|
|
133
|
+
// and a brand-new session publishes `running` before anyone has typed into
|
|
134
|
+
// it (#1723).
|
|
135
|
+
//
|
|
136
|
+
// Creation path only — the reuse branch above has already returned — and
|
|
137
|
+
// before `createSession`, so no live pane is ever matched against a stale
|
|
138
|
+
// generation. Bumped even when the launch then fails: falling back to the
|
|
139
|
+
// scraper is always safe, trusting a dead session's events is not.
|
|
140
|
+
(0, agent_session_lifecycle_1.beginAgentSession)({ worktreeId, cliToolId: sources_1.CODEX_CLI_TOOL_ID, instanceId });
|
|
69
141
|
try {
|
|
70
142
|
// Create tmux session. Codex is inline-rendered, so its transcript lives in
|
|
71
143
|
// the pane scrollback — depth comes from the shared TMUX_HISTORY_LIMIT
|
|
@@ -76,8 +148,18 @@ class CodexTool extends base_1.BaseCLITool {
|
|
|
76
148
|
});
|
|
77
149
|
// Wait a moment for the session to be created
|
|
78
150
|
await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
|
|
151
|
+
// Issue #1760: hand this session its correlation keys, writing codex's
|
|
152
|
+
// hooks config first if it is not already there. codex has no
|
|
153
|
+
// `--settings`, so the keys ride in environment assignments on the launch
|
|
154
|
+
// line and the hook commands read them back — which is what tells `codex`
|
|
155
|
+
// from `codex-2` in one worktree, since `cwd` is identical for both.
|
|
156
|
+
//
|
|
157
|
+
// Falls back to the bare command on any failure and when
|
|
158
|
+
// `CM_AGENT_HOOKS_INJECT=0`; a session that starts without hooks is the
|
|
159
|
+
// pre-#1760 status quo, and a session that fails to start is not.
|
|
160
|
+
const launchCommand = (0, agent_session_lifecycle_1.prepareAgentLaunch)({ worktreeId, cliToolId: sources_1.CODEX_CLI_TOOL_ID, instanceId }, this.command).command;
|
|
79
161
|
// Start Codex CLI in interactive mode
|
|
80
|
-
await (0, tmux_1.sendKeys)(sessionName,
|
|
162
|
+
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
81
163
|
// Wait for Codex to initialize
|
|
82
164
|
await new Promise((resolve) => setTimeout(resolve, CODEX_INIT_WAIT_MS));
|
|
83
165
|
// Poll until Codex interactive prompt is ready
|
|
@@ -102,6 +184,7 @@ class CodexTool extends base_1.BaseCLITool {
|
|
|
102
184
|
// update branch re-sends "2" every poll and the live prompt gets "222...".
|
|
103
185
|
let updateDialogHandled = false;
|
|
104
186
|
let trustDialogHandled = false;
|
|
187
|
+
let hooksReviewHandled = false;
|
|
105
188
|
for (let i = 0; i < CODEX_INIT_MAX_ATTEMPTS; i++) {
|
|
106
189
|
try {
|
|
107
190
|
const rawOutput = await (0, tmux_1.capturePane)(sessionName, 50);
|
|
@@ -114,6 +197,17 @@ class CodexTool extends base_1.BaseCLITool {
|
|
|
114
197
|
logger.info('codex-prompt-detected');
|
|
115
198
|
return;
|
|
116
199
|
}
|
|
200
|
+
// Issue #1760: "Hooks need review". Reached only when no genuine prompt
|
|
201
|
+
// exists (the check above returned), so this cannot fire on a live
|
|
202
|
+
// prompt, and the one-shot guard stops a second key on a re-render.
|
|
203
|
+
// Declines: granting trust would write the operator's config.toml.
|
|
204
|
+
if (!hooksReviewHandled && isCodexHooksReviewDialog(output)) {
|
|
205
|
+
await (0, tmux_1.sendKeys)(sessionName, CODEX_HOOKS_REVIEW_DECLINE_KEY, false);
|
|
206
|
+
hooksReviewHandled = true;
|
|
207
|
+
logger.info('codex-hooks-review-declined');
|
|
208
|
+
await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.CODEX_DIALOG_SETTLE_MS));
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
117
211
|
// Issue #892: classify the bottom-most ACTIVE dialog by position. A dialog
|
|
118
212
|
// whose text is only residual scrollback above a genuine prompt returns
|
|
119
213
|
// null here, so no stray key is sent after it has been dismissed.
|
|
@@ -17,6 +17,9 @@ const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
|
|
|
17
17
|
const cli_patterns_1 = require("../detection/cli-patterns");
|
|
18
18
|
const copilot_constants_1 = require("../../config/copilot-constants");
|
|
19
19
|
const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
|
|
20
|
+
const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
|
|
21
|
+
const hook_settings_1 = require("../../lib/hooks/sources/copilot/hook-settings");
|
|
22
|
+
const tool_id_1 = require("../../lib/hooks/sources/copilot/tool-id");
|
|
20
23
|
const errors_1 = require("../../lib/errors");
|
|
21
24
|
const logger_1 = require("../../lib/logger");
|
|
22
25
|
const logger = (0, logger_1.createLogger)('cli-tools/copilot');
|
|
@@ -112,6 +115,18 @@ class CopilotTool extends base_1.BaseCLITool {
|
|
|
112
115
|
logger.info('copilot-session-exists');
|
|
113
116
|
return;
|
|
114
117
|
}
|
|
118
|
+
// Issue #1761: fence this session off from the previous copilot process's
|
|
119
|
+
// events. The state is keyed by (worktree, tool, instance), a key the new
|
|
120
|
+
// session reuses verbatim, so without this the old process's last
|
|
121
|
+
// `user_prompt_submit` reads as the new one's and a session publishes
|
|
122
|
+
// `running` before anybody has typed into it (#1723).
|
|
123
|
+
//
|
|
124
|
+
// Creation path only — the reuse branch above has already returned — and
|
|
125
|
+
// before the pane exists, so no live pane is ever judged against a stale
|
|
126
|
+
// generation. Outside the try, so a launch that then fails is still fenced:
|
|
127
|
+
// falling back to the scraper is always safe, trusting a dead session's
|
|
128
|
+
// events is not.
|
|
129
|
+
(0, agent_session_lifecycle_1.beginAgentSession)({ worktreeId, cliToolId: tool_id_1.COPILOT_CLI_TOOL_ID, instanceId });
|
|
115
130
|
try {
|
|
116
131
|
// Create tmux session. Scrollback depth comes from the shared
|
|
117
132
|
// TMUX_HISTORY_LIMIT default (Issue #1624) — do not re-hardcode it here.
|
|
@@ -121,8 +136,22 @@ class CopilotTool extends base_1.BaseCLITool {
|
|
|
121
136
|
});
|
|
122
137
|
// Wait a moment for the session to be created
|
|
123
138
|
await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
|
|
139
|
+
// Issue #1761: hand this session its hook configuration, so structured
|
|
140
|
+
// lifecycle events and Auto-Yes adjudication exist without the operator
|
|
141
|
+
// having edited ~/.copilot/settings.json by hand.
|
|
142
|
+
//
|
|
143
|
+
// The returned command is `gh copilot` with two environment assignments
|
|
144
|
+
// in front of it. Copilot's config is one file for the whole machine, so
|
|
145
|
+
// unlike Claude's `--settings` it cannot carry the correlation keys —
|
|
146
|
+
// they ride in the environment and the hook reads them when it fires.
|
|
147
|
+
// See `lib/hooks/sources/copilot/hook-settings`.
|
|
148
|
+
//
|
|
149
|
+
// Fails open in every branch: with `CM_AGENT_HOOKS_INJECT=0`, or with a
|
|
150
|
+
// settings file that cannot be read or written, this is byte-for-byte the
|
|
151
|
+
// `gh copilot` this line has always sent.
|
|
152
|
+
const launchCommand = (0, agent_session_lifecycle_1.prepareAgentLaunch)({ worktreeId, cliToolId: tool_id_1.COPILOT_CLI_TOOL_ID, instanceId }, hook_settings_1.COPILOT_LAUNCH_COMMAND).command;
|
|
124
153
|
// Start Copilot CLI in interactive mode
|
|
125
|
-
await (0, tmux_1.sendKeys)(sessionName,
|
|
154
|
+
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
126
155
|
// Wait for Copilot to initialize
|
|
127
156
|
await new Promise((resolve) => setTimeout(resolve, COPILOT_INIT_WAIT_MS));
|
|
128
157
|
// Poll until Copilot interactive prompt is ready
|
|
@@ -15,6 +15,9 @@ const tmux_1 = require("../tmux/tmux");
|
|
|
15
15
|
const submit_verified_sender_1 = require("./submit-verified-sender");
|
|
16
16
|
const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
|
|
17
17
|
const cli_patterns_1 = require("../detection/cli-patterns");
|
|
18
|
+
const sources_1 = require("../../lib/hooks/sources");
|
|
19
|
+
const source_1 = require("../../lib/hooks/sources/gemini/source");
|
|
20
|
+
const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
|
|
18
21
|
const logger_1 = require("../../lib/logger");
|
|
19
22
|
const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
|
|
20
23
|
const logger = (0, logger_1.createLogger)('cli-tools/gemini');
|
|
@@ -73,6 +76,13 @@ class GeminiTool extends base_1.BaseCLITool {
|
|
|
73
76
|
logger.info('gemini-session-sessionname');
|
|
74
77
|
return;
|
|
75
78
|
}
|
|
79
|
+
// Issue #1762: fence this instance's structured events off from the process
|
|
80
|
+
// that used to hold the same (worktree, tool, instance) key. On the creation
|
|
81
|
+
// path only — the reuse branch above has already returned — and before the
|
|
82
|
+
// pane exists, so no live pane is ever judged against a stale generation.
|
|
83
|
+
// Bumped even if the launch below then fails: falling back to the screen
|
|
84
|
+
// scraper is always safe, trusting a dead session's events is not.
|
|
85
|
+
(0, agent_session_lifecycle_1.beginAgentSession)({ worktreeId, cliToolId: sources_1.GEMINI_CLI_TOOL_ID, instanceId });
|
|
76
86
|
try {
|
|
77
87
|
// Create tmux session. Scrollback depth comes from the shared
|
|
78
88
|
// TMUX_HISTORY_LIMIT default (Issue #1624) — do not re-hardcode it here.
|
|
@@ -82,8 +92,24 @@ class GeminiTool extends base_1.BaseCLITool {
|
|
|
82
92
|
});
|
|
83
93
|
// Wait a moment for the session to be created
|
|
84
94
|
await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
|
|
85
|
-
//
|
|
86
|
-
|
|
95
|
+
// Issue #1762: hand this worktree its own hooks config, merged into any
|
|
96
|
+
// `.gemini/settings.json` that is already there. Written from here rather
|
|
97
|
+
// than from the source's `prepareLaunch` because the worktree path is the
|
|
98
|
+
// one input `AgentInstanceRef` does not carry, and gemini is the only tool
|
|
99
|
+
// whose config is scoped to a worktree. Fail-open: a config that cannot be
|
|
100
|
+
// written costs the events and nothing else.
|
|
101
|
+
(0, source_1.injectGeminiHookSettings)(worktreePath, {
|
|
102
|
+
worktreeId,
|
|
103
|
+
cliToolId: sources_1.GEMINI_CLI_TOOL_ID,
|
|
104
|
+
instanceId,
|
|
105
|
+
});
|
|
106
|
+
// Start Gemini CLI in interactive mode (no flags = interactive REPL).
|
|
107
|
+
// `prepareAgentLaunch` prefixes `CM_HOOK_URL`, which is what tells the
|
|
108
|
+
// receiver which instance an event came from — `.gemini/settings.json` is
|
|
109
|
+
// per worktree and cannot. `CM_AGENT_HOOKS_INJECT=0` returns the bare
|
|
110
|
+
// command, unchanged from before this Issue.
|
|
111
|
+
const launchCommand = (0, agent_session_lifecycle_1.prepareAgentLaunch)({ worktreeId, cliToolId: sources_1.GEMINI_CLI_TOOL_ID, instanceId }, this.command).command;
|
|
112
|
+
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
87
113
|
// Wait for Gemini to initialize (minimum wait for banner/auth)
|
|
88
114
|
await new Promise((resolve) => setTimeout(resolve, GEMINI_INIT_WAIT_MS));
|
|
89
115
|
// Poll until Gemini interactive prompt is ready
|
|
@@ -8,6 +8,22 @@
|
|
|
8
8
|
* - sendMessage: sends text via tmux send-keys + Enter
|
|
9
9
|
* - killSession: sends `/exit` command then falls back to tmux kill-session
|
|
10
10
|
* - interrupt(): inherits BaseCLITool default (Escape key) [D2-008]
|
|
11
|
+
*
|
|
12
|
+
* ## Structured events (Issue #1763, Epic #1720 Phase 4-5)
|
|
13
|
+
*
|
|
14
|
+
* opencode is the one supported agent that does not push lifecycle events at
|
|
15
|
+
* CommandMate: it is *subscribed to*. #1758 §5.1.2 measured that the plain TUI
|
|
16
|
+
* serves the same HTTP API `opencode serve` does once it is given `--port`, so
|
|
17
|
+
* the whole of the launch change is that flag — no second process, no pid to
|
|
18
|
+
* track, no orphan to reap, and `killSession`'s `/exit`, the init wait and
|
|
19
|
+
* `reconcileExistingSession` are all untouched.
|
|
20
|
+
*
|
|
21
|
+
* What is added around it is three calls into
|
|
22
|
+
* `@/lib/hooks/sources/opencode/runtime`: reserve a port before the launch
|
|
23
|
+
* command is built, attach the event stream once the TUI is up, and release
|
|
24
|
+
* both when the pane is killed. Every one of them is fail-open — a session that
|
|
25
|
+
* starts without structured events is the pre-#1763 status quo, and the screen
|
|
26
|
+
* scraper keeps deciding for it exactly as before.
|
|
11
27
|
*/
|
|
12
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
29
|
exports.OpenCodeTool = exports.OPENCODE_INIT_WAIT_MS = exports.OPENCODE_PANE_HEIGHT = exports.OPENCODE_EXIT_COMMAND = void 0;
|
|
@@ -19,6 +35,8 @@ const opencode_config_1 = require("./opencode-config");
|
|
|
19
35
|
const child_process_1 = require("child_process");
|
|
20
36
|
const util_1 = require("util");
|
|
21
37
|
const logger_1 = require("../../lib/logger");
|
|
38
|
+
const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
|
|
39
|
+
const runtime_1 = require("../../lib/hooks/sources/opencode/runtime");
|
|
22
40
|
const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
|
|
23
41
|
const logger = (0, logger_1.createLogger)('cli-tools/opencode');
|
|
24
42
|
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
@@ -77,15 +95,29 @@ class OpenCodeTool extends base_1.BaseCLITool {
|
|
|
77
95
|
throw new Error('OpenCode is not installed or not in PATH');
|
|
78
96
|
}
|
|
79
97
|
const sessionName = this.getSessionName(worktreeId, instanceId);
|
|
98
|
+
const target = (0, runtime_1.opencodeTarget)(worktreeId, instanceId);
|
|
80
99
|
const exists = await (0, tmux_1.hasSession)(sessionName);
|
|
81
100
|
if (exists) {
|
|
82
101
|
await this.reconcileExistingSession(sessionName, {
|
|
83
102
|
windowWidth: 80,
|
|
84
103
|
windowHeight: exports.OPENCODE_PANE_HEIGHT,
|
|
85
104
|
});
|
|
105
|
+
// Issue #1763: the pane outlived this process (a CommandMate restart), so
|
|
106
|
+
// the subscription that used to watch it is gone while its server is
|
|
107
|
+
// still listening. Recovered from the recorded port, health-checked, and
|
|
108
|
+
// skipped silently when there is nothing there. NOT a new generation —
|
|
109
|
+
// the pane is the same one, and fencing here would discard a still-valid
|
|
110
|
+
// verdict on every reconnect.
|
|
111
|
+
await (0, runtime_1.resumeOpencodeEventStream)(target, worktreePath);
|
|
86
112
|
logger.info('opencode-session-sessionname');
|
|
87
113
|
return;
|
|
88
114
|
}
|
|
115
|
+
// Issue #1759 (S8) / #1763: everything the previous opencode process
|
|
116
|
+
// reported through this (worktreeId, instanceId) belongs to a session that
|
|
117
|
+
// no longer exists, and the key is reused verbatim by the one about to be
|
|
118
|
+
// created. On the creation path only, before the pane exists, and even if
|
|
119
|
+
// the launch then fails — falling back to the scraper is always safe.
|
|
120
|
+
(0, agent_session_lifecycle_1.beginAgentSession)(target);
|
|
89
121
|
try {
|
|
90
122
|
// Generate opencode.json if not present (non-fatal on failure)
|
|
91
123
|
await (0, opencode_config_1.ensureOpencodeConfig)(worktreePath);
|
|
@@ -109,10 +141,23 @@ class OpenCodeTool extends base_1.BaseCLITool {
|
|
|
109
141
|
catch {
|
|
110
142
|
// Non-fatal: resize may fail in some environments
|
|
111
143
|
}
|
|
112
|
-
//
|
|
113
|
-
|
|
144
|
+
// Issue #1763: pick the port before the command is built. The TUI is its
|
|
145
|
+
// own HTTP server once `--port` is passed (#1758 §5.1.2), and CommandMate
|
|
146
|
+
// assigns the number because opencode's `--port 0` is not an OS-assigned
|
|
147
|
+
// port — it tries 4096 first and only then falls back to an ephemeral one
|
|
148
|
+
// that nothing can read back (§5.9). Null means "no structured events",
|
|
149
|
+
// and the launch below is then the pre-#1763 bare command.
|
|
150
|
+
await (0, runtime_1.reserveOpencodeServerPort)(target, worktreePath);
|
|
151
|
+
// Start OpenCode TUI. `prepareAgentLaunch` asks the tool's own
|
|
152
|
+
// `AgentEventSource` for the command line (S3/S4/S5) and never throws.
|
|
153
|
+
const launchCommand = (0, agent_session_lifecycle_1.prepareAgentLaunch)(target, this.command).command;
|
|
154
|
+
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
114
155
|
// Wait for OpenCode to initialize (GPU model loading via Ollama)
|
|
115
156
|
await new Promise((resolve) => setTimeout(resolve, exports.OPENCODE_INIT_WAIT_MS));
|
|
157
|
+
// Issue #1763: subscribe once the server has had the same time to come up
|
|
158
|
+
// that the TUI has. Health-checked first, so an opencode too old to know
|
|
159
|
+
// `--port` costs one probe and nothing else.
|
|
160
|
+
await (0, runtime_1.attachOpencodeEventStream)(target);
|
|
116
161
|
logger.info('started-opencode-session:sessionname');
|
|
117
162
|
}
|
|
118
163
|
catch (error) {
|
|
@@ -164,6 +209,10 @@ class OpenCodeTool extends base_1.BaseCLITool {
|
|
|
164
209
|
*/
|
|
165
210
|
async killSession(worktreeId, instanceId) {
|
|
166
211
|
const sessionName = this.getSessionName(worktreeId, instanceId);
|
|
212
|
+
// Issue #1763: stop watching before the pane goes, so the stream is not
|
|
213
|
+
// reconnecting to a server that is being shut down. Also gives the port
|
|
214
|
+
// back — the pane is what held it. Never throws.
|
|
215
|
+
await (0, runtime_1.releaseOpencodeEventStream)((0, runtime_1.opencodeTarget)(worktreeId, instanceId));
|
|
167
216
|
try {
|
|
168
217
|
// Step 1: Check if the tmux session currently exists
|
|
169
218
|
const exists = await (0, tmux_1.hasSession)(sessionName);
|