@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type InstrumentationGap } from '@reticlehq/core';
|
|
1
|
+
import { type InstrumentationGap } from '@reticlehq/core/artifacts';
|
|
2
2
|
/**
|
|
3
3
|
* The gaps ONE action revealed, derived from facts the act path already holds.
|
|
4
4
|
*
|
|
@@ -75,6 +75,13 @@ export interface ActionInstrumentationFacts {
|
|
|
75
75
|
* finished, and a bare flag makes them read the same.
|
|
76
76
|
*/
|
|
77
77
|
openIntentCount?: number;
|
|
78
|
+
/**
|
|
79
|
+
* The page announced that this build has the `data-reticle-source` stamp switched OFF.
|
|
80
|
+
*
|
|
81
|
+
* Only `true` means anything: absent is "nobody said", which is every SDK predating the field and
|
|
82
|
+
* every hand-wired connect. See the HELLO field of the same name.
|
|
83
|
+
*/
|
|
84
|
+
sourceMappingDisabled?: boolean;
|
|
78
85
|
/**
|
|
79
86
|
* How long the OLDEST open intent has been owed, in ms.
|
|
80
87
|
*
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import { InstrumentationGapKind, dedupeGaps, instrumentationGap, } from '@reticlehq/core';
|
|
1
|
+
import { InstrumentationGapKind, dedupeGaps, instrumentationGap, } from '@reticlehq/core/artifacts';
|
|
2
2
|
export function gapsForAction(facts) {
|
|
3
3
|
const gaps = [];
|
|
4
4
|
// A red verdict names the control and cannot name the line that renders it. That is the round trip
|
|
5
5
|
// the agent is about to spend, and the one a build plugin removes permanently.
|
|
6
|
+
//
|
|
7
|
+
// ...unless the project TURNED THE STAMP OFF, which is a different situation with the opposite
|
|
8
|
+
// remedy. Reported from the field: a react-three-fiber app disabled `sourceMapping` because the
|
|
9
|
+
// stamp crashed it to a white screen, and every red verdict afterwards told the reader to install
|
|
10
|
+
// a build plugin that was already there. Only a definite `false` counts — an SDK that says nothing
|
|
11
|
+
// is unknown, and reading silence as an opt-out would mute the honest gap for every app that
|
|
12
|
+
// simply has no source mapping at all.
|
|
6
13
|
if (!facts.pass && facts.source === undefined) {
|
|
7
|
-
|
|
14
|
+
const off = true === facts.sourceMappingDisabled;
|
|
15
|
+
gaps.push(instrumentationGap(off ? InstrumentationGapKind.SOURCE_MAPPING_OFF : InstrumentationGapKind.NO_SOURCE_MAPPING, off
|
|
16
|
+
? 'source mapping is turned off for this build, so no element carries data-reticle-source'
|
|
17
|
+
: 'the control that was driven carries no source mapping', 'this verdict can name the control but not the file and line that render it, so finding the code is a separate search', { ...(facts.ref === undefined ? {} : { ref: facts.ref }) }));
|
|
8
18
|
}
|
|
9
19
|
// Asked about state, and there is no state channel to answer from. Deliberately UNLOCATED: a store
|
|
10
20
|
// is registered once at app setup, nowhere near the control that was driven, and nothing in this
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InstrumentationGap } from '@reticlehq/core';
|
|
1
|
+
import type { InstrumentationGap } from '@reticlehq/core/artifacts';
|
|
2
2
|
/**
|
|
3
3
|
* Of the controls this session drove, how many could Reticle fully observe?
|
|
4
4
|
*
|
|
@@ -19,7 +19,7 @@ import type { InstrumentationGap } from '@reticlehq/core';
|
|
|
19
19
|
* happened to be driven would put the number on the wrong thing and make it move for reasons the
|
|
20
20
|
* agent cannot act on. Those gaps are reported alongside, not divided into.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
interface Observability {
|
|
23
23
|
/** Distinct controls driven this session. */
|
|
24
24
|
driven: number;
|
|
25
25
|
/** How many of them Reticle could fully observe. */
|
|
@@ -28,10 +28,11 @@ export interface Observability {
|
|
|
28
28
|
percent?: number;
|
|
29
29
|
}
|
|
30
30
|
export declare function observabilityOf(drivenRefs: Iterable<string>, gaps: readonly InstrumentationGap[]): Observability;
|
|
31
|
-
|
|
31
|
+
interface CoverageRegression {
|
|
32
32
|
was: number;
|
|
33
33
|
now: number;
|
|
34
34
|
}
|
|
35
35
|
export declare function coverageRegressed(best: {
|
|
36
36
|
percent: number;
|
|
37
37
|
} | undefined, current: Observability): CoverageRegression | undefined;
|
|
38
|
+
export {};
|
|
@@ -47,12 +47,12 @@ export type ToolTier = (typeof ToolTier)[keyof typeof ToolTier];
|
|
|
47
47
|
/** Which surface would have put this tool in front of an agent, read off that surface's own set. */
|
|
48
48
|
export declare function tierOfTool(tool: string): ToolTier;
|
|
49
49
|
/** How many tools fell in each tier on one side of the called/never-called split. */
|
|
50
|
-
|
|
50
|
+
interface TierCounts {
|
|
51
51
|
default: number;
|
|
52
52
|
extended: number;
|
|
53
53
|
runOnly: number;
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
interface ToolHitRate {
|
|
56
56
|
/** False when this session carries no ledger — "not watched", never "nothing was called". */
|
|
57
57
|
observed: boolean;
|
|
58
58
|
/** The four-plus-two cells: every tier, called and never-called. Omitted when not observed. */
|
|
@@ -84,3 +84,4 @@ export declare function foldToolHitRate(input: {
|
|
|
84
84
|
calls: ReadonlyMap<string, number> | undefined;
|
|
85
85
|
allTools: readonly string[];
|
|
86
86
|
}): ToolHitRate;
|
|
87
|
+
export {};
|
|
@@ -16,6 +16,8 @@ export declare function bodiesNotCaptured(calls: {
|
|
|
16
16
|
method?: string;
|
|
17
17
|
requestBody?: string;
|
|
18
18
|
responseBody?: string;
|
|
19
|
-
}[]
|
|
19
|
+
}[],
|
|
20
|
+
/** The page SDK's version, so the remedy can check whether it applies. See body-capture-remedy. */
|
|
21
|
+
sdkVersion?: string): {
|
|
20
22
|
bodiesNotCaptured?: string;
|
|
21
23
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bodyCaptureRemedy } from './body-capture-remedy.js';
|
|
1
2
|
/** Methods that normally carry a payload — the ones where a missing body is worth explaining. */
|
|
2
3
|
const BODY_BEARING_METHODS = new Set(['POST', 'PUT', 'PATCH']);
|
|
3
4
|
/**
|
|
@@ -14,7 +15,9 @@ const BODY_BEARING_METHODS = new Set(['POST', 'PUT', 'PATCH']);
|
|
|
14
15
|
* anywhere in the result, so an app that already captures bodies pays nothing and the field's
|
|
15
16
|
* PRESENCE is the warning.
|
|
16
17
|
*/
|
|
17
|
-
export function bodiesNotCaptured(calls
|
|
18
|
+
export function bodiesNotCaptured(calls,
|
|
19
|
+
/** The page SDK's version, so the remedy can check whether it applies. See body-capture-remedy. */
|
|
20
|
+
sdkVersion) {
|
|
18
21
|
const bodyBearing = calls.filter((c) => BODY_BEARING_METHODS.has((c.method ?? '').toUpperCase()));
|
|
19
22
|
if (0 === bodyBearing.length)
|
|
20
23
|
return {};
|
|
@@ -32,6 +35,7 @@ export function bodiesNotCaptured(calls) {
|
|
|
32
35
|
// config they do not have. Remediation that does not apply to the reader's stack is worse than
|
|
33
36
|
// none: it sends them to edit a file that is not there and reads as a tool that does not know
|
|
34
37
|
// what it is looking at.
|
|
35
|
-
bodiesNotCaptured: 'request/response bodies are NOT being recorded, so an absent body here means UNSEEN, not
|
|
38
|
+
bodiesNotCaptured: 'request/response bodies are NOT being recorded, so an absent body here means UNSEEN, not ' +
|
|
39
|
+
`empty — the payload of these calls was never inspected. ${bodyCaptureRemedy(sdkVersion)}`,
|
|
36
40
|
};
|
|
37
41
|
}
|
|
@@ -38,6 +38,15 @@ export interface UnsettledWindow {
|
|
|
38
38
|
* agent that never chose that wait cannot be expected to recognise it in a timeout about it.
|
|
39
39
|
*/
|
|
40
40
|
export declare function describeWaitTarget(predicate: Predicate): string;
|
|
41
|
+
/**
|
|
42
|
+
* Did the assertion name a request that is still open?
|
|
43
|
+
*
|
|
44
|
+
* `waitForPredicate` reports a miss ("no request to /register") the moment the budget ends, even
|
|
45
|
+
* when that same POST is sitting in `stillInFlight`. Grading that miss `no` is a race against the
|
|
46
|
+
* backend, not a defect. Matching is the discriminator: an unrelated poll left open does not pardon
|
|
47
|
+
* a named URL that never started.
|
|
48
|
+
*/
|
|
49
|
+
export declare function namedNetIsInFlight(predicate: Predicate, stillInFlight: readonly string[]): boolean;
|
|
41
50
|
/**
|
|
42
51
|
* The `because` sentence for an unsettled verdict: the wait, the observation, the next move.
|
|
43
52
|
*
|
|
@@ -52,6 +52,51 @@ export function describeWaitTarget(predicate) {
|
|
|
52
52
|
return `the absence of ${describeWaitTarget(predicate.predicate)}`;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Did the assertion name a request that is still open?
|
|
57
|
+
*
|
|
58
|
+
* `waitForPredicate` reports a miss ("no request to /register") the moment the budget ends, even
|
|
59
|
+
* when that same POST is sitting in `stillInFlight`. Grading that miss `no` is a race against the
|
|
60
|
+
* backend, not a defect. Matching is the discriminator: an unrelated poll left open does not pardon
|
|
61
|
+
* a named URL that never started.
|
|
62
|
+
*/
|
|
63
|
+
export function namedNetIsInFlight(predicate, stillInFlight) {
|
|
64
|
+
if (0 === stillInFlight.length)
|
|
65
|
+
return false;
|
|
66
|
+
const nets = [];
|
|
67
|
+
const walk = (p) => {
|
|
68
|
+
switch (p.kind) {
|
|
69
|
+
case PredicateKind.ALL_OF:
|
|
70
|
+
case PredicateKind.ANY_OF:
|
|
71
|
+
for (const child of p.predicates)
|
|
72
|
+
walk(child);
|
|
73
|
+
return;
|
|
74
|
+
case PredicateKind.NET: {
|
|
75
|
+
if (undefined === p.urlContains || 0 === p.urlContains.length)
|
|
76
|
+
return;
|
|
77
|
+
nets.push({
|
|
78
|
+
...(undefined === p.method ? {} : { method: p.method }),
|
|
79
|
+
urlContains: p.urlContains,
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
default:
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
walk(predicate);
|
|
88
|
+
return nets.some((net) => stillInFlight.some((label) => inFlightLabelMatchesNet(label, net)));
|
|
89
|
+
}
|
|
90
|
+
function inFlightLabelMatchesNet(label, net) {
|
|
91
|
+
const space = label.indexOf(' ');
|
|
92
|
+
if (space <= 0)
|
|
93
|
+
return false;
|
|
94
|
+
const method = label.slice(0, space);
|
|
95
|
+
const url = label.slice(space + 1);
|
|
96
|
+
if (undefined !== net.method && method.toUpperCase() !== net.method.toUpperCase())
|
|
97
|
+
return false;
|
|
98
|
+
return url.includes(net.urlContains);
|
|
99
|
+
}
|
|
55
100
|
/**
|
|
56
101
|
* The `because` sentence for an unsettled verdict: the wait, the observation, the next move.
|
|
57
102
|
*
|
|
@@ -24,6 +24,14 @@ interface VerifiedInputs {
|
|
|
24
24
|
* confidently-wrong-by-accident this clause exists to remove.
|
|
25
25
|
*/
|
|
26
26
|
observationLost?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The URL the session was on when observation was lost.
|
|
29
|
+
*
|
|
30
|
+
* Only meaningful with `observationLost`. A route that 500s tears the page down mid-wait; naming
|
|
31
|
+
* the last URL in the verdict is the difference between "install problem" and "the page died on
|
|
32
|
+
* /X" (#808). Absent when we never knew, which must not invent a route.
|
|
33
|
+
*/
|
|
34
|
+
lastUrl?: string;
|
|
27
35
|
honesty: HonestyBlock;
|
|
28
36
|
/**
|
|
29
37
|
* Set when the assertion could not be EVALUATED at all — an under-specified call, or nothing
|
|
@@ -36,6 +44,15 @@ interface VerifiedInputs {
|
|
|
36
44
|
* ones are floored at the act's cursor and cannot be satisfied by the past.
|
|
37
45
|
*/
|
|
38
46
|
alreadyTrue?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The `alreadyTrue` match was against a HIDDEN element/text — DOM presence, not something a person
|
|
49
|
+
* could see on screen. `text`/`element` predicates match presence by default (`visible: true` is
|
|
50
|
+
* opt-in), so a dialog's content mounted-but-hidden before the click reads identically to content
|
|
51
|
+
* that was genuinely already showing, and named neither fact (#889). Only changes `because`; the
|
|
52
|
+
* verdict itself is unchanged; a hidden pre-existing match is exactly as unfalsifiable as a visible
|
|
53
|
+
* one.
|
|
54
|
+
*/
|
|
55
|
+
alreadyTrueHiddenMatch?: boolean;
|
|
39
56
|
/** Cross-channel disagreements found in the action's window. */
|
|
40
57
|
contradictions?: readonly {
|
|
41
58
|
kind: string;
|
|
@@ -43,7 +60,7 @@ interface VerifiedInputs {
|
|
|
43
60
|
/** Did a real frame flush before the wait gave up? */
|
|
44
61
|
settled?: boolean;
|
|
45
62
|
/**
|
|
46
|
-
* The caller
|
|
63
|
+
* The caller named a consequence before acting — an `until`/`predicate` of its own,
|
|
47
64
|
* rather than the default "wait for the page to go idle".
|
|
48
65
|
*
|
|
49
66
|
* This is the epistemic core of the tool: a declaration made before the action cannot be
|
|
@@ -61,6 +78,12 @@ interface VerifiedInputs {
|
|
|
61
78
|
* outright. Fixing a false negative must not open a path to a false positive.
|
|
62
79
|
*/
|
|
63
80
|
declaredConsequence?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* A net the caller named is still open. `waitForPredicate` reports that as `pass: false` ("no
|
|
83
|
+
* request to …") the instant the budget ends, which made a cold backend `assertion_failed` and a
|
|
84
|
+
* warm one `proved`. Absence of a settle is not evidence the request never happened — see #669.
|
|
85
|
+
*/
|
|
86
|
+
namedRequestInFlight?: boolean;
|
|
64
87
|
/**
|
|
65
88
|
* A write in this window answered `202 Accepted` — the server took the request and has NOT
|
|
66
89
|
* finished processing it.
|
|
@@ -76,6 +99,14 @@ interface VerifiedInputs {
|
|
|
76
99
|
* Empty means nothing went unread.
|
|
77
100
|
*/
|
|
78
101
|
outcomeUnread?: readonly string[];
|
|
102
|
+
/**
|
|
103
|
+
* The page SDK's version, so the unread-body remedy can check whether it applies.
|
|
104
|
+
*
|
|
105
|
+
* This clause is the highest-traffic producer of that advice — every 2xx write whose body went
|
|
106
|
+
* unread lands here — and it inlined the setting unconditionally, which is what a reporter saw six
|
|
107
|
+
* times at a repo pinned below the release that introduced it. See body-capture-remedy.ts.
|
|
108
|
+
*/
|
|
109
|
+
sdkVersion?: string | undefined;
|
|
79
110
|
/**
|
|
80
111
|
* The declared consequence that held does not depend on the response body — an exact string
|
|
81
112
|
* rendered, a store path, a signal, a route, or an element located by role / name / testid. See
|
package/dist/honesty/verified.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bodyCaptureRemedy } from './body-capture-remedy.js';
|
|
2
|
+
import { ContradictionKind, MUTATING_METHODS, Verified, VerifiedReason, isAbsenceDerived, isAdvisory, } from '@reticlehq/core';
|
|
2
3
|
import { HonestyGrade } from './honesty.js';
|
|
3
4
|
import { unsettledBecause } from './unsettled.js';
|
|
5
|
+
import { pageTornDownWhileOn } from '../session/no-session-diagnosis.js';
|
|
4
6
|
export function decideVerified(inputs) {
|
|
5
7
|
const { pass, honesty, contradictions = [], settled, outcomePending, outcomeUnread } = inputs;
|
|
6
8
|
/** The caller named a consequence before acting and it held — see `declaredConsequence`. */
|
|
@@ -22,10 +24,13 @@ export function decideVerified(inputs) {
|
|
|
22
24
|
//
|
|
23
25
|
// UNKNOWN, for the same reason `unclean_capture` is: the evidence is ABSENT, not negative.
|
|
24
26
|
if (true === inputs.observationLost) {
|
|
27
|
+
const lost = undefined === inputs.lastUrl || '' === inputs.lastUrl
|
|
28
|
+
? 'the tab disconnected'
|
|
29
|
+
: pageTornDownWhileOn(inputs.lastUrl);
|
|
25
30
|
return {
|
|
26
31
|
verified: Verified.UNKNOWN,
|
|
27
32
|
verifiedReason: VerifiedReason.OBSERVATION_LOST,
|
|
28
|
-
because:
|
|
33
|
+
because: `${lost} while this action was being observed, so its outcome was never ` +
|
|
29
34
|
'seen — this says nothing about the app. Call reticle_sessions for the current session ' +
|
|
30
35
|
'(a reloaded tab keeps its id; a closed one is gone) and repeat the action if it is safe ' +
|
|
31
36
|
'to repeat',
|
|
@@ -49,6 +54,20 @@ export function decideVerified(inputs) {
|
|
|
49
54
|
'`until` naming what the action should cause (a signal, a request, a route, or store state)',
|
|
50
55
|
};
|
|
51
56
|
}
|
|
57
|
+
// ABOVE the failure clause, same reason as observationLost: absence is not evidence.
|
|
58
|
+
//
|
|
59
|
+
// A `{ net, urlContains }` miss while that URL is still in `stillInFlight` is a window that
|
|
60
|
+
// closed early, not a request that never happened. The same result object already named the
|
|
61
|
+
// in-flight POST; calling it `assertion_failed` contradicted that field and flipped green on a
|
|
62
|
+
// warm backend. UNKNOWN, and the sentence says to re-check once the request lands.
|
|
63
|
+
if (false === pass && true === inputs.namedRequestInFlight) {
|
|
64
|
+
return {
|
|
65
|
+
verified: Verified.UNKNOWN,
|
|
66
|
+
verifiedReason: VerifiedReason.WINDOW_CLOSED_EARLY,
|
|
67
|
+
because: unsettledBecause('the request this assertion named was still in flight when the window closed, so the ' +
|
|
68
|
+
'consequence was not disproved — it had not finished', inputs.unsettled),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
52
71
|
// A failed assertion is the most actionable fact there is; it leads — including over
|
|
53
72
|
// `alreadyTrue`, because a condition that held before AND fails now is a real regression.
|
|
54
73
|
if (false === pass) {
|
|
@@ -72,7 +91,11 @@ export function decideVerified(inputs) {
|
|
|
72
91
|
// every `no` verdict in the field. See ABSENCE_DERIVED_CONTRADICTIONS for why a false negative
|
|
73
92
|
// costs more than it looks: it makes an agent redo work that succeeded, or stop believing the
|
|
74
93
|
// verdict channel, which is the product.
|
|
75
|
-
|
|
94
|
+
// Advisory findings are dropped from the decision entirely, at both tiers below. They are true and
|
|
95
|
+
// they ride out in `contradictions`; what they are not is evidence about the consequence the
|
|
96
|
+
// caller declared, because they concern traffic the assertion never named (#673).
|
|
97
|
+
const deciding = contradictions.filter((c) => !isAdvisory(c.kind));
|
|
98
|
+
const observed = deciding.filter((c) => !isAbsenceDerived(c.kind));
|
|
76
99
|
if (observed.length > 0) {
|
|
77
100
|
const kinds = observed.map((c) => c.kind).join(', ');
|
|
78
101
|
return {
|
|
@@ -99,7 +122,7 @@ export function decideVerified(inputs) {
|
|
|
99
122
|
const settlementOnly = declaredHeld &&
|
|
100
123
|
inputs.unsettled !== undefined &&
|
|
101
124
|
!openWrite &&
|
|
102
|
-
|
|
125
|
+
deciding.every((c) => c.kind === ContradictionKind.REQUEST_NEVER_SETTLED);
|
|
103
126
|
// `signal-without-consequence` gets its own sentence, because the generic one is FALSE for it.
|
|
104
127
|
//
|
|
105
128
|
// That sentence says the window "closed before the app finished" and then explains that a poll or
|
|
@@ -108,8 +131,8 @@ export function decideVerified(inputs) {
|
|
|
108
131
|
// explanation that told the agent to wait longer or assert something else, when the fact to act on
|
|
109
132
|
// was that the app announced a consequence it did not deliver. A right verdict with a wrong reason
|
|
110
133
|
// sends an agent to the wrong place, which costs as much as the wrong verdict did.
|
|
111
|
-
const signalOnly =
|
|
112
|
-
|
|
134
|
+
const signalOnly = deciding.length > 0 &&
|
|
135
|
+
deciding.every((c) => c.kind === ContradictionKind.SIGNAL_WITHOUT_CONSEQUENCE);
|
|
113
136
|
if (signalOnly) {
|
|
114
137
|
return {
|
|
115
138
|
verified: Verified.UNKNOWN,
|
|
@@ -121,8 +144,8 @@ export function decideVerified(inputs) {
|
|
|
121
144
|
'the store holds) before trusting it',
|
|
122
145
|
};
|
|
123
146
|
}
|
|
124
|
-
if (
|
|
125
|
-
const kinds =
|
|
147
|
+
if (deciding.length > 0 && !settlementOnly) {
|
|
148
|
+
const kinds = deciding.map((c) => c.kind).join(', ');
|
|
126
149
|
return {
|
|
127
150
|
verified: Verified.UNKNOWN,
|
|
128
151
|
// NOT `unsettled`: this clause fires whether or not the page went idle, and naming it after
|
|
@@ -171,7 +194,10 @@ export function decideVerified(inputs) {
|
|
|
171
194
|
return {
|
|
172
195
|
verified: true === settled ? Verified.NO_FAULT : Verified.UNKNOWN,
|
|
173
196
|
verifiedReason: VerifiedReason.ALREADY_TRUE,
|
|
174
|
-
because: 'the declared consequence was already true before this action, so it proves nothing about it — assert something the action CHANGES (a signal, a request, a route, or store state)'
|
|
197
|
+
because: 'the declared consequence was already true before this action, so it proves nothing about it — assert something the action CHANGES (a signal, a request, a route, or store state)' +
|
|
198
|
+
(true === inputs.alreadyTrueHiddenMatch
|
|
199
|
+
? '. That prior match was against a HIDDEN element — this predicate checks DOM presence, not visibility, by default; add `visible: true` if you meant "this is showing", not merely "this exists"'
|
|
200
|
+
: ''),
|
|
175
201
|
};
|
|
176
202
|
}
|
|
177
203
|
// Dirty capture is NOT failure. The layer could not see part of the window, so any green is a
|
|
@@ -259,7 +285,8 @@ export function decideVerified(inputs) {
|
|
|
259
285
|
verified: Verified.UNKNOWN,
|
|
260
286
|
verifiedReason: VerifiedReason.OUTCOME_UNREAD,
|
|
261
287
|
because: `a write returned 2xx with a response body that was never recorded (${outcomeUnread.join('; ')}), so its outcome is unread` +
|
|
262
|
-
' — a 200 describes the transport, not the result (a batch reports per-item failures in the body, and every GraphQL error is a 200).
|
|
288
|
+
' — a 200 describes the transport, not the result (a batch reports per-item failures in the body, and every GraphQL error is a 200). ' +
|
|
289
|
+
bodyCaptureRemedy(inputs.sdkVersion),
|
|
263
290
|
};
|
|
264
291
|
}
|
|
265
292
|
// Never settled: the page may still be moving, so the observation window may have closed early.
|
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
import { type ImpactCounts, type ImpactDefect, type ImpactScope, type ImpactSnapshot } from '@reticlehq/core';
|
|
2
|
-
export interface ImpactPaths {
|
|
3
|
-
project: string;
|
|
4
|
-
global: string;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Where this project's dashboard lives, if `reticle link` recorded one.
|
|
8
|
-
*
|
|
9
|
-
* Read from the link file rather than derived from the API url, because the API origin and the
|
|
10
|
-
* console origin are different hosts in every deployment that is not a laptop. Synchronous and
|
|
11
|
-
* best-effort, like every other read in this file: a missing or malformed link file means no link
|
|
12
|
-
* in the HUD, never a failed tool call.
|
|
13
|
-
*/
|
|
14
|
-
export declare function readDashboardUrl(reticleRoot: string): string | undefined;
|
|
1
|
+
import { type ImpactCounts, type ImpactDefect, type AccountState, type ImpactScope, type ImpactSnapshot } from '@reticlehq/core';
|
|
15
2
|
/** Read a scope, tolerating absence and corruption: a broken file starts over rather than throwing. */
|
|
16
3
|
export declare function readScope(path: string, now: number): ImpactScope;
|
|
17
4
|
/** YYYY-MM-DD in local time - the day boundary a person recognises, not UTC's. */
|
|
@@ -46,6 +33,10 @@ export declare class ImpactStore {
|
|
|
46
33
|
reticleRoot: string;
|
|
47
34
|
projectName?: string;
|
|
48
35
|
now?: () => number;
|
|
36
|
+
/** Where `~/.reticle` lives. Defaults to the real home; overridden so the shared scope is testable. */
|
|
37
|
+
globalRoot?: string;
|
|
38
|
+
/** Reads whether this machine is signed in. Injected so the store stays testable and pure-ish. */
|
|
39
|
+
account?: () => AccountState;
|
|
49
40
|
});
|
|
50
41
|
/** Notified after every fold, so the HUD can be pushed a fresh snapshot. */
|
|
51
42
|
onChange(fn: () => void): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
+
import { readAccountState } from '../cloud/account-state.js';
|
|
4
5
|
import { ReticleDir, IMPACT_DAILY_BUCKETS, IMPACT_DEFECT_LIMIT, IMPACT_SCHEMA_VERSION, ImpactScopeSchema, addImpactCounts, emptyImpactCounts, emptyImpactRecords, estimateImpactSavings, } from '@reticlehq/core';
|
|
5
6
|
/**
|
|
6
7
|
* Where Reticle keeps the record of what it has done for you.
|
|
@@ -18,12 +19,16 @@ const WRITE_DEBOUNCE_MS = 800;
|
|
|
18
19
|
const CLOUD_LINK_FILE = 'cloud.json';
|
|
19
20
|
/**
|
|
20
21
|
* `reticleRoot` is the project's own `.reticle` directory (what every other store here is handed);
|
|
21
|
-
* the global scope
|
|
22
|
+
* the global scope lives beside the daemon's own state in `~/.reticle`.
|
|
23
|
+
*
|
|
24
|
+
* `globalRoot` defaults to the home directory and exists to be overridden. Reaching for `homedir()`
|
|
25
|
+
* implicitly made the machine-wide scope untestable — the one scope with a concurrency bug in it —
|
|
26
|
+
* so the seam is the fix's precondition, not a test affordance.
|
|
22
27
|
*/
|
|
23
|
-
function impactPaths(reticleRoot) {
|
|
28
|
+
function impactPaths(reticleRoot, globalRoot) {
|
|
24
29
|
return {
|
|
25
30
|
project: join(reticleRoot, ReticleDir.IMPACT_FILE),
|
|
26
|
-
global: join(
|
|
31
|
+
global: join(globalRoot, ReticleDir.ROOT, ReticleDir.IMPACT_FILE),
|
|
27
32
|
};
|
|
28
33
|
}
|
|
29
34
|
/**
|
|
@@ -34,7 +39,7 @@ function impactPaths(reticleRoot) {
|
|
|
34
39
|
* best-effort, like every other read in this file: a missing or malformed link file means no link
|
|
35
40
|
* in the HUD, never a failed tool call.
|
|
36
41
|
*/
|
|
37
|
-
|
|
42
|
+
function readDashboardUrl(reticleRoot) {
|
|
38
43
|
try {
|
|
39
44
|
const raw = JSON.parse(readFileSync(join(reticleRoot, CLOUD_LINK_FILE), 'utf8'));
|
|
40
45
|
if ('object' !== typeof raw || null === raw)
|
|
@@ -157,10 +162,23 @@ export class ImpactStore {
|
|
|
157
162
|
#dashboardUrl;
|
|
158
163
|
#project;
|
|
159
164
|
#global;
|
|
165
|
+
/**
|
|
166
|
+
* Deltas folded since the last machine-wide write, kept so the write can be a MERGE.
|
|
167
|
+
*
|
|
168
|
+
* The project scope has one writer and can be written wholesale. The machine-wide file is shared
|
|
169
|
+
* by every daemon on the box, so writing an in-memory copy taken at construction erases whatever a
|
|
170
|
+
* sibling wrote in between — which is what made the machine streak read LOWER than a single
|
|
171
|
+
* project's. Replaying these onto whatever is on disk at flush time is what makes the write additive.
|
|
172
|
+
*/
|
|
173
|
+
#pendingGlobal = [];
|
|
160
174
|
#timer;
|
|
161
175
|
#onChange;
|
|
176
|
+
#account;
|
|
162
177
|
constructor(opts) {
|
|
163
|
-
this.#paths = impactPaths(opts.reticleRoot);
|
|
178
|
+
this.#paths = impactPaths(opts.reticleRoot, opts.globalRoot ?? homedir());
|
|
179
|
+
// Resolved per snapshot, not cached: a user who runs `reticle login` in another terminal must
|
|
180
|
+
// see the HUD change without restarting the daemon that is watching their app.
|
|
181
|
+
this.#account = opts.account ?? (() => readAccountState(homedir(), process.env));
|
|
164
182
|
this.#now = opts.now ?? (() => Date.now());
|
|
165
183
|
this.#projectName = opts.projectName;
|
|
166
184
|
this.#dashboardUrl = readDashboardUrl(opts.reticleRoot);
|
|
@@ -175,7 +193,10 @@ export class ImpactStore {
|
|
|
175
193
|
record(delta, meta = {}) {
|
|
176
194
|
const now = this.#now();
|
|
177
195
|
this.#project = applyDelta(this.#project, delta, now, meta);
|
|
196
|
+
// Folded in memory so a reader sees it immediately, AND buffered so the durable write can replay
|
|
197
|
+
// it onto a file a sibling may have moved on since. The in-memory copy is rebased at flush.
|
|
178
198
|
this.#global = applyDelta(this.#global, delta, now, meta);
|
|
199
|
+
this.#pendingGlobal.push({ delta, now, meta });
|
|
179
200
|
this.#scheduleFlush();
|
|
180
201
|
this.#onChange?.();
|
|
181
202
|
}
|
|
@@ -189,6 +210,7 @@ export class ImpactStore {
|
|
|
189
210
|
snap.projectName = this.#projectName;
|
|
190
211
|
if (this.#dashboardUrl !== undefined)
|
|
191
212
|
snap.dashboardUrl = this.#dashboardUrl;
|
|
213
|
+
snap.account = this.#account();
|
|
192
214
|
return snap;
|
|
193
215
|
}
|
|
194
216
|
/** Write now (process exit, tests). */
|
|
@@ -197,8 +219,29 @@ export class ImpactStore {
|
|
|
197
219
|
clearTimeout(this.#timer);
|
|
198
220
|
this.#timer = undefined;
|
|
199
221
|
}
|
|
222
|
+
// One writer: the in-memory copy IS the truth.
|
|
200
223
|
writeScope(this.#paths.project, this.#project);
|
|
201
|
-
|
|
224
|
+
this.#flushGlobal();
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Publish this store's contribution to the machine-wide scope WITHOUT discarding anyone else's.
|
|
228
|
+
*
|
|
229
|
+
* Re-reads what is on disk and replays only the deltas recorded since the last write, so a sibling
|
|
230
|
+
* daemon's day survives. The merged result is adopted in memory too, which is what keeps the HUD's
|
|
231
|
+
* "everything on this machine" honest rather than showing this process's private view forever.
|
|
232
|
+
*
|
|
233
|
+
* ponytail: read-modify-write, not locked. Two daemons flushing inside the same read→rename window
|
|
234
|
+
* can still drop one batch — far rarer than the every-second-flush loss this replaces, and it costs
|
|
235
|
+
* one debounce window rather than a whole history. Upgrade to a lock file (exclusive `wx` create
|
|
236
|
+
* plus a stale timeout) if the counters are ever load-bearing for anything but the HUD.
|
|
237
|
+
*/
|
|
238
|
+
#flushGlobal() {
|
|
239
|
+
if (0 === this.#pendingGlobal.length)
|
|
240
|
+
return;
|
|
241
|
+
const merged = this.#pendingGlobal.reduce((scope, p) => applyDelta(scope, p.delta, p.now, p.meta), readScope(this.#paths.global, this.#now()));
|
|
242
|
+
writeScope(this.#paths.global, merged);
|
|
243
|
+
this.#global = merged;
|
|
244
|
+
this.#pendingGlobal = [];
|
|
202
245
|
}
|
|
203
246
|
#scheduleFlush() {
|
|
204
247
|
if (this.#timer !== undefined)
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { resolveProjectCloud } from './cloud/cloud-config.js';
|
|
5
5
|
import { startSyncDaemon } from './cloud/sync-daemon.js';
|
|
6
|
-
import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, projectCandidates, } from '@reticlehq/core';
|
|
6
|
+
import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, projectCandidates, } from '@reticlehq/core/artifacts';
|
|
7
7
|
import { discoverProjectConfigs } from './cli/config-discovery.js';
|
|
8
8
|
import { projectCandidatesFrom, resolveArtifactRoot, } from './project/artifact-root.js';
|
|
9
9
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type RedactionPolicy, type ReticleEvent } from '@reticlehq/core';
|
|
1
|
+
import { URL_RAW, type RedactionPolicy, type ReticleEvent } from '@reticlehq/core';
|
|
2
2
|
/**
|
|
3
3
|
* CDP-authoritative network detail. Wherever the daemon has a CDP connection — whether it launched
|
|
4
4
|
* the browser or attached to one someone else started — `page.on('response')` sees the FULL response:
|
|
@@ -9,7 +9,23 @@ import { type RedactionPolicy, type ReticleEvent } from '@reticlehq/core';
|
|
|
9
9
|
* `page.on` attachment is thin Playwright glue (exercised e2e, like the pool launcher).
|
|
10
10
|
*/
|
|
11
11
|
export interface NetworkDetail {
|
|
12
|
+
/**
|
|
13
|
+
* The URL as the agent reads it, redacted by the same rule the in-page observer applies.
|
|
14
|
+
*
|
|
15
|
+
* It arrives raw from the driver, exactly like the headers and the request body below, and it
|
|
16
|
+
* carries credentials just as routinely: a presigned upload, an OAuth callback, a single-use reset
|
|
17
|
+
* link. Redacting the other two and not this one left the one field nobody had to parse to read.
|
|
18
|
+
*/
|
|
12
19
|
url: string;
|
|
20
|
+
/**
|
|
21
|
+
* The URL before redaction, present only when redaction rewrote it.
|
|
22
|
+
*
|
|
23
|
+
* The grader-only match haystack, the same field and the same contract as on a NET_REQUEST: it is
|
|
24
|
+
* how `urlContains` still matches a public path segment the heuristic rewrote, and how the merge
|
|
25
|
+
* below pairs this detail with the request the SDK reported. `withoutUrlRaw` strips it before any
|
|
26
|
+
* event is rendered to an agent.
|
|
27
|
+
*/
|
|
28
|
+
[URL_RAW]?: string;
|
|
13
29
|
method?: string;
|
|
14
30
|
status: number;
|
|
15
31
|
headers: Record<string, string>;
|
|
@@ -24,6 +40,15 @@ export interface NetworkDetail {
|
|
|
24
40
|
* window.fetch frame at all), rewrites requests invisibly. Available on the DRIVE path only.
|
|
25
41
|
*/
|
|
26
42
|
requestBody?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Present, and only ever `true`, when the bound above cut the captured body short.
|
|
45
|
+
*
|
|
46
|
+
* The same field and the same contract the in-page observer already emits. A shortened body an
|
|
47
|
+
* agent cannot tell apart from a whole one is the false green this project exists to prevent: a
|
|
48
|
+
* `net` assertion over a payload that was cut reads as an absence of the thing that was cut off.
|
|
49
|
+
* Omitted when the whole body fits, so the caveat means something when it appears.
|
|
50
|
+
*/
|
|
51
|
+
requestBodyTruncated?: boolean;
|
|
27
52
|
/**
|
|
28
53
|
* The document that ISSUED the request, which is what identifies the session it belongs to.
|
|
29
54
|
*
|