@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
|
@@ -5,30 +5,232 @@
|
|
|
5
5
|
* Split out of act-tools.ts along its natural seam: the act tools care only about the outcome
|
|
6
6
|
* (`result` defined = it went native), never about provider availability, box resolution,
|
|
7
7
|
* drag-target inspection or the reason taxonomy.
|
|
8
|
+
*
|
|
9
|
+
* Also hosts `rewriteUploadArgs` — the daemon-side path that lets an agent name a file on disk
|
|
10
|
+
* and have its REAL bytes reach the browser's `<input type="file">`. The browser SDK cannot read
|
|
11
|
+
* the filesystem; the daemon can. The rewrite happens here, before the ACT command crosses the
|
|
12
|
+
* bridge, so the browser side sees a normal `{ content, name, type }` call and `assertUploadArgs`
|
|
13
|
+
* keeps its invariant (no fabricated bytes, no silently-dropped keys).
|
|
14
|
+
*
|
|
15
|
+
* Trust boundary: scoped to the project root (one level above `deps.reticleRoot`), resolved
|
|
16
|
+
* through `realpath` so symlinks cannot escape it. Sensitive files (.env*, .git/, *.pem, id_*,
|
|
17
|
+
* .npmrc) are denied with a message that says why. The cap is derived from the bridge's own
|
|
18
|
+
* MAX_MESSAGE_BYTES with the base64 4/3 inflation factor applied, so the encoded payload always
|
|
19
|
+
* fits in one WebSocket frame.
|
|
8
20
|
*/
|
|
9
|
-
import { ActionType, InputModeReason, ReticleCommand } from '@reticlehq/core';
|
|
10
|
-
import { isPointerAction } from '../input/real-input.js';
|
|
21
|
+
import { ActionType, InputModeReason, ReticleCommand, TRANSPORT_LIMITS } from '@reticlehq/core';
|
|
22
|
+
import { boxCenter, isPointerAction } from '../input/real-input.js';
|
|
11
23
|
import { assertDragNotDestructive, assertNotDestructive } from './act-danger.js';
|
|
12
24
|
import { NATIVE_INPUT_ARG } from '@reticlehq/core';
|
|
13
25
|
import { asString, asRecord } from './tools-helpers.js';
|
|
14
26
|
import { commandOrThrow } from './tool-kit.js';
|
|
15
27
|
import { asBox } from './act-helpers.js';
|
|
28
|
+
import { isAbsolute, join, relative, extname, basename } from 'node:path';
|
|
29
|
+
/**
|
|
30
|
+
* Minimal extension → MIME-type table for the file types agents most commonly upload.
|
|
31
|
+
* Falls back to `application/octet-stream` for anything not listed here — the browser and
|
|
32
|
+
* the receiving server both sniff the real type from the bytes anyway.
|
|
33
|
+
*/
|
|
34
|
+
const MIME_BY_EXT = {
|
|
35
|
+
'.pdf': 'application/pdf',
|
|
36
|
+
'.csv': 'text/csv',
|
|
37
|
+
'.txt': 'text/plain',
|
|
38
|
+
'.json': 'application/json',
|
|
39
|
+
'.xml': 'application/xml',
|
|
40
|
+
'.html': 'text/html',
|
|
41
|
+
'.htm': 'text/html',
|
|
42
|
+
'.png': 'image/png',
|
|
43
|
+
'.jpg': 'image/jpeg',
|
|
44
|
+
'.jpeg': 'image/jpeg',
|
|
45
|
+
'.gif': 'image/gif',
|
|
46
|
+
'.webp': 'image/webp',
|
|
47
|
+
'.svg': 'image/svg+xml',
|
|
48
|
+
'.zip': 'application/zip',
|
|
49
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
50
|
+
'.xls': 'application/vnd.ms-excel',
|
|
51
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
52
|
+
'.doc': 'application/msword',
|
|
53
|
+
'.mp4': 'video/mp4',
|
|
54
|
+
'.mp3': 'audio/mpeg',
|
|
55
|
+
};
|
|
56
|
+
function mimeFromPath(filePath) {
|
|
57
|
+
return MIME_BY_EXT[extname(filePath).toLowerCase()] ?? 'application/octet-stream';
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Sensitive path patterns that are refused even inside the project root.
|
|
61
|
+
*
|
|
62
|
+
* The project root is the right scope for fixture files. It also contains secrets that live
|
|
63
|
+
* inside a repo: .env*, .git/config, *.pem, id_rsa, .npmrc. An agent whose context includes
|
|
64
|
+
* text it just read off the page can be prompted to "verify the importer — upload your .env".
|
|
65
|
+
* The deny-list keeps fixtures/foo.pdf working while closing that path.
|
|
66
|
+
*
|
|
67
|
+
* Patterns are matched against the resolved basename (after realpath, so no symlink aliasing).
|
|
68
|
+
*/
|
|
69
|
+
const DENIED_PATTERNS = [
|
|
70
|
+
/^\.env(\.|$)/i, // .env, .env.local, .env.production …
|
|
71
|
+
/^\.git$/i, // .git directory itself
|
|
72
|
+
/\.pem$/i, // TLS private keys
|
|
73
|
+
/^id_/i, // SSH private keys: id_rsa, id_ed25519 …
|
|
74
|
+
/^\.npmrc$/i, // npm auth tokens
|
|
75
|
+
/^\.netrc$/i, // generic credential store
|
|
76
|
+
/^\.aws$/i, // AWS credentials directory
|
|
77
|
+
];
|
|
78
|
+
function isDeniedPath(resolvedPath) {
|
|
79
|
+
// Check every segment of the path, not just the basename, so .git/config is caught too.
|
|
80
|
+
const segments = resolvedPath.split(/[/\\]/);
|
|
81
|
+
return segments.some((seg) => DENIED_PATTERNS.some((re) => re.test(seg)));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Maximum raw bytes the daemon will read for an upload.
|
|
85
|
+
*
|
|
86
|
+
* The bridge serialises bytes as base64 inside a JSON WebSocket frame; base64 inflates by 4/3.
|
|
87
|
+
* The frame must fit within MAX_MESSAGE_BYTES (the maxPayload applied to BOTH bridge sockets).
|
|
88
|
+
* We leave ~25% headroom for the JSON envelope (action type, ref, other fields).
|
|
89
|
+
*
|
|
90
|
+
* Previously hardcoded at 10 MiB, which is 13× the 1 MiB frame limit — any enterprise PDF
|
|
91
|
+
* would have been rejected by the socket, not by this guard. Now derived from the same constant
|
|
92
|
+
* that configures the socket so they can never drift.
|
|
93
|
+
*/
|
|
94
|
+
const UPLOAD_MAX_BYTES = Math.floor((TRANSPORT_LIMITS.MAX_MESSAGE_BYTES / (4 / 3)) * 0.75);
|
|
95
|
+
/**
|
|
96
|
+
* Resolve and validate a caller-supplied upload path.
|
|
97
|
+
*
|
|
98
|
+
* 1. Resolve to absolute (join against project root for relative paths).
|
|
99
|
+
* 2. Call `realpath` to follow symlinks — `relative()` is lexical and a symlink inside the tree
|
|
100
|
+
* can point outside it; realpath is the only reliable check.
|
|
101
|
+
* 3. Confirm the real path is within the project root.
|
|
102
|
+
* 4. Confirm the path does not match the sensitive-file deny-list.
|
|
103
|
+
*
|
|
104
|
+
* Returns the resolved real path on success. Throws with a user-readable message on any violation.
|
|
105
|
+
*/
|
|
106
|
+
async function resolveUploadPath(rawPath, projectRoot, fs) {
|
|
107
|
+
const abs = isAbsolute(rawPath) ? rawPath : join(projectRoot, rawPath);
|
|
108
|
+
// realpath resolves symlinks; it also rejects ENOENT, so we get a clear missing-file error.
|
|
109
|
+
const real = await fs.realpath(abs).catch(() => {
|
|
110
|
+
throw new Error(`upload path '${rawPath}' could not be read: file not found or not accessible.`);
|
|
111
|
+
});
|
|
112
|
+
const rel = relative(projectRoot, real);
|
|
113
|
+
if (rel.startsWith('..') || isAbsolute(rel)) {
|
|
114
|
+
throw new Error(`upload path '${rawPath}' resolves to '${real}', which is outside the project root ` +
|
|
115
|
+
`'${projectRoot}' — only files within the project directory may be uploaded. ` +
|
|
116
|
+
'Use a path relative to the project root, or an absolute path inside it.');
|
|
117
|
+
}
|
|
118
|
+
if (isDeniedPath(real)) {
|
|
119
|
+
throw new Error(`upload path '${rawPath}' matches a sensitive-file pattern and cannot be read by the daemon. ` +
|
|
120
|
+
'Fixture files for upload tests should live in a dedicated directory (e.g. fixtures/).');
|
|
121
|
+
}
|
|
122
|
+
return real;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* If the action is `upload` AND the inner args carry `path`, read the file from disk and rewrite
|
|
126
|
+
* the args to `{ content, name, type, __base64: true }` before the ACT command reaches the browser.
|
|
127
|
+
*
|
|
128
|
+
* Returns the (possibly rewritten) args object. All other actions are returned unchanged.
|
|
129
|
+
*
|
|
130
|
+
* This is the SINGLE interception point — called from session.command() so flow replay, crawl,
|
|
131
|
+
* and every other ACT dispatch site are covered without per-site wiring.
|
|
132
|
+
*/
|
|
133
|
+
export async function rewriteUploadArgs(deps, action, innerArgs) {
|
|
134
|
+
if (action !== ActionType.UPLOAD)
|
|
135
|
+
return innerArgs;
|
|
136
|
+
const rawPath = asString(innerArgs['path']);
|
|
137
|
+
if (rawPath === undefined)
|
|
138
|
+
return innerArgs; // no path → browser handles as inline upload
|
|
139
|
+
// Project root is one level above .reticle/
|
|
140
|
+
const projectRoot = join(deps.reticleRoot, '..');
|
|
141
|
+
// Blocker 4 fix: stat FIRST, before allocating read buffer, so a huge file fails fast.
|
|
142
|
+
const realPath = await resolveUploadPath(rawPath, projectRoot, deps.fs);
|
|
143
|
+
const fileStat = await deps.fs.stat(realPath).catch(() => {
|
|
144
|
+
throw new Error(`upload path '${rawPath}' could not be read: file not found or not accessible.`);
|
|
145
|
+
});
|
|
146
|
+
// Blocker 2 fix: cap is now derived from TRANSPORT_LIMITS so it can never exceed the socket limit.
|
|
147
|
+
if (fileStat.size > UPLOAD_MAX_BYTES) {
|
|
148
|
+
throw new Error(`upload path '${rawPath}' is ${fileStat.size} bytes, which exceeds the ` +
|
|
149
|
+
`${UPLOAD_MAX_BYTES} byte upload limit (bridge frame cap). ` +
|
|
150
|
+
'Split the file or pass its bytes as args.content directly.');
|
|
151
|
+
}
|
|
152
|
+
const bytes = await deps.fs.readFileBytes(realPath).catch(() => {
|
|
153
|
+
throw new Error(`upload path '${rawPath}' could not be read: file not found or not accessible.`);
|
|
154
|
+
});
|
|
155
|
+
// Encode as base-64 so the bytes survive JSON serialisation across the bridge.
|
|
156
|
+
const content = Buffer.from(bytes).toString('base64');
|
|
157
|
+
// Infer MIME type from the file extension; fall back to octet-stream if unknown.
|
|
158
|
+
const callerType = asString(innerArgs['type']);
|
|
159
|
+
const type = callerType ?? mimeFromPath(realPath);
|
|
160
|
+
// Default filename to the basename of the REAL path (post-symlink-resolution).
|
|
161
|
+
const callerName = asString(innerArgs['name']);
|
|
162
|
+
const name = callerName ?? basename(realPath);
|
|
163
|
+
// Strip 'path' — the browser does not know it; assertUploadArgs would refuse it.
|
|
164
|
+
// __base64: true tells the browser-side dispatch to decode content before File construction.
|
|
165
|
+
const { path: _dropped, name: _n, type: _t, ...rest } = innerArgs;
|
|
166
|
+
return { ...rest, content, name, type, __base64: true };
|
|
167
|
+
}
|
|
16
168
|
/** Synthetic outcome with a diagnostic reason (provider configured but native input skipped). */
|
|
17
169
|
function synthetic(reason) {
|
|
18
170
|
return reason === undefined
|
|
19
171
|
? { result: undefined, settled: false }
|
|
20
172
|
: { result: undefined, settled: false, reason };
|
|
21
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Hover without a native pointer is a false success: synthetic mouseover reports dispatched and
|
|
176
|
+
* settled while CSS `:hover` never applies. Same refusal shape as contenteditable — name the gap
|
|
177
|
+
* rather than pretend the action ran.
|
|
178
|
+
*/
|
|
179
|
+
export const HOVER_NEEDS_POINTER_MSG = 'cannot hover without a real pointer — CSS :hover only applies to a native mouse move, never to a synthetic mouseover';
|
|
180
|
+
function hoverSucceeded(center) {
|
|
181
|
+
return { result: { performed: true, center, action: ActionType.HOVER }, settled: true };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Drive hover through a real pointer (CDP provider, then a leased Playwright page), or refuse.
|
|
185
|
+
*
|
|
186
|
+
* A leased tab already owns Chromium — the pool can move the mouse even when `reticle drive` /
|
|
187
|
+
* `RETICLE_CDP_URL` never configured a provider. Falling through to synthetic dispatch is the
|
|
188
|
+
* bug: the tool reports done and the styles never ran.
|
|
189
|
+
*/
|
|
190
|
+
async function hoverForReal(deps, session, ref, inner, provider) {
|
|
191
|
+
const providerReady = provider !== undefined && (await provider.isAvailableFor(session.url));
|
|
192
|
+
if (!providerReady && deps.pool === undefined) {
|
|
193
|
+
throw new Error(HOVER_NEEDS_POINTER_MSG);
|
|
194
|
+
}
|
|
195
|
+
const inspected = await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, { ref });
|
|
196
|
+
assertNotDestructive(ActionType.HOVER, inner, inspected);
|
|
197
|
+
const box = asBox(inspected);
|
|
198
|
+
if (box === undefined) {
|
|
199
|
+
throw new Error(HOVER_NEEDS_POINTER_MSG);
|
|
200
|
+
}
|
|
201
|
+
if (providerReady && provider !== undefined) {
|
|
202
|
+
try {
|
|
203
|
+
const performed = await provider.perform(session.url, ActionType.HOVER, box, {});
|
|
204
|
+
if (performed.performed)
|
|
205
|
+
return hoverSucceeded(performed.center);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// A provider error used to fall back to synthetic dispatch. For hover that is a lie.
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const { cx, cy } = boxCenter(box);
|
|
212
|
+
if (true === (await deps.pool?.hoverLease(session.id, cx, cy))) {
|
|
213
|
+
return hoverSucceeded({ cx, cy });
|
|
214
|
+
}
|
|
215
|
+
throw new Error(HOVER_NEEDS_POINTER_MSG);
|
|
216
|
+
}
|
|
22
217
|
/**
|
|
23
218
|
* Attempt to drive a pointer action via native input. Returns a synthetic outcome (with a
|
|
24
219
|
* `reason` when a provider is configured) whenever the synthetic path should run — no matching
|
|
25
220
|
* page, unresolvable box, declined, etc. A throw inside the provider becomes a synthetic fallback
|
|
26
221
|
* flagged with `fellBack`. `result` is defined only on a real success.
|
|
222
|
+
*
|
|
223
|
+
* Hover is the exception: it never returns synthetic. CSS `:hover` is not applied by
|
|
224
|
+
* `dispatchEvent`, so a synthetic success is the defect. The call either drives a real pointer
|
|
225
|
+
* or throws.
|
|
27
226
|
*/
|
|
28
227
|
export async function tryRealInput(deps, session, ref, action, args) {
|
|
29
228
|
const provider = deps.realInput;
|
|
30
229
|
const inner = asRecord(args['args']);
|
|
31
230
|
const askedForNative = true === inner[NATIVE_INPUT_ARG];
|
|
231
|
+
if (action === ActionType.HOVER) {
|
|
232
|
+
return hoverForReal(deps, session, ref, inner, provider);
|
|
233
|
+
}
|
|
32
234
|
if (provider === undefined) {
|
|
33
235
|
// Silent by default: with no provider EVERY action is synthetic, and a reason on all of them is
|
|
34
236
|
// noise on the most-used tool in the product. But an agent that passed native:true asked a
|
|
@@ -45,6 +247,12 @@ export async function tryRealInput(deps, session, ref, action, args) {
|
|
|
45
247
|
if ((action === ActionType.CLICK || action === ActionType.DBLCLICK) && !askedForNative) {
|
|
46
248
|
return synthetic(InputModeReason.SYNTHETIC_CLICK_PREFERRED);
|
|
47
249
|
}
|
|
250
|
+
// Under version skew CDP is unusable while DOM tools still work (#688). Surface the skew sentence
|
|
251
|
+
// rather than "page not correlated" / a silent provider-error fallback — those send the agent
|
|
252
|
+
// hunting a dead context that is not dead.
|
|
253
|
+
if (session.versionSkew !== undefined) {
|
|
254
|
+
throw new Error(session.versionSkew);
|
|
255
|
+
}
|
|
48
256
|
if (!(await provider.isAvailableFor(session.url)))
|
|
49
257
|
return synthetic(InputModeReason.PAGE_NOT_CORRELATED);
|
|
50
258
|
const inspected = await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, { ref });
|
|
@@ -83,6 +291,8 @@ export async function tryRealInput(deps, session, ref, action, args) {
|
|
|
83
291
|
return { result: { performed: true, center: performed.center, action }, settled: true };
|
|
84
292
|
}
|
|
85
293
|
catch {
|
|
294
|
+
if (session.versionSkew !== undefined)
|
|
295
|
+
throw new Error(session.versionSkew);
|
|
86
296
|
return {
|
|
87
297
|
result: undefined,
|
|
88
298
|
settled: false,
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { EventType, ReticleCommand, SnapshotMode } from '@reticlehq/core';
|
|
2
|
+
import { EventType, ReticleCommand, SnapshotMode, urlForMatch } from '@reticlehq/core';
|
|
3
3
|
import { ReticleTool } from './tool-names.js';
|
|
4
|
+
import { cursorSchema } from './numeric-bounds.js';
|
|
4
5
|
import { reconcile } from '../events/reconcile.js';
|
|
5
6
|
import { salvageJson } from '../events/json-salvage.js';
|
|
6
7
|
import { withControl } from '../session/control-envelope.js';
|
|
7
8
|
import { asNumber, asString } from './tools-helpers.js';
|
|
8
9
|
import { sessionIdShape, commandOrThrow } from './tool-kit.js';
|
|
10
|
+
import { readCompleteTree } from './complete-snapshot.js';
|
|
9
11
|
/**
|
|
10
12
|
* Does the screen agree with the data the app was given?
|
|
11
13
|
*
|
|
@@ -28,7 +30,7 @@ async function capturedBodies(session, since, urlContains) {
|
|
|
28
30
|
for (const event of events) {
|
|
29
31
|
if (event.type !== EventType.NET_REQUEST)
|
|
30
32
|
continue;
|
|
31
|
-
const url =
|
|
33
|
+
const url = urlForMatch(event.data);
|
|
32
34
|
if (urlContains !== undefined && !url.includes(urlContains))
|
|
33
35
|
continue;
|
|
34
36
|
total += 1;
|
|
@@ -54,8 +56,7 @@ export const RECONCILE_TOOLS = [
|
|
|
54
56
|
description: 'Compare what the API returned against what the page RENDERS. Catches the class no status code and no assertion can: a USD amount shown with a ₹ sign, a record the API calls `on_hold` displayed as "pending". Needs response bodies — enable `captureNetworkBodies` — and says so when they are missing rather than reporting a clean result over data it never read.',
|
|
55
57
|
inputSchema: {
|
|
56
58
|
...sessionIdShape,
|
|
57
|
-
since:
|
|
58
|
-
.number()
|
|
59
|
+
since: cursorSchema
|
|
59
60
|
.optional()
|
|
60
61
|
.describe('Cursor from a prior act — scopes to responses received after it. Default: 0.'),
|
|
61
62
|
urlContains: z
|
|
@@ -88,11 +89,24 @@ export const RECONCILE_TOOLS = [
|
|
|
88
89
|
: `${String(total)} response(s) matched but NONE carried a recorded body, so nothing was compared. Enable it where your app calls connect(): \`reticle.connect({ captureNetworkBodies: true })\`, then re-run`,
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
|
-
|
|
92
|
+
// The whole page, INCLUDING whatever the snapshot's node cap cut off. A comparison against a
|
|
93
|
+
// prefix of the page can miss the row that disagrees and can invent one that does not — the
|
|
94
|
+
// value it was looking for may simply sit past the cut.
|
|
95
|
+
const page = await readCompleteTree((scope) => commandOrThrow(deps, session.id, ReticleCommand.SNAPSHOT, {
|
|
92
96
|
mode: SnapshotMode.FULL,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const mismatches = reconcile(bodies,
|
|
97
|
+
...(scope === undefined ? {} : { scope, includeRoot: true }),
|
|
98
|
+
}));
|
|
99
|
+
const mismatches = reconcile(bodies, page.tree);
|
|
100
|
+
// ASYMMETRIC on purpose. A mismatch FOUND in a partial page is still found — one witness proves
|
|
101
|
+
// presence, and withholding it because the rest of the page was unread helps nobody. An EMPTY
|
|
102
|
+
// result is the universal claim "nothing anywhere disagrees", and a partial page cannot support
|
|
103
|
+
// it. So the positive keeps working and only the negative is refused.
|
|
104
|
+
if (0 === mismatches.length && !page.complete) {
|
|
105
|
+
throw new Error(`the page could not be read completely — ${page.incompleteBecause ?? 'the snapshot was cut'}. ` +
|
|
106
|
+
'Nothing was found to disagree with the API, but that is not the same as nothing disagreeing: ' +
|
|
107
|
+
'the record that does could be in the part that was never read. Narrow the page (close overlays, ' +
|
|
108
|
+
'navigate to the view holding these records) and re-run.');
|
|
109
|
+
}
|
|
96
110
|
// A truncated body is declared whether or not anything was found in it: "no mismatches" over a
|
|
97
111
|
// partially-read payload is a weaker statement than over a whole one, and the difference is
|
|
98
112
|
// exactly the kind of omission this layer exists to refuse.
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/** One candidate the browser returned for a target query. */
|
|
2
|
-
export interface TargetCandidate {
|
|
3
|
-
ref?: unknown;
|
|
4
|
-
role?: unknown;
|
|
5
|
-
name?: unknown;
|
|
6
|
-
visible?: unknown;
|
|
7
|
-
}
|
|
8
1
|
/** What the resolution produced: a ref to act on, or the reason there isn't one. */
|
|
9
2
|
export type TargetResolution = {
|
|
10
3
|
readonly kind: 'ref';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Did the document this app served carry any Reticle at all?
|
|
3
|
+
*
|
|
4
|
+
* `LeaseEvidence.sdkMarker` was read in two places and set by nothing, so the clause that
|
|
5
|
+
* distinguishes "installed but not connecting" from "never installed" could never fire. That
|
|
6
|
+
* distinction is the whole difference between the diagnosis a new user needs and the four causes
|
|
7
|
+
* they were getting, every one of which presupposes the SDK is already there.
|
|
8
|
+
*
|
|
9
|
+
* The probe is a plain GET of the page that was just loaded, so it costs one localhost round trip on
|
|
10
|
+
* a path that has already failed and is about to print a paragraph.
|
|
11
|
+
*
|
|
12
|
+
* ## Every uncertainty resolves to silence, on purpose
|
|
13
|
+
*
|
|
14
|
+
* `true` requires the EXACT marker the Vite plugin injects into `<head>` of every served document —
|
|
15
|
+
* a constant this repo already owns, so there is no new contract and no guessing.
|
|
16
|
+
*
|
|
17
|
+
* `false` requires the served document to contain no `reticle` anywhere, in any case. That is the
|
|
18
|
+
* only evidence strong enough to say "this app ships no SDK", and it is deliberately harder to earn
|
|
19
|
+
* than `true` is: a false `false` accuses a working install, and a false `true` SUPPRESSES the one
|
|
20
|
+
* instruction an uninstrumented user needs. Both are worse than saying nothing.
|
|
21
|
+
*
|
|
22
|
+
* Anything else — a fetch that failed, a timeout, a document that mentions Reticle without the
|
|
23
|
+
* marker (a Next or Babel install, where the SDK arrives through a module and never touches the
|
|
24
|
+
* HTML) — returns `undefined`, and the hint says nothing about markers at all.
|
|
25
|
+
*/
|
|
26
|
+
/** Pure: the verdict for a document body we managed to read. Exported so the rule is testable. */
|
|
27
|
+
export declare function readSdkMarker(body: string): boolean | undefined;
|
|
28
|
+
/** Never throws, never hangs. A probe that can break the diagnosis is worse than no probe. */
|
|
29
|
+
export declare function probeSdkMarker(url: string): Promise<boolean | undefined>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Did the document this app served carry any Reticle at all?
|
|
3
|
+
*
|
|
4
|
+
* `LeaseEvidence.sdkMarker` was read in two places and set by nothing, so the clause that
|
|
5
|
+
* distinguishes "installed but not connecting" from "never installed" could never fire. That
|
|
6
|
+
* distinction is the whole difference between the diagnosis a new user needs and the four causes
|
|
7
|
+
* they were getting, every one of which presupposes the SDK is already there.
|
|
8
|
+
*
|
|
9
|
+
* The probe is a plain GET of the page that was just loaded, so it costs one localhost round trip on
|
|
10
|
+
* a path that has already failed and is about to print a paragraph.
|
|
11
|
+
*
|
|
12
|
+
* ## Every uncertainty resolves to silence, on purpose
|
|
13
|
+
*
|
|
14
|
+
* `true` requires the EXACT marker the Vite plugin injects into `<head>` of every served document —
|
|
15
|
+
* a constant this repo already owns, so there is no new contract and no guessing.
|
|
16
|
+
*
|
|
17
|
+
* `false` requires the served document to contain no `reticle` anywhere, in any case. That is the
|
|
18
|
+
* only evidence strong enough to say "this app ships no SDK", and it is deliberately harder to earn
|
|
19
|
+
* than `true` is: a false `false` accuses a working install, and a false `true` SUPPRESSES the one
|
|
20
|
+
* instruction an uninstrumented user needs. Both are worse than saying nothing.
|
|
21
|
+
*
|
|
22
|
+
* Anything else — a fetch that failed, a timeout, a document that mentions Reticle without the
|
|
23
|
+
* marker (a Next or Babel install, where the SDK arrives through a module and never touches the
|
|
24
|
+
* HTML) — returns `undefined`, and the hint says nothing about markers at all.
|
|
25
|
+
*/
|
|
26
|
+
import { RETICLE_RENDER_PREHOOK } from '@reticlehq/core';
|
|
27
|
+
/** A page that has not answered in this long is not going to inform a diagnosis. */
|
|
28
|
+
const PROBE_TIMEOUT_MS = 1_500;
|
|
29
|
+
/** Enough of the document to carry a head-prepended script; a whole SPA bundle is not wanted. */
|
|
30
|
+
const PROBE_MAX_BYTES = 200_000;
|
|
31
|
+
/** Pure: the verdict for a document body we managed to read. Exported so the rule is testable. */
|
|
32
|
+
export function readSdkMarker(body) {
|
|
33
|
+
if (body.includes(RETICLE_RENDER_PREHOOK))
|
|
34
|
+
return true;
|
|
35
|
+
// Case-insensitive and deliberately broad: ANY mention is enough to withhold the accusation.
|
|
36
|
+
if (!/reticle/i.test(body))
|
|
37
|
+
return false;
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/** Never throws, never hangs. A probe that can break the diagnosis is worse than no probe. */
|
|
41
|
+
export async function probeSdkMarker(url) {
|
|
42
|
+
const controller = new AbortController();
|
|
43
|
+
const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
46
|
+
if (!res.ok)
|
|
47
|
+
return undefined;
|
|
48
|
+
const body = (await res.text()).slice(0, PROBE_MAX_BYTES);
|
|
49
|
+
return readSdkMarker(body);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
}
|
|
57
|
+
}
|
package/dist/tools/tool-kit.d.ts
CHANGED
|
@@ -56,6 +56,18 @@ export interface ToolDeps<Ext = unknown> {
|
|
|
56
56
|
* exactly today's behaviour.
|
|
57
57
|
*/
|
|
58
58
|
artifactRootFor?: (projectId: string | undefined) => ArtifactRoot;
|
|
59
|
+
/**
|
|
60
|
+
* A verification run just landed — wake cloud sync instead of waiting for its next tick.
|
|
61
|
+
*
|
|
62
|
+
* The run itself is pushed immediately on this path, but everything AROUND it — the impact
|
|
63
|
+
* counters, the flows it exercised, flake and intent — moves only on the timer. That is what
|
|
64
|
+
* produced a dashboard showing a fresh run beside stale numbers, which reads as a sync bug and is
|
|
65
|
+
* really just two different clocks. Waking the loop here makes the run and its context arrive
|
|
66
|
+
* together.
|
|
67
|
+
*
|
|
68
|
+
* Optional: absent ⇒ the timer alone, which is exactly today's behaviour.
|
|
69
|
+
*/
|
|
70
|
+
onRunPersisted?: () => void;
|
|
59
71
|
/**
|
|
60
72
|
* This daemon's OWN project id, derived from the directory it was started in.
|
|
61
73
|
*
|
|
@@ -130,6 +142,18 @@ export interface ToolDef<Ext = unknown> {
|
|
|
130
142
|
*/
|
|
131
143
|
handler(deps: ToolDeps<Ext>, args: Record<string, unknown>): Promise<unknown>;
|
|
132
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* The optional inline intent, on every tool that draws a verdict.
|
|
147
|
+
*
|
|
148
|
+
* One field, carrying either prose or the id of an intent already declared, because the surface is
|
|
149
|
+
* the scarce thing and a second field for the reference case would double the cost of the idea to
|
|
150
|
+
* make one of its two uses marginally more explicit. Told apart by the ledger — see
|
|
151
|
+
* `intent/inline-intent.ts`.
|
|
152
|
+
*
|
|
153
|
+
* The first sentence is what a lean surface advertises, so it carries both readings; the rest is for
|
|
154
|
+
* the profiles that send the whole description.
|
|
155
|
+
*/
|
|
156
|
+
export declare const intentArg: z.ZodOptional<z.ZodString>;
|
|
133
157
|
export declare const sessionIdShape: {
|
|
134
158
|
sessionId: z.ZodOptional<z.ZodString>;
|
|
135
159
|
};
|
package/dist/tools/tool-kit.js
CHANGED
|
@@ -7,6 +7,27 @@
|
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { ReticleCommand, SnapshotMode } from '@reticlehq/core';
|
|
9
9
|
import { normalizeLines } from '../project/baselines.js';
|
|
10
|
+
/**
|
|
11
|
+
* The optional inline intent, on every tool that draws a verdict.
|
|
12
|
+
*
|
|
13
|
+
* One field, carrying either prose or the id of an intent already declared, because the surface is
|
|
14
|
+
* the scarce thing and a second field for the reference case would double the cost of the idea to
|
|
15
|
+
* make one of its two uses marginally more explicit. Told apart by the ledger — see
|
|
16
|
+
* `intent/inline-intent.ts`.
|
|
17
|
+
*
|
|
18
|
+
* The first sentence is what a lean surface advertises, so it carries both readings; the rest is for
|
|
19
|
+
* the profiles that send the whole description.
|
|
20
|
+
*/
|
|
21
|
+
export const intentArg = z
|
|
22
|
+
.string()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('What this change is meant to make true — as a durable statement ABOUT THE PRODUCT, or the id of an intent you already declared. ' +
|
|
25
|
+
'Write it so a teammate who was not here understands it in six months: name the behaviour, not this run. ' +
|
|
26
|
+
'GOOD: "a filtered issue queue is a shareable link — the filter is in the URL". ' +
|
|
27
|
+
'BAD: "P1 step 5", "drive 2 of 2", "same as before the fix", "the board renders cleanly" — these name a session, ' +
|
|
28
|
+
'a step number or nothing checkable, and the person reading them later has none of that context. ' +
|
|
29
|
+
'This is shared memory: it is pooled per project and later agents read it back to avoid re-deriving what you just established. ' +
|
|
30
|
+
'Recorded in .reticle/intent.json, the same ledger reticle_intent writes, and marked proved by this verdict if it passes.');
|
|
10
31
|
export const sessionIdShape = {
|
|
11
32
|
sessionId: z
|
|
12
33
|
.string()
|
|
@@ -47,9 +47,14 @@ export declare const ReticleTool: {
|
|
|
47
47
|
readonly COVERAGE: "reticle_coverage";
|
|
48
48
|
readonly VERIFY_CHANGE: "reticle_verify_change";
|
|
49
49
|
/** Merged: change/flows/affected/coverage/crawl — "what is proved, and what is not". */
|
|
50
|
+
readonly INTENT: "reticle_intent";
|
|
51
|
+
/** what THIS run has established, proved, and not yet discharged — pulled when memory is gone. */
|
|
52
|
+
readonly CONTEXT: "reticle_context";
|
|
50
53
|
readonly VERIFY: "reticle_verify";
|
|
51
54
|
/** read cross-run history (.reticle/project.json) + diff-vs-last for a name. */
|
|
52
55
|
readonly PROJECT: "reticle_project";
|
|
56
|
+
/** The team's shared memory, read on demand — see memory/memory-tools.ts. */
|
|
57
|
+
readonly MEMORY: "reticle_memory";
|
|
53
58
|
/** explicitly record a run outcome (the manual companion to auto-recording). */
|
|
54
59
|
readonly RUN_RECORD: "reticle_run_record";
|
|
55
60
|
/** capture a pixel screenshot (CDP/driven browser) →.reticle/visual/<name>.png. */
|
package/dist/tools/tool-names.js
CHANGED
|
@@ -48,9 +48,14 @@ export const ReticleTool = {
|
|
|
48
48
|
COVERAGE: 'reticle_coverage',
|
|
49
49
|
VERIFY_CHANGE: 'reticle_verify_change',
|
|
50
50
|
/** Merged: change/flows/affected/coverage/crawl — "what is proved, and what is not". */
|
|
51
|
+
INTENT: 'reticle_intent',
|
|
52
|
+
/** what THIS run has established, proved, and not yet discharged — pulled when memory is gone. */
|
|
53
|
+
CONTEXT: 'reticle_context',
|
|
51
54
|
VERIFY: 'reticle_verify',
|
|
52
55
|
/** read cross-run history (.reticle/project.json) + diff-vs-last for a name. */
|
|
53
56
|
PROJECT: 'reticle_project',
|
|
57
|
+
/** The team's shared memory, read on demand — see memory/memory-tools.ts. */
|
|
58
|
+
MEMORY: 'reticle_memory',
|
|
54
59
|
/** explicitly record a run outcome (the manual companion to auto-recording). */
|
|
55
60
|
RUN_RECORD: 'reticle_run_record',
|
|
56
61
|
/** capture a pixel screenshot (CDP/driven browser) →.reticle/visual/<name>.png. */
|
|
@@ -70,6 +70,45 @@ export declare const TOOL_SURFACE: {
|
|
|
70
70
|
* supplies the evidence the surface would otherwise have to go and find.
|
|
71
71
|
*/
|
|
72
72
|
readonly VERIFY: "verify";
|
|
73
|
+
/**
|
|
74
|
+
* EXPERIMENTAL, opt-in, and UNDER MEASUREMENT. Not a recommendation, and not on a path to becoming
|
|
75
|
+
* the default until it has a number of its own.
|
|
76
|
+
*
|
|
77
|
+
* It exists because of the finding recorded on VERIFY above, read forwards instead of backwards.
|
|
78
|
+
* That surface cut the bill 37% and TRIPLED false alarms, and the five new false alarms were not
|
|
79
|
+
* scattered: every one of them was a defect whose evidence lives in `reticle_state`,
|
|
80
|
+
* `reticle_network` or `reticle_observe`. So the token saving and the accuracy loss came from two
|
|
81
|
+
* different cuts, and only one of them has to be paid for. `lean` keeps every observation tool and
|
|
82
|
+
* cuts the rest.
|
|
83
|
+
*
|
|
84
|
+
* What stays, and why each one is not a candidate for removal:
|
|
85
|
+
* SNAPSHOT / QUERY look. Without them the agent cannot name an element it did not already know.
|
|
86
|
+
* STATE / NETWORK / CONSOLE / OBSERVE the four evidence tools. Dropping these is the MEASURED
|
|
87
|
+
* cause of the tripled false-alarm rate. They are the point of the profile.
|
|
88
|
+
* ACT_AND_WAIT the only tool that acts AND returns a verdict. Omitting `until` makes it
|
|
89
|
+
* act-then-settle, which is what `reticle_act` does, so `act` is redundant here.
|
|
90
|
+
* ASSERT the only way to get a verdict WITHOUT acting. `act_and_wait` requires an
|
|
91
|
+
* `action`, so this is a capability it genuinely cannot express, not a synonym.
|
|
92
|
+
*
|
|
93
|
+
* What goes, and what it costs (one `reticle_run` hop each — `unadvertisedToolHelp` hands the agent
|
|
94
|
+
* the exact call, so a dropped name never comes back as "not found"):
|
|
95
|
+
* ACT subsumed: `act_and_wait` with `until` omitted is act-then-settle.
|
|
96
|
+
* WAIT_FOR subsumed: `act_and_wait { until }` takes the same PredicateSchema.
|
|
97
|
+
* ACT_SEQUENCE batching is a cost optimisation on top of a surface built to be cheap.
|
|
98
|
+
* NAVIGATE once per run, not once per turn — the wrong thing to pay for every turn.
|
|
99
|
+
* SESSIONS the CLI answers this (`reticle status`) without spending any turn budget.
|
|
100
|
+
* INSPECT fix-side, not verdict-side: it is called once a bug is FOUND, so its hop is
|
|
101
|
+
* paid once per finding rather than once per turn. Nothing else maps a node to
|
|
102
|
+
* a source file, which is why it is a hop and not a deletion.
|
|
103
|
+
* SESSION the lease block and the pause hint name it in prose, which is what made it
|
|
104
|
+
* load-bearing on the default surface. Under a trimmed surface the
|
|
105
|
+
* unadvertised-tool help turns that into a working `reticle_run` call.
|
|
106
|
+
* FEEDBACK the one drop with a KNOWN CEILING: an unadvertised feedback channel collects
|
|
107
|
+
* little to nothing, so a long-lived profile must not ship without it. Acceptable
|
|
108
|
+
* only because this profile is opt-in and short-lived by construction; if it
|
|
109
|
+
* ever stops being an experiment, feedback comes back first.
|
|
110
|
+
*/
|
|
111
|
+
readonly LEAN: "lean";
|
|
73
112
|
};
|
|
74
113
|
export type ToolSurface = (typeof TOOL_SURFACE)[keyof typeof TOOL_SURFACE];
|
|
75
114
|
/**
|
|
@@ -79,8 +118,6 @@ export type ToolSurface = (typeof TOOL_SURFACE)[keyof typeof TOOL_SURFACE];
|
|
|
79
118
|
* users to shop among alternatives that did not meaningfully differ.
|
|
80
119
|
*/
|
|
81
120
|
export declare const ADVERTISE_ALL_ENV = "RETICLE_ADVERTISE_ALL_TOOLS";
|
|
82
|
-
/** Opt into the smallest verdict-capable surface. Read by the DAEMON at startup, like the others. */
|
|
83
|
-
export declare const VERIFY_SURFACE_ENV = "RETICLE_VERIFY_SURFACE";
|
|
84
121
|
/**
|
|
85
122
|
* The retired setting, still read so nobody's shell profile breaks.
|
|
86
123
|
*
|
|
@@ -98,6 +135,17 @@ export declare const CORE_TOOL_NAMES: ReadonlySet<string>;
|
|
|
98
135
|
* 48 by itself can push a user's other servers out or be dropped wholesale. The budget is a count,
|
|
99
136
|
* so no amount of trimming parameter prose buys anything back — only advertising fewer names does.
|
|
100
137
|
*
|
|
138
|
+
* That is true of the COUNT cap and false of everything else, which is worth separating here before
|
|
139
|
+
* somebody reads it as "prose is free". Measured against two competitor MCP servers on the same
|
|
140
|
+
* agent loop: this server advertises the FEWEST tools of the three and ships the HEAVIEST schema
|
|
141
|
+
* block, and 83% of that weight is parameter descriptions, not tool descriptions. The block is
|
|
142
|
+
* re-sent every turn, so it is multiplied by turn count before one byte of evidence is counted, and
|
|
143
|
+
* it was most of the gap in that run.
|
|
144
|
+
*
|
|
145
|
+
* Which does NOT license trimming by eye. Those descriptions are what make a tool get called
|
|
146
|
+
* correctly, and one malformed call costs a whole extra turn — more than the bytes saved. The
|
|
147
|
+
* outcome to measure is turn count and error rate, not size.
|
|
148
|
+
*
|
|
101
149
|
* Everything omitted stays in the registry, stays catalogued by `reticle_tools`, and stays callable
|
|
102
150
|
* by name through `reticle_run { tool, args }`. The cost is one discovery hop on the cold tail, which
|
|
103
151
|
* is the same trade the default surface has always made, applied one level further out.
|
|
@@ -113,11 +161,42 @@ export declare const CORE_TOOL_NAMES: ReadonlySet<string>;
|
|
|
113
161
|
*/
|
|
114
162
|
export declare const EXTENDED_TOOL_NAMES: ReadonlySet<string>;
|
|
115
163
|
/**
|
|
116
|
-
* The
|
|
117
|
-
*
|
|
118
|
-
*
|
|
164
|
+
* The lean surface: look, observe, act-with-a-verdict, assert. Pinned by lean-surface.test.ts,
|
|
165
|
+
* because an experiment whose independent variable drifts mid-flight measures nothing. Every
|
|
166
|
+
* inclusion and every exclusion is argued at TOOL_SURFACE.LEAN.
|
|
167
|
+
*
|
|
168
|
+
* ## MEASURED: it is cheaper and less correct. Do not promote it to the default.
|
|
169
|
+
*
|
|
170
|
+
* Run as its own arm of the fix-and-verify benchmark — same five bugs, same model, same budget, the
|
|
171
|
+
* full surface as its control:
|
|
172
|
+
*
|
|
173
|
+
* | | full surface | lean |
|
|
174
|
+
* |---|---|---|
|
|
175
|
+
* | bugs fixed | **5/5** | **3/5** |
|
|
176
|
+
* | FALSE GREENS | **0** | **1** |
|
|
177
|
+
*
|
|
178
|
+
* The false green is the finding, and it is the first this benchmark has ever produced. On
|
|
179
|
+
* `broken-form-validation` the lean agent finished in SIX turns and 62k tokens — the cheapest cell
|
|
180
|
+
* of the whole run, a fifth of what the full surface spent — edited the file, took one snapshot,
|
|
181
|
+
* and ended with a hypothetical walkthrough: "Enter spaces + valid name -> spaces are trimmed,
|
|
182
|
+
* valid part is used. VERDICT: FIXED". The submit button was still enabled for a whitespace-only
|
|
183
|
+
* service. It reasoned about what its own code would now do instead of driving it, which is exactly
|
|
184
|
+
* the failure this product exists to catch, produced by our own surface.
|
|
185
|
+
*
|
|
186
|
+
* The other loss has the same root from the other side. On `cross-component-regression` the agent
|
|
187
|
+
* called `reticle_tools` three times and `reticle_run` eight, saying "since Reticle isn't
|
|
188
|
+
* connecting, let me see what reticle_tools offers" — it spent the run hunting for capabilities
|
|
189
|
+
* this surface does not advertise instead of using them, and never fixed the bug.
|
|
190
|
+
*
|
|
191
|
+
* That is the same result the `verify` surface already recorded — dropping the observation tools
|
|
192
|
+
* TRIPLED false alarms — arriving from the other direction: strip the surface and the agent stops
|
|
193
|
+
* verifying, then claims anyway. The known ceiling written here was that `reticle_feedback` goes
|
|
194
|
+
* uncollected. This is a bigger one and it was not predicted.
|
|
195
|
+
*
|
|
196
|
+
* It stays opt-in and it stays measured. The one thing it genuinely bought — the ceiling-bound cell
|
|
197
|
+
* halved, 30 turns to 14 — is not worth a verdict that lies, because the product is the verdict.
|
|
119
198
|
*/
|
|
120
|
-
export declare const
|
|
199
|
+
export declare const LEAN_TOOL_NAMES: ReadonlySet<string>;
|
|
121
200
|
/**
|
|
122
201
|
* Which surface is live.
|
|
123
202
|
*
|