@reticlehq/server 2.4.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} +63 -13
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +1 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +9 -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-parse.d.ts → cli/cli-parse.d.ts} +2 -11
- package/dist/{cli-parse.js → cli/cli-parse.js} +113 -60
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
- package/dist/{cli-port.js → cli/cli-port.js} +9 -9
- 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 +174 -92
- 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.js +24 -9
- 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 +53 -7
- 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.js +4 -1
- 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 +3 -2
- package/dist/init/detect.js +16 -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 +2 -4
- package/dist/init/next-patch.js +109 -19
- package/dist/init/node-io.js +23 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +132 -8
- package/dist/init/plan.d.ts +73 -8
- package/dist/init/plan.js +133 -34
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +5 -0
- package/dist/init/run.js +210 -36
- package/dist/init/slash-command.d.ts +9 -0
- package/dist/init/slash-command.js +10 -1
- package/dist/init/snippets.d.ts +7 -10
- package/dist/init/snippets.js +44 -15
- package/dist/init/vite-config.js +1 -1
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.js +1 -1
- package/dist/input/network-detail.js +8 -8
- 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 +52 -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 +14 -2
- package/dist/session/session.js +69 -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 +81 -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/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -67
- package/dist/mcp-proxy.js +0 -337
- 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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which tool results count as a completed verification, as one pure rule.
|
|
3
|
+
*
|
|
4
|
+
* It used to live inline in `runTool` and read `status === 'pass'`, mapping everything else to
|
|
5
|
+
* "no verdict". That was wrong in both directions at once, and the shape of the error is visible in
|
|
6
|
+
* the production data:
|
|
7
|
+
*
|
|
8
|
+
* - a FAILING suite emitted nothing, so `verification_completed` only ever fired on greens while
|
|
9
|
+
* `bugsInResult` fired on the reds — bugs with no verifications to divide them by;
|
|
10
|
+
* - an EMPTY suite ("all 0 flows pass") emitted `verified: yes, passed: true`.
|
|
11
|
+
*
|
|
12
|
+
* Extracted because a rule this easy to get wrong, feeding the one number shown to investors, should
|
|
13
|
+
* be readable and testable on its own rather than inferred from a ternary inside a dispatcher.
|
|
14
|
+
*/
|
|
15
|
+
import { VerifiedReason } from '@reticlehq/core';
|
|
16
|
+
import { VERIFICATION_TOOLS } from '../tools/feedback-tools.js';
|
|
17
|
+
import { getBrowserMode } from './browser-mode.js';
|
|
18
|
+
/**
|
|
19
|
+
* The deciding clause, read off the result `decideVerified` wrote it onto.
|
|
20
|
+
*
|
|
21
|
+
* Narrowed against core's own list rather than forwarded: a result is an untyped record here, and a
|
|
22
|
+
* value that is not a member is not a reason — absent beats a string nobody can group by. Derived
|
|
23
|
+
* from the enum, never re-listed, so a new clause is covered the day it exists.
|
|
24
|
+
*/
|
|
25
|
+
const REASONS = new Set(Object.values(VerifiedReason));
|
|
26
|
+
function reasonOf(result) {
|
|
27
|
+
const raw = result['verifiedReason'];
|
|
28
|
+
return 'string' === typeof raw && REASONS.has(raw) ? raw : undefined;
|
|
29
|
+
}
|
|
30
|
+
/** Suite statuses. `unverifiable` is a suite that ran but proved nothing — including an empty one. */
|
|
31
|
+
const SuiteStatus = {
|
|
32
|
+
PASS: 'pass',
|
|
33
|
+
FAIL: 'fail',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The verification payload for a result, or undefined when nothing was verified.
|
|
37
|
+
*
|
|
38
|
+
* `falseGreenCaught` is the one that matters: the assertion PASSED and Reticle still refused to call
|
|
39
|
+
* it verified. That is the product's thesis reduced to a boolean.
|
|
40
|
+
*/
|
|
41
|
+
export function verificationOf(toolName, result, durationMs, brand) {
|
|
42
|
+
if (!VERIFICATION_TOOLS.has(toolName))
|
|
43
|
+
return undefined;
|
|
44
|
+
// A paused session REFUSES the call — nothing driven, nothing asserted. The refusal carries a
|
|
45
|
+
// verdict field so the agent never reads undefined off it, and that field must not be mistaken
|
|
46
|
+
// here for work that happened.
|
|
47
|
+
if (true === result['paused'])
|
|
48
|
+
return undefined;
|
|
49
|
+
const verified = 'string' === typeof result['verified'] ? result['verified'] : undefined;
|
|
50
|
+
// flow_verify reports `status: pass|fail|unverifiable`; assert reports a boolean `pass`. Accept
|
|
51
|
+
// either shape so the whole family is covered without normalizing four tools' contracts for a
|
|
52
|
+
// metric's convenience — but a status that is neither pass nor fail is NOT a verdict, and an empty
|
|
53
|
+
// suite reports exactly that.
|
|
54
|
+
const status = result['status'];
|
|
55
|
+
const passed = 'boolean' === typeof result['pass']
|
|
56
|
+
? result['pass']
|
|
57
|
+
: status === SuiteStatus.PASS
|
|
58
|
+
? true
|
|
59
|
+
: status === SuiteStatus.FAIL
|
|
60
|
+
? false
|
|
61
|
+
: undefined;
|
|
62
|
+
if (verified === undefined && passed === undefined)
|
|
63
|
+
return undefined;
|
|
64
|
+
const reason = reasonOf(result);
|
|
65
|
+
return {
|
|
66
|
+
via: toolName,
|
|
67
|
+
verified: verified ?? (true === passed ? 'yes' : 'no'),
|
|
68
|
+
passed: passed ?? 'yes' === verified,
|
|
69
|
+
falseGreenCaught: true === passed && 'no' === verified,
|
|
70
|
+
durationMs,
|
|
71
|
+
// Headless CI, a human watching, or somebody's own dev server — three different products behind
|
|
72
|
+
// one number until this was recorded.
|
|
73
|
+
browser: getBrowserMode(),
|
|
74
|
+
// WHICH browser, when the page said. `attached` is the common case and says only that Reticle
|
|
75
|
+
// launched nothing; the brand is the difference between "somebody's own browser" and knowing it
|
|
76
|
+
// was Edge. Omitted when unreported — a desktop webview and an older SDK both have no answer,
|
|
77
|
+
// and "unknown" would be a value on a dashboard rather than a gap.
|
|
78
|
+
...(brand !== undefined ? { brand } : {}),
|
|
79
|
+
// WHY, not just what. `verified: unknown` covered seven different clauses of the honesty rule
|
|
80
|
+
// belonging to three different owners — the agent, the app, and Reticle's own blind spots — and
|
|
81
|
+
// they need opposite responses. A suite verdict has no clause behind it and reports none.
|
|
82
|
+
...(reason === undefined ? {} : { reason }),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* must never fail the run that found it.
|
|
11
11
|
*/
|
|
12
12
|
import { ActionType, AnchorKind } from '@reticlehq/core';
|
|
13
|
-
import { CapsuleStore, capsuleId } from '../capsule/capsule-store.js';
|
|
13
|
+
import { CapsuleStore, capsuleId, CAPSULE_VERSION } from '../capsule/capsule-store.js';
|
|
14
14
|
import { ReticleTool } from './tool-names.js';
|
|
15
15
|
import { asRecord, asString } from './tools-helpers.js';
|
|
16
16
|
/** Returns the capsule id when one was written, or undefined when there was nothing to save. */
|
|
@@ -23,7 +23,7 @@ export async function saveFailedAssertCapsule(inputs) {
|
|
|
23
23
|
.map((l) => ('name' in l ? `${l.kind} ${String(l.name)}` : l.kind))
|
|
24
24
|
.join(' AND ');
|
|
25
25
|
const saved = await new CapsuleStore(deps.fs, deps.reticleRoot).save({
|
|
26
|
-
version:
|
|
26
|
+
version: CAPSULE_VERSION,
|
|
27
27
|
id,
|
|
28
28
|
createdAt: deps.now(),
|
|
29
29
|
origin: 'failed-assert',
|
|
@@ -5,3 +5,15 @@ export declare function assertNotDestructive(action: ActionType, innerArgs: Reco
|
|
|
5
5
|
* source alone never says so.
|
|
6
6
|
*/
|
|
7
7
|
export declare function assertDragNotDestructive(innerArgs: Record<string, unknown>, from: unknown, to: unknown): void;
|
|
8
|
+
/**
|
|
9
|
+
* `reticle_act_and_wait` cannot drive native input, and used to take `args.native` and ignore it.
|
|
10
|
+
*
|
|
11
|
+
* An open `args` passthrough accepted the field, the handler drove the page through the SDK anyway,
|
|
12
|
+
* and the result claimed success — so an agent asking for the one thing a synthetic click cannot do
|
|
13
|
+
* (a file picker, the clipboard, an `isTrusted`-gated handler) got a synthetic click and no hint
|
|
14
|
+
* that its request had been dropped. A silently ignored argument is a false promise; refusing with
|
|
15
|
+
* the route that DOES work is the honest answer.
|
|
16
|
+
*/
|
|
17
|
+
export declare const NATIVE_INPUT_UNSUPPORTED: string;
|
|
18
|
+
/** Refuse rather than silently drop a native-input request the act-then-wait path cannot honour. */
|
|
19
|
+
export declare function assertNativeInputSupported(innerArgs: Record<string, unknown>): void;
|
package/dist/tools/act-danger.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionType, DANGEROUS_ACTION_CONFIRM_ARG, isDangerousActionText } from '@reticlehq/core';
|
|
1
|
+
import { ActionType, DANGEROUS_ACTION_CONFIRM_ARG, NATIVE_INPUT_ARG, isDangerousActionText, } from '@reticlehq/core';
|
|
2
2
|
import { asRecord, asString } from './tools-helpers.js';
|
|
3
3
|
/**
|
|
4
4
|
* Refuse to drive a money-moving or destructive control by accident.
|
|
@@ -25,7 +25,7 @@ function descriptorText(value) {
|
|
|
25
25
|
export function assertNotDestructive(action, innerArgs, inspected) {
|
|
26
26
|
if (action !== ActionType.CLICK && action !== ActionType.DBLCLICK)
|
|
27
27
|
return;
|
|
28
|
-
if (innerArgs[DANGEROUS_ACTION_CONFIRM_ARG]
|
|
28
|
+
if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
|
|
29
29
|
return;
|
|
30
30
|
if (!isDangerousActionText(descriptorText(inspected)))
|
|
31
31
|
return;
|
|
@@ -36,9 +36,26 @@ export function assertNotDestructive(action, innerArgs, inspected) {
|
|
|
36
36
|
* source alone never says so.
|
|
37
37
|
*/
|
|
38
38
|
export function assertDragNotDestructive(innerArgs, from, to) {
|
|
39
|
-
if (innerArgs[DANGEROUS_ACTION_CONFIRM_ARG]
|
|
39
|
+
if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
|
|
40
40
|
return;
|
|
41
41
|
if (!isDangerousActionText(`${descriptorText(from)} ${descriptorText(to)}`))
|
|
42
42
|
return;
|
|
43
43
|
throw new Error(`potentially destructive native action blocked; retry with args.${DANGEROUS_ACTION_CONFIRM_ARG}=true`);
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* `reticle_act_and_wait` cannot drive native input, and used to take `args.native` and ignore it.
|
|
47
|
+
*
|
|
48
|
+
* An open `args` passthrough accepted the field, the handler drove the page through the SDK anyway,
|
|
49
|
+
* and the result claimed success — so an agent asking for the one thing a synthetic click cannot do
|
|
50
|
+
* (a file picker, the clipboard, an `isTrusted`-gated handler) got a synthetic click and no hint
|
|
51
|
+
* that its request had been dropped. A silently ignored argument is a false promise; refusing with
|
|
52
|
+
* the route that DOES work is the honest answer.
|
|
53
|
+
*/
|
|
54
|
+
export const NATIVE_INPUT_UNSUPPORTED = `reticle_act_and_wait cannot drive native input, so args.${NATIVE_INPUT_ARG} would be ignored. ` +
|
|
55
|
+
`Use reticle_act { args: { ${NATIVE_INPUT_ARG}: true } } for the trusted click, then assert the ` +
|
|
56
|
+
'consequence with reticle_assert / reticle_observe using the `since` cursor it returns.';
|
|
57
|
+
/** Refuse rather than silently drop a native-input request the act-then-wait path cannot honour. */
|
|
58
|
+
export function assertNativeInputSupported(innerArgs) {
|
|
59
|
+
if (true === innerArgs[NATIVE_INPUT_ARG])
|
|
60
|
+
throw new Error(NATIVE_INPUT_UNSUPPORTED);
|
|
61
|
+
}
|
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
* session, a command, or a tool definition — these are total functions over their arguments, which
|
|
6
6
|
* is why they can be tested without a browser and why they were the right thing to move.
|
|
7
7
|
*/
|
|
8
|
-
import { ActionType } from '@reticlehq/core';
|
|
8
|
+
import { ActionType, ConsequenceKind } from '@reticlehq/core';
|
|
9
9
|
import { HonestyGrade } from '../honesty/honesty.js';
|
|
10
10
|
import { asNumber, asRecord, asString } from './tools-helpers.js';
|
|
11
11
|
/** The strongest consequence grade a set of expected links proves (signal > net > state > presence). */
|
|
12
12
|
export function gradeOf(links) {
|
|
13
|
-
if (links.some((l) =>
|
|
13
|
+
if (links.some((l) => ConsequenceKind.SIGNAL === l.kind))
|
|
14
14
|
return HonestyGrade.SIGNAL;
|
|
15
|
-
if (links.some((l) =>
|
|
15
|
+
if (links.some((l) => ConsequenceKind.NET === l.kind))
|
|
16
16
|
return HonestyGrade.NET;
|
|
17
|
-
if (links.some((l) =>
|
|
17
|
+
if (links.some((l) => ConsequenceKind.STATE === l.kind))
|
|
18
18
|
return HonestyGrade.STATE;
|
|
19
19
|
return HonestyGrade.PRESENCE;
|
|
20
20
|
}
|
|
@@ -1,10 +1,2 @@
|
|
|
1
1
|
import { type ToolDef } from './tool-kit.js';
|
|
2
|
-
/**
|
|
3
|
-
* Narrow the wire's `action` to a real ActionType, or undefined.
|
|
4
|
-
*
|
|
5
|
-
* It used to be `asString(args['action']) ?? ''`, so an unknown or missing action became the empty
|
|
6
|
-
* string and travelled on — reaching the browser as a command it could not perform, and reported back
|
|
7
|
-
* as a generic failure rather than "that is not an action". Validate at the boundary, per the project's
|
|
8
|
-
* unknown-plus-narrowing rule, so a typo is rejected where it can still be explained.
|
|
9
|
-
*/
|
|
10
2
|
export declare const ACT_TOOLS: ToolDef[];
|
package/dist/tools/act-tools.js
CHANGED
|
@@ -1,104 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Action tools — reticle_act, reticle_act_sequence, reticle_act_and_wait
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Action tools — reticle_act, reticle_act_sequence, reticle_act_and_wait. Split out of tools.ts to keep
|
|
3
|
+
* that file under the line cap and assembled back into the tool list there via ...ACT_TOOLS; the
|
|
4
|
+
* native-input attempt itself lives in real-input-attempt.ts for the same reason.
|
|
5
5
|
*/
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { aliasParam } from './alias-args.js';
|
|
8
|
+
import { captureAct, compileSequenceStep } from '../flows/replay.js';
|
|
9
|
+
import { ActionType, ActionWarning, DEFAULT_ASSERT_TIMEOUT_MS, InputMode, ReticleCommand, Verified, PredicateKind, } from '@reticlehq/core';
|
|
10
|
+
import { assertNativeInputSupported } from './act-danger.js';
|
|
10
11
|
import { leanActResult, mutatedWithin } from './act-view.js';
|
|
11
12
|
import { ReticleTool } from './tool-names.js';
|
|
12
13
|
import { buildReactionReport, summarizeReaction } from '../events/reaction.js';
|
|
13
14
|
import { causalSummary } from '../capsule/causal-summary.js';
|
|
14
15
|
import { findContradictions } from '../events/contradictions.js';
|
|
15
16
|
import { decideVerified } from '../honesty/verified.js';
|
|
17
|
+
import { readsDomState } from '../honesty/already-true.js';
|
|
16
18
|
import { saveFailedAssertCapsule } from './act-capsule.js';
|
|
17
19
|
import { buildDivergenceCapsule } from '../capsule/capsule.js';
|
|
18
20
|
import { predicateToExpectedLinks } from '../capsule/predicate-to-links.js';
|
|
19
21
|
import { buildHonestyBlock } from '../honesty/honesty.js';
|
|
20
|
-
import { buildCoverageStatement, blindSpotsFromState, impeachesCapture, } from '../honesty/blind-spots.js';
|
|
22
|
+
import { buildCoverageStatement, blindSpotsFromState, Coverage, impeachesCapture, } from '../honesty/blind-spots.js';
|
|
21
23
|
import { hasAcceptedWrite } from '../honesty/accepted-write.js';
|
|
22
24
|
import { hasUnreadWriteOutcome } from '../honesty/unread-outcome.js';
|
|
23
|
-
import { assertDragNotDestructive, assertNotDestructive } from './act-danger.js';
|
|
24
25
|
import { evaluatePredicate, waitForPredicate, provenExpectedLinks, PredicateSchema, } from '../events/predicate.js';
|
|
25
26
|
import { healthEnvelope, refuseIfThrottled } from '../session/session-health.js';
|
|
26
|
-
import { pausedShortCircuit, pausedOutputShape, withControl } from '../session/control-envelope.js';
|
|
27
|
+
import { pausedShortCircuit, pausedOutputShape, withControl, PAUSED_NO_VERDICT, } from '../session/control-envelope.js';
|
|
27
28
|
import { asString, asNumber, asRecord, sourceOf } from './tools-helpers.js';
|
|
28
|
-
import { sessionIdShape
|
|
29
|
-
import { asActionType,
|
|
30
|
-
|
|
31
|
-
function synthetic(reason) {
|
|
32
|
-
return reason === undefined
|
|
33
|
-
? { result: undefined, settled: false }
|
|
34
|
-
: { result: undefined, settled: false, reason };
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Attempt to drive a pointer action via native input. Returns a synthetic outcome (with a
|
|
38
|
-
* `reason` when a provider is configured) whenever the synthetic path should run — no matching
|
|
39
|
-
* page, unresolvable box, declined, etc. A throw inside the provider becomes a synthetic fallback
|
|
40
|
-
* flagged with `fellBack`. `result` is defined only on a real success.
|
|
41
|
-
*/
|
|
42
|
-
async function tryRealInput(deps, session, ref, action, args) {
|
|
43
|
-
const provider = deps.realInput;
|
|
44
|
-
if (provider === undefined)
|
|
45
|
-
return synthetic(); // real input not configured — no diagnostic
|
|
46
|
-
if (!isPointerAction(action))
|
|
47
|
-
return synthetic(InputModeReason.NOT_POINTER); // fill/type stay synthetic
|
|
48
|
-
const inner = asRecord(args['args']);
|
|
49
|
-
// "Don't click, run the code": a click/dblclick runs the occlusion-honest SYNTHETIC path by default
|
|
50
|
-
// even with a provider configured — no coordinate gesture to be intercepted by the HUD or missed
|
|
51
|
-
// off-screen. Opt into a trusted native click with args.native:true (file pickers, clipboard,
|
|
52
|
-
// isTrusted-gated handlers). hover/drag genuinely need native pointer state, so they stay real.
|
|
53
|
-
if ((action === ActionType.CLICK || action === ActionType.DBLCLICK) && inner['native'] !== true) {
|
|
54
|
-
return synthetic(InputModeReason.SYNTHETIC_CLICK_PREFERRED);
|
|
55
|
-
}
|
|
56
|
-
if (!(await provider.isAvailableFor(session.url)))
|
|
57
|
-
return synthetic(InputModeReason.PAGE_NOT_CORRELATED);
|
|
58
|
-
const inspected = await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, { ref });
|
|
59
|
-
assertNotDestructive(action, inner, inspected);
|
|
60
|
-
const box = asBox(inspected);
|
|
61
|
-
if (box === undefined)
|
|
62
|
-
return synthetic(InputModeReason.ELEMENT_NOT_LOCATABLE);
|
|
63
|
-
let toBox;
|
|
64
|
-
if (action === ActionType.DRAG) {
|
|
65
|
-
const toRef = asString(inner['toRef']);
|
|
66
|
-
if (toRef === undefined)
|
|
67
|
-
return synthetic(InputModeReason.DRAG_TARGET_UNRESOLVED);
|
|
68
|
-
const targetInspected = await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, {
|
|
69
|
-
ref: toRef,
|
|
70
|
-
});
|
|
71
|
-
// A drag is judged on BOTH ends: dropping onto "Trash" is destructive however innocent the
|
|
72
|
-
// thing being dragged looks.
|
|
73
|
-
assertDragNotDestructive(inner, inspected, targetInspected);
|
|
74
|
-
toBox = asBox(targetInspected);
|
|
75
|
-
if (toBox === undefined)
|
|
76
|
-
return synthetic(InputModeReason.DRAG_TARGET_UNRESOLVED);
|
|
77
|
-
}
|
|
78
|
-
const performArgs = {};
|
|
79
|
-
const value = asString(inner['value']);
|
|
80
|
-
if (value !== undefined)
|
|
81
|
-
performArgs.value = value;
|
|
82
|
-
const text = asString(inner['text']);
|
|
83
|
-
if (text !== undefined)
|
|
84
|
-
performArgs.text = text;
|
|
85
|
-
if (toBox !== undefined)
|
|
86
|
-
performArgs.toBox = toBox;
|
|
87
|
-
try {
|
|
88
|
-
const performed = await provider.perform(session.url, action, box, performArgs);
|
|
89
|
-
if (!performed.performed)
|
|
90
|
-
return synthetic(InputModeReason.PROVIDER_DECLINED);
|
|
91
|
-
return { result: { performed: true, center: performed.center, action }, settled: true };
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
return {
|
|
95
|
-
result: undefined,
|
|
96
|
-
settled: false,
|
|
97
|
-
fellBack: true,
|
|
98
|
-
reason: InputModeReason.PROVIDER_ERROR,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
}
|
|
29
|
+
import { sessionIdShape } from './tool-kit.js';
|
|
30
|
+
import { asActionType, gradeOf } from './act-helpers.js';
|
|
31
|
+
import { tryRealInput } from './real-input-attempt.js';
|
|
102
32
|
/**
|
|
103
33
|
* Narrow the wire's `action` to a real ActionType, or undefined.
|
|
104
34
|
*
|
|
@@ -107,18 +37,29 @@ async function tryRealInput(deps, session, ref, action, args) {
|
|
|
107
37
|
* as a generic failure rather than "that is not an action". Validate at the boundary, per the project's
|
|
108
38
|
* unknown-plus-narrowing rule, so a typo is rejected where it can still be explained.
|
|
109
39
|
*/
|
|
40
|
+
/**
|
|
41
|
+
* The action vocabulary, derived from ActionType — never retyped.
|
|
42
|
+
*
|
|
43
|
+
* The description used to list thirteen actions while ActionType had seventeen: blur, upload, drag
|
|
44
|
+
* and webmcp were real, callable, and undocumented, because a hand-copied list drifts the moment
|
|
45
|
+
* someone adds an arm. Deriving both the schema and the prose from the enum makes that impossible.
|
|
46
|
+
*
|
|
47
|
+
* The handler already refused an unknown action with a good message; the schema now refuses it
|
|
48
|
+
* one layer earlier, before any session is resolved or any work is done.
|
|
49
|
+
*/
|
|
50
|
+
const ACTION_TYPE_VALUES = Object.values(ActionType);
|
|
51
|
+
const ACTION_TYPE_LIST = ACTION_TYPE_VALUES.join(' | ');
|
|
52
|
+
const actionTypeEnum = z.enum(ACTION_TYPE_VALUES);
|
|
110
53
|
export const ACT_TOOLS = [
|
|
111
54
|
{
|
|
112
55
|
name: ReticleTool.ACT,
|
|
113
56
|
example: { ref: 'e42', action: 'fill', args: { value: 'hello' } },
|
|
114
|
-
description: 'Execute one action against a ref: click|dblclick|hover|focus|fill|type|clear|select|check|uncheck|submit|press|scrollIntoView. Returns immediately with a `since` cursor — observe the reaction with reticle_observe. Carries effect:{dispatched,targetMatched,visible,enabled,focusMoved,valueChanged,domMutatedWithin,occluded,occludedBy,scrolledIntoView} to tell "action missed" from "app didn\'t react"; dispatched=landed, settled=a real frame flushed, and a settle timeout never fails the tool. Fields at their uninformative default are OMITTED so a clean action collapses to its consequence: an absent dispatched/targetMatched/visible/enabled means true, an absent occluded/scrolledIntoView/valueChanged/defaultPrevented means false, an absent focusMoved/occludedBy means null. occluded=true means the click point is covered by another element (a real user could not click it) — synthetic dispatch still delivered the event; scrolledIntoView=true means an off-viewport target was scrolled in first. inputMode is "real" (native CDP, no synthetic effect block) or "synthetic"; clicks default to the occlusion-honest synthetic path even when CDP is configured — pass args.native:true to force a trusted native click (file pickers, clipboard). inputModeReason explains any real→synthetic choice so it is never silent. Full model (real-input, throttled tabs, `reticle drive`): docs/usage.md §18.',
|
|
57
|
+
description: 'Execute one action against a ref: click|dblclick|hover|focus|fill|type|clear|select|check|uncheck|submit|press|scrollIntoView. Returns immediately with a `since` cursor — observe the reaction with reticle_observe. For a VERDICT instead of a cursor use reticle_act_and_wait { until } — the same action plus the assertion, in one call; prefer it whenever the action is supposed to cause something. Carries effect:{dispatched,targetMatched,visible,enabled,focusMoved,valueChanged,domMutatedWithin,occluded,occludedBy,scrolledIntoView} to tell "action missed" from "app didn\'t react"; dispatched=landed, settled=a real frame flushed, and a settle timeout never fails the tool. Fields at their uninformative default are OMITTED so a clean action collapses to its consequence: an absent dispatched/targetMatched/visible/enabled means true, an absent occluded/scrolledIntoView/valueChanged/defaultPrevented means false, an absent focusMoved/occludedBy means null. occluded=true means the click point is covered by another element (a real user could not click it) — synthetic dispatch still delivered the event; scrolledIntoView=true means an off-viewport target was scrolled in first. inputMode is "real" (native CDP, no synthetic effect block) or "synthetic"; clicks default to the occlusion-honest synthetic path even when CDP is configured — pass args.native:true to force a trusted native click (file pickers, clipboard). inputModeReason explains any real→synthetic choice so it is never silent. Full model (real-input, throttled tabs, `reticle drive`): docs/usage.md §18.',
|
|
115
58
|
inputSchema: {
|
|
116
59
|
ref: z
|
|
117
60
|
.string()
|
|
118
61
|
.describe(`Element ref (e.g. 'e42') from reticle_snapshot/reticle_query — stable until the element leaves the DOM, so no re-snapshot between actions.`),
|
|
119
|
-
action:
|
|
120
|
-
.string()
|
|
121
|
-
.describe('Action to perform: click | dblclick | hover | focus | fill | type | clear | select | check | uncheck | submit | press | scrollIntoView'),
|
|
62
|
+
action: actionTypeEnum.describe(`Action to perform: ${ACTION_TYPE_LIST}`),
|
|
122
63
|
args: z
|
|
123
64
|
.record(z.unknown())
|
|
124
65
|
.optional()
|
|
@@ -163,10 +104,8 @@ export const ACT_TOOLS = [
|
|
|
163
104
|
return paused;
|
|
164
105
|
refuseIfThrottled(session, args['refuseWhenThrottled']);
|
|
165
106
|
const since = session.elapsed();
|
|
166
|
-
|
|
167
|
-
//
|
|
168
|
-
// measurement standing, which would judge this window with the last one's evidence.
|
|
169
|
-
session.lastAct.markEffect(asString(args['action']), undefined);
|
|
107
|
+
// The act cursor + effect are marked only once the action has actually DISPATCHED (below, on
|
|
108
|
+
// each success path) — a refused act must leave nothing behind for the next observe to judge.
|
|
170
109
|
const ref = asString(args['ref']) ?? '';
|
|
171
110
|
// Open the journal's action-attribution window BEFORE dispatching, so it covers the native path
|
|
172
111
|
// too. It used to open only on the synthetic path, and the native path returned above it — so a
|
|
@@ -181,10 +120,11 @@ export const ACT_TOOLS = [
|
|
|
181
120
|
// drive native pointer input when a provider is available; otherwise fall back.
|
|
182
121
|
const real = await tryRealInput(deps, session, ref, action, args);
|
|
183
122
|
if (real.result !== undefined) {
|
|
184
|
-
|
|
185
|
-
deps.recordings.capture(compileActStep(args, real.result));
|
|
186
|
-
}
|
|
123
|
+
captureAct(deps.recordings, args, real.result);
|
|
187
124
|
settledOutcome = real.settled ?? undefined;
|
|
125
|
+
// Native input reports no synthetic effect block, so nothing measured in-target: undefined
|
|
126
|
+
// (the weaker empty-window test), never a fabricated zero.
|
|
127
|
+
session.lastAct.markActed(since, action, undefined);
|
|
188
128
|
return withControl(session, {
|
|
189
129
|
since,
|
|
190
130
|
inputMode: InputMode.REAL,
|
|
@@ -202,17 +142,15 @@ export const ACT_TOOLS = [
|
|
|
202
142
|
});
|
|
203
143
|
if (!result.ok)
|
|
204
144
|
throw new Error(result.error ?? 'act failed');
|
|
205
|
-
|
|
206
|
-
deps.recordings.capture(compileActStep(args, result.result));
|
|
207
|
-
}
|
|
145
|
+
captureAct(deps.recordings, args, result.result);
|
|
208
146
|
// lift dispatch/settle status to the envelope (a settle timeout is NOT a failure).
|
|
209
147
|
const r = asRecord(result.result);
|
|
210
|
-
if (typeof r['settled']
|
|
148
|
+
if ('boolean' === typeof r['settled'])
|
|
211
149
|
settledOutcome = r['settled'];
|
|
212
150
|
// Keep what only this call measured, so the observe that judges this window can ask whether
|
|
213
151
|
// anything happened INSIDE the target — the one fact that separates a dead control from a
|
|
214
152
|
// page that was merely busy with something else.
|
|
215
|
-
session.lastAct.
|
|
153
|
+
session.lastAct.markActed(since, action, mutatedWithin(r));
|
|
216
154
|
return withControl(session, {
|
|
217
155
|
since,
|
|
218
156
|
inputMode: InputMode.SYNTHETIC,
|
|
@@ -222,13 +160,13 @@ export const ACT_TOOLS = [
|
|
|
222
160
|
settled: r['settled'] ?? null,
|
|
223
161
|
settleReason: r['settleReason'] ?? null,
|
|
224
162
|
result: leanActResult(result.result),
|
|
225
|
-
...(real.fellBack
|
|
163
|
+
...(true === real.fellBack ? { warning: ActionWarning.REAL_INPUT_FELL_BACK } : {}),
|
|
226
164
|
...healthEnvelope(session),
|
|
227
165
|
});
|
|
228
166
|
}
|
|
229
167
|
finally {
|
|
230
168
|
// Close the window on every exit (settle or throw), recording the action + settle outcome.
|
|
231
|
-
session.finishAction(undefined, settledOutcome,
|
|
169
|
+
session.finishAction(undefined, settledOutcome, true === settledOutcome ? session.elapsed() - since : undefined);
|
|
232
170
|
}
|
|
233
171
|
},
|
|
234
172
|
},
|
|
@@ -258,12 +196,15 @@ export const ACT_TOOLS = [
|
|
|
258
196
|
if (paused !== undefined)
|
|
259
197
|
return paused;
|
|
260
198
|
const since = session.elapsed();
|
|
261
|
-
session.lastAct.markCursor(since); // honesty: a later wait_for/assert floors at this cursor
|
|
262
199
|
session.beginAction(ReticleTool.ACT_SEQUENCE, asRecord(args));
|
|
263
200
|
try {
|
|
264
201
|
const result = await session.command(ReticleCommand.ACT_SEQUENCE, { steps: args['steps'] });
|
|
265
202
|
if (!result.ok)
|
|
266
203
|
throw new Error(result.error ?? 'act_sequence failed');
|
|
204
|
+
// Marked only once the sequence ran: a refused sequence must leave no cursor behind for a
|
|
205
|
+
// later observe to judge. No single action and no in-target measurement here — per-step
|
|
206
|
+
// effects live in steps[] — so the effect is cleared rather than inherited from an earlier act.
|
|
207
|
+
session.lastAct.markActed(since, undefined, undefined);
|
|
267
208
|
if (deps.recordings.active().length > 0) {
|
|
268
209
|
deps.recordings.capture(compileSequenceStep(args, result.result));
|
|
269
210
|
}
|
|
@@ -283,7 +224,11 @@ export const ACT_TOOLS = [
|
|
|
283
224
|
},
|
|
284
225
|
{
|
|
285
226
|
name: ReticleTool.ACT_AND_WAIT,
|
|
286
|
-
example: {
|
|
227
|
+
example: {
|
|
228
|
+
ref: 'e42',
|
|
229
|
+
action: 'click',
|
|
230
|
+
until: { kind: PredicateKind.SIGNAL, name: 'todos:loaded' },
|
|
231
|
+
},
|
|
287
232
|
description: 'Act on a ref, then wait for a predicate to hold — one hop for the act->observe->assert loop. ' +
|
|
288
233
|
'Omit `until` to wait for the page to settle (network + DOM idle) — use this instead of a fixed sleep. ' +
|
|
289
234
|
'Returns { effect } (the action result), { verdict } (predicate pass/evidence/near-miss), ' +
|
|
@@ -294,13 +239,12 @@ export const ACT_TOOLS = [
|
|
|
294
239
|
ref: z
|
|
295
240
|
.string()
|
|
296
241
|
.describe(`Element ref (e.g. 'e42') from reticle_snapshot/reticle_query — stable until the element leaves the DOM, so no re-snapshot between actions.`),
|
|
297
|
-
action:
|
|
298
|
-
.string()
|
|
299
|
-
.describe('Action to perform: click | dblclick | hover | focus | fill | type | clear | select | check | uncheck | submit | press | scrollIntoView'),
|
|
242
|
+
action: actionTypeEnum.describe(`Action to perform: ${ACTION_TYPE_LIST}`),
|
|
300
243
|
args: z
|
|
301
244
|
.record(z.unknown())
|
|
302
245
|
.optional()
|
|
303
246
|
.describe('Action-specific arguments: { value } for fill/select, { text } for type/press, { confirmDangerous: true } for a potentially destructive control.'),
|
|
247
|
+
predicate: PredicateSchema.optional().describe('Alias for `until` (the name reticle_assert / reticle_wait_for use).'),
|
|
304
248
|
until: PredicateSchema.optional().describe('Predicate to wait for after the action completes (same shape as reticle_assert). OMIT to wait for the page to SETTLE — network + DOM idle — the deterministic default instead of a sleep. To assert a consequence AND settle, allOf them: { kind: "allOf", predicates: [<your predicate>, { kind: "settled" }] }.'),
|
|
305
249
|
timeout_ms: z
|
|
306
250
|
.number()
|
|
@@ -374,26 +318,44 @@ export const ACT_TOOLS = [
|
|
|
374
318
|
handler: async (deps, args) => {
|
|
375
319
|
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
376
320
|
// Live-control: refuse to drive the page (no act, no predicate eval) while paused.
|
|
321
|
+
//
|
|
322
|
+
// A VERDICT rides out with the refusal. This is the one tool here that promises `verified`,
|
|
323
|
+
// and the bare pause payload omitted it — so an agent reading `result.verified` got undefined
|
|
324
|
+
// from a call that carried no error, which is neither yes, no, nor unknown. It also broke this
|
|
325
|
+
// tool's own outputSchema, where `because` is required.
|
|
377
326
|
const paused = pausedShortCircuit(session);
|
|
378
|
-
if (paused !== undefined)
|
|
379
|
-
return paused;
|
|
327
|
+
if (paused !== undefined) {
|
|
328
|
+
return { ...paused, verified: Verified.UNKNOWN, because: PAUSED_NO_VERDICT };
|
|
329
|
+
}
|
|
380
330
|
refuseIfThrottled(session, args['refuseWhenThrottled']);
|
|
381
331
|
// Omitting `until` waits for the page to settle (idle) — the deterministic default vs a sleep.
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
332
|
+
// `predicate` is what reticle_assert / reticle_wait_for call this — see alias-args.ts.
|
|
333
|
+
const withUntil = aliasParam(args, 'until', ['predicate']);
|
|
334
|
+
const until = withUntil['until'] !== undefined
|
|
335
|
+
? PredicateSchema.parse(withUntil['until'])
|
|
336
|
+
: { kind: PredicateKind.SETTLED };
|
|
385
337
|
const timeout = asNumber(args['timeout_ms']) ?? DEFAULT_ASSERT_TIMEOUT_MS;
|
|
338
|
+
// Before anything is driven: this path cannot honour a native-input request, and taking the
|
|
339
|
+
// argument and ignoring it told the agent its trusted click had happened. See act-danger.
|
|
340
|
+
assertNativeInputSupported(asRecord(args['args']));
|
|
386
341
|
const since = session.elapsed();
|
|
387
342
|
// `dropped` is cumulative for the SESSION, so comparing against a pre-action reading is what
|
|
388
343
|
// asks "did the buffer lose anything while THIS action was observed?" — the question the
|
|
389
344
|
// honesty block means. Reading the raw total pinned every later verdict to `unknown` after a
|
|
390
345
|
// single early eviction (measured on the Next.js demo at dropped:51, windows intact).
|
|
391
346
|
const droppedBefore = session.bufferHealth().dropped;
|
|
392
|
-
|
|
347
|
+
// The cursor + effect are marked after the act dispatches (below) — a refused act leaves none.
|
|
393
348
|
// The attribution window stays open across the settle wait below, so post-dispatch async events
|
|
394
349
|
// (the whole point of act_and_wait) attribute to this action. finishAction fires after the wait.
|
|
395
350
|
session.beginAction(ReticleTool.ACT_AND_WAIT, asRecord(args));
|
|
396
351
|
let settledOutcome;
|
|
352
|
+
// Was the declared consequence ALREADY TRUE? Only asked for predicates that read live DOM
|
|
353
|
+
// state — event-based ones are floored at this act's cursor and cannot be satisfied by the
|
|
354
|
+
// past, so they need no pre-check and pay nothing. One extra query, on the path where a green
|
|
355
|
+
// is otherwise unfalsifiable. See honesty/already-true.
|
|
356
|
+
const alreadyTrue = until !== undefined && readsDomState(until)
|
|
357
|
+
? (await evaluatePredicate(session, until, since, false)).pass
|
|
358
|
+
: false;
|
|
397
359
|
try {
|
|
398
360
|
const actResult = await session.command(ReticleCommand.ACT, {
|
|
399
361
|
ref: args['ref'],
|
|
@@ -402,12 +364,16 @@ export const ACT_TOOLS = [
|
|
|
402
364
|
});
|
|
403
365
|
if (!actResult.ok)
|
|
404
366
|
throw new Error(actResult.error ?? 'act failed');
|
|
367
|
+
captureAct(deps.recordings, args, actResult.result);
|
|
368
|
+
// Dispatched — now this act owns the cursor and the effect. Marking its OWN measurement also
|
|
369
|
+
// stops the spread below from inheriting an earlier reticle_act's action and mutation count.
|
|
370
|
+
session.lastAct.markActed(since, asString(args['action']), mutatedWithin(asRecord(actResult.result)));
|
|
405
371
|
// Honesty: floor the predicate at this act's cursor so a stale buffered event can't satisfy it.
|
|
406
372
|
const verdict = timeout > 0
|
|
407
373
|
? await waitForPredicate(session, until, timeout, since)
|
|
408
374
|
: await evaluatePredicate(session, until, since);
|
|
409
375
|
const r = asRecord(actResult.result);
|
|
410
|
-
if (typeof r['settled']
|
|
376
|
+
if ('boolean' === typeof r['settled'])
|
|
411
377
|
settledOutcome = r['settled'];
|
|
412
378
|
// Where the acted element is written. Captured at act time alongside the anchor, so it is
|
|
413
379
|
// available even when the action unmounted its own target.
|
|
@@ -442,7 +408,7 @@ export const ACT_TOOLS = [
|
|
|
442
408
|
grade: gradeOf(gradedLinks),
|
|
443
409
|
attribution: 'window',
|
|
444
410
|
truncated: session.bufferHealth().dropped > droppedBefore,
|
|
445
|
-
coveragePartial:
|
|
411
|
+
coveragePartial: Coverage.PARTIAL === coverage.coverage,
|
|
446
412
|
...(impeaching.note === undefined ? {} : { blindSpots: [impeaching.note] }),
|
|
447
413
|
});
|
|
448
414
|
const capsuleSaved = await saveFailedAssertCapsule({
|
|
@@ -467,6 +433,9 @@ export const ACT_TOOLS = [
|
|
|
467
433
|
const outcomeUnread = hasUnreadWriteOutcome(windowEvents);
|
|
468
434
|
const decision = decideVerified({
|
|
469
435
|
pass: verdict.pass,
|
|
436
|
+
...(alreadyTrue ? { alreadyTrue } : {}),
|
|
437
|
+
// An assertion nobody could evaluate must not be reported as one the app failed.
|
|
438
|
+
...(verdict.inconclusive === undefined ? {} : { inconclusive: verdict.inconclusive }),
|
|
470
439
|
honesty,
|
|
471
440
|
contradictions,
|
|
472
441
|
...(outcomePending ? { outcomePending } : {}),
|
|
@@ -505,7 +474,7 @@ export const ACT_TOOLS = [
|
|
|
505
474
|
});
|
|
506
475
|
}
|
|
507
476
|
finally {
|
|
508
|
-
session.finishAction(undefined, settledOutcome,
|
|
477
|
+
session.finishAction(undefined, settledOutcome, true === settledOutcome ? session.elapsed() - since : undefined);
|
|
509
478
|
}
|
|
510
479
|
},
|
|
511
480
|
},
|
package/dist/tools/act-view.js
CHANGED
|
@@ -23,11 +23,11 @@ const NULL_NOISE = new Set(['occludedBy', 'focusMoved']);
|
|
|
23
23
|
function leanEffect(effect) {
|
|
24
24
|
const out = {};
|
|
25
25
|
for (const [k, v] of Object.entries(effect)) {
|
|
26
|
-
if (TRUE_NOISE.has(k) &&
|
|
26
|
+
if (TRUE_NOISE.has(k) && true === v)
|
|
27
27
|
continue;
|
|
28
|
-
if (FALSE_NOISE.has(k) &&
|
|
28
|
+
if (FALSE_NOISE.has(k) && false === v)
|
|
29
29
|
continue;
|
|
30
|
-
if (NULL_NOISE.has(k) &&
|
|
30
|
+
if (NULL_NOISE.has(k) && null === v)
|
|
31
31
|
continue;
|
|
32
32
|
out[k] = v;
|
|
33
33
|
}
|
|
@@ -35,11 +35,11 @@ function leanEffect(effect) {
|
|
|
35
35
|
}
|
|
36
36
|
/** Return a copy of an act command result with its `effect` block leaned. Non-objects pass through. */
|
|
37
37
|
export function leanActResult(result) {
|
|
38
|
-
if (
|
|
38
|
+
if (null === result || typeof result !== 'object')
|
|
39
39
|
return result;
|
|
40
40
|
const r = result;
|
|
41
41
|
const effect = r['effect'];
|
|
42
|
-
if (
|
|
42
|
+
if (null === effect || typeof effect !== 'object')
|
|
43
43
|
return result;
|
|
44
44
|
return { ...r, effect: leanEffect(effect) };
|
|
45
45
|
}
|
|
@@ -51,8 +51,8 @@ export function leanActResult(result) {
|
|
|
51
51
|
*/
|
|
52
52
|
export function mutatedWithin(result) {
|
|
53
53
|
const effect = result['effect'];
|
|
54
|
-
if (typeof effect !== 'object' ||
|
|
54
|
+
if (typeof effect !== 'object' || null === effect)
|
|
55
55
|
return undefined;
|
|
56
56
|
const value = effect['domMutatedWithin'];
|
|
57
|
-
return typeof value
|
|
57
|
+
return 'number' === typeof value ? value : 0;
|
|
58
58
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accepting a neighbouring tool's spelling for the same parameter.
|
|
3
|
+
*
|
|
4
|
+
* Reticle's surface has a few places where one concept has two names, and every one of them is a
|
|
5
|
+
* rejected call for an agent that learned the other spelling from the tool next door:
|
|
6
|
+
*
|
|
7
|
+
* reticle_act_and_wait { until } vs reticle_wait_for / reticle_assert { predicate }
|
|
8
|
+
* reticle_annotate { flow } vs every other flow tool { flowName }
|
|
9
|
+
*
|
|
10
|
+
* Both cross a path the product prescribes — `act_and_wait` is the most-used tool, and
|
|
11
|
+
* `reticle_flow_save`'s own description tells the agent to go and call `reticle_annotate`.
|
|
12
|
+
*
|
|
13
|
+
* Aliasing, not renaming: the canonical name stays the contract, an explicit canonical value always
|
|
14
|
+
* wins, and a call carrying neither is left untouched so the schema error still names what was
|
|
15
|
+
* missing instead of being replaced by a guess.
|
|
16
|
+
*/
|
|
17
|
+
export declare function aliasParam(args: Record<string, unknown>, canonical: string, aliases: readonly string[]): Record<string, unknown>;
|