@reticlehq/server 3.8.2 → 3.10.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 +201 -104
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/SKILL.md +13 -12
- package/bin/launch.js +13 -0
- package/bin/package.json +3 -0
- package/bin/reticle.js +66 -8
- package/dist/chromium-launch-options.d.ts +25 -0
- package/dist/chromium-launch-options.js +21 -1
- package/dist/command/cli/adhoc-suite.js +2 -0
- package/dist/command/cli/adhoc-verdict.d.ts +2 -43
- package/dist/command/cli/adhoc-verdict.js +50 -160
- package/dist/command/cli/answers/gate-exit.d.ts +6 -2
- package/dist/command/cli/answers/gate-exit.js +12 -2
- package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
- package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
- package/dist/command/cli/ci-hint.d.ts +44 -0
- package/dist/command/cli/ci-hint.js +72 -0
- package/dist/command/cli/cli-doctor.d.ts +6 -1
- package/dist/command/cli/cli-doctor.js +46 -5
- package/dist/command/cli/cli-flow-commands.d.ts +7 -1
- package/dist/command/cli/cli-flow-commands.js +39 -24
- package/dist/command/cli/cli-kill.d.ts +21 -0
- package/dist/command/cli/cli-kill.js +47 -4
- package/dist/command/cli/cli-parse-compare.d.ts +22 -0
- package/dist/command/cli/cli-parse-compare.js +75 -0
- package/dist/command/cli/cli-parse-grade.d.ts +36 -0
- package/dist/command/cli/cli-parse-grade.js +75 -0
- package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
- package/dist/command/cli/cli-parse-grammar.js +18 -20
- package/dist/command/cli/cli-parse-verify.d.ts +10 -0
- package/dist/command/cli/cli-parse-verify.js +63 -14
- package/dist/command/cli/cli-parse.d.ts +22 -17
- package/dist/command/cli/cli-parse.js +54 -32
- package/dist/command/cli/cli-update-commands.d.ts +1 -1
- package/dist/command/cli/cli-update-commands.js +29 -20
- package/dist/command/cli/cli-usage.js +61 -31
- package/dist/command/cli/cli-verify.d.ts +17 -3
- package/dist/command/cli/cli-verify.js +59 -21
- package/dist/command/cli/cloud-cli.js +2 -119
- package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
- package/dist/command/cli/daemon-link/daemon-client.js +205 -0
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +6 -1
- package/dist/command/cli/doctor/browser/chromium-hint.js +11 -5
- package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
- package/dist/command/cli/doctor/doctor-fix.d.ts +44 -0
- package/dist/command/cli/doctor/doctor-fix.js +89 -0
- package/dist/command/cli/doctor/doctor-launcher-line.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-launcher-line.js +60 -0
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/doctor-repairs.d.ts +64 -0
- package/dist/command/cli/doctor-repairs.js +214 -0
- package/dist/command/cli/drive/drive-command.js +3 -7
- package/dist/command/cli/ensure-daemon.d.ts +26 -0
- package/dist/command/cli/ensure-daemon.js +57 -0
- package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
- package/dist/command/cli/finished-run/grade-command.js +180 -0
- package/dist/command/cli/first-use-checks.d.ts +41 -0
- package/dist/command/cli/first-use-checks.js +127 -0
- package/dist/command/cli/first-verdict-node.d.ts +3 -0
- package/dist/command/cli/first-verdict-node.js +189 -0
- package/dist/command/cli/first-verdict.d.ts +126 -0
- package/dist/command/cli/first-verdict.js +267 -0
- package/dist/command/cli/launch/sdk-version-match.d.ts +3 -2
- package/dist/command/cli/launch/sdk-version-match.js +3 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +7 -6
- package/dist/command/cli/mcp-command.js +13 -4
- package/dist/command/cli/ports/port-holder.d.ts +11 -0
- package/dist/command/cli/ports/port-holder.js +36 -4
- package/dist/command/cli/setup-mcp-cli.d.ts +27 -1
- package/dist/command/cli/setup-mcp-cli.js +98 -23
- package/dist/command/cli/status/status-lines.d.ts +2 -0
- package/dist/command/cli/status/status-lines.js +72 -17
- package/dist/command/cli/status-fields.d.ts +5 -1
- package/dist/command/cli/status-fields.js +24 -6
- package/dist/command/cli/try-command.d.ts +7 -0
- package/dist/command/cli/try-command.js +11 -3
- package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
- package/dist/command/cli/two-builds/compare-command.js +314 -0
- package/dist/command/cli/verify-against.d.ts +61 -0
- package/dist/command/cli/verify-against.js +187 -0
- package/dist/command/cli/verify-no-session.d.ts +2 -0
- package/dist/command/cli/verify-no-session.js +13 -0
- package/dist/command/cli/verify-results.d.ts +35 -2
- package/dist/command/cli/verify-results.js +76 -10
- package/dist/command/cli.js +112 -70
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +3 -1
- package/dist/command/daemon/binding/daemon-status-probe.js +4 -2
- package/dist/command/daemon/daemon-argv.d.ts +9 -0
- package/dist/command/daemon/daemon-argv.js +11 -0
- package/dist/command/daemon/daemon-resolve.d.ts +8 -1
- package/dist/command/daemon/daemon-resolve.js +27 -1
- package/dist/command/daemon/daemon.d.ts +22 -4
- package/dist/command/daemon/daemon.js +52 -10
- package/dist/command/daemon/lifetime/daemon-exit.d.ts +24 -0
- package/dist/command/daemon/lifetime/daemon-exit.js +39 -0
- package/dist/command/daemon/lifetime/death-cause.d.ts +15 -0
- package/dist/command/daemon/lifetime/death-cause.js +149 -0
- package/dist/command/daemon/lifetime/heartbeat.d.ts +7 -15
- package/dist/command/daemon/lifetime/heartbeat.js +11 -15
- package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
- package/dist/command/daemon/lifetime/startup-failure.js +62 -0
- package/dist/command/daemon/port-state.d.ts +14 -0
- package/dist/command/daemon/port-state.js +31 -0
- package/dist/command/setup/agent-configs.d.ts +2 -0
- package/dist/command/setup/agent-configs.js +42 -15
- package/dist/command/setup/agent-next-steps.d.ts +77 -0
- package/dist/command/setup/agent-next-steps.js +284 -0
- package/dist/command/setup/agent-writer.js +11 -6
- package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
- package/dist/command/setup/bringup/dev-server-files.js +17 -0
- package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
- package/dist/command/setup/bringup/static-page-server.js +43 -9
- package/dist/command/setup/init/init-host.js +2 -0
- package/dist/command/setup/init/init-runtime.d.ts +6 -1
- package/dist/command/setup/init/init-runtime.js +63 -21
- package/dist/command/setup/mcp-launcher.d.ts +3 -0
- package/dist/command/setup/mcp-launcher.js +11 -0
- package/dist/command/setup/node-effects.d.ts +3 -2
- package/dist/command/setup/node-effects.js +9 -3
- package/dist/command/setup/remaining-steps.d.ts +8 -0
- package/dist/command/setup/remaining-steps.js +10 -0
- package/dist/command/setup/run-setup.d.ts +2 -0
- package/dist/command/setup/run-setup.js +9 -1
- package/dist/command/setup/setup-command.js +36 -15
- package/dist/command/setup/setup-mcp.d.ts +8 -0
- package/dist/command/setup/setup-mcp.js +47 -20
- package/dist/command/setup/start-dev-server.d.ts +8 -0
- package/dist/command/setup/start-dev-server.js +43 -0
- package/dist/command/update/updater.js +6 -0
- package/dist/command/version/identity/server-version.d.ts +2 -0
- package/dist/command/version/identity/server-version.js +2 -0
- package/dist/command/version/version-skew.d.ts +7 -0
- package/dist/command/version/version-skew.js +14 -3
- package/dist/features/compare/action-plan.d.ts +260 -0
- package/dist/features/compare/action-plan.js +543 -0
- package/dist/features/compare/behaviour-compare.d.ts +24 -0
- package/dist/features/compare/behaviour-compare.js +85 -0
- package/dist/features/compare/behaviour-diff.d.ts +97 -0
- package/dist/features/compare/behaviour-diff.js +202 -0
- package/dist/features/compare/behaviour-report.d.ts +40 -0
- package/dist/features/compare/behaviour-report.js +128 -0
- package/dist/features/compare/build-readings.d.ts +106 -0
- package/dist/features/compare/build-readings.js +250 -0
- package/dist/features/compare/compare-diff.d.ts +62 -0
- package/dist/features/compare/compare-diff.js +177 -0
- package/dist/features/compare/compare-report-html.d.ts +6 -0
- package/dist/features/compare/compare-report-html.js +102 -0
- package/dist/features/compare/compare-report.d.ts +15 -0
- package/dist/features/compare/compare-report.js +73 -0
- package/dist/features/compare/compare-run.d.ts +60 -0
- package/dist/features/compare/compare-run.js +123 -0
- package/dist/features/compare/outside/effect-lines.d.ts +26 -0
- package/dist/features/compare/outside/effect-lines.js +103 -0
- package/dist/features/compare/outside/state-sources.d.ts +76 -0
- package/dist/features/compare/outside/state-sources.js +164 -0
- package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
- package/dist/features/compare/outside/value-dictionary.js +227 -0
- package/dist/features/compare/visible-content.d.ts +20 -0
- package/dist/features/compare/visible-content.js +125 -0
- package/dist/features/crawl/crawl-empty.js +2 -1
- package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
- package/dist/features/crawl/crawl-exhaustive.js +138 -0
- package/dist/features/crawl/crawl-tools.d.ts +9 -2
- package/dist/features/crawl/crawl-tools.js +28 -12
- package/dist/features/crawl/crawl.d.ts +8 -9
- package/dist/features/crawl/crawl.js +61 -65
- package/dist/features/exhaust/app-coverage.d.ts +5 -1
- package/dist/features/exhaust/app-coverage.js +3 -1
- package/dist/features/exhaust/code-coverage.js +8 -4
- package/dist/features/exhaust/edge-cases.d.ts +133 -0
- package/dist/features/exhaust/edge-cases.js +310 -0
- package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
- package/dist/features/exhaust/edges/edge-drive.js +364 -0
- package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
- package/dist/features/exhaust/edges/edge-run.js +169 -0
- package/dist/features/exhaust/explorer.d.ts +87 -14
- package/dist/features/exhaust/explorer.js +159 -25
- package/dist/features/exhaust/failure-branches.d.ts +5 -0
- package/dist/features/exhaust/failure-branches.js +29 -11
- package/dist/features/exhaust/invariants.d.ts +112 -0
- package/dist/features/exhaust/invariants.js +188 -0
- package/dist/features/exhaust/ledger.d.ts +68 -0
- package/dist/features/exhaust/ledger.js +121 -13
- package/dist/features/exhaust/session-fold.d.ts +10 -0
- package/dist/features/exhaust/session-fold.js +46 -3
- package/dist/features/exhaust/session-port.d.ts +3 -1
- package/dist/features/exhaust/session-port.js +97 -28
- package/dist/features/grade/grade-answer.d.ts +99 -0
- package/dist/features/grade/grade-answer.js +112 -0
- package/dist/features/grade/grade-noise.d.ts +27 -0
- package/dist/features/grade/grade-noise.js +55 -0
- package/dist/features/grade/grade-renumber.d.ts +12 -0
- package/dist/features/grade/grade-renumber.js +86 -0
- package/dist/features/grade/grade-state.d.ts +50 -0
- package/dist/features/grade/grade-state.js +93 -0
- package/dist/features/grade/grade-suggest.d.ts +33 -0
- package/dist/features/grade/grade-suggest.js +98 -0
- package/dist/features/license/license.d.ts +11 -71
- package/dist/features/license/license.js +43 -136
- package/dist/features/visual/desktop-capture.d.ts +33 -0
- package/dist/features/visual/desktop-capture.js +94 -0
- package/dist/features/visual/step-shots.js +139 -35
- package/dist/features/visual/visual-tools.d.ts +0 -18
- package/dist/features/visual/visual-tools.js +67 -99
- package/dist/import-graph.js +5 -6
- package/dist/index.d.ts +3 -1
- package/dist/index.js +19 -7
- package/dist/judgement/capsule/causal-summary.js +9 -2
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
- package/dist/judgement/runs/reticle-runner.js +16 -2
- package/dist/judgement/runs/run-ledger.d.ts +7 -5
- package/dist/judgement/runs/run-ledger.js +98 -69
- package/dist/judgement/runs/runner-port.d.ts +2 -0
- package/dist/judgement/runs/runner-port.js +23 -5
- package/dist/judgement/runs/verification-sync.d.ts +6 -3
- package/dist/judgement/runs/verification-sync.js +21 -10
- package/dist/language/flows/affected-tools.d.ts +1 -1
- package/dist/language/flows/affected-tools.js +49 -30
- package/dist/language/flows/change/git-changed.d.ts +2 -1
- package/dist/language/flows/change/git-changed.js +19 -2
- package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
- package/dist/language/flows/fields/flow-secret-field.js +77 -21
- package/dist/language/flows/flow-anchor.d.ts +60 -2
- package/dist/language/flows/flow-anchor.js +125 -20
- package/dist/language/flows/flow-classify.d.ts +20 -0
- package/dist/language/flows/flow-classify.js +90 -12
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +25 -1
- package/dist/language/flows/flow-journey.d.ts +38 -4
- package/dist/language/flows/flow-journey.js +138 -8
- package/dist/language/flows/flow-replay-run.d.ts +0 -16
- package/dist/language/flows/flow-replay-run.js +76 -14
- package/dist/language/flows/flow-replay-types.d.ts +4 -2
- package/dist/language/flows/flow-replay.d.ts +19 -1
- package/dist/language/flows/flow-replay.js +169 -52
- package/dist/language/flows/flow-replayable.js +7 -1
- package/dist/language/flows/flow-start-path.d.ts +34 -5
- package/dist/language/flows/flow-start-path.js +202 -15
- package/dist/language/flows/flow-step-runners.d.ts +14 -13
- package/dist/language/flows/flow-step-runners.js +184 -109
- package/dist/language/flows/flow-store-for-session.d.ts +26 -25
- package/dist/language/flows/flow-store-for-session.js +51 -5
- package/dist/language/flows/flow-tools.js +46 -33
- package/dist/language/flows/flows.js +176 -76
- package/dist/language/flows/recording/tape/recordings.d.ts +21 -16
- package/dist/language/flows/recording/tape/recordings.js +112 -49
- package/dist/language/flows/replay.d.ts +36 -12
- package/dist/language/flows/replay.js +193 -50
- package/dist/language/flows/steps/page-steps.d.ts +68 -0
- package/dist/language/flows/steps/page-steps.js +198 -0
- package/dist/language/flows/suite/sequential-run.d.ts +68 -0
- package/dist/language/flows/suite/sequential-run.js +96 -0
- package/dist/language/flows/verify-change-tools.js +26 -15
- package/dist/launch-chromium.d.ts +1 -0
- package/dist/launch-chromium.js +49 -12
- package/dist/memory/cloud/background-sync.d.ts +13 -0
- package/dist/memory/cloud/background-sync.js +24 -3
- package/dist/memory/cloud/sync-daemon.js +26 -27
- package/dist/memory/cloud/sync-screenshots.js +6 -1
- package/dist/memory/journal/attach-journal.js +2 -1
- package/dist/memory/journal/drive-flow.d.ts +7 -0
- package/dist/memory/journal/drive-flow.js +44 -13
- package/dist/memory/journal/journal-recorder.d.ts +4 -2
- package/dist/memory/journal/session-journal.d.ts +7 -0
- package/dist/memory/journal/session-journal.js +58 -10
- package/dist/memory/project/root-claims.d.ts +9 -0
- package/dist/memory/project/root-claims.js +9 -4
- package/dist/portal/bridge/bridge.d.ts +13 -4
- package/dist/portal/bridge/bridge.js +10 -9
- package/dist/portal/input/download-capture.d.ts +7 -0
- package/dist/portal/input/download-capture.js +92 -0
- package/dist/portal/input/network-detail.d.ts +6 -0
- package/dist/portal/input/network-detail.js +12 -1
- package/dist/portal/input/network-mock-tools.js +49 -13
- package/dist/portal/input/network-mock.d.ts +8 -0
- package/dist/portal/input/network-mock.js +30 -10
- package/dist/portal/input/page-capture.d.ts +24 -0
- package/dist/portal/input/page-capture.js +33 -0
- package/dist/portal/input/real-input.d.ts +35 -18
- package/dist/portal/input/real-input.js +99 -33
- package/dist/portal/input/viewport-tools.d.ts +8 -0
- package/dist/portal/input/viewport-tools.js +81 -21
- package/dist/portal/pool/browser-pool.d.ts +16 -30
- package/dist/portal/pool/browser-pool.js +64 -52
- package/dist/portal/pool/context-permissions.d.ts +34 -17
- package/dist/portal/pool/context-permissions.js +94 -1
- package/dist/portal/pool/csp-widen.d.ts +62 -0
- package/dist/portal/pool/csp-widen.js +132 -0
- package/dist/portal/pool/determinism.d.ts +52 -0
- package/dist/portal/pool/determinism.js +259 -0
- package/dist/portal/pool/lease-emulation.d.ts +34 -1
- package/dist/portal/pool/lease-emulation.js +18 -0
- package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
- package/dist/portal/pool/lease-marker-stamp.js +15 -1
- package/dist/portal/pool/lease-page-error.d.ts +56 -0
- package/dist/portal/pool/lease-page-error.js +160 -0
- package/dist/portal/pool/page-error-journal.d.ts +63 -0
- package/dist/portal/pool/page-error-journal.js +208 -0
- package/dist/portal/pool/playwright-launcher.js +71 -15
- package/dist/portal/pool/pool-contract.d.ts +49 -1
- package/dist/portal/pool/pool-lease.d.ts +70 -2
- package/dist/portal/pool/pool-lease.js +23 -0
- package/dist/portal/pool/zero-install.d.ts +8 -0
- package/dist/portal/pool/zero-install.js +91 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +11 -2
- package/dist/portal/session/dev-server/dev-command.js +110 -16
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
- package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
- package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
- package/dist/portal/session/dev-server/project-ports.js +62 -0
- package/dist/portal/session/facts/departed-network.d.ts +66 -0
- package/dist/portal/session/facts/departed-network.js +142 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
- package/dist/portal/session/no-session-diagnosis.js +76 -27
- package/dist/portal/session/no-session-next-action.d.ts +14 -0
- package/dist/portal/session/no-session-next-action.js +56 -7
- package/dist/portal/session/no-session-watch.d.ts +6 -0
- package/dist/portal/session/no-session-watch.js +41 -4
- package/dist/portal/session/page-commands/undeclared-command.d.ts +9 -3
- package/dist/portal/session/page-commands/undeclared-command.js +16 -7
- package/dist/portal/session/recorded-gaps.d.ts +1 -1
- package/dist/portal/session/recorded-gaps.js +5 -2
- package/dist/portal/session/serving-directory.d.ts +10 -0
- package/dist/portal/session/serving-directory.js +39 -8
- package/dist/portal/session/session-manager.d.ts +10 -0
- package/dist/portal/session/session-manager.js +211 -81
- package/dist/portal/session/session.js +4 -3
- package/dist/portal/session/zero-install-first-call.d.ts +20 -1
- package/dist/portal/session/zero-install-first-call.js +34 -2
- package/dist/start-wiring.d.ts +30 -2
- package/dist/start-wiring.js +74 -9
- package/dist/surface/mcp/mcp-proxy.js +10 -2
- package/dist/surface/mcp/mcp.js +8 -5
- package/dist/surface/mcp/peer/agents-connected.d.ts +6 -0
- package/dist/surface/mcp/peer/agents-connected.js +58 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +17 -5
- package/dist/surface/mcp/shared-params.d.ts +2 -0
- package/dist/surface/mcp/shared-params.js +31 -0
- package/dist/surface/tools/act/act-observation.d.ts +40 -2
- package/dist/surface/tools/act/act-observation.js +118 -12
- package/dist/surface/tools/act/act-preflight.js +38 -18
- package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
- package/dist/surface/tools/act/act-sequence-retry.js +61 -5
- package/dist/surface/tools/act/act-target.js +3 -1
- package/dist/surface/tools/act/after-match.d.ts +10 -0
- package/dist/surface/tools/act/after-match.js +40 -0
- package/dist/surface/tools/act/already-true.js +1 -1
- package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
- package/dist/surface/tools/act/navigation/reload-result.js +25 -2
- package/dist/surface/tools/act/resolve-target.d.ts +17 -1
- package/dist/surface/tools/act/resolve-target.js +59 -9
- package/dist/surface/tools/act/sequence-grade.d.ts +15 -1
- package/dist/surface/tools/act/sequence-grade.js +45 -27
- package/dist/surface/tools/act/settle-in-flight.js +27 -15
- package/dist/surface/tools/act-command.d.ts +23 -0
- package/dist/surface/tools/act-command.js +50 -0
- package/dist/surface/tools/act-sequence-tool.js +81 -50
- package/dist/surface/tools/act-tools.d.ts +1 -13
- package/dist/surface/tools/act-tools.js +83 -95
- package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
- package/dist/surface/tools/agent/build-staleness.js +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +18 -12
- package/dist/surface/tools/browser-tools.js +87 -13
- package/dist/surface/tools/compare-tools.d.ts +11 -0
- package/dist/surface/tools/compare-tools.js +94 -0
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.js +11 -3
- package/dist/surface/tools/empty-session-list.d.ts +2 -0
- package/dist/surface/tools/empty-session-list.js +4 -2
- package/dist/surface/tools/error-recovery.d.ts +12 -1
- package/dist/surface/tools/error-recovery.js +131 -40
- package/dist/surface/tools/explore-tools.js +1 -1
- package/dist/surface/tools/invoke-tool.js +29 -21
- package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
- package/dist/surface/tools/lease-coverage-tools.js +128 -0
- package/dist/surface/tools/lease-hint.d.ts +42 -1
- package/dist/surface/tools/lease-hint.js +52 -3
- package/dist/surface/tools/lease-readiness.d.ts +47 -0
- package/dist/surface/tools/lease-readiness.js +126 -0
- package/dist/surface/tools/lease-seed.d.ts +16 -1
- package/dist/surface/tools/lease-seed.js +37 -4
- package/dist/surface/tools/lease-tools.d.ts +3 -41
- package/dist/surface/tools/lease-tools.js +104 -124
- package/dist/surface/tools/lease-url-identity.d.ts +46 -0
- package/dist/surface/tools/lease-url-identity.js +97 -0
- package/dist/surface/tools/merge-tools.js +99 -2
- package/dist/surface/tools/merged-name-redirect.js +5 -1
- package/dist/surface/tools/observe-tools.js +15 -9
- package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
- package/dist/surface/tools/read/console-pre-attach.js +23 -0
- package/dist/surface/tools/read-tools.js +33 -30
- package/dist/surface/tools/real-input-attempt.d.ts +0 -8
- package/dist/surface/tools/real-input-attempt.js +143 -28
- package/dist/surface/tools/repeat-hint.d.ts +40 -0
- package/dist/surface/tools/repeat-hint.js +171 -0
- package/dist/surface/tools/run-report.d.ts +2 -2
- package/dist/surface/tools/run-report.js +27 -15
- package/dist/surface/tools/session-ports.d.ts +14 -0
- package/dist/surface/tools/session-ports.js +53 -0
- package/dist/surface/tools/tool-kit.d.ts +18 -0
- package/dist/surface/tools/tool-kit.js +9 -3
- package/dist/surface/tools/tool-surface.js +0 -1
- package/dist/surface/tools/tools.js +54 -18
- package/dist/surface/tools/type-spelling.d.ts +13 -0
- package/dist/surface/tools/type-spelling.js +36 -0
- package/dist/telemetry/agent-client.d.ts +6 -0
- package/dist/telemetry/agent-client.js +10 -0
- package/dist/telemetry/argument-shape.js +17 -7
- package/dist/telemetry/cli-telemetry.d.ts +10 -1
- package/dist/telemetry/cli-telemetry.js +15 -0
- package/dist/telemetry/init-telemetry.d.ts +2 -1
- package/dist/telemetry/init-telemetry.js +47 -2
- package/dist/telemetry/install-trace.js +23 -4
- package/dist/telemetry/onboarding-funnel.d.ts +19 -2
- package/dist/telemetry/onboarding-funnel.js +25 -3
- package/dist/telemetry/proxy-telemetry.d.ts +2 -1
- package/dist/telemetry/proxy-telemetry.js +2 -2
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +67 -27
- package/dist/telemetry/telemetry.d.ts +3 -1
- package/dist/telemetry/telemetry.js +60 -28
- package/dist/telemetry/tool-refused.d.ts +6 -3
- package/dist/telemetry/tool-refused.js +15 -3
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +1 -1
- package/docs/architecture.md +3 -6
- package/docs/capabilities.mdx +61 -1
- package/docs/cli/affected.mdx +9 -7
- package/docs/cli/cloud.mdx +3 -3
- package/docs/cli/compare.mdx +94 -0
- package/docs/cli/doctor.mdx +41 -2
- package/docs/cli/grade.mdx +97 -0
- package/docs/cli/help.mdx +4 -2
- package/docs/cli/license.mdx +13 -12
- package/docs/cli/restart.mdx +7 -5
- package/docs/cli/rollback.mdx +5 -5
- package/docs/cli/setup.mdx +13 -2
- package/docs/cli/status.mdx +4 -0
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/update.mdx +2 -2
- package/docs/cli/verify.mdx +23 -1
- package/docs/cli.mdx +34 -24
- package/docs/deploy-checks.md +25 -0
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +17 -22
- package/docs/environment.mdx +1 -0
- package/docs/first-drive.md +1 -1
- package/docs/flows.md +11 -1
- package/docs/frameworks.mdx +9 -9
- package/docs/getting-started.md +1 -1
- package/docs/install-manual.mdx +16 -1
- package/docs/license-activation.mdx +3 -3
- package/docs/packages/engine.mdx +1 -1
- package/docs/packages/server.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/test.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/platform-integration.md +1 -1
- package/docs/predicates.mdx +1 -1
- package/docs/quickstart.mdx +68 -49
- package/docs/skill-setup.md +1 -1
- package/docs/skill.md +14 -13
- package/docs/telemetry.md +6 -4
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/verify.mdx +4 -1
- package/docs/troubleshooting.mdx +1 -1
- package/docs/usage.md +17 -14
- package/package.json +18 -14
- package/LICENSE-ENTERPRISE +0 -37
- package/dist/features/ee/audit-log.d.ts +0 -14
- package/dist/features/ee/audit-log.js +0 -11
- package/dist/features/exhaust/exhaust-run.d.ts +0 -33
- package/dist/features/exhaust/exhaust-run.js +0 -82
|
@@ -11,6 +11,24 @@ export const ColorScheme = {
|
|
|
11
11
|
NO_PREFERENCE: 'no-preference',
|
|
12
12
|
};
|
|
13
13
|
export const ReducedMotion = { REDUCE: 'reduce', NO_PREFERENCE: 'no-preference' };
|
|
14
|
+
export const Pointer = { FINE: 'fine', COARSE: 'coarse' };
|
|
15
|
+
/**
|
|
16
|
+
* Translate a lease's emulation into context options. `pointer` is not one of Playwright's: a
|
|
17
|
+
* coarse primary pointer is what touch support produces, so it travels as `hasTouch`.
|
|
18
|
+
*
|
|
19
|
+
* Measured in real headless Chromium: `hasTouch: true` makes `(pointer: coarse)` match and
|
|
20
|
+
* `(pointer: fine)` stop matching, with `maxTouchPoints` at 1. `isMobile` does the same and also
|
|
21
|
+
* changes viewport-meta handling, so it is deliberately left out.
|
|
22
|
+
*/
|
|
23
|
+
export function contextEmulationOf(emulate) {
|
|
24
|
+
const { pointer, deterministic, ...rest } = emulate;
|
|
25
|
+
return {
|
|
26
|
+
...rest,
|
|
27
|
+
...(pointer === undefined ? {} : { hasTouch: Pointer.COARSE === pointer }),
|
|
28
|
+
// A deterministic page draws at one density on every machine.
|
|
29
|
+
...(deterministic === undefined ? {} : { deviceScaleFactor: 1 }),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
14
32
|
/** The page's own layout viewport, read back after a resize. */
|
|
15
33
|
const VIEWPORT_READ_SCRIPT = '[window.innerWidth, window.innerHeight]';
|
|
16
34
|
/** The size a page reports for itself, or undefined when it cannot be read. */
|
|
@@ -5,6 +5,7 @@ interface LeaseMarkerArg {
|
|
|
5
5
|
targetOrigin: string;
|
|
6
6
|
sessionParam: string;
|
|
7
7
|
projectParam: string;
|
|
8
|
+
sessionStorageKey: string;
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* Init script restoring the marker a server redirect dropped, so the SDK registers under the lease id.
|
|
@@ -13,6 +14,11 @@ interface LeaseMarkerArg {
|
|
|
13
14
|
* target origin (never an identity provider's or any third party's URL), and the pool installs it
|
|
14
15
|
* only when the navigated URL's marker matches this lease's id. Must stay self-contained: Playwright
|
|
15
16
|
* serialises the function source into the page.
|
|
17
|
+
*
|
|
18
|
+
* It also writes the lease id to the tab session store before page scripts run. An SPA that calls
|
|
19
|
+
* `history.replaceState` on load drops the URL marker before `connect()`, and the SDK then reads
|
|
20
|
+
* the id from that store (#2026). A value the tab holds already stays: a reload holds the lease id,
|
|
21
|
+
* and a different value is one the SDK stored for an explicit `connect({ session })`.
|
|
16
22
|
*/
|
|
17
23
|
export declare function stampLeaseMarker(arg: LeaseMarkerArg): void;
|
|
18
24
|
export declare function installLeaseMarker(page: PooledPage, leaseId: string, targetOrigin: string | undefined, project: string | undefined): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RETICLE_URL_PARAM } from '@reticlehq/core';
|
|
1
|
+
import { RETICLE_SESSION_STORAGE_KEY, RETICLE_URL_PARAM } from '@reticlehq/core';
|
|
2
2
|
/**
|
|
3
3
|
* Init script restoring the marker a server redirect dropped, so the SDK registers under the lease id.
|
|
4
4
|
* It runs only in pages of the lease's own browser context, so a person's tab at the same URL never
|
|
@@ -6,11 +6,24 @@ import { RETICLE_URL_PARAM } from '@reticlehq/core';
|
|
|
6
6
|
* target origin (never an identity provider's or any third party's URL), and the pool installs it
|
|
7
7
|
* only when the navigated URL's marker matches this lease's id. Must stay self-contained: Playwright
|
|
8
8
|
* serialises the function source into the page.
|
|
9
|
+
*
|
|
10
|
+
* It also writes the lease id to the tab session store before page scripts run. An SPA that calls
|
|
11
|
+
* `history.replaceState` on load drops the URL marker before `connect()`, and the SDK then reads
|
|
12
|
+
* the id from that store (#2026). A value the tab holds already stays: a reload holds the lease id,
|
|
13
|
+
* and a different value is one the SDK stored for an explicit `connect({ session })`.
|
|
9
14
|
*/
|
|
10
15
|
export function stampLeaseMarker(arg) {
|
|
11
16
|
const win = globalThis.window;
|
|
12
17
|
if (win === undefined || win.top !== win || win.location.origin !== arg.targetOrigin)
|
|
13
18
|
return;
|
|
19
|
+
try {
|
|
20
|
+
if (null === win.sessionStorage.getItem(arg.sessionStorageKey)) {
|
|
21
|
+
win.sessionStorage.setItem(arg.sessionStorageKey, arg.session);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// A page with storage blocked still gets the URL marker below.
|
|
26
|
+
}
|
|
14
27
|
try {
|
|
15
28
|
const url = new URL(win.location.href);
|
|
16
29
|
if (url.searchParams.has(arg.sessionParam))
|
|
@@ -34,6 +47,7 @@ export async function installLeaseMarker(page, leaseId, targetOrigin, project) {
|
|
|
34
47
|
targetOrigin,
|
|
35
48
|
sessionParam: RETICLE_URL_PARAM.SESSION,
|
|
36
49
|
projectParam: RETICLE_URL_PARAM.PROJECT,
|
|
50
|
+
sessionStorageKey: RETICLE_SESSION_STORAGE_KEY,
|
|
37
51
|
});
|
|
38
52
|
}
|
|
39
53
|
export function leaseMarkerOf(url) {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { PooledDialog, PooledPage, PooledPageError } from './pool-contract.js';
|
|
2
|
+
/** The page-error hooks `BrowserPoolOptions` carries, forwarded here verbatim. */
|
|
3
|
+
export interface LeasePageErrorHooks {
|
|
4
|
+
onPageError?: ((leaseId: string, error: PooledPageError) => void) | undefined;
|
|
5
|
+
onLeaseClosed?: ((leaseId: string) => void) | undefined;
|
|
6
|
+
/** Each native dialog the pool dismissed. See `BrowserPoolOptions.onDialog`. */
|
|
7
|
+
onDialog?: ((leaseId: string, dialog: Pick<PooledDialog, 'kind' | 'message'>) => void) | undefined;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Install the lease's uncaught-error listener and its early console queue. Call BEFORE the first
|
|
11
|
+
* navigation — the timing is the whole fix: an import-time throw lands during `goto`, and one
|
|
12
|
+
* attached after it reads a broken page as healthy. Forwarded with the lease id, because the
|
|
13
|
+
* session for this lease usually does not exist yet, so the sink buffers.
|
|
14
|
+
*/
|
|
15
|
+
export declare function listenForPageErrors(page: PooledPage, leaseId: string, hooks: LeasePageErrorHooks | undefined): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Report a closed lease — manual release, reaper and crash reclaim all funnel through the pool's
|
|
18
|
+
* one release path. The wiring owns whether anything was kept FOR that lease and drops it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function reportLeaseClosed(hooks: LeasePageErrorHooks | undefined, leaseId: string): void;
|
|
21
|
+
/** How many console calls and error events a page keeps for an SDK that has not installed yet. */
|
|
22
|
+
export declare const EARLY_CAPTURE_LIMIT = 200;
|
|
23
|
+
/**
|
|
24
|
+
* The argument for `queueEarlyConsole`. Every driven page gets the same one: a pool lease, and
|
|
25
|
+
* `verify`'s own page. It looks for the SDK every `pollMs`, for at most `maxPolls` looks (30 s).
|
|
26
|
+
*/
|
|
27
|
+
export declare const EARLY_CAPTURE_ARG: {
|
|
28
|
+
key: string;
|
|
29
|
+
limit: number;
|
|
30
|
+
pollMs: number;
|
|
31
|
+
maxPolls: number;
|
|
32
|
+
};
|
|
33
|
+
interface EarlyCaptureArg {
|
|
34
|
+
key: string;
|
|
35
|
+
limit: number;
|
|
36
|
+
pollMs: number;
|
|
37
|
+
maxPolls: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Init script: keep the console errors and warnings a page logs before the SDK installs.
|
|
41
|
+
*
|
|
42
|
+
* An app connects the SDK from a module script or a bundle, and both run after the page's own
|
|
43
|
+
* classic scripts. An error such a script logs while the page loads was lost, so "no console
|
|
44
|
+
* errors" passed on a page that logged one on every load. Playwright's own console event is not a
|
|
45
|
+
* substitute: it also reports messages the browser writes itself (a failed WebSocket, which is how
|
|
46
|
+
* the SDK's own first dial can fail), and the SDK's console channel has never counted those.
|
|
47
|
+
*
|
|
48
|
+
* So this keeps the RAW calls and events. Once the SDK has wrapped the console, this hands them to
|
|
49
|
+
* it: each call goes through the console again and each event is dispatched again, so the SDK's own
|
|
50
|
+
* handlers report them and the meaning of "console error" stays the SDK's. The SDK needs no code for
|
|
51
|
+
* this, so a page that Reticle does not drive downloads nothing for it. Entries the bound did not
|
|
52
|
+
* keep are declared with one console warning. Must stay self-contained: Playwright serialises the
|
|
53
|
+
* function source into the page.
|
|
54
|
+
*/
|
|
55
|
+
export declare function queueEarlyConsole(arg: EarlyCaptureArg): void;
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page-error side of a lease: listen for the throws the SDK will never see, and tell the
|
|
3
|
+
* wiring when the lease is gone.
|
|
4
|
+
*
|
|
5
|
+
* An app whose module graph throws during the first navigation fires its `error` event while the
|
|
6
|
+
* page still has no Reticle SDK in it — the zero-install reader is only evaluated after `goto`
|
|
7
|
+
* resolves — so nothing on the page records it, and a blank page reported as a bare console-error
|
|
8
|
+
* count (#1966). The server's Playwright handle is attached before the navigation, so it is the
|
|
9
|
+
* one witness that cannot miss the throw. This module owns that wiring for `BrowserPool`; the
|
|
10
|
+
* journal that buffers and journals what arrives lives in `page-error-journal.ts`.
|
|
11
|
+
*/
|
|
12
|
+
import { RETICLE_EARLY_CAPTURE } from '@reticlehq/core';
|
|
13
|
+
/**
|
|
14
|
+
* Install the lease's uncaught-error listener and its early console queue. Call BEFORE the first
|
|
15
|
+
* navigation — the timing is the whole fix: an import-time throw lands during `goto`, and one
|
|
16
|
+
* attached after it reads a broken page as healthy. Forwarded with the lease id, because the
|
|
17
|
+
* session for this lease usually does not exist yet, so the sink buffers.
|
|
18
|
+
*/
|
|
19
|
+
export async function listenForPageErrors(page, leaseId, hooks) {
|
|
20
|
+
page.onPageError?.((error) => {
|
|
21
|
+
hooks?.onPageError?.(leaseId, error);
|
|
22
|
+
});
|
|
23
|
+
await page.addInitScript?.(queueEarlyConsole, EARLY_CAPTURE_ARG);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Report a closed lease — manual release, reaper and crash reclaim all funnel through the pool's
|
|
27
|
+
* one release path. The wiring owns whether anything was kept FOR that lease and drops it.
|
|
28
|
+
*/
|
|
29
|
+
export function reportLeaseClosed(hooks, leaseId) {
|
|
30
|
+
hooks?.onLeaseClosed?.(leaseId);
|
|
31
|
+
}
|
|
32
|
+
/** How many console calls and error events a page keeps for an SDK that has not installed yet. */
|
|
33
|
+
export const EARLY_CAPTURE_LIMIT = 200;
|
|
34
|
+
/**
|
|
35
|
+
* The argument for `queueEarlyConsole`. Every driven page gets the same one: a pool lease, and
|
|
36
|
+
* `verify`'s own page. It looks for the SDK every `pollMs`, for at most `maxPolls` looks (30 s).
|
|
37
|
+
*/
|
|
38
|
+
export const EARLY_CAPTURE_ARG = {
|
|
39
|
+
key: RETICLE_EARLY_CAPTURE,
|
|
40
|
+
limit: EARLY_CAPTURE_LIMIT,
|
|
41
|
+
pollMs: 50,
|
|
42
|
+
maxPolls: 600,
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Init script: keep the console errors and warnings a page logs before the SDK installs.
|
|
46
|
+
*
|
|
47
|
+
* An app connects the SDK from a module script or a bundle, and both run after the page's own
|
|
48
|
+
* classic scripts. An error such a script logs while the page loads was lost, so "no console
|
|
49
|
+
* errors" passed on a page that logged one on every load. Playwright's own console event is not a
|
|
50
|
+
* substitute: it also reports messages the browser writes itself (a failed WebSocket, which is how
|
|
51
|
+
* the SDK's own first dial can fail), and the SDK's console channel has never counted those.
|
|
52
|
+
*
|
|
53
|
+
* So this keeps the RAW calls and events. Once the SDK has wrapped the console, this hands them to
|
|
54
|
+
* it: each call goes through the console again and each event is dispatched again, so the SDK's own
|
|
55
|
+
* handlers report them and the meaning of "console error" stays the SDK's. The SDK needs no code for
|
|
56
|
+
* this, so a page that Reticle does not drive downloads nothing for it. Entries the bound did not
|
|
57
|
+
* keep are declared with one console warning. Must stay self-contained: Playwright serialises the
|
|
58
|
+
* function source into the page.
|
|
59
|
+
*/
|
|
60
|
+
export function queueEarlyConsole(arg) {
|
|
61
|
+
const win = globalThis.window;
|
|
62
|
+
if (win === undefined || win[arg.key] !== undefined) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const capture = { queue: [], dropped: 0 };
|
|
66
|
+
win[arg.key] = capture;
|
|
67
|
+
const keep = (item) => {
|
|
68
|
+
if (win[arg.key] !== capture) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (capture.queue.length < arg.limit) {
|
|
72
|
+
capture.queue.push(item);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
capture.dropped += 1;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const mine = {};
|
|
79
|
+
for (const method of ['error', 'warn']) {
|
|
80
|
+
const original = win.console[method];
|
|
81
|
+
const wrapper = (...args) => {
|
|
82
|
+
keep({ method, args });
|
|
83
|
+
original.apply(win.console, args);
|
|
84
|
+
};
|
|
85
|
+
mine[method] = wrapper;
|
|
86
|
+
win.console[method] = wrapper;
|
|
87
|
+
}
|
|
88
|
+
win.addEventListener('error', (event) => keep({ event }), true);
|
|
89
|
+
win.addEventListener('unhandledrejection', (event) => keep({ event }), true);
|
|
90
|
+
// A copy of a kept event: an event that has been dispatched once is copied, not sent again.
|
|
91
|
+
const copy = (event) => {
|
|
92
|
+
if ('reason' in event) {
|
|
93
|
+
return new win.PromiseRejectionEvent('unhandledrejection', {
|
|
94
|
+
promise: event['promise'],
|
|
95
|
+
reason: event['reason'],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const init = {};
|
|
99
|
+
for (const field of ['message', 'filename', 'lineno', 'colno', 'error']) {
|
|
100
|
+
if (event[field] !== undefined) {
|
|
101
|
+
init[field] = event[field];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return new win.ErrorEvent('error', init);
|
|
105
|
+
};
|
|
106
|
+
// A `<script>`, `<img>` or `<link>` that failed to load dispatches a plain `Event` on its element,
|
|
107
|
+
// and only the element names the URL. A copy on `window` loses it, and the SDK reported an error
|
|
108
|
+
// with no message, no source and line 0 (#2220). So such an event is sent again from the same
|
|
109
|
+
// element, and the SDK's capture listener on `window` reads the tag and the URL. A listener added
|
|
110
|
+
// after the SDK's stops it on `window`, so the page's own handlers on the element do not run twice.
|
|
111
|
+
// An element that left the page cannot reach `window`, so it gets the copy.
|
|
112
|
+
const resend = (event) => {
|
|
113
|
+
const target = event['target'];
|
|
114
|
+
if ('message' in event || 'reason' in event || true !== target?.isConnected) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const again = new win.Event('error');
|
|
118
|
+
const stop = (seen) => {
|
|
119
|
+
if (seen === again) {
|
|
120
|
+
again.stopPropagation();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
win.addEventListener('error', stop, true);
|
|
124
|
+
try {
|
|
125
|
+
target.dispatchEvent?.(again);
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
win.removeEventListener('error', stop, true);
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
};
|
|
132
|
+
let polls = 0;
|
|
133
|
+
const timer = win.setInterval(() => {
|
|
134
|
+
polls += 1;
|
|
135
|
+
// The SDK has installed when the console is no longer this script's own wrapper.
|
|
136
|
+
const installed = win.console.error !== mine.error;
|
|
137
|
+
if (!installed && polls < arg.maxPolls) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
win.clearInterval(timer);
|
|
141
|
+
win[arg.key] = null;
|
|
142
|
+
if (!installed) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
for (const item of capture.queue) {
|
|
146
|
+
if ('method' in item) {
|
|
147
|
+
win.console[item['method']](...item['args']);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const event = item['event'];
|
|
151
|
+
if (!resend(event)) {
|
|
152
|
+
win.dispatchEvent(copy(event));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (capture.dropped > 0) {
|
|
157
|
+
win.console.warn(`reticle: ${String(capture.dropped)} early console entries were not kept`);
|
|
158
|
+
}
|
|
159
|
+
}, arg.pollMs);
|
|
160
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Journal the uncaught page errors the SDK was never installed in time to see.
|
|
3
|
+
*
|
|
4
|
+
* An app whose module graph throws during the first navigation fires its `error` event while the
|
|
5
|
+
* page still has no Reticle SDK in it — the zero-install reader is only evaluated after `goto`
|
|
6
|
+
* resolves. The agent then saw a blank page and a bare console-error count, and had to re-run the
|
|
7
|
+
* app under `--enable-logging` to learn why (#1966). The pool's Playwright handle is attached
|
|
8
|
+
* before the navigation, so the server is the one witness that cannot miss the throw.
|
|
9
|
+
*
|
|
10
|
+
* The pool hands errors here with the lease id. The session for that lease usually does not exist
|
|
11
|
+
* yet, so errors buffer per lease and flush the moment the session connects
|
|
12
|
+
* (`bridge.attachSessionCreate`). One already connected means the error is journaled at once.
|
|
13
|
+
*
|
|
14
|
+
* The daemon runs for days, so both maps are told to forget: a lease that is released or reaped
|
|
15
|
+
* has its buffer dropped (`onLeaseClosed`), and a session that has ended has its dedupe memory
|
|
16
|
+
* dropped (`onSessionEnd`). Without those, every failed lease and every ended session leaves an
|
|
17
|
+
* entry behind forever.
|
|
18
|
+
*
|
|
19
|
+
* Deduplication: when the SDK IS installed it reports the same throw as `error.uncaught` — with
|
|
20
|
+
* the browser's `Uncaught ` prefix on the message, which Playwright's spelling lacks. Both
|
|
21
|
+
* spellings fold into one identity (`uncaughtIdentity`), and whichever source is seen first
|
|
22
|
+
* suppresses the other's copy. A journal write is deferred by a short window first, so an SDK
|
|
23
|
+
* event racing the page error lands inside the window and wins. The reverse race — our copy
|
|
24
|
+
* first, the SDK's right after — can still journal both; the SDK's own event is authoritative and
|
|
25
|
+
* only the redundant second copy is cost, not wrong evidence.
|
|
26
|
+
*/
|
|
27
|
+
import { type ReticleEvent } from '@reticlehq/core';
|
|
28
|
+
import type { PooledDialog, PooledPageError } from './pool-contract.js';
|
|
29
|
+
/** The session surface the journal needs. The real `Session` satisfies this structurally. */
|
|
30
|
+
export interface JournalableSession {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly url?: string;
|
|
33
|
+
pushEvent(event: ReticleEvent): void;
|
|
34
|
+
eventsSince(cursor: number): ReticleEvent[];
|
|
35
|
+
onEvent(listener: (event: ReticleEvent) => void): () => void;
|
|
36
|
+
}
|
|
37
|
+
export interface PageErrorJournal {
|
|
38
|
+
/** The pool's sink — hand it to `BrowserPoolOptions.onPageError`. */
|
|
39
|
+
onPoolPageError(leaseId: string, error: PooledPageError): void;
|
|
40
|
+
/** The bridge hook — register with `bridge.attachSessionCreate`. */
|
|
41
|
+
onSessionCreate(session: JournalableSession): void;
|
|
42
|
+
/** The pool's release hook — register with `BrowserPoolOptions.onLeaseClosed`. */
|
|
43
|
+
onLeaseClosed(leaseId: string): void;
|
|
44
|
+
/** The bridge hook — register with `bridge.attachSessionEnd`. */
|
|
45
|
+
onSessionEnd(sessionId: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* The pool's dialog sink — hand it to `BrowserPoolOptions.onDialog`. Journals the dismissal as
|
|
48
|
+
* `dialog.opened` on the lease's session, so a verdict reads it as the SDK's own answer. A dialog
|
|
49
|
+
* before the session connects is dropped: no action window can hold it.
|
|
50
|
+
*/
|
|
51
|
+
onPoolDialog(leaseId: string, dialog: Pick<PooledDialog, 'kind' | 'message'>): void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Fold the two spellings of one throw into one identity. Chromium prefixes a window-reported
|
|
55
|
+
* uncaught error with `Uncaught ` and often the error class (`Uncaught TypeError: ...`); the
|
|
56
|
+
* Playwright `pageerror` message is the bare exception text. Everything after the first line is a
|
|
57
|
+
* stack fragment and never matches, so it is dropped.
|
|
58
|
+
*/
|
|
59
|
+
export declare function uncaughtIdentity(rawMessage: string): string;
|
|
60
|
+
export declare function makePageErrorJournal(deps: {
|
|
61
|
+
resolveSession: (leaseId: string) => JournalableSession | undefined;
|
|
62
|
+
sleep?: (ms: number) => Promise<void>;
|
|
63
|
+
}): PageErrorJournal;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Journal the uncaught page errors the SDK was never installed in time to see.
|
|
3
|
+
*
|
|
4
|
+
* An app whose module graph throws during the first navigation fires its `error` event while the
|
|
5
|
+
* page still has no Reticle SDK in it — the zero-install reader is only evaluated after `goto`
|
|
6
|
+
* resolves. The agent then saw a blank page and a bare console-error count, and had to re-run the
|
|
7
|
+
* app under `--enable-logging` to learn why (#1966). The pool's Playwright handle is attached
|
|
8
|
+
* before the navigation, so the server is the one witness that cannot miss the throw.
|
|
9
|
+
*
|
|
10
|
+
* The pool hands errors here with the lease id. The session for that lease usually does not exist
|
|
11
|
+
* yet, so errors buffer per lease and flush the moment the session connects
|
|
12
|
+
* (`bridge.attachSessionCreate`). One already connected means the error is journaled at once.
|
|
13
|
+
*
|
|
14
|
+
* The daemon runs for days, so both maps are told to forget: a lease that is released or reaped
|
|
15
|
+
* has its buffer dropped (`onLeaseClosed`), and a session that has ended has its dedupe memory
|
|
16
|
+
* dropped (`onSessionEnd`). Without those, every failed lease and every ended session leaves an
|
|
17
|
+
* entry behind forever.
|
|
18
|
+
*
|
|
19
|
+
* Deduplication: when the SDK IS installed it reports the same throw as `error.uncaught` — with
|
|
20
|
+
* the browser's `Uncaught ` prefix on the message, which Playwright's spelling lacks. Both
|
|
21
|
+
* spellings fold into one identity (`uncaughtIdentity`), and whichever source is seen first
|
|
22
|
+
* suppresses the other's copy. A journal write is deferred by a short window first, so an SDK
|
|
23
|
+
* event racing the page error lands inside the window and wins. The reverse race — our copy
|
|
24
|
+
* first, the SDK's right after — can still journal both; the SDK's own event is authoritative and
|
|
25
|
+
* only the redundant second copy is cost, not wrong evidence.
|
|
26
|
+
*/
|
|
27
|
+
import { CONSOLE_EVENT, EventType, RETICLE_URL_PARAM } from '@reticlehq/core';
|
|
28
|
+
/**
|
|
29
|
+
* What a dismissal answers, per dialog kind the wire knows. The same replies the SDK gives
|
|
30
|
+
* (`observers/dialogs.ts`): `confirm` is "no", `prompt` is no value, `alert` has no answer.
|
|
31
|
+
* A kind not in this map (`beforeunload`) is not a `dialog.opened` and is not reported.
|
|
32
|
+
*/
|
|
33
|
+
const DISMISSED_ANSWER = {
|
|
34
|
+
alert: {},
|
|
35
|
+
confirm: { answered: false },
|
|
36
|
+
prompt: { answered: null },
|
|
37
|
+
};
|
|
38
|
+
/** How many errors wait per lease for a session that may never connect. Older ones give way. */
|
|
39
|
+
const MAX_BUFFERED_PER_LEASE = 8;
|
|
40
|
+
/** How many recent error identities a session tracks — the dedupe memory for both sources. */
|
|
41
|
+
const MAX_TRACKED_IDENTITIES = 32;
|
|
42
|
+
/**
|
|
43
|
+
* How long a journal write waits before committing, so an SDK event for the same throw — the
|
|
44
|
+
* websocket hop against a CDP callback — can arrive first and claim the identity.
|
|
45
|
+
*/
|
|
46
|
+
const DEDUPE_WAIT_MS = 50;
|
|
47
|
+
/**
|
|
48
|
+
* Fold the two spellings of one throw into one identity. Chromium prefixes a window-reported
|
|
49
|
+
* uncaught error with `Uncaught ` and often the error class (`Uncaught TypeError: ...`); the
|
|
50
|
+
* Playwright `pageerror` message is the bare exception text. Everything after the first line is a
|
|
51
|
+
* stack fragment and never matches, so it is dropped.
|
|
52
|
+
*/
|
|
53
|
+
export function uncaughtIdentity(rawMessage) {
|
|
54
|
+
const withoutPrefix = rawMessage.replace(/^Uncaught\s+/i, '');
|
|
55
|
+
const firstLine = withoutPrefix.split('\n')[0] ?? withoutPrefix;
|
|
56
|
+
// Lazy `*?`: the greedy spelling cannot match a BARE `Error: ` — the leading character class
|
|
57
|
+
// eats the `E` and backtracking never gives it back.
|
|
58
|
+
return firstLine.replace(/^[A-Za-z0-9_]*?(?:Error|Exception):\s*/, '');
|
|
59
|
+
}
|
|
60
|
+
/** The first stack frame's file and line — the `source (file:line)` the issue asks the agent to see. */
|
|
61
|
+
const STACK_FRAME = /\bat\s+(\S+?):(\d+):\d+\b/;
|
|
62
|
+
function originOf(stack) {
|
|
63
|
+
if (stack === undefined) {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
const frame = STACK_FRAME.exec(stack);
|
|
67
|
+
const source = frame?.[1];
|
|
68
|
+
const line = frame?.[2];
|
|
69
|
+
if (source === undefined || line === undefined) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
return { source, line: Number(line) };
|
|
73
|
+
}
|
|
74
|
+
function leaseParamOf(url) {
|
|
75
|
+
if (url === undefined) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return new URL(url).searchParams.get(RETICLE_URL_PARAM.SESSION) ?? undefined;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const realSleep = (ms) => {
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
setTimeout(resolve, ms);
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
export function makePageErrorJournal(deps) {
|
|
91
|
+
const sleep = deps.sleep ?? realSleep;
|
|
92
|
+
/** Errors heard before their session connected, keyed by lease id. */
|
|
93
|
+
const buffered = new Map();
|
|
94
|
+
/** Per session: identities already claimed by the SDK or by us, oldest first. */
|
|
95
|
+
const seen = new Map();
|
|
96
|
+
const mark = (sessionId, identity) => {
|
|
97
|
+
const identities = seen.get(sessionId) ?? [];
|
|
98
|
+
if (identities.includes(identity)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
identities.push(identity);
|
|
102
|
+
if (identities.length > MAX_TRACKED_IDENTITIES) {
|
|
103
|
+
identities.shift();
|
|
104
|
+
}
|
|
105
|
+
seen.set(sessionId, identities);
|
|
106
|
+
};
|
|
107
|
+
const seenBefore = (sessionId, identity) => {
|
|
108
|
+
return (seen.get(sessionId) ?? []).includes(identity);
|
|
109
|
+
};
|
|
110
|
+
const journal = async (session, error, droppedCount) => {
|
|
111
|
+
await sleep(DEDUPE_WAIT_MS);
|
|
112
|
+
const identity = uncaughtIdentity(error.message);
|
|
113
|
+
if (seenBefore(session.id, identity)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
mark(session.id, identity);
|
|
117
|
+
const { source, line } = originOf(error.stack);
|
|
118
|
+
session.pushEvent({
|
|
119
|
+
t: 0, // pushEvent re-stamps with the session's own clock
|
|
120
|
+
type: EventType.CONSOLE_ERROR,
|
|
121
|
+
sessionId: session.id,
|
|
122
|
+
data: {
|
|
123
|
+
message: error.message,
|
|
124
|
+
kind: CONSOLE_EVENT.PAGE_ERROR,
|
|
125
|
+
...(error.stack === undefined ? {} : { stack: error.stack }),
|
|
126
|
+
...(source === undefined ? {} : { source }),
|
|
127
|
+
...(line === undefined ? {} : { line }),
|
|
128
|
+
...(droppedCount === undefined
|
|
129
|
+
? {}
|
|
130
|
+
: { [CONSOLE_EVENT.DROPPED_PAGE_ERRORS]: droppedCount }),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
// A page error must never break the connect path or the session that receives it.
|
|
135
|
+
const journalSafely = (session, error, droppedCount) => {
|
|
136
|
+
journal(session, error, droppedCount).catch(() => undefined);
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
onPoolPageError(leaseId, error) {
|
|
140
|
+
const session = deps.resolveSession(leaseId);
|
|
141
|
+
if (session !== undefined) {
|
|
142
|
+
journalSafely(session, error);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const entry = buffered.get(leaseId) ?? { errors: [], dropped: 0 };
|
|
146
|
+
if (entry.errors.length >= MAX_BUFFERED_PER_LEASE) {
|
|
147
|
+
entry.errors.shift();
|
|
148
|
+
entry.dropped += 1;
|
|
149
|
+
}
|
|
150
|
+
entry.errors.push(error);
|
|
151
|
+
buffered.set(leaseId, entry);
|
|
152
|
+
},
|
|
153
|
+
onSessionCreate(session) {
|
|
154
|
+
// Claim identities the SDK reports, so a page error the SDK already journaled adds no copy.
|
|
155
|
+
session.onEvent((event) => {
|
|
156
|
+
if (event.type !== EventType.ERROR_UNCAUGHT) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const message = event.data['message'];
|
|
160
|
+
if ('string' !== typeof message) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
mark(session.id, uncaughtIdentity(message));
|
|
164
|
+
});
|
|
165
|
+
const leaseId = leaseParamOf(session.url);
|
|
166
|
+
if (leaseId === undefined) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const entry = buffered.get(leaseId);
|
|
170
|
+
if (entry === undefined) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
buffered.delete(leaseId);
|
|
174
|
+
// The buffer is bounded, so a page that threw harder than the bound reports the loss on the
|
|
175
|
+
// first error that survived — the count names what the agent is not seeing.
|
|
176
|
+
entry.errors.forEach((error, i) => {
|
|
177
|
+
journalSafely(session, error, 0 === i && entry.dropped > 0 ? entry.dropped : undefined);
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
// A lease that is released or reaped will never flush: its session already ended, or its page
|
|
181
|
+
// died before any SDK connected. The buffer has nowhere to go, so it goes away.
|
|
182
|
+
onLeaseClosed(leaseId) {
|
|
183
|
+
buffered.delete(leaseId);
|
|
184
|
+
},
|
|
185
|
+
// The dedupe memory belongs to one session's lifetime; an ended session's identities would
|
|
186
|
+
// otherwise suppress nothing real and grow for the life of the daemon.
|
|
187
|
+
onSessionEnd(sessionId) {
|
|
188
|
+
seen.delete(sessionId);
|
|
189
|
+
},
|
|
190
|
+
onPoolDialog(leaseId, dialog) {
|
|
191
|
+
// An own key only: an inherited name such as `toString` is not a dialog kind.
|
|
192
|
+
const kind = dialog.kind;
|
|
193
|
+
const answer = kind !== undefined && Object.hasOwn(DISMISSED_ANSWER, kind)
|
|
194
|
+
? DISMISSED_ANSWER[kind]
|
|
195
|
+
: undefined;
|
|
196
|
+
const session = deps.resolveSession(leaseId);
|
|
197
|
+
if (answer === undefined || session === undefined) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
session.pushEvent({
|
|
201
|
+
t: 0, // pushEvent re-stamps with the session's own clock
|
|
202
|
+
type: EventType.DIALOG_OPENED,
|
|
203
|
+
sessionId: session.id,
|
|
204
|
+
data: { kind: dialog.kind, message: dialog.message, ...answer },
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|