@reticlehq/server 2.4.0 → 2.6.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 +719 -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} +13 -1
- package/dist/{bridge.js → bridge/bridge.js} +95 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
- 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/cli-doctor.d.ts +1 -0
- package/dist/cli/cli-doctor.js +135 -0
- 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-feedback.d.ts +31 -0
- package/dist/cli/cli-parse-feedback.js +56 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
- package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
- package/dist/{cli-port.js → cli/cli-port.js} +9 -9
- 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/doctor-daemon-line.d.ts +37 -0
- package/dist/cli/doctor-daemon-line.js +35 -0
- package/dist/cli/port-holder.d.ts +43 -0
- package/dist/cli/port-holder.js +99 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +302 -100
- 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 +73 -0
- package/dist/daemon/daemon-resilience.js +279 -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} +105 -48
- package/dist/daemon/heartbeat.d.ts +115 -0
- package/dist/daemon/heartbeat.js +141 -0
- 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} +23 -2
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
- package/dist/daemon/port-presence.d.ts +76 -0
- package/dist/daemon/port-presence.js +93 -0
- package/dist/daemon/state-dir.d.ts +7 -0
- package/dist/daemon/state-dir.js +59 -0
- package/dist/daemon/unclean-predecessor.d.ts +23 -0
- package/dist/daemon/unclean-predecessor.js +27 -0
- 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 +53 -20
- package/dist/events/predicate-eval.js +179 -55
- package/dist/events/predicate-parse.d.ts +23 -0
- package/dist/events/predicate-parse.js +54 -0
- package/dist/events/predicate.d.ts +7 -2
- package/dist/events/predicate.js +126 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +2 -17
- 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.js +24 -9
- 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 +6 -8
- package/dist/flows/flow-replay.js +132 -48
- 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 +156 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +63 -21
- 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/role-drift-reason.d.ts +20 -0
- package/dist/flows/role-drift-reason.js +39 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/suite-flakes.d.ts +33 -0
- package/dist/flows/suite-flakes.js +32 -0
- 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 +35 -3
- package/dist/honesty/blind-spots.js +57 -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 +31 -3
- package/dist/honesty/verified.js +64 -8
- package/dist/http-server.js +23 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +9 -6
- package/dist/index.js +41 -25
- package/dist/init/agent-rules.d.ts +20 -1
- package/dist/init/agent-rules.js +64 -8
- 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.js +4 -1
- package/dist/init/cra.d.ts +50 -0
- package/dist/init/cra.js +106 -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 +9 -2
- package/dist/init/detect.js +24 -5
- package/dist/init/mcp-clients.d.ts +81 -0
- package/dist/init/mcp-clients.js +248 -0
- 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 +2 -4
- package/dist/init/next-patch.js +109 -19
- package/dist/init/node-io.js +29 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +166 -8
- package/dist/init/plan.d.ts +85 -6
- package/dist/init/plan.js +196 -33
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +23 -1
- package/dist/init/run.js +338 -47
- package/dist/init/slash-command.d.ts +9 -0
- package/dist/init/slash-command.js +10 -1
- package/dist/init/snippets.d.ts +7 -10
- package/dist/init/snippets.js +44 -15
- package/dist/init/vite-config.js +1 -1
- package/dist/init/windows-quote.d.ts +38 -0
- package/dist/init/windows-quote.js +70 -0
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.js +1 -1
- package/dist/input/network-detail.js +8 -8
- 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 +32 -0
- package/dist/mcp/mcp-outage.js +55 -0
- package/dist/mcp/mcp-proxy.d.ts +134 -0
- package/dist/mcp/mcp-proxy.js +720 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +139 -37
- 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/browser-pool.d.ts +9 -0
- package/dist/pool/browser-pool.js +38 -9
- 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/attachment-history.d.ts +46 -0
- package/dist/session/attachment-history.js +65 -0
- 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 +45 -0
- package/dist/session/no-session-diagnosis.js +120 -0
- package/dist/session/no-session-watch.d.ts +21 -0
- package/dist/session/no-session-watch.js +69 -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/pending-commands.js +1 -0
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +43 -8
- package/dist/session/session-info.js +46 -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 +6 -0
- package/dist/session/session-manager.js +124 -12
- 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 +15 -2
- package/dist/session/session.js +77 -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/crash-cause.d.ts +47 -0
- package/dist/telemetry/crash-cause.js +104 -0
- package/dist/telemetry/daemon-telemetry.d.ts +38 -1
- package/dist/telemetry/daemon-telemetry.js +37 -12
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-class.d.ts +26 -0
- package/dist/telemetry/error-class.js +52 -0
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +8 -3
- package/dist/telemetry/feedback-cli.js +52 -8
- package/dist/telemetry/feedback-context.d.ts +23 -6
- package/dist/telemetry/feedback-context.js +105 -5
- 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.d.ts +7 -0
- package/dist/telemetry/git-facts.js +5 -5
- package/dist/telemetry/project-profile.js +2 -1
- package/dist/telemetry/session-metrics.d.ts +47 -3
- package/dist/telemetry/session-metrics.js +315 -15
- package/dist/telemetry/telemetry.d.ts +45 -2
- package/dist/telemetry/telemetry.js +150 -37
- 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 +114 -118
- 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 +9 -1
- package/dist/tools/assert-verdict.js +21 -6
- package/dist/tools/browser-tools.js +36 -11
- package/dist/tools/contract-tools.d.ts +0 -5
- package/dist/tools/contract-tools.js +27 -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 +96 -19
- package/dist/tools/error-recovery.d.ts +34 -2
- package/dist/tools/error-recovery.js +255 -4
- package/dist/tools/feedback-invite.d.ts +48 -0
- package/dist/tools/feedback-invite.js +71 -0
- 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 -12
- package/dist/tools/invoke-tool.js +126 -33
- 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-arrival.d.ts +26 -0
- package/dist/tools/navigate-arrival.js +60 -0
- package/dist/tools/navigate-result.d.ts +31 -0
- package/dist/tools/navigate-result.js +38 -0
- package/dist/tools/observe-tools.js +72 -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 +81 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +93 -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 +16 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +217 -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/README.md +55 -0
- 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-dispatch-receiver.yml +76 -0
- package/docs/fixtures.md +45 -0
- package/docs/flows.md +166 -0
- package/docs/gate-plan.md +98 -0
- package/docs/gates.md +117 -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/matrix/2.5.0/claude-code-machine.json +19 -0
- package/docs/matrix/2.5.0/codex-machine.json +20 -0
- package/docs/matrix/2.5.0/cursor-machine.json +22 -0
- package/docs/matrix/2.5.0/gemini-machine.json +22 -0
- package/docs/matrix/2.5.0/opencode-machine.json +22 -0
- package/docs/matrix/2.5.0/vscode-machine.json +22 -0
- package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
- package/docs/matrix/MATRIX.md +26 -0
- package/docs/matrix/README.md +59 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/system-map.md +152 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +191 -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-doctor.d.ts +0 -6
- package/dist/cli-doctor.js +0 -60
- package/dist/cli-launch.js +0 -112
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -67
- package/dist/mcp-proxy.js +0 -337
- 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-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,171 @@
|
|
|
1
|
+
# Reticle Integration Patterns
|
|
2
|
+
|
|
3
|
+
The basics in [Getting Started](getting-started.md) work with **zero app changes**. This doc is the _recommended_ shape for a real codebase: a minimal production footprint, a signal layer that can't silently drift, and an adoption path that starts paying off on day one — no rewrite required.
|
|
4
|
+
|
|
5
|
+
- [1 — Start here: reuse what you already have](#1--start-here-reuse-what-you-already-have)
|
|
6
|
+
- [2 — Inject the emitter (zero prod bundle)](#2--inject-the-emitter-zero-prod-bundle)
|
|
7
|
+
- [3 — Emit signals from the store layer, not N call sites](#3--emit-signals-from-the-store-layer-not-n-call-sites)
|
|
8
|
+
- [4 — Self-registering domains (`registerReticleDomain`)](#4--self-registering-domains-registerreticledomain)
|
|
9
|
+
- [5 — Keep the signal layer from rotting (`@reticlehq/eslint-plugin`)](#5--keep-the-signal-layer-from-rotting-reticleeslint-plugin)
|
|
10
|
+
- [6 — Limitation: un-scriptable tabs → `reticle drive`](#6--limitation-un-scriptable-tabs--reticle-drive)
|
|
11
|
+
- [Checklist](#checklist)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1 — Start here: reuse what you already have
|
|
16
|
+
|
|
17
|
+
Adoption is **free → cheap → targeted**. You don't instrument everything; you reuse what you have, then add the handful of facts the DOM can't express.
|
|
18
|
+
|
|
19
|
+
**1. Reuse your existing `data-testid` (free).** If you already test with Playwright or Cypress, your testids work in Reticle unchanged — `reticle_query({ by: 'testid', value: 'checkout' })` matches them exactly. No new markup, no new code.
|
|
20
|
+
|
|
21
|
+
**2. Advertise the surface from your existing constants (cheap).** You already keep a `TestIds` constant object for your E2E suite — pass it straight in. Now `reticle_capabilities()` tells a fresh agent your whole surface without reading source.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { registerCapabilities } from '@reticlehq/browser';
|
|
25
|
+
import { TestIds } from '../e2e/test-ids'; // the same constants your Playwright suite uses
|
|
26
|
+
|
|
27
|
+
registerCapabilities({
|
|
28
|
+
testids: Object.values(TestIds),
|
|
29
|
+
signals: ['order:saved'],
|
|
30
|
+
stores: ['cart'],
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**3. Add signals only at the ~20 commit points that matter (targeted).** Emit `reticle.signal(name, data)` at the moments the DOM can't show — a save committed, a webhook arrived, an edit applied, an async generation finished. You instrument the off-DOM facts you'd otherwise eyeball, not every line.
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { reticle } from '@reticlehq/browser';
|
|
38
|
+
onSaved(() => reticle.signal('order:saved', { id, total }));
|
|
39
|
+
// agent: reticle_assert({ predicate: { kind: 'signal', name: 'order:saved', dataMatches: { id: '*' } } })
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
That's day-one usefulness with no rewrite. The rest of this doc is how to do steps 2–3 _well_ so the signal layer stays honest as the app grows.
|
|
43
|
+
|
|
44
|
+
## 2 — Inject the emitter (zero prod bundle)
|
|
45
|
+
|
|
46
|
+
The #1 objection: _"I don't want a test tool in my production bundle."_ The answer: components never import `@reticlehq/browser`. They depend on a tiny structural interface, `ReticleEmitter` (`{ signal, state }`), and the real emitter is injected once at the top. `createReticleEmitter()` returns an emitter that proxies to the connected `reticle` singleton and is a **safe no-op** until `reticle.connect()` runs — so nothing breaks in production or before connect, and **`@reticlehq/browser` stays out of the prod bundle.**
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
// app/emit.ts — the one place that touches @reticlehq/browser
|
|
50
|
+
import { createReticleEmitter } from '@reticlehq/browser';
|
|
51
|
+
export const emit = createReticleEmitter(); // no-op until reticle.connect()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// any component — depends on the interface, not the SDK
|
|
56
|
+
import { emit } from '../emit';
|
|
57
|
+
function onSaved(id: string, total: number) {
|
|
58
|
+
emit.signal('order:saved', { id, total });
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The emitter re-checks the connection on every call, so you can create it at module load — before `reticle.connect()` — and it starts forwarding the moment Reticle connects. (See [getting-started Step 2](getting-started.md#step-2--embed-the-sdk-in-your-app) for where `reticle.connect()` goes.) This is the single highest-leverage decision; everything below assumes it.
|
|
63
|
+
|
|
64
|
+
## 3 — Emit signals from the store layer, not N call sites
|
|
65
|
+
|
|
66
|
+
The smell: every store mutation hand-emits a signal right after it, and over dozens of call sites the two **drift** — a new mutation path forgets the emit and the contract silently breaks. Drive the signal from where the state actually changes instead.
|
|
67
|
+
|
|
68
|
+
**Pattern A — store middleware (sketch).** One audited map from action → signal lives next to the store, so _state changed ⇒ signal fired_ is structural, not a thing each call site remembers.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// store-with-reticle.ts — sketch: one audited transition map, not N call sites
|
|
72
|
+
import { emit } from './emit';
|
|
73
|
+
|
|
74
|
+
const signalFor: Record<string, (next: State) => readonly [string, Record<string, unknown>]> = {
|
|
75
|
+
reorderSections: (s) => ['section:reordered', { order: s.order }],
|
|
76
|
+
addSection: (s) => ['section:added', { count: s.sections.length }],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
function dispatch(action: keyof typeof signalFor, run: () => State): void {
|
|
80
|
+
const next = run();
|
|
81
|
+
const make = signalFor[action];
|
|
82
|
+
if (make !== undefined) emit.signal(...make(next));
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Pattern B — `commitAndSignal` (lighter).** When you don't want a middleware, pair the mutation and its signal in one call that can't drift. It runs `mutate()`, emits the signal exactly once, and returns the mutation's value.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { commitAndSignal } from '@reticlehq/browser';
|
|
90
|
+
import { emit } from '../emit';
|
|
91
|
+
|
|
92
|
+
const next = commitAndSignal(
|
|
93
|
+
emit,
|
|
94
|
+
() => store.reorderSections(fromId, toId),
|
|
95
|
+
'section:reordered',
|
|
96
|
+
deriveOrder(store.getState()),
|
|
97
|
+
);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
If `mutate` throws, the mutation never happened — so **the signal is not emitted and the error propagates** unchanged.
|
|
101
|
+
|
|
102
|
+
> **The documented exception:** genuinely view-level signals — render or async completions like `diff:shown` or `caption:generated`, which aren't store state — legitimately stay in your components. Only commit-point signals belong in the store layer.
|
|
103
|
+
|
|
104
|
+
Pair this with store registration so the agent can _read_ state instead of you emitting a signal per fact: `registerStore('workspace', useWorkspace)`, then `reticle_state({ store: 'workspace' })`.
|
|
105
|
+
|
|
106
|
+
## 4 — Self-registering domains (`registerReticleDomain`)
|
|
107
|
+
|
|
108
|
+
Rather than maintaining one central flat-map of the whole testable surface (and remembering to wire each new area into it), co-locate one `reticle.ts` per domain that exports its `{ testids, signals, stores }` and self-registers. The capability registry assembles itself from every domain — later calls accumulate as a union, with no duplicates.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
// features/sections/reticle.ts — co-locate a domain's testids + signals in one module
|
|
112
|
+
import { registerReticleDomain } from '@reticlehq/browser';
|
|
113
|
+
|
|
114
|
+
export const SectionTestIds = { list: 'section-list', add: 'section-add' } as const;
|
|
115
|
+
export const SectionSignals = { reordered: 'section:reordered' } as const;
|
|
116
|
+
|
|
117
|
+
registerReticleDomain({
|
|
118
|
+
testids: Object.values(SectionTestIds),
|
|
119
|
+
signals: Object.values(SectionSignals),
|
|
120
|
+
stores: ['workspace'],
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// features/search/reticle.ts
|
|
126
|
+
import { registerReticleDomain } from '@reticlehq/browser';
|
|
127
|
+
registerReticleDomain({ testids: ['search-input'], signals: ['search:ran'] });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Importing both modules in dev makes `reticle_capabilities()` return the merged surface (`testids: ['section-list', 'section-add', 'search-input']`, `signals: ['section:reordered', 'search:ran']`, `stores: ['workspace']`). `registerReticleDomain` is a thin convenience over `registerCapabilities` — same merge-idempotent, HMR-safe semantics — so it composes with §1's "use your existing constants." (Named flows stay an explicit `registerCapabilities({ flows })` concern — their last-writer-wins semantics don't fit "accumulate from many domains.")
|
|
131
|
+
|
|
132
|
+
## 5 — Keep the signal layer from rotting (`@reticlehq/eslint-plugin`)
|
|
133
|
+
|
|
134
|
+
A signal layer silently rots: someone adds a mutation path and forgets the signal, and the agent's contract breaks with no error. The lint rule catches it at the only moment that's cheap — review.
|
|
135
|
+
|
|
136
|
+
The rule **`reticle/require-signal-on-mutation`** flags a function that calls a configured store mutator but emits no signal in the same function. It is a **safe no-op until you tell it which calls mutate state** (`mutators`) and which call emits a signal (`signalCallee`, default `signal` / `reticleSignal`):
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
// eslint.config.js (flat config)
|
|
140
|
+
import reticle from '@reticlehq/eslint-plugin';
|
|
141
|
+
|
|
142
|
+
export default [
|
|
143
|
+
{
|
|
144
|
+
plugins: { reticle },
|
|
145
|
+
rules: {
|
|
146
|
+
'reticle/require-signal-on-mutation': [
|
|
147
|
+
'warn',
|
|
148
|
+
{ mutators: ['set', 'reorderSections', 'addSection'], signalCallee: 'signal' },
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Or turn it on with the shipped preset (warns, with empty no-op defaults you then configure): `plugin.configs.recommended`. A function that calls a mutator and a signal together passes; a mutator with no signal reports `store mutation without a mapped Reticle signal`. The documented view-level exceptions from §3 simply don't list those view callees as `mutators`, so they never fire.
|
|
156
|
+
|
|
157
|
+
## 6 — Limitation: un-scriptable tabs → `reticle drive`
|
|
158
|
+
|
|
159
|
+
Reticle observes and drives a tab through the in-page SDK plus (optionally) CDP. It **cannot bring to front or recover a browser tab the OS won't let it script** — e.g. a backgrounded tab, or a non-default browser (Dia, etc.) reporting `hidden:true` / `throttled:true`.
|
|
160
|
+
|
|
161
|
+
When that happens, `reticle_sessions` and every act/assert result carry a `session.recommendation` saying so. The escape hatch is **`reticle drive <url>`** (add `--headed` to watch) — Reticle launches and owns a guaranteed-scriptable browser. See [usage §18](usage.md#18-real-input-mode--native-hover--drag) for the full note.
|
|
162
|
+
|
|
163
|
+
## Checklist
|
|
164
|
+
|
|
165
|
+
- [ ] One `app/emit.ts` is the **only** module importing `@reticlehq/browser`; components import the emitter.
|
|
166
|
+
- [ ] `reticle.connect()` is dev-gated; the prod bundle has no `@reticlehq/browser`.
|
|
167
|
+
- [ ] Signals fire from the store layer (middleware or `commitAndSignal`); view-level exceptions are explicit.
|
|
168
|
+
- [ ] Each domain self-registers via `registerReticleDomain`; `reticle_capabilities()` returns the full surface.
|
|
169
|
+
- [ ] Existing Playwright/Cypress testids are reused, not duplicated.
|
|
170
|
+
- [ ] `reticle/require-signal-on-mutation` is enabled with your `mutators` + `signalCallee`.
|
|
171
|
+
- [ ] The team knows `reticle drive <url>` for un-scriptable tabs.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Test unpublished Reticle changes in a real app (local registry)
|
|
2
|
+
|
|
3
|
+
> **For normal use, Reticle is on public npm** — just `npm i -D @reticlehq/react @reticlehq/vite-plugin` (see [Getting Started](getting-started.md)). You only need this guide to test **local, unpublished changes** to the Reticle packages in a real external app before they ship.
|
|
4
|
+
|
|
5
|
+
Because the `@reticlehq/*` packages depend on each other via the workspace protocol, plain `npm pack` tarballs don't resolve cleanly. The reliable way to exercise your in-progress changes in a real app is a tiny **local registry** (Verdaccio) — the same path CI uses to validate a publish.
|
|
6
|
+
|
|
7
|
+
## 1. Publish @reticlehq/\* to a local registry
|
|
8
|
+
|
|
9
|
+
From the Reticle repo:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bash scripts/local-registry.sh
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This starts a **fresh** Verdaccio on `http://localhost:4873`, creates a user/token, and publishes all `@reticlehq/*` packages there at the current workspace version:
|
|
16
|
+
|
|
17
|
+
| Package | What you install it for |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| **`@reticlehq/react`** | **install this** — the browser SDK kit you embed (re-exports the browser sensor, so one install gives both `reticle` and `install`) |
|
|
20
|
+
| `@reticlehq/vite-plugin` | dev-only source mapping + `connect()` injection (Vite) |
|
|
21
|
+
| `@reticlehq/next` | Next.js build wrapper (`withReticle`) |
|
|
22
|
+
| `@reticlehq/server` | the bridge + MCP server (your agent runs it, `npx @reticlehq/server mcp`) |
|
|
23
|
+
| `@reticlehq/babel-plugin` | React 19 source stamping (Babel) |
|
|
24
|
+
| `@reticlehq/test` | write declarative, signal-bound specs (`reticleTest`) |
|
|
25
|
+
| `@reticlehq/eslint-plugin` | the `require-signal-on-mutation` lint rule |
|
|
26
|
+
| `@reticlehq/core` | shared wire contract (pulled in automatically) |
|
|
27
|
+
|
|
28
|
+
For a browser app, install `@reticlehq/react` plus the build plugin for your framework (`@reticlehq/vite-plugin` or `@reticlehq/next`); `@reticlehq/server` is what your agent runs. (Verified: an external `npm i @reticlehq/react` resolves its graph, including `@reticlehq/core`, and imports correctly.) Leave the registry running.
|
|
29
|
+
|
|
30
|
+
> Note: pre-2.0 docs used a single `@reticlehq/core` umbrella package that re-exported everything; it's been split into the audience-scoped packages above.
|
|
31
|
+
|
|
32
|
+
## 2. Point your app at the local registry
|
|
33
|
+
|
|
34
|
+
In your app's project root, add an `.npmrc` (scopes only `@reticle` to the local registry; everything else still comes from npm):
|
|
35
|
+
|
|
36
|
+
```ini
|
|
37
|
+
@reticlehq:registry=http://localhost:4873/
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 3. Install + wire it up
|
|
41
|
+
|
|
42
|
+
Install the SDK kit plus the Vite build plugin (source mapping + `connect()` injection):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm i -D @reticlehq/react @reticlehq/vite-plugin
|
|
46
|
+
# Next.js instead of Vite? npm i -D @reticlehq/react @reticlehq/next
|
|
47
|
+
# optional: npm i -D @reticlehq/eslint-plugin # require-signal-on-mutation lint rule
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then follow [Getting Started](getting-started.md): embed `reticle.connect()` (dev only) from `@reticlehq/react`, add the MCP server to your agent, and (React) `install()` the adapter from `@reticlehq/react`. For the fastest agent loop, also do [Step 6 — make your app agent-legible](getting-started.md) (testids, `reticle.signal`, `registerStore`, `registerCapabilities`) and the [integration patterns](integration-patterns.md) (`createReticleEmitter` for zero prod-bundle cost).
|
|
51
|
+
|
|
52
|
+
> **Upgrading.** The packages are currently **1.2.0**; new tools land as minor bumps. `scripts/local-registry.sh` resets Verdaccio and republishes the current version, so pull the latest in your app explicitly — `npm install @reticlehq/react@latest`:
|
|
53
|
+
>
|
|
54
|
+
> ```bash
|
|
55
|
+
> npm i -D @reticlehq/react@latest @reticlehq/vite-plugin@latest @reticlehq/eslint-plugin@latest
|
|
56
|
+
> ```
|
|
57
|
+
|
|
58
|
+
**Run the MCP server** from the local registry too — `npx @reticlehq/server` _is_ the server:
|
|
59
|
+
|
|
60
|
+
```jsonc
|
|
61
|
+
// .mcp.json — point npx at the local registry so it fetches @reticlehq/server from Verdaccio
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"reticle": {
|
|
65
|
+
"command": "npx",
|
|
66
|
+
"args": ["--registry", "http://localhost:4873/", "@reticlehq/server", "mcp"],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Next.js specifics (verified on Next 15 / React 19)
|
|
73
|
+
|
|
74
|
+
`next.config.mjs`:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
import reticleNext from '@reticlehq/next';
|
|
78
|
+
/** @type {import('next').NextConfig} */
|
|
79
|
+
const nextConfig = {};
|
|
80
|
+
export default reticleNext.withReticle(nextConfig); // dev-only; keeps SWC; adds file:line mapping
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Mount the SDK from a dev-only client component (see the Next.js section in [Getting Started](getting-started.md)).
|
|
84
|
+
|
|
85
|
+
## Real input for hover/drag (optional)
|
|
86
|
+
|
|
87
|
+
Synthetic events can't trigger native `onMouseEnter`/pointer state (hover menus, tooltips, pointer drag). Enable **real input** so the server drives genuine pointer input and `reticle_act` reports `inputMode:"real"`:
|
|
88
|
+
|
|
89
|
+
- **Easiest — `reticle drive`:** Reticle launches its own scriptable, headless-capable browser at your app URL (no flags to juggle):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx --registry http://localhost:4873/ @reticlehq/server drive http://localhost:4310 # add --headed to watch
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **Or attach to your own browser:** launch it with `--remote-debugging-port=9222`, then point the MCP server at it via `env`:
|
|
96
|
+
|
|
97
|
+
```jsonc
|
|
98
|
+
// .mcp.json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"reticle": {
|
|
102
|
+
"command": "npx",
|
|
103
|
+
"args": ["--registry", "http://localhost:4873/", "@reticlehq/server", "mcp"],
|
|
104
|
+
"env": { "RETICLE_CDP_URL": "http://localhost:9222" },
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
With neither set, Reticle stays synthetic (zero extra deps) and says so via `inputMode`. See [usage §18](usage.md#18-real-input-mode--native-hover--drag).
|
|
111
|
+
|
|
112
|
+
## Write replayable specs + git-checked flows
|
|
113
|
+
|
|
114
|
+
- **Specs:** with `@reticlehq/test`, turn checks into `reticleTest("…", async t => { await t.act(...); await t.expectSignal(...) })` — signal/testid-bound, `reticle_clock` for determinism, `t.expectInputModeReal()` to skip-with-reason when real input isn't active. Run them headless via `reticle drive` (the same path CI uses).
|
|
115
|
+
- **Flows:** record a flow once and Reticle writes it to a git-checked `.reticle/flows/<name>.json` (anchored on testid/signal); `reticle_flow_replay` re-resolves anchors at run time and reports **legible drift** with a nearest-match; `reticle_flow_heal` proposes/applies the rebind. A fresh agent reads `.reticle/contract.json` to learn your testable surface without grepping source.
|
|
116
|
+
|
|
117
|
+
## When you're ready for real npm
|
|
118
|
+
|
|
119
|
+
The same packages publish to public npm unchanged — `pnpm -r publish --access public` after `npm login`. The Verdaccio run above is a faithful rehearsal of that.
|
|
120
|
+
|
|
121
|
+
## Cleanup
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pkill -f verdaccio # stop the local registry
|
|
125
|
+
# remove the line from your app's .npmrc when you switch to published packages
|
|
126
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reticle": {
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"commit": "33629413"
|
|
5
|
+
},
|
|
6
|
+
"client": {
|
|
7
|
+
"id": "claude-code"
|
|
8
|
+
},
|
|
9
|
+
"host": {
|
|
10
|
+
"os": "darwin 25.5.0",
|
|
11
|
+
"node": "v22.14.0"
|
|
12
|
+
},
|
|
13
|
+
"checks": {
|
|
14
|
+
"snippet": true
|
|
15
|
+
},
|
|
16
|
+
"verdict": "runnable-unverified",
|
|
17
|
+
"by": "machine (client-compat.mjs)",
|
|
18
|
+
"note": "machine-checkable half only — nobody has run this client"
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reticle": {
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"commit": "33629413"
|
|
5
|
+
},
|
|
6
|
+
"client": {
|
|
7
|
+
"id": "codex"
|
|
8
|
+
},
|
|
9
|
+
"host": {
|
|
10
|
+
"os": "darwin 25.5.0",
|
|
11
|
+
"node": "v22.14.0"
|
|
12
|
+
},
|
|
13
|
+
"checks": {
|
|
14
|
+
"writes": false,
|
|
15
|
+
"snippet": true
|
|
16
|
+
},
|
|
17
|
+
"verdict": "manual-snippet",
|
|
18
|
+
"by": "machine (client-compat.mjs)",
|
|
19
|
+
"note": "machine-checkable half only — nobody has run this client"
|
|
20
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reticle": {
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"commit": "33629413"
|
|
5
|
+
},
|
|
6
|
+
"client": {
|
|
7
|
+
"id": "cursor"
|
|
8
|
+
},
|
|
9
|
+
"host": {
|
|
10
|
+
"os": "darwin 25.5.0",
|
|
11
|
+
"node": "v22.14.0"
|
|
12
|
+
},
|
|
13
|
+
"checks": {
|
|
14
|
+
"writes": true,
|
|
15
|
+
"parses": true,
|
|
16
|
+
"initialize": true,
|
|
17
|
+
"tools": 17
|
|
18
|
+
},
|
|
19
|
+
"verdict": "runnable-unverified",
|
|
20
|
+
"by": "machine (client-compat.mjs)",
|
|
21
|
+
"note": "machine-checkable half only — nobody has run this client"
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reticle": {
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"commit": "33629413"
|
|
5
|
+
},
|
|
6
|
+
"client": {
|
|
7
|
+
"id": "gemini"
|
|
8
|
+
},
|
|
9
|
+
"host": {
|
|
10
|
+
"os": "darwin 25.5.0",
|
|
11
|
+
"node": "v22.14.0"
|
|
12
|
+
},
|
|
13
|
+
"checks": {
|
|
14
|
+
"writes": true,
|
|
15
|
+
"parses": true,
|
|
16
|
+
"initialize": true,
|
|
17
|
+
"tools": 17
|
|
18
|
+
},
|
|
19
|
+
"verdict": "runnable-unverified",
|
|
20
|
+
"by": "machine (client-compat.mjs)",
|
|
21
|
+
"note": "machine-checkable half only — nobody has run this client"
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reticle": {
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"commit": "33629413"
|
|
5
|
+
},
|
|
6
|
+
"client": {
|
|
7
|
+
"id": "opencode"
|
|
8
|
+
},
|
|
9
|
+
"host": {
|
|
10
|
+
"os": "darwin 25.5.0",
|
|
11
|
+
"node": "v22.14.0"
|
|
12
|
+
},
|
|
13
|
+
"checks": {
|
|
14
|
+
"writes": true,
|
|
15
|
+
"parses": true,
|
|
16
|
+
"initialize": true,
|
|
17
|
+
"tools": 17
|
|
18
|
+
},
|
|
19
|
+
"verdict": "runnable-unverified",
|
|
20
|
+
"by": "machine (client-compat.mjs)",
|
|
21
|
+
"note": "machine-checkable half only — nobody has run this client"
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reticle": {
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"commit": "33629413"
|
|
5
|
+
},
|
|
6
|
+
"client": {
|
|
7
|
+
"id": "vscode"
|
|
8
|
+
},
|
|
9
|
+
"host": {
|
|
10
|
+
"os": "darwin 25.5.0",
|
|
11
|
+
"node": "v22.14.0"
|
|
12
|
+
},
|
|
13
|
+
"checks": {
|
|
14
|
+
"writes": true,
|
|
15
|
+
"parses": true,
|
|
16
|
+
"initialize": true,
|
|
17
|
+
"tools": 17
|
|
18
|
+
},
|
|
19
|
+
"verdict": "runnable-unverified",
|
|
20
|
+
"by": "machine (client-compat.mjs)",
|
|
21
|
+
"note": "machine-checkable half only — nobody has run this client"
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reticle": {
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"commit": "33629413"
|
|
5
|
+
},
|
|
6
|
+
"client": {
|
|
7
|
+
"id": "windsurf"
|
|
8
|
+
},
|
|
9
|
+
"host": {
|
|
10
|
+
"os": "darwin 25.5.0",
|
|
11
|
+
"node": "v22.14.0"
|
|
12
|
+
},
|
|
13
|
+
"checks": {
|
|
14
|
+
"writes": true,
|
|
15
|
+
"parses": true,
|
|
16
|
+
"initialize": true,
|
|
17
|
+
"tools": 17
|
|
18
|
+
},
|
|
19
|
+
"verdict": "runnable-unverified",
|
|
20
|
+
"by": "machine (client-compat.mjs)",
|
|
21
|
+
"note": "machine-checkable half only — nobody has run this client"
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Client matrix
|
|
2
|
+
|
|
3
|
+
> Which MCP clients Reticle is known to work in, and on whose machine that was measured.
|
|
4
|
+
> Generated by `node apps/e2e/matrix.mjs` — do not edit by hand.
|
|
5
|
+
|
|
6
|
+
| | meaning |
|
|
7
|
+
| --- | --- |
|
|
8
|
+
| ✅ | a human ran the real client and the tools were usable |
|
|
9
|
+
| ◐ | `init` wrote a runnable entry where the client documents it — **nobody has run that client** |
|
|
10
|
+
| ✎ | `init` will not write this format (TOML); the user pastes a block |
|
|
11
|
+
| ❌ | somebody ran it and it did not work — the most useful row here |
|
|
12
|
+
|
|
13
|
+
**◐ is not a tick.** It means the half a machine can check passed. Only ✅ means somebody saw
|
|
14
|
+
Reticle working in that client.
|
|
15
|
+
|
|
16
|
+
## 2.5.0
|
|
17
|
+
|
|
18
|
+
| client | verdict | tools | os | submitted by |
|
|
19
|
+
| --- | --- | --- | --- | --- |
|
|
20
|
+
| claude-code | ◐ runnable-unverified | — | darwin 25.5.0 | machine (client-compat.mjs) |
|
|
21
|
+
| codex | ✎ manual-snippet | — | darwin 25.5.0 | machine (client-compat.mjs) |
|
|
22
|
+
| cursor | ◐ runnable-unverified | — | darwin 25.5.0 | machine (client-compat.mjs) |
|
|
23
|
+
| gemini | ◐ runnable-unverified | — | darwin 25.5.0 | machine (client-compat.mjs) |
|
|
24
|
+
| opencode | ◐ runnable-unverified | — | darwin 25.5.0 | machine (client-compat.mjs) |
|
|
25
|
+
| vscode | ◐ runnable-unverified | — | darwin 25.5.0 | machine (client-compat.mjs) |
|
|
26
|
+
| windsurf | ◐ runnable-unverified | — | darwin 25.5.0 | machine (client-compat.mjs) |
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Submitting a client-matrix record
|
|
2
|
+
|
|
3
|
+
[`MATRIX.md`](./MATRIX.md) says which MCP clients Reticle is known to work in. Right now most rows are **◐** — `init` writes a runnable entry where that client documents it, and **nobody has run that client**. Turning a ◐ into a ✅ takes about three minutes and is the single most useful thing an outside contributor can do for this project.
|
|
4
|
+
|
|
5
|
+
You do not tell us it works. You **run one command, run your client, and submit what came out** — the same shape as [CNCF's Kubernetes conformance](https://github.com/cncf/k8s-conformance), and for the same reason: a claim nobody can trace back to a machine and a commit cannot gate anything.
|
|
6
|
+
|
|
7
|
+
## 1. The machine half (no client needed)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm build
|
|
11
|
+
node apps/e2e/client-compat.mjs --only <client> --json /tmp/compat.json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
That checks `init` writes a config where your client documents it, that it parses under **that client's own key**, and that the command inside it starts and advertises tools. It does **not** check that your client reads it — which is the whole reason step 2 exists.
|
|
15
|
+
|
|
16
|
+
## 2. The human half (this is the part that matters)
|
|
17
|
+
|
|
18
|
+
1. Run `npx @reticlehq/server init` in a real project.
|
|
19
|
+
2. **Restart your client.** Every one of them reads MCP config at startup; this is the step people skip and then report a bug about.
|
|
20
|
+
3. Ask it to list Reticle's tools. Note **how many** it lists.
|
|
21
|
+
4. Kill the daemon (`lsof -nP -iTCP:4400 -sTCP:LISTEN -t | xargs kill`) and use a Reticle tool again. Does the client recover on its own, or does it need a manual reconnect?
|
|
22
|
+
5. If anything failed, copy your client's error text **verbatim**. That sentence is worth more than the rest of the record put together — it is the only thing that says what your client actually thinks went wrong.
|
|
23
|
+
|
|
24
|
+
## 3. Submit it
|
|
25
|
+
|
|
26
|
+
Write `docs/matrix/<reticle-version>/<client>-<yourhandle>.json`:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"reticle": { "version": "2.5.0", "commit": "abc1234" },
|
|
31
|
+
"client": { "id": "windsurf", "version": "1.2.3" },
|
|
32
|
+
"host": { "os": "darwin 25.5.0", "node": "v22.14.0" },
|
|
33
|
+
"checks": {
|
|
34
|
+
"registered": true,
|
|
35
|
+
"toolsVisible": 17,
|
|
36
|
+
"survivedDaemonRestart": true,
|
|
37
|
+
"clientError": ""
|
|
38
|
+
},
|
|
39
|
+
"verdict": "works",
|
|
40
|
+
"by": "your-github-handle"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Validate it, regenerate the table, open a PR:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
node apps/e2e/matrix.mjs --validate docs/matrix/<version>/<file>.json
|
|
48
|
+
node apps/e2e/matrix.mjs # regenerates MATRIX.md
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
CI runs the same validator on every PR, so a malformed record fails before a human reads it.
|
|
52
|
+
|
|
53
|
+
## What the validator insists on, and why
|
|
54
|
+
|
|
55
|
+
- **`works` requires `checks.toolsVisible > 0`.** "It works" with no number is the self-report this whole flow exists to replace.
|
|
56
|
+
- **`broken` requires `checks.clientError`** — your client's own words, verbatim. A failure nobody can act on is worse than no record.
|
|
57
|
+
- **Every record needs a host and a version.** A cell nobody can place is decoration.
|
|
58
|
+
|
|
59
|
+
**A `broken` record is as welcome as a `works` one and passes exactly the same checks.** It is usually more useful: it is how we find out that a client changed its config path, or that our entry shape stopped being read. If your client does not work, that is the record we most want.
|
package/docs/mint.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://mintlify.com/schema.json",
|
|
3
|
+
"name": "Reticle",
|
|
4
|
+
"logo": {
|
|
5
|
+
"light": "/logo/light.svg",
|
|
6
|
+
"dark": "/logo/dark.svg"
|
|
7
|
+
},
|
|
8
|
+
"favicon": "/favicon.svg",
|
|
9
|
+
"colors": {
|
|
10
|
+
"primary": "#8b7bff",
|
|
11
|
+
"light": "#a99bff",
|
|
12
|
+
"dark": "#6b5bdf",
|
|
13
|
+
"background": {
|
|
14
|
+
"dark": "#0b0d12"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"topbarLinks": [
|
|
18
|
+
{
|
|
19
|
+
"name": "GitHub",
|
|
20
|
+
"url": "https://github.com/reticlehq/reticle"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"topbarCtaButton": {
|
|
24
|
+
"name": "npm",
|
|
25
|
+
"url": "https://www.npmjs.com/package/@reticlehq/react"
|
|
26
|
+
},
|
|
27
|
+
"navigation": [
|
|
28
|
+
{
|
|
29
|
+
"group": "Get started",
|
|
30
|
+
"pages": ["getting-started", "local-registry"]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"group": "Integrate",
|
|
34
|
+
"pages": [
|
|
35
|
+
"platform-integration",
|
|
36
|
+
"desktop-apps",
|
|
37
|
+
"integration-patterns",
|
|
38
|
+
"flows",
|
|
39
|
+
"testing",
|
|
40
|
+
"multi-agent-testing",
|
|
41
|
+
"human-control",
|
|
42
|
+
"deploy-checks"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"group": "Concepts",
|
|
47
|
+
"pages": ["architecture", "benchmarks", "token-efficiency"]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"group": "Reference",
|
|
51
|
+
"pages": ["usage", "agent-cheatsheet", "telemetry"]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"group": "Enterprise",
|
|
55
|
+
"pages": ["enterprise"]
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"footerSocials": {
|
|
59
|
+
"github": "https://github.com/reticlehq/reticle"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Multi-agent & multi-project testing
|
|
2
|
+
|
|
3
|
+
Reticle is built for the messy real world: several apps running at once, ports that shift between runs, and many agents driving different flows of the same app in parallel — without each one spinning up its own Chromium. This page explains how that works and how to use it.
|
|
4
|
+
|
|
5
|
+
## The mental model
|
|
6
|
+
|
|
7
|
+
- **One daemon per machine.** `reticle mcp` discovers a running daemon (via `~/.reticle`) or starts one. A crashed daemon's stale pidfile is reclaimed automatically, so you never chase "port already in use" or a zombie server.
|
|
8
|
+
- **Identity is the app, not the port.** The build plugin stamps a stable `projectId` that travels in every connection. If your Next app usually runs on `:3000` but boots on `:3001` today, Reticle still knows which app it is — and an agent scoped to project A will never accidentally drive project B's tab. Origin is only a fallback hint.
|
|
9
|
+
- **One browser, many contexts.** When agents need their own headless tabs, the daemon's **browser pool** launches a single Chromium and hands out isolated contexts (one per flow) — cheap, and capped so a big fan-out can't exhaust the machine. Over-cap requests queue.
|
|
10
|
+
- **Attach-only.** Reticle never starts your dev server. It connects to an app you're already running (or opens a headless tab pointed at it).
|
|
11
|
+
|
|
12
|
+
## Manual testing — ~5 minutes
|
|
13
|
+
|
|
14
|
+
1. Add the plugin (Vite/Next) or one `reticle.connect()` call. (See [getting-started](./getting-started.md).)
|
|
15
|
+
2. Start your app as you normally do.
|
|
16
|
+
3. Open it in a browser — the in-page panel shows Reticle is connected.
|
|
17
|
+
4. Click around; flag anything that looks wrong with the "Flag a bug" annotator. The agent drains those with `reticle_session {action:"review"}`.
|
|
18
|
+
|
|
19
|
+
## Agent testing — ~2 minutes
|
|
20
|
+
|
|
21
|
+
With the app running and instrumented, an agent drives a flow end to end:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
reticle_lease {action:"acquire"} { url: "http://localhost:3000/dashboard" }
|
|
25
|
+
→ { sessionId: "lease-…", ready: true, leased: 1, queued: 0 }
|
|
26
|
+
reticle_act { sessionId, ... } # drive the flow
|
|
27
|
+
reticle_assert { sessionId, ... } # verify intent
|
|
28
|
+
reticle_lease {action:"release"} { sessionId } # free the slot
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`reticle_lease {action:"acquire"}` opens a fresh isolated headless context against your **already-running** app, stamps the lease identity into the URL so the app's own SDK registers under a sessionId you can target, and waits until that tab has connected (`ready: true`) before returning — so the sessionId is usable immediately. Release when the flow finishes.
|
|
32
|
+
|
|
33
|
+
## 10 agents, 10 flows, one dashboard
|
|
34
|
+
|
|
35
|
+
This is the design target, and it needs no special setup:
|
|
36
|
+
|
|
37
|
+
- Each agent calls `reticle_lease {action:"acquire"}` for the same dashboard URL → its own isolated context (own cookies/storage) in the **one** shared Chromium.
|
|
38
|
+
- The pool caps simultaneous contexts (`RETICLE_MAX_CONTEXTS`, default scales with CPU under a ceiling); extra acquires queue and proceed as slots free.
|
|
39
|
+
- Flows can't bleed into each other — contexts are isolated and every session is scoped by `projectId`.
|
|
40
|
+
- A single crashed page is reclaimed on its own, and if an agent crashes or hangs its lease stops being touched and the **lease reaper** reclaims the context after a TTL, freeing the slot. One dead agent never starves the others.
|
|
41
|
+
|
|
42
|
+
`reticle_sessions` lists everything with `projectId` (group by app) and `leased` (pool context vs a human tab), so an orchestrator can see the whole fleet at a glance.
|
|
43
|
+
|
|
44
|
+
## Knobs
|
|
45
|
+
|
|
46
|
+
| Env | Default | Effect |
|
|
47
|
+
| --- | --- | --- |
|
|
48
|
+
| `RETICLE_MAX_CONTEXTS` | `min(8, cpus-1)` | Max simultaneous leased headless contexts. |
|
|
49
|
+
| `RETICLE_PORT` | from `.reticle.json`, else `4400` | Daemon port (rarely needed — discovery handles it). |
|
|
50
|
+
|
|
51
|
+
## Why not just open many browsers?
|
|
52
|
+
|
|
53
|
+
Ten Chromiums is hundreds of MB each and will thrash a laptop. Ten contexts in one browser is a few MB apiece and fully isolated — same correctness, a fraction of the cost. That's the whole point of the pool.
|
|
54
|
+
|
|
55
|
+
**Measured** (`bench/harness/multi-agent-throughput.mjs`): 16 verification flows that take **35.4s** one-at-a-time finish in **5.2s** across 8 leased contexts on a single Chromium — **6.78× faster**, ~30s saved per batch, with all 8 contexts live at peak. The speed-up scales with agent count up to the cap; the win over launching a browser per agent grows with how much per-agent browser startup you avoid.
|