@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,199 @@
|
|
|
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 { existsSync, readFileSync } from 'node:fs';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { planAgentConfigs } from './agent-configs.js';
|
|
12
|
+
import { AppShape, readShape } from './desktop-shape.js';
|
|
13
|
+
import { stopOnInterrupt } from './interrupt.js';
|
|
14
|
+
import { applyAgentPlan, applyAgentSkills } from './agent-writer.js';
|
|
15
|
+
import { ApprovalOutcome, grantAutoApproval } from './auto-approve.js';
|
|
16
|
+
import { agentIo } from './agent-io.js';
|
|
17
|
+
import { EnsureDaemon, ensureDaemon, nodeEnsureDaemonDeps } from './ensure-daemon.js';
|
|
18
|
+
import { openInBrowser } from '../cli/cli-launch.js';
|
|
19
|
+
import { chooseDriver, DRIVERS, shouldEscalate } from './drive-plan.js';
|
|
20
|
+
import { driveWith } from './drive-agent.js';
|
|
21
|
+
import { binaryExists, flowsSaved, listSessions, OwnedDevServer, probePage, } from './node-effects.js';
|
|
22
|
+
import { runSetupPhases, } from './run-setup.js';
|
|
23
|
+
/** The capabilities file init scaffolds, in the order a project is likely to have it. */
|
|
24
|
+
const CAPABILITY_FILES = ['reticle-dev.tsx', 'reticle-dev.ts', 'reticle-dev.jsx', 'reticle-dev.js'];
|
|
25
|
+
/** `electron` in either dependency list, read the same way init's desktop doctor reads it. */
|
|
26
|
+
function hasElectronDependency(dir) {
|
|
27
|
+
try {
|
|
28
|
+
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
|
|
29
|
+
if ('object' !== typeof pkg || null === pkg)
|
|
30
|
+
return false;
|
|
31
|
+
const p = pkg;
|
|
32
|
+
return undefined !== { ...p.dependencies, ...p.devDependencies }['electron'];
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Register with the agents `init` does not reach, and leave the skill where one loads skills from.
|
|
40
|
+
*
|
|
41
|
+
* init covers eight clients and only where it finds them installed, which leaves VS Code's USER
|
|
42
|
+
* scope unwritten — it exists on machines today, and init only ever writes the project-scope file,
|
|
43
|
+
* so a VS Code user has no tools outside the directory they ran init in.
|
|
44
|
+
*/
|
|
45
|
+
export function registerOtherAgents(print) {
|
|
46
|
+
const platform = process.platform;
|
|
47
|
+
const home = homedir();
|
|
48
|
+
const results = applyAgentPlan(planAgentConfigs({ home, platform, exists: agentIo.exists, readFile: agentIo.readFile }), agentIo);
|
|
49
|
+
const wrote = results.filter((r) => 'created' === r.action || 'merged' === r.action);
|
|
50
|
+
if (0 < wrote.length) {
|
|
51
|
+
print(`registered the MCP server with ${wrote.length} more agent(s): ${wrote.map((r) => r.name).join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
// A format we will not rewrite is somebody's to edit, so it has to be said rather than skipped.
|
|
54
|
+
for (const manual of results.filter((r) => 'manual' === r.action)) {
|
|
55
|
+
print(`${manual.name}: ${manual.why} — add the reticle entry to ${manual.file} by hand.`);
|
|
56
|
+
}
|
|
57
|
+
const skills = applyAgentSkills(agentIo, { home, platform });
|
|
58
|
+
if (0 < skills.length)
|
|
59
|
+
print(`wrote the /reticle skill for ${skills.length} agent(s)`);
|
|
60
|
+
// Registration alone still leaves an Accept dialog in front of every call, and a verification run
|
|
61
|
+
// makes dozens of them: the loop is only autonomous once the tools are pre-approved.
|
|
62
|
+
const approvals = grantAutoApproval(agentIo, { home, platform });
|
|
63
|
+
const granted = approvals.filter((a) => ApprovalOutcome.GRANTED === a.outcome);
|
|
64
|
+
if (0 < granted.length) {
|
|
65
|
+
print(`pre-approved the reticle tools in ${granted.map((a) => a.name).join(', ')} — no Accept prompt per call`);
|
|
66
|
+
}
|
|
67
|
+
for (const noted of approvals.filter((a) => undefined !== a.warn)) {
|
|
68
|
+
print(`${noted.name}: ${String(noted.warn)}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Run the phases against the real world.
|
|
73
|
+
*
|
|
74
|
+
* The dev server is stopped on every ending except success, where it is handed to the user: an
|
|
75
|
+
* instrumented app they can watch is the deliverable, and killing it would leave them with config
|
|
76
|
+
* files and a dead tab.
|
|
77
|
+
*/
|
|
78
|
+
export async function runSetupCommand(input, print) {
|
|
79
|
+
if (input.registerAgents)
|
|
80
|
+
registerOtherAgents(print);
|
|
81
|
+
// Before the app is booted, because the app's whole job from here is to dial this port. Without
|
|
82
|
+
// it the phases wait out their budget and then report the SDK as the thing that failed.
|
|
83
|
+
const daemon = await ensureDaemon(input.bridgePort, nodeEnsureDaemonDeps());
|
|
84
|
+
if (EnsureDaemon.UNAVAILABLE === daemon) {
|
|
85
|
+
print(`could not start the Reticle daemon on port ${String(input.bridgePort)}, so nothing is listening for the app to connect to. Run \`npx @reticlehq/server serve --port ${String(input.bridgePort)}\` and try again.`);
|
|
86
|
+
}
|
|
87
|
+
// Which shell this is decides three things the phases would otherwise get wrong: opening a
|
|
88
|
+
// browser (harmful for desktop, where the app's own window is the client), waiting for an HTTP
|
|
89
|
+
// port (a Tauri webview has none), and how long the app gets to appear (a cold `tauri dev` builds
|
|
90
|
+
// Rust first). The same evidence init's desktop doctor reads.
|
|
91
|
+
const shape = readShape({
|
|
92
|
+
hasTauriConf: existsSync(join(input.appDir, 'src-tauri', 'tauri.conf.json')),
|
|
93
|
+
hasElectronDep: hasElectronDependency(input.appDir),
|
|
94
|
+
});
|
|
95
|
+
if (AppShape.WEB !== shape)
|
|
96
|
+
print(`detected a ${shape} app`);
|
|
97
|
+
const server = new OwnedDevServer();
|
|
98
|
+
// Both roots, because in a monorepo `.reticle/` sits at the app root rather than where setup ran.
|
|
99
|
+
const flowRoots = [input.invokedAt, input.appDir];
|
|
100
|
+
let lastDrive;
|
|
101
|
+
let escalated;
|
|
102
|
+
const effects = {
|
|
103
|
+
startDevServer: (command, cwd) => {
|
|
104
|
+
print(`starting: ${command}`);
|
|
105
|
+
server.start(command, cwd, input.env);
|
|
106
|
+
return Promise.resolve();
|
|
107
|
+
},
|
|
108
|
+
devServerOutput: () => server.output(),
|
|
109
|
+
devServerExited: () => server.exited(),
|
|
110
|
+
devServerQuietForMs: () => server.quietForMs(),
|
|
111
|
+
observedPorts: () => server.listeningPorts(),
|
|
112
|
+
probePage,
|
|
113
|
+
openBrowser: async (url) => {
|
|
114
|
+
const failure = await openInBrowser(url);
|
|
115
|
+
if (null !== failure) {
|
|
116
|
+
print(`could not open a browser (${failure}). On a machine with none — CI, a container, an SSH ` +
|
|
117
|
+
'session — take a tab Reticle owns instead: reticle_run({ tool: "reticle_lease", args: ' +
|
|
118
|
+
`{ action: "acquire", url: "${url}" } }).`);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
listSessions: () => listSessions(input.bridgePort),
|
|
122
|
+
// Asked before the drive rather than inferred from its result: "nothing to drive with" and
|
|
123
|
+
// "drove and proved nothing" are different answers and only the second is a failure.
|
|
124
|
+
driverAvailable: () => null !==
|
|
125
|
+
chooseDriver(DRIVERS, (bin) => ({ present: binaryExists(bin), runs: binaryExists(bin) })),
|
|
126
|
+
drive: (url, session) => {
|
|
127
|
+
const driver = chooseDriver(DRIVERS, (bin) => ({
|
|
128
|
+
present: binaryExists(bin),
|
|
129
|
+
// Present is not enough: a CLI that does not run produces an empty session that looks
|
|
130
|
+
// exactly like success.
|
|
131
|
+
runs: binaryExists(bin),
|
|
132
|
+
}));
|
|
133
|
+
if (null === driver)
|
|
134
|
+
return Promise.resolve(null);
|
|
135
|
+
// Only when the SESSION says they were never finished. init fills this file for a
|
|
136
|
+
// conventional app — it detects a state library and the testids — so opening it otherwise
|
|
137
|
+
// spends a turn on work already done, and grants write access nobody needed.
|
|
138
|
+
const capabilitiesFile = false === session.hasCapabilities
|
|
139
|
+
? CAPABILITY_FILES.map((f) => join(input.appDir, 'src', f)).find((p) => existsSync(p))
|
|
140
|
+
: undefined;
|
|
141
|
+
print('');
|
|
142
|
+
print(` ▸ WATCH ${url} NOW — the HUD is on, and you are about to see Reticle drive your app.`);
|
|
143
|
+
print('');
|
|
144
|
+
const request = {
|
|
145
|
+
url,
|
|
146
|
+
sessionId: session.sessionId,
|
|
147
|
+
tabThrottled: false,
|
|
148
|
+
budgetUsd: input.driveBudgetUsd,
|
|
149
|
+
...(undefined === input.flow ? {} : { flow: input.flow }),
|
|
150
|
+
...(undefined === input.driveModel ? {} : { model: input.driveModel }),
|
|
151
|
+
...(undefined === capabilitiesFile ? {} : { unfinishedCapabilitiesFile: capabilitiesFile }),
|
|
152
|
+
};
|
|
153
|
+
lastDrive = driveWith(driver, request, input.appDir);
|
|
154
|
+
// A weak flow only ACTS, so it passes when the feature is broken — and setup replays saved
|
|
155
|
+
// flows, which turns one weak recording into a permanent green. Re-record it rather than
|
|
156
|
+
// hand the trade to the user.
|
|
157
|
+
if (shouldEscalate({
|
|
158
|
+
escalationEnabled: input.escalateWeakFlow,
|
|
159
|
+
fasterModel: input.driveModel,
|
|
160
|
+
flowSaved: flowsSaved(flowRoots),
|
|
161
|
+
...(undefined === lastDrive.grade ? { grade: undefined } : { grade: lastDrive.grade }),
|
|
162
|
+
})) {
|
|
163
|
+
print(`the saved flow graded \`${lastDrive.grade ?? 'unknown'}\` — re-recording with the default model`);
|
|
164
|
+
const stronger = driveWith(driver, { ...request, model: undefined }, input.appDir);
|
|
165
|
+
escalated = { from: lastDrive.grade ?? 'unknown', to: stronger.grade ?? 'unknown' };
|
|
166
|
+
if (undefined !== stronger.grade)
|
|
167
|
+
lastDrive = stronger;
|
|
168
|
+
}
|
|
169
|
+
if (undefined !== lastDrive.incomplete) {
|
|
170
|
+
print(`the drive did not finish, and ${lastDrive.incomplete}`);
|
|
171
|
+
}
|
|
172
|
+
return Promise.resolve('' === lastDrive.text ? null : lastDrive.text);
|
|
173
|
+
},
|
|
174
|
+
flowsSaved: () => flowsSaved(flowRoots),
|
|
175
|
+
now: () => Date.now(),
|
|
176
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
177
|
+
note: print,
|
|
178
|
+
};
|
|
179
|
+
// A `finally` does not run on SIGINT, and this phase owns a detached dev server — see interrupt.ts.
|
|
180
|
+
const releaseSignals = stopOnInterrupt(() => {
|
|
181
|
+
server.stop();
|
|
182
|
+
}, process);
|
|
183
|
+
try {
|
|
184
|
+
const outcome = await runSetupPhases({ ...input, shape }, effects);
|
|
185
|
+
// The app stays up only when there is something worth watching.
|
|
186
|
+
if (outcome.ok)
|
|
187
|
+
server.handOver();
|
|
188
|
+
return {
|
|
189
|
+
...outcome,
|
|
190
|
+
...(undefined === escalated ? {} : { escalated }),
|
|
191
|
+
...(undefined === lastDrive?.turns ? {} : { driveTurns: lastDrive.turns }),
|
|
192
|
+
...(undefined === lastDrive?.costUsd ? {} : { driveCostUsd: lastDrive.costUsd }),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
releaseSignals();
|
|
197
|
+
server.stop();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract between the agent and the script.
|
|
3
|
+
*
|
|
4
|
+
* Everything setup does is deterministic except for the handful of things that require reading a
|
|
5
|
+
* repository and a human's request and understanding both. Those are exactly what an agent is good
|
|
6
|
+
* at and code is not, so they arrive here as ARGUMENTS rather than as steps somebody walks through:
|
|
7
|
+
* the agent decides, the script executes, and neither does the other's job.
|
|
8
|
+
*
|
|
9
|
+
* What only an agent can know, and therefore what belongs in this type:
|
|
10
|
+
*
|
|
11
|
+
* --flow which journey proves the thing the user cares about. Code can enumerate buttons; it
|
|
12
|
+
* cannot know that checkout matters and the theme toggle does not. Measured, naming
|
|
13
|
+
* it took one app's drive from a ten-minute timeout to 138 seconds, because the turns
|
|
14
|
+
* go into FINDING a flow and naming one removes that search.
|
|
15
|
+
* --app which app in a monorepo the user meant. init can find the ones that are servable;
|
|
16
|
+
* only the request says which of them is being worked on.
|
|
17
|
+
* --env what the app needs to get past its own front door. A key in .env.example, a mock
|
|
18
|
+
* backend named in the README, the variable that skips an auth wall: all readable by
|
|
19
|
+
* an agent, none inferable by a script. Without this a real app sits on a login
|
|
20
|
+
* screen and there is no flow to drive at all.
|
|
21
|
+
* --dev-cmd the command, when the project's own scripts do not say it plainly.
|
|
22
|
+
* --url the app is already running here, so do not start anything.
|
|
23
|
+
*
|
|
24
|
+
* Everything else on this type is an operator's dial, not an agent's judgement.
|
|
25
|
+
*/
|
|
26
|
+
/** The bridge port. NOT the dev server's, and conflating them is a documented setup failure. */
|
|
27
|
+
export declare const DEFAULT_BRIDGE_PORT = 4400;
|
|
28
|
+
/** Per-phase budget. The drive gets its own, much larger one. */
|
|
29
|
+
export declare const DEFAULT_PHASE_TIMEOUT_MS = 120000;
|
|
30
|
+
/** What the drive may spend before it is stopped. */
|
|
31
|
+
export declare const DEFAULT_DRIVE_BUDGET_USD = 3;
|
|
32
|
+
export interface SetupOptions {
|
|
33
|
+
/** The journey to drive, in the caller's own words. */
|
|
34
|
+
readonly flow?: string | undefined;
|
|
35
|
+
/** Which app in a monorepo. */
|
|
36
|
+
readonly app?: string | undefined;
|
|
37
|
+
/** Environment the app needs in order to reach a usable state. */
|
|
38
|
+
readonly env?: Readonly<Record<string, string>> | undefined;
|
|
39
|
+
/** The dev command, when the project's scripts do not say it plainly. */
|
|
40
|
+
readonly devCommand?: string | undefined;
|
|
41
|
+
/** The app is already served here. */
|
|
42
|
+
readonly url?: string | undefined;
|
|
43
|
+
readonly bridgePort: number;
|
|
44
|
+
readonly phaseTimeoutMs: number;
|
|
45
|
+
readonly driveBudgetUsd: number;
|
|
46
|
+
readonly driveModel?: string | undefined;
|
|
47
|
+
/** Register the MCP with the other coding agents on this machine. */
|
|
48
|
+
readonly registerAgents: boolean;
|
|
49
|
+
/** Open a browser. False for CI, a headless box, or a tab the user already has. */
|
|
50
|
+
readonly openBrowser: boolean;
|
|
51
|
+
/** Drive a flow. False leaves step five to the caller, and is the only honest way to skip it. */
|
|
52
|
+
readonly drive: boolean;
|
|
53
|
+
/** Re-record with the stronger model when the saved flow is graded weaker than `asserted`. */
|
|
54
|
+
readonly escalateWeakFlow: boolean;
|
|
55
|
+
/** Restart a dev server whose bundle predates the build-config edit. */
|
|
56
|
+
readonly restartStaleDevServer: boolean;
|
|
57
|
+
/** Write files and stop, which is what `init` did before it grew the runtime phases. */
|
|
58
|
+
readonly filesOnly: boolean;
|
|
59
|
+
/** A license key, written to .env and never echoed. */
|
|
60
|
+
readonly licenseKey?: string | undefined;
|
|
61
|
+
}
|
|
62
|
+
export declare const DEFAULT_SETUP_OPTIONS: SetupOptions;
|
|
63
|
+
/** One `KEY=VALUE`, as an agent would pass it. Everything after the first `=` is the value. */
|
|
64
|
+
export declare function parseEnvAssignment(pair: string): {
|
|
65
|
+
key: string;
|
|
66
|
+
value: string;
|
|
67
|
+
} | null;
|
|
68
|
+
/**
|
|
69
|
+
* Fold repeated `--env KEY=VALUE` into one map, ignoring what cannot be a variable.
|
|
70
|
+
*
|
|
71
|
+
* A malformed pair is dropped rather than guessed at: an agent that meant `--env` and typed
|
|
72
|
+
* something else should see its variable missing, not see setup invent one.
|
|
73
|
+
*/
|
|
74
|
+
export declare function collectEnv(pairs: readonly string[]): Record<string, string>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract between the agent and the script.
|
|
3
|
+
*
|
|
4
|
+
* Everything setup does is deterministic except for the handful of things that require reading a
|
|
5
|
+
* repository and a human's request and understanding both. Those are exactly what an agent is good
|
|
6
|
+
* at and code is not, so they arrive here as ARGUMENTS rather than as steps somebody walks through:
|
|
7
|
+
* the agent decides, the script executes, and neither does the other's job.
|
|
8
|
+
*
|
|
9
|
+
* What only an agent can know, and therefore what belongs in this type:
|
|
10
|
+
*
|
|
11
|
+
* --flow which journey proves the thing the user cares about. Code can enumerate buttons; it
|
|
12
|
+
* cannot know that checkout matters and the theme toggle does not. Measured, naming
|
|
13
|
+
* it took one app's drive from a ten-minute timeout to 138 seconds, because the turns
|
|
14
|
+
* go into FINDING a flow and naming one removes that search.
|
|
15
|
+
* --app which app in a monorepo the user meant. init can find the ones that are servable;
|
|
16
|
+
* only the request says which of them is being worked on.
|
|
17
|
+
* --env what the app needs to get past its own front door. A key in .env.example, a mock
|
|
18
|
+
* backend named in the README, the variable that skips an auth wall: all readable by
|
|
19
|
+
* an agent, none inferable by a script. Without this a real app sits on a login
|
|
20
|
+
* screen and there is no flow to drive at all.
|
|
21
|
+
* --dev-cmd the command, when the project's own scripts do not say it plainly.
|
|
22
|
+
* --url the app is already running here, so do not start anything.
|
|
23
|
+
*
|
|
24
|
+
* Everything else on this type is an operator's dial, not an agent's judgement.
|
|
25
|
+
*/
|
|
26
|
+
/** The bridge port. NOT the dev server's, and conflating them is a documented setup failure. */
|
|
27
|
+
export const DEFAULT_BRIDGE_PORT = 4400;
|
|
28
|
+
/** Per-phase budget. The drive gets its own, much larger one. */
|
|
29
|
+
export const DEFAULT_PHASE_TIMEOUT_MS = 120_000;
|
|
30
|
+
/** What the drive may spend before it is stopped. */
|
|
31
|
+
export const DEFAULT_DRIVE_BUDGET_USD = 3;
|
|
32
|
+
export const DEFAULT_SETUP_OPTIONS = {
|
|
33
|
+
bridgePort: DEFAULT_BRIDGE_PORT,
|
|
34
|
+
phaseTimeoutMs: DEFAULT_PHASE_TIMEOUT_MS,
|
|
35
|
+
driveBudgetUsd: DEFAULT_DRIVE_BUDGET_USD,
|
|
36
|
+
registerAgents: true,
|
|
37
|
+
openBrowser: true,
|
|
38
|
+
drive: true,
|
|
39
|
+
escalateWeakFlow: true,
|
|
40
|
+
restartStaleDevServer: true,
|
|
41
|
+
filesOnly: false,
|
|
42
|
+
};
|
|
43
|
+
/** One `KEY=VALUE`, as an agent would pass it. Everything after the first `=` is the value. */
|
|
44
|
+
export function parseEnvAssignment(pair) {
|
|
45
|
+
const at = pair.indexOf('=');
|
|
46
|
+
if (0 >= at)
|
|
47
|
+
return null;
|
|
48
|
+
const key = pair.slice(0, at).trim();
|
|
49
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
50
|
+
return null;
|
|
51
|
+
return { key, value: pair.slice(at + 1) };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Fold repeated `--env KEY=VALUE` into one map, ignoring what cannot be a variable.
|
|
55
|
+
*
|
|
56
|
+
* A malformed pair is dropped rather than guessed at: an agent that meant `--env` and typed
|
|
57
|
+
* something else should see its variable missing, not see setup invent one.
|
|
58
|
+
*/
|
|
59
|
+
export function collectEnv(pairs) {
|
|
60
|
+
const env = {};
|
|
61
|
+
for (const pair of pairs) {
|
|
62
|
+
const parsed = parseEnvAssignment(pair);
|
|
63
|
+
if (null !== parsed)
|
|
64
|
+
env[parsed.key] = parsed.value;
|
|
65
|
+
}
|
|
66
|
+
return env;
|
|
67
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does a transcript exist for this Claude Code session.
|
|
3
|
+
*
|
|
4
|
+
* Two roots, because a shared-config install writes elsewhere and a restart that checks only one of
|
|
5
|
+
* them refuses a session that is perfectly real.
|
|
6
|
+
*/
|
|
7
|
+
export declare function claudeTranscriptExists(sessionId: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* The codex conversation running in `cwd`, when one is.
|
|
10
|
+
*
|
|
11
|
+
* Codex names no session in the environment — unlike Claude Code — so it is recognised from the
|
|
12
|
+
* rollout file it is writing. Not yet wired to a reader here: returning undefined means "no codex
|
|
13
|
+
* session identified", which is the honest answer until one exists, and the decision it feeds says
|
|
14
|
+
* plainly that the client identified no conversation.
|
|
15
|
+
*/
|
|
16
|
+
export declare function codexSessionFor(_cwd: string): string | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which conversation is asking, and does it really exist.
|
|
3
|
+
*
|
|
4
|
+
* Both answers come off the filesystem, so they are kept away from the pure decision in relaunch.ts
|
|
5
|
+
* — that is the part worth testing, and it is the part that can be wrong without anybody noticing.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
/**
|
|
11
|
+
* Does a transcript exist for this Claude Code session.
|
|
12
|
+
*
|
|
13
|
+
* Two roots, because a shared-config install writes elsewhere and a restart that checks only one of
|
|
14
|
+
* them refuses a session that is perfectly real.
|
|
15
|
+
*/
|
|
16
|
+
export function claudeTranscriptExists(sessionId) {
|
|
17
|
+
for (const base of [
|
|
18
|
+
join(homedir(), '.claude-shared', 'projects'),
|
|
19
|
+
join(homedir(), '.claude', 'projects'),
|
|
20
|
+
]) {
|
|
21
|
+
try {
|
|
22
|
+
for (const project of readdirSync(base)) {
|
|
23
|
+
if (existsSync(join(base, project, `${sessionId}.jsonl`)))
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
/* that history directory does not exist on this machine */
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The codex conversation running in `cwd`, when one is.
|
|
35
|
+
*
|
|
36
|
+
* Codex names no session in the environment — unlike Claude Code — so it is recognised from the
|
|
37
|
+
* rollout file it is writing. Not yet wired to a reader here: returning undefined means "no codex
|
|
38
|
+
* session identified", which is the honest answer until one exists, and the decision it feeds says
|
|
39
|
+
* plainly that the client identified no conversation.
|
|
40
|
+
*/
|
|
41
|
+
export function codexSessionFor(_cwd) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
package/dist/status-payload.d.ts
CHANGED
|
@@ -24,8 +24,21 @@ interface StatusPayload {
|
|
|
24
24
|
* running the command we tell them to run in `init`'s closing line deserves the same sentence.
|
|
25
25
|
*/
|
|
26
26
|
why?: string;
|
|
27
|
+
/** Port of the verify HTTP endpoint this daemon serves — present only when started with `--http`. */
|
|
28
|
+
verifyPort?: number;
|
|
27
29
|
}
|
|
28
30
|
export declare function statusPayload(sessionCount: number, sessions: SessionInfo[],
|
|
29
31
|
/** The no-session diagnosis, injected so this stays pure and the port probe stays off this path. */
|
|
30
|
-
why?: string): StatusPayload;
|
|
32
|
+
why?: string, verifyPort?: number): StatusPayload;
|
|
33
|
+
/**
|
|
34
|
+
* Why `serve --http` cannot claim success against an already-running daemon — or undefined when
|
|
35
|
+
* that daemon already serves the verify HTTP endpoint on the wanted port.
|
|
36
|
+
*
|
|
37
|
+
* `serve` never hands flags to a daemon that is already up, so `--http`/`--http-port` against one
|
|
38
|
+
* used to be accepted, dropped, and reported as success (#687). This is the read side of the
|
|
39
|
+
* `verifyPort` field above: the daemon says which port it serves, and `serve` compares. A daemon
|
|
40
|
+
* too old to report the field — or one that did not answer /status — reads as "not honoured",
|
|
41
|
+
* which errs loud rather than green.
|
|
42
|
+
*/
|
|
43
|
+
export declare function verifyEndpointMismatch(status: unknown, wantedPort: number): string | undefined;
|
|
31
44
|
export {};
|
package/dist/status-payload.js
CHANGED
|
@@ -2,7 +2,7 @@ import { CONTRACT_FINGERPRINT } from '@reticlehq/core';
|
|
|
2
2
|
import { SERVER_VERSION } from './version/server-version.js';
|
|
3
3
|
export function statusPayload(sessionCount, sessions,
|
|
4
4
|
/** The no-session diagnosis, injected so this stays pure and the port probe stays off this path. */
|
|
5
|
-
why) {
|
|
5
|
+
why, verifyPort) {
|
|
6
6
|
return {
|
|
7
7
|
running: true,
|
|
8
8
|
version: SERVER_VERSION,
|
|
@@ -12,5 +12,27 @@ why) {
|
|
|
12
12
|
// Only when there is nothing to explain away: a diagnosis printed beside a live session would
|
|
13
13
|
// contradict it.
|
|
14
14
|
...(0 === sessionCount && why !== undefined ? { why } : {}),
|
|
15
|
+
...(verifyPort === undefined ? {} : { verifyPort }),
|
|
15
16
|
};
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Why `serve --http` cannot claim success against an already-running daemon — or undefined when
|
|
20
|
+
* that daemon already serves the verify HTTP endpoint on the wanted port.
|
|
21
|
+
*
|
|
22
|
+
* `serve` never hands flags to a daemon that is already up, so `--http`/`--http-port` against one
|
|
23
|
+
* used to be accepted, dropped, and reported as success (#687). This is the read side of the
|
|
24
|
+
* `verifyPort` field above: the daemon says which port it serves, and `serve` compares. A daemon
|
|
25
|
+
* too old to report the field — or one that did not answer /status — reads as "not honoured",
|
|
26
|
+
* which errs loud rather than green.
|
|
27
|
+
*/
|
|
28
|
+
export function verifyEndpointMismatch(status, wantedPort) {
|
|
29
|
+
const served = 'object' === typeof status && status !== null
|
|
30
|
+
? status['verifyPort']
|
|
31
|
+
: undefined;
|
|
32
|
+
if (served === wantedPort)
|
|
33
|
+
return undefined;
|
|
34
|
+
const fix = 'stop it (`reticle stop`) and run `reticle serve --http` again';
|
|
35
|
+
return 'number' === typeof served
|
|
36
|
+
? `the daemon already running serves the verify HTTP endpoint on :${String(served)}, not :${String(wantedPort)} — ${fix}`
|
|
37
|
+
: `a daemon is already running without the verify HTTP endpoint — \`--http\` cannot be applied to it; ${fix}`;
|
|
38
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutomationHint } from '@reticlehq/core';
|
|
2
|
-
|
|
2
|
+
interface AutomationInput {
|
|
3
3
|
env: NodeJS.ProcessEnv;
|
|
4
4
|
fileExists: (path: string) => boolean;
|
|
5
5
|
hasTty: boolean;
|
|
@@ -15,3 +15,4 @@ export interface AutomationInput {
|
|
|
15
15
|
export declare function resolveAutomationHint(input: AutomationInput): AutomationHint | undefined;
|
|
16
16
|
/** The real reading, taken once per process. */
|
|
17
17
|
export declare function currentAutomationHint(env: NodeJS.ProcessEnv): AutomationHint | undefined;
|
|
18
|
+
export {};
|
|
@@ -53,4 +53,11 @@ export interface DaemonTelemetry {
|
|
|
53
53
|
* Report the daemon as started, profile the project once, and keep the session counters flushing.
|
|
54
54
|
* Every timer is `unref`'d so telemetry can never be the reason a daemon refuses to exit.
|
|
55
55
|
*/
|
|
56
|
-
export declare function installDaemonTelemetry(cwd: string, now?: () => number
|
|
56
|
+
export declare function installDaemonTelemetry(cwd: string, now?: () => number,
|
|
57
|
+
/**
|
|
58
|
+
* The bridge port this daemon bound, so the profile can say whether an app for this project has
|
|
59
|
+
* EVER connected. Optional: a caller that does not know the port gets a profile without that bit
|
|
60
|
+
* rather than one with a guessed `false`, which is the value the whole field exists to make
|
|
61
|
+
* trustworthy.
|
|
62
|
+
*/
|
|
63
|
+
port?: number): DaemonTelemetry;
|
|
@@ -10,10 +10,34 @@ import { TelemetryEventKind } from '@reticlehq/core';
|
|
|
10
10
|
import { getTelemetry } from './telemetry.js';
|
|
11
11
|
import { getSessionMetrics } from './session-metrics.js';
|
|
12
12
|
import { profileProject } from './project-profile.js';
|
|
13
|
-
import {
|
|
13
|
+
import { hasProjectConnectedBefore } from '../session/connection-memory.js';
|
|
14
|
+
import { readProjectId } from '../cli/cli-port.js';
|
|
15
|
+
import { reticleStateHome } from '../daemon/daemon.js';
|
|
16
|
+
import { startUpdateCheck, updateNudgeState } from '../update/update-nudge.js';
|
|
14
17
|
import { markDaemonStart } from './mcp-connection.js';
|
|
15
18
|
import { markInstrumentationClock } from './app-instrumented.js';
|
|
16
19
|
import { markStallClock, STALL_AFTER_MS, stallUptime } from '../session/stall-clock.js';
|
|
20
|
+
/**
|
|
21
|
+
* Whether an app for this project has ever reached Reticle, read from the same durable memory the
|
|
22
|
+
* no-session diagnosis and the server instructions read.
|
|
23
|
+
*
|
|
24
|
+
* Best-effort: an unreadable state home must never be the reason a daemon start emits nothing, and
|
|
25
|
+
* `undefined` here means "not measured" rather than "no". A `false` invented from a read error is
|
|
26
|
+
* exactly the lie this field exists to avoid — it would put the working installs into the cohort we
|
|
27
|
+
* are trying to size.
|
|
28
|
+
*/
|
|
29
|
+
function installFacts(cwd, port) {
|
|
30
|
+
if (port === undefined)
|
|
31
|
+
return undefined;
|
|
32
|
+
try {
|
|
33
|
+
return {
|
|
34
|
+
appConnectedBefore: hasProjectConnectedBefore(reticleStateHome(), port, readProjectId(cwd)),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
17
41
|
/** Let the daemon finish coming up before walking the source tree for a profile. */
|
|
18
42
|
const PROJECT_PROFILE_DELAY_MS = 5_000;
|
|
19
43
|
/**
|
|
@@ -44,11 +68,34 @@ export const SESSION_FLUSH_MS = 5 * 60 * 1000;
|
|
|
44
68
|
* and 74% of daemons never serve a tool at all.
|
|
45
69
|
*/
|
|
46
70
|
export const FIRST_FLUSH_MS = 90 * 1000;
|
|
71
|
+
/**
|
|
72
|
+
* Attach what the update nudge did to a summary that is about to be sent.
|
|
73
|
+
*
|
|
74
|
+
* Merged HERE rather than inside `SessionMetrics` deliberately: the nudge's state lives in the
|
|
75
|
+
* update module, and a second copy of it in the metrics object would be one more thing that can
|
|
76
|
+
* drift from the flag the delivery path actually reads. `summarize()` stays a pure roll-up of
|
|
77
|
+
* counters.
|
|
78
|
+
*/
|
|
79
|
+
function withNudgeState(summary) {
|
|
80
|
+
const nudge = updateNudgeState();
|
|
81
|
+
return {
|
|
82
|
+
...summary,
|
|
83
|
+
updateNudged: nudge.shown,
|
|
84
|
+
...(nudge.offered === undefined ? {} : { updateOffered: nudge.offered }),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
47
87
|
/**
|
|
48
88
|
* Report the daemon as started, profile the project once, and keep the session counters flushing.
|
|
49
89
|
* Every timer is `unref`'d so telemetry can never be the reason a daemon refuses to exit.
|
|
50
90
|
*/
|
|
51
|
-
export function installDaemonTelemetry(cwd, now = () => Date.now()
|
|
91
|
+
export function installDaemonTelemetry(cwd, now = () => Date.now(),
|
|
92
|
+
/**
|
|
93
|
+
* The bridge port this daemon bound, so the profile can say whether an app for this project has
|
|
94
|
+
* EVER connected. Optional: a caller that does not know the port gets a profile without that bit
|
|
95
|
+
* rather than one with a guessed `false`, which is the value the whole field exists to make
|
|
96
|
+
* trustworthy.
|
|
97
|
+
*/
|
|
98
|
+
port) {
|
|
52
99
|
const metrics = getSessionMetrics(now);
|
|
53
100
|
void getTelemetry().emit(TelemetryEventKind.DAEMON_STARTED);
|
|
54
101
|
// Ask npm whether a newer Reticle exists, so the agent can tell the human. Nothing checked before
|
|
@@ -66,7 +113,7 @@ export function installDaemonTelemetry(cwd, now = () => Date.now()) {
|
|
|
66
113
|
setTimeout(() => {
|
|
67
114
|
try {
|
|
68
115
|
void getTelemetry().emit(TelemetryEventKind.PROJECT_PROFILED, {
|
|
69
|
-
project: profileProject(cwd, now()),
|
|
116
|
+
project: profileProject(cwd, now(), installFacts(cwd, port)),
|
|
70
117
|
});
|
|
71
118
|
}
|
|
72
119
|
catch {
|
|
@@ -93,7 +140,7 @@ export function installDaemonTelemetry(cwd, now = () => Date.now()) {
|
|
|
93
140
|
return; // an idle window is not worth an event
|
|
94
141
|
// NOT daemon_stopped: the daemon is still running. See SESSION_PROGRESS.
|
|
95
142
|
void getTelemetry().emit(TelemetryEventKind.SESSION_PROGRESS, {
|
|
96
|
-
session: metrics.summarize(false),
|
|
143
|
+
session: withNudgeState(metrics.summarize(false)),
|
|
97
144
|
});
|
|
98
145
|
metrics.reset();
|
|
99
146
|
};
|
|
@@ -114,7 +161,7 @@ export function installDaemonTelemetry(cwd, now = () => Date.now()) {
|
|
|
114
161
|
// The rich one: the whole session in a single event — duration, the tool histogram, error
|
|
115
162
|
// shapes, verifications, browser launches. This is what replaced the per-tool-call event.
|
|
116
163
|
await getTelemetry().emit(TelemetryEventKind.DAEMON_STOPPED, {
|
|
117
|
-
session: metrics.summarize(true, exit),
|
|
164
|
+
session: withNudgeState(metrics.summarize(true, exit)),
|
|
118
165
|
});
|
|
119
166
|
})();
|
|
120
167
|
return stopped;
|
|
@@ -30,7 +30,7 @@ export const ErrorClass = {
|
|
|
30
30
|
const PATTERNS = [
|
|
31
31
|
[/confirmdangerous|destructive action blocked/i, ErrorClass.REFUSAL],
|
|
32
32
|
[
|
|
33
|
-
/\brefus(e|ed|es|ing)\b|not supported|unsupported|cannot .* a <|contenteditable/i,
|
|
33
|
+
/\brefus(e|ed|es|ing)\b|not supported|unsupported|cannot .* a <|contenteditable|cannot hover/i,
|
|
34
34
|
ErrorClass.REFUSAL,
|
|
35
35
|
],
|
|
36
36
|
[
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { McpScope, type Feedback } from '@reticlehq/core';
|
|
1
|
+
import { McpScope, StackUnknownReason, type Feedback } from '@reticlehq/core';
|
|
2
2
|
/** The context fields — the whole `Feedback` shape minus what the author supplies. */
|
|
3
3
|
export type FeedbackContext = Pick<Feedback, 'stack' | 'stackMajor' | 'runtime' | 'engine' | 'driver' | 'client' | 'clientVersion' | 'mcpScope'>;
|
|
4
4
|
export declare function detectStack(cwd: string, read?: (path: string) => string): {
|
|
5
5
|
stack?: string;
|
|
6
6
|
stackMajor?: number;
|
|
7
7
|
stackSource?: 'cwd' | 'workspace';
|
|
8
|
+
stackUnknownReason?: StackUnknownReason;
|
|
8
9
|
};
|
|
9
10
|
/**
|
|
10
11
|
* User scope unless the project checks in its own MCP registration. `reticle init` deliberately
|
|
@@ -12,6 +13,13 @@ export declare function detectStack(cwd: string, read?: (path: string) => string
|
|
|
12
13
|
* itself the interesting signal.
|
|
13
14
|
*/
|
|
14
15
|
export declare function detectMcpScope(cwd: string, exists?: (path: string) => boolean): McpScope;
|
|
16
|
+
/**
|
|
17
|
+
* The MCP client on the other end, self-reported in its `initialize` handshake (`claude-code`,
|
|
18
|
+
* `cursor-vscode`, …). Read through a HOOK rather than sniffing env vars: the handshake is the
|
|
19
|
+
* client's own claim about itself, while an env-var table is a guess that silently rots every time a
|
|
20
|
+
* vendor renames a variable. Unset (a CLI run with no MCP peer) simply reports no client.
|
|
21
|
+
*/
|
|
22
|
+
/** Kept as the telemetry-facing name; the mechanism itself lives in mcp/client-identity.ts. */
|
|
15
23
|
export declare function setMcpClientNameHook(hook: () => {
|
|
16
24
|
name?: string;
|
|
17
25
|
version?: string;
|