commandmate 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +164 -164
- package/.next/app-path-routes-manifest.json +30 -30
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- 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_client-reference-manifest.js +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_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_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.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_client-reference-manifest.js +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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +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_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_client-reference-manifest.js +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_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_client-reference-manifest.js +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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/strict/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/strict/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_client-reference-manifest.js +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_client-reference-manifest.js +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_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_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_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_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.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 +1 -1
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +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 +3 -3
- 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 +30 -30
- package/.next/server/chunks/1601.js +2 -2
- package/.next/server/chunks/1656.js +2 -2
- package/.next/server/chunks/1945.js +3 -3
- package/.next/server/chunks/2069.js +1 -0
- package/.next/server/chunks/2422.js +1 -1
- package/.next/server/chunks/2534.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/4243.js +1 -1
- package/.next/server/chunks/4248.js +1 -1
- package/.next/server/chunks/4280.js +1 -1
- package/.next/server/chunks/6119.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6486.js +1 -1
- package/.next/server/chunks/720.js +2 -2
- package/.next/server/chunks/7604.js +2 -2
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8581.js +3 -3
- package/.next/server/chunks/8728.js +3 -3
- package/.next/server/chunks/8791.js +1 -1
- package/.next/server/chunks/9390.js +1 -1
- package/.next/server/chunks/9627.js +3 -3
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/1128-914c559913546bbc.js +1 -0
- package/.next/static/chunks/{2407-26139a8a2f1adbe8.js → 2407-ab92ae9837c39dce.js} +1 -1
- package/.next/static/chunks/{2619-a305ad4810f1393a.js → 2619-04bc32f026a0d946.js} +1 -1
- package/.next/static/chunks/{2863-f0121af1cbbedb3b.js → 2863-7173dc305dffc4f6.js} +1 -1
- package/.next/static/chunks/6288-6fadbba6e84393b2.js +1 -0
- package/.next/static/chunks/7665-e9ef6e0a85b9fa49.js +1 -0
- package/.next/static/chunks/{8165-c68b3dc844210e6a.js → 8165-263172c17ae8e8c3.js} +1 -1
- package/.next/static/chunks/9079-987939d231704218.js +1 -0
- package/.next/static/chunks/app/{layout-46b4cbf07520d9b6.js → layout-aa8e66164a4367af.js} +1 -1
- package/.next/static/chunks/app/sessions/{page-fb241bed579c67aa.js → page-cfd645305e25fd33.js} +1 -1
- package/.next/static/chunks/app/skills/[skillId]/{page-52ba58712faf3378.js → page-cb6e74fd9a6a571d.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-704086656fb45699.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/{page-cbc7749a33450cc5.js → page-a8d7886703ffc7b4.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/terminal/{page-c601f3b3ece6557e.js → page-373ea37b16d930c7.js} +1 -1
- package/.next/static/css/{743f79ff19ab91e9.css → db8959555c3d9c96.css} +1 -1
- package/.next/trace +13 -13
- package/dist/cli/commands/auto-yes.d.ts.map +1 -1
- package/dist/cli/commands/auto-yes.js +3 -5
- package/dist/cli/commands/capture.d.ts.map +1 -1
- package/dist/cli/commands/capture.js +5 -0
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +4 -6
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +8 -1
- package/dist/cli/config/duration-constants.d.ts +16 -13
- package/dist/cli/config/duration-constants.d.ts.map +1 -1
- package/dist/cli/config/duration-constants.js +21 -21
- package/dist/cli/docs/agent-operations.d.ts +1 -1
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +2 -1
- package/dist/cli/types/api-responses.d.ts +29 -1
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.d.ts +24 -1
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +54 -1
- package/dist/config/auto-yes-duration.d.ts +56 -0
- package/dist/config/auto-yes-duration.d.ts.map +1 -0
- package/dist/config/auto-yes-duration.js +76 -0
- package/dist/server/src/config/auto-yes-config.js +14 -19
- package/dist/server/src/config/auto-yes-duration.js +76 -0
- package/dist/server/src/lib/auto-yes-state.js +9 -2
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +137 -14
- package/dist/server/src/lib/detection/composer-text.js +215 -11
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice-chrome.js +193 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +8 -191
- package/dist/server/src/lib/detection/upstream-faults.js +19 -2
- package/dist/server/src/lib/hooks/structured-decision-response.js +1 -1
- package/dist/server/src/lib/polling/auto-yes-manual-answer.js +4 -2
- package/dist/server/src/lib/polling/response-checker.js +12 -15
- package/dist/server/src/lib/session/composer-clear.js +56 -4
- package/dist/server/src/lib/session/current-output-builder.js +2 -1
- package/dist/server/src/lib/session/send-attempt-state.js +2 -1
- package/dist/server/src/lib/session/waiting-episode-state-ops.js +152 -0
- package/dist/server/src/lib/session/waiting-episode-state-store.js +202 -0
- package/dist/server/src/lib/session/waiting-episode-state-types.js +6 -0
- package/dist/server/src/lib/session/waiting-episode-state.js +26 -339
- package/dist/server/src/lib/tmux/tmux.js +25 -0
- package/dist/server/src/lib/verification/gate-runner.js +11 -4
- package/docs/en/user-guide/webapp-guide.md +15 -10
- package/package.json +1 -1
- package/release-notes/0.49.0.json +45 -0
- package/.next/server/chunks/6722.js +0 -1
- package/.next/static/chunks/2099-c3e0ab70a1b95dd3.js +0 -1
- package/.next/static/chunks/5371-a549a0d2c2044d41.js +0 -1
- package/.next/static/chunks/6288-09feba367cc2c65e.js +0 -1
- package/.next/static/chunks/9079-406b84d3cc891a87.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9cf1ecc59e36a004.js +0 -1
- /package/.next/static/{PTlAswXLq8PAe2vUJ8dVP → 3_JtAZaKMJEDFIIfkHv4n}/_buildManifest.js +0 -0
- /package/.next/static/{PTlAswXLq8PAe2vUJ8dVP → 3_JtAZaKMJEDFIIfkHv4n}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Claude chrome (task panel, session-diff HUD, preview pane) split out of prompt-detect-multiple-choice.ts (Issue #3924). */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SUMMARY_LINE_PATTERN = void 0;
|
|
5
|
+
exports.findClaudeTaskPanelLines = findClaudeTaskPanelLines;
|
|
6
|
+
exports.findClaudeChrome = findClaudeChrome;
|
|
7
|
+
exports.maskClaudeChrome = maskClaudeChrome;
|
|
8
|
+
const picker_chrome_1 = require("./tools/claude/picker-chrome");
|
|
9
|
+
/**
|
|
10
|
+
* [Issue #704] Pattern for Claude Code v2.1.142 prompt summary lines such as
|
|
11
|
+
* " … +1 pending"
|
|
12
|
+
* " +2 pending"
|
|
13
|
+
* " ↑3 more"
|
|
14
|
+
* " ↓1 more"
|
|
15
|
+
* These lines are rendered BELOW the real options when Claude collapses the
|
|
16
|
+
* remaining choices. They are not selectable options and must be skipped by
|
|
17
|
+
* Pass 2 — otherwise NORMAL_OPTION_PATTERN matches them as option N with
|
|
18
|
+
* label "pending"/"more", and the resulting (number, label) tuple poisons
|
|
19
|
+
* isValidPrecedingOption() so that the real 1./2./3. options above are all
|
|
20
|
+
* rejected (collectedOptions ends up with length 1 and Layer 4 returns
|
|
21
|
+
* no_prompt).
|
|
22
|
+
*
|
|
23
|
+
* Scope of the pattern (FP suppression is critical):
|
|
24
|
+
* - Anchored at line start (^) and requires the trailing keyword to be
|
|
25
|
+
* `pending` or `more` followed by a word boundary (\b). This intentionally
|
|
26
|
+
* prevents matching legitimate option labels that mention these words
|
|
27
|
+
* mid-sentence (e.g. `2. Postpone (will remain pending)`,
|
|
28
|
+
* `3. Show me more`) — those still flow through NORMAL_OPTION_PATTERN.
|
|
29
|
+
* - Optional leading ellipsis (… U+2026) and optional collapse direction
|
|
30
|
+
* marker (+ - ↑ ↓ ⏵ ⏷) accept the variants observed in the wild.
|
|
31
|
+
* - [Issue #1708] `completed` joined `pending`/`more` after a live capture
|
|
32
|
+
* (fixtures/claude-live-1708) showed the task panel collapsing FINISHED
|
|
33
|
+
* rows as `… +2 completed`. That wording was outside the #704 guard, so it
|
|
34
|
+
* reached Pass 2 and was collected as `option 2 / label='completed'` —
|
|
35
|
+
* poisoning isValidPrecedingOption() exactly as `… +1 pending` used to.
|
|
36
|
+
* - The pattern deliberately omits any free `(.+)` capture — it must
|
|
37
|
+
* short-circuit, not extract data.
|
|
38
|
+
*
|
|
39
|
+
* Pattern shape: linear regex with no nested quantifiers — ReDoS safe (S4-001).
|
|
40
|
+
*/
|
|
41
|
+
exports.SUMMARY_LINE_PATTERN = /^\s*[…]?\s*[+\-↑↓⏵⏷]?\s*\d+\s+(?:pending|more|completed)\b/i;
|
|
42
|
+
/**
|
|
43
|
+
* [Issue #1708] Header row of Claude Code's bottom-anchored task panel, e.g.
|
|
44
|
+
* " 7 tasks (0 done, 1 in progress, 6 open)"
|
|
45
|
+
*
|
|
46
|
+
* The panel is an overlay pinned to the LAST rows of the pane, structurally
|
|
47
|
+
* unrelated to whatever the agent is currently blocked on. In a production pane
|
|
48
|
+
* (200x1000, alternate screen) a prompt dialog therefore renders near the top and
|
|
49
|
+
* the panel ~880 blank rows below it — rows `compactBlankRows` collapses to one,
|
|
50
|
+
* putting the panel squarely inside Pass 2's 50-line reverse-scan window.
|
|
51
|
+
*
|
|
52
|
+
* That is only harmless while something trims it away. #704/#807 trim at a
|
|
53
|
+
* footer, but the AskUserQuestion *confirmation* step ("Ready to submit your
|
|
54
|
+
* answers?") renders NEITHER footer, so nothing trims and the panel is the first
|
|
55
|
+
* thing the reverse scan meets. Measured on the live capture in
|
|
56
|
+
* fixtures/claude-live-1708/askuserquestion-submit-taskpanel.txt, two of its rows
|
|
57
|
+
* match NORMAL_OPTION_PATTERN independently:
|
|
58
|
+
* - this header → option 7, label "tasks (2 done, 1 in progress, 4 open)"
|
|
59
|
+
* - "… +2 completed" → option 2, label "completed"
|
|
60
|
+
* Either one alone is enough to make isValidPrecedingOption() reject the real
|
|
61
|
+
* `❯ 1. Submit answers` / `2. Cancel` above it, so collectedOptions never reaches
|
|
62
|
+
* 2 and the frame is published as "no prompt" — Issue #1708's silent timeout.
|
|
63
|
+
*
|
|
64
|
+
* Anchored at line start and requires the literal `tasks (` + `<n> done`, which
|
|
65
|
+
* no option label produced by any supported CLI carries. ReDoS safe (S4-001):
|
|
66
|
+
* bounded repetition, no nesting.
|
|
67
|
+
*/
|
|
68
|
+
const CLAUDE_TASK_PANEL_HEADER_PATTERN = /^\s*\d{1,3}\s+tasks?\s+\(\d+\s+done\b/i;
|
|
69
|
+
/**
|
|
70
|
+
* [Issue #1708] A single task row of the panel described above, e.g. " ◼ Phase 3".
|
|
71
|
+
*
|
|
72
|
+
* Today these rows are inert — `isContinuationLine()` already absorbs them — but
|
|
73
|
+
* only because none of the observed titles begins with a digit. A title like
|
|
74
|
+
* "2. Fix the parser" would render as " ◻ 2. Fix the parser" and be collected as
|
|
75
|
+
* option 2, reintroducing the same poisoning from a direction no single-line
|
|
76
|
+
* guard anticipates. Skipping the whole panel block closes that off structurally
|
|
77
|
+
* instead of one wording at a time.
|
|
78
|
+
*
|
|
79
|
+
* The glyph set is exactly the two forms measured on a live pane — ◼ (in
|
|
80
|
+
* progress) and ◻ (open) — and nothing else. Every extra glyph is a line this
|
|
81
|
+
* guard can delete from the frame, so the set is an allowlist of things seen,
|
|
82
|
+
* not a guess at what the family might contain:
|
|
83
|
+
*
|
|
84
|
+
* - ❯ ● › are DEFAULT_OPTION_PATTERN's cursor indicators.
|
|
85
|
+
* - ⏺ ⎿ ✔ ✗ are the transcript markers TURN_BOUNDARY_PATTERN reads.
|
|
86
|
+
* - ☐ ☑ ☒ are the natural rendering of a MULTI-SELECT choice, and
|
|
87
|
+
* AskUserQuestion has a multiSelect mode. Measured: with those in the set,
|
|
88
|
+
* `☐ 1. Blue / ☑ 2. Green / ☐ 3. Red` under a panel header is claimed as
|
|
89
|
+
* panel content and the prompt above it disappears entirely
|
|
90
|
+
* (isPrompt=false, zero options) — Issue #1708's own failure, recreated by
|
|
91
|
+
* its fix. See the regression test.
|
|
92
|
+
*
|
|
93
|
+
* A completed row never appears (the panel collapses those into `… +N
|
|
94
|
+
* completed`, which SUMMARY_LINE_PATTERN handles), so no done-marker is needed.
|
|
95
|
+
* An unmatched glyph simply ends the block, which is the safe direction: those
|
|
96
|
+
* rows go back to being handled exactly as they were before this guard existed.
|
|
97
|
+
*
|
|
98
|
+
* To widen this, capture a live pane that actually renders the new glyph and add
|
|
99
|
+
* it as a fixture. Do not widen it from the shape of the character.
|
|
100
|
+
*/
|
|
101
|
+
const CLAUDE_TASK_PANEL_ROW_PATTERN = /^\s*[◼◻]\s/;
|
|
102
|
+
/**
|
|
103
|
+
* [Issue #2468] Claude Code 2.1.267's session-diff HUD row, in the two
|
|
104
|
+
* spellings measured on a live pane (tests/fixtures/claude-live-2468):
|
|
105
|
+
* "No changes this session"
|
|
106
|
+
* "+28 files edited before this session (show)"
|
|
107
|
+
*
|
|
108
|
+
* Chrome like the task panel, but a single row and RIGHT-aligned: on a
|
|
109
|
+
* 200-column pane it is ~110 columns of padding and then the text. It is
|
|
110
|
+
* therefore matched on the trimmed row — a reader that slices the row from the
|
|
111
|
+
* left sees nothing but blanks.
|
|
112
|
+
*
|
|
113
|
+
* It broke two readers. Pass 2 below parses `+2 files edited …` as option 2,
|
|
114
|
+
* which poisons the real `1. / 2.` above it the way `… +2 completed` did in
|
|
115
|
+
* #1708. And `findClaudeTranscriptTail` took it for the last transcript row, so
|
|
116
|
+
* `detectClaudeDialog` read it as the footer of the AskUserQuestion
|
|
117
|
+
* confirmation screen — a dialog that draws no footer — and refused a dialog
|
|
118
|
+
* that was open on screen: Auto-Yes, the UI's answer and `commandmate respond`
|
|
119
|
+
* all got `prompt_no_longer_active`.
|
|
120
|
+
*
|
|
121
|
+
* `files?` is the one spelling not seen on a pane: a one-file session was not
|
|
122
|
+
* captured, and the singular is accepted so that it is not the case that
|
|
123
|
+
* regresses. Everything else is literal, for the same reason the glyph set
|
|
124
|
+
* above is an allowlist. Anchored at both ends, no nested quantifiers — ReDoS
|
|
125
|
+
* safe (S4-001).
|
|
126
|
+
*/
|
|
127
|
+
const CLAUDE_SESSION_DIFF_HUD_PATTERN = /^(?:No changes this session|\+\d+ files? edited before this session \(show\))$/;
|
|
128
|
+
/**
|
|
129
|
+
* Line indices belonging to Claude's bottom-pinned panels within [start, end).
|
|
130
|
+
*
|
|
131
|
+
* Two panels, both chrome rather than transcript or prompt:
|
|
132
|
+
* - the task panel (#1708). Walks down from each header row for as long as
|
|
133
|
+
* rows keep looking like panel content (task rows or the collapsed `… +N …`
|
|
134
|
+
* summary). The first line that is neither ends the block, so a panel that
|
|
135
|
+
* abuts other content cannot swallow it.
|
|
136
|
+
* - the session-diff HUD (#2468), a single row claimed on its own.
|
|
137
|
+
*
|
|
138
|
+
* Pass 2 here, `findClaudeTranscriptTail` and `detectClaudeDialog`'s footer
|
|
139
|
+
* guard all read this one set — through {@link findClaudeChrome}, which adds an
|
|
140
|
+
* AskUserQuestion preview pane to it (Issue #2486) — so they cannot disagree
|
|
141
|
+
* about which rows are chrome.
|
|
142
|
+
*
|
|
143
|
+
* @param lines - Full frame, already ANSI/box stripped
|
|
144
|
+
* @param start - First index to consider (inclusive)
|
|
145
|
+
* @param end - Last index to consider (exclusive)
|
|
146
|
+
* @returns Indices Pass 2 must treat as metadata rather than options
|
|
147
|
+
*/
|
|
148
|
+
function findClaudeTaskPanelLines(lines, start, end) {
|
|
149
|
+
const panelLines = new Set();
|
|
150
|
+
for (let i = Math.max(0, start); i < Math.min(end, lines.length); i++) {
|
|
151
|
+
if (CLAUDE_SESSION_DIFF_HUD_PATTERN.test(lines[i].trim())) {
|
|
152
|
+
panelLines.add(i);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (!CLAUDE_TASK_PANEL_HEADER_PATTERN.test(lines[i]))
|
|
156
|
+
continue;
|
|
157
|
+
panelLines.add(i);
|
|
158
|
+
for (let j = i + 1; j < Math.min(end, lines.length); j++) {
|
|
159
|
+
const row = lines[j];
|
|
160
|
+
if (!CLAUDE_TASK_PANEL_ROW_PATTERN.test(row) && !exports.SUMMARY_LINE_PATTERN.test(row))
|
|
161
|
+
break;
|
|
162
|
+
panelLines.add(j);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return panelLines;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Claude's chrome within [start, end). See {@link ClaudeChrome}.
|
|
169
|
+
*
|
|
170
|
+
* @param lines - Full frame, ANSI stripped; box drawing optional
|
|
171
|
+
* @param start - First index to consider (inclusive)
|
|
172
|
+
* @param end - Last index to consider (exclusive)
|
|
173
|
+
*/
|
|
174
|
+
function findClaudeChrome(lines, start, end) {
|
|
175
|
+
const rows = findClaudeTaskPanelLines(lines, start, end);
|
|
176
|
+
const panes = (0, picker_chrome_1.findClaudePreviewPanes)(lines, start, end);
|
|
177
|
+
for (const row of panes.rows)
|
|
178
|
+
rows.add(row);
|
|
179
|
+
return { rows, cuts: panes.cuts };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The frame with Claude's chrome taken out: chrome rows blank, rows shared with
|
|
183
|
+
* the preview pane cut back to the options column. Row indices are unchanged,
|
|
184
|
+
* so a reader of the result can report positions in the original frame.
|
|
185
|
+
*/
|
|
186
|
+
function maskClaudeChrome(lines, chrome) {
|
|
187
|
+
return lines.map((line, i) => {
|
|
188
|
+
if (chrome.rows.has(i))
|
|
189
|
+
return '';
|
|
190
|
+
const cut = chrome.cuts.get(i);
|
|
191
|
+
return cut === undefined ? line : line.slice(0, cut).trimEnd();
|
|
192
|
+
});
|
|
193
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/** Multiple choice prompt detection logic extracted from prompt-detector.ts (Issue #575). */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.joinApprovalTarget = exports.findApprovalContextStart = exports.findApprovalContextRange = exports.APPROVAL_TARGET_MAX_LOOKBACK = void 0;
|
|
5
|
-
exports.findClaudeTaskPanelLines = findClaudeTaskPanelLines;
|
|
6
|
-
exports.findClaudeChrome = findClaudeChrome;
|
|
7
|
-
exports.maskClaudeChrome = maskClaudeChrome;
|
|
4
|
+
exports.maskClaudeChrome = exports.findClaudeTaskPanelLines = exports.findClaudeChrome = exports.joinApprovalTarget = exports.findApprovalContextStart = exports.findApprovalContextRange = exports.APPROVAL_TARGET_MAX_LOOKBACK = void 0;
|
|
8
5
|
exports.isTypedTextFieldOption = isTypedTextFieldOption;
|
|
9
6
|
exports.buildMultipleChoiceResult = buildMultipleChoiceResult;
|
|
10
7
|
exports.detectMultipleChoicePrompt = detectMultipleChoicePrompt;
|
|
@@ -14,12 +11,17 @@ const patterns_1 = require("./tools/claude/patterns");
|
|
|
14
11
|
const prompt_view_1 = require("../../lib/session/prompt-view");
|
|
15
12
|
const option_recommendation_1 = require("./option-recommendation");
|
|
16
13
|
const picker_chrome_1 = require("./tools/claude/picker-chrome");
|
|
14
|
+
const prompt_detect_multiple_choice_chrome_1 = require("./prompt-detect-multiple-choice-chrome");
|
|
17
15
|
const prompt_detect_multiple_choice_approval_context_1 = require("./prompt-detect-multiple-choice-approval-context");
|
|
18
16
|
var prompt_detect_multiple_choice_approval_context_2 = require("./prompt-detect-multiple-choice-approval-context");
|
|
19
17
|
Object.defineProperty(exports, "APPROVAL_TARGET_MAX_LOOKBACK", { enumerable: true, get: function () { return prompt_detect_multiple_choice_approval_context_2.APPROVAL_TARGET_MAX_LOOKBACK; } });
|
|
20
18
|
Object.defineProperty(exports, "findApprovalContextRange", { enumerable: true, get: function () { return prompt_detect_multiple_choice_approval_context_2.findApprovalContextRange; } });
|
|
21
19
|
Object.defineProperty(exports, "findApprovalContextStart", { enumerable: true, get: function () { return prompt_detect_multiple_choice_approval_context_2.findApprovalContextStart; } });
|
|
22
20
|
Object.defineProperty(exports, "joinApprovalTarget", { enumerable: true, get: function () { return prompt_detect_multiple_choice_approval_context_2.joinApprovalTarget; } });
|
|
21
|
+
var prompt_detect_multiple_choice_chrome_2 = require("./prompt-detect-multiple-choice-chrome");
|
|
22
|
+
Object.defineProperty(exports, "findClaudeChrome", { enumerable: true, get: function () { return prompt_detect_multiple_choice_chrome_2.findClaudeChrome; } });
|
|
23
|
+
Object.defineProperty(exports, "findClaudeTaskPanelLines", { enumerable: true, get: function () { return prompt_detect_multiple_choice_chrome_2.findClaudeTaskPanelLines; } });
|
|
24
|
+
Object.defineProperty(exports, "maskClaudeChrome", { enumerable: true, get: function () { return prompt_detect_multiple_choice_chrome_2.maskClaudeChrome; } });
|
|
23
25
|
// ============================================================================
|
|
24
26
|
// Constants
|
|
25
27
|
// ============================================================================
|
|
@@ -64,158 +66,6 @@ const DEFAULT_OPTION_PATTERN = /^\s*[\u276F\u25CF\u203A][^\d]*(\d+)(?:\.(?!\d)|\
|
|
|
64
66
|
* Layer 4 (2+ options), and Layer 5 (SEC-001 question line validation).
|
|
65
67
|
*/
|
|
66
68
|
const NORMAL_OPTION_PATTERN = /^\s*[^\d]{0,3}(\d+)(?:\.(?!\d)|\s+)\s*(.+)$/;
|
|
67
|
-
/**
|
|
68
|
-
* [Issue #1708] Header row of Claude Code's bottom-anchored task panel, e.g.
|
|
69
|
-
* " 7 tasks (0 done, 1 in progress, 6 open)"
|
|
70
|
-
*
|
|
71
|
-
* The panel is an overlay pinned to the LAST rows of the pane, structurally
|
|
72
|
-
* unrelated to whatever the agent is currently blocked on. In a production pane
|
|
73
|
-
* (200x1000, alternate screen) a prompt dialog therefore renders near the top and
|
|
74
|
-
* the panel ~880 blank rows below it — rows `compactBlankRows` collapses to one,
|
|
75
|
-
* putting the panel squarely inside Pass 2's 50-line reverse-scan window.
|
|
76
|
-
*
|
|
77
|
-
* That is only harmless while something trims it away. #704/#807 trim at a
|
|
78
|
-
* footer, but the AskUserQuestion *confirmation* step ("Ready to submit your
|
|
79
|
-
* answers?") renders NEITHER footer, so nothing trims and the panel is the first
|
|
80
|
-
* thing the reverse scan meets. Measured on the live capture in
|
|
81
|
-
* fixtures/claude-live-1708/askuserquestion-submit-taskpanel.txt, two of its rows
|
|
82
|
-
* match NORMAL_OPTION_PATTERN independently:
|
|
83
|
-
* - this header → option 7, label "tasks (2 done, 1 in progress, 4 open)"
|
|
84
|
-
* - "… +2 completed" → option 2, label "completed"
|
|
85
|
-
* Either one alone is enough to make isValidPrecedingOption() reject the real
|
|
86
|
-
* `❯ 1. Submit answers` / `2. Cancel` above it, so collectedOptions never reaches
|
|
87
|
-
* 2 and the frame is published as "no prompt" — Issue #1708's silent timeout.
|
|
88
|
-
*
|
|
89
|
-
* Anchored at line start and requires the literal `tasks (` + `<n> done`, which
|
|
90
|
-
* no option label produced by any supported CLI carries. ReDoS safe (S4-001):
|
|
91
|
-
* bounded repetition, no nesting.
|
|
92
|
-
*/
|
|
93
|
-
const CLAUDE_TASK_PANEL_HEADER_PATTERN = /^\s*\d{1,3}\s+tasks?\s+\(\d+\s+done\b/i;
|
|
94
|
-
/**
|
|
95
|
-
* [Issue #1708] A single task row of the panel described above, e.g. " ◼ Phase 3".
|
|
96
|
-
*
|
|
97
|
-
* Today these rows are inert — `isContinuationLine()` already absorbs them — but
|
|
98
|
-
* only because none of the observed titles begins with a digit. A title like
|
|
99
|
-
* "2. Fix the parser" would render as " ◻ 2. Fix the parser" and be collected as
|
|
100
|
-
* option 2, reintroducing the same poisoning from a direction no single-line
|
|
101
|
-
* guard anticipates. Skipping the whole panel block closes that off structurally
|
|
102
|
-
* instead of one wording at a time.
|
|
103
|
-
*
|
|
104
|
-
* The glyph set is exactly the two forms measured on a live pane — ◼ (in
|
|
105
|
-
* progress) and ◻ (open) — and nothing else. Every extra glyph is a line this
|
|
106
|
-
* guard can delete from the frame, so the set is an allowlist of things seen,
|
|
107
|
-
* not a guess at what the family might contain:
|
|
108
|
-
*
|
|
109
|
-
* - ❯ ● › are DEFAULT_OPTION_PATTERN's cursor indicators.
|
|
110
|
-
* - ⏺ ⎿ ✔ ✗ are the transcript markers TURN_BOUNDARY_PATTERN reads.
|
|
111
|
-
* - ☐ ☑ ☒ are the natural rendering of a MULTI-SELECT choice, and
|
|
112
|
-
* AskUserQuestion has a multiSelect mode. Measured: with those in the set,
|
|
113
|
-
* `☐ 1. Blue / ☑ 2. Green / ☐ 3. Red` under a panel header is claimed as
|
|
114
|
-
* panel content and the prompt above it disappears entirely
|
|
115
|
-
* (isPrompt=false, zero options) — Issue #1708's own failure, recreated by
|
|
116
|
-
* its fix. See the regression test.
|
|
117
|
-
*
|
|
118
|
-
* A completed row never appears (the panel collapses those into `… +N
|
|
119
|
-
* completed`, which SUMMARY_LINE_PATTERN handles), so no done-marker is needed.
|
|
120
|
-
* An unmatched glyph simply ends the block, which is the safe direction: those
|
|
121
|
-
* rows go back to being handled exactly as they were before this guard existed.
|
|
122
|
-
*
|
|
123
|
-
* To widen this, capture a live pane that actually renders the new glyph and add
|
|
124
|
-
* it as a fixture. Do not widen it from the shape of the character.
|
|
125
|
-
*/
|
|
126
|
-
const CLAUDE_TASK_PANEL_ROW_PATTERN = /^\s*[◼◻]\s/;
|
|
127
|
-
/**
|
|
128
|
-
* [Issue #2468] Claude Code 2.1.267's session-diff HUD row, in the two
|
|
129
|
-
* spellings measured on a live pane (tests/fixtures/claude-live-2468):
|
|
130
|
-
* "No changes this session"
|
|
131
|
-
* "+28 files edited before this session (show)"
|
|
132
|
-
*
|
|
133
|
-
* Chrome like the task panel, but a single row and RIGHT-aligned: on a
|
|
134
|
-
* 200-column pane it is ~110 columns of padding and then the text. It is
|
|
135
|
-
* therefore matched on the trimmed row — a reader that slices the row from the
|
|
136
|
-
* left sees nothing but blanks.
|
|
137
|
-
*
|
|
138
|
-
* It broke two readers. Pass 2 below parses `+2 files edited …` as option 2,
|
|
139
|
-
* which poisons the real `1. / 2.` above it the way `… +2 completed` did in
|
|
140
|
-
* #1708. And `findClaudeTranscriptTail` took it for the last transcript row, so
|
|
141
|
-
* `detectClaudeDialog` read it as the footer of the AskUserQuestion
|
|
142
|
-
* confirmation screen — a dialog that draws no footer — and refused a dialog
|
|
143
|
-
* that was open on screen: Auto-Yes, the UI's answer and `commandmate respond`
|
|
144
|
-
* all got `prompt_no_longer_active`.
|
|
145
|
-
*
|
|
146
|
-
* `files?` is the one spelling not seen on a pane: a one-file session was not
|
|
147
|
-
* captured, and the singular is accepted so that it is not the case that
|
|
148
|
-
* regresses. Everything else is literal, for the same reason the glyph set
|
|
149
|
-
* above is an allowlist. Anchored at both ends, no nested quantifiers — ReDoS
|
|
150
|
-
* safe (S4-001).
|
|
151
|
-
*/
|
|
152
|
-
const CLAUDE_SESSION_DIFF_HUD_PATTERN = /^(?:No changes this session|\+\d+ files? edited before this session \(show\))$/;
|
|
153
|
-
/**
|
|
154
|
-
* Line indices belonging to Claude's bottom-pinned panels within [start, end).
|
|
155
|
-
*
|
|
156
|
-
* Two panels, both chrome rather than transcript or prompt:
|
|
157
|
-
* - the task panel (#1708). Walks down from each header row for as long as
|
|
158
|
-
* rows keep looking like panel content (task rows or the collapsed `… +N …`
|
|
159
|
-
* summary). The first line that is neither ends the block, so a panel that
|
|
160
|
-
* abuts other content cannot swallow it.
|
|
161
|
-
* - the session-diff HUD (#2468), a single row claimed on its own.
|
|
162
|
-
*
|
|
163
|
-
* Pass 2 here, `findClaudeTranscriptTail` and `detectClaudeDialog`'s footer
|
|
164
|
-
* guard all read this one set — through {@link findClaudeChrome}, which adds an
|
|
165
|
-
* AskUserQuestion preview pane to it (Issue #2486) — so they cannot disagree
|
|
166
|
-
* about which rows are chrome.
|
|
167
|
-
*
|
|
168
|
-
* @param lines - Full frame, already ANSI/box stripped
|
|
169
|
-
* @param start - First index to consider (inclusive)
|
|
170
|
-
* @param end - Last index to consider (exclusive)
|
|
171
|
-
* @returns Indices Pass 2 must treat as metadata rather than options
|
|
172
|
-
*/
|
|
173
|
-
function findClaudeTaskPanelLines(lines, start, end) {
|
|
174
|
-
const panelLines = new Set();
|
|
175
|
-
for (let i = Math.max(0, start); i < Math.min(end, lines.length); i++) {
|
|
176
|
-
if (CLAUDE_SESSION_DIFF_HUD_PATTERN.test(lines[i].trim())) {
|
|
177
|
-
panelLines.add(i);
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
if (!CLAUDE_TASK_PANEL_HEADER_PATTERN.test(lines[i]))
|
|
181
|
-
continue;
|
|
182
|
-
panelLines.add(i);
|
|
183
|
-
for (let j = i + 1; j < Math.min(end, lines.length); j++) {
|
|
184
|
-
const row = lines[j];
|
|
185
|
-
if (!CLAUDE_TASK_PANEL_ROW_PATTERN.test(row) && !SUMMARY_LINE_PATTERN.test(row))
|
|
186
|
-
break;
|
|
187
|
-
panelLines.add(j);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return panelLines;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Claude's chrome within [start, end). See {@link ClaudeChrome}.
|
|
194
|
-
*
|
|
195
|
-
* @param lines - Full frame, ANSI stripped; box drawing optional
|
|
196
|
-
* @param start - First index to consider (inclusive)
|
|
197
|
-
* @param end - Last index to consider (exclusive)
|
|
198
|
-
*/
|
|
199
|
-
function findClaudeChrome(lines, start, end) {
|
|
200
|
-
const rows = findClaudeTaskPanelLines(lines, start, end);
|
|
201
|
-
const panes = (0, picker_chrome_1.findClaudePreviewPanes)(lines, start, end);
|
|
202
|
-
for (const row of panes.rows)
|
|
203
|
-
rows.add(row);
|
|
204
|
-
return { rows, cuts: panes.cuts };
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* The frame with Claude's chrome taken out: chrome rows blank, rows shared with
|
|
208
|
-
* the preview pane cut back to the options column. Row indices are unchanged,
|
|
209
|
-
* so a reader of the result can report positions in the original frame.
|
|
210
|
-
*/
|
|
211
|
-
function maskClaudeChrome(lines, chrome) {
|
|
212
|
-
return lines.map((line, i) => {
|
|
213
|
-
if (chrome.rows.has(i))
|
|
214
|
-
return '';
|
|
215
|
-
const cut = chrome.cuts.get(i);
|
|
216
|
-
return cut === undefined ? line : line.slice(0, cut).trimEnd();
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
69
|
/**
|
|
220
70
|
* [Issue #2486] An indented row in the options column beside a preview pane.
|
|
221
71
|
*
|
|
@@ -240,39 +90,6 @@ const QUESTION_CONTEXT_LINES = 5;
|
|
|
240
90
|
* not selectable options and must not be parsed as numbered choices.
|
|
241
91
|
*/
|
|
242
92
|
const COLLAPSED_OUTPUT_PATTERN = /^\s*\[[^\]]*\d+\s+lines?\]/i;
|
|
243
|
-
/**
|
|
244
|
-
* [Issue #704] Pattern for Claude Code v2.1.142 prompt summary lines such as
|
|
245
|
-
* " … +1 pending"
|
|
246
|
-
* " +2 pending"
|
|
247
|
-
* " ↑3 more"
|
|
248
|
-
* " ↓1 more"
|
|
249
|
-
* These lines are rendered BELOW the real options when Claude collapses the
|
|
250
|
-
* remaining choices. They are not selectable options and must be skipped by
|
|
251
|
-
* Pass 2 — otherwise NORMAL_OPTION_PATTERN matches them as option N with
|
|
252
|
-
* label "pending"/"more", and the resulting (number, label) tuple poisons
|
|
253
|
-
* isValidPrecedingOption() so that the real 1./2./3. options above are all
|
|
254
|
-
* rejected (collectedOptions ends up with length 1 and Layer 4 returns
|
|
255
|
-
* no_prompt).
|
|
256
|
-
*
|
|
257
|
-
* Scope of the pattern (FP suppression is critical):
|
|
258
|
-
* - Anchored at line start (^) and requires the trailing keyword to be
|
|
259
|
-
* `pending` or `more` followed by a word boundary (\b). This intentionally
|
|
260
|
-
* prevents matching legitimate option labels that mention these words
|
|
261
|
-
* mid-sentence (e.g. `2. Postpone (will remain pending)`,
|
|
262
|
-
* `3. Show me more`) — those still flow through NORMAL_OPTION_PATTERN.
|
|
263
|
-
* - Optional leading ellipsis (… U+2026) and optional collapse direction
|
|
264
|
-
* marker (+ - ↑ ↓ ⏵ ⏷) accept the variants observed in the wild.
|
|
265
|
-
* - [Issue #1708] `completed` joined `pending`/`more` after a live capture
|
|
266
|
-
* (fixtures/claude-live-1708) showed the task panel collapsing FINISHED
|
|
267
|
-
* rows as `… +2 completed`. That wording was outside the #704 guard, so it
|
|
268
|
-
* reached Pass 2 and was collected as `option 2 / label='completed'` —
|
|
269
|
-
* poisoning isValidPrecedingOption() exactly as `… +1 pending` used to.
|
|
270
|
-
* - The pattern deliberately omits any free `(.+)` capture — it must
|
|
271
|
-
* short-circuit, not extract data.
|
|
272
|
-
*
|
|
273
|
-
* Pattern shape: linear regex with no nested quantifiers — ReDoS safe (S4-001).
|
|
274
|
-
*/
|
|
275
|
-
const SUMMARY_LINE_PATTERN = /^\s*[…]?\s*[+\-↑↓⏵⏷]?\s*\d+\s+(?:pending|more|completed)\b/i;
|
|
276
93
|
/**
|
|
277
94
|
* [Issue #704] Pattern for the Claude Code v2.1.142 prompt footer line.
|
|
278
95
|
* Example: " Esc to cancel · Tab to amend → · Herding…"
|
|
@@ -1060,7 +877,7 @@ function collectOptions(lines, scanStart, effectiveEnd) {
|
|
|
1060
877
|
// [Issue #2486] The same reading carries an AskUserQuestion preview pane, whose
|
|
1061
878
|
// rows the scan likewise meets before the top border that identifies it — and
|
|
1062
879
|
// some of which it shares with the options column.
|
|
1063
|
-
const chrome = findClaudeChrome(lines, scanStart, effectiveEnd);
|
|
880
|
+
const chrome = (0, prompt_detect_multiple_choice_chrome_1.findClaudeChrome)(lines, scanStart, effectiveEnd);
|
|
1064
881
|
for (let i = effectiveEnd - 1; i >= scanStart; i--) {
|
|
1065
882
|
// [Issue #1708] Chrome rows are metadata. Same handling as the
|
|
1066
883
|
// COLLAPSED_OUTPUT / SUMMARY guards below: skip without ending the scan, so
|
|
@@ -1139,7 +956,7 @@ function isMetadataRow(state, rawLine, line, paneCut) {
|
|
|
1139
956
|
// against future Claude renderings where the footer line text changes
|
|
1140
957
|
// (S1 still catches) or the summary line wording changes (S2 still
|
|
1141
958
|
// catches via footer trim).
|
|
1142
|
-
if (SUMMARY_LINE_PATTERN.test(line)) {
|
|
959
|
+
if (prompt_detect_multiple_choice_chrome_1.SUMMARY_LINE_PATTERN.test(line)) {
|
|
1143
960
|
state.continuationLineCount++;
|
|
1144
961
|
return true;
|
|
1145
962
|
}
|
|
@@ -54,6 +54,22 @@ exports.UPSTREAM_FAULTS = [
|
|
|
54
54
|
pattern: /\bmaximum context length is \d+ tokens\b/i,
|
|
55
55
|
selfRetrying: false,
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
// codex ends the turn with `Selected model is at capacity` and does not
|
|
59
|
+
// answer (Issue #3936); the same model will not, so the cue is to switch
|
|
60
|
+
// instance or model. Anchored on the sentence, not on "at capacity": the
|
|
61
|
+
// 529 line above says "The API is at capacity" and is `overloaded`.
|
|
62
|
+
//
|
|
63
|
+
// And on codex's own error row, not on the sentence alone: `■ ` at column
|
|
64
|
+
// 0, the shape codex 0.160.0 draws its errors in (measured on
|
|
65
|
+
// `■ Conversation interrupted`). A reply that names or quotes the error —
|
|
66
|
+
// this Issue, its review — is indented (`• ` / ` `), and an input echo
|
|
67
|
+
// starts with `› `; matching those would fail a healthy turn with exit 11.
|
|
68
|
+
// The capacity row itself is unmeasured (the fixture is synthetic).
|
|
69
|
+
id: 'model-capacity',
|
|
70
|
+
pattern: /^■ Selected model is at capacity\b/im,
|
|
71
|
+
selfRetrying: false,
|
|
72
|
+
},
|
|
57
73
|
{ id: 'limit-reached', pattern: /\blimit reached\b/i, selfRetrying: false },
|
|
58
74
|
{ id: 'api-error', pattern: /\bAPI Error(?::|\s+\d{3})/i, selfRetrying: false },
|
|
59
75
|
];
|
|
@@ -90,8 +106,9 @@ function matchUpstreamFault(frame) {
|
|
|
90
106
|
const clean = (0, ansi_1.stripAnsi)(frame);
|
|
91
107
|
for (const fault of exports.UPSTREAM_FAULTS) {
|
|
92
108
|
const match = fault.pattern.exec(clean);
|
|
93
|
-
// The patterns are
|
|
94
|
-
//
|
|
109
|
+
// The patterns are stateless (no /g; only `model-capacity` is anchored, to a
|
|
110
|
+
// line start with /m), so `exec` cannot carry a lastIndex between calls —
|
|
111
|
+
// but reading `index` requires exec regardless.
|
|
95
112
|
if (!match)
|
|
96
113
|
continue;
|
|
97
114
|
const lineStart = clean.lastIndexOf('\n', match.index) + 1;
|
|
@@ -465,5 +465,5 @@ async function answerStructuredDecision({ worktreeId, cliToolId, instanceId, ans
|
|
|
465
465
|
reply: option.reply,
|
|
466
466
|
delivered,
|
|
467
467
|
});
|
|
468
|
-
return { kind: 'answered', decisionId: decision.id, option, delivered };
|
|
468
|
+
return { kind: 'answered', decisionId: decision.id, option, delivered, receipt: delivery };
|
|
469
469
|
}
|
|
@@ -82,8 +82,10 @@ function noteManualSendStarting(ticket, promptData) {
|
|
|
82
82
|
* (the manual-answer sequence moves on): one that read no hold before its
|
|
83
83
|
* last wait does not type over this one.
|
|
84
84
|
*
|
|
85
|
-
* An end nobody reports (
|
|
86
|
-
* the
|
|
85
|
+
* An end nobody reports (a send that threw saying nothing of its keys, a
|
|
86
|
+
* verdict the agent's API gave no receipt for — both only abandoned; a
|
|
87
|
+
* structured verdict `/prompt-response` refused) leaves the hold as it was. A verdict delivered over the
|
|
88
|
+
* agent's API reports its receipt like a send (Issue #3925).
|
|
87
89
|
*/
|
|
88
90
|
function settleManualAnswer(ticket, outcome, at = Date.now()) {
|
|
89
91
|
const { compositeKey, attempt } = ticket;
|
|
@@ -63,6 +63,7 @@ const prompt_push_gate_1 = require("../../lib/push/prompt-push-gate");
|
|
|
63
63
|
// Issue #2000: deep path for the same reason as the two imports above.
|
|
64
64
|
const failure_push_notifier_1 = require("../../lib/push/failure-push-notifier");
|
|
65
65
|
const upstream_faults_1 = require("../../lib/detection/upstream-faults");
|
|
66
|
+
const realtime_snippet_1 = require("../../lib/realtime-snippet");
|
|
66
67
|
const waiting_push_notifier_1 = require("../../lib/push/waiting-push-notifier");
|
|
67
68
|
const waiting_episode_binding_1 = require("../../lib/session/waiting-episode-binding");
|
|
68
69
|
const waiting_episode_state_1 = require("../../lib/session/waiting-episode-state");
|
|
@@ -71,19 +72,6 @@ const auto_yes_state_1 = require("../../lib/auto-yes-state");
|
|
|
71
72
|
const auto_yes_poller_state_1 = require("./auto-yes-poller-state");
|
|
72
73
|
const task_transition_service_1 = require("../../lib/tasks/task-transition-service");
|
|
73
74
|
const response_checker_blank_response_1 = require("./response-checker-blank-response");
|
|
74
|
-
/**
|
|
75
|
-
* How many rows from the bottom of a capture the upstream-fault check reads
|
|
76
|
-
* (Issue #2000).
|
|
77
|
-
*
|
|
78
|
-
* The same 100 as `current-output-builder`'s `realtimeSnippet`, and for the
|
|
79
|
-
* reason #1839 gives there: the wider capture keeps a banner from an hour ago
|
|
80
|
-
* in scope, and "is this happening now" is the only question worth ringing a
|
|
81
|
-
* phone about. Keeping the two windows equal also means the notification and
|
|
82
|
-
* the `upstreamFault` field an operator reads in `capture --json` are judging
|
|
83
|
-
* the same rows — a fault that rang but is invisible in the payload next to it
|
|
84
|
-
* is unverifiable.
|
|
85
|
-
*/
|
|
86
|
-
const UPSTREAM_FAULT_SCAN_ROWS = 100;
|
|
87
75
|
/**
|
|
88
76
|
* Raise a push notification when this frame shows a NEW upstream fault
|
|
89
77
|
* (Issue #2000).
|
|
@@ -100,8 +88,17 @@ const UPSTREAM_FAULT_SCAN_ROWS = 100;
|
|
|
100
88
|
* anything, so the level can be handed over on every poll.
|
|
101
89
|
*/
|
|
102
90
|
function observeUpstreamFaultForPush(worktreeId, cliToolId, instanceId, output) {
|
|
103
|
-
|
|
104
|
-
|
|
91
|
+
// The rows `current-output-builder` publishes as `realtimeSnippet` and judges
|
|
92
|
+
// `upstreamFault` on (#1839), cut by the same function rather than a copy of
|
|
93
|
+
// its window. The reason is #1839's: the wider capture keeps a banner from an
|
|
94
|
+
// hour ago in scope, and "is this happening now" is the only question worth
|
|
95
|
+
// ringing a phone about. Sharing the cut also means the notification and the
|
|
96
|
+
// `upstreamFault` an operator reads in `capture --json` judge the same rows —
|
|
97
|
+
// a fault that rang but is invisible in the payload next to it is
|
|
98
|
+
// unverifiable. Issue #3936: the plain last 100 rows (#2000) drifted from the
|
|
99
|
+
// snippet when #2768 anchored it to the last row with content, so an inline
|
|
100
|
+
// codex pane showed `model-capacity` in the capture and never rang.
|
|
101
|
+
const match = (0, upstream_faults_1.matchUpstreamFault)((0, realtime_snippet_1.buildRealtimeSnippet)(output));
|
|
105
102
|
// Fire-and-forget, like every other push call in this file: the poller must
|
|
106
103
|
// not slow down or break because a notification could not be delivered.
|
|
107
104
|
void (0, failure_push_notifier_1.notifyUpstreamFaultPush)({
|
|
@@ -30,13 +30,60 @@
|
|
|
30
30
|
* frame back to the browser.
|
|
31
31
|
*/
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.COMPOSER_CLEAR_REDRAW_DELAY_MS = exports.MAX_COMPOSER_CLEAR_PASSES = void 0;
|
|
33
|
+
exports.COMPOSER_CLEAR_REDRAW_DELAY_MS = exports.MAX_COMPOSER_CLEAR_PASSES = exports.findCodexLiveComposerRow = exports.codexDialogBelow = void 0;
|
|
34
|
+
exports.isCodexPasteTokenOnly = isCodexPasteTokenOnly;
|
|
34
35
|
exports.clearComposer = clearComposer;
|
|
35
36
|
const tmux_1 = require("../../lib/tmux/tmux");
|
|
36
37
|
const tmux_capture_cache_1 = require("../../lib/tmux/tmux-capture-cache");
|
|
37
38
|
const composer_text_1 = require("../../lib/detection/composer-text");
|
|
39
|
+
Object.defineProperty(exports, "codexDialogBelow", { enumerable: true, get: function () { return composer_text_1.codexDialogBelow; } });
|
|
40
|
+
Object.defineProperty(exports, "findCodexLiveComposerRow", { enumerable: true, get: function () { return composer_text_1.findCodexLiveComposerRow; } });
|
|
38
41
|
const logger_1 = require("../../lib/logger");
|
|
39
42
|
const logger = (0, logger_1.createLogger)('composer-clear');
|
|
43
|
+
/**
|
|
44
|
+
* The composer as this loop reads it.
|
|
45
|
+
*
|
|
46
|
+
* {@link extractLiveComposerText} — the reading the unsent bar is drawn from
|
|
47
|
+
* (Issue #3927) — and for codex one more reading when it finds no
|
|
48
|
+
* composer (Issue #3906): a body of more paragraphs than the extractor's four
|
|
49
|
+
* blocks — the partly arrived paste the sender reads as `pasting` and withdraws
|
|
50
|
+
* through this function — is still a composer holding text. Only `content` is
|
|
51
|
+
* taken from that reading; anything else stays `no_composer`, so a frame this
|
|
52
|
+
* reading cannot vouch for is never reported as emptied.
|
|
53
|
+
*
|
|
54
|
+
* A composer the extractor does find is still not the live one when a dialog
|
|
55
|
+
* option is drawn below it ({@link codexDialogBelow}): that frame is
|
|
56
|
+
* `no_composer`, so no `C-e`+`C-u` goes into the dialog.
|
|
57
|
+
*/
|
|
58
|
+
function readClearComposer(capture, cliToolId) {
|
|
59
|
+
const read = (0, composer_text_1.extractLiveComposerText)(capture, cliToolId);
|
|
60
|
+
if (cliToolId !== 'codex' || read.state !== 'no_composer')
|
|
61
|
+
return read;
|
|
62
|
+
const rows = capture.split('\n');
|
|
63
|
+
const row = (0, composer_text_1.findCodexLiveComposerRow)(rows);
|
|
64
|
+
if (row < 0)
|
|
65
|
+
return read;
|
|
66
|
+
const held = (0, composer_text_1.readCodexComposerFrom)(rows, row);
|
|
67
|
+
return held.state === 'content' ? held : read;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A codex composer holding exactly one collapsed paste placeholder and nothing
|
|
71
|
+
* else (Issue #3937) — the one frame a pass sends `C-e`+`BSpace` for instead of
|
|
72
|
+
* `C-e`+`C-u` ({@link clearComposerPasteToken}).
|
|
73
|
+
*
|
|
74
|
+
* Anything more than the bare placeholder keeps the `C-e`+`C-u` pass, because
|
|
75
|
+
* none of it has been measured on a live codex: a placeholder with typed text
|
|
76
|
+
* after it (`C-e`+`BSpace` would eat the last typed character, not the
|
|
77
|
+
* placeholder), two placeholders, and a body quoting the placeholder's wording
|
|
78
|
+
* among other text. A body that is nothing but that wording typed out is not
|
|
79
|
+
* told apart here; one `BSpace` takes its `]`, and from the next read on it is
|
|
80
|
+
* ordinary text again — `C-e`+`C-u` — so the read-back still decides.
|
|
81
|
+
*/
|
|
82
|
+
const CODEX_PASTE_TOKEN_ONLY = /^\[Pasted Content \d+ chars?\]$/;
|
|
83
|
+
/** Whether a pass over this reading should send `C-e`+`BSpace` (Issue #3937). */
|
|
84
|
+
function isCodexPasteTokenOnly(read, cliToolId) {
|
|
85
|
+
return cliToolId === 'codex' && read.state === 'content' && CODEX_PASTE_TOKEN_ONLY.test(read.text.trim());
|
|
86
|
+
}
|
|
40
87
|
/**
|
|
41
88
|
* Upper bound on `C-e`+`C-u` passes.
|
|
42
89
|
*
|
|
@@ -67,15 +114,20 @@ async function clearComposer(sessionName, cliToolId, options = {}) {
|
|
|
67
114
|
const delay = options.delay ?? defaultDelay;
|
|
68
115
|
const maxPasses = options.maxPasses ?? exports.MAX_COMPOSER_CLEAR_PASSES;
|
|
69
116
|
let passes = 0;
|
|
70
|
-
let result = (
|
|
117
|
+
let result = readClearComposer(await capture(sessionName), cliToolId);
|
|
71
118
|
// Snapshot before the first pass: every later read shows less of it (a pass
|
|
72
119
|
// eats one row), so this is the only moment the full text exists.
|
|
73
120
|
const discardedText = result.state === 'content' ? result.text : '';
|
|
74
121
|
while (result.state === 'content' && passes < maxPasses) {
|
|
75
|
-
|
|
122
|
+
if (isCodexPasteTokenOnly(result, cliToolId)) {
|
|
123
|
+
await (options.sendClearPasteToken ?? tmux_1.clearComposerPasteToken)(sessionName);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
await sendClear(sessionName);
|
|
127
|
+
}
|
|
76
128
|
passes++;
|
|
77
129
|
await delay(exports.COMPOSER_CLEAR_REDRAW_DELAY_MS);
|
|
78
|
-
result = (
|
|
130
|
+
result = readClearComposer(await capture(sessionName), cliToolId);
|
|
79
131
|
}
|
|
80
132
|
// Whatever happened above, the cached frame is now stale: the poller and the
|
|
81
133
|
// WebSocket push both read through `captureSessionOutput`, and serving the
|
|
@@ -914,7 +914,8 @@ async function buildPayload(db, worktreeId, cliToolId, instanceId) {
|
|
|
914
914
|
// Issue #1879: structural, and computed here next to the other frame readers
|
|
915
915
|
// rather than inside a status branch — the bar it feeds must appear on the
|
|
916
916
|
// strength of what is in the box, never on the strength of a status verdict.
|
|
917
|
-
|
|
917
|
+
// Issue #3927: the live box — a codex composer left above a dialog is not one.
|
|
918
|
+
const composer = (0, composer_text_1.extractLiveComposerText)(output, cliToolId);
|
|
918
919
|
// Issue #1839: judged on exactly what is published as `realtimeSnippet`, not
|
|
919
920
|
// on `output`. The wider capture keeps a banner from an hour ago in scope, and
|
|
920
921
|
// a fault the operator cannot see in the payload next to it is unverifiable.
|
|
@@ -134,7 +134,8 @@ function classifySendFailure(error) {
|
|
|
134
134
|
// The readiness waits of every tool throw before typing anything.
|
|
135
135
|
if (/prompt not ready/i.test(message))
|
|
136
136
|
return { state: 'not-sent', reason: 'prompt-not-ready' };
|
|
137
|
-
// #1880: refused before the body, after only the clear keys (C-e / C-u
|
|
137
|
+
// #1880: refused before the body, after only the clear keys (C-e / C-u, or C-e / BSpace
|
|
138
|
+
// for a codex composer holding only a paste placeholder, #3937).
|
|
138
139
|
if (/still holds unsent text after \d+ clear passes/.test(message)) {
|
|
139
140
|
return { state: 'not-sent', reason: 'composer-not-empty' };
|
|
140
141
|
}
|