@reticlehq/server 2.8.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +130 -813
- package/dist/bridge/bridge.d.ts +23 -0
- package/dist/bridge/bridge.js +80 -2
- package/dist/bridge/bridge.test-harness.d.ts +5 -0
- package/dist/bridge/bridge.test-harness.js +6 -4
- package/dist/capsule/capsule-store.d.ts +2 -2
- package/dist/capsule/causal-summary.d.ts +16 -1
- package/dist/capsule/causal-summary.js +2 -1
- package/dist/cli/cli-doctor.js +41 -13
- package/dist/cli/cli-flow-commands.js +8 -8
- package/dist/cli/cli-launch.d.ts +0 -17
- package/dist/cli/cli-launch.js +0 -28
- package/dist/cli/cli-parse.d.ts +1 -1
- package/dist/cli/cli-parse.js +1 -1
- package/dist/cli/cli-port.d.ts +24 -0
- package/dist/cli/cli-port.js +66 -35
- package/dist/cli/cli-verify.d.ts +21 -0
- package/dist/cli/cli-verify.js +45 -1
- package/dist/cli/config-discovery.d.ts +35 -0
- package/dist/cli/config-discovery.js +163 -0
- package/dist/cli/doctor-daemon-line.js +4 -1
- package/dist/cli/doctor-project-line.d.ts +37 -0
- package/dist/cli/doctor-project-line.js +35 -0
- package/dist/cli/doctor-rows.d.ts +51 -0
- package/dist/cli/doctor-rows.js +54 -0
- package/dist/cli/doctor-sessions-line.js +6 -3
- package/dist/cli/drive-attach.d.ts +99 -0
- package/dist/cli/drive-attach.js +203 -0
- package/dist/cli/drive-command.d.ts +13 -0
- package/dist/cli/drive-command.js +103 -0
- package/dist/cli/open-note.d.ts +20 -0
- package/dist/cli/open-note.js +37 -0
- package/dist/cli/status-next-action.d.ts +37 -0
- package/dist/cli/status-next-action.js +61 -0
- package/dist/cli.js +65 -70
- package/dist/crawl/crawl.d.ts +7 -2
- package/dist/crawl/crawl.js +52 -9
- package/dist/ee/audit-log.js +2 -2
- package/dist/events/body-failures.d.ts +2 -2
- package/dist/events/contradiction-folds.d.ts +45 -0
- package/dist/events/contradiction-folds.js +42 -0
- package/dist/events/contradictions.d.ts +51 -1
- package/dist/events/contradictions.js +26 -7
- package/dist/events/declared.d.ts +45 -0
- package/dist/events/declared.js +73 -0
- package/dist/events/echo-mismatch.d.ts +9 -3
- package/dist/events/echo-mismatch.js +33 -5
- package/dist/events/event-filters.d.ts +1 -1
- package/dist/events/event-filters.js +7 -4
- package/dist/events/predicate-eval.d.ts +17 -91
- package/dist/events/predicate-eval.js +85 -326
- package/dist/events/predicate-parse.js +31 -3
- package/dist/events/predicate-route.d.ts +32 -0
- package/dist/events/predicate-route.js +102 -0
- package/dist/events/predicate-schema.d.ts +99 -0
- package/dist/events/predicate-schema.js +309 -0
- package/dist/events/predicate.d.ts +8 -0
- package/dist/events/predicate.js +127 -5
- package/dist/events/stale-response.d.ts +2 -3
- package/dist/events/stale-response.js +49 -2
- package/dist/events/unit-mismatch.d.ts +2 -2
- package/dist/flows/annotate-tools.js +3 -0
- package/dist/flows/annotate.js +18 -4
- package/dist/flows/coverage.d.ts +38 -5
- package/dist/flows/coverage.js +30 -4
- package/dist/flows/decision.d.ts +15 -0
- package/dist/flows/decision.js +1 -1
- package/dist/flows/flow-replay-run.js +8 -1
- package/dist/flows/flow-tools.js +9 -1
- package/dist/flows/replay.js +3 -0
- package/dist/flows/verify-change-tools.js +1 -1
- package/dist/honesty/accepted-write.d.ts +3 -0
- package/dist/honesty/accepted-write.js +8 -3
- package/dist/honesty/already-true.js +15 -0
- package/dist/honesty/blind-spots.js +4 -0
- package/dist/honesty/honesty.d.ts +11 -0
- package/dist/honesty/honesty.js +1 -0
- package/dist/honesty/uncaptured-bodies.d.ts +4 -2
- package/dist/honesty/uncaptured-bodies.js +9 -2
- package/dist/honesty/unread-outcome.d.ts +8 -0
- package/dist/honesty/unread-outcome.js +36 -9
- package/dist/honesty/unsettled.d.ts +9 -0
- package/dist/honesty/unsettled.js +7 -3
- package/dist/honesty/verified.d.ts +29 -3
- package/dist/honesty/verified.js +67 -8
- package/dist/http-server.d.ts +19 -0
- package/dist/http-server.js +202 -4
- package/dist/impact/impact-recorder.d.ts +23 -0
- package/dist/impact/impact-recorder.js +76 -0
- package/dist/impact/impact-store.d.ts +47 -0
- package/dist/impact/impact-store.js +171 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +60 -10
- package/dist/init/agent-rules.d.ts +20 -0
- package/dist/init/agent-rules.js +36 -0
- package/dist/init/astro-patch.js +27 -1
- package/dist/init/confirm.d.ts +21 -3
- package/dist/init/confirm.js +32 -5
- package/dist/init/connect-steps.d.ts +2 -0
- package/dist/init/connect-steps.js +50 -0
- package/dist/init/csp-check.d.ts +38 -0
- package/dist/init/csp-check.js +92 -0
- package/dist/init/csp-doctor.d.ts +25 -0
- package/dist/init/csp-doctor.js +49 -0
- package/dist/init/cursor.js +6 -9
- package/dist/init/detect.d.ts +8 -0
- package/dist/init/detect.js +33 -3
- package/dist/init/mcp-clients.d.ts +1 -0
- package/dist/init/mcp-clients.js +51 -7
- package/dist/init/mcp.d.ts +16 -2
- package/dist/init/mcp.js +23 -1
- package/dist/init/plan-framework.d.ts +10 -0
- package/dist/init/plan-framework.js +25 -1
- package/dist/init/plan.d.ts +7 -2
- package/dist/init/plan.js +66 -30
- package/dist/init/run.js +11 -5
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/slash-command.js +12 -4
- package/dist/init/snippets.d.ts +1 -1
- package/dist/init/snippets.js +35 -7
- package/dist/init/vite-config.js +24 -2
- package/dist/input/network-mock-tools.js +8 -1
- package/dist/input/scroll-find.d.ts +6 -0
- package/dist/input/scroll-find.js +17 -2
- package/dist/input/scroll-tools.js +4 -0
- package/dist/journal/attach-journal.d.ts +9 -1
- package/dist/journal/attach-journal.js +13 -2
- package/dist/journal/journal-query.d.ts +13 -0
- package/dist/journal/journal-query.js +28 -3
- package/dist/journal/workspace-gitignore.d.ts +10 -0
- package/dist/journal/workspace-gitignore.js +59 -0
- package/dist/license/license.d.ts +69 -4
- package/dist/license/license.js +84 -15
- package/dist/mcp/mcp-proxy.d.ts +7 -93
- package/dist/mcp/mcp-proxy.js +183 -250
- package/dist/mcp/mcp.d.ts +27 -2
- package/dist/mcp/mcp.js +77 -36
- package/dist/mcp/proxy-daemon-probe.d.ts +8 -0
- package/dist/mcp/proxy-daemon-probe.js +48 -0
- package/dist/mcp/proxy-handshake.d.ts +82 -1
- package/dist/mcp/proxy-handshake.js +110 -3
- package/dist/mcp/proxy-lifecycle.d.ts +10 -1
- package/dist/mcp/proxy-lifecycle.js +11 -2
- package/dist/mcp/proxy-log.d.ts +63 -0
- package/dist/mcp/proxy-log.js +132 -0
- package/dist/mcp/server-instructions.d.ts +17 -0
- package/dist/mcp/server-instructions.js +56 -0
- package/dist/mcp/shared-params.d.ts +27 -0
- package/dist/mcp/shared-params.js +36 -0
- package/dist/mcp/sse-frame-parser.d.ts +19 -0
- package/dist/mcp/sse-frame-parser.js +41 -0
- package/dist/project/reticle-dir.d.ts +2 -0
- package/dist/project/reticle-dir.js +1 -0
- package/dist/session/connection-memory.d.ts +59 -0
- package/dist/session/connection-memory.js +108 -0
- package/dist/session/dev-command.d.ts +26 -0
- package/dist/session/dev-command.js +89 -0
- package/dist/session/dev-server-probe.d.ts +68 -0
- package/dist/session/dev-server-probe.js +102 -7
- package/dist/session/last-act.d.ts +26 -0
- package/dist/session/last-act.js +35 -1
- package/dist/session/live-control-tools.js +42 -5
- package/dist/session/no-session-diagnosis.d.ts +55 -2
- package/dist/session/no-session-diagnosis.js +199 -22
- package/dist/session/no-session-next-action.d.ts +46 -0
- package/dist/session/no-session-next-action.js +85 -0
- package/dist/session/no-session-watch.d.ts +40 -1
- package/dist/session/no-session-watch.js +172 -11
- package/dist/session/pending-commands.d.ts +12 -0
- package/dist/session/pending-commands.js +13 -1
- package/dist/session/session-manager.d.ts +4 -0
- package/dist/session/session-manager.js +35 -5
- package/dist/session/session-readiness.js +1 -1
- package/dist/session/session.d.ts +10 -1
- package/dist/session/session.js +146 -5
- package/dist/telemetry/app-instrumented.d.ts +8 -0
- package/dist/telemetry/app-instrumented.js +10 -0
- package/dist/telemetry/bug-found.d.ts +18 -0
- package/dist/telemetry/bug-found.js +14 -0
- package/dist/telemetry/daemon-telemetry.js +29 -0
- package/dist/telemetry/install-source.d.ts +20 -0
- package/dist/telemetry/install-source.js +34 -0
- package/dist/telemetry/instrumentation-stall.d.ts +57 -0
- package/dist/telemetry/instrumentation-stall.js +103 -0
- package/dist/telemetry/license-activation.d.ts +48 -0
- package/dist/telemetry/license-activation.js +57 -0
- package/dist/telemetry/session-metrics.d.ts +8 -0
- package/dist/telemetry/session-metrics.js +10 -0
- package/dist/telemetry/telemetry.d.ts +3 -1
- package/dist/telemetry/telemetry.js +23 -3
- package/dist/tools/act-tools.js +166 -29
- package/dist/tools/assert-verdict.js +32 -7
- package/dist/tools/browser-tools.js +10 -0
- package/dist/tools/dynamic-tools.js +17 -8
- package/dist/tools/error-recovery.js +10 -6
- package/dist/tools/feedback-tools.js +31 -6
- package/dist/tools/invoke-tool.d.ts +1 -1
- package/dist/tools/invoke-tool.js +30 -8
- package/dist/tools/lease-hint.d.ts +30 -8
- package/dist/tools/lease-hint.js +65 -14
- package/dist/tools/lease-tools.d.ts +11 -0
- package/dist/tools/lease-tools.js +141 -96
- package/dist/tools/observe-tools.js +9 -3
- package/dist/tools/react-grace.d.ts +4 -0
- package/dist/tools/react-grace.js +6 -3
- package/dist/tools/read-tools.js +47 -1
- package/dist/tools/resolve-target.d.ts +33 -0
- package/dist/tools/resolve-target.js +53 -0
- package/dist/tools/settle-in-flight.d.ts +15 -0
- package/dist/tools/settle-in-flight.js +42 -2
- package/dist/tools/snapshot-delta.d.ts +3 -0
- package/dist/tools/snapshot-delta.js +67 -6
- package/dist/tools/tool-kit.d.ts +38 -3
- package/dist/tools/tool-names.d.ts +2 -0
- package/dist/tools/tool-names.js +2 -0
- package/dist/tools/tool-surface.d.ts +60 -0
- package/dist/tools/tool-surface.js +84 -19
- package/dist/tools/tools.js +61 -5
- package/dist/tools/unadvertised-help.js +9 -6
- package/docs/README.md +1 -2
- package/docs/actions.mdx +41 -8
- package/docs/agent-cheatsheet.md +6 -4
- package/docs/architecture.md +6 -0
- package/docs/benchmarks.md +60 -12
- package/docs/best-practices.mdx +11 -6
- package/docs/capabilities.mdx +204 -57
- package/docs/cli/affected.mdx +6 -4
- package/docs/cli/capsules.mdx +9 -8
- package/docs/cli/cloud.mdx +30 -4
- package/docs/cli/doctor.mdx +41 -8
- package/docs/cli/drive.mdx +34 -15
- package/docs/cli/gate.mdx +6 -6
- package/docs/cli/help.mdx +8 -6
- package/docs/cli/hunt.mdx +20 -4
- package/docs/cli/identify.mdx +16 -2
- package/docs/cli/init.mdx +35 -8
- package/docs/cli/kill.mdx +5 -5
- package/docs/cli/license.mdx +1 -1
- package/docs/cli/open.mdx +6 -0
- package/docs/cli/restart.mdx +6 -6
- package/docs/cli/serve.mdx +10 -3
- package/docs/cli/status.mdx +29 -5
- package/docs/cli/stop.mdx +8 -0
- package/docs/cli/verify.mdx +20 -1
- package/docs/cli/version.mdx +1 -1
- package/docs/cli/watch.mdx +9 -2
- package/docs/cli.mdx +44 -22
- package/docs/debugging.md +3 -1
- package/docs/deploy-checks.md +31 -2
- package/docs/desktop.mdx +276 -80
- package/docs/docs.json +13 -12
- package/docs/enterprise.md +9 -3
- package/docs/faq.mdx +49 -35
- package/docs/fixtures.md +2 -0
- package/docs/flows.md +7 -5
- package/docs/for-agents.md +3 -3
- package/docs/frameworks.mdx +46 -7
- package/docs/gate-plan.md +4 -2
- package/docs/gates.md +8 -6
- package/docs/getting-started.md +53 -12
- package/docs/human-control.md +2 -0
- package/docs/index.mdx +43 -1
- package/docs/install-agentic.mdx +13 -4
- package/docs/install-manual.mdx +6 -4
- package/docs/instrumentation.mdx +7 -1
- package/docs/integration-patterns.md +3 -1
- package/docs/local-registry.md +4 -2
- package/docs/multi-agent-testing.md +3 -1
- package/docs/packages/babel-plugin.mdx +15 -6
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/core.mdx +26 -16
- package/docs/packages/electron.mdx +5 -7
- package/docs/packages/eslint-plugin.mdx +5 -3
- package/docs/packages/next.mdx +3 -5
- package/docs/packages/react.mdx +3 -3
- package/docs/packages/server.mdx +2 -2
- package/docs/packages/tauri.mdx +6 -6
- package/docs/packages/test.mdx +18 -7
- package/docs/packages/vite-plugin.mdx +4 -6
- package/docs/packages.mdx +14 -13
- package/docs/platform-integration.md +2 -0
- package/docs/predicates.mdx +76 -7
- package/docs/quickstart.mdx +51 -5
- package/docs/recipes.mdx +63 -23
- package/docs/skill-file.mdx +3 -1
- package/docs/state-management.mdx +2 -0
- package/docs/system-map.md +3 -1
- package/docs/telemetry-contract.md +33 -5
- package/docs/telemetry-events.mdx +63 -18
- package/docs/telemetry.md +8 -5
- package/docs/testing.md +35 -1
- package/docs/token-efficiency.md +8 -2
- package/docs/tools-act-and-wait.mdx +64 -14
- package/docs/tools-act-sequence.mdx +34 -17
- package/docs/tools-act.mdx +24 -11
- package/docs/tools-assert.mdx +69 -10
- package/docs/tools-console.mdx +11 -7
- package/docs/tools-inspect.mdx +11 -8
- package/docs/tools-navigate.mdx +41 -5
- package/docs/tools-network.mdx +42 -9
- package/docs/tools-observe.mdx +66 -10
- package/docs/tools-overview.mdx +26 -23
- package/docs/tools-query.mdx +52 -8
- package/docs/tools-session-and-feedback.mdx +35 -9
- package/docs/tools-sessions.mdx +39 -10
- package/docs/tools-snapshot.mdx +57 -3
- package/docs/tools-state.mdx +24 -1
- package/docs/tools-tools-and-run.mdx +55 -24
- package/docs/tools-wait-for.mdx +34 -15
- package/docs/troubleshooting.mdx +80 -10
- package/docs/usage.md +46 -24
- package/docs/vs-chrome-devtools-mcp.mdx +41 -0
- package/docs/vs-playwright-mcp.mdx +42 -2
- package/docs/vs-screenshots.mdx +42 -1
- package/docs/what-is-reticle.mdx +1 -1
- package/docs/why-reticle.mdx +3 -1
- package/package.json +5 -5
- package/docs/desktop-apps.md +0 -265
package/docs/tools-wait-for.mdx
CHANGED
|
@@ -4,13 +4,15 @@ description: Block until a predicate holds. Or return immediately if it is alrea
|
|
|
4
4
|
icon: hourglass
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
`reticle_wait_for`
|
|
7
|
+
`reticle_wait_for` blocks until a predicate holds, or returns straight away if it is already satisfied in the recent event buffer. Reach for it when you are waiting on something you did not cause. It reads and waits; it does not produce a verdict, so finish with [`reticle_assert`](/tools-assert).
|
|
8
|
+
|
|
9
|
+
It takes the same predicate grammar as [`reticle_act_and_wait`](/tools-act-and-wait), without the action. Use it when the thing you are waiting for was triggered by something other than your own click. A timer, a websocket push, a background refresh.
|
|
8
10
|
|
|
9
11
|
## Example
|
|
10
12
|
|
|
11
13
|
```json
|
|
12
14
|
{
|
|
13
|
-
"predicate": { "kind": "text", "
|
|
15
|
+
"predicate": { "kind": "text", "contains": "Invalid email or password" },
|
|
14
16
|
"timeout_ms": 3000
|
|
15
17
|
}
|
|
16
18
|
```
|
|
@@ -22,12 +24,12 @@ Real response:
|
|
|
22
24
|
"pass": true,
|
|
23
25
|
"evidence": [
|
|
24
26
|
{
|
|
25
|
-
"ref": "
|
|
27
|
+
"ref": "e606",
|
|
26
28
|
"role": "alert",
|
|
27
29
|
"name": "Invalid email or password",
|
|
28
|
-
"visible":
|
|
30
|
+
"visible": true,
|
|
29
31
|
"source": "src/components/Login.tsx:72",
|
|
30
|
-
"states": ["present", "
|
|
32
|
+
"states": ["present", "visible", "enabled"]
|
|
31
33
|
}
|
|
32
34
|
]
|
|
33
35
|
}
|
|
@@ -36,31 +38,48 @@ Real response:
|
|
|
36
38
|
The evidence is the element itself, with the file and line that rendered it.
|
|
37
39
|
|
|
38
40
|
<Note>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
screen,
|
|
41
|
+
A `text` predicate matches **presence** by default, so an element that is in the DOM but hidden
|
|
42
|
+
satisfies it and comes back with `visible: false` and `states: ["present", "hidden", "enabled"]`.
|
|
43
|
+
If you need it on screen, say so: `{ "kind": "text", "contains": "…", "visible": true }`.
|
|
42
44
|
</Note>
|
|
43
45
|
|
|
46
|
+
The field is `contains`. `value` and `text` are accepted as aliases and rewritten to it, but `contains` is what the published schema declares.
|
|
47
|
+
|
|
44
48
|
## It checks the buffer first
|
|
45
49
|
|
|
46
50
|
`wait_for` does not always wait. If the predicate is already satisfied in the recent event buffer, it returns immediately. This means you can act, then wait, without racing. The event that happened while you were composing the second call is not lost.
|
|
47
51
|
|
|
48
52
|
## Arguments
|
|
49
53
|
|
|
50
|
-
| Argument
|
|
51
|
-
|
|
|
52
|
-
| `predicate` (or `until`) | `{ kind, ...fields }`, see the [predicate
|
|
53
|
-
| `timeout_ms`
|
|
54
|
-
| `since`
|
|
54
|
+
| Argument | What it does |
|
|
55
|
+
| ------------------------ | ----------------------------------------------------------------- |
|
|
56
|
+
| `predicate` (or `until`) | `{ kind, ...fields }`, see the [predicate reference](/predicates) |
|
|
57
|
+
| `timeout_ms` | Maximum wait. Default 4000 |
|
|
58
|
+
| `since` | Cursor from a prior act, to widen or narrow the window explicitly |
|
|
59
|
+
|
|
60
|
+
By default the wait only counts events since your last act, so a signal that fired before the action cannot satisfy it. A miss returns a near-miss diagnosis rather than a bare `false`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"pass": false,
|
|
65
|
+
"failureReason": "no element matched {\"text\":\"Deployments\"}",
|
|
66
|
+
"observed": "no matching element on the page",
|
|
67
|
+
"expected": "an element matching {\"text\":\"Deployments\"}",
|
|
68
|
+
"assertion": "element.present",
|
|
69
|
+
"evidence": { "presentTestids": ["login-email", "login-password", "login-submit"] }
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`presentTestids` there is the answer to the real question. The text was missing because the app had logged out, not because the render was slow.
|
|
55
74
|
|
|
56
75
|
## Prefer `act_and_wait` when you caused it
|
|
57
76
|
|
|
58
77
|
If your own action is supposed to trigger the thing, put the predicate in the action:
|
|
59
78
|
|
|
60
79
|
```json
|
|
61
|
-
{ "ref": "
|
|
80
|
+
{ "ref": "e605", "action": "click", "until": { "kind": "signal", "name": "auth:granted" } }
|
|
62
81
|
```
|
|
63
82
|
|
|
64
|
-
One call instead of two, no gap for an event to slip through, and
|
|
83
|
+
One call instead of two, no gap for an event to slip through, and, the part that actually matters, the expectation is recorded _before_ the action rather than chosen after it.
|
|
65
84
|
|
|
66
85
|
Save `wait_for` for consequences you did not cause.
|
package/docs/troubleshooting.mdx
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Troubleshooting: it worked on my tab
|
|
2
|
+
title: 'Troubleshooting: it worked on my tab'
|
|
3
3
|
sidebarTitle: Troubleshooting
|
|
4
4
|
description: Nothing connected, the click did nothing, the verdict says unknown. The failures people actually hit, and the diagnostic for each.
|
|
5
5
|
icon: stethoscope
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
**Run `npx @reticlehq/server doctor` first.** It checks Chromium, the daemon, the port and the connected sessions in one command, and it names the problem in most cases. If `sessions` reads zero, your app is not reaching the bridge, and a port mismatch is the usual reason.
|
|
9
|
+
|
|
10
|
+
Reticle is a verification layer that runs a dev-only SDK inside your running web app. Two halves have to be working: your agent needs the MCP server registered, and your app needs the SDK connected to the local daemon. Almost every failure below is one of those two halves being absent.
|
|
11
|
+
|
|
8
12
|
Most of these were hit while writing these docs. The diagnostics quoted are real output.
|
|
9
13
|
|
|
10
14
|
## Start here
|
|
@@ -17,7 +21,7 @@ npx @reticlehq/server doctor
|
|
|
17
21
|
reticle doctor
|
|
18
22
|
node v22.14.0
|
|
19
23
|
chromium ✓ installed
|
|
20
|
-
daemon ✓ running on :4400 (pid 24735,
|
|
24
|
+
daemon ✓ running on :4400 (pid 24735, v<the version you have>)
|
|
21
25
|
sessions ✓ 1 page connected
|
|
22
26
|
bridge port 4400 (your app must dial THIS port, not your dev-server port)
|
|
23
27
|
daemon log /Users/you/.reticle/daemon-4400.log
|
|
@@ -35,30 +39,51 @@ By far the most common. Reticle's own diagnostic is unusually good here. It tell
|
|
|
35
39
|
```json
|
|
36
40
|
{
|
|
37
41
|
"sessionCount": 0,
|
|
38
|
-
"why": "no browser session connected, but one WAS connected to this daemon earlier, so the wiring is correct. The tab was closed, navigated away, or hard-reloaded. Ask the human to reopen the app (or run `reticle open`), or reload the tab. You do not have to wait for the human: reticle_lease {action:\"acquire\", url} opens a browser Reticle drives itself."
|
|
42
|
+
"why": "no browser session connected, but one WAS connected to this daemon earlier, so the wiring is correct. The tab was closed, navigated away, or hard-reloaded. Ask the human to reopen the app (or run `reticle open`), or reload the tab. You do not have to wait for the human: reticle_lease {action:\"acquire\", url} opens a browser Reticle drives itself, and returns a sessionId you can use immediately (reach it with reticle_run {tool:\"reticle_lease\"} if it is not advertised directly; release it when you finish). Then call reticle_sessions again."
|
|
39
43
|
}
|
|
40
44
|
```
|
|
41
45
|
|
|
42
|
-
Read the `why`.
|
|
46
|
+
Read the `why`. If it says one **was** connected earlier, stop debugging your config and just reopen the tab. If it says nothing ever connected, the wiring itself is wrong, so work through the list below.
|
|
43
47
|
|
|
44
48
|
If nothing ever connected:
|
|
45
49
|
|
|
46
|
-
- Is the app running, and open in a browser?
|
|
50
|
+
- Is the app running, and open in a browser? If nothing is serving it, see [Who starts the dev server](#who-starts-the-dev-server): this is the agent's job, not a question to put to you.
|
|
47
51
|
- Is `reticle.connect()` actually executing? Check the dev guard is true.
|
|
48
52
|
- **Port mismatch.** This is the big one.
|
|
49
53
|
|
|
54
|
+
### Who starts the dev server
|
|
55
|
+
|
|
56
|
+
**The agent does.** If no dev server is listening, the agent reads your project's own dev script out of `package.json`, runs it in the background, tells you in one line that it is running and how to stop it, and carries on. It used to be told the opposite, "Reticle never starts the dev server for you, that's your job", and agents obeyed it exactly: they read the rule, declined to start anything, and ended the turn with nothing verified.
|
|
57
|
+
|
|
58
|
+
The daemon still does not do it, on purpose. A build process started by a long-lived background daemon is invisible to whoever's machine it is running on, was never consented to, cannot easily be stopped, and orphans when the daemon exits. It would also mean a piece of standing infrastructure runs arbitrary `package.json` scripts. An agent already has shell access in your repo, already runs install and build commands, and runs inside a host that asks you before it does. A dev server the agent starts is in the transcript, attributable to a step you approved, and stoppable.
|
|
59
|
+
|
|
60
|
+
The agent does not have to work out the command. When `reticle_sessions` comes back with nothing connected, it carries a `next_action` naming which of four cases this is and, where one applies, the literal command and port: start the dev server, run `reticle init`, open the app, or reopen a tab that went away. The command is read from this project's own scripts, so a project with no recognisable dev script gets no command rather than a guess.
|
|
61
|
+
|
|
62
|
+
The guards, which apply wherever this instruction appears:
|
|
63
|
+
|
|
64
|
+
1. **Never start a second one.** If something is already listening on the app's port, it is adopted.
|
|
65
|
+
2. **Never guess the command.** It comes from `package.json` scripts. If there is no recognisable dev script, the agent says so and stops rather than inventing one.
|
|
66
|
+
3. **Never kill anything**: not a dev server, not a daemon, not a port holder, including one it started.
|
|
67
|
+
4. **Background it, and say so.** You must know a server is running and how to stop it. A dev server you do not know about is the same failure one step later.
|
|
68
|
+
5. **The permission prompt is your agent host's.** Nothing in Reticle bypasses or auto-approves it, and you can always decline.
|
|
69
|
+
|
|
70
|
+
The corollary matters as much: if a dev server IS already running and nothing connects, the cause is the SDK not loading in the page. An agent that answers this state by asking you to start a server you are already running has ended the conversation with nothing fixed. Work the checklist above instead.
|
|
71
|
+
|
|
50
72
|
### The port mismatch
|
|
51
73
|
|
|
52
74
|
Your app dials a port; the daemon listens on one. When they disagree, the app runs perfectly and silently never connects. The browser console says so:
|
|
53
75
|
|
|
54
76
|
```
|
|
55
|
-
[Reticle] could not
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
[Reticle] this page could not open a websocket to ws://localhost:4460/reticle. 3 attempts, all
|
|
78
|
+
failed. That is everything the page can see: from inside the browser it cannot tell a daemon that
|
|
79
|
+
is not there from one it is not allowed to reach, so this is not evidence about the daemon. What
|
|
80
|
+
answers it: run `npx @reticlehq/server status` to see whether one is listening and on which port.
|
|
81
|
+
If your app runs in a container, devcontainer or WSL, the daemon is on a different host, so set
|
|
82
|
+
the URL explicitly (Vite: VITE_RETICLE_WS_URL, or reticle.connect({ url })). An https page cannot
|
|
83
|
+
open a ws:// socket at all. Still retrying…
|
|
59
84
|
```
|
|
60
85
|
|
|
61
|
-
That message names the exact
|
|
86
|
+
That message names the exact address it tried. Compare its port with `bridge port` from `doctor` and set whichever is wrong:
|
|
62
87
|
|
|
63
88
|
```bash
|
|
64
89
|
VITE_RETICLE_WS_URL=ws://localhost:4400/reticle pnpm dev
|
|
@@ -188,6 +213,51 @@ Stale sessions pointing at dev servers you restarted an hour ago look completely
|
|
|
188
213
|
|
|
189
214
|
`reticle_session { action: "end" }` when you are done, `{ action: "yield" }` when pausing. Both revive on your next action.
|
|
190
215
|
|
|
216
|
+
## Frequently asked questions
|
|
217
|
+
|
|
218
|
+
<AccordionGroup>
|
|
219
|
+
<Accordion title="Why does reticle_sessions return nothing when my app is clearly running?">
|
|
220
|
+
Your app running is not the same as your app being connected. The SDK has to dial the daemon's
|
|
221
|
+
bridge port over a WebSocket. If `npx @reticlehq/server doctor` shows the daemon up and
|
|
222
|
+
`sessions` at zero, either `reticle.connect()` never executed (check the dev guard is actually
|
|
223
|
+
true) or the app dialled the wrong port. The browser console names the exact address it tried.
|
|
224
|
+
</Accordion>
|
|
225
|
+
|
|
226
|
+
<Accordion title="Which port should my app dial?">
|
|
227
|
+
The bridge port, which defaults to `4400`. It is **not** your dev-server port. A dev server on
|
|
228
|
+
4312 and a daemon on 4400 is the normal, correct arrangement. `doctor` prints the bridge port
|
|
229
|
+
under `bridge port`.
|
|
230
|
+
</Accordion>
|
|
231
|
+
|
|
232
|
+
<Accordion title="I restarted my agent and the reticle tools still are not there.">
|
|
233
|
+
If you registered the MCP server while the agent was running, restart the agent process itself:
|
|
234
|
+
quit and reopen Claude Code, reload the window in Cursor, or press Start in `.vscode/mcp.json` in
|
|
235
|
+
VS Code. `/mcp` only manages servers that were already loaded, so it cannot discover a new one. If
|
|
236
|
+
the tools are still missing, run `claude mcp list` and check `reticle` is present.
|
|
237
|
+
</Accordion>
|
|
238
|
+
|
|
239
|
+
<Accordion title="Does a verdict of unknown mean the test failed?">
|
|
240
|
+
No, and it does not mean it passed either. `verified: "unknown"` means Reticle drove the app and
|
|
241
|
+
could not tell what happened, usually because the page never settled inside the observation
|
|
242
|
+
window. Report it as unknown. If you hit one on an unthrottled context and believe it is wrong,
|
|
243
|
+
that is a Reticle defect worth sending with `reticle_feedback`.
|
|
244
|
+
</Accordion>
|
|
245
|
+
|
|
246
|
+
<Accordion title="Is it safe to kill port 4400 to restart things?">
|
|
247
|
+
No. `lsof -ti tcp:4400 | xargs kill -9` matches your agent's MCP proxy as well as the daemon and
|
|
248
|
+
takes the connection down with it. Use `npx @reticlehq/server stop`, or at minimum add
|
|
249
|
+
`-sTCP:LISTEN` so only the listener matches.
|
|
250
|
+
</Accordion>
|
|
251
|
+
|
|
252
|
+
<Accordion title="Why does my agent click a button and nothing happens?">
|
|
253
|
+
Check the response for a `warning` about a throttled tab. A backgrounded browser tab has its
|
|
254
|
+
timers suppressed, so synthetic pointer gestures can silently no-op while the action still
|
|
255
|
+
reports success. Drive a lease instead, which is a context Reticle owns and never backgrounds.
|
|
256
|
+
If the tab is fine, check `occluded` on the element: a click landing on a modal overlay looks
|
|
257
|
+
identical from the outside.
|
|
258
|
+
</Accordion>
|
|
259
|
+
</AccordionGroup>
|
|
260
|
+
|
|
191
261
|
<Card title="Still stuck?" icon="comments" href="/tools-session-and-feedback">
|
|
192
262
|
`reticle feedback --agent` works even when nothing is installed and the daemon will not start.
|
|
193
263
|
</Card>
|
package/docs/usage.md
CHANGED
|
@@ -4,7 +4,9 @@ description: 'The full reference and cookbook: every tool, flag, and workflow, w
|
|
|
4
4
|
icon: book
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Every Reticle drive is the same four steps: **look** (`reticle_snapshot` / `reticle_query`), **act** (`reticle_act` / `reticle_act_sequence`), **observe** (`reticle_observe` / `reticle_network` / `reticle_state`), **assert** (`reticle_assert`). Only `reticle_act_and_wait` and `reticle_assert` produce a verdict, so a run that ends anywhere else proved nothing. This page is the full reference for every tool, predicate, action and flag in that loop.
|
|
8
|
+
|
|
9
|
+
If you haven't set up Reticle yet, start with [Getting Started](getting-started.md).
|
|
8
10
|
|
|
9
11
|
> **Start here instead, unless you want the long form.** This page predates the focused reference and still holds the fullest single narrative, but almost everything in it now has a page of its own that is shorter, searchable and verified against a running app:
|
|
10
12
|
>
|
|
@@ -41,6 +43,9 @@ The full reference and cookbook. If you haven't set up Reticle yet, start with [
|
|
|
41
43
|
13. [Best practices & gotchas](#13-best-practices--gotchas)
|
|
42
44
|
14. [FAQ](#14-faq)
|
|
43
45
|
15. [Security & privacy](#15-security--privacy)
|
|
46
|
+
16. [Presenter mode, narration & fake clock](#16-presenter-mode-narration--fake-clock-watch--control)
|
|
47
|
+
17. [Evidence-of-effect, act+await, state, capabilities, replay](#17-evidence-of-effect-actawait-state-capabilities-replay)
|
|
48
|
+
18. [Real input mode: native hover & drag](#18-real-input-mode-native-hover--drag)
|
|
44
49
|
|
|
45
50
|
---
|
|
46
51
|
|
|
@@ -86,7 +91,7 @@ List connected tabs. → `{ sessions: [{ sessionId, url, title, lastSeenMs, hidd
|
|
|
86
91
|
A semantic, accessibility-tree view of the page.
|
|
87
92
|
|
|
88
93
|
- **args:** `mode?: 'full' | 'interactive' | 'status'` (default `full`), `scope?` (CSS selector or ref), `diff?: boolean`, `sessionId?`.
|
|
89
|
-
- **returns:** `{ tree, status: { route, title, visibleDialogs }, nodes, truncated, cost: { bytes, tokens } }`.
|
|
94
|
+
- **returns:** `{ tree, status: { route, title, visibleDialogs, overlayHidingPage }, nodes, truncated, cost: { bytes, tokens } }`.
|
|
90
95
|
- **`diff: true`** returns only what changed since your last snapshot of the same scope/mode: `{ mode: 'delta', delta: { added, removed, addedCount, removedCount } }` or `{ mode: 'unchanged' }` (no full tree). The first call (and any call after a route change) still returns the full tree. ~99% fewer tokens to re-look after an action; see [token-efficiency.md](token-efficiency.md).
|
|
91
96
|
- **`cost`** is an estimated size of the result. Re-scope (`mode`/`scope`) before reading if large.
|
|
92
97
|
|
|
@@ -123,11 +128,11 @@ Deep detail on one element, including the signals a snapshot/a11y tree omits, so
|
|
|
123
128
|
|
|
124
129
|
Perform one action / several in order.
|
|
125
130
|
|
|
126
|
-
- **`reticle_act` args:** `ref`, `action`, `args?`, `refuseWhenThrottled?`, `sessionId?`. → `{ since, dispatched, settled, settleReason, result, session, warning? }` where `result = { ok, ref, action, dispatched, settled, settleReason, effect }`. The `session` block `{ lastSeenMs, throttled, focused }`
|
|
131
|
+
- **`reticle_act` args:** `ref`, `action`, `args?`, `refuseWhenThrottled?`, `sessionId?`. → `{ since, dispatched, settled, settleReason, result, session, warning? }` where `result = { ok, ref, action, dispatched, settled, settleReason, effect }`. The `session` block `{ lastSeenMs, throttled, focused }` reports tab health on every act; when `throttled` is true a `warning` string is also attached. Pass `refuseWhenThrottled: true` to hard-fail instead of warning (opt-in; default is warn-only so background testing never breaks).
|
|
127
132
|
- **`reticle_act_sequence` args:** `steps: [{ ref, action, args? }]`. → `{ since, dispatched, result }` where `result = { ok, count, effects: [...], steps: [...] }` (one `effect` per step; each step carries its own `dispatched`/`settled`/`settleReason`).
|
|
128
133
|
- See [§5](#5-actions-full-list) for the action list.
|
|
129
134
|
|
|
130
|
-
**Dispatch vs settle
|
|
135
|
+
**Dispatch vs settle.** The action is two phases: the **dispatch** (the synchronous click/fill, which is what can fail) and the **settle** (waiting one animation frame so React's commit lands before we return). The settle is **bounded** (~200ms): in a throttled/background tab `requestAnimationFrame` never fires, so Reticle falls back to a timer and resolves anyway. A settle timeout is therefore **never an error**: `reticle_act` resolves with `settled:false, settleReason:"timeout"` and the dispatch (the click) has still landed. Only a real dispatch failure (stale ref, wrong element type) throws.
|
|
131
136
|
|
|
132
137
|
| top-level field | meaning |
|
|
133
138
|
| --- | --- |
|
|
@@ -179,7 +184,7 @@ The timeline + summary of what happened.
|
|
|
179
184
|
Act, then wait for a predicate: the whole act→observe→assert loop in one hop.
|
|
180
185
|
|
|
181
186
|
- **args:** `ref`, `action`, `args?`, `until: <predicate>`, `timeout_ms?` (default 4000; 0 = evaluate once), `refuseWhenThrottled?`, `sessionId?`.
|
|
182
|
-
- **returns:** `{ effect, verdict, trace, session, warning? }`. `effect` is the action result (`{ ok, ref, action }`), `verdict` is `{ pass, evidence?, failureReason? }`, `trace` is the reaction report of everything the app did after the action, and `session`
|
|
187
|
+
- **returns:** `{ effect, verdict, trace, session, warning? }`. `effect` is the action result (`{ ok, ref, action }`), `verdict` is `{ pass, evidence?, failureReason? }`, `trace` is the reaction report of everything the app did after the action, and `session` is the tab-health block `{ lastSeenMs, throttled, focused }` (with a `warning` when throttled). A failing `verdict` still returns `effect` + `trace` so you can see what _did_ happen. The predicate is automatically floored at this act's cursor, so it only matches events the action actually caused.
|
|
183
188
|
|
|
184
189
|
### `reticle_wait_for`
|
|
185
190
|
|
|
@@ -210,7 +215,7 @@ Compare what the API **returned** against what the page **renders**.
|
|
|
210
215
|
|
|
211
216
|
Fast targeted lookups without a full timeline.
|
|
212
217
|
|
|
213
|
-
- `reticle_network({ since?, method?, urlContains?, status? })` → `{ calls }`
|
|
218
|
+
- `reticle_network({ since?, method?, urlContains?, status?, bodies? })` → `{ calls }`. Pass `bodies: false` for a body-free listing (method/url/status/timing only); bodies dominate the payload, so the common "did POST /x return 200?" read gets much cheaper.
|
|
214
219
|
- `reticle_console({ level?, since? })` → `{ logs }`
|
|
215
220
|
- `reticle_animations()` → running/recent animations.
|
|
216
221
|
|
|
@@ -281,10 +286,10 @@ List interactive elements + console-error count for autonomous exploration. See
|
|
|
281
286
|
|
|
282
287
|
### Flows, recorder & self-healing (`.reticle/`)
|
|
283
288
|
|
|
284
|
-
`reticle_contract_save`, `reticle_flow_save` / `reticle_flow_save_recorded` / `reticle_flow {action:"list"}` / `reticle_flow {action:"load"}` / `reticle_flow_replay` / `
|
|
289
|
+
`reticle_contract_save`, `reticle_flow_save` / `reticle_flow_save_recorded` / `reticle_flow {action:"list"}` / `reticle_flow {action:"load"}` / `reticle_flow_replay` / `reticle_verify {action:"flows"}`, `reticle_flow_heal`, `reticle_annotate`: record once, replay forever (anchored on testid/signal, or on an auto-derived component/source anchor when there's no testid), with legible drift + self-heal. Full guide: [Flows, the recorder & self-healing](flows.md).
|
|
285
290
|
|
|
286
|
-
- **`
|
|
287
|
-
- **Decision envelope:** on a drift/fail, `reticle_flow_replay` (and each `
|
|
291
|
+
- **`reticle_verify({ action: "flows", names?, sessionId? })`** is the regression-suite call: it replays EVERY saved flow (or a subset) deterministically and returns one verdict `{ status, passed, failed, failures: [{ flow, verdict, whatChanged, whereInSource, nextAction }] }`. Passing flows are counted; only failures carry detail. Run it after any change: one call, no LLM per flow.
|
|
292
|
+
- **Decision envelope:** on a drift/fail, `reticle_flow_replay` (and each `reticle_verify {action:"flows"}` failure) returns the actionable fix: `whatChanged`, `whereInSource` (`file:line`), and a one-line `nextAction` (e.g. "rebind the anchor to 'new-deploy', or update the flow if intended").
|
|
288
293
|
|
|
289
294
|
### Human-in-the-loop control
|
|
290
295
|
|
|
@@ -344,11 +349,14 @@ A **predicate** declares what should be true. `reticle_assert` / `reticle_wait_f
|
|
|
344
349
|
// An element exists / is in a state
|
|
345
350
|
{ "kind": "element", "query": { "role": "dialog", "name": "Confirm" }, "state": "visible" }
|
|
346
351
|
// query supports: role, name, text, label, placeholder, testid, alt, scope
|
|
347
|
-
// state: visible | hidden | enabled | disabled | checked | expanded | focused | present
|
|
352
|
+
// state: visible | hidden | enabled | disabled | checked | expanded | focused | present | inViewport
|
|
353
|
+
// inViewport asserts the element is in the viewport NOW (not just in the DOM), so a scrollIntoView is gradeable
|
|
348
354
|
// add "absent": true to assert it is NOT there (regression / removal)
|
|
349
355
|
|
|
350
|
-
// Visible text
|
|
356
|
+
// Visible text: page-wide, or `scope` it to a subtree (CSS selector or ref) so a match
|
|
357
|
+
// in a background tab can't satisfy an assertion about the dialog that just opened
|
|
351
358
|
{ "kind": "text", "contains": "Saved successfully", "visible": true }
|
|
359
|
+
{ "kind": "text", "contains": "Floor 3", "scope": "[role=dialog]" }
|
|
352
360
|
|
|
353
361
|
// A network call happened
|
|
354
362
|
{ "kind": "net", "method": "POST", "urlContains": "/api/order", "status": 200, "since": 1820 }
|
|
@@ -406,7 +414,7 @@ A `state` assertion is graded as a **consequence** (a wrong element or stale ren
|
|
|
406
414
|
| `select` | `{ value }` | `<select>` option |
|
|
407
415
|
| `check` / `uncheck` | n/a | checkbox/radio |
|
|
408
416
|
| `submit` | n/a | submits the element's `<form>` |
|
|
409
|
-
| `press` | `{ key }` | keydown/up (default `Enter`) |
|
|
417
|
+
| `press` | `{ key, modifiers? }` | keydown/up (default `Enter`); `modifiers`: an array of `Meta` / `Control` / `Shift` / `Alt` for Cmd+K-style shortcuts |
|
|
410
418
|
| `scrollIntoView` | n/a | |
|
|
411
419
|
| `upload` | `{ name, content?, type? }` | sets a file on `<input type=file>` |
|
|
412
420
|
| `drag` | `{ toRef }` | pointer-based drag (dnd-kit / rbd) + HTML5 DnD |
|
|
@@ -483,7 +491,7 @@ reticle_assert({ timeout_ms: 3000, predicate: { kind: "element",
|
|
|
483
491
|
query: { text: "Invoice #4821", scope: "[data-testid=item-list]" }, state: "visible" } })
|
|
484
492
|
```
|
|
485
493
|
|
|
486
|
-
> Note: if your list is **virtualized** (react-window/virtuoso), off-screen
|
|
494
|
+
> Note: if your list is **virtualized** (react-window/virtuoso), an off-screen row is not in the DOM at all, so `reticle_query` correctly finds nothing. Use **`reticle_scroll_to`** to scroll the windowed container until the row renders, then query it: `reticle_scroll_to({ by: "text", value: "Invoice #4821", container: "[data-testid=item-list]" })`. Asserting against the data with `reticle.signal` or `reticle_state` also works and is cheaper.
|
|
487
495
|
|
|
488
496
|
### "Login form: does it actually authorize?"
|
|
489
497
|
|
|
@@ -560,7 +568,7 @@ reticle.describe({
|
|
|
560
568
|
});
|
|
561
569
|
```
|
|
562
570
|
|
|
563
|
-
The agent reads this back with `reticle_capabilities()`; see [§3](#3-
|
|
571
|
+
The agent reads this back with `reticle_capabilities()`; see [§3](#3-the-tools-full-reference).
|
|
564
572
|
|
|
565
573
|
```jsonc
|
|
566
574
|
reticle_assert({ timeout_ms: 30000, predicate: {
|
|
@@ -569,7 +577,7 @@ reticle_assert({ timeout_ms: 30000, predicate: {
|
|
|
569
577
|
|
|
570
578
|
#### Keeping signals from drifting (lint)
|
|
571
579
|
|
|
572
|
-
Signals only help if you actually emit one whenever user-visible state changes. The `@reticlehq/eslint-plugin` package ships
|
|
580
|
+
Signals only help if you actually emit one whenever user-visible state changes. The `@reticlehq/eslint-plugin` package ships `reticle/require-signal-on-mutation`, which flags any function that calls a configured store **mutator** but never fires the **signal callee** in the same body, so the signal map can't silently fall behind the store. (The package also ships `reticle/no-internal-tags`; both are on in its `recommended` config.)
|
|
573
581
|
|
|
574
582
|
```js
|
|
575
583
|
// eslint.config.mjs
|
|
@@ -705,7 +713,7 @@ Reticle is cheap by design ([benchmark](token-efficiency.md)), but keep it that
|
|
|
705
713
|
- **Stable handles for controls.** Prefer `data-testid` over names that include dynamic counts (e.g. "Notifications (3)"); the count changes the accessible name.
|
|
706
714
|
- **Always thread `since`.** Pass the cursor from `reticle_act` into `observe`/`assert` so you only consider what happened _after_ the action.
|
|
707
715
|
- **Use `timeout_ms` for async.** Don't assert instantly on something that arrives over the network or after a re-render.
|
|
708
|
-
- **Watch `session.throttled
|
|
716
|
+
- **Watch `session.throttled`.** Background tabs throttle timers/rAF/pointer gestures, so an act can silently no-op. Every `reticle_act` / `reticle_assert` / `reticle_act_and_wait` result carries `session: { lastSeenMs, throttled, focused }` and, when throttled, a `warning`. Refocus the tab (or run it foregrounded) before driving; pass `refuseWhenThrottled: true` to hard-fail instead.
|
|
709
717
|
- **Scope big pages.** On dashboards with hundreds of elements, scope queries to the panel you care about.
|
|
710
718
|
- **Never breaks your app.** Observers are additive and reversible (`reticle.disconnect()` restores patched globals). It won't interfere with your app's behavior.
|
|
711
719
|
|
|
@@ -713,19 +721,33 @@ Reticle is cheap by design ([benchmark](token-efficiency.md)), but keep it that
|
|
|
713
721
|
|
|
714
722
|
## 14. FAQ
|
|
715
723
|
|
|
716
|
-
|
|
724
|
+
### Does this run in production?
|
|
725
|
+
|
|
726
|
+
No. Keep `reticle.connect()` behind a dev guard. The SDK is side-effect-free and tree-shakes out of prod builds.
|
|
727
|
+
|
|
728
|
+
### Do I have to change my components?
|
|
729
|
+
|
|
730
|
+
No, for basic look/act/observe. You'll get better results by adding `data-testid`s and labels where the agent needs precision.
|
|
731
|
+
|
|
732
|
+
### Does it work without React?
|
|
733
|
+
|
|
734
|
+
The core (DOM/network/route/console/animation/snapshot/actions) is framework-agnostic and is gated against a vanilla-TS app. React, Next.js, Remix and Astro each have an app and a CI gate. SvelteKit is wired end-to-end. `reticle init` writes the client hook and the Vite plugin, and the plugin stamps `data-reticle-source` into `.svelte` components so verdicts carry `file:line`. But there is still no SvelteKit app in CI, so it is unverified rather than supported. Vue has a Pinia store adapter and nothing else: no detection, no `.vue` stamping, no gate. See [what Svelte support is and is not](getting-started.md#what-svelte-support-is-and-what-it-is-not).
|
|
735
|
+
|
|
736
|
+
### Can it judge whether my UI looks good?
|
|
737
|
+
|
|
738
|
+
No. Reticle verifies behavior, not aesthetics. Visual/pixel correctness and "does it feel right" remain human (or a visual-diff tool).
|
|
717
739
|
|
|
718
|
-
|
|
740
|
+
### Does it replace Playwright/Cypress?
|
|
719
741
|
|
|
720
|
-
|
|
742
|
+
No. Those are your scripted CI suite. Reticle is for in-loop verification while the agent codes, and for the cases you never automated. They compose.
|
|
721
743
|
|
|
722
|
-
|
|
744
|
+
### How does it compare to Playwright MCP / Chrome DevTools MCP?
|
|
723
745
|
|
|
724
|
-
|
|
746
|
+
Those let an agent drive/ inspect a _separate_ browser; Reticle verifies your _own running app_ (real session/auth) with assertions + regression as first-class, far more cheaply. See [Reticle vs Playwright MCP](vs-playwright-mcp.mdx) and [Reticle vs Chrome DevTools MCP](vs-chrome-devtools-mcp.mdx).
|
|
725
747
|
|
|
726
|
-
|
|
748
|
+
### Multiple tabs/apps?
|
|
727
749
|
|
|
728
|
-
|
|
750
|
+
Each is a session; pass `sessionId` to any tool when more than one is connected (`reticle_sessions` lists them).
|
|
729
751
|
|
|
730
752
|
---
|
|
731
753
|
|
|
@@ -794,7 +816,7 @@ A session starts on the agent's first activity and must reliably end even when t
|
|
|
794
816
|
- **Agent kills the Reticle server process** (so no push can arrive) → the SDK self-ends the session after it can't reach the bridge for `BRIDGE_LOST_MS` (~15s), showing "lost connection to Reticle."
|
|
795
817
|
- **Slow-but-alive agent** → if it goes quiet long enough to auto-end and then acts again, the session **revives** automatically (an explicit `reticle_session {action:"end"}` stays terminal).
|
|
796
818
|
|
|
797
|
-
Tune the idle window with `reticle_session({ idleEndMs })`; it updates both the browser timer and the server reaper. The human keeps the panel (with Copy/Export of the run) after any end.
|
|
819
|
+
Tune the idle window with `reticle_session({ action: "tune", idleEndMs })`; it updates both the browser timer and the server reaper. The human keeps the panel (with Copy/Export of the run) after any end.
|
|
798
820
|
|
|
799
821
|
### `reticle_session {action:"narrate"}`: show the agent's intent
|
|
800
822
|
|
|
@@ -80,3 +80,44 @@ Neither tool forbids the other. A reasonable setup:
|
|
|
80
80
|
handlers, double submits, silent validation.
|
|
81
81
|
</Card>
|
|
82
82
|
</CardGroup>
|
|
83
|
+
|
|
84
|
+
## FAQ
|
|
85
|
+
|
|
86
|
+
<AccordionGroup>
|
|
87
|
+
|
|
88
|
+
<Accordion title="Which two bugs did Chrome DevTools MCP miss?">
|
|
89
|
+
The ones where the page is fine and the program is not: a mutation that never reached the store,
|
|
90
|
+
and a success handler that fired for the wrong reason. DevTools MCP is DOM-and-network only, so it
|
|
91
|
+
can describe what the page looks like and what it requested, but it cannot ask the application
|
|
92
|
+
what it believes. There is nothing in the DOM or the network log that separates those cases from
|
|
93
|
+
working code.
|
|
94
|
+
</Accordion>
|
|
95
|
+
|
|
96
|
+
<Accordion title="If DevTools MCP is cheaper, why not just use it?">
|
|
97
|
+
Because it is cheaper partly by looking at less. Cost per look is half a metric; the other half is
|
|
98
|
+
whether the look catches the bug. Verification Efficiency combines them and gates on catching
|
|
99
|
+
every bug with zero false alarms, and on that measure Reticle leads 12.27 to 10.55. The 7%
|
|
100
|
+
per-look advantage also inverts on a real app: on the production dashboard measured here DevTools'
|
|
101
|
+
snapshot was 1,105 tokens against Reticle's 678.
|
|
102
|
+
</Accordion>
|
|
103
|
+
|
|
104
|
+
<Accordion title="Can I run both MCP servers at once?">
|
|
105
|
+
Yes. They are independent servers with distinct tool names. A reasonable split is DevTools MCP for
|
|
106
|
+
performance traces, deep Chrome internals and real screenshots, and Reticle for the verify loop
|
|
107
|
+
and the source pointer.
|
|
108
|
+
</Accordion>
|
|
109
|
+
|
|
110
|
+
<Accordion title="Do I have to instrument my app to use Reticle?">
|
|
111
|
+
You have to add the dev-only SDK, which is what `npx @reticlehq/server init` does. Beyond that,
|
|
112
|
+
look, act and observe work with zero component changes. Signals and registered stores are
|
|
113
|
+
optional, and they are what buys the assertions DevTools MCP structurally cannot make. If
|
|
114
|
+
instrumenting is off the table entirely, DevTools MCP is the correct choice.
|
|
115
|
+
</Accordion>
|
|
116
|
+
|
|
117
|
+
<Accordion title="Does Reticle give me performance traces?">
|
|
118
|
+
Not in the way Chrome's own tooling does, and it does not pretend to. It can tell you a React
|
|
119
|
+
render storm is happening (commit rate climbing with no DOM mutation), which DevTools MCP does not
|
|
120
|
+
surface. For flame charts and deep tracing, use Chrome's.
|
|
121
|
+
</Accordion>
|
|
122
|
+
|
|
123
|
+
</AccordionGroup>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Reticle vs Playwright MCP: outside in, inside out
|
|
2
|
+
title: 'Reticle vs Playwright MCP: outside in, inside out'
|
|
3
3
|
sidebarTitle: vs Playwright MCP
|
|
4
4
|
description: Playwright MCP drives any website from the outside. Reticle verifies an app you own from the inside. They solve different problems, and they compose.
|
|
5
5
|
icon: masks-theater
|
|
@@ -49,7 +49,7 @@ This is not a courtesy section. There are real cases where Playwright is the cor
|
|
|
49
49
|
- **Assert on the app's own success signal.** When your app emits `auth:granted`, Reticle can require that exact signal. Playwright can check that a DOM element appeared, which a mock also does.
|
|
50
50
|
- **Read application state.** `stateDiffs` shows `auth` moving from `null` to a real user. A POST that returns `200` without touching state is caught here and nowhere else.
|
|
51
51
|
- **Map a DOM node to source.** `src/components/Login.tsx:81`, via the React fiber tree. Playwright can tell you the selector; it cannot tell you the file.
|
|
52
|
-
- **Grade its own evidence.** Every verdict reports `honesty.grade`: `presence`, `state` or `signal
|
|
52
|
+
- **Grade its own evidence.** Every verdict reports `honesty.grade`: `presence`, `state`, `net` or `signal`, weakest to strongest. Plus whether the capture was clean and complete.
|
|
53
53
|
- **Say "unknown".** Reticle distinguishes "did not happen" from "I could not tell". That third state is the difference between a check and a coin flip.
|
|
54
54
|
|
|
55
55
|
## The false-green case
|
|
@@ -83,3 +83,43 @@ Reticle also has a driven mode (`reticle drive`) that provides real CDP input an
|
|
|
83
83
|
that looks fine.
|
|
84
84
|
</Card>
|
|
85
85
|
</CardGroup>
|
|
86
|
+
|
|
87
|
+
## FAQ
|
|
88
|
+
|
|
89
|
+
<AccordionGroup>
|
|
90
|
+
|
|
91
|
+
<Accordion title="Can I use Reticle and Playwright MCP at the same time?">
|
|
92
|
+
Yes, and it is a common setup. They are separate MCP servers with separate tool namespaces, so
|
|
93
|
+
nothing collides. Drive with Playwright where it is stronger (a third-party OAuth hop, a
|
|
94
|
+
cross-browser pass, a genuine pixel diff) and assert with Reticle where the question is "did my
|
|
95
|
+
app actually do the thing".
|
|
96
|
+
</Accordion>
|
|
97
|
+
|
|
98
|
+
<Accordion title="Does Reticle work on a site I do not own?">
|
|
99
|
+
No. Reticle reads the program from inside the page, which requires a dev-only SDK in the app. No
|
|
100
|
+
SDK, no Reticle. That is the single hardest boundary between these two tools, and it is why
|
|
101
|
+
Playwright MCP is the right answer for anything you cannot instrument.
|
|
102
|
+
</Accordion>
|
|
103
|
+
|
|
104
|
+
<Accordion title="Is Reticle actually cheaper, or only on the pages you chose?">
|
|
105
|
+
On a full-tree-vs-full-tree comparison the gap is modest, roughly 1.8x. The large gap comes from
|
|
106
|
+
not needing the full tree: Playwright MCP's primary perception primitive returns the accessibility
|
|
107
|
+
tree, Reticle's answers a specific question. On a small page both are cheap. On the real
|
|
108
|
+
production dashboard measured here Reticle was about 2.1x leaner. Force Reticle to dump the whole
|
|
109
|
+
page every step and you save very little.
|
|
110
|
+
</Accordion>
|
|
111
|
+
|
|
112
|
+
<Accordion title="Does Reticle support Firefox or WebKit?">
|
|
113
|
+
Not meaningfully. The always-on in-page SDK is browser-agnostic, but the driven paths that provide
|
|
114
|
+
real pointer input and screenshots go through CDP and are Chromium-oriented. If cross-browser is a
|
|
115
|
+
requirement, that is a Playwright job.
|
|
116
|
+
</Accordion>
|
|
117
|
+
|
|
118
|
+
<Accordion title="Do I have to give up my existing Playwright suite?">
|
|
119
|
+
No, and you should not. A scripted Playwright or Cypress suite is what gates your releases.
|
|
120
|
+
Reticle is the in-loop check while an agent is coding and the safety net for the flows nobody ever
|
|
121
|
+
got round to automating. Your existing `data-testid` attributes work in Reticle unchanged, so the
|
|
122
|
+
two share the same anchors.
|
|
123
|
+
</Accordion>
|
|
124
|
+
|
|
125
|
+
</AccordionGroup>
|
package/docs/vs-screenshots.mdx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Screenshots lie: why pixels cannot verify AI-generated code
|
|
2
|
+
title: 'Screenshots lie: why pixels cannot verify AI-generated code'
|
|
3
3
|
sidebarTitle: vs screenshots
|
|
4
4
|
description: The failures that AI coding agents actually produce are invisible in a rendered frame. Here is why a better vision model does not fix that.
|
|
5
5
|
icon: image
|
|
@@ -86,6 +86,47 @@ We have not proven that Reticle makes agents _fix_ more bugs. A controlled test
|
|
|
86
86
|
|
|
87
87
|
What is measured is detection: 10 of 10 injected regressions caught with zero false alarms, against 9 and 8 for the alternatives.
|
|
88
88
|
|
|
89
|
+
## FAQ
|
|
90
|
+
|
|
91
|
+
<AccordionGroup>
|
|
92
|
+
|
|
93
|
+
<Accordion title="Would a better vision model fix this?">
|
|
94
|
+
No, and this is the single most expensive misconception on the page. Model quality improves the
|
|
95
|
+
_reading_ of evidence; it cannot create evidence that was never captured. A screenshot of a page
|
|
96
|
+
whose bug is non-visual contains no signal about that bug at any resolution, so a stronger model
|
|
97
|
+
produces a more confident version of the same wrong answer.
|
|
98
|
+
</Accordion>
|
|
99
|
+
|
|
100
|
+
<Accordion title="So screenshots are useless?">
|
|
101
|
+
No. They are the correct instrument for a specific class of bug: a font that failed to load, a GPU
|
|
102
|
+
or compositing glitch, layout that breaks only at one viewport, and anything where the rendered
|
|
103
|
+
frame _is_ the thing under test. Reticle ships `reticle_screenshot` and `reticle_visual_diff` for
|
|
104
|
+
exactly those. If pixel regression is your primary concern, a dedicated visual tool will serve you
|
|
105
|
+
better.
|
|
106
|
+
</Accordion>
|
|
107
|
+
|
|
108
|
+
<Accordion title="What does Reticle read instead of pixels?">
|
|
109
|
+
The program: the network log (did the request fire, how many times, what did it return), the
|
|
110
|
+
console, client-side routing, registered store state, and any signal the app itself emits. A
|
|
111
|
+
response carrying `net.total: 1` rules out a double submit; a non-empty `stateDiffs` rules out
|
|
112
|
+
mock data; `consoleErrors: 0` rules out "it worked but threw".
|
|
113
|
+
</Accordion>
|
|
114
|
+
|
|
115
|
+
<Accordion title="Does that mean Reticle catches every bug?">
|
|
116
|
+
No. It has not been shown to make agents _fix_ more bugs; a controlled fix-rate test did not show
|
|
117
|
+
an improvement and we consider that question open. What is measured is detection: 10 of 10
|
|
118
|
+
injected regressions caught with zero false alarms, against 9 and 8 for the alternatives. And a
|
|
119
|
+
purely visual regression still needs the opt-in visual diff.
|
|
120
|
+
</Accordion>
|
|
121
|
+
|
|
122
|
+
<Accordion title="Can I keep using screenshots alongside Reticle?">
|
|
123
|
+
Yes, and for a visual product you probably should. The argument here is not that screenshots are
|
|
124
|
+
bad, it is that a screenshot cannot answer "did the code work", which is the question an AI coding
|
|
125
|
+
agent needs answered on every change.
|
|
126
|
+
</Accordion>
|
|
127
|
+
|
|
128
|
+
</AccordionGroup>
|
|
129
|
+
|
|
89
130
|
<CardGroup cols={2}>
|
|
90
131
|
<Card title="See a real verdict" icon="rocket" href="/quickstart">
|
|
91
132
|
Five minutes, and the output on that page was captured live.
|
package/docs/what-is-reticle.mdx
CHANGED
|
@@ -12,7 +12,7 @@ It exists because an agent that writes code cannot otherwise tell whether that c
|
|
|
12
12
|
## How it works, in three parts
|
|
13
13
|
|
|
14
14
|
1. **An SDK inside the app.** Dev-only, stripped from production builds, added by a build plugin. It instruments the DOM, network, routing, console, animations, and your state library.
|
|
15
|
-
2. **A local bridge.** The SDK connects over a WebSocket to a daemon on your machine.
|
|
15
|
+
2. **A local bridge.** The SDK connects over a WebSocket to a daemon on your machine. The daemon never starts or manages your dev server. It attaches to the app that is running. If none is, the agent is the one told to start it, using your project's own dev script, in the background, and saying so.
|
|
16
16
|
3. **An MCP server.** The daemon exposes the app to your agent as tools. `reticle_snapshot` to look, `reticle_act_and_wait` to act and prove in one call, `reticle_state` and `reticle_network` to observe, `reticle_assert` to check.
|
|
17
17
|
|
|
18
18
|
Everything stays on your machine. No application code, DOM, network traffic, or console output leaves it.
|
package/docs/why-reticle.mdx
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Why Reticle: seeing is not believing
|
|
2
|
+
title: 'Why Reticle: seeing is not believing'
|
|
3
3
|
sidebarTitle: Why Reticle
|
|
4
4
|
description: AI-written code fails by looking correct. Reticle reads the running program instead of the pixels, so a silent failure stops being invisible.
|
|
5
5
|
icon: lightbulb
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
**Use Reticle because AI-written code fails by looking correct.** A screenshot cannot show you whether a request fired, what status it returned, whether application state changed, or whether one click sent one request or two. Reticle reads the running program instead of the pixels, so those silent failures stop being invisible.
|
|
9
|
+
|
|
8
10
|
Software has always had bugs. What changed is the _kind_ of bug that reaches you.
|
|
9
11
|
|
|
10
12
|
A human writing code by hand produces errors that announce themselves: a stack trace, a red screen, a test that goes red. An agent writing code produces something more dangerous: a change that looks completely correct, renders beautifully, and does nothing. The page is fine. The data never saved.
|