@reticlehq/server 2.13.1 → 2.14.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/SKILL.md +8 -18
- package/bin/reticle.js +3 -0
- package/dist/bridge/auth-failure-reason.js +12 -1
- package/dist/bridge/bridge-security.d.ts +8 -0
- package/dist/bridge/bridge-security.js +6 -1
- package/dist/bridge/bridge.d.ts +3 -0
- package/dist/bridge/bridge.js +23 -1
- package/dist/bridge/bridge.test-harness.d.ts +10 -1
- package/dist/bridge/bridge.test-harness.js +7 -1
- package/dist/bridge/pairing-token.d.ts +17 -0
- package/dist/bridge/pairing-token.js +26 -0
- package/dist/bridge/token-auth.d.ts +7 -0
- package/dist/bridge/token-auth.js +7 -1
- package/dist/capsule/capsule-store.d.ts +4 -4
- package/dist/capsule/capsule.d.ts +1 -1
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +21 -1
- package/dist/capsule/causal-summary.js +8 -6
- package/dist/capsule/divergence.d.ts +1 -1
- package/dist/capsule/divergence.js +26 -10
- package/dist/chromium-launch-options.d.ts +2 -1
- package/dist/cli/adhoc-verdict.d.ts +58 -0
- package/dist/cli/adhoc-verdict.js +115 -0
- package/dist/cli/chromium-hint.d.ts +18 -4
- package/dist/cli/chromium-hint.js +54 -6
- package/dist/cli/cli-doctor.js +1 -2
- package/dist/cli/cli-flow-commands.d.ts +16 -2
- package/dist/cli/cli-flow-commands.js +28 -9
- package/dist/cli/cli-parse-feedback.js +1 -1
- package/dist/cli/cli-parse.d.ts +2 -1
- package/dist/cli/cli-parse.js +38 -0
- package/dist/cli/cli-port.d.ts +5 -13
- package/dist/cli/cli-port.js +5 -38
- package/dist/cli/cli-update-commands.js +4 -4
- package/dist/cli/cli-verify.d.ts +24 -7
- package/dist/cli/cli-verify.js +87 -13
- package/dist/cli/cloud-keystore.d.ts +2 -1
- package/dist/cli/cloud-login.d.ts +2 -1
- package/dist/cli/cloud-session.d.ts +2 -1
- package/dist/cli/cloud-session.js +1 -1
- package/dist/cli/daemon-start-options.d.ts +2 -1
- package/dist/cli/device-flow.d.ts +2 -2
- package/dist/cli/doctor-daemon-line.d.ts +2 -1
- package/dist/cli/doctor-project-line.d.ts +2 -1
- package/dist/cli/doctor-rows.d.ts +2 -1
- package/dist/cli/drive-attach.d.ts +4 -3
- package/dist/cli/gate-hook-message.d.ts +2 -1
- package/dist/cli/status-next-action.js +10 -11
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +21 -9
- package/dist/cloud/account-state.d.ts +20 -0
- package/dist/cloud/account-state.js +65 -0
- package/dist/cloud/cloud-sync.d.ts +46 -3
- package/dist/cloud/cloud-sync.js +116 -0
- package/dist/cloud/sync-cycle.d.ts +5 -4
- package/dist/cloud/sync-cycle.js +1 -1
- package/dist/cloud/sync-daemon.d.ts +3 -19
- package/dist/cloud/sync-daemon.js +2 -2
- package/dist/crawl/crawl-tools.js +1 -1
- package/dist/crawl/crawl.d.ts +5 -5
- package/dist/crawl/crawl.js +138 -4
- package/dist/daemon/daemon-resilience.d.ts +3 -2
- package/dist/daemon/daemon-resilience.js +43 -4
- package/dist/daemon/daemon-resolve.d.ts +0 -9
- package/dist/daemon/daemon-resolve.js +2 -2
- package/dist/daemon/dev-servers.d.ts +1 -1
- package/dist/daemon/dev-servers.js +1 -1
- package/dist/daemon/loopback-alias.d.ts +2 -1
- package/dist/daemon/startup-failure.d.ts +11 -0
- package/dist/daemon/startup-failure.js +46 -0
- package/dist/events/contradictions.d.ts +10 -16
- package/dist/events/contradictions.js +149 -17
- package/dist/events/declared.d.ts +13 -1
- package/dist/events/declared.js +59 -3
- package/dist/events/echo-mismatch.js +75 -6
- package/dist/events/lineage.d.ts +52 -0
- package/dist/events/lineage.js +122 -0
- package/dist/events/net-evidence.d.ts +21 -0
- package/dist/events/net-evidence.js +81 -0
- package/dist/events/predicate-console.d.ts +13 -0
- package/dist/events/predicate-console.js +138 -0
- package/dist/events/predicate-element.d.ts +13 -0
- package/dist/events/predicate-element.js +214 -0
- package/dist/events/predicate-eval.d.ts +15 -3
- package/dist/events/predicate-eval.js +128 -110
- package/dist/events/predicate-parse.js +34 -1
- package/dist/events/predicate-request-body.d.ts +50 -0
- package/dist/events/predicate-request-body.js +120 -0
- package/dist/events/predicate-route.d.ts +21 -1
- package/dist/events/predicate-route.js +149 -21
- package/dist/events/predicate-schema.d.ts +8 -1
- package/dist/events/predicate-schema.js +34 -0
- package/dist/events/predicate.d.ts +2 -0
- package/dist/events/predicate.js +52 -159
- package/dist/events/split-text-miss.d.ts +6 -4
- package/dist/events/split-text-miss.js +12 -5
- package/dist/flows/affected-tools.js +22 -9
- package/dist/flows/annotate.d.ts +2 -1
- package/dist/flows/annotate.js +19 -1
- package/dist/flows/coverage.d.ts +2 -2
- package/dist/flows/flake.d.ts +3 -3
- package/dist/flows/flow-expect-grammar.d.ts +33 -0
- package/dist/flows/flow-expect-grammar.js +155 -0
- package/dist/flows/flow-replay-run.d.ts +1 -1
- package/dist/flows/flow-replay-run.js +28 -15
- package/dist/flows/flow-replay.d.ts +10 -0
- package/dist/flows/flow-replay.js +57 -22
- package/dist/flows/flow-secret-field.d.ts +14 -0
- package/dist/flows/flow-secret-field.js +22 -0
- package/dist/flows/flow-step-runners.d.ts +1 -0
- package/dist/flows/flow-step-runners.js +91 -34
- package/dist/flows/flow-success.js +22 -0
- package/dist/flows/flow-tools.js +27 -8
- package/dist/flows/flow-upload-resolve.d.ts +23 -0
- package/dist/flows/flow-upload-resolve.js +46 -0
- package/dist/flows/flows.d.ts +15 -0
- package/dist/flows/flows.js +17 -14
- package/dist/flows/git-changed.d.ts +23 -4
- package/dist/flows/git-changed.js +32 -6
- package/dist/flows/heal-run.js +2 -2
- package/dist/flows/predicate-to-expect.d.ts +3 -2
- package/dist/flows/predicate-to-expect.js +11 -5
- package/dist/flows/recording-backtrack.d.ts +24 -0
- package/dist/flows/recording-backtrack.js +73 -0
- package/dist/flows/recordings.d.ts +6 -0
- package/dist/flows/replay-halt.d.ts +26 -0
- package/dist/flows/replay-halt.js +30 -0
- package/dist/flows/replay.d.ts +0 -8
- package/dist/flows/replay.js +15 -3
- package/dist/flows/suite-flakes.d.ts +2 -1
- package/dist/flows/verify-change-tools.js +16 -4
- package/dist/honesty/already-true.d.ts +14 -0
- package/dist/honesty/already-true.js +47 -4
- package/dist/honesty/blind-spots.d.ts +3 -1
- package/dist/honesty/blind-spots.js +24 -1
- package/dist/honesty/body-capture-remedy.d.ts +42 -0
- package/dist/honesty/body-capture-remedy.js +95 -0
- package/dist/honesty/feature-capture.d.ts +3 -2
- package/dist/honesty/feature-capture.js +1 -1
- package/dist/honesty/gap-ledger.d.ts +1 -1
- package/dist/honesty/gap-ledger.js +1 -1
- package/dist/honesty/instrumentation-gaps.d.ts +8 -1
- package/dist/honesty/instrumentation-gaps.js +12 -2
- package/dist/honesty/observability.d.ts +4 -3
- package/dist/honesty/tool-hit-rate.d.ts +3 -2
- package/dist/honesty/uncaptured-bodies.d.ts +3 -1
- package/dist/honesty/uncaptured-bodies.js +6 -2
- package/dist/honesty/unsettled.d.ts +9 -0
- package/dist/honesty/unsettled.js +45 -0
- package/dist/honesty/verified.d.ts +32 -1
- package/dist/honesty/verified.js +37 -10
- package/dist/impact/impact-store.d.ts +5 -14
- package/dist/impact/impact-store.js +49 -6
- package/dist/index.js +1 -1
- package/dist/input/network-detail.d.ts +26 -1
- package/dist/input/network-detail.js +44 -12
- package/dist/input/network-mock-tools.js +24 -17
- package/dist/input/real-input.js +1 -1
- package/dist/input/viewport-tools.js +25 -13
- package/dist/intent/inline-intent.d.ts +0 -10
- package/dist/intent/inline-intent.js +1 -1
- package/dist/intent/intent-shard-store.d.ts +3 -2
- package/dist/intent/intent-shard-store.js +1 -1
- package/dist/intent/intent-shard.d.ts +14 -14
- package/dist/intent/intent-shard.js +4 -4
- package/dist/intent/intent-store.d.ts +1 -1
- package/dist/intent/intent-store.js +1 -1
- package/dist/intent/intent-subject.d.ts +2 -1
- package/dist/intent/open-intents.d.ts +3 -2
- package/dist/license/license.d.ts +4 -4
- package/dist/license/license.js +1 -1
- package/dist/mcp/attach-memory.d.ts +2 -1
- package/dist/mcp/client-identity.d.ts +2 -1
- package/dist/mcp/mcp-outage.d.ts +1 -1
- package/dist/mcp/mcp-outage.js +1 -1
- package/dist/mcp/mcp-proxy.d.ts +3 -1
- package/dist/mcp/mcp-proxy.js +32 -7
- package/dist/mcp/mcp.js +3 -1
- package/dist/mcp/proxy-handshake.d.ts +2 -1
- package/dist/mcp/proxy-idle-exit.d.ts +45 -0
- package/dist/mcp/proxy-idle-exit.js +94 -0
- package/dist/mcp/server-instructions.d.ts +2 -1
- package/dist/memory/project-memory.d.ts +2 -3
- package/dist/memory/project-memory.js +1 -1
- package/dist/oracles/propose-consequences.js +5 -5
- package/dist/oracles/self-instrument.d.ts +2 -2
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/platform.d.ts +3 -5
- package/dist/platform.js +3 -5
- package/dist/pool/browser-pool.d.ts +92 -0
- package/dist/pool/browser-pool.js +119 -5
- package/dist/pool/playwright-launcher.js +29 -2
- package/dist/pool/storage-seed.d.ts +23 -0
- package/dist/pool/storage-seed.js +198 -0
- package/dist/project/artifact-root.d.ts +2 -1
- package/dist/project/memory-fs.d.ts +2 -1
- package/dist/project/reticle-dir.js +1 -1
- package/dist/project/session-root.d.ts +8 -0
- package/dist/project/session-root.js +9 -2
- package/dist/runs/reticle-runner.d.ts +14 -1
- package/dist/runs/reticle-runner.js +39 -2
- package/dist/session/attachment-history.d.ts +2 -1
- package/dist/session/fake-session.d.ts +37 -0
- package/dist/session/fake-session.js +81 -0
- package/dist/session/lease-visibility.d.ts +2 -1
- package/dist/session/live-control-tools.js +6 -0
- package/dist/session/no-session-diagnosis.d.ts +17 -1
- package/dist/session/no-session-diagnosis.js +32 -6
- package/dist/session/no-session-next-action.d.ts +14 -0
- package/dist/session/no-session-next-action.js +18 -0
- package/dist/session/no-session-watch.js +46 -3
- package/dist/session/registered-projects.d.ts +54 -0
- package/dist/session/registered-projects.js +73 -0
- package/dist/session/review-store.d.ts +2 -1
- package/dist/session/session-health.d.ts +62 -1
- package/dist/session/session-health.js +82 -2
- package/dist/session/session-manager.d.ts +11 -1
- package/dist/session/session-manager.js +18 -3
- package/dist/session/session-replaced.d.ts +24 -0
- package/dist/session/session-replaced.js +29 -0
- package/dist/session/session-successor.js +20 -4
- package/dist/session/session.d.ts +13 -5
- package/dist/session/session.js +31 -23
- package/dist/setup/agent-configs.d.ts +3 -2
- package/dist/setup/agent-writer.d.ts +3 -2
- package/dist/setup/approval-migration.d.ts +2 -1
- package/dist/setup/approval-migration.js +1 -1
- package/dist/setup/auto-approve.d.ts +11 -19
- package/dist/setup/auto-approve.js +15 -12
- package/dist/{init → setup}/confirm.js +1 -1
- package/dist/setup/desktop-shape.d.ts +3 -2
- package/dist/setup/drive-agent.d.ts +2 -3
- package/dist/setup/drive-agent.js +1 -1
- package/dist/setup/drive-plan.d.ts +3 -2
- package/dist/setup/ensure-daemon.d.ts +2 -1
- package/dist/setup/existing-app.d.ts +12 -0
- package/dist/setup/existing-app.js +13 -0
- package/dist/setup/init-host.d.ts +11 -0
- package/dist/setup/init-host.js +26 -0
- package/dist/setup/init-runtime.d.ts +4 -10
- package/dist/setup/init-runtime.js +23 -16
- package/dist/setup/license-key.d.ts +2 -1
- package/dist/setup/listeners.d.ts +2 -1
- package/dist/setup/loopback-probe-urls.d.ts +18 -0
- package/dist/setup/loopback-probe-urls.js +52 -0
- package/dist/setup/node-effects.d.ts +3 -0
- package/dist/setup/node-effects.js +26 -11
- package/dist/setup/page-probe.d.ts +5 -0
- package/dist/setup/relaunch.d.ts +2 -1
- package/dist/setup/run-setup.d.ts +5 -1
- package/dist/setup/run-setup.js +141 -50
- package/dist/setup/setup-command.d.ts +3 -2
- package/dist/setup/setup-command.js +8 -0
- package/dist/setup/setup-options.d.ts +5 -3
- package/dist/setup/setup-options.js +3 -2
- package/dist/setup/wait-progress.d.ts +28 -0
- package/dist/setup/wait-progress.js +43 -0
- package/dist/telemetry/app-instrumented.d.ts +2 -1
- package/dist/telemetry/app-instrumented.js +1 -1
- package/dist/telemetry/automation-hint.d.ts +1 -1
- package/dist/telemetry/automation-hint.js +1 -1
- package/dist/telemetry/cli-telemetry.js +1 -1
- package/dist/telemetry/connect-failure.d.ts +1 -1
- package/dist/telemetry/connect-failure.js +1 -1
- package/dist/telemetry/crash-cause.d.ts +3 -2
- package/dist/telemetry/crash-cause.js +1 -1
- package/dist/telemetry/daemon-telemetry.d.ts +3 -2
- package/dist/telemetry/daemon-telemetry.js +1 -1
- package/dist/telemetry/error-class.d.ts +3 -2
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-cli.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +3 -2
- package/dist/telemetry/feedback-context.js +5 -4
- package/dist/telemetry/feedback-local.d.ts +3 -2
- package/dist/telemetry/feedback.d.ts +4 -3
- package/dist/telemetry/feedback.js +1 -1
- package/dist/telemetry/git-facts.d.ts +2 -2
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/identify.d.ts +1 -1
- package/dist/telemetry/identify.js +1 -1
- package/dist/telemetry/init-telemetry.d.ts +2 -14
- package/dist/telemetry/init-telemetry.js +4 -13
- package/dist/telemetry/install-source.d.ts +7 -13
- package/dist/telemetry/install-source.js +7 -29
- package/dist/telemetry/license-activation.d.ts +3 -2
- package/dist/telemetry/license-activation.js +1 -1
- package/dist/telemetry/machine-snapshot.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +1 -1
- package/dist/telemetry/project-profile.d.ts +0 -14
- package/dist/telemetry/project-profile.js +1 -1
- package/dist/telemetry/proxy-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +1 -1
- package/dist/telemetry/telemetry.d.ts +1 -17
- package/dist/telemetry/telemetry.js +4 -4
- package/dist/telemetry/tool-refused.d.ts +1 -1
- package/dist/telemetry/tool-refused.js +1 -1
- package/dist/tools/act-observation.d.ts +3 -2
- package/dist/tools/act-preflight.js +8 -3
- package/dist/tools/act-target.d.ts +4 -0
- package/dist/tools/act-target.js +14 -5
- package/dist/tools/act-tools.d.ts +0 -2
- package/dist/tools/act-tools.js +45 -15
- package/dist/tools/assert-source.d.ts +30 -0
- package/dist/tools/assert-source.js +21 -0
- package/dist/tools/assert-verdict.d.ts +1 -1
- package/dist/tools/assert-verdict.js +14 -4
- package/dist/tools/browser-tools.js +28 -8
- package/dist/tools/complete-snapshot.d.ts +3 -2
- package/dist/tools/dynamic-tools.js +8 -0
- package/dist/tools/error-recovery.d.ts +10 -0
- package/dist/tools/error-recovery.js +23 -0
- package/dist/tools/feedback-tools.js +1 -1
- package/dist/tools/invoke-tool.js +2 -0
- package/dist/tools/lease-tools.d.ts +48 -20
- package/dist/tools/lease-tools.js +311 -76
- package/dist/tools/lineage-tools.d.ts +14 -0
- package/dist/tools/lineage-tools.js +64 -0
- package/dist/tools/merge-tools.js +1 -3
- package/dist/tools/navigate-arrival.d.ts +45 -1
- package/dist/tools/navigate-arrival.js +39 -6
- package/dist/tools/navigate-result.d.ts +8 -1
- package/dist/tools/navigate-result.js +14 -4
- package/dist/tools/numeric-bounds.d.ts +0 -11
- package/dist/tools/numeric-bounds.js +4 -4
- package/dist/tools/observe-tools.js +22 -26
- package/dist/tools/observed-nothing.d.ts +9 -0
- package/dist/tools/observed-nothing.js +2 -1
- package/dist/tools/read-tools.js +12 -17
- package/dist/tools/real-input-attempt.d.ts +14 -6
- package/dist/tools/real-input-attempt.js +75 -18
- package/dist/tools/reconcile-tools.js +2 -1
- package/dist/tools/reload-result.d.ts +6 -1
- package/dist/tools/reload-result.js +7 -2
- package/dist/tools/resolve-target.d.ts +7 -1
- package/dist/tools/resolve-target.js +72 -9
- package/dist/tools/settle-in-flight.d.ts +1 -1
- package/dist/tools/settle-in-flight.js +9 -2
- package/dist/tools/snapshot-delta.d.ts +24 -1
- package/dist/tools/snapshot-delta.js +66 -7
- package/dist/tools/tool-names.d.ts +1 -0
- package/dist/tools/tool-names.js +1 -0
- package/dist/tools/tool-surface.js +20 -2
- package/dist/tools/tools.js +120 -11
- package/dist/tools/verify-next-baton.d.ts +2 -1
- package/dist/update/sdk-sync.d.ts +1 -1
- package/dist/update/sdk-sync.js +1 -1
- package/dist/update/updater.js +1 -1
- package/dist/version/sdk-fix.d.ts +2 -1
- package/dist/version/version-skew.d.ts +0 -8
- package/dist/version/version-skew.js +1 -1
- package/docs/README.md +8 -6
- package/docs/actions.mdx +1 -1
- package/docs/agent-cheatsheet.md +4 -2
- package/docs/architecture.md +1 -1
- package/docs/cli/mcp.mdx +3 -1
- package/docs/cli/watch.mdx +1 -1
- package/docs/cli.mdx +1 -1
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +8 -3
- package/docs/faq.mdx +9 -0
- package/docs/first-drive.md +136 -0
- package/docs/frameworks.mdx +29 -7
- package/docs/gate-plan.md +2 -2
- package/docs/gates.md +19 -11
- package/docs/getting-started.md +7 -1
- package/docs/http-transport.md +164 -0
- package/docs/index.mdx +5 -3
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +15 -3
- package/docs/platform-integration.md +1 -1
- package/docs/predicates.mdx +2 -0
- package/docs/system-map.md +2 -2
- package/docs/telemetry-contract.md +4 -4
- package/docs/telemetry-events.mdx +1 -1
- package/docs/telemetry.md +3 -3
- package/docs/tools-navigate.mdx +14 -6
- package/docs/usage.md +4 -4
- package/package.json +7 -4
- package/dist/init/agent-rules.d.ts +0 -116
- package/dist/init/agent-rules.js +0 -279
- package/dist/init/app-choice.d.ts +0 -20
- package/dist/init/app-choice.js +0 -25
- package/dist/init/astro-host.d.ts +0 -21
- package/dist/init/astro-host.js +0 -27
- package/dist/init/astro-patch.d.ts +0 -33
- package/dist/init/astro-patch.js +0 -300
- package/dist/init/capabilities.d.ts +0 -40
- package/dist/init/capabilities.js +0 -150
- package/dist/init/closing-hint.d.ts +0 -36
- package/dist/init/closing-hint.js +0 -120
- package/dist/init/connect-steps.d.ts +0 -2
- package/dist/init/connect-steps.js +0 -50
- package/dist/init/cra.d.ts +0 -79
- package/dist/init/cra.js +0 -178
- package/dist/init/csp-check.d.ts +0 -38
- package/dist/init/csp-check.js +0 -107
- package/dist/init/csp-doctor.d.ts +0 -32
- package/dist/init/csp-doctor.js +0 -54
- package/dist/init/desktop-doctor.d.ts +0 -41
- package/dist/init/desktop-doctor.js +0 -198
- package/dist/init/detect.d.ts +0 -96
- package/dist/init/detect.js +0 -209
- package/dist/init/dev-script.d.ts +0 -50
- package/dist/init/dev-script.js +0 -66
- package/dist/init/format-generated.d.ts +0 -18
- package/dist/init/format-generated.js +0 -39
- package/dist/init/install-hint.d.ts +0 -9
- package/dist/init/install-hint.js +0 -29
- package/dist/init/mcp-clients.d.ts +0 -89
- package/dist/init/mcp-clients.js +0 -299
- package/dist/init/mcp-registered.d.ts +0 -12
- package/dist/init/mcp-registered.js +0 -14
- package/dist/init/mcp.d.ts +0 -89
- package/dist/init/mcp.js +0 -129
- package/dist/init/next-patch.d.ts +0 -39
- package/dist/init/next-patch.js +0 -196
- package/dist/init/node-io.d.ts +0 -7
- package/dist/init/node-io.js +0 -117
- package/dist/init/non-js-project.d.ts +0 -27
- package/dist/init/non-js-project.js +0 -84
- package/dist/init/patch-kind.d.ts +0 -20
- package/dist/init/patch-kind.js +0 -10
- package/dist/init/plan-framework.d.ts +0 -64
- package/dist/init/plan-framework.js +0 -565
- package/dist/init/plan.d.ts +0 -242
- package/dist/init/plan.js +0 -703
- package/dist/init/preflight.d.ts +0 -28
- package/dist/init/preflight.js +0 -38
- package/dist/init/project-id.d.ts +0 -12
- package/dist/init/project-id.js +0 -25
- package/dist/init/refresh-rules.d.ts +0 -39
- package/dist/init/refresh-rules.js +0 -51
- package/dist/init/run.d.ts +0 -117
- package/dist/init/run.js +0 -806
- package/dist/init/slash-command.d.ts +0 -27
- package/dist/init/slash-command.js +0 -132
- package/dist/init/snippets.d.ts +0 -134
- package/dist/init/snippets.js +0 -572
- package/dist/init/vite-config.d.ts +0 -18
- package/dist/init/vite-config.js +0 -114
- package/dist/init/windows-quote.d.ts +0 -38
- package/dist/init/windows-quote.js +0 -70
- package/dist/init/workspace-apps.d.ts +0 -30
- package/dist/init/workspace-apps.js +0 -178
- package/dist/project/remember-project.d.ts +0 -24
- package/dist/project/remember-project.js +0 -40
- /package/dist/{init → setup}/confirm.d.ts +0 -0
|
@@ -96,16 +96,22 @@ export function predicateToExpect(predicate) {
|
|
|
96
96
|
* `reticle_verify` then correctly called it `unverifiable` — the record → save → verify path
|
|
97
97
|
* completing all the way to a run that could never be a pass. Found by driving it.
|
|
98
98
|
*
|
|
99
|
-
* The list below is exactly what `successToPredicate` reads,
|
|
100
|
-
*
|
|
99
|
+
* The list below is exactly what `successToPredicate` reads, including an element located by
|
|
100
|
+
* role or name. A testid is also asserted directly against the DOM by the step runner. Anything
|
|
101
|
+
* it cannot compile is still dropped.
|
|
101
102
|
*/
|
|
102
103
|
export function enforcedOnReplay(expect) {
|
|
103
104
|
if (expect === undefined)
|
|
104
105
|
return undefined;
|
|
105
106
|
const kept = {};
|
|
106
|
-
// The step runner asserts
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
// The step runner asserts a testid against the live DOM before the predicate engine. Role and
|
|
108
|
+
// name are not that path: successToPredicate compiles them, and dropping them here made a
|
|
109
|
+
// recorded `until` by button name vanish so the saved flow could not go red.
|
|
110
|
+
const element = expect.element;
|
|
111
|
+
if (undefined !== element &&
|
|
112
|
+
(undefined !== element.testid || undefined !== element.role || undefined !== element.name)) {
|
|
113
|
+
kept.element = element;
|
|
114
|
+
}
|
|
109
115
|
// Everything `successToPredicate` can compile. Replay evaluates EVERY kind of expect through it
|
|
110
116
|
// (see assertStepExpect); this list is what that function actually reads.
|
|
111
117
|
if (expect.state !== undefined)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A recorded flow that backtracks across pages cannot replay.
|
|
3
|
+
*
|
|
4
|
+
* The recorder stores clicks, not navigations. A drive that opens a product and then returns to
|
|
5
|
+
* search saves both clicks; replay lands on the product page and looks for a control that only
|
|
6
|
+
* exists on search. That shape is visible from the route list with no change to the flow file, so
|
|
7
|
+
* stop/save can warn instead of writing a flow that fails on the next click.
|
|
8
|
+
*/
|
|
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
|
+
/**
|
|
18
|
+
* Warn when the journey left a page and came back. Linear A→B→C is silent: that still cannot
|
|
19
|
+
* replay either if a later click needs a previous page, but the reported failure is specifically
|
|
20
|
+
* the backtrack, and inventing a warning for every multi-page drive would teach agents to ignore it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function recordingBacktrackWarning(routes: readonly string[]): string | undefined;
|
|
23
|
+
/** Ordered pages the recording sat on, startPath first, consecutive stays collapsed. */
|
|
24
|
+
export declare function routesFromRecording(startPath: string | undefined, events: readonly ReticleEvent[]): string[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A recorded flow that backtracks across pages cannot replay.
|
|
3
|
+
*
|
|
4
|
+
* The recorder stores clicks, not navigations. A drive that opens a product and then returns to
|
|
5
|
+
* search saves both clicks; replay lands on the product page and looks for a control that only
|
|
6
|
+
* exists on search. That shape is visible from the route list with no change to the flow file, so
|
|
7
|
+
* stop/save can warn instead of writing a flow that fails on the next click.
|
|
8
|
+
*/
|
|
9
|
+
import { EventType } from '@reticlehq/core';
|
|
10
|
+
import { routePathOf } from '../events/predicate-route.js';
|
|
11
|
+
function asString(value) {
|
|
12
|
+
return 'string' === typeof value ? value : undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The page a recording step sat on, comparable across path routers and hash routers.
|
|
16
|
+
*
|
|
17
|
+
* A startPath is stored as pathname+hash (`/#/search`); a ROUTE_CHANGE carries them separately.
|
|
18
|
+
* Without this, every hash-router page compares as `/` and a backtrack is invisible.
|
|
19
|
+
*/
|
|
20
|
+
export function recordingRouteOf(pathname, hash = '') {
|
|
21
|
+
const hashIdx = pathname.indexOf('#/');
|
|
22
|
+
if (hashIdx >= 0) {
|
|
23
|
+
return routePathOf(pathname.slice(0, hashIdx) || '/', pathname.slice(hashIdx));
|
|
24
|
+
}
|
|
25
|
+
const fragment = hash.startsWith('#') ? hash : 0 === hash.length ? '' : `#${hash}`;
|
|
26
|
+
if (fragment.startsWith('#/'))
|
|
27
|
+
return routePathOf(0 === pathname.length ? '/' : pathname, fragment);
|
|
28
|
+
return pathname;
|
|
29
|
+
}
|
|
30
|
+
function collapseConsecutive(routes) {
|
|
31
|
+
const out = [];
|
|
32
|
+
for (const route of routes) {
|
|
33
|
+
if (0 === route.length)
|
|
34
|
+
continue;
|
|
35
|
+
if (out.at(-1) === route)
|
|
36
|
+
continue;
|
|
37
|
+
out.push(route);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Warn when the journey left a page and came back. Linear A→B→C is silent: that still cannot
|
|
43
|
+
* replay either if a later click needs a previous page, but the reported failure is specifically
|
|
44
|
+
* the backtrack, and inventing a warning for every multi-page drive would teach agents to ignore it.
|
|
45
|
+
*/
|
|
46
|
+
export function recordingBacktrackWarning(routes) {
|
|
47
|
+
const path = collapseConsecutive(routes);
|
|
48
|
+
const seen = new Set();
|
|
49
|
+
for (const route of path) {
|
|
50
|
+
if (seen.has(route)) {
|
|
51
|
+
return (`this recording returned to ${route} after leaving it — replay has no navigation steps, so ` +
|
|
52
|
+
'the next click looks for a control that only exists on a page the tab is no longer on. ' +
|
|
53
|
+
`Journey: ${path.join(' → ')}. Split into one flow per page, or record the control that navigates back.`);
|
|
54
|
+
}
|
|
55
|
+
seen.add(route);
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
/** Ordered pages the recording sat on, startPath first, consecutive stays collapsed. */
|
|
60
|
+
export function routesFromRecording(startPath, events) {
|
|
61
|
+
const raw = [];
|
|
62
|
+
if (undefined !== startPath)
|
|
63
|
+
raw.push(recordingRouteOf(startPath));
|
|
64
|
+
for (const event of events) {
|
|
65
|
+
if (EventType.ROUTE_CHANGE !== event.type)
|
|
66
|
+
continue;
|
|
67
|
+
const pathname = asString(event.data['pathname']) ?? asString(event.data['to']);
|
|
68
|
+
if (undefined === pathname)
|
|
69
|
+
continue;
|
|
70
|
+
raw.push(recordingRouteOf(pathname, asString(event.data['hash']) ?? ''));
|
|
71
|
+
}
|
|
72
|
+
return collapseConsecutive(raw);
|
|
73
|
+
}
|
|
@@ -22,6 +22,12 @@ export interface CompiledProgram {
|
|
|
22
22
|
version: number;
|
|
23
23
|
steps: RecordedStep[];
|
|
24
24
|
startPath?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Pages the recording sat on, in order, consecutive stays collapsed. In-memory only — not written
|
|
27
|
+
* to the flow file. Lets save warn about a backtrack (a journey that cannot replay) without a
|
|
28
|
+
* format change.
|
|
29
|
+
*/
|
|
30
|
+
routes?: string[];
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* Tracks in-flight recordings (name -> { buffer cursor at record_start, captured steps })
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a replay stopped before the end of the flow, and how much it never reached.
|
|
3
|
+
*
|
|
4
|
+
* `replayFlow` breaks on the first failing step. That is right — once a consequence did not hold,
|
|
5
|
+
* every later step would run against a state the flow never described, and continuing would turn
|
|
6
|
+
* one wrong result into several. What was missing is any way for the caller to SEE it: a two-step
|
|
7
|
+
* flow that halted comes back with one step result, and a reader who does not know the halt rule
|
|
8
|
+
* sees a step that is simply absent.
|
|
9
|
+
*
|
|
10
|
+
* Reported as "replay silently skips a second destructive step" — with three correct observations
|
|
11
|
+
* and a wrong conclusion, because the output offered no other reading.
|
|
12
|
+
*/
|
|
13
|
+
import type { FlowStepResult } from '@reticlehq/core';
|
|
14
|
+
/** Set on a replay that stopped early; omitted entirely otherwise, so a clean pass stays flat. */
|
|
15
|
+
export interface ReplayHalt {
|
|
16
|
+
/** Index of the step that stopped the run. */
|
|
17
|
+
atStep: number;
|
|
18
|
+
/** How many of the flow's steps were never attempted because of it. */
|
|
19
|
+
notAttempted: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* `total` is the FLOW's step count, deliberately — not `results.length`. The results array is the
|
|
23
|
+
* short one, so deriving the total from it would report zero skipped on every halt, which is the
|
|
24
|
+
* defect restated as its own fix.
|
|
25
|
+
*/
|
|
26
|
+
export declare function haltedFrom(results: readonly FlowStepResult[], total: number): ReplayHalt | undefined;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a replay stopped before the end of the flow, and how much it never reached.
|
|
3
|
+
*
|
|
4
|
+
* `replayFlow` breaks on the first failing step. That is right — once a consequence did not hold,
|
|
5
|
+
* every later step would run against a state the flow never described, and continuing would turn
|
|
6
|
+
* one wrong result into several. What was missing is any way for the caller to SEE it: a two-step
|
|
7
|
+
* flow that halted comes back with one step result, and a reader who does not know the halt rule
|
|
8
|
+
* sees a step that is simply absent.
|
|
9
|
+
*
|
|
10
|
+
* Reported as "replay silently skips a second destructive step" — with three correct observations
|
|
11
|
+
* and a wrong conclusion, because the output offered no other reading.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* `total` is the FLOW's step count, deliberately — not `results.length`. The results array is the
|
|
15
|
+
* short one, so deriving the total from it would report zero skipped on every halt, which is the
|
|
16
|
+
* defect restated as its own fix.
|
|
17
|
+
*/
|
|
18
|
+
export function haltedFrom(results, total) {
|
|
19
|
+
const last = results[results.length - 1];
|
|
20
|
+
if (last === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
if (last.ok && last.drift === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
const notAttempted = total - results.length;
|
|
25
|
+
// A failure on the FINAL step is not a halt: nothing was left to attempt, and `notAttempted: 0`
|
|
26
|
+
// would send a reader looking for steps that do not exist.
|
|
27
|
+
if (notAttempted <= 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
return { atStep: last.step, notAttempted };
|
|
30
|
+
}
|
package/dist/flows/replay.d.ts
CHANGED
|
@@ -13,14 +13,6 @@ export declare function ambiguousTestidNote(value: string): string;
|
|
|
13
13
|
* resolution — can't drift between them.
|
|
14
14
|
*/
|
|
15
15
|
export declare function queryRefs(result: CommandResult): string[];
|
|
16
|
-
/**
|
|
17
|
-
* The environment variable that supplies one redacted field.
|
|
18
|
-
*
|
|
19
|
-
* Named after the field so it is guessable from the flow alone: `auth-password` is read from
|
|
20
|
-
* `RETICLE_SECRET_AUTH_PASSWORD`. A scheme requiring a lookup table would mean the flow says a
|
|
21
|
-
* value is missing and cannot say what to set.
|
|
22
|
-
*/
|
|
23
|
-
export declare const secretEnvKey: (field: string) => string;
|
|
24
16
|
/**
|
|
25
17
|
* A destructive-action confirmation is one-shot and must never persist into a recording.
|
|
26
18
|
*
|
package/dist/flows/replay.js
CHANGED
|
@@ -31,7 +31,7 @@ export function queryRefs(result) {
|
|
|
31
31
|
* `RETICLE_SECRET_AUTH_PASSWORD`. A scheme requiring a lookup table would mean the flow says a
|
|
32
32
|
* value is missing and cannot say what to set.
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
const secretEnvKey = (field) => `RETICLE_SECRET_${field.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase()}`;
|
|
35
35
|
/**
|
|
36
36
|
* A destructive-action confirmation is one-shot and must never persist into a recording.
|
|
37
37
|
*
|
|
@@ -57,6 +57,18 @@ export function replayActionArgs(value, confirmDangerous = false, field) {
|
|
|
57
57
|
}
|
|
58
58
|
return args;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* The field a compiled recording step names — the same mapping `anchorFieldName` uses on a
|
|
62
|
+
* FlowAnchor, read off the `by`/`value`/`name` shape `compileAnchorArgs` emits.
|
|
63
|
+
*/
|
|
64
|
+
function recordedFieldName(step) {
|
|
65
|
+
const by = asString(step['by']);
|
|
66
|
+
if (QueryBy.TESTID === by)
|
|
67
|
+
return asString(step['value']);
|
|
68
|
+
if (QueryBy.ROLE === by)
|
|
69
|
+
return asString(step['name']);
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
60
72
|
/** The element's source location from an action result, when the framework stamped one. */
|
|
61
73
|
function sourceFromResult(res) {
|
|
62
74
|
const source = asRecord(res['source']);
|
|
@@ -265,7 +277,7 @@ export async function replayProgram(session, program, confirmDangerous = false)
|
|
|
265
277
|
liveSteps.push({
|
|
266
278
|
ref,
|
|
267
279
|
action: asString(sub['action']) ?? '',
|
|
268
|
-
args: replayActionArgs(sub['args'], confirmDangerous),
|
|
280
|
+
args: replayActionArgs(sub['args'], confirmDangerous, recordedFieldName(sub)),
|
|
269
281
|
});
|
|
270
282
|
}
|
|
271
283
|
// Attribute the step's effects to the step: unattributed effects are learned as ambient
|
|
@@ -293,7 +305,7 @@ export async function replayProgram(session, program, confirmDangerous = false)
|
|
|
293
305
|
r = await session.command(ReticleCommand.ACT, {
|
|
294
306
|
ref,
|
|
295
307
|
action: asString(step.args['action']) ?? '',
|
|
296
|
-
args: replayActionArgs(step.args['args'], confirmDangerous),
|
|
308
|
+
args: replayActionArgs(step.args['args'], confirmDangerous, recordedFieldName(step.args)),
|
|
297
309
|
});
|
|
298
310
|
}
|
|
299
311
|
finally {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { ReplayStatus } from '@reticlehq/core';
|
|
19
19
|
import type { FileSystemPort } from '../project/fs-port.js';
|
|
20
20
|
/** Just the shape this needs from a replay — so a test does not have to build a whole result. */
|
|
21
|
-
|
|
21
|
+
interface FlakeOutcome {
|
|
22
22
|
readonly name: string;
|
|
23
23
|
readonly status: ReplayStatus;
|
|
24
24
|
}
|
|
@@ -31,3 +31,4 @@ export interface FlakeOutcome {
|
|
|
31
31
|
export declare function recordSuiteFlakes(fs: FileSystemPort, reticleRoot: string, runs: readonly {
|
|
32
32
|
readonly replay: FlakeOutcome;
|
|
33
33
|
}[]): Promise<readonly string[]>;
|
|
34
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { sessionRoot } from '../project/session-root.js';
|
|
2
|
+
import { sessionRoot, sessionProjectId } from '../project/session-root.js';
|
|
3
3
|
import { verdictForSuite } from './verify-change-verdict.js';
|
|
4
4
|
import { attributedFailures } from './attributed-failure.js';
|
|
5
5
|
import { Verified } from '@reticlehq/core';
|
|
@@ -62,16 +62,28 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
62
62
|
? args['files'].filter((f) => 'string' === typeof f)
|
|
63
63
|
: [];
|
|
64
64
|
const since = asString(args['since']);
|
|
65
|
-
|
|
65
|
+
// The daemon's cwd is not the project — it is wherever the MCP host started it, which for a
|
|
66
|
+
// globally-registered server is `/` or `$HOME`. `deps.reticleRoot` is the project directory
|
|
67
|
+
// this daemon was configured with, and it is the right tree to diff.
|
|
68
|
+
const changed = await resolveChangedFiles(files, since, deps.reticleRoot);
|
|
69
|
+
const changedFiles = changed.files;
|
|
66
70
|
// Same root flow_save wrote to. A read that resolved differently would report "no flows
|
|
67
71
|
// covered this change" over flows that exist, which reads as a clean result rather than
|
|
68
72
|
// an error, so the disagreement would be invisible.
|
|
69
|
-
|
|
73
|
+
// Both halves of the address from one place. The root was already resolved from the session;
|
|
74
|
+
// the id used to come from the daemon's cwd, so they named different projects.
|
|
75
|
+
const sid = asString(args['sessionId']);
|
|
76
|
+
const flows = await loadNamedFlows(deps.fs, sessionRoot(deps, sid), sessionProjectId(deps, sid));
|
|
70
77
|
const { affected, unknownProvenance } = affectedSavedFlows(flows, changedFiles);
|
|
71
78
|
if (0 === changedFiles.length) {
|
|
72
79
|
return {
|
|
73
80
|
verified: Verified.UNKNOWN,
|
|
74
|
-
|
|
81
|
+
// A git failure and a clean tree both arrive here with no files, and they are opposite
|
|
82
|
+
// answers: one means there was nothing to decide about, the other means the question was
|
|
83
|
+
// never asked. `unknown` is right either way, but the REASON is what the caller acts on.
|
|
84
|
+
because: changed.failed
|
|
85
|
+
? `could not read the diff for \`${String(since)}\`, so nothing was compared: ${changed.reason ?? 'git failed'}`
|
|
86
|
+
: 'no changed files were given, so there was nothing to decide about',
|
|
75
87
|
changedFiles,
|
|
76
88
|
flowsRun: [],
|
|
77
89
|
unknownProvenance,
|
|
@@ -1,2 +1,16 @@
|
|
|
1
1
|
import type { Predicate } from '../events/predicate.js';
|
|
2
2
|
export declare function readsDomState(predicate: Predicate): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* #889: an `already_true` verdict says the declared consequence held before the action, so nothing
|
|
5
|
+
* was proven — but a `text`/`element` predicate matches DOM presence by default, not visibility (a
|
|
6
|
+
* caller has to pass `visible: true` to ask for that). A dialog's content mounted-but-hidden before
|
|
7
|
+
* the click therefore reads as "already true", identically to genuinely-visible content that was
|
|
8
|
+
* already on screen — and the `already_true` message named neither which node it matched nor that
|
|
9
|
+
* the match was invisible, which is what made it read as proof of nothing rather than as the
|
|
10
|
+
* mid-load/hidden-mount trap it actually was.
|
|
11
|
+
*
|
|
12
|
+
* True only when the predicate itself did not ask for a state (an explicit `visible`/other state
|
|
13
|
+
* constraint already means the caller saw and handled this) AND every matched element is hidden —
|
|
14
|
+
* one visible match among several is a fair, ordinary read and is left alone.
|
|
15
|
+
*/
|
|
16
|
+
export declare function alreadyTrueHiddenMatch(predicate: Predicate, evidence: unknown): boolean;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Which predicates can be satisfied by something that was already true before the action.
|
|
3
3
|
*
|
|
4
|
-
* Event-based kinds (net, signal,
|
|
4
|
+
* Event-based kinds (net, signal, console, animation, settled) are evaluated against the
|
|
5
5
|
* event buffer floored at the act's own cursor, so a stale event cannot satisfy them — that floor is
|
|
6
|
-
* why `act_and_wait` can trust them at all.
|
|
6
|
+
* why `act_and_wait` can trust them at all.
|
|
7
7
|
*
|
|
8
|
-
* `element` and `text` read the LIVE DOM,
|
|
8
|
+
* `element` and `text` read the LIVE DOM, `route` can fall back to the current route, and `state` reads
|
|
9
|
+
* live store memory via STATE_READ — where no event floor applies. A condition that held before the
|
|
9
10
|
* click holds after it and passes instantly, whatever the action did. Measured in the field: a click
|
|
10
11
|
* asserted with `{ kind: 'text', contains: 'Parallel Routes' }` returned `verified: "yes"` in 478ms
|
|
11
12
|
* against `routeChanges: 0`, because the predicate matched the nav link that was already on screen —
|
|
12
|
-
* the real navigation landed 1.8 seconds later.
|
|
13
|
+
* the real navigation landed 1.8 seconds later. Similarly, a pre-existing store value (e.g. cart.count == 3)
|
|
14
|
+
* satisfies an inert action if not checked before dispatch.
|
|
13
15
|
*
|
|
14
16
|
* So these are the kinds worth evaluating BEFORE the act, to find out whether the green means
|
|
15
17
|
* anything.
|
|
@@ -31,9 +33,15 @@ export function readsDomState(predicate) {
|
|
|
31
33
|
// One extra query on the act path buys `already_true`, reported as UNKNOWN with the reason
|
|
32
34
|
// rather than as a pass. The fallback removed a false red; this is what stops it becoming a
|
|
33
35
|
// false green, and that is the trade this codebase never makes.
|
|
36
|
+
//
|
|
37
|
+
// STATE is here for the same reason: it is evaluated via STATE_READ against live in-memory stores
|
|
38
|
+
// rather than through the floored event buffer. If the store already holds the expected value
|
|
39
|
+
// before dispatch, an inert click would immediately pass post-dispatch on the pre-existing state.
|
|
40
|
+
// Evaluating before dispatch routes that pre-existing condition into already_true.
|
|
34
41
|
case PredicateKind.ELEMENT:
|
|
35
42
|
case PredicateKind.TEXT:
|
|
36
43
|
case PredicateKind.ROUTE:
|
|
44
|
+
case PredicateKind.STATE:
|
|
37
45
|
return true;
|
|
38
46
|
case PredicateKind.ALL_OF:
|
|
39
47
|
case PredicateKind.ANY_OF:
|
|
@@ -44,3 +52,38 @@ export function readsDomState(predicate) {
|
|
|
44
52
|
return false;
|
|
45
53
|
}
|
|
46
54
|
}
|
|
55
|
+
/** True when `value` looks like an element/text predicate's passing evidence — an array of descriptors. */
|
|
56
|
+
function isDescriptorArray(value) {
|
|
57
|
+
if (!Array.isArray(value))
|
|
58
|
+
return false;
|
|
59
|
+
return value.every((v) => {
|
|
60
|
+
if ('object' !== typeof v || null === v)
|
|
61
|
+
return false;
|
|
62
|
+
return 'boolean' === typeof v['visible'];
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* #889: an `already_true` verdict says the declared consequence held before the action, so nothing
|
|
67
|
+
* was proven — but a `text`/`element` predicate matches DOM presence by default, not visibility (a
|
|
68
|
+
* caller has to pass `visible: true` to ask for that). A dialog's content mounted-but-hidden before
|
|
69
|
+
* the click therefore reads as "already true", identically to genuinely-visible content that was
|
|
70
|
+
* already on screen — and the `already_true` message named neither which node it matched nor that
|
|
71
|
+
* the match was invisible, which is what made it read as proof of nothing rather than as the
|
|
72
|
+
* mid-load/hidden-mount trap it actually was.
|
|
73
|
+
*
|
|
74
|
+
* True only when the predicate itself did not ask for a state (an explicit `visible`/other state
|
|
75
|
+
* constraint already means the caller saw and handled this) AND every matched element is hidden —
|
|
76
|
+
* one visible match among several is a fair, ordinary read and is left alone.
|
|
77
|
+
*/
|
|
78
|
+
export function alreadyTrueHiddenMatch(predicate, evidence) {
|
|
79
|
+
if (predicate.kind !== PredicateKind.ELEMENT && predicate.kind !== PredicateKind.TEXT) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
if (predicate.kind === PredicateKind.ELEMENT && predicate.state !== undefined)
|
|
83
|
+
return false;
|
|
84
|
+
if (predicate.kind === PredicateKind.TEXT && true === predicate.visible)
|
|
85
|
+
return false;
|
|
86
|
+
if (!isDescriptorArray(evidence) || 0 === evidence.length)
|
|
87
|
+
return false;
|
|
88
|
+
return evidence.every((el) => !el.visible);
|
|
89
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { BlindSpotKind } from '@reticlehq/core';
|
|
8
8
|
import { BlindSpotKind, type ReticleEvent } from '@reticlehq/core';
|
|
9
|
-
|
|
9
|
+
interface BlindSpot {
|
|
10
10
|
kind: BlindSpotKind;
|
|
11
11
|
count: number;
|
|
12
12
|
}
|
|
@@ -89,6 +89,7 @@ interface AbsencePredicate {
|
|
|
89
89
|
query?: {
|
|
90
90
|
scope?: unknown;
|
|
91
91
|
};
|
|
92
|
+
predicate?: AbsencePredicate;
|
|
92
93
|
}
|
|
93
94
|
/**
|
|
94
95
|
* When an absence assertion targets a page with regions Reticle cannot observe, "the element is
|
|
@@ -127,5 +128,6 @@ export declare function isStateUnwatched(spots: readonly BlindSpot[]): boolean;
|
|
|
127
128
|
export declare function restsOnCompleteWindow(predicate: {
|
|
128
129
|
kind: string;
|
|
129
130
|
absent?: boolean;
|
|
131
|
+
count?: number;
|
|
130
132
|
predicate?: unknown;
|
|
131
133
|
}): boolean;
|
|
@@ -176,7 +176,19 @@ function droppedByTransport(events) {
|
|
|
176
176
|
* - CLOSED_SHADOW_ROOT — a subtree Reticle cannot see into
|
|
177
177
|
*/
|
|
178
178
|
export function absenceBlindSpotNote(predicate, spots) {
|
|
179
|
-
|
|
179
|
+
// `not(element)` is the third spelling of an absence assertion — the `not` wrapper IS the
|
|
180
|
+
// negation, so the inner predicate will not carry `absent: true`. Unwrap and synthesize it.
|
|
181
|
+
// `not(element { absent: true })` is a double negative — presence — and a positive match is
|
|
182
|
+
// not threatened by an unobservable region.
|
|
183
|
+
if (PredicateKind.NOT === predicate.kind && predicate.predicate !== undefined) {
|
|
184
|
+
if (PredicateKind.ELEMENT === predicate.predicate.kind) {
|
|
185
|
+
if (true === predicate.predicate.absent)
|
|
186
|
+
return undefined;
|
|
187
|
+
return absenceBlindSpotNote({ ...predicate.predicate, absent: true }, spots);
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
if (PredicateKind.ELEMENT !== predicate.kind || true !== predicate.absent)
|
|
180
192
|
return undefined;
|
|
181
193
|
const relevant = spots.filter((spot) => spot.count > 0 &&
|
|
182
194
|
(spot.kind === BlindSpotKind.CROSS_ORIGIN_IFRAME
|
|
@@ -221,5 +233,16 @@ export function isStateUnwatched(spots) {
|
|
|
221
233
|
export function restsOnCompleteWindow(predicate) {
|
|
222
234
|
if (PredicateKind.NOT === predicate.kind)
|
|
223
235
|
return true;
|
|
236
|
+
// An exact cardinality of ZERO is the third spelling of an absence claim, and reading only
|
|
237
|
+
// `absent` missed it: `{ kind: "net", urlContains: "/api/auth/sign-in", count: 0 }` says "this
|
|
238
|
+
// request was never made", and the call the buffer evicted is precisely the disproof. It was
|
|
239
|
+
// graded `yes` over a window known to have lost scarce evidence — the exact false green the two
|
|
240
|
+
// clauses above exist to prevent, reached by the one spelling they did not cover (#668).
|
|
241
|
+
//
|
|
242
|
+
// Only zero. `count: 2` asserts calls were MADE and found them; events aged out elsewhere in the
|
|
243
|
+
// buffer do not unmake a positive finding, which is the same reason a plain presence assertion is
|
|
244
|
+
// left alone here.
|
|
245
|
+
if (0 === predicate.count)
|
|
246
|
+
return true;
|
|
224
247
|
return true === predicate.absent;
|
|
225
248
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What to tell a caller whose assertion needs response bodies, given what the connected SDK can do.
|
|
3
|
+
*
|
|
4
|
+
* The remedy used to be one fixed sentence naming `captureNetworkBodies`. Reported from the field:
|
|
5
|
+
* six verdicts came back `unknown`/`outcome_unread` advising that setting against a pinned
|
|
6
|
+
* `@reticlehq/react` from before it existed. The reporter set the env var, restarted, got the
|
|
7
|
+
* identical message, and worked it out by elimination.
|
|
8
|
+
*
|
|
9
|
+
* The daemon is TOLD the page's SDK version at HELLO and threw it away after computing skew. Nothing
|
|
10
|
+
* new has to be observed to say the true thing — it only has to be kept.
|
|
11
|
+
*
|
|
12
|
+
* Same class as the version-skew remedy that named a React package on Vue projects: remedy text that
|
|
13
|
+
* does not check whether it applies. A wrong remedy is worse than none, because it is followed.
|
|
14
|
+
*/
|
|
15
|
+
/** The release that introduced `captureNetworkBodies` — see the browser observer's history. */
|
|
16
|
+
export declare const BODY_CAPTURE_MIN_VERSION = "2.4.0";
|
|
17
|
+
/**
|
|
18
|
+
* The remedy for this session, or the honest statement that there is none.
|
|
19
|
+
*
|
|
20
|
+
* An UNKNOWN version is treated as capable. A hand-wired connect reports none, and guessing "too
|
|
21
|
+
* old" there would hide a working fix from someone who could have used it; the opposite guess costs
|
|
22
|
+
* one wasted read. The asymmetry decides it.
|
|
23
|
+
*/
|
|
24
|
+
export declare function bodyCaptureRemedy(sdkVersion: string | undefined): string;
|
|
25
|
+
/**
|
|
26
|
+
* Refuse a body clause the session cannot answer, BEFORE the action is spent.
|
|
27
|
+
*
|
|
28
|
+
* Reported: an `act_and_wait` matched the right call and returned `verified: "no"` with "a matching
|
|
29
|
+
* call with no recorded body", because that project's `connect()` does not pass
|
|
30
|
+
* `captureNetworkBodies` — and the reporter could not change it, since it was not their project's
|
|
31
|
+
* config to edit for an unrelated verification task. The action bought nothing that was not knowable
|
|
32
|
+
* in advance, and on a drive that mutates state an action is not always repeatable. That is what
|
|
33
|
+
* makes this a pre-flight rather than a better failure message.
|
|
34
|
+
*
|
|
35
|
+
* Refuses ONLY on a DECLARED `captureBodies: false`. An SDK too old to announce the setting sends
|
|
36
|
+
* nothing, and refusing on silence would break every session predating the announcement for a clause
|
|
37
|
+
* many of them satisfy — the same asymmetry that makes an unknown VERSION count as capable.
|
|
38
|
+
*/
|
|
39
|
+
export declare function bodyClauseRefusal(predicate: unknown, session: {
|
|
40
|
+
captureBodies?: boolean | undefined;
|
|
41
|
+
sdkVersion?: string | undefined;
|
|
42
|
+
}): string | undefined;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What to tell a caller whose assertion needs response bodies, given what the connected SDK can do.
|
|
3
|
+
*
|
|
4
|
+
* The remedy used to be one fixed sentence naming `captureNetworkBodies`. Reported from the field:
|
|
5
|
+
* six verdicts came back `unknown`/`outcome_unread` advising that setting against a pinned
|
|
6
|
+
* `@reticlehq/react` from before it existed. The reporter set the env var, restarted, got the
|
|
7
|
+
* identical message, and worked it out by elimination.
|
|
8
|
+
*
|
|
9
|
+
* The daemon is TOLD the page's SDK version at HELLO and threw it away after computing skew. Nothing
|
|
10
|
+
* new has to be observed to say the true thing — it only has to be kept.
|
|
11
|
+
*
|
|
12
|
+
* Same class as the version-skew remedy that named a React package on Vue projects: remedy text that
|
|
13
|
+
* does not check whether it applies. A wrong remedy is worse than none, because it is followed.
|
|
14
|
+
*/
|
|
15
|
+
/** The release that introduced `captureNetworkBodies` — see the browser observer's history. */
|
|
16
|
+
export const BODY_CAPTURE_MIN_VERSION = '2.4.0';
|
|
17
|
+
/**
|
|
18
|
+
* Compare two dotted versions numerically.
|
|
19
|
+
*
|
|
20
|
+
* String comparison inverts exactly where it hurts: `'2.10.0' < '2.4.0'` lexically, so a NEWER SDK
|
|
21
|
+
* would be told it is too old and the working remedy withheld. A prerelease suffix is dropped rather
|
|
22
|
+
* than parsed — `2.13.0-rc.1` is 2.13 for this question, and nothing here needs finer.
|
|
23
|
+
*/
|
|
24
|
+
function isAtLeast(version, minimum) {
|
|
25
|
+
const parts = (v) => v
|
|
26
|
+
.split('-')[0]
|
|
27
|
+
?.split('.')
|
|
28
|
+
.map((n) => Number.parseInt(n, 10) || 0) ?? [];
|
|
29
|
+
const have = parts(version);
|
|
30
|
+
const want = parts(minimum);
|
|
31
|
+
for (let i = 0; i < Math.max(have.length, want.length); i += 1) {
|
|
32
|
+
const a = have[i] ?? 0;
|
|
33
|
+
const b = want[i] ?? 0;
|
|
34
|
+
if (a !== b)
|
|
35
|
+
return a > b;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
/** The sentence naming the setting — only ever produced for an SDK that HAS it. */
|
|
40
|
+
const ENABLE_IT = 'Turn it on where your app calls connect(): `reticle.connect({ captureNetworkBodies: true })`, ' +
|
|
41
|
+
'or for the Vite plugin `reticle({ captureNetworkBodies: true })` / VITE_RETICLE_CAPTURE_BODIES=1. ' +
|
|
42
|
+
'Then re-run the action.';
|
|
43
|
+
/**
|
|
44
|
+
* The remedy for this session, or the honest statement that there is none.
|
|
45
|
+
*
|
|
46
|
+
* An UNKNOWN version is treated as capable. A hand-wired connect reports none, and guessing "too
|
|
47
|
+
* old" there would hide a working fix from someone who could have used it; the opposite guess costs
|
|
48
|
+
* one wasted read. The asymmetry decides it.
|
|
49
|
+
*/
|
|
50
|
+
export function bodyCaptureRemedy(sdkVersion) {
|
|
51
|
+
if (sdkVersion !== undefined && !isAtLeast(sdkVersion, BODY_CAPTURE_MIN_VERSION)) {
|
|
52
|
+
return (`this page's SDK is ${sdkVersion}, and body capture needs ${BODY_CAPTURE_MIN_VERSION} or ` +
|
|
53
|
+
'newer — the setting does not exist in your version, so there is nothing to switch on. ' +
|
|
54
|
+
'Upgrade the SDK, or assert on something else this session can see.');
|
|
55
|
+
}
|
|
56
|
+
return ENABLE_IT;
|
|
57
|
+
}
|
|
58
|
+
/** Clause names that can only be answered from a recorded body. */
|
|
59
|
+
const BODY_CLAUSE_FIELDS = ['bodyContains', 'requestBodyContains'];
|
|
60
|
+
/** Does this predicate, or anything nested in it, ask about a body? */
|
|
61
|
+
function asksAboutBody(predicate) {
|
|
62
|
+
if (null === predicate || 'object' !== typeof predicate)
|
|
63
|
+
return false;
|
|
64
|
+
const node = predicate;
|
|
65
|
+
if (BODY_CLAUSE_FIELDS.some((f) => node[f] !== undefined))
|
|
66
|
+
return true;
|
|
67
|
+
// The reported shape is an `allOf` carrying route + console + the net clause that needs bodies.
|
|
68
|
+
// Checking only the top level would miss every realistic call.
|
|
69
|
+
const nested = node['predicates'] ?? node['predicate'];
|
|
70
|
+
if (Array.isArray(nested))
|
|
71
|
+
return nested.some(asksAboutBody);
|
|
72
|
+
return asksAboutBody(nested);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Refuse a body clause the session cannot answer, BEFORE the action is spent.
|
|
76
|
+
*
|
|
77
|
+
* Reported: an `act_and_wait` matched the right call and returned `verified: "no"` with "a matching
|
|
78
|
+
* call with no recorded body", because that project's `connect()` does not pass
|
|
79
|
+
* `captureNetworkBodies` — and the reporter could not change it, since it was not their project's
|
|
80
|
+
* config to edit for an unrelated verification task. The action bought nothing that was not knowable
|
|
81
|
+
* in advance, and on a drive that mutates state an action is not always repeatable. That is what
|
|
82
|
+
* makes this a pre-flight rather than a better failure message.
|
|
83
|
+
*
|
|
84
|
+
* Refuses ONLY on a DECLARED `captureBodies: false`. An SDK too old to announce the setting sends
|
|
85
|
+
* nothing, and refusing on silence would break every session predating the announcement for a clause
|
|
86
|
+
* many of them satisfy — the same asymmetry that makes an unknown VERSION count as capable.
|
|
87
|
+
*/
|
|
88
|
+
export function bodyClauseRefusal(predicate, session) {
|
|
89
|
+
if (false !== session.captureBodies)
|
|
90
|
+
return undefined;
|
|
91
|
+
if (!asksAboutBody(predicate))
|
|
92
|
+
return undefined;
|
|
93
|
+
return ('this assertion reads a request/response BODY, and this session is not recording them — it ' +
|
|
94
|
+
`would fail whatever the app did. Nothing ran, so no action was spent. ${bodyCaptureRemedy(session.sdkVersion)}`);
|
|
95
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Intent, type JournalAction } from '@reticlehq/core';
|
|
1
|
+
import { type Intent, type JournalAction } from '@reticlehq/core/artifacts';
|
|
2
2
|
/**
|
|
3
3
|
* Were `reticle_context` and `reticle_intent` used at all — and did using them change anything?
|
|
4
4
|
*
|
|
@@ -94,7 +94,7 @@ export declare function noteToolDispatched(session: {
|
|
|
94
94
|
capture?: CaptureLedger;
|
|
95
95
|
}, tool: string): void;
|
|
96
96
|
/** What one session's use of the two features looks like. Everything but `observed` is omitted when nothing was recorded. */
|
|
97
|
-
|
|
97
|
+
interface FeatureCapture {
|
|
98
98
|
/** False when this daemon recorded nothing for the session — "not watched", never "not used". */
|
|
99
99
|
observed: boolean;
|
|
100
100
|
/** The ledger dropped calls, so every count is a floor. Present only when true. */
|
|
@@ -148,3 +148,4 @@ export declare function foldFeatureCapture(input: {
|
|
|
148
148
|
/** The session's dispatched-action count now, so a run that ended on an action is not read as idle. */
|
|
149
149
|
finalActions: number;
|
|
150
150
|
}): FeatureCapture;
|
|
151
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IntentState, JournalVerdictEffectSchema, RUN_ESTABLISHED_CAP, } from '@reticlehq/core';
|
|
1
|
+
import { IntentState, JournalVerdictEffectSchema, RUN_ESTABLISHED_CAP, } from '@reticlehq/core/artifacts';
|
|
2
2
|
import { subjectOf } from '../runs/run-context.js';
|
|
3
3
|
import { ReticleTool } from '../tools/tool-names.js';
|
|
4
4
|
/**
|