@reticlehq/server 3.9.0 → 3.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +13 -12
- 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 -187
- 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/cli-doctor.js +1 -1
- package/dist/command/cli/cli-flow-commands.js +2 -9
- package/dist/command/cli/cli-kill.js +1 -1
- 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 +17 -15
- package/dist/command/cli/cli-parse.js +44 -27
- 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 +58 -29
- package/dist/command/cli/cli-verify.d.ts +11 -2
- package/dist/command/cli/cli-verify.js +27 -8
- 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/doctor-daemon-line.js +6 -3
- package/dist/command/cli/doctor-repairs.js +1 -1
- 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/lifecycle/daemon-lifecycle.d.ts +1 -1
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +1 -1
- package/dist/command/cli/ports/port-holder.d.ts +9 -0
- package/dist/command/cli/ports/port-holder.js +27 -3
- package/dist/command/cli/status/status-lines.d.ts +1 -0
- package/dist/command/cli/status/status-lines.js +22 -0
- package/dist/command/cli/status-fields.d.ts +5 -1
- package/dist/command/cli/status-fields.js +7 -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 +46 -41
- 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 +12 -1
- package/dist/command/daemon/daemon.js +35 -4
- package/dist/command/daemon/lifetime/death-cause.js +21 -2
- package/dist/command/daemon/lifetime/heartbeat.js +7 -2
- package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
- package/dist/command/daemon/lifetime/startup-failure.js +62 -0
- 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-runtime.js +43 -2
- 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 +6 -3
- package/dist/command/update/updater.js +6 -0
- package/dist/command/version/version-skew.js +1 -1
- 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/visual/visual-tools.js +66 -19
- package/dist/index.js +7 -4
- 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/runner-port.d.ts +2 -0
- package/dist/judgement/runs/runner-port.js +23 -5
- 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 +44 -0
- 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 +19 -3
- package/dist/language/flows/recording/tape/recordings.js +71 -5
- package/dist/language/flows/replay.d.ts +19 -5
- package/dist/language/flows/replay.js +99 -7
- 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/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/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/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 +28 -9
- 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 +22 -20
- package/dist/portal/input/real-input.js +41 -31
- package/dist/portal/input/viewport-tools.d.ts +8 -0
- package/dist/portal/input/viewport-tools.js +75 -18
- package/dist/portal/pool/browser-pool.d.ts +11 -29
- package/dist/portal/pool/browser-pool.js +35 -17
- package/dist/portal/pool/context-permissions.d.ts +17 -3
- package/dist/portal/pool/context-permissions.js +36 -5
- 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 +44 -17
- package/dist/portal/pool/lease-page-error.js +147 -5
- package/dist/portal/pool/page-error-journal.d.ts +7 -1
- package/dist/portal/pool/page-error-journal.js +27 -0
- package/dist/portal/pool/playwright-launcher.js +51 -8
- package/dist/portal/pool/pool-contract.d.ts +27 -1
- package/dist/portal/pool/pool-lease.d.ts +36 -1
- package/dist/portal/pool/zero-install.d.ts +8 -0
- package/dist/portal/pool/zero-install.js +23 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +7 -6
- package/dist/portal/session/dev-server/dev-command.js +61 -15
- 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 +67 -25
- package/dist/portal/session/no-session-next-action.d.ts +2 -0
- package/dist/portal/session/no-session-next-action.js +12 -3
- package/dist/portal/session/no-session-watch.d.ts +6 -0
- package/dist/portal/session/no-session-watch.js +38 -3
- 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 +174 -63
- package/dist/start-wiring.d.ts +2 -1
- package/dist/start-wiring.js +2 -1
- package/dist/surface/mcp/mcp.js +3 -2
- 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 +1 -1
- 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/after-match.d.ts +4 -1
- package/dist/surface/tools/act/after-match.js +8 -1
- 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 +15 -1
- package/dist/surface/tools/act/resolve-target.js +24 -1
- package/dist/surface/tools/act/sequence-grade.d.ts +12 -0
- package/dist/surface/tools/act/sequence-grade.js +37 -26
- 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 +78 -50
- package/dist/surface/tools/act-tools.d.ts +1 -13
- package/dist/surface/tools/act-tools.js +74 -86
- 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 +16 -37
- package/dist/surface/tools/browser-tools.js +12 -6
- 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 +9 -2
- package/dist/surface/tools/error-recovery.d.ts +12 -1
- package/dist/surface/tools/error-recovery.js +91 -21
- package/dist/surface/tools/explore-tools.js +1 -1
- package/dist/surface/tools/invoke-tool.js +3 -2
- 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 +35 -1
- package/dist/surface/tools/lease-hint.js +42 -0
- package/dist/surface/tools/lease-readiness.d.ts +26 -0
- package/dist/surface/tools/lease-readiness.js +79 -0
- package/dist/surface/tools/lease-seed.d.ts +14 -1
- package/dist/surface/tools/lease-seed.js +35 -3
- package/dist/surface/tools/lease-tools.d.ts +3 -41
- package/dist/surface/tools/lease-tools.js +62 -110
- 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/session-ports.d.ts +14 -0
- package/dist/surface/tools/session-ports.js +53 -0
- package/dist/surface/tools/tool-kit.d.ts +6 -0
- package/dist/surface/tools/tool-kit.js +2 -3
- package/dist/surface/tools/tool-surface.js +0 -1
- package/dist/surface/tools/tools.js +28 -5
- 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 +1 -0
- package/dist/telemetry/tool-refused.d.ts +6 -3
- package/dist/telemetry/tool-refused.js +8 -3
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +1 -1
- 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 +1 -1
- package/docs/cli/grade.mdx +97 -0
- package/docs/cli/init.mdx +2 -2
- package/docs/cli/license.mdx +9 -7
- package/docs/cli/restart.mdx +7 -5
- package/docs/cli/rollback.mdx +5 -5
- 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 +33 -23
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +2 -2
- package/docs/environment.mdx +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 +1 -1
- package/docs/license-activation.mdx +3 -3
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +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 +2 -2
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/verify.mdx +4 -1
- package/docs/troubleshooting.mdx +30 -1
- package/docs/usage.md +17 -14
- package/package.json +11 -14
- package/dist/features/exhaust/exhaust-run.d.ts +0 -33
- package/dist/features/exhaust/exhaust-run.js +0 -82
|
@@ -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. */
|
|
@@ -3,13 +3,15 @@ import { withLeaseKeyboard } from '../../surface/tools/real-input-attempt.js';
|
|
|
3
3
|
import { learnFromRun } from '@reticlehq/engine/evidence/learned-guards.js';
|
|
4
4
|
import { EventType, FLOW_SIGNAL_TIMEOUT_MS, FlowErrorCode, RecordedFlowSchema, ReplayStatus, RunKind, RunStatus, } from '@reticlehq/core';
|
|
5
5
|
import { haltedFrom } from './recording/replay-halt.js';
|
|
6
|
-
import { establishedState } from './flow-journey.js';
|
|
6
|
+
import { establishedState, startPathBudgetMs } from './flow-journey.js';
|
|
7
|
+
import { resolveSessionWithin } from '../../portal/session/timing/resolve-within.js';
|
|
8
|
+
import { WALL_CLOCK } from '../../portal/session/timing/wall-clock.js';
|
|
7
9
|
import { asString } from '@reticlehq/core';
|
|
8
10
|
import { partialStatus, replayFlow, replayWindow } from './flow-replay.js';
|
|
9
11
|
import { staleTargetResult } from './flow-step-runners.js';
|
|
10
12
|
import { assertSuccess, dynamicTestids, successLabel, SUCCESS_STEP_TOOL } from './flow-success.js';
|
|
11
13
|
import { buildDecision, unverifiableReason } from './decision.js';
|
|
12
|
-
import { unsuppliedSecrets } from './fields/flow-secret-field.js';
|
|
14
|
+
import { secretEnvKey, secretValue, unsuppliedSecrets } from './fields/flow-secret-field.js';
|
|
13
15
|
import { assertStepExpect, DocumentLostDuringReplay, resumeArg, unresolvedResume, } from './flow-replay.js';
|
|
14
16
|
import { isDocumentGoneError } from '../../portal/session/facts/session-replaced.js';
|
|
15
17
|
import { classifyFlowAssertions, flattenSteps } from './flow-classify.js';
|
|
@@ -33,7 +35,8 @@ import { replayStepShooter } from '../../features/visual/step-shots.js';
|
|
|
33
35
|
import { flowsForSession } from './flow-store-for-session.js';
|
|
34
36
|
import { projectForRoot } from '../../memory/project/project-for-root.js';
|
|
35
37
|
import { CROSS_STEP_INDEX, crossStepOnly, declaredIn } from './flow-cross-step.js';
|
|
36
|
-
import { arriveAtStartPath, startPathMismatchHint, withStartPageWait } from './flow-start-path.js';
|
|
38
|
+
import { arriveAtStartPath, navigateAndAwait, noteReplayEnded, routeClaimsWhereTheTabIs, startPathMismatchHint, withStartPageWait, } from './flow-start-path.js';
|
|
39
|
+
import { requestSender } from './steps/page-steps.js';
|
|
37
40
|
export function latestRecordedFlow(events) {
|
|
38
41
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
39
42
|
const event = events[i];
|
|
@@ -226,6 +229,15 @@ async function firstUnmetPrecondition(session, flow, since) {
|
|
|
226
229
|
* transport failure by design, and a step in the middle of a journey has no budget to sit and poll
|
|
227
230
|
* with. `navigateAndAwait` CAN wait, because it knows it asked for the navigation, and it does.
|
|
228
231
|
*/
|
|
232
|
+
/** The path a `navigate` step expects to land on: `/u07` for `/u07?x=1` or `http://host/u07`. */
|
|
233
|
+
function pathOfUrl(url) {
|
|
234
|
+
try {
|
|
235
|
+
return new URL(url, 'http://localhost').pathname;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return url;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
229
241
|
export function lostDocumentResult(name, lost) {
|
|
230
242
|
return {
|
|
231
243
|
name,
|
|
@@ -244,6 +256,20 @@ export function lostDocumentResult(name, lost) {
|
|
|
244
256
|
export async function replayNamedFlow(deps, args,
|
|
245
257
|
/** Wraps the session the steps run on, so a caller can watch what they resolved. */
|
|
246
258
|
observe = (s) => s) {
|
|
259
|
+
try {
|
|
260
|
+
return await replayOnce(deps, args, observe);
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
// Where this flow left the tab, so the next flow's reset does not keep its query.
|
|
264
|
+
try {
|
|
265
|
+
noteReplayEnded(deps.sessions.resolve(asString(args['sessionId'])));
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
/* no tab is left to note */
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async function replayOnce(deps, args, observe) {
|
|
247
273
|
const startedAt = deps.now();
|
|
248
274
|
const name = asString(args['flowName']) ?? '';
|
|
249
275
|
// Resolve within the connecting app's scope so a shared daemon replays THIS project's flow, not a
|
|
@@ -274,7 +300,9 @@ observe = (s) => s) {
|
|
|
274
300
|
const replayRoot = sessionRoot(deps, asString(args['sessionId']));
|
|
275
301
|
const intents = new IntentStore(deps.fs, replayRoot, { now: deps.now });
|
|
276
302
|
const intentSaid = await flowIntentStatement(intents, loaded.value);
|
|
277
|
-
|
|
303
|
+
// `verify` reloads the page between flows. A flow that starts in the reconnect gap waits for the
|
|
304
|
+
// page, up to the start-page budget, instead of ending unknown at once (#2168).
|
|
305
|
+
const connected = await resolveSessionWithin(deps.sessions, asString(args['sessionId']), startPathBudgetMs(loaded.value), { now: deps.now, sleep: WALL_CLOCK.sleep });
|
|
278
306
|
// The FlowFile contract: replay navigates to the flow's startPath before step 1, and the steps run
|
|
279
307
|
// on the session the SDK reconnects as. When arrival can't be confirmed, replay proceeds on the
|
|
280
308
|
// connected session — and the hint below turns the wrong-page drift into an actionable next move.
|
|
@@ -285,10 +313,11 @@ observe = (s) => s) {
|
|
|
285
313
|
const stillLoading = pool === undefined ? undefined : () => pool.leaseLoading(connected.id);
|
|
286
314
|
const arrival = continueAt === undefined
|
|
287
315
|
? await arriveAtStartPath(deps.sessions, connected, loaded.value, undefined, undefined, stillLoading)
|
|
288
|
-
:
|
|
316
|
+
: routeClaimsWhereTheTabIs(loaded.value, connected);
|
|
289
317
|
// Continuing on `connected` after the tab left timed every step out on a dead handle (#1129).
|
|
290
|
-
|
|
291
|
-
|
|
318
|
+
// A declared route that replay could not reach is the same: the steps would run on another page.
|
|
319
|
+
const reason = arrival.lost ?? arrival.unreached;
|
|
320
|
+
if (reason !== undefined) {
|
|
292
321
|
return withStartPageWait({
|
|
293
322
|
name: loaded.value.name,
|
|
294
323
|
status: ReplayStatus.UNVERIFIABLE,
|
|
@@ -303,6 +332,29 @@ observe = (s) => s) {
|
|
|
303
332
|
// Floor the success oracle at the start of THIS replay so a stale signal from a prior run
|
|
304
333
|
// in the same session can't fake a pass.
|
|
305
334
|
const replayFloor = session.elapsed();
|
|
335
|
+
/*
|
|
336
|
+
* A `navigate` step loads another page, and the page that asked for the load is gone. The journey
|
|
337
|
+
* goes on with the session of the page that loaded, and so does everything judged after the last
|
|
338
|
+
* step: the cross-step pass, the success oracle and the deviation report. That page's clock starts
|
|
339
|
+
* at its own connect, so its floor is 0.
|
|
340
|
+
*/
|
|
341
|
+
let live = session;
|
|
342
|
+
let liveFloor = replayFloor;
|
|
343
|
+
const openPage = async (url) => {
|
|
344
|
+
const outcome = await navigateAndAwait(deps.sessions, live, url, pathOfUrl(url), startPathBudgetMs(loaded.value), undefined, { acceptRedirect: true });
|
|
345
|
+
if ('arrived' === outcome.kind || 'redirected' === outcome.kind) {
|
|
346
|
+
live = outcome.session;
|
|
347
|
+
liveFloor = 0;
|
|
348
|
+
return { session: observe(withLeaseKeyboard(deps, live)) };
|
|
349
|
+
}
|
|
350
|
+
if ('refused' === outcome.kind) {
|
|
351
|
+
return { error: `the page refused to open ${url}` };
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
error: `no page of this app answered in ${String(outcome.waitedMs)} ms after opening ${url}; ` +
|
|
355
|
+
'the page may have left the app, which a flow cannot follow',
|
|
356
|
+
};
|
|
357
|
+
};
|
|
306
358
|
// An upload path becomes bytes once, before step 1, against the app's project (#1769).
|
|
307
359
|
const uploadDeps = { fs: deps.fs, reticleRoot: replayRoot };
|
|
308
360
|
const replayable = await resolveFlowUploads(uploadDeps, loaded.value);
|
|
@@ -382,9 +434,14 @@ observe = (s) => s) {
|
|
|
382
434
|
// Bug-sweep mode: keep going past a step whose action ran and whose consequence merely did
|
|
383
435
|
// not hold, so one flow reports one verdict per step instead of stopping at the first defect.
|
|
384
436
|
sweep: true === args['sweep'],
|
|
437
|
+
navigate: openPage,
|
|
438
|
+
request: requestSender(() => live.url, deps.realInput, (account) => secretValue(secretEnvKey(account), process.env)),
|
|
385
439
|
...(from === undefined ? {} : { from }),
|
|
386
440
|
...(to === undefined ? {} : { to }),
|
|
387
441
|
...(continueAt === undefined ? {} : { at: continueAt }),
|
|
442
|
+
// Replay loaded the start page, so the load is part of the journey. The successor session
|
|
443
|
+
// began with that load, at time 0.
|
|
444
|
+
...(arrival.session === undefined ? {} : { loadSince: 0 }),
|
|
388
445
|
// The HUD's replay chip, when the human pressed ▶. In-process only: an agent's arguments
|
|
389
446
|
// arrive as JSON and can never carry a function.
|
|
390
447
|
onStepResult: replayStepShooter(deps, session.id),
|
|
@@ -412,9 +469,9 @@ observe = (s) => s) {
|
|
|
412
469
|
*/
|
|
413
470
|
const crossStep = (() => {
|
|
414
471
|
try {
|
|
415
|
-
const span = { since:
|
|
416
|
-
const events =
|
|
417
|
-
const scope = { ...appScope(
|
|
472
|
+
const span = { since: liveFloor, until: live.elapsed() };
|
|
473
|
+
const events = live.eventsSince(liveFloor).filter((e) => e.t >= liveFloor);
|
|
474
|
+
const scope = { ...appScope(live), declared: declaredIn(loaded.value.steps) };
|
|
418
475
|
return crossStepOnly(stepEffect(events, span, scope).contradictions ?? [], steps);
|
|
419
476
|
}
|
|
420
477
|
catch {
|
|
@@ -426,11 +483,11 @@ observe = (s) => s) {
|
|
|
426
483
|
// even though all locators resolved — the regression a healed-but-wrong locator ships green.
|
|
427
484
|
const stepsClean = steps.length > 0 && steps.every((s) => s.ok && s.drift === undefined);
|
|
428
485
|
if (stepsClean && loaded.value.success !== undefined) {
|
|
429
|
-
const verdict = await assertSuccess(
|
|
486
|
+
const verdict = await assertSuccess(live, loaded.value.success, dynamicTestids(loaded.value), waitForPredicate,
|
|
430
487
|
// The flow's own declaration, not the built-in floor: an app slow enough to need a longer
|
|
431
488
|
// step wait is slow enough that its OUTCOME lands late too, and greening every step only to
|
|
432
489
|
// fail the success oracle at 4s is the same false red one layer down.
|
|
433
|
-
loaded.value.signalTimeoutMs ?? FLOW_SIGNAL_TIMEOUT_MS,
|
|
490
|
+
loaded.value.signalTimeoutMs ?? FLOW_SIGNAL_TIMEOUT_MS, liveFloor);
|
|
434
491
|
const row = {
|
|
435
492
|
step: steps.length,
|
|
436
493
|
tool: SUCCESS_STEP_TOOL,
|
|
@@ -440,7 +497,12 @@ observe = (s) => s) {
|
|
|
440
497
|
};
|
|
441
498
|
steps.push(row);
|
|
442
499
|
}
|
|
443
|
-
|
|
500
|
+
// The caller's request counts only when replay itself loaded the start page and stayed there.
|
|
501
|
+
const startControlRequired = true === args['requireStartControl'] &&
|
|
502
|
+
arrival.session !== undefined &&
|
|
503
|
+
arrival.redirected === undefined &&
|
|
504
|
+
arrival.resetCost === undefined;
|
|
505
|
+
const staleTarget = staleTargetResult(name, steps, { startControlRequired });
|
|
444
506
|
if (staleTarget !== undefined) {
|
|
445
507
|
return withStartPageWait(staleTarget, arrival);
|
|
446
508
|
}
|
|
@@ -480,7 +542,7 @@ observe = (s) => s) {
|
|
|
480
542
|
}
|
|
481
543
|
}
|
|
482
544
|
// Push-default: the deviation report over this drive's segments, learned across runs. Best-effort.
|
|
483
|
-
const deviation = await computeReplayDeviation(deps,
|
|
545
|
+
const deviation = await computeReplayDeviation(deps, live, liveFloor, replayRoot);
|
|
484
546
|
/*
|
|
485
547
|
* What the team already knows about this flow, fetched on the agent's behalf.
|
|
486
548
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CommandResult, ReticleEvent } from '@reticlehq/core';
|
|
2
2
|
import type { Predicate } from '@reticlehq/engine/question/predicate/predicate-schema.js';
|
|
3
|
-
import type { EvalResult } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
3
|
+
import type { Baselines, EvalResult } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
4
4
|
/**
|
|
5
5
|
* The shapes a replay needs from whatever is driving it.
|
|
6
6
|
*
|
|
@@ -41,6 +41,8 @@ export interface FlowReplaySession {
|
|
|
41
41
|
* `since` is the event-time floor (default 0 = whole buffer): pass the cursor captured before a
|
|
42
42
|
* replay so the success oracle can't be satisfied by a stale signal from a prior replay/run.
|
|
43
43
|
*/
|
|
44
|
-
export type WaitForSignal = (session: FlowReplaySession, predicate: Predicate, timeoutMs: number, since?: number
|
|
44
|
+
export type WaitForSignal = (session: FlowReplaySession, predicate: Predicate, timeoutMs: number, since?: number,
|
|
45
|
+
/** Readings taken before the action, for `unchanged` and the other comparing properties. */
|
|
46
|
+
baselines?: Baselines) => Promise<EvalResult>;
|
|
45
47
|
/** Injected sleeper so tests drive replay with a no-op clock; production waits on a real timer. */
|
|
46
48
|
export type Sleep = (ms: number) => Promise<void>;
|
|
@@ -2,6 +2,8 @@ export { nearestIsAmbiguous, nearestTestid, resolveQuery } from './flow-anchor.j
|
|
|
2
2
|
import type { FlowReplaySession, WaitForSignal, Sleep } from './flow-replay-types.js';
|
|
3
3
|
export type { FlowReplaySession, WaitForSignal, Sleep } from './flow-replay-types.js';
|
|
4
4
|
import { type Drift, type FlowFile, type FlowStep, type FlowStepResult, ReplayStatus } from '@reticlehq/core';
|
|
5
|
+
import { type Baselines } from '@reticlehq/engine/evidence/baseline.js';
|
|
6
|
+
import { type OpenPage, type SendRequest } from './steps/page-steps.js';
|
|
5
7
|
/**
|
|
6
8
|
* The document this replay was driving went away mid-run.
|
|
7
9
|
*
|
|
@@ -42,7 +44,7 @@ export declare function runTestidStep(session: FlowReplaySession, step: FlowStep
|
|
|
42
44
|
* quietly stop agreeing, and the disagreement would land as a flow that replays green against a
|
|
43
45
|
* state it was never meant to run in.
|
|
44
46
|
*/
|
|
45
|
-
export declare function assertStepExpect(session: FlowReplaySession, expect: NonNullable<FlowStep['expect']>, waitForSignal: WaitForSignal, timeoutMs: number, since: number): Promise<Drift | undefined>;
|
|
47
|
+
export declare function assertStepExpect(session: FlowReplaySession, expect: NonNullable<FlowStep['expect']>, waitForSignal: WaitForSignal, timeoutMs: number, since: number, baselines?: Baselines): Promise<Drift | undefined>;
|
|
46
48
|
/**
|
|
47
49
|
* A text check that missed only on case says so, with what would hold. Text checks match case
|
|
48
50
|
* exactly now, so a flow saved when they did not goes red on capitalisation alone; the person
|
|
@@ -62,6 +64,13 @@ export declare function withCaseOnly(reason: string, verdict: {
|
|
|
62
64
|
* How a replay is loaded, where it starts reporting, and how far it goes.
|
|
63
65
|
*/
|
|
64
66
|
export interface ReplayFromOptions {
|
|
67
|
+
/**
|
|
68
|
+
* Opens the page a `navigate` step names and returns the session of the page that loaded. Supplied
|
|
69
|
+
* by the caller that owns the sessions. Absent: a `navigate` step fails, never skips.
|
|
70
|
+
*/
|
|
71
|
+
navigate?: OpenPage<FlowReplaySession>;
|
|
72
|
+
/** Sends the request a `request` step names. Absent: a `request` step fails, never skips. */
|
|
73
|
+
request?: SendRequest;
|
|
65
74
|
/**
|
|
66
75
|
* Told after every step it reports: how many are done, of how many, and whether this one held.
|
|
67
76
|
* The HUD's replay chip draws its progress from this; it must never throw into the replay.
|
|
@@ -102,6 +111,15 @@ export interface ReplayFromOptions {
|
|
|
102
111
|
* replayer had honoured invocations for an hour by then. Two replay paths; one was wired.
|
|
103
112
|
*/
|
|
104
113
|
resolveFlow?: (name: string) => Promise<FlowFile | undefined>;
|
|
114
|
+
/**
|
|
115
|
+
* Set when replay loaded the start page itself: the session time the load began.
|
|
116
|
+
*
|
|
117
|
+
* The load is then part of the journey. An error the page logged while it loaded lands before
|
|
118
|
+
* step 1 opens its window, so a "no console errors" on step 1 passed over it. Only console-absent
|
|
119
|
+
* clauses look back to here. A `net` or `text` clause that did so could be satisfied by the load
|
|
120
|
+
* and not by the step, which is a false pass.
|
|
121
|
+
*/
|
|
122
|
+
loadSince?: number;
|
|
105
123
|
/** The invocation chain that reached this flow, nearest caller first. Empty at the top. */
|
|
106
124
|
via?: readonly {
|
|
107
125
|
flow: string;
|