@reticlehq/server 2.13.0 → 2.14.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 +8 -18
- package/bin/reticle.js +3 -0
- package/dist/bridge/auth-failure-reason.js +12 -1
- package/dist/bridge/bridge-security.d.ts +8 -0
- package/dist/bridge/bridge-security.js +6 -1
- package/dist/bridge/bridge.d.ts +3 -0
- package/dist/bridge/bridge.js +23 -1
- package/dist/bridge/bridge.test-harness.d.ts +10 -1
- package/dist/bridge/bridge.test-harness.js +7 -1
- package/dist/bridge/pairing-token.d.ts +17 -0
- package/dist/bridge/pairing-token.js +26 -0
- package/dist/bridge/token-auth.d.ts +7 -0
- package/dist/bridge/token-auth.js +7 -1
- package/dist/capsule/capsule-store.d.ts +4 -4
- package/dist/capsule/capsule.d.ts +1 -1
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +21 -1
- package/dist/capsule/causal-summary.js +8 -6
- package/dist/capsule/divergence.d.ts +1 -1
- package/dist/capsule/divergence.js +26 -10
- package/dist/chromium-launch-options.d.ts +2 -1
- package/dist/cli/adhoc-verdict.d.ts +58 -0
- package/dist/cli/adhoc-verdict.js +115 -0
- package/dist/cli/chromium-hint.d.ts +18 -4
- package/dist/cli/chromium-hint.js +54 -6
- package/dist/cli/cli-doctor.js +1 -2
- package/dist/cli/cli-flow-commands.d.ts +16 -2
- package/dist/cli/cli-flow-commands.js +28 -9
- package/dist/cli/cli-parse-feedback.js +1 -1
- package/dist/cli/cli-parse.d.ts +2 -1
- package/dist/cli/cli-parse.js +38 -0
- package/dist/cli/cli-port.d.ts +5 -13
- package/dist/cli/cli-port.js +5 -38
- package/dist/cli/cli-update-commands.js +4 -4
- package/dist/cli/cli-verify.d.ts +24 -7
- package/dist/cli/cli-verify.js +87 -13
- package/dist/cli/cloud-keystore.d.ts +2 -1
- package/dist/cli/cloud-login.d.ts +2 -1
- package/dist/cli/cloud-session.d.ts +2 -1
- package/dist/cli/cloud-session.js +1 -1
- package/dist/cli/daemon-start-options.d.ts +2 -1
- package/dist/cli/device-flow.d.ts +2 -2
- package/dist/cli/doctor-daemon-line.d.ts +2 -1
- package/dist/cli/doctor-project-line.d.ts +2 -1
- package/dist/cli/doctor-rows.d.ts +2 -1
- package/dist/cli/drive-attach.d.ts +4 -3
- package/dist/cli/gate-hook-message.d.ts +2 -1
- package/dist/cli/status-next-action.js +10 -11
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +21 -9
- package/dist/cloud/account-state.d.ts +20 -0
- package/dist/cloud/account-state.js +65 -0
- package/dist/cloud/cloud-sync.d.ts +46 -3
- package/dist/cloud/cloud-sync.js +116 -0
- package/dist/cloud/sync-cycle.d.ts +5 -4
- package/dist/cloud/sync-cycle.js +1 -1
- package/dist/cloud/sync-daemon.d.ts +3 -19
- package/dist/cloud/sync-daemon.js +2 -2
- package/dist/crawl/crawl-tools.js +1 -1
- package/dist/crawl/crawl.d.ts +5 -5
- package/dist/crawl/crawl.js +138 -4
- package/dist/daemon/daemon-resilience.d.ts +3 -2
- package/dist/daemon/daemon-resilience.js +43 -4
- package/dist/daemon/daemon-resolve.d.ts +0 -9
- package/dist/daemon/daemon-resolve.js +2 -2
- package/dist/daemon/dev-servers.d.ts +1 -1
- package/dist/daemon/dev-servers.js +1 -1
- package/dist/daemon/loopback-alias.d.ts +2 -1
- package/dist/daemon/startup-failure.d.ts +11 -0
- package/dist/daemon/startup-failure.js +46 -0
- package/dist/events/contradictions.d.ts +10 -16
- package/dist/events/contradictions.js +149 -17
- package/dist/events/declared.d.ts +13 -1
- package/dist/events/declared.js +59 -3
- package/dist/events/echo-mismatch.js +75 -6
- package/dist/events/lineage.d.ts +52 -0
- package/dist/events/lineage.js +122 -0
- package/dist/events/net-evidence.d.ts +21 -0
- package/dist/events/net-evidence.js +81 -0
- package/dist/events/predicate-console.d.ts +13 -0
- package/dist/events/predicate-console.js +138 -0
- package/dist/events/predicate-element.d.ts +13 -0
- package/dist/events/predicate-element.js +214 -0
- package/dist/events/predicate-eval.d.ts +15 -3
- package/dist/events/predicate-eval.js +128 -110
- package/dist/events/predicate-parse.js +34 -1
- package/dist/events/predicate-request-body.d.ts +50 -0
- package/dist/events/predicate-request-body.js +120 -0
- package/dist/events/predicate-route.d.ts +21 -1
- package/dist/events/predicate-route.js +149 -21
- package/dist/events/predicate-schema.d.ts +8 -1
- package/dist/events/predicate-schema.js +34 -0
- package/dist/events/predicate.d.ts +2 -0
- package/dist/events/predicate.js +52 -159
- package/dist/events/split-text-miss.d.ts +6 -4
- package/dist/events/split-text-miss.js +12 -5
- package/dist/flows/affected-tools.js +22 -9
- package/dist/flows/annotate.d.ts +2 -1
- package/dist/flows/annotate.js +19 -1
- package/dist/flows/coverage.d.ts +2 -2
- package/dist/flows/flake.d.ts +3 -3
- package/dist/flows/flow-expect-grammar.d.ts +33 -0
- package/dist/flows/flow-expect-grammar.js +155 -0
- package/dist/flows/flow-replay-run.d.ts +1 -1
- package/dist/flows/flow-replay-run.js +28 -15
- package/dist/flows/flow-replay.d.ts +10 -0
- package/dist/flows/flow-replay.js +57 -22
- package/dist/flows/flow-secret-field.d.ts +14 -0
- package/dist/flows/flow-secret-field.js +22 -0
- package/dist/flows/flow-step-runners.d.ts +1 -0
- package/dist/flows/flow-step-runners.js +91 -34
- package/dist/flows/flow-success.js +22 -0
- package/dist/flows/flow-tools.js +27 -8
- package/dist/flows/flow-upload-resolve.d.ts +23 -0
- package/dist/flows/flow-upload-resolve.js +46 -0
- package/dist/flows/flows.d.ts +15 -0
- package/dist/flows/flows.js +17 -14
- package/dist/flows/git-changed.d.ts +23 -4
- package/dist/flows/git-changed.js +32 -6
- package/dist/flows/heal-run.js +2 -2
- package/dist/flows/predicate-to-expect.d.ts +3 -2
- package/dist/flows/predicate-to-expect.js +11 -5
- package/dist/flows/recording-backtrack.d.ts +24 -0
- package/dist/flows/recording-backtrack.js +73 -0
- package/dist/flows/recordings.d.ts +6 -0
- package/dist/flows/replay-halt.d.ts +26 -0
- package/dist/flows/replay-halt.js +30 -0
- package/dist/flows/replay.d.ts +0 -8
- package/dist/flows/replay.js +15 -3
- package/dist/flows/suite-flakes.d.ts +2 -1
- package/dist/flows/verify-change-tools.js +16 -4
- package/dist/honesty/already-true.d.ts +14 -0
- package/dist/honesty/already-true.js +47 -4
- package/dist/honesty/blind-spots.d.ts +3 -1
- package/dist/honesty/blind-spots.js +24 -1
- package/dist/honesty/body-capture-remedy.d.ts +42 -0
- package/dist/honesty/body-capture-remedy.js +95 -0
- package/dist/honesty/feature-capture.d.ts +3 -2
- package/dist/honesty/feature-capture.js +1 -1
- package/dist/honesty/gap-ledger.d.ts +1 -1
- package/dist/honesty/gap-ledger.js +1 -1
- package/dist/honesty/instrumentation-gaps.d.ts +8 -1
- package/dist/honesty/instrumentation-gaps.js +12 -2
- package/dist/honesty/observability.d.ts +4 -3
- package/dist/honesty/tool-hit-rate.d.ts +3 -2
- package/dist/honesty/uncaptured-bodies.d.ts +3 -1
- package/dist/honesty/uncaptured-bodies.js +6 -2
- package/dist/honesty/unsettled.d.ts +9 -0
- package/dist/honesty/unsettled.js +45 -0
- package/dist/honesty/verified.d.ts +32 -1
- package/dist/honesty/verified.js +37 -10
- package/dist/impact/impact-store.d.ts +5 -14
- package/dist/impact/impact-store.js +49 -6
- package/dist/index.js +1 -1
- package/dist/input/network-detail.d.ts +26 -1
- package/dist/input/network-detail.js +44 -12
- package/dist/input/network-mock-tools.js +24 -17
- package/dist/input/real-input.js +1 -1
- package/dist/input/viewport-tools.js +25 -13
- package/dist/intent/inline-intent.d.ts +0 -10
- package/dist/intent/inline-intent.js +1 -1
- package/dist/intent/intent-shard-store.d.ts +3 -2
- package/dist/intent/intent-shard-store.js +1 -1
- package/dist/intent/intent-shard.d.ts +14 -14
- package/dist/intent/intent-shard.js +4 -4
- package/dist/intent/intent-store.d.ts +1 -1
- package/dist/intent/intent-store.js +1 -1
- package/dist/intent/intent-subject.d.ts +2 -1
- package/dist/intent/open-intents.d.ts +3 -2
- package/dist/license/license.d.ts +4 -4
- package/dist/license/license.js +1 -1
- package/dist/mcp/attach-memory.d.ts +2 -1
- package/dist/mcp/client-identity.d.ts +2 -1
- package/dist/mcp/mcp-outage.d.ts +1 -1
- package/dist/mcp/mcp-outage.js +1 -1
- package/dist/mcp/mcp-proxy.d.ts +3 -1
- package/dist/mcp/mcp-proxy.js +32 -7
- package/dist/mcp/mcp.js +3 -1
- package/dist/mcp/proxy-handshake.d.ts +2 -1
- package/dist/mcp/proxy-idle-exit.d.ts +45 -0
- package/dist/mcp/proxy-idle-exit.js +94 -0
- package/dist/mcp/server-instructions.d.ts +2 -1
- package/dist/memory/project-memory.d.ts +2 -3
- package/dist/memory/project-memory.js +1 -1
- package/dist/oracles/propose-consequences.js +5 -5
- package/dist/oracles/self-instrument.d.ts +2 -2
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/platform.d.ts +3 -5
- package/dist/platform.js +3 -5
- package/dist/pool/browser-pool.d.ts +92 -0
- package/dist/pool/browser-pool.js +119 -5
- package/dist/pool/playwright-launcher.js +29 -2
- package/dist/pool/storage-seed.d.ts +23 -0
- package/dist/pool/storage-seed.js +198 -0
- package/dist/project/artifact-root.d.ts +2 -1
- package/dist/project/memory-fs.d.ts +2 -1
- package/dist/project/reticle-dir.js +1 -1
- package/dist/project/session-root.d.ts +8 -0
- package/dist/project/session-root.js +9 -2
- package/dist/runs/reticle-runner.d.ts +14 -1
- package/dist/runs/reticle-runner.js +39 -2
- package/dist/session/attachment-history.d.ts +2 -1
- package/dist/session/fake-session.d.ts +37 -0
- package/dist/session/fake-session.js +81 -0
- package/dist/session/lease-visibility.d.ts +2 -1
- package/dist/session/live-control-tools.js +6 -0
- package/dist/session/no-session-diagnosis.d.ts +17 -1
- package/dist/session/no-session-diagnosis.js +32 -6
- package/dist/session/no-session-next-action.d.ts +14 -0
- package/dist/session/no-session-next-action.js +18 -0
- package/dist/session/no-session-watch.js +46 -3
- package/dist/session/registered-projects.d.ts +54 -0
- package/dist/session/registered-projects.js +73 -0
- package/dist/session/review-store.d.ts +2 -1
- package/dist/session/session-health.d.ts +62 -1
- package/dist/session/session-health.js +82 -2
- package/dist/session/session-manager.d.ts +11 -1
- package/dist/session/session-manager.js +18 -3
- package/dist/session/session-replaced.d.ts +24 -0
- package/dist/session/session-replaced.js +29 -0
- package/dist/session/session-successor.js +20 -4
- package/dist/session/session.d.ts +13 -5
- package/dist/session/session.js +31 -23
- package/dist/setup/agent-configs.d.ts +3 -2
- package/dist/setup/agent-writer.d.ts +3 -2
- package/dist/setup/approval-migration.d.ts +2 -1
- package/dist/setup/approval-migration.js +1 -1
- package/dist/setup/auto-approve.d.ts +11 -19
- package/dist/setup/auto-approve.js +15 -12
- package/dist/{init → setup}/confirm.js +1 -1
- package/dist/setup/desktop-shape.d.ts +3 -2
- package/dist/setup/dev-server-wait.d.ts +8 -0
- package/dist/setup/dev-server-wait.js +3 -1
- package/dist/setup/drive-agent.d.ts +2 -3
- package/dist/setup/drive-agent.js +1 -1
- package/dist/setup/drive-plan.d.ts +3 -2
- package/dist/setup/ensure-daemon.d.ts +2 -1
- package/dist/setup/existing-app.d.ts +12 -0
- package/dist/setup/existing-app.js +13 -0
- package/dist/setup/init-host.d.ts +11 -0
- package/dist/setup/init-host.js +26 -0
- package/dist/setup/init-runtime.d.ts +4 -10
- package/dist/setup/init-runtime.js +23 -16
- package/dist/setup/license-key.d.ts +2 -1
- package/dist/setup/listeners.d.ts +2 -1
- package/dist/setup/loopback-probe-urls.d.ts +18 -0
- package/dist/setup/loopback-probe-urls.js +52 -0
- package/dist/setup/node-effects.d.ts +3 -0
- package/dist/setup/node-effects.js +26 -11
- package/dist/setup/page-probe.d.ts +5 -0
- package/dist/setup/relaunch.d.ts +2 -1
- package/dist/setup/run-setup.d.ts +5 -1
- package/dist/setup/run-setup.js +155 -48
- package/dist/setup/setup-command.d.ts +3 -2
- package/dist/setup/setup-command.js +8 -0
- package/dist/setup/setup-options.d.ts +5 -3
- package/dist/setup/setup-options.js +3 -2
- package/dist/setup/wait-progress.d.ts +28 -0
- package/dist/setup/wait-progress.js +43 -0
- package/dist/telemetry/app-instrumented.d.ts +2 -1
- package/dist/telemetry/app-instrumented.js +1 -1
- package/dist/telemetry/automation-hint.d.ts +1 -1
- package/dist/telemetry/automation-hint.js +1 -1
- package/dist/telemetry/cli-telemetry.js +1 -1
- package/dist/telemetry/connect-failure.d.ts +1 -1
- package/dist/telemetry/connect-failure.js +1 -1
- package/dist/telemetry/crash-cause.d.ts +3 -2
- package/dist/telemetry/crash-cause.js +1 -1
- package/dist/telemetry/daemon-telemetry.d.ts +3 -2
- package/dist/telemetry/daemon-telemetry.js +1 -1
- package/dist/telemetry/error-class.d.ts +3 -2
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-cli.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +3 -2
- package/dist/telemetry/feedback-context.js +5 -4
- package/dist/telemetry/feedback-local.d.ts +3 -2
- package/dist/telemetry/feedback.d.ts +4 -3
- package/dist/telemetry/feedback.js +1 -1
- package/dist/telemetry/git-facts.d.ts +2 -2
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/identify.d.ts +1 -1
- package/dist/telemetry/identify.js +1 -1
- package/dist/telemetry/init-telemetry.d.ts +2 -14
- package/dist/telemetry/init-telemetry.js +4 -13
- package/dist/telemetry/install-source.d.ts +7 -13
- package/dist/telemetry/install-source.js +7 -29
- package/dist/telemetry/license-activation.d.ts +3 -2
- package/dist/telemetry/license-activation.js +1 -1
- package/dist/telemetry/machine-snapshot.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +1 -1
- package/dist/telemetry/project-profile.d.ts +0 -14
- package/dist/telemetry/project-profile.js +1 -1
- package/dist/telemetry/proxy-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +1 -1
- package/dist/telemetry/telemetry.d.ts +1 -17
- package/dist/telemetry/telemetry.js +4 -4
- package/dist/telemetry/tool-refused.d.ts +1 -1
- package/dist/telemetry/tool-refused.js +1 -1
- package/dist/tools/act-observation.d.ts +3 -2
- package/dist/tools/act-preflight.js +8 -3
- package/dist/tools/act-target.d.ts +4 -0
- package/dist/tools/act-target.js +14 -5
- package/dist/tools/act-tools.d.ts +0 -2
- package/dist/tools/act-tools.js +45 -15
- package/dist/tools/assert-source.d.ts +30 -0
- package/dist/tools/assert-source.js +21 -0
- package/dist/tools/assert-verdict.d.ts +1 -1
- package/dist/tools/assert-verdict.js +14 -4
- package/dist/tools/browser-tools.js +28 -8
- package/dist/tools/complete-snapshot.d.ts +3 -2
- package/dist/tools/dynamic-tools.js +8 -0
- package/dist/tools/error-recovery.d.ts +10 -0
- package/dist/tools/error-recovery.js +23 -0
- package/dist/tools/feedback-tools.js +1 -1
- package/dist/tools/invoke-tool.js +2 -0
- package/dist/tools/lease-tools.d.ts +48 -20
- package/dist/tools/lease-tools.js +311 -76
- package/dist/tools/lineage-tools.d.ts +14 -0
- package/dist/tools/lineage-tools.js +64 -0
- package/dist/tools/merge-tools.js +1 -3
- package/dist/tools/navigate-arrival.d.ts +45 -1
- package/dist/tools/navigate-arrival.js +39 -6
- package/dist/tools/navigate-result.d.ts +8 -1
- package/dist/tools/navigate-result.js +14 -4
- package/dist/tools/numeric-bounds.d.ts +0 -11
- package/dist/tools/numeric-bounds.js +4 -4
- package/dist/tools/observe-tools.js +22 -26
- package/dist/tools/observed-nothing.d.ts +9 -0
- package/dist/tools/observed-nothing.js +2 -1
- package/dist/tools/read-tools.js +12 -17
- package/dist/tools/real-input-attempt.d.ts +14 -6
- package/dist/tools/real-input-attempt.js +75 -18
- package/dist/tools/reconcile-tools.js +2 -1
- package/dist/tools/reload-result.d.ts +6 -1
- package/dist/tools/reload-result.js +7 -2
- package/dist/tools/resolve-target.d.ts +7 -1
- package/dist/tools/resolve-target.js +72 -9
- package/dist/tools/settle-in-flight.d.ts +1 -1
- package/dist/tools/settle-in-flight.js +9 -2
- package/dist/tools/snapshot-delta.d.ts +24 -1
- package/dist/tools/snapshot-delta.js +66 -7
- package/dist/tools/tool-names.d.ts +1 -0
- package/dist/tools/tool-names.js +1 -0
- package/dist/tools/tool-surface.js +20 -2
- package/dist/tools/tools.js +120 -11
- package/dist/tools/verify-next-baton.d.ts +2 -1
- package/dist/update/sdk-sync.d.ts +1 -1
- package/dist/update/sdk-sync.js +1 -1
- package/dist/update/updater.js +1 -1
- package/dist/version/sdk-fix.d.ts +2 -1
- package/dist/version/version-skew.d.ts +0 -8
- package/dist/version/version-skew.js +1 -1
- package/docs/README.md +8 -6
- package/docs/actions.mdx +1 -1
- package/docs/agent-cheatsheet.md +4 -2
- package/docs/architecture.md +1 -1
- package/docs/cli/mcp.mdx +3 -1
- package/docs/cli/watch.mdx +1 -1
- package/docs/cli.mdx +1 -1
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +8 -3
- package/docs/faq.mdx +9 -0
- package/docs/first-drive.md +136 -0
- package/docs/frameworks.mdx +29 -7
- package/docs/gate-plan.md +2 -2
- package/docs/gates.md +19 -11
- package/docs/getting-started.md +7 -1
- package/docs/http-transport.md +164 -0
- package/docs/index.mdx +5 -3
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +15 -3
- package/docs/platform-integration.md +1 -1
- package/docs/predicates.mdx +2 -0
- package/docs/system-map.md +2 -2
- package/docs/telemetry-contract.md +4 -4
- package/docs/telemetry-events.mdx +1 -1
- package/docs/telemetry.md +3 -3
- package/docs/tools-navigate.mdx +14 -6
- package/docs/usage.md +4 -4
- package/package.json +7 -4
- package/dist/init/agent-rules.d.ts +0 -116
- package/dist/init/agent-rules.js +0 -279
- package/dist/init/app-choice.d.ts +0 -20
- package/dist/init/app-choice.js +0 -25
- package/dist/init/astro-host.d.ts +0 -21
- package/dist/init/astro-host.js +0 -27
- package/dist/init/astro-patch.d.ts +0 -33
- package/dist/init/astro-patch.js +0 -300
- package/dist/init/capabilities.d.ts +0 -40
- package/dist/init/capabilities.js +0 -150
- package/dist/init/closing-hint.d.ts +0 -36
- package/dist/init/closing-hint.js +0 -120
- package/dist/init/connect-steps.d.ts +0 -2
- package/dist/init/connect-steps.js +0 -50
- package/dist/init/cra.d.ts +0 -79
- package/dist/init/cra.js +0 -178
- package/dist/init/csp-check.d.ts +0 -38
- package/dist/init/csp-check.js +0 -107
- package/dist/init/csp-doctor.d.ts +0 -32
- package/dist/init/csp-doctor.js +0 -54
- package/dist/init/desktop-doctor.d.ts +0 -41
- package/dist/init/desktop-doctor.js +0 -198
- package/dist/init/detect.d.ts +0 -96
- package/dist/init/detect.js +0 -209
- package/dist/init/dev-script.d.ts +0 -50
- package/dist/init/dev-script.js +0 -66
- package/dist/init/format-generated.d.ts +0 -18
- package/dist/init/format-generated.js +0 -39
- package/dist/init/install-hint.d.ts +0 -9
- package/dist/init/install-hint.js +0 -29
- package/dist/init/mcp-clients.d.ts +0 -89
- package/dist/init/mcp-clients.js +0 -299
- package/dist/init/mcp-registered.d.ts +0 -12
- package/dist/init/mcp-registered.js +0 -14
- package/dist/init/mcp.d.ts +0 -89
- package/dist/init/mcp.js +0 -129
- package/dist/init/next-patch.d.ts +0 -39
- package/dist/init/next-patch.js +0 -196
- package/dist/init/node-io.d.ts +0 -7
- package/dist/init/node-io.js +0 -117
- package/dist/init/non-js-project.d.ts +0 -27
- package/dist/init/non-js-project.js +0 -84
- package/dist/init/patch-kind.d.ts +0 -20
- package/dist/init/patch-kind.js +0 -10
- package/dist/init/plan-framework.d.ts +0 -64
- package/dist/init/plan-framework.js +0 -565
- package/dist/init/plan.d.ts +0 -242
- package/dist/init/plan.js +0 -703
- package/dist/init/preflight.d.ts +0 -28
- package/dist/init/preflight.js +0 -38
- package/dist/init/project-id.d.ts +0 -12
- package/dist/init/project-id.js +0 -25
- package/dist/init/refresh-rules.d.ts +0 -39
- package/dist/init/refresh-rules.js +0 -51
- package/dist/init/run.d.ts +0 -117
- package/dist/init/run.js +0 -806
- package/dist/init/slash-command.d.ts +0 -27
- package/dist/init/slash-command.js +0 -132
- package/dist/init/snippets.d.ts +0 -134
- package/dist/init/snippets.js +0 -572
- package/dist/init/vite-config.d.ts +0 -18
- package/dist/init/vite-config.js +0 -114
- package/dist/init/windows-quote.d.ts +0 -38
- package/dist/init/windows-quote.js +0 -70
- package/dist/init/workspace-apps.d.ts +0 -30
- package/dist/init/workspace-apps.js +0 -178
- package/dist/project/remember-project.d.ts +0 -24
- package/dist/project/remember-project.js +0 -40
- /package/dist/{init → setup}/confirm.d.ts +0 -0
|
@@ -1,4 +1,61 @@
|
|
|
1
|
-
import { BrowserBrand, BUFFER_EVICTION_WARNING, THROTTLED_WARNING } from '@reticlehq/core';
|
|
1
|
+
import { BrowserBrand, EventType, BUFFER_EVICTION_WARNING, THROTTLED_STARVED_NOTE, THROTTLED_WARNING, } from '@reticlehq/core';
|
|
2
|
+
/**
|
|
3
|
+
* How long a request may be in flight before it is worth reporting on session health.
|
|
4
|
+
*
|
|
5
|
+
* A drive always has requests in the air, so a low bar puts a scary number on every healthy session
|
|
6
|
+
* and trains agents to ignore the field — the same way a guard that fires on routine controls trains
|
|
7
|
+
* them to pass `confirmDangerous` reflexively.
|
|
8
|
+
*
|
|
9
|
+
* 10s, and it is a HEURISTIC with no clean separation available: "a slow endpoint" and "a server
|
|
10
|
+
* that will never answer" are the same observation until one of them finishes. The number is chosen
|
|
11
|
+
* to sit between the two things actually measured, not derived from anything.
|
|
12
|
+
*
|
|
13
|
+
* Below it: this repo's own battery runs its API with `REFLECT_MS=6000` on purpose, and at the 5s
|
|
14
|
+
* this first shipped with, a healthy app with one slow route was permanently non-nominal and carried
|
|
15
|
+
* a scary number on every result.
|
|
16
|
+
*
|
|
17
|
+
* Above it: both reported wedges — 10.4s on one, 170s+ on the other. 10.4s clears this by 400ms,
|
|
18
|
+
* which is thin, and an app with an 8-second endpoint will not be warned about a wedge until 10s.
|
|
19
|
+
* That is the trade: an over-firing field gets ignored, and a field nobody reads catches nothing.
|
|
20
|
+
*/
|
|
21
|
+
export const PENDING_NAVIGATION_NOTICE_MS = 10_000;
|
|
22
|
+
/**
|
|
23
|
+
* Said beside `dispatched` / `settled` on a skewed session, because those fields read as success.
|
|
24
|
+
*
|
|
25
|
+
* The reporter's own account of the cost: "the false-positive dispatch confirmation is what cost the
|
|
26
|
+
* most time — I trusted `dispatched: true, domMutatedWithin: Nms` as real signal for a long time
|
|
27
|
+
* before suspecting the skew banner." Stating the contradiction is the whole job here; the existing
|
|
28
|
+
* banner was accurate and simply never appeared where it was needed.
|
|
29
|
+
*/
|
|
30
|
+
const SKEW_VERDICT_WARNING = 'version skew: `dispatched` and `settled` on this session mean the event was sent, NOT that the ' +
|
|
31
|
+
'app acted on it — a skewed pair drops actions silently. Re-read the control (aria-checked, ' +
|
|
32
|
+
'data-state, its text) before trusting any verdict from this session, and converge the versions:';
|
|
33
|
+
/**
|
|
34
|
+
* The age of the oldest request that STARTED and never completed, or undefined when there is none
|
|
35
|
+
* worth reporting.
|
|
36
|
+
*
|
|
37
|
+
* Matched on request id, the way `detectHungRequests` and the route oracle's `unansweredIn` do: a
|
|
38
|
+
* NET_PENDING with no NET_REQUEST carrying the same id. Three readings of one fact is two too many,
|
|
39
|
+
* but they read different windows — this one is the whole session, which is the point: a wedge that
|
|
40
|
+
* began before the current action is exactly the case a per-window reading cannot see.
|
|
41
|
+
*/
|
|
42
|
+
export function pendingNavigationMs(events, nowMs) {
|
|
43
|
+
const settled = new Set();
|
|
44
|
+
for (const e of events)
|
|
45
|
+
if (e.type === EventType.NET_REQUEST)
|
|
46
|
+
settled.add(e.data['id']);
|
|
47
|
+
let oldest;
|
|
48
|
+
for (const e of events) {
|
|
49
|
+
if (e.type !== EventType.NET_PENDING || settled.has(e.data['id']))
|
|
50
|
+
continue;
|
|
51
|
+
if (oldest === undefined || e.t < oldest)
|
|
52
|
+
oldest = e.t;
|
|
53
|
+
}
|
|
54
|
+
if (oldest === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
const age = nowMs - oldest;
|
|
57
|
+
return age >= PENDING_NAVIGATION_NOTICE_MS ? age : undefined;
|
|
58
|
+
}
|
|
2
59
|
/**
|
|
3
60
|
* Buffer-honesty envelope for observe/network/console. When the ring buffer has evicted anything
|
|
4
61
|
* (age/size cap), a "no such event" answer may be a false negative — so we attach the drop count and
|
|
@@ -20,9 +77,21 @@ export function bufferEnvelope(session) {
|
|
|
20
77
|
*/
|
|
21
78
|
export function healthEnvelope(session) {
|
|
22
79
|
const health = session.health();
|
|
23
|
-
|
|
80
|
+
// A request stuck in flight makes a session non-nominal even when the tab is fine, because it is
|
|
81
|
+
// the one condition where every observation is about to be about a page that is not moving.
|
|
82
|
+
const nominal = !health.throttled &&
|
|
83
|
+
health.focused &&
|
|
84
|
+
health.recommendation === undefined &&
|
|
85
|
+
health.pendingNavigationMs === undefined &&
|
|
86
|
+
health.versionSkew === undefined;
|
|
24
87
|
if (nominal)
|
|
25
88
|
return {};
|
|
89
|
+
// Skew outranks the throttle warning when both hold: a throttled tab makes a reading unreliable,
|
|
90
|
+
// a skewed link makes the ACTION unreliable, and there is no point warning about the quality of an
|
|
91
|
+
// observation of something that may never have happened.
|
|
92
|
+
if (health.versionSkew !== undefined) {
|
|
93
|
+
return { session: health, warning: `${SKEW_VERDICT_WARNING} ${health.versionSkew}` };
|
|
94
|
+
}
|
|
26
95
|
return health.throttled ? { session: health, warning: THROTTLED_WARNING } : { session: health };
|
|
27
96
|
}
|
|
28
97
|
/**
|
|
@@ -50,12 +119,23 @@ const STARVED_WAIT_NOTE = ' (the tab was throttled during this wait, so starvati
|
|
|
50
119
|
* Suffix the starvation note onto a FAILED predicate verdict when the tab is throttled. A pass is
|
|
51
120
|
* returned untouched — starvation is context for a failure, not a reason to doubt a hold. Pure and
|
|
52
121
|
* generic over the verdict shape so assert/wait_for/act_and_wait share one rule.
|
|
122
|
+
*
|
|
123
|
+
* The sentence FOLLOWS the field, and does not decide for itself. `annotateThrottledMiss`
|
|
124
|
+
* (predicate.ts) owns the question of whether this failure was reached by not having seen
|
|
125
|
+
* something, which is the only reading a starved tab casts doubt on; it stamps
|
|
126
|
+
* `THROTTLED_STARVED_NOTE` when it was. Deciding again here got two answers to one question:
|
|
127
|
+
* an `absent: true` assertion that MATCHED 13 elements was graded honestly in the field an agent
|
|
128
|
+
* gates on, and told in prose that the tab may never have rendered. It also doubled up on a failure
|
|
129
|
+
* that already carried a more specific reason (an unreadable locator, a superseded window), where
|
|
130
|
+
* the concrete diagnosis is the one that should lead.
|
|
53
131
|
*/
|
|
54
132
|
export function annotateStarvedFailure(session, verdict) {
|
|
55
133
|
if (true === verdict.pass || verdict.failureReason === undefined)
|
|
56
134
|
return verdict;
|
|
57
135
|
if (true !== session.throttled())
|
|
58
136
|
return verdict;
|
|
137
|
+
if (THROTTLED_STARVED_NOTE !== verdict.inconclusive)
|
|
138
|
+
return verdict;
|
|
59
139
|
return { ...verdict, failureReason: `${verdict.failureReason}${STARVED_WAIT_NOTE}` };
|
|
60
140
|
}
|
|
61
141
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { NoSessionReason } from '@reticlehq/core';
|
|
1
|
+
import type { NoSessionReason } from '@reticlehq/core/telemetry';
|
|
2
2
|
import { Session, type SessionInfo } from './session.js';
|
|
3
3
|
import { AttachmentHistory } from './attachment-history.js';
|
|
4
4
|
import type { NoSessionNextAction } from './no-session-next-action.js';
|
|
5
|
+
import { type SessionIdentity } from './session-successor.js';
|
|
5
6
|
/**
|
|
6
7
|
* The agent's active project, used to scope auto-selection. `projectId` is the stable build-stamped
|
|
7
8
|
* identity (authoritative when present); `url` is the app origin, a fallback hint for older SDKs that
|
|
@@ -61,6 +62,15 @@ export declare class SessionManager {
|
|
|
61
62
|
* vanished rather than about the daemon's lifetime history (#611).
|
|
62
63
|
*/
|
|
63
64
|
lastDeparted(): string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* The session that most recently disappeared — id, last URL, project — or undefined.
|
|
67
|
+
*
|
|
68
|
+
* `lastDeparted` is the id alone, which is what lease-expiry attribution needs. This is the
|
|
69
|
+
* rest: a route that 500s tears the page down and the SDK never reconnects, and without the
|
|
70
|
+
* URL `reticle_sessions` is an empty list with no history (#808). Tombstones, not live rows:
|
|
71
|
+
* a connected tab is in `list()`, not here.
|
|
72
|
+
*/
|
|
73
|
+
lastKnown(): SessionIdentity | undefined;
|
|
64
74
|
resolve(sessionId?: string, scope?: ResolveScope): Session;
|
|
65
75
|
}
|
|
66
76
|
export {};
|
|
@@ -303,9 +303,20 @@ export class SessionManager {
|
|
|
303
303
|
* vanished rather than about the daemon's lifetime history (#611).
|
|
304
304
|
*/
|
|
305
305
|
lastDeparted() {
|
|
306
|
+
return this.lastKnown()?.id;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* The session that most recently disappeared — id, last URL, project — or undefined.
|
|
310
|
+
*
|
|
311
|
+
* `lastDeparted` is the id alone, which is what lease-expiry attribution needs. This is the
|
|
312
|
+
* rest: a route that 500s tears the page down and the SDK never reconnects, and without the
|
|
313
|
+
* URL `reticle_sessions` is an empty list with no history (#808). Tombstones, not live rows:
|
|
314
|
+
* a connected tab is in `list()`, not here.
|
|
315
|
+
*/
|
|
316
|
+
lastKnown() {
|
|
306
317
|
let last;
|
|
307
|
-
for (const
|
|
308
|
-
last =
|
|
318
|
+
for (const record of this.#tombstones.values())
|
|
319
|
+
last = record;
|
|
309
320
|
return last;
|
|
310
321
|
}
|
|
311
322
|
/**
|
|
@@ -324,8 +335,12 @@ export class SessionManager {
|
|
|
324
335
|
#unknownSessionError(sessionId) {
|
|
325
336
|
const live = [...this.#sessions.values()];
|
|
326
337
|
if (0 === live.length) {
|
|
338
|
+
const known = this.lastKnown();
|
|
339
|
+
const where = undefined === known ? '' : ` Last seen on ${known.url}.`;
|
|
327
340
|
return (`no connected session with id '${sessionId}', and no sessions are connected at all — so ` +
|
|
328
|
-
'there is no other id to retry with.
|
|
341
|
+
'there is no other id to retry with.' +
|
|
342
|
+
where +
|
|
343
|
+
' The app is not dialling this daemon; call ' +
|
|
329
344
|
'reticle_sessions for the diagnosis rather than retrying this call.');
|
|
330
345
|
}
|
|
331
346
|
const named = live.map((s) => `'${s.id}' (${s.url})`).join(', ');
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "The page reconnected under the same id" — one sentence, and one way to recognise it.
|
|
3
|
+
*
|
|
4
|
+
* A server-rendered app reconnects its SDK on EVERY full page load, so on an MPA this is the normal
|
|
5
|
+
* consequence of following a link, not a fault. The bridge rejects any command that was in flight
|
|
6
|
+
* when it happens, and a caller that reads that rejection as fatal cannot walk past the first link:
|
|
7
|
+
* reported against `reticle_verify action=crawl`, which died on link one of every Django, Rails and
|
|
8
|
+
* plain-HTML app.
|
|
9
|
+
*
|
|
10
|
+
* The prefix lives here so the producer and the predicate cannot drift apart. This repo has already
|
|
11
|
+
* shipped that exact bug twice — a remedy string restated in a second place, and a redaction name
|
|
12
|
+
* derived two different ways — and both times the halves disagreed silently.
|
|
13
|
+
*/
|
|
14
|
+
/** The start of the reason the bridge hands to commands displaced by a reconnect. */
|
|
15
|
+
export declare const SESSION_REPLACED_PREFIX = "session replaced by a newer connection";
|
|
16
|
+
/** Build the full reason. The only place this sentence is written. */
|
|
17
|
+
export declare function sessionReplacedReason(id: string, url: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Was this failure a reconnect rather than a fault?
|
|
20
|
+
*
|
|
21
|
+
* Accepts the thrown value rather than a string so every call site can hand over whatever it caught
|
|
22
|
+
* without each one re-deriving a message — an `Error`, a bare string, or something else entirely.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isSessionReplacedError(error: unknown): boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "The page reconnected under the same id" — one sentence, and one way to recognise it.
|
|
3
|
+
*
|
|
4
|
+
* A server-rendered app reconnects its SDK on EVERY full page load, so on an MPA this is the normal
|
|
5
|
+
* consequence of following a link, not a fault. The bridge rejects any command that was in flight
|
|
6
|
+
* when it happens, and a caller that reads that rejection as fatal cannot walk past the first link:
|
|
7
|
+
* reported against `reticle_verify action=crawl`, which died on link one of every Django, Rails and
|
|
8
|
+
* plain-HTML app.
|
|
9
|
+
*
|
|
10
|
+
* The prefix lives here so the producer and the predicate cannot drift apart. This repo has already
|
|
11
|
+
* shipped that exact bug twice — a remedy string restated in a second place, and a redaction name
|
|
12
|
+
* derived two different ways — and both times the halves disagreed silently.
|
|
13
|
+
*/
|
|
14
|
+
/** The start of the reason the bridge hands to commands displaced by a reconnect. */
|
|
15
|
+
export const SESSION_REPLACED_PREFIX = 'session replaced by a newer connection';
|
|
16
|
+
/** Build the full reason. The only place this sentence is written. */
|
|
17
|
+
export function sessionReplacedReason(id, url) {
|
|
18
|
+
return `${SESSION_REPLACED_PREFIX} claiming the same id (${id}) from ${url}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Was this failure a reconnect rather than a fault?
|
|
22
|
+
*
|
|
23
|
+
* Accepts the thrown value rather than a string so every call site can hand over whatever it caught
|
|
24
|
+
* without each one re-deriving a message — an `Error`, a bare string, or something else entirely.
|
|
25
|
+
*/
|
|
26
|
+
export function isSessionReplacedError(error) {
|
|
27
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28
|
+
return message.includes(SESSION_REPLACED_PREFIX);
|
|
29
|
+
}
|
|
@@ -73,13 +73,29 @@ export function pickDocumentSuccessor(live, departed) {
|
|
|
73
73
|
const origin = originOf(departed.url);
|
|
74
74
|
if (origin === undefined)
|
|
75
75
|
return undefined;
|
|
76
|
+
// A candidate must belong to the same app. `sessionStorage` is scoped to the ORIGIN rather than
|
|
77
|
+
// the app, so recycling a port (stop app A on :3000, start app B, reload the tab) has app B read
|
|
78
|
+
// app A's id out of storage and register under it with its own projectId. The guard was on the
|
|
79
|
+
// candidate search only, and a matching id returned before it ran — so an explicit sessionId was
|
|
80
|
+
// answered by a different product, and every observation afterwards was about the wrong app
|
|
81
|
+
// (#785).
|
|
82
|
+
const sameProject = (s) => departed.projectId === undefined || s.projectId === departed.projectId;
|
|
76
83
|
const atOrigin = live.filter((s) => originOf(s.url) === origin);
|
|
77
84
|
const sameId = atOrigin.find((s) => s.id === departed.id);
|
|
85
|
+
// `mayInherit` deliberately does NOT apply here. It asks whether ANOTHER session may take over a
|
|
86
|
+
// claimed identity, and a matching id is not another session — it is this one, reconnecting with
|
|
87
|
+
// the id that survived in sessionStorage. The claim lives in a URL PARAM, which an ordinary
|
|
88
|
+
// navigation drops (`navigate { url, reload: true }` reloads the bare address), so demanding it
|
|
89
|
+
// again would refuse the reload this whole mechanism exists for. A leased context has its own
|
|
90
|
+
// sessionStorage, so a human's tab cannot pick the id up that way — only by carrying the param,
|
|
91
|
+
// which satisfies `mayInherit` anyway. The gate would cost the ordinary case and buy nothing.
|
|
92
|
+
//
|
|
93
|
+
// Refusal, not a second guess, when the project does not match: an exact id match belonging to
|
|
94
|
+
// another app is the strongest evidence there is that the caller's id is stale, so falling
|
|
95
|
+
// through to whatever else sits on the origin would be the silent redirect this prevents.
|
|
78
96
|
if (sameId !== undefined)
|
|
79
|
-
return sameId;
|
|
80
|
-
const others = atOrigin.filter((s) => s.id !== departed.id &&
|
|
81
|
-
(departed.projectId === undefined || s.projectId === departed.projectId) &&
|
|
82
|
-
mayInherit(departed, s));
|
|
97
|
+
return sameProject(sameId) ? sameId : undefined;
|
|
98
|
+
const others = atOrigin.filter((s) => s.id !== departed.id && sameProject(s) && mayInherit(departed, s));
|
|
83
99
|
if (1 !== others.length)
|
|
84
100
|
return undefined;
|
|
85
101
|
return others[0];
|
|
@@ -43,6 +43,15 @@ export declare class Session {
|
|
|
43
43
|
hasCapabilities: boolean;
|
|
44
44
|
/** Set when the page's SDK version differs from the daemon's (see version-skew.ts). */
|
|
45
45
|
versionSkew?: string;
|
|
46
|
+
/** SDK version from HELLO; kept so a remedy can check it applies — see body-capture-remedy.ts. */
|
|
47
|
+
sdkVersion?: string | undefined;
|
|
48
|
+
/** Whether the page records network bodies; undefined on an SDK too old to say. See HELLO. */
|
|
49
|
+
captureBodies?: boolean | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Whether this build stamps `data-reticle-source`, when the build plugin said. See HELLO.
|
|
52
|
+
* `false` separates "the project turned it off" from "nothing provides one"; undefined is unknown.
|
|
53
|
+
*/
|
|
54
|
+
sourceMapping?: boolean | undefined;
|
|
46
55
|
/**
|
|
47
56
|
* Extra key names this app declared sensitive via `connect({ redact: { keys } })`. Held so the
|
|
48
57
|
* DRIVEN path can redact them too — a request body the daemon captures from the network stack
|
|
@@ -86,6 +95,9 @@ export declare class Session {
|
|
|
86
95
|
get currentDocumentId(): string | undefined;
|
|
87
96
|
/** The edit epoch the most recent stamped event was observed under. See `#editEpoch`. */
|
|
88
97
|
get currentEditEpoch(): number | undefined;
|
|
98
|
+
/** Seed/auth precondition failure reason, if any. */
|
|
99
|
+
setPreconditionFailure(reason: string): void;
|
|
100
|
+
preconditionFailure(): string | undefined;
|
|
89
101
|
/** Re-stamp an incoming event with server-relative time, buffer it, and fan out. */
|
|
90
102
|
pushEvent(event: ReticleEvent, byteSize?: number): void;
|
|
91
103
|
recordActedRef(ref: string): void;
|
|
@@ -264,9 +276,5 @@ export declare class Session {
|
|
|
264
276
|
/** Wired by SessionManager.add, the one place every session registers. */
|
|
265
277
|
setViewers(read: () => Session[]): void;
|
|
266
278
|
}
|
|
267
|
-
/**
|
|
268
|
-
* Re-exported from session-manager.ts so the public import path (`./session.js`) is unchanged for
|
|
269
|
-
* the many call sites that resolve a target session. The class lives in its own file to keep both
|
|
270
|
-
* units under the file-size cap.
|
|
271
|
-
*/
|
|
279
|
+
/** Re-exported so the public import path (`./session.js`) is unchanged. */
|
|
272
280
|
export { SessionManager } from './session-manager.js';
|
package/dist/session/session.js
CHANGED
|
@@ -3,7 +3,7 @@ import { LastAct } from './last-act.js';
|
|
|
3
3
|
import { GapLedger } from '../honesty/gap-ledger.js';
|
|
4
4
|
import { CaptureLedger } from '../honesty/feature-capture.js';
|
|
5
5
|
import { commandTimeoutMessage } from './command-timeout.js';
|
|
6
|
-
import { readHealthEvent } from './session-health.js';
|
|
6
|
+
import { readHealthEvent, pendingNavigationMs } from './session-health.js';
|
|
7
7
|
import { MIRRORED_COMMANDS, mirroredNarration } from './session-mirror.js';
|
|
8
8
|
/** The HUD does not need 200 frames to watch a counter climb; the last state always lands. */
|
|
9
9
|
const IMPACT_PUSH_DEBOUNCE_MS = 700;
|
|
@@ -86,6 +86,15 @@ export class Session {
|
|
|
86
86
|
hasCapabilities;
|
|
87
87
|
/** Set when the page's SDK version differs from the daemon's (see version-skew.ts). */
|
|
88
88
|
versionSkew;
|
|
89
|
+
/** SDK version from HELLO; kept so a remedy can check it applies — see body-capture-remedy.ts. */
|
|
90
|
+
sdkVersion;
|
|
91
|
+
/** Whether the page records network bodies; undefined on an SDK too old to say. See HELLO. */
|
|
92
|
+
captureBodies;
|
|
93
|
+
/**
|
|
94
|
+
* Whether this build stamps `data-reticle-source`, when the build plugin said. See HELLO.
|
|
95
|
+
* `false` separates "the project turned it off" from "nothing provides one"; undefined is unknown.
|
|
96
|
+
*/
|
|
97
|
+
sourceMapping;
|
|
89
98
|
/**
|
|
90
99
|
* Extra key names this app declared sensitive via `connect({ redact: { keys } })`. Held so the
|
|
91
100
|
* DRIVEN path can redact them too — a request body the daemon captures from the network stack
|
|
@@ -128,28 +137,16 @@ export class Session {
|
|
|
128
137
|
#journalReader;
|
|
129
138
|
/** What this session has learned by watching its own stream: ambient churn + blind-spot levels. */
|
|
130
139
|
#observed = new ObservedState();
|
|
140
|
+
/** Set when a required application precondition (e.g. seedStorage) was not established. */
|
|
141
|
+
#preconditionFailure;
|
|
131
142
|
/**
|
|
132
143
|
* Which document is on screen right now — the one the most recent stamped event was observed under.
|
|
133
|
-
*
|
|
134
|
-
* DERIVED, never announced. The SDK mints a document id once per real document and stamps it on
|
|
135
|
-
* every event, so the stream already carries the answer; asking the page for it separately would be
|
|
136
|
-
* a second source of truth that can disagree with the evidence it is supposed to scope. A full
|
|
137
|
-
* navigation or a reload builds a new document, mints a new id, and the first event carrying it
|
|
138
|
-
* moves this forward — which is exactly the moment the previous document's evidence stops being
|
|
139
|
-
* about the world. An SPA route change keeps the same document and so keeps the same id, which is
|
|
140
|
-
* correct rather than a limitation: same JavaScript context, same in-flight requests, same evidence.
|
|
141
|
-
*
|
|
142
|
-
* An UNSTAMPED event never clears this. An SDK older than the field stamps nothing, and letting one
|
|
143
|
-
* such event blank the current document would make every later comparison vacuous.
|
|
144
|
+
* DERIVED, never announced: the SDK stamps it on events. An unstamped event never clears this.
|
|
144
145
|
*/
|
|
145
146
|
#documentId;
|
|
146
147
|
/**
|
|
147
|
-
* The edit epoch of the most recent stamped event. DERIVED for the same reason `#documentId` is
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* A hot update advances it INSIDE the same document, which is the case `#documentId` structurally
|
|
151
|
-
* cannot see — no navigation, same page, replaced code. An unstamped event never clears it: most
|
|
152
|
-
* pages have no hot-update channel at all, so absence is "unknown", never "back to no edits".
|
|
148
|
+
* The edit epoch of the most recent stamped event. DERIVED for the same reason `#documentId` is.
|
|
149
|
+
* A hot update advances it inside the same document. An unstamped event never clears it.
|
|
153
150
|
*/
|
|
154
151
|
#editEpoch;
|
|
155
152
|
constructor(hello, socket, clock) {
|
|
@@ -159,6 +156,7 @@ export class Session {
|
|
|
159
156
|
this.title = hello.title;
|
|
160
157
|
this.adapters = hello.adapters;
|
|
161
158
|
this.hasCapabilities = hello.hasCapabilities ?? false;
|
|
159
|
+
this.sourceMapping = hello.sourceMapping;
|
|
162
160
|
this.redactKeys = hello.redactKeys ?? [];
|
|
163
161
|
this.#socket = socket;
|
|
164
162
|
this.#clock = clock;
|
|
@@ -214,10 +212,17 @@ export class Session {
|
|
|
214
212
|
}
|
|
215
213
|
/** The attachable health block — single source of truth for the tools. */
|
|
216
214
|
health() {
|
|
215
|
+
// From event t=0, not a cursor: a wedge that began before the current action is exactly the
|
|
216
|
+
// case a per-window reading cannot see, and is the one both reporters hit.
|
|
217
|
+
const stuck = pendingNavigationMs(this.eventsSince(0), this.elapsed());
|
|
217
218
|
const base = {
|
|
218
219
|
lastSeenMs: this.lastSeenMs(),
|
|
219
220
|
throttled: this.throttled(),
|
|
220
221
|
focused: this.#focused,
|
|
222
|
+
...(stuck === undefined ? {} : { pendingNavigationMs: stuck }),
|
|
223
|
+
// Carried onto every act/assert result, not just reticle_sessions: skew drops actions
|
|
224
|
+
// SILENTLY, and the fields it contradicts are on the act verdict.
|
|
225
|
+
...(this.versionSkew === undefined ? {} : { versionSkew: this.versionSkew }),
|
|
221
226
|
};
|
|
222
227
|
// attach the escape-hatch hint only when un-scriptable (keeps field absent otherwise).
|
|
223
228
|
const recommendation = buildSessionRecommendation({
|
|
@@ -268,6 +273,13 @@ export class Session {
|
|
|
268
273
|
get currentEditEpoch() {
|
|
269
274
|
return this.#editEpoch;
|
|
270
275
|
}
|
|
276
|
+
/** Seed/auth precondition failure reason, if any. */
|
|
277
|
+
setPreconditionFailure(reason) {
|
|
278
|
+
this.#preconditionFailure = reason;
|
|
279
|
+
}
|
|
280
|
+
preconditionFailure() {
|
|
281
|
+
return this.#preconditionFailure;
|
|
282
|
+
}
|
|
271
283
|
/** Re-stamp an incoming event with server-relative time, buffer it, and fan out. */
|
|
272
284
|
pushEvent(event, byteSize) {
|
|
273
285
|
if (event.documentId !== undefined)
|
|
@@ -875,9 +887,5 @@ export class Session {
|
|
|
875
887
|
}
|
|
876
888
|
}
|
|
877
889
|
}
|
|
878
|
-
/**
|
|
879
|
-
* Re-exported from session-manager.ts so the public import path (`./session.js`) is unchanged for
|
|
880
|
-
* the many call sites that resolve a target session. The class lives in its own file to keep both
|
|
881
|
-
* units under the file-size cap.
|
|
882
|
-
*/
|
|
890
|
+
/** Re-exported so the public import path (`./session.js`) is unchanged. */
|
|
883
891
|
export { SessionManager } from './session-manager.js';
|
|
@@ -40,7 +40,7 @@ export interface PlatformPaths {
|
|
|
40
40
|
readonly linux: string;
|
|
41
41
|
readonly win32: string;
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
interface AgentClient {
|
|
44
44
|
readonly id: string;
|
|
45
45
|
readonly name: string;
|
|
46
46
|
readonly confidence: AgentConfidence;
|
|
@@ -68,7 +68,7 @@ export interface AgentPlanStep {
|
|
|
68
68
|
readonly entry?: Readonly<Record<string, unknown>> | undefined;
|
|
69
69
|
readonly why: string;
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
interface AgentPlanInput {
|
|
72
72
|
readonly home: string;
|
|
73
73
|
readonly platform: keyof PlatformPaths;
|
|
74
74
|
readonly exists: (path: string) => boolean;
|
|
@@ -94,3 +94,4 @@ export declare const joinFor: (os: keyof PlatformPaths) => ((...parts: string[])
|
|
|
94
94
|
* makes the Windows rows testable from any machine.
|
|
95
95
|
*/
|
|
96
96
|
export declare function planAgentConfigs(input: AgentPlanInput): AgentPlanStep[];
|
|
97
|
+
export {};
|
|
@@ -17,7 +17,7 @@ export interface AgentWriterIo {
|
|
|
17
17
|
readonly writeFile: (path: string, contents: string) => void;
|
|
18
18
|
readonly mkdirp: (dir: string) => void;
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
interface AgentWriteResult {
|
|
21
21
|
readonly id: string;
|
|
22
22
|
readonly name: string;
|
|
23
23
|
readonly file: string;
|
|
@@ -35,7 +35,7 @@ export interface AgentWriteResult {
|
|
|
35
35
|
export declare function applyAgentPlan(plan: readonly AgentPlanStep[], io: AgentWriterIo): AgentWriteResult[];
|
|
36
36
|
/** The `/reticle` instruction, for agents that load skills from a directory. */
|
|
37
37
|
export declare const RETICLE_SKILL = "---\nname: reticle\ndescription: Verify a change against the running app, from inside it, before calling it done.\n---\n\nReticle exposes the running app to you as `reticle_*` MCP tools. Look, act, observe, assert.\n\nOnly `reticle_act_and_wait` and `reticle_assert` produce a verdict. Everything else moves or\nreads the app and proves nothing, so a drive that ends without one of those has no result however\nmany tools it used.\n\n`verified: \"unknown\"` is not a pass: it means Reticle could not tell what happened.\n`verified: \"no-fault\"` is not a pass either, because nothing was declared to prove. Never weaken a\ncheck to make it pass.\n\nThe cheapest path that answers the question:\n\n- \"did my edit break anything?\" -> `reticle_run({ tool: \"reticle_verify\", args: { action: \"change\", files: [...] } })`\n- \"does this known journey still work?\" -> `reticle_run({ tool: \"reticle_flow_replay\", args: { flowName: \"...\" } })`\n- \"does this new behaviour work?\" -> `reticle_act_sequence` for the setup, then ONE `reticle_act_and_wait`\n";
|
|
38
|
-
|
|
38
|
+
interface SkillWriteResult {
|
|
39
39
|
readonly id: string;
|
|
40
40
|
readonly file: string;
|
|
41
41
|
}
|
|
@@ -49,3 +49,4 @@ export declare function applyAgentSkills(io: AgentWriterIo, where: {
|
|
|
49
49
|
readonly home: string;
|
|
50
50
|
readonly platform: keyof PlatformPaths;
|
|
51
51
|
}): SkillWriteResult[];
|
|
52
|
+
export {};
|
|
@@ -33,7 +33,7 @@ export interface MigrationDeps {
|
|
|
33
33
|
readonly version: string;
|
|
34
34
|
readonly log: (event: string, data: Record<string, unknown>) => void;
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
interface MigrationResult {
|
|
37
37
|
readonly ran: boolean;
|
|
38
38
|
readonly granted: readonly string[];
|
|
39
39
|
readonly deferred: readonly string[];
|
|
@@ -45,3 +45,4 @@ export interface MigrationResult {
|
|
|
45
45
|
* re-scan on every editor launch for the rest of the version's life.
|
|
46
46
|
*/
|
|
47
47
|
export declare function migrateApprovals(deps: MigrationDeps): MigrationResult;
|
|
48
|
+
export {};
|
|
@@ -47,7 +47,7 @@ export function migrateApprovals(deps) {
|
|
|
47
47
|
}
|
|
48
48
|
let results = [];
|
|
49
49
|
try {
|
|
50
|
-
results = grantAutoApproval(io, { home, platform }
|
|
50
|
+
results = grantAutoApproval(io, { home, platform });
|
|
51
51
|
}
|
|
52
52
|
catch {
|
|
53
53
|
return NOTHING;
|
|
@@ -13,9 +13,12 @@
|
|
|
13
13
|
* defensible; the same edit made globally would hand the same pass to every other MCP server
|
|
14
14
|
* they ever install, including ones that reach the network. Their approval gate is not ours to
|
|
15
15
|
* dismantle, only ours to step out of.
|
|
16
|
-
* 2. Nothing is
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* 2. Nothing is CREATED where the file itself takes over settings we cannot read — Cursor's
|
|
17
|
+
* permissions.json supersedes its in-app allowlist and pins the run mode to Allowlist, so a user
|
|
18
|
+
* who ran everything without asking loses that and cannot switch back while the file exists.
|
|
19
|
+
* Merging into one they already have is fine; making one is not, so that case is DEFERRED with
|
|
20
|
+
* the in-app step to take instead. Nothing is written at all for an agent that is not installed:
|
|
21
|
+
* an absent agent has no Accept button to remove.
|
|
19
22
|
*
|
|
20
23
|
* Codex is absent by design: its approval policy is global (`approval_policy`), so there is no
|
|
21
24
|
* reticle-shaped rule to write, and its config is TOML, which this repo never rewrites. Its
|
|
@@ -36,8 +39,8 @@ export interface ApprovalGrant {
|
|
|
36
39
|
/** Adds our rule to a parsed config, leaving everything else exactly as it was. */
|
|
37
40
|
readonly grant: (current: Record<string, unknown>) => Record<string, unknown>;
|
|
38
41
|
/**
|
|
39
|
-
* True where
|
|
40
|
-
*
|
|
42
|
+
* True where CREATING this file supersedes in-app settings we can neither read nor restore. Such
|
|
43
|
+
* a grant is only ever merged into a file the user already owns, never created.
|
|
41
44
|
*/
|
|
42
45
|
readonly supersedesInApp?: boolean;
|
|
43
46
|
}
|
|
@@ -60,26 +63,15 @@ export interface ApprovalResult {
|
|
|
60
63
|
/** Set where the user has to be told something the write alone does not say. */
|
|
61
64
|
readonly warn?: string;
|
|
62
65
|
}
|
|
63
|
-
|
|
66
|
+
interface ApprovalWhere {
|
|
64
67
|
readonly home: string;
|
|
65
68
|
readonly platform: keyof PlatformPaths;
|
|
66
69
|
}
|
|
67
|
-
export interface ApprovalOptions {
|
|
68
|
-
/**
|
|
69
|
-
* Refuse any grant that would CREATE a file superseding an allowlist we cannot read.
|
|
70
|
-
*
|
|
71
|
-
* Set on the unattended path. Writing Cursor's permissions.json for the first time takes over
|
|
72
|
-
* from what the user approved inside the app, which is a fair trade when they just ran a command
|
|
73
|
-
* and can read the line saying so, and not a fair trade at all when a version bump did it behind
|
|
74
|
-
* them: their OTHER MCP servers would start prompting again and nothing would connect that to us.
|
|
75
|
-
* Merging into a file they already own stays safe either way.
|
|
76
|
-
*/
|
|
77
|
-
readonly onlyIfNoSupersede?: boolean;
|
|
78
|
-
}
|
|
79
70
|
/**
|
|
80
71
|
* Pre-approve Reticle's tools everywhere the machine has an agent that would otherwise ask.
|
|
81
72
|
*
|
|
82
73
|
* A client that cannot be written is reported, never thrown: one unwritable settings file is not a
|
|
83
74
|
* reason to leave the other three prompting.
|
|
84
75
|
*/
|
|
85
|
-
export declare function grantAutoApproval(io: AgentWriterIo, where: ApprovalWhere, grants?: readonly ApprovalGrant[]
|
|
76
|
+
export declare function grantAutoApproval(io: AgentWriterIo, where: ApprovalWhere, grants?: readonly ApprovalGrant[]): ApprovalResult[];
|
|
77
|
+
export {};
|
|
@@ -13,9 +13,12 @@
|
|
|
13
13
|
* defensible; the same edit made globally would hand the same pass to every other MCP server
|
|
14
14
|
* they ever install, including ones that reach the network. Their approval gate is not ours to
|
|
15
15
|
* dismantle, only ours to step out of.
|
|
16
|
-
* 2. Nothing is
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* 2. Nothing is CREATED where the file itself takes over settings we cannot read — Cursor's
|
|
17
|
+
* permissions.json supersedes its in-app allowlist and pins the run mode to Allowlist, so a user
|
|
18
|
+
* who ran everything without asking loses that and cannot switch back while the file exists.
|
|
19
|
+
* Merging into one they already have is fine; making one is not, so that case is DEFERRED with
|
|
20
|
+
* the in-app step to take instead. Nothing is written at all for an agent that is not installed:
|
|
21
|
+
* an absent agent has no Accept button to remove.
|
|
19
22
|
*
|
|
20
23
|
* Codex is absent by design: its approval policy is global (`approval_policy`), so there is no
|
|
21
24
|
* reticle-shaped rule to write, and its config is TOML, which this repo never rewrites. Its
|
|
@@ -54,8 +57,11 @@ export const APPROVAL_GRANTS = [
|
|
|
54
57
|
markers: [home('.cursor')],
|
|
55
58
|
rule: 'mcpAllowlist += "reticle:*"',
|
|
56
59
|
// Documented: "when a key appears in permissions.json, it fully replaces the in-app allowlist
|
|
57
|
-
// for that type"
|
|
58
|
-
//
|
|
60
|
+
// for that type" — and in practice it costs more than that. A non-empty mcpAllowlist also locks
|
|
61
|
+
// the run mode to Allowlist, so a user who had Run Everything ("YOLO") on loses it and cannot
|
|
62
|
+
// turn it back on while the file exists; Cursor has acknowledged that as their bug. Merging into
|
|
63
|
+
// a file the user already made is safe — they are already in Allowlist mode. Creating one is
|
|
64
|
+
// not, so we never do: see the DEFERRED branch below.
|
|
59
65
|
supersedesInApp: true,
|
|
60
66
|
grant: (c) => addToList(c, null, 'mcpAllowlist', `${RETICLE_KEY}:*`),
|
|
61
67
|
},
|
|
@@ -107,7 +113,7 @@ export const ApprovalOutcome = {
|
|
|
107
113
|
* A client that cannot be written is reported, never thrown: one unwritable settings file is not a
|
|
108
114
|
* reason to leave the other three prompting.
|
|
109
115
|
*/
|
|
110
|
-
export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS
|
|
116
|
+
export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS) {
|
|
111
117
|
const join = joinFor(where.platform);
|
|
112
118
|
return grants.map((grant) => {
|
|
113
119
|
const file = join(where.home, grant.paths[where.platform]);
|
|
@@ -117,11 +123,11 @@ export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS, options =
|
|
|
117
123
|
return { ...base, outcome: ApprovalOutcome.ABSENT };
|
|
118
124
|
try {
|
|
119
125
|
const existed = io.exists(file);
|
|
120
|
-
if (true ===
|
|
126
|
+
if (true === grant.supersedesInApp && !existed) {
|
|
121
127
|
return {
|
|
122
128
|
...base,
|
|
123
129
|
outcome: ApprovalOutcome.DEFERRED,
|
|
124
|
-
warn: `creating ${file} would
|
|
130
|
+
warn: `left ${grant.name}'s approvals alone — creating ${file} would take over its in-app settings and pin the run mode to Allowlist. If ${grant.name} asks before every reticle tool call, add \`${RETICLE_KEY}:*\` under Settings → Agents → Approvals & Execution (nothing to do if you run everything already).`,
|
|
125
131
|
};
|
|
126
132
|
}
|
|
127
133
|
// A settings file we cannot parse is left exactly as it is. Reformatting somebody's config to
|
|
@@ -134,10 +140,7 @@ export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS, options =
|
|
|
134
140
|
return { ...base, outcome: ApprovalOutcome.ALREADY };
|
|
135
141
|
io.mkdirp(file.slice(0, Math.max(0, file.lastIndexOf('/'))));
|
|
136
142
|
io.writeFile(file, `${JSON.stringify(next, null, INDENT)}\n`);
|
|
137
|
-
|
|
138
|
-
? `${file} now governs which MCP tools run without asking; anything you had approved inside ${grant.name} itself will ask again until it is listed here too`
|
|
139
|
-
: undefined;
|
|
140
|
-
return { ...base, outcome: ApprovalOutcome.GRANTED, ...(undefined === warn ? {} : { warn }) };
|
|
143
|
+
return { ...base, outcome: ApprovalOutcome.GRANTED };
|
|
141
144
|
}
|
|
142
145
|
catch (err) {
|
|
143
146
|
return {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
import { InitConfirmation, RETICLE_DEFAULT_PORT } from '@reticlehq/core';
|
|
32
32
|
import { fetchStatus, summarizeStatus } from '../cli/cli-launch.js';
|
|
33
33
|
import { readDevServers } from '../daemon/dev-servers.js';
|
|
34
|
-
import { devServersForProject } from '@reticlehq/core';
|
|
34
|
+
import { devServersForProject } from '@reticlehq/core/artifacts';
|
|
35
35
|
import { readProjectId } from '../cli/cli-port.js';
|
|
36
36
|
import { reticleStateHome } from '../daemon/daemon.js';
|
|
37
37
|
import { reportInitOutcome } from '../telemetry/init-telemetry.js';
|
|
@@ -24,7 +24,7 @@ export declare const AppShape: {
|
|
|
24
24
|
export type AppShape = (typeof AppShape)[keyof typeof AppShape];
|
|
25
25
|
export declare const isDesktop: (shape: AppShape) => boolean;
|
|
26
26
|
/** Files and dependencies that name the shell, read through an injected reader. */
|
|
27
|
-
|
|
27
|
+
interface ShapeEvidence {
|
|
28
28
|
/** `src-tauri/tauri.conf.json` exists. */
|
|
29
29
|
readonly hasTauriConf: boolean;
|
|
30
30
|
/** `electron` is a dependency or devDependency. */
|
|
@@ -44,7 +44,7 @@ export declare function isDesktopOrigin(url: string): boolean;
|
|
|
44
44
|
* Returned as data rather than branched at each call site, so the differences are visible in one
|
|
45
45
|
* place and testable without a desktop runtime.
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
interface ShapePolicy {
|
|
48
48
|
/** Open a browser at the url. False for desktop: the app window is already the client. */
|
|
49
49
|
readonly openBrowser: boolean;
|
|
50
50
|
/** Require an HTTP response before looking for a session. */
|
|
@@ -55,3 +55,4 @@ export interface ShapePolicy {
|
|
|
55
55
|
readonly note: string | undefined;
|
|
56
56
|
}
|
|
57
57
|
export declare function policyFor(shape: AppShape): ShapePolicy;
|
|
58
|
+
export {};
|