commandmate 0.22.1 → 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 +86 -86
- package/.next/app-path-routes-manifest.json +17 -17
- 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/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 +17 -17
- 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-63dacfbab082ca8d.js → 6488-95ba9d24a76f0cc5.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/{page-c1be93f2595d0803.js → page-966a451ae7b0903a.js} +1 -1
- package/.next/trace +12 -12
- package/dist/cli/docs/agent-operations.d.ts +1 -1
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +5 -1
- package/dist/cli/utils/api-client.d.ts.map +1 -1
- package/dist/cli/utils/api-client.js +9 -2
- package/dist/cli/utils/server-url.d.ts +23 -0
- package/dist/cli/utils/server-url.d.ts.map +1 -1
- package/dist/cli/utils/server-url.js +33 -0
- 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/{d4vh4O18WEhXs4WbdShIs → wH6K6QNPHSdATFkVoIcy3}/_buildManifest.js +0 -0
- /package/.next/static/{d4vh4O18WEhXs4WbdShIs → wH6K6QNPHSdATFkVoIcy3}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What a normalised opencode event does to CommandMate's state (Issue #1763).
|
|
4
|
+
*
|
|
5
|
+
* The consuming half of the pipeline was already tool-agnostic before this
|
|
6
|
+
* Issue: `agent-event-state`, `permission-decision-service`,
|
|
7
|
+
* `current-output-builder`, `status-mapping` and `prompt-waiting-composition`
|
|
8
|
+
* all key off (worktree, tool, instance) and branch on nothing else. So this
|
|
9
|
+
* module is deliberately thin — it is the same sequence
|
|
10
|
+
* `POST /api/hooks/agent-event` and `POST /api/hooks/permission-request`
|
|
11
|
+
* perform, called in-process because there is no request to perform it on.
|
|
12
|
+
*
|
|
13
|
+
* The one place the two paths differ is *where the verdict goes*. A hook is
|
|
14
|
+
* answered in the body of the request it arrived on; an opencode approval is
|
|
15
|
+
* answered by a POST to the agent's own server, minutes later if need be. Both
|
|
16
|
+
* are spelled `answerPendingDecision(source, …)`, which is the seam #1759 built
|
|
17
|
+
* for exactly this.
|
|
18
|
+
*
|
|
19
|
+
* ## Why the database imports are dynamic
|
|
20
|
+
*
|
|
21
|
+
* `../registry` statically imports `./source`, so anything `./source`'s module
|
|
22
|
+
* graph reaches becomes part of every import of `@/lib/hooks/sources` —
|
|
23
|
+
* including `better-sqlite3` if this module were pulled in eagerly. The state
|
|
24
|
+
* effects are needed only when an event actually arrives, so they are imported
|
|
25
|
+
* then. (`./source` does not import this module at all; the runtime wires the
|
|
26
|
+
* two together.)
|
|
27
|
+
*
|
|
28
|
+
* Nothing here throws. An event is a fact about a session that is still
|
|
29
|
+
* running, and a failure to record it must cost the record, never the session.
|
|
30
|
+
*
|
|
31
|
+
* @module lib/hooks/sources/opencode/ingest
|
|
32
|
+
*/
|
|
33
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
34
|
+
if (k2 === undefined) k2 = k;
|
|
35
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
36
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
37
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
38
|
+
}
|
|
39
|
+
Object.defineProperty(o, k2, desc);
|
|
40
|
+
}) : (function(o, m, k, k2) {
|
|
41
|
+
if (k2 === undefined) k2 = k;
|
|
42
|
+
o[k2] = m[k];
|
|
43
|
+
}));
|
|
44
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
45
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
46
|
+
}) : function(o, v) {
|
|
47
|
+
o["default"] = v;
|
|
48
|
+
});
|
|
49
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
50
|
+
var ownKeys = function(o) {
|
|
51
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
52
|
+
var ar = [];
|
|
53
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
54
|
+
return ar;
|
|
55
|
+
};
|
|
56
|
+
return ownKeys(o);
|
|
57
|
+
};
|
|
58
|
+
return function (mod) {
|
|
59
|
+
if (mod && mod.__esModule) return mod;
|
|
60
|
+
var result = {};
|
|
61
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
62
|
+
__setModuleDefault(result, mod);
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
})();
|
|
66
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
67
|
+
exports.ingestOpencodeEvent = ingestOpencodeEvent;
|
|
68
|
+
const logger_1 = require("../../../../lib/logger");
|
|
69
|
+
const agent_event_types_1 = require("../../../../lib/hooks/agent-event-types");
|
|
70
|
+
const agent_event_state_1 = require("../../../../lib/session/agent-event-state");
|
|
71
|
+
const structured_prompt_1 = require("../../../../lib/session/structured-prompt");
|
|
72
|
+
const abstain_1 = require("../abstain");
|
|
73
|
+
const event_mapper_1 = require("../event-mapper");
|
|
74
|
+
const pending_decisions_1 = require("../pending-decisions");
|
|
75
|
+
const registry_1 = require("../registry");
|
|
76
|
+
const mappers_1 = require("./mappers");
|
|
77
|
+
const payloads_1 = require("./payloads");
|
|
78
|
+
const tool_id_1 = require("./tool-id");
|
|
79
|
+
const logger = (0, logger_1.createLogger)('lib/hooks/sources/opencode/ingest');
|
|
80
|
+
/** The one-line human-facing summary for a notification, or null. */
|
|
81
|
+
function describeNotification(event) {
|
|
82
|
+
const properties = (0, event_mapper_1.isPlainObject)(event.raw.properties) ? event.raw.properties : {};
|
|
83
|
+
if (event.detail === mappers_1.OPENCODE_PERMISSION_DETAIL) {
|
|
84
|
+
// `metadata.command` exists for `bash` approvals and is what the human is
|
|
85
|
+
// being asked about; the approval kind is the fallback for the rest.
|
|
86
|
+
return ((0, event_mapper_1.readNestedString)(properties, ['metadata', 'command']) ??
|
|
87
|
+
(0, event_mapper_1.readStringField)(properties, 'permission'));
|
|
88
|
+
}
|
|
89
|
+
if (event.detail === mappers_1.OPENCODE_QUESTION_DETAIL) {
|
|
90
|
+
const questions = properties.questions;
|
|
91
|
+
if (Array.isArray(questions) && (0, event_mapper_1.isPlainObject)(questions[0])) {
|
|
92
|
+
return (0, event_mapper_1.readStringField)(questions[0], 'question');
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (event.detail === mappers_1.OPENCODE_ERROR_DETAIL) {
|
|
97
|
+
return ((0, event_mapper_1.readNestedString)(properties, ['error', 'data', 'message']) ??
|
|
98
|
+
(0, event_mapper_1.readNestedString)(properties, ['error', 'name']));
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Adjudicate one approval and answer it over REST (Auto-Yes v2).
|
|
104
|
+
*
|
|
105
|
+
* The adjudication itself is `resolvePermissionRequest`, unchanged and shared
|
|
106
|
+
* with every hook-based tool. What is opencode-specific is what silence means:
|
|
107
|
+
* on Claude an abstention costs a dialog, here it costs the session, because
|
|
108
|
+
* the agent waits with no timeout at all (#1758 §5.5.3). `describeAbstain` is
|
|
109
|
+
* how that gets said out loud — nothing else will say it, since a blocked
|
|
110
|
+
* opencode session looks exactly like one that is thinking.
|
|
111
|
+
*/
|
|
112
|
+
async function adjudicatePermission(target, event, instanceId) {
|
|
113
|
+
const source = (0, registry_1.getAgentEventSource)(tool_id_1.OPENCODE_CLI_TOOL_ID);
|
|
114
|
+
const pending = (0, payloads_1.toOpencodePendingPermission)(event.raw, event.receivedAt);
|
|
115
|
+
if (!pending) {
|
|
116
|
+
logger.info('opencode-permission-unparsed', {
|
|
117
|
+
worktreeId: target.worktreeId,
|
|
118
|
+
instanceId,
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const { resolvePermissionRequest } = await Promise.resolve().then(() => __importStar(require('../../../../lib/hooks/permission-decision-service')));
|
|
123
|
+
const payload = source.parsePermissionRequest(event.raw);
|
|
124
|
+
const decision = resolvePermissionRequest({ worktreeId: target.worktreeId, cliToolId: tool_id_1.OPENCODE_CLI_TOOL_ID, instanceId }, payload);
|
|
125
|
+
const verdict = decision.behavior === 'allow' ? { kind: 'allowOnce' } : { kind: 'abstain' };
|
|
126
|
+
if (verdict.kind === 'abstain') {
|
|
127
|
+
const abstain = (0, abstain_1.describeAbstain)(source);
|
|
128
|
+
if (!abstain.safe) {
|
|
129
|
+
logger.warn('permission-request-abstain-blocks-agent', {
|
|
130
|
+
worktreeId: target.worktreeId,
|
|
131
|
+
cliToolId: tool_id_1.OPENCODE_CLI_TOOL_ID,
|
|
132
|
+
instanceId,
|
|
133
|
+
toolName: payload?.toolName ?? null,
|
|
134
|
+
decisionId: pending.id,
|
|
135
|
+
reason: decision.reason,
|
|
136
|
+
consequence: abstain.summary,
|
|
137
|
+
blocksForMs: abstain.blocksForMs,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// C2: for this source the verdict leaves over its own connection, and the
|
|
142
|
+
// `{}` that comes back is an acknowledgement rather than a reply body.
|
|
143
|
+
await (0, pending_decisions_1.answerPendingDecision)(source, { worktreeId: target.worktreeId, cliToolId: tool_id_1.OPENCODE_CLI_TOOL_ID, instanceId }, pending, verdict);
|
|
144
|
+
logger.info('opencode-permission-decided', {
|
|
145
|
+
worktreeId: target.worktreeId,
|
|
146
|
+
instanceId,
|
|
147
|
+
decisionId: pending.id,
|
|
148
|
+
toolName: payload?.toolName ?? null,
|
|
149
|
+
behavior: decision.behavior,
|
|
150
|
+
reason: decision.reason,
|
|
151
|
+
suppressedBy: decision.suppressedBy,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Record a question so `capture --prompts` and the picker can show its options.
|
|
156
|
+
*
|
|
157
|
+
* The structured content Claude never had: its `AskUserQuestion` picker has to
|
|
158
|
+
* be read off the screen (#1708), while opencode publishes the questions and
|
|
159
|
+
* their choices as data (#1758 §5.2.4).
|
|
160
|
+
*
|
|
161
|
+
* `reportPermissionRequestPending` is called alongside because that is the only
|
|
162
|
+
* exported way to tell the detection layer a human is blocked, and a question
|
|
163
|
+
* blocks exactly as an approval does — the session reads `busy` and no
|
|
164
|
+
* `session.idle` arrives until it is answered (§5.3.1). It is recorded as
|
|
165
|
+
* provisional, so it expires unless the scraper corroborates it, which is the
|
|
166
|
+
* same treatment Claude's pre-dialog prediction gets.
|
|
167
|
+
*/
|
|
168
|
+
function recordQuestion(target, event, instanceId) {
|
|
169
|
+
const source = (0, registry_1.getAgentEventSource)(tool_id_1.OPENCODE_CLI_TOOL_ID);
|
|
170
|
+
const spec = source.parseQuestion(event.raw);
|
|
171
|
+
if (!spec)
|
|
172
|
+
return;
|
|
173
|
+
(0, agent_event_state_1.recordAskUserQuestion)(target.worktreeId, tool_id_1.OPENCODE_CLI_TOOL_ID, instanceId, spec, event.receivedAt);
|
|
174
|
+
(0, agent_event_state_1.reportPermissionRequestPending)(target.worktreeId, tool_id_1.OPENCODE_CLI_TOOL_ID, instanceId, 'question', event.receivedAt);
|
|
175
|
+
logger.info('opencode-question-recorded', {
|
|
176
|
+
worktreeId: target.worktreeId,
|
|
177
|
+
instanceId,
|
|
178
|
+
questionCount: spec.questions.length,
|
|
179
|
+
optionCounts: spec.questions.map((question) => question.choices.length),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/** Apply a completed turn to whatever the worktree is doing. */
|
|
183
|
+
async function applyStop(target, instanceId) {
|
|
184
|
+
const [{ getDbInstance }, { getWorktreeById }, { applyAgentStopEvent }] = await Promise.all([
|
|
185
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/db/db-instance'))),
|
|
186
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/db'))),
|
|
187
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/hooks/agent-event-service'))),
|
|
188
|
+
]);
|
|
189
|
+
const db = getDbInstance();
|
|
190
|
+
const worktree = getWorktreeById(db, target.worktreeId) ?? null;
|
|
191
|
+
if (!worktree) {
|
|
192
|
+
// The worktree was removed while its pane was still running. Ordinary, not
|
|
193
|
+
// an error — the same outcome the hook receiver reports as 202-and-drop.
|
|
194
|
+
logger.info('opencode-stop-unresolved-target', { worktreeId: target.worktreeId });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const outcome = await applyAgentStopEvent(db, worktree, tool_id_1.OPENCODE_CLI_TOOL_ID, instanceId);
|
|
198
|
+
logger.info('opencode-stop-applied', {
|
|
199
|
+
worktreeId: target.worktreeId,
|
|
200
|
+
instanceId,
|
|
201
|
+
taskId: outcome.taskId,
|
|
202
|
+
taskEventApplied: outcome.taskEventApplied,
|
|
203
|
+
verificationRunId: outcome.verificationRunId,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Apply one event from the stream.
|
|
208
|
+
*
|
|
209
|
+
* @param target - The instance the subscription belongs to
|
|
210
|
+
* @param event - Already normalised and already through the turn gate
|
|
211
|
+
*/
|
|
212
|
+
async function ingestOpencodeEvent(target, event) {
|
|
213
|
+
const instanceId = target.instanceId ?? tool_id_1.OPENCODE_CLI_TOOL_ID;
|
|
214
|
+
try {
|
|
215
|
+
// The same guard the hook receiver applies. It cannot catch the abort
|
|
216
|
+
// double-idle on its own — see `./turn-gate`, which is what does — but it
|
|
217
|
+
// does cover a frame delivered twice by a re-sync racing the live stream.
|
|
218
|
+
if ((0, agent_event_state_1.isDuplicateAgentEvent)(target.worktreeId, tool_id_1.OPENCODE_CLI_TOOL_ID, instanceId, event.event, event.conversationId, event.receivedAt, event.detail)) {
|
|
219
|
+
logger.info('opencode-event-duplicate-dropped', {
|
|
220
|
+
worktreeId: target.worktreeId,
|
|
221
|
+
instanceId,
|
|
222
|
+
event: event.event,
|
|
223
|
+
detail: event.detail,
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
(0, agent_event_state_1.recordAgentEvent)(target.worktreeId, tool_id_1.OPENCODE_CLI_TOOL_ID, instanceId, {
|
|
228
|
+
event: event.event,
|
|
229
|
+
at: event.receivedAt,
|
|
230
|
+
detail: event.detail?.slice(0, agent_event_types_1.MAX_EVENT_DETAIL_LENGTH) ?? null,
|
|
231
|
+
sessionId: event.conversationId,
|
|
232
|
+
message: describeNotification(event)?.slice(0, structured_prompt_1.MAX_STRUCTURED_PROMPT_MESSAGE_LENGTH) ?? null,
|
|
233
|
+
});
|
|
234
|
+
if (event.event === 'notification' && event.detail === mappers_1.OPENCODE_PERMISSION_DETAIL) {
|
|
235
|
+
await adjudicatePermission(target, event, instanceId);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (event.event === 'notification' && event.detail === mappers_1.OPENCODE_QUESTION_DETAIL) {
|
|
239
|
+
recordQuestion(target, event, instanceId);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (event.event === 'stop') {
|
|
243
|
+
await applyStop(target, instanceId);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
logger.info('opencode-event-received', {
|
|
247
|
+
worktreeId: target.worktreeId,
|
|
248
|
+
instanceId,
|
|
249
|
+
event: event.event,
|
|
250
|
+
detail: event.detail,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
logger.error('opencode-event-ingest-failed', {
|
|
255
|
+
worktreeId: target.worktreeId,
|
|
256
|
+
instanceId,
|
|
257
|
+
event: event.event,
|
|
258
|
+
error: error instanceof Error ? error.message : String(error),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* opencode's SSE frames, mapped onto the seven words (Issue #1763, constraint
|
|
4
|
+
* C4).
|
|
5
|
+
*
|
|
6
|
+
* Every rule below is lifted from a frame that was actually captured from
|
|
7
|
+
* `opencode` 1.18.3 — the files in `tests/fixtures/hooks/opencode/` — and not
|
|
8
|
+
* from the server's OpenAPI document. The document is wrong in at least one
|
|
9
|
+
* place that matters (`server.heartbeat` is not in its `Event` union yet
|
|
10
|
+
* arrives every ten seconds, #1758 D5), so the fixtures are the specification
|
|
11
|
+
* and the tests read them directly.
|
|
12
|
+
*
|
|
13
|
+
* Three of the rules cannot be written as a name table, which is the whole
|
|
14
|
+
* reason {@link EventMapper} is a function (#1758 §5.2.3):
|
|
15
|
+
*
|
|
16
|
+
* - `message.part.updated` is `pre_tool_use` or `post_tool_use` depending on
|
|
17
|
+
* `part.state.status` — one event name, two words.
|
|
18
|
+
* - `user_prompt_submit` has no event of its own; it is `message.updated` with
|
|
19
|
+
* `info.role === "user"`.
|
|
20
|
+
* - `notification` is three unrelated events told apart only by `detail`.
|
|
21
|
+
*
|
|
22
|
+
* ## The envelope
|
|
23
|
+
*
|
|
24
|
+
* ```json
|
|
25
|
+
* { "id": "evt_…", "type": "session.idle", "properties": { "sessionID": "ses_…" } }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* The event's name is in `type` and everything else is nested under
|
|
29
|
+
* `properties`, so `conversationIdFields` — which reads flat keys — cannot
|
|
30
|
+
* reach `sessionID`. {@link withConversationId} fills it in from the nested
|
|
31
|
+
* path instead, which is the C5 escape hatch the interface documents.
|
|
32
|
+
*
|
|
33
|
+
* @module lib/hooks/sources/opencode/mappers
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.OPENCODE_MAPPERS = exports.OPENCODE_ERROR_DETAIL = exports.OPENCODE_QUESTION_DETAIL = exports.OPENCODE_PERMISSION_DETAIL = void 0;
|
|
37
|
+
exports.frameProperties = frameProperties;
|
|
38
|
+
exports.frameSessionId = frameSessionId;
|
|
39
|
+
exports.partToolName = partToolName;
|
|
40
|
+
exports.partCallId = partCallId;
|
|
41
|
+
exports.withConversationId = withConversationId;
|
|
42
|
+
const event_mapper_1 = require("../event-mapper");
|
|
43
|
+
/**
|
|
44
|
+
* Detail for `permission.asked`.
|
|
45
|
+
*
|
|
46
|
+
* Deliberately Claude's spelling. `wait --on-prompt` has to mean the same thing
|
|
47
|
+
* on both tools, and `agent-event-state` opens its prompt-waiting record on
|
|
48
|
+
* exactly this string — renaming it for opencode would leave the state machine
|
|
49
|
+
* silently unable to see an opencode approval dialog (#1758 §9.3).
|
|
50
|
+
*/
|
|
51
|
+
exports.OPENCODE_PERMISSION_DETAIL = 'permission_prompt';
|
|
52
|
+
/**
|
|
53
|
+
* Detail for `question.asked`.
|
|
54
|
+
*
|
|
55
|
+
* A new value rather than a reuse: opencode's `question` tool is answered with
|
|
56
|
+
* structured choices over its own endpoint, and nothing above this layer should
|
|
57
|
+
* be able to confuse it with an approval it could allow. There is no
|
|
58
|
+
* `idle_prompt` equivalent on opencode at all — no event reports "the agent is
|
|
59
|
+
* sitting at the composer" — so that spelling never appears here.
|
|
60
|
+
*/
|
|
61
|
+
exports.OPENCODE_QUESTION_DETAIL = 'question_prompt';
|
|
62
|
+
/** Detail for `session.error`. */
|
|
63
|
+
exports.OPENCODE_ERROR_DETAIL = 'error';
|
|
64
|
+
/** The `properties` object of a frame, or an empty object. */
|
|
65
|
+
function frameProperties(payload) {
|
|
66
|
+
return (0, event_mapper_1.isPlainObject)(payload.properties) ? payload.properties : {};
|
|
67
|
+
}
|
|
68
|
+
/** `properties.sessionID`, the id every frame that belongs to a turn carries. */
|
|
69
|
+
function frameSessionId(payload) {
|
|
70
|
+
return (0, event_mapper_1.readNestedString)(frameProperties(payload), ['sessionID']);
|
|
71
|
+
}
|
|
72
|
+
/** `properties.part.state.status` — `pending` / `running` / `completed` / `error`. */
|
|
73
|
+
function partStatus(payload) {
|
|
74
|
+
return (0, event_mapper_1.readNestedString)(frameProperties(payload), ['part', 'state', 'status']);
|
|
75
|
+
}
|
|
76
|
+
/** Whether this `message.part.updated` describes a tool call rather than text. */
|
|
77
|
+
function partIsToolCall(payload) {
|
|
78
|
+
return (0, event_mapper_1.readNestedString)(frameProperties(payload), ['part', 'type']) === 'tool';
|
|
79
|
+
}
|
|
80
|
+
/** `properties.part.tool` — `bash`, `read`, … (lower case, unlike Claude's). */
|
|
81
|
+
function partToolName(payload) {
|
|
82
|
+
return (0, event_mapper_1.readNestedString)(frameProperties(payload), ['part', 'tool']);
|
|
83
|
+
}
|
|
84
|
+
/** `properties.part.callID` — the correlation key for one tool call. */
|
|
85
|
+
function partCallId(payload) {
|
|
86
|
+
return (0, event_mapper_1.readNestedString)(frameProperties(payload), ['part', 'callID']);
|
|
87
|
+
}
|
|
88
|
+
/** Statuses that mean the tool call is over, either way (#1758 §5.2.3). */
|
|
89
|
+
const FINISHED_PART_STATUSES = ['completed', 'error'];
|
|
90
|
+
/**
|
|
91
|
+
* The ordered rules, first match wins.
|
|
92
|
+
*
|
|
93
|
+
* `session.status`, `server.connected`, `server.heartbeat`, `permission.replied`
|
|
94
|
+
* and `question.replied` are deliberately absent. They are real frames that
|
|
95
|
+
* arrive on every healthy connection and they map to none of the seven words,
|
|
96
|
+
* so they fall through, return null and are counted (C8) — which is what the
|
|
97
|
+
* interface asks for and what stops a ten-second keepalive from throwing.
|
|
98
|
+
*
|
|
99
|
+
* `session.status(idle)` in particular must NOT be mapped: it is emitted in the
|
|
100
|
+
* same millisecond as `session.idle` and mapping both would report every turn's
|
|
101
|
+
* completion twice (#1758 §5.3.2 rule 4).
|
|
102
|
+
*/
|
|
103
|
+
const OPENCODE_BASE_MAPPERS = [
|
|
104
|
+
// 1:1. `session.deleted` only fires for an explicit `DELETE /session/:id` —
|
|
105
|
+
// the TUI's `/exit` emits nothing at all — so it is expressible but is not
|
|
106
|
+
// the signal that the agent is gone. That stays tmux's job (#1758 §5.6.4).
|
|
107
|
+
(0, event_mapper_1.whenNamed)('session.idle', 'stop'),
|
|
108
|
+
(0, event_mapper_1.whenNamed)('session.created', 'session_start'),
|
|
109
|
+
(0, event_mapper_1.whenNamed)('session.deleted', 'session_end'),
|
|
110
|
+
// Composite: no dedicated event, so the role decides.
|
|
111
|
+
(type, payload) => type === 'message.updated' &&
|
|
112
|
+
(0, event_mapper_1.readNestedString)(frameProperties(payload), ['info', 'role']) === 'user'
|
|
113
|
+
? { event: 'user_prompt_submit' }
|
|
114
|
+
: null,
|
|
115
|
+
// Partial match: one event name, two words, chosen by a nested status.
|
|
116
|
+
// `pending` — the frame just before `running` — maps to nothing on purpose;
|
|
117
|
+
// mapping it as well would report every tool call twice.
|
|
118
|
+
(type, payload) => type === 'message.part.updated' && partIsToolCall(payload) && partStatus(payload) === 'running'
|
|
119
|
+
? {
|
|
120
|
+
event: 'pre_tool_use',
|
|
121
|
+
detail: (0, event_mapper_1.boundDetail)(partToolName(payload)),
|
|
122
|
+
toolCallId: partCallId(payload),
|
|
123
|
+
}
|
|
124
|
+
: null,
|
|
125
|
+
(type, payload) => type === 'message.part.updated' &&
|
|
126
|
+
partIsToolCall(payload) &&
|
|
127
|
+
FINISHED_PART_STATUSES.includes(partStatus(payload) ?? '')
|
|
128
|
+
? {
|
|
129
|
+
event: 'post_tool_use',
|
|
130
|
+
detail: (0, event_mapper_1.boundDetail)(partToolName(payload)),
|
|
131
|
+
toolCallId: partCallId(payload),
|
|
132
|
+
}
|
|
133
|
+
: null,
|
|
134
|
+
// Bundle: three unrelated events collapse into `notification`.
|
|
135
|
+
(0, event_mapper_1.whenNamed)('permission.asked', 'notification', exports.OPENCODE_PERMISSION_DETAIL),
|
|
136
|
+
(0, event_mapper_1.whenNamed)('question.asked', 'notification', exports.OPENCODE_QUESTION_DETAIL),
|
|
137
|
+
(0, event_mapper_1.whenNamed)('session.error', 'notification', exports.OPENCODE_ERROR_DETAIL),
|
|
138
|
+
];
|
|
139
|
+
/**
|
|
140
|
+
* Fill `conversationId` from the nested envelope.
|
|
141
|
+
*
|
|
142
|
+
* @param mapper - A rule that may or may not have set the field itself
|
|
143
|
+
* @returns The same rule, with `properties.sessionID` as the fallback
|
|
144
|
+
*/
|
|
145
|
+
function withConversationId(mapper) {
|
|
146
|
+
return (type, payload) => {
|
|
147
|
+
const mapped = mapper(type, payload);
|
|
148
|
+
if (!mapped)
|
|
149
|
+
return null;
|
|
150
|
+
return { ...mapped, conversationId: mapped.conversationId ?? frameSessionId(payload) };
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** The rules an opencode source carries. */
|
|
154
|
+
exports.OPENCODE_MAPPERS = OPENCODE_BASE_MAPPERS.map(withConversationId);
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Reading opencode's approval and question payloads (Issue #1763, seam S7).
|
|
4
|
+
*
|
|
5
|
+
* Both shapes are taken from captured frames — `permission-asked.json` and
|
|
6
|
+
* `question-asked.json` in `tests/fixtures/hooks/opencode/` — and both are
|
|
7
|
+
* parsed into the vocabulary the tool-independent adjudicator already speaks,
|
|
8
|
+
* so `permission-decision-service` and `agent-event-state` need no opencode
|
|
9
|
+
* branch.
|
|
10
|
+
*
|
|
11
|
+
* ## The tool name is not in the approval payload
|
|
12
|
+
*
|
|
13
|
+
* `permission.asked` carries `permission` (the *kind* of approval:
|
|
14
|
+
* `external_directory`, …), `patterns`, `metadata` and `tool.callID` — but no
|
|
15
|
+
* tool name (#1758 §5.4). Auto-Yes matches deny patterns against what is being
|
|
16
|
+
* asked for, so the name has to come from somewhere: the `message.part.updated`
|
|
17
|
+
* frame for the same `callID` carries `part.tool` (`bash`), and it arrives
|
|
18
|
+
* first, so the subscription records the correlation as it goes and this module
|
|
19
|
+
* looks it up. When the correlation is missing the approval kind is used
|
|
20
|
+
* instead — an unknown tool name makes `collectToolInputMatchTexts` fall back
|
|
21
|
+
* to the serialised input, which contains the command, so the matching surface
|
|
22
|
+
* never silently empties.
|
|
23
|
+
*
|
|
24
|
+
* ## What the deny patterns see
|
|
25
|
+
*
|
|
26
|
+
* `metadata` verbatim (it holds `command` for `bash` approvals), plus the
|
|
27
|
+
* approval kind and its patterns. Nothing from the pane and nothing from an
|
|
28
|
+
* earlier request can reach it — the property #1699 was opened about.
|
|
29
|
+
*
|
|
30
|
+
* @module lib/hooks/sources/opencode/payloads
|
|
31
|
+
*/
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.MAX_TRACKED_TOOL_CALLS = void 0;
|
|
34
|
+
exports.rememberOpencodeToolCall = rememberOpencodeToolCall;
|
|
35
|
+
exports.lookupOpencodeToolName = lookupOpencodeToolName;
|
|
36
|
+
exports.resetOpencodeToolCalls = resetOpencodeToolCalls;
|
|
37
|
+
exports.parseOpencodePermissionRequest = parseOpencodePermissionRequest;
|
|
38
|
+
exports.parseOpencodeQuestion = parseOpencodeQuestion;
|
|
39
|
+
exports.toOpencodePendingPermission = toOpencodePendingPermission;
|
|
40
|
+
exports.toOpencodePendingQuestion = toOpencodePendingQuestion;
|
|
41
|
+
const ask_user_question_payload_1 = require("../../../../lib/hooks/ask-user-question-payload");
|
|
42
|
+
const event_mapper_1 = require("../event-mapper");
|
|
43
|
+
/**
|
|
44
|
+
* Cap on remembered `callID -> tool name` pairs.
|
|
45
|
+
*
|
|
46
|
+
* One per tool call, and a long session makes thousands, so the map is bounded
|
|
47
|
+
* and the oldest is dropped. Forgetting costs a less specific tool name on an
|
|
48
|
+
* approval, never a wrong one.
|
|
49
|
+
*/
|
|
50
|
+
exports.MAX_TRACKED_TOOL_CALLS = 256;
|
|
51
|
+
const toolCallNames = (globalThis.__opencodeToolCallNames ??= new Map());
|
|
52
|
+
/** Record that a tool call has a name, seen on a `message.part.updated` frame. */
|
|
53
|
+
function rememberOpencodeToolCall(callId, toolName) {
|
|
54
|
+
toolCallNames.delete(callId);
|
|
55
|
+
toolCallNames.set(callId, toolName);
|
|
56
|
+
while (toolCallNames.size > exports.MAX_TRACKED_TOOL_CALLS) {
|
|
57
|
+
const oldest = toolCallNames.keys().next();
|
|
58
|
+
if (oldest.done)
|
|
59
|
+
break;
|
|
60
|
+
toolCallNames.delete(oldest.value);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** The tool behind a `callID`, or null when it has not been seen. */
|
|
64
|
+
function lookupOpencodeToolName(callId) {
|
|
65
|
+
if (!callId)
|
|
66
|
+
return null;
|
|
67
|
+
return toolCallNames.get(callId) ?? null;
|
|
68
|
+
}
|
|
69
|
+
/** Forget every correlation. Test seam. */
|
|
70
|
+
function resetOpencodeToolCalls() {
|
|
71
|
+
toolCallNames.clear();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The object an approval or question lives in.
|
|
75
|
+
*
|
|
76
|
+
* Two callers hand this module two different shapes and both are legitimate:
|
|
77
|
+
* the SSE frame `{ id, type, properties }`, and the bare object
|
|
78
|
+
* `GET /permission` returns when a missed event is being recovered. Unwrapping
|
|
79
|
+
* here is what lets the reconnect path reuse the live path's parsing.
|
|
80
|
+
*/
|
|
81
|
+
function unwrap(payload) {
|
|
82
|
+
if (typeof payload.type === 'string' && (0, event_mapper_1.isPlainObject)(payload.properties)) {
|
|
83
|
+
return payload.properties;
|
|
84
|
+
}
|
|
85
|
+
return payload;
|
|
86
|
+
}
|
|
87
|
+
/** `tool.callID`, present on both approvals and questions. */
|
|
88
|
+
function toolCallId(properties) {
|
|
89
|
+
return (0, event_mapper_1.readNestedString)(properties, ['tool', 'callID']);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Read a `permission.asked` frame, or a pending entry from `GET /permission`.
|
|
93
|
+
*
|
|
94
|
+
* @param payload - The frame or the bare permission object
|
|
95
|
+
* @returns The payload the adjudicator judges, or null when this is not an
|
|
96
|
+
* approval this server can read — which every caller must treat as "no
|
|
97
|
+
* decision", i.e. leave it for the human
|
|
98
|
+
*/
|
|
99
|
+
function parseOpencodePermissionRequest(payload) {
|
|
100
|
+
const properties = unwrap(payload);
|
|
101
|
+
const permissionId = (0, event_mapper_1.readStringField)(properties, 'id');
|
|
102
|
+
const sessionId = (0, event_mapper_1.readStringField)(properties, 'sessionID');
|
|
103
|
+
// Both are required: the id is what a verdict is addressed to, and without a
|
|
104
|
+
// session there is nothing to attribute the request to.
|
|
105
|
+
if (!permissionId || !sessionId)
|
|
106
|
+
return null;
|
|
107
|
+
// `question.asked` shares the envelope; it is answered with choices, never
|
|
108
|
+
// adjudicated as an approval.
|
|
109
|
+
if (Array.isArray(properties.questions))
|
|
110
|
+
return null;
|
|
111
|
+
const kind = (0, event_mapper_1.readStringField)(properties, 'permission');
|
|
112
|
+
const metadata = (0, event_mapper_1.isPlainObject)(properties.metadata) ? properties.metadata : {};
|
|
113
|
+
return {
|
|
114
|
+
toolName: lookupOpencodeToolName(toolCallId(properties)) ?? kind ?? 'permission',
|
|
115
|
+
toolInput: {
|
|
116
|
+
permission: kind,
|
|
117
|
+
patterns: Array.isArray(properties.patterns) ? properties.patterns : [],
|
|
118
|
+
...metadata,
|
|
119
|
+
},
|
|
120
|
+
// `per_…` is what the reply URL takes, and it is stable for the life of the
|
|
121
|
+
// request — the correlation key Claude spells `prompt_id`.
|
|
122
|
+
promptId: permissionId,
|
|
123
|
+
sessionId,
|
|
124
|
+
// opencode has no equivalent of Claude's permission mode: the dialog is
|
|
125
|
+
// drawn unconditionally (§5.5.3), so there is no "do not ask me" state that
|
|
126
|
+
// would make a no-decision non-blocking.
|
|
127
|
+
permissionMode: null,
|
|
128
|
+
// `always` is the rule that would be saved by answering `always`. Recorded
|
|
129
|
+
// for the audit trail, never a decision input — it is opencode's suggestion,
|
|
130
|
+
// which is the thing being judged.
|
|
131
|
+
permissionSuggestions: Array.isArray(properties.always) ? properties.always : null,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Read a `question.asked` frame (#1758 §5.2.4).
|
|
136
|
+
*
|
|
137
|
+
* The one place opencode is *better* structured than Claude: the choices arrive
|
|
138
|
+
* as data, where Claude's `AskUserQuestion` picker had to be read off the
|
|
139
|
+
* screen. The inner shape — `{ question, header, options: [{ label,
|
|
140
|
+
* description }] }` — is identical to Claude's `tool_input.questions`, so the
|
|
141
|
+
* existing strict parser is reused rather than re-derived.
|
|
142
|
+
*
|
|
143
|
+
* @returns The spec, or null when this is not a readable question
|
|
144
|
+
*/
|
|
145
|
+
function parseOpencodeQuestion(payload) {
|
|
146
|
+
const properties = unwrap(payload);
|
|
147
|
+
const questionId = (0, event_mapper_1.readStringField)(properties, 'id');
|
|
148
|
+
if (!questionId)
|
|
149
|
+
return null;
|
|
150
|
+
const questions = (0, ask_user_question_payload_1.parseAskUserQuestionToolInput)({ questions: properties.questions });
|
|
151
|
+
if (questions === null)
|
|
152
|
+
return null;
|
|
153
|
+
return { questions, promptId: questionId };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Turn an approval into the transport-independent {@link PendingDecision}.
|
|
157
|
+
*
|
|
158
|
+
* @param payload - A `permission.asked` frame or a `GET /permission` entry
|
|
159
|
+
* @param askedAt - Epoch ms
|
|
160
|
+
*/
|
|
161
|
+
function toOpencodePendingPermission(payload, askedAt) {
|
|
162
|
+
const parsed = parseOpencodePermissionRequest(payload);
|
|
163
|
+
if (!parsed || !parsed.promptId)
|
|
164
|
+
return null;
|
|
165
|
+
return {
|
|
166
|
+
kind: 'permission',
|
|
167
|
+
id: parsed.promptId,
|
|
168
|
+
conversationId: parsed.sessionId,
|
|
169
|
+
subject: { kind: 'permission', toolName: parsed.toolName, toolInput: parsed.toolInput },
|
|
170
|
+
raw: payload,
|
|
171
|
+
askedAt,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** Turn a question into a {@link PendingDecision}. */
|
|
175
|
+
function toOpencodePendingQuestion(payload, askedAt) {
|
|
176
|
+
const properties = unwrap(payload);
|
|
177
|
+
const questionId = (0, event_mapper_1.readStringField)(properties, 'id');
|
|
178
|
+
const spec = parseOpencodeQuestion(payload);
|
|
179
|
+
if (!questionId || !spec)
|
|
180
|
+
return null;
|
|
181
|
+
return {
|
|
182
|
+
kind: 'question',
|
|
183
|
+
id: questionId,
|
|
184
|
+
conversationId: (0, event_mapper_1.readStringField)(properties, 'sessionID'),
|
|
185
|
+
subject: { kind: 'question', spec },
|
|
186
|
+
raw: payload,
|
|
187
|
+
askedAt,
|
|
188
|
+
};
|
|
189
|
+
}
|