commandmate 0.26.1 → 0.27.1
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 +430 -402
- package/.next/app-path-routes-manifest.json +32 -28
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +12 -12
- package/.next/react-loadable-manifest.json +6 -6
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +24 -0
- 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 +2 -2
- 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/capabilities/route.js +1 -0
- package/.next/server/app/api/capabilities/route.js.nft.json +1 -0
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route.js +2 -2
- 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 +2 -2
- 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 +19 -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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route.js +1 -1
- package/.next/server/app/api/push/escalation/route.js.nft.json +1 -1
- package/.next/server/app/api/push/escalation/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_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_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_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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route.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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +2 -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]/env/route.js +2 -0
- package/.next/server/app/api/worktrees/[id]/env/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/resolve-target/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -0
- 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 +1 -5
- 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 +2 -6
- 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 +3 -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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/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 +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +3 -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_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_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 -1
- 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_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_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_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.js +2 -2
- package/.next/server/app/chat/page.js.nft.json +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page.js +2 -2
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page.js +2 -2
- package/.next/server/app/more/page.js.nft.json +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page.js +2 -2
- package/.next/server/app/repositories/page.js.nft.json +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page.js +1 -1
- package/.next/server/app/review/page.js.nft.json +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js +2 -2
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page.js +2 -2
- package/.next/server/app/skills/installed/page.js.nft.json +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page.js +2 -2
- package/.next/server/app/skills/page.js.nft.json +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +2 -2
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +9 -7
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +32 -28
- package/.next/server/chunks/1029.js +1 -1
- package/.next/server/chunks/1068.js +3 -1
- package/.next/server/chunks/1296.js +1 -1
- package/.next/server/chunks/1538.js +7 -0
- package/.next/server/chunks/1601.js +2 -2
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +3 -2
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2546.js +1 -1
- package/.next/server/chunks/2689.js +13 -0
- package/.next/server/chunks/2759.js +8 -0
- package/.next/server/chunks/2839.js +1 -0
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3292.js +1 -0
- package/.next/server/chunks/3417.js +1 -1
- package/.next/server/chunks/3480.js +1 -17
- package/.next/server/chunks/4393.js +1 -1
- package/.next/server/chunks/4423.js +1 -0
- package/.next/server/chunks/4444.js +22 -8
- package/.next/server/chunks/4565.js +3 -0
- package/.next/server/chunks/467.js +1 -0
- package/.next/server/chunks/4905.js +5 -0
- package/.next/server/chunks/5017.js +1 -0
- package/.next/server/chunks/5089.js +1 -0
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/5220.js +1 -0
- package/.next/server/chunks/5354.js +1 -0
- package/.next/server/chunks/552.js +19 -0
- package/.next/server/chunks/5571.js +1 -1
- package/.next/server/chunks/5998.js +3 -3
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6274.js +11 -11
- package/.next/server/chunks/6285.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6781.js +1 -1
- package/.next/server/chunks/7472.js +1 -0
- package/.next/server/chunks/7506.js +1 -0
- package/.next/server/chunks/848.js +1 -1
- package/.next/server/chunks/8536.js +1 -1
- package/.next/server/chunks/880.js +1 -1
- package/.next/server/chunks/8807.js +5 -5
- package/.next/server/chunks/9067.js +1 -1
- package/.next/server/chunks/9284.js +1 -0
- package/.next/server/chunks/9358.js +1 -1
- package/.next/server/chunks/9508.js +1 -0
- package/.next/server/chunks/9938.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/9IXgWfBUtnEnRqnbmErTp/_buildManifest.js +1 -0
- package/.next/static/chunks/{2053-dfac2d49227cc942.js → 2053-c0e931eb4391baa1.js} +1 -1
- package/.next/static/chunks/3361-5d6e93fba8a92bc1.js +1 -0
- package/.next/static/chunks/3922.0d2e64daf9f187da.js +1 -0
- package/.next/static/chunks/{5229-35f9acab97a553c5.js → 5229-1f5f19740a8d6c11.js} +1 -1
- package/.next/static/chunks/{6381-0e097c0286155883.js → 6381-8cfe50a5ca5425b1.js} +1 -1
- package/.next/static/chunks/{8649-f551b0010dba4986.js → 8521-c43994fb58501d13.js} +1 -1
- package/.next/static/chunks/app/api/app/update/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/app/update-check/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/assistant/conversation/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/assistant/current-output/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/assistant/messages/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/assistant/session/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/assistant/start/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/assistant/terminal/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/assistant/tools/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/auth/login/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/auth/status/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/capabilities/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/daily-summary/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/daily-summary/status/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/external-apps/[id]/health/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/external-apps/[id]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/external-apps/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/fs/browse/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/fs/recent-paths/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/hooks/agent-event/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/hooks/claude-done/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/hooks/permission-request/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/metrics/vibe/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/ollama/models/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/push/escalation/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/push/subscriptions/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/push/vapid/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/todos/[todoId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/todos/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/clone/[jobId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/clone/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/excluded/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/restore/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/scan/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/sync/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/repositories/validate-path/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/sidebar/group-order/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/skills/[id]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/skills/installations/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/skills/operations/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/skills/reindex/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/skills/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/slash-commands/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/tasks/[taskId]/cancel/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/tasks/[taskId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/templates/[id]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/templates/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/todos/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/verification/runs/[runId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/verification/runs/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/auto-yes/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/capture/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/clear-composer/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cli-tool/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cmate/schedules/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/current-output/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/env/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/[logId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/files/[...path]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/create/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/delete/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branches/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/checkout/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/commit/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/diff/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/fetch/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/log/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/pull/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/push/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/reset/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/revert/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/show/[commitHash]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stage/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/staged/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/[index]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/apply/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/pop/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/push/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/status/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/unstage/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/working-diff/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/interrupt/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/kill-session/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/logs/[filename]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/logs/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/marp-render/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/memos/[memoId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/memos/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/messages/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/resolve-target/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/respond/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/[scheduleId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/active/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/search/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/send/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/install/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/plan/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update-plan/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/slash-commands/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/special-keys/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/start-polling/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tasks/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/terminal/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/timers/history/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/timers/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/todos/[todoId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/todos/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tree/[...path]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tree/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/upload/[...path]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/[runId]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/viewed/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/chat/{page-439b35c9cb90dbe0.js → page-231df45127e876c2.js} +1 -1
- package/.next/static/chunks/app/login/{page-eefd515d8c37e5ee.js → page-adc51f0f7cbb7ac6.js} +1 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/more/{page-889b7546a8a553dc.js → page-3266dfa945972649.js} +1 -1
- package/.next/static/chunks/app/{page-a8d04ac0e3e4c9fc.js → page-0305431f43195431.js} +1 -1
- package/.next/static/chunks/app/proxy/[...path]/route-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/repositories/{page-f3c4d8839b1ca203.js → page-ae8fa55749976f79.js} +1 -1
- package/.next/static/chunks/app/review/{page-6dc88649fe88ac16.js → page-8f63aff862fee421.js} +1 -1
- package/.next/static/chunks/app/sessions/{page-2c9ed9e969097688.js → page-4d5382b257077c14.js} +1 -1
- package/.next/static/chunks/app/skills/[skillId]/{page-19beef6121c4c982.js → page-aaf0bcb1dfb558b9.js} +1 -1
- package/.next/static/chunks/app/skills/installed/{page-d5b9cef6d21fabda.js → page-dafc486410e839b7.js} +1 -1
- package/.next/static/chunks/app/skills/{page-06ef637e07dd9377.js → page-9e3c91da31514893.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/{page-5fed3984fafcd910.js → page-90f96977c741bbf8.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/layout-6add0d88e2828c23.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-f7b3dfbe757dc6b8.js +1 -0
- package/.next/static/chunks/{webpack-9d892a9ff07c289d.js → webpack-6a9668cf54a26eba.js} +1 -1
- package/.next/static/css/928e5a8830fbf997.css +1 -0
- package/.next/trace +13 -12
- package/.next/types/app/api/capabilities/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/clear-composer/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/env/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/resolve-target/route.ts +347 -0
- package/.next/types/routes.d.ts +5 -1
- package/.next/types/validator.ts +36 -0
- package/dist/cli/commands/auto-yes.d.ts.map +1 -1
- package/dist/cli/commands/auto-yes.js +40 -2
- package/dist/cli/commands/capture.d.ts.map +1 -1
- package/dist/cli/commands/capture.js +28 -13
- package/dist/cli/commands/instances.d.ts +30 -18
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +32 -36
- package/dist/cli/commands/ls.d.ts.map +1 -1
- package/dist/cli/commands/ls.js +73 -1
- package/dist/cli/commands/respond.d.ts.map +1 -1
- package/dist/cli/commands/respond.js +18 -1
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +21 -15
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +38 -0
- package/dist/cli/commands/wait.d.ts +26 -0
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +390 -43
- package/dist/cli/config/cli-dependencies.d.ts.map +1 -1
- package/dist/cli/config/cli-dependencies.js +16 -0
- package/dist/cli/config/cli-tool-ids.d.ts +11 -0
- package/dist/cli/config/cli-tool-ids.d.ts.map +1 -1
- package/dist/cli/config/cli-tool-ids.js +12 -1
- package/dist/cli/types/api-responses.d.ts +443 -2
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/utils/api-client.d.ts +18 -0
- package/dist/cli/utils/api-client.d.ts.map +1 -1
- package/dist/cli/utils/api-client.js +22 -0
- package/dist/cli/utils/server-capabilities.d.ts +56 -0
- package/dist/cli/utils/server-capabilities.d.ts.map +1 -0
- package/dist/cli/utils/server-capabilities.js +166 -0
- package/dist/cli/utils/session-target.d.ts +70 -0
- package/dist/cli/utils/session-target.d.ts.map +1 -0
- package/dist/cli/utils/session-target.js +118 -0
- package/dist/lib/cli-tools/copilot-executable.d.ts +79 -0
- package/dist/lib/cli-tools/copilot-executable.d.ts.map +1 -0
- package/dist/lib/cli-tools/copilot-executable.js +155 -0
- package/dist/lib/cli-tools/types.d.ts +61 -0
- package/dist/lib/cli-tools/types.d.ts.map +1 -1
- package/dist/lib/cli-tools/types.js +41 -0
- package/dist/lib/detection/ansi.d.ts +2 -1
- package/dist/lib/detection/ansi.d.ts.map +1 -1
- package/dist/lib/detection/ansi.js +3 -2
- package/dist/lib/detection/tools/verified-against.d.ts +84 -0
- package/dist/lib/detection/tools/verified-against.d.ts.map +1 -0
- package/dist/lib/detection/tools/verified-against.js +88 -0
- package/dist/lib/detection/version-probes.d.ts +207 -0
- package/dist/lib/detection/version-probes.d.ts.map +1 -0
- package/dist/lib/detection/version-probes.js +290 -0
- package/dist/lib/security/env-sanitizer.d.ts +198 -0
- package/dist/lib/security/env-sanitizer.d.ts.map +1 -0
- package/dist/lib/security/env-sanitizer.js +232 -0
- package/dist/server/locales/en/notifications.json +17 -6
- package/dist/server/locales/ja/notifications.json +17 -6
- package/dist/server/src/config/activity-bar-config.js +4 -0
- package/dist/server/src/config/cli-tool-timing-config.js +59 -2
- package/dist/server/src/config/copilot-constants.js +11 -1
- package/dist/server/src/config/detection-evidence-config.js +129 -0
- package/dist/server/src/config/schedule-config.js +56 -7
- package/dist/server/src/config/tmux-pane-config.js +18 -1
- package/dist/server/src/lib/auto-yes-poller.js +36 -0
- package/dist/server/src/lib/cli-tools/antigravity.js +4 -2
- package/dist/server/src/lib/cli-tools/base.js +131 -10
- package/dist/server/src/lib/cli-tools/capture-spec.js +56 -0
- package/dist/server/src/lib/cli-tools/claude.js +1 -1
- package/dist/server/src/lib/cli-tools/codex.js +6 -2
- package/dist/server/src/lib/cli-tools/composer-spec.js +96 -0
- package/dist/server/src/lib/cli-tools/copilot-executable.js +155 -0
- package/dist/server/src/lib/cli-tools/copilot.js +307 -81
- package/dist/server/src/lib/cli-tools/gemini.js +8 -6
- package/dist/server/src/lib/cli-tools/graceful-exit.js +181 -0
- package/dist/server/src/lib/cli-tools/index.js +11 -1
- package/dist/server/src/lib/cli-tools/manager.js +56 -4
- package/dist/server/src/lib/cli-tools/opencode-config.js +195 -24
- package/dist/server/src/lib/cli-tools/opencode.js +233 -30
- package/dist/server/src/lib/cli-tools/session-name.js +57 -0
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +213 -30
- package/dist/server/src/lib/cli-tools/types.js +41 -0
- package/dist/server/src/lib/cli-tools/vibe-local.js +7 -3
- package/dist/server/src/lib/cmate-cli-tool-parser.js +29 -1
- package/dist/server/src/lib/cmate-parser.js +12 -4
- package/dist/server/src/lib/db/db.js +5 -1
- package/dist/server/src/lib/db/migrations/v41-push-subscriptions.js +11 -0
- package/dist/server/src/lib/db/push-subscriptions-db.js +60 -3
- package/dist/server/src/lib/detection/ansi.js +3 -2
- package/dist/server/src/lib/detection/cli-patterns.js +901 -67
- package/dist/server/src/lib/detection/composer-text.js +455 -0
- package/dist/server/src/lib/detection/idle-evidence-observation.js +51 -0
- package/dist/server/src/lib/detection/model-info-extractor.js +195 -4
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +19 -0
- package/dist/server/src/lib/detection/status-detector.js +76 -675
- package/dist/server/src/lib/detection/status-reason.js +57 -0
- package/dist/server/src/lib/detection/tools/antigravity/detect.js +75 -0
- package/dist/server/src/lib/detection/tools/claude/detect.js +144 -0
- package/dist/server/src/lib/detection/tools/claude/patterns.js +98 -0
- package/dist/server/src/lib/detection/tools/claude/prompt.js +124 -0
- package/dist/server/src/lib/detection/tools/codex/detect.js +343 -0
- package/dist/server/src/lib/detection/tools/codex/prompt.js +104 -0
- package/dist/server/src/lib/detection/tools/copilot/detect.js +185 -0
- package/dist/server/src/lib/detection/tools/copilot/prompt.js +89 -0
- package/dist/server/src/lib/detection/tools/dialog-block.js +159 -0
- package/dist/server/src/lib/detection/tools/frame.js +47 -0
- package/dist/server/src/lib/detection/tools/opencode/detect.js +242 -0
- package/dist/server/src/lib/detection/tools/opencode/prompt.js +85 -0
- package/dist/server/src/lib/detection/tools/registry.js +58 -0
- package/dist/server/src/lib/detection/tools/run-detection.js +202 -0
- package/dist/server/src/lib/detection/tools/types.js +34 -0
- package/dist/server/src/lib/detection/tools/verified-against.js +88 -0
- package/dist/server/src/lib/hooks/agent-event-types.js +19 -1
- package/dist/server/src/lib/hooks/permission-adjudication.js +160 -0
- package/dist/server/src/lib/hooks/permission-decision-service.js +81 -14
- package/dist/server/src/lib/hooks/permission-decision-state.js +62 -0
- package/dist/server/src/lib/hooks/permission-request-payload.js +18 -1
- package/dist/server/src/lib/hooks/sources/antigravity/source.js +10 -0
- package/dist/server/src/lib/hooks/sources/claude/source.js +20 -0
- package/dist/server/src/lib/hooks/sources/codex/source.js +10 -0
- package/dist/server/src/lib/hooks/sources/copilot/hook-settings.js +403 -50
- package/dist/server/src/lib/hooks/sources/copilot/source.js +40 -4
- package/dist/server/src/lib/hooks/sources/define-source.js +23 -1
- package/dist/server/src/lib/hooks/sources/event-mapper.js +29 -1
- package/dist/server/src/lib/hooks/sources/gemini/source.js +10 -0
- package/dist/server/src/lib/hooks/sources/index.js +3 -1
- package/dist/server/src/lib/hooks/sources/launch-command.js +96 -1
- package/dist/server/src/lib/hooks/sources/legacy-relay.js +12 -0
- package/dist/server/src/lib/hooks/sources/opencode/client.js +295 -32
- package/dist/server/src/lib/hooks/sources/opencode/ingest.js +120 -42
- package/dist/server/src/lib/hooks/sources/opencode/mappers.js +95 -3
- package/dist/server/src/lib/hooks/sources/opencode/ports.js +70 -9
- package/dist/server/src/lib/hooks/sources/opencode/runtime.js +62 -4
- package/dist/server/src/lib/hooks/sources/opencode/source.js +54 -3
- package/dist/server/src/lib/hooks/sources/opencode/subscription.js +254 -26
- package/dist/server/src/lib/hooks/sources/opencode/turn-gate.js +119 -4
- package/dist/server/src/lib/hooks/sources/pending-decisions.js +40 -2
- package/dist/server/src/lib/hooks/tool-input-normalization-state.js +67 -0
- package/dist/server/src/lib/hooks/tool-input-normalization.js +157 -0
- package/dist/server/src/lib/job-executor.js +16 -4
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +184 -0
- package/dist/server/src/lib/polling/response-checker.js +243 -26
- package/dist/server/src/lib/push/failure-episode-state.js +127 -0
- package/dist/server/src/lib/push/failure-push-notifier.js +344 -0
- package/dist/server/src/lib/push/index.js +35 -1
- package/dist/server/src/lib/push/prompt-card-state.js +93 -0
- package/dist/server/src/lib/push/prompt-push-gate.js +130 -0
- package/dist/server/src/lib/push/push-sender.js +82 -7
- package/dist/server/src/lib/push/resolution-push-notifier.js +193 -0
- package/dist/server/src/lib/push/waiting-push-notifier.js +42 -0
- package/dist/server/src/lib/response-cleaner.js +93 -20
- package/dist/server/src/lib/response-extractor.js +98 -5
- package/dist/server/src/lib/security/env-sanitizer.js +179 -5
- package/dist/server/src/lib/session/agent-event-state.js +1038 -189
- package/dist/server/src/lib/session/agent-session-lifecycle.js +8 -0
- package/dist/server/src/lib/session/claude-executor.js +23 -3
- package/dist/server/src/lib/session/claude-session.js +11 -1
- package/dist/server/src/lib/session/composer-clear.js +95 -0
- package/dist/server/src/lib/session/current-output-builder.js +267 -25
- package/dist/server/src/lib/session/provisional-turn.js +310 -0
- package/dist/server/src/lib/session/resolve-session-target.js +156 -0
- package/dist/server/src/lib/session/send-user-message.js +14 -18
- package/dist/server/src/lib/session/session-start-error.js +53 -1
- package/dist/server/src/lib/session/status-evidence.js +217 -0
- package/dist/server/src/lib/session/structured-prompt.js +46 -4
- package/dist/server/src/lib/session/waiting-episode-state.js +24 -0
- package/dist/server/src/lib/tmux/key-sequence.js +120 -0
- package/dist/server/src/lib/tmux/tmux.js +91 -19
- package/dist/server/src/lib/tui-accumulator.js +68 -1
- package/dist/server/src/lib/verification/gate-runner.js +84 -1
- package/dist/server/src/lib/ws-server.js +8 -5
- package/dist/server/src/types/cli-tool-contracts.js +75 -0
- package/dist/server/src/types/terminal-keys.js +37 -0
- package/dist/types/cli-tool-contracts.d.ts +235 -0
- package/dist/types/cli-tool-contracts.d.ts.map +1 -0
- package/dist/types/cli-tool-contracts.js +75 -0
- package/package.json +3 -2
- package/public/sw.js +65 -2
- package/.next/server/chunks/1841.js +0 -1
- package/.next/server/chunks/233.js +0 -1
- package/.next/server/chunks/3022.js +0 -1
- package/.next/server/chunks/3270.js +0 -1
- package/.next/server/chunks/4455.js +0 -1
- package/.next/server/chunks/5959.js +0 -27
- package/.next/server/chunks/7458.js +0 -6
- package/.next/server/chunks/8730.js +0 -1
- package/.next/server/chunks/9058.js +0 -1
- package/.next/server/chunks/9687.js +0 -3
- package/.next/static/chunks/3898-ef488bfa804507d9.js +0 -1
- package/.next/static/chunks/9263.f2034ec525abcf72.js +0 -1
- package/.next/static/chunks/app/api/app/update/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/app/update-check/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/assistant/conversation/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/assistant/current-output/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/assistant/messages/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/assistant/session/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/assistant/start/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/assistant/terminal/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/assistant/tools/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/auth/login/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/auth/status/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/daily-summary/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/daily-summary/status/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/external-apps/[id]/health/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/external-apps/[id]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/external-apps/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/fs/browse/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/fs/recent-paths/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/hooks/agent-event/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/hooks/claude-done/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/hooks/permission-request/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/metrics/vibe/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/ollama/models/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/push/escalation/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/push/subscriptions/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/push/vapid/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/todos/[todoId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/todos/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/clone/[jobId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/clone/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/excluded/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/restore/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/scan/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/sync/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/repositories/validate-path/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/sidebar/group-order/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/skills/[id]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/skills/installations/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/skills/operations/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/skills/reindex/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/skills/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/slash-commands/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/tasks/[taskId]/cancel/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/tasks/[taskId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/templates/[id]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/templates/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/todos/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/verification/runs/[runId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/verification/runs/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/auto-yes/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/capture/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cli-tool/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cmate/schedules/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/current-output/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/[logId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/files/[...path]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/create/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/delete/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branches/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/checkout/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/commit/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/diff/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/fetch/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/log/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/pull/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/push/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/reset/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/revert/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/show/[commitHash]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stage/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/staged/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/[index]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/apply/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/pop/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/push/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/status/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/unstage/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/working-diff/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/interrupt/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/kill-session/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/logs/[filename]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/logs/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/marp-render/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/memos/[memoId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/memos/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/messages/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/respond/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/[scheduleId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/active/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/search/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/send/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/install/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/plan/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update-plan/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/slash-commands/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/special-keys/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/start-polling/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tasks/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/terminal/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/timers/history/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/timers/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/todos/[todoId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/todos/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tree/[...path]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tree/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/upload/[...path]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/[runId]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/viewed/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/proxy/[...path]/route-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/layout-fae89f8d4cc3ff97.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-fdfca039b7f5837e.js +0 -1
- package/.next/static/css/e865c4979a41aa03.css +0 -1
- package/.next/static/wyFLOpmKpr45BHZ4svYpE/_buildManifest.js +0 -1
- /package/.next/static/{wyFLOpmKpr45BHZ4svYpE → 9IXgWfBUtnEnRqnbmErTp}/_ssgManifest.js +0 -0
|
@@ -39,7 +39,13 @@
|
|
|
39
39
|
* @module lib/session/agent-event-state
|
|
40
40
|
*/
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.STRUCTURED_PROMPT_PROVISIONAL_MAX_AGE_MS = exports.STRUCTURED_STATE_MAX_AGE_MS = exports.AGENT_EVENT_DEDUP_WINDOW_MS = void 0;
|
|
42
|
+
exports.LIFECYCLE_AGENT_EVENT_TYPES = exports.STRUCTURED_PROMPT_PROVISIONAL_MAX_AGE_MS = exports.STRUCTURED_STATE_MAX_AGE_MS = exports.AGENT_EVENT_DEDUP_WINDOW_MS = void 0;
|
|
43
|
+
exports.getAgentEventDropCounts = getAgentEventDropCounts;
|
|
44
|
+
exports.getPendingDecisions = getPendingDecisions;
|
|
45
|
+
exports.getAgentTurn = getAgentTurn;
|
|
46
|
+
exports.getPublishedAgentTurn = getPublishedAgentTurn;
|
|
47
|
+
exports.closeAgentTurn = closeAgentTurn;
|
|
48
|
+
exports.observeScraperCompletionEvidence = observeScraperCompletionEvidence;
|
|
43
49
|
exports.recordAgentStopEvent = recordAgentStopEvent;
|
|
44
50
|
exports.getLastStopEventAt = getLastStopEventAt;
|
|
45
51
|
exports.recordAgentEvent = recordAgentEvent;
|
|
@@ -65,6 +71,8 @@ exports.getAskUserQuestion = getAskUserQuestion;
|
|
|
65
71
|
exports.clearAskUserQuestion = clearAskUserQuestion;
|
|
66
72
|
exports.isDuplicateAgentEvent = isDuplicateAgentEvent;
|
|
67
73
|
exports.getRecentEventKeyCount = getRecentEventKeyCount;
|
|
74
|
+
exports.classifyAgentEventDelivery = classifyAgentEventDelivery;
|
|
75
|
+
exports.getRecentEventIdentityCount = getRecentEventIdentityCount;
|
|
68
76
|
exports.clearAgentStopEvents = clearAgentStopEvents;
|
|
69
77
|
const auto_yes_state_1 = require("../../lib/auto-yes-state");
|
|
70
78
|
const agent_event_types_1 = require("../../lib/hooks/agent-event-types");
|
|
@@ -72,7 +80,9 @@ const permission_request_payload_1 = require("../../lib/hooks/permission-request
|
|
|
72
80
|
// Issue #1784: the terminal-frame half of "which model / effort is this on".
|
|
73
81
|
const model_info_extractor_1 = require("../../lib/detection/model-info-extractor");
|
|
74
82
|
const status_mapping_1 = require("../../lib/session/status-mapping");
|
|
75
|
-
|
|
83
|
+
// Issue #1930: the turn model. Pure, so importing it here costs nothing at
|
|
84
|
+
// runtime beyond the two bounds it owns.
|
|
85
|
+
const provisional_turn_1 = require("../../lib/session/provisional-turn");
|
|
76
86
|
/** compositeKey -> epoch ms of the most recent stop event. */
|
|
77
87
|
const lastStopEventAt = globalThis.__agentEventLastStopAt ??
|
|
78
88
|
(globalThis.__agentEventLastStopAt = new Map());
|
|
@@ -82,15 +92,54 @@ const lastAgentEvent = globalThis.__agentEventLast ??
|
|
|
82
92
|
/** compositeKey -> epoch ms the current generation began. See {@link beginAgentEventGeneration}. */
|
|
83
93
|
const generationStartedAt = globalThis.__agentEventGenerationStartedAt ??
|
|
84
94
|
(globalThis.__agentEventGenerationStartedAt = new Map());
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
/**
|
|
96
|
+
* compositeKey -> the turn this instance is in, or the last one it was in
|
|
97
|
+
* (Issue #1930).
|
|
98
|
+
*
|
|
99
|
+
* The map that replaced "the newest event is the verdict". `lastAgentEvent`
|
|
100
|
+
* above is still written on every delivery and is still what
|
|
101
|
+
* `structuredEvents.lastEventType` publishes — it answers "did anything reach
|
|
102
|
+
* this server, and for the right instance?", which is a diagnostic question.
|
|
103
|
+
* This map answers the state question, and the two deliberately disagree
|
|
104
|
+
* whenever an event carries no verdict.
|
|
105
|
+
*
|
|
106
|
+
* The open dialogs #1725 kept in a map of their own live on the turn now
|
|
107
|
+
* ({@link TurnRecord.pendingDecisions}), because a dialog only ever happens
|
|
108
|
+
* *inside* a turn and holding them apart is what let a generation bump retire
|
|
109
|
+
* one and not the other.
|
|
110
|
+
*/
|
|
111
|
+
const agentTurns = globalThis.__agentEventTurns ??
|
|
112
|
+
(globalThis.__agentEventTurns = new Map());
|
|
113
|
+
/** compositeKey -> what this instance has had dropped, and why (Issue #1930). */
|
|
114
|
+
const dropCounts = globalThis.__agentEventDrops ??
|
|
115
|
+
(globalThis.__agentEventDrops = new Map());
|
|
116
|
+
/**
|
|
117
|
+
* Monotonic suffix for {@link TurnRecord.turnId}.
|
|
118
|
+
*
|
|
119
|
+
* Two turns can open in the same millisecond — a `post_tool_use` closing one
|
|
120
|
+
* agent's turn while another's `user_prompt_submit` lands — and an id built from
|
|
121
|
+
* the timestamp alone would then compare equal across instances. `wait` reads a
|
|
122
|
+
* change of id as "a new turn began", so the collision would be a missed turn
|
|
123
|
+
* boundary rather than a cosmetic clash.
|
|
124
|
+
*/
|
|
125
|
+
const turnSequence = globalThis.__agentEventTurnSeq ??
|
|
126
|
+
(globalThis.__agentEventTurnSeq = { value: 0 });
|
|
88
127
|
/** compositeKey -> the `AskUserQuestion` call currently in flight (#1726). */
|
|
89
128
|
const askUserQuestion = globalThis.__agentEventAskUserQuestion ??
|
|
90
129
|
(globalThis.__agentEventAskUserQuestion = new Map());
|
|
91
130
|
/** dedup key -> epoch ms it was first seen. See {@link isDuplicateAgentEvent}. */
|
|
92
131
|
const recentEventKeys = globalThis.__agentEventRecentKeys ??
|
|
93
132
|
(globalThis.__agentEventRecentKeys = new Map());
|
|
133
|
+
/**
|
|
134
|
+
* compositeKey -> (identity key -> epoch ms it was first seen) (Issue #1899).
|
|
135
|
+
*
|
|
136
|
+
* Nested rather than flat so the bound is *per instance*, which the flat
|
|
137
|
+
* {@link recentEventKeys} is not: one chatty agent must not be able to evict
|
|
138
|
+
* another's ids and let a genuine re-delivery through on a quiet pane. See
|
|
139
|
+
* {@link claimEventIdentity}.
|
|
140
|
+
*/
|
|
141
|
+
const recentEventIdentities = globalThis.__agentEventRecentIdentities ??
|
|
142
|
+
(globalThis.__agentEventRecentIdentities = new Map());
|
|
94
143
|
/** compositeKey -> the agent's own "I am waiting for instructions" (#1786). */
|
|
95
144
|
const awaitingInstruction = globalThis.__agentEventAwaitingInstruction ??
|
|
96
145
|
(globalThis.__agentEventAwaitingInstruction = new Map());
|
|
@@ -148,6 +197,209 @@ const capturedModelInfo = globalThis.__agentCapturedModelInfo ??
|
|
|
148
197
|
exports.AGENT_EVENT_DEDUP_WINDOW_MS = 3000;
|
|
149
198
|
/** Cap on retained dedup keys, so a long-lived server cannot grow one per turn. */
|
|
150
199
|
const MAX_RECENT_EVENT_KEYS = 512;
|
|
200
|
+
/** A zeroed tally. */
|
|
201
|
+
function emptyDropCounts() {
|
|
202
|
+
return {
|
|
203
|
+
dedupDropped: { identity: 0, timeWindow: 0 },
|
|
204
|
+
decisionEvicted: 0,
|
|
205
|
+
idsDiscarded: 0,
|
|
206
|
+
dialogTimedOut: 0,
|
|
207
|
+
decisionOverflow: 0,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/** The live tally for one instance, created on first use. */
|
|
211
|
+
function dropsFor(key) {
|
|
212
|
+
let counts = dropCounts.get(key);
|
|
213
|
+
if (!counts) {
|
|
214
|
+
counts = emptyDropCounts();
|
|
215
|
+
dropCounts.set(key, counts);
|
|
216
|
+
// The composite key is (worktree, tool, instance) and a long-lived server
|
|
217
|
+
// accumulates worktrees; bounded like every other map here (DR4-009).
|
|
218
|
+
trimOldestEntries(dropCounts, MAX_RECENT_EVENT_KEYS);
|
|
219
|
+
}
|
|
220
|
+
return counts;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* What this instance has had dropped, or a zeroed tally when nothing has.
|
|
224
|
+
*
|
|
225
|
+
* A copy, not the live object: this is published on the hot path and a caller
|
|
226
|
+
* that could mutate it would be able to erase the evidence.
|
|
227
|
+
*/
|
|
228
|
+
function getAgentEventDropCounts(worktreeId, cliToolId, instanceId) {
|
|
229
|
+
const counts = dropCounts.get((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId));
|
|
230
|
+
if (!counts)
|
|
231
|
+
return emptyDropCounts();
|
|
232
|
+
return { ...counts, dedupDropped: { ...counts.dedupDropped } };
|
|
233
|
+
}
|
|
234
|
+
/** Epoch ms the current generation began, or null when none was opened. */
|
|
235
|
+
function currentGeneration(key) {
|
|
236
|
+
return generationStartedAt.get(key) ?? null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The turn this instance is in, with the generation fence applied.
|
|
240
|
+
*
|
|
241
|
+
* A turn whose {@link TurnRecord.generationAt} is not the current generation was
|
|
242
|
+
* opened by a process that has been replaced. It is not deleted — the fence is
|
|
243
|
+
* cheap and deleting would lose the eviction tally — it is simply not this
|
|
244
|
+
* instance's turn any more.
|
|
245
|
+
*/
|
|
246
|
+
function fencedTurn(key) {
|
|
247
|
+
const turn = agentTurns.get(key);
|
|
248
|
+
if (!turn)
|
|
249
|
+
return null;
|
|
250
|
+
if (turn.generationAt !== currentGeneration(key))
|
|
251
|
+
return null;
|
|
252
|
+
return turn;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* {@link fencedTurn} with the staleness bound applied.
|
|
256
|
+
*
|
|
257
|
+
* The bound is applied by *closing* the turn rather than by hiding it, so
|
|
258
|
+
* `capture --json` says `closedBy: 'stale'` instead of going quiet — "the agent
|
|
259
|
+
* never reported the end of this turn" and "nothing has been reported at all"
|
|
260
|
+
* are different problems and an operator has to be able to tell them apart.
|
|
261
|
+
*
|
|
262
|
+
* Writes on read, which this module already does for corroboration, and is
|
|
263
|
+
* idempotent: the close is stamped from the event's own clock, so it does not
|
|
264
|
+
* move with the reader's `now`.
|
|
265
|
+
*/
|
|
266
|
+
function effectiveTurn(key, now) {
|
|
267
|
+
const turn = fencedTurn(key);
|
|
268
|
+
if (turn === null)
|
|
269
|
+
return null;
|
|
270
|
+
if (turn.closedAt === null && now - turn.displayEvent.at >= provisional_turn_1.TURN_STALE_AFTER_MS) {
|
|
271
|
+
turn.closedAt = turn.displayEvent.at + provisional_turn_1.TURN_STALE_AFTER_MS;
|
|
272
|
+
turn.closedBy = 'stale';
|
|
273
|
+
}
|
|
274
|
+
return turn;
|
|
275
|
+
}
|
|
276
|
+
/** Next {@link TurnRecord.turnId}. See {@link turnSequence}. */
|
|
277
|
+
function nextTurnId(at) {
|
|
278
|
+
turnSequence.value += 1;
|
|
279
|
+
return `turn-${at}-${turnSequence.value}`;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Drop the decisions this turn is holding, counting them.
|
|
283
|
+
*
|
|
284
|
+
* The generation path is the one §4 D3 決定 2 names: a process that has been
|
|
285
|
+
* replaced cannot have its approvals answered, and leaving them behind would
|
|
286
|
+
* publish `waiting` for a pane whose dialog went away with the process that
|
|
287
|
+
* drew it.
|
|
288
|
+
*/
|
|
289
|
+
function evictDecisions(key, turn) {
|
|
290
|
+
if (turn.pendingDecisions.length === 0)
|
|
291
|
+
return;
|
|
292
|
+
dropsFor(key).decisionEvicted += turn.pendingDecisions.length;
|
|
293
|
+
turn.pendingDecisions = [];
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* The decisions still describing something, dropping the ones that are not.
|
|
297
|
+
*
|
|
298
|
+
* The retention bound of {@link DIALOG_PENDING_MAX_MS}, applied on read and
|
|
299
|
+
* counted as `dialogTimedOut` so the release is visible. Kept here rather than
|
|
300
|
+
* in a timer for the reason the rest of this module has no timers: the state
|
|
301
|
+
* describes a live tmux session, and a reader is the only thing that ever needs
|
|
302
|
+
* the answer.
|
|
303
|
+
*/
|
|
304
|
+
function livePendingDecisions(key, turn, now) {
|
|
305
|
+
const live = turn.pendingDecisions.filter((decision) => (0, provisional_turn_1.isDecisionLive)(decision, now));
|
|
306
|
+
const dropped = turn.pendingDecisions.length - live.length;
|
|
307
|
+
if (dropped > 0) {
|
|
308
|
+
dropsFor(key).dialogTimedOut += dropped;
|
|
309
|
+
turn.pendingDecisions = live;
|
|
310
|
+
}
|
|
311
|
+
return live;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* The approvals this instance is blocked on right now (Issue #1930).
|
|
315
|
+
*
|
|
316
|
+
* Published as `structuredEvents.pendingDecisions`; `#1932` is what teaches
|
|
317
|
+
* `commandmate respond` to name one of these ids.
|
|
318
|
+
*
|
|
319
|
+
* @param now - Epoch ms; defaults to now
|
|
320
|
+
*/
|
|
321
|
+
function getPendingDecisions(worktreeId, cliToolId, instanceId, now = Date.now()) {
|
|
322
|
+
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
323
|
+
const turn = effectiveTurn(key, now);
|
|
324
|
+
if (turn === null)
|
|
325
|
+
return [];
|
|
326
|
+
return livePendingDecisions(key, turn, now);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The turn record for one instance, fenced and aged, or null.
|
|
330
|
+
*
|
|
331
|
+
* The seam `current-output-builder` publishes from and the suites drive. The
|
|
332
|
+
* returned object is the live record; nothing outside this module writes to it.
|
|
333
|
+
*
|
|
334
|
+
* @param now - Epoch ms; defaults to now
|
|
335
|
+
*/
|
|
336
|
+
function getAgentTurn(worktreeId, cliToolId, instanceId, now = Date.now()) {
|
|
337
|
+
return effectiveTurn((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId), now);
|
|
338
|
+
}
|
|
339
|
+
/** {@link getAgentTurn} projected onto the four published fields. */
|
|
340
|
+
function getPublishedAgentTurn(worktreeId, cliToolId, instanceId, now = Date.now()) {
|
|
341
|
+
return (0, provisional_turn_1.derivePublishedTurn)(getAgentTurn(worktreeId, cliToolId, instanceId, now));
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Close the open turn on evidence that did not come from the agent (Issue #1930).
|
|
345
|
+
*
|
|
346
|
+
* The one seam for the two {@link TurnCloseReason} values nothing in the event
|
|
347
|
+
* stream can produce:
|
|
348
|
+
*
|
|
349
|
+
* - `scraper_evidence` — see {@link observeScraperCompletionEvidence}, which is
|
|
350
|
+
* the caller in this tree.
|
|
351
|
+
* - `resync_idle` — a source whose `capabilities.resync` lets it be re-read
|
|
352
|
+
* answering "not busy" after a dropped transport. The reconnect loop that
|
|
353
|
+
* would call it lives in `sources/opencode/subscription`, which is #1931's
|
|
354
|
+
* file and deliberately untouched here; the value is in the vocabulary and
|
|
355
|
+
* the seam accepts it, so wiring it is a call site rather than a new state.
|
|
356
|
+
*
|
|
357
|
+
* A no-op when no turn is open, which is the ordinary case: neither of these is
|
|
358
|
+
* a statement about a session that already reported it finished.
|
|
359
|
+
*
|
|
360
|
+
* @param at - Epoch ms; defaults to now
|
|
361
|
+
* @returns Whether a turn was closed
|
|
362
|
+
*/
|
|
363
|
+
function closeAgentTurn(worktreeId, cliToolId, instanceId, closedBy, at = Date.now()) {
|
|
364
|
+
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
365
|
+
const turn = fencedTurn(key);
|
|
366
|
+
if (turn === null || turn.closedAt !== null)
|
|
367
|
+
return false;
|
|
368
|
+
turn.closedAt = at;
|
|
369
|
+
turn.closedBy = closedBy;
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Feed one poll's reading of the pane into the turn's completion counter
|
|
374
|
+
* (Issue #1930).
|
|
375
|
+
*
|
|
376
|
+
* `completed` is the scraper's own verdict — `ready` with positive evidence —
|
|
377
|
+
* NOT the merged one, which would be circular: the merge is what the structured
|
|
378
|
+
* layer's `running` overrides, so reading it back would only ever confirm this
|
|
379
|
+
* layer's own answer.
|
|
380
|
+
*
|
|
381
|
+
* See {@link SCRAPER_COMPLETION_POLLS} for why three, and for why closing here
|
|
382
|
+
* does not complete a `commandmate wait`.
|
|
383
|
+
*
|
|
384
|
+
* @param at - Epoch ms; defaults to now
|
|
385
|
+
* @returns Whether this poll closed the turn
|
|
386
|
+
*/
|
|
387
|
+
function observeScraperCompletionEvidence(worktreeId, cliToolId, instanceId, completed, at = Date.now()) {
|
|
388
|
+
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
389
|
+
const turn = fencedTurn(key);
|
|
390
|
+
if (turn === null || turn.closedAt !== null)
|
|
391
|
+
return false;
|
|
392
|
+
if (!completed) {
|
|
393
|
+
turn.scraperCompletionPolls = 0;
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
turn.scraperCompletionPolls += 1;
|
|
397
|
+
if (turn.scraperCompletionPolls < provisional_turn_1.SCRAPER_COMPLETION_POLLS)
|
|
398
|
+
return false;
|
|
399
|
+
turn.closedAt = at;
|
|
400
|
+
turn.closedBy = 'scraper_evidence';
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
151
403
|
/**
|
|
152
404
|
* Record that `instanceId` reported it stopped.
|
|
153
405
|
*
|
|
@@ -164,22 +416,100 @@ function recordAgentStopEvent(worktreeId, cliToolId, instanceId, at = Date.now()
|
|
|
164
416
|
function getLastStopEventAt(worktreeId, cliToolId, instanceId) {
|
|
165
417
|
return lastStopEventAt.get((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId)) ?? null;
|
|
166
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* The verdicts that mean this instance is inside a turn (Issue #1903).
|
|
421
|
+
*
|
|
422
|
+
* `waiting` is in the list because a dialog only happens *during* a turn: the
|
|
423
|
+
* agent asked for permission in the middle of the work it was doing, and the
|
|
424
|
+
* turn it belongs to is as open as one reading `running`. Leaving it out would
|
|
425
|
+
* fix the measured copilot window (`UserPromptSubmit` -> `SessionStart`) and
|
|
426
|
+
* leave the same hole one event further in.
|
|
427
|
+
*
|
|
428
|
+
* `ready` (`stop` / `idle_prompt`) and null (no event, a previous generation, a
|
|
429
|
+
* stale one, or an event with no verdict at all) both mean no turn is open.
|
|
430
|
+
*/
|
|
431
|
+
const OPEN_TURN_STATUSES = ['running', 'waiting'];
|
|
432
|
+
/**
|
|
433
|
+
* Whether this `session_start` is the current turn's own, arriving late
|
|
434
|
+
* (Issue #1903).
|
|
435
|
+
*
|
|
436
|
+
* copilot 1.0.80 fires `UserPromptSubmit` and *then* `SessionStart`, 12-15 s
|
|
437
|
+
* later on a first turn — measured twice, and the payload says so itself: the
|
|
438
|
+
* captured `SessionStart` carries `initial_prompt` with the text of the prompt
|
|
439
|
+
* that was already submitted. Under the "newest event is the verdict" model
|
|
440
|
+
* that arrival erased `running / hook_prompt_submit`, because
|
|
441
|
+
* `agentEventToSessionStatus` answers null for `session_start`; the pane fell
|
|
442
|
+
* back to the scraper, which reads a generating copilot frame as `ready`
|
|
443
|
+
* (#1885), and a `commandmate wait` started inside that window exited 0 with
|
|
444
|
+
* `basis=scraper_ready` while the agent was still thinking.
|
|
445
|
+
*
|
|
446
|
+
* The rule is the design policy's (§4 D3 decision 2): *an event carrying no
|
|
447
|
+
* verdict does not close an open turn*. Here that is expressed as "it does not
|
|
448
|
+
* replace the event the verdict is read from either", because this model has
|
|
449
|
+
* one record where the turn model has two fields.
|
|
450
|
+
*
|
|
451
|
+
* Three conditions, and each one is load-bearing:
|
|
452
|
+
*
|
|
453
|
+
* 1. **The source declares it.** Not a tool id — flip copilot's capability to
|
|
454
|
+
* `false` and the late frame overwrites again, flip claude's to `true` and
|
|
455
|
+
* claude's would be held. #1901 reads `permissionHookPredictsDialog` the
|
|
456
|
+
* same way.
|
|
457
|
+
* 2. **A turn is open**, judged by {@link getStructuredSessionState} *as of the
|
|
458
|
+
* arriving event's own timestamp* — which is how this inherits the
|
|
459
|
+
* generation fence and the {@link STRUCTURED_STATE_MAX_AGE_MS} bound rather
|
|
460
|
+
* than growing a second copy of either. A `session_start` on an idle
|
|
461
|
+
* instance, after a `stop`, or as the first event of a session is recorded
|
|
462
|
+
* exactly as it always was, generation bump included. That is what keeps
|
|
463
|
+
* `/clear` working: it arrives as `session_end` (verdict null, so the turn
|
|
464
|
+
* is no longer open) followed by `session_start`.
|
|
465
|
+
* 3. **It does not name a different agent session.** A genuine restart inside
|
|
466
|
+
* the pane is a different `session_id`, and holding *that* frame would be
|
|
467
|
+
* the real cost of this rule — the instance would keep publishing the dead
|
|
468
|
+
* process's `running`. When either side is null the two are treated as the
|
|
469
|
+
* same session, because "no id" is the shape a hand-configured #1549 hook
|
|
470
|
+
* posts and the fix has to survive it; that residue is bounded by
|
|
471
|
+
* {@link STRUCTURED_STATE_MAX_AGE_MS}, after which the scraper takes the
|
|
472
|
+
* session back, and by `beginAgentEventGeneration` on every session
|
|
473
|
+
* CommandMate itself (re)starts.
|
|
474
|
+
*/
|
|
475
|
+
function isLateSessionStart(worktreeId, cliToolId, instanceId, record, options) {
|
|
476
|
+
if (record.event !== 'session_start')
|
|
477
|
+
return false;
|
|
478
|
+
if (options.sessionStartMayArriveLate !== true)
|
|
479
|
+
return false;
|
|
480
|
+
const openTurn = getStructuredSessionState(worktreeId, cliToolId, instanceId, record.at);
|
|
481
|
+
if (openTurn === null || !OPEN_TURN_STATUSES.includes(openTurn.status))
|
|
482
|
+
return false;
|
|
483
|
+
const openSessionId = lastAgentEvent.get((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId))?.sessionId ?? null;
|
|
484
|
+
if (openSessionId === null || record.sessionId == null)
|
|
485
|
+
return true;
|
|
486
|
+
return record.sessionId === openSessionId;
|
|
487
|
+
}
|
|
167
488
|
/**
|
|
168
489
|
* Record any structured event against an instance (Issue #1722).
|
|
169
490
|
*
|
|
170
491
|
* Deliberately does not touch `lastStopEventAt`: that timestamp belongs to
|
|
171
492
|
* `applyAgentStopEvent`, which writes it alongside the task transition it drives
|
|
172
493
|
* so the two cannot disagree.
|
|
494
|
+
*
|
|
495
|
+
* @param options - What the delivering source declares about itself (#1903)
|
|
496
|
+
* @returns Whether the delivery was applied, so the caller can log a held one.
|
|
497
|
+
* Callers that do not care may ignore it; every pre-#1903 caller does.
|
|
173
498
|
*/
|
|
174
|
-
function recordAgentEvent(worktreeId, cliToolId, instanceId, record) {
|
|
499
|
+
function recordAgentEvent(worktreeId, cliToolId, instanceId, record, options = {}) {
|
|
175
500
|
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
501
|
+
if (isLateSessionStart(worktreeId, cliToolId, instanceId, record, options)) {
|
|
502
|
+
// Issue #1903. Held, not discarded: the model latch below is not part of
|
|
503
|
+
// the turn and never was. `SessionStart` is the one event Claude puts a
|
|
504
|
+
// model on, so a source that both declares this capability and reports a
|
|
505
|
+
// model would otherwise be the single case where the model is extracted and
|
|
506
|
+
// then dropped on the floor. copilot reports none today (#1783), which is
|
|
507
|
+
// exactly why this has to be decided here rather than left to be noticed.
|
|
508
|
+
latchAgentModel(key, record);
|
|
509
|
+
return { recorded: false, skipped: 'late-session-start' };
|
|
182
510
|
}
|
|
511
|
+
lastAgentEvent.set(key, record);
|
|
512
|
+
latchAgentModel(key, record);
|
|
183
513
|
applyAskUserQuestionTransition(key, record);
|
|
184
514
|
if (record.event === 'session_start') {
|
|
185
515
|
// The agent restarting inside a pane CommandMate never touched — `claude`
|
|
@@ -188,9 +518,59 @@ function recordAgentEvent(worktreeId, cliToolId, instanceId, record) {
|
|
|
188
518
|
// new tmux session is. Recorded from the event's own timestamp, so the
|
|
189
519
|
// event that opens a generation is never stale against it.
|
|
190
520
|
generationStartedAt.set(key, record.at);
|
|
521
|
+
// Issue #1930: and the turn the previous process was in ends with it. Done
|
|
522
|
+
// here rather than inside the transition below so the two paths into a new
|
|
523
|
+
// generation — this one and `beginAgentEventGeneration` — close the turn in
|
|
524
|
+
// exactly one place.
|
|
525
|
+
fenceTurnForNewGeneration(key, record.at);
|
|
191
526
|
}
|
|
192
|
-
|
|
527
|
+
// Issue #1930: after the generation is settled, so a turn opened by this
|
|
528
|
+
// event belongs to the generation the event itself opened.
|
|
529
|
+
applyTurnTransition(key, record);
|
|
193
530
|
applyAwaitingInstructionTransition(key, record);
|
|
531
|
+
return { recorded: true };
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* End the open turn because the process that owned it has been replaced
|
|
535
|
+
* (Issue #1930).
|
|
536
|
+
*
|
|
537
|
+
* The turn is closed and re-stamped into the new generation rather than
|
|
538
|
+
* deleted, which is the difference between `capture --json` reporting
|
|
539
|
+
* `closedBy: 'generation'` and reporting nothing at all. Its approvals go: they
|
|
540
|
+
* were raised by a process that no longer exists, and answering one would
|
|
541
|
+
* deliver a verdict into a slot nobody is holding.
|
|
542
|
+
*/
|
|
543
|
+
function fenceTurnForNewGeneration(key, at) {
|
|
544
|
+
const turn = agentTurns.get(key);
|
|
545
|
+
if (!turn)
|
|
546
|
+
return;
|
|
547
|
+
evictDecisions(key, turn);
|
|
548
|
+
// A turn that had ALREADY ended is left where it is, which means the fence in
|
|
549
|
+
// `fencedTurn` hides it from here on. That asymmetry is deliberate: the
|
|
550
|
+
// previous process's `Stop` is not this process's, and re-stamping it into the
|
|
551
|
+
// new generation would publish `ready` — "the agent finished" — for a session
|
|
552
|
+
// nobody has typed into yet. Before #1930 the same protection came from
|
|
553
|
+
// comparing the event's timestamp against the generation.
|
|
554
|
+
if (turn.closedAt !== null)
|
|
555
|
+
return;
|
|
556
|
+
// An OPEN turn is closed and carried across, so `capture --json` can say
|
|
557
|
+
// `closedBy: 'generation'` rather than going quiet. `getStructuredSessionState`
|
|
558
|
+
// reads that reason as "nothing is known", so it publishes no verdict either
|
|
559
|
+
// way; what it buys is an operator being able to tell "the session was
|
|
560
|
+
// restarted under this turn" from "nothing was ever reported".
|
|
561
|
+
turn.closedAt = at;
|
|
562
|
+
turn.closedBy = 'generation';
|
|
563
|
+
turn.generationAt = at;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Issue #1783: latch, never clear. An event without a model is the ordinary
|
|
567
|
+
* case (Claude sends one on `SessionStart` alone), and reading it as "the model
|
|
568
|
+
* is now unknown" would blank the display on the very next event.
|
|
569
|
+
*/
|
|
570
|
+
function latchAgentModel(key, record) {
|
|
571
|
+
if (typeof record.model === 'string' && record.model !== '') {
|
|
572
|
+
lastAgentModel.set(key, record.model.slice(0, agent_event_types_1.MAX_EVENT_DETAIL_LENGTH));
|
|
573
|
+
}
|
|
194
574
|
}
|
|
195
575
|
/**
|
|
196
576
|
* The third state: "this turn is over and nobody has told me what to do next"
|
|
@@ -278,77 +658,93 @@ function isAwaitingInstruction(worktreeId, cliToolId, instanceId) {
|
|
|
278
658
|
return getAwaitingInstruction(worktreeId, cliToolId, instanceId) !== null;
|
|
279
659
|
}
|
|
280
660
|
/**
|
|
281
|
-
* The
|
|
282
|
-
* (Issue #
|
|
283
|
-
*
|
|
284
|
-
* | event
|
|
285
|
-
*
|
|
286
|
-
* | `
|
|
287
|
-
* | `
|
|
288
|
-
* | `
|
|
289
|
-
* | `stop`
|
|
290
|
-
* | `
|
|
291
|
-
* | `
|
|
292
|
-
* | `
|
|
293
|
-
* | `
|
|
294
|
-
*
|
|
295
|
-
* `
|
|
296
|
-
* `
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* `
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
|
|
324
|
-
|
|
661
|
+
* The turn transition: what one delivery does to {@link TurnRecord}
|
|
662
|
+
* (Issue #1930, §4 D3 決定 2).
|
|
663
|
+
*
|
|
664
|
+
* | event | turn | dialog | display |
|
|
665
|
+
* |------------------------------------|-------------------------|----------|---------|
|
|
666
|
+
* | `user_prompt_submit` | **opens a new one** | release | yes |
|
|
667
|
+
* | `pre_tool_use` | continues, else opens | unchanged| yes |
|
|
668
|
+
* | `post_tool_use` | continues, else opens | release | yes |
|
|
669
|
+
* | `stop` (this session) | **closes** `stop` | release | yes |
|
|
670
|
+
* | `stop` (another session) | unchanged | unchanged| no |
|
|
671
|
+
* | `session_end` | **closes** `session_end`| release | no |
|
|
672
|
+
* | `session_start` | new generation (above) | evicted | no |
|
|
673
|
+
* | `notification(permission_prompt)` | unchanged | **open** | only to bootstrap |
|
|
674
|
+
* | `notification(permission_replied)` | unchanged | release | no |
|
|
675
|
+
* | `notification(idle_prompt)` | unchanged | release | yes |
|
|
676
|
+
* | `notification(anything else)` | unchanged | unchanged| no |
|
|
677
|
+
*
|
|
678
|
+
* Four rows are the whole point of the Issue, and each of them was a defect
|
|
679
|
+
* under "the newest event is the verdict":
|
|
680
|
+
*
|
|
681
|
+
* - **`user_prompt_submit` opens a new turn, the tool-call events continue the
|
|
682
|
+
* open one.** That is what makes {@link TurnRecord.turnId} an identity: #1926
|
|
683
|
+
* re-stamped it on every `pre_tool_use`, so a consumer reading a changed id
|
|
684
|
+
* as "a new turn began" false-positived several times inside one turn. `wait`
|
|
685
|
+
* reads it that way now.
|
|
686
|
+
* - **A `stop` naming another session changes nothing.** opencode publishes
|
|
687
|
+
* `session.idle` for every session its server holds, other processes'
|
|
688
|
+
* included (#1758 §5.6).
|
|
689
|
+
* - **`session_start` / `session_end` / an unknown notification /
|
|
690
|
+
* `permission_replied` do not become the displayed event.** An event carrying
|
|
691
|
+
* no verdict must not erase one. #1903 fixed the single measured instance
|
|
692
|
+
* (copilot's late `SessionStart`) by holding the delivery; the rule is
|
|
693
|
+
* general here, and the capability it reads is still the only reason a
|
|
694
|
+
* `session_start` is held rather than recorded.
|
|
695
|
+
* - **`notification(permission_prompt)` does not become the displayed event
|
|
696
|
+
* when a turn already exists.** The dialog is a fact about the *pane*, and
|
|
697
|
+
* it is published by the decision ledger below. Letting it overwrite the
|
|
698
|
+
* display is how the pre-#1898 `waiting` outlived the approval that caused
|
|
699
|
+
* it: released, the record still said `waiting`, because the only thing it
|
|
700
|
+
* could read was the event that opened the dialog.
|
|
701
|
+
*
|
|
702
|
+
* `idle_prompt` is the one row that publishes `ready` **without** closing the
|
|
703
|
+
* turn, and that is a measurement rather than an oversight: #1839 caught Claude
|
|
704
|
+
* emitting it 62 s into a turn that ran nothing, so it cannot be a turn
|
|
705
|
+
* boundary. `wait`'s gate stays armed through it; what changes is only what the
|
|
706
|
+
* pane displays.
|
|
707
|
+
*/
|
|
708
|
+
function applyTurnTransition(key, record) {
|
|
709
|
+
const generation = currentGeneration(key);
|
|
710
|
+
// An event stamped before the current generation was produced by a process
|
|
711
|
+
// that has been replaced. Refusing it here is what makes re-recording a stale
|
|
712
|
+
// `user_prompt_submit` at its original timestamp a no-op.
|
|
713
|
+
if (generation !== null && record.at < generation)
|
|
714
|
+
return;
|
|
325
715
|
switch (record.event) {
|
|
326
|
-
case 'notification':
|
|
327
|
-
if (record.detail === 'permission_prompt') {
|
|
328
|
-
openPromptWaiting(key, {
|
|
329
|
-
source: 'notification',
|
|
330
|
-
message: record.message ?? null,
|
|
331
|
-
at: record.at,
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
else if (record.detail === 'idle_prompt') {
|
|
335
|
-
promptWaiting.delete(key);
|
|
336
|
-
}
|
|
337
|
-
return;
|
|
338
|
-
case 'stop':
|
|
339
716
|
case 'user_prompt_submit':
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
promptWaiting.delete(key);
|
|
717
|
+
openTurn(key, record, generation, { continueOpen: false });
|
|
718
|
+
releaseAllDecisions(key);
|
|
343
719
|
return;
|
|
344
720
|
case 'post_tool_use':
|
|
721
|
+
openTurn(key, record, generation, { continueOpen: true });
|
|
345
722
|
// The tool call the dialog was gating has finished, so somebody answered
|
|
346
723
|
// it (Issue #1726). This is the release #1725 could not have: it had no
|
|
347
724
|
// event meaning "the human answered", only `Stop` meaning "the turn
|
|
348
725
|
// ended", which can be minutes later.
|
|
349
|
-
|
|
726
|
+
releaseAllDecisions(key);
|
|
350
727
|
return;
|
|
351
728
|
case 'pre_tool_use':
|
|
729
|
+
// Deliberately leaves the dialog alone (Issue #1726). It is the
|
|
730
|
+
// `AskUserQuestion` invocation, and a picker being *about to be drawn* is
|
|
731
|
+
// not a fact this state can carry.
|
|
732
|
+
openTurn(key, record, generation, { continueOpen: true });
|
|
733
|
+
return;
|
|
734
|
+
case 'stop':
|
|
735
|
+
applyStopToTurn(key, record, generation);
|
|
736
|
+
return;
|
|
737
|
+
case 'session_end':
|
|
738
|
+
applySessionEndToTurn(key, record);
|
|
739
|
+
releaseAllDecisions(key);
|
|
740
|
+
return;
|
|
741
|
+
case 'session_start':
|
|
742
|
+
// The generation was opened by `recordAgentEvent` before this ran, and
|
|
743
|
+
// `fenceTurnForNewGeneration` closed the turn with it. Nothing further:
|
|
744
|
+
// the frame carries no verdict, so it is not the displayed event either.
|
|
745
|
+
return;
|
|
746
|
+
case 'notification':
|
|
747
|
+
applyNotificationToTurn(key, record, generation);
|
|
352
748
|
return;
|
|
353
749
|
default:
|
|
354
750
|
// exhaustive check: a new AgentEventType must decide its transition here
|
|
@@ -356,6 +752,304 @@ function applyPromptWaitingTransition(key, record) {
|
|
|
356
752
|
return;
|
|
357
753
|
}
|
|
358
754
|
}
|
|
755
|
+
/** The three fields the turn keeps from a record. */
|
|
756
|
+
function displayOf(record) {
|
|
757
|
+
return { event: record.event, at: record.at, detail: record.detail };
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Open a turn for this event, or move the display onto an open one.
|
|
761
|
+
*
|
|
762
|
+
* @param continueOpen - Whether an already-open turn of the same session is
|
|
763
|
+
* this event's turn. False for `user_prompt_submit`, which IS a new turn.
|
|
764
|
+
*/
|
|
765
|
+
function openTurn(key, record, generation, { continueOpen }) {
|
|
766
|
+
const turn = fencedTurn(key);
|
|
767
|
+
const openHere = turn !== null &&
|
|
768
|
+
turn.closedAt === null &&
|
|
769
|
+
turn.openedAt !== null &&
|
|
770
|
+
(0, provisional_turn_1.closesTurn)(turn, record.sessionId);
|
|
771
|
+
if (continueOpen && openHere) {
|
|
772
|
+
turn.displayEvent = displayOf(record);
|
|
773
|
+
// A hand-configured relay posts no session id, so a turn can learn one
|
|
774
|
+
// partway through. Learning it is what lets a later `stop` be matched.
|
|
775
|
+
turn.sessionId ??= record.sessionId;
|
|
776
|
+
turn.scraperCompletionPolls = 0;
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
// A dialog-only record (openedAt null) is not a turn; its decisions belong to
|
|
780
|
+
// the turn that is opening now, because that is when they were raised.
|
|
781
|
+
const carried = turn !== null && turn.openedAt === null && turn.closedAt === null
|
|
782
|
+
? turn.pendingDecisions
|
|
783
|
+
: [];
|
|
784
|
+
agentTurns.set(key, {
|
|
785
|
+
turnId: nextTurnId(record.at),
|
|
786
|
+
sessionId: record.sessionId,
|
|
787
|
+
openedAt: record.at,
|
|
788
|
+
closedAt: null,
|
|
789
|
+
closedBy: null,
|
|
790
|
+
generationAt: generation,
|
|
791
|
+
displayEvent: displayOf(record),
|
|
792
|
+
pendingDecisions: carried,
|
|
793
|
+
scraperCompletionPolls: 0,
|
|
794
|
+
});
|
|
795
|
+
boundTurnMap();
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Apply a `stop` — the only event that is the agent saying its turn is over.
|
|
799
|
+
*
|
|
800
|
+
* A `stop` for another session leaves everything alone; see {@link closesTurn}.
|
|
801
|
+
* A `stop` with no turn open still records one, because "this instance last
|
|
802
|
+
* reported it stopped at T" is the fact `ready` is published from, and the
|
|
803
|
+
* opening it never saw is published as null rather than guessed.
|
|
804
|
+
*/
|
|
805
|
+
function applyStopToTurn(key, record, generation) {
|
|
806
|
+
const turn = fencedTurn(key);
|
|
807
|
+
if (turn !== null && turn.closedAt === null && turn.openedAt !== null) {
|
|
808
|
+
if (!(0, provisional_turn_1.closesTurn)(turn, record.sessionId))
|
|
809
|
+
return;
|
|
810
|
+
turn.closedAt = record.at;
|
|
811
|
+
turn.closedBy = 'stop';
|
|
812
|
+
turn.displayEvent = displayOf(record);
|
|
813
|
+
releaseAllDecisions(key);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
agentTurns.set(key, {
|
|
817
|
+
turnId: nextTurnId(record.at),
|
|
818
|
+
sessionId: record.sessionId,
|
|
819
|
+
openedAt: null,
|
|
820
|
+
closedAt: record.at,
|
|
821
|
+
closedBy: 'stop',
|
|
822
|
+
generationAt: generation,
|
|
823
|
+
displayEvent: displayOf(record),
|
|
824
|
+
pendingDecisions: [],
|
|
825
|
+
scraperCompletionPolls: 0,
|
|
826
|
+
});
|
|
827
|
+
boundTurnMap();
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* `session_end`: the agent session this instance was talking to is gone.
|
|
831
|
+
*
|
|
832
|
+
* **Overwrites an existing close reason**, which is the one place in this
|
|
833
|
+
* module where a later event rewrites an earlier verdict, and #1723's contract
|
|
834
|
+
* is why. `/clear` emits `SessionEnd(reason=clear)` on a session that is alive
|
|
835
|
+
* and about to keep going, and the turn before it may perfectly well have ended
|
|
836
|
+
* with a `Stop`. Leaving that `Stop` standing would keep publishing "the agent
|
|
837
|
+
* finished" about a conversation that no longer exists — the integration pin is
|
|
838
|
+
* `current-output-structured-status-1723`, which asserts the session goes back
|
|
839
|
+
* to the scraper the moment `SessionEnd` lands.
|
|
840
|
+
*
|
|
841
|
+
* `closedBy: 'session_end'` publishes no verdict of its own (see
|
|
842
|
+
* {@link getStructuredSessionState}), so this is a retirement rather than a
|
|
843
|
+
* different answer.
|
|
844
|
+
*/
|
|
845
|
+
function applySessionEndToTurn(key, record) {
|
|
846
|
+
const turn = fencedTurn(key);
|
|
847
|
+
if (turn === null)
|
|
848
|
+
return;
|
|
849
|
+
if (!(0, provisional_turn_1.closesTurn)(turn, record.sessionId))
|
|
850
|
+
return;
|
|
851
|
+
turn.closedAt = record.at;
|
|
852
|
+
turn.closedBy = 'session_end';
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* The notification rows of the transition table.
|
|
856
|
+
*
|
|
857
|
+
* Matched on `notification_type` ({@link AgentEventRecord.detail}), never on the
|
|
858
|
+
* human-facing `message` (D3): the observed messages are English prose the agent
|
|
859
|
+
* is free to reword.
|
|
860
|
+
*/
|
|
861
|
+
function applyNotificationToTurn(key, record, generation) {
|
|
862
|
+
if (record.detail === 'permission_prompt') {
|
|
863
|
+
if (record.promptSettled === true) {
|
|
864
|
+
// Issue #1898: adjudicated before it was recorded, and the verdict
|
|
865
|
+
// reached the agent. There is no dialog and there never was one for a
|
|
866
|
+
// human to answer — opening a record for it published `waiting` for the
|
|
867
|
+
// whole of the tool call that followed (measured at eight seconds).
|
|
868
|
+
releaseSettledDecision(key, record);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
openDecision(key, generation, {
|
|
872
|
+
source: 'notification',
|
|
873
|
+
at: record.at,
|
|
874
|
+
message: record.message ?? null,
|
|
875
|
+
toolName: null,
|
|
876
|
+
decisionId: record.decisionId ?? null,
|
|
877
|
+
bootstrapDisplay: displayOf(record),
|
|
878
|
+
});
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
if (record.detail === agent_event_types_1.PERMISSION_REPLIED_DETAIL) {
|
|
882
|
+
// Issue #1898. The agent's own statement that the dialog is gone — whoever
|
|
883
|
+
// answered it. Not a word this build reads a status from, so it decides
|
|
884
|
+
// nothing; all it does is retire the record.
|
|
885
|
+
if (record.promptSettled === true)
|
|
886
|
+
releaseSettledDecision(key, record);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
if (record.detail === 'idle_prompt') {
|
|
890
|
+
// The agent reporting it is sitting at the composer waiting for input is the
|
|
891
|
+
// agent saying nothing is in front of that composer.
|
|
892
|
+
releaseAllDecisions(key);
|
|
893
|
+
const turn = fencedTurn(key);
|
|
894
|
+
if (turn !== null && turn.closedAt === null) {
|
|
895
|
+
turn.displayEvent = displayOf(record);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
if (turn !== null)
|
|
899
|
+
return;
|
|
900
|
+
// Nothing to attach it to — the first thing this instance ever said, or the
|
|
901
|
+
// first since a generation. It still carries a verdict (`ready`), and #1723
|
|
902
|
+
// publishes it, so a display-only record is opened to hold it. `openedAt`
|
|
903
|
+
// stays null: an agent sitting at its composer is not in a turn, and a
|
|
904
|
+
// record that claimed otherwise would gate `wait` on a turn nobody opened.
|
|
905
|
+
agentTurns.set(key, {
|
|
906
|
+
turnId: nextTurnId(record.at),
|
|
907
|
+
sessionId: record.sessionId,
|
|
908
|
+
openedAt: null,
|
|
909
|
+
closedAt: null,
|
|
910
|
+
closedBy: null,
|
|
911
|
+
generationAt: generation,
|
|
912
|
+
displayEvent: displayOf(record),
|
|
913
|
+
pendingDecisions: [],
|
|
914
|
+
scraperCompletionPolls: 0,
|
|
915
|
+
});
|
|
916
|
+
boundTurnMap();
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
// An unrecognised notification type is not evidence of anything, and guessing
|
|
920
|
+
// would be worse than the scraper.
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Open, or refresh, a dialog record on this instance's turn.
|
|
924
|
+
*
|
|
925
|
+
* `bootstrapDisplay` is used only when there is no record at all to hang the
|
|
926
|
+
* decision on. That record is deliberately **not** an open turn — its
|
|
927
|
+
* `openedAt` is null — so releasing the decision hands the pane back to the
|
|
928
|
+
* scraper rather than asserting `running` for a turn nobody ever saw open.
|
|
929
|
+
*/
|
|
930
|
+
function openDecision(key, generation, input) {
|
|
931
|
+
let turn = fencedTurn(key);
|
|
932
|
+
if (turn === null) {
|
|
933
|
+
turn = {
|
|
934
|
+
turnId: nextTurnId(input.at),
|
|
935
|
+
sessionId: null,
|
|
936
|
+
openedAt: null,
|
|
937
|
+
closedAt: null,
|
|
938
|
+
closedBy: null,
|
|
939
|
+
generationAt: generation,
|
|
940
|
+
displayEvent: input.bootstrapDisplay,
|
|
941
|
+
pendingDecisions: [],
|
|
942
|
+
scraperCompletionPolls: 0,
|
|
943
|
+
};
|
|
944
|
+
agentTurns.set(key, turn);
|
|
945
|
+
boundTurnMap();
|
|
946
|
+
}
|
|
947
|
+
else if (turn.closedAt !== null) {
|
|
948
|
+
// The dialog is being raised after the turn it would have belonged to
|
|
949
|
+
// ended — a `PermissionRequest` racing a `Stop`, or a re-check landing
|
|
950
|
+
// late. Re-open a dialog-only record rather than resurrecting the turn.
|
|
951
|
+
turn = {
|
|
952
|
+
turnId: nextTurnId(input.at),
|
|
953
|
+
sessionId: null,
|
|
954
|
+
openedAt: null,
|
|
955
|
+
closedAt: null,
|
|
956
|
+
closedBy: null,
|
|
957
|
+
generationAt: generation,
|
|
958
|
+
displayEvent: input.bootstrapDisplay,
|
|
959
|
+
pendingDecisions: [],
|
|
960
|
+
scraperCompletionPolls: 0,
|
|
961
|
+
};
|
|
962
|
+
agentTurns.set(key, turn);
|
|
963
|
+
boundTurnMap();
|
|
964
|
+
}
|
|
965
|
+
// Issue #1930 / S1: an id that fails validation is DISCARDED, not truncated —
|
|
966
|
+
// a truncated id compares equal to a different id sharing its prefix, and the
|
|
967
|
+
// reply to one approval would then retire another's record.
|
|
968
|
+
const decisionId = input.decisionId === null ? null : (0, provisional_turn_1.acceptExternalId)(input.decisionId);
|
|
969
|
+
if (input.decisionId !== null && decisionId === null)
|
|
970
|
+
dropsFor(key).idsDiscarded += 1;
|
|
971
|
+
const confirmed = input.source === 'notification';
|
|
972
|
+
// Which record, if any, this report is a second sighting of.
|
|
973
|
+
//
|
|
974
|
+
// - an id matches its own record;
|
|
975
|
+
// - an id with no record of its own may still be *confirming* the anonymous
|
|
976
|
+
// prediction that forecast it. That is the measured pair: a
|
|
977
|
+
// `PermissionRequest` this server declined to decide fires ~6 s before the
|
|
978
|
+
// `Notification(permission_prompt)` that proves the dialog, and the
|
|
979
|
+
// request carries no id;
|
|
980
|
+
// - an anonymous report merges only with another anonymous one. A source
|
|
981
|
+
// that publishes no ids therefore keeps exactly one dialog record per
|
|
982
|
+
// instance, which is what #1725 did and the honest limit for it — matching
|
|
983
|
+
// an anonymous report against an *identified* record would let one
|
|
984
|
+
// approval's forecast confirm a different approval's dialog.
|
|
985
|
+
const existing = decisionId !== null
|
|
986
|
+
? (turn.pendingDecisions.find((decision) => decision.decisionId === decisionId) ??
|
|
987
|
+
turn.pendingDecisions.find((decision) => decision.decisionId === null && decision.source === 'permission-request'))
|
|
988
|
+
: turn.pendingDecisions.find((decision) => decision.decisionId === null);
|
|
989
|
+
if (existing) {
|
|
990
|
+
// The same dialog, reported twice: `PermissionRequest` predicted it and the
|
|
991
|
+
// `Notification` then proved it. Keep the earliest `at` — that is when the
|
|
992
|
+
// human was first blocked, and it is what the scraper-release grace and the
|
|
993
|
+
// age bound are measured from — and take the confirmation.
|
|
994
|
+
existing.message = (0, provisional_turn_1.boundDecisionMessage)(input.message) ?? existing.message;
|
|
995
|
+
existing.toolName = (0, provisional_turn_1.boundDecisionToolName)(input.toolName) ?? existing.toolName;
|
|
996
|
+
existing.decisionId ??= decisionId;
|
|
997
|
+
if (confirmed) {
|
|
998
|
+
existing.source = 'notification';
|
|
999
|
+
existing.confirmedAt ??= input.at;
|
|
1000
|
+
}
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (turn.pendingDecisions.length >= provisional_turn_1.MAX_PENDING_DECISIONS_PER_TURN) {
|
|
1004
|
+
// S14(d). Counted rather than dropped in silence: an agent raising more
|
|
1005
|
+
// approvals than this in one turn is a fact an operator has to be able to
|
|
1006
|
+
// see, and the oldest is the one a human has been looking at.
|
|
1007
|
+
dropsFor(key).decisionOverflow += 1;
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
turn.pendingDecisions.push({
|
|
1011
|
+
decisionId,
|
|
1012
|
+
at: input.at,
|
|
1013
|
+
source: input.source,
|
|
1014
|
+
message: (0, provisional_turn_1.boundDecisionMessage)(input.message),
|
|
1015
|
+
toolName: (0, provisional_turn_1.boundDecisionToolName)(input.toolName),
|
|
1016
|
+
confirmedAt: confirmed ? input.at : null,
|
|
1017
|
+
scraperCorroborated: false,
|
|
1018
|
+
recorded: false,
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
/** Retire every dialog this instance was holding. */
|
|
1022
|
+
function releaseAllDecisions(key) {
|
|
1023
|
+
const turn = fencedTurn(key);
|
|
1024
|
+
if (turn !== null)
|
|
1025
|
+
turn.pendingDecisions = [];
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Retire the record a verdict was delivered for (Issue #1898).
|
|
1029
|
+
*
|
|
1030
|
+
* Matched on {@link AgentEventRecord.decisionId} when both sides have one: an
|
|
1031
|
+
* agent that can run two approvals at once would otherwise have the reply to
|
|
1032
|
+
* the first retire the record for the second. When either side is anonymous the
|
|
1033
|
+
* release is unconditional — an unmatched id is a source that publishes none,
|
|
1034
|
+
* and refusing to act there would leave the pre-#1898 stall in place for it.
|
|
1035
|
+
*/
|
|
1036
|
+
function releaseSettledDecision(key, record) {
|
|
1037
|
+
const turn = fencedTurn(key);
|
|
1038
|
+
if (turn === null || turn.pendingDecisions.length === 0)
|
|
1039
|
+
return;
|
|
1040
|
+
const settledId = record.decisionId === null || record.decisionId === undefined
|
|
1041
|
+
? null
|
|
1042
|
+
: (0, provisional_turn_1.acceptExternalId)(record.decisionId);
|
|
1043
|
+
if (settledId === null) {
|
|
1044
|
+
turn.pendingDecisions = [];
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
turn.pendingDecisions = turn.pendingDecisions.filter((decision) => decision.decisionId !== null && decision.decisionId !== settledId);
|
|
1048
|
+
}
|
|
1049
|
+
/** Keep the turn map bounded the way every other map in this module is. */
|
|
1050
|
+
function boundTurnMap() {
|
|
1051
|
+
trimOldestEntries(agentTurns, MAX_RECENT_EVENT_KEYS);
|
|
1052
|
+
}
|
|
359
1053
|
/**
|
|
360
1054
|
* @returns The last structured event reported by this instance, or null when it
|
|
361
1055
|
* has reported none.
|
|
@@ -456,22 +1150,15 @@ function getLastKnownAgentEffort(worktreeId, cliToolId, instanceId) {
|
|
|
456
1150
|
}
|
|
457
1151
|
/**
|
|
458
1152
|
* How long a structured verdict is trusted after the event that produced it
|
|
459
|
-
* (Issue #1723).
|
|
1153
|
+
* (Issue #1723; one expression since #1930).
|
|
460
1154
|
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
* called done. The bound converts "forever" into "at most this long", after
|
|
467
|
-
* which the scraper takes the session back.
|
|
468
|
-
*
|
|
469
|
-
* Deliberately generous. A single agent turn running past 30 minutes is
|
|
470
|
-
* ordinary for the workloads this tool exists to babysit, and expiring a live
|
|
471
|
-
* verdict mid-turn costs the whole benefit of the two-layer split. Expiry is
|
|
472
|
-
* not a failure mode: it is exactly the pre-#1723 behaviour.
|
|
1155
|
+
* The turn model owns the number now — see `provisional-turn`'s
|
|
1156
|
+
* {@link TURN_STALE_AFTER_MS}, which documents why it is 30 minutes. This name
|
|
1157
|
+
* is kept because it is what `status-evidence` and the #1723/#1725/#1903 suites
|
|
1158
|
+
* read, and because "the age bound on a verdict" and "how long a turn may run
|
|
1159
|
+
* unheard-from" really are the same fact rather than two that happen to agree.
|
|
473
1160
|
*/
|
|
474
|
-
exports.STRUCTURED_STATE_MAX_AGE_MS =
|
|
1161
|
+
exports.STRUCTURED_STATE_MAX_AGE_MS = provisional_turn_1.TURN_STALE_AFTER_MS;
|
|
475
1162
|
/**
|
|
476
1163
|
* Open a new generation for this instance, invalidating everything reported
|
|
477
1164
|
* before now (Issue #1723).
|
|
@@ -492,11 +1179,13 @@ exports.STRUCTURED_STATE_MAX_AGE_MS = 30 * 60 * 1000;
|
|
|
492
1179
|
function beginAgentEventGeneration(worktreeId, cliToolId, instanceId, at = Date.now()) {
|
|
493
1180
|
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
494
1181
|
generationStartedAt.set(key, at);
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
|
|
1182
|
+
// Issue #1930: the turn the previous process was in ends with that process,
|
|
1183
|
+
// and the approvals it was holding go with it — they were raised against a
|
|
1184
|
+
// slot nobody is holding open any more. The turn record is kept, closed as
|
|
1185
|
+
// `closedBy: 'generation'`, so `capture --json` can say why it ended instead
|
|
1186
|
+
// of going quiet. The dialogs #1725 kept in a map of their own live on that
|
|
1187
|
+
// record now, which is what makes this one call retire both.
|
|
1188
|
+
fenceTurnForNewGeneration(key, at);
|
|
500
1189
|
// Same reasoning for the question that dialog was asking (Issue #1726).
|
|
501
1190
|
askUserQuestion.delete(key);
|
|
502
1191
|
// And for "waiting for your input" (Issue #1786): a new process has not asked
|
|
@@ -514,6 +1203,10 @@ function beginAgentEventGeneration(worktreeId, cliToolId, instanceId, at = Date.
|
|
|
514
1203
|
// a relaunch would show the old process's effort with no frame left that
|
|
515
1204
|
// could contradict it. The next poll re-reads a live footer immediately.
|
|
516
1205
|
capturedModelInfo.delete(key);
|
|
1206
|
+
// Issue #1899: the ids claimed for this key were issued by the process that
|
|
1207
|
+
// has just been replaced. Unlike the time-window keys, they never expire on
|
|
1208
|
+
// their own, so a generation is the only thing that retires them.
|
|
1209
|
+
recentEventIdentities.delete(key);
|
|
517
1210
|
}
|
|
518
1211
|
/**
|
|
519
1212
|
* @returns Epoch ms the current generation began, or null when no generation
|
|
@@ -536,143 +1229,153 @@ function discardAgentEventState(worktreeId, cliToolId, instanceId) {
|
|
|
536
1229
|
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
537
1230
|
lastAgentEvent.delete(key);
|
|
538
1231
|
generationStartedAt.delete(key);
|
|
539
|
-
|
|
1232
|
+
// Issue #1930: the turn, and with it the dialogs it was holding. Not counted
|
|
1233
|
+
// as an eviction — the session is gone, so there is nobody left to tell.
|
|
1234
|
+
agentTurns.delete(key);
|
|
1235
|
+
dropCounts.delete(key);
|
|
540
1236
|
askUserQuestion.delete(key);
|
|
541
1237
|
awaitingInstruction.delete(key);
|
|
542
1238
|
// Issue #1783: the session that was on this model no longer exists.
|
|
543
1239
|
lastAgentModel.delete(key);
|
|
544
1240
|
// Issue #1784: nor does the pane its footer was read from.
|
|
545
1241
|
capturedModelInfo.delete(key);
|
|
1242
|
+
// Issue #1899: nor do the frame ids that session issued.
|
|
1243
|
+
recentEventIdentities.delete(key);
|
|
546
1244
|
}
|
|
547
1245
|
/**
|
|
548
|
-
* The status this instance's
|
|
549
|
-
*
|
|
1246
|
+
* The status this instance's turn implies, or null when it implies nothing
|
|
1247
|
+
* (Issue #1723, re-derived from the turn model in #1930).
|
|
550
1248
|
*
|
|
551
1249
|
* Null is the answer for every session on a machine where hooks never fire,
|
|
552
1250
|
* which is what keeps the unconfigured environment on exactly the behaviour it
|
|
553
|
-
* had before
|
|
554
|
-
*
|
|
555
|
-
* - the
|
|
556
|
-
*
|
|
557
|
-
* -
|
|
558
|
-
*
|
|
559
|
-
*
|
|
1251
|
+
* had before #1723. It is also the answer when:
|
|
1252
|
+
*
|
|
1253
|
+
* - the turn belongs to a previous generation, i.e. to an agent process that
|
|
1254
|
+
* used to live in this pane;
|
|
1255
|
+
* - nothing has been heard about it for {@link STRUCTURED_STATE_MAX_AGE_MS};
|
|
1256
|
+
* - the turn ended for a reason that says nothing about whether the pane is
|
|
1257
|
+
* free — `session_end` (a `/clear` mid-turn), `stale`, `generation`, or the
|
|
1258
|
+
* two the *screen* closed it on (`scraper_evidence`, `resync_idle`). Only
|
|
1259
|
+
* the agent's own `Stop` publishes `ready` over this channel; everything
|
|
1260
|
+
* else hands the pane back to the scraper, which is the layer those closures
|
|
1261
|
+
* came from in the first place;
|
|
1262
|
+
* - no decision stands and the displayed event carries no verdict.
|
|
1263
|
+
*
|
|
1264
|
+
* ## The derivation, in the order it is applied
|
|
1265
|
+
*
|
|
1266
|
+
* | condition | verdict |
|
|
1267
|
+
* |---------------------------------------------|-------------------------------|
|
|
1268
|
+
* | an unanswered decision is live | `waiting` |
|
|
1269
|
+
* | the turn was closed by `stop` | `ready` / `hook_stop` |
|
|
1270
|
+
* | the turn was closed by anything else | null |
|
|
1271
|
+
* | the displayed event carries a verdict | that verdict |
|
|
1272
|
+
* | a turn is open and it does not | `running` / `hook_prompt_submit` |
|
|
1273
|
+
* | otherwise | null |
|
|
1274
|
+
*
|
|
1275
|
+
* §4 D3's `running ⟺ open turn ∧ 未裁定なし ∧ 現世代` falls out of the first,
|
|
1276
|
+
* fourth and fifth rows together with the generation fence — with one measured
|
|
1277
|
+
* exception written into the table above it: `notification(idle_prompt)`
|
|
1278
|
+
* publishes `ready` **without** closing the turn (#1839 caught Claude emitting
|
|
1279
|
+
* it 62 s into a turn that ran nothing, so it cannot be a boundary). A reader
|
|
1280
|
+
* that needs the boundary rather than the display reads `closedAt`.
|
|
560
1281
|
*
|
|
561
1282
|
* Whether the tmux session is alive is NOT checked here — the caller
|
|
562
|
-
* (`buildCurrentOutput`) has already answered that
|
|
563
|
-
*
|
|
564
|
-
*
|
|
1283
|
+
* (`buildCurrentOutput`) has already answered that with the CLI tool's own
|
|
1284
|
+
* `isRunning()` and returned early, and asking twice would mean a second tmux
|
|
1285
|
+
* round-trip on the hot path for an answer it is holding.
|
|
565
1286
|
*
|
|
566
1287
|
* @param now - Epoch ms; defaults to now
|
|
567
1288
|
*/
|
|
568
1289
|
function getStructuredSessionState(worktreeId, cliToolId, instanceId, now = Date.now()) {
|
|
569
1290
|
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
570
|
-
const
|
|
571
|
-
if (
|
|
1291
|
+
const turn = effectiveTurn(key, now);
|
|
1292
|
+
if (turn === null)
|
|
572
1293
|
return null;
|
|
573
|
-
const
|
|
574
|
-
if (
|
|
1294
|
+
const display = turn.displayEvent;
|
|
1295
|
+
if (now - display.at >= exports.STRUCTURED_STATE_MAX_AGE_MS)
|
|
575
1296
|
return null;
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
1297
|
+
const seen = { event: display.event, at: display.at, detail: display.detail };
|
|
1298
|
+
const pending = livePendingDecisions(key, turn, now);
|
|
1299
|
+
if (pending.length > 0) {
|
|
1300
|
+
// The two kinds of evidence stay apart in the reason token, exactly as
|
|
1301
|
+
// `structuredWaitingReason` keeps them apart for the payload: a
|
|
1302
|
+
// `Notification` is proof a dialog exists, a `PermissionRequest` this
|
|
1303
|
+
// server declined to decide is the prediction that one is about to.
|
|
1304
|
+
const reason = pending[0].source === 'notification'
|
|
1305
|
+
? status_mapping_1.HOOK_STATUS_REASON.PERMISSION_PROMPT
|
|
1306
|
+
: status_mapping_1.HOOK_STATUS_REASON.PERMISSION_REQUEST;
|
|
1307
|
+
return { status: 'waiting', reason, ...seen };
|
|
1308
|
+
}
|
|
1309
|
+
if (turn.closedAt !== null) {
|
|
1310
|
+
if (turn.closedBy === 'stop') {
|
|
1311
|
+
return { status: 'ready', reason: status_mapping_1.HOOK_STATUS_REASON.STOP, ...seen };
|
|
1312
|
+
}
|
|
580
1313
|
return null;
|
|
581
|
-
|
|
1314
|
+
}
|
|
1315
|
+
const verdict = (0, status_mapping_1.agentEventToSessionStatus)(display.event, display.detail);
|
|
1316
|
+
// A `waiting` read off the displayed event is a dialog nothing is holding any
|
|
1317
|
+
// more — the ledger above is what answers that question now. The turn is
|
|
1318
|
+
// still open, so the agent is still working.
|
|
1319
|
+
if (verdict === null || verdict.status === 'waiting') {
|
|
1320
|
+
if (turn.openedAt === null)
|
|
1321
|
+
return null;
|
|
1322
|
+
return { status: 'running', reason: status_mapping_1.HOOK_STATUS_REASON.PROMPT_SUBMIT, ...seen };
|
|
1323
|
+
}
|
|
1324
|
+
return { ...verdict, ...seen };
|
|
582
1325
|
}
|
|
583
1326
|
/**
|
|
584
1327
|
* How long a `permission-request`-sourced record survives without
|
|
585
|
-
* corroboration (Issue #1725).
|
|
586
|
-
*
|
|
587
|
-
* The
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
*
|
|
594
|
-
* A prediction that nothing confirms must expire, because the cost of it
|
|
595
|
-
* sticking is a `wait --on-prompt agent` that exits 10 on a session with no
|
|
596
|
-
* dialog in it — a worker aborted for nothing. 20 s is generous against both
|
|
597
|
-
* confirmations: the `Notification` lands at ~6 s, and the scraper reads the
|
|
598
|
-
* pane through a 5 s capture cache on a 5 s poll. Expiring is not a failure
|
|
599
|
-
* mode; it is the pre-#1725 behaviour for a dialog neither layer ever saw.
|
|
600
|
-
*/
|
|
601
|
-
exports.STRUCTURED_PROMPT_PROVISIONAL_MAX_AGE_MS = 20_000;
|
|
602
|
-
/** Open, or refresh, the prompt-waiting record for one instance. */
|
|
603
|
-
function openPromptWaiting(key, input) {
|
|
604
|
-
const existing = promptWaiting.get(key);
|
|
605
|
-
const confirmed = input.source === 'notification';
|
|
606
|
-
if (existing) {
|
|
607
|
-
// The same dialog, reported twice: `PermissionRequest` predicted it and the
|
|
608
|
-
// `Notification` then proved it. Keep the earliest `at` — that is when the
|
|
609
|
-
// human was first blocked, and it is what the scraper-release grace and the
|
|
610
|
-
// age bound are measured from — and take the confirmation.
|
|
611
|
-
existing.message = input.message ?? existing.message;
|
|
612
|
-
existing.toolName = input.toolName ?? existing.toolName;
|
|
613
|
-
if (confirmed) {
|
|
614
|
-
existing.source = 'notification';
|
|
615
|
-
existing.confirmedAt ??= input.at;
|
|
616
|
-
}
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
promptWaiting.set(key, {
|
|
620
|
-
at: input.at,
|
|
621
|
-
source: input.source,
|
|
622
|
-
message: input.message
|
|
623
|
-
? input.message.slice(0, structured_prompt_1.MAX_STRUCTURED_PROMPT_MESSAGE_LENGTH)
|
|
624
|
-
: null,
|
|
625
|
-
toolName: input.toolName ?? null,
|
|
626
|
-
confirmedAt: confirmed ? input.at : null,
|
|
627
|
-
scraperCorroborated: false,
|
|
628
|
-
recorded: false,
|
|
629
|
-
});
|
|
630
|
-
}
|
|
1328
|
+
* corroboration (Issue #1725; one expression since #1930).
|
|
1329
|
+
*
|
|
1330
|
+
* The turn model owns the number — see `provisional-turn`'s
|
|
1331
|
+
* {@link DIALOG_PENDING_MAX_MS}, whose doc has the measurement. Kept under this
|
|
1332
|
+
* name for `permission-decision-service`, which cites it, and for the #1725
|
|
1333
|
+
* suite.
|
|
1334
|
+
*/
|
|
1335
|
+
exports.STRUCTURED_PROMPT_PROVISIONAL_MAX_AGE_MS = provisional_turn_1.DIALOG_PENDING_MAX_MS.predicted;
|
|
631
1336
|
/**
|
|
632
1337
|
* Report that a dialog is open because the agent asked us to adjudicate one and
|
|
633
1338
|
* we declined to (Issue #1725, Auto-Yes v2's no-decision path).
|
|
634
1339
|
*
|
|
635
1340
|
* Provisional: see {@link STRUCTURED_PROMPT_PROVISIONAL_MAX_AGE_MS}.
|
|
636
1341
|
*
|
|
1342
|
+
* Called by `permission-decision-service` and by opencode's ingest, both of
|
|
1343
|
+
* which have already applied the `permissionHookPredictsDialog` capability gate
|
|
1344
|
+
* (#1901) — this function is told, it does not decide.
|
|
1345
|
+
*
|
|
637
1346
|
* @param at - Epoch ms; defaults to now
|
|
638
1347
|
*/
|
|
639
1348
|
function reportPermissionRequestPending(worktreeId, cliToolId, instanceId, toolName, at = Date.now()) {
|
|
640
|
-
|
|
1349
|
+
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
1350
|
+
openDecision(key, currentGeneration(key), {
|
|
641
1351
|
source: 'permission-request',
|
|
1352
|
+
at,
|
|
642
1353
|
message: null,
|
|
643
1354
|
toolName,
|
|
644
|
-
|
|
1355
|
+
decisionId: null,
|
|
1356
|
+
// A forecast carries no event of its own, so the record it bootstraps is
|
|
1357
|
+
// described by the thing it is forecasting. `structuredEvents.lastEventType`
|
|
1358
|
+
// is unaffected — that reads `getLastAgentEvent`, which nothing here writes.
|
|
1359
|
+
bootstrapDisplay: { event: 'notification', at, detail: 'permission_prompt' },
|
|
645
1360
|
});
|
|
646
1361
|
}
|
|
647
1362
|
/**
|
|
648
1363
|
* The open dialog this instance's structured events imply, or null.
|
|
649
1364
|
*
|
|
650
1365
|
* Bounded exactly like {@link getStructuredSessionState}: a record from a
|
|
651
|
-
* previous generation is not this session's, and one
|
|
652
|
-
*
|
|
653
|
-
*
|
|
654
|
-
* {@link STRUCTURED_PROMPT_PROVISIONAL_MAX_AGE_MS}.
|
|
1366
|
+
* previous generation is not this session's, and one past its retention bound
|
|
1367
|
+
* has outlived the fact it describes. An unconfirmed `permission-request`
|
|
1368
|
+
* record expires far sooner — see {@link DIALOG_PENDING_MAX_MS}.
|
|
655
1369
|
*
|
|
656
|
-
* The
|
|
657
|
-
*
|
|
1370
|
+
* The oldest live decision, because that is the one a human has been looking
|
|
1371
|
+
* at, and because it is the record #1725 published when there could only ever
|
|
1372
|
+
* be one. The returned object is the live record, not a copy:
|
|
1373
|
+
* `prompt-waiting-composition` marks corroboration and the history write on it.
|
|
658
1374
|
*
|
|
659
1375
|
* @param now - Epoch ms; defaults to now
|
|
660
1376
|
*/
|
|
661
1377
|
function getStructuredPromptWaiting(worktreeId, cliToolId, instanceId, now = Date.now()) {
|
|
662
|
-
|
|
663
|
-
const state = promptWaiting.get(key);
|
|
664
|
-
if (!state)
|
|
665
|
-
return null;
|
|
666
|
-
const generation = generationStartedAt.get(key);
|
|
667
|
-
if (generation !== undefined && state.at < generation)
|
|
668
|
-
return null;
|
|
669
|
-
if (now - state.at >= exports.STRUCTURED_STATE_MAX_AGE_MS)
|
|
670
|
-
return null;
|
|
671
|
-
if (state.confirmedAt === null &&
|
|
672
|
-
now - state.at >= exports.STRUCTURED_PROMPT_PROVISIONAL_MAX_AGE_MS) {
|
|
673
|
-
return null;
|
|
674
|
-
}
|
|
675
|
-
return state;
|
|
1378
|
+
return getPendingDecisions(worktreeId, cliToolId, instanceId, now)[0] ?? null;
|
|
676
1379
|
}
|
|
677
1380
|
/**
|
|
678
1381
|
* Record that the scraper has seen a blocking frame while this dialog is open
|
|
@@ -680,12 +1383,12 @@ function getStructuredPromptWaiting(worktreeId, cliToolId, instanceId, now = Dat
|
|
|
680
1383
|
*
|
|
681
1384
|
* Two effects, both needed: it confirms a provisional record, and it arms the
|
|
682
1385
|
* only release rule the scraper is entitled to apply. See
|
|
683
|
-
* {@link
|
|
1386
|
+
* {@link StructuredPendingDecision.scraperCorroborated}.
|
|
684
1387
|
*
|
|
685
1388
|
* @param at - Epoch ms; defaults to now
|
|
686
1389
|
*/
|
|
687
1390
|
function corroborateStructuredPromptWaiting(worktreeId, cliToolId, instanceId, at = Date.now()) {
|
|
688
|
-
const state =
|
|
1391
|
+
const state = getStructuredPromptWaiting(worktreeId, cliToolId, instanceId, at);
|
|
689
1392
|
if (!state)
|
|
690
1393
|
return;
|
|
691
1394
|
state.scraperCorroborated = true;
|
|
@@ -693,18 +1396,23 @@ function corroborateStructuredPromptWaiting(worktreeId, cliToolId, instanceId, a
|
|
|
693
1396
|
}
|
|
694
1397
|
/** Note that this episode's prompt-history row has been written. */
|
|
695
1398
|
function markStructuredPromptRecorded(worktreeId, cliToolId, instanceId) {
|
|
696
|
-
const state =
|
|
1399
|
+
const state = getStructuredPromptWaiting(worktreeId, cliToolId, instanceId);
|
|
697
1400
|
if (state)
|
|
698
1401
|
state.recorded = true;
|
|
699
1402
|
}
|
|
700
1403
|
/**
|
|
701
1404
|
* Release the prompt-waiting record — the dialog is gone (Issue #1725).
|
|
702
1405
|
*
|
|
703
|
-
* Called from the
|
|
1406
|
+
* Called from the turn transition above and from `prompt-waiting-composition`
|
|
704
1407
|
* when the scraper reports that the frame it corroborated has cleared.
|
|
1408
|
+
*
|
|
1409
|
+
* Releases every decision this instance is holding, not only the first. The
|
|
1410
|
+
* scraper's statement is about the *pane*, and a pane with no dialog on it has
|
|
1411
|
+
* no dialogs on it — retiring one and leaving the rest would publish `waiting`
|
|
1412
|
+
* for a frame the scraper has just said is clear.
|
|
705
1413
|
*/
|
|
706
1414
|
function clearStructuredPromptWaiting(worktreeId, cliToolId, instanceId) {
|
|
707
|
-
|
|
1415
|
+
releaseAllDecisions((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId));
|
|
708
1416
|
}
|
|
709
1417
|
/**
|
|
710
1418
|
* When the in-flight question is released (Issue #1726).
|
|
@@ -889,13 +1597,154 @@ function pruneRecentEventKeys(now) {
|
|
|
889
1597
|
function getRecentEventKeyCount() {
|
|
890
1598
|
return recentEventKeys.size;
|
|
891
1599
|
}
|
|
1600
|
+
// =============================================================================
|
|
1601
|
+
// Identity de-duplication (Issue #1899)
|
|
1602
|
+
// =============================================================================
|
|
1603
|
+
/**
|
|
1604
|
+
* Event words whose repeat is a fact about the session, not a re-delivery
|
|
1605
|
+
* (Issue #1899; design §4 D3 decision 2).
|
|
1606
|
+
*
|
|
1607
|
+
* These are the two words that end something, and neither of them carries an
|
|
1608
|
+
* id on any source measured so far. `session.idle` — opencode's `stop` — is
|
|
1609
|
+
* `{ "sessionID": "ses_…" }` and nothing else, which is exactly why
|
|
1610
|
+
* `sources/opencode/turn-gate` exists. With no id, a generic deduper has only
|
|
1611
|
+
* the clock, and the clock cannot tell a second turn from a second delivery:
|
|
1612
|
+
* #1899 measured a real `stop` 2.5 s after the previous one being dropped,
|
|
1613
|
+
* which leaves the newest event at `user_prompt_submit`, the instance reading
|
|
1614
|
+
* `running`, and `commandmate wait` blocked until the 30-minute staleness
|
|
1615
|
+
* bound.
|
|
1616
|
+
*
|
|
1617
|
+
* **The exemption is conditional on the source declaring an identity**, and
|
|
1618
|
+
* that condition is the whole safety argument. A source that declares one is a
|
|
1619
|
+
* source with a real deduper elsewhere — on the SSE path, `TurnGate`, which
|
|
1620
|
+
* arms on `session.status(busy)` and completes on the first `session.idle`
|
|
1621
|
+
* after arming, so the abort double-idle (19 ms apart, §5.3.2) never reaches
|
|
1622
|
+
* the ingest at all. Push hooks have no such gate: #1722's concatenated
|
|
1623
|
+
* settings really do post two `Stop`s for one turn, and
|
|
1624
|
+
* {@link isDuplicateAgentEvent} — which is what every hook receiver still
|
|
1625
|
+
* calls — is left untouched for them.
|
|
1626
|
+
*/
|
|
1627
|
+
exports.LIFECYCLE_AGENT_EVENT_TYPES = ['stop', 'session_end'];
|
|
1628
|
+
/**
|
|
1629
|
+
* Whether this delivery is a second copy of one already handled (Issue #1899).
|
|
1630
|
+
*
|
|
1631
|
+
* The tool-agnostic replacement for calling {@link isDuplicateAgentEvent}
|
|
1632
|
+
* directly, and it branches on the source's declared capability rather than on
|
|
1633
|
+
* its name — flip opencode's `eventIdentity` to `null` and every case below
|
|
1634
|
+
* falls back to the 3-second window it used before this Issue.
|
|
1635
|
+
*
|
|
1636
|
+
* Three rules, in order:
|
|
1637
|
+
*
|
|
1638
|
+
* 1. **The frame has an id** — key on it, with no time bound at all. An id is
|
|
1639
|
+
* a claim about identity that a clock cannot improve on: two approvals
|
|
1640
|
+
* 1 s apart are two approvals, and the same approval replayed by
|
|
1641
|
+
* `resyncPending` four minutes later is still one approval. That second
|
|
1642
|
+
* half is what the ingest's window was reaching for ("a frame delivered
|
|
1643
|
+
* twice by a re-sync racing the live stream") and never actually covered,
|
|
1644
|
+
* since a re-sync is not obliged to land within three seconds.
|
|
1645
|
+
* 2. **No id, but the word ends something** — never suppressed. See
|
|
1646
|
+
* {@link LIFECYCLE_AGENT_EVENT_TYPES}.
|
|
1647
|
+
* 3. **Anything else** — the time window, unchanged. That is every push
|
|
1648
|
+
* source, and the identity-declaring source's `session.created` /
|
|
1649
|
+
* `session.error`, which publish no id either but are not turn boundaries.
|
|
1650
|
+
*
|
|
1651
|
+
* Calling this *claims* the key, exactly as {@link isDuplicateAgentEvent} does:
|
|
1652
|
+
* ask once per delivery and act on the answer.
|
|
1653
|
+
*/
|
|
1654
|
+
function classifyAgentEventDelivery(delivery) {
|
|
1655
|
+
const composite = (0, auto_yes_state_1.buildCompositeKey)(delivery.worktreeId, delivery.cliToolId, delivery.instanceId);
|
|
1656
|
+
if (delivery.identityKind !== null) {
|
|
1657
|
+
// Issue #1930 / S1: the id is the agent's, and it becomes a Map key. One
|
|
1658
|
+
// that fails validation is DISCARDED — the delivery then takes the
|
|
1659
|
+
// no-id path below, which is a path this function already has — rather than
|
|
1660
|
+
// truncated, because a truncated id compares equal to a different id that
|
|
1661
|
+
// shares its prefix and would drop a real event as a repeat.
|
|
1662
|
+
const identity = delivery.identity === null ? null : (0, provisional_turn_1.acceptExternalId)(delivery.identity);
|
|
1663
|
+
if (delivery.identity !== null && identity === null)
|
|
1664
|
+
dropsFor(composite).idsDiscarded += 1;
|
|
1665
|
+
if (identity !== null) {
|
|
1666
|
+
if (claimEventIdentity(delivery, identity)) {
|
|
1667
|
+
dropsFor(composite).dedupDropped.identity += 1;
|
|
1668
|
+
return { duplicate: true, by: 'identity' };
|
|
1669
|
+
}
|
|
1670
|
+
return { duplicate: false };
|
|
1671
|
+
}
|
|
1672
|
+
if (exports.LIFECYCLE_AGENT_EVENT_TYPES.includes(delivery.event)) {
|
|
1673
|
+
return { duplicate: false };
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
const duplicate = isDuplicateAgentEvent(delivery.worktreeId, delivery.cliToolId, delivery.instanceId, delivery.event, delivery.sessionId, delivery.at, delivery.detail);
|
|
1677
|
+
if (!duplicate)
|
|
1678
|
+
return { duplicate: false };
|
|
1679
|
+
dropsFor(composite).dedupDropped.timeWindow += 1;
|
|
1680
|
+
return { duplicate: true, by: 'time-window' };
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Claim `(event, detail, identity)` for one instance, answering whether it was
|
|
1684
|
+
* already claimed.
|
|
1685
|
+
*
|
|
1686
|
+
* `event` and `detail` are in the key and are not decoration: opencode asks an
|
|
1687
|
+
* approval under `properties.id` and answers it under `properties.requestID`
|
|
1688
|
+
* with **the same `per_…` value** (#1898), so a key made of the identity alone
|
|
1689
|
+
* would read `permission.replied` as a repeat of `permission.asked` and drop
|
|
1690
|
+
* the one frame that releases the dialog. The same shape covers
|
|
1691
|
+
* `pre_tool_use` / `post_tool_use`, which share a `callID`.
|
|
1692
|
+
*
|
|
1693
|
+
* `sessionId` is deliberately *not* in the key. An id is unique within the
|
|
1694
|
+
* agent that issued it, and folding in a field that is null on some deliveries
|
|
1695
|
+
* would let the same frame through twice.
|
|
1696
|
+
*/
|
|
1697
|
+
function claimEventIdentity(delivery, identity) {
|
|
1698
|
+
const composite = (0, auto_yes_state_1.buildCompositeKey)(delivery.worktreeId, delivery.cliToolId, delivery.instanceId);
|
|
1699
|
+
let claimed = recentEventIdentities.get(composite);
|
|
1700
|
+
if (!claimed) {
|
|
1701
|
+
claimed = new Map();
|
|
1702
|
+
recentEventIdentities.set(composite, claimed);
|
|
1703
|
+
// Bound the instances as well as the entries per instance: the composite
|
|
1704
|
+
// key is (worktree, tool, instance) and a long-lived server accumulates
|
|
1705
|
+
// worktrees (DR4-009).
|
|
1706
|
+
trimOldestEntries(recentEventIdentities, MAX_RECENT_EVENT_KEYS);
|
|
1707
|
+
}
|
|
1708
|
+
const key = [delivery.event, delivery.detail ?? '', identity].join(' ');
|
|
1709
|
+
if (claimed.has(key))
|
|
1710
|
+
return true;
|
|
1711
|
+
claimed.set(key, delivery.at);
|
|
1712
|
+
trimOldestEntries(claimed, MAX_RECENT_EVENT_KEYS);
|
|
1713
|
+
return false;
|
|
1714
|
+
}
|
|
1715
|
+
/** Drop the oldest entries until the map fits. Maps iterate in insertion order. */
|
|
1716
|
+
function trimOldestEntries(entries, max) {
|
|
1717
|
+
while (entries.size > max) {
|
|
1718
|
+
const oldest = entries.keys().next();
|
|
1719
|
+
if (oldest.done)
|
|
1720
|
+
break;
|
|
1721
|
+
entries.delete(oldest.value);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* How many identities are retained for one instance, or across all of them when
|
|
1726
|
+
* no instance is named. Test seam for the bound above.
|
|
1727
|
+
*/
|
|
1728
|
+
function getRecentEventIdentityCount(worktreeId, cliToolId, instanceId) {
|
|
1729
|
+
if (worktreeId === undefined || cliToolId === undefined) {
|
|
1730
|
+
let total = 0;
|
|
1731
|
+
for (const claimed of recentEventIdentities.values())
|
|
1732
|
+
total += claimed.size;
|
|
1733
|
+
return total;
|
|
1734
|
+
}
|
|
1735
|
+
return recentEventIdentities.get((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId))?.size ?? 0;
|
|
1736
|
+
}
|
|
892
1737
|
/** Drop every recorded event. Test seam. */
|
|
893
1738
|
function clearAgentStopEvents() {
|
|
894
1739
|
lastStopEventAt.clear();
|
|
895
1740
|
lastAgentEvent.clear();
|
|
896
1741
|
recentEventKeys.clear();
|
|
1742
|
+
recentEventIdentities.clear();
|
|
897
1743
|
generationStartedAt.clear();
|
|
898
|
-
|
|
1744
|
+
// Issue #1930: the turns, the dialogs they hold, and the tally of what was
|
|
1745
|
+
// dropped from them.
|
|
1746
|
+
agentTurns.clear();
|
|
1747
|
+
dropCounts.clear();
|
|
899
1748
|
askUserQuestion.clear();
|
|
900
1749
|
awaitingInstruction.clear();
|
|
901
1750
|
// Issue #1783. CI runs with `fileParallelism: false`, so every suite in the
|