@reticlehq/server 2.3.0 → 2.5.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 +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +105 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse.d.ts +143 -0
- package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
- package/dist/{cli-port.js → cli/cli-port.js} +29 -8
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +182 -93
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.d.ts +0 -6
- package/dist/flows/decision.js +64 -8
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +67 -13
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.d.ts +17 -0
- package/dist/init/capabilities.js +54 -0
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +26 -0
- package/dist/init/detect.js +78 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +39 -0
- package/dist/init/next-patch.js +196 -0
- package/dist/init/node-io.js +48 -6
- package/dist/init/patch-kind.d.ts +20 -0
- package/dist/init/patch-kind.js +10 -0
- package/dist/init/plan-framework.d.ts +50 -0
- package/dist/init/plan-framework.js +341 -0
- package/dist/init/plan.d.ts +119 -6
- package/dist/init/plan.js +230 -131
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +22 -0
- package/dist/init/run.js +393 -28
- package/dist/init/slash-command.d.ts +27 -0
- package/dist/init/slash-command.js +61 -0
- package/dist/init/snippets.d.ts +41 -3
- package/dist/init/snippets.js +185 -10
- package/dist/init/vite-config.d.ts +4 -10
- package/dist/init/vite-config.js +15 -7
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.d.ts +15 -0
- package/dist/input/driven-redaction.js +76 -0
- package/dist/input/network-detail.d.ts +9 -3
- package/dist/input/network-detail.js +43 -23
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +58 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +20 -2
- package/dist/session/session.js +76 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +85 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/cli-parse.d.ts +0 -140
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -35
- package/dist/mcp-proxy.js +0 -190
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* Observe / wait / assert tools — reticle_observe, reticle_wait_for, reticle_assert, reticle_network,
|
|
3
3
|
* reticle_console, reticle_animations. Split out of tools.ts; assembled back via...OBSERVE_TOOLS.
|
|
4
4
|
*/
|
|
5
|
+
import { noteEmptyRead } from './observed-nothing.js';
|
|
5
6
|
import { z } from 'zod';
|
|
6
|
-
import {
|
|
7
|
+
import { aliasParam } from './alias-args.js';
|
|
8
|
+
import { CONSOLE_LEVELS, ReticleCommand, DEFAULT_ASSERT_TIMEOUT_MS, PredicateKind, } from '@reticlehq/core';
|
|
7
9
|
import { ReticleTool } from './tool-names.js';
|
|
8
10
|
import { buildReactionReport } from '../events/reaction.js';
|
|
9
11
|
import { findContradictions } from '../events/contradictions.js';
|
|
@@ -54,11 +56,21 @@ function lastActSourceOnFailure(session, pass) {
|
|
|
54
56
|
* really do interleave; it is only redundant at this boundary.
|
|
55
57
|
*/
|
|
56
58
|
function withoutConstantSessionId(event) {
|
|
57
|
-
if (typeof event !== 'object' ||
|
|
59
|
+
if (typeof event !== 'object' || null === event)
|
|
58
60
|
return event;
|
|
59
61
|
const { sessionId: _sessionId, ...rest } = event;
|
|
60
62
|
return rest;
|
|
61
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* The console levels, derived from core's console EventTypes — see CONSOLE_LEVELS.
|
|
66
|
+
*
|
|
67
|
+
* A free string here filtered by building `console.${level}`, so `level:'ERROR'` or `level:'fatal'`
|
|
68
|
+
* matched nothing and returned an empty log list. On a page that HAS logs the zero-match hint saves
|
|
69
|
+
* it (it reports which levels are present), but on a quiet page the answer is identical to a genuine
|
|
70
|
+
* all-clear. Refusing the value outright is the only reading with no ambiguity.
|
|
71
|
+
*/
|
|
72
|
+
const CONSOLE_LEVEL_LIST = CONSOLE_LEVELS.join(' | ');
|
|
73
|
+
const consoleLevelEnum = z.enum(CONSOLE_LEVELS);
|
|
62
74
|
export const OBSERVE_TOOLS = [
|
|
63
75
|
{
|
|
64
76
|
name: ReticleTool.OBSERVE,
|
|
@@ -180,10 +192,12 @@ export const OBSERVE_TOOLS = [
|
|
|
180
192
|
},
|
|
181
193
|
{
|
|
182
194
|
name: ReticleTool.WAIT_FOR,
|
|
183
|
-
example: { predicate: { kind:
|
|
195
|
+
example: { predicate: { kind: PredicateKind.STATE, path: 'todos.length', equals: 3 } },
|
|
184
196
|
description: 'Block until a predicate is satisfied (or already true in the recent buffer), else time out. Returns matching evidence or a near-miss diagnosis. By default it only counts events since your last act, so a signal buffered BEFORE the action can never fake a pass; pass `since` (an observe/act cursor) to widen or narrow that window explicitly.',
|
|
185
197
|
inputSchema: {
|
|
186
|
-
predicate: PredicateSchema.describe('Predicate to wait for: { signal }, { net }, { element }, { kind: "net", ok: false } (assert on the OUTCOME — the honest field for IPC, which has no status code), { kind: "state", store, path, equals } (assert a registered store\'s value directly — the source of truth no DOM read can reach; equals takes a literal or { $gte | $contains | $length } pattern), { kind: "settled", quietMs } (deterministic network + DOM idle — prefer this over a fixed sleep), or a combination via allOf/anyOf.'),
|
|
198
|
+
predicate: PredicateSchema.optional().describe('Predicate to wait for: { signal }, { net }, { element }, { kind: "net", ok: false } (assert on the OUTCOME — the honest field for IPC, which has no status code), { kind: "state", store, path, equals } (assert a registered store\'s value directly — the source of truth no DOM read can reach; equals takes a literal or { $gte | $contains | $length } pattern), { kind: "settled", quietMs } (deterministic network + DOM idle — prefer this over a fixed sleep), or a combination via allOf/anyOf.'),
|
|
199
|
+
// Same concept, the neighbouring tool's name. See alias-args.ts.
|
|
200
|
+
until: PredicateSchema.optional().describe("Alias for `predicate` (act_and_wait's name)."),
|
|
187
201
|
timeout_ms: z.number().optional().describe('Maximum wait in milliseconds. Default: 4000.'),
|
|
188
202
|
since: z
|
|
189
203
|
.number()
|
|
@@ -218,7 +232,8 @@ export const OBSERVE_TOOLS = [
|
|
|
218
232
|
},
|
|
219
233
|
handler: async (deps, args) => {
|
|
220
234
|
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
221
|
-
|
|
235
|
+
// `until` is act_and_wait's name for this — see alias-args.ts.
|
|
236
|
+
const predicate = PredicateSchema.parse(aliasParam(args, 'predicate', ['until'])['predicate']);
|
|
222
237
|
// Honesty: explicit since wins; else default to the last act's cursor; else the whole buffer.
|
|
223
238
|
const since = asNumber(args['since']) ?? session.lastAct.cursor() ?? 0;
|
|
224
239
|
const verdict = await waitForPredicate(session, predicate, asNumber(args['timeout_ms']) ?? DEFAULT_ASSERT_TIMEOUT_MS, since);
|
|
@@ -234,10 +249,11 @@ export const OBSERVE_TOOLS = [
|
|
|
234
249
|
},
|
|
235
250
|
{
|
|
236
251
|
name: ReticleTool.ASSERT,
|
|
237
|
-
example: { predicate: { kind:
|
|
252
|
+
example: { predicate: { kind: PredicateKind.SIGNAL, name: 'todos:loaded' } },
|
|
238
253
|
description: 'Evaluate a predicate (optionally waiting up to timeout_ms). Returns { pass, evidence, failureReason? }. The end of every verify loop. Prefer a { signal } or { net } consequence over { element }/{ text } presence — a passing presence-only assertion returns `advice` because a wrong/healed element can fake it. By default it only counts events since your last act, so a stale buffered signal can never fake a pass; pass `since` (an observe/act cursor) to set the window explicitly.',
|
|
239
254
|
inputSchema: {
|
|
240
|
-
predicate: PredicateSchema.describe('Predicate to evaluate: { signal }, { net }, { element } or a combination.'),
|
|
255
|
+
predicate: PredicateSchema.optional().describe('Predicate to evaluate: { signal }, { net }, { element } or a combination.'),
|
|
256
|
+
until: PredicateSchema.optional().describe("Alias for `predicate` (act_and_wait's name)."),
|
|
241
257
|
timeout_ms: z
|
|
242
258
|
.number()
|
|
243
259
|
.optional()
|
|
@@ -287,7 +303,8 @@ export const OBSERVE_TOOLS = [
|
|
|
287
303
|
},
|
|
288
304
|
handler: async (deps, args) => {
|
|
289
305
|
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
290
|
-
|
|
306
|
+
// `until` is act_and_wait's name for this — see alias-args.ts.
|
|
307
|
+
const predicate = PredicateSchema.parse(aliasParam(args, 'predicate', ['until'])['predicate']);
|
|
291
308
|
const timeout = asNumber(args['timeout_ms']) ?? 0;
|
|
292
309
|
// Honesty: explicit since wins; else default to the last act's cursor; else the whole buffer.
|
|
293
310
|
const since = asNumber(args['since']) ?? session.lastAct.cursor() ?? 0;
|
|
@@ -306,7 +323,7 @@ export const OBSERVE_TOOLS = [
|
|
|
306
323
|
: assertsDerivedIpcStatus(predicate)
|
|
307
324
|
? { advice: DERIVED_IPC_STATUS_ADVICE }
|
|
308
325
|
: {};
|
|
309
|
-
const { decision, contradictions, coverage } = await assertVerdict(session, predicate, verdict.pass, since);
|
|
326
|
+
const { decision, contradictions, coverage } = await assertVerdict(session, predicate, verdict.pass, since, verdict.inconclusive);
|
|
310
327
|
return withControl(session, {
|
|
311
328
|
...decision,
|
|
312
329
|
...verdict,
|
|
@@ -374,7 +391,7 @@ export const OBSERVE_TOOLS = [
|
|
|
374
391
|
const method = asString(args['method']);
|
|
375
392
|
const urlContains = asString(args['urlContains']);
|
|
376
393
|
const status = asNumber(args['status']);
|
|
377
|
-
const ok = typeof args['ok']
|
|
394
|
+
const ok = 'boolean' === typeof args['ok'] ? args['ok'] : undefined;
|
|
378
395
|
const limit = asNumber(args['limit']);
|
|
379
396
|
const buffer = bufferEnvelope(session);
|
|
380
397
|
// Completed calls + unresolved in-flight requests (a hung request shows as pending).
|
|
@@ -385,18 +402,21 @@ export const OBSERVE_TOOLS = [
|
|
|
385
402
|
}));
|
|
386
403
|
const matched = allNet.filter((e) => matchNet(e, method, urlContains, status, ok));
|
|
387
404
|
// zero-match filter returns what DID fire, not a bare [].
|
|
388
|
-
if (matched.length
|
|
405
|
+
if (0 === matched.length && allNet.length > 0) {
|
|
389
406
|
return withSizeCost({ calls: matched, hint: netEmptyHint(allNet), ...buffer });
|
|
390
407
|
}
|
|
391
408
|
// Default the cap so an omitted `limit` can't dump a whole flooded session (since defaults to 0).
|
|
392
409
|
const { events: budgeted, droppedOldest } = applyEventBudget(matched, limit ?? DEFAULT_QUERY_LIMIT);
|
|
393
410
|
const calls = budgeted.map(projectNetCall);
|
|
394
|
-
|
|
411
|
+
// A zero-match FILTER already reports what did fire (netEmptyHint above). Zero calls at all
|
|
412
|
+
// fell through as a bare `[]`, which is indistinguishable from an observer that is not
|
|
413
|
+
// recording — and those need opposite responses. Say the look happened.
|
|
414
|
+
return withSizeCost(noteEmptyRead({
|
|
395
415
|
calls,
|
|
396
416
|
...(droppedOldest > 0 ? { total: matched.length, droppedOldest } : {}),
|
|
397
417
|
...bodiesNotCaptured(calls),
|
|
398
418
|
...buffer,
|
|
399
|
-
});
|
|
419
|
+
}, 'calls', { noun: 'network calls' }));
|
|
400
420
|
},
|
|
401
421
|
},
|
|
402
422
|
{
|
|
@@ -404,10 +424,9 @@ export const OBSERVE_TOOLS = [
|
|
|
404
424
|
example: { level: 'error' },
|
|
405
425
|
description: 'Console/error log. Fast path for "were there any errors during this flow?". When a level filter matches nothing, returns a `hint` { totalInWindow, byLevel } so 0 errors is distinguishable from a silent page.',
|
|
406
426
|
inputSchema: {
|
|
407
|
-
level:
|
|
408
|
-
.string()
|
|
427
|
+
level: consoleLevelEnum
|
|
409
428
|
.optional()
|
|
410
|
-
.describe(
|
|
429
|
+
.describe(`Log level filter: ${CONSOLE_LEVEL_LIST}. Omit to return all levels.`),
|
|
411
430
|
since: z
|
|
412
431
|
.number()
|
|
413
432
|
.optional()
|
|
@@ -451,15 +470,19 @@ export const OBSERVE_TOOLS = [
|
|
|
451
470
|
})).filter(isConsoleEvent);
|
|
452
471
|
const matched = allConsole.filter((e) => matchConsole(e, level));
|
|
453
472
|
// zero matches at this level → report what levels ARE present (not a bare []).
|
|
454
|
-
if (matched.length
|
|
473
|
+
if (0 === matched.length && allConsole.length > 0) {
|
|
455
474
|
return withSizeCost({ logs: matched, hint: consoleEmptyHint(allConsole), ...buffer });
|
|
456
475
|
}
|
|
457
476
|
// Default the cap so an omitted `limit` can't dump a whole flooded session (since defaults to 0).
|
|
458
477
|
const { events: budgeted, droppedOldest } = applyEventBudget(matched, limit ?? DEFAULT_QUERY_LIMIT);
|
|
459
478
|
const logs = budgeted.map(projectConsoleLog);
|
|
460
|
-
|
|
479
|
+
// Say the look HAPPENED when it found nothing. A quiet page and a dead console observer both
|
|
480
|
+
// produced `{ logs: [] }`, and "no console errors" is the claim agents lean on most — see
|
|
481
|
+
// observed-nothing.ts, whose own header names this exact case and which every other array-
|
|
482
|
+
// returning read here was already wired to.
|
|
483
|
+
return withSizeCost(noteEmptyRead(droppedOldest > 0
|
|
461
484
|
? { logs, total: matched.length, droppedOldest, ...buffer }
|
|
462
|
-
: { logs, ...buffer });
|
|
485
|
+
: { logs, ...buffer }, 'logs', { noun: 'console lines' }));
|
|
463
486
|
},
|
|
464
487
|
},
|
|
465
488
|
{
|
|
@@ -469,6 +492,17 @@ export const OBSERVE_TOOLS = [
|
|
|
469
492
|
outputSchema: {
|
|
470
493
|
animations: z.array(z.unknown()),
|
|
471
494
|
},
|
|
472
|
-
handler: (deps, args) =>
|
|
495
|
+
handler: async (deps, args) => {
|
|
496
|
+
const result = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.ANIMATIONS, {});
|
|
497
|
+
// A page with nothing animating and an observer that is not watching both return `[]`, and the
|
|
498
|
+
// second means the run should not be trusted. Say the look happened.
|
|
499
|
+
return isPlainRecord(result)
|
|
500
|
+
? noteEmptyRead(result, 'animations', { noun: 'animations running or completed' })
|
|
501
|
+
: result;
|
|
502
|
+
},
|
|
473
503
|
},
|
|
474
504
|
];
|
|
505
|
+
/** Narrow a command result to something noteEmptyRead can annotate. */
|
|
506
|
+
function isPlainRecord(value) {
|
|
507
|
+
return 'object' === typeof value && value !== null && !Array.isArray(value);
|
|
508
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Say that the look HAPPENED, when the look found nothing.
|
|
3
|
+
*
|
|
4
|
+
* A quiet page and a dead observer produce the same JSON: `[]`. Reported across seven tools in a
|
|
5
|
+
* field sweep — network with 0 calls, console with 0 logs, reconcile comparing 0, crawl with 0 steps,
|
|
6
|
+
* an empty message inbox — none of which could be told apart from the observer not working. Those
|
|
7
|
+
* need opposite responses: one means "the app did nothing", the other means "stop trusting this run".
|
|
8
|
+
*
|
|
9
|
+
* One shared shape rather than a field per tool: an empty read states the window it watched. "I
|
|
10
|
+
* watched 2000ms and saw nothing" is a finding. `[]` is an absence of information wearing the same
|
|
11
|
+
* clothes as one.
|
|
12
|
+
*/
|
|
13
|
+
interface EmptyReadContext {
|
|
14
|
+
/** The observation window, when the read has one. */
|
|
15
|
+
windowMs?: number;
|
|
16
|
+
/** What was being counted, in the agent's words: "console lines", "network calls". */
|
|
17
|
+
noun: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Add `observed: true` and a note when `key` holds an EMPTY array. Returns the result untouched
|
|
21
|
+
* otherwise — including when it is a refusal, where "I observed nothing" would be actively
|
|
22
|
+
* misleading on top of an error that already explains itself.
|
|
23
|
+
*/
|
|
24
|
+
export declare function noteEmptyRead(result: Record<string, unknown>, key: string, context: EmptyReadContext): Record<string, unknown>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Say that the look HAPPENED, when the look found nothing.
|
|
3
|
+
*
|
|
4
|
+
* A quiet page and a dead observer produce the same JSON: `[]`. Reported across seven tools in a
|
|
5
|
+
* field sweep — network with 0 calls, console with 0 logs, reconcile comparing 0, crawl with 0 steps,
|
|
6
|
+
* an empty message inbox — none of which could be told apart from the observer not working. Those
|
|
7
|
+
* need opposite responses: one means "the app did nothing", the other means "stop trusting this run".
|
|
8
|
+
*
|
|
9
|
+
* One shared shape rather than a field per tool: an empty read states the window it watched. "I
|
|
10
|
+
* watched 2000ms and saw nothing" is a finding. `[]` is an absence of information wearing the same
|
|
11
|
+
* clothes as one.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Add `observed: true` and a note when `key` holds an EMPTY array. Returns the result untouched
|
|
15
|
+
* otherwise — including when it is a refusal, where "I observed nothing" would be actively
|
|
16
|
+
* misleading on top of an error that already explains itself.
|
|
17
|
+
*/
|
|
18
|
+
export function noteEmptyRead(result, key, context) {
|
|
19
|
+
if ('string' === typeof result['error'])
|
|
20
|
+
return result;
|
|
21
|
+
const value = result[key];
|
|
22
|
+
if (!Array.isArray(value) || value.length > 0)
|
|
23
|
+
return result;
|
|
24
|
+
if (result['note'] !== undefined)
|
|
25
|
+
return result;
|
|
26
|
+
const window = context.windowMs === undefined ? '' : ` over the last ${String(context.windowMs)}ms`;
|
|
27
|
+
return {
|
|
28
|
+
...result,
|
|
29
|
+
// The distinction the field sweep could not make: this is a RESULT, not a failure to look.
|
|
30
|
+
observed: true,
|
|
31
|
+
note: `no ${context.noun}${window} — the observation ran and found none, which is a result, not a missing reading`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* number that is silently wrong. `count` survives because it is a scalar. Trust it over the array.
|
|
20
20
|
*/
|
|
21
21
|
export function paginateQueryResult(result, limit, countOnly) {
|
|
22
|
-
if (typeof result !== 'object' ||
|
|
22
|
+
if (typeof result !== 'object' || null === result)
|
|
23
23
|
return result;
|
|
24
24
|
const record = result;
|
|
25
25
|
const elements = record['elements'];
|
|
@@ -28,7 +28,7 @@ export function paginateQueryResult(result, limit, countOnly) {
|
|
|
28
28
|
const counted = record['count'];
|
|
29
29
|
// Older browser SDKs (and the zero-match hint path) may not carry `count`; fall back rather than
|
|
30
30
|
// report nothing, but never prefer the array when the browser told us the real number.
|
|
31
|
-
const total = typeof counted
|
|
31
|
+
const total = 'number' === typeof counted ? counted : elements.length;
|
|
32
32
|
const truncatedOnWire = total > elements.length;
|
|
33
33
|
if (countOnly) {
|
|
34
34
|
const { elements: _dropped, ...rest } = record;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accepting the element-query shape the rest of the surface teaches.
|
|
3
|
+
*
|
|
4
|
+
* `reticle_query` asks for a strategy and a value — `{ by: 'text', value: 'Deploy' }`. Every
|
|
5
|
+
* predicate asks for named fields — `{ text: 'Deploy' }`, `{ testid: 'submit' }` — and that is where
|
|
6
|
+
* an agent meets element queries most often (`act_and_wait { until: { kind:'element', query } }`,
|
|
7
|
+
* `reticle_assert`, `wait_for`). Carrying the predicate's spelling to `reticle_query` earns
|
|
8
|
+
* `-32602: Unknown parameter for reticle_query: text`.
|
|
9
|
+
*
|
|
10
|
+
* It is a small thing that lands in an expensive place: of the sessions that called any tool in a
|
|
11
|
+
* day, HALF made exactly one call and stopped. A rejected first call is a bounced session, and the
|
|
12
|
+
* agent has no way to know the rejection was about spelling rather than the page.
|
|
13
|
+
*
|
|
14
|
+
* Translation only — `by`/`value` remain the tool's contract, an explicit pair always wins, and a
|
|
15
|
+
* call carrying neither shape is left alone so the schema error still explains itself rather than
|
|
16
|
+
* being replaced by a confident wrong query.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeQueryArgs(args: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accepting the element-query shape the rest of the surface teaches.
|
|
3
|
+
*
|
|
4
|
+
* `reticle_query` asks for a strategy and a value — `{ by: 'text', value: 'Deploy' }`. Every
|
|
5
|
+
* predicate asks for named fields — `{ text: 'Deploy' }`, `{ testid: 'submit' }` — and that is where
|
|
6
|
+
* an agent meets element queries most often (`act_and_wait { until: { kind:'element', query } }`,
|
|
7
|
+
* `reticle_assert`, `wait_for`). Carrying the predicate's spelling to `reticle_query` earns
|
|
8
|
+
* `-32602: Unknown parameter for reticle_query: text`.
|
|
9
|
+
*
|
|
10
|
+
* It is a small thing that lands in an expensive place: of the sessions that called any tool in a
|
|
11
|
+
* day, HALF made exactly one call and stopped. A rejected first call is a bounced session, and the
|
|
12
|
+
* agent has no way to know the rejection was about spelling rather than the page.
|
|
13
|
+
*
|
|
14
|
+
* Translation only — `by`/`value` remain the tool's contract, an explicit pair always wins, and a
|
|
15
|
+
* call carrying neither shape is left alone so the schema error still explains itself rather than
|
|
16
|
+
* being replaced by a confident wrong query.
|
|
17
|
+
*/
|
|
18
|
+
/** Most specific first: the order Reticle prefers anchors everywhere else (testid is gold). */
|
|
19
|
+
const FIELD_TO_STRATEGY = [
|
|
20
|
+
['testid', 'testid'],
|
|
21
|
+
['label', 'label'],
|
|
22
|
+
['placeholder', 'placeholder'],
|
|
23
|
+
['alt', 'alt'],
|
|
24
|
+
['text', 'text'],
|
|
25
|
+
['role', 'role'],
|
|
26
|
+
];
|
|
27
|
+
export function normalizeQueryArgs(args) {
|
|
28
|
+
// An explicit pair is the contract; never second-guess it.
|
|
29
|
+
if ('string' === typeof args['by'] && 'string' === typeof args['value'])
|
|
30
|
+
return args;
|
|
31
|
+
for (const [field, strategy] of FIELD_TO_STRATEGY) {
|
|
32
|
+
const value = args[field];
|
|
33
|
+
if ('string' === typeof value && value.length > 0) {
|
|
34
|
+
// `name` stays as-is: with role it is the accessible-name filter the predicate also uses.
|
|
35
|
+
return { ...args, by: strategy, value };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return args;
|
|
39
|
+
}
|
package/dist/tools/read-tools.js
CHANGED
|
@@ -2,18 +2,43 @@
|
|
|
2
2
|
* Read / record / replay tools — baselines + diff, recordings + replay, narrate, clock, state,
|
|
3
3
|
* explore. Split out of tools.ts; assembled back via...READ_TOOLS.
|
|
4
4
|
*/
|
|
5
|
+
import { resolveAnnotateTarget } from '../flows/annotate-target.js';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
import { EventType, ReticleCommand, REPLAY_PROGRAM_VERSION, SnapshotMode } from '@reticlehq/core';
|
|
7
8
|
import { ReticleTool } from './tool-names.js';
|
|
8
9
|
import { proposeConsequences } from '../oracles/propose-consequences.js';
|
|
9
10
|
import { replayProgram } from '../flows/replay.js';
|
|
10
11
|
import { diffLines } from '../project/baselines.js';
|
|
11
|
-
import { selectPath, capDepth } from '../session/state-select.js';
|
|
12
|
+
import { selectPath, capDepth, projectComponentState } from '../session/state-select.js';
|
|
12
13
|
import { costHint } from '../session/output-budget.js';
|
|
13
14
|
import { buildReactionReport } from '../events/reaction.js';
|
|
14
15
|
import { asString, asNumber, parseInteractive } from './tools-helpers.js';
|
|
15
16
|
import { sessionIdShape, commandOrThrow, snapshotTree } from './tool-kit.js';
|
|
16
17
|
import { bufferEnvelope } from '../session/session-health.js';
|
|
18
|
+
/**
|
|
19
|
+
* What record-stop says about a step that compiled to no anchor at all.
|
|
20
|
+
*
|
|
21
|
+
* `stable: false` does not mean "brittle". It means the compiler found no testid, no accessible
|
|
22
|
+
* role+name and no component/source, so the step is bound to a live `ref` — a handle that dies with
|
|
23
|
+
* the session. The replayer already states this outcome in full (see DEGRADED_STEP_REASON): the step
|
|
24
|
+
* can never resolve on replay.
|
|
25
|
+
*
|
|
26
|
+
* The old wording here — "replay may be brittle (in-session only)" — described a fatal condition as
|
|
27
|
+
* a quality note, so the agent saved the flow and learned the truth several calls later, as an
|
|
28
|
+
* unhealable `anchor_degraded` drift at flow_verify, by which point nothing on screen points back
|
|
29
|
+
* to the element that needed the attribute. Capture time is the only moment the fix is cheap: the
|
|
30
|
+
* element is still on the page and the human is still in the loop.
|
|
31
|
+
*/
|
|
32
|
+
function unanchoredWarning(count) {
|
|
33
|
+
return (`${String(count)} step(s) not bound to a testid, an accessible role+name, or a component — ` +
|
|
34
|
+
'they are pinned to a live ref, so they will replay in THIS session and can never resolve in ' +
|
|
35
|
+
'another one. Add a data-testid to those elements and record the flow again, or accept that ' +
|
|
36
|
+
'this recording is single-session only.');
|
|
37
|
+
}
|
|
38
|
+
/** Severities the presenter HUD can render. */
|
|
39
|
+
const HUD_LEVELS = ['info', 'warn', 'error'];
|
|
40
|
+
const HUD_LEVEL_LIST = HUD_LEVELS.join(' | ');
|
|
41
|
+
const hudLevelEnum = z.enum(HUD_LEVELS);
|
|
17
42
|
export const READ_TOOLS = [
|
|
18
43
|
{
|
|
19
44
|
name: ReticleTool.BASELINE_SAVE,
|
|
@@ -64,7 +89,11 @@ export const READ_TOOLS = [
|
|
|
64
89
|
buffer: z.unknown().optional(),
|
|
65
90
|
},
|
|
66
91
|
handler: async (deps, args) => {
|
|
67
|
-
|
|
92
|
+
// `name` too: the merged reticle_baseline family made this required field optional, and the
|
|
93
|
+
// SIBLING that creates a baseline calls it `name` — its description even says to reuse it
|
|
94
|
+
// here. So `{ action:"diff", name:"x" }` is a valid call that used to look up 'default' and
|
|
95
|
+
// report "no baseline named 'default'", naming something the caller never mentioned.
|
|
96
|
+
const name = asString(args['baseline']) ?? asString(args['name']) ?? 'default';
|
|
68
97
|
const base = deps.baselines.get(name);
|
|
69
98
|
if (base === undefined)
|
|
70
99
|
throw new Error(`no baseline named '${name}'`);
|
|
@@ -127,10 +156,17 @@ export const READ_TOOLS = [
|
|
|
127
156
|
},
|
|
128
157
|
handler: (deps, args) => {
|
|
129
158
|
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
130
|
-
|
|
159
|
+
// The recording actually RUNNING when there is exactly one, not the literal name `default` —
|
|
160
|
+
// the same defaulting that made `annotate` report "no steps" against a recording full of them.
|
|
161
|
+
// With several running, `default` stays the documented answer rather than a guess.
|
|
162
|
+
const name = resolveAnnotateTarget(asString(args['recordingName']), deps.recordings.active());
|
|
131
163
|
const rec = deps.recordings.stop(name);
|
|
132
|
-
if (rec === undefined)
|
|
133
|
-
|
|
164
|
+
if (rec === undefined) {
|
|
165
|
+
const active = deps.recordings.active();
|
|
166
|
+
throw new Error(0 === active.length
|
|
167
|
+
? `no active recording named '${name}' — none is in progress`
|
|
168
|
+
: `no active recording named '${name}'; in progress: ${active.map((r) => `'${r}'`).join(', ')}`);
|
|
169
|
+
}
|
|
134
170
|
const events = session.eventsSince(rec.cursor);
|
|
135
171
|
const program = {
|
|
136
172
|
name,
|
|
@@ -147,7 +183,7 @@ export const READ_TOOLS = [
|
|
|
147
183
|
program,
|
|
148
184
|
...(unstable > 0
|
|
149
185
|
? {
|
|
150
|
-
warning:
|
|
186
|
+
warning: unanchoredWarning(unstable),
|
|
151
187
|
}
|
|
152
188
|
: {}),
|
|
153
189
|
...(proposedConsequences.length > 0 ? { proposedConsequences } : {}),
|
|
@@ -186,7 +222,7 @@ export const READ_TOOLS = [
|
|
|
186
222
|
throw new Error(`no compiled recording named '${name}'`);
|
|
187
223
|
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
188
224
|
const since = session.elapsed();
|
|
189
|
-
const steps = await replayProgram(session, program, args['confirmDangerous']
|
|
225
|
+
const steps = await replayProgram(session, program, true === args['confirmDangerous']);
|
|
190
226
|
return { recordingName: name, since, steps, ok: steps.every((s) => s.ok) };
|
|
191
227
|
},
|
|
192
228
|
},
|
|
@@ -197,10 +233,11 @@ export const READ_TOOLS = [
|
|
|
197
233
|
text: z
|
|
198
234
|
.string()
|
|
199
235
|
.describe('Short sentence describing your next action, shown on the presenter HUD for the developer watching.'),
|
|
200
|
-
|
|
201
|
-
|
|
236
|
+
// Derived, like every other advertised vocabulary here: a free string let a typo through to
|
|
237
|
+
// a HUD that then rendered an unknown severity.
|
|
238
|
+
level: hudLevelEnum
|
|
202
239
|
.optional()
|
|
203
|
-
.describe(
|
|
240
|
+
.describe(`Display severity: ${HUD_LEVEL_LIST}. Default: info.`),
|
|
204
241
|
...sessionIdShape,
|
|
205
242
|
},
|
|
206
243
|
outputSchema: { ok: z.boolean() },
|
|
@@ -227,9 +264,11 @@ export const READ_TOOLS = [
|
|
|
227
264
|
reset: z.boolean().optional().describe('Restore the real clock.'),
|
|
228
265
|
...sessionIdShape,
|
|
229
266
|
},
|
|
267
|
+
// The command returns `{ frozen }`. It used to declare `{ ok, elapsed }` — neither of which any
|
|
268
|
+
// clock code path produces — and MCP strips undeclared fields from structuredContent, so a
|
|
269
|
+
// successful freeze and a failed one both validated to `{}` and became indistinguishable.
|
|
230
270
|
outputSchema: {
|
|
231
|
-
|
|
232
|
-
elapsed: z.number().optional(),
|
|
271
|
+
frozen: z.boolean().optional(),
|
|
233
272
|
},
|
|
234
273
|
handler: (deps, args) => commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.CLOCK, {
|
|
235
274
|
freeze: args['freeze'],
|
|
@@ -270,8 +309,26 @@ export const READ_TOOLS = [
|
|
|
270
309
|
store: z.string().optional(),
|
|
271
310
|
path: z.string().optional(),
|
|
272
311
|
availableKeys: z.array(z.string()).optional(),
|
|
312
|
+
// Present only when `availableKeys` is a SAMPLE. Declared for the same reason as the truncation
|
|
313
|
+
// report below: a capped list of 50 keys with no count reads as a complete one, so an agent
|
|
314
|
+
// concludes the key it wants is absent when it is merely past the cap.
|
|
315
|
+
totalKeys: z.number().optional(),
|
|
273
316
|
component: z
|
|
274
|
-
.object({
|
|
317
|
+
.object({
|
|
318
|
+
ok: z.boolean(),
|
|
319
|
+
reason: z.string().optional(),
|
|
320
|
+
state: z.unknown().optional(),
|
|
321
|
+
// The component name and the projected hook VALUES — declared so a schema-strict client
|
|
322
|
+
// keeps them (they were the point of passing `ref` at all).
|
|
323
|
+
component: z.string().optional(),
|
|
324
|
+
hooks: z.array(z.unknown()).optional(),
|
|
325
|
+
// Present only when effect hooks were projected out. Declared for the same reason as the
|
|
326
|
+
// store truncation report below: a shortened list with no marker reads as a complete one.
|
|
327
|
+
truncation: z
|
|
328
|
+
.object({ droppedItems: z.number(), note: z.string() })
|
|
329
|
+
.optional()
|
|
330
|
+
.describe('Present only when effect hooks were dropped — `hooks` is a projection.'),
|
|
331
|
+
})
|
|
275
332
|
.optional(),
|
|
276
333
|
// Truncation report — present ONLY when a transport cap trimmed the value. Declared so a
|
|
277
334
|
// schema-strict client on the `full` profile KEEPS it: this is a false-green GUARD, and dropping
|
|
@@ -294,23 +351,31 @@ export const READ_TOOLS = [
|
|
|
294
351
|
// Forward path/depth so a CURRENT browser SDK scopes the read IN-PAGE, before the transport —
|
|
295
352
|
// the value never gets size-truncated in transit. (An older SDK ignores them and returns the
|
|
296
353
|
// whole store; we then scope server-side below as a back-compat fallback.)
|
|
297
|
-
const
|
|
354
|
+
const raw = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.STATE_READ, {
|
|
298
355
|
ref: args['ref'],
|
|
299
356
|
store,
|
|
300
357
|
path,
|
|
301
358
|
depth,
|
|
302
359
|
});
|
|
360
|
+
// Project the component hook read BEFORE anything else touches it: React hands back the raw
|
|
361
|
+
// fiber hook list, whose effect entries are chained null-filled internals an agent cannot act
|
|
362
|
+
// on (measured: 2632 -> 1459 bytes on a five-state/three-effect component). Done here rather
|
|
363
|
+
// than in the SDK so an older browser build gets the same projection. The drop is disclosed
|
|
364
|
+
// on `component.truncation`.
|
|
365
|
+
const result = 'object' === typeof raw && null !== raw && 'component' in raw
|
|
366
|
+
? { ...raw, component: projectComponentState(raw.component) }
|
|
367
|
+
: raw;
|
|
303
368
|
// Normalize storeNames to a string[] regardless of how the wire delivered it — the
|
|
304
369
|
// outputSchema requires an array, and a non-array here makes MCP reject the whole result
|
|
305
370
|
// (so the agent gets nothing instead of the state). Defensive: a string becomes a 1-element array.
|
|
306
371
|
const root = result;
|
|
307
372
|
const names = Array.isArray(root.storeNames)
|
|
308
|
-
? root.storeNames.filter((n) => typeof n
|
|
309
|
-
: typeof root.storeNames
|
|
373
|
+
? root.storeNames.filter((n) => 'string' === typeof n)
|
|
374
|
+
: 'string' === typeof root.storeNames && root.storeNames.length > 0
|
|
310
375
|
? [root.storeNames]
|
|
311
376
|
: [];
|
|
312
377
|
// The browser already scoped it in-page (the `found` shape) — pass through, just safe storeNames.
|
|
313
|
-
if (typeof root.found
|
|
378
|
+
if ('boolean' === typeof root.found) {
|
|
314
379
|
return { ...root, storeNames: names };
|
|
315
380
|
}
|
|
316
381
|
if (path === undefined && depth === undefined) {
|
|
@@ -368,7 +433,7 @@ export const READ_TOOLS = [
|
|
|
368
433
|
// The walk stops at its node cap and returns a document-order prefix, so an inventory taken
|
|
369
434
|
// from a big page is a floor, not a census — and this is the tool crawl's description points
|
|
370
435
|
// agents at first for "a non-destructive list of what is here".
|
|
371
|
-
...(snap.truncated
|
|
436
|
+
...(true === snap.truncated ? { truncated: true } : {}),
|
|
372
437
|
consoleErrors,
|
|
373
438
|
hint: 'act on each ref, observe the reaction, and report failed requests / console errors / dead controls',
|
|
374
439
|
// Buffer-honesty: the console-error count spans the whole buffer, which evicts — signal it.
|
|
@@ -408,13 +473,13 @@ export const READ_TOOLS = [
|
|
|
408
473
|
const area = asString(args['area']);
|
|
409
474
|
const key = asString(args['key']);
|
|
410
475
|
const result = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.STORAGE_READ, area !== undefined ? { area } : {});
|
|
411
|
-
const data = (typeof result
|
|
476
|
+
const data = ('object' === typeof result && result !== null ? result : {});
|
|
412
477
|
if (key === undefined)
|
|
413
478
|
return data;
|
|
414
479
|
// Look up the key in the scoped area, or across all three areas when none was named.
|
|
415
480
|
const areas = area !== undefined ? [data] : [data['local'], data['session'], data['cookies']];
|
|
416
481
|
for (const a of areas) {
|
|
417
|
-
if (typeof a
|
|
482
|
+
if ('object' === typeof a && a !== null && key in a) {
|
|
418
483
|
return { area, key, value: String(a[key]), found: true };
|
|
419
484
|
}
|
|
420
485
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The native-input attempt — everything that decides whether a pointer action is driven through a
|
|
3
|
+
* real input provider or falls back to the occlusion-honest synthetic path, and WHY.
|
|
4
|
+
*
|
|
5
|
+
* Split out of act-tools.ts along its natural seam: the act tools care only about the outcome
|
|
6
|
+
* (`result` defined = it went native), never about provider availability, box resolution,
|
|
7
|
+
* drag-target inspection or the reason taxonomy.
|
|
8
|
+
*/
|
|
9
|
+
import { ActionType, InputModeReason } from '@reticlehq/core';
|
|
10
|
+
import type { Session } from '../session/session.js';
|
|
11
|
+
import { type ToolDeps } from './tool-kit.js';
|
|
12
|
+
interface RealActResult {
|
|
13
|
+
/** Defined only on a successful native action; `undefined` means the synthetic path runs. */
|
|
14
|
+
result: unknown;
|
|
15
|
+
settled: boolean;
|
|
16
|
+
/** Set when a provider was available but threw — surfaces the fallback to the agent. */
|
|
17
|
+
fellBack?: boolean;
|
|
18
|
+
/** Why we went synthetic despite a configured provider (field bug #2: never a silent fallback). */
|
|
19
|
+
reason?: InputModeReason;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Attempt to drive a pointer action via native input. Returns a synthetic outcome (with a
|
|
23
|
+
* `reason` when a provider is configured) whenever the synthetic path should run — no matching
|
|
24
|
+
* page, unresolvable box, declined, etc. A throw inside the provider becomes a synthetic fallback
|
|
25
|
+
* flagged with `fellBack`. `result` is defined only on a real success.
|
|
26
|
+
*/
|
|
27
|
+
export declare function tryRealInput(deps: ToolDeps, session: Session, ref: string, action: ActionType, args: Record<string, unknown>): Promise<RealActResult>;
|
|
28
|
+
export {};
|