commandmate 0.49.0 → 0.49.2
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 +251 -248
- package/.next/app-path-routes-manifest.json +22 -21
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +8 -0
- 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 +2 -2
- 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 +2 -2
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.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/[relayId]/route.js +1 -0
- package/.next/server/app/api/relays/[relayId]/route.js.nft.json +1 -0
- package/.next/server/app/api/relays/[relayId]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/relays/route.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 +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.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.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route.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.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.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.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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route.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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route.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 +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 +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 +2 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/logs/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route.js.nft.json +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]/prompt-response/strict/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/strict/route.js.nft.json +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.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.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.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.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 +144 -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 +2 -2
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_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.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 +103 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/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.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_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 +11 -11
- 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 +22 -21
- package/.next/server/chunks/1062.js +1 -0
- package/.next/server/chunks/{936.js → 110.js} +1 -1
- package/.next/server/chunks/1501.js +5 -4
- package/.next/server/chunks/1656.js +2 -2
- package/.next/server/chunks/1957.js +2 -0
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2897.js +1 -0
- package/.next/server/chunks/3417.js +1 -0
- package/.next/server/chunks/3505.js +1 -1
- package/.next/server/chunks/{4280.js → 3756.js} +1 -1
- package/.next/server/chunks/3801.js +31 -0
- package/.next/server/chunks/4911.js +1 -0
- package/.next/server/chunks/4936.js +1 -0
- package/.next/server/chunks/5209.js +2 -1
- package/.next/server/chunks/5354.js +1 -1
- package/.next/server/chunks/{9130.js → 5630.js} +1 -1
- package/.next/server/chunks/610.js +1 -0
- 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/6781.js +1 -1
- package/.next/server/chunks/6808.js +4 -0
- package/.next/server/chunks/7023.js +14 -0
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8581.js +8 -8
- package/.next/server/chunks/8728.js +2 -2
- package/.next/server/chunks/8782.js +1 -1
- package/.next/server/chunks/8807.js +63 -53
- package/.next/server/chunks/8918.js +103 -1
- package/.next/server/chunks/9198.js +144 -0
- package/.next/server/chunks/9390.js +1 -1
- package/.next/server/chunks/9584.js +1 -1
- package/.next/server/chunks/9627.js +1 -1
- package/.next/server/chunks/9731.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/{2407-ab92ae9837c39dce.js → 2407-07a237575e1ceb9e.js} +1 -1
- package/.next/static/chunks/3931-61ad1a4fc0208d0e.js +1 -0
- package/.next/static/chunks/5794-7975b8e51e731b19.js +1 -0
- package/.next/static/chunks/6966-d8dc6032410ffaba.js +1 -0
- package/.next/static/chunks/7197-ed3b3d99ac3bd450.js +1 -0
- package/.next/static/chunks/7636-a48cf0f7110250c5.js +1 -0
- package/.next/static/chunks/app/api/agents/update/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/agents/versions/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/app/release-notes/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/app/update/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/app/update-check/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/auth/login/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/auth/status/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/capabilities/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/daily-summary/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/daily-summary/status/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/external-apps/[id]/health/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/external-apps/[id]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/external-apps/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/fs/browse/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/fs/recent-paths/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/hooks/agent-event/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/hooks/claude-done/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/hooks/permission-request/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/metrics/vibe/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/ollama/models/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/push/escalation/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/push/subscriptions/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/push/vapid/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/relays/[relayId]/cancel/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/relays/[relayId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/relays/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/remote/pair/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/clone/[jobId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/clone/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/excluded/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/restore/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/scan/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/sync/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/repositories/validate-path/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/settings/default-agents/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/settings/default-surface-mode/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/sidebar/group-order/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/skills/[id]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/skills/installations/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/skills/operations/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/skills/reindex/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/skills/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/slash-commands/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/tasks/[taskId]/cancel/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/tasks/[taskId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/templates/[id]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/templates/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/verification/runs/[runId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/verification/runs/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/auto-yes/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/capture/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/clear-composer/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cli-reference/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cli-tool/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cmate/schedules/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/current-output/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/direct-input/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/env/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/[logId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/files/[...path]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/create/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/delete/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branches/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/checkout/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/commit/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/diff/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/fetch/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/log/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/pull/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/push/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/reset/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/revert/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/show/[commitHash]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stage/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/staged/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/[index]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/apply/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/pop/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/push/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/status/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/unstage/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/working-diff/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/instances/notes/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/instances/opencode/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/interrupt/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/kill-session/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/logs/[filename]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/logs/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/marp-render/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/memos/[memoId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/memos/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/messages/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/opencode/diff/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/opencode/session/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/opencode/share/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/strict/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/resolve-target/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/respond/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/[scheduleId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/active/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/search/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/send/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/install/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/plan/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update-plan/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/slash-commands/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/special-keys/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tasks/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/terminal/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/timers/history/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/timers/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/todos/[todoId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/todos/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tree/[...path]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tree/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/upload/[...path]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/config/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/[runId]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/viewed/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/apply-default-agents/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/layout-4f64316957b0a8e5.js +1 -0
- package/.next/static/chunks/app/manifest.webmanifest/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/more/page-ff5af5ca48774c89.js +1 -0
- package/.next/static/chunks/app/page-b4863bfe0452e09d.js +1 -0
- package/.next/static/chunks/app/proxy/[...path]/route-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/review/page-ff3efb96fdf9bb4b.js +1 -0
- package/.next/static/chunks/app/sessions/page-c6eb7c02db2f4c80.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/layout-a4f129c451de7859.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-33afc6aa96289fb0.js +1 -0
- package/.next/static/css/c1e307d05daf93b6.css +1 -0
- package/.next/trace +13 -13
- package/.next/types/app/api/relays/[relayId]/route.ts +347 -0
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/dist/cli/commands/ask.d.ts.map +1 -1
- package/dist/cli/commands/ask.js +233 -17
- package/dist/cli/commands/attach.d.ts +4 -1
- package/dist/cli/commands/attach.d.ts.map +1 -1
- package/dist/cli/commands/attach.js +19 -7
- package/dist/cli/commands/command-target.d.ts +7 -0
- package/dist/cli/commands/command-target.d.ts.map +1 -1
- package/dist/cli/commands/command-target.js +10 -0
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +38 -2
- package/dist/cli/commands/message-dispatch.d.ts +39 -5
- package/dist/cli/commands/message-dispatch.d.ts.map +1 -1
- package/dist/cli/commands/message-dispatch.js +100 -8
- package/dist/cli/commands/relays.d.ts +26 -2
- package/dist/cli/commands/relays.d.ts.map +1 -1
- package/dist/cli/commands/relays.js +70 -5
- package/dist/cli/commands/respond-strict.d.ts.map +1 -1
- package/dist/cli/commands/respond-strict.js +22 -5
- package/dist/cli/commands/respond.d.ts +12 -0
- package/dist/cli/commands/respond.d.ts.map +1 -1
- package/dist/cli/commands/respond.js +83 -18
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +14 -8
- package/dist/cli/commands/wait-hook-stop-settle.d.ts +66 -0
- package/dist/cli/commands/wait-hook-stop-settle.d.ts.map +1 -0
- package/dist/cli/commands/wait-hook-stop-settle.js +150 -0
- package/dist/cli/commands/wait-instance.d.ts +29 -0
- package/dist/cli/commands/wait-instance.d.ts.map +1 -0
- package/dist/cli/commands/wait-instance.js +56 -0
- package/dist/cli/commands/wait-notices.d.ts +87 -0
- package/dist/cli/commands/wait-notices.d.ts.map +1 -0
- package/dist/cli/commands/wait-notices.js +301 -0
- package/dist/cli/commands/wait-poll-bookkeeping.d.ts +15 -0
- package/dist/cli/commands/wait-poll-bookkeeping.d.ts.map +1 -0
- package/dist/cli/commands/wait-poll-bookkeeping.js +57 -0
- package/dist/cli/commands/wait-poll-state.d.ts +136 -0
- package/dist/cli/commands/wait-poll-state.d.ts.map +1 -0
- package/dist/cli/commands/wait-poll-state.js +6 -0
- package/dist/cli/commands/wait-timing.d.ts +46 -0
- package/dist/cli/commands/wait-timing.d.ts.map +1 -0
- package/dist/cli/commands/wait-timing.js +99 -0
- package/dist/cli/commands/wait-turn-boundary.d.ts +284 -0
- package/dist/cli/commands/wait-turn-boundary.d.ts.map +1 -0
- package/dist/cli/commands/wait-turn-boundary.js +427 -0
- package/dist/cli/commands/wait-verify.d.ts +51 -0
- package/dist/cli/commands/wait-verify.d.ts.map +1 -0
- package/dist/cli/commands/wait-verify.js +128 -0
- package/dist/cli/commands/wait.d.ts +6 -105
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +240 -968
- package/dist/cli/commands/whoami.d.ts.map +1 -1
- package/dist/cli/commands/whoami.js +3 -1
- package/dist/cli/docs/agent-operations.d.ts +2 -2
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +24 -1
- package/dist/cli/types/api-responses.d.ts +49 -3
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/utils/api-client.d.ts +69 -1
- package/dist/cli/utils/api-client.d.ts.map +1 -1
- package/dist/cli/utils/api-client.js +226 -18
- package/dist/cli/utils/follow-up-command.d.ts +38 -0
- package/dist/cli/utils/follow-up-command.d.ts.map +1 -0
- package/dist/cli/utils/follow-up-command.js +57 -0
- package/dist/cli/utils/reply-ledger.d.ts +80 -1
- package/dist/cli/utils/reply-ledger.d.ts.map +1 -1
- package/dist/cli/utils/reply-ledger.js +147 -2
- package/dist/cli/utils/server-capabilities.d.ts +18 -0
- package/dist/cli/utils/server-capabilities.d.ts.map +1 -1
- package/dist/cli/utils/server-capabilities.js +36 -3
- package/dist/cli/utils/session-target.d.ts +1 -1
- package/dist/cli/utils/session-target.d.ts.map +1 -1
- package/dist/cli/utils/session-target.js +3 -0
- package/dist/cli/utils/verify-runner.d.ts +7 -0
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +51 -15
- package/dist/lib/cli-tools/session-name.d.ts.map +1 -1
- package/dist/lib/cli-tools/session-name.js +11 -3
- package/dist/lib/session/send-attempt-types.d.ts +48 -1
- package/dist/lib/session/send-attempt-types.d.ts.map +1 -1
- package/dist/lib/session/send-attempt-types.js +18 -1
- package/dist/lib/verification/session-kill-ledger.d.ts +110 -0
- package/dist/lib/verification/session-kill-ledger.d.ts.map +1 -0
- package/dist/lib/verification/session-kill-ledger.js +174 -0
- package/dist/server/locales/en/worktree.json +20 -3
- package/dist/server/locales/ja/worktree.json +20 -3
- package/dist/server/src/config/cli-tool-timing-config.js +1 -7
- package/dist/server/src/lib/auto-yes-poller.js +54 -11
- package/dist/server/src/lib/cli-tools/antigravity.js +45 -9
- package/dist/server/src/lib/cli-tools/base.js +39 -0
- package/dist/server/src/lib/cli-tools/codex.js +67 -15
- package/dist/server/src/lib/cli-tools/command-code-plan-review.js +24 -2
- package/dist/server/src/lib/cli-tools/command-code.js +7 -3
- package/dist/server/src/lib/cli-tools/copilot.js +125 -33
- package/dist/server/src/lib/cli-tools/gemini.js +37 -10
- package/dist/server/src/lib/cli-tools/launch-keys.js +39 -0
- package/dist/server/src/lib/cli-tools/opencode-v2.js +6 -2
- package/dist/server/src/lib/cli-tools/opencode.js +60 -15
- package/dist/server/src/lib/cli-tools/session-name.js +11 -3
- package/dist/server/src/lib/cli-tools/submit-composer-read.js +590 -0
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +512 -620
- package/dist/server/src/lib/cli-tools/vibe-local.js +4 -2
- package/dist/server/src/lib/db/db-migration-path.js +63 -2
- package/dist/server/src/lib/db/migrations/index.js +2 -0
- package/dist/server/src/lib/db/migrations/pre-migration-backup.js +266 -0
- package/dist/server/src/lib/db/migrations/runner.js +37 -4
- package/dist/server/src/lib/db/migrations/v62-session-relays-bind.js +102 -0
- package/dist/server/src/lib/db/relay-bind-db.js +437 -0
- package/dist/server/src/lib/db/relay-db.js +59 -96
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +5 -4
- package/dist/server/src/lib/detection/upstream-faults.js +40 -6
- package/dist/server/src/lib/hooks/sources/copilot/hook-settings.js +2 -3
- package/dist/server/src/lib/hooks/sources/opencode/client.js +12 -6
- package/dist/server/src/lib/polling/response-checker.js +7 -5
- package/dist/server/src/lib/prompt-answer-errors.js +362 -0
- package/dist/server/src/lib/prompt-answer-sender.js +264 -288
- package/dist/server/src/lib/push/failure-episode-state.js +4 -1
- package/dist/server/src/lib/realtime/terminal-broadcast.js +1 -4
- package/dist/server/src/lib/realtime-snippet.js +54 -0
- package/dist/server/src/lib/relay/relay-bind.js +294 -0
- package/dist/server/src/lib/relay/relay-boot.js +51 -0
- package/dist/server/src/lib/relay/relay-bound-reply.js +519 -0
- package/dist/server/src/lib/relay/relay-delivery.js +134 -220
- package/dist/server/src/lib/relay/relay-messages.js +39 -4
- package/dist/server/src/lib/relay/relay-overlap.js +92 -0
- package/dist/server/src/lib/relay/relay-system-line.js +3 -2
- package/dist/server/src/lib/relay/types.js +12 -1
- package/dist/server/src/lib/session/agent-event-state.js +4 -0
- package/dist/server/src/lib/session/agent-session-lifecycle.js +23 -0
- package/dist/server/src/lib/session/claude-session.js +59 -8
- package/dist/server/src/lib/session/composer-clear.js +54 -1
- package/dist/server/src/lib/session/current-output-builder.js +26 -8
- package/dist/server/src/lib/session/current-output-history-writers.js +1 -1
- package/dist/server/src/lib/session/instance-input-lock.js +664 -0
- package/dist/server/src/lib/session/prompt-submit-receipt.js +155 -0
- package/dist/server/src/lib/session/prompt-waiting-guard.js +10 -3
- package/dist/server/src/lib/session/send-attempt-state.js +65 -0
- package/dist/server/src/lib/session/send-attempt-types.js +18 -1
- package/dist/server/src/lib/session/send-user-message.js +84 -14
- package/dist/server/src/lib/session/session-launch-flight.js +243 -0
- package/dist/server/src/lib/session/session-starting-state.js +40 -4
- package/dist/server/src/lib/session/structured-prompt.js +12 -9
- package/dist/server/src/lib/session/waiting-episode-binding.js +4 -2
- package/dist/server/src/lib/session-cleanup.js +32 -0
- package/dist/server/src/lib/session-key-sender.js +0 -27
- package/dist/server/src/lib/verification/env-clean-gate.js +129 -0
- package/dist/server/src/lib/verification/env-snapshot.js +11 -2
- package/dist/server/src/lib/verification/session-kill-ledger.js +174 -0
- package/package.json +5 -1
- package/release-notes/0.49.1.json +66 -0
- package/release-notes/0.49.2.json +49 -0
- package/.next/server/chunks/1210.js +0 -1
- package/.next/server/chunks/1945.js +0 -14
- package/.next/server/chunks/1990.js +0 -103
- package/.next/server/chunks/4194.js +0 -67
- package/.next/server/chunks/5845.js +0 -1
- package/.next/server/chunks/720.js +0 -103
- package/.next/server/chunks/7604.js +0 -2
- package/.next/server/chunks/9540.js +0 -1
- package/.next/static/chunks/4768-ad6f82f034f21eae.js +0 -1
- package/.next/static/chunks/5794-80ac3f06f098ea29.js +0 -1
- package/.next/static/chunks/6654-f3534f35f16644bc.js +0 -1
- package/.next/static/chunks/7665-e9ef6e0a85b9fa49.js +0 -1
- package/.next/static/chunks/8606-cf6bdbff44a764cf.js +0 -1
- package/.next/static/chunks/9079-987939d231704218.js +0 -1
- package/.next/static/chunks/app/api/agents/update/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/agents/versions/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/app/release-notes/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/app/update/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/app/update-check/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/auth/login/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/auth/status/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/capabilities/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/daily-summary/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/daily-summary/status/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/external-apps/[id]/health/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/external-apps/[id]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/external-apps/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/fs/browse/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/fs/recent-paths/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/hooks/agent-event/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/hooks/claude-done/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/hooks/permission-request/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/metrics/vibe/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/ollama/models/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/push/escalation/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/push/subscriptions/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/push/vapid/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/relays/[relayId]/cancel/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/relays/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/remote/pair/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/clone/[jobId]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/clone/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/excluded/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/restore/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/scan/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/sync/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/repositories/validate-path/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/settings/default-agents/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/settings/default-surface-mode/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/sidebar/group-order/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/skills/[id]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/skills/installations/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/skills/operations/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/skills/reindex/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/skills/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/slash-commands/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/tasks/[taskId]/cancel/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/tasks/[taskId]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/templates/[id]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/templates/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/verification/runs/[runId]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/verification/runs/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/auto-yes/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/capture/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/clear-composer/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cli-reference/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cli-tool/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cmate/schedules/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/current-output/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/direct-input/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/env/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/[logId]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/files/[...path]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/create/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/delete/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branches/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/checkout/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/commit/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/diff/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/fetch/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/log/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/pull/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/push/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/reset/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/revert/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/show/[commitHash]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stage/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/staged/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/[index]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/apply/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/pop/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/push/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/status/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/unstage/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/working-diff/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/instances/notes/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/instances/opencode/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/interrupt/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/kill-session/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/logs/[filename]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/logs/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/marp-render/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/memos/[memoId]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/memos/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/messages/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/opencode/diff/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/opencode/session/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/opencode/share/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/strict/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/resolve-target/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/respond/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/[scheduleId]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/active/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/search/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/send/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/install/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/plan/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update-plan/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/slash-commands/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/special-keys/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tasks/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/terminal/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/timers/history/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/timers/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/todos/[todoId]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/todos/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tree/[...path]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tree/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/upload/[...path]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/config/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/[runId]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/viewed/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/apply-default-agents/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/layout-aa8e66164a4367af.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/more/page-87c84987b2765213.js +0 -1
- package/.next/static/chunks/app/page-1b820c421db48c2c.js +0 -1
- package/.next/static/chunks/app/proxy/[...path]/route-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/review/page-e2fd4a590646a2d8.js +0 -1
- package/.next/static/chunks/app/sessions/page-cfd645305e25fd33.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/layout-c81284a9d8b35304.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-a8d7886703ffc7b4.js +0 -1
- package/.next/static/css/db8959555c3d9c96.css +0 -1
- /package/.next/static/{3_JtAZaKMJEDFIIfkHv4n → yGatWDsS532awSs51kXyd}/_buildManifest.js +0 -0
- /package/.next/static/{3_JtAZaKMJEDFIIfkHv4n → yGatWDsS532awSs51kXyd}/_ssgManifest.js +0 -0
|
@@ -79,6 +79,23 @@
|
|
|
79
79
|
* codex, Enter now waits until the composer shows the typed body
|
|
80
80
|
* ({@link confirmTypedBodyLanded}); a body that never appears THROWS before
|
|
81
81
|
* Enter, as #2464 does for a paste.
|
|
82
|
+
*
|
|
83
|
+
* Issue #3945 is two sends to one instance at once. A send that withdrew its
|
|
84
|
+
* body, or cleared before typing, erased whatever was in the composer — another
|
|
85
|
+
* send's body included — and a codex placeholder carries only a count, so a
|
|
86
|
+
* late one could not be told from the next send's. A send now runs as the
|
|
87
|
+
* instance's only writer (`lib/session/instance-input-lock`), re-reads the pane
|
|
88
|
+
* once it owns it ({@link recheckComposerBeforeInput}),
|
|
89
|
+
* checks it still owns the composer before every key, watches an `unseen`
|
|
90
|
+
* paste a little longer before giving up, and leaves a mark for a body that
|
|
91
|
+
* may still arrive, which the next send waits out ({@link waitOutCarriedPaste}).
|
|
92
|
+
*
|
|
93
|
+
* Issue #4035 is a claude session's first prompt reported sent when it was
|
|
94
|
+
* not. Keys typed right after the prompt appeared were drawn ~1 s later and
|
|
95
|
+
* their Enter was lost, while the read-back still showed the dim placeholder
|
|
96
|
+
* (rule F). On that first prompt a B or F verdict now waits for claude's own
|
|
97
|
+
* `UserPromptSubmit` ({@link awaitPromptReport}), and a later send that clears
|
|
98
|
+
* a body this one called sent says so ({@link reportSubmittedBodyInComposer}).
|
|
82
99
|
*/
|
|
83
100
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
84
101
|
if (k2 === undefined) k2 = k;
|
|
@@ -114,10 +131,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
114
131
|
};
|
|
115
132
|
})();
|
|
116
133
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
117
|
-
exports.LITERAL_SEND_MAX_BYTES = void 0;
|
|
118
|
-
exports.classifySubmit = classifySubmit;
|
|
119
|
-
exports.isSubmitted = isSubmitted;
|
|
120
|
-
exports.classifyPasteLanded = classifyPasteLanded;
|
|
134
|
+
exports.PROMPT_REPORT_WAIT_MS = exports.LITERAL_SEND_MAX_BYTES = exports.isSubmitted = exports.classifySubmitWithRule = exports.classifySubmit = exports.classifyPasteLanded = exports.recheckComposerBeforeInput = void 0;
|
|
121
135
|
exports.clearComposerBeforeSend = clearComposerBeforeSend;
|
|
122
136
|
exports.sendMessageWithSubmitVerification = sendMessageWithSubmitVerification;
|
|
123
137
|
const childProcess = __importStar(require("child_process"));
|
|
@@ -127,19 +141,23 @@ const composer_spec_1 = require("./composer-spec");
|
|
|
127
141
|
const cli_patterns_1 = require("../detection/cli-patterns");
|
|
128
142
|
const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
|
|
129
143
|
const logger_1 = require("../../lib/logger");
|
|
144
|
+
const global_state_1 = require("../../lib/global-state");
|
|
130
145
|
const composer_clear_1 = require("../../lib/session/composer-clear");
|
|
146
|
+
Object.defineProperty(exports, "recheckComposerBeforeInput", { enumerable: true, get: function () { return composer_clear_1.recheckComposerBeforeInput; } });
|
|
147
|
+
const prompt_submit_receipt_1 = require("../../lib/session/prompt-submit-receipt");
|
|
148
|
+
const send_attempt_state_1 = require("../../lib/session/send-attempt-state");
|
|
149
|
+
const send_attempt_types_1 = require("../../lib/session/send-attempt-types");
|
|
150
|
+
const instance_input_lock_1 = require("../../lib/session/instance-input-lock");
|
|
131
151
|
const composer_text_1 = require("../../lib/detection/composer-text");
|
|
152
|
+
const submit_composer_read_1 = require("./submit-composer-read");
|
|
153
|
+
// Issue #3963: the composer reads moved to `submit-composer-read`; re-exported
|
|
154
|
+
// so every caller and test keeps importing them from here.
|
|
155
|
+
var submit_composer_read_2 = require("./submit-composer-read");
|
|
156
|
+
Object.defineProperty(exports, "classifyPasteLanded", { enumerable: true, get: function () { return submit_composer_read_2.classifyPasteLanded; } });
|
|
157
|
+
Object.defineProperty(exports, "classifySubmit", { enumerable: true, get: function () { return submit_composer_read_2.classifySubmit; } });
|
|
158
|
+
Object.defineProperty(exports, "classifySubmitWithRule", { enumerable: true, get: function () { return submit_composer_read_2.classifySubmitWithRule; } });
|
|
159
|
+
Object.defineProperty(exports, "isSubmitted", { enumerable: true, get: function () { return submit_composer_read_2.isSubmitted; } });
|
|
132
160
|
const logger = (0, logger_1.createLogger)('cli-tools/submit-verified-sender');
|
|
133
|
-
/**
|
|
134
|
-
* Version-resilient pasted-text placeholder.
|
|
135
|
-
*
|
|
136
|
-
* Broader than PASTED_TEXT_PATTERN (`/\[Pasted text #\d+/`) so it still matches
|
|
137
|
-
* when a CLI version drops the `#N` and renders `[Pasted text +46 lines]`
|
|
138
|
-
* (Issue #1469 condition 2). Used ONLY as a positive "still in the input box"
|
|
139
|
-
* signal — never as the sole submit gate — so applying it to every tool is safe:
|
|
140
|
-
* a tool that never renders it simply never matches.
|
|
141
|
-
*/
|
|
142
|
-
const PASTE_PLACEHOLDER_PATTERN = /\[Pasted text[\s#]/;
|
|
143
161
|
/**
|
|
144
162
|
* Largest body typed as keystrokes (`send-keys -l`); anything longer is pasted
|
|
145
163
|
* (Issue #2464).
|
|
@@ -178,558 +196,33 @@ const TYPED_BODY_POLL_MS = 50;
|
|
|
178
196
|
const DEFAULT_TYPED_BODY_ATTEMPTS = 100;
|
|
179
197
|
/** Per-command timeout for `load-buffer` / `paste-buffer`, as for every other tmux call. */
|
|
180
198
|
const PASTE_COMMAND_TIMEOUT_MS = 5000;
|
|
181
|
-
/** Makes each send's tmux paste buffer name unique within this process. */
|
|
182
|
-
let pasteBufferSeq = 0;
|
|
183
|
-
/**
|
|
184
|
-
* A slash-command sitting on the input line (Issue #1501).
|
|
185
|
-
*
|
|
186
|
-
* TUI completion popups replace a typed slash command with a highlighted menu
|
|
187
|
-
* item (`/status` -> `/statusline`, `/review` -> `/teamwork-preview`); the
|
|
188
|
-
* result is always another slash command. Scoping the "replaced" verdict to
|
|
189
|
-
* `/…` text keeps idle-prompt placeholders that some TUIs paint on an empty
|
|
190
|
-
* composer (gemini's "Type your message or @path", claude's hints, "? for
|
|
191
|
-
* shortcuts") — none of which start with `/` — from being mistaken for a
|
|
192
|
-
* substitution, so a genuinely-submitted message never fails spuriously.
|
|
193
|
-
*/
|
|
194
|
-
const REPLACEMENT_COMMAND_PATTERN = /^\/[A-Za-z]/;
|
|
195
199
|
/**
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* `input-line-marker`. opencode draws no marker at all, so applying it there was
|
|
202
|
-
* wrong in both directions — see {@link readComposer}.
|
|
200
|
+
* Makes each send's tmux paste buffer name unique within this process — on
|
|
201
|
+
* `globalThis` (Issue #3945 review #11): tmux buffers are server-wide, and a
|
|
202
|
+
* module-local counter restarts at 0 in every bundle `next dev` loads this file
|
|
203
|
+
* into, so two sessions' sends could load into one buffer name (the second
|
|
204
|
+
* `load-buffer` overwriting the first before its `paste-buffer`).
|
|
203
205
|
*/
|
|
204
|
-
const
|
|
206
|
+
const pasteBufferSeq = (0, global_state_1.getOrInitGlobal)('__submitPasteBufferSeq', () => ({ last: 0 }));
|
|
205
207
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* capture: the busy marker of every tool lives in a status bar pinned to the
|
|
210
|
-
* bottom of the pane, so it is read from a short tail even when the capture
|
|
211
|
-
* itself is opencode's whole 200-row frame. Widening it to that frame would
|
|
212
|
-
* match `esc interrupt` printed inside a reply.
|
|
213
|
-
*
|
|
214
|
-
* "The bottom" is the last row that holds anything (Issue #2464). codex,
|
|
215
|
-
* Command Code and agy draw inline from the TOP of the 1000-row pane, so the
|
|
216
|
-
* last twelve rows of the capture are blank padding — measured at rows 306,
|
|
217
|
-
* 297 and 286 of 1,002 — and reading them found no composer on any send to
|
|
218
|
-
* those three tools. See {@link withoutTrailingBlankRows}.
|
|
208
|
+
* Rows read for the re-check under the input lock (Issue #3945). The same
|
|
209
|
+
* capture `clearComposer` takes for its first read, so on a tool that clears
|
|
210
|
+
* before sending that read is the re-check's frame and no extra read is made.
|
|
219
211
|
*/
|
|
220
|
-
const
|
|
221
|
-
/**
|
|
222
|
-
|
|
223
|
-
*
|
|
224
|
-
* Two readers, because the two families of TUI say "this is the input line" in
|
|
225
|
-
* incompatible ways:
|
|
226
|
-
*
|
|
227
|
-
* - **marker tools** (`reader: 'input-line-marker'`) draw a `>` / `❯` / `›` at
|
|
228
|
-
* the start of the row. Scanned bottom-up over the tail window so a status bar
|
|
229
|
-
* below the box does not hide it.
|
|
230
|
-
* - **opencode** draws a box with a gutter and no marker anywhere. Its buffer
|
|
231
|
-
* rows are located structurally by {@link findOpenCodeComposerRows} (#1911's
|
|
232
|
-
* chrome walk), and `Ask anything...` inside that box is opencode's own way of
|
|
233
|
-
* saying the buffer is empty (#1883) rather than a row of text.
|
|
234
|
-
*
|
|
235
|
-
* Before this split, opencode went through the marker reader, which is wrong
|
|
236
|
-
* both ways round. It never matched opencode's composer, so EVERY opencode send
|
|
237
|
-
* was classified `submitted` without evidence — #1471's "the Enter was
|
|
238
|
-
* swallowed" recovery has never once run on opencode. And when a `>` did land in
|
|
239
|
-
* the window from somewhere else (a Markdown quote in a reply), the reader
|
|
240
|
-
* treated that row as the input line and answered from it.
|
|
241
|
-
*
|
|
242
|
-
* Issue #1933 turned "which reader" from a `cliToolId === 'opencode'` test plus
|
|
243
|
-
* a set membership test into {@link ComposerSpec.reader}, so the answer is one
|
|
244
|
-
* declaration the tool owns rather than two tables a new tool has to be added
|
|
245
|
-
* to. `unreadable` is the third arm, and it is the one #1906 found opencode
|
|
246
|
-
* silently occupying: it means every send is classified `submitted` without
|
|
247
|
-
* evidence, so it is a state to declare knowingly, never to fall into.
|
|
248
|
-
*/
|
|
249
|
-
function readComposer(reader, lines, windowLines) {
|
|
250
|
-
if (reader === 'opencode-box') {
|
|
251
|
-
const rows = (0, cli_patterns_1.findOpenCodeComposerRows)(lines);
|
|
252
|
-
if (rows === null)
|
|
253
|
-
return { kind: 'absent' };
|
|
254
|
-
// The placeholder is painted only while the buffer is empty, and only
|
|
255
|
-
// inside the box — hence matched on the raw, still-guttered row (#1883).
|
|
256
|
-
if (rows.some((row) => cli_patterns_1.OPENCODE_IDLE_COMPOSER_PATTERN.test(row)))
|
|
257
|
-
return { kind: 'empty' };
|
|
258
|
-
const first = rows.map(cli_patterns_1.stripOpenCodeGutter).find((row) => row.length > 0);
|
|
259
|
-
if (first === undefined)
|
|
260
|
-
return { kind: 'empty' };
|
|
261
|
-
return { kind: 'text', text: first, rows };
|
|
262
|
-
}
|
|
263
|
-
if (reader !== 'input-line-marker')
|
|
264
|
-
return { kind: 'absent' };
|
|
265
|
-
const inputLine = findInputLine(windowLines);
|
|
266
|
-
if (inputLine === null)
|
|
267
|
-
return { kind: 'absent' };
|
|
268
|
-
const stripped = stripInputMarker(inputLine);
|
|
269
|
-
if (stripped.length === 0)
|
|
270
|
-
return { kind: 'empty' };
|
|
271
|
-
return { kind: 'text', text: stripped, rows: [inputLine] };
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* The captured rows without the blank rows under the last drawn one
|
|
275
|
-
* (Issue #2464).
|
|
276
|
-
*
|
|
277
|
-
* `capture-pane` returns every row of the pane, and a TUI that renders inline
|
|
278
|
-
* from the top (codex, Command Code, agy) fills only the first few hundred of
|
|
279
|
-
* the 1000 rows the session is created with. The composer is the last thing
|
|
280
|
-
* such a TUI draws, so "the tail of the pane" has to mean "the tail of what is
|
|
281
|
-
* drawn" — otherwise the tail window is all padding, no composer is ever found,
|
|
282
|
-
* and every send to those tools classifies as `submitted` whatever the
|
|
283
|
-
* composer actually holds. A bottom-anchored TUI (claude) has no trailing blank
|
|
284
|
-
* rows, so for it this is the identity.
|
|
285
|
-
*/
|
|
286
|
-
function withoutTrailingBlankRows(lines) {
|
|
287
|
-
let end = lines.length;
|
|
288
|
-
while (end > 0 && lines[end - 1].trim() === '')
|
|
289
|
-
end--;
|
|
290
|
-
return lines.slice(0, end);
|
|
291
|
-
}
|
|
212
|
+
const RECHECK_CAPTURE_LINES = 200;
|
|
213
|
+
/** How often a send waiting out a carried paste mark reads the composer (Issue #3945). */
|
|
214
|
+
const CARRIED_MARK_POLL_MS = 200;
|
|
292
215
|
/** Default bounded recovery Enters (each read back) before giving up (throwing). */
|
|
293
216
|
const DEFAULT_VERIFY_ATTEMPTS = 4;
|
|
294
217
|
/**
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* the
|
|
298
|
-
*
|
|
299
|
-
*/
|
|
300
|
-
function firstNonBlankLine(message) {
|
|
301
|
-
const line = message
|
|
302
|
-
.split('\n')
|
|
303
|
-
.map((l) => l.trim())
|
|
304
|
-
.find((l) => l.length > 0);
|
|
305
|
-
return line ?? '';
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Locate the active input line within the pane tail: the last line that begins
|
|
309
|
-
* with a prompt marker. Scanned bottom-up so a status-bar/footer line rendered
|
|
310
|
-
* BELOW the input box (antigravity's "? for shortcuts", vibe-local's status bar)
|
|
311
|
-
* does not hide the real input line above it.
|
|
218
|
+
* How long a B/F verdict on a session's first prompt waits for the agent's own
|
|
219
|
+
* report of it (Issue #4035). Claude 2.1.314 reported the first prompt 0.66–0.74 s
|
|
220
|
+
* after the send began in all 29 submitted first sends measured; an unsent body
|
|
221
|
+
* was drawn into the composer 0.9–1.7 s after its Enter.
|
|
312
222
|
*/
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return windowLines[i];
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return null;
|
|
320
|
-
}
|
|
321
|
-
/** Input line text with the prompt marker stripped and surrounding space trimmed. */
|
|
322
|
-
function stripInputMarker(inputLine) {
|
|
323
|
-
return inputLine.replace(INPUT_LINE_MARKER, '').trim();
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Whether the (marker-stripped, non-empty) input-line text is still our
|
|
327
|
-
* unsent body rather than a TUI-substituted command.
|
|
328
|
-
*
|
|
329
|
-
* The still-unsent body appears verbatim on the input line. Line wrapping can
|
|
330
|
-
* visually truncate it to a PREFIX of the first line, but a completion popup
|
|
331
|
-
* always produces a DIFFERENT string — the body plus a completion suffix
|
|
332
|
-
* (`/status` -> `/statusline`) or an unrelated command (`/review` ->
|
|
333
|
-
* `/teamwork-preview`) — which is never a prefix of the body. So the body is
|
|
334
|
-
* "still there" iff the input text is a prefix of (or equals) the body's first
|
|
335
|
-
* line. This deliberately rejects `/statusline` for a `/status` body: the safe
|
|
336
|
-
* rule is "input text that is not the body (or a prefix of it) is NOT resent".
|
|
337
|
-
*/
|
|
338
|
-
function inputMatchesBody(strippedInput, message) {
|
|
339
|
-
const bodyFirstLine = firstNonBlankLine(message);
|
|
340
|
-
if (bodyFirstLine.length === 0)
|
|
341
|
-
return false;
|
|
342
|
-
return bodyFirstLine.startsWith(strippedInput);
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Collapsed-paste placeholders, as measured at the production 200x1000
|
|
346
|
-
* geometry (Issue #2464, `tests/fixtures/long-body-2464/`):
|
|
347
|
-
*
|
|
348
|
-
* claude 2.1.268 `[Pasted text #86 +59 lines]` for 60 lines, `[Pasted text #87]` for one
|
|
349
|
-
* agy 1.2.0 `[Pasted text #5 +60 lines]` for 60 lines, `[Pasted text #6 12288 chars]` for one
|
|
350
|
-
* codex 0.154.0 `[Pasted Content 12288 chars]`
|
|
351
|
-
* Command Code 1.53.0 `[PROBE P12k… +60L]` — the first ten characters, then the line count
|
|
352
|
-
*
|
|
353
|
-
* The `#N` stays optional for #1469's version drift (`[Pasted text +46 lines]`).
|
|
354
|
-
*/
|
|
355
|
-
const PASTE_TOKEN_PATTERNS = [
|
|
356
|
-
{
|
|
357
|
-
pattern: /\[Pasted text(?: #\d+)?(?: \+(\d+) lines?| (\d+) chars?)?\]/g,
|
|
358
|
-
read: (m) => m[1] !== undefined ? { lines: Number(m[1]) } : m[2] !== undefined ? { chars: Number(m[2]) } : {},
|
|
359
|
-
},
|
|
360
|
-
{ pattern: /\[Pasted Content (\d+) chars?\]/g, read: (m) => ({ chars: Number(m[1]) }) },
|
|
361
|
-
{ pattern: /\[([^\]\n]*?)… \+(\d+)L\]/g, read: (m) => ({ head: m[1], lineTotal: Number(m[2]) }) },
|
|
362
|
-
];
|
|
363
|
-
/** Every paste placeholder on a composer row, and the text left around them. */
|
|
364
|
-
function findPasteTokens(text) {
|
|
365
|
-
const tokens = [];
|
|
366
|
-
let rest = text;
|
|
367
|
-
for (const { pattern, read } of PASTE_TOKEN_PATTERNS) {
|
|
368
|
-
for (const match of text.matchAll(pattern)) {
|
|
369
|
-
tokens.push(read(match));
|
|
370
|
-
rest = rest.replace(match[0], '');
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return { tokens, rest: rest.trim() };
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Does one placeholder account for the whole body?
|
|
377
|
-
*
|
|
378
|
-
* Counts below the body's mean the paste is still arriving; counts above it mean
|
|
379
|
-
* the composer holds more than the body. The two units that differ between
|
|
380
|
-
* tools for the same `+M lines` wording (claude: 59 for a 60-line body, agy: 60)
|
|
381
|
-
* are both accepted — a bracketed paste arrives as one unit, so the failure this
|
|
382
|
-
* guards against is a count that is wildly short (the 24-byte tail of #2464),
|
|
383
|
-
* not one that is off by one line.
|
|
384
|
-
*/
|
|
385
|
-
function comparePasteToken(token, message) {
|
|
386
|
-
const newlines = (message.match(/\n/g) ?? []).length;
|
|
387
|
-
if (token.head !== undefined) {
|
|
388
|
-
const normalizedHead = token.head.replace(/\s+/g, ' ').trim();
|
|
389
|
-
if (!message.replace(/\s+/g, ' ').startsWith(normalizedHead))
|
|
390
|
-
return 'mismatch';
|
|
391
|
-
}
|
|
392
|
-
let observed;
|
|
393
|
-
let accepted;
|
|
394
|
-
if (token.chars !== undefined) {
|
|
395
|
-
observed = token.chars;
|
|
396
|
-
accepted = [[...message].length, message.length];
|
|
397
|
-
}
|
|
398
|
-
else if (token.lineTotal !== undefined) {
|
|
399
|
-
observed = token.lineTotal;
|
|
400
|
-
accepted = [newlines + 1];
|
|
401
|
-
}
|
|
402
|
-
else if (token.lines !== undefined) {
|
|
403
|
-
observed = token.lines;
|
|
404
|
-
accepted = [newlines, newlines + 1];
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
// claude's bare `[Pasted text #N]` is what it draws for a single line.
|
|
408
|
-
return newlines === 0 ? 'landed' : 'pasting';
|
|
409
|
-
}
|
|
410
|
-
if (accepted.includes(observed))
|
|
411
|
-
return 'landed';
|
|
412
|
-
return observed < Math.min(...accepted) ? 'pasting' : 'mismatch';
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* The composer as {@link classifySubmit} reads it (Issue #3205).
|
|
416
|
-
*
|
|
417
|
-
* {@link readComposer}'s marker reader takes the bottom-most `›` row of the
|
|
418
|
-
* tail as the input line. A codex composer holding several lines draws its
|
|
419
|
-
* continuation rows under the `›` row, and when one of them itself begins with
|
|
420
|
-
* `›` — a body quoting a dialog's `› 1. Yes, proceed (y)` — that continuation
|
|
421
|
-
* row was read as the input line: its text is not the body's first line, so a
|
|
422
|
-
* body still sitting in the composer was classified `submitted` (F). Measured:
|
|
423
|
-
* `tests/fixtures/codex-multiline-composer-3205/tc104-raw-pane.txt`.
|
|
424
|
-
*
|
|
425
|
-
* So for codex a composer of two or more rows is read with
|
|
426
|
-
* `extractComposerText` — the #1890 reader `awaitTypedBody` already uses —
|
|
427
|
-
* and its first line is the input line. Every other frame (a one-row
|
|
428
|
-
* composer, an empty or ghost one, none at all, another tool) is read exactly
|
|
429
|
-
* as before.
|
|
430
|
-
*
|
|
431
|
-
* Issue #3880 (H-2): this is also the reader before Enter
|
|
432
|
-
* ({@link classifyPasteLanded}) and in the error text ({@link describeComposer}).
|
|
433
|
-
* The paste check used to read the twelve-row tail with {@link readComposer}
|
|
434
|
-
* alone, so a codex composer taller than that window (a 20-line paste, or 600
|
|
435
|
-
* bytes wrapped in a narrow pane) put its `›` row above the window — `unseen`,
|
|
436
|
-
* and the body that was in the composer was withdrawn and reported as not
|
|
437
|
-
* landed — and a continuation row starting with `>` (a Markdown quote) was taken
|
|
438
|
-
* for the input line — `mismatch`. The same frame read after Enter was
|
|
439
|
-
* `pending`. One reader on both sides of Enter keeps the two verdicts about the
|
|
440
|
-
* same composer.
|
|
441
|
-
*/
|
|
442
|
-
/**
|
|
443
|
-
* Everything a codex composer holds, for deciding that a pasted body arrived in
|
|
444
|
-
* full (Issue #3880).
|
|
445
|
-
*
|
|
446
|
-
* `extractComposerText` answers for the UI: its text stops at 2,000 characters,
|
|
447
|
-
* at 40 rows and at the composer's first blank row, so a composer that matches
|
|
448
|
-
* it may still be missing the rest of the body — or hold something after it.
|
|
449
|
-
* This reads the raw rows instead, from the `›` row down to the last block of
|
|
450
|
-
* the frame (codex's model / cwd footer), so a body with blank lines, more than
|
|
451
|
-
* 40 rows or more than 2,000 characters is compared whole, and anything typed
|
|
452
|
-
* after the body is part of what is compared.
|
|
453
|
-
*/
|
|
454
|
-
function codexComposerRegion(output) {
|
|
455
|
-
const raw = output.split('\n');
|
|
456
|
-
const box = (0, composer_text_1.findCodexInputBox)(raw);
|
|
457
|
-
if (box === null)
|
|
458
|
-
return null;
|
|
459
|
-
return codexRegionFrom(raw, box.firstRow, box.lastRow);
|
|
460
|
-
}
|
|
461
|
-
/**
|
|
462
|
-
* {@link codexComposerRegion} from a known `›` row (Issue #3906): the rows from
|
|
463
|
-
* `firstRow` down to the frame's last block (the footer), ANSI and glyph
|
|
464
|
-
* stripped. `lastRow` is the end of the `›` row's own block, so a composer that
|
|
465
|
-
* is itself the frame's last block is read to the end.
|
|
466
|
-
*/
|
|
467
|
-
function codexRegionFrom(raw, firstRow, lastRow) {
|
|
468
|
-
const isBlank = (row) => (0, cli_patterns_1.stripAnsi)(row).trim() === '';
|
|
469
|
-
let end = raw.length - 1;
|
|
470
|
-
while (end >= 0 && isBlank(raw[end]))
|
|
471
|
-
end--;
|
|
472
|
-
let lastBlockStart = end;
|
|
473
|
-
while (lastBlockStart > 0 && !isBlank(raw[lastBlockStart - 1]))
|
|
474
|
-
lastBlockStart--;
|
|
475
|
-
const stop = lastBlockStart > lastRow ? lastBlockStart - 1 : end;
|
|
476
|
-
const rows = raw.slice(firstRow, stop + 1).map((row) => (0, cli_patterns_1.stripAnsi)(row));
|
|
477
|
-
rows[0] = rows[0].replace(/^\s*\u203A ?/, '');
|
|
478
|
-
return rows.join('\n');
|
|
479
|
-
}
|
|
480
|
-
/** End (inclusive) of the blank-row-delimited block that starts at `row`. */
|
|
481
|
-
function blockEndFrom(raw, row) {
|
|
482
|
-
let end = row;
|
|
483
|
-
while (end + 1 < raw.length && (0, cli_patterns_1.stripAnsi)(raw[end + 1]).trim() !== '')
|
|
484
|
-
end++;
|
|
485
|
-
return end;
|
|
486
|
-
}
|
|
487
|
-
/** The bottom-most row of `windowLines` the marker reader takes as the input line, as a frame index. */
|
|
488
|
-
function markerRowIndex(windowLines, windowStart) {
|
|
489
|
-
for (let i = windowLines.length - 1; i >= 0; i--) {
|
|
490
|
-
if (INPUT_LINE_MARKER.test(windowLines[i]))
|
|
491
|
-
return windowStart + i;
|
|
492
|
-
}
|
|
493
|
-
return -1;
|
|
494
|
-
}
|
|
495
|
-
function readSubmitComposer(output, cliToolId, reader, lines, windowLines, message) {
|
|
496
|
-
if (cliToolId === 'codex' && reader === 'input-line-marker') {
|
|
497
|
-
const raw = output.split('\n');
|
|
498
|
-
const windowStart = lines.length - windowLines.length;
|
|
499
|
-
const read = (0, composer_text_1.extractComposerText)(output, cliToolId);
|
|
500
|
-
if (read.state === 'content') {
|
|
501
|
-
const box = (0, composer_text_1.findCodexInputBox)(raw);
|
|
502
|
-
// Issue #3906: a dialog option drawn below the composer the extractor
|
|
503
|
-
// located means the dialog is what the pane shows now, on every branch
|
|
504
|
-
// below — that composer is one left from before, not this send's.
|
|
505
|
-
if (box !== null && (0, composer_text_1.codexDialogBelow)(raw, box.firstRow))
|
|
506
|
-
return { kind: 'absent' };
|
|
507
|
-
const outsideWindow = box !== null && box.firstRow < windowStart;
|
|
508
|
-
const region = codexComposerRegion(output) ?? read.text;
|
|
509
|
-
if (read.text.includes('\n')) {
|
|
510
|
-
// Issue #3906: a composer of many rows with no blank line (20 lines) has
|
|
511
|
-
// its `›` row above the window too, and is marked so like the others.
|
|
512
|
-
return {
|
|
513
|
-
kind: 'text',
|
|
514
|
-
text: firstNonBlankLine(read.text),
|
|
515
|
-
rows: read.text.split('\n'),
|
|
516
|
-
region,
|
|
517
|
-
...(outsideWindow ? { outsideWindow } : {}),
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
// Issue #3906 (1): the `›` row sits above the twelve-row window (a one-line
|
|
521
|
-
// paragraph, a blank line, then a tall one). The marker reader would see no
|
|
522
|
-
// input line there — `unseen` before Enter, `submitted` after it — so the
|
|
523
|
-
// composer `extractComposerText` located is read instead, whole.
|
|
524
|
-
if (outsideWindow && (0, composer_text_1.findCodexLiveComposerRow)(raw) === box.firstRow) {
|
|
525
|
-
return { kind: 'text', text: read.text.trim(), rows: [raw[box.firstRow]], region, outsideWindow: true };
|
|
526
|
-
}
|
|
527
|
-
// Issue #3880 (N1): a one-row excerpt is read as before for its verdict
|
|
528
|
-
// after Enter, but still carries the whole region — `head` with the rest of
|
|
529
|
-
// the body under a blank row is one excerpt row and must not pass for the
|
|
530
|
-
// whole body before Enter.
|
|
531
|
-
const single = readComposer(reader, lines, windowLines);
|
|
532
|
-
return single.kind === 'text' ? { ...single, region } : single;
|
|
533
|
-
}
|
|
534
|
-
if (read.state === 'no_composer') {
|
|
535
|
-
// Issue #3906 (2): more paragraphs than the four blocks the extractor
|
|
536
|
-
// searches. Whatever the marker reader takes must carry the whole region,
|
|
537
|
-
// so the composer's first line alone never passes for the body. The row is
|
|
538
|
-
// the live composer only when no dialog option is drawn below it
|
|
539
|
-
// ({@link findCodexLiveComposerRow}, the order `findCodexBottomGlyphRow`
|
|
540
|
-
// reads a frame in): a composer row left above an approval dialog is
|
|
541
|
-
// never this send's composer, however its text reads.
|
|
542
|
-
const composerRow = (0, composer_text_1.findCodexLiveComposerRow)(raw);
|
|
543
|
-
if (composerRow >= 0) {
|
|
544
|
-
const region = codexRegionFrom(raw, composerRow, blockEndFrom(raw, composerRow));
|
|
545
|
-
const text = stripInputMarker((0, cli_patterns_1.stripAnsi)(raw[composerRow]));
|
|
546
|
-
if (composerRow >= windowStart) {
|
|
547
|
-
const single = readComposer(reader, lines, windowLines);
|
|
548
|
-
if (single.kind === 'text' && markerRowIndex(windowLines, windowStart) === composerRow) {
|
|
549
|
-
return { ...single, region };
|
|
550
|
-
}
|
|
551
|
-
return single;
|
|
552
|
-
}
|
|
553
|
-
// Above the window as well, with no dialog below it.
|
|
554
|
-
if (message !== undefined && text.length > 0 && inputMatchesBody(text, message)) {
|
|
555
|
-
return { kind: 'text', text, rows: [raw[composerRow]], region, outsideWindow: true };
|
|
556
|
-
}
|
|
557
|
-
// Not the body's first line. A composer whose own block is empty or
|
|
558
|
-
// placeholder-only is empty; real text is read too — a `/…` command
|
|
559
|
-
// there is `replaced` as it is inside the window, and anything else is
|
|
560
|
-
// not proof that the message left ({@link classifySubmit}: `indeterminate`).
|
|
561
|
-
const held = (0, composer_text_1.readCodexComposerFrom)(raw.slice(0, blockEndFrom(raw, composerRow) + 1), composerRow);
|
|
562
|
-
if (held.state !== 'content')
|
|
563
|
-
return { kind: 'empty' };
|
|
564
|
-
return {
|
|
565
|
-
kind: 'text',
|
|
566
|
-
text: firstNonBlankLine(held.text),
|
|
567
|
-
rows: [raw[composerRow]],
|
|
568
|
-
region,
|
|
569
|
-
outsideWindow: true,
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
return readComposer(reader, lines, windowLines);
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Classify a captured pane into submitted / pending / replaced (Issue #1501).
|
|
578
|
-
*
|
|
579
|
-
* Version-independent by design — does NOT require the paste placeholder:
|
|
580
|
-
* A. The tool is generating a response -> submitted.
|
|
581
|
-
* B. No composer, or the composer is empty -> submitted.
|
|
582
|
-
* C. A paste placeholder is folded in the composer -> pending (Enter eaten).
|
|
583
|
-
* D. The body is still verbatim in the composer -> pending (resend Enter).
|
|
584
|
-
* E. A DIFFERENT slash command is in the composer -> replaced (TUI popup
|
|
585
|
-
* autocompleted the command; clear the line and throw, never resend Enter).
|
|
586
|
-
* F. Any other non-empty text (idle placeholder/hint) -> submitted (unchanged
|
|
587
|
-
* pre-#1501 permissive default, so normal sends never spuriously fail) —
|
|
588
|
-
* except in a codex composer above the read window -> indeterminate
|
|
589
|
-
* (Issue #3906).
|
|
590
|
-
*
|
|
591
|
-
* C–F are scoped to the composer only, so the user-message echo that a TUI
|
|
592
|
-
* prints into its history above the prompt never causes a false verdict.
|
|
593
|
-
*
|
|
594
|
-
* Issue #1906: "the composer" is read per tool by {@link readComposer} rather
|
|
595
|
-
* than by looking for a `>` in the last twelve rows. For opencode — which draws
|
|
596
|
-
* no marker — that read was unreachable, so every send reached F (in fact B) and
|
|
597
|
-
* this function has never returned anything but `submitted` for it.
|
|
598
|
-
*
|
|
599
|
-
* Issue #2464: the tail window starts at the last drawn row, not at the last row
|
|
600
|
-
* of the pane ({@link withoutTrailingBlankRows}), and C recognises every tool's
|
|
601
|
-
* paste placeholder ({@link findPasteTokens}), not only claude's.
|
|
602
|
-
*
|
|
603
|
-
* @param output - Captured pane, ANSI intact. For opencode this must be the
|
|
604
|
-
* whole pane; {@link ComposerSpec.verifyCaptureLines} is what asks tmux for it.
|
|
605
|
-
* @param cliToolId - CLI tool id, for the "generating" detector
|
|
606
|
-
* @param message - The body that was typed
|
|
607
|
-
* @param composer - The tool's composer description (Issue #1933). Defaults to
|
|
608
|
-
* the tool's own, so every existing three-argument caller is unchanged.
|
|
609
|
-
*/
|
|
610
|
-
function classifySubmit(output, cliToolId, message, composerSpec = (0, composer_spec_1.resolveComposerSpec)(cliToolId)) {
|
|
611
|
-
const clean = (0, cli_patterns_1.stripAnsi)(output);
|
|
612
|
-
const lines = withoutTrailingBlankRows(clean.split('\n'));
|
|
613
|
-
const windowLines = lines.slice(-VERIFY_WINDOW_LINES);
|
|
614
|
-
const windowStr = windowLines.join('\n');
|
|
615
|
-
// A. Actively generating a response => the message was accepted. Read from the
|
|
616
|
-
// tail window for every tool: each one's busy marker lives in a status bar
|
|
617
|
-
// pinned to the bottom of the pane, and widening it to opencode's whole
|
|
618
|
-
// frame would match `esc interrupt` printed inside a reply.
|
|
619
|
-
if ((0, cli_patterns_1.detectThinking)(cliToolId, windowStr)) {
|
|
620
|
-
return 'submitted';
|
|
621
|
-
}
|
|
622
|
-
const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, windowLines, message);
|
|
623
|
-
// B. No composer visible (scrolled off / dialog / still starting), or the
|
|
624
|
-
// composer is empty => the message left the box => submitted.
|
|
625
|
-
if (composer.kind !== 'text') {
|
|
626
|
-
return 'submitted';
|
|
627
|
-
}
|
|
628
|
-
// C. A paste placeholder is still folded into the composer => body is there.
|
|
629
|
-
if (composer.rows.some((row) => PASTE_PLACEHOLDER_PATTERN.test(row) || findPasteTokens(row).tokens.length > 0)) {
|
|
630
|
-
return 'pending';
|
|
631
|
-
}
|
|
632
|
-
// D. The typed body is still sitting in the composer => resend Enter.
|
|
633
|
-
if (inputMatchesBody(composer.text, message)) {
|
|
634
|
-
return 'pending';
|
|
635
|
-
}
|
|
636
|
-
// E. A different slash command is in the composer => a completion popup
|
|
637
|
-
// replaced what we typed. (Scoped to `/…` so idle-prompt placeholders that
|
|
638
|
-
// some TUIs paint on an empty composer are never mistaken for this.)
|
|
639
|
-
if (REPLACEMENT_COMMAND_PATTERN.test(composer.text)) {
|
|
640
|
-
return 'replaced';
|
|
641
|
-
}
|
|
642
|
-
// F. Non-empty, non-command steady-state text (idle placeholder / hint) =>
|
|
643
|
-
// submitted, preserving the pre-#1501 permissive default. Not for a codex
|
|
644
|
-
// composer above the window (Issue #3906): before #3906 that frame was
|
|
645
|
-
// read as no composer at all; text there that is not the body says
|
|
646
|
-
// neither that the message left nor that Enter would send it.
|
|
647
|
-
return composer.outsideWindow ? 'indeterminate' : 'submitted';
|
|
648
|
-
}
|
|
649
|
-
/**
|
|
650
|
-
* Backward-compatible boolean view of {@link classifySubmit}: submitted vs not.
|
|
651
|
-
* A `replaced` verdict is NOT "submitted", so this returns false for it too.
|
|
652
|
-
*/
|
|
653
|
-
function isSubmitted(output, cliToolId, message, composerSpec = (0, composer_spec_1.resolveComposerSpec)(cliToolId)) {
|
|
654
|
-
return classifySubmit(output, cliToolId, message, composerSpec) === 'submitted';
|
|
655
|
-
}
|
|
656
|
-
/**
|
|
657
|
-
* How much of a pasted body the composer shows, read BEFORE Enter (Issue #2464).
|
|
658
|
-
*
|
|
659
|
-
* The question #2464 needs answered is not "did Enter work" but "is what Enter
|
|
660
|
-
* would submit the message we sent". Claude kept the last 24 bytes of a 12 KB
|
|
661
|
-
* brief as ordinary composer text; every check that runs after Enter saw an
|
|
662
|
-
* empty box and a generating model, i.e. a perfect send. So the composer is
|
|
663
|
-
* judged here, while pressing Enter can still be withheld:
|
|
664
|
-
*
|
|
665
|
-
* 1. A collapsed placeholder is compared by the count the TUI prints — lines
|
|
666
|
-
* for claude, agy and Command Code, characters for codex and agy's
|
|
667
|
-
* single-line form ({@link comparePasteToken}).
|
|
668
|
-
* 2. Plain text is the body only if it is the body's BEGINNING. Text that
|
|
669
|
-
* occurs in the body but not at its start is exactly the #2464 tail, and is
|
|
670
|
-
* a mismatch rather than "not drawn yet".
|
|
671
|
-
* 3. Anything else — the idle placeholder, a ghost suggestion, no composer —
|
|
672
|
-
* shows nothing of the body. `unseen`, which the caller decides about.
|
|
673
|
-
*
|
|
674
|
-
* Reads the composer the way {@link classifySubmit} does
|
|
675
|
-
* ({@link readSubmitComposer}), so a history echo of an earlier message above the
|
|
676
|
-
* composer is never mistaken for this one, and a frame both functions see holds
|
|
677
|
-
* the same composer for both (Issue #3880). A multi-row codex composer is
|
|
678
|
-
* compared as a whole ({@link codexComposerRegion}), whitespace removed
|
|
679
|
-
* (wrapping and continuation indent proof): only the WHOLE body is `landed`;
|
|
680
|
-
* the body's beginning alone is `pasting` (never enough to press Enter on), the
|
|
681
|
-
* body plus anything else or text from inside it is `mismatch`. The whole body
|
|
682
|
-
* is checked before placeholders, so a body that itself quotes
|
|
683
|
-
* `[Pasted Content 600 chars]` is its own text, not codex's placeholder; a real
|
|
684
|
-
* placeholder is only ever the composer's first row.
|
|
685
|
-
*
|
|
686
|
-
* Issue #3906: a codex composer outside the twelve-row window, or split into
|
|
687
|
-
* more blocks than `extractComposerText` searches, is still read with its whole
|
|
688
|
-
* region ({@link readSubmitComposer}), so neither `unseen` for a body that is
|
|
689
|
-
* there nor `landed` on its first line alone.
|
|
690
|
-
*/
|
|
691
|
-
function classifyPasteLanded(output, cliToolId, message, composerSpec = (0, composer_spec_1.resolveComposerSpec)(cliToolId)) {
|
|
692
|
-
const lines = withoutTrailingBlankRows((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
693
|
-
const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES), message);
|
|
694
|
-
if (composer.kind !== 'text')
|
|
695
|
-
return 'unseen';
|
|
696
|
-
if (composer.region !== undefined) {
|
|
697
|
-
const shown = compactText(composer.region);
|
|
698
|
-
const body = compactText(message);
|
|
699
|
-
if (shown === body)
|
|
700
|
-
return 'landed';
|
|
701
|
-
const placeholder = findPasteTokens(composer.text);
|
|
702
|
-
if (placeholder.tokens.length > 0) {
|
|
703
|
-
// A placeholder plus anything else — on its row or under it — is not the body as sent.
|
|
704
|
-
const below = compactText(composer.region.split('\n').slice(1).join('\n'));
|
|
705
|
-
if (placeholder.tokens.length > 1 || placeholder.rest !== '' || below !== '')
|
|
706
|
-
return 'mismatch';
|
|
707
|
-
return comparePasteToken(placeholder.tokens[0], message);
|
|
708
|
-
}
|
|
709
|
-
if (body.startsWith(shown))
|
|
710
|
-
return 'pasting';
|
|
711
|
-
// The body's beginning followed by something else, the body plus more, or a
|
|
712
|
-
// piece from inside it: the composer holds something other than the body.
|
|
713
|
-
if (inputMatchesBody(composer.text, message) || shown.startsWith(body) || body.includes(shown)) {
|
|
714
|
-
return 'mismatch';
|
|
715
|
-
}
|
|
716
|
-
return 'unseen';
|
|
717
|
-
}
|
|
718
|
-
// The whole body on one row is the body, even when it quotes a placeholder.
|
|
719
|
-
if (compactText(composer.text) === compactText(message))
|
|
720
|
-
return 'landed';
|
|
721
|
-
const { tokens, rest } = findPasteTokens(composer.text);
|
|
722
|
-
if (tokens.length === 0) {
|
|
723
|
-
if (inputMatchesBody(composer.text, message))
|
|
724
|
-
return 'landed';
|
|
725
|
-
if (message.includes(composer.text))
|
|
726
|
-
return 'mismatch';
|
|
727
|
-
return 'unseen';
|
|
728
|
-
}
|
|
729
|
-
if (tokens.length > 1 || rest !== '')
|
|
730
|
-
return 'mismatch';
|
|
731
|
-
return comparePasteToken(tokens[0], message);
|
|
732
|
-
}
|
|
223
|
+
exports.PROMPT_REPORT_WAIT_MS = 5000;
|
|
224
|
+
/** How often the report and the composer are looked at during that wait. */
|
|
225
|
+
const PROMPT_REPORT_POLL_MS = 100;
|
|
733
226
|
/** Run one tmux command, optionally feeding it `input` on stdin. */
|
|
734
227
|
function runTmux(args, input) {
|
|
735
228
|
return new Promise((resolve, reject) => {
|
|
@@ -756,12 +249,52 @@ function runTmux(args, input) {
|
|
|
756
249
|
* per line for a TUI that did not ask for brackets. `-d` drops the buffer on
|
|
757
250
|
* success; a failure drops it here, so no body is left behind in the server.
|
|
758
251
|
*
|
|
759
|
-
*
|
|
252
|
+
* Issue #3945 (review-1 #2): ownership is asked again between the two commands,
|
|
253
|
+
* right before `paste-buffer` — the session may have been replaced while
|
|
254
|
+
* `load-buffer` ran. A lost ticket drops the buffer and throws before anything
|
|
255
|
+
* reaches the pane (`input lock lost, nothing was typed`).
|
|
256
|
+
*
|
|
257
|
+
* Issue #4011: a failed `load-buffer` throws with `delivery: 'not-started'`
|
|
258
|
+
* (the field `TmuxSendError` carries) — it runs before `paste-buffer`, so none
|
|
259
|
+
* of the body reached the pane, however it failed (an exit status or a timeout).
|
|
260
|
+
* Issue #4031: worded `Failed to load message into tmux buffer` and coded
|
|
261
|
+
* `PASTE_NOT_STARTED_CODE`, so `classifySendFailure` records it `not-sent`.
|
|
262
|
+
*
|
|
263
|
+
* @throws Error when either tmux command fails, or `ticket` lost the composer
|
|
760
264
|
*/
|
|
761
|
-
async function pasteBody(sessionName, message) {
|
|
762
|
-
|
|
265
|
+
async function pasteBody(sessionName, message, ticket, cliToolId) {
|
|
266
|
+
pasteBufferSeq.last += 1;
|
|
267
|
+
const bufferName = `cm-send-${process.pid}-${pasteBufferSeq.last}`;
|
|
763
268
|
try {
|
|
764
269
|
await runTmux(['load-buffer', '-b', bufferName, '-'], message);
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
await runTmux(['delete-buffer', '-b', bufferName]).catch(() => undefined);
|
|
273
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
274
|
+
// Issue #4031: its own wording and code, apart from `paste-buffer`'s
|
|
275
|
+
// failure below (whose body may be on the pane), so the send is recorded
|
|
276
|
+
// `not-sent` with or without a bound relay.
|
|
277
|
+
throw Object.assign(new Error(`Failed to load message into tmux buffer: ${errorMessage}`), {
|
|
278
|
+
delivery: 'not-started',
|
|
279
|
+
code: send_attempt_types_1.PASTE_NOT_STARTED_CODE,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (!ticket.isCurrent()) {
|
|
283
|
+
await runTmux(['delete-buffer', '-b', bufferName]).catch(() => undefined);
|
|
284
|
+
assertInputOwner(ticket, cliToolId, 'paste-buffer', false);
|
|
285
|
+
}
|
|
286
|
+
// Issue #3953: the send's stamp, as the typed branch takes it — after
|
|
287
|
+
// `load-buffer` (up to its timeout), right before the body reaches the pane.
|
|
288
|
+
// Issue #3992: a refused input boundary (#3957) stops the send here, with the
|
|
289
|
+
// body loaded but not pasted — the buffer is dropped before the refusal goes up.
|
|
290
|
+
try {
|
|
291
|
+
(0, instance_input_lock_1.noteInputStart)();
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
await runTmux(['delete-buffer', '-b', bufferName]).catch(() => undefined);
|
|
295
|
+
throw error;
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
765
298
|
await runTmux(['paste-buffer', '-p', '-r', '-d', '-b', bufferName, '-t', (0, tmux_1.exactTarget)(sessionName)]);
|
|
766
299
|
}
|
|
767
300
|
catch (error) {
|
|
@@ -772,8 +305,8 @@ async function pasteBody(sessionName, message) {
|
|
|
772
305
|
}
|
|
773
306
|
/** The composer's first row, for an error message a person has to act on. */
|
|
774
307
|
function describeComposer(output, cliToolId, composerSpec, message) {
|
|
775
|
-
const lines = withoutTrailingBlankRows((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
776
|
-
const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES), message);
|
|
308
|
+
const lines = (0, submit_composer_read_1.withoutTrailingBlankRows)((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
309
|
+
const composer = (0, submit_composer_read_1.readSubmitComposer)(output, cliToolId, composerSpec.reader, lines, lines.slice(-submit_composer_read_1.VERIFY_WINDOW_LINES), message);
|
|
777
310
|
if (composer.kind === 'absent')
|
|
778
311
|
return 'no composer on screen';
|
|
779
312
|
if (composer.kind === 'empty')
|
|
@@ -787,15 +320,27 @@ function describeComposer(output, cliToolId, composerSpec, message) {
|
|
|
787
320
|
* Leaving it would put the fragment in front of the next message. claude and
|
|
788
321
|
* codex go through the verified #1879 loop (a paste can span rows); the rest
|
|
789
322
|
* get the single `C-u` the #1501 path already sends to them.
|
|
323
|
+
*
|
|
324
|
+
* @returns Whether the composer was confirmed empty afterwards (Issue #3945):
|
|
325
|
+
* false for a `C-u` nothing can read back, a failed clear, and a ticket that
|
|
326
|
+
* no longer owns the composer (no key is sent then).
|
|
790
327
|
*/
|
|
791
|
-
async function withdrawUnsentBody(sessionName, cliToolId, composerSpec) {
|
|
328
|
+
async function withdrawUnsentBody(sessionName, cliToolId, composerSpec, ticket) {
|
|
329
|
+
// Issue #3945: only this send's own composer is taken back. A ticket that is
|
|
330
|
+
// no longer current means another writer owns the composer now, and what it
|
|
331
|
+
// holds is that writer's.
|
|
332
|
+
if (!ticket.isCurrent()) {
|
|
333
|
+
logger.error('paste-withdraw-skipped-not-owner', { sessionName, cliToolId, generation: ticket.generation });
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
792
336
|
try {
|
|
793
337
|
if (composerSpec.clearBeforeSend) {
|
|
794
|
-
await (0, composer_clear_1.clearComposer)(sessionName, cliToolId);
|
|
795
|
-
|
|
796
|
-
else {
|
|
797
|
-
await (0, tmux_1.clearInputLine)(sessionName);
|
|
338
|
+
const result = await (0, composer_clear_1.clearComposer)(sessionName, cliToolId, { beforePass: () => ticket.isCurrent() });
|
|
339
|
+
return result.cleared;
|
|
798
340
|
}
|
|
341
|
+
await (0, tmux_1.clearInputLine)(sessionName);
|
|
342
|
+
// One `C-u` into a composer nothing here can read: sent, never confirmed.
|
|
343
|
+
return false;
|
|
799
344
|
}
|
|
800
345
|
catch (clearError) {
|
|
801
346
|
logger.error('paste-withdraw-failed', {
|
|
@@ -803,8 +348,86 @@ async function withdrawUnsentBody(sessionName, cliToolId, composerSpec) {
|
|
|
803
348
|
cliToolId,
|
|
804
349
|
error: clearError instanceof Error ? clearError.message : String(clearError),
|
|
805
350
|
});
|
|
351
|
+
return false;
|
|
806
352
|
}
|
|
807
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Throw unless `ticket` still owns the composer (Issue #3945 stage 3). Called
|
|
356
|
+
* right before every key a send sends — body, paste, each Enter — so work still
|
|
357
|
+
* in flight after its ownership ended cannot reach the next owner's composer.
|
|
358
|
+
*
|
|
359
|
+
* @param typed - Whether any of the body may already be in the composer, which
|
|
360
|
+
* decides how the failure classifies (`not-sent` before the body only).
|
|
361
|
+
*/
|
|
362
|
+
function assertInputOwner(ticket, cliToolId, step, typed) {
|
|
363
|
+
if (ticket.isCurrent())
|
|
364
|
+
return;
|
|
365
|
+
logger.error('input-lock-lost', { sessionName: ticket.sessionName, cliToolId, step, generation: ticket.generation });
|
|
366
|
+
if (!typed) {
|
|
367
|
+
throw new Error(`Session ${ticket.sessionName} was handed to another writer before ${step}; input lock lost, nothing was typed (Issue #3945).`);
|
|
368
|
+
}
|
|
369
|
+
throw new Error(`Session ${ticket.sessionName} was handed to another writer before ${step}; part of the message may be in its composer (Issue #3945).`);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Hold off typing while a carried paste mark is fresh (Issue #3945 stage 4).
|
|
373
|
+
*
|
|
374
|
+
* A previous send gave up on a body that may still be drawn. Until the mark
|
|
375
|
+
* expires, nothing of this send is typed: a placeholder of the same count as
|
|
376
|
+
* this body would be taken for it ({@link classifyPasteLanded} cannot tell
|
|
377
|
+
* them apart). What appears meanwhile is taken out at once — it is the previous
|
|
378
|
+
* send's, and this owner is the one left to handle it. A tool whose composer is
|
|
379
|
+
* not readable (no `clearBeforeSend`) only waits.
|
|
380
|
+
*
|
|
381
|
+
* @returns Whether there was a mark to wait out
|
|
382
|
+
*/
|
|
383
|
+
async function waitOutCarriedPaste(sessionName, cliToolId, composer, ticket) {
|
|
384
|
+
const mark = (0, instance_input_lock_1.getCarriedPasteMark)(sessionName);
|
|
385
|
+
if (mark === null)
|
|
386
|
+
return false;
|
|
387
|
+
logger.warn('carried-paste-mark-wait', {
|
|
388
|
+
sessionName,
|
|
389
|
+
cliToolId,
|
|
390
|
+
originGeneration: mark.originGeneration,
|
|
391
|
+
generation: ticket.generation,
|
|
392
|
+
remainingMs: mark.expiresAt - Date.now(),
|
|
393
|
+
});
|
|
394
|
+
while (Date.now() < mark.expiresAt) {
|
|
395
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(CARRIED_MARK_POLL_MS, mark.expiresAt - Date.now())));
|
|
396
|
+
if (!composer.clearBeforeSend)
|
|
397
|
+
continue;
|
|
398
|
+
assertInputOwner(ticket, cliToolId, 'the carried paste wait', false);
|
|
399
|
+
let frame;
|
|
400
|
+
try {
|
|
401
|
+
frame = await (0, tmux_1.capturePane)(sessionName, RECHECK_CAPTURE_LINES);
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// The re-check after the wait decides about a pane that cannot be read.
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if ((0, composer_clear_1.readClearComposer)(frame, cliToolId).state !== 'content')
|
|
408
|
+
continue;
|
|
409
|
+
const result = await (0, composer_clear_1.clearComposer)(sessionName, cliToolId, {
|
|
410
|
+
initialCapture: frame,
|
|
411
|
+
beforePass: () => ticket.isCurrent(),
|
|
412
|
+
});
|
|
413
|
+
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
414
|
+
logger.warn('carried-paste-withdrawn', {
|
|
415
|
+
sessionName,
|
|
416
|
+
cliToolId,
|
|
417
|
+
originGeneration: mark.originGeneration,
|
|
418
|
+
cleared: result.cleared,
|
|
419
|
+
discardedText: result.discardedText,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
(0, instance_input_lock_1.clearExpiredCarriedPasteMark)(ticket);
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
/** Whether the composer in `output` holds any text at all, read as the sender reads it. */
|
|
426
|
+
function composerHoldsText(output, cliToolId, composerSpec, message) {
|
|
427
|
+
const lines = (0, submit_composer_read_1.withoutTrailingBlankRows)((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
428
|
+
return ((0, submit_composer_read_1.readSubmitComposer)(output, cliToolId, composerSpec.reader, lines, lines.slice(-submit_composer_read_1.VERIFY_WINDOW_LINES), message)
|
|
429
|
+
.kind === 'text');
|
|
430
|
+
}
|
|
808
431
|
/**
|
|
809
432
|
* Wait until the composer shows the whole pasted body; decide whether Enter
|
|
810
433
|
* may be pressed (Issue #2464).
|
|
@@ -825,42 +448,99 @@ async function withdrawUnsentBody(sessionName, cliToolId, composerSpec) {
|
|
|
825
448
|
* drawn in ways this module has not measured, and refusing on them would turn
|
|
826
449
|
* "cannot see it" into "did not send it" for every long message they get.
|
|
827
450
|
*
|
|
451
|
+
* Issue #3945 (stage 4): an `unseen` on a measured tool is not given up at the
|
|
452
|
+
* end of the 2 s. The composer is watched for {@link LATE_PASTE_OBSERVE_MS}
|
|
453
|
+
* more, still under the input lock — so whatever appears in it is this send's
|
|
454
|
+
* (or a person's typing), never another send's — and a placeholder or body that
|
|
455
|
+
* appears is withdrawn before the throw. A read that throws is a failure like
|
|
456
|
+
* the others: withdrawn, `body-not-landed`. When the body may still arrive —
|
|
457
|
+
* nothing of it was ever seen, it was only partly there, a read failed, or the
|
|
458
|
+
* withdrawal could not be confirmed — a carried mark holds the next send off
|
|
459
|
+
* the composer ({@link leaveCarriedPasteMark}).
|
|
460
|
+
*
|
|
828
461
|
* @throws Error when the body cannot be confirmed in the composer
|
|
829
462
|
*/
|
|
830
463
|
async function confirmPasteLanded(params) {
|
|
831
|
-
const { sessionName, message, cliToolId, composer, attempts } = params;
|
|
464
|
+
const { sessionName, message, cliToolId, composer, attempts, ticket } = params;
|
|
832
465
|
let state = 'unseen';
|
|
833
466
|
let output = '';
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
467
|
+
let readError = null;
|
|
468
|
+
/** ms after the 2 s check that something of the body appeared, during the late watch. */
|
|
469
|
+
let lateAfterMs = null;
|
|
470
|
+
try {
|
|
471
|
+
for (let attempt = 0; attempt < Math.max(1, attempts); attempt++) {
|
|
472
|
+
await new Promise((resolve) => setTimeout(resolve, PASTE_LANDED_POLL_MS));
|
|
473
|
+
output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
|
|
474
|
+
state = (0, submit_composer_read_1.classifyPasteLanded)(output, cliToolId, message, composer);
|
|
475
|
+
if (state === 'landed' || state === 'mismatch')
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
if (state === 'unseen' && composer.clearBeforeSend) {
|
|
479
|
+
const observeAttempts = Math.ceil(instance_input_lock_1.LATE_PASTE_OBSERVE_MS / PASTE_LANDED_POLL_MS);
|
|
480
|
+
for (let attempt = 1; attempt <= observeAttempts; attempt++) {
|
|
481
|
+
await new Promise((resolve) => setTimeout(resolve, PASTE_LANDED_POLL_MS));
|
|
482
|
+
output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
|
|
483
|
+
state = (0, submit_composer_read_1.classifyPasteLanded)(output, cliToolId, message, composer);
|
|
484
|
+
if (state !== 'unseen') {
|
|
485
|
+
lateAfterMs = attempt * PASTE_LANDED_POLL_MS;
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
readError = error instanceof Error ? error.message : String(error);
|
|
840
493
|
}
|
|
841
|
-
if (state === 'landed')
|
|
494
|
+
if (readError === null && lateAfterMs === null && state === 'landed')
|
|
842
495
|
return;
|
|
843
|
-
if (state === 'unseen' && !composer.clearBeforeSend) {
|
|
496
|
+
if (readError === null && state === 'unseen' && !composer.clearBeforeSend) {
|
|
844
497
|
logger.warn('paste-landed-unverified', { sessionName, cliToolId, bytes: Buffer.byteLength(message, 'utf8') });
|
|
845
498
|
return;
|
|
846
499
|
}
|
|
847
500
|
const bytes = Buffer.byteLength(message, 'utf8');
|
|
848
501
|
const lineCount = message.split('\n').length;
|
|
849
|
-
const shown = describeComposer(output, cliToolId, composer, message);
|
|
850
|
-
await withdrawUnsentBody(sessionName, cliToolId, composer);
|
|
502
|
+
const shown = readError === null ? describeComposer(output, cliToolId, composer, message) : 'an unreadable pane';
|
|
503
|
+
const withdrawn = await withdrawUnsentBody(sessionName, cliToolId, composer, ticket);
|
|
851
504
|
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
505
|
+
const mayStillArrive = readError !== null || state === 'unseen' || state === 'pasting' || !withdrawn;
|
|
506
|
+
if (mayStillArrive)
|
|
507
|
+
(0, instance_input_lock_1.leaveCarriedPasteMark)(ticket);
|
|
508
|
+
logger.error('paste-not-landed', {
|
|
509
|
+
sessionName,
|
|
510
|
+
cliToolId,
|
|
511
|
+
state,
|
|
512
|
+
bytes,
|
|
513
|
+
lines: lineCount,
|
|
514
|
+
shown,
|
|
515
|
+
lateAfterMs,
|
|
516
|
+
readError,
|
|
517
|
+
withdrawn,
|
|
518
|
+
carried: mayStillArrive,
|
|
519
|
+
});
|
|
520
|
+
const what = readError !== null
|
|
521
|
+
? `the composer could not be read while the paste was checked (${readError})`
|
|
522
|
+
: lateAfterMs !== null
|
|
523
|
+
? `the paste appeared ${lateAfterMs}ms after the ${attempts * PASTE_LANDED_POLL_MS}ms check and was taken back out (the composer showed ${shown})`
|
|
524
|
+
: state === 'mismatch'
|
|
525
|
+
? `the composer does not hold the message as sent (it shows ${shown})`
|
|
526
|
+
: state === 'pasting'
|
|
527
|
+
? `the paste had not finished arriving after ${attempts * PASTE_LANDED_POLL_MS}ms (the composer shows ${shown})`
|
|
528
|
+
: `the message never appeared in the composer (${shown})`;
|
|
858
529
|
throw new Error(`Message body did not arrive intact for session ${sessionName}: ${what}; the message is ${bytes} bytes / ${lineCount} lines. ` +
|
|
859
530
|
'Enter was not pressed, so nothing was submitted (Issue #2464).');
|
|
860
531
|
}
|
|
861
|
-
/**
|
|
862
|
-
|
|
863
|
-
|
|
532
|
+
/**
|
|
533
|
+
* Whether the composer shows a paste placeholder whose count is not this body's
|
|
534
|
+
* (Issue #3945): one counting fewer or more, or more than one placeholder.
|
|
535
|
+
*/
|
|
536
|
+
function composerShowsForeignPasteToken(output, cliToolId, message, composerSpec) {
|
|
537
|
+
const lines = (0, submit_composer_read_1.withoutTrailingBlankRows)((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
538
|
+
const composer = (0, submit_composer_read_1.readSubmitComposer)(output, cliToolId, composerSpec.reader, lines, lines.slice(-submit_composer_read_1.VERIFY_WINDOW_LINES), message);
|
|
539
|
+
if (composer.kind !== 'text')
|
|
540
|
+
return false;
|
|
541
|
+
if (!composer.rows.some((row) => (0, submit_composer_read_1.findPasteTokens)(row).tokens.length > 0))
|
|
542
|
+
return false;
|
|
543
|
+
return (0, submit_composer_read_1.classifyPasteLanded)(output, cliToolId, message, composerSpec) !== 'landed';
|
|
864
544
|
}
|
|
865
545
|
/**
|
|
866
546
|
* The part of a typed body the composer reader can see (Issue #3366).
|
|
@@ -880,9 +560,9 @@ function visibleTypedBody(message) {
|
|
|
880
560
|
}
|
|
881
561
|
/** Whether the whole codex composer, read past the bounded readers (Issue #3906), is exactly `message`. */
|
|
882
562
|
function codexComposerShowsWholeBody(output, message, composerSpec) {
|
|
883
|
-
const lines = withoutTrailingBlankRows((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
884
|
-
const composer = readSubmitComposer(output, 'codex', composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES), message);
|
|
885
|
-
return composer.kind === 'text' && composer.region !== undefined && compactText(composer.region) === compactText(message);
|
|
563
|
+
const lines = (0, submit_composer_read_1.withoutTrailingBlankRows)((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
564
|
+
const composer = (0, submit_composer_read_1.readSubmitComposer)(output, 'codex', composerSpec.reader, lines, lines.slice(-submit_composer_read_1.VERIFY_WINDOW_LINES), message);
|
|
565
|
+
return composer.kind === 'text' && composer.region !== undefined && (0, submit_composer_read_1.compactText)(composer.region) === (0, submit_composer_read_1.compactText)(message);
|
|
886
566
|
}
|
|
887
567
|
/**
|
|
888
568
|
* Wait until the composer shows the typed body; only then may Enter be pressed
|
|
@@ -908,36 +588,110 @@ function codexComposerShowsWholeBody(output, message, composerSpec) {
|
|
|
908
588
|
* @throws Error when the composer does not show the body within the attempts
|
|
909
589
|
*/
|
|
910
590
|
async function confirmTypedBodyLanded(params) {
|
|
911
|
-
const { sessionName, message, cliToolId, composer, attempts } = params;
|
|
912
|
-
const expected = compactText(visibleTypedBody(message));
|
|
591
|
+
const { sessionName, message, cliToolId, composer, attempts, ticket } = params;
|
|
592
|
+
const expected = (0, submit_composer_read_1.compactText)(visibleTypedBody(message));
|
|
913
593
|
if (expected === '')
|
|
914
594
|
return;
|
|
915
595
|
let output = '';
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
596
|
+
let readError = null;
|
|
597
|
+
try {
|
|
598
|
+
for (let attempt = 0; attempt < Math.max(1, attempts); attempt++) {
|
|
599
|
+
await new Promise((resolve) => setTimeout(resolve, TYPED_BODY_POLL_MS));
|
|
600
|
+
output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
|
|
601
|
+
// Issue #3906: on codex the whole composer must show the whole body. The
|
|
602
|
+
// extractor's excerpt stops at the first blank row, so its match is only
|
|
603
|
+
// the first paragraph — a five-paragraph body with one paragraph drawn
|
|
604
|
+
// would pass on it — and it cannot reach a body of more than four blocks.
|
|
605
|
+
if (cliToolId === 'codex') {
|
|
606
|
+
if (codexComposerShowsWholeBody(output, message, composer))
|
|
607
|
+
return;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
const read = (0, composer_text_1.extractComposerText)(output, cliToolId);
|
|
611
|
+
if (read.state === 'content' && (0, submit_composer_read_1.compactText)(read.text) === expected)
|
|
925
612
|
return;
|
|
926
|
-
continue;
|
|
927
613
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
614
|
+
}
|
|
615
|
+
catch (error) {
|
|
616
|
+
readError = error instanceof Error ? error.message : String(error);
|
|
931
617
|
}
|
|
932
618
|
const bytes = Buffer.byteLength(message, 'utf8');
|
|
933
|
-
const shown = describeComposer(output, cliToolId, composer, message);
|
|
934
|
-
|
|
619
|
+
const shown = readError === null ? describeComposer(output, cliToolId, composer, message) : 'an unreadable pane';
|
|
620
|
+
// Issue #3945: a body never seen (codex can draw a burst late), a read that
|
|
621
|
+
// threw, or a withdrawal that was not confirmed may still put text in the
|
|
622
|
+
// composer after this throw — the next send waits that out.
|
|
623
|
+
const seen = readError === null && composerHoldsText(output, cliToolId, composer, message);
|
|
624
|
+
const withdrawn = await withdrawUnsentBody(sessionName, cliToolId, composer, ticket);
|
|
935
625
|
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
936
|
-
|
|
626
|
+
const mayStillArrive = !seen || !withdrawn;
|
|
627
|
+
if (mayStillArrive)
|
|
628
|
+
(0, instance_input_lock_1.leaveCarriedPasteMark)(ticket);
|
|
629
|
+
logger.error('typed-body-not-landed', { sessionName, cliToolId, bytes, attempts, shown, readError, carried: mayStillArrive });
|
|
937
630
|
throw new Error(`Message body did not appear in the composer for session ${sessionName} within ` +
|
|
938
631
|
`${attempts * TYPED_BODY_POLL_MS}ms (it shows ${shown}); the message is ${bytes} bytes. ` +
|
|
939
632
|
'Enter was not pressed, so nothing was submitted (Issue #3366).');
|
|
940
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* Whether a verdict is one the agent's own report must back (Issue #4035):
|
|
636
|
+
* `submitted` from B or F — the screen saying only that the body is not in the
|
|
637
|
+
* composer. A (generating) stands on its own. A body starting with `/` or `!`
|
|
638
|
+
* is run by the TUI itself, which is not measured to report it as a prompt.
|
|
639
|
+
*/
|
|
640
|
+
function needsPromptReport(state, rule, message) {
|
|
641
|
+
if (state !== 'submitted' || rule === 'A')
|
|
642
|
+
return false;
|
|
643
|
+
return !/^\s*[/!]/.test(message);
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Watch a session's first prompt until the agent reports it (Issue #4035).
|
|
647
|
+
*
|
|
648
|
+
* Reads the hook record and the composer every {@link PROMPT_REPORT_POLL_MS}
|
|
649
|
+
* until `deadline`, without pressing anything. The deadline is the send's, not
|
|
650
|
+
* the call's: a send that waits again after a resent Enter gets only what is
|
|
651
|
+
* left of its {@link PROMPT_REPORT_WAIT_MS} (it holds the input lock meanwhile).
|
|
652
|
+
*
|
|
653
|
+
* - the agent reported the prompt — `received`;
|
|
654
|
+
* - the screen changes its verdict — generating (A), or the body drawn into
|
|
655
|
+
* the composer after all (C/D, `pending`: the caller resends Enter, as for
|
|
656
|
+
* any typed-but-unsent body), or a completion (E) — `screen`, with the frame;
|
|
657
|
+
* - neither, to the end — `unreported`: nothing says the message left.
|
|
658
|
+
*/
|
|
659
|
+
async function awaitPromptReport(params) {
|
|
660
|
+
const { sessionName, message, cliToolId, composer, enteredAt, deadline } = params;
|
|
661
|
+
let rule = params.rule;
|
|
662
|
+
while (Date.now() < deadline) {
|
|
663
|
+
await new Promise((resolve) => setTimeout(resolve, PROMPT_REPORT_POLL_MS));
|
|
664
|
+
if ((0, prompt_submit_receipt_1.readPromptSubmitReceipt)(sessionName, cliToolId, enteredAt) === 'received')
|
|
665
|
+
return { verdict: 'received' };
|
|
666
|
+
const output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
|
|
667
|
+
const verdict = (0, submit_composer_read_1.classifySubmitWithRule)(output, cliToolId, message, composer);
|
|
668
|
+
if (verdict.state !== 'submitted' || verdict.rule === 'A')
|
|
669
|
+
return { verdict: 'screen', ...verdict, output };
|
|
670
|
+
rule = verdict.rule;
|
|
671
|
+
}
|
|
672
|
+
if ((0, prompt_submit_receipt_1.readPromptSubmitReceipt)(sessionName, cliToolId, enteredAt) === 'received')
|
|
673
|
+
return { verdict: 'received' };
|
|
674
|
+
return { verdict: 'unreported', rule };
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Say so when the pre-send clear finds the body this session last reported
|
|
678
|
+
* submitted (Issue #4035): that body never reached the agent, and the send that
|
|
679
|
+
* said `sent` for it was wrong. The instance's `lastSend` is turned
|
|
680
|
+
* `indeterminate` (the send now running records its own outcome after it).
|
|
681
|
+
*/
|
|
682
|
+
function reportSubmittedBodyInComposer(sessionName, cliToolId, composerText, cleared) {
|
|
683
|
+
if (composerText === '' || !(0, prompt_submit_receipt_1.matchesSubmittedBody)(sessionName, composerText))
|
|
684
|
+
return;
|
|
685
|
+
const target = (0, prompt_submit_receipt_1.getPromptSubmitTarget)(sessionName);
|
|
686
|
+
if (target !== null)
|
|
687
|
+
(0, send_attempt_state_1.markLastSendDiscarded)({ ...target, instanceId: target.instanceId ?? target.cliToolId });
|
|
688
|
+
logger.warn('pre-send-found-submitted-body', {
|
|
689
|
+
sessionName,
|
|
690
|
+
cliToolId,
|
|
691
|
+
cleared,
|
|
692
|
+
length: composerText.length,
|
|
693
|
+
});
|
|
694
|
+
}
|
|
941
695
|
/**
|
|
942
696
|
* Empty the composer before the body is typed into it (Issue #1880).
|
|
943
697
|
*
|
|
@@ -985,10 +739,19 @@ async function confirmTypedBodyLanded(params) {
|
|
|
985
739
|
*
|
|
986
740
|
* @throws Error when the composer still holds text after the pass cap.
|
|
987
741
|
*/
|
|
988
|
-
async function clearComposerBeforeSend(sessionName, cliToolId, composerSpec = (0, composer_spec_1.resolveComposerSpec)(cliToolId)) {
|
|
742
|
+
async function clearComposerBeforeSend(sessionName, cliToolId, composerSpec = (0, composer_spec_1.resolveComposerSpec)(cliToolId), options = {}) {
|
|
989
743
|
if (!composerSpec.clearBeforeSend)
|
|
990
744
|
return;
|
|
991
|
-
|
|
745
|
+
// Issue #3945: the first read is the re-check's frame, and every pass asks
|
|
746
|
+
// first whether this send still owns the composer.
|
|
747
|
+
const { ticket } = options;
|
|
748
|
+
const result = await (0, composer_clear_1.clearComposer)(sessionName, cliToolId, {
|
|
749
|
+
...(options.initialCapture !== undefined ? { initialCapture: options.initialCapture } : {}),
|
|
750
|
+
...(ticket !== undefined ? { beforePass: () => ticket.isCurrent() } : {}),
|
|
751
|
+
});
|
|
752
|
+
if (ticket !== undefined)
|
|
753
|
+
assertInputOwner(ticket, cliToolId, 'the pre-send clear', false);
|
|
754
|
+
reportSubmittedBodyInComposer(sessionName, cliToolId, result.discardedText, result.state !== 'content');
|
|
992
755
|
if (result.state === 'content') {
|
|
993
756
|
logger.error('pre-send-composer-clear-failed', {
|
|
994
757
|
sessionName,
|
|
@@ -1029,23 +792,59 @@ async function clearComposerBeforeSend(sessionName, cliToolId, composerSpec = (0
|
|
|
1029
792
|
* and Enter waits until the composer shows all of it (Issue #2464); a body that
|
|
1030
793
|
* does not arrive intact THROWS before Enter, with nothing submitted.
|
|
1031
794
|
*
|
|
795
|
+
* Issue #3945: the whole of it — clear, body, Enter, read-back, withdrawal —
|
|
796
|
+
* runs as the session's only writer ({@link withInstanceInputLock}); before
|
|
797
|
+
* anything is typed the pane is re-read under the lock, on every send
|
|
798
|
+
* ({@link recheckComposerBeforeInput}), and a carried paste mark is waited out
|
|
799
|
+
* ({@link waitOutCarriedPaste}, then the re-check again).
|
|
800
|
+
*
|
|
1032
801
|
* @throws Error when the body does not arrive intact, or submit cannot be
|
|
1033
|
-
* confirmed within the bounded attempts
|
|
802
|
+
* confirmed within the bounded attempts; `InstanceInputLockTimeoutError`
|
|
803
|
+
* when the composer was not free in time (nothing typed).
|
|
1034
804
|
*/
|
|
1035
805
|
async function sendMessageWithSubmitVerification(params) {
|
|
806
|
+
await (0, instance_input_lock_1.withInstanceInputLock)(params.sessionName, (ticket) => sendUnderInputLock(params, ticket));
|
|
807
|
+
}
|
|
808
|
+
async function sendUnderInputLock(params, ticket) {
|
|
1036
809
|
const { sessionName, message, cliToolId, composer = (0, composer_spec_1.resolveComposerSpec)(cliToolId), textInputWaitMs = cli_tool_timing_config_1.TUI_TEXT_INPUT_WAIT_MS, interEnterWaitMs = cli_tool_timing_config_1.TUI_TEXT_INPUT_WAIT_MS, verifyAttempts = DEFAULT_VERIFY_ATTEMPTS, verifyDelayMs = cli_tool_timing_config_1.TUI_MESSAGE_PROCESSED_WAIT_MS, pasteLandedAttempts = DEFAULT_PASTE_LANDED_ATTEMPTS, awaitTypedBody = false, typedBodyAttempts = DEFAULT_TYPED_BODY_ATTEMPTS, } = params;
|
|
1037
810
|
const submitEnterCount = params.submitEnterCount ?? composer.submitEnterCount;
|
|
811
|
+
// -1. Issue #3945 (review-1 #4): every send re-reads the pane once it owns
|
|
812
|
+
// the composer, whether or not it queued — the prompt guard and the
|
|
813
|
+
// tool's readiness wait ran before the lock, and a dialog can open in the
|
|
814
|
+
// waits between them and the first key (claude's settle wait, a queue
|
|
815
|
+
// behind another writer). A dialog refuses the send at once, before any
|
|
816
|
+
// carried paste is waited out (a refusal leaves the mark as it is); a
|
|
817
|
+
// send that did wait one out reads again after the wait. The last read
|
|
818
|
+
// is the pre-send clear's first: a claude/codex send with no mark reads
|
|
819
|
+
// no more often than before; any other tool gains this one capture.
|
|
820
|
+
let frame = await (0, composer_clear_1.recheckComposerBeforeInput)(sessionName, cliToolId);
|
|
821
|
+
if (await waitOutCarriedPaste(sessionName, cliToolId, composer, ticket)) {
|
|
822
|
+
frame = await (0, composer_clear_1.recheckComposerBeforeInput)(sessionName, cliToolId);
|
|
823
|
+
}
|
|
1038
824
|
// 0. Empty the composer first (Issue #1880). The body below is typed at the
|
|
1039
825
|
// TUI's current cursor position, so anything already in the box would be
|
|
1040
826
|
// spliced into it. Throws for a claude composer that could not be emptied.
|
|
1041
|
-
await clearComposerBeforeSend(sessionName, cliToolId, composer);
|
|
827
|
+
await clearComposerBeforeSend(sessionName, cliToolId, composer, { initialCapture: frame, ticket });
|
|
1042
828
|
if (Buffer.byteLength(message, 'utf8') > exports.LITERAL_SEND_MAX_BYTES) {
|
|
1043
829
|
// 1'. A body that would reach the TUI as more than one pty read is pasted
|
|
1044
830
|
// as ONE bracketed paste, and Enter waits until the composer shows all
|
|
1045
831
|
// of it (Issue #2464). Typed, Claude Code would keep only its last read.
|
|
1046
832
|
const pastedAt = Date.now();
|
|
1047
|
-
|
|
1048
|
-
|
|
833
|
+
assertInputOwner(ticket, cliToolId, 'the paste', false);
|
|
834
|
+
try {
|
|
835
|
+
await pasteBody(sessionName, message, ticket, cliToolId);
|
|
836
|
+
}
|
|
837
|
+
catch (error) {
|
|
838
|
+
// Whether tmux put any of it into the pane is unknown — except after a
|
|
839
|
+
// refused input boundary (Issue #3992) or a failed `load-buffer` (Issue
|
|
840
|
+
// #4011, `not-started`), both before `paste-buffer`: a mark then would
|
|
841
|
+
// only hold the next send off for nothing.
|
|
842
|
+
const notStarted = error?.delivery === 'not-started';
|
|
843
|
+
if (!(0, instance_input_lock_1.isInputBoundaryRefusal)(error) && !notStarted)
|
|
844
|
+
(0, instance_input_lock_1.leaveCarriedPasteMark)(ticket);
|
|
845
|
+
throw error;
|
|
846
|
+
}
|
|
847
|
+
await confirmPasteLanded({ sessionName, message, cliToolId, composer, attempts: pasteLandedAttempts, ticket });
|
|
1049
848
|
// 2'. Keep the tool's own registration delay as a floor.
|
|
1050
849
|
const remaining = textInputWaitMs - (Date.now() - pastedAt);
|
|
1051
850
|
if (remaining > 0) {
|
|
@@ -1063,13 +862,29 @@ async function sendMessageWithSubmitVerification(params) {
|
|
|
1063
862
|
// nowhere at all, with `rc 0`. All four measured on tmux 3.5a; the argv is
|
|
1064
863
|
// built in `lib/tmux/key-sequence.ts`, which carries the table.
|
|
1065
864
|
const typedAt = Date.now();
|
|
1066
|
-
|
|
865
|
+
assertInputOwner(ticket, cliToolId, 'the body', false);
|
|
866
|
+
// Issue #3953: the send's stamp is taken here — after the lock, the carried
|
|
867
|
+
// mark, the re-check and the clear, right before the body's first key — so a
|
|
868
|
+
// previous turn that ended during any of them is older than this send.
|
|
869
|
+
(0, instance_input_lock_1.noteInputStart)();
|
|
870
|
+
try {
|
|
871
|
+
await (0, tmux_1.sendKeys)(sessionName, message, false, { literal: true });
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
// Issue #3945 (review-1 #6): a keystroke send that failed may still have
|
|
875
|
+
// put part of the body into the pane (a timed-out `send-keys`, #3880's
|
|
876
|
+
// `indeterminate`), to be drawn after this send has let the composer go.
|
|
877
|
+
// Only a call tmux never started (`TmuxSendError.delivery`) typed nothing.
|
|
878
|
+
if (error?.delivery !== 'not-started')
|
|
879
|
+
(0, instance_input_lock_1.leaveCarriedPasteMark)(ticket);
|
|
880
|
+
throw error;
|
|
881
|
+
}
|
|
1067
882
|
// 2. Let the TUI register the input before pressing Enter. On codex that
|
|
1068
883
|
// means waiting until the body is drawn (Issue #3366): an Enter pressed
|
|
1069
884
|
// while codex still holds the keystrokes as a paste burst becomes a
|
|
1070
885
|
// newline, and the body stays unsent. The fixed wait stays as a floor.
|
|
1071
886
|
if (awaitTypedBody && composer_text_1.SUPPORTED_COMPOSER_TOOLS.has(cliToolId)) {
|
|
1072
|
-
await confirmTypedBodyLanded({ sessionName, message, cliToolId, composer, attempts: typedBodyAttempts });
|
|
887
|
+
await confirmTypedBodyLanded({ sessionName, message, cliToolId, composer, attempts: typedBodyAttempts, ticket });
|
|
1073
888
|
}
|
|
1074
889
|
const remaining = textInputWaitMs - (Date.now() - typedAt);
|
|
1075
890
|
if (remaining > 0) {
|
|
@@ -1077,7 +892,10 @@ async function sendMessageWithSubmitVerification(params) {
|
|
|
1077
892
|
}
|
|
1078
893
|
}
|
|
1079
894
|
// 3. Submit as a separate command (double Enter for vibe-local's IME mode).
|
|
895
|
+
// Issue #4035: the agent's own report of the prompt counts from here.
|
|
896
|
+
const enteredAt = Date.now();
|
|
1080
897
|
for (let i = 0; i < Math.max(1, submitEnterCount); i++) {
|
|
898
|
+
assertInputOwner(ticket, cliToolId, 'Enter', true);
|
|
1081
899
|
await (0, tmux_1.sendSpecialKeys)(sessionName, ['Enter']);
|
|
1082
900
|
if (i < submitEnterCount - 1) {
|
|
1083
901
|
await new Promise((resolve) => setTimeout(resolve, interEnterWaitMs));
|
|
@@ -1092,11 +910,62 @@ async function sendMessageWithSubmitVerification(params) {
|
|
|
1092
910
|
// was reported "could not be confirmed" — a sent message as exit 99. The
|
|
1093
911
|
// last read sends nothing: no Enter goes out that is not read back.
|
|
1094
912
|
const attempts = Math.max(1, verifyAttempts);
|
|
913
|
+
// Issue #4035: one wait for the agent's report per send, however many reads.
|
|
914
|
+
let promptReportDeadline = null;
|
|
1095
915
|
for (let attempt = 0; attempt <= attempts; attempt++) {
|
|
1096
916
|
await new Promise((resolve) => setTimeout(resolve, verifyDelayMs));
|
|
1097
|
-
|
|
1098
|
-
|
|
917
|
+
let output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
|
|
918
|
+
let { state, rule } = (0, submit_composer_read_1.classifySubmitWithRule)(output, cliToolId, message, composer);
|
|
919
|
+
const receipt = needsPromptReport(state, rule, message)
|
|
920
|
+
? (0, prompt_submit_receipt_1.readPromptSubmitReceipt)(sessionName, cliToolId, enteredAt)
|
|
921
|
+
: 'not-required';
|
|
922
|
+
// Issue #4035: every verdict names the rule that gave it, so a send reported
|
|
923
|
+
// `sent` can be traced to what the screen showed.
|
|
924
|
+
logger.info('submit-classified', { sessionName, cliToolId, attempt, state, rule, receipt });
|
|
925
|
+
if (receipt === 'awaited') {
|
|
926
|
+
// Issue #4035: the screen says only that the body is not in the composer,
|
|
927
|
+
// on a session that has not reported a prompt yet. Watch, without Enter,
|
|
928
|
+
// until the agent reports it or the composer shows the body after all.
|
|
929
|
+
promptReportDeadline ??= Date.now() + exports.PROMPT_REPORT_WAIT_MS;
|
|
930
|
+
const watched = await awaitPromptReport({
|
|
931
|
+
sessionName,
|
|
932
|
+
message,
|
|
933
|
+
cliToolId,
|
|
934
|
+
composer,
|
|
935
|
+
enteredAt,
|
|
936
|
+
rule,
|
|
937
|
+
deadline: promptReportDeadline,
|
|
938
|
+
});
|
|
939
|
+
if (watched.verdict === 'unreported') {
|
|
940
|
+
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
941
|
+
logger.error('submit-not-confirmed:prompt-unreported', {
|
|
942
|
+
sessionName,
|
|
943
|
+
cliToolId,
|
|
944
|
+
attempt,
|
|
945
|
+
rule: watched.rule,
|
|
946
|
+
waitedMs: exports.PROMPT_REPORT_WAIT_MS,
|
|
947
|
+
});
|
|
948
|
+
throw new Error(`Message submit could not be confirmed for session ${sessionName}: the composer did not show the body ` +
|
|
949
|
+
`(rule ${watched.rule}) but the agent did not report the prompt within ${exports.PROMPT_REPORT_WAIT_MS}ms ` +
|
|
950
|
+
'(Issue #4035).');
|
|
951
|
+
}
|
|
952
|
+
if (watched.verdict === 'received') {
|
|
953
|
+
logger.info('submit-confirmed-by-prompt-hook', { sessionName, cliToolId, attempt, rule });
|
|
954
|
+
state = 'submitted';
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
({ state, rule, output } = watched);
|
|
958
|
+
logger.info('submit-classified', { sessionName, cliToolId, attempt, state, rule, receipt: 'awaited' });
|
|
959
|
+
}
|
|
960
|
+
}
|
|
1099
961
|
if (state === 'submitted') {
|
|
962
|
+
// Issue #4035: only a body the screen alone called submitted is remembered
|
|
963
|
+
// for the next send's clear. One the agent reported did leave, and the same
|
|
964
|
+
// text back in the composer is claude restoring it (Esc), the history (↑),
|
|
965
|
+
// or a person typing it — not a send that never arrived.
|
|
966
|
+
if ((0, prompt_submit_receipt_1.readPromptSubmitReceipt)(sessionName, cliToolId, enteredAt) !== 'received') {
|
|
967
|
+
(0, prompt_submit_receipt_1.noteSubmittedBody)(sessionName, message);
|
|
968
|
+
}
|
|
1100
969
|
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
1101
970
|
return;
|
|
1102
971
|
}
|
|
@@ -1107,7 +976,12 @@ async function sendMessageWithSubmitVerification(params) {
|
|
|
1107
976
|
// (flavor B). Clear the input line (best-effort) and surface the failure
|
|
1108
977
|
// to the caller instead of ever resending Enter.
|
|
1109
978
|
try {
|
|
1110
|
-
|
|
979
|
+
if (ticket.isCurrent()) {
|
|
980
|
+
await (0, tmux_1.clearInputLine)(sessionName);
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
logger.error('submit-clear-input-skipped-not-owner', { sessionName, cliToolId, generation: ticket.generation });
|
|
984
|
+
}
|
|
1111
985
|
}
|
|
1112
986
|
catch (clearError) {
|
|
1113
987
|
logger.error('submit-clear-input-failed', {
|
|
@@ -1129,12 +1003,30 @@ async function sendMessageWithSubmitVerification(params) {
|
|
|
1129
1003
|
logger.warn('submit-not-confirmed:composer-unreadable', { sessionName, cliToolId, attempt });
|
|
1130
1004
|
continue;
|
|
1131
1005
|
}
|
|
1006
|
+
// Issue #3945 (review #1): a paste placeholder that does not count this
|
|
1007
|
+
// body is not this body. It can only be a paste that arrived after the
|
|
1008
|
+
// initial Enter (a carried send's, past its mark), and another Enter would
|
|
1009
|
+
// submit it. One that counts exactly this body cannot be told apart: that
|
|
1010
|
+
// Enter is the accepted gap (LATE_TOKEN_AFTER_ENTER_SAME_COUNT in the tests).
|
|
1011
|
+
if (composerShowsForeignPasteToken(output, cliToolId, message, composer)) {
|
|
1012
|
+
logger.warn('submit-not-confirmed:foreign-paste-token', { sessionName, cliToolId, attempt });
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
// Issue #4035: the Enter thought lost may only have been late — the agent
|
|
1016
|
+
// reporting the prompt now means it went, and another Enter would land on an
|
|
1017
|
+
// empty composer, or on a dialog that the prompt just opened.
|
|
1018
|
+
if ((0, prompt_submit_receipt_1.readPromptSubmitReceipt)(sessionName, cliToolId, enteredAt) === 'received') {
|
|
1019
|
+
logger.info('submit-confirmed-by-prompt-hook', { sessionName, cliToolId, attempt, rule });
|
|
1020
|
+
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1132
1023
|
// state === 'pending' — still typed-but-unsent, resend a single Enter and re-check.
|
|
1133
1024
|
logger.warn('submit-not-confirmed:resending-enter', {
|
|
1134
1025
|
sessionName,
|
|
1135
1026
|
cliToolId,
|
|
1136
1027
|
attempt,
|
|
1137
1028
|
});
|
|
1029
|
+
assertInputOwner(ticket, cliToolId, 'a resent Enter', true);
|
|
1138
1030
|
await (0, tmux_1.sendSpecialKeys)(sessionName, ['Enter']);
|
|
1139
1031
|
}
|
|
1140
1032
|
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|