devharness 0.8.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/LICENSE +21 -0
- package/README.md +185 -0
- package/build/atomic-write.d.ts +28 -0
- package/build/atomic-write.d.ts.map +1 -0
- package/build/atomic-write.js +126 -0
- package/build/atomic-write.js.map +1 -0
- package/build/cdp-manager.d.ts +524 -0
- package/build/cdp-manager.d.ts.map +1 -0
- package/build/cdp-manager.js +1828 -0
- package/build/cdp-manager.js.map +1 -0
- package/build/cdp-manager.test.d.ts +6 -0
- package/build/cdp-manager.test.d.ts.map +1 -0
- package/build/cdp-manager.test.js +546 -0
- package/build/cdp-manager.test.js.map +1 -0
- package/build/chrome-launcher.d.ts +400 -0
- package/build/chrome-launcher.d.ts.map +1 -0
- package/build/chrome-launcher.js +1071 -0
- package/build/chrome-launcher.js.map +1 -0
- package/build/chrome-launcher.test.d.ts +11 -0
- package/build/chrome-launcher.test.d.ts.map +1 -0
- package/build/chrome-launcher.test.js +204 -0
- package/build/chrome-launcher.test.js.map +1 -0
- package/build/clickable-cache.d.ts +73 -0
- package/build/clickable-cache.d.ts.map +1 -0
- package/build/clickable-cache.js +90 -0
- package/build/clickable-cache.js.map +1 -0
- package/build/command-recorder.d.ts +401 -0
- package/build/command-recorder.d.ts.map +1 -0
- package/build/command-recorder.js +701 -0
- package/build/command-recorder.js.map +1 -0
- package/build/config.d.ts +390 -0
- package/build/config.d.ts.map +1 -0
- package/build/config.js +826 -0
- package/build/config.js.map +1 -0
- package/build/config.test.d.ts +2 -0
- package/build/config.test.d.ts.map +1 -0
- package/build/config.test.js +112 -0
- package/build/config.test.js.map +1 -0
- package/build/connection-manager.d.ts +159 -0
- package/build/connection-manager.d.ts.map +1 -0
- package/build/connection-manager.js +432 -0
- package/build/connection-manager.js.map +1 -0
- package/build/connection-manager.test.d.ts +11 -0
- package/build/connection-manager.test.d.ts.map +1 -0
- package/build/connection-manager.test.js +114 -0
- package/build/connection-manager.test.js.map +1 -0
- package/build/console-monitor.d.ts +150 -0
- package/build/console-monitor.d.ts.map +1 -0
- package/build/console-monitor.js +454 -0
- package/build/console-monitor.js.map +1 -0
- package/build/dashboard/bundle.js +1 -0
- package/build/dashboard/dashboard-client.d.ts +42 -0
- package/build/dashboard/dashboard-client.d.ts.map +1 -0
- package/build/dashboard/dashboard-client.js +184 -0
- package/build/dashboard/dashboard-client.js.map +1 -0
- package/build/dashboard/dashboard-hub.d.ts +50 -0
- package/build/dashboard/dashboard-hub.d.ts.map +1 -0
- package/build/dashboard/dashboard-hub.js +654 -0
- package/build/dashboard/dashboard-hub.js.map +1 -0
- package/build/dashboard/hub-lock.d.ts +16 -0
- package/build/dashboard/hub-lock.d.ts.map +1 -0
- package/build/dashboard/hub-lock.js +77 -0
- package/build/dashboard/hub-lock.js.map +1 -0
- package/build/dashboard/index.d.ts +25 -0
- package/build/dashboard/index.d.ts.map +1 -0
- package/build/dashboard/index.js +77 -0
- package/build/dashboard/index.js.map +1 -0
- package/build/dashboard/types.d.ts +83 -0
- package/build/dashboard/types.d.ts.map +1 -0
- package/build/dashboard/types.js +6 -0
- package/build/dashboard/types.js.map +1 -0
- package/build/debug-logger.d.ts +85 -0
- package/build/debug-logger.d.ts.map +1 -0
- package/build/debug-logger.js +195 -0
- package/build/debug-logger.js.map +1 -0
- package/build/debugger-aware-wrapper.d.ts +37 -0
- package/build/debugger-aware-wrapper.d.ts.map +1 -0
- package/build/debugger-aware-wrapper.js +128 -0
- package/build/debugger-aware-wrapper.js.map +1 -0
- package/build/dom-change-monitor.d.ts +92 -0
- package/build/dom-change-monitor.d.ts.map +1 -0
- package/build/dom-change-monitor.js +437 -0
- package/build/dom-change-monitor.js.map +1 -0
- package/build/element-collector.d.ts +16 -0
- package/build/element-collector.d.ts.map +1 -0
- package/build/element-collector.js +207 -0
- package/build/element-collector.js.map +1 -0
- package/build/error-helpers.d.ts +43 -0
- package/build/error-helpers.d.ts.map +1 -0
- package/build/error-helpers.js +143 -0
- package/build/error-helpers.js.map +1 -0
- package/build/formatters/console-formatter.d.ts +160 -0
- package/build/formatters/console-formatter.d.ts.map +1 -0
- package/build/formatters/console-formatter.js +338 -0
- package/build/formatters/console-formatter.js.map +1 -0
- package/build/helpers/parser-plugins.d.ts +44 -0
- package/build/helpers/parser-plugins.d.ts.map +1 -0
- package/build/helpers/parser-plugins.js +102 -0
- package/build/helpers/parser-plugins.js.map +1 -0
- package/build/helpers/paths.d.ts +79 -0
- package/build/helpers/paths.d.ts.map +1 -0
- package/build/helpers/paths.js +168 -0
- package/build/helpers/paths.js.map +1 -0
- package/build/helpers/process-liveness.d.ts +24 -0
- package/build/helpers/process-liveness.d.ts.map +1 -0
- package/build/helpers/process-liveness.js +36 -0
- package/build/helpers/process-liveness.js.map +1 -0
- package/build/helpers/process-liveness.test.d.ts +2 -0
- package/build/helpers/process-liveness.test.d.ts.map +1 -0
- package/build/helpers/process-liveness.test.js +40 -0
- package/build/helpers/process-liveness.test.js.map +1 -0
- package/build/helpers/sequence-tree.d.ts +27 -0
- package/build/helpers/sequence-tree.d.ts.map +1 -0
- package/build/helpers/sequence-tree.js +80 -0
- package/build/helpers/sequence-tree.js.map +1 -0
- package/build/helpers/sequence-tree.test.d.ts +2 -0
- package/build/helpers/sequence-tree.test.d.ts.map +1 -0
- package/build/helpers/sequence-tree.test.js +122 -0
- package/build/helpers/sequence-tree.test.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +2007 -0
- package/build/index.js.map +1 -0
- package/build/interaction-recorder.d.ts +214 -0
- package/build/interaction-recorder.d.ts.map +1 -0
- package/build/interaction-recorder.js +2007 -0
- package/build/interaction-recorder.js.map +1 -0
- package/build/issue-tracker.d.ts +107 -0
- package/build/issue-tracker.d.ts.map +1 -0
- package/build/issue-tracker.js +582 -0
- package/build/issue-tracker.js.map +1 -0
- package/build/issue-tracker.test.d.ts +2 -0
- package/build/issue-tracker.test.d.ts.map +1 -0
- package/build/issue-tracker.test.js +88 -0
- package/build/issue-tracker.test.js.map +1 -0
- package/build/launch-chrome-port.test.d.ts +14 -0
- package/build/launch-chrome-port.test.d.ts.map +1 -0
- package/build/launch-chrome-port.test.js +97 -0
- package/build/launch-chrome-port.test.js.map +1 -0
- package/build/log-processor/orchestrator.d.ts +32 -0
- package/build/log-processor/orchestrator.d.ts.map +1 -0
- package/build/log-processor/orchestrator.js +27 -0
- package/build/log-processor/orchestrator.js.map +1 -0
- package/build/logpoint-execution-tracker.d.ts +57 -0
- package/build/logpoint-execution-tracker.d.ts.map +1 -0
- package/build/logpoint-execution-tracker.js +112 -0
- package/build/logpoint-execution-tracker.js.map +1 -0
- package/build/mcp-supervisor.d.ts +3 -0
- package/build/mcp-supervisor.d.ts.map +1 -0
- package/build/mcp-supervisor.js +211 -0
- package/build/mcp-supervisor.js.map +1 -0
- package/build/messages.d.ts +165 -0
- package/build/messages.d.ts.map +1 -0
- package/build/messages.js +574 -0
- package/build/messages.js.map +1 -0
- package/build/network-monitor.d.ts +172 -0
- package/build/network-monitor.d.ts.map +1 -0
- package/build/network-monitor.js +350 -0
- package/build/network-monitor.js.map +1 -0
- package/build/overlays.d.ts +49 -0
- package/build/overlays.d.ts.map +1 -0
- package/build/overlays.js +409 -0
- package/build/overlays.js.map +1 -0
- package/build/persistent-profiles.test.d.ts +15 -0
- package/build/persistent-profiles.test.d.ts.map +1 -0
- package/build/persistent-profiles.test.js +470 -0
- package/build/persistent-profiles.test.js.map +1 -0
- package/build/port-reserver.d.ts +32 -0
- package/build/port-reserver.d.ts.map +1 -0
- package/build/port-reserver.js +118 -0
- package/build/port-reserver.js.map +1 -0
- package/build/puppeteer-manager.d.ts +47 -0
- package/build/puppeteer-manager.d.ts.map +1 -0
- package/build/puppeteer-manager.js +116 -0
- package/build/puppeteer-manager.js.map +1 -0
- package/build/recording-coverage.test.d.ts +12 -0
- package/build/recording-coverage.test.d.ts.map +1 -0
- package/build/recording-coverage.test.js +72 -0
- package/build/recording-coverage.test.js.map +1 -0
- package/build/reference-validator.d.ts +47 -0
- package/build/reference-validator.d.ts.map +1 -0
- package/build/reference-validator.js +86 -0
- package/build/reference-validator.js.map +1 -0
- package/build/reference-validator.test.d.ts +5 -0
- package/build/reference-validator.test.d.ts.map +1 -0
- package/build/reference-validator.test.js +243 -0
- package/build/reference-validator.test.js.map +1 -0
- package/build/replay-cursor.d.ts +27 -0
- package/build/replay-cursor.d.ts.map +1 -0
- package/build/replay-cursor.js +240 -0
- package/build/replay-cursor.js.map +1 -0
- package/build/runners/docker-compose-runner.d.ts +69 -0
- package/build/runners/docker-compose-runner.d.ts.map +1 -0
- package/build/runners/docker-compose-runner.js +438 -0
- package/build/runners/docker-compose-runner.js.map +1 -0
- package/build/runners/docker-runner.d.ts +65 -0
- package/build/runners/docker-runner.d.ts.map +1 -0
- package/build/runners/docker-runner.js +414 -0
- package/build/runners/docker-runner.js.map +1 -0
- package/build/runners/index.d.ts +19 -0
- package/build/runners/index.d.ts.map +1 -0
- package/build/runners/index.js +35 -0
- package/build/runners/index.js.map +1 -0
- package/build/runners/native-runner.d.ts +95 -0
- package/build/runners/native-runner.d.ts.map +1 -0
- package/build/runners/native-runner.js +370 -0
- package/build/runners/native-runner.js.map +1 -0
- package/build/runners/types.d.ts +147 -0
- package/build/runners/types.d.ts.map +1 -0
- package/build/runners/types.js +25 -0
- package/build/runners/types.js.map +1 -0
- package/build/self-restart.d.ts +12 -0
- package/build/self-restart.d.ts.map +1 -0
- package/build/self-restart.js +41 -0
- package/build/self-restart.js.map +1 -0
- package/build/self-restart.test.d.ts +2 -0
- package/build/self-restart.test.d.ts.map +1 -0
- package/build/self-restart.test.js +34 -0
- package/build/self-restart.test.js.map +1 -0
- package/build/server-claims.d.ts +161 -0
- package/build/server-claims.d.ts.map +1 -0
- package/build/server-claims.js +382 -0
- package/build/server-claims.js.map +1 -0
- package/build/server-claims.test.d.ts +2 -0
- package/build/server-claims.test.d.ts.map +1 -0
- package/build/server-claims.test.js +197 -0
- package/build/server-claims.test.js.map +1 -0
- package/build/server-manager.d.ts +438 -0
- package/build/server-manager.d.ts.map +1 -0
- package/build/server-manager.js +1674 -0
- package/build/server-manager.js.map +1 -0
- package/build/server-manager.test.d.ts +17 -0
- package/build/server-manager.test.d.ts.map +1 -0
- package/build/server-manager.test.js +122 -0
- package/build/server-manager.test.js.map +1 -0
- package/build/server-ownership.test.d.ts +2 -0
- package/build/server-ownership.test.d.ts.map +1 -0
- package/build/server-ownership.test.js +238 -0
- package/build/server-ownership.test.js.map +1 -0
- package/build/server-watcher.d.ts +21 -0
- package/build/server-watcher.d.ts.map +1 -0
- package/build/server-watcher.js +105 -0
- package/build/server-watcher.js.map +1 -0
- package/build/session-detector.d.ts +47 -0
- package/build/session-detector.d.ts.map +1 -0
- package/build/session-detector.js +229 -0
- package/build/session-detector.js.map +1 -0
- package/build/session-detector.test.d.ts +9 -0
- package/build/session-detector.test.d.ts.map +1 -0
- package/build/session-detector.test.js +45 -0
- package/build/session-detector.test.js.map +1 -0
- package/build/sourcemap-handler.d.ts +114 -0
- package/build/sourcemap-handler.d.ts.map +1 -0
- package/build/sourcemap-handler.js +479 -0
- package/build/sourcemap-handler.js.map +1 -0
- package/build/supervisor/child-manager.d.ts +43 -0
- package/build/supervisor/child-manager.d.ts.map +1 -0
- package/build/supervisor/child-manager.js +148 -0
- package/build/supervisor/child-manager.js.map +1 -0
- package/build/supervisor/child-manager.test.d.ts +2 -0
- package/build/supervisor/child-manager.test.d.ts.map +1 -0
- package/build/supervisor/child-manager.test.js +107 -0
- package/build/supervisor/child-manager.test.js.map +1 -0
- package/build/supervisor/client-watcher.d.ts +48 -0
- package/build/supervisor/client-watcher.d.ts.map +1 -0
- package/build/supervisor/client-watcher.js +186 -0
- package/build/supervisor/client-watcher.js.map +1 -0
- package/build/supervisor/client-watcher.test.d.ts +2 -0
- package/build/supervisor/client-watcher.test.d.ts.map +1 -0
- package/build/supervisor/client-watcher.test.js +121 -0
- package/build/supervisor/client-watcher.test.js.map +1 -0
- package/build/supervisor/idle-config.d.ts +30 -0
- package/build/supervisor/idle-config.d.ts.map +1 -0
- package/build/supervisor/idle-config.js +76 -0
- package/build/supervisor/idle-config.js.map +1 -0
- package/build/supervisor/idle-config.test.d.ts +2 -0
- package/build/supervisor/idle-config.test.d.ts.map +1 -0
- package/build/supervisor/idle-config.test.js +119 -0
- package/build/supervisor/idle-config.test.js.map +1 -0
- package/build/supervisor/ndjson-reader.d.ts +47 -0
- package/build/supervisor/ndjson-reader.d.ts.map +1 -0
- package/build/supervisor/ndjson-reader.js +75 -0
- package/build/supervisor/ndjson-reader.js.map +1 -0
- package/build/supervisor/ndjson-reader.test.d.ts +2 -0
- package/build/supervisor/ndjson-reader.test.d.ts.map +1 -0
- package/build/supervisor/ndjson-reader.test.js +92 -0
- package/build/supervisor/ndjson-reader.test.js.map +1 -0
- package/build/supervisor/pidfile.d.ts +11 -0
- package/build/supervisor/pidfile.d.ts.map +1 -0
- package/build/supervisor/pidfile.js +29 -0
- package/build/supervisor/pidfile.js.map +1 -0
- package/build/supervisor/pidfile.test.d.ts +8 -0
- package/build/supervisor/pidfile.test.d.ts.map +1 -0
- package/build/supervisor/pidfile.test.js +46 -0
- package/build/supervisor/pidfile.test.js.map +1 -0
- package/build/supervisor/restart-coordinator.d.ts +73 -0
- package/build/supervisor/restart-coordinator.d.ts.map +1 -0
- package/build/supervisor/restart-coordinator.js +300 -0
- package/build/supervisor/restart-coordinator.js.map +1 -0
- package/build/supervisor/restart-coordinator.test.d.ts +2 -0
- package/build/supervisor/restart-coordinator.test.d.ts.map +1 -0
- package/build/supervisor/restart-coordinator.test.js +434 -0
- package/build/supervisor/restart-coordinator.test.js.map +1 -0
- package/build/test-support/fake-execute-tool-call.d.ts +11 -0
- package/build/test-support/fake-execute-tool-call.d.ts.map +1 -0
- package/build/test-support/fake-execute-tool-call.js +19 -0
- package/build/test-support/fake-execute-tool-call.js.map +1 -0
- package/build/tool-error.d.ts +14 -0
- package/build/tool-error.d.ts.map +1 -0
- package/build/tool-error.js +18 -0
- package/build/tool-error.js.map +1 -0
- package/build/tool-response.d.ts +293 -0
- package/build/tool-response.d.ts.map +1 -0
- package/build/tool-response.js +365 -0
- package/build/tool-response.js.map +1 -0
- package/build/tools/assert-tools.d.ts +62 -0
- package/build/tools/assert-tools.d.ts.map +1 -0
- package/build/tools/assert-tools.js +330 -0
- package/build/tools/assert-tools.js.map +1 -0
- package/build/tools/breakpoint-await-abort.test.d.ts +2 -0
- package/build/tools/breakpoint-await-abort.test.d.ts.map +1 -0
- package/build/tools/breakpoint-await-abort.test.js +124 -0
- package/build/tools/breakpoint-await-abort.test.js.map +1 -0
- package/build/tools/breakpoint-tools.d.ts +99 -0
- package/build/tools/breakpoint-tools.d.ts.map +1 -0
- package/build/tools/breakpoint-tools.js +1197 -0
- package/build/tools/breakpoint-tools.js.map +1 -0
- package/build/tools/config-tools.d.ts +58 -0
- package/build/tools/config-tools.d.ts.map +1 -0
- package/build/tools/config-tools.js +180 -0
- package/build/tools/config-tools.js.map +1 -0
- package/build/tools/connection-routing.test.d.ts +12 -0
- package/build/tools/connection-routing.test.d.ts.map +1 -0
- package/build/tools/connection-routing.test.js +206 -0
- package/build/tools/connection-routing.test.js.map +1 -0
- package/build/tools/console-tools.d.ts +84 -0
- package/build/tools/console-tools.d.ts.map +1 -0
- package/build/tools/console-tools.js +241 -0
- package/build/tools/console-tools.js.map +1 -0
- package/build/tools/content-tools.d.ts +74 -0
- package/build/tools/content-tools.d.ts.map +1 -0
- package/build/tools/content-tools.js +582 -0
- package/build/tools/content-tools.js.map +1 -0
- package/build/tools/dashboard-tools.d.ts +38 -0
- package/build/tools/dashboard-tools.d.ts.map +1 -0
- package/build/tools/dashboard-tools.js +123 -0
- package/build/tools/dashboard-tools.js.map +1 -0
- package/build/tools/dom-tools.d.ts +41 -0
- package/build/tools/dom-tools.d.ts.map +1 -0
- package/build/tools/dom-tools.js +277 -0
- package/build/tools/dom-tools.js.map +1 -0
- package/build/tools/download-tools.d.ts +55 -0
- package/build/tools/download-tools.d.ts.map +1 -0
- package/build/tools/download-tools.js +280 -0
- package/build/tools/download-tools.js.map +1 -0
- package/build/tools/execution-tools.d.ts +40 -0
- package/build/tools/execution-tools.d.ts.map +1 -0
- package/build/tools/execution-tools.js +121 -0
- package/build/tools/execution-tools.js.map +1 -0
- package/build/tools/input-abort.test.d.ts +2 -0
- package/build/tools/input-abort.test.d.ts.map +1 -0
- package/build/tools/input-abort.test.js +227 -0
- package/build/tools/input-abort.test.js.map +1 -0
- package/build/tools/input-ambiguous-selector.test.d.ts +2 -0
- package/build/tools/input-ambiguous-selector.test.d.ts.map +1 -0
- package/build/tools/input-ambiguous-selector.test.js +34 -0
- package/build/tools/input-ambiguous-selector.test.js.map +1 -0
- package/build/tools/input-tools.d.ts +168 -0
- package/build/tools/input-tools.d.ts.map +1 -0
- package/build/tools/input-tools.js +1397 -0
- package/build/tools/input-tools.js.map +1 -0
- package/build/tools/inspection-tools.d.ts +112 -0
- package/build/tools/inspection-tools.d.ts.map +1 -0
- package/build/tools/inspection-tools.js +637 -0
- package/build/tools/inspection-tools.js.map +1 -0
- package/build/tools/interpolation.d.ts +30 -0
- package/build/tools/interpolation.d.ts.map +1 -0
- package/build/tools/interpolation.js +143 -0
- package/build/tools/interpolation.js.map +1 -0
- package/build/tools/issues-abort-hygiene.test.d.ts +2 -0
- package/build/tools/issues-abort-hygiene.test.d.ts.map +1 -0
- package/build/tools/issues-abort-hygiene.test.js +79 -0
- package/build/tools/issues-abort-hygiene.test.js.map +1 -0
- package/build/tools/issues-tools.d.ts +98 -0
- package/build/tools/issues-tools.d.ts.map +1 -0
- package/build/tools/issues-tools.js +821 -0
- package/build/tools/issues-tools.js.map +1 -0
- package/build/tools/issues-tools.test.d.ts +8 -0
- package/build/tools/issues-tools.test.d.ts.map +1 -0
- package/build/tools/issues-tools.test.js +184 -0
- package/build/tools/issues-tools.test.js.map +1 -0
- package/build/tools/modal-tools.d.ts +73 -0
- package/build/tools/modal-tools.d.ts.map +1 -0
- package/build/tools/modal-tools.js +161 -0
- package/build/tools/modal-tools.js.map +1 -0
- package/build/tools/network-tools.d.ts +72 -0
- package/build/tools/network-tools.d.ts.map +1 -0
- package/build/tools/network-tools.js +432 -0
- package/build/tools/network-tools.js.map +1 -0
- package/build/tools/page-tools.d.ts +78 -0
- package/build/tools/page-tools.d.ts.map +1 -0
- package/build/tools/page-tools.js +255 -0
- package/build/tools/page-tools.js.map +1 -0
- package/build/tools/plugin-tools.d.ts +16 -0
- package/build/tools/plugin-tools.d.ts.map +1 -0
- package/build/tools/plugin-tools.js +17 -0
- package/build/tools/plugin-tools.js.map +1 -0
- package/build/tools/replay-abort.test.d.ts +2 -0
- package/build/tools/replay-abort.test.d.ts.map +1 -0
- package/build/tools/replay-abort.test.js +259 -0
- package/build/tools/replay-abort.test.js.map +1 -0
- package/build/tools/replay-add-conditional.test.d.ts +2 -0
- package/build/tools/replay-add-conditional.test.d.ts.map +1 -0
- package/build/tools/replay-add-conditional.test.js +205 -0
- package/build/tools/replay-add-conditional.test.js.map +1 -0
- package/build/tools/replay-background-run.test.d.ts +2 -0
- package/build/tools/replay-background-run.test.d.ts.map +1 -0
- package/build/tools/replay-background-run.test.js +176 -0
- package/build/tools/replay-background-run.test.js.map +1 -0
- package/build/tools/replay-capture-variables.test.d.ts +2 -0
- package/build/tools/replay-capture-variables.test.d.ts.map +1 -0
- package/build/tools/replay-capture-variables.test.js +331 -0
- package/build/tools/replay-capture-variables.test.js.map +1 -0
- package/build/tools/replay-conditional-idb-and-launch.test.d.ts +11 -0
- package/build/tools/replay-conditional-idb-and-launch.test.d.ts.map +1 -0
- package/build/tools/replay-conditional-idb-and-launch.test.js +429 -0
- package/build/tools/replay-conditional-idb-and-launch.test.js.map +1 -0
- package/build/tools/replay-conditional-timeout.test.d.ts +14 -0
- package/build/tools/replay-conditional-timeout.test.d.ts.map +1 -0
- package/build/tools/replay-conditional-timeout.test.js +126 -0
- package/build/tools/replay-conditional-timeout.test.js.map +1 -0
- package/build/tools/replay-declare.test.d.ts +2 -0
- package/build/tools/replay-declare.test.d.ts.map +1 -0
- package/build/tools/replay-declare.test.js +106 -0
- package/build/tools/replay-declare.test.js.map +1 -0
- package/build/tools/replay-declared-browser-cleanup.test.d.ts +2 -0
- package/build/tools/replay-declared-browser-cleanup.test.d.ts.map +1 -0
- package/build/tools/replay-declared-browser-cleanup.test.js +103 -0
- package/build/tools/replay-declared-browser-cleanup.test.js.map +1 -0
- package/build/tools/replay-declared-profile.test.d.ts +2 -0
- package/build/tools/replay-declared-profile.test.d.ts.map +1 -0
- package/build/tools/replay-declared-profile.test.js +116 -0
- package/build/tools/replay-declared-profile.test.js.map +1 -0
- package/build/tools/replay-executor.d.ts +556 -0
- package/build/tools/replay-executor.d.ts.map +1 -0
- package/build/tools/replay-executor.js +2347 -0
- package/build/tools/replay-executor.js.map +1 -0
- package/build/tools/replay-folder-save.test.d.ts +2 -0
- package/build/tools/replay-folder-save.test.d.ts.map +1 -0
- package/build/tools/replay-folder-save.test.js +63 -0
- package/build/tools/replay-folder-save.test.js.map +1 -0
- package/build/tools/replay-formatters.d.ts +148 -0
- package/build/tools/replay-formatters.d.ts.map +1 -0
- package/build/tools/replay-formatters.js +856 -0
- package/build/tools/replay-formatters.js.map +1 -0
- package/build/tools/replay-kill-chrome-on-finish.test.d.ts +2 -0
- package/build/tools/replay-kill-chrome-on-finish.test.d.ts.map +1 -0
- package/build/tools/replay-kill-chrome-on-finish.test.js +175 -0
- package/build/tools/replay-kill-chrome-on-finish.test.js.map +1 -0
- package/build/tools/replay-multi-connection.test.d.ts +2 -0
- package/build/tools/replay-multi-connection.test.d.ts.map +1 -0
- package/build/tools/replay-multi-connection.test.js +547 -0
- package/build/tools/replay-multi-connection.test.js.map +1 -0
- package/build/tools/replay-output-and-record-options.test.d.ts +2 -0
- package/build/tools/replay-output-and-record-options.test.d.ts.map +1 -0
- package/build/tools/replay-output-and-record-options.test.js +240 -0
- package/build/tools/replay-output-and-record-options.test.js.map +1 -0
- package/build/tools/replay-production-error-shape.test.d.ts +7 -0
- package/build/tools/replay-production-error-shape.test.d.ts.map +1 -0
- package/build/tools/replay-production-error-shape.test.js +93 -0
- package/build/tools/replay-production-error-shape.test.js.map +1 -0
- package/build/tools/replay-rebase.test.d.ts +2 -0
- package/build/tools/replay-rebase.test.d.ts.map +1 -0
- package/build/tools/replay-rebase.test.js +58 -0
- package/build/tools/replay-rebase.test.js.map +1 -0
- package/build/tools/replay-retry-classification.test.d.ts +9 -0
- package/build/tools/replay-retry-classification.test.d.ts.map +1 -0
- package/build/tools/replay-retry-classification.test.js +57 -0
- package/build/tools/replay-retry-classification.test.js.map +1 -0
- package/build/tools/replay-run-registry.d.ts +55 -0
- package/build/tools/replay-run-registry.d.ts.map +1 -0
- package/build/tools/replay-run-registry.js +65 -0
- package/build/tools/replay-run-registry.js.map +1 -0
- package/build/tools/replay-sequence-reload.test.d.ts +2 -0
- package/build/tools/replay-sequence-reload.test.d.ts.map +1 -0
- package/build/tools/replay-sequence-reload.test.js +82 -0
- package/build/tools/replay-sequence-reload.test.js.map +1 -0
- package/build/tools/replay-socket-scope.test.d.ts +2 -0
- package/build/tools/replay-socket-scope.test.d.ts.map +1 -0
- package/build/tools/replay-socket-scope.test.js +75 -0
- package/build/tools/replay-socket-scope.test.js.map +1 -0
- package/build/tools/replay-step-connection.test.d.ts +2 -0
- package/build/tools/replay-step-connection.test.d.ts.map +1 -0
- package/build/tools/replay-step-connection.test.js +292 -0
- package/build/tools/replay-step-connection.test.js.map +1 -0
- package/build/tools/replay-step-timeout.test.d.ts +2 -0
- package/build/tools/replay-step-timeout.test.d.ts.map +1 -0
- package/build/tools/replay-step-timeout.test.js +204 -0
- package/build/tools/replay-step-timeout.test.js.map +1 -0
- package/build/tools/replay-tags.test.d.ts +2 -0
- package/build/tools/replay-tags.test.d.ts.map +1 -0
- package/build/tools/replay-tags.test.js +99 -0
- package/build/tools/replay-tags.test.js.map +1 -0
- package/build/tools/replay-teardown-and-foreach.test.d.ts +13 -0
- package/build/tools/replay-teardown-and-foreach.test.d.ts.map +1 -0
- package/build/tools/replay-teardown-and-foreach.test.js +318 -0
- package/build/tools/replay-teardown-and-foreach.test.js.map +1 -0
- package/build/tools/replay-tool-name-validation.test.d.ts +2 -0
- package/build/tools/replay-tool-name-validation.test.d.ts.map +1 -0
- package/build/tools/replay-tool-name-validation.test.js +280 -0
- package/build/tools/replay-tool-name-validation.test.js.map +1 -0
- package/build/tools/replay-tools.d.ts +288 -0
- package/build/tools/replay-tools.d.ts.map +1 -0
- package/build/tools/replay-tools.js +2994 -0
- package/build/tools/replay-tools.js.map +1 -0
- package/build/tools/request-abort.test.d.ts +2 -0
- package/build/tools/request-abort.test.d.ts.map +1 -0
- package/build/tools/request-abort.test.js +130 -0
- package/build/tools/request-abort.test.js.map +1 -0
- package/build/tools/request-tools.d.ts +56 -0
- package/build/tools/request-tools.d.ts.map +1 -0
- package/build/tools/request-tools.js +174 -0
- package/build/tools/request-tools.js.map +1 -0
- package/build/tools/screenshot-tools.d.ts +135 -0
- package/build/tools/screenshot-tools.d.ts.map +1 -0
- package/build/tools/screenshot-tools.js +636 -0
- package/build/tools/screenshot-tools.js.map +1 -0
- package/build/tools/server-tools.d.ts +88 -0
- package/build/tools/server-tools.d.ts.map +1 -0
- package/build/tools/server-tools.js +485 -0
- package/build/tools/server-tools.js.map +1 -0
- package/build/tools/source-tools.d.ts +46 -0
- package/build/tools/source-tools.d.ts.map +1 -0
- package/build/tools/source-tools.js +47 -0
- package/build/tools/source-tools.js.map +1 -0
- package/build/tools/storage-tools.d.ts +141 -0
- package/build/tools/storage-tools.d.ts.map +1 -0
- package/build/tools/storage-tools.js +894 -0
- package/build/tools/storage-tools.js.map +1 -0
- package/build/tools/storage-tools.test.d.ts +13 -0
- package/build/tools/storage-tools.test.d.ts.map +1 -0
- package/build/tools/storage-tools.test.js +707 -0
- package/build/tools/storage-tools.test.js.map +1 -0
- package/build/tools/tab-tools.d.ts +42 -0
- package/build/tools/tab-tools.d.ts.map +1 -0
- package/build/tools/tab-tools.js +296 -0
- package/build/tools/tab-tools.js.map +1 -0
- package/build/tools/wait-tools.d.ts +85 -0
- package/build/tools/wait-tools.d.ts.map +1 -0
- package/build/tools/wait-tools.js +236 -0
- package/build/tools/wait-tools.js.map +1 -0
- package/build/tools/wait-tools.test.d.ts +9 -0
- package/build/tools/wait-tools.test.d.ts.map +1 -0
- package/build/tools/wait-tools.test.js +193 -0
- package/build/tools/wait-tools.test.js.map +1 -0
- package/build/types.d.ts +133 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +5 -0
- package/build/types.js.map +1 -0
- package/build/ui-verifier.d.ts +88 -0
- package/build/ui-verifier.d.ts.map +1 -0
- package/build/ui-verifier.js +540 -0
- package/build/ui-verifier.js.map +1 -0
- package/build/utils/abort.d.ts +68 -0
- package/build/utils/abort.d.ts.map +1 -0
- package/build/utils/abort.js +159 -0
- package/build/utils/abort.js.map +1 -0
- package/build/utils/abort.test.d.ts +2 -0
- package/build/utils/abort.test.d.ts.map +1 -0
- package/build/utils/abort.test.js +174 -0
- package/build/utils/abort.test.js.map +1 -0
- package/build/utils/modal-detection-core.d.ts +150 -0
- package/build/utils/modal-detection-core.d.ts.map +1 -0
- package/build/utils/modal-detection-core.js +332 -0
- package/build/utils/modal-detection-core.js.map +1 -0
- package/build/utils/modal-detection-core.test.d.ts +5 -0
- package/build/utils/modal-detection-core.test.d.ts.map +1 -0
- package/build/utils/modal-detection-core.test.js +492 -0
- package/build/utils/modal-detection-core.test.js.map +1 -0
- package/build/utils/modal-detector.d.ts +29 -0
- package/build/utils/modal-detector.d.ts.map +1 -0
- package/build/utils/modal-detector.js +281 -0
- package/build/utils/modal-detector.js.map +1 -0
- package/build/utils/modal-dismissal.d.ts +32 -0
- package/build/utils/modal-dismissal.d.ts.map +1 -0
- package/build/utils/modal-dismissal.js +190 -0
- package/build/utils/modal-dismissal.js.map +1 -0
- package/build/utils/port-check.d.ts +31 -0
- package/build/utils/port-check.d.ts.map +1 -0
- package/build/utils/port-check.js +87 -0
- package/build/utils/port-check.js.map +1 -0
- package/build/utils/port-check.test.d.ts +2 -0
- package/build/utils/port-check.test.d.ts.map +1 -0
- package/build/utils/port-check.test.js +66 -0
- package/build/utils/port-check.test.js.map +1 -0
- package/build/utils/selector-resolver.d.ts +66 -0
- package/build/utils/selector-resolver.d.ts.map +1 -0
- package/build/utils/selector-resolver.js +200 -0
- package/build/utils/selector-resolver.js.map +1 -0
- package/build/validation-helpers.d.ts +41 -0
- package/build/validation-helpers.d.ts.map +1 -0
- package/build/validation-helpers.js +236 -0
- package/build/validation-helpers.js.map +1 -0
- package/build/window-sizing.d.ts +26 -0
- package/build/window-sizing.d.ts.map +1 -0
- package/build/window-sizing.js +87 -0
- package/build/window-sizing.js.map +1 -0
- package/build/window-sizing.test.d.ts +8 -0
- package/build/window-sizing.test.d.ts.map +1 -0
- package/build/window-sizing.test.js +113 -0
- package/build/window-sizing.test.js.map +1 -0
- package/docs/README.md +74 -0
- package/docs/automation.md +562 -0
- package/docs/debugging.md +229 -0
- package/docs/installation.md +133 -0
- package/docs/instructions.md +227 -0
- package/docs/mcp-instructions.md +15 -0
- package/docs/message-system.md +327 -0
- package/docs/messages.md +3480 -0
- package/docs/parser-plugins.md +132 -0
- package/docs/replay.md +1411 -0
- package/docs/troubleshooting.md +272 -0
- package/docs/working_with_llm_notes.md +5 -0
- package/package.json +87 -0
- package/skills/devharness/SKILL.md +195 -0
- package/skills/devharness/references/sequences.md +613 -0
- package/skills/devharness/references/tool-categories.md +99 -0
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issues Tools
|
|
3
|
+
* MCP tools for tracking bugs and features as Markdown files (title + body + labels + comments)
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { promises as fs } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { createTool } from '../validation-helpers.js';
|
|
9
|
+
import { createSuccessResponse, createErrorResponse } from '../messages.js';
|
|
10
|
+
import { showVerificationOverlay, showTestReadyOverlay } from '../interaction-recorder.js';
|
|
11
|
+
import { showOverlay, getWorkOnNoSequenceConfig, } from '../overlays.js';
|
|
12
|
+
import { requireValidReference } from '../reference-validator.js';
|
|
13
|
+
import { initializeTracker, addIssue, getIssue, getIssues, updateIssueStatus, updateIssueSequenceFile, addIssueComment, acknowledgeAllBugs, getIssueSequencesDir, generateSequenceFilename, } from '../issue-tracker.js';
|
|
14
|
+
import { checkUrlPort } from '../utils/port-check.js';
|
|
15
|
+
const issuesSchema = z.object({
|
|
16
|
+
action: z.enum(['list', 'create', 'workOn', 'resolve', 'acknowledge', 'comment'])
|
|
17
|
+
.describe('Issue action: list (list all issues), create (create new issue), workOn (start working on issue), resolve (opens an interactive browser verification flow and waits for a PERSON to click Fixed/Not Fixed - a human must physically confirm before the issue is marked fixed/implemented, so an agent cannot close an issue this way and should use `comment` to record findings instead), acknowledge (acknowledge pending bugs), comment (append a comment to an issue)'),
|
|
18
|
+
id: z.number().optional()
|
|
19
|
+
.describe('Issue ID (for workOn, resolve, comment actions)'),
|
|
20
|
+
type: z.enum(['bug', 'feature']).optional()
|
|
21
|
+
.describe('Issue type (required for create, optional filter for list)'),
|
|
22
|
+
status: z.enum(['pending', 'acknowledged', 'in_progress', 'fixed', 'implemented']).optional()
|
|
23
|
+
.describe('Issue status (optional filter for list)'),
|
|
24
|
+
title: z.string().optional()
|
|
25
|
+
.describe('Short one-line issue title (required for create)'),
|
|
26
|
+
body: z.string().optional()
|
|
27
|
+
.describe('Markdown body: steps to reproduce, expected/actual, code blocks, etc (optional for create)'),
|
|
28
|
+
labels: z.array(z.string()).optional()
|
|
29
|
+
.describe('Labels to attach (create) or filter by - matches issues with ANY of the given labels (list)'),
|
|
30
|
+
text: z.string().optional()
|
|
31
|
+
.describe('Comment text in Markdown (required for comment action)'),
|
|
32
|
+
sequenceName: z.string().optional()
|
|
33
|
+
.describe('Name of existing sequence to link (for create - moves sequence to issues folder)'),
|
|
34
|
+
startUrl: z.string().optional()
|
|
35
|
+
.describe('Starting URL for manual issue verification (required for create when no sequenceName provided)'),
|
|
36
|
+
connectionReason: z.string().optional()
|
|
37
|
+
.describe('Browser connection reference (for workOn - to replay sequence)'),
|
|
38
|
+
connections: z.record(z.string()).optional()
|
|
39
|
+
.describe("workOn/resolve: rebind a multi-connection repro sequence's recorded references onto this session - { \"<recorded reference>\": \"<reference here>\" }"),
|
|
40
|
+
keepBrowserOpen: z.boolean().optional()
|
|
41
|
+
.describe('Keep browser tab open after verification (default: false, closes tab after resolve)'),
|
|
42
|
+
search: z.string().optional()
|
|
43
|
+
.describe('Search term to filter issues by title, body, comments, or recording name (for list)'),
|
|
44
|
+
includeCompleted: z.boolean().optional()
|
|
45
|
+
.describe('Include fixed/implemented issues in list (default: false, only shows active issues)'),
|
|
46
|
+
includeSequence: z.boolean().optional()
|
|
47
|
+
.describe('Include sequence recording for issue (default: true). When false, no sequence is created and Chrome does not open.'),
|
|
48
|
+
}).strict();
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// resolve() human-verification timeout
|
|
51
|
+
// =============================================================================
|
|
52
|
+
/**
|
|
53
|
+
* Upper bound on how long `resolve` will wait for a human to respond to the
|
|
54
|
+
* interactive "ready to begin?" / "is this fixed?" overlays. Kept comfortably
|
|
55
|
+
* under Puppeteer's default protocolTimeout (180_000ms) so that a human
|
|
56
|
+
* simply walking away surfaces as our own typed ISSUES_RESOLVE_TIMEOUT error
|
|
57
|
+
* instead of a raw `Runtime.callFunctionOn timed out` leaking through from
|
|
58
|
+
* the underlying page.evaluate() call that waits on the overlay's Promise.
|
|
59
|
+
* Exported so tests can pass a much shorter value.
|
|
60
|
+
*/
|
|
61
|
+
export const DEFAULT_RESOLVE_VERIFICATION_TIMEOUT_MS = 150_000;
|
|
62
|
+
/** Internal marker so the resolve handler can distinguish "human never answered" from other failures. */
|
|
63
|
+
class IssuesResolveTimeoutError extends Error {
|
|
64
|
+
issueId;
|
|
65
|
+
timeoutMs;
|
|
66
|
+
constructor(issueId, timeoutMs) {
|
|
67
|
+
super(`Timed out after ${timeoutMs}ms waiting for human verification on issue #${issueId}`);
|
|
68
|
+
this.issueId = issueId;
|
|
69
|
+
this.timeoutMs = timeoutMs;
|
|
70
|
+
this.name = 'IssuesResolveTimeoutError';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Race a human-response promise (overlay click) against a bounded timeout. */
|
|
74
|
+
async function withVerificationTimeout(promise, timeoutMs, issueId) {
|
|
75
|
+
let timer;
|
|
76
|
+
const timeout = new Promise((_, reject) => {
|
|
77
|
+
timer = setTimeout(() => reject(new IssuesResolveTimeoutError(issueId, timeoutMs)), timeoutMs);
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
return await Promise.race([promise, timeout]);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// =============================================================================
|
|
87
|
+
// Helper Functions
|
|
88
|
+
// =============================================================================
|
|
89
|
+
function formatIssuesList(issues) {
|
|
90
|
+
if (issues.length === 0) {
|
|
91
|
+
return 'No issues found.';
|
|
92
|
+
}
|
|
93
|
+
const lines = [];
|
|
94
|
+
// Calculate column widths
|
|
95
|
+
const idWidth = Math.max(2, ...issues.map(i => String(i.id).length));
|
|
96
|
+
const statusWidth = Math.max(6, ...issues.map(i => i.status.length));
|
|
97
|
+
const typeWidth = 7; // "feature" is longest
|
|
98
|
+
// Header
|
|
99
|
+
lines.push(`${'ID'.padEnd(idWidth)} ${'STATUS'.padEnd(statusWidth)} ${'TYPE'.padEnd(typeWidth)} TITLE`);
|
|
100
|
+
for (const issue of issues) {
|
|
101
|
+
const id = String(issue.id).padEnd(idWidth);
|
|
102
|
+
const status = issue.status.toUpperCase().padEnd(statusWidth);
|
|
103
|
+
const type = issue.type.padEnd(typeWidth);
|
|
104
|
+
const labelSuffix = issue.labels.length > 0 ? ` [${issue.labels.join(', ')}]` : '';
|
|
105
|
+
// Truncate title to ~60 chars (minus label suffix) for readability
|
|
106
|
+
const maxTitleLen = Math.max(10, 60 - labelSuffix.length);
|
|
107
|
+
const title = issue.title.length > maxTitleLen
|
|
108
|
+
? issue.title.substring(0, maxTitleLen - 3) + '...'
|
|
109
|
+
: issue.title;
|
|
110
|
+
lines.push(`${id} ${status} ${type} ${title}${labelSuffix}`);
|
|
111
|
+
}
|
|
112
|
+
return lines.join('\n');
|
|
113
|
+
}
|
|
114
|
+
function getStatusIcon(status) {
|
|
115
|
+
switch (status) {
|
|
116
|
+
case 'pending': return '⏳';
|
|
117
|
+
case 'acknowledged': return '👀';
|
|
118
|
+
case 'in_progress': return '🔧';
|
|
119
|
+
case 'fixed': return '✅';
|
|
120
|
+
case 'implemented': return '✅';
|
|
121
|
+
default: return '•';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function formatIssueDetails(issue) {
|
|
125
|
+
const typeIcon = issue.type === 'bug' ? '🐛' : '✨';
|
|
126
|
+
const statusIcon = getStatusIcon(issue.status);
|
|
127
|
+
const lines = [
|
|
128
|
+
`${typeIcon} **Issue #${issue.id}** - ${issue.type.toUpperCase()}`,
|
|
129
|
+
`**Status:** ${statusIcon} ${issue.status}`,
|
|
130
|
+
`**Title:** ${issue.title}`,
|
|
131
|
+
];
|
|
132
|
+
if (issue.labels.length > 0) {
|
|
133
|
+
lines.push(`**Labels:** ${issue.labels.join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
if (issue.body) {
|
|
136
|
+
lines.push('', '**Body:**', '', issue.body);
|
|
137
|
+
}
|
|
138
|
+
lines.push('', `**Recording:** ${issue.recordingName || 'none'}`, `**Reported:** ${issue.reportedAt.toISOString()}`);
|
|
139
|
+
if (issue.sequenceFile) {
|
|
140
|
+
lines.push(`**Sequence:** \`${issue.sequenceFile}\``);
|
|
141
|
+
}
|
|
142
|
+
if (issue.acknowledgedAt) {
|
|
143
|
+
lines.push(`**Acknowledged:** ${issue.acknowledgedAt.toISOString()}`);
|
|
144
|
+
}
|
|
145
|
+
if (issue.startedAt) {
|
|
146
|
+
lines.push(`**Started:** ${issue.startedAt.toISOString()}`);
|
|
147
|
+
}
|
|
148
|
+
if (issue.resolvedAt) {
|
|
149
|
+
lines.push(`**Resolved:** ${issue.resolvedAt.toISOString()}`);
|
|
150
|
+
}
|
|
151
|
+
if (issue.comments.length > 0) {
|
|
152
|
+
lines.push('', '**Comments:**');
|
|
153
|
+
for (const c of issue.comments) {
|
|
154
|
+
lines.push(`- ${c.timestamp.toISOString()}: ${c.text}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return lines.join('\n');
|
|
158
|
+
}
|
|
159
|
+
function formatCommentTimeline(issue) {
|
|
160
|
+
if (issue.comments.length === 0)
|
|
161
|
+
return 'No comments yet.';
|
|
162
|
+
return issue.comments.map(c => `- ${c.timestamp.toISOString()}: ${c.text}`).join('\n');
|
|
163
|
+
}
|
|
164
|
+
// =============================================================================
|
|
165
|
+
// Tool Export
|
|
166
|
+
// =============================================================================
|
|
167
|
+
export function createIssuesTools(executeToolCall, getSequencePath, getPageForConnection) {
|
|
168
|
+
return {
|
|
169
|
+
issues: createTool('Track and manage bugs and features as Markdown issues (title, Markdown body, labels, comments). Actions: list (show all issues with optional filters), create (create new issue with title/body/labels, optionally linking a sequence), workOn (start working on issue with auto-replay), resolve (HUMAN-ONLY interactive verification: opens a browser overlay and waits for a person to confirm the fix before marking fixed/implemented - agents are refused immediately, use `comment` instead), acknowledge (acknowledge pending bugs to unblock tools), comment (append a Markdown comment to an issue)', issuesSchema, async (args, abortSignal) => {
|
|
170
|
+
// Initialize tracker on first use
|
|
171
|
+
await initializeTracker();
|
|
172
|
+
switch (args.action) {
|
|
173
|
+
case 'list': {
|
|
174
|
+
const filter = {};
|
|
175
|
+
if (args.type)
|
|
176
|
+
filter.type = args.type;
|
|
177
|
+
if (args.status)
|
|
178
|
+
filter.status = args.status;
|
|
179
|
+
if (args.includeCompleted)
|
|
180
|
+
filter.includeCompleted = true;
|
|
181
|
+
if (args.labels && args.labels.length > 0)
|
|
182
|
+
filter.labels = args.labels;
|
|
183
|
+
let issues = await getIssues(filter);
|
|
184
|
+
// Apply search filter
|
|
185
|
+
if (args.search) {
|
|
186
|
+
const searchLower = args.search.toLowerCase();
|
|
187
|
+
issues = issues.filter(i => i.id.toString() === args.search ||
|
|
188
|
+
i.title.toLowerCase().includes(searchLower) ||
|
|
189
|
+
i.body.toLowerCase().includes(searchLower) ||
|
|
190
|
+
i.recordingName.toLowerCase().includes(searchLower) ||
|
|
191
|
+
i.comments.some(c => c.text.toLowerCase().includes(searchLower)));
|
|
192
|
+
}
|
|
193
|
+
// Get counts from filtered list
|
|
194
|
+
const bugCount = issues.filter(i => i.type === 'bug').length;
|
|
195
|
+
const featureCount = issues.filter(i => i.type === 'feature').length;
|
|
196
|
+
const pendingCount = issues.filter(i => i.status === 'pending').length;
|
|
197
|
+
return createSuccessResponse('ISSUES_LIST', {
|
|
198
|
+
count: issues.length,
|
|
199
|
+
bugCount,
|
|
200
|
+
featureCount,
|
|
201
|
+
pendingCount,
|
|
202
|
+
issuesList: formatIssuesList(issues),
|
|
203
|
+
search: args.search || null,
|
|
204
|
+
includeCompleted: args.includeCompleted || false,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
case 'create': {
|
|
208
|
+
if (!args.type) {
|
|
209
|
+
return createErrorResponse('ISSUES_MISSING_TYPE', {
|
|
210
|
+
message: 'Issue type is required (bug or feature)',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (!args.title) {
|
|
214
|
+
return createErrorResponse('ISSUES_MISSING_TITLE', {
|
|
215
|
+
message: 'Issue title is required',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// Check if sequence should be included (default: true)
|
|
219
|
+
const includeSequence = args.includeSequence !== false;
|
|
220
|
+
// Reject about:blank as user-provided startUrl (reserved as sentinel for includeSequence: false)
|
|
221
|
+
if (args.startUrl === 'about:blank') {
|
|
222
|
+
return createErrorResponse('ISSUES_INVALID_START_URL', {
|
|
223
|
+
message: 'about:blank is not allowed as startUrl. Use includeSequence: false to create an issue without sequence recording.',
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// Check if localhost URL has an active server
|
|
227
|
+
if (args.startUrl) {
|
|
228
|
+
const portCheck = await checkUrlPort(args.startUrl);
|
|
229
|
+
if (portCheck && !portCheck.open) {
|
|
230
|
+
return createErrorResponse('ISSUES_LOCALHOST_NOT_ACTIVE', {
|
|
231
|
+
startUrl: args.startUrl,
|
|
232
|
+
port: portCheck.port,
|
|
233
|
+
message: `No server running on localhost:${portCheck.port}. Start the server before creating the issue.`,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Require startUrl when no sequence is provided and includeSequence is true
|
|
238
|
+
if (includeSequence && !args.sequenceName && !args.startUrl) {
|
|
239
|
+
return createErrorResponse('ISSUES_MISSING_START_URL', {
|
|
240
|
+
message: 'startUrl is required when no sequenceName is provided (needed for verification). Use includeSequence: false to skip sequence creation.',
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
let sequenceFile = '';
|
|
244
|
+
// If sequenceName provided, move/copy sequence to issues folder
|
|
245
|
+
if (args.sequenceName && getSequencePath) {
|
|
246
|
+
const sourcePath = await getSequencePath(args.sequenceName);
|
|
247
|
+
if (sourcePath) {
|
|
248
|
+
// Generate new filename for issues folder
|
|
249
|
+
// We'll use a temporary ID, then update after creating the issue
|
|
250
|
+
const tempId = Date.now();
|
|
251
|
+
const newFilename = generateSequenceFilename(args.type, tempId, args.title);
|
|
252
|
+
const destPath = join(getIssueSequencesDir(), newFilename);
|
|
253
|
+
try {
|
|
254
|
+
// Copy the sequence file
|
|
255
|
+
await fs.copyFile(sourcePath, destPath);
|
|
256
|
+
sequenceFile = newFilename;
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
return createErrorResponse('ISSUES_SEQUENCE_COPY_FAILED', {
|
|
260
|
+
sequenceName: args.sequenceName,
|
|
261
|
+
error: error.message,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
return createErrorResponse('ISSUES_SEQUENCE_NOT_FOUND', {
|
|
267
|
+
sequenceName: args.sequenceName,
|
|
268
|
+
message: `Sequence "${args.sequenceName}" not found`,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Manually created issues start as acknowledged (no blocking)
|
|
273
|
+
// When includeSequence is false, use about:blank as sentinel to skip recording on resolve
|
|
274
|
+
const effectiveStartUrl = !includeSequence ? 'about:blank' : (args.startUrl || '');
|
|
275
|
+
const issue = await addIssue({
|
|
276
|
+
type: args.type,
|
|
277
|
+
title: args.title,
|
|
278
|
+
sequenceFile,
|
|
279
|
+
recordingName: args.sequenceName || 'manual',
|
|
280
|
+
initialStatus: 'acknowledged',
|
|
281
|
+
startUrl: effectiveStartUrl,
|
|
282
|
+
body: args.body,
|
|
283
|
+
labels: args.labels,
|
|
284
|
+
});
|
|
285
|
+
// If we created a sequence file with temp ID, rename it with real ID
|
|
286
|
+
if (sequenceFile && args.sequenceName) {
|
|
287
|
+
const correctFilename = generateSequenceFilename(args.type, issue.id, args.title);
|
|
288
|
+
if (correctFilename !== sequenceFile) {
|
|
289
|
+
const oldPath = join(getIssueSequencesDir(), sequenceFile);
|
|
290
|
+
const newPath = join(getIssueSequencesDir(), correctFilename);
|
|
291
|
+
try {
|
|
292
|
+
await fs.rename(oldPath, newPath);
|
|
293
|
+
await updateIssueSequenceFile(issue.id, correctFilename);
|
|
294
|
+
issue.sequenceFile = correctFilename;
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
// Keep old filename if rename fails
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return createSuccessResponse('ISSUES_CREATED', {
|
|
302
|
+
id: issue.id,
|
|
303
|
+
type: issue.type,
|
|
304
|
+
title: issue.title,
|
|
305
|
+
sequenceFile: issue.sequenceFile || null,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
case 'workOn': {
|
|
309
|
+
if (!args.id) {
|
|
310
|
+
return createErrorResponse('ISSUES_MISSING_ID', {
|
|
311
|
+
message: 'Issue ID is required',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
const issue = await getIssue(args.id);
|
|
315
|
+
if (!issue) {
|
|
316
|
+
return createErrorResponse('ISSUES_NOT_FOUND', {
|
|
317
|
+
id: args.id,
|
|
318
|
+
message: `Issue #${args.id} not found`,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
// Update status to in_progress
|
|
322
|
+
await updateIssueStatus(args.id, 'in_progress');
|
|
323
|
+
// Use provided connectionReason or generate one
|
|
324
|
+
let connectionRef = null;
|
|
325
|
+
if (args.connectionReason) {
|
|
326
|
+
connectionRef = requireValidReference(args.connectionReason);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
connectionRef = `${issue.type} ${issue.id} workOn`;
|
|
330
|
+
}
|
|
331
|
+
const hasSequence = !!issue.sequenceFile;
|
|
332
|
+
// Register cleanup on abort signal to close browser tab if the
|
|
333
|
+
// call is cancelled mid-work. The listener MUST be detached once
|
|
334
|
+
// this handler settles: as a sequence step the signal is the
|
|
335
|
+
// RUN's long-lived signal, so a permanent listener would (a)
|
|
336
|
+
// accumulate one per issues step and (b) close this step's tab
|
|
337
|
+
// when the run is cancelled minutes after the step finished.
|
|
338
|
+
let detachAbortListener;
|
|
339
|
+
if (abortSignal && connectionRef) {
|
|
340
|
+
const onAbort = () => {
|
|
341
|
+
executeToolCall('tab', { action: 'close', reference: connectionRef }).catch(() => { });
|
|
342
|
+
};
|
|
343
|
+
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
344
|
+
detachAbortListener = () => abortSignal.removeEventListener('abort', onAbort);
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
if (hasSequence) {
|
|
348
|
+
// Auto-replay sequence if available
|
|
349
|
+
const sequencePath = join(getIssueSequencesDir(), issue.sequenceFile);
|
|
350
|
+
const sequenceName = issue.sequenceFile.replace(/\.json$/, '');
|
|
351
|
+
// Load and run sequence (errors propagate via ToolError)
|
|
352
|
+
await executeToolCall('replay', {
|
|
353
|
+
action: 'load',
|
|
354
|
+
filename: sequencePath,
|
|
355
|
+
});
|
|
356
|
+
await executeToolCall('replay', {
|
|
357
|
+
action: 'run',
|
|
358
|
+
// Blocking: workOn's response reports the replay's outcome, so
|
|
359
|
+
// the run must complete (or fail) before we return.
|
|
360
|
+
wait: true,
|
|
361
|
+
name: sequenceName,
|
|
362
|
+
connectionReason: connectionRef,
|
|
363
|
+
// A repro that spans two browsers has per-step references from
|
|
364
|
+
// the session that recorded it; without this there is no way to
|
|
365
|
+
// rebind them and the repro cannot run here at all.
|
|
366
|
+
...(args.connections && { connections: args.connections }),
|
|
367
|
+
showReplayOverlay: true,
|
|
368
|
+
issueId: issue.id,
|
|
369
|
+
issueType: issue.type,
|
|
370
|
+
issueTitle: issue.title,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
else if (issue.startUrl && getPageForConnection) {
|
|
374
|
+
// No sequence but has startUrl - launch browser, navigate, and show options overlay
|
|
375
|
+
let page = await getPageForConnection(connectionRef);
|
|
376
|
+
if (!page) {
|
|
377
|
+
// Auto-launch Chrome
|
|
378
|
+
try {
|
|
379
|
+
await executeToolCall('launchChrome', {
|
|
380
|
+
reference: connectionRef,
|
|
381
|
+
});
|
|
382
|
+
page = await getPageForConnection(connectionRef);
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
return createErrorResponse('ISSUES_CHROME_LAUNCH_FAILED', {
|
|
386
|
+
message: `Failed to launch Chrome: ${error.message}`,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (!page) {
|
|
391
|
+
return createErrorResponse('ISSUES_PAGE_ERROR', {
|
|
392
|
+
message: 'Failed to get browser page after launch',
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
// Navigate to startUrl
|
|
396
|
+
await executeToolCall('navigate', {
|
|
397
|
+
action: 'goto',
|
|
398
|
+
connectionReason: connectionRef,
|
|
399
|
+
url: issue.startUrl,
|
|
400
|
+
waitUntil: 'load',
|
|
401
|
+
});
|
|
402
|
+
// Refresh page reference after navigation
|
|
403
|
+
page = await getPageForConnection(connectionRef);
|
|
404
|
+
if (!page) {
|
|
405
|
+
return createErrorResponse('ISSUES_PAGE_ERROR', {
|
|
406
|
+
message: 'Lost browser page after navigation',
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
// Show overlay with options: Cancel, Explore, or Record
|
|
410
|
+
const overlayConfig = getWorkOnNoSequenceConfig(issue.type, issue.id, issue.title);
|
|
411
|
+
const result = await showOverlay(page, overlayConfig);
|
|
412
|
+
if (result.action === 'cancel') {
|
|
413
|
+
return createSuccessResponse('ISSUES_WORK_CANCELLED', {
|
|
414
|
+
id: issue.id,
|
|
415
|
+
type: issue.type,
|
|
416
|
+
title: issue.title,
|
|
417
|
+
message: 'Work session cancelled by user',
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
if (result.action === 'record') {
|
|
421
|
+
// Start recording user's actions - this blocks until recording completes
|
|
422
|
+
const recordingResult = await executeToolCall('replay', {
|
|
423
|
+
action: 'recordInteraction',
|
|
424
|
+
connectionReason: connectionRef,
|
|
425
|
+
name: `${issue.type}-${issue.id}-repro`,
|
|
426
|
+
startUrl: issue.startUrl,
|
|
427
|
+
issueId: issue.id,
|
|
428
|
+
issueType: issue.type,
|
|
429
|
+
issueTitle: issue.title,
|
|
430
|
+
});
|
|
431
|
+
// Return the recording result directly so user sees what was recorded
|
|
432
|
+
return recordingResult;
|
|
433
|
+
}
|
|
434
|
+
// action === 'explore' - just leave browser open for manual exploration
|
|
435
|
+
}
|
|
436
|
+
return createSuccessResponse('ISSUES_WORK_STARTED', {
|
|
437
|
+
id: issue.id,
|
|
438
|
+
type: issue.type,
|
|
439
|
+
title: issue.title,
|
|
440
|
+
sequenceFile: issue.sequenceFile || null,
|
|
441
|
+
details: formatIssueDetails(issue),
|
|
442
|
+
replayStarted: hasSequence,
|
|
443
|
+
browserLaunched: !hasSequence && !!issue.startUrl,
|
|
444
|
+
connectionReason: connectionRef,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
finally {
|
|
448
|
+
detachAbortListener?.();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
case 'resolve': {
|
|
452
|
+
if (!args.id) {
|
|
453
|
+
return createErrorResponse('ISSUES_MISSING_ID', {
|
|
454
|
+
message: 'Issue ID is required',
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
// resolve is human-gated by construction: the only thing that can
|
|
458
|
+
// settle showTestReadyOverlay/showVerificationOverlay is a real
|
|
459
|
+
// click in the browser, so an agent calling this cannot close an
|
|
460
|
+
// issue - it can only wait. That wait is what the bounded timeout
|
|
461
|
+
// below exists to cap. We deliberately do NOT try to detect an
|
|
462
|
+
// agent caller up front: the overlay already enforces the policy,
|
|
463
|
+
// and every available signal for "who is calling" is a heuristic
|
|
464
|
+
// that risks refusing a genuine human.
|
|
465
|
+
if (!getPageForConnection) {
|
|
466
|
+
return createErrorResponse('ISSUES_NO_PAGE_ACCESS', {
|
|
467
|
+
message: 'Cannot access browser page for verification',
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
const issue = await getIssue(args.id);
|
|
471
|
+
if (!issue) {
|
|
472
|
+
return createErrorResponse('ISSUES_NOT_FOUND', {
|
|
473
|
+
id: args.id,
|
|
474
|
+
message: `Issue #${args.id} not found`,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
// Generate a unique reference for this resolve session
|
|
478
|
+
const connectionRef = `${issue.type} ${issue.id} resolve`;
|
|
479
|
+
// Always create a fresh tab for resolve verification
|
|
480
|
+
let page = await getPageForConnection(connectionRef);
|
|
481
|
+
if (!page) {
|
|
482
|
+
// Try to create a new tab first (if Chrome is already running)
|
|
483
|
+
try {
|
|
484
|
+
await executeToolCall('tab', {
|
|
485
|
+
action: 'create',
|
|
486
|
+
reference: connectionRef,
|
|
487
|
+
});
|
|
488
|
+
page = await getPageForConnection(connectionRef);
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
// Chrome not running - launch it
|
|
492
|
+
try {
|
|
493
|
+
await executeToolCall('launchChrome', {
|
|
494
|
+
reference: connectionRef,
|
|
495
|
+
});
|
|
496
|
+
page = await getPageForConnection(connectionRef);
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
return createErrorResponse('ISSUES_CHROME_LAUNCH_FAILED', {
|
|
500
|
+
message: `Failed to launch Chrome: ${error.message}`,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (!page) {
|
|
506
|
+
return createErrorResponse('ISSUES_PAGE_ERROR', {
|
|
507
|
+
message: 'Failed to get browser page after launch',
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
const hasSequence = !!issue.sequenceFile;
|
|
511
|
+
// about:blank with no sequence means skip straight to completion overlay
|
|
512
|
+
const skipToCompletion = !hasSequence && issue.startUrl === 'about:blank';
|
|
513
|
+
// Navigate to startUrl first (before showing overlay) if no sequence
|
|
514
|
+
if (!hasSequence) {
|
|
515
|
+
// Use startUrl if available, otherwise about:blank
|
|
516
|
+
const targetUrl = issue.startUrl || 'about:blank';
|
|
517
|
+
// executeToolCall throws on error
|
|
518
|
+
await executeToolCall('navigate', {
|
|
519
|
+
action: 'goto',
|
|
520
|
+
connectionReason: connectionRef,
|
|
521
|
+
url: targetUrl,
|
|
522
|
+
waitUntil: 'load',
|
|
523
|
+
});
|
|
524
|
+
// Refresh page reference after navigation
|
|
525
|
+
page = await getPageForConnection(connectionRef);
|
|
526
|
+
if (!page) {
|
|
527
|
+
return createErrorResponse('ISSUES_PAGE_ERROR', {
|
|
528
|
+
message: 'Lost browser page after navigation',
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
// Skip "Ready to begin?" overlay if this is a no-sequence issue
|
|
533
|
+
let readyAction;
|
|
534
|
+
if (skipToCompletion) {
|
|
535
|
+
readyAction = 'begin'; // Simulate clicking begin
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
// Bound the wait for a human response: if nobody ever clicks the
|
|
539
|
+
// overlay (e.g. the person walked away), fail with a typed error
|
|
540
|
+
// instead of letting Puppeteer's own ~180s protocolTimeout leak
|
|
541
|
+
// through as a raw "Runtime.callFunctionOn timed out".
|
|
542
|
+
try {
|
|
543
|
+
readyAction = await withVerificationTimeout(showTestReadyOverlay(page, issue.type, issue.title, issue.id, hasSequence), DEFAULT_RESOLVE_VERIFICATION_TIMEOUT_MS, issue.id);
|
|
544
|
+
}
|
|
545
|
+
catch (error) {
|
|
546
|
+
if (error instanceof IssuesResolveTimeoutError) {
|
|
547
|
+
if (!args.keepBrowserOpen) {
|
|
548
|
+
try {
|
|
549
|
+
await executeToolCall('tab', { action: 'close', reference: connectionRef });
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
// Non-fatal
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return createErrorResponse('ISSUES_RESOLVE_TIMEOUT', {
|
|
556
|
+
id: issue.id,
|
|
557
|
+
timeoutSeconds: Math.round(DEFAULT_RESOLVE_VERIFICATION_TIMEOUT_MS / 1000),
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (readyAction === 'cancel') {
|
|
564
|
+
return createSuccessResponse('ISSUES_VERIFICATION_CANCELLED', {
|
|
565
|
+
id: issue.id,
|
|
566
|
+
type: issue.type,
|
|
567
|
+
title: issue.title,
|
|
568
|
+
message: 'Verification cancelled by user',
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
// Handle re-record: start recording instead of replaying
|
|
572
|
+
if (readyAction === 'rerecord') {
|
|
573
|
+
// Start recording user's actions to replace existing sequence
|
|
574
|
+
// recordInteraction will navigate to startUrl if provided
|
|
575
|
+
const recordingResult = await executeToolCall('replay', {
|
|
576
|
+
action: 'recordInteraction',
|
|
577
|
+
connectionReason: connectionRef,
|
|
578
|
+
name: `${issue.type}-${issue.id}-repro`,
|
|
579
|
+
startUrl: issue.startUrl || 'about:blank',
|
|
580
|
+
issueId: issue.id,
|
|
581
|
+
issueType: issue.type,
|
|
582
|
+
issueTitle: issue.title,
|
|
583
|
+
overwrite: true,
|
|
584
|
+
});
|
|
585
|
+
// Cancellation from `_meta`: searching the sentence for
|
|
586
|
+
// "cancelled" also matched a recorded page whose own text
|
|
587
|
+
// happened to contain the word.
|
|
588
|
+
const resultText = recordingResult?.content?.[0]?.text || '';
|
|
589
|
+
if (recordingResult?._meta?.replay?.cancelled) {
|
|
590
|
+
return recordingResult;
|
|
591
|
+
}
|
|
592
|
+
// Return with clear instruction to run resolve again
|
|
593
|
+
return createSuccessResponse('ISSUES_SEQUENCE_RERECORDED', {
|
|
594
|
+
type: issue.type,
|
|
595
|
+
id: issue.id,
|
|
596
|
+
recordingDetails: resultText,
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
// Play the sequence if available, otherwise record user actions
|
|
600
|
+
// Track replay execution results to surface to user
|
|
601
|
+
let replayResult = null;
|
|
602
|
+
if (hasSequence) {
|
|
603
|
+
const sequencePath = join(getIssueSequencesDir(), issue.sequenceFile);
|
|
604
|
+
const sequenceName = issue.sequenceFile.replace(/\.json$/, '');
|
|
605
|
+
const closeVerificationTab = async () => {
|
|
606
|
+
if (args.keepBrowserOpen)
|
|
607
|
+
return;
|
|
608
|
+
try {
|
|
609
|
+
await executeToolCall('tab', { action: 'close', reference: connectionRef });
|
|
610
|
+
}
|
|
611
|
+
catch {
|
|
612
|
+
// Non-fatal
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
// A run that FAILS returns normally and is read below; a run that
|
|
616
|
+
// cannot start at all (missing sequence file, Chrome refused to
|
|
617
|
+
// launch) THROWS. That throw used to escape this handler, so the
|
|
618
|
+
// verification browser was left open on the one path where the
|
|
619
|
+
// user is least likely to be watching it.
|
|
620
|
+
let replayText;
|
|
621
|
+
try {
|
|
622
|
+
await executeToolCall('replay', {
|
|
623
|
+
action: 'load',
|
|
624
|
+
filename: sequencePath,
|
|
625
|
+
});
|
|
626
|
+
replayResult = await executeToolCall('replay', {
|
|
627
|
+
action: 'run',
|
|
628
|
+
// Blocking: the failure check below parses the run's result.
|
|
629
|
+
wait: true,
|
|
630
|
+
name: sequenceName,
|
|
631
|
+
connectionReason: connectionRef,
|
|
632
|
+
...(args.connections && { connections: args.connections }),
|
|
633
|
+
showReplayOverlay: true,
|
|
634
|
+
issueId: issue.id,
|
|
635
|
+
issueType: issue.type,
|
|
636
|
+
issueTitle: issue.title,
|
|
637
|
+
});
|
|
638
|
+
replayText = replayResult?.content?.[0]?.text || '';
|
|
639
|
+
}
|
|
640
|
+
catch (replayError) {
|
|
641
|
+
await closeVerificationTab();
|
|
642
|
+
return createErrorResponse('ISSUES_REPLAY_FAILED', {
|
|
643
|
+
id: issue.id,
|
|
644
|
+
type: issue.type,
|
|
645
|
+
title: issue.title,
|
|
646
|
+
replayDetails: replayError?.response?.content?.[0]?.text
|
|
647
|
+
|| replayError?.message
|
|
648
|
+
|| 'The replay tool failed to run the sequence.',
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
// From the run's own `_meta`, not its rendered summary: the text
|
|
652
|
+
// form coupled this to the exact "**Failed:** 0" wording in
|
|
653
|
+
// replay-formatters, so a reformat there would have silently
|
|
654
|
+
// turned every failed verification into a pass.
|
|
655
|
+
const runMeta = replayResult?._meta?.replay;
|
|
656
|
+
const replayFailed = runMeta?.success === false || (runMeta?.failedSteps ?? 0) > 0;
|
|
657
|
+
if (replayFailed) {
|
|
658
|
+
await closeVerificationTab();
|
|
659
|
+
// Return error immediately with replay details
|
|
660
|
+
return createErrorResponse('ISSUES_REPLAY_FAILED', {
|
|
661
|
+
id: issue.id,
|
|
662
|
+
type: issue.type,
|
|
663
|
+
title: issue.title,
|
|
664
|
+
replayDetails: replayText,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
else if (!skipToCompletion) {
|
|
669
|
+
// No sequence and not skipping - start recording user's actions (executeToolCall throws on error)
|
|
670
|
+
const recordingResult = await executeToolCall('replay', {
|
|
671
|
+
action: 'recordInteraction',
|
|
672
|
+
connectionReason: connectionRef,
|
|
673
|
+
name: `verify-${issue.type}-${issue.id}`,
|
|
674
|
+
startUrl: issue.startUrl || 'about:blank',
|
|
675
|
+
// Pass issue info so recording saves to issues folder
|
|
676
|
+
issueId: issue.id,
|
|
677
|
+
issueType: issue.type,
|
|
678
|
+
issueTitle: issue.title,
|
|
679
|
+
});
|
|
680
|
+
// Cancellation from `_meta`: searching the sentence for
|
|
681
|
+
// "cancelled" also matched a recorded page whose own text
|
|
682
|
+
// happened to contain the word.
|
|
683
|
+
const resultText = recordingResult?.content?.[0]?.text || '';
|
|
684
|
+
if (recordingResult?._meta?.replay?.cancelled) {
|
|
685
|
+
return recordingResult;
|
|
686
|
+
}
|
|
687
|
+
// Return with instruction to run resolve again to verify with new sequence
|
|
688
|
+
return createSuccessResponse('ISSUES_SEQUENCE_RERECORDED', {
|
|
689
|
+
type: issue.type,
|
|
690
|
+
id: issue.id,
|
|
691
|
+
recordingDetails: resultText,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
// If skipToCompletion, we fall through directly to verification overlay
|
|
695
|
+
// Refresh page reference after replay (in case it changed)
|
|
696
|
+
page = await getPageForConnection(connectionRef);
|
|
697
|
+
if (!page) {
|
|
698
|
+
return createErrorResponse('ISSUES_PAGE_ERROR', {
|
|
699
|
+
message: 'Lost browser page during replay',
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
// Show verification overlay and wait for user response - bounded
|
|
703
|
+
// the same way as the "ready to begin?" overlay above.
|
|
704
|
+
let verification;
|
|
705
|
+
try {
|
|
706
|
+
verification = await withVerificationTimeout(showVerificationOverlay(page, issue.type, issue.title, issue.id), DEFAULT_RESOLVE_VERIFICATION_TIMEOUT_MS, issue.id);
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
if (error instanceof IssuesResolveTimeoutError) {
|
|
710
|
+
if (!args.keepBrowserOpen) {
|
|
711
|
+
try {
|
|
712
|
+
await executeToolCall('tab', { action: 'close', reference: connectionRef });
|
|
713
|
+
}
|
|
714
|
+
catch {
|
|
715
|
+
// Non-fatal
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return createErrorResponse('ISSUES_RESOLVE_TIMEOUT', {
|
|
719
|
+
id: issue.id,
|
|
720
|
+
timeoutSeconds: Math.round(DEFAULT_RESOLVE_VERIFICATION_TIMEOUT_MS / 1000),
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
// Close the tab after verification (unless keepBrowserOpen is true)
|
|
726
|
+
if (!args.keepBrowserOpen) {
|
|
727
|
+
try {
|
|
728
|
+
await executeToolCall('tab', {
|
|
729
|
+
action: 'close',
|
|
730
|
+
reference: connectionRef,
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
catch (error) {
|
|
734
|
+
// Non-fatal - tab close failed but verification completed
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
// Extract replay execution details if available
|
|
738
|
+
let replayDetails = null;
|
|
739
|
+
if (replayResult?.content?.[0]?.text) {
|
|
740
|
+
replayDetails = replayResult.content[0].text;
|
|
741
|
+
}
|
|
742
|
+
// Fold the verification comment into the issue's permanent Markdown timeline
|
|
743
|
+
if (verification.comment) {
|
|
744
|
+
await addIssueComment(args.id, verification.comment);
|
|
745
|
+
}
|
|
746
|
+
if (verification.resolved) {
|
|
747
|
+
// User confirmed resolution
|
|
748
|
+
const newStatus = issue.type === 'bug' ? 'fixed' : 'implemented';
|
|
749
|
+
await updateIssueStatus(args.id, newStatus);
|
|
750
|
+
return createSuccessResponse('ISSUES_RESOLVED', {
|
|
751
|
+
id: issue.id,
|
|
752
|
+
type: issue.type,
|
|
753
|
+
status: newStatus,
|
|
754
|
+
title: issue.title,
|
|
755
|
+
userComment: verification.comment || null,
|
|
756
|
+
replayDetails,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
// User said not resolved - include replay details so user can see what happened
|
|
761
|
+
return createSuccessResponse('ISSUES_NOT_RESOLVED', {
|
|
762
|
+
id: issue.id,
|
|
763
|
+
type: issue.type,
|
|
764
|
+
status: issue.status,
|
|
765
|
+
title: issue.title,
|
|
766
|
+
userComment: verification.comment || null,
|
|
767
|
+
replayDetails,
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
case 'acknowledge': {
|
|
772
|
+
const acknowledged = await acknowledgeAllBugs();
|
|
773
|
+
if (acknowledged.length === 0) {
|
|
774
|
+
return createSuccessResponse('ISSUES_NONE_PENDING', {
|
|
775
|
+
message: 'No pending bugs to acknowledge',
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
const bugList = acknowledged.map(b => `- #${b.id}: ${b.title}`).join('\n');
|
|
779
|
+
return createSuccessResponse('ISSUES_ACKNOWLEDGED', {
|
|
780
|
+
count: acknowledged.length,
|
|
781
|
+
bugList,
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
case 'comment': {
|
|
785
|
+
if (!args.id) {
|
|
786
|
+
return createErrorResponse('ISSUES_MISSING_ID', {
|
|
787
|
+
message: 'Issue ID is required',
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
if (!args.text) {
|
|
791
|
+
return createErrorResponse('ISSUES_MISSING_TEXT', {
|
|
792
|
+
message: 'Comment text is required',
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
const issue = await getIssue(args.id);
|
|
796
|
+
if (!issue) {
|
|
797
|
+
return createErrorResponse('ISSUES_NOT_FOUND', {
|
|
798
|
+
id: args.id,
|
|
799
|
+
message: `Issue #${args.id} not found`,
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
const updated = await addIssueComment(args.id, args.text);
|
|
803
|
+
return createSuccessResponse('ISSUES_COMMENT_ADDED', {
|
|
804
|
+
id: issue.id,
|
|
805
|
+
type: issue.type,
|
|
806
|
+
title: issue.title,
|
|
807
|
+
commentCount: updated?.comments.length ?? issue.comments.length,
|
|
808
|
+
timeline: formatCommentTimeline(updated ?? issue),
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
default: {
|
|
812
|
+
return createErrorResponse('ISSUES_INVALID_ACTION', {
|
|
813
|
+
action: args.action,
|
|
814
|
+
message: `Unknown action: ${args.action}`,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}),
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
//# sourceMappingURL=issues-tools.js.map
|