@reticlehq/server 3.2.0 → 3.4.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/LICENSE-ENTERPRISE +1 -1
- package/README.md +3 -3
- package/SKILL.md +23 -22
- package/dist/chromium-launch-options.d.ts +4 -3
- package/dist/chromium-launch-options.js +6 -2
- package/dist/command/cli/adhoc-suite.d.ts +41 -0
- package/dist/command/cli/adhoc-suite.js +159 -0
- package/dist/command/cli/adhoc-verdict.d.ts +58 -2
- package/dist/command/cli/adhoc-verdict.js +108 -13
- package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
- package/dist/command/cli/answers/gate-hook-message.js +6 -0
- package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
- package/dist/command/cli/auth/cloud-keystore.js +0 -3
- package/dist/command/cli/auth/cloud-session.d.ts +4 -0
- package/dist/command/cli/auth/cloud-session.js +2 -0
- package/dist/command/cli/cli-doctor.js +30 -8
- package/dist/command/cli/cli-flow-commands.d.ts +3 -6
- package/dist/command/cli/cli-flow-commands.js +67 -4
- 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 +14 -1
- package/dist/command/cli/cli-parse.js +54 -8
- package/dist/command/cli/cli-update-commands.js +8 -1
- package/dist/command/cli/cli-verify.d.ts +80 -0
- package/dist/command/cli/cli-verify.js +181 -26
- package/dist/command/cli/cloud-cli.js +5 -2
- package/dist/command/cli/cloud-kit.d.ts +0 -2
- package/dist/command/cli/cloud-kit.js +3 -5
- package/dist/command/cli/cloud-login.js +21 -3
- package/dist/command/cli/demo/demo-page.js +6 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
- package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
- package/dist/command/cli/launch/cli-launch.d.ts +9 -29
- package/dist/command/cli/launch/cli-launch.js +27 -49
- package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
- package/dist/command/cli/launch/sdk-version-match.js +206 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
- package/dist/command/cli/ports/resolve/cli-port.d.ts +38 -0
- package/dist/command/cli/ports/resolve/cli-port.js +169 -15
- package/dist/command/cli/report-command.d.ts +16 -0
- package/dist/command/cli/report-command.js +97 -0
- package/dist/command/cli/setup-mcp-cli.js +13 -13
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +21 -7
- package/dist/command/cli.js +67 -10
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +44 -2
- package/dist/command/daemon/binding/daemon-status-probe.js +79 -6
- package/dist/command/daemon/binding/port-presence.d.ts +1 -1
- package/dist/command/daemon/daemon-resolve.d.ts +9 -0
- package/dist/command/daemon/daemon-resolve.js +1 -1
- package/dist/command/daemon/idle-shutdown.d.ts +2 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
- package/dist/command/dev/stale-issue-guard.d.ts +3 -4
- package/dist/command/dev/stale-issue-guard.js +8 -8
- package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
- package/dist/command/setup/bringup/connect-lease.js +38 -0
- package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
- package/dist/command/setup/bringup/dev-server-files.js +131 -0
- package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
- package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
- package/dist/command/setup/bringup/ensure-daemon.js +135 -16
- package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
- package/dist/command/setup/bringup/static-page-server.js +151 -0
- package/dist/command/setup/desktop-shape.d.ts +4 -0
- package/dist/command/setup/desktop-shape.js +18 -2
- package/dist/command/setup/init/init-host.js +8 -0
- package/dist/command/setup/init/init-port.d.ts +15 -0
- package/dist/command/setup/init/init-port.js +44 -0
- package/dist/command/setup/init/init-runtime.d.ts +1 -1
- package/dist/command/setup/init/init-runtime.js +39 -2
- package/dist/command/setup/node-effects.d.ts +27 -14
- package/dist/command/setup/node-effects.js +80 -44
- package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
- package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
- package/dist/command/setup/probe/page-probe.d.ts +13 -2
- package/dist/command/setup/probe/page-probe.js +18 -3
- package/dist/command/setup/run-setup.d.ts +26 -1
- package/dist/command/setup/run-setup.js +96 -13
- package/dist/command/setup/session-pick.d.ts +13 -0
- package/dist/command/setup/session-pick.js +20 -0
- package/dist/command/setup/setup-command.d.ts +2 -14
- package/dist/command/setup/setup-command.js +105 -31
- package/dist/command/setup/setup-mcp.js +3 -3
- package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
- package/dist/command/setup/terminal/wait-progress.js +17 -0
- package/dist/features/crawl/crawl-empty.js +1 -1
- package/dist/features/crawl/crawl-tools.d.ts +6 -0
- package/dist/features/crawl/crawl-tools.js +57 -0
- package/dist/features/crawl/crawl.d.ts +2 -0
- package/dist/features/crawl/crawl.js +171 -156
- package/dist/features/exhaust/app-coverage.d.ts +33 -0
- package/dist/features/exhaust/app-coverage.js +31 -0
- package/dist/features/exhaust/code-coverage.d.ts +47 -0
- package/dist/features/exhaust/code-coverage.js +77 -0
- package/dist/features/exhaust/exhaust-run.d.ts +33 -0
- package/dist/features/exhaust/exhaust-run.js +82 -0
- package/dist/features/exhaust/explorer.d.ts +103 -0
- package/dist/features/exhaust/explorer.js +195 -0
- package/dist/features/exhaust/failure-branches.d.ts +33 -0
- package/dist/features/exhaust/failure-branches.js +92 -0
- package/dist/features/exhaust/ledger.d.ts +202 -0
- package/dist/features/exhaust/ledger.js +252 -0
- package/dist/features/exhaust/session-fold.d.ts +31 -0
- package/dist/features/exhaust/session-fold.js +79 -0
- package/dist/features/exhaust/session-port.d.ts +36 -0
- package/dist/features/exhaust/session-port.js +102 -0
- package/dist/features/harness/drive-report.d.ts +0 -9
- package/dist/features/harness/drive-report.js +18 -0
- package/dist/features/harness/driver.js +4 -7
- package/dist/features/harness/fill-values.d.ts +6 -0
- package/dist/features/harness/fill-values.js +3 -0
- package/dist/features/harness/jev-driver.js +36 -22
- package/dist/features/harness/openai-driver.js +4 -7
- package/dist/features/harness/platform-config.d.ts +10 -0
- package/dist/features/harness/platform-config.js +13 -6
- package/dist/index.d.ts +10 -1
- package/dist/index.js +78 -104
- 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 +40 -9
- package/dist/judgement/oracles/self-instrument.js +4 -2
- package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
- 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/replay-mapping.d.ts +2 -2
- package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
- 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 +3 -1
- package/dist/judgement/runs/verification-sync.js +1 -1
- 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/change/affected.d.ts +9 -0
- package/dist/language/flows/change/affected.js +35 -0
- package/dist/language/flows/change/gate.d.ts +20 -0
- package/dist/language/flows/change/gate.js +12 -1
- package/dist/language/flows/flow-anchor.d.ts +12 -0
- package/dist/language/flows/flow-anchor.js +20 -0
- package/dist/language/flows/flow-author.d.ts +7 -0
- package/dist/language/flows/flow-author.js +19 -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-journey.d.ts +54 -0
- package/dist/language/flows/flow-journey.js +119 -0
- 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 +33 -12
- package/dist/language/flows/flow-replay-run.js +131 -52
- package/dist/language/flows/flow-replay-types.d.ts +9 -0
- package/dist/language/flows/flow-replay.d.ts +27 -12
- package/dist/language/flows/flow-replay.js +127 -111
- package/dist/language/flows/flow-result.d.ts +2 -0
- package/dist/language/flows/flow-result.js +25 -0
- 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-success.d.ts +21 -6
- package/dist/language/flows/flow-success.js +50 -138
- package/dist/language/flows/flow-tools.js +17 -5
- package/dist/language/flows/flows.d.ts +18 -5
- package/dist/language/flows/flows.js +101 -16
- 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/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/replay-halt.js +3 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +31 -2
- package/dist/language/flows/recording/tape/recordings.js +55 -1
- package/dist/language/flows/replay.d.ts +19 -0
- package/dist/language/flows/replay.js +53 -13
- 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/verify-change-tools.js +19 -2
- package/dist/launch-chromium.d.ts +80 -0
- package/dist/launch-chromium.js +218 -0
- package/dist/memory/cloud/cloud-config.d.ts +11 -2
- package/dist/memory/cloud/cloud-config.js +47 -7
- package/dist/memory/cloud/cloud-sync.d.ts +4 -3
- package/dist/memory/cloud/cloud-sync.js +7 -10
- package/dist/memory/cloud/harness-config.d.ts +2 -0
- package/dist/memory/cloud/harness-offer.js +5 -6
- package/dist/memory/cloud/harness-switch.js +5 -6
- package/dist/memory/cloud/sync-cycle.d.ts +78 -1
- package/dist/memory/cloud/sync-cycle.js +321 -53
- package/dist/memory/cloud/sync-daemon.d.ts +13 -0
- package/dist/memory/cloud/sync-daemon.js +45 -20
- package/dist/memory/cloud/sync-disk.js +27 -8
- package/dist/memory/impact/impact-store.js +6 -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 +252 -140
- package/dist/memory/intent/intent-shard.d.ts +0 -23
- package/dist/memory/intent/intent-shard.js +1 -11
- 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/deviation-report.js +4 -1
- package/dist/memory/journal/deviation-service.js +10 -2
- package/dist/memory/journal/drive-flow.d.ts +19 -14
- package/dist/memory/journal/drive-flow.js +65 -48
- package/dist/memory/journal/envelope-store.js +2 -3
- package/dist/memory/journal/journal-recorder.d.ts +21 -0
- 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 +14 -10
- package/dist/memory/journal/on-disk/retention.js +13 -12
- 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 -11
- package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -45
- package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
- package/dist/memory/journal/on-disk/workspace-tiers.js +90 -0
- package/dist/memory/journal/route-template.d.ts +22 -0
- package/dist/memory/journal/route-template.js +44 -0
- package/dist/memory/journal/session-end.d.ts +13 -2
- package/dist/memory/journal/session-end.js +88 -18
- package/dist/memory/journal/session-journal.d.ts +8 -1
- package/dist/memory/journal/session-journal.js +10 -0
- package/dist/memory/project/baselines.d.ts +1 -1
- package/dist/memory/project/dir/reticle-dir.d.ts +3 -1
- package/dist/memory/project/dir/reticle-dir.js +1 -0
- package/dist/memory/project/fs/fs-port.js +44 -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/project-tools.js +3 -1
- 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 +1 -1
- package/dist/portal/bridge/auth-failure-reason.js +9 -4
- package/dist/portal/bridge/bridge.d.ts +8 -0
- package/dist/portal/bridge/bridge.js +43 -2
- package/dist/portal/input/js-coverage.d.ts +25 -0
- package/dist/portal/input/js-coverage.js +30 -0
- package/dist/portal/input/real-input.d.ts +11 -0
- package/dist/portal/input/real-input.js +31 -5
- 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.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +21 -13
- 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-command.d.ts +2 -2
- package/dist/portal/session/dev-server/dev-command.js +30 -12
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
- package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
- package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
- package/dist/portal/session/dev-server/route-status-probe.js +57 -0
- 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/observed-state.d.ts +11 -0
- package/dist/portal/session/facts/observed-state.js +34 -0
- package/dist/portal/session/live-control-tools.js +48 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
- package/dist/portal/session/no-session-diagnosis.js +62 -5
- package/dist/portal/session/no-session-next-action.d.ts +13 -0
- package/dist/portal/session/no-session-next-action.js +48 -9
- package/dist/portal/session/no-session-watch.d.ts +18 -0
- package/dist/portal/session/no-session-watch.js +88 -2
- package/dist/portal/session/presence/attachment-history.d.ts +13 -2
- package/dist/portal/session/session-health.d.ts +61 -0
- package/dist/portal/session/session-health.js +43 -3
- package/dist/portal/session/session-info.d.ts +6 -0
- package/dist/portal/session/session-manager.d.ts +29 -6
- package/dist/portal/session/session-manager.js +42 -5
- package/dist/portal/session/session-reaper.d.ts +28 -0
- package/dist/portal/session/session-reaper.js +49 -1
- 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 +10 -0
- package/dist/portal/session/session.js +34 -26
- package/dist/surface/http-server.d.ts +2 -1
- package/dist/surface/http-server.js +4 -2
- package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
- package/dist/surface/mcp/mcp-proxy.js +5 -5
- package/dist/surface/mcp/mcp.js +4 -2
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
- package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
- package/dist/surface/mcp/server-instructions.js +21 -8
- package/dist/surface/tools/act/after-match.d.ts +17 -0
- package/dist/surface/tools/act/after-match.js +30 -0
- 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/durable.d.ts +69 -0
- package/dist/surface/tools/act/durable.js +139 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +19 -6
- package/dist/surface/tools/act/navigation/navigate-arrival.js +60 -10
- package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
- package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
- package/dist/surface/tools/act/proved-controls.d.ts +9 -0
- package/dist/surface/tools/act/proved-controls.js +11 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
- package/dist/surface/tools/act/sequence-grade.js +32 -1
- 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.d.ts +7 -0
- package/dist/surface/tools/act-capsule.js +68 -8
- package/dist/surface/tools/act-sequence-tool.js +25 -8
- package/dist/surface/tools/act-tools.js +51 -74
- 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 +26 -3
- package/dist/surface/tools/assert/assert-verdict.js +32 -6
- package/dist/surface/tools/browser-tools.js +17 -4
- package/dist/surface/tools/coverage-tools.js +27 -1
- package/dist/surface/tools/dynamic-tools.js +6 -0
- package/dist/surface/tools/error-recovery.js +4 -4
- package/dist/surface/tools/feedback-invite.js +6 -4
- package/dist/surface/tools/feedback-tools.d.ts +2 -1
- package/dist/surface/tools/feedback-tools.js +1 -1
- package/dist/surface/tools/harness-explore.d.ts +3 -2
- package/dist/surface/tools/harness-explore.js +7 -6
- package/dist/surface/tools/invoke-tool.js +17 -4
- 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 +68 -11
- package/dist/surface/tools/lineage-tools.js +1 -1
- package/dist/surface/tools/live-call-text.js +4 -1
- package/dist/surface/tools/observe-tools.js +23 -7
- 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 +5 -0
- package/dist/surface/tools/tool-kit.js +6 -1
- package/dist/surface/tools/tool-surface.js +17 -5
- package/dist/surface/tools/tools.js +68 -29
- package/dist/telemetry/argument-shape.d.ts +0 -2
- package/dist/telemetry/argument-shape.js +0 -4
- package/dist/telemetry/error-class.d.ts +2 -3
- package/dist/telemetry/error-class.js +2 -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 +0 -2
- package/dist/telemetry/harness-drive.js +0 -4
- package/dist/telemetry/hud-metrics.d.ts +32 -0
- package/dist/telemetry/hud-metrics.js +95 -0
- package/dist/telemetry/session-metrics.d.ts +5 -0
- package/dist/telemetry/session-metrics.js +15 -1
- package/dist/wire-journal.d.ts +34 -0
- package/dist/wire-journal.js +104 -0
- package/dist/workspace-packages.d.ts +0 -2
- package/dist/workspace-packages.js +1 -9
- package/docs/README.md +5 -2
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +4 -4
- package/docs/architecture.md +6 -6
- package/docs/best-practices.mdx +1 -1
- package/docs/capabilities.mdx +32 -1
- package/docs/cli/cloud.mdx +21 -9
- package/docs/cli/doctor.mdx +4 -0
- package/docs/cli/feedback.mdx +2 -2
- package/docs/cli/gate.mdx +9 -4
- package/docs/cli/init.mdx +1 -0
- package/docs/cli/report.mdx +44 -0
- package/docs/cli/update.mdx +10 -0
- package/docs/cli.mdx +9 -0
- package/docs/desktop.mdx +14 -10
- package/docs/environment.mdx +87 -0
- package/docs/faq.mdx +18 -16
- package/docs/first-drive.md +5 -13
- package/docs/flows.md +32 -2
- package/docs/frameworks.mdx +140 -9
- package/docs/getting-started.md +3 -3
- package/docs/http-transport.md +6 -3
- package/docs/human-control.md +2 -2
- package/docs/index.mdx +16 -6
- package/docs/install-agentic.mdx +37 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/integration-patterns.md +3 -3
- package/docs/multi-agent-testing.md +1 -1
- package/docs/packages/babel-plugin.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/react.mdx +2 -2
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/vite-plugin.mdx +2 -2
- package/docs/packages.mdx +9 -9
- package/docs/predicates.mdx +176 -7
- package/docs/quickstart.mdx +2 -2
- package/docs/recipes.mdx +2 -2
- package/docs/state-management.mdx +2 -2
- package/docs/system-map.md +1 -1
- package/docs/telemetry.md +1 -1
- package/docs/token-efficiency.md +3 -3
- package/docs/tools/act-and-wait.mdx +1 -0
- package/docs/tools/act-sequence.mdx +1 -1
- package/docs/tools/console.mdx +1 -1
- package/docs/tools/inspect.mdx +1 -1
- package/docs/tools/network.mdx +1 -1
- package/docs/tools/query.mdx +11 -11
- package/docs/tools/sessions.mdx +1 -1
- package/docs/tools/snapshot.mdx +1 -1
- package/docs/tools/state.mdx +1 -1
- package/docs/tools/wait-for.mdx +1 -1
- package/docs/usage.md +8 -7
- package/docs/use-cases.mdx +157 -0
- package/docs/vs-screenshots.mdx +3 -3
- package/docs/what-is-recorded.md +62 -0
- package/docs/what-is-reticle.mdx +17 -20
- package/docs/why-reticle.mdx +14 -21
- package/package.json +11 -10
- package/dist/judgement/capsule/minimize.d.ts +0 -12
- package/dist/judgement/capsule/minimize.js +0 -22
- package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
- package/dist/judgement/outcome/predicate-to-expect.js +0 -148
- package/dist/judgement/phenomena/phenomena.d.ts +0 -26
- package/dist/judgement/phenomena/phenomena.js +0 -101
- package/dist/language/flows/flow-report.d.ts +0 -22
- package/dist/language/flows/flow-report.js +0 -106
package/dist/command/cli.js
CHANGED
|
@@ -14,7 +14,10 @@ import { attachStatusFields } from '../surface/mcp/attach-memory.js';
|
|
|
14
14
|
import { splitBrainFields, withNextAction } from './cli/status-fields.js';
|
|
15
15
|
import { reticleStateHome } from './daemon/daemon.js';
|
|
16
16
|
import { handleMcp } from './cli/mcp-command.js';
|
|
17
|
-
import {
|
|
17
|
+
import { handleReport } from './cli/report-command.js';
|
|
18
|
+
import { daemonProjectAt, resolveDaemonForProject } from './daemon/daemon-resolve.js';
|
|
19
|
+
import { pickDaemonPortToBind } from './daemon/binding/free-port.js';
|
|
20
|
+
import { portForInit, portFromEnv } from './setup/init/init-port.js';
|
|
18
21
|
import { daemonStartOptions } from './cli/daemon-start-options.js';
|
|
19
22
|
import { handleWatch, handleCapsules, handleGate, loadNamedFlows, resolveChangedFiles, } from './cli/cli-flow-commands.js';
|
|
20
23
|
import { RETICLE_DEFAULT_PORT, ReticleDir, ReticleEnv, devServersForProject, } from '@reticlehq/core';
|
|
@@ -48,19 +51,38 @@ import { continueAfterInit } from './setup/init/init-runtime.js';
|
|
|
48
51
|
import { handleDoctor } from './cli/cli-doctor.js';
|
|
49
52
|
import { serverInitHost } from './setup/init/init-host.js';
|
|
50
53
|
import { describeLicense } from '../features/license/license.js';
|
|
51
|
-
import { isLikelyDevServerPort, devServerPortWarning, readProjectPort, readProjectId, } from './cli/ports/resolve/cli-port.js';
|
|
52
|
-
import {
|
|
54
|
+
import { isLikelyDevServerPort, devServerPortWarning, readProjectPort, readProjectId, workspacePortConflict, projectDirOf, } from './cli/ports/resolve/cli-port.js';
|
|
55
|
+
import { nodeSpawner, readTextFile, reexecAtVersion, versionMatchNote, versionToMatch, } from './cli/launch/sdk-version-match.js';
|
|
56
|
+
import { DAEMON_INNER_COMMAND, PORT_FLAG, parseCliArgs, CLI_USAGE, dialsTheDaemon, } from './cli/cli-parse.js';
|
|
53
57
|
import { handleFeedback, handleIdentify, handleTelemetry } from '../telemetry/feedback-cli.js';
|
|
54
58
|
import { installDaemonTelemetry } from '../telemetry/daemon-telemetry.js';
|
|
55
59
|
import { reportCliRun } from '../telemetry/cli-telemetry.js';
|
|
56
60
|
// Re-exported so existing imports (and the CLI tests) keep resolving from '@/cli.js'.
|
|
57
61
|
export { parseCliArgs, CLI_USAGE };
|
|
58
|
-
function handleInit(parsed) {
|
|
62
|
+
async function handleInit(parsed) {
|
|
59
63
|
const cwd = process.cwd();
|
|
64
|
+
// RETICLE_PORT counts as explicit, as it does for every other command: resolved once here, so the
|
|
65
|
+
// port written into the project and the port the runtime phase binds are the same number.
|
|
66
|
+
const explicit = parsed.port ?? portFromEnv(process.env);
|
|
67
|
+
const port = await portForInit(explicit, readProjectPort(cwd), readProjectId(cwd), {
|
|
68
|
+
// The registry names a daemon's owner; an older daemon that never registered is known by the
|
|
69
|
+
// projects its connected pages announced.
|
|
70
|
+
daemonProjects: async (p) => {
|
|
71
|
+
const claimed = daemonProjectAt(p, reticleStateHome());
|
|
72
|
+
if (claimed !== undefined && claimed.length > 0)
|
|
73
|
+
return [claimed];
|
|
74
|
+
const { sessions } = summarizeStatus(await fetchStatus(p));
|
|
75
|
+
return [
|
|
76
|
+
...new Set(sessions.flatMap((s) => (s.projectId === undefined ? [] : [s.projectId]))),
|
|
77
|
+
];
|
|
78
|
+
},
|
|
79
|
+
daemonPresent: async (p) => presenceIsUsable(await probePresence(p, { tcpOpen: probeDaemon, status: fetchStatus })),
|
|
80
|
+
pickPort: (p) => pickDaemonPortToBind(p),
|
|
81
|
+
});
|
|
60
82
|
const io = buildNodeIo(cwd, serverInitHost());
|
|
61
83
|
const result = runInit({
|
|
62
84
|
cwd,
|
|
63
|
-
port
|
|
85
|
+
port,
|
|
64
86
|
mcp: parsed.mcp,
|
|
65
87
|
dryRun: parsed.dryRun,
|
|
66
88
|
install: parsed.install,
|
|
@@ -69,12 +91,13 @@ function handleInit(parsed) {
|
|
|
69
91
|
// past a missing package manager, and could not honour that while never being told about it.
|
|
70
92
|
...(parsed.url === undefined ? {} : { url: parsed.url }),
|
|
71
93
|
captureBodies: true === parsed.captureBodies,
|
|
94
|
+
hooks: true === parsed.hooks,
|
|
72
95
|
// The outcome is reported by confirmInstall instead, once it knows whether an app connected —
|
|
73
96
|
// `init` writing files was never the same thing as `init` working (#269).
|
|
74
97
|
deferOutcome: true,
|
|
75
98
|
continuesToRuntime: true !== parsed.filesOnly && true !== parsed.dryRun,
|
|
76
99
|
}, io);
|
|
77
|
-
|
|
100
|
+
await continueAfterInit({ ...parsed, port }, result, io, cwd);
|
|
78
101
|
}
|
|
79
102
|
// `serve`, `stop` and `restart` live in `cli/lifecycle/daemon-lifecycle.ts`: one idea, and the
|
|
80
103
|
// largest group this file could give up without splitting something that belongs together.
|
|
@@ -522,6 +545,26 @@ export function main() {
|
|
|
522
545
|
if (licenseKey !== undefined)
|
|
523
546
|
process.env[LICENSE_KEY_ENV] = licenseKey;
|
|
524
547
|
const argv = process.argv.slice(2);
|
|
548
|
+
// Before anything reports or writes: a CLI a major away from the project's SDK makes every verdict
|
|
549
|
+
// `version_skew`, and the unpinned `npx @reticlehq/server` every agent entry uses resolves the
|
|
550
|
+
// LATEST major whatever the project installed. Hand the same arguments to the matching release and
|
|
551
|
+
// step aside — it reports its own run. See cli/launch/sdk-version-match.ts.
|
|
552
|
+
const matchVersion = versionToMatch({
|
|
553
|
+
argv,
|
|
554
|
+
cliVersion: SERVER_VERSION,
|
|
555
|
+
env: process.env,
|
|
556
|
+
projectDir: projectDirOf(process.cwd()),
|
|
557
|
+
readFile: readTextFile,
|
|
558
|
+
});
|
|
559
|
+
if (matchVersion !== undefined) {
|
|
560
|
+
process.stderr.write(`${versionMatchNote(matchVersion, SERVER_VERSION)}\n`);
|
|
561
|
+
reexecAtVersion(matchVersion, argv, process.env, {
|
|
562
|
+
spawn: nodeSpawner,
|
|
563
|
+
exit: (code) => process.exit(code),
|
|
564
|
+
warn: (line) => process.stderr.write(`${line}\n`),
|
|
565
|
+
});
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
525
568
|
// Every invocation passes through here — the single chokepoint for the "how often is it used / how
|
|
526
569
|
// many distinct machines + projects" metrics. Fire-and-forget: a metric must never delay or fail a run.
|
|
527
570
|
//
|
|
@@ -547,14 +590,20 @@ export function main() {
|
|
|
547
590
|
void runCloudCommand(argv).then((code) => process.exit(code));
|
|
548
591
|
return;
|
|
549
592
|
}
|
|
550
|
-
const
|
|
551
|
-
const envPort = portEnv !== undefined ? parseInt(portEnv, 10) : undefined;
|
|
593
|
+
const envPort = portFromEnv(process.env);
|
|
552
594
|
const projectPort = readProjectPort(process.cwd());
|
|
553
595
|
// Say so rather than letting the daemon fight the dev server for the port and fail with an
|
|
554
596
|
// EADDRINUSE that mentions neither file nor cause.
|
|
555
597
|
if (projectPort !== undefined && isLikelyDevServerPort(projectPort)) {
|
|
556
598
|
process.stderr.write(`${devServerPortWarning(projectPort)}\n`);
|
|
557
599
|
}
|
|
600
|
+
// A monorepo root whose wired apps disagree on a port: nothing is picked, and without this line the
|
|
601
|
+
// default below would be — another project's daemon, answered as if it were this one.
|
|
602
|
+
const portConflict = projectPort === undefined && envPort === undefined && !argv.includes(PORT_FLAG)
|
|
603
|
+
? workspacePortConflict(process.cwd())
|
|
604
|
+
: undefined;
|
|
605
|
+
if (portConflict !== undefined)
|
|
606
|
+
process.stderr.write(`${portConflict}\n`);
|
|
558
607
|
// Registry BEFORE the default, and after both explicit sources.
|
|
559
608
|
//
|
|
560
609
|
// This is the line that ends the split brain. Build plugins have always asked the registry which
|
|
@@ -571,6 +620,11 @@ export function main() {
|
|
|
571
620
|
// Headed by default; hidden only where there is no display to be headed on. A run nobody can see
|
|
572
621
|
// is a run nobody trusts, and every "did it actually do anything?" cost a human round-trip.
|
|
573
622
|
const parsed = parseCliArgs(argv, defaultPort, process.env['CI'] !== undefined);
|
|
623
|
+
// The refusal the line above promised. Printing it and carrying on let the default port answer
|
|
624
|
+
// anyway, for whichever project's daemon owned it.
|
|
625
|
+
if (portConflict !== undefined && dialsTheDaemon(parsed)) {
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
574
628
|
switch (parsed.kind) {
|
|
575
629
|
case 'error':
|
|
576
630
|
// Two audiences, two channels. The structured line is for logs and scripts; the plain text is
|
|
@@ -581,7 +635,7 @@ export function main() {
|
|
|
581
635
|
process.exit(1);
|
|
582
636
|
break;
|
|
583
637
|
case 'init':
|
|
584
|
-
handleInit(parsed);
|
|
638
|
+
void handleInit(parsed);
|
|
585
639
|
break;
|
|
586
640
|
case 'serve':
|
|
587
641
|
handleServe(parsed);
|
|
@@ -667,7 +721,10 @@ export function main() {
|
|
|
667
721
|
void handleHunt(parsed.dir);
|
|
668
722
|
break;
|
|
669
723
|
case 'gate':
|
|
670
|
-
void handleGate(parsed.files, parsed.since, parsed.hook);
|
|
724
|
+
void handleGate(parsed.files, parsed.since, parsed.hook, parsed.acceptCoverage);
|
|
725
|
+
break;
|
|
726
|
+
case 'report':
|
|
727
|
+
void handleReport(parsed.session, parsed.hook);
|
|
671
728
|
break;
|
|
672
729
|
case 'watch':
|
|
673
730
|
handleWatch();
|
|
@@ -1,2 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* GET the daemon's /status JSON. Resolves to the parsed body, or undefined on any failure —
|
|
3
|
+
* including a body that is not a Reticle daemon's, so every caller reads a stranger as absent and
|
|
4
|
+
* `probePresence` calls the port FOREIGN.
|
|
5
|
+
*
|
|
6
|
+
* `host` defaults to loopback, which is every caller's actual target — except `bootSession`
|
|
7
|
+
* (reticlehq/reticle#1165 review), whose Bridge can bind a non-default host via `RETICLE_HOST`.
|
|
8
|
+
* Hardcoding loopback here made that preflight check the wrong address.
|
|
9
|
+
*/
|
|
10
|
+
export declare function fetchStatus(port: number, host?: string,
|
|
11
|
+
/** Also tell the daemon a setup run is waiting on it, so it does not idle out mid-wait. */
|
|
12
|
+
hold?: boolean): Promise<unknown>;
|
|
13
|
+
/** One connected tab as `reticle status` reports it — the at-a-glance health line. */
|
|
14
|
+
interface StatusSession {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
url: string;
|
|
17
|
+
projectId?: string;
|
|
18
|
+
throttled: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The tab is backgrounded. Reported by the daemon on every session; `false` on one too old to say,
|
|
21
|
+
* which reads as visible — the conservative answer, since it keeps the reuse this command has
|
|
22
|
+
* always done rather than opening a tab on a guess.
|
|
23
|
+
*/
|
|
24
|
+
hidden: boolean;
|
|
25
|
+
stale: boolean;
|
|
26
|
+
pendingMarks: number;
|
|
27
|
+
/** The tab is attached and answering nothing — see Session.unresponsive. Absent on an older daemon. */
|
|
28
|
+
unresponsive?: true;
|
|
29
|
+
/** Since the SDK last sent anything, heartbeats included. Absent on a body that did not say. */
|
|
30
|
+
lastSeenMs?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reduce the daemon's /status JSON to the compact view `reticle status` prints. Pure: narrows the
|
|
34
|
+
* untrusted wire payload (never `any`) and tolerates a missing/partial body so a malformed response
|
|
35
|
+
* degrades to "running, 0 sessions" instead of throwing.
|
|
36
|
+
*/
|
|
37
|
+
export declare function summarizeStatus(payload: unknown): {
|
|
38
|
+
sessionCount: number;
|
|
39
|
+
sessions: StatusSession[];
|
|
40
|
+
why?: string;
|
|
41
|
+
/** The same diagnosis without the differential, for a surface a person reads. */
|
|
42
|
+
whyLead?: string;
|
|
43
|
+
};
|
|
44
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as http from 'node:http';
|
|
2
|
-
import {
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { LOOPBACK_HOST, STATUS_HOLD_QUERY, STATUS_PATH } from '@reticlehq/core';
|
|
3
4
|
import { loopbackAgent } from '../../../surface/loopback-agent.js';
|
|
4
5
|
/**
|
|
5
6
|
* The impure half of `probePresence`: ask the port whether a Reticle daemon is answering.
|
|
@@ -15,13 +16,38 @@ import { loopbackAgent } from '../../../surface/loopback-agent.js';
|
|
|
15
16
|
*/
|
|
16
17
|
/** How long the daemon /status probe waits before giving up — a local loopback call is near-instant. */
|
|
17
18
|
const STATUS_PROBE_TIMEOUT_MS = 1000;
|
|
18
|
-
/**
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The fields every Reticle daemon's `/status` has carried, which no stranger's is likely to.
|
|
21
|
+
*
|
|
22
|
+
* Any JSON used to count. An unrelated MCP server on the port init chose answered `/status` with its
|
|
23
|
+
* own body, was adopted as the daemon, the page's bridge dial got WebSocket 404s, and init blamed the
|
|
24
|
+
* dev server. `contract` is NOT required: a daemon from before it existed is still Reticle, and has
|
|
25
|
+
* to be recognised as one so init can replace it as skewed rather than call it a stranger.
|
|
26
|
+
*/
|
|
27
|
+
const ReticleStatusSchema = z
|
|
28
|
+
.object({
|
|
29
|
+
running: z.literal(true),
|
|
30
|
+
version: z.string().min(1),
|
|
31
|
+
sessions: z.array(z.unknown()),
|
|
32
|
+
})
|
|
33
|
+
.passthrough();
|
|
34
|
+
/**
|
|
35
|
+
* GET the daemon's /status JSON. Resolves to the parsed body, or undefined on any failure —
|
|
36
|
+
* including a body that is not a Reticle daemon's, so every caller reads a stranger as absent and
|
|
37
|
+
* `probePresence` calls the port FOREIGN.
|
|
38
|
+
*
|
|
39
|
+
* `host` defaults to loopback, which is every caller's actual target — except `bootSession`
|
|
40
|
+
* (reticlehq/reticle#1165 review), whose Bridge can bind a non-default host via `RETICLE_HOST`.
|
|
41
|
+
* Hardcoding loopback here made that preflight check the wrong address.
|
|
42
|
+
*/
|
|
43
|
+
export function fetchStatus(port, host = LOOPBACK_HOST,
|
|
44
|
+
/** Also tell the daemon a setup run is waiting on it, so it does not idle out mid-wait. */
|
|
45
|
+
hold = false) {
|
|
20
46
|
return new Promise((resolve) => {
|
|
21
47
|
const req = http.get({
|
|
22
|
-
host
|
|
48
|
+
host,
|
|
23
49
|
port,
|
|
24
|
-
path: STATUS_PATH,
|
|
50
|
+
path: hold ? `${STATUS_PATH}?${STATUS_HOLD_QUERY}=1` : STATUS_PATH,
|
|
25
51
|
timeout: STATUS_PROBE_TIMEOUT_MS,
|
|
26
52
|
// Shares the proxy's keep-alive agent: the proxy calls this on every reconnect, and a socket
|
|
27
53
|
// per probe is the same churn the POST leg was fixed for.
|
|
@@ -32,7 +58,8 @@ export function fetchStatus(port) {
|
|
|
32
58
|
res.on('data', (chunk) => (body += chunk));
|
|
33
59
|
res.on('end', () => {
|
|
34
60
|
try {
|
|
35
|
-
|
|
61
|
+
const parsed = ReticleStatusSchema.safeParse(JSON.parse(body));
|
|
62
|
+
resolve(parsed.success ? parsed.data : undefined);
|
|
36
63
|
}
|
|
37
64
|
catch {
|
|
38
65
|
resolve(undefined);
|
|
@@ -46,3 +73,49 @@ export function fetchStatus(port) {
|
|
|
46
73
|
});
|
|
47
74
|
});
|
|
48
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Reduce the daemon's /status JSON to the compact view `reticle status` prints. Pure: narrows the
|
|
78
|
+
* untrusted wire payload (never `any`) and tolerates a missing/partial body so a malformed response
|
|
79
|
+
* degrades to "running, 0 sessions" instead of throwing.
|
|
80
|
+
*/
|
|
81
|
+
export function summarizeStatus(payload) {
|
|
82
|
+
if (typeof payload !== 'object' || null === payload)
|
|
83
|
+
return { sessionCount: 0, sessions: [] };
|
|
84
|
+
const obj = payload;
|
|
85
|
+
// Carried through to the printed line: with no sessions this is the whole answer, and dropping it
|
|
86
|
+
// here would silently undo the reason it is on the wire.
|
|
87
|
+
const why = 'string' === typeof obj['why'] ? obj['why'] : undefined;
|
|
88
|
+
// Absent on a daemon older than this field, which is why every reader falls back to `why`.
|
|
89
|
+
const whyLead = 'string' === typeof obj['whyLead'] ? obj['whyLead'] : undefined;
|
|
90
|
+
const raw = Array.isArray(obj['sessions']) ? obj['sessions'] : [];
|
|
91
|
+
const sessions = raw
|
|
92
|
+
.map((s) => {
|
|
93
|
+
if (typeof s !== 'object' || null === s)
|
|
94
|
+
return null;
|
|
95
|
+
const r = s;
|
|
96
|
+
const sessionId = 'string' === typeof r['sessionId'] ? r['sessionId'] : '';
|
|
97
|
+
if ('' === sessionId)
|
|
98
|
+
return null;
|
|
99
|
+
return {
|
|
100
|
+
sessionId,
|
|
101
|
+
url: 'string' === typeof r['url'] ? r['url'] : '',
|
|
102
|
+
...('string' === typeof r['projectId'] && 0 < r['projectId'].length
|
|
103
|
+
? { projectId: r['projectId'] }
|
|
104
|
+
: {}),
|
|
105
|
+
throttled: true === r['throttled'],
|
|
106
|
+
hidden: true === r['hidden'],
|
|
107
|
+
stale: true === r['stale'],
|
|
108
|
+
pendingMarks: 'number' === typeof r['pendingMarks'] ? r['pendingMarks'] : 0,
|
|
109
|
+
...(true === r['unresponsive'] ? { unresponsive: true } : {}),
|
|
110
|
+
...('number' === typeof r['lastSeenMs'] ? { lastSeenMs: r['lastSeenMs'] } : {}),
|
|
111
|
+
};
|
|
112
|
+
})
|
|
113
|
+
.filter((s) => s !== null);
|
|
114
|
+
const sessionCount = 'number' === typeof obj['sessionCount'] ? obj['sessionCount'] : sessions.length;
|
|
115
|
+
return {
|
|
116
|
+
sessionCount,
|
|
117
|
+
sessions,
|
|
118
|
+
...(why === undefined ? {} : { why }),
|
|
119
|
+
...(whyLead === undefined ? {} : { whyLead }),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -65,7 +65,7 @@ export declare function probePresence(port: number, probes: {
|
|
|
65
65
|
* these lets the sentence tell them apart when — and only when — the recorded pid for the port
|
|
66
66
|
* matches the process actually holding it.
|
|
67
67
|
*/
|
|
68
|
-
|
|
68
|
+
interface PortHolder {
|
|
69
69
|
/** The pid we recorded for this port, from the daemon pid file. `null` when there is none. */
|
|
70
70
|
ourPid: number | null;
|
|
71
71
|
/** The pid actually holding the port, when the caller could determine it. */
|
|
@@ -16,6 +16,15 @@
|
|
|
16
16
|
* This module is the one answer both sides now use. It is pure and injectable: no process spawning,
|
|
17
17
|
* no port binding, nothing that needs a real `~/.reticle` to test.
|
|
18
18
|
*/
|
|
19
|
+
import { type DaemonRegistryEntry } from '@reticlehq/core/artifacts';
|
|
20
|
+
/**
|
|
21
|
+
* Every well-formed registry entry in `home`.
|
|
22
|
+
*
|
|
23
|
+
* A corrupt or half-written entry is skipped rather than thrown on: the registry is written by other
|
|
24
|
+
* processes that can be killed mid-write, and one bad file must not make a machine's daemons
|
|
25
|
+
* undiscoverable. Validated through core's schema so a shape change cannot silently pass here.
|
|
26
|
+
*/
|
|
27
|
+
export declare function readDaemonRegistry(home: string): DaemonRegistryEntry[];
|
|
19
28
|
/**
|
|
20
29
|
* The port of the live daemon serving `projectId`, or undefined when this project has none.
|
|
21
30
|
*
|
|
@@ -26,7 +26,7 @@ import { DaemonRegistryEntrySchema, daemonRegistryPort, pickDaemonPort, } from '
|
|
|
26
26
|
* processes that can be killed mid-write, and one bad file must not make a machine's daemons
|
|
27
27
|
* undiscoverable. Validated through core's schema so a shape change cannot silently pass here.
|
|
28
28
|
*/
|
|
29
|
-
function readDaemonRegistry(home) {
|
|
29
|
+
export function readDaemonRegistry(home) {
|
|
30
30
|
let files;
|
|
31
31
|
try {
|
|
32
32
|
files = readdirSync(home);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface IdleShutdownOptions {
|
|
2
2
|
/** True when nothing is using the daemon: no agent connected, no browser session, no pool lease. */
|
|
3
3
|
isIdle: () => boolean;
|
|
4
4
|
/** Clean teardown + process exit. Called at most once. */
|
|
@@ -52,3 +52,4 @@ export declare function resolveIdleCheckMs(raw: string | undefined): number;
|
|
|
52
52
|
* broken.
|
|
53
53
|
*/
|
|
54
54
|
export declare function resolveIdleShutdownMs(raw: string | undefined): number;
|
|
55
|
+
export {};
|
|
@@ -21,6 +21,12 @@
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function noteToolCall(): void;
|
|
23
23
|
export declare function everServedToolCall(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* How long one hold from a setup run keeps the daemon from idling out. `init` renews it on a timer
|
|
26
|
+
* well inside this window for as long as it runs, so a run that dies stops holding within it.
|
|
27
|
+
*/
|
|
28
|
+
export declare const SETUP_HOLD_MS = 60000;
|
|
29
|
+
export declare function noteSetupHold(now: number): void;
|
|
24
30
|
/** Tests only. */
|
|
25
31
|
export declare function resetDaemonUsefulness(): void;
|
|
26
32
|
interface UsefulnessFacts {
|
|
@@ -56,5 +62,5 @@ interface IdlePool {
|
|
|
56
62
|
* clause the shutdown watcher could never fire on the many daemons that spawn in a directory with
|
|
57
63
|
* no app and sit there for the length of an editor session.
|
|
58
64
|
*/
|
|
59
|
-
export declare function buildIdlePredicate(agentConnected: () => boolean, sessions: IdleSessions, pool: IdlePool): () => boolean;
|
|
65
|
+
export declare function buildIdlePredicate(agentConnected: () => boolean, sessions: IdleSessions, pool: IdlePool, now?: () => number): () => boolean;
|
|
60
66
|
export {};
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* anybody could lose. When it does shut down, the MCP proxy reconnects and `ensureDaemon` spawns a
|
|
20
20
|
* fresh one on the next call, which is a path already exercised hundreds of times a day.
|
|
21
21
|
*/
|
|
22
|
+
import { STATUS_HOLD_MS } from '@reticlehq/core';
|
|
22
23
|
/** Set the first time any tool is dispatched through runTool — the chokepoint every path shares. */
|
|
23
24
|
let servedToolCall = false;
|
|
24
25
|
export function noteToolCall() {
|
|
@@ -27,9 +28,30 @@ export function noteToolCall() {
|
|
|
27
28
|
export function everServedToolCall() {
|
|
28
29
|
return servedToolCall;
|
|
29
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* How long one hold from a setup run keeps the daemon from idling out. `init` renews it on a timer
|
|
33
|
+
* well inside this window for as long as it runs, so a run that dies stops holding within it.
|
|
34
|
+
*/
|
|
35
|
+
export const SETUP_HOLD_MS = STATUS_HOLD_MS;
|
|
36
|
+
/**
|
|
37
|
+
* When a setup run last said it is waiting on this daemon — see STATUS_HOLD_QUERY.
|
|
38
|
+
*
|
|
39
|
+
* `init` starts the daemon and can then wait on the app longer than the idle grace: a cold desktop
|
|
40
|
+
* build, a slow first compile. The daemon counted none of that as activity, idled out
|
|
41
|
+
* (`reticle_daemon_idle_exit`) while init was still waiting for the app to dial it, and the app came
|
|
42
|
+
* up to a closed port.
|
|
43
|
+
*/
|
|
44
|
+
let setupHeldAt;
|
|
45
|
+
export function noteSetupHold(now) {
|
|
46
|
+
setupHeldAt = now;
|
|
47
|
+
}
|
|
48
|
+
function setupHolding(now) {
|
|
49
|
+
return undefined !== setupHeldAt && now - setupHeldAt < SETUP_HOLD_MS;
|
|
50
|
+
}
|
|
30
51
|
/** Tests only. */
|
|
31
52
|
export function resetDaemonUsefulness() {
|
|
32
53
|
servedToolCall = false;
|
|
54
|
+
setupHeldAt = undefined;
|
|
33
55
|
}
|
|
34
56
|
/**
|
|
35
57
|
* True when the daemon has done nothing for anyone and has nothing to lose by exiting.
|
|
@@ -49,13 +71,14 @@ export function isUselessDaemon(facts) {
|
|
|
49
71
|
* clause the shutdown watcher could never fire on the many daemons that spawn in a directory with
|
|
50
72
|
* no app and sit there for the length of an editor session.
|
|
51
73
|
*/
|
|
52
|
-
export function buildIdlePredicate(agentConnected, sessions, pool) {
|
|
53
|
-
return () =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
74
|
+
export function buildIdlePredicate(agentConnected, sessions, pool, now = () => Date.now()) {
|
|
75
|
+
return () => !setupHolding(now()) &&
|
|
76
|
+
(!agentConnected() ||
|
|
77
|
+
isUselessDaemon({
|
|
78
|
+
servedToolCall: everServedToolCall(),
|
|
79
|
+
everConnected: sessions.everConnected(),
|
|
80
|
+
activeLeases: pool.activeCount(),
|
|
81
|
+
})) &&
|
|
59
82
|
0 === sessions.count() &&
|
|
60
83
|
0 === pool.activeCount();
|
|
61
84
|
}
|
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ## Why `fixed-pending-release` is an accepted answer
|
|
10
10
|
*
|
|
11
|
-
* Work
|
|
12
|
-
* ships. The label is
|
|
13
|
-
*
|
|
14
|
-
* when they close things, only to say so.
|
|
11
|
+
* Work that is already ON the release branch is legitimately still open; the issue closes when the
|
|
12
|
+
* version ships. The label is that fact. It is not a fact about an open pull request. Applying it
|
|
13
|
+
* there is what told contributors a fix had shipped when the pull request was still unmerged.
|
|
15
14
|
*
|
|
16
15
|
* Pure. The git log and the GitHub lookup belong to the caller, which is what makes every branch
|
|
17
16
|
* below testable without a network or a repo.
|
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ## Why `fixed-pending-release` is an accepted answer
|
|
10
10
|
*
|
|
11
|
-
* Work
|
|
12
|
-
* ships. The label is
|
|
13
|
-
*
|
|
14
|
-
* when they close things, only to say so.
|
|
11
|
+
* Work that is already ON the release branch is legitimately still open; the issue closes when the
|
|
12
|
+
* version ships. The label is that fact. It is not a fact about an open pull request. Applying it
|
|
13
|
+
* there is what told contributors a fix had shipped when the pull request was still unmerged.
|
|
15
14
|
*
|
|
16
15
|
* Pure. The git log and the GitHub lookup belong to the caller, which is what makes every branch
|
|
17
16
|
* below testable without a network or a repo.
|
|
@@ -61,9 +60,10 @@ export function staleIssues(claimed, issues) {
|
|
|
61
60
|
/** The message the guard prints. Held here so the check and its tests cannot describe it differently. */
|
|
62
61
|
export function staleIssueReport(stale) {
|
|
63
62
|
const list = stale.map((n) => `#${String(n)}`).join(', ');
|
|
63
|
+
const pronoun = 1 === stale.length ? 'it' : 'them';
|
|
64
64
|
return (`${list} ${1 === stale.length ? 'is' : 'are'} still OPEN, and a commit on this branch says it ` +
|
|
65
|
-
`closes ${
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`
|
|
65
|
+
`closes ${pronoun}. Anyone browsing the tracker reads that as work nobody has started. Close ` +
|
|
66
|
+
`${pronoun} when this merges. Do not apply \`${PENDING_RELEASE_LABEL}\` from an open pull ` +
|
|
67
|
+
`request: that label means the fix is already on the release branch and waiting for the version ` +
|
|
68
|
+
`to ship. Applying it earlier is what sent contributors away from work that was not merged.`);
|
|
69
69
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The connect proof for a run with no system browser to lean on: `init --no-open`, CI, a container,
|
|
3
|
+
* an agent, or a launcher that failed.
|
|
4
|
+
*
|
|
5
|
+
* Opens the app in a lease on the daemon at `port`, over the MCP transport every daemon serves --
|
|
6
|
+
* the same route `reticle verify` takes, so there is no second browser stack and no second daemon
|
|
7
|
+
* fighting this one for the port.
|
|
8
|
+
*/
|
|
9
|
+
/** A lease this run opened, and the way to hand it back; or why it could not be opened. */
|
|
10
|
+
type LeaseOutcome = {
|
|
11
|
+
readonly sessionId: string;
|
|
12
|
+
readonly release: () => Promise<void>;
|
|
13
|
+
} | {
|
|
14
|
+
readonly failed: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function openLeaseFor(port: number, url: string, token: string | undefined): Promise<LeaseOutcome>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The connect proof for a run with no system browser to lean on: `init --no-open`, CI, a container,
|
|
3
|
+
* an agent, or a launcher that failed.
|
|
4
|
+
*
|
|
5
|
+
* Opens the app in a lease on the daemon at `port`, over the MCP transport every daemon serves --
|
|
6
|
+
* the same route `reticle verify` takes, so there is no second browser stack and no second daemon
|
|
7
|
+
* fighting this one for the port.
|
|
8
|
+
*/
|
|
9
|
+
import { acquireLease, connectOverSse, endpointFor, releaseLease, } from '../../cli/adhoc-verdict.js';
|
|
10
|
+
const reason = (error) => (error instanceof Error ? error.message : String(error));
|
|
11
|
+
export async function openLeaseFor(port, url, token) {
|
|
12
|
+
let caller;
|
|
13
|
+
try {
|
|
14
|
+
caller = await connectOverSse(endpointFor(port, token));
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return { failed: `could not reach the daemon on port ${String(port)}: ${reason(error)}` };
|
|
18
|
+
}
|
|
19
|
+
const close = () => caller.close().catch(() => undefined);
|
|
20
|
+
try {
|
|
21
|
+
const got = await acquireLease(caller, url);
|
|
22
|
+
if ('failed' in got) {
|
|
23
|
+
await close();
|
|
24
|
+
return { failed: got.failed.join(' ') };
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
sessionId: got.leased,
|
|
28
|
+
release: async () => {
|
|
29
|
+
await releaseLease(caller, got.leased);
|
|
30
|
+
await close();
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
await close();
|
|
36
|
+
return { failed: reason(error) };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `init` leaves in the state home about a dev server it handed over: the log, its one rotated
|
|
3
|
+
* predecessor, and a record of where the server is.
|
|
4
|
+
*
|
|
5
|
+
* All keyed on the app directory, beside the daemon logs and so outside the project: no bundler
|
|
6
|
+
* watcher ever sees them change (a `.reticle/` write inside the app once put Vite into a continuous
|
|
7
|
+
* full-reload loop), and two projects never share a file.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
export declare function devServerLogName(cwd: string): string;
|
|
11
|
+
/** Where a handed-over server is: enough to find it again and to prove it is still that server. */
|
|
12
|
+
declare const HandedOverSchema: z.ZodObject<{
|
|
13
|
+
pid: z.ZodNumber;
|
|
14
|
+
url: z.ZodString;
|
|
15
|
+
appDir: z.ZodString;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
url: string;
|
|
18
|
+
pid: number;
|
|
19
|
+
appDir: string;
|
|
20
|
+
}, {
|
|
21
|
+
url: string;
|
|
22
|
+
pid: number;
|
|
23
|
+
appDir: string;
|
|
24
|
+
}>;
|
|
25
|
+
export type HandedOver = z.infer<typeof HandedOverSchema>;
|
|
26
|
+
/**
|
|
27
|
+
* Written when `init` leaves a server running, so the next `init` can attach to it.
|
|
28
|
+
*
|
|
29
|
+
* Only Vite announces itself (through its plugin). Re-running init while a Next or Angular server
|
|
30
|
+
* it had started was still up spawned a second `dev`, which failed with "port 3000 is already in use
|
|
31
|
+
* — something else is serving there" — the something being the server init itself had left.
|
|
32
|
+
*/
|
|
33
|
+
export declare function recordHandOver(dir: string, record: HandedOver): void;
|
|
34
|
+
/**
|
|
35
|
+
* The url of the server `init` handed over for `appDir`, when that process is alive AND the url
|
|
36
|
+
* still answers — both, because a recycled pid proves nothing and a url answering proves only that
|
|
37
|
+
* SOMETHING is there. Undefined otherwise, and the caller starts one.
|
|
38
|
+
*/
|
|
39
|
+
export declare function handedOverUrl(dir: string, appDir: string, deps: {
|
|
40
|
+
alive: (pid: number) => boolean;
|
|
41
|
+
answers: (url: string) => Promise<boolean>;
|
|
42
|
+
}): Promise<string | undefined>;
|
|
43
|
+
/**
|
|
44
|
+
* Stop the server `init` handed over for `appDir`, and forget it. True when one was stopped.
|
|
45
|
+
*
|
|
46
|
+
* For a port move: Next reads the daemon url into `NEXT_PUBLIC_RETICLE_URL` once, when `next dev`
|
|
47
|
+
* starts, so attaching to the running server after `init --port <new>` left the page dialling the
|
|
48
|
+
* old port — which the same run had just stopped. Only a server this record proves init started is
|
|
49
|
+
* touched; one the user started is theirs to restart.
|
|
50
|
+
*/
|
|
51
|
+
export declare function stopHandedOver(dir: string, appDir: string, deps: {
|
|
52
|
+
alive: (pid: number) => boolean;
|
|
53
|
+
kill: (pid: number) => void;
|
|
54
|
+
}): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Remove what servers that are gone left behind: their logs, rotated logs and records.
|
|
57
|
+
*
|
|
58
|
+
* Every run wrote a log per project and nothing ever deleted one, so the state home kept a file for
|
|
59
|
+
* every app `init` had ever started. A record whose pid is dead takes its logs with it; a log with no
|
|
60
|
+
* record at all goes once it is old. `keep` is the app about to start, whose files are replaced
|
|
61
|
+
* anyway.
|
|
62
|
+
*/
|
|
63
|
+
export declare function sweepDeadDevServers(dir: string, deps: {
|
|
64
|
+
alive: (pid: number) => boolean;
|
|
65
|
+
now: number;
|
|
66
|
+
keep?: string;
|
|
67
|
+
}): void;
|
|
68
|
+
export {};
|