@reticlehq/server 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +719 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +13 -1
- package/dist/{bridge.js → bridge/bridge.js} +95 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/cli/cli-doctor.d.ts +1 -0
- package/dist/cli/cli-doctor.js +135 -0
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse-feedback.d.ts +31 -0
- package/dist/cli/cli-parse-feedback.js +56 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
- package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
- package/dist/{cli-port.js → cli/cli-port.js} +9 -9
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli/doctor-daemon-line.d.ts +37 -0
- package/dist/cli/doctor-daemon-line.js +35 -0
- package/dist/cli/port-holder.d.ts +43 -0
- package/dist/cli/port-holder.js +99 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +302 -100
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +73 -0
- package/dist/daemon/daemon-resilience.js +279 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +105 -48
- package/dist/daemon/heartbeat.d.ts +115 -0
- package/dist/daemon/heartbeat.js +141 -0
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +23 -2
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
- package/dist/daemon/port-presence.d.ts +76 -0
- package/dist/daemon/port-presence.js +93 -0
- package/dist/daemon/state-dir.d.ts +7 -0
- package/dist/daemon/state-dir.js +59 -0
- package/dist/daemon/unclean-predecessor.d.ts +23 -0
- package/dist/daemon/unclean-predecessor.js +27 -0
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +53 -20
- package/dist/events/predicate-eval.js +179 -55
- package/dist/events/predicate-parse.d.ts +23 -0
- package/dist/events/predicate-parse.js +54 -0
- package/dist/events/predicate.d.ts +7 -2
- package/dist/events/predicate.js +126 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +2 -17
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.js +24 -9
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +6 -8
- package/dist/flows/flow-replay.js +132 -48
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +156 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +63 -21
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/role-drift-reason.d.ts +20 -0
- package/dist/flows/role-drift-reason.js +39 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/suite-flakes.d.ts +33 -0
- package/dist/flows/suite-flakes.js +32 -0
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +35 -3
- package/dist/honesty/blind-spots.js +57 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +31 -3
- package/dist/honesty/verified.js +64 -8
- package/dist/http-server.js +23 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +9 -6
- package/dist/index.js +41 -25
- package/dist/init/agent-rules.d.ts +20 -1
- package/dist/init/agent-rules.js +64 -8
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.js +4 -1
- package/dist/init/cra.d.ts +50 -0
- package/dist/init/cra.js +106 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +9 -2
- package/dist/init/detect.js +24 -5
- package/dist/init/mcp-clients.d.ts +81 -0
- package/dist/init/mcp-clients.js +248 -0
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +2 -4
- package/dist/init/next-patch.js +109 -19
- package/dist/init/node-io.js +29 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +166 -8
- package/dist/init/plan.d.ts +85 -6
- package/dist/init/plan.js +196 -33
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +23 -1
- package/dist/init/run.js +338 -47
- package/dist/init/slash-command.d.ts +9 -0
- package/dist/init/slash-command.js +10 -1
- package/dist/init/snippets.d.ts +7 -10
- package/dist/init/snippets.js +44 -15
- package/dist/init/vite-config.js +1 -1
- package/dist/init/windows-quote.d.ts +38 -0
- package/dist/init/windows-quote.js +70 -0
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.js +1 -1
- package/dist/input/network-detail.js +8 -8
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +32 -0
- package/dist/mcp/mcp-outage.js +55 -0
- package/dist/mcp/mcp-proxy.d.ts +134 -0
- package/dist/mcp/mcp-proxy.js +720 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +139 -37
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/browser-pool.d.ts +9 -0
- package/dist/pool/browser-pool.js +38 -9
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/attachment-history.d.ts +46 -0
- package/dist/session/attachment-history.js +65 -0
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +45 -0
- package/dist/session/no-session-diagnosis.js +120 -0
- package/dist/session/no-session-watch.d.ts +21 -0
- package/dist/session/no-session-watch.js +69 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/pending-commands.js +1 -0
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +43 -8
- package/dist/session/session-info.js +46 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +6 -0
- package/dist/session/session-manager.js +124 -12
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +15 -2
- package/dist/session/session.js +77 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/crash-cause.d.ts +47 -0
- package/dist/telemetry/crash-cause.js +104 -0
- package/dist/telemetry/daemon-telemetry.d.ts +38 -1
- package/dist/telemetry/daemon-telemetry.js +37 -12
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-class.d.ts +26 -0
- package/dist/telemetry/error-class.js +52 -0
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +8 -3
- package/dist/telemetry/feedback-cli.js +52 -8
- package/dist/telemetry/feedback-context.d.ts +23 -6
- package/dist/telemetry/feedback-context.js +105 -5
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.d.ts +7 -0
- package/dist/telemetry/git-facts.js +5 -5
- package/dist/telemetry/project-profile.js +2 -1
- package/dist/telemetry/session-metrics.d.ts +47 -3
- package/dist/telemetry/session-metrics.js +315 -15
- package/dist/telemetry/telemetry.d.ts +45 -2
- package/dist/telemetry/telemetry.js +150 -37
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +114 -118
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +9 -1
- package/dist/tools/assert-verdict.js +21 -6
- package/dist/tools/browser-tools.js +36 -11
- package/dist/tools/contract-tools.d.ts +0 -5
- package/dist/tools/contract-tools.js +27 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +96 -19
- package/dist/tools/error-recovery.d.ts +34 -2
- package/dist/tools/error-recovery.js +255 -4
- package/dist/tools/feedback-invite.d.ts +48 -0
- package/dist/tools/feedback-invite.js +71 -0
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -12
- package/dist/tools/invoke-tool.js +126 -33
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-arrival.d.ts +26 -0
- package/dist/tools/navigate-arrival.js +60 -0
- package/dist/tools/navigate-result.d.ts +31 -0
- package/dist/tools/navigate-result.js +38 -0
- package/dist/tools/observe-tools.js +72 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +81 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +93 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +16 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +217 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/README.md +55 -0
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures-dispatch-receiver.yml +76 -0
- package/docs/fixtures.md +45 -0
- package/docs/flows.md +166 -0
- package/docs/gate-plan.md +98 -0
- package/docs/gates.md +117 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/matrix/2.5.0/claude-code-machine.json +19 -0
- package/docs/matrix/2.5.0/codex-machine.json +20 -0
- package/docs/matrix/2.5.0/cursor-machine.json +22 -0
- package/docs/matrix/2.5.0/gemini-machine.json +22 -0
- package/docs/matrix/2.5.0/opencode-machine.json +22 -0
- package/docs/matrix/2.5.0/vscode-machine.json +22 -0
- package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
- package/docs/matrix/MATRIX.md +26 -0
- package/docs/matrix/README.md +59 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/system-map.md +152 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +191 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-doctor.d.ts +0 -6
- package/dist/cli-doctor.js +0 -60
- package/dist/cli-launch.js +0 -112
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -67
- package/dist/mcp-proxy.js +0 -337
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
package/dist/session/session.js
CHANGED
|
@@ -2,7 +2,8 @@ import { LastAct } from './last-act.js';
|
|
|
2
2
|
import { commandTimeoutMessage } from './command-timeout.js';
|
|
3
3
|
import { readHealthEvent } from './session-health.js';
|
|
4
4
|
import { PendingCommands } from './pending-commands.js';
|
|
5
|
-
import {
|
|
5
|
+
import { span } from '../trace.js';
|
|
6
|
+
import { AppRuntime, EventType, HumanControlDataSchema, HumanControlKind, HumanMarkDataSchema, ReticleCommand, MessageKind, parseEventPayload, PresenterTone, SESSION_HEALTH, SESSION_LEASE, SESSION_LIFECYCLE, SessionState, } from '@reticlehq/core';
|
|
6
7
|
import { RingBuffer } from '../events/ring-buffer.js';
|
|
7
8
|
import { filterEvents, mergeEventsBySeq, } from '../journal/journal-query.js';
|
|
8
9
|
import {} from '../journal/ambient.js';
|
|
@@ -13,6 +14,7 @@ import { ReviewStore } from './review-store.js';
|
|
|
13
14
|
import { buildSessionRecommendation } from './session-recommendation.js';
|
|
14
15
|
import { buildPresenterArgs } from './presenter-args.js';
|
|
15
16
|
import { buildSessionLease } from './session-lease.js';
|
|
17
|
+
import { buildSessionInfo } from './session-info.js';
|
|
16
18
|
const DEFAULT_COMMAND_TIMEOUT_MS = 8000;
|
|
17
19
|
/** Prefix on correlated command ids (c1, c2, …) — distinguishes them from mark ids. */
|
|
18
20
|
const COMMAND_ID_PREFIX = 'c';
|
|
@@ -32,6 +34,8 @@ export class Session {
|
|
|
32
34
|
title;
|
|
33
35
|
adapters;
|
|
34
36
|
hasCapabilities;
|
|
37
|
+
/** Set when the page's SDK version differs from the daemon's (see version-skew.ts). */
|
|
38
|
+
versionSkew;
|
|
35
39
|
/**
|
|
36
40
|
* Extra key names this app declared sensitive via `connect({ redact: { keys } })`. Held so the
|
|
37
41
|
* DRIVEN path can redact them too — a request body the daemon captures from the network stack
|
|
@@ -44,12 +48,15 @@ export class Session {
|
|
|
44
48
|
#buffer = new RingBuffer();
|
|
45
49
|
#pending = new PendingCommands();
|
|
46
50
|
#listeners = new Set();
|
|
51
|
+
#disconnectListeners = new Set();
|
|
47
52
|
#actionSeq = 0;
|
|
48
53
|
#lastSeenAt;
|
|
49
54
|
#hidden = false;
|
|
50
55
|
/** Which shell the page reported (PAGE_HEALTH). Undefined until the first report lands. */
|
|
51
56
|
#runtime;
|
|
52
57
|
#engine;
|
|
58
|
+
/** Which browser the page said it is (chrome/edge/arc/…). Undefined on an SDK too old to report one. */
|
|
59
|
+
#brand;
|
|
53
60
|
#focused = true;
|
|
54
61
|
/** Liveness: wall-clock of the last AGENT command (distinct from browser chatter / lastSeen). */
|
|
55
62
|
#lastAgentActivityAt;
|
|
@@ -101,14 +108,18 @@ export class Session {
|
|
|
101
108
|
* `runtime` is optional so an older SDK (which does not report it) still works — it simply loses
|
|
102
109
|
* the desktop-specific timeout diagnosis rather than breaking.
|
|
103
110
|
*/
|
|
104
|
-
applyHealth(hidden, focused, runtime, engine) {
|
|
111
|
+
applyHealth(hidden, focused, runtime, engine, brand) {
|
|
105
112
|
this.#hidden = hidden;
|
|
106
113
|
this.#focused = focused;
|
|
107
|
-
if (
|
|
114
|
+
if (AppRuntime.ELECTRON === runtime ||
|
|
115
|
+
AppRuntime.TAURI === runtime ||
|
|
116
|
+
AppRuntime.WEB === runtime) {
|
|
108
117
|
this.#runtime = runtime;
|
|
109
118
|
}
|
|
110
119
|
if (engine !== undefined)
|
|
111
120
|
this.#engine = engine;
|
|
121
|
+
if (brand !== undefined)
|
|
122
|
+
this.#brand = brand;
|
|
112
123
|
}
|
|
113
124
|
/** The shell (web/electron/tauri) and rendering engine the SDK reported. Undefined on an older SDK. */
|
|
114
125
|
get runtime() {
|
|
@@ -117,6 +128,10 @@ export class Session {
|
|
|
117
128
|
get engine() {
|
|
118
129
|
return this.#engine;
|
|
119
130
|
}
|
|
131
|
+
/** The browser brand the SDK reported. Absent (never guessed) when the page did not say. */
|
|
132
|
+
get brand() {
|
|
133
|
+
return this.#brand;
|
|
134
|
+
}
|
|
120
135
|
/** Throttled if the tab is hidden OR we have not heard from it recently. */
|
|
121
136
|
throttled() {
|
|
122
137
|
return this.#hidden || this.lastSeenMs() > SESSION_HEALTH.STALE_THRESHOLD_MS;
|
|
@@ -137,29 +152,19 @@ export class Session {
|
|
|
137
152
|
return recommendation === undefined ? base : { ...base, recommendation };
|
|
138
153
|
}
|
|
139
154
|
info() {
|
|
140
|
-
|
|
141
|
-
|
|
155
|
+
return buildSessionInfo({
|
|
156
|
+
id: this.id,
|
|
142
157
|
url: this.url,
|
|
143
|
-
|
|
158
|
+
projectId: this.projectId,
|
|
144
159
|
title: this.title,
|
|
145
160
|
adapters: this.adapters,
|
|
146
161
|
hasCapabilities: this.hasCapabilities,
|
|
162
|
+
versionSkew: this.versionSkew,
|
|
147
163
|
hidden: this.#hidden,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
base.cleanup_suggestion =
|
|
153
|
-
'Call reticle_session{action:"end"} to free this session before starting new work.';
|
|
154
|
-
}
|
|
155
|
-
// Surface human bug reports in reticle_sessions (only when > 0, so a clean session adds nothing).
|
|
156
|
-
const marks = this.#review.pendingCount();
|
|
157
|
-
if (marks > 0) {
|
|
158
|
-
base.pendingMarks = marks;
|
|
159
|
-
const s = marks === 1 ? '' : 's';
|
|
160
|
-
base.review_suggestion = `The human flagged ${String(marks)} issue${s} on this tab — call reticle_session{action:"review"} to see and fix ${marks === 1 ? 'it' : 'them'}.`;
|
|
161
|
-
}
|
|
162
|
-
return base;
|
|
164
|
+
health: () => this.health(),
|
|
165
|
+
staleMs: () => this.staleMs(),
|
|
166
|
+
pendingMarkCount: () => this.#review.pendingCount(),
|
|
167
|
+
});
|
|
163
168
|
}
|
|
164
169
|
/** Wall-clock age of the session in milliseconds. */
|
|
165
170
|
staleMs() {
|
|
@@ -169,7 +174,7 @@ export class Session {
|
|
|
169
174
|
pushEvent(event, byteSize) {
|
|
170
175
|
if (event.type === EventType.PAGE_HEALTH) {
|
|
171
176
|
const r = readHealthEvent(event.data);
|
|
172
|
-
this.applyHealth(r.hidden ?? this.#hidden, r.focused ?? this.#focused, r.runtime, r.engine);
|
|
177
|
+
this.applyHealth(r.hidden ?? this.#hidden, r.focused ?? this.#focused, r.runtime, r.engine, r.brand);
|
|
173
178
|
}
|
|
174
179
|
// The in-page half of Reticle reporting its own failure — recorded like a tool error
|
|
175
180
|
// (fingerprinted, variables stripped) so a broken observer is as visible as a broken tool.
|
|
@@ -195,7 +200,7 @@ export class Session {
|
|
|
195
200
|
// pushState/replaceState/popstate; without this the URL stays frozen at the hello value, and
|
|
196
201
|
// URL-based CDP correlation (real input) silently breaks after the first client-side nav.
|
|
197
202
|
const to = event.data['to'];
|
|
198
|
-
if (typeof to
|
|
203
|
+
if ('string' === typeof to && to.length > 0)
|
|
199
204
|
this.url = to;
|
|
200
205
|
}
|
|
201
206
|
const t = this.elapsed();
|
|
@@ -218,6 +223,35 @@ export class Session {
|
|
|
218
223
|
recordActedRef(ref) {
|
|
219
224
|
this.#observed.recordActedRef(ref);
|
|
220
225
|
}
|
|
226
|
+
actedLabels() {
|
|
227
|
+
return this.#observed.actedLabels();
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Derive the snapshot-shaped label (`button "Deploy"`) from an act reply and remember it.
|
|
231
|
+
*
|
|
232
|
+
* Same shape the interactive snapshot emits, so coverage can match a control the agent drove
|
|
233
|
+
* against the same control after a re-render gave it a different ref.
|
|
234
|
+
*/
|
|
235
|
+
recordActedLabelFrom(result) {
|
|
236
|
+
const payload = result.result;
|
|
237
|
+
if (typeof payload !== 'object' || null === payload)
|
|
238
|
+
return;
|
|
239
|
+
const record = payload;
|
|
240
|
+
// The TESTID first: it is the strongest identity a control has and it survives any re-render.
|
|
241
|
+
// Coverage previously matched only on `role "name"`, so a control with a testid but no accessible
|
|
242
|
+
// name — or on a stack where the act reply carried neither — was unrecognisable after a
|
|
243
|
+
// re-render, and coverage read `exercised: 0` however much work had been done.
|
|
244
|
+
const testid = record['testid'];
|
|
245
|
+
if ('string' === typeof testid && testid.length > 0)
|
|
246
|
+
this.#observed.recordActedLabel(testid);
|
|
247
|
+
const role = record['role'];
|
|
248
|
+
const name = record['name'];
|
|
249
|
+
if (typeof role !== 'string' || typeof name !== 'string')
|
|
250
|
+
return;
|
|
251
|
+
if (0 === role.length || 0 === name.length)
|
|
252
|
+
return;
|
|
253
|
+
this.#observed.recordActedLabel(`${role} "${name}"`);
|
|
254
|
+
}
|
|
221
255
|
actedRefs() {
|
|
222
256
|
return this.#observed.actedRefs();
|
|
223
257
|
}
|
|
@@ -340,6 +374,10 @@ export class Session {
|
|
|
340
374
|
this.#listeners.add(listener);
|
|
341
375
|
return () => this.#listeners.delete(listener);
|
|
342
376
|
}
|
|
377
|
+
onDisconnect(listener) {
|
|
378
|
+
this.#disconnectListeners.add(listener);
|
|
379
|
+
return () => this.#disconnectListeners.delete(listener);
|
|
380
|
+
}
|
|
343
381
|
/** Send a command to the browser and await its reply (or time out). */
|
|
344
382
|
command(name, args = {}, timeoutMs = DEFAULT_COMMAND_TIMEOUT_MS) {
|
|
345
383
|
// Recorded HERE rather than at the tool call sites: this is the one place every driven action
|
|
@@ -348,7 +386,7 @@ export class Session {
|
|
|
348
386
|
// still wrong, and it made the number depend on which tool the agent happened to use.
|
|
349
387
|
if (name === ReticleCommand.ACT) {
|
|
350
388
|
const ref = args['ref'];
|
|
351
|
-
if (typeof ref
|
|
389
|
+
if ('string' === typeof ref)
|
|
352
390
|
this.recordActedRef(ref);
|
|
353
391
|
}
|
|
354
392
|
const id = this.#pending.nextId(COMMAND_ID_PREFIX);
|
|
@@ -369,7 +407,17 @@ export class Session {
|
|
|
369
407
|
}));
|
|
370
408
|
this.#socket.send(payload);
|
|
371
409
|
const sentAt = Date.now(); // browser-leg timing; see recordBrowserLatency for why it is split out
|
|
372
|
-
|
|
410
|
+
// Traced because this is the boundary that decides whether a slow tool call is OUR overhead or
|
|
411
|
+
// the app taking its time — the split telemetry reports in aggregate, made visible per call.
|
|
412
|
+
return span('browser.command', { command: name, sessionId: this.id }, () => awaited)
|
|
413
|
+
.then((result) => {
|
|
414
|
+
// The label is only knowable from the REPLY — the request carries a ref, and a ref dies with
|
|
415
|
+
// the next re-render. Recorded here so coverage survives frameworks that replace nodes.
|
|
416
|
+
if (name === ReticleCommand.ACT)
|
|
417
|
+
this.recordActedLabelFrom(result);
|
|
418
|
+
return result;
|
|
419
|
+
})
|
|
420
|
+
.finally(() => recordBrowserLatency(Date.now() - sentAt));
|
|
373
421
|
}
|
|
374
422
|
handleResult(result) {
|
|
375
423
|
this.#pending.settle(result);
|
|
@@ -377,6 +425,9 @@ export class Session {
|
|
|
377
425
|
/** Reject everything still in flight — used on disconnect. */
|
|
378
426
|
rejectAll(reason) {
|
|
379
427
|
this.#pending.rejectAll(reason);
|
|
428
|
+
for (const listener of this.#disconnectListeners)
|
|
429
|
+
listener();
|
|
430
|
+
this.#disconnectListeners.clear();
|
|
380
431
|
}
|
|
381
432
|
/** End this transport without letting a stale socket remove its replacement session. */
|
|
382
433
|
disconnect(reason) {
|
|
@@ -490,7 +541,7 @@ export class Session {
|
|
|
490
541
|
// still wrong, and it made the number depend on which tool the agent happened to use.
|
|
491
542
|
if (name === ReticleCommand.ACT) {
|
|
492
543
|
const ref = args['ref'];
|
|
493
|
-
if (typeof ref
|
|
544
|
+
if ('string' === typeof ref)
|
|
494
545
|
this.recordActedRef(ref);
|
|
495
546
|
}
|
|
496
547
|
const id = this.#pending.nextId(COMMAND_ID_PREFIX);
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* without paying the truncation tax); the server re-exports it for its back-compat fallback path
|
|
5
5
|
* (an older browser that returns the whole store) and for the predicate engine's `state` assertion.
|
|
6
6
|
*/
|
|
7
|
-
export { selectPath, capDepth, type PathSelection } from '@reticlehq/core';
|
|
7
|
+
export { selectPath, capDepth, projectComponentState, type PathSelection } from '@reticlehq/core';
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* without paying the truncation tax); the server re-exports it for its back-compat fallback path
|
|
5
5
|
* (an older browser that returns the whole store) and for the predicate engine's `state` assertion.
|
|
6
6
|
*/
|
|
7
|
-
export { selectPath, capDepth } from '@reticlehq/core';
|
|
7
|
+
export { selectPath, capDepth, projectComponentState } from '@reticlehq/core';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `GET /status` answers — the daemon describing itself.
|
|
3
|
+
*
|
|
4
|
+
* Carries the daemon's OWN version, because a daemon outlives every agent attached to it: after an
|
|
5
|
+
* upgrade the new CLI attaches to the old daemon and serves its code, and until this field existed
|
|
6
|
+
* there was no surface anywhere — not /status, not `reticle status` — naming the version actually
|
|
7
|
+
* answering requests. See describeDaemonSkew.
|
|
8
|
+
*/
|
|
9
|
+
import type { SessionInfo } from './session/session-info.js';
|
|
10
|
+
interface StatusPayload {
|
|
11
|
+
running: true;
|
|
12
|
+
version: string;
|
|
13
|
+
/** The wire contract this daemon speaks — what another process compares against, not the version. */
|
|
14
|
+
contract: string;
|
|
15
|
+
sessionCount: number;
|
|
16
|
+
sessions: SessionInfo[];
|
|
17
|
+
}
|
|
18
|
+
export declare function statusPayload(sessionCount: number, sessions: SessionInfo[]): StatusPayload;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CONTRACT_FINGERPRINT } from '@reticlehq/core';
|
|
2
|
+
import { SERVER_VERSION } from './version/server-version.js';
|
|
3
|
+
export function statusPayload(sessionCount, sessions) {
|
|
4
|
+
return {
|
|
5
|
+
running: true,
|
|
6
|
+
version: SERVER_VERSION,
|
|
7
|
+
contract: CONTRACT_FINGERPRINT,
|
|
8
|
+
sessionCount,
|
|
9
|
+
sessions,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -57,10 +57,19 @@ export function describeParam(name, value) {
|
|
|
57
57
|
return name;
|
|
58
58
|
return `${name}:${allowed.has(value) ? value : OTHER_VALUE}`;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Parameters that carry no information about how the tool is USED, and so are not counted.
|
|
62
|
+
*
|
|
63
|
+
* `sessionId` is accepted by nearly every tool and passed on nearly every call, so counting it
|
|
64
|
+
* produced a row per tool saying only "the agent addressed a session" — in one real day's export it
|
|
65
|
+
* was a third of the toolParams block. This histogram exists to answer "is anyone using the argument
|
|
66
|
+
* we shipped last release", and a parameter every call carries can never answer it.
|
|
67
|
+
*/
|
|
68
|
+
const UNINFORMATIVE_PARAMS = new Set(['sessionId']);
|
|
60
69
|
/** Every parameter an agent actually passed to one tool call, reduced to names (+ safe enums). */
|
|
61
70
|
export function describeToolParams(args) {
|
|
62
71
|
return Object.keys(args)
|
|
63
|
-
.filter((key) => args[key] !== undefined)
|
|
72
|
+
.filter((key) => args[key] !== undefined && !UNINFORMATIVE_PARAMS.has(key))
|
|
64
73
|
.map((key) => describeParam(key, args[key]))
|
|
65
74
|
.sort();
|
|
66
75
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Was this verification watched by a person, or run by a machine?
|
|
3
|
+
*
|
|
4
|
+
* A verification's cost and its meaning both depend on it. A headless run is CI or an unattended
|
|
5
|
+
* agent loop — it should be fast, and there is nobody to notice a wrong answer. A headed run has a
|
|
6
|
+
* human in front of it. And the most common case of all is neither: the SDK in somebody's own dev
|
|
7
|
+
* server, in their own browser, which Reticle never launched at all.
|
|
8
|
+
*
|
|
9
|
+
* Recorded once at daemon start, because that is when the decision is made and it does not change
|
|
10
|
+
* for the daemon's life. Defaults to ATTACHED, which is the honest answer when Reticle launched
|
|
11
|
+
* nothing: claiming "headless" for a browser we did not open would be a guess about somebody's
|
|
12
|
+
* machine.
|
|
13
|
+
*/
|
|
14
|
+
export declare const BrowserMode: {
|
|
15
|
+
/** Reticle launched the browser with no display — CI, or an unattended agent loop. */
|
|
16
|
+
readonly HEADLESS: "headless";
|
|
17
|
+
/** Reticle launched a visible browser — a human is watching. */
|
|
18
|
+
readonly HEADED: "headed";
|
|
19
|
+
/** Reticle launched nothing: the SDK connected from a browser somebody else opened. */
|
|
20
|
+
readonly ATTACHED: "attached";
|
|
21
|
+
};
|
|
22
|
+
export type BrowserMode = (typeof BrowserMode)[keyof typeof BrowserMode];
|
|
23
|
+
/** Record how this daemon drives a browser. Called once, at start. */
|
|
24
|
+
export declare function setBrowserMode(mode: BrowserMode): void;
|
|
25
|
+
export declare function getBrowserMode(): BrowserMode;
|
|
26
|
+
/** Tests only — restore the default so cases do not leak into each other. */
|
|
27
|
+
export declare function resetBrowserMode(): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Was this verification watched by a person, or run by a machine?
|
|
3
|
+
*
|
|
4
|
+
* A verification's cost and its meaning both depend on it. A headless run is CI or an unattended
|
|
5
|
+
* agent loop — it should be fast, and there is nobody to notice a wrong answer. A headed run has a
|
|
6
|
+
* human in front of it. And the most common case of all is neither: the SDK in somebody's own dev
|
|
7
|
+
* server, in their own browser, which Reticle never launched at all.
|
|
8
|
+
*
|
|
9
|
+
* Recorded once at daemon start, because that is when the decision is made and it does not change
|
|
10
|
+
* for the daemon's life. Defaults to ATTACHED, which is the honest answer when Reticle launched
|
|
11
|
+
* nothing: claiming "headless" for a browser we did not open would be a guess about somebody's
|
|
12
|
+
* machine.
|
|
13
|
+
*/
|
|
14
|
+
export const BrowserMode = {
|
|
15
|
+
/** Reticle launched the browser with no display — CI, or an unattended agent loop. */
|
|
16
|
+
HEADLESS: 'headless',
|
|
17
|
+
/** Reticle launched a visible browser — a human is watching. */
|
|
18
|
+
HEADED: 'headed',
|
|
19
|
+
/** Reticle launched nothing: the SDK connected from a browser somebody else opened. */
|
|
20
|
+
ATTACHED: 'attached',
|
|
21
|
+
};
|
|
22
|
+
let current = BrowserMode.ATTACHED;
|
|
23
|
+
/** Record how this daemon drives a browser. Called once, at start. */
|
|
24
|
+
export function setBrowserMode(mode) {
|
|
25
|
+
current = mode;
|
|
26
|
+
}
|
|
27
|
+
export function getBrowserMode() {
|
|
28
|
+
return current;
|
|
29
|
+
}
|
|
30
|
+
/** Tests only — restore the default so cases do not leak into each other. */
|
|
31
|
+
export function resetBrowserMode() {
|
|
32
|
+
current = BrowserMode.ATTACHED;
|
|
33
|
+
}
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Counting the defects Reticle actually finds.
|
|
3
|
-
*
|
|
4
|
-
* Every other metric in this product measures whether Reticle is USED. This one measures whether it
|
|
5
|
-
* WORKS — and it is the only number that can honestly be put in front of an investor or published,
|
|
6
|
-
* because it counts outcomes for users rather than activity by them.
|
|
7
|
-
*
|
|
8
|
-
* It is read off tool RESULTS at the single chokepoint every tool already crosses, rather than
|
|
9
|
-
* emitted from the four places that produce findings. That is deliberate: the detectors are pure
|
|
10
|
-
* functions and should stay that way, and reading results means a fifth source of findings is covered
|
|
11
|
-
* the day it starts returning one of these shapes, instead of the day someone remembers to instrument
|
|
12
|
-
* it.
|
|
13
|
-
*
|
|
14
|
-
* What is sent is the KIND from Reticle's own findings vocabulary — `signal-contradicted`,
|
|
15
|
-
* `console-error`, `duplicate-request`. Never a selector, a URL, an element, or a description of
|
|
16
|
-
* anyone's app: that a class of defect was found, never what it was found in.
|
|
17
|
-
*/
|
|
18
1
|
import { type BugFound } from '@reticlehq/core';
|
|
19
2
|
/**
|
|
20
3
|
* A defect as the CLASSIFIER sees it — everything except `repeat`.
|
|
@@ -24,10 +7,23 @@ import { type BugFound } from '@reticlehq/core';
|
|
|
24
7
|
*/
|
|
25
8
|
export type BugCandidate = Omit<BugFound, 'repeat'>;
|
|
26
9
|
/**
|
|
27
|
-
*
|
|
10
|
+
* `bug.attribution` was REMOVED, deliberately, and this note is here so it is not re-added casually.
|
|
11
|
+
*
|
|
12
|
+
* It shipped twice and was wrong both times. First it stamped `app` on every contradiction, including
|
|
13
|
+
* `request-never-settled` — which Reticle raises from the ABSENCE of a settle event, so a dev-overlay
|
|
14
|
+
* poll manufactured it with the app doing nothing wrong. That was fixed, and a real drive then found
|
|
15
|
+
* it still inverted: across two full runs, EVERY `attribution: 'app'` was a misattribution, while the
|
|
16
|
+
* one defect that genuinely was a bad agent predicate carried none. One session would have published
|
|
17
|
+
* "2 defects in the app" against a true count of 0.
|
|
18
|
+
*
|
|
19
|
+
* A metric that is confidently wrong about WHOSE fault a defect is, is worse than no metric: it is
|
|
20
|
+
* the number a founder steers on, and it points at the customer. Counting nothing is recoverable;
|
|
21
|
+
* publishing a false accusation about somebody's product is not.
|
|
28
22
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
23
|
+
* Bringing it back needs evidence that actually separates the cases. It does not exist in the payload
|
|
24
|
+
* today: `element.present` covers "the button is missing", "the API is down" and "the agent mistyped
|
|
25
|
+
* a testid" identically, and `signal.absent` covers both a signal the app never fired and a ref
|
|
26
|
+
* Reticle drove into nothing. Until a verdict carries the reason it came out that way, the honest
|
|
27
|
+
* output is no owner at all.
|
|
32
28
|
*/
|
|
33
29
|
export declare function bugsInResult(toolName: string, result: Record<string, unknown>): BugCandidate[];
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* `console-error`, `duplicate-request`. Never a selector, a URL, an element, or a description of
|
|
16
16
|
* anyone's app: that a class of defect was found, never what it was found in.
|
|
17
17
|
*/
|
|
18
|
+
import { ReticleTool } from '../tools/tool-names.js';
|
|
18
19
|
import { BugSource, ContradictionKind } from '@reticlehq/core';
|
|
19
20
|
/** Cap per tool call — a pathological crawl must not turn one call into a thousand events. */
|
|
20
21
|
const MAX_BUGS_PER_CALL = 25;
|
|
@@ -24,7 +25,7 @@ function kindsOf(value) {
|
|
|
24
25
|
return value
|
|
25
26
|
.map((entry) => {
|
|
26
27
|
const kind = entry?.kind;
|
|
27
|
-
return typeof kind
|
|
28
|
+
return 'string' === typeof kind ? kind : undefined;
|
|
28
29
|
})
|
|
29
30
|
.filter((kind) => kind !== undefined);
|
|
30
31
|
}
|
|
@@ -35,16 +36,111 @@ function kindsOf(value) {
|
|
|
35
36
|
* the case the product exists for — the screen looked right, the write failed, and every other tool
|
|
36
37
|
* on the market would have called it green. That pairing is what `falseGreen` marks.
|
|
37
38
|
*/
|
|
39
|
+
/**
|
|
40
|
+
* The verdict this result carries, whatever envelope it arrived in.
|
|
41
|
+
*
|
|
42
|
+
* `reticle_assert` reports a top-level boolean `pass`. `reticle_act_and_wait` — the tool agents
|
|
43
|
+
* actually reach for, and the one whose description calls it the act->observe->assert loop — reports
|
|
44
|
+
* `verdict.pass` with the summary at `verified`. Reading only `result.pass` meant a FAILED
|
|
45
|
+
* act_and_wait produced no bug at all, and a contradiction found next to a PASSING one was recorded
|
|
46
|
+
* with `falseGreen: false` — deflating the single number this product exists to publish, by the
|
|
47
|
+
* shape of its own envelope.
|
|
48
|
+
*/
|
|
49
|
+
function verdictOf(result) {
|
|
50
|
+
if ('boolean' === typeof result['pass'])
|
|
51
|
+
return result['pass'];
|
|
52
|
+
const verdict = result['verdict'];
|
|
53
|
+
if ('object' === typeof verdict && verdict !== null) {
|
|
54
|
+
const nested = verdict['pass'];
|
|
55
|
+
if ('boolean' === typeof nested)
|
|
56
|
+
return nested;
|
|
57
|
+
}
|
|
58
|
+
// `verified` is the field the agent is told to gate on; "unknown" is deliberately not a verdict.
|
|
59
|
+
const verified = result['verified'];
|
|
60
|
+
if ('yes' === verified)
|
|
61
|
+
return true;
|
|
62
|
+
if ('no' === verified)
|
|
63
|
+
return false;
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
/** The assertion label, from either envelope — act_and_wait nests it under `verdict`. */
|
|
67
|
+
function assertionOf(result) {
|
|
68
|
+
if (result['assertion'] !== undefined)
|
|
69
|
+
return result['assertion'];
|
|
70
|
+
const verdict = result['verdict'];
|
|
71
|
+
return 'object' === typeof verdict && verdict !== null
|
|
72
|
+
? verdict['assertion']
|
|
73
|
+
: undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A verdict that could not be reached is not a defect found. `inconclusive` is set when the
|
|
77
|
+
* assertion was never evaluated — an under-specified call, or nothing instrumented to read — so
|
|
78
|
+
* counting it would put the agent's own mistakes into the number we publish.
|
|
79
|
+
*/
|
|
80
|
+
function isInconclusive(result) {
|
|
81
|
+
const verdict = result['verdict'];
|
|
82
|
+
return ('object' === typeof verdict &&
|
|
83
|
+
verdict !== null &&
|
|
84
|
+
'string' === typeof verdict['inconclusive']);
|
|
85
|
+
}
|
|
86
|
+
/** A suite row whose replay never STARTED — see SuiteFlowResult.couldNotRun. */
|
|
87
|
+
function couldNotRun(failure) {
|
|
88
|
+
return ('object' === typeof failure &&
|
|
89
|
+
failure !== null &&
|
|
90
|
+
true === failure.couldNotRun);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `bug.attribution` was REMOVED, deliberately, and this note is here so it is not re-added casually.
|
|
94
|
+
*
|
|
95
|
+
* It shipped twice and was wrong both times. First it stamped `app` on every contradiction, including
|
|
96
|
+
* `request-never-settled` — which Reticle raises from the ABSENCE of a settle event, so a dev-overlay
|
|
97
|
+
* poll manufactured it with the app doing nothing wrong. That was fixed, and a real drive then found
|
|
98
|
+
* it still inverted: across two full runs, EVERY `attribution: 'app'` was a misattribution, while the
|
|
99
|
+
* one defect that genuinely was a bad agent predicate carried none. One session would have published
|
|
100
|
+
* "2 defects in the app" against a true count of 0.
|
|
101
|
+
*
|
|
102
|
+
* A metric that is confidently wrong about WHOSE fault a defect is, is worse than no metric: it is
|
|
103
|
+
* the number a founder steers on, and it points at the customer. Counting nothing is recoverable;
|
|
104
|
+
* publishing a false accusation about somebody's product is not.
|
|
105
|
+
*
|
|
106
|
+
* Bringing it back needs evidence that actually separates the cases. It does not exist in the payload
|
|
107
|
+
* today: `element.present` covers "the button is missing", "the API is down" and "the agent mistyped
|
|
108
|
+
* a testid" identically, and `signal.absent` covers both a signal the app never fired and a ref
|
|
109
|
+
* Reticle drove into nothing. Until a verdict carries the reason it came out that way, the honest
|
|
110
|
+
* output is no owner at all.
|
|
111
|
+
*/
|
|
38
112
|
export function bugsInResult(toolName, result) {
|
|
113
|
+
// `reticle_run` is a WRAPPER: its handler calls runTool on the real tool, which already reported
|
|
114
|
+
// that result's defects under the real tool's name — then the outer chokepoint reports the very
|
|
115
|
+
// same object again as `reticle_run`. One sweep of 34 events was a perfect mirror image:
|
|
116
|
+
// flow-regression x8/reticle_flow_verify AND x8/reticle_run; assertion-failed x8/reticle_verify_change
|
|
117
|
+
// AND x8/reticle_run. Sixteen of the thirty-four were echoes, and a headline number that doubles
|
|
118
|
+
// because of HOW a tool was reached is not a measurement.
|
|
119
|
+
//
|
|
120
|
+
// Nothing is lost by staying silent here: when the inner tool refuses or throws, the wrapper
|
|
121
|
+
// returns { error, tool, params, hint }, which carries no verdict and no failures to classify.
|
|
122
|
+
if (toolName === ReticleTool.RUN)
|
|
123
|
+
return [];
|
|
39
124
|
const bugs = [];
|
|
40
|
-
const
|
|
125
|
+
const verdict = verdictOf(result);
|
|
126
|
+
const passed = true === verdict;
|
|
41
127
|
// 1. Contradictions — channels disagreeing. Invisible to a human watching the screen.
|
|
42
128
|
for (const kind of kindsOf(result['contradictions'])) {
|
|
43
|
-
|
|
129
|
+
// A contradiction is two of the APP's own channels disagreeing — never the agent's phrasing.
|
|
130
|
+
// Omitted for the one kind Reticle can produce on its own: see contradictionAttribution.
|
|
131
|
+
bugs.push({
|
|
132
|
+
source: BugSource.CONTRADICTION,
|
|
133
|
+
kind,
|
|
134
|
+
falseGreen: passed,
|
|
135
|
+
tool: toolName,
|
|
136
|
+
});
|
|
44
137
|
}
|
|
45
138
|
// 2. Crawl anomalies — found autonomously, with nobody writing a test. Crawl reports its own
|
|
46
139
|
// contradictions inside `anomalies` too, so classify by kind rather than by which array it sat in.
|
|
47
140
|
for (const kind of kindsOf(result['anomalies'])) {
|
|
141
|
+
// Crawl reports contradictions in this array too, so the same kind must get the same owner
|
|
142
|
+
// wherever it arrives — a `request-never-settled` found by the crawler is no better evidence
|
|
143
|
+
// about whose fault it was than one found by an assertion.
|
|
48
144
|
bugs.push({
|
|
49
145
|
source: BugSource.CRAWL,
|
|
50
146
|
kind,
|
|
@@ -52,28 +148,44 @@ export function bugsInResult(toolName, result) {
|
|
|
52
148
|
// — even though no assertion was made. See the definition on `falseGreen`.
|
|
53
149
|
falseGreen: CONTRADICTION_KINDS.has(kind),
|
|
54
150
|
tool: toolName,
|
|
151
|
+
// Nobody wrote a predicate here, so there is no bad request to blame: a crawl anomaly is a
|
|
152
|
+
// fault the app produced on its own.
|
|
55
153
|
});
|
|
56
154
|
}
|
|
57
155
|
// 3. A failed assertion — the agent declared a consequence and it did not hold.
|
|
58
156
|
// Only when nothing above already explained it, or one defect would be counted twice.
|
|
59
|
-
|
|
60
|
-
|
|
157
|
+
// A red the APP earned, not one Reticle produced. `verified: "unknown"` is the honesty layer
|
|
158
|
+
// saying nothing was proved either way, and a top-level `error` is a refusal — measured over one
|
|
159
|
+
// sweep, every bug_found emitted was one of these two: "no session connected" from reticle_run and
|
|
160
|
+
// an unverifiable suite from reticle_verify_change, counted as defects in the user's app.
|
|
161
|
+
const reticleFailure = 'unknown' === result['verified'] || 'string' === typeof result['error'];
|
|
162
|
+
if (false === verdict && 0 === bugs.length && !isInconclusive(result) && !reticleFailure) {
|
|
163
|
+
const reason = assertionOf(result);
|
|
164
|
+
const kind = 'string' === typeof reason && reason.length > 0 ? reason.slice(0, 64) : 'assertion-failed';
|
|
61
165
|
bugs.push({
|
|
62
166
|
source: BugSource.ASSERTION,
|
|
63
|
-
kind
|
|
167
|
+
kind,
|
|
64
168
|
falseGreen: false,
|
|
65
169
|
tool: toolName,
|
|
170
|
+
// Omitted when the label cannot say whose fault it was — see ASSERTION_ATTRIBUTION.
|
|
66
171
|
});
|
|
67
172
|
}
|
|
68
173
|
// 4. Replay failures — a saved flow that used to pass no longer does. That is a regression caught,
|
|
69
174
|
// which is a different and stronger claim than "a check failed".
|
|
70
|
-
if (
|
|
71
|
-
for (const
|
|
175
|
+
if ('fail' === result['status'] && Array.isArray(result['failures'])) {
|
|
176
|
+
for (const failure of result['failures'].slice(0, MAX_BUGS_PER_CALL)) {
|
|
177
|
+
// A suite that failed because nothing could RUN is not a regression in the user's app. This
|
|
178
|
+
// rule never looked past `status`, so a project whose flow files did not resolve reported one
|
|
179
|
+
// "defect found" per flow — Reticle's own failure, published as the customer's.
|
|
180
|
+
if (couldNotRun(failure))
|
|
181
|
+
continue;
|
|
72
182
|
bugs.push({
|
|
73
183
|
source: BugSource.REPLAY,
|
|
74
184
|
kind: 'flow-regression',
|
|
75
185
|
falseGreen: false,
|
|
76
186
|
tool: toolName,
|
|
187
|
+
// A saved flow that used to pass and no longer does is a regression in the app. Rows that
|
|
188
|
+
// never RAN are skipped above precisely so Reticle's own failures do not land here.
|
|
77
189
|
});
|
|
78
190
|
}
|
|
79
191
|
}
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* `_daemon` is EXCLUDED, and that exclusion is the whole point of this function. `reticle mcp` and
|
|
5
|
-
* `reticle serve` start the daemon by re-running this very binary, so the child re-entered the CLI
|
|
6
|
-
* entry point and emitted a second event for what a person experienced as ONE action. The old
|
|
7
|
-
* `invoke` metric was therefore inflated ~2x — and inflated worst on the agent-driven sessions that
|
|
8
|
-
* matter most, while one-shot commands like `version` counted once. That skewed the RATIO between
|
|
9
|
-
* commands, not merely the scale, which is the kind of error you cannot correct for after the fact.
|
|
10
|
-
* The daemon\'s own lifecycle is already reported by `daemon_started` / `daemon_stopped`; a spawned
|
|
11
|
-
* daemon is not a CLI run.
|
|
12
|
-
*/
|
|
1
|
+
/** True when this command represents a person typing something. Exported for the gate. */
|
|
2
|
+
export declare function isHumanCliCommand(command: string): boolean;
|
|
13
3
|
export declare function reportCliRun(argv: readonly string[]): void;
|