@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
package/SKILL.md
CHANGED
|
@@ -38,13 +38,9 @@ There are exactly three places you may stop, and none is a question about a pref
|
|
|
38
38
|
2. **Your host asks the human to approve a command.** That prompt belongs to the host. Never bypass or suppress it, and take a refusal as the answer. `init` writing a pre-approval rule for the `reticle` server is not that: it is a scoped, announced config change the human asked for by running the command, and it covers only Reticle's own tools.
|
|
39
39
|
3. **Only if no agent CLI exists on the machine at all**, the client restart that loads the MCP tools (step 2). If `claude`, `codex`, `cursor-agent` or `gemini` is installed, you do NOT stop. Step 2 shows you how to drive from a child process instead, and that path is measured to be the difference between a verdict and a dead end.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Better still, install the on-disk copy first. It survives the restart on its own, and it is the same content:
|
|
41
|
+
**On Claude Code and Codex, do not ask the human to restart at all.** `npx @reticlehq/server init --relaunch` prints the exact resume command for this conversation; run that command yourself rather than handing the person a chore. It refuses when the id has no transcript behind it, because `--resume` on an empty id opens a blank session that looks exactly like success. Everywhere else the restart genuinely is theirs: name the one step for their client and ask for that alone.
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
npx skills add reticlehq/reticle --skill install-and-verify -y
|
|
47
|
-
```
|
|
43
|
+
If you do have to stop, the restart ends your turn. This file was fetched into the context that just ended, so nothing carries it across unless you make it. **Before you trigger the restart, write the remaining steps into `RETICLE-SETUP.md` at the repo root**, one line each, with the step you stopped at marked. That file is what you re-read afterwards. Delete it once step 5 has produced a verdict.
|
|
48
44
|
|
|
49
45
|
**After the restart, resume at the step you were on.** Do not start over, and do not skip forward.
|
|
50
46
|
|
|
@@ -89,11 +85,7 @@ Read **VERIFY** below when the question is "does this still work?" rather than "
|
|
|
89
85
|
|
|
90
86
|
# SETUP
|
|
91
87
|
|
|
92
|
-
**One command. It does all of it, and it ends with a verdict.**
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
RETICLE_INSTALL_SOURCE=skill_file npx @reticlehq/server@latest init --flow "<the journey worth proving>"
|
|
96
|
-
```
|
|
88
|
+
**One command, the one at the top of this file. It does all of it, and it ends with a verdict.**
|
|
97
89
|
|
|
98
90
|
`@latest` is deliberate: `npx` caches, and a stale cached CLI is the most common silent setup failure. Never pin a version here.
|
|
99
91
|
|
|
@@ -142,7 +134,7 @@ curl https://docs.reticle.sh/install-manual.md # register the MCP, wire the
|
|
|
142
134
|
curl https://docs.reticle.sh/troubleshooting.md # nothing connected, click did nothing, verdict unknown
|
|
143
135
|
```
|
|
144
136
|
|
|
145
|
-
**Vite + React**, **Next.js**, **Remix** and **Astro** are the frameworks with an app and a CI gate behind them, so their wiring is proven rather than assumed. The SDK is framework-agnostic and usually connects elsewhere, but say that honestly rather than reporting a proven install.
|
|
137
|
+
**Vite + React**, **Next.js**, **Remix** and **Astro** are the frameworks with an app and a CI gate behind them, so their wiring is proven rather than assumed. **TanStack Start** is detected and given a client-only recipe, but nothing in CI proves it. The SDK is framework-agnostic and usually connects elsewhere, but say that honestly rather than reporting a proven install.
|
|
146
138
|
|
|
147
139
|
Do not reconstruct it from memory. Three things decide whether it works, and all three get skipped:
|
|
148
140
|
|
|
@@ -198,18 +190,18 @@ Work down this list and stop at the first row that fits. Do not hand-drive a flo
|
|
|
198
190
|
|
|
199
191
|
| The question | The call | Calls |
|
|
200
192
|
| --- | --- | --- |
|
|
201
|
-
| "Did my edit break anything?" | `
|
|
193
|
+
| "Did my edit break anything?" | `reticle_verify({ action: "change", files: ["src/App.tsx"] })` | 1 |
|
|
202
194
|
| "Does this known journey still work?" | `reticle_run({ tool: "reticle_flow_replay", args: { flowName: "login" } })` | 1 |
|
|
203
195
|
| "Does this new behaviour work?" | `reticle_act_sequence` for the setup, then ONE `reticle_act_and_wait` | 2 |
|
|
204
196
|
| No MCP available at all | `npx @reticlehq/server verify <url>` in the shell | 1, no MCP |
|
|
205
197
|
|
|
206
|
-
`reticle_verify`
|
|
198
|
+
`reticle_verify` IS on the advertised tool list, so call it directly. `reticle_flow_replay` is not: it is reached through `reticle_run` exactly as written above. That is the supported call shape, not a workaround, and it is why you have to be told it exists at all.
|
|
207
199
|
|
|
208
200
|
`reticle_verify {action:"change"}` answers `unknown` when no saved flow covers the files you changed. That is the honest answer and not a failure. Nothing ran, so nothing was proved. It is also the signal to record one. Never read it as a pass.
|
|
209
201
|
|
|
210
202
|
## Two more you have to be told about
|
|
211
203
|
|
|
212
|
-
|
|
204
|
+
Extended surface, so they are not in the tool list you were handed, and reached through `reticle_run`.
|
|
213
205
|
|
|
214
206
|
**Context compacted, a turn starting, or a sub-agent taking over?** Ask what this run already established, instead of re-snapshotting to rediscover what you already knew:
|
|
215
207
|
|
|
@@ -263,11 +255,9 @@ reticle_act_and_wait({ sessionId, ref, action: "click", until: { kind: "allOf",
|
|
|
263
255
|
|
|
264
256
|
Prefer `reticle_act_and_wait({ ref, action, until })`. It names the expected consequence **before** the action, which is the difference between a check and a rationalisation.
|
|
265
257
|
|
|
266
|
-
A verdict of `verified: "unknown"` is not a pass. It means Reticle drove the app and could not tell what happened. Report it as unknown. `verified: "no-fault"` is not a pass either. It means the page settled and no channel reported a problem, but nothing was declared to prove, so assert a consequence the action CHANGES. **Never weaken a check to make it pass.**
|
|
267
|
-
|
|
268
258
|
Then report what you drove, what it produced, and the `file:line` for anything broken.
|
|
269
259
|
|
|
270
|
-
The surface is deliberately small: `default`
|
|
260
|
+
The surface is deliberately small: `default` 19, `all` 30. Editors budget tools across every MCP server you have connected (Cursor allows 40 in total), so the count is capped rather than allowed to grow. `reticle_tools` loads the argument grammar for the rest on demand, and `reticle_run` invokes any of them by name. Nothing is unreachable; the cold tail just costs one discovery hop.
|
|
271
261
|
|
|
272
262
|
- Batching, regression suites, reading a verdict: `https://docs.reticle.sh/agent-cheatsheet.md`
|
|
273
263
|
- Every predicate and action: `https://docs.reticle.sh/predicates.md`, `https://docs.reticle.sh/actions.md`
|
package/bin/reticle.js
ADDED
|
@@ -26,7 +26,18 @@ const NO_TOKEN = 'authentication failed: no pairing token on the page';
|
|
|
26
26
|
/** WebSocket close reasons are capped at 123 bytes; a longer one throws and closes with nothing. */
|
|
27
27
|
const MAX_REASON_BYTES = 123;
|
|
28
28
|
export function authFailureReason(servedProjects, helloProject, helloToken) {
|
|
29
|
-
|
|
29
|
+
// ONE served project, not merely some. A daemon that has accepted sessions from SEVERAL projects
|
|
30
|
+
// is demonstrably not owned by any one of them, so "that daemon is not yours, stop it" is the
|
|
31
|
+
// wrong story — and its advice is actively harmful there, because stopping it breaks the projects
|
|
32
|
+
// that are working. This is the normal shape for a globally-registered daemon, which several
|
|
33
|
+
// editors start from the user's home directory and point at everything.
|
|
34
|
+
//
|
|
35
|
+
// With one served project the inference holds and is worth making: the daemon belongs to that
|
|
36
|
+
// project and this HELLO is somebody else's. With several, the token is the suspect, and the plain
|
|
37
|
+
// reason says so without prescribing a recovery that costs other people their sessions.
|
|
38
|
+
if (helloProject !== undefined &&
|
|
39
|
+
1 === servedProjects.size &&
|
|
40
|
+
!servedProjects.has(helloProject)) {
|
|
30
41
|
const reason = `this daemon serves a different project — run \`reticle stop\` and retry`;
|
|
31
42
|
return Buffer.byteLength(reason, 'utf8') <= MAX_REASON_BYTES ? reason : PLAIN;
|
|
32
43
|
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { StartOptions } from '../index.js';
|
|
2
|
+
import { pairingTokenSource } from './pairing-token.js';
|
|
2
3
|
/** The security contract the bridge/daemon enforce: bind host, pairing token, and WS origin allow-list. */
|
|
3
4
|
interface BridgeSecurity {
|
|
4
5
|
host?: string;
|
|
5
6
|
token?: string;
|
|
6
7
|
allowedOrigins?: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Which source supplied `token`, decided HERE because this is the only place that knows.
|
|
10
|
+
* By the time the Bridge sees it, an auto-provisioned token and one passed by a caller are the
|
|
11
|
+
* same field — so computing it downstream would report `explicit` for every daemon and describe
|
|
12
|
+
* nothing. See pairingTokenSource for why a refusal needs this at all.
|
|
13
|
+
*/
|
|
14
|
+
tokenSource?: ReturnType<typeof pairingTokenSource>;
|
|
7
15
|
}
|
|
8
16
|
/**
|
|
9
17
|
* Resolve the bridge's security options from explicit options first, then the environment. Shared by
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReticleEnv } from '@reticlehq/core';
|
|
2
|
-
import { defaultPairingTokenDir, nodePairingTokenDeps, readOrCreatePairingToken, } from './pairing-token.js';
|
|
2
|
+
import { defaultPairingTokenDir, nodePairingTokenDeps, readOrCreatePairingToken, pairingTokenSource, } from './pairing-token.js';
|
|
3
3
|
import { log } from '../log.js';
|
|
4
4
|
/**
|
|
5
5
|
* Resolve the bridge's security options from explicit options first, then the environment. Shared by
|
|
@@ -7,6 +7,10 @@ import { log } from '../log.js';
|
|
|
7
7
|
* every entrypoint — a past divergence let daemon mode silently run with auth disabled.
|
|
8
8
|
*/
|
|
9
9
|
export function resolveBridgeSecurity(options) {
|
|
10
|
+
const tokenSource = pairingTokenSource({
|
|
11
|
+
explicit: options.token !== undefined && 0 < options.token.length,
|
|
12
|
+
env: process.env,
|
|
13
|
+
});
|
|
10
14
|
const envToken = process.env[ReticleEnv.TOKEN];
|
|
11
15
|
const envOrigins = process.env[ReticleEnv.ALLOWED_ORIGINS];
|
|
12
16
|
const host = options.host ?? process.env[ReticleEnv.HOST];
|
|
@@ -20,6 +24,7 @@ export function resolveBridgeSecurity(options) {
|
|
|
20
24
|
...(host === undefined ? {} : { host }),
|
|
21
25
|
...(token === undefined ? {} : { token }),
|
|
22
26
|
...(allowedOrigins === undefined ? {} : { allowedOrigins }),
|
|
27
|
+
tokenSource,
|
|
23
28
|
};
|
|
24
29
|
}
|
|
25
30
|
/**
|
package/dist/bridge/bridge.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as http from 'node:http';
|
|
2
2
|
import { Session, SessionManager } from '../session/session.js';
|
|
3
|
+
import { pairingTokenSource } from './pairing-token.js';
|
|
3
4
|
/** A human clicked ▶ on a saved flow in the panel — replay it with no agent. Wired by the daemon. */
|
|
4
5
|
type ReplayRequestHandler = (sessionId: string, flowName: string) => void;
|
|
5
6
|
/** Called once a browser session connects, so the daemon can push it the replayable-flow list. */
|
|
@@ -41,6 +42,8 @@ interface BridgeOptions {
|
|
|
41
42
|
port: number;
|
|
42
43
|
host?: string;
|
|
43
44
|
token?: string;
|
|
45
|
+
/** Which source supplied `token`. Set by resolveBridgeSecurity; logged on a refusal, never sent. */
|
|
46
|
+
tokenSource?: ReturnType<typeof pairingTokenSource>;
|
|
44
47
|
allowedOrigins?: string[];
|
|
45
48
|
maxMessagesPerSecond?: number;
|
|
46
49
|
maxSessions?: number;
|
package/dist/bridge/bridge.js
CHANGED
|
@@ -5,8 +5,10 @@ import { WebSocketServer } from 'ws';
|
|
|
5
5
|
import { EventType, HumanControlKind, RETICLE_WS_PATH, ReticleMessageSchema, LOOPBACK_HOST, MessageKind, ReticleEnv, RETICLE_PROTOCOL_VERSION, TRANSPORT_LIMITS, isLocalPage, isLoopbackHostname, isOpaqueOrigin, OPAQUE_ORIGIN, isHighValueEvent, RATE_CAP_HIGH_VALUE_RESERVE_RATIO, CONTRACT_FINGERPRINT, } from '@reticlehq/core';
|
|
6
6
|
import { Session, SessionManager } from '../session/session.js';
|
|
7
7
|
import { tokensMatch } from './token-auth.js';
|
|
8
|
+
import { pairingTokenSource } from './pairing-token.js';
|
|
8
9
|
import { log } from '../log.js';
|
|
9
10
|
import { getSessionMetrics } from '../telemetry/session-metrics.js';
|
|
11
|
+
import { sessionReplacedReason } from '../session/session-replaced.js';
|
|
10
12
|
import { describeSkew, sdkFix, SkewPair } from '../version/version-skew.js';
|
|
11
13
|
import { noteVersionSkew } from '../version/version-nudge.js';
|
|
12
14
|
import { protocolSkewReason } from './protocol-skew.js';
|
|
@@ -174,6 +176,8 @@ export class Bridge {
|
|
|
174
176
|
#wss;
|
|
175
177
|
#clock;
|
|
176
178
|
#token;
|
|
179
|
+
/** Which source supplied #token — logged on a refusal so a mismatch names its own cause. */
|
|
180
|
+
#tokenSource;
|
|
177
181
|
/**
|
|
178
182
|
* Projects this daemon has ACCEPTED a session from. Evidence, not derivation: it is what lets a
|
|
179
183
|
* leaked daemon say "I belong to another project" instead of "authentication failed". See
|
|
@@ -223,6 +227,15 @@ export class Bridge {
|
|
|
223
227
|
this.#clock = options.clock ?? (() => Date.now());
|
|
224
228
|
this.#token =
|
|
225
229
|
options.token !== undefined && options.token.length > 0 ? options.token : undefined;
|
|
230
|
+
// Supplied by resolveBridgeSecurity, which is the only place that can tell an auto-provisioned
|
|
231
|
+
// token from one a caller passed — both arrive here as options.token. Defaulted rather than
|
|
232
|
+
// required so a directly-constructed Bridge (tests, embedders) still logs something true.
|
|
233
|
+
this.#tokenSource =
|
|
234
|
+
options.tokenSource ??
|
|
235
|
+
pairingTokenSource({
|
|
236
|
+
explicit: options.token !== undefined && 0 < options.token.length,
|
|
237
|
+
env: process.env,
|
|
238
|
+
});
|
|
226
239
|
// An entry that fails to normalize — typically a scheme-less `myapp.test` — used to be
|
|
227
240
|
// filtered out with no trace: the allow-list LOOKED configured, every dial from that origin
|
|
228
241
|
// was refused at the gate, and the refusal read as a scheme mismatch in the page rather than
|
|
@@ -446,10 +459,16 @@ export class Bridge {
|
|
|
446
459
|
// token is not wrong, it is somebody else's — and "authentication failed" sends the user to
|
|
447
460
|
// check the one thing that is fine. See auth-failure-reason.
|
|
448
461
|
const reason = authFailureReason(this.#servedProjects, parsed.projectId, parsed.token);
|
|
462
|
+
// The token SOURCE, never the token. A refusal here has no other witness: the value is
|
|
463
|
+
// machine-level on both sides, so the daemon's cwd cannot explain a mismatch (#685) and
|
|
464
|
+
// the only thing that can is the environment the two processes were started in — which
|
|
465
|
+
// differs exactly when an IDE spawns the MCP server globally instead of from the shell
|
|
466
|
+
// that launched the dev server.
|
|
449
467
|
log('authentication_failed', {
|
|
450
468
|
served: [...this.#servedProjects],
|
|
451
469
|
...(parsed.projectId === undefined ? {} : { helloProject: parsed.projectId }),
|
|
452
470
|
presented: parsed.token !== undefined && 0 < parsed.token.length,
|
|
471
|
+
tokenSource: this.#tokenSource,
|
|
453
472
|
});
|
|
454
473
|
this.sessions.noteClosure(WS_CLOSE_REASON.AUTH_FAILED, this.#clock());
|
|
455
474
|
socket.close(WS_CLOSE.AUTH_FAILED[0], reason);
|
|
@@ -509,7 +528,7 @@ export class Bridge {
|
|
|
509
528
|
// the old handle can finish against the live connection instead of returning an error whose
|
|
510
529
|
// only answer is to go and rediscover an id that has not changed. See Session.succeededBy.
|
|
511
530
|
replaced.succeededBy(session);
|
|
512
|
-
replaced.disconnect(
|
|
531
|
+
replaced.disconnect(sessionReplacedReason(session.id, session.url), true);
|
|
513
532
|
}
|
|
514
533
|
// The daemon is the single judge of skew, and HELLO is where the page announces itself.
|
|
515
534
|
// Reported on the session (reticle_sessions) AND queued for the next tool result, because an
|
|
@@ -520,6 +539,9 @@ export class Bridge {
|
|
|
520
539
|
contract: parsed.contract,
|
|
521
540
|
fix: this.#sdkFix(),
|
|
522
541
|
}, { version: SERVER_VERSION, contract: CONTRACT_FINGERPRINT });
|
|
542
|
+
// Kept so a remedy can check whether it applies to THIS page — see body-capture-remedy.
|
|
543
|
+
session.sdkVersion = parsed.sdkVersion;
|
|
544
|
+
session.captureBodies = parsed.captureBodies;
|
|
523
545
|
if (skew !== undefined) {
|
|
524
546
|
log('version_skew', {
|
|
525
547
|
sessionId: session.id,
|
|
@@ -27,10 +27,19 @@ export declare class FakeBrowser {
|
|
|
27
27
|
name: string;
|
|
28
28
|
args: Record<string, unknown>;
|
|
29
29
|
}[];
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* The snapshot tree to return. Mutable so tests can vary the page between calls.
|
|
32
|
+
*
|
|
33
|
+
* The counters are optional because the fields they stand for are: the browser omits `nodes`-
|
|
34
|
+
* adjacent diagnostics on a page that has nothing to report, and a test asserting on one has to be
|
|
35
|
+
* able to send a payload shaped like the real thing.
|
|
36
|
+
*/
|
|
31
37
|
snapshotResult: {
|
|
32
38
|
tree: string;
|
|
33
39
|
status: Record<string, unknown>;
|
|
40
|
+
nodes?: number;
|
|
41
|
+
hiddenSkipped?: number;
|
|
42
|
+
leanSkipped?: number;
|
|
34
43
|
};
|
|
35
44
|
constructor(port: number, sessionId: string, hasCapabilities?: boolean);
|
|
36
45
|
open(): Promise<void>;
|
|
@@ -34,7 +34,13 @@ export class FakeBrowser {
|
|
|
34
34
|
queryResolves = true;
|
|
35
35
|
/** Records every command the bridge sent (for replay assertions). */
|
|
36
36
|
received = [];
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* The snapshot tree to return. Mutable so tests can vary the page between calls.
|
|
39
|
+
*
|
|
40
|
+
* The counters are optional because the fields they stand for are: the browser omits `nodes`-
|
|
41
|
+
* adjacent diagnostics on a page that has nothing to report, and a test asserting on one has to be
|
|
42
|
+
* able to send a payload shaped like the real thing.
|
|
43
|
+
*/
|
|
38
44
|
snapshotResult = {
|
|
39
45
|
tree: '- button "Pay" (ref=e7)\n- dialog "Order confirmed" (ref=e12)',
|
|
40
46
|
status: { route: '/checkout' },
|
|
@@ -16,6 +16,23 @@ export interface PairingTokenDeps {
|
|
|
16
16
|
export declare const PAIRING_TOKEN_DIR_ENV: "RETICLE_PAIRING_TOKEN_DIR";
|
|
17
17
|
/** The default directory that holds the token — the per-user ~/.reticle so it's shared across projects. */
|
|
18
18
|
export declare function defaultPairingTokenDir(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Which of the three sources supplied the token this process is using.
|
|
21
|
+
*
|
|
22
|
+
* The SOURCE, never the secret. #685 reports a globally-registered daemon refusing an app that
|
|
23
|
+
* `doctor` calls correctly wired, and blames the daemon's cwd — but cwd cannot reach this value:
|
|
24
|
+
* resolution is options.token, then RETICLE_TOKEN, then the file under ~/.reticle, and the Vite
|
|
25
|
+
* plugin reads that same file. What CAN diverge is the ENVIRONMENT, because an IDE spawning a global
|
|
26
|
+
* MCP server does not inherit the shell that started the dev server. Either variable then moves one
|
|
27
|
+
* side and not the other, and neither is visible in anything we print — so the refusal is a mystery
|
|
28
|
+
* with no witness, which is how this report has stayed open on a guessed cause.
|
|
29
|
+
*
|
|
30
|
+
* Pure, and takes its env, so the order can be pinned by tests rather than by reading two files.
|
|
31
|
+
*/
|
|
32
|
+
export declare function pairingTokenSource(input: {
|
|
33
|
+
explicit: boolean;
|
|
34
|
+
env: NodeJS.ProcessEnv;
|
|
35
|
+
}): 'explicit' | 'env:RETICLE_TOKEN' | 'env:RETICLE_PAIRING_TOKEN_DIR' | 'default';
|
|
19
36
|
export declare function pairingTokenPath(dir: string): string;
|
|
20
37
|
/** Production IO adapter: 0600 file + 0700 dir so only the owner can read the secret. */
|
|
21
38
|
export declare function nodePairingTokenDeps(): PairingTokenDeps;
|
|
@@ -15,6 +15,32 @@ export function defaultPairingTokenDir() {
|
|
|
15
15
|
return override;
|
|
16
16
|
return join(homedir(), ReticleDir.ROOT);
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Which of the three sources supplied the token this process is using.
|
|
20
|
+
*
|
|
21
|
+
* The SOURCE, never the secret. #685 reports a globally-registered daemon refusing an app that
|
|
22
|
+
* `doctor` calls correctly wired, and blames the daemon's cwd — but cwd cannot reach this value:
|
|
23
|
+
* resolution is options.token, then RETICLE_TOKEN, then the file under ~/.reticle, and the Vite
|
|
24
|
+
* plugin reads that same file. What CAN diverge is the ENVIRONMENT, because an IDE spawning a global
|
|
25
|
+
* MCP server does not inherit the shell that started the dev server. Either variable then moves one
|
|
26
|
+
* side and not the other, and neither is visible in anything we print — so the refusal is a mystery
|
|
27
|
+
* with no witness, which is how this report has stayed open on a guessed cause.
|
|
28
|
+
*
|
|
29
|
+
* Pure, and takes its env, so the order can be pinned by tests rather than by reading two files.
|
|
30
|
+
*/
|
|
31
|
+
export function pairingTokenSource(input) {
|
|
32
|
+
// Same precedence the resolver applies, deliberately: a source that did not decide the value is a
|
|
33
|
+
// misdirection, and this exists to stop one.
|
|
34
|
+
if (input.explicit)
|
|
35
|
+
return 'explicit';
|
|
36
|
+
const token = input.env[ReticleEnv.TOKEN];
|
|
37
|
+
if (token !== undefined && 0 < token.length)
|
|
38
|
+
return `env:${ReticleEnv.TOKEN}`;
|
|
39
|
+
const dir = input.env[PAIRING_TOKEN_DIR_ENV];
|
|
40
|
+
if (dir !== undefined && 0 < dir.length)
|
|
41
|
+
return `env:${PAIRING_TOKEN_DIR_ENV}`;
|
|
42
|
+
return 'default';
|
|
43
|
+
}
|
|
18
44
|
export function pairingTokenPath(dir) {
|
|
19
45
|
return join(dir, ReticleDir.PAIRING_TOKEN_FILE);
|
|
20
46
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* The query parameter a client may present its pairing token in.
|
|
4
|
+
*
|
|
5
|
+
* Exported because the CLI now presents the same token when it attaches to a running daemon over the
|
|
6
|
+
* MCP HTTP transport, and a second spelling of this string is a second way for auth to fail.
|
|
7
|
+
*/
|
|
8
|
+
export declare const TOKEN_QUERY_PARAM = "token";
|
|
2
9
|
/**
|
|
3
10
|
* Timing-safe pairing-token comparison. Length-guards first because `timingSafeEqual` throws on a
|
|
4
11
|
* length mismatch — and the guard itself is not a timing oracle (length is not the secret).
|
|
@@ -2,7 +2,13 @@ import { timingSafeEqual } from 'node:crypto';
|
|
|
2
2
|
import { isLoopbackHostname } from '@reticlehq/core';
|
|
3
3
|
/** `Authorization: Bearer <token>` prefix and the query-param fallback for clients that can't set headers. */
|
|
4
4
|
const BEARER_PREFIX = 'Bearer ';
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The query parameter a client may present its pairing token in.
|
|
7
|
+
*
|
|
8
|
+
* Exported because the CLI now presents the same token when it attaches to a running daemon over the
|
|
9
|
+
* MCP HTTP transport, and a second spelling of this string is a second way for auth to fail.
|
|
10
|
+
*/
|
|
11
|
+
export const TOKEN_QUERY_PARAM = 'token';
|
|
6
12
|
/** Node reports IPv4 loopback over a dual-stack socket as this mapped form; strip it before classifying. */
|
|
7
13
|
const IPV4_MAPPED_PREFIX = /^::ffff:/i;
|
|
8
14
|
/**
|
|
@@ -31,19 +31,19 @@ declare const CapsuleSchema: z.ZodObject<{
|
|
|
31
31
|
/** Minimal steps to reproduce (prefix-trimmed). */
|
|
32
32
|
steps: z.ZodArray<z.ZodType<FlowStep, z.ZodTypeDef, FlowStep>, "many">;
|
|
33
33
|
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
origin: string;
|
|
35
|
+
expected: string;
|
|
34
36
|
id: string;
|
|
35
37
|
version: 1;
|
|
36
|
-
expected: string;
|
|
37
|
-
origin: string;
|
|
38
38
|
steps: FlowStep[];
|
|
39
39
|
observed: string;
|
|
40
40
|
createdAt: number;
|
|
41
41
|
flow?: string | undefined;
|
|
42
42
|
}, {
|
|
43
|
+
origin: string;
|
|
44
|
+
expected: string;
|
|
43
45
|
id: string;
|
|
44
46
|
version: 1;
|
|
45
|
-
expected: string;
|
|
46
|
-
origin: string;
|
|
47
47
|
steps: FlowStep[];
|
|
48
48
|
observed: string;
|
|
49
49
|
createdAt: number;
|
|
@@ -15,4 +15,4 @@ export interface DivergenceCapsule {
|
|
|
15
15
|
blastRadius: string[];
|
|
16
16
|
}
|
|
17
17
|
/** Compose the full capsule from the declared chain and the observed (attributed) event window. */
|
|
18
|
-
export declare function buildDivergenceCapsule(expected: readonly ExpectedLink[], observed: readonly ReticleEvent[]): DivergenceCapsule;
|
|
18
|
+
export declare function buildDivergenceCapsule(expected: readonly ExpectedLink[], observed: readonly ReticleEvent[], truncated?: boolean): DivergenceCapsule;
|
package/dist/capsule/capsule.js
CHANGED
|
@@ -26,10 +26,10 @@ function blastRadius(expected, observed) {
|
|
|
26
26
|
return radius;
|
|
27
27
|
}
|
|
28
28
|
/** Compose the full capsule from the declared chain and the observed (attributed) event window. */
|
|
29
|
-
export function buildDivergenceCapsule(expected, observed) {
|
|
29
|
+
export function buildDivergenceCapsule(expected, observed, truncated = false) {
|
|
30
30
|
return {
|
|
31
|
-
summary: causalSummary(observed),
|
|
32
|
-
firstDivergence: firstDivergence(expected, observed),
|
|
31
|
+
summary: causalSummary(observed, { truncated }),
|
|
32
|
+
firstDivergence: firstDivergence(expected, observed, truncated),
|
|
33
33
|
blastRadius: blastRadius(expected, observed),
|
|
34
34
|
};
|
|
35
35
|
}
|
|
@@ -76,10 +76,30 @@ export interface CausalSummary {
|
|
|
76
76
|
* `stateSettleMs` and `elided` follow.
|
|
77
77
|
*/
|
|
78
78
|
stateUnwatched?: true;
|
|
79
|
+
/**
|
|
80
|
+
* TRUE when the ring buffer evicted events belonging to this window, so every count and list here
|
|
81
|
+
* is a FLOOR, not a total — `net.total: 0` means "none survived", never "none happened".
|
|
82
|
+
*
|
|
83
|
+
* The same distinction `stateUnwatched` draws, from the other cause, and it was the more expensive
|
|
84
|
+
* one. A field app rewrote one localStorage key thousands of times a minute; the no-op writes
|
|
85
|
+
* starved the buffer (`held: 2000, dropped: 70482`), and the verdict for that window reported
|
|
86
|
+
* `net.total: 0`, `stateDiffs: []` and "state never changed" — while a POST that had returned 200
|
|
87
|
+
* in that exact window carried the entire root cause in its body. The agent read the zero and was
|
|
88
|
+
* about to report "clicking Accept fires no network request", which points a developer at the
|
|
89
|
+
* click handler instead of at the payload the server rejected.
|
|
90
|
+
*
|
|
91
|
+
* `honesty.integrity.losses` already said `buffer_loss`. It was not enough: the headline fields an
|
|
92
|
+
* agent reads to form a verdict are these, and a bare `0` several levels above a nested integrity
|
|
93
|
+
* flag reads as a fact. The absolute and the caveat must not be separable, so the caveat rides on
|
|
94
|
+
* the summary that carries the absolutes.
|
|
95
|
+
*/
|
|
96
|
+
truncated?: true;
|
|
79
97
|
}
|
|
80
98
|
/** What only the SESSION knows — level facts the event window cannot contain. See `stateUnwatched`. */
|
|
81
|
-
|
|
99
|
+
interface SummaryContext {
|
|
82
100
|
stateUnwatched?: boolean;
|
|
101
|
+
/** Did the buffer drop events from this window — see `CausalSummary.truncated`. */
|
|
102
|
+
truncated?: boolean;
|
|
83
103
|
}
|
|
84
104
|
/**
|
|
85
105
|
* THE INVARIANT: every field of this summary has a bounded size that does not depend on how much the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventType, PerfMetric, isDevToolingUrl } from '@reticlehq/core';
|
|
2
|
-
import {
|
|
2
|
+
import { routeOfEvent } from '../events/predicate-route.js';
|
|
3
3
|
/**
|
|
4
4
|
* THE INVARIANT: every field of this summary has a bounded size that does not depend on how much the
|
|
5
5
|
* app did. A verdict travels in the same payload as its evidence, so an unbounded evidence field is
|
|
@@ -119,14 +119,15 @@ export function causalSummary(events, context = {}) {
|
|
|
119
119
|
}
|
|
120
120
|
break;
|
|
121
121
|
}
|
|
122
|
-
case EventType.ROUTE_CHANGE:
|
|
122
|
+
case EventType.ROUTE_CHANGE: {
|
|
123
123
|
// The ROUTER's path. Reporting the document pathname left `route: "/"` on every action of a
|
|
124
124
|
// hash-routed app — the same reading that made a route predicate unsatisfiable there, and an
|
|
125
|
-
// agent reasoning off this field would conclude the route never changed. See
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
// agent reasoning off this field would conclude the route never changed. See routeOfEvent.
|
|
126
|
+
const routed = routeOfEvent(data);
|
|
127
|
+
if (routed !== undefined)
|
|
128
|
+
route = routed.routePath;
|
|
129
129
|
break;
|
|
130
|
+
}
|
|
130
131
|
case EventType.SIGNAL:
|
|
131
132
|
pushUnique(signals, data['name']);
|
|
132
133
|
break;
|
|
@@ -166,5 +167,6 @@ export function causalSummary(events, context = {}) {
|
|
|
166
167
|
...(layoutShift === undefined ? {} : { layoutShift }),
|
|
167
168
|
longTasks,
|
|
168
169
|
...(true === context.stateUnwatched ? { stateUnwatched: true } : {}),
|
|
170
|
+
...(true === context.truncated ? { truncated: true } : {}),
|
|
169
171
|
};
|
|
170
172
|
}
|
|
@@ -32,4 +32,4 @@ export interface Divergence {
|
|
|
32
32
|
* Walk the declared chain in order; return the first link not satisfied by the observed events (with a
|
|
33
33
|
* side-by-side of what happened instead), or null when the whole chain held.
|
|
34
34
|
*/
|
|
35
|
-
export declare function firstDivergence(expected: readonly ExpectedLink[], observed: readonly ReticleEvent[]): Divergence | null;
|
|
35
|
+
export declare function firstDivergence(expected: readonly ExpectedLink[], observed: readonly ReticleEvent[], truncated?: boolean): Divergence | null;
|
|
@@ -20,26 +20,42 @@ function satisfies(link, events) {
|
|
|
20
20
|
(link.status === undefined || e.data['status'] === link.status));
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Describe what WAS observed for a link that failed — the closest attempt, for a side-by-side.
|
|
25
|
+
*
|
|
26
|
+
* `truncated` says the buffer dropped events belonging to this window, which turns every sentence
|
|
27
|
+
* below from a claim about the app into a claim about a partial reading. It is the difference
|
|
28
|
+
* between "the request was never made" and "no surviving event shows the request", and a field
|
|
29
|
+
* session proved they are not interchangeable: a POST that returned 200 inside the window carried
|
|
30
|
+
* the whole root cause, and the capsule for that same window said `state "cad" never changed`. The
|
|
31
|
+
* agent believed the absolute — the strongest, most specific, and most wrong thing this tool can
|
|
32
|
+
* say. Absence of evidence must never be typed as evidence of absence.
|
|
33
|
+
*/
|
|
34
|
+
function observedFor(link, events, truncated) {
|
|
25
35
|
if (ConsequenceKind.NET === link.kind) {
|
|
26
36
|
const match = net(events).find((e) => 'string' === typeof e.data['url'] && e.data['url'].includes(link.urlContains));
|
|
27
|
-
if (match
|
|
28
|
-
return
|
|
29
|
-
|
|
37
|
+
if (match !== undefined) {
|
|
38
|
+
return `${link.urlContains} responded ${String(match.data['status'])} (expected ${String(link.status)})`;
|
|
39
|
+
}
|
|
40
|
+
return truncated
|
|
41
|
+
? `no surviving event shows a request to ${link.urlContains} — capture truncated, so this is not evidence there was none`
|
|
42
|
+
: `no request to ${link.urlContains}`;
|
|
30
43
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
const subject = ConsequenceKind.SIGNAL === link.kind ? `signal "${link.name}"` : `state "${link.name}"`;
|
|
45
|
+
if (truncated)
|
|
46
|
+
return `${subject} was not observed — capture truncated, so absence is unproven`;
|
|
47
|
+
return ConsequenceKind.SIGNAL === link.kind
|
|
48
|
+
? `${subject} never fired`
|
|
49
|
+
: `${subject} never changed`;
|
|
34
50
|
}
|
|
35
51
|
/**
|
|
36
52
|
* Walk the declared chain in order; return the first link not satisfied by the observed events (with a
|
|
37
53
|
* side-by-side of what happened instead), or null when the whole chain held.
|
|
38
54
|
*/
|
|
39
|
-
export function firstDivergence(expected, observed) {
|
|
55
|
+
export function firstDivergence(expected, observed, truncated = false) {
|
|
40
56
|
for (const link of expected) {
|
|
41
57
|
if (!satisfies(link, observed)) {
|
|
42
|
-
return { expected: link, observed: observedFor(link, observed) };
|
|
58
|
+
return { expected: link, observed: observedFor(link, observed, truncated) };
|
|
43
59
|
}
|
|
44
60
|
}
|
|
45
61
|
return null;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
export declare const CHROMIUM_ANTI_THROTTLING_ARGS: readonly ["--disable-background-timer-throttling", "--disable-backgrounding-occluded-windows", "--disable-renderer-backgrounding"];
|
|
19
19
|
/** The options object handed to `chromium.launch` at both launch sites. */
|
|
20
|
-
|
|
20
|
+
interface ChromiumLaunchOptions {
|
|
21
21
|
headless: boolean;
|
|
22
22
|
args: string[];
|
|
23
23
|
}
|
|
@@ -26,3 +26,4 @@ export interface ChromiumLaunchOptions {
|
|
|
26
26
|
* each call so no caller can mutate the constant behind everyone else's back.
|
|
27
27
|
*/
|
|
28
28
|
export declare function chromiumLaunchOptions(headless: boolean): ChromiumLaunchOptions;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A verdict from the CLI, against the daemon that is already running.
|
|
3
|
+
*
|
|
4
|
+
* THE DEAD END THIS REMOVES. The largest single cluster in the field feedback is not a defect in any
|
|
5
|
+
* tool — it is that the tools were not loaded in the client, and there was no supported way to reach
|
|
6
|
+
* a verdict from that state. The shape was always the same: the app is instrumented, the daemon is
|
|
7
|
+
* healthy, `doctor` shows a live connected page, and the client (Codex, Cursor Cloud, Antigravity,
|
|
8
|
+
* Gemini CLI, or a Claude Code session whose MCP link dropped) exposes no `reticle_*` tools, because
|
|
9
|
+
* they load only at client startup. The documented CLI fallback then refused, because the daemon
|
|
10
|
+
* already owned the port — precisely the state a successful install leaves you in. The other CLI
|
|
11
|
+
* verdict paths need saved flows, and a first-install project has none. And our own guidance says
|
|
12
|
+
* never to stop the daemon, because that kills the agent's MCP link.
|
|
13
|
+
*
|
|
14
|
+
* So an agent held a live, correctly-wired app and no path to a verdict short of a human restarting
|
|
15
|
+
* their editor. Several reporters fell back to Playwright or to their own client and said honestly
|
|
16
|
+
* that Reticle could not produce a verdict.
|
|
17
|
+
*
|
|
18
|
+
* HOW, AND WHY IT ADDS NO NEW SURFACE. One reporter found the answer themselves: they drove the full
|
|
19
|
+
* tool surface over the daemon's HTTP/SSE transport with a hand-written client, and it worked. That
|
|
20
|
+
* transport is not opt-in — `start()` calls `attachMcp` unconditionally, so every daemon has it. So
|
|
21
|
+
* this asks the running daemon the same questions an agent would, over the same transport, using the
|
|
22
|
+
* MCP client the SDK already ships. Nothing new is exposed and nothing is bound: the daemon keeps
|
|
23
|
+
* the port, the agent keeps its link, and there is nothing to stop.
|
|
24
|
+
*
|
|
25
|
+
* It is deliberately the CLI equivalent of ONE `act_and_wait`: navigate, then assert. A CLI that
|
|
26
|
+
* grew its own scripting language would be a second way to express a journey, competing with flows
|
|
27
|
+
* and drifting from them.
|
|
28
|
+
*/
|
|
29
|
+
/** What a one-shot verdict reports back to the caller. */
|
|
30
|
+
export interface AdhocVerdict {
|
|
31
|
+
/** Process exit code: 0 only when the predicate was PROVED. */
|
|
32
|
+
code: number;
|
|
33
|
+
/** Lines to print, in order. */
|
|
34
|
+
lines: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface AdhocVerdictOptions {
|
|
37
|
+
port: number;
|
|
38
|
+
/** Navigated to first when given; omitted to assert against wherever the session already is. */
|
|
39
|
+
url?: string;
|
|
40
|
+
/** The predicate, already parsed from the caller's JSON. */
|
|
41
|
+
predicate: unknown;
|
|
42
|
+
token?: string;
|
|
43
|
+
/** Injected so a test does not need a live daemon. */
|
|
44
|
+
connect?: (endpoint: URL) => Promise<ToolCaller>;
|
|
45
|
+
}
|
|
46
|
+
/** The narrow slice of an MCP client this needs — one call, then close. */
|
|
47
|
+
export interface ToolCaller {
|
|
48
|
+
call(name: string, args: Record<string, unknown>): Promise<unknown>;
|
|
49
|
+
close(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Navigate (optionally) and assert, against the daemon already on this port.
|
|
53
|
+
*
|
|
54
|
+
* Exit code 0 ONLY for `verified: "yes"`. `unknown` exits non-zero on purpose: it means Reticle could
|
|
55
|
+
* not tell what happened, and a CI step that treats "could not tell" as success is the false green
|
|
56
|
+
* this whole product exists to prevent.
|
|
57
|
+
*/
|
|
58
|
+
export declare function runAdhocVerdict(options: AdhocVerdictOptions): Promise<AdhocVerdict>;
|