@reticlehq/server 3.5.0 → 3.7.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/README.md +1 -1
- package/SKILL.md +123 -116
- package/dist/chromium-launch-options.d.ts +3 -1
- package/dist/chromium-launch-options.js +2 -1
- package/dist/command/cli/adhoc-suite.d.ts +14 -0
- package/dist/command/cli/adhoc-suite.js +42 -7
- package/dist/command/cli/adhoc-verdict.d.ts +19 -2
- package/dist/command/cli/adhoc-verdict.js +76 -9
- package/dist/command/cli/answers/gate-exit.d.ts +23 -0
- package/dist/command/cli/answers/gate-exit.js +45 -0
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +3 -1
- package/dist/command/cli/answers/open-note.d.ts +2 -0
- package/dist/command/cli/answers/open-note.js +22 -0
- package/dist/command/cli/auth/device-flow.d.ts +13 -2
- package/dist/command/cli/auth/device-flow.js +17 -0
- package/dist/command/cli/cli-doctor.js +14 -0
- package/dist/command/cli/cli-flow-commands.d.ts +0 -11
- package/dist/command/cli/cli-flow-commands.js +31 -112
- package/dist/command/cli/cli-kill.js +3 -2
- package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
- package/dist/command/cli/cli-parse-grammar.js +75 -0
- package/dist/command/cli/cli-parse-verify.d.ts +2 -0
- package/dist/command/cli/cli-parse-verify.js +18 -0
- package/dist/command/cli/cli-parse.d.ts +19 -28
- package/dist/command/cli/cli-parse.js +82 -182
- package/dist/command/cli/cli-update-commands.d.ts +1 -1
- package/dist/command/cli/cli-update-commands.js +61 -9
- package/dist/command/cli/cli-usage.d.ts +33 -0
- package/dist/command/cli/cli-usage.js +287 -0
- package/dist/command/cli/cli-verify.d.ts +35 -5
- package/dist/command/cli/cli-verify.js +114 -35
- package/dist/command/cli/cloud-cli.d.ts +13 -0
- package/dist/command/cli/cloud-cli.js +192 -341
- package/dist/command/cli/cloud-kit.d.ts +12 -0
- package/dist/command/cli/cloud-kit.js +41 -2
- package/dist/command/cli/cloud-link.d.ts +57 -0
- package/dist/command/cli/cloud-link.js +308 -0
- package/dist/command/cli/cloud-login.d.ts +22 -0
- package/dist/command/cli/cloud-login.js +96 -8
- package/dist/command/cli/context-command.d.ts +1 -0
- package/dist/command/cli/context-command.js +33 -0
- package/dist/command/cli/daemon-start-options.d.ts +3 -0
- package/dist/command/cli/daemon-start-options.js +24 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +46 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +99 -1
- package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
- package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
- package/dist/command/cli/doctor/doctor-project-line.js +5 -4
- package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
- package/dist/command/cli/doctor/doctor-rows.js +6 -0
- package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
- package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
- package/dist/command/cli/drive/drive-attach.d.ts +27 -2
- package/dist/command/cli/drive/drive-attach.js +71 -4
- package/dist/command/cli/drive/drive-command.d.ts +2 -1
- package/dist/command/cli/drive/drive-command.js +13 -7
- package/dist/command/cli/known-regression.d.ts +13 -0
- package/dist/command/cli/known-regression.js +34 -0
- package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
- package/dist/command/cli/launch/sdk-version-match.js +25 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +15 -3
- package/dist/command/cli/mcp-command.d.ts +6 -0
- package/dist/command/cli/mcp-command.js +13 -5
- package/dist/command/cli/ports/port-holder.d.ts +10 -0
- package/dist/command/cli/ports/port-holder.js +25 -1
- package/dist/command/cli/project-name.d.ts +29 -1
- package/dist/command/cli/project-name.js +107 -2
- package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
- package/dist/command/cli/setup-mcp-cli.js +35 -12
- package/dist/command/cli/status/status-lines.d.ts +1 -0
- package/dist/command/cli/status/status-lines.js +47 -2
- package/dist/command/cli/status/workspace-links.d.ts +10 -0
- package/dist/command/cli/status/workspace-links.js +29 -0
- package/dist/command/cli/try-command.d.ts +96 -0
- package/dist/command/cli/try-command.js +362 -0
- package/dist/command/cli/tutorial.d.ts +0 -100
- package/dist/command/cli/tutorial.js +0 -181
- package/dist/command/cli/verify-results.d.ts +19 -0
- package/dist/command/cli/verify-results.js +29 -0
- package/dist/command/cli-onboarding.d.ts +0 -11
- package/dist/command/cli-onboarding.js +0 -15
- package/dist/command/cli.d.ts +20 -0
- package/dist/command/cli.js +130 -104
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
- package/dist/command/daemon/binding/daemon-status-probe.js +1 -0
- package/dist/command/daemon/daemon-resilience.js +4 -4
- package/dist/command/daemon/daemon-resolve.d.ts +23 -1
- package/dist/command/daemon/daemon-resolve.js +79 -9
- package/dist/command/setup/agent-writer.d.ts +1 -1
- package/dist/command/setup/agent-writer.js +3 -2
- package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
- package/dist/command/setup/bringup/bridge-port.js +13 -3
- package/dist/command/setup/bringup/connect-lease.d.ts +1 -0
- package/dist/command/setup/bringup/connect-lease.js +1 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +8 -0
- package/dist/command/setup/bringup/dev-server-wait.js +19 -0
- package/dist/command/setup/bringup/ensure-daemon.js +9 -2
- package/dist/command/setup/init/agent-restart.d.ts +23 -0
- package/dist/command/setup/init/agent-restart.js +28 -0
- package/dist/command/setup/init/init-port.d.ts +14 -1
- package/dist/command/setup/init/init-port.js +32 -2
- package/dist/command/setup/init/init-runtime.d.ts +22 -2
- package/dist/command/setup/init/init-runtime.js +114 -38
- package/dist/command/setup/node-effects.d.ts +2 -0
- package/dist/command/setup/node-effects.js +38 -9
- package/dist/command/setup/probe/existing-app.d.ts +12 -0
- package/dist/command/setup/probe/existing-app.js +14 -0
- package/dist/command/setup/remaining-steps.js +2 -1
- package/dist/command/setup/run-setup.d.ts +34 -9
- package/dist/command/setup/run-setup.js +65 -51
- package/dist/command/setup/setup-command.d.ts +25 -1
- package/dist/command/setup/setup-command.js +59 -4
- package/dist/command/setup/setup-mcp.js +25 -2
- package/dist/command/setup/tauri-signing.d.ts +19 -0
- package/dist/command/setup/tauri-signing.js +79 -0
- package/dist/command/setup/terminal/listeners.d.ts +10 -1
- package/dist/command/setup/terminal/listeners.js +37 -6
- package/dist/command/update/update-nudge.d.ts +9 -0
- package/dist/command/update/update-nudge.js +1 -1
- package/dist/command/update/updater.js +11 -15
- package/dist/command/version/version-nudge.js +3 -0
- package/dist/command/version/version-skew.d.ts +20 -1
- package/dist/command/version/version-skew.js +49 -4
- package/dist/features/crawl/crawl-tools.js +4 -0
- package/dist/features/crawl/crawl.d.ts +6 -0
- package/dist/features/crawl/crawl.js +23 -2
- package/dist/features/exhaust/ledger.d.ts +21 -14
- package/dist/features/exhaust/ledger.js +35 -9
- package/dist/features/harness/app-findings.d.ts +2 -0
- package/dist/features/harness/app-findings.js +52 -0
- package/dist/features/harness/drive-report.d.ts +61 -1
- package/dist/features/harness/drive-report.js +394 -26
- package/dist/features/harness/drive-runs.d.ts +120 -0
- package/dist/features/harness/drive-runs.js +289 -0
- package/dist/features/harness/drivers.d.ts +10 -14
- package/dist/features/harness/drivers.js +10 -14
- package/dist/features/harness/explore/console-pages.d.ts +15 -0
- package/dist/features/harness/explore/console-pages.js +146 -0
- package/dist/features/harness/explore/disparity.d.ts +19 -0
- package/dist/features/harness/explore/disparity.js +71 -0
- package/dist/features/harness/explore/fake-app.d.ts +16 -0
- package/dist/features/harness/explore/fake-app.js +49 -0
- package/dist/features/harness/explore/goal-search.d.ts +26 -0
- package/dist/features/harness/explore/goal-search.js +144 -0
- package/dist/features/harness/explore/page-tree.d.ts +49 -0
- package/dist/features/harness/explore/page-tree.js +148 -0
- package/dist/features/harness/explore/site-map.d.ts +34 -0
- package/dist/features/harness/explore/site-map.js +150 -0
- package/dist/features/harness/fill-values.d.ts +3 -73
- package/dist/features/harness/fill-values.js +3 -169
- package/dist/features/harness/goals.d.ts +82 -0
- package/dist/features/harness/goals.js +330 -0
- package/dist/features/harness/harness.d.ts +51 -1
- package/dist/features/harness/harness.js +207 -6
- package/dist/features/harness/platform/drive-target.d.ts +30 -0
- package/dist/features/harness/platform/drive-target.js +81 -0
- package/dist/features/harness/platform/local-apps.d.ts +127 -0
- package/dist/features/harness/platform/local-apps.js +280 -0
- package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
- package/dist/features/harness/platform/persona-credentials.js +69 -0
- package/dist/features/harness/platform/platform-drives.d.ts +61 -0
- package/dist/features/harness/platform/platform-drives.js +114 -0
- package/dist/features/harness/platform/refusal.d.ts +52 -0
- package/dist/features/harness/platform/refusal.js +71 -0
- package/dist/features/harness/platform/remote-drive.d.ts +186 -0
- package/dist/features/harness/platform/remote-drive.js +378 -0
- package/dist/features/harness/platform/script.d.ts +94 -0
- package/dist/features/harness/platform/script.js +260 -0
- package/dist/features/harness/platform/server-driver.d.ts +54 -0
- package/dist/features/harness/platform/server-driver.js +182 -0
- package/dist/features/harness/platform-config.d.ts +41 -1
- package/dist/features/harness/platform-config.js +106 -9
- package/dist/features/harness/route-guess.d.ts +13 -0
- package/dist/features/harness/route-guess.js +49 -0
- package/dist/features/harness/script/app-controls.d.ts +5 -0
- package/dist/features/harness/script/app-controls.js +40 -0
- package/dist/features/harness/script/lane-ids.d.ts +14 -0
- package/dist/features/harness/script/lane-ids.js +16 -0
- package/dist/features/harness/script/script-run.d.ts +70 -0
- package/dist/features/harness/script/script-run.js +606 -0
- package/dist/features/license/license.d.ts +2 -2
- package/dist/features/visual/step-shots.d.ts +31 -0
- package/dist/features/visual/step-shots.js +231 -0
- package/dist/features/visual/visual-tools.d.ts +10 -1
- package/dist/features/visual/visual-tools.js +62 -17
- package/dist/hooks/client-directory.d.ts +18 -0
- package/dist/hooks/client-directory.js +57 -0
- package/dist/hooks/coding-agents.d.ts +34 -0
- package/dist/hooks/coding-agents.js +117 -0
- package/dist/hooks/driven-by.d.ts +23 -0
- package/dist/hooks/driven-by.js +56 -0
- package/dist/hooks/hook-bus.d.ts +4 -0
- package/dist/hooks/hook-bus.js +18 -0
- package/dist/hooks/hook-commands.js +15 -4
- package/dist/hooks/tool-log.d.ts +9 -0
- package/dist/hooks/tool-log.js +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +80 -243
- package/dist/judgement/capsule/capsule-store.d.ts +2 -2
- package/dist/judgement/capsule/causal-summary.d.ts +15 -1
- package/dist/judgement/capsule/causal-summary.js +33 -0
- package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
- package/dist/judgement/runs/artifact/build-verification-run.js +25 -3
- package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
- package/dist/judgement/runs/artifact/gap-summary.js +12 -3
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +1 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +37 -11
- package/dist/judgement/runs/artifact/run-store.d.ts +14 -2
- package/dist/judgement/runs/artifact/run-store.js +195 -3
- package/dist/judgement/runs/context-tools.js +59 -1
- package/dist/judgement/runs/default-run-id.d.ts +3 -0
- package/dist/judgement/runs/default-run-id.js +16 -0
- package/dist/judgement/runs/drive-run.d.ts +71 -1
- package/dist/judgement/runs/drive-run.js +317 -5
- package/dist/judgement/runs/repair-prompt.js +1 -1
- package/dist/judgement/runs/resume.d.ts +44 -0
- package/dist/judgement/runs/resume.js +137 -0
- package/dist/judgement/runs/reticle-runner.js +13 -3
- package/dist/judgement/runs/runner-port.d.ts +5 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +5 -2
- package/dist/judgement/runs/verification-sync.js +40 -18
- package/dist/language/flows/change/gate.d.ts +10 -0
- package/dist/language/flows/change/gate.js +22 -0
- package/dist/language/flows/decision.js +36 -1
- package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
- package/dist/language/flows/fields/flow-secret-field.js +25 -2
- package/dist/language/flows/flow-anchor.d.ts +61 -10
- package/dist/language/flows/flow-anchor.js +98 -6
- package/dist/language/flows/flow-cross-step.d.ts +15 -0
- package/dist/language/flows/flow-cross-step.js +21 -0
- package/dist/language/flows/flow-intent.d.ts +1 -0
- package/dist/language/flows/flow-journey.d.ts +6 -0
- package/dist/language/flows/flow-journey.js +20 -0
- package/dist/language/flows/flow-learning.d.ts +4 -0
- package/dist/language/flows/flow-learning.js +45 -14
- package/dist/language/flows/flow-mutate-tools.js +5 -3
- package/dist/language/flows/flow-replay-run.d.ts +23 -9
- package/dist/language/flows/flow-replay-run.js +115 -110
- package/dist/language/flows/flow-replay-types.d.ts +1 -1
- package/dist/language/flows/flow-replay.d.ts +40 -1
- package/dist/language/flows/flow-replay.js +110 -22
- package/dist/language/flows/flow-result.d.ts +22 -1
- package/dist/language/flows/flow-result.js +47 -2
- package/dist/language/flows/flow-scope.js +5 -1
- package/dist/language/flows/flow-step-runners.d.ts +22 -2
- package/dist/language/flows/flow-step-runners.js +247 -47
- package/dist/language/flows/flow-store-for-session.d.ts +2 -2
- package/dist/language/flows/flow-store-for-session.js +7 -3
- package/dist/language/flows/flow-tools.d.ts +10 -0
- package/dist/language/flows/flow-tools.js +112 -89
- package/dist/language/flows/flows.d.ts +16 -1
- package/dist/language/flows/flows.js +74 -12
- package/dist/language/flows/heal-run.js +6 -4
- package/dist/language/flows/narrow-write.d.ts +7 -0
- package/dist/language/flows/narrow-write.js +36 -1
- package/dist/language/flows/recording/flake.d.ts +2 -2
- package/dist/language/flows/recording/recording-backtrack.js +14 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +58 -4
- package/dist/language/flows/recording/tape/recordings.js +95 -13
- package/dist/language/flows/replay-from-hud.d.ts +20 -0
- package/dist/language/flows/replay-from-hud.js +51 -0
- package/dist/language/flows/replay.d.ts +9 -5
- package/dist/language/flows/replay.js +56 -15
- package/dist/language/flows/single-replay.d.ts +7 -0
- package/dist/language/flows/single-replay.js +26 -0
- package/dist/language/flows/suite/suite-flakes.js +2 -0
- package/dist/language/flows/suite-selection.d.ts +2 -15
- package/dist/language/flows/suite-selection.js +4 -3
- package/dist/language/flows/verify-change-tools.d.ts +1 -0
- package/dist/language/flows/verify-change-tools.js +202 -191
- package/dist/launch-chromium.d.ts +27 -5
- package/dist/launch-chromium.js +38 -8
- package/dist/memory/cloud/cloud-config.d.ts +22 -1
- package/dist/memory/cloud/cloud-config.js +34 -5
- package/dist/memory/cloud/cloud-sync.d.ts +17 -14
- package/dist/memory/cloud/cloud-sync.js +15 -4
- package/dist/memory/cloud/harness-config.d.ts +75 -1
- package/dist/memory/cloud/harness-config.js +115 -24
- package/dist/memory/cloud/hud-notices-source.d.ts +21 -0
- package/dist/memory/cloud/hud-notices-source.js +128 -0
- package/dist/memory/cloud/memory-scope.d.ts +161 -0
- package/dist/memory/cloud/memory-scope.js +227 -0
- package/dist/memory/cloud/sync-cycle.d.ts +44 -7
- package/dist/memory/cloud/sync-cycle.js +129 -15
- package/dist/memory/cloud/sync-daemon.d.ts +11 -0
- package/dist/memory/cloud/sync-daemon.js +121 -9
- package/dist/memory/cloud/sync-disk.d.ts +1 -1
- package/dist/memory/cloud/sync-disk.js +31 -10
- package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
- package/dist/memory/cloud/sync-screenshots.js +105 -0
- package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
- package/dist/memory/cloud/unsynced-roots.js +166 -0
- package/dist/memory/impact/account-watch.d.ts +34 -0
- package/dist/memory/impact/account-watch.js +59 -0
- package/dist/memory/impact/impact-recorder.d.ts +8 -0
- package/dist/memory/impact/impact-recorder.js +12 -2
- package/dist/memory/impact/impact-store.d.ts +17 -4
- package/dist/memory/impact/impact-store.js +169 -63
- package/dist/memory/intent/inline-intent.d.ts +1 -0
- package/dist/memory/intent/intent-shard-store.js +16 -4
- package/dist/memory/intent/intent-shard.d.ts +14 -2
- package/dist/memory/intent/intent-store.d.ts +1 -0
- package/dist/memory/intent/intent-tools.js +122 -4
- package/dist/memory/intent/open-intents.d.ts +6 -0
- package/dist/memory/intent/open-intents.js +10 -1
- package/dist/memory/intent/settled-words.d.ts +24 -0
- package/dist/memory/intent/settled-words.js +130 -0
- package/dist/memory/journal/drive-flow.d.ts +16 -0
- package/dist/memory/journal/drive-flow.js +34 -2
- package/dist/memory/journal/drive-run-flush.d.ts +2 -0
- package/dist/memory/journal/drive-run-flush.js +20 -5
- package/dist/memory/journal/envelope.d.ts +8 -8
- package/dist/memory/journal/journal-recorder.js +52 -3
- package/dist/memory/journal/on-disk/retention.d.ts +8 -0
- package/dist/memory/journal/on-disk/retention.js +32 -2
- package/dist/memory/journal/on-disk/startup-maintenance.js +3 -1
- package/dist/memory/journal/on-disk/workspace-tiers.js +9 -4
- package/dist/memory/journal/session-end.d.ts +15 -1
- package/dist/memory/journal/session-end.js +42 -9
- package/dist/memory/project/artifact-root-resolver.d.ts +26 -1
- package/dist/memory/project/artifact-root-resolver.js +125 -16
- package/dist/memory/project/artifact-root.d.ts +46 -8
- package/dist/memory/project/artifact-root.js +89 -10
- package/dist/memory/project/dir/reticle-dir.d.ts +18 -2
- package/dist/memory/project/dir/reticle-dir.js +26 -3
- package/dist/memory/project/link-directory.d.ts +15 -0
- package/dist/memory/project/link-directory.js +44 -0
- package/dist/memory/project/project-tools.js +22 -3
- package/dist/memory/project/root-claims.d.ts +6 -0
- package/dist/memory/project/root-claims.js +39 -0
- package/dist/memory/project/session-root.d.ts +26 -1
- package/dist/memory/project/session-root.js +49 -5
- package/dist/memory/project/sync-status.d.ts +85 -0
- package/dist/memory/project/sync-status.js +224 -0
- package/dist/memory/recall/memory-tools.js +47 -2
- package/dist/memory/recall/platform-memory.d.ts +34 -0
- package/dist/memory/recall/platform-memory.js +139 -0
- package/dist/memory/recall/project-memory.d.ts +19 -0
- package/dist/memory/recall/project-memory.js +80 -13
- package/dist/portal/bridge/bridge.d.ts +9 -3
- package/dist/portal/bridge/bridge.js +82 -22
- package/dist/portal/bridge/harness-request.d.ts +23 -0
- package/dist/portal/bridge/harness-request.js +27 -0
- package/dist/portal/bridge/pairing-token.d.ts +9 -0
- package/dist/portal/bridge/pairing-token.js +17 -0
- package/dist/portal/bridge/wrong-path.d.ts +17 -0
- package/dist/portal/bridge/wrong-path.js +35 -0
- package/dist/portal/input/network-mock-tools.js +11 -2
- package/dist/portal/input/real-input.d.ts +83 -10
- package/dist/portal/input/real-input.js +218 -30
- package/dist/portal/input/viewport-tools.js +3 -3
- package/dist/portal/pool/browser-pool.d.ts +70 -9
- package/dist/portal/pool/browser-pool.js +190 -37
- package/dist/portal/pool/context-permissions.d.ts +33 -0
- package/dist/portal/pool/context-permissions.js +33 -0
- package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
- package/dist/portal/pool/lease-marker-stamp.js +49 -0
- package/dist/portal/pool/playwright-launcher.d.ts +35 -0
- package/dist/portal/pool/playwright-launcher.js +64 -3
- package/dist/portal/pool/pool-contract.d.ts +28 -5
- package/dist/portal/session/agent-link.d.ts +8 -0
- package/dist/portal/session/agent-link.js +17 -0
- package/dist/portal/session/command-payload.d.ts +8 -0
- package/dist/portal/session/command-payload.js +24 -0
- package/dist/portal/session/daemon-answers.d.ts +3 -3
- package/dist/portal/session/daemon-answers.js +4 -2
- package/dist/portal/session/dev-server/dev-command.d.ts +21 -0
- package/dist/portal/session/dev-server/dev-command.js +87 -12
- package/dist/portal/session/facts/departure-note.d.ts +22 -0
- package/dist/portal/session/facts/departure-note.js +47 -0
- package/dist/portal/session/facts/observed-state.d.ts +16 -18
- package/dist/portal/session/facts/observed-state.js +46 -3
- package/dist/portal/session/facts/session-replaced.d.ts +5 -0
- package/dist/portal/session/facts/session-replaced.js +5 -0
- package/dist/portal/session/first-run-wiring.d.ts +42 -0
- package/dist/portal/session/first-run-wiring.js +132 -0
- package/dist/portal/session/human/live-control.d.ts +9 -2
- package/dist/portal/session/human/live-control.js +12 -3
- package/dist/portal/session/human/notes-ledger.d.ts +20 -0
- package/dist/portal/session/human/notes-ledger.js +55 -0
- package/dist/portal/session/human/review-store.d.ts +12 -0
- package/dist/portal/session/human/review-store.js +18 -10
- package/dist/portal/session/live-control-tools.d.ts +0 -13
- package/dist/portal/session/live-control-tools.js +107 -13
- package/dist/portal/session/narration-args.d.ts +5 -0
- package/dist/portal/session/narration-args.js +17 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +8 -1
- package/dist/portal/session/no-session-diagnosis.js +46 -9
- package/dist/portal/session/no-session-next-action.d.ts +31 -0
- package/dist/portal/session/no-session-next-action.js +159 -60
- package/dist/portal/session/no-session-watch.d.ts +14 -1
- package/dist/portal/session/no-session-watch.js +227 -17
- package/dist/portal/session/presence/session-lease.js +1 -1
- package/dist/portal/session/presence/session-mirror.js +3 -0
- package/dist/portal/session/recorded-gaps.d.ts +97 -0
- package/dist/portal/session/recorded-gaps.js +251 -0
- package/dist/portal/session/serving-directory.d.ts +27 -0
- package/dist/portal/session/serving-directory.js +45 -0
- package/dist/portal/session/session-health.d.ts +18 -0
- package/dist/portal/session/session-health.js +9 -1
- package/dist/portal/session/session-info.d.ts +8 -1
- package/dist/portal/session/session-info.js +8 -1
- package/dist/portal/session/session-manager.d.ts +39 -0
- package/dist/portal/session/session-manager.js +98 -11
- package/dist/portal/session/session-reaper.js +4 -1
- package/dist/portal/session/session-successor.d.ts +13 -0
- package/dist/portal/session/session-successor.js +1 -1
- package/dist/portal/session/session-tools.js +26 -3
- package/dist/portal/session/session.d.ts +22 -30
- package/dist/portal/session/session.js +81 -79
- package/dist/portal/session/timing/command-timeout.js +25 -1
- package/dist/portal/session/timing/wall-clock.d.ts +1 -1
- package/dist/portal/session/timing/wall-clock.js +1 -1
- package/dist/portal/session/zero-install-first-call.d.ts +31 -0
- package/dist/portal/session/zero-install-first-call.js +88 -0
- package/dist/start-options.d.ts +12 -0
- package/dist/start-wiring.d.ts +63 -0
- package/dist/start-wiring.js +169 -0
- package/dist/status-payload.d.ts +23 -1
- package/dist/status-payload.js +36 -1
- package/dist/surface/http-server.js +18 -2
- package/dist/surface/mcp/attach-memory.d.ts +1 -0
- package/dist/surface/mcp/attach-memory.js +12 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +9 -0
- package/dist/surface/mcp/mcp-proxy.js +65 -13
- package/dist/surface/mcp/mcp.d.ts +34 -6
- package/dist/surface/mcp/mcp.js +61 -17
- package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
- package/dist/surface/mcp/proxy/drive-calls.js +27 -0
- package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
- package/dist/surface/mcp/server-instructions.d.ts +4 -2
- package/dist/surface/mcp/server-instructions.js +32 -14
- package/dist/surface/mcp/shared-params.js +2 -2
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +1 -0
- package/dist/surface/tools/account-tool.d.ts +22 -0
- package/dist/surface/tools/account-tool.js +157 -0
- package/dist/surface/tools/act/act-danger.d.ts +11 -0
- package/dist/surface/tools/act/act-danger.js +38 -1
- package/dist/surface/tools/act/act-observation.d.ts +33 -0
- package/dist/surface/tools/act/act-observation.js +57 -0
- package/dist/surface/tools/act/act-preflight.js +25 -2
- package/dist/surface/tools/act/act-target.js +8 -3
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +8 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.js +56 -2
- package/dist/surface/tools/act/resolve-target.d.ts +1 -1
- package/dist/surface/tools/act/resolve-target.js +18 -3
- package/dist/surface/tools/act/screen-request.d.ts +34 -0
- package/dist/surface/tools/act/screen-request.js +113 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
- package/dist/surface/tools/act/sequence-grade.js +13 -2
- package/dist/surface/tools/act/settle-in-flight.js +25 -3
- package/dist/surface/tools/act-sequence-tool.js +138 -29
- package/dist/surface/tools/act-tools.d.ts +0 -9
- package/dist/surface/tools/act-tools.js +71 -41
- package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
- package/dist/surface/tools/agent/app-notes-learning.js +80 -0
- package/dist/surface/tools/agent/app-notes.d.ts +4 -0
- package/dist/surface/tools/agent/app-notes.js +48 -0
- package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
- package/dist/surface/tools/agent/build-staleness.js +62 -0
- package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
- package/dist/surface/tools/agent/compact-envelope.js +374 -0
- package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
- package/dist/surface/tools/agent/for-the-agent.js +44 -0
- package/dist/surface/tools/agent/notices.d.ts +50 -0
- package/dist/surface/tools/agent/notices.js +152 -0
- package/dist/surface/tools/agent/reteach.d.ts +11 -0
- package/dist/surface/tools/agent/reteach.js +50 -0
- package/dist/surface/tools/args/alias-args.d.ts +5 -0
- package/dist/surface/tools/args/alias-args.js +10 -0
- package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
- package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
- package/dist/surface/tools/assert/assert-source.d.ts +15 -0
- package/dist/surface/tools/assert/assert-source.js +40 -0
- package/dist/surface/tools/assert/assert-verdict.d.ts +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +42 -17
- package/dist/surface/tools/browser-tools.js +51 -2
- package/dist/surface/tools/chat-drives.d.ts +101 -0
- package/dist/surface/tools/chat-drives.js +409 -0
- package/dist/surface/tools/coverage-tools.d.ts +2 -10
- package/dist/surface/tools/coverage-tools.js +2 -53
- package/dist/surface/tools/dynamic-tools.js +29 -1
- package/dist/surface/tools/empty-session-list.d.ts +51 -0
- package/dist/surface/tools/empty-session-list.js +120 -0
- package/dist/surface/tools/error-recovery.d.ts +14 -0
- package/dist/surface/tools/error-recovery.js +105 -8
- package/dist/surface/tools/explore-tools.d.ts +31 -1
- package/dist/surface/tools/explore-tools.js +438 -66
- package/dist/surface/tools/feedback-tools.d.ts +2 -3
- package/dist/surface/tools/feedback-tools.js +18 -3
- package/dist/surface/tools/gap-novelty.d.ts +3 -1
- package/dist/surface/tools/gap-novelty.js +8 -1
- package/dist/surface/tools/harness-explore.d.ts +118 -57
- package/dist/surface/tools/harness-explore.js +344 -292
- package/dist/surface/tools/harness-gate.d.ts +88 -0
- package/dist/surface/tools/harness-gate.js +235 -0
- package/dist/surface/tools/harness-hunt.d.ts +43 -0
- package/dist/surface/tools/harness-hunt.js +215 -0
- package/dist/surface/tools/harness-plan.d.ts +17 -0
- package/dist/surface/tools/harness-plan.js +38 -0
- package/dist/surface/tools/harness-script.d.ts +30 -0
- package/dist/surface/tools/harness-script.js +560 -0
- package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
- package/dist/surface/tools/harness-tab-guard.js +27 -0
- package/dist/surface/tools/harness-toolset.d.ts +22 -1
- package/dist/surface/tools/harness-toolset.js +101 -7
- package/dist/surface/tools/human-notes.d.ts +16 -0
- package/dist/surface/tools/human-notes.js +27 -0
- package/dist/surface/tools/invoke-tool.d.ts +5 -2
- package/dist/surface/tools/invoke-tool.js +263 -85
- package/dist/surface/tools/lease-hint.js +26 -5
- package/dist/surface/tools/lease-permissions.d.ts +33 -0
- package/dist/surface/tools/lease-permissions.js +73 -0
- package/dist/surface/tools/lease-readiness.d.ts +38 -0
- package/dist/surface/tools/lease-readiness.js +57 -0
- package/dist/surface/tools/lease-seed.d.ts +49 -0
- package/dist/surface/tools/lease-seed.js +167 -0
- package/dist/surface/tools/lease-tools.d.ts +13 -24
- package/dist/surface/tools/lease-tools.js +146 -197
- package/dist/surface/tools/live-call-text.js +4 -4
- package/dist/surface/tools/merge-tools.js +42 -2
- package/dist/surface/tools/merged-name-redirect.js +4 -0
- package/dist/surface/tools/next-step.d.ts +29 -0
- package/dist/surface/tools/next-step.js +214 -0
- package/dist/surface/tools/observe-tools.js +45 -9
- package/dist/surface/tools/platform-moment.d.ts +72 -0
- package/dist/surface/tools/platform-moment.js +296 -0
- package/dist/surface/tools/query-hint-schema.d.ts +7 -0
- package/dist/surface/tools/query-hint-schema.js +10 -0
- package/dist/surface/tools/read-tools.js +71 -14
- package/dist/surface/tools/real-input-attempt.js +96 -13
- package/dist/surface/tools/reticle-result.d.ts +120 -0
- package/dist/surface/tools/reticle-result.js +364 -0
- package/dist/surface/tools/tool-kit.d.ts +83 -3
- package/dist/surface/tools/tool-kit.js +77 -7
- package/dist/surface/tools/tools.js +43 -59
- package/dist/telemetry/account-envelope.d.ts +7 -0
- package/dist/telemetry/account-envelope.js +61 -0
- package/dist/telemetry/agent-client.d.ts +15 -0
- package/dist/telemetry/agent-client.js +57 -0
- package/dist/telemetry/app-instrumented.js +9 -2
- package/dist/telemetry/argument-shape.d.ts +7 -0
- package/dist/telemetry/argument-shape.js +86 -1
- package/dist/telemetry/automation-hint.d.ts +2 -3
- package/dist/telemetry/automation-hint.js +33 -8
- package/dist/telemetry/cli-telemetry.js +7 -2
- package/dist/telemetry/daemon-telemetry.js +3 -0
- package/dist/telemetry/error-fingerprint.d.ts +10 -1
- package/dist/telemetry/error-fingerprint.js +61 -5
- package/dist/telemetry/feedback-cli.d.ts +0 -13
- package/dist/telemetry/feedback-cli.js +0 -77
- package/dist/telemetry/feedback.d.ts +13 -0
- package/dist/telemetry/feedback.js +183 -17
- package/dist/telemetry/harness-run.d.ts +18 -0
- package/dist/telemetry/harness-run.js +80 -0
- package/dist/telemetry/hud-events.d.ts +16 -0
- package/dist/telemetry/hud-events.js +27 -0
- package/dist/telemetry/hud-metrics.d.ts +29 -6
- package/dist/telemetry/hud-metrics.js +116 -25
- package/dist/telemetry/init-telemetry.js +2 -2
- package/dist/telemetry/install-source.d.ts +13 -25
- package/dist/telemetry/install-source.js +44 -3
- package/dist/telemetry/mcp-connection.d.ts +0 -14
- package/dist/telemetry/mcp-connection.js +5 -1
- package/dist/telemetry/onboarding-firsts.d.ts +32 -0
- package/dist/telemetry/onboarding-firsts.js +81 -6
- package/dist/telemetry/platform-linked.d.ts +5 -0
- package/dist/telemetry/platform-linked.js +32 -0
- package/dist/telemetry/project-profile.js +5 -3
- package/dist/telemetry/proxy-telemetry.js +4 -1
- package/dist/telemetry/session-metrics.d.ts +15 -4
- package/dist/telemetry/session-metrics.js +88 -52
- package/dist/telemetry/telemetry.d.ts +38 -2
- package/dist/telemetry/telemetry.js +144 -34
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +13 -4
- package/dist/wire-journal.d.ts +2 -2
- package/dist/wire-journal.js +20 -6
- package/docs/README.md +3 -3
- package/docs/actions.mdx +6 -3
- package/docs/adapters.md +1 -1
- package/docs/agent-cheatsheet.md +25 -28
- package/docs/architecture.md +5 -5
- package/docs/autodrive.md +33 -28
- package/docs/benchmarks.md +5 -5
- package/docs/best-practices.mdx +27 -9
- package/docs/capabilities.mdx +14 -16
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +54 -75
- package/docs/cli/doctor.mdx +14 -3
- package/docs/cli/drive.mdx +20 -14
- package/docs/cli/feedback.mdx +3 -3
- package/docs/cli/gate.mdx +7 -2
- package/docs/cli/help.mdx +50 -55
- package/docs/cli/init.mdx +43 -28
- package/docs/cli/license.mdx +12 -8
- package/docs/cli/mcp.mdx +4 -4
- package/docs/cli/open.mdx +3 -3
- package/docs/cli/report.mdx +4 -2
- package/docs/cli/restart.mdx +2 -2
- package/docs/cli/rollback.mdx +14 -8
- package/docs/cli/serve.mdx +4 -4
- package/docs/cli/setup.mdx +23 -0
- package/docs/cli/status.mdx +18 -5
- package/docs/cli/stop.mdx +67 -8
- package/docs/cli/try.mdx +62 -0
- package/docs/cli/update.mdx +4 -3
- package/docs/cli/verify.mdx +17 -6
- package/docs/cli/version.mdx +5 -4
- package/docs/cli.mdx +111 -42
- package/docs/debugging.md +3 -3
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +9 -3
- package/docs/enterprise.md +11 -10
- package/docs/environment.mdx +7 -8
- package/docs/faq.mdx +3 -3
- package/docs/first-drive.md +5 -4
- package/docs/flows.md +35 -27
- package/docs/for-agents.md +4 -5
- package/docs/frameworks.mdx +32 -21
- package/docs/getting-started.md +65 -61
- package/docs/human-control.md +16 -16
- package/docs/index.mdx +15 -42
- package/docs/install-agentic.mdx +22 -37
- package/docs/install-manual.mdx +23 -13
- package/docs/instrumentation.mdx +5 -3
- package/docs/integration-patterns.md +7 -7
- package/docs/license-activation.mdx +1 -1
- package/docs/multi-agent-testing.md +24 -6
- package/docs/packages/babel-plugin.mdx +4 -2
- package/docs/packages/browser.mdx +16 -10
- package/docs/packages/core.mdx +12 -5
- package/docs/packages/electron.mdx +3 -2
- package/docs/packages/engine.mdx +1 -1
- package/docs/packages/eslint-plugin.mdx +18 -12
- package/docs/packages/next.mdx +8 -7
- package/docs/packages/open-verification.mdx +1 -1
- package/docs/packages/server.mdx +5 -5
- package/docs/packages/tauri.mdx +9 -9
- package/docs/packages/test.mdx +4 -10
- package/docs/packages/vite-plugin.mdx +32 -4
- package/docs/packages.mdx +11 -15
- package/docs/platform-integration.md +6 -5
- package/docs/predicates.mdx +40 -24
- package/docs/quickstart.mdx +69 -30
- package/docs/required-checks.md +8 -7
- package/docs/reticle-manual.md +15 -5
- package/docs/skill-file.mdx +17 -79
- package/docs/skill-setup.md +9 -9
- package/docs/skill.md +200 -0
- package/docs/system-map.md +7 -7
- package/docs/telemetry.md +23 -25
- package/docs/testing.md +4 -4
- package/docs/token-efficiency.md +12 -12
- package/docs/tools/act-and-wait.mdx +9 -2
- package/docs/tools/act-sequence.mdx +6 -7
- package/docs/tools/act.mdx +2 -2
- package/docs/tools/assert.mdx +18 -17
- package/docs/tools/console.mdx +9 -9
- package/docs/tools/inspect.mdx +16 -7
- package/docs/tools/look.mdx +24 -0
- package/docs/tools/navigate.mdx +5 -3
- package/docs/tools/network.mdx +2 -2
- package/docs/tools/observe.mdx +6 -6
- package/docs/tools/overview.mdx +34 -33
- package/docs/tools/query.mdx +5 -5
- package/docs/tools/session-and-feedback.mdx +22 -13
- package/docs/tools/sessions.mdx +8 -8
- package/docs/tools/snapshot.mdx +6 -5
- package/docs/tools/state.mdx +9 -9
- package/docs/tools/tools-and-run.mdx +26 -24
- package/docs/tools/verify.mdx +28 -0
- package/docs/tools/wait-for.mdx +8 -4
- package/docs/troubleshooting.mdx +21 -6
- package/docs/usage.md +55 -46
- package/docs/use-cases.mdx +3 -3
- package/docs/vs-chrome-devtools-mcp.mdx +9 -6
- package/docs/vs-playwright-mcp.mdx +3 -1
- package/docs/what-is-recorded.md +17 -5
- package/docs/what-is-reticle.mdx +5 -3
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/cli/demo/demo-page.d.ts +0 -78
- package/dist/command/cli/demo/demo-page.js +0 -241
- package/dist/command/cli/demo/demo-run.d.ts +0 -38
- package/dist/command/cli/demo/demo-run.js +0 -138
- package/dist/features/harness/driver.d.ts +0 -50
- package/dist/features/harness/driver.js +0 -236
- package/dist/features/harness/jev-driver.d.ts +0 -188
- package/dist/features/harness/jev-driver.js +0 -678
- package/dist/features/harness/openai-driver.d.ts +0 -58
- package/dist/features/harness/openai-driver.js +0 -206
- package/dist/judgement/hunt/hunt-report.d.ts +0 -49
- package/dist/judgement/hunt/hunt-report.js +0 -84
- package/dist/language/flows/buddy-status.d.ts +0 -25
- package/dist/language/flows/buddy-status.js +0 -32
- package/dist/language/flows/change/watch-batcher.d.ts +0 -18
- package/dist/language/flows/change/watch-batcher.js +0 -19
- package/dist/memory/cloud/harness-offer.d.ts +0 -44
- package/dist/memory/cloud/harness-offer.js +0 -145
- package/dist/memory/project/dir/fill-value-store.d.ts +0 -32
- package/dist/memory/project/dir/fill-value-store.js +0 -88
- package/dist/surface/tools/feature-capture.d.ts +0 -151
- package/dist/surface/tools/feature-capture.js +0 -235
- package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
- package/dist/surface/tools/tool-hit-rate.js +0 -90
- package/dist/telemetry/harness-drive.d.ts +0 -4
- package/dist/telemetry/harness-drive.js +0 -35
- package/dist/telemetry/identify.d.ts +0 -22
- package/dist/telemetry/identify.js +0 -73
- package/docs/cli/capsules.mdx +0 -70
- package/docs/cli/hunt.mdx +0 -73
- package/docs/cli/identify.mdx +0 -76
- package/docs/cli/kill.mdx +0 -91
- package/docs/cli/watch.mdx +0 -62
- package/docs/local-registry.md +0 -143
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ActionType, DANGEROUS_ACTION_CONFIRM_ARG, NATIVE_INPUT_ARG, isDangerousActionText, } from '@reticlehq/core';
|
|
1
|
+
import { ActionType, DANGEROUS_ACTION_CONFIRM_ARG, NATIVE_INPUT_ARG, isDangerousActionText, outwardActionOf, outwardRefusal, } from '@reticlehq/core';
|
|
2
|
+
import { currentDrivenBy, harnessOutwardAllowed } from '../../../hooks/driven-by.js';
|
|
2
3
|
import { asRecord, asString } from '@reticlehq/core';
|
|
3
4
|
/**
|
|
4
5
|
* Refuse to drive a money-moving or destructive control by accident.
|
|
@@ -29,9 +30,21 @@ function descriptorRole(value) {
|
|
|
29
30
|
function isDestructiveDescriptor(value) {
|
|
30
31
|
return isDangerousActionText(descriptorText(value), descriptorRole(value));
|
|
31
32
|
}
|
|
33
|
+
/** A Harness call presses an outward control only when its journey asked (`harnessOutwardAllowed`). */
|
|
34
|
+
function assertOutwardAllowed(...descriptors) {
|
|
35
|
+
const allowed = harnessOutwardAllowed();
|
|
36
|
+
if (allowed === undefined)
|
|
37
|
+
return;
|
|
38
|
+
for (const descriptor of descriptors) {
|
|
39
|
+
const kind = outwardActionOf(descriptorText(descriptor), descriptorRole(descriptor));
|
|
40
|
+
if (kind !== undefined && !allowed.includes(kind))
|
|
41
|
+
throw new Error(outwardRefusal(kind));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
32
44
|
export function assertNotDestructive(action, innerArgs, inspected) {
|
|
33
45
|
if (action !== ActionType.CLICK && action !== ActionType.DBLCLICK)
|
|
34
46
|
return;
|
|
47
|
+
assertOutwardAllowed(inspected);
|
|
35
48
|
if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
|
|
36
49
|
return;
|
|
37
50
|
if (!isDestructiveDescriptor(inspected))
|
|
@@ -44,6 +57,7 @@ export function assertNotDestructive(action, innerArgs, inspected) {
|
|
|
44
57
|
* option dragged onto Save is not a payment.
|
|
45
58
|
*/
|
|
46
59
|
export function assertDragNotDestructive(innerArgs, from, to) {
|
|
60
|
+
assertOutwardAllowed(from, to);
|
|
47
61
|
if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
|
|
48
62
|
return;
|
|
49
63
|
if (!isDestructiveDescriptor(from) && !isDestructiveDescriptor(to))
|
|
@@ -67,3 +81,26 @@ export function assertNativeInputSupported(innerArgs) {
|
|
|
67
81
|
if (true === innerArgs[NATIVE_INPUT_ARG])
|
|
68
82
|
throw new Error(NATIVE_INPUT_UNSUPPORTED);
|
|
69
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* The agent's own declaration counts as the confirmation, for that one control.
|
|
86
|
+
*
|
|
87
|
+
* `reticle_act_and_wait { target: { role: "button", name: "Place order" }, until }` names the
|
|
88
|
+
* control by the very words the guard flags AND names the consequence before acting: asking again
|
|
89
|
+
* for `confirmDangerous` cost a round trip per step on a shop's ordinary path and protected nothing.
|
|
90
|
+
* A `ref` or `testid` does not count — neither says what the control does, and a stale ref is
|
|
91
|
+
* exactly how the wrong button gets pressed. A Harness call never takes this path: its outward
|
|
92
|
+
* guard decides alone.
|
|
93
|
+
*/
|
|
94
|
+
export function confirmedByDeclaration(innerArgs, target, declared) {
|
|
95
|
+
if (!declared || true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
|
|
96
|
+
return innerArgs;
|
|
97
|
+
if (currentDrivenBy() !== undefined)
|
|
98
|
+
return innerArgs;
|
|
99
|
+
const named = asRecord(target);
|
|
100
|
+
// The words must themselves be the dangerous ones: naming "Continue" on a button whose form posts
|
|
101
|
+
// to /delete-account consents to nothing, and the guard still reads the whole control.
|
|
102
|
+
const words = ['name', 'text', 'label'].map((key) => asString(named[key]) ?? '').join(' ');
|
|
103
|
+
return isDangerousActionText(words, asString(named['role']))
|
|
104
|
+
? { ...innerArgs, [DANGEROUS_ACTION_CONFIRM_ARG]: true }
|
|
105
|
+
: innerArgs;
|
|
106
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
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 { type ReticleEvent } from '@reticlehq/core';
|
|
13
14
|
import type { Session } from '../../../portal/session/session.js';
|
|
14
15
|
import { type SuccessorRegistry } from '../../../portal/session/session-successor.js';
|
|
15
16
|
import type { EvalResult } from '@reticlehq/engine/question/predicate/predicate-eval.js';
|
|
@@ -39,4 +40,36 @@ interface FollowedOutcome {
|
|
|
39
40
|
* `awaitDocumentSuccessor` refuses to guess — a wrong follow drives somebody else's app.
|
|
40
41
|
*/
|
|
41
42
|
export declare function followLostObservation(input: FollowSuccessor): Promise<FollowedOutcome>;
|
|
43
|
+
/** Why a consequence that did not follow a click on a disabled control is not the app's failure. */
|
|
44
|
+
export declare const DISABLED_TARGET_NOTE: string;
|
|
45
|
+
/**
|
|
46
|
+
* The page reports `enabled: false` on an act whose target was disabled at dispatch. A missed
|
|
47
|
+
* consequence then says nothing about the app, so the verdict is undecided rather than "no".
|
|
48
|
+
*/
|
|
49
|
+
export declare function disabledTargetNote(pass: boolean, actResult: unknown): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* The requests of a failed check's window, as its record carries them: method, url, status and the
|
|
52
|
+
* start of the response body (redacted in the page before it was ever sent). Dev tooling is left out.
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* The requests a check keeps: on a miss, the window's calls that explain it; on a held network check,
|
|
56
|
+
* the request that answered it, so a share page can name "a POST to /api/…", not "a request was seen".
|
|
57
|
+
*/
|
|
58
|
+
export declare function checkNet(verdict: {
|
|
59
|
+
pass: boolean;
|
|
60
|
+
evidence?: unknown;
|
|
61
|
+
}, events: readonly ReticleEvent[]): {
|
|
62
|
+
net?: {
|
|
63
|
+
method: string;
|
|
64
|
+
url: string;
|
|
65
|
+
status?: number;
|
|
66
|
+
body?: string;
|
|
67
|
+
}[];
|
|
68
|
+
};
|
|
69
|
+
export declare function windowNet(events: readonly ReticleEvent[]): {
|
|
70
|
+
method: string;
|
|
71
|
+
url: string;
|
|
72
|
+
status?: number;
|
|
73
|
+
body?: string;
|
|
74
|
+
}[];
|
|
42
75
|
export {};
|
|
@@ -10,6 +10,7 @@
|
|
|
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
14
|
import { awaitDocumentSuccessor, } from '../../../portal/session/session-successor.js';
|
|
14
15
|
/**
|
|
15
16
|
* Follow the unique same-origin successor and re-ask there, or leave the verdict untouched.
|
|
@@ -34,3 +35,59 @@ export async function followLostObservation(input) {
|
|
|
34
35
|
return { session: next, verdict, followed: true };
|
|
35
36
|
return { session: next, verdict: await reevaluate(next, leftover), followed: true };
|
|
36
37
|
}
|
|
38
|
+
/** Why a consequence that did not follow a click on a disabled control is not the app's failure. */
|
|
39
|
+
export const DISABLED_TARGET_NOTE = 'the control was disabled when it was acted on, so nothing a user could do there would have ' +
|
|
40
|
+
'caused the declared consequence. Check why it was disabled (an empty required field, a save ' +
|
|
41
|
+
'that already happened) before calling this a defect.';
|
|
42
|
+
/**
|
|
43
|
+
* The page reports `enabled: false` on an act whose target was disabled at dispatch. A missed
|
|
44
|
+
* consequence then says nothing about the app, so the verdict is undecided rather than "no".
|
|
45
|
+
*/
|
|
46
|
+
export function disabledTargetNote(pass, actResult) {
|
|
47
|
+
if (pass)
|
|
48
|
+
return undefined;
|
|
49
|
+
return false === asRecord(actResult)['enabled'] ? DISABLED_TARGET_NOTE : undefined;
|
|
50
|
+
}
|
|
51
|
+
/** Calls quoted on a failed check, and how much of each response body. */
|
|
52
|
+
const MAX_NET_CALLS = 5;
|
|
53
|
+
const MAX_BODY_CHARS = 300;
|
|
54
|
+
/**
|
|
55
|
+
* The requests of a failed check's window, as its record carries them: method, url, status and the
|
|
56
|
+
* start of the response body (redacted in the page before it was ever sent). Dev tooling is left out.
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* The requests a check keeps: on a miss, the window's calls that explain it; on a held network check,
|
|
60
|
+
* the request that answered it, so a share page can name "a POST to /api/…", not "a request was seen".
|
|
61
|
+
*/
|
|
62
|
+
export function checkNet(verdict, events) {
|
|
63
|
+
if (!verdict.pass)
|
|
64
|
+
return { net: windowNet(events) };
|
|
65
|
+
const seen = asRecord(verdict.evidence);
|
|
66
|
+
const method = asString(seen['method']);
|
|
67
|
+
const url = asString(seen['url']);
|
|
68
|
+
if (method === undefined || url === undefined)
|
|
69
|
+
return {};
|
|
70
|
+
const status = seen['status'];
|
|
71
|
+
return { net: [{ method, url, ...('number' === typeof status ? { status } : {}) }] };
|
|
72
|
+
}
|
|
73
|
+
export function windowNet(events) {
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const event of events) {
|
|
76
|
+
if (EventType.NET_REQUEST !== event.type)
|
|
77
|
+
continue;
|
|
78
|
+
const url = asString(event.data['url']);
|
|
79
|
+
if (url === undefined || isDevToolingUrl(url))
|
|
80
|
+
continue;
|
|
81
|
+
const status = event.data['status'];
|
|
82
|
+
const body = asString(event.data['responseBody']);
|
|
83
|
+
out.push({
|
|
84
|
+
method: (asString(event.data['method']) ?? 'GET').toUpperCase(),
|
|
85
|
+
url,
|
|
86
|
+
...('number' === typeof status ? { status } : {}),
|
|
87
|
+
...(body === undefined ? {} : { body: body.slice(0, MAX_BODY_CHARS) }),
|
|
88
|
+
});
|
|
89
|
+
if (MAX_NET_CALLS <= out.length)
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* outcome than "unknown".
|
|
12
12
|
*/
|
|
13
13
|
import { PredicateSchema } from '@reticlehq/engine/question/predicate/predicate-schema.js';
|
|
14
|
-
import { isGlobalPressCall } from '@reticlehq/core';
|
|
14
|
+
import { SESSION_REF, isGlobalPressCall, unissuedRefRefusal } from '@reticlehq/core';
|
|
15
15
|
import { SessionReplacedError } from '../../../portal/session/page-commands/pending-commands.js';
|
|
16
16
|
import { assertNativeInputSupported } from './act-danger.js';
|
|
17
17
|
import { unevaluablePredicateReason, vacuousPredicateReason, } from '@reticlehq/engine/question/predicate/predicate-precheck.js';
|
|
@@ -80,6 +80,19 @@ export function sequenceStepArgs(step) {
|
|
|
80
80
|
flat[key] = value;
|
|
81
81
|
}
|
|
82
82
|
const nested = step['args'];
|
|
83
|
+
if ('string' === typeof nested) {
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(nested);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
throw new Error(`step.args is a string that is not valid JSON — pass an object, not a string. Got: ${nested.slice(0, 120)}`);
|
|
90
|
+
}
|
|
91
|
+
if (null === parsed || 'object' !== typeof parsed || Array.isArray(parsed)) {
|
|
92
|
+
throw new Error(`step.args is a JSON string but not an object — pass an object, not ${null === parsed ? 'null' : typeof parsed}. Got: ${nested.slice(0, 120)}`);
|
|
93
|
+
}
|
|
94
|
+
return { ...flat, ...parsed };
|
|
95
|
+
}
|
|
83
96
|
return { ...flat, ...(null !== nested && 'object' === typeof nested ? nested : {}) };
|
|
84
97
|
}
|
|
85
98
|
export function assertSequenceSteps(steps) {
|
|
@@ -113,8 +126,18 @@ export function assertSequenceSteps(steps) {
|
|
|
113
126
|
'Refused rather than dropped, because a step whose declaration was discarded reports ' +
|
|
114
127
|
'as a step that declared nothing. Nothing was acted on.');
|
|
115
128
|
}
|
|
116
|
-
if ('string' === typeof step['
|
|
129
|
+
if ('string' === typeof step['args']) {
|
|
130
|
+
sequenceStepArgs(step);
|
|
131
|
+
}
|
|
132
|
+
if ('string' === typeof step['ref'] && step['ref'].length > 0) {
|
|
133
|
+
// The same shape check `resolveActTarget` makes, moved ahead of the first step: left there, a
|
|
134
|
+
// malformed ref in step three is only found after steps one and two have already acted.
|
|
135
|
+
if (!SESSION_REF.test(step['ref'])) {
|
|
136
|
+
throw new Error(`step ${String(i)}: ${unissuedRefRefusal(step['ref'])} ` +
|
|
137
|
+
'Nothing was acted on — the whole sequence is refused so a bad step cannot leave the earlier ones half-applied.');
|
|
138
|
+
}
|
|
117
139
|
return;
|
|
140
|
+
}
|
|
118
141
|
if (step['target'] !== undefined)
|
|
119
142
|
return;
|
|
120
143
|
if (isGlobalPressCall(step))
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* dispatch out of the file that opens the window would have weakened a real invariant to satisfy a
|
|
8
8
|
* line count. This resolves a TARGET and dispatches only QUERY, so that guard is untouched.
|
|
9
9
|
*/
|
|
10
|
-
import { ReticleCommand, isGlobalPressCall } from '@reticlehq/core';
|
|
10
|
+
import { ReticleCommand, SESSION_REF, isGlobalPressCall, unissuedRefRefusal, } from '@reticlehq/core';
|
|
11
11
|
import { normalizeQueryArgs } from '../read/query-shape.js';
|
|
12
12
|
import { resolveTargetRef } from './resolve-target.js';
|
|
13
13
|
import { asRecord, asString, asNumber } from '@reticlehq/core';
|
|
@@ -49,8 +49,13 @@ export async function resolveActTarget(session, args,
|
|
|
49
49
|
*/
|
|
50
50
|
appearanceMs) {
|
|
51
51
|
const ref = asString(args['ref']);
|
|
52
|
-
if (ref !== undefined && ref.length > 0)
|
|
52
|
+
if (ref !== undefined && ref.length > 0) {
|
|
53
|
+
// Refused here, at the boundary, and never re-read as a `target`: guessing what an arbitrary
|
|
54
|
+
// string meant is the silent re-resolution the stale-ref refusal exists to prevent.
|
|
55
|
+
if (!SESSION_REF.test(ref))
|
|
56
|
+
return { kind: 'error', message: unissuedRefRefusal(ref) };
|
|
53
57
|
return { kind: 'ref', ref };
|
|
58
|
+
}
|
|
54
59
|
const target = args['target'];
|
|
55
60
|
if (target === undefined) {
|
|
56
61
|
if (isGlobalPressCall(args))
|
|
@@ -94,7 +99,7 @@ appearanceMs) {
|
|
|
94
99
|
// One match, or an ambiguity: both are answered by the existing resolver, which already ranks
|
|
95
100
|
// candidates inside its refusal so the next turn needs no snapshot.
|
|
96
101
|
if ('wait' !== decision.do)
|
|
97
|
-
return resolveTargetRef(found);
|
|
102
|
+
return resolveTargetRef(found, asRecord(out.result)['hint']);
|
|
98
103
|
await new Promise((resolve) => setTimeout(resolve, decision.waitMs));
|
|
99
104
|
}
|
|
100
105
|
}
|
|
@@ -59,6 +59,14 @@ export interface ArrivalScope {
|
|
|
59
59
|
* "the app redirected the newly arrived document somewhere else."
|
|
60
60
|
*/
|
|
61
61
|
navigatedFrom: string;
|
|
62
|
+
/**
|
|
63
|
+
* The DOCUMENT the session was on when the navigation was dispatched.
|
|
64
|
+
*
|
|
65
|
+
* A navigation to the page already shown replaces the document without changing the URL, so the
|
|
66
|
+
* old document is still the connected one until the replacement reports (#1320). Only a different
|
|
67
|
+
* document identity proves the replacement; `undefined` on both sides proves nothing and waits.
|
|
68
|
+
*/
|
|
69
|
+
navigatedFromDocumentId?: string;
|
|
62
70
|
/** Sessions already on the target before dispatch cannot prove this navigation arrived. */
|
|
63
71
|
priorIds: ReadonlySet<string>;
|
|
64
72
|
}
|
|
@@ -42,6 +42,21 @@ function samePage(a, b) {
|
|
|
42
42
|
return false;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Same origin and path, different fragment: the browser changes the URL WITHOUT replacing the
|
|
47
|
+
* document. There is no replacement to wait for on this path, so it must not be treated as a
|
|
48
|
+
* same-page navigation (#1320).
|
|
49
|
+
*/
|
|
50
|
+
function sameDocumentFragmentChange(a, b) {
|
|
51
|
+
if (!samePage(a, b))
|
|
52
|
+
return false;
|
|
53
|
+
try {
|
|
54
|
+
return new URL(a).hash !== new URL(b).hash;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
45
60
|
/**
|
|
46
61
|
* The navigation target as an absolute URL, resolved against the tab being navigated — the same
|
|
47
62
|
* resolution the browser applies. An agent passes a path (`/orders?id=4`) as often as a full URL,
|
|
@@ -70,13 +85,39 @@ export function idsAtTarget(sessions, target) {
|
|
|
70
85
|
}
|
|
71
86
|
return ids;
|
|
72
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Whether `current` is a different document from the one the navigation was dispatched on.
|
|
90
|
+
*
|
|
91
|
+
* A new Session object under the same id is not enough: the SDK also reopens its socket in place,
|
|
92
|
+
* and that replaces the Session while the page stays exactly where it was. What a real unload
|
|
93
|
+
* changes is the document id — the one sampled before dispatch (`navigatedFromDocumentId`) against
|
|
94
|
+
* the one the live session is reporting (`currentDocumentId`). Either side not having reported one
|
|
95
|
+
* yet is "cannot tell", which is never reported as a new document.
|
|
96
|
+
*/
|
|
97
|
+
function documentUnloaded(scope, current) {
|
|
98
|
+
const before = scope.navigatedFromDocumentId;
|
|
99
|
+
const after = current.currentDocumentId;
|
|
100
|
+
return before !== undefined && after !== undefined && before !== after;
|
|
101
|
+
}
|
|
73
102
|
function findArrival(sessions, target, scope, currentSession) {
|
|
74
103
|
let arrived = null;
|
|
75
|
-
for
|
|
104
|
+
// One snapshot for the whole look, so "is the driven session still connected?" and "has a
|
|
105
|
+
// replacement reported?" are answered from the same moment.
|
|
106
|
+
const snapshot = sessions.all();
|
|
107
|
+
const drivenPresent = snapshot.some((s) => s.id === currentSession.id);
|
|
108
|
+
// Navigating to the page already shown replaces its document without moving the URL, so the
|
|
109
|
+
// OLD document is still the connected one at the first looks; its presence there is not
|
|
110
|
+
// arrival until the replacement reports a different document identity (#1320). A fragment-only
|
|
111
|
+
// change keeps the document, so it is an arrival as soon as the URL matches.
|
|
112
|
+
const replacesDocument = samePage(scope.navigatedFrom, target) &&
|
|
113
|
+
!sameDocumentFragmentChange(scope.navigatedFrom, target);
|
|
114
|
+
for (const s of snapshot) {
|
|
76
115
|
// The currently tracked document gets priority because it is the strongest
|
|
77
116
|
// evidence of where this navigation went.
|
|
78
117
|
if (s.id === currentSession.id) {
|
|
79
118
|
if (samePage(s.url, target)) {
|
|
119
|
+
if (replacesDocument && s.currentDocumentId === scope.navigatedFromDocumentId)
|
|
120
|
+
continue;
|
|
80
121
|
return { sessionId: s.id };
|
|
81
122
|
}
|
|
82
123
|
// Intermediate routes are not necessarily redirects. Keep polling so
|
|
@@ -87,6 +128,12 @@ function findArrival(sessions, target, scope, currentSession) {
|
|
|
87
128
|
// navigation arrived.
|
|
88
129
|
if (scope.priorIds.has(s.id))
|
|
89
130
|
continue;
|
|
131
|
+
// While the replaced document is still connected, a DIFFERENT session at the target is an
|
|
132
|
+
// unrelated arrival (a second tab, a neighbour app): the replacement this navigation waits
|
|
133
|
+
// for has not reported yet, and letting the newcomer stand in would confirm too early and
|
|
134
|
+
// name the wrong session (#1320).
|
|
135
|
+
if (replacesDocument && drivenPresent)
|
|
136
|
+
continue;
|
|
90
137
|
if (samePage(s.url, target)) {
|
|
91
138
|
if (null === arrived)
|
|
92
139
|
arrived = { sessionId: s.id };
|
|
@@ -122,7 +169,11 @@ export async function awaitArrival(sessions, target, scope, timeoutMs = ARRIVAL_
|
|
|
122
169
|
// landed when the target was never observed.
|
|
123
170
|
const current = sessions.get(scope.navigatedSession.id);
|
|
124
171
|
if (current !== undefined) {
|
|
125
|
-
|
|
172
|
+
// Sitting on `navigatedFrom` means two different things: a tab that has not moved, or a page
|
|
173
|
+
// that unloaded and came back here (an auth guard bouncing `/checkout` to `/login` while the
|
|
174
|
+
// tab started on `/login`). Only a document that is provably new tells them apart.
|
|
175
|
+
if (!samePage(current.url, target) &&
|
|
176
|
+
(!samePage(current.url, scope.navigatedFrom) || documentUnloaded(scope, current))) {
|
|
126
177
|
return {
|
|
127
178
|
sessionId: current.id,
|
|
128
179
|
landedOn: current.url,
|
|
@@ -131,6 +182,9 @@ export async function awaitArrival(sessions, target, scope, timeoutMs = ARRIVAL_
|
|
|
131
182
|
return null;
|
|
132
183
|
}
|
|
133
184
|
const successor = await awaitDocumentSuccessor(sessions, scope.navigatedSession, 0, clock);
|
|
185
|
+
// A successor under another id is only "unique at the origin", which a tab opened by someone else
|
|
186
|
+
// after the driven one went away satisfies equally. On the starting page that guess cannot be told
|
|
187
|
+
// from a redirect, so it is not reported as one.
|
|
134
188
|
if (successor !== null &&
|
|
135
189
|
!samePage(successor.url, target) &&
|
|
136
190
|
!samePage(successor.url, scope.navigatedFrom)) {
|
|
@@ -29,4 +29,4 @@ export type TargetResolution = {
|
|
|
29
29
|
* When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
|
|
30
30
|
* agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
|
|
31
31
|
*/
|
|
32
|
-
export declare function resolveTargetRef(candidates: readonly unknown[]): TargetResolution;
|
|
32
|
+
export declare function resolveTargetRef(candidates: readonly unknown[], hint?: unknown): TargetResolution;
|
|
@@ -61,6 +61,21 @@ function formatCandidate(c, anyInViewport) {
|
|
|
61
61
|
const head = name.length > 0 ? `${ref} (${role} "${name}"` : `${ref} (${role}`;
|
|
62
62
|
return `${head}; ${where})`;
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* The names that role really has, when an exact role + name query just missed (the browser's
|
|
66
|
+
* `nameNearMiss` hint). Names are matched exactly, so one that carries a changing part — "billing
|
|
67
|
+
* Last run 1 minute ago" — is unreachable by name; `testid` or a `text` match is the durable way.
|
|
68
|
+
*/
|
|
69
|
+
function nearNamesClause(hint) {
|
|
70
|
+
const near = asRecord(hint)['nameNearMiss'];
|
|
71
|
+
const names = Array.isArray(near) ? near.filter((n) => 'string' === typeof n) : [];
|
|
72
|
+
if (0 === names.length) {
|
|
73
|
+
return 'widen the query, or take a reticle_snapshot to see what is actually on the page.';
|
|
74
|
+
}
|
|
75
|
+
return (`names are matched exactly, and that role is called ${names.map((n) => `"${n}"`).join(', ')}. ` +
|
|
76
|
+
'Use that name, or — when the name carries a part that changes, like a time — target it by ' +
|
|
77
|
+
'`testid` or by `text` (a contained match).');
|
|
78
|
+
}
|
|
64
79
|
/**
|
|
65
80
|
* Turn a target QUERY into the single ref an action can be performed on.
|
|
66
81
|
*
|
|
@@ -81,15 +96,15 @@ function formatCandidate(c, anyInViewport) {
|
|
|
81
96
|
* When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
|
|
82
97
|
* agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
|
|
83
98
|
*/
|
|
84
|
-
export function resolveTargetRef(candidates) {
|
|
99
|
+
export function resolveTargetRef(candidates, hint) {
|
|
85
100
|
const all = candidates.map((c) => asRecord(c));
|
|
86
101
|
const visible = all.filter((c) => c.visible !== false);
|
|
87
102
|
const usable = visible.length > 0 ? visible : all;
|
|
88
103
|
if (0 === usable.length) {
|
|
89
104
|
return {
|
|
90
105
|
kind: 'error',
|
|
91
|
-
message: 'target matched no element. Nothing was acted on and no verdict is possible —
|
|
92
|
-
|
|
106
|
+
message: 'target matched no element. Nothing was acted on and no verdict is possible — ' +
|
|
107
|
+
nearNamesClause(hint),
|
|
93
108
|
};
|
|
94
109
|
}
|
|
95
110
|
if (usable.length > 1) {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opt-in screen/request check, wired: after an act_and_wait, compare what the window's write
|
|
3
|
+
* requests carried with the values the page's form controls show (engine: screen-request.ts).
|
|
4
|
+
*
|
|
5
|
+
* OPT-IN (`RETICLE_SCREEN_REQUEST_CHECK=1`) and ADVISORY. It ships behind the flag until its
|
|
6
|
+
* false-alarm rate is measured at zero on the e2e battery, the benchmark and the persona fixtures;
|
|
7
|
+
* until then it must cost nothing for anyone who did not ask. It runs AFTER the verdict is decided
|
|
8
|
+
* and is appended to `contradictions` as an advisory kind, so it can never move a verdict.
|
|
9
|
+
*/
|
|
10
|
+
import { type Contradiction, type ReticleEvent } from '@reticlehq/core';
|
|
11
|
+
import type { Session } from '../../../portal/session/session.js';
|
|
12
|
+
export declare const SCREEN_REQUEST_CHECK_ENV = "RETICLE_SCREEN_REQUEST_CHECK";
|
|
13
|
+
export interface SentBody {
|
|
14
|
+
method: string;
|
|
15
|
+
url: string;
|
|
16
|
+
/** The request body as sent (JSON text). */
|
|
17
|
+
body: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ShownControl {
|
|
20
|
+
/** `name` or `id` attribute — the only link from a control to a field. */
|
|
21
|
+
field: string;
|
|
22
|
+
/** The control's current value (a select's selected option value, an input's text). */
|
|
23
|
+
value: string;
|
|
24
|
+
/** What the user sees, when it differs from the value (a select's option label). */
|
|
25
|
+
label?: string | undefined;
|
|
26
|
+
ref?: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
export declare function screenRequestMismatches(sent: readonly SentBody[], controls: readonly ShownControl[]): Contradiction[];
|
|
29
|
+
/** Advisory findings for this window, or none when the check is off or nothing was written. */
|
|
30
|
+
export declare function screenRequestFindings(session: Session, windowEvents: readonly ReticleEvent[]): Promise<Contradiction[]>;
|
|
31
|
+
/** The `contradictions` field: the verdict's own, then any advisory ones; omitted when both are empty. */
|
|
32
|
+
export declare function withAdvisories(decided: readonly Contradiction[], advisory: readonly Contradiction[]): {
|
|
33
|
+
contradictions?: Contradiction[];
|
|
34
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opt-in screen/request check, wired: after an act_and_wait, compare what the window's write
|
|
3
|
+
* requests carried with the values the page's form controls show (engine: screen-request.ts).
|
|
4
|
+
*
|
|
5
|
+
* OPT-IN (`RETICLE_SCREEN_REQUEST_CHECK=1`) and ADVISORY. It ships behind the flag until its
|
|
6
|
+
* false-alarm rate is measured at zero on the e2e battery, the benchmark and the persona fixtures;
|
|
7
|
+
* until then it must cost nothing for anyone who did not ask. It runs AFTER the verdict is decided
|
|
8
|
+
* and is appended to `contradictions` as an advisory kind, so it can never move a verdict.
|
|
9
|
+
*/
|
|
10
|
+
import { ContradictionKind, EventType, REDACTED_VALUE, ReticleCommand, } from '@reticlehq/core';
|
|
11
|
+
export const SCREEN_REQUEST_CHECK_ENV = 'RETICLE_SCREEN_REQUEST_CHECK';
|
|
12
|
+
const READ_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);
|
|
13
|
+
/** The form controls whose value a request can carry. */
|
|
14
|
+
const CONTROL_ROLES = ['combobox', 'listbox', 'textbox', 'spinbutton'];
|
|
15
|
+
function primitiveFields(body) {
|
|
16
|
+
const out = new Map();
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(body);
|
|
19
|
+
if ('object' !== typeof parsed || null === parsed || Array.isArray(parsed))
|
|
20
|
+
return out;
|
|
21
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
22
|
+
if ('string' === typeof value || 'number' === typeof value || 'boolean' === typeof value) {
|
|
23
|
+
out.set(key.toLowerCase(), String(value));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Not JSON: nothing comparable, so nothing reported.
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
function pathOf(url) {
|
|
33
|
+
try {
|
|
34
|
+
return new URL(url, 'http://local.invalid').pathname;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return url;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function screenRequestMismatches(sent, controls) {
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const request of sent) {
|
|
43
|
+
const fields = primitiveFields(request.body);
|
|
44
|
+
if (0 === fields.size)
|
|
45
|
+
continue;
|
|
46
|
+
for (const control of controls) {
|
|
47
|
+
const key = control.field.toLowerCase();
|
|
48
|
+
const carried = fields.get(key);
|
|
49
|
+
if (carried === undefined || carried === control.value)
|
|
50
|
+
continue;
|
|
51
|
+
const shown = control.label !== undefined && control.label !== control.value
|
|
52
|
+
? `"${control.label}" (value "${control.value}")`
|
|
53
|
+
: `"${control.value}"`;
|
|
54
|
+
out.push({
|
|
55
|
+
kind: ContradictionKind.SCREEN_REQUEST_MISMATCH,
|
|
56
|
+
claim: `the screen's "${control.field}" control shows ${shown}`,
|
|
57
|
+
counter: `${request.method.toUpperCase()} ${pathOf(request.url)} sent ${control.field}=${JSON.stringify(carried)}`,
|
|
58
|
+
detail: `${control.ref === undefined ? '' : `${control.ref} `}${control.field}: shown ${shown}, sent ${JSON.stringify(carried)}`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function sentBodies(events) {
|
|
65
|
+
return events.flatMap((event) => {
|
|
66
|
+
if (EventType.NET_REQUEST !== event.type)
|
|
67
|
+
return [];
|
|
68
|
+
const raw = event.data['method'];
|
|
69
|
+
const method = ('string' === typeof raw ? raw : 'GET').toUpperCase();
|
|
70
|
+
const body = event.data['requestBody'];
|
|
71
|
+
const url = event.data['url'];
|
|
72
|
+
if (READ_METHODS.has(method) || 'string' !== typeof body || 'string' !== typeof url)
|
|
73
|
+
return [];
|
|
74
|
+
return [{ method, url, body }];
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async function shownControls(session) {
|
|
78
|
+
const out = [];
|
|
79
|
+
for (const role of CONTROL_ROLES) {
|
|
80
|
+
const res = await session.command(ReticleCommand.MATCH, {
|
|
81
|
+
query: { role, attrs: ['name', 'id'] },
|
|
82
|
+
});
|
|
83
|
+
if (!res.ok)
|
|
84
|
+
continue;
|
|
85
|
+
for (const el of res.result?.elements ?? []) {
|
|
86
|
+
const field = el.attrs?.['name'] ?? el.attrs?.['id'];
|
|
87
|
+
// No value read, or a redacted one: nothing to compare, so nothing is guessed.
|
|
88
|
+
if (field === undefined || el.value === undefined || REDACTED_VALUE === el.value)
|
|
89
|
+
continue;
|
|
90
|
+
out.push({ field, value: el.value, ref: el.ref });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
/** Advisory findings for this window, or none when the check is off or nothing was written. */
|
|
96
|
+
export async function screenRequestFindings(session, windowEvents) {
|
|
97
|
+
if ('1' !== process.env[SCREEN_REQUEST_CHECK_ENV])
|
|
98
|
+
return [];
|
|
99
|
+
const sent = sentBodies(windowEvents);
|
|
100
|
+
if (0 === sent.length)
|
|
101
|
+
return [];
|
|
102
|
+
try {
|
|
103
|
+
return screenRequestMismatches(sent, await shownControls(session));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** The `contradictions` field: the verdict's own, then any advisory ones; omitted when both are empty. */
|
|
110
|
+
export function withAdvisories(decided, advisory) {
|
|
111
|
+
const all = [...decided, ...advisory];
|
|
112
|
+
return 0 === all.length ? {} : { contradictions: all };
|
|
113
|
+
}
|
|
@@ -40,6 +40,12 @@ export interface StepExpectation {
|
|
|
40
40
|
* filter act_and_wait applies. A held step with one is a UI that advanced over a failed write.
|
|
41
41
|
*/
|
|
42
42
|
contradicted?: readonly string[];
|
|
43
|
+
/**
|
|
44
|
+
* Why this step could not be judged at all: its target was missing (nothing was acted on), the
|
|
45
|
+
* page left the app and the observation went with it, or the predicate could not be read. Such a
|
|
46
|
+
* step is undecided, never a miss — `unknown`, as act_and_wait says.
|
|
47
|
+
*/
|
|
48
|
+
undecided?: string;
|
|
43
49
|
}
|
|
44
50
|
export interface SequenceGrade {
|
|
45
51
|
verified: Verified;
|
|
@@ -80,5 +86,7 @@ export declare function gradeSequence(steps: readonly StepExpectation[], plan?:
|
|
|
80
86
|
* two-of-twelve plan as a regression flow is a reasonable thing to do and a bad thing to do without
|
|
81
87
|
* knowing it.
|
|
82
88
|
*/
|
|
89
|
+
/** The stopped recording a proved journey is held under, so the `keep` offer can be taken. */
|
|
90
|
+
export declare const JOURNEY_RECORDING = "last-journey";
|
|
83
91
|
export declare function offerToKeep(grade: SequenceGrade): string | undefined;
|
|
84
92
|
export {};
|
|
@@ -29,7 +29,7 @@ export function gradeSequence(steps, plan = { planned: steps.length, dispatched:
|
|
|
29
29
|
const declared = declaring.length;
|
|
30
30
|
// `held: false` on an undeclared step means it could not be performed at all — the journey broke
|
|
31
31
|
// there whether or not anything was declared about it.
|
|
32
|
-
const missed = steps.find((step) => (step.declared ? true !== step.held : false === step.held));
|
|
32
|
+
const missed = steps.find((step) => step.undecided === undefined && (step.declared ? true !== step.held : false === step.held));
|
|
33
33
|
// A miss decides the grade before coverage does. How much of the plan declared nothing is
|
|
34
34
|
// context on a red, never a softening of it.
|
|
35
35
|
if (missed !== undefined) {
|
|
@@ -52,6 +52,15 @@ export function gradeSequence(steps, plan = { planned: steps.length, dispatched:
|
|
|
52
52
|
because: `step ${String(contradictedAt + 1)}: channels disagree about this action (${kinds}) even though it went on`,
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
// Nothing failed that was seen: a step nobody could judge leaves the plan undecided, saying why.
|
|
56
|
+
const unjudged = steps.find((step) => step.undecided !== undefined);
|
|
57
|
+
if (unjudged !== undefined)
|
|
58
|
+
return {
|
|
59
|
+
verified: Verified.UNKNOWN,
|
|
60
|
+
declared,
|
|
61
|
+
total,
|
|
62
|
+
because: `a step could not be judged — ${unjudged.undecided ?? ''}`,
|
|
63
|
+
};
|
|
55
64
|
/*
|
|
56
65
|
* `unknown` rather than `no-fault`, and the distinction is load-bearing.
|
|
57
66
|
*
|
|
@@ -125,11 +134,13 @@ export function gradeSequence(steps, plan = { planned: steps.length, dispatched:
|
|
|
125
134
|
* two-of-twelve plan as a regression flow is a reasonable thing to do and a bad thing to do without
|
|
126
135
|
* knowing it.
|
|
127
136
|
*/
|
|
137
|
+
/** The stopped recording a proved journey is held under, so the `keep` offer can be taken. */
|
|
138
|
+
export const JOURNEY_RECORDING = 'last-journey';
|
|
128
139
|
export function offerToKeep(grade) {
|
|
129
140
|
if (grade.verified !== Verified.YES || 0 === grade.declared)
|
|
130
141
|
return undefined;
|
|
131
142
|
return (`this plan proved something (${String(grade.declared)} of ${String(grade.total)} steps declared a ` +
|
|
132
143
|
'consequence and every one held) — keep it as a regression flow with ' +
|
|
133
|
-
|
|
144
|
+
`reticle_flow_save { flowName: "${JOURNEY_RECORDING}", saveAs: "<name>" }, and the steps that declared nothing will replay ` +
|
|
134
145
|
'without proving anything, exactly as they did here');
|
|
135
146
|
}
|