@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
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
*
|
|
19
19
|
* Pure: no IO, no clock.
|
|
20
20
|
*/
|
|
21
|
-
import { flowExpectHasConsequence, flowExpectIsPresenceOnly } from '@reticlehq/core';
|
|
21
|
+
import { PredicateKind, flowExpectHasConsequence, flowExpectIsPresenceOnly } from '@reticlehq/core';
|
|
22
|
+
import { z } from 'zod';
|
|
22
23
|
export const FlowAssertionGrade = {
|
|
23
24
|
/** At least one step (or the success end-condition) asserts a signal/network consequence. */
|
|
24
25
|
ASSERTED: 'asserted',
|
|
@@ -27,8 +28,32 @@ export const FlowAssertionGrade = {
|
|
|
27
28
|
/** Performs actions but asserts nothing observable — passes even if the feature is broken. */
|
|
28
29
|
ASSERTION_FREE: 'assertion-free',
|
|
29
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* The `assertions` block of the `reticle_flow_save` result. Declared here, next to the type it
|
|
33
|
+
* describes, because a validating profile removes a field that the output schema does not declare.
|
|
34
|
+
*/
|
|
35
|
+
export const FLOW_ASSERTIONS_OUTPUT = z
|
|
36
|
+
.object({
|
|
37
|
+
grade: z.string().describe('asserted | presence-only | assertion-free'),
|
|
38
|
+
hasConsequenceAssertion: z.boolean(),
|
|
39
|
+
totalSteps: z.number(),
|
|
40
|
+
consequenceSteps: z.number(),
|
|
41
|
+
weakSteps: z.number(),
|
|
42
|
+
checks: z
|
|
43
|
+
.record(z.string(), z.number())
|
|
44
|
+
.optional()
|
|
45
|
+
.describe('Checks of each predicate kind (text, element, route, net, signal, …) the flow runs.'),
|
|
46
|
+
warning: z.string().optional(),
|
|
47
|
+
})
|
|
48
|
+
.optional();
|
|
30
49
|
const ASSERTION_FREE_WARNING = 'This flow performs actions but asserts no observable consequence, so it will pass even if the feature is broken. Add a consequence assertion with reticle_annotate (assert-signal / assert-net) or a success-state.';
|
|
31
|
-
|
|
50
|
+
/** Why a presence check is weaker than a consequence, and the fix. Shared by both presence notes. */
|
|
51
|
+
const PRESENCE_ONLY_ADVICE = 'A locator healed to the wrong element can still pass it. Add a consequence assertion (assert-signal / assert-net / success-state).';
|
|
52
|
+
const PRESENCE_ONLY_WARNING = `This flow only checks element presence, not an observable consequence (signal/network). ${PRESENCE_ONLY_ADVICE}`;
|
|
53
|
+
/** The presence note when the checks are known: it names them, then says why they are weak. */
|
|
54
|
+
const presenceOnlyWarning = (checks) => `This flow checks ${checks} on the page. These are presence checks, not an observable consequence (signal/network). ${PRESENCE_ONLY_ADVICE}`;
|
|
55
|
+
/** The intent note when the flow has presence checks: it names them, then says what is not checked (#1801). */
|
|
56
|
+
const intentWithPresenceWarning = (checks) => `This flow declares a business intent and checks ${checks} on the page, but no step checks a request, a signal or app state. A change that keeps the page as it is but breaks the work behind it still passes. Add a success-state consequence so the flow fails when the goal stops being met.`;
|
|
32
57
|
const INTENT_WITHOUT_OUTCOME_WARNING = 'This flow declares a business intent but asserts no observable outcome (signal/network) — it claims to verify a goal it cannot actually check. Add a success-state consequence so the flow fails when the goal stops being met.';
|
|
33
58
|
// Consequence vs presence-only classification lives in @reticlehq/core (one source for all graders).
|
|
34
59
|
const expectIsConsequence = flowExpectHasConsequence;
|
|
@@ -50,8 +75,9 @@ export function flattenSteps(steps) {
|
|
|
50
75
|
const out = [];
|
|
51
76
|
for (const s of steps) {
|
|
52
77
|
out.push(s);
|
|
53
|
-
if (s.steps !== undefined)
|
|
78
|
+
if (s.steps !== undefined) {
|
|
54
79
|
out.push(...flattenSteps(s.steps));
|
|
80
|
+
}
|
|
55
81
|
}
|
|
56
82
|
return out;
|
|
57
83
|
}
|
|
@@ -72,24 +98,65 @@ export function flattenSteps(steps) {
|
|
|
72
98
|
* having run — grading is reached from save, which is exactly where a bad composite arrives first.
|
|
73
99
|
*/
|
|
74
100
|
function invokedAssertsConsequence(flow, invoked, seen) {
|
|
75
|
-
if (seen.has(flow.name))
|
|
101
|
+
if (seen.has(flow.name)) {
|
|
76
102
|
return false;
|
|
103
|
+
}
|
|
77
104
|
const deeper = new Set([...seen, flow.name]);
|
|
78
105
|
for (const step of flattenSteps(flow.steps)) {
|
|
79
|
-
if (step.invoke === undefined)
|
|
106
|
+
if (step.invoke === undefined) {
|
|
80
107
|
continue;
|
|
108
|
+
}
|
|
81
109
|
const sub = invoked.get(step.invoke);
|
|
82
|
-
if (sub === undefined)
|
|
110
|
+
if (sub === undefined) {
|
|
83
111
|
continue;
|
|
112
|
+
}
|
|
84
113
|
const subSteps = flattenSteps(sub.steps);
|
|
85
114
|
if (subSteps.some((s) => expectIsConsequence(s.expect)) || expectIsConsequence(sub.success)) {
|
|
86
115
|
return true;
|
|
87
116
|
}
|
|
88
|
-
if (invokedAssertsConsequence(sub, invoked, deeper))
|
|
117
|
+
if (invokedAssertsConsequence(sub, invoked, deeper)) {
|
|
89
118
|
return true;
|
|
119
|
+
}
|
|
90
120
|
}
|
|
91
121
|
return false;
|
|
92
122
|
}
|
|
123
|
+
/** Every leaf clause of an expectation, composites flattened. */
|
|
124
|
+
function leafClauses(expect) {
|
|
125
|
+
if (expect === undefined) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
if (PredicateKind.ALL_OF === expect.kind || PredicateKind.ANY_OF === expect.kind) {
|
|
129
|
+
return expect.predicates.flatMap(leafClauses);
|
|
130
|
+
}
|
|
131
|
+
if (PredicateKind.NOT === expect.kind) {
|
|
132
|
+
return leafClauses(expect.predicate);
|
|
133
|
+
}
|
|
134
|
+
return [expect];
|
|
135
|
+
}
|
|
136
|
+
/** Checks of each kind over the given expectations, keys in name order, or undefined for none. */
|
|
137
|
+
function countChecks(expects) {
|
|
138
|
+
const counts = new Map();
|
|
139
|
+
for (const clause of expects.flatMap(leafClauses)) {
|
|
140
|
+
counts.set(clause.kind, (counts.get(clause.kind) ?? 0) + 1);
|
|
141
|
+
}
|
|
142
|
+
if (0 === counts.size) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
return Object.fromEntries([...counts.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
146
|
+
}
|
|
147
|
+
/** "text (3), element (1) and route (1)": most checks first, ties in name order. */
|
|
148
|
+
function describeChecks(checks) {
|
|
149
|
+
const parts = Object.entries(checks)
|
|
150
|
+
.sort(([a, x], [b, y]) => y - x || a.localeCompare(b))
|
|
151
|
+
.map(([kind, n]) => `${kind} (${String(n)})`);
|
|
152
|
+
const last = parts.pop() ?? '';
|
|
153
|
+
return 0 === parts.length ? last : `${parts.join(', ')} and ${last}`;
|
|
154
|
+
}
|
|
155
|
+
function responseIsDeclared(step) {
|
|
156
|
+
const r = step.response;
|
|
157
|
+
return (r !== undefined &&
|
|
158
|
+
(r.status !== undefined || r.contains !== undefined || r.absent !== undefined));
|
|
159
|
+
}
|
|
93
160
|
export function classifyFlowAssertions(flow,
|
|
94
161
|
/**
|
|
95
162
|
* The flows this one invokes, already loaded.
|
|
@@ -103,16 +170,20 @@ invoked = new Map()) {
|
|
|
103
170
|
let consequenceSteps = 0;
|
|
104
171
|
let weakSteps = 0;
|
|
105
172
|
for (const s of all) {
|
|
106
|
-
|
|
173
|
+
// A request step's declared response judges what the server answered: a consequence.
|
|
174
|
+
if (expectIsConsequence(s.expect) || responseIsDeclared(s)) {
|
|
107
175
|
consequenceSteps++;
|
|
108
|
-
|
|
176
|
+
}
|
|
177
|
+
else if (expectIsWeak(s.expect)) {
|
|
109
178
|
weakSteps++;
|
|
179
|
+
}
|
|
110
180
|
}
|
|
111
181
|
const invokedAsserts = invokedAssertsConsequence(flow, invoked, new Set());
|
|
112
182
|
const successIsConsequence = expectIsConsequence(flow.success);
|
|
113
183
|
const successIsWeak = expectIsWeak(flow.success);
|
|
114
184
|
const hasConsequenceAssertion = consequenceSteps > 0 || successIsConsequence || invokedAsserts;
|
|
115
185
|
const hasAnyAssertion = hasConsequenceAssertion || weakSteps > 0 || successIsWeak;
|
|
186
|
+
const checks = countChecks([...all.map((s) => s.expect), flow.success]);
|
|
116
187
|
const intent = flow.intent;
|
|
117
188
|
const intentVerified = intent !== undefined && hasConsequenceAssertion;
|
|
118
189
|
let grade;
|
|
@@ -122,7 +193,9 @@ invoked = new Map()) {
|
|
|
122
193
|
}
|
|
123
194
|
else if (hasAnyAssertion) {
|
|
124
195
|
grade = FlowAssertionGrade.PRESENCE_ONLY;
|
|
125
|
-
|
|
196
|
+
// What it checks first, so the grade does not read as "checks nothing" (#1840).
|
|
197
|
+
warning =
|
|
198
|
+
checks === undefined ? PRESENCE_ONLY_WARNING : presenceOnlyWarning(describeChecks(checks));
|
|
126
199
|
}
|
|
127
200
|
else {
|
|
128
201
|
grade = FlowAssertionGrade.ASSERTION_FREE;
|
|
@@ -130,8 +203,12 @@ invoked = new Map()) {
|
|
|
130
203
|
}
|
|
131
204
|
// A declared-but-unverifiable business goal is the sharper failure: surface it over the generic
|
|
132
205
|
// assertion warning, since the flow actively claims to check something it cannot.
|
|
133
|
-
if (intent !== undefined && !hasConsequenceAssertion)
|
|
134
|
-
warning =
|
|
206
|
+
if (intent !== undefined && !hasConsequenceAssertion) {
|
|
207
|
+
warning =
|
|
208
|
+
checks === undefined
|
|
209
|
+
? INTENT_WITHOUT_OUTCOME_WARNING
|
|
210
|
+
: intentWithPresenceWarning(describeChecks(checks));
|
|
211
|
+
}
|
|
135
212
|
return {
|
|
136
213
|
grade,
|
|
137
214
|
hasConsequenceAssertion,
|
|
@@ -139,6 +216,7 @@ invoked = new Map()) {
|
|
|
139
216
|
consequenceSteps,
|
|
140
217
|
weakSteps,
|
|
141
218
|
successIsConsequence,
|
|
219
|
+
...(checks === undefined ? {} : { checks }),
|
|
142
220
|
...(intent !== undefined ? { intent } : {}),
|
|
143
221
|
intentVerified,
|
|
144
222
|
...(warning !== undefined ? { warning } : {}),
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Contradiction, type FlowStep, type FlowStepResult, type Predicate } from '@reticlehq/core';
|
|
2
|
+
/** True for a finding that only means something inside one action's window (see ONE_ACTION_ONLY). */
|
|
3
|
+
export declare function isOneActionOnly(found: {
|
|
4
|
+
kind: string;
|
|
5
|
+
}): boolean;
|
|
6
|
+
/** A step's own findings, without the ones only the end of the replay can decide. */
|
|
7
|
+
export declare function stepContradictions(found: readonly Contradiction[]): Contradiction[];
|
|
2
8
|
/** Cross-step findings belong to no single step; -1 is the address the suite verdict already uses. */
|
|
3
9
|
export declare const CROSS_STEP_INDEX = -1;
|
|
4
10
|
/**
|
|
@@ -1,8 +1,32 @@
|
|
|
1
|
+
import { ContradictionKind, } from '@reticlehq/core';
|
|
1
2
|
import { flattenSteps } from './flow-classify.js';
|
|
2
3
|
/** A contradiction's identity for de-duplication: the rule that fired, and the evidence it fired on. */
|
|
3
4
|
function contradictionId(found) {
|
|
4
5
|
return `${found.kind}|${found.detail}`;
|
|
5
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Kinds that only mean something inside ONE action's window. A duplicate request claims "one user
|
|
9
|
+
* action was performed"; across a whole flow there are many actions, so a toggle hit once per step
|
|
10
|
+
* (Pause, then Resume) read as a double submit (#2013). Each step already checks its own window.
|
|
11
|
+
*/
|
|
12
|
+
const ONE_ACTION_ONLY = new Set([
|
|
13
|
+
ContradictionKind.DUPLICATE_REQUEST,
|
|
14
|
+
ContradictionKind.DUPLICATE_REQUEST_UNRELATED,
|
|
15
|
+
]);
|
|
16
|
+
/** True for a finding that only means something inside one action's window (see ONE_ACTION_ONLY). */
|
|
17
|
+
export function isOneActionOnly(found) {
|
|
18
|
+
return ONE_ACTION_ONLY.has(found.kind);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Kinds a step window cannot decide. "Never settled" means never, and a step's window closes while
|
|
22
|
+
* the replay still runs: a request a step's predecessor started late can finish a moment after this
|
|
23
|
+
* window ends. The whole-span pass, run when the replay is over, sees whether it ever came back.
|
|
24
|
+
*/
|
|
25
|
+
const DECIDED_AT_THE_END = new Set([ContradictionKind.REQUEST_NEVER_SETTLED]);
|
|
26
|
+
/** A step's own findings, without the ones only the end of the replay can decide. */
|
|
27
|
+
export function stepContradictions(found) {
|
|
28
|
+
return found.filter((c) => !DECIDED_AT_THE_END.has(c.kind));
|
|
29
|
+
}
|
|
6
30
|
/** Cross-step findings belong to no single step; -1 is the address the suite verdict already uses. */
|
|
7
31
|
export const CROSS_STEP_INDEX = -1;
|
|
8
32
|
/**
|
|
@@ -18,7 +42,7 @@ export const CROSS_STEP_INDEX = -1;
|
|
|
18
42
|
*/
|
|
19
43
|
export function crossStepOnly(whole, steps) {
|
|
20
44
|
const seen = new Set(steps.flatMap((step) => (step.contradictions ?? []).map(contradictionId)));
|
|
21
|
-
return whole.filter((found) => !seen.has(contradictionId(found)));
|
|
45
|
+
return whole.filter((found) => !seen.has(contradictionId(found)) && !isOneActionOnly(found));
|
|
22
46
|
}
|
|
23
47
|
/**
|
|
24
48
|
* Every consequence the flow declared, sub-steps included. The whole-span pass judges the same
|
|
@@ -36,12 +36,37 @@ export declare function routeClaims(startPath: string | undefined, steps: readon
|
|
|
36
36
|
/**
|
|
37
37
|
* The preconditions only another flow can establish.
|
|
38
38
|
*
|
|
39
|
-
* A route claim
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* A route claim with a `pathname` is not one of them: replay navigates to a declared route itself
|
|
40
|
+
* (see `declaredRoute`), and checks the claim, its `contains` too, once the tab is there (see
|
|
41
|
+
* `unmetRouteClaim`). So a route claim neither opts the flow out of the reset nor gates the run
|
|
42
|
+
* here. The claim on the page the flow starts on is the one a saved drive writes for the map (see
|
|
43
|
+
* routeClaims) — reading it as "starts from leftover state" would stop every auto-saved flow from
|
|
44
|
+
* ever being reset. A route claim on ANOTHER page was read that way, and a suite then ran the flow
|
|
45
|
+
* on whatever page the previous flow left open (#2034).
|
|
43
46
|
*/
|
|
44
47
|
export declare function establishedState(flow: FlowFile): readonly Predicate[];
|
|
48
|
+
/**
|
|
49
|
+
* The route a flow declares in `requires` when that says more than its `startPath`: the first
|
|
50
|
+
* such claim, in any order of the claims. Undefined when there is none, and replay then arrives
|
|
51
|
+
* at `startPath` as before. Replay puts the tab on this route before step 1 (#2034).
|
|
52
|
+
*/
|
|
53
|
+
export declare function declaredRoute(flow: FlowFile): string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* The first declared route claim that the page `observed` does not satisfy, said for a reader.
|
|
56
|
+
* Undefined when they all hold. Each claim is checked whole: its `pathname` by `onRoute`, and its
|
|
57
|
+
* `contains` against the whole route, as the `route` predicate checks it.
|
|
58
|
+
*/
|
|
59
|
+
export declare function unmetRouteClaim(flow: FlowFile, observed: string): string | undefined;
|
|
60
|
+
/** Whether a declared route names an entity id with a `:name` segment, for example `/cases/:id/notes`. */
|
|
61
|
+
export declare function isRoutePattern(route: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Is the tab on a declared route?
|
|
64
|
+
*
|
|
65
|
+
* A plain route is compared as `samePath` compares a `startPath`. A pattern is compared segment by
|
|
66
|
+
* segment on the router's path, and a `:name` segment matches any one non-empty segment. The query
|
|
67
|
+
* is not compared for a pattern: the pattern says which page, and the id says which record.
|
|
68
|
+
*/
|
|
69
|
+
export declare function onRoute(observed: string, route: string): boolean;
|
|
45
70
|
/**
|
|
46
71
|
* Is the tab where the flow asked to start? Up to a trailing slash, and up to the query the flow
|
|
47
72
|
* did not ask about.
|
|
@@ -66,3 +91,12 @@ export declare function samePath(observed: string, expected: string): boolean;
|
|
|
66
91
|
* consequence can also wait 15 s for its page.
|
|
67
92
|
*/
|
|
68
93
|
export declare function startPathBudgetMs(flow: FlowFile): number;
|
|
94
|
+
/**
|
|
95
|
+
* A pattern route with its `:name` segments filled from the page `observed`.
|
|
96
|
+
*
|
|
97
|
+
* A segment is taken only when every segment before it in the route agrees with the page: from
|
|
98
|
+
* `/cases/c-42/files`, the route `/cases/:id/notes` becomes `/cases/c-42/notes`. So another section
|
|
99
|
+
* of the same case keeps the case, and the recorded id, which can be a deleted record, is not used.
|
|
100
|
+
* Undefined when the page has no id to give (`/settings`), or the route has no `:name` segment.
|
|
101
|
+
*/
|
|
102
|
+
export declare function fillRouteParams(route: string, observed: string): string | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { PredicateKind } from '@reticlehq/core';
|
|
3
|
+
import { routePathOf } from '@reticlehq/engine/question/predicate/predicate-route.js';
|
|
3
4
|
/**
|
|
4
5
|
* One journey, however many times it was driven.
|
|
5
6
|
*
|
|
@@ -104,17 +105,108 @@ export function routeClaims(startPath, steps) {
|
|
|
104
105
|
/**
|
|
105
106
|
* The preconditions only another flow can establish.
|
|
106
107
|
*
|
|
107
|
-
* A route claim
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
108
|
+
* A route claim with a `pathname` is not one of them: replay navigates to a declared route itself
|
|
109
|
+
* (see `declaredRoute`), and checks the claim, its `contains` too, once the tab is there (see
|
|
110
|
+
* `unmetRouteClaim`). So a route claim neither opts the flow out of the reset nor gates the run
|
|
111
|
+
* here. The claim on the page the flow starts on is the one a saved drive writes for the map (see
|
|
112
|
+
* routeClaims) — reading it as "starts from leftover state" would stop every auto-saved flow from
|
|
113
|
+
* ever being reset. A route claim on ANOTHER page was read that way, and a suite then ran the flow
|
|
114
|
+
* on whatever page the previous flow left open (#2034).
|
|
111
115
|
*/
|
|
112
116
|
export function establishedState(flow) {
|
|
117
|
+
return (flow.requires ?? []).filter((claim) => !(PredicateKind.ROUTE === claim.kind && claim.pathname !== undefined));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The route claims in `requires` that say more than the flow's `startPath`, in order.
|
|
121
|
+
*
|
|
122
|
+
* A claim on the `startPath` itself is left out: that is the claim every saved drive writes, and
|
|
123
|
+
* replay already arrives there. Every other claim names a page the flow needs (#2034).
|
|
124
|
+
*/
|
|
125
|
+
function pageClaims(flow) {
|
|
113
126
|
const start = flow.startPath;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
claim.pathname
|
|
117
|
-
|
|
127
|
+
const claims = [];
|
|
128
|
+
for (const claim of flow.requires ?? []) {
|
|
129
|
+
if (PredicateKind.ROUTE !== claim.kind || claim.pathname === undefined) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (0 === claim.pathname.length) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (start !== undefined && samePath(claim.pathname, start)) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
claims.push({
|
|
139
|
+
pathname: claim.pathname,
|
|
140
|
+
...(claim.contains === undefined ? {} : { contains: claim.contains }),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return claims;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The route a flow declares in `requires` when that says more than its `startPath`: the first
|
|
147
|
+
* such claim, in any order of the claims. Undefined when there is none, and replay then arrives
|
|
148
|
+
* at `startPath` as before. Replay puts the tab on this route before step 1 (#2034).
|
|
149
|
+
*/
|
|
150
|
+
export function declaredRoute(flow) {
|
|
151
|
+
return pageClaims(flow)[0]?.pathname;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The first declared route claim that the page `observed` does not satisfy, said for a reader.
|
|
155
|
+
* Undefined when they all hold. Each claim is checked whole: its `pathname` by `onRoute`, and its
|
|
156
|
+
* `contains` against the whole route, as the `route` predicate checks it.
|
|
157
|
+
*/
|
|
158
|
+
export function unmetRouteClaim(flow, observed) {
|
|
159
|
+
for (const claim of pageClaims(flow)) {
|
|
160
|
+
if (!onRoute(observed, claim.pathname)) {
|
|
161
|
+
return `it is not on ${claim.pathname}`;
|
|
162
|
+
}
|
|
163
|
+
if (claim.contains !== undefined && !observed.includes(claim.contains)) {
|
|
164
|
+
return `its address does not contain "${claim.contains}"`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
/** The mark of a route segment that stands for any one entity id: `:id` in `/cases/:id/notes`. */
|
|
170
|
+
const ROUTE_PARAM_MARK = ':';
|
|
171
|
+
/** Whether a route segment is a placeholder such as `:id`. */
|
|
172
|
+
function isRouteParam(segment) {
|
|
173
|
+
return segment.startsWith(ROUTE_PARAM_MARK) && ROUTE_PARAM_MARK.length < segment.length;
|
|
174
|
+
}
|
|
175
|
+
/** The segments of the path the router is on, without a query or a trailing slash. */
|
|
176
|
+
function routerSegments(path) {
|
|
177
|
+
let parsed;
|
|
178
|
+
try {
|
|
179
|
+
parsed = new URL(path, 'http://localhost');
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
return routePathOf(parsed.pathname, parsed.hash).replace(/\/$/, '').split('/');
|
|
185
|
+
}
|
|
186
|
+
/** Whether a declared route names an entity id with a `:name` segment, for example `/cases/:id/notes`. */
|
|
187
|
+
export function isRoutePattern(route) {
|
|
188
|
+
return (routerSegments(route) ?? []).some(isRouteParam);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Is the tab on a declared route?
|
|
192
|
+
*
|
|
193
|
+
* A plain route is compared as `samePath` compares a `startPath`. A pattern is compared segment by
|
|
194
|
+
* segment on the router's path, and a `:name` segment matches any one non-empty segment. The query
|
|
195
|
+
* is not compared for a pattern: the pattern says which page, and the id says which record.
|
|
196
|
+
*/
|
|
197
|
+
export function onRoute(observed, route) {
|
|
198
|
+
if (!isRoutePattern(route)) {
|
|
199
|
+
return samePath(observed, route);
|
|
200
|
+
}
|
|
201
|
+
const here = routerSegments(observed);
|
|
202
|
+
const wanted = routerSegments(route);
|
|
203
|
+
if (here === undefined || wanted === undefined || here.length !== wanted.length) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
return wanted.every((segment, i) => {
|
|
207
|
+
const at = here[i] ?? '';
|
|
208
|
+
return isRouteParam(segment) ? 0 < at.length : segment === at;
|
|
209
|
+
});
|
|
118
210
|
}
|
|
119
211
|
/**
|
|
120
212
|
* Is the tab where the flow asked to start? Up to a trailing slash, and up to the query the flow
|
|
@@ -150,3 +242,41 @@ export function startPathBudgetMs(flow) {
|
|
|
150
242
|
const first = flow.steps[0]?.timeoutMs ?? flow.signalTimeoutMs ?? 0;
|
|
151
243
|
return Math.max(START_PATH_ARRIVAL_TIMEOUT_MS, first);
|
|
152
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* A pattern route with its `:name` segments filled from the page `observed`.
|
|
247
|
+
*
|
|
248
|
+
* A segment is taken only when every segment before it in the route agrees with the page: from
|
|
249
|
+
* `/cases/c-42/files`, the route `/cases/:id/notes` becomes `/cases/c-42/notes`. So another section
|
|
250
|
+
* of the same case keeps the case, and the recorded id, which can be a deleted record, is not used.
|
|
251
|
+
* Undefined when the page has no id to give (`/settings`), or the route has no `:name` segment.
|
|
252
|
+
*/
|
|
253
|
+
export function fillRouteParams(route, observed) {
|
|
254
|
+
const wanted = routerSegments(route);
|
|
255
|
+
const here = routerSegments(observed);
|
|
256
|
+
if (wanted === undefined || here === undefined) {
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
const values = [];
|
|
260
|
+
for (const [i, segment] of wanted.entries()) {
|
|
261
|
+
if (!isRouteParam(segment)) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const agrees = wanted
|
|
265
|
+
.slice(0, i)
|
|
266
|
+
.every((before, j) => isRouteParam(before) || before === here[j]);
|
|
267
|
+
const value = here[i] ?? '';
|
|
268
|
+
if (!agrees || 0 === value.length) {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
values.push(value);
|
|
272
|
+
}
|
|
273
|
+
if (0 === values.length) {
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
let next = 0;
|
|
277
|
+
return route.replace(/(?<=\/):[^/?#]+/g, (param) => {
|
|
278
|
+
const value = values[next] ?? param;
|
|
279
|
+
next += 1;
|
|
280
|
+
return value;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
@@ -6,22 +6,6 @@ export declare function latestRecordedFlow(events: ReticleEvent[]): {
|
|
|
6
6
|
flow: import('@reticlehq/core').FlowFile;
|
|
7
7
|
} | undefined;
|
|
8
8
|
export { flowErrorMessage } from './flow-result.js';
|
|
9
|
-
/**
|
|
10
|
-
* The honest answer when the document replay was driving went away mid-run.
|
|
11
|
-
*
|
|
12
|
-
* Not a pass: nothing graded the journey, and the steps in hand are only the ones the departed
|
|
13
|
-
* document answered. Not a failure either — the app was never observed doing anything wrong, and
|
|
14
|
-
* reporting one would be the false red that sends a reader into product code that is fine. So it
|
|
15
|
-
* lands in the bucket this engine already has for "nothing was proved", the same one an unmet
|
|
16
|
-
* precondition uses, with the steps that DID answer attached and no row invented for the one that
|
|
17
|
-
* did not.
|
|
18
|
-
*
|
|
19
|
-
* Why replay reports rather than follows: the successor cannot be identified at the moment the
|
|
20
|
-
* socket closes. `SessionManager.remove` runs from the close handler, writes the tombstone a
|
|
21
|
-
* successor is later matched against, and has nothing to match yet — so the rejection is a plain
|
|
22
|
-
* transport failure by design, and a step in the middle of a journey has no budget to sit and poll
|
|
23
|
-
* with. `navigateAndAwait` CAN wait, because it knows it asked for the navigation, and it does.
|
|
24
|
-
*/
|
|
25
9
|
export declare function lostDocumentResult(name: string, lost: DocumentLostDuringReplay): FlowReplayResult;
|
|
26
10
|
export declare function replayNamedFlow(deps: ToolDeps, args: Record<string, unknown>,
|
|
27
11
|
/** Wraps the session the steps run on, so a caller can watch what they resolved. */
|