@reticlehq/server 3.8.2 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -104
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/SKILL.md +13 -12
- package/bin/launch.js +13 -0
- package/bin/package.json +3 -0
- package/bin/reticle.js +66 -8
- package/dist/chromium-launch-options.d.ts +25 -0
- package/dist/chromium-launch-options.js +21 -1
- package/dist/command/cli/adhoc-suite.js +2 -0
- package/dist/command/cli/adhoc-verdict.d.ts +2 -43
- package/dist/command/cli/adhoc-verdict.js +50 -160
- package/dist/command/cli/answers/gate-exit.d.ts +6 -2
- package/dist/command/cli/answers/gate-exit.js +12 -2
- package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
- package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
- package/dist/command/cli/ci-hint.d.ts +44 -0
- package/dist/command/cli/ci-hint.js +72 -0
- package/dist/command/cli/cli-doctor.d.ts +6 -1
- package/dist/command/cli/cli-doctor.js +46 -5
- package/dist/command/cli/cli-flow-commands.d.ts +7 -1
- package/dist/command/cli/cli-flow-commands.js +39 -24
- package/dist/command/cli/cli-kill.d.ts +21 -0
- package/dist/command/cli/cli-kill.js +47 -4
- package/dist/command/cli/cli-parse-compare.d.ts +22 -0
- package/dist/command/cli/cli-parse-compare.js +75 -0
- package/dist/command/cli/cli-parse-grade.d.ts +36 -0
- package/dist/command/cli/cli-parse-grade.js +75 -0
- package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
- package/dist/command/cli/cli-parse-grammar.js +18 -20
- package/dist/command/cli/cli-parse-verify.d.ts +10 -0
- package/dist/command/cli/cli-parse-verify.js +63 -14
- package/dist/command/cli/cli-parse.d.ts +22 -17
- package/dist/command/cli/cli-parse.js +54 -32
- package/dist/command/cli/cli-update-commands.d.ts +1 -1
- package/dist/command/cli/cli-update-commands.js +29 -20
- package/dist/command/cli/cli-usage.js +61 -31
- package/dist/command/cli/cli-verify.d.ts +17 -3
- package/dist/command/cli/cli-verify.js +59 -21
- package/dist/command/cli/cloud-cli.js +2 -119
- package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
- package/dist/command/cli/daemon-link/daemon-client.js +205 -0
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +6 -1
- package/dist/command/cli/doctor/browser/chromium-hint.js +11 -5
- package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
- package/dist/command/cli/doctor/doctor-fix.d.ts +44 -0
- package/dist/command/cli/doctor/doctor-fix.js +89 -0
- package/dist/command/cli/doctor/doctor-launcher-line.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-launcher-line.js +60 -0
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/doctor-repairs.d.ts +64 -0
- package/dist/command/cli/doctor-repairs.js +214 -0
- package/dist/command/cli/drive/drive-command.js +3 -7
- package/dist/command/cli/ensure-daemon.d.ts +26 -0
- package/dist/command/cli/ensure-daemon.js +57 -0
- package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
- package/dist/command/cli/finished-run/grade-command.js +180 -0
- package/dist/command/cli/first-use-checks.d.ts +41 -0
- package/dist/command/cli/first-use-checks.js +127 -0
- package/dist/command/cli/first-verdict-node.d.ts +3 -0
- package/dist/command/cli/first-verdict-node.js +189 -0
- package/dist/command/cli/first-verdict.d.ts +126 -0
- package/dist/command/cli/first-verdict.js +267 -0
- package/dist/command/cli/launch/sdk-version-match.d.ts +3 -2
- package/dist/command/cli/launch/sdk-version-match.js +3 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +7 -6
- package/dist/command/cli/mcp-command.js +13 -4
- package/dist/command/cli/ports/port-holder.d.ts +11 -0
- package/dist/command/cli/ports/port-holder.js +36 -4
- package/dist/command/cli/setup-mcp-cli.d.ts +27 -1
- package/dist/command/cli/setup-mcp-cli.js +98 -23
- package/dist/command/cli/status/status-lines.d.ts +2 -0
- package/dist/command/cli/status/status-lines.js +72 -17
- package/dist/command/cli/status-fields.d.ts +5 -1
- package/dist/command/cli/status-fields.js +24 -6
- package/dist/command/cli/try-command.d.ts +7 -0
- package/dist/command/cli/try-command.js +11 -3
- package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
- package/dist/command/cli/two-builds/compare-command.js +314 -0
- package/dist/command/cli/verify-against.d.ts +61 -0
- package/dist/command/cli/verify-against.js +187 -0
- package/dist/command/cli/verify-no-session.d.ts +2 -0
- package/dist/command/cli/verify-no-session.js +13 -0
- package/dist/command/cli/verify-results.d.ts +35 -2
- package/dist/command/cli/verify-results.js +76 -10
- package/dist/command/cli.js +112 -70
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +3 -1
- package/dist/command/daemon/binding/daemon-status-probe.js +4 -2
- package/dist/command/daemon/daemon-argv.d.ts +9 -0
- package/dist/command/daemon/daemon-argv.js +11 -0
- package/dist/command/daemon/daemon-resolve.d.ts +8 -1
- package/dist/command/daemon/daemon-resolve.js +27 -1
- package/dist/command/daemon/daemon.d.ts +22 -4
- package/dist/command/daemon/daemon.js +52 -10
- package/dist/command/daemon/lifetime/daemon-exit.d.ts +24 -0
- package/dist/command/daemon/lifetime/daemon-exit.js +39 -0
- package/dist/command/daemon/lifetime/death-cause.d.ts +15 -0
- package/dist/command/daemon/lifetime/death-cause.js +149 -0
- package/dist/command/daemon/lifetime/heartbeat.d.ts +7 -15
- package/dist/command/daemon/lifetime/heartbeat.js +11 -15
- package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
- package/dist/command/daemon/lifetime/startup-failure.js +62 -0
- package/dist/command/daemon/port-state.d.ts +14 -0
- package/dist/command/daemon/port-state.js +31 -0
- package/dist/command/setup/agent-configs.d.ts +2 -0
- package/dist/command/setup/agent-configs.js +42 -15
- package/dist/command/setup/agent-next-steps.d.ts +77 -0
- package/dist/command/setup/agent-next-steps.js +284 -0
- package/dist/command/setup/agent-writer.js +11 -6
- package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
- package/dist/command/setup/bringup/dev-server-files.js +17 -0
- package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
- package/dist/command/setup/bringup/static-page-server.js +43 -9
- package/dist/command/setup/init/init-host.js +2 -0
- package/dist/command/setup/init/init-runtime.d.ts +6 -1
- package/dist/command/setup/init/init-runtime.js +63 -21
- package/dist/command/setup/mcp-launcher.d.ts +3 -0
- package/dist/command/setup/mcp-launcher.js +11 -0
- package/dist/command/setup/node-effects.d.ts +3 -2
- package/dist/command/setup/node-effects.js +9 -3
- package/dist/command/setup/remaining-steps.d.ts +8 -0
- package/dist/command/setup/remaining-steps.js +10 -0
- package/dist/command/setup/run-setup.d.ts +2 -0
- package/dist/command/setup/run-setup.js +9 -1
- package/dist/command/setup/setup-command.js +36 -15
- package/dist/command/setup/setup-mcp.d.ts +8 -0
- package/dist/command/setup/setup-mcp.js +47 -20
- package/dist/command/setup/start-dev-server.d.ts +8 -0
- package/dist/command/setup/start-dev-server.js +43 -0
- package/dist/command/update/updater.js +6 -0
- package/dist/command/version/identity/server-version.d.ts +2 -0
- package/dist/command/version/identity/server-version.js +2 -0
- package/dist/command/version/version-skew.d.ts +7 -0
- package/dist/command/version/version-skew.js +14 -3
- package/dist/features/compare/action-plan.d.ts +260 -0
- package/dist/features/compare/action-plan.js +543 -0
- package/dist/features/compare/behaviour-compare.d.ts +24 -0
- package/dist/features/compare/behaviour-compare.js +85 -0
- package/dist/features/compare/behaviour-diff.d.ts +97 -0
- package/dist/features/compare/behaviour-diff.js +202 -0
- package/dist/features/compare/behaviour-report.d.ts +40 -0
- package/dist/features/compare/behaviour-report.js +128 -0
- package/dist/features/compare/build-readings.d.ts +106 -0
- package/dist/features/compare/build-readings.js +250 -0
- package/dist/features/compare/compare-diff.d.ts +62 -0
- package/dist/features/compare/compare-diff.js +177 -0
- package/dist/features/compare/compare-report-html.d.ts +6 -0
- package/dist/features/compare/compare-report-html.js +102 -0
- package/dist/features/compare/compare-report.d.ts +15 -0
- package/dist/features/compare/compare-report.js +73 -0
- package/dist/features/compare/compare-run.d.ts +60 -0
- package/dist/features/compare/compare-run.js +123 -0
- package/dist/features/compare/outside/effect-lines.d.ts +26 -0
- package/dist/features/compare/outside/effect-lines.js +103 -0
- package/dist/features/compare/outside/state-sources.d.ts +76 -0
- package/dist/features/compare/outside/state-sources.js +164 -0
- package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
- package/dist/features/compare/outside/value-dictionary.js +227 -0
- package/dist/features/compare/visible-content.d.ts +20 -0
- package/dist/features/compare/visible-content.js +125 -0
- package/dist/features/crawl/crawl-empty.js +2 -1
- package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
- package/dist/features/crawl/crawl-exhaustive.js +138 -0
- package/dist/features/crawl/crawl-tools.d.ts +9 -2
- package/dist/features/crawl/crawl-tools.js +28 -12
- package/dist/features/crawl/crawl.d.ts +8 -9
- package/dist/features/crawl/crawl.js +61 -65
- package/dist/features/exhaust/app-coverage.d.ts +5 -1
- package/dist/features/exhaust/app-coverage.js +3 -1
- package/dist/features/exhaust/code-coverage.js +8 -4
- package/dist/features/exhaust/edge-cases.d.ts +133 -0
- package/dist/features/exhaust/edge-cases.js +310 -0
- package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
- package/dist/features/exhaust/edges/edge-drive.js +364 -0
- package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
- package/dist/features/exhaust/edges/edge-run.js +169 -0
- package/dist/features/exhaust/explorer.d.ts +87 -14
- package/dist/features/exhaust/explorer.js +159 -25
- package/dist/features/exhaust/failure-branches.d.ts +5 -0
- package/dist/features/exhaust/failure-branches.js +29 -11
- package/dist/features/exhaust/invariants.d.ts +112 -0
- package/dist/features/exhaust/invariants.js +188 -0
- package/dist/features/exhaust/ledger.d.ts +68 -0
- package/dist/features/exhaust/ledger.js +121 -13
- package/dist/features/exhaust/session-fold.d.ts +10 -0
- package/dist/features/exhaust/session-fold.js +46 -3
- package/dist/features/exhaust/session-port.d.ts +3 -1
- package/dist/features/exhaust/session-port.js +97 -28
- package/dist/features/grade/grade-answer.d.ts +99 -0
- package/dist/features/grade/grade-answer.js +112 -0
- package/dist/features/grade/grade-noise.d.ts +27 -0
- package/dist/features/grade/grade-noise.js +55 -0
- package/dist/features/grade/grade-renumber.d.ts +12 -0
- package/dist/features/grade/grade-renumber.js +86 -0
- package/dist/features/grade/grade-state.d.ts +50 -0
- package/dist/features/grade/grade-state.js +93 -0
- package/dist/features/grade/grade-suggest.d.ts +33 -0
- package/dist/features/grade/grade-suggest.js +98 -0
- package/dist/features/license/license.d.ts +11 -71
- package/dist/features/license/license.js +43 -136
- package/dist/features/visual/desktop-capture.d.ts +33 -0
- package/dist/features/visual/desktop-capture.js +94 -0
- package/dist/features/visual/step-shots.js +139 -35
- package/dist/features/visual/visual-tools.d.ts +0 -18
- package/dist/features/visual/visual-tools.js +67 -99
- package/dist/import-graph.js +5 -6
- package/dist/index.d.ts +3 -1
- package/dist/index.js +19 -7
- package/dist/judgement/capsule/causal-summary.js +9 -2
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
- package/dist/judgement/runs/reticle-runner.js +16 -2
- package/dist/judgement/runs/run-ledger.d.ts +7 -5
- package/dist/judgement/runs/run-ledger.js +98 -69
- package/dist/judgement/runs/runner-port.d.ts +2 -0
- package/dist/judgement/runs/runner-port.js +23 -5
- package/dist/judgement/runs/verification-sync.d.ts +6 -3
- package/dist/judgement/runs/verification-sync.js +21 -10
- package/dist/language/flows/affected-tools.d.ts +1 -1
- package/dist/language/flows/affected-tools.js +49 -30
- package/dist/language/flows/change/git-changed.d.ts +2 -1
- package/dist/language/flows/change/git-changed.js +19 -2
- package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
- package/dist/language/flows/fields/flow-secret-field.js +77 -21
- package/dist/language/flows/flow-anchor.d.ts +60 -2
- package/dist/language/flows/flow-anchor.js +125 -20
- package/dist/language/flows/flow-classify.d.ts +20 -0
- package/dist/language/flows/flow-classify.js +90 -12
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +25 -1
- package/dist/language/flows/flow-journey.d.ts +38 -4
- package/dist/language/flows/flow-journey.js +138 -8
- package/dist/language/flows/flow-replay-run.d.ts +0 -16
- package/dist/language/flows/flow-replay-run.js +76 -14
- package/dist/language/flows/flow-replay-types.d.ts +4 -2
- package/dist/language/flows/flow-replay.d.ts +19 -1
- package/dist/language/flows/flow-replay.js +169 -52
- package/dist/language/flows/flow-replayable.js +7 -1
- package/dist/language/flows/flow-start-path.d.ts +34 -5
- package/dist/language/flows/flow-start-path.js +202 -15
- package/dist/language/flows/flow-step-runners.d.ts +14 -13
- package/dist/language/flows/flow-step-runners.js +184 -109
- package/dist/language/flows/flow-store-for-session.d.ts +26 -25
- package/dist/language/flows/flow-store-for-session.js +51 -5
- package/dist/language/flows/flow-tools.js +46 -33
- package/dist/language/flows/flows.js +176 -76
- package/dist/language/flows/recording/tape/recordings.d.ts +21 -16
- package/dist/language/flows/recording/tape/recordings.js +112 -49
- package/dist/language/flows/replay.d.ts +36 -12
- package/dist/language/flows/replay.js +193 -50
- package/dist/language/flows/steps/page-steps.d.ts +68 -0
- package/dist/language/flows/steps/page-steps.js +198 -0
- package/dist/language/flows/suite/sequential-run.d.ts +68 -0
- package/dist/language/flows/suite/sequential-run.js +96 -0
- package/dist/language/flows/verify-change-tools.js +26 -15
- package/dist/launch-chromium.d.ts +1 -0
- package/dist/launch-chromium.js +49 -12
- package/dist/memory/cloud/background-sync.d.ts +13 -0
- package/dist/memory/cloud/background-sync.js +24 -3
- package/dist/memory/cloud/sync-daemon.js +26 -27
- package/dist/memory/cloud/sync-screenshots.js +6 -1
- package/dist/memory/journal/attach-journal.js +2 -1
- package/dist/memory/journal/drive-flow.d.ts +7 -0
- package/dist/memory/journal/drive-flow.js +44 -13
- package/dist/memory/journal/journal-recorder.d.ts +4 -2
- package/dist/memory/journal/session-journal.d.ts +7 -0
- package/dist/memory/journal/session-journal.js +58 -10
- package/dist/memory/project/root-claims.d.ts +9 -0
- package/dist/memory/project/root-claims.js +9 -4
- package/dist/portal/bridge/bridge.d.ts +13 -4
- package/dist/portal/bridge/bridge.js +10 -9
- package/dist/portal/input/download-capture.d.ts +7 -0
- package/dist/portal/input/download-capture.js +92 -0
- package/dist/portal/input/network-detail.d.ts +6 -0
- package/dist/portal/input/network-detail.js +12 -1
- package/dist/portal/input/network-mock-tools.js +49 -13
- package/dist/portal/input/network-mock.d.ts +8 -0
- package/dist/portal/input/network-mock.js +30 -10
- package/dist/portal/input/page-capture.d.ts +24 -0
- package/dist/portal/input/page-capture.js +33 -0
- package/dist/portal/input/real-input.d.ts +35 -18
- package/dist/portal/input/real-input.js +99 -33
- package/dist/portal/input/viewport-tools.d.ts +8 -0
- package/dist/portal/input/viewport-tools.js +81 -21
- package/dist/portal/pool/browser-pool.d.ts +16 -30
- package/dist/portal/pool/browser-pool.js +64 -52
- package/dist/portal/pool/context-permissions.d.ts +34 -17
- package/dist/portal/pool/context-permissions.js +94 -1
- package/dist/portal/pool/csp-widen.d.ts +62 -0
- package/dist/portal/pool/csp-widen.js +132 -0
- package/dist/portal/pool/determinism.d.ts +52 -0
- package/dist/portal/pool/determinism.js +259 -0
- package/dist/portal/pool/lease-emulation.d.ts +34 -1
- package/dist/portal/pool/lease-emulation.js +18 -0
- package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
- package/dist/portal/pool/lease-marker-stamp.js +15 -1
- package/dist/portal/pool/lease-page-error.d.ts +56 -0
- package/dist/portal/pool/lease-page-error.js +160 -0
- package/dist/portal/pool/page-error-journal.d.ts +63 -0
- package/dist/portal/pool/page-error-journal.js +208 -0
- package/dist/portal/pool/playwright-launcher.js +71 -15
- package/dist/portal/pool/pool-contract.d.ts +49 -1
- package/dist/portal/pool/pool-lease.d.ts +70 -2
- package/dist/portal/pool/pool-lease.js +23 -0
- package/dist/portal/pool/zero-install.d.ts +8 -0
- package/dist/portal/pool/zero-install.js +91 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +11 -2
- package/dist/portal/session/dev-server/dev-command.js +110 -16
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
- package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
- package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
- package/dist/portal/session/dev-server/project-ports.js +62 -0
- package/dist/portal/session/facts/departed-network.d.ts +66 -0
- package/dist/portal/session/facts/departed-network.js +142 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
- package/dist/portal/session/no-session-diagnosis.js +76 -27
- package/dist/portal/session/no-session-next-action.d.ts +14 -0
- package/dist/portal/session/no-session-next-action.js +56 -7
- package/dist/portal/session/no-session-watch.d.ts +6 -0
- package/dist/portal/session/no-session-watch.js +41 -4
- package/dist/portal/session/page-commands/undeclared-command.d.ts +9 -3
- package/dist/portal/session/page-commands/undeclared-command.js +16 -7
- package/dist/portal/session/recorded-gaps.d.ts +1 -1
- package/dist/portal/session/recorded-gaps.js +5 -2
- package/dist/portal/session/serving-directory.d.ts +10 -0
- package/dist/portal/session/serving-directory.js +39 -8
- package/dist/portal/session/session-manager.d.ts +10 -0
- package/dist/portal/session/session-manager.js +211 -81
- package/dist/portal/session/session.js +4 -3
- package/dist/portal/session/zero-install-first-call.d.ts +20 -1
- package/dist/portal/session/zero-install-first-call.js +34 -2
- package/dist/start-wiring.d.ts +30 -2
- package/dist/start-wiring.js +74 -9
- package/dist/surface/mcp/mcp-proxy.js +10 -2
- package/dist/surface/mcp/mcp.js +8 -5
- package/dist/surface/mcp/peer/agents-connected.d.ts +6 -0
- package/dist/surface/mcp/peer/agents-connected.js +58 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +17 -5
- package/dist/surface/mcp/shared-params.d.ts +2 -0
- package/dist/surface/mcp/shared-params.js +31 -0
- package/dist/surface/tools/act/act-observation.d.ts +40 -2
- package/dist/surface/tools/act/act-observation.js +118 -12
- package/dist/surface/tools/act/act-preflight.js +38 -18
- package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
- package/dist/surface/tools/act/act-sequence-retry.js +61 -5
- package/dist/surface/tools/act/act-target.js +3 -1
- package/dist/surface/tools/act/after-match.d.ts +10 -0
- package/dist/surface/tools/act/after-match.js +40 -0
- package/dist/surface/tools/act/already-true.js +1 -1
- package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
- package/dist/surface/tools/act/navigation/reload-result.js +25 -2
- package/dist/surface/tools/act/resolve-target.d.ts +17 -1
- package/dist/surface/tools/act/resolve-target.js +59 -9
- package/dist/surface/tools/act/sequence-grade.d.ts +15 -1
- package/dist/surface/tools/act/sequence-grade.js +45 -27
- package/dist/surface/tools/act/settle-in-flight.js +27 -15
- package/dist/surface/tools/act-command.d.ts +23 -0
- package/dist/surface/tools/act-command.js +50 -0
- package/dist/surface/tools/act-sequence-tool.js +81 -50
- package/dist/surface/tools/act-tools.d.ts +1 -13
- package/dist/surface/tools/act-tools.js +83 -95
- package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
- package/dist/surface/tools/agent/build-staleness.js +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +18 -12
- package/dist/surface/tools/browser-tools.js +87 -13
- package/dist/surface/tools/compare-tools.d.ts +11 -0
- package/dist/surface/tools/compare-tools.js +94 -0
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.js +11 -3
- package/dist/surface/tools/empty-session-list.d.ts +2 -0
- package/dist/surface/tools/empty-session-list.js +4 -2
- package/dist/surface/tools/error-recovery.d.ts +12 -1
- package/dist/surface/tools/error-recovery.js +131 -40
- package/dist/surface/tools/explore-tools.js +1 -1
- package/dist/surface/tools/invoke-tool.js +29 -21
- package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
- package/dist/surface/tools/lease-coverage-tools.js +128 -0
- package/dist/surface/tools/lease-hint.d.ts +42 -1
- package/dist/surface/tools/lease-hint.js +52 -3
- package/dist/surface/tools/lease-readiness.d.ts +47 -0
- package/dist/surface/tools/lease-readiness.js +126 -0
- package/dist/surface/tools/lease-seed.d.ts +16 -1
- package/dist/surface/tools/lease-seed.js +37 -4
- package/dist/surface/tools/lease-tools.d.ts +3 -41
- package/dist/surface/tools/lease-tools.js +104 -124
- package/dist/surface/tools/lease-url-identity.d.ts +46 -0
- package/dist/surface/tools/lease-url-identity.js +97 -0
- package/dist/surface/tools/merge-tools.js +99 -2
- package/dist/surface/tools/merged-name-redirect.js +5 -1
- package/dist/surface/tools/observe-tools.js +15 -9
- package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
- package/dist/surface/tools/read/console-pre-attach.js +23 -0
- package/dist/surface/tools/read-tools.js +33 -30
- package/dist/surface/tools/real-input-attempt.d.ts +0 -8
- package/dist/surface/tools/real-input-attempt.js +143 -28
- package/dist/surface/tools/repeat-hint.d.ts +40 -0
- package/dist/surface/tools/repeat-hint.js +171 -0
- package/dist/surface/tools/run-report.d.ts +2 -2
- package/dist/surface/tools/run-report.js +27 -15
- package/dist/surface/tools/session-ports.d.ts +14 -0
- package/dist/surface/tools/session-ports.js +53 -0
- package/dist/surface/tools/tool-kit.d.ts +18 -0
- package/dist/surface/tools/tool-kit.js +9 -3
- package/dist/surface/tools/tool-surface.js +0 -1
- package/dist/surface/tools/tools.js +54 -18
- package/dist/surface/tools/type-spelling.d.ts +13 -0
- package/dist/surface/tools/type-spelling.js +36 -0
- package/dist/telemetry/agent-client.d.ts +6 -0
- package/dist/telemetry/agent-client.js +10 -0
- package/dist/telemetry/argument-shape.js +17 -7
- package/dist/telemetry/cli-telemetry.d.ts +10 -1
- package/dist/telemetry/cli-telemetry.js +15 -0
- package/dist/telemetry/init-telemetry.d.ts +2 -1
- package/dist/telemetry/init-telemetry.js +47 -2
- package/dist/telemetry/install-trace.js +23 -4
- package/dist/telemetry/onboarding-funnel.d.ts +19 -2
- package/dist/telemetry/onboarding-funnel.js +25 -3
- package/dist/telemetry/proxy-telemetry.d.ts +2 -1
- package/dist/telemetry/proxy-telemetry.js +2 -2
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +67 -27
- package/dist/telemetry/telemetry.d.ts +3 -1
- package/dist/telemetry/telemetry.js +60 -28
- package/dist/telemetry/tool-refused.d.ts +6 -3
- package/dist/telemetry/tool-refused.js +15 -3
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +1 -1
- package/docs/architecture.md +3 -6
- package/docs/capabilities.mdx +61 -1
- package/docs/cli/affected.mdx +9 -7
- package/docs/cli/cloud.mdx +3 -3
- package/docs/cli/compare.mdx +94 -0
- package/docs/cli/doctor.mdx +41 -2
- package/docs/cli/grade.mdx +97 -0
- package/docs/cli/help.mdx +4 -2
- package/docs/cli/license.mdx +13 -12
- package/docs/cli/restart.mdx +7 -5
- package/docs/cli/rollback.mdx +5 -5
- package/docs/cli/setup.mdx +13 -2
- package/docs/cli/status.mdx +4 -0
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/update.mdx +2 -2
- package/docs/cli/verify.mdx +23 -1
- package/docs/cli.mdx +34 -24
- package/docs/deploy-checks.md +25 -0
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +17 -22
- package/docs/environment.mdx +1 -0
- package/docs/first-drive.md +1 -1
- package/docs/flows.md +11 -1
- package/docs/frameworks.mdx +9 -9
- package/docs/getting-started.md +1 -1
- package/docs/install-manual.mdx +16 -1
- package/docs/license-activation.mdx +3 -3
- package/docs/packages/engine.mdx +1 -1
- package/docs/packages/server.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/test.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/platform-integration.md +1 -1
- package/docs/predicates.mdx +1 -1
- package/docs/quickstart.mdx +68 -49
- package/docs/skill-setup.md +1 -1
- package/docs/skill.md +14 -13
- package/docs/telemetry.md +6 -4
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/verify.mdx +4 -1
- package/docs/troubleshooting.mdx +1 -1
- package/docs/usage.md +17 -14
- package/package.json +18 -14
- package/LICENSE-ENTERPRISE +0 -37
- package/dist/features/ee/audit-log.d.ts +0 -14
- package/dist/features/ee/audit-log.js +0 -11
- package/dist/features/exhaust/exhaust-run.d.ts +0 -33
- package/dist/features/exhaust/exhaust-run.js +0 -82
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TESTID_FOUND_UNDER_SCHEMA } from './query-hint-schema.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { EMPTY_SESSION_LIST_OUTPUT, emptySessionList, nextStepBesideLiveSessions, } from './empty-session-list.js';
|
|
4
|
-
import { AttrNamesSchema, QueryBy, ReticleCommand, SnapshotMode } from '@reticlehq/core';
|
|
3
|
+
import { EMPTY_SESSION_LIST_OUTPUT, emptySessionList, NEXT_STEP_FIELD, nextStepBesideLiveSessions, } from './empty-session-list.js';
|
|
4
|
+
import { AttrNamesSchema, NearestTargetSchema, QueryBy, ReticleCommand, SnapshotMode, } from '@reticlehq/core';
|
|
5
5
|
import { ReticleTool } from '@reticlehq/core';
|
|
6
6
|
import { withSizeCost } from '../../portal/session/output-budget.js';
|
|
7
7
|
import { applySnapshotDelta, SnapshotCache } from './read/snapshot-delta.js';
|
|
@@ -40,6 +40,7 @@ import { buildCoverageTools } from './coverage-tools.js';
|
|
|
40
40
|
import { VERIFY_CHANGE_TOOLS } from '../../language/flows/verify-change-tools.js';
|
|
41
41
|
import { CRAWL_TOOLS } from '../../features/crawl/crawl-tools.js';
|
|
42
42
|
import { EXPLORE_TOOLS } from './explore-tools.js';
|
|
43
|
+
import { COMPARE_TOOLS } from './compare-tools.js';
|
|
43
44
|
import { SCROLL_TOOLS } from '../../portal/input/scroll-tools.js';
|
|
44
45
|
import { NETWORK_MOCK_TOOLS } from '../../portal/input/network-mock-tools.js';
|
|
45
46
|
import { VIEWPORT_TOOLS } from '../../portal/input/viewport-tools.js';
|
|
@@ -145,11 +146,12 @@ export const RAW_TOOLS = withCompactEnvelope([
|
|
|
145
146
|
})));
|
|
146
147
|
const sessions = await list();
|
|
147
148
|
// An empty list is a dead end on its own; see empty-session-list.ts for what it carries.
|
|
148
|
-
if (0 === sessions.length)
|
|
149
|
+
if (0 === sessions.length) {
|
|
149
150
|
return emptySessionList(deps, sessions);
|
|
151
|
+
}
|
|
150
152
|
// Another project's tab is up. This caller's project may still never have been wired.
|
|
151
153
|
const nextStep = nextStepBesideLiveSessions(deps, sessions);
|
|
152
|
-
return nextStep === undefined ? { sessions } : { sessions,
|
|
154
|
+
return nextStep === undefined ? { sessions } : { sessions, [NEXT_STEP_FIELD]: nextStep };
|
|
153
155
|
},
|
|
154
156
|
},
|
|
155
157
|
{
|
|
@@ -177,6 +179,10 @@ export const RAW_TOOLS = withCompactEnvelope([
|
|
|
177
179
|
.boolean()
|
|
178
180
|
.optional()
|
|
179
181
|
.describe('Also measure the page for what a person would see broken: text cut off with no ellipsis, controls drawn over each other, text under WCAG contrast. Returns `audit: [{ kind, ref, what, detail }]`. Off by default; it forces layout.'),
|
|
182
|
+
charts: z
|
|
183
|
+
.boolean()
|
|
184
|
+
.optional()
|
|
185
|
+
.describe('Also read each SVG chart as geometry: every bar, dot, line and slice as its box in percent of the chart box, with no colour. A wrong value moves a shape; a chart drawn larger or in other colours reads the same. Returns `charts: [{ label, shapes, cut? }]`. Off by default; it forces layout.'),
|
|
180
186
|
...sessionIdShape,
|
|
181
187
|
},
|
|
182
188
|
outputSchema: {
|
|
@@ -259,6 +265,7 @@ export const RAW_TOOLS = withCompactEnvelope([
|
|
|
259
265
|
mode,
|
|
260
266
|
...(true === args['ids'] ? { ids: true } : {}),
|
|
261
267
|
...(true === args['audit'] ? { audit: true } : {}),
|
|
268
|
+
...(true === args['charts'] ? { charts: true } : {}),
|
|
262
269
|
}).then((raw) => withSizeCost(noteUnmountedRoot(noteHiddenPage(noteEmptyLeanTree(applySnapshotDelta(raw, {
|
|
263
270
|
sessionId: resolved.id,
|
|
264
271
|
scope: asString(args['scope']) ?? '',
|
|
@@ -284,7 +291,7 @@ export const RAW_TOOLS = withCompactEnvelope([
|
|
|
284
291
|
name: z
|
|
285
292
|
.string()
|
|
286
293
|
.optional()
|
|
287
|
-
.describe('
|
|
294
|
+
.describe('Exact accessible-name filter - narrows results when `by` is role and the page has many elements of that role. Matches the whole name; use `text` for a partial match.'),
|
|
288
295
|
scope: z
|
|
289
296
|
.string()
|
|
290
297
|
.optional()
|
|
@@ -398,6 +405,10 @@ export const RAW_TOOLS = withCompactEnvelope([
|
|
|
398
405
|
.optional()
|
|
399
406
|
.describe('Present when a ROLE+NAME search missed and that role DOES carry a nearly-matching name — role+name is exact, so "Mesh" does not find "2 Mesh". Retry with one of these spellings; no snapshot needed.'),
|
|
400
407
|
testidFoundUnder: TESTID_FOUND_UNDER_SCHEMA,
|
|
408
|
+
nearest: z
|
|
409
|
+
.array(NearestTargetSchema)
|
|
410
|
+
.optional()
|
|
411
|
+
.describe('Present when a search missed and the page has visible targets like the one asked for: up to 3, the asked role first, then by name similarity. Each has a ref to retry with. nameTruncated is true when the name was cut. Advice only, never a match.'),
|
|
401
412
|
})
|
|
402
413
|
.optional()
|
|
403
414
|
.describe('Present only on zero matches — tells you what IS on the page so you can diagnose the miss.'),
|
|
@@ -417,13 +428,18 @@ export const RAW_TOOLS = withCompactEnvelope([
|
|
|
417
428
|
.boolean()
|
|
418
429
|
.optional()
|
|
419
430
|
.describe('True when a `scope` was given but resolved to nothing — zero matches then means the scope is gone, NOT that the element is absent. The search did not widen to the whole page. Re-check the scope before concluding anything.'),
|
|
431
|
+
scopeMatches: z
|
|
432
|
+
.number()
|
|
433
|
+
.optional()
|
|
434
|
+
.describe('Present when a CSS `scope` matched more than one element: how many it matched. The search covered all of them. To read one item, scope to its ref.'),
|
|
420
435
|
},
|
|
421
436
|
handler: (deps, rawArgs) => {
|
|
422
437
|
// Accept the predicate's named-field spelling too — see query-shape.ts.
|
|
423
438
|
const args = normalizeQueryArgs(rawArgs);
|
|
424
439
|
const empty = emptyQueryRefusal(args);
|
|
425
|
-
if (empty !== undefined)
|
|
440
|
+
if (empty !== undefined) {
|
|
426
441
|
return Promise.reject(empty);
|
|
442
|
+
}
|
|
427
443
|
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
428
444
|
return commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.QUERY, {
|
|
429
445
|
by: args['by'],
|
|
@@ -466,6 +482,7 @@ export const RAW_TOOLS = withCompactEnvelope([
|
|
|
466
482
|
// reticle_crawl — autonomous click-everything + anomaly report. See crawl-tools.ts.
|
|
467
483
|
...CRAWL_TOOLS,
|
|
468
484
|
...EXPLORE_TOOLS,
|
|
485
|
+
...COMPARE_TOOLS,
|
|
469
486
|
// reticle_scroll_to — reveal a virtualized off-screen row. See scroll-tools.ts.
|
|
470
487
|
...SCROLL_TOOLS,
|
|
471
488
|
// Session lifecycle: reticle_session — tune the presenter session (idle-end). See session-tools.ts.
|
|
@@ -513,8 +530,12 @@ export const MERGE_PLANS = [
|
|
|
513
530
|
},
|
|
514
531
|
{
|
|
515
532
|
name: ReticleTool.RECORD,
|
|
516
|
-
description: 'Timeline recording: { action: "start" } begins recording under a name, "stop" ends it and returns the reaction report plus a compiled replayable program.',
|
|
517
|
-
members: {
|
|
533
|
+
description: 'Timeline recording: { action: "start" } begins recording under a name, "stop" ends it and returns the reaction report plus a compiled replayable program, "replay" runs that program again by recordingName (destructive controls need confirmDangerous:true on each replay).',
|
|
534
|
+
members: {
|
|
535
|
+
start: ReticleTool.RECORD_START,
|
|
536
|
+
stop: ReticleTool.RECORD_STOP,
|
|
537
|
+
replay: ReticleTool.REPLAY,
|
|
538
|
+
},
|
|
518
539
|
},
|
|
519
540
|
{
|
|
520
541
|
name: ReticleTool.FLOW,
|
|
@@ -552,7 +573,7 @@ export const MERGE_PLANS = [
|
|
|
552
573
|
},
|
|
553
574
|
{
|
|
554
575
|
name: ReticleTool.VERIFY,
|
|
555
|
-
description: 'The Reticle Harness is { action: "explore" }: when the user asks for the Harness, or asks Reticle to drive and verify the app for you, call it and do not drive the app yourself. Every other action says what is proved, and what is not. Start from a change: { action: "affected" } names which saved flows must re-verify for the files you edited (pass `files`, and/or `since` for a git ref), and "change" goes further and actually replays them, answering with one `verified` plus `because`. `unknown` there is the honest answer when NO saved flow covers the change: nothing ran, so nothing was proved, and it is never reported as green. Without a change to start from: "flows" replays every saved flow for one consolidated suite verdict (deterministic, no LLM per flow), and "coverage" lists the interactive controls you have and have NOT driven this session — an untouched list still holding the controls your change affects means you are not done. "crawl" is the no-script option: it drives every reachable control itself and reports single-channel faults (console errors, failed requests, dead controls) and CONTRADICTIONS, two channels disagreeing about the same click — the false greens a human cannot see, because a human watches the screen and the screen looks correct. "explore" is the Reticle Harness: it drives the app for you on the platform — pass a `persona` and it completes that whole journey — and RECORDS what it drove, so the next run replays it with no model in the loop and none of the driving costs your context. "mutate" grades the FLOW rather than the app: it breaks the endpoint a saved flow declared it depends on and replays it, and a flow that stays GREEN through a broken subject is a click sequence, not a test. "heal" repairs a flow whose LOCATOR drifted (a renamed testid), re-asserting the saved consequence before it writes and refusing when that stops firing — it heals a locator, never an intent. DESTRUCTIVE: crawl and
|
|
576
|
+
description: 'The Reticle Harness is { action: "explore" }: when the user asks for the Harness, or asks Reticle to drive and verify the app for you, call it and do not drive the app yourself. Every other action says what is proved, and what is not. Start from a change: { action: "affected" } names which saved flows must re-verify for the files you edited (pass `files`, and/or `since` for a git ref), and "change" goes further and actually replays them, answering with one `verified` plus `because`. `unknown` there is the honest answer when NO saved flow covers the change: nothing ran, so nothing was proved, and it is never reported as green. Without a change to start from: "flows" replays every saved flow for one consolidated suite verdict (deterministic, no LLM per flow), and "coverage" lists the interactive controls you have and have NOT driven this session — an untouched list still holding the controls your change affects means you are not done. "crawl" is the no-script option: it drives every reachable control itself and reports single-channel faults (console errors, failed requests, dead controls) and CONTRADICTIONS, two channels disagreeing about the same click — the false greens a human cannot see, because a human watches the screen and the screen looks correct. "explore" is the Reticle Harness: it drives the app for you on the platform — pass a `persona` and it completes that whole journey — and RECORDS what it drove, so the next run replays it with no model in the loop and none of the driving costs your context. "mutate" grades the FLOW rather than the app: it breaks the endpoint a saved flow declared it depends on and replays it, and a flow that stays GREEN through a broken subject is a click sequence, not a test. "heal" repairs a flow whose LOCATOR drifted (a renamed testid), re-asserting the saved consequence before it writes and refusing when that stops firing — it heals a locator, never an intent. "compare" drives two running builds (`reference`, `candidate`, optional `base`) the same way and lists what users would see differ and what the app sent; poll with `runId`. DESTRUCTIVE: crawl, explore and compare really click, and may navigate or mutate state; "mutate" really fails a request on a driven page, and always puts it back.',
|
|
556
577
|
members: {
|
|
557
578
|
change: ReticleTool.VERIFY_CHANGE,
|
|
558
579
|
flows: ReticleTool.FLOW_VERIFY,
|
|
@@ -570,6 +591,9 @@ export const MERGE_PLANS = [
|
|
|
570
591
|
// An action rather than a tenth tool, and safe to fold in because heal only rebinds a LOCATOR:
|
|
571
592
|
// it re-asserts the saved consequence before writing and refuses when that stops firing.
|
|
572
593
|
heal: ReticleTool.FLOW_HEAL,
|
|
594
|
+
// Two running builds, driven the same way from outside the app: what differs. The same run
|
|
595
|
+
// and JSON as `reticle compare --json`.
|
|
596
|
+
compare: ReticleTool.VERIFY_COMPARE,
|
|
573
597
|
},
|
|
574
598
|
// No default action ON PURPOSE. The name implies no single member, and one of them really
|
|
575
599
|
// clicks: a fallthrough that guessed wrong would drive the app rather than read it. Every action
|
|
@@ -578,8 +602,12 @@ export const MERGE_PLANS = [
|
|
|
578
602
|
},
|
|
579
603
|
{
|
|
580
604
|
name: ReticleTool.LEASE,
|
|
581
|
-
description: 'Isolated headless contexts from the shared pool: { action: "acquire" } leases one and navigates it to the app URL, "release" closes it and frees the slot.',
|
|
582
|
-
members: {
|
|
605
|
+
description: 'Isolated headless contexts from the shared pool: { action: "acquire" } leases one and navigates it to the app URL, "release" closes it and frees the slot, "coverage" starts or takes JS coverage of the leased page (which code ran).',
|
|
606
|
+
members: {
|
|
607
|
+
acquire: ReticleTool.LEASE_ACQUIRE,
|
|
608
|
+
release: ReticleTool.LEASE_RELEASE,
|
|
609
|
+
coverage: ReticleTool.LEASE_COVERAGE,
|
|
610
|
+
},
|
|
583
611
|
},
|
|
584
612
|
];
|
|
585
613
|
/**
|
|
@@ -590,6 +618,7 @@ export const RETIRED_FROM_SURFACE = [
|
|
|
590
618
|
ReticleTool.RUN_RECORD, // auto-recording on flow_replay already persists run outcomes
|
|
591
619
|
ReticleTool.REFRESH, // absorbed into reticle_navigate { reload: true }
|
|
592
620
|
ReticleTool.WAIT_READY, // server-internal: the first live call already blocks for the session
|
|
621
|
+
ReticleTool.EXPLORE, // the interactive snapshot plus the console give the same answer
|
|
593
622
|
];
|
|
594
623
|
/**
|
|
595
624
|
* An empty `interactive` tree is a claim, and it is usually the wrong one.
|
|
@@ -603,14 +632,17 @@ export const RETIRED_FROM_SURFACE = [
|
|
|
603
632
|
* The browser counts what leanness passed over, so the tool can say which of the two it is.
|
|
604
633
|
*/
|
|
605
634
|
function noteEmptyLeanTree(result, mode) {
|
|
606
|
-
if (SnapshotMode.INTERACTIVE !== mode)
|
|
635
|
+
if (SnapshotMode.INTERACTIVE !== mode) {
|
|
607
636
|
return result;
|
|
637
|
+
}
|
|
608
638
|
const row = asRecord(result);
|
|
609
|
-
if (0 !== asNumber(row['nodes']))
|
|
639
|
+
if (0 !== asNumber(row['nodes'])) {
|
|
610
640
|
return result;
|
|
641
|
+
}
|
|
611
642
|
const skipped = asNumber(row['leanSkipped']) ?? 0;
|
|
612
|
-
if (0 === skipped)
|
|
643
|
+
if (0 === skipped) {
|
|
613
644
|
return result;
|
|
645
|
+
}
|
|
614
646
|
return {
|
|
615
647
|
...row,
|
|
616
648
|
note: `no element on this page carries an interactive ARIA role, so this mode found nothing — ` +
|
|
@@ -635,18 +667,22 @@ function noteEmptyLeanTree(result, mode) {
|
|
|
635
667
|
* empty tree it replaces.
|
|
636
668
|
*/
|
|
637
669
|
function noteHiddenPage(result, mode) {
|
|
638
|
-
if (SnapshotMode.STATUS === mode)
|
|
670
|
+
if (SnapshotMode.STATUS === mode) {
|
|
639
671
|
return result;
|
|
672
|
+
}
|
|
640
673
|
const row = asRecord(result);
|
|
641
|
-
if (0 !== asNumber(row['nodes']))
|
|
674
|
+
if (0 !== asNumber(row['nodes'])) {
|
|
642
675
|
return result;
|
|
676
|
+
}
|
|
643
677
|
// A note already there is the more specific one (leanness), and two explanations for one empty
|
|
644
678
|
// tree is worse than the better of them alone.
|
|
645
|
-
if (row['note'] !== undefined)
|
|
679
|
+
if (row['note'] !== undefined) {
|
|
646
680
|
return result;
|
|
681
|
+
}
|
|
647
682
|
const skipped = asNumber(row['hiddenSkipped']) ?? 0;
|
|
648
|
-
if (0 === skipped)
|
|
683
|
+
if (0 === skipped) {
|
|
649
684
|
return result;
|
|
685
|
+
}
|
|
650
686
|
return {
|
|
651
687
|
...row,
|
|
652
688
|
note: `this is NOT an empty page: ${String(skipped)} subtree(s) were skipped because their root ` +
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `fill` spelling mapped onto the `type` action.
|
|
3
|
+
*
|
|
4
|
+
* The tool documents `{ text }` for type and `{ value }` for fill/select, and agents mix them up
|
|
5
|
+
* constantly — `reticle_act` with `{ action: "type", value: "…" }` then fails at run time with
|
|
6
|
+
* "type requires a string `text`", after the action window opened (reticlehq/reticle#2126, second
|
|
7
|
+
* item). When `text` is absent and `value` is a string, `value` becomes `text`, in the flat args
|
|
8
|
+
* and in the nested `args` object. A non-string `value` is left alone so the browser's own
|
|
9
|
+
* refusal keeps its accurate message.
|
|
10
|
+
*
|
|
11
|
+
* Shared by the dispatch point (act-command.ts) and the tests.
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeTypeSpelling(actArgs: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `fill` spelling mapped onto the `type` action.
|
|
3
|
+
*
|
|
4
|
+
* The tool documents `{ text }` for type and `{ value }` for fill/select, and agents mix them up
|
|
5
|
+
* constantly — `reticle_act` with `{ action: "type", value: "…" }` then fails at run time with
|
|
6
|
+
* "type requires a string `text`", after the action window opened (reticlehq/reticle#2126, second
|
|
7
|
+
* item). When `text` is absent and `value` is a string, `value` becomes `text`, in the flat args
|
|
8
|
+
* and in the nested `args` object. A non-string `value` is left alone so the browser's own
|
|
9
|
+
* refusal keeps its accurate message.
|
|
10
|
+
*
|
|
11
|
+
* Shared by the dispatch point (act-command.ts) and the tests.
|
|
12
|
+
*/
|
|
13
|
+
import { ActionType } from '@reticlehq/core';
|
|
14
|
+
export function normalizeTypeSpelling(actArgs) {
|
|
15
|
+
if (ActionType.TYPE !== actArgs['action']) {
|
|
16
|
+
return actArgs;
|
|
17
|
+
}
|
|
18
|
+
const out = { ...actArgs };
|
|
19
|
+
const mapValueToText = (record) => {
|
|
20
|
+
if (record['text'] !== undefined || 'string' !== typeof record['value']) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
record['text'] = record['value'];
|
|
24
|
+
delete record['value'];
|
|
25
|
+
return true;
|
|
26
|
+
};
|
|
27
|
+
mapValueToText(out);
|
|
28
|
+
const nested = out['args'];
|
|
29
|
+
if (null !== nested && 'object' === typeof nested && !Array.isArray(nested)) {
|
|
30
|
+
const nestedCopy = { ...nested };
|
|
31
|
+
if (mapValueToText(nestedCopy)) {
|
|
32
|
+
out['args'] = nestedCopy;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
@@ -11,5 +11,11 @@ import { AgentClient, LaunchMethod } from '@reticlehq/core/telemetry';
|
|
|
11
11
|
export declare function envAgentClient(env: NodeJS.ProcessEnv): AgentClient | undefined;
|
|
12
12
|
/** The client for an event being built now. */
|
|
13
13
|
export declare function currentAgentClient(env: NodeJS.ProcessEnv): AgentClient | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* The folder in `${CLAUDE_PLUGIN_DATA}` that holds the Claude Code plugin's pinned server copies.
|
|
16
|
+
* `plugins/reticle/scripts/mcp-server.mjs` cannot import this file, so it keeps a copy of the name.
|
|
17
|
+
* `plugin-mcp-launch.test.ts` fails when the two differ.
|
|
18
|
+
*/
|
|
19
|
+
export declare const PLUGIN_SERVER_DIR = "reticle-plugin-server";
|
|
14
20
|
/** How the process was launched, from the path of its entry script. Only the bucket leaves. */
|
|
15
21
|
export declare function launchMethodOf(entry: string | undefined): LaunchMethod;
|
|
@@ -28,6 +28,12 @@ export function envAgentClient(env) {
|
|
|
28
28
|
export function currentAgentClient(env) {
|
|
29
29
|
return agentClientOf(callingClientName()) ?? envAgentClient(env);
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* The folder in `${CLAUDE_PLUGIN_DATA}` that holds the Claude Code plugin's pinned server copies.
|
|
33
|
+
* `plugins/reticle/scripts/mcp-server.mjs` cannot import this file, so it keeps a copy of the name.
|
|
34
|
+
* `plugin-mcp-launch.test.ts` fails when the two differ.
|
|
35
|
+
*/
|
|
36
|
+
export const PLUGIN_SERVER_DIR = 'reticle-plugin-server';
|
|
31
37
|
/**
|
|
32
38
|
* Where a global install puts packages, by package manager. A path that matches none of these but
|
|
33
39
|
* still sits in a `node_modules` is a project's own dependency.
|
|
@@ -49,6 +55,10 @@ export function launchMethodOf(entry) {
|
|
|
49
55
|
const path = entry.replace(/\\/g, '/').toLowerCase();
|
|
50
56
|
if (path.includes('/_npx/') || path.includes('/dlx-'))
|
|
51
57
|
return LaunchMethod.NPX;
|
|
58
|
+
// The Claude Code plugin's own pinned copy: in a `node_modules`, but not the user's project.
|
|
59
|
+
if (path.includes(`/${PLUGIN_SERVER_DIR}/`)) {
|
|
60
|
+
return LaunchMethod.OTHER;
|
|
61
|
+
}
|
|
52
62
|
if (GLOBAL_DIRS.some((dir) => path.includes(dir)))
|
|
53
63
|
return LaunchMethod.GLOBAL;
|
|
54
64
|
if (path.includes('/node_modules/'))
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* exact values allowed — anything outside the list is reported as `other`, so a schema change can
|
|
19
19
|
* never quietly start forwarding free text.
|
|
20
20
|
*/
|
|
21
|
-
import { ActionType, FeedbackKind, QueryBy, SnapshotMode } from '@reticlehq/core';
|
|
21
|
+
import { ActionType, FeedbackKind, QueryBy, ReticleTool, SnapshotMode } from '@reticlehq/core';
|
|
22
22
|
/**
|
|
23
23
|
* Parameters whose values are enums WE define, and the exact values allowed on the wire. An
|
|
24
24
|
* unrecognized value reports `other` rather than itself; that fallback is what makes this safe
|
|
@@ -35,6 +35,9 @@ const SAFE_ENUM_PARAMS = new Map([
|
|
|
35
35
|
['by', new Set(Object.values(QueryBy))],
|
|
36
36
|
['mode', new Set(Object.values(SnapshotMode))],
|
|
37
37
|
['kind', new Set(Object.values(FeedbackKind))],
|
|
38
|
+
// `reticle_run { tool }` names the tool it calls. Our own tool names, so the session summary can
|
|
39
|
+
// say which tools agents reach through it. A name that is not a Reticle tool reports `other`.
|
|
40
|
+
['tool', new Set(Object.values(ReticleTool))],
|
|
38
41
|
// `level` has no core enum — console levels come from the DOM console API, not from us. Listed
|
|
39
42
|
// literally BECAUSE there is nothing to derive from, which is a different situation from the four
|
|
40
43
|
// above and the reason it is called out rather than left looking like an oversight.
|
|
@@ -49,10 +52,12 @@ export const OTHER_VALUE = 'other';
|
|
|
49
52
|
*/
|
|
50
53
|
export function allowParamValues(name, values) {
|
|
51
54
|
const allowed = SAFE_ENUM_PARAMS.get(name);
|
|
52
|
-
if (allowed === undefined)
|
|
55
|
+
if (allowed === undefined) {
|
|
53
56
|
return;
|
|
54
|
-
|
|
57
|
+
}
|
|
58
|
+
for (const value of values) {
|
|
55
59
|
allowed.add(value);
|
|
60
|
+
}
|
|
56
61
|
}
|
|
57
62
|
/**
|
|
58
63
|
* The reportable form of one parameter: `name` on its own, or `name:value` when the parameter is a
|
|
@@ -60,10 +65,12 @@ export function allowParamValues(name, values) {
|
|
|
60
65
|
*/
|
|
61
66
|
export function describeParam(name, value) {
|
|
62
67
|
const allowed = SAFE_ENUM_PARAMS.get(name);
|
|
63
|
-
if (allowed === undefined)
|
|
68
|
+
if (allowed === undefined) {
|
|
64
69
|
return name;
|
|
65
|
-
|
|
70
|
+
}
|
|
71
|
+
if (typeof value !== 'string') {
|
|
66
72
|
return name;
|
|
73
|
+
}
|
|
67
74
|
return `${name}:${allowed.has(value) ? value : OTHER_VALUE}`;
|
|
68
75
|
}
|
|
69
76
|
/**
|
|
@@ -96,10 +103,12 @@ export function describeCliFlags(argv) {
|
|
|
96
103
|
const flags = new Set();
|
|
97
104
|
for (const arg of argv) {
|
|
98
105
|
// A bare `--` ends the options: everything after it is passed through to something else.
|
|
99
|
-
if (END_OF_OPTIONS === arg)
|
|
106
|
+
if (END_OF_OPTIONS === arg) {
|
|
100
107
|
break;
|
|
101
|
-
|
|
108
|
+
}
|
|
109
|
+
if (!arg.startsWith('--')) {
|
|
102
110
|
continue;
|
|
111
|
+
}
|
|
103
112
|
// `--port=9000` — split so the NAME is reported and the value is discarded with everything else.
|
|
104
113
|
const name = arg.split('=')[0] ?? arg;
|
|
105
114
|
flags.add(KNOWN_CLI_FLAGS.has(name) ? name : OTHER_VALUE);
|
|
@@ -117,6 +126,7 @@ const KNOWN_CLI_FLAGS = new Set([
|
|
|
117
126
|
'--accept-coverage',
|
|
118
127
|
'--agent',
|
|
119
128
|
'--app',
|
|
129
|
+
'--base',
|
|
120
130
|
'--bug',
|
|
121
131
|
'--capture-bodies',
|
|
122
132
|
'--confirm-dangerous',
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Lives in its own module so `cli.ts` stays a dispatcher, and so the `_daemon` exclusion below sits
|
|
5
5
|
* next to the explanation of why it exists rather than buried in a startup function.
|
|
6
6
|
*/
|
|
7
|
-
import { TelemetryActor } from '@reticlehq/core/telemetry';
|
|
7
|
+
import { TelemetryActor, type GateCompleted } from '@reticlehq/core/telemetry';
|
|
8
|
+
import { type Telemetry } from './telemetry.js';
|
|
8
9
|
/** True when this command represents a person typing something. Exported for the gate. */
|
|
9
10
|
export declare function isHumanCliCommand(command: string): boolean;
|
|
10
11
|
/**
|
|
@@ -23,3 +24,11 @@ export declare function cliActorOf(env: NodeJS.ProcessEnv): TelemetryActor;
|
|
|
23
24
|
*/
|
|
24
25
|
export declare function claimInstallReport(command: string, firstRun: boolean, stateDir: string): boolean;
|
|
25
26
|
export declare function reportCliRun(argv: readonly string[]): void;
|
|
27
|
+
/**
|
|
28
|
+
* `gate_completed`: how one `reticle gate` run ended.
|
|
29
|
+
*
|
|
30
|
+
* `cli_command_run` says that the gate ran. Only this event says whether it passed. The gate is a
|
|
31
|
+
* one-shot command that exits immediately, so the send is DETACHED, like `init_completed`.
|
|
32
|
+
* Best-effort: a metric never changes what the gate decides.
|
|
33
|
+
*/
|
|
34
|
+
export declare function reportGateCompleted(gate: GateCompleted, telemetry?: Pick<Telemetry, 'emit'>): void;
|
|
@@ -161,3 +161,18 @@ export function reportCliRun(argv) {
|
|
|
161
161
|
flags: describeCliFlags(argv),
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* `gate_completed`: how one `reticle gate` run ended.
|
|
166
|
+
*
|
|
167
|
+
* `cli_command_run` says that the gate ran. Only this event says whether it passed. The gate is a
|
|
168
|
+
* one-shot command that exits immediately, so the send is DETACHED, like `init_completed`.
|
|
169
|
+
* Best-effort: a metric never changes what the gate decides.
|
|
170
|
+
*/
|
|
171
|
+
export function reportGateCompleted(gate, telemetry = getTelemetry()) {
|
|
172
|
+
try {
|
|
173
|
+
void telemetry.emit(TelemetryEventKind.GATE_COMPLETED, { gate, detach: true });
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
/* a metric never fails the gate */
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* event loop alive and tax the command by most of a second.
|
|
12
12
|
*/
|
|
13
13
|
import { type InitOutcome } from '@reticlehq/core/telemetry';
|
|
14
|
-
|
|
14
|
+
import { InitFailure } from '@reticlehq/init';
|
|
15
|
+
export { InitFailure };
|
|
15
16
|
/** Report one init outcome. Best-effort; setup must never fail because a metric did. */
|
|
16
17
|
export declare function reportInitOutcome(init: InitOutcome): void;
|
|
@@ -10,12 +10,56 @@
|
|
|
10
10
|
* same treatment `cli_command_run` gets, for the same reason: an in-process fetch would keep the
|
|
11
11
|
* event loop alive and tax the command by most of a second.
|
|
12
12
|
*/
|
|
13
|
-
import { TelemetryEventKind } from '@reticlehq/core/telemetry';
|
|
13
|
+
import { OnboardingPhase, OnboardingStop, TelemetryEventKind, } from '@reticlehq/core/telemetry';
|
|
14
|
+
import { InitFailure } from '@reticlehq/init';
|
|
14
15
|
import { getTelemetry } from './telemetry.js';
|
|
15
16
|
import { INSTALL_STATE_DIR, resolveInstallSource } from './install-source.js';
|
|
17
|
+
import { reportOnboardingStop } from './onboarding-funnel.js';
|
|
16
18
|
// The failure vocabulary lives in `@reticlehq/init`, which is what classifies a failed run.
|
|
17
19
|
// Re-exported so every consumer here is unchanged.
|
|
18
|
-
export { InitFailure }
|
|
20
|
+
export { InitFailure };
|
|
21
|
+
const PROJECT_DETECTED = 'project_detected';
|
|
22
|
+
const INSTRUMENTED = 'instrumented';
|
|
23
|
+
const APP_CONNECTED = 'app_connected';
|
|
24
|
+
/**
|
|
25
|
+
* Each init failure as one stop, with the first-run step it blocked. A `Record` over the whole
|
|
26
|
+
* enum, so a new `InitFailure` does not compile until it names its stop.
|
|
27
|
+
*/
|
|
28
|
+
const INIT_STOP = {
|
|
29
|
+
[InitFailure.NO_PACKAGE_JSON]: { step: PROJECT_DETECTED, stop: OnboardingStop.NO_WEB_APP },
|
|
30
|
+
[InitFailure.MALFORMED_PACKAGE_JSON]: {
|
|
31
|
+
step: PROJECT_DETECTED,
|
|
32
|
+
stop: OnboardingStop.SETUP_FAILED,
|
|
33
|
+
},
|
|
34
|
+
[InitFailure.PREFLIGHT]: { step: PROJECT_DETECTED, stop: OnboardingStop.SETUP_FAILED },
|
|
35
|
+
[InitFailure.DEPENDENCY_INSTALL]: { step: INSTRUMENTED, stop: OnboardingStop.SETUP_FAILED },
|
|
36
|
+
[InitFailure.MCP_REGISTRATION]: { step: INSTRUMENTED, stop: OnboardingStop.MCP_NOT_REGISTERED },
|
|
37
|
+
[InitFailure.OTHER]: { step: INSTRUMENTED, stop: OnboardingStop.UNCLASSIFIED },
|
|
38
|
+
[InitFailure.BRIDGE_OCCUPIED]: { step: APP_CONNECTED, stop: OnboardingStop.SETUP_FAILED },
|
|
39
|
+
[InitFailure.DEV_SERVER]: { step: APP_CONNECTED, stop: OnboardingStop.DEV_SERVER_FAILED },
|
|
40
|
+
[InitFailure.APP_CONNECTION]: { step: APP_CONNECTED, stop: OnboardingStop.APP_NOT_CONNECTED },
|
|
41
|
+
[InitFailure.RUNTIME_ERROR]: { step: APP_CONNECTED, stop: OnboardingStop.UNCLASSIFIED },
|
|
42
|
+
[InitFailure.INTERRUPTED]: { step: APP_CONNECTED, stop: OnboardingStop.INTERRUPTED },
|
|
43
|
+
};
|
|
44
|
+
const INIT_STOP_UNKNOWN = INIT_STOP[InitFailure.OTHER];
|
|
45
|
+
function initStop(reason) {
|
|
46
|
+
if (reason !== undefined && Object.hasOwn(INIT_STOP, reason)) {
|
|
47
|
+
return INIT_STOP[reason];
|
|
48
|
+
}
|
|
49
|
+
return INIT_STOP_UNKNOWN;
|
|
50
|
+
}
|
|
51
|
+
/** The stop row of a failed init, or nothing for a run that worked. */
|
|
52
|
+
function reportInitStop(init) {
|
|
53
|
+
if (init.ok) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
reportOnboardingStop({
|
|
57
|
+
phase: OnboardingPhase.FIRST_RUN,
|
|
58
|
+
...initStop(init.reason),
|
|
59
|
+
...(init.reason === undefined ? {} : { reason: init.reason }),
|
|
60
|
+
...(init.stack === undefined ? {} : { stack: init.stack }),
|
|
61
|
+
}, { detach: true });
|
|
62
|
+
}
|
|
19
63
|
/** Report one init outcome. Best-effort; setup must never fail because a metric did. */
|
|
20
64
|
export function reportInitOutcome(init) {
|
|
21
65
|
try {
|
|
@@ -27,6 +71,7 @@ export function reportInitOutcome(init) {
|
|
|
27
71
|
// marker, same refusal to infer — see install-source.ts.
|
|
28
72
|
installSource: resolveInstallSource(process.env, INSTALL_STATE_DIR),
|
|
29
73
|
});
|
|
74
|
+
reportInitStop(init);
|
|
30
75
|
}
|
|
31
76
|
catch {
|
|
32
77
|
/* a metric must never break `reticle init` */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { OnboardingStepSchema } from '@reticlehq/core/telemetry';
|
|
3
|
+
import { OnboardingStepSchema, OnboardingStop } from '@reticlehq/core/telemetry';
|
|
4
4
|
import { reportOnboardingStep } from './onboarding-funnel.js';
|
|
5
5
|
/**
|
|
6
6
|
* What the one-line installer could not report itself.
|
|
@@ -27,6 +27,22 @@ const TRACE_FILE = 'install-trace.jsonl';
|
|
|
27
27
|
* file must not turn into thousands of events. Far above any real install, which writes three.
|
|
28
28
|
*/
|
|
29
29
|
const MAX_LINES = 32;
|
|
30
|
+
/**
|
|
31
|
+
* The stop that each installer failure reason names. The keys are the reasons that `install.sh`
|
|
32
|
+
* and `install.ps1` write. A reason not in this table gets no stop.
|
|
33
|
+
*/
|
|
34
|
+
const INSTALLER_STOP = {
|
|
35
|
+
no_node: OnboardingStop.NO_NODE,
|
|
36
|
+
node_too_old: OnboardingStop.NODE_TOO_OLD,
|
|
37
|
+
no_npm: OnboardingStop.CLI_INSTALL_FAILED,
|
|
38
|
+
npm_install: OnboardingStop.CLI_INSTALL_FAILED,
|
|
39
|
+
not_on_path: OnboardingStop.CLI_INSTALL_FAILED,
|
|
40
|
+
};
|
|
41
|
+
function installerStop(reason) {
|
|
42
|
+
return reason !== undefined && Object.hasOwn(INSTALLER_STOP, reason)
|
|
43
|
+
? INSTALLER_STOP[reason]
|
|
44
|
+
: undefined;
|
|
45
|
+
}
|
|
30
46
|
/**
|
|
31
47
|
* Drain the installer's breadcrumbs, once.
|
|
32
48
|
*
|
|
@@ -36,8 +52,9 @@ const MAX_LINES = 32;
|
|
|
36
52
|
*/
|
|
37
53
|
export function drainInstallTrace(stateDir) {
|
|
38
54
|
const path = join(stateDir, TRACE_FILE);
|
|
39
|
-
if (!existsSync(path))
|
|
55
|
+
if (!existsSync(path)) {
|
|
40
56
|
return 0;
|
|
57
|
+
}
|
|
41
58
|
let drained = 0;
|
|
42
59
|
try {
|
|
43
60
|
const lines = readFileSync(path, 'utf8')
|
|
@@ -55,9 +72,11 @@ export function drainInstallTrace(stateDir) {
|
|
|
55
72
|
// deliberate belt-and-braces: this is the one path where the payload is a file a person can
|
|
56
73
|
// edit, and rule 3 is that unvalidated text never reaches the wire.
|
|
57
74
|
const step = OnboardingStepSchema.safeParse(parsed);
|
|
58
|
-
if (!step.success)
|
|
75
|
+
if (!step.success) {
|
|
59
76
|
continue;
|
|
60
|
-
|
|
77
|
+
}
|
|
78
|
+
const stop = installerStop(step.data.reason);
|
|
79
|
+
void reportOnboardingStep({ ...step.data, ...(stop === undefined ? {} : { stop }) });
|
|
61
80
|
drained += 1;
|
|
62
81
|
}
|
|
63
82
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OnboardingPhase, type OnboardingStep } from '@reticlehq/core/telemetry';
|
|
1
|
+
import { OnboardingPhase, type OnboardingStep, type OnboardingStop } from '@reticlehq/core/telemetry';
|
|
2
2
|
/**
|
|
3
3
|
* The one place an onboarding step is reported.
|
|
4
4
|
*
|
|
@@ -11,7 +11,24 @@ import { OnboardingPhase, type OnboardingStep } from '@reticlehq/core/telemetry'
|
|
|
11
11
|
* arriving as a step name would be a rule-3 leak that no reader would ever notice, because a funnel
|
|
12
12
|
* with one weird row still renders.
|
|
13
13
|
*/
|
|
14
|
-
export declare function reportOnboardingStep(step: OnboardingStep
|
|
14
|
+
export declare function reportOnboardingStep(step: OnboardingStep, opts?: {
|
|
15
|
+
detach?: boolean;
|
|
16
|
+
}): Promise<boolean>;
|
|
17
|
+
/** One stop: the rung it blocked and its name. The status is always `failed`. */
|
|
18
|
+
export type StopRow = Omit<OnboardingStep, 'status' | 'stop'> & {
|
|
19
|
+
stop: OnboardingStop;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Report one stop on the path to a first verdict, once for each process and stop name.
|
|
23
|
+
*
|
|
24
|
+
* A daemon can fail to launch Chromium on every lease. One row says the stop happened here; more
|
|
25
|
+
* rows count retries, not people. Use `detach` from a CLI that exits next, or the row is lost.
|
|
26
|
+
*/
|
|
27
|
+
export declare function reportOnboardingStop(row: StopRow, opts?: {
|
|
28
|
+
detach?: boolean;
|
|
29
|
+
}): void;
|
|
30
|
+
/** Tests only: forget the stops this process sent. */
|
|
31
|
+
export declare function resetOnboardingStops(): void;
|
|
15
32
|
/**
|
|
16
33
|
* Time a step and report it, whatever happens to it.
|
|
17
34
|
*
|
|
@@ -12,13 +12,35 @@ import { getTelemetry } from './telemetry.js';
|
|
|
12
12
|
* arriving as a step name would be a rule-3 leak that no reader would ever notice, because a funnel
|
|
13
13
|
* with one weird row still renders.
|
|
14
14
|
*/
|
|
15
|
-
export async function reportOnboardingStep(step) {
|
|
15
|
+
export async function reportOnboardingStep(step, opts = {}) {
|
|
16
16
|
const parsed = OnboardingStepSchema.safeParse(step);
|
|
17
17
|
// A malformed step is DROPPED rather than sent or thrown. Sending it would put unvalidated text on
|
|
18
18
|
// the wire; throwing would let a metric change what the user's install does, which rule 5 forbids.
|
|
19
|
-
if (!parsed.success)
|
|
19
|
+
if (!parsed.success) {
|
|
20
20
|
return false;
|
|
21
|
-
|
|
21
|
+
}
|
|
22
|
+
return getTelemetry().emit(TelemetryEventKind.ONBOARDING_STEP, {
|
|
23
|
+
onboarding: parsed.data,
|
|
24
|
+
...(true === opts.detach ? { detach: true } : {}),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const stopsSent = new Set();
|
|
28
|
+
/**
|
|
29
|
+
* Report one stop on the path to a first verdict, once for each process and stop name.
|
|
30
|
+
*
|
|
31
|
+
* A daemon can fail to launch Chromium on every lease. One row says the stop happened here; more
|
|
32
|
+
* rows count retries, not people. Use `detach` from a CLI that exits next, or the row is lost.
|
|
33
|
+
*/
|
|
34
|
+
export function reportOnboardingStop(row, opts = {}) {
|
|
35
|
+
if (stopsSent.has(row.stop)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
stopsSent.add(row.stop);
|
|
39
|
+
void reportOnboardingStep({ ...row, status: OnboardingStepStatus.FAILED }, opts);
|
|
40
|
+
}
|
|
41
|
+
/** Tests only: forget the stops this process sent. */
|
|
42
|
+
export function resetOnboardingStops() {
|
|
43
|
+
stopsSent.clear();
|
|
22
44
|
}
|
|
23
45
|
/**
|
|
24
46
|
* Time a step and report it, whatever happens to it.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* never arrived: the POST was killed every time and nothing threw.
|
|
11
11
|
*/
|
|
12
12
|
import { type ProxyExitReason, type WakeRefusalPresence } from '@reticlehq/core/telemetry';
|
|
13
|
+
import type { DaemonDeath } from '@reticlehq/core';
|
|
13
14
|
import { type SessionMetrics } from './session-metrics.js';
|
|
14
15
|
import { type Telemetry } from './telemetry.js';
|
|
15
16
|
export declare function flushProxySessionMetrics(telemetry?: Telemetry, metrics?: SessionMetrics): Promise<void>;
|
|
@@ -37,7 +38,7 @@ export declare function reportProxyExited(reason: ProxyExitReason, telemetry?: T
|
|
|
37
38
|
/** Everything the proxy sends on its way out. Awaited, then the caller exits. */
|
|
38
39
|
export declare function flushProxyExit(reason: ProxyExitReason): Promise<void>;
|
|
39
40
|
/** Send `daemon_died_unclean`. Fire-and-forget is fine for callers: the process lives on. */
|
|
40
|
-
export declare function reportDaemonDiedUnclean(ageMs: number, telemetry?: Telemetry): Promise<void>;
|
|
41
|
+
export declare function reportDaemonDiedUnclean(ageMs: number, cause?: Omit<DaemonDeath, 'ageMs'>, telemetry?: Telemetry): Promise<void>;
|
|
41
42
|
/**
|
|
42
43
|
* Send `wake_refused`, once per process. A dormant proxy refuses again on every client request, and
|
|
43
44
|
* one event per request would count the client's retries, not the refusal.
|
|
@@ -73,9 +73,9 @@ export async function flushProxyExit(reason) {
|
|
|
73
73
|
await Promise.allSettled([flushProxySessionMetrics(), reportProxyExited(reason)]);
|
|
74
74
|
}
|
|
75
75
|
/** Send `daemon_died_unclean`. Fire-and-forget is fine for callers: the process lives on. */
|
|
76
|
-
export async function reportDaemonDiedUnclean(ageMs, telemetry = getTelemetry()) {
|
|
76
|
+
export async function reportDaemonDiedUnclean(ageMs, cause, telemetry = getTelemetry()) {
|
|
77
77
|
await telemetry.emit(TelemetryEventKind.DAEMON_DIED_UNCLEAN, {
|
|
78
|
-
daemonDeath: { ageMs: Math.max(0, Math.round(ageMs)) },
|
|
78
|
+
daemonDeath: { ...cause, ageMs: Math.max(0, Math.round(ageMs)) },
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
/**
|