@reticlehq/server 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +105 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse.d.ts +143 -0
- package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
- package/dist/{cli-port.js → cli/cli-port.js} +29 -8
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +182 -93
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.d.ts +0 -6
- package/dist/flows/decision.js +64 -8
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +67 -13
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.d.ts +17 -0
- package/dist/init/capabilities.js +54 -0
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +26 -0
- package/dist/init/detect.js +78 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +39 -0
- package/dist/init/next-patch.js +196 -0
- package/dist/init/node-io.js +48 -6
- package/dist/init/patch-kind.d.ts +20 -0
- package/dist/init/patch-kind.js +10 -0
- package/dist/init/plan-framework.d.ts +50 -0
- package/dist/init/plan-framework.js +341 -0
- package/dist/init/plan.d.ts +119 -6
- package/dist/init/plan.js +230 -131
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +22 -0
- package/dist/init/run.js +393 -28
- package/dist/init/slash-command.d.ts +27 -0
- package/dist/init/slash-command.js +61 -0
- package/dist/init/snippets.d.ts +41 -3
- package/dist/init/snippets.js +185 -10
- package/dist/init/vite-config.d.ts +4 -10
- package/dist/init/vite-config.js +15 -7
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.d.ts +15 -0
- package/dist/input/driven-redaction.js +76 -0
- package/dist/input/network-detail.d.ts +9 -3
- package/dist/input/network-detail.js +43 -23
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +58 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +20 -2
- package/dist/session/session.js +76 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +85 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/cli-parse.d.ts +0 -140
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -35
- package/dist/mcp-proxy.js +0 -190
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
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({
|
|
@@ -95,7 +95,7 @@ export class RingBuffer {
|
|
|
95
95
|
continue;
|
|
96
96
|
}
|
|
97
97
|
const victim = this.#findChurnAfterHead();
|
|
98
|
-
if (
|
|
98
|
+
if (-1 === victim) {
|
|
99
99
|
// Buffer genuinely full of high-signal events — fall back to FIFO so it stays bounded.
|
|
100
100
|
this.#totalBytes -= this.#eventBytes[this.#head] ?? 0;
|
|
101
101
|
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
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { aliasParam } from '../tools/alias-args.js';
|
|
2
3
|
import { AnnotationErrorCode, AnnotationSchema } from '@reticlehq/core';
|
|
3
4
|
import { RECOVERY } from '../tools/error-recovery.js';
|
|
4
5
|
/**
|
|
@@ -12,7 +13,7 @@ const UNKNOWN_KIND_RECOVERY = 'Annotations are structured, never free text. Pass
|
|
|
12
13
|
import { ReticleTool } from '../tools/tool-names.js';
|
|
13
14
|
import { asString } from '../tools/tools-helpers.js';
|
|
14
15
|
import { compileAnnotation } from './annotate.js';
|
|
15
|
-
|
|
16
|
+
import { resolveAnnotateTarget } from './annotate-target.js';
|
|
16
17
|
/**
|
|
17
18
|
* The reticle_annotate tool. A STRUCTURED annotation (the AnnotationSchema
|
|
18
19
|
* discriminated union) is compiled into the live recording's per-step expect / flow dynamic[] /
|
|
@@ -48,6 +49,12 @@ export const ANNOTATE_TOOLS = [
|
|
|
48
49
|
inputSchema: {
|
|
49
50
|
// `flow` selects the recording; `name`/`signal`/`testid`/`dataMatches` are the annotation fields.
|
|
50
51
|
flow: z.string().optional().describe("Named recording to annotate. Defaults to 'default'."),
|
|
52
|
+
// Every other flow tool says `flowName`, and reticle_flow_save's own description sends the
|
|
53
|
+
// agent here — so the spelling it arrives with is the one this tool did not accept.
|
|
54
|
+
flowName: z
|
|
55
|
+
.string()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe('Alias for `flow` (the name every other flow tool uses).'),
|
|
51
58
|
kind: z
|
|
52
59
|
.string()
|
|
53
60
|
.describe('Annotation kind: assert-signal | assert-visible | assert-state | mark-dynamic | success-state | intent.'),
|
|
@@ -114,7 +121,10 @@ export const ANNOTATE_TOOLS = [
|
|
|
114
121
|
code: z.string().optional(),
|
|
115
122
|
},
|
|
116
123
|
handler: (deps, args) => {
|
|
117
|
-
|
|
124
|
+
// The recording actually RUNNING when there is exactly one, not the literal name `default`.
|
|
125
|
+
// Reproduced on three apps: a named recording plus an unnamed annotate reported
|
|
126
|
+
// `annotate_no_step` while the steps sat in the recording the agent had started.
|
|
127
|
+
const name = resolveAnnotateTarget(asString(aliasParam(args, 'flow', ['flowName'])['flow']), deps.recordings.active());
|
|
118
128
|
// Structured boundary: a free NL string / unknown kind fails the schema → UNKNOWN_KIND.
|
|
119
129
|
const parsed = AnnotationSchema.safeParse(args);
|
|
120
130
|
if (!parsed.success) {
|
|
@@ -134,8 +144,20 @@ export const ANNOTATE_TOOLS = [
|
|
|
134
144
|
});
|
|
135
145
|
}
|
|
136
146
|
const outcome = compileAnnotation(parsed.data, stepCount ?? 0);
|
|
137
|
-
if (!outcome.result.ok)
|
|
147
|
+
if (!outcome.result.ok) {
|
|
148
|
+
// Name what IS recording. `annotate_no_step` alone sent the agent off to record MORE steps —
|
|
149
|
+
// into the same empty recording — when the steps it already has are in another one.
|
|
150
|
+
const active = deps.recordings.active();
|
|
151
|
+
const elsewhere = active.filter((r) => r !== name && (deps.recordings.stepCount(r) ?? 0) > 0);
|
|
152
|
+
if (outcome.result.code === AnnotationErrorCode.NO_STEP_TO_ANNOTATE &&
|
|
153
|
+
elsewhere.length > 0) {
|
|
154
|
+
return Promise.resolve({
|
|
155
|
+
...outcome.result,
|
|
156
|
+
recovery: `'${name}' has no steps yet, but ${elsewhere.map((r) => `'${r}'`).join(', ')} does. Pass \`flow\` to say which recording this belongs to, or drive an action first.`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
138
159
|
return Promise.resolve(outcome.result);
|
|
160
|
+
}
|
|
139
161
|
const patch = outcome.patch;
|
|
140
162
|
if (patch !== undefined) {
|
|
141
163
|
if (patch.dynamicAdd !== undefined)
|
package/dist/flows/annotate.js
CHANGED
|
@@ -16,7 +16,7 @@ import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICA
|
|
|
16
16
|
export function compileAnnotation(a, stepCount) {
|
|
17
17
|
switch (a.kind) {
|
|
18
18
|
case AnnotationKind.ASSERT_SIGNAL: {
|
|
19
|
-
if (
|
|
19
|
+
if (0 === stepCount)
|
|
20
20
|
return noStep();
|
|
21
21
|
const expect = { signal: a.name };
|
|
22
22
|
if (a.dataMatches !== undefined)
|
|
@@ -24,12 +24,12 @@ export function compileAnnotation(a, stepCount) {
|
|
|
24
24
|
return stepPatch(a, stepCount, expect);
|
|
25
25
|
}
|
|
26
26
|
case AnnotationKind.ASSERT_VISIBLE: {
|
|
27
|
-
if (
|
|
27
|
+
if (0 === stepCount)
|
|
28
28
|
return noStep();
|
|
29
29
|
return stepPatch(a, stepCount, { element: { testid: a.testid } });
|
|
30
30
|
}
|
|
31
31
|
case AnnotationKind.ASSERT_STATE: {
|
|
32
|
-
if (
|
|
32
|
+
if (0 === stepCount)
|
|
33
33
|
return noStep();
|
|
34
34
|
const state = { path: a.statePath };
|
|
35
35
|
if (a.store !== undefined)
|
|
@@ -38,6 +38,11 @@ export function compileAnnotation(a, stepCount) {
|
|
|
38
38
|
state.equals = a.equals;
|
|
39
39
|
return stepPatch(a, stepCount, { state });
|
|
40
40
|
}
|
|
41
|
+
case AnnotationKind.ASSERT_NET: {
|
|
42
|
+
if (0 === stepCount)
|
|
43
|
+
return noStep();
|
|
44
|
+
return stepPatch(a, stepCount, { net: a.net });
|
|
45
|
+
}
|
|
41
46
|
case AnnotationKind.MARK_DYNAMIC:
|
|
42
47
|
return {
|
|
43
48
|
result: { ok: true, target: AnnotationTarget.FLOW, compiled: describeCompiled(a) },
|
|
@@ -109,6 +114,14 @@ export function describeCompiled(a) {
|
|
|
109
114
|
return `${COMPILED_PREDICATE_PREFIX} assert ${a.testid} visible`;
|
|
110
115
|
case AnnotationKind.ASSERT_STATE:
|
|
111
116
|
return `${COMPILED_PREDICATE_PREFIX} assert state ${a.statePath}${a.equals !== undefined ? ` == ${JSON.stringify(a.equals)}` : ''}`;
|
|
117
|
+
case AnnotationKind.ASSERT_NET: {
|
|
118
|
+
// Say the COUNT out loud when there is one. "will assert POST /refund" and "will assert
|
|
119
|
+
// exactly 1x POST /refund" are different gates, and the confirmation is the only place the
|
|
120
|
+
// author sees which one they just attached.
|
|
121
|
+
const what = `${a.net.method ?? 'any'} ${a.net.urlContains ?? '(any url)'}`;
|
|
122
|
+
const times = a.net.count === undefined ? '' : ` exactly ${String(a.net.count)}x`;
|
|
123
|
+
return `${COMPILED_PREDICATE_PREFIX} assert${times} request ${what}`;
|
|
124
|
+
}
|
|
112
125
|
case AnnotationKind.MARK_DYNAMIC:
|
|
113
126
|
return `${COMPILED_PREDICATE_PREFIX} ignore ${a.testid} (dynamic)`;
|
|
114
127
|
case AnnotationKind.SUCCESS_STATE:
|
|
@@ -124,7 +137,7 @@ export function describeCompiled(a) {
|
|
|
124
137
|
}
|
|
125
138
|
if (a.console !== undefined) {
|
|
126
139
|
const level = a.console.level ?? 'error';
|
|
127
|
-
return `${COMPILED_PREDICATE_PREFIX} succeed when ${a.console.absent
|
|
140
|
+
return `${COMPILED_PREDICATE_PREFIX} succeed when ${true === a.console.absent ? `no console.${level}` : `console.${level}`}`;
|
|
128
141
|
}
|
|
129
142
|
return `${COMPILED_PREDICATE_PREFIX} succeed when ${a.testid ?? ''} visible`;
|
|
130
143
|
case AnnotationKind.INTENT:
|
|
@@ -12,7 +12,7 @@ export interface StepExpect {
|
|
|
12
12
|
step: number;
|
|
13
13
|
expect?: FlowExpect;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
interface DowngradeFinding {
|
|
16
16
|
step: number;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
@@ -21,3 +21,4 @@ export interface DowngradeFinding {
|
|
|
21
21
|
* lives with the affected index.
|
|
22
22
|
*/
|
|
23
23
|
export declare function detectDowngrades(before: readonly StepExpect[], after: readonly StepExpect[]): DowngradeFinding[];
|
|
24
|
+
export {};
|
|
@@ -1,74 +1,10 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
1
|
import type { FileSystemPort } from '../project/fs-port.js';
|
|
3
2
|
import type { StepExpect } from './assertion-integrity.js';
|
|
4
|
-
|
|
5
|
-
* The anti-reward-hacking baseline. Detecting an assertion DOWNGRADE requires knowing what the
|
|
6
|
-
* flow asserted the last time it PASSED — otherwise a weakened flow simply looks like a flow that always
|
|
7
|
-
* asserted little. Flows are git-checked and agent-editable, so we cannot trust the file itself as its own
|
|
8
|
-
* history.
|
|
9
|
-
*
|
|
10
|
-
* So: on every passing replay we record that flow's per-step assertion shape here, and the gate diffs the
|
|
11
|
-
* current file against it. Recorded only on PASS, deliberately — a failing run must never become the
|
|
12
|
-
* baseline a later weakening is measured against.
|
|
13
|
-
*
|
|
14
|
-
* Loads never throw: a missing/corrupt ledger degrades to "no baseline", which means no downgrade can be
|
|
15
|
-
* claimed. Failing OPEN is right here — accusing an agent of gaming the gate on the strength of an
|
|
16
|
-
* unreadable file would be worse than missing one downgrade.
|
|
17
|
-
*/
|
|
18
|
-
export declare const AssertionTiersFileSchema: z.ZodObject<{
|
|
19
|
-
version: z.ZodLiteral<1>;
|
|
20
|
-
flows: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
21
|
-
steps: z.ZodArray<z.ZodObject<{
|
|
22
|
-
step: z.ZodNumber;
|
|
23
|
-
expect: z.ZodOptional<z.ZodUnknown>;
|
|
24
|
-
}, "strip", z.ZodTypeAny, {
|
|
25
|
-
step: number;
|
|
26
|
-
expect?: unknown;
|
|
27
|
-
}, {
|
|
28
|
-
step: number;
|
|
29
|
-
expect?: unknown;
|
|
30
|
-
}>, "many">;
|
|
31
|
-
/** Source files this flow covered when it last passed — lets the gate scope DELETION findings to
|
|
32
|
-
* the files that actually changed, instead of flagging every intentionally-removed flow. */
|
|
33
|
-
sources: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
34
|
-
}, "strip", z.ZodTypeAny, {
|
|
35
|
-
steps: {
|
|
36
|
-
step: number;
|
|
37
|
-
expect?: unknown;
|
|
38
|
-
}[];
|
|
39
|
-
sources: string[];
|
|
40
|
-
}, {
|
|
41
|
-
steps: {
|
|
42
|
-
step: number;
|
|
43
|
-
expect?: unknown;
|
|
44
|
-
}[];
|
|
45
|
-
sources?: string[] | undefined;
|
|
46
|
-
}>>;
|
|
47
|
-
}, "strip", z.ZodTypeAny, {
|
|
48
|
-
version: 1;
|
|
49
|
-
flows: Record<string, {
|
|
50
|
-
steps: {
|
|
51
|
-
step: number;
|
|
52
|
-
expect?: unknown;
|
|
53
|
-
}[];
|
|
54
|
-
sources: string[];
|
|
55
|
-
}>;
|
|
56
|
-
}, {
|
|
57
|
-
version: 1;
|
|
58
|
-
flows: Record<string, {
|
|
59
|
-
steps: {
|
|
60
|
-
step: number;
|
|
61
|
-
expect?: unknown;
|
|
62
|
-
}[];
|
|
63
|
-
sources?: string[] | undefined;
|
|
64
|
-
}>;
|
|
65
|
-
}>;
|
|
66
|
-
export interface FlowBaseline {
|
|
3
|
+
interface FlowBaseline {
|
|
67
4
|
steps: StepExpect[];
|
|
68
5
|
sources: string[];
|
|
69
6
|
}
|
|
70
|
-
|
|
71
|
-
export declare const ASSERTION_TIERS_VERSION = 1;
|
|
7
|
+
type FlowTiers = Record<string, FlowBaseline>;
|
|
72
8
|
export declare class AssertionTiersStore {
|
|
73
9
|
#private;
|
|
74
10
|
constructor(fs: FileSystemPort, root: string);
|
|
@@ -77,3 +13,4 @@ export declare class AssertionTiersStore {
|
|
|
77
13
|
/** Record one flow's assertion shape + covered sources after it PASSED. Never breaks a replay. */
|
|
78
14
|
recordPassing(name: string, steps: readonly StepExpect[], sources?: readonly string[]): Promise<void>;
|
|
79
15
|
}
|
|
16
|
+
export {};
|
|
@@ -16,7 +16,7 @@ import { withFileLock } from '../project/file-lock.js';
|
|
|
16
16
|
* claimed. Failing OPEN is right here — accusing an agent of gaming the gate on the strength of an
|
|
17
17
|
* unreadable file would be worse than missing one downgrade.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
const AssertionTiersFileSchema = z.object({
|
|
20
20
|
version: z.literal(1),
|
|
21
21
|
flows: z.record(z.object({
|
|
22
22
|
steps: z.array(z.object({ step: z.number(), expect: z.unknown().optional() })),
|
|
@@ -25,7 +25,7 @@ export const AssertionTiersFileSchema = z.object({
|
|
|
25
25
|
sources: z.array(z.string()).default([]),
|
|
26
26
|
})),
|
|
27
27
|
});
|
|
28
|
-
|
|
28
|
+
const ASSERTION_TIERS_VERSION = 1;
|
|
29
29
|
export class AssertionTiersStore {
|
|
30
30
|
#fs;
|
|
31
31
|
#path;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which failing flows can support a verdict ABOUT THE CHANGED FILES.
|
|
3
|
+
*
|
|
4
|
+
* `affectedSavedFlows` deliberately re-runs any flow whose covered sources it cannot determine —
|
|
5
|
+
* over-running beats silently skipping — and reports those names in `unknownProvenance`. The verdict
|
|
6
|
+
* then ignored that and answered NO whenever anything failed, producing the observed
|
|
7
|
+
*
|
|
8
|
+
* verified: "no", because "1 of 1 covering flows failed
|
|
9
|
+
* (1 of them re-run only because Reticle cannot tell which sources they cover)"
|
|
10
|
+
*
|
|
11
|
+
* — a negative claim about `src/App.tsx` whose own explanation admits the evidence is not tied to it.
|
|
12
|
+
*
|
|
13
|
+
* This tool already refuses the mirror-image error: an uncovered change is UNKNOWN, never a green,
|
|
14
|
+
* because "nothing ran, so nothing was proved". A red nothing earned is the same mistake reversed.
|
|
15
|
+
*/
|
|
16
|
+
/** Failing flows minus the ones that only ran because their provenance is unknown. */
|
|
17
|
+
export declare function attributedFailures(failingFlows: readonly string[], unknownProvenance: readonly string[]): string[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which failing flows can support a verdict ABOUT THE CHANGED FILES.
|
|
3
|
+
*
|
|
4
|
+
* `affectedSavedFlows` deliberately re-runs any flow whose covered sources it cannot determine —
|
|
5
|
+
* over-running beats silently skipping — and reports those names in `unknownProvenance`. The verdict
|
|
6
|
+
* then ignored that and answered NO whenever anything failed, producing the observed
|
|
7
|
+
*
|
|
8
|
+
* verified: "no", because "1 of 1 covering flows failed
|
|
9
|
+
* (1 of them re-run only because Reticle cannot tell which sources they cover)"
|
|
10
|
+
*
|
|
11
|
+
* — a negative claim about `src/App.tsx` whose own explanation admits the evidence is not tied to it.
|
|
12
|
+
*
|
|
13
|
+
* This tool already refuses the mirror-image error: an uncovered change is UNKNOWN, never a green,
|
|
14
|
+
* because "nothing ran, so nothing was proved". A red nothing earned is the same mistake reversed.
|
|
15
|
+
*/
|
|
16
|
+
/** Failing flows minus the ones that only ran because their provenance is unknown. */
|
|
17
|
+
export function attributedFailures(failingFlows, unknownProvenance) {
|
|
18
|
+
const unattributable = new Set(unknownProvenance);
|
|
19
|
+
return failingFlows.filter((flow) => !unattributable.has(flow));
|
|
20
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* nominal" needs no detail; a deviation needs its name, because a status line that says only "1 deviation"
|
|
7
7
|
* forces the human to go digging, which is exactly the work the buddy exists to remove.
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
interface BuddyStatusInput {
|
|
10
10
|
/** Every saved flow. */
|
|
11
11
|
total: number;
|
|
12
12
|
/** Flows with a passing artifact. */
|
|
@@ -22,3 +22,4 @@ export interface BuddyStatusInput {
|
|
|
22
22
|
* `✗ 2 deviations: checkout, billing · 39 nominal · 1 flaky quarantined`
|
|
23
23
|
*/
|
|
24
24
|
export declare function formatBuddyStatus(input: BuddyStatusInput): string;
|
|
25
|
+
export {};
|
|
@@ -16,13 +16,13 @@ const MAX_NAMED = 2;
|
|
|
16
16
|
export function formatBuddyStatus(input) {
|
|
17
17
|
const parts = [];
|
|
18
18
|
const deviations = input.deviations.length;
|
|
19
|
-
if (
|
|
19
|
+
if (0 === deviations) {
|
|
20
20
|
parts.push(`✓ ${String(input.passing)}/${String(input.total)} flows nominal`);
|
|
21
21
|
}
|
|
22
22
|
else {
|
|
23
23
|
const named = input.deviations.slice(0, MAX_NAMED).join(', ');
|
|
24
24
|
const rest = deviations - Math.min(MAX_NAMED, deviations);
|
|
25
|
-
parts.push(`✗ ${String(deviations)} deviation${
|
|
25
|
+
parts.push(`✗ ${String(deviations)} deviation${1 === deviations ? '' : 's'}: ${named}${rest > 0 ? ` +${String(rest)} more` : ''}`);
|
|
26
26
|
parts.push(`${String(input.passing)} nominal`);
|
|
27
27
|
}
|
|
28
28
|
if (input.quarantined.length > 0) {
|
package/dist/flows/coverage.d.ts
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* (contract testids/signals/flows) actually touched by passing verification, so "everything" is honest
|
|
5
5
|
* about what everything means. Pure set math; the deviation report + gate carry the number.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
interface DeclaredSurface {
|
|
8
8
|
testids: readonly string[];
|
|
9
9
|
signals: readonly string[];
|
|
10
10
|
flows: readonly string[];
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
interface CoverageDimension {
|
|
13
13
|
total: number;
|
|
14
14
|
covered: number;
|
|
15
15
|
/** Percent covered, rounded; 100 when nothing is declared (vacuously complete). */
|
|
@@ -25,3 +25,4 @@ export interface Coverage {
|
|
|
25
25
|
overallPct: number;
|
|
26
26
|
}
|
|
27
27
|
export declare function computeCoverage(declared: DeclaredSurface, exercised: DeclaredSurface): Coverage;
|
|
28
|
+
export {};
|
package/dist/flows/coverage.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
function dimension(declared, exercised) {
|
|
8
8
|
const uncovered = declared.filter((d) => !exercised.has(d));
|
|
9
9
|
const covered = declared.length - uncovered.length;
|
|
10
|
-
const pct = declared.length
|
|
10
|
+
const pct = 0 === declared.length ? 100 : Math.round((covered / declared.length) * 100);
|
|
11
11
|
return { total: declared.length, covered, pct, uncovered };
|
|
12
12
|
}
|
|
13
13
|
export function computeCoverage(declared, exercised) {
|
|
@@ -16,6 +16,6 @@ export function computeCoverage(declared, exercised) {
|
|
|
16
16
|
const flows = dimension(declared.flows, new Set(exercised.flows));
|
|
17
17
|
const total = testids.total + signals.total + flows.total;
|
|
18
18
|
const covered = testids.covered + signals.covered + flows.covered;
|
|
19
|
-
const overallPct =
|
|
19
|
+
const overallPct = 0 === total ? 100 : Math.round((covered / total) * 100);
|
|
20
20
|
return { testids, signals, flows, overallPct };
|
|
21
21
|
}
|
package/dist/flows/decision.d.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { type FlowFile, type FlowReplayResult, type ReplayDecision, type SuiteVerdict } from '@reticlehq/core';
|
|
2
2
|
export declare function buildDecision(result: FlowReplayResult, flow?: FlowFile): ReplayDecision;
|
|
3
|
-
/**
|
|
4
|
-
* Aggregate per-flow replays into one suite verdict — the autonomous loop's consolidated answer
|
|
5
|
-
* after a build: did anything break, and what's the prioritized fix list. Pure: pass the already
|
|
6
|
-
* decision-annotated replay results + their flows. Passing flows are counted; only failures carry
|
|
7
|
-
* detail (token-cheap), each with the actionable decision so the agent fixes without re-querying.
|
|
8
|
-
*/
|
|
9
3
|
export declare function buildSuiteVerdict(runs: ReadonlyArray<{
|
|
10
4
|
replay: FlowReplayResult;
|
|
11
5
|
flow?: FlowFile;
|