@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
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which "add the reticle entry by hand" lines have already been said on this machine.
|
|
3
|
+
*
|
|
4
|
+
* Two commands register agents, deliberately: the installer does it because it once reached fewer
|
|
5
|
+
* clients than `init` and sent people to a project directory to finish a half-done job, and `init`
|
|
6
|
+
* does it because that is where the project gets wired. Both then narrate the clients whose config
|
|
7
|
+
* we refuse to rewrite -- a Zed settings file with comments in it, a Continue YAML somebody
|
|
8
|
+
* formatted -- so a `curl | sh` followed by `reticle init` prints the same two paragraphs twice in
|
|
9
|
+
* one sitting, and prints them again on every re-run after that, forever.
|
|
10
|
+
*
|
|
11
|
+
* The REGISTRATION stays in both places. The sentence is the thing that repeats, and it is a
|
|
12
|
+
* sentence about a MACHINE rather than about a run: a config we will not rewrite stays un-rewritten
|
|
13
|
+
* until somebody edits it, at which point the notice's own text changes and it is said again.
|
|
14
|
+
*
|
|
15
|
+
* Stamped by content, not by run. If the stamp cannot be read or written the notice simply prints,
|
|
16
|
+
* which is the failure that costs a duplicate line rather than a missed instruction.
|
|
17
|
+
*/
|
|
18
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
/** Where the stamp lives, under the state home the caller resolves. */
|
|
21
|
+
export const NOTICE_MEMORY_BASENAME = 'agent-notices.json';
|
|
22
|
+
/** Identity of a notice: the same client, the same file, for the same reason. */
|
|
23
|
+
export function noticeKey(n) {
|
|
24
|
+
return JSON.stringify([n.name, n.file, n.why]);
|
|
25
|
+
}
|
|
26
|
+
/** Pure. The notices `said` has not already recorded, in order, without repeats. */
|
|
27
|
+
export function unsaidNotices(said, notices) {
|
|
28
|
+
const seen = new Set(said);
|
|
29
|
+
const out = [];
|
|
30
|
+
for (const n of notices) {
|
|
31
|
+
const key = noticeKey(n);
|
|
32
|
+
if (seen.has(key))
|
|
33
|
+
continue;
|
|
34
|
+
seen.add(key);
|
|
35
|
+
out.push(n);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
/** Every key recorded before. An unreadable or malformed stamp reads as "nothing was said". */
|
|
40
|
+
export function readSaid(stateHome) {
|
|
41
|
+
try {
|
|
42
|
+
const raw = readFileSync(join(stateHome, NOTICE_MEMORY_BASENAME), 'utf8');
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
if (!Array.isArray(parsed))
|
|
45
|
+
return [];
|
|
46
|
+
return parsed.filter((k) => 'string' === typeof k);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Record these keys beside whatever was there. A failed write costs a repeated line, nothing more. */
|
|
53
|
+
export function rememberSaid(stateHome, keys) {
|
|
54
|
+
if (0 === keys.length)
|
|
55
|
+
return;
|
|
56
|
+
const merged = [...new Set([...readSaid(stateHome), ...keys])];
|
|
57
|
+
try {
|
|
58
|
+
mkdirSync(stateHome, { recursive: true });
|
|
59
|
+
writeFileSync(join(stateHome, NOTICE_MEMORY_BASENAME), JSON.stringify(merged, null, 2));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Said once too often is the cost. Silence here is deliberate: a stamp that cannot be written
|
|
63
|
+
// must not fail an install.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -56,6 +56,7 @@ export declare const WAIT_CEILING_MS: number;
|
|
|
56
56
|
* that is a live server whatever its parent did.
|
|
57
57
|
*/
|
|
58
58
|
export declare function judgeWait(facts: WaitFacts): WaitVerdict;
|
|
59
|
+
export declare function portBusyMessage(output: string): string | undefined;
|
|
59
60
|
export declare function announcedUrl(output: string): string | undefined;
|
|
60
61
|
/**
|
|
61
62
|
* The url to watch: whatever the tool announced, else one composed from a port we OBSERVED our own
|
|
@@ -45,6 +45,22 @@ export function judgeWait(facts) {
|
|
|
45
45
|
? WaitVerdict.HUNG
|
|
46
46
|
: WaitVerdict.WAITING;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* CRA (and webpack) print a prompt rather than a URL when their default port is taken, then exit
|
|
50
|
+
* or hang waiting for a tty. Setup used to report only "exited without serving anything", which
|
|
51
|
+
* hid the port that was actually busy (#802).
|
|
52
|
+
*/
|
|
53
|
+
const CRA_PORT_BUSY = /Something is already running on port (\d+)/i;
|
|
54
|
+
const NAMED_PORT_BUSY = /Port (\d+) is (?:already )?in use/i;
|
|
55
|
+
const EADDRINUSE_PORT = /(?:EADDRINUSE|address already in use)[^\d]*(\d+)/i;
|
|
56
|
+
export function portBusyMessage(output) {
|
|
57
|
+
const match = CRA_PORT_BUSY.exec(output) ?? NAMED_PORT_BUSY.exec(output) ?? EADDRINUSE_PORT.exec(output);
|
|
58
|
+
const port = match?.[1];
|
|
59
|
+
if (undefined === port)
|
|
60
|
+
return undefined;
|
|
61
|
+
return (`port ${port} is already in use — something else is serving there, so this launcher did not ` +
|
|
62
|
+
`bind. Pass --url http://localhost:${port} if that process is this app, or free the port and re-run.`);
|
|
63
|
+
}
|
|
48
64
|
/** A url the dev server announced, if it has announced one. Never composed, only read. */
|
|
49
65
|
const URL_IN_OUTPUT = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]):\d+/;
|
|
50
66
|
export function announcedUrl(output) {
|
|
@@ -16,7 +16,7 @@ import { spawnDaemon } from '../../daemon/daemon.js';
|
|
|
16
16
|
import { probePresence, presenceIsUsable } from '../../daemon/binding/port-presence.js';
|
|
17
17
|
import { waitForDaemon, probeDaemon } from '../../../surface/mcp/mcp-proxy.js';
|
|
18
18
|
import { daemonSpawnArgs } from '../../cli/daemon-start-options.js';
|
|
19
|
-
import { fetchStatus } from '../../
|
|
19
|
+
import { fetchStatus } from '../../daemon/binding/daemon-status-probe.js';
|
|
20
20
|
export const EnsureDaemon = {
|
|
21
21
|
/** Something usable was already there. */
|
|
22
22
|
ADOPTED: 'adopted',
|
|
@@ -3,6 +3,9 @@ import { reportInitOutcome } from '../../../telemetry/init-telemetry.js';
|
|
|
3
3
|
import { reportOnboardingStep } from '../../../telemetry/onboarding-funnel.js';
|
|
4
4
|
import { defaultPairingTokenDir, readOrCreatePairingTokenSync, } from '../../../portal/bridge/pairing-token.js';
|
|
5
5
|
import { declaredInstallSource } from '../../../telemetry/install-source.js';
|
|
6
|
+
/** The variable Claude Code sets on every process it spawns, and the value it sets it to. */
|
|
7
|
+
const CLAUDE_CODE_MARKER_ENV = 'CLAUDECODE';
|
|
8
|
+
const CLAUDE_CODE_MARKER_VALUE = '1';
|
|
6
9
|
export function serverInitHost() {
|
|
7
10
|
return {
|
|
8
11
|
span(name, fields, fn) {
|
|
@@ -33,5 +36,10 @@ export function serverInitHost() {
|
|
|
33
36
|
installSource() {
|
|
34
37
|
return declaredInstallSource();
|
|
35
38
|
},
|
|
39
|
+
// Claude Code marks every process it spawns. If the marker ever changes, init falls back to the
|
|
40
|
+
// notice it printed before, which is the safe direction.
|
|
41
|
+
insideClaudeCode() {
|
|
42
|
+
return CLAUDE_CODE_MARKER_VALUE === process.env[CLAUDE_CODE_MARKER_ENV];
|
|
43
|
+
},
|
|
36
44
|
};
|
|
37
45
|
}
|
|
@@ -15,7 +15,7 @@ import { relaunchDecision } from '../bringup/relaunch.js';
|
|
|
15
15
|
import { claudeTranscriptExists, codexSessionFor } from '../terminal/transcripts.js';
|
|
16
16
|
import { probePresence } from '../../daemon/binding/port-presence.js';
|
|
17
17
|
import { probeDaemon } from '../../../surface/mcp/proxy/proxy-daemon-probe.js';
|
|
18
|
-
import { fetchStatus } from '../../
|
|
18
|
+
import { fetchStatus } from '../../daemon/binding/daemon-status-probe.js';
|
|
19
19
|
import { collectEnv, DEFAULT_PHASE_TIMEOUT_MS } from '../setup-options.js';
|
|
20
20
|
/** How often the runtime phases look again: fast enough not to be the wait, slow enough to be free. */
|
|
21
21
|
const POLL_MS = 250;
|
|
@@ -129,6 +129,18 @@ export async function continueAfterInit(parsed, result, io, cwd) {
|
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
131
|
io.print('');
|
|
132
|
+
// The precondition for everything the closing asks for, printed on the path that could not say
|
|
133
|
+
// it. An agent client reads its MCP server list when it STARTS and never re-reads it, so on the
|
|
134
|
+
// run that first registers Reticle the `reticle_*` tools are not in the session that just asked
|
|
135
|
+
// for them -- and this path closes by telling that session to call `reticle_act_and_wait`.
|
|
136
|
+
// `restartHint` has said this for a long time and is printed only when init stops at the files.
|
|
137
|
+
if (true === result.mcpNewlyRegistered) {
|
|
138
|
+
io.print('Reticle was registered with your agent by this run, and an agent reads its tool list only ' +
|
|
139
|
+
'when it starts: the `reticle_*` tools are NOT in this session yet. Restart it first — ' +
|
|
140
|
+
'`npx @reticlehq/server init --relaunch` prints the exact resume command for Claude Code ' +
|
|
141
|
+
'and Codex. Once per machine.');
|
|
142
|
+
io.print('');
|
|
143
|
+
}
|
|
132
144
|
if (outcome.ok && !outcome.flowSaved) {
|
|
133
145
|
// Success, and no flow. Saying "a flow was driven" here would replace a wrong exit code with
|
|
134
146
|
// a wrong sentence, which is the worse of the two: the exit code is read by CI and the
|
|
@@ -15,7 +15,7 @@ import { spawn, spawnSync } from 'node:child_process';
|
|
|
15
15
|
import { Socket } from 'node:net';
|
|
16
16
|
import { existsSync, readdirSync } from 'node:fs';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
|
-
import { fetchStatus } from '../
|
|
18
|
+
import { fetchStatus } from '../daemon/binding/daemon-status-probe.js';
|
|
19
19
|
import { descendants, parseLsofPorts, parseNetstatListeners, parseWmicProcesses, } from './terminal/listeners.js';
|
|
20
20
|
import { loopbackProbeUrls } from './probe/loopback-probe-urls.js';
|
|
21
21
|
const WINDOWS = 'win32' === process.platform;
|
|
@@ -43,8 +43,15 @@ export function describePage(finding, url) {
|
|
|
43
43
|
return (`${url} is served, and the SDK is NOT in the page. That is almost always a dev server still ` +
|
|
44
44
|
'serving the bundle it built before the build config was edited: restart it and hard-reload.');
|
|
45
45
|
case PageFinding.SDK_PRESENT:
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// This used to end "so it loaded and returned early", which is a claim about a code path this
|
|
47
|
+
// check cannot see — and at least one real occurrence was the opposite. On a Windows install
|
|
48
|
+
// gate cell the page console read "could not open a websocket to ws://localhost:<port>, 3
|
|
49
|
+
// attempts, all failed": the SDK dialled three times and the socket failed, having returned
|
|
50
|
+
// early from nothing. What is knowable here is that the SDK is present and no session arrived.
|
|
51
|
+
return (`The SDK IS in the page at ${url} and never dialled the bridge: a localhost guard, a ` +
|
|
52
|
+
'production-only check, a bridge port that differs on the two sides, or `localhost` ' +
|
|
53
|
+
'resolving to IPv6 where the daemon is only on IPv4 — `npx @reticlehq/server doctor` names ' +
|
|
54
|
+
'that last one, and the page console names the first three.');
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
/**
|
|
@@ -39,6 +39,18 @@ export interface SetupOutcome {
|
|
|
39
39
|
readonly notes: string[];
|
|
40
40
|
}
|
|
41
41
|
/** Everything the sequence needs from the world, so none of it is reached for directly. */
|
|
42
|
+
/**
|
|
43
|
+
* The daemon's reason for "no session", in the two lengths its two readers need.
|
|
44
|
+
*
|
|
45
|
+
* Same fact, not two facts: `full` is the lead plus the differential behind it, so a reader given
|
|
46
|
+
* only one of them is never given something the other contradicts.
|
|
47
|
+
*/
|
|
48
|
+
export interface DaemonReason {
|
|
49
|
+
/** One line, printed to the person watching the install. */
|
|
50
|
+
readonly lead: string;
|
|
51
|
+
/** The whole differential, recorded for the agent reading `--json`. Defaults to the lead. */
|
|
52
|
+
readonly full?: string;
|
|
53
|
+
}
|
|
42
54
|
export interface SetupEffects {
|
|
43
55
|
/** Start the dev server. Resolves once started; the caller owns stopping it. */
|
|
44
56
|
readonly startDevServer: (command: string, cwd: string) => Promise<void>;
|
|
@@ -70,6 +82,19 @@ export interface SetupEffects {
|
|
|
70
82
|
*/
|
|
71
83
|
readonly openBrowser: (url: string) => Promise<void>;
|
|
72
84
|
readonly listSessions: () => Promise<CandidateSession[]>;
|
|
85
|
+
/**
|
|
86
|
+
* The daemon's own account of why nothing connected, if it can be asked.
|
|
87
|
+
*
|
|
88
|
+
* The daemon is the only party that can see a hello it REFUSED, and a refusal is the one piece of
|
|
89
|
+
* positive evidence in the whole failure -- only an SDK dials the bridge, so a turned-away page
|
|
90
|
+
* proves the app is running, instrumented and pointed here. `page-probe.ts` has said since it was
|
|
91
|
+
* written that this sentence is the one to lead with and that the page finding merely adds the
|
|
92
|
+
* page-side fact; only the second half was wired.
|
|
93
|
+
*
|
|
94
|
+
* Optional, and allowed to fail: setup must still report what it saw when the daemon cannot be
|
|
95
|
+
* reached, which is itself one of the states this runs in.
|
|
96
|
+
*/
|
|
97
|
+
readonly daemonWhy?: () => Promise<DaemonReason | undefined>;
|
|
73
98
|
readonly now: () => number;
|
|
74
99
|
readonly sleep: (ms: number) => Promise<void>;
|
|
75
100
|
readonly note: (line: string) => void;
|
|
@@ -95,11 +120,4 @@ export interface SetupInput {
|
|
|
95
120
|
readonly phaseTimeoutMs: number;
|
|
96
121
|
readonly pollMs: number;
|
|
97
122
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Run the phases. Stops at the first one that cannot continue, and always says what is left.
|
|
100
|
-
*
|
|
101
|
-
* A phase that fails is not an error to throw: the caller is usually an agent, and a thrown
|
|
102
|
-
* exception loses the four things it needs — how far this got, the url, the session, and what to do
|
|
103
|
-
* about it.
|
|
104
|
-
*/
|
|
105
123
|
export declare function runSetupPhases(input: SetupInput, fx: SetupEffects): Promise<SetupOutcome>;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* way of going wrong, and the alternative to injecting them is a test that boots a real dev server
|
|
14
14
|
* and a real browser to find out what happens when neither works.
|
|
15
15
|
*/
|
|
16
|
-
import { judgeWait, QUIET_MEANS_HUNG_MS, urlToWatch, WaitVerdict, } from './bringup/dev-server-wait.js';
|
|
16
|
+
import { judgeWait, portBusyMessage, QUIET_MEANS_HUNG_MS, urlToWatch, WaitVerdict, } from './bringup/dev-server-wait.js';
|
|
17
17
|
import { waitProgressLine } from './terminal/wait-progress.js';
|
|
18
18
|
/**
|
|
19
19
|
* Windows gets longer to say something before silence counts as a wedge.
|
|
@@ -69,6 +69,23 @@ const stop = (input, phase, partial, notes) => ({
|
|
|
69
69
|
* exception loses the four things it needs — how far this got, the url, the session, and what to do
|
|
70
70
|
* about it.
|
|
71
71
|
*/
|
|
72
|
+
/**
|
|
73
|
+
* Ask the daemon why, and treat every failure as "it did not say".
|
|
74
|
+
*
|
|
75
|
+
* A daemon that cannot be reached is one of the states this runs in, so an error here is data, not
|
|
76
|
+
* an exception: the page finding below still prints and the run still ends with its own verdict.
|
|
77
|
+
*/
|
|
78
|
+
async function daemonWhy(fx) {
|
|
79
|
+
if (undefined === fx.daemonWhy)
|
|
80
|
+
return undefined;
|
|
81
|
+
try {
|
|
82
|
+
const why = await fx.daemonWhy();
|
|
83
|
+
return undefined === why || 0 === why.lead.length ? undefined : why;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
72
89
|
export async function runSetupPhases(input, fx) {
|
|
73
90
|
const notes = [];
|
|
74
91
|
const note = (line) => {
|
|
@@ -139,7 +156,8 @@ export async function runSetupPhases(input, fx) {
|
|
|
139
156
|
break;
|
|
140
157
|
}
|
|
141
158
|
if (WaitVerdict.DEAD === verdict) {
|
|
142
|
-
note(
|
|
159
|
+
note(portBusyMessage(fx.devServerOutput()) ??
|
|
160
|
+
'The dev server exited without serving anything.');
|
|
143
161
|
return stop(input, SetupPhase.DEV_SERVER, {}, notes);
|
|
144
162
|
}
|
|
145
163
|
if (WaitVerdict.HUNG === verdict) {
|
|
@@ -238,7 +256,25 @@ export async function runSetupPhases(input, fx) {
|
|
|
238
256
|
'the capture helper not installed, or a CSP that blocks the bridge: run `npx @reticlehq/server doctor`.');
|
|
239
257
|
}
|
|
240
258
|
else {
|
|
241
|
-
//
|
|
259
|
+
// The daemon first, when it has something to say: it is the only party that can see a hello it
|
|
260
|
+
// refused, and that outranks anything inferred from an absence. Then the page, which is the
|
|
261
|
+
// one thing the daemon cannot know, and worth one fetch.
|
|
262
|
+
const why = await daemonWhy(fx);
|
|
263
|
+
if (undefined !== why) {
|
|
264
|
+
// Two readers, two lengths, one fetch.
|
|
265
|
+
//
|
|
266
|
+
// The person watching an install gets the LEAD — that is what trimming this diagnosis was
|
|
267
|
+
// for. The agent reads `notes` out of `--json`, and what it acts on is the differential:
|
|
268
|
+
// which ports were actually scanned (so an empty result is not proof), why a missing
|
|
269
|
+
// `.reticle.json` is expected in a monorepo, and the lease that opens a URL on a box with
|
|
270
|
+
// no browser at all.
|
|
271
|
+
//
|
|
272
|
+
// Printing only the lead put BOTH readers on the lead, which silently emptied the agent
|
|
273
|
+
// surface: `init --json` stopped mentioning the lease, and `break/break-matrix.mjs`
|
|
274
|
+
// (`no-browser-to-open`) went red for exactly that reason.
|
|
275
|
+
fx.note(why.lead);
|
|
276
|
+
notes.push(why.full ?? why.lead);
|
|
277
|
+
}
|
|
242
278
|
note(describePage(readPage(await fx.probePage(url)), url));
|
|
243
279
|
}
|
|
244
280
|
return stop(input, SetupPhase.CONNECT, { url }, notes);
|
|
@@ -256,12 +292,18 @@ export async function runSetupPhases(input, fx) {
|
|
|
256
292
|
// Both routes, because `explore` needs a model: without ANTHROPIC_API_KEY it answers "No model
|
|
257
293
|
// configured to drive the app". Naming only that one hands the reader a dead end on any machine
|
|
258
294
|
// without a key, which is the same defect this release spent its time removing everywhere else.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
295
|
+
// Three short lines rather than one paragraph, and each says who it is for.
|
|
296
|
+
//
|
|
297
|
+
// This was a single 524-character note, measured on a real first run: eighty-three words, no
|
|
298
|
+
// break, naming `reticle_*` tools at a reader who had just typed `reticle init` in a terminal and
|
|
299
|
+
// has no such tools to call. The facts were right and the shape made them unreadable, which on
|
|
300
|
+
// the last line of onboarding is the same as not saying them.
|
|
301
|
+
note(`Connected. ${url} is instrumented. Onboarding is done; nothing is verified yet.`);
|
|
302
|
+
note('Agent: drive one flow and end it with `reticle_act_and_wait` or `reticle_assert`. Those two ' +
|
|
303
|
+
'produce a verdict; nothing else does.');
|
|
304
|
+
note('Or hand over the whole drive: `reticle_verify { action: "explore", persona: "<who does ' +
|
|
305
|
+
'what>" }` records what it drove, so later runs replay with no model in the loop. Needs ' +
|
|
306
|
+
'ANTHROPIC_API_KEY.');
|
|
265
307
|
return {
|
|
266
308
|
ok: true,
|
|
267
309
|
reachedPhase: SetupPhase.CONNECT,
|
|
@@ -22,7 +22,10 @@ import { reticleStateHome } from '../daemon/daemon.js';
|
|
|
22
22
|
import { readDevServers } from '../daemon/dev-servers.js';
|
|
23
23
|
import { urlOfExistingApp } from './probe/existing-app.js';
|
|
24
24
|
import { listSessions, OwnedDevServer, probePage } from './node-effects.js';
|
|
25
|
+
import { fetchStatus } from '../daemon/binding/daemon-status-probe.js';
|
|
26
|
+
import { summarizeStatus } from '../cli/launch/cli-launch.js';
|
|
25
27
|
import { runSetupPhases, } from './run-setup.js';
|
|
28
|
+
import { noticeKey, readSaid, rememberSaid, unsaidNotices } from './agent-notice-memory.js';
|
|
26
29
|
/**
|
|
27
30
|
* The dev server a crash should take with it, if one is running right now.
|
|
28
31
|
*
|
|
@@ -89,10 +92,20 @@ export function registerOtherAgents(print) {
|
|
|
89
92
|
if (0 < wrote.length) {
|
|
90
93
|
print(`registered the MCP server with ${wrote.length} more agent(s): ${wrote.map((r) => r.name).join(', ')}`);
|
|
91
94
|
}
|
|
92
|
-
// A format we will not rewrite is somebody's to edit, so it has to be said rather than skipped
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
// A format we will not rewrite is somebody's to edit, so it has to be said rather than skipped --
|
|
96
|
+
// but said ONCE. Two commands call this function by design, so a `curl | sh` followed by
|
|
97
|
+
// `reticle init` printed the same two paragraphs twice in one sitting, and again on every re-run
|
|
98
|
+
// after that. See agent-notice-memory.ts: the stamp is keyed on the notice's own text, so a
|
|
99
|
+
// config that changes is reported again.
|
|
100
|
+
const stateHome = reticleStateHome();
|
|
101
|
+
const manual = results
|
|
102
|
+
.filter((r) => 'manual' === r.action)
|
|
103
|
+
.map((r) => ({ name: r.name, file: r.file, why: r.why }));
|
|
104
|
+
const fresh = unsaidNotices(readSaid(stateHome), manual);
|
|
105
|
+
for (const notice of fresh) {
|
|
106
|
+
print(`${notice.name}: ${notice.why} — add the reticle entry to ${notice.file} by hand.`);
|
|
95
107
|
}
|
|
108
|
+
rememberSaid(stateHome, fresh.map((n) => noticeKey(n)));
|
|
96
109
|
const skills = applyAgentSkills(agentIo, { home, platform });
|
|
97
110
|
if (0 < skills.length)
|
|
98
111
|
print(`wrote the /reticle skill for ${skills.length} agent(s)`);
|
|
@@ -166,6 +179,21 @@ export async function runSetupCommand(input, print) {
|
|
|
166
179
|
}
|
|
167
180
|
},
|
|
168
181
|
listSessions: () => listSessions(input.bridgePort),
|
|
182
|
+
// `summarizeStatus` already narrows this payload for `reticle status`; reusing it here keeps one
|
|
183
|
+
// reader of the wire shape rather than two that can disagree about which key carries the reason.
|
|
184
|
+
// Both lengths, from the one read: the lead is printed to the person watching the install, the
|
|
185
|
+
// full differential is recorded for the agent reading `--json`. Returning only the lead here is
|
|
186
|
+
// what emptied the agent surface — see the note at the call site in `run-setup.ts`.
|
|
187
|
+
daemonWhy: async () => {
|
|
188
|
+
const status = summarizeStatus(await fetchStatus(input.bridgePort));
|
|
189
|
+
const full = status.why;
|
|
190
|
+
// A daemon too old to report the short one falls back to the long one, because saying the
|
|
191
|
+
// right thing at length beats saying nothing.
|
|
192
|
+
const lead = status.whyLead ?? full;
|
|
193
|
+
if (undefined === lead)
|
|
194
|
+
return undefined;
|
|
195
|
+
return undefined === full ? { lead } : { lead, full };
|
|
196
|
+
},
|
|
169
197
|
now: () => Date.now(),
|
|
170
198
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
171
199
|
note: print,
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
* it worked.
|
|
30
30
|
*/
|
|
31
31
|
import { InitConfirmation, RETICLE_DEFAULT_PORT } from '@reticlehq/core';
|
|
32
|
-
import {
|
|
32
|
+
import { summarizeStatus } from '../../cli/launch/cli-launch.js';
|
|
33
|
+
import { fetchStatus } from '../../daemon/binding/daemon-status-probe.js';
|
|
33
34
|
import { readDevServers } from '../../daemon/dev-servers.js';
|
|
34
35
|
import { devServersForProject } from '@reticlehq/core/artifacts';
|
|
35
36
|
import { readProjectId } from '../../cli/ports/resolve/cli-port.js';
|
|
@@ -29,6 +29,33 @@ type ExecutionKind = (typeof ExecutionKind)[keyof typeof ExecutionKind];
|
|
|
29
29
|
* `apply_update` npm-installs into the user's own project (polluting their package.json).
|
|
30
30
|
*/
|
|
31
31
|
export declare function classifyExecutionKind(script: string): ExecutionKind;
|
|
32
|
+
/**
|
|
33
|
+
* Spawn npm, through a shell on Windows because `npm.cmd` cannot be spawned without one.
|
|
34
|
+
*
|
|
35
|
+
* Since the CVE-2024-27980 fix -- in every supported Node -- spawning a `.cmd` with no shell throws
|
|
36
|
+
* EINVAL. This repo has already paid for that lesson once, at `init/src/node-io.ts`: `reticle setup
|
|
37
|
+
* mcp` probed for the Claude CLI with a bare `execFileSync`, the probe threw, and the installer
|
|
38
|
+
* told the user their machine had no Claude Code on it. The same shape sat here, in the SELF-UPDATE
|
|
39
|
+
* path -- the channel every other fix reaches a user through, and the one place a silent failure
|
|
40
|
+
* strands somebody on an old version indefinitely.
|
|
41
|
+
*
|
|
42
|
+
* Arguments are quoted with init's own rule rather than a second copy of it. They are internal
|
|
43
|
+
* today (`install`, `-g`, and a package spec whose version comes from the registry), but a shell
|
|
44
|
+
* turns "internal" into "whatever the registry said", and the quoting is one import.
|
|
45
|
+
*/
|
|
46
|
+
export declare function npmSpawn(args: string[],
|
|
47
|
+
/**
|
|
48
|
+
* Injected so the Windows branch is testable on every platform.
|
|
49
|
+
*
|
|
50
|
+
* The bug this fixes could only be OBSERVED on Windows, and a check that can only fail on a
|
|
51
|
+
* machine nobody here runs is a check that fails in CI at the earliest and in the field at the
|
|
52
|
+
* latest. The rule is a pure function of the platform string, so it is one.
|
|
53
|
+
*/
|
|
54
|
+
on?: string): {
|
|
55
|
+
file: string;
|
|
56
|
+
argv: string[];
|
|
57
|
+
shell: boolean;
|
|
58
|
+
};
|
|
32
59
|
interface NpmInstall {
|
|
33
60
|
args: string[];
|
|
34
61
|
cwd?: string;
|
|
@@ -6,6 +6,7 @@ import { dirname, join } from 'node:path';
|
|
|
6
6
|
import { loadManifest, saveManifest } from './update-checker.js';
|
|
7
7
|
import { RETICLE_NPM_PACKAGE, SERVER_VERSION } from '../version/identity/server-version.js';
|
|
8
8
|
import { log } from '../../log.js';
|
|
9
|
+
import { windowsShellArg } from '@reticlehq/init';
|
|
9
10
|
import { TelemetryEventKind } from '@reticlehq/core/telemetry';
|
|
10
11
|
import { getTelemetry } from '../../telemetry/telemetry.js';
|
|
11
12
|
import { wasNudged } from './nudge-credit.js';
|
|
@@ -34,7 +35,6 @@ export async function reportVersionChange(from, to, direction) {
|
|
|
34
35
|
/* best-effort, like every other send */
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
const NPM_BIN = NodePlatform.WINDOWS === platform() ? 'npm.cmd' : 'npm';
|
|
38
38
|
const NPM_TIMEOUT_MS = 120_000;
|
|
39
39
|
/** How this reticle process was launched — determines which npm strategy to use for updates. */
|
|
40
40
|
const ExecutionKind = {
|
|
@@ -91,9 +91,42 @@ function findLocalProjectRoot() {
|
|
|
91
91
|
dir = parent;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Spawn npm, through a shell on Windows because `npm.cmd` cannot be spawned without one.
|
|
96
|
+
*
|
|
97
|
+
* Since the CVE-2024-27980 fix -- in every supported Node -- spawning a `.cmd` with no shell throws
|
|
98
|
+
* EINVAL. This repo has already paid for that lesson once, at `init/src/node-io.ts`: `reticle setup
|
|
99
|
+
* mcp` probed for the Claude CLI with a bare `execFileSync`, the probe threw, and the installer
|
|
100
|
+
* told the user their machine had no Claude Code on it. The same shape sat here, in the SELF-UPDATE
|
|
101
|
+
* path -- the channel every other fix reaches a user through, and the one place a silent failure
|
|
102
|
+
* strands somebody on an old version indefinitely.
|
|
103
|
+
*
|
|
104
|
+
* Arguments are quoted with init's own rule rather than a second copy of it. They are internal
|
|
105
|
+
* today (`install`, `-g`, and a package spec whose version comes from the registry), but a shell
|
|
106
|
+
* turns "internal" into "whatever the registry said", and the quoting is one import.
|
|
107
|
+
*/
|
|
108
|
+
export function npmSpawn(args,
|
|
109
|
+
/**
|
|
110
|
+
* Injected so the Windows branch is testable on every platform.
|
|
111
|
+
*
|
|
112
|
+
* The bug this fixes could only be OBSERVED on Windows, and a check that can only fail on a
|
|
113
|
+
* machine nobody here runs is a check that fails in CI at the earliest and in the field at the
|
|
114
|
+
* latest. The rule is a pure function of the platform string, so it is one.
|
|
115
|
+
*/
|
|
116
|
+
on = platform()) {
|
|
117
|
+
const bin = NodePlatform.WINDOWS === on ? 'npm.cmd' : 'npm';
|
|
118
|
+
if (NodePlatform.WINDOWS !== on)
|
|
119
|
+
return { file: bin, argv: args, shell: false };
|
|
120
|
+
return { file: [bin, ...args.map(windowsShellArg)].join(' '), argv: [], shell: true };
|
|
121
|
+
}
|
|
94
122
|
function runNpm(args, opts = {}) {
|
|
123
|
+
const spawn = npmSpawn(args);
|
|
95
124
|
return new Promise((resolve, reject) => {
|
|
96
|
-
execFile(
|
|
125
|
+
execFile(spawn.file, spawn.argv, {
|
|
126
|
+
timeout: NPM_TIMEOUT_MS,
|
|
127
|
+
...(spawn.shell ? { shell: true } : {}),
|
|
128
|
+
...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),
|
|
129
|
+
}, (err, _stdout, stderr) => {
|
|
97
130
|
if (err !== null) {
|
|
98
131
|
reject(new Error(`npm ${args.join(' ')} failed: ${stderr !== '' ? stderr : err.message}`));
|
|
99
132
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { sessionRoot } from '../../memory/project/session-root.js';
|
|
3
|
+
import { projectForRoot } from '../../memory/project/project-for-root.js';
|
|
2
4
|
import { ReticleTool } from '@reticlehq/core';
|
|
3
5
|
import { asNumber, asString } from '@reticlehq/core';
|
|
4
6
|
import { stepCountSchema, timeoutMsSchema } from '../../surface/tools/args/numeric-bounds.js';
|
|
@@ -80,7 +82,7 @@ export const CRAWL_TOOLS = [
|
|
|
80
82
|
...routesFromEvents(session.eventsSince(since)),
|
|
81
83
|
];
|
|
82
84
|
if (routes.length > 0)
|
|
83
|
-
await deps.
|
|
85
|
+
await projectForRoot(deps, sessionRoot(deps, session.id)).recordRoutes(routes);
|
|
84
86
|
return report;
|
|
85
87
|
},
|
|
86
88
|
},
|
|
@@ -16,6 +16,8 @@ export interface CrawlSession {
|
|
|
16
16
|
/** The page under test, so a third-party beacon cannot be reported against a control. Optional
|
|
17
17
|
* for the same reason `currentDocumentId` is. */
|
|
18
18
|
readonly url?: string | undefined;
|
|
19
|
+
/** Same-origin endpoints the project declared as background. Optional like `url`. */
|
|
20
|
+
readonly background?: readonly string[] | undefined;
|
|
19
21
|
/**
|
|
20
22
|
* Is the page BACKGROUNDED? A hidden tab has its rAF clamped and the DOM observer flushes on rAF,
|
|
21
23
|
* so real renders emit nothing and every absence-derived contradiction reads that silence as a
|
|
@@ -8,7 +8,8 @@ import { span } from '../../trace.js';
|
|
|
8
8
|
import { ActionType, CRAWL_DEFAULTS, CrawlAnomalyKind, DANGEROUS_ACTION_CONFIRM_ARG, EventType, ReticleCommand, } from '@reticlehq/core';
|
|
9
9
|
import { crawlEmptyNote } from './crawl-empty.js';
|
|
10
10
|
import { asNumber, asRecord, asString } from '@reticlehq/core';
|
|
11
|
-
import { parseInteractive
|
|
11
|
+
import { parseInteractive } from '@reticlehq/core';
|
|
12
|
+
import { sourceOf } from '../../surface/tools/tools-helpers.js';
|
|
12
13
|
import { isSessionReplacedError } from '../../portal/session/facts/session-replaced.js';
|
|
13
14
|
import { ReticleTool } from '@reticlehq/core';
|
|
14
15
|
import { findContradictions } from '@reticlehq/engine/disagreement/contradictions.js';
|
|
@@ -346,6 +347,7 @@ export async function crawl(session, opts, sleep) {
|
|
|
346
347
|
currentDocumentId: session.currentDocumentId,
|
|
347
348
|
currentEditEpoch: session.currentEditEpoch,
|
|
348
349
|
appOrigin: session.url,
|
|
350
|
+
background: session.background,
|
|
349
351
|
// A hidden tab cannot be observed, so its silence is not evidence. Stated from the session
|
|
350
352
|
// rather than left to a heartbeat landing inside a 300ms window.
|
|
351
353
|
pageHidden: session.throttled?.(),
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a drive did, written from the evidence rather than narrated by the model that drove.
|
|
3
|
+
*
|
|
4
|
+
* `HarnessResult.summary` is the driver's own account, and it has always carried a warning that
|
|
5
|
+
* nothing downstream grades it. That was a caveat when the driver was a language model. It became a
|
|
6
|
+
* hole the moment a System One model could drive: Jev answers typed questions and cannot produce a
|
|
7
|
+
* sentence, so `summary` came back EMPTY, and an agent calling `explore` got a result with no
|
|
8
|
+
* account of what had happened in it at all.
|
|
9
|
+
*
|
|
10
|
+
* Restoring prose would have been the obvious fix and the wrong one. The drive already records
|
|
11
|
+
* every call it made, with the arguments it used and the verdict the engine returned — so the
|
|
12
|
+
* account can be DERIVED. That is strictly better than a narration: a model summarising its own
|
|
13
|
+
* drive is the one witness with a reason to round "unknown" up to "worked", and this cannot, because
|
|
14
|
+
* it is not writing about the drive, it is reading it.
|
|
15
|
+
*
|
|
16
|
+
* Written for the agent on the other end of the MCP call. It leads with what was proved, because
|
|
17
|
+
* that is what a coding agent has to gate on, and it says "not proved" in those words rather than
|
|
18
|
+
* leaving a verdict of `unknown` to be read as a pass.
|
|
19
|
+
*/
|
|
20
|
+
import type { ToolOutcome } from './harness.js';
|
|
21
|
+
/** One driven action, reduced to what a reader needs: what was done, and what it proved. */
|
|
22
|
+
export interface DrivenStep {
|
|
23
|
+
/** The action verb, e.g. `click`. */
|
|
24
|
+
action: string;
|
|
25
|
+
/** The element, named the way a person would say it, falling back to the ref. */
|
|
26
|
+
target: string;
|
|
27
|
+
/** The engine's verdict for this step, when the step declared anything. */
|
|
28
|
+
verified: string | undefined;
|
|
29
|
+
/** The one sentence naming the deciding evidence. */
|
|
30
|
+
because: string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* What the drive CLAIMED would happen, in the words it declared before acting.
|
|
33
|
+
*
|
|
34
|
+
* Carried because "3 action(s) FAILED" is not triageable without it. A failure is either a defect
|
|
35
|
+
* in the app or a wrong guess by the driver, those need opposite responses, and the declared
|
|
36
|
+
* consequence is the only thing that tells them apart. Shown on failures only: on a pass it is
|
|
37
|
+
* noise, and the report is read every drive.
|
|
38
|
+
*/
|
|
39
|
+
claimed: string | undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Replays this drive ran, and what each came back with.
|
|
43
|
+
*
|
|
44
|
+
* The verdict is `status`, not `passed`. Reading the wrong key reported ten replays as "undecided"
|
|
45
|
+
* on a run where every one of them had answered — which is the same false-nothing this report was
|
|
46
|
+
* written to stop, made by the report itself.
|
|
47
|
+
*
|
|
48
|
+
* Three outcomes, kept apart because they mean different things to whoever reads this. `ok` is the
|
|
49
|
+
* journey still holding. `error` is it failing. `drift` is the flow's own anchors no longer
|
|
50
|
+
* resolving — the app moved under a recording, which is a finding about the RECORDING rather than
|
|
51
|
+
* proof the feature broke, and collapsing it into "failed" would send somebody to fix working code.
|
|
52
|
+
*/
|
|
53
|
+
export declare const ReplayOutcome: {
|
|
54
|
+
readonly OK: "ok";
|
|
55
|
+
readonly DRIFT: "drift";
|
|
56
|
+
readonly ERROR: "error";
|
|
57
|
+
};
|
|
58
|
+
export declare function replayedFlows(toolCalls: readonly ToolOutcome[]): {
|
|
59
|
+
name: string;
|
|
60
|
+
status: string | undefined;
|
|
61
|
+
}[];
|
|
62
|
+
/** Reduce the raw call log to the actions that actually drove the app. */
|
|
63
|
+
export declare function drivenSteps(toolCalls: readonly ToolOutcome[]): DrivenStep[];
|
|
64
|
+
/**
|
|
65
|
+
* The account, as lines.
|
|
66
|
+
*
|
|
67
|
+
* Proved and not-proved are counted separately and both are stated, because the failure mode this
|
|
68
|
+
* whole product exists to prevent is a reader taking "we did twelve things" for "twelve things
|
|
69
|
+
* work". `unknown` is deliberately grouped under NOT proved and labelled as undecided evidence
|
|
70
|
+
* rather than as a failure — it calls for a better check, not a code change, and collapsing the two
|
|
71
|
+
* would send an agent to rewrite working code.
|
|
72
|
+
*/
|
|
73
|
+
export declare function describeDrive(toolCalls: readonly ToolOutcome[], savedFlows: readonly string[]): string;
|