@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,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telling the human that the agent went somewhere they cannot see.
|
|
3
|
+
*
|
|
4
|
+
* A lease is a separate pooled context, and it is the highest-value path in the product for
|
|
5
|
+
* autonomous work. It also has one failure mode that is entirely about people: the HUD lives in the
|
|
6
|
+
* human's OWN tab, so while an agent drives a lease that tab shows nothing at all. An idle agent and
|
|
7
|
+
* a busy one are then indistinguishable from the only seat a human occupies.
|
|
8
|
+
*
|
|
9
|
+
* That is not hypothetical. An agent followed the daemon's own throttle recommendation into a lease,
|
|
10
|
+
* drove fifteen calls, and the developer watching an empty HUD asked why nothing was running. The
|
|
11
|
+
* product was working perfectly and looked broken, which is the worst combination available.
|
|
12
|
+
*
|
|
13
|
+
* The fix rides on machinery that already exists — `pushNarration` posts `ReticleCommand.NARRATE`
|
|
14
|
+
* and every SDK in the field already renders it — so nothing here moves the wire contract. It is a
|
|
15
|
+
* message to tabs that are ALREADY connected, not a new capability.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Shown on a watching human's HUD when an agent takes a lease.
|
|
19
|
+
*
|
|
20
|
+
* States the fact and its consequence, because the fact alone ("agent leased a context") does not
|
|
21
|
+
* tell somebody why their screen is still. The consequence is the whole reason to say anything.
|
|
22
|
+
*/
|
|
23
|
+
export const AGENT_DRIVING_ELSEWHERE = 'An agent is driving a separate leased context — its actions will not appear in this tab.';
|
|
24
|
+
/** Shown when the lease is released, so a HUD that went quiet is explained in both directions. */
|
|
25
|
+
export const AGENT_DRIVING_HERE_AGAIN = 'The agent released its separate context — this tab is live again.';
|
|
26
|
+
/**
|
|
27
|
+
* Shown to a tab that CONNECTS while a lease is already open.
|
|
28
|
+
*
|
|
29
|
+
* The acquire-time notice only reaches tabs that were already connected, so opening the app — or
|
|
30
|
+
* merely reloading it — during a lease landed somebody on a silent HUD with nothing explaining it.
|
|
31
|
+
* That is the same dark-HUD failure the acquire notice exists to prevent, arrived at from the other
|
|
32
|
+
* direction, and it is the more common one: a person opens the dashboard BECAUSE they want to watch.
|
|
33
|
+
*
|
|
34
|
+
* Worded for arrival rather than for a change of state — nothing just happened from this tab's point
|
|
35
|
+
* of view, so "an agent is driving elsewhere" would describe an event it never saw.
|
|
36
|
+
*/
|
|
37
|
+
export const AGENT_ALREADY_DRIVING_ELSEWHERE = 'An agent is already driving a separate leased context — its actions will not appear in this tab.';
|
|
38
|
+
/**
|
|
39
|
+
* Whether a session arriving now should be told a lease is already running.
|
|
40
|
+
*
|
|
41
|
+
* Same rules as `watchersToNotify`, expressed for one session: never tell a leased context about
|
|
42
|
+
* itself, and never tell somebody watching app A about a lease against app B.
|
|
43
|
+
*/
|
|
44
|
+
export function shouldGreetWithLeaseNotice(session, leasedSessionIds, leasedProjectIds) {
|
|
45
|
+
if (leasedSessionIds.includes(session.id))
|
|
46
|
+
return false;
|
|
47
|
+
if (0 === leasedSessionIds.length)
|
|
48
|
+
return false;
|
|
49
|
+
return leasedProjectIds.some((projectId) => projectId === undefined || session.projectId === undefined || session.projectId === projectId);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Which connected sessions belong to a human who should be told.
|
|
53
|
+
*
|
|
54
|
+
* Two exclusions, both deliberate:
|
|
55
|
+
*
|
|
56
|
+
* - Leased sessions. They ARE the agent's context; narrating "an agent is driving elsewhere" into
|
|
57
|
+
* the tab doing the driving is noise, and with several leases open they would talk about each
|
|
58
|
+
* other. Membership comes from the pool rather than an id prefix, because a naming convention is
|
|
59
|
+
* not a fact and `pool.leasedSessionIds()` is.
|
|
60
|
+
*
|
|
61
|
+
* - Other projects. One daemon serves many, and somebody watching app A does not need to hear about
|
|
62
|
+
* a lease taken against app B. A session that declares no project is NOT excluded: an older SDK
|
|
63
|
+
* sends no projectId, and silently dropping those would reintroduce the dark HUD for exactly the
|
|
64
|
+
* people least likely to work out why.
|
|
65
|
+
*
|
|
66
|
+
* Pure: no IO, no clock, no session objects. The caller does the posting.
|
|
67
|
+
*/
|
|
68
|
+
export function watchersToNotify(sessions, leasedSessionIds, projectId) {
|
|
69
|
+
const leased = new Set(leasedSessionIds);
|
|
70
|
+
return sessions
|
|
71
|
+
.filter((session) => !leased.has(session.id))
|
|
72
|
+
.filter((session) => projectId === undefined ||
|
|
73
|
+
session.projectId === undefined ||
|
|
74
|
+
session.projectId === projectId)
|
|
75
|
+
.map((session) => session.id);
|
|
76
|
+
}
|
|
@@ -3,12 +3,7 @@ import { noteEmptyRead } from '../tools/observed-nothing.js';
|
|
|
3
3
|
import { AGENT_ASK_NOTICE, AGENT_WAITING_NOTICE, PresenterTone, SessionState, YIELD_WITHOUT_SESSION_NOTE, } from '@reticlehq/core';
|
|
4
4
|
import { ReticleTool } from '../tools/tool-names.js';
|
|
5
5
|
import { sessionIdShape } from '../tools/tool-kit.js';
|
|
6
|
-
import {
|
|
7
|
-
import { waitForReady, RETICLE_LOOP_GUIDE } from './session-readiness.js';
|
|
8
|
-
import { recoveryFor } from '../tools/error-recovery.js';
|
|
9
|
-
/** Default + ceiling for the readiness wait — keep it short so a truly-missing app fails fast. */
|
|
10
|
-
const WAIT_READY_DEFAULT_MS = 5000;
|
|
11
|
-
const WAIT_READY_MAX_MS = 30000;
|
|
6
|
+
import { asString } from '../tools/tools-helpers.js';
|
|
12
7
|
/**
|
|
13
8
|
* Is this a turn ending with nothing attached, rather than a call about a specific tab?
|
|
14
9
|
*
|
|
@@ -225,42 +220,6 @@ export const LIVE_CONTROL_TOOLS = [
|
|
|
225
220
|
return Promise.resolve(out);
|
|
226
221
|
},
|
|
227
222
|
},
|
|
228
|
-
{
|
|
229
|
-
name: ReticleTool.WAIT_READY,
|
|
230
|
-
description: 'Block until the app is connected, then continue — call this once right after init so your ' +
|
|
231
|
-
'first real tool call does not lose the race with the SDK connecting its WebSocket. Returns as ' +
|
|
232
|
-
'soon as a session exists (no latency if one already does), or after `timeoutMs` with a ' +
|
|
233
|
-
'recovery hint if none appears.',
|
|
234
|
-
inputSchema: {
|
|
235
|
-
timeoutMs: z
|
|
236
|
-
.number()
|
|
237
|
-
.optional()
|
|
238
|
-
.describe('How long to wait for a session (ms). Default 5000, max 30000.'),
|
|
239
|
-
},
|
|
240
|
-
outputSchema: {
|
|
241
|
-
ready: z.boolean(),
|
|
242
|
-
sessionCount: z.number(),
|
|
243
|
-
loop: z.string().optional(),
|
|
244
|
-
recovery: z.string().optional(),
|
|
245
|
-
},
|
|
246
|
-
handler: async (deps, args) => {
|
|
247
|
-
const requested = asNumber(args['timeoutMs']) ?? WAIT_READY_DEFAULT_MS;
|
|
248
|
-
const timeoutMs = Math.max(0, Math.min(requested, WAIT_READY_MAX_MS));
|
|
249
|
-
const ready = await waitForReady({
|
|
250
|
-
count: () => deps.sessions.count(),
|
|
251
|
-
timeoutMs,
|
|
252
|
-
now: deps.now,
|
|
253
|
-
sleep: (ms) => new Promise((res) => setTimeout(res, ms)),
|
|
254
|
-
});
|
|
255
|
-
// The first response a fresh agent gets — carry the loop guide so it learns how to drive Reticle.
|
|
256
|
-
if (ready)
|
|
257
|
-
return { ready: true, sessionCount: deps.sessions.count(), loop: RETICLE_LOOP_GUIDE };
|
|
258
|
-
const recovery = recoveryFor('no browser session connected');
|
|
259
|
-
return recovery !== undefined
|
|
260
|
-
? { ready: false, sessionCount: 0, recovery }
|
|
261
|
-
: { ready: false, sessionCount: 0 };
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
223
|
];
|
|
265
224
|
/**
|
|
266
225
|
* The single actionable next-move for a pending mark: point the agent at the source (or the element
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* Pure: everything it needs is passed in, so the probe that discovers listening ports stays out of
|
|
19
19
|
* the hot resolve() path and this stays unit-testable.
|
|
20
20
|
*/
|
|
21
|
+
import { NoSessionReason } from '@reticlehq/core';
|
|
21
22
|
import { type LeaseBrowserState } from './lease-availability.js';
|
|
22
23
|
export interface NoSessionFacts {
|
|
23
24
|
/** Whether ANY session has connected to this daemon since it booted. */
|
|
@@ -117,6 +118,16 @@ export interface NoSessionFacts {
|
|
|
117
118
|
* install never finished" to the user-facing diagnosis rather than only to telemetry.
|
|
118
119
|
*/
|
|
119
120
|
daemonUpMs?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Well-known Reticle ports other than `port` that currently accept a connection.
|
|
123
|
+
*
|
|
124
|
+
* Remaining half of #261. We cannot see an SDK dialling a port we are not on, but we can see that
|
|
125
|
+
* a well-known Reticle port has a listener. That is an observation: somebody else's daemon, another
|
|
126
|
+
* project, and an unrelated process all look the same from here. The sentence that reports it
|
|
127
|
+
* names the ports and refuses the conclusion. Absent or empty means nothing else was listening,
|
|
128
|
+
* which is the common case and must not gain a paragraph.
|
|
129
|
+
*/
|
|
130
|
+
siblingListeners?: readonly number[];
|
|
120
131
|
}
|
|
121
132
|
/**
|
|
122
133
|
* Every branch ends with this, and `recoveryFor` keys on it to suppress the generic no-session
|
|
@@ -124,4 +135,26 @@ export interface NoSessionFacts {
|
|
|
124
135
|
* generic one that contradicts it.
|
|
125
136
|
*/
|
|
126
137
|
export declare const SELF_RECOVERING_MARKER = "Then call reticle_sessions again \u2014 it will appear within a second of the page loading.";
|
|
138
|
+
/**
|
|
139
|
+
* The diagnosis, and the CODE for the branch that produced it.
|
|
140
|
+
*
|
|
141
|
+
* The prose has always been well-ranked and it was the only output, so the population that installs
|
|
142
|
+
* Reticle and never gets an app connected arrived as one undifferentiated silence. "Restarted the
|
|
143
|
+
* dev server and it still did not connect" and "never started the app" need opposite fixes and were
|
|
144
|
+
* the same absence (#615).
|
|
145
|
+
*
|
|
146
|
+
* One function returning both, rather than a classifier beside the writer. A reason computed
|
|
147
|
+
* separately would drift from the sentence the user is actually shown, and then the metric would
|
|
148
|
+
* describe a diagnosis nobody received.
|
|
149
|
+
*/
|
|
150
|
+
export declare function explainNoSession(facts: NoSessionFacts): {
|
|
151
|
+
reason: NoSessionReason;
|
|
152
|
+
message: string;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* The message alone, for the callers that render it.
|
|
156
|
+
*
|
|
157
|
+
* Kept as the narrow surface it always was: most callers want the sentence, and handing them the
|
|
158
|
+
* pair would spread a telemetry concern across every error path that shows a user this text.
|
|
159
|
+
*/
|
|
127
160
|
export declare function diagnoseNoSession(facts: NoSessionFacts): string;
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
* Pure: everything it needs is passed in, so the probe that discovers listening ports stays out of
|
|
19
19
|
* the hot resolve() path and this stays unit-testable.
|
|
20
20
|
*/
|
|
21
|
+
import { NoSessionReason } from '@reticlehq/core';
|
|
21
22
|
import { leaseCaveat } from './lease-availability.js';
|
|
22
23
|
import { DEV_SERVER_PORTS } from '../cli/cli-port.js';
|
|
24
|
+
import { siblingListenerNote } from '../cli/sibling-ports.js';
|
|
23
25
|
import { STALL_AFTER_MS } from './stall-clock.js';
|
|
24
26
|
/** The one framework whose most likely cause differs from every other framework's. */
|
|
25
27
|
const NUXT = 'nuxt';
|
|
@@ -217,6 +219,24 @@ function portMismatchClause(facts) {
|
|
|
217
219
|
` Either restart the daemon on ${String(configured)}, or update \`.reticle.json\` to` +
|
|
218
220
|
` ${String(facts.port)} and restart the dev server so the app picks it up.`);
|
|
219
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* A listener on a well-known Reticle port we did not bind. Observation, not a cause.
|
|
224
|
+
*
|
|
225
|
+
* Leading space so the caller can concatenate it unconditionally. Empty when there is nothing to
|
|
226
|
+
* say, including when the occupied sibling is already the configured-port mismatch — that sentence
|
|
227
|
+
* already named both numbers, and repeating 4460 as a second "cause" would overclaim.
|
|
228
|
+
*/
|
|
229
|
+
function siblingListenerClause(facts) {
|
|
230
|
+
const occupied = facts.siblingListeners ?? [];
|
|
231
|
+
if (0 === occupied.length)
|
|
232
|
+
return '';
|
|
233
|
+
const mismatch = facts.projectPort;
|
|
234
|
+
const extra = mismatch !== undefined && mismatch !== facts.port
|
|
235
|
+
? occupied.filter((port) => port !== mismatch)
|
|
236
|
+
: occupied;
|
|
237
|
+
const note = siblingListenerNote(facts.port, extra);
|
|
238
|
+
return note === undefined ? '' : ` ${note}`;
|
|
239
|
+
}
|
|
220
240
|
/**
|
|
221
241
|
* The tail every "the app is wired and silent" branch ends with, ranked.
|
|
222
242
|
*
|
|
@@ -228,7 +248,7 @@ function rankedCauses(facts) {
|
|
|
228
248
|
const nuxt = facts.framework?.toLowerCase() === NUXT ? `${NUXT_STALE_BUNDLE} ` : '';
|
|
229
249
|
return (`${nuxt}${REAL_CAUSES} If none of those, the app may be dialling a different daemon than this ` +
|
|
230
250
|
`one (on ${String(facts.port)}): check the app's reticle port matches ` +
|
|
231
|
-
`${String(facts.port)}.${portMismatchClause(facts)} ${NON_LOCALHOST_GATE}`);
|
|
251
|
+
`${String(facts.port)}.${portMismatchClause(facts)}${siblingListenerClause(facts)} ${NON_LOCALHOST_GATE}`);
|
|
232
252
|
}
|
|
233
253
|
/**
|
|
234
254
|
* The scope answer, when the config was somewhere else all along.
|
|
@@ -281,7 +301,30 @@ function stallClause(facts) {
|
|
|
281
301
|
'The most likely cause is that the dev server was not restarted after adding the Reticle ' +
|
|
282
302
|
'plugin, so the running bundle carries no SDK. Restart the dev server first. ');
|
|
283
303
|
}
|
|
284
|
-
|
|
304
|
+
function alreadyListeningClause(listening) {
|
|
305
|
+
if (0 === listening.length)
|
|
306
|
+
return '';
|
|
307
|
+
if (1 === listening.length) {
|
|
308
|
+
const port = listening[0];
|
|
309
|
+
return (` An app is already listening on ${String(port)}; just open http://localhost:${String(port)} ` +
|
|
310
|
+
'— do not start a second stack.');
|
|
311
|
+
}
|
|
312
|
+
return (` An app is already listening on ${listening.join(', ')}; just open one of those URLs — do not ` +
|
|
313
|
+
'start a second stack.');
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* The diagnosis, and the CODE for the branch that produced it.
|
|
317
|
+
*
|
|
318
|
+
* The prose has always been well-ranked and it was the only output, so the population that installs
|
|
319
|
+
* Reticle and never gets an app connected arrived as one undifferentiated silence. "Restarted the
|
|
320
|
+
* dev server and it still did not connect" and "never started the app" need opposite fixes and were
|
|
321
|
+
* the same absence (#615).
|
|
322
|
+
*
|
|
323
|
+
* One function returning both, rather than a classifier beside the writer. A reason computed
|
|
324
|
+
* separately would drift from the sentence the user is actually shown, and then the metric would
|
|
325
|
+
* describe a diagnosis nobody received.
|
|
326
|
+
*/
|
|
327
|
+
export function explainNoSession(facts) {
|
|
285
328
|
const { everConnected, initialized, listening, port } = facts;
|
|
286
329
|
// Named when known: a claim about a missing file is a claim about ONE directory.
|
|
287
330
|
const where = facts.directory === undefined
|
|
@@ -292,19 +335,23 @@ export function diagnoseNoSession(facts) {
|
|
|
292
335
|
// from the field (#157): an aged-out lease produced "the tab was closed … ask the human to
|
|
293
336
|
// reopen the app", which is wrong on every clause — there is no human tab, and the recovery it
|
|
294
337
|
// names is unavailable to the caller while the one that works goes unmentioned. The reporter
|
|
295
|
-
// went looking for a port mismatch.
|
|
296
|
-
//
|
|
338
|
+
// went looking for a port mismatch.
|
|
339
|
+
//
|
|
340
|
+
// The hedge that used to close this branch ("if you were driving a human tab instead…") is
|
|
341
|
+
// gone with the reason for it. `leaseExpired` was a lifetime count of reaps, so it could not
|
|
342
|
+
// tell which session went and had to cover both; it now names the session that actually
|
|
343
|
+
// departed (#611), so this branch is only reached when the thing that vanished WAS the lease.
|
|
297
344
|
if (true === facts.leaseExpired) {
|
|
298
|
-
return ('no browser session connected, but one WAS connected to this daemon earlier, so the wiring ' +
|
|
299
|
-
'is correct.
|
|
300
|
-
'
|
|
301
|
-
'
|
|
302
|
-
|
|
303
|
-
`instead, it went away — reopen it or run ${OPEN_CMD_BARE}. ${RETRY}`);
|
|
345
|
+
return reason(NoSessionReason.LEASE_EXPIRED, 'no browser session connected, but one WAS connected to this daemon earlier, so the wiring ' +
|
|
346
|
+
'is correct. The session that went away was a pooled lease and it aged out; a lease is a ' +
|
|
347
|
+
'headless context, not a human tab, and it takes its cookies with it (so an authenticated ' +
|
|
348
|
+
'app needs signing in again). Re-acquire with reticle_lease {action:"acquire", url} and ' +
|
|
349
|
+
`carry on.${alreadyListeningClause(listening)} ${RETRY}`);
|
|
304
350
|
}
|
|
305
|
-
return ('no browser session connected, but one WAS connected to this daemon earlier, so the wiring ' +
|
|
351
|
+
return reason(NoSessionReason.TAB_GONE, 'no browser session connected, but one WAS connected to this daemon earlier, so the wiring ' +
|
|
306
352
|
'is correct. The tab was closed, navigated away, or hard-reloaded. Ask the human to reopen ' +
|
|
307
|
-
`the app (or run ${OPEN_CMD_BARE}), or reload the tab
|
|
353
|
+
`the app (or run ${OPEN_CMD_BARE}), or reload the tab.${alreadyListeningClause(listening)} ` +
|
|
354
|
+
`${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`);
|
|
308
355
|
}
|
|
309
356
|
// Ranked ahead of every "we have never seen one" branch below, because it is the one fact that
|
|
310
357
|
// OUTRANKS them: those branches all reason from an absence, and this reasons from a recorded
|
|
@@ -315,13 +362,13 @@ export function diagnoseNoSession(facts) {
|
|
|
315
362
|
? `${unattributedListeners(listening)} If the dev server is not running, start it first ` +
|
|
316
363
|
'(the command is in `next_action`).'
|
|
317
364
|
: unattributedListeners(listening);
|
|
318
|
-
return `${RESTARTED_LEAD} ${OPEN_THE_APP} ${listeners} ${rankedCauses(facts)} ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}
|
|
365
|
+
return reason(NoSessionReason.APP_NOT_REOPENED, `${RESTARTED_LEAD} ${OPEN_THE_APP} ${listeners} ${rankedCauses(facts)} ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`);
|
|
319
366
|
}
|
|
320
367
|
// A config found elsewhere outranks every "you may have no SDK" branch below: those reason from
|
|
321
368
|
// an absence in ONE directory, and this is a file we read in another. Ranked here so the reader is
|
|
322
369
|
// never sent through an install on a project that has demonstrably been installed.
|
|
323
370
|
if (!initialized && (facts.configsElsewhere ?? []).length > 0) {
|
|
324
|
-
return ('no browser session connected, and this daemon has never seen one. ' +
|
|
371
|
+
return reason(NoSessionReason.CONFIG_ELSEWHERE, 'no browser session connected, and this daemon has never seen one. ' +
|
|
325
372
|
`${configsElsewhereClause(facts)} ${unattributedListeners(listening)} ${OPEN_THE_APP} ` +
|
|
326
373
|
`${rankedCauses(facts)} ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`);
|
|
327
374
|
}
|
|
@@ -341,7 +388,7 @@ export function diagnoseNoSession(facts) {
|
|
|
341
388
|
// So: report what was actually checked, name the directory, and name the case where the absence
|
|
342
389
|
// is expected rather than diagnostic.
|
|
343
390
|
if (!initialized) {
|
|
344
|
-
return ('no browser session connected. Two things to weigh, and neither of them is proof. ' +
|
|
391
|
+
return reason(NoSessionReason.NO_LISTENER_NO_CONFIG, 'no browser session connected. Two things to weigh, and neither of them is proof. ' +
|
|
345
392
|
`(1) ${slowListenerClause(facts)}Nothing is listening on the ports Reticle scans (${SCANNED_PORTS}), so the dev server ` +
|
|
346
393
|
'may not be running — START IT YOURSELF, in the background, using the command in ' +
|
|
347
394
|
"`next_action` (it is read from this project's own scripts; if there is none, that field " +
|
|
@@ -353,9 +400,9 @@ export function diagnoseNoSession(facts) {
|
|
|
353
400
|
`file ${INIT_CMD} writes, so the app may carry no Reticle SDK — but check the app's ` +
|
|
354
401
|
'OWN directory before re-running `init`: in a monorepo the daemon often runs at the root ' +
|
|
355
402
|
'while the app lives in a subdirectory, and an app wired by the Vite or Babel plugin ' +
|
|
356
|
-
`carries the SDK without that file at all.${searchedClause(facts)} ${leaseAdvice(URL_THEN_LEASE, facts)} ${RETRY}`);
|
|
403
|
+
`carries the SDK without that file at all.${searchedClause(facts)}${siblingListenerClause(facts)} ${leaseAdvice(URL_THEN_LEASE, facts)} ${RETRY}`);
|
|
357
404
|
}
|
|
358
|
-
return (`${stallClause(facts)}no browser session connected, and this daemon has never seen one. ${OPEN_THE_APP} ` +
|
|
405
|
+
return reason(NoSessionReason.NO_LISTENER, `${stallClause(facts)}no browser session connected, and this daemon has never seen one. ${OPEN_THE_APP} ` +
|
|
359
406
|
'Nothing is listening on the ports Reticle scans ' +
|
|
360
407
|
`(${SCANNED_PORTS}) either, and the most common reason for that is a dev server that is not ` +
|
|
361
408
|
'running: start it yourself in the background with the command in `next_action` — it is read ' +
|
|
@@ -371,23 +418,34 @@ export function diagnoseNoSession(facts) {
|
|
|
371
418
|
// Deliberately NOT offering reticle_lease here, and a test pins that: a lease opens a URL, and
|
|
372
419
|
// if nothing is listening there is nothing at any URL to open. Asking for the real one is the
|
|
373
420
|
// only move that can recover the :7699 case.
|
|
374
|
-
`the app IS running, ask the human for its URL rather than assuming it is down. ${leaseAdvice(URL_THEN_LEASE, facts)}
|
|
375
|
-
|
|
376
|
-
// answered above, leading with that certainty instead of behind this scan's guess.
|
|
377
|
-
`${RETRY}`);
|
|
421
|
+
`the app IS running, ask the human for its URL rather than assuming it is down. ${leaseAdvice(URL_THEN_LEASE, facts)}` +
|
|
422
|
+
`${siblingListenerClause(facts)} ${RETRY}`);
|
|
378
423
|
}
|
|
379
424
|
if (!initialized) {
|
|
380
|
-
return ('no browser session connected, and this daemon has never seen one. ' +
|
|
425
|
+
return reason(NoSessionReason.NO_CONFIG, 'no browser session connected, and this daemon has never seen one. ' +
|
|
381
426
|
`What was actually checked: there is no \`.reticle.json\` in ${where}. That is the file ` +
|
|
382
427
|
`${INIT_CMD} writes, so the app may carry no Reticle SDK — but it is not proof, and the ` +
|
|
383
428
|
'same absence is expected in a monorepo whose daemon runs at the root while the app lives in ' +
|
|
384
429
|
"a subdirectory, or in an app wired by the Vite or Babel plugin. Check the app's OWN " +
|
|
385
430
|
`directory: if it has no config, run ${INIT_CMD} there and restart the dev server; if it ` +
|
|
386
431
|
`has one, the app is wired and simply has no page open — ${OPEN_CMD}. ` +
|
|
387
|
-
`${unattributedListeners(listening)}${searchedClause(facts)} ${RETRY}`);
|
|
432
|
+
`${unattributedListeners(listening)}${searchedClause(facts)}${siblingListenerClause(facts)} ${RETRY}`);
|
|
388
433
|
}
|
|
389
|
-
return (`${stallClause(facts)}no browser session connected, and this daemon has never seen one for this project, which is ` +
|
|
434
|
+
return reason(NoSessionReason.SDK_NOT_REACHING_DAEMON, `${stallClause(facts)}no browser session connected, and this daemon has never seen one for this project, which is ` +
|
|
390
435
|
`wired for Reticle. ${OPEN_THE_APP} ${unattributedListeners(listening)} ` +
|
|
391
436
|
'If the page IS open and still does not appear, the app is wired and the SDK is not reaching ' +
|
|
392
437
|
`this daemon (on ${String(port)}). ${rankedCauses(facts)} ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`);
|
|
393
438
|
}
|
|
439
|
+
/** Pair a branch's verdict with its prose, so neither can be produced without the other. */
|
|
440
|
+
function reason(code, message) {
|
|
441
|
+
return { reason: code, message };
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* The message alone, for the callers that render it.
|
|
445
|
+
*
|
|
446
|
+
* Kept as the narrow surface it always was: most callers want the sentence, and handing them the
|
|
447
|
+
* pair would spread a telemetry concern across every error path that shows a user this text.
|
|
448
|
+
*/
|
|
449
|
+
export function diagnoseNoSession(facts) {
|
|
450
|
+
return explainNoSession(facts).message;
|
|
451
|
+
}
|
|
@@ -29,6 +29,18 @@ interface NextActionFacts {
|
|
|
29
29
|
initialized: boolean;
|
|
30
30
|
listening: readonly number[];
|
|
31
31
|
dev: DevCommand | undefined;
|
|
32
|
+
/** Configs found in other workspace directories: positive evidence of a scope mismatch. */
|
|
33
|
+
configsElsewhere?: readonly {
|
|
34
|
+
directory: string;
|
|
35
|
+
projectId?: string;
|
|
36
|
+
}[];
|
|
37
|
+
/**
|
|
38
|
+
* The sentence for a live daemon elsewhere that this project's app is actually connected to.
|
|
39
|
+
*
|
|
40
|
+
* Passed rendered rather than as ports, because the rule that produces it lives in the module that
|
|
41
|
+
* owns "which daemon is whose" and this file must stay pure. Absent when there is no split.
|
|
42
|
+
*/
|
|
43
|
+
splitBrain?: string;
|
|
32
44
|
/**
|
|
33
45
|
* An app for this project has connected on this port before, from durable state.
|
|
34
46
|
*
|
|
@@ -18,12 +18,52 @@ const INIT_COMMAND = 'reticle init';
|
|
|
18
18
|
const OPEN_COMMAND = 'reticle open';
|
|
19
19
|
const LOCALHOST = 'http://localhost';
|
|
20
20
|
export function nextActionFor(facts) {
|
|
21
|
+
// FIRST, above every other cause, because it is the only one supported by positive evidence about
|
|
22
|
+
// a daemon rather than by an absence. This project's own connection record names a live daemon
|
|
23
|
+
// that is not this one, which means the app is up and instrumented and the agent is simply looking
|
|
24
|
+
// at the wrong process. Every branch below would send it to fix something that is not broken —
|
|
25
|
+
// start a dev server that is running, open a page that is open, or re-run `init` over a config
|
|
26
|
+
// that works — and the agent has no way to suspect the real cause from here.
|
|
27
|
+
const splitBrain = facts.splitBrain;
|
|
28
|
+
if (splitBrain !== undefined) {
|
|
29
|
+
return { action: NoSessionAction.DAEMON_SPLIT, reason: splitBrain };
|
|
30
|
+
}
|
|
21
31
|
if (facts.everConnected) {
|
|
32
|
+
const listening = facts.listening;
|
|
33
|
+
const only = 1 === listening.length ? listening[0] : undefined;
|
|
34
|
+
const bound = 0 === listening.length
|
|
35
|
+
? ''
|
|
36
|
+
: only === undefined
|
|
37
|
+
? ` An app is already listening on ${listening.join(', ')}; just open the URL the human names — do not start a second stack.`
|
|
38
|
+
: ` An app is already listening on ${String(only)}; just open ${LOCALHOST}:${String(only)} — do not start a second stack.`;
|
|
22
39
|
return {
|
|
23
40
|
action: NoSessionAction.REOPEN_APP,
|
|
41
|
+
...(only === undefined
|
|
42
|
+
? {}
|
|
43
|
+
: { command: `${OPEN_COMMAND} ${LOCALHOST}:${String(only)}`, port: only }),
|
|
24
44
|
reason: 'a session was connected to this daemon earlier, so the wiring is correct — the tab was ' +
|
|
25
45
|
'closed, reloaded, or the lease aged out. Reopen the app, or take one you own with ' +
|
|
26
|
-
'reticle_lease {action:"acquire", url}.'
|
|
46
|
+
'reticle_lease {action:"acquire", url}.' +
|
|
47
|
+
bound,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const configsElsewhere = facts.configsElsewhere ?? [];
|
|
51
|
+
if (!facts.initialized && configsElsewhere.length > 0) {
|
|
52
|
+
const named = configsElsewhere
|
|
53
|
+
.map((config) => config.projectId === undefined
|
|
54
|
+
? config.directory
|
|
55
|
+
: `${config.directory} ('${config.projectId}')`)
|
|
56
|
+
.join(', ');
|
|
57
|
+
const projectIds = configsElsewhere.flatMap((config) => config.projectId === undefined ? [] : [config.projectId]);
|
|
58
|
+
const leaseAlternative = 0 === projectIds.length
|
|
59
|
+
? ''
|
|
60
|
+
: ' Alternatively, acquire the app URL explicitly with reticle_lease ' +
|
|
61
|
+
'{action:"acquire", url, projectId} using the matching projectId above.';
|
|
62
|
+
return {
|
|
63
|
+
action: NoSessionAction.OPEN_APP,
|
|
64
|
+
reason: `a \`.reticle.json\` was found outside this daemon's directory: ${named}. This is a ` +
|
|
65
|
+
"scope problem, not an install problem. Restart the daemon from the app's directory (or " +
|
|
66
|
+
`point it there), then open the app.${leaseAlternative}`,
|
|
27
67
|
};
|
|
28
68
|
}
|
|
29
69
|
if (0 === facts.listening.length) {
|
|
@@ -41,9 +81,22 @@ export function nextActionFor(facts) {
|
|
|
41
81
|
action: NoSessionAction.START_DEV_SERVER,
|
|
42
82
|
command: dev.command,
|
|
43
83
|
...(dev.port === undefined ? {} : { port: dev.port }),
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
84
|
+
// "Probably", to match the branch above it and the paragraph beside it.
|
|
85
|
+
//
|
|
86
|
+
// The same fact was stated at two confidence levels by one function: no dev script said the
|
|
87
|
+
// app was "probably not running", and THIS branch — the one that hands over a command to run
|
|
88
|
+
// — asserted it flatly. That is the branch where being wrong costs something, because the
|
|
89
|
+
// agent then starts a SECOND dev server on a second port, which the guard below calls the
|
|
90
|
+
// exact confusion this probe exists to prevent.
|
|
91
|
+
//
|
|
92
|
+
// It also contradicted its own payload: the prose beside it says the scan is narrow, that a
|
|
93
|
+
// server on any other port is invisible to it, and that a running app should be checked for
|
|
94
|
+
// rather than assumed away. Measured on a machine with three dev servers up on ports the scan
|
|
95
|
+
// does not cover, it reported the app was not running.
|
|
96
|
+
reason: 'nothing is listening on the ports Reticle scans, so the app is probably not running — ' +
|
|
97
|
+
'though that scan is narrow, so if it IS up on another port, ask for its URL instead of ' +
|
|
98
|
+
`starting a second one. This is the project's own \`${dev.script}\` script — run it in the ` +
|
|
99
|
+
'background, tell the human it is running, then call reticle_sessions again.',
|
|
47
100
|
};
|
|
48
101
|
}
|
|
49
102
|
// Something IS up. Whatever else is true, do NOT hand back a start command: a second dev server on
|
|
@@ -68,12 +121,30 @@ export function nextActionFor(facts) {
|
|
|
68
121
|
`\`${OPEN_COMMAND} <url>\`.`,
|
|
69
122
|
};
|
|
70
123
|
}
|
|
124
|
+
// Two causes sit under this branch and only one of them is fixed by opening a page.
|
|
125
|
+
//
|
|
126
|
+
// The prose diagnosis next door already ranks them and puts the stale bundle first: `init` writes
|
|
127
|
+
// the plugin into a config the RUNNING dev server has already read, so the served bundle carries
|
|
128
|
+
// no SDK and reloading it cannot ever produce a session. This function — the half an agent
|
|
129
|
+
// actually executes — used to say only "open the app", which is the answer to the OTHER cause.
|
|
130
|
+
// An agent that follows it opens a bundle with no Reticle in it, sees no session, and has been
|
|
131
|
+
// given no reason to suspect the one thing that would have worked.
|
|
132
|
+
//
|
|
133
|
+
// Named only while nothing has EVER connected. Once a session has been here the wiring is proven
|
|
134
|
+
// and sending an agent to restart a working dev server is a wild goose chase.
|
|
135
|
+
const neverConnected = true !== facts.previouslyConnected;
|
|
71
136
|
return {
|
|
72
137
|
action: NoSessionAction.OPEN_APP,
|
|
73
138
|
command: `${OPEN_COMMAND} ${LOCALHOST}:${String(only)}`,
|
|
74
139
|
port: only,
|
|
75
|
-
reason:
|
|
76
|
-
'
|
|
140
|
+
reason: neverConnected
|
|
141
|
+
? 'this project is wired and a dev server is listening, but no app has ever connected to ' +
|
|
142
|
+
'this daemon — Reticle only ever sees a page a browser has LOADED, and nothing has loaded ' +
|
|
143
|
+
'one. If opening it does not produce a session, the dev server is older than the Reticle ' +
|
|
144
|
+
'plugin and its bundle carries no SDK: restart the dev server (do not rely on HMR) and ' +
|
|
145
|
+
'load the page again.'
|
|
146
|
+
: 'this project is wired and a dev server is listening — Reticle only ever sees a page a ' +
|
|
147
|
+
'browser has LOADED, and nothing has loaded one.',
|
|
77
148
|
};
|
|
78
149
|
}
|
|
79
150
|
/** The same action as one sentence, for the prose message the human reads. */
|
|
@@ -19,11 +19,23 @@ interface NoSessionWatchOptions {
|
|
|
19
19
|
directory?: string;
|
|
20
20
|
probe?: () => Promise<number[]>;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* Well-known Reticle ports other than ours that currently accept a connection.
|
|
23
|
+
*
|
|
24
|
+
* Remaining half of #261. Injected so tests can pin the observation without opening a socket.
|
|
25
|
+
* Production (no `probe` override) probes those ports itself. A test that injects `probe` and
|
|
26
|
+
* omits this is opting out: it owns the scan.
|
|
27
|
+
*/
|
|
28
|
+
occupiedSiblings?: () => Promise<readonly number[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Whether a given session id belonged to a pooled lease that aged out, if a pool exists.
|
|
31
|
+
* Injected as a predicate rather than the pool itself: the diagnosis needs one answer, and
|
|
32
|
+
* taking the whole pool would tie the session layer to the browser layer for it.
|
|
33
|
+
*
|
|
34
|
+
* Was a lifetime `reapedLeases: () => number` count, asked as `> 0`. That latched: after the
|
|
35
|
+
* first reap, every closed human tab was reported as an expired lease for the rest of the
|
|
36
|
+
* daemon's life, and the recovery it named would have thrown away the app session (#611).
|
|
25
37
|
*/
|
|
26
|
-
|
|
38
|
+
wasReapedLease?: (sessionId: string) => boolean;
|
|
27
39
|
/**
|
|
28
40
|
* Opens a URL in a browser Reticle owns, and resolves once it is open.
|
|
29
41
|
*
|
|
@@ -53,8 +65,8 @@ export declare function startNoSessionWatch(options: NoSessionWatchOptions): ()
|
|
|
53
65
|
* adjacent blocks in the bootstrap.
|
|
54
66
|
*/
|
|
55
67
|
export declare function wireSessionScope(sessions: SessionManager, activeProjectId: string | undefined, port: number,
|
|
56
|
-
/**
|
|
57
|
-
|
|
68
|
+
/** Asks the pool whether a session id was a lease it aged out; omitted when there is no pool. */
|
|
69
|
+
wasReapedLease?: (sessionId: string) => boolean,
|
|
58
70
|
/** Opens a URL in a Reticle-owned browser (the pool). Omitted ⇒ auto-attach is off. */
|
|
59
71
|
attach?: (url: string) => Promise<unknown>): () => void;
|
|
60
72
|
export {};
|