@reticlehq/server 3.6.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/SKILL.md +122 -123
- 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 +18 -2
- package/dist/command/cli/adhoc-verdict.js +70 -6
- 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/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 +28 -105
- 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 +34 -4
- package/dist/command/cli/cli-verify.js +81 -12
- package/dist/command/cli/cloud-cli.d.ts +13 -0
- package/dist/command/cli/cloud-cli.js +145 -333
- 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 +15 -1
- package/dist/command/cli/cloud-login.js +67 -13
- 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/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/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 +11 -2
- 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.js +1 -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 +29 -8
- 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 -178
- 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 +14 -0
- package/dist/command/cli.js +103 -83
- package/dist/command/daemon/daemon-resilience.js +4 -4
- package/dist/command/daemon/daemon-resolve.d.ts +9 -1
- package/dist/command/daemon/daemon-resolve.js +49 -10
- 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/dev-server-log-cap.js +2 -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 +10 -1
- package/dist/command/setup/init/init-port.js +22 -2
- package/dist/command/setup/init/init-runtime.d.ts +12 -2
- package/dist/command/setup/init/init-runtime.js +96 -37
- package/dist/command/setup/node-effects.d.ts +2 -0
- package/dist/command/setup/node-effects.js +36 -8
- 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 +1 -1
- package/dist/command/setup/run-setup.d.ts +29 -9
- package/dist/command/setup/run-setup.js +34 -28
- package/dist/command/setup/setup-command.d.ts +23 -0
- package/dist/command/setup/setup-command.js +57 -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 +38 -2
- package/dist/features/crawl/crawl.js +14 -1
- package/dist/features/exhaust/ledger.d.ts +14 -14
- package/dist/features/harness/drive-report.d.ts +39 -5
- package/dist/features/harness/drive-report.js +271 -54
- 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 +1 -1
- package/dist/features/harness/drivers.js +1 -1
- 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/goals.d.ts +68 -7
- package/dist/features/harness/goals.js +289 -36
- package/dist/features/harness/harness.d.ts +30 -1
- package/dist/features/harness/harness.js +141 -7
- package/dist/features/harness/platform/local-apps.d.ts +36 -3
- package/dist/features/harness/platform/local-apps.js +81 -5
- 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 +8 -2
- package/dist/features/harness/platform/remote-drive.js +22 -9
- package/dist/features/harness/platform/script.d.ts +57 -11
- package/dist/features/harness/platform/script.js +144 -12
- package/dist/features/harness/platform/server-driver.d.ts +17 -4
- package/dist/features/harness/platform/server-driver.js +29 -26
- package/dist/features/harness/platform-config.d.ts +38 -5
- package/dist/features/harness/platform-config.js +93 -8
- package/dist/features/harness/route-guess.d.ts +13 -0
- package/dist/features/harness/route-guess.js +49 -0
- package/dist/features/harness/script/script-run.d.ts +30 -1
- package/dist/features/harness/script/script-run.js +355 -48
- 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.js +22 -14
- 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 +21 -2
- package/dist/hooks/driven-by.js +42 -2
- package/dist/hooks/hook-bus.d.ts +4 -0
- package/dist/hooks/hook-bus.js +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +26 -183
- 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 +20 -4
- 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.js +25 -9
- package/dist/judgement/runs/artifact/run-store.d.ts +13 -1
- package/dist/judgement/runs/artifact/run-store.js +175 -8
- 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 +53 -5
- package/dist/judgement/runs/drive-run.js +234 -20
- package/dist/judgement/runs/resume.d.ts +44 -0
- package/dist/judgement/runs/resume.js +137 -0
- package/dist/judgement/runs/reticle-runner.js +5 -2
- 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 +2 -0
- package/dist/judgement/runs/verification-sync.js +36 -15
- package/dist/language/flows/decision.js +2 -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 +7 -2
- package/dist/language/flows/flow-anchor.js +10 -3
- 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 +37 -3
- package/dist/language/flows/flow-replay-run.js +16 -18
- package/dist/language/flows/flow-replay-types.d.ts +1 -1
- package/dist/language/flows/flow-replay.d.ts +18 -1
- package/dist/language/flows/flow-replay.js +22 -7
- package/dist/language/flows/flow-result.d.ts +22 -1
- package/dist/language/flows/flow-result.js +47 -2
- package/dist/language/flows/flow-step-runners.d.ts +2 -0
- package/dist/language/flows/flow-step-runners.js +12 -6
- package/dist/language/flows/flow-store-for-session.js +5 -1
- package/dist/language/flows/flow-tools.d.ts +10 -0
- package/dist/language/flows/flow-tools.js +61 -62
- package/dist/language/flows/flows.d.ts +16 -1
- package/dist/language/flows/flows.js +69 -10
- package/dist/language/flows/narrow-write.d.ts +7 -0
- package/dist/language/flows/narrow-write.js +36 -1
- package/dist/language/flows/recording/recording-backtrack.js +14 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +29 -1
- package/dist/language/flows/recording/tape/recordings.js +60 -10
- package/dist/language/flows/replay.js +29 -6
- package/dist/language/flows/single-replay.d.ts +7 -0
- package/dist/language/flows/single-replay.js +26 -0
- package/dist/language/flows/verify-change-tools.d.ts +1 -0
- package/dist/language/flows/verify-change-tools.js +200 -193
- package/dist/memory/cloud/cloud-config.d.ts +10 -1
- package/dist/memory/cloud/cloud-config.js +19 -5
- package/dist/memory/cloud/cloud-sync.js +3 -3
- package/dist/memory/cloud/harness-config.d.ts +49 -0
- package/dist/memory/cloud/harness-config.js +16 -20
- package/dist/memory/cloud/memory-scope.d.ts +17 -0
- package/dist/memory/cloud/memory-scope.js +22 -0
- package/dist/memory/cloud/sync-cycle.d.ts +32 -4
- package/dist/memory/cloud/sync-cycle.js +113 -12
- package/dist/memory/cloud/sync-daemon.d.ts +11 -0
- package/dist/memory/cloud/sync-daemon.js +114 -4
- package/dist/memory/cloud/sync-disk.js +26 -2
- 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 +16 -1
- package/dist/memory/impact/account-watch.js +26 -1
- package/dist/memory/impact/impact-store.d.ts +1 -4
- package/dist/memory/impact/impact-store.js +32 -42
- 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 +12 -0
- package/dist/memory/intent/intent-store.d.ts +1 -0
- package/dist/memory/intent/intent-tools.js +42 -2
- 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/journal-recorder.js +46 -3
- package/dist/memory/journal/on-disk/retention.d.ts +5 -0
- package/dist/memory/journal/on-disk/retention.js +27 -2
- package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
- package/dist/memory/journal/session-end.d.ts +15 -1
- package/dist/memory/journal/session-end.js +38 -7
- package/dist/memory/project/artifact-root-resolver.js +33 -5
- package/dist/memory/project/artifact-root.d.ts +4 -3
- package/dist/memory/project/artifact-root.js +2 -2
- package/dist/memory/project/dir/reticle-dir.d.ts +14 -0
- package/dist/memory/project/dir/reticle-dir.js +23 -2
- package/dist/memory/project/link-directory.d.ts +15 -0
- package/dist/memory/project/link-directory.js +44 -0
- 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 +2 -0
- package/dist/memory/project/session-root.js +9 -1
- package/dist/memory/project/sync-status.d.ts +46 -0
- package/dist/memory/project/sync-status.js +135 -1
- package/dist/memory/recall/memory-tools.js +46 -3
- 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.js +36 -8
- package/dist/portal/bridge/bridge.d.ts +9 -5
- package/dist/portal/bridge/bridge.js +66 -23
- package/dist/portal/bridge/harness-request.d.ts +23 -0
- package/dist/portal/bridge/harness-request.js +27 -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 +2 -0
- package/dist/portal/input/real-input.js +13 -10
- package/dist/portal/pool/browser-pool.d.ts +48 -0
- package/dist/portal/pool/browser-pool.js +120 -2
- 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 +27 -5
- package/dist/portal/pool/playwright-launcher.js +59 -14
- package/dist/portal/pool/pool-contract.d.ts +21 -1
- 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.js +6 -1
- 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 +9 -0
- package/dist/portal/session/dev-server/dev-command.js +27 -1
- 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/human/live-control.d.ts +9 -2
- package/dist/portal/session/human/live-control.js +12 -3
- package/dist/portal/session/live-control-tools.js +82 -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 +1 -1
- package/dist/portal/session/no-session-diagnosis.js +31 -9
- package/dist/portal/session/no-session-next-action.d.ts +20 -0
- package/dist/portal/session/no-session-next-action.js +134 -61
- package/dist/portal/session/no-session-watch.js +179 -22
- package/dist/portal/session/presence/session-lease.js +1 -1
- package/dist/portal/session/recorded-gaps.d.ts +97 -0
- package/dist/portal/session/recorded-gaps.js +251 -0
- package/dist/portal/session/session-health.d.ts +11 -0
- package/dist/portal/session/session-health.js +3 -1
- package/dist/portal/session/session-info.js +2 -1
- package/dist/portal/session/session-manager.d.ts +18 -0
- package/dist/portal/session/session-manager.js +54 -3
- package/dist/portal/session/session-successor.d.ts +8 -0
- package/dist/portal/session/session-successor.js +1 -1
- package/dist/portal/session/session.d.ts +15 -28
- package/dist/portal/session/session.js +60 -61
- 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 +7 -1
- package/dist/start-wiring.d.ts +63 -0
- package/dist/start-wiring.js +169 -0
- package/dist/status-payload.d.ts +13 -1
- package/dist/status-payload.js +13 -1
- package/dist/surface/http-server.js +7 -1
- 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 +4 -0
- package/dist/surface/mcp/mcp-proxy.js +29 -8
- package/dist/surface/mcp/mcp.d.ts +23 -0
- package/dist/surface/mcp/mcp.js +34 -4
- 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.js +22 -14
- package/dist/surface/mcp/shared-params.js +1 -1
- 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/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-sequence-tool.js +136 -27
- package/dist/surface/tools/act-tools.d.ts +0 -9
- package/dist/surface/tools/act-tools.js +48 -34
- 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.js +25 -15
- package/dist/surface/tools/browser-tools.js +43 -1
- package/dist/surface/tools/chat-drives.d.ts +75 -0
- package/dist/surface/tools/chat-drives.js +265 -21
- 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 +10 -0
- package/dist/surface/tools/empty-session-list.js +45 -5
- package/dist/surface/tools/error-recovery.d.ts +8 -0
- package/dist/surface/tools/error-recovery.js +69 -5
- package/dist/surface/tools/explore-tools.d.ts +23 -2
- package/dist/surface/tools/explore-tools.js +377 -74
- package/dist/surface/tools/feedback-tools.d.ts +2 -3
- package/dist/surface/tools/feedback-tools.js +4 -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 +52 -73
- package/dist/surface/tools/harness-explore.js +177 -146
- 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 +5 -0
- package/dist/surface/tools/harness-plan.js +7 -0
- package/dist/surface/tools/harness-script.d.ts +22 -0
- package/dist/surface/tools/harness-script.js +256 -38
- 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 +8 -0
- package/dist/surface/tools/harness-toolset.js +27 -4
- 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 +220 -93
- 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 +20 -0
- package/dist/surface/tools/lease-seed.js +45 -1
- package/dist/surface/tools/lease-tools.d.ts +10 -1
- package/dist/surface/tools/lease-tools.js +119 -69
- package/dist/surface/tools/live-call-text.js +2 -2
- 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 +11 -1
- package/dist/surface/tools/next-step.js +73 -18
- package/dist/surface/tools/observe-tools.js +43 -5
- package/dist/surface/tools/platform-moment.d.ts +60 -0
- package/dist/surface/tools/platform-moment.js +132 -4
- package/dist/surface/tools/read-tools.js +65 -14
- 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 +68 -2
- package/dist/surface/tools/tool-kit.js +73 -1
- package/dist/surface/tools/tools.js +25 -13
- 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 +27 -6
- package/dist/telemetry/hud-metrics.js +99 -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 +26 -2
- package/dist/telemetry/telemetry.js +102 -21
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +12 -4
- package/dist/wire-journal.d.ts +2 -2
- package/dist/wire-journal.js +16 -5
- package/docs/README.md +2 -2
- package/docs/actions.mdx +6 -3
- package/docs/adapters.md +1 -1
- package/docs/agent-cheatsheet.md +24 -27
- package/docs/architecture.md +5 -5
- package/docs/autodrive.md +13 -12
- 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 +8 -3
- package/docs/cli/drive.mdx +12 -12
- 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 +32 -15
- 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 +13 -4
- 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 +3 -2
- package/docs/cli.mdx +110 -41
- package/docs/debugging.md +3 -3
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +5 -3
- package/docs/enterprise.md +11 -10
- package/docs/environment.mdx +5 -2
- 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 +66 -64
- package/docs/human-control.md +15 -14
- package/docs/index.mdx +15 -43
- package/docs/install-agentic.mdx +22 -37
- package/docs/install-manual.mdx +22 -12
- 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 +8 -8
- package/docs/packages/test.mdx +4 -10
- package/docs/packages/vite-plugin.mdx +11 -4
- package/docs/packages.mdx +11 -15
- package/docs/platform-integration.md +6 -5
- package/docs/predicates.mdx +40 -24
- package/docs/quickstart.mdx +58 -29
- 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 +22 -24
- 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 +13 -4
- 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 +51 -45
- 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 +7 -5
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +6 -5
- package/dist/command/cli/demo/demo-page.d.ts +0 -78
- package/dist/command/cli/demo/demo-page.js +0 -249
- package/dist/command/cli/demo/demo-run.d.ts +0 -38
- package/dist/command/cli/demo/demo-run.js +0 -138
- 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/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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `permissions` argument of `reticle_lease { action: "acquire" }` (#1370).
|
|
3
|
+
*
|
|
4
|
+
* Granted before the first navigation of a fresh lease. On an origin that already has a live lease,
|
|
5
|
+
* acquire hands back THAT lease rather than opening a second tab, and it may be another agent's tab
|
|
6
|
+
* mid-flow — nothing at this layer knows whose it is. So its grants never change there: the same list
|
|
7
|
+
* is a plain reuse, a different one is refused with the way out (release, then acquire again).
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { ReticleTool } from '@reticlehq/core';
|
|
11
|
+
import { LeasePermissionError, NOTIFICATIONS_PERMISSION, } from '../../portal/pool/context-permissions.js';
|
|
12
|
+
export const LEASE_PERMISSIONS_ARG = z
|
|
13
|
+
.array(z.string().min(1))
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Browser permissions to grant on the app origin before the first navigation, e.g. ["geolocation", "clipboard-read"]. A fresh lease starts with none granted. An origin that already has a lease keeps its grants: a different list there is refused, so release it and acquire again to change them. Names are Playwright\'s, and one it does not know is refused.');
|
|
16
|
+
/** The family member these refusals come from, named the way `open-note.ts` names it. */
|
|
17
|
+
const LeaseAction = { ACQUIRE: 'acquire' };
|
|
18
|
+
/** How this tool's refusals name the call: built from `ReticleTool.LEASE`, not spelled out. */
|
|
19
|
+
const ACQUIRE = `${ReticleTool.LEASE}{action:"${LeaseAction.ACQUIRE}"}`;
|
|
20
|
+
const INVALID_PERMISSIONS = `${ACQUIRE} permissions must be a list of permission names, e.g. ["geolocation"]`;
|
|
21
|
+
/** The one `Notification.permission` value that means the page can see a notifications grant. */
|
|
22
|
+
const GRANTED = 'granted';
|
|
23
|
+
/** The validated list, undefined when the caller passed none, or a refusal naming the right shape. */
|
|
24
|
+
export function parseLeasePermissions(raw) {
|
|
25
|
+
if (raw === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
const parsed = LEASE_PERMISSIONS_ARG.safeParse(raw);
|
|
28
|
+
if (!parsed.success)
|
|
29
|
+
throw new Error(INVALID_PERMISSIONS);
|
|
30
|
+
return parsed.data;
|
|
31
|
+
}
|
|
32
|
+
/** A permission failure, reworded for the agent; undefined for every other error. */
|
|
33
|
+
export function permissionRefusal(err) {
|
|
34
|
+
return err instanceof LeasePermissionError ? new Error(`${ACQUIRE} ${err.message}`) : undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* What to tell the agent when the page cannot see a notifications grant, else undefined.
|
|
38
|
+
*
|
|
39
|
+
* Undefined too when the page could not be asked: the grant was made, and a reading nobody took is
|
|
40
|
+
* not evidence that it failed.
|
|
41
|
+
*/
|
|
42
|
+
export async function notificationReadBack(pool, sessionId, permissions) {
|
|
43
|
+
if (true !== permissions?.includes(NOTIFICATIONS_PERMISSION))
|
|
44
|
+
return undefined;
|
|
45
|
+
const reading = await pool.notificationPermission(sessionId);
|
|
46
|
+
if (reading === undefined || GRANTED === reading)
|
|
47
|
+
return undefined;
|
|
48
|
+
return (`notifications was granted, but this page's Notification.permission still reads "${reading}": ` +
|
|
49
|
+
'the pooled headless browser does not support notifications, so UI gated on ' +
|
|
50
|
+
'Notification.permission will not see the grant. navigator.permissions.query({ name: "notifications" }) does report it.');
|
|
51
|
+
}
|
|
52
|
+
const sameSet = (a, b) => a.length === b.length &&
|
|
53
|
+
a.every((name) => b.includes(name)) &&
|
|
54
|
+
b.every((name) => a.includes(name));
|
|
55
|
+
/**
|
|
56
|
+
* Refuse to reuse a held lease under a different set of grants. Nothing to refuse when the caller
|
|
57
|
+
* named no list, or the same one in any order.
|
|
58
|
+
*/
|
|
59
|
+
export function refuseRegrant(pool, sessionId, permissions) {
|
|
60
|
+
if (permissions === undefined)
|
|
61
|
+
return;
|
|
62
|
+
const held = pool.permissionsOf(sessionId);
|
|
63
|
+
if (held === undefined || sameSet(permissions, held))
|
|
64
|
+
return;
|
|
65
|
+
throw new Error(`${ACQUIRE} this origin's lease ${sessionId} already holds [${held.join(', ')}], and it may be ` +
|
|
66
|
+
"another agent's live tab, so its grants are not changed in place. If it is yours, release it " +
|
|
67
|
+
'and acquire again with the new list.');
|
|
68
|
+
}
|
|
69
|
+
/** `{ hint }` joining whichever parts are present, or nothing at all. */
|
|
70
|
+
export function hintOf(...parts) {
|
|
71
|
+
const present = parts.filter((part) => part !== undefined);
|
|
72
|
+
return 0 === present.length ? {} : { hint: present.join(' ') };
|
|
73
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is a leased tab usable right now? The checks `reticle_lease` runs before it may say `ready: true`.
|
|
3
|
+
* Presence in the sessions map is not enough: the tab has to answer, and it has to be visible.
|
|
4
|
+
*/
|
|
5
|
+
/** The narrow slice of a session the probe needs. Anything that quacks like this works. */
|
|
6
|
+
interface ProbeableSession {
|
|
7
|
+
command?: (name: string, args: Record<string, unknown>, timeoutMs: number) => Promise<unknown>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Did the SDK report its tab hidden? A hidden tab answers commands while its timers and rAF are
|
|
11
|
+
* throttled, so a lease on it is `ready` by every other test and verifies nothing (#1351).
|
|
12
|
+
*
|
|
13
|
+
* Known gap: this reads the last PAGE_HEALTH, which lands just after HELLO — a check in that gap sees the
|
|
14
|
+
* default `false`. The pool foregrounding the page is what covers the gap; this catches the rest.
|
|
15
|
+
*/
|
|
16
|
+
export declare function tabHidden(session: unknown): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Does this tab still answer?
|
|
19
|
+
*
|
|
20
|
+
* PRESENCE IS NOT LIVENESS. The sessions map still holds a tab that is attached, streaming events,
|
|
21
|
+
* and answering nothing, so `ready` read off a row in that map hands back a lease that `snapshot`,
|
|
22
|
+
* `state` and `console` then reject (#692). It has to mean the tab replied.
|
|
23
|
+
*
|
|
24
|
+
* ANY reply counts, including one that reports the command failed. The question is whether the SDK
|
|
25
|
+
* answers at all, not what it says — so an SDK too old to know the command replies
|
|
26
|
+
* `unknown command '…'`, and that is proof. Only the absence of a reply is evidence of absence:
|
|
27
|
+
* `PendingCommands.track` REJECTS on timeout and on a dropped socket, and resolves on every real
|
|
28
|
+
* answer, so the two cases are already separated for us.
|
|
29
|
+
*
|
|
30
|
+
* `CAPABILITIES` is the probe because it takes no arguments and returns a small fixed list. No new
|
|
31
|
+
* wire command is introduced: this rides an existing round trip.
|
|
32
|
+
*
|
|
33
|
+
* Fails OPEN. A registry entry with no `command` is a shape this code did not put there, and
|
|
34
|
+
* turning a lease that works into a refusal over a probe that could not run would be a worse
|
|
35
|
+
* failure than the one being fixed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function probeLeaseAlive(session: ProbeableSession | undefined, timeoutMs?: number): Promise<boolean>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is a leased tab usable right now? The checks `reticle_lease` runs before it may say `ready: true`.
|
|
3
|
+
* Presence in the sessions map is not enough: the tab has to answer, and it has to be visible.
|
|
4
|
+
*/
|
|
5
|
+
import { ReticleCommand } from '@reticlehq/core';
|
|
6
|
+
/**
|
|
7
|
+
* How long a liveness probe waits for the tab to say anything at all.
|
|
8
|
+
*
|
|
9
|
+
* Short on purpose. This is not "finish the work", it is "are you there" — a page executing
|
|
10
|
+
* JavaScript answers a no-argument command in single-digit milliseconds, and a wedged one is not
|
|
11
|
+
* going to answer in two seconds either.
|
|
12
|
+
*/
|
|
13
|
+
const LEASE_PROBE_TIMEOUT_MS = 1_500;
|
|
14
|
+
/**
|
|
15
|
+
* Did the SDK report its tab hidden? A hidden tab answers commands while its timers and rAF are
|
|
16
|
+
* throttled, so a lease on it is `ready` by every other test and verifies nothing (#1351).
|
|
17
|
+
*
|
|
18
|
+
* Known gap: this reads the last PAGE_HEALTH, which lands just after HELLO — a check in that gap sees the
|
|
19
|
+
* default `false`. The pool foregrounding the page is what covers the gap; this catches the rest.
|
|
20
|
+
*/
|
|
21
|
+
export function tabHidden(session) {
|
|
22
|
+
return (null !== session &&
|
|
23
|
+
'object' === typeof session &&
|
|
24
|
+
true === session.info?.().hidden);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Does this tab still answer?
|
|
28
|
+
*
|
|
29
|
+
* PRESENCE IS NOT LIVENESS. The sessions map still holds a tab that is attached, streaming events,
|
|
30
|
+
* and answering nothing, so `ready` read off a row in that map hands back a lease that `snapshot`,
|
|
31
|
+
* `state` and `console` then reject (#692). It has to mean the tab replied.
|
|
32
|
+
*
|
|
33
|
+
* ANY reply counts, including one that reports the command failed. The question is whether the SDK
|
|
34
|
+
* answers at all, not what it says — so an SDK too old to know the command replies
|
|
35
|
+
* `unknown command '…'`, and that is proof. Only the absence of a reply is evidence of absence:
|
|
36
|
+
* `PendingCommands.track` REJECTS on timeout and on a dropped socket, and resolves on every real
|
|
37
|
+
* answer, so the two cases are already separated for us.
|
|
38
|
+
*
|
|
39
|
+
* `CAPABILITIES` is the probe because it takes no arguments and returns a small fixed list. No new
|
|
40
|
+
* wire command is introduced: this rides an existing round trip.
|
|
41
|
+
*
|
|
42
|
+
* Fails OPEN. A registry entry with no `command` is a shape this code did not put there, and
|
|
43
|
+
* turning a lease that works into a refusal over a probe that could not run would be a worse
|
|
44
|
+
* failure than the one being fixed.
|
|
45
|
+
*/
|
|
46
|
+
export async function probeLeaseAlive(session, timeoutMs = LEASE_PROBE_TIMEOUT_MS) {
|
|
47
|
+
const send = session?.command;
|
|
48
|
+
if (send === undefined)
|
|
49
|
+
return true;
|
|
50
|
+
try {
|
|
51
|
+
await send.call(session, ReticleCommand.CAPABILITIES, {}, timeoutMs);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -15,7 +15,27 @@ export declare function looksLikeStorageStateExport(seed: unknown): boolean;
|
|
|
15
15
|
* with ANSI codes) into a short, clean reason — so the agent/user sees "is the app running?" instead
|
|
16
16
|
* of an internals-leaking wall of text.
|
|
17
17
|
*/
|
|
18
|
+
/** What {@link cleanNavError} answers for a navigation that ran out of time. */
|
|
19
|
+
export declare const NAV_TIMED_OUT = "navigation timed out";
|
|
18
20
|
export declare function cleanNavError(err: unknown, seed?: SeedStorage): string;
|
|
21
|
+
/** What a lease whose navigation hit a certificate error tells the caller. */
|
|
22
|
+
export declare const NAV_CERT_REFUSED: string;
|
|
23
|
+
/** The question a failed lease navigation asks, when nothing says the app is up. */
|
|
24
|
+
export declare const NAV_FAILED_QUESTION = "is the app running there?";
|
|
25
|
+
/** What a lease navigation that ran out of time tells the caller to do. */
|
|
26
|
+
export declare const NAV_TIMEOUT_ADVICE: string;
|
|
27
|
+
/**
|
|
28
|
+
* The sentence for a lease whose first navigation failed.
|
|
29
|
+
*
|
|
30
|
+
* A timeout and a refusal are different stories and get different sentences. A refusal
|
|
31
|
+
* (`net::ERR_CONNECTION_REFUSED` and the like) means nothing is serving there, so asking whether the
|
|
32
|
+
* app is running is the right question. A timeout proves less — only that the page was not ready in
|
|
33
|
+
* time — and its most common cause is a dev server compiling a route for the first time, which
|
|
34
|
+
* routinely outlasts the navigation budget. Asking "is the app running?" there sends the agent
|
|
35
|
+
* hunting for a dev server that is up instead of retrying, so the timeout sentence names both
|
|
36
|
+
* cases and leads with the retry.
|
|
37
|
+
*/
|
|
38
|
+
export declare function navFailureMessage(url: string, err: unknown, seed?: SeedStorage): string;
|
|
19
39
|
/**
|
|
20
40
|
* Determine if a required seedStorage application precondition failed.
|
|
21
41
|
*
|
|
@@ -65,6 +65,8 @@ export function looksLikeStorageStateExport(seed) {
|
|
|
65
65
|
* with ANSI codes) into a short, clean reason — so the agent/user sees "is the app running?" instead
|
|
66
66
|
* of an internals-leaking wall of text.
|
|
67
67
|
*/
|
|
68
|
+
/** What {@link cleanNavError} answers for a navigation that ran out of time. */
|
|
69
|
+
export const NAV_TIMED_OUT = 'navigation timed out';
|
|
68
70
|
export function cleanNavError(err, seed) {
|
|
69
71
|
const rawMsg = err instanceof Error ? err.message : String(err);
|
|
70
72
|
const msg = scrubSeedFromError(rawMsg, seed);
|
|
@@ -76,13 +78,55 @@ export function cleanNavError(err, seed) {
|
|
|
76
78
|
if (netCode !== null)
|
|
77
79
|
return netCode[0];
|
|
78
80
|
if (/timeout/i.test(firstLine))
|
|
79
|
-
return
|
|
81
|
+
return NAV_TIMED_OUT;
|
|
80
82
|
return firstLine
|
|
81
83
|
.replace(/^page\.goto:\s*/, '')
|
|
82
84
|
.replace(/\s+at\s+https?:\/\/\S+.*$/, '')
|
|
83
85
|
.trim()
|
|
84
86
|
.slice(0, 100);
|
|
85
87
|
}
|
|
88
|
+
/** Chromium's certificate failures: `net::ERR_CERT_AUTHORITY_INVALID`, `ERR_CERT_DATE_INVALID`, … */
|
|
89
|
+
const CERT_ERROR = /^net::ERR_CERT_/;
|
|
90
|
+
/** What a lease whose navigation hit a certificate error tells the caller. */
|
|
91
|
+
export const NAV_CERT_REFUSED = 'the browser refused its TLS certificate, which a self-signed or mkcert dev certificate gets. ' +
|
|
92
|
+
'If it is your dev server, acquire again with ignoreHTTPSErrors: true to accept it for this lease';
|
|
93
|
+
/** The question a failed lease navigation asks, when nothing says the app is up. */
|
|
94
|
+
export const NAV_FAILED_QUESTION = 'is the app running there?';
|
|
95
|
+
/** What a lease navigation that ran out of time tells the caller to do. */
|
|
96
|
+
export const NAV_TIMEOUT_ADVICE = 'If the app is running, a dev server compiling a route for the first time can take longer than ' +
|
|
97
|
+
'that: retry the acquire. If it is not running, start it first.';
|
|
98
|
+
/** Lead of the timeout sentence, before the budget ("within 30 s", or "in time" when unknown). */
|
|
99
|
+
const NAV_TIMEOUT_LEAD = 'the page did not finish loading';
|
|
100
|
+
const NAV_TIMEOUT_UNKNOWN_BUDGET = 'in time';
|
|
101
|
+
/** Playwright's own wording for a navigation that ran out of time, with the budget it used. */
|
|
102
|
+
const PLAYWRIGHT_TIMEOUT = /Timeout (\d+)ms exceeded/;
|
|
103
|
+
/**
|
|
104
|
+
* The sentence for a lease whose first navigation failed.
|
|
105
|
+
*
|
|
106
|
+
* A timeout and a refusal are different stories and get different sentences. A refusal
|
|
107
|
+
* (`net::ERR_CONNECTION_REFUSED` and the like) means nothing is serving there, so asking whether the
|
|
108
|
+
* app is running is the right question. A timeout proves less — only that the page was not ready in
|
|
109
|
+
* time — and its most common cause is a dev server compiling a route for the first time, which
|
|
110
|
+
* routinely outlasts the navigation budget. Asking "is the app running?" there sends the agent
|
|
111
|
+
* hunting for a dev server that is up instead of retrying, so the timeout sentence names both
|
|
112
|
+
* cases and leads with the retry.
|
|
113
|
+
*/
|
|
114
|
+
export function navFailureMessage(url, err, seed) {
|
|
115
|
+
const reason = cleanNavError(err, seed);
|
|
116
|
+
// A certificate the browser refused is not an app that is down: the server answered, with a cert
|
|
117
|
+
// no CA here vouches for (a self-signed or mkcert dev certificate). Asking "is the app running?"
|
|
118
|
+
// sent the caller to restart a dev server that was fine (#1255).
|
|
119
|
+
if (CERT_ERROR.test(reason))
|
|
120
|
+
return `could not open ${url} — ${NAV_CERT_REFUSED} (${reason})`;
|
|
121
|
+
if (NAV_TIMED_OUT !== reason)
|
|
122
|
+
return `could not open ${url} — ${NAV_FAILED_QUESTION} (${reason})`;
|
|
123
|
+
const rawMsg = err instanceof Error ? err.message : String(err);
|
|
124
|
+
const budgetMs = PLAYWRIGHT_TIMEOUT.exec(rawMsg)?.[1];
|
|
125
|
+
const within = budgetMs === undefined
|
|
126
|
+
? NAV_TIMEOUT_UNKNOWN_BUDGET
|
|
127
|
+
: `within ${String(Number(budgetMs) / 1000)} s`;
|
|
128
|
+
return `could not open ${url} — ${NAV_TIMEOUT_LEAD} ${within} (${reason}). ${NAV_TIMEOUT_ADVICE}`;
|
|
129
|
+
}
|
|
86
130
|
/**
|
|
87
131
|
* Determine if a required seedStorage application precondition failed.
|
|
88
132
|
*
|
|
@@ -15,7 +15,8 @@ import { type NetworkMutationPort } from '../../portal/input/network-mutation.js
|
|
|
15
15
|
import type { Perturbation } from '@reticlehq/core';
|
|
16
16
|
import { type SeedStorage } from '@reticlehq/core';
|
|
17
17
|
import { HudVisibility } from '@reticlehq/core';
|
|
18
|
-
import type
|
|
18
|
+
import { type ToolDef, type ToolDeps } from './tool-kit.js';
|
|
19
|
+
import type { BrowserPool } from '../../portal/pool/browser-pool.js';
|
|
19
20
|
/**
|
|
20
21
|
* Append Reticle identity params (__reticle_session, optional __reticle_project) to a URL so the app's own SDK
|
|
21
22
|
* adopts them on connect. Pure; falls back to plain concatenation if the URL can't be parsed.
|
|
@@ -112,6 +113,14 @@ export declare function connectOrInject(lease: {
|
|
|
112
113
|
}>;
|
|
113
114
|
export declare function hasOriginLock(origin: string): boolean;
|
|
114
115
|
export declare const LEASE_ACQUIRE_TOOL: ToolDef;
|
|
116
|
+
/**
|
|
117
|
+
* Release one lease and everything that hangs off it. Returns how many leases are still held.
|
|
118
|
+
*
|
|
119
|
+
* Shared with `reticle_session { action: "end" }`: an ended session that kept its lease stayed
|
|
120
|
+
* connected until the reaper took it, and every id-less call from the next agent was refused as
|
|
121
|
+
* ambiguous in the meantime.
|
|
122
|
+
*/
|
|
123
|
+
export declare function releaseLease(deps: ToolDeps, pool: BrowserPool, sessionId: string): Promise<number>;
|
|
115
124
|
export declare const LEASE_TOOLS: ToolDef[];
|
|
116
125
|
/**
|
|
117
126
|
* The state a suite should boot its flows from: whatever the agent is already sitting in.
|
|
@@ -13,6 +13,7 @@ import { fixturePortFor, seedFromStorageState, } from '../../portal/input/storag
|
|
|
13
13
|
import { mutationPortFor } from '../../portal/input/network-mutation.js';
|
|
14
14
|
import { z } from 'zod';
|
|
15
15
|
import { leaseNotConnectedHint } from './lease-hint.js';
|
|
16
|
+
import { probeLeaseAlive, tabHidden } from './lease-readiness.js';
|
|
16
17
|
import { probeSdkMarker } from './gaps/sdk-marker-probe.js';
|
|
17
18
|
import { observeWebDocument } from '../../portal/session/dev-server/served-document.js';
|
|
18
19
|
import { diagnoseObservedWebCsp } from '@reticlehq/init';
|
|
@@ -20,10 +21,13 @@ import { readProjectFramework, readProjectId } from '../../command/cli/ports/res
|
|
|
20
21
|
import { hasAnyAppConnectedBefore } from '../../memory/recall/prior/connection-memory.js';
|
|
21
22
|
import { AGENT_DRIVING_ELSEWHERE, AGENT_DRIVING_HERE_AGAIN, watchersToNotify, } from '../../portal/session/lease-visibility.js';
|
|
22
23
|
import { reticleStateHome } from '../../command/daemon/daemon.js';
|
|
23
|
-
import {
|
|
24
|
+
import { claimArtifactRoot, dropArtifactRootClaim } from '../../memory/project/root-claims.js';
|
|
25
|
+
import { LeaseNotReadyReason, RETICLE_URL_PARAM, RETICLE_DEFAULT_PORT, SeedStorageSchema, } from '@reticlehq/core';
|
|
24
26
|
import { HudVisibility, ReticleTool, SESSION_HEALTH } from '@reticlehq/core';
|
|
27
|
+
import { HOST_STUB_NOTE } from './tool-kit.js';
|
|
25
28
|
import { asString } from '@reticlehq/core';
|
|
26
29
|
import { chromiumPreflightRefusal } from '../../command/cli/doctor/browser/chromium-hint.js';
|
|
30
|
+
import { LEASE_PERMISSIONS_ARG, hintOf, notificationReadBack, parseLeasePermissions, permissionRefusal, refuseRegrant, } from './lease-permissions.js';
|
|
27
31
|
/**
|
|
28
32
|
* Everything the daemon already knows about why a leased tab might not have dialled in.
|
|
29
33
|
*
|
|
@@ -89,7 +93,9 @@ function originOf(url) {
|
|
|
89
93
|
const ALREADY_HELD_LEASE = 'you already hold a lease on this origin';
|
|
90
94
|
function alreadyHeldHint(sessionId, origin) {
|
|
91
95
|
return (`${ALREADY_HELD_LEASE} (${origin}): ${sessionId} — reused it rather than opening a second ` +
|
|
92
|
-
'tab, which would make every later call require sessionId.
|
|
96
|
+
'tab, which would make every later call require sessionId. It is still signed in as whoever ' +
|
|
97
|
+
'used it last (cookies and storage come with it): release it first for a fresh context or ' +
|
|
98
|
+
'another identity.');
|
|
93
99
|
}
|
|
94
100
|
/**
|
|
95
101
|
* Append Reticle identity params (__reticle_session, optional __reticle_project) to a URL so the app's own SDK
|
|
@@ -115,7 +121,7 @@ export function appendReticleParams(url, session, projectId, hud) {
|
|
|
115
121
|
}
|
|
116
122
|
}
|
|
117
123
|
export { cleanNavError, evaluateSeedPrecondition, scrubSeedFromError } from './lease-seed.js';
|
|
118
|
-
import {
|
|
124
|
+
import { evaluateSeedPrecondition, looksLikeStorageStateExport, navFailureMessage, scrubSeedFromError, } from './lease-seed.js';
|
|
119
125
|
/** A fresh, collision-resistant lease id. Uses crypto at the I/O boundary (not pure logic). */
|
|
120
126
|
function newLeaseId() {
|
|
121
127
|
const uuid = 'function' === typeof globalThis.crypto?.randomUUID
|
|
@@ -186,46 +192,6 @@ function sessionParamOf(url) {
|
|
|
186
192
|
}
|
|
187
193
|
const LEASE_READY_ATTEMPTS = 100;
|
|
188
194
|
const LEASE_READY_POLL_MS = 100;
|
|
189
|
-
/**
|
|
190
|
-
* How long a liveness probe waits for the tab to say anything at all.
|
|
191
|
-
*
|
|
192
|
-
* Short on purpose. This is not "finish the work", it is "are you there" — a page executing
|
|
193
|
-
* JavaScript answers a no-argument command in single-digit milliseconds, and a wedged one is not
|
|
194
|
-
* going to answer in two seconds either.
|
|
195
|
-
*/
|
|
196
|
-
const LEASE_PROBE_TIMEOUT_MS = 1_500;
|
|
197
|
-
/**
|
|
198
|
-
* Does this tab still answer?
|
|
199
|
-
*
|
|
200
|
-
* PRESENCE IS NOT LIVENESS. The sessions map still holds a tab that is attached, streaming events,
|
|
201
|
-
* and answering nothing, so `ready` read off a row in that map hands back a lease that `snapshot`,
|
|
202
|
-
* `state` and `console` then reject (#692). It has to mean the tab replied.
|
|
203
|
-
*
|
|
204
|
-
* ANY reply counts, including one that reports the command failed. The question is whether the SDK
|
|
205
|
-
* answers at all, not what it says — so an SDK too old to know the command replies
|
|
206
|
-
* `unknown command '…'`, and that is proof. Only the absence of a reply is evidence of absence:
|
|
207
|
-
* `PendingCommands.track` REJECTS on timeout and on a dropped socket, and resolves on every real
|
|
208
|
-
* answer, so the two cases are already separated for us.
|
|
209
|
-
*
|
|
210
|
-
* `CAPABILITIES` is the probe because it takes no arguments and returns a small fixed list. No new
|
|
211
|
-
* wire command is introduced: this rides an existing round trip.
|
|
212
|
-
*
|
|
213
|
-
* Fails OPEN. A registry entry with no `command` is a shape this code did not put there, and
|
|
214
|
-
* turning a lease that works into a refusal over a probe that could not run would be a worse
|
|
215
|
-
* failure than the one being fixed.
|
|
216
|
-
*/
|
|
217
|
-
async function probeLeaseAlive(session, timeoutMs = LEASE_PROBE_TIMEOUT_MS) {
|
|
218
|
-
const send = session?.command;
|
|
219
|
-
if (send === undefined)
|
|
220
|
-
return true;
|
|
221
|
-
try {
|
|
222
|
-
await send.call(session, ReticleCommand.CAPABILITIES, {}, timeoutMs);
|
|
223
|
-
return true;
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
195
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
230
196
|
/**
|
|
231
197
|
* Poll until the leased tab's SDK has actually registered on the bridge, so the sessionId we return
|
|
@@ -314,7 +280,7 @@ export function hasOriginLock(origin) {
|
|
|
314
280
|
}
|
|
315
281
|
export const LEASE_ACQUIRE_TOOL = {
|
|
316
282
|
name: ReticleTool.LEASE_ACQUIRE,
|
|
317
|
-
description: 'Lease a fresh isolated
|
|
283
|
+
description: 'Lease a fresh isolated browser context from the shared pool (shown or hidden as the daemon was started; `headed: true` forces a window) and navigate it to the app URL (the app must already be running; one with no Reticle SDK gets one supplied, reported as zeroInstall). If this origin is already leased and still connected, this returns THAT session rather than minting a second tab — a second acquire on the same origin poisons default session resolution. Returns the sessionId the leased tab registers — pass it to other tools. The pool keeps all leases in ONE browser and caps concurrency; if at capacity this waits for a free slot. Release with reticle_lease{action:"release"} when the flow is done. PREFER AN ALREADY-OPEN TAB: if reticle_sessions lists a non-leased session for this app, drive THAT instead — a lease is invisible to the person watching the app, whose HUD lives in their own tab, and a tab flagged hidden/throttled is often still driveable. Lease for isolation you actually need (a second identity, a clean context, parallel flows) or when driving the open tab has failed — this call answers with `preferExisting` when a live tab was available.',
|
|
318
284
|
inputSchema: {
|
|
319
285
|
url: z
|
|
320
286
|
.string()
|
|
@@ -324,29 +290,50 @@ export const LEASE_ACQUIRE_TOOL = {
|
|
|
324
290
|
.optional()
|
|
325
291
|
.describe('Stable project id to stamp on the leased tab so the agent can scope to it.'),
|
|
326
292
|
seedStorage: SeedStorageSchema.optional().describe('Initial storage state (local, session and cookies) to seed before the first navigation (e.g. `seedStorage: { local: { token: "..." } }` to start already authenticated).'),
|
|
293
|
+
initScript: z
|
|
294
|
+
.string()
|
|
295
|
+
.optional()
|
|
296
|
+
.describe("JavaScript installed in the tab BEFORE any of the page's own scripts run. Use it for TEST DOUBLES of the host bindings a desktop shell injects — Wails `window.go.*`, Tauri `window.runtime`/`__TAURI_INTERNALS__`, an Electron preload object — so a view that calls them on mount is driven against the stub instead of throwing. It stays on for this lease only, and it is disposed with the lease. Every verdict on this lease carries a note that a stub was installed, so a green is never read as one against the real shell."),
|
|
327
297
|
headed: z
|
|
328
298
|
.boolean()
|
|
329
299
|
.optional()
|
|
330
300
|
.describe('Open it in a browser window somebody can watch, instead of headless.'),
|
|
301
|
+
ignoreHTTPSErrors: z
|
|
302
|
+
.boolean()
|
|
303
|
+
.optional()
|
|
304
|
+
.describe('Accept a self-signed or mkcert certificate on an https dev server, for this lease only. Off by default.'),
|
|
331
305
|
hud: z
|
|
332
306
|
.enum([HudVisibility.SHOWN, HudVisibility.HIDDEN, HudVisibility.REMOVED])
|
|
333
307
|
.optional()
|
|
334
308
|
.describe('How the HUD starts on the page: shown (default), hidden, or removed.'),
|
|
309
|
+
permissions: LEASE_PERMISSIONS_ARG,
|
|
310
|
+
root: z
|
|
311
|
+
.string()
|
|
312
|
+
.optional()
|
|
313
|
+
.describe("Absolute path of the .reticle folder this lease's runs belong in — the caller's project. Without it, the page's project id decides, and a page with none lands in ~/.reticle/unmatched, which nothing syncs."),
|
|
335
314
|
},
|
|
336
315
|
outputSchema: {
|
|
337
316
|
sessionId: z.string(),
|
|
338
317
|
url: z.string(),
|
|
339
318
|
ready: z
|
|
340
319
|
.boolean()
|
|
341
|
-
.describe('Whether the leased tab is connected AND answering.
|
|
320
|
+
.describe('Whether the leased tab is connected AND answering. It is NOT a claim that the app has hydrated, so an act can miss: make the first act reticle_act_and_wait with an `until`, or reticle_assert { action: "wait" }. Empty reads may mean not hydrated. False ⇒ read notReadyReason.'),
|
|
342
321
|
zeroInstall: z
|
|
343
322
|
.boolean()
|
|
344
323
|
.optional()
|
|
345
324
|
.describe('Present when this app ships no Reticle SDK and the lease supplied one. Verdicts work on the DOM, network, console and routes; there is no framework adapter, so no component state and no source file:line. Install with `npx @reticlehq/server init` for those.'),
|
|
325
|
+
hostStub: z
|
|
326
|
+
.string()
|
|
327
|
+
.optional()
|
|
328
|
+
.describe('Present when this leased tab runs an init script stubbing the bindings a desktop shell injects. Verdicts on this lease are about the app with that stub, not about the real shell.'),
|
|
346
329
|
notReadyReason: z
|
|
347
|
-
.enum([
|
|
330
|
+
.enum([
|
|
331
|
+
LeaseNotReadyReason.SDK_NEVER_DIALLED,
|
|
332
|
+
LeaseNotReadyReason.SDK_STOPPED_ANSWERING,
|
|
333
|
+
LeaseNotReadyReason.TAB_HIDDEN,
|
|
334
|
+
])
|
|
348
335
|
.optional()
|
|
349
|
-
.describe('Present only when ready is false. sdk_never_dialled ⇒ nothing connected within the wait, so check the install (the app may not embed @reticlehq/core). sdk_stopped_answering ⇒ an SDK did connect and has stopped replying, so the tab is wedged and needs recovering, not reinstalling.'),
|
|
336
|
+
.describe('Present only when ready is false. sdk_never_dialled ⇒ nothing connected within the wait, so check the install (the app may not embed @reticlehq/core). sdk_stopped_answering ⇒ an SDK did connect and has stopped replying, so the tab is wedged and needs recovering, not reinstalling. tab_hidden ⇒ the SDK answers but its tab is hidden, so timers and animations are throttled and nothing on it verifies; bring the tab to the front, or release and re-acquire.'),
|
|
350
337
|
expiresInMs: z
|
|
351
338
|
.number()
|
|
352
339
|
.describe('Milliseconds until this lease expires if untouched. Each tool call that targets this session resets the clock. Plan your work to finish or re-acquire before this runs out.'),
|
|
@@ -386,8 +373,13 @@ export const LEASE_ACQUIRE_TOOL = {
|
|
|
386
373
|
throw new Error(refusal);
|
|
387
374
|
}
|
|
388
375
|
const projectId = asString(args['projectId']);
|
|
376
|
+
const root = asString(args['root']);
|
|
389
377
|
const headed = true === args['headed'];
|
|
390
378
|
const hud = Object.values(HudVisibility).find((v) => v === args['hud']);
|
|
379
|
+
const permissions = parseLeasePermissions(args['permissions']);
|
|
380
|
+
// The stub of the shell bindings, when the caller supplied one. Installed by the pool before
|
|
381
|
+
// the first navigation, so it is already defined when the app's first module runs.
|
|
382
|
+
const initScript = asString(args['initScript']);
|
|
391
383
|
const seedStorageArg = args['seedStorage'];
|
|
392
384
|
let validatedSeed;
|
|
393
385
|
if (seedStorageArg !== undefined) {
|
|
@@ -423,10 +415,31 @@ export const LEASE_ACQUIRE_TOOL = {
|
|
|
423
415
|
try {
|
|
424
416
|
// Only a lease THIS caller took: another agent's tab is never reused or released (#1226).
|
|
425
417
|
// A window asked for is never answered with the headless tab on that origin, nor the reverse.
|
|
418
|
+
// A held lease that already runs the SAME stub is exactly what the caller asked for, so it is
|
|
419
|
+
// left alone and reused below — releasing it would throw away the tab, and everything loaded
|
|
420
|
+
// in it, for nothing. A lease with no stub, or with a different one, is released FIRST instead:
|
|
421
|
+
// an init script has to exist before the page's own scripts run, which a loaded page cannot
|
|
422
|
+
// do, and the resolve below then finds nothing and mints a fresh context with the stub. A
|
|
423
|
+
// lease of the other kind is left alone, exactly as it is when a window was asked for.
|
|
424
|
+
const heldForStub = origin === undefined ? undefined : pool.leaseIdOnOrigin(origin, deps.attachId);
|
|
425
|
+
if (initScript !== undefined &&
|
|
426
|
+
heldForStub !== undefined &&
|
|
427
|
+
pool.isHeaded(heldForStub) === headed &&
|
|
428
|
+
pool.hostStubScript?.(heldForStub) !== initScript) {
|
|
429
|
+
await pool.release(heldForStub);
|
|
430
|
+
}
|
|
426
431
|
const held = origin === undefined ? undefined : pool.leaseIdOnOrigin(origin, deps.attachId);
|
|
427
432
|
const existing = held !== undefined && pool.isHeaded(held) === headed ? held : undefined;
|
|
428
|
-
|
|
429
|
-
|
|
433
|
+
// A context's certificate setting is fixed when it is made, so an explicit request for the
|
|
434
|
+
// other one cannot be answered by the held lease: handing it back would keep checks off after
|
|
435
|
+
// `false`, or keep a local certificate refused after `true` (#1255). Omitted keeps the lease.
|
|
436
|
+
const certAsked = args['ignoreHTTPSErrors'];
|
|
437
|
+
const certMismatch = existing !== undefined &&
|
|
438
|
+
'boolean' === typeof certAsked &&
|
|
439
|
+
pool.ignoresHTTPSErrors(existing) !== certAsked;
|
|
440
|
+
if ((validatedSeed !== undefined || certMismatch) && existing !== undefined) {
|
|
441
|
+
// Seeding or a different certificate setting asked for: release its own context so the new
|
|
442
|
+
// lease starts with what was asked.
|
|
430
443
|
await pool.release(existing);
|
|
431
444
|
}
|
|
432
445
|
else if (existing !== undefined && origin !== undefined) {
|
|
@@ -443,23 +456,33 @@ export const LEASE_ACQUIRE_TOOL = {
|
|
|
443
456
|
// A no-op when the id resolved to itself; load-bearing when it did not, because every later
|
|
444
457
|
// touch and release arrives under the id being handed back here.
|
|
445
458
|
pool.alias(resolved, existing);
|
|
459
|
+
refuseRegrant(pool, existing, permissions);
|
|
446
460
|
pool.touch(existing);
|
|
447
461
|
// Probed only HERE, never on the mint path below. There, the readiness wait resolved
|
|
448
462
|
// moments ago and IS the liveness evidence; on reuse the last evidence may be minutes
|
|
449
463
|
// old, or there may be none at all — `unresponsive` is set by past commands failing, and
|
|
450
464
|
// its own contract says absence means "answering, OR NOT ASKED YET". So the happy path of
|
|
451
465
|
// a first acquire pays nothing for this.
|
|
452
|
-
const
|
|
466
|
+
const reusedSession = deps.sessions.get(resolved);
|
|
467
|
+
const alive = await probeLeaseAlive(reusedSession);
|
|
468
|
+
const reuseReason = !alive
|
|
469
|
+
? LeaseNotReadyReason.SDK_STOPPED_ANSWERING
|
|
470
|
+
: tabHidden(reusedSession)
|
|
471
|
+
? LeaseNotReadyReason.TAB_HIDDEN
|
|
472
|
+
: undefined;
|
|
453
473
|
return {
|
|
454
474
|
sessionId: resolved,
|
|
455
475
|
url,
|
|
456
|
-
ready:
|
|
457
|
-
...(
|
|
476
|
+
ready: reuseReason === undefined,
|
|
477
|
+
...(reuseReason === undefined ? {} : { notReadyReason: reuseReason }),
|
|
458
478
|
reused: true,
|
|
459
479
|
expiresInMs: pool.leaseTtlMs(),
|
|
460
480
|
leased: pool.activeCount(),
|
|
461
481
|
queued: pool.queuedCount(),
|
|
462
482
|
hint: alreadyHeldHint(resolved, origin),
|
|
483
|
+
// Asked of the pool, not remembered here: the stub was installed when THIS lease was
|
|
484
|
+
// minted, and the caller reusing it now may be seeing it for the first time.
|
|
485
|
+
...(true === pool.stubInstalled?.(existing) ? { hostStub: HOST_STUB_NOTE } : {}),
|
|
463
486
|
...(alreadyOpen === undefined
|
|
464
487
|
? {}
|
|
465
488
|
: { preferExisting: { sessionId: alreadyOpen, note: PREFER_EXISTING_NOTE } }),
|
|
@@ -471,6 +494,9 @@ export const LEASE_ACQUIRE_TOOL = {
|
|
|
471
494
|
await pool.release(existing);
|
|
472
495
|
}
|
|
473
496
|
const sessionId = newLeaseId();
|
|
497
|
+
// Before the page can dial in: the session is stamped with its root the moment it registers.
|
|
498
|
+
if (root !== undefined)
|
|
499
|
+
claimArtifactRoot(sessionId, root);
|
|
474
500
|
const navUrl = appendReticleParams(url, sessionId, projectId, hud);
|
|
475
501
|
let lease;
|
|
476
502
|
try {
|
|
@@ -479,15 +505,21 @@ export const LEASE_ACQUIRE_TOOL = {
|
|
|
479
505
|
...(headed ? { headed } : {}),
|
|
480
506
|
...(deps.attachId === undefined ? {} : { owner: deps.attachId }),
|
|
481
507
|
...(validatedSeed !== undefined ? { seedStorage: validatedSeed } : {}),
|
|
508
|
+
...(initScript !== undefined ? { initScript } : {}),
|
|
509
|
+
...(permissions !== undefined ? { permissions } : {}),
|
|
510
|
+
...(true === args['ignoreHTTPSErrors'] ? { ignoreHTTPSErrors: true } : {}),
|
|
482
511
|
});
|
|
483
512
|
}
|
|
484
513
|
catch (err) {
|
|
514
|
+
const refusal = permissionRefusal(err);
|
|
515
|
+
if (refusal !== undefined)
|
|
516
|
+
throw refusal;
|
|
485
517
|
if (err instanceof Error && err.message.startsWith('Storage seeding failed')) {
|
|
486
518
|
throw new Error(scrubSeedFromError(err.message, validatedSeed));
|
|
487
519
|
}
|
|
488
520
|
// A raw page.goto failure is noisy and leaks the internal URL params — surface a clean,
|
|
489
|
-
// actionable message instead.
|
|
490
|
-
throw new Error(
|
|
521
|
+
// actionable message instead, one that tells a slow first compile from a missing app.
|
|
522
|
+
throw new Error(navFailureMessage(url, err, validatedSeed));
|
|
491
523
|
}
|
|
492
524
|
// Wait for the leased tab's SDK to connect so the returned sessionId is usable right away.
|
|
493
525
|
// Resolved rather than assumed: an app that names its own session registers under that name,
|
|
@@ -515,15 +547,21 @@ export const LEASE_ACQUIRE_TOOL = {
|
|
|
515
547
|
tellWatchers(deps, projectId, AGENT_DRIVING_ELSEWHERE);
|
|
516
548
|
// ready means the SDK dialled in — not that contracts match. Carry the skew warning on acquire
|
|
517
549
|
// so the agent does not learn it only after a CDP tool invents a closed page (#688).
|
|
550
|
+
const mintReason = !ready
|
|
551
|
+
? LeaseNotReadyReason.SDK_NEVER_DIALLED
|
|
552
|
+
: tabHidden(session)
|
|
553
|
+
? LeaseNotReadyReason.TAB_HIDDEN
|
|
554
|
+
: undefined;
|
|
518
555
|
const versionSkew = registeredId === undefined ? undefined : deps.sessions.get(registeredId)?.versionSkew;
|
|
519
556
|
return {
|
|
520
557
|
sessionId: registeredId ?? lease.sessionId,
|
|
521
558
|
url,
|
|
522
|
-
ready,
|
|
559
|
+
ready: mintReason === undefined,
|
|
523
560
|
// The other half of the pair. `ready: false` carried two opposite situations under one
|
|
524
561
|
// word: no SDK ever dialled in (look at the install) versus one dialled in and stopped
|
|
525
|
-
// answering (recover the tab). They want different next actions, so they get names
|
|
526
|
-
|
|
562
|
+
// answering (recover the tab). They want different next actions, so they get names — and so
|
|
563
|
+
// does a third: dialled in and answering, but hidden, so nothing on it verifies (#1351).
|
|
564
|
+
...(mintReason === undefined ? {} : { notReadyReason: mintReason }),
|
|
527
565
|
...(zeroInstall ? { zeroInstall: true } : {}),
|
|
528
566
|
expiresInMs: pool.leaseTtlMs(),
|
|
529
567
|
leased: pool.activeCount(),
|
|
@@ -537,9 +575,10 @@ export const LEASE_ACQUIRE_TOOL = {
|
|
|
537
575
|
},
|
|
538
576
|
}),
|
|
539
577
|
...(versionSkew === undefined ? {} : { versionSkew }),
|
|
540
|
-
...(
|
|
541
|
-
|
|
542
|
-
|
|
578
|
+
...(initScript === undefined ? {} : { hostStub: HOST_STUB_NOTE }),
|
|
579
|
+
...hintOf(ready
|
|
580
|
+
? undefined
|
|
581
|
+
: await notConnectedHint(deps, url, pool.dialFailureUrl?.(lease.sessionId)), ready ? await notificationReadBack(pool, lease.sessionId, permissions) : undefined),
|
|
543
582
|
};
|
|
544
583
|
}
|
|
545
584
|
finally {
|
|
@@ -579,17 +618,28 @@ const LEASE_RELEASE_TOOL = {
|
|
|
579
618
|
if (sessionId === undefined || 0 === sessionId.length) {
|
|
580
619
|
throw new Error('reticle_lease{action:"release"} requires a sessionId');
|
|
581
620
|
}
|
|
582
|
-
|
|
583
|
-
// ask which project it belonged to.
|
|
584
|
-
const projectId = deps.sessions.get(sessionId)?.projectId;
|
|
585
|
-
await pool.release(sessionId);
|
|
586
|
-
// Only once the LAST lease is gone. Announcing "live again" while another lease still drives
|
|
587
|
-
// would be a lie, and a HUD that says the wrong thing is worse than one that says nothing.
|
|
588
|
-
if (0 === pool.activeCount())
|
|
589
|
-
tellWatchers(deps, projectId, AGENT_DRIVING_HERE_AGAIN);
|
|
590
|
-
return { released: true, leased: pool.activeCount() };
|
|
621
|
+
return { released: true, leased: await releaseLease(deps, pool, sessionId) };
|
|
591
622
|
},
|
|
592
623
|
};
|
|
624
|
+
/**
|
|
625
|
+
* Release one lease and everything that hangs off it. Returns how many leases are still held.
|
|
626
|
+
*
|
|
627
|
+
* Shared with `reticle_session { action: "end" }`: an ended session that kept its lease stayed
|
|
628
|
+
* connected until the reaper took it, and every id-less call from the next agent was refused as
|
|
629
|
+
* ambiguous in the meantime.
|
|
630
|
+
*/
|
|
631
|
+
export async function releaseLease(deps, pool, sessionId) {
|
|
632
|
+
// Read the project BEFORE releasing: afterwards the session is gone and there is nothing to
|
|
633
|
+
// ask which project it belonged to.
|
|
634
|
+
const projectId = deps.sessions.get(sessionId)?.projectId;
|
|
635
|
+
await pool.release(sessionId);
|
|
636
|
+
dropArtifactRootClaim(sessionId);
|
|
637
|
+
// Only once the LAST lease is gone. Announcing "live again" while another lease still drives
|
|
638
|
+
// would be a lie, and a HUD that says the wrong thing is worse than one that says nothing.
|
|
639
|
+
if (0 === pool.activeCount())
|
|
640
|
+
tellWatchers(deps, projectId, AGENT_DRIVING_HERE_AGAIN);
|
|
641
|
+
return pool.activeCount();
|
|
642
|
+
}
|
|
593
643
|
/**
|
|
594
644
|
* What to say when a lease was taken while a real tab was already open.
|
|
595
645
|
*
|