@reticlehq/server 2.3.0 → 2.5.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 +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +105 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse.d.ts +143 -0
- package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
- package/dist/{cli-port.js → cli/cli-port.js} +29 -8
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +182 -93
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.d.ts +0 -6
- package/dist/flows/decision.js +64 -8
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +67 -13
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.d.ts +17 -0
- package/dist/init/capabilities.js +54 -0
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +26 -0
- package/dist/init/detect.js +78 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +39 -0
- package/dist/init/next-patch.js +196 -0
- package/dist/init/node-io.js +48 -6
- package/dist/init/patch-kind.d.ts +20 -0
- package/dist/init/patch-kind.js +10 -0
- package/dist/init/plan-framework.d.ts +50 -0
- package/dist/init/plan-framework.js +341 -0
- package/dist/init/plan.d.ts +119 -6
- package/dist/init/plan.js +230 -131
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +22 -0
- package/dist/init/run.js +393 -28
- package/dist/init/slash-command.d.ts +27 -0
- package/dist/init/slash-command.js +61 -0
- package/dist/init/snippets.d.ts +41 -3
- package/dist/init/snippets.js +185 -10
- package/dist/init/vite-config.d.ts +4 -10
- package/dist/init/vite-config.js +15 -7
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.d.ts +15 -0
- package/dist/input/driven-redaction.js +76 -0
- package/dist/input/network-detail.d.ts +9 -3
- package/dist/input/network-detail.js +43 -23
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +58 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +20 -2
- package/dist/session/session.js +76 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +85 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/cli-parse.d.ts +0 -140
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -35
- package/dist/mcp-proxy.js +0 -190
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which predicates can be satisfied by something that was already true before the action.
|
|
3
|
+
*
|
|
4
|
+
* Event-based kinds (net, signal, route, console, animation, settled) are evaluated against the
|
|
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. `state` is read through the same floored path.
|
|
7
|
+
*
|
|
8
|
+
* `element` and `text` read the LIVE DOM, where no floor exists. A condition that held before the
|
|
9
|
+
* click holds after it and passes instantly, whatever the action did. Measured in the field: a click
|
|
10
|
+
* asserted with `{ kind: 'text', contains: 'Parallel Routes' }` returned `verified: "yes"` in 478ms
|
|
11
|
+
* 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
|
+
*
|
|
14
|
+
* So these are the kinds worth evaluating BEFORE the act, to find out whether the green means
|
|
15
|
+
* anything.
|
|
16
|
+
*/
|
|
17
|
+
import { PredicateKind } from '@reticlehq/core';
|
|
18
|
+
export function readsDomState(predicate) {
|
|
19
|
+
switch (predicate.kind) {
|
|
20
|
+
case PredicateKind.ELEMENT:
|
|
21
|
+
case PredicateKind.TEXT:
|
|
22
|
+
return true;
|
|
23
|
+
case PredicateKind.ALL_OF:
|
|
24
|
+
case PredicateKind.ANY_OF:
|
|
25
|
+
return predicate.predicates.some(readsDomState);
|
|
26
|
+
case PredicateKind.NOT:
|
|
27
|
+
return readsDomState(predicate.predicate);
|
|
28
|
+
default:
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -6,12 +6,25 @@
|
|
|
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
|
}
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Whether the observation window saw everything it was asked about.
|
|
15
|
+
*
|
|
16
|
+
* NOT core's VerdictStatus, which happens to spell one of its values 'partial' too: that one grades
|
|
17
|
+
* a verification RUN (some flows passed, some failed), this one grades what could be OBSERVED. Two
|
|
18
|
+
* meanings behind one string is exactly how a comparison ends up reading the wrong field, so they
|
|
19
|
+
* stay separate vocabularies.
|
|
20
|
+
*/
|
|
21
|
+
export declare const Coverage: {
|
|
22
|
+
readonly FULL: "full";
|
|
23
|
+
readonly PARTIAL: "partial";
|
|
24
|
+
};
|
|
25
|
+
export type Coverage = (typeof Coverage)[keyof typeof Coverage];
|
|
26
|
+
interface CoverageStatement {
|
|
27
|
+
coverage: Coverage;
|
|
15
28
|
/** Present only when partial — the human/agent-legible list of what went unobserved. */
|
|
16
29
|
note?: string;
|
|
17
30
|
spots: BlindSpot[];
|
|
@@ -8,22 +8,34 @@
|
|
|
8
8
|
// honesty-side imports keep working.
|
|
9
9
|
export { BlindSpotKind } from '@reticlehq/core';
|
|
10
10
|
import { BlindSpotKind, EventType } from '@reticlehq/core';
|
|
11
|
+
/**
|
|
12
|
+
* Whether the observation window saw everything it was asked about.
|
|
13
|
+
*
|
|
14
|
+
* NOT core's VerdictStatus, which happens to spell one of its values 'partial' too: that one grades
|
|
15
|
+
* a verification RUN (some flows passed, some failed), this one grades what could be OBSERVED. Two
|
|
16
|
+
* meanings behind one string is exactly how a comparison ends up reading the wrong field, so they
|
|
17
|
+
* stay separate vocabularies.
|
|
18
|
+
*/
|
|
19
|
+
export const Coverage = {
|
|
20
|
+
FULL: 'full',
|
|
21
|
+
PARTIAL: 'partial',
|
|
22
|
+
};
|
|
11
23
|
const LABEL = {
|
|
12
|
-
[BlindSpotKind.CLOSED_SHADOW_ROOT]: (n) => `${String(n)} closed shadow root${
|
|
13
|
-
[BlindSpotKind.CROSS_ORIGIN_IFRAME]: (n) => `${String(n)} cross-origin frame${
|
|
24
|
+
[BlindSpotKind.CLOSED_SHADOW_ROOT]: (n) => `${String(n)} closed shadow root${1 === n ? '' : 's'} unobserved`,
|
|
25
|
+
[BlindSpotKind.CROSS_ORIGIN_IFRAME]: (n) => `${String(n)} cross-origin frame${1 === n ? '' : 's'} unobserved`,
|
|
14
26
|
// The DOM of these frames IS observed; only their network is not. Saying so precisely matters — a
|
|
15
27
|
// flat "unobserved" would discard a real capability and push agents back to guessing.
|
|
16
|
-
[BlindSpotKind.UNINSTRUMENTED_FRAME]: (n) => `${String(n)} same-origin frame${
|
|
17
|
-
[BlindSpotKind.VIRTUALIZED_UNMOUNTED]: (n) => `${String(n)} virtualized unmounted row${
|
|
28
|
+
[BlindSpotKind.UNINSTRUMENTED_FRAME]: (n) => `${String(n)} same-origin frame${1 === n ? '' : 's'} observed for DOM but not network — requests they make are invisible`,
|
|
29
|
+
[BlindSpotKind.VIRTUALIZED_UNMOUNTED]: (n) => `${String(n)} virtualized unmounted row${1 === n ? '' : 's'} unobserved`,
|
|
18
30
|
// Not "some rows we could not see" — the events never reached the observer, so this window is a
|
|
19
31
|
// SAMPLE of what the app did. Phrased as a caveat on what the whole result MEANS.
|
|
20
|
-
[BlindSpotKind.RATE_LIMITED]: (n) => `${String(n)} event${
|
|
32
|
+
[BlindSpotKind.RATE_LIMITED]: (n) => `${String(n)} event${1 === n ? '' : 's'} dropped by the bridge rate cap, so this window is SAMPLED — raise RETICLE_MAX_MESSAGES_PER_SECOND for a busy app`,
|
|
21
33
|
// Not a count of things — a single fact about the page. Phrased so the coverage line reads as a
|
|
22
34
|
// caveat on what the network view MEANS, not as a tally.
|
|
23
35
|
// A count of unverifiable ACTIONS, not of things we failed to look at. The distinction matters:
|
|
24
36
|
// nothing here was missed, there was simply never a verdict to observe, and no amount of extra
|
|
25
37
|
// instrumentation would produce one.
|
|
26
|
-
[BlindSpotKind.VERDICTLESS_SEND]: (n) => `${String(n)} one-way IPC send${
|
|
38
|
+
[BlindSpotKind.VERDICTLESS_SEND]: (n) => `${String(n)} one-way IPC send${1 === n ? '' : 's'} dispatched with NO verdict — the renderer never learns whether the main process handled ${1 === n ? 'it' : 'them'}, so this cannot be confirmed from the page`,
|
|
27
39
|
[BlindSpotKind.WRAPPED_NETWORK]: () => 'fetch was already wrapped before Reticle, so recorded requests may differ from what was sent',
|
|
28
40
|
// A fact about the app's wiring, not a tally. Says what the empty network view MEANS, because an
|
|
29
41
|
// empty one here is indistinguishable from "this app made no backend calls" — and that reading is
|
|
@@ -53,8 +65,8 @@ export function impeachesCapture(kind) {
|
|
|
53
65
|
/** Compose the coverage statement. `full` (no note) when nothing was unobserved. */
|
|
54
66
|
export function buildCoverageStatement(spots) {
|
|
55
67
|
const present = spots.filter((s) => s.count > 0);
|
|
56
|
-
if (present.length
|
|
57
|
-
return { coverage:
|
|
68
|
+
if (0 === present.length)
|
|
69
|
+
return { coverage: Coverage.FULL, spots: [] };
|
|
58
70
|
// Each label carries its own ending. Appending a blanket " unobserved" here produced
|
|
59
71
|
// "...may differ from what was sent unobserved" for the wrapped-network caveat, which is a
|
|
60
72
|
// sentence rather than a count — and the same dangle appeared the moment a second prose-shaped
|
|
@@ -63,9 +75,9 @@ export function buildCoverageStatement(spots) {
|
|
|
63
75
|
// daemon can report one, and `LABEL[kind](count)` threw a TypeError on the verdict path when it
|
|
64
76
|
// did — turning "there is something I could not see" into a crashed assert, which is worse than
|
|
65
77
|
// either the caveat or the silence. Unknown kinds degrade to their own name.
|
|
66
|
-
const label = (s) => typeof LABEL[s.kind]
|
|
67
|
-
const note =
|
|
68
|
-
return { coverage:
|
|
78
|
+
const label = (s) => 'function' === typeof LABEL[s.kind] ? LABEL[s.kind](s.count) : `${s.kind} (${String(s.count)})`;
|
|
79
|
+
const note = `${Coverage.PARTIAL} — ${present.map(label).join(', ')}`;
|
|
80
|
+
return { coverage: Coverage.PARTIAL, note, spots: present };
|
|
69
81
|
}
|
|
70
82
|
/**
|
|
71
83
|
* Reduce a window's BLIND_SPOT events to one spot per kind — the LATEST reported count wins (the sensor
|
|
@@ -79,7 +91,7 @@ export function blindSpotsFromEvents(events) {
|
|
|
79
91
|
continue;
|
|
80
92
|
const kind = e.data['kind'];
|
|
81
93
|
const count = e.data['count'];
|
|
82
|
-
if (typeof kind
|
|
94
|
+
if ('string' === typeof kind && 'number' === typeof count) {
|
|
83
95
|
latest.set(kind, count);
|
|
84
96
|
}
|
|
85
97
|
}
|
|
@@ -14,7 +14,7 @@ export declare const HonestyGrade: {
|
|
|
14
14
|
readonly NONE: "none";
|
|
15
15
|
};
|
|
16
16
|
export type HonestyGrade = (typeof HonestyGrade)[keyof typeof HonestyGrade];
|
|
17
|
-
|
|
17
|
+
interface HonestyInputs {
|
|
18
18
|
grade: HonestyGrade;
|
|
19
19
|
/** Present when a causal chain is presented; `window` = time-heuristic, never dataflow truth. */
|
|
20
20
|
attribution?: string;
|
|
@@ -43,7 +43,7 @@ export interface HonestyBlock {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
export declare function buildHonestyBlock(inputs: HonestyInputs): HonestyBlock;
|
|
46
|
-
|
|
46
|
+
interface HonestyBar {
|
|
47
47
|
/** The minimum grade a green must carry to be trusted (default: any). */
|
|
48
48
|
minGrade?: HonestyGrade;
|
|
49
49
|
/** Require capture integrity to be clean (no truncation / blind spots). */
|
|
@@ -54,3 +54,4 @@ export declare function meetsHonestyBar(block: HonestyBlock, bar?: HonestyBar):
|
|
|
54
54
|
ok: boolean;
|
|
55
55
|
reasons: string[];
|
|
56
56
|
};
|
|
57
|
+
export {};
|
package/dist/honesty/honesty.js
CHANGED
|
@@ -26,7 +26,7 @@ const MIN_ENVELOPE_SAMPLES = 3;
|
|
|
26
26
|
const FULL_COVERAGE_PCT = 100;
|
|
27
27
|
export function buildHonestyBlock(inputs) {
|
|
28
28
|
const issues = [];
|
|
29
|
-
if (inputs.truncated
|
|
29
|
+
if (true === inputs.truncated)
|
|
30
30
|
issues.push('capture truncated');
|
|
31
31
|
for (const spot of inputs.blindSpots ?? [])
|
|
32
32
|
issues.push(`blind spot: ${spot}`);
|
|
@@ -46,7 +46,7 @@ export function buildHonestyBlock(inputs) {
|
|
|
46
46
|
? {}
|
|
47
47
|
: { envelope: { samples, sufficient: samples >= MIN_ENVELOPE_SAMPLES } }),
|
|
48
48
|
coverage: { ...(pct === undefined ? {} : { pct }), partial },
|
|
49
|
-
integrity: { clean: issues.length
|
|
49
|
+
integrity: { clean: 0 === issues.length, issues },
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
/** Whether a verdict's honesty block clears the harness's bar. Reasons list every failed criterion. */
|
|
@@ -55,8 +55,8 @@ export function meetsHonestyBar(block, bar = {}) {
|
|
|
55
55
|
if (bar.minGrade !== undefined && GRADE_RANK[block.grade] < GRADE_RANK[bar.minGrade]) {
|
|
56
56
|
reasons.push(`grade ${block.grade} below required ${bar.minGrade}`);
|
|
57
57
|
}
|
|
58
|
-
if (bar.requireIntegrityClean
|
|
58
|
+
if (true === bar.requireIntegrityClean && !block.integrity.clean) {
|
|
59
59
|
reasons.push(`integrity not clean: ${block.integrity.issues.join('; ')}`);
|
|
60
60
|
}
|
|
61
|
-
return { ok: reasons.length
|
|
61
|
+
return { ok: 0 === reasons.length, reasons };
|
|
62
62
|
}
|
|
@@ -15,7 +15,7 @@ const BODY_BEARING_METHODS = new Set(['POST', 'PUT', 'PATCH']);
|
|
|
15
15
|
*/
|
|
16
16
|
export function bodiesNotCaptured(calls) {
|
|
17
17
|
const bodyBearing = calls.filter((c) => BODY_BEARING_METHODS.has((c.method ?? '').toUpperCase()));
|
|
18
|
-
if (bodyBearing.length
|
|
18
|
+
if (0 === bodyBearing.length)
|
|
19
19
|
return {};
|
|
20
20
|
if (bodyBearing.some((c) => c.requestBody !== undefined))
|
|
21
21
|
return {};
|
|
@@ -36,7 +36,7 @@ export function hasUnreadWriteOutcome(events) {
|
|
|
36
36
|
return events.some((event) => {
|
|
37
37
|
if (event.type !== EventType.NET_REQUEST)
|
|
38
38
|
return false;
|
|
39
|
-
const method = (typeof event.data['method']
|
|
39
|
+
const method = ('string' === typeof event.data['method'] ? event.data['method'] : '').toUpperCase();
|
|
40
40
|
if (method === IPC_METHOD || !MUTATING_METHODS.includes(method))
|
|
41
41
|
return false;
|
|
42
42
|
const status = event.data['status'];
|
|
@@ -45,6 +45,6 @@ export function hasUnreadWriteOutcome(events) {
|
|
|
45
45
|
if (event.data['responseBody'] !== undefined)
|
|
46
46
|
return false; // read — findBodyFailures judged it
|
|
47
47
|
const size = event.data['responseSize'];
|
|
48
|
-
return typeof size
|
|
48
|
+
return 'number' === typeof size && size > 0;
|
|
49
49
|
});
|
|
50
50
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Verified } from '@reticlehq/core';
|
|
1
|
+
import { Verified, VerifiedReason } from '@reticlehq/core';
|
|
2
2
|
import { type HonestyBlock } from './honesty.js';
|
|
3
3
|
/**
|
|
4
4
|
* The decision rule: eight trust dimensions in, one answer out.
|
|
@@ -11,10 +11,21 @@ import { type HonestyBlock } from './honesty.js';
|
|
|
11
11
|
* Ordering is deliberate, because the first matching clause also writes `because`, and the reader
|
|
12
12
|
* should be told the most actionable fact rather than the first true one.
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
interface VerifiedInputs {
|
|
15
15
|
/** Did the declared consequence hold? Undefined when the action declared none. */
|
|
16
16
|
pass?: boolean;
|
|
17
17
|
honesty: HonestyBlock;
|
|
18
|
+
/**
|
|
19
|
+
* Set when the assertion could not be EVALUATED at all — an under-specified call, or nothing
|
|
20
|
+
* instrumented to read. Carries the sentence naming what is missing.
|
|
21
|
+
*/
|
|
22
|
+
inconclusive?: string;
|
|
23
|
+
/**
|
|
24
|
+
* The declared consequence was ALREADY TRUE before the action ran, so its holding afterwards says
|
|
25
|
+
* nothing about the action. Only meaningful for predicates that read live DOM state — event-based
|
|
26
|
+
* ones are floored at the act's cursor and cannot be satisfied by the past.
|
|
27
|
+
*/
|
|
28
|
+
alreadyTrue?: boolean;
|
|
18
29
|
/** Cross-channel disagreements found in the action's window. */
|
|
19
30
|
contradictions?: readonly {
|
|
20
31
|
kind: string;
|
|
@@ -32,9 +43,17 @@ export interface VerifiedInputs {
|
|
|
32
43
|
*/
|
|
33
44
|
outcomeUnread?: boolean;
|
|
34
45
|
}
|
|
35
|
-
|
|
46
|
+
interface VerifiedVerdict {
|
|
36
47
|
verified: Verified;
|
|
48
|
+
/**
|
|
49
|
+
* WHICH clause below decided this, as a closed enum. `verified` has three values and this rule has
|
|
50
|
+
* ten clauses, so the verdict alone collapses opposite facts — "Reticle caught a real bug", "the
|
|
51
|
+
* agent wrote a bad predicate" and "Reticle could not see" all arrived as one string. Named here,
|
|
52
|
+
* beside the sentence, so the vocabulary cannot drift from the branches that produce it.
|
|
53
|
+
*/
|
|
54
|
+
verifiedReason: VerifiedReason;
|
|
37
55
|
/** One sentence naming the deciding evidence — never a restatement of the field. */
|
|
38
56
|
because: string;
|
|
39
57
|
}
|
|
40
58
|
export declare function decideVerified(inputs: VerifiedInputs): VerifiedVerdict;
|
|
59
|
+
export {};
|
package/dist/honesty/verified.js
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
import { Verified } from '@reticlehq/core';
|
|
1
|
+
import { Verified, VerifiedReason } from '@reticlehq/core';
|
|
2
2
|
import { HonestyGrade } from './honesty.js';
|
|
3
3
|
export function decideVerified(inputs) {
|
|
4
4
|
const { pass, honesty, contradictions = [], settled, outcomePending, outcomeUnread } = inputs;
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// Ahead of the failure clause, because a failure is only the most actionable fact when there WAS
|
|
6
|
+
// one. An assertion nobody could evaluate is not a defect in the app, and calling it one was
|
|
7
|
+
// putting the agent's own malformed calls into the bug count.
|
|
8
|
+
if (inputs.inconclusive !== undefined) {
|
|
9
|
+
return {
|
|
10
|
+
verified: Verified.UNKNOWN,
|
|
11
|
+
verifiedReason: VerifiedReason.INCONCLUSIVE,
|
|
12
|
+
because: `the assertion could not be evaluated: ${inputs.inconclusive}`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// A failed assertion is the most actionable fact there is; it leads — including over
|
|
16
|
+
// `alreadyTrue`, because a condition that held before AND fails now is a real regression.
|
|
17
|
+
if (false === pass) {
|
|
18
|
+
return {
|
|
19
|
+
verified: Verified.NO,
|
|
20
|
+
verifiedReason: VerifiedReason.ASSERTION_FAILED,
|
|
21
|
+
because: 'the declared consequence did not hold',
|
|
22
|
+
};
|
|
8
23
|
}
|
|
9
24
|
// A contradiction outranks a passing assertion, and that inversion is the whole point: the case
|
|
10
25
|
// this product exists to catch is a green assertion sitting on top of a failed write. Measured on
|
|
@@ -14,15 +29,35 @@ export function decideVerified(inputs) {
|
|
|
14
29
|
const kinds = contradictions.map((c) => c.kind).join(', ');
|
|
15
30
|
return {
|
|
16
31
|
verified: Verified.NO,
|
|
32
|
+
verifiedReason: VerifiedReason.CONTRADICTED,
|
|
17
33
|
because: `channels disagree about this action (${kinds}) even though the assertion passed`,
|
|
18
34
|
};
|
|
19
35
|
}
|
|
36
|
+
// Below the contradiction clause, and for the same reason a contradiction outranks a dirty
|
|
37
|
+
// capture: evidence AGAINST beats absence of evidence, whichever absence it is. Both can hold at
|
|
38
|
+
// once — assert `{ text: 'Saved' }` that was already on screen while the write 500s — and ordering
|
|
39
|
+
// this first downgraded a DETECTED false green from NO to UNKNOWN, which reads as "assert
|
|
40
|
+
// something else" rather than "this is broken".
|
|
41
|
+
//
|
|
42
|
+
// A green that was already green before the action is not evidence about the action. Measured in
|
|
43
|
+
// the field: a click asserted with `{ text: 'Parallel Routes' }` returned verified "yes" in 478ms
|
|
44
|
+
// with routeChanges 0, because the text was the nav link already on screen — the real navigation
|
|
45
|
+
// landed 1.8s later. UNKNOWN rather than NO on purpose: the app may well be fine, and reporting a
|
|
46
|
+
// failure we did not observe would be its own false claim.
|
|
47
|
+
if (true === inputs.alreadyTrue) {
|
|
48
|
+
return {
|
|
49
|
+
verified: Verified.UNKNOWN,
|
|
50
|
+
verifiedReason: VerifiedReason.ALREADY_TRUE,
|
|
51
|
+
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)',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
20
54
|
// Dirty capture is NOT failure. The layer could not see part of the window, so any green is a
|
|
21
55
|
// statement about what it happened to observe — which is precisely the thing that must not be
|
|
22
56
|
// reported as proof.
|
|
23
57
|
if (!honesty.integrity.clean) {
|
|
24
58
|
return {
|
|
25
59
|
verified: Verified.UNKNOWN,
|
|
60
|
+
verifiedReason: VerifiedReason.UNCLEAN_CAPTURE,
|
|
26
61
|
because: `capture was not clean (${honesty.integrity.issues.join('; ')}), so a green here would only describe what was observed`,
|
|
27
62
|
};
|
|
28
63
|
}
|
|
@@ -32,6 +67,7 @@ export function decideVerified(inputs) {
|
|
|
32
67
|
if (honesty.grade === HonestyGrade.NONE) {
|
|
33
68
|
return {
|
|
34
69
|
verified: Verified.UNKNOWN,
|
|
70
|
+
verifiedReason: VerifiedReason.VACUOUS_GRADE,
|
|
35
71
|
because: 'nothing was asserted at a real grade, so passing proves nothing — assert a signal, request, or state path',
|
|
36
72
|
};
|
|
37
73
|
}
|
|
@@ -43,9 +79,10 @@ export function decideVerified(inputs) {
|
|
|
43
79
|
// optimistically rendered "dispatched", the page settled, and the verdict came back `yes` — then
|
|
44
80
|
// the server REVERTED it to `held` 1.2s later. Every channel agreed, and every channel was early.
|
|
45
81
|
// UNKNOWN rather than NO: nothing has failed, and saying it has would be its own false report.
|
|
46
|
-
if (
|
|
82
|
+
if (true === outcomePending) {
|
|
47
83
|
return {
|
|
48
84
|
verified: Verified.UNKNOWN,
|
|
85
|
+
verifiedReason: VerifiedReason.OUTCOME_PENDING,
|
|
49
86
|
because: 'a write returned 202 Accepted, so the server has not finished processing it — this window cannot contain the outcome; re-check once it reconciles',
|
|
50
87
|
};
|
|
51
88
|
}
|
|
@@ -56,16 +93,18 @@ export function decideVerified(inputs) {
|
|
|
56
93
|
//
|
|
57
94
|
// UNKNOWN, not NO: nothing is known to have failed. The remedy is in the sentence, because an
|
|
58
95
|
// agent that cannot act on a caveat will learn to skip it.
|
|
59
|
-
if (
|
|
96
|
+
if (true === outcomeUnread) {
|
|
60
97
|
return {
|
|
61
98
|
verified: Verified.UNKNOWN,
|
|
99
|
+
verifiedReason: VerifiedReason.OUTCOME_UNREAD,
|
|
62
100
|
because: 'a write returned 2xx with a response body that was never recorded, so its outcome is unread — a 200 describes the transport, not the result (a batch reports per-item failures in the body, and every GraphQL error is a 200). Enable it where your app calls connect(): `reticle.connect({ captureNetworkBodies: true })`, then re-run',
|
|
63
101
|
};
|
|
64
102
|
}
|
|
65
103
|
// Never settled: the page may still be moving, so the observation window may have closed early.
|
|
66
|
-
if (
|
|
104
|
+
if (false === settled) {
|
|
67
105
|
return {
|
|
68
106
|
verified: Verified.UNKNOWN,
|
|
107
|
+
verifiedReason: VerifiedReason.UNSETTLED,
|
|
69
108
|
because: 'the page never settled, so the reaction window may have closed before the app finished',
|
|
70
109
|
};
|
|
71
110
|
}
|
|
@@ -79,7 +118,8 @@ export function decideVerified(inputs) {
|
|
|
79
118
|
// own. Seen on a one-way IPC send: coverage said the outcome was unobservable, `because` said clean.
|
|
80
119
|
return {
|
|
81
120
|
verified: Verified.YES,
|
|
82
|
-
|
|
121
|
+
verifiedReason: VerifiedReason.PROVED,
|
|
122
|
+
because: true === honesty.coverage?.partial
|
|
83
123
|
? `assertion held at ${honesty.grade} grade with no channel disagreeing, but coverage was PARTIAL — see \`coverage\` for what went unobserved`
|
|
84
124
|
: `assertion held at ${honesty.grade} grade over a clean capture with no channel disagreeing`,
|
|
85
125
|
};
|
package/dist/http-server.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as http from 'node:http';
|
|
2
2
|
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
3
3
|
import { MCP_SSE_PATH, MCP_MESSAGE_PATH, STATUS_PATH } from '@reticlehq/core';
|
|
4
|
+
import { noteAgentPeer, PEER_VERSION_PARAM, PEER_CONTRACT_PARAM } from './version/peer-announce.js';
|
|
4
5
|
import { log } from './log.js';
|
|
5
6
|
import { reportMcpConnected } from './telemetry/mcp-connection.js';
|
|
6
|
-
import { isLoopbackHost, isLocalWebOrigin, isLoopbackPeer, requestToken, tokensMatch, } from './token-auth.js';
|
|
7
|
+
import { isLoopbackHost, isLocalWebOrigin, isLoopbackPeer, requestToken, tokensMatch, } from './bridge/token-auth.js';
|
|
7
8
|
/**
|
|
8
9
|
* Creates a shared HTTP server that handles both the WebSocket bridge (browser SDK) and the
|
|
9
10
|
* SSE MCP transport (Claude/agent). Does NOT call listen — caller controls that.
|
|
@@ -52,13 +53,19 @@ export function createSharedServer(options = {}) {
|
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
|
-
if (req.method
|
|
56
|
+
if ('GET' === req.method && path === STATUS_PATH) {
|
|
56
57
|
const body = JSON.stringify(statusProvider?.() ?? { running: true });
|
|
57
58
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
58
59
|
res.end(body);
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
|
-
if (req.method
|
|
62
|
+
if ('GET' === req.method && path === MCP_SSE_PATH) {
|
|
63
|
+
// The agent's MCP server announces itself here, on the connect it already makes. The daemon is
|
|
64
|
+
// the single judge of skew, so this is where the third pair is decided — and unlike the CLI's
|
|
65
|
+
// own check (a stderr line no agent reads), a nudge queued here rides out on the agent's next
|
|
66
|
+
// tool result. That is the pair the user hits after `npm update`: a cached npx MCP package
|
|
67
|
+
// talking to a daemon from a different build.
|
|
68
|
+
noteAgentPeer(url.searchParams.get(PEER_VERSION_PARAM), url.searchParams.get(PEER_CONTRACT_PARAM));
|
|
62
69
|
if (mcpFactory === undefined) {
|
|
63
70
|
res.writeHead(503, { 'Content-Type': 'text/plain' });
|
|
64
71
|
res.end('MCP server not ready');
|
|
@@ -71,14 +78,14 @@ export function createSharedServer(options = {}) {
|
|
|
71
78
|
const transport = new SSEServerTransport(MCP_MESSAGE_PATH, res);
|
|
72
79
|
const sid = transport.sessionId;
|
|
73
80
|
transports.set(sid, transport);
|
|
74
|
-
if (transports.size
|
|
81
|
+
if (1 === transports.size)
|
|
75
82
|
agentPresence?.(true); // first agent attached
|
|
76
83
|
res.on('close', () => {
|
|
77
84
|
transports.delete(sid);
|
|
78
85
|
transport.close().catch(() => undefined);
|
|
79
86
|
mcpServer.close().catch(() => undefined);
|
|
80
87
|
log('mcp_client_disconnected', { sessionId: sid });
|
|
81
|
-
if (transports.size
|
|
88
|
+
if (0 === transports.size)
|
|
82
89
|
agentPresence?.(false); // last agent detached → it's the human's turn
|
|
83
90
|
});
|
|
84
91
|
mcpServer
|
|
@@ -96,9 +103,9 @@ export function createSharedServer(options = {}) {
|
|
|
96
103
|
});
|
|
97
104
|
return;
|
|
98
105
|
}
|
|
99
|
-
if (req.method
|
|
106
|
+
if ('POST' === req.method && path === MCP_MESSAGE_PATH) {
|
|
100
107
|
const sessionId = url.searchParams.get('sessionId');
|
|
101
|
-
if (
|
|
108
|
+
if (null === sessionId) {
|
|
102
109
|
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
103
110
|
res.end('missing sessionId');
|
|
104
111
|
return;
|
|
@@ -33,7 +33,7 @@ export interface HuntRun {
|
|
|
33
33
|
/** Controls actually driven. A run that drove nothing proves nothing and is reported separately. */
|
|
34
34
|
stepsRun?: number;
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
interface HuntSummary {
|
|
37
37
|
runs: number;
|
|
38
38
|
/** Runs that actually drove at least one control. The denominator that can honestly be quoted. */
|
|
39
39
|
runsWithCoverage: number;
|
|
@@ -48,3 +48,4 @@ export interface HuntSummary {
|
|
|
48
48
|
headline: string;
|
|
49
49
|
}
|
|
50
50
|
export declare function summarizeHunt(runs: readonly HuntRun[]): HuntSummary;
|
|
51
|
+
export {};
|
package/dist/hunt/hunt-report.js
CHANGED
|
@@ -74,10 +74,10 @@ export function summarizeHunt(runs) {
|
|
|
74
74
|
* this project exists to distrust.
|
|
75
75
|
*/
|
|
76
76
|
function buildHeadline(withCoverage, flaggedRuns, contradictions) {
|
|
77
|
-
if (
|
|
77
|
+
if (0 === withCoverage) {
|
|
78
78
|
return 'no run drove a single control — nothing was measured, which is not the same as nothing being wrong';
|
|
79
79
|
}
|
|
80
|
-
if (
|
|
80
|
+
if (0 === flaggedRuns) {
|
|
81
81
|
return `${String(withCoverage)} merged changes crawled, no cross-channel contradictions found — confirm coverage was real before reading this as clean`;
|
|
82
82
|
}
|
|
83
83
|
return `${String(flaggedRuns)} of ${String(withCoverage)} merged, already-green changes carried a candidate false green (${String(contradictions)} contradictions) — each needs manual confirmation before it is counted`;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Bridge } from './bridge.js';
|
|
1
|
+
import { Bridge } from './bridge/bridge.js';
|
|
2
2
|
import type { OwnedRealInputProvider, RealInputProvider, InjectConnectOptions } from './input/real-input.js';
|
|
3
3
|
export { ReticleTool } from './tools/tool-names.js';
|
|
4
4
|
export { RingBuffer } from './events/ring-buffer.js';
|
|
5
|
-
export { Bridge } from './bridge.js';
|
|
5
|
+
export { Bridge } from './bridge/bridge.js';
|
|
6
6
|
export { Session, SessionManager } from './session/session.js';
|
|
7
7
|
export type { SessionInfo, SessionHealth } from './session/session.js';
|
|
8
8
|
export { buildSessionRecommendation } from './session/session-recommendation.js';
|
|
@@ -33,12 +33,12 @@ export { BrowserPool, DEFAULT_LEASE_TTL_MS } from './pool/browser-pool.js';
|
|
|
33
33
|
export type { Lease, Launcher, PooledBrowser } from './pool/browser-pool.js';
|
|
34
34
|
export { playwrightLauncher, resolveMaxContexts } from './pool/playwright-launcher.js';
|
|
35
35
|
export { appendReticleParams } from './tools/lease-tools.js';
|
|
36
|
-
export { writePid, removePid, isRunning, logPath, readPid, isAlive } from './daemon.js';
|
|
36
|
+
export { writePid, removePid, isRunning, logPath, readPid, isAlive } from './daemon/daemon.js';
|
|
37
37
|
export type { CrawlReport, CrawlAnomaly, CrawlOptions, CrawlSession } from './crawl/crawl.js';
|
|
38
38
|
export { scrollToFind } from './input/scroll-find.js';
|
|
39
39
|
export type { ScrollFindResult, ScrollFindQuery, ScrollFindSession } from './input/scroll-find.js';
|
|
40
|
-
export { CORE_TOOL_NAMES,
|
|
41
|
-
export type {
|
|
40
|
+
export { CORE_TOOL_NAMES, TOOL_SURFACE, TOOL_PROFILE_ENV, filterTools, resolveToolSurface, } from './tools/tool-surface.js';
|
|
41
|
+
export type { ToolSurface } from './tools/tool-surface.js';
|
|
42
42
|
export { AnnotationStore } from './flows/annotation-store.js';
|
|
43
43
|
export { replayFlow, nearestTestid } from './flows/flow-replay.js';
|
|
44
44
|
export type { FlowReplaySession, WaitForSignal } from './flows/flow-replay.js';
|
|
@@ -118,11 +118,13 @@ export interface RunningServer {
|
|
|
118
118
|
/** True when nothing is using the daemon: no agent connected, no browser session, no pool lease.
|
|
119
119
|
* The daemon entry (cli.ts) polls this to self-shut-down when idle so Reticle never lingers. */
|
|
120
120
|
isIdle?: () => boolean;
|
|
121
|
+
/** True while an agent's MCP client is attached — see IdleShutdownOptions.agentAttached. */
|
|
122
|
+
agentAttached?: () => boolean;
|
|
121
123
|
/** The pairing token the bridge is enforcing (explicit, env, or auto-provisioned); undefined if none. */
|
|
122
124
|
token?: string;
|
|
123
125
|
close: () => Promise<void>;
|
|
124
126
|
}
|
|
125
|
-
export { resolveBridgeSecurity } from './bridge-security.js';
|
|
127
|
+
export { resolveBridgeSecurity } from './bridge/bridge-security.js';
|
|
126
128
|
/** Start the Reticle bridge (browser WS endpoint) and, by default, the MCP stdio server. */
|
|
127
129
|
export declare function start(options?: StartOptions): Promise<RunningServer>;
|
|
128
130
|
/**
|