@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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learn a flow step's source file from the element its anchor resolved to on a replay.
|
|
3
|
+
*
|
|
4
|
+
* A step recorded before the build stamped the app names no file, so a scoped re-verify cannot say
|
|
5
|
+
* which edits it covers. Every replay already asks the page for each anchor's element, and that
|
|
6
|
+
* element carries its stamp now: this records the answer at the one place every resolution passes
|
|
7
|
+
* through (the session's QUERY), rather than threading it through each step runner.
|
|
8
|
+
*/
|
|
9
|
+
import { ReticleCommand, asRecord, asString } from '@reticlehq/core';
|
|
10
|
+
import { anchorQueryArgs } from './flow-step-runners.js';
|
|
11
|
+
/** `src/Pay.tsx:12:4` or `src/Pay.tsx:12` → `{file, line}`; anything else is not a location. */
|
|
12
|
+
export function parseCompactSource(value) {
|
|
13
|
+
const match = /^(.+?):(\d+)(?::\d+)?$/.exec(value);
|
|
14
|
+
const file = match?.[1];
|
|
15
|
+
const line = match?.[2];
|
|
16
|
+
if (file === undefined || line === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
return { file, line: Number(line) };
|
|
19
|
+
}
|
|
20
|
+
function keyOf(args) {
|
|
21
|
+
if (args === undefined)
|
|
22
|
+
return undefined;
|
|
23
|
+
const by = asString(args['by']);
|
|
24
|
+
const value = asString(args['value']);
|
|
25
|
+
if (by === undefined || value === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
return JSON.stringify([by, value, asString(args['name']) ?? null]);
|
|
28
|
+
}
|
|
29
|
+
/** The one element a query found, when exactly one was found and it carries a stamp. */
|
|
30
|
+
function soleSource(result) {
|
|
31
|
+
const elements = asRecord(result)?.['elements'];
|
|
32
|
+
if (!Array.isArray(elements) || elements.length !== 1)
|
|
33
|
+
return undefined;
|
|
34
|
+
const source = asString(asRecord(elements[0])?.['source']);
|
|
35
|
+
return source === undefined ? undefined : parseCompactSource(source);
|
|
36
|
+
}
|
|
37
|
+
/** Wrap a replay session so every anchor QUERY that resolves to one stamped element is remembered. */
|
|
38
|
+
export function recordingSources(inner) {
|
|
39
|
+
const sources = new Map();
|
|
40
|
+
const session = new Proxy(inner, {
|
|
41
|
+
// Bound to the target, never the proxy: a real Session keeps `#private` state, and a method
|
|
42
|
+
// called with the proxy as `this` throws reading it.
|
|
43
|
+
get(target, prop) {
|
|
44
|
+
if (prop !== 'command') {
|
|
45
|
+
const value = Reflect.get(target, prop, target);
|
|
46
|
+
return 'function' === typeof value ? value.bind(target) : value;
|
|
47
|
+
}
|
|
48
|
+
return async (name, args) => {
|
|
49
|
+
const result = await target.command(name, args);
|
|
50
|
+
const key = name === ReticleCommand.QUERY ? keyOf(args) : undefined;
|
|
51
|
+
const source = result.ok ? soleSource(result.result) : undefined;
|
|
52
|
+
if (key !== undefined && source !== undefined)
|
|
53
|
+
sources.set(key, source);
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
return { session, sources };
|
|
59
|
+
}
|
|
60
|
+
function learnSteps(steps, sources) {
|
|
61
|
+
let changed = false;
|
|
62
|
+
const next = steps.map((step) => {
|
|
63
|
+
const nested = step.steps === undefined ? undefined : learnSteps(step.steps, sources);
|
|
64
|
+
const learned = step.source === undefined
|
|
65
|
+
? sources.get(keyOf(anchorQueryArgs(step.anchor) ?? undefined) ?? '')
|
|
66
|
+
: undefined;
|
|
67
|
+
if (learned === undefined && nested?.changed !== true)
|
|
68
|
+
return step;
|
|
69
|
+
changed = true;
|
|
70
|
+
return {
|
|
71
|
+
...step,
|
|
72
|
+
...(learned === undefined ? {} : { source: learned }),
|
|
73
|
+
...(nested === undefined ? {} : { steps: nested.steps }),
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
return { steps: next, changed };
|
|
77
|
+
}
|
|
78
|
+
/** The flow with every sourceless step given the file its anchor resolved to; undefined if none was. */
|
|
79
|
+
export function withLearnedSources(flow, sources) {
|
|
80
|
+
if (0 === sources.size)
|
|
81
|
+
return undefined;
|
|
82
|
+
const { steps, changed } = learnSteps(flow.steps, sources);
|
|
83
|
+
return changed ? { ...flow, steps } : undefined;
|
|
84
|
+
}
|
|
@@ -7,13 +7,6 @@
|
|
|
7
7
|
* stop/save can warn instead of writing a flow that fails on the next click.
|
|
8
8
|
*/
|
|
9
9
|
import { type ReticleEvent } from '@reticlehq/core';
|
|
10
|
-
/**
|
|
11
|
-
* The page a recording step sat on, comparable across path routers and hash routers.
|
|
12
|
-
*
|
|
13
|
-
* A startPath is stored as pathname+hash (`/#/search`); a ROUTE_CHANGE carries them separately.
|
|
14
|
-
* Without this, every hash-router page compares as `/` and a backtrack is invisible.
|
|
15
|
-
*/
|
|
16
|
-
export declare function recordingRouteOf(pathname: string, hash?: string): string;
|
|
17
10
|
/**
|
|
18
11
|
* Warn when the journey left a page and came back. Linear A→B→C is silent: that still cannot
|
|
19
12
|
* replay either if a later click needs a previous page, but the reported failure is specifically
|
|
@@ -17,7 +17,7 @@ function asString(value) {
|
|
|
17
17
|
* A startPath is stored as pathname+hash (`/#/search`); a ROUTE_CHANGE carries them separately.
|
|
18
18
|
* Without this, every hash-router page compares as `/` and a backtrack is invisible.
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
function recordingRouteOf(pathname, hash = '') {
|
|
21
21
|
const hashIdx = pathname.indexOf('#/');
|
|
22
22
|
if (hashIdx >= 0) {
|
|
23
23
|
return routePathOf(pathname.slice(0, hashIdx) || '/', pathname.slice(hashIdx));
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { FlowStepResult } from '@reticlehq/core';
|
|
14
14
|
/** Set on a replay that stopped early; omitted entirely otherwise, so a clean pass stays flat. */
|
|
15
|
-
|
|
15
|
+
interface ReplayHalt {
|
|
16
16
|
/** Index of the step that stopped the run. */
|
|
17
17
|
atStep: number;
|
|
18
18
|
/** How many of the flow's steps were never attempted because of it. */
|
|
@@ -24,3 +24,4 @@ export interface ReplayHalt {
|
|
|
24
24
|
* defect restated as its own fix.
|
|
25
25
|
*/
|
|
26
26
|
export declare function haltedFrom(results: readonly FlowStepResult[], total: number): ReplayHalt | undefined;
|
|
27
|
+
export {};
|
|
@@ -21,7 +21,9 @@ export function haltedFrom(results, total) {
|
|
|
21
21
|
return undefined;
|
|
22
22
|
if (last.ok && last.drift === undefined)
|
|
23
23
|
return undefined;
|
|
24
|
-
|
|
24
|
+
// From the step's own index, not the count of results: a resumed replay does not report its setup
|
|
25
|
+
// prefix, so the results array no longer starts at step 0.
|
|
26
|
+
const notAttempted = total - (last.step + 1);
|
|
25
27
|
// A failure on the FINAL step is not a halt: nothing was left to attempt, and `notAttempted: 0`
|
|
26
28
|
// would send a reader looking for steps that do not exist.
|
|
27
29
|
if (notAttempted <= 0)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type FlowFile, type Predicate } from '@reticlehq/core';
|
|
2
2
|
/** One captured agent action, normalized for replay. */
|
|
3
3
|
export interface RecordedStep {
|
|
4
4
|
/** ReticleTool.ACT | ReticleTool.ACT_SEQUENCE */
|
|
@@ -8,7 +8,7 @@ export interface RecordedStep {
|
|
|
8
8
|
/** false if any ref could not be normalized to a testid (replay only valid in-session). */
|
|
9
9
|
stable: boolean;
|
|
10
10
|
/** Optional post-condition annotation carried into the on-disk flow's expect. */
|
|
11
|
-
expect?:
|
|
11
|
+
expect?: Predicate;
|
|
12
12
|
/**
|
|
13
13
|
* The route this step ran on. RECORDER-INTERNAL: it never reaches the on-disk flow.
|
|
14
14
|
*
|
|
@@ -27,6 +27,12 @@ export interface RecordedStep {
|
|
|
27
27
|
* `invoke` first and treats the step as a call.
|
|
28
28
|
*/
|
|
29
29
|
invoke?: string;
|
|
30
|
+
/** The page this step ran on. Written to the saved step (unlike `route`, which cuts journeys). */
|
|
31
|
+
page?: string;
|
|
32
|
+
/** The page it ended on once it settled — see `markEnded`. */
|
|
33
|
+
endPage?: string;
|
|
34
|
+
/** Why the agent took this step: the `intent` it declared on the action. Names the saved flow. */
|
|
35
|
+
intent?: string;
|
|
30
36
|
}
|
|
31
37
|
interface ActiveRecording {
|
|
32
38
|
cursor: number;
|
|
@@ -59,6 +65,8 @@ export interface CompiledProgram {
|
|
|
59
65
|
* format change.
|
|
60
66
|
*/
|
|
61
67
|
routes?: string[];
|
|
68
|
+
/** Who made it — see FlowFile.author. Stamped by the caller that knows. */
|
|
69
|
+
author?: FlowFile['author'];
|
|
62
70
|
}
|
|
63
71
|
/**
|
|
64
72
|
* The `tool` written on an invocation step — core's constant, re-exported for this module's callers.
|
|
@@ -105,6 +113,27 @@ export declare class RecordingStore {
|
|
|
105
113
|
* anything should not carry an empty tape, and "did anything happen at all" stays answerable.
|
|
106
114
|
*/
|
|
107
115
|
capture(step: RecordedStep, route?: string): void;
|
|
116
|
+
/**
|
|
117
|
+
* Add a consequence to the last captured step of every active recording.
|
|
118
|
+
*
|
|
119
|
+
* A separate `reticle_assert` after an act is how an agent proves what the act did, and it never
|
|
120
|
+
* reached the recorder — only the act tools called `capture` — so the flow kept the click and lost
|
|
121
|
+
* the proof. What the step already declared is kept; the new check joins it under `allOf`.
|
|
122
|
+
*/
|
|
123
|
+
attachExpect(expect: Predicate): void;
|
|
124
|
+
/**
|
|
125
|
+
* A navigation happened. Navigating records no step, so until the next one is captured, a check
|
|
126
|
+
* proved now is about the page the navigation opened, not about the last step — and kept on that
|
|
127
|
+
* step, replay would ask it right after the click, of a page it was never true on.
|
|
128
|
+
*/
|
|
129
|
+
markNavigated(): void;
|
|
130
|
+
/**
|
|
131
|
+
* The page the step just captured ended on, once its action settled.
|
|
132
|
+
*
|
|
133
|
+
* Fills only a step that has none, so calling it after an action that captured nothing leaves the
|
|
134
|
+
* previous step's own answer alone.
|
|
135
|
+
*/
|
|
136
|
+
markEnded(page: string | undefined): void;
|
|
108
137
|
/**
|
|
109
138
|
* Returns the active recording (cursor + steps) and clears it, or undefined if not recording.
|
|
110
139
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FlowStepTool } from '@reticlehq/core';
|
|
1
|
+
import { FlowStepTool, PredicateKind } from '@reticlehq/core';
|
|
2
2
|
/**
|
|
3
3
|
* The `tool` written on an invocation step — core's constant, re-exported for this module's callers.
|
|
4
4
|
*
|
|
@@ -31,6 +31,8 @@ const AMBIENT_STEP_CAP = 400;
|
|
|
31
31
|
export class RecordingStore {
|
|
32
32
|
#active = new Map();
|
|
33
33
|
#compiled = new Map();
|
|
34
|
+
/** A navigation came after the last captured step — see markNavigated. */
|
|
35
|
+
#navigatedSinceStep = false;
|
|
34
36
|
start(name, cursor, startPath) {
|
|
35
37
|
const openedOver = new Map();
|
|
36
38
|
for (const [outer, rec] of this.#active)
|
|
@@ -67,6 +69,7 @@ export class RecordingStore {
|
|
|
67
69
|
* anything should not carry an empty tape, and "did anything happen at all" stays answerable.
|
|
68
70
|
*/
|
|
69
71
|
capture(step, route) {
|
|
72
|
+
this.#navigatedSinceStep = false;
|
|
70
73
|
if (!this.#active.has(AMBIENT_RECORDING)) {
|
|
71
74
|
this.#active.set(AMBIENT_RECORDING, {
|
|
72
75
|
cursor: 0,
|
|
@@ -83,12 +86,63 @@ export class RecordingStore {
|
|
|
83
86
|
// opened on purpose is not capped — they said when it starts and they say when it stops.
|
|
84
87
|
if (AMBIENT_RECORDING === name && rec.steps.length >= AMBIENT_STEP_CAP)
|
|
85
88
|
continue;
|
|
89
|
+
// A step nobody marked ended where the next one began.
|
|
90
|
+
const previous = rec.steps.at(-1);
|
|
91
|
+
if (previous !== undefined && previous.endPage === undefined && step.page !== undefined) {
|
|
92
|
+
previous.endPage = step.page;
|
|
93
|
+
}
|
|
86
94
|
// The route rides on the AMBIENT tape only: a recording somebody opened deliberately is
|
|
87
95
|
// already one journey by construction, and stamping a route on its steps would change what a
|
|
88
96
|
// deliberate recording contains.
|
|
89
97
|
rec.steps.push(AMBIENT_RECORDING === name && route !== undefined ? { ...step, route } : step);
|
|
90
98
|
}
|
|
91
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Add a consequence to the last captured step of every active recording.
|
|
102
|
+
*
|
|
103
|
+
* A separate `reticle_assert` after an act is how an agent proves what the act did, and it never
|
|
104
|
+
* reached the recorder — only the act tools called `capture` — so the flow kept the click and lost
|
|
105
|
+
* the proof. What the step already declared is kept; the new check joins it under `allOf`.
|
|
106
|
+
*/
|
|
107
|
+
attachExpect(expect) {
|
|
108
|
+
if (this.#navigatedSinceStep)
|
|
109
|
+
return;
|
|
110
|
+
for (const rec of this.#active.values()) {
|
|
111
|
+
const last = rec.steps.at(-1);
|
|
112
|
+
if (last === undefined)
|
|
113
|
+
continue;
|
|
114
|
+
const held = last.expect;
|
|
115
|
+
last.expect =
|
|
116
|
+
held === undefined
|
|
117
|
+
? expect
|
|
118
|
+
: PredicateKind.ALL_OF === held.kind
|
|
119
|
+
? { ...held, predicates: [...held.predicates, expect] }
|
|
120
|
+
: { kind: PredicateKind.ALL_OF, predicates: [held, expect] };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A navigation happened. Navigating records no step, so until the next one is captured, a check
|
|
125
|
+
* proved now is about the page the navigation opened, not about the last step — and kept on that
|
|
126
|
+
* step, replay would ask it right after the click, of a page it was never true on.
|
|
127
|
+
*/
|
|
128
|
+
markNavigated() {
|
|
129
|
+
this.#navigatedSinceStep = true;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The page the step just captured ended on, once its action settled.
|
|
133
|
+
*
|
|
134
|
+
* Fills only a step that has none, so calling it after an action that captured nothing leaves the
|
|
135
|
+
* previous step's own answer alone.
|
|
136
|
+
*/
|
|
137
|
+
markEnded(page) {
|
|
138
|
+
if (page === undefined)
|
|
139
|
+
return;
|
|
140
|
+
for (const rec of this.#active.values()) {
|
|
141
|
+
const last = rec.steps.at(-1);
|
|
142
|
+
if (last !== undefined && last.endPage === undefined)
|
|
143
|
+
last.endPage = page;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
92
146
|
/**
|
|
93
147
|
* Returns the active recording (cursor + steps) and clears it, or undefined if not recording.
|
|
94
148
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Predicate } from '@reticlehq/core';
|
|
1
2
|
import { type CommandResult } from '@reticlehq/core';
|
|
2
3
|
import type { RecordedStep, CompiledProgram } from './recording/tape/recordings.js';
|
|
3
4
|
import type { Session } from '../../portal/session/session.js';
|
|
@@ -27,6 +28,13 @@ export declare function queryRefs(result: CommandResult): string[];
|
|
|
27
28
|
* an empty box fails identically and tells the reader nothing.
|
|
28
29
|
*/
|
|
29
30
|
export declare function replayActionArgs(value: unknown, confirmDangerous?: boolean, field?: string): Record<string, unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* The pathname of a session's live url, or undefined when it has none yet.
|
|
33
|
+
*
|
|
34
|
+
* Pathname rather than the whole url, because that is what `startPath` is compared against and what
|
|
35
|
+
* a hash-routed app makes meaningless in the document location — see routeOfEvent.
|
|
36
|
+
*/
|
|
37
|
+
export declare function pathOf(url: string | undefined): string | undefined;
|
|
30
38
|
/**
|
|
31
39
|
* Capture an act into every in-flight recording, or do nothing when none is running.
|
|
32
40
|
*
|
|
@@ -40,6 +48,17 @@ export declare function captureAct(recordings: {
|
|
|
40
48
|
}, args: Record<string, unknown>, res: unknown,
|
|
41
49
|
/** Where this step ran, so the ambient tape can be cut into journeys. See RecordedStep.route. */
|
|
42
50
|
route?: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Capture an act_and_wait once its verdict is known, keeping `until` as the step's expectation only
|
|
53
|
+
* on a yes. Recorded before the verdict, a step kept an `until` that came back `no` or `no-fault` —
|
|
54
|
+
* a regression test asserting something never once observed to hold because of the action. The
|
|
55
|
+
* action itself is always recorded: it happened, and replay needs it to reach the next step.
|
|
56
|
+
*/
|
|
57
|
+
export declare function captureVerdictedAct(recordings: Parameters<typeof captureAct>[0], args: Record<string, unknown>, res: unknown, route: string | undefined, verified: string | undefined): void;
|
|
58
|
+
/** Fold a PASSING standalone assertion into the step it proved. See RecordingStore.attachExpect. */
|
|
59
|
+
export declare function captureAssertion(recordings: {
|
|
60
|
+
attachExpect: (expect: Predicate) => void;
|
|
61
|
+
}, raw: unknown): void;
|
|
43
62
|
/**
|
|
44
63
|
* Compile a single reticle_act invocation into a normalized RecordedStep using the action result. Anchor
|
|
45
64
|
* priority mirrors synthesizeAnchor: a data-testid is the gold standard; failing that, the element's
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { REDACTED_FILL, secretEnvKey } from './fields/flow-secret-field.js';
|
|
2
|
+
import { FlowPredicateSchema, Verified } from '@reticlehq/core';
|
|
2
3
|
import { DANGEROUS_ACTION_CONFIRM_ARG, ReticleCommand, QueryBy, } from '@reticlehq/core';
|
|
3
4
|
import { ReticleTool } from '@reticlehq/core';
|
|
4
5
|
import { formatStepAddress } from 'open-verification';
|
|
5
|
-
import { predicateToExpect, enforcedOnReplay } from '../../judgement/outcome/predicate-to-expect.js';
|
|
6
|
-
import { PredicateSchema } from '@reticlehq/engine/question/predicate/predicate.js';
|
|
7
6
|
import { asRecord, asString } from '@reticlehq/core';
|
|
8
7
|
/**
|
|
9
8
|
* The note attached when a testid resolves to multiple live elements (the first match is used).
|
|
@@ -72,6 +71,22 @@ function sourceFromResult(res) {
|
|
|
72
71
|
out['column'] = source['column'];
|
|
73
72
|
return out;
|
|
74
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* The pathname of a session's live url, or undefined when it has none yet.
|
|
76
|
+
*
|
|
77
|
+
* Pathname rather than the whole url, because that is what `startPath` is compared against and what
|
|
78
|
+
* a hash-routed app makes meaningless in the document location — see routeOfEvent.
|
|
79
|
+
*/
|
|
80
|
+
export function pathOf(url) {
|
|
81
|
+
if (url === undefined)
|
|
82
|
+
return undefined;
|
|
83
|
+
try {
|
|
84
|
+
return new URL(url).pathname;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
75
90
|
/**
|
|
76
91
|
* Capture an act into every in-flight recording, or do nothing when none is running.
|
|
77
92
|
*
|
|
@@ -88,33 +103,58 @@ route) {
|
|
|
88
103
|
// — "recording is a property of the system rather than a rule an agent has to remember" — was true
|
|
89
104
|
// about the intent and false about the code, and every drive nobody wrapped by hand was discarded.
|
|
90
105
|
//
|
|
91
|
-
// That is the whole supply of regression tests this product could have had for free:
|
|
92
|
-
//
|
|
93
|
-
//
|
|
106
|
+
// That is the whole supply of regression tests this product could have had for free: few saved
|
|
107
|
+
// flows are mutation-testable and few steps declare a consequence, and a drive that is never
|
|
108
|
+
// recorded cannot contribute to either.
|
|
94
109
|
const step = compileActStep(args, res);
|
|
95
110
|
// Keep the assertion the agent actually made. `act_and_wait { until }` IS the agent saying what
|
|
96
111
|
// success means — the large majority of calls carry one — and dropping it produced a flow graded
|
|
97
112
|
// "assertion-free: it will pass even if the feature is broken", which is the regression-suite
|
|
98
|
-
// story failing at its last step.
|
|
99
|
-
// predicate-to-expect.ts.
|
|
113
|
+
// story failing at its last step. See enforceableExpect below.
|
|
100
114
|
const expect = enforceableExpect(args['until'] ?? args['predicate']);
|
|
101
115
|
if (expect !== undefined)
|
|
102
116
|
step.expect = expect;
|
|
117
|
+
if (route !== undefined)
|
|
118
|
+
step.page = route;
|
|
119
|
+
const intent = asString(args['intent'])?.trim();
|
|
120
|
+
if (intent !== undefined && intent.length > 0)
|
|
121
|
+
step.intent = intent;
|
|
103
122
|
recordings.capture(step, route);
|
|
104
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Capture an act_and_wait once its verdict is known, keeping `until` as the step's expectation only
|
|
126
|
+
* on a yes. Recorded before the verdict, a step kept an `until` that came back `no` or `no-fault` —
|
|
127
|
+
* a regression test asserting something never once observed to hold because of the action. The
|
|
128
|
+
* action itself is always recorded: it happened, and replay needs it to reach the next step.
|
|
129
|
+
*/
|
|
130
|
+
export function captureVerdictedAct(recordings, args, res, route, verified) {
|
|
131
|
+
const { until: _until, predicate: _predicate, ...unproved } = args;
|
|
132
|
+
captureAct(recordings, Verified.YES === verified ? args : unproved, res, route);
|
|
133
|
+
}
|
|
134
|
+
/** Fold a PASSING standalone assertion into the step it proved. See RecordingStore.attachExpect. */
|
|
135
|
+
export function captureAssertion(recordings, raw) {
|
|
136
|
+
const expect = enforceableExpect(raw);
|
|
137
|
+
if (expect !== undefined)
|
|
138
|
+
recordings.attachExpect(expect);
|
|
139
|
+
}
|
|
105
140
|
/**
|
|
106
141
|
* A declared predicate, as a recorded expectation — or nothing.
|
|
107
142
|
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
143
|
+
* It is now the SAME OBJECT. A step's `expect` is a `Predicate`, so what the agent declared in
|
|
144
|
+
* `until` is what the flow file stores, and there is no conversion left to lose anything in.
|
|
145
|
+
*
|
|
146
|
+
* What was here before was a lossy round trip through a flat struct with one slot per kind. An
|
|
147
|
+
* `allOf[netA, netB]` saved as one of the two with nothing said; anything the struct could not
|
|
148
|
+
* express was dropped, and a filter then removed the kinds replay would not have checked anyway.
|
|
149
|
+
* Every one of those was a claim the agent made and the file did not keep.
|
|
112
150
|
*/
|
|
113
151
|
function enforceableExpect(raw) {
|
|
114
152
|
if (raw === undefined)
|
|
115
153
|
return undefined;
|
|
116
|
-
|
|
117
|
-
|
|
154
|
+
// The flow file's own schema, so capture keeps exactly what a load would accept — a predicate
|
|
155
|
+
// holding a session ref is refused by both, rather than recorded here and rejected on read.
|
|
156
|
+
const parsed = FlowPredicateSchema.safeParse(raw);
|
|
157
|
+
return parsed.success ? parsed.data : undefined;
|
|
118
158
|
}
|
|
119
159
|
/**
|
|
120
160
|
* Compile a single reticle_act invocation into a normalized RecordedStep using the action result. Anchor
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Predicate } from '@reticlehq/core';
|
|
2
2
|
export declare class AnnotationStore {
|
|
3
3
|
#private;
|
|
4
4
|
/** Testids whose CONTENT must not be asserted at replay (mark-dynamic). Copy, never the live array. */
|
|
5
5
|
dynamic(name: string): string[];
|
|
6
6
|
/** The flow's golden end-condition (success-state), or undefined. */
|
|
7
|
-
success(name: string):
|
|
7
|
+
success(name: string): Predicate | undefined;
|
|
8
8
|
/** The flow's declared business goal (intent annotation), or undefined. */
|
|
9
9
|
intent(name: string): string | undefined;
|
|
10
10
|
/** Per-step expect predicates compiled from assert-* annotations, keyed by step index. */
|
|
11
|
-
stepExpect(name: string): Map<number,
|
|
11
|
+
stepExpect(name: string): Map<number, Predicate>;
|
|
12
12
|
/** Append a dynamic testid (mark-dynamic). De-duped so repeated marks stay idempotent. */
|
|
13
13
|
addDynamic(name: string, testid: string): void;
|
|
14
14
|
/** Set the flow's success expectation (success-state); a later set overwrites. */
|
|
15
|
-
setSuccess(name: string, expect:
|
|
15
|
+
setSuccess(name: string, expect: Predicate): void;
|
|
16
16
|
/** Set the flow's declared business goal (intent); a later set overwrites. */
|
|
17
17
|
setIntent(name: string, text: string): void;
|
|
18
18
|
/** Set one step's expect (assert-signal / assert-visible); a later set on the same index wins. */
|
|
19
|
-
setStepExpect(name: string, index: number, expect:
|
|
19
|
+
setStepExpect(name: string, index: number, expect: Predicate): void;
|
|
20
20
|
/** Drop a recording's annotations (called after reticle_flow_save folds them onto disk). */
|
|
21
21
|
clear(name: string): void;
|
|
22
22
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
+
import { writeFileAtomic } from '../../../memory/project/fs/write-atomic.js';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { reticleDirPaths } from '../../../memory/project/dir/reticle-dir.js';
|
|
4
5
|
import { withFileLock } from '../../../memory/project/file-lock.js';
|
|
@@ -77,9 +78,7 @@ export class AssertionTiersStore {
|
|
|
77
78
|
async save(tiers) {
|
|
78
79
|
await this.#fs.mkdir(dirname(this.#path));
|
|
79
80
|
const body = `${JSON.stringify({ version: ASSERTION_TIERS_VERSION, flows: tiers }, null, 2)}\n`;
|
|
80
|
-
|
|
81
|
-
await this.#fs.writeFile(tmp, body);
|
|
82
|
-
await this.#fs.rename(tmp, this.#path);
|
|
81
|
+
await writeFileAtomic(this.#fs, this.#path, body);
|
|
83
82
|
}
|
|
84
83
|
/** Record one flow's assertion shape + covered sources after it PASSED. Never breaks a replay. */
|
|
85
84
|
async recordPassing(name, steps, sources = []) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
+
import { writeFileAtomic } from '../../../memory/project/fs/write-atomic.js';
|
|
2
3
|
import { reticleDirPaths } from '../../../memory/project/dir/reticle-dir.js';
|
|
3
4
|
import { FlakeFileSchema, emptyRecord, isFlaky, recordOutcome, } from '../recording/flake.js';
|
|
4
5
|
const FLAKE_FILE_VERSION = 1;
|
|
@@ -38,9 +39,7 @@ export class FlakeStore {
|
|
|
38
39
|
async save(ledger) {
|
|
39
40
|
await this.#fs.mkdir(dirname(this.#path));
|
|
40
41
|
const body = `${JSON.stringify({ version: FLAKE_FILE_VERSION, flows: ledger }, null, 2)}\n`;
|
|
41
|
-
|
|
42
|
-
await this.#fs.writeFile(tmp, body);
|
|
43
|
-
await this.#fs.rename(tmp, this.#path);
|
|
42
|
+
await writeFileAtomic(this.#fs, this.#path, body);
|
|
44
43
|
}
|
|
45
44
|
/** Accrue one unchanged-code replay outcome for a flow and persist. */
|
|
46
45
|
async record(flow, passed) {
|
|
@@ -24,7 +24,7 @@ interface CoverageDimension {
|
|
|
24
24
|
/** Declared-but-never-exercised members — what "everything" is silently missing. */
|
|
25
25
|
uncovered: string[];
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
interface Coverage {
|
|
28
28
|
testids: CoverageDimension;
|
|
29
29
|
signals: CoverageDimension;
|
|
30
30
|
flows: CoverageDimension;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - one flow throwing never sinks the run — it is captured as that item's outcome, because a suite that
|
|
10
10
|
* aborts on the first crash tells you nothing about the other 199 flows.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
interface Outcome<T> {
|
|
13
13
|
ok: boolean;
|
|
14
14
|
value?: T;
|
|
15
15
|
error?: string;
|
|
@@ -24,3 +24,4 @@ export declare function mapWithConcurrency<I, T>(items: readonly I[], limit: num
|
|
|
24
24
|
* and never below 1. Explicit `requested` (the tool's `parallel` arg) wins when it is a sane number.
|
|
25
25
|
*/
|
|
26
26
|
export declare function resolveConcurrency(flowCount: number, poolCapacity: number, requested?: number): number;
|
|
27
|
+
export {};
|
|
@@ -172,9 +172,12 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
172
172
|
const extra = await inspectAfterReplay(deps, args, cursor);
|
|
173
173
|
if (extra.contradictions.length > 0) {
|
|
174
174
|
const kinds = extra.contradictions.map((c) => c.kind).join(', ');
|
|
175
|
+
const earned = attributedFailures(affected, unknownProvenance).length > 0;
|
|
175
176
|
return {
|
|
176
|
-
verified: Verified.NO,
|
|
177
|
-
because:
|
|
177
|
+
verified: earned ? Verified.NO : Verified.UNKNOWN,
|
|
178
|
+
because: earned
|
|
179
|
+
? `every covering flow passed, but channels disagreed during the replay (${kinds}) — a green suite over a failed write is the false green worth looking at${provenanceNote}`
|
|
180
|
+
: `channels disagreed during the replay (${kinds}), but Reticle cannot tell which sources any of the flows that ran cover (${affected.join(', ')}), so this says nothing about the files you changed. Drive the change directly; the drive is saved as a flow with its sources stamped`,
|
|
178
181
|
changedFiles,
|
|
179
182
|
flowsRun: affected,
|
|
180
183
|
suite,
|
|
@@ -184,6 +187,19 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
184
187
|
measured: extra.measured,
|
|
185
188
|
};
|
|
186
189
|
}
|
|
190
|
+
// The mirror of `earnedNo`: a green from flows that ran only because their sources are unknown
|
|
191
|
+
// says nothing about these files either. At least one attributed flow has to have passed.
|
|
192
|
+
if (0 === attributedFailures(affected, unknownProvenance).length) {
|
|
193
|
+
return {
|
|
194
|
+
verified: Verified.UNKNOWN,
|
|
195
|
+
because: `the flows that ran passed, but Reticle cannot tell which sources any of them cover (${affected.join(', ')}), so this says nothing about the files you changed. Drive the change directly; the drive is saved as a flow with its sources stamped`,
|
|
196
|
+
changedFiles,
|
|
197
|
+
flowsRun: affected,
|
|
198
|
+
suite,
|
|
199
|
+
unknownProvenance,
|
|
200
|
+
measured: extra.measured,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
187
203
|
return {
|
|
188
204
|
verified: Verified.YES,
|
|
189
205
|
because: `all ${String(passed)} flows covering these files passed${provenanceNote}${extra.untouched === undefined ? '' : `, with ${String(extra.untouched.length)} controls left undriven`}`,
|
|
@@ -226,6 +242,7 @@ async function inspectAfterReplay(deps, args, cursor) {
|
|
|
226
242
|
currentDocumentId: session.currentDocumentId,
|
|
227
243
|
currentEditEpoch: session.currentEditEpoch,
|
|
228
244
|
appOrigin: session.url,
|
|
245
|
+
background: session.background,
|
|
229
246
|
// The replay is the action, and the cursor is where it started.
|
|
230
247
|
actionSince: cursor,
|
|
231
248
|
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE place a Reticle-owned Chromium is launched — the pooled lease browser
|
|
3
|
+
* (`playwright-launcher`) and the drive browser (`real-input`) both come through here.
|
|
4
|
+
*
|
|
5
|
+
* Why it is more than `chromium.launch`: on a fresh machine the `playwright` package is present
|
|
6
|
+
* (npx pulled it in with the server) but its browser build is not, because that is a separate
|
|
7
|
+
* ~150 MiB download nobody told the user about. Every lease, `reticle verify`, `open` and `drive`
|
|
8
|
+
* then refused with "Chromium is not installed for Playwright. Run: npx playwright@… install
|
|
9
|
+
* chromium" — the first run stopped on a human step a machine could do. Most machines already have
|
|
10
|
+
* a Chrome or an Edge, and Playwright drives either through `channel`, so that is tried before any
|
|
11
|
+
* download; only a machine with neither gets the pinned install, run for them and announced.
|
|
12
|
+
*
|
|
13
|
+
* Announcements go to STDERR, never stdout: `reticle mcp` speaks JSON-RPC on stdout and one stray
|
|
14
|
+
* line there corrupts the client's stream.
|
|
15
|
+
*/
|
|
16
|
+
import { type ChromiumLaunchOptions } from './chromium-launch-options.js';
|
|
17
|
+
import { type ChromiumProbe } from './command/cli/doctor/browser/chromium-hint.js';
|
|
18
|
+
/** The installed-browser channels Playwright can drive in place of its own build. */
|
|
19
|
+
export declare const ChromiumChannel: {
|
|
20
|
+
readonly CHROME: "chrome";
|
|
21
|
+
readonly MSEDGE: "msedge";
|
|
22
|
+
};
|
|
23
|
+
export type ChromiumChannel = (typeof ChromiumChannel)[keyof typeof ChromiumChannel];
|
|
24
|
+
export declare const CHANNEL_LABEL: Record<ChromiumChannel, string>;
|
|
25
|
+
/** Candidate executable paths for a channel on this platform; empty where it is not installable. */
|
|
26
|
+
export declare function channelExecutableCandidates(channel: ChromiumChannel, platform: NodeJS.Platform, env: NodeJS.ProcessEnv): string[];
|
|
27
|
+
/** The first installed Chrome/Edge on this machine, or undefined. Looks only; installs nothing. */
|
|
28
|
+
export declare function findInstalledChannel(deps: Pick<ChromiumTargetDeps, 'exists' | 'platform' | 'env'>): ChromiumChannel | undefined;
|
|
29
|
+
/** Everything the resolver touches, injected so it runs against a fake machine in tests. */
|
|
30
|
+
export interface ChromiumTargetDeps {
|
|
31
|
+
/** Where the bundled playwright expects its Chromium build. */
|
|
32
|
+
executablePath: () => string;
|
|
33
|
+
exists: (path: string) => boolean;
|
|
34
|
+
platform: NodeJS.Platform;
|
|
35
|
+
env: NodeJS.ProcessEnv;
|
|
36
|
+
/** Run the pinned Chromium install; resolves true when it succeeded. */
|
|
37
|
+
install: () => Promise<boolean>;
|
|
38
|
+
/** A progress line for a human — stderr in production. */
|
|
39
|
+
announce: (line: string) => void;
|
|
40
|
+
}
|
|
41
|
+
/** Which browser to launch: the bundled build (no channel), an installed channel, or none at all. */
|
|
42
|
+
export type ChromiumTarget = {
|
|
43
|
+
found: true;
|
|
44
|
+
channel?: ChromiumChannel;
|
|
45
|
+
} | {
|
|
46
|
+
found: false;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Decide which browser a launch will use, installing the pinned Chromium only as the last resort.
|
|
50
|
+
* Order: bundled build on disk → installed Chrome → installed Edge → install, then bundled.
|
|
51
|
+
*/
|
|
52
|
+
export declare function resolveChromiumTarget(deps: ChromiumTargetDeps): Promise<ChromiumTarget>;
|
|
53
|
+
/** The subset of Playwright's `BrowserType` this file needs, so tests can pass a fake. */
|
|
54
|
+
export interface LaunchableChromium<B> {
|
|
55
|
+
executablePath: () => string;
|
|
56
|
+
launch: (options: ChromiumLaunchOptions) => Promise<B>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A progress writer that says each distinct line once. The Chrome/Edge fallback line was written on
|
|
60
|
+
* every launch, and the pool launches per context, so one daemon repeated the same notice into the
|
|
61
|
+
* terminal of whoever started it.
|
|
62
|
+
*/
|
|
63
|
+
export declare function announceOnce(write: (line: string) => void): (line: string) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Launch a Chromium with the shared options, on whichever browser this machine can offer. When none
|
|
66
|
+
* can be found or installed, launch anyway: Playwright's own error is what `chromiumLaunchHint`
|
|
67
|
+
* turns into the pinned install command, so the user still gets the one line that fixes it.
|
|
68
|
+
*/
|
|
69
|
+
export declare function launchChromium<B>(chromium: LaunchableChromium<B>, headless: boolean, deps?: ChromiumTargetDeps): Promise<B>;
|
|
70
|
+
/**
|
|
71
|
+
* The lease preflight's probe: "can a launch succeed?", which now includes the Chrome/Edge fallback
|
|
72
|
+
* and the automatic install. Without this the preflight would refuse a lease on a machine the
|
|
73
|
+
* launcher itself could serve. Falls back to the plain probe so a real refusal keeps its evidence.
|
|
74
|
+
*/
|
|
75
|
+
export declare function probeLaunchableChromium(): Promise<ChromiumProbe>;
|
|
76
|
+
/**
|
|
77
|
+
* `doctor`'s probe: the plain Chromium probe, plus the installed browser that stands in when the
|
|
78
|
+
* bundled build is missing. Never installs — a diagnostic that downloads 150 MiB is not one.
|
|
79
|
+
*/
|
|
80
|
+
export declare function probeChromiumWithFallback(): Promise<ChromiumProbe>;
|