@reticlehq/server 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-ENTERPRISE +1 -1
- package/README.md +3 -3
- package/SKILL.md +23 -22
- package/dist/chromium-launch-options.d.ts +4 -3
- package/dist/chromium-launch-options.js +6 -2
- package/dist/command/cli/adhoc-suite.d.ts +41 -0
- package/dist/command/cli/adhoc-suite.js +159 -0
- package/dist/command/cli/adhoc-verdict.d.ts +58 -2
- package/dist/command/cli/adhoc-verdict.js +108 -13
- package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
- package/dist/command/cli/answers/gate-hook-message.js +6 -0
- package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
- package/dist/command/cli/auth/cloud-keystore.js +0 -3
- package/dist/command/cli/auth/cloud-session.d.ts +4 -0
- package/dist/command/cli/auth/cloud-session.js +2 -0
- package/dist/command/cli/cli-doctor.js +30 -8
- package/dist/command/cli/cli-flow-commands.d.ts +3 -6
- package/dist/command/cli/cli-flow-commands.js +67 -4
- package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
- package/dist/command/cli/cli-parse-grammar.js +7 -0
- package/dist/command/cli/cli-parse-verify.d.ts +2 -5
- package/dist/command/cli/cli-parse-verify.js +86 -13
- package/dist/command/cli/cli-parse.d.ts +14 -1
- package/dist/command/cli/cli-parse.js +54 -8
- package/dist/command/cli/cli-update-commands.js +8 -1
- package/dist/command/cli/cli-verify.d.ts +80 -0
- package/dist/command/cli/cli-verify.js +181 -26
- package/dist/command/cli/cloud-cli.js +5 -2
- package/dist/command/cli/cloud-kit.d.ts +0 -2
- package/dist/command/cli/cloud-kit.js +3 -5
- package/dist/command/cli/cloud-login.js +21 -3
- package/dist/command/cli/demo/demo-page.js +6 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
- package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
- package/dist/command/cli/launch/cli-launch.d.ts +9 -29
- package/dist/command/cli/launch/cli-launch.js +27 -49
- package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
- package/dist/command/cli/launch/sdk-version-match.js +206 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
- package/dist/command/cli/ports/resolve/cli-port.d.ts +38 -0
- package/dist/command/cli/ports/resolve/cli-port.js +169 -15
- package/dist/command/cli/report-command.d.ts +16 -0
- package/dist/command/cli/report-command.js +97 -0
- package/dist/command/cli/setup-mcp-cli.js +13 -13
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +21 -7
- package/dist/command/cli.js +67 -10
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +44 -2
- package/dist/command/daemon/binding/daemon-status-probe.js +79 -6
- package/dist/command/daemon/binding/port-presence.d.ts +1 -1
- package/dist/command/daemon/daemon-resolve.d.ts +9 -0
- package/dist/command/daemon/daemon-resolve.js +1 -1
- package/dist/command/daemon/idle-shutdown.d.ts +2 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
- package/dist/command/dev/stale-issue-guard.d.ts +3 -4
- package/dist/command/dev/stale-issue-guard.js +8 -8
- package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
- package/dist/command/setup/bringup/connect-lease.js +38 -0
- package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
- package/dist/command/setup/bringup/dev-server-files.js +131 -0
- package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
- package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
- package/dist/command/setup/bringup/ensure-daemon.js +135 -16
- package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
- package/dist/command/setup/bringup/static-page-server.js +151 -0
- package/dist/command/setup/desktop-shape.d.ts +4 -0
- package/dist/command/setup/desktop-shape.js +18 -2
- package/dist/command/setup/init/init-host.js +8 -0
- package/dist/command/setup/init/init-port.d.ts +15 -0
- package/dist/command/setup/init/init-port.js +44 -0
- package/dist/command/setup/init/init-runtime.d.ts +1 -1
- package/dist/command/setup/init/init-runtime.js +39 -2
- package/dist/command/setup/node-effects.d.ts +27 -14
- package/dist/command/setup/node-effects.js +80 -44
- package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
- package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
- package/dist/command/setup/probe/page-probe.d.ts +13 -2
- package/dist/command/setup/probe/page-probe.js +18 -3
- package/dist/command/setup/run-setup.d.ts +26 -1
- package/dist/command/setup/run-setup.js +96 -13
- package/dist/command/setup/session-pick.d.ts +13 -0
- package/dist/command/setup/session-pick.js +20 -0
- package/dist/command/setup/setup-command.d.ts +2 -14
- package/dist/command/setup/setup-command.js +105 -31
- package/dist/command/setup/setup-mcp.js +3 -3
- package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
- package/dist/command/setup/terminal/wait-progress.js +17 -0
- package/dist/features/crawl/crawl-empty.js +1 -1
- package/dist/features/crawl/crawl-tools.d.ts +6 -0
- package/dist/features/crawl/crawl-tools.js +57 -0
- package/dist/features/crawl/crawl.d.ts +2 -0
- package/dist/features/crawl/crawl.js +171 -156
- package/dist/features/exhaust/app-coverage.d.ts +33 -0
- package/dist/features/exhaust/app-coverage.js +31 -0
- package/dist/features/exhaust/code-coverage.d.ts +47 -0
- package/dist/features/exhaust/code-coverage.js +77 -0
- package/dist/features/exhaust/exhaust-run.d.ts +33 -0
- package/dist/features/exhaust/exhaust-run.js +82 -0
- package/dist/features/exhaust/explorer.d.ts +103 -0
- package/dist/features/exhaust/explorer.js +195 -0
- package/dist/features/exhaust/failure-branches.d.ts +33 -0
- package/dist/features/exhaust/failure-branches.js +92 -0
- package/dist/features/exhaust/ledger.d.ts +202 -0
- package/dist/features/exhaust/ledger.js +252 -0
- package/dist/features/exhaust/session-fold.d.ts +31 -0
- package/dist/features/exhaust/session-fold.js +79 -0
- package/dist/features/exhaust/session-port.d.ts +36 -0
- package/dist/features/exhaust/session-port.js +102 -0
- package/dist/features/harness/drive-report.d.ts +0 -9
- package/dist/features/harness/drive-report.js +18 -0
- package/dist/features/harness/driver.js +4 -7
- package/dist/features/harness/fill-values.d.ts +6 -0
- package/dist/features/harness/fill-values.js +3 -0
- package/dist/features/harness/jev-driver.js +36 -22
- package/dist/features/harness/openai-driver.js +4 -7
- package/dist/features/harness/platform-config.d.ts +10 -0
- package/dist/features/harness/platform-config.js +13 -6
- package/dist/index.d.ts +10 -1
- package/dist/index.js +78 -104
- package/dist/judgement/capsule/capsule-store.d.ts +31 -2
- package/dist/judgement/capsule/capsule-store.js +58 -6
- package/dist/judgement/capsule/capsule.js +5 -0
- package/dist/judgement/capsule/causal-summary.d.ts +13 -1
- package/dist/judgement/capsule/causal-summary.js +7 -0
- package/dist/judgement/domain/domain-model.js +40 -9
- package/dist/judgement/oracles/self-instrument.js +4 -2
- package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
- package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
- package/dist/judgement/runs/artifact/gap-report.js +46 -0
- package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
- package/dist/judgement/runs/artifact/gap-summary.js +85 -0
- package/dist/judgement/runs/artifact/render-report.js +4 -1
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
- package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
- package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
- package/dist/judgement/runs/artifact/to-artifact.js +1 -1
- package/dist/judgement/runs/context-tools.js +21 -3
- package/dist/judgement/runs/drive-run.js +7 -2
- package/dist/judgement/runs/verification-sync.d.ts +3 -1
- package/dist/judgement/runs/verification-sync.js +1 -1
- package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
- package/dist/language/flows/annotate-notes/annotate.js +66 -18
- package/dist/language/flows/change/affected.d.ts +9 -0
- package/dist/language/flows/change/affected.js +35 -0
- package/dist/language/flows/change/gate.d.ts +20 -0
- package/dist/language/flows/change/gate.js +12 -1
- package/dist/language/flows/flow-anchor.d.ts +12 -0
- package/dist/language/flows/flow-anchor.js +20 -0
- package/dist/language/flows/flow-author.d.ts +7 -0
- package/dist/language/flows/flow-author.js +19 -0
- package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
- package/dist/language/flows/flow-expect-grammar.js +92 -11
- package/dist/language/flows/flow-intent.js +1 -1
- package/dist/language/flows/flow-journey.d.ts +54 -0
- package/dist/language/flows/flow-journey.js +119 -0
- package/dist/language/flows/flow-learning.d.ts +3 -0
- package/dist/language/flows/flow-learning.js +39 -8
- package/dist/language/flows/flow-replay-run.d.ts +33 -12
- package/dist/language/flows/flow-replay-run.js +131 -52
- package/dist/language/flows/flow-replay-types.d.ts +9 -0
- package/dist/language/flows/flow-replay.d.ts +27 -12
- package/dist/language/flows/flow-replay.js +127 -111
- package/dist/language/flows/flow-result.d.ts +2 -0
- package/dist/language/flows/flow-result.js +25 -0
- package/dist/language/flows/flow-step-runners.d.ts +26 -10
- package/dist/language/flows/flow-step-runners.js +20 -4
- package/dist/language/flows/flow-success.d.ts +21 -6
- package/dist/language/flows/flow-success.js +50 -138
- package/dist/language/flows/flow-tools.js +17 -5
- package/dist/language/flows/flows.d.ts +18 -5
- package/dist/language/flows/flows.js +101 -16
- package/dist/language/flows/heal.d.ts +9 -1
- package/dist/language/flows/heal.js +24 -2
- package/dist/language/flows/learned-sources.d.ts +21 -0
- package/dist/language/flows/learned-sources.js +84 -0
- package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
- package/dist/language/flows/recording/recording-backtrack.js +1 -1
- package/dist/language/flows/recording/replay-halt.d.ts +2 -1
- package/dist/language/flows/recording/replay-halt.js +3 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +31 -2
- package/dist/language/flows/recording/tape/recordings.js +55 -1
- package/dist/language/flows/replay.d.ts +19 -0
- package/dist/language/flows/replay.js +53 -13
- package/dist/language/flows/stores/annotation-store.d.ts +5 -5
- package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
- package/dist/language/flows/stores/flake-store.js +2 -3
- package/dist/language/flows/suite/coverage.d.ts +1 -1
- package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
- package/dist/language/flows/verify-change-tools.js +19 -2
- package/dist/launch-chromium.d.ts +80 -0
- package/dist/launch-chromium.js +218 -0
- package/dist/memory/cloud/cloud-config.d.ts +11 -2
- package/dist/memory/cloud/cloud-config.js +47 -7
- package/dist/memory/cloud/cloud-sync.d.ts +4 -3
- package/dist/memory/cloud/cloud-sync.js +7 -10
- package/dist/memory/cloud/harness-config.d.ts +2 -0
- package/dist/memory/cloud/harness-offer.js +5 -6
- package/dist/memory/cloud/harness-switch.js +5 -6
- package/dist/memory/cloud/sync-cycle.d.ts +78 -1
- package/dist/memory/cloud/sync-cycle.js +321 -53
- package/dist/memory/cloud/sync-daemon.d.ts +13 -0
- package/dist/memory/cloud/sync-daemon.js +45 -20
- package/dist/memory/cloud/sync-disk.js +27 -8
- package/dist/memory/impact/impact-store.js +6 -0
- package/dist/memory/intent/inline-intent.js +8 -3
- package/dist/memory/intent/intent-shard-store.d.ts +33 -47
- package/dist/memory/intent/intent-shard-store.js +252 -140
- package/dist/memory/intent/intent-shard.d.ts +0 -23
- package/dist/memory/intent/intent-shard.js +1 -11
- package/dist/memory/intent/intent-store.d.ts +13 -8
- package/dist/memory/intent/intent-store.js +46 -90
- package/dist/memory/intent/intent-tools.js +34 -23
- package/dist/memory/journal/ambient-store.js +2 -3
- package/dist/memory/journal/deviation-report.js +4 -1
- package/dist/memory/journal/deviation-service.js +10 -2
- package/dist/memory/journal/drive-flow.d.ts +19 -14
- package/dist/memory/journal/drive-flow.js +65 -48
- package/dist/memory/journal/envelope-store.js +2 -3
- package/dist/memory/journal/journal-recorder.d.ts +21 -0
- package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
- package/dist/memory/journal/on-disk/retain-policy.js +44 -0
- package/dist/memory/journal/on-disk/retention.d.ts +14 -10
- package/dist/memory/journal/on-disk/retention.js +13 -12
- package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
- package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -11
- package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -45
- package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
- package/dist/memory/journal/on-disk/workspace-tiers.js +90 -0
- package/dist/memory/journal/route-template.d.ts +22 -0
- package/dist/memory/journal/route-template.js +44 -0
- package/dist/memory/journal/session-end.d.ts +13 -2
- package/dist/memory/journal/session-end.js +88 -18
- package/dist/memory/journal/session-journal.d.ts +8 -1
- package/dist/memory/journal/session-journal.js +10 -0
- package/dist/memory/project/baselines.d.ts +1 -1
- package/dist/memory/project/dir/reticle-dir.d.ts +3 -1
- package/dist/memory/project/dir/reticle-dir.js +1 -0
- package/dist/memory/project/fs/fs-port.js +44 -5
- package/dist/memory/project/fs/write-atomic.d.ts +26 -0
- package/dist/memory/project/fs/write-atomic.js +48 -0
- package/dist/memory/project/project-tools.js +3 -1
- package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
- package/dist/memory/recall/prior/connection-memory.js +8 -0
- package/dist/memory/recall/registered-projects.d.ts +2 -1
- package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
- package/dist/portal/bridge/auth-failure-reason.js +9 -4
- package/dist/portal/bridge/bridge.d.ts +8 -0
- package/dist/portal/bridge/bridge.js +43 -2
- package/dist/portal/input/js-coverage.d.ts +25 -0
- package/dist/portal/input/js-coverage.js +30 -0
- package/dist/portal/input/real-input.d.ts +11 -0
- package/dist/portal/input/real-input.js +31 -5
- package/dist/portal/pool/browser-pool.d.ts +10 -0
- package/dist/portal/pool/browser-pool.js +10 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +21 -13
- package/dist/portal/pool/pool-contract.d.ts +2 -0
- package/dist/portal/pool/storage-seed.d.ts +2 -1
- package/dist/portal/pool/zero-install.d.ts +27 -0
- package/dist/portal/pool/zero-install.js +54 -0
- package/dist/portal/realm/web-realm.js +6 -0
- package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
- package/dist/portal/session/dev-server/dev-command.js +30 -12
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
- package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
- package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
- package/dist/portal/session/dev-server/route-status-probe.js +57 -0
- package/dist/portal/session/dev-server/served-document.d.ts +21 -0
- package/dist/portal/session/dev-server/served-document.js +57 -0
- package/dist/portal/session/facts/background-traffic.d.ts +9 -0
- package/dist/portal/session/facts/background-traffic.js +25 -0
- package/dist/portal/session/facts/observed-state.d.ts +11 -0
- package/dist/portal/session/facts/observed-state.js +34 -0
- package/dist/portal/session/live-control-tools.js +48 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
- package/dist/portal/session/no-session-diagnosis.js +62 -5
- package/dist/portal/session/no-session-next-action.d.ts +13 -0
- package/dist/portal/session/no-session-next-action.js +48 -9
- package/dist/portal/session/no-session-watch.d.ts +18 -0
- package/dist/portal/session/no-session-watch.js +88 -2
- package/dist/portal/session/presence/attachment-history.d.ts +13 -2
- package/dist/portal/session/session-health.d.ts +61 -0
- package/dist/portal/session/session-health.js +43 -3
- package/dist/portal/session/session-info.d.ts +6 -0
- package/dist/portal/session/session-manager.d.ts +29 -6
- package/dist/portal/session/session-manager.js +42 -5
- package/dist/portal/session/session-reaper.d.ts +28 -0
- package/dist/portal/session/session-reaper.js +49 -1
- package/dist/portal/session/session-successor.d.ts +11 -0
- package/dist/portal/session/session-successor.js +14 -1
- package/dist/portal/session/session-verdict-facts.d.ts +28 -0
- package/dist/portal/session/session-verdict-facts.js +15 -0
- package/dist/portal/session/session.d.ts +10 -0
- package/dist/portal/session/session.js +34 -26
- package/dist/surface/http-server.d.ts +2 -1
- package/dist/surface/http-server.js +4 -2
- package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
- package/dist/surface/mcp/mcp-proxy.js +5 -5
- package/dist/surface/mcp/mcp.js +4 -2
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
- package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
- package/dist/surface/mcp/server-instructions.js +21 -8
- package/dist/surface/tools/act/after-match.d.ts +17 -0
- package/dist/surface/tools/act/after-match.js +30 -0
- package/dist/surface/tools/act/already-true.d.ts +40 -0
- package/dist/surface/tools/act/already-true.js +36 -0
- package/dist/surface/tools/act/durable.d.ts +69 -0
- package/dist/surface/tools/act/durable.js +139 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +19 -6
- package/dist/surface/tools/act/navigation/navigate-arrival.js +60 -10
- package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
- package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
- package/dist/surface/tools/act/proved-controls.d.ts +9 -0
- package/dist/surface/tools/act/proved-controls.js +11 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
- package/dist/surface/tools/act/sequence-grade.js +32 -1
- package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
- package/dist/surface/tools/act/settle-in-flight.js +29 -6
- package/dist/surface/tools/act-capsule.d.ts +7 -0
- package/dist/surface/tools/act-capsule.js +68 -8
- package/dist/surface/tools/act-sequence-tool.js +25 -8
- package/dist/surface/tools/act-tools.js +51 -74
- package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
- package/dist/surface/tools/args/numeric-bounds.js +30 -3
- package/dist/surface/tools/assert/assert-grade.js +26 -3
- package/dist/surface/tools/assert/assert-verdict.js +32 -6
- package/dist/surface/tools/browser-tools.js +17 -4
- package/dist/surface/tools/coverage-tools.js +27 -1
- package/dist/surface/tools/dynamic-tools.js +6 -0
- package/dist/surface/tools/error-recovery.js +4 -4
- package/dist/surface/tools/feedback-invite.js +6 -4
- package/dist/surface/tools/feedback-tools.d.ts +2 -1
- package/dist/surface/tools/feedback-tools.js +1 -1
- package/dist/surface/tools/harness-explore.d.ts +3 -2
- package/dist/surface/tools/harness-explore.js +7 -6
- package/dist/surface/tools/invoke-tool.js +17 -4
- package/dist/surface/tools/lease-hint.d.ts +8 -0
- package/dist/surface/tools/lease-hint.js +10 -0
- package/dist/surface/tools/lease-tools.d.ts +16 -28
- package/dist/surface/tools/lease-tools.js +68 -11
- package/dist/surface/tools/lineage-tools.js +1 -1
- package/dist/surface/tools/live-call-text.js +4 -1
- package/dist/surface/tools/observe-tools.js +23 -7
- package/dist/surface/tools/real-input-attempt.d.ts +0 -5
- package/dist/surface/tools/real-input-attempt.js +1 -1
- package/dist/surface/tools/tool-kit.d.ts +5 -0
- package/dist/surface/tools/tool-kit.js +6 -1
- package/dist/surface/tools/tool-surface.js +17 -5
- package/dist/surface/tools/tools.js +68 -29
- package/dist/telemetry/argument-shape.d.ts +0 -2
- package/dist/telemetry/argument-shape.js +0 -4
- package/dist/telemetry/error-class.d.ts +2 -3
- package/dist/telemetry/error-class.js +2 -3
- package/dist/telemetry/feedback-local.d.ts +1 -0
- package/dist/telemetry/feedback-local.js +1 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/harness-drive.d.ts +0 -2
- package/dist/telemetry/harness-drive.js +0 -4
- package/dist/telemetry/hud-metrics.d.ts +32 -0
- package/dist/telemetry/hud-metrics.js +95 -0
- package/dist/telemetry/session-metrics.d.ts +5 -0
- package/dist/telemetry/session-metrics.js +15 -1
- package/dist/wire-journal.d.ts +34 -0
- package/dist/wire-journal.js +104 -0
- package/dist/workspace-packages.d.ts +0 -2
- package/dist/workspace-packages.js +1 -9
- package/docs/README.md +5 -2
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +4 -4
- package/docs/architecture.md +6 -6
- package/docs/best-practices.mdx +1 -1
- package/docs/capabilities.mdx +32 -1
- package/docs/cli/cloud.mdx +21 -9
- package/docs/cli/doctor.mdx +4 -0
- package/docs/cli/feedback.mdx +2 -2
- package/docs/cli/gate.mdx +9 -4
- package/docs/cli/init.mdx +1 -0
- package/docs/cli/report.mdx +44 -0
- package/docs/cli/update.mdx +10 -0
- package/docs/cli.mdx +9 -0
- package/docs/desktop.mdx +14 -10
- package/docs/environment.mdx +87 -0
- package/docs/faq.mdx +18 -16
- package/docs/first-drive.md +5 -13
- package/docs/flows.md +32 -2
- package/docs/frameworks.mdx +140 -9
- package/docs/getting-started.md +3 -3
- package/docs/http-transport.md +6 -3
- package/docs/human-control.md +2 -2
- package/docs/index.mdx +16 -6
- package/docs/install-agentic.mdx +37 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/integration-patterns.md +3 -3
- package/docs/multi-agent-testing.md +1 -1
- package/docs/packages/babel-plugin.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/react.mdx +2 -2
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/vite-plugin.mdx +2 -2
- package/docs/packages.mdx +9 -9
- package/docs/predicates.mdx +176 -7
- package/docs/quickstart.mdx +2 -2
- package/docs/recipes.mdx +2 -2
- package/docs/state-management.mdx +2 -2
- package/docs/system-map.md +1 -1
- package/docs/telemetry.md +1 -1
- package/docs/token-efficiency.md +3 -3
- package/docs/tools/act-and-wait.mdx +1 -0
- package/docs/tools/act-sequence.mdx +1 -1
- package/docs/tools/console.mdx +1 -1
- package/docs/tools/inspect.mdx +1 -1
- package/docs/tools/network.mdx +1 -1
- package/docs/tools/query.mdx +11 -11
- package/docs/tools/sessions.mdx +1 -1
- package/docs/tools/snapshot.mdx +1 -1
- package/docs/tools/state.mdx +1 -1
- package/docs/tools/wait-for.mdx +1 -1
- package/docs/usage.md +8 -7
- package/docs/use-cases.mdx +157 -0
- package/docs/vs-screenshots.mdx +3 -3
- package/docs/what-is-recorded.md +62 -0
- package/docs/what-is-reticle.mdx +17 -20
- package/docs/why-reticle.mdx +14 -21
- package/package.json +11 -10
- package/dist/judgement/capsule/minimize.d.ts +0 -12
- package/dist/judgement/capsule/minimize.js +0 -22
- package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
- package/dist/judgement/outcome/predicate-to-expect.js +0 -148
- package/dist/judgement/phenomena/phenomena.d.ts +0 -26
- package/dist/judgement/phenomena/phenomena.js +0 -101
- package/dist/language/flows/flow-report.d.ts +0 -22
- package/dist/language/flows/flow-report.js +0 -106
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export declare const PROXY_IDLE_EXIT_MS: number;
|
|
11
11
|
/** The local diagnostic emitted before an abandoned proxy exits. */
|
|
12
12
|
export declare const PROXY_IDLE_EXIT_EVENT = "reticle_mcp_proxy_idle_exit";
|
|
13
|
-
|
|
13
|
+
interface ProxyIdleExitOptions {
|
|
14
14
|
/** Called once after the client link has stayed quiet for the whole grace. */
|
|
15
15
|
onExit: (idleMs: number) => void;
|
|
16
16
|
/** True while a request or queued message still needs an answer. */
|
|
@@ -43,3 +43,4 @@ export declare class ProxyIdleExit {
|
|
|
43
43
|
* zero as the explicit opt-out. Invalid values retain the conservative one-day default.
|
|
44
44
|
*/
|
|
45
45
|
export declare function resolveProxyIdleExitMs(raw: string | undefined): number;
|
|
46
|
+
export {};
|
|
@@ -31,11 +31,22 @@ import { surfaceVocabulary, listOf } from './surface-vocabulary.js';
|
|
|
31
31
|
* measured reason. A briefing that names a tool the agent was not given does not merely confuse it;
|
|
32
32
|
* it makes the agent stop using the product altogether.
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
/*
|
|
35
|
+
* `reticle gate` is FOLDED INTO the done sentence rather than given a paragraph, and that is the
|
|
36
|
+
* budget rule working rather than a stylistic choice.
|
|
37
|
+
*
|
|
38
|
+
* The instruction block has a hard byte cap whose own note says a raise must be paid for with a
|
|
39
|
+
* measurement, and that if one cannot be shown the right answer is to cut an older sentence. There
|
|
40
|
+
* is no measurement for `gate`: it is built, it has anti-reward-hacking, and it appeared in no
|
|
41
|
+
* surface an agent reads, which is an argument and not a number. Cutting a measured sentence to
|
|
42
|
+
* make room for an unmeasured one would be strictly worse. So it costs about a hundred characters
|
|
43
|
+
* inside the sentence that already names the exact moment it applies to, and the cap did not move.
|
|
44
|
+
*/
|
|
45
|
+
const verdictDiscipline = (v) => `Reticle verifies a running web app from the inside: go (${v.navigate}), look (${listOf(v.look, v.find)}), act and prove in one hop (${v.actAndWait}), observe (${listOf(v.observe, v.state, v.network, v.console)}), assert (${v.assert}). Verify a user-facing change against the real app before you call it done — \`npx @reticlehq/server gate --since <ref>\` fails unless a passing artifact covers every flow your edits affect — and never weaken a check to make it pass.
|
|
35
46
|
|
|
36
|
-
Only ${v.actAndWait}
|
|
47
|
+
Only ${v.act.length > 0 ? `${v.actAndWait}, ${v.assert} and ${v.act}{steps}` : `${v.actAndWait} and ${v.assert}`} produce a verdict. Everything else moves or reads the app and proves nothing, so a drive that ends without one of those two has no result however many tools it used. Prefer ${v.actAndWait}({ ref, action, until }) — it names the expected consequence BEFORE the action, which is the difference between a check and a rationalisation. Only verified:"yes" is a pass — "unknown" means Reticle could not tell what happened, "no-fault" means nothing was declared to prove. Report either as not proved.
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
Done is the journey's END proved, not its first step: drive each step your change touches and give the last an \`until\` naming the end state (\`durable: true\` if it must persist). A "yes" on it over a clean capture IS the answer — stop.
|
|
39
50
|
|
|
40
51
|
DIAGNOSING a bug? Read the source first — Reticle proves what the app DOES, not why. Drive to CONFIRM a fix, not to find one.`;
|
|
41
52
|
/**
|
|
@@ -73,13 +84,15 @@ const replayFirst = (v) => {
|
|
|
73
84
|
*
|
|
74
85
|
* Deliberately concrete: the command, what it does to the app, and the call that confirms it. A
|
|
75
86
|
* general instruction to "set Reticle up" is the state these agents are already in — they have the
|
|
76
|
-
* tools and no app, and nothing tells them that combination is incomplete.
|
|
77
|
-
* server
|
|
78
|
-
*
|
|
87
|
+
* tools and no app, and nothing tells them that combination is incomplete. `init` starts the dev
|
|
88
|
+
* server and opens the app itself, so the restart is named only for the case it cannot cover: a dev
|
|
89
|
+
* server that was already running read the config before `init` edited it, which is the single most
|
|
90
|
+
* common way this step half-succeeds. Telling every agent to restart sent them to kill the server
|
|
91
|
+
* `init` had just started.
|
|
79
92
|
*/
|
|
80
93
|
const firstMoveFor = (v) => `FIRST: no app has ever connected to Reticle in this project, so nothing here can be verified yet. Having these tools is not the same as being set up — the tools reach a daemon, and the daemon has nothing to look at until the app itself is instrumented.
|
|
81
94
|
|
|
82
|
-
Fix that before anything else: run \`npx @reticlehq/server init\` in the project
|
|
95
|
+
Fix that before anything else: run \`npx @reticlehq/server init\` in the project. It starts the dev server and opens the app itself; only if a dev server was ALREADY running without Reticle, restart it and reload the tab. Confirm with ${v.sessions} — a session listed there is the proof, and until one appears no other tool can tell you anything about this app.`;
|
|
83
96
|
/**
|
|
84
97
|
* When to reach for the tools that are advertised but never explained, and how to reach the two
|
|
85
98
|
* that are explained but not advertised. Both halves of the same defect.
|
|
@@ -100,7 +113,7 @@ Fix that before anything else: run \`npx @reticlehq/server init\` in the project
|
|
|
100
113
|
*/
|
|
101
114
|
const reachFor = (v) => {
|
|
102
115
|
const lines = [
|
|
103
|
-
`${v.observe} is the evidence channel: everything the page did since a cursor, in one read. Take it BEFORE you call a build broken — measured, stripping the observation tools TRIPLED false alarms
|
|
116
|
+
`${v.observe} is the evidence channel: everything the page did since a cursor, in one read. Take it BEFORE you call a build broken — measured, stripping the observation tools TRIPLED false alarms. ${v.settle} settles a page that is changing without you; ${v.inspect} maps an element to its source file:line, which turns a finding into an edit${v.yield.length > 0 ? `; ${v.yield} hands the tab back to the human` : ''}.`,
|
|
104
117
|
];
|
|
105
118
|
// Said ONLY when both halves are advertised. A catalogue with no way to invoke what it lists sends
|
|
106
119
|
// the agent at a tool it cannot call and then off the product entirely — see
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What act_and_wait does between the moment its predicate first holds and the moment it grades.
|
|
3
|
+
*
|
|
4
|
+
* The predicate resolves the INSTANT it holds, which on an optimistically-navigating app is while
|
|
5
|
+
* the write is still in flight — so the verdict was taken over a window the app had not finished,
|
|
6
|
+
* and came back `unknown / unsettled` asking the CALLER to re-check. A login that genuinely
|
|
7
|
+
* succeeded returned at 492ms of an 8000ms budget with every corroborating channel agreeing. So the
|
|
8
|
+
* rest of the budget the caller already granted is spent here, in three steps, each deciding
|
|
9
|
+
* nothing on its own.
|
|
10
|
+
*/
|
|
11
|
+
import type { Predicate } from '@reticlehq/core';
|
|
12
|
+
import { waitForInFlight } from './settle-in-flight.js';
|
|
13
|
+
import { revertedAfterMatch } from './durable.js';
|
|
14
|
+
type Waited = Parameters<typeof waitForInFlight>[0] & Parameters<typeof revertedAfterMatch>[0];
|
|
15
|
+
/** Returns the sentence to fail with when the consequence reverted, else undefined. */
|
|
16
|
+
export declare function finishAfterMatch(session: Waited, until: Predicate, since: number, baselines: Parameters<typeof revertedAfterMatch>[3], remaining: () => number): Promise<string | undefined>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What act_and_wait does between the moment its predicate first holds and the moment it grades.
|
|
3
|
+
*
|
|
4
|
+
* The predicate resolves the INSTANT it holds, which on an optimistically-navigating app is while
|
|
5
|
+
* the write is still in flight — so the verdict was taken over a window the app had not finished,
|
|
6
|
+
* and came back `unknown / unsettled` asking the CALLER to re-check. A login that genuinely
|
|
7
|
+
* succeeded returned at 492ms of an 8000ms budget with every corroborating channel agreeing. So the
|
|
8
|
+
* rest of the budget the caller already granted is spent here, in three steps, each deciding
|
|
9
|
+
* nothing on its own.
|
|
10
|
+
*/
|
|
11
|
+
import { waitForInFlight } from './settle-in-flight.js';
|
|
12
|
+
import { waitForReaction } from './react-grace.js';
|
|
13
|
+
import { revertedAfterMatch } from './durable.js';
|
|
14
|
+
/** Returns the sentence to fail with when the consequence reverted, else undefined. */
|
|
15
|
+
export async function finishAfterMatch(session, until, since, baselines, remaining) {
|
|
16
|
+
const sleep = { sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) };
|
|
17
|
+
// 1. Follow any open request to its response. A response that arrives as a FAILURE still
|
|
18
|
+
// contradicts — and now arrives INSIDE the window, where the detector can see it at all — while a
|
|
19
|
+
// request still open when the budget runs out reports unsettled, an honest limit rather than an
|
|
20
|
+
// early exit. Costs nothing on the common path, where no request is in flight.
|
|
21
|
+
await waitForInFlight(session, since, remaining(), sleep);
|
|
22
|
+
// 2. Then the app's REACTION to that response. Close the window between the response and the
|
|
23
|
+
// re-render and every channel agrees the app took a successful write and did nothing —
|
|
24
|
+
// `response-ignored`, a `verified:"no"` on a correct app, produced entirely by where we stopped
|
|
25
|
+
// looking. Paid only in the shape that would otherwise be accused, and short enough that a
|
|
26
|
+
// genuinely dropped response is still reported.
|
|
27
|
+
await waitForReaction(session, since, remaining(), sleep);
|
|
28
|
+
// 3. Then ask whether what held still holds — see revertedAfterMatch.
|
|
29
|
+
return revertedAfterMatch(session, until, since, baselines);
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Predicate } from '@reticlehq/engine/question/predicate/predicate-schema.js';
|
|
2
|
+
import type { PredicateSession } from '@reticlehq/engine/question/predicate/predicate-session.js';
|
|
3
|
+
import type { Baselines } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
4
|
+
/** Everything the pre-action check learned, which is three facts about one reading. */
|
|
5
|
+
export interface AlreadyTrueReading {
|
|
6
|
+
/** The consequence held BEFORE the action, so the action proved nothing about it. */
|
|
7
|
+
alreadyTrue: boolean;
|
|
8
|
+
/** That pre-existing match was against something hidden — a stronger reason to doubt the claim. */
|
|
9
|
+
alreadyTrueHiddenMatch: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* WHAT was already true, kept rather than discarded.
|
|
12
|
+
*
|
|
13
|
+
* `already_true` tells an agent its assertion held before it acted; the next question is always
|
|
14
|
+
* "true how?" and it was unanswerable, because the reading decided the bit and was then thrown
|
|
15
|
+
* away. Afterwards the action has run and the state may have moved, so the one moment that
|
|
16
|
+
* mattered is gone. Present only when the answer is yes: a verdict that was genuinely caused
|
|
17
|
+
* carries its own evidence and needs no baseline beside it.
|
|
18
|
+
*/
|
|
19
|
+
alreadyTrueEvidence?: unknown;
|
|
20
|
+
/**
|
|
21
|
+
* What each comparing leaf was reading BEFORE the action — the predicate language's past tense.
|
|
22
|
+
*
|
|
23
|
+
* Taken here because this is already the one place that looks at the app before it is touched,
|
|
24
|
+
* and a second pre-action pass would be a second round trip for a reading we are standing next
|
|
25
|
+
* to. Empty unless the caller actually asked for a comparison, so nothing pays for it.
|
|
26
|
+
*/
|
|
27
|
+
baselines: Baselines;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Ask whether the declared consequence was already true, once, before the action.
|
|
31
|
+
*
|
|
32
|
+
* Only for predicates that read live DOM state. Event-based ones are floored at this act's cursor
|
|
33
|
+
* and cannot be satisfied by the past, so they need no pre-check and pay nothing. One extra query,
|
|
34
|
+
* on the path where a green is otherwise unfalsifiable.
|
|
35
|
+
*
|
|
36
|
+
* Gathered here rather than as three locals at the call site because they are three readings OF ONE
|
|
37
|
+
* THING, and `act-tools.ts` sits on the 1000-line cohesion cap: the rule says split before adding,
|
|
38
|
+
* and this is the unit that wanted splitting.
|
|
39
|
+
*/
|
|
40
|
+
export declare function readAlreadyTrue(session: PredicateSession, until: Predicate | undefined, since: number): Promise<AlreadyTrueReading>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { evaluatePredicate } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
2
|
+
import { readsDomState } from '@reticlehq/engine/evidence/already-true.js';
|
|
3
|
+
import { alreadyTrueHiddenMatch as alreadyTrueHiddenMatchOf } from '@reticlehq/engine/evidence/already-true.js';
|
|
4
|
+
import { captureBaselines } from '@reticlehq/engine/evidence/baseline.js';
|
|
5
|
+
/**
|
|
6
|
+
* Ask whether the declared consequence was already true, once, before the action.
|
|
7
|
+
*
|
|
8
|
+
* Only for predicates that read live DOM state. Event-based ones are floored at this act's cursor
|
|
9
|
+
* and cannot be satisfied by the past, so they need no pre-check and pay nothing. One extra query,
|
|
10
|
+
* on the path where a green is otherwise unfalsifiable.
|
|
11
|
+
*
|
|
12
|
+
* Gathered here rather than as three locals at the call site because they are three readings OF ONE
|
|
13
|
+
* THING, and `act-tools.ts` sits on the 1000-line cohesion cap: the rule says split before adding,
|
|
14
|
+
* and this is the unit that wanted splitting.
|
|
15
|
+
*/
|
|
16
|
+
export async function readAlreadyTrue(session, until, since) {
|
|
17
|
+
// Before the pre-check, because the pre-check EVALUATES the predicate and a relative leaf with no
|
|
18
|
+
// baseline yet would report `inconclusive` — reading the app twice to answer a question we had
|
|
19
|
+
// not taken the reading for.
|
|
20
|
+
const baselines = await captureBaselines(session, until);
|
|
21
|
+
const precheck = until !== undefined && readsDomState(until)
|
|
22
|
+
? await evaluatePredicate(session, until, since, false, baselines)
|
|
23
|
+
: undefined;
|
|
24
|
+
const alreadyTrue = precheck?.pass ?? false;
|
|
25
|
+
if (!alreadyTrue || until === undefined) {
|
|
26
|
+
return { alreadyTrue: false, alreadyTrueHiddenMatch: false, baselines };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
alreadyTrue: true,
|
|
30
|
+
baselines,
|
|
31
|
+
// The pre-check evidence is already in hand, so asking whether the match was against something
|
|
32
|
+
// hidden costs nothing and lets the message name it (#889).
|
|
33
|
+
alreadyTrueHiddenMatch: alreadyTrueHiddenMatchOf(until, precheck?.evidence),
|
|
34
|
+
...(precheck?.evidence === undefined ? {} : { alreadyTrueEvidence: precheck.evidence }),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `act_and_wait { durable: true }` — after a yes, reload and ask again.
|
|
3
|
+
*
|
|
4
|
+
* Every verdict is scoped to one action's window, so "saved" was proved by the toast that said so,
|
|
5
|
+
* and nothing ever asked whether the thing was still there once the page came back. That is the
|
|
6
|
+
* difference between a UI that advanced and a change that persisted, and it was reachable only by
|
|
7
|
+
* hand: navigate, then a separate assert that nothing tied to the action that caused it.
|
|
8
|
+
*
|
|
9
|
+
* Only the part of `until` that describes the page as it IS can be asked of a fresh document. A
|
|
10
|
+
* request or a signal happened once, in a window that died with the old document; a route is read
|
|
11
|
+
* from route events a reload does not replay. So those clauses are dropped, never re-asserted.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { type Predicate } from '@reticlehq/core';
|
|
15
|
+
import type { decideVerified } from '@reticlehq/engine/evidence/verified.js';
|
|
16
|
+
type VerifiedVerdict = ReturnType<typeof decideVerified>;
|
|
17
|
+
import { evaluatePredicate } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
18
|
+
import type { Session } from '../../../portal/session/session.js';
|
|
19
|
+
export declare const durableArg: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
export declare const durableOutput: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
21
|
+
/** The part of a predicate a fresh document can answer, or undefined when there is none. */
|
|
22
|
+
export declare function durablePart(p: Predicate): Predicate | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* What the re-check found. `held` absent means no answer: the page never came back to be asked, or
|
|
25
|
+
* it came back and could not answer (`inconclusive`).
|
|
26
|
+
*/
|
|
27
|
+
export interface Durability {
|
|
28
|
+
held?: boolean;
|
|
29
|
+
observed?: string;
|
|
30
|
+
skipped?: string;
|
|
31
|
+
inconclusive?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A re-check's predicate result, as durability. Only a page that ANSWERED can say the consequence is
|
|
35
|
+
* gone: an unreadable one (its store not up yet) or one the tab left mid-read is no answer, and
|
|
36
|
+
* reporting either as "did not persist" is a confident no about something nobody saw.
|
|
37
|
+
*/
|
|
38
|
+
export declare function durabilityOf(verdict: {
|
|
39
|
+
pass: boolean;
|
|
40
|
+
observed?: string | undefined;
|
|
41
|
+
inconclusive?: string | undefined;
|
|
42
|
+
observationLost?: boolean | undefined;
|
|
43
|
+
}): Durability;
|
|
44
|
+
/**
|
|
45
|
+
* Apply `durable` to a decided verdict. Pure over `recheck`, which the caller supplies — see
|
|
46
|
+
* reloadAndRecheck for the one that reloads a real page.
|
|
47
|
+
*/
|
|
48
|
+
export declare function withDurability(asked: unknown, until: Predicate, decision: VerifiedVerdict, recheck: (part: Predicate) => Promise<Durability>): Promise<{
|
|
49
|
+
decision: VerifiedVerdict;
|
|
50
|
+
durable?: Durability;
|
|
51
|
+
}>;
|
|
52
|
+
/** Reload the page, wait for it to re-announce itself, and ask `part` of the fresh document. */
|
|
53
|
+
export declare function reloadAndRecheck(deps: {
|
|
54
|
+
sessions: {
|
|
55
|
+
get(id: string): Session | undefined;
|
|
56
|
+
};
|
|
57
|
+
now: () => number;
|
|
58
|
+
}, session: Session, part: Predicate, timeoutMs: number): Promise<Durability>;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the page-state part of a consequence that held has since gone — an optimistic UI that
|
|
61
|
+
* rolled back. Returns the sentence to fail with, or undefined when it still holds.
|
|
62
|
+
*
|
|
63
|
+
* The wait resolves the instant a predicate holds, and the in-flight wait after it only follows a
|
|
64
|
+
* request to its response. A row that appeared and was removed a tick later, with no failing
|
|
65
|
+
* request to contradict it, was a yes. Only element/text/state clauses are re-read: a request or a
|
|
66
|
+
* signal does not un-happen, and re-reading `settled` on a page that polls would accuse it falsely.
|
|
67
|
+
*/
|
|
68
|
+
export declare function revertedAfterMatch(session: Parameters<typeof evaluatePredicate>[0], until: Predicate, since: number, baselines: Parameters<typeof evaluatePredicate>[4]): Promise<string | undefined>;
|
|
69
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `act_and_wait { durable: true }` — after a yes, reload and ask again.
|
|
3
|
+
*
|
|
4
|
+
* Every verdict is scoped to one action's window, so "saved" was proved by the toast that said so,
|
|
5
|
+
* and nothing ever asked whether the thing was still there once the page came back. That is the
|
|
6
|
+
* difference between a UI that advanced and a change that persisted, and it was reachable only by
|
|
7
|
+
* hand: navigate, then a separate assert that nothing tied to the action that caused it.
|
|
8
|
+
*
|
|
9
|
+
* Only the part of `until` that describes the page as it IS can be asked of a fresh document. A
|
|
10
|
+
* request or a signal happened once, in a window that died with the old document; a route is read
|
|
11
|
+
* from route events a reload does not replay. So those clauses are dropped, never re-asserted.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { PredicateKind, ReticleCommand, Verified, VerifiedReason, } from '@reticlehq/core';
|
|
15
|
+
import { evaluatePredicate, waitForPredicate, } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
16
|
+
import { RELOAD_RECONNECT_TIMEOUT_MS, waitForReconnect, } from '../../../portal/session/session-reconnect.js';
|
|
17
|
+
export const durableArg = z
|
|
18
|
+
.boolean()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('After a yes, reload and require the element/text/state part of `until` to hold again.');
|
|
21
|
+
// Loose and undescribed on purpose: the output surface is re-sent every turn. `because` carries
|
|
22
|
+
// the answer; this is the detail — { held?, observed?, skipped? }.
|
|
23
|
+
export const durableOutput = z.record(z.unknown()).optional();
|
|
24
|
+
const RE_READABLE = new Set([
|
|
25
|
+
PredicateKind.ELEMENT,
|
|
26
|
+
PredicateKind.TEXT,
|
|
27
|
+
PredicateKind.STATE,
|
|
28
|
+
]);
|
|
29
|
+
/** The part of a predicate a fresh document can answer, or undefined when there is none. */
|
|
30
|
+
export function durablePart(p) {
|
|
31
|
+
if (PredicateKind.ALL_OF === p.kind) {
|
|
32
|
+
const kept = p.predicates.map(durablePart).filter((c) => c !== undefined);
|
|
33
|
+
if (0 === kept.length)
|
|
34
|
+
return undefined;
|
|
35
|
+
return 1 === kept.length ? kept[0] : { kind: PredicateKind.ALL_OF, predicates: kept };
|
|
36
|
+
}
|
|
37
|
+
// Narrowing an OR to the branches that survive would change the claim, not re-check it.
|
|
38
|
+
if (PredicateKind.ANY_OF === p.kind)
|
|
39
|
+
return p.predicates.every((c) => durablePart(c) === c) ? p : undefined;
|
|
40
|
+
if (PredicateKind.NOT === p.kind)
|
|
41
|
+
return durablePart(p.predicate) === p.predicate ? p : undefined;
|
|
42
|
+
return RE_READABLE.has(p.kind) ? p : undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A re-check's predicate result, as durability. Only a page that ANSWERED can say the consequence is
|
|
46
|
+
* gone: an unreadable one (its store not up yet) or one the tab left mid-read is no answer, and
|
|
47
|
+
* reporting either as "did not persist" is a confident no about something nobody saw.
|
|
48
|
+
*/
|
|
49
|
+
export function durabilityOf(verdict) {
|
|
50
|
+
const observed = verdict.observed === undefined ? {} : { observed: verdict.observed };
|
|
51
|
+
if (true === verdict.pass)
|
|
52
|
+
return { held: true, ...observed };
|
|
53
|
+
if (verdict.inconclusive !== undefined)
|
|
54
|
+
return { inconclusive: verdict.inconclusive, ...observed };
|
|
55
|
+
if (true === verdict.observationLost)
|
|
56
|
+
return observed;
|
|
57
|
+
return { held: false, ...observed };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Apply `durable` to a decided verdict. Pure over `recheck`, which the caller supplies — see
|
|
61
|
+
* reloadAndRecheck for the one that reloads a real page.
|
|
62
|
+
*/
|
|
63
|
+
export async function withDurability(asked, until, decision, recheck) {
|
|
64
|
+
if (true !== asked || Verified.YES !== decision.verified)
|
|
65
|
+
return { decision };
|
|
66
|
+
const part = durablePart(until);
|
|
67
|
+
if (part === undefined) {
|
|
68
|
+
const skipped = 'nothing declared describes the page itself — a request or a signal happens once and cannot be seen again after a reload; add an element, text or state clause';
|
|
69
|
+
return { decision, durable: { skipped } };
|
|
70
|
+
}
|
|
71
|
+
const durable = await recheck(part);
|
|
72
|
+
if (true === durable.held)
|
|
73
|
+
return { decision, durable };
|
|
74
|
+
if (durable.inconclusive !== undefined) {
|
|
75
|
+
return {
|
|
76
|
+
durable,
|
|
77
|
+
decision: {
|
|
78
|
+
verified: Verified.UNKNOWN,
|
|
79
|
+
verifiedReason: VerifiedReason.INCONCLUSIVE,
|
|
80
|
+
because: `the consequence held, but the reloaded page could not answer whether it still does (${durable.inconclusive})`,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (durable.held === undefined) {
|
|
85
|
+
return {
|
|
86
|
+
durable,
|
|
87
|
+
decision: {
|
|
88
|
+
verified: Verified.UNKNOWN,
|
|
89
|
+
verifiedReason: VerifiedReason.OBSERVATION_LOST,
|
|
90
|
+
because: `the consequence held, but the page did not come back after the reload to be asked again${durable.observed === undefined ? '' : ` (${durable.observed})`}`,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
durable,
|
|
96
|
+
decision: {
|
|
97
|
+
verified: Verified.NO,
|
|
98
|
+
verifiedReason: VerifiedReason.ASSERTION_FAILED,
|
|
99
|
+
because: `the consequence held, then was gone after a reload — it did not persist${durable.observed === undefined ? '' : ` (observed ${durable.observed})`}`,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Reload the page, wait for it to re-announce itself, and ask `part` of the fresh document. */
|
|
104
|
+
export async function reloadAndRecheck(deps, session, part, timeoutMs) {
|
|
105
|
+
// Same floor navigate { reload } sets: nothing from the old document may answer for the new one.
|
|
106
|
+
session.lastAct.markNavigated(session.elapsed());
|
|
107
|
+
const sent = await session.command(ReticleCommand.REFRESH, { hard: false });
|
|
108
|
+
if (!sent.ok)
|
|
109
|
+
return { observed: sent.error ?? 'the reload was refused' };
|
|
110
|
+
const back = await waitForReconnect({
|
|
111
|
+
current: () => deps.sessions.get(session.id),
|
|
112
|
+
previous: session,
|
|
113
|
+
timeoutMs: Math.max(timeoutMs, RELOAD_RECONNECT_TIMEOUT_MS),
|
|
114
|
+
now: deps.now,
|
|
115
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
116
|
+
});
|
|
117
|
+
const fresh = back ? deps.sessions.get(session.id) : undefined;
|
|
118
|
+
if (fresh === undefined)
|
|
119
|
+
return {};
|
|
120
|
+
return durabilityOf(await waitForPredicate(fresh, part, timeoutMs, 0));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Whether the page-state part of a consequence that held has since gone — an optimistic UI that
|
|
124
|
+
* rolled back. Returns the sentence to fail with, or undefined when it still holds.
|
|
125
|
+
*
|
|
126
|
+
* The wait resolves the instant a predicate holds, and the in-flight wait after it only follows a
|
|
127
|
+
* request to its response. A row that appeared and was removed a tick later, with no failing
|
|
128
|
+
* request to contradict it, was a yes. Only element/text/state clauses are re-read: a request or a
|
|
129
|
+
* signal does not un-happen, and re-reading `settled` on a page that polls would accuse it falsely.
|
|
130
|
+
*/
|
|
131
|
+
export async function revertedAfterMatch(session, until, since, baselines) {
|
|
132
|
+
const part = durablePart(until);
|
|
133
|
+
if (part === undefined)
|
|
134
|
+
return undefined;
|
|
135
|
+
const now = await evaluatePredicate(session, part, since, true, baselines);
|
|
136
|
+
if (true === now.pass || now.inconclusive !== undefined)
|
|
137
|
+
return undefined;
|
|
138
|
+
return `held, then reverted before the action finished${now.observed === undefined ? '' : ` — now ${now.observed}`}`;
|
|
139
|
+
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* would make the daemon wait. `assert` / `wait_for` / `act_and_wait` all spend the caller's
|
|
18
18
|
* `timeout_ms` (resolve-within.ts); this is the same budget applied to the same wait.
|
|
19
19
|
*/
|
|
20
|
+
import type { Session } from '../../../../portal/session/session.js';
|
|
20
21
|
import type { SessionManager } from '../../../../portal/session/session-manager.js';
|
|
21
22
|
import type { NavigateArrival } from './navigate-result.js';
|
|
22
23
|
/**
|
|
@@ -24,6 +25,12 @@ import type { NavigateArrival } from './navigate-result.js';
|
|
|
24
25
|
* and the SDK to dial back; short enough to not hang. A caller who knows the app is slower says so.
|
|
25
26
|
*/
|
|
26
27
|
export declare const ARRIVAL_TIMEOUT_MS = 5000;
|
|
28
|
+
/**
|
|
29
|
+
* The navigation target as an absolute URL, resolved against the tab being navigated — the same
|
|
30
|
+
* resolution the browser applies. An agent passes a path (`/orders?id=4`) as often as a full URL,
|
|
31
|
+
* and `samePage` cannot parse a path, so without this a relative navigation could never confirm.
|
|
32
|
+
*/
|
|
33
|
+
export declare function absoluteTarget(target: string, base: string): string;
|
|
27
34
|
/**
|
|
28
35
|
* Which session an arrival may be attributed to.
|
|
29
36
|
*
|
|
@@ -39,14 +46,20 @@ export declare const ARRIVAL_TIMEOUT_MS = 5000;
|
|
|
39
46
|
*/
|
|
40
47
|
export interface ArrivalScope {
|
|
41
48
|
/**
|
|
42
|
-
* The
|
|
49
|
+
* The actual Session object that received the navigation command.
|
|
50
|
+
*
|
|
51
|
+
* A full-document navigation can destroy this session and reconnect the new document
|
|
52
|
+
* with the same id or a different id, so successor detection needs the original object.
|
|
53
|
+
*/
|
|
54
|
+
navigatedSession: Session;
|
|
55
|
+
/**
|
|
56
|
+
* The URL the session was on before navigation started.
|
|
43
57
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* report `confirmed: false` for the navigations most likely to have worked.
|
|
58
|
+
* This lets us distinguish "the navigation is still in progress" from
|
|
59
|
+
* "the app redirected the newly arrived document somewhere else."
|
|
47
60
|
*/
|
|
48
|
-
|
|
49
|
-
/** Sessions already on the target
|
|
61
|
+
navigatedFrom: string;
|
|
62
|
+
/** Sessions already on the target before dispatch cannot prove this navigation arrived. */
|
|
50
63
|
priorIds: ReadonlySet<string>;
|
|
51
64
|
}
|
|
52
65
|
/**
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* would make the daemon wait. `assert` / `wait_for` / `act_and_wait` all spend the caller's
|
|
18
18
|
* `timeout_ms` (resolve-within.ts); this is the same budget applied to the same wait.
|
|
19
19
|
*/
|
|
20
|
+
import { awaitDocumentSuccessor } from '../../../../portal/session/session-successor.js';
|
|
20
21
|
/**
|
|
21
22
|
* The default when the caller gives no `timeout_ms`: long enough for a dev server to serve a page
|
|
22
23
|
* and the SDK to dial back; short enough to not hang. A caller who knows the app is slower says so.
|
|
@@ -41,6 +42,19 @@ function samePage(a, b) {
|
|
|
41
42
|
return false;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The navigation target as an absolute URL, resolved against the tab being navigated — the same
|
|
47
|
+
* resolution the browser applies. An agent passes a path (`/orders?id=4`) as often as a full URL,
|
|
48
|
+
* and `samePage` cannot parse a path, so without this a relative navigation could never confirm.
|
|
49
|
+
*/
|
|
50
|
+
export function absoluteTarget(target, base) {
|
|
51
|
+
try {
|
|
52
|
+
return new URL(target, base).href;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return target;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
44
58
|
/**
|
|
45
59
|
* The sessions already on `target` — the set `ArrivalScope.priorIds` is built from, sampled before
|
|
46
60
|
* the navigation is dispatched.
|
|
@@ -56,19 +70,27 @@ export function idsAtTarget(sessions, target) {
|
|
|
56
70
|
}
|
|
57
71
|
return ids;
|
|
58
72
|
}
|
|
59
|
-
function findArrival(sessions, target, scope) {
|
|
73
|
+
function findArrival(sessions, target, scope, currentSession) {
|
|
60
74
|
let arrived = null;
|
|
61
75
|
for (const s of sessions.all()) {
|
|
62
|
-
|
|
76
|
+
// The currently tracked document gets priority because it is the strongest
|
|
77
|
+
// evidence of where this navigation went.
|
|
78
|
+
if (s.id === currentSession.id) {
|
|
79
|
+
if (samePage(s.url, target)) {
|
|
80
|
+
return { sessionId: s.id };
|
|
81
|
+
}
|
|
82
|
+
// Intermediate routes are not necessarily redirects. Keep polling so
|
|
83
|
+
// the document has a chance to reach the requested target.
|
|
63
84
|
continue;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
}
|
|
86
|
+
// A different session that was already at the target cannot prove this
|
|
87
|
+
// navigation arrived.
|
|
67
88
|
if (scope.priorIds.has(s.id))
|
|
68
89
|
continue;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
if (samePage(s.url, target)) {
|
|
91
|
+
if (null === arrived)
|
|
92
|
+
arrived = { sessionId: s.id };
|
|
93
|
+
}
|
|
72
94
|
}
|
|
73
95
|
return arrived;
|
|
74
96
|
}
|
|
@@ -83,11 +105,39 @@ const REAL_CLOCK = {
|
|
|
83
105
|
export async function awaitArrival(sessions, target, scope, timeoutMs = ARRIVAL_TIMEOUT_MS, clock = REAL_CLOCK) {
|
|
84
106
|
const deadline = clock.now() + timeoutMs;
|
|
85
107
|
for (;;) {
|
|
86
|
-
|
|
108
|
+
// Keep checking the live session registry for the entire caller-provided
|
|
109
|
+
// budget. This is important because a document can disconnect and reconnect
|
|
110
|
+
// under another id, and the new session may be at the requested target,
|
|
111
|
+
// including on a different origin.
|
|
112
|
+
const found = findArrival(sessions, target, scope, scope.navigatedSession);
|
|
87
113
|
if (found !== null)
|
|
88
114
|
return found;
|
|
89
115
|
if (clock.now() >= deadline)
|
|
90
|
-
|
|
116
|
+
break;
|
|
91
117
|
await clock.sleep(POLL_MS);
|
|
92
118
|
}
|
|
119
|
+
// Only after the navigation wait has expired do we use successor detection.
|
|
120
|
+
// At this point it is no longer being used to decide whether the requested
|
|
121
|
+
// navigation arrived. It only helps explain where the replaced document
|
|
122
|
+
// landed when the target was never observed.
|
|
123
|
+
const current = sessions.get(scope.navigatedSession.id);
|
|
124
|
+
if (current !== undefined) {
|
|
125
|
+
if (!samePage(current.url, scope.navigatedFrom) && !samePage(current.url, target)) {
|
|
126
|
+
return {
|
|
127
|
+
sessionId: current.id,
|
|
128
|
+
landedOn: current.url,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const successor = await awaitDocumentSuccessor(sessions, scope.navigatedSession, 0, clock);
|
|
134
|
+
if (successor !== null &&
|
|
135
|
+
!samePage(successor.url, target) &&
|
|
136
|
+
!samePage(successor.url, scope.navigatedFrom)) {
|
|
137
|
+
return {
|
|
138
|
+
sessionId: successor.id,
|
|
139
|
+
landedOn: successor.url,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
93
143
|
}
|
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The honest envelope for a navigation nobody can confirm.
|
|
3
|
-
*
|
|
4
|
-
* `window.location.assign(url)` returns before the page moves, and the SDK that would report on the
|
|
5
|
-
* new document is destroyed by the navigation itself. So a successful NAVIGATE means the browser
|
|
6
|
-
* accepted the instruction — not that anything arrived. Driven against a dead URL it returned
|
|
7
|
-
* `{"ok":true}` while the session died, which is the worst combination available: the agent believes
|
|
8
|
-
* it navigated, is looking at nothing, and has lost Reticle.
|
|
9
|
-
*
|
|
10
|
-
* `reticle_act` already draws this line — `dispatched` (sent) versus `settled` (a frame flushed).
|
|
11
|
-
* This gives navigate the same honesty without changing what it does.
|
|
12
|
-
*/
|
|
13
1
|
/** The session the SDK reconnected as after the page moved, when one was observed in the window. */
|
|
14
2
|
export interface NavigateArrival {
|
|
15
3
|
sessionId: string;
|
|
4
|
+
landedOn?: string;
|
|
16
5
|
}
|
|
17
6
|
export declare function navigateResult(result: {
|
|
18
7
|
ok?: unknown;
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The honest envelope for a navigation nobody can confirm.
|
|
3
|
+
*
|
|
4
|
+
* `window.location.assign(url)` returns before the page moves, and the SDK that would report on the
|
|
5
|
+
* new document is destroyed by the navigation itself. So a successful NAVIGATE means the browser
|
|
6
|
+
* accepted the instruction — not that anything arrived. Driven against a dead URL it returned
|
|
7
|
+
* `{"ok":true}` while the session died, which is the worst combination available: the agent believes
|
|
8
|
+
* it navigated, is looking at nothing, and has lost Reticle.
|
|
9
|
+
*
|
|
10
|
+
* `reticle_act` already draws this line — `dispatched` (sent) versus `settled` (a frame flushed).
|
|
11
|
+
* This gives navigate the same honesty without changing what it does.
|
|
12
|
+
*/
|
|
13
|
+
import { NAVIGATION_REDIRECT_NOTE } from '@reticlehq/core';
|
|
1
14
|
export function navigateResult(result,
|
|
2
15
|
/**
|
|
3
16
|
* Arrival, if the daemon saw the SDK come back. `null` keeps the original honest envelope.
|
|
@@ -26,12 +39,17 @@ waitedMs) {
|
|
|
26
39
|
if (!ok)
|
|
27
40
|
return base;
|
|
28
41
|
if (arrival !== null) {
|
|
42
|
+
const redirected = 'string' === typeof arrival.landedOn;
|
|
29
43
|
return {
|
|
30
44
|
...base,
|
|
31
|
-
confirmed:
|
|
32
|
-
// The SDK that reconnects after a navigation is a NEW session with a new id. Returning it here
|
|
33
|
-
// is the difference between a confirmation and a confirmation the agent can act on.
|
|
45
|
+
confirmed: !redirected,
|
|
34
46
|
sessionId: arrival.sessionId,
|
|
47
|
+
...(redirected
|
|
48
|
+
? {
|
|
49
|
+
landedOn: arrival.landedOn,
|
|
50
|
+
note: `${NAVIGATION_REDIRECT_NOTE} Landed on: ${arrival.landedOn}`,
|
|
51
|
+
}
|
|
52
|
+
: {}),
|
|
35
53
|
};
|
|
36
54
|
}
|
|
37
55
|
return {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remember the control an act_and_wait PROVED, for the coverage ledger's `proved` level.
|
|
3
|
+
*
|
|
4
|
+
* Structural rather than `Session`, and optional, so a test stub that never proves anything need not
|
|
5
|
+
* implement it — the same reason `beginAction` is optional on the crawl session.
|
|
6
|
+
*/
|
|
7
|
+
export declare function noteProved(session: {
|
|
8
|
+
recordProvedFrom?: (actPayload: unknown) => void;
|
|
9
|
+
}, verified: string, actPayload: unknown): void;
|