@reticlehq/server 2.4.0 → 2.6.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 +719 -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} +13 -1
- package/dist/{bridge.js → bridge/bridge.js} +95 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
- 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/cli-doctor.d.ts +1 -0
- package/dist/cli/cli-doctor.js +135 -0
- 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-feedback.d.ts +31 -0
- package/dist/cli/cli-parse-feedback.js +56 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
- package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
- 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/doctor-daemon-line.d.ts +37 -0
- package/dist/cli/doctor-daemon-line.js +35 -0
- package/dist/cli/port-holder.d.ts +43 -0
- package/dist/cli/port-holder.js +99 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +302 -100
- 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 +73 -0
- package/dist/daemon/daemon-resilience.js +279 -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} +105 -48
- package/dist/daemon/heartbeat.d.ts +115 -0
- package/dist/daemon/heartbeat.js +141 -0
- 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} +23 -2
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
- package/dist/daemon/port-presence.d.ts +76 -0
- package/dist/daemon/port-presence.js +93 -0
- package/dist/daemon/state-dir.d.ts +7 -0
- package/dist/daemon/state-dir.js +59 -0
- package/dist/daemon/unclean-predecessor.d.ts +23 -0
- package/dist/daemon/unclean-predecessor.js +27 -0
- 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 +53 -20
- package/dist/events/predicate-eval.js +179 -55
- package/dist/events/predicate-parse.d.ts +23 -0
- package/dist/events/predicate-parse.js +54 -0
- package/dist/events/predicate.d.ts +7 -2
- package/dist/events/predicate.js +126 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +2 -17
- 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 +6 -8
- package/dist/flows/flow-replay.js +132 -48
- 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 +156 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +63 -21
- 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/role-drift-reason.d.ts +20 -0
- package/dist/flows/role-drift-reason.js +39 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/suite-flakes.d.ts +33 -0
- package/dist/flows/suite-flakes.js +32 -0
- 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 +35 -3
- package/dist/honesty/blind-spots.js +57 -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 +31 -3
- package/dist/honesty/verified.js +64 -8
- package/dist/http-server.js +23 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +9 -6
- package/dist/index.js +41 -25
- package/dist/init/agent-rules.d.ts +20 -1
- package/dist/init/agent-rules.js +64 -8
- 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 +50 -0
- package/dist/init/cra.js +106 -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 +9 -2
- package/dist/init/detect.js +24 -5
- package/dist/init/mcp-clients.d.ts +81 -0
- package/dist/init/mcp-clients.js +248 -0
- 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 +29 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +166 -8
- package/dist/init/plan.d.ts +85 -6
- package/dist/init/plan.js +196 -33
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +23 -1
- package/dist/init/run.js +338 -47
- 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/windows-quote.d.ts +38 -0
- package/dist/init/windows-quote.js +70 -0
- 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 +32 -0
- package/dist/mcp/mcp-outage.js +55 -0
- package/dist/mcp/mcp-proxy.d.ts +134 -0
- package/dist/mcp/mcp-proxy.js +720 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +139 -37
- 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/browser-pool.d.ts +9 -0
- package/dist/pool/browser-pool.js +38 -9
- 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/attachment-history.d.ts +46 -0
- package/dist/session/attachment-history.js +65 -0
- 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 +45 -0
- package/dist/session/no-session-diagnosis.js +120 -0
- package/dist/session/no-session-watch.d.ts +21 -0
- package/dist/session/no-session-watch.js +69 -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/pending-commands.js +1 -0
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +43 -8
- package/dist/session/session-info.js +46 -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 +6 -0
- package/dist/session/session-manager.js +124 -12
- 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 +15 -2
- package/dist/session/session.js +77 -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/crash-cause.d.ts +47 -0
- package/dist/telemetry/crash-cause.js +104 -0
- package/dist/telemetry/daemon-telemetry.d.ts +38 -1
- package/dist/telemetry/daemon-telemetry.js +37 -12
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-class.d.ts +26 -0
- package/dist/telemetry/error-class.js +52 -0
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +8 -3
- package/dist/telemetry/feedback-cli.js +52 -8
- package/dist/telemetry/feedback-context.d.ts +23 -6
- package/dist/telemetry/feedback-context.js +105 -5
- 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.d.ts +7 -0
- package/dist/telemetry/git-facts.js +5 -5
- package/dist/telemetry/project-profile.js +2 -1
- package/dist/telemetry/session-metrics.d.ts +47 -3
- package/dist/telemetry/session-metrics.js +315 -15
- package/dist/telemetry/telemetry.d.ts +45 -2
- package/dist/telemetry/telemetry.js +150 -37
- 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 +114 -118
- 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 +9 -1
- package/dist/tools/assert-verdict.js +21 -6
- package/dist/tools/browser-tools.js +36 -11
- package/dist/tools/contract-tools.d.ts +0 -5
- package/dist/tools/contract-tools.js +27 -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 +96 -19
- package/dist/tools/error-recovery.d.ts +34 -2
- package/dist/tools/error-recovery.js +255 -4
- package/dist/tools/feedback-invite.d.ts +48 -0
- package/dist/tools/feedback-invite.js +71 -0
- 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 -12
- package/dist/tools/invoke-tool.js +126 -33
- 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-arrival.d.ts +26 -0
- package/dist/tools/navigate-arrival.js +60 -0
- package/dist/tools/navigate-result.d.ts +31 -0
- package/dist/tools/navigate-result.js +38 -0
- package/dist/tools/observe-tools.js +72 -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 +93 -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 +16 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +217 -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/README.md +55 -0
- 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-dispatch-receiver.yml +76 -0
- package/docs/fixtures.md +45 -0
- package/docs/flows.md +166 -0
- package/docs/gate-plan.md +98 -0
- package/docs/gates.md +117 -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/matrix/2.5.0/claude-code-machine.json +19 -0
- package/docs/matrix/2.5.0/codex-machine.json +20 -0
- package/docs/matrix/2.5.0/cursor-machine.json +22 -0
- package/docs/matrix/2.5.0/gemini-machine.json +22 -0
- package/docs/matrix/2.5.0/opencode-machine.json +22 -0
- package/docs/matrix/2.5.0/vscode-machine.json +22 -0
- package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
- package/docs/matrix/MATRIX.md +26 -0
- package/docs/matrix/README.md +59 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/system-map.md +152 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +191 -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-doctor.d.ts +0 -6
- package/dist/cli-doctor.js +0 -60
- 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-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
package/dist/events/predicate.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { ElementState, ReticleCommand, } from '@reticlehq/core';
|
|
1
|
+
import { ElementState, PredicateKind, ReticleCommand, } from '@reticlehq/core';
|
|
2
|
+
import { log } from '../log.js';
|
|
3
|
+
import { bindSpanContext } from '../trace.js';
|
|
2
4
|
import { selectPath, capDepth } from '../session/state-select.js';
|
|
5
|
+
import { describeTestidMiss } from './testid-near-miss.js';
|
|
3
6
|
import { predicateToExpectedLinks } from '../capsule/predicate-to-links.js';
|
|
4
7
|
import { isAmbient, ambientKeyOf } from '../journal/ambient.js';
|
|
5
8
|
import { PredicateSchema, matchValue, evalNet, evalRoute, evalConsole, evalAnimation, evalSignal, evalSettled, } from './predicate-eval.js';
|
|
6
|
-
export { PredicateSchema };
|
|
9
|
+
export { PredicateSchema, PredicateKind };
|
|
7
10
|
async function matchOnce(session, query, state) {
|
|
8
11
|
const res = await session.command(ReticleCommand.MATCH, { query, state });
|
|
9
12
|
if (!res.ok)
|
|
@@ -23,7 +26,7 @@ async function evalElement(session, query, state, absent, diagnose) {
|
|
|
23
26
|
// silently widening to the whole page is the original false green. So scopeMissing FAILS presence
|
|
24
27
|
// (on the wait_for path this just keeps polling until the scope appears).
|
|
25
28
|
if (absent) {
|
|
26
|
-
if (match.scopeMissing
|
|
29
|
+
if (true === match.scopeMissing) {
|
|
27
30
|
return { pass: true, evidence: { absent: true, scopeMissing: true } };
|
|
28
31
|
}
|
|
29
32
|
return match.matched
|
|
@@ -37,7 +40,7 @@ async function evalElement(session, query, state, absent, diagnose) {
|
|
|
37
40
|
}
|
|
38
41
|
: { pass: true, evidence: { absent: true } };
|
|
39
42
|
}
|
|
40
|
-
if (match.scopeMissing
|
|
43
|
+
if (true === match.scopeMissing) {
|
|
41
44
|
return {
|
|
42
45
|
pass: false,
|
|
43
46
|
failureReason: `scope resolved to no element — cannot confirm ${subject} is present`,
|
|
@@ -96,12 +99,19 @@ async function evalElement(session, query, state, absent, diagnose) {
|
|
|
96
99
|
};
|
|
97
100
|
}
|
|
98
101
|
}
|
|
102
|
+
// The testid near-miss: name what IS here, so a typo is one step from fixed rather than a dead
|
|
103
|
+
// end. reticle_query has always done this; the predicate path had no equivalent. See
|
|
104
|
+
// testid-near-miss.ts.
|
|
105
|
+
const present = match.hint?.presentTestids ?? [];
|
|
106
|
+
const alsoHere = query.testid === undefined ? undefined : describeTestidMiss(query.testid, present);
|
|
107
|
+
const suffix = alsoHere === undefined || '' === alsoHere ? '' : ` — ${alsoHere}`;
|
|
99
108
|
return {
|
|
100
109
|
pass: false,
|
|
101
|
-
failureReason: `no element matched ${subject}${state === undefined ? '' : ` in state '${state}'`}`,
|
|
102
|
-
observed:
|
|
110
|
+
failureReason: `no element matched ${subject}${state === undefined ? '' : ` in state '${state}'`}${suffix}`,
|
|
111
|
+
observed: `no matching element on the page${suffix}`,
|
|
103
112
|
expected: `an element matching ${subject}${state === undefined ? '' : ` in state '${state}'`}`,
|
|
104
113
|
assertion: 'element.present',
|
|
114
|
+
...(present.length > 0 ? { evidence: { presentTestids: present } } : {}),
|
|
105
115
|
};
|
|
106
116
|
}
|
|
107
117
|
async function evalState(session, p) {
|
|
@@ -117,14 +127,15 @@ async function evalState(session, p) {
|
|
|
117
127
|
}
|
|
118
128
|
const stores = (res.result ?? {}).stores ?? {};
|
|
119
129
|
const names = Object.keys(stores);
|
|
120
|
-
const storeName = p.store ?? (names.length
|
|
130
|
+
const storeName = p.store ?? (1 === names.length ? names[0] : undefined);
|
|
121
131
|
if (storeName === undefined) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
// Neither of these is a finding about the app. With several stores registered the call simply did
|
|
133
|
+
// not say which one to read; with none, there is nothing to read at all. No assertion was
|
|
134
|
+
// evaluated either way, so this is inconclusive rather than failed — see honesty/inconclusive.
|
|
135
|
+
const reason = 0 === names.length
|
|
136
|
+
? 'no registered store to read state from'
|
|
137
|
+
: `multiple stores (${names.join(', ')}); name one with \`store\``;
|
|
138
|
+
return { pass: false, failureReason: reason, inconclusive: reason };
|
|
128
139
|
}
|
|
129
140
|
const selection = selectPath(stores[storeName], p.path);
|
|
130
141
|
if (!selection.found) {
|
|
@@ -153,31 +164,39 @@ async function evalState(session, p) {
|
|
|
153
164
|
evidence: { store: storeName, path: p.path, value: capDepth(selection.value, 1) },
|
|
154
165
|
};
|
|
155
166
|
}
|
|
167
|
+
/** The predicate's own event-time floor, if its kind carries one. */
|
|
168
|
+
function predicateSince(predicate) {
|
|
169
|
+
return 'since' in predicate && 'number' === typeof predicate.since ? predicate.since : 0;
|
|
170
|
+
}
|
|
156
171
|
export async function evaluatePredicate(session, predicate, since = 0,
|
|
157
172
|
// Compute the (extra-round-trip) near-miss diagnostics on element failures. Default true so a
|
|
158
173
|
// one-shot assert is fully diagnostic; the wait loop passes false on its interim polls (which read
|
|
159
174
|
// only `pass`) and true on the final timeout eval, so a flood no longer pays for a diagnostic nobody
|
|
160
175
|
// reads. Only element/text failures have a near-miss; everything else ignores this.
|
|
161
176
|
diagnose = true) {
|
|
162
|
-
|
|
177
|
+
// A predicate's own `since` is a TIGHTER event-time floor than the caller's — an agent that took
|
|
178
|
+
// `since` from the act it just performed is scoping the assertion to that action's aftermath. It is
|
|
179
|
+
// applied here, once, rather than in each eval: the floor means the same thing for every kind that
|
|
180
|
+
// reads the event stream, and net/console re-applying it is a no-op.
|
|
181
|
+
const events = session.eventsSince(Math.max(since, predicateSince(predicate)));
|
|
163
182
|
switch (predicate.kind) {
|
|
164
|
-
case
|
|
183
|
+
case PredicateKind.ELEMENT:
|
|
165
184
|
return evalElement(session, predicate.query, predicate.state, predicate.absent ?? false, diagnose);
|
|
166
|
-
case
|
|
167
|
-
return evalElement(session, { text: predicate.contains }, predicate.visible
|
|
168
|
-
case
|
|
185
|
+
case PredicateKind.TEXT:
|
|
186
|
+
return evalElement(session, { text: predicate.contains }, true === predicate.visible ? ElementState.VISIBLE : undefined, predicate.absent ?? false, diagnose);
|
|
187
|
+
case PredicateKind.NET:
|
|
169
188
|
return evalNet(events, predicate);
|
|
170
|
-
case
|
|
189
|
+
case PredicateKind.ROUTE:
|
|
171
190
|
return evalRoute(events, predicate);
|
|
172
|
-
case
|
|
191
|
+
case PredicateKind.CONSOLE:
|
|
173
192
|
return evalConsole(events, predicate);
|
|
174
|
-
case
|
|
193
|
+
case PredicateKind.ANIMATION:
|
|
175
194
|
return evalAnimation(events, predicate);
|
|
176
|
-
case
|
|
195
|
+
case PredicateKind.SIGNAL:
|
|
177
196
|
return evalSignal(events, predicate);
|
|
178
|
-
case
|
|
197
|
+
case PredicateKind.STATE:
|
|
179
198
|
return evalState(session, predicate);
|
|
180
|
-
case
|
|
199
|
+
case PredicateKind.SETTLED: {
|
|
181
200
|
// Drop events on learned-ambient regions (chat/ticker churn) before the settle check — by ref
|
|
182
201
|
// alone, NOT by attribution: window-attribution ("happened during the action window") is a time
|
|
183
202
|
// heuristic, never causation, so a chat message arriving mid-window must not hold settle open.
|
|
@@ -185,7 +204,7 @@ diagnose = true) {
|
|
|
185
204
|
const settleEvents = counts === undefined ? events : events.filter((e) => !isAmbient(counts, ambientKeyOf(e)));
|
|
186
205
|
return evalSettled(settleEvents, predicate, session.elapsed());
|
|
187
206
|
}
|
|
188
|
-
case
|
|
207
|
+
case PredicateKind.ALL_OF: {
|
|
189
208
|
const results = await Promise.all(predicate.predicates.map((p) => evaluatePredicate(session, p, since, diagnose)));
|
|
190
209
|
const failed = results.find((r) => !r.pass);
|
|
191
210
|
return failed === undefined
|
|
@@ -196,14 +215,14 @@ diagnose = true) {
|
|
|
196
215
|
evidence: results,
|
|
197
216
|
};
|
|
198
217
|
}
|
|
199
|
-
case
|
|
218
|
+
case PredicateKind.ANY_OF: {
|
|
200
219
|
const results = await Promise.all(predicate.predicates.map((p) => evaluatePredicate(session, p, since, diagnose)));
|
|
201
220
|
const passed = results.find((r) => r.pass);
|
|
202
221
|
return passed !== undefined
|
|
203
222
|
? { pass: true, evidence: passed.evidence }
|
|
204
223
|
: { pass: false, failureReason: 'no sub-predicate of anyOf matched', evidence: results };
|
|
205
224
|
}
|
|
206
|
-
case
|
|
225
|
+
case PredicateKind.NOT: {
|
|
207
226
|
const inner = await evaluatePredicate(session, predicate.predicate, since, diagnose);
|
|
208
227
|
return inner.pass
|
|
209
228
|
? { pass: false, failureReason: 'negated predicate unexpectedly held', evidence: inner }
|
|
@@ -244,12 +263,12 @@ const COUNT_CONFIRM_MS = 300;
|
|
|
244
263
|
* must stay that way, or every ordinary wait pays the confirmation delay for nothing.
|
|
245
264
|
*/
|
|
246
265
|
function assertsExactCount(predicate) {
|
|
247
|
-
if (
|
|
266
|
+
if (PredicateKind.ALL_OF === predicate.kind || PredicateKind.ANY_OF === predicate.kind) {
|
|
248
267
|
return predicate.predicates.some(assertsExactCount);
|
|
249
268
|
}
|
|
250
|
-
if (
|
|
269
|
+
if (PredicateKind.NOT === predicate.kind)
|
|
251
270
|
return assertsExactCount(predicate.predicate);
|
|
252
|
-
return
|
|
271
|
+
return PredicateKind.NET === predicate.kind && predicate.count !== undefined;
|
|
253
272
|
}
|
|
254
273
|
/**
|
|
255
274
|
* Evaluate now, else wait for it to become true (on each event + a poll) until timeout. `since` is
|
|
@@ -263,19 +282,36 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
|
|
|
263
282
|
failureReason: error instanceof Error ? error.message : String(error),
|
|
264
283
|
});
|
|
265
284
|
let cooldownTimer;
|
|
285
|
+
/** One-shot re-check timed to when a time-based predicate could first pass. See retryAfterMs. */
|
|
286
|
+
let hintTimer;
|
|
266
287
|
// An exact-count wait keeps watching after it first reads true — see COUNT_CONFIRM_MS.
|
|
267
288
|
const holdsForCount = assertsExactCount(predicate);
|
|
268
289
|
let confirming = false;
|
|
269
290
|
let confirmTimer;
|
|
291
|
+
/** Report a wait that could not run, and END it — see guardedCheck. */
|
|
292
|
+
const failWait = (error) => {
|
|
293
|
+
log('reticle_wait_failed', {
|
|
294
|
+
predicate: predicate.kind,
|
|
295
|
+
error: error instanceof Error ? error.message : String(error),
|
|
296
|
+
});
|
|
297
|
+
finish({
|
|
298
|
+
...failed(error),
|
|
299
|
+
failureReason: 'the wait could not be evaluated and was ended rather than left pending: ' +
|
|
300
|
+
(error instanceof Error ? error.message : String(error)),
|
|
301
|
+
});
|
|
302
|
+
};
|
|
270
303
|
const finish = (result) => {
|
|
271
304
|
if (done)
|
|
272
305
|
return;
|
|
273
306
|
done = true;
|
|
274
307
|
unsub();
|
|
308
|
+
unsubDisconnect?.();
|
|
275
309
|
clearInterval(interval);
|
|
276
310
|
clearTimeout(timer);
|
|
277
311
|
if (cooldownTimer !== undefined)
|
|
278
312
|
clearTimeout(cooldownTimer);
|
|
313
|
+
if (hintTimer !== undefined)
|
|
314
|
+
clearTimeout(hintTimer);
|
|
279
315
|
if (confirmTimer !== undefined)
|
|
280
316
|
clearTimeout(confirmTimer);
|
|
281
317
|
resolve(result);
|
|
@@ -305,8 +341,19 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
|
|
|
305
341
|
// final timeout eval below runs with full diagnostics.
|
|
306
342
|
void evaluatePredicate(session, predicate, since, false)
|
|
307
343
|
.then((r) => {
|
|
308
|
-
if (!r.pass)
|
|
344
|
+
if (!r.pass) {
|
|
345
|
+
// A time-based failure knows when it could stop being one — re-check THEN rather than on
|
|
346
|
+
// the next blind tick. Without this, every `settled` wait paid up to a full poll interval
|
|
347
|
+
// of dead time after the quiet window had already closed: measured at 566–627ms across
|
|
348
|
+
// the fleet for a 500ms window, on the call an agent makes after almost every action.
|
|
349
|
+
// Additive — the backstop interval below still runs, so a missed hint costs nothing.
|
|
350
|
+
const hint = r.retryAfterMs;
|
|
351
|
+
if ('number' === typeof hint && hint > 0 && hint < POLL_INTERVAL_MS) {
|
|
352
|
+
clearTimeout(hintTimer);
|
|
353
|
+
hintTimer = setTimeout(check, hint);
|
|
354
|
+
}
|
|
309
355
|
return;
|
|
356
|
+
}
|
|
310
357
|
// "Exactly N" cannot be concluded from a passing sample — the count can still rise. Hold,
|
|
311
358
|
// re-evaluate WITH diagnostics, and let that second read be the verdict: if an N+1th
|
|
312
359
|
// arrived in the meantime it now fails, carrying observed/expected rather than a bare no.
|
|
@@ -344,10 +391,54 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
|
|
|
344
391
|
}, MIN_RECHECK_GAP_MS);
|
|
345
392
|
});
|
|
346
393
|
};
|
|
394
|
+
/**
|
|
395
|
+
* Run a re-check so that NOTHING can leave this promise pending.
|
|
396
|
+
*
|
|
397
|
+
* `check` is fired from an event listener and an interval, neither of which is inside the
|
|
398
|
+
* awaited chain. A synchronous throw there reached the process as an uncaughtException; a
|
|
399
|
+
* rejection reached it as an unhandledRejection. Either way the wait never resolved and the tool
|
|
400
|
+
* handler never returned — the agent sees a call that simply never comes back.
|
|
401
|
+
*
|
|
402
|
+
* This half covers the SYNCHRONOUS throw only. The rejection half is handled inside `check`, by
|
|
403
|
+
* the `.catch` on its own promise chain — `check` returns void and voids the promise it starts,
|
|
404
|
+
* so there is nothing to hand back here to await. Said explicitly because the obvious-looking
|
|
405
|
+
* `if (result instanceof Promise) result.catch(…)` that used to sit here was dead code that read
|
|
406
|
+
* like the rejection guard, and removing `check`'s own `.catch` as redundant would have restored
|
|
407
|
+
* the exact hang this exists to prevent.
|
|
408
|
+
*
|
|
409
|
+
* That is the exact shape reported from a Plane (Next 14 + MobX) session, which tears the page
|
|
410
|
+
* session down and rebuilds it on EVERY navigation, so a `{kind:"route"}` predicate always races
|
|
411
|
+
* a teardown of the very session it is watching: `browser.command ok:true` for the click, and
|
|
412
|
+
* then no `tool.handler` for that callId, ever.
|
|
413
|
+
*
|
|
414
|
+
* A wait that cannot evaluate is a FAILED wait, not an eternal one. It now says so and finishes.
|
|
415
|
+
*/
|
|
416
|
+
const guardedCheck = () => {
|
|
417
|
+
try {
|
|
418
|
+
check();
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
failWait(error);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
// Bound to the CALL that started this wait. The listener fires from the WebSocket message
|
|
425
|
+
// handler and the interval from the timer queue — neither is in the awaited chain, so without
|
|
426
|
+
// this every re-check opened a new call at depth 0 and emitted a `browser.command` with no
|
|
427
|
+
// `tool.handler`. Measured on one healthy run: 23 such orphans, which is precisely the
|
|
428
|
+
// documented signature of a HUNG call. A diagnostic that fires on healthy runs is not one.
|
|
429
|
+
const boundCheck = bindSpanContext(guardedCheck);
|
|
347
430
|
const unsub = session.onEvent(() => {
|
|
348
|
-
|
|
431
|
+
boundCheck();
|
|
432
|
+
});
|
|
433
|
+
const unsubDisconnect = session.onDisconnect?.(() => {
|
|
434
|
+
// `observationLost` is what stops this being graded as an app defect. `pass: false` is still
|
|
435
|
+
// correct — the consequence was not seen to hold — but on its own it reached the verdict rule
|
|
436
|
+
// as ASSERTION_FAILED, so a reload mid-wait reported "the declared consequence did not hold"
|
|
437
|
+
// against a healthy component, by file and line. The flag is structured rather than inferred
|
|
438
|
+
// from this string, because every other `failureReason` here is prose about the APP.
|
|
439
|
+
finish({ pass: false, failureReason: 'session disconnected', observationLost: true });
|
|
349
440
|
});
|
|
350
|
-
const interval = setInterval(
|
|
441
|
+
const interval = setInterval(boundCheck, POLL_INTERVAL_MS);
|
|
351
442
|
const timer = setTimeout(() => {
|
|
352
443
|
void evaluatePredicate(session, predicate, since)
|
|
353
444
|
.then((r) => {
|
|
@@ -382,11 +473,11 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
|
|
|
382
473
|
* green ⇏ this-branch-held, re-checks each branch and keeps the winners.
|
|
383
474
|
*/
|
|
384
475
|
export async function provenExpectedLinks(session, predicate, since = 0) {
|
|
385
|
-
if (
|
|
476
|
+
if (PredicateKind.ALL_OF === predicate.kind) {
|
|
386
477
|
const per = await Promise.all(predicate.predicates.map((p) => provenExpectedLinks(session, p, since)));
|
|
387
478
|
return per.flat();
|
|
388
479
|
}
|
|
389
|
-
if (
|
|
480
|
+
if (PredicateKind.ANY_OF === predicate.kind) {
|
|
390
481
|
const per = await Promise.all(predicate.predicates.map(async (p) => (await evaluatePredicate(session, p, since)).pass
|
|
391
482
|
? provenExpectedLinks(session, p, since)
|
|
392
483
|
: []));
|
package/dist/events/reconcile.js
CHANGED
|
@@ -39,7 +39,7 @@ const ZERO_DECIMAL = new Set(['JPY', 'KRW', 'VND']);
|
|
|
39
39
|
const STATE_FIELDS = new Set(['status', 'state']);
|
|
40
40
|
const ITEM_KEYS = ['items', 'results', 'data', 'records', 'rows', 'entries'];
|
|
41
41
|
function isRecord(value) {
|
|
42
|
-
return typeof value
|
|
42
|
+
return 'object' === typeof value && value !== null && !Array.isArray(value);
|
|
43
43
|
}
|
|
44
44
|
/** Every object carrying an `id`, at any depth. These are the things a UI renders as rows. */
|
|
45
45
|
export function entitiesIn(node, out = []) {
|
|
@@ -102,7 +102,7 @@ function wrongCurrencyMark(text, amounts, expected) {
|
|
|
102
102
|
}
|
|
103
103
|
function markBefore(text, amount, expected) {
|
|
104
104
|
const at = text.indexOf(amount);
|
|
105
|
-
if (
|
|
105
|
+
if (-1 === at)
|
|
106
106
|
return undefined; // this rendering is not on screen — try the next
|
|
107
107
|
// Look just before the number, where a symbol or code sits. A window, not the whole page: the
|
|
108
108
|
// point is what is attached to THIS amount.
|
|
@@ -167,9 +167,9 @@ export function reconcile(bodies, pageText) {
|
|
|
167
167
|
// number must appear with a currency marker attached. A non-money number that happens to share a
|
|
168
168
|
// decimal form (a weight, a count) is rendered bare and never reaches the comparison.
|
|
169
169
|
const currency = entity['currency'];
|
|
170
|
-
if (typeof currency
|
|
170
|
+
if ('string' === typeof currency) {
|
|
171
171
|
for (const [field, value] of Object.entries(entity)) {
|
|
172
|
-
if (typeof value !== 'number' ||
|
|
172
|
+
if (typeof value !== 'number' || 'currency' === field)
|
|
173
173
|
continue;
|
|
174
174
|
const printed = majorForms(value, currency);
|
|
175
175
|
const wrong = wrongCurrencyMark(pageText, printed, currency);
|
|
@@ -196,7 +196,7 @@ export function reconcile(bodies, pageText) {
|
|
|
196
196
|
const others = [...(vocabulary.get(field) ?? [])].filter((v) => v !== value);
|
|
197
197
|
const shown = others.filter((v) => shows(pageText, v));
|
|
198
198
|
const key = `${id}:${field}`;
|
|
199
|
-
if (shown.length
|
|
199
|
+
if (0 === shown.length || seen.has(key))
|
|
200
200
|
continue;
|
|
201
201
|
seen.add(key);
|
|
202
202
|
found.push({
|
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/**
|
|
3
|
-
* The high-volume, low-signal floor. These fire continuously on any live page (count-up text, CSS
|
|
4
|
-
* animation frames, React commits, health heartbeats, scroll) and are the ONLY thing that should be
|
|
5
|
-
* sacrificed when the buffer is full. Everything else — a failed request, a console error, a domain
|
|
6
|
-
* signal, a truncation/blind-spot marker — is scarce evidence a verdict depends on, and a churning
|
|
7
|
-
* region must never be able to push it out of the window.
|
|
8
|
-
*/
|
|
9
|
-
const CHURN_TYPES = new Set([
|
|
10
|
-
EventType.DOM_TEXT,
|
|
11
|
-
EventType.ANIM_START,
|
|
12
|
-
EventType.ANIM_END,
|
|
13
|
-
EventType.RENDER_COMMIT,
|
|
14
|
-
EventType.PAGE_HEALTH,
|
|
15
|
-
EventType.SCROLL_POSITION,
|
|
16
|
-
]);
|
|
1
|
+
import { CHURN_TYPES, RING_BUFFER_DEFAULTS } from '@reticlehq/core';
|
|
17
2
|
/** How far forward to look for a churn event to sacrifice before falling back to plain FIFO. */
|
|
18
3
|
const CHURN_SCAN_LIMIT = 256;
|
|
19
4
|
/**
|
|
@@ -95,7 +80,7 @@ export class RingBuffer {
|
|
|
95
80
|
continue;
|
|
96
81
|
}
|
|
97
82
|
const victim = this.#findChurnAfterHead();
|
|
98
|
-
if (
|
|
83
|
+
if (-1 === victim) {
|
|
99
84
|
// Buffer genuinely full of high-signal events — fall back to FIFO so it stays bounded.
|
|
100
85
|
this.#totalBytes -= this.#eventBytes[this.#head] ?? 0;
|
|
101
86
|
this.#head += 1;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ContradictionKind, EventType } from '@reticlehq/core';
|
|
2
2
|
function pathOf(url) {
|
|
3
3
|
const q = url.indexOf('?');
|
|
4
|
-
return
|
|
4
|
+
return -1 === q ? url : url.slice(0, q);
|
|
5
5
|
}
|
|
6
6
|
function queryOf(url) {
|
|
7
7
|
const q = url.indexOf('?');
|
|
8
|
-
return
|
|
8
|
+
return -1 === q ? '' : url.slice(q + 1);
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Parameters that ENUMERATE a collection rather than select within it.
|
|
@@ -52,13 +52,13 @@ function flights(events) {
|
|
|
52
52
|
const issued = new Map();
|
|
53
53
|
const out = [];
|
|
54
54
|
for (const event of events) {
|
|
55
|
-
const id = typeof event.data['id']
|
|
55
|
+
const id = 'string' === typeof event.data['id'] ? event.data['id'] : undefined;
|
|
56
56
|
if (id === undefined)
|
|
57
57
|
continue;
|
|
58
|
-
const at = typeof event.t
|
|
58
|
+
const at = 'number' === typeof event.t ? event.t : 0;
|
|
59
59
|
if (event.type === EventType.NET_PENDING) {
|
|
60
|
-
const url = typeof event.data['url']
|
|
61
|
-
const method = typeof event.data['method']
|
|
60
|
+
const url = 'string' === typeof event.data['url'] ? event.data['url'] : '';
|
|
61
|
+
const method = 'string' === typeof event.data['method'] ? event.data['method'] : 'GET';
|
|
62
62
|
issued.set(id, { url, method, at });
|
|
63
63
|
continue;
|
|
64
64
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning "no element matched {testid: …}" into something an agent can act on.
|
|
3
|
+
*
|
|
4
|
+
* `reticle_query` on a missing testid already returns the testids that ARE present, so a typo is one
|
|
5
|
+
* step from fixed. The same failure through a PREDICATE — `act_and_wait { until: { kind:'element',
|
|
6
|
+
* query:{ testid } } }` — said only "no element matched" and stopped, naming nothing.
|
|
7
|
+
*
|
|
8
|
+
* The near-miss machinery covered an element in the wrong STATE and a wrong NAME for a role (which
|
|
9
|
+
* even lists the names it saw). Testid — the anchor this codebase calls the gold standard — had
|
|
10
|
+
* none, and it is the one agents use most.
|
|
11
|
+
*
|
|
12
|
+
* It also just became the highest-traffic failure in the product: putting act_and_wait on the
|
|
13
|
+
* verification path means agents assert far more, so a failed element assertion is now the common
|
|
14
|
+
* case rather than the rare one.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The "…but these ARE here" clause, or undefined when there is nothing useful to say.
|
|
18
|
+
*
|
|
19
|
+
* Returns the empty string when the queried testid is itself present: the miss was then about STATE,
|
|
20
|
+
* not presence, and "you asked for X; X is present" reads as a contradiction rather than a hint.
|
|
21
|
+
*/
|
|
22
|
+
export declare function describeTestidMiss(wanted: string, present: readonly string[]): string | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning "no element matched {testid: …}" into something an agent can act on.
|
|
3
|
+
*
|
|
4
|
+
* `reticle_query` on a missing testid already returns the testids that ARE present, so a typo is one
|
|
5
|
+
* step from fixed. The same failure through a PREDICATE — `act_and_wait { until: { kind:'element',
|
|
6
|
+
* query:{ testid } } }` — said only "no element matched" and stopped, naming nothing.
|
|
7
|
+
*
|
|
8
|
+
* The near-miss machinery covered an element in the wrong STATE and a wrong NAME for a role (which
|
|
9
|
+
* even lists the names it saw). Testid — the anchor this codebase calls the gold standard — had
|
|
10
|
+
* none, and it is the one agents use most.
|
|
11
|
+
*
|
|
12
|
+
* It also just became the highest-traffic failure in the product: putting act_and_wait on the
|
|
13
|
+
* verification path means agents assert far more, so a failed element assertion is now the common
|
|
14
|
+
* case rather than the rare one.
|
|
15
|
+
*/
|
|
16
|
+
/** Enough to recognise the one you meant; not so many that a large page floods the message. */
|
|
17
|
+
const MAX_LISTED = 12;
|
|
18
|
+
/**
|
|
19
|
+
* The "…but these ARE here" clause, or undefined when there is nothing useful to say.
|
|
20
|
+
*
|
|
21
|
+
* Returns the empty string when the queried testid is itself present: the miss was then about STATE,
|
|
22
|
+
* not presence, and "you asked for X; X is present" reads as a contradiction rather than a hint.
|
|
23
|
+
*/
|
|
24
|
+
export function describeTestidMiss(wanted, present) {
|
|
25
|
+
if (0 === present.length)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (present.includes(wanted))
|
|
28
|
+
return '';
|
|
29
|
+
const shown = present.slice(0, MAX_LISTED);
|
|
30
|
+
const rest = present.length - shown.length;
|
|
31
|
+
const tail = rest > 0 ? `, and ${String(rest)} more` : '';
|
|
32
|
+
return `testids present here: ${shown.join(', ')}${tail}`;
|
|
33
|
+
}
|
|
@@ -37,7 +37,7 @@ const SCALES = [100, 1000];
|
|
|
37
37
|
*/
|
|
38
38
|
const ID_PATTERN = /[A-Za-z]{2,}_[A-Za-z0-9]{2,}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}/g;
|
|
39
39
|
function parse(raw) {
|
|
40
|
-
if (typeof raw !== 'string' || raw.length
|
|
40
|
+
if (typeof raw !== 'string' || 0 === raw.length)
|
|
41
41
|
return undefined;
|
|
42
42
|
try {
|
|
43
43
|
return JSON.parse(raw);
|
|
@@ -53,12 +53,12 @@ function moneyByEntity(node, into) {
|
|
|
53
53
|
moneyByEntity(item, into);
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
if (typeof node !== 'object' ||
|
|
56
|
+
if (typeof node !== 'object' || null === node)
|
|
57
57
|
return;
|
|
58
58
|
const record = node;
|
|
59
59
|
// An id may arrive as a number (`{"id": 9}`) — normalised so a numeric path segment matches it.
|
|
60
60
|
const raw = record['id'];
|
|
61
|
-
const id =
|
|
61
|
+
const id = 'string' === typeof raw ? raw : 'number' === typeof raw ? String(raw) : undefined;
|
|
62
62
|
if (id !== undefined) {
|
|
63
63
|
for (const [key, value] of Object.entries(record)) {
|
|
64
64
|
if (!MONEY_FIELDS.has(key) || typeof value !== 'number')
|
|
@@ -79,9 +79,9 @@ function idsIn(url, body) {
|
|
|
79
79
|
if (segment.length > 0 && /^\d+$/.test(segment))
|
|
80
80
|
found.add(segment);
|
|
81
81
|
}
|
|
82
|
-
if (typeof body
|
|
82
|
+
if ('object' === typeof body && body !== null) {
|
|
83
83
|
for (const value of Object.values(body)) {
|
|
84
|
-
if (typeof value
|
|
84
|
+
if ('string' === typeof value)
|
|
85
85
|
for (const id of value.match(ID_PATTERN) ?? [])
|
|
86
86
|
found.add(id);
|
|
87
87
|
}
|
|
@@ -90,7 +90,7 @@ function idsIn(url, body) {
|
|
|
90
90
|
}
|
|
91
91
|
/** The scale between a known value and a sent one, or undefined when they are consistent. */
|
|
92
92
|
function scaleBetween(known, sent) {
|
|
93
|
-
if (
|
|
93
|
+
if (0 === sent || 0 === known)
|
|
94
94
|
return undefined;
|
|
95
95
|
for (const scale of SCALES) {
|
|
96
96
|
// A tolerance, because the major-unit form is a rounded render of the minor one.
|
|
@@ -127,11 +127,11 @@ export function findUnitMismatches(events, prior = []) {
|
|
|
127
127
|
for (const event of events) {
|
|
128
128
|
if (event.type !== EventType.NET_REQUEST)
|
|
129
129
|
continue;
|
|
130
|
-
const url = typeof event.data['url']
|
|
130
|
+
const url = 'string' === typeof event.data['url'] ? event.data['url'] : '';
|
|
131
131
|
const requestBody = parse(event.data['requestBody']);
|
|
132
132
|
// Compare BEFORE recording this call's own response, or a request that echoes its own reply
|
|
133
133
|
// would compare against itself.
|
|
134
|
-
if (typeof requestBody
|
|
134
|
+
if ('object' === typeof requestBody && requestBody !== null) {
|
|
135
135
|
const sentFields = requestBody;
|
|
136
136
|
for (const id of idsIn(url, requestBody)) {
|
|
137
137
|
const fields = known.get(id);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ReticleTool } from '../tools/tool-names.js';
|
|
3
|
-
import { loadNamedFlows, resolveChangedFiles } from '../cli-flow-commands.js';
|
|
3
|
+
import { loadNamedFlows, resolveChangedFiles } from '../cli/cli-flow-commands.js';
|
|
4
4
|
import { affectedSavedFlows } from './flow-sources.js';
|
|
5
5
|
/**
|
|
6
6
|
* `reticle_affected` — which saved flows must re-verify for a set of changed files.
|
|
@@ -40,13 +40,28 @@ export const AFFECTED_TOOLS = [
|
|
|
40
40
|
},
|
|
41
41
|
handler: async (deps, args) => {
|
|
42
42
|
const files = Array.isArray(args['files'])
|
|
43
|
-
? args['files'].filter((f) => typeof f
|
|
43
|
+
? args['files'].filter((f) => 'string' === typeof f)
|
|
44
44
|
: [];
|
|
45
|
-
const since = typeof args['since']
|
|
45
|
+
const since = 'string' === typeof args['since'] ? args['since'] : undefined;
|
|
46
46
|
const changedFiles = await resolveChangedFiles(files, since);
|
|
47
47
|
const flows = await loadNamedFlows(deps.fs, deps.reticleRoot);
|
|
48
48
|
const result = affectedSavedFlows(flows, changedFiles);
|
|
49
|
-
|
|
49
|
+
// Three different situations produced the same empty answer: no files changed, no flows saved,
|
|
50
|
+
// and a git ref that resolved to nothing. Only the first means "nothing to re-verify"; the
|
|
51
|
+
// others mean the question could not be answered, and re-running nothing on that basis is how a
|
|
52
|
+
// regression ships. Say which one it is.
|
|
53
|
+
const why = 0 === flows.length
|
|
54
|
+
? 'no saved flows exist yet, so nothing COULD be affected — record one with reticle_record then reticle_flow_save'
|
|
55
|
+
: 0 === changedFiles.length
|
|
56
|
+
? since === undefined && 0 === files.length
|
|
57
|
+
? 'no files were given and no `since` ref was passed, so nothing was compared — pass the files you edited, or a git ref'
|
|
58
|
+
: 'nothing changed against that input, so no saved flow needs re-verifying'
|
|
59
|
+
: undefined;
|
|
60
|
+
return {
|
|
61
|
+
changedFiles,
|
|
62
|
+
...result,
|
|
63
|
+
...(why === undefined ? {} : { observed: true, note: why }),
|
|
64
|
+
};
|
|
50
65
|
},
|
|
51
66
|
},
|
|
52
67
|
];
|
package/dist/flows/affected.js
CHANGED
|
@@ -23,7 +23,7 @@ export function affectedFlows(flows, changedFiles) {
|
|
|
23
23
|
const affected = [];
|
|
24
24
|
const unknownProvenance = [];
|
|
25
25
|
for (const flow of flows) {
|
|
26
|
-
if (flow.sources === undefined || flow.sources.length
|
|
26
|
+
if (flow.sources === undefined || 0 === flow.sources.length) {
|
|
27
27
|
affected.push(flow.name);
|
|
28
28
|
unknownProvenance.push(flow.name);
|
|
29
29
|
continue;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveAnnotateTarget(explicit: string | undefined, active: readonly string[]): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which recording an annotation belongs to when the call does not say.
|
|
3
|
+
*
|
|
4
|
+
* It used to be the literal name `default`, unconditionally. Reproduced on three apps: an agent that
|
|
5
|
+
* starts `reticle_record { recordingName: "my-flow" }`, acts, and then annotates without repeating
|
|
6
|
+
* the name gets `annotate_no_step` — because the steps are in "my-flow" and the annotation went
|
|
7
|
+
* looking in an empty "default". The agent's rational next move is to record MORE steps, into the
|
|
8
|
+
* wrong place.
|
|
9
|
+
*
|
|
10
|
+
* With exactly one recording in progress there is nothing to disambiguate, so that is the answer.
|
|
11
|
+
* With several, `default` stays the documented choice: silently picking one of the others would put
|
|
12
|
+
* an assertion in a flow the agent never named, which is worse than an error.
|
|
13
|
+
*/
|
|
14
|
+
/** The documented fallback name, matching what `reticle_record` starts when given none. */
|
|
15
|
+
const DEFAULT_RECORDING = 'default';
|
|
16
|
+
export function resolveAnnotateTarget(explicit, active) {
|
|
17
|
+
if (explicit !== undefined && explicit.length > 0)
|
|
18
|
+
return explicit;
|
|
19
|
+
return 1 === active.length ? (active[0] ?? DEFAULT_RECORDING) : DEFAULT_RECORDING;
|
|
20
|
+
}
|