@reticlehq/server 3.7.0 → 3.8.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 +59 -68
- package/bin/reticle.js +9 -2
- package/dist/command/cli/adhoc-suite.js +1 -1
- package/dist/command/cli/adhoc-verdict.d.ts +12 -0
- package/dist/command/cli/adhoc-verdict.js +62 -4
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +5 -1
- package/dist/command/cli/auth/cloud-session.d.ts +11 -19
- package/dist/command/cli/auth/cloud-session.js +23 -0
- package/dist/command/cli/auth/device-flow.d.ts +3 -37
- package/dist/command/cli/cli-doctor.js +66 -21
- package/dist/command/cli/cli-flow-commands.js +53 -11
- package/dist/command/cli/cli-kill.d.ts +8 -0
- package/dist/command/cli/cli-kill.js +43 -3
- package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
- package/dist/command/cli/cli-parse-feedback.js +13 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
- package/dist/command/cli/cli-parse-grammar.js +1 -6
- package/dist/command/cli/cli-parse-serve.d.ts +24 -0
- package/dist/command/cli/cli-parse-serve.js +124 -0
- package/dist/command/cli/cli-parse.d.ts +12 -11
- package/dist/command/cli/cli-parse.js +100 -123
- package/dist/command/cli/cli-usage.d.ts +0 -11
- package/dist/command/cli/cli-usage.js +43 -16
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +29 -14
- package/dist/command/cli/cloud-cli.js +27 -9
- package/dist/command/cli/cloud-kit.js +3 -3
- package/dist/command/cli/cloud-link.d.ts +1 -20
- package/dist/command/cli/cloud-link.js +2 -0
- package/dist/command/cli/cloud-login.d.ts +6 -2
- package/dist/command/cli/cloud-login.js +13 -41
- package/dist/command/cli/config/checkout.d.ts +10 -0
- package/dist/command/cli/config/checkout.js +78 -0
- package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
- 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/drive/drive-attach.d.ts +5 -0
- package/dist/command/cli/drive/drive-attach.js +21 -1
- package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
- package/dist/command/cli/hook/codex-transcript.js +224 -0
- package/dist/command/cli/hook/episode-structure.d.ts +24 -0
- package/dist/command/cli/hook/episode-structure.js +138 -0
- package/dist/command/cli/hook/hook-command.d.ts +9 -0
- package/dist/command/cli/hook/hook-command.js +211 -0
- package/dist/command/cli/hook/hook-fast.d.ts +31 -0
- package/dist/command/cli/hook/hook-fast.js +136 -0
- package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
- package/dist/command/cli/hook/prompt-hook.js +199 -0
- package/dist/command/cli/hook/tool-hook.d.ts +21 -0
- package/dist/command/cli/hook/tool-hook.js +259 -0
- package/dist/command/cli/launch/cli-launch.d.ts +2 -0
- package/dist/command/cli/launch/cli-launch.js +22 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
- package/dist/command/cli/mcp-command.d.ts +9 -0
- package/dist/command/cli/mcp-command.js +127 -41
- package/dist/command/cli/open-navigate.d.ts +43 -0
- package/dist/command/cli/open-navigate.js +85 -0
- package/dist/command/cli/ports/port-holder.d.ts +16 -4
- package/dist/command/cli/ports/port-holder.js +58 -12
- package/dist/command/cli/ports/resolve/cli-port.js +18 -2
- package/dist/command/cli/project-name.d.ts +1 -7
- package/dist/command/cli/project-name.js +1 -0
- package/dist/command/cli/report-command.d.ts +8 -2
- package/dist/command/cli/report-command.js +95 -18
- package/dist/command/cli/status-fields.d.ts +9 -2
- package/dist/command/cli/status-fields.js +148 -4
- package/dist/command/cli/try-command.d.ts +3 -4
- package/dist/command/cli/try-command.js +6 -11
- package/dist/command/cli/verify-results.d.ts +4 -1
- package/dist/command/cli/verify-results.js +3 -1
- package/dist/command/cli.d.ts +1 -7
- package/dist/command/cli.js +79 -160
- package/dist/command/daemon/daemon-resilience.d.ts +6 -1
- package/dist/command/daemon/daemon-resilience.js +31 -18
- package/dist/command/daemon/daemon.d.ts +11 -0
- package/dist/command/daemon/daemon.js +70 -2
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
- package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
- package/dist/command/daemon/wake-decision.js +4 -2
- package/dist/command/daemon/win32-escape.d.ts +32 -0
- package/dist/command/daemon/win32-escape.js +119 -0
- package/dist/command/setup/agent-configs.d.ts +9 -0
- package/dist/command/setup/agent-configs.js +9 -0
- package/dist/command/setup/auto-approve.d.ts +10 -3
- package/dist/command/setup/auto-approve.js +51 -3
- package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
- package/dist/command/setup/bringup/dev-server-wait.js +5 -1
- package/dist/command/setup/bringup/ensure-daemon.js +4 -7
- package/dist/command/setup/init/init-host.js +6 -0
- package/dist/command/setup/init/init-port.js +5 -4
- package/dist/command/setup/init/init-runtime.js +7 -1
- package/dist/command/setup/node-effects.d.ts +8 -0
- package/dist/command/setup/node-effects.js +36 -4
- package/dist/command/setup/probe/dev-port.d.ts +26 -0
- package/dist/command/setup/probe/dev-port.js +117 -0
- package/dist/command/setup/probe/page-probe.d.ts +1 -1
- package/dist/command/setup/probe/page-probe.js +11 -3
- package/dist/command/setup/remaining-steps.d.ts +0 -7
- package/dist/command/setup/remaining-steps.js +3 -2
- package/dist/command/setup/run-setup.d.ts +11 -0
- package/dist/command/setup/run-setup.js +33 -5
- package/dist/command/setup/setup-command.d.ts +2 -0
- package/dist/command/setup/setup-command.js +22 -4
- package/dist/command/update/updater.d.ts +1 -6
- package/dist/command/update/updater.js +4 -5
- package/dist/command/update/version-seen.d.ts +24 -0
- package/dist/command/update/version-seen.js +65 -0
- package/dist/command/version/peer-announce.d.ts +9 -1
- package/dist/command/version/peer-announce.js +25 -2
- package/dist/command/version/sdk-fix.d.ts +5 -6
- package/dist/command/version/sdk-fix.js +17 -32
- package/dist/command/version/version-skew.d.ts +16 -1
- package/dist/command/version/version-skew.js +84 -10
- package/dist/features/crawl/crawl-tools.js +2 -2
- package/dist/features/crawl/crawl.js +2 -0
- package/dist/features/exhaust/ledger.d.ts +15 -96
- package/dist/features/exhaust/ledger.js +3 -3
- package/dist/features/harness/drive-runs.d.ts +28 -4
- package/dist/features/harness/drive-runs.js +99 -7
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/harness.d.ts +5 -0
- package/dist/features/harness/harness.js +24 -12
- package/dist/features/harness/platform/platform-drives.d.ts +7 -1
- package/dist/features/harness/platform/platform-drives.js +12 -7
- package/dist/features/harness/platform/refusal.d.ts +1 -1
- package/dist/features/harness/platform/refusal.js +4 -4
- package/dist/features/harness/platform/remote-drive.js +70 -28
- package/dist/features/harness/platform/server-driver.js +22 -1
- package/dist/features/harness/platform-config.d.ts +1 -10
- package/dist/features/harness/platform-config.js +0 -20
- package/dist/features/license/license.d.ts +2 -14
- package/dist/features/visual/step-shots.d.ts +33 -1
- package/dist/features/visual/step-shots.js +155 -1
- package/dist/features/visual/visual-tools.js +12 -4
- package/dist/hooks/coding-agents.d.ts +6 -0
- package/dist/hooks/coding-agents.js +24 -3
- package/dist/hooks/episode-stamp.d.ts +32 -0
- package/dist/hooks/episode-stamp.js +61 -0
- package/dist/hooks/hook-commands.js +19 -9
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -24
- package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
- package/dist/judgement/app-graph/journal-steps.js +92 -0
- package/dist/judgement/capsule/capsule-store.d.ts +2 -39
- package/dist/judgement/domain/domain-model.js +1 -10
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
- package/dist/judgement/runs/artifact/run-store.js +153 -28
- package/dist/judgement/runs/default-run-id.d.ts +3 -1
- package/dist/judgement/runs/default-run-id.js +15 -3
- package/dist/judgement/runs/drive-run.d.ts +3 -3
- package/dist/judgement/runs/drive-run.js +11 -7
- package/dist/judgement/runs/run-ledger.d.ts +21 -0
- package/dist/judgement/runs/run-ledger.js +143 -0
- package/dist/judgement/runs/runner-port.d.ts +1 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +4 -3
- package/dist/judgement/runs/verification-sync.js +27 -5
- package/dist/language/flows/affected-tools.js +2 -1
- package/dist/language/flows/annotate-notes/annotate.js +8 -3
- package/dist/language/flows/annotate-tools.js +1 -1
- package/dist/language/flows/change/affected.d.ts +8 -0
- package/dist/language/flows/change/affected.js +20 -7
- package/dist/language/flows/decision.js +19 -4
- package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
- package/dist/language/flows/fields/flow-secret-field.js +67 -5
- package/dist/language/flows/flow-anchor.d.ts +5 -0
- package/dist/language/flows/flow-anchor.js +4 -0
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +9 -0
- package/dist/language/flows/flow-expect-grammar.js +15 -10
- package/dist/language/flows/flow-intent.js +6 -1
- package/dist/language/flows/flow-journey.d.ts +8 -0
- package/dist/language/flows/flow-journey.js +13 -0
- package/dist/language/flows/flow-lease.d.ts +27 -0
- package/dist/language/flows/flow-lease.js +59 -0
- package/dist/language/flows/flow-replay-run.d.ts +1 -87
- package/dist/language/flows/flow-replay-run.js +111 -310
- package/dist/language/flows/flow-replay.js +21 -19
- package/dist/language/flows/flow-replayable.d.ts +38 -0
- package/dist/language/flows/flow-replayable.js +179 -0
- package/dist/language/flows/flow-start-path.d.ts +108 -0
- package/dist/language/flows/flow-start-path.js +313 -0
- package/dist/language/flows/flow-step-runners.d.ts +23 -2
- package/dist/language/flows/flow-step-runners.js +191 -32
- package/dist/language/flows/flow-store-for-session.d.ts +8 -1
- package/dist/language/flows/flow-store-for-session.js +6 -0
- package/dist/language/flows/flow-tools.d.ts +2 -23
- package/dist/language/flows/flow-tools.js +63 -113
- package/dist/language/flows/flows.d.ts +4 -6
- package/dist/language/flows/flows.js +90 -15
- package/dist/language/flows/heal-run.d.ts +10 -4
- package/dist/language/flows/heal-run.js +26 -9
- package/dist/language/flows/recording/flake.d.ts +3 -27
- package/dist/language/flows/recording/flake.js +1 -1
- package/dist/language/flows/recording/recording-backtrack.js +1 -4
- package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
- package/dist/language/flows/recording/tape/recordings.js +9 -3
- package/dist/language/flows/replay.d.ts +25 -5
- package/dist/language/flows/replay.js +83 -16
- package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
- package/dist/language/flows/suite/coverage.d.ts +9 -0
- package/dist/language/flows/suite/coverage.js +12 -1
- package/dist/language/flows/suite-selection.d.ts +25 -0
- package/dist/language/flows/suite-selection.js +41 -9
- package/dist/machine/git-tracks.d.ts +6 -0
- package/dist/machine/git-tracks.js +36 -0
- package/dist/memory/cloud/background-sync.d.ts +25 -0
- package/dist/memory/cloud/background-sync.js +79 -0
- package/dist/memory/cloud/cloud-config.d.ts +12 -1
- package/dist/memory/cloud/cloud-config.js +39 -13
- package/dist/memory/cloud/cloud-sync.d.ts +3 -24
- package/dist/memory/cloud/cloud-sync.js +5 -2
- package/dist/memory/cloud/harness-config.d.ts +0 -7
- package/dist/memory/cloud/memory-scope.d.ts +19 -0
- package/dist/memory/cloud/memory-scope.js +48 -0
- package/dist/memory/cloud/sync-answer.d.ts +43 -0
- package/dist/memory/cloud/sync-answer.js +42 -0
- package/dist/memory/cloud/sync-cycle.d.ts +40 -10
- package/dist/memory/cloud/sync-cycle.js +97 -144
- package/dist/memory/cloud/sync-daemon.d.ts +50 -3
- package/dist/memory/cloud/sync-daemon.js +473 -83
- package/dist/memory/cloud/sync-describe.d.ts +28 -0
- package/dist/memory/cloud/sync-describe.js +92 -0
- package/dist/memory/cloud/sync-disk.d.ts +9 -1
- package/dist/memory/cloud/sync-disk.js +137 -33
- package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
- package/dist/memory/cloud/sync-flow-versions.js +42 -0
- package/dist/memory/impact/impact-store.js +32 -15
- package/dist/memory/intent/intent-shard.d.ts +41 -266
- package/dist/memory/intent/intent-tools.js +20 -61
- package/dist/memory/intent/record-request.d.ts +27 -0
- package/dist/memory/intent/record-request.js +88 -0
- package/dist/memory/intent/settled-words.js +9 -1
- package/dist/memory/journal/drive-flow.d.ts +3 -0
- package/dist/memory/journal/drive-flow.js +11 -41
- package/dist/memory/journal/envelope-store.js +1 -1
- package/dist/memory/journal/envelope.d.ts +6 -154
- package/dist/memory/journal/episode-log.d.ts +107 -0
- package/dist/memory/journal/episode-log.js +302 -0
- package/dist/memory/journal/journal-recorder.d.ts +7 -0
- package/dist/memory/journal/journal-recorder.js +22 -9
- package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
- package/dist/memory/journal/on-disk/ambient-file.js +1 -1
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
- package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
- package/dist/memory/journal/self-observation.js +3 -1
- package/dist/memory/journal/session-end.js +23 -9
- package/dist/memory/journal/session-journal.js +39 -20
- package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
- package/dist/memory/project/artifact-root-resolver.js +68 -23
- package/dist/memory/project/artifact-root.d.ts +17 -3
- package/dist/memory/project/artifact-root.js +47 -9
- package/dist/memory/project/fs/fs-port.d.ts +5 -0
- package/dist/memory/project/fs/fs-port.js +13 -0
- package/dist/memory/project/learned-routes.d.ts +3 -4
- package/dist/memory/project/learned-routes.js +3 -11
- package/dist/memory/project/link-directory.js +5 -2
- package/dist/memory/project/root-claims.d.ts +3 -1
- package/dist/memory/project/root-claims.js +41 -7
- package/dist/memory/project/session-root.d.ts +1 -1
- package/dist/memory/project/session-root.js +1 -15
- package/dist/memory/project/sync-status.js +5 -6
- package/dist/memory/recall/memory-tools.js +2 -2
- package/dist/memory/recall/platform-memory.d.ts +3 -1
- package/dist/memory/recall/platform-memory.js +9 -1
- package/dist/memory/recall/project-memory.js +15 -19
- package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
- package/dist/portal/bridge/auth-failure-reason.js +19 -0
- package/dist/portal/bridge/bridge.d.ts +19 -4
- package/dist/portal/bridge/bridge.js +150 -177
- package/dist/portal/bridge/harness-request.d.ts +15 -0
- package/dist/portal/bridge/harness-request.js +19 -0
- package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
- package/dist/portal/bridge/origin/origin-follow.js +110 -0
- package/dist/portal/bridge/pairing-token.d.ts +6 -0
- package/dist/portal/bridge/pairing-token.js +47 -16
- package/dist/portal/bridge/protocol-skew.d.ts +27 -2
- package/dist/portal/bridge/protocol-skew.js +66 -2
- package/dist/portal/bridge/session-nonces.d.ts +46 -0
- package/dist/portal/bridge/session-nonces.js +72 -0
- package/dist/portal/input/viewport-tools.d.ts +31 -0
- package/dist/portal/input/viewport-tools.js +106 -15
- package/dist/portal/pool/bounded-close.d.ts +23 -0
- package/dist/portal/pool/bounded-close.js +38 -0
- package/dist/portal/pool/browser-pool.d.ts +44 -39
- package/dist/portal/pool/browser-pool.js +247 -95
- package/dist/portal/pool/context-permissions.d.ts +9 -1
- package/dist/portal/pool/context-permissions.js +26 -0
- package/dist/portal/pool/lease-emulation.d.ts +37 -0
- package/dist/portal/pool/lease-emulation.js +29 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +74 -7
- package/dist/portal/pool/pool-contract.d.ts +23 -1
- package/dist/portal/pool/pool-lease.d.ts +128 -0
- package/dist/portal/pool/pool-lease.js +49 -0
- package/dist/portal/pool/zero-install.d.ts +8 -1
- package/dist/portal/pool/zero-install.js +24 -2
- package/dist/portal/production/production-activation.d.ts +136 -0
- package/dist/portal/production/production-activation.js +173 -0
- package/dist/portal/production/production-drive.d.ts +52 -0
- package/dist/portal/production/production-drive.js +301 -0
- package/dist/portal/production/production-hello.d.ts +23 -0
- package/dist/portal/production/production-hello.js +23 -0
- package/dist/portal/realm/web-realm.js +42 -15
- package/dist/portal/session/control-envelope.d.ts +1 -1
- package/dist/portal/session/dev-server/served-document.d.ts +0 -5
- package/dist/portal/session/dev-server/served-document.js +6 -8
- package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
- package/dist/portal/session/first-run-wiring.d.ts +3 -36
- package/dist/portal/session/first-run-wiring.js +10 -103
- package/dist/portal/session/live-control-tools.js +61 -19
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +40 -15
- package/dist/portal/session/no-session-watch.d.ts +7 -0
- package/dist/portal/session/no-session-watch.js +67 -23
- package/dist/portal/session/output-budget.d.ts +10 -0
- package/dist/portal/session/output-budget.js +16 -0
- package/dist/portal/session/presence/session-lease.d.ts +2 -0
- package/dist/portal/session/presence/session-lease.js +8 -0
- package/dist/portal/session/recorded-gaps.d.ts +15 -11
- package/dist/portal/session/recorded-gaps.js +39 -18
- package/dist/portal/session/serving-directory.d.ts +24 -2
- package/dist/portal/session/serving-directory.js +93 -5
- package/dist/portal/session/session-info.d.ts +26 -0
- package/dist/portal/session/session-info.js +20 -0
- package/dist/portal/session/session-manager.d.ts +35 -3
- package/dist/portal/session/session-manager.js +69 -14
- package/dist/portal/session/session-successor.js +1 -9
- package/dist/portal/session/session-tools.js +22 -4
- package/dist/portal/session/session.d.ts +10 -10
- package/dist/portal/session/session.js +37 -29
- package/dist/portal/session/zero-install-first-call.d.ts +7 -2
- package/dist/portal/session/zero-install-first-call.js +43 -20
- package/dist/start-wiring.d.ts +2 -2
- package/dist/start-wiring.js +17 -3
- package/dist/status-payload.d.ts +11 -1
- package/dist/status-payload.js +7 -1
- package/dist/surface/agent-presence-grace.d.ts +11 -0
- package/dist/surface/agent-presence-grace.js +34 -0
- package/dist/surface/http-server.d.ts +12 -1
- package/dist/surface/http-server.js +64 -15
- package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
- package/dist/surface/mcp/faults/mcp-outage.js +19 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
- package/dist/surface/mcp/mcp-proxy.js +194 -267
- package/dist/surface/mcp/mcp.d.ts +20 -3
- package/dist/surface/mcp/mcp.js +88 -25
- package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
- package/dist/surface/mcp/proxy/drive-calls.js +114 -5
- package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
- package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
- package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
- package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
- package/dist/surface/mcp/server-instructions.js +35 -16
- package/dist/surface/mcp/shared-params.d.ts +8 -0
- package/dist/surface/mcp/shared-params.js +15 -1
- package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
- package/dist/surface/mcp/sse-frame-parser.js +18 -0
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +13 -0
- package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
- package/dist/surface/mcp/tool-catalog-cache.js +14 -4
- package/dist/surface/tools/account-tool.d.ts +4 -3
- package/dist/surface/tools/account-tool.js +20 -31
- package/dist/surface/tools/act/act-danger.d.ts +2 -0
- package/dist/surface/tools/act/act-danger.js +41 -14
- package/dist/surface/tools/act/act-observation.d.ts +11 -0
- package/dist/surface/tools/act/act-observation.js +33 -0
- package/dist/surface/tools/act/act-preflight.js +10 -3
- package/dist/surface/tools/act/act-sequence-retry.js +2 -1
- package/dist/surface/tools/act/act-target.js +19 -5
- package/dist/surface/tools/act/durable.js +1 -1
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
- package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
- package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
- package/dist/surface/tools/act/unsettled-find.js +71 -0
- package/dist/surface/tools/act-sequence-tool.js +53 -24
- package/dist/surface/tools/act-tools.d.ts +2 -3
- package/dist/surface/tools/act-tools.js +49 -47
- package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
- package/dist/surface/tools/agent/for-the-agent.js +41 -3
- package/dist/surface/tools/assert/assert-grade.js +8 -3
- package/dist/surface/tools/assert/assert-verdict.js +4 -2
- package/dist/surface/tools/browser-tools.js +7 -4
- package/dist/surface/tools/chat-drives.js +50 -25
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.d.ts +8 -0
- package/dist/surface/tools/dynamic-tools.js +89 -34
- package/dist/surface/tools/empty-session-list.d.ts +18 -25
- package/dist/surface/tools/empty-session-list.js +67 -38
- package/dist/surface/tools/error-recovery.d.ts +17 -7
- package/dist/surface/tools/error-recovery.js +53 -20
- package/dist/surface/tools/explore-tools.d.ts +10 -0
- package/dist/surface/tools/explore-tools.js +72 -19
- package/dist/surface/tools/feedback-tools.js +23 -2
- package/dist/surface/tools/gap-novelty.d.ts +6 -0
- package/dist/surface/tools/gap-novelty.js +9 -0
- package/dist/surface/tools/gaps/capability-absences.js +4 -3
- package/dist/surface/tools/harness-explore.d.ts +1 -1
- package/dist/surface/tools/harness-explore.js +6 -4
- package/dist/surface/tools/harness-gate.d.ts +43 -21
- package/dist/surface/tools/harness-gate.js +55 -81
- package/dist/surface/tools/harness-script.d.ts +2 -0
- package/dist/surface/tools/harness-script.js +11 -6
- package/dist/surface/tools/harness-toolset.js +3 -4
- package/dist/surface/tools/invoke-tool.d.ts +0 -8
- package/dist/surface/tools/invoke-tool.js +164 -178
- package/dist/surface/tools/lease-hint.d.ts +9 -0
- package/dist/surface/tools/lease-hint.js +22 -3
- package/dist/surface/tools/lease-permissions.d.ts +1 -1
- package/dist/surface/tools/lease-readiness.d.ts +55 -0
- package/dist/surface/tools/lease-readiness.js +150 -2
- package/dist/surface/tools/lease-tools.d.ts +3 -75
- package/dist/surface/tools/lease-tools.js +124 -188
- package/dist/surface/tools/map-tools.d.ts +20 -0
- package/dist/surface/tools/map-tools.js +145 -0
- package/dist/surface/tools/merge-tools.js +11 -7
- package/dist/surface/tools/next-step.js +6 -3
- package/dist/surface/tools/observe-tools.js +46 -35
- package/dist/surface/tools/platform-moment.d.ts +3 -12
- package/dist/surface/tools/platform-moment.js +12 -24
- package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
- package/dist/surface/tools/read/complete-snapshot.js +13 -2
- package/dist/surface/tools/read/query-paginate.js +6 -0
- package/dist/surface/tools/read/query-shape.d.ts +9 -0
- package/dist/surface/tools/read/query-shape.js +15 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
- package/dist/surface/tools/read-tools.js +5 -5
- package/dist/surface/tools/real-input-attempt.d.ts +11 -0
- package/dist/surface/tools/real-input-attempt.js +206 -38
- package/dist/surface/tools/reconcile-tools.js +21 -8
- package/dist/surface/tools/reticle-result.d.ts +3 -0
- package/dist/surface/tools/reticle-result.js +8 -1
- package/dist/surface/tools/run-report.d.ts +22 -0
- package/dist/surface/tools/run-report.js +141 -0
- package/dist/surface/tools/session-ports.d.ts +57 -0
- package/dist/surface/tools/session-ports.js +102 -0
- package/dist/surface/tools/tool-kit.d.ts +58 -4
- package/dist/surface/tools/tool-kit.js +140 -6
- package/dist/surface/tools/tool-surface.d.ts +2 -0
- package/dist/surface/tools/tool-surface.js +45 -0
- package/dist/surface/tools/tools.js +49 -87
- package/dist/telemetry/automation-hint.d.ts +5 -0
- package/dist/telemetry/automation-hint.js +7 -0
- package/dist/telemetry/cli-telemetry.d.ts +22 -0
- package/dist/telemetry/cli-telemetry.js +80 -11
- package/dist/telemetry/daemon-telemetry.d.ts +3 -1
- package/dist/telemetry/daemon-telemetry.js +9 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +8 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/feedback.js +51 -4
- package/dist/telemetry/harness-run.d.ts +3 -3
- package/dist/telemetry/harness-run.js +18 -7
- package/dist/telemetry/hud-events.d.ts +1 -1
- package/dist/telemetry/hud-metrics.d.ts +6 -3
- package/dist/telemetry/hud-metrics.js +20 -8
- package/dist/telemetry/install-source.js +1 -1
- package/dist/telemetry/mcp-connection.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +21 -2
- package/dist/telemetry/onboarding-firsts.d.ts +6 -1
- package/dist/telemetry/onboarding-firsts.js +41 -1
- package/dist/telemetry/proxy-telemetry.d.ts +42 -0
- package/dist/telemetry/proxy-telemetry.js +70 -1
- package/dist/telemetry/session-metrics.d.ts +1 -3
- package/dist/telemetry/session-metrics.js +16 -12
- package/dist/telemetry/telemetry.d.ts +9 -1
- package/dist/telemetry/telemetry.js +16 -1
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +7 -1
- package/dist/wire-journal.js +12 -2
- package/docs/README.md +4 -2
- package/docs/actions.mdx +7 -6
- package/docs/adapters.md +2 -1
- package/docs/agent-cheatsheet.md +19 -5
- package/docs/architecture.md +13 -10
- package/docs/autodrive.md +6 -5
- package/docs/benchmarks.md +2 -2
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +6 -6
- package/docs/cli/doctor.mdx +3 -1
- package/docs/cli/feedback.mdx +11 -7
- package/docs/cli/gate.mdx +1 -1
- package/docs/cli/help.mdx +1 -1
- package/docs/cli/init.mdx +20 -16
- package/docs/cli/mcp.mdx +5 -2
- package/docs/cli/open.mdx +5 -2
- package/docs/cli/report.mdx +1 -1
- package/docs/cli/restart.mdx +1 -1
- package/docs/cli/rollback.mdx +4 -4
- package/docs/cli/serve.mdx +7 -5
- package/docs/cli/setup.mdx +1 -1
- package/docs/cli/status.mdx +6 -2
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/telemetry.mdx +5 -1
- package/docs/cli/try.mdx +8 -8
- package/docs/cli/update.mdx +8 -5
- package/docs/cli/verify.mdx +12 -5
- package/docs/cli/version.mdx +1 -1
- package/docs/cli.mdx +10 -6
- package/docs/debugging.md +1 -1
- package/docs/deploy-checks.md +8 -6
- package/docs/desktop.mdx +14 -18
- package/docs/enterprise.md +6 -6
- package/docs/environment.mdx +5 -3
- package/docs/faq.mdx +9 -6
- package/docs/first-drive.md +19 -9
- package/docs/flows.md +3 -4
- package/docs/for-agents.md +1 -1
- package/docs/frameworks.mdx +34 -11
- package/docs/getting-started.md +46 -29
- package/docs/http-transport.md +2 -2
- package/docs/human-control.md +8 -8
- package/docs/index.mdx +2 -2
- package/docs/install-agentic.mdx +18 -19
- package/docs/install-manual.mdx +8 -4
- package/docs/integration-patterns.md +5 -5
- package/docs/multi-agent-testing.md +7 -5
- package/docs/packages/browser.mdx +34 -2
- package/docs/packages/core.mdx +2 -1
- package/docs/packages/electron.mdx +15 -3
- package/docs/packages/eslint-plugin.mdx +1 -1
- package/docs/packages/next.mdx +17 -5
- package/docs/packages/react.mdx +8 -1
- package/docs/packages/server.mdx +3 -3
- package/docs/packages/tauri.mdx +3 -3
- package/docs/packages/vite-plugin.mdx +1 -1
- package/docs/packages.mdx +19 -6
- package/docs/platform-integration.md +12 -4
- package/docs/predicates.mdx +18 -7
- package/docs/production-mode.md +108 -0
- package/docs/prompt-capture.md +99 -0
- package/docs/quickstart.mdx +14 -12
- package/docs/recipes.mdx +9 -8
- package/docs/required-checks.md +3 -2
- package/docs/skill-file.mdx +4 -3
- package/docs/skill-setup.md +2 -2
- package/docs/skill.md +62 -70
- package/docs/state-files.md +1 -0
- package/docs/state-management.mdx +11 -9
- package/docs/system-map.md +14 -13
- package/docs/telemetry.md +32 -24
- package/docs/testing.md +9 -5
- package/docs/tools/act-and-wait.mdx +17 -13
- package/docs/tools/act-sequence.mdx +40 -66
- package/docs/tools/act.mdx +7 -5
- package/docs/tools/assert.mdx +1 -1
- package/docs/tools/look.mdx +1 -1
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/network.mdx +13 -11
- package/docs/tools/overview.mdx +3 -3
- package/docs/tools/query.mdx +3 -3
- package/docs/tools/session-and-feedback.mdx +9 -6
- package/docs/tools/sessions.mdx +7 -0
- package/docs/tools/snapshot.mdx +9 -7
- package/docs/tools/tools-and-run.mdx +11 -10
- package/docs/tools/verify.mdx +1 -1
- package/docs/troubleshooting.mdx +21 -9
- package/docs/usage.md +58 -43
- package/docs/use-cases.mdx +2 -2
- package/docs/vs-chrome-devtools-mcp.mdx +5 -5
- package/docs/vs-playwright-mcp.mdx +5 -4
- package/docs/what-is-recorded.md +12 -7
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/dev/stale-issue-guard.d.ts +0 -34
- package/dist/command/dev/stale-issue-guard.js +0 -69
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { resolveFlowUploads } from './fields/flow-upload-resolve.js';
|
|
2
|
+
import { withLeaseKeyboard } from '../../surface/tools/real-input-attempt.js';
|
|
2
3
|
import { learnFromRun } from '@reticlehq/engine/evidence/learned-guards.js';
|
|
3
|
-
import { EventType, FLOW_SIGNAL_TIMEOUT_MS, FlowErrorCode, RecordedFlowSchema, ReplayStatus,
|
|
4
|
+
import { EventType, FLOW_SIGNAL_TIMEOUT_MS, FlowErrorCode, RecordedFlowSchema, ReplayStatus, RunKind, RunStatus, } from '@reticlehq/core';
|
|
4
5
|
import { haltedFrom } from './recording/replay-halt.js';
|
|
5
|
-
import { establishedState
|
|
6
|
-
import {
|
|
7
|
-
import { navigablePath, routeOfEvent, routeOfUrl, } from '@reticlehq/engine/question/predicate/predicate-route.js';
|
|
8
|
-
import { carryReticleIdentity } from '../../surface/tools/lease-tools.js';
|
|
6
|
+
import { establishedState } from './flow-journey.js';
|
|
7
|
+
import { asString } from '@reticlehq/core';
|
|
9
8
|
import { partialStatus, replayFlow, replayWindow } from './flow-replay.js';
|
|
10
|
-
import {
|
|
11
|
-
import { queryRefs } from './replay.js';
|
|
9
|
+
import { staleTargetResult } from './flow-step-runners.js';
|
|
12
10
|
import { assertSuccess, dynamicTestids, successLabel, SUCCESS_STEP_TOOL } from './flow-success.js';
|
|
13
11
|
import { buildDecision, unverifiableReason } from './decision.js';
|
|
14
12
|
import { unsuppliedSecrets } from './fields/flow-secret-field.js';
|
|
@@ -20,7 +18,7 @@ import { IntentStore } from '../../memory/intent/intent-store.js';
|
|
|
20
18
|
import { safeSessionTarget, sessionRoot, } from '../../memory/project/session-root.js';
|
|
21
19
|
import { waitForPredicate } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
22
20
|
import { computeSegments } from '../../memory/journal/rollups.js';
|
|
23
|
-
import { stepEffect } from '@reticlehq/engine/evidence/step-effect.js';
|
|
21
|
+
import { appScope, stepEffect } from '@reticlehq/engine/evidence/step-effect.js';
|
|
24
22
|
import { AssertionTiersStore } from './stores/assertion-tiers-store.js';
|
|
25
23
|
import { toFlowSources } from './change/flow-sources.js';
|
|
26
24
|
import { reportAndAccumulate } from '../../memory/journal/deviation-service.js';
|
|
@@ -28,21 +26,24 @@ import { EnvelopeStore } from '../../memory/journal/envelope-store.js';
|
|
|
28
26
|
import { homedir } from 'node:os';
|
|
29
27
|
import { cloudFetch, syncRunRecordToCloud, SyncOutcome } from '../../memory/cloud/cloud-sync.js';
|
|
30
28
|
import { resolveProjectCloud } from '../../memory/cloud/cloud-config.js';
|
|
31
|
-
import { memoryReadHeaders,
|
|
29
|
+
import { memoryReadHeaders, readMemoryPages, scopedMemoryEntries, } from '../../memory/cloud/memory-scope.js';
|
|
32
30
|
import { consultSubjectFor, selectConsulted } from './flow-memory-consult.js';
|
|
33
31
|
import { log } from '../../log.js';
|
|
34
32
|
import { replayStepShooter } from '../../features/visual/step-shots.js';
|
|
35
33
|
import { flowsForSession } from './flow-store-for-session.js';
|
|
36
34
|
import { projectForRoot } from '../../memory/project/project-for-root.js';
|
|
37
|
-
import { CROSS_STEP_INDEX, crossStepOnly } from './flow-cross-step.js';
|
|
35
|
+
import { CROSS_STEP_INDEX, crossStepOnly, declaredIn } from './flow-cross-step.js';
|
|
36
|
+
import { arriveAtStartPath, startPathMismatchHint, withStartPageWait } from './flow-start-path.js';
|
|
38
37
|
export function latestRecordedFlow(events) {
|
|
39
38
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
40
39
|
const event = events[i];
|
|
41
|
-
if (event?.type !== EventType.FLOW_RECORDED)
|
|
40
|
+
if (event?.type !== EventType.FLOW_RECORDED) {
|
|
42
41
|
continue;
|
|
42
|
+
}
|
|
43
43
|
const parsed = RecordedFlowSchema.safeParse(event.data);
|
|
44
|
-
if (parsed.success)
|
|
44
|
+
if (parsed.success) {
|
|
45
45
|
return { name: parsed.data.name, flow: parsed.data.flow };
|
|
46
|
+
}
|
|
46
47
|
}
|
|
47
48
|
return undefined;
|
|
48
49
|
}
|
|
@@ -80,43 +81,14 @@ recordRoot) {
|
|
|
80
81
|
// it was worse here: a replay outcome that should have reached the dashboard silently did not,
|
|
81
82
|
// because the daemon's own directory has no link file.
|
|
82
83
|
const cloud = await resolveProjectCloud(deps.fs, recordRoot, homedir(), process.env);
|
|
83
|
-
if (null === cloud.config || !cloud.policy.memory)
|
|
84
|
-
return;
|
|
84
|
+
if (null === cloud.config || !cloud.policy.memory) {
|
|
85
|
+
return;
|
|
86
|
+
} // not attached / memory disabled → local only
|
|
85
87
|
const result = await syncRunRecordToCloud({ kind: RunKind.FLOW_REPLAY, name, status: runStatus, at: deps.now(), durationMs }, projectId, cloud.config, cloudFetch);
|
|
86
88
|
if (result.outcome !== SyncOutcome.SYNCED) {
|
|
87
89
|
log('cloud-run-record-sync-failed', { flow: name, status: result.status, error: result.error });
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
|
-
/**
|
|
91
|
-
* The route the tab is on now: the last observed route.change, falling back to the pathname of the
|
|
92
|
-
* session's own URL. The fallback matters — a tab that hard-loaded its page emits no route event
|
|
93
|
-
* (the route observer only sees pushState/replaceState/popstate), but the HELLO url still names
|
|
94
|
-
* where it sits, and without it a wrong-page replay reported plain drift with no hint at all.
|
|
95
|
-
*/
|
|
96
|
-
function currentPathOf(session) {
|
|
97
|
-
const routes = session.eventsSince(0).filter((e) => e.type === EventType.ROUTE_CHANGE);
|
|
98
|
-
const last = routes.at(-1);
|
|
99
|
-
// pathname + search + hash, because `startPath` is compared against this and must stay NAVIGABLE.
|
|
100
|
-
//
|
|
101
|
-
// Reading the pathname alone made both sides `/` on a hash router — always "same path", so the
|
|
102
|
-
// hint never fired however far the tab had drifted, on the router desktop renderers use by
|
|
103
|
-
// default. Leaving the SEARCH out was the mirror defect: `startPath` keeps its query, so a flow
|
|
104
|
-
// starting at `/admin/events/7?tab=wrap` never matched a tab sitting on exactly that, re-navigated
|
|
105
|
-
// on every replay, and the re-navigation killed the session mid-flow.
|
|
106
|
-
//
|
|
107
|
-
// Comparing without the query instead would have been worse in the direction that matters. The
|
|
108
|
-
// query usually decides what the page renders, so a tab on `?tab=summary` would read as "already
|
|
109
|
-
// at `?tab=wrap`" and the replay would start on the wrong page with nothing saying so. Both sides
|
|
110
|
-
// carry it, which keeps a real difference visible and stops the false one.
|
|
111
|
-
// Reticle's own params dropped, as the recording drops them from `startPath` (#1411).
|
|
112
|
-
const observed = last === undefined ? undefined : routeOfEvent(last);
|
|
113
|
-
if (observed !== undefined)
|
|
114
|
-
return navigablePath(observed);
|
|
115
|
-
if (session.url === undefined)
|
|
116
|
-
return undefined;
|
|
117
|
-
const fromUrl = routeOfUrl(session.url);
|
|
118
|
-
return fromUrl === undefined ? undefined : navigablePath(fromUrl);
|
|
119
|
-
}
|
|
120
92
|
/**
|
|
121
93
|
* Ask the project what it already knows about this flow.
|
|
122
94
|
*
|
|
@@ -131,36 +103,36 @@ function currentPathOf(session) {
|
|
|
131
103
|
*/
|
|
132
104
|
async function consultProjectMemory(deps, flow, root) {
|
|
133
105
|
const subject = consultSubjectFor(flow);
|
|
134
|
-
if (subject === undefined)
|
|
106
|
+
if (subject === undefined) {
|
|
135
107
|
return undefined;
|
|
108
|
+
}
|
|
136
109
|
try {
|
|
137
110
|
// The APP's root, not the daemon's (wherever it was launched): its link file is this project's.
|
|
138
111
|
const cloud = await resolveProjectCloud(deps.fs, root, homedir(), process.env);
|
|
139
|
-
if (null === cloud.config || !cloud.policy.memory)
|
|
112
|
+
if (null === cloud.config || !cloud.policy.memory) {
|
|
140
113
|
return undefined;
|
|
114
|
+
}
|
|
141
115
|
// Scoped to the linked project. The key covers a whole workspace, so a read that named no
|
|
142
116
|
// project could be answered with ANOTHER repo's knowledge and this replay would carry it into a
|
|
143
117
|
// verdict as if it were this app's — see `memory-scope.ts` for the whole shape.
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
118
|
+
const config = cloud.config;
|
|
119
|
+
const body = await readMemoryPages(config.url, { projectId: cloud.projectId, subject }, async (url) => {
|
|
120
|
+
const res = await cloudFetch(url, {
|
|
121
|
+
method: 'GET',
|
|
122
|
+
headers: memoryReadHeaders(config.apiKey),
|
|
123
|
+
});
|
|
124
|
+
// `res.json` is a METHOD: read as a property it silently made "the project knows nothing".
|
|
125
|
+
return 200 === res.status ? await res.json() : undefined;
|
|
148
126
|
});
|
|
149
|
-
if (
|
|
127
|
+
if (body === undefined) {
|
|
150
128
|
return undefined;
|
|
151
|
-
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
// empty case and is why this took a live drive to notice at all.
|
|
155
|
-
const body = await res.json();
|
|
156
|
-
// The envelope is the check that works: the platform names the project on the RESPONSE and not
|
|
157
|
-
// on each entry, so the entry-level filter alone kept a whole sibling workspace while looking
|
|
158
|
-
// like it did something. `undefined` means the response could not be shown to be this project's
|
|
159
|
-
// — nothing is attached, because a verdict carrying another repo's "established knowledge" is
|
|
160
|
-
// worse than one carrying none. See `memory-scope.ts`.
|
|
129
|
+
}
|
|
130
|
+
// `undefined`: the response could not be shown to be this project's, so nothing is attached —
|
|
131
|
+
// a verdict carrying another repo's "established knowledge" is worse than none. See memory-scope.
|
|
161
132
|
const entries = scopedMemoryEntries(body, cloud.projectId);
|
|
162
|
-
if (entries === undefined)
|
|
133
|
+
if (entries === undefined) {
|
|
163
134
|
return undefined;
|
|
135
|
+
}
|
|
164
136
|
const picked = selectConsulted(entries);
|
|
165
137
|
return 0 === picked.length ? undefined : picked;
|
|
166
138
|
}
|
|
@@ -169,211 +141,6 @@ async function consultProjectMemory(deps, flow, root) {
|
|
|
169
141
|
return undefined;
|
|
170
142
|
}
|
|
171
143
|
}
|
|
172
|
-
/**
|
|
173
|
-
* When a flow records the page its journey started on (`startPath`) and the tab is currently on a
|
|
174
|
-
* different route, step 1 drifts for a reason that has nothing to do with the app regressing — the
|
|
175
|
-
* anchor simply isn't on this page yet. Detect that so the decision says "navigate there first"
|
|
176
|
-
* instead of a mystifying "a step no longer matches". Returns undefined when the routes agree or the
|
|
177
|
-
* current route is unobservable — never a false alarm. Replay normally never gets here on a
|
|
178
|
-
* wrong page (arriveAtStartPath navigates before step 1); this is the fallback when navigation
|
|
179
|
-
* was refused. A page that never reconnected is reported as unverifiable before step 1.
|
|
180
|
-
*/
|
|
181
|
-
export function startPathMismatchHint(flow, session) {
|
|
182
|
-
const startPath = flow.startPath;
|
|
183
|
-
if (startPath === undefined || 0 === startPath.length)
|
|
184
|
-
return undefined;
|
|
185
|
-
const current = currentPathOf(session);
|
|
186
|
-
if (current === undefined || samePath(current, startPath))
|
|
187
|
-
return undefined;
|
|
188
|
-
return `this flow's journey starts on ${startPath} but the tab is on ${current} — navigate there (reticle_navigate { url: "${startPath}" }), then replay`;
|
|
189
|
-
}
|
|
190
|
-
/** How long replay waits for the SDK to reconnect on the start page before falling back to the hint. */
|
|
191
|
-
const START_PATH_ARRIVAL_TIMEOUT_MS = 5_000;
|
|
192
|
-
const START_PATH_POLL_MS = 100;
|
|
193
|
-
const REAL_ARRIVAL_CLOCK = {
|
|
194
|
-
now: () => Date.now(),
|
|
195
|
-
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
196
|
-
};
|
|
197
|
-
/**
|
|
198
|
-
* The session `oldId` denotes right now — itself, or the tombstone successor `resolve` rebinds to
|
|
199
|
-
* after a full-document navigation. A different instance proves the SDK reconnected even when the
|
|
200
|
-
* app redirected to another route. Keyed to the navigated tab's own identity on purpose: matching
|
|
201
|
-
* "any session at the target page" could hand replay an unrelated tab already sitting there.
|
|
202
|
-
*/
|
|
203
|
-
function arrivedSuccessor(sessions, oldId, target,
|
|
204
|
-
/** The handle that issued the navigation. */
|
|
205
|
-
issuedBy, isReload) {
|
|
206
|
-
try {
|
|
207
|
-
const candidate = sessions.resolve(oldId);
|
|
208
|
-
// A reload can return the dying handle on the same path. Compare instances, not ids: a leased
|
|
209
|
-
// tab can reconnect under the same id. A redirect counts only on a new instance.
|
|
210
|
-
if (isReload && candidate === issuedBy)
|
|
211
|
-
return undefined;
|
|
212
|
-
const path = currentPathOf(candidate);
|
|
213
|
-
if (path === undefined)
|
|
214
|
-
return undefined;
|
|
215
|
-
const matches = samePath(path, target);
|
|
216
|
-
// A still-connected old tab on the wrong route is not a redirected successor.
|
|
217
|
-
return matches || candidate !== issuedBy ? { session: candidate, matches } : undefined;
|
|
218
|
-
}
|
|
219
|
-
catch {
|
|
220
|
-
return undefined;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Can step 1 start from where the tab already is?
|
|
225
|
-
*
|
|
226
|
-
* A route mismatch is NOT on its own a reason to navigate. The condition worth a page load is that
|
|
227
|
-
* the first anchor is not reachable from here — and a flow whose first anchor lives somewhere
|
|
228
|
-
* persistent (a sidebar, a header, a nav rail) is reachable from every route.
|
|
229
|
-
*
|
|
230
|
-
* Navigating anyway is expensive in the one direction that matters: a full page load tears the
|
|
231
|
-
* session down and brings the app back COLD, which for a flow recorded after signing in is the
|
|
232
|
-
* login screen. Step 1 then reports its anchor missing and blames a file that is completely fine.
|
|
233
|
-
* A best-effort step that can leave the caller WORSE off than skipping it is not best-effort.
|
|
234
|
-
*
|
|
235
|
-
* Unresolvable either way (no anchor we can query, a failed query) reads as "cannot tell", and the
|
|
236
|
-
* navigation goes ahead — the pre-existing behaviour, and the safe direction for a check whose whole
|
|
237
|
-
* job is to avoid making things worse.
|
|
238
|
-
*/
|
|
239
|
-
async function firstStepResolvesHere(session, flow) {
|
|
240
|
-
const first = flow.steps?.[0];
|
|
241
|
-
if (first === undefined)
|
|
242
|
-
return false;
|
|
243
|
-
const args = anchorQueryArgs(first.anchor);
|
|
244
|
-
if (null === args)
|
|
245
|
-
return false;
|
|
246
|
-
try {
|
|
247
|
-
return 0 < queryRefs(await session.command(ReticleCommand.QUERY, args)).length;
|
|
248
|
-
}
|
|
249
|
-
catch {
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Replay's half of the FlowFile contract: put the tab on the flow's `startPath` before step 1, and
|
|
255
|
-
* RELOAD it if it is already there.
|
|
256
|
-
*
|
|
257
|
-
* The reload is the state contract. Replaying "add a task" twice against a tab nobody reset gives
|
|
258
|
-
* the second run a world the recording never saw, and its assertions then describe the first run
|
|
259
|
-
* rather than the app. Our own `replay-determinism.mjs` has always refreshed before every run — the
|
|
260
|
-
* determinism we measured was never the determinism a user got.
|
|
261
|
-
*
|
|
262
|
-
* `requires` is the declared opt-out, and its first caller. A flow that says it starts from state
|
|
263
|
-
* another flow established is the one flow a page load would destroy, so it is never reset — here
|
|
264
|
-
* or from the wrong page. Silence is not an opt-out: a flow that depends on leftovers without
|
|
265
|
-
* saying so is a flow whose green means nothing on a fresh machine, and CI is a fresh machine.
|
|
266
|
-
*
|
|
267
|
-
* A full-page load tears down the session socket, so the navigation must happen here — before any
|
|
268
|
-
* step runs — and the replay continues on the session the SDK reconnects as (found via the same
|
|
269
|
-
* tombstone rebind that lets `resolve(oldId)` answer after any navigation). Best-effort by design:
|
|
270
|
-
* when the flow carries no startPath, the current route is unobservable, or navigation is refused,
|
|
271
|
-
* replay proceeds on the connected session. A missing successor stops before step 1; a redirect
|
|
272
|
-
* continues on the live successor with a route-specific hint.
|
|
273
|
-
*/
|
|
274
|
-
export async function arriveAtStartPath(sessions, session, flow, timeoutMs = START_PATH_ARRIVAL_TIMEOUT_MS, clock = REAL_ARRIVAL_CLOCK) {
|
|
275
|
-
const target = flow.startPath;
|
|
276
|
-
if (target === undefined || 0 === target.length)
|
|
277
|
-
return {};
|
|
278
|
-
const current = currentPathOf(session);
|
|
279
|
-
if (current === undefined)
|
|
280
|
-
return {};
|
|
281
|
-
// The declared opt-out, both directions. See above.
|
|
282
|
-
if (0 < establishedState(flow).length)
|
|
283
|
-
return {};
|
|
284
|
-
const here = samePath(current, target);
|
|
285
|
-
// A route mismatch that step 1 can start from anyway is not worth a page load: navigating away
|
|
286
|
-
// from a persistent anchor could only hurt, and did. Arriving is the goal; resetting a tab that is
|
|
287
|
-
// ALREADY here is a different decision, taken above, and this must not override it.
|
|
288
|
-
if (!here && (await firstStepResolvesHere(session, flow)))
|
|
289
|
-
return {};
|
|
290
|
-
let destination;
|
|
291
|
-
try {
|
|
292
|
-
// startPath is a pathname (a host belongs to the machine, not the journey) — resolve it
|
|
293
|
-
// against the tab's own URL to get something the browser can be sent to. Already HERE, the
|
|
294
|
-
// reset reloads the page the tab is on: `samePath` ignored a query the flow did not record, and
|
|
295
|
-
// navigating to `target` would strip it, so the replay would test a different page than the one
|
|
296
|
-
// it just agreed it was on.
|
|
297
|
-
destination = new URL(here ? current : target, session.url).toString();
|
|
298
|
-
}
|
|
299
|
-
catch {
|
|
300
|
-
return {}; // no usable base URL — nowhere to navigate from
|
|
301
|
-
}
|
|
302
|
-
// Measured either side of the reset, and only when the reset is a reload: reachable before and
|
|
303
|
-
// gone after is the reload's doing, not the flow file's. Without this, an app holding its session
|
|
304
|
-
// in memory comes back signed out and step 1 blames a component that is completely fine.
|
|
305
|
-
const resolvedBefore = here && (await firstStepResolvesHere(session, flow));
|
|
306
|
-
const outcome = await navigateAndAwait(sessions, session, destination, target, timeoutMs, clock, here);
|
|
307
|
-
if ('lost' === outcome.kind) {
|
|
308
|
-
return {
|
|
309
|
-
lost: `replay ${here ? 'reloaded' : 'navigated to'} ${target} before step 1 and no page was connected ` +
|
|
310
|
-
`after ${String(timeoutMs)}ms, so the session it was driving is gone and nothing ran. A slow ` +
|
|
311
|
-
`or backgrounded tab can take longer to come back: bring it to the front and replay`,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
if ('refused' === outcome.kind)
|
|
315
|
-
return {};
|
|
316
|
-
if ('redirected' === outcome.kind) {
|
|
317
|
-
const path = currentPathOf(outcome.session) ?? 'another route';
|
|
318
|
-
return {
|
|
319
|
-
session: outcome.session,
|
|
320
|
-
redirected: `replay ${here ? 'reloaded' : 'navigated to'} ${target} before step 1, but the tab reconnected on ${path}. Check the redirect or sign in before replaying`,
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
const arrived = outcome.session;
|
|
324
|
-
if (!resolvedBefore || (await firstStepResolvesHere(arrived, flow)))
|
|
325
|
-
return { session: arrived };
|
|
326
|
-
return {
|
|
327
|
-
session: arrived,
|
|
328
|
-
resetCost: resetCostHint(flow, target),
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Why step 1 failed after the reset, and the one line that opts the flow out.
|
|
333
|
-
*
|
|
334
|
-
* No tool writes `requires`, so naming the field was advice nobody could act on without guessing its
|
|
335
|
-
* shape. The first anchor IS the precondition: the element step 1 needs, present before it runs.
|
|
336
|
-
*/
|
|
337
|
-
function resetCostHint(flow, target) {
|
|
338
|
-
const first = flow.steps?.[0];
|
|
339
|
-
const needs = first === undefined ? undefined : anchorPrecondition(first.anchor);
|
|
340
|
-
const optOut = needs === undefined
|
|
341
|
-
? 'Declare what it needs (`requires`) in the flow file to opt out of the reset'
|
|
342
|
-
: `Add \`"requires":${JSON.stringify([needs])}\` to the flow file to opt out of the reset`;
|
|
343
|
-
return (`replay reloaded ${target} before step 1 and the first anchor did not come back — this flow ` +
|
|
344
|
-
`starts from state a page load discards. ${optOut}, or record it from a cold page`);
|
|
345
|
-
}
|
|
346
|
-
export async function navigateAndAwait(sessions, session, destination, expectedPath, timeoutMs = START_PATH_ARRIVAL_TIMEOUT_MS, clock = REAL_ARRIVAL_CLOCK,
|
|
347
|
-
/** True when the destination is the page we are already on — see `arrivedSuccessor`. */
|
|
348
|
-
isReload = false) {
|
|
349
|
-
// A leased tab is addressed by URL params, so navigating without them would strand the lease.
|
|
350
|
-
const url = carryReticleIdentity(session.url, destination);
|
|
351
|
-
try {
|
|
352
|
-
const outcome = await session.command(ReticleCommand.NAVIGATE, { url });
|
|
353
|
-
if (!outcome.ok || true !== asRecord(outcome.result)['ok'])
|
|
354
|
-
return { kind: 'refused' };
|
|
355
|
-
}
|
|
356
|
-
catch (error) {
|
|
357
|
-
// A navigation that unloads the page rejects the very command that asked for it — the transport
|
|
358
|
-
// dies with the document. Reading that as "the navigate failed" skipped the arrival poll below
|
|
359
|
-
// and left replay driving a handle that could never answer again, which is how a flow with a
|
|
360
|
-
// startPath died at step 1 on an app that had loaded perfectly. Anything else is a real refusal.
|
|
361
|
-
if (!isDocumentGoneError(error))
|
|
362
|
-
return { kind: 'refused' };
|
|
363
|
-
}
|
|
364
|
-
const deadline = clock.now() + timeoutMs;
|
|
365
|
-
for (;;) {
|
|
366
|
-
const arrived = arrivedSuccessor(sessions, session.id, expectedPath, session, isReload);
|
|
367
|
-
if (true === arrived?.matches)
|
|
368
|
-
return { kind: 'arrived', session: arrived.session };
|
|
369
|
-
// A live redirect is a different result from no SDK reconnection at all.
|
|
370
|
-
if (clock.now() >= deadline)
|
|
371
|
-
return arrived === undefined
|
|
372
|
-
? { kind: 'lost' }
|
|
373
|
-
: { kind: 'redirected', session: arrived.session };
|
|
374
|
-
await clock.sleep(START_PATH_POLL_MS);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
144
|
/**
|
|
378
145
|
* Replay one named flow end to end: load → re-resolve+run each step → assert the success oracle →
|
|
379
146
|
* status + decision. Shared by reticle_flow_replay (single flow) and reticle_flow_verify (whole suite) so
|
|
@@ -393,16 +160,19 @@ async function loadInvokedFlows(deps, flow, target) {
|
|
|
393
160
|
const seen = new Set([flow.name]);
|
|
394
161
|
while (queue.length > 0) {
|
|
395
162
|
const current = queue.shift();
|
|
396
|
-
if (current === undefined)
|
|
163
|
+
if (current === undefined) {
|
|
397
164
|
continue;
|
|
165
|
+
}
|
|
398
166
|
for (const step of flattenSteps(current.steps)) {
|
|
399
167
|
const name = step.invoke;
|
|
400
|
-
if (name === undefined || seen.has(name))
|
|
168
|
+
if (name === undefined || seen.has(name)) {
|
|
401
169
|
continue;
|
|
170
|
+
}
|
|
402
171
|
seen.add(name);
|
|
403
172
|
const sub = await flowsForSession(deps, target).flows.load(name, target.projectId);
|
|
404
|
-
if (!sub.ok)
|
|
173
|
+
if (!sub.ok) {
|
|
405
174
|
continue;
|
|
175
|
+
}
|
|
406
176
|
out.set(name, sub.value);
|
|
407
177
|
queue.push(sub.value);
|
|
408
178
|
}
|
|
@@ -429,8 +199,9 @@ async function firstUnmetPrecondition(session, flow, since) {
|
|
|
429
199
|
drift = await assertStepExpect(session, claim, waitForPredicate, 0, since);
|
|
430
200
|
}
|
|
431
201
|
catch (error) {
|
|
432
|
-
if (!isDocumentGoneError(error))
|
|
202
|
+
if (!isDocumentGoneError(error)) {
|
|
433
203
|
throw error;
|
|
204
|
+
}
|
|
434
205
|
return `the page went away while this flow's preconditions were being checked, so nothing ran and nothing was proved. Replay again once the page is back.`;
|
|
435
206
|
}
|
|
436
207
|
if (drift !== undefined) {
|
|
@@ -509,16 +280,21 @@ observe = (s) => s) {
|
|
|
509
280
|
// connected session — and the hint below turns the wrong-page drift into an actionable next move.
|
|
510
281
|
// Continuing at a step means the page is already there; going to the start would undo it.
|
|
511
282
|
const { to, at: continueAt } = replayWindow(args);
|
|
512
|
-
const
|
|
283
|
+
const pool = deps.pool;
|
|
284
|
+
// Only a pool lease can say its page is still loading; any other tab keeps the step budget.
|
|
285
|
+
const stillLoading = pool === undefined ? undefined : () => pool.leaseLoading(connected.id);
|
|
286
|
+
const arrival = continueAt === undefined
|
|
287
|
+
? await arriveAtStartPath(deps.sessions, connected, loaded.value, undefined, undefined, stillLoading)
|
|
288
|
+
: {};
|
|
513
289
|
// Continuing on `connected` after the tab left timed every step out on a dead handle (#1129).
|
|
514
290
|
if (arrival.lost !== undefined) {
|
|
515
291
|
const reason = arrival.lost;
|
|
516
|
-
return {
|
|
292
|
+
return withStartPageWait({
|
|
517
293
|
name: loaded.value.name,
|
|
518
294
|
status: ReplayStatus.UNVERIFIABLE,
|
|
519
295
|
steps: [],
|
|
520
296
|
unverifiable: { reason },
|
|
521
|
-
};
|
|
297
|
+
}, arrival);
|
|
522
298
|
}
|
|
523
299
|
const session = arrival.session ?? connected;
|
|
524
300
|
// The reset's own cost outranks the wrong-page hint: if the reload is why step 1 cannot start,
|
|
@@ -527,9 +303,9 @@ observe = (s) => s) {
|
|
|
527
303
|
// Floor the success oracle at the start of THIS replay so a stale signal from a prior run
|
|
528
304
|
// in the same session can't fake a pass.
|
|
529
305
|
const replayFloor = session.elapsed();
|
|
530
|
-
//
|
|
531
|
-
|
|
532
|
-
const replayable = await resolveFlowUploads(
|
|
306
|
+
// An upload path becomes bytes once, before step 1, against the app's project (#1769).
|
|
307
|
+
const uploadDeps = { fs: deps.fs, reticleRoot: replayRoot };
|
|
308
|
+
const replayable = await resolveFlowUploads(uploadDeps, loaded.value);
|
|
533
309
|
// Loaded once and used for BOTH the replay and the grading below. A composite asserts through
|
|
534
310
|
// what it runs, and a grader that cannot see the sub-flows reports `unverifiable` on a journey
|
|
535
311
|
// that checks itself thoroughly — right about the file, wrong about the journey.
|
|
@@ -567,7 +343,7 @@ observe = (s) => s) {
|
|
|
567
343
|
const missing = unsuppliedSecrets(replayable, process.env);
|
|
568
344
|
if (missing.length > 0) {
|
|
569
345
|
const names = missing.map((each) => each.envKey).join(', ');
|
|
570
|
-
return {
|
|
346
|
+
return withStartPageWait({
|
|
571
347
|
name: replayable.name,
|
|
572
348
|
status: ReplayStatus.UNVERIFIABLE,
|
|
573
349
|
steps: [],
|
|
@@ -577,31 +353,31 @@ observe = (s) => s) {
|
|
|
577
353
|
`the value typed would be the placeholder itself, and any failure after it would be ` +
|
|
578
354
|
`about this and not about the application.`,
|
|
579
355
|
},
|
|
580
|
-
};
|
|
356
|
+
}, arrival);
|
|
581
357
|
}
|
|
582
358
|
const from = resumeArg(args['from']);
|
|
583
359
|
const badResume = unresolvedResume(from, replayable.steps);
|
|
584
360
|
if (badResume !== undefined) {
|
|
585
361
|
const error = { code: FlowErrorCode.STEP_NOT_FOUND, message: badResume };
|
|
586
|
-
return { name: replayable.name, status: ReplayStatus.ERROR, steps: [], error };
|
|
362
|
+
return withStartPageWait({ name: replayable.name, status: ReplayStatus.ERROR, steps: [], error }, arrival);
|
|
587
363
|
}
|
|
588
364
|
const unmet = await firstUnmetPrecondition(session, replayable, replayFloor);
|
|
589
365
|
if (unmet !== undefined) {
|
|
590
|
-
return {
|
|
366
|
+
return withStartPageWait({
|
|
591
367
|
name: replayable.name,
|
|
592
368
|
status: ReplayStatus.UNVERIFIABLE,
|
|
593
369
|
steps: [],
|
|
594
370
|
unverifiable: { reason: unmet },
|
|
595
|
-
};
|
|
371
|
+
}, arrival);
|
|
596
372
|
}
|
|
597
373
|
let steps;
|
|
598
374
|
try {
|
|
599
|
-
steps = await replayFlow(observe(session), replayable, waitForPredicate, FLOW_SIGNAL_TIMEOUT_MS, true === args['confirmDangerous'], undefined, {
|
|
375
|
+
steps = await replayFlow(observe(withLeaseKeyboard(deps, session)), replayable, waitForPredicate, FLOW_SIGNAL_TIMEOUT_MS, true === args['confirmDangerous'], undefined, {
|
|
600
376
|
// How an `invoke` step finds the flow it runs. Scoped to the same project as the flow being
|
|
601
377
|
// replayed, so a composite cannot reach into another app's store for a same-named sub-journey.
|
|
602
378
|
resolveFlow: async (invoked) => {
|
|
603
379
|
const sub = await flowsForSession(deps, target).flows.load(invoked, projectId);
|
|
604
|
-
return sub.ok ? await resolveFlowUploads(
|
|
380
|
+
return sub.ok ? await resolveFlowUploads(uploadDeps, sub.value) : undefined;
|
|
605
381
|
},
|
|
606
382
|
// Bug-sweep mode: keep going past a step whose action ran and whose consequence merely did
|
|
607
383
|
// not hold, so one flow reports one verdict per step instead of stopping at the first defect.
|
|
@@ -618,26 +394,28 @@ observe = (s) => s) {
|
|
|
618
394
|
});
|
|
619
395
|
}
|
|
620
396
|
catch (error) {
|
|
621
|
-
if (!(error instanceof DocumentLostDuringReplay))
|
|
397
|
+
if (!(error instanceof DocumentLostDuringReplay)) {
|
|
622
398
|
throw error;
|
|
623
|
-
|
|
399
|
+
}
|
|
400
|
+
return withStartPageWait(lostDocumentResult(name, error), arrival);
|
|
624
401
|
}
|
|
625
402
|
// Only part of the journey was driven here: its steps were checked, nothing about the whole flow.
|
|
626
|
-
if (0 < (continueAt ?? 0) || (to !== undefined && to < loaded.value.steps.length))
|
|
627
|
-
return { name, status: partialStatus(steps), steps };
|
|
403
|
+
if (0 < (continueAt ?? 0) || (to !== undefined && to < loaded.value.steps.length)) {
|
|
404
|
+
return withStartPageWait({ name, status: partialStatus(steps), steps }, arrival);
|
|
405
|
+
}
|
|
628
406
|
// Computed HERE, before the synthetic success row is appended below: once that row is pushed,
|
|
629
407
|
// `steps.length` no longer counts only the flow's own steps and the arithmetic is wrong.
|
|
630
408
|
const halted = haltedFrom(steps, loaded.value.steps.length);
|
|
631
409
|
/*
|
|
632
|
-
* The whole-span pass, run once the journey is over. Best-effort
|
|
633
|
-
*
|
|
634
|
-
* finding is a smaller loss than a run that dies computing one.
|
|
410
|
+
* The whole-span pass, run once the journey is over. Best-effort: a session stub without
|
|
411
|
+
* `eventsSince` must not abort a healthy replay. A missing cross-step finding costs less.
|
|
635
412
|
*/
|
|
636
413
|
const crossStep = (() => {
|
|
637
414
|
try {
|
|
638
415
|
const span = { since: replayFloor, until: session.elapsed() };
|
|
639
416
|
const events = session.eventsSince(replayFloor).filter((e) => e.t >= replayFloor);
|
|
640
|
-
|
|
417
|
+
const scope = { ...appScope(session), declared: declaredIn(loaded.value.steps) };
|
|
418
|
+
return crossStepOnly(stepEffect(events, span, scope).contradictions ?? [], steps);
|
|
641
419
|
}
|
|
642
420
|
catch {
|
|
643
421
|
return [];
|
|
@@ -663,11 +441,21 @@ observe = (s) => s) {
|
|
|
663
441
|
steps.push(row);
|
|
664
442
|
}
|
|
665
443
|
const staleTarget = staleTargetResult(name, steps);
|
|
666
|
-
if (staleTarget !== undefined)
|
|
667
|
-
return staleTarget;
|
|
444
|
+
if (staleTarget !== undefined) {
|
|
445
|
+
return withStartPageWait(staleTarget, arrival);
|
|
446
|
+
}
|
|
668
447
|
const driftSteps = steps.filter((s) => s.drift !== undefined).length;
|
|
669
448
|
const allOk = steps.every((s) => s.ok);
|
|
670
|
-
|
|
449
|
+
let status;
|
|
450
|
+
if (driftSteps > 0) {
|
|
451
|
+
status = ReplayStatus.DRIFT;
|
|
452
|
+
}
|
|
453
|
+
else if (allOk) {
|
|
454
|
+
status = ReplayStatus.OK;
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
status = ReplayStatus.ERROR;
|
|
458
|
+
}
|
|
671
459
|
await recordReplayRun(deps, name, status, driftSteps, deps.now() - startedAt, projectId, replayRoot);
|
|
672
460
|
// Anti-reward-hacking baseline: record what this flow asserted ONLY when it passed clean. A
|
|
673
461
|
// failing run must never become the baseline a later weakening is measured against.
|
|
@@ -714,32 +502,41 @@ observe = (s) => s) {
|
|
|
714
502
|
};
|
|
715
503
|
errored.decision = buildDecision(errored, loaded.value, intentSaid, invokedFlows);
|
|
716
504
|
applyStartPathHint(errored, startPathHint);
|
|
717
|
-
if (deviation !== undefined)
|
|
505
|
+
if (deviation !== undefined) {
|
|
718
506
|
errored.deviation = deviation;
|
|
719
|
-
|
|
507
|
+
}
|
|
508
|
+
if (knows !== undefined) {
|
|
720
509
|
errored.knows = knows;
|
|
721
|
-
|
|
510
|
+
}
|
|
511
|
+
if (crossStep.length > 0) {
|
|
722
512
|
errored.crossStep = crossStep;
|
|
723
|
-
|
|
513
|
+
}
|
|
514
|
+
return withStartPageWait(errored, arrival);
|
|
724
515
|
}
|
|
725
516
|
const result = { name, status, steps };
|
|
726
|
-
if (halted !== undefined)
|
|
517
|
+
if (halted !== undefined) {
|
|
727
518
|
result.halted = halted;
|
|
728
|
-
|
|
519
|
+
}
|
|
520
|
+
if (knows !== undefined) {
|
|
729
521
|
result.knows = knows;
|
|
522
|
+
}
|
|
730
523
|
// A green that cannot go red is not a pass: the same judgement `reticle_flow_verify` makes.
|
|
731
524
|
const cannotFail = status === ReplayStatus.OK ? unverifiableReason(loaded.value, invokedFlows) : undefined;
|
|
732
|
-
if (cannotFail !== undefined)
|
|
525
|
+
if (cannotFail !== undefined) {
|
|
733
526
|
result.unverifiable = { reason: cannotFail };
|
|
527
|
+
}
|
|
734
528
|
// One check of the session that ran it, so its end does not say "nothing was checked".
|
|
735
529
|
noteReplayCheck(session, name, status, cannotFail !== undefined);
|
|
736
|
-
if (status !== ReplayStatus.OK)
|
|
530
|
+
if (status !== ReplayStatus.OK) {
|
|
737
531
|
result.decision = buildDecision(result, loaded.value, intentSaid, invokedFlows);
|
|
532
|
+
}
|
|
738
533
|
applyStartPathHint(result, startPathHint);
|
|
739
|
-
if (deviation !== undefined)
|
|
534
|
+
if (deviation !== undefined) {
|
|
740
535
|
result.deviation = deviation;
|
|
741
|
-
|
|
536
|
+
}
|
|
537
|
+
if (crossStep.length > 0) {
|
|
742
538
|
result.crossStep = crossStep;
|
|
539
|
+
}
|
|
743
540
|
/*
|
|
744
541
|
* What this run taught the flow.
|
|
745
542
|
*
|
|
@@ -776,10 +573,12 @@ observe = (s) => s) {
|
|
|
776
573
|
observed: steps.some((step) => step.digest !== undefined),
|
|
777
574
|
});
|
|
778
575
|
result.learned = learning.guards;
|
|
779
|
-
if (learning.promoted.length > 0)
|
|
576
|
+
if (learning.promoted.length > 0) {
|
|
780
577
|
result.promoted = learning.promoted;
|
|
781
|
-
|
|
578
|
+
}
|
|
579
|
+
if (learning.regressed.length > 0) {
|
|
782
580
|
result.regressed = learning.regressed;
|
|
581
|
+
}
|
|
783
582
|
/*
|
|
784
583
|
* Write it back, or none of the above compounds.
|
|
785
584
|
*
|
|
@@ -792,7 +591,7 @@ observe = (s) => s) {
|
|
|
792
591
|
* lesson is smaller than losing the run that produced it. Skipped entirely when nothing moved,
|
|
793
592
|
* so a clean replay of a flow with nothing to learn does not rewrite a file for no reason.
|
|
794
593
|
*/
|
|
795
|
-
return result;
|
|
594
|
+
return withStartPageWait(result, arrival);
|
|
796
595
|
}
|
|
797
596
|
/**
|
|
798
597
|
* The deviation report over a replay's route segments — compared against the learned envelope, which is
|
|
@@ -803,8 +602,9 @@ async function computeReplayDeviation(deps, session, floor,
|
|
|
803
602
|
root) {
|
|
804
603
|
try {
|
|
805
604
|
const segments = computeSegments(session.eventsSince(floor));
|
|
806
|
-
if (0 === segments.length)
|
|
605
|
+
if (0 === segments.length) {
|
|
807
606
|
return undefined;
|
|
607
|
+
}
|
|
808
608
|
return await reportAndAccumulate(new EnvelopeStore(deps.fs, root), segments);
|
|
809
609
|
}
|
|
810
610
|
catch {
|
|
@@ -813,8 +613,9 @@ root) {
|
|
|
813
613
|
}
|
|
814
614
|
/** Fold a start-page-mismatch hint onto a non-passing replay's decision (the actionable next move). */
|
|
815
615
|
function applyStartPathHint(result, hint) {
|
|
816
|
-
if (hint === undefined || result.decision === undefined)
|
|
616
|
+
if (hint === undefined || result.decision === undefined) {
|
|
817
617
|
return;
|
|
618
|
+
}
|
|
818
619
|
result.decision.suggestedFix = hint;
|
|
819
620
|
result.decision.nextAction = hint;
|
|
820
621
|
}
|