@reticlehq/server 2.11.0 → 2.13.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 +93 -111
- package/dist/bridge/auth-failure-reason.d.ts +6 -2
- package/dist/bridge/auth-failure-reason.js +15 -8
- package/dist/bridge/bridge.d.ts +18 -14
- package/dist/bridge/bridge.js +248 -165
- package/dist/bridge/bridge.test-harness.d.ts +2 -0
- package/dist/bridge/bridge.test-harness.js +8 -1
- package/dist/bridge/pairing-token.d.ts +5 -0
- package/dist/bridge/pairing-token.js +28 -0
- package/dist/bridge/protocol-skew.d.ts +37 -0
- package/dist/bridge/protocol-skew.js +45 -0
- package/dist/capsule/capsule-store.d.ts +8 -7
- package/dist/capsule/capsule-store.js +1 -1
- package/dist/capsule/causal-summary.d.ts +2 -2
- package/dist/capsule/causal-summary.js +7 -2
- package/dist/chromium-launch-options.d.ts +28 -0
- package/dist/chromium-launch-options.js +29 -0
- package/dist/cli/cli-doctor.js +39 -1
- package/dist/cli/cli-flow-commands.d.ts +3 -1
- package/dist/cli/cli-flow-commands.js +34 -4
- package/dist/cli/cli-kill.d.ts +2 -1
- package/dist/cli/cli-launch.d.ts +11 -1
- package/dist/cli/cli-launch.js +59 -10
- package/dist/cli/cli-parse.d.ts +17 -1
- package/dist/cli/cli-parse.js +238 -12
- package/dist/cli/cli-update-commands.js +20 -1
- package/dist/cli/cli-verify.d.ts +2 -1
- package/dist/cli/cli-verify.js +5 -4
- package/dist/cli/cloud-cli.js +428 -253
- package/dist/cli/cloud-keystore.d.ts +37 -0
- package/dist/cli/cloud-keystore.js +97 -0
- package/dist/cli/cloud-kit.d.ts +37 -0
- package/dist/cli/cloud-kit.js +121 -0
- package/dist/cli/cloud-login.d.ts +17 -0
- package/dist/cli/cloud-login.js +183 -0
- package/dist/cli/cloud-session.d.ts +48 -0
- package/dist/cli/cloud-session.js +53 -0
- package/dist/cli/config-discovery.d.ts +2 -1
- package/dist/cli/config-discovery.js +1 -0
- package/dist/cli/daemon-start-options.d.ts +32 -0
- package/dist/cli/daemon-start-options.js +38 -0
- package/dist/cli/device-flow.d.ts +76 -0
- package/dist/cli/device-flow.js +56 -0
- package/dist/cli/doctor-rows.d.ts +12 -1
- package/dist/cli/doctor-rows.js +12 -1
- package/dist/cli/drive-attach.d.ts +13 -0
- package/dist/cli/drive-attach.js +18 -0
- package/dist/cli/drive-command.js +7 -3
- package/dist/cli/gate-exit.d.ts +21 -0
- package/dist/cli/gate-exit.js +21 -0
- package/dist/cli/gate-hook-message.d.ts +33 -0
- package/dist/cli/gate-hook-message.js +46 -0
- package/dist/cli/mcp-command.d.ts +25 -0
- package/dist/cli/mcp-command.js +174 -0
- package/dist/cli/project-name.d.ts +30 -0
- package/dist/cli/project-name.js +44 -0
- package/dist/cli/sibling-ports.d.ts +37 -0
- package/dist/cli/sibling-ports.js +56 -0
- package/dist/cli/status-next-action.d.ts +14 -0
- package/dist/cli/status-next-action.js +4 -4
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +167 -186
- package/dist/cloud/cloud-config.d.ts +24 -2
- package/dist/cloud/cloud-config.js +64 -5
- package/dist/cloud/cloud-sync.d.ts +16 -5
- package/dist/cloud/cloud-sync.js +64 -1
- package/dist/cloud/sync-cycle.d.ts +110 -0
- package/dist/cloud/sync-cycle.js +270 -0
- package/dist/cloud/sync-daemon.d.ts +69 -0
- package/dist/cloud/sync-daemon.js +260 -0
- package/dist/cloud/sync-disk.d.ts +9 -0
- package/dist/cloud/sync-disk.js +191 -0
- package/dist/cloud/sync-hash.d.ts +2 -0
- package/dist/cloud/sync-hash.js +35 -0
- package/dist/crawl/crawl-tools.js +5 -3
- package/dist/crawl/crawl.d.ts +12 -13
- package/dist/crawl/crawl.js +25 -3
- package/dist/daemon/daemon-resolve.d.ts +122 -0
- package/dist/daemon/daemon-resolve.js +187 -0
- package/dist/daemon/dev-servers.d.ts +15 -0
- package/dist/daemon/dev-servers.js +68 -0
- package/dist/daemon/free-port.d.ts +29 -0
- package/dist/daemon/free-port.js +55 -0
- package/dist/daemon/heartbeat.d.ts +0 -4
- package/dist/daemon/heartbeat.js +2 -2
- package/dist/daemon/wake-decision.d.ts +32 -0
- package/dist/daemon/wake-decision.js +38 -0
- package/dist/dev/stale-issue-guard.d.ts +0 -2
- package/dist/dev/stale-issue-guard.js +1 -1
- package/dist/domain/domain-tools.js +2 -2
- package/dist/events/contradictions.d.ts +59 -0
- package/dist/events/contradictions.js +253 -27
- package/dist/events/declared.d.ts +17 -0
- package/dist/events/declared.js +43 -1
- package/dist/events/echo-mismatch.js +65 -1
- package/dist/events/event-filters.d.ts +8 -0
- package/dist/events/event-filters.js +19 -2
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +17 -0
- package/dist/events/predicate-asks.d.ts +13 -0
- package/dist/events/predicate-asks.js +22 -0
- package/dist/events/predicate-eval.js +117 -27
- package/dist/events/predicate-parse.d.ts +0 -6
- package/dist/events/predicate-parse.js +43 -3
- package/dist/events/predicate-precheck.d.ts +8 -0
- package/dist/events/predicate-precheck.js +41 -0
- package/dist/events/predicate-route.d.ts +14 -0
- package/dist/events/predicate-route.js +36 -8
- package/dist/events/predicate-schema.d.ts +44 -0
- package/dist/events/predicate-schema.js +126 -23
- package/dist/events/predicate.d.ts +20 -0
- package/dist/events/predicate.js +77 -6
- package/dist/events/split-text-miss.d.ts +30 -0
- package/dist/events/split-text-miss.js +44 -0
- package/dist/flows/coverage.d.ts +0 -8
- package/dist/flows/coverage.js +1 -1
- package/dist/flows/decision.d.ts +5 -1
- package/dist/flows/decision.js +33 -7
- package/dist/flows/flake.d.ts +6 -8
- package/dist/flows/flake.js +1 -1
- package/dist/flows/flow-intent.d.ts +52 -0
- package/dist/flows/flow-intent.js +123 -0
- package/dist/flows/flow-memory-consult.d.ts +55 -0
- package/dist/flows/flow-memory-consult.js +38 -0
- package/dist/flows/flow-replay-run.d.ts +18 -7
- package/dist/flows/flow-replay-run.js +263 -23
- package/dist/flows/flow-replay.js +11 -2
- package/dist/flows/flow-step-runners.d.ts +8 -0
- package/dist/flows/flow-step-runners.js +8 -2
- package/dist/flows/flow-store-for-session.d.ts +31 -0
- package/dist/flows/flow-store-for-session.js +33 -0
- package/dist/flows/flow-success.js +15 -3
- package/dist/flows/flow-tools.js +87 -33
- package/dist/flows/flows.d.ts +14 -1
- package/dist/flows/flows.js +502 -24
- package/dist/flows/heal-run.js +5 -2
- package/dist/flows/predicate-to-expect.d.ts +17 -12
- package/dist/flows/predicate-to-expect.js +36 -15
- package/dist/flows/recordings.d.ts +13 -1
- package/dist/flows/recordings.js +17 -2
- package/dist/flows/replay.d.ts +22 -2
- package/dist/flows/replay.js +28 -2
- package/dist/flows/server-verify.js +16 -2
- package/dist/flows/verify-change-tools.js +9 -3
- package/dist/honesty/blind-spots.d.ts +51 -15
- package/dist/honesty/blind-spots.js +64 -4
- package/dist/honesty/feature-capture.d.ts +150 -0
- package/dist/honesty/feature-capture.js +233 -0
- package/dist/honesty/honesty.d.ts +24 -0
- package/dist/honesty/honesty.js +25 -1
- package/dist/honesty/instrumentation-gaps.d.ts +39 -6
- package/dist/honesty/instrumentation-gaps.js +88 -5
- package/dist/honesty/tool-hit-rate.d.ts +86 -0
- package/dist/honesty/tool-hit-rate.js +90 -0
- package/dist/honesty/undeclared-change.d.ts +51 -0
- package/dist/honesty/undeclared-change.js +56 -0
- package/dist/honesty/verified.d.ts +16 -0
- package/dist/honesty/verified.js +84 -5
- package/dist/http-server.d.ts +0 -1
- package/dist/http-server.js +13 -1
- package/dist/hunt/hunt-report.d.ts +0 -2
- package/dist/hunt/hunt-report.js +1 -1
- package/dist/impact/impact-recorder.d.ts +31 -8
- package/dist/impact/impact-recorder.js +104 -21
- package/dist/impact/impact-store.d.ts +13 -4
- package/dist/impact/impact-store.js +44 -3
- package/dist/index.d.ts +4 -1
- package/dist/index.js +113 -7
- package/dist/init/agent-rules.d.ts +30 -6
- package/dist/init/agent-rules.js +54 -13
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +28 -0
- package/dist/init/capabilities.d.ts +4 -3
- package/dist/init/capabilities.js +23 -3
- package/dist/init/closing-hint.d.ts +36 -0
- package/dist/init/closing-hint.js +120 -0
- package/dist/init/confirm.d.ts +27 -28
- package/dist/init/confirm.js +51 -11
- package/dist/init/cra.d.ts +34 -5
- package/dist/init/cra.js +96 -25
- package/dist/init/csp-check.js +17 -2
- package/dist/init/csp-doctor.d.ts +8 -1
- package/dist/init/csp-doctor.js +8 -3
- package/dist/init/dev-script.d.ts +50 -0
- package/dist/init/dev-script.js +66 -0
- package/dist/init/format-generated.d.ts +18 -0
- package/dist/init/format-generated.js +39 -0
- package/dist/init/install-hint.d.ts +9 -0
- package/dist/init/install-hint.js +29 -0
- package/dist/init/mcp-clients.d.ts +7 -0
- package/dist/init/mcp-clients.js +7 -0
- package/dist/init/mcp.d.ts +17 -6
- package/dist/init/mcp.js +27 -11
- package/dist/init/node-io.js +11 -1
- package/dist/init/plan-framework.d.ts +0 -5
- package/dist/init/plan-framework.js +147 -37
- package/dist/init/plan.d.ts +30 -37
- package/dist/init/plan.js +50 -60
- package/dist/init/preflight.d.ts +28 -0
- package/dist/init/preflight.js +38 -0
- package/dist/init/refresh-rules.d.ts +39 -0
- package/dist/init/refresh-rules.js +51 -0
- package/dist/init/run.d.ts +32 -35
- package/dist/init/run.js +151 -202
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/snippets.js +90 -31
- package/dist/init/vite-config.d.ts +1 -1
- package/dist/init/vite-config.js +39 -25
- package/dist/init/workspace-apps.d.ts +17 -10
- package/dist/init/workspace-apps.js +110 -0
- package/dist/input/network-mock-tools.js +3 -6
- package/dist/input/real-input.d.ts +2 -0
- package/dist/input/real-input.js +28 -7
- package/dist/input/scroll-find.d.ts +7 -4
- package/dist/input/scroll-find.js +63 -25
- package/dist/input/scroll-tools.js +4 -6
- package/dist/input/viewport-tools.js +5 -4
- package/dist/intent/inline-intent.d.ts +37 -0
- package/dist/intent/inline-intent.js +193 -0
- package/dist/intent/intent-shard-store.d.ts +72 -0
- package/dist/intent/intent-shard-store.js +203 -0
- package/dist/intent/intent-shard.d.ts +411 -0
- package/dist/intent/intent-shard.js +176 -0
- package/dist/intent/intent-store.d.ts +49 -0
- package/dist/intent/intent-store.js +133 -0
- package/dist/intent/intent-subject.d.ts +45 -0
- package/dist/intent/intent-subject.js +177 -0
- package/dist/intent/intent-tools.d.ts +2 -0
- package/dist/intent/intent-tools.js +143 -0
- package/dist/intent/open-intents.d.ts +42 -0
- package/dist/intent/open-intents.js +40 -0
- package/dist/journal/envelope.d.ts +26 -26
- package/dist/journal/journal-recorder.d.ts +23 -0
- package/dist/journal/journal-recorder.js +35 -0
- package/dist/license/license-env.d.ts +29 -0
- package/dist/license/license-env.js +148 -0
- package/dist/loopback-agent.d.ts +31 -0
- package/dist/loopback-agent.js +57 -0
- package/dist/mcp/attach-memory.d.ts +83 -0
- package/dist/mcp/attach-memory.js +185 -0
- package/dist/mcp/client-identity.d.ts +31 -0
- package/dist/mcp/client-identity.js +51 -0
- package/dist/mcp/mcp-post-transport.d.ts +26 -0
- package/dist/mcp/mcp-post-transport.js +109 -0
- package/dist/mcp/mcp-proxy.d.ts +2 -8
- package/dist/mcp/mcp-proxy.js +64 -62
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +52 -24
- package/dist/mcp/proxy-backoff.d.ts +11 -0
- package/dist/mcp/proxy-backoff.js +13 -0
- package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
- package/dist/mcp/proxy-daemon-probe.js +7 -1
- package/dist/mcp/proxy-handshake.d.ts +23 -0
- package/dist/mcp/proxy-handshake.js +31 -0
- package/dist/mcp/server-instructions.js +45 -3
- package/dist/mcp/tool-catalog-cache.d.ts +16 -0
- package/dist/mcp/tool-catalog-cache.js +22 -0
- package/dist/memory/memory-tools.d.ts +2 -0
- package/dist/memory/memory-tools.js +82 -0
- package/dist/memory/project-memory.d.ts +34 -0
- package/dist/memory/project-memory.js +96 -0
- package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
- package/dist/oracles/flow-instrument-gaps.js +4 -5
- package/dist/oracles/propose-consequences.js +12 -3
- package/dist/oracles/self-instrument.d.ts +9 -9
- package/dist/oracles/self-instrument.js +19 -13
- package/dist/pool/browser-pool.d.ts +75 -5
- package/dist/pool/browser-pool.js +144 -5
- package/dist/pool/playwright-launcher.js +12 -1
- package/dist/project/fs-port.d.ts +3 -0
- package/dist/project/fs-port.js +3 -2
- package/dist/project/memory-fs.js +6 -1
- package/dist/project/project-tools.js +6 -35
- package/dist/project/reticle-dir.d.ts +17 -2
- package/dist/project/reticle-dir.js +22 -4
- package/dist/runs/context-tools.d.ts +2 -0
- package/dist/runs/context-tools.js +89 -0
- package/dist/runs/run-context.d.ts +40 -0
- package/dist/runs/run-context.js +158 -0
- package/dist/runs/run-diff.d.ts +2 -1
- package/dist/runs/run-store.d.ts +10 -0
- package/dist/runs/run-store.js +10 -0
- package/dist/runs/verification-sync.d.ts +0 -11
- package/dist/runs/verification-sync.js +24 -3
- package/dist/runs/verify-server.d.ts +8 -1
- package/dist/runs/verify-server.js +14 -2
- package/dist/session/command-timeout.d.ts +5 -2
- package/dist/session/command-timeout.js +5 -4
- package/dist/session/dev-server-probe.d.ts +0 -8
- package/dist/session/dev-server-probe.js +1 -1
- package/dist/session/last-act.d.ts +7 -1
- package/dist/session/last-act.js +2 -2
- package/dist/session/lease-visibility.d.ts +67 -0
- package/dist/session/lease-visibility.js +76 -0
- package/dist/session/live-control-tools.js +1 -42
- package/dist/session/no-session-diagnosis.d.ts +33 -0
- package/dist/session/no-session-diagnosis.js +82 -24
- package/dist/session/no-session-next-action.d.ts +12 -0
- package/dist/session/no-session-next-action.js +77 -6
- package/dist/session/no-session-watch.d.ts +18 -6
- package/dist/session/no-session-watch.js +125 -75
- package/dist/session/output-budget.js +4 -1
- package/dist/session/pending-commands.d.ts +11 -1
- package/dist/session/pending-commands.js +12 -2
- package/dist/session/resolve-within.d.ts +34 -0
- package/dist/session/resolve-within.js +53 -0
- package/dist/session/session-health.d.ts +9 -0
- package/dist/session/session-health.js +23 -0
- package/dist/session/session-info.d.ts +18 -0
- package/dist/session/session-info.js +11 -0
- package/dist/session/session-manager.d.ts +14 -0
- package/dist/session/session-manager.js +111 -4
- package/dist/session/session-mirror.d.ts +16 -0
- package/dist/session/session-mirror.js +22 -0
- package/dist/session/session-recommendation.d.ts +19 -5
- package/dist/session/session-recommendation.js +25 -9
- package/dist/session/session-successor.d.ts +37 -0
- package/dist/session/session-successor.js +113 -0
- package/dist/session/session-tools.js +2 -2
- package/dist/session/session.d.ts +53 -3
- package/dist/session/session.js +162 -9
- package/dist/session/wall-clock.d.ts +12 -0
- package/dist/session/wall-clock.js +14 -0
- package/dist/setup/agent-configs.d.ts +96 -0
- package/dist/setup/agent-configs.js +333 -0
- package/dist/setup/agent-io.d.ts +9 -0
- package/dist/setup/agent-io.js +16 -0
- package/dist/setup/agent-writer.d.ts +51 -0
- package/dist/setup/agent-writer.js +115 -0
- package/dist/setup/approval-migration.d.ts +47 -0
- package/dist/setup/approval-migration.js +68 -0
- package/dist/setup/auto-approve.d.ts +85 -0
- package/dist/setup/auto-approve.js +150 -0
- package/dist/setup/bridge-port.d.ts +15 -0
- package/dist/setup/bridge-port.js +24 -0
- package/dist/setup/desktop-shape.d.ts +57 -0
- package/dist/setup/desktop-shape.js +63 -0
- package/dist/setup/dev-server-wait.d.ts +60 -0
- package/dist/setup/dev-server-wait.js +65 -0
- package/dist/setup/drive-agent.d.ts +71 -0
- package/dist/setup/drive-agent.js +171 -0
- package/dist/setup/drive-plan.d.ts +68 -0
- package/dist/setup/drive-plan.js +130 -0
- package/dist/setup/ensure-daemon.d.ts +29 -0
- package/dist/setup/ensure-daemon.js +58 -0
- package/dist/setup/init-runtime.d.ts +47 -0
- package/dist/setup/init-runtime.js +160 -0
- package/dist/setup/interrupt.d.ts +25 -0
- package/dist/setup/interrupt.js +40 -0
- package/dist/setup/license-key.d.ts +39 -0
- package/dist/setup/license-key.js +72 -0
- package/dist/setup/listeners.d.ts +37 -0
- package/dist/setup/listeners.js +85 -0
- package/dist/setup/node-effects.d.ts +60 -0
- package/dist/setup/node-effects.js +210 -0
- package/dist/setup/page-probe.d.ts +39 -0
- package/dist/setup/page-probe.js +49 -0
- package/dist/setup/relaunch.d.ts +36 -0
- package/dist/setup/relaunch.js +57 -0
- package/dist/setup/remaining-steps.d.ts +19 -0
- package/dist/setup/remaining-steps.js +31 -0
- package/dist/setup/run-setup.d.ts +99 -0
- package/dist/setup/run-setup.js +187 -0
- package/dist/setup/session-pick.d.ts +47 -0
- package/dist/setup/session-pick.js +51 -0
- package/dist/setup/setup-command.d.ts +45 -0
- package/dist/setup/setup-command.js +199 -0
- package/dist/setup/setup-options.d.ts +74 -0
- package/dist/setup/setup-options.js +67 -0
- package/dist/setup/transcripts.d.ts +16 -0
- package/dist/setup/transcripts.js +43 -0
- package/dist/status-payload.d.ts +14 -1
- package/dist/status-payload.js +23 -1
- package/dist/telemetry/automation-hint.d.ts +2 -1
- package/dist/telemetry/daemon-telemetry.d.ts +8 -1
- package/dist/telemetry/daemon-telemetry.js +52 -5
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +9 -1
- package/dist/telemetry/feedback-context.js +60 -29
- package/dist/telemetry/feedback-local.d.ts +15 -0
- package/dist/telemetry/feedback-local.js +56 -0
- package/dist/telemetry/feedback-outbox.d.ts +2 -1
- package/dist/telemetry/feedback.d.ts +11 -0
- package/dist/telemetry/feedback.js +19 -6
- package/dist/telemetry/init-telemetry.d.ts +2 -0
- package/dist/telemetry/init-telemetry.js +2 -0
- package/dist/telemetry/install-source.d.ts +14 -0
- package/dist/telemetry/install-source.js +30 -0
- package/dist/telemetry/license-activation.d.ts +7 -1
- package/dist/telemetry/license-activation.js +9 -5
- package/dist/telemetry/mcp-connection.js +7 -0
- package/dist/telemetry/project-profile.d.ts +12 -9
- package/dist/telemetry/project-profile.js +12 -7
- package/dist/telemetry/proxy-telemetry.d.ts +3 -0
- package/dist/telemetry/proxy-telemetry.js +21 -0
- package/dist/telemetry/run-telemetry.d.ts +0 -2
- package/dist/telemetry/run-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +12 -0
- package/dist/telemetry/session-metrics.js +35 -1
- package/dist/telemetry/telemetry.js +17 -2
- package/dist/telemetry/tool-refused.d.ts +3 -1
- package/dist/telemetry/tool-refused.js +34 -2
- package/dist/telemetry/verification-of.js +2 -2
- package/dist/tools/act-capsule.d.ts +10 -0
- package/dist/tools/act-capsule.js +8 -2
- package/dist/tools/act-danger.d.ts +2 -1
- package/dist/tools/act-danger.js +11 -3
- package/dist/tools/act-observation.d.ts +41 -0
- package/dist/tools/act-observation.js +36 -0
- package/dist/tools/act-preflight.d.ts +20 -0
- package/dist/tools/act-preflight.js +98 -0
- package/dist/tools/act-sequence-retry.d.ts +45 -0
- package/dist/tools/act-sequence-retry.js +58 -0
- package/dist/tools/act-sequence-tool.d.ts +13 -0
- package/dist/tools/act-sequence-tool.js +135 -0
- package/dist/tools/act-target.d.ts +14 -0
- package/dist/tools/act-target.js +47 -0
- package/dist/tools/act-tools.d.ts +21 -1
- package/dist/tools/act-tools.js +196 -204
- package/dist/tools/assert-source.d.ts +22 -0
- package/dist/tools/assert-source.js +53 -0
- package/dist/tools/assert-verdict.d.ts +20 -3
- package/dist/tools/assert-verdict.js +58 -20
- package/dist/tools/browser-tools.js +6 -22
- package/dist/tools/complete-snapshot.d.ts +48 -0
- package/dist/tools/complete-snapshot.js +75 -0
- package/dist/tools/coverage-tools.d.ts +10 -1
- package/dist/tools/coverage-tools.js +123 -69
- package/dist/tools/dynamic-tools.js +53 -13
- package/dist/tools/error-recovery.d.ts +25 -14
- package/dist/tools/error-recovery.js +78 -9
- package/dist/tools/feedback-tools.d.ts +1 -1
- package/dist/tools/feedback-tools.js +2 -2
- package/dist/tools/invoke-tool.js +121 -11
- package/dist/tools/lease-hint.d.ts +10 -0
- package/dist/tools/lease-hint.js +53 -4
- package/dist/tools/lease-tools.d.ts +35 -5
- package/dist/tools/lease-tools.js +191 -13
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +22 -0
- package/dist/tools/navigate-arrival.d.ts +0 -2
- package/dist/tools/navigate-arrival.js +1 -1
- package/dist/tools/numeric-bounds.d.ts +100 -0
- package/dist/tools/numeric-bounds.js +125 -0
- package/dist/tools/observe-tools.js +109 -65
- package/dist/tools/read-tools.js +31 -8
- package/dist/tools/real-input-attempt.d.ts +32 -0
- package/dist/tools/real-input-attempt.js +212 -2
- package/dist/tools/reconcile-tools.js +22 -8
- package/dist/tools/resolve-target.d.ts +0 -7
- package/dist/tools/sdk-marker-probe.d.ts +29 -0
- package/dist/tools/sdk-marker-probe.js +57 -0
- package/dist/tools/snapshot-delta.d.ts +1 -1
- package/dist/tools/tool-kit.d.ts +24 -0
- package/dist/tools/tool-kit.js +21 -0
- package/dist/tools/tool-names.d.ts +5 -0
- package/dist/tools/tool-names.js +5 -0
- package/dist/tools/tool-surface.d.ts +85 -6
- package/dist/tools/tool-surface.js +122 -2
- package/dist/tools/tools-helpers.d.ts +19 -0
- package/dist/tools/tools-helpers.js +46 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +78 -12
- package/dist/tools/unadvertised-help.js +4 -4
- package/dist/tools/verify-next-baton.d.ts +53 -0
- package/dist/tools/verify-next-baton.js +52 -0
- package/dist/update/reticle-deps.d.ts +9 -0
- package/dist/update/reticle-deps.js +27 -0
- package/dist/update/sdk-sync.d.ts +1 -2
- package/dist/update/sdk-sync.js +1 -21
- package/dist/update/update-nudge.d.ts +25 -10
- package/dist/update/update-nudge.js +32 -3
- package/dist/update/updater.d.ts +1 -1
- package/dist/version/peer-announce.js +4 -2
- package/dist/version/sdk-fix.d.ts +47 -0
- package/dist/version/sdk-fix.js +127 -0
- package/dist/version/version-nudge.d.ts +14 -0
- package/dist/version/version-nudge.js +21 -0
- package/dist/version/version-skew.d.ts +38 -3
- package/dist/version/version-skew.js +83 -7
- package/dist/visual/visual-store.d.ts +6 -6
- package/dist/visual/visual-store.js +18 -16
- package/dist/visual/visual-tools.js +57 -12
- package/docs/README.md +2 -1
- package/docs/cli/cloud.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -0
- package/docs/cli/serve.mdx +4 -2
- package/docs/cli/verify.mdx +4 -5
- package/docs/cli.mdx +7 -3
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +2 -2
- package/docs/gates.md +1 -1
- package/docs/getting-started.md +19 -3
- package/docs/index.mdx +9 -6
- package/docs/install-agentic.mdx +38 -12
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/server.mdx +1 -1
- package/docs/packages.mdx +8 -6
- package/docs/platform-integration.md +17 -0
- package/docs/predicates.mdx +8 -0
- package/docs/reticle-manual.md +106 -0
- package/docs/skill-file.mdx +3 -3
- package/docs/telemetry-contract.md +108 -4
- package/docs/tools-act-sequence.mdx +16 -0
- package/docs/tools-tools-and-run.mdx +2 -4
- package/docs/troubleshooting.mdx +9 -0
- package/docs/usage.md +4 -3
- package/package.json +4 -3
- package/dist/init/cursor.d.ts +0 -24
- package/dist/init/cursor.js +0 -80
- package/dist/session/session-readiness.d.ts +0 -34
- package/dist/session/session-readiness.js +0 -36
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which side of a protocol mismatch is the stale one, and therefore what to tell the user to do.
|
|
3
|
+
*
|
|
4
|
+
* The close reason was a single fixed string — "upgrade @reticlehq/browser" — sent whichever way the
|
|
5
|
+
* versions disagreed. That is correct only when the page is BEHIND the daemon, and the common real
|
|
6
|
+
* skew runs the other way: the app's package.json pulls a current `@reticlehq/browser` while the
|
|
7
|
+
* daemon is an older `@reticlehq/server` that npx resolved from cache. Observed in this repo's own
|
|
8
|
+
* logs as `got: 2, expected: 1` — a browser two steps ahead, told to upgrade itself.
|
|
9
|
+
*
|
|
10
|
+
* Advice that names the wrong component is worse than none: the user upgrades the thing that was
|
|
11
|
+
* already current, sees no change, and concludes the tool is broken.
|
|
12
|
+
*
|
|
13
|
+
* Kept short on purpose — a WebSocket close reason is capped at 123 BYTES, and a reason that
|
|
14
|
+
* overflows is dropped by the socket layer, which would take the diagnosis with it.
|
|
15
|
+
*
|
|
16
|
+
* NOT a duplicate of `version/version-skew.ts`, and the two should not be merged. That module
|
|
17
|
+
* describes peers which connected SUCCESSFULLY and then disagree about behaviour, so it has both
|
|
18
|
+
* versions and a wire-contract fingerprint to reason from and a whole sentence to say it in. This
|
|
19
|
+
* one runs where the hello was rejected outright: the connection never forms, a single integer is
|
|
20
|
+
* all that is known, and the answer has to fit in a close frame. The remedies are worded to match
|
|
21
|
+
* that module's, because a user who hits both should not be told two different things.
|
|
22
|
+
*/
|
|
23
|
+
/** The page is older than the daemon: its SDK cannot speak the protocol the daemon expects. */
|
|
24
|
+
export const BROWSER_IS_OLDER = 'protocol mismatch — upgrade @reticlehq/browser in this app';
|
|
25
|
+
/** The daemon is older than the page. Usually an npx cache serving a stale @reticlehq/server. */
|
|
26
|
+
export const DAEMON_IS_OLDER = 'protocol mismatch — the daemon is older; run `reticle stop`, then retry';
|
|
27
|
+
/** The versions disagree but not in a direction we can read. */
|
|
28
|
+
export const SKEW_UNKNOWN = 'protocol version mismatch — @reticlehq/browser and server disagree';
|
|
29
|
+
/** The cap a WebSocket close reason must fit inside, in bytes. */
|
|
30
|
+
export const CLOSE_REASON_MAX_BYTES = 123;
|
|
31
|
+
/**
|
|
32
|
+
* Name the stale component, given what the page announced and what this daemon speaks.
|
|
33
|
+
*
|
|
34
|
+
* `got` is the page's protocol version, `expected` is the daemon's. A `got` we could not read at
|
|
35
|
+
* all falls back to naming both, which is vague but never actively misleading.
|
|
36
|
+
*/
|
|
37
|
+
export const protocolSkewReason = (got, expected) => {
|
|
38
|
+
if (null === got || !Number.isFinite(got))
|
|
39
|
+
return SKEW_UNKNOWN;
|
|
40
|
+
if (got < expected)
|
|
41
|
+
return BROWSER_IS_OLDER;
|
|
42
|
+
if (got > expected)
|
|
43
|
+
return DAEMON_IS_OLDER;
|
|
44
|
+
return SKEW_UNKNOWN;
|
|
45
|
+
};
|
|
@@ -16,7 +16,7 @@ import type { FileSystemPort } from '../project/fs-port.js';
|
|
|
16
16
|
* in the schema literal, and in act-capsule's writer — with the named one used by nobody.
|
|
17
17
|
*/
|
|
18
18
|
export declare const CAPSULE_VERSION = 1;
|
|
19
|
-
|
|
19
|
+
declare const CapsuleSchema: z.ZodObject<{
|
|
20
20
|
version: z.ZodLiteral<1>;
|
|
21
21
|
id: z.ZodString;
|
|
22
22
|
/** The flow the failure happened in, when it came from one (a bare assert has no flow). */
|
|
@@ -31,21 +31,21 @@ export declare const CapsuleSchema: z.ZodObject<{
|
|
|
31
31
|
/** Minimal steps to reproduce (prefix-trimmed). */
|
|
32
32
|
steps: z.ZodArray<z.ZodType<FlowStep, z.ZodTypeDef, FlowStep>, "many">;
|
|
33
33
|
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
id: string;
|
|
35
|
+
version: 1;
|
|
34
36
|
expected: string;
|
|
35
37
|
origin: string;
|
|
36
|
-
version: 1;
|
|
37
|
-
id: string;
|
|
38
|
-
observed: string;
|
|
39
38
|
steps: FlowStep[];
|
|
39
|
+
observed: string;
|
|
40
40
|
createdAt: number;
|
|
41
41
|
flow?: string | undefined;
|
|
42
42
|
}, {
|
|
43
|
+
id: string;
|
|
44
|
+
version: 1;
|
|
43
45
|
expected: string;
|
|
44
46
|
origin: string;
|
|
45
|
-
version: 1;
|
|
46
|
-
id: string;
|
|
47
|
-
observed: string;
|
|
48
47
|
steps: FlowStep[];
|
|
48
|
+
observed: string;
|
|
49
49
|
createdAt: number;
|
|
50
50
|
flow?: string | undefined;
|
|
51
51
|
}>;
|
|
@@ -68,3 +68,4 @@ export declare class CapsuleStore {
|
|
|
68
68
|
export declare function capsuleId(now: number, label: string): string;
|
|
69
69
|
/** The minimal reproduction: the steps up to and including the one that failed (prefix trim). */
|
|
70
70
|
export declare function minimalSteps(steps: readonly FlowStep[], failedIndex: number): FlowStep[];
|
|
71
|
+
export {};
|
|
@@ -17,7 +17,7 @@ import { reticleDirPaths } from '../project/reticle-dir.js';
|
|
|
17
17
|
* in the schema literal, and in act-capsule's writer — with the named one used by nobody.
|
|
18
18
|
*/
|
|
19
19
|
export const CAPSULE_VERSION = 1;
|
|
20
|
-
|
|
20
|
+
const CapsuleSchema = z.object({
|
|
21
21
|
version: z.literal(CAPSULE_VERSION),
|
|
22
22
|
id: z.string(),
|
|
23
23
|
/** The flow the failure happened in, when it came from one (a bare assert has no flow). */
|
|
@@ -4,7 +4,7 @@ import { type ReticleEvent } from '@reticlehq/core';
|
|
|
4
4
|
* app did in the act's window, as counts + a headline, not a raw event dump. Diffs come from the
|
|
5
5
|
* storage/state events; attribution from. Pure composition over the attributed event window.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
interface StateDiff {
|
|
8
8
|
path: string;
|
|
9
9
|
from: unknown;
|
|
10
10
|
to: unknown;
|
|
@@ -20,7 +20,7 @@ export interface StateDiff {
|
|
|
20
20
|
*/
|
|
21
21
|
atMs: number;
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
interface StorageDiff {
|
|
24
24
|
key: string;
|
|
25
25
|
from?: unknown;
|
|
26
26
|
to?: unknown;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EventType, PerfMetric, isDevToolingUrl } from '@reticlehq/core';
|
|
2
|
+
import { routePathOf } from '../events/predicate-route.js';
|
|
2
3
|
/**
|
|
3
4
|
* THE INVARIANT: every field of this summary has a bounded size that does not depend on how much the
|
|
4
5
|
* app did. A verdict travels in the same payload as its evidence, so an unbounded evidence field is
|
|
@@ -119,8 +120,12 @@ export function causalSummary(events, context = {}) {
|
|
|
119
120
|
break;
|
|
120
121
|
}
|
|
121
122
|
case EventType.ROUTE_CHANGE:
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
// The ROUTER's path. Reporting the document pathname left `route: "/"` on every action of a
|
|
124
|
+
// hash-routed app — the same reading that made a route predicate unsatisfiable there, and an
|
|
125
|
+
// agent reasoning off this field would conclude the route never changed. See routePathOf.
|
|
126
|
+
if ('string' === typeof data['pathname']) {
|
|
127
|
+
route = routePathOf(data['pathname'], 'string' === typeof data['hash'] ? data['hash'] : '');
|
|
128
|
+
}
|
|
124
129
|
break;
|
|
125
130
|
case EventType.SIGNAL:
|
|
126
131
|
pushUnique(signals, data['name']);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The launch options every Reticle-driven Chromium gets, in one place.
|
|
3
|
+
*
|
|
4
|
+
* A pooled browser serves several lease contexts while only one page is ever the visible one, and a
|
|
5
|
+
* driven page can sit in a window the host does not treat as visible. For everyone else Chromium
|
|
6
|
+
* throttles timers, suspends rAF, and deprioritizes the renderer, which is exactly the state a
|
|
7
|
+
* session reports as `throttled: true`: WebSocket- and timer-driven DOM updates land seconds late,
|
|
8
|
+
* asserts time out on content that appears right after the timeout, with no refetch, because the
|
|
9
|
+
* app was fine and the browser was parked. The three switches below are the documented opt-outs,
|
|
10
|
+
* shared by both launch sites so the pooled path (`playwright-launcher`) and the drive path
|
|
11
|
+
* (`real-input`) cannot drift apart.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Chromium switches that keep background pages running at full speed. Named individually rather
|
|
15
|
+
* than as one free string per call site, so a typo is a test failure instead of a silently ignored
|
|
16
|
+
* argument.
|
|
17
|
+
*/
|
|
18
|
+
export declare const CHROMIUM_ANTI_THROTTLING_ARGS: readonly ["--disable-background-timer-throttling", "--disable-backgrounding-occluded-windows", "--disable-renderer-backgrounding"];
|
|
19
|
+
/** The options object handed to `chromium.launch` at both launch sites. */
|
|
20
|
+
export interface ChromiumLaunchOptions {
|
|
21
|
+
headless: boolean;
|
|
22
|
+
args: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the launch options for a headless or headed Chromium. Pure; returns a fresh mutable array
|
|
26
|
+
* each call so no caller can mutate the constant behind everyone else's back.
|
|
27
|
+
*/
|
|
28
|
+
export declare function chromiumLaunchOptions(headless: boolean): ChromiumLaunchOptions;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The launch options every Reticle-driven Chromium gets, in one place.
|
|
3
|
+
*
|
|
4
|
+
* A pooled browser serves several lease contexts while only one page is ever the visible one, and a
|
|
5
|
+
* driven page can sit in a window the host does not treat as visible. For everyone else Chromium
|
|
6
|
+
* throttles timers, suspends rAF, and deprioritizes the renderer, which is exactly the state a
|
|
7
|
+
* session reports as `throttled: true`: WebSocket- and timer-driven DOM updates land seconds late,
|
|
8
|
+
* asserts time out on content that appears right after the timeout, with no refetch, because the
|
|
9
|
+
* app was fine and the browser was parked. The three switches below are the documented opt-outs,
|
|
10
|
+
* shared by both launch sites so the pooled path (`playwright-launcher`) and the drive path
|
|
11
|
+
* (`real-input`) cannot drift apart.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Chromium switches that keep background pages running at full speed. Named individually rather
|
|
15
|
+
* than as one free string per call site, so a typo is a test failure instead of a silently ignored
|
|
16
|
+
* argument.
|
|
17
|
+
*/
|
|
18
|
+
export const CHROMIUM_ANTI_THROTTLING_ARGS = [
|
|
19
|
+
'--disable-background-timer-throttling',
|
|
20
|
+
'--disable-backgrounding-occluded-windows',
|
|
21
|
+
'--disable-renderer-backgrounding',
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Build the launch options for a headless or headed Chromium. Pure; returns a fresh mutable array
|
|
25
|
+
* each call so no caller can mutate the constant behind everyone else's back.
|
|
26
|
+
*/
|
|
27
|
+
export function chromiumLaunchOptions(headless) {
|
|
28
|
+
return { headless, args: [...CHROMIUM_ANTI_THROTTLING_ARGS] };
|
|
29
|
+
}
|
package/dist/cli/cli-doctor.js
CHANGED
|
@@ -17,6 +17,10 @@ import { diagnoseDesktop, isDesktopProject } from '../init/desktop-doctor.js';
|
|
|
17
17
|
import { diagnoseWebCsp } from '../init/csp-doctor.js';
|
|
18
18
|
import { RETICLE_CONFIG_BASENAME, diagnosePortMismatch, readProjectId, readProjectPort, } from './cli-port.js';
|
|
19
19
|
import { DoctorRow, doctorRow } from './doctor-rows.js';
|
|
20
|
+
import { attachState, describeAttachState } from '../mcp/attach-memory.js';
|
|
21
|
+
import { findOccupiedSiblings, siblingListenerNote } from './sibling-ports.js';
|
|
22
|
+
import { daemonsServingProjectElsewhere, resolveDaemonForProject, splitBrainNote, wrongDaemonNote, } from '../daemon/daemon-resolve.js';
|
|
23
|
+
import { isAlive } from '../daemon/daemon.js';
|
|
20
24
|
/**
|
|
21
25
|
* `reticle doctor` — collapse the ~6 independent first-run failure modes into one command. Checks the
|
|
22
26
|
* Chromium install (the #1 silent failure), whether a daemon is up on the resolved bridge port, and
|
|
@@ -40,6 +44,18 @@ function asIdentity(payload) {
|
|
|
40
44
|
...(contract === undefined ? {} : { contract }),
|
|
41
45
|
};
|
|
42
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* One project, two daemons — asked from whichever end this command is standing on.
|
|
49
|
+
*
|
|
50
|
+
* Shares the rule with `reticle status` rather than restating it: the empty daemon can see that the
|
|
51
|
+
* app connected somewhere else, the daemon holding the app can only see that this project owns
|
|
52
|
+
* another one, and a second copy of either half would be a second opinion waiting to disagree.
|
|
53
|
+
*/
|
|
54
|
+
function splitBrainLine(port, projectId) {
|
|
55
|
+
const home = reticleStateHome();
|
|
56
|
+
const elsewhere = splitBrainNote(port, daemonsServingProjectElsewhere(projectId, port, home, isAlive, (other) => hasProjectConnectedBefore(home, other, projectId)));
|
|
57
|
+
return elsewhere ?? wrongDaemonNote(port, resolveDaemonForProject(projectId, home, isAlive));
|
|
58
|
+
}
|
|
43
59
|
export async function handleDoctor(port) {
|
|
44
60
|
const line = (s) => {
|
|
45
61
|
process.stdout.write(`${s}\n`);
|
|
@@ -91,10 +107,32 @@ export async function handleDoctor(port) {
|
|
|
91
107
|
const mismatch = diagnosePortMismatch(port, projectPort);
|
|
92
108
|
if (mismatch !== undefined)
|
|
93
109
|
line(doctorRow(DoctorRow.PORT_CHECK, `✗ ${mismatch}`));
|
|
110
|
+
const projectId = readProjectId(process.cwd());
|
|
111
|
+
// The hop between the agent and this daemon, stated as its own row rather than inferred from the
|
|
112
|
+
// ones above it. `sessions` says the BROWSER reached the daemon; nothing said whether the AGENT
|
|
113
|
+
// did, and those two are exactly the pair a user cannot tell apart when one of them is broken.
|
|
114
|
+
const attach = attachState(reticleStateHome(), port);
|
|
115
|
+
const attachAction = describeAttachState(attach);
|
|
116
|
+
// A SPLIT outranks the attach record, and this is the one place it can. Both records are per PORT,
|
|
117
|
+
// so on a machine whose daemons have split in two the attach row reports "nothing has ever
|
|
118
|
+
// attached here" and sends the reader to re-register a server that is registered and running —
|
|
119
|
+
// the confident wrong answer for a link that is not missing but misaimed.
|
|
120
|
+
const split = splitBrainLine(port, projectId);
|
|
121
|
+
line(doctorRow(DoctorRow.AGENT_LINK, split !== undefined
|
|
122
|
+
? `✗ ${split}`
|
|
123
|
+
: attachAction === undefined
|
|
124
|
+
? "✓ an MCP client has listed and called Reticle's tools on this port"
|
|
125
|
+
: `✗ ${attachAction}`));
|
|
126
|
+
// Remaining half of #261. The SDK dialling a port we are not on is invisible as a refused inbound,
|
|
127
|
+
// but a listener on a well-known Reticle port is visible. Report the observation; do not conclude
|
|
128
|
+
// it is the daemon this app wants — somebody else's daemon looks the same from here.
|
|
129
|
+
const occupiedSiblings = await findOccupiedSiblings(port, probeDaemon);
|
|
130
|
+
const siblingNote = siblingListenerNote(port, occupiedSiblings);
|
|
131
|
+
if (siblingNote !== undefined)
|
|
132
|
+
line(doctorRow(DoctorRow.SIBLING, siblingNote));
|
|
94
133
|
// The check this checklist was missing: is the APP wired, not just the tools. Everything above can
|
|
95
134
|
// be green in a project that has never been through `init`, and that combination is precisely the
|
|
96
135
|
// one `doctor` gets run to explain.
|
|
97
|
-
const projectId = readProjectId(process.cwd());
|
|
98
136
|
line(projectWiringLine({
|
|
99
137
|
projectId,
|
|
100
138
|
previouslyConnected: hasProjectConnectedBefore(reticleStateHome(), port, projectId),
|
|
@@ -27,4 +27,6 @@ export declare function handleCapsules(): Promise<void>;
|
|
|
27
27
|
* changed files. Flaky flows are quarantined (surfaced, not blocking). The environment-side enforcement
|
|
28
28
|
* that makes verification unavoidable. Never throws; a fault fails closed (exit 1).
|
|
29
29
|
*/
|
|
30
|
-
export declare function handleGate(files: string[], since: string | undefined
|
|
30
|
+
export declare function handleGate(files: string[], since: string | undefined,
|
|
31
|
+
/** Hook mode: prose for a human, and silence when there was simply nothing to check. */
|
|
32
|
+
hook?: boolean): Promise<void>;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* opens first. These five functions form one cohesive group: they all read the flow store and report on
|
|
6
6
|
* verification state, and none of them touch daemon lifecycle, which is what the rest of cli.ts does.
|
|
7
7
|
*/
|
|
8
|
+
import { GateExit } from './gate-exit.js';
|
|
9
|
+
import { gateHookMessage, GATE_SKIP_ENV } from './gate-hook-message.js';
|
|
8
10
|
import { readProjectId } from './cli-port.js';
|
|
9
11
|
import { changedFilesSince } from '../flows/git-changed.js';
|
|
10
12
|
import { join } from 'node:path';
|
|
@@ -143,7 +145,9 @@ export async function handleCapsules() {
|
|
|
143
145
|
* changed files. Flaky flows are quarantined (surfaced, not blocking). The environment-side enforcement
|
|
144
146
|
* that makes verification unavoidable. Never throws; a fault fails closed (exit 1).
|
|
145
147
|
*/
|
|
146
|
-
export async function handleGate(files, since
|
|
148
|
+
export async function handleGate(files, since,
|
|
149
|
+
/** Hook mode: prose for a human, and silence when there was simply nothing to check. */
|
|
150
|
+
hook = false) {
|
|
147
151
|
try {
|
|
148
152
|
const fs = createNodeFileSystem();
|
|
149
153
|
const reticleRoot = join(process.cwd(), ReticleDir.ROOT);
|
|
@@ -196,11 +200,37 @@ export async function handleGate(files, since) {
|
|
|
196
200
|
...(result.deleted.length > 0 ? { deletedCoverage: result.deleted } : {}),
|
|
197
201
|
coverage: flowCoverage,
|
|
198
202
|
});
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
// Two non-zero codes, because two callers want opposite things from the same run. CI wants any
|
|
204
|
+
// problem to fail. A Stop hook wants to block a real regression and NOT block a project that
|
|
205
|
+
// has simply not recorded a flow yet — which is every project on its first day, and measured
|
|
206
|
+
// before this change as an exit 1 that would have blocked every stop. See GateExit.
|
|
207
|
+
if (!pass) {
|
|
208
|
+
const code = result.pass && flowCoverage.outcome !== undefined
|
|
209
|
+
? GateExit.NOTHING_TO_CHECK
|
|
210
|
+
: GateExit.FAIL;
|
|
211
|
+
process.exitCode = code;
|
|
212
|
+
if (hook) {
|
|
213
|
+
// An honest escape hatch, because the alternative to one is not compliance — it is somebody
|
|
214
|
+
// deleting the hook, and a deleted gate protects nothing. Recorded either way.
|
|
215
|
+
if (process.env[GATE_SKIP_ENV] !== undefined) {
|
|
216
|
+
process.stderr.write(`Reticle: gate skipped via ${GATE_SKIP_ENV}. This change is unverified.\n`);
|
|
217
|
+
process.exitCode = GateExit.PASS;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const message = gateHookMessage(code, {
|
|
221
|
+
uncovered: result.uncovered,
|
|
222
|
+
quarantined: result.quarantined,
|
|
223
|
+
// A downgrade is reported per flow with its step indices; the hook names the flow.
|
|
224
|
+
downgraded: result.downgraded.map((d) => d.flow),
|
|
225
|
+
deleted: result.deleted,
|
|
226
|
+
});
|
|
227
|
+
if (message !== undefined)
|
|
228
|
+
process.stderr.write(`${message}\n`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
201
231
|
}
|
|
202
232
|
catch (error) {
|
|
203
233
|
log('reticle_gate_failed', { error: error instanceof Error ? error.message : String(error) });
|
|
204
|
-
process.exitCode =
|
|
234
|
+
process.exitCode = GateExit.FAIL;
|
|
205
235
|
}
|
|
206
236
|
}
|
package/dist/cli/cli-kill.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare const KillAction: {
|
|
|
25
25
|
readonly REFUSE: "refuse";
|
|
26
26
|
};
|
|
27
27
|
export type KillAction = (typeof KillAction)[keyof typeof KillAction];
|
|
28
|
-
|
|
28
|
+
interface KillPlan {
|
|
29
29
|
action: KillAction;
|
|
30
30
|
/** The single pid to signal. Present only for `kill`. */
|
|
31
31
|
pid?: number;
|
|
@@ -56,3 +56,4 @@ export declare function planKill(input: {
|
|
|
56
56
|
}): KillPlan;
|
|
57
57
|
/** `reticle kill` — free the port. Resolves to whether the port is now free. */
|
|
58
58
|
export declare function runKill(port: number, force: boolean): Promise<boolean>;
|
|
59
|
+
export {};
|
package/dist/cli/cli-launch.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ interface StatusSession {
|
|
|
10
10
|
throttled: boolean;
|
|
11
11
|
stale: boolean;
|
|
12
12
|
pendingMarks: number;
|
|
13
|
+
/** The tab is attached and answering nothing — see Session.unresponsive. Absent on an older daemon. */
|
|
14
|
+
unresponsive?: true;
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Reduce the daemon's /status JSON to the compact view `reticle status` prints. Pure: narrows the
|
|
@@ -62,8 +64,9 @@ type OpenDecision = {
|
|
|
62
64
|
* is run by a human whose browser this is, and yanking the page they are looking at to somewhere else
|
|
63
65
|
* is a bigger surprise than being told where the tab actually is.
|
|
64
66
|
*/
|
|
65
|
-
export declare function decideOpen(
|
|
67
|
+
export declare function decideOpen(all: {
|
|
66
68
|
url: string;
|
|
69
|
+
unresponsive?: boolean;
|
|
67
70
|
}[], url: string | undefined): OpenDecision;
|
|
68
71
|
/** The OS command that opens a URL in the default browser, per platform. Pure — unit-tested. */
|
|
69
72
|
export declare function openCommand(url: string, platform: NodeJS.Platform): {
|
|
@@ -79,4 +82,11 @@ export declare function openCommand(url: string, platform: NodeJS.Platform): {
|
|
|
79
82
|
* port problem while nothing had ever opened.
|
|
80
83
|
*/
|
|
81
84
|
export declare function openInBrowser(url: string, platform?: NodeJS.Platform, run?: (cmd: string, args: string[]) => Promise<string | null>): Promise<string | null>;
|
|
85
|
+
/**
|
|
86
|
+
* What a finished launcher means, or null when nothing is wrong.
|
|
87
|
+
*
|
|
88
|
+
* Exported for its own tests: the headless case is the one that matters and it cannot be produced
|
|
89
|
+
* by spawning a real launcher on a developer machine.
|
|
90
|
+
*/
|
|
91
|
+
export declare function launcherFailure(code: number | null, signal: NodeJS.Signals | null): string | null;
|
|
82
92
|
export {};
|
package/dist/cli/cli-launch.js
CHANGED
|
@@ -2,10 +2,11 @@ import * as http from 'node:http';
|
|
|
2
2
|
import { NodePlatform } from '../platform.js';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { isOpaqueOrigin, LOOPBACK_HOST, STATUS_PATH } from '@reticlehq/core';
|
|
5
|
-
import {
|
|
5
|
+
import { daemonFix, describeSkew } from '../version/version-skew.js';
|
|
6
6
|
import { CONTRACT_FINGERPRINT } from '@reticlehq/core';
|
|
7
7
|
import { SERVER_VERSION } from '../version/server-version.js';
|
|
8
8
|
import { log } from '../log.js';
|
|
9
|
+
import { loopbackAgent } from '../loopback-agent.js';
|
|
9
10
|
/**
|
|
10
11
|
* Reduce the daemon's /status JSON to the compact view `reticle status` prints. Pure: narrows the
|
|
11
12
|
* untrusted wire payload (never `any`) and tolerates a missing/partial body so a malformed response
|
|
@@ -33,6 +34,7 @@ export function summarizeStatus(payload) {
|
|
|
33
34
|
throttled: true === r['throttled'],
|
|
34
35
|
stale: true === r['stale'],
|
|
35
36
|
pendingMarks: 'number' === typeof r['pendingMarks'] ? r['pendingMarks'] : 0,
|
|
37
|
+
...(true === r['unresponsive'] ? { unresponsive: true } : {}),
|
|
36
38
|
};
|
|
37
39
|
})
|
|
38
40
|
.filter((s) => s !== null);
|
|
@@ -55,11 +57,13 @@ function statusField(payload, key) {
|
|
|
55
57
|
*/
|
|
56
58
|
export async function warnOnDaemonSkew(port) {
|
|
57
59
|
const status = await fetchStatus(port);
|
|
60
|
+
const daemonVersion = statusField(status, 'version');
|
|
58
61
|
const skew = describeSkew({
|
|
59
62
|
what: 'the daemon already running on this port',
|
|
60
|
-
version:
|
|
63
|
+
version: daemonVersion,
|
|
61
64
|
contract: statusField(status, 'contract'),
|
|
62
|
-
|
|
65
|
+
// The daemon is the peer here and this process is the agent side.
|
|
66
|
+
fix: daemonFix(daemonVersion, SERVER_VERSION),
|
|
63
67
|
}, { version: SERVER_VERSION, contract: CONTRACT_FINGERPRINT });
|
|
64
68
|
if (skew !== undefined)
|
|
65
69
|
log('reticle_daemon_skew', { port, warning: skew });
|
|
@@ -69,7 +73,15 @@ const STATUS_PROBE_TIMEOUT_MS = 1000;
|
|
|
69
73
|
/** GET the daemon's /status JSON. Resolves to the parsed body, or undefined on any failure. */
|
|
70
74
|
export function fetchStatus(port) {
|
|
71
75
|
return new Promise((resolve) => {
|
|
72
|
-
const req = http.get({
|
|
76
|
+
const req = http.get({
|
|
77
|
+
host: LOOPBACK_HOST,
|
|
78
|
+
port,
|
|
79
|
+
path: STATUS_PATH,
|
|
80
|
+
timeout: STATUS_PROBE_TIMEOUT_MS,
|
|
81
|
+
// Shares the proxy's keep-alive agent: the proxy calls this on every reconnect, and a socket
|
|
82
|
+
// per probe is the same churn the POST leg was fixed for.
|
|
83
|
+
agent: loopbackAgent,
|
|
84
|
+
}, (res) => {
|
|
73
85
|
let body = '';
|
|
74
86
|
res.setEncoding('utf8');
|
|
75
87
|
res.on('data', (chunk) => (body += chunk));
|
|
@@ -117,7 +129,11 @@ function sameOrigin(a, b) {
|
|
|
117
129
|
* is run by a human whose browser this is, and yanking the page they are looking at to somewhere else
|
|
118
130
|
* is a bigger surprise than being told where the tab actually is.
|
|
119
131
|
*/
|
|
120
|
-
export function decideOpen(
|
|
132
|
+
export function decideOpen(all, url) {
|
|
133
|
+
// A tab that has stopped answering is not a tab you can be handed. `open` is the command a caller
|
|
134
|
+
// reaches for to RECOVER, and reusing the wedged one left no way out short of killing the daemon:
|
|
135
|
+
// ending the session only flips a flag on the record, it does not make the page answer.
|
|
136
|
+
const sessions = all.filter((s) => true !== s.unresponsive);
|
|
121
137
|
if (url === undefined) {
|
|
122
138
|
const first = sessions[0];
|
|
123
139
|
return first !== undefined ? { action: 'reuse', url: first.url } : { action: 'need-url' };
|
|
@@ -159,16 +175,49 @@ export async function openInBrowser(url, platform = process.platform, run = defa
|
|
|
159
175
|
const { cmd, args } = openCommand(url, platform);
|
|
160
176
|
return run(cmd, args);
|
|
161
177
|
}
|
|
162
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* What a finished launcher means, or null when nothing is wrong.
|
|
180
|
+
*
|
|
181
|
+
* Exported for its own tests: the headless case is the one that matters and it cannot be produced
|
|
182
|
+
* by spawning a real launcher on a developer machine.
|
|
183
|
+
*/
|
|
184
|
+
export function launcherFailure(code, signal) {
|
|
185
|
+
if (null !== signal)
|
|
186
|
+
return `the browser launcher was killed by ${signal}`;
|
|
187
|
+
// null code with no signal: it never reported an outcome in the window we waited. Some desktop
|
|
188
|
+
// launchers stay attached rather than handing off, and inventing a failure there would be worse
|
|
189
|
+
// than the silence — it would send someone to fix a browser that opened.
|
|
190
|
+
if (null === code || 0 === code)
|
|
191
|
+
return null;
|
|
192
|
+
return (`the browser launcher exited ${String(code)} — on a machine with no browser (CI, a container, ` +
|
|
193
|
+
'an SSH session, WSL with no host browser) it starts fine and then has nothing to open');
|
|
194
|
+
}
|
|
195
|
+
/** How long to wait for the launcher to hand off before assuming it did. */
|
|
196
|
+
const LAUNCHER_EXIT_MS = 2_000;
|
|
197
|
+
/** null when the launcher opened something; the failure message when it did not. */
|
|
163
198
|
function defaultRun(cmd, args) {
|
|
164
199
|
return new Promise((resolve) => {
|
|
165
200
|
const child = spawn(cmd, args, { detached: true, stdio: 'ignore' });
|
|
201
|
+
let settled = false;
|
|
202
|
+
const finish = (value) => {
|
|
203
|
+
if (settled)
|
|
204
|
+
return;
|
|
205
|
+
settled = true;
|
|
206
|
+
resolve(value);
|
|
207
|
+
};
|
|
166
208
|
// An unhandled 'error' on a ChildProcess throws — and ENOENT on the launcher is exactly the case
|
|
167
209
|
// this function exists to report, so it must be listened for either way.
|
|
168
|
-
child.on('error', (err) =>
|
|
169
|
-
|
|
210
|
+
child.on('error', (err) => finish(err.message));
|
|
211
|
+
// The EXIT, not the spawn. Answering "did the command begin" reported success on every headless
|
|
212
|
+
// box, where the launcher begins perfectly well and then finds nothing to open — see
|
|
213
|
+
// launcherFailure. Every launcher we use hands off and exits immediately, so this costs nothing
|
|
214
|
+
// on the path where it works.
|
|
215
|
+
child.on('exit', (code, signal) => finish(launcherFailure(code, signal)));
|
|
216
|
+
// ...and a launcher that stays attached must not hold up the install.
|
|
217
|
+
const timer = setTimeout(() => {
|
|
170
218
|
child.unref();
|
|
171
|
-
|
|
172
|
-
});
|
|
219
|
+
finish(null);
|
|
220
|
+
}, LAUNCHER_EXIT_MS);
|
|
221
|
+
timer.unref?.();
|
|
173
222
|
});
|
|
174
223
|
}
|
package/dist/cli/cli-parse.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { AGENT_FLAG, BUG_FLAG, FEEDBACK_KINDS, KIND_FLAG, RATING_FLAG, } from '.
|
|
|
13
13
|
* telling readers to do exactly that on 110 lines before it was caught, so the invocation now leads
|
|
14
14
|
* the usage block rather than being a footnote somewhere else.
|
|
15
15
|
*/
|
|
16
|
-
export declare const CLI_USAGE = "usage: npx @reticlehq/server <command> (or `reticle <command>` once the bin is on your PATH)\n\n reticle init [--dry-run] [--port N] [--no-mcp] [--no-install] [--app <dir>] (wire Reticle into the project in this directory)\n --app picks WHICH app in a monorepo, when several are found\n --no-mcp skips MORE than the server registration: also the agent rule files\n (CLAUDE.md / AGENTS.md / .cursor) and the /reticle command, because all three\n only make sense once the tools are reachable.\n reticle serve [--port N] [--drive <url>] [--headless] [--http] [--http-port N] [--http-token T]\n reticle stop [--port N] [--quiet] (stop the daemon we started, by its recorded pid)\n reticle kill [--port N] [--force] (free the port by its LISTENER, never the agent's mcp proxy)\n reticle restart [--port N] [--force] (kill, then start a daemon and wait for a real bind)\n reticle status [--port N]\n reticle doctor [--port N] (one command to diagnose setup: Chromium, daemon, port)\n reticle open [url] [--port N] (show the app: reuse the connected tab, else open one)\n reticle verify <url> [--headed] [--timeout N] [--storage-state <file>] (one-shot: drive the URL, verify saved flows, exit 0=pass)\n reticle affected [--since <ref>] [file...] (which saved flows must re-verify for the changed files)\n reticle gate [--since <ref>] [file...] (exit non-zero unless passing artifacts cover the affected flows)\n reticle watch [url] (on save, report which saved flows must re-verify)\n reticle drive <url> [--headless] (foreground mode, for debugging)\n reticle mcp [--port N] [--drive <url>] [--headless] (MCP stdio proxy; auto-starts daemon if needed)\n reticle update (install the latest server version and restart)\n reticle rollback (restore the previous server version and restart)\n reticle license (show enterprise license status: active | eval | missing)\n reticle telemetry [status|enable|disable] (anonymous usage metrics; status shows what's sent + the policy)\n reticle feedback [--rating 1-5] [--bug] \"message\" (tell us what worked and what didn't; prints exactly what it sends)\n reticle feedback --agent --kind <bug|gap|ambiguity|feature_request|improvement|experience> \"message\"\n (agents: file from anywhere, including a setup that never finished)\n reticle identify --context company|side_project|open_source|learning [--company N] [--email E] [--forget]\n (OPT-IN: tell us who you are, e.g. for support or an enterprise trial)\n\nCloud (link this project to Reticle
|
|
16
|
+
export declare const CLI_USAGE = "usage: npx @reticlehq/server <command> (or `reticle <command>` once the bin is on your PATH)\n\n reticle init [--dry-run] [--port N] [--no-mcp] [--no-install] [--app <dir>]\n [--flow \"<what to drive>\"] [--env KEY=VALUE]... [--files-only] (wire Reticle into the project in this directory)\n --app picks WHICH app in a monorepo, when several are found\n --flow names the journey worth proving, in your own words. Only you know\n which one matters, and naming it is the difference between a drive that\n finishes and one that spends its budget looking for something to do\n --env is what the app needs to reach a usable state: the key from\n .env.example, the mock backend, the variable that skips an auth wall.\n Repeatable, and a value may contain spaces and equals signs\n --files-only writes the files and stops, which is what init did before it\n learned to boot the app and prove the install works\n --license writes the key to .env and keeps .env out of git\n --json puts the result on stdout, so an agent reads one object\n --no-drive / --no-open / --no-agents / --url / --timeout / --drive-model\n are the runtime dials: CI, a headless box, or an app you already run\n --no-mcp skips MORE than the server registration: also the agent rule files\n (CLAUDE.md / AGENTS.md / .cursor) and the /reticle command, because all three\n only make sense once the tools are reachable.\n reticle serve [--port N] [--drive <url>] [--headless] [--http] [--http-port N] [--http-token T]\n reticle stop [--port N] [--quiet] (stop the daemon we started, by its recorded pid)\n reticle kill [--port N] [--force] (free the port by its LISTENER, never the agent's mcp proxy)\n reticle restart [--port N] [--force] (kill, then start a daemon and wait for a real bind)\n reticle status [--port N]\n reticle doctor [--port N] (one command to diagnose setup: Chromium, daemon, port)\n reticle open [url] [--port N] (show the app: reuse the connected tab, else open one)\n reticle verify <url> [--port N] [--headed] [--timeout N] [--storage-state <file>] [--session-id <id>] (one-shot: drive the URL, verify saved flows, exit 0=pass)\n reticle affected [--since <ref>] [file...] (which saved flows must re-verify for the changed files)\n reticle gate [--since <ref>] [file...] (exit non-zero unless passing artifacts cover the affected flows)\n reticle watch [url] (on save, report which saved flows must re-verify)\n reticle drive <url> [--headless] (foreground mode, for debugging)\n reticle mcp [--port N] [--drive <url>] [--headless] (MCP stdio proxy; auto-starts daemon if needed)\n reticle update (install the latest server version and restart)\n reticle rollback (restore the previous server version and restart)\n reticle license (show enterprise license status: active | eval | missing)\n reticle telemetry [status|enable|disable] (anonymous usage metrics; status shows what's sent + the policy)\n reticle feedback [--rating 1-5] [--bug] \"message\" (tell us what worked and what didn't; prints exactly what it sends)\n reticle feedback --agent --kind <bug|gap|ambiguity|feature_request|improvement|experience> \"message\"\n (agents: file from anywhere, including a setup that never finished)\n reticle identify --context company|side_project|open_source|learning [--company N] [--email E] [--forget]\n (OPT-IN: tell us who you are, e.g. for support or an enterprise trial)\n\nCloud (link this project to Reticle; runs/flows recorded on the dashboard):\n reticle login [--url <u>] [--email <e>] [--code <c>] [--org <n>]\n (browser device flow by default; --email mails a code)\n reticle logout [--url <u>] (sign out of ONE host; others stay signed in)\n reticle link [--project <name|id>] (bind this repo: mints a scoped key, writes .reticle/cloud.json)\n reticle whoami (who am I signed in as, and is this repo attached?)\n reticle project <ls|create <name>> (list or create cloud projects)\n reticle config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]\n reticle push | sync [--watch] (one sync cycle: send the difference, collect decisions)\n reticle runs | regression | share <runId> (read cloud state; regression exits 3 if any flow broke)\n\n'drive' shows the browser; everything else is hidden (serve/mcp own the pool behind leases, replay\nand the spec runner: batch work). --headed opts any of them in, --headless hides drive, CI hides it.";
|
|
17
17
|
/** The `reticle telemetry` sub-actions. Bare `reticle telemetry` means `status`. */
|
|
18
18
|
export declare const TelemetryAction: {
|
|
19
19
|
readonly STATUS: "status";
|
|
@@ -47,6 +47,19 @@ export type CliResult = {
|
|
|
47
47
|
dryRun: boolean;
|
|
48
48
|
install: boolean;
|
|
49
49
|
app: string | undefined;
|
|
50
|
+
flow: string | undefined;
|
|
51
|
+
env: string[];
|
|
52
|
+
filesOnly: boolean;
|
|
53
|
+
captureBodies: boolean;
|
|
54
|
+
json: boolean;
|
|
55
|
+
drive: boolean;
|
|
56
|
+
relaunch: boolean;
|
|
57
|
+
open: boolean;
|
|
58
|
+
agents: boolean;
|
|
59
|
+
url: string | undefined;
|
|
60
|
+
timeoutSeconds: number | undefined;
|
|
61
|
+
driveModel: string | undefined;
|
|
62
|
+
licenseKey: string | undefined;
|
|
50
63
|
} | {
|
|
51
64
|
kind: 'serve';
|
|
52
65
|
port: number;
|
|
@@ -111,8 +124,10 @@ export type CliResult = {
|
|
|
111
124
|
kind: 'verify';
|
|
112
125
|
url: string;
|
|
113
126
|
headless: boolean;
|
|
127
|
+
port: number;
|
|
114
128
|
timeoutMs?: number;
|
|
115
129
|
storageState?: string;
|
|
130
|
+
sessionId?: string;
|
|
116
131
|
} | {
|
|
117
132
|
kind: 'affected';
|
|
118
133
|
files: string[];
|
|
@@ -126,6 +141,7 @@ export type CliResult = {
|
|
|
126
141
|
kind: 'gate';
|
|
127
142
|
files: string[];
|
|
128
143
|
since?: string;
|
|
144
|
+
hook?: boolean;
|
|
129
145
|
} | {
|
|
130
146
|
kind: 'watch';
|
|
131
147
|
url?: string;
|