commandmate 0.46.0 → 0.46.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 +259 -259
- package/.next/app-path-routes-manifest.json +14 -14
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +9 -9
- package/.next/react-loadable-manifest.json +2 -2
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.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 +9 -9
- 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.nft.json +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.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.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route.js +9 -9
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -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.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +7 -7
- 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 +14 -14
- package/.next/server/chunks/1501.js +2 -2
- package/.next/server/chunks/1559.js +1 -1
- package/.next/server/chunks/2460.js +1 -1
- package/.next/server/chunks/2908.js +1 -1
- package/.next/server/chunks/2984.js +25 -0
- package/.next/server/chunks/5209.js +1 -1
- package/.next/server/chunks/5292.js +2 -2
- package/.next/server/chunks/5845.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6722.js +1 -1
- package/.next/server/chunks/8581.js +4 -4
- package/.next/server/chunks/8728.js +2 -2
- package/.next/server/chunks/8791.js +1 -1
- package/.next/server/chunks/8918.js +5 -5
- package/.next/server/chunks/9390.js +1 -1
- package/.next/server/chunks/9627.js +2 -1
- package/.next/server/chunks/9731.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{2407-acb644c5487e1720.js → 2407-9703c569370b8a9d.js} +1 -1
- package/.next/static/chunks/{2717.21abf5b6bbd45520.js → 2717.1583400c272dc1d9.js} +1 -1
- package/.next/static/chunks/5371-0dc947683e46e78c.js +1 -0
- package/.next/static/chunks/5794-4eee51ff1e878aa6.js +1 -0
- package/.next/static/chunks/{8407-9a72e6f07c5d5458.js → 8407-155f84e5ba05f1d5.js} +1 -1
- package/.next/static/chunks/app/{layout-ae30ce63569c22be.js → layout-3e357690446cd2da.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-effbf0796881dd5a.js +1 -0
- package/.next/static/chunks/{webpack-c0b821f340afdebf.js → webpack-468b7b33030234b3.js} +1 -1
- package/.next/trace +13 -13
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +6 -1
- package/dist/cli/types/api-responses.d.ts +8 -6
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts +13 -1
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -1
- package/dist/lib/polling/auto-yes-suppression-reason.js +3 -0
- package/dist/server/src/lib/assistant-response-saver.js +13 -0
- package/dist/server/src/lib/auto-yes-poller.js +252 -54
- package/dist/server/src/lib/conversation-logger.js +14 -2
- package/dist/server/src/lib/db/chat-db.js +2 -1
- package/dist/server/src/lib/history/user-turn-recorder.js +14 -0
- package/dist/server/src/lib/hooks/sources/antigravity/history.js +28 -0
- package/dist/server/src/lib/hooks/sources/claude/history.js +7 -0
- package/dist/server/src/lib/hooks/sources/codex/history.js +7 -0
- package/dist/server/src/lib/hooks/sources/command-code/history.js +7 -0
- package/dist/server/src/lib/hooks/sources/transcript-history.js +34 -0
- package/dist/server/src/lib/log-manager.js +7 -2
- package/dist/server/src/lib/polling/auto-yes-enter-fallback-state.js +22 -3
- package/dist/server/src/lib/polling/auto-yes-poller-state.js +234 -0
- package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +3 -0
- package/dist/server/src/lib/polling/pending-scraped-response.js +119 -3
- package/dist/server/src/lib/polling/poller-owner.js +110 -0
- package/dist/server/src/lib/polling/response-checker.js +202 -39
- package/dist/server/src/lib/polling/response-dedup.js +8 -0
- package/dist/server/src/lib/polling/response-poller-core.js +131 -12
- package/dist/server/src/lib/polling/structured-history-gate.js +20 -1
- package/dist/server/src/lib/prompt-answer-sender.js +59 -12
- package/dist/server/src/lib/realtime/terminal-broadcast.js +14 -0
- package/dist/server/src/lib/session/chat-turn-progress.js +57 -6
- package/dist/server/src/lib/session/current-output-builder.js +19 -0
- package/dist/server/src/lib/session/worktree-session-reconcile.js +83 -10
- package/dist/server/src/lib/timer-manager.js +7 -0
- package/dist/server/src/lib/tmux/tmux.js +24 -2
- package/dist/server/src/lib/verification/env-clean-gate.js +30 -2
- package/package.json +1 -1
- package/release-notes/0.46.1.json +40 -0
- package/.next/server/chunks/5625.js +0 -25
- package/.next/static/chunks/5371-a94ed618148287a0.js +0 -1
- package/.next/static/chunks/5794-947fb4934d8be193.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-2eb1d0fd0a077b7e.js +0 -1
- /package/.next/static/{zI2nQ6WKboSbFbMF4EtrJ → QxDf7lm-JJkWoj-TyWiSB}/_buildManifest.js +0 -0
- /package/.next/static/{zI2nQ6WKboSbFbMF4EtrJ → QxDf7lm-JJkWoj-TyWiSB}/_ssgManifest.js +0 -0
|
@@ -88,6 +88,7 @@ const logger = (0, logger_1.createLogger)('auto-yes-poller');
|
|
|
88
88
|
const path_validator_1 = require("./security/path-validator");
|
|
89
89
|
const auto_yes_state_1 = require("./auto-yes-state");
|
|
90
90
|
const global_state_1 = require("./global-state");
|
|
91
|
+
const auto_yes_suppression_reason_1 = require("./polling/auto-yes-suppression-reason");
|
|
91
92
|
const auto_yes_poller_state_1 = require("./polling/auto-yes-poller-state");
|
|
92
93
|
const auto_yes_prompt_suppression_1 = require("./polling/auto-yes-prompt-suppression");
|
|
93
94
|
/** In-memory storage for poller states (globalThis for hot reload persistence) */
|
|
@@ -158,8 +159,8 @@ function isPollerActive(compositeKey) {
|
|
|
158
159
|
* @param compositeKey - Composite key
|
|
159
160
|
* @param timestamp - Timestamp value (Date.now())
|
|
160
161
|
*/
|
|
161
|
-
function updateLastServerResponseTimestamp(compositeKey, timestamp) {
|
|
162
|
-
const pollerState =
|
|
162
|
+
function updateLastServerResponseTimestamp(compositeKey, timestamp, owner) {
|
|
163
|
+
const pollerState = ownedPollerState(compositeKey, owner);
|
|
163
164
|
if (pollerState) {
|
|
164
165
|
pollerState.lastServerResponseTimestamp = timestamp;
|
|
165
166
|
}
|
|
@@ -170,9 +171,10 @@ function updateLastServerResponseTimestamp(compositeKey, timestamp) {
|
|
|
170
171
|
* and after an answer is sent.
|
|
171
172
|
*
|
|
172
173
|
* @param compositeKey - Composite key
|
|
174
|
+
* @param owner - The tick's own poller state (Issue #3706); see {@link ownedPollerState}
|
|
173
175
|
*/
|
|
174
|
-
function resetErrorCount(compositeKey) {
|
|
175
|
-
const pollerState =
|
|
176
|
+
function resetErrorCount(compositeKey, owner) {
|
|
177
|
+
const pollerState = ownedPollerState(compositeKey, owner);
|
|
176
178
|
if (pollerState) {
|
|
177
179
|
pollerState.consecutiveErrors = 0;
|
|
178
180
|
pollerState.currentInterval = auto_yes_state_1.POLLING_INTERVAL_MS;
|
|
@@ -183,9 +185,10 @@ function resetErrorCount(compositeKey) {
|
|
|
183
185
|
* [IA-MF-001] compositeKey-based: extracts worktreeId/cliToolId for disableAutoYes.
|
|
184
186
|
*
|
|
185
187
|
* @param compositeKey - Composite key
|
|
188
|
+
* @param owner - The tick's own poller state (Issue #3706); see {@link ownedPollerState}
|
|
186
189
|
*/
|
|
187
|
-
function incrementErrorCount(compositeKey) {
|
|
188
|
-
const pollerState =
|
|
190
|
+
function incrementErrorCount(compositeKey, owner) {
|
|
191
|
+
const pollerState = ownedPollerState(compositeKey, owner);
|
|
189
192
|
if (pollerState) {
|
|
190
193
|
pollerState.consecutiveErrors++;
|
|
191
194
|
pollerState.currentInterval = (0, auto_yes_state_1.calculateBackoffInterval)(pollerState.consecutiveErrors);
|
|
@@ -209,6 +212,62 @@ function incrementErrorCount(compositeKey) {
|
|
|
209
212
|
}
|
|
210
213
|
}
|
|
211
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* The poller state under `compositeKey`, but only when it is `owner` — the
|
|
217
|
+
* tick's own (Issue #3706). Without `owner` (callers outside a tick, and tests
|
|
218
|
+
* driving `detectAndRespondToPrompt` with a hand-built state) it is the plain
|
|
219
|
+
* by-key read this always was.
|
|
220
|
+
*/
|
|
221
|
+
function ownedPollerState(compositeKey, owner) {
|
|
222
|
+
const pollerState = getPollerState(compositeKey);
|
|
223
|
+
if (owner !== undefined && pollerState !== owner)
|
|
224
|
+
return undefined;
|
|
225
|
+
return pollerState;
|
|
226
|
+
}
|
|
227
|
+
/** Is the tick's poller still the one under its key (not stopped, not replaced)? */
|
|
228
|
+
function isPollerCurrent(generation) {
|
|
229
|
+
return getPollerState(generation.compositeKey) === generation.pollerState;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* May the tick still act: its poller, its grant (the same enable, not expired)
|
|
233
|
+
* and its session (no relaunch since) are all still the ones it started under.
|
|
234
|
+
* Checked after capture, after each wait before a key, and inside the send.
|
|
235
|
+
*/
|
|
236
|
+
function isTickCurrent(generation) {
|
|
237
|
+
if (!isPollerCurrent(generation))
|
|
238
|
+
return false;
|
|
239
|
+
// `getAutoYesState` returns the stored object until it expires, when it
|
|
240
|
+
// stores (and returns) a disabled one — so an expiry fails identity too.
|
|
241
|
+
const grant = (0, auto_yes_state_1.getAutoYesState)(generation.worktreeId, generation.cliToolId, generation.instanceId);
|
|
242
|
+
if (grant !== generation.grant || !grant.enabled)
|
|
243
|
+
return false;
|
|
244
|
+
return isSameSession(generation);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Is the session the tick answered still the one it started on (Issue #3706)?
|
|
248
|
+
* What the follow-ups of an answer already sent — the response poller restart,
|
|
249
|
+
* the terminal push — are judged by: a disable after the keys landed does not
|
|
250
|
+
* un-send them, a relaunch does make them about another process.
|
|
251
|
+
*/
|
|
252
|
+
function isSameSession(generation) {
|
|
253
|
+
return (0, auto_yes_enter_fallback_1.getEnterFallbackSessionEpoch)(generation.compositeKey) === generation.sessionEpoch;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* End a tick that found its generation gone (Issue #3706). It changes nothing;
|
|
257
|
+
* it re-arms its timer only when its own poller is still the current one — a
|
|
258
|
+
* renewed grant or a relaunched session under the same poller — so the chain
|
|
259
|
+
* goes on and the next tick starts from the new state.
|
|
260
|
+
*/
|
|
261
|
+
function endStaleTick(generation) {
|
|
262
|
+
logger.debug('poller:auto-yes-tick-superseded', {
|
|
263
|
+
worktreeId: generation.worktreeId,
|
|
264
|
+
cliToolId: generation.cliToolId,
|
|
265
|
+
instanceId: generation.instanceId,
|
|
266
|
+
pollerCurrent: isPollerCurrent(generation),
|
|
267
|
+
});
|
|
268
|
+
if (isPollerCurrent(generation))
|
|
269
|
+
scheduleNextPoll(generation);
|
|
270
|
+
}
|
|
212
271
|
/**
|
|
213
272
|
* Check if the given prompt has already been answered recently.
|
|
214
273
|
*
|
|
@@ -409,9 +468,13 @@ function isStillEnterFallbackScreen(prompt, fresh) {
|
|
|
409
468
|
* 6. a capture taken now, past the capture cache, must still be that screen
|
|
410
469
|
* ({@link isStillEnterFallbackScreen}).
|
|
411
470
|
*
|
|
471
|
+
* Issue #3706: with a `generation` (a tick of `pollAutoYes`), the waits of
|
|
472
|
+
* steps 5 and 6 are each followed by {@link isTickCurrent}; a tick that lost
|
|
473
|
+
* its generation sends nothing and changes nothing.
|
|
474
|
+
*
|
|
412
475
|
* @returns true when the Enter was sent; the caller returns `responded`
|
|
413
476
|
*/
|
|
414
|
-
async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput) {
|
|
477
|
+
async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput, generation) {
|
|
415
478
|
const { worktreeId, cliToolId, instanceId, pollerState, promptData, frameKey } = prompt;
|
|
416
479
|
const judgement = dialogGate.dialog === null ? (0, auto_yes_enter_fallback_1.judgeEnterFallback)(cliToolId, rawOutput) : null;
|
|
417
480
|
if (judgement === null || !judgement.eligible) {
|
|
@@ -481,6 +544,8 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
481
544
|
// 5. Never another server's session (Issue #2865).
|
|
482
545
|
const sessionName = manager_1.CLIToolManager.getInstance().getTool(cliToolId).getSessionName(worktreeId, instanceId);
|
|
483
546
|
const ownership = await (0, worktree_session_ownership_1.checkWorktreeSessionOwnership)(worktreeId, sessionName);
|
|
547
|
+
if (generation !== undefined && !isTickCurrent(generation))
|
|
548
|
+
return false;
|
|
484
549
|
if (ownership === null || ownership.verdict === 'foreign') {
|
|
485
550
|
(0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
|
|
486
551
|
worktreeId,
|
|
@@ -500,6 +565,8 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
500
565
|
// has the box back. Everything that decided the Enter is judged again on the
|
|
501
566
|
// fresh frame; any difference sends nothing, and the next tick starts over.
|
|
502
567
|
const fresh = await (0, cli_session_1.captureSessionOutputFresh)(worktreeId, cliToolId, (rawOutput ?? '').split('\n').length, instanceId);
|
|
568
|
+
if (generation !== undefined && !isTickCurrent(generation))
|
|
569
|
+
return false;
|
|
503
570
|
if (!isStillEnterFallbackScreen(prompt, fresh)) {
|
|
504
571
|
pollerState.enterFallbackCandidateKey = null;
|
|
505
572
|
logger.debug('poller:auto-yes-enter-fallback-fresh-frame-differs', {
|
|
@@ -516,13 +583,20 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
516
583
|
finally {
|
|
517
584
|
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
518
585
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
586
|
+
// Issue #3706: the Enter is out, but the per-screen record is the CURRENT
|
|
587
|
+
// session's: when the poller was stopped (the record was forgotten with it)
|
|
588
|
+
// or a new session began while `send-keys` was awaited, the return of this
|
|
589
|
+
// send must not put the record back. The audit row below is kept either way.
|
|
590
|
+
if (generation === undefined || (isPollerCurrent(generation) && isSameSession(generation))) {
|
|
591
|
+
pollerState.enterFallbackSentKey = frameKey;
|
|
592
|
+
pollerState.enterFallbackCandidateKey = null;
|
|
593
|
+
(0, auto_yes_enter_fallback_1.recordEnterFallbackSent)(worktreeId, cliToolId, instanceId, {
|
|
594
|
+
promptType: promptData.type,
|
|
595
|
+
refusalReason: judgement.refusalReason,
|
|
596
|
+
screenKey,
|
|
597
|
+
...(generation !== undefined ? { epoch: generation.sessionEpoch } : {}),
|
|
598
|
+
});
|
|
599
|
+
}
|
|
526
600
|
logger.info('poller:auto-yes-enter-fallback-sent', {
|
|
527
601
|
worktreeId,
|
|
528
602
|
cliToolId,
|
|
@@ -531,7 +605,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
531
605
|
refusalReason: judgement.refusalReason,
|
|
532
606
|
composerState: judgement.composerState,
|
|
533
607
|
});
|
|
534
|
-
await finishAnsweredPrompt(prompt, promptDetection, exports.AUTO_YES_ENTER_FALLBACK_ANSWER);
|
|
608
|
+
await finishAnsweredPrompt(prompt, promptDetection, exports.AUTO_YES_ENTER_FALLBACK_ANSWER, generation);
|
|
535
609
|
return true;
|
|
536
610
|
}
|
|
537
611
|
/**
|
|
@@ -543,24 +617,42 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
543
617
|
* @param promptDetection - The detection `prompt.promptData` was read from; its
|
|
544
618
|
* content becomes the audit row's
|
|
545
619
|
* @param answer - The answer that was sent
|
|
620
|
+
* @param generation - The tick's generation (Issue #3706). The keys are out, so
|
|
621
|
+
* the task event and the audit row are always written: they record what the
|
|
622
|
+
* agent actually received. What belongs to the poller is written to the
|
|
623
|
+
* tick's own poller state only, never to a successor under the same key, and
|
|
624
|
+
* the response poller restart and the terminal push are skipped when the
|
|
625
|
+
* session was relaunched meanwhile.
|
|
546
626
|
*/
|
|
547
|
-
async function finishAnsweredPrompt(prompt, promptDetection, answer) {
|
|
627
|
+
async function finishAnsweredPrompt(prompt, promptDetection, answer, generation) {
|
|
548
628
|
const { worktreeId, cliToolId, instanceId, compositeKey, pollerState, promptData, promptKey } = prompt;
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
//
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
629
|
+
// Issue #3706: the keys went to the session the tick started on. When a new
|
|
630
|
+
// one was begun while they were being sent, the new process never received
|
|
631
|
+
// this answer, so nothing that stands for "this instance's prompt was just
|
|
632
|
+
// answered" is written: not the dedup key (it would keep Auto-Yes off the new
|
|
633
|
+
// process's identical question), not the poller's response time, not the
|
|
634
|
+
// task event (the task looked up now may be the new session's). Only the
|
|
635
|
+
// audit row is, and it may not adopt a pending row saved after the tick began.
|
|
636
|
+
const sameSession = generation === undefined || isSameSession(generation);
|
|
637
|
+
if (sameSession) {
|
|
638
|
+
// 6. Update timestamp and reset error count
|
|
639
|
+
updateLastServerResponseTimestamp(compositeKey, Date.now(), generation?.pollerState);
|
|
640
|
+
resetErrorCount(compositeKey, generation?.pollerState);
|
|
641
|
+
// 7. Record answered prompt key and timestamp
|
|
642
|
+
pollerState.lastAnsweredPromptKey = promptKey;
|
|
643
|
+
pollerState.lastAnsweredAt = Date.now();
|
|
644
|
+
pollerState.lastSkipWarnKey = null;
|
|
645
|
+
}
|
|
646
|
+
logger.info('poller:response-sent', { worktreeId, cliToolId, instanceId, sameSession });
|
|
557
647
|
// Issue #1548: the prompt was answered without a human. Raised only after
|
|
558
648
|
// the keys actually reached tmux, so a send that threw is not logged as an
|
|
559
649
|
// answer. No-ops when this instance is not running a contract.
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
650
|
+
if (sameSession) {
|
|
651
|
+
(0, task_transition_service_1.applyEventToActiveTask)((0, db_instance_1.getDbInstance)(), worktreeId, cliToolId,
|
|
652
|
+
// Undefined means the primary instance, which the task lookup identifies
|
|
653
|
+
// by the tool id itself (see getActiveTaskForInstance).
|
|
654
|
+
instanceId ?? cliToolId, 'prompt_answered_auto', { promptType: promptData.type });
|
|
655
|
+
}
|
|
564
656
|
// Issue #1685: persist question/options/answer to chat history so the audit
|
|
565
657
|
// trail survives even when the answer landed inside the response poller's
|
|
566
658
|
// interval and the prompt was never saved as a pending message. Must never
|
|
@@ -575,6 +667,7 @@ async function finishAnsweredPrompt(prompt, promptDetection, answer) {
|
|
|
575
667
|
answer,
|
|
576
668
|
answeredBy: 'auto',
|
|
577
669
|
content: promptDetection.rawContent || promptDetection.cleanContent,
|
|
670
|
+
...(sameSession ? {} : { pendingSavedAtOrBefore: generation.startedAt }),
|
|
578
671
|
});
|
|
579
672
|
}
|
|
580
673
|
catch (recordError) {
|
|
@@ -605,6 +698,10 @@ async function finishAnsweredPrompt(prompt, promptDetection, answer) {
|
|
|
605
698
|
Promise.resolve().then(() => __importStar(require('../lib/polling/response-poller'))),
|
|
606
699
|
Promise.resolve().then(() => __importStar(require('../lib/realtime/terminal-broadcast'))),
|
|
607
700
|
]);
|
|
701
|
+
// Re-read after the await, not the `sameSession` taken before the audit: a
|
|
702
|
+
// relaunch while the modules loaded makes the restart about the new process.
|
|
703
|
+
if (!sameSession || (generation !== undefined && !isSameSession(generation)))
|
|
704
|
+
return;
|
|
608
705
|
startResponsePolling(worktreeId, cliToolId, instanceId);
|
|
609
706
|
void broadcastTerminalSnapshotAfterInteraction(worktreeId, cliToolId, instanceId);
|
|
610
707
|
}
|
|
@@ -621,10 +718,18 @@ async function finishAnsweredPrompt(prompt, promptDetection, answer) {
|
|
|
621
718
|
* @param rawOutput - The SAME tick's capture with its box drawing intact
|
|
622
719
|
* (Issue #2522). Omitting it costs Command Code's `AskUserQuestion` and
|
|
623
720
|
* nothing else; see {@link capturePollerFrame}.
|
|
721
|
+
* @param generation - The calling tick's generation (Issue #3706). Given, every
|
|
722
|
+
* wait before a key — the ownership check, the Enter fallback's fresh capture,
|
|
723
|
+
* each wait inside the send — is followed by {@link isTickCurrent}, and a tick
|
|
724
|
+
* that lost it types nothing more and returns `no_answer`. Omitted (direct
|
|
725
|
+
* callers, tests), nothing is checked, as before.
|
|
624
726
|
* @returns 'responded' | 'no_prompt' | 'duplicate' | 'no_answer' | 'error'
|
|
625
727
|
*/
|
|
626
|
-
async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, precomputedLines, instanceId, rawOutput) {
|
|
728
|
+
async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, precomputedLines, instanceId, rawOutput, generation) {
|
|
627
729
|
const compositeKey = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
730
|
+
// Issue #3706 / #3713: the send in flight, settled without an await once it
|
|
731
|
+
// comes back — as a partial answer when it was stopped with keys on the pane.
|
|
732
|
+
let attempt = null;
|
|
628
733
|
try {
|
|
629
734
|
// 1. Detect prompt.
|
|
630
735
|
//
|
|
@@ -683,6 +788,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
683
788
|
const frame = (0, frame_1.normalizeFrame)(rawOutput ?? cleanOutput, cliToolId);
|
|
684
789
|
const promptDetection = (0, response_checker_1.detectPromptOnCleanFrame)(cleanOutput, cliToolId, precomputedLines, rawOutput, receiptScope, frame);
|
|
685
790
|
if (!promptDetection.isPrompt || !promptDetection.promptData) {
|
|
791
|
+
(0, auto_yes_poller_state_1.forgetPartialAnswer)(compositeKey);
|
|
686
792
|
pollerState.lastAnsweredPromptKey = null;
|
|
687
793
|
pollerState.lastAnsweredAt = null;
|
|
688
794
|
pollerState.lastSkipWarnKey = null;
|
|
@@ -708,6 +814,37 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
708
814
|
promptKey,
|
|
709
815
|
frameKey,
|
|
710
816
|
};
|
|
817
|
+
// 2.5. Issue #3706: an earlier answer to this prompt was stopped after some
|
|
818
|
+
// of its keys reached the pane (a `y` whose Enter was withheld). Typing the
|
|
819
|
+
// answer again would append to what is already in the input (`yy`), and
|
|
820
|
+
// what the pane holds now cannot be told from the frame, so the prompt is
|
|
821
|
+
// left for a human until it leaves the screen, another prompt replaces it
|
|
822
|
+
// (`partialAnswerScreenKey`: options and approvalTarget count, the cursor
|
|
823
|
+
// does not), or the session is relaunched. Recorded as a suppression, so
|
|
824
|
+
// the prompt Push and `cmate wait` see "withheld", not "answering".
|
|
825
|
+
const screenKey = (0, auto_yes_poller_state_1.partialAnswerScreenKey)(promptDetection.promptData);
|
|
826
|
+
const partial = (0, auto_yes_poller_state_1.getPartialAnswer)(compositeKey, epoch);
|
|
827
|
+
if (partial !== null) {
|
|
828
|
+
if (partial.screenKey === screenKey) {
|
|
829
|
+
(0, auto_yes_prompt_suppression_1.suppressAndWarnOnce)(judged, {
|
|
830
|
+
reason: auto_yes_suppression_reason_1.PARTIAL_ANSWER_SUPPRESSION_REASON,
|
|
831
|
+
mode: null,
|
|
832
|
+
promptType: promptDetection.promptData.type,
|
|
833
|
+
}, `${frameKey}\u0000partial-answer`, 'poller:auto-yes-skipped-partial-answer', { promptType: promptDetection.promptData.type, stoppedAt: partial.at });
|
|
834
|
+
return 'no_answer';
|
|
835
|
+
}
|
|
836
|
+
// Another prompt is on screen: only the sends at the forgotten one's
|
|
837
|
+
// screen go with it (#3713) — one in flight at this screen keeps its hold.
|
|
838
|
+
(0, auto_yes_poller_state_1.forgetPartialAnswer)(compositeKey, partial.screenKey);
|
|
839
|
+
}
|
|
840
|
+
// Issue #3713: a worktree ID move carried an answer still being sent to
|
|
841
|
+
// this session's key. Its keys may already be on the pane, and how its send
|
|
842
|
+
// ends is not known yet — so this screen gets no keys until it comes back
|
|
843
|
+
// (recorded as a partial answer above, or gone).
|
|
844
|
+
if ((0, auto_yes_poller_state_1.hasMovedSendInFlight)(compositeKey, epoch, screenKey)) {
|
|
845
|
+
(0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000moved-send-in-flight`, 'poller:auto-yes-skipped-moved-send-in-flight', { worktreeId, cliToolId, instanceId, promptType: promptDetection.promptData.type });
|
|
846
|
+
return 'no_answer';
|
|
847
|
+
}
|
|
711
848
|
// 3., 3.2., 3.5. Frames that are not Auto-Yes's to answer: see
|
|
712
849
|
// `suppressIfNotOursToAnswer`.
|
|
713
850
|
const notOurs = (0, auto_yes_prompt_suppression_1.suppressIfNotOursToAnswer)(judged, frame);
|
|
@@ -720,7 +857,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
720
857
|
// 3.6. Issue #3397: a refused frame may be a choice screen CommandMate
|
|
721
858
|
// cannot read; with Auto-Yes on it gets one Enter. See `tryEnterFallback`.
|
|
722
859
|
if (notOurs.kind === 'dialog-gate-refused') {
|
|
723
|
-
return (await tryEnterFallback(judged, promptDetection, notOurs.dialogGate, rawOutput))
|
|
860
|
+
return (await tryEnterFallback(judged, promptDetection, notOurs.dialogGate, rawOutput, generation))
|
|
724
861
|
? 'responded'
|
|
725
862
|
: 'no_answer';
|
|
726
863
|
}
|
|
@@ -759,6 +896,8 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
759
896
|
// Issue #2865: never auto-answer a same-named session another CommandMate
|
|
760
897
|
// server created. The poller keeps running; it just does not type.
|
|
761
898
|
const ownership = await (0, worktree_session_ownership_1.checkWorktreeSessionOwnership)(worktreeId, sessionName);
|
|
899
|
+
if (generation !== undefined && !isTickCurrent(generation))
|
|
900
|
+
return 'no_answer';
|
|
762
901
|
if (ownership === null || ownership.verdict === 'foreign') {
|
|
763
902
|
(0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
|
|
764
903
|
worktreeId,
|
|
@@ -770,23 +909,42 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
770
909
|
});
|
|
771
910
|
return 'no_answer';
|
|
772
911
|
}
|
|
912
|
+
attempt = (0, auto_yes_poller_state_1.beginSendAttempt)(compositeKey, screenKey, generation?.sessionEpoch ?? epoch);
|
|
773
913
|
try {
|
|
774
914
|
await (0, prompt_answer_sender_1.sendPromptAnswer)({
|
|
775
915
|
sessionName,
|
|
776
916
|
answer,
|
|
777
917
|
cliToolId,
|
|
778
918
|
promptData: promptDetection.promptData,
|
|
919
|
+
// Issue #3706: asked after each wait inside the send.
|
|
920
|
+
...(generation !== undefined ? { stillCurrent: () => isTickCurrent(generation) } : {}),
|
|
779
921
|
});
|
|
780
922
|
}
|
|
781
923
|
finally {
|
|
782
924
|
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
783
925
|
}
|
|
926
|
+
(0, auto_yes_poller_state_1.settleSendAttempt)(attempt, false);
|
|
784
927
|
// 6., 7. The answer reached tmux: see `finishAnsweredPrompt`.
|
|
785
|
-
await finishAnsweredPrompt(judged, promptDetection, answer);
|
|
928
|
+
await finishAnsweredPrompt(judged, promptDetection, answer, generation);
|
|
786
929
|
return 'responded';
|
|
787
930
|
}
|
|
788
|
-
catch {
|
|
789
|
-
|
|
931
|
+
catch (error) {
|
|
932
|
+
// Issue #3706: a tick that lost its generation — the send stopped part-way
|
|
933
|
+
// (`PromptAnswerAbortedError`), or failed after the grant went — is not a
|
|
934
|
+
// failure of the poller that now holds the key, and not an answer either.
|
|
935
|
+
if (generation !== undefined && !isTickCurrent(generation)) {
|
|
936
|
+
const keysSent = error instanceof Error && 'keysSent' in error ? Boolean(error.keysSent) : null;
|
|
937
|
+
// A stop with keys already on the pane: the next tick must not type the
|
|
938
|
+
// answer again over them (step 2.5). Recorded under the attempt's key as
|
|
939
|
+
// it is now — the new one when a worktree ID move carried it (#3713).
|
|
940
|
+
if (attempt !== null)
|
|
941
|
+
(0, auto_yes_poller_state_1.settleSendAttempt)(attempt, keysSent === true);
|
|
942
|
+
logger.info('poller:auto-yes-answer-abandoned', { worktreeId, cliToolId, instanceId, keysSent });
|
|
943
|
+
return 'no_answer';
|
|
944
|
+
}
|
|
945
|
+
if (attempt !== null)
|
|
946
|
+
(0, auto_yes_poller_state_1.settleSendAttempt)(attempt, false);
|
|
947
|
+
incrementErrorCount(compositeKey, generation?.pollerState);
|
|
790
948
|
logger.warn('poller:detect-respond-error', { worktreeId, cliToolId, instanceId });
|
|
791
949
|
return 'error';
|
|
792
950
|
}
|
|
@@ -797,19 +955,36 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
797
955
|
* @param worktreeId - Worktree identifier
|
|
798
956
|
* @param cliToolId - CLI tool type being polled
|
|
799
957
|
* @param instanceId - Agent instance ID being polled (Issue #896; equals cliToolId for primary)
|
|
958
|
+
* @param owner - The poller state whose timer fired (Issue #3706); a timer of a
|
|
959
|
+
* poller that has since been replaced does nothing
|
|
800
960
|
*/
|
|
801
|
-
async function pollAutoYes(worktreeId, cliToolId, instanceId) {
|
|
961
|
+
async function pollAutoYes(worktreeId, cliToolId, instanceId, owner) {
|
|
802
962
|
const compositeKey = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
803
963
|
// 1. Validate context
|
|
804
964
|
const pollerState = getPollerState(compositeKey);
|
|
965
|
+
if (owner !== undefined && pollerState !== owner)
|
|
966
|
+
return;
|
|
805
967
|
const contextResult = validatePollingContext(compositeKey, pollerState);
|
|
806
968
|
if (contextResult !== 'valid') {
|
|
807
969
|
return;
|
|
808
970
|
}
|
|
971
|
+
const grant = (0, auto_yes_state_1.getAutoYesState)(worktreeId, cliToolId, instanceId);
|
|
972
|
+
if (!grant?.enabled)
|
|
973
|
+
return;
|
|
974
|
+
// Issue #3706: what this tick acts for, compared after every wait below.
|
|
975
|
+
const generation = {
|
|
976
|
+
compositeKey,
|
|
977
|
+
worktreeId,
|
|
978
|
+
cliToolId,
|
|
979
|
+
instanceId,
|
|
980
|
+
pollerState: pollerState,
|
|
981
|
+
grant,
|
|
982
|
+
sessionEpoch: (0, auto_yes_enter_fallback_1.getEnterFallbackSessionEpoch)(compositeKey),
|
|
983
|
+
startedAt: Date.now(),
|
|
984
|
+
};
|
|
809
985
|
try {
|
|
810
986
|
// 2. Capture and clean output
|
|
811
|
-
const
|
|
812
|
-
const captureLines = autoYesState?.stopPattern
|
|
987
|
+
const captureLines = grant.stopPattern
|
|
813
988
|
? auto_yes_config_1.FULL_CAPTURE_LINES
|
|
814
989
|
: auto_yes_config_1.REDUCED_CAPTURE_LINES;
|
|
815
990
|
// Issue #2522: one capture, both spellings. The cleaned string drives every
|
|
@@ -817,6 +992,10 @@ async function pollAutoYes(worktreeId, cliToolId, instanceId) {
|
|
|
817
992
|
// so a dialog anchored on box drawing is not invisible to the one path that
|
|
818
993
|
// can answer it without a human.
|
|
819
994
|
const { raw: rawOutput, clean: cleanOutput } = await capturePollerFrame(worktreeId, cliToolId, captureLines, instanceId);
|
|
995
|
+
if (!isTickCurrent(generation)) {
|
|
996
|
+
endStaleTick(generation);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
820
999
|
if (pollerState.waitingForSession) {
|
|
821
1000
|
pollerState.waitingForSession = false;
|
|
822
1001
|
logger.info('poller:session-appeared', { worktreeId, cliToolId, instanceId });
|
|
@@ -827,23 +1006,27 @@ async function pollAutoYes(worktreeId, cliToolId, instanceId) {
|
|
|
827
1006
|
return;
|
|
828
1007
|
}
|
|
829
1008
|
// 4. Detect and respond to prompt
|
|
830
|
-
const result = await detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, lines, instanceId, rawOutput);
|
|
1009
|
+
const result = await detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, lines, instanceId, rawOutput, generation);
|
|
1010
|
+
if (!isTickCurrent(generation)) {
|
|
1011
|
+
endStaleTick(generation);
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
831
1014
|
// Issue #3329: a poll that ran to the end ends the run of errors, so only
|
|
832
1015
|
// failures in a row reach AUTO_STOP_ERROR_THRESHOLD — not ones scattered
|
|
833
1016
|
// over hours. Not on 'error': a capture that works followed by an answer
|
|
834
1017
|
// that fails every time must still back off and stop.
|
|
835
1018
|
if (result !== 'error') {
|
|
836
|
-
resetErrorCount(compositeKey);
|
|
1019
|
+
resetErrorCount(compositeKey, generation.pollerState);
|
|
837
1020
|
}
|
|
838
1021
|
if (result === 'responded') {
|
|
839
|
-
scheduleNextPoll(
|
|
1022
|
+
scheduleNextPoll(generation, auto_yes_state_1.COOLDOWN_INTERVAL_MS);
|
|
840
1023
|
return;
|
|
841
1024
|
}
|
|
842
1025
|
// 5. Thinking check
|
|
843
1026
|
if (result === 'no_prompt') {
|
|
844
1027
|
const recentLines = lines.slice(-auto_yes_state_1.THINKING_CHECK_LINE_COUNT).join('\n');
|
|
845
1028
|
if ((0, cli_patterns_1.detectThinking)(cliToolId, recentLines)) {
|
|
846
|
-
scheduleNextPoll(
|
|
1029
|
+
scheduleNextPoll(generation, auto_yes_config_1.THINKING_POLLING_INTERVAL_MS);
|
|
847
1030
|
return;
|
|
848
1031
|
}
|
|
849
1032
|
}
|
|
@@ -853,18 +1036,23 @@ async function pollAutoYes(worktreeId, cliToolId, instanceId) {
|
|
|
853
1036
|
// the session it answers for (`auto-yes-lifecycle`), and `send --auto-yes`
|
|
854
1037
|
// enables it before the session starts — so wait until `expiresAt`, at the
|
|
855
1038
|
// backoff cap, instead of counting toward `consecutive_errors`.
|
|
856
|
-
|
|
1039
|
+
const sessionMissing = await isSessionMissing(worktreeId, cliToolId, instanceId);
|
|
1040
|
+
if (!isTickCurrent(generation)) {
|
|
1041
|
+
endStaleTick(generation);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
if (sessionMissing) {
|
|
857
1045
|
if (!pollerState.waitingForSession) {
|
|
858
1046
|
pollerState.waitingForSession = true;
|
|
859
1047
|
logger.info('poller:waiting-for-session', { worktreeId, cliToolId, instanceId });
|
|
860
1048
|
}
|
|
861
|
-
scheduleNextPoll(
|
|
1049
|
+
scheduleNextPoll(generation, auto_yes_state_1.MAX_BACKOFF_MS);
|
|
862
1050
|
return;
|
|
863
1051
|
}
|
|
864
|
-
incrementErrorCount(compositeKey);
|
|
1052
|
+
incrementErrorCount(compositeKey, generation.pollerState);
|
|
865
1053
|
logger.warn('poller:poll-error', { worktreeId, cliToolId, instanceId, error: (0, errors_1.getErrorMessage)(error) });
|
|
866
1054
|
}
|
|
867
|
-
scheduleNextPoll(
|
|
1055
|
+
scheduleNextPoll(generation);
|
|
868
1056
|
}
|
|
869
1057
|
/**
|
|
870
1058
|
* Whether a failed capture failed because the session does not exist
|
|
@@ -883,19 +1071,20 @@ async function isSessionMissing(worktreeId, cliToolId, instanceId) {
|
|
|
883
1071
|
/**
|
|
884
1072
|
* Schedule the next polling iteration
|
|
885
1073
|
*
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
1074
|
+
* Issue #3706: on the tick's OWN poller state, and only while it is still the
|
|
1075
|
+
* one under its key. Re-reading by key let a tick that outlived a disable →
|
|
1076
|
+
* enable write its timer over the new poller's `timerId`, leaving two chains.
|
|
1077
|
+
*
|
|
1078
|
+
* @param generation - The tick's generation
|
|
889
1079
|
* @param overrideInterval - Optional override for the next poll delay
|
|
890
1080
|
*/
|
|
891
|
-
function scheduleNextPoll(
|
|
892
|
-
|
|
893
|
-
const pollerState = getPollerState(compositeKey);
|
|
894
|
-
if (!pollerState)
|
|
1081
|
+
function scheduleNextPoll(generation, overrideInterval) {
|
|
1082
|
+
if (!isPollerCurrent(generation))
|
|
895
1083
|
return;
|
|
1084
|
+
const { worktreeId, cliToolId, instanceId, pollerState } = generation;
|
|
896
1085
|
const interval = Math.max(overrideInterval ?? pollerState.currentInterval, auto_yes_state_1.POLLING_INTERVAL_MS);
|
|
897
1086
|
pollerState.timerId = setTimeout(() => {
|
|
898
|
-
pollAutoYes(worktreeId, cliToolId, instanceId);
|
|
1087
|
+
void pollAutoYes(worktreeId, cliToolId, instanceId, pollerState);
|
|
899
1088
|
}, interval);
|
|
900
1089
|
}
|
|
901
1090
|
// =============================================================================
|
|
@@ -958,7 +1147,7 @@ function startAutoYesPolling(worktreeId, cliToolId, instanceId) {
|
|
|
958
1147
|
(0, auto_yes_policy_1.invalidateSessionAutoYesPolicy)(compositeKey);
|
|
959
1148
|
// Start polling immediately
|
|
960
1149
|
pollerState.timerId = setTimeout(() => {
|
|
961
|
-
pollAutoYes(worktreeId, cliToolId, effectiveInstanceId);
|
|
1150
|
+
void pollAutoYes(worktreeId, cliToolId, effectiveInstanceId, pollerState);
|
|
962
1151
|
}, auto_yes_state_1.POLLING_INTERVAL_MS);
|
|
963
1152
|
logger.info('poller:started', { worktreeId, cliToolId, instanceId: effectiveInstanceId });
|
|
964
1153
|
return { started: true };
|
|
@@ -998,6 +1187,7 @@ function stopAllAutoYesPolling() {
|
|
|
998
1187
|
}
|
|
999
1188
|
autoYesPollerStates.clear();
|
|
1000
1189
|
(0, auto_yes_enter_fallback_1.clearEnterFallbacks)();
|
|
1190
|
+
(0, auto_yes_poller_state_1.clearPartialAnswers)();
|
|
1001
1191
|
}
|
|
1002
1192
|
/**
|
|
1003
1193
|
* Get all composite keys that have active auto-yes poller entries.
|
|
@@ -1017,12 +1207,20 @@ function getAutoYesPollerCompositeKeys() {
|
|
|
1017
1207
|
* [SF-001] Uses shared filterCompositeKeysByWorktree (DRY).
|
|
1018
1208
|
*
|
|
1019
1209
|
* @param worktreeId - Worktree identifier
|
|
1210
|
+
* @param options.keepPartialAnswers - Issue #3713 (re-instruction 1, P2-1): the
|
|
1211
|
+
* disable route's "every agent" passes it — a disable, like one agent's
|
|
1212
|
+
* `stopAutoYesPolling`, keeps the memory of an answer stopped part-way and
|
|
1213
|
+
* the sends still in flight, so a disable → enable does not type over a
|
|
1214
|
+
* partial input. Omitted (the worktree being deleted, `session-cleanup` via
|
|
1215
|
+
* `auto-yes-lifecycle`), both are dropped and a late send records nothing.
|
|
1020
1216
|
*/
|
|
1021
|
-
function stopAutoYesPollingByWorktree(worktreeId) {
|
|
1217
|
+
function stopAutoYesPollingByWorktree(worktreeId, options) {
|
|
1022
1218
|
const pollerKeys = (0, auto_yes_state_1.filterCompositeKeysByWorktree)(Array.from(autoYesPollerStates.keys()), worktreeId);
|
|
1023
1219
|
pollerKeys.forEach(key => stopAutoYesPolling(key));
|
|
1024
1220
|
// Issue #3397: and the records of instances whose poller had already stopped.
|
|
1025
1221
|
(0, auto_yes_enter_fallback_1.forgetEnterFallbacksByWorktree)(worktreeId);
|
|
1222
|
+
if (!options?.keepPartialAnswers)
|
|
1223
|
+
(0, auto_yes_poller_state_1.forgetPartialAnswersByWorktree)(worktreeId);
|
|
1026
1224
|
}
|
|
1027
1225
|
/**
|
|
1028
1226
|
* Check if any auto-yes poller is active for a given worktreeId.
|
|
@@ -8,18 +8,30 @@ exports.recordClaudeConversation = recordClaudeConversation;
|
|
|
8
8
|
const db_1 = require("./db");
|
|
9
9
|
const log_manager_1 = require("./log-manager");
|
|
10
10
|
const logger_1 = require("./logger");
|
|
11
|
+
const poller_owner_1 = require("./polling/poller-owner");
|
|
11
12
|
const logger = (0, logger_1.createLogger)('conversation-logger');
|
|
12
13
|
/**
|
|
13
14
|
* Persist the latest Claude response alongside the most recent user prompt.
|
|
14
15
|
* Errors are swallowed so the calling API route can continue responding.
|
|
15
16
|
*/
|
|
16
|
-
async function recordClaudeConversation(db, worktreeId, claudeResponse, cliToolId = 'claude') {
|
|
17
|
+
async function recordClaudeConversation(db, worktreeId, claudeResponse, cliToolId = 'claude', options = {}) {
|
|
18
|
+
const { pollerKey } = options;
|
|
19
|
+
const mayWrite = pollerKey === undefined ? undefined : () => (0, poller_owner_1.mayActForPollerKey)(pollerKey);
|
|
20
|
+
if (mayWrite && !mayWrite()) {
|
|
21
|
+
logger.info('conversation:log-refused', { worktreeId, cliToolId });
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
17
24
|
const lastUserMessage = (0, db_1.getLastUserMessage)(db, worktreeId);
|
|
18
25
|
if (!lastUserMessage) {
|
|
19
26
|
return;
|
|
20
27
|
}
|
|
21
28
|
try {
|
|
22
|
-
await (0, log_manager_1.createLog)(worktreeId, lastUserMessage.content, claudeResponse, cliToolId
|
|
29
|
+
const logPath = await (0, log_manager_1.createLog)(worktreeId, lastUserMessage.content, claudeResponse, cliToolId, {
|
|
30
|
+
mayWrite,
|
|
31
|
+
});
|
|
32
|
+
if (logPath === null) {
|
|
33
|
+
logger.info('conversation:log-refused', { worktreeId, cliToolId });
|
|
34
|
+
}
|
|
23
35
|
}
|
|
24
36
|
catch (error) {
|
|
25
37
|
logger.error('conversation:log-failed', { error: error instanceof Error ? error.message : String(error) });
|
|
@@ -693,9 +693,10 @@ function recordAnsweredPrompt(db, params) {
|
|
|
693
693
|
AND message_type = 'prompt'
|
|
694
694
|
AND json_extract(prompt_data, '$.status') = 'pending'
|
|
695
695
|
${exports.ACTIVE_FILTER}
|
|
696
|
+
${params.pendingSavedAtOrBefore !== undefined ? 'AND timestamp <= ?' : ''}
|
|
696
697
|
ORDER BY timestamp DESC
|
|
697
698
|
`)
|
|
698
|
-
.all(worktreeId, resolvedInstanceId);
|
|
699
|
+
.all(worktreeId, resolvedInstanceId, ...(params.pendingSavedAtOrBefore !== undefined ? [params.pendingSavedAtOrBefore] : []));
|
|
699
700
|
let target = null;
|
|
700
701
|
for (const row of rows) {
|
|
701
702
|
try {
|
|
@@ -101,6 +101,7 @@ exports.USER_TURN_ADOPTION_WINDOW_MS = void 0;
|
|
|
101
101
|
exports.normalizeUserTurnContent = normalizeUserTurnContent;
|
|
102
102
|
exports.recordUserTurn = recordUserTurn;
|
|
103
103
|
const logger_1 = require("../../lib/logger");
|
|
104
|
+
const poller_owner_1 = require("../../lib/polling/poller-owner");
|
|
104
105
|
const logger = (0, logger_1.createLogger)('lib/history/user-turn-recorder');
|
|
105
106
|
/**
|
|
106
107
|
* How far from the agent's own clock a `/send` row may sit and still be the
|
|
@@ -182,6 +183,19 @@ async function recordUserTurn(target, key, content, timestampMs, options = {}) {
|
|
|
182
183
|
timestampMs: existing.timestamp.getTime(),
|
|
183
184
|
};
|
|
184
185
|
}
|
|
186
|
+
// Issue #3705: the last `await` is behind us, and from here the recorder
|
|
187
|
+
// writes — a key onto an existing `/send` row, or a new row — with none
|
|
188
|
+
// between. A retired tick does neither under the instance it lost: both are
|
|
189
|
+
// keyed by `request_id`, which stops the same row being written twice and
|
|
190
|
+
// says nothing about who may write it.
|
|
191
|
+
if (!(0, poller_owner_1.mayActForAgentInstance)(target)) {
|
|
192
|
+
logger.info('user-turn-record-refused', {
|
|
193
|
+
...describe(target),
|
|
194
|
+
requestId: key,
|
|
195
|
+
generation: (0, poller_owner_1.currentPollerOwner)()?.generation,
|
|
196
|
+
});
|
|
197
|
+
return { outcome: 'refused', messageId: null, timestampMs: null };
|
|
198
|
+
}
|
|
185
199
|
const at = timestampMs > 0 ? timestampMs : Date.now();
|
|
186
200
|
const adopted = adoptExistingRow(chatDb, db, target, instanceId, key, normalized, at, options);
|
|
187
201
|
if (adopted)
|