@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
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
* holds to. Detection is entirely best-effort: an unreadable package.json or an SDK too old to report
|
|
10
10
|
* its runtime yields `undefined`, never a throw and never a guess.
|
|
11
11
|
*/
|
|
12
|
+
import { mcpClientIdentity, setMcpClientIdentityHook } from '../mcp/client-identity.js';
|
|
12
13
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
13
14
|
import { isAbsolute, join } from 'node:path';
|
|
14
|
-
import { AppRuntime, McpScope, PageDriver } from '@reticlehq/core';
|
|
15
|
+
import { AppRuntime, McpScope, PageDriver, StackUnknownReason, } from '@reticlehq/core';
|
|
15
16
|
import { parseMajor } from '../init/detect.js';
|
|
16
|
-
import { findWorkspaceApps } from '../init/
|
|
17
|
+
import { findWorkspaceApps } from '../init/workspace-apps.js';
|
|
17
18
|
/**
|
|
18
19
|
* Dependency → reported stack, in resolution order. Meta-frameworks come FIRST: a Next app also
|
|
19
20
|
* depends on react, and reporting `react` for it would collapse the single most important segment
|
|
@@ -40,10 +41,14 @@ const PACKAGE_JSON = 'package.json';
|
|
|
40
41
|
/** A project-scoped MCP registration lives in one of these; `reticle init` writes user scope instead. */
|
|
41
42
|
const PROJECT_MCP_FILES = ['.mcp.json', join('.cursor', 'mcp.json'), join('.vscode', 'mcp.json')];
|
|
42
43
|
/**
|
|
43
|
-
* The project's framework and its MAJOR version
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* The project's framework and its MAJOR version. The major comes from `parseMajor` — the same range
|
|
45
|
+
* parser `reticle init` uses to decide React 19 source mapping — so a range like `^15.0.0-canary.3`
|
|
46
|
+
* narrows the same way in both places instead of via a second, subtly different reader.
|
|
47
|
+
*
|
|
48
|
+
* `no-manifest` covers absent AND unreadable: both mean this directory could not answer, which is
|
|
49
|
+
* the distinction that matters to the caller. Unreadable is not separated because a manifest we
|
|
50
|
+
* cannot parse is not evidence about the app, and a reason nobody can act on is noise in a closed
|
|
51
|
+
* vocabulary.
|
|
47
52
|
*/
|
|
48
53
|
function stackOfManifest(dir, read) {
|
|
49
54
|
let pkg;
|
|
@@ -51,7 +56,7 @@ function stackOfManifest(dir, read) {
|
|
|
51
56
|
pkg = JSON.parse(read(join(dir, PACKAGE_JSON)));
|
|
52
57
|
}
|
|
53
58
|
catch {
|
|
54
|
-
return {};
|
|
59
|
+
return { kind: 'no-manifest' };
|
|
55
60
|
}
|
|
56
61
|
const deps = { ...pkg.devDependencies, ...pkg.dependencies };
|
|
57
62
|
for (const [dep, stack] of STACK_BY_DEP) {
|
|
@@ -59,15 +64,17 @@ function stackOfManifest(dir, read) {
|
|
|
59
64
|
if (range === undefined)
|
|
60
65
|
continue;
|
|
61
66
|
const stackMajor = parseMajor(range);
|
|
62
|
-
return { stack, ...(stackMajor !== undefined ? { stackMajor } : {}) };
|
|
67
|
+
return { kind: 'found', stack, ...(stackMajor !== undefined ? { stackMajor } : {}) };
|
|
63
68
|
}
|
|
64
|
-
return {};
|
|
69
|
+
return { kind: 'unrecognised', declaresWorkspaces: pkg.workspaces !== undefined };
|
|
65
70
|
}
|
|
66
71
|
export function detectStack(cwd, read = readFile) {
|
|
67
72
|
// The cwd wins when it IS an app — never redirect away from the real answer.
|
|
68
73
|
const here = stackOfManifest(cwd, read);
|
|
69
|
-
if (here.
|
|
70
|
-
|
|
74
|
+
if ('found' === here.kind) {
|
|
75
|
+
const { stack, stackMajor } = here;
|
|
76
|
+
return { stack, ...(stackMajor !== undefined ? { stackMajor } : {}), stackSource: 'cwd' };
|
|
77
|
+
}
|
|
71
78
|
// Otherwise look for the app. The daemon's cwd is wherever the agent's client happened to launch,
|
|
72
79
|
// which for a real repo is the ROOT while the app sits in `frontend/`, `web/`, or a declared
|
|
73
80
|
// workspace. Reading one directory and giving up is why this returned nothing for every project
|
|
@@ -78,19 +85,53 @@ export function detectStack(cwd, read = readFile) {
|
|
|
78
85
|
// declared workspaces (pnpm-workspace.yaml, package.json `workspaces`) AND scans top-level
|
|
79
86
|
// directories, and it was widened once already for a repo shape a user hit twice. A second
|
|
80
87
|
// detector here would drift from it the first time either changed.
|
|
88
|
+
//
|
|
89
|
+
// Every exit below carries WHY, because an empty result is not a cause. Four different facts used
|
|
90
|
+
// to arrive as one `{}`: no app anywhere, an app we read and did not recognise, workspace apps
|
|
91
|
+
// that were all unrecognised, and discovery erroring. They have different fixes, and the bucket
|
|
92
|
+
// was unusable while they were indistinguishable (#617).
|
|
93
|
+
let sawWorkspaceApp = false;
|
|
81
94
|
try {
|
|
82
95
|
for (const app of findWorkspaceApps(profileIo(cwd))) {
|
|
83
96
|
const found = stackOfManifest(join(cwd, app), read);
|
|
84
97
|
// Reported as `workspace` so the share of projects needing discovery is measurable — that
|
|
85
98
|
// number is what says whether our inference needs an agent to correct it.
|
|
86
|
-
if (found
|
|
87
|
-
|
|
99
|
+
if ('found' === found.kind) {
|
|
100
|
+
const { stack, stackMajor } = found;
|
|
101
|
+
return {
|
|
102
|
+
stack,
|
|
103
|
+
...(stackMajor !== undefined ? { stackMajor } : {}),
|
|
104
|
+
stackSource: 'workspace',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// A manifest we READ and did not recognise is evidence about the app; a directory with no
|
|
108
|
+
// manifest is not an app at all and says nothing either way.
|
|
109
|
+
if ('unrecognised' === found.kind)
|
|
110
|
+
sawWorkspaceApp = true;
|
|
88
111
|
}
|
|
89
112
|
}
|
|
90
113
|
catch {
|
|
91
114
|
// Discovery touches the filesystem; a permission error must never cost us the profile event.
|
|
115
|
+
// Named rather than folded into "found nothing": an error is not an absence, and a detector
|
|
116
|
+
// that reports one as the other sends the reader looking for a project that was there.
|
|
117
|
+
return { stackUnknownReason: StackUnknownReason.DISCOVERY_FAILED };
|
|
118
|
+
}
|
|
119
|
+
if (sawWorkspaceApp) {
|
|
120
|
+
return { stackUnknownReason: StackUnknownReason.WORKSPACE_APPS_UNRECOGNISED };
|
|
92
121
|
}
|
|
93
|
-
|
|
122
|
+
// The cwd's own manifest is the stronger statement when it exists: we read this app and did not
|
|
123
|
+
// know it. Ranked below the workspace answer only because discovery may have found the REAL app.
|
|
124
|
+
if ('unrecognised' === here.kind) {
|
|
125
|
+
// A monorepo root naming no framework is not an unrecognised APP — the app is a directory down
|
|
126
|
+
// and discovery did not surface it. Reporting MANIFEST_UNRECOGNISED here would read as "add
|
|
127
|
+
// this framework to the table" about a manifest that describes no framework at all.
|
|
128
|
+
return {
|
|
129
|
+
stackUnknownReason: here.declaresWorkspaces
|
|
130
|
+
? StackUnknownReason.WORKSPACE_ROOT_NO_APPS
|
|
131
|
+
: StackUnknownReason.MANIFEST_UNRECOGNISED,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return { stackUnknownReason: StackUnknownReason.NO_APP_FOUND };
|
|
94
135
|
}
|
|
95
136
|
/**
|
|
96
137
|
* The three filesystem methods `findWorkspaceApps` needs, rooted at `cwd`.
|
|
@@ -150,26 +191,16 @@ function fileExists(path) {
|
|
|
150
191
|
* client's own claim about itself, while an env-var table is a guess that silently rots every time a
|
|
151
192
|
* vendor renames a variable. Unset (a CLI run with no MCP peer) simply reports no client.
|
|
152
193
|
*/
|
|
153
|
-
|
|
194
|
+
/** Kept as the telemetry-facing name; the mechanism itself lives in mcp/client-identity.ts. */
|
|
154
195
|
export function setMcpClientNameHook(hook) {
|
|
155
|
-
|
|
196
|
+
setMcpClientIdentityHook(hook);
|
|
156
197
|
}
|
|
157
|
-
/**
|
|
158
|
-
* The client's own name and version from its handshake.
|
|
159
|
-
*
|
|
160
|
-
* Note what is NOT here and cannot be: the MODEL. MCP's `clientInfo` carries a name, a title and a
|
|
161
|
-
* version and has no concept of a model, so the transport genuinely cannot tell us. The agent
|
|
162
|
-
* self-reports it on the feedback itself, which is the only mechanism available and a reliable one
|
|
163
|
-
* given the report is already something the agent authored.
|
|
164
|
-
*/
|
|
165
198
|
function detectClient() {
|
|
166
199
|
try {
|
|
167
|
-
const info =
|
|
168
|
-
const name = info?.name;
|
|
169
|
-
const version = info?.version;
|
|
200
|
+
const info = mcpClientIdentity();
|
|
170
201
|
return {
|
|
171
|
-
...(name
|
|
172
|
-
...(version
|
|
202
|
+
...(info.name === undefined ? {} : { client: info.name }),
|
|
203
|
+
...(info.version === undefined ? {} : { clientVersion: info.version }),
|
|
173
204
|
};
|
|
174
205
|
}
|
|
175
206
|
catch {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Feedback } from '@reticlehq/core';
|
|
2
|
+
export interface SavedFeedback {
|
|
3
|
+
path: string;
|
|
4
|
+
/** A ready-to-run command that turns the file into an issue. */
|
|
5
|
+
command: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Write the report under `<cwd>/.reticle/feedback/`. Returns undefined if it cannot be written —
|
|
9
|
+
* a feedback save that throws would take down the tool call that was reporting a problem, which is
|
|
10
|
+
* the one outcome worse than losing the report.
|
|
11
|
+
*/
|
|
12
|
+
export declare function saveFeedbackLocally(cwd: string, input: {
|
|
13
|
+
kind?: string;
|
|
14
|
+
source?: string;
|
|
15
|
+
}, text: string, context: Partial<Feedback>, now?: number): SavedFeedback | undefined;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a feedback report goes when it cannot be sent.
|
|
3
|
+
*
|
|
4
|
+
* The report is fully written and redacted by the time delivery is refused. Throwing it away
|
|
5
|
+
* punishes exactly the behaviour the channel exists to encourage — and it hits hardest where the
|
|
6
|
+
* reports are best, because a Reticle source checkout disables telemetry by cwd, so the contributor
|
|
7
|
+
* who can name the file and the line is the one person who cannot file at all.
|
|
8
|
+
*
|
|
9
|
+
* Writing it down costs nothing and loses nothing. The receipt then carries a path and a command,
|
|
10
|
+
* so the human is handed one thing to run instead of being asked to retype what an agent already
|
|
11
|
+
* wrote.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { ReticleDir } from '@reticlehq/core';
|
|
16
|
+
/** Reports live beside the rest of the workspace, not in a temp dir somebody has to be told about. */
|
|
17
|
+
const FEEDBACK_SUBDIR = 'feedback';
|
|
18
|
+
/** Filesystem-safe, sortable, and unique enough for one report per second per kind. */
|
|
19
|
+
const stamp = (now) => new Date(now).toISOString().replace(/[:.]/g, '-').replace('Z', '');
|
|
20
|
+
const asMarkdown = (input, text, context) => {
|
|
21
|
+
const title = `Reticle feedback: ${input.kind ?? 'note'}`;
|
|
22
|
+
return [
|
|
23
|
+
`# ${title}`,
|
|
24
|
+
'',
|
|
25
|
+
text.trim(),
|
|
26
|
+
'',
|
|
27
|
+
'## Context',
|
|
28
|
+
'',
|
|
29
|
+
'```json',
|
|
30
|
+
JSON.stringify(context, null, 2),
|
|
31
|
+
'```',
|
|
32
|
+
'',
|
|
33
|
+
].join('\n');
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Write the report under `<cwd>/.reticle/feedback/`. Returns undefined if it cannot be written —
|
|
37
|
+
* a feedback save that throws would take down the tool call that was reporting a problem, which is
|
|
38
|
+
* the one outcome worse than losing the report.
|
|
39
|
+
*/
|
|
40
|
+
export function saveFeedbackLocally(cwd, input, text, context, now = Date.now()) {
|
|
41
|
+
try {
|
|
42
|
+
const dir = join(cwd, ReticleDir.ROOT, FEEDBACK_SUBDIR);
|
|
43
|
+
mkdirSync(dir, { recursive: true });
|
|
44
|
+
const path = join(dir, `${stamp(now)}-${input.kind ?? 'note'}.md`);
|
|
45
|
+
writeFileSync(path, asMarkdown(input, text, context), 'utf8');
|
|
46
|
+
return {
|
|
47
|
+
path,
|
|
48
|
+
// `--body-file` rather than an inlined body: the report contains newlines, quotes and JSON,
|
|
49
|
+
// and a command somebody has to repair before running is a command they do not run.
|
|
50
|
+
command: `gh issue create --repo reticlehq/reticle --title "Reticle feedback: ${input.kind ?? 'note'}" --body-file "${path}"`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** One queued report: the payload, plus what is needed to find and remove it again. */
|
|
2
|
-
|
|
2
|
+
interface OutboxEntry {
|
|
3
3
|
id: string;
|
|
4
4
|
t: string;
|
|
5
5
|
payload: unknown;
|
|
@@ -20,3 +20,4 @@ export declare function readOutbox(home?: string): OutboxEntry[];
|
|
|
20
20
|
* Volume is a handful of lines a week, so the cost is irrelevant.
|
|
21
21
|
*/
|
|
22
22
|
export declare function markDelivered(id: string | null, home?: string): void;
|
|
23
|
+
export {};
|
|
@@ -55,6 +55,17 @@ export interface FeedbackReceipt {
|
|
|
55
55
|
*/
|
|
56
56
|
accepted: boolean;
|
|
57
57
|
reason?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Where the report was written when it could not be SENT.
|
|
60
|
+
*
|
|
61
|
+
* A refusal used to be the end of the road: the report was already written by then, and throwing
|
|
62
|
+
* it away punished exactly the behaviour this channel exists to encourage. Worse, the commonest
|
|
63
|
+
* refusal is a Reticle SOURCE CHECKOUT — so the reporter best placed to name a file and a line was
|
|
64
|
+
* the only one who could not file at all.
|
|
65
|
+
*/
|
|
66
|
+
savedTo?: string;
|
|
67
|
+
/** A ready-to-run command that turns the saved file into an issue. */
|
|
68
|
+
fileWith?: string;
|
|
58
69
|
/** Which redaction rules fired. Empty when nothing needed removing. */
|
|
59
70
|
redacted: string[];
|
|
60
71
|
/** The exact context that went with it, echoed back so the send is never a black box. */
|
|
@@ -26,6 +26,7 @@ import { platform } from 'node:os';
|
|
|
26
26
|
import { getTelemetry } from './telemetry.js';
|
|
27
27
|
import { noteFeedbackUndelivered } from './feedback-delivery.js';
|
|
28
28
|
import { isReticleSourceCheckout } from './dev-repo.js';
|
|
29
|
+
import { saveFeedbackLocally } from './feedback-local.js';
|
|
29
30
|
import { SERVER_VERSION } from '../version/server-version.js';
|
|
30
31
|
import { feedbackContext } from './feedback-context.js';
|
|
31
32
|
import { markDelivered, outboxPath, queueFeedback } from './feedback-outbox.js';
|
|
@@ -140,15 +141,27 @@ export async function submitFeedback(input, opts = {}) {
|
|
|
140
141
|
}
|
|
141
142
|
const telemetry = getTelemetry();
|
|
142
143
|
if (!telemetry.enabled) {
|
|
144
|
+
/*
|
|
145
|
+
* Cannot SEND is not the same as cannot FILE.
|
|
146
|
+
*
|
|
147
|
+
* The report is fully written and redacted by this point. Discarding it punishes the exact
|
|
148
|
+
* behaviour this channel exists to encourage, and it hits hardest where the reports are best: a
|
|
149
|
+
* Reticle source checkout disables telemetry by cwd, so a contributor who can name the file and
|
|
150
|
+
* the line was the one person who could not file at all.
|
|
151
|
+
*
|
|
152
|
+
* So it goes to disk and the receipt says where, plus a command that turns it into an issue.
|
|
153
|
+
* Nothing is lost, and the human is handed one thing to run rather than asked to retype it.
|
|
154
|
+
*/
|
|
155
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
156
|
+
const saved = saveFeedbackLocally(cwd, input, body.text, context);
|
|
143
157
|
return {
|
|
144
158
|
sent: false,
|
|
145
159
|
accepted: false,
|
|
146
|
-
// Named precisely, because the commonest cause is not what the old wording suggested
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
: `telemetry is disabled on this machine, so feedback has nowhere to go. Re-enable with \`reticle telemetry enable\`, or open an issue at https://github.com/reticlehq/reticle/issues.`,
|
|
160
|
+
// Named precisely, because the commonest cause is not what the old wording suggested.
|
|
161
|
+
reason: isReticleSourceCheckout(cwd)
|
|
162
|
+
? 'this is a Reticle source checkout, where telemetry is disabled by design, so this could not be sent — it has been written to disk instead, and nothing was lost.'
|
|
163
|
+
: `telemetry is disabled on this machine, so this could not be sent — it has been written to disk instead. Re-enable with \`reticle telemetry enable\` to send directly.`,
|
|
164
|
+
...(saved === undefined ? {} : { savedTo: saved.path, fileWith: saved.command }),
|
|
152
165
|
redacted,
|
|
153
166
|
context,
|
|
154
167
|
};
|
|
@@ -15,6 +15,8 @@ import { type InitOutcome } from '@reticlehq/core';
|
|
|
15
15
|
export declare const InitFailure: {
|
|
16
16
|
/** Run outside a project root. The single most common first-run mistake. */
|
|
17
17
|
readonly NO_PACKAGE_JSON: "no_package_json";
|
|
18
|
+
/** The manifest exists and is not valid JSON — a trailing comma, usually. Never a stack trace. */
|
|
19
|
+
readonly MALFORMED_PACKAGE_JSON: "malformed_package_json";
|
|
18
20
|
/** The package manager failed: offline, a locked registry, a broken install. */
|
|
19
21
|
readonly DEPENDENCY_INSTALL: "dependency_install";
|
|
20
22
|
/** The `claude mcp add` step failed — the CLI is missing or refused. Reticle installs but is unreachable. */
|
|
@@ -17,6 +17,8 @@ import { resolveInstallSource } from './install-source.js';
|
|
|
17
17
|
export const InitFailure = {
|
|
18
18
|
/** Run outside a project root. The single most common first-run mistake. */
|
|
19
19
|
NO_PACKAGE_JSON: 'no_package_json',
|
|
20
|
+
/** The manifest exists and is not valid JSON — a trailing comma, usually. Never a stack trace. */
|
|
21
|
+
MALFORMED_PACKAGE_JSON: 'malformed_package_json',
|
|
20
22
|
/** The package manager failed: offline, a locked registry, a broken install. */
|
|
21
23
|
DEPENDENCY_INSTALL: 'dependency_install',
|
|
22
24
|
/** The `claude mcp add` step failed — the CLI is missing or refused. Reticle installs but is unreachable. */
|
|
@@ -54,3 +54,17 @@ export declare function declaredInstallSource(env?: NodeJS.ProcessEnv): InstallS
|
|
|
54
54
|
* data, and a hand-edited one must not be able to put an arbitrary string on the wire.
|
|
55
55
|
*/
|
|
56
56
|
export declare function projectInstallSource(cwd: string, env?: NodeJS.ProcessEnv): InstallSource;
|
|
57
|
+
/**
|
|
58
|
+
* The same config with `installSource` added, or `undefined` when there is nothing to add.
|
|
59
|
+
*
|
|
60
|
+
* `installSource` is written only when `.reticle.json` is CREATED, and `init` reports that file as
|
|
61
|
+
* `already exists` on every re-run — so everyone who was already here when the marker shipped, and
|
|
62
|
+
* everyone who ran `init` once without one, keeps `unknown` forever with no way to fix it. That is
|
|
63
|
+
* most of the population the field exists to describe.
|
|
64
|
+
*
|
|
65
|
+
* An EXISTING value is never overwritten. The first channel is the one that brought the user in, and
|
|
66
|
+
* a later `init` through a different route must not take credit for an acquisition it did not make.
|
|
67
|
+
* Anything unparseable is left exactly as it is: a config is user data, and `init` rewriting a file
|
|
68
|
+
* it could not read would be a far worse failure than a missing dimension.
|
|
69
|
+
*/
|
|
70
|
+
export declare function configWithInstallSource(source: string | null | undefined, declared: string | undefined): string | undefined;
|
|
@@ -72,3 +72,33 @@ export function projectInstallSource(cwd, env = process.env) {
|
|
|
72
72
|
}
|
|
73
73
|
return resolveInstallSource(env);
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* The same config with `installSource` added, or `undefined` when there is nothing to add.
|
|
77
|
+
*
|
|
78
|
+
* `installSource` is written only when `.reticle.json` is CREATED, and `init` reports that file as
|
|
79
|
+
* `already exists` on every re-run — so everyone who was already here when the marker shipped, and
|
|
80
|
+
* everyone who ran `init` once without one, keeps `unknown` forever with no way to fix it. That is
|
|
81
|
+
* most of the population the field exists to describe.
|
|
82
|
+
*
|
|
83
|
+
* An EXISTING value is never overwritten. The first channel is the one that brought the user in, and
|
|
84
|
+
* a later `init` through a different route must not take credit for an acquisition it did not make.
|
|
85
|
+
* Anything unparseable is left exactly as it is: a config is user data, and `init` rewriting a file
|
|
86
|
+
* it could not read would be a far worse failure than a missing dimension.
|
|
87
|
+
*/
|
|
88
|
+
export function configWithInstallSource(source, declared) {
|
|
89
|
+
if (undefined === declared || null === source || undefined === source)
|
|
90
|
+
return undefined;
|
|
91
|
+
let parsed;
|
|
92
|
+
try {
|
|
93
|
+
parsed = JSON.parse(source);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
if (null === parsed || 'object' !== typeof parsed || Array.isArray(parsed))
|
|
99
|
+
return undefined;
|
|
100
|
+
const fields = parsed;
|
|
101
|
+
if (undefined !== fields['installSource'])
|
|
102
|
+
return undefined;
|
|
103
|
+
return `${JSON.stringify({ ...fields, installSource: declared }, null, 2)}\n`;
|
|
104
|
+
}
|
|
@@ -30,6 +30,12 @@ import { LicenseActivation } from '@reticlehq/core';
|
|
|
30
30
|
export interface LicenseFacts {
|
|
31
31
|
licenseId?: string;
|
|
32
32
|
licenseStatus?: LicenseActivation;
|
|
33
|
+
/**
|
|
34
|
+
* A key was placed, whether or not this build could judge it. See LICENSE_KEY_PRESENT in core.
|
|
35
|
+
* Reported even in evaluation mode, which is the whole point: that is the case where every other
|
|
36
|
+
* licence property is deliberately silent.
|
|
37
|
+
*/
|
|
38
|
+
licenseKeyPresent?: boolean;
|
|
33
39
|
}
|
|
34
40
|
/**
|
|
35
41
|
* Resolve activation for one event. The clock is passed in (rule 7) and is the EVENT's clock, not a
|
|
@@ -45,4 +51,4 @@ export interface LicenseFacts {
|
|
|
45
51
|
* Never throws. A telemetry property may not change behaviour (rule 5), and this one sits in front of
|
|
46
52
|
* key parsing, which is the part most likely to be handed something malformed.
|
|
47
53
|
*/
|
|
48
|
-
export declare function licenseFacts(now: number, env?: NodeJS.ProcessEnv): LicenseFacts;
|
|
54
|
+
export declare function licenseFacts(now: number, env?: NodeJS.ProcessEnv, baked?: string): LicenseFacts;
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* holds a customer list.
|
|
27
27
|
*/
|
|
28
28
|
import { LicenseActivation } from '@reticlehq/core';
|
|
29
|
-
import { describeLicense } from '../license/license.js';
|
|
29
|
+
import { describeLicense, LICENSE_KEY_ENV } from '../license/license.js';
|
|
30
30
|
/**
|
|
31
31
|
* Resolve activation for one event. The clock is passed in (rule 7) and is the EVENT's clock, not a
|
|
32
32
|
* value captured at startup: sessions here run to eleven hours, so a key that expires mid-session must
|
|
@@ -41,13 +41,17 @@ import { describeLicense } from '../license/license.js';
|
|
|
41
41
|
* Never throws. A telemetry property may not change behaviour (rule 5), and this one sits in front of
|
|
42
42
|
* key parsing, which is the part most likely to be handed something malformed.
|
|
43
43
|
*/
|
|
44
|
-
export function licenseFacts(now, env = process.env) {
|
|
44
|
+
export function licenseFacts(now, env = process.env, baked) {
|
|
45
45
|
try {
|
|
46
|
-
const report = describeLicense(now, env);
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const report = baked === undefined ? describeLicense(now, env) : describeLicense(now, env, baked);
|
|
47
|
+
// Asked BEFORE the eval short-circuit, because eval is exactly the case that used to be silent.
|
|
48
|
+
const keyPresent = (env[LICENSE_KEY_ENV] ?? '').length > 0;
|
|
49
|
+
if (LicenseActivation.EVAL === report.status) {
|
|
50
|
+
return keyPresent ? { licenseKeyPresent: true } : {};
|
|
51
|
+
}
|
|
49
52
|
return {
|
|
50
53
|
licenseStatus: report.status,
|
|
54
|
+
...(keyPresent ? { licenseKeyPresent: true } : {}),
|
|
51
55
|
...(report.licenseId !== undefined ? { licenseId: report.licenseId } : {}),
|
|
52
56
|
};
|
|
53
57
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { TelemetryEventKind } from '@reticlehq/core';
|
|
13
13
|
import { getTelemetry } from './telemetry.js';
|
|
14
|
+
import { appEverConnected } from './app-instrumented.js';
|
|
14
15
|
let connectsSeen = 0;
|
|
15
16
|
/** Set when the daemon starts, so the FIRST connect can report how long Reticle sat unused. */
|
|
16
17
|
let daemonStartedAt;
|
|
@@ -30,6 +31,12 @@ export function reportMcpConnected(client, now = () => Date.now()) {
|
|
|
30
31
|
// sat there. On a reconnect it is just how long the daemon has been up.
|
|
31
32
|
daemonAgeMs: daemonStartedAt === undefined ? 0 : Math.max(0, now() - daemonStartedAt),
|
|
32
33
|
...(client !== undefined ? { client } : {}),
|
|
34
|
+
// WHAT THE AGENT HAD TO LOOK AT. Most clients that attach never call a tool, and that cohort
|
|
35
|
+
// emits nothing at all — an agent that reads the instructions, learns nothing is wired and
|
|
36
|
+
// stops has refused nothing, so `tool_refused` cannot see it either. Read off the same flag
|
|
37
|
+
// `app_instrumented` sets, never a second counter, so the two halves of the funnel cannot
|
|
38
|
+
// disagree about one daemon run.
|
|
39
|
+
appConnected: appEverConnected(),
|
|
33
40
|
},
|
|
34
41
|
});
|
|
35
42
|
}
|
|
@@ -13,9 +13,6 @@ export declare const FeatureFamily: {
|
|
|
13
13
|
readonly BUG_CAPSULES: "bug_capsules";
|
|
14
14
|
readonly CROSS_RUN_HISTORY: "cross_run_history";
|
|
15
15
|
};
|
|
16
|
-
export type FeatureFamily = (typeof FeatureFamily)[keyof typeof FeatureFamily];
|
|
17
|
-
/** Count source files, stopping at the cap (the cap itself lands in the largest bucket, correctly). */
|
|
18
|
-
export declare function countSourceFiles(root: string, readDir?: DirReader): number;
|
|
19
16
|
export declare function sizeBucket(sourceFiles: number): ProjectSize;
|
|
20
17
|
/**
|
|
21
18
|
* Project age in whole WEEKS, from the repo's first commit.
|
|
@@ -30,15 +27,21 @@ export declare function sizeBucket(sourceFiles: number): ProjectSize;
|
|
|
30
27
|
export declare function projectAgeWeeks(root: string, now: number, read?: FileReader): number | undefined;
|
|
31
28
|
/** True when package.json declares workspaces — monorepos exercise very different code paths. */
|
|
32
29
|
export declare function isMonorepo(root: string, read?: FileReader): boolean;
|
|
33
|
-
interface DirEntry {
|
|
34
|
-
name: string;
|
|
35
|
-
isDirectory: boolean;
|
|
36
|
-
}
|
|
37
|
-
type DirReader = (dir: string) => DirEntry[];
|
|
38
30
|
type FileReader = (path: string) => string;
|
|
39
31
|
/**
|
|
40
32
|
* Build the full profile. Best-effort throughout: an unreadable project yields a profile of zeroes
|
|
41
33
|
* rather than an exception, because a telemetry snapshot must never be able to fail a daemon start.
|
|
42
34
|
*/
|
|
43
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Facts about the install that the filesystem cannot answer.
|
|
37
|
+
*
|
|
38
|
+
* Passed in rather than read here for the same reason `previouslyConnected` is passed to the MCP
|
|
39
|
+
* server: it needs the daemon's port and the state home, and a profile that reached for those would
|
|
40
|
+
* stop being a pure function of a directory.
|
|
41
|
+
*/
|
|
42
|
+
export interface InstallFacts {
|
|
43
|
+
/** Has an app for this project EVER connected to Reticle, from durable state. */
|
|
44
|
+
appConnectedBefore: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare function profileProject(cwd: string, now: number, install?: InstallFacts): ProjectProfile;
|
|
44
47
|
export {};
|
|
@@ -15,6 +15,7 @@ import { join } from 'node:path';
|
|
|
15
15
|
import { ProjectSize, ReticleDir } from '@reticlehq/core';
|
|
16
16
|
import { gitFacts } from './git-facts.js';
|
|
17
17
|
import { detectStack } from './feedback-context.js';
|
|
18
|
+
import { readProjectId } from '../cli/cli-port.js';
|
|
18
19
|
/**
|
|
19
20
|
* The feature FAMILIES, and the on-disk evidence that a project has adopted each. Named rather than
|
|
20
21
|
* counted so the vocabulary stays stable as tools are added, renamed, or merged into action-dispatched
|
|
@@ -45,7 +46,7 @@ const MAX_FILES_SCANNED = 6000;
|
|
|
45
46
|
const MAX_WALK_DEPTH = 8;
|
|
46
47
|
const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000;
|
|
47
48
|
/** Count source files, stopping at the cap (the cap itself lands in the largest bucket, correctly). */
|
|
48
|
-
|
|
49
|
+
function countSourceFiles(root, readDir = defaultReadDir) {
|
|
49
50
|
let count = 0;
|
|
50
51
|
const walk = (dir, depth) => {
|
|
51
52
|
if (depth > MAX_WALK_DEPTH || count >= MAX_FILES_SCANNED)
|
|
@@ -151,11 +152,7 @@ function exists(path) {
|
|
|
151
152
|
return false;
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
|
-
|
|
155
|
-
* Build the full profile. Best-effort throughout: an unreadable project yields a profile of zeroes
|
|
156
|
-
* rather than an exception, because a telemetry snapshot must never be able to fail a daemon start.
|
|
157
|
-
*/
|
|
158
|
-
export function profileProject(cwd, now) {
|
|
155
|
+
export function profileProject(cwd, now, install) {
|
|
159
156
|
const reticleRoot = join(cwd, ReticleDir.ROOT);
|
|
160
157
|
const flowCount = countIn(reticleRoot, ReticleDir.FLOWS_SUBDIR, /\.json$/);
|
|
161
158
|
const baselineCount = countIn(reticleRoot, ReticleDir.BASELINES_SUBDIR);
|
|
@@ -180,7 +177,7 @@ export function profileProject(cwd, now) {
|
|
|
180
177
|
featuresUsed.push(FeatureFamily.BUG_CAPSULES);
|
|
181
178
|
if (hasHistory)
|
|
182
179
|
featuresUsed.push(FeatureFamily.CROSS_RUN_HISTORY);
|
|
183
|
-
const { stack, stackMajor, stackSource } = detectStack(cwd);
|
|
180
|
+
const { stack, stackMajor, stackSource, stackUnknownReason } = detectStack(cwd);
|
|
184
181
|
const ageWeeks = projectAgeWeeks(cwd, now);
|
|
185
182
|
const git = gitFacts(cwd);
|
|
186
183
|
return {
|
|
@@ -189,6 +186,8 @@ export function profileProject(cwd, now) {
|
|
|
189
186
|
...(stack !== undefined ? { stack } : {}),
|
|
190
187
|
...(stackSource !== undefined ? { stackSource } : {}),
|
|
191
188
|
...(stackMajor !== undefined ? { stackMajor } : {}),
|
|
189
|
+
// Present only when `stack` is absent, so the field's presence marks the unknown bucket.
|
|
190
|
+
...(stackUnknownReason !== undefined ? { stackUnknownReason } : {}),
|
|
192
191
|
size: sizeBucket(countSourceFiles(cwd)),
|
|
193
192
|
monorepo: isMonorepo(cwd),
|
|
194
193
|
...(ageWeeks !== undefined ? { ageWeeks } : {}),
|
|
@@ -202,5 +201,11 @@ export function profileProject(cwd, now) {
|
|
|
202
201
|
// Rounded to 2dp: the exact ratio is a function of the counts above, and an unrounded float would
|
|
203
202
|
// just be a higher-cardinality restatement of them.
|
|
204
203
|
featureDepth: Math.round((featuresUsed.length / ALL_FAMILIES.length) * 100) / 100,
|
|
204
|
+
// WHERE the install stopped, for the daemons that never see an app. `daemon_started` minus
|
|
205
|
+
// `app_instrumented` says one happened and cannot say why; these two bits split that silence
|
|
206
|
+
// into "never ran init", "ran it and no page has ever reached us", and "works, just not up
|
|
207
|
+
// right now". See the fields on ProjectProfileSchema.
|
|
208
|
+
initialized: readProjectId(cwd) !== undefined,
|
|
209
|
+
...(install === undefined ? {} : { appConnectedBefore: install.appConnectedBefore }),
|
|
205
210
|
};
|
|
206
211
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The MCP proxy's session rollup.
|
|
3
|
+
*
|
|
4
|
+
* POST socket failures (`ENOBUFS`, `EMFILE`, `ECONNREFUSED` before any bytes were sent) happen in
|
|
5
|
+
* this process, not the daemon's. `tool_refused` never fires (the handler never ran) and
|
|
6
|
+
* `mcp_connection_lost` never fires (the SSE stream is fine). Counts live on SessionMetrics here
|
|
7
|
+
* and leave as one `session_progress` — an existing kind, omitted when nothing happened.
|
|
8
|
+
*
|
|
9
|
+
* The send is awaited. Fire-and-forget microseconds before `process.exit` is how `daemon_stopped`
|
|
10
|
+
* never arrived: the POST was killed every time and nothing threw.
|
|
11
|
+
*/
|
|
12
|
+
import { TelemetryEventKind } from '@reticlehq/core';
|
|
13
|
+
import { getSessionMetrics } from './session-metrics.js';
|
|
14
|
+
import { getTelemetry } from './telemetry.js';
|
|
15
|
+
export async function flushProxySessionMetrics(telemetry = getTelemetry(), metrics = getSessionMetrics()) {
|
|
16
|
+
if (metrics.empty)
|
|
17
|
+
return;
|
|
18
|
+
await telemetry.emit(TelemetryEventKind.SESSION_PROGRESS, {
|
|
19
|
+
session: metrics.summarize(true),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
* how a replay works.
|
|
12
12
|
*/
|
|
13
13
|
import { type ReticleVerificationRun } from '@reticlehq/core';
|
|
14
|
-
/** The tool name reported for this path, so CI runs are distinguishable from agent-driven ones. */
|
|
15
|
-
export declare const VERIFY_SURFACE = "reticle_verify";
|
|
16
14
|
/**
|
|
17
15
|
* Report one completed verification run: the verdict, and one bug per failing flow.
|
|
18
16
|
*
|
|
@@ -14,7 +14,7 @@ import { BugAttribution, BugSource, RunFlowStatus, TelemetryActor, TelemetryEven
|
|
|
14
14
|
import { getSessionMetrics } from './session-metrics.js';
|
|
15
15
|
import { getTelemetry } from './telemetry.js';
|
|
16
16
|
/** The tool name reported for this path, so CI runs are distinguishable from agent-driven ones. */
|
|
17
|
-
|
|
17
|
+
const VERIFY_SURFACE = 'reticle_verify';
|
|
18
18
|
/**
|
|
19
19
|
* Report one completed verification run: the verdict, and one bug per failing flow.
|
|
20
20
|
*
|
|
@@ -65,6 +65,18 @@ export declare class SessionMetrics {
|
|
|
65
65
|
* one inside a much larger pile. Same treatment otherwise — fingerprinted, variables stripped.
|
|
66
66
|
*/
|
|
67
67
|
recordSdkFailure(site: string, message: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* The MCP proxy could not POST a tool call because the TCP socket itself failed (`ENOBUFS`,
|
|
70
|
+
* `EMFILE`, `ECONNREFUSED` before any bytes left). The SSE stream is still up, so this is
|
|
71
|
+
* invisible to `mcp_connection_lost`, and the handler never ran, so it is invisible to
|
|
72
|
+
* `tool_refused`. Counted so a dashboard can see it without a new event kind.
|
|
73
|
+
*/
|
|
74
|
+
recordPostSocketFailure(): void;
|
|
75
|
+
/**
|
|
76
|
+
* A bounded retry of an unsent POST then delivered. The pair against `recordPostSocketFailure`:
|
|
77
|
+
* the retry that quietly saved a call is a different fact from the one that never ran.
|
|
78
|
+
*/
|
|
79
|
+
recordPostRetrySaved(): void;
|
|
68
80
|
/** The agent's recent approach run + the call in flight — context for a crash report. */
|
|
69
81
|
get trail(): {
|
|
70
82
|
breadcrumb: string[];
|