@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
|
@@ -10,8 +10,15 @@
|
|
|
10
10
|
* used from the two points where observation can be lost: a write rejected at dispatch, and a wait
|
|
11
11
|
* cut off mid-predicate.
|
|
12
12
|
*/
|
|
13
|
-
import { EventType, asRecord, asString, isDevToolingUrl } from '@reticlehq/core';
|
|
13
|
+
import { DriftReason, EventType, NetInitiator, asRecord, asString, isDevToolingUrl, } from '@reticlehq/core';
|
|
14
|
+
import { sdkBehindNote } from '../../../portal/session/session-info.js';
|
|
14
15
|
import { awaitDocumentSuccessor, } from '../../../portal/session/session-successor.js';
|
|
16
|
+
/** How long an act whose page navigated away waits for the lease's successor to connect. */
|
|
17
|
+
export const NAVIGATION_FOLLOW_GRACE_MS = 8_000;
|
|
18
|
+
const REAL_CLOCK = {
|
|
19
|
+
now: () => Date.now(),
|
|
20
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
21
|
+
};
|
|
15
22
|
/**
|
|
16
23
|
* Follow the unique same-origin successor and re-ask there, or leave the verdict untouched.
|
|
17
24
|
*
|
|
@@ -22,18 +29,65 @@ import { awaitDocumentSuccessor, } from '../../../portal/session/session-success
|
|
|
22
29
|
*/
|
|
23
30
|
export async function followLostObservation(input) {
|
|
24
31
|
const { sessions, session, verdict, timeout, predicateStarted, reevaluate } = input;
|
|
25
|
-
|
|
32
|
+
const clock = input.clock ?? REAL_CLOCK;
|
|
33
|
+
const spent = session.elapsed() - predicateStarted;
|
|
34
|
+
const remaining = timeout - spent;
|
|
35
|
+
// Phase 1 — the wait was CUT OFF mid-predicate. The page re-announces on the new document;
|
|
36
|
+
// re-ask there with whatever budget is left.
|
|
37
|
+
if (true === verdict.observationLost && remaining > 0) {
|
|
38
|
+
const next = await awaitDocumentSuccessor(sessions, session, remaining);
|
|
39
|
+
if (null !== next) {
|
|
40
|
+
// Computed on the DEPARTED session, before the caller points at the successor: the new
|
|
41
|
+
// session's elapsed() starts at 0 and would silently refund the whole budget the caller had
|
|
42
|
+
// already spent.
|
|
43
|
+
const leftover = timeout - (session.elapsed() - predicateStarted);
|
|
44
|
+
if (leftover <= 0)
|
|
45
|
+
return { session: next, verdict, followed: true };
|
|
46
|
+
return { session: next, verdict: await reevaluate(next, leftover), followed: true };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Phase 2 — gated on NAVIGATION EVIDENCE ALREADY IN THE WINDOW (#2009): a FAILURE graded while
|
|
50
|
+
// the action's own full-page navigation was still in flight reads "still on /login" for a
|
|
51
|
+
// sign-in that worked. The departure observer records the leaving as a NET_PENDING at dispatch
|
|
52
|
+
// time — while the old document's socket is still alive to deliver it — so the evidence is
|
|
53
|
+
// already in the buffer when the verdict is graded. Its presence unlocks the successor wait
|
|
54
|
+
// (bounded by the grace); its absence means the failure is about a document that never moved,
|
|
55
|
+
// and it stands UNCHANGED and AT ONCE — no wait, and the grace is never spent on it.
|
|
56
|
+
const grace = input.failedFollowGraceMs;
|
|
57
|
+
const registryCapable = 'function' === typeof sessions.get && 'function' === typeof sessions.all;
|
|
58
|
+
if (true === verdict.observationLost ||
|
|
59
|
+
true === verdict.pass ||
|
|
60
|
+
grace === undefined ||
|
|
61
|
+
input.since === undefined ||
|
|
62
|
+
!registryCapable) {
|
|
26
63
|
return { session, verdict, followed: false };
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
64
|
+
}
|
|
65
|
+
const departurePending = session
|
|
66
|
+
.eventsSince(input.since)
|
|
67
|
+
.some((e) => e.type === EventType.NET_PENDING && e.data['initiator'] === NetInitiator.NAVIGATION);
|
|
68
|
+
if (!departurePending) {
|
|
30
69
|
return { session, verdict, followed: false };
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
70
|
+
}
|
|
71
|
+
const next = await awaitDocumentSuccessor(sessions, session, grace, clock);
|
|
72
|
+
if (null !== next) {
|
|
73
|
+
return {
|
|
74
|
+
session: next,
|
|
75
|
+
verdict: await reevaluate(next, grace),
|
|
76
|
+
followed: true,
|
|
77
|
+
budgetExtendedMs: grace,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
session,
|
|
82
|
+
followed: false,
|
|
83
|
+
verdict: {
|
|
84
|
+
...verdict,
|
|
85
|
+
observationLost: true,
|
|
86
|
+
failureReason: `the act's page left its document and no successor session connected within ` +
|
|
87
|
+
`${String(grace)} ms. If this action navigated, the outcome after the navigation was ` +
|
|
88
|
+
'never observed — re-run reticle_session { action: "list" } and re-drive on the live session.',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
37
91
|
}
|
|
38
92
|
/** Why a consequence that did not follow a click on a disabled control is not the app's failure. */
|
|
39
93
|
export const DISABLED_TARGET_NOTE = 'the control was disabled when it was acted on, so nothing a user could do there would have ' +
|
|
@@ -52,7 +106,9 @@ export function disabledTargetNote(pass, actResult) {
|
|
|
52
106
|
const UNREACHABLE = {
|
|
53
107
|
covered: (by) => `a person could not have clicked the target: it is covered by ${by}. Dismiss or move what ` +
|
|
54
108
|
'covers it, then act again',
|
|
55
|
-
|
|
109
|
+
HIDDEN: 'a person could not have clicked the target: it is not visible (for example, it is inside a ' +
|
|
110
|
+
'closed <details> or has visibility: hidden). Show it the way a person would, then act again',
|
|
111
|
+
NO_SIZE: 'a person could not have clicked the target: it has no size (zero width or zero height). Give it a size, or act on ' +
|
|
56
112
|
'the control a person actually clicks',
|
|
57
113
|
OFF_VIEWPORT: 'a person could not have clicked the target: it is outside the viewport even after scrolling ' +
|
|
58
114
|
'it into view. Bring it on screen, then act again',
|
|
@@ -68,6 +124,11 @@ const UNREACHABLE = {
|
|
|
68
124
|
export function unreachableTargetNote(actResult) {
|
|
69
125
|
const effect = asRecord(asRecord(actResult)['effect']);
|
|
70
126
|
if (true === effect['occluded']) {
|
|
127
|
+
// A hidden target keeps its box, so the hit test finds whatever is under it. That element
|
|
128
|
+
// covers nothing: the target is not shown.
|
|
129
|
+
if (false === effect['visible']) {
|
|
130
|
+
return UNREACHABLE.HIDDEN;
|
|
131
|
+
}
|
|
71
132
|
const by = asString(effect['occluderName']) ?? asString(effect['occludedBy']);
|
|
72
133
|
return UNREACHABLE.covered(by ?? 'another element');
|
|
73
134
|
}
|
|
@@ -77,10 +138,55 @@ export function unreachableTargetNote(actResult) {
|
|
|
77
138
|
return UNREACHABLE.OFF_VIEWPORT;
|
|
78
139
|
return undefined;
|
|
79
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* The drift for the first sub-step of an `ACT_SEQUENCE` result whose target no person could have
|
|
143
|
+
* clicked, or undefined. The page reports one effect for each sub-step it ran, in order. Synthetic
|
|
144
|
+
* dispatch reaches an unreachable target all the same, so a sequence is read sub-step by sub-step, as
|
|
145
|
+
* a single act is. `labelOf` names the sub-step, by its index in this result.
|
|
146
|
+
*/
|
|
147
|
+
export function unreachableSubStepDrift(sequenceResult, labelOf) {
|
|
148
|
+
const effects = asRecord(sequenceResult)['effects'];
|
|
149
|
+
const list = Array.isArray(effects) ? effects : [];
|
|
150
|
+
for (let at = 0; at < list.length; at += 1) {
|
|
151
|
+
const reason = unreachableTargetNote({ effect: list[at] });
|
|
152
|
+
if (reason !== undefined) {
|
|
153
|
+
return {
|
|
154
|
+
reasonKind: DriftReason.TARGET_UNREACHABLE,
|
|
155
|
+
reason,
|
|
156
|
+
anchor: labelOf(at),
|
|
157
|
+
nearest: null,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
80
163
|
/** Why the acted target leaves the verdict undecided: no person could reach it, or it was disabled. */
|
|
81
164
|
export function actTargetNote(pass, actResult) {
|
|
82
165
|
return unreachableTargetNote(actResult) ?? disabledTargetNote(pass, actResult);
|
|
83
166
|
}
|
|
167
|
+
// Unanchored: the engine prefixes the reason with "the page did not answer:".
|
|
168
|
+
const COMMAND_TIMEOUT = /command '[^']*' timed out after (\d+)ms/;
|
|
169
|
+
/**
|
|
170
|
+
* The cause and the one fix for a read that came back `unknown`, in place of a bare fact (#1990).
|
|
171
|
+
* A page whose SDK is behind this daemon may not serve `match` at all, so the fix is the same
|
|
172
|
+
* SDK-behind sentence the listing shows. On a current page the read ran out of the assertion's own
|
|
173
|
+
* budget, and the fix is more of it. Any reason that is not a command timeout is returned as it came.
|
|
174
|
+
*/
|
|
175
|
+
export function diagnoseTimeout(reason, session) {
|
|
176
|
+
const granted = reason === undefined ? undefined : COMMAND_TIMEOUT.exec(reason)?.[1];
|
|
177
|
+
if (reason === undefined || granted === undefined) {
|
|
178
|
+
return reason;
|
|
179
|
+
}
|
|
180
|
+
const behind = sdkBehindNote(session);
|
|
181
|
+
if (behind !== undefined) {
|
|
182
|
+
return `${reason} ${behind}`;
|
|
183
|
+
}
|
|
184
|
+
return (`${reason} This read was bounded by what the assertion's budget had left ` +
|
|
185
|
+
`(${granted}ms): the budget ran out while the page was still working. ` +
|
|
186
|
+
'Give the assertion more time with `timeout_ms`.');
|
|
187
|
+
}
|
|
188
|
+
/** Why an act's verdict is `unknown`: a read that timed out, else an unreachable or disabled target. */
|
|
189
|
+
export const actInconclusive = (verdict, actResult, session) => diagnoseTimeout(verdict.inconclusive, session) ?? actTargetNote(verdict.pass, actResult);
|
|
84
190
|
/** Calls quoted on a failed check, and how much of each response body. */
|
|
85
191
|
const MAX_NET_CALLS = 5;
|
|
86
192
|
const MAX_BODY_CHARS = 300;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* outcome than "unknown".
|
|
12
12
|
*/
|
|
13
13
|
import { PredicateSchema } from '@reticlehq/engine/question/predicate/predicate-schema.js';
|
|
14
|
+
import { parsePredicate } from '@reticlehq/engine/question/predicate/predicate-parse.js';
|
|
14
15
|
import { SESSION_REF, isGlobalPressCall, unissuedRefRefusal } from '@reticlehq/core';
|
|
15
16
|
import { isDocumentGoneError } from '../../../portal/session/facts/session-replaced.js';
|
|
16
17
|
import { SessionReplacedError } from '../../../portal/session/page-commands/pending-commands.js';
|
|
@@ -77,8 +78,9 @@ const STEP_STRUCTURE_KEYS = new Set([
|
|
|
77
78
|
export function sequenceStepArgs(step) {
|
|
78
79
|
const flat = {};
|
|
79
80
|
for (const [key, value] of Object.entries(step)) {
|
|
80
|
-
if (!STEP_STRUCTURE_KEYS.has(key))
|
|
81
|
+
if (!STEP_STRUCTURE_KEYS.has(key)) {
|
|
81
82
|
flat[key] = value;
|
|
83
|
+
}
|
|
82
84
|
}
|
|
83
85
|
const nested = step['args'];
|
|
84
86
|
if ('string' === typeof nested) {
|
|
@@ -96,6 +98,22 @@ export function sequenceStepArgs(step) {
|
|
|
96
98
|
}
|
|
97
99
|
return { ...flat, ...(null !== nested && 'object' === typeof nested ? nested : {}) };
|
|
98
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Why `parsePredicate` refuses this `expect`, as the sentence `reticle_assert` gives.
|
|
103
|
+
*
|
|
104
|
+
* Called only after `PredicateSchema` has refused the value, so the parse throws. The schema stays
|
|
105
|
+
* the test for refusal: `parsePredicate` also refuses a value that the schema accepts, and this
|
|
106
|
+
* check must not refuse more than it did before.
|
|
107
|
+
*/
|
|
108
|
+
function expectParseIssue(value) {
|
|
109
|
+
try {
|
|
110
|
+
parsePredicate(value);
|
|
111
|
+
return '';
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return error instanceof Error ? error.message : String(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
99
117
|
export function assertSequenceSteps(steps) {
|
|
100
118
|
if (0 === steps.length) {
|
|
101
119
|
throw new Error('reticle_act_sequence was given no steps. Pass steps: [{ ref, action, args? }] or [{ target, action, args? }] — nothing was acted on.');
|
|
@@ -118,18 +136,15 @@ export function assertSequenceSteps(steps) {
|
|
|
118
136
|
* declared; nothing told it the declaration was discarded, and that sentence invites it to add
|
|
119
137
|
* the very thing it just wrote. Same false green as an unreadable key, through a key this tool
|
|
120
138
|
* does read.
|
|
139
|
+
*
|
|
140
|
+
* The reason is the sentence `reticle_assert` gives for the same predicate: the field that failed
|
|
141
|
+
* and the fields that work. A fixed sentence with generic examples did not say that `css` is not a
|
|
142
|
+
* `query` field, so the caller had to guess again. That sentence also carries the schema's own
|
|
143
|
+
* words, such as the keys it saw when it could not tell the kind.
|
|
121
144
|
*/
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const said = parsed.error.issues
|
|
126
|
-
.filter((issue) => 'custom' === issue.code)
|
|
127
|
-
.map((issue) => ` ${issue.message}.`)
|
|
128
|
-
.join('');
|
|
129
|
-
throw new Error(`step ${String(i)} carries an \`expect\` this tool cannot parse.${said} ` +
|
|
130
|
-
'A consequence is a predicate object with a `kind` — e.g. { kind: "signal", name: ' +
|
|
131
|
-
'"order:placed" }, { kind: "element", by: "testid", value: "receipt" }, or ' +
|
|
132
|
-
'{ kind: "settled" } — the same shape reticle_act_and_wait takes as `until`. ' +
|
|
145
|
+
if (step['expect'] !== undefined && !PredicateSchema.safeParse(step['expect']).success) {
|
|
146
|
+
throw new Error(`step ${String(i)} carries an \`expect\` this tool cannot parse: ` +
|
|
147
|
+
`${expectParseIssue(step['expect'])} ` +
|
|
133
148
|
'Refused rather than dropped, because a step whose declaration was discarded reports ' +
|
|
134
149
|
'as a step that declared nothing. Nothing was acted on.');
|
|
135
150
|
}
|
|
@@ -145,14 +160,16 @@ export function assertSequenceSteps(steps) {
|
|
|
145
160
|
}
|
|
146
161
|
return;
|
|
147
162
|
}
|
|
148
|
-
if (step['target'] !== undefined)
|
|
163
|
+
if (step['target'] !== undefined) {
|
|
149
164
|
return;
|
|
150
|
-
|
|
165
|
+
}
|
|
166
|
+
if (isGlobalPressCall(step)) {
|
|
151
167
|
return;
|
|
168
|
+
}
|
|
152
169
|
throw new Error(`step ${String(i)} has no \`ref\` or \`target\`. ` +
|
|
153
170
|
'Sequence steps take `ref` (from reticle_query/reticle_snapshot) or `target` ' +
|
|
154
171
|
'(e.g. { testid } or { label }), the same locator reticle_act accepts. ' +
|
|
155
|
-
'A press of Escape, Tab, or a modifier shortcut is a document key and needs neither. ' +
|
|
172
|
+
'A press of Escape, Tab, an arrow key or a modifier shortcut, or any press with `document: true`, is a document key and needs neither. ' +
|
|
156
173
|
'Nothing was acted on — the whole sequence is refused so a bad step cannot leave the earlier ones half-applied.');
|
|
157
174
|
});
|
|
158
175
|
}
|
|
@@ -165,13 +182,15 @@ export function preflightAct(actArgs, until) {
|
|
|
165
182
|
// the agent its trusted click had happened. See act-danger.
|
|
166
183
|
assertNativeInputSupported(actArgs);
|
|
167
184
|
const unevaluable = unevaluablePredicateReason(until);
|
|
168
|
-
if (unevaluable !== undefined)
|
|
185
|
+
if (unevaluable !== undefined) {
|
|
169
186
|
throw new Error(unevaluable);
|
|
187
|
+
}
|
|
170
188
|
// A predicate that was already true when it was written answered `no-fault` / `already_true` —
|
|
171
189
|
// verdict-shaped, and carrying no proof of anything the action did. See vacuousPredicateReason.
|
|
172
190
|
const vacuous = vacuousPredicateReason(until);
|
|
173
|
-
if (vacuous !== undefined)
|
|
191
|
+
if (vacuous !== undefined) {
|
|
174
192
|
throw new Error(vacuous);
|
|
193
|
+
}
|
|
175
194
|
}
|
|
176
195
|
/**
|
|
177
196
|
* Dispatch a write, or report that its outcome went unobserved.
|
|
@@ -191,8 +210,9 @@ export async function dispatchAct(send) {
|
|
|
191
210
|
return await send();
|
|
192
211
|
}
|
|
193
212
|
catch (error) {
|
|
194
|
-
if (error instanceof SessionReplacedError || isDocumentGoneError(error))
|
|
213
|
+
if (error instanceof SessionReplacedError || isDocumentGoneError(error)) {
|
|
195
214
|
return null;
|
|
215
|
+
}
|
|
196
216
|
throw error;
|
|
197
217
|
}
|
|
198
218
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
* re-render,
|
|
2
|
+
* The things a sequence step needs beyond a plain act: a retry when the ref goes stale under a
|
|
3
|
+
* re-render, a result shaped for the per-step report, and the `burst` pace (at the end).
|
|
4
4
|
*
|
|
5
5
|
* ── THE RETRY ─────────────────────────────────────────────────────────────────────────────────
|
|
6
6
|
*
|
|
@@ -42,4 +42,25 @@ export declare function runStepWithStaleRetry(attempt: () => Promise<StepOutcome
|
|
|
42
42
|
* nothing to look for".
|
|
43
43
|
*/
|
|
44
44
|
export declare function describeStepResult(step: Record<string, unknown>, result: Record<string, unknown>): Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Ceiling for a burst gap. Past this it is not a burst, it is a paced walk.
|
|
47
|
+
*
|
|
48
|
+
* HOW TO PICK A GAP, because both edges are real and neither is guessable. It must outlast the
|
|
49
|
+
* handler committing and dispatching its request, and undercut the response it is racing.
|
|
50
|
+
* Measured against a superseded-filter race (the superseded query 700ms, the one replacing it 90ms):
|
|
51
|
+
* at 0ms the first request was never issued at all, and at the ordinary settle (~500ms in a
|
|
52
|
+
* throttled tab, where rAF never fires and the frame budget always times out) the two clicks
|
|
53
|
+
* serialised and never overlapped. 50ms sat in the window. A different app needs a different
|
|
54
|
+
* number: read its own slow endpoint.
|
|
55
|
+
*/
|
|
56
|
+
export declare const BURST_MAX_GAP_MS = 500;
|
|
57
|
+
/** A recorded gap as the live tool would apply it: clamped, or absent. */
|
|
58
|
+
export declare function burstGap(burstMs: number | undefined): number | undefined;
|
|
59
|
+
/** A burst step returns as soon as it is dispatched: the gap, not the settle, paces the sequence. */
|
|
60
|
+
export declare function pacedArgs(args: Record<string, unknown>, gap: number | undefined): Record<string, unknown>;
|
|
61
|
+
/**
|
|
62
|
+
* Wait out the rest of the gap that began when the previous step was dispatched, if there is one. The gap is between
|
|
63
|
+
* ACTIONS, as live, so time the previous step already took counts towards it.
|
|
64
|
+
*/
|
|
65
|
+
export declare function holdGap(elapsed: () => number, since: number | undefined, gap: number | undefined, sleep: (ms: number) => Promise<void>): Promise<void>;
|
|
45
66
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
* re-render,
|
|
2
|
+
* The things a sequence step needs beyond a plain act: a retry when the ref goes stale under a
|
|
3
|
+
* re-render, a result shaped for the per-step report, and the `burst` pace (at the end).
|
|
4
4
|
*
|
|
5
5
|
* ── THE RETRY ─────────────────────────────────────────────────────────────────────────────────
|
|
6
6
|
*
|
|
@@ -30,8 +30,9 @@ const realSleep = {
|
|
|
30
30
|
*/
|
|
31
31
|
export async function runStepWithStaleRetry(attempt, session, since, budgetMs, opts = realSleep) {
|
|
32
32
|
const first = await attempt();
|
|
33
|
-
if (first.ok || !isStaleRefError(first.error))
|
|
33
|
+
if (first.ok || !isStaleRefError(first.error)) {
|
|
34
34
|
return first;
|
|
35
|
+
}
|
|
35
36
|
await waitForReaction(session, since, budgetMs, opts);
|
|
36
37
|
return attempt();
|
|
37
38
|
}
|
|
@@ -51,9 +52,64 @@ export function describeStepResult(step, result) {
|
|
|
51
52
|
settleReason: result['settleReason'] ?? null,
|
|
52
53
|
};
|
|
53
54
|
// `reticleUi`: the step hit Reticle's own HUD, so the recorder leaves it out of the app's flow.
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
// `secret`: the SDK says the target holds a secret. Without it a sequence saved a password in clear.
|
|
56
|
+
// `nth`/`of`/`testidOf`: the control's position among namesakes, which the recorder anchors on.
|
|
57
|
+
// `componentNth`/`componentOf`: its position among the elements of its component anchor.
|
|
58
|
+
for (const key of [
|
|
59
|
+
'testid',
|
|
60
|
+
'testidOf',
|
|
61
|
+
'testidNth',
|
|
62
|
+
'nth',
|
|
63
|
+
'of',
|
|
64
|
+
'component',
|
|
65
|
+
'componentNth',
|
|
66
|
+
'componentOf',
|
|
67
|
+
'ownText',
|
|
68
|
+
'role',
|
|
69
|
+
'name',
|
|
70
|
+
'within',
|
|
71
|
+
'source',
|
|
72
|
+
'warning',
|
|
73
|
+
'reticleUi',
|
|
74
|
+
'secret',
|
|
75
|
+
]) {
|
|
76
|
+
if (result[key] !== undefined) {
|
|
56
77
|
out[key] = result[key];
|
|
78
|
+
}
|
|
57
79
|
}
|
|
58
80
|
return out;
|
|
59
81
|
}
|
|
82
|
+
// ── THE BURST PACE ── shared by the live tool and the replay of a saved sequence (#2130). ──────
|
|
83
|
+
/**
|
|
84
|
+
* Ceiling for a burst gap. Past this it is not a burst, it is a paced walk.
|
|
85
|
+
*
|
|
86
|
+
* HOW TO PICK A GAP, because both edges are real and neither is guessable. It must outlast the
|
|
87
|
+
* handler committing and dispatching its request, and undercut the response it is racing.
|
|
88
|
+
* Measured against a superseded-filter race (the superseded query 700ms, the one replacing it 90ms):
|
|
89
|
+
* at 0ms the first request was never issued at all, and at the ordinary settle (~500ms in a
|
|
90
|
+
* throttled tab, where rAF never fires and the frame budget always times out) the two clicks
|
|
91
|
+
* serialised and never overlapped. 50ms sat in the window. A different app needs a different
|
|
92
|
+
* number: read its own slow endpoint.
|
|
93
|
+
*/
|
|
94
|
+
export const BURST_MAX_GAP_MS = 500;
|
|
95
|
+
/** A recorded gap as the live tool would apply it: clamped, or absent. */
|
|
96
|
+
export function burstGap(burstMs) {
|
|
97
|
+
return burstMs === undefined ? undefined : Math.max(0, Math.min(burstMs, BURST_MAX_GAP_MS));
|
|
98
|
+
}
|
|
99
|
+
/** A burst step returns as soon as it is dispatched: the gap, not the settle, paces the sequence. */
|
|
100
|
+
export function pacedArgs(args, gap) {
|
|
101
|
+
return gap === undefined ? args : { ...args, settleMs: 0 };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Wait out the rest of the gap that began when the previous step was dispatched, if there is one. The gap is between
|
|
105
|
+
* ACTIONS, as live, so time the previous step already took counts towards it.
|
|
106
|
+
*/
|
|
107
|
+
export async function holdGap(elapsed, since, gap, sleep) {
|
|
108
|
+
if (since === undefined || gap === undefined) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const remaining = gap - (elapsed() - since);
|
|
112
|
+
if (remaining > 0) {
|
|
113
|
+
await sleep(remaining);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -48,7 +48,9 @@ export async function resolveActTarget(session, args,
|
|
|
48
48
|
* was ignored.
|
|
49
49
|
*/
|
|
50
50
|
appearanceMs) {
|
|
51
|
-
|
|
51
|
+
// A ref inside the target object (`target: { ref }`) is the same instruction as a top-level ref.
|
|
52
|
+
// Read as a query it named nothing, and the step failed with "Nothing was searched".
|
|
53
|
+
const ref = asString(args['ref']) ?? asString(asRecord(args['target'])['ref']);
|
|
52
54
|
if (ref !== undefined && ref.length > 0) {
|
|
53
55
|
// Refused here, at the boundary, and never re-read as a `target`: guessing what an arbitrary
|
|
54
56
|
// string meant is the silent re-resolution the stale-ref refusal exists to prevent.
|
|
@@ -9,9 +9,19 @@
|
|
|
9
9
|
* nothing on its own.
|
|
10
10
|
*/
|
|
11
11
|
import type { Predicate } from '@reticlehq/core';
|
|
12
|
+
import { evaluatePredicate, waitForPredicate, type EvalResult } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
12
13
|
import { waitForInFlight } from './settle-in-flight.js';
|
|
13
14
|
import { revertedAfterMatch } from './durable.js';
|
|
15
|
+
type WaitArgs = Parameters<typeof waitForPredicate>;
|
|
16
|
+
/** Wait for the predicate inside the budget, or read it once when there is no budget. */
|
|
17
|
+
export declare function evaluateAfterAct(session: WaitArgs[0], until: Predicate, timeout: number, since: number, baselines: WaitArgs[4]): Promise<EvalResult>;
|
|
14
18
|
type Waited = Parameters<typeof waitForInFlight>[0] & Parameters<typeof revertedAfterMatch>[0];
|
|
15
19
|
/** Returns the sentence to fail with when the consequence reverted, else undefined. */
|
|
16
20
|
export declare function finishAfterMatch(session: Waited, until: Predicate, since: number, baselines: Parameters<typeof revertedAfterMatch>[3], remaining: () => number): Promise<string | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* A green that rests only on hidden matches, read again until a match shows, for at most
|
|
23
|
+
* SHOWN_GRACE_MS. Returns the reading that shows one, or `verdict` unchanged. Nothing is predicted:
|
|
24
|
+
* the verdict is judged on what the page painted, and a node that stays hidden stays `hidden_match`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function awaitShownMatch(session: Parameters<typeof evaluatePredicate>[0], until: Predicate, since: number, baselines: Parameters<typeof evaluatePredicate>[4], verdict: EvalResult, remaining: () => number): Promise<EvalResult>;
|
|
17
27
|
export {};
|
|
@@ -8,9 +8,18 @@
|
|
|
8
8
|
* rest of the budget the caller already granted is spent here, in three steps, each deciding
|
|
9
9
|
* nothing on its own.
|
|
10
10
|
*/
|
|
11
|
+
import { hiddenMatchNote } from '@reticlehq/engine/evidence/already-true.js';
|
|
12
|
+
import { evaluatePredicate, waitForPredicate, } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
11
13
|
import { waitForInFlight } from './settle-in-flight.js';
|
|
12
14
|
import { waitForReaction } from './react-grace.js';
|
|
13
15
|
import { revertedAfterMatch } from './durable.js';
|
|
16
|
+
/** Wait for the predicate inside the budget, or read it once when there is no budget. */
|
|
17
|
+
export function evaluateAfterAct(session, until, timeout, since, baselines) {
|
|
18
|
+
if (timeout > 0) {
|
|
19
|
+
return waitForPredicate(session, until, timeout, since, baselines);
|
|
20
|
+
}
|
|
21
|
+
return evaluatePredicate(session, until, since, true, baselines);
|
|
22
|
+
}
|
|
14
23
|
/** Returns the sentence to fail with when the consequence reverted, else undefined. */
|
|
15
24
|
export async function finishAfterMatch(session, until, since, baselines, remaining) {
|
|
16
25
|
const sleep = { sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) };
|
|
@@ -28,3 +37,34 @@ export async function finishAfterMatch(session, until, since, baselines, remaini
|
|
|
28
37
|
// 3. Then ask whether what held still holds — see revertedAfterMatch.
|
|
29
38
|
return revertedAfterMatch(session, until, since, baselines);
|
|
30
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* How long a match that holds only on hidden nodes gets to show. An entrance fade or a toast mounted
|
|
42
|
+
* at opacity 0 reads hidden for its first frame, and the wait matched on that frame. A node hidden
|
|
43
|
+
* by design pays this once, on a verdict that is `unknown` either way.
|
|
44
|
+
*/
|
|
45
|
+
const SHOWN_GRACE_MS = 1000;
|
|
46
|
+
/** Poll cadence — the same as the reaction grace. */
|
|
47
|
+
const SHOWN_POLL_MS = 50;
|
|
48
|
+
/**
|
|
49
|
+
* A green that rests only on hidden matches, read again until a match shows, for at most
|
|
50
|
+
* SHOWN_GRACE_MS. Returns the reading that shows one, or `verdict` unchanged. Nothing is predicted:
|
|
51
|
+
* the verdict is judged on what the page painted, and a node that stays hidden stays `hidden_match`.
|
|
52
|
+
*/
|
|
53
|
+
export async function awaitShownMatch(session, until, since, baselines, verdict, remaining) {
|
|
54
|
+
if (!verdict.pass || hiddenMatchNote(until, verdict.evidence) === undefined) {
|
|
55
|
+
return verdict;
|
|
56
|
+
}
|
|
57
|
+
// Counted in reads, not read off the session clock, so a clock that does not move still ends it.
|
|
58
|
+
const reads = Math.floor(Math.min(SHOWN_GRACE_MS, remaining()) / SHOWN_POLL_MS);
|
|
59
|
+
for (let read = 0; read < reads; read += 1) {
|
|
60
|
+
await new Promise((resolve) => setTimeout(resolve, SHOWN_POLL_MS));
|
|
61
|
+
const now = await evaluatePredicate(session, until, since, true, baselines);
|
|
62
|
+
if (!now.pass) {
|
|
63
|
+
return verdict;
|
|
64
|
+
}
|
|
65
|
+
if (hiddenMatchNote(until, now.evidence) === undefined) {
|
|
66
|
+
return now;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return verdict;
|
|
70
|
+
}
|
|
@@ -18,7 +18,7 @@ export async function readAlreadyTrue(session, until, since) {
|
|
|
18
18
|
// baseline yet would report `inconclusive` — reading the app twice to answer a question we had
|
|
19
19
|
// not taken the reading for.
|
|
20
20
|
const baselines = await captureBaselines(session, until);
|
|
21
|
-
const precheck = until !== undefined && readsDomState(until)
|
|
21
|
+
const precheck = until !== undefined && readsDomState(until, since)
|
|
22
22
|
? await evaluatePredicate(session, until, since, false, baselines)
|
|
23
23
|
: undefined;
|
|
24
24
|
const alreadyTrue = precheck?.pass ?? false;
|
|
@@ -16,4 +16,15 @@
|
|
|
16
16
|
* branch reports it (navigate-result.ts): so "Reticle stopped waiting" is distinguishable from "the
|
|
17
17
|
* page never came back", and the fix — a larger `timeout_ms` — is visible in the result.
|
|
18
18
|
*/
|
|
19
|
-
export declare function reloadResult(reconnected: boolean, waitedMs: number): Record<string, unknown>;
|
|
19
|
+
export declare function reloadResult(reconnected: boolean, waitedMs: number, hard?: HardReload): Record<string, unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* How a `hard` reload reached the page (#1785). A leased tab reloads through the browser with the
|
|
22
|
+
* cache ignored. Any other tab can only fetch its document again, so its modules can be stale.
|
|
23
|
+
*/
|
|
24
|
+
export declare const HardReload: {
|
|
25
|
+
readonly CACHE_IGNORED: "cache_ignored";
|
|
26
|
+
readonly DOCUMENT_ONLY: "document_only";
|
|
27
|
+
};
|
|
28
|
+
export type HardReload = (typeof HardReload)[keyof typeof HardReload];
|
|
29
|
+
/** The `hardReload` field for a reload, or nothing when the call did not ask for `hard`. */
|
|
30
|
+
export declare function hardReloadOf(hard: boolean, throughBrowser: boolean): HardReload | undefined;
|
|
@@ -27,8 +27,31 @@ const RELOAD_RECONNECTED_NOTE = 'the page came back and re-announced itself unde
|
|
|
27
27
|
* branch reports it (navigate-result.ts): so "Reticle stopped waiting" is distinguishable from "the
|
|
28
28
|
* page never came back", and the fix — a larger `timeout_ms` — is visible in the result.
|
|
29
29
|
*/
|
|
30
|
-
export function reloadResult(reconnected, waitedMs) {
|
|
31
|
-
|
|
30
|
+
export function reloadResult(reconnected, waitedMs, hard) {
|
|
31
|
+
const base = reconnected
|
|
32
32
|
? { ok: true, confirmed: true, note: RELOAD_RECONNECTED_NOTE }
|
|
33
33
|
: { ok: true, confirmed: false, waitedMs, note: RELOAD_NOTE };
|
|
34
|
+
if (hard === undefined) {
|
|
35
|
+
return base;
|
|
36
|
+
}
|
|
37
|
+
return { ...base, hardReload: hard, note: `${base.note} ${HARD_RELOAD_NOTE[hard]}` };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* How a `hard` reload reached the page (#1785). A leased tab reloads through the browser with the
|
|
41
|
+
* cache ignored. Any other tab can only fetch its document again, so its modules can be stale.
|
|
42
|
+
*/
|
|
43
|
+
export const HardReload = {
|
|
44
|
+
CACHE_IGNORED: 'cache_ignored',
|
|
45
|
+
DOCUMENT_ONLY: 'document_only',
|
|
46
|
+
};
|
|
47
|
+
const HARD_RELOAD_NOTE = {
|
|
48
|
+
[HardReload.CACHE_IGNORED]: 'The browser reloaded with its cache ignored, so every script and module was fetched again.',
|
|
49
|
+
[HardReload.DOCUMENT_ONLY]: 'This tab is not a lease, so only the document was fetched again. Scripts and modules can still come from the browser cache. To fetch them again, reload the tab in the browser with Cmd+Shift+R, or take a lease with reticle_lease.',
|
|
50
|
+
};
|
|
51
|
+
/** The `hardReload` field for a reload, or nothing when the call did not ask for `hard`. */
|
|
52
|
+
export function hardReloadOf(hard, throughBrowser) {
|
|
53
|
+
if (!hard) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
return throughBrowser ? HardReload.CACHE_IGNORED : HardReload.DOCUMENT_ONLY;
|
|
34
57
|
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
export type TargetResolution = {
|
|
3
3
|
readonly kind: 'ref';
|
|
4
4
|
readonly ref: string;
|
|
5
|
+
/** Set when every match was hidden and the action uses the hidden one. Says so to the caller. */
|
|
6
|
+
readonly hiddenTarget?: string;
|
|
5
7
|
} | {
|
|
6
8
|
readonly kind: 'global';
|
|
7
9
|
readonly ref: '';
|
|
@@ -9,6 +11,18 @@ export type TargetResolution = {
|
|
|
9
11
|
readonly kind: 'error';
|
|
10
12
|
readonly message: string;
|
|
11
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* What a result says when every match was hidden and the action used the hidden one. It does not
|
|
16
|
+
* say that a person could not reach it: a hidden input with a visible label is reached through the
|
|
17
|
+
* label, and the reach check after the action decides that.
|
|
18
|
+
*/
|
|
19
|
+
export declare function hiddenTargetNote(ref: string): string;
|
|
20
|
+
/** The `hiddenTarget` field for a tool result, or nothing when the action used a visible match. */
|
|
21
|
+
export declare function hiddenTargetField(resolution: TargetResolution): {
|
|
22
|
+
hiddenTarget?: string;
|
|
23
|
+
};
|
|
24
|
+
/** The words before the nearest-target list in a no_match refusal. The repeat hint reads after it. */
|
|
25
|
+
export declare const NEAREST_TARGETS_LEAD = " The nearest targets on the page, most similar first: ";
|
|
12
26
|
/**
|
|
13
27
|
* Turn a target QUERY into the single ref an action can be performed on.
|
|
14
28
|
*
|
|
@@ -24,7 +38,9 @@ export type TargetResolution = {
|
|
|
24
38
|
*
|
|
25
39
|
* Invisible matches are filtered before counting: a hidden duplicate is a common way for a
|
|
26
40
|
* legitimately unambiguous query to look ambiguous, and refusing on it would push callers back to
|
|
27
|
-
* the two-turn path for no gain.
|
|
41
|
+
* the two-turn path for no gain. When every match is hidden, the one hidden match is used and the
|
|
42
|
+
* resolution names it in `hiddenTarget`. It is not refused: a hidden input is often clicked through
|
|
43
|
+
* its visible label.
|
|
28
44
|
*
|
|
29
45
|
* When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
|
|
30
46
|
* agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
|