@reticlehq/server 3.6.0 → 3.7.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 +122 -123
- package/dist/command/cli/adhoc-suite.d.ts +14 -0
- package/dist/command/cli/adhoc-suite.js +42 -7
- package/dist/command/cli/adhoc-verdict.d.ts +18 -2
- package/dist/command/cli/adhoc-verdict.js +70 -6
- package/dist/command/cli/answers/gate-exit.d.ts +23 -0
- package/dist/command/cli/answers/gate-exit.js +45 -0
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +3 -1
- package/dist/command/cli/auth/device-flow.d.ts +13 -2
- package/dist/command/cli/auth/device-flow.js +17 -0
- package/dist/command/cli/cli-doctor.js +14 -0
- package/dist/command/cli/cli-flow-commands.d.ts +0 -11
- package/dist/command/cli/cli-flow-commands.js +28 -105
- package/dist/command/cli/cli-kill.js +3 -2
- package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
- package/dist/command/cli/cli-parse-grammar.js +75 -0
- package/dist/command/cli/cli-parse-verify.d.ts +2 -0
- package/dist/command/cli/cli-parse-verify.js +18 -0
- package/dist/command/cli/cli-parse.d.ts +19 -28
- package/dist/command/cli/cli-parse.js +82 -182
- package/dist/command/cli/cli-update-commands.d.ts +1 -1
- package/dist/command/cli/cli-update-commands.js +61 -9
- package/dist/command/cli/cli-usage.d.ts +33 -0
- package/dist/command/cli/cli-usage.js +287 -0
- package/dist/command/cli/cli-verify.d.ts +34 -4
- package/dist/command/cli/cli-verify.js +81 -12
- package/dist/command/cli/cloud-cli.d.ts +13 -0
- package/dist/command/cli/cloud-cli.js +145 -333
- package/dist/command/cli/cloud-kit.d.ts +12 -0
- package/dist/command/cli/cloud-kit.js +41 -2
- package/dist/command/cli/cloud-link.d.ts +57 -0
- package/dist/command/cli/cloud-link.js +308 -0
- package/dist/command/cli/cloud-login.d.ts +15 -1
- package/dist/command/cli/cloud-login.js +67 -13
- package/dist/command/cli/context-command.d.ts +1 -0
- package/dist/command/cli/context-command.js +33 -0
- package/dist/command/cli/daemon-start-options.d.ts +3 -0
- package/dist/command/cli/daemon-start-options.js +24 -3
- package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
- package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
- package/dist/command/cli/doctor/doctor-project-line.js +5 -4
- package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
- package/dist/command/cli/doctor/doctor-rows.js +6 -0
- package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
- package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
- package/dist/command/cli/known-regression.d.ts +13 -0
- package/dist/command/cli/known-regression.js +34 -0
- package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
- package/dist/command/cli/launch/sdk-version-match.js +25 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +11 -2
- package/dist/command/cli/mcp-command.d.ts +6 -0
- package/dist/command/cli/mcp-command.js +13 -5
- package/dist/command/cli/ports/port-holder.js +1 -1
- package/dist/command/cli/project-name.d.ts +29 -1
- package/dist/command/cli/project-name.js +107 -2
- package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
- package/dist/command/cli/setup-mcp-cli.js +29 -8
- package/dist/command/cli/status/status-lines.d.ts +1 -0
- package/dist/command/cli/status/status-lines.js +47 -2
- package/dist/command/cli/status/workspace-links.d.ts +10 -0
- package/dist/command/cli/status/workspace-links.js +29 -0
- package/dist/command/cli/try-command.d.ts +96 -0
- package/dist/command/cli/try-command.js +362 -0
- package/dist/command/cli/tutorial.d.ts +0 -100
- package/dist/command/cli/tutorial.js +0 -178
- package/dist/command/cli/verify-results.d.ts +19 -0
- package/dist/command/cli/verify-results.js +29 -0
- package/dist/command/cli-onboarding.d.ts +0 -11
- package/dist/command/cli-onboarding.js +0 -15
- package/dist/command/cli.d.ts +14 -0
- package/dist/command/cli.js +103 -83
- package/dist/command/daemon/daemon-resilience.js +4 -4
- package/dist/command/daemon/daemon-resolve.d.ts +9 -1
- package/dist/command/daemon/daemon-resolve.js +49 -10
- package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
- package/dist/command/setup/bringup/bridge-port.js +13 -3
- package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
- package/dist/command/setup/bringup/ensure-daemon.js +9 -2
- package/dist/command/setup/init/agent-restart.d.ts +23 -0
- package/dist/command/setup/init/agent-restart.js +28 -0
- package/dist/command/setup/init/init-port.d.ts +10 -1
- package/dist/command/setup/init/init-port.js +22 -2
- package/dist/command/setup/init/init-runtime.d.ts +12 -2
- package/dist/command/setup/init/init-runtime.js +96 -37
- package/dist/command/setup/node-effects.d.ts +2 -0
- package/dist/command/setup/node-effects.js +36 -8
- package/dist/command/setup/probe/existing-app.d.ts +12 -0
- package/dist/command/setup/probe/existing-app.js +14 -0
- package/dist/command/setup/remaining-steps.js +1 -1
- package/dist/command/setup/run-setup.d.ts +29 -9
- package/dist/command/setup/run-setup.js +34 -28
- package/dist/command/setup/setup-command.d.ts +23 -0
- package/dist/command/setup/setup-command.js +57 -4
- package/dist/command/setup/setup-mcp.js +25 -2
- package/dist/command/setup/tauri-signing.d.ts +19 -0
- package/dist/command/setup/tauri-signing.js +79 -0
- package/dist/command/setup/terminal/listeners.d.ts +10 -1
- package/dist/command/setup/terminal/listeners.js +37 -6
- package/dist/command/update/update-nudge.d.ts +9 -0
- package/dist/command/update/update-nudge.js +1 -1
- package/dist/command/update/updater.js +11 -15
- package/dist/command/version/version-nudge.js +3 -0
- package/dist/command/version/version-skew.d.ts +20 -1
- package/dist/command/version/version-skew.js +38 -2
- package/dist/features/crawl/crawl.js +14 -1
- package/dist/features/exhaust/ledger.d.ts +14 -14
- package/dist/features/harness/drive-report.d.ts +39 -5
- package/dist/features/harness/drive-report.js +271 -54
- package/dist/features/harness/drive-runs.d.ts +120 -0
- package/dist/features/harness/drive-runs.js +289 -0
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/explore/console-pages.d.ts +15 -0
- package/dist/features/harness/explore/console-pages.js +146 -0
- package/dist/features/harness/explore/disparity.d.ts +19 -0
- package/dist/features/harness/explore/disparity.js +71 -0
- package/dist/features/harness/explore/fake-app.d.ts +16 -0
- package/dist/features/harness/explore/fake-app.js +49 -0
- package/dist/features/harness/explore/goal-search.d.ts +26 -0
- package/dist/features/harness/explore/goal-search.js +144 -0
- package/dist/features/harness/explore/page-tree.d.ts +49 -0
- package/dist/features/harness/explore/page-tree.js +148 -0
- package/dist/features/harness/explore/site-map.d.ts +34 -0
- package/dist/features/harness/explore/site-map.js +150 -0
- package/dist/features/harness/goals.d.ts +68 -7
- package/dist/features/harness/goals.js +289 -36
- package/dist/features/harness/harness.d.ts +30 -1
- package/dist/features/harness/harness.js +141 -7
- package/dist/features/harness/platform/local-apps.d.ts +36 -3
- package/dist/features/harness/platform/local-apps.js +81 -5
- package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
- package/dist/features/harness/platform/persona-credentials.js +69 -0
- package/dist/features/harness/platform/platform-drives.d.ts +61 -0
- package/dist/features/harness/platform/platform-drives.js +114 -0
- package/dist/features/harness/platform/refusal.d.ts +52 -0
- package/dist/features/harness/platform/refusal.js +71 -0
- package/dist/features/harness/platform/remote-drive.d.ts +8 -2
- package/dist/features/harness/platform/remote-drive.js +22 -9
- package/dist/features/harness/platform/script.d.ts +57 -11
- package/dist/features/harness/platform/script.js +144 -12
- package/dist/features/harness/platform/server-driver.d.ts +17 -4
- package/dist/features/harness/platform/server-driver.js +29 -26
- package/dist/features/harness/platform-config.d.ts +38 -5
- package/dist/features/harness/platform-config.js +93 -8
- package/dist/features/harness/route-guess.d.ts +13 -0
- package/dist/features/harness/route-guess.js +49 -0
- package/dist/features/harness/script/script-run.d.ts +30 -1
- package/dist/features/harness/script/script-run.js +355 -48
- package/dist/features/license/license.d.ts +2 -2
- package/dist/features/visual/step-shots.d.ts +31 -0
- package/dist/features/visual/step-shots.js +231 -0
- package/dist/features/visual/visual-tools.js +22 -14
- package/dist/hooks/client-directory.d.ts +18 -0
- package/dist/hooks/client-directory.js +57 -0
- package/dist/hooks/coding-agents.d.ts +34 -0
- package/dist/hooks/coding-agents.js +117 -0
- package/dist/hooks/driven-by.d.ts +21 -2
- package/dist/hooks/driven-by.js +42 -2
- package/dist/hooks/hook-bus.d.ts +4 -0
- package/dist/hooks/hook-bus.js +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +26 -183
- package/dist/judgement/capsule/capsule-store.d.ts +2 -2
- package/dist/judgement/capsule/causal-summary.d.ts +15 -1
- package/dist/judgement/capsule/causal-summary.js +33 -0
- package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
- package/dist/judgement/runs/artifact/build-verification-run.js +20 -4
- package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
- package/dist/judgement/runs/artifact/gap-summary.js +12 -3
- package/dist/judgement/runs/artifact/replay-mapping.js +25 -9
- package/dist/judgement/runs/artifact/run-store.d.ts +13 -1
- package/dist/judgement/runs/artifact/run-store.js +175 -8
- package/dist/judgement/runs/context-tools.js +59 -1
- package/dist/judgement/runs/default-run-id.d.ts +3 -0
- package/dist/judgement/runs/default-run-id.js +16 -0
- package/dist/judgement/runs/drive-run.d.ts +53 -5
- package/dist/judgement/runs/drive-run.js +234 -20
- package/dist/judgement/runs/resume.d.ts +44 -0
- package/dist/judgement/runs/resume.js +137 -0
- package/dist/judgement/runs/reticle-runner.js +5 -2
- package/dist/judgement/runs/runner-port.d.ts +5 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +2 -0
- package/dist/judgement/runs/verification-sync.js +36 -15
- package/dist/language/flows/decision.js +2 -1
- package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
- package/dist/language/flows/fields/flow-secret-field.js +25 -2
- package/dist/language/flows/flow-anchor.d.ts +7 -2
- package/dist/language/flows/flow-anchor.js +10 -3
- package/dist/language/flows/flow-intent.d.ts +1 -0
- package/dist/language/flows/flow-journey.d.ts +6 -0
- package/dist/language/flows/flow-journey.js +20 -0
- package/dist/language/flows/flow-learning.d.ts +4 -0
- package/dist/language/flows/flow-learning.js +37 -3
- package/dist/language/flows/flow-replay-run.js +16 -18
- package/dist/language/flows/flow-replay-types.d.ts +1 -1
- package/dist/language/flows/flow-replay.d.ts +18 -1
- package/dist/language/flows/flow-replay.js +22 -7
- package/dist/language/flows/flow-result.d.ts +22 -1
- package/dist/language/flows/flow-result.js +47 -2
- package/dist/language/flows/flow-step-runners.d.ts +2 -0
- package/dist/language/flows/flow-step-runners.js +12 -6
- package/dist/language/flows/flow-store-for-session.js +5 -1
- package/dist/language/flows/flow-tools.d.ts +10 -0
- package/dist/language/flows/flow-tools.js +61 -62
- package/dist/language/flows/flows.d.ts +16 -1
- package/dist/language/flows/flows.js +69 -10
- package/dist/language/flows/narrow-write.d.ts +7 -0
- package/dist/language/flows/narrow-write.js +36 -1
- package/dist/language/flows/recording/recording-backtrack.js +14 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +29 -1
- package/dist/language/flows/recording/tape/recordings.js +60 -10
- package/dist/language/flows/replay.js +29 -6
- package/dist/language/flows/single-replay.d.ts +7 -0
- package/dist/language/flows/single-replay.js +26 -0
- package/dist/language/flows/verify-change-tools.d.ts +1 -0
- package/dist/language/flows/verify-change-tools.js +200 -193
- package/dist/memory/cloud/cloud-config.d.ts +10 -1
- package/dist/memory/cloud/cloud-config.js +19 -5
- package/dist/memory/cloud/cloud-sync.js +3 -3
- package/dist/memory/cloud/harness-config.d.ts +49 -0
- package/dist/memory/cloud/harness-config.js +16 -20
- package/dist/memory/cloud/memory-scope.d.ts +17 -0
- package/dist/memory/cloud/memory-scope.js +22 -0
- package/dist/memory/cloud/sync-cycle.d.ts +32 -4
- package/dist/memory/cloud/sync-cycle.js +113 -12
- package/dist/memory/cloud/sync-daemon.d.ts +11 -0
- package/dist/memory/cloud/sync-daemon.js +114 -4
- package/dist/memory/cloud/sync-disk.js +26 -2
- package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
- package/dist/memory/cloud/sync-screenshots.js +105 -0
- package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
- package/dist/memory/cloud/unsynced-roots.js +166 -0
- package/dist/memory/impact/account-watch.d.ts +16 -1
- package/dist/memory/impact/account-watch.js +26 -1
- package/dist/memory/impact/impact-store.d.ts +1 -4
- package/dist/memory/impact/impact-store.js +32 -42
- package/dist/memory/intent/inline-intent.d.ts +1 -0
- package/dist/memory/intent/intent-shard-store.js +16 -4
- package/dist/memory/intent/intent-shard.d.ts +12 -0
- package/dist/memory/intent/intent-store.d.ts +1 -0
- package/dist/memory/intent/intent-tools.js +42 -2
- package/dist/memory/intent/open-intents.d.ts +6 -0
- package/dist/memory/intent/open-intents.js +10 -1
- package/dist/memory/intent/settled-words.d.ts +24 -0
- package/dist/memory/intent/settled-words.js +130 -0
- package/dist/memory/journal/drive-flow.d.ts +16 -0
- package/dist/memory/journal/drive-flow.js +34 -2
- package/dist/memory/journal/drive-run-flush.d.ts +2 -0
- package/dist/memory/journal/drive-run-flush.js +20 -5
- package/dist/memory/journal/journal-recorder.js +46 -3
- package/dist/memory/journal/on-disk/retention.d.ts +5 -0
- package/dist/memory/journal/on-disk/retention.js +27 -2
- package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
- package/dist/memory/journal/session-end.d.ts +15 -1
- package/dist/memory/journal/session-end.js +38 -7
- package/dist/memory/project/artifact-root-resolver.js +33 -5
- package/dist/memory/project/artifact-root.d.ts +4 -3
- package/dist/memory/project/artifact-root.js +2 -2
- package/dist/memory/project/dir/reticle-dir.d.ts +14 -0
- package/dist/memory/project/dir/reticle-dir.js +23 -2
- package/dist/memory/project/link-directory.d.ts +15 -0
- package/dist/memory/project/link-directory.js +44 -0
- package/dist/memory/project/root-claims.d.ts +6 -0
- package/dist/memory/project/root-claims.js +39 -0
- package/dist/memory/project/session-root.d.ts +2 -0
- package/dist/memory/project/session-root.js +9 -1
- package/dist/memory/project/sync-status.d.ts +46 -0
- package/dist/memory/project/sync-status.js +135 -1
- package/dist/memory/recall/memory-tools.js +46 -3
- package/dist/memory/recall/platform-memory.d.ts +34 -0
- package/dist/memory/recall/platform-memory.js +139 -0
- package/dist/memory/recall/project-memory.js +36 -8
- package/dist/portal/bridge/bridge.d.ts +9 -5
- package/dist/portal/bridge/bridge.js +66 -23
- package/dist/portal/bridge/harness-request.d.ts +23 -0
- package/dist/portal/bridge/harness-request.js +27 -0
- package/dist/portal/bridge/wrong-path.d.ts +17 -0
- package/dist/portal/bridge/wrong-path.js +35 -0
- package/dist/portal/input/network-mock-tools.js +11 -2
- package/dist/portal/input/real-input.d.ts +2 -0
- package/dist/portal/input/real-input.js +13 -10
- package/dist/portal/pool/browser-pool.d.ts +48 -0
- package/dist/portal/pool/browser-pool.js +120 -2
- package/dist/portal/pool/context-permissions.d.ts +33 -0
- package/dist/portal/pool/context-permissions.js +33 -0
- package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
- package/dist/portal/pool/lease-marker-stamp.js +49 -0
- package/dist/portal/pool/playwright-launcher.d.ts +27 -5
- package/dist/portal/pool/playwright-launcher.js +59 -14
- package/dist/portal/pool/pool-contract.d.ts +21 -1
- package/dist/portal/session/agent-link.d.ts +8 -0
- package/dist/portal/session/agent-link.js +17 -0
- package/dist/portal/session/command-payload.js +6 -1
- package/dist/portal/session/daemon-answers.d.ts +3 -3
- package/dist/portal/session/daemon-answers.js +4 -2
- package/dist/portal/session/dev-server/dev-command.d.ts +9 -0
- package/dist/portal/session/dev-server/dev-command.js +27 -1
- package/dist/portal/session/facts/observed-state.d.ts +16 -18
- package/dist/portal/session/facts/observed-state.js +46 -3
- package/dist/portal/session/human/live-control.d.ts +9 -2
- package/dist/portal/session/human/live-control.js +12 -3
- package/dist/portal/session/live-control-tools.js +82 -13
- package/dist/portal/session/narration-args.d.ts +5 -0
- package/dist/portal/session/narration-args.js +17 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +1 -1
- package/dist/portal/session/no-session-diagnosis.js +31 -9
- package/dist/portal/session/no-session-next-action.d.ts +20 -0
- package/dist/portal/session/no-session-next-action.js +134 -61
- package/dist/portal/session/no-session-watch.js +179 -22
- package/dist/portal/session/presence/session-lease.js +1 -1
- package/dist/portal/session/recorded-gaps.d.ts +97 -0
- package/dist/portal/session/recorded-gaps.js +251 -0
- package/dist/portal/session/session-health.d.ts +11 -0
- package/dist/portal/session/session-health.js +3 -1
- package/dist/portal/session/session-info.js +2 -1
- package/dist/portal/session/session-manager.d.ts +18 -0
- package/dist/portal/session/session-manager.js +54 -3
- package/dist/portal/session/session-successor.d.ts +8 -0
- package/dist/portal/session/session-successor.js +1 -1
- package/dist/portal/session/session.d.ts +15 -28
- package/dist/portal/session/session.js +60 -61
- package/dist/portal/session/timing/command-timeout.js +25 -1
- package/dist/portal/session/timing/wall-clock.d.ts +1 -1
- package/dist/portal/session/timing/wall-clock.js +1 -1
- package/dist/portal/session/zero-install-first-call.d.ts +31 -0
- package/dist/portal/session/zero-install-first-call.js +88 -0
- package/dist/start-options.d.ts +7 -1
- package/dist/start-wiring.d.ts +63 -0
- package/dist/start-wiring.js +169 -0
- package/dist/status-payload.d.ts +13 -1
- package/dist/status-payload.js +13 -1
- package/dist/surface/http-server.js +7 -1
- package/dist/surface/mcp/attach-memory.d.ts +1 -0
- package/dist/surface/mcp/attach-memory.js +12 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +4 -0
- package/dist/surface/mcp/mcp-proxy.js +29 -8
- package/dist/surface/mcp/mcp.d.ts +23 -0
- package/dist/surface/mcp/mcp.js +34 -4
- package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
- package/dist/surface/mcp/proxy/drive-calls.js +27 -0
- package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
- package/dist/surface/mcp/server-instructions.js +22 -14
- package/dist/surface/mcp/shared-params.js +1 -1
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +1 -0
- package/dist/surface/tools/account-tool.d.ts +22 -0
- package/dist/surface/tools/account-tool.js +157 -0
- package/dist/surface/tools/act/act-danger.d.ts +11 -0
- package/dist/surface/tools/act/act-danger.js +38 -1
- package/dist/surface/tools/act/act-observation.d.ts +33 -0
- package/dist/surface/tools/act/act-observation.js +57 -0
- package/dist/surface/tools/act/act-preflight.js +25 -2
- package/dist/surface/tools/act/act-target.js +8 -3
- package/dist/surface/tools/act/resolve-target.d.ts +1 -1
- package/dist/surface/tools/act/resolve-target.js +18 -3
- package/dist/surface/tools/act/screen-request.d.ts +34 -0
- package/dist/surface/tools/act/screen-request.js +113 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
- package/dist/surface/tools/act/sequence-grade.js +13 -2
- package/dist/surface/tools/act-sequence-tool.js +136 -27
- package/dist/surface/tools/act-tools.d.ts +0 -9
- package/dist/surface/tools/act-tools.js +48 -34
- package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
- package/dist/surface/tools/agent/app-notes-learning.js +80 -0
- package/dist/surface/tools/agent/app-notes.d.ts +4 -0
- package/dist/surface/tools/agent/app-notes.js +48 -0
- package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
- package/dist/surface/tools/agent/build-staleness.js +62 -0
- package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
- package/dist/surface/tools/agent/compact-envelope.js +374 -0
- package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
- package/dist/surface/tools/agent/for-the-agent.js +44 -0
- package/dist/surface/tools/agent/notices.d.ts +50 -0
- package/dist/surface/tools/agent/notices.js +152 -0
- package/dist/surface/tools/agent/reteach.d.ts +11 -0
- package/dist/surface/tools/agent/reteach.js +50 -0
- package/dist/surface/tools/args/alias-args.d.ts +5 -0
- package/dist/surface/tools/args/alias-args.js +10 -0
- package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
- package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
- package/dist/surface/tools/assert/assert-source.d.ts +15 -0
- package/dist/surface/tools/assert/assert-source.js +40 -0
- package/dist/surface/tools/assert/assert-verdict.js +25 -15
- package/dist/surface/tools/browser-tools.js +43 -1
- package/dist/surface/tools/chat-drives.d.ts +75 -0
- package/dist/surface/tools/chat-drives.js +265 -21
- package/dist/surface/tools/coverage-tools.d.ts +2 -10
- package/dist/surface/tools/coverage-tools.js +2 -53
- package/dist/surface/tools/dynamic-tools.js +29 -1
- package/dist/surface/tools/empty-session-list.d.ts +10 -0
- package/dist/surface/tools/empty-session-list.js +45 -5
- package/dist/surface/tools/error-recovery.d.ts +8 -0
- package/dist/surface/tools/error-recovery.js +69 -5
- package/dist/surface/tools/explore-tools.d.ts +23 -2
- package/dist/surface/tools/explore-tools.js +377 -74
- package/dist/surface/tools/feedback-tools.d.ts +2 -3
- package/dist/surface/tools/feedback-tools.js +4 -3
- package/dist/surface/tools/gap-novelty.d.ts +3 -1
- package/dist/surface/tools/gap-novelty.js +8 -1
- package/dist/surface/tools/harness-explore.d.ts +52 -73
- package/dist/surface/tools/harness-explore.js +177 -146
- package/dist/surface/tools/harness-gate.d.ts +88 -0
- package/dist/surface/tools/harness-gate.js +235 -0
- package/dist/surface/tools/harness-hunt.d.ts +43 -0
- package/dist/surface/tools/harness-hunt.js +215 -0
- package/dist/surface/tools/harness-plan.d.ts +5 -0
- package/dist/surface/tools/harness-plan.js +7 -0
- package/dist/surface/tools/harness-script.d.ts +22 -0
- package/dist/surface/tools/harness-script.js +256 -38
- package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
- package/dist/surface/tools/harness-tab-guard.js +27 -0
- package/dist/surface/tools/harness-toolset.d.ts +8 -0
- package/dist/surface/tools/harness-toolset.js +27 -4
- package/dist/surface/tools/human-notes.d.ts +16 -0
- package/dist/surface/tools/human-notes.js +27 -0
- package/dist/surface/tools/invoke-tool.d.ts +5 -2
- package/dist/surface/tools/invoke-tool.js +220 -93
- package/dist/surface/tools/lease-hint.js +26 -5
- package/dist/surface/tools/lease-permissions.d.ts +33 -0
- package/dist/surface/tools/lease-permissions.js +73 -0
- package/dist/surface/tools/lease-readiness.d.ts +38 -0
- package/dist/surface/tools/lease-readiness.js +57 -0
- package/dist/surface/tools/lease-seed.d.ts +20 -0
- package/dist/surface/tools/lease-seed.js +45 -1
- package/dist/surface/tools/lease-tools.d.ts +10 -1
- package/dist/surface/tools/lease-tools.js +119 -69
- package/dist/surface/tools/live-call-text.js +2 -2
- package/dist/surface/tools/merge-tools.js +42 -2
- package/dist/surface/tools/merged-name-redirect.js +4 -0
- package/dist/surface/tools/next-step.d.ts +11 -1
- package/dist/surface/tools/next-step.js +73 -18
- package/dist/surface/tools/observe-tools.js +43 -5
- package/dist/surface/tools/platform-moment.d.ts +60 -0
- package/dist/surface/tools/platform-moment.js +132 -4
- package/dist/surface/tools/read-tools.js +65 -14
- package/dist/surface/tools/reticle-result.d.ts +120 -0
- package/dist/surface/tools/reticle-result.js +364 -0
- package/dist/surface/tools/tool-kit.d.ts +68 -2
- package/dist/surface/tools/tool-kit.js +73 -1
- package/dist/surface/tools/tools.js +25 -13
- package/dist/telemetry/account-envelope.d.ts +7 -0
- package/dist/telemetry/account-envelope.js +61 -0
- package/dist/telemetry/agent-client.d.ts +15 -0
- package/dist/telemetry/agent-client.js +57 -0
- package/dist/telemetry/app-instrumented.js +9 -2
- package/dist/telemetry/argument-shape.d.ts +7 -0
- package/dist/telemetry/argument-shape.js +86 -1
- package/dist/telemetry/automation-hint.d.ts +2 -3
- package/dist/telemetry/automation-hint.js +33 -8
- package/dist/telemetry/cli-telemetry.js +7 -2
- package/dist/telemetry/daemon-telemetry.js +3 -0
- package/dist/telemetry/error-fingerprint.d.ts +10 -1
- package/dist/telemetry/error-fingerprint.js +61 -5
- package/dist/telemetry/feedback-cli.d.ts +0 -13
- package/dist/telemetry/feedback-cli.js +0 -77
- package/dist/telemetry/feedback.d.ts +13 -0
- package/dist/telemetry/feedback.js +183 -17
- package/dist/telemetry/harness-run.d.ts +18 -0
- package/dist/telemetry/harness-run.js +80 -0
- package/dist/telemetry/hud-events.d.ts +16 -0
- package/dist/telemetry/hud-events.js +27 -0
- package/dist/telemetry/hud-metrics.d.ts +27 -6
- package/dist/telemetry/hud-metrics.js +99 -25
- package/dist/telemetry/init-telemetry.js +2 -2
- package/dist/telemetry/install-source.d.ts +13 -25
- package/dist/telemetry/install-source.js +44 -3
- package/dist/telemetry/mcp-connection.d.ts +0 -14
- package/dist/telemetry/mcp-connection.js +5 -1
- package/dist/telemetry/onboarding-firsts.d.ts +32 -0
- package/dist/telemetry/onboarding-firsts.js +81 -6
- package/dist/telemetry/platform-linked.d.ts +5 -0
- package/dist/telemetry/platform-linked.js +32 -0
- package/dist/telemetry/project-profile.js +5 -3
- package/dist/telemetry/proxy-telemetry.js +4 -1
- package/dist/telemetry/session-metrics.d.ts +15 -4
- package/dist/telemetry/session-metrics.js +88 -52
- package/dist/telemetry/telemetry.d.ts +26 -2
- package/dist/telemetry/telemetry.js +102 -21
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +12 -4
- package/dist/wire-journal.d.ts +2 -2
- package/dist/wire-journal.js +16 -5
- package/docs/README.md +2 -2
- package/docs/actions.mdx +6 -3
- package/docs/adapters.md +1 -1
- package/docs/agent-cheatsheet.md +24 -27
- package/docs/architecture.md +5 -5
- package/docs/autodrive.md +13 -12
- package/docs/benchmarks.md +5 -5
- package/docs/best-practices.mdx +27 -9
- package/docs/capabilities.mdx +14 -16
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +54 -75
- package/docs/cli/doctor.mdx +8 -3
- package/docs/cli/drive.mdx +12 -12
- package/docs/cli/feedback.mdx +3 -3
- package/docs/cli/gate.mdx +7 -2
- package/docs/cli/help.mdx +50 -55
- package/docs/cli/init.mdx +32 -15
- package/docs/cli/license.mdx +12 -8
- package/docs/cli/mcp.mdx +4 -4
- package/docs/cli/open.mdx +3 -3
- package/docs/cli/report.mdx +4 -2
- package/docs/cli/restart.mdx +2 -2
- package/docs/cli/rollback.mdx +14 -8
- package/docs/cli/serve.mdx +4 -4
- package/docs/cli/setup.mdx +23 -0
- package/docs/cli/status.mdx +13 -4
- package/docs/cli/stop.mdx +67 -8
- package/docs/cli/try.mdx +62 -0
- package/docs/cli/update.mdx +4 -3
- package/docs/cli/verify.mdx +17 -6
- package/docs/cli/version.mdx +3 -2
- package/docs/cli.mdx +110 -41
- package/docs/debugging.md +3 -3
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +5 -3
- package/docs/enterprise.md +11 -10
- package/docs/environment.mdx +5 -2
- package/docs/faq.mdx +3 -3
- package/docs/first-drive.md +5 -4
- package/docs/flows.md +35 -27
- package/docs/for-agents.md +4 -5
- package/docs/frameworks.mdx +32 -21
- package/docs/getting-started.md +66 -64
- package/docs/human-control.md +15 -14
- package/docs/index.mdx +15 -43
- package/docs/install-agentic.mdx +22 -37
- package/docs/install-manual.mdx +22 -12
- package/docs/instrumentation.mdx +5 -3
- package/docs/integration-patterns.md +7 -7
- package/docs/license-activation.mdx +1 -1
- package/docs/multi-agent-testing.md +24 -6
- package/docs/packages/babel-plugin.mdx +4 -2
- package/docs/packages/browser.mdx +16 -10
- package/docs/packages/core.mdx +12 -5
- package/docs/packages/electron.mdx +3 -2
- package/docs/packages/engine.mdx +1 -1
- package/docs/packages/eslint-plugin.mdx +18 -12
- package/docs/packages/next.mdx +8 -7
- package/docs/packages/open-verification.mdx +1 -1
- package/docs/packages/server.mdx +5 -5
- package/docs/packages/tauri.mdx +8 -8
- package/docs/packages/test.mdx +4 -10
- package/docs/packages/vite-plugin.mdx +11 -4
- package/docs/packages.mdx +11 -15
- package/docs/platform-integration.md +6 -5
- package/docs/predicates.mdx +40 -24
- package/docs/quickstart.mdx +58 -29
- package/docs/required-checks.md +8 -7
- package/docs/reticle-manual.md +15 -5
- package/docs/skill-file.mdx +17 -79
- package/docs/skill-setup.md +9 -9
- package/docs/skill.md +200 -0
- package/docs/system-map.md +7 -7
- package/docs/telemetry.md +22 -24
- package/docs/testing.md +4 -4
- package/docs/token-efficiency.md +12 -12
- package/docs/tools/act-and-wait.mdx +9 -2
- package/docs/tools/act-sequence.mdx +6 -7
- package/docs/tools/act.mdx +2 -2
- package/docs/tools/assert.mdx +18 -17
- package/docs/tools/console.mdx +9 -9
- package/docs/tools/inspect.mdx +16 -7
- package/docs/tools/look.mdx +24 -0
- package/docs/tools/navigate.mdx +5 -3
- package/docs/tools/network.mdx +2 -2
- package/docs/tools/observe.mdx +6 -6
- package/docs/tools/overview.mdx +34 -33
- package/docs/tools/query.mdx +5 -5
- package/docs/tools/session-and-feedback.mdx +13 -4
- package/docs/tools/sessions.mdx +8 -8
- package/docs/tools/snapshot.mdx +6 -5
- package/docs/tools/state.mdx +9 -9
- package/docs/tools/tools-and-run.mdx +26 -24
- package/docs/tools/verify.mdx +28 -0
- package/docs/tools/wait-for.mdx +8 -4
- package/docs/troubleshooting.mdx +21 -6
- package/docs/usage.md +51 -45
- package/docs/use-cases.mdx +3 -3
- package/docs/vs-chrome-devtools-mcp.mdx +9 -6
- package/docs/vs-playwright-mcp.mdx +3 -1
- package/docs/what-is-recorded.md +7 -5
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +6 -5
- package/dist/command/cli/demo/demo-page.d.ts +0 -78
- package/dist/command/cli/demo/demo-page.js +0 -249
- package/dist/command/cli/demo/demo-run.d.ts +0 -38
- package/dist/command/cli/demo/demo-run.js +0 -138
- package/dist/judgement/hunt/hunt-report.d.ts +0 -49
- package/dist/judgement/hunt/hunt-report.js +0 -84
- package/dist/language/flows/buddy-status.d.ts +0 -25
- package/dist/language/flows/buddy-status.js +0 -32
- package/dist/language/flows/change/watch-batcher.d.ts +0 -18
- package/dist/language/flows/change/watch-batcher.js +0 -19
- package/dist/memory/cloud/harness-offer.d.ts +0 -44
- package/dist/memory/cloud/harness-offer.js +0 -145
- package/dist/surface/tools/feature-capture.d.ts +0 -151
- package/dist/surface/tools/feature-capture.js +0 -235
- package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
- package/dist/surface/tools/tool-hit-rate.js +0 -90
- package/dist/telemetry/harness-drive.d.ts +0 -4
- package/dist/telemetry/harness-drive.js +0 -35
- package/dist/telemetry/identify.d.ts +0 -22
- package/dist/telemetry/identify.js +0 -73
- package/docs/cli/capsules.mdx +0 -70
- package/docs/cli/hunt.mdx +0 -73
- package/docs/cli/identify.mdx +0 -76
- package/docs/cli/kill.mdx +0 -91
- package/docs/cli/watch.mdx +0 -62
|
@@ -40,8 +40,13 @@ const VARIABLE_PARTS = [
|
|
|
40
40
|
// ── Then the grouping rules ──────────────────────────────────────────────────────────────────
|
|
41
41
|
/"[^"]*"|'[^']*'|`[^`]*`/g, // quoted values — names, selectors, snippets
|
|
42
42
|
/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi, // URLs
|
|
43
|
+
// Windows paths run to the end of the message: a directory name may hold a space, and
|
|
44
|
+
// `C:\Users\Jane Doe\…` cut at the first space sent the surname.
|
|
45
|
+
/(?:\\\\\?\\)?[A-Za-z]:[\\/].*$/g,
|
|
46
|
+
/~[\\/]\S*/g, // home-relative paths
|
|
43
47
|
/(?:\/[\w.-]+){2,}\/?/g, // POSIX-ish paths
|
|
44
|
-
/
|
|
48
|
+
/\b\d{1,3}(?:\.\d{1,3}){3}(?::\d+)?\b/g, // IPv4 (+ port)
|
|
49
|
+
/\b[\w-]+(?:\.[\w-]+)+\b/g, // hostnames and other dotted names
|
|
45
50
|
/\b[0-9a-f]{8,}\b/gi, // hex ids, hashes, uuids-without-dashes
|
|
46
51
|
/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, // uuids
|
|
47
52
|
/\d+/g, // any remaining number — ports, counts, timeouts
|
|
@@ -71,16 +76,67 @@ const VARIABLE_PARTS = [
|
|
|
71
76
|
* for a reason unrelated to this one.
|
|
72
77
|
*/
|
|
73
78
|
const MAX_SKELETON_INPUT = 1024;
|
|
74
|
-
/**
|
|
75
|
-
|
|
79
|
+
/** Variable parts blanked, whitespace collapsed, length bounded. The GROUPING basis; never sent. */
|
|
80
|
+
function redactedShape(message) {
|
|
76
81
|
let skeleton = message.slice(0, MAX_SKELETON_INPUT);
|
|
77
82
|
for (const pattern of VARIABLE_PARTS)
|
|
78
83
|
skeleton = skeleton.replace(pattern, '*');
|
|
79
84
|
return skeleton.replace(/\s+/g, ' ').trim().slice(0, 200);
|
|
80
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Sentences Reticle itself authors, and the fixed text sent for each. The OUTPUT is always the
|
|
88
|
+
* label, never the matched message, so text appended to one of ours cannot ride along.
|
|
89
|
+
*/
|
|
90
|
+
const RETICLE_MESSAGES = [
|
|
91
|
+
{ match: /^no browser session connected\b/i, label: 'no browser session connected' },
|
|
92
|
+
{ match: /^multiple sessions connected\b/i, label: 'multiple sessions connected' },
|
|
93
|
+
{ match: /^no connected session with id\b/i, label: 'no connected session with id *' },
|
|
94
|
+
{ match: /^no baseline named\b/i, label: 'no baseline named *' },
|
|
95
|
+
{ match: /^no (?:active|compiled) recording named\b/i, label: 'no recording named *' },
|
|
96
|
+
{ match: /no longer resolves to an element/i, label: 'ref * no longer resolves to an element' },
|
|
97
|
+
{ match: /^session disconnected$/i, label: 'session disconnected' },
|
|
98
|
+
{ match: /^command '[^']*' timed out after\b/i, label: 'command * timed out after *' },
|
|
99
|
+
{
|
|
100
|
+
match: /did not resolve in current page/i,
|
|
101
|
+
label: 'flow step * did not resolve in current page',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
match: /potentially destructive (?:\w+ )*(?:action|tool) blocked/i,
|
|
105
|
+
label: 'potentially destructive action blocked',
|
|
106
|
+
},
|
|
107
|
+
{ match: /^pairing token is required\b/i, label: 'pairing token is required' },
|
|
108
|
+
{ match: /no <option> with value/i, label: 'no <option> with value *' },
|
|
109
|
+
{ match: /cannot hover without a real pointer/i, label: 'cannot hover without a real pointer' },
|
|
110
|
+
{ match: /browser pool (?:unavailable|is shut down)/i, label: 'browser pool unavailable' },
|
|
111
|
+
{ match: /Chromium is not installed/i, label: 'Chromium is not installed' },
|
|
112
|
+
{ match: /^act failed$/i, label: 'act failed' },
|
|
113
|
+
];
|
|
114
|
+
/** A Node error code (`ENOENT`, `ERR_INVALID_ARG_TYPE`): an identifier Node defines, never data. */
|
|
115
|
+
const NODE_ERROR_CODE = /\b(?:E[A-Z]{2,}|ERR_[A-Z_]{2,})\b/;
|
|
116
|
+
/** The built-in error classes. A custom class name is the app's own word, so it is not kept. */
|
|
117
|
+
const BUILTIN_ERROR_CLASS = /^(?:Error|TypeError|RangeError|SyntaxError|ReferenceError|EvalError|URIError|AggregateError|AbortError|TimeoutError|DOMException|ZodError)\b/;
|
|
118
|
+
/**
|
|
119
|
+
* What an error message may say on the wire, and it is ALLOW-listed: a sentence Reticle authors
|
|
120
|
+
* (sent as our own fixed label), else the bare Node error code or built-in class plus the
|
|
121
|
+
* fingerprint, else the fingerprint alone.
|
|
122
|
+
*
|
|
123
|
+
* It used to be the message with known-variable parts blanked, which is a deny-list, and a
|
|
124
|
+
* deny-list loses: a hostname after `getaddrinfo ENOTFOUND`, a Windows home directory with a space
|
|
125
|
+
* in it, a `~` path and a person's name in free text all reached the wire. Nothing written by
|
|
126
|
+
* anybody else survives this function, because it never echoes its input.
|
|
127
|
+
*/
|
|
128
|
+
export function errorSkeleton(message) {
|
|
129
|
+
const head = message.slice(0, MAX_SKELETON_INPUT).trim();
|
|
130
|
+
for (const { match, label } of RETICLE_MESSAGES)
|
|
131
|
+
if (match.test(head))
|
|
132
|
+
return label;
|
|
133
|
+
const fingerprint = `#${fingerprintError(message)}`;
|
|
134
|
+
const code = BUILTIN_ERROR_CLASS.exec(head)?.[0] ?? NODE_ERROR_CODE.exec(head)?.[0];
|
|
135
|
+
return code === undefined ? fingerprint : `${code} ${fingerprint}`;
|
|
136
|
+
}
|
|
81
137
|
/** A stable, opaque group key for an error message. Same defect ⇒ same key, on every machine. */
|
|
82
138
|
export function fingerprintError(message) {
|
|
83
|
-
return createHash('sha256').update(
|
|
139
|
+
return createHash('sha256').update(redactedShape(message)).digest('hex').slice(0, 12);
|
|
84
140
|
}
|
|
85
141
|
/**
|
|
86
142
|
* Frames from a stack trace that belong to RETICLE, reduced to `basename:line`.
|
|
@@ -121,7 +177,7 @@ export function fingerprintCrash(errorType, stack, message) {
|
|
|
121
177
|
const frames = reticleFrames(stack);
|
|
122
178
|
const basis = frames.length > 0
|
|
123
179
|
? `${errorType}|${frames.join('|')}`
|
|
124
|
-
: `${errorType}|${
|
|
180
|
+
: `${errorType}|${redactedShape(message)}`;
|
|
125
181
|
return createHash('sha256').update(basis).digest('hex').slice(0, 12);
|
|
126
182
|
}
|
|
127
183
|
/** The error's constructor name (`TypeError`), or `Unknown` for a thrown non-Error. */
|
|
@@ -15,19 +15,6 @@ import { TelemetryAction } from '../command/cli/cli-parse.js';
|
|
|
15
15
|
* show you the payload is asking for trust it has not earned.
|
|
16
16
|
*/
|
|
17
17
|
export declare function handleFeedback(text: string, rating: number | undefined, bug: boolean, feedbackKind?: string, agent?: boolean): Promise<void>;
|
|
18
|
-
/**
|
|
19
|
-
* `reticle identify` — opt-in, and it shows the consent notice before it sends anything.
|
|
20
|
-
*
|
|
21
|
-
* Deliberately NOT interactive: a prompt that appears in the middle of someone's work is pressure,
|
|
22
|
-
* and this is the one command where the user must be choosing freely. They type what they want to
|
|
23
|
-
* share, or they never run it at all.
|
|
24
|
-
*/
|
|
25
|
-
export declare function handleIdentify(parsed: {
|
|
26
|
-
context?: string;
|
|
27
|
-
company?: string;
|
|
28
|
-
email?: string;
|
|
29
|
-
forget: boolean;
|
|
30
|
-
}): Promise<void>;
|
|
31
18
|
/**
|
|
32
19
|
* `reticle telemetry [status|enable|disable]` — the user-facing control for anonymous usage metrics.
|
|
33
20
|
* `disable` persists a machine-wide opt-out (survives shells, unlike RETICLE_TELEMETRY=0); `status`
|
|
@@ -3,7 +3,6 @@ import { AGENT_FLAG, BUG_FLAG, FEEDBACK_KINDS, KIND_FLAG, RATING_FLAG, } from '.
|
|
|
3
3
|
import { describeFeedbackPayload, submitFeedback } from './feedback.js';
|
|
4
4
|
import { describeTelemetry, setTelemetryEnabled } from './telemetry.js';
|
|
5
5
|
import { TelemetryAction } from '../command/cli/cli-parse.js';
|
|
6
|
-
import { clearIdentity, IDENTIFY_NOTICE, readIdentity, saveIdentity, submitIdentity, UsageContextKind, } from './identify.js';
|
|
7
6
|
/**
|
|
8
7
|
* `reticle feedback [--rating 1-5] [--bug] "what happened"` — the feedback channel as a command.
|
|
9
8
|
*
|
|
@@ -49,82 +48,6 @@ export async function handleFeedback(text, rating, bug, feedbackKind, agent = fa
|
|
|
49
48
|
line(receipt.sent
|
|
50
49
|
? 'thanks your feedback is in. it genuinely changes what gets built next.'
|
|
51
50
|
: `not sent ${receipt.reason ?? 'unknown reason'}`);
|
|
52
|
-
for (const extra of identifyInvite(agent))
|
|
53
|
-
line(extra);
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Offer a HUMAN a way to be replied to — printed after the receipt, never as a prompt.
|
|
57
|
-
*
|
|
58
|
-
* Feedback is anonymous by design and stays that way: the report has already been sent by the time
|
|
59
|
-
* this prints, so nothing here gates or delays it. This is an offer, not a question.
|
|
60
|
-
*
|
|
61
|
-
* It routes through `reticle identify`, which already does consented identity properly — it prints
|
|
62
|
-
* what it will send, states that identifying links this machine's prior anonymous history to that
|
|
63
|
-
* identity, and has `--forget`. Capturing an address inline on `feedback_submitted` instead would
|
|
64
|
-
* put PII on the anonymous event stream and break what `docs/telemetry.md` promises in public:
|
|
65
|
-
* "no domain sniffing, no email inference" and "no personal data is collected".
|
|
66
|
-
*
|
|
67
|
-
* Only for humans (an agent has no email), and only when nobody has identified yet — so somebody
|
|
68
|
-
* who declined is never asked twice.
|
|
69
|
-
*/
|
|
70
|
-
function identifyInvite(agent) {
|
|
71
|
-
if (agent)
|
|
72
|
-
return [];
|
|
73
|
-
try {
|
|
74
|
-
if (readIdentity() !== undefined)
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
return []; // unreadable identity file: say nothing rather than risk asking twice
|
|
79
|
-
}
|
|
80
|
-
return [
|
|
81
|
-
'',
|
|
82
|
-
'want a reply? `reticle identify --context <company|side_project|oss|learning> --email you@example.com`',
|
|
83
|
-
' optional, and it tells you exactly what it sends before sending it.',
|
|
84
|
-
];
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* `reticle identify` — opt-in, and it shows the consent notice before it sends anything.
|
|
88
|
-
*
|
|
89
|
-
* Deliberately NOT interactive: a prompt that appears in the middle of someone's work is pressure,
|
|
90
|
-
* and this is the one command where the user must be choosing freely. They type what they want to
|
|
91
|
-
* share, or they never run it at all.
|
|
92
|
-
*/
|
|
93
|
-
export async function handleIdentify(parsed) {
|
|
94
|
-
const line = (s) => {
|
|
95
|
-
process.stdout.write(`${s}\n`);
|
|
96
|
-
};
|
|
97
|
-
if (parsed.forget) {
|
|
98
|
-
clearIdentity();
|
|
99
|
-
line('forgotten the local identity file is deleted; nothing further is sent.');
|
|
100
|
-
line(' to have what was already sent removed, email support@reticlehq.com');
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const context = Object.values(UsageContextKind).find((k) => k === parsed.context);
|
|
104
|
-
if (context === undefined) {
|
|
105
|
-
const existing = readIdentity();
|
|
106
|
-
if (existing !== undefined)
|
|
107
|
-
line(`current ${JSON.stringify(existing)}`);
|
|
108
|
-
line(`usage: reticle identify --context <${Object.values(UsageContextKind).join('|')}>`);
|
|
109
|
-
line(' [--company "Acme"] [--email you@acme.com] [--forget]');
|
|
110
|
-
line('');
|
|
111
|
-
line(IDENTIFY_NOTICE);
|
|
112
|
-
process.exitCode = existing === undefined ? 1 : 0;
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const identity = {
|
|
116
|
-
context,
|
|
117
|
-
...(parsed.company !== undefined ? { company: parsed.company } : {}),
|
|
118
|
-
...(parsed.email !== undefined ? { email: parsed.email } : {}),
|
|
119
|
-
};
|
|
120
|
-
line(IDENTIFY_NOTICE);
|
|
121
|
-
line('');
|
|
122
|
-
line(`sending ${JSON.stringify(identity)}`);
|
|
123
|
-
saveIdentity(identity);
|
|
124
|
-
const sent = await submitIdentity(identity);
|
|
125
|
-
line(sent
|
|
126
|
-
? 'thanks saved and sent. we may reach out; `reticle identify --forget` undoes this.'
|
|
127
|
-
: 'not sent telemetry is disabled on this machine, so it was saved locally only.');
|
|
128
51
|
}
|
|
129
52
|
/**
|
|
130
53
|
* `reticle telemetry [status|enable|disable]` — the user-facing control for anonymous usage metrics.
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* disables it too, since it rides the same emitter.
|
|
23
23
|
*/
|
|
24
24
|
import { type Feedback } from '@reticlehq/core/telemetry';
|
|
25
|
+
import { type Telemetry } from './telemetry.js';
|
|
25
26
|
import { type SessionFacts } from './feedback-context.js';
|
|
26
27
|
/** Kills the feedback channel ONLY — adoption counters keep flowing. */
|
|
27
28
|
export declare const FEEDBACK_ENV = "RETICLE_FEEDBACK";
|
|
@@ -34,6 +35,12 @@ interface Redaction {
|
|
|
34
35
|
}
|
|
35
36
|
/** Strip credential/PII shapes and cap the length. Pure — the tests read like a spec of what leaks. */
|
|
36
37
|
export declare function redactFeedbackText(input: string, max: number): Redaction;
|
|
38
|
+
/**
|
|
39
|
+
* A trace reduced to Reticle's own words, in order: which tools were called and what verdict each
|
|
40
|
+
* gave. The raw trace is the call/response trail, and the responses carry the page's text and what
|
|
41
|
+
* was typed into it, which no redaction rule can tell from prose. So nothing else is kept.
|
|
42
|
+
*/
|
|
43
|
+
export declare function summarizeTrace(trace: string): string;
|
|
37
44
|
/** True when the feedback channel is switched off on its own (the emitter's switches apply as well). */
|
|
38
45
|
export declare function feedbackDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
39
46
|
/** What the author supplies; the environment context is detected, never asked for. */
|
|
@@ -89,6 +96,12 @@ export declare function submitFeedback(input: FeedbackInput, opts?: {
|
|
|
89
96
|
*/
|
|
90
97
|
background?: boolean;
|
|
91
98
|
}): Promise<FeedbackReceipt>;
|
|
99
|
+
/**
|
|
100
|
+
* Retry what the outbox holds: the receipt for an undelivered report promises "It will be retried",
|
|
101
|
+
* and until this ran on daemon start nothing ever read the outbox back. Bounded, sequential, and
|
|
102
|
+
* best-effort; a report the endpoint does not accept stays queued. Returns how many were delivered.
|
|
103
|
+
*/
|
|
104
|
+
export declare function drainFeedbackOutbox(telemetry?: Telemetry, home?: string): Promise<number>;
|
|
92
105
|
/**
|
|
93
106
|
* The receipt's context PLUS the fields the emitter stamps on every event regardless of kind — the
|
|
94
107
|
* reticle version, the OS, and whether this is CI. They are the single most-asked question of any bug
|
|
@@ -22,18 +22,77 @@
|
|
|
22
22
|
* disables it too, since it rides the same emitter.
|
|
23
23
|
*/
|
|
24
24
|
import { FeedbackSchema, FEEDBACK_TEXT_MAX, FEEDBACK_TRACE_MAX, FEEDBACK_FIELD_MAX, TelemetryEventKind, } from '@reticlehq/core/telemetry';
|
|
25
|
-
import { platform } from 'node:os';
|
|
25
|
+
import { homedir, platform } from 'node:os';
|
|
26
26
|
import { getTelemetry } from './telemetry.js';
|
|
27
27
|
import { noteFeedbackUndelivered } from './feedback-delivery.js';
|
|
28
28
|
import { isReticleSourceCheckout } from './dev-repo.js';
|
|
29
29
|
import { saveFeedbackLocally } from './feedback-local.js';
|
|
30
30
|
import { SERVER_VERSION } from '../command/version/identity/server-version.js';
|
|
31
31
|
import { feedbackContext } from './feedback-context.js';
|
|
32
|
-
import { markDelivered, outboxPath, queueFeedback } from './feedback-outbox.js';
|
|
32
|
+
import { markDelivered, outboxPath, queueFeedback, readOutbox } from './feedback-outbox.js';
|
|
33
33
|
/** Kills the feedback channel ONLY — adoption counters keep flowing. */
|
|
34
34
|
export const FEEDBACK_ENV = 'RETICLE_FEEDBACK';
|
|
35
35
|
/** What replaces a redacted match. Visible on purpose: a reader must see that something was removed. */
|
|
36
36
|
export const REDACTED = '[redacted]';
|
|
37
|
+
/** This machine, as a host: the app under test, never anybody's infrastructure. Port allowed. */
|
|
38
|
+
const LOCAL_HOST = /^(?:localhost|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|0\.0\.0\.0|\[::1\])(?::\d+)?$/i;
|
|
39
|
+
/** A dotted name ending in one of these is a source file, not a host. */
|
|
40
|
+
const FILE_EXTENSIONS = new Set([
|
|
41
|
+
'ts',
|
|
42
|
+
'tsx',
|
|
43
|
+
'mts',
|
|
44
|
+
'cts',
|
|
45
|
+
'js',
|
|
46
|
+
'jsx',
|
|
47
|
+
'mjs',
|
|
48
|
+
'cjs',
|
|
49
|
+
'json',
|
|
50
|
+
'md',
|
|
51
|
+
'mdx',
|
|
52
|
+
'css',
|
|
53
|
+
'scss',
|
|
54
|
+
'html',
|
|
55
|
+
'vue',
|
|
56
|
+
'svelte',
|
|
57
|
+
'astro',
|
|
58
|
+
'py',
|
|
59
|
+
'rs',
|
|
60
|
+
'go',
|
|
61
|
+
'yml',
|
|
62
|
+
'yaml',
|
|
63
|
+
'toml',
|
|
64
|
+
'lock',
|
|
65
|
+
'log',
|
|
66
|
+
'txt',
|
|
67
|
+
'map',
|
|
68
|
+
'sh',
|
|
69
|
+
'env',
|
|
70
|
+
'd',
|
|
71
|
+
]);
|
|
72
|
+
/** Top-level domains common enough that a one-dot name ending in one is a host. */
|
|
73
|
+
const COMMON_TLDS = new Set([
|
|
74
|
+
'com',
|
|
75
|
+
'net',
|
|
76
|
+
'org',
|
|
77
|
+
'io',
|
|
78
|
+
'dev',
|
|
79
|
+
'app',
|
|
80
|
+
'co',
|
|
81
|
+
'ai',
|
|
82
|
+
'cloud',
|
|
83
|
+
'internal',
|
|
84
|
+
'local',
|
|
85
|
+
'corp',
|
|
86
|
+
'lan',
|
|
87
|
+
'site',
|
|
88
|
+
'tech',
|
|
89
|
+
'xyz',
|
|
90
|
+
'us',
|
|
91
|
+
'uk',
|
|
92
|
+
'de',
|
|
93
|
+
'in',
|
|
94
|
+
'eu',
|
|
95
|
+
]);
|
|
37
96
|
/**
|
|
38
97
|
* The shapes that leak. Ordered most-specific first — a URL carrying inline credentials must be
|
|
39
98
|
* caught as a URL before the bare-token rule chews the middle out of it.
|
|
@@ -45,6 +104,15 @@ export const REDACTED = '[redacted]';
|
|
|
45
104
|
const REDACTIONS = [
|
|
46
105
|
// scheme://user:pass@host — the credential is in the authority section.
|
|
47
106
|
{ pattern: /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+:[^\s/@]+@/gi, label: 'url-credentials' },
|
|
107
|
+
// A URL keeps its scheme, and its host and path ONLY when the host is this machine (the app under
|
|
108
|
+
// test, which is what a report is about). Any other host is a staging site, a customer's domain
|
|
109
|
+
// or an internal service, so it goes with its path. The query and fragment always go: that is
|
|
110
|
+
// where ids, emails and tokens ride.
|
|
111
|
+
{
|
|
112
|
+
pattern: /\b([a-z][a-z0-9+.-]*:\/\/)([^\s/?#"'<>`]+)([^\s?#"'<>`]*)(?:[?#][^\s"'<>`]*)?/gi,
|
|
113
|
+
label: 'url',
|
|
114
|
+
replace: (_m, scheme = '', host = '', path = '') => LOCAL_HOST.test(host) ? `${scheme}${host}${path}` : `${scheme}${REDACTED}`,
|
|
115
|
+
},
|
|
48
116
|
{ pattern: /\b[\w.%+-]+@[\w.-]+\.[a-z]{2,}\b/gi, label: 'email' },
|
|
49
117
|
// Credential assignments: the KEY stays (it is the useful part), the VALUE goes. The auth SCHEME
|
|
50
118
|
// (`Bearer`, `Basic`, `Token`) is consumed as part of the value, not left as the value — matching
|
|
@@ -59,24 +127,72 @@ const REDACTIONS = [
|
|
|
59
127
|
{ pattern: /\bAKIA[0-9A-Z]{16}\b/g, label: 'aws-key' },
|
|
60
128
|
{ pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, label: 'slack-token' },
|
|
61
129
|
{ pattern: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, label: 'jwt' },
|
|
62
|
-
// Home directories carry the account name, and a trace is full of them.
|
|
63
|
-
|
|
130
|
+
// Home directories carry the account name, and a trace is full of them. The name runs to the next
|
|
131
|
+
// separator, quote or line end, NOT the next space: `C:\Users\Jane Doe\…` stopped at the space and
|
|
132
|
+
// sent the surname. Then the rest of the path goes with it. Over-redacting a sentence that follows
|
|
133
|
+
// a bare home directory is the price, and it is the right way round.
|
|
134
|
+
{
|
|
135
|
+
pattern: /(?:\\\\\?\\)?(?:\/Users\/|\/home\/|[A-Za-z]:[\\/]Users[\\/])[^\\/\r\n"'`]+(?:[\\/][^\s\\/"'`]*)*/g,
|
|
136
|
+
label: 'home-path',
|
|
137
|
+
},
|
|
138
|
+
// Any other absolute path: a Windows one (segments may hold spaces when a separator follows)...
|
|
139
|
+
{
|
|
140
|
+
pattern: /(?:\\\\\?\\)?\b[A-Za-z]:[\\/](?:[^\\/\r\n"'`<>|*?]+[\\/])*[^\s\\/"'`<>|*?]*/g,
|
|
141
|
+
label: 'path',
|
|
142
|
+
},
|
|
143
|
+
// ...a home-relative one, and a POSIX one of two or more segments. A path is a directory layout,
|
|
144
|
+
// and a directory layout names clients, projects and people.
|
|
145
|
+
{ pattern: /~[\\/][^\s"'`]*/g, label: 'path' },
|
|
146
|
+
{ pattern: /(?<![\w.~])\/[\w.@+-]+(?:\/[\w.@+-]+)+\/?/g, label: 'path' },
|
|
147
|
+
{
|
|
148
|
+
pattern: /\b\d{1,3}(?:\.\d{1,3}){3}\b/g,
|
|
149
|
+
label: 'ip',
|
|
150
|
+
replace: (ip) => (LOCAL_HOST.test(ip) ? ip : REDACTED),
|
|
151
|
+
},
|
|
152
|
+
// Hostnames: a staging site, an internal service, a customer's domain. A dotted name is a host when
|
|
153
|
+
// it has two or more dots or ends in a common top-level domain, and never when it ends in a source
|
|
154
|
+
// file's extension — `vite.config.ts` is what a report is about, `db.internal.acme.com` is not.
|
|
155
|
+
{
|
|
156
|
+
pattern: /\b(?:[a-z0-9-]+\.)+[a-z][a-z0-9-]*\b/gi,
|
|
157
|
+
label: 'hostname',
|
|
158
|
+
replace: (name) => {
|
|
159
|
+
const labels = name.toLowerCase().split('.');
|
|
160
|
+
const last = labels[labels.length - 1] ?? '';
|
|
161
|
+
if (FILE_EXTENSIONS.has(last))
|
|
162
|
+
return name;
|
|
163
|
+
return labels.length > 2 || COMMON_TLDS.has(last) ? REDACTED : name;
|
|
164
|
+
},
|
|
165
|
+
},
|
|
64
166
|
];
|
|
65
167
|
/** Strip credential/PII shapes and cap the length. Pure — the tests read like a spec of what leaks. */
|
|
66
168
|
export function redactFeedbackText(input, max) {
|
|
67
169
|
const removed = new Set();
|
|
68
170
|
let text = input;
|
|
69
|
-
for (const { pattern, label } of REDACTIONS) {
|
|
70
|
-
text = text.replace(pattern, (
|
|
71
|
-
|
|
171
|
+
for (const { pattern, label, replace } of REDACTIONS) {
|
|
172
|
+
text = text.replace(pattern, (match, ...groups) => {
|
|
173
|
+
const captures = groups.map((g) => ('string' === typeof g ? g : ''));
|
|
72
174
|
// A rule with a leading capture keeps it (the scheme, the `api_key=` key) so the reader still
|
|
73
175
|
// sees WHAT was removed, not just that something was.
|
|
74
|
-
const
|
|
75
|
-
|
|
176
|
+
const out = replace === undefined ? `${captures[0] ?? ''}${REDACTED}` : replace(match, ...captures);
|
|
177
|
+
if (out !== match)
|
|
178
|
+
removed.add(label);
|
|
179
|
+
return out;
|
|
76
180
|
});
|
|
77
181
|
}
|
|
78
182
|
return { text: text.slice(0, max), removed: [...removed] };
|
|
79
183
|
}
|
|
184
|
+
/** Our words in a trace: tool names and the verdict vocabulary. Nothing else is kept. */
|
|
185
|
+
const TRACE_WORDS = /\breticle_[a-z_]+\b|\b(?:verified|yes|no|unknown|no-fault|ok|passed|failed|refused|error|timeout)\b/gi;
|
|
186
|
+
/**
|
|
187
|
+
* A trace reduced to Reticle's own words, in order: which tools were called and what verdict each
|
|
188
|
+
* gave. The raw trace is the call/response trail, and the responses carry the page's text and what
|
|
189
|
+
* was typed into it, which no redaction rule can tell from prose. So nothing else is kept.
|
|
190
|
+
*/
|
|
191
|
+
export function summarizeTrace(trace) {
|
|
192
|
+
return (trace.match(TRACE_WORDS) ?? []).join(' ').slice(0, FEEDBACK_TRACE_MAX);
|
|
193
|
+
}
|
|
194
|
+
/** The removal label when a trace lost anything to `summarizeTrace`. */
|
|
195
|
+
const TRACE_VALUES_LABEL = 'trace-values';
|
|
80
196
|
/** True when the feedback channel is switched off on its own (the emitter's switches apply as well). */
|
|
81
197
|
export function feedbackDisabled(env = process.env) {
|
|
82
198
|
const value = (env[FEEDBACK_ENV] ?? '').toLowerCase();
|
|
@@ -98,6 +214,7 @@ const AUTHOR_WRITTEN_FIELDS = new Set([
|
|
|
98
214
|
'currentApproach',
|
|
99
215
|
'model',
|
|
100
216
|
]);
|
|
217
|
+
const TRACE_FIELD = 'trace';
|
|
101
218
|
/** Per-field caps; anything unlisted uses the standard field cap. */
|
|
102
219
|
const FIELD_CAPS = {
|
|
103
220
|
text: FEEDBACK_TEXT_MAX,
|
|
@@ -120,14 +237,7 @@ export async function submitFeedback(input, opts = {}) {
|
|
|
120
237
|
// sk-…" would have shipped the token. Enumerating the fields to protect is the same class of
|
|
121
238
|
// mistake as hand-copying an enum: correct on the day it is written, quietly wrong at the next
|
|
122
239
|
// addition. Deriving the set means a sixth field is covered the moment it exists.
|
|
123
|
-
const redactions =
|
|
124
|
-
for (const [key, value] of Object.entries(input)) {
|
|
125
|
-
if (typeof value !== 'string' || '' === value)
|
|
126
|
-
continue;
|
|
127
|
-
if (!AUTHOR_WRITTEN_FIELDS.has(key))
|
|
128
|
-
continue;
|
|
129
|
-
redactions.set(key, redactFeedbackText(value, FIELD_CAPS[key] ?? FEEDBACK_FIELD_MAX));
|
|
130
|
-
}
|
|
240
|
+
const redactions = redactAuthorFields(input);
|
|
131
241
|
const body = redactions.get('text') ?? { text: '', removed: [] };
|
|
132
242
|
const redacted = [...new Set([...redactions.values()].flatMap((r) => r.removed))];
|
|
133
243
|
if (feedbackDisabled(env)) {
|
|
@@ -231,6 +341,62 @@ export async function submitFeedback(input, opts = {}) {
|
|
|
231
341
|
context,
|
|
232
342
|
};
|
|
233
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* Every author-written field of a report, redacted under the CURRENT rules. One function for the
|
|
346
|
+
* send and for the outbox drain, so a report queued under older, weaker rules is re-redacted before
|
|
347
|
+
* it finally goes.
|
|
348
|
+
*/
|
|
349
|
+
function redactAuthorFields(input) {
|
|
350
|
+
const redactions = new Map();
|
|
351
|
+
for (const [key, value] of Object.entries(input)) {
|
|
352
|
+
if (typeof value !== 'string' || '' === value)
|
|
353
|
+
continue;
|
|
354
|
+
if (!AUTHOR_WRITTEN_FIELDS.has(key))
|
|
355
|
+
continue;
|
|
356
|
+
if (TRACE_FIELD === key) {
|
|
357
|
+
const summary = summarizeTrace(value);
|
|
358
|
+
redactions.set(key, {
|
|
359
|
+
text: summary,
|
|
360
|
+
removed: summary === value ? [] : [TRACE_VALUES_LABEL],
|
|
361
|
+
});
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
redactions.set(key, redactFeedbackText(value, FIELD_CAPS[key] ?? FEEDBACK_FIELD_MAX));
|
|
365
|
+
}
|
|
366
|
+
return redactions;
|
|
367
|
+
}
|
|
368
|
+
/** Reports one daemon start retries. A backlog drains over several starts, never in one burst. */
|
|
369
|
+
const OUTBOX_DRAIN_MAX = 5;
|
|
370
|
+
/**
|
|
371
|
+
* Retry what the outbox holds: the receipt for an undelivered report promises "It will be retried",
|
|
372
|
+
* and until this ran on daemon start nothing ever read the outbox back. Bounded, sequential, and
|
|
373
|
+
* best-effort; a report the endpoint does not accept stays queued. Returns how many were delivered.
|
|
374
|
+
*/
|
|
375
|
+
export async function drainFeedbackOutbox(telemetry = getTelemetry(), home = homedir()) {
|
|
376
|
+
if (!telemetry.enabled || feedbackDisabled())
|
|
377
|
+
return 0;
|
|
378
|
+
let delivered = 0;
|
|
379
|
+
for (const entry of readOutbox(home).slice(0, OUTBOX_DRAIN_MAX)) {
|
|
380
|
+
const queued = FeedbackSchema.safeParse(entry.payload);
|
|
381
|
+
// A payload that cannot pass the schema can never be sent; it stays where a person can read it.
|
|
382
|
+
if (!queued.success)
|
|
383
|
+
continue;
|
|
384
|
+
const fields = Object.fromEntries([...redactAuthorFields(queued.data)].map(([key, r]) => [key, r.text]));
|
|
385
|
+
const parsed = FeedbackSchema.safeParse({ ...queued.data, ...fields });
|
|
386
|
+
if (!parsed.success)
|
|
387
|
+
continue;
|
|
388
|
+
try {
|
|
389
|
+
if (await telemetry.emit(TelemetryEventKind.FEEDBACK_SUBMITTED, { feedback: parsed.data })) {
|
|
390
|
+
markDelivered(entry.id, home);
|
|
391
|
+
delivered += 1;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
/* stays queued for the next start */
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return delivered;
|
|
399
|
+
}
|
|
234
400
|
/**
|
|
235
401
|
* The receipt's context PLUS the fields the emitter stamps on every event regardless of kind — the
|
|
236
402
|
* reticle version, the OS, and whether this is CI. They are the single most-asked question of any bug
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HarnessRunSource, type HarnessRun } from '@reticlehq/core/telemetry';
|
|
2
|
+
import { DriveOrigin, DriveStatus } from '../features/harness/drive-runs.js';
|
|
3
|
+
import { type Telemetry } from './telemetry.js';
|
|
4
|
+
export interface HarnessRunStart {
|
|
5
|
+
source: HarnessRunSource;
|
|
6
|
+
hosted: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Who started it. A saved plan is a plan whoever pressed Run. */
|
|
9
|
+
export declare function harnessRunSource(origin: DriveOrigin, planId: string | undefined): HarnessRunSource;
|
|
10
|
+
/** How a finished run ended, from what the registry recorded. */
|
|
11
|
+
export declare function harnessRunEnd(status: DriveStatus, result: Record<string, unknown> | undefined): Pick<HarnessRun, 'outcome' | 'journeys' | 'replayed'>;
|
|
12
|
+
/** Report the run's start, run it, report its end, and hand back exactly what it did. */
|
|
13
|
+
export declare function reportHarnessRun<T extends {
|
|
14
|
+
status: DriveStatus;
|
|
15
|
+
result: Record<string, unknown>;
|
|
16
|
+
}>(start: HarnessRunStart, run: () => Promise<T>, refused: (error: unknown) => boolean, telemetry?: Pick<Telemetry, 'emit'>): Promise<T>;
|
|
17
|
+
/** A run the gate refused before it could start: still somebody who asked for one. */
|
|
18
|
+
export declare function reportHarnessBlocked(start: HarnessRunStart, telemetry?: Pick<Telemetry, 'emit'>): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `harness_run`: the Harness funnel. One `started` and one `completed` per run, whoever started it.
|
|
3
|
+
*
|
|
4
|
+
* Emitted around the drive itself (`startDrive`'s run), not read off the tool answer: a drive started
|
|
5
|
+
* from the HUD is never polled by an agent, and a drive whose daemon is asked again later answers
|
|
6
|
+
* from disk, so only the run's own start and end see every run exactly once.
|
|
7
|
+
*
|
|
8
|
+
* A drive the gate refuses before it starts is still a run somebody asked for, and reports as a
|
|
9
|
+
* `started` + `completed: blocked` pair, so the funnel's denominator is everyone who pressed Run.
|
|
10
|
+
*/
|
|
11
|
+
import { asRecord } from '@reticlehq/core';
|
|
12
|
+
import { JourneyBy } from '@reticlehq/core/artifacts';
|
|
13
|
+
import { HarnessRunOutcome, HarnessRunPhase, HarnessRunSource, TelemetryEventKind, countBucket, } from '@reticlehq/core/telemetry';
|
|
14
|
+
import { DriveOrigin, DriveStatus } from '../features/harness/drive-runs.js';
|
|
15
|
+
import { getTelemetry } from './telemetry.js';
|
|
16
|
+
const SOURCE_OF = {
|
|
17
|
+
[DriveOrigin.AGENT]: HarnessRunSource.AGENT,
|
|
18
|
+
[DriveOrigin.HUD]: HarnessRunSource.HUD,
|
|
19
|
+
[DriveOrigin.CHAT]: HarnessRunSource.DASHBOARD,
|
|
20
|
+
[DriveOrigin.TRY]: HarnessRunSource.CLI_TRY,
|
|
21
|
+
};
|
|
22
|
+
/** Who started it. A saved plan is a plan whoever pressed Run. */
|
|
23
|
+
export function harnessRunSource(origin, planId) {
|
|
24
|
+
return planId === undefined ? SOURCE_OF[origin] : HarnessRunSource.PLAN;
|
|
25
|
+
}
|
|
26
|
+
/** How a finished run ended, from what the registry recorded. */
|
|
27
|
+
export function harnessRunEnd(status, result) {
|
|
28
|
+
const journeys = Array.isArray(result?.['journeys']) ? result['journeys'] : undefined;
|
|
29
|
+
const replayed = (journeys ?? []).filter((j) => JourneyBy.REPLAY === asRecord(j)['by']).length;
|
|
30
|
+
const outcome = DriveStatus.REFUSED === status
|
|
31
|
+
? HarnessRunOutcome.BLOCKED
|
|
32
|
+
: DriveStatus.BROKEN === status
|
|
33
|
+
? HarnessRunOutcome.FAILED
|
|
34
|
+
: DriveStatus.STOPPED === status
|
|
35
|
+
? HarnessRunOutcome.STOPPED
|
|
36
|
+
: true === result?.['proved']
|
|
37
|
+
? HarnessRunOutcome.PROVED
|
|
38
|
+
: HarnessRunOutcome.NOT_PROVED;
|
|
39
|
+
// An unplanned drive is one journey; a refused one drove none.
|
|
40
|
+
const count = journeys?.length ?? (HarnessRunOutcome.BLOCKED === outcome ? 0 : 1);
|
|
41
|
+
return { outcome, journeys: countBucket(count), replayed: countBucket(replayed) };
|
|
42
|
+
}
|
|
43
|
+
function emit(telemetry, harness) {
|
|
44
|
+
try {
|
|
45
|
+
void telemetry.emit(TelemetryEventKind.HARNESS_RUN, { harness });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
/* a metric may never change a drive */
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Report the run's start, run it, report its end, and hand back exactly what it did. */
|
|
52
|
+
export async function reportHarnessRun(start, run, refused, telemetry = getTelemetry()) {
|
|
53
|
+
emit(telemetry, { phase: HarnessRunPhase.STARTED, ...start });
|
|
54
|
+
try {
|
|
55
|
+
const done = await run();
|
|
56
|
+
emit(telemetry, {
|
|
57
|
+
phase: HarnessRunPhase.COMPLETED,
|
|
58
|
+
...start,
|
|
59
|
+
...harnessRunEnd(done.status, done.result),
|
|
60
|
+
});
|
|
61
|
+
return done;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
emit(telemetry, {
|
|
65
|
+
phase: HarnessRunPhase.COMPLETED,
|
|
66
|
+
...start,
|
|
67
|
+
...harnessRunEnd(refused(error) ? DriveStatus.REFUSED : DriveStatus.BROKEN, undefined),
|
|
68
|
+
});
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** A run the gate refused before it could start: still somebody who asked for one. */
|
|
73
|
+
export function reportHarnessBlocked(start, telemetry = getTelemetry()) {
|
|
74
|
+
emit(telemetry, { phase: HarnessRunPhase.STARTED, ...start });
|
|
75
|
+
emit(telemetry, {
|
|
76
|
+
phase: HarnessRunPhase.COMPLETED,
|
|
77
|
+
...start,
|
|
78
|
+
...harnessRunEnd(DriveStatus.REFUSED, undefined),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HUD's own events: `hud_viewed` when a page is opened, `hud_signin_prompted` when a sign-in or
|
|
3
|
+
* link ask is shown. The windowed summary (`hudPanelMs`, `hudJourney`) says how long and in what
|
|
4
|
+
* order; these say how often and by whom, which a window summed across a session cannot.
|
|
5
|
+
*
|
|
6
|
+
* Sent by the daemon, like every HUD measure, so every opt-out applies, and only from the names the
|
|
7
|
+
* wire schema already narrowed. The dedupe (per tab, per page or surface) lives in `HudMetrics`.
|
|
8
|
+
*/
|
|
9
|
+
import type { HudUseData } from '@reticlehq/core';
|
|
10
|
+
import { type Telemetry } from './telemetry.js';
|
|
11
|
+
/** Count one HUD use from one tab, and send the events it amounts to. */
|
|
12
|
+
export declare function reportHudUse(use: HudUseData, tab: string, telemetry?: Pick<Telemetry, 'emit'>): void;
|
|
13
|
+
/** The tab went into the background or came back: its HUD time pauses while it is hidden. */
|
|
14
|
+
export declare function noteHudTabHidden(tab: string, hidden: boolean): void;
|
|
15
|
+
/** The tab went away. */
|
|
16
|
+
export declare function noteHudTabClosed(tab: string): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { TelemetryEventKind, countBucket } from '@reticlehq/core/telemetry';
|
|
2
|
+
import { getSessionMetrics } from './session-metrics.js';
|
|
3
|
+
import { getTelemetry } from './telemetry.js';
|
|
4
|
+
/** Count one HUD use from one tab, and send the events it amounts to. */
|
|
5
|
+
export function reportHudUse(use, tab, telemetry = getTelemetry()) {
|
|
6
|
+
const signals = getSessionMetrics().recordHudUse(use, tab);
|
|
7
|
+
const tabs = countBucket(signals.tabs);
|
|
8
|
+
try {
|
|
9
|
+
if (signals.viewed !== undefined)
|
|
10
|
+
void telemetry.emit(TelemetryEventKind.HUD_VIEWED, { hud: { view: signals.viewed, tabs } });
|
|
11
|
+
if (signals.prompted !== undefined)
|
|
12
|
+
void telemetry.emit(TelemetryEventKind.HUD_SIGNIN_PROMPTED, {
|
|
13
|
+
hud: { surface: signals.prompted, tabs },
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
/* a metric may never change behaviour */
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** The tab went into the background or came back: its HUD time pauses while it is hidden. */
|
|
21
|
+
export function noteHudTabHidden(tab, hidden) {
|
|
22
|
+
getSessionMetrics().setHudTabHidden(tab, hidden);
|
|
23
|
+
}
|
|
24
|
+
/** The tab went away. */
|
|
25
|
+
export function noteHudTabClosed(tab) {
|
|
26
|
+
getSessionMetrics().closeHudTab(tab);
|
|
27
|
+
}
|