@reticlehq/server 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +105 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse.d.ts +143 -0
- package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
- package/dist/{cli-port.js → cli/cli-port.js} +29 -8
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +182 -93
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.d.ts +0 -6
- package/dist/flows/decision.js +64 -8
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +67 -13
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.d.ts +17 -0
- package/dist/init/capabilities.js +54 -0
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +26 -0
- package/dist/init/detect.js +78 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +39 -0
- package/dist/init/next-patch.js +196 -0
- package/dist/init/node-io.js +48 -6
- package/dist/init/patch-kind.d.ts +20 -0
- package/dist/init/patch-kind.js +10 -0
- package/dist/init/plan-framework.d.ts +50 -0
- package/dist/init/plan-framework.js +341 -0
- package/dist/init/plan.d.ts +119 -6
- package/dist/init/plan.js +230 -131
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +22 -0
- package/dist/init/run.js +393 -28
- package/dist/init/slash-command.d.ts +27 -0
- package/dist/init/slash-command.js +61 -0
- package/dist/init/snippets.d.ts +41 -3
- package/dist/init/snippets.js +185 -10
- package/dist/init/vite-config.d.ts +4 -10
- package/dist/init/vite-config.js +15 -7
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.d.ts +15 -0
- package/dist/input/driven-redaction.js +76 -0
- package/dist/input/network-detail.d.ts +9 -3
- package/dist/input/network-detail.js +43 -23
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +58 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +20 -2
- package/dist/session/session.js +76 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +85 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/cli-parse.d.ts +0 -140
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -35
- package/dist/mcp-proxy.js +0 -190
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A durable outbox for feedback, written BEFORE the network is touched.
|
|
3
|
+
*
|
|
4
|
+
* Feedback is the only qualitative channel this product has, and it carries an agent's whole
|
|
5
|
+
* root-cause analysis — often an hour of driving distilled into one payload. It was sent with the
|
|
6
|
+
* same 2-second fire-and-forget budget as a usage counter, with no retry and no persistence, so a
|
|
7
|
+
* 1.3-second network hiccup destroyed the report outright. Measured to the collector from one
|
|
8
|
+
* machine with a WARM DNS cache: dns 0.003s, connect 0.233s, tls 0.467s, total 0.694s — roughly a
|
|
9
|
+
* third of the entire budget spent before a byte of payload moves, and that is the good case. A
|
|
10
|
+
* fresh short-lived CLI process pays cold DNS and a cold route on top.
|
|
11
|
+
*
|
|
12
|
+
* So the order is inverted: write it down first, then try to send. A failed send is then a queued
|
|
13
|
+
* report rather than a lost one, and `sent: false` becomes a statement about the network instead of
|
|
14
|
+
* an epitaph.
|
|
15
|
+
*
|
|
16
|
+
* JSONL, appended, one payload per line. Append-only is the point — a crash mid-write costs at most
|
|
17
|
+
* the line being written, never the ones already there.
|
|
18
|
+
*/
|
|
19
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import { homedir } from 'node:os';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { randomUUID } from 'node:crypto';
|
|
23
|
+
import { ReticleDir } from '@reticlehq/core';
|
|
24
|
+
export function outboxPath(home = homedir()) {
|
|
25
|
+
return join(home, ReticleDir.ROOT, 'feedback-outbox.jsonl');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Persist a report and return its id. Never throws: failing to queue must not also fail the send
|
|
29
|
+
* that was about to happen anyway.
|
|
30
|
+
*/
|
|
31
|
+
export function queueFeedback(payload, now = () => new Date(), home = homedir()) {
|
|
32
|
+
const id = randomUUID();
|
|
33
|
+
try {
|
|
34
|
+
mkdirSync(join(home, ReticleDir.ROOT), { recursive: true });
|
|
35
|
+
const entry = { id, t: now().toISOString(), payload };
|
|
36
|
+
appendFileSync(outboxPath(home), `${JSON.stringify(entry)}\n`, 'utf8');
|
|
37
|
+
return id;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Read every queued report. A malformed line is skipped, never fatal — the rest still matter. */
|
|
44
|
+
export function readOutbox(home = homedir()) {
|
|
45
|
+
try {
|
|
46
|
+
const path = outboxPath(home);
|
|
47
|
+
if (!existsSync(path))
|
|
48
|
+
return [];
|
|
49
|
+
return readFileSync(path, 'utf8')
|
|
50
|
+
.split('\n')
|
|
51
|
+
.filter((line) => 0 < line.trim().length)
|
|
52
|
+
.flatMap((line) => {
|
|
53
|
+
try {
|
|
54
|
+
return [JSON.parse(line)];
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Drop a report that was delivered. Rewrites the file without it.
|
|
67
|
+
*
|
|
68
|
+
* A rewrite rather than a tombstone because the file's whole purpose is "what still needs sending" —
|
|
69
|
+
* a reader should be able to `cat` it and see exactly the backlog, with no filtering rule to learn.
|
|
70
|
+
* Volume is a handful of lines a week, so the cost is irrelevant.
|
|
71
|
+
*/
|
|
72
|
+
export function markDelivered(id, home = homedir()) {
|
|
73
|
+
if (null === id)
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
const remaining = readOutbox(home).filter((entry) => entry.id !== id);
|
|
77
|
+
const path = outboxPath(home);
|
|
78
|
+
if (0 === remaining.length) {
|
|
79
|
+
writeFileSync(path, '', 'utf8');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
writeFileSync(path, `${remaining.map((entry) => JSON.stringify(entry)).join('\n')}\n`, 'utf8');
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// A report that stays queued after a successful send is a duplicate at worst. Losing one is not.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -39,8 +39,21 @@ export declare function feedbackDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
|
39
39
|
/** What the author supplies; the environment context is detected, never asked for. */
|
|
40
40
|
export type FeedbackInput = Pick<Feedback, 'source' | 'kind' | 'text' | 'trace' | 'rating' | 'need' | 'impact' | 'currentApproach' | 'model'>;
|
|
41
41
|
export interface FeedbackReceipt {
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* DELIVERY confirmed. Only ever true when the send was awaited and the endpoint accepted it —
|
|
44
|
+
* never as an optimistic stand-in for "we handed it to the emitter". That distinction is the whole
|
|
45
|
+
* value of this field: it was once unconditional, so a DNS miss and a 4xx both reported "filed".
|
|
46
|
+
*
|
|
47
|
+
* A backgrounded send (the agent tool) therefore returns `sent: false` with `accepted: true` —
|
|
48
|
+
* see below. It is not a failure, and the note says so.
|
|
49
|
+
*/
|
|
43
50
|
sent: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Validated, redacted and handed to the emitter. TRUE says the report is well-formed and on its
|
|
53
|
+
* way; it does NOT say it arrived. If a backgrounded send then fails, the reporter is told on its
|
|
54
|
+
* next tool result — see feedback-delivery.ts.
|
|
55
|
+
*/
|
|
56
|
+
accepted: boolean;
|
|
44
57
|
reason?: string;
|
|
45
58
|
/** Which redaction rules fired. Empty when nothing needed removing. */
|
|
46
59
|
redacted: string[];
|
|
@@ -56,6 +69,14 @@ export declare function submitFeedback(input: FeedbackInput, opts?: {
|
|
|
56
69
|
cwd?: string;
|
|
57
70
|
session?: SessionFacts;
|
|
58
71
|
env?: NodeJS.ProcessEnv;
|
|
72
|
+
/**
|
|
73
|
+
* Return as soon as the report is validated and queued, instead of waiting out the POST.
|
|
74
|
+
*
|
|
75
|
+
* Used by the AGENT tool: ~340ms of network on a call made mid-task is the product blocking the
|
|
76
|
+
* user's work to talk about itself. NOT used by `reticle feedback`, where a human typed the
|
|
77
|
+
* command and is waiting for an answer — there, confirmed delivery is the answer.
|
|
78
|
+
*/
|
|
79
|
+
background?: boolean;
|
|
59
80
|
}): Promise<FeedbackReceipt>;
|
|
60
81
|
/**
|
|
61
82
|
* The receipt's context PLUS the fields the emitter stamps on every event regardless of kind — the
|
|
@@ -24,8 +24,11 @@
|
|
|
24
24
|
import { FeedbackSchema, FEEDBACK_TEXT_MAX, FEEDBACK_TRACE_MAX, FEEDBACK_FIELD_MAX, TelemetryEventKind, } from '@reticlehq/core';
|
|
25
25
|
import { platform } from 'node:os';
|
|
26
26
|
import { getTelemetry } from './telemetry.js';
|
|
27
|
-
import {
|
|
27
|
+
import { noteFeedbackUndelivered } from './feedback-delivery.js';
|
|
28
|
+
import { isReticleSourceCheckout } from './dev-repo.js';
|
|
29
|
+
import { SERVER_VERSION } from '../version/server-version.js';
|
|
28
30
|
import { feedbackContext } from './feedback-context.js';
|
|
31
|
+
import { markDelivered, outboxPath, queueFeedback } from './feedback-outbox.js';
|
|
29
32
|
/** Kills the feedback channel ONLY — adoption counters keep flowing. */
|
|
30
33
|
export const FEEDBACK_ENV = 'RETICLE_FEEDBACK';
|
|
31
34
|
/** What replaces a redacted match. Visible on purpose: a reader must see that something was removed. */
|
|
@@ -67,7 +70,7 @@ export function redactFeedbackText(input, max) {
|
|
|
67
70
|
removed.add(label);
|
|
68
71
|
// A rule with a leading capture keeps it (the scheme, the `api_key=` key) so the reader still
|
|
69
72
|
// sees WHAT was removed, not just that something was.
|
|
70
|
-
const prefix = typeof groups[0]
|
|
73
|
+
const prefix = 'string' === typeof groups[0] ? groups[0] : '';
|
|
71
74
|
return `${prefix}${REDACTED}`;
|
|
72
75
|
});
|
|
73
76
|
}
|
|
@@ -76,7 +79,7 @@ export function redactFeedbackText(input, max) {
|
|
|
76
79
|
/** True when the feedback channel is switched off on its own (the emitter's switches apply as well). */
|
|
77
80
|
export function feedbackDisabled(env = process.env) {
|
|
78
81
|
const value = (env[FEEDBACK_ENV] ?? '').toLowerCase();
|
|
79
|
-
return
|
|
82
|
+
return '0' === value || 'false' === value || 'off' === value || 'no' === value;
|
|
80
83
|
}
|
|
81
84
|
/**
|
|
82
85
|
* The fields an author writes, and therefore the fields that must be redacted. ONE list, used both to
|
|
@@ -118,7 +121,7 @@ export async function submitFeedback(input, opts = {}) {
|
|
|
118
121
|
// addition. Deriving the set means a sixth field is covered the moment it exists.
|
|
119
122
|
const redactions = new Map();
|
|
120
123
|
for (const [key, value] of Object.entries(input)) {
|
|
121
|
-
if (typeof value !== 'string' ||
|
|
124
|
+
if (typeof value !== 'string' || '' === value)
|
|
122
125
|
continue;
|
|
123
126
|
if (!AUTHOR_WRITTEN_FIELDS.has(key))
|
|
124
127
|
continue;
|
|
@@ -127,13 +130,25 @@ export async function submitFeedback(input, opts = {}) {
|
|
|
127
130
|
const body = redactions.get('text') ?? { text: '', removed: [] };
|
|
128
131
|
const redacted = [...new Set([...redactions.values()].flatMap((r) => r.removed))];
|
|
129
132
|
if (feedbackDisabled(env)) {
|
|
130
|
-
return {
|
|
133
|
+
return {
|
|
134
|
+
sent: false,
|
|
135
|
+
accepted: false,
|
|
136
|
+
reason: `feedback is disabled by ${FEEDBACK_ENV}`,
|
|
137
|
+
redacted,
|
|
138
|
+
context,
|
|
139
|
+
};
|
|
131
140
|
}
|
|
132
141
|
const telemetry = getTelemetry();
|
|
133
142
|
if (!telemetry.enabled) {
|
|
134
143
|
return {
|
|
135
144
|
sent: false,
|
|
136
|
-
|
|
145
|
+
accepted: false,
|
|
146
|
+
// Named precisely, because the commonest cause is not what the old wording suggested: a
|
|
147
|
+
// Reticle SOURCE CHECKOUT disables telemetry by cwd, and that is exactly where release runs and
|
|
148
|
+
// contributor sessions happen — so the reports most worth having were the ones silently lost.
|
|
149
|
+
reason: isReticleSourceCheckout(opts.cwd ?? process.cwd())
|
|
150
|
+
? 'this is a Reticle source checkout, where telemetry is disabled by design — so feedback has nowhere to go. Open an issue at https://github.com/reticlehq/reticle/issues instead, or run from the app you are verifying.'
|
|
151
|
+
: `telemetry is disabled on this machine, so feedback has nowhere to go. Re-enable with \`reticle telemetry enable\`, or open an issue at https://github.com/reticlehq/reticle/issues.`,
|
|
137
152
|
redacted,
|
|
138
153
|
context,
|
|
139
154
|
};
|
|
@@ -151,13 +166,57 @@ export async function submitFeedback(input, opts = {}) {
|
|
|
151
166
|
if (!parsed.success) {
|
|
152
167
|
return {
|
|
153
168
|
sent: false,
|
|
169
|
+
accepted: false,
|
|
154
170
|
reason: parsed.error.issues[0]?.message ?? 'invalid feedback',
|
|
155
171
|
redacted,
|
|
156
172
|
context,
|
|
157
173
|
};
|
|
158
174
|
}
|
|
159
|
-
|
|
160
|
-
|
|
175
|
+
// BACKGROUND: return now, deliver after. The agent calls this mid-task and a ~340ms POST is the
|
|
176
|
+
// product blocking the user's work to talk about itself. The honesty that the awaited version was
|
|
177
|
+
// written for is kept elsewhere: `sent` stays false (nothing is confirmed yet), `accepted` says
|
|
178
|
+
// the report is well-formed and queued, and a send that then fails reaches the reporter on its
|
|
179
|
+
// next tool result instead of vanishing.
|
|
180
|
+
// Written down BEFORE the network is touched, on both paths. A failed send is then a queued report
|
|
181
|
+
// rather than a lost one — see feedback-outbox. Reported from the field: a 1.3s hiccup destroyed a
|
|
182
|
+
// root-cause analysis that took an hour of driving to produce, and it survived only because the
|
|
183
|
+
// reporter happened to have written the markdown by hand first.
|
|
184
|
+
const queued = queueFeedback(parsed.data);
|
|
185
|
+
if (true === opts.background) {
|
|
186
|
+
void telemetry
|
|
187
|
+
.emit(TelemetryEventKind.FEEDBACK_SUBMITTED, { feedback: parsed.data })
|
|
188
|
+
.then((ok) => {
|
|
189
|
+
if (ok)
|
|
190
|
+
markDelivered(queued);
|
|
191
|
+
else
|
|
192
|
+
noteFeedbackUndelivered('the telemetry endpoint did not accept it');
|
|
193
|
+
})
|
|
194
|
+
.catch((error) => {
|
|
195
|
+
noteFeedbackUndelivered(error instanceof Error ? error.message : String(error));
|
|
196
|
+
});
|
|
197
|
+
return { sent: false, accepted: true, redacted, context };
|
|
198
|
+
}
|
|
199
|
+
// `sent` reflects DELIVERY, not handing the payload to the emitter. It was unconditional, so a DNS
|
|
200
|
+
// miss or a 4xx both reported "filed" — and this is the only qualitative channel the product has,
|
|
201
|
+
// so a silent failure here loses the report AND tells the reporter it worked.
|
|
202
|
+
const delivered = await telemetry.emit(TelemetryEventKind.FEEDBACK_SUBMITTED, {
|
|
203
|
+
feedback: parsed.data,
|
|
204
|
+
});
|
|
205
|
+
if (delivered) {
|
|
206
|
+
markDelivered(queued);
|
|
207
|
+
return { sent: true, accepted: true, redacted, context };
|
|
208
|
+
}
|
|
209
|
+
// Undelivered, but no longer lost. `accepted` is true when it reached the outbox: the report is
|
|
210
|
+
// well-formed and on disk, which is a materially different situation from "it is gone".
|
|
211
|
+
return {
|
|
212
|
+
sent: false,
|
|
213
|
+
accepted: queued !== null,
|
|
214
|
+
reason: queued !== null
|
|
215
|
+
? `the report could not be delivered (the network call failed or was rejected), but it is SAVED at ${outboxPath()} and nothing was lost. It will be retried; you can also open an issue at https://github.com/reticlehq/reticle/issues with the same detail.`
|
|
216
|
+
: 'the report could not be delivered and could not be saved locally either — it was NOT filed. Retry, or open an issue at https://github.com/reticlehq/reticle/issues with the same detail.',
|
|
217
|
+
redacted,
|
|
218
|
+
context,
|
|
219
|
+
};
|
|
161
220
|
}
|
|
162
221
|
/**
|
|
163
222
|
* The receipt's context PLUS the fields the emitter stamps on every event regardless of kind — the
|
|
@@ -96,7 +96,7 @@ export function gitFacts(cwd, read = defaultRead, exists = defaultExists) {
|
|
|
96
96
|
if (url === undefined)
|
|
97
97
|
return { state: GitState.LOCAL_ONLY };
|
|
98
98
|
const origin = normalizeGitOrigin(url);
|
|
99
|
-
if (
|
|
99
|
+
if ('' === origin)
|
|
100
100
|
return { state: GitState.LOCAL_ONLY };
|
|
101
101
|
return { state: GitState.REMOTE, origin, forge: forgeOf(origin) };
|
|
102
102
|
}
|
|
@@ -62,6 +62,8 @@ export declare class SessionMetrics {
|
|
|
62
62
|
breadcrumb: string[];
|
|
63
63
|
inFlight: string | undefined;
|
|
64
64
|
};
|
|
65
|
+
/** One action driven at the page. Settled by the next verdict; see #unsettledActions. */
|
|
66
|
+
recordAction(): void;
|
|
65
67
|
recordVerification(): void;
|
|
66
68
|
/**
|
|
67
69
|
* One defect found in the app under test — the outcome number, kept broken down by kind.
|
|
@@ -21,6 +21,17 @@ import { describeToolParams } from './argument-shape.js';
|
|
|
21
21
|
import { machineSnapshot } from './machine-snapshot.js';
|
|
22
22
|
/** Cap the distinct error shapes held in memory — a pathological loop must not grow unbounded. */
|
|
23
23
|
const MAX_ERROR_KINDS = 40;
|
|
24
|
+
/** Distinct tool names whose repeat-runs are tracked. Its own cap: the tool surface, not error shapes. */
|
|
25
|
+
const MAX_REPEAT_TOOLS = 64;
|
|
26
|
+
/**
|
|
27
|
+
* Tool errors that all mean the same thing to a user: the agent asked, and there was no app it could
|
|
28
|
+
* reach. Three different messages, one experience — "nothing is connected" — and it is the error
|
|
29
|
+
* that ends most sessions before a single verification happens.
|
|
30
|
+
*
|
|
31
|
+
* `multiple sessions connected` belongs here too: the agent could not reach AN app because it could
|
|
32
|
+
* not tell which one, and the outcome is identical.
|
|
33
|
+
*/
|
|
34
|
+
const NO_SESSION_ERROR = /no browser session connected|no connected session with id|multiple sessions connected/i;
|
|
24
35
|
/** Cap distinct MCP clients recorded; more than a handful on one daemon is already the story. */
|
|
25
36
|
const MAX_CLIENTS = 8;
|
|
26
37
|
/** Bounds on the parameter map. Our own schemas are already finite; these are a belt-and-braces cap. */
|
|
@@ -71,6 +82,25 @@ export class SessionMetrics {
|
|
|
71
82
|
* capped: the kind vocabulary is a bounded enum plus oracle names, not user data.
|
|
72
83
|
*/
|
|
73
84
|
#seenBugKinds = new Set();
|
|
85
|
+
/**
|
|
86
|
+
* Tool errors meaning "the agent could not reach an app at all" — the single biggest drop-off in
|
|
87
|
+
* the funnel, and until now reachable only by unpacking `errors[]` in HogQL. 74% of daemons never
|
|
88
|
+
* call a tool, and of the sessions that made exactly one call, most bounced on this.
|
|
89
|
+
*/
|
|
90
|
+
#noSessionErrors = 0;
|
|
91
|
+
/** Longest back-to-back run per tool — a retry loop, which `toolCounts` cannot distinguish. */
|
|
92
|
+
#repeatRuns = new Map();
|
|
93
|
+
#lastTool;
|
|
94
|
+
#currentRun = 0;
|
|
95
|
+
/**
|
|
96
|
+
* Actions driven since the last verdict — the run that is left hanging if the loop stops here.
|
|
97
|
+
*
|
|
98
|
+
* NOT `actions - verifications`. That difference ignores ORDER, so a verdict that drove nothing
|
|
99
|
+
* (a `flow_verify` over saved flows) silently paid for a genuinely abandoned action elsewhere, and
|
|
100
|
+
* a session could verify first and act after and look settled. What is abandoned is the trailing
|
|
101
|
+
* run, which is exactly what the field claims to mean.
|
|
102
|
+
*/
|
|
103
|
+
#unsettledActions = 0;
|
|
74
104
|
#clients = new Set();
|
|
75
105
|
#startedAt;
|
|
76
106
|
#now;
|
|
@@ -120,6 +150,16 @@ export class SessionMetrics {
|
|
|
120
150
|
this.#unknownToolCalls += 1;
|
|
121
151
|
}
|
|
122
152
|
recordToolCall(tool, args) {
|
|
153
|
+
// A run of the same tool back to back. Five calls to reticle_act is engagement; five in a row
|
|
154
|
+
// after four failures is an agent stuck, and toolCounts reports both as "5".
|
|
155
|
+
this.#currentRun = tool === this.#lastTool ? this.#currentRun + 1 : 1;
|
|
156
|
+
this.#lastTool = tool;
|
|
157
|
+
if (this.#currentRun > 1) {
|
|
158
|
+
const best = this.#repeatRuns.get(tool) ?? 0;
|
|
159
|
+
if (this.#currentRun > best &&
|
|
160
|
+
(this.#repeatRuns.has(tool) || this.#repeatRuns.size < MAX_REPEAT_TOOLS))
|
|
161
|
+
this.#repeatRuns.set(tool, this.#currentRun);
|
|
162
|
+
}
|
|
123
163
|
this.#toolCalls += 1;
|
|
124
164
|
bump(this.#toolCounts, tool);
|
|
125
165
|
this.#inFlight = tool;
|
|
@@ -150,6 +190,8 @@ export class SessionMetrics {
|
|
|
150
190
|
*/
|
|
151
191
|
recordToolError(message, tool) {
|
|
152
192
|
this.#toolErrors += 1;
|
|
193
|
+
if (NO_SESSION_ERROR.test(message))
|
|
194
|
+
this.#noSessionErrors += 1;
|
|
153
195
|
const fingerprint = fingerprintError(message);
|
|
154
196
|
const existing = this.#errorKinds.get(fingerprint);
|
|
155
197
|
if (existing !== undefined) {
|
|
@@ -194,8 +236,14 @@ export class SessionMetrics {
|
|
|
194
236
|
get trail() {
|
|
195
237
|
return { breadcrumb: [...this.#breadcrumb], inFlight: this.#inFlight };
|
|
196
238
|
}
|
|
239
|
+
/** One action driven at the page. Settled by the next verdict; see #unsettledActions. */
|
|
240
|
+
recordAction() {
|
|
241
|
+
this.#unsettledActions += 1;
|
|
242
|
+
}
|
|
197
243
|
recordVerification() {
|
|
198
244
|
this.#verifications += 1;
|
|
245
|
+
// A verdict settles whatever was outstanding, however many actions it took to get there.
|
|
246
|
+
this.#unsettledActions = 0;
|
|
199
247
|
}
|
|
200
248
|
/**
|
|
201
249
|
* One defect found in the app under test — the outcome number, kept broken down by kind.
|
|
@@ -258,6 +306,12 @@ export class SessionMetrics {
|
|
|
258
306
|
verifications: this.#verifications,
|
|
259
307
|
bugsFound: this.#bugsFound,
|
|
260
308
|
...(this.#bugKinds.size > 0 ? { bugKinds: Object.fromEntries(this.#bugKinds) } : {}),
|
|
309
|
+
...(this.#noSessionErrors > 0 ? { noSessionErrors: this.#noSessionErrors } : {}),
|
|
310
|
+
...(this.#repeatRuns.size > 0
|
|
311
|
+
? { consecutiveRepeats: Object.fromEntries(this.#repeatRuns) }
|
|
312
|
+
: {}),
|
|
313
|
+
// Absent rather than zero, so the PRESENCE of the field is the signal on a dashboard.
|
|
314
|
+
...(this.#unsettledActions > 0 ? { abandonedActions: this.#unsettledActions } : {}),
|
|
261
315
|
...(this.#toolParams.size > 0
|
|
262
316
|
? {
|
|
263
317
|
toolParams: Object.fromEntries([...this.#toolParams].map(([tool, params]) => [tool, Object.fromEntries(params)])),
|
|
@@ -277,17 +331,22 @@ export class SessionMetrics {
|
|
|
277
331
|
}
|
|
278
332
|
/** True when nothing at all happened — a flush of an idle daemon is not worth an event. */
|
|
279
333
|
get empty() {
|
|
280
|
-
return (this.#toolCalls
|
|
281
|
-
this.#verifications
|
|
282
|
-
this.#toolErrors
|
|
283
|
-
this.#bugsFound
|
|
284
|
-
this.#sdkFailures
|
|
334
|
+
return (0 === this.#toolCalls &&
|
|
335
|
+
0 === this.#verifications &&
|
|
336
|
+
0 === this.#toolErrors &&
|
|
337
|
+
0 === this.#bugsFound &&
|
|
338
|
+
0 === this.#sdkFailures);
|
|
285
339
|
}
|
|
286
340
|
/** Zero the counters after a non-final flush so the next flush reports the NEXT window, not a total. */
|
|
287
341
|
reset() {
|
|
288
342
|
this.#toolCalls = 0;
|
|
289
343
|
this.#toolErrors = 0;
|
|
290
344
|
this.#verifications = 0;
|
|
345
|
+
this.#noSessionErrors = 0;
|
|
346
|
+
this.#repeatRuns.clear();
|
|
347
|
+
this.#lastTool = undefined;
|
|
348
|
+
this.#currentRun = 0;
|
|
349
|
+
this.#unsettledActions = 0;
|
|
291
350
|
this.#toolCounts.clear();
|
|
292
351
|
this.#toolParams.clear();
|
|
293
352
|
this.#errorKinds.clear();
|
|
@@ -302,7 +361,10 @@ export class SessionMetrics {
|
|
|
302
361
|
this.#unknownToolCalls = 0;
|
|
303
362
|
this.#bugsFound = 0;
|
|
304
363
|
this.#bugKinds.clear();
|
|
305
|
-
|
|
364
|
+
// #seenBugKinds is deliberately NOT cleared. It is not a window counter — it is the
|
|
365
|
+
// session-lifetime memory behind `repeat` on bug_found, and zeroing it made the same defect,
|
|
366
|
+
// found again after a flush, report as a newly distinct one. Sessions run to 11.5 hours in the
|
|
367
|
+
// data, so that was up to 23 chances to count one defect many times.
|
|
306
368
|
}
|
|
307
369
|
}
|
|
308
370
|
function bump(map, key) {
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
import { TelemetryEventKind, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type McpConnection, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core';
|
|
1
|
+
import { TelemetryEventKind, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type McpConnection, type McpOutage, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core';
|
|
2
|
+
/**
|
|
3
|
+
* Feedback gets its own, much larger budget — and a retry.
|
|
4
|
+
*
|
|
5
|
+
* 2s + drop is right for a usage counter: the metric is worthless if it costs the user latency, and
|
|
6
|
+
* losing one changes nothing. Feedback is the opposite on both counts. It is the only qualitative
|
|
7
|
+
* channel the product has, it carries an agent's entire root-cause analysis, and losing one loses
|
|
8
|
+
* work that cannot be reconstructed.
|
|
9
|
+
*
|
|
10
|
+
* Measured to the collector with a WARM DNS cache: dns 0.003s, connect 0.233s, tls 0.467s, total
|
|
11
|
+
* 0.694s — a third of the old budget gone before any payload moves, on the good path. A fresh
|
|
12
|
+
* short-lived CLI process pays cold DNS and a cold route on top, which is exactly the case that
|
|
13
|
+
* failed in the field and then succeeded on retry from a warmer process.
|
|
14
|
+
*/
|
|
15
|
+
export declare const FEEDBACK_TIMEOUT_MS = 15000;
|
|
16
|
+
export declare const FEEDBACK_RETRIES = 1;
|
|
17
|
+
export declare const FEEDBACK_RETRY_BACKOFF_MS = 750;
|
|
2
18
|
/** The current telemetry state and which control set it — what `reticle telemetry status` prints. */
|
|
3
19
|
export declare const describeTelemetry: (env?: NodeJS.ProcessEnv, dir?: string, cwd?: string) => {
|
|
4
20
|
enabled: boolean;
|
|
@@ -38,6 +54,8 @@ export interface TelemetryExtra {
|
|
|
38
54
|
init?: InitOutcome;
|
|
39
55
|
/** `bug_found`: one defect Reticle found in the app under test. */
|
|
40
56
|
bug?: BugFound;
|
|
57
|
+
/** `mcp_connection_lost`: the agent lost its tools — which stage, and why. */
|
|
58
|
+
outage?: McpOutage;
|
|
41
59
|
}
|
|
42
60
|
export interface Telemetry {
|
|
43
61
|
/**
|
|
@@ -45,7 +63,14 @@ export interface Telemetry {
|
|
|
45
63
|
* CLI process can exit immediately instead of waiting out the POST (use it from command entry
|
|
46
64
|
* points; daemon-side events send in-process).
|
|
47
65
|
*/
|
|
48
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Send one event. Resolves to whether it was DELIVERED.
|
|
68
|
+
*
|
|
69
|
+
* Almost every caller ignores the result and should — a lost metric must never surface to a user.
|
|
70
|
+
* `reticle_feedback` is the exception: it hands a receipt to an agent, and reporting "filed" for a
|
|
71
|
+
* send that failed loses the report and lies about it in the same breath.
|
|
72
|
+
*/
|
|
73
|
+
emit(kind: TelemetryEventKind, extra?: TelemetryExtra): Promise<boolean>;
|
|
49
74
|
readonly enabled: boolean;
|
|
50
75
|
/** True the very first run on this machine — the CLI emits INSTALL alongside the first INVOKE. */
|
|
51
76
|
readonly firstRun: boolean;
|