@reticlehq/server 2.13.0 → 2.14.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 +8 -18
- package/bin/reticle.js +3 -0
- package/dist/bridge/auth-failure-reason.js +12 -1
- package/dist/bridge/bridge-security.d.ts +8 -0
- package/dist/bridge/bridge-security.js +6 -1
- package/dist/bridge/bridge.d.ts +3 -0
- package/dist/bridge/bridge.js +23 -1
- package/dist/bridge/bridge.test-harness.d.ts +10 -1
- package/dist/bridge/bridge.test-harness.js +7 -1
- package/dist/bridge/pairing-token.d.ts +17 -0
- package/dist/bridge/pairing-token.js +26 -0
- package/dist/bridge/token-auth.d.ts +7 -0
- package/dist/bridge/token-auth.js +7 -1
- package/dist/capsule/capsule-store.d.ts +4 -4
- package/dist/capsule/capsule.d.ts +1 -1
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +21 -1
- package/dist/capsule/causal-summary.js +8 -6
- package/dist/capsule/divergence.d.ts +1 -1
- package/dist/capsule/divergence.js +26 -10
- package/dist/chromium-launch-options.d.ts +2 -1
- package/dist/cli/adhoc-verdict.d.ts +58 -0
- package/dist/cli/adhoc-verdict.js +115 -0
- package/dist/cli/chromium-hint.d.ts +18 -4
- package/dist/cli/chromium-hint.js +54 -6
- package/dist/cli/cli-doctor.js +1 -2
- package/dist/cli/cli-flow-commands.d.ts +16 -2
- package/dist/cli/cli-flow-commands.js +28 -9
- package/dist/cli/cli-parse-feedback.js +1 -1
- package/dist/cli/cli-parse.d.ts +2 -1
- package/dist/cli/cli-parse.js +38 -0
- package/dist/cli/cli-port.d.ts +5 -13
- package/dist/cli/cli-port.js +5 -38
- package/dist/cli/cli-update-commands.js +4 -4
- package/dist/cli/cli-verify.d.ts +24 -7
- package/dist/cli/cli-verify.js +87 -13
- package/dist/cli/cloud-keystore.d.ts +2 -1
- package/dist/cli/cloud-login.d.ts +2 -1
- package/dist/cli/cloud-session.d.ts +2 -1
- package/dist/cli/cloud-session.js +1 -1
- package/dist/cli/daemon-start-options.d.ts +2 -1
- package/dist/cli/device-flow.d.ts +2 -2
- package/dist/cli/doctor-daemon-line.d.ts +2 -1
- package/dist/cli/doctor-project-line.d.ts +2 -1
- package/dist/cli/doctor-rows.d.ts +2 -1
- package/dist/cli/drive-attach.d.ts +4 -3
- package/dist/cli/gate-hook-message.d.ts +2 -1
- package/dist/cli/status-next-action.js +10 -11
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +21 -9
- package/dist/cloud/account-state.d.ts +20 -0
- package/dist/cloud/account-state.js +65 -0
- package/dist/cloud/cloud-sync.d.ts +46 -3
- package/dist/cloud/cloud-sync.js +116 -0
- package/dist/cloud/sync-cycle.d.ts +5 -4
- package/dist/cloud/sync-cycle.js +1 -1
- package/dist/cloud/sync-daemon.d.ts +3 -19
- package/dist/cloud/sync-daemon.js +2 -2
- package/dist/crawl/crawl-tools.js +1 -1
- package/dist/crawl/crawl.d.ts +5 -5
- package/dist/crawl/crawl.js +138 -4
- package/dist/daemon/daemon-resilience.d.ts +3 -2
- package/dist/daemon/daemon-resilience.js +43 -4
- package/dist/daemon/daemon-resolve.d.ts +0 -9
- package/dist/daemon/daemon-resolve.js +2 -2
- package/dist/daemon/dev-servers.d.ts +1 -1
- package/dist/daemon/dev-servers.js +1 -1
- package/dist/daemon/loopback-alias.d.ts +2 -1
- package/dist/daemon/startup-failure.d.ts +11 -0
- package/dist/daemon/startup-failure.js +46 -0
- package/dist/events/contradictions.d.ts +10 -16
- package/dist/events/contradictions.js +149 -17
- package/dist/events/declared.d.ts +13 -1
- package/dist/events/declared.js +59 -3
- package/dist/events/echo-mismatch.js +75 -6
- package/dist/events/lineage.d.ts +52 -0
- package/dist/events/lineage.js +122 -0
- package/dist/events/net-evidence.d.ts +21 -0
- package/dist/events/net-evidence.js +81 -0
- package/dist/events/predicate-console.d.ts +13 -0
- package/dist/events/predicate-console.js +138 -0
- package/dist/events/predicate-element.d.ts +13 -0
- package/dist/events/predicate-element.js +214 -0
- package/dist/events/predicate-eval.d.ts +15 -3
- package/dist/events/predicate-eval.js +128 -110
- package/dist/events/predicate-parse.js +34 -1
- package/dist/events/predicate-request-body.d.ts +50 -0
- package/dist/events/predicate-request-body.js +120 -0
- package/dist/events/predicate-route.d.ts +21 -1
- package/dist/events/predicate-route.js +149 -21
- package/dist/events/predicate-schema.d.ts +8 -1
- package/dist/events/predicate-schema.js +34 -0
- package/dist/events/predicate.d.ts +2 -0
- package/dist/events/predicate.js +52 -159
- package/dist/events/split-text-miss.d.ts +6 -4
- package/dist/events/split-text-miss.js +12 -5
- package/dist/flows/affected-tools.js +22 -9
- package/dist/flows/annotate.d.ts +2 -1
- package/dist/flows/annotate.js +19 -1
- package/dist/flows/coverage.d.ts +2 -2
- package/dist/flows/flake.d.ts +3 -3
- package/dist/flows/flow-expect-grammar.d.ts +33 -0
- package/dist/flows/flow-expect-grammar.js +155 -0
- package/dist/flows/flow-replay-run.d.ts +1 -1
- package/dist/flows/flow-replay-run.js +28 -15
- package/dist/flows/flow-replay.d.ts +10 -0
- package/dist/flows/flow-replay.js +57 -22
- package/dist/flows/flow-secret-field.d.ts +14 -0
- package/dist/flows/flow-secret-field.js +22 -0
- package/dist/flows/flow-step-runners.d.ts +1 -0
- package/dist/flows/flow-step-runners.js +91 -34
- package/dist/flows/flow-success.js +22 -0
- package/dist/flows/flow-tools.js +27 -8
- package/dist/flows/flow-upload-resolve.d.ts +23 -0
- package/dist/flows/flow-upload-resolve.js +46 -0
- package/dist/flows/flows.d.ts +15 -0
- package/dist/flows/flows.js +17 -14
- package/dist/flows/git-changed.d.ts +23 -4
- package/dist/flows/git-changed.js +32 -6
- package/dist/flows/heal-run.js +2 -2
- package/dist/flows/predicate-to-expect.d.ts +3 -2
- package/dist/flows/predicate-to-expect.js +11 -5
- package/dist/flows/recording-backtrack.d.ts +24 -0
- package/dist/flows/recording-backtrack.js +73 -0
- package/dist/flows/recordings.d.ts +6 -0
- package/dist/flows/replay-halt.d.ts +26 -0
- package/dist/flows/replay-halt.js +30 -0
- package/dist/flows/replay.d.ts +0 -8
- package/dist/flows/replay.js +15 -3
- package/dist/flows/suite-flakes.d.ts +2 -1
- package/dist/flows/verify-change-tools.js +16 -4
- package/dist/honesty/already-true.d.ts +14 -0
- package/dist/honesty/already-true.js +47 -4
- package/dist/honesty/blind-spots.d.ts +3 -1
- package/dist/honesty/blind-spots.js +24 -1
- package/dist/honesty/body-capture-remedy.d.ts +42 -0
- package/dist/honesty/body-capture-remedy.js +95 -0
- package/dist/honesty/feature-capture.d.ts +3 -2
- package/dist/honesty/feature-capture.js +1 -1
- package/dist/honesty/gap-ledger.d.ts +1 -1
- package/dist/honesty/gap-ledger.js +1 -1
- package/dist/honesty/instrumentation-gaps.d.ts +8 -1
- package/dist/honesty/instrumentation-gaps.js +12 -2
- package/dist/honesty/observability.d.ts +4 -3
- package/dist/honesty/tool-hit-rate.d.ts +3 -2
- package/dist/honesty/uncaptured-bodies.d.ts +3 -1
- package/dist/honesty/uncaptured-bodies.js +6 -2
- package/dist/honesty/unsettled.d.ts +9 -0
- package/dist/honesty/unsettled.js +45 -0
- package/dist/honesty/verified.d.ts +32 -1
- package/dist/honesty/verified.js +37 -10
- package/dist/impact/impact-store.d.ts +5 -14
- package/dist/impact/impact-store.js +49 -6
- package/dist/index.js +1 -1
- package/dist/input/network-detail.d.ts +26 -1
- package/dist/input/network-detail.js +44 -12
- package/dist/input/network-mock-tools.js +24 -17
- package/dist/input/real-input.js +1 -1
- package/dist/input/viewport-tools.js +25 -13
- package/dist/intent/inline-intent.d.ts +0 -10
- package/dist/intent/inline-intent.js +1 -1
- package/dist/intent/intent-shard-store.d.ts +3 -2
- package/dist/intent/intent-shard-store.js +1 -1
- package/dist/intent/intent-shard.d.ts +14 -14
- package/dist/intent/intent-shard.js +4 -4
- package/dist/intent/intent-store.d.ts +1 -1
- package/dist/intent/intent-store.js +1 -1
- package/dist/intent/intent-subject.d.ts +2 -1
- package/dist/intent/open-intents.d.ts +3 -2
- package/dist/license/license.d.ts +4 -4
- package/dist/license/license.js +1 -1
- package/dist/mcp/attach-memory.d.ts +2 -1
- package/dist/mcp/client-identity.d.ts +2 -1
- package/dist/mcp/mcp-outage.d.ts +1 -1
- package/dist/mcp/mcp-outage.js +1 -1
- package/dist/mcp/mcp-proxy.d.ts +3 -1
- package/dist/mcp/mcp-proxy.js +32 -7
- package/dist/mcp/mcp.js +3 -1
- package/dist/mcp/proxy-handshake.d.ts +2 -1
- package/dist/mcp/proxy-idle-exit.d.ts +45 -0
- package/dist/mcp/proxy-idle-exit.js +94 -0
- package/dist/mcp/server-instructions.d.ts +2 -1
- package/dist/memory/project-memory.d.ts +2 -3
- package/dist/memory/project-memory.js +1 -1
- package/dist/oracles/propose-consequences.js +5 -5
- package/dist/oracles/self-instrument.d.ts +2 -2
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/platform.d.ts +3 -5
- package/dist/platform.js +3 -5
- package/dist/pool/browser-pool.d.ts +92 -0
- package/dist/pool/browser-pool.js +119 -5
- package/dist/pool/playwright-launcher.js +29 -2
- package/dist/pool/storage-seed.d.ts +23 -0
- package/dist/pool/storage-seed.js +198 -0
- package/dist/project/artifact-root.d.ts +2 -1
- package/dist/project/memory-fs.d.ts +2 -1
- package/dist/project/reticle-dir.js +1 -1
- package/dist/project/session-root.d.ts +8 -0
- package/dist/project/session-root.js +9 -2
- package/dist/runs/reticle-runner.d.ts +14 -1
- package/dist/runs/reticle-runner.js +39 -2
- package/dist/session/attachment-history.d.ts +2 -1
- package/dist/session/fake-session.d.ts +37 -0
- package/dist/session/fake-session.js +81 -0
- package/dist/session/lease-visibility.d.ts +2 -1
- package/dist/session/live-control-tools.js +6 -0
- package/dist/session/no-session-diagnosis.d.ts +17 -1
- package/dist/session/no-session-diagnosis.js +32 -6
- package/dist/session/no-session-next-action.d.ts +14 -0
- package/dist/session/no-session-next-action.js +18 -0
- package/dist/session/no-session-watch.js +46 -3
- package/dist/session/registered-projects.d.ts +54 -0
- package/dist/session/registered-projects.js +73 -0
- package/dist/session/review-store.d.ts +2 -1
- package/dist/session/session-health.d.ts +62 -1
- package/dist/session/session-health.js +82 -2
- package/dist/session/session-manager.d.ts +11 -1
- package/dist/session/session-manager.js +18 -3
- package/dist/session/session-replaced.d.ts +24 -0
- package/dist/session/session-replaced.js +29 -0
- package/dist/session/session-successor.js +20 -4
- package/dist/session/session.d.ts +13 -5
- package/dist/session/session.js +31 -23
- package/dist/setup/agent-configs.d.ts +3 -2
- package/dist/setup/agent-writer.d.ts +3 -2
- package/dist/setup/approval-migration.d.ts +2 -1
- package/dist/setup/approval-migration.js +1 -1
- package/dist/setup/auto-approve.d.ts +11 -19
- package/dist/setup/auto-approve.js +15 -12
- package/dist/{init → setup}/confirm.js +1 -1
- package/dist/setup/desktop-shape.d.ts +3 -2
- package/dist/setup/dev-server-wait.d.ts +8 -0
- package/dist/setup/dev-server-wait.js +3 -1
- package/dist/setup/drive-agent.d.ts +2 -3
- package/dist/setup/drive-agent.js +1 -1
- package/dist/setup/drive-plan.d.ts +3 -2
- package/dist/setup/ensure-daemon.d.ts +2 -1
- package/dist/setup/existing-app.d.ts +12 -0
- package/dist/setup/existing-app.js +13 -0
- package/dist/setup/init-host.d.ts +11 -0
- package/dist/setup/init-host.js +26 -0
- package/dist/setup/init-runtime.d.ts +4 -10
- package/dist/setup/init-runtime.js +23 -16
- package/dist/setup/license-key.d.ts +2 -1
- package/dist/setup/listeners.d.ts +2 -1
- package/dist/setup/loopback-probe-urls.d.ts +18 -0
- package/dist/setup/loopback-probe-urls.js +52 -0
- package/dist/setup/node-effects.d.ts +3 -0
- package/dist/setup/node-effects.js +26 -11
- package/dist/setup/page-probe.d.ts +5 -0
- package/dist/setup/relaunch.d.ts +2 -1
- package/dist/setup/run-setup.d.ts +5 -1
- package/dist/setup/run-setup.js +155 -48
- package/dist/setup/setup-command.d.ts +3 -2
- package/dist/setup/setup-command.js +8 -0
- package/dist/setup/setup-options.d.ts +5 -3
- package/dist/setup/setup-options.js +3 -2
- package/dist/setup/wait-progress.d.ts +28 -0
- package/dist/setup/wait-progress.js +43 -0
- package/dist/telemetry/app-instrumented.d.ts +2 -1
- package/dist/telemetry/app-instrumented.js +1 -1
- package/dist/telemetry/automation-hint.d.ts +1 -1
- package/dist/telemetry/automation-hint.js +1 -1
- package/dist/telemetry/cli-telemetry.js +1 -1
- package/dist/telemetry/connect-failure.d.ts +1 -1
- package/dist/telemetry/connect-failure.js +1 -1
- package/dist/telemetry/crash-cause.d.ts +3 -2
- package/dist/telemetry/crash-cause.js +1 -1
- package/dist/telemetry/daemon-telemetry.d.ts +3 -2
- package/dist/telemetry/daemon-telemetry.js +1 -1
- package/dist/telemetry/error-class.d.ts +3 -2
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-cli.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +3 -2
- package/dist/telemetry/feedback-context.js +5 -4
- package/dist/telemetry/feedback-local.d.ts +3 -2
- package/dist/telemetry/feedback.d.ts +4 -3
- package/dist/telemetry/feedback.js +1 -1
- package/dist/telemetry/git-facts.d.ts +2 -2
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/identify.d.ts +1 -1
- package/dist/telemetry/identify.js +1 -1
- package/dist/telemetry/init-telemetry.d.ts +2 -14
- package/dist/telemetry/init-telemetry.js +4 -13
- package/dist/telemetry/install-source.d.ts +7 -13
- package/dist/telemetry/install-source.js +7 -29
- package/dist/telemetry/license-activation.d.ts +3 -2
- package/dist/telemetry/license-activation.js +1 -1
- package/dist/telemetry/machine-snapshot.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +1 -1
- package/dist/telemetry/project-profile.d.ts +0 -14
- package/dist/telemetry/project-profile.js +1 -1
- package/dist/telemetry/proxy-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +1 -1
- package/dist/telemetry/telemetry.d.ts +1 -17
- package/dist/telemetry/telemetry.js +4 -4
- package/dist/telemetry/tool-refused.d.ts +1 -1
- package/dist/telemetry/tool-refused.js +1 -1
- package/dist/tools/act-observation.d.ts +3 -2
- package/dist/tools/act-preflight.js +8 -3
- package/dist/tools/act-target.d.ts +4 -0
- package/dist/tools/act-target.js +14 -5
- package/dist/tools/act-tools.d.ts +0 -2
- package/dist/tools/act-tools.js +45 -15
- package/dist/tools/assert-source.d.ts +30 -0
- package/dist/tools/assert-source.js +21 -0
- package/dist/tools/assert-verdict.d.ts +1 -1
- package/dist/tools/assert-verdict.js +14 -4
- package/dist/tools/browser-tools.js +28 -8
- package/dist/tools/complete-snapshot.d.ts +3 -2
- package/dist/tools/dynamic-tools.js +8 -0
- package/dist/tools/error-recovery.d.ts +10 -0
- package/dist/tools/error-recovery.js +23 -0
- package/dist/tools/feedback-tools.js +1 -1
- package/dist/tools/invoke-tool.js +2 -0
- package/dist/tools/lease-tools.d.ts +48 -20
- package/dist/tools/lease-tools.js +311 -76
- package/dist/tools/lineage-tools.d.ts +14 -0
- package/dist/tools/lineage-tools.js +64 -0
- package/dist/tools/merge-tools.js +1 -3
- package/dist/tools/navigate-arrival.d.ts +45 -1
- package/dist/tools/navigate-arrival.js +39 -6
- package/dist/tools/navigate-result.d.ts +8 -1
- package/dist/tools/navigate-result.js +14 -4
- package/dist/tools/numeric-bounds.d.ts +0 -11
- package/dist/tools/numeric-bounds.js +4 -4
- package/dist/tools/observe-tools.js +22 -26
- package/dist/tools/observed-nothing.d.ts +9 -0
- package/dist/tools/observed-nothing.js +2 -1
- package/dist/tools/read-tools.js +12 -17
- package/dist/tools/real-input-attempt.d.ts +14 -6
- package/dist/tools/real-input-attempt.js +75 -18
- package/dist/tools/reconcile-tools.js +2 -1
- package/dist/tools/reload-result.d.ts +6 -1
- package/dist/tools/reload-result.js +7 -2
- package/dist/tools/resolve-target.d.ts +7 -1
- package/dist/tools/resolve-target.js +72 -9
- package/dist/tools/settle-in-flight.d.ts +1 -1
- package/dist/tools/settle-in-flight.js +9 -2
- package/dist/tools/snapshot-delta.d.ts +24 -1
- package/dist/tools/snapshot-delta.js +66 -7
- package/dist/tools/tool-names.d.ts +1 -0
- package/dist/tools/tool-names.js +1 -0
- package/dist/tools/tool-surface.js +20 -2
- package/dist/tools/tools.js +120 -11
- package/dist/tools/verify-next-baton.d.ts +2 -1
- package/dist/update/sdk-sync.d.ts +1 -1
- package/dist/update/sdk-sync.js +1 -1
- package/dist/update/updater.js +1 -1
- package/dist/version/sdk-fix.d.ts +2 -1
- package/dist/version/version-skew.d.ts +0 -8
- package/dist/version/version-skew.js +1 -1
- package/docs/README.md +8 -6
- package/docs/actions.mdx +1 -1
- package/docs/agent-cheatsheet.md +4 -2
- package/docs/architecture.md +1 -1
- package/docs/cli/mcp.mdx +3 -1
- package/docs/cli/watch.mdx +1 -1
- package/docs/cli.mdx +1 -1
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +8 -3
- package/docs/faq.mdx +9 -0
- package/docs/first-drive.md +136 -0
- package/docs/frameworks.mdx +29 -7
- package/docs/gate-plan.md +2 -2
- package/docs/gates.md +19 -11
- package/docs/getting-started.md +7 -1
- package/docs/http-transport.md +164 -0
- package/docs/index.mdx +5 -3
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +15 -3
- package/docs/platform-integration.md +1 -1
- package/docs/predicates.mdx +2 -0
- package/docs/system-map.md +2 -2
- package/docs/telemetry-contract.md +4 -4
- package/docs/telemetry-events.mdx +1 -1
- package/docs/telemetry.md +3 -3
- package/docs/tools-navigate.mdx +14 -6
- package/docs/usage.md +4 -4
- package/package.json +7 -4
- package/dist/init/agent-rules.d.ts +0 -116
- package/dist/init/agent-rules.js +0 -279
- package/dist/init/app-choice.d.ts +0 -20
- package/dist/init/app-choice.js +0 -25
- package/dist/init/astro-host.d.ts +0 -21
- package/dist/init/astro-host.js +0 -27
- package/dist/init/astro-patch.d.ts +0 -33
- package/dist/init/astro-patch.js +0 -300
- package/dist/init/capabilities.d.ts +0 -40
- package/dist/init/capabilities.js +0 -150
- package/dist/init/closing-hint.d.ts +0 -36
- package/dist/init/closing-hint.js +0 -120
- package/dist/init/connect-steps.d.ts +0 -2
- package/dist/init/connect-steps.js +0 -50
- package/dist/init/cra.d.ts +0 -79
- package/dist/init/cra.js +0 -178
- package/dist/init/csp-check.d.ts +0 -38
- package/dist/init/csp-check.js +0 -107
- package/dist/init/csp-doctor.d.ts +0 -32
- package/dist/init/csp-doctor.js +0 -54
- package/dist/init/desktop-doctor.d.ts +0 -41
- package/dist/init/desktop-doctor.js +0 -198
- package/dist/init/detect.d.ts +0 -96
- package/dist/init/detect.js +0 -209
- package/dist/init/dev-script.d.ts +0 -50
- package/dist/init/dev-script.js +0 -66
- package/dist/init/format-generated.d.ts +0 -18
- package/dist/init/format-generated.js +0 -39
- package/dist/init/install-hint.d.ts +0 -9
- package/dist/init/install-hint.js +0 -29
- package/dist/init/mcp-clients.d.ts +0 -89
- package/dist/init/mcp-clients.js +0 -299
- package/dist/init/mcp-registered.d.ts +0 -12
- package/dist/init/mcp-registered.js +0 -14
- package/dist/init/mcp.d.ts +0 -89
- package/dist/init/mcp.js +0 -129
- package/dist/init/next-patch.d.ts +0 -39
- package/dist/init/next-patch.js +0 -196
- package/dist/init/node-io.d.ts +0 -7
- package/dist/init/node-io.js +0 -117
- package/dist/init/non-js-project.d.ts +0 -27
- package/dist/init/non-js-project.js +0 -84
- package/dist/init/patch-kind.d.ts +0 -20
- package/dist/init/patch-kind.js +0 -10
- package/dist/init/plan-framework.d.ts +0 -64
- package/dist/init/plan-framework.js +0 -565
- package/dist/init/plan.d.ts +0 -242
- package/dist/init/plan.js +0 -703
- package/dist/init/preflight.d.ts +0 -28
- package/dist/init/preflight.js +0 -38
- package/dist/init/project-id.d.ts +0 -12
- package/dist/init/project-id.js +0 -25
- package/dist/init/refresh-rules.d.ts +0 -39
- package/dist/init/refresh-rules.js +0 -51
- package/dist/init/run.d.ts +0 -117
- package/dist/init/run.js +0 -806
- package/dist/init/slash-command.d.ts +0 -27
- package/dist/init/slash-command.js +0 -132
- package/dist/init/snippets.d.ts +0 -134
- package/dist/init/snippets.js +0 -572
- package/dist/init/vite-config.d.ts +0 -18
- package/dist/init/vite-config.js +0 -114
- package/dist/init/windows-quote.d.ts +0 -38
- package/dist/init/windows-quote.js +0 -70
- package/dist/init/workspace-apps.d.ts +0 -30
- package/dist/init/workspace-apps.js +0 -178
- package/dist/project/remember-project.d.ts +0 -24
- package/dist/project/remember-project.js +0 -40
- /package/dist/{init → setup}/confirm.d.ts +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
/** What the resolution produced: a ref to act on, or the reason there isn't one. */
|
|
1
|
+
/** What the resolution produced: a ref to act on, a document-key press, or the reason there isn't one. */
|
|
2
2
|
export type TargetResolution = {
|
|
3
3
|
readonly kind: 'ref';
|
|
4
4
|
readonly ref: string;
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: 'global';
|
|
7
|
+
readonly ref: '';
|
|
5
8
|
} | {
|
|
6
9
|
readonly kind: 'error';
|
|
7
10
|
readonly message: string;
|
|
@@ -22,5 +25,8 @@ export type TargetResolution = {
|
|
|
22
25
|
* Invisible matches are filtered before counting: a hidden duplicate is a common way for a
|
|
23
26
|
* legitimately unambiguous query to look ambiguous, and refusing on it would push callers back to
|
|
24
27
|
* the two-turn path for no gain.
|
|
28
|
+
*
|
|
29
|
+
* When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
|
|
30
|
+
* agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
|
|
25
31
|
*/
|
|
26
32
|
export declare function resolveTargetRef(candidates: readonly unknown[]): TargetResolution;
|
|
@@ -1,4 +1,67 @@
|
|
|
1
|
+
import { ElementState } from '@reticlehq/core';
|
|
1
2
|
import { asRecord } from './tools-helpers.js';
|
|
3
|
+
/**
|
|
4
|
+
* Roles that usually name the control an agent meant, ahead of a wrapping `generic` that
|
|
5
|
+
* happened to inherit the same accessible name (header CTA vs empty-state CTA).
|
|
6
|
+
*/
|
|
7
|
+
const ROLE_RANK = {
|
|
8
|
+
button: 0,
|
|
9
|
+
link: 1,
|
|
10
|
+
menuitem: 2,
|
|
11
|
+
tab: 3,
|
|
12
|
+
checkbox: 4,
|
|
13
|
+
radio: 5,
|
|
14
|
+
switch: 6,
|
|
15
|
+
option: 7,
|
|
16
|
+
combobox: 8,
|
|
17
|
+
textbox: 9,
|
|
18
|
+
searchbox: 10,
|
|
19
|
+
};
|
|
20
|
+
function roleRank(role) {
|
|
21
|
+
if ('string' !== typeof role)
|
|
22
|
+
return 100;
|
|
23
|
+
return ROLE_RANK[role] ?? 50;
|
|
24
|
+
}
|
|
25
|
+
function statesOf(c) {
|
|
26
|
+
return Array.isArray(c.states) ? c.states.filter((s) => 'string' === typeof s) : [];
|
|
27
|
+
}
|
|
28
|
+
function inViewport(c) {
|
|
29
|
+
return statesOf(c).includes(ElementState.IN_VIEWPORT);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Stable ranking for the refusal message only. Never used to pick — an action must not guess.
|
|
33
|
+
*
|
|
34
|
+
* Order: in-viewport over off-screen, then more-specific roles over `generic`. Visibility is
|
|
35
|
+
* already applied before this runs (hidden duplicates are filtered), so it is not a sort key here.
|
|
36
|
+
*/
|
|
37
|
+
function compareCandidates(a, b) {
|
|
38
|
+
const viewport = Number(inViewport(b)) - Number(inViewport(a));
|
|
39
|
+
if (0 !== viewport)
|
|
40
|
+
return viewport;
|
|
41
|
+
return roleRank(a.role) - roleRank(b.role);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Annotate where the candidate sits. `anyInViewport` is true when at least one sibling carried
|
|
45
|
+
* `inViewport` — only then is absence meaningful as off-screen. Without that signal, saying
|
|
46
|
+
* "off-screen" would invent a fact the descriptor never reported.
|
|
47
|
+
*/
|
|
48
|
+
function formatCandidate(c, anyInViewport) {
|
|
49
|
+
const role = 'string' === typeof c.role ? c.role : '?';
|
|
50
|
+
const name = 'string' === typeof c.name ? c.name : '';
|
|
51
|
+
const ref = 'string' === typeof c.ref ? c.ref : '?';
|
|
52
|
+
const visible = false !== c.visible;
|
|
53
|
+
let where;
|
|
54
|
+
if (!visible)
|
|
55
|
+
where = 'hidden';
|
|
56
|
+
else if (inViewport(c))
|
|
57
|
+
where = 'in-viewport';
|
|
58
|
+
else if (anyInViewport)
|
|
59
|
+
where = 'off-screen';
|
|
60
|
+
else
|
|
61
|
+
where = 'visible';
|
|
62
|
+
const head = name.length > 0 ? `${ref} (${role} "${name}"` : `${ref} (${role}`;
|
|
63
|
+
return `${head}; ${where})`;
|
|
64
|
+
}
|
|
2
65
|
/**
|
|
3
66
|
* Turn a target QUERY into the single ref an action can be performed on.
|
|
4
67
|
*
|
|
@@ -15,6 +78,9 @@ import { asRecord } from './tools-helpers.js';
|
|
|
15
78
|
* Invisible matches are filtered before counting: a hidden duplicate is a common way for a
|
|
16
79
|
* legitimately unambiguous query to look ambiguous, and refusing on it would push callers back to
|
|
17
80
|
* the two-turn path for no gain.
|
|
81
|
+
*
|
|
82
|
+
* When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
|
|
83
|
+
* agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
|
|
18
84
|
*/
|
|
19
85
|
export function resolveTargetRef(candidates) {
|
|
20
86
|
const all = candidates.map((c) => asRecord(c));
|
|
@@ -28,20 +94,17 @@ export function resolveTargetRef(candidates) {
|
|
|
28
94
|
};
|
|
29
95
|
}
|
|
30
96
|
if (usable.length > 1) {
|
|
31
|
-
const
|
|
97
|
+
const ranked = [...usable].sort(compareCandidates);
|
|
98
|
+
const anyInViewport = ranked.some(inViewport);
|
|
99
|
+
const named = ranked
|
|
32
100
|
.slice(0, 5)
|
|
33
|
-
.map((c) =>
|
|
34
|
-
const role = 'string' === typeof c.role ? c.role : '?';
|
|
35
|
-
const name = 'string' === typeof c.name ? c.name : '';
|
|
36
|
-
const ref = 'string' === typeof c.ref ? c.ref : '?';
|
|
37
|
-
return name.length > 0 ? `${ref} (${role} "${name}")` : `${ref} (${role})`;
|
|
38
|
-
})
|
|
101
|
+
.map((c) => formatCandidate(c, anyInViewport))
|
|
39
102
|
.join(', ');
|
|
40
103
|
return {
|
|
41
104
|
kind: 'error',
|
|
42
105
|
message: `target matched ${String(usable.length)} elements and an action must not guess between ` +
|
|
43
|
-
`them: ${named}. Narrow the query (add role/name/testid
|
|
44
|
-
'`ref` from reticle_query.',
|
|
106
|
+
`them (ranked: in-viewport, then role): ${named}. Narrow the query (add role/name/testid ` +
|
|
107
|
+
'or a scope), or pass an explicit `ref` from reticle_query.',
|
|
45
108
|
};
|
|
46
109
|
}
|
|
47
110
|
const only = usable[0];
|
|
@@ -77,7 +77,7 @@ export declare function repeatedRequestLabels(events: readonly {
|
|
|
77
77
|
type: string;
|
|
78
78
|
data: Record<string, unknown>;
|
|
79
79
|
}[]): string[];
|
|
80
|
-
|
|
80
|
+
interface SettleResult {
|
|
81
81
|
/** True when nothing this window started is still outstanding. */
|
|
82
82
|
settled: boolean;
|
|
83
83
|
/** What was still open when the wait ended — empty when `settled`. */
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* - still in flight when the budget genuinely runs out → `unsettled`, as before, but now an honest
|
|
28
28
|
* limit instead of an early exit.
|
|
29
29
|
*/
|
|
30
|
-
import { EventType, isDevToolingUrl } from '@reticlehq/core';
|
|
30
|
+
import { EventType, NetInitiator, isDevToolingUrl } from '@reticlehq/core';
|
|
31
31
|
/**
|
|
32
32
|
* The dev toolchain talking about ITSELF is not the app finishing its work.
|
|
33
33
|
*
|
|
@@ -37,6 +37,13 @@ import { EventType, isDevToolingUrl } from '@reticlehq/core';
|
|
|
37
37
|
* was graded on the strength of a Next webpack hot-update the same payload printed as ignored.
|
|
38
38
|
*/
|
|
39
39
|
const isDevTooling = (data) => isDevToolingUrl('string' === typeof data['url'] ? data['url'] : undefined);
|
|
40
|
+
/**
|
|
41
|
+
* A departure records where the browser was SENT, not a request whose result we will see — the
|
|
42
|
+
* response is delivered to a document this session does not live into. It can never be matched by a
|
|
43
|
+
* NET_REQUEST, so counting it as outstanding would mean no page containing an outbound link or a
|
|
44
|
+
* download button ever settles again. See `NetInitiator.NAVIGATION`.
|
|
45
|
+
*/
|
|
46
|
+
const isDeparture = (data) => data['initiator'] === NetInitiator.NAVIGATION;
|
|
40
47
|
/** How often to re-read the buffer. Short enough to return promptly, long enough not to spin. */
|
|
41
48
|
const POLL_MS = 50;
|
|
42
49
|
const idOf = (data) => 'string' === typeof data['id'] ? data['id'] : undefined;
|
|
@@ -58,7 +65,7 @@ export function inFlightRequestIds(events) {
|
|
|
58
65
|
}
|
|
59
66
|
const open = [];
|
|
60
67
|
for (const e of events) {
|
|
61
|
-
if (e.type !== EventType.NET_PENDING || isDevTooling(e.data))
|
|
68
|
+
if (e.type !== EventType.NET_PENDING || isDevTooling(e.data) || isDeparture(e.data))
|
|
62
69
|
continue;
|
|
63
70
|
const id = idOf(e.data);
|
|
64
71
|
if (id !== undefined && !settled.has(id) && !open.includes(id))
|
|
@@ -12,6 +12,17 @@
|
|
|
12
12
|
* invalidates the prior snapshot (a diff across pages would be meaningless), so the next snapshot
|
|
13
13
|
* returns full — never a misleading cross-page delta.
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* Pure: was the prior snapshot of this scope both RECENT and SMALLER than this one? If so, the
|
|
17
|
+
* earlier reading was very likely taken mid-load — content arrived in the gap between the two calls
|
|
18
|
+
* — and any presence-grade conclusion drawn from that earlier snapshot ("this route renders
|
|
19
|
+
* nothing") is suspect. Silent otherwise: a stale prior or a page that shrank (or stayed the same
|
|
20
|
+
* size) is not this trap.
|
|
21
|
+
*/
|
|
22
|
+
export declare function snapshotGrowthNote(prior: {
|
|
23
|
+
tree: string;
|
|
24
|
+
atMs: number;
|
|
25
|
+
} | undefined, nextTree: string, nowMs: number, windowMs?: number): string | undefined;
|
|
15
26
|
export declare const SnapshotDeltaMode: {
|
|
16
27
|
readonly FULL: "full";
|
|
17
28
|
readonly DELTA: "delta";
|
|
@@ -45,11 +56,23 @@ export declare function snapshotDelta(prevTree: string | undefined, nextTree: st
|
|
|
45
56
|
/** Per-(session,scope,mode) last-snapshot cache. Route-aware: a route change invalidates the entry. */
|
|
46
57
|
export declare class SnapshotCache {
|
|
47
58
|
#private;
|
|
48
|
-
constructor(max?: number);
|
|
59
|
+
constructor(max?: number, now?: () => number);
|
|
49
60
|
/** True when an entry exists for this key (regardless of route match). */
|
|
50
61
|
has(key: string): boolean;
|
|
62
|
+
/** The clock this cache was constructed with — callers compare against it so `remember`'s
|
|
63
|
+
* timestamp and a caller's "now" can never drift onto two different clocks. */
|
|
64
|
+
now(): number;
|
|
51
65
|
/** Last tree for this key IF the route still matches; undefined when absent or route changed. */
|
|
52
66
|
recall(key: string, route: string): string | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* The last remembered tree for this key AND when it was remembered, IF the route still matches.
|
|
69
|
+
* Same route-gating as `recall` — a route change makes the prior entry meaningless for comparison,
|
|
70
|
+
* not just for diffing.
|
|
71
|
+
*/
|
|
72
|
+
priorEntry(key: string, route: string): {
|
|
73
|
+
tree: string;
|
|
74
|
+
atMs: number;
|
|
75
|
+
} | undefined;
|
|
53
76
|
remember(key: string, route: string, tree: string): void;
|
|
54
77
|
}
|
|
55
78
|
export declare function snapshotCacheKey(sessionId: string, scope: string, mode: string): string;
|
|
@@ -158,6 +158,37 @@ function focusedLine(tree) {
|
|
|
158
158
|
const text = withoutFocus(focused).trim();
|
|
159
159
|
return { id: REF_MARKER.exec(focused)?.[0]?.trim() ?? deltaKey(focused), text };
|
|
160
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* How recent a smaller prior snapshot has to be for its growth to be worth flagging (#787) — long
|
|
163
|
+
* enough to catch the "route committed, then hydrated" window the bug report describes (hundreds of
|
|
164
|
+
* ms up to a couple of seconds), short enough that an unrelated LATER snapshot of a page that simply
|
|
165
|
+
* grew through normal use (an infinite-scroll load, a live feed) doesn't get blamed on load timing.
|
|
166
|
+
*/
|
|
167
|
+
const GROWTH_NOTE_WINDOW_MS = 5000;
|
|
168
|
+
/** Non-empty lines only — a blank line in the tree text is formatting, not a node. */
|
|
169
|
+
function nodeCount(tree) {
|
|
170
|
+
return tree.split('\n').filter((line) => '' !== line.trim()).length;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Pure: was the prior snapshot of this scope both RECENT and SMALLER than this one? If so, the
|
|
174
|
+
* earlier reading was very likely taken mid-load — content arrived in the gap between the two calls
|
|
175
|
+
* — and any presence-grade conclusion drawn from that earlier snapshot ("this route renders
|
|
176
|
+
* nothing") is suspect. Silent otherwise: a stale prior or a page that shrank (or stayed the same
|
|
177
|
+
* size) is not this trap.
|
|
178
|
+
*/
|
|
179
|
+
export function snapshotGrowthNote(prior, nextTree, nowMs, windowMs = GROWTH_NOTE_WINDOW_MS) {
|
|
180
|
+
if (prior === undefined)
|
|
181
|
+
return undefined;
|
|
182
|
+
const elapsedMs = nowMs - prior.atMs;
|
|
183
|
+
if (elapsedMs < 0 || elapsedMs > windowMs)
|
|
184
|
+
return undefined;
|
|
185
|
+
const priorCount = nodeCount(prior.tree);
|
|
186
|
+
const nextCount = nodeCount(nextTree);
|
|
187
|
+
if (nextCount <= priorCount)
|
|
188
|
+
return undefined;
|
|
189
|
+
return (`this scope had ${String(priorCount)} nodes ${String(elapsedMs)}ms ago and has ${String(nextCount)} now — ` +
|
|
190
|
+
'content likely arrived after your last look; a presence conclusion drawn from that earlier snapshot may have been taken mid-load');
|
|
191
|
+
}
|
|
161
192
|
export const SnapshotDeltaMode = {
|
|
162
193
|
FULL: 'full',
|
|
163
194
|
DELTA: 'delta',
|
|
@@ -200,15 +231,30 @@ const DEFAULT_MAX_ENTRIES = 50;
|
|
|
200
231
|
export class SnapshotCache {
|
|
201
232
|
#map = new Map();
|
|
202
233
|
#max;
|
|
203
|
-
|
|
234
|
+
#now;
|
|
235
|
+
constructor(max = DEFAULT_MAX_ENTRIES, now = Date.now) {
|
|
204
236
|
this.#max = max;
|
|
237
|
+
this.#now = now;
|
|
205
238
|
}
|
|
206
239
|
/** True when an entry exists for this key (regardless of route match). */
|
|
207
240
|
has(key) {
|
|
208
241
|
return this.#map.has(key);
|
|
209
242
|
}
|
|
243
|
+
/** The clock this cache was constructed with — callers compare against it so `remember`'s
|
|
244
|
+
* timestamp and a caller's "now" can never drift onto two different clocks. */
|
|
245
|
+
now() {
|
|
246
|
+
return this.#now();
|
|
247
|
+
}
|
|
210
248
|
/** Last tree for this key IF the route still matches; undefined when absent or route changed. */
|
|
211
249
|
recall(key, route) {
|
|
250
|
+
return this.priorEntry(key, route)?.tree;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The last remembered tree for this key AND when it was remembered, IF the route still matches.
|
|
254
|
+
* Same route-gating as `recall` — a route change makes the prior entry meaningless for comparison,
|
|
255
|
+
* not just for diffing.
|
|
256
|
+
*/
|
|
257
|
+
priorEntry(key, route) {
|
|
212
258
|
const entry = this.#map.get(key);
|
|
213
259
|
if (entry === undefined || entry.route !== route)
|
|
214
260
|
return undefined;
|
|
@@ -216,7 +262,7 @@ export class SnapshotCache {
|
|
|
216
262
|
// (Map preserves insertion order; delete+set moves it to the most-recently-used end).
|
|
217
263
|
this.#map.delete(key);
|
|
218
264
|
this.#map.set(key, entry);
|
|
219
|
-
return entry.tree;
|
|
265
|
+
return { tree: entry.tree, atMs: entry.atMs };
|
|
220
266
|
}
|
|
221
267
|
remember(key, route, tree) {
|
|
222
268
|
// Re-insert to move the key to the most-recently-used end (a plain set keeps its old position).
|
|
@@ -226,7 +272,7 @@ export class SnapshotCache {
|
|
|
226
272
|
if (oldest !== undefined)
|
|
227
273
|
this.#map.delete(oldest);
|
|
228
274
|
}
|
|
229
|
-
this.#map.set(key, { route, tree });
|
|
275
|
+
this.#map.set(key, { route, tree, atMs: this.#now() });
|
|
230
276
|
}
|
|
231
277
|
}
|
|
232
278
|
/**
|
|
@@ -260,17 +306,23 @@ export function applySnapshotDelta(raw, opts, cache) {
|
|
|
260
306
|
: {};
|
|
261
307
|
const route = 'string' === typeof status['route'] ? status['route'] : '';
|
|
262
308
|
const key = snapshotCacheKey(opts.sessionId, opts.scope, opts.mode);
|
|
309
|
+
// Read the growth signal BEFORE remember() overwrites the entry it compares against — needed on
|
|
310
|
+
// both branches below (#787), not only the diff:true path, since the reporter's own repro used a
|
|
311
|
+
// plain snapshot the second time.
|
|
312
|
+
const priorEntry = cache.priorEntry(key, route);
|
|
313
|
+
const growthNote = snapshotGrowthNote(priorEntry, tree, cache.now());
|
|
314
|
+
const growth = growthNote === undefined ? {} : { growthWarning: growthNote };
|
|
263
315
|
if (!opts.diff) {
|
|
264
316
|
cache.remember(key, route, tree);
|
|
265
|
-
return
|
|
317
|
+
return { ...r, ...growth };
|
|
266
318
|
}
|
|
267
|
-
const prev =
|
|
319
|
+
const prev = priorEntry?.tree;
|
|
268
320
|
const hadEntry = cache.has(key);
|
|
269
321
|
cache.remember(key, route, tree);
|
|
270
322
|
const decision = snapshotDelta(prev, tree);
|
|
271
323
|
if (decision.mode === SnapshotDeltaMode.FULL) {
|
|
272
324
|
const reason = hadEntry ? 'route changed' : 'first snapshot for this route';
|
|
273
|
-
return { ...r, mode: SnapshotDeltaMode.FULL, reason };
|
|
325
|
+
return { ...r, mode: SnapshotDeltaMode.FULL, reason, ...growth };
|
|
274
326
|
}
|
|
275
327
|
// The walk stops at a node cap and returns a DOCUMENT-ORDER PREFIX, so two capped snapshots of a
|
|
276
328
|
// large page are identical whenever the change happened past the cap — and "unchanged" is then a
|
|
@@ -282,7 +334,13 @@ export function applySnapshotDelta(raw, opts, cache) {
|
|
|
282
334
|
// field is simply absent forever.
|
|
283
335
|
const moved = decision.focusChanged === undefined ? {} : { focusChanged: decision.focusChanged };
|
|
284
336
|
if (decision.mode === SnapshotDeltaMode.UNCHANGED) {
|
|
285
|
-
return {
|
|
337
|
+
return {
|
|
338
|
+
mode: SnapshotDeltaMode.UNCHANGED,
|
|
339
|
+
status: r['status'],
|
|
340
|
+
...moved,
|
|
341
|
+
...capped,
|
|
342
|
+
...growth,
|
|
343
|
+
};
|
|
286
344
|
}
|
|
287
345
|
const { changed, ...structuralDelta } = decision.delta;
|
|
288
346
|
const changedField = changed.length > 0 ? { changed, changedCount: changed.length } : {};
|
|
@@ -293,5 +351,6 @@ export function applySnapshotDelta(raw, opts, cache) {
|
|
|
293
351
|
status: r['status'],
|
|
294
352
|
...moved,
|
|
295
353
|
...capped,
|
|
354
|
+
...growth,
|
|
296
355
|
};
|
|
297
356
|
}
|
|
@@ -10,6 +10,7 @@ export declare const ReticleTool: {
|
|
|
10
10
|
readonly ACT_SEQUENCE: "reticle_act_sequence";
|
|
11
11
|
readonly ACT_AND_WAIT: "reticle_act_and_wait";
|
|
12
12
|
readonly OBSERVE: "reticle_observe";
|
|
13
|
+
readonly LINEAGE: "reticle_lineage";
|
|
13
14
|
readonly WAIT_FOR: "reticle_wait_for";
|
|
14
15
|
readonly NETWORK: "reticle_network";
|
|
15
16
|
readonly CONSOLE: "reticle_console";
|
package/dist/tools/tool-names.js
CHANGED
|
@@ -11,6 +11,7 @@ export const ReticleTool = {
|
|
|
11
11
|
ACT_SEQUENCE: FlowStepTool.ACT_SEQUENCE,
|
|
12
12
|
ACT_AND_WAIT: FlowStepTool.ACT_AND_WAIT,
|
|
13
13
|
OBSERVE: 'reticle_observe',
|
|
14
|
+
LINEAGE: 'reticle_lineage',
|
|
14
15
|
WAIT_FOR: 'reticle_wait_for',
|
|
15
16
|
NETWORK: 'reticle_network',
|
|
16
17
|
CONSOLE: 'reticle_console',
|
|
@@ -215,6 +215,26 @@ export const CORE_TOOL_NAMES = new Set([
|
|
|
215
215
|
// deleting the instruction from the lease and the pause hint — deletes the handback protocol,
|
|
216
216
|
// because there is nowhere else those two calls are ever named.
|
|
217
217
|
ReticleTool.SESSION,
|
|
218
|
+
// VERIFY is the only advertised tool that can CONCLUDE, and its absence was measurably the most
|
|
219
|
+
// expensive thing on this surface.
|
|
220
|
+
//
|
|
221
|
+
// Measured on the Layer B agent loop (bench/raw/agent-loop-results.json, Haiku 4.5, 25-turn cap):
|
|
222
|
+
// on the no-regression control — a CLEAN app, where the right answer is "nothing is wrong" —
|
|
223
|
+
// Playwright MCP concluded in 7 turns and Reticle burned all 25 and never answered, at 333k tokens
|
|
224
|
+
// against Playwright's 54k. That one cell was a quarter of Reticle's total cost across five
|
|
225
|
+
// scenarios. Removing it, Reticle is at parity on the other four and wins two outright.
|
|
226
|
+
//
|
|
227
|
+
// The cause is compositional rather than behavioural: every other tool in this set answers "here
|
|
228
|
+
// is more to look at". None of them can say "there is nothing more". On a healthy app — which is
|
|
229
|
+
// most runs — that is a loop with no exit, and 25 was just where the cap fell.
|
|
230
|
+
//
|
|
231
|
+
// `reticle_verify` is the exit. { action: "crawl" } drives every reachable control itself and
|
|
232
|
+
// reports the whole fault set in ONE call, and { action: "flows" } replays the saved flows with no
|
|
233
|
+
// model in the loop at all — the path Layer C measures at 271 tokens, deterministic, 0% flake.
|
|
234
|
+
// Both were built and neither was reachable: the same "a tool an agent must already know about is
|
|
235
|
+
// a tool that never gets called" argument that put INSPECT and FEEDBACK here, applied to the one
|
|
236
|
+
// tool that ends the loop.
|
|
237
|
+
ReticleTool.VERIFY,
|
|
218
238
|
]);
|
|
219
239
|
/**
|
|
220
240
|
* The extended surface: what `all` advertises BEYOND the default set.
|
|
@@ -269,8 +289,6 @@ export const EXTENDED_TOOL_NAMES = new Set([
|
|
|
269
289
|
ReticleTool.CLOCK,
|
|
270
290
|
ReticleTool.NETWORK_MOCK,
|
|
271
291
|
ReticleTool.STORAGE,
|
|
272
|
-
// Merged change/flows/affected/coverage/crawl — one name where three of these used to sit.
|
|
273
|
-
ReticleTool.VERIFY,
|
|
274
292
|
]);
|
|
275
293
|
/**
|
|
276
294
|
* The verify surface: one acting tool that returns a verdict, plus the two meta-tools that reach
|
package/dist/tools/tools.js
CHANGED
|
@@ -17,6 +17,8 @@ import { paginateQueryResult } from './query-paginate.js';
|
|
|
17
17
|
*/
|
|
18
18
|
const QUERY_BY_VALUES = Object.values(QueryBy);
|
|
19
19
|
const QUERY_BY_LIST = QUERY_BY_VALUES.join(' | ');
|
|
20
|
+
/** Every `by`/`value` shorthand says the same thing; the parameter name carries the rest. */
|
|
21
|
+
const QUERY_ALIAS = 'Predicate spelling of by/value.';
|
|
20
22
|
const queryByEnum = z.enum(QUERY_BY_VALUES);
|
|
21
23
|
import { CONTRACT_TOOLS } from './contract-tools.js';
|
|
22
24
|
import { DOMAIN_TOOLS } from '../domain/domain-tools.js';
|
|
@@ -42,6 +44,7 @@ import { sessionIdShape, commandOrThrow } from './tool-kit.js';
|
|
|
42
44
|
import { applyMerges } from './merge-tools.js';
|
|
43
45
|
import { ACT_TOOLS } from './act-tools.js';
|
|
44
46
|
import { OBSERVE_TOOLS } from './observe-tools.js';
|
|
47
|
+
import { LINEAGE_TOOLS } from './lineage-tools.js';
|
|
45
48
|
import { RECONCILE_TOOLS } from './reconcile-tools.js';
|
|
46
49
|
import { READ_TOOLS } from './read-tools.js';
|
|
47
50
|
import { LEASE_TOOLS } from './lease-tools.js';
|
|
@@ -117,6 +120,13 @@ export const RAW_TOOLS = [
|
|
|
117
120
|
})
|
|
118
121
|
.optional()
|
|
119
122
|
.describe("Present ONLY when `sessions` is empty: the same answer as `why`, executable. `command` is the LITERAL command to run, sourced from this project's own package.json scripts and lockfile — it is absent, never guessed, when the project declares no dev script. `action` is one of daemon_split | start_dev_server | run_init | open_app | reopen_app. `daemon_split` outranks the rest and means the app IS running and instrumented, on a DIFFERENT daemon than the one you are attached to — do not start or re-init anything, read `reason`."),
|
|
123
|
+
lastKnown: z
|
|
124
|
+
.object({
|
|
125
|
+
sessionId: z.string(),
|
|
126
|
+
url: z.string(),
|
|
127
|
+
})
|
|
128
|
+
.optional()
|
|
129
|
+
.describe('Present ONLY when `sessions` is empty AND a tab was connected to this daemon earlier: the last sessionId and URL that tab was on when it disappeared. A route that 500s tears the page down and the SDK never reconnects — this is the last thing we knew, not a route-500 verdict. An empty list with no lastKnown is a tab that never arrived.'),
|
|
120
130
|
},
|
|
121
131
|
handler: async (deps) => {
|
|
122
132
|
const provider = deps.realInput;
|
|
@@ -135,10 +145,14 @@ export const RAW_TOOLS = [
|
|
|
135
145
|
// The executable half. `why` is for the human reading the transcript; this is the one the
|
|
136
146
|
// agent acts on, so it never has to parse a paragraph to find a command inside it.
|
|
137
147
|
const next = deps.sessions.noSessionNextAction();
|
|
148
|
+
// The last tab, when one was here. Optional-call: test stubs of SessionManager predate it.
|
|
149
|
+
const known = deps.sessions.lastKnown?.();
|
|
150
|
+
const lastKnown = undefined === known ? undefined : { sessionId: known.id, url: known.url };
|
|
138
151
|
return {
|
|
139
152
|
sessions,
|
|
140
153
|
...(why === undefined ? {} : { why }),
|
|
141
154
|
...(next === undefined ? {} : { next_action: next }),
|
|
155
|
+
...(undefined === lastKnown ? {} : { lastKnown }),
|
|
142
156
|
};
|
|
143
157
|
}
|
|
144
158
|
return { sessions };
|
|
@@ -214,11 +228,15 @@ export const RAW_TOOLS = [
|
|
|
214
228
|
note: z
|
|
215
229
|
.string()
|
|
216
230
|
.optional()
|
|
217
|
-
.describe('
|
|
231
|
+
.describe('Why this result is not what it looks like. Four causes, all of which otherwise read as "the page is empty": a diff computed over a capped tree ("unchanged" is then partial); an interactive-mode tree emptied by leanness; a page whose elements all computed hidden; and an UNMOUNTED app, where the DOM under the scope holds almost nothing — that last one will not improve by waiting, so read reticle_console for an uncaught error instead.'),
|
|
218
232
|
scopeMissing: z
|
|
219
233
|
.boolean()
|
|
220
234
|
.optional()
|
|
221
235
|
.describe('True when a scope was given but resolved to nothing — the tree is EMPTY on purpose, not because the page is empty. Do not read an absent element as absent from the page; re-check the scope.'),
|
|
236
|
+
growthWarning: z
|
|
237
|
+
.string()
|
|
238
|
+
.optional()
|
|
239
|
+
.describe('Present when this same scope was smaller moments ago — content likely arrived after your last look, and a presence/absence conclusion drawn from that earlier snapshot may have been taken mid-load. Re-check rather than trusting the earlier read.'),
|
|
222
240
|
},
|
|
223
241
|
// async so a synchronous resolve() failure (no session connected) surfaces as a REJECTED promise —
|
|
224
242
|
// the handler contract every caller relies on — not a throw that escapes a direct invocation.
|
|
@@ -233,24 +251,24 @@ export const RAW_TOOLS = [
|
|
|
233
251
|
return commandOrThrow(deps, resolved.id, ReticleCommand.SNAPSHOT, {
|
|
234
252
|
scope: args['scope'],
|
|
235
253
|
mode,
|
|
236
|
-
}).then((raw) => withSizeCost(noteEmptyLeanTree(applySnapshotDelta(raw, {
|
|
254
|
+
}).then((raw) => withSizeCost(noteUnmountedRoot(noteHiddenPage(noteEmptyLeanTree(applySnapshotDelta(raw, {
|
|
237
255
|
sessionId: resolved.id,
|
|
238
256
|
scope: asString(args['scope']) ?? '',
|
|
239
257
|
mode,
|
|
240
258
|
diff: true === args['diff'],
|
|
241
|
-
}, SNAPSHOT_CACHE), mode)));
|
|
259
|
+
}, SNAPSHOT_CACHE), mode), mode), mode)));
|
|
242
260
|
},
|
|
243
261
|
},
|
|
244
262
|
{
|
|
245
263
|
name: ReticleTool.QUERY,
|
|
246
264
|
example: { by: 'testid', value: 'todo-list' },
|
|
247
|
-
description: `Find elements by Testing-Library semantics, INCLUDING open shadow roots — \`count_only:true\` gives just the count (~30x smaller); \`limit\` caps descriptors. Pass \`by\` (${QUERY_BY_LIST}) and \`value\` (the query string). Returns matching refs + descriptors + visibility. Pass \`attrs:["href"]\` to project attributes (link/image URLs) onto each match. Pass \`limit\` to cap descriptors (broad role queries can be large) or \`count_only:true\` for just the match count — both cut tokens. On zero matches, also returns hint:{ route, presentRegions[], knownEmptyState } so you can distinguish an empty state from a missing element WITHOUT taking a snapshot. ASKING SEVERAL QUESTIONS ABOUT THE PAGE? One reticle_snapshot answers them all at once — a run of queries costs a round trip each and returns what the snapshot already held.`,
|
|
265
|
+
description: `Find elements by Testing-Library semantics, INCLUDING open shadow roots — \`count_only:true\` gives just the count (~30x smaller); \`limit\` caps descriptors. Pass \`by\` (${QUERY_BY_LIST}) and \`value\` (the query string). Returns matching refs + descriptors + visibility. Pass \`attrs:["href"]\` to project attributes (link/image URLs) onto each match. Pass \`limit\` to cap descriptors (broad role queries can be large) or \`count_only:true\` for just the match count — both cut tokens. On zero matches, also returns hint:{ route, presentRegions[], knownEmptyState, nameNearMiss[] } so you can distinguish an empty state from a missing element WITHOUT taking a snapshot — nameNearMiss carries the labels that role really has when an exact role+name query just missed. ASKING SEVERAL QUESTIONS ABOUT THE PAGE? One reticle_snapshot answers them all at once — a run of queries costs a round trip each and returns what the snapshot already held.`,
|
|
248
266
|
inputSchema: {
|
|
249
267
|
// Constrained to the enum, NOT z.string(). A free string let `by:'css'` through to the
|
|
250
268
|
// browser's `default: return []`, so an unsupported strategy answered "0 matches" — which
|
|
251
269
|
// reads as "the element is not on the page". Measured on a live page: by:'css' value:'body'
|
|
252
270
|
// returned count 0. A false negative is the one answer this product must never invent.
|
|
253
|
-
by: queryByEnum.optional().describe(
|
|
271
|
+
by: queryByEnum.optional().describe('Query strategy.'),
|
|
254
272
|
value: z
|
|
255
273
|
.string()
|
|
256
274
|
.optional()
|
|
@@ -280,12 +298,15 @@ export const RAW_TOOLS = [
|
|
|
280
298
|
.describe('Return just { count } (no element descriptors) — use when you only need "how many match?" and not their refs.'),
|
|
281
299
|
// The predicate's spelling, accepted as an alias for by/value so the shape an agent learns from
|
|
282
300
|
// act_and_wait/assert also works here. See query-shape.ts.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
301
|
+
// Six copies of one sentence, re-sent every turn. The parameter NAME already says which
|
|
302
|
+
// strategy it selects, and they sit in a block together, so each only has to say it is the
|
|
303
|
+
// predicate spelling of by/value.
|
|
304
|
+
testid: z.string().optional().describe(QUERY_ALIAS),
|
|
305
|
+
text: z.string().optional().describe(QUERY_ALIAS),
|
|
306
|
+
role: z.string().optional().describe(QUERY_ALIAS),
|
|
307
|
+
label: z.string().optional().describe(QUERY_ALIAS),
|
|
308
|
+
placeholder: z.string().optional().describe(QUERY_ALIAS),
|
|
309
|
+
alt: z.string().optional().describe(QUERY_ALIAS),
|
|
289
310
|
...sessionIdShape,
|
|
290
311
|
},
|
|
291
312
|
outputSchema: {
|
|
@@ -355,6 +376,12 @@ export const RAW_TOOLS = [
|
|
|
355
376
|
.passthrough()
|
|
356
377
|
.optional()
|
|
357
378
|
.describe("Present when a TEXT search missed but the string IS on the page, split across this container's children. Retry as { scope: <ref>, self: true } — no text query can match a string no single element owns."),
|
|
379
|
+
// Declared here for the same reason presentRegions had to be: the browser emits it, and an
|
|
380
|
+
// undeclared field is stripped from structuredContent without a word.
|
|
381
|
+
nameNearMiss: z
|
|
382
|
+
.array(z.string())
|
|
383
|
+
.optional()
|
|
384
|
+
.describe('Present when a ROLE+NAME search missed and that role DOES carry a nearly-matching name — role+name is exact, so "Mesh" does not find "2 Mesh". Retry with one of these spellings; no snapshot needed.'),
|
|
358
385
|
})
|
|
359
386
|
.optional()
|
|
360
387
|
.describe('Present only on zero matches — tells you what IS on the page so you can diagnose the miss.'),
|
|
@@ -516,6 +543,7 @@ export const RAW_TOOLS = [
|
|
|
516
543
|
...BROWSER_TOOLS,
|
|
517
544
|
...ACT_TOOLS,
|
|
518
545
|
...OBSERVE_TOOLS,
|
|
546
|
+
...LINEAGE_TOOLS,
|
|
519
547
|
...READ_TOOLS,
|
|
520
548
|
...LEASE_TOOLS,
|
|
521
549
|
// reticle_feedback — the agent reports that RETICLE failed (not that the app did). See feedback-tools.ts.
|
|
@@ -627,4 +655,85 @@ function noteEmptyLeanTree(result, mode) {
|
|
|
627
655
|
`by testid rather than by role.`,
|
|
628
656
|
};
|
|
629
657
|
}
|
|
658
|
+
/**
|
|
659
|
+
* An empty tree on a page full of elements is the same claim, from the other cause.
|
|
660
|
+
*
|
|
661
|
+
* `noteEmptyLeanTree` covers leanness and returns early for every other mode, so a `full` snapshot
|
|
662
|
+
* that comes back `{ tree: "", nodes: 0 }` says nothing at all — and that is the shape #672 was
|
|
663
|
+
* reported as: 44 buttons and 12 textboxes on the page, every one computing hidden, both modes
|
|
664
|
+
* empty. The reporter spent about six tool calls and a large console dump establishing the page was
|
|
665
|
+
* fine and the snapshot was wrong, then drove the flow off `reticle_query` refs, which is a
|
|
666
|
+
* workaround no tool description mentions.
|
|
667
|
+
*
|
|
668
|
+
* So the note names the count, says plainly that this is not an empty page, and hands over the path
|
|
669
|
+
* that does work. It does NOT diagnose why the page is hidden: the walk knows what it skipped and
|
|
670
|
+
* cannot know why, and a note that guessed would be the same kind of overconfident answer as the
|
|
671
|
+
* empty tree it replaces.
|
|
672
|
+
*/
|
|
673
|
+
/**
|
|
674
|
+
* How much DOM there is under the mount container, when the tree is empty and nothing was skipped.
|
|
675
|
+
*
|
|
676
|
+
* A handful of elements is a container with nothing in it. React's own root div counts as one, and a
|
|
677
|
+
* wrapper or two is ordinary, so this is deliberately not `=== 0`.
|
|
678
|
+
*/
|
|
679
|
+
const UNMOUNTED_ROOT_MAX_ELEMENTS = 3;
|
|
680
|
+
/**
|
|
681
|
+
* The third cause of an empty tree, and the one the walk cannot see: the app is not mounted.
|
|
682
|
+
*
|
|
683
|
+
* Reported from the field with the react-three-fiber crash. The source-mapping stamp threw inside
|
|
684
|
+
* R3F's commit phase, React unmounted the entire tree, and the page went white — and the snapshot
|
|
685
|
+
* answered `{ tree: "", nodes: 0 }`, which is also what a page that has not rendered yet answers.
|
|
686
|
+
* The reporter spent a diagnosis pass separating the two, and said that detecting a dead root would
|
|
687
|
+
* have pointed straight at the cause.
|
|
688
|
+
*
|
|
689
|
+
* The other two notes here explain an empty tree by what the WALK passed over. This one cannot: a
|
|
690
|
+
* walk that visited nothing has nothing to have skipped. It reads the DOM count instead, which is
|
|
691
|
+
* why the browser now reports it, and it fires only when the skip counts are silent — a page whose
|
|
692
|
+
* elements were all hidden has plenty of DOM and belongs to `noteHiddenPage`.
|
|
693
|
+
*
|
|
694
|
+
* Like both of its neighbours it names a fact and hands over the next read, and does not diagnose:
|
|
695
|
+
* "not mounted" is certain from the count, WHY is not, and the console is where the answer is.
|
|
696
|
+
*/
|
|
697
|
+
function noteUnmountedRoot(result, mode) {
|
|
698
|
+
if (SnapshotMode.STATUS === mode)
|
|
699
|
+
return result;
|
|
700
|
+
const row = asRecord(result);
|
|
701
|
+
if (0 !== asNumber(row['nodes']))
|
|
702
|
+
return result;
|
|
703
|
+
if (row['note'] !== undefined)
|
|
704
|
+
return result;
|
|
705
|
+
const elements = asNumber(row['domElements']);
|
|
706
|
+
if (elements === undefined || elements > UNMOUNTED_ROOT_MAX_ELEMENTS)
|
|
707
|
+
return result;
|
|
708
|
+
return {
|
|
709
|
+
...row,
|
|
710
|
+
note: `the tree is empty because there is almost nothing in the DOM: ${String(elements)} ` +
|
|
711
|
+
`element(s) under this scope. That is a mount container with nothing rendered into it, not a ` +
|
|
712
|
+
`page whose contents were skipped — so the app is UNMOUNTED rather than slow, and waiting ` +
|
|
713
|
+
`will not change it. An app that was rendering and then stopped has usually thrown: read ` +
|
|
714
|
+
`reticle_console for an uncaught error, and check anything that runs inside a framework ` +
|
|
715
|
+
`commit phase. If the app has genuinely not started yet, load it and snapshot again.`,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
function noteHiddenPage(result, mode) {
|
|
719
|
+
if (SnapshotMode.STATUS === mode)
|
|
720
|
+
return result;
|
|
721
|
+
const row = asRecord(result);
|
|
722
|
+
if (0 !== asNumber(row['nodes']))
|
|
723
|
+
return result;
|
|
724
|
+
// A note already there is the more specific one (leanness), and two explanations for one empty
|
|
725
|
+
// tree is worse than the better of them alone.
|
|
726
|
+
if (row['note'] !== undefined)
|
|
727
|
+
return result;
|
|
728
|
+
const skipped = asNumber(row['hiddenSkipped']) ?? 0;
|
|
729
|
+
if (0 === skipped)
|
|
730
|
+
return result;
|
|
731
|
+
return {
|
|
732
|
+
...row,
|
|
733
|
+
note: `this is NOT an empty page: ${String(skipped)} subtree(s) were skipped because their root ` +
|
|
734
|
+
`computed hidden (aria-hidden, the hidden attribute, or display:none), which is what left the ` +
|
|
735
|
+
`tree empty. If the page is plainly rendered, the visibility computation is the thing that is ` +
|
|
736
|
+
`wrong rather than the app — drive it by reticle_query refs, which do not depend on this walk.`,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
630
739
|
export const TOOLS = applyMerges(RAW_TOOLS, MERGE_PLANS, RETIRED_FROM_SURFACE);
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
export declare const SUPPRESS_VERIFY_NEXT_ENV = "RETICLE_SUPPRESS_VERIFY_NEXT";
|
|
30
30
|
/** What the agent must supply itself. Phrased as an instruction so it cannot be sent verbatim. */
|
|
31
31
|
export declare const UNTIL_PLACEHOLDER = "<name the consequence this action causes>";
|
|
32
|
-
|
|
32
|
+
interface VerifyNextBaton {
|
|
33
33
|
/** The fact that produced this, in the agent's own terms. */
|
|
34
34
|
why: string;
|
|
35
35
|
/** A call, ready to make once `until` is filled in. */
|
|
@@ -51,3 +51,4 @@ export declare function verifyNextBaton(prose: string, lastAct: {
|
|
|
51
51
|
action?: string | undefined;
|
|
52
52
|
ref?: string | undefined;
|
|
53
53
|
}): VerifyNextBaton;
|
|
54
|
+
export {};
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* stale registry cache and reinstall the skew being fixed. Measured once as pnpm taking 2.2.1 while
|
|
12
12
|
* npm took 2.3.0 in the next project over.
|
|
13
13
|
*/
|
|
14
|
-
import { type PackageManager } from '
|
|
14
|
+
import { type PackageManager } from '@reticlehq/init';
|
|
15
15
|
export { reticleDepsOf } from './reticle-deps.js';
|
|
16
16
|
/** The install to run, or null when this project has nothing of ours to sync. */
|
|
17
17
|
export declare function sdkSyncCommand(pm: PackageManager, packages: readonly string[], version: string): {
|