@reticlehq/server 3.7.0 → 3.8.1
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 +59 -68
- package/bin/reticle.js +9 -2
- package/dist/command/cli/adhoc-suite.js +1 -1
- package/dist/command/cli/adhoc-verdict.d.ts +12 -0
- package/dist/command/cli/adhoc-verdict.js +62 -4
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +5 -1
- package/dist/command/cli/auth/cloud-session.d.ts +11 -19
- package/dist/command/cli/auth/cloud-session.js +23 -0
- package/dist/command/cli/auth/device-flow.d.ts +3 -37
- package/dist/command/cli/cli-doctor.js +66 -21
- package/dist/command/cli/cli-flow-commands.js +53 -11
- package/dist/command/cli/cli-kill.d.ts +8 -0
- package/dist/command/cli/cli-kill.js +43 -3
- package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
- package/dist/command/cli/cli-parse-feedback.js +13 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
- package/dist/command/cli/cli-parse-grammar.js +1 -6
- package/dist/command/cli/cli-parse-serve.d.ts +24 -0
- package/dist/command/cli/cli-parse-serve.js +124 -0
- package/dist/command/cli/cli-parse.d.ts +12 -11
- package/dist/command/cli/cli-parse.js +100 -123
- package/dist/command/cli/cli-usage.d.ts +0 -11
- package/dist/command/cli/cli-usage.js +43 -16
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +29 -14
- package/dist/command/cli/cloud-cli.js +27 -9
- package/dist/command/cli/cloud-kit.js +3 -3
- package/dist/command/cli/cloud-link.d.ts +1 -20
- package/dist/command/cli/cloud-link.js +2 -0
- package/dist/command/cli/cloud-login.d.ts +6 -2
- package/dist/command/cli/cloud-login.js +13 -41
- package/dist/command/cli/config/checkout.d.ts +10 -0
- package/dist/command/cli/config/checkout.js +78 -0
- package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/drive/drive-attach.d.ts +5 -0
- package/dist/command/cli/drive/drive-attach.js +21 -1
- package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
- package/dist/command/cli/hook/codex-transcript.js +224 -0
- package/dist/command/cli/hook/episode-structure.d.ts +24 -0
- package/dist/command/cli/hook/episode-structure.js +138 -0
- package/dist/command/cli/hook/hook-command.d.ts +9 -0
- package/dist/command/cli/hook/hook-command.js +211 -0
- package/dist/command/cli/hook/hook-fast.d.ts +31 -0
- package/dist/command/cli/hook/hook-fast.js +136 -0
- package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
- package/dist/command/cli/hook/prompt-hook.js +199 -0
- package/dist/command/cli/hook/tool-hook.d.ts +21 -0
- package/dist/command/cli/hook/tool-hook.js +259 -0
- package/dist/command/cli/launch/cli-launch.d.ts +2 -0
- package/dist/command/cli/launch/cli-launch.js +22 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
- package/dist/command/cli/mcp-command.d.ts +9 -0
- package/dist/command/cli/mcp-command.js +127 -41
- package/dist/command/cli/open-navigate.d.ts +43 -0
- package/dist/command/cli/open-navigate.js +85 -0
- package/dist/command/cli/ports/port-holder.d.ts +16 -4
- package/dist/command/cli/ports/port-holder.js +58 -12
- package/dist/command/cli/ports/resolve/cli-port.js +18 -2
- package/dist/command/cli/project-name.d.ts +1 -7
- package/dist/command/cli/project-name.js +1 -0
- package/dist/command/cli/report-command.d.ts +8 -2
- package/dist/command/cli/report-command.js +95 -18
- package/dist/command/cli/status-fields.d.ts +9 -2
- package/dist/command/cli/status-fields.js +148 -4
- package/dist/command/cli/try-command.d.ts +3 -4
- package/dist/command/cli/try-command.js +6 -11
- package/dist/command/cli/verify-results.d.ts +4 -1
- package/dist/command/cli/verify-results.js +3 -1
- package/dist/command/cli.d.ts +1 -7
- package/dist/command/cli.js +79 -160
- package/dist/command/daemon/daemon-resilience.d.ts +6 -1
- package/dist/command/daemon/daemon-resilience.js +31 -18
- package/dist/command/daemon/daemon.d.ts +11 -0
- package/dist/command/daemon/daemon.js +70 -2
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
- package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
- package/dist/command/daemon/wake-decision.js +4 -2
- package/dist/command/daemon/win32-escape.d.ts +32 -0
- package/dist/command/daemon/win32-escape.js +119 -0
- package/dist/command/setup/agent-configs.d.ts +9 -0
- package/dist/command/setup/agent-configs.js +9 -0
- package/dist/command/setup/auto-approve.d.ts +10 -3
- package/dist/command/setup/auto-approve.js +51 -3
- package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
- package/dist/command/setup/bringup/dev-server-wait.js +5 -1
- package/dist/command/setup/bringup/ensure-daemon.js +4 -7
- package/dist/command/setup/init/init-host.js +6 -0
- package/dist/command/setup/init/init-port.js +5 -4
- package/dist/command/setup/init/init-runtime.js +7 -1
- package/dist/command/setup/node-effects.d.ts +8 -0
- package/dist/command/setup/node-effects.js +36 -4
- package/dist/command/setup/probe/dev-port.d.ts +26 -0
- package/dist/command/setup/probe/dev-port.js +117 -0
- package/dist/command/setup/probe/page-probe.d.ts +1 -1
- package/dist/command/setup/probe/page-probe.js +11 -3
- package/dist/command/setup/remaining-steps.d.ts +0 -7
- package/dist/command/setup/remaining-steps.js +3 -2
- package/dist/command/setup/run-setup.d.ts +11 -0
- package/dist/command/setup/run-setup.js +33 -5
- package/dist/command/setup/setup-command.d.ts +2 -0
- package/dist/command/setup/setup-command.js +22 -4
- package/dist/command/update/updater.d.ts +1 -6
- package/dist/command/update/updater.js +4 -5
- package/dist/command/update/version-seen.d.ts +24 -0
- package/dist/command/update/version-seen.js +65 -0
- package/dist/command/version/peer-announce.d.ts +9 -1
- package/dist/command/version/peer-announce.js +25 -2
- package/dist/command/version/sdk-fix.d.ts +5 -6
- package/dist/command/version/sdk-fix.js +17 -32
- package/dist/command/version/version-skew.d.ts +16 -1
- package/dist/command/version/version-skew.js +84 -10
- package/dist/features/crawl/crawl-tools.js +2 -2
- package/dist/features/crawl/crawl.js +2 -0
- package/dist/features/exhaust/ledger.d.ts +15 -96
- package/dist/features/exhaust/ledger.js +3 -3
- package/dist/features/harness/drive-runs.d.ts +28 -4
- package/dist/features/harness/drive-runs.js +99 -7
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/harness.d.ts +5 -0
- package/dist/features/harness/harness.js +24 -12
- package/dist/features/harness/platform/platform-drives.d.ts +7 -1
- package/dist/features/harness/platform/platform-drives.js +12 -7
- package/dist/features/harness/platform/refusal.d.ts +1 -1
- package/dist/features/harness/platform/refusal.js +4 -4
- package/dist/features/harness/platform/remote-drive.js +70 -28
- package/dist/features/harness/platform/server-driver.js +22 -1
- package/dist/features/harness/platform-config.d.ts +1 -10
- package/dist/features/harness/platform-config.js +0 -20
- package/dist/features/license/license.d.ts +2 -14
- package/dist/features/visual/step-shots.d.ts +33 -1
- package/dist/features/visual/step-shots.js +155 -1
- package/dist/features/visual/visual-tools.js +12 -4
- package/dist/hooks/coding-agents.d.ts +6 -0
- package/dist/hooks/coding-agents.js +24 -3
- package/dist/hooks/episode-stamp.d.ts +32 -0
- package/dist/hooks/episode-stamp.js +61 -0
- package/dist/hooks/hook-commands.js +19 -9
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -24
- package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
- package/dist/judgement/app-graph/journal-steps.js +92 -0
- package/dist/judgement/capsule/capsule-store.d.ts +2 -39
- package/dist/judgement/domain/domain-model.js +1 -10
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
- package/dist/judgement/runs/artifact/run-store.js +153 -28
- package/dist/judgement/runs/default-run-id.d.ts +3 -1
- package/dist/judgement/runs/default-run-id.js +15 -3
- package/dist/judgement/runs/drive-run.d.ts +3 -3
- package/dist/judgement/runs/drive-run.js +11 -7
- package/dist/judgement/runs/run-ledger.d.ts +21 -0
- package/dist/judgement/runs/run-ledger.js +143 -0
- package/dist/judgement/runs/runner-port.d.ts +1 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +4 -3
- package/dist/judgement/runs/verification-sync.js +27 -5
- package/dist/language/flows/affected-tools.js +2 -1
- package/dist/language/flows/annotate-notes/annotate.js +8 -3
- package/dist/language/flows/annotate-tools.js +1 -1
- package/dist/language/flows/change/affected.d.ts +8 -0
- package/dist/language/flows/change/affected.js +20 -7
- package/dist/language/flows/decision.js +19 -4
- package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
- package/dist/language/flows/fields/flow-secret-field.js +67 -5
- package/dist/language/flows/flow-anchor.d.ts +5 -0
- package/dist/language/flows/flow-anchor.js +4 -0
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +9 -0
- package/dist/language/flows/flow-expect-grammar.js +15 -10
- package/dist/language/flows/flow-intent.js +6 -1
- package/dist/language/flows/flow-journey.d.ts +8 -0
- package/dist/language/flows/flow-journey.js +13 -0
- package/dist/language/flows/flow-lease.d.ts +27 -0
- package/dist/language/flows/flow-lease.js +59 -0
- package/dist/language/flows/flow-replay-run.d.ts +1 -87
- package/dist/language/flows/flow-replay-run.js +111 -310
- package/dist/language/flows/flow-replay.js +21 -19
- package/dist/language/flows/flow-replayable.d.ts +38 -0
- package/dist/language/flows/flow-replayable.js +179 -0
- package/dist/language/flows/flow-start-path.d.ts +108 -0
- package/dist/language/flows/flow-start-path.js +313 -0
- package/dist/language/flows/flow-step-runners.d.ts +23 -2
- package/dist/language/flows/flow-step-runners.js +191 -32
- package/dist/language/flows/flow-store-for-session.d.ts +8 -1
- package/dist/language/flows/flow-store-for-session.js +6 -0
- package/dist/language/flows/flow-tools.d.ts +2 -23
- package/dist/language/flows/flow-tools.js +63 -113
- package/dist/language/flows/flows.d.ts +4 -6
- package/dist/language/flows/flows.js +90 -15
- package/dist/language/flows/heal-run.d.ts +10 -4
- package/dist/language/flows/heal-run.js +26 -9
- package/dist/language/flows/recording/flake.d.ts +3 -27
- package/dist/language/flows/recording/flake.js +1 -1
- package/dist/language/flows/recording/recording-backtrack.js +1 -4
- package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
- package/dist/language/flows/recording/tape/recordings.js +9 -3
- package/dist/language/flows/replay.d.ts +25 -5
- package/dist/language/flows/replay.js +83 -16
- package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
- package/dist/language/flows/suite/coverage.d.ts +9 -0
- package/dist/language/flows/suite/coverage.js +12 -1
- package/dist/language/flows/suite-selection.d.ts +25 -0
- package/dist/language/flows/suite-selection.js +41 -9
- package/dist/machine/git-tracks.d.ts +6 -0
- package/dist/machine/git-tracks.js +36 -0
- package/dist/memory/cloud/background-sync.d.ts +25 -0
- package/dist/memory/cloud/background-sync.js +79 -0
- package/dist/memory/cloud/cloud-config.d.ts +12 -1
- package/dist/memory/cloud/cloud-config.js +39 -13
- package/dist/memory/cloud/cloud-sync.d.ts +3 -24
- package/dist/memory/cloud/cloud-sync.js +5 -2
- package/dist/memory/cloud/harness-config.d.ts +0 -7
- package/dist/memory/cloud/memory-scope.d.ts +19 -0
- package/dist/memory/cloud/memory-scope.js +48 -0
- package/dist/memory/cloud/sync-answer.d.ts +43 -0
- package/dist/memory/cloud/sync-answer.js +42 -0
- package/dist/memory/cloud/sync-cycle.d.ts +40 -10
- package/dist/memory/cloud/sync-cycle.js +97 -144
- package/dist/memory/cloud/sync-daemon.d.ts +50 -3
- package/dist/memory/cloud/sync-daemon.js +473 -83
- package/dist/memory/cloud/sync-describe.d.ts +28 -0
- package/dist/memory/cloud/sync-describe.js +92 -0
- package/dist/memory/cloud/sync-disk.d.ts +9 -1
- package/dist/memory/cloud/sync-disk.js +137 -33
- package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
- package/dist/memory/cloud/sync-flow-versions.js +42 -0
- package/dist/memory/impact/impact-store.js +32 -15
- package/dist/memory/intent/intent-shard.d.ts +41 -266
- package/dist/memory/intent/intent-tools.js +20 -61
- package/dist/memory/intent/record-request.d.ts +27 -0
- package/dist/memory/intent/record-request.js +88 -0
- package/dist/memory/intent/settled-words.js +9 -1
- package/dist/memory/journal/drive-flow.d.ts +3 -0
- package/dist/memory/journal/drive-flow.js +11 -41
- package/dist/memory/journal/envelope-store.js +1 -1
- package/dist/memory/journal/envelope.d.ts +6 -154
- package/dist/memory/journal/episode-log.d.ts +107 -0
- package/dist/memory/journal/episode-log.js +302 -0
- package/dist/memory/journal/journal-recorder.d.ts +7 -0
- package/dist/memory/journal/journal-recorder.js +22 -9
- package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
- package/dist/memory/journal/on-disk/ambient-file.js +1 -1
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
- package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
- package/dist/memory/journal/self-observation.js +3 -1
- package/dist/memory/journal/session-end.js +23 -9
- package/dist/memory/journal/session-journal.js +39 -20
- package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
- package/dist/memory/project/artifact-root-resolver.js +68 -23
- package/dist/memory/project/artifact-root.d.ts +17 -3
- package/dist/memory/project/artifact-root.js +47 -9
- package/dist/memory/project/fs/fs-port.d.ts +5 -0
- package/dist/memory/project/fs/fs-port.js +13 -0
- package/dist/memory/project/learned-routes.d.ts +3 -4
- package/dist/memory/project/learned-routes.js +3 -11
- package/dist/memory/project/link-directory.js +5 -2
- package/dist/memory/project/root-claims.d.ts +3 -1
- package/dist/memory/project/root-claims.js +41 -7
- package/dist/memory/project/session-root.d.ts +1 -1
- package/dist/memory/project/session-root.js +1 -15
- package/dist/memory/project/sync-status.js +5 -6
- package/dist/memory/recall/memory-tools.js +2 -2
- package/dist/memory/recall/platform-memory.d.ts +3 -1
- package/dist/memory/recall/platform-memory.js +9 -1
- package/dist/memory/recall/project-memory.js +15 -19
- package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
- package/dist/portal/bridge/auth-failure-reason.js +19 -0
- package/dist/portal/bridge/bridge.d.ts +19 -4
- package/dist/portal/bridge/bridge.js +150 -177
- package/dist/portal/bridge/harness-request.d.ts +15 -0
- package/dist/portal/bridge/harness-request.js +19 -0
- package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
- package/dist/portal/bridge/origin/origin-follow.js +110 -0
- package/dist/portal/bridge/pairing-token.d.ts +6 -0
- package/dist/portal/bridge/pairing-token.js +47 -16
- package/dist/portal/bridge/protocol-skew.d.ts +27 -2
- package/dist/portal/bridge/protocol-skew.js +66 -2
- package/dist/portal/bridge/session-nonces.d.ts +46 -0
- package/dist/portal/bridge/session-nonces.js +72 -0
- package/dist/portal/input/viewport-tools.d.ts +31 -0
- package/dist/portal/input/viewport-tools.js +106 -15
- package/dist/portal/pool/bounded-close.d.ts +23 -0
- package/dist/portal/pool/bounded-close.js +38 -0
- package/dist/portal/pool/browser-pool.d.ts +44 -39
- package/dist/portal/pool/browser-pool.js +247 -95
- package/dist/portal/pool/context-permissions.d.ts +9 -1
- package/dist/portal/pool/context-permissions.js +26 -0
- package/dist/portal/pool/lease-emulation.d.ts +37 -0
- package/dist/portal/pool/lease-emulation.js +29 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +74 -7
- package/dist/portal/pool/pool-contract.d.ts +23 -1
- package/dist/portal/pool/pool-lease.d.ts +128 -0
- package/dist/portal/pool/pool-lease.js +49 -0
- package/dist/portal/pool/zero-install.d.ts +8 -1
- package/dist/portal/pool/zero-install.js +24 -2
- package/dist/portal/production/production-activation.d.ts +136 -0
- package/dist/portal/production/production-activation.js +173 -0
- package/dist/portal/production/production-drive.d.ts +52 -0
- package/dist/portal/production/production-drive.js +301 -0
- package/dist/portal/production/production-hello.d.ts +23 -0
- package/dist/portal/production/production-hello.js +23 -0
- package/dist/portal/realm/web-realm.js +42 -15
- package/dist/portal/session/control-envelope.d.ts +1 -1
- package/dist/portal/session/dev-server/served-document.d.ts +0 -5
- package/dist/portal/session/dev-server/served-document.js +6 -8
- package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
- package/dist/portal/session/first-run-wiring.d.ts +3 -36
- package/dist/portal/session/first-run-wiring.js +10 -103
- package/dist/portal/session/live-control-tools.js +61 -19
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +40 -15
- package/dist/portal/session/no-session-watch.d.ts +7 -0
- package/dist/portal/session/no-session-watch.js +67 -23
- package/dist/portal/session/output-budget.d.ts +10 -0
- package/dist/portal/session/output-budget.js +16 -0
- package/dist/portal/session/presence/session-lease.d.ts +2 -0
- package/dist/portal/session/presence/session-lease.js +8 -0
- package/dist/portal/session/recorded-gaps.d.ts +15 -11
- package/dist/portal/session/recorded-gaps.js +39 -18
- package/dist/portal/session/serving-directory.d.ts +24 -2
- package/dist/portal/session/serving-directory.js +93 -5
- package/dist/portal/session/session-info.d.ts +26 -0
- package/dist/portal/session/session-info.js +20 -0
- package/dist/portal/session/session-manager.d.ts +35 -3
- package/dist/portal/session/session-manager.js +69 -14
- package/dist/portal/session/session-successor.js +1 -9
- package/dist/portal/session/session-tools.js +22 -4
- package/dist/portal/session/session.d.ts +10 -10
- package/dist/portal/session/session.js +37 -29
- package/dist/portal/session/zero-install-first-call.d.ts +7 -2
- package/dist/portal/session/zero-install-first-call.js +43 -20
- package/dist/start-wiring.d.ts +2 -2
- package/dist/start-wiring.js +17 -3
- package/dist/status-payload.d.ts +11 -1
- package/dist/status-payload.js +7 -1
- package/dist/surface/agent-presence-grace.d.ts +11 -0
- package/dist/surface/agent-presence-grace.js +34 -0
- package/dist/surface/http-server.d.ts +12 -1
- package/dist/surface/http-server.js +64 -15
- package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
- package/dist/surface/mcp/faults/mcp-outage.js +19 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
- package/dist/surface/mcp/mcp-proxy.js +194 -267
- package/dist/surface/mcp/mcp.d.ts +20 -3
- package/dist/surface/mcp/mcp.js +88 -25
- package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
- package/dist/surface/mcp/proxy/drive-calls.js +114 -5
- package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
- package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
- package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
- package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
- package/dist/surface/mcp/server-instructions.js +35 -16
- package/dist/surface/mcp/shared-params.d.ts +8 -0
- package/dist/surface/mcp/shared-params.js +15 -1
- package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
- package/dist/surface/mcp/sse-frame-parser.js +18 -0
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +13 -0
- package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
- package/dist/surface/mcp/tool-catalog-cache.js +14 -4
- package/dist/surface/tools/account-tool.d.ts +4 -3
- package/dist/surface/tools/account-tool.js +20 -31
- package/dist/surface/tools/act/act-danger.d.ts +2 -0
- package/dist/surface/tools/act/act-danger.js +41 -14
- package/dist/surface/tools/act/act-observation.d.ts +11 -0
- package/dist/surface/tools/act/act-observation.js +33 -0
- package/dist/surface/tools/act/act-preflight.js +10 -3
- package/dist/surface/tools/act/act-sequence-retry.js +2 -1
- package/dist/surface/tools/act/act-target.js +19 -5
- package/dist/surface/tools/act/durable.js +1 -1
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
- package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
- package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
- package/dist/surface/tools/act/unsettled-find.js +71 -0
- package/dist/surface/tools/act-sequence-tool.js +53 -24
- package/dist/surface/tools/act-tools.d.ts +2 -3
- package/dist/surface/tools/act-tools.js +49 -47
- package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
- package/dist/surface/tools/agent/for-the-agent.js +41 -3
- package/dist/surface/tools/assert/assert-grade.js +8 -3
- package/dist/surface/tools/assert/assert-verdict.js +4 -2
- package/dist/surface/tools/browser-tools.js +7 -4
- package/dist/surface/tools/chat-drives.js +50 -25
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.d.ts +8 -0
- package/dist/surface/tools/dynamic-tools.js +89 -34
- package/dist/surface/tools/empty-session-list.d.ts +18 -25
- package/dist/surface/tools/empty-session-list.js +67 -38
- package/dist/surface/tools/error-recovery.d.ts +17 -7
- package/dist/surface/tools/error-recovery.js +53 -20
- package/dist/surface/tools/explore-tools.d.ts +10 -0
- package/dist/surface/tools/explore-tools.js +72 -19
- package/dist/surface/tools/feedback-tools.js +23 -2
- package/dist/surface/tools/gap-novelty.d.ts +6 -0
- package/dist/surface/tools/gap-novelty.js +9 -0
- package/dist/surface/tools/gaps/capability-absences.js +4 -3
- package/dist/surface/tools/harness-explore.d.ts +1 -1
- package/dist/surface/tools/harness-explore.js +6 -4
- package/dist/surface/tools/harness-gate.d.ts +43 -21
- package/dist/surface/tools/harness-gate.js +55 -81
- package/dist/surface/tools/harness-script.d.ts +2 -0
- package/dist/surface/tools/harness-script.js +11 -6
- package/dist/surface/tools/harness-toolset.js +3 -4
- package/dist/surface/tools/invoke-tool.d.ts +0 -8
- package/dist/surface/tools/invoke-tool.js +164 -178
- package/dist/surface/tools/lease-hint.d.ts +9 -0
- package/dist/surface/tools/lease-hint.js +22 -3
- package/dist/surface/tools/lease-permissions.d.ts +1 -1
- package/dist/surface/tools/lease-readiness.d.ts +55 -0
- package/dist/surface/tools/lease-readiness.js +150 -2
- package/dist/surface/tools/lease-tools.d.ts +3 -75
- package/dist/surface/tools/lease-tools.js +124 -188
- package/dist/surface/tools/map-tools.d.ts +20 -0
- package/dist/surface/tools/map-tools.js +145 -0
- package/dist/surface/tools/merge-tools.js +11 -7
- package/dist/surface/tools/next-step.js +6 -3
- package/dist/surface/tools/observe-tools.js +46 -35
- package/dist/surface/tools/platform-moment.d.ts +3 -12
- package/dist/surface/tools/platform-moment.js +12 -24
- package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
- package/dist/surface/tools/read/complete-snapshot.js +13 -2
- package/dist/surface/tools/read/query-paginate.js +6 -0
- package/dist/surface/tools/read/query-shape.d.ts +9 -0
- package/dist/surface/tools/read/query-shape.js +15 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
- package/dist/surface/tools/read-tools.js +5 -5
- package/dist/surface/tools/real-input-attempt.d.ts +11 -0
- package/dist/surface/tools/real-input-attempt.js +206 -38
- package/dist/surface/tools/reconcile-tools.js +21 -8
- package/dist/surface/tools/reticle-result.d.ts +3 -0
- package/dist/surface/tools/reticle-result.js +8 -1
- package/dist/surface/tools/run-report.d.ts +22 -0
- package/dist/surface/tools/run-report.js +141 -0
- package/dist/surface/tools/session-ports.d.ts +57 -0
- package/dist/surface/tools/session-ports.js +102 -0
- package/dist/surface/tools/tool-kit.d.ts +58 -4
- package/dist/surface/tools/tool-kit.js +140 -6
- package/dist/surface/tools/tool-surface.d.ts +2 -0
- package/dist/surface/tools/tool-surface.js +45 -0
- package/dist/surface/tools/tools.js +49 -87
- package/dist/telemetry/automation-hint.d.ts +5 -0
- package/dist/telemetry/automation-hint.js +7 -0
- package/dist/telemetry/cli-telemetry.d.ts +22 -0
- package/dist/telemetry/cli-telemetry.js +80 -11
- package/dist/telemetry/daemon-telemetry.d.ts +3 -1
- package/dist/telemetry/daemon-telemetry.js +9 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +8 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/feedback.js +51 -4
- package/dist/telemetry/harness-run.d.ts +3 -3
- package/dist/telemetry/harness-run.js +18 -7
- package/dist/telemetry/hud-events.d.ts +1 -1
- package/dist/telemetry/hud-metrics.d.ts +6 -3
- package/dist/telemetry/hud-metrics.js +20 -8
- package/dist/telemetry/install-source.js +1 -1
- package/dist/telemetry/mcp-connection.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +21 -2
- package/dist/telemetry/onboarding-firsts.d.ts +6 -1
- package/dist/telemetry/onboarding-firsts.js +41 -1
- package/dist/telemetry/proxy-telemetry.d.ts +42 -0
- package/dist/telemetry/proxy-telemetry.js +70 -1
- package/dist/telemetry/session-metrics.d.ts +1 -3
- package/dist/telemetry/session-metrics.js +16 -12
- package/dist/telemetry/telemetry.d.ts +9 -1
- package/dist/telemetry/telemetry.js +16 -1
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +7 -1
- package/dist/wire-journal.js +12 -2
- package/docs/README.md +4 -2
- package/docs/actions.mdx +7 -6
- package/docs/adapters.md +2 -1
- package/docs/agent-cheatsheet.md +19 -5
- package/docs/architecture.md +13 -10
- package/docs/autodrive.md +6 -5
- package/docs/benchmarks.md +2 -2
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +6 -6
- package/docs/cli/doctor.mdx +3 -1
- package/docs/cli/feedback.mdx +11 -7
- package/docs/cli/gate.mdx +1 -1
- package/docs/cli/help.mdx +1 -1
- package/docs/cli/init.mdx +20 -16
- package/docs/cli/mcp.mdx +5 -2
- package/docs/cli/open.mdx +5 -2
- package/docs/cli/report.mdx +1 -1
- package/docs/cli/restart.mdx +1 -1
- package/docs/cli/rollback.mdx +4 -4
- package/docs/cli/serve.mdx +7 -5
- package/docs/cli/setup.mdx +1 -1
- package/docs/cli/status.mdx +6 -2
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/telemetry.mdx +5 -1
- package/docs/cli/try.mdx +8 -8
- package/docs/cli/update.mdx +8 -5
- package/docs/cli/verify.mdx +12 -5
- package/docs/cli/version.mdx +1 -1
- package/docs/cli.mdx +10 -6
- package/docs/debugging.md +1 -1
- package/docs/deploy-checks.md +8 -6
- package/docs/desktop.mdx +14 -18
- package/docs/enterprise.md +6 -6
- package/docs/environment.mdx +5 -3
- package/docs/faq.mdx +9 -6
- package/docs/first-drive.md +19 -9
- package/docs/flows.md +3 -4
- package/docs/for-agents.md +1 -1
- package/docs/frameworks.mdx +34 -11
- package/docs/getting-started.md +46 -29
- package/docs/http-transport.md +2 -2
- package/docs/human-control.md +8 -8
- package/docs/index.mdx +2 -2
- package/docs/install-agentic.mdx +18 -19
- package/docs/install-manual.mdx +8 -4
- package/docs/integration-patterns.md +5 -5
- package/docs/multi-agent-testing.md +7 -5
- package/docs/packages/browser.mdx +34 -2
- package/docs/packages/core.mdx +2 -1
- package/docs/packages/electron.mdx +15 -3
- package/docs/packages/eslint-plugin.mdx +1 -1
- package/docs/packages/next.mdx +17 -5
- package/docs/packages/react.mdx +8 -1
- package/docs/packages/server.mdx +3 -3
- package/docs/packages/tauri.mdx +3 -3
- package/docs/packages/vite-plugin.mdx +1 -1
- package/docs/packages.mdx +19 -6
- package/docs/platform-integration.md +12 -4
- package/docs/predicates.mdx +18 -7
- package/docs/production-mode.md +108 -0
- package/docs/prompt-capture.md +99 -0
- package/docs/quickstart.mdx +14 -12
- package/docs/recipes.mdx +9 -8
- package/docs/required-checks.md +3 -2
- package/docs/skill-file.mdx +4 -3
- package/docs/skill-setup.md +2 -2
- package/docs/skill.md +62 -70
- package/docs/state-files.md +1 -0
- package/docs/state-management.mdx +11 -9
- package/docs/system-map.md +14 -13
- package/docs/telemetry.md +32 -24
- package/docs/testing.md +9 -5
- package/docs/tools/act-and-wait.mdx +17 -13
- package/docs/tools/act-sequence.mdx +40 -66
- package/docs/tools/act.mdx +7 -5
- package/docs/tools/assert.mdx +1 -1
- package/docs/tools/look.mdx +1 -1
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/network.mdx +13 -11
- package/docs/tools/overview.mdx +3 -3
- package/docs/tools/query.mdx +3 -3
- package/docs/tools/session-and-feedback.mdx +9 -6
- package/docs/tools/sessions.mdx +7 -0
- package/docs/tools/snapshot.mdx +9 -7
- package/docs/tools/tools-and-run.mdx +11 -10
- package/docs/tools/verify.mdx +1 -1
- package/docs/troubleshooting.mdx +21 -9
- package/docs/usage.md +58 -43
- package/docs/use-cases.mdx +2 -2
- package/docs/vs-chrome-devtools-mcp.mdx +5 -5
- package/docs/vs-playwright-mcp.mdx +5 -4
- package/docs/what-is-recorded.md +12 -7
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/dev/stale-issue-guard.d.ts +0 -34
- package/dist/command/dev/stale-issue-guard.js +0 -69
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
* URLs, no error messages — the accumulator has no way to hold them, which is a stronger guarantee
|
|
16
16
|
* than a rule saying it shouldn't.
|
|
17
17
|
*/
|
|
18
|
-
import { BrowserLaunchKind,
|
|
18
|
+
import { BrowserLaunchKind, } from '@reticlehq/core/telemetry';
|
|
19
19
|
import { errorSkeleton, fingerprintError } from './error-fingerprint.js';
|
|
20
20
|
import { describeToolParams } from './argument-shape.js';
|
|
21
21
|
import { machineSnapshot } from './machine-snapshot.js';
|
|
22
22
|
import { classifyError } from './error-class.js';
|
|
23
23
|
import { HudMetrics } from './hud-metrics.js';
|
|
24
|
+
import { releaseVersionOf } from '../command/update/version-seen.js';
|
|
24
25
|
/** Cap the distinct error shapes held in memory — a pathological loop must not grow unbounded. */
|
|
25
26
|
const MAX_ERROR_KINDS = 40;
|
|
26
27
|
/** Distinct tool names whose repeat-runs are tracked. Its own cap: the tool surface, not error shapes. */
|
|
@@ -143,7 +144,6 @@ export class SessionMetrics {
|
|
|
143
144
|
/** Platform and Harness advisories shown to an agent this window, by kind. */
|
|
144
145
|
#nudges = new Map();
|
|
145
146
|
/** Harness drives refused for coverage this window, by coverage bucket. */
|
|
146
|
-
#harnessRefused = new Map();
|
|
147
147
|
/** Error buckets by whose defect, this window. */
|
|
148
148
|
#errorClasses = new Map();
|
|
149
149
|
/**
|
|
@@ -160,6 +160,13 @@ export class SessionMetrics {
|
|
|
160
160
|
*/
|
|
161
161
|
#appConnects = 0;
|
|
162
162
|
#firstAppAt;
|
|
163
|
+
/**
|
|
164
|
+
* Tool calls before the first app connected, session-lifetime like `#appConnects`. An agent that
|
|
165
|
+
* works against a daemon with no app sends no verdict and often no refusal; this gives it a shape.
|
|
166
|
+
*/
|
|
167
|
+
#toolCallsBeforeApp = 0;
|
|
168
|
+
/** The SDK version of the last page that connected, session-lifetime like `#appConnects`. */
|
|
169
|
+
#sdkVersion;
|
|
163
170
|
#clients = new Set();
|
|
164
171
|
/** Client name -> version, session-lifetime. Never cleared: who drove is a fact about the session. */
|
|
165
172
|
#clientVersions = new Map();
|
|
@@ -267,6 +274,9 @@ export class SessionMetrics {
|
|
|
267
274
|
}
|
|
268
275
|
this.#toolCalls += 1;
|
|
269
276
|
this.#lifetimeToolCalls += 1;
|
|
277
|
+
if (this.#firstAppAt === undefined) {
|
|
278
|
+
this.#toolCallsBeforeApp += 1;
|
|
279
|
+
}
|
|
270
280
|
bump(this.#toolCounts, tool);
|
|
271
281
|
this.#inFlight = tool;
|
|
272
282
|
this.#breadcrumb.push(tool);
|
|
@@ -390,8 +400,9 @@ export class SessionMetrics {
|
|
|
390
400
|
* Zero of these at shutdown means the daemon ran and no app ever connected — a broken install.
|
|
391
401
|
* Non-zero with no tool calls is the opposite problem. See SessionSummary.appConnects.
|
|
392
402
|
*/
|
|
393
|
-
recordAppConnected() {
|
|
403
|
+
recordAppConnected(sdkVersion) {
|
|
394
404
|
this.#appConnects += 1;
|
|
405
|
+
this.#sdkVersion = releaseVersionOf(sdkVersion) ?? this.#sdkVersion;
|
|
395
406
|
this.#firstAppAt ??= this.#now();
|
|
396
407
|
}
|
|
397
408
|
/** One action driven at the page. Settled by the next verdict; see #unsettledActions. */
|
|
@@ -489,10 +500,6 @@ export class SessionMetrics {
|
|
|
489
500
|
recordNudge(kind) {
|
|
490
501
|
bump(this.#nudges, kind);
|
|
491
502
|
}
|
|
492
|
-
/** A Harness drive was refused because the app's coverage is below the gate. */
|
|
493
|
-
recordHarnessRefused(percent) {
|
|
494
|
-
bump(this.#harnessRefused, coverageBucketOf(percent));
|
|
495
|
-
}
|
|
496
503
|
/** Which tool surface was advertised to agents this session. */
|
|
497
504
|
recordSurface(surface) {
|
|
498
505
|
this.#surface = surface.slice(0, 32);
|
|
@@ -574,9 +581,6 @@ export class SessionMetrics {
|
|
|
574
581
|
: {}),
|
|
575
582
|
...(this.#feedbackPrompted > 0 ? { feedbackPrompted: this.#feedbackPrompted } : {}),
|
|
576
583
|
...(this.#nudges.size > 0 ? { nudgesShown: Object.fromEntries(this.#nudges) } : {}),
|
|
577
|
-
...(this.#harnessRefused.size > 0
|
|
578
|
-
? { harnessRefusedCoverage: Object.fromEntries(this.#harnessRefused) }
|
|
579
|
-
: {}),
|
|
580
584
|
...(this.#errorClasses.size > 0
|
|
581
585
|
? { errorClasses: Object.fromEntries(this.#errorClasses) }
|
|
582
586
|
: {}),
|
|
@@ -588,6 +592,8 @@ export class SessionMetrics {
|
|
|
588
592
|
...(this.#firstAppAt === undefined
|
|
589
593
|
? {}
|
|
590
594
|
: { msToFirstApp: Math.max(0, this.#firstAppAt - this.#startedAt) }),
|
|
595
|
+
...(this.#sdkVersion === undefined ? {} : { sdkVersion: this.#sdkVersion }),
|
|
596
|
+
...(this.#toolCallsBeforeApp > 0 ? { toolCallsBeforeApp: this.#toolCallsBeforeApp } : {}),
|
|
591
597
|
...this.#hud.summarize(),
|
|
592
598
|
final,
|
|
593
599
|
// Only on a real exit. A periodic flush carrying `exit: "unknown"` would read as a daemon that
|
|
@@ -649,7 +655,6 @@ export class SessionMetrics {
|
|
|
649
655
|
0 === this.#postSocketFailures &&
|
|
650
656
|
0 === this.#postRetriesSaved &&
|
|
651
657
|
0 === this.#nudges.size &&
|
|
652
|
-
0 === this.#harnessRefused.size &&
|
|
653
658
|
this.#hud.empty);
|
|
654
659
|
}
|
|
655
660
|
/** Zero the counters after a non-final flush so the next flush reports the NEXT window, not a total. */
|
|
@@ -680,7 +685,6 @@ export class SessionMetrics {
|
|
|
680
685
|
this.#bugKinds.clear();
|
|
681
686
|
this.#hud.reset();
|
|
682
687
|
this.#nudges.clear();
|
|
683
|
-
this.#harnessRefused.clear();
|
|
684
688
|
// These five were never cleared, so every flush restated them and a sum over a session's
|
|
685
689
|
// windows came out larger than its tool calls. Counts are windowed, all of them; only the
|
|
686
690
|
// facts below (an app ever connected, which clients, which bug kinds seen) outlive a window.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TelemetryEventKind, ProjectIdSource, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type OnboardingStep, type InstallSource, type McpConnection, type McpOutage, type ToolRefusal, type AppInstrumentation, type HarnessRun, type HudEvent, type PlatformLink, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core/telemetry';
|
|
1
|
+
import { TelemetryEventKind, ProjectIdSource, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type OnboardingStep, type InstallSource, type McpConnection, type McpOutage, type DaemonDeath, type ProxyExit, type WakeRefusal, type DaemonSpawnMethod, type ToolRefusal, type AppInstrumentation, type HarnessRun, type HudEvent, type PlatformLink, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core/telemetry';
|
|
2
2
|
/**
|
|
3
3
|
* Presence of this file marks the MACHINE as the Reticle team's own: every event carries
|
|
4
4
|
* `internal: true`. The persistent twin of `RETICLE_INTERNAL=1`, for a dev machine where setting a
|
|
@@ -107,6 +107,14 @@ export interface TelemetryExtra {
|
|
|
107
107
|
link?: PlatformLink;
|
|
108
108
|
/** `harness_run`: one phase of one Harness run. */
|
|
109
109
|
harness?: HarnessRun;
|
|
110
|
+
/** `mcp_proxy_exited`: why the proxy left, and what it saw in its life. */
|
|
111
|
+
proxyExit?: ProxyExit;
|
|
112
|
+
/** `daemon_died_unclean`: how old the dead daemon was when its pidfile was found. */
|
|
113
|
+
daemonDeath?: DaemonDeath;
|
|
114
|
+
/** `wake_refused`: what held the port. */
|
|
115
|
+
wakeRefusal?: WakeRefusal;
|
|
116
|
+
/** `daemon_started`: how the parent started the daemon. */
|
|
117
|
+
spawnMethod?: DaemonSpawnMethod;
|
|
110
118
|
/**
|
|
111
119
|
* `platform_linked`: the workspace just linked. Overrides the envelope's own reading, which looks
|
|
112
120
|
* at the daemon's working directory and not at the project the HUD linked.
|
|
@@ -49,6 +49,12 @@ const POSTHOG_KEY = 'phc_q4yCxKMXHfWQ43VAz4HxJXZFLqFi4nBbg5v3avemggYo';
|
|
|
49
49
|
* still key on distinct_id.
|
|
50
50
|
*/
|
|
51
51
|
const POSTHOG_PERSONLESS = { $process_person_profile: false };
|
|
52
|
+
/**
|
|
53
|
+
* No address and no location leave with an event. `$geoip_disable` stops PostHog from looking up a
|
|
54
|
+
* city, a postal code and coordinates from the IP. PostHog also stores the request IP as `$ip` when
|
|
55
|
+
* the event leaves `$ip` empty (`null` counts as empty), so a fixed placeholder takes its place.
|
|
56
|
+
*/
|
|
57
|
+
const POSTHOG_NO_IP = { $geoip_disable: true, $ip: '0.0.0.0' };
|
|
52
58
|
/**
|
|
53
59
|
* PostHog's group type for a licensed customer. Sent as `$groups` so PostHog can aggregate a whole
|
|
54
60
|
* organisation natively: per-company retention, "which orgs went quiet", one row per customer rather
|
|
@@ -461,6 +467,11 @@ export const createTelemetry = (opts) => {
|
|
|
461
467
|
...(extra?.hud !== undefined ? { hud: extra.hud } : {}),
|
|
462
468
|
...(extra?.link !== undefined ? { link: extra.link } : {}),
|
|
463
469
|
...(extra?.harness !== undefined ? { harness: extra.harness } : {}),
|
|
470
|
+
...(extra?.proxyExit !== undefined ? { proxyExit: extra.proxyExit } : {}),
|
|
471
|
+
...(extra?.daemonDeath !== undefined ? { daemonDeath: extra.daemonDeath } : {}),
|
|
472
|
+
...(extra?.wakeRefusal !== undefined ? { wakeRefusal: extra.wakeRefusal } : {}),
|
|
473
|
+
// A SCALAR: the event build and the wire schema, no `blocks` entry.
|
|
474
|
+
...(extra?.spawnMethod !== undefined ? { spawnMethod: extra.spawnMethod } : {}),
|
|
464
475
|
};
|
|
465
476
|
// The wire schema's caps are only real if something applies them. An event that fails is
|
|
466
477
|
// DROPPED, never sent and never repaired: the payload is exactly what the caps exist to stop.
|
|
@@ -473,7 +484,7 @@ export const createTelemetry = (opts) => {
|
|
|
473
484
|
// PostHog filters and breakdowns operate on top-level properties, so a nested `feedback.stack`
|
|
474
485
|
// would be invisible to the exact "which stacks report the most bugs" question this exists to
|
|
475
486
|
// answer. One prefix keeps them grouped in the UI without a nested path.
|
|
476
|
-
const { anonymousId: distinctId, event: name, ts, sessionId: eventSessionId, feedback, session, project, verification, versionChange, crash, identity, connection, init, onboarding, bug, refusal, outage, instrumentation, hud, link, harness, ...rest } = event;
|
|
487
|
+
const { anonymousId: distinctId, event: name, ts, sessionId: eventSessionId, feedback, session, project, verification, versionChange, crash, identity, connection, init, onboarding, bug, refusal, outage, instrumentation, hud, link, harness, proxyExit, daemonDeath, wakeRefusal, ...rest } = event;
|
|
477
488
|
// `$session_id` is PostHog's OWN session property, so sending ours under that name lights up
|
|
478
489
|
// its built-in session views and funnels for free rather than requiring custom HogQL everywhere.
|
|
479
490
|
// `sessionId` rides only on events that happened inside a daemon run. A one-shot CLI command
|
|
@@ -508,6 +519,9 @@ export const createTelemetry = (opts) => {
|
|
|
508
519
|
hud,
|
|
509
520
|
link,
|
|
510
521
|
harness,
|
|
522
|
+
proxyExit,
|
|
523
|
+
daemonDeath,
|
|
524
|
+
wakeRefusal,
|
|
511
525
|
};
|
|
512
526
|
for (const [prefix, block] of Object.entries(blocks)) {
|
|
513
527
|
for (const [key, value] of Object.entries(block ?? {})) {
|
|
@@ -521,6 +535,7 @@ export const createTelemetry = (opts) => {
|
|
|
521
535
|
properties: {
|
|
522
536
|
...properties,
|
|
523
537
|
...POSTHOG_PERSONLESS,
|
|
538
|
+
...POSTHOG_NO_IP,
|
|
524
539
|
// Only on a licensed build. An unlicensed event has no organisation to belong to, and an
|
|
525
540
|
// empty group key would mint a phantom "no org" bucket that every OSS install falls into.
|
|
526
541
|
...(event.licenseId !== undefined
|
|
@@ -19,4 +19,6 @@ import { type BrowserBrand, type Verification } from '@reticlehq/core';
|
|
|
19
19
|
*/
|
|
20
20
|
export declare function verificationOf(toolName: string, result: Record<string, unknown>, durationMs: number, brand?: BrowserBrand,
|
|
21
21
|
/** The call ran inside a Harness drive's async context (`currentDrivenBy()`), read by the caller. */
|
|
22
|
-
drivenByHarness?: boolean
|
|
22
|
+
drivenByHarness?: boolean,
|
|
23
|
+
/** The page SDK version the session announced on HELLO. */
|
|
24
|
+
pageSdkVersion?: string): Verification | undefined;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { CaptureLoss, ReplayStatus, ReticleTool, VerificationDriver, Verified, VerifiedReason, } from '@reticlehq/core';
|
|
14
14
|
import { VERDICT_TOOLS } from '../surface/tools/feedback-tools.js';
|
|
15
15
|
import { getBrowserMode } from './browser-mode.js';
|
|
16
|
+
import { releaseVersionOf } from '../command/update/version-seen.js';
|
|
16
17
|
/**
|
|
17
18
|
* The deciding clause, read off the result `decideVerified` wrote it onto.
|
|
18
19
|
*
|
|
@@ -116,7 +117,9 @@ function passedOf(result, status) {
|
|
|
116
117
|
*/
|
|
117
118
|
export function verificationOf(toolName, result, durationMs, brand,
|
|
118
119
|
/** The call ran inside a Harness drive's async context (`currentDrivenBy()`), read by the caller. */
|
|
119
|
-
drivenByHarness = false
|
|
120
|
+
drivenByHarness = false,
|
|
121
|
+
/** The page SDK version the session announced on HELLO. */
|
|
122
|
+
pageSdkVersion) {
|
|
120
123
|
if (!VERDICT_TOOLS.has(toolName))
|
|
121
124
|
return undefined;
|
|
122
125
|
// A paused session REFUSES the call — nothing driven, nothing asserted. The refusal carries a
|
|
@@ -141,6 +144,7 @@ drivenByHarness = false) {
|
|
|
141
144
|
return undefined;
|
|
142
145
|
const reason = reasonOf(result);
|
|
143
146
|
const uncleanLoss = uncleanLossOf(result, reason);
|
|
147
|
+
const sdkVersion = releaseVersionOf(pageSdkVersion);
|
|
144
148
|
return {
|
|
145
149
|
via: toolName,
|
|
146
150
|
verified: verified ?? (true === passed ? 'yes' : 'no'),
|
|
@@ -179,5 +183,7 @@ drivenByHarness = false) {
|
|
|
179
183
|
// agent keeps calling tools, so a process-wide flag credited the agent's verdicts too. Absent is
|
|
180
184
|
// the ordinary case — the agent called the tool itself.
|
|
181
185
|
...(drivenByHarness ? { driven: VerificationDriver.HARNESS } : {}),
|
|
186
|
+
// WHICH page SDK the verdict was judged on. A page older than the daemon misses newer fixes.
|
|
187
|
+
...(sdkVersion === undefined ? {} : { sdkVersion }),
|
|
182
188
|
};
|
|
183
189
|
}
|
package/dist/wire-journal.js
CHANGED
|
@@ -14,6 +14,7 @@ import { originOf } from './portal/session/session-manager.js';
|
|
|
14
14
|
import { Bridge } from './portal/bridge/bridge.js';
|
|
15
15
|
import { FlowStore } from './language/flows/flows.js';
|
|
16
16
|
import { makeJournalAttach } from './memory/journal/attach-journal.js';
|
|
17
|
+
import { setEpisodeLogEnabled } from './memory/journal/episode-log.js';
|
|
17
18
|
import { endLiveSessions, makeSessionEnd, recordDriveRun } from './memory/journal/session-end.js';
|
|
18
19
|
import { attachDriveRunFlush } from './memory/journal/drive-run-flush.js';
|
|
19
20
|
import { AmbientStore } from './memory/journal/ambient-store.js';
|
|
@@ -34,13 +35,15 @@ import { readBackgroundTraffic } from './portal/session/facts/background-traffic
|
|
|
34
35
|
*/
|
|
35
36
|
export function attachJournal(bridge, deps) {
|
|
36
37
|
const journalAttach = makeJournalAttach(deps);
|
|
38
|
+
// The episode log is part of the journal: the same switch turns both off.
|
|
39
|
+
setEpisodeLogEnabled(deps.enabled);
|
|
37
40
|
const ambientStore = new AmbientStore(deps.fs, deps.reticleRoot);
|
|
38
41
|
// Built once, not per session: the resolver walks config discovery and the user-level registry,
|
|
39
42
|
// and neither changes between two tabs connecting a second apart.
|
|
40
43
|
const resolveArtifactRoot = artifactRootResolver(deps.reticleRoot, {
|
|
41
44
|
servingDirectory: servingDirectoryOf,
|
|
42
45
|
});
|
|
43
|
-
|
|
46
|
+
const stampProject = (session) => {
|
|
44
47
|
// Stamp the project's own `.reticle` before ANY counter fires for this session. Without it every
|
|
45
48
|
// verdict is recorded against wherever the daemon was started, which is how one app's evidence
|
|
46
49
|
// reached a different account's production dashboard.
|
|
@@ -58,9 +61,13 @@ export function attachJournal(bridge, deps) {
|
|
|
58
61
|
// repository nor the root we were about to write into: it created `.reticle/` — holding nothing
|
|
59
62
|
// but the ignore file — wherever the daemon was launched, coming back every boot after the user
|
|
60
63
|
// deleted it, while the journals this ignore protects landed in another tree, uncovered.
|
|
61
|
-
if (deps.enabled)
|
|
64
|
+
if (deps.enabled) {
|
|
62
65
|
void ensureWorkspaceGitignore(deps.fs, session.artifactRoot);
|
|
66
|
+
}
|
|
63
67
|
journalAttach(session);
|
|
68
|
+
};
|
|
69
|
+
bridge.attachSessionCreate((session) => {
|
|
70
|
+
stampProject(session);
|
|
64
71
|
// Seed the learned ambient map so a fresh session starts knowing which regions churn, instead of
|
|
65
72
|
// re-learning from zero. Best-effort + async: a late seed still helps, a failure is silent.
|
|
66
73
|
if (deps.enabled) {
|
|
@@ -70,6 +77,9 @@ export function attachJournal(bridge, deps) {
|
|
|
70
77
|
.catch(() => undefined);
|
|
71
78
|
}
|
|
72
79
|
});
|
|
80
|
+
// A page that connected without a project and names one later (an app's own connect() ran before
|
|
81
|
+
// the build plugin's) is stamped again, so its evidence leaves the unmatched bucket from now on.
|
|
82
|
+
bridge.attachProjectAdopted(stampProject);
|
|
73
83
|
// Teardown: flush the journal tail to disk + persist what this session learned.
|
|
74
84
|
const teardown = makeSessionEnd({
|
|
75
85
|
...deps,
|
package/docs/README.md
CHANGED
|
@@ -69,11 +69,13 @@ Everything here is published to [docs.reticle.sh](https://docs.reticle.sh) by `d
|
|
|
69
69
|
| [multi-agent-testing.md](multi-agent-testing.md) | more than one agent on one app |
|
|
70
70
|
| [human-control.md](human-control.md) | taking the wheel back from the agent |
|
|
71
71
|
| [deploy-checks.md](deploy-checks.md) | running Reticle against a deployed build |
|
|
72
|
+
| [production-mode.md](production-mode.md) | driving a live site under a policy that refuses payments, deletions and invites |
|
|
72
73
|
| [token-efficiency.md](token-efficiency.md) | why it costs less than a screenshot loop |
|
|
73
74
|
| [benchmarks.md](benchmarks.md) | how the numbers were measured, including where Reticle loses |
|
|
74
75
|
| [vs-playwright-mcp.mdx](vs-playwright-mcp.mdx) | outside-in vs inside-out, and when Playwright is right |
|
|
75
76
|
| [vs-chrome-devtools-mcp.mdx](vs-chrome-devtools-mcp.mdx) | cheaper per look, catches less: the trade, measured |
|
|
76
77
|
| [vs-screenshots.mdx](vs-screenshots.mdx) | why a better vision model does not fix a non-visual bug |
|
|
78
|
+
| [prompt-capture.md](prompt-capture.md) | the prompt hook `init` installs: what it keeps, what leaves, and every switch to turn it off |
|
|
77
79
|
| [what-is-recorded.md](what-is-recorded.md) | what Reticle keeps, what is redacted, and what it takes for any of it to leave your machine |
|
|
78
80
|
| [telemetry.md](telemetry.md) | what is collected, and how to turn it off |
|
|
79
81
|
| [local-registry.md](https://github.com/reticlehq/reticle/blob/main/docs/local-registry.md) | installing an unpublished build |
|
|
@@ -92,8 +94,8 @@ Everything here is published to [docs.reticle.sh](https://docs.reticle.sh) by `d
|
|
|
92
94
|
| [adapters.md](adapters.md) | what an adapter is, the three kinds, and how to write one |
|
|
93
95
|
| [state-files.md](state-files.md) | what to do when a file on disk came from a different version of Reticle |
|
|
94
96
|
| [system-map.md](system-map.md) | how a tool call reaches the app, and which failures are silent |
|
|
95
|
-
| [telemetry
|
|
96
|
-
| [telemetry
|
|
97
|
+
| [contributing/telemetry/contract.md](https://github.com/reticlehq/reticle/blob/main/contributing/telemetry/contract.md) | required reading before touching anything that emits |
|
|
98
|
+
| [contributing/telemetry/events.md](https://github.com/reticlehq/reticle/blob/main/contributing/telemetry/events.md) | every event kind and its payload, when it is sent, why, and an example |
|
|
97
99
|
| [first-drive.md](first-drive.md) | clone to a real verdict in about fifteen minutes, and the traps on the way |
|
|
98
100
|
| [debugging.md](debugging.md) | debugging Reticle itself: the four signals and what each answers |
|
|
99
101
|
| [fixtures.md](https://github.com/reticlehq/reticle/blob/main/docs/fixtures.md) | the sibling `reticle-fixtures` repo: install complexity, not regressions |
|
package/docs/actions.mdx
CHANGED
|
@@ -5,7 +5,7 @@ description: Every action reticle_act and reticle_act_and_wait can perform, with
|
|
|
5
5
|
icon: hand-pointer
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Every action Reticle can perform takes the same shape: a `ref` from [`reticle_look { action: "page" }`](/tools/snapshot) or [`reticle_look { action: "find" }`](/tools/query)
|
|
8
|
+
Every action Reticle can perform takes the same shape: an element, an `action` name, and an optional `args` object. Give the element as a `ref` from [`reticle_look { action: "page" }`](/tools/snapshot) or [`reticle_look { action: "find" }`](/tools/query). Or give a `target` (`{ testid }`, `{ text }`, `{ role, name }` or `{ label }`) that Reticle finds in the same call. A press of Escape, Tab or a modifier shortcut goes to the document and needs neither. The same actions are available on [`reticle_act`](/tools/act), which proves nothing, and on [`reticle_act_and_wait`](/tools/act-and-wait), which returns a verdict.
|
|
9
9
|
|
|
10
10
|
```json
|
|
11
11
|
{ "ref": "e104", "action": "fill", "args": { "value": "reticle" } }
|
|
@@ -13,7 +13,7 @@ Every action Reticle can perform takes the same shape: a `ref` from [`reticle_lo
|
|
|
13
13
|
|
|
14
14
|
| Action | Arguments | Notes |
|
|
15
15
|
| --- | --- | --- |
|
|
16
|
-
| `click` / `dblclick` | none | Dispatches a
|
|
16
|
+
| `click` / `dblclick` | none | Dispatches the full pointer and mouse sequence on the element. `args.native: true` forces a trusted native click |
|
|
17
17
|
| `hover` | none | `mouseover` + `mouseenter`, so JS hover state triggers |
|
|
18
18
|
| `focus` / `blur` | none | |
|
|
19
19
|
| `fill` | `{ value }` | Sets value via a React-safe native setter, then `input` and `change` |
|
|
@@ -53,7 +53,7 @@ Reticle blocks a control that looks destructive before acting on it:
|
|
|
53
53
|
```json
|
|
54
54
|
{
|
|
55
55
|
"error": "potentially destructive action blocked; retry with args.confirmDangerous=true",
|
|
56
|
-
"recovery": "Reticle blocked a
|
|
56
|
+
"recovery": "Reticle blocked a control whose label reads as consequential (delete, remove, revoke, pay…) on purpose. If you mean it, send the SAME call again with `args: { confirmDangerous: true }`: it goes inside the `args` object, not beside it. This is a deliberate refusal, not a defect: there is nothing to report."
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
@@ -96,13 +96,14 @@ Several actions in one round trip:
|
|
|
96
96
|
|
|
97
97
|
<Warning>
|
|
98
98
|
[`reticle_act { steps: [...] }`](/tools/act-sequence) returns `ok: true` when the steps were
|
|
99
|
-
**dispatched**, not when they worked.
|
|
100
|
-
|
|
99
|
+
**dispatched**, not when they worked. A step without `expect` proves nothing. Give the last step
|
|
100
|
+
an `expect` that names the end state, for example `"expect": { "kind": "signal", "name": "auth:granted" }`.
|
|
101
|
+
The sequence then returns a verdict and stops at the first `expect` that does not hold.
|
|
101
102
|
</Warning>
|
|
102
103
|
|
|
103
104
|
## Real versus synthetic input
|
|
104
105
|
|
|
105
|
-
`inputMode` in the response says which you got. `synthetic` means events dispatched programmatically.
|
|
106
|
+
`inputMode` in the response says which you got. `synthetic` means events dispatched programmatically. This is correct for most apps. `real` means native CDP input. You get it under `reticle drive`, and a few drag-and-drop and pointer-gesture libraries need it. On a leased tab (`reticle_lease`), a `press` uses the real keyboard: Tab moves focus, and a press with a `ref` focuses that element first, so Enter or Space presses it as a person would.
|
|
106
107
|
|
|
107
108
|
<Warning>
|
|
108
109
|
On a throttled tab, synthetic timers and pointer gestures can silently no-op. Pass
|
package/docs/adapters.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Writing an adapter
|
|
3
3
|
description: What an adapter is, which kinds exist, and how to build one of your own.
|
|
4
|
+
noindex: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Reticle watches an app from the inside. Most of what it does is the same everywhere: a click is a click, a failed request is a failed request. A few things are not, and those are what adapters are for.
|
|
@@ -13,7 +14,7 @@ There are three kinds, and they answer three different questions.
|
|
|
13
14
|
| **build** | how does the SDK get into the page, and how does an element learn which file it came from? | `@reticlehq/vite-plugin` |
|
|
14
15
|
| **realm** | how do we watch this kind of app's own traffic and take its picture? | `@reticlehq/electron` |
|
|
15
16
|
|
|
16
|
-
You almost certainly want the first one. It is
|
|
17
|
+
You almost certainly want the first one. It is one of two kinds you can write today without a change to Reticle itself (the other is a lint rule, see the end of this page). It is the kind that makes a failing check say `src/CartRow.tsx:42` instead of "something on the page".
|
|
17
18
|
|
|
18
19
|
## Writing a framework adapter
|
|
19
20
|
|
package/docs/agent-cheatsheet.md
CHANGED
|
@@ -6,7 +6,7 @@ icon: bolt
|
|
|
6
6
|
|
|
7
7
|
The loop is **look → act → observe → assert**. Verdicts come from `reticle_act_and_wait`, `reticle_assert`, `reticle_act { steps }` when a step declares `expect`, and `reticle_verify` (`change`/`flows`). Everything else moves or reads the app and proves nothing. Only `verified: "yes"` is a pass: `unknown` means Reticle could not tell, and `no-fault` means nothing was declared to prove. Reach for `reticle_act_and_wait({ ref, action, until })` first: it names the expected consequence before the action, which is the difference between a check and a rationalisation.
|
|
8
8
|
|
|
9
|
-
One screen to get fluent. Reticle is the **proof layer for AI agents**: no screenshots, no vision model, evidence not prose. Everything below returns structured data. Full guide: [
|
|
9
|
+
One screen to get fluent. Reticle is the **proof layer for AI agents**: no screenshots, no vision model, evidence not prose. Everything below returns structured data. Full guide: [Usage](/usage).
|
|
10
10
|
|
|
11
11
|
## The core loop: look → act → observe → assert
|
|
12
12
|
|
|
@@ -48,6 +48,7 @@ Two consequences worth knowing:
|
|
|
48
48
|
|
|
49
49
|
- **You can assert what a field contains.** Do not read the value with `reticle_look { action: "find" }` and compare it in prose: that produces no verdict and does not count as verification.
|
|
50
50
|
- **`{ role, text }` checks the text,** so it does not match every button with that role.
|
|
51
|
+
- **A `text` predicate reads rendered text, not a field's value.** Text typed into an `<input>` or a `<textarea>` is not on the page as text, so `{ kind: "text", contains }` does not see it. Assert it with `value` on an `element` predicate, as above.
|
|
51
52
|
|
|
52
53
|
Fields that nothing can check are refused rather than ignored: `by` without a `value`, and `label`, `placeholder`, `testid` or `component` when a higher-precedence field already selected the element. `alt` is the one that can still be checked beside another locator: on an image it is compared with the image's `alt` attribute (so `alt: ""` passes on a decorative image but not on one missing its alt), and on anything that is not an image it is refused, with a hint to use `name`. An element query is a first-match dispatch, not a conjunction.
|
|
53
54
|
|
|
@@ -121,7 +122,7 @@ Most of what used to be a tool of its own is now an action on one of them:
|
|
|
121
122
|
|
|
122
123
|
Call an old name and you are told the new one. `reticle_tools` prints the live surface and every mapping.
|
|
123
124
|
|
|
124
|
-
- `reticle_verify`: **the
|
|
125
|
+
- `reticle_verify`: **the tool that checks the whole app or the whole change in one call.** `{ action:"crawl" }` drives every reachable control itself and reports the whole fault set in one call (destructive: it really clicks). `{ action:"flows" }` replays every saved flow with no model in the loop. Reach for it when you think you are done: every other tool here answers "here is more to look at".
|
|
125
126
|
|
|
126
127
|
Not on this surface at all, but one hop away: `reticle_run { tool, args }` calls any registered tool by name, advertised or not, and `reticle_tools { names: [...] }` loads its arguments. That reaches `reticle_capabilities` (the app's whole testable surface in one call), `reticle_domain` (learn the app + gaps), `reticle_run { tool: "reticle_baseline", args: { action: "diff" } }`, `reticle_project` (run history), `reticle_screenshot`, `reticle_visual_diff`, `reticle_storage`, `reticle_network_mock` and `reticle_clock`. A daemon started with `RETICLE_ADVERTISE_ALL_TOOLS=1` advertises some of these directly (`reticle_capabilities`, `reticle_screenshot`, `reticle_visual_diff`, `reticle_storage`, `reticle_network_mock`, `reticle_clock`, plus `reticle_record`, `reticle_flow_save`, `reticle_flow_replay`, `reticle_intent` and `reticle_context`); the rest stay behind `reticle_run`. It also switches to the unmerged spelling: `reticle_snapshot`/`reticle_query`/`reticle_inspect`/`reticle_state` replace `reticle_look`, `reticle_network`/`reticle_console` replace `reticle_observe { action }`, `reticle_wait_for` replaces `reticle_assert { action: "wait" }`, `reticle_sessions`/`reticle_feedback` replace `reticle_session` list/feedback, and `reticle_act_sequence` replaces `reticle_act { steps }`. Examples on this page use the default spelling.
|
|
127
128
|
|
|
@@ -151,6 +152,8 @@ Recent additions, each of which answers a question agents were previously asking
|
|
|
151
152
|
| `elided` | `reticle_act_and_wait` | Some diff arrays were capped. The count is real even when the list is trimmed, so a small array does not mean a quiet app. |
|
|
152
153
|
| `colorTokens`, `themeScope` | `reticle_look { action: "element" }` | Every design token matching a colour, not one arbitrary winner, and which theme the reading was taken under. The singular `colorToken` is `null` when several tokens share a colour, because naming one of them was the defect. |
|
|
153
154
|
| `why` | `reticle_session { action: "list" }`, when the list is empty | Why nothing is connected, and the next action. An empty list is never the end of the road. |
|
|
155
|
+
| `engineBlindSpots` | `reticle_act_and_wait`, `reticle_assert` | What the engine could not see in this run: `pixels-unobserved` once an action ran with a `<canvas>` on screen, `claim-arithmetic-unchecked` while a claim states an amount and no verdict ran a `compare`. A clean verdict says nothing about these. |
|
|
156
|
+
| `sdkVersion`, `sdkBehind` | `reticle_session { action: "list" }` | The Reticle SDK version in each tab. `sdkBehind` is present when the page is a minor release or more behind the daemon, and gives the update command. Each verdict on such a page carries a short `sdk_behind` note. |
|
|
154
157
|
|
|
155
158
|
**When a verdict comes back `unsettled`,** it now names what it was waiting for and what the window actually held. Read those before retrying: an `unknown` that explains itself is a retry with a better `until`, and one that does not is a dead end.
|
|
156
159
|
|
|
@@ -158,15 +161,26 @@ Recent additions, each of which answers a question agents were previously asking
|
|
|
158
161
|
|
|
159
162
|
## Start here
|
|
160
163
|
|
|
161
|
-
1. **First move, always:** `reticle_session { action: "list" }` finds the connected tab. Just ran `reticle init` or restarted the dev server? You do not need to poll: live tools wait briefly for a tab that is about to connect. Omit `sessionId` if there's only one tab. **An empty list is not a dead end: read
|
|
164
|
+
1. **First move, always:** `reticle_session { action: "list" }` finds the connected tab. Just ran `reticle init` or restarted the dev server? You do not need to poll: live tools wait briefly for a tab that is about to connect. Omit `sessionId` if there's only one tab. **An empty list is not a dead end: read `why`.** It names the case (no app running, an app that never dialled this daemon, a project that is not wired, or a tab that closed) and the fix for each. In a project that is not wired, `next_step` also names the next calls. `lease` (present when an app is running) is the zero-install check of the running app: do it first, because it changes no file. `consent` is the question for `init`, which changes `package.json`, the lockfile and the build config: ask the user, and run `init` only if they agree. Do not fall back to static reasoning until you have read `why`.
|
|
162
165
|
2. `reticle_run({ tool: "reticle_domain", args: {} })` learns the app BEFORE testing: the saved flows, what each asserts, and the **gaps** (declared signals/testids that no flow verifies, i.e. untested intent). Tells you what to test and where the real risk is without crawling the whole app. Falls back to `reticle_run({ tool: "reticle_capabilities", args: {} })` for the raw testable surface (`testids`, `signals`, `stores`, `flows`).
|
|
163
166
|
3. Run the loop: **look → act → observe → assert**, cross-checking the 4 layers on anything that matters.
|
|
164
167
|
|
|
168
|
+
## No MCP tools in this host
|
|
169
|
+
|
|
170
|
+
A cloud agent or a worker can have a shell and no MCP tools. Use the CLI. `verify --expect` asks a running daemon and never starts one, so start it first:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npx @reticlehq/server serve --headless
|
|
174
|
+
npx @reticlehq/server verify http://localhost:5173/ --expect '{"kind":"text","contains":"Welcome"}'
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
It exits `0` only on `verified: "yes"`. `--expect-file <path>` reads the predicate from a file, and `--timeout N` sets how long it waits (default `4000` ms). To move a connected tab to another page first, run `npx @reticlehq/server open <url> --navigate`. For an action that `--expect` cannot do, use the [HTTP MCP transport](/http-transport).
|
|
178
|
+
|
|
165
179
|
## Token note
|
|
166
180
|
|
|
167
|
-
- **Keep observation cheap.** Prefer `reticle_look { action: "find" }` / scoped or `interactive` `reticle_look { action: "page" }` / `reticle_assert` over dumping the full tree. A full verify loop is ~100 tokens; see [
|
|
181
|
+
- **Keep observation cheap.** Prefer `reticle_look { action: "find" }` / scoped or `interactive` `reticle_look { action: "page" }` / `reticle_assert` over dumping the full tree. A full verify loop is ~100 tokens; see [Token efficiency](/token-efficiency) (~73× leaner than full-tree snapshots).
|
|
168
182
|
- **Re-look with `reticle_look({ action: "page", diff:true })`** after an action; it returns only what changed (`mode:delta`/`unchanged`), ~99% fewer tokens than a full re-snapshot and no stale tree to mis-read. Every snapshot/query result carries `cost:{ bytes, tokens }`, so re-scope before reading if it's large.
|
|
169
183
|
- **Cap broad reads.** `reticle_look { action: "find" }` takes `limit` (caps descriptors; reports `total`/`truncated`) and `count_only` (just the match count). `reticle_observe { action: "network" }` / `reticle_observe { action: "console" }` take `limit` (most-recent-N, reports `droppedOldest`) and carry the same `cost` hint, so a busy page or wide window never floods your context unnoticed.
|
|
170
184
|
- **A saved flow tells you if it's a real test.** `reticle_flow_save` returns `assertions.grade` (`asserted` / `presence-only` / `assertion-free`); if it's not `asserted`, add a consequence (`reticle_annotate` assert-signal/assert-net or a success-state) so it can't pass while broken. On replay, an ambiguous heal (two testids tie) is surfaced, never auto-applied. And an `apply` heal re-replays the rebound flow and **refuses to write** if the success consequence no longer fires (`status:consequence_broken`): it heals the locator, never the intent.
|
|
171
185
|
- **Predicate schema is not bloated.** The recursive predicate DSL used by `reticle_assert` / `reticle_assert { action: "wait" }` / `reticle_act_and_wait` is defined once (`PredicateSchema` in `@reticlehq/core`) and shared by all three tools. No action needed.
|
|
172
|
-
- **One tool surface, and a hatch behind it.** Reticle advertises navigate/act/act_and_wait/assert/look/observe/session/verify, plus `reticle_tools` and `reticle_run`. That is the whole detect loop, the file-pointer, the feedback channel, the handback, and the
|
|
186
|
+
- **One tool surface, and a hatch behind it.** Reticle advertises navigate/act/act_and_wait/assert/look/observe/session/verify, plus `reticle_tools` and `reticle_run`. That is the whole detect loop, the file-pointer, the feedback channel, the handback, and the tool that checks the whole app in one call, reached by an `action` argument rather than by a name each. `reticle_tools` discovers: no args lists every registered tool name and summary, `names:[…]` loads full params on demand. `reticle_run { tool, args }` then calls any of them, advertised or not, which is what keeps recording a flow, screenshots, visual diff, storage, network mocking and the clock reachable without spending a slot on each. A daemon started with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read at startup, so restart it) advertises a wider set outright (the default tools plus the extra ones listed above), with output schemas, under the unmerged names listed above. It is not the whole registry: the count is capped because editors budget tools across every connected MCP server. `reticle_verify` is advertised: `{ action:"flows" }` replays every saved flow with no model in the loop, and `{ action:"crawl" }` drives every reachable control itself and reports the whole fault set in one call. **Sizes are deliberately not quoted here.** A count in prose goes stale, and has three times already. `reticle_tools` reports the live surface, and SKILL.md carries the one gated table.
|
package/docs/architecture.md
CHANGED
|
@@ -4,9 +4,9 @@ description: 'The moving parts, the data flow between them, and the design decis
|
|
|
4
4
|
icon: sitemap
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Reticle has three moving parts: a
|
|
7
|
+
Reticle has three moving parts: a development **SDK** inside your page that instruments the DOM, network, console, routing and framework state; a local **server** that runs a WebSocket bridge to that SDK and an MCP server for your agent; and a git-checked **`.reticle/` workspace** holding flows, baselines and run artifacts. Your agent calls an MCP tool, the server turns it into a command over the WebSocket, the SDK runs it in the real page and streams structured events back.
|
|
8
8
|
|
|
9
|
-
> For engineers evaluating Reticle, integrating it at scale, or contributing. It explains the moving parts, the data flow, and the design decisions behind them. If you just want to get running, start with [
|
|
9
|
+
> For engineers evaluating Reticle, integrating it at scale, or contributing. It explains the moving parts, the data flow, and the design decisions behind them. If you just want to get running, start with [Getting started](/getting-started); come back here when you want to know _why_.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -38,6 +38,9 @@ Reticle is a pnpm + Turborepo monorepo. The split is not cosmetic: each boundary
|
|
|
38
38
|
| Package | Runs in | Responsibility | Hard rule |
|
|
39
39
|
| --- | --- | --- | --- |
|
|
40
40
|
| `@reticlehq/core` | both | The **wire contract**: every constant + zod schema crossing any boundary | Depends only on `zod` and `open-verification` |
|
|
41
|
+
| `open-verification` | both | The Open Verification Protocol: vocabulary, rules, `adjudicate()` | Depends only on `zod` |
|
|
42
|
+
| `@reticlehq/engine` | Node | The rules that decide a verdict | No browser, no daemon, no CLI |
|
|
43
|
+
| `@reticlehq/init` | Node (build time) | `reticle init`: writes files and runs the package manager | Never opens a socket |
|
|
41
44
|
| `@reticlehq/browser` | the browser | Instrument the page; execute commands; emit events | Never imports Node APIs |
|
|
42
45
|
| `@reticlehq/server` | Node | The bridge, the MCP server, the `reticle` CLI, flow/run storage | Never imports DOM APIs |
|
|
43
46
|
| `@reticlehq/react` | the browser | The SDK **kit** you install in a browser app: re-exports the browser sensor (so one install gives both `reticle` and `install`) and maps a DOM node → React component → source `file:line` | Core works without the source-mapping half |
|
|
@@ -57,7 +60,7 @@ The browser and server both import it; neither can invent a message the other do
|
|
|
57
60
|
|
|
58
61
|
## The data flow, end to end
|
|
59
62
|
|
|
60
|
-
1. **Connect.** In dev, your app calls `reticle.connect({ session })`. The SDK opens a WebSocket to the bridge (`ws://localhost:4400/reticle` by default) and sends a `HELLO` carrying the session id, protocol version, and (if configured) a pairing token. Each browser tab uses `SESSION_AUTO` (a unique id), so multiple apps/tabs never collide.
|
|
63
|
+
1. **Connect.** In dev, your app calls `reticle.connect({ session })`. The SDK opens a WebSocket to the bridge (`ws://localhost:4400/reticle` by default) and sends a `HELLO` carrying the session id, protocol version, and (if configured) a pairing token. Each browser tab uses `SESSION_AUTO` (a unique id), so multiple apps/tabs never collide. A second `connect()` on a live page does not open a second socket. If it names a `projectId` and the first call did not, the SDK sends `HELLO` again with the id, and the bridge moves the session to that project. An app that calls `connect()` itself, before the build plugin's `connect({ projectId })`, keeps its project this way.
|
|
61
64
|
2. **Capture.** The SDK installs observers: a `MutationObserver` for DOM changes, wrappers around `fetch`/`XHR` for network, a console hook, a history hook for routing, and registries the app opts into (`registerStore`, `registerCapabilities`, `reticle.signal`). Events flow into a bounded **ring buffer**, so recent history is always available and memory is capped.
|
|
62
65
|
3. **Look / act.** The agent calls an MCP tool. `reticle_look { action: "find" }` finds an element by role/text/testid/ component and returns a stable **ref**. `reticle_act` dispatches an action against that ref and returns an **effect** report (did it land, did the DOM mutate, did focus move…). The server sends the command over the WebSocket; the SDK runs it and replies.
|
|
63
66
|
4. **Observe.** After an action, the agent reads what happened with `reticle_observe { action: "network" }`, `reticle_observe { action: "console" }`, `reticle_look { action: "state" }`, or the reaction digest from `reticle_act_and_wait`. The server pulls the relevant slice of the ring buffer (scoped to a cursor so stale events can't leak in) and returns a compact summary.
|
|
@@ -79,15 +82,15 @@ Reticle is honest about which tier a given assertion used, so "green" carries it
|
|
|
79
82
|
|
|
80
83
|
### 2. Structured reads, not screenshots
|
|
81
84
|
|
|
82
|
-
A screenshot is ~1,365 image tokens per look, slow, non-deterministic, and **blind to everything non-visual**: the failed request, the console error, the route that didn't change. Reticle reads the accessibility tree, the network log, the console, and framework state as compact structured data. That's an order of magnitude cheaper _and_ it sees the bugs pixels can't. See [
|
|
85
|
+
A screenshot is ~1,365 image tokens per look, slow, non-deterministic, and **blind to everything non-visual**: the failed request, the console error, the route that didn't change. Reticle reads the accessibility tree, the network log, the console, and framework state as compact structured data. That's an order of magnitude cheaper _and_ it sees the bugs pixels can't. See [Benchmarks](/benchmarks) for the measured comparison.
|
|
83
86
|
|
|
84
87
|
### 3. Record once, replay deterministically
|
|
85
88
|
|
|
86
89
|
The same verification runs over and over, every commit, every CI run. Reticle records a **flow** once, then replays it with **no AI model**: it re-resolves each element's durable anchor against the live DOM and re-asserts the declared consequence. A CI gate diffs the verdict exactly, at ~0% flake, for a couple hundred tokens, versus an agent re-driving the whole flow with the model every time. Self- healing rebinds a drifted anchor **only if the consequence still fires**, so it never "heals to the wrong element and ships the regression."
|
|
87
90
|
|
|
88
|
-
### 4.
|
|
91
|
+
### 4. Development by default, your app data stays local
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
By default the SDK is tree-shaken out of production builds and connects only to a local bridge. [Production mode](/production-mode) is the one exception: it is opt-in, and a page loads the SDK only with a signed, single-use activation ticket. The bridge binds to loopback by default. It refuses a connect without the pairing token, also on localhost. Exposing it beyond localhost also needs the token (and the server refuses to start on a non-loopback host unless `RETICLE_ALLOWED_ORIGINS` names the app's origins). Every environment variable that gates a security control is a single named constant, so a typo can't silently disable auth. Nothing from the app under test leaves your machine unless you link a project: no DOM, network, console, state, or source. What a linked project syncs is set by `reticle config`; see [what is recorded](/what-is-recorded). The CLI reports anonymous, opt-out usage metrics only (a random id + event names like `cli_command_run`/`daemon_started`; no code, no PII; see [telemetry](/telemetry)); opt out with `reticle telemetry disable`, `RETICLE_TELEMETRY=0`, or `DO_NOT_TRACK=1`. The one thing that carries free text is feedback you or your agent deliberately send (`reticle feedback` / `reticle_session { action: "feedback" }`). It is never collected passively, it is redacted before sending, and it is disabled separately with `RETICLE_FEEDBACK=0`.
|
|
91
94
|
|
|
92
95
|
---
|
|
93
96
|
|
|
@@ -107,7 +110,7 @@ This is plain, reviewable, version-controllable data, not a black box.
|
|
|
107
110
|
## Running at scale (multiple apps, multiple projects)
|
|
108
111
|
|
|
109
112
|
- **Multiple apps / tabs on one bridge:** fine. Each connection has a unique `SESSION_AUTO` id; a tool call targets the focused/most-recent session, or you pass an explicit `sessionId`.
|
|
110
|
-
- **Multiple isolated projects:** give each project its own bridge port via `RETICLE_PORT` (set it in the MCP config and dial the same port from the app). A port already in use fails fast with a clear error rather than hanging. See [
|
|
113
|
+
- **Multiple isolated projects:** give each project its own bridge port via `RETICLE_PORT` (set it in the MCP config and dial the same port from the app). A port already in use fails fast with a clear error rather than hanging. See [Getting started → Running multiple apps at once](/getting-started#running-multiple-apps-at-once).
|
|
111
114
|
- **CI / no MCP:** `reticle verify <url>` replays the saved flows headlessly and exits non-zero on failure. It writes the same verdict artifact the MCP path produces, with no agent in the loop.
|
|
112
115
|
|
|
113
116
|
---
|
|
@@ -125,7 +128,7 @@ See [LICENSE](https://github.com/reticlehq/reticle/blob/main/LICENSE) and each p
|
|
|
125
128
|
|
|
126
129
|
## Where to go next
|
|
127
130
|
|
|
128
|
-
- [
|
|
129
|
-
- [
|
|
130
|
-
- [
|
|
131
|
+
- [Getting started](/getting-started) wires Reticle into your app in a couple of minutes.
|
|
132
|
+
- [Benchmarks](/benchmarks) covers how we measure, and the honest results against the alternatives.
|
|
133
|
+
- [Usage](/usage) is the full tool reference and advanced modes.
|
|
131
134
|
- [CONTRIBUTING.md](https://github.com/reticlehq/reticle/blob/main/CONTRIBUTING.md) has the development loop and the rules.
|
package/docs/autodrive.md
CHANGED
|
@@ -56,7 +56,7 @@ Two arguments of the flow replay make the branches possible, and you can use the
|
|
|
56
56
|
|
|
57
57
|
## What it needs
|
|
58
58
|
|
|
59
|
-
**A project on the Reticle platform.** Every plan includes the Harness. A new account gets
|
|
59
|
+
**A project on the Reticle platform.** Every plan includes the Harness. A new account gets free credits to try it once, a card starts a 14-day trial with 500, and the paid plan includes 1,000 credits per seat each month. Run `reticle connect` once in your app's folder, then switch the Harness on for the project (`npx @reticlehq/server config --harness on`, or the switch in the HUD). The Harness runs on the platform: planning, personas, and every choice of what to press next. A model key of your own does not drive it, and a machine with no platform link gets the refusal and the way to fix it.
|
|
60
60
|
|
|
61
61
|
| Variable | What it does |
|
|
62
62
|
| --- | --- |
|
|
@@ -68,20 +68,21 @@ Two arguments of the flow replay make the branches possible, and you can use the
|
|
|
68
68
|
|
|
69
69
|
## How it decides
|
|
70
70
|
|
|
71
|
-
The platform drives with Jev, a System One model that answers typed questions and cannot write. Every choice is a selection from candidates read off the page: which element, which tool, what consequence to claim.
|
|
71
|
+
The platform drives with Jev, a System One model that answers typed questions and cannot write. Every choice is a selection from candidates read off the page: which element, which tool, what consequence to claim. Four rules sit around those choices:
|
|
72
72
|
|
|
73
73
|
- A journey that names a page it has not reached ("then turn on automatic refunds in Settings") is not finished. The Harness goes there instead of stopping.
|
|
74
74
|
- A dialog whose destructive action already ran is closed rather than treated as the end of the journey.
|
|
75
75
|
- A control the destructive-action gate refused is a write, so the Harness always claims a request or a signal for it. A save the server rejects then fails visibly instead of passing as nothing to prove.
|
|
76
|
+
- The Harness takes an outward action (invite, send, share, delete, publish, revoke, sign out, change a role, create a key, change an item's state) only when the person's journey asks for it in so many words. Paying is not read from a button's words: "Start free trial", "Upgrade", "Subscribe" and "Place order" are pressed like any other control. Money moves when somebody types card or bank details (a field whose autocomplete is `cc-number`, `cc-csc` or `cc-exp`, or whose name or label says card number, CVC, expiry or IBAN) or leaves for a payment provider (Stripe, Dodo Payments checkout, Paddle, Lemon Squeezy, PayPal, Razorpay, Braintree). The Harness does those two only when the journey asks to pay.
|
|
76
77
|
|
|
77
78
|
## Turning it off, and who pays for it
|
|
78
79
|
|
|
79
80
|
A project linked to a Reticle workspace reads two things from it before a drive starts, and honours both:
|
|
80
81
|
|
|
81
|
-
- **The switch.**
|
|
82
|
-
- **Credits.** Harness drives and dashboard chat checks spend
|
|
82
|
+
- **The switch.** The Harness is off for a new project, because each drive spends credits. Switch it on for each project: use the Reticle Harness switch in the HUD, the dashboard (Settings → Projects), or run `npx @reticlehq/server config --harness on` in the project. While it is off, a drive refuses and tells you how to switch it on. If you switch it off while a platform drive runs, the drive stops at its next turn with `stopReason: "stopped"`, keeps what it drove, and says so in the Agent Log. The switch does not change the tools that your own agent drives with.
|
|
83
|
+
- **Credits.** Harness drives and dashboard chat checks spend credits, the same credits that pay for the rest of the platform. The platform sets the amount for your plan (see above). The HUD shows how many are left; once they are spent a drive is refused with that number and the way to get more.
|
|
83
84
|
|
|
84
|
-
If
|
|
85
|
+
If Reticle cannot read the project settings from the platform, the drive does not start. It needs a confirmed yes from the platform for the switch and the credits. Try again when the platform answers.
|
|
85
86
|
|
|
86
87
|
## What it will not do
|
|
87
88
|
|
package/docs/benchmarks.md
CHANGED
|
@@ -160,7 +160,7 @@ Measured on the observation-cost pass (numbers regenerate from `bench/raw/`). Th
|
|
|
160
160
|
| Playwright MCP | 9 / 10 | 0.91 | 1,292 | 6.97 |
|
|
161
161
|
|
|
162
162
|
- **Reticle is the only tool that caught every regression**, with zero false alarms on the control.
|
|
163
|
-
- DevTools is a
|
|
163
|
+
- DevTools is a little cheaper per look (758 against 815 tokens), **because it catches less.** Only Reticle passes the 100% catch gate, so only Reticle has a qualifying Verification Efficiency. The table shows the ungated value for the other two, for comparison only: 12.27 against 10.55 and 6.97.
|
|
164
164
|
- Playwright catches a lot but is ~1.6× more expensive per look, so its efficiency is lowest here.
|
|
165
165
|
|
|
166
166
|
### 5c. A real production app (the Reticle dashboard)
|
|
@@ -284,7 +284,7 @@ Reticle is **most efficient exactly where big companies hurt most**: a large num
|
|
|
284
284
|
- `bench/METRIC.md` holds the exact metric definitions.
|
|
285
285
|
- `bench/METHODOLOGY.md`: full design (controls, scenarios, fairness).
|
|
286
286
|
- `bench/history.jsonl`: every measured run over time.
|
|
287
|
-
- `bench/raw/` holds raw payloads and per-scenario results
|
|
287
|
+
- `bench/raw/` holds raw payloads and per-scenario results. `pnpm bench` regenerates them. Git ignores them, except the five files that a published number rests on (`schema-tax.json`, `compiled-suite-vs-replay.json`, `suite-rre.json`, `leak-stress.json`, `stress-tiers.json`).
|
|
288
288
|
|
|
289
289
|
---
|
|
290
290
|
|
package/docs/capabilities.mdx
CHANGED
|
@@ -4,7 +4,7 @@ description: Coverage, storage, fake clocks, autonomous crawling, visual diffing
|
|
|
4
4
|
icon: sparkles
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
These are the Reticle tools that do something other than look, act, observe and assert: control coverage, client storage, a fake clock, autonomous crawling, semantic and visual baselines, network mocking, flow recording and replay, and the CI artifact. Reach for them when the verify loop is not the shape of the question. They are **not advertised on the default tool surface, but they are reachable from it**: `reticle_run({ tool, args })` dispatches to any registered tool by name, advertised or not, which is the spelling used on this page. Starting the daemon with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read once at startup, so restart it) advertises
|
|
7
|
+
These are the Reticle tools that do something other than look, act, observe and assert: control coverage, client storage, a fake clock, autonomous crawling, semantic and visual baselines, network mocking, flow recording and replay, and the CI artifact. Reach for them when the verify loop is not the shape of the question. They are **not advertised on the default tool surface, but they are reachable from it**: `reticle_run({ tool, args })` dispatches to any registered tool by name, advertised or not, which is the spelling used on this page. Starting the daemon with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read once at startup, so restart it) advertises more tools outright, with their output schemas: capabilities, intent, context, storage, clock, screenshot, visual diff, network mocking, recording, flow save and replay. That surface uses the separate tool names (`reticle_sessions`, `reticle_snapshot`, `reticle_query`, `reticle_network`, `reticle_console`, `reticle_wait_for`), not `reticle_look` and the `action` forms that these docs use. The rest stay behind `reticle_run` on every surface.
|
|
8
8
|
|
|
9
9
|
Most of the time you want the verify loop. Look, act, observe, assert. These are the tools for the times you want something else, and they are the ones people are most surprised exist.
|
|
10
10
|
|