@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,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removing the Accept button, for the tools Reticle owns and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* A drive that stops on a per-call approval dialog is not automated, and the dialog is per CALL:
|
|
5
|
+
* a single verification run makes dozens, so a human sits there clicking through a loop that was
|
|
6
|
+
* supposed to run without them. Every client below documents a way to pre-approve a named server,
|
|
7
|
+
* and this writes exactly that rule, at install time, in the client's own spelling.
|
|
8
|
+
*
|
|
9
|
+
* Two limits, both deliberate:
|
|
10
|
+
*
|
|
11
|
+
* 1. ONLY `reticle` is allowed. Not `mcp(*)`, not a global auto-run switch. Reticle's tools drive
|
|
12
|
+
* the user's own dev server on their own machine, which is what makes pre-approving them
|
|
13
|
+
* defensible; the same edit made globally would hand the same pass to every other MCP server
|
|
14
|
+
* they ever install, including ones that reach the network. Their approval gate is not ours to
|
|
15
|
+
* dismantle, only ours to step out of.
|
|
16
|
+
* 2. Nothing is written for an agent that is not installed. An absent agent has no Accept button to
|
|
17
|
+
* remove, and the file would be litter — Cursor's especially, since that one TAKES OVER from an
|
|
18
|
+
* allowlist we cannot read.
|
|
19
|
+
*
|
|
20
|
+
* Codex is absent by design: its approval policy is global (`approval_policy`), so there is no
|
|
21
|
+
* reticle-shaped rule to write, and its config is TOML, which this repo never rewrites. Its
|
|
22
|
+
* headless form, `codex exec`, does not prompt at all — which is the form the drive uses.
|
|
23
|
+
*/
|
|
24
|
+
import { type PlatformPaths } from './agent-configs.js';
|
|
25
|
+
import type { AgentWriterIo } from './agent-writer.js';
|
|
26
|
+
/** A documented way to pre-approve one MCP server, in one client's own spelling. */
|
|
27
|
+
export interface ApprovalGrant {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly name: string;
|
|
30
|
+
/** Home-relative, per platform. */
|
|
31
|
+
readonly paths: PlatformPaths;
|
|
32
|
+
/** Present if ANY of these exist. An agent that is not installed is not configured. */
|
|
33
|
+
readonly markers: readonly PlatformPaths[];
|
|
34
|
+
/** The rule, in the client's words, for the plan output. */
|
|
35
|
+
readonly rule: string;
|
|
36
|
+
/** Adds our rule to a parsed config, leaving everything else exactly as it was. */
|
|
37
|
+
readonly grant: (current: Record<string, unknown>) => Record<string, unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* True where creating this file supersedes an in-app allowlist we cannot read, so the user has to
|
|
40
|
+
* be told rather than have their other servers quietly start prompting again.
|
|
41
|
+
*/
|
|
42
|
+
readonly supersedesInApp?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare const APPROVAL_GRANTS: readonly ApprovalGrant[];
|
|
45
|
+
export declare const ApprovalOutcome: {
|
|
46
|
+
readonly GRANTED: "granted";
|
|
47
|
+
readonly ALREADY: "already";
|
|
48
|
+
readonly ABSENT: "absent";
|
|
49
|
+
/** Deliberately left for an explicit run, because doing it unattended costs the user something. */
|
|
50
|
+
readonly DEFERRED: "deferred";
|
|
51
|
+
readonly FAILED: "failed";
|
|
52
|
+
};
|
|
53
|
+
export type ApprovalOutcome = (typeof ApprovalOutcome)[keyof typeof ApprovalOutcome];
|
|
54
|
+
export interface ApprovalResult {
|
|
55
|
+
readonly id: string;
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly file: string;
|
|
58
|
+
readonly outcome: ApprovalOutcome;
|
|
59
|
+
readonly rule: string;
|
|
60
|
+
/** Set where the user has to be told something the write alone does not say. */
|
|
61
|
+
readonly warn?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ApprovalWhere {
|
|
64
|
+
readonly home: string;
|
|
65
|
+
readonly platform: keyof PlatformPaths;
|
|
66
|
+
}
|
|
67
|
+
export interface ApprovalOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Refuse any grant that would CREATE a file superseding an allowlist we cannot read.
|
|
70
|
+
*
|
|
71
|
+
* Set on the unattended path. Writing Cursor's permissions.json for the first time takes over
|
|
72
|
+
* from what the user approved inside the app, which is a fair trade when they just ran a command
|
|
73
|
+
* and can read the line saying so, and not a fair trade at all when a version bump did it behind
|
|
74
|
+
* them: their OTHER MCP servers would start prompting again and nothing would connect that to us.
|
|
75
|
+
* Merging into a file they already own stays safe either way.
|
|
76
|
+
*/
|
|
77
|
+
readonly onlyIfNoSupersede?: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Pre-approve Reticle's tools everywhere the machine has an agent that would otherwise ask.
|
|
81
|
+
*
|
|
82
|
+
* A client that cannot be written is reported, never thrown: one unwritable settings file is not a
|
|
83
|
+
* reason to leave the other three prompting.
|
|
84
|
+
*/
|
|
85
|
+
export declare function grantAutoApproval(io: AgentWriterIo, where: ApprovalWhere, grants?: readonly ApprovalGrant[], options?: ApprovalOptions): ApprovalResult[];
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removing the Accept button, for the tools Reticle owns and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* A drive that stops on a per-call approval dialog is not automated, and the dialog is per CALL:
|
|
5
|
+
* a single verification run makes dozens, so a human sits there clicking through a loop that was
|
|
6
|
+
* supposed to run without them. Every client below documents a way to pre-approve a named server,
|
|
7
|
+
* and this writes exactly that rule, at install time, in the client's own spelling.
|
|
8
|
+
*
|
|
9
|
+
* Two limits, both deliberate:
|
|
10
|
+
*
|
|
11
|
+
* 1. ONLY `reticle` is allowed. Not `mcp(*)`, not a global auto-run switch. Reticle's tools drive
|
|
12
|
+
* the user's own dev server on their own machine, which is what makes pre-approving them
|
|
13
|
+
* defensible; the same edit made globally would hand the same pass to every other MCP server
|
|
14
|
+
* they ever install, including ones that reach the network. Their approval gate is not ours to
|
|
15
|
+
* dismantle, only ours to step out of.
|
|
16
|
+
* 2. Nothing is written for an agent that is not installed. An absent agent has no Accept button to
|
|
17
|
+
* remove, and the file would be litter — Cursor's especially, since that one TAKES OVER from an
|
|
18
|
+
* allowlist we cannot read.
|
|
19
|
+
*
|
|
20
|
+
* Codex is absent by design: its approval policy is global (`approval_policy`), so there is no
|
|
21
|
+
* reticle-shaped rule to write, and its config is TOML, which this repo never rewrites. Its
|
|
22
|
+
* headless form, `codex exec`, does not prompt at all — which is the form the drive uses.
|
|
23
|
+
*/
|
|
24
|
+
import { joinFor } from './agent-configs.js';
|
|
25
|
+
const RETICLE_KEY = 'reticle';
|
|
26
|
+
const INDENT = 2;
|
|
27
|
+
const home = (path) => ({ darwin: path, linux: path, win32: path });
|
|
28
|
+
/** Append to a nested array, without duplicating and without disturbing what is already in it. */
|
|
29
|
+
function addToList(current, outer, key, value) {
|
|
30
|
+
const scope = null === outer
|
|
31
|
+
? current
|
|
32
|
+
: 'object' === typeof current[outer] && null !== current[outer]
|
|
33
|
+
? { ...current[outer] }
|
|
34
|
+
: {};
|
|
35
|
+
const list = Array.isArray(scope[key]) ? scope[key] : [];
|
|
36
|
+
if (list.includes(value))
|
|
37
|
+
return current;
|
|
38
|
+
const next = { ...scope, [key]: [...list, value] };
|
|
39
|
+
return null === outer ? next : { ...current, [outer]: next };
|
|
40
|
+
}
|
|
41
|
+
export const APPROVAL_GRANTS = [
|
|
42
|
+
{
|
|
43
|
+
id: 'claude-code',
|
|
44
|
+
name: 'Claude Code',
|
|
45
|
+
paths: home('.claude/settings.json'),
|
|
46
|
+
markers: [home('.claude')],
|
|
47
|
+
rule: 'permissions.allow += "mcp__reticle"',
|
|
48
|
+
grant: (c) => addToList(c, 'permissions', 'allow', 'mcp__reticle'),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'cursor',
|
|
52
|
+
name: 'Cursor',
|
|
53
|
+
paths: home('.cursor/permissions.json'),
|
|
54
|
+
markers: [home('.cursor')],
|
|
55
|
+
rule: 'mcpAllowlist += "reticle:*"',
|
|
56
|
+
// Documented: "when a key appears in permissions.json, it fully replaces the in-app allowlist
|
|
57
|
+
// for that type". Merging keeps everything already in the FILE; it cannot restore what was only
|
|
58
|
+
// ever clicked in the UI, so the user is told.
|
|
59
|
+
supersedesInApp: true,
|
|
60
|
+
grant: (c) => addToList(c, null, 'mcpAllowlist', `${RETICLE_KEY}:*`),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'antigravity',
|
|
64
|
+
name: 'Antigravity',
|
|
65
|
+
paths: home('.gemini/antigravity-cli/settings.json'),
|
|
66
|
+
markers: [
|
|
67
|
+
{
|
|
68
|
+
darwin: 'Library/Application Support/Antigravity',
|
|
69
|
+
linux: '.config/Antigravity',
|
|
70
|
+
win32: 'AppData/Roaming/Antigravity',
|
|
71
|
+
},
|
|
72
|
+
home('.gemini/antigravity-cli'),
|
|
73
|
+
],
|
|
74
|
+
rule: 'permissions.allow += "mcp(reticle/*)"',
|
|
75
|
+
grant: (c) => addToList(c, 'permissions', 'allow', `mcp(${RETICLE_KEY}/*)`),
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'gemini-cli',
|
|
79
|
+
name: 'Gemini CLI',
|
|
80
|
+
paths: home('.gemini/settings.json'),
|
|
81
|
+
markers: [home('.gemini')],
|
|
82
|
+
rule: 'mcpServers.reticle.trust = true',
|
|
83
|
+
// Gemini has no allowlist: trust is a property of the server entry itself, so this reaches into
|
|
84
|
+
// the entry init already wrote rather than adding a rule beside it.
|
|
85
|
+
grant: (c) => {
|
|
86
|
+
const servers = 'object' === typeof c['mcpServers'] && null !== c['mcpServers']
|
|
87
|
+
? { ...c['mcpServers'] }
|
|
88
|
+
: {};
|
|
89
|
+
const entry = 'object' === typeof servers[RETICLE_KEY] && null !== servers[RETICLE_KEY]
|
|
90
|
+
? servers[RETICLE_KEY]
|
|
91
|
+
: {};
|
|
92
|
+
return { ...c, mcpServers: { ...servers, [RETICLE_KEY]: { ...entry, trust: true } } };
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
export const ApprovalOutcome = {
|
|
97
|
+
GRANTED: 'granted',
|
|
98
|
+
ALREADY: 'already',
|
|
99
|
+
ABSENT: 'absent',
|
|
100
|
+
/** Deliberately left for an explicit run, because doing it unattended costs the user something. */
|
|
101
|
+
DEFERRED: 'deferred',
|
|
102
|
+
FAILED: 'failed',
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Pre-approve Reticle's tools everywhere the machine has an agent that would otherwise ask.
|
|
106
|
+
*
|
|
107
|
+
* A client that cannot be written is reported, never thrown: one unwritable settings file is not a
|
|
108
|
+
* reason to leave the other three prompting.
|
|
109
|
+
*/
|
|
110
|
+
export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS, options = {}) {
|
|
111
|
+
const join = joinFor(where.platform);
|
|
112
|
+
return grants.map((grant) => {
|
|
113
|
+
const file = join(where.home, grant.paths[where.platform]);
|
|
114
|
+
const base = { id: grant.id, name: grant.name, file, rule: grant.rule };
|
|
115
|
+
const installed = grant.markers.some((m) => io.exists(join(where.home, m[where.platform])));
|
|
116
|
+
if (!installed)
|
|
117
|
+
return { ...base, outcome: ApprovalOutcome.ABSENT };
|
|
118
|
+
try {
|
|
119
|
+
const existed = io.exists(file);
|
|
120
|
+
if (true === options.onlyIfNoSupersede && true === grant.supersedesInApp && !existed) {
|
|
121
|
+
return {
|
|
122
|
+
...base,
|
|
123
|
+
outcome: ApprovalOutcome.DEFERRED,
|
|
124
|
+
warn: `creating ${file} would supersede what you approved inside ${grant.name}, which is not something to do unannounced. Run: npx @reticlehq/server init --files-only`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// A settings file we cannot parse is left exactly as it is. Reformatting somebody's config to
|
|
128
|
+
// add one line is a worse outcome than one dialog they have to click.
|
|
129
|
+
const current = existed
|
|
130
|
+
? JSON.parse(io.readFile(file))
|
|
131
|
+
: {};
|
|
132
|
+
const next = grant.grant(current);
|
|
133
|
+
if (next === current)
|
|
134
|
+
return { ...base, outcome: ApprovalOutcome.ALREADY };
|
|
135
|
+
io.mkdirp(file.slice(0, Math.max(0, file.lastIndexOf('/'))));
|
|
136
|
+
io.writeFile(file, `${JSON.stringify(next, null, INDENT)}\n`);
|
|
137
|
+
const warn = true === grant.supersedesInApp && !existed
|
|
138
|
+
? `${file} now governs which MCP tools run without asking; anything you had approved inside ${grant.name} itself will ask again until it is listed here too`
|
|
139
|
+
: undefined;
|
|
140
|
+
return { ...base, outcome: ApprovalOutcome.GRANTED, ...(undefined === warn ? {} : { warn }) };
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
return {
|
|
144
|
+
...base,
|
|
145
|
+
outcome: ApprovalOutcome.FAILED,
|
|
146
|
+
warn: String(err?.message ?? err).slice(0, 120),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge port, checked before the connect wait.
|
|
3
|
+
*
|
|
4
|
+
* The bridge is the channel between the daemon and the SDK in the page. While something else holds
|
|
5
|
+
* that port no session can ever appear, however correct the instrumentation is — so a run that goes
|
|
6
|
+
* ahead spends its entire connect budget and then reports what looks like a wiring problem, sending
|
|
7
|
+
* the reader to the one place that is fine.
|
|
8
|
+
*
|
|
9
|
+
* `serve` refuses a held port too, in prose written for somebody starting a daemon. This is the same
|
|
10
|
+
* fact told to somebody installing, and it has to say what the bridge IS: that sentence is what
|
|
11
|
+
* makes "no session appeared" mean something.
|
|
12
|
+
*/
|
|
13
|
+
import { PortPresence } from '../daemon/port-presence.js';
|
|
14
|
+
/** The refusal to print, or undefined when the port is usable. */
|
|
15
|
+
export declare function bridgeOccupied(presence: PortPresence, port: number): string | undefined;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge port, checked before the connect wait.
|
|
3
|
+
*
|
|
4
|
+
* The bridge is the channel between the daemon and the SDK in the page. While something else holds
|
|
5
|
+
* that port no session can ever appear, however correct the instrumentation is — so a run that goes
|
|
6
|
+
* ahead spends its entire connect budget and then reports what looks like a wiring problem, sending
|
|
7
|
+
* the reader to the one place that is fine.
|
|
8
|
+
*
|
|
9
|
+
* `serve` refuses a held port too, in prose written for somebody starting a daemon. This is the same
|
|
10
|
+
* fact told to somebody installing, and it has to say what the bridge IS: that sentence is what
|
|
11
|
+
* makes "no session appeared" mean something.
|
|
12
|
+
*/
|
|
13
|
+
import { PortPresence } from '../daemon/port-presence.js';
|
|
14
|
+
/** The refusal to print, or undefined when the port is usable. */
|
|
15
|
+
export function bridgeOccupied(presence, port) {
|
|
16
|
+
// DAEMON is the ordinary case — that is the daemon init is about to use. FREE is fine too: one
|
|
17
|
+
// gets started. Only a stranger is fatal.
|
|
18
|
+
if (PortPresence.FOREIGN !== presence)
|
|
19
|
+
return undefined;
|
|
20
|
+
return (`port ${String(port)} is held by something that is not a Reticle daemon. That is the bridge — ` +
|
|
21
|
+
'the channel between the daemon and the SDK in your page — so no session can ever appear while ' +
|
|
22
|
+
'it is occupied, however correct the wiring is. Free it, or re-run with --port <n> and set the ' +
|
|
23
|
+
'same port in .reticle.json.');
|
|
24
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a desktop app differs from a web app, for the phases that run after the files are written.
|
|
3
|
+
*
|
|
4
|
+
* `init` already understands Electron and Tauri when it writes files: desktop-doctor.ts checks the
|
|
5
|
+
* Tauri CSP for a blocked bridge or IPC, the Electron preload, the capture helper. The runtime
|
|
6
|
+
* phases did not, and they made three web-shaped assumptions that are wrong here — one of them
|
|
7
|
+
* actively harmful.
|
|
8
|
+
*
|
|
9
|
+
* 1. **Do not open a browser.** The app's own window IS the client. Opening a tab creates a SECOND
|
|
10
|
+
* session that is not the app, and the session picker would then have to choose between them —
|
|
11
|
+
* which is exactly the stale-tab false green, arranged deliberately.
|
|
12
|
+
* 2. **Do not wait for an HTTP port.** Tauri serves `tauri://localhost` (`http://tauri.localhost`
|
|
13
|
+
* on Windows) from inside the webview; there is nothing for `fetch` to probe. Electron under
|
|
14
|
+
* electron-vite does serve a renderer over HTTP, but the window is what matters, not the port.
|
|
15
|
+
* 3. **Wait for the app to launch instead.** `tauri dev` builds Rust before a window appears, which
|
|
16
|
+
* is minutes on a cold cache, so the budget has to be the app's, not a web app's.
|
|
17
|
+
*/
|
|
18
|
+
/** What kind of thing setup is about to run. */
|
|
19
|
+
export declare const AppShape: {
|
|
20
|
+
readonly WEB: "web";
|
|
21
|
+
readonly ELECTRON: "electron";
|
|
22
|
+
readonly TAURI: "tauri";
|
|
23
|
+
};
|
|
24
|
+
export type AppShape = (typeof AppShape)[keyof typeof AppShape];
|
|
25
|
+
export declare const isDesktop: (shape: AppShape) => boolean;
|
|
26
|
+
/** Files and dependencies that name the shell, read through an injected reader. */
|
|
27
|
+
export interface ShapeEvidence {
|
|
28
|
+
/** `src-tauri/tauri.conf.json` exists. */
|
|
29
|
+
readonly hasTauriConf: boolean;
|
|
30
|
+
/** `electron` is a dependency or devDependency. */
|
|
31
|
+
readonly hasElectronDep: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Which shell this is.
|
|
35
|
+
*
|
|
36
|
+
* Tauri wins when both are present: a project with a Tauri config is a Tauri app whatever else it
|
|
37
|
+
* has installed, and electron sometimes appears in a monorepo's root devDependencies.
|
|
38
|
+
*/
|
|
39
|
+
export declare function readShape(evidence: ShapeEvidence): AppShape;
|
|
40
|
+
export declare function isDesktopOrigin(url: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* How the phases should behave for this shape.
|
|
43
|
+
*
|
|
44
|
+
* Returned as data rather than branched at each call site, so the differences are visible in one
|
|
45
|
+
* place and testable without a desktop runtime.
|
|
46
|
+
*/
|
|
47
|
+
export interface ShapePolicy {
|
|
48
|
+
/** Open a browser at the url. False for desktop: the app window is already the client. */
|
|
49
|
+
readonly openBrowser: boolean;
|
|
50
|
+
/** Require an HTTP response before looking for a session. */
|
|
51
|
+
readonly requireHttpReady: boolean;
|
|
52
|
+
/** How long the app gets to produce a session, which for Tauri includes a Rust build. */
|
|
53
|
+
readonly connectBudgetMs: number;
|
|
54
|
+
/** Said out loud, because a desktop run looks stuck if nobody explains the wait. */
|
|
55
|
+
readonly note: string | undefined;
|
|
56
|
+
}
|
|
57
|
+
export declare function policyFor(shape: AppShape): ShapePolicy;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a desktop app differs from a web app, for the phases that run after the files are written.
|
|
3
|
+
*
|
|
4
|
+
* `init` already understands Electron and Tauri when it writes files: desktop-doctor.ts checks the
|
|
5
|
+
* Tauri CSP for a blocked bridge or IPC, the Electron preload, the capture helper. The runtime
|
|
6
|
+
* phases did not, and they made three web-shaped assumptions that are wrong here — one of them
|
|
7
|
+
* actively harmful.
|
|
8
|
+
*
|
|
9
|
+
* 1. **Do not open a browser.** The app's own window IS the client. Opening a tab creates a SECOND
|
|
10
|
+
* session that is not the app, and the session picker would then have to choose between them —
|
|
11
|
+
* which is exactly the stale-tab false green, arranged deliberately.
|
|
12
|
+
* 2. **Do not wait for an HTTP port.** Tauri serves `tauri://localhost` (`http://tauri.localhost`
|
|
13
|
+
* on Windows) from inside the webview; there is nothing for `fetch` to probe. Electron under
|
|
14
|
+
* electron-vite does serve a renderer over HTTP, but the window is what matters, not the port.
|
|
15
|
+
* 3. **Wait for the app to launch instead.** `tauri dev` builds Rust before a window appears, which
|
|
16
|
+
* is minutes on a cold cache, so the budget has to be the app's, not a web app's.
|
|
17
|
+
*/
|
|
18
|
+
/** What kind of thing setup is about to run. */
|
|
19
|
+
export const AppShape = {
|
|
20
|
+
WEB: 'web',
|
|
21
|
+
ELECTRON: 'electron',
|
|
22
|
+
TAURI: 'tauri',
|
|
23
|
+
};
|
|
24
|
+
export const isDesktop = (shape) => AppShape.WEB !== shape;
|
|
25
|
+
/**
|
|
26
|
+
* Which shell this is.
|
|
27
|
+
*
|
|
28
|
+
* Tauri wins when both are present: a project with a Tauri config is a Tauri app whatever else it
|
|
29
|
+
* has installed, and electron sometimes appears in a monorepo's root devDependencies.
|
|
30
|
+
*/
|
|
31
|
+
export function readShape(evidence) {
|
|
32
|
+
if (evidence.hasTauriConf)
|
|
33
|
+
return AppShape.TAURI;
|
|
34
|
+
if (evidence.hasElectronDep)
|
|
35
|
+
return AppShape.ELECTRON;
|
|
36
|
+
return AppShape.WEB;
|
|
37
|
+
}
|
|
38
|
+
/** A Tauri webview's own origin, which no HTTP probe from outside can reach. */
|
|
39
|
+
const TAURI_ORIGINS = ['tauri://localhost', 'http://tauri.localhost'];
|
|
40
|
+
export function isDesktopOrigin(url) {
|
|
41
|
+
return TAURI_ORIGINS.some((origin) => url.startsWith(origin));
|
|
42
|
+
}
|
|
43
|
+
const WEB_CONNECT_MS = 120_000;
|
|
44
|
+
/** A cold `tauri dev` compiles the Rust side before a window exists. */
|
|
45
|
+
const DESKTOP_CONNECT_MS = 10 * 60_000;
|
|
46
|
+
export function policyFor(shape) {
|
|
47
|
+
if (AppShape.WEB === shape) {
|
|
48
|
+
return {
|
|
49
|
+
openBrowser: true,
|
|
50
|
+
requireHttpReady: true,
|
|
51
|
+
connectBudgetMs: WEB_CONNECT_MS,
|
|
52
|
+
note: undefined,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
openBrowser: false,
|
|
57
|
+
requireHttpReady: false,
|
|
58
|
+
connectBudgetMs: DESKTOP_CONNECT_MS,
|
|
59
|
+
note: AppShape.TAURI === shape
|
|
60
|
+
? 'Tauri app: not opening a browser, because its own window is the client. A cold `tauri dev` builds the Rust side first, so the window can take minutes to appear.'
|
|
61
|
+
: 'Electron app: not opening a browser, because its own window is the client. Waiting for the app to launch and dial in.',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deciding when a dev server we started is up, hung, or gone.
|
|
3
|
+
*
|
|
4
|
+
* Nothing else in the package owns a spawned dev server, so this is the one genuinely new decision
|
|
5
|
+
* setup contributes. All three cases below were failures in the field:
|
|
6
|
+
*
|
|
7
|
+
* - a url in the output is an ANNOUNCEMENT, not readiness. Next prints `- Local: …` before it can
|
|
8
|
+
* serve, so a tab opened on that line lands on a 404 and the whole connect budget is then spent
|
|
9
|
+
* blaming the SDK for a server that had not finished starting.
|
|
10
|
+
* - a launcher that EXITS is not necessarily a dead server. `astro dev` forks the real one, prints
|
|
11
|
+
* the url and returns, which was read as death and failed Astro every time.
|
|
12
|
+
* - a fixed budget is the wrong instrument for "is this hung?". The heaviest real monorepo builds
|
|
13
|
+
* two shared packages before its app serves and was measured taking twenty minutes, while a
|
|
14
|
+
* genuinely wedged server writes nothing at all and gets exactly the same budget.
|
|
15
|
+
*/
|
|
16
|
+
/** Everything the decision needs, gathered by the caller. */
|
|
17
|
+
export interface WaitFacts {
|
|
18
|
+
/** The dev server's own output so far. */
|
|
19
|
+
readonly output: string;
|
|
20
|
+
/** Whether the process we spawned has exited. */
|
|
21
|
+
readonly launcherExited: boolean;
|
|
22
|
+
/** Whether the url we are watching answers. */
|
|
23
|
+
readonly serving: boolean;
|
|
24
|
+
/** Milliseconds since the dev server last wrote anything. */
|
|
25
|
+
readonly quietForMs: number;
|
|
26
|
+
/** Milliseconds since the wait began. */
|
|
27
|
+
readonly elapsedMs: number;
|
|
28
|
+
}
|
|
29
|
+
export declare const WaitVerdict: {
|
|
30
|
+
/** It is up. Carry on. */
|
|
31
|
+
readonly READY: "ready";
|
|
32
|
+
/** Still building or still starting; keep waiting. */
|
|
33
|
+
readonly WAITING: "waiting";
|
|
34
|
+
/** The launcher exited and nothing it started is answering. */
|
|
35
|
+
readonly DEAD: "dead";
|
|
36
|
+
/** It has gone silent without ever serving. */
|
|
37
|
+
readonly HUNG: "hung";
|
|
38
|
+
};
|
|
39
|
+
export type WaitVerdict = (typeof WaitVerdict)[keyof typeof WaitVerdict];
|
|
40
|
+
/** Silence for this long, with nothing served, means hung rather than busy. */
|
|
41
|
+
export declare const QUIET_MEANS_HUNG_MS = 45000;
|
|
42
|
+
/** Even a talkative dev server has had enough rope by here. */
|
|
43
|
+
export declare const WAIT_CEILING_MS: number;
|
|
44
|
+
/**
|
|
45
|
+
* Whether to keep waiting.
|
|
46
|
+
*
|
|
47
|
+
* `serving` outranks everything: a launcher that exited while the port answers has DAEMONIZED, and
|
|
48
|
+
* that is a live server whatever its parent did.
|
|
49
|
+
*/
|
|
50
|
+
export declare function judgeWait(facts: WaitFacts): WaitVerdict;
|
|
51
|
+
export declare function announcedUrl(output: string): string | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* The url to watch: whatever the tool announced, else one composed from a port we OBSERVED our own
|
|
54
|
+
* process group binding.
|
|
55
|
+
*
|
|
56
|
+
* The second is not a guess. `react-scripts` prints no parseable url outside a tty, so a CRA app
|
|
57
|
+
* compiled successfully, served happily, and setup timed out insisting no url appeared — the
|
|
58
|
+
* evidence there is the process we spawned, not a convention.
|
|
59
|
+
*/
|
|
60
|
+
export declare function urlToWatch(output: string, observedPorts: readonly number[]): string | undefined;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deciding when a dev server we started is up, hung, or gone.
|
|
3
|
+
*
|
|
4
|
+
* Nothing else in the package owns a spawned dev server, so this is the one genuinely new decision
|
|
5
|
+
* setup contributes. All three cases below were failures in the field:
|
|
6
|
+
*
|
|
7
|
+
* - a url in the output is an ANNOUNCEMENT, not readiness. Next prints `- Local: …` before it can
|
|
8
|
+
* serve, so a tab opened on that line lands on a 404 and the whole connect budget is then spent
|
|
9
|
+
* blaming the SDK for a server that had not finished starting.
|
|
10
|
+
* - a launcher that EXITS is not necessarily a dead server. `astro dev` forks the real one, prints
|
|
11
|
+
* the url and returns, which was read as death and failed Astro every time.
|
|
12
|
+
* - a fixed budget is the wrong instrument for "is this hung?". The heaviest real monorepo builds
|
|
13
|
+
* two shared packages before its app serves and was measured taking twenty minutes, while a
|
|
14
|
+
* genuinely wedged server writes nothing at all and gets exactly the same budget.
|
|
15
|
+
*/
|
|
16
|
+
export const WaitVerdict = {
|
|
17
|
+
/** It is up. Carry on. */
|
|
18
|
+
READY: 'ready',
|
|
19
|
+
/** Still building or still starting; keep waiting. */
|
|
20
|
+
WAITING: 'waiting',
|
|
21
|
+
/** The launcher exited and nothing it started is answering. */
|
|
22
|
+
DEAD: 'dead',
|
|
23
|
+
/** It has gone silent without ever serving. */
|
|
24
|
+
HUNG: 'hung',
|
|
25
|
+
};
|
|
26
|
+
/** Silence for this long, with nothing served, means hung rather than busy. */
|
|
27
|
+
export const QUIET_MEANS_HUNG_MS = 45_000;
|
|
28
|
+
/** Even a talkative dev server has had enough rope by here. */
|
|
29
|
+
export const WAIT_CEILING_MS = 25 * 60_000;
|
|
30
|
+
/**
|
|
31
|
+
* Whether to keep waiting.
|
|
32
|
+
*
|
|
33
|
+
* `serving` outranks everything: a launcher that exited while the port answers has DAEMONIZED, and
|
|
34
|
+
* that is a live server whatever its parent did.
|
|
35
|
+
*/
|
|
36
|
+
export function judgeWait(facts) {
|
|
37
|
+
if (facts.serving)
|
|
38
|
+
return WaitVerdict.READY;
|
|
39
|
+
if (facts.launcherExited)
|
|
40
|
+
return WaitVerdict.DEAD;
|
|
41
|
+
if (facts.elapsedMs >= WAIT_CEILING_MS)
|
|
42
|
+
return WaitVerdict.HUNG;
|
|
43
|
+
// Output IS progress. Silence is what distinguishes a build from a wedge.
|
|
44
|
+
return facts.quietForMs >= QUIET_MEANS_HUNG_MS ? WaitVerdict.HUNG : WaitVerdict.WAITING;
|
|
45
|
+
}
|
|
46
|
+
/** A url the dev server announced, if it has announced one. Never composed, only read. */
|
|
47
|
+
const URL_IN_OUTPUT = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]):\d+/;
|
|
48
|
+
export function announcedUrl(output) {
|
|
49
|
+
return URL_IN_OUTPUT.exec(output)?.[0];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The url to watch: whatever the tool announced, else one composed from a port we OBSERVED our own
|
|
53
|
+
* process group binding.
|
|
54
|
+
*
|
|
55
|
+
* The second is not a guess. `react-scripts` prints no parseable url outside a tty, so a CRA app
|
|
56
|
+
* compiled successfully, served happily, and setup timed out insisting no url appeared — the
|
|
57
|
+
* evidence there is the process we spawned, not a convention.
|
|
58
|
+
*/
|
|
59
|
+
export function urlToWatch(output, observedPorts) {
|
|
60
|
+
const announced = announcedUrl(output);
|
|
61
|
+
if (undefined !== announced)
|
|
62
|
+
return announced;
|
|
63
|
+
const port = observedPorts[0];
|
|
64
|
+
return undefined === port ? undefined : `http://localhost:${port}`;
|
|
65
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asking a child agent to drive the app, and reading what it says back.
|
|
3
|
+
*
|
|
4
|
+
* The drive needs a model, and the model does not need to be the caller. A client reads its MCP
|
|
5
|
+
* server list once at startup and cannot reload it, so an agent that has just run `init` does not
|
|
6
|
+
* yet have the reticle_* tools — which is why the hand-driven procedure asks for a restart, and why
|
|
7
|
+
* three runs in five ended there having shown the user nothing. A child process started after init
|
|
8
|
+
* reads the list init just wrote. No restart, no lost context, no waiting on a human.
|
|
9
|
+
*
|
|
10
|
+
* Two details here are not stylistic, and both cost a real run to learn.
|
|
11
|
+
*/
|
|
12
|
+
import { type DriverSpec } from './drive-plan.js';
|
|
13
|
+
/** Ten minutes. Long, because the drive is a model working, and bounded, because it can wedge. */
|
|
14
|
+
export declare const DRIVE_TIMEOUT_MS: number;
|
|
15
|
+
export interface DriveRequest {
|
|
16
|
+
readonly url: string;
|
|
17
|
+
readonly sessionId: string;
|
|
18
|
+
/** The journey to drive, in the caller's own words. Absent means the child picks one. */
|
|
19
|
+
readonly flow?: string | undefined;
|
|
20
|
+
/** The capabilities file, when the session says it was never finished. */
|
|
21
|
+
readonly unfinishedCapabilitiesFile?: string | undefined;
|
|
22
|
+
/** The tab is hidden or throttled, so the child should not blame the app for a frozen page. */
|
|
23
|
+
readonly tabThrottled: boolean;
|
|
24
|
+
readonly budgetUsd: number;
|
|
25
|
+
readonly model?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
export interface DriveReport {
|
|
28
|
+
/** The agent's own account, kept verbatim and never treated as a pass on its own. */
|
|
29
|
+
readonly text: string;
|
|
30
|
+
readonly grade?: string | undefined;
|
|
31
|
+
readonly turns?: number | undefined;
|
|
32
|
+
readonly costUsd?: number | undefined;
|
|
33
|
+
/** Set when the run was cut short, naming where it had got to. */
|
|
34
|
+
readonly incomplete?: string | undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Findings are the point, not the verdict.
|
|
38
|
+
*
|
|
39
|
+
* A green verdict demonstrates the mechanism. What people remember is Reticle telling them
|
|
40
|
+
* something they did not know — measured on a real app, a drive's own assertion passed and Reticle
|
|
41
|
+
* overruled it on a 504 fired inside the action window, which the drive then reported rather than
|
|
42
|
+
* explaining away. That only happens if the drive is asked for it: asked for a verdict alone, it
|
|
43
|
+
* returns a verdict alone, and everything else the page did goes unmentioned.
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* The task LEADS.
|
|
47
|
+
*
|
|
48
|
+
* Measured: with the situation first and a capabilities dump in the middle, a model answered "I
|
|
49
|
+
* don't see an actual task or request from you yet" and drove nothing — one turn, no verdict, and
|
|
50
|
+
* setup reported success. Context goes after the ask, never before it.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildDrivePrompt(req: DriveRequest): string;
|
|
53
|
+
/**
|
|
54
|
+
* NDJSON, one event per line.
|
|
55
|
+
*
|
|
56
|
+
* `--output-format json` emits nothing until the run completes, so a drive killed at the timeout
|
|
57
|
+
* left no trace of what it had been doing — measured four times on one app, every one reporting
|
|
58
|
+
* "the drive produced no output". Streaming turns that into evidence, because spawnSync hands back
|
|
59
|
+
* the stdout it collected before killing the child.
|
|
60
|
+
*/
|
|
61
|
+
export declare function readDriveOutput(out: string): DriveReport;
|
|
62
|
+
/**
|
|
63
|
+
* The prompt goes on STDIN.
|
|
64
|
+
*
|
|
65
|
+
* `--allowedTools` is variadic: with the prompt passed positionally after it, the flag swallowed
|
|
66
|
+
* the prompt as another tool name and the run exited in two seconds having done nothing, with the
|
|
67
|
+
* error on stderr — which the caller was discarding. No flag ordering is safe across four CLIs.
|
|
68
|
+
*/
|
|
69
|
+
export declare function driveWith(driver: DriverSpec, req: DriveRequest, cwd: string): DriveReport;
|
|
70
|
+
/** Everything that is not Claude Code reports prose, so the report IS its output. */
|
|
71
|
+
export declare function readPlainOutput(out: string): DriveReport;
|