@reticlehq/server 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-ENTERPRISE +1 -1
- package/README.md +3 -3
- package/SKILL.md +12 -14
- package/dist/chromium-launch-options.d.ts +4 -3
- package/dist/chromium-launch-options.js +6 -2
- package/dist/command/cli/adhoc-suite.d.ts +22 -0
- package/dist/command/cli/adhoc-suite.js +90 -19
- package/dist/command/cli/adhoc-verdict.d.ts +35 -0
- package/dist/command/cli/adhoc-verdict.js +90 -3
- package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
- package/dist/command/cli/answers/gate-hook-message.js +6 -0
- package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
- package/dist/command/cli/auth/cloud-keystore.js +0 -3
- package/dist/command/cli/auth/cloud-session.d.ts +6 -2
- package/dist/command/cli/auth/cloud-session.js +2 -0
- package/dist/command/cli/auth/device-flow.d.ts +2 -2
- package/dist/command/cli/cli-doctor.js +7 -5
- package/dist/command/cli/cli-flow-commands.d.ts +3 -6
- package/dist/command/cli/cli-flow-commands.js +67 -4
- package/dist/command/cli/cli-parse.d.ts +8 -1
- package/dist/command/cli/cli-parse.js +24 -4
- package/dist/command/cli/cli-update-commands.js +8 -1
- package/dist/command/cli/cli-verify.d.ts +24 -3
- package/dist/command/cli/cli-verify.js +54 -11
- package/dist/command/cli/cloud-cli.js +64 -2
- package/dist/command/cli/cloud-kit.js +3 -5
- package/dist/command/cli/cloud-login.d.ts +1 -1
- package/dist/command/cli/cloud-login.js +27 -8
- package/dist/command/cli/demo/demo-page.js +6 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
- package/dist/command/cli/launch/cli-launch.d.ts +8 -0
- package/dist/command/cli/launch/cli-launch.js +21 -9
- package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
- package/dist/command/cli/launch/sdk-version-match.js +206 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
- package/dist/command/cli/ports/resolve/cli-port.d.ts +25 -0
- package/dist/command/cli/ports/resolve/cli-port.js +125 -15
- package/dist/command/cli/setup-mcp-cli.js +21 -13
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +21 -7
- package/dist/command/cli.js +79 -11
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +14 -2
- package/dist/command/daemon/binding/daemon-status-probe.js +34 -6
- package/dist/command/daemon/daemon-resolve.d.ts +9 -0
- package/dist/command/daemon/daemon-resolve.js +1 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
- package/dist/command/dev/stale-issue-guard.d.ts +3 -4
- package/dist/command/dev/stale-issue-guard.js +8 -8
- package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
- package/dist/command/setup/bringup/connect-lease.js +38 -0
- package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
- package/dist/command/setup/bringup/dev-server-files.js +131 -0
- package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +2 -0
- package/dist/command/setup/bringup/dev-server-wait.js +1 -1
- package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
- package/dist/command/setup/bringup/ensure-daemon.js +135 -16
- package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
- package/dist/command/setup/bringup/static-page-server.js +151 -0
- package/dist/command/setup/desktop-shape.d.ts +4 -0
- package/dist/command/setup/desktop-shape.js +18 -2
- package/dist/command/setup/init/init-port.d.ts +15 -0
- package/dist/command/setup/init/init-port.js +44 -0
- package/dist/command/setup/init/init-runtime.d.ts +1 -1
- package/dist/command/setup/init/init-runtime.js +91 -9
- package/dist/command/setup/node-effects.d.ts +27 -14
- package/dist/command/setup/node-effects.js +86 -45
- package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
- package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
- package/dist/command/setup/probe/page-probe.d.ts +13 -2
- package/dist/command/setup/probe/page-probe.js +18 -3
- package/dist/command/setup/run-setup.d.ts +28 -1
- package/dist/command/setup/run-setup.js +103 -15
- package/dist/command/setup/session-pick.d.ts +13 -0
- package/dist/command/setup/session-pick.js +20 -0
- package/dist/command/setup/setup-command.d.ts +2 -14
- package/dist/command/setup/setup-command.js +105 -31
- package/dist/command/setup/setup-mcp.d.ts +2 -0
- package/dist/command/setup/setup-mcp.js +17 -8
- package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
- package/dist/command/setup/terminal/wait-progress.js +17 -0
- package/dist/command/version/peer-announce.d.ts +6 -2
- package/dist/command/version/peer-announce.js +8 -6
- package/dist/command/version/version-nudge.d.ts +17 -3
- package/dist/command/version/version-nudge.js +25 -4
- package/dist/features/crawl/crawl-empty.js +1 -1
- package/dist/features/crawl/crawl-tools.d.ts +6 -0
- package/dist/features/crawl/crawl-tools.js +57 -0
- package/dist/features/crawl/crawl.js +171 -157
- package/dist/features/exhaust/app-coverage.d.ts +33 -0
- package/dist/features/exhaust/app-coverage.js +31 -0
- package/dist/features/exhaust/code-coverage.d.ts +47 -0
- package/dist/features/exhaust/code-coverage.js +77 -0
- package/dist/features/exhaust/exhaust-run.d.ts +33 -0
- package/dist/features/exhaust/exhaust-run.js +82 -0
- package/dist/features/exhaust/explorer.d.ts +103 -0
- package/dist/features/exhaust/explorer.js +195 -0
- package/dist/features/exhaust/failure-branches.d.ts +33 -0
- package/dist/features/exhaust/failure-branches.js +92 -0
- package/dist/features/exhaust/ledger.d.ts +202 -0
- package/dist/features/exhaust/ledger.js +252 -0
- package/dist/features/exhaust/session-fold.d.ts +31 -0
- package/dist/features/exhaust/session-fold.js +79 -0
- package/dist/features/exhaust/session-port.d.ts +36 -0
- package/dist/features/exhaust/session-port.js +102 -0
- package/dist/features/harness/drive-report.d.ts +0 -9
- package/dist/features/harness/drive-report.js +18 -0
- package/dist/features/harness/driver.js +4 -7
- package/dist/features/harness/fill-values.d.ts +10 -0
- package/dist/features/harness/fill-values.js +50 -11
- package/dist/features/harness/jev-driver.js +30 -13
- package/dist/features/harness/openai-driver.d.ts +12 -0
- package/dist/features/harness/openai-driver.js +21 -12
- package/dist/features/harness/platform-config.js +5 -6
- package/dist/features/license/license.d.ts +2 -2
- package/dist/index.d.ts +9 -0
- package/dist/index.js +56 -19
- package/dist/judgement/capsule/capsule-store.d.ts +4 -4
- package/dist/judgement/domain/domain-model.js +3 -1
- package/dist/judgement/oracles/self-instrument.js +4 -2
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
- package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
- package/dist/judgement/runs/verification-sync.d.ts +3 -1
- package/dist/judgement/runs/verification-sync.js +1 -1
- package/dist/language/flows/change/affected.d.ts +9 -0
- package/dist/language/flows/change/affected.js +35 -0
- package/dist/language/flows/change/gate.d.ts +20 -0
- package/dist/language/flows/change/gate.js +12 -1
- package/dist/language/flows/flow-author.d.ts +7 -0
- package/dist/language/flows/flow-author.js +19 -0
- package/dist/language/flows/flow-expect-grammar.js +1 -1
- package/dist/language/flows/flow-journey.d.ts +54 -0
- package/dist/language/flows/flow-journey.js +119 -0
- package/dist/language/flows/flow-replay-run.d.ts +2 -3
- package/dist/language/flows/flow-replay-run.js +13 -47
- package/dist/language/flows/flow-replay.d.ts +22 -8
- package/dist/language/flows/flow-replay.js +55 -9
- package/dist/language/flows/flow-result.d.ts +2 -0
- package/dist/language/flows/flow-result.js +25 -0
- package/dist/language/flows/flow-tools.js +17 -5
- package/dist/language/flows/flows.d.ts +10 -1
- package/dist/language/flows/flows.js +90 -14
- package/dist/language/flows/recording/replay-halt.js +3 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +30 -1
- package/dist/language/flows/recording/tape/recordings.js +55 -1
- package/dist/language/flows/replay.d.ts +19 -0
- package/dist/language/flows/replay.js +42 -6
- package/dist/language/flows/verify-change-tools.js +5 -2
- package/dist/launch-chromium.d.ts +80 -0
- package/dist/launch-chromium.js +218 -0
- package/dist/memory/cloud/cloud-config.d.ts +11 -2
- package/dist/memory/cloud/cloud-config.js +47 -7
- package/dist/memory/cloud/cloud-sync.d.ts +4 -1
- package/dist/memory/cloud/cloud-sync.js +6 -9
- package/dist/memory/cloud/harness-offer.js +5 -6
- package/dist/memory/cloud/harness-switch.js +5 -6
- package/dist/memory/cloud/sync-cycle.d.ts +77 -3
- package/dist/memory/cloud/sync-cycle.js +325 -55
- package/dist/memory/cloud/sync-daemon.js +24 -20
- package/dist/memory/cloud/sync-disk.js +16 -2
- package/dist/memory/intent/intent-shard.d.ts +10 -32
- package/dist/memory/intent/intent-shard.js +0 -10
- package/dist/memory/intent/intent-tools.js +8 -3
- package/dist/memory/journal/drive-flow.d.ts +18 -13
- package/dist/memory/journal/drive-flow.js +45 -37
- package/dist/memory/journal/envelope.d.ts +8 -8
- package/dist/memory/journal/journal-query.d.ts +14 -0
- package/dist/memory/journal/journal-query.js +15 -0
- package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
- package/dist/memory/journal/session-end.d.ts +9 -1
- package/dist/memory/journal/session-end.js +24 -12
- package/dist/memory/project/artifact-root-resolver.d.ts +10 -0
- package/dist/memory/project/artifact-root-resolver.js +46 -19
- package/dist/memory/project/dir/reticle-dir.d.ts +2 -0
- package/dist/memory/project/dir/reticle-dir.js +1 -0
- package/dist/memory/project/project-tools.js +3 -1
- package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
- package/dist/portal/bridge/auth-failure-reason.js +4 -2
- package/dist/portal/bridge/bridge.d.ts +12 -3
- package/dist/portal/bridge/bridge.js +49 -3
- package/dist/portal/input/js-coverage.d.ts +25 -0
- package/dist/portal/input/js-coverage.js +30 -0
- package/dist/portal/input/real-input.d.ts +11 -0
- package/dist/portal/input/real-input.js +29 -4
- package/dist/portal/input/viewport-tools.js +16 -6
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +20 -13
- package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
- package/dist/portal/session/dev-server/dev-command.js +30 -12
- package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
- package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
- package/dist/portal/session/dev-server/route-status-probe.js +57 -0
- package/dist/portal/session/facts/observed-state.d.ts +11 -0
- package/dist/portal/session/facts/observed-state.js +34 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
- package/dist/portal/session/no-session-diagnosis.js +62 -5
- package/dist/portal/session/no-session-next-action.d.ts +13 -0
- package/dist/portal/session/no-session-next-action.js +48 -9
- package/dist/portal/session/no-session-watch.d.ts +18 -0
- package/dist/portal/session/no-session-watch.js +88 -2
- package/dist/portal/session/session-health.js +3 -2
- package/dist/portal/session/session-manager.d.ts +20 -6
- package/dist/portal/session/session-manager.js +19 -3
- package/dist/portal/session/session-reaper.d.ts +28 -0
- package/dist/portal/session/session-reaper.js +49 -1
- package/dist/portal/session/session.d.ts +10 -8
- package/dist/portal/session/session.js +24 -32
- package/dist/surface/http-server.d.ts +3 -2
- package/dist/surface/http-server.js +8 -5
- package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
- package/dist/surface/mcp/mcp-proxy.js +2 -2
- package/dist/surface/mcp/mcp.js +5 -3
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
- package/dist/surface/mcp/proxy/proxy-log.js +3 -5
- package/dist/surface/mcp/server-instructions.js +9 -7
- package/dist/surface/tools/act/after-match.d.ts +17 -0
- package/dist/surface/tools/act/after-match.js +30 -0
- package/dist/surface/tools/act/durable.d.ts +69 -0
- package/dist/surface/tools/act/durable.js +139 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +13 -6
- package/dist/surface/tools/act/navigation/navigate-arrival.js +47 -10
- package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
- package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
- package/dist/surface/tools/act/proved-controls.d.ts +9 -0
- package/dist/surface/tools/act/proved-controls.js +11 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
- package/dist/surface/tools/act/sequence-grade.js +32 -1
- package/dist/surface/tools/act-capsule.d.ts +7 -0
- package/dist/surface/tools/act-capsule.js +46 -1
- package/dist/surface/tools/act-sequence-tool.js +25 -8
- package/dist/surface/tools/act-tools.js +28 -52
- package/dist/surface/tools/assert/assert-grade.js +12 -1
- package/dist/surface/tools/assert/assert-verdict.js +2 -2
- package/dist/surface/tools/browser-tools.js +14 -2
- package/dist/surface/tools/coverage-tools.js +27 -1
- package/dist/surface/tools/dynamic-tools.js +29 -2
- package/dist/surface/tools/error-recovery.js +5 -6
- package/dist/surface/tools/feedback-invite.js +6 -4
- package/dist/surface/tools/harness-explore.d.ts +3 -2
- package/dist/surface/tools/harness-explore.js +12 -7
- package/dist/surface/tools/invoke-tool.js +12 -8
- package/dist/surface/tools/lease-tools.js +15 -6
- package/dist/surface/tools/live-call-text.js +4 -1
- package/dist/surface/tools/observe-tools.js +9 -1
- package/dist/surface/tools/tool-kit.d.ts +10 -0
- package/dist/surface/tools/tools.js +4 -0
- package/dist/telemetry/argument-shape.d.ts +0 -2
- package/dist/telemetry/argument-shape.js +1 -5
- package/dist/telemetry/error-class.d.ts +2 -3
- package/dist/telemetry/error-class.js +2 -3
- package/dist/telemetry/harness-drive.d.ts +0 -2
- package/dist/telemetry/harness-drive.js +0 -4
- package/dist/telemetry/onboarding-firsts.d.ts +2 -2
- package/dist/telemetry/onboarding-firsts.js +2 -2
- package/dist/wire-journal.d.ts +3 -0
- package/dist/wire-journal.js +4 -0
- package/dist/workspace-packages.d.ts +0 -2
- package/dist/workspace-packages.js +1 -9
- package/docs/README.md +4 -2
- package/docs/actions.mdx +3 -4
- package/docs/agent-cheatsheet.md +4 -4
- package/docs/architecture.md +6 -6
- package/docs/best-practices.mdx +1 -1
- package/docs/capabilities.mdx +32 -1
- package/docs/cli/cloud.mdx +20 -8
- package/docs/cli/doctor.mdx +4 -0
- package/docs/cli/feedback.mdx +2 -2
- package/docs/cli/gate.mdx +9 -4
- package/docs/cli/update.mdx +10 -0
- package/docs/cli.mdx +22 -7
- package/docs/desktop.mdx +14 -10
- package/docs/environment.mdx +87 -0
- package/docs/faq.mdx +18 -16
- package/docs/first-drive.md +5 -13
- package/docs/flows.md +21 -2
- package/docs/frameworks.mdx +29 -10
- package/docs/getting-started.md +15 -5
- package/docs/http-transport.md +6 -3
- package/docs/human-control.md +2 -2
- package/docs/index.mdx +16 -6
- package/docs/install-agentic.mdx +2 -2
- package/docs/install-manual.mdx +1 -1
- package/docs/integration-patterns.md +3 -3
- package/docs/multi-agent-testing.md +1 -1
- package/docs/packages/babel-plugin.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/next.mdx +3 -0
- package/docs/packages/react.mdx +2 -2
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/vite-plugin.mdx +4 -2
- package/docs/packages.mdx +9 -9
- package/docs/predicates.mdx +50 -5
- package/docs/quickstart.mdx +12 -10
- package/docs/recipes.mdx +2 -2
- package/docs/state-management.mdx +2 -2
- package/docs/system-map.md +1 -1
- package/docs/telemetry.md +1 -1
- package/docs/token-efficiency.md +3 -3
- package/docs/tools/act-and-wait.mdx +1 -0
- package/docs/tools/act-sequence.mdx +1 -1
- package/docs/tools/act.mdx +1 -1
- package/docs/tools/console.mdx +1 -1
- package/docs/tools/inspect.mdx +1 -1
- package/docs/tools/network.mdx +1 -1
- package/docs/tools/query.mdx +1 -1
- package/docs/tools/sessions.mdx +1 -1
- package/docs/tools/snapshot.mdx +1 -1
- package/docs/tools/state.mdx +1 -1
- package/docs/tools/wait-for.mdx +1 -1
- package/docs/usage.md +7 -7
- package/docs/use-cases.mdx +157 -0
- package/docs/vs-screenshots.mdx +3 -3
- package/docs/what-is-recorded.md +2 -2
- package/docs/what-is-reticle.mdx +17 -20
- package/docs/why-reticle.mdx +14 -21
- package/package.json +10 -9
- package/dist/judgement/capsule/minimize.d.ts +0 -12
- package/dist/judgement/capsule/minimize.js +0 -22
- package/dist/judgement/phenomena/phenomena.d.ts +0 -27
- package/dist/judgement/phenomena/phenomena.js +0 -101
- package/dist/language/flows/flow-report.d.ts +0 -22
- package/dist/language/flows/flow-report.js +0 -106
package/docs/first-drive.md
CHANGED
|
@@ -46,18 +46,10 @@ If `~/.reticle/pairing-token` does not exist yet, run any `reticle` command once
|
|
|
46
46
|
|
|
47
47
|
This is the shortest path to seeing Reticle work, and it needs no MCP client at all.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Ask for a verdict, naming the app's url. With no tab open on that origin, Reticle opens one in a browser it drives (Playwright's Chromium, or an installed Chrome or Edge when that is missing) and closes it afterwards; a tab you already have open is used instead:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
node
|
|
53
|
-
.then(b=>b.newPage().then(p=>p.goto('http://localhost:4310/')
|
|
54
|
-
.then(()=>new Promise(r=>setTimeout(r,60000)))))" &
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Then ask for a verdict:
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
node server/dist/command/cli.js verify "" --port 4400 \
|
|
52
|
+
node server/dist/command/cli.js verify http://localhost:4310/ --port 4400 \
|
|
61
53
|
--expect '{"kind":"text","contains":"Reticle"}'
|
|
62
54
|
```
|
|
63
55
|
|
|
@@ -72,7 +64,7 @@ That is the whole product in one line: a claim, checked against a running page,
|
|
|
72
64
|
Try making it fail, because a tool that only ever says yes is not telling you anything:
|
|
73
65
|
|
|
74
66
|
```bash
|
|
75
|
-
node server/dist/command/cli.js verify
|
|
67
|
+
node server/dist/command/cli.js verify http://localhost:4310/ --port 4400 \
|
|
76
68
|
--expect '{"kind":"text","contains":"definitely not on this page"}'
|
|
77
69
|
```
|
|
78
70
|
|
|
@@ -80,8 +72,8 @@ node server/dist/command/cli.js verify "" --port 4400 \
|
|
|
80
72
|
|
|
81
73
|
If your editor has the Reticle MCP server, the same app is now drivable. The four calls worth knowing, in the order you actually use them:
|
|
82
74
|
|
|
83
|
-
- `
|
|
84
|
-
- `
|
|
75
|
+
- `reticle_look { action: "page" }`: what is on the page
|
|
76
|
+
- `reticle_look { action: "find", by: "testid", value: "..." }`: find one thing, cheaply
|
|
85
77
|
- `reticle_act_and_wait { ref, action, until }` (**the one that produces a verdict**)
|
|
86
78
|
- `reticle_observe`: everything the page did, when you need the evidence
|
|
87
79
|
|
package/docs/flows.md
CHANGED
|
@@ -87,6 +87,25 @@ reticle_flow {action:"load"}({ flowName: "create-task" }) // → the flow JSON
|
|
|
87
87
|
reticle_flow_replay({ flowName: "create-task" }) // re-resolve each anchor against the LIVE DOM, run it
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
### Resume from a step
|
|
91
|
+
|
|
92
|
+
```jsonc
|
|
93
|
+
reticle_flow_replay({ flowName: "create-task", from: 3 }) // by 0-based index
|
|
94
|
+
reticle_flow_replay({ flowName: "create-task", from: "submit" }) // or by a step's `id`
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
There is no state to restore, so the steps before `from` run again quickly as setup: their actions run, their `expect`s are not checked, and they are not reported. Replay is then checked and reported from `from` on. If a setup step fails, it is reported, because the resume never reached the step you asked for. A setup step declared `"effect": "commits"` refuses the resume and the whole flow replays, so resuming never re-sends a payment or a message silently. A `from` that names no step is an error, not a full replay.
|
|
98
|
+
|
|
99
|
+
## Flows saved from a drive
|
|
100
|
+
|
|
101
|
+
Every session's driving is saved as a flow at teardown when it declared a consequence. These flows:
|
|
102
|
+
|
|
103
|
+
- are named from the `intent` you pass to `reticle_act_and_wait` (`drive-close-an-issue`), and carry it as the flow's `intent`;
|
|
104
|
+
- merge: another drive that starts on the same page and takes the same steps updates the existing file instead of adding a copy. The existing flow keeps its name and everything it already says; the new drive only fills gaps. Flows you named yourself are never merged;
|
|
105
|
+
- record, on every step, the `page` it ran on and the `endPage` it led to;
|
|
106
|
+
- declare `requires` (the start page) and `ensures` (the end page) as route claims, so `canFollow` can tell which flows chain. Replay treats a required start page as met by navigating there;
|
|
107
|
+
- carry `author: { agent, person }`: the MCP client that drove it and the email of whoever ran `reticle login` on that machine. Either is left out when it is not known.
|
|
108
|
+
|
|
90
109
|
## Delete a flow
|
|
91
110
|
|
|
92
111
|
```jsonc
|
|
@@ -105,7 +124,7 @@ A renamed or obsolete flow otherwise lingers in `reticle_flow {action:"list"}` a
|
|
|
105
124
|
- `drift`: an anchor missed (a testid was renamed, or a signal never fired). The result is **legible**, never a blind failure: `{ step, anchor, drift: { reasonKind: "testid_not_found", nearest: "send-message" } }`. (This is the "whose fault is it" principle.)
|
|
106
125
|
- `error`: the flow file is missing/invalid, or a resolved action failed. Runtime failures include the failed step and a top-level error envelope.
|
|
107
126
|
|
|
108
|
-
A testid-_preserving_ refactor (you moved markup but kept the testids) still replays green. A step whose element has **no testid** is anchored on its component + source location (`{ kind: "component", component, source: { file, line } }`), an auto-derived stable anchor, so a flow records cleanly with zero hand-added testids and replay re-resolves it via `
|
|
127
|
+
A testid-_preserving_ refactor (you moved markup but kept the testids) still replays green. A step whose element has **no testid** is anchored on its component + source location (`{ kind: "component", component, source: { file, line } }`), an auto-derived stable anchor, so a flow records cleanly with zero hand-added testids and replay re-resolves it via `reticle_look { action: "find" } by:'component'`.
|
|
109
128
|
|
|
110
129
|
### The decision envelope: what to do next, not just pass/fail
|
|
111
130
|
|
|
@@ -175,7 +194,7 @@ With `apply: false` the flow file is **never modified**; you get the proposed di
|
|
|
175
194
|
| `reticle_flow {action:"list"}` | `{}` | flows on disk |
|
|
176
195
|
| `reticle_flow {action:"load"}` | `{ flowName }` | the flow JSON |
|
|
177
196
|
| `reticle_flow {action:"delete"}` | `{ flowName }` | `{ deleted: true }`, or `{ error, code }` (`not_found` when no such flow) |
|
|
178
|
-
| `reticle_flow_replay` | `{ flowName }` | `{ status, steps, decision? }` (decision on drift/fail) |
|
|
197
|
+
| `reticle_flow_replay` | `{ flowName, from?, sweep? }` | `{ status, steps, decision? }` (decision on drift/fail); `from` resumes at a step |
|
|
179
198
|
| `reticle_verify {action:"flows"}` | `{ names?, sessionId? }` | suite verdict `{ status, passed, failed, failures[] }` |
|
|
180
199
|
| `reticle_verify { action: "heal" }` | `{ flowName, apply? }` | propose / apply nearest-match rebind |
|
|
181
200
|
| `reticle_annotate` | `{ kind, … }` | compile a structured annotation into the flow |
|
package/docs/frameworks.mdx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'Frameworks: proven, wired, and the difference'
|
|
3
3
|
sidebarTitle: Frameworks
|
|
4
|
-
description: Which frameworks Reticle supports, which are wired but unverified, and the exact wiring for each one. React, Next.js, Remix, Astro, SvelteKit, TanStack Start, Nuxt, Vue and plain HTML.
|
|
4
|
+
description: Which frameworks Reticle supports, which are wired but unverified, and the exact wiring for each one. React, Next.js, Remix, Astro, SvelteKit, TanStack Start, Nuxt, Vue, Angular and plain HTML.
|
|
5
5
|
icon: layer-group
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
**React + Vite, Next.js, Remix and Astro are fully supported, each with an example app driven to a verdict in CI. Astro, SvelteKit, Nuxt, React Router framework mode, Create React App and
|
|
8
|
+
**React + Vite, Next.js, Remix and Astro are fully supported, each with an example app driven to a verdict in CI. Astro, SvelteKit, Nuxt, React Router framework mode, Create React App, Vue and Angular have their INSTALL gated: the install gate scaffolds each from scratch on every change and requires a real session, but no gate drives them to a verdict. TanStack Start, Preact and plain HTML are wired by `init` and documented but not gated at all, so treat them as plausible rather than proven.** The exact wiring for each is below.
|
|
9
9
|
|
|
10
10
|
Reticle is a verification layer that embeds a dev-only SDK in your running web app so an AI coding agent can prove a change works. Its core is framework-neutral: DOM, network, console, routing, animations and source mapping work anywhere JavaScript runs.
|
|
11
11
|
|
|
@@ -32,9 +32,10 @@ Two columns say different things and it is worth keeping them apart. **CI gate**
|
|
|
32
32
|
| **Nuxt** | yes | no | install only | unproven | Install gated, drive unverified |
|
|
33
33
|
| **React Router (framework mode)** | yes | no | install only | yes | Install gated, drive unverified |
|
|
34
34
|
| **Create React App** | yes | no | install only | unproven | Install gated, drive unverified |
|
|
35
|
+
| **Angular 17+** | yes | no | install only | no | Install gated, drive unverified |
|
|
35
36
|
| **Plain HTML** | yes | no | no | no | Wired, unverified |
|
|
36
37
|
| **Vue (non-Nuxt)** | as Vite | no | install only | no | Install gated, drive unverified |
|
|
37
|
-
| **Preact** | as Vite | no | no | unproven | Sensor, React adapter via `preact/compat` |
|
|
38
|
+
| **Preact** | as Vite | no | no | transform-tested; drive unproven | Sensor, React adapter via `preact/compat` |
|
|
38
39
|
| **Server-rendered (Django, Flask, FastAPI, Streamlit, Rails)** | yes | no | no | no | Wired, unverified |
|
|
39
40
|
|
|
40
41
|
<Warning>
|
|
@@ -86,9 +87,9 @@ module.exports = withReticle({/* your config */});
|
|
|
86
87
|
|
|
87
88
|
## Remix and Astro
|
|
88
89
|
|
|
89
|
-
Remix
|
|
90
|
+
Remix v2 on Vite is its own framework to `init`, not plain Vite. Remix SSRs its own HTML, so the plugin's `index.html` injection never reaches the page and the connect comes from `app/entry.client.tsx` instead, the same way as React Router framework mode. `init` adds the dev-only connect line to that entry, or writes the file when the app has none: `app/entry.client.tsx` only exists after `remix reveal`, so the written file is Remix's own default entry (`RemixBrowser`, `hydrateRoot`) with the one line added. The Vite plugin, `reticle()` in the `vite.config` that carries `vitePlugin`, stamps `file:line` and serves the connect module the entry imports. The classic Remix compiler (no `vite.config`) gets a manual step rather than a green plan, because there is no plugin to serve that module.
|
|
90
91
|
|
|
91
|
-
Astro
|
|
92
|
+
Astro uses the Vite plugin (wire it with `inject: false` if you want source pointers on JSX islands) and uses a different channel for the pairing token. `vite.define` does not reach Astro's client pipeline on 7.2+, so the identifier stays literal and the bridge refuses the dial. `init` reads the token in frontmatter and puts it on a `<meta name="reticle-pairing-token">`. Reading it per request also means a token written after the dev server started is picked up on the next load, with no restart.
|
|
92
93
|
|
|
93
94
|
Use a processed page `<script>` with a static SDK import. Vite owns that module graph from the first transform:
|
|
94
95
|
|
|
@@ -155,7 +156,7 @@ useEffect(() => {
|
|
|
155
156
|
}, []);
|
|
156
157
|
```
|
|
157
158
|
|
|
158
|
-
`init`
|
|
159
|
+
`init` writes this for you: a `src/reticle-connect.tsx` client effect that connects, declares `__RETICLE_TOKEN__` so `tsc` passes, and loads `src/reticle-dev.ts`, plus `<ReticleConnect />` rendered before `<Scripts />` in `src/routes/__root.tsx`. When the root route is not a shape it can patch, it prints a recipe naming the real parts (`shellComponent`, `<Scripts />`) instead. There is no example app and no CI gate, so treat a connected session as the proof, not the plan.
|
|
159
160
|
|
|
160
161
|
## Nuxt
|
|
161
162
|
|
|
@@ -217,6 +218,10 @@ Detected as its own UI library, because a Preact app using `preact/compat` alias
|
|
|
217
218
|
|
|
218
219
|
The sensor works. If you use `preact/compat`, the React adapter can attach through the alias, but that path is not gated.
|
|
219
220
|
|
|
221
|
+
**The source-pointer transform works on Preact code, and that is measured rather than assumed.** The stamper is JSX-syntactic: it tags a host element by its tag name and reads nothing about React, so a Preact `.jsx` or `.tsx` file is stamped exactly as a React one is. `class` instead of `className`, a `@jsxImportSource preact` pragma and a `@preact/signals` value in the tree all leave it unmoved. There are tests for each of those in `adapters/build/babel-plugin`. They prove the Babel transform, not the `@preact/preset-vite` pipeline end to end: nobody has yet driven a Preact app and seen a verdict name a `file:line`, so the drive stays unproven until someone does.
|
|
222
|
+
|
|
223
|
+
What is still missing on Preact is the enrichment above the DOM: `reticle_inspect` returns no `component`/`componentStack`, `@preact/signals` has no `reticle_state` adapter, and the render meter counts React commits only. Tracked in [#129](https://github.com/reticlehq/reticle/issues/129).
|
|
224
|
+
|
|
220
225
|
## Create React App and plain bundlers
|
|
221
226
|
|
|
222
227
|
No config file to patch, so `connect()` goes in your entry module.
|
|
@@ -231,14 +236,28 @@ The same shape works for webpack, Parcel, and the Vue and Svelte CLIs.
|
|
|
231
236
|
|
|
232
237
|
## Plain HTML
|
|
233
238
|
|
|
239
|
+
A directory with an `index.html` and no `package.json` is wired by `init` itself: it writes the connect snippet into `index.html`, before `</body>`, writes `.reticle.json`, and hands over to setup. The block is marked, so a re-run finds it and never writes a second one, and a re-run with a new `--port` rewrites the port inside it. It loads the SDK from a CDN and runs only when the page is opened on `localhost`, `127.0.0.1` or `[::1]`: a static file has no development build to strip it, so the host is the only line between the snippet and a published page. The page carries no pairing token: `init` writes it into `reticle.local.js` beside the page, which the snippet imports, and adds that file to `.gitignore`. Keep it out of anything you publish, and let each teammate run `reticle init` for their own. Several pages and no `index.html` is a choice `init` does not make for you: it prints the snippet, filled in with your project id and token, for you to paste:
|
|
240
|
+
|
|
234
241
|
```html
|
|
235
242
|
<script type="module">
|
|
236
|
-
import { reticle } from '/
|
|
237
|
-
reticle.connect();
|
|
243
|
+
import { reticle } from 'https://cdn.jsdelivr.net/npm/@reticlehq/browser@<version>/+esm';
|
|
244
|
+
reticle.connect({ projectId: '<your-project-id>', token: '<pairing token>' });
|
|
238
245
|
</script>
|
|
239
246
|
```
|
|
240
247
|
|
|
241
|
-
|
|
248
|
+
A bare `/node_modules/...` import does not work here: the SDK's own `dist` imports bare specifiers, which a browser cannot resolve without a bundler. A page with a `package.json` and a bundler takes the entry-module path above instead.
|
|
249
|
+
|
|
250
|
+
## Angular
|
|
251
|
+
|
|
252
|
+
Angular 17 and later, on the Vite-based `@angular/build` dev server. `init` installs only `@reticlehq/browser`, the framework-neutral sensor, and makes three edits:
|
|
253
|
+
|
|
254
|
+
- **The browser entry** (`src/main.ts`, or whatever `angular.json` names) gets a connect guarded on Angular's `ngDevMode` global (`typeof ngDevMode === 'undefined' || ngDevMode`), through a dynamic `import('@reticlehq/browser')` appended after the bootstrap. A production `ng build` defines `ngDevMode` as `false`, so the build drops the block and the chunk with it. The output carries no Reticle code at all, and the app never waits on Reticle. `isDevMode()` is not used because the build cannot fold a function call; an entry wired that way by an older `init` is rewritten in place on the next run.
|
|
255
|
+
- **`reticle.proxy.mjs`**, beside `angular.json`, answers one same-origin route, `/__reticle/pairing-token`, with this machine's pairing token, read from `~/.reticle/pairing-token` at request time. It refuses a request that did not arrive over loopback or came from another origin.
|
|
256
|
+
- **`angular.json`** gets the serve target's `proxyConfig` pointed at that file. `ng serve` loads it; `ng build` never reads a proxy config, so a production bundle has no route to the token and the token is never written into the project.
|
|
257
|
+
|
|
258
|
+
The token takes that route because the Angular CLI has no config file that runs code: a literal in `angular.json` commits a per-machine secret, and a gitignored module imported from `main.ts` is bundled into `ng build` whether or not anything can reach it. An existing `proxyConfig` is yours and is never replaced: the step goes manual and names the entry to add to it. A workspace with several served applications is also manual, because which one is the app is not `init`'s to guess.
|
|
259
|
+
|
|
260
|
+
You get DOM, network, console, routing and state tools. You do not get component identity (that is the React adapter) or a `source` field (the stamper covers JSX and Svelte). The install gate scaffolds an Angular app (no SSR) on every change and requires a session; SSR was proven by hand, and nothing drives an Angular app to a verdict.
|
|
242
261
|
|
|
243
262
|
## Server-rendered apps (Django, Flask, FastAPI, Rails)
|
|
244
263
|
|
|
@@ -327,7 +346,7 @@ Electron and Tauri invert the direction: your app dials the daemon, and there is
|
|
|
327
346
|
|
|
328
347
|
## Not supported
|
|
329
348
|
|
|
330
|
-
|
|
349
|
+
Solid appears in dependency detection but has no wiring, no adapter and no gate. The sensor may work if you call `connect()` yourself. Nothing proves it, so we are not going to list them as supported.
|
|
331
350
|
|
|
332
351
|
## Frequently asked questions
|
|
333
352
|
|
package/docs/getting-started.md
CHANGED
|
@@ -4,7 +4,7 @@ description: 'Zero to your agent verifying your real app, step by step, with wor
|
|
|
4
4
|
icon: rocket
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
**To get started with Reticle: run the installer,
|
|
7
|
+
**To get started with Reticle Cloud: run the installer, then `reticle connect --project "My App"` in your app directory before opening your coding agent.** The installer puts the CLI on the machine and registers its MCP tools with supported agents. `connect` wires your app, checks that a browser session connected, opens sign-in approval if needed, links the named cloud project, and syncs existing history. The first run is what proves anything: ask your agent to verify one journey in your app, or use `reticle_verify { action: "explore", persona: "<the journey worth proving>" }`. A connected app is not a verified one. For local-only verification without an account, use `reticle init` instead. You need Node 20.11 or newer, an app you run locally, and an agent that speaks MCP.
|
|
8
8
|
|
|
9
9
|
> **Looking for the fast path?** [Quickstart](/quickstart) gets you to a real verdict in five minutes, and every response on it was captured live. [Agentic install](/install-agentic) and [Manual install](/install-manual) cover setup in detail, per agent and per framework.
|
|
10
10
|
>
|
|
@@ -58,7 +58,17 @@ Everything is **dev-only** and **localhost-only**. It's tree-shaken out of produ
|
|
|
58
58
|
|
|
59
59
|
---
|
|
60
60
|
|
|
61
|
-
##
|
|
61
|
+
## Cloud setup: `reticle connect`
|
|
62
|
+
|
|
63
|
+
After the one-time [installer](/quickstart), run this in your app directory:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx @reticlehq/server connect --project "My App"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Approve the short browser code if Reticle asks you to sign in. The command then links this folder and sends any history already on the machine. Open or restart your coding agent and ask it to verify one real journey. For a new project, the dashboard has no result until that first run is recorded.
|
|
70
|
+
|
|
71
|
+
## Local-only path: `reticle init`
|
|
62
72
|
|
|
63
73
|
From your project root:
|
|
64
74
|
|
|
@@ -78,7 +88,7 @@ The bridge + MCP server is a single process that serves all your projects, so it
|
|
|
78
88
|
|
|
79
89
|
Re-running is safe: already-registered and already-patched steps are skipped, and on a wired project it goes straight to proving the app still works. Preview without writing via `npx @reticlehq/server init --dry-run`.
|
|
80
90
|
|
|
81
|
-
`init` does not stop at writing files. It starts your dev server
|
|
91
|
+
`init` does not stop at writing files. It starts your dev server, opens the app, and waits for a session to connect from inside it. That connection is the whole proof that onboarding worked. If a dev server was already running without Reticle, restart it once: it read the build config before `init` edited it. It exits non-zero if nothing connected and prints what is left to do. It does NOT drive. Then prove a flow. That is the FIRST RUN, and it is a separate call: `reticle_verify { action: "explore", persona: "<who does what>" }`. It drives with a model inside the daemon and records what it drove, so later checks replay it with no model in the loop.
|
|
82
92
|
|
|
83
93
|
These carry what the command cannot work out for itself:
|
|
84
94
|
|
|
@@ -90,7 +100,7 @@ These carry what the command cannot work out for itself:
|
|
|
90
100
|
|
|
91
101
|
The rest are dials: `--license <key>` (writes it to `.env` and keeps `.env` out of git), `--json` (one object for an agent to read), `--files-only` (write, register, pre-approve, and stop, which is what `init` did before it learned to boot the app, and what an existing install re-runs to pick up new wiring), `--relaunch` (prints the command that restarts the conversation you are in, with the tools loaded: the restart step most installs stall on, and it composes with `--files-only`), `--no-open`, `--dry-run`, `--port N`, `--no-mcp`, `--no-install`.
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
If you ran `--files-only` with a dev server already running, restart it, then skip to [Step 4](#step-4-run-it-and-verify-the-connection). The manual steps below explain what `init` sets up, if you prefer to wire it yourself.
|
|
94
104
|
|
|
95
105
|
---
|
|
96
106
|
|
|
@@ -464,7 +474,7 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
|
|
|
464
474
|
|
|
465
475
|
**You do not get** component identity. `@reticlehq/react` walks the fiber tree to answer "which component rendered this element"; there is no Svelte equivalent, so snapshots carry the file and line but no component name. Stamping targets Svelte 5's compiler AST and also accepts Svelte 4's; `.svelte.ts` runes modules are code rather than markup and are not stamped.
|
|
466
476
|
|
|
467
|
-
**It is
|
|
477
|
+
**It is install-gated, drive-unverified.** The install gate scaffolds a SvelteKit app from scratch, runs `init`, boots it and waits for a session, so the setup is proven. There is no SvelteKit example app that CI drives to a verdict, so the drive is not. [Frameworks](/frameworks) has the full status table.
|
|
468
478
|
|
|
469
479
|
**Vue is install-gated, drive-unverified.** `init` detects a Vue app (as a Vite app with Vue as the UI library) and the install gate scaffolds one from scratch, runs `init`, boots it and waits for a session, so the setup is proven. What is not proven is the drive: there is no Vue example app in CI. `piniaStore` reads a Pinia store, and everything the framework-neutral core provides works. What you do not get is a `source` field, because source stamping covers JSX and Svelte components and a `.vue` single-file component is neither. See [Frameworks](/frameworks) for the full status table.
|
|
470
480
|
|
package/docs/http-transport.md
CHANGED
|
@@ -8,7 +8,7 @@ Most clients reach Reticle over stdio: they spawn `reticle mcp`, and the tools a
|
|
|
8
8
|
|
|
9
9
|
The daemon also speaks MCP over HTTP, on the same port as everything else. Nothing has to reload for a client to reach it, so this is the transport for scripted runs, CI, a language with no MCP client library, and any editor that will not pick up tools mid-session.
|
|
10
10
|
|
|
11
|
-
It is the same tool surface. Not a subset, not a simplified one. `
|
|
11
|
+
It is the same tool surface. Not a subset, not a simplified one. `reticle_look { action: "page" }`, `reticle_act_and_wait`, `reticle_assert` and the rest behave exactly as they do over stdio, because they are the same server behind a different transport.
|
|
12
12
|
|
|
13
13
|
## The two endpoints
|
|
14
14
|
|
|
@@ -136,7 +136,10 @@ async function start() {
|
|
|
136
136
|
const listed = await send('tools/list', {});
|
|
137
137
|
console.log(listed.result.tools.map((t) => t.name).join(', '));
|
|
138
138
|
|
|
139
|
-
const snapshot = await send('tools/call', {
|
|
139
|
+
const snapshot = await send('tools/call', {
|
|
140
|
+
name: 'reticle_look',
|
|
141
|
+
arguments: { action: 'page' },
|
|
142
|
+
});
|
|
140
143
|
console.log(snapshot.result.content[0].text);
|
|
141
144
|
}
|
|
142
145
|
```
|
|
@@ -159,6 +162,6 @@ The `202` comes back here; the tool list appears in the first shell.
|
|
|
159
162
|
|
|
160
163
|
## What this does not change
|
|
161
164
|
|
|
162
|
-
The transport carries the tools. It does not replace the rest of the setup. The app still has to be instrumented and connected, and `
|
|
165
|
+
The transport carries the tools. It does not replace the rest of the setup. The app still has to be instrumented and connected, and `reticle_session { action: "list" }` is still what tells you whether a session is there to drive. A verdict reached over HTTP is a verdict reached the usual way: only `verified: "yes"` is a pass.
|
|
163
166
|
|
|
164
167
|
The endpoints, the handshake, the three status codes and the `202`-then-SSE shape are pinned by `server/src/mcp-http-transport.test.ts`, which drives them over raw HTTP with no client library, so the contract this page describes fails the build if it changes.
|
package/docs/human-control.md
CHANGED
|
@@ -17,7 +17,7 @@ The panel header shows a running **✓N ✗M** tally: the verdict score, live. I
|
|
|
17
17
|
MCP is **pull-based**: the agent only receives data when it calls a tool. Reticle can't freeze the model mid-thought, but it **intercepts the agent's next tool call**, which is exactly when the agent would touch the page. That makes steering reliable:
|
|
18
18
|
|
|
19
19
|
- **Nudge** (a message, no pause). Your text is queued and rides back on the agent's **next** tool result as `guidance` (delivered once). The agent reads it and adjusts.
|
|
20
|
-
- **Pause.** The agent's next `reticle_act`
|
|
20
|
+
- **Pause.** The agent's next `reticle_act` (single or `steps`) / `reticle_act_and_wait` **refuses to execute** and returns `{ paused: true, guidance: ["<your message>"], hint: "…" }`. The page is untouched until resume. _Read-only tools (snapshot/query/observe) still work_, so the agent can look while paused.
|
|
21
21
|
- **End.** The session is over; the panel shows "Session ended" and clears.
|
|
22
22
|
|
|
23
23
|
## From the panel (the human)
|
|
@@ -35,7 +35,7 @@ The floating panel (bottom-center, `present: true`) gives you:
|
|
|
35
35
|
You don't have to describe a bug in prose. **Point at it.** The flag captures the element's re-resolvable anchor _and_ the source `file:line` (when the framework stamped one), so the agent fixes the exact element and code, not a guess. The loop:
|
|
36
36
|
|
|
37
37
|
1. **You** flag the element and type the problem → Reticle emits a `HUMAN_MARK`.
|
|
38
|
-
2. **The agent** drains it with `reticle_session {action:"review"}`, getting note + element label + `source: { file, line }` + a ready-to-act `fix` hint. `
|
|
38
|
+
2. **The agent** drains it with `reticle_session {action:"review"}`, getting note + element label + `source: { file, line }` + a ready-to-act `fix` hint. `reticle_session { action: "list" }` also reports `pendingMarks` so the agent notices flags.
|
|
39
39
|
3. **The agent** opens the file, fixes it, and calls `reticle_session {action:"review"}({ resolve: "m1" })`.
|
|
40
40
|
4. **You** see **"✓ fixed: \<your note\>"** land in the panel. Flag → fix → confirmation.
|
|
41
41
|
|
package/docs/index.mdx
CHANGED
|
@@ -73,13 +73,16 @@ Afterwards, use it whenever you change any user-facing behaviour.
|
|
|
73
73
|
The four files it writes, the marks it prints, and how to read its report.
|
|
74
74
|
</Card>
|
|
75
75
|
|
|
76
|
-
<CardGroup cols={
|
|
76
|
+
<CardGroup cols={3}>
|
|
77
77
|
<Card title="Quickstart" icon="rocket" href="/quickstart">
|
|
78
78
|
Install, connect your agent, and get a real verdict. Five minutes.
|
|
79
79
|
</Card>
|
|
80
80
|
<Card title="Why Reticle" icon="lightbulb" href="/why-reticle">
|
|
81
81
|
The false-green problem, and the evidence that this fixes it.
|
|
82
82
|
</Card>
|
|
83
|
+
<Card title="Use cases" icon="list-check" href="/use-cases">
|
|
84
|
+
Agent-built changes, security behaviour, accessibility, performance, SEO basics and personas.
|
|
85
|
+
</Card>
|
|
83
86
|
</CardGroup>
|
|
84
87
|
|
|
85
88
|
<CardGroup cols={3}>
|
|
@@ -127,18 +130,25 @@ Reticle clicks, watches, and reports what actually happened. A verdict of `unkno
|
|
|
127
130
|
single step.
|
|
128
131
|
</Card>
|
|
129
132
|
<Card title="Yours, locally" icon="lock">
|
|
130
|
-
Dev-only
|
|
133
|
+
Dev-only and localhost-only. Your app's data stays on your machine; anonymous usage counts are
|
|
134
|
+
sent unless you [turn them off](/telemetry).
|
|
131
135
|
</Card>
|
|
132
136
|
</CardGroup>
|
|
133
137
|
|
|
138
|
+
## What people use it for
|
|
139
|
+
|
|
140
|
+
Beyond proving an agent's change, people use Reticle to check security behaviour (access control by role, calls that must never fire, CSP violations), accessibility and UX (roles, names, focus, keyboard), performance (request counts, long tasks, layout shift), SEO basics (titles, headings, links, redirects), and to drive the app as different users. [Use cases](/use-cases) has what each one checks and a call to run.
|
|
141
|
+
|
|
142
|
+
It pairs well with a visual testing tool for pixel diffs, Playwright for sites you don't own, axe for full WCAG audits and a security scanner for vulnerability discovery.
|
|
143
|
+
|
|
134
144
|
## The evidence
|
|
135
145
|
|
|
136
|
-
|
|
146
|
+
Every number is measured by a committed harness, including the ties.
|
|
137
147
|
|
|
138
148
|

|
|
139
149
|
|
|
140
150
|
<Card title="Read the full benchmark" icon="chart-column" href="/benchmarks">
|
|
141
|
-
How every number was measured, what it means, and where
|
|
151
|
+
How every number was measured, what it means, and where the tools tie.
|
|
142
152
|
</Card>
|
|
143
153
|
|
|
144
154
|
## Built for agents, readable by humans
|
|
@@ -151,7 +161,7 @@ Reticle is used by a coding agent far more often than by a person, so these docs
|
|
|
151
161
|
|
|
152
162
|
## Build with us
|
|
153
163
|
|
|
154
|
-
|
|
164
|
+
The roadmap moves on what people actually hit, so tell us.
|
|
155
165
|
|
|
156
166
|
<CardGroup cols={2}>
|
|
157
167
|
<Card title="Join the Discord" icon="discord" href="https://discord.gg/BwAbzv9ZRz">
|
|
@@ -163,4 +173,4 @@ Reticle is early, and the sharp edges get filed down fastest when someone tells
|
|
|
163
173
|
</Card>
|
|
164
174
|
</CardGroup>
|
|
165
175
|
|
|
166
|
-
Found a bug, or something you wished existed? `
|
|
176
|
+
Found a bug, or something you wished existed? `reticle_session { action: "feedback" }` sends it in one call from your agent, and `npx @reticlehq/server feedback "…"` from a terminal. If Reticle could not tell you what happened, that is our defect and we want to know.
|
package/docs/install-agentic.mdx
CHANGED
|
@@ -131,7 +131,7 @@ That `ℹ` line is the one people skim past, so here it is in full:
|
|
|
131
131
|
```
|
|
132
132
|
[ℹ] AGENT: finish the capabilities file → src/reticle-dev.ts
|
|
133
133
|
src/reticle-dev.ts was written but registers nothing, so `hasCapabilities` stays false
|
|
134
|
-
and
|
|
134
|
+
and reticle_look { action: "state" } has nothing to read.
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
`init` wrote the file, but a brand-new Vite template has no testids and no state library, so there was nothing to register. Reticle tells you this instead of counting it as a win. See [Instrument your app](/instrumentation) for what to put in it.
|
|
@@ -248,7 +248,7 @@ Three of these are answers only the caller has. `init` reads your repository; it
|
|
|
248
248
|
| `--env KEY=VALUE` | What the app needs to reach a usable state: the key from `.env.example`, the mock backend, the variable that skips an auth wall. Repeatable, and a value may contain spaces and equals signs. Without it a real app sits on a login screen and there is no flow to drive. |
|
|
249
249
|
| `--app <dir>` | Monorepos. `init` can find the apps that are servable; only your request says which one you are working in. |
|
|
250
250
|
| `--license <key>` | Writes `RETICLE_LICENSE_KEY` to `.env` and keeps `.env` out of git. Do not do those two by hand. |
|
|
251
|
-
| `--json` | One object on stdout
|
|
251
|
+
| `--json` | One result object on stdout; progress and package-manager output go to stderr. Default init reports runtime setup and connection. `--files-only` and `--dry-run` report the wiring result. Init does not drive a flow or produce a verification verdict. |
|
|
252
252
|
| `--files-only` | Write the files, register the MCP server with every agent on the machine, pre-approve Reticle's tools, and stop: no dev server, no drive. What `init` did before it learned to boot the app, and the upgrade path for a machine that installed Reticle on an earlier version. |
|
|
253
253
|
| `--no-open` | Do not open a browser: CI, a container, an SSH session, or a tab you already have. |
|
|
254
254
|
| `--dry-run` | See the plan before anything is written. |
|
package/docs/install-manual.mdx
CHANGED
|
@@ -22,7 +22,7 @@ The basics in [Getting Started](getting-started.md) work with **zero app changes
|
|
|
22
22
|
|
|
23
23
|
Adoption is **free → cheap → targeted**. You don't instrument everything; you reuse what you have, then add the handful of facts the DOM can't express.
|
|
24
24
|
|
|
25
|
-
**1. Reuse your existing `data-testid` (free).** If you already test with Playwright or Cypress, your testids work in Reticle unchanged; `
|
|
25
|
+
**1. Reuse your existing `data-testid` (free).** If you already test with Playwright or Cypress, your testids work in Reticle unchanged; `reticle_look({ action: "find", by: 'testid', value: 'checkout' })` matches them exactly. No new markup, no new code.
|
|
26
26
|
|
|
27
27
|
**2. Advertise the surface from your existing constants (cheap).** You already keep a `TestIds` constant object for your E2E suite. Pass it straight in. Now `reticle_capabilities()` tells a fresh agent your whole surface without reading source.
|
|
28
28
|
|
|
@@ -107,7 +107,7 @@ If `mutate` throws, the mutation never happened, so **the signal is not emitted
|
|
|
107
107
|
|
|
108
108
|
> **The documented exception:** genuinely view-level signals (render or async completions like `diff:shown` or `caption:generated`, which aren't store state) legitimately stay in your components. Only commit-point signals belong in the store layer.
|
|
109
109
|
|
|
110
|
-
Pair this with store registration so the agent can _read_ state instead of you emitting a signal per fact: `registerStore('workspace', useWorkspace)`, then `
|
|
110
|
+
Pair this with store registration so the agent can _read_ state instead of you emitting a signal per fact: `registerStore('workspace', useWorkspace)`, then `reticle_look({ action: "state", store: 'workspace' })`.
|
|
111
111
|
|
|
112
112
|
## 4. Self-registering domains (`registerReticleDomain`)
|
|
113
113
|
|
|
@@ -164,7 +164,7 @@ Or turn it on with the shipped preset: `plugin.configs.recommended`, which sets
|
|
|
164
164
|
|
|
165
165
|
Reticle observes and drives a tab through the in-page SDK plus (optionally) CDP. It **cannot bring to front or recover a browser tab the OS won't let it script**: a backgrounded tab, say, or a non-default browser (Dia, etc.) reporting `hidden:true` / `throttled:true`.
|
|
166
166
|
|
|
167
|
-
When that happens, `
|
|
167
|
+
When that happens, `reticle_session { action: "list" }` and every act/assert result carry a `session.recommendation` saying so. The escape hatch is **`reticle drive <url>`** (add `--headed` to watch). Reticle launches and owns a guaranteed-scriptable browser. See [usage §18](usage.md#18-real-input-mode-native-hover--drag) for the full note.
|
|
168
168
|
|
|
169
169
|
## Checklist
|
|
170
170
|
|
|
@@ -45,7 +45,7 @@ This is the design target, and it needs no special setup:
|
|
|
45
45
|
- Flows can't bleed into each other: contexts are isolated and every session is scoped by `projectId`.
|
|
46
46
|
- A single crashed page is reclaimed on its own, and if an agent crashes or hangs its lease stops being touched and the **lease reaper** reclaims the context after a TTL, freeing the slot. One dead agent never starves the others.
|
|
47
47
|
|
|
48
|
-
`
|
|
48
|
+
`reticle_session { action: "list" }` lists everything with `projectId` (group by app) and `leased` (pool context vs a human tab), so an orchestrator can see the whole fleet at a glance.
|
|
49
49
|
|
|
50
50
|
## Knobs
|
|
51
51
|
|
|
@@ -76,4 +76,4 @@ module.exports = {
|
|
|
76
76
|
|
|
77
77
|
## Why an attribute rather than a runtime lookup
|
|
78
78
|
|
|
79
|
-
The stamp survives minification of everything else, costs nothing at runtime, and is readable from the DOM node itself. That is what lets [`
|
|
79
|
+
The stamp survives minification of everything else, costs nothing at runtime, and is readable from the DOM node itself. That is what lets [`reticle_look { action: "element" }`](/tools/inspect) answer with a file and a line rather than a component name and a shrug.
|
|
@@ -12,7 +12,7 @@ icon: window
|
|
|
12
12
|
|
|
13
13
|
This is what makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel. It never imports a Node API; that boundary is enforced by the dependency graph.
|
|
14
14
|
|
|
15
|
-
[`
|
|
15
|
+
[`reticle_look { action: "find" }`](/tools/query) speaks role, label and testid instead of CSS selectors through its own local accessibility engine: one engine computes both what elements are called and what they match by, so a reported identity always round-trips.
|
|
16
16
|
|
|
17
17
|
## When you need it directly
|
|
18
18
|
|
|
@@ -90,7 +90,7 @@ Also exported: `getCapabilities()`, `hasCapabilities()`, `setCapabilitiesListene
|
|
|
90
90
|
|
|
91
91
|
## State adapters
|
|
92
92
|
|
|
93
|
-
Register a store and its contents become readable through [`
|
|
93
|
+
Register a store and its contents become readable through [`reticle_look { action: "state" }`](/tools/state).
|
|
94
94
|
|
|
95
95
|
`registerStore`, `unregisterStore`, `storeNames`, `readStores`, `readStoresWithTruncation`.
|
|
96
96
|
|
package/docs/packages/next.mdx
CHANGED
|
@@ -44,10 +44,13 @@ Returns the config unchanged when `NODE_ENV === 'production'`. Otherwise it merg
|
|
|
44
44
|
| --- | --- |
|
|
45
45
|
| `webpack` | Pushes an `enforce: 'pre'` rule for `/\.(t\|j)sx$/`, excluding `node_modules`, using this package's loader. Your own `webpack` function still runs afterwards |
|
|
46
46
|
| `turbopack` | On Next 15.3 and above only: rules for `*.tsx` and `*.jsx` pointing at `@reticlehq/next/loader` |
|
|
47
|
+
| `env.NEXT_PUBLIC_RETICLE_URL` | The bridge URL: a valid `RETICLE_PORT` override, otherwise project daemon discovery and `.reticle.json` |
|
|
47
48
|
| `env.NEXT_PUBLIC_RETICLE_TOKEN` | The pairing token, when one exists on disk |
|
|
48
49
|
| `env.NEXT_PUBLIC_RETICLE_ROOT` | `process.cwd()`, so source paths come back repository-relative |
|
|
49
50
|
| `env.NEXT_PUBLIC_RETICLE_SDK_VERSION` | The version of the app's installed `@reticlehq/react` |
|
|
50
51
|
|
|
52
|
+
Daemon discovery reads `RETICLE_STATE_DIR` (default `~/.reticle`). Pairing tokens use `RETICLE_PAIRING_TOKEN_DIR` independently. The generated `ReticleDev` component reads `NEXT_PUBLIC_RETICLE_URL`; restart the dev server after changing these settings.
|
|
53
|
+
|
|
51
54
|
`readPairingToken()` is also exported at runtime. It is not declared in `index.d.ts`, so TypeScript will not see it.
|
|
52
55
|
|
|
53
56
|
### `./loader`
|
package/docs/packages/react.mdx
CHANGED
|
@@ -76,7 +76,7 @@ import { useReticleStore } from '@reticlehq/react/store';
|
|
|
76
76
|
|
|
77
77
|
function Cart() {
|
|
78
78
|
const [items, setItems] = useState([]);
|
|
79
|
-
useReticleStore('cart', items); // now readable via
|
|
79
|
+
useReticleStore('cart', items); // now readable via reticle_look { action: "state" }
|
|
80
80
|
}
|
|
81
81
|
```
|
|
82
82
|
|
|
@@ -87,5 +87,5 @@ function Cart() {
|
|
|
87
87
|
React 19 dropped `_debugSource`, so `identify` can name the component but not the file unless something stamps the source at build time. That is [`@reticlehq/babel-plugin`](/packages/babel-plugin), pulled in for you by [`@reticlehq/vite-plugin`](/packages/vite-plugin) or [`@reticlehq/next`](/packages/next).
|
|
88
88
|
|
|
89
89
|
<Card title="Reading component state" icon="layer-group" href="/state-management">
|
|
90
|
-
What `
|
|
90
|
+
What `reticle_look { action: "state" }` can see, and how to widen it.
|
|
91
91
|
</Card>
|
package/docs/packages/tauri.mdx
CHANGED
|
@@ -21,7 +21,7 @@ Tauri IPC observation needs nothing on the Rust side. An `invoke('load_todos')`
|
|
|
21
21
|
|
|
22
22
|
```toml
|
|
23
23
|
[dependencies]
|
|
24
|
-
reticle-tauri = "3.
|
|
24
|
+
reticle-tauri = "3.5"
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Its own dependencies are `tauri = "2"` plus the per-platform capture backends: `objc2` and friends on macOS, `webview2-com` and `windows` on Windows, `webkit2gtk` and `cairo-rs` on Linux and the BSDs.
|
|
@@ -31,6 +31,8 @@ export default defineConfig({
|
|
|
31
31
|
[`@reticlehq/server`](/packages/server).
|
|
32
32
|
</Warning>
|
|
33
33
|
|
|
34
|
+
Set `RETICLE_PORT` in the dev server or desktop build environment to use the same custom bridge port as the CLI. A valid value overrides discovery and `.reticle.json`; invalid values are ignored.
|
|
35
|
+
|
|
34
36
|
## Why it exists
|
|
35
37
|
|
|
36
38
|
`apply: 'serve'` means it never reaches a production build. That is the guarantee that lets you leave it in the config permanently: there is no way to ship Reticle to your users by forgetting to remove a line.
|
|
@@ -61,7 +63,7 @@ Plus a dynamic import of your dev module if one exists. It looks for `src/reticl
|
|
|
61
63
|
| `token` | `string` | `readPairingToken()`, from `~/.reticle/pairing-token` |
|
|
62
64
|
| `sourceMapping` | `boolean` | `true`. Set `false` to skip the Babel pass |
|
|
63
65
|
| `inject` | `boolean` | `true`. Set `false` to call `connect()` yourself |
|
|
64
|
-
| `desktop` | `boolean` | `false`. When `true` the
|
|
66
|
+
| `desktop` | `boolean` | `false`. When `true` the connect passes `allowInProduction: true`, which an Electron or Tauri renderer needs, and a `vite build` in a non-production mode (`--mode development`) is instrumented too. A production-mode build is stubbed like a web build |
|
|
65
67
|
| `captureNetworkBodies` | `boolean` | `false`. Also enabled by `VITE_RETICLE_CAPTURE_BODIES=1` |
|
|
66
68
|
| `onWarn` | `(message: string) => void` | `console.warn` |
|
|
67
69
|
|
|
@@ -87,7 +89,7 @@ Types: `ReticleVitePluginOptions`, `ReticleVitePlugin`, `ViteDevServerLike`.
|
|
|
87
89
|
reticle({ desktop: true });
|
|
88
90
|
```
|
|
89
91
|
|
|
90
|
-
A packaged Electron or Tauri renderer is
|
|
92
|
+
A packaged Electron or Tauri renderer is loaded from a custom protocol with no dev server and reports `NODE_ENV=production`, so the default guards would refuse. This flag injects the connect into the entry module and opts it into production. It does NOT instrument a release: a production-mode `vite build` (the default) ships no Reticle code, exactly like a web build. To drive a packaged renderer with no dev server, build it with `vite build --mode development`.
|
|
91
93
|
|
|
92
94
|
<Card title="Desktop wiring in full" icon="display" href="/desktop">
|
|
93
95
|
Including the Tauri CSP whose failure is completely silent.
|
package/docs/packages.mdx
CHANGED
|
@@ -38,12 +38,12 @@ You never install `@reticlehq/server`. Your agent runs it with `npx`.
|
|
|
38
38
|
|
|
39
39
|
The package split is not cosmetic. Each boundary enforces something:
|
|
40
40
|
|
|
41
|
-
| Package
|
|
42
|
-
|
|
|
43
|
-
| `core`
|
|
44
|
-
| `browser` | the browser | Never imports a Node API
|
|
45
|
-
| `server`
|
|
46
|
-
| `react`
|
|
41
|
+
| Package | Runs in | Hard rule |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `core` | both | Depends only on `zod` and `open-verification`. Sits at the bottom of the graph |
|
|
44
|
+
| `browser` | the browser | Never imports a Node API |
|
|
45
|
+
| `server` | Node | Never imports a DOM API |
|
|
46
|
+
| `react` | the browser | Optional. Core must work without it |
|
|
47
47
|
|
|
48
48
|
Those rules are why the browser SDK cannot accidentally pull in a filesystem call, and why the server cannot accidentally depend on `window`. They are enforced by the dependency graph, not by discipline.
|
|
49
49
|
|
|
@@ -72,7 +72,7 @@ Those rules are why the browser SDK cannot accidentally pull in a filesystem cal
|
|
|
72
72
|
|
|
73
73
|
**Why it exists:** this is the part that makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel.
|
|
74
74
|
|
|
75
|
-
**Depends on:** `@reticlehq/core`. [`
|
|
75
|
+
**Depends on:** `@reticlehq/core`. [`reticle_look { action: "find" }`](/tools/query) speaks role, label and testid rather than CSS selectors through its own local accessibility engine, so what a snapshot reports is exactly what a query matches.
|
|
76
76
|
|
|
77
77
|
**Install it directly when** you are not on React, or you want `connect()` under your own control:
|
|
78
78
|
|
|
@@ -147,7 +147,7 @@ export default defineConfig({ plugins: [reticle(), react()] });
|
|
|
147
147
|
|
|
148
148
|
**Stamps `data-reticle-source="file:line:col"` onto JSX host elements.**
|
|
149
149
|
|
|
150
|
-
**Why it exists:** React 19 dropped `_debugSource`, which is what source mapping used to rely on. This plugin puts the information back, at build time, in dev only. Without it, `
|
|
150
|
+
**Why it exists:** React 19 dropped `_debugSource`, which is what source mapping used to rely on. This plugin puts the information back, at build time, in dev only. Without it, `reticle_look { action: "element" }` can still tell you the component name but not the file.
|
|
151
151
|
|
|
152
152
|
**Depends on:** nothing. **Peer:** `@babel/core`.
|
|
153
153
|
|
|
@@ -239,7 +239,7 @@ A lint rule makes the signal layer self-enforcing, which is the only way a conve
|
|
|
239
239
|
|
|
240
240
|
```toml
|
|
241
241
|
[dependencies]
|
|
242
|
-
reticle-tauri = "3.
|
|
242
|
+
reticle-tauri = "3.5"
|
|
243
243
|
```
|
|
244
244
|
|
|
245
245
|
<Warning>
|