@reticlehq/server 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +63 -13
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +1 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +9 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +2 -11
- package/dist/{cli-parse.js → cli/cli-parse.js} +113 -60
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
- package/dist/{cli-port.js → cli/cli-port.js} +9 -9
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +174 -92
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.js +24 -9
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +53 -7
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.js +4 -1
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +3 -2
- package/dist/init/detect.js +16 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +2 -4
- package/dist/init/next-patch.js +109 -19
- package/dist/init/node-io.js +23 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +132 -8
- package/dist/init/plan.d.ts +73 -8
- package/dist/init/plan.js +133 -34
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +5 -0
- package/dist/init/run.js +210 -36
- package/dist/init/slash-command.d.ts +9 -0
- package/dist/init/slash-command.js +10 -1
- package/dist/init/snippets.d.ts +7 -10
- package/dist/init/snippets.js +44 -15
- package/dist/init/vite-config.js +1 -1
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.js +1 -1
- package/dist/input/network-detail.js +8 -8
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +52 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +14 -2
- package/dist/session/session.js +69 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +81 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -67
- package/dist/mcp-proxy.js +0 -337
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which directories to look in for the app, when `reticle init` runs at a monorepo root.
|
|
3
|
+
*
|
|
4
|
+
* This used to be the literal list `['apps', 'packages']`. Measured on a real repo with three Next
|
|
5
|
+
* apps at `web/`, `admin/` and `space/`, it found none — so the redirect never fired, init ran
|
|
6
|
+
* against the root, and it reported ✓ for writing `app/reticle-dev.tsx` into a directory Next never
|
|
7
|
+
* compiles. A ⚠ tells a human to act; a ✓ tells them it is handled.
|
|
8
|
+
*
|
|
9
|
+
* A workspace DECLARES its packages, so that declaration is used first and directory names are not
|
|
10
|
+
* guessed at all. Where nothing is declared, every top-level directory is a better candidate than two
|
|
11
|
+
* hardcoded ones — filtered by the usual non-source suspects, and still subject to the `looksLikeApp`
|
|
12
|
+
* check that follows, so a wrong guess here costs a `package.json` read.
|
|
13
|
+
*/
|
|
14
|
+
interface WorkspaceSources {
|
|
15
|
+
/** Raw contents of pnpm-workspace.yaml, when present. */
|
|
16
|
+
pnpmWorkspace?: string;
|
|
17
|
+
/** The `workspaces` field of package.json — array form or the yarn/npm object form. */
|
|
18
|
+
pkgWorkspaces?: unknown;
|
|
19
|
+
/** Directory names at the repo root, used when nothing is declared. */
|
|
20
|
+
topLevelDirs?: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
export declare function workspaceParents(sources: WorkspaceSources): string[];
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which directories to look in for the app, when `reticle init` runs at a monorepo root.
|
|
3
|
+
*
|
|
4
|
+
* This used to be the literal list `['apps', 'packages']`. Measured on a real repo with three Next
|
|
5
|
+
* apps at `web/`, `admin/` and `space/`, it found none — so the redirect never fired, init ran
|
|
6
|
+
* against the root, and it reported ✓ for writing `app/reticle-dev.tsx` into a directory Next never
|
|
7
|
+
* compiles. A ⚠ tells a human to act; a ✓ tells them it is handled.
|
|
8
|
+
*
|
|
9
|
+
* A workspace DECLARES its packages, so that declaration is used first and directory names are not
|
|
10
|
+
* guessed at all. Where nothing is declared, every top-level directory is a better candidate than two
|
|
11
|
+
* hardcoded ones — filtered by the usual non-source suspects, and still subject to the `looksLikeApp`
|
|
12
|
+
* check that follows, so a wrong guess here costs a `package.json` read.
|
|
13
|
+
*/
|
|
14
|
+
/** Directories that are never a workspace package, whatever the layout. */
|
|
15
|
+
const NEVER_A_PACKAGE = new Set(['node_modules', 'dist', 'build', 'out', 'coverage', 'target']);
|
|
16
|
+
/** `packages/*` -> `packages`; `web` -> `web`. The base directory a glob searches. */
|
|
17
|
+
function globBase(pattern) {
|
|
18
|
+
const base = pattern
|
|
19
|
+
.split('/')[0]
|
|
20
|
+
?.trim()
|
|
21
|
+
.replace(/^['"]|['"]$/g, '');
|
|
22
|
+
return base === undefined || '' === base || '.' === base || base.includes('*') ? undefined : base;
|
|
23
|
+
}
|
|
24
|
+
function fromPnpm(yaml) {
|
|
25
|
+
// A deliberately small reader rather than a YAML dependency: this file is a list of globs, and the
|
|
26
|
+
// only shape that matters is `- 'pattern'` under `packages:`. A malformed file yields nothing,
|
|
27
|
+
// which falls through to the top-level scan rather than failing the install.
|
|
28
|
+
const out = [];
|
|
29
|
+
let inPackages = false;
|
|
30
|
+
for (const line of yaml.split('\n')) {
|
|
31
|
+
if (/^packages\s*:/.test(line)) {
|
|
32
|
+
inPackages = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (inPackages && /^\S/.test(line))
|
|
36
|
+
break;
|
|
37
|
+
const item = /^\s*-\s*(.+)$/.exec(line);
|
|
38
|
+
if (inPackages && item?.[1] !== undefined) {
|
|
39
|
+
const base = globBase(item[1]);
|
|
40
|
+
if (base !== undefined)
|
|
41
|
+
out.push(base);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
function fromPkg(workspaces) {
|
|
47
|
+
const list = Array.isArray(workspaces)
|
|
48
|
+
? workspaces
|
|
49
|
+
: 'object' === typeof workspaces && workspaces !== null
|
|
50
|
+
? workspaces.packages
|
|
51
|
+
: undefined;
|
|
52
|
+
if (!Array.isArray(list))
|
|
53
|
+
return [];
|
|
54
|
+
return list
|
|
55
|
+
.filter((p) => 'string' === typeof p)
|
|
56
|
+
.map(globBase)
|
|
57
|
+
.filter((p) => p !== undefined);
|
|
58
|
+
}
|
|
59
|
+
export function workspaceParents(sources) {
|
|
60
|
+
const declared = [
|
|
61
|
+
...(sources.pnpmWorkspace === undefined ? [] : fromPnpm(sources.pnpmWorkspace)),
|
|
62
|
+
...fromPkg(sources.pkgWorkspaces),
|
|
63
|
+
];
|
|
64
|
+
const candidates = declared.length > 0
|
|
65
|
+
? declared
|
|
66
|
+
: (sources.topLevelDirs ?? []).filter((d) => !d.startsWith('.') && !NEVER_A_PACKAGE.has(d));
|
|
67
|
+
return [...new Set(candidates)];
|
|
68
|
+
}
|
|
@@ -35,7 +35,7 @@ function invalidate() {
|
|
|
35
35
|
}
|
|
36
36
|
/** Record what a session declared sensitive. Called when its hello lands. */
|
|
37
37
|
export function declareDrivenRedactionKeys(sessionId, keys) {
|
|
38
|
-
if (keys.length
|
|
38
|
+
if (0 === keys.length) {
|
|
39
39
|
if (declaredBySession.delete(sessionId))
|
|
40
40
|
invalidate();
|
|
41
41
|
return;
|
|
@@ -21,7 +21,7 @@ const DEFAULT_POLICY = { isSensitiveKey: defaultIsSensitiveKey };
|
|
|
21
21
|
function redactByKey(value, policy) {
|
|
22
22
|
if (Array.isArray(value))
|
|
23
23
|
return value.map((v) => redactByKey(v, policy));
|
|
24
|
-
if (value !== null && typeof value
|
|
24
|
+
if (value !== null && 'object' === typeof value) {
|
|
25
25
|
const out = {};
|
|
26
26
|
for (const [k, v] of Object.entries(value)) {
|
|
27
27
|
out[k] = policy.isSensitiveKey(k) ? REDACTED_VALUE : redactByKey(v, policy);
|
|
@@ -82,15 +82,15 @@ export function buildNetworkDetail(raw, policy = DEFAULT_POLICY) {
|
|
|
82
82
|
status: raw.status,
|
|
83
83
|
headers: projectHeaders(raw.headers, policy),
|
|
84
84
|
...(raw.resourceType === undefined ? {} : { resourceType: raw.resourceType }),
|
|
85
|
-
...(raw.requestBody === undefined || raw.requestBody.length
|
|
85
|
+
...(raw.requestBody === undefined || 0 === raw.requestBody.length
|
|
86
86
|
? {}
|
|
87
87
|
: { requestBody: projectWireBody(raw.requestBody, policy) }),
|
|
88
|
-
...(raw.pageUrl === undefined || raw.pageUrl.length
|
|
88
|
+
...(raw.pageUrl === undefined || 0 === raw.pageUrl.length ? {} : { pageUrl: raw.pageUrl }),
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
function keyOf(url, method) {
|
|
92
|
-
const m = typeof method
|
|
93
|
-
const u = typeof url
|
|
92
|
+
const m = 'string' === typeof method ? method.toUpperCase() : '';
|
|
93
|
+
const u = 'string' === typeof url ? url : '';
|
|
94
94
|
return `${m} ${u}`;
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
@@ -127,9 +127,9 @@ export function mergeNetworkDetail(events) {
|
|
|
127
127
|
// finding — that is the whole reason for capturing it — so the authoritative one wins and the
|
|
128
128
|
// divergence is flagged rather than silently resolved.
|
|
129
129
|
const wireBody = e.data['requestBody'];
|
|
130
|
-
if (typeof wireBody
|
|
130
|
+
if ('string' === typeof wireBody) {
|
|
131
131
|
const pageBody = data['requestBody'];
|
|
132
|
-
if (typeof pageBody
|
|
132
|
+
if ('string' === typeof pageBody && pageBody !== wireBody) {
|
|
133
133
|
data['requestBodyDivergedFromPage'] = true;
|
|
134
134
|
}
|
|
135
135
|
data['requestBody'] = wireBody;
|
|
@@ -162,7 +162,7 @@ export function attachNetworkDetail(page, emit) {
|
|
|
162
162
|
status: response.status(),
|
|
163
163
|
headers,
|
|
164
164
|
...(resourceType === undefined ? {} : { resourceType }),
|
|
165
|
-
...(
|
|
165
|
+
...(null === postData ? {} : { requestBody: postData }),
|
|
166
166
|
pageUrl: page.url(),
|
|
167
167
|
},
|
|
168
168
|
// Resolved per response, not per attachment: a session declaring extra keys can connect
|
|
@@ -5,7 +5,7 @@ import { sessionIdShape } from '../tools/tool-kit.js';
|
|
|
5
5
|
import { asString } from '../tools/tools-helpers.js';
|
|
6
6
|
function mockProvider(deps) {
|
|
7
7
|
const p = deps.realInput;
|
|
8
|
-
return p !== undefined && typeof p.setMocks
|
|
8
|
+
return p !== undefined && 'function' === typeof p.setMocks ? p : undefined;
|
|
9
9
|
}
|
|
10
10
|
const ruleShape = z.object({
|
|
11
11
|
urlContains: z
|
|
@@ -80,7 +80,7 @@ export const NETWORK_MOCK_TOOLS = [
|
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
83
|
-
const rules = args['clear']
|
|
83
|
+
const rules = true === args['clear'] ? [] : toRules(args['mocks']);
|
|
84
84
|
const applied = await provider.setMocks(session.url, rules);
|
|
85
85
|
return { applied, count: applied ? rules.length : 0 };
|
|
86
86
|
},
|
|
@@ -10,7 +10,7 @@ export function matchMock(rules, req) {
|
|
|
10
10
|
continue;
|
|
11
11
|
if (rule.method !== undefined && rule.method.toUpperCase() !== req.method.toUpperCase())
|
|
12
12
|
continue;
|
|
13
|
-
if (rule.abort
|
|
13
|
+
if (true === rule.abort)
|
|
14
14
|
return { kind: 'abort' };
|
|
15
15
|
const outcome = {
|
|
16
16
|
kind: 'fulfill',
|
|
@@ -27,11 +27,11 @@ export function matchMock(rules, req) {
|
|
|
27
27
|
}
|
|
28
28
|
/** Apply a matched outcome to a Playwright Route. Exported so the route handler is testable in isolation. */
|
|
29
29
|
export async function applyOutcome(route, outcome, sleep) {
|
|
30
|
-
if (outcome.kind
|
|
30
|
+
if ('continue' === outcome.kind) {
|
|
31
31
|
await route.continue();
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
if (outcome.kind
|
|
34
|
+
if ('abort' === outcome.kind) {
|
|
35
35
|
await route.abort('failed');
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
@@ -51,7 +51,7 @@ const realSleep = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
|
51
51
|
*/
|
|
52
52
|
export async function installNetworkMocks(page, rules, sleep = realSleep) {
|
|
53
53
|
await page.unroute('**/*').catch(() => undefined);
|
|
54
|
-
if (rules.length
|
|
54
|
+
if (0 === rules.length)
|
|
55
55
|
return;
|
|
56
56
|
await page.route('**/*', (route) => {
|
|
57
57
|
const req = route.request();
|
package/dist/input/real-input.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { gotoOptions } from '../pool/playwright-launcher.js';
|
|
1
2
|
import { BrowserLaunchKind } from '@reticlehq/core';
|
|
2
3
|
import { getSessionMetrics } from '../telemetry/session-metrics.js';
|
|
3
4
|
import { classifyConnectFailure } from '../telemetry/connect-failure.js';
|
|
@@ -93,7 +94,7 @@ const SCREENSHOT_DETERMINISM = { style: HIDE_RETICLE_CHROME_CSS, animations: 'di
|
|
|
93
94
|
export async function capturePage(page, opts) {
|
|
94
95
|
const buf = await page.screenshot(opts.clip !== undefined
|
|
95
96
|
? { ...SCREENSHOT_DETERMINISM, clip: opts.clip }
|
|
96
|
-
: opts.fullPage
|
|
97
|
+
: true === opts.fullPage
|
|
97
98
|
? { ...SCREENSHOT_DETERMINISM, fullPage: true }
|
|
98
99
|
: { ...SCREENSHOT_DETERMINISM });
|
|
99
100
|
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
@@ -269,7 +270,11 @@ export class LaunchedRealInputProvider {
|
|
|
269
270
|
if (this.#onNetworkDetail !== undefined)
|
|
270
271
|
attachNetworkDetail(page, this.#onNetworkDetail);
|
|
271
272
|
try {
|
|
272
|
-
|
|
273
|
+
// Same navigation rule as the pool, and for the same measured reason: Playwright's default
|
|
274
|
+
// waits for `load`, which an app with one never-finishing subresource never fires — 30s of
|
|
275
|
+
// nothing and then a failure that blames the app. The SDK connect is a module script, so it
|
|
276
|
+
// has already run by DOMContentLoaded. See gotoOptions.
|
|
277
|
+
await page.goto(this.#driveUrl, gotoOptions(undefined));
|
|
273
278
|
}
|
|
274
279
|
catch (e) {
|
|
275
280
|
throw new DriveError(DriveErrorCode.NAVIGATE_FAILED, e instanceof Error ? e.message : String(e));
|
|
@@ -369,7 +374,7 @@ export function selectPage(pages, sessionUrl) {
|
|
|
369
374
|
return exact;
|
|
370
375
|
const target = stripVolatile(sessionUrl);
|
|
371
376
|
const loose = pages.filter((p) => stripVolatile(p.url()) === target);
|
|
372
|
-
return loose.length
|
|
377
|
+
return 1 === loose.length ? loose[0] : undefined;
|
|
373
378
|
}
|
|
374
379
|
function stripVolatile(url) {
|
|
375
380
|
const hash = url.indexOf('#');
|
|
@@ -38,7 +38,7 @@ export async function scrollToFind(session, q, opts = {}) {
|
|
|
38
38
|
return { found: true, element: hit, scrolls, exhausted: false };
|
|
39
39
|
// Fall through to linear refinement from current position (already near the target).
|
|
40
40
|
const data = asRecord(sr.result);
|
|
41
|
-
if (data['atEnd']
|
|
41
|
+
if (true === data['atEnd'] || false === data['scrolled']) {
|
|
42
42
|
return { found: false, scrolls, exhausted: true };
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -50,7 +50,7 @@ export async function scrollToFind(session, q, opts = {}) {
|
|
|
50
50
|
if (hit !== undefined)
|
|
51
51
|
return { found: true, element: hit, scrolls, exhausted: false };
|
|
52
52
|
// Reached the bottom or the container would not move — no more rows to reveal.
|
|
53
|
-
if (data['atEnd']
|
|
53
|
+
if (true === data['atEnd'] || false === data['scrolled']) {
|
|
54
54
|
return { found: false, scrolls, exhausted: true };
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -1,7 +1,2 @@
|
|
|
1
1
|
import type { ToolDef } from '../tools/tools.js';
|
|
2
|
-
/**
|
|
3
|
-
* reticle_scroll_to — the windowed/virtualized-list escape hatch. A plain reticle_query
|
|
4
|
-
* only sees rendered nodes, so an off-screen row in a react-window/react-virtualized list returns
|
|
5
|
-
* nothing. This scrolls the container until the row mounts, then returns its ref.
|
|
6
|
-
*/
|
|
7
2
|
export declare const SCROLL_TOOLS: ToolDef[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { QueryBy } from '@reticlehq/core';
|
|
2
3
|
import { ReticleTool } from '../tools/tool-names.js';
|
|
3
4
|
import { asNumber, asString } from '../tools/tools-helpers.js';
|
|
4
5
|
import { scrollToFind } from './scroll-find.js';
|
|
@@ -7,14 +8,20 @@ import { scrollToFind } from './scroll-find.js';
|
|
|
7
8
|
* only sees rendered nodes, so an off-screen row in a react-window/react-virtualized list returns
|
|
8
9
|
* nothing. This scrolls the container until the row mounts, then returns its ref.
|
|
9
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* The query strategies, derived from QueryBy — the same vocabulary reticle_query uses, and for the
|
|
13
|
+
* same reason. A free string here produced the identical false negative: `by:'css'` scrolled the
|
|
14
|
+
* whole list and reported `{ found: false, exhausted: true }`, which reads as "the row is not in
|
|
15
|
+
* this list" rather than "that strategy does not exist".
|
|
16
|
+
*/
|
|
17
|
+
const QUERY_BY_LIST = Object.values(QueryBy).join(' | ');
|
|
18
|
+
const queryByEnum = z.enum(Object.values(QueryBy));
|
|
10
19
|
export const SCROLL_TOOLS = [
|
|
11
20
|
{
|
|
12
21
|
name: ReticleTool.SCROLL_TO,
|
|
13
|
-
description: 'Find an element in a VIRTUALIZED list that has not rendered yet. Pass `by` (
|
|
22
|
+
description: 'Find an element in a VIRTUALIZED list that has not rendered yet. Pass `by` (see the parameter for the exact strategies) and `value` (query string) to identify the target row. Scrolls the container until the row mounts, the list ends, or maxScrolls (default 20) is spent. Pass targetIndex + totalCount for bisection — jumps directly to the estimated offset in one scroll (e.g. targetIndex:800 totalCount:1000 jumps to 80% of scrollHeight). Returns { found, element?, scrolls, exhausted }.',
|
|
14
23
|
inputSchema: {
|
|
15
|
-
by:
|
|
16
|
-
.string()
|
|
17
|
-
.describe('Query strategy for finding the target: role | text | testid | label | placeholder | alt'),
|
|
24
|
+
by: queryByEnum.describe(`Query strategy for finding the target: ${QUERY_BY_LIST}`),
|
|
18
25
|
value: z
|
|
19
26
|
.string()
|
|
20
27
|
.describe('Query value for the selected strategy (the element to scroll into view).'),
|
|
@@ -8,7 +8,7 @@ const MIN_DIM = 64;
|
|
|
8
8
|
const MAX_DIM = 10000;
|
|
9
9
|
function viewportProvider(deps) {
|
|
10
10
|
const p = deps.realInput;
|
|
11
|
-
return p !== undefined && typeof p.setViewport
|
|
11
|
+
return p !== undefined && 'function' === typeof p.setViewport
|
|
12
12
|
? p
|
|
13
13
|
: undefined;
|
|
14
14
|
}
|
|
@@ -54,6 +54,6 @@ export const VIEWPORT_TOOLS = [
|
|
|
54
54
|
];
|
|
55
55
|
/** Clamp a requested dimension into [MIN_DIM, MAX_DIM]; a missing/NaN value falls back to MIN_DIM. */
|
|
56
56
|
function clampDim(value) {
|
|
57
|
-
const n = typeof value
|
|
57
|
+
const n = 'number' === typeof value && Number.isFinite(value) ? Math.round(value) : MIN_DIM;
|
|
58
58
|
return Math.max(MIN_DIM, Math.min(n, MAX_DIM));
|
|
59
59
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { FileSystemPort } from '../project/fs-port.js';
|
|
2
2
|
import { type AmbientCounts } from './ambient.js';
|
|
3
|
-
/** Bumped on any breaking change to the persisted ambient-map shape. */
|
|
4
|
-
export declare const AMBIENT_FILE_VERSION = 1;
|
|
5
3
|
/**
|
|
6
4
|
* Persists the learned ambient-churn region map at `.reticle/ambient.json`. Local, single-project
|
|
7
5
|
* memory (stays OSS — OSS-VS-SERVER). Loads never throw: missing/malformed/wrong-version degrades to
|
|
@@ -2,7 +2,7 @@ import { dirname } from 'node:path';
|
|
|
2
2
|
import { reticleDirPaths } from '../project/reticle-dir.js';
|
|
3
3
|
import { AmbientFileSchema } from './ambient.js';
|
|
4
4
|
/** Bumped on any breaking change to the persisted ambient-map shape. */
|
|
5
|
-
|
|
5
|
+
const AMBIENT_FILE_VERSION = 1;
|
|
6
6
|
/**
|
|
7
7
|
* Persists the learned ambient-churn region map at `.reticle/ambient.json`. Local, single-project
|
|
8
8
|
* memory (stays OSS — OSS-VS-SERVER). Loads never throw: missing/malformed/wrong-version degrades to
|
package/dist/journal/ambient.js
CHANGED
|
@@ -8,7 +8,7 @@ import { z } from 'zod';
|
|
|
8
8
|
*/
|
|
9
9
|
export function ambientKeyOf(event) {
|
|
10
10
|
const region = event.data?.['region'];
|
|
11
|
-
if (typeof region
|
|
11
|
+
if ('string' === typeof region && region.length > 0)
|
|
12
12
|
return region;
|
|
13
13
|
return event.ref;
|
|
14
14
|
}
|
|
@@ -7,7 +7,7 @@ export interface JournalTarget {
|
|
|
7
7
|
elapsed(): number;
|
|
8
8
|
setJournal(recorder: JournalRecorder, reader?: JournalReader): void;
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
interface JournalAttachDeps {
|
|
11
11
|
fs: FileSystemPort;
|
|
12
12
|
reticleRoot: string;
|
|
13
13
|
/** Journaling is on by default; the opt-out (`.reticle.json` journal:false / env) sets this false. */
|
|
@@ -19,3 +19,4 @@ export interface JournalAttachDeps {
|
|
|
19
19
|
* journaling concern). The recorder's clock is the session's own elapsed time.
|
|
20
20
|
*/
|
|
21
21
|
export declare function makeJournalAttach(deps: JournalAttachDeps): (session: JournalTarget) => void;
|
|
22
|
+
export {};
|
|
@@ -6,13 +6,13 @@ function headlineFor(report) {
|
|
|
6
6
|
if (report.insufficientSamples) {
|
|
7
7
|
return `envelope too new (need ${String(MIN_ENVELOPE_SAMPLES)} runs) — see causal summary${truncNote}`;
|
|
8
8
|
}
|
|
9
|
-
if (report.deviations.length
|
|
10
|
-
const s = report.nominalSegments
|
|
9
|
+
if (0 === report.deviations.length) {
|
|
10
|
+
const s = 1 === report.nominalSegments ? '' : 's';
|
|
11
11
|
return `${String(report.nominalSegments)} segment${s} nominal${truncNote}`;
|
|
12
12
|
}
|
|
13
13
|
const routes = [...new Set(report.deviations.map((d) => d.route))].join(', ');
|
|
14
14
|
const n = report.deviations.length;
|
|
15
|
-
return `${String(n)} deviation${
|
|
15
|
+
return `${String(n)} deviation${1 === n ? '' : 's'}: ${routes}${truncNote}`;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Judge observed segments against their per-route envelopes. Segments without a route, or whose
|
|
@@ -27,7 +27,7 @@ export function buildDeviationReport(envelopes, segments, zThreshold) {
|
|
|
27
27
|
if (segment.route === undefined)
|
|
28
28
|
continue;
|
|
29
29
|
// A truncated segment's counts understate reality — judging it could read a false "nominal".
|
|
30
|
-
if (segment.truncated
|
|
30
|
+
if (true === segment.truncated) {
|
|
31
31
|
truncated += 1;
|
|
32
32
|
continue;
|
|
33
33
|
}
|
|
@@ -36,7 +36,7 @@ export function buildDeviationReport(envelopes, segments, zThreshold) {
|
|
|
36
36
|
continue;
|
|
37
37
|
judged += 1;
|
|
38
38
|
const segDeviations = compareSegment(envelope, segment, zThreshold);
|
|
39
|
-
if (segDeviations.length
|
|
39
|
+
if (0 === segDeviations.length)
|
|
40
40
|
nominal += 1;
|
|
41
41
|
else
|
|
42
42
|
deviations.push(...segDeviations);
|
|
@@ -47,7 +47,7 @@ export function buildDeviationReport(envelopes, segments, zThreshold) {
|
|
|
47
47
|
nominalSegments: nominal,
|
|
48
48
|
judgedSegments: judged,
|
|
49
49
|
truncatedSegments: truncated,
|
|
50
|
-
insufficientSamples:
|
|
50
|
+
insufficientSamples: 0 === judged,
|
|
51
51
|
};
|
|
52
52
|
return { ...base, headline: headlineFor(base) };
|
|
53
53
|
}
|
|
@@ -20,7 +20,7 @@ export async function reportAndAccumulate(store, segments, zThreshold) {
|
|
|
20
20
|
if (segment.route === undefined)
|
|
21
21
|
continue;
|
|
22
22
|
// Never learn from a truncated sample — its understated counts would poison the baseline.
|
|
23
|
-
if (segment.truncated
|
|
23
|
+
if (true === segment.truncated)
|
|
24
24
|
continue;
|
|
25
25
|
const current = envelopes.get(segment.route) ?? emptyEnvelope(segment.route);
|
|
26
26
|
envelopes.set(segment.route, addSegmentToEnvelope(current, segment));
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { FileSystemPort } from '../project/fs-port.js';
|
|
2
2
|
import { type RouteEnvelope } from './envelope.js';
|
|
3
|
-
/** Bumped on any breaking change to the persisted envelope shape (matches the runs/contract convention). */
|
|
4
|
-
export declare const ENVELOPE_FILE_VERSION = 1;
|
|
5
3
|
/**
|
|
6
4
|
* Persists the learned per-route envelopes at `.reticle/envelopes.json`. Local, single-project statistics
|
|
7
5
|
* (single-player memory stays OSS — OSS-VS-SERVER). Loads never throw: a missing, malformed, or
|
|
@@ -3,7 +3,7 @@ import { dirname } from 'node:path';
|
|
|
3
3
|
import { reticleDirPaths } from '../project/reticle-dir.js';
|
|
4
4
|
import { RouteEnvelopeSchema } from './envelope.js';
|
|
5
5
|
/** Bumped on any breaking change to the persisted envelope shape (matches the runs/contract convention). */
|
|
6
|
-
|
|
6
|
+
const ENVELOPE_FILE_VERSION = 1;
|
|
7
7
|
const EnvelopeFileSchema = z.object({
|
|
8
8
|
version: z.literal(ENVELOPE_FILE_VERSION),
|
|
9
9
|
routes: z.record(RouteEnvelopeSchema),
|
|
@@ -21,7 +21,7 @@ export declare function stddev(stats: MetricStats): number;
|
|
|
21
21
|
/** How many stddevs above the mean `x` sits (0 when there's no spread yet). */
|
|
22
22
|
export declare function zScore(stats: MetricStats, x: number): number;
|
|
23
23
|
/** The metrics an envelope tracks per route. */
|
|
24
|
-
|
|
24
|
+
declare const ENVELOPE_METRICS: readonly ["durationMs", "net", "netErrors", "consoleErrors"];
|
|
25
25
|
export type EnvelopeMetric = (typeof ENVELOPE_METRICS)[number];
|
|
26
26
|
export interface RouteEnvelope {
|
|
27
27
|
route: string;
|
|
@@ -31,8 +31,6 @@ export interface RouteEnvelope {
|
|
|
31
31
|
}
|
|
32
32
|
/** Below this, an envelope is noise — the deviation report falls back to the causal summary. */
|
|
33
33
|
export declare const MIN_ENVELOPE_SAMPLES = 3;
|
|
34
|
-
/** Default flag threshold in stddevs. The number is a placeholder for fleet-calibrated priors. */
|
|
35
|
-
export declare const DEFAULT_Z_THRESHOLD = 3;
|
|
36
34
|
/** Persisted shape of a route envelope. Validated on load; a bad file degrades to no envelope. */
|
|
37
35
|
export declare const RouteEnvelopeSchema: z.ZodObject<{
|
|
38
36
|
route: z.ZodString;
|
|
@@ -229,3 +227,4 @@ export interface Deviation {
|
|
|
229
227
|
* falls back to the causal summary in that case.
|
|
230
228
|
*/
|
|
231
229
|
export declare function compareSegment(envelope: RouteEnvelope, segment: SegmentRollup, zThreshold?: number): Deviation[];
|
|
230
|
+
export {};
|
package/dist/journal/envelope.js
CHANGED
|
@@ -15,14 +15,14 @@ export function stddev(stats) {
|
|
|
15
15
|
/** How many stddevs above the mean `x` sits (0 when there's no spread yet). */
|
|
16
16
|
export function zScore(stats, x) {
|
|
17
17
|
const sd = stddev(stats);
|
|
18
|
-
return
|
|
18
|
+
return 0 === sd ? 0 : (x - stats.mean) / sd;
|
|
19
19
|
}
|
|
20
20
|
/** The metrics an envelope tracks per route. */
|
|
21
|
-
|
|
21
|
+
const ENVELOPE_METRICS = ['durationMs', 'net', 'netErrors', 'consoleErrors'];
|
|
22
22
|
/** Below this, an envelope is noise — the deviation report falls back to the causal summary. */
|
|
23
23
|
export const MIN_ENVELOPE_SAMPLES = 3;
|
|
24
24
|
/** Default flag threshold in stddevs. The number is a placeholder for fleet-calibrated priors. */
|
|
25
|
-
|
|
25
|
+
const DEFAULT_Z_THRESHOLD = 3;
|
|
26
26
|
const MetricStatsSchema = z.object({
|
|
27
27
|
count: z.number(),
|
|
28
28
|
mean: z.number(),
|
|
@@ -8,7 +8,7 @@ export function mergeEventsBySeq(journal, buffer) {
|
|
|
8
8
|
const bySeq = new Map();
|
|
9
9
|
const noSeq = [];
|
|
10
10
|
for (const event of [...journal, ...buffer]) {
|
|
11
|
-
if (typeof event.seq
|
|
11
|
+
if ('number' === typeof event.seq)
|
|
12
12
|
bySeq.set(event.seq, event);
|
|
13
13
|
else
|
|
14
14
|
noSeq.push(event);
|
|
@@ -8,7 +8,7 @@ export interface JournalSink {
|
|
|
8
8
|
export interface JournalReader {
|
|
9
9
|
readEvents(): Promise<ReticleEvent[]>;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
interface JournalRecorderOptions {
|
|
12
12
|
/** Injected elapsed-ms clock (never read from Date here — the clock-injection rule). */
|
|
13
13
|
now: () => number;
|
|
14
14
|
/** Flush the pending-event batch once this many accumulate. */
|
|
@@ -35,3 +35,4 @@ export declare class JournalRecorder {
|
|
|
35
35
|
/** Persist any buffered events now (call on session end). Awaits the write chain to settle. */
|
|
36
36
|
flush(): Promise<void>;
|
|
37
37
|
}
|
|
38
|
+
export {};
|
|
@@ -27,7 +27,7 @@ export class JournalRecorder {
|
|
|
27
27
|
const active = this.#active;
|
|
28
28
|
if (active !== undefined) {
|
|
29
29
|
out = { ...event, actionId: active.actionId, attribution: EventAttribution.WINDOW };
|
|
30
|
-
if (typeof event.seq
|
|
30
|
+
if ('number' === typeof event.seq) {
|
|
31
31
|
active.seqFrom =
|
|
32
32
|
active.seqFrom === undefined ? event.seq : Math.min(active.seqFrom, event.seq);
|
|
33
33
|
active.seqTo = active.seqTo === undefined ? event.seq : Math.max(active.seqTo, event.seq);
|
|
@@ -71,7 +71,7 @@ export class JournalRecorder {
|
|
|
71
71
|
await this.#chain;
|
|
72
72
|
}
|
|
73
73
|
#enqueueFlush() {
|
|
74
|
-
if (this.#pending.length
|
|
74
|
+
if (0 === this.#pending.length)
|
|
75
75
|
return;
|
|
76
76
|
const batch = this.#pending;
|
|
77
77
|
this.#pending = [];
|
package/dist/journal/rollups.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventType } from '@reticlehq/core';
|
|
1
|
+
import { EventType, StreamDirection } from '@reticlehq/core';
|
|
2
2
|
function finalize(acc) {
|
|
3
3
|
return {
|
|
4
4
|
...(acc.route === undefined ? {} : { route: acc.route }),
|
|
@@ -9,13 +9,13 @@ function finalize(acc) {
|
|
|
9
9
|
net: { total: acc.netTotal, errors: acc.netErrors },
|
|
10
10
|
consoleErrors: acc.consoleErrors,
|
|
11
11
|
statePathsChanged: acc.statePaths,
|
|
12
|
-
...(acc.streams.frames
|
|
12
|
+
...(0 === acc.streams.frames ? {} : { streams: acc.streams }),
|
|
13
13
|
...(acc.truncated ? { truncated: true } : {}),
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
function isNetError(event) {
|
|
17
17
|
const status = event.data['status'];
|
|
18
|
-
return event.data['ok']
|
|
18
|
+
return false === event.data['ok'] || ('number' === typeof status && status >= 400);
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Fold an ordered event list into route segments split at each ROUTE_CHANGE. Pure: no clock, no IO —
|
|
@@ -41,13 +41,13 @@ export function computeSegments(events) {
|
|
|
41
41
|
if (acc !== undefined)
|
|
42
42
|
segments.push(finalize(acc));
|
|
43
43
|
const pathname = event.data['pathname'];
|
|
44
|
-
acc = open(typeof pathname
|
|
44
|
+
acc = open('string' === typeof pathname ? pathname : undefined, event.t);
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
47
|
if (acc === undefined)
|
|
48
48
|
acc = open(undefined, event.t);
|
|
49
49
|
acc.to = event.t;
|
|
50
|
-
if (typeof event.actionId
|
|
50
|
+
if ('string' === typeof event.actionId)
|
|
51
51
|
acc.actionIds.add(event.actionId);
|
|
52
52
|
if (event.type === EventType.NET_REQUEST) {
|
|
53
53
|
acc.netTotal += 1;
|
|
@@ -59,17 +59,17 @@ export function computeSegments(events) {
|
|
|
59
59
|
}
|
|
60
60
|
if (event.type === EventType.STATE_CHANGE) {
|
|
61
61
|
const name = event.data['name'];
|
|
62
|
-
if (typeof name
|
|
62
|
+
if ('string' === typeof name && !acc.statePaths.includes(name))
|
|
63
63
|
acc.statePaths.push(name);
|
|
64
64
|
}
|
|
65
65
|
if (event.type === EventType.NET_STREAM) {
|
|
66
66
|
acc.streams.frames += 1;
|
|
67
67
|
const direction = event.data['direction'];
|
|
68
|
-
if (
|
|
68
|
+
if (StreamDirection.OPEN === direction)
|
|
69
69
|
acc.streams.opened += 1;
|
|
70
|
-
else if (
|
|
70
|
+
else if (StreamDirection.IN === direction)
|
|
71
71
|
acc.streams.in += 1;
|
|
72
|
-
else if (
|
|
72
|
+
else if (StreamDirection.OUT === direction)
|
|
73
73
|
acc.streams.out += 1;
|
|
74
74
|
}
|
|
75
75
|
if (event.type === EventType.TRUNCATED || event.type === EventType.TRANSPORT_OVERFLOW) {
|
|
@@ -17,7 +17,7 @@ export interface SessionEndTarget {
|
|
|
17
17
|
ambientCounts(): AmbientCounts;
|
|
18
18
|
ownAmbientCounts(): AmbientCounts;
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
interface SessionEndDeps {
|
|
21
21
|
fs: FileSystemPort;
|
|
22
22
|
reticleRoot: string;
|
|
23
23
|
/** Journaling/persistence off (opt-out) → teardown is a no-op. */
|
|
@@ -29,3 +29,4 @@ export interface SessionEndDeps {
|
|
|
29
29
|
* starts already knowing which regions churn.
|
|
30
30
|
*/
|
|
31
31
|
export declare function makeSessionEnd(deps: SessionEndDeps): (session: SessionEndTarget) => Promise<void>;
|
|
32
|
+
export {};
|
|
@@ -37,7 +37,7 @@ export class SessionJournal {
|
|
|
37
37
|
this.#sessionId = sessionId;
|
|
38
38
|
}
|
|
39
39
|
async appendEvents(events) {
|
|
40
|
-
if (events.length
|
|
40
|
+
if (0 === events.length)
|
|
41
41
|
return;
|
|
42
42
|
await this.#ensureDir();
|
|
43
43
|
const text = `${events.map((e) => JSON.stringify(e)).join('\n')}\n`;
|
|
@@ -108,11 +108,11 @@ export class SessionJournal {
|
|
|
108
108
|
#ingestTail(tail) {
|
|
109
109
|
const cache = this.#eventCache ?? (this.#eventCache = []);
|
|
110
110
|
const end = tail.lastIndexOf('\n') + 1;
|
|
111
|
-
if (
|
|
111
|
+
if (0 === end)
|
|
112
112
|
return 0; // no complete line yet
|
|
113
113
|
const complete = tail.slice(0, end);
|
|
114
114
|
for (const line of complete.split('\n')) {
|
|
115
|
-
if (line.length
|
|
115
|
+
if (0 === line.length)
|
|
116
116
|
continue;
|
|
117
117
|
let parsed;
|
|
118
118
|
try {
|
|
@@ -148,7 +148,7 @@ export class SessionJournal {
|
|
|
148
148
|
}
|
|
149
149
|
const out = [];
|
|
150
150
|
for (const line of text.split('\n')) {
|
|
151
|
-
if (line.length
|
|
151
|
+
if (0 === line.length)
|
|
152
152
|
continue;
|
|
153
153
|
let parsed;
|
|
154
154
|
try {
|