commandmate 0.47.2 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +180 -180
- package/.next/app-path-routes-manifest.json +30 -30
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/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.js +3 -3
- 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.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js +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 +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route.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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +3 -3
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -103
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +2 -2
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +12 -12
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +30 -30
- package/.next/server/chunks/1191.js +11 -4
- package/.next/server/chunks/1501.js +1 -1
- package/.next/server/chunks/1601.js +2 -2
- package/.next/server/chunks/1656.js +2 -2
- package/.next/server/chunks/1945.js +3 -3
- package/.next/server/chunks/1990.js +103 -0
- package/.next/server/chunks/2069.js +1 -0
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2422.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2534.js +1 -1
- package/.next/server/chunks/255.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3505.js +2 -2
- package/.next/server/chunks/4243.js +1 -1
- package/.next/server/chunks/4248.js +1 -1
- package/.next/server/chunks/4280.js +1 -0
- package/.next/server/chunks/5209.js +1 -1
- package/.next/server/chunks/6119.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6486.js +9 -8
- package/.next/server/chunks/6781.js +1 -1
- package/.next/server/chunks/720.js +103 -0
- package/.next/server/chunks/7604.js +2 -0
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8581.js +8 -8
- package/.next/server/chunks/8728.js +3 -3
- package/.next/server/chunks/8791.js +1 -1
- package/.next/server/chunks/8807.js +19 -18
- package/.next/server/chunks/8918.js +2 -104
- package/.next/server/chunks/936.js +1 -1
- package/.next/server/chunks/9390.js +1 -1
- package/.next/server/chunks/9540.js +1 -1
- package/.next/server/chunks/9627.js +5 -5
- package/.next/server/chunks/9731.js +2 -2
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/1128-914c559913546bbc.js +1 -0
- package/.next/static/chunks/{2407-a6533805a86d05bd.js → 2407-ab92ae9837c39dce.js} +1 -1
- package/.next/static/chunks/{2863-6a07ec82fb52f4dd.js → 2863-7173dc305dffc4f6.js} +1 -1
- package/.next/static/chunks/6288-6fadbba6e84393b2.js +1 -0
- package/.next/static/chunks/7665-e9ef6e0a85b9fa49.js +1 -0
- package/.next/static/chunks/8407-35bbfc84793d7004.js +1 -0
- package/.next/static/chunks/9079-987939d231704218.js +1 -0
- package/.next/static/chunks/app/{layout-538d8d2746ad1588.js → layout-aa8e66164a4367af.js} +1 -1
- package/.next/static/chunks/app/sessions/page-cfd645305e25fd33.js +1 -0
- package/.next/static/chunks/app/skills/[skillId]/{page-52ba58712faf3378.js → page-cb6e74fd9a6a571d.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-704086656fb45699.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-a8d7886703ffc7b4.js +1 -0
- package/.next/static/css/{743f79ff19ab91e9.css → db8959555c3d9c96.css} +1 -1
- package/.next/trace +13 -13
- package/dist/cli/commands/auto-yes.d.ts +24 -0
- package/dist/cli/commands/auto-yes.d.ts.map +1 -1
- package/dist/cli/commands/auto-yes.js +76 -8
- package/dist/cli/commands/capture.d.ts.map +1 -1
- package/dist/cli/commands/capture.js +5 -0
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +10 -1
- package/dist/cli/commands/prompt.d.ts.map +1 -1
- package/dist/cli/commands/prompt.js +5 -3
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +19 -6
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +8 -1
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +101 -13
- package/dist/cli/config/duration-constants.d.ts +16 -13
- package/dist/cli/config/duration-constants.d.ts.map +1 -1
- package/dist/cli/config/duration-constants.js +21 -21
- package/dist/cli/docs/agent-operations.d.ts +2 -2
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +29 -1
- package/dist/cli/types/api-responses.d.ts +145 -3
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +4 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/resource-resolvers.d.ts +0 -18
- package/dist/cli/utils/resource-resolvers.d.ts.map +1 -1
- package/dist/cli/utils/resource-resolvers.js +1 -51
- package/dist/cli/utils/verify-runner.d.ts +24 -1
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +54 -1
- package/dist/config/auto-yes-duration.d.ts +56 -0
- package/dist/config/auto-yes-duration.d.ts.map +1 -0
- package/dist/config/auto-yes-duration.js +76 -0
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts +54 -2
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -1
- package/dist/lib/polling/auto-yes-suppression-reason.js +33 -2
- package/dist/lib/prompt-observation/types.d.ts +13 -0
- package/dist/lib/prompt-observation/types.d.ts.map +1 -1
- package/dist/server/locales/en/worktree.json +2 -1
- package/dist/server/locales/ja/worktree.json +2 -1
- package/dist/server/src/config/auto-yes-config.js +14 -19
- package/dist/server/src/config/auto-yes-duration.js +76 -0
- package/dist/server/src/lib/auto-yes-poller-answer-target.js +38 -0
- package/dist/server/src/lib/auto-yes-poller-keys.js +35 -0
- package/dist/server/src/lib/auto-yes-poller-session-presence.js +26 -0
- package/dist/server/src/lib/auto-yes-poller-tick.js +233 -0
- package/dist/server/src/lib/auto-yes-poller.js +337 -376
- package/dist/server/src/lib/auto-yes-state.js +59 -4
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +235 -19
- package/dist/server/src/lib/db/chat-db.js +5 -1
- package/dist/server/src/lib/db/verification-db.js +23 -2
- package/dist/server/src/lib/detection/composer-text.js +215 -11
- package/dist/server/src/lib/detection/option-recommendation.js +120 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice-approval-context.js +290 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice-chrome.js +193 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +402 -539
- package/dist/server/src/lib/detection/prompt-detector.js +92 -21
- package/dist/server/src/lib/detection/prompt-screen-key.js +60 -0
- package/dist/server/src/lib/detection/tools/antigravity/dialog.js +158 -35
- package/dist/server/src/lib/detection/tools/claude/detect.js +111 -10
- package/dist/server/src/lib/detection/tools/command-code/dialog.js +67 -0
- package/dist/server/src/lib/detection/tools/dialog-block.js +16 -0
- package/dist/server/src/lib/detection/upstream-faults.js +19 -2
- package/dist/server/src/lib/detection/waiting-screen-reading.js +98 -0
- package/dist/server/src/lib/hooks/permission-decision-service.js +7 -0
- package/dist/server/src/lib/hooks/structured-decision-response.js +1 -1
- package/dist/server/src/lib/polling/auto-yes-confirm-page.js +2 -2
- package/dist/server/src/lib/polling/auto-yes-confirmation-ledger-types.js +3 -0
- package/dist/server/src/lib/polling/auto-yes-confirmation-ledger.js +34 -3
- package/dist/server/src/lib/polling/auto-yes-decision-state.js +195 -0
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +139 -10
- package/dist/server/src/lib/polling/auto-yes-enter-fallback.js +14 -0
- package/dist/server/src/lib/polling/auto-yes-manual-answer.js +124 -0
- package/dist/server/src/lib/polling/auto-yes-option-effect.js +157 -0
- package/dist/server/src/lib/polling/auto-yes-policy.js +11 -5
- package/dist/server/src/lib/polling/auto-yes-poller-capture.js +15 -2
- package/dist/server/src/lib/polling/auto-yes-poller-generation.js +42 -1
- package/dist/server/src/lib/polling/auto-yes-poller-receipt-log.js +42 -5
- package/dist/server/src/lib/polling/auto-yes-poller-state.js +147 -49
- package/dist/server/src/lib/polling/auto-yes-poller-steps.js +172 -2
- package/dist/server/src/lib/polling/auto-yes-prompt-suppression.js +2 -2
- package/dist/server/src/lib/polling/auto-yes-question-choice.js +57 -0
- package/dist/server/src/lib/polling/auto-yes-resolver.js +96 -21
- package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +33 -2
- package/dist/server/src/lib/polling/auto-yes-suppression-state.js +25 -1
- package/dist/server/src/lib/polling/prompt-screen-reading.js +232 -0
- package/dist/server/src/lib/polling/response-checker-dialog-gate.js +39 -0
- package/dist/server/src/lib/polling/response-checker.js +145 -58
- package/dist/server/src/lib/prompt-answer-sender.js +104 -10
- package/dist/server/src/lib/push/notification-dedup.js +15 -2
- package/dist/server/src/lib/push/prompt-card-state.js +140 -19
- package/dist/server/src/lib/push/prompt-push-gate.js +158 -12
- package/dist/server/src/lib/push/push-sender.js +23 -3
- package/dist/server/src/lib/push/resolution-push-notifier.js +14 -0
- package/dist/server/src/lib/push/waiting-push-notifier.js +139 -17
- package/dist/server/src/lib/realtime/terminal-broadcast.js +16 -0
- package/dist/server/src/lib/session/agent-event-state.js +25 -5
- package/dist/server/src/lib/session/agent-event-turn.js +140 -46
- package/dist/server/src/lib/session/cli-session.js +21 -2
- package/dist/server/src/lib/session/composer-clear.js +56 -4
- package/dist/server/src/lib/session/current-output-builder.js +66 -12
- package/dist/server/src/lib/session/send-attempt-state.js +24 -7
- package/dist/server/src/lib/session/waiting-episode-binding-observation.js +289 -0
- package/dist/server/src/lib/session/waiting-episode-binding-resolve.js +38 -0
- package/dist/server/src/lib/session/waiting-episode-binding.js +276 -0
- package/dist/server/src/lib/session/waiting-episode-state-ops.js +152 -0
- package/dist/server/src/lib/session/waiting-episode-state-store.js +202 -0
- package/dist/server/src/lib/session/waiting-episode-state-types.js +6 -0
- package/dist/server/src/lib/session/waiting-episode-state.js +68 -91
- package/dist/server/src/lib/session/waiting-episode-verdict.js +174 -0
- package/dist/server/src/lib/tmux/tmux-capture-cache.js +76 -11
- package/dist/server/src/lib/tmux/tmux.js +79 -9
- package/dist/server/src/lib/verification/gate-runner.js +172 -16
- package/dist/server/src/lib/verification/machine-lock.js +5 -0
- package/dist/server/src/lib/ws-server.js +18 -3
- package/dist/server/src/types/auto-yes-answer-mode.js +38 -0
- package/dist/server/src/types/auto-yes-decision.js +61 -0
- package/dist/server/src/types/auto-yes.js +6 -0
- package/dist/server/src/types/prompt-reading.js +32 -0
- package/dist/types/auto-yes-answer-mode.d.ts +35 -0
- package/dist/types/auto-yes-answer-mode.d.ts.map +1 -0
- package/dist/types/auto-yes-answer-mode.js +38 -0
- package/docs/en/user-guide/webapp-guide.md +47 -13
- package/package.json +1 -1
- package/public/sw.js +76 -4
- package/release-notes/0.48.0.json +57 -0
- package/release-notes/0.49.0.json +45 -0
- package/.next/server/chunks/1559.js +0 -2
- package/.next/server/chunks/2460.js +0 -1
- package/.next/server/chunks/5595.js +0 -1
- package/.next/server/chunks/610.js +0 -1
- package/.next/server/chunks/6722.js +0 -1
- package/.next/static/chunks/5371-a549a0d2c2044d41.js +0 -1
- package/.next/static/chunks/602-1c1488959215e140.js +0 -1
- package/.next/static/chunks/6288-09feba367cc2c65e.js +0 -1
- package/.next/static/chunks/8407-155f84e5ba05f1d5.js +0 -1
- package/.next/static/chunks/9079-24f9fa549ed298f1.js +0 -1
- package/.next/static/chunks/app/sessions/page-e02f1adb1d1c1ccc.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9cf1ecc59e36a004.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-46aced20b2b1c010.js +0 -1
- /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → 3_JtAZaKMJEDFIIfkHv4n}/_buildManifest.js +0 -0
- /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → 3_JtAZaKMJEDFIIfkHv4n}/_ssgManifest.js +0 -0
|
@@ -16,6 +16,7 @@ exports.buildCompositeKey = buildCompositeKey;
|
|
|
16
16
|
exports.extractWorktreeId = extractWorktreeId;
|
|
17
17
|
exports.extractCliToolId = extractCliToolId;
|
|
18
18
|
exports.extractInstanceId = extractInstanceId;
|
|
19
|
+
exports.autoYesAnswerModeOf = autoYesAnswerModeOf;
|
|
19
20
|
exports.isAutoYesExpired = isAutoYesExpired;
|
|
20
21
|
exports.getAutoYesState = getAutoYesState;
|
|
21
22
|
exports.setAutoYesEnabled = setAutoYesEnabled;
|
|
@@ -36,12 +37,14 @@ exports.getCompositeKeysByWorktree = getCompositeKeysByWorktree;
|
|
|
36
37
|
exports.deleteAutoYesStateByWorktree = deleteAutoYesStateByWorktree;
|
|
37
38
|
exports.getEnabledAutoYesByWorktree = getEnabledAutoYesByWorktree;
|
|
38
39
|
exports.getRestartReleasedAutoYesByWorktree = getRestartReleasedAutoYesByWorktree;
|
|
40
|
+
exports.getEnabledAutoYesAnswerModeByWorktree = getEnabledAutoYesAnswerModeByWorktree;
|
|
39
41
|
exports.migrateAutoYesStateWorktreeIds = migrateAutoYesStateWorktreeIds;
|
|
40
42
|
exports.calculateBackoffInterval = calculateBackoffInterval;
|
|
41
43
|
const auto_yes_config_1 = require("../config/auto-yes-config");
|
|
42
44
|
const path_validator_1 = require("./security/path-validator");
|
|
43
45
|
const types_1 = require("./cli-tools/types");
|
|
44
46
|
const logger_1 = require("../lib/logger");
|
|
47
|
+
const auto_yes_answer_mode_1 = require("../types/auto-yes-answer-mode");
|
|
45
48
|
const global_state_1 = require("./global-state");
|
|
46
49
|
const logger = (0, logger_1.createLogger)('auto-yes-state');
|
|
47
50
|
// =============================================================================
|
|
@@ -124,6 +127,14 @@ function extractInstanceId(compositeKey) {
|
|
|
124
127
|
// parts[2] = alias instanceId; fall back to parts[1] (cliToolId) for primary keys.
|
|
125
128
|
return parts[2] ?? parts[1] ?? null;
|
|
126
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* The answer mode a state stands for (Issue #3863): its own, or
|
|
132
|
+
* `recommended` for a state that predates the field. Not meaningful on a
|
|
133
|
+
* disabled state, whose mode a later enable does not inherit.
|
|
134
|
+
*/
|
|
135
|
+
function autoYesAnswerModeOf(state) {
|
|
136
|
+
return state.answerMode ?? auto_yes_answer_mode_1.DEFAULT_AUTO_YES_ANSWER_MODE;
|
|
137
|
+
}
|
|
127
138
|
/** In-memory storage for auto-yes states (globalThis for hot reload persistence) */
|
|
128
139
|
const autoYesStates = (0, global_state_1.getOrInitGlobal)('__autoYesStates', () => new Map());
|
|
129
140
|
// =============================================================================
|
|
@@ -192,14 +203,28 @@ function getAutoYesState(worktreeId, cliToolId = 'claude', instanceId) {
|
|
|
192
203
|
* @param worktreeId - Worktree identifier
|
|
193
204
|
* @param cliToolId - CLI tool type
|
|
194
205
|
* @param enabled - Whether to enable or disable auto-yes
|
|
195
|
-
* @param duration - Optional duration in milliseconds (must be an
|
|
196
|
-
* Defaults to DEFAULT_AUTO_YES_DURATION (1 hour) when omitted.
|
|
206
|
+
* @param duration - Optional duration in milliseconds (must be an AUTO_YES_DURATIONS value,
|
|
207
|
+
* 1〜24 whole hours). Defaults to DEFAULT_AUTO_YES_DURATION (1 hour) when omitted.
|
|
208
|
+
* Issue #3913: checked at run time too — an enable with any other
|
|
209
|
+
* value throws a RangeError and leaves the state as it was.
|
|
197
210
|
* @param stopPattern - Optional regex pattern for stop condition (Issue #314).
|
|
198
211
|
* @param instanceId - Optional instance identifier (Issue #896; defaults to primary).
|
|
199
|
-
|
|
200
|
-
|
|
212
|
+
* @param answerMode - Issue #3863: how questions are answered. Omitted, an
|
|
213
|
+
* enable over a LIVE grant keeps that grant's mode, and any other enable —
|
|
214
|
+
* from off, or over a grant that has run out — starts at `recommended`.
|
|
215
|
+
* Whether the grant is live is read through {@link getAutoYesState}, which
|
|
216
|
+
* turns an expired grant into a disabled one first, so an enable at or after
|
|
217
|
+
* the deadline is a new grant, not a continuation. Ignored when disabling.
|
|
218
|
+
*/
|
|
219
|
+
function setAutoYesEnabled(worktreeId, cliToolId, enabled, duration, stopPattern, instanceId, answerMode) {
|
|
201
220
|
const key = buildCompositeKey(worktreeId, cliToolId, instanceId);
|
|
202
221
|
if (enabled) {
|
|
222
|
+
// Issue #3913: the type is not proof (a cast, or a caller outside the
|
|
223
|
+
// route's check), so the value is checked before anything is written.
|
|
224
|
+
if (duration !== undefined && !(0, auto_yes_config_1.isAllowedDuration)(duration)) {
|
|
225
|
+
throw new RangeError(`Invalid Auto-Yes duration: ${String(duration)} (must be 1-24 whole hours in ms)`);
|
|
226
|
+
}
|
|
227
|
+
const current = answerMode === undefined ? getAutoYesState(worktreeId, cliToolId, instanceId) : null;
|
|
203
228
|
const now = Date.now();
|
|
204
229
|
const effectiveDuration = duration ?? auto_yes_config_1.DEFAULT_AUTO_YES_DURATION;
|
|
205
230
|
const state = {
|
|
@@ -207,6 +232,7 @@ function setAutoYesEnabled(worktreeId, cliToolId, enabled, duration, stopPattern
|
|
|
207
232
|
enabledAt: now,
|
|
208
233
|
expiresAt: now + effectiveDuration,
|
|
209
234
|
stopPattern,
|
|
235
|
+
answerMode: answerMode ?? (current?.enabled ? autoYesAnswerModeOf(current) : auto_yes_answer_mode_1.DEFAULT_AUTO_YES_ANSWER_MODE),
|
|
210
236
|
};
|
|
211
237
|
autoYesStates.set(key, state);
|
|
212
238
|
notifyChangeAfterShutdown();
|
|
@@ -681,6 +707,35 @@ function getRestartReleasedAutoYesByWorktree() {
|
|
|
681
707
|
}
|
|
682
708
|
return byWorktree;
|
|
683
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* The answer mode of every armed instance, grouped by worktree and keyed by
|
|
712
|
+
* instance, for the worktree list (Issue #3863).
|
|
713
|
+
*
|
|
714
|
+
* Its own map rather than a field on {@link getEnabledAutoYesByWorktree}'s
|
|
715
|
+
* entries, whose `{ enabled, expiresAt }` shape the list pins (#2512). The same
|
|
716
|
+
* instances that map holds (armed, read through {@link getAutoYesState} so an
|
|
717
|
+
* expiry resolved at read time drops out), each with the mode it runs under.
|
|
718
|
+
*
|
|
719
|
+
* @returns worktreeId -> instanceId -> mode; worktrees with nothing armed are absent
|
|
720
|
+
*/
|
|
721
|
+
function getEnabledAutoYesAnswerModeByWorktree() {
|
|
722
|
+
const byWorktree = new Map();
|
|
723
|
+
// A snapshot of the keys: an expired read below rewrites its own entry.
|
|
724
|
+
for (const key of Array.from(autoYesStates.keys())) {
|
|
725
|
+
const cliToolId = extractCliToolId(key);
|
|
726
|
+
if (!cliToolId)
|
|
727
|
+
continue;
|
|
728
|
+
const worktreeId = extractWorktreeId(key);
|
|
729
|
+
const instanceId = extractInstanceId(key) ?? cliToolId;
|
|
730
|
+
const state = getAutoYesState(worktreeId, cliToolId, instanceId);
|
|
731
|
+
if (!state?.enabled)
|
|
732
|
+
continue;
|
|
733
|
+
const entries = byWorktree.get(worktreeId) ?? {};
|
|
734
|
+
entries[instanceId] = autoYesAnswerModeOf(state);
|
|
735
|
+
byWorktree.set(worktreeId, entries);
|
|
736
|
+
}
|
|
737
|
+
return byWorktree;
|
|
738
|
+
}
|
|
684
739
|
/**
|
|
685
740
|
* Re-key every auto-yes state whose worktree ID is being renamed
|
|
686
741
|
* (Issue #1621 Phase 3).
|
|
@@ -289,7 +289,7 @@ function withoutTrailingBlankRows(lines) {
|
|
|
289
289
|
end--;
|
|
290
290
|
return lines.slice(0, end);
|
|
291
291
|
}
|
|
292
|
-
/** Default bounded read
|
|
292
|
+
/** Default bounded recovery Enters (each read back) before giving up (throwing). */
|
|
293
293
|
const DEFAULT_VERIFY_ATTEMPTS = 4;
|
|
294
294
|
/**
|
|
295
295
|
* First non-blank line of the message, trimmed. This is what a TUI shows on the
|
|
@@ -427,12 +427,148 @@ function comparePasteToken(token, message) {
|
|
|
427
427
|
* and its first line is the input line. Every other frame (a one-row
|
|
428
428
|
* composer, an empty or ghost one, none at all, another tool) is read exactly
|
|
429
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.
|
|
430
453
|
*/
|
|
431
|
-
function
|
|
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) {
|
|
432
496
|
if (cliToolId === 'codex' && reader === 'input-line-marker') {
|
|
497
|
+
const raw = output.split('\n');
|
|
498
|
+
const windowStart = lines.length - windowLines.length;
|
|
433
499
|
const read = (0, composer_text_1.extractComposerText)(output, cliToolId);
|
|
434
|
-
if (read.state === 'content'
|
|
435
|
-
|
|
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
|
+
}
|
|
436
572
|
}
|
|
437
573
|
}
|
|
438
574
|
return readComposer(reader, lines, windowLines);
|
|
@@ -448,7 +584,9 @@ function readSubmitComposer(output, cliToolId, reader, lines, windowLines) {
|
|
|
448
584
|
* E. A DIFFERENT slash command is in the composer -> replaced (TUI popup
|
|
449
585
|
* autocompleted the command; clear the line and throw, never resend Enter).
|
|
450
586
|
* F. Any other non-empty text (idle placeholder/hint) -> submitted (unchanged
|
|
451
|
-
* pre-#1501 permissive default, so normal sends never spuriously fail)
|
|
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).
|
|
452
590
|
*
|
|
453
591
|
* C–F are scoped to the composer only, so the user-message echo that a TUI
|
|
454
592
|
* prints into its history above the prompt never causes a false verdict.
|
|
@@ -481,7 +619,7 @@ function classifySubmit(output, cliToolId, message, composerSpec = (0, composer_
|
|
|
481
619
|
if ((0, cli_patterns_1.detectThinking)(cliToolId, windowStr)) {
|
|
482
620
|
return 'submitted';
|
|
483
621
|
}
|
|
484
|
-
const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, windowLines);
|
|
622
|
+
const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, windowLines, message);
|
|
485
623
|
// B. No composer visible (scrolled off / dialog / still starting), or the
|
|
486
624
|
// composer is empty => the message left the box => submitted.
|
|
487
625
|
if (composer.kind !== 'text') {
|
|
@@ -502,8 +640,11 @@ function classifySubmit(output, cliToolId, message, composerSpec = (0, composer_
|
|
|
502
640
|
return 'replaced';
|
|
503
641
|
}
|
|
504
642
|
// F. Non-empty, non-command steady-state text (idle placeholder / hint) =>
|
|
505
|
-
// submitted, preserving the pre-#1501 permissive default.
|
|
506
|
-
|
|
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';
|
|
507
648
|
}
|
|
508
649
|
/**
|
|
509
650
|
* Backward-compatible boolean view of {@link classifySubmit}: submitted vs not.
|
|
@@ -530,14 +671,53 @@ function isSubmitted(output, cliToolId, message, composerSpec = (0, composer_spe
|
|
|
530
671
|
* 3. Anything else — the idle placeholder, a ghost suggestion, no composer —
|
|
531
672
|
* shows nothing of the body. `unseen`, which the caller decides about.
|
|
532
673
|
*
|
|
533
|
-
* Reads the
|
|
534
|
-
*
|
|
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.
|
|
535
690
|
*/
|
|
536
691
|
function classifyPasteLanded(output, cliToolId, message, composerSpec = (0, composer_spec_1.resolveComposerSpec)(cliToolId)) {
|
|
537
692
|
const lines = withoutTrailingBlankRows((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
538
|
-
const composer =
|
|
693
|
+
const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES), message);
|
|
539
694
|
if (composer.kind !== 'text')
|
|
540
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';
|
|
541
721
|
const { tokens, rest } = findPasteTokens(composer.text);
|
|
542
722
|
if (tokens.length === 0) {
|
|
543
723
|
if (inputMatchesBody(composer.text, message))
|
|
@@ -591,9 +771,9 @@ async function pasteBody(sessionName, message) {
|
|
|
591
771
|
}
|
|
592
772
|
}
|
|
593
773
|
/** The composer's first row, for an error message a person has to act on. */
|
|
594
|
-
function describeComposer(output, composerSpec) {
|
|
774
|
+
function describeComposer(output, cliToolId, composerSpec, message) {
|
|
595
775
|
const lines = withoutTrailingBlankRows((0, cli_patterns_1.stripAnsi)(output).split('\n'));
|
|
596
|
-
const composer =
|
|
776
|
+
const composer = readSubmitComposer(output, cliToolId, composerSpec.reader, lines, lines.slice(-VERIFY_WINDOW_LINES), message);
|
|
597
777
|
if (composer.kind === 'absent')
|
|
598
778
|
return 'no composer on screen';
|
|
599
779
|
if (composer.kind === 'empty')
|
|
@@ -666,7 +846,7 @@ async function confirmPasteLanded(params) {
|
|
|
666
846
|
}
|
|
667
847
|
const bytes = Buffer.byteLength(message, 'utf8');
|
|
668
848
|
const lineCount = message.split('\n').length;
|
|
669
|
-
const shown = describeComposer(output, composer);
|
|
849
|
+
const shown = describeComposer(output, cliToolId, composer, message);
|
|
670
850
|
await withdrawUnsentBody(sessionName, cliToolId, composer);
|
|
671
851
|
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
672
852
|
logger.error('paste-not-landed', { sessionName, cliToolId, state, bytes, lines: lineCount, shown });
|
|
@@ -698,6 +878,12 @@ function visibleTypedBody(message) {
|
|
|
698
878
|
const blank = lines.findIndex((line, index) => index > first && line.trim() === '');
|
|
699
879
|
return lines.slice(0, blank < 0 ? lines.length : blank).join('\n');
|
|
700
880
|
}
|
|
881
|
+
/** Whether the whole codex composer, read past the bounded readers (Issue #3906), is exactly `message`. */
|
|
882
|
+
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);
|
|
886
|
+
}
|
|
701
887
|
/**
|
|
702
888
|
* Wait until the composer shows the typed body; only then may Enter be pressed
|
|
703
889
|
* (Issue #3366).
|
|
@@ -711,6 +897,11 @@ function visibleTypedBody(message) {
|
|
|
711
897
|
* a prefix is not enough, because codex can draw the first keystrokes before it
|
|
712
898
|
* decides the rest are a burst.
|
|
713
899
|
*
|
|
900
|
+
* On codex the verdict is the whole composer against the whole body
|
|
901
|
+
* ({@link codexComposerShowsWholeBody}, Issue #3906): the extractor's text is
|
|
902
|
+
* the body's first paragraph at most, so on its own it would let Enter go on a
|
|
903
|
+
* body whose later paragraphs had not been drawn.
|
|
904
|
+
*
|
|
714
905
|
* A body that never appears is taken back out and the send THROWS without
|
|
715
906
|
* Enter — nothing was submitted, so the caller must not print `Message sent.`.
|
|
716
907
|
*
|
|
@@ -725,12 +916,21 @@ async function confirmTypedBodyLanded(params) {
|
|
|
725
916
|
for (let attempt = 0; attempt < Math.max(1, attempts); attempt++) {
|
|
726
917
|
await new Promise((resolve) => setTimeout(resolve, TYPED_BODY_POLL_MS));
|
|
727
918
|
output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
|
|
919
|
+
// Issue #3906: on codex the whole composer must show the whole body. The
|
|
920
|
+
// extractor's excerpt stops at the first blank row, so its match is only
|
|
921
|
+
// the first paragraph — a five-paragraph body with one paragraph drawn
|
|
922
|
+
// would pass on it — and it cannot reach a body of more than four blocks.
|
|
923
|
+
if (cliToolId === 'codex') {
|
|
924
|
+
if (codexComposerShowsWholeBody(output, message, composer))
|
|
925
|
+
return;
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
728
928
|
const read = (0, composer_text_1.extractComposerText)(output, cliToolId);
|
|
729
929
|
if (read.state === 'content' && compactText(read.text) === expected)
|
|
730
930
|
return;
|
|
731
931
|
}
|
|
732
932
|
const bytes = Buffer.byteLength(message, 'utf8');
|
|
733
|
-
const shown = describeComposer(output, composer);
|
|
933
|
+
const shown = describeComposer(output, cliToolId, composer, message);
|
|
734
934
|
await withdrawUnsentBody(sessionName, cliToolId, composer);
|
|
735
935
|
(0, tmux_capture_cache_1.invalidateCache)(sessionName);
|
|
736
936
|
logger.error('typed-body-not-landed', { sessionName, cliToolId, bytes, attempts, shown });
|
|
@@ -819,9 +1019,10 @@ async function clearComposerBeforeSend(sessionName, cliToolId, composerSpec = (0
|
|
|
819
1019
|
*
|
|
820
1020
|
* The body and Enter are always separate tmux commands (never batched), so the
|
|
821
1021
|
* TUI cannot swallow the Enter inside a bracketed-paste buffer. After the
|
|
822
|
-
* initial submit the pane is read back
|
|
823
|
-
*
|
|
824
|
-
*
|
|
1022
|
+
* initial submit the pane is read back; each time it is still pending an extra
|
|
1023
|
+
* Enter is sent, up to `verifyAttempts` of them, and every Enter — the last one
|
|
1024
|
+
* included (Issue #3880) — is read back before a verdict. If submit can never
|
|
1025
|
+
* be confirmed the function THROWS — callers must surface that as a failure, never as
|
|
825
1026
|
* success (Issues #1469/#1470/#1471).
|
|
826
1027
|
*
|
|
827
1028
|
* A body longer than {@link LITERAL_SEND_MAX_BYTES} is pasted instead of typed,
|
|
@@ -884,8 +1085,14 @@ async function sendMessageWithSubmitVerification(params) {
|
|
|
884
1085
|
}
|
|
885
1086
|
// 4. Read-back verification: confirm the message left the input box, resend
|
|
886
1087
|
// Enter while it is still pending, and throw if it never submits.
|
|
1088
|
+
//
|
|
1089
|
+
// Every Enter is followed by a read (Issue #3880, H-1): `attempts` resends
|
|
1090
|
+
// take `attempts + 1` reads. The loop used to resend on its last pending read
|
|
1091
|
+
// and throw straight after, so a message that only that last Enter submitted
|
|
1092
|
+
// was reported "could not be confirmed" — a sent message as exit 99. The
|
|
1093
|
+
// last read sends nothing: no Enter goes out that is not read back.
|
|
887
1094
|
const attempts = Math.max(1, verifyAttempts);
|
|
888
|
-
for (let attempt = 0; attempt
|
|
1095
|
+
for (let attempt = 0; attempt <= attempts; attempt++) {
|
|
889
1096
|
await new Promise((resolve) => setTimeout(resolve, verifyDelayMs));
|
|
890
1097
|
const output = await (0, tmux_1.capturePane)(sessionName, { startLine: -composer.verifyCaptureLines });
|
|
891
1098
|
const state = classifySubmit(output, cliToolId, message, composer);
|
|
@@ -913,6 +1120,15 @@ async function sendMessageWithSubmitVerification(params) {
|
|
|
913
1120
|
logger.error('submit-replaced-by-tui-completion', { sessionName, cliToolId, attempt });
|
|
914
1121
|
throw new Error(`Message was replaced by a TUI autocompletion for session ${sessionName}; the input no longer matches the sent text. Cleared the input line without submitting to avoid executing a different command.`);
|
|
915
1122
|
}
|
|
1123
|
+
// state === 'pending' / 'indeterminate' after the final read-back: give up below.
|
|
1124
|
+
if (attempt === attempts)
|
|
1125
|
+
break;
|
|
1126
|
+
// state === 'indeterminate' (Issue #3906) — no Enter: the composer does not
|
|
1127
|
+
// show the body, so another Enter could submit whatever it does show.
|
|
1128
|
+
if (state === 'indeterminate') {
|
|
1129
|
+
logger.warn('submit-not-confirmed:composer-unreadable', { sessionName, cliToolId, attempt });
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
916
1132
|
// state === 'pending' — still typed-but-unsent, resend a single Enter and re-check.
|
|
917
1133
|
logger.warn('submit-not-confirmed:resending-enter', {
|
|
918
1134
|
sessionName,
|
|
@@ -715,7 +715,11 @@ function recordAnsweredPrompt(db, params) {
|
|
|
715
715
|
}
|
|
716
716
|
const answeredAt = new Date().toISOString();
|
|
717
717
|
if (target) {
|
|
718
|
-
|
|
718
|
+
// Issue #3881 (review P2-2): the row keeps the reading it was saved with;
|
|
719
|
+
// one saved without a reading (before #3881) takes the answerer's, so a
|
|
720
|
+
// row's classification does not depend on which writer came first.
|
|
721
|
+
const reading = target.parsed.reading === undefined && promptData.reading !== undefined ? { reading: promptData.reading } : {};
|
|
722
|
+
const updated = { ...target.parsed, ...reading, status: 'answered', answer, answeredAt, answeredBy };
|
|
719
723
|
updatePromptData(db, target.id, updated);
|
|
720
724
|
return { message: getMessageById(db, target.id), created: false };
|
|
721
725
|
}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
23
|
exports.MAX_RUN_HISTORY_DAYS = exports.MAX_RUN_HISTORY_LIMIT = exports.DEFAULT_RUN_HISTORY_LIMIT = exports.VERIFICATION_GATE_SOURCES = exports.VERIFICATION_TRIGGERS = void 0;
|
|
24
|
+
exports.parseGateTiming = parseGateTiming;
|
|
24
25
|
exports.createVerificationRun = createVerificationRun;
|
|
25
26
|
exports.finishVerificationRun = finishVerificationRun;
|
|
26
27
|
exports.createGateResult = createGateResult;
|
|
@@ -54,6 +55,20 @@ exports.VERIFICATION_TRIGGERS = ['manual', 'wait', 'api', 'task'];
|
|
|
54
55
|
* The runtime tuple mirrors the CHECK constraint in migration v56.
|
|
55
56
|
*/
|
|
56
57
|
exports.VERIFICATION_GATE_SOURCES = ['builtin', 'verify.yaml', 'contract'];
|
|
58
|
+
/**
|
|
59
|
+
* How much of a gate's `log_tail` the history listing reads: enough for the
|
|
60
|
+
* `[timing]` line, which is the last line — never the log body.
|
|
61
|
+
*/
|
|
62
|
+
const TIMING_TAIL_CHARS = 200;
|
|
63
|
+
/** The last line of `logEnd`, when it is the runner's `[timing]` line. */
|
|
64
|
+
const TIMING_LINE = /(?:^|\n)\[timing\] queue=(\d+(?:\.\d+)?)s mutex=(\d+(?:\.\d+)?)s run=(\d+(?:\.\d+)?)s\s*$/;
|
|
65
|
+
/** The {@link VerificationGateTiming} of a gate whose `log_tail` ends with `logEnd`, or null. */
|
|
66
|
+
function parseGateTiming(logEnd) {
|
|
67
|
+
const match = TIMING_LINE.exec(logEnd ?? '');
|
|
68
|
+
if (!match)
|
|
69
|
+
return null;
|
|
70
|
+
return { queueSec: Number(match[1]), mutexSec: Number(match[2]), runSec: Number(match[3]) };
|
|
71
|
+
}
|
|
57
72
|
const RUN_COLUMNS = `
|
|
58
73
|
id, worktree_id, instance_id, task_id, trigger, status, base_ref, started_at, finished_at
|
|
59
74
|
`;
|
|
@@ -247,7 +262,8 @@ function clampInt(value, fallback, min, max) {
|
|
|
247
262
|
}
|
|
248
263
|
/**
|
|
249
264
|
* Recent runs across worktrees, newest first, each with its gate verdicts but
|
|
250
|
-
* no log bodies (Issue #1593).
|
|
265
|
+
* no log bodies (Issue #1593). A gate's `[timing]` line is the one piece of its
|
|
266
|
+
* log that is carried, parsed into numbers (`timing`, Issue #3884).
|
|
251
267
|
*
|
|
252
268
|
* `worktreeId` is optional because the advisor that consumes this (#1594) looks
|
|
253
269
|
* at a repository's trend, not one worktree's. Ordering follows the same
|
|
@@ -285,7 +301,8 @@ function listVerificationRunsForPeriod(db, opts = {}) {
|
|
|
285
301
|
const placeholders = runRows.map(() => '?').join(', ');
|
|
286
302
|
const gateRows = db
|
|
287
303
|
.prepare(`
|
|
288
|
-
SELECT run_id, gate_id, status, exit_code, duration_ms, source
|
|
304
|
+
SELECT run_id, gate_id, status, exit_code, duration_ms, source,
|
|
305
|
+
substr(log_tail, -${TIMING_TAIL_CHARS}) AS log_end
|
|
289
306
|
FROM verification_gate_results
|
|
290
307
|
WHERE run_id IN (${placeholders})
|
|
291
308
|
ORDER BY started_at ASC, id ASC
|
|
@@ -300,6 +317,10 @@ function listVerificationRunsForPeriod(db, opts = {}) {
|
|
|
300
317
|
durationMs: row.duration_ms,
|
|
301
318
|
source: row.source ?? null,
|
|
302
319
|
};
|
|
320
|
+
// Only the numbers leave this function: the log stays out of the listing (#3884).
|
|
321
|
+
const timing = parseGateTiming(row.log_end);
|
|
322
|
+
if (timing)
|
|
323
|
+
summary.timing = timing;
|
|
303
324
|
const bucket = byRun.get(row.run_id);
|
|
304
325
|
if (bucket)
|
|
305
326
|
bucket.push(summary);
|