@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
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* the thin Playwright adapter that satisfies these interfaces lives separately.
|
|
11
11
|
*/
|
|
12
12
|
import { unreachableUrlIn } from '@reticlehq/core';
|
|
13
|
-
import { seedStorageInto } from './storage-seed.js';
|
|
13
|
+
import { seedStorageInto, targetOriginOf } from './storage-seed.js';
|
|
14
|
+
import { installLeaseMarker, leaseMarkerOf } from './lease-marker-stamp.js';
|
|
15
|
+
import { grantLeasePermissions, NOTIFICATION_PERMISSION_READ, NOTIFICATION_READ_TIMEOUT_MS, } from './context-permissions.js';
|
|
14
16
|
/** Default lease time-to-live: a lease untouched for this long is presumed orphaned and reclaimed. */
|
|
15
17
|
export const DEFAULT_LEASE_TTL_MS = 5 * 60_000;
|
|
16
18
|
/** Default cap on how long a lease's initial navigation may take before it fails (frees the slot). */
|
|
@@ -20,20 +22,29 @@ const DEFAULT_NAV_TIMEOUT_MS = 30_000;
|
|
|
20
22
|
* this is slack for a burst sweep, not a history.
|
|
21
23
|
*/
|
|
22
24
|
const MAX_REMEMBERED_REAPED_LEASES = 64;
|
|
25
|
+
/** What a headed lease is refused with where no window can be opened. */
|
|
26
|
+
export const NO_HEADED_BROWSER = 'no headed browser can be opened here';
|
|
23
27
|
/**
|
|
24
|
-
*
|
|
28
|
+
* What an acquire that asked for an init script is refused with when the page cannot run one.
|
|
29
|
+
*
|
|
30
|
+
* A refusal rather than a lease: the caller asked for a stub of the bindings a desktop shell
|
|
31
|
+
* injects, and a tab opened without it would throw in the app's view and read as a broken app.
|
|
32
|
+
*/
|
|
33
|
+
const INIT_SCRIPT_UNSUPPORTED = 'Init script failed: this page does not support init scripts, so the requested stub would not be there';
|
|
34
|
+
/**
|
|
35
|
+
* Owns a headless browser (and, when asked, a headed one) and leases isolated contexts out of them,
|
|
36
|
+
* capped at `maxContexts` across both.
|
|
25
37
|
* Not exported as a singleton — the daemon owns one instance.
|
|
26
38
|
*/
|
|
27
39
|
export class BrowserPool {
|
|
28
|
-
#launch;
|
|
29
40
|
#max;
|
|
30
41
|
#genId;
|
|
31
42
|
#now;
|
|
32
43
|
#ttl;
|
|
33
44
|
#zeroInstall;
|
|
34
45
|
#navTimeout;
|
|
35
|
-
#
|
|
36
|
-
#
|
|
46
|
+
#headless;
|
|
47
|
+
#headed;
|
|
37
48
|
#closed = false;
|
|
38
49
|
/** Leases reclaimed for going stale — see reapedLeaseCount(). */
|
|
39
50
|
#reapedLeases = 0;
|
|
@@ -65,7 +76,8 @@ export class BrowserPool {
|
|
|
65
76
|
constructor(launch, opts) {
|
|
66
77
|
if (opts.maxContexts < 1)
|
|
67
78
|
throw new Error('maxContexts must be >= 1');
|
|
68
|
-
this.#
|
|
79
|
+
this.#headless = { launch, headed: false };
|
|
80
|
+
this.#headed = { launch: opts.launchHeaded, headed: true };
|
|
69
81
|
this.#max = opts.maxContexts;
|
|
70
82
|
this.#genId = opts.genSessionId;
|
|
71
83
|
this.#now = opts.now ?? (() => Date.now());
|
|
@@ -93,16 +105,22 @@ export class BrowserPool {
|
|
|
93
105
|
return [...this.#active.keys()];
|
|
94
106
|
}
|
|
95
107
|
/**
|
|
96
|
-
* The public session id of an active lease on this origin, if
|
|
108
|
+
* The public session id of an active lease on this origin that `owner` took, if there is one.
|
|
97
109
|
*
|
|
98
110
|
* The lease TOOL reuses that id instead of minting a second context: a second acquire on the same
|
|
99
|
-
* origin used to leave both tabs connected and poison default session resolution (#600).
|
|
100
|
-
*
|
|
111
|
+
* origin used to leave both tabs connected and poison default session resolution (#600). Only the
|
|
112
|
+
* caller that took the lease gets it back: handing it to whoever asked put two agents in one tab,
|
|
113
|
+
* each one's verdicts resting on the other's actions (#1226). A caller with no owner cannot be told
|
|
114
|
+
* apart from another, so it always gets its own. The pool's own `acquire` still mints.
|
|
101
115
|
*
|
|
102
116
|
* Prefers the alias the agent was given, when the app registered under its own name.
|
|
103
117
|
*/
|
|
104
|
-
leaseIdOnOrigin(origin) {
|
|
118
|
+
leaseIdOnOrigin(origin, owner) {
|
|
119
|
+
if (owner === undefined)
|
|
120
|
+
return undefined;
|
|
105
121
|
for (const [leaseId, lease] of this.#active) {
|
|
122
|
+
if (lease.owner !== owner)
|
|
123
|
+
continue;
|
|
106
124
|
let leaseOrigin;
|
|
107
125
|
try {
|
|
108
126
|
leaseOrigin = new URL(lease.url).origin;
|
|
@@ -172,6 +190,15 @@ export class BrowserPool {
|
|
|
172
190
|
#leaseIdOf(sessionId) {
|
|
173
191
|
return this.#active.has(sessionId) ? sessionId : (this.#aliases.get(sessionId) ?? sessionId);
|
|
174
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Whether `sessionId` is a live lease that `owner` may let go of: one it took, or one taken by a
|
|
195
|
+
* caller with no id, which cannot be told apart from it. Another agent's lease is never this
|
|
196
|
+
* caller's to close (#1226).
|
|
197
|
+
*/
|
|
198
|
+
heldBy(sessionId, owner) {
|
|
199
|
+
const lease = this.#active.get(this.#leaseIdOf(sessionId));
|
|
200
|
+
return lease !== undefined && (lease.owner === undefined || lease.owner === owner);
|
|
201
|
+
}
|
|
175
202
|
/** Mark a lease as still in use (called on agent activity) so the reaper doesn't reclaim it. */
|
|
176
203
|
touch(sessionId) {
|
|
177
204
|
const lease = this.#active.get(this.#leaseIdOf(sessionId));
|
|
@@ -206,6 +233,28 @@ export class BrowserPool {
|
|
|
206
233
|
return undefined;
|
|
207
234
|
}
|
|
208
235
|
}
|
|
236
|
+
/** The URL a lease was opened on — the one that carries its Reticle identity. */
|
|
237
|
+
leaseUrl(sessionId) {
|
|
238
|
+
return this.#active.get(this.#leaseIdOf(sessionId))?.url;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Navigate a leased page through the browser. For a lease whose page has no SDK to take the
|
|
242
|
+
* command — a navigate to an image left the lease unreachable, and the next navigate answered "no
|
|
243
|
+
* connected session". False when this is not a lease or the page would not go.
|
|
244
|
+
*/
|
|
245
|
+
async gotoLease(sessionId, url, timeoutMs) {
|
|
246
|
+
const lease = this.#active.get(this.#leaseIdOf(sessionId));
|
|
247
|
+
if (lease === undefined)
|
|
248
|
+
return false;
|
|
249
|
+
this.touch(sessionId);
|
|
250
|
+
try {
|
|
251
|
+
await lease.page.goto(url, timeoutMs === undefined ? {} : { timeoutMs });
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
209
258
|
/**
|
|
210
259
|
* Move the real pointer on a leased page, or false when this session is not a lease or cannot
|
|
211
260
|
* hover. Same optional-capability contract as screenshotLease: absence must read as "could not
|
|
@@ -266,6 +315,52 @@ export class BrowserPool {
|
|
|
266
315
|
return false;
|
|
267
316
|
}
|
|
268
317
|
}
|
|
318
|
+
/** What a lease was granted when it opened, or undefined for a session that is not a lease. */
|
|
319
|
+
permissionsOf(sessionId) {
|
|
320
|
+
return this.#active.get(this.#leaseIdOf(sessionId))?.permissions;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Whether this lease's page runs a caller-supplied init script — a stub of the bindings a desktop
|
|
324
|
+
* shell injects. Alias-resolved like every agent-facing lookup, and false for any session that is
|
|
325
|
+
* not a lease with a stub, so a verdict can say when it was measured against a fake shell.
|
|
326
|
+
*/
|
|
327
|
+
stubInstalled(sessionId) {
|
|
328
|
+
return this.#active.get(this.#leaseIdOf(sessionId))?.hostStub !== undefined;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* The init script this lease installed, or undefined when it runs none. Alias-resolved like
|
|
332
|
+
* `stubInstalled`. The lease tool compares it to the script a caller asks for: the same text means
|
|
333
|
+
* the held tab already is what was asked for, so it is reused rather than released and remade.
|
|
334
|
+
*/
|
|
335
|
+
hostStubScript(sessionId) {
|
|
336
|
+
return this.#active.get(this.#leaseIdOf(sessionId))?.hostStubScript;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* What the leased page's `Notification.permission` says, or undefined when it cannot be asked.
|
|
340
|
+
*
|
|
341
|
+
* Read back rather than inferred from the grant, because the headless shell answers `denied`
|
|
342
|
+
* after a notifications grant — see context-permissions.ts. Bounded, so a page that never answers
|
|
343
|
+
* reads as "could not ask" instead of holding the caller.
|
|
344
|
+
*/
|
|
345
|
+
async notificationPermission(sessionId) {
|
|
346
|
+
const lease = this.#active.get(this.#leaseIdOf(sessionId));
|
|
347
|
+
if (lease === undefined || lease.page.evaluate === undefined)
|
|
348
|
+
return undefined;
|
|
349
|
+
let timer;
|
|
350
|
+
const gaveUp = new Promise((resolve) => {
|
|
351
|
+
timer = setTimeout(() => resolve(undefined), NOTIFICATION_READ_TIMEOUT_MS);
|
|
352
|
+
});
|
|
353
|
+
try {
|
|
354
|
+
const value = await Promise.race([lease.page.evaluate(NOTIFICATION_PERMISSION_READ), gaveUp]);
|
|
355
|
+
return 'string' === typeof value ? value : undefined;
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
finally {
|
|
361
|
+
clearTimeout(timer);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
269
364
|
async sweepExpired() {
|
|
270
365
|
const now = this.#now();
|
|
271
366
|
const expired = [...this.#active.entries()]
|
|
@@ -314,6 +409,10 @@ export class BrowserPool {
|
|
|
314
409
|
async acquire(url, opts = {}) {
|
|
315
410
|
if (this.#closed)
|
|
316
411
|
throw new Error('browser pool is shut down');
|
|
412
|
+
const slot = true === opts.headed ? this.#headed : this.#headless;
|
|
413
|
+
// Refused before a slot is claimed, so a refusal never holds one.
|
|
414
|
+
if (slot.launch === undefined)
|
|
415
|
+
throw new Error(NO_HEADED_BROWSER);
|
|
317
416
|
// #waitForSlot claims the slot synchronously (bumps #occupied) before returning, so the cap holds
|
|
318
417
|
// even when many acquires race through the gate in the same tick.
|
|
319
418
|
await this.#waitForSlot(opts.signal);
|
|
@@ -322,9 +421,22 @@ export class BrowserPool {
|
|
|
322
421
|
let pending;
|
|
323
422
|
let pendingDialogMessage;
|
|
324
423
|
try {
|
|
325
|
-
const browser = await this.#ensureBrowser();
|
|
326
|
-
context = await browser.newContext();
|
|
424
|
+
const browser = await this.#ensureBrowser(slot);
|
|
425
|
+
context = await browser.newContext(true === opts.ignoreHTTPSErrors ? { ignoreHTTPSErrors: true } : undefined);
|
|
426
|
+
if (opts.permissions !== undefined)
|
|
427
|
+
await grantLeasePermissions(context, opts.permissions, url);
|
|
327
428
|
const page = await context.newPage();
|
|
429
|
+
// A caller-supplied init script is a stub of the bindings a desktop shell injects, and it has
|
|
430
|
+
// to exist before the app's first module runs. Installed here — before goto — so the very
|
|
431
|
+
// first document gets it, and kept for the lease (see hostStub) so a reload keeps it too.
|
|
432
|
+
let hostStub;
|
|
433
|
+
if (opts.initScript !== undefined) {
|
|
434
|
+
const added = await page.addInitScript?.(opts.initScript);
|
|
435
|
+
if (added === undefined || 'function' !== typeof added.dispose) {
|
|
436
|
+
throw new Error(INIT_SCRIPT_UNSUPPORTED);
|
|
437
|
+
}
|
|
438
|
+
hostStub = added;
|
|
439
|
+
}
|
|
328
440
|
// Per-page crash isolation: if THIS renderer dies, reclaim only this lease — the shared browser
|
|
329
441
|
// and every other agent's context keep running. (A full browser death is handled by #onCrash.)
|
|
330
442
|
page.onCrash(() => {
|
|
@@ -354,6 +466,10 @@ export class BrowserPool {
|
|
|
354
466
|
pendingDialogMessage = dialog.message;
|
|
355
467
|
void dialog.dismiss();
|
|
356
468
|
});
|
|
469
|
+
const marker = leaseMarkerOf(url);
|
|
470
|
+
if (marker?.session === sessionId) {
|
|
471
|
+
await installLeaseMarker(page, sessionId, targetOriginOf(url), marker.project);
|
|
472
|
+
}
|
|
357
473
|
let seedHandle;
|
|
358
474
|
let checkSeedError;
|
|
359
475
|
if (opts.seedStorage !== undefined) {
|
|
@@ -375,8 +491,11 @@ export class BrowserPool {
|
|
|
375
491
|
// #occupied. Registering the lease now would resurrect a dead entry against a crashed browser with
|
|
376
492
|
// the slot count out of sync (drifting below #active.size, eventually exceeding the cap). If we're
|
|
377
493
|
// no longer the live browser, bail — the catch below closes the context and returns the slot.
|
|
378
|
-
if (
|
|
494
|
+
if (slot.browser !== browser)
|
|
379
495
|
throw new Error('browser crashed during navigation');
|
|
496
|
+
// Foreground it: a page opened beside other tabs can start hidden (#1351). Best-effort — a tab
|
|
497
|
+
// that stays hidden is reported by name at the lease, which failing here would lose.
|
|
498
|
+
await page.bringToFront?.().catch(() => undefined);
|
|
380
499
|
let navStatus;
|
|
381
500
|
if (null !== navRes && 'object' === typeof navRes && 'status' in navRes) {
|
|
382
501
|
const s = navRes.status;
|
|
@@ -395,12 +514,19 @@ export class BrowserPool {
|
|
|
395
514
|
}
|
|
396
515
|
}
|
|
397
516
|
this.#active.set(sessionId, {
|
|
517
|
+
headed: slot.headed,
|
|
398
518
|
context,
|
|
399
519
|
page,
|
|
400
520
|
url,
|
|
521
|
+
...(opts.owner === undefined ? {} : { owner: opts.owner }),
|
|
401
522
|
touchedAt: this.#now(),
|
|
523
|
+
permissions: opts.permissions ?? [],
|
|
524
|
+
ignoresHTTPSErrors: true === opts.ignoreHTTPSErrors,
|
|
402
525
|
...(pending === undefined ? {} : { dialFailureUrl: pending }),
|
|
403
526
|
...(pendingDialogMessage === undefined ? {} : { lastDialogMessage: pendingDialogMessage }),
|
|
527
|
+
...(hostStub === undefined || opts.initScript === undefined
|
|
528
|
+
? {}
|
|
529
|
+
: { hostStub, hostStubScript: opts.initScript }),
|
|
404
530
|
});
|
|
405
531
|
return {
|
|
406
532
|
sessionId,
|
|
@@ -450,17 +576,19 @@ export class BrowserPool {
|
|
|
450
576
|
await Promise.all(contexts.map((c) => c.close().catch(() => undefined)));
|
|
451
577
|
// If a launch is in flight, await it so we can close the resulting browser — otherwise it
|
|
452
578
|
// resolves after shutdown returns and the Chromium process is orphaned (hundreds of MB leaked).
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
579
|
+
for (const slot of [this.#headless, this.#headed]) {
|
|
580
|
+
const inflight = slot.launching;
|
|
581
|
+
if (inflight !== undefined) {
|
|
582
|
+
const launched = await inflight.catch(() => undefined);
|
|
583
|
+
if (launched !== undefined && launched !== slot.browser) {
|
|
584
|
+
await launched.close().catch(() => undefined);
|
|
585
|
+
}
|
|
458
586
|
}
|
|
587
|
+
const browser = slot.browser;
|
|
588
|
+
slot.browser = undefined;
|
|
589
|
+
if (browser !== undefined)
|
|
590
|
+
await browser.close().catch(() => undefined);
|
|
459
591
|
}
|
|
460
|
-
const browser = this.#browser;
|
|
461
|
-
this.#browser = undefined;
|
|
462
|
-
if (browser !== undefined)
|
|
463
|
-
await browser.close().catch(() => undefined);
|
|
464
592
|
for (const waiter of this.#waiters.splice(0))
|
|
465
593
|
waiter();
|
|
466
594
|
}
|
|
@@ -475,6 +603,9 @@ export class BrowserPool {
|
|
|
475
603
|
if (leaseId === sessionId)
|
|
476
604
|
this.#aliases.delete(registered);
|
|
477
605
|
}
|
|
606
|
+
// Disposed with the lease, which is what "for that lease only" means. Best-effort: a context
|
|
607
|
+
// that is already gone (a crash) makes the disposal moot, not a reason to fail the release.
|
|
608
|
+
await lease.hostStub?.dispose().catch(() => undefined);
|
|
478
609
|
await lease.context.close().catch(() => undefined);
|
|
479
610
|
this.#releaseSlot();
|
|
480
611
|
}
|
|
@@ -532,38 +663,60 @@ export class BrowserPool {
|
|
|
532
663
|
if (next !== undefined)
|
|
533
664
|
next();
|
|
534
665
|
}
|
|
535
|
-
async #ensureBrowser() {
|
|
666
|
+
async #ensureBrowser(slot) {
|
|
536
667
|
if (this.#closed)
|
|
537
668
|
throw new Error('browser pool is shut down');
|
|
538
|
-
if (
|
|
539
|
-
return
|
|
669
|
+
if (slot.browser !== undefined && slot.browser.isConnected())
|
|
670
|
+
return slot.browser;
|
|
671
|
+
const launch = slot.launch;
|
|
672
|
+
if (launch === undefined)
|
|
673
|
+
throw new Error(NO_HEADED_BROWSER);
|
|
540
674
|
// De-dupe concurrent launches: the first acquire to find no browser starts one; the rest await it.
|
|
541
|
-
if (
|
|
675
|
+
if (slot.launching === undefined) {
|
|
542
676
|
// The in-flight promise MUST be cleared on failure as well as success. It used to be cleared only
|
|
543
677
|
// in the success callback, so the first failed launch — Chromium not downloaded, a transient
|
|
544
678
|
// EAGAIN — left a permanently rejected promise here, and every subsequent acquire for the daemon's
|
|
545
679
|
// lifetime re-returned that same stale rejection. Running `npx playwright install` did not help;
|
|
546
680
|
// only restarting the daemon did. A retry must be allowed to actually retry.
|
|
547
|
-
|
|
681
|
+
slot.launching = launch()
|
|
548
682
|
.then((b) => {
|
|
549
|
-
b.onDisconnected(() => this.#onCrash(b));
|
|
550
|
-
|
|
683
|
+
b.onDisconnected(() => this.#onCrash(slot, b));
|
|
684
|
+
slot.browser = b;
|
|
551
685
|
return b;
|
|
552
686
|
})
|
|
553
687
|
.finally(() => {
|
|
554
|
-
|
|
688
|
+
slot.launching = undefined;
|
|
555
689
|
});
|
|
556
690
|
}
|
|
557
|
-
return
|
|
691
|
+
return slot.launching;
|
|
558
692
|
}
|
|
559
|
-
/**
|
|
560
|
-
|
|
561
|
-
|
|
693
|
+
/**
|
|
694
|
+
* A browser process died: drop the leases it held (they're invalid) and clear its slot so the next
|
|
695
|
+
* acquire of that kind relaunches it. The other browser's leases live on.
|
|
696
|
+
*/
|
|
697
|
+
#onCrash(slot, crashed) {
|
|
698
|
+
if (slot.browser !== crashed)
|
|
562
699
|
return; // a stale handler from a prior browser
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
700
|
+
slot.browser = undefined;
|
|
701
|
+
let freed = 0;
|
|
702
|
+
for (const [id, lease] of this.#active) {
|
|
703
|
+
if (lease.headed !== slot.headed)
|
|
704
|
+
continue;
|
|
705
|
+
this.#active.delete(id);
|
|
706
|
+
freed += 1;
|
|
707
|
+
}
|
|
708
|
+
// A lease mid-acquire on the dead browser holds a slot too; it frees it in its own catch. When
|
|
709
|
+
// every lease belonged to this browser, nothing else can be holding one.
|
|
710
|
+
this.#occupied = 0 === this.#active.size ? 0 : Math.max(0, this.#occupied - freed);
|
|
566
711
|
for (const waiter of this.#waiters.splice(0))
|
|
567
712
|
waiter(); // let them re-claim + relaunch
|
|
568
713
|
}
|
|
714
|
+
/** Whether this lease's context skips certificate checks. A context cannot change it later. */
|
|
715
|
+
ignoresHTTPSErrors(sessionId) {
|
|
716
|
+
return true === this.#active.get(this.#leaseIdOf(sessionId))?.ignoresHTTPSErrors;
|
|
717
|
+
}
|
|
718
|
+
/** Whether this lease lives in the headed browser. */
|
|
719
|
+
isHeaded(sessionId) {
|
|
720
|
+
return true === this.#active.get(this.#leaseIdOf(sessionId))?.headed;
|
|
721
|
+
}
|
|
569
722
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser permissions on a leased context: granted before the first navigation, and read back where
|
|
3
|
+
* the browser can contradict the grant.
|
|
4
|
+
*
|
|
5
|
+
* Never changed on a lease that is already open. Acquire hands an origin's live lease to whoever asks
|
|
6
|
+
* for that origin, so the lease may be another agent's tab mid-flow, and nothing here knows whose it
|
|
7
|
+
* is. A different set means a fresh lease.
|
|
8
|
+
*
|
|
9
|
+
* Each grant is scoped to the origin the lease was opened on, so a third-party frame the app embeds
|
|
10
|
+
* is not handed the same grant as the app.
|
|
11
|
+
*
|
|
12
|
+
* Notifications are why the read-back exists. Playwright's headless shell — what the pool launches
|
|
13
|
+
* when Playwright's own Chromium is installed — has no notification support: after a grant,
|
|
14
|
+
* `navigator.permissions.query` reports `granted` while `Notification.permission` still reads
|
|
15
|
+
* `denied`. A page that gates its UI on the second never sees the grant, so a lease that reported
|
|
16
|
+
* "granted" there would be reporting something the page cannot see.
|
|
17
|
+
*/
|
|
18
|
+
import type { PooledContext } from './pool-contract.js';
|
|
19
|
+
/** The permission whose grant the headless shell does not pass on to `Notification.permission`. */
|
|
20
|
+
export declare const NOTIFICATIONS_PERMISSION = "notifications";
|
|
21
|
+
/** Evaluated in the leased page. A string, because the pool's page surface evaluates scripts. */
|
|
22
|
+
export declare const NOTIFICATION_PERMISSION_READ = "typeof Notification === 'undefined' ? 'unsupported' : Notification.permission";
|
|
23
|
+
/**
|
|
24
|
+
* How long a read-back may take. Bounded because acquire waits on it, and a wedged renderer never
|
|
25
|
+
* answers an evaluation: an unbounded one would turn "this tab is stuck" into "acquire hangs".
|
|
26
|
+
*/
|
|
27
|
+
export declare const NOTIFICATION_READ_TIMEOUT_MS = 1500;
|
|
28
|
+
/** A permission could not be granted or cleared. Its message is written for the agent. */
|
|
29
|
+
export declare class LeasePermissionError extends Error {
|
|
30
|
+
readonly name = "LeasePermissionError";
|
|
31
|
+
}
|
|
32
|
+
/** Grant `permissions` on the origin of `url`. Nothing to grant is a no-op, not a refusal. */
|
|
33
|
+
export declare function grantLeasePermissions(context: PooledContext, permissions: readonly string[], url: string): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** The permission whose grant the headless shell does not pass on to `Notification.permission`. */
|
|
2
|
+
export const NOTIFICATIONS_PERMISSION = 'notifications';
|
|
3
|
+
/** Evaluated in the leased page. A string, because the pool's page surface evaluates scripts. */
|
|
4
|
+
export const NOTIFICATION_PERMISSION_READ = "typeof Notification === 'undefined' ? 'unsupported' : Notification.permission";
|
|
5
|
+
/**
|
|
6
|
+
* How long a read-back may take. Bounded because acquire waits on it, and a wedged renderer never
|
|
7
|
+
* answers an evaluation: an unbounded one would turn "this tab is stuck" into "acquire hangs".
|
|
8
|
+
*/
|
|
9
|
+
export const NOTIFICATION_READ_TIMEOUT_MS = 1_500;
|
|
10
|
+
/** Playwright prefixes its errors with the call that failed; the agent needs only the reason. */
|
|
11
|
+
const PLAYWRIGHT_CALL_PREFIX = /^browserContext\.\w+:\s*/;
|
|
12
|
+
/** A permission could not be granted or cleared. Its message is written for the agent. */
|
|
13
|
+
export class LeasePermissionError extends Error {
|
|
14
|
+
name = 'LeasePermissionError';
|
|
15
|
+
}
|
|
16
|
+
function reasonOf(err) {
|
|
17
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18
|
+
return (message.split('\n')[0] ?? message).replace(PLAYWRIGHT_CALL_PREFIX, '');
|
|
19
|
+
}
|
|
20
|
+
/** Grant `permissions` on the origin of `url`. Nothing to grant is a no-op, not a refusal. */
|
|
21
|
+
export async function grantLeasePermissions(context, permissions, url) {
|
|
22
|
+
if (0 === permissions.length)
|
|
23
|
+
return;
|
|
24
|
+
if (context.grantPermissions === undefined) {
|
|
25
|
+
throw new LeasePermissionError('this browser context cannot grant permissions');
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
await context.grantPermissions([...permissions], { origin: new URL(url).origin });
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
throw new LeasePermissionError(`could not grant ${permissions.join(', ')}: ${reasonOf(err)}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PooledPage } from './pool-contract.js';
|
|
2
|
+
interface LeaseMarkerArg {
|
|
3
|
+
session: string;
|
|
4
|
+
project?: string | undefined;
|
|
5
|
+
targetOrigin: string;
|
|
6
|
+
sessionParam: string;
|
|
7
|
+
projectParam: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Init script restoring the marker a server redirect dropped, so the SDK registers under the lease id.
|
|
11
|
+
* It runs only in pages of the lease's own browser context, so a person's tab at the same URL never
|
|
12
|
+
* carries the marker and is never adopted. It writes only in the top frame, only on the lease's own
|
|
13
|
+
* target origin (never an identity provider's or any third party's URL), and the pool installs it
|
|
14
|
+
* only when the navigated URL's marker matches this lease's id. Must stay self-contained: Playwright
|
|
15
|
+
* serialises the function source into the page.
|
|
16
|
+
*/
|
|
17
|
+
export declare function stampLeaseMarker(arg: LeaseMarkerArg): void;
|
|
18
|
+
export declare function installLeaseMarker(page: PooledPage, leaseId: string, targetOrigin: string | undefined, project: string | undefined): Promise<void>;
|
|
19
|
+
export declare function leaseMarkerOf(url: string): {
|
|
20
|
+
session: string;
|
|
21
|
+
project: string | undefined;
|
|
22
|
+
} | undefined;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { RETICLE_URL_PARAM } from '@reticlehq/core';
|
|
2
|
+
/**
|
|
3
|
+
* Init script restoring the marker a server redirect dropped, so the SDK registers under the lease id.
|
|
4
|
+
* It runs only in pages of the lease's own browser context, so a person's tab at the same URL never
|
|
5
|
+
* carries the marker and is never adopted. It writes only in the top frame, only on the lease's own
|
|
6
|
+
* target origin (never an identity provider's or any third party's URL), and the pool installs it
|
|
7
|
+
* only when the navigated URL's marker matches this lease's id. Must stay self-contained: Playwright
|
|
8
|
+
* serialises the function source into the page.
|
|
9
|
+
*/
|
|
10
|
+
export function stampLeaseMarker(arg) {
|
|
11
|
+
const win = globalThis.window;
|
|
12
|
+
if (win === undefined || win.top !== win || win.location.origin !== arg.targetOrigin)
|
|
13
|
+
return;
|
|
14
|
+
try {
|
|
15
|
+
const url = new URL(win.location.href);
|
|
16
|
+
if (url.searchParams.has(arg.sessionParam))
|
|
17
|
+
return;
|
|
18
|
+
url.searchParams.set(arg.sessionParam, arg.session);
|
|
19
|
+
if (arg.project !== undefined && !url.searchParams.has(arg.projectParam)) {
|
|
20
|
+
url.searchParams.set(arg.projectParam, arg.project);
|
|
21
|
+
}
|
|
22
|
+
win.history.replaceState(null, '', url.toString());
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function installLeaseMarker(page, leaseId, targetOrigin, project) {
|
|
29
|
+
if (page.addInitScript === undefined || targetOrigin === undefined)
|
|
30
|
+
return;
|
|
31
|
+
await page.addInitScript(stampLeaseMarker, {
|
|
32
|
+
session: leaseId,
|
|
33
|
+
project,
|
|
34
|
+
targetOrigin,
|
|
35
|
+
sessionParam: RETICLE_URL_PARAM.SESSION,
|
|
36
|
+
projectParam: RETICLE_URL_PARAM.PROJECT,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function leaseMarkerOf(url) {
|
|
40
|
+
try {
|
|
41
|
+
const params = new URL(url).searchParams;
|
|
42
|
+
const session = params.get(RETICLE_URL_PARAM.SESSION) ?? '';
|
|
43
|
+
const project = params.get(RETICLE_URL_PARAM.PROJECT) ?? '';
|
|
44
|
+
return '' === session ? undefined : { session, project: '' === project ? undefined : project };
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -6,6 +6,41 @@
|
|
|
6
6
|
* Headless by default — the pool exists for fault-tolerant headless multi-agent testing.
|
|
7
7
|
*/
|
|
8
8
|
import type { Launcher } from './browser-pool.js';
|
|
9
|
+
import type { ScreenshotOptions } from './pool-contract.js';
|
|
10
|
+
/**
|
|
11
|
+
* Reticle paints its own UI (presenter HUD, glow, annotator marks, tour) into the page. It is
|
|
12
|
+
* time-varying, since the activity log and border state change with every command, so capturing it
|
|
13
|
+
* makes a fresh screenshot of an unchanged page differ from its baseline. Playwright applies `style`
|
|
14
|
+
* for the shot only and then reverts it, so the page under test is never changed. Disabling animations
|
|
15
|
+
* settles any remaining transitions. Shared by every capture path, driven and leased, so both hide
|
|
16
|
+
* the same set: core's `RETICLE_OVERLAY_SELECTOR`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SCREENSHOT_DETERMINISM: {
|
|
19
|
+
readonly style: "[data-reticle-overlay],[data-reticle-cursor],[data-reticle-hud],[data-reticle-glow],[data-reticle-mark],[data-reticle-blocker],[data-reticle-tour]{display:none !important}";
|
|
20
|
+
readonly animations: "disabled";
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* How Playwright captures a leased tab: a PNG by default, a JPEG for a live picture. Every capture
|
|
24
|
+
* hides Reticle's own UI with `SCREENSHOT_DETERMINISM`, the same as the driven path, so a lease
|
|
25
|
+
* baseline shows the app and not the HUD.
|
|
26
|
+
*/
|
|
27
|
+
export declare function screenshotOptions(opts: ScreenshotOptions): {
|
|
28
|
+
fullPage: boolean;
|
|
29
|
+
style?: string;
|
|
30
|
+
animations?: 'disabled';
|
|
31
|
+
type?: 'jpeg';
|
|
32
|
+
quality?: number;
|
|
33
|
+
scale?: 'css';
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* A step screenshot with Reticle's own UI out of it. The step path injects nothing into the page
|
|
37
|
+
* (no stylesheet, no fast-forwarded animation), so the HUD covered a third of every picture. Only
|
|
38
|
+
* our own hosts change, by their own inline `visibility`, for this one capture, and are put back
|
|
39
|
+
* whatever the capture did: the app's DOM, styles and events are not touched.
|
|
40
|
+
*/
|
|
41
|
+
export declare function captureWithChromeHidden(page: {
|
|
42
|
+
evaluate: (script: string) => Promise<unknown>;
|
|
43
|
+
}, capture: () => Promise<Uint8Array>): Promise<Uint8Array>;
|
|
9
44
|
/**
|
|
10
45
|
* How a leased tab navigates. Pure, and exported, because the decision in it is worth a test while
|
|
11
46
|
* the rest of this file needs a real Chromium.
|
|
@@ -6,11 +6,61 @@
|
|
|
6
6
|
* Headless by default — the pool exists for fault-tolerant headless multi-agent testing.
|
|
7
7
|
*/
|
|
8
8
|
import { BrowserLaunchKind } from '@reticlehq/core/telemetry';
|
|
9
|
+
import { HIDE_RETICLE_CHROME_CSS, RETICLE_OVERLAY_SELECTOR } from '@reticlehq/core';
|
|
9
10
|
import { launchChromium } from '../../launch-chromium.js';
|
|
10
11
|
import { getSessionMetrics } from '../../telemetry/session-metrics.js';
|
|
11
12
|
import { classifyConnectFailure } from '../../telemetry/connect-failure.js';
|
|
12
13
|
import { chromiumInstallCommand, chromiumInstallDepsCommand, bundledPlaywrightVersion, } from '../../command/cli/doctor/browser/chromium-hint.js';
|
|
13
14
|
import { installNetworkMocks } from '../input/network-mock.js';
|
|
15
|
+
/**
|
|
16
|
+
* Reticle paints its own UI (presenter HUD, glow, annotator marks, tour) into the page. It is
|
|
17
|
+
* time-varying, since the activity log and border state change with every command, so capturing it
|
|
18
|
+
* makes a fresh screenshot of an unchanged page differ from its baseline. Playwright applies `style`
|
|
19
|
+
* for the shot only and then reverts it, so the page under test is never changed. Disabling animations
|
|
20
|
+
* settles any remaining transitions. Shared by every capture path, driven and leased, so both hide
|
|
21
|
+
* the same set: core's `RETICLE_OVERLAY_SELECTOR`.
|
|
22
|
+
*/
|
|
23
|
+
export const SCREENSHOT_DETERMINISM = {
|
|
24
|
+
style: HIDE_RETICLE_CHROME_CSS,
|
|
25
|
+
animations: 'disabled',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* How Playwright captures a leased tab: a PNG by default, a JPEG for a live picture. Every capture
|
|
29
|
+
* hides Reticle's own UI with `SCREENSHOT_DETERMINISM`, the same as the driven path, so a lease
|
|
30
|
+
* baseline shows the app and not the HUD.
|
|
31
|
+
*/
|
|
32
|
+
export function screenshotOptions(opts) {
|
|
33
|
+
// A step screenshot is evidence about the page, so it must not change it: `animations: disabled`
|
|
34
|
+
// fast-forwards running animations to their end and fires their events in the app under test.
|
|
35
|
+
if (true === opts.cssScale && opts.jpegQuality !== undefined) {
|
|
36
|
+
return { fullPage: false, type: 'jpeg', quality: opts.jpegQuality, scale: 'css' };
|
|
37
|
+
}
|
|
38
|
+
const base = { ...SCREENSHOT_DETERMINISM, fullPage: true === opts.fullPage };
|
|
39
|
+
return opts.jpegQuality === undefined
|
|
40
|
+
? base
|
|
41
|
+
: { ...base, type: 'jpeg', quality: opts.jpegQuality };
|
|
42
|
+
}
|
|
43
|
+
/** Where the hidden hosts' own visibility waits to be put back, on the page's window. */
|
|
44
|
+
const SHOT_HIDDEN_KEY = '__reticleShotHidden';
|
|
45
|
+
/** A string literal safe to splice into a script: JSON plus the characters that end one early. */
|
|
46
|
+
const scriptLiteral = (value) => JSON.stringify(value).replace(/[<>/\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
|
|
47
|
+
const HIDE_CHROME_SCRIPT = `(() => { const held = []; for (const el of document.querySelectorAll(${scriptLiteral(RETICLE_OVERLAY_SELECTOR)})) { held.push([el, el.style.visibility]); el.style.visibility = 'hidden'; } window.${SHOT_HIDDEN_KEY} = held; })()`;
|
|
48
|
+
const RESTORE_CHROME_SCRIPT = `(() => { for (const [el, was] of window.${SHOT_HIDDEN_KEY} ?? []) el.style.visibility = was; delete window.${SHOT_HIDDEN_KEY}; })()`;
|
|
49
|
+
/**
|
|
50
|
+
* A step screenshot with Reticle's own UI out of it. The step path injects nothing into the page
|
|
51
|
+
* (no stylesheet, no fast-forwarded animation), so the HUD covered a third of every picture. Only
|
|
52
|
+
* our own hosts change, by their own inline `visibility`, for this one capture, and are put back
|
|
53
|
+
* whatever the capture did: the app's DOM, styles and events are not touched.
|
|
54
|
+
*/
|
|
55
|
+
export async function captureWithChromeHidden(page, capture) {
|
|
56
|
+
await page.evaluate(HIDE_CHROME_SCRIPT);
|
|
57
|
+
try {
|
|
58
|
+
return await capture();
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
await page.evaluate(RESTORE_CHROME_SCRIPT).catch(() => undefined);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
14
64
|
/**
|
|
15
65
|
* How a leased tab navigates. Pure, and exported, because the decision in it is worth a test while
|
|
16
66
|
* the rest of this file needs a real Chromium.
|
|
@@ -33,8 +83,8 @@ export function gotoOptions(timeoutMs) {
|
|
|
33
83
|
function wrapBrowser(browser) {
|
|
34
84
|
return {
|
|
35
85
|
isConnected: () => browser.isConnected(),
|
|
36
|
-
newContext: async () => {
|
|
37
|
-
const context = await browser.newContext();
|
|
86
|
+
newContext: async (opts) => {
|
|
87
|
+
const context = await browser.newContext(true === opts?.ignoreHTTPSErrors ? { ignoreHTTPSErrors: true } : undefined);
|
|
38
88
|
return {
|
|
39
89
|
newPage: async () => {
|
|
40
90
|
const page = await context.newPage();
|
|
@@ -43,7 +93,15 @@ function wrapBrowser(browser) {
|
|
|
43
93
|
close: () => page.close(),
|
|
44
94
|
evaluate: (script) => page.evaluate(script),
|
|
45
95
|
// Playwright returns a Buffer; Uint8Array is what the visual store and differ take.
|
|
46
|
-
|
|
96
|
+
// Same determinism as the driven path's capturePage: without it a lease baseline caught
|
|
97
|
+
// the HUD, its activity log and the toolbar, and a HUD change read as a regression (#1355).
|
|
98
|
+
screenshot: async (opts) => {
|
|
99
|
+
const shoot = async () => new Uint8Array(await page.screenshot(screenshotOptions(opts ?? {})));
|
|
100
|
+
// A step capture injects no stylesheet, so our own hosts are hidden another way.
|
|
101
|
+
return true === opts?.cssScale
|
|
102
|
+
? captureWithChromeHidden({ evaluate: (script) => page.evaluate(script) }, shoot)
|
|
103
|
+
: shoot();
|
|
104
|
+
},
|
|
47
105
|
// Three moves, same as performGesture: a single move to the center can be a no-op if
|
|
48
106
|
// the pointer was already there, and CSS :hover needs a native hit-test to apply.
|
|
49
107
|
hover: async (x, y) => {
|
|
@@ -53,6 +111,7 @@ function wrapBrowser(browser) {
|
|
|
53
111
|
},
|
|
54
112
|
installMocks: (rules) => installNetworkMocks(page, [...rules]),
|
|
55
113
|
setViewport: (size) => page.setViewportSize(size),
|
|
114
|
+
bringToFront: () => page.bringToFront(),
|
|
56
115
|
onCrash: (handler) => page.on('crash', handler),
|
|
57
116
|
onConsole: (handler) => page.on('console', (msg) => handler(msg.text())),
|
|
58
117
|
onDialog: (handler) => page.on('dialog', (dialog) => handler({ message: dialog.message(), dismiss: () => dialog.dismiss() })),
|
|
@@ -67,6 +126,8 @@ function wrapBrowser(browser) {
|
|
|
67
126
|
};
|
|
68
127
|
},
|
|
69
128
|
addCookies: (cookies) => context.addCookies(cookies),
|
|
129
|
+
grantPermissions: (permissions, opts) => context.grantPermissions(permissions, opts),
|
|
130
|
+
clearPermissions: () => context.clearPermissions(),
|
|
70
131
|
close: () => context.close(),
|
|
71
132
|
};
|
|
72
133
|
},
|