@reticlehq/server 3.1.0 → 3.3.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 +30 -28
- package/dist/command/cli/adhoc-suite.d.ts +19 -0
- package/dist/command/cli/adhoc-suite.js +88 -0
- package/dist/command/cli/adhoc-verdict.d.ts +23 -2
- package/dist/command/cli/adhoc-verdict.js +18 -10
- package/dist/command/cli/auth/device-flow.d.ts +2 -2
- package/dist/command/cli/cli-doctor.js +37 -5
- package/dist/command/cli/cli-flow-commands.d.ts +2 -1
- package/dist/command/cli/cli-kill.js +1 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
- package/dist/command/cli/cli-parse-grammar.js +7 -0
- package/dist/command/cli/cli-parse-verify.d.ts +2 -5
- package/dist/command/cli/cli-parse-verify.js +86 -13
- package/dist/command/cli/cli-parse.d.ts +8 -1
- package/dist/command/cli/cli-parse.js +38 -7
- package/dist/command/cli/cli-verify.d.ts +59 -0
- package/dist/command/cli/cli-verify.js +131 -19
- package/dist/command/cli/cloud-cli.js +7 -6
- package/dist/command/cli/cloud-kit.d.ts +0 -2
- package/dist/command/cli/cloud-kit.js +3 -7
- package/dist/command/cli/config/config-discovery.d.ts +12 -0
- package/dist/command/cli/config/config-discovery.js +19 -0
- package/dist/command/cli/doctor/doctor-rows.d.ts +12 -0
- package/dist/command/cli/doctor/doctor-rows.js +12 -0
- package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
- package/dist/command/cli/drive/drive-command.js +1 -1
- package/dist/command/cli/launch/cli-launch.d.ts +3 -23
- package/dist/command/cli/launch/cli-launch.js +29 -70
- package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +33 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +198 -0
- package/dist/command/cli/mcp-command.js +5 -3
- package/dist/command/cli/ports/resolve/cli-port.d.ts +15 -1
- package/dist/command/cli/ports/resolve/cli-port.js +50 -2
- package/dist/command/cli/report-command.d.ts +16 -0
- package/dist/command/cli/report-command.js +97 -0
- package/dist/command/cli/status/status-lines.d.ts +28 -0
- package/dist/command/cli/status/status-lines.js +117 -0
- package/dist/command/cli.d.ts +1 -1
- package/dist/command/cli.js +29 -177
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +32 -0
- package/dist/command/daemon/binding/daemon-status-probe.js +93 -0
- package/dist/command/daemon/binding/loopback-reach.d.ts +42 -0
- package/dist/command/daemon/binding/loopback-reach.js +63 -0
- package/dist/command/daemon/binding/port-presence.d.ts +1 -1
- package/dist/command/daemon/daemon-resilience.d.ts +13 -1
- package/dist/command/daemon/daemon-resilience.js +12 -1
- package/dist/command/daemon/daemon.d.ts +2 -11
- package/dist/command/daemon/daemon.js +8 -11
- package/dist/command/daemon/idle-shutdown.d.ts +2 -1
- package/dist/command/daemon/lifetime/heartbeat.d.ts +8 -0
- package/dist/command/daemon/lifetime/heartbeat.js +10 -0
- package/dist/command/daemon/lifetime/log-cap.d.ts +68 -0
- package/dist/command/daemon/lifetime/log-cap.js +115 -0
- package/dist/command/setup/agent-notice-memory.d.ts +16 -0
- package/dist/command/setup/agent-notice-memory.js +65 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +1 -0
- package/dist/command/setup/bringup/dev-server-wait.js +16 -0
- package/dist/command/setup/bringup/ensure-daemon.js +1 -1
- package/dist/command/setup/init/init-host.js +8 -0
- package/dist/command/setup/init/init-runtime.js +13 -1
- package/dist/command/setup/node-effects.js +1 -1
- package/dist/command/setup/probe/page-probe.js +9 -2
- package/dist/command/setup/run-setup.d.ts +25 -7
- package/dist/command/setup/run-setup.js +51 -9
- package/dist/command/setup/setup-command.js +31 -3
- package/dist/command/setup/terminal/confirm.js +2 -1
- package/dist/command/update/updater.d.ts +27 -0
- package/dist/command/update/updater.js +35 -2
- package/dist/features/crawl/crawl-tools.js +3 -1
- package/dist/features/crawl/crawl.d.ts +2 -0
- package/dist/features/crawl/crawl.js +3 -1
- package/dist/features/harness/drive-report.d.ts +73 -0
- package/dist/features/harness/drive-report.js +196 -0
- package/dist/features/harness/driver.js +29 -8
- package/dist/features/harness/drivers.d.ts +19 -0
- package/dist/features/harness/drivers.js +19 -0
- package/dist/features/harness/fill-values.d.ts +67 -0
- package/dist/features/harness/fill-values.js +153 -0
- package/dist/features/harness/jev-driver.d.ts +188 -0
- package/dist/features/harness/jev-driver.js +661 -0
- package/dist/features/harness/openai-driver.d.ts +46 -0
- package/dist/features/harness/openai-driver.js +197 -0
- package/dist/features/harness/platform-config.d.ts +62 -0
- package/dist/features/harness/platform-config.js +97 -0
- package/dist/features/visual/visual-tools.js +26 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +80 -130
- package/dist/judgement/capsule/capsule-store.d.ts +31 -2
- package/dist/judgement/capsule/capsule-store.js +58 -6
- package/dist/judgement/capsule/capsule.js +5 -0
- package/dist/judgement/capsule/causal-summary.d.ts +13 -1
- package/dist/judgement/capsule/causal-summary.js +7 -0
- package/dist/judgement/domain/domain-model.js +38 -9
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
- package/dist/judgement/phenomena/phenomena.d.ts +2 -1
- package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
- package/dist/judgement/runs/artifact/gap-report.js +46 -0
- package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
- package/dist/judgement/runs/artifact/gap-summary.js +85 -0
- package/dist/judgement/runs/artifact/render-report.js +4 -1
- package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
- package/dist/judgement/runs/artifact/to-artifact.js +1 -1
- package/dist/judgement/runs/context-tools.js +21 -3
- package/dist/judgement/runs/drive-run.js +7 -2
- package/dist/judgement/runs/verification-sync.d.ts +2 -2
- package/dist/judgement/runs/verification-sync.js +9 -2
- package/dist/language/flows/affected-tools.js +6 -3
- package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
- package/dist/language/flows/annotate-notes/annotate.js +66 -18
- package/dist/language/flows/flow-anchor.d.ts +12 -0
- package/dist/language/flows/flow-anchor.js +20 -0
- package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
- package/dist/language/flows/flow-expect-grammar.js +92 -11
- package/dist/language/flows/flow-intent.js +1 -1
- package/dist/language/flows/flow-learning.d.ts +3 -0
- package/dist/language/flows/flow-learning.js +39 -8
- package/dist/language/flows/flow-replay-run.d.ts +55 -10
- package/dist/language/flows/flow-replay-run.js +217 -58
- package/dist/language/flows/flow-replay-types.d.ts +9 -0
- package/dist/language/flows/flow-replay.d.ts +29 -11
- package/dist/language/flows/flow-replay.js +221 -203
- package/dist/language/flows/flow-result.d.ts +18 -1
- package/dist/language/flows/flow-result.js +18 -2
- package/dist/language/flows/flow-step-runners.d.ts +26 -10
- package/dist/language/flows/flow-step-runners.js +20 -4
- package/dist/language/flows/flow-store-for-session.d.ts +2 -1
- package/dist/language/flows/flow-success.d.ts +21 -6
- package/dist/language/flows/flow-success.js +50 -138
- package/dist/language/flows/flow-tools.js +8 -4
- package/dist/language/flows/flows.d.ts +15 -10
- package/dist/language/flows/flows.js +17 -4
- package/dist/language/flows/heal.d.ts +9 -1
- package/dist/language/flows/heal.js +24 -2
- package/dist/language/flows/learned-sources.d.ts +21 -0
- package/dist/language/flows/learned-sources.js +84 -0
- package/dist/language/flows/narrow-write.d.ts +4 -4
- package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
- package/dist/language/flows/recording/recording-backtrack.js +1 -1
- package/dist/language/flows/recording/replay-halt.d.ts +2 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +2 -2
- package/dist/language/flows/replay.js +13 -9
- package/dist/language/flows/stores/annotation-store.d.ts +5 -5
- package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
- package/dist/language/flows/stores/flake-store.js +2 -3
- package/dist/language/flows/suite/coverage.d.ts +1 -1
- package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
- package/dist/language/flows/suite-selection.d.ts +15 -1
- package/dist/language/flows/suite-selection.js +1 -1
- package/dist/language/flows/verify-change-tools.js +21 -4
- package/dist/memory/cloud/account-state.d.ts +1 -1
- package/dist/memory/cloud/account-state.js +5 -4
- package/dist/memory/cloud/cloud-config.d.ts +9 -0
- package/dist/memory/cloud/cloud-config.js +9 -0
- package/dist/memory/cloud/cloud-sync.d.ts +7 -4
- package/dist/memory/cloud/cloud-sync.js +11 -6
- package/dist/memory/cloud/harness-config.d.ts +12 -0
- package/dist/memory/cloud/harness-config.js +58 -0
- package/dist/memory/cloud/harness-offer.d.ts +44 -0
- package/dist/memory/cloud/harness-offer.js +146 -0
- package/dist/memory/cloud/harness-switch.d.ts +11 -0
- package/dist/memory/cloud/harness-switch.js +53 -0
- package/dist/memory/cloud/sync-cycle.d.ts +12 -0
- package/dist/memory/cloud/sync-cycle.js +29 -4
- package/dist/memory/cloud/sync-daemon.d.ts +35 -2
- package/dist/memory/cloud/sync-daemon.js +45 -0
- package/dist/memory/cloud/sync-disk.js +11 -6
- package/dist/memory/impact/impact-recorder.d.ts +8 -0
- package/dist/memory/impact/impact-recorder.js +2 -1
- package/dist/memory/impact/impact-store.d.ts +5 -0
- package/dist/memory/impact/impact-store.js +25 -0
- package/dist/memory/intent/inline-intent.js +8 -3
- package/dist/memory/intent/intent-shard-store.d.ts +33 -47
- package/dist/memory/intent/intent-shard-store.js +253 -141
- package/dist/memory/intent/intent-shard.d.ts +22 -23
- package/dist/memory/intent/intent-shard.js +1 -1
- package/dist/memory/intent/intent-store.d.ts +13 -8
- package/dist/memory/intent/intent-store.js +46 -90
- package/dist/memory/intent/intent-tools.js +34 -23
- package/dist/memory/journal/ambient-store.js +2 -3
- package/dist/memory/journal/attach-journal.d.ts +11 -0
- package/dist/memory/journal/attach-journal.js +1 -1
- package/dist/memory/journal/deviation-report.js +4 -1
- package/dist/memory/journal/deviation-service.js +10 -2
- package/dist/memory/journal/drive-flow.d.ts +2 -2
- package/dist/memory/journal/drive-flow.js +21 -12
- package/dist/memory/journal/envelope-store.js +2 -3
- package/dist/memory/journal/journal-query.d.ts +35 -1
- package/dist/memory/journal/journal-query.js +46 -0
- package/dist/memory/journal/journal-recorder.d.ts +71 -2
- package/dist/memory/journal/journal-recorder.js +45 -7
- package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
- package/dist/memory/journal/on-disk/retain-policy.js +44 -0
- package/dist/memory/journal/on-disk/retention.d.ts +91 -2
- package/dist/memory/journal/on-disk/retention.js +181 -11
- package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
- package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -2
- package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -38
- package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
- package/dist/memory/journal/on-disk/workspace-tiers.js +88 -0
- package/dist/memory/journal/route-template.d.ts +22 -0
- package/dist/memory/journal/route-template.js +44 -0
- package/dist/memory/journal/self-observation.d.ts +3 -0
- package/dist/memory/journal/self-observation.js +67 -0
- package/dist/memory/journal/session-end.d.ts +17 -4
- package/dist/memory/journal/session-end.js +85 -7
- package/dist/memory/journal/session-journal.d.ts +122 -5
- package/dist/memory/journal/session-journal.js +489 -32
- package/dist/memory/project/artifact-root-resolver.d.ts +3 -0
- package/dist/memory/project/artifact-root-resolver.js +89 -0
- package/dist/memory/project/artifact-root.d.ts +39 -2
- package/dist/memory/project/artifact-root.js +58 -1
- package/dist/memory/project/baselines.d.ts +1 -1
- package/dist/memory/project/dir/fill-value-store.d.ts +32 -0
- package/dist/memory/project/dir/fill-value-store.js +88 -0
- package/dist/memory/project/dir/reticle-dir.d.ts +15 -5
- package/dist/memory/project/dir/reticle-dir.js +11 -0
- package/dist/memory/project/fs/fs-port.d.ts +22 -1
- package/dist/memory/project/fs/fs-port.js +72 -5
- package/dist/memory/project/fs/write-atomic.d.ts +26 -0
- package/dist/memory/project/fs/write-atomic.js +48 -0
- package/dist/memory/project/learned-routes.d.ts +16 -2
- package/dist/memory/project/learned-routes.js +26 -10
- package/dist/memory/project/project-for-root.d.ts +30 -0
- package/dist/memory/project/project-for-root.js +43 -0
- package/dist/memory/project/project-tools.js +10 -5
- package/dist/memory/project/session-root.d.ts +35 -6
- package/dist/memory/project/session-root.js +70 -8
- package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
- package/dist/memory/recall/prior/connection-memory.js +8 -0
- package/dist/memory/recall/registered-projects.d.ts +2 -1
- package/dist/portal/bridge/auth-failure-reason.d.ts +9 -0
- package/dist/portal/bridge/auth-failure-reason.js +20 -3
- package/dist/portal/bridge/bridge.d.ts +6 -1
- package/dist/portal/bridge/bridge.js +36 -2
- package/dist/portal/input/real-input.js +2 -1
- package/dist/portal/pool/browser-pool.d.ts +10 -0
- package/dist/portal/pool/browser-pool.js +10 -0
- package/dist/portal/pool/playwright-launcher.js +1 -0
- package/dist/portal/pool/pool-contract.d.ts +2 -0
- package/dist/portal/pool/storage-seed.d.ts +2 -1
- package/dist/portal/pool/zero-install.d.ts +27 -0
- package/dist/portal/pool/zero-install.js +54 -0
- package/dist/portal/realm/web-realm.js +6 -0
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
- package/dist/portal/session/dev-server/served-document.d.ts +21 -0
- package/dist/portal/session/dev-server/served-document.js +57 -0
- package/dist/portal/session/facts/background-traffic.d.ts +9 -0
- package/dist/portal/session/facts/background-traffic.js +25 -0
- package/dist/portal/session/facts/session-replaced.d.ts +19 -0
- package/dist/portal/session/facts/session-replaced.js +22 -0
- package/dist/portal/session/live-control-tools.js +48 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +1 -0
- package/dist/portal/session/no-session-diagnosis.js +67 -60
- package/dist/portal/session/no-session-next-action.js +3 -2
- package/dist/portal/session/no-session-watch.js +14 -10
- package/dist/portal/session/presence/attachment-history.d.ts +13 -2
- package/dist/portal/session/rebind.d.ts +18 -0
- package/dist/portal/session/rebind.js +38 -0
- package/dist/portal/session/session-health.d.ts +61 -0
- package/dist/portal/session/session-health.js +40 -1
- package/dist/portal/session/session-info.d.ts +6 -0
- package/dist/portal/session/session-manager.d.ts +13 -0
- package/dist/portal/session/session-manager.js +42 -3
- package/dist/portal/session/session-successor.d.ts +11 -0
- package/dist/portal/session/session-successor.js +14 -1
- package/dist/portal/session/session-verdict-facts.d.ts +28 -0
- package/dist/portal/session/session-verdict-facts.js +15 -0
- package/dist/portal/session/session.d.ts +26 -10
- package/dist/portal/session/session.js +48 -49
- package/dist/status-payload.d.ts +12 -1
- package/dist/status-payload.js +4 -1
- package/dist/surface/http-server.js +3 -5
- package/dist/surface/mcp/faults/mcp-outage.d.ts +2 -1
- package/dist/surface/mcp/faults/mcp-outage.js +20 -4
- package/dist/surface/mcp/mcp-proxy.d.ts +10 -1
- package/dist/surface/mcp/mcp-proxy.js +23 -12
- package/dist/surface/mcp/mcp.d.ts +10 -1
- package/dist/surface/mcp/mcp.js +59 -29
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +15 -2
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +22 -5
- package/dist/surface/mcp/proxy/proxy-log.d.ts +5 -11
- package/dist/surface/mcp/proxy/proxy-log.js +7 -22
- package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
- package/dist/surface/mcp/server-instructions.js +12 -1
- package/dist/surface/tools/act/act-preflight.js +6 -1
- package/dist/surface/tools/act/already-true.d.ts +40 -0
- package/dist/surface/tools/act/already-true.js +36 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +6 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.js +13 -0
- package/dist/surface/tools/act/sequence-grade.js +13 -0
- package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
- package/dist/surface/tools/act/settle-in-flight.js +29 -6
- package/dist/surface/tools/act-capsule.js +49 -8
- package/dist/surface/tools/act-tools.js +25 -24
- package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
- package/dist/surface/tools/args/numeric-bounds.js +30 -3
- package/dist/surface/tools/assert/assert-grade.js +14 -2
- package/dist/surface/tools/assert/assert-verdict.js +74 -21
- package/dist/surface/tools/browser-tools.js +4 -3
- package/dist/surface/tools/coverage-tools.js +5 -2
- package/dist/surface/tools/dynamic-tools.js +43 -46
- package/dist/surface/tools/error-recovery.d.ts +6 -0
- package/dist/surface/tools/error-recovery.js +15 -0
- package/dist/surface/tools/explore-tools.js +80 -7
- package/dist/surface/tools/feedback-tools.d.ts +2 -1
- package/dist/surface/tools/feedback-tools.js +50 -3
- package/dist/surface/tools/first-sentence.d.ts +24 -0
- package/dist/surface/tools/first-sentence.js +0 -0
- package/dist/surface/tools/harness-explore.d.ts +125 -1
- package/dist/surface/tools/harness-explore.js +401 -15
- package/dist/surface/tools/harness-plan.d.ts +76 -0
- package/dist/surface/tools/harness-plan.js +156 -0
- package/dist/surface/tools/harness-toolset.js +12 -1
- package/dist/surface/tools/invoke-tool.js +82 -9
- package/dist/surface/tools/lease-hint.d.ts +8 -0
- package/dist/surface/tools/lease-hint.js +10 -0
- package/dist/surface/tools/lease-tools.d.ts +16 -28
- package/dist/surface/tools/lease-tools.js +53 -5
- package/dist/surface/tools/lineage-tools.js +1 -1
- package/dist/surface/tools/live-call-text.d.ts +6 -0
- package/dist/surface/tools/live-call-text.js +36 -2
- package/dist/surface/tools/observe-tools.js +69 -15
- package/dist/surface/tools/read/query-windowed.d.ts +1 -1
- package/dist/surface/tools/read/query-windowed.js +1 -1
- package/dist/surface/tools/read-tools.js +1 -2
- package/dist/surface/tools/real-input-attempt.d.ts +0 -5
- package/dist/surface/tools/real-input-attempt.js +1 -1
- package/dist/surface/tools/tool-kit.d.ts +23 -3
- package/dist/surface/tools/tool-kit.js +6 -1
- package/dist/surface/tools/tool-surface.js +17 -5
- package/dist/surface/tools/tools-helpers.d.ts +8 -7
- package/dist/surface/tools/tools-helpers.js +11 -13
- package/dist/surface/tools/tools.js +70 -30
- package/dist/telemetry/bug-found.js +32 -2
- package/dist/telemetry/feedback-context.js +3 -3
- package/dist/telemetry/feedback-local.d.ts +1 -0
- package/dist/telemetry/feedback-local.js +1 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/harness-drive.d.ts +6 -0
- package/dist/telemetry/harness-drive.js +39 -0
- package/dist/telemetry/hud-metrics.d.ts +32 -0
- package/dist/telemetry/hud-metrics.js +95 -0
- package/dist/telemetry/mcp-connection.d.ts +14 -1
- package/dist/telemetry/mcp-connection.js +17 -2
- package/dist/telemetry/onboarding-firsts.d.ts +30 -1
- package/dist/telemetry/onboarding-firsts.js +54 -12
- package/dist/telemetry/session-metrics.d.ts +5 -0
- package/dist/telemetry/session-metrics.js +15 -1
- package/dist/telemetry/verification-of.js +31 -3
- package/dist/wire-journal.d.ts +31 -0
- package/dist/wire-journal.js +100 -0
- package/docs/README.md +1 -0
- package/docs/agent-cheatsheet.md +7 -3
- package/docs/autodrive.md +42 -2
- package/docs/benchmarks.md +1 -1
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/cloud.mdx +11 -9
- package/docs/cli/doctor.mdx +1 -0
- package/docs/cli/init.mdx +1 -0
- package/docs/cli/report.mdx +44 -0
- package/docs/cli/status.mdx +35 -20
- package/docs/cli.mdx +8 -18
- package/docs/desktop.mdx +1 -1
- package/docs/faq.mdx +1 -1
- package/docs/flows.md +11 -0
- package/docs/frameworks.mdx +113 -1
- package/docs/install-agentic.mdx +36 -0
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/predicates.mdx +144 -4
- package/docs/system-map.md +6 -4
- package/docs/telemetry.md +2 -1
- package/docs/token-efficiency.md +2 -2
- package/docs/tools/overview.mdx +1 -1
- package/docs/tools/query.mdx +10 -10
- package/docs/troubleshooting.mdx +8 -2
- package/docs/usage.md +2 -2
- package/docs/what-is-recorded.md +62 -0
- package/package.json +5 -5
- package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
- package/dist/judgement/outcome/predicate-to-expect.js +0 -148
|
@@ -2,7 +2,7 @@ import { type SeedStorage } from '@reticlehq/core';
|
|
|
2
2
|
import type { InitScriptHandle, PooledContext, PooledCookie, PooledPage } from './pool-contract.js';
|
|
3
3
|
export declare function targetOriginOf(navUrl: string): string | undefined;
|
|
4
4
|
export declare function normalizeCookies(cookies: SeedStorage['cookies'], navUrl: string): PooledCookie[];
|
|
5
|
-
|
|
5
|
+
interface StorageSeedResult {
|
|
6
6
|
handle?: InitScriptHandle;
|
|
7
7
|
checkError?: () => void;
|
|
8
8
|
}
|
|
@@ -21,3 +21,4 @@ export interface StorageSeedResult {
|
|
|
21
21
|
* has resolved, ensuring one-time semantics (reloads or future navigations do not reapply the seed).
|
|
22
22
|
*/
|
|
23
23
|
export declare function seedStorageInto(context: PooledContext, page: PooledPage, targetUrl: string, seed: SeedStorage): Promise<StorageSeedResult | void>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface InjectedConnect {
|
|
2
|
+
url: string;
|
|
3
|
+
/** Absent when this daemon runs without a pairing token; the bridge then asks for none. */
|
|
4
|
+
token?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The connect options a page is given when WE supply the connection rather than the app.
|
|
8
|
+
*
|
|
9
|
+
* One builder for both injection paths — this one, and re-pointing an SDK a hosted preview already
|
|
10
|
+
* ships (`real-input.ts`) — so the two cannot come to disagree about what a daemon-driven page needs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function injectedConnectArgs(opts: InjectedConnect): Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* The script for a page: install the SDK if the page has none, then connect it to this daemon.
|
|
15
|
+
*
|
|
16
|
+
* The guard is on the SINGLETON, not on a timer: a page that loaded its own SDK already holds
|
|
17
|
+
* `__reticleInstance`, and evaluating a second copy would register a second session for one tab.
|
|
18
|
+
* `connect` is a no-op once connected, so calling it on the page's own instance is safe.
|
|
19
|
+
*/
|
|
20
|
+
export declare function zeroInstallScript(bundle: string, opts: InjectedConnect): string;
|
|
21
|
+
/**
|
|
22
|
+
* The single-file build, or undefined when this install has none.
|
|
23
|
+
*
|
|
24
|
+
* Read once and kept: it is ~450KB and identical for every lease. Absent means an older browser
|
|
25
|
+
* package or a broken install, and the caller then behaves exactly as it did before this existed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function readReaderBundle(): string | undefined;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zero-install reader: how a page whose app never ran `init` still reaches a verdict.
|
|
3
|
+
*
|
|
4
|
+
* `@reticlehq/browser` builds itself into one file (`dist/reticle-inject.js`). The daemon reads that
|
|
5
|
+
* file as TEXT and evaluates it in a leased page that never connected, so the server never imports DOM
|
|
6
|
+
* code and the page needs no build step. What comes back is an ordinary session that reports no
|
|
7
|
+
* framework adapter and no source mapping, which is the honest description of what it can see.
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
/** The subpath `@reticlehq/browser` exports its single-file build under. */
|
|
12
|
+
const READER_SPECIFIER = '@reticlehq/browser/inject';
|
|
13
|
+
/**
|
|
14
|
+
* The connect options a page is given when WE supply the connection rather than the app.
|
|
15
|
+
*
|
|
16
|
+
* One builder for both injection paths — this one, and re-pointing an SDK a hosted preview already
|
|
17
|
+
* ships (`real-input.ts`) — so the two cannot come to disagree about what a daemon-driven page needs.
|
|
18
|
+
*/
|
|
19
|
+
export function injectedConnectArgs(opts) {
|
|
20
|
+
return {
|
|
21
|
+
allowNonLocalhost: true,
|
|
22
|
+
...(opts.token === undefined ? {} : { token: opts.token }),
|
|
23
|
+
url: opts.url,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The script for a page: install the SDK if the page has none, then connect it to this daemon.
|
|
28
|
+
*
|
|
29
|
+
* The guard is on the SINGLETON, not on a timer: a page that loaded its own SDK already holds
|
|
30
|
+
* `__reticleInstance`, and evaluating a second copy would register a second session for one tab.
|
|
31
|
+
* `connect` is a no-op once connected, so calling it on the page's own instance is safe.
|
|
32
|
+
*/
|
|
33
|
+
export function zeroInstallScript(bundle, opts) {
|
|
34
|
+
const args = JSON.stringify(injectedConnectArgs(opts));
|
|
35
|
+
return `(() => {\nif (!globalThis.__reticleInstance) {\n${bundle}\n}\nglobalThis.__reticleInstance.connect(${args});\n})();`;
|
|
36
|
+
}
|
|
37
|
+
let cached;
|
|
38
|
+
/**
|
|
39
|
+
* The single-file build, or undefined when this install has none.
|
|
40
|
+
*
|
|
41
|
+
* Read once and kept: it is ~450KB and identical for every lease. Absent means an older browser
|
|
42
|
+
* package or a broken install, and the caller then behaves exactly as it did before this existed.
|
|
43
|
+
*/
|
|
44
|
+
export function readReaderBundle() {
|
|
45
|
+
if (cached === undefined) {
|
|
46
|
+
try {
|
|
47
|
+
cached = readFileSync(createRequire(import.meta.url).resolve(READER_SPECIFIER), 'utf8');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
cached = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return cached ?? undefined;
|
|
54
|
+
}
|
|
@@ -93,6 +93,11 @@ export const CHANNEL_OF_PREFIX = {
|
|
|
93
93
|
// channel at all.
|
|
94
94
|
anim: ProtocolChannel.UI,
|
|
95
95
|
dialog: ProtocolChannel.UI,
|
|
96
|
+
// A form field's value is on the subject's surface in exactly the sense `ui` means: it is what
|
|
97
|
+
// the person is looking at and typing into. The protocol has no `input` channel and does not
|
|
98
|
+
// want one — a field is a rendered thing, and the observation carries the field's NAME and
|
|
99
|
+
// length, never its contents for a password or a card.
|
|
100
|
+
field: ProtocolChannel.UI,
|
|
96
101
|
focus: ProtocolChannel.UI,
|
|
97
102
|
render: ProtocolChannel.UI,
|
|
98
103
|
reveal: ProtocolChannel.UI,
|
|
@@ -124,6 +129,7 @@ export const NOT_EVIDENCE = {
|
|
|
124
129
|
context: "Reticle's own context tool opening, not something the application did",
|
|
125
130
|
flow: 'a flow being recorded by the tool',
|
|
126
131
|
human: 'a person driving the HUD: pause, resume, a mark. The operator, not the subject',
|
|
132
|
+
hud: "usage telemetry about Reticle's own panel, taken off before the buffer. The tool, not the subject",
|
|
127
133
|
page: "the SDK's own health report about the page it is inside",
|
|
128
134
|
sdk: 'the SDK itself failing, which is a fact about the observer',
|
|
129
135
|
transport: "the tool's own buffer overflowing",
|
|
@@ -25,7 +25,7 @@ export declare const PortState: {
|
|
|
25
25
|
/** Answered with a document: a dev server serving an app. */
|
|
26
26
|
readonly SERVES_DOCUMENT: "serves-document";
|
|
27
27
|
};
|
|
28
|
-
|
|
28
|
+
type PortState = (typeof PortState)[keyof typeof PortState];
|
|
29
29
|
/** The probe's answer for one port, however it was obtained. */
|
|
30
30
|
export declare function classifyPort(port: number, probe: (port: number) => Promise<PortState>): Promise<PortState>;
|
|
31
31
|
/**
|
|
@@ -71,3 +71,4 @@ export declare function probeDevServerStates(ports?: readonly number[], probe?:
|
|
|
71
71
|
}>;
|
|
72
72
|
/** Every candidate port serving a document, in ascending order. Never rejects. */
|
|
73
73
|
export declare function probeDevServers(ports?: readonly number[], probe?: (port: number) => Promise<boolean>): Promise<number[]>;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The document a local dev server actually serves: its CSP header and HTML, read once and failing
|
|
3
|
+
* soft. Shared by `doctor` (is a policy blocking the bridge?) and a lease that never dialled (was
|
|
4
|
+
* it the page's own policy?), which is why it lives beside the dev-server probe and not in either.
|
|
5
|
+
*/
|
|
6
|
+
/** What a served page said about its policy. The same shape `diagnoseObservedWebCsp` reads. */
|
|
7
|
+
export interface ObservedWebDocument {
|
|
8
|
+
url: string;
|
|
9
|
+
headers: string[];
|
|
10
|
+
html: string;
|
|
11
|
+
}
|
|
12
|
+
export type PageRequest = (url: string, init: RequestInit) => Promise<Response>;
|
|
13
|
+
interface ConnectedDocument {
|
|
14
|
+
url: string;
|
|
15
|
+
projectId?: string;
|
|
16
|
+
}
|
|
17
|
+
/** A connected tab for this project, or on its observed origin, disproves a CSP diagnosis. */
|
|
18
|
+
export declare function hasConnectedDocument(sessions: readonly ConnectedDocument[], documentUrls: readonly string[], projectId?: string): boolean;
|
|
19
|
+
/** Read the document a live local dev server actually sends, failing soft like the rest of doctor. */
|
|
20
|
+
export declare function observeWebDocument(rawUrl: string, request?: PageRequest): Promise<ObservedWebDocument | undefined>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const CSP_HEADER = 'content-security-policy';
|
|
2
|
+
const DOCUMENT_TIMEOUT_MS = 1_000;
|
|
3
|
+
const LOOPBACK_HOSTS = ['localhost', '127.0.0.1', '[::1]'];
|
|
4
|
+
const requestPage = (url, init) => fetch(url, init);
|
|
5
|
+
function loopbackDocumentUrl(raw) {
|
|
6
|
+
try {
|
|
7
|
+
const url = new URL(raw);
|
|
8
|
+
if (!['http:', 'https:'].includes(url.protocol))
|
|
9
|
+
return undefined;
|
|
10
|
+
return LOOPBACK_HOSTS.includes(url.hostname) ? url : undefined;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function originOf(raw) {
|
|
17
|
+
try {
|
|
18
|
+
return new URL(raw).origin;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** A connected tab for this project, or on its observed origin, disproves a CSP diagnosis. */
|
|
25
|
+
export function hasConnectedDocument(sessions, documentUrls, projectId) {
|
|
26
|
+
if (projectId !== undefined && sessions.some((session) => session.projectId === projectId)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
const origins = new Set(documentUrls.map(originOf).filter((value) => value !== undefined));
|
|
30
|
+
return sessions.some((session) => {
|
|
31
|
+
const origin = originOf(session.url);
|
|
32
|
+
return undefined !== origin && origins.has(origin);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** Read the document a live local dev server actually sends, failing soft like the rest of doctor. */
|
|
36
|
+
export async function observeWebDocument(rawUrl, request = requestPage) {
|
|
37
|
+
const url = loopbackDocumentUrl(rawUrl);
|
|
38
|
+
if (url === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
try {
|
|
41
|
+
const response = await request(url.href, {
|
|
42
|
+
redirect: 'error',
|
|
43
|
+
signal: AbortSignal.timeout(DOCUMENT_TIMEOUT_MS),
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok)
|
|
46
|
+
return undefined;
|
|
47
|
+
const policy = response.headers.get(CSP_HEADER);
|
|
48
|
+
return {
|
|
49
|
+
url: url.href,
|
|
50
|
+
headers: null === policy ? [] : [policy],
|
|
51
|
+
html: await response.text(),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read `background` out of an already-parsed `.reticle.json`: the same-origin endpoints the app calls
|
|
3
|
+
* on its own (telemetry, heartbeats), which must not hold a settle window open or contradict a claim.
|
|
4
|
+
*
|
|
5
|
+
* Takes the record rather than a path, like `readRetainPolicy`, because the config search belongs to
|
|
6
|
+
* the startup code that already does it once. Never throws, and drops a bad entry rather than the
|
|
7
|
+
* whole list.
|
|
8
|
+
*/
|
|
9
|
+
export declare function readBackgroundTraffic(config: Record<string, unknown> | undefined): string[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The key a project writes in `.reticle.json`. */
|
|
2
|
+
const BACKGROUND_KEY = 'background';
|
|
3
|
+
/**
|
|
4
|
+
* Shorter than this names no endpoint. `/` and `//` are refused on this rule, and so is anything a
|
|
5
|
+
* mistype could leave behind.
|
|
6
|
+
*
|
|
7
|
+
* ponytail: a length floor, not a proof. `/api` passes and would still exclude the whole backend;
|
|
8
|
+
* the entries a verdict ignored are reported on it, which is what keeps a too-broad pattern visible.
|
|
9
|
+
*/
|
|
10
|
+
const MIN_PATTERN_LENGTH = 3;
|
|
11
|
+
function isEndpointPattern(value) {
|
|
12
|
+
return 'string' === typeof value && value.replace(/\//g, '').length >= MIN_PATTERN_LENGTH - 1;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Read `background` out of an already-parsed `.reticle.json`: the same-origin endpoints the app calls
|
|
16
|
+
* on its own (telemetry, heartbeats), which must not hold a settle window open or contradict a claim.
|
|
17
|
+
*
|
|
18
|
+
* Takes the record rather than a path, like `readRetainPolicy`, because the config search belongs to
|
|
19
|
+
* the startup code that already does it once. Never throws, and drops a bad entry rather than the
|
|
20
|
+
* whole list.
|
|
21
|
+
*/
|
|
22
|
+
export function readBackgroundTraffic(config) {
|
|
23
|
+
const raw = config?.[BACKGROUND_KEY];
|
|
24
|
+
return Array.isArray(raw) ? raw.filter(isEndpointPattern) : [];
|
|
25
|
+
}
|
|
@@ -22,3 +22,22 @@ export declare function sessionReplacedReason(id: string, url: string): string;
|
|
|
22
22
|
* without each one re-deriving a message — an `Error`, a bare string, or something else entirely.
|
|
23
23
|
*/
|
|
24
24
|
export declare function isSessionReplacedError(error: unknown): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The reason every command still in flight is rejected with when the socket under it closes.
|
|
27
|
+
*
|
|
28
|
+
* A full-document navigation closes that socket, so on an MPA — or on any click that loads a new
|
|
29
|
+
* page — this arrives for the exact command that CAUSED it, and the successor is still connecting.
|
|
30
|
+
* `SessionManager.remove` cannot tell those two apart at the moment it runs: it is called from the
|
|
31
|
+
* socket's own close handler, the tombstone it writes is what a successor is later matched against,
|
|
32
|
+
* and nothing has arrived yet. So the rejection stays a plain transport failure, as
|
|
33
|
+
* `PendingCommands.rejectAll` documents, and a caller that CAN wait decides what it means.
|
|
34
|
+
*/
|
|
35
|
+
export declare const SESSION_DISCONNECTED_REASON = "session disconnected";
|
|
36
|
+
/**
|
|
37
|
+
* Is this failure the page's transport going away, rather than an answer from the page?
|
|
38
|
+
*
|
|
39
|
+
* True for both shapes the same event takes: the reconnect that claimed the id back, and the close
|
|
40
|
+
* that landed before any successor did. A caller holding a budget can follow either; a caller that
|
|
41
|
+
* cannot must say it stopped watching, never that the app failed.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isDocumentGoneError(error: unknown): boolean;
|
|
@@ -27,3 +27,25 @@ export function isSessionReplacedError(error) {
|
|
|
27
27
|
const message = error instanceof Error ? error.message : String(error);
|
|
28
28
|
return message.includes(SESSION_REPLACED_PREFIX);
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* The reason every command still in flight is rejected with when the socket under it closes.
|
|
32
|
+
*
|
|
33
|
+
* A full-document navigation closes that socket, so on an MPA — or on any click that loads a new
|
|
34
|
+
* page — this arrives for the exact command that CAUSED it, and the successor is still connecting.
|
|
35
|
+
* `SessionManager.remove` cannot tell those two apart at the moment it runs: it is called from the
|
|
36
|
+
* socket's own close handler, the tombstone it writes is what a successor is later matched against,
|
|
37
|
+
* and nothing has arrived yet. So the rejection stays a plain transport failure, as
|
|
38
|
+
* `PendingCommands.rejectAll` documents, and a caller that CAN wait decides what it means.
|
|
39
|
+
*/
|
|
40
|
+
export const SESSION_DISCONNECTED_REASON = 'session disconnected';
|
|
41
|
+
/**
|
|
42
|
+
* Is this failure the page's transport going away, rather than an answer from the page?
|
|
43
|
+
*
|
|
44
|
+
* True for both shapes the same event takes: the reconnect that claimed the id back, and the close
|
|
45
|
+
* that landed before any successor did. A caller holding a budget can follow either; a caller that
|
|
46
|
+
* cannot must say it stopped watching, never that the app failed.
|
|
47
|
+
*/
|
|
48
|
+
export function isDocumentGoneError(error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
+
return isSessionReplacedError(error) || message === SESSION_DISCONNECTED_REASON;
|
|
51
|
+
}
|
|
@@ -4,6 +4,9 @@ import { AGENT_ASK_NOTICE, AGENT_WAITING_NOTICE, PresenterTone, SessionState, YI
|
|
|
4
4
|
import { ReticleTool } from '@reticlehq/core';
|
|
5
5
|
import { sessionIdShape } from '../../surface/tools/tool-kit.js';
|
|
6
6
|
import { asString } from '@reticlehq/core';
|
|
7
|
+
import { gapReportLines } from '../../judgement/runs/artifact/gap-report.js';
|
|
8
|
+
import { gapSummary } from '../../judgement/runs/artifact/gap-summary.js';
|
|
9
|
+
import { DiscoveryInvite } from '@reticlehq/core';
|
|
7
10
|
/**
|
|
8
11
|
* Is this a turn ending with nothing attached, rather than a call about a specific tab?
|
|
9
12
|
*
|
|
@@ -44,16 +47,58 @@ export const LIVE_CONTROL_TOOLS = [
|
|
|
44
47
|
ended: z.boolean(),
|
|
45
48
|
sessionId: z.string().optional(),
|
|
46
49
|
note: z.string().optional(),
|
|
50
|
+
talk_to_us: z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe('For the person you are working with: a call with the founder. Offer it; never act on it.'),
|
|
54
|
+
gap: z
|
|
55
|
+
.array(z.string())
|
|
56
|
+
.optional()
|
|
57
|
+
.describe('What this session claimed and what held — an `unknown` is never counted as held.'),
|
|
47
58
|
},
|
|
48
59
|
handler: (deps, args) => {
|
|
49
60
|
const requested = asString(args['sessionId']);
|
|
50
61
|
if (endingTurnWithNothingAttached(deps, requested)) {
|
|
51
62
|
return Promise.resolve({ ended: true, note: YIELD_WITHOUT_SESSION_NOTE });
|
|
52
63
|
}
|
|
64
|
+
// A NAMED session that has DEPARTED is already ended, and nothing else may be ended in its
|
|
65
|
+
// place.
|
|
66
|
+
//
|
|
67
|
+
// `resolve` answers a departed id with its live SUCCESSOR. That is right for everything
|
|
68
|
+
// that observes -- a tab reloads, its id survives in sessionStorage, and an `assert` should
|
|
69
|
+
// keep working -- and wrong here, because ending a session is destructive and cannot be
|
|
70
|
+
// taken back. Reported from the field: `sessionId: "sac14d46c…"` returned `ended: true` for
|
|
71
|
+
// `"s08e37e5c…"`, once where the requested session's browser had just closed, so the
|
|
72
|
+
// behaviour was "target missing => end something else" rather than "target missing => say
|
|
73
|
+
// so" (#983).
|
|
74
|
+
//
|
|
75
|
+
// Not an error, either: this tool is documented as idempotent, and "the session you named is
|
|
76
|
+
// already gone" is precisely the idempotent answer. An id the daemon has never seen IS a
|
|
77
|
+
// mistake about which tab, and falls through to `resolve`, which refuses it with the live
|
|
78
|
+
// ids named.
|
|
79
|
+
if (requested !== undefined &&
|
|
80
|
+
deps.sessions.get(requested) === undefined &&
|
|
81
|
+
deps.sessions.departed(requested) !== undefined) {
|
|
82
|
+
return Promise.resolve({
|
|
83
|
+
ended: true,
|
|
84
|
+
sessionId: requested,
|
|
85
|
+
note: `session '${requested}' had already disconnected, so there was nothing to end. ` +
|
|
86
|
+
'Nothing else was ended: a named session is matched exactly, never substituted.',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Resolved synchronously, as before: a named session that does not exist still throws here
|
|
90
|
+
// rather than becoming a rejected promise the caller did not expect.
|
|
53
91
|
const session = deps.sessions.resolve(requested);
|
|
54
|
-
|
|
55
|
-
session.
|
|
56
|
-
|
|
92
|
+
const summary = asString(args['summary']);
|
|
93
|
+
return session.readJournalActions().then((actions) => {
|
|
94
|
+
// The moment the agent calls the task complete is when "and what did not hold" is worth a
|
|
95
|
+
// line: the same fold `reticle_context` and `reticle report` serve, so they cannot disagree.
|
|
96
|
+
const gap = gapReportLines(gapSummary(actions));
|
|
97
|
+
const panel = [summary, gap[0]].filter((line) => line !== undefined);
|
|
98
|
+
// One PRESENTER push for the transition; the summary and the gap headline ride together.
|
|
99
|
+
session.setState(SessionState.ENDED, 0 === panel.length ? undefined : panel.join('\n'));
|
|
100
|
+
return { ended: true, sessionId: session.id, gap, talk_to_us: DiscoveryInvite.AGENT };
|
|
101
|
+
});
|
|
57
102
|
},
|
|
58
103
|
},
|
|
59
104
|
{
|
|
@@ -165,6 +165,7 @@ export declare const SELF_RECOVERING_MARKER = "Then call reticle_sessions again
|
|
|
165
165
|
export declare function explainNoSession(facts: NoSessionFacts): {
|
|
166
166
|
reason: NoSessionReason;
|
|
167
167
|
message: string;
|
|
168
|
+
detail?: string;
|
|
168
169
|
};
|
|
169
170
|
/**
|
|
170
171
|
* The message alone, for the callers that render it.
|
|
@@ -129,8 +129,10 @@ const NUXT_STALE_BUNDLE = 'This project is Nuxt: the most likely cause by a dist
|
|
|
129
129
|
const RESTARTED_LEAD = 'no browser session connected. This daemon has served none since it started, but it is a NEW ' +
|
|
130
130
|
'process — an app for this project HAS connected on this port before, which is recorded ' +
|
|
131
131
|
'durably. So the wiring is correct and the port is correct: what is failing now is the SDK ' +
|
|
132
|
-
'reaching initialise on the page.
|
|
133
|
-
|
|
132
|
+
'reaching initialise on the page.';
|
|
133
|
+
/** Why the obvious move is the wrong one here. Reasoning, so it rides in the detail. */
|
|
134
|
+
const DO_NOT_REINSTALL = 'Re-running the install is the wrong move here: it cannot help on a project that has ' +
|
|
135
|
+
'demonstrably connected, and it can overwrite a working config.';
|
|
134
136
|
/**
|
|
135
137
|
* The ports the scan actually covers, rendered for the message.
|
|
136
138
|
*
|
|
@@ -259,10 +261,8 @@ function configsElsewhereClause(facts) {
|
|
|
259
261
|
.map((c) => (c.projectId === undefined ? c.directory : `${c.directory} ('${c.projectId}')`))
|
|
260
262
|
.join(', ');
|
|
261
263
|
return (`A \`.reticle.json\` WAS found outside this daemon's directory: ${named}. So the app is wired ` +
|
|
262
|
-
'and this is a SCOPE problem, not an install problem
|
|
263
|
-
'
|
|
264
|
-
"directory. Restart the daemon from the app's directory (or point it there) rather than " +
|
|
265
|
-
'installing anything.');
|
|
264
|
+
'and this is a SCOPE problem, not an install problem. Restart the daemon from the ' +
|
|
265
|
+
"app's directory (or point it there) rather than installing anything.");
|
|
266
266
|
}
|
|
267
267
|
/** Where we looked, when we found nothing. An absence nobody can check is not evidence. */
|
|
268
268
|
function searchedClause(facts) {
|
|
@@ -327,13 +327,12 @@ export function explainNoSession(facts) {
|
|
|
327
327
|
if (true === facts.authRefused) {
|
|
328
328
|
return reason(NoSessionReason.AUTH_REFUSED, 'no browser session connected, and the reason is not the app: this daemon REFUSED the last ' +
|
|
329
329
|
'page that dialled it, because the pairing token it presented is not the one this daemon ' +
|
|
330
|
-
'holds. Only an SDK dials the bridge, so the wiring is correct and the app is running.
|
|
331
|
-
'token is read by the build plugin when the dev server STARTS, so the usual cause is a ' +
|
|
332
|
-
'daemon restarted (or a `~/.reticle` cleared) after the dev server was already up
|
|
333
|
-
'
|
|
334
|
-
'
|
|
335
|
-
'
|
|
336
|
-
'The page says the same thing in the browser console.');
|
|
330
|
+
'holds. Only an SDK dials the bridge, so the wiring is correct and the app is running. ' +
|
|
331
|
+
'Restart the dev server so it picks up the current token.', 'The token is read by the build plugin when the dev server STARTS, so the usual cause is a ' +
|
|
332
|
+
'daemon restarted (or a `~/.reticle` cleared) after the dev server was already up. If the ' +
|
|
333
|
+
'two were started from different environments — an editor spawning the MCP server globally ' +
|
|
334
|
+
'while the dev server runs from a shell — they can be reading different `~/.reticle` ' +
|
|
335
|
+
'directories instead. The page says the same thing in the browser console.');
|
|
337
336
|
}
|
|
338
337
|
if (everConnected) {
|
|
339
338
|
// A reaped lease first, because it is the one cause with POSITIVE evidence behind it. Calling
|
|
@@ -344,15 +343,13 @@ export function explainNoSession(facts) {
|
|
|
344
343
|
// (#611), so this branch is only reached when the thing that vanished WAS the lease.
|
|
345
344
|
if (true === facts.leaseExpired) {
|
|
346
345
|
return reason(NoSessionReason.LEASE_EXPIRED, 'no browser session connected, but one WAS connected to this daemon earlier, so the wiring ' +
|
|
347
|
-
'is correct. The session that went away was a pooled lease and it aged out
|
|
348
|
-
'headless context, not a human tab, and it takes its cookies with it
|
|
349
|
-
|
|
350
|
-
`carry on.${alreadyListeningClause(listening)} ${RETRY}`);
|
|
346
|
+
'is correct. The session that went away was a pooled lease and it aged out. Re-acquire ' +
|
|
347
|
+
`with reticle_lease {action:"acquire", url} and carry on. ${RETRY}`, 'A lease is a headless context, not a human tab, and it takes its cookies with it, so an ' +
|
|
348
|
+
`authenticated app needs signing in again.${alreadyListeningClause(listening)}`);
|
|
351
349
|
}
|
|
352
350
|
return reason(NoSessionReason.TAB_GONE, 'no browser session connected, but one WAS connected to this daemon earlier, so the wiring ' +
|
|
353
351
|
`is correct. ${tabGoneWhat(facts.lastKnownUrl)} Ask the human to reopen ` +
|
|
354
|
-
`the app (or run ${OPEN_CMD_BARE}), or reload the tab
|
|
355
|
-
`${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`);
|
|
352
|
+
`the app (or run ${OPEN_CMD_BARE}), or reload the tab. ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`, alreadyListeningClause(listening).trim());
|
|
356
353
|
}
|
|
357
354
|
// Ranked ahead of every "we have never seen one" branch below, because it is the one fact that
|
|
358
355
|
// OUTRANKS them: those branches all reason from an absence, and this reasons from a recorded
|
|
@@ -363,15 +360,16 @@ export function explainNoSession(facts) {
|
|
|
363
360
|
? `${unattributedListeners(listening)} If the dev server is not running, start it first ` +
|
|
364
361
|
'(the command is in `next_action`).'
|
|
365
362
|
: unattributedListeners(listening);
|
|
366
|
-
return reason(NoSessionReason.APP_NOT_REOPENED, `${RESTARTED_LEAD} ${OPEN_THE_APP} ${
|
|
363
|
+
return reason(NoSessionReason.APP_NOT_REOPENED, `${RESTARTED_LEAD} ${OPEN_THE_APP} ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`, `${DO_NOT_REINSTALL} ${listeners} ${rankedCauses(facts)}`);
|
|
367
364
|
}
|
|
368
365
|
// A config found elsewhere outranks every "you may have no SDK" branch below: those reason from
|
|
369
366
|
// an absence in ONE directory, and this is a file we read in another. Ranked here so the reader is
|
|
370
367
|
// never sent through an install on a project that has demonstrably been installed.
|
|
371
368
|
if (!initialized && (facts.configsElsewhere ?? []).length > 0) {
|
|
372
369
|
return reason(NoSessionReason.CONFIG_ELSEWHERE, 'no browser session connected, and this daemon has never seen one. ' +
|
|
373
|
-
`${configsElsewhereClause(facts)} ${
|
|
374
|
-
|
|
370
|
+
`${configsElsewhereClause(facts)} ${OPEN_THE_APP} ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`, 'This daemon was started somewhere that is not the project, which is the normal outcome when ' +
|
|
371
|
+
`an editor launches it from your home directory. ${unattributedListeners(listening)} ` +
|
|
372
|
+
`${rankedCauses(facts)}`);
|
|
375
373
|
}
|
|
376
374
|
if (0 === listening.length) {
|
|
377
375
|
// Lead with the stronger EVIDENCE, and do not overstate what it is.
|
|
@@ -390,54 +388,62 @@ export function explainNoSession(facts) {
|
|
|
390
388
|
// is expected rather than diagnostic.
|
|
391
389
|
if (!initialized) {
|
|
392
390
|
return reason(NoSessionReason.NO_LISTENER_NO_CONFIG, 'no browser session connected. Two things to weigh, and neither of them is proof. ' +
|
|
393
|
-
`(1) ${slowListenerClause(facts)}Nothing is listening on the ports Reticle scans
|
|
394
|
-
'may not be running
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
'running.
|
|
398
|
-
'
|
|
399
|
-
`
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
'while the app lives in a subdirectory, and an app wired by the Vite or Babel plugin ' +
|
|
404
|
-
`carries the SDK without that file at all.${searchedClause(facts)}${siblingListenerClause(facts)} ${leaseAdvice(URL_THEN_LEASE, facts)} ${RETRY}`);
|
|
391
|
+
`(1) ${slowListenerClause(facts)}Nothing is listening on the ports Reticle scans, so the ` +
|
|
392
|
+
'dev server may not be running: START IT YOURSELF, in the background, using the command ' +
|
|
393
|
+
'in `next_action`, and tell the human in one line that it is running. ' +
|
|
394
|
+
`(2) There is no \`.reticle.json\` in ${where}, which is the file ${INIT_CMD} writes, so ` +
|
|
395
|
+
`the app may carry no Reticle SDK. Check the app's OWN directory before re-running it. ${RETRY}`, `The scan covers ${SCANNED_PORTS} and nothing else, so a server on any other port is ` +
|
|
396
|
+
'invisible to it: if the app IS running, ask for its URL rather than assuming it is down, ' +
|
|
397
|
+
`and open it with ${OPEN_CMD}. The missing config is not proof either — in a monorepo the ` +
|
|
398
|
+
'daemon often runs at the root while the app lives in a subdirectory, and an app wired by ' +
|
|
399
|
+
'the Vite or Babel plugin carries the SDK without that file at all.' +
|
|
400
|
+
`${searchedClause(facts)}${siblingListenerClause(facts)} ${leaseAdvice(URL_THEN_LEASE, facts)}`);
|
|
405
401
|
}
|
|
406
402
|
return reason(NoSessionReason.NO_LISTENER, `${stallClause(facts)}no browser session connected, and this daemon has never seen one. ${OPEN_THE_APP} ` +
|
|
407
|
-
'Nothing is listening on the ports Reticle scans ' +
|
|
408
|
-
|
|
409
|
-
'
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
'
|
|
403
|
+
'Nothing is listening on the ports Reticle scans either, and the usual reason is a dev ' +
|
|
404
|
+
'server that is not running: start it yourself in the background with the command in ' +
|
|
405
|
+
'`next_action`, tell the human in one line that it is running, then open the app in a ' +
|
|
406
|
+
`browser. ${RETRY}`,
|
|
407
|
+
// The caveat is in the detail rather than omitted because the scan is NARROW: an empty result
|
|
408
|
+
// is not proof of absence. Reported twice, once against a server answering 200 on :7699 and
|
|
409
|
+
// once against one on :5000 under a custom hostname. The common defaults are in the scanned
|
|
410
|
+
// set, which narrows the gap and cannot close it — anything passed to `--port` is invisible.
|
|
411
|
+
`The scan covers ${SCANNED_PORTS} and nothing else, so it is not proof: a server on any other ` +
|
|
412
|
+
'port is invisible to it. If the app IS running, ask the human for its URL rather than ' +
|
|
417
413
|
// Deliberately NOT offering reticle_lease here, and a test pins that: a lease opens a URL, and
|
|
418
414
|
// if nothing is listening there is nothing at any URL to open. Asking for the real one is the
|
|
419
415
|
// only move that can recover the :7699 case.
|
|
420
|
-
`
|
|
421
|
-
|
|
416
|
+
`assuming it is down. The \`next_action\` command is read from this project's own scripts, ` +
|
|
417
|
+
'and says so rather than guessing when there is none. ' +
|
|
418
|
+
`${leaseAdvice(URL_THEN_LEASE, facts)}${siblingListenerClause(facts)}`);
|
|
422
419
|
}
|
|
423
420
|
if (!initialized) {
|
|
424
421
|
return reason(NoSessionReason.NO_CONFIG, 'no browser session connected, and this daemon has never seen one. ' +
|
|
425
|
-
`What was actually checked: there is no \`.reticle.json\` in ${where}
|
|
426
|
-
`${INIT_CMD} writes, so the app may carry no Reticle SDK
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
`
|
|
430
|
-
|
|
431
|
-
|
|
422
|
+
`What was actually checked: there is no \`.reticle.json\` in ${where}, which is the file ` +
|
|
423
|
+
`${INIT_CMD} writes, so the app may carry no Reticle SDK. Check the app's OWN directory: ` +
|
|
424
|
+
`if it has no config, run ${INIT_CMD} ` +
|
|
425
|
+
'there and restart the dev server; if it has one, the app is wired and simply has no page ' +
|
|
426
|
+
`open — ${OPEN_CMD}. ${RETRY}`, 'That absence is not proof: the same one is expected in a monorepo whose daemon runs at the ' +
|
|
427
|
+
'root while the app lives in a subdirectory, or in an app wired by the Vite or Babel ' +
|
|
428
|
+
`plugin. ${unattributedListeners(listening)}${searchedClause(facts)}${siblingListenerClause(facts)}`);
|
|
432
429
|
}
|
|
433
430
|
return reason(NoSessionReason.SDK_NOT_REACHING_DAEMON, `${stallClause(facts)}no browser session connected, and this daemon has never seen one for this project, which is ` +
|
|
434
|
-
`wired for Reticle. ${OPEN_THE_APP}
|
|
435
|
-
|
|
436
|
-
`this daemon (on ${String(port)}). ${rankedCauses(facts)} ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`);
|
|
431
|
+
`wired for Reticle. ${OPEN_THE_APP} If the page IS open and still does not appear, the SDK ` +
|
|
432
|
+
`is not reaching this daemon (on ${String(port)}). ${leaseAdvice(SELF_SERVE, facts)} ${RETRY}`, `${unattributedListeners(listening)} ${rankedCauses(facts)}`);
|
|
437
433
|
}
|
|
438
|
-
/**
|
|
439
|
-
|
|
440
|
-
|
|
434
|
+
/**
|
|
435
|
+
* Pair a branch's verdict with its prose, so neither can be produced without the other.
|
|
436
|
+
*
|
|
437
|
+
* TWO pieces of prose now. `message` is the lead: what is true and what to do about it, in about
|
|
438
|
+
* four sentences. `detail` is the differential behind it -- the port-scan caveat, the ranked causes,
|
|
439
|
+
* the lease offer -- which is the part that was making this five hundred words in one paragraph.
|
|
440
|
+
*
|
|
441
|
+
* Split rather than deleted. Almost every clause in `detail` exists because somebody was sent the
|
|
442
|
+
* wrong way without it, and the reader who needs it is the agent about to spend a drive on a guess.
|
|
443
|
+
* The reader who does not is the person watching `reticle init`, who gets the lead.
|
|
444
|
+
*/
|
|
445
|
+
function reason(code, message, detail) {
|
|
446
|
+
return { reason: code, message, ...(undefined === detail ? {} : { detail }) };
|
|
441
447
|
}
|
|
442
448
|
/**
|
|
443
449
|
* The message alone, for the callers that render it.
|
|
@@ -446,5 +452,6 @@ function reason(code, message) {
|
|
|
446
452
|
* pair would spread a telemetry concern across every error path that shows a user this text.
|
|
447
453
|
*/
|
|
448
454
|
export function diagnoseNoSession(facts) {
|
|
449
|
-
|
|
455
|
+
const { message, detail } = explainNoSession(facts);
|
|
456
|
+
return undefined === detail ? message : `${message} ${detail}`;
|
|
450
457
|
}
|
|
@@ -140,8 +140,9 @@ export function nextActionFor(facts) {
|
|
|
140
140
|
"own. Point the plugin at the daemon's copy — mount `~/.reticle/pairing-token` into the " +
|
|
141
141
|
`container read-only and set \`${ReticleEnv.PAIRING_TOKEN_DIR}\` to the directory holding ` +
|
|
142
142
|
'it — then ' +
|
|
143
|
-
'restart the dev server, because the token is inlined when Vite resolves its config
|
|
144
|
-
'
|
|
143
|
+
'restart the dev server, because the token is inlined when Vite resolves its config and ' +
|
|
144
|
+
'kept in the prebundled module. Reloading the page serves that same token, whether the ' +
|
|
145
|
+
'dev server is on this machine or in a container.',
|
|
145
146
|
};
|
|
146
147
|
}
|
|
147
148
|
if (!facts.initialized && true === facts.authRefused) {
|