@reticlehq/server 2.11.0 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +93 -111
- package/dist/bridge/auth-failure-reason.d.ts +6 -2
- package/dist/bridge/auth-failure-reason.js +15 -8
- package/dist/bridge/bridge.d.ts +18 -14
- package/dist/bridge/bridge.js +248 -165
- package/dist/bridge/bridge.test-harness.d.ts +2 -0
- package/dist/bridge/bridge.test-harness.js +8 -1
- package/dist/bridge/pairing-token.d.ts +5 -0
- package/dist/bridge/pairing-token.js +28 -0
- package/dist/bridge/protocol-skew.d.ts +37 -0
- package/dist/bridge/protocol-skew.js +45 -0
- package/dist/capsule/capsule-store.d.ts +8 -7
- package/dist/capsule/capsule-store.js +1 -1
- package/dist/capsule/causal-summary.d.ts +2 -2
- package/dist/capsule/causal-summary.js +7 -2
- package/dist/chromium-launch-options.d.ts +28 -0
- package/dist/chromium-launch-options.js +29 -0
- package/dist/cli/cli-doctor.js +39 -1
- package/dist/cli/cli-flow-commands.d.ts +3 -1
- package/dist/cli/cli-flow-commands.js +34 -4
- package/dist/cli/cli-kill.d.ts +2 -1
- package/dist/cli/cli-launch.d.ts +11 -1
- package/dist/cli/cli-launch.js +59 -10
- package/dist/cli/cli-parse.d.ts +17 -1
- package/dist/cli/cli-parse.js +238 -12
- package/dist/cli/cli-update-commands.js +20 -1
- package/dist/cli/cli-verify.d.ts +2 -1
- package/dist/cli/cli-verify.js +5 -4
- package/dist/cli/cloud-cli.js +428 -253
- package/dist/cli/cloud-keystore.d.ts +37 -0
- package/dist/cli/cloud-keystore.js +97 -0
- package/dist/cli/cloud-kit.d.ts +37 -0
- package/dist/cli/cloud-kit.js +121 -0
- package/dist/cli/cloud-login.d.ts +17 -0
- package/dist/cli/cloud-login.js +183 -0
- package/dist/cli/cloud-session.d.ts +48 -0
- package/dist/cli/cloud-session.js +53 -0
- package/dist/cli/config-discovery.d.ts +2 -1
- package/dist/cli/config-discovery.js +1 -0
- package/dist/cli/daemon-start-options.d.ts +32 -0
- package/dist/cli/daemon-start-options.js +38 -0
- package/dist/cli/device-flow.d.ts +76 -0
- package/dist/cli/device-flow.js +56 -0
- package/dist/cli/doctor-rows.d.ts +12 -1
- package/dist/cli/doctor-rows.js +12 -1
- package/dist/cli/drive-attach.d.ts +13 -0
- package/dist/cli/drive-attach.js +18 -0
- package/dist/cli/drive-command.js +7 -3
- package/dist/cli/gate-exit.d.ts +21 -0
- package/dist/cli/gate-exit.js +21 -0
- package/dist/cli/gate-hook-message.d.ts +33 -0
- package/dist/cli/gate-hook-message.js +46 -0
- package/dist/cli/mcp-command.d.ts +25 -0
- package/dist/cli/mcp-command.js +174 -0
- package/dist/cli/project-name.d.ts +30 -0
- package/dist/cli/project-name.js +44 -0
- package/dist/cli/sibling-ports.d.ts +37 -0
- package/dist/cli/sibling-ports.js +56 -0
- package/dist/cli/status-next-action.d.ts +14 -0
- package/dist/cli/status-next-action.js +4 -4
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +167 -186
- package/dist/cloud/cloud-config.d.ts +24 -2
- package/dist/cloud/cloud-config.js +64 -5
- package/dist/cloud/cloud-sync.d.ts +16 -5
- package/dist/cloud/cloud-sync.js +64 -1
- package/dist/cloud/sync-cycle.d.ts +110 -0
- package/dist/cloud/sync-cycle.js +270 -0
- package/dist/cloud/sync-daemon.d.ts +69 -0
- package/dist/cloud/sync-daemon.js +260 -0
- package/dist/cloud/sync-disk.d.ts +9 -0
- package/dist/cloud/sync-disk.js +191 -0
- package/dist/cloud/sync-hash.d.ts +2 -0
- package/dist/cloud/sync-hash.js +35 -0
- package/dist/crawl/crawl-tools.js +5 -3
- package/dist/crawl/crawl.d.ts +12 -13
- package/dist/crawl/crawl.js +25 -3
- package/dist/daemon/daemon-resolve.d.ts +122 -0
- package/dist/daemon/daemon-resolve.js +187 -0
- package/dist/daemon/dev-servers.d.ts +15 -0
- package/dist/daemon/dev-servers.js +68 -0
- package/dist/daemon/free-port.d.ts +29 -0
- package/dist/daemon/free-port.js +55 -0
- package/dist/daemon/heartbeat.d.ts +0 -4
- package/dist/daemon/heartbeat.js +2 -2
- package/dist/daemon/wake-decision.d.ts +32 -0
- package/dist/daemon/wake-decision.js +38 -0
- package/dist/dev/stale-issue-guard.d.ts +0 -2
- package/dist/dev/stale-issue-guard.js +1 -1
- package/dist/domain/domain-tools.js +2 -2
- package/dist/events/contradictions.d.ts +59 -0
- package/dist/events/contradictions.js +253 -27
- package/dist/events/declared.d.ts +17 -0
- package/dist/events/declared.js +43 -1
- package/dist/events/echo-mismatch.js +65 -1
- package/dist/events/event-filters.d.ts +8 -0
- package/dist/events/event-filters.js +19 -2
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +17 -0
- package/dist/events/predicate-asks.d.ts +13 -0
- package/dist/events/predicate-asks.js +22 -0
- package/dist/events/predicate-eval.js +117 -27
- package/dist/events/predicate-parse.d.ts +0 -6
- package/dist/events/predicate-parse.js +43 -3
- package/dist/events/predicate-precheck.d.ts +8 -0
- package/dist/events/predicate-precheck.js +41 -0
- package/dist/events/predicate-route.d.ts +14 -0
- package/dist/events/predicate-route.js +36 -8
- package/dist/events/predicate-schema.d.ts +44 -0
- package/dist/events/predicate-schema.js +126 -23
- package/dist/events/predicate.d.ts +20 -0
- package/dist/events/predicate.js +77 -6
- package/dist/events/split-text-miss.d.ts +30 -0
- package/dist/events/split-text-miss.js +44 -0
- package/dist/flows/coverage.d.ts +0 -8
- package/dist/flows/coverage.js +1 -1
- package/dist/flows/decision.d.ts +5 -1
- package/dist/flows/decision.js +33 -7
- package/dist/flows/flake.d.ts +6 -8
- package/dist/flows/flake.js +1 -1
- package/dist/flows/flow-intent.d.ts +52 -0
- package/dist/flows/flow-intent.js +123 -0
- package/dist/flows/flow-memory-consult.d.ts +55 -0
- package/dist/flows/flow-memory-consult.js +38 -0
- package/dist/flows/flow-replay-run.d.ts +18 -7
- package/dist/flows/flow-replay-run.js +263 -23
- package/dist/flows/flow-replay.js +11 -2
- package/dist/flows/flow-step-runners.d.ts +8 -0
- package/dist/flows/flow-step-runners.js +8 -2
- package/dist/flows/flow-store-for-session.d.ts +31 -0
- package/dist/flows/flow-store-for-session.js +33 -0
- package/dist/flows/flow-success.js +15 -3
- package/dist/flows/flow-tools.js +87 -33
- package/dist/flows/flows.d.ts +14 -1
- package/dist/flows/flows.js +502 -24
- package/dist/flows/heal-run.js +5 -2
- package/dist/flows/predicate-to-expect.d.ts +17 -12
- package/dist/flows/predicate-to-expect.js +36 -15
- package/dist/flows/recordings.d.ts +13 -1
- package/dist/flows/recordings.js +17 -2
- package/dist/flows/replay.d.ts +22 -2
- package/dist/flows/replay.js +28 -2
- package/dist/flows/server-verify.js +16 -2
- package/dist/flows/verify-change-tools.js +9 -3
- package/dist/honesty/blind-spots.d.ts +51 -15
- package/dist/honesty/blind-spots.js +64 -4
- package/dist/honesty/feature-capture.d.ts +150 -0
- package/dist/honesty/feature-capture.js +233 -0
- package/dist/honesty/honesty.d.ts +24 -0
- package/dist/honesty/honesty.js +25 -1
- package/dist/honesty/instrumentation-gaps.d.ts +39 -6
- package/dist/honesty/instrumentation-gaps.js +88 -5
- package/dist/honesty/tool-hit-rate.d.ts +86 -0
- package/dist/honesty/tool-hit-rate.js +90 -0
- package/dist/honesty/undeclared-change.d.ts +51 -0
- package/dist/honesty/undeclared-change.js +56 -0
- package/dist/honesty/verified.d.ts +16 -0
- package/dist/honesty/verified.js +84 -5
- package/dist/http-server.d.ts +0 -1
- package/dist/http-server.js +13 -1
- package/dist/hunt/hunt-report.d.ts +0 -2
- package/dist/hunt/hunt-report.js +1 -1
- package/dist/impact/impact-recorder.d.ts +31 -8
- package/dist/impact/impact-recorder.js +104 -21
- package/dist/impact/impact-store.d.ts +13 -4
- package/dist/impact/impact-store.js +44 -3
- package/dist/index.d.ts +4 -1
- package/dist/index.js +113 -7
- package/dist/init/agent-rules.d.ts +30 -6
- package/dist/init/agent-rules.js +54 -13
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +28 -0
- package/dist/init/capabilities.d.ts +4 -3
- package/dist/init/capabilities.js +23 -3
- package/dist/init/closing-hint.d.ts +36 -0
- package/dist/init/closing-hint.js +120 -0
- package/dist/init/confirm.d.ts +27 -28
- package/dist/init/confirm.js +51 -11
- package/dist/init/cra.d.ts +34 -5
- package/dist/init/cra.js +96 -25
- package/dist/init/csp-check.js +17 -2
- package/dist/init/csp-doctor.d.ts +8 -1
- package/dist/init/csp-doctor.js +8 -3
- package/dist/init/dev-script.d.ts +50 -0
- package/dist/init/dev-script.js +66 -0
- package/dist/init/format-generated.d.ts +18 -0
- package/dist/init/format-generated.js +39 -0
- package/dist/init/install-hint.d.ts +9 -0
- package/dist/init/install-hint.js +29 -0
- package/dist/init/mcp-clients.d.ts +7 -0
- package/dist/init/mcp-clients.js +7 -0
- package/dist/init/mcp.d.ts +17 -6
- package/dist/init/mcp.js +27 -11
- package/dist/init/node-io.js +11 -1
- package/dist/init/plan-framework.d.ts +0 -5
- package/dist/init/plan-framework.js +147 -37
- package/dist/init/plan.d.ts +30 -37
- package/dist/init/plan.js +50 -60
- package/dist/init/preflight.d.ts +28 -0
- package/dist/init/preflight.js +38 -0
- package/dist/init/refresh-rules.d.ts +39 -0
- package/dist/init/refresh-rules.js +51 -0
- package/dist/init/run.d.ts +32 -35
- package/dist/init/run.js +151 -202
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/snippets.js +90 -31
- package/dist/init/vite-config.d.ts +1 -1
- package/dist/init/vite-config.js +39 -25
- package/dist/init/workspace-apps.d.ts +17 -10
- package/dist/init/workspace-apps.js +110 -0
- package/dist/input/network-mock-tools.js +3 -6
- package/dist/input/real-input.d.ts +2 -0
- package/dist/input/real-input.js +28 -7
- package/dist/input/scroll-find.d.ts +7 -4
- package/dist/input/scroll-find.js +63 -25
- package/dist/input/scroll-tools.js +4 -6
- package/dist/input/viewport-tools.js +5 -4
- package/dist/intent/inline-intent.d.ts +37 -0
- package/dist/intent/inline-intent.js +193 -0
- package/dist/intent/intent-shard-store.d.ts +72 -0
- package/dist/intent/intent-shard-store.js +203 -0
- package/dist/intent/intent-shard.d.ts +411 -0
- package/dist/intent/intent-shard.js +176 -0
- package/dist/intent/intent-store.d.ts +49 -0
- package/dist/intent/intent-store.js +133 -0
- package/dist/intent/intent-subject.d.ts +45 -0
- package/dist/intent/intent-subject.js +177 -0
- package/dist/intent/intent-tools.d.ts +2 -0
- package/dist/intent/intent-tools.js +143 -0
- package/dist/intent/open-intents.d.ts +42 -0
- package/dist/intent/open-intents.js +40 -0
- package/dist/journal/envelope.d.ts +26 -26
- package/dist/journal/journal-recorder.d.ts +23 -0
- package/dist/journal/journal-recorder.js +35 -0
- package/dist/license/license-env.d.ts +29 -0
- package/dist/license/license-env.js +148 -0
- package/dist/loopback-agent.d.ts +31 -0
- package/dist/loopback-agent.js +57 -0
- package/dist/mcp/attach-memory.d.ts +83 -0
- package/dist/mcp/attach-memory.js +185 -0
- package/dist/mcp/client-identity.d.ts +31 -0
- package/dist/mcp/client-identity.js +51 -0
- package/dist/mcp/mcp-post-transport.d.ts +26 -0
- package/dist/mcp/mcp-post-transport.js +109 -0
- package/dist/mcp/mcp-proxy.d.ts +2 -8
- package/dist/mcp/mcp-proxy.js +64 -62
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +52 -24
- package/dist/mcp/proxy-backoff.d.ts +11 -0
- package/dist/mcp/proxy-backoff.js +13 -0
- package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
- package/dist/mcp/proxy-daemon-probe.js +7 -1
- package/dist/mcp/proxy-handshake.d.ts +23 -0
- package/dist/mcp/proxy-handshake.js +31 -0
- package/dist/mcp/server-instructions.js +45 -3
- package/dist/mcp/tool-catalog-cache.d.ts +16 -0
- package/dist/mcp/tool-catalog-cache.js +22 -0
- package/dist/memory/memory-tools.d.ts +2 -0
- package/dist/memory/memory-tools.js +82 -0
- package/dist/memory/project-memory.d.ts +34 -0
- package/dist/memory/project-memory.js +96 -0
- package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
- package/dist/oracles/flow-instrument-gaps.js +4 -5
- package/dist/oracles/propose-consequences.js +12 -3
- package/dist/oracles/self-instrument.d.ts +9 -9
- package/dist/oracles/self-instrument.js +19 -13
- package/dist/pool/browser-pool.d.ts +75 -5
- package/dist/pool/browser-pool.js +144 -5
- package/dist/pool/playwright-launcher.js +12 -1
- package/dist/project/fs-port.d.ts +3 -0
- package/dist/project/fs-port.js +3 -2
- package/dist/project/memory-fs.js +6 -1
- package/dist/project/project-tools.js +6 -35
- package/dist/project/reticle-dir.d.ts +17 -2
- package/dist/project/reticle-dir.js +22 -4
- package/dist/runs/context-tools.d.ts +2 -0
- package/dist/runs/context-tools.js +89 -0
- package/dist/runs/run-context.d.ts +40 -0
- package/dist/runs/run-context.js +158 -0
- package/dist/runs/run-diff.d.ts +2 -1
- package/dist/runs/run-store.d.ts +10 -0
- package/dist/runs/run-store.js +10 -0
- package/dist/runs/verification-sync.d.ts +0 -11
- package/dist/runs/verification-sync.js +24 -3
- package/dist/runs/verify-server.d.ts +8 -1
- package/dist/runs/verify-server.js +14 -2
- package/dist/session/command-timeout.d.ts +5 -2
- package/dist/session/command-timeout.js +5 -4
- package/dist/session/dev-server-probe.d.ts +0 -8
- package/dist/session/dev-server-probe.js +1 -1
- package/dist/session/last-act.d.ts +7 -1
- package/dist/session/last-act.js +2 -2
- package/dist/session/lease-visibility.d.ts +67 -0
- package/dist/session/lease-visibility.js +76 -0
- package/dist/session/live-control-tools.js +1 -42
- package/dist/session/no-session-diagnosis.d.ts +33 -0
- package/dist/session/no-session-diagnosis.js +82 -24
- package/dist/session/no-session-next-action.d.ts +12 -0
- package/dist/session/no-session-next-action.js +77 -6
- package/dist/session/no-session-watch.d.ts +18 -6
- package/dist/session/no-session-watch.js +125 -75
- package/dist/session/output-budget.js +4 -1
- package/dist/session/pending-commands.d.ts +11 -1
- package/dist/session/pending-commands.js +12 -2
- package/dist/session/resolve-within.d.ts +34 -0
- package/dist/session/resolve-within.js +53 -0
- package/dist/session/session-health.d.ts +9 -0
- package/dist/session/session-health.js +23 -0
- package/dist/session/session-info.d.ts +18 -0
- package/dist/session/session-info.js +11 -0
- package/dist/session/session-manager.d.ts +14 -0
- package/dist/session/session-manager.js +111 -4
- package/dist/session/session-mirror.d.ts +16 -0
- package/dist/session/session-mirror.js +22 -0
- package/dist/session/session-recommendation.d.ts +19 -5
- package/dist/session/session-recommendation.js +25 -9
- package/dist/session/session-successor.d.ts +37 -0
- package/dist/session/session-successor.js +113 -0
- package/dist/session/session-tools.js +2 -2
- package/dist/session/session.d.ts +53 -3
- package/dist/session/session.js +162 -9
- package/dist/session/wall-clock.d.ts +12 -0
- package/dist/session/wall-clock.js +14 -0
- package/dist/setup/agent-configs.d.ts +96 -0
- package/dist/setup/agent-configs.js +333 -0
- package/dist/setup/agent-io.d.ts +9 -0
- package/dist/setup/agent-io.js +16 -0
- package/dist/setup/agent-writer.d.ts +51 -0
- package/dist/setup/agent-writer.js +115 -0
- package/dist/setup/approval-migration.d.ts +47 -0
- package/dist/setup/approval-migration.js +68 -0
- package/dist/setup/auto-approve.d.ts +85 -0
- package/dist/setup/auto-approve.js +150 -0
- package/dist/setup/bridge-port.d.ts +15 -0
- package/dist/setup/bridge-port.js +24 -0
- package/dist/setup/desktop-shape.d.ts +57 -0
- package/dist/setup/desktop-shape.js +63 -0
- package/dist/setup/dev-server-wait.d.ts +60 -0
- package/dist/setup/dev-server-wait.js +65 -0
- package/dist/setup/drive-agent.d.ts +71 -0
- package/dist/setup/drive-agent.js +171 -0
- package/dist/setup/drive-plan.d.ts +68 -0
- package/dist/setup/drive-plan.js +130 -0
- package/dist/setup/ensure-daemon.d.ts +29 -0
- package/dist/setup/ensure-daemon.js +58 -0
- package/dist/setup/init-runtime.d.ts +47 -0
- package/dist/setup/init-runtime.js +160 -0
- package/dist/setup/interrupt.d.ts +25 -0
- package/dist/setup/interrupt.js +40 -0
- package/dist/setup/license-key.d.ts +39 -0
- package/dist/setup/license-key.js +72 -0
- package/dist/setup/listeners.d.ts +37 -0
- package/dist/setup/listeners.js +85 -0
- package/dist/setup/node-effects.d.ts +60 -0
- package/dist/setup/node-effects.js +210 -0
- package/dist/setup/page-probe.d.ts +39 -0
- package/dist/setup/page-probe.js +49 -0
- package/dist/setup/relaunch.d.ts +36 -0
- package/dist/setup/relaunch.js +57 -0
- package/dist/setup/remaining-steps.d.ts +19 -0
- package/dist/setup/remaining-steps.js +31 -0
- package/dist/setup/run-setup.d.ts +99 -0
- package/dist/setup/run-setup.js +187 -0
- package/dist/setup/session-pick.d.ts +47 -0
- package/dist/setup/session-pick.js +51 -0
- package/dist/setup/setup-command.d.ts +45 -0
- package/dist/setup/setup-command.js +199 -0
- package/dist/setup/setup-options.d.ts +74 -0
- package/dist/setup/setup-options.js +67 -0
- package/dist/setup/transcripts.d.ts +16 -0
- package/dist/setup/transcripts.js +43 -0
- package/dist/status-payload.d.ts +14 -1
- package/dist/status-payload.js +23 -1
- package/dist/telemetry/automation-hint.d.ts +2 -1
- package/dist/telemetry/daemon-telemetry.d.ts +8 -1
- package/dist/telemetry/daemon-telemetry.js +52 -5
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +9 -1
- package/dist/telemetry/feedback-context.js +60 -29
- package/dist/telemetry/feedback-local.d.ts +15 -0
- package/dist/telemetry/feedback-local.js +56 -0
- package/dist/telemetry/feedback-outbox.d.ts +2 -1
- package/dist/telemetry/feedback.d.ts +11 -0
- package/dist/telemetry/feedback.js +19 -6
- package/dist/telemetry/init-telemetry.d.ts +2 -0
- package/dist/telemetry/init-telemetry.js +2 -0
- package/dist/telemetry/install-source.d.ts +14 -0
- package/dist/telemetry/install-source.js +30 -0
- package/dist/telemetry/license-activation.d.ts +7 -1
- package/dist/telemetry/license-activation.js +9 -5
- package/dist/telemetry/mcp-connection.js +7 -0
- package/dist/telemetry/project-profile.d.ts +12 -9
- package/dist/telemetry/project-profile.js +12 -7
- package/dist/telemetry/proxy-telemetry.d.ts +3 -0
- package/dist/telemetry/proxy-telemetry.js +21 -0
- package/dist/telemetry/run-telemetry.d.ts +0 -2
- package/dist/telemetry/run-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +12 -0
- package/dist/telemetry/session-metrics.js +35 -1
- package/dist/telemetry/telemetry.js +17 -2
- package/dist/telemetry/tool-refused.d.ts +3 -1
- package/dist/telemetry/tool-refused.js +34 -2
- package/dist/telemetry/verification-of.js +2 -2
- package/dist/tools/act-capsule.d.ts +10 -0
- package/dist/tools/act-capsule.js +8 -2
- package/dist/tools/act-danger.d.ts +2 -1
- package/dist/tools/act-danger.js +11 -3
- package/dist/tools/act-observation.d.ts +41 -0
- package/dist/tools/act-observation.js +36 -0
- package/dist/tools/act-preflight.d.ts +20 -0
- package/dist/tools/act-preflight.js +98 -0
- package/dist/tools/act-sequence-retry.d.ts +45 -0
- package/dist/tools/act-sequence-retry.js +58 -0
- package/dist/tools/act-sequence-tool.d.ts +13 -0
- package/dist/tools/act-sequence-tool.js +135 -0
- package/dist/tools/act-target.d.ts +14 -0
- package/dist/tools/act-target.js +47 -0
- package/dist/tools/act-tools.d.ts +21 -1
- package/dist/tools/act-tools.js +196 -204
- package/dist/tools/assert-source.d.ts +22 -0
- package/dist/tools/assert-source.js +53 -0
- package/dist/tools/assert-verdict.d.ts +20 -3
- package/dist/tools/assert-verdict.js +58 -20
- package/dist/tools/browser-tools.js +6 -22
- package/dist/tools/complete-snapshot.d.ts +48 -0
- package/dist/tools/complete-snapshot.js +75 -0
- package/dist/tools/coverage-tools.d.ts +10 -1
- package/dist/tools/coverage-tools.js +123 -69
- package/dist/tools/dynamic-tools.js +53 -13
- package/dist/tools/error-recovery.d.ts +25 -14
- package/dist/tools/error-recovery.js +78 -9
- package/dist/tools/feedback-tools.d.ts +1 -1
- package/dist/tools/feedback-tools.js +2 -2
- package/dist/tools/invoke-tool.js +121 -11
- package/dist/tools/lease-hint.d.ts +10 -0
- package/dist/tools/lease-hint.js +53 -4
- package/dist/tools/lease-tools.d.ts +35 -5
- package/dist/tools/lease-tools.js +191 -13
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +22 -0
- package/dist/tools/navigate-arrival.d.ts +0 -2
- package/dist/tools/navigate-arrival.js +1 -1
- package/dist/tools/numeric-bounds.d.ts +100 -0
- package/dist/tools/numeric-bounds.js +125 -0
- package/dist/tools/observe-tools.js +109 -65
- package/dist/tools/read-tools.js +31 -8
- package/dist/tools/real-input-attempt.d.ts +32 -0
- package/dist/tools/real-input-attempt.js +212 -2
- package/dist/tools/reconcile-tools.js +22 -8
- package/dist/tools/resolve-target.d.ts +0 -7
- package/dist/tools/sdk-marker-probe.d.ts +29 -0
- package/dist/tools/sdk-marker-probe.js +57 -0
- package/dist/tools/snapshot-delta.d.ts +1 -1
- package/dist/tools/tool-kit.d.ts +24 -0
- package/dist/tools/tool-kit.js +21 -0
- package/dist/tools/tool-names.d.ts +5 -0
- package/dist/tools/tool-names.js +5 -0
- package/dist/tools/tool-surface.d.ts +85 -6
- package/dist/tools/tool-surface.js +122 -2
- package/dist/tools/tools-helpers.d.ts +19 -0
- package/dist/tools/tools-helpers.js +46 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +78 -12
- package/dist/tools/unadvertised-help.js +4 -4
- package/dist/tools/verify-next-baton.d.ts +53 -0
- package/dist/tools/verify-next-baton.js +52 -0
- package/dist/update/reticle-deps.d.ts +9 -0
- package/dist/update/reticle-deps.js +27 -0
- package/dist/update/sdk-sync.d.ts +1 -2
- package/dist/update/sdk-sync.js +1 -21
- package/dist/update/update-nudge.d.ts +25 -10
- package/dist/update/update-nudge.js +32 -3
- package/dist/update/updater.d.ts +1 -1
- package/dist/version/peer-announce.js +4 -2
- package/dist/version/sdk-fix.d.ts +47 -0
- package/dist/version/sdk-fix.js +127 -0
- package/dist/version/version-nudge.d.ts +14 -0
- package/dist/version/version-nudge.js +21 -0
- package/dist/version/version-skew.d.ts +38 -3
- package/dist/version/version-skew.js +83 -7
- package/dist/visual/visual-store.d.ts +6 -6
- package/dist/visual/visual-store.js +18 -16
- package/dist/visual/visual-tools.js +57 -12
- package/docs/README.md +2 -1
- package/docs/cli/cloud.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -0
- package/docs/cli/serve.mdx +4 -2
- package/docs/cli/verify.mdx +4 -5
- package/docs/cli.mdx +7 -3
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +2 -2
- package/docs/gates.md +1 -1
- package/docs/getting-started.md +19 -3
- package/docs/index.mdx +9 -6
- package/docs/install-agentic.mdx +38 -12
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/server.mdx +1 -1
- package/docs/packages.mdx +8 -6
- package/docs/platform-integration.md +17 -0
- package/docs/predicates.mdx +8 -0
- package/docs/reticle-manual.md +106 -0
- package/docs/skill-file.mdx +3 -3
- package/docs/telemetry-contract.md +108 -4
- package/docs/tools-act-sequence.mdx +16 -0
- package/docs/tools-tools-and-run.mdx +2 -4
- package/docs/troubleshooting.mdx +9 -0
- package/docs/usage.md +4 -3
- package/package.json +4 -3
- package/dist/init/cursor.d.ts +0 -24
- package/dist/init/cursor.js +0 -80
- package/dist/session/session-readiness.d.ts +0 -34
- package/dist/session/session-readiness.js +0 -36
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What restarting the calling client should do — decided, not performed.
|
|
3
|
+
*
|
|
4
|
+
* The dangerous case is the one this exists for: `--resume <id>` on an id with NO transcript opens
|
|
5
|
+
* an EMPTY conversation under that id, silently. Nothing errors, the client starts, the session
|
|
6
|
+
* looks real, and every tool the restart existed to load is missing. A restart that lands there is
|
|
7
|
+
* worse than no restart, because it is indistinguishable from success.
|
|
8
|
+
*
|
|
9
|
+
* The decision is pure and testable; opening a terminal is neither, and is left to the caller.
|
|
10
|
+
*/
|
|
11
|
+
/** What the caller should do, once the environment has been read. */
|
|
12
|
+
export declare const RelaunchOutcome: {
|
|
13
|
+
/** Claude Code named its own session and a transcript backs it. */
|
|
14
|
+
readonly CLAUDE: "claude";
|
|
15
|
+
/** No claude session, but a codex rollout in this directory identifies one. */
|
|
16
|
+
readonly CODEX: "codex";
|
|
17
|
+
/** A session id with nothing behind it — the case that looks like success. */
|
|
18
|
+
readonly REFUSED: "refused";
|
|
19
|
+
/** The client tells a child process nothing about which conversation it is. */
|
|
20
|
+
readonly UNKNOWN_CLIENT: "unknown-client";
|
|
21
|
+
};
|
|
22
|
+
export type RelaunchOutcome = (typeof RelaunchOutcome)[keyof typeof RelaunchOutcome];
|
|
23
|
+
export interface RelaunchEnv {
|
|
24
|
+
/** Claude Code puts its session id in the environment; nothing else does. */
|
|
25
|
+
readonly claudeSessionId?: string | undefined;
|
|
26
|
+
/** Codex is recognised from the rollout file it is writing, not from the environment. */
|
|
27
|
+
readonly codexSessionId?: string | undefined;
|
|
28
|
+
/** Does a transcript actually exist for this id. */
|
|
29
|
+
transcriptExists(sessionId: string): boolean;
|
|
30
|
+
readonly cwd: string;
|
|
31
|
+
}
|
|
32
|
+
export interface RelaunchDecision {
|
|
33
|
+
readonly outcome: RelaunchOutcome;
|
|
34
|
+
readonly message: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function relaunchDecision(env: RelaunchEnv): RelaunchDecision;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What restarting the calling client should do — decided, not performed.
|
|
3
|
+
*
|
|
4
|
+
* The dangerous case is the one this exists for: `--resume <id>` on an id with NO transcript opens
|
|
5
|
+
* an EMPTY conversation under that id, silently. Nothing errors, the client starts, the session
|
|
6
|
+
* looks real, and every tool the restart existed to load is missing. A restart that lands there is
|
|
7
|
+
* worse than no restart, because it is indistinguishable from success.
|
|
8
|
+
*
|
|
9
|
+
* The decision is pure and testable; opening a terminal is neither, and is left to the caller.
|
|
10
|
+
*/
|
|
11
|
+
/** What the caller should do, once the environment has been read. */
|
|
12
|
+
export const RelaunchOutcome = {
|
|
13
|
+
/** Claude Code named its own session and a transcript backs it. */
|
|
14
|
+
CLAUDE: 'claude',
|
|
15
|
+
/** No claude session, but a codex rollout in this directory identifies one. */
|
|
16
|
+
CODEX: 'codex',
|
|
17
|
+
/** A session id with nothing behind it — the case that looks like success. */
|
|
18
|
+
REFUSED: 'refused',
|
|
19
|
+
/** The client tells a child process nothing about which conversation it is. */
|
|
20
|
+
UNKNOWN_CLIENT: 'unknown-client',
|
|
21
|
+
};
|
|
22
|
+
/** The prompt a resumed session is handed, so the restart continues the work rather than idling. */
|
|
23
|
+
const RESUME_PROMPT = 'Reticle is installed. Continue where we left off.';
|
|
24
|
+
export function relaunchDecision(env) {
|
|
25
|
+
const { claudeSessionId, codexSessionId } = env;
|
|
26
|
+
// A claude id wins whenever there is one: that client named ITSELF, and a stale rollout file from
|
|
27
|
+
// an earlier codex run in the same directory must not redirect the restart.
|
|
28
|
+
if (claudeSessionId !== undefined && '' !== claudeSessionId) {
|
|
29
|
+
if (!env.transcriptExists(claudeSessionId)) {
|
|
30
|
+
return {
|
|
31
|
+
outcome: RelaunchOutcome.REFUSED,
|
|
32
|
+
message: `refusing to restart: no transcript exists yet for ${claudeSessionId}, and \`--resume\` ` +
|
|
33
|
+
'on an id with no transcript opens an EMPTY conversation that looks exactly like it ' +
|
|
34
|
+
'worked. Say something in this session first, then re-run with --relaunch.',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
outcome: RelaunchOutcome.CLAUDE,
|
|
39
|
+
message: 'restart this conversation with the tools loaded:\n ' +
|
|
40
|
+
`cd ${JSON.stringify(env.cwd)} && claude --resume ${claudeSessionId} ` +
|
|
41
|
+
`${JSON.stringify(RESUME_PROMPT)}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (codexSessionId !== undefined && '' !== codexSessionId) {
|
|
45
|
+
return {
|
|
46
|
+
outcome: RelaunchOutcome.CODEX,
|
|
47
|
+
message: 'restart this conversation with the tools loaded:\n ' +
|
|
48
|
+
`cd ${JSON.stringify(env.cwd)} && codex resume ${codexSessionId} ` +
|
|
49
|
+
`${JSON.stringify(RESUME_PROMPT)}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
outcome: RelaunchOutcome.UNKNOWN_CLIENT,
|
|
54
|
+
message: 'this client does not tell a child process which conversation it is, so nothing here can ' +
|
|
55
|
+
'resume it. Restart it once and the reticle_* tools will be there.',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What is left to do by hand, from wherever setup stopped.
|
|
3
|
+
*
|
|
4
|
+
* Naming a cause is not the same as being recoverable. Setup can misbehave, and when it does the
|
|
5
|
+
* caller should not have to re-read the whole procedure and work out for itself which parts already
|
|
6
|
+
* worked — it should pick up at the step that did not. Ordered, and only ever the REMAINING steps.
|
|
7
|
+
*/
|
|
8
|
+
/** How far a run got. Everything here is knowable from the result object setup already builds. */
|
|
9
|
+
export interface Progress {
|
|
10
|
+
readonly initDone: boolean;
|
|
11
|
+
readonly devServerUp: boolean;
|
|
12
|
+
readonly sessionConnected: boolean;
|
|
13
|
+
readonly flowSaved: boolean;
|
|
14
|
+
/** Set when the caller supplied a url, so nobody is told to start a server they already run. */
|
|
15
|
+
readonly urlSuppliedByCaller: boolean;
|
|
16
|
+
readonly url?: string | undefined;
|
|
17
|
+
readonly devCommand?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
export declare function remainingSteps(p: Progress): string[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What is left to do by hand, from wherever setup stopped.
|
|
3
|
+
*
|
|
4
|
+
* Naming a cause is not the same as being recoverable. Setup can misbehave, and when it does the
|
|
5
|
+
* caller should not have to re-read the whole procedure and work out for itself which parts already
|
|
6
|
+
* worked — it should pick up at the step that did not. Ordered, and only ever the REMAINING steps.
|
|
7
|
+
*/
|
|
8
|
+
const DOCS_INDEX = 'https://docs.reticle.sh/llms.txt';
|
|
9
|
+
export function remainingSteps(p) {
|
|
10
|
+
const steps = [];
|
|
11
|
+
if (!p.initDone) {
|
|
12
|
+
steps.push('Run `npx @reticlehq/server@latest init` here, and fix every ⚠ it reports.');
|
|
13
|
+
}
|
|
14
|
+
if (!p.devServerUp && !p.urlSuppliedByCaller) {
|
|
15
|
+
steps.push(`Start the dev server yourself: ${p.devCommand ?? 'the dev script in package.json'}, then open the app in a browser.`);
|
|
16
|
+
}
|
|
17
|
+
if (!p.sessionConnected) {
|
|
18
|
+
steps.push('Confirm a session appears with `reticle_sessions`. If the list is empty, read its ' +
|
|
19
|
+
'`next_action`: the usual cause is a dev server that was already running when the build ' +
|
|
20
|
+
'config was edited, so restart it and hard-reload' +
|
|
21
|
+
(undefined === p.url ? '.' : ` ${p.url}.`));
|
|
22
|
+
}
|
|
23
|
+
if (!p.flowSaved) {
|
|
24
|
+
steps.push('Drive one flow and produce a verdict: `reticle_snapshot({mode:"interactive"})` once, ' +
|
|
25
|
+
'`reticle_act_sequence` for the setup steps, then ONE `reticle_act_and_wait({ref,action,until})`. ' +
|
|
26
|
+
'Wrap it in `reticle_record` start/stop and `reticle_flow_save`, and check the grade it ' +
|
|
27
|
+
'returns is `asserted` — anything else only acts, so it passes even when the feature breaks.');
|
|
28
|
+
}
|
|
29
|
+
steps.push(`The whole procedure, if you need it: \`curl ${DOCS_INDEX}\`.`);
|
|
30
|
+
return steps;
|
|
31
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The half of setup that happens after the files are written.
|
|
3
|
+
*
|
|
4
|
+
* `init` wires a project; this gets the app running with the SDK inside it, proves a session
|
|
5
|
+
* connected, and drives one flow to a verdict. Measured against an agent doing the same steps by
|
|
6
|
+
* hand from SKILL.md across five real applications: 176 model turns, $9.92, forty minutes, and a
|
|
7
|
+
* verdict in two runs out of five — three ended by asking a human to restart their client, having
|
|
8
|
+
* shown them nothing.
|
|
9
|
+
*
|
|
10
|
+
* Every effect is injected. That is not ceremony: the sequence has five phases, each with its own
|
|
11
|
+
* way of going wrong, and the alternative to injecting them is a test that boots a real dev server
|
|
12
|
+
* and a real browser to find out what happens when neither works.
|
|
13
|
+
*/
|
|
14
|
+
import { type CandidateSession } from './session-pick.js';
|
|
15
|
+
import { type PageProbe } from './page-probe.js';
|
|
16
|
+
import { AppShape } from './desktop-shape.js';
|
|
17
|
+
/** Where a run got to, and why it stopped if it did. */
|
|
18
|
+
export declare const SetupPhase: {
|
|
19
|
+
readonly DEV_SERVER: "dev-server";
|
|
20
|
+
readonly CONNECT: "connect";
|
|
21
|
+
readonly DRIVE: "drive";
|
|
22
|
+
readonly DONE: "done";
|
|
23
|
+
};
|
|
24
|
+
export type SetupPhase = (typeof SetupPhase)[keyof typeof SetupPhase];
|
|
25
|
+
export interface SetupOutcome {
|
|
26
|
+
/** True only when a flow was driven AND saved. Writing files is not an install. */
|
|
27
|
+
readonly ok: boolean;
|
|
28
|
+
readonly reachedPhase: SetupPhase;
|
|
29
|
+
readonly url?: string | undefined;
|
|
30
|
+
readonly sessionId?: string | undefined;
|
|
31
|
+
/** The drive's own report, kept verbatim and never trusted as a pass on its own. */
|
|
32
|
+
readonly verdict?: string | undefined;
|
|
33
|
+
readonly flowSaved: boolean;
|
|
34
|
+
/** What the caller should do next, when this did not finish. */
|
|
35
|
+
readonly fallback: string[];
|
|
36
|
+
readonly notes: string[];
|
|
37
|
+
}
|
|
38
|
+
/** Everything the sequence needs from the world, so none of it is reached for directly. */
|
|
39
|
+
export interface SetupEffects {
|
|
40
|
+
/** Start the dev server. Resolves once started; the caller owns stopping it. */
|
|
41
|
+
readonly startDevServer: (command: string, cwd: string) => Promise<void>;
|
|
42
|
+
/** Everything the dev server has printed so far. */
|
|
43
|
+
readonly devServerOutput: () => string;
|
|
44
|
+
readonly devServerExited: () => boolean;
|
|
45
|
+
/** Milliseconds since it last printed anything. */
|
|
46
|
+
readonly devServerQuietForMs: () => number;
|
|
47
|
+
/** Ports our own process group is listening on. */
|
|
48
|
+
readonly observedPorts: () => number[];
|
|
49
|
+
/** Fetch the url and report what came back. */
|
|
50
|
+
readonly probePage: (url: string) => Promise<PageProbe>;
|
|
51
|
+
readonly openBrowser: (url: string) => Promise<void>;
|
|
52
|
+
readonly listSessions: () => Promise<CandidateSession[]>;
|
|
53
|
+
/** Drive one flow. Returns the agent's report, or null when nobody could. */
|
|
54
|
+
readonly drive: (url: string, session: CandidateSession) => Promise<string | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Whether this machine has an agent CLI at all.
|
|
57
|
+
*
|
|
58
|
+
* Separate from `drive` returning null, because those are different facts and only one of them is
|
|
59
|
+
* a failure. A drive that RAN and proved nothing is a result worth a non-zero exit. A drive that
|
|
60
|
+
* could not run is the absence of a tool on the machine, and reporting the install as failed for
|
|
61
|
+
* it told CI runners — where no agent CLI is ever installed — that a perfectly good install had
|
|
62
|
+
* not worked.
|
|
63
|
+
*/
|
|
64
|
+
readonly driverAvailable: () => boolean;
|
|
65
|
+
readonly flowsSaved: () => boolean;
|
|
66
|
+
readonly now: () => number;
|
|
67
|
+
readonly sleep: (ms: number) => Promise<void>;
|
|
68
|
+
readonly note: (line: string) => void;
|
|
69
|
+
}
|
|
70
|
+
export interface SetupInput {
|
|
71
|
+
readonly appDir: string;
|
|
72
|
+
readonly devCommand?: string | undefined;
|
|
73
|
+
/** The app is already served here, so nothing is started. */
|
|
74
|
+
readonly suppliedUrl?: string | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* The caller's own budget for the connect wait, when they named one.
|
|
77
|
+
*
|
|
78
|
+
* Absent, the shape's policy decides — a desktop shell genuinely needs longer than a browser tab.
|
|
79
|
+
* Present, it WINS, including when it is shorter: the deadline used to be
|
|
80
|
+
* `max(phaseTimeoutMs, policy)`, so `--timeout 3` could only ever lengthen the wait. A timeout the
|
|
81
|
+
* tool ignores is a lie, and it left every hostile-environment check killed by its own harness
|
|
82
|
+
* before setup could say what was wrong.
|
|
83
|
+
*/
|
|
84
|
+
readonly connectBudgetMs?: number | undefined;
|
|
85
|
+
readonly openBrowser: boolean;
|
|
86
|
+
readonly drive: boolean;
|
|
87
|
+
/** Web, Electron or Tauri. Desktop changes three things; see desktop-shape.ts. */
|
|
88
|
+
readonly shape: AppShape;
|
|
89
|
+
readonly phaseTimeoutMs: number;
|
|
90
|
+
readonly pollMs: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Run the phases. Stops at the first one that cannot continue, and always says what is left.
|
|
94
|
+
*
|
|
95
|
+
* A phase that fails is not an error to throw: the caller is usually an agent, and a thrown
|
|
96
|
+
* exception loses the four things it needs — how far this got, the url, the session, and what to do
|
|
97
|
+
* about it.
|
|
98
|
+
*/
|
|
99
|
+
export declare function runSetupPhases(input: SetupInput, fx: SetupEffects): Promise<SetupOutcome>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The half of setup that happens after the files are written.
|
|
3
|
+
*
|
|
4
|
+
* `init` wires a project; this gets the app running with the SDK inside it, proves a session
|
|
5
|
+
* connected, and drives one flow to a verdict. Measured against an agent doing the same steps by
|
|
6
|
+
* hand from SKILL.md across five real applications: 176 model turns, $9.92, forty minutes, and a
|
|
7
|
+
* verdict in two runs out of five — three ended by asking a human to restart their client, having
|
|
8
|
+
* shown them nothing.
|
|
9
|
+
*
|
|
10
|
+
* Every effect is injected. That is not ceremony: the sequence has five phases, each with its own
|
|
11
|
+
* way of going wrong, and the alternative to injecting them is a test that boots a real dev server
|
|
12
|
+
* and a real browser to find out what happens when neither works.
|
|
13
|
+
*/
|
|
14
|
+
import { judgeWait, urlToWatch, WaitVerdict } from './dev-server-wait.js';
|
|
15
|
+
import { pickSession } from './session-pick.js';
|
|
16
|
+
import { readPage, describePage } from './page-probe.js';
|
|
17
|
+
import { remainingSteps } from './remaining-steps.js';
|
|
18
|
+
import { AppShape, isDesktop, policyFor } from './desktop-shape.js';
|
|
19
|
+
/** Where a run got to, and why it stopped if it did. */
|
|
20
|
+
export const SetupPhase = {
|
|
21
|
+
DEV_SERVER: 'dev-server',
|
|
22
|
+
CONNECT: 'connect',
|
|
23
|
+
DRIVE: 'drive',
|
|
24
|
+
DONE: 'done',
|
|
25
|
+
};
|
|
26
|
+
const asProgress = (input, o) => ({
|
|
27
|
+
initDone: true,
|
|
28
|
+
devServerUp: undefined !== o.url,
|
|
29
|
+
sessionConnected: undefined !== o.sessionId,
|
|
30
|
+
flowSaved: true === o.flowSaved,
|
|
31
|
+
urlSuppliedByCaller: undefined !== input.suppliedUrl,
|
|
32
|
+
...(undefined === o.url ? {} : { url: o.url }),
|
|
33
|
+
...(undefined === input.devCommand ? {} : { devCommand: input.devCommand }),
|
|
34
|
+
});
|
|
35
|
+
const stop = (input, phase, partial, notes) => ({
|
|
36
|
+
ok: false,
|
|
37
|
+
reachedPhase: phase,
|
|
38
|
+
flowSaved: false,
|
|
39
|
+
...partial,
|
|
40
|
+
notes,
|
|
41
|
+
fallback: remainingSteps(asProgress(input, partial)),
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Run the phases. Stops at the first one that cannot continue, and always says what is left.
|
|
45
|
+
*
|
|
46
|
+
* A phase that fails is not an error to throw: the caller is usually an agent, and a thrown
|
|
47
|
+
* exception loses the four things it needs — how far this got, the url, the session, and what to do
|
|
48
|
+
* about it.
|
|
49
|
+
*/
|
|
50
|
+
export async function runSetupPhases(input, fx) {
|
|
51
|
+
const notes = [];
|
|
52
|
+
const note = (line) => {
|
|
53
|
+
notes.push(line);
|
|
54
|
+
fx.note(line);
|
|
55
|
+
};
|
|
56
|
+
// ── the app has to be running ────────────────────────────────────────────────────────────────
|
|
57
|
+
let url = input.suppliedUrl;
|
|
58
|
+
if (undefined === url) {
|
|
59
|
+
if (undefined === input.devCommand) {
|
|
60
|
+
note(
|
|
61
|
+
// "stop here rather than invent one" is the SKILL.md rule, and the words are the contract —
|
|
62
|
+
// inventing a dev command is how a setup script runs the wrong thing and reports success.
|
|
63
|
+
'No dev command: the project names no dev, start or serve script, and there is no --url. ' +
|
|
64
|
+
'Stopping here rather than invent one — pass --dev-cmd or --url to say what serves this app.');
|
|
65
|
+
return stop(input, SetupPhase.DEV_SERVER, {}, notes);
|
|
66
|
+
}
|
|
67
|
+
await fx.startDevServer(input.devCommand, input.appDir);
|
|
68
|
+
const startedAt = fx.now();
|
|
69
|
+
for (;;) {
|
|
70
|
+
const watching = urlToWatch(fx.devServerOutput(), fx.observedPorts());
|
|
71
|
+
const serving = undefined === watching ? false : (await fx.probePage(watching)).served;
|
|
72
|
+
const verdict = judgeWait({
|
|
73
|
+
output: fx.devServerOutput(),
|
|
74
|
+
launcherExited: fx.devServerExited(),
|
|
75
|
+
serving,
|
|
76
|
+
quietForMs: fx.devServerQuietForMs(),
|
|
77
|
+
elapsedMs: fx.now() - startedAt,
|
|
78
|
+
});
|
|
79
|
+
if (WaitVerdict.READY === verdict && undefined !== watching) {
|
|
80
|
+
url = watching;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
// A desktop shell serves its webview from inside the app, so there is no port to answer and
|
|
84
|
+
// nothing to be READY. Once it has announced a url, or bound one we can see, that is as far
|
|
85
|
+
// as this phase can get: the session it dials from its own window is the real signal.
|
|
86
|
+
if (isDesktop(input.shape) && undefined !== watching) {
|
|
87
|
+
url = watching;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (WaitVerdict.DEAD === verdict) {
|
|
91
|
+
note('The dev server exited without serving anything.');
|
|
92
|
+
return stop(input, SetupPhase.DEV_SERVER, {}, notes);
|
|
93
|
+
}
|
|
94
|
+
if (WaitVerdict.HUNG === verdict) {
|
|
95
|
+
// Says BOTH were checked. A server that prints nothing but IS listening is the CRA case and
|
|
96
|
+
// must not be failed, so a reader has to be able to tell "we looked at the log" from "we
|
|
97
|
+
// looked at the log AND the ports".
|
|
98
|
+
note('The dev server neither printed a URL nor bound a port, so setup has nothing to open. ' +
|
|
99
|
+
'Check its log, or pass --url with the address it serves.');
|
|
100
|
+
return stop(input, SetupPhase.DEV_SERVER, {}, notes);
|
|
101
|
+
}
|
|
102
|
+
await fx.sleep(input.pollMs);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// ── and something has to connect from inside it ──────────────────────────────────────────────
|
|
106
|
+
const policy = policyFor(input.shape);
|
|
107
|
+
// Through `note`, not `fx.note`: a caller reading the result should see it too.
|
|
108
|
+
if (undefined !== policy.note)
|
|
109
|
+
note(policy.note);
|
|
110
|
+
const before = new Set((await fx.listSessions()).map((s) => s.sessionId));
|
|
111
|
+
// Never for a desktop app: its own window is the client, and a browser tab would be a SECOND
|
|
112
|
+
// session that is not the app.
|
|
113
|
+
if (input.openBrowser && policy.openBrowser)
|
|
114
|
+
await fx.openBrowser(url);
|
|
115
|
+
const deadline = fx.now() + (input.connectBudgetMs ?? Math.max(input.phaseTimeoutMs, policy.connectBudgetMs));
|
|
116
|
+
let session = null;
|
|
117
|
+
for (;;) {
|
|
118
|
+
// On a desktop app, only the desktop window counts. AppShape and the runtime a page reports use
|
|
119
|
+
// the same three names, so the shape IS the requirement — see session-pick.
|
|
120
|
+
session = pickSession(await fx.listSessions(), url, before, isDesktop(input.shape) ? input.shape : undefined);
|
|
121
|
+
if (null !== session)
|
|
122
|
+
break;
|
|
123
|
+
if (deadline <= fx.now())
|
|
124
|
+
break;
|
|
125
|
+
await fx.sleep(input.pollMs);
|
|
126
|
+
}
|
|
127
|
+
if (null === session) {
|
|
128
|
+
if (isDesktop(input.shape)) {
|
|
129
|
+
// Nothing outside the app can fetch its webview, so there is no page to describe. What is
|
|
130
|
+
// wrong here is desktop-shaped: the preload, the capture helper, or a CSP that blocks the
|
|
131
|
+
// bridge — all of which `reticle doctor` checks.
|
|
132
|
+
note('The app never dialled in. For a desktop shell that is usually the preload not being required, ' +
|
|
133
|
+
'the capture helper not installed, or a CSP that blocks the bridge: run `npx @reticlehq/server doctor`.');
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// What the page contains is the one thing the daemon cannot know, so it is worth one fetch.
|
|
137
|
+
note(describePage(readPage(await fx.probePage(url)), url));
|
|
138
|
+
}
|
|
139
|
+
return stop(input, SetupPhase.CONNECT, { url }, notes);
|
|
140
|
+
}
|
|
141
|
+
// ── and it has to be driven, or nothing has been proved ──────────────────────────────────────
|
|
142
|
+
if (!input.drive) {
|
|
143
|
+
return {
|
|
144
|
+
ok: true,
|
|
145
|
+
reachedPhase: SetupPhase.CONNECT,
|
|
146
|
+
url,
|
|
147
|
+
sessionId: session.sessionId,
|
|
148
|
+
flowSaved: false,
|
|
149
|
+
notes,
|
|
150
|
+
fallback: [],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Nothing to drive WITH is not the same as driving and proving nothing, and only the second is a
|
|
154
|
+
// failure of this command. The app is installed, instrumented, booted and connected; the one step
|
|
155
|
+
// left needs a tool this machine does not have, so it is reported and handed to the caller.
|
|
156
|
+
if (!fx.driverAvailable()) {
|
|
157
|
+
note('Installed and connected, but no agent CLI is on this machine (claude, codex, opencode, ' +
|
|
158
|
+
'cursor-agent or gemini), so nothing drove the app and no verdict was produced.');
|
|
159
|
+
return {
|
|
160
|
+
ok: true,
|
|
161
|
+
reachedPhase: SetupPhase.CONNECT,
|
|
162
|
+
url,
|
|
163
|
+
sessionId: session.sessionId,
|
|
164
|
+
flowSaved: false,
|
|
165
|
+
notes,
|
|
166
|
+
fallback: remainingSteps(asProgress(input, { url, sessionId: session.sessionId, flowSaved: false })),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const verdict = await fx.drive(url, session);
|
|
170
|
+
const flowSaved = fx.flowsSaved();
|
|
171
|
+
if (null === verdict) {
|
|
172
|
+
note('No agent CLI could drive the app, so nothing was proved.');
|
|
173
|
+
}
|
|
174
|
+
if (!flowSaved) {
|
|
175
|
+
return stop(input, SetupPhase.DRIVE, { url, sessionId: session.sessionId, ...(null === verdict ? {} : { verdict }) }, notes);
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
ok: true,
|
|
179
|
+
reachedPhase: SetupPhase.DONE,
|
|
180
|
+
url,
|
|
181
|
+
sessionId: session.sessionId,
|
|
182
|
+
...(null === verdict ? {} : { verdict }),
|
|
183
|
+
flowSaved: true,
|
|
184
|
+
notes,
|
|
185
|
+
fallback: [],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which connected session to drive, out of everything the daemon is holding.
|
|
3
|
+
*
|
|
4
|
+
* This is a false-green guard, which is why it is its own module. Two ways to get it wrong, and
|
|
5
|
+
* both report a successful install for an app that was never verified:
|
|
6
|
+
*
|
|
7
|
+
* - Match "any session" and you pass on somebody else's tab. A daemon usually holds several, and
|
|
8
|
+
* one of them being alive says nothing about whether THIS app connected.
|
|
9
|
+
* - Match "the first session on this url" and you drive whichever the daemon listed first, usually
|
|
10
|
+
* the oldest: a tab whose dev server died yesterday. The HUD then plays to a window nobody is
|
|
11
|
+
* watching, and the verdict describes a page the user cannot see.
|
|
12
|
+
*/
|
|
13
|
+
/** The fields of a daemon session this decision actually reads. */
|
|
14
|
+
export interface CandidateSession {
|
|
15
|
+
readonly sessionId: string;
|
|
16
|
+
readonly url?: string;
|
|
17
|
+
readonly hidden?: boolean;
|
|
18
|
+
readonly throttled?: boolean;
|
|
19
|
+
readonly lastSeenMs?: number;
|
|
20
|
+
/** False when the capabilities file init scaffolded was never completed. */
|
|
21
|
+
readonly hasCapabilities?: boolean;
|
|
22
|
+
/** Which shell answered. Absent on an SDK too old to report one — see requiredRuntime below. */
|
|
23
|
+
readonly runtime?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The session to drive, or null when nothing on this url qualifies.
|
|
27
|
+
*
|
|
28
|
+
* Preference order, strongest evidence first:
|
|
29
|
+
* 1. NEW since we opened the tab — definitely this run's, definitely alive
|
|
30
|
+
* 2. visible and not throttled — the tab a human is actually looking at
|
|
31
|
+
* 3. least stale — the best of a bad set
|
|
32
|
+
*
|
|
33
|
+
* `before` is the set of session ids that already existed when this run started; anything outside
|
|
34
|
+
* it belongs to us.
|
|
35
|
+
*/
|
|
36
|
+
export declare function pickSession(sessions: readonly CandidateSession[], url: string, before?: ReadonlySet<string>,
|
|
37
|
+
/**
|
|
38
|
+
* The runtime this project's app RUNS as, when it is a desktop one.
|
|
39
|
+
*
|
|
40
|
+
* A desktop shell serves its renderer from an ordinary dev server, so its window and a browser tab
|
|
41
|
+
* open on the same origin are indistinguishable by url. Driving the tab passes every check here —
|
|
42
|
+
* live, on the url, SDK present — while having none of the app's IPC and none of its commands, so
|
|
43
|
+
* the verdict describes a different program that happens to share an address.
|
|
44
|
+
*
|
|
45
|
+
* Undefined for a web project, where the runtime is not a distinction worth making.
|
|
46
|
+
*/
|
|
47
|
+
requiredRuntime?: string): CandidateSession | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which connected session to drive, out of everything the daemon is holding.
|
|
3
|
+
*
|
|
4
|
+
* This is a false-green guard, which is why it is its own module. Two ways to get it wrong, and
|
|
5
|
+
* both report a successful install for an app that was never verified:
|
|
6
|
+
*
|
|
7
|
+
* - Match "any session" and you pass on somebody else's tab. A daemon usually holds several, and
|
|
8
|
+
* one of them being alive says nothing about whether THIS app connected.
|
|
9
|
+
* - Match "the first session on this url" and you drive whichever the daemon listed first, usually
|
|
10
|
+
* the oldest: a tab whose dev server died yesterday. The HUD then plays to a window nobody is
|
|
11
|
+
* watching, and the verdict describes a page the user cannot see.
|
|
12
|
+
*/
|
|
13
|
+
/** Sorts a hidden tab after a visible one; among equals, the least stale first. */
|
|
14
|
+
const isLive = (s) => true !== s.hidden && true !== s.throttled;
|
|
15
|
+
const staleness = (s) => s.lastSeenMs ?? Number.POSITIVE_INFINITY;
|
|
16
|
+
/**
|
|
17
|
+
* The session to drive, or null when nothing on this url qualifies.
|
|
18
|
+
*
|
|
19
|
+
* Preference order, strongest evidence first:
|
|
20
|
+
* 1. NEW since we opened the tab — definitely this run's, definitely alive
|
|
21
|
+
* 2. visible and not throttled — the tab a human is actually looking at
|
|
22
|
+
* 3. least stale — the best of a bad set
|
|
23
|
+
*
|
|
24
|
+
* `before` is the set of session ids that already existed when this run started; anything outside
|
|
25
|
+
* it belongs to us.
|
|
26
|
+
*/
|
|
27
|
+
export function pickSession(sessions, url, before = new Set(),
|
|
28
|
+
/**
|
|
29
|
+
* The runtime this project's app RUNS as, when it is a desktop one.
|
|
30
|
+
*
|
|
31
|
+
* A desktop shell serves its renderer from an ordinary dev server, so its window and a browser tab
|
|
32
|
+
* open on the same origin are indistinguishable by url. Driving the tab passes every check here —
|
|
33
|
+
* live, on the url, SDK present — while having none of the app's IPC and none of its commands, so
|
|
34
|
+
* the verdict describes a different program that happens to share an address.
|
|
35
|
+
*
|
|
36
|
+
* Undefined for a web project, where the runtime is not a distinction worth making.
|
|
37
|
+
*/
|
|
38
|
+
requiredRuntime) {
|
|
39
|
+
const wanted = String(url).replace(/\/$/, '');
|
|
40
|
+
const onUrl = sessions
|
|
41
|
+
.filter((s) => (s?.url ?? '').startsWith(wanted))
|
|
42
|
+
// An SDK too old to report a runtime is ACCEPTED rather than excluded: re-running init is the
|
|
43
|
+
// only upgrade path an existing user has, and refusing them their own install to enforce a
|
|
44
|
+
// field their SDK cannot send would break the one route out.
|
|
45
|
+
.filter((s) => undefined === requiredRuntime || undefined === s.runtime || requiredRuntime === s.runtime);
|
|
46
|
+
if (0 === onUrl.length)
|
|
47
|
+
return null;
|
|
48
|
+
const fresh = onUrl.filter((s) => !before.has(s.sessionId));
|
|
49
|
+
const pool = 0 < fresh.length ? fresh : onUrl;
|
|
50
|
+
return pool.find(isLive) ?? [...pool].sort((a, b) => staleness(a) - staleness(b))[0] ?? null;
|
|
51
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wire: everything init established, plus the world, plus the phases.
|
|
3
|
+
*
|
|
4
|
+
* Kept apart from cli.ts so the assembly is readable in one place and so `handleInit` stays the
|
|
5
|
+
* three lines it was. The only decision here is which effects to hand over; the sequencing lives in
|
|
6
|
+
* run-setup.ts and the pieces it calls.
|
|
7
|
+
*/
|
|
8
|
+
import { type SetupInput, type SetupOutcome } from './run-setup.js';
|
|
9
|
+
export interface SetupCommandInput extends Omit<SetupInput, 'shape'> {
|
|
10
|
+
/** Where setup was invoked, which is not the app directory in a monorepo. */
|
|
11
|
+
readonly invokedAt: string;
|
|
12
|
+
readonly bridgePort: number;
|
|
13
|
+
readonly env: Readonly<Record<string, string>>;
|
|
14
|
+
readonly flow?: string | undefined;
|
|
15
|
+
readonly driveBudgetUsd: number;
|
|
16
|
+
readonly driveModel?: string | undefined;
|
|
17
|
+
readonly escalateWeakFlow: boolean;
|
|
18
|
+
/** Register the MCP server with the coding agents init does not itself reach. */
|
|
19
|
+
readonly registerAgents: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Register with the agents `init` does not reach, and leave the skill where one loads skills from.
|
|
23
|
+
*
|
|
24
|
+
* init covers eight clients and only where it finds them installed, which leaves VS Code's USER
|
|
25
|
+
* scope unwritten — it exists on machines today, and init only ever writes the project-scope file,
|
|
26
|
+
* so a VS Code user has no tools outside the directory they ran init in.
|
|
27
|
+
*/
|
|
28
|
+
export declare function registerOtherAgents(print: (line: string) => void): void;
|
|
29
|
+
export interface SetupCommandResult extends SetupOutcome {
|
|
30
|
+
/** Set when a weak flow was re-recorded with the stronger model. */
|
|
31
|
+
readonly escalated?: {
|
|
32
|
+
readonly from: string;
|
|
33
|
+
readonly to: string;
|
|
34
|
+
} | undefined;
|
|
35
|
+
readonly driveTurns?: number | undefined;
|
|
36
|
+
readonly driveCostUsd?: number | undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Run the phases against the real world.
|
|
40
|
+
*
|
|
41
|
+
* The dev server is stopped on every ending except success, where it is handed to the user: an
|
|
42
|
+
* instrumented app they can watch is the deliverable, and killing it would leave them with config
|
|
43
|
+
* files and a dead tab.
|
|
44
|
+
*/
|
|
45
|
+
export declare function runSetupCommand(input: SetupCommandInput, print: (line: string) => void): Promise<SetupCommandResult>;
|