@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
|
@@ -16,9 +16,35 @@
|
|
|
16
16
|
* human never agreed to interrupt. So the envelope carries the fact and the exact command, and the
|
|
17
17
|
* decision stays with the people whose machine it is.
|
|
18
18
|
*/
|
|
19
|
-
import { checkForUpdate } from './update-checker.js';
|
|
20
|
-
import { SERVER_VERSION } from '../server-version.js';
|
|
19
|
+
import { checkForUpdate, loadManifest } from './update-checker.js';
|
|
20
|
+
import { SERVER_VERSION } from '../version/server-version.js';
|
|
21
21
|
import { log } from '../log.js';
|
|
22
|
+
import { creditNudge } from './nudge-credit.js';
|
|
23
|
+
/**
|
|
24
|
+
* Is `candidate` a strictly NEWER release than `current`?
|
|
25
|
+
*
|
|
26
|
+
* Replaces a plain `!==`, which told anyone on a version newer than the published one — a
|
|
27
|
+
* prerelease, a local build, a rollback in progress — to "update" to something older. A nudge that
|
|
28
|
+
* prompts a downgrade is one people learn to ignore, and this is the mechanism the entire adoption
|
|
29
|
+
* story rests on. Numeric per segment, so 2.10.0 correctly beats 2.9.0 (string order does not), and
|
|
30
|
+
* a bare release beats its own prerelease.
|
|
31
|
+
*/
|
|
32
|
+
export function isNewerVersion(candidate, current) {
|
|
33
|
+
const parse = (v) => {
|
|
34
|
+
const [core = '', ...rest] = v.split(/[-+]/);
|
|
35
|
+
return { parts: core.split('.').map((n) => Number.parseInt(n, 10) || 0), pre: rest.length > 0 };
|
|
36
|
+
};
|
|
37
|
+
const a = parse(candidate);
|
|
38
|
+
const b = parse(current);
|
|
39
|
+
for (let i = 0; i < 3; i++) {
|
|
40
|
+
const left = a.parts[i] ?? 0;
|
|
41
|
+
const right = b.parts[i] ?? 0;
|
|
42
|
+
if (left !== right)
|
|
43
|
+
return left > right;
|
|
44
|
+
}
|
|
45
|
+
// Same numbers: a final release is newer than its own prerelease, and nothing else counts.
|
|
46
|
+
return !a.pre && b.pre;
|
|
47
|
+
}
|
|
22
48
|
/** Let the daemon finish coming up before touching the network. */
|
|
23
49
|
const CHECK_DELAY_MS = 8_000;
|
|
24
50
|
const UPDATE_COMMAND = 'reticle update';
|
|
@@ -29,20 +55,63 @@ let delivered = false;
|
|
|
29
55
|
* for 24h on disk, and falls back to the cached manifest when the registry is unreachable — so this
|
|
30
56
|
* costs one npm registry request per day per machine, and nothing at all when offline.
|
|
31
57
|
*/
|
|
58
|
+
/** How much of the release description may ride on a tool result. See buildNudge. */
|
|
59
|
+
const MAX_CHANGELOG_CHARS = 240;
|
|
60
|
+
const MAX_LISTED_BREAKING = 4;
|
|
61
|
+
/**
|
|
62
|
+
* Describe the release, not just its number.
|
|
63
|
+
*
|
|
64
|
+
* `packages/server/package.json` carries `reticle.changelog` and `reticle.breakingChanges`, and
|
|
65
|
+
* `update-checker` parses both into the manifest — where they stopped. This function named two
|
|
66
|
+
* versions and nothing else, so both fields were written, shipped, parsed and discarded. Latent
|
|
67
|
+
* metadata nobody ever printed.
|
|
68
|
+
*
|
|
69
|
+
* Survivable while releases are additive; actively harmful for one that is not. An agent told only
|
|
70
|
+
* "2.4.1 → 2.5.0" runs `reticle update` mid-task and discovers a retired environment variable and
|
|
71
|
+
* six newly-strict parameters by breaking on them.
|
|
72
|
+
*
|
|
73
|
+
* Bounded on purpose: this rides on a tool result every turn until it is delivered, so a long
|
|
74
|
+
* changelog is a per-turn tax. The count of anything elided is stated rather than silently dropped.
|
|
75
|
+
*/
|
|
76
|
+
export function buildNudge(latestVersion, currentVersion, release) {
|
|
77
|
+
const changelog = (release?.changelog ?? '').trim();
|
|
78
|
+
const breaking = (release?.breakingChanges ?? []).filter((line) => 0 < line.trim().length);
|
|
79
|
+
const listed = breaking.slice(0, MAX_LISTED_BREAKING);
|
|
80
|
+
const rest = breaking.length - listed.length;
|
|
81
|
+
return {
|
|
82
|
+
currentVersion,
|
|
83
|
+
latestVersion,
|
|
84
|
+
command: UPDATE_COMMAND,
|
|
85
|
+
action: `A newer Reticle is available (${currentVersion} → ${latestVersion}). ` +
|
|
86
|
+
('' === changelog ? '' : `${changelog.slice(0, MAX_CHANGELOG_CHARS)} `) +
|
|
87
|
+
(0 === breaking.length
|
|
88
|
+
? ''
|
|
89
|
+
: `This release has BREAKING changes: ${listed.join('; ')}` +
|
|
90
|
+
(0 < rest ? ` (+${String(rest)} more — see the changelog)` : '') +
|
|
91
|
+
'. Read them before updating. ') +
|
|
92
|
+
`Tell the human, and run \`${UPDATE_COMMAND}\` if they agree — it restarts the daemon, ` +
|
|
93
|
+
'so do it between tasks rather than mid-verification. Continue your current task first.',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
32
96
|
export function startUpdateCheck(now = () => Date.now()) {
|
|
97
|
+
// The cached answer first, so the very first tool call can carry it — see armUpdateNudgeFrom.
|
|
98
|
+
armUpdateNudgeFrom(loadManifest());
|
|
33
99
|
setTimeout(() => {
|
|
34
100
|
void checkForUpdate(SERVER_VERSION, now)
|
|
35
101
|
.then((manifest) => {
|
|
36
102
|
if (!manifest.updateAvailable || manifest.latestVersion === undefined)
|
|
37
103
|
return;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
104
|
+
if (!isNewerVersion(manifest.latestVersion, SERVER_VERSION))
|
|
105
|
+
return;
|
|
106
|
+
// WITH the release description. The manifest has carried `changelog` and `breakingChanges`
|
|
107
|
+
// all along; nothing read them.
|
|
108
|
+
pending = buildNudge(manifest.latestVersion, SERVER_VERSION, {
|
|
109
|
+
...(manifest.changelog === undefined ? {} : { changelog: manifest.changelog }),
|
|
110
|
+
...(manifest.breakingChanges === undefined
|
|
111
|
+
? {}
|
|
112
|
+
: { breakingChanges: manifest.breakingChanges }),
|
|
113
|
+
});
|
|
114
|
+
delivered = false;
|
|
46
115
|
log('reticle_update_available', {
|
|
47
116
|
from: SERVER_VERSION,
|
|
48
117
|
to: manifest.latestVersion,
|
|
@@ -53,20 +122,64 @@ export function startUpdateCheck(now = () => Date.now()) {
|
|
|
53
122
|
});
|
|
54
123
|
}, CHECK_DELAY_MS).unref();
|
|
55
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* What `reticle update` should install, or undefined when it should do nothing.
|
|
127
|
+
*
|
|
128
|
+
* `handleUpdate` gated on `manifest.updateAvailable`, a plain `latest !== current`, so whenever the
|
|
129
|
+
* registry's latest was OLDER than the running build — a prerelease, a local build, a rollback in
|
|
130
|
+
* progress, a stale npx cache — it announced the move and then installed the downgrade. Reported by
|
|
131
|
+
* a real user on 2026-08-06: "both the `update_available` banner and `reticle update` report the
|
|
132
|
+
* current and target versions swapped, so an upgrade is described as a downgrade."
|
|
133
|
+
*/
|
|
134
|
+
export function updateTarget(manifest, currentVersion = SERVER_VERSION) {
|
|
135
|
+
const latest = manifest.latestVersion;
|
|
136
|
+
if (latest === undefined || !isNewerVersion(latest, currentVersion))
|
|
137
|
+
return undefined;
|
|
138
|
+
return latest;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Arm from an already-cached answer, synchronously, at daemon boot.
|
|
142
|
+
*
|
|
143
|
+
* The network check fires 8s after boot and the nudge is delivered by riding a tool result — but
|
|
144
|
+
* half the sessions that use Reticle at all make exactly ONE tool call, usually in the first
|
|
145
|
+
* seconds. The check had not finished, so the single chance to tell them was gone, and the
|
|
146
|
+
* population most in need of the update is precisely the one that never heard about it. Yesterday's
|
|
147
|
+
* answer is already on disk; use it now and let the network refresh behind it.
|
|
148
|
+
*/
|
|
149
|
+
export function armUpdateNudgeFrom(cached, currentVersion = SERVER_VERSION) {
|
|
150
|
+
const latest = cached?.latestVersion;
|
|
151
|
+
if (latest === undefined || !isNewerVersion(latest, currentVersion))
|
|
152
|
+
return;
|
|
153
|
+
// The CACHED path matters more than the live one: it is what the very first tool call carries, and
|
|
154
|
+
// it is the only nudge an offline machine ever sees. Dropping the release description here would
|
|
155
|
+
// have left the fields unread on the commonest path while looking fixed.
|
|
156
|
+
pending = buildNudge(latest, currentVersion, {
|
|
157
|
+
...(cached?.changelog === undefined ? {} : { changelog: cached.changelog }),
|
|
158
|
+
...(cached?.breakingChanges === undefined ? {} : { breakingChanges: cached.breakingChanges }),
|
|
159
|
+
});
|
|
160
|
+
delivered = false;
|
|
161
|
+
}
|
|
56
162
|
/** The nudge, once. Returns undefined afterwards so a long session is told exactly one time. */
|
|
57
163
|
export function takeUpdateNudge() {
|
|
58
164
|
if (delivered || pending === undefined)
|
|
59
165
|
return undefined;
|
|
60
166
|
delivered = true;
|
|
167
|
+
// Leave a mark an `reticle update` in another process can read, so version_changed can say whether
|
|
168
|
+
// this nudge is what caused the upgrade. Best-effort by construction — see nudge-credit.
|
|
169
|
+
creditNudge(pending.latestVersion);
|
|
61
170
|
return pending;
|
|
62
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* The newer version, if the cached answer says there is one — WITHOUT consuming the agent's
|
|
174
|
+
* one-shot nudge. `reticle status` is a human reading a terminal; the two channels are separate
|
|
175
|
+
* audiences and must not steal each other's message.
|
|
176
|
+
*/
|
|
177
|
+
export function availableUpdate(currentVersion = SERVER_VERSION) {
|
|
178
|
+
const latest = loadManifest()?.latestVersion;
|
|
179
|
+
return latest !== undefined && isNewerVersion(latest, currentVersion) ? latest : undefined;
|
|
180
|
+
}
|
|
63
181
|
/** Tests only — drop the module state so each case starts clean. */
|
|
64
182
|
export function resetUpdateNudge() {
|
|
65
183
|
pending = undefined;
|
|
66
184
|
delivered = false;
|
|
67
185
|
}
|
|
68
|
-
/** Tests only — seed a pending nudge without going near the network. */
|
|
69
|
-
export function setPendingUpdate(nudge) {
|
|
70
|
-
pending = nudge;
|
|
71
|
-
delivered = false;
|
|
72
|
-
}
|
package/dist/update/updater.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Which way the installed version moved. A rollback means a release hurt someone enough to retreat. */
|
|
2
|
-
|
|
2
|
+
declare const VersionChangeDirection: {
|
|
3
3
|
readonly UPDATE: "update";
|
|
4
4
|
readonly ROLLBACK: "rollback";
|
|
5
5
|
};
|
|
@@ -22,14 +22,6 @@ declare const ExecutionKind: {
|
|
|
22
22
|
readonly LOCAL: "local";
|
|
23
23
|
};
|
|
24
24
|
type ExecutionKind = (typeof ExecutionKind)[keyof typeof ExecutionKind];
|
|
25
|
-
/**
|
|
26
|
-
* Infer how reticle was launched from process.argv[1].
|
|
27
|
-
*
|
|
28
|
-
* npm's npx cache stores packages under a path containing `_npx`, which is the
|
|
29
|
-
* most reliable cross-platform signal. Local installs always live inside a
|
|
30
|
-
* `node_modules` directory. Everything else is treated as a global install.
|
|
31
|
-
*/
|
|
32
|
-
export declare function detectExecutionKind(): ExecutionKind;
|
|
33
25
|
/**
|
|
34
26
|
* Classify a launch from the entry script path. Pure so it's testable. Global installs ALSO live under
|
|
35
27
|
* a `node_modules`, but under the npm global prefix — a `lib/node_modules` (unix: /usr/local, Homebrew,
|
package/dist/update/updater.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
+
import { NodePlatform } from '../platform.js';
|
|
2
3
|
import { existsSync } from 'node:fs';
|
|
3
4
|
import { platform } from 'node:os';
|
|
4
5
|
import { dirname, join } from 'node:path';
|
|
5
6
|
import { loadManifest, saveManifest } from './update-checker.js';
|
|
6
|
-
import { RETICLE_NPM_PACKAGE, SERVER_VERSION } from '../server-version.js';
|
|
7
|
+
import { RETICLE_NPM_PACKAGE, SERVER_VERSION } from '../version/server-version.js';
|
|
7
8
|
import { log } from '../log.js';
|
|
8
9
|
import { TelemetryEventKind } from '@reticlehq/core';
|
|
9
10
|
import { getTelemetry } from '../telemetry/telemetry.js';
|
|
11
|
+
import { wasNudged } from './nudge-credit.js';
|
|
10
12
|
/** Which way the installed version moved. A rollback means a release hurt someone enough to retreat. */
|
|
11
|
-
|
|
13
|
+
const VersionChangeDirection = {
|
|
12
14
|
UPDATE: 'update',
|
|
13
15
|
ROLLBACK: 'rollback',
|
|
14
16
|
};
|
|
@@ -21,15 +23,18 @@ export const VersionChangeDirection = {
|
|
|
21
23
|
*/
|
|
22
24
|
export async function reportVersionChange(from, to, direction) {
|
|
23
25
|
try {
|
|
26
|
+
const nudged = wasNudged(to);
|
|
24
27
|
await getTelemetry().emit(TelemetryEventKind.VERSION_CHANGED, {
|
|
25
|
-
|
|
28
|
+
// Only when true: an absent flag reads as "we do not know", which is honest for a machine that
|
|
29
|
+
// updated from a shell script or a package bump with no agent involved at all.
|
|
30
|
+
versionChange: { from, to, direction, ...(nudged ? { nudged } : {}) },
|
|
26
31
|
});
|
|
27
32
|
}
|
|
28
33
|
catch {
|
|
29
34
|
/* best-effort, like every other send */
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
|
-
const NPM_BIN = platform()
|
|
37
|
+
const NPM_BIN = NodePlatform.WINDOWS === platform() ? 'npm.cmd' : 'npm';
|
|
33
38
|
const NPM_TIMEOUT_MS = 120_000;
|
|
34
39
|
/** How this reticle process was launched — determines which npm strategy to use for updates. */
|
|
35
40
|
const ExecutionKind = {
|
|
@@ -47,7 +52,7 @@ const ExecutionKind = {
|
|
|
47
52
|
* most reliable cross-platform signal. Local installs always live inside a
|
|
48
53
|
* `node_modules` directory. Everything else is treated as a global install.
|
|
49
54
|
*/
|
|
50
|
-
|
|
55
|
+
function detectExecutionKind() {
|
|
51
56
|
return classifyExecutionKind(process.argv[1] ?? '');
|
|
52
57
|
}
|
|
53
58
|
/**
|
|
@@ -116,7 +121,7 @@ export function installArgs(version, kind, localRoot) {
|
|
|
116
121
|
async function installVersion(version, kind) {
|
|
117
122
|
const localRoot = kind === ExecutionKind.LOCAL ? findLocalProjectRoot() : null;
|
|
118
123
|
const plan = installArgs(version, kind, localRoot);
|
|
119
|
-
if (
|
|
124
|
+
if (null === plan) {
|
|
120
125
|
// npx re-resolves the package from npm on the next Claude Code restart — no npm
|
|
121
126
|
// install needed. The restart itself is what triggers the update.
|
|
122
127
|
log('reticle_update_npx_strategy', {
|
|
@@ -124,7 +129,7 @@ async function installVersion(version, kind) {
|
|
|
124
129
|
});
|
|
125
130
|
return;
|
|
126
131
|
}
|
|
127
|
-
if (kind === ExecutionKind.LOCAL &&
|
|
132
|
+
if (kind === ExecutionKind.LOCAL && null === localRoot) {
|
|
128
133
|
// Could not find a project root — fell back to a global install as a safe default.
|
|
129
134
|
log('reticle_update_local_no_root', { fallback: 'global' });
|
|
130
135
|
}
|
|
@@ -166,7 +171,7 @@ export async function applyUpdate(targetVersion) {
|
|
|
166
171
|
*/
|
|
167
172
|
export async function rollback() {
|
|
168
173
|
const manifest = loadManifest();
|
|
169
|
-
if (
|
|
174
|
+
if (null === manifest || manifest.previousVersion === undefined) {
|
|
170
175
|
throw new Error('No previous version available for rollback');
|
|
171
176
|
}
|
|
172
177
|
const prev = manifest.previousVersion;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const PEER_VERSION_PARAM = "peerVersion";
|
|
2
|
+
export declare const PEER_CONTRACT_PARAM = "peerContract";
|
|
3
|
+
/** Compare an attaching agent process against this daemon, and queue the nudge when they disagree. */
|
|
4
|
+
export declare function noteAgentPeer(version: string | null, contract: string | null): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How another Reticle PROCESS announces itself to the daemon.
|
|
3
|
+
*
|
|
4
|
+
* The SDK announces in HELLO and the CLI reads /status, but the MCP server an agent spawns had no
|
|
5
|
+
* announcement at all — and it is the piece the user actually hits: `npx @reticlehq/server mcp`
|
|
6
|
+
* resolves from a cache, so an agent can be running a months-old MCP package against a current
|
|
7
|
+
* daemon with nothing anywhere saying so.
|
|
8
|
+
*
|
|
9
|
+
* It rides the SSE connect the proxy already makes, as two query params. Query params rather than a
|
|
10
|
+
* new message because the connection happens before any protocol exchange, and because an older
|
|
11
|
+
* proxy that sends neither is handled correctly by describeSkew (absent contract + differing version
|
|
12
|
+
* = a build that predates the field).
|
|
13
|
+
*/
|
|
14
|
+
import { CONTRACT_FINGERPRINT } from '@reticlehq/core';
|
|
15
|
+
import { SERVER_VERSION } from './server-version.js';
|
|
16
|
+
import { describeSkew, DAEMON_FIX, SkewPair } from './version-skew.js';
|
|
17
|
+
import { noteVersionSkew } from './version-nudge.js';
|
|
18
|
+
export const PEER_VERSION_PARAM = 'peerVersion';
|
|
19
|
+
export const PEER_CONTRACT_PARAM = 'peerContract';
|
|
20
|
+
/** Compare an attaching agent process against this daemon, and queue the nudge when they disagree. */
|
|
21
|
+
export function noteAgentPeer(version, contract) {
|
|
22
|
+
const skew = describeSkew({
|
|
23
|
+
what: "the agent's MCP server",
|
|
24
|
+
version: version ?? undefined,
|
|
25
|
+
contract: contract ?? undefined,
|
|
26
|
+
fix: DAEMON_FIX,
|
|
27
|
+
}, { version: SERVER_VERSION, contract: CONTRACT_FINGERPRINT });
|
|
28
|
+
if (skew !== undefined)
|
|
29
|
+
noteVersionSkew(SkewPair.DAEMON, skew);
|
|
30
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
|
|
2
|
+
// NOTE: a RUNTIME path, not an import — `tsc` does not check it and a file move will not rewrite
|
|
3
|
+
// it. Two levels up because this file sits in `version/`: from `src/version/` and from
|
|
4
|
+
// `dist/version/` alike, `../../package.json` is the package manifest. Moving this file again means
|
|
5
|
+
// changing this line by hand; nothing else will tell you.
|
|
6
|
+
const _pkg = createRequire(import.meta.url)('../../package.json');
|
|
3
7
|
/** The Reticle server version, read from package.json at startup. */
|
|
4
8
|
export const SERVER_VERSION = _pkg.version;
|
|
5
9
|
/**
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Getting skew in front of the AGENT, on whatever tool it happens to be calling.
|
|
3
|
+
*
|
|
4
|
+
* Every check built before this reported into a place nobody looks: SDK skew landed in
|
|
5
|
+
* `reticle_sessions.versionSkew`, which an agent driving a flow never calls, and daemon skew went to
|
|
6
|
+
* a CLI log line no agent reads. So an agent could work an entire session against a mismatched pair,
|
|
7
|
+
* watch tools behave oddly, and never be told the one fact that explains it.
|
|
8
|
+
*
|
|
9
|
+
* This is the delivery channel — the same one the update nudge uses, for the same reason: an
|
|
10
|
+
* out-of-date install is worth mentioning whatever the agent is doing. It rides out on the next tool
|
|
11
|
+
* result, then goes quiet until the skew CHANGES. Told, not nagged: a banner on every call is one an
|
|
12
|
+
* agent learns to skip, and it would cost those tokens on every call for the rest of the session.
|
|
13
|
+
*/
|
|
14
|
+
import type { SkewPair } from './version-skew.js';
|
|
15
|
+
interface VersionSkewNudge {
|
|
16
|
+
pair: SkewPair;
|
|
17
|
+
action: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Record a skew for delivery. Re-arms only when the message DIFFERS from the one already delivered:
|
|
21
|
+
* a second tab on a third version is news; the same tab reconnecting is not.
|
|
22
|
+
*/
|
|
23
|
+
export declare function noteVersionSkew(pair: SkewPair, action: string): void;
|
|
24
|
+
/** The next undelivered skew, once. */
|
|
25
|
+
export declare function takeVersionSkew(): VersionSkewNudge | undefined;
|
|
26
|
+
/** Tests only — drop the module state so each case starts clean. */
|
|
27
|
+
export declare function resetVersionSkew(): void;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Getting skew in front of the AGENT, on whatever tool it happens to be calling.
|
|
3
|
+
*
|
|
4
|
+
* Every check built before this reported into a place nobody looks: SDK skew landed in
|
|
5
|
+
* `reticle_sessions.versionSkew`, which an agent driving a flow never calls, and daemon skew went to
|
|
6
|
+
* a CLI log line no agent reads. So an agent could work an entire session against a mismatched pair,
|
|
7
|
+
* watch tools behave oddly, and never be told the one fact that explains it.
|
|
8
|
+
*
|
|
9
|
+
* This is the delivery channel — the same one the update nudge uses, for the same reason: an
|
|
10
|
+
* out-of-date install is worth mentioning whatever the agent is doing. It rides out on the next tool
|
|
11
|
+
* result, then goes quiet until the skew CHANGES. Told, not nagged: a banner on every call is one an
|
|
12
|
+
* agent learns to skip, and it would cost those tokens on every call for the rest of the session.
|
|
13
|
+
*/
|
|
14
|
+
/** Pending per pair — the SDK and the daemon are different upgrades and each is worth saying once. */
|
|
15
|
+
const pending = new Map();
|
|
16
|
+
/** The last message delivered for each pair, so the same skew is never repeated. */
|
|
17
|
+
const delivered = new Map();
|
|
18
|
+
/**
|
|
19
|
+
* Record a skew for delivery. Re-arms only when the message DIFFERS from the one already delivered:
|
|
20
|
+
* a second tab on a third version is news; the same tab reconnecting is not.
|
|
21
|
+
*/
|
|
22
|
+
export function noteVersionSkew(pair, action) {
|
|
23
|
+
if (delivered.get(pair) === action)
|
|
24
|
+
return;
|
|
25
|
+
pending.set(pair, action);
|
|
26
|
+
}
|
|
27
|
+
/** The next undelivered skew, once. */
|
|
28
|
+
export function takeVersionSkew() {
|
|
29
|
+
for (const [pair, action] of pending) {
|
|
30
|
+
pending.delete(pair);
|
|
31
|
+
delivered.set(pair, action);
|
|
32
|
+
return { pair, action };
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/** Tests only — drop the module state so each case starts clean. */
|
|
37
|
+
export function resetVersionSkew() {
|
|
38
|
+
pending.clear();
|
|
39
|
+
delivered.clear();
|
|
40
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does this peer speak our contract? The daemon is the only thing that asks.
|
|
3
|
+
*
|
|
4
|
+
* Reticle is three separately-installed pieces — the SDK in the page, this daemon, and the MCP
|
|
5
|
+
* server the agent spawns — upgraded independently and therefore drifting constantly. Each pair
|
|
6
|
+
* fails the same silent way: `protocolVersion` still matches, the connection succeeds, and only
|
|
7
|
+
* BEHAVIOUR disagrees. A user hit exactly that with a 2.2.1 SDK against a 2.3.0 daemon and saw a
|
|
8
|
+
* bare `-32000` with nothing on either side naming a version.
|
|
9
|
+
*
|
|
10
|
+
* Two decisions shape this file.
|
|
11
|
+
*
|
|
12
|
+
* The daemon is the SINGLE judge. Every piece announces itself at a connect it already makes — the
|
|
13
|
+
* SDK in HELLO, the CLI and the MCP server over /status — and comparing happens here, once. Three
|
|
14
|
+
* independent pairwise checks would be three copies to keep in step and three separate complaints
|
|
15
|
+
* about one upgrade; a table in one place can say what is actually true: which pieces disagree, and
|
|
16
|
+
* which one is behind.
|
|
17
|
+
*
|
|
18
|
+
* The signal is the CONTRACT FINGERPRINT, not the version. Version equality is wrong in both
|
|
19
|
+
* directions: it fires on 2.4.0-vs-2.4.1 where nothing changed — so every patch release makes every
|
|
20
|
+
* un-upgraded app cry wolf, and the warning becomes noise — and it cannot fire at all for two
|
|
21
|
+
* different BUILDS of one version number, which is exactly what a stale daemon and a cached npx
|
|
22
|
+
* install are. The fingerprint is derived from core's wire vocabulary, so it moves when a name on
|
|
23
|
+
* the wire changes and stays put when internals do. Versions still ride along, because they are what
|
|
24
|
+
* a human acts on.
|
|
25
|
+
*/
|
|
26
|
+
/** Which pair disagreed. Named so the nudge can report each independently. */
|
|
27
|
+
export declare const SkewPair: {
|
|
28
|
+
/** The SDK in the page vs this daemon. */
|
|
29
|
+
readonly SDK: "sdk";
|
|
30
|
+
/** Another Reticle process — a CLI, or the MCP server an agent spawned — vs this daemon. */
|
|
31
|
+
readonly DAEMON: "daemon";
|
|
32
|
+
};
|
|
33
|
+
export type SkewPair = (typeof SkewPair)[keyof typeof SkewPair];
|
|
34
|
+
/** What a piece says about itself when it connects. */
|
|
35
|
+
export interface PeerIdentity {
|
|
36
|
+
/** Human-readable subject for the message: "the page", "the daemon on this port". */
|
|
37
|
+
what: string;
|
|
38
|
+
/** Package version, when the peer supplied one. Undefined on a hand-wired connect. */
|
|
39
|
+
version: string | undefined;
|
|
40
|
+
/** Contract fingerprint. Undefined means the peer predates the field — itself evidence of age. */
|
|
41
|
+
contract: string | undefined;
|
|
42
|
+
/** What the human runs to bring this piece into line. */
|
|
43
|
+
fix: string;
|
|
44
|
+
}
|
|
45
|
+
/** Everything a build knows about itself. */
|
|
46
|
+
interface SelfIdentity {
|
|
47
|
+
version: string;
|
|
48
|
+
contract: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The one sentence to tell the agent, or undefined when the pair agrees.
|
|
52
|
+
*
|
|
53
|
+
* Silent when the fingerprints MATCH, whatever the versions say — that is the point: a patch bump
|
|
54
|
+
* that changed no wire name is not skew, and warning about it is how a real warning gets ignored.
|
|
55
|
+
*
|
|
56
|
+
* A peer with no fingerprint is treated as skewed ONLY when its version also differs from ours: the
|
|
57
|
+
* field is absent exactly on builds that predate it, so "no fingerprint AND a different version" is
|
|
58
|
+
* a build provably older than this daemon. With neither signal there is nothing to go on, and
|
|
59
|
+
* inventing a warning would be as dishonest as staying silent about a real one.
|
|
60
|
+
*/
|
|
61
|
+
export declare function describeSkew(peer: PeerIdentity, self: SelfIdentity): string | undefined;
|
|
62
|
+
/** The fix line for a page whose SDK disagrees with this daemon. */
|
|
63
|
+
export declare function sdkFix(daemonVersion: string): string;
|
|
64
|
+
/** The fix line for another Reticle process that disagrees with the daemon it attached to. */
|
|
65
|
+
export declare const DAEMON_FIX: string;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does this peer speak our contract? The daemon is the only thing that asks.
|
|
3
|
+
*
|
|
4
|
+
* Reticle is three separately-installed pieces — the SDK in the page, this daemon, and the MCP
|
|
5
|
+
* server the agent spawns — upgraded independently and therefore drifting constantly. Each pair
|
|
6
|
+
* fails the same silent way: `protocolVersion` still matches, the connection succeeds, and only
|
|
7
|
+
* BEHAVIOUR disagrees. A user hit exactly that with a 2.2.1 SDK against a 2.3.0 daemon and saw a
|
|
8
|
+
* bare `-32000` with nothing on either side naming a version.
|
|
9
|
+
*
|
|
10
|
+
* Two decisions shape this file.
|
|
11
|
+
*
|
|
12
|
+
* The daemon is the SINGLE judge. Every piece announces itself at a connect it already makes — the
|
|
13
|
+
* SDK in HELLO, the CLI and the MCP server over /status — and comparing happens here, once. Three
|
|
14
|
+
* independent pairwise checks would be three copies to keep in step and three separate complaints
|
|
15
|
+
* about one upgrade; a table in one place can say what is actually true: which pieces disagree, and
|
|
16
|
+
* which one is behind.
|
|
17
|
+
*
|
|
18
|
+
* The signal is the CONTRACT FINGERPRINT, not the version. Version equality is wrong in both
|
|
19
|
+
* directions: it fires on 2.4.0-vs-2.4.1 where nothing changed — so every patch release makes every
|
|
20
|
+
* un-upgraded app cry wolf, and the warning becomes noise — and it cannot fire at all for two
|
|
21
|
+
* different BUILDS of one version number, which is exactly what a stale daemon and a cached npx
|
|
22
|
+
* install are. The fingerprint is derived from core's wire vocabulary, so it moves when a name on
|
|
23
|
+
* the wire changes and stays put when internals do. Versions still ride along, because they are what
|
|
24
|
+
* a human acts on.
|
|
25
|
+
*/
|
|
26
|
+
/** Which pair disagreed. Named so the nudge can report each independently. */
|
|
27
|
+
export const SkewPair = {
|
|
28
|
+
/** The SDK in the page vs this daemon. */
|
|
29
|
+
SDK: 'sdk',
|
|
30
|
+
/** Another Reticle process — a CLI, or the MCP server an agent spawned — vs this daemon. */
|
|
31
|
+
DAEMON: 'daemon',
|
|
32
|
+
};
|
|
33
|
+
function versionPhrase(peer, self) {
|
|
34
|
+
return peer.version === undefined
|
|
35
|
+
? `${peer.what} is on an unknown version; this daemon is ${self.version}`
|
|
36
|
+
: `${peer.what} is ${peer.version}; this daemon is ${self.version}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The one sentence to tell the agent, or undefined when the pair agrees.
|
|
40
|
+
*
|
|
41
|
+
* Silent when the fingerprints MATCH, whatever the versions say — that is the point: a patch bump
|
|
42
|
+
* that changed no wire name is not skew, and warning about it is how a real warning gets ignored.
|
|
43
|
+
*
|
|
44
|
+
* A peer with no fingerprint is treated as skewed ONLY when its version also differs from ours: the
|
|
45
|
+
* field is absent exactly on builds that predate it, so "no fingerprint AND a different version" is
|
|
46
|
+
* a build provably older than this daemon. With neither signal there is nothing to go on, and
|
|
47
|
+
* inventing a warning would be as dishonest as staying silent about a real one.
|
|
48
|
+
*/
|
|
49
|
+
export function describeSkew(peer, self) {
|
|
50
|
+
if (peer.contract !== undefined && peer.contract === self.contract)
|
|
51
|
+
return undefined;
|
|
52
|
+
if (peer.contract === undefined) {
|
|
53
|
+
if (peer.version === undefined || peer.version === self.version)
|
|
54
|
+
return undefined;
|
|
55
|
+
return (`version skew: ${versionPhrase(peer, self)}, and it is too old to report which wire contract ` +
|
|
56
|
+
`it speaks. They connect anyway and then disagree about behaviour, which usually surfaces as ` +
|
|
57
|
+
`a bare -32000 with nothing naming a version. ${peer.fix}`);
|
|
58
|
+
}
|
|
59
|
+
return (`version skew: ${versionPhrase(peer, self)}, and they speak DIFFERENT wire contracts ` +
|
|
60
|
+
`(${peer.contract} vs ${self.contract}) — so this is a real mismatch, not a harmless patch ` +
|
|
61
|
+
`difference. Tools will behave in ways neither side reports. ${peer.fix}`);
|
|
62
|
+
}
|
|
63
|
+
/** The fix line for a page whose SDK disagrees with this daemon. */
|
|
64
|
+
export function sdkFix(daemonVersion) {
|
|
65
|
+
return (`Tell the human to install the matching SDK (\`npm i -D @reticlehq/react@${daemonVersion}\`) ` +
|
|
66
|
+
'or run `reticle update`, then restart their dev server so the page reloads with it.');
|
|
67
|
+
}
|
|
68
|
+
/** The fix line for another Reticle process that disagrees with the daemon it attached to. */
|
|
69
|
+
export const DAEMON_FIX = 'A daemon outlives the agents attached to it, so it keeps serving its OWN code until it restarts ' +
|
|
70
|
+
'— anything fixed in the newer package is simply absent. Run `reticle stop` and retry to replace ' +
|
|
71
|
+
'it (other agents on that daemon will need to reconnect).';
|
|
@@ -32,7 +32,7 @@ function changedRegion(out, width, height) {
|
|
|
32
32
|
for (let x = 0; x < width; x += 1) {
|
|
33
33
|
const i = (y * width + x) * RGBA;
|
|
34
34
|
// pixelmatch marks a changed pixel solid red (default diffColor) at full alpha.
|
|
35
|
-
if (out[i]
|
|
35
|
+
if (255 === out[i] && 0 === out[i + 1] && 0 === out[i + 2] && 255 === out[i + 3]) {
|
|
36
36
|
if (x < minX)
|
|
37
37
|
minX = x;
|
|
38
38
|
if (y < minY)
|
|
@@ -80,7 +80,7 @@ export async function diffPng(baselineBytes, currentBytes, opts = {}) {
|
|
|
80
80
|
const threshold = opts.threshold ?? VISUAL_PIXEL_THRESHOLD;
|
|
81
81
|
const changedPixels = pixelmatch(a.data, b.data, out.data, width, height, { threshold });
|
|
82
82
|
const totalPixels = width * height;
|
|
83
|
-
const ratio =
|
|
83
|
+
const ratio = 0 === totalPixels ? 0 : changedPixels / totalPixels;
|
|
84
84
|
const maxRatio = opts.maxRatio ?? 0;
|
|
85
85
|
const region = changedPixels > 0 ? changedRegion(out.data, width, height) : undefined;
|
|
86
86
|
const result = {
|
|
@@ -16,7 +16,7 @@ const rectShape = z.object({
|
|
|
16
16
|
});
|
|
17
17
|
function screenshotProvider(deps) {
|
|
18
18
|
const p = deps.realInput;
|
|
19
|
-
return p !== undefined && typeof p.screenshot
|
|
19
|
+
return p !== undefined && 'function' === typeof p.screenshot
|
|
20
20
|
? p
|
|
21
21
|
: undefined;
|
|
22
22
|
}
|
|
@@ -55,7 +55,7 @@ async function buildOpts(deps, sessionId, args) {
|
|
|
55
55
|
if (box !== undefined)
|
|
56
56
|
return { clip: box };
|
|
57
57
|
}
|
|
58
|
-
return args['fullPage']
|
|
58
|
+
return true === args['fullPage'] ? { fullPage: true } : {};
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* Ask the desktop shell to photograph its own window.
|
|
@@ -110,7 +110,7 @@ function isCapturePath(path) {
|
|
|
110
110
|
function isCompletePng(bytes) {
|
|
111
111
|
if (bytes.byteLength < 12)
|
|
112
112
|
return false;
|
|
113
|
-
return bytes.subarray(bytes.byteLength - 8, bytes.byteLength - 4).toString('ascii')
|
|
113
|
+
return 'IEND' === bytes.subarray(bytes.byteLength - 8, bytes.byteLength - 4).toString('ascii');
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
116
|
* Get pixels for a session, by whichever route exists.
|
|
@@ -137,7 +137,7 @@ async function capture(deps, sessionId, args) {
|
|
|
137
137
|
if (png !== undefined)
|
|
138
138
|
return { png };
|
|
139
139
|
}
|
|
140
|
-
const desktop = await desktopCapture(deps, sessionId, args['fullPage']
|
|
140
|
+
const desktop = await desktopCapture(deps, sessionId, true === args['fullPage']);
|
|
141
141
|
if (desktop.png !== undefined)
|
|
142
142
|
return { png: desktop.png };
|
|
143
143
|
if (desktop.reason !== undefined)
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Docs index
|
|
2
|
+
|
|
3
|
+
Two audiences share this directory, which is why it looked like twenty-five unsorted files. Every page states its own audience in the first blockquote; this index is the shortcut.
|
|
4
|
+
|
|
5
|
+
**User docs are published** to [reticle.sh](https://reticle.sh) via `mint.json`. **Contributor docs are not** — they are for people working _on_ Reticle, and they live here because they belong next to what they describe.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## For people using Reticle
|
|
10
|
+
|
|
11
|
+
**Start:** [getting-started.md](getting-started.md) → [usage.md](usage.md) (the full tool reference).
|
|
12
|
+
|
|
13
|
+
| Page | What it answers |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| [getting-started.md](getting-started.md) | install it, connect an agent, verify something |
|
|
16
|
+
| [usage.md](usage.md) | every tool, every argument — the reference |
|
|
17
|
+
| [agent-cheatsheet.md](agent-cheatsheet.md) | the condensed version an agent keeps in context |
|
|
18
|
+
| [architecture.md](architecture.md) | how it works, and why it is built this way |
|
|
19
|
+
| [platform-integration.md](platform-integration.md) | Vite, Next, Remix, Astro, plain HTML |
|
|
20
|
+
| [desktop-apps.md](desktop-apps.md) | Electron and Tauri |
|
|
21
|
+
| [integration-patterns.md](integration-patterns.md) | wiring it into a real codebase |
|
|
22
|
+
| [flows.md](flows.md) | record → save → replay → heal |
|
|
23
|
+
| [testing.md](testing.md) | `@reticlehq/test` — turning a session into a CI suite |
|
|
24
|
+
| [multi-agent-testing.md](multi-agent-testing.md) | more than one agent on one app |
|
|
25
|
+
| [human-control.md](human-control.md) | taking the wheel back from the agent |
|
|
26
|
+
| [deploy-checks.md](deploy-checks.md) | running Reticle against a deployed build |
|
|
27
|
+
| [token-efficiency.md](token-efficiency.md) | why it costs less than a screenshot loop |
|
|
28
|
+
| [benchmarks.md](benchmarks.md) | how the numbers were measured, including where Reticle loses |
|
|
29
|
+
| [telemetry.md](telemetry.md) | what is collected, and how to turn it off |
|
|
30
|
+
| [local-registry.md](local-registry.md) | installing an unpublished build |
|
|
31
|
+
| [enterprise.md](enterprise.md) | the licensed surface |
|
|
32
|
+
|
|
33
|
+
## For people working on Reticle
|
|
34
|
+
|
|
35
|
+
**Start:** [`CONTRIBUTING.md`](../CONTRIBUTING.md) → [gates.md](gates.md).
|
|
36
|
+
|
|
37
|
+
| Page | What it answers |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| [gates.md](gates.md) | **I changed some files — which gate do I run?** |
|
|
40
|
+
| [gate-plan.md](gate-plan.md) | why the gates are shaped this way, and what is still unbuilt |
|
|
41
|
+
| [system-map.md](system-map.md) | how a tool call reaches the app, and which failures are silent |
|
|
42
|
+
| [telemetry-contract.md](telemetry-contract.md) | required reading before touching anything that emits |
|
|
43
|
+
| [debugging.md](debugging.md) | debugging Reticle itself — the four signals and what each answers |
|
|
44
|
+
| [fixtures.md](fixtures.md) | the sibling `reticle-fixtures` repo: install complexity, not regressions |
|
|
45
|
+
| [matrix/README.md](matrix/README.md) | submitting an MCP-client compatibility record — the best first contribution |
|
|
46
|
+
| [matrix/MATRIX.md](matrix/MATRIX.md) | which clients are known to work (generated; do not hand-edit) |
|
|
47
|
+
| [`../apps/README.md`](../apps/README.md) | what belongs in `apps/`, and what does not |
|
|
48
|
+
| [`../apps/e2e/harness-rules.md`](../apps/e2e/harness-rules.md) | the four rules every gate obeys, and the incident behind each |
|
|
49
|
+
| [`../bench/README.md`](../bench/README.md) | what in `bench/` is live and what is a kept one-off study |
|
|
50
|
+
|
|
51
|
+
`fixtures-dispatch-receiver.yml` is not a doc — it is the workflow template `reticle-fixtures` needs, kept in this repo so the two ends of the contract cannot drift apart.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
**Adding a page?** Put it in the right table above. If it is user-facing, add it to `mint.json` too — a page that is published but unlisted is a page nobody finds, and a page listed here but absent from `mint.json` is one users cannot reach at all.
|