@reticlehq/server 2.7.0 → 2.8.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/README.md +14 -5
- package/SKILL.md +64 -29
- package/dist/capsule/causal-summary.d.ts +20 -0
- package/dist/capsule/causal-summary.js +31 -6
- package/dist/capsule/divergence.js +7 -1
- package/dist/cli/chromium-hint.d.ts +33 -5
- package/dist/cli/chromium-hint.js +82 -12
- package/dist/cli/cli-doctor.js +28 -25
- package/dist/cli/cli-kill.d.ts +58 -0
- package/dist/cli/cli-kill.js +162 -0
- package/dist/cli/cli-launch.d.ts +17 -0
- package/dist/cli/cli-launch.js +28 -0
- package/dist/cli/cli-parse.d.ts +16 -1
- package/dist/cli/cli-parse.js +32 -9
- package/dist/cli/cli-port.d.ts +8 -0
- package/dist/cli/cli-port.js +16 -0
- package/dist/cli/cli-verify.js +2 -2
- package/dist/cli/doctor-sessions-line.d.ts +12 -0
- package/dist/cli/doctor-sessions-line.js +66 -0
- package/dist/cli/port-holder.d.ts +10 -0
- package/dist/cli/port-holder.js +26 -2
- package/dist/cli.js +99 -14
- package/dist/crawl/crawl-tools.js +12 -2
- package/dist/crawl/crawl.d.ts +13 -0
- package/dist/crawl/crawl.js +50 -2
- package/dist/events/contradictions.d.ts +14 -0
- package/dist/events/contradictions.js +26 -17
- package/dist/events/predicate-eval.d.ts +20 -1
- package/dist/events/predicate-eval.js +125 -3
- package/dist/events/predicate.js +170 -18
- package/dist/flows/flow-classify.js +1 -1
- package/dist/flows/flow-tools.js +12 -4
- package/dist/flows/heal-run.js +1 -1
- package/dist/honesty/blind-spots.js +6 -2
- package/dist/honesty/unsettled.d.ts +40 -0
- package/dist/honesty/unsettled.js +78 -0
- package/dist/honesty/verified.d.ts +7 -0
- package/dist/honesty/verified.js +3 -3
- package/dist/index.js +3 -0
- package/dist/init/agent-rules.d.ts +16 -0
- package/dist/init/agent-rules.js +96 -22
- package/dist/init/astro-patch.d.ts +2 -1
- package/dist/init/astro-patch.js +11 -5
- package/dist/init/capabilities.d.ts +22 -0
- package/dist/init/capabilities.js +76 -0
- package/dist/init/confirm.d.ts +96 -0
- package/dist/init/confirm.js +138 -0
- package/dist/init/cra.js +3 -2
- package/dist/init/non-js-project.js +29 -5
- package/dist/init/plan-framework.js +53 -15
- package/dist/init/plan.d.ts +40 -4
- package/dist/init/plan.js +163 -40
- package/dist/init/run.d.ts +24 -2
- package/dist/init/run.js +148 -42
- package/dist/init/slash-command.d.ts +1 -1
- package/dist/init/slash-command.js +46 -6
- package/dist/init/snippets.d.ts +44 -4
- package/dist/init/snippets.js +161 -37
- package/dist/init/vite-config.js +29 -3
- package/dist/input/network-mock-tools.d.ts +19 -0
- package/dist/input/network-mock-tools.js +29 -4
- package/dist/license/license.js +1 -1
- package/dist/mcp/mcp-proxy.d.ts +69 -3
- package/dist/mcp/mcp-proxy.js +205 -20
- package/dist/mcp/mcp.js +23 -7
- package/dist/mcp/proxy-lifecycle.d.ts +1 -1
- package/dist/mcp/proxy-lifecycle.js +1 -1
- package/dist/pool/browser-pool.d.ts +22 -2
- package/dist/pool/browser-pool.js +44 -4
- package/dist/project/learned-routes.d.ts +24 -0
- package/dist/project/learned-routes.js +63 -0
- package/dist/project/project-store.d.ts +6 -0
- package/dist/project/project-store.js +25 -1
- package/dist/session/no-session-diagnosis.d.ts +19 -0
- package/dist/session/no-session-diagnosis.js +71 -18
- package/dist/session/no-session-watch.js +12 -0
- package/dist/session/ref-provenance.d.ts +54 -0
- package/dist/session/ref-provenance.js +61 -0
- package/dist/session/session-lease.js +1 -1
- package/dist/telemetry/automation-hint.d.ts +17 -0
- package/dist/telemetry/automation-hint.js +71 -0
- package/dist/telemetry/bug-found.d.ts +0 -20
- package/dist/telemetry/bug-found.js +38 -21
- package/dist/telemetry/cli-telemetry.js +7 -1
- package/dist/telemetry/feedback.js +1 -1
- package/dist/telemetry/init-telemetry.js +9 -1
- package/dist/telemetry/install-source.d.ts +36 -0
- package/dist/telemetry/install-source.js +40 -0
- package/dist/telemetry/run-telemetry.js +4 -1
- package/dist/telemetry/session-metrics.js +5 -0
- package/dist/telemetry/telemetry.d.ts +5 -1
- package/dist/telemetry/telemetry.js +15 -2
- package/dist/telemetry/tool-refused.d.ts +19 -0
- package/dist/telemetry/tool-refused.js +55 -0
- package/dist/telemetry/verification-of.js +46 -7
- package/dist/temp-dir.d.ts +27 -0
- package/dist/temp-dir.js +46 -0
- package/dist/tools/act-tools.js +45 -1
- package/dist/tools/assert-grade.d.ts +1 -1
- package/dist/tools/assert-grade.js +27 -5
- package/dist/tools/assert-verdict.js +18 -1
- package/dist/tools/contract-tools.js +4 -0
- package/dist/tools/dynamic-tools.js +1 -1
- package/dist/tools/error-recovery.d.ts +10 -0
- package/dist/tools/error-recovery.js +64 -4
- package/dist/tools/feedback-invite.js +1 -1
- package/dist/tools/invoke-tool.js +139 -23
- package/dist/tools/lease-tools.d.ts +29 -1
- package/dist/tools/lease-tools.js +58 -5
- package/dist/tools/navigate-result.js +1 -1
- package/dist/tools/observe-tools.js +1 -1
- package/dist/tools/observed-nothing.js +1 -1
- package/dist/tools/react-grace.d.ts +48 -0
- package/dist/tools/react-grace.js +87 -0
- package/dist/tools/read-tools.js +20 -4
- package/dist/tools/settle-in-flight.d.ts +79 -0
- package/dist/tools/settle-in-flight.js +106 -0
- package/dist/tools/snapshot-delta.d.ts +7 -0
- package/dist/tools/snapshot-delta.js +130 -6
- package/dist/tools/tool-surface.js +19 -0
- package/dist/tools/tools.js +16 -4
- package/docs/README.md +56 -11
- package/docs/actions.mdx +87 -0
- package/docs/agent-cheatsheet.md +93 -29
- package/docs/architecture.md +46 -35
- package/docs/benchmarks.md +76 -72
- package/docs/best-practices.mdx +211 -0
- package/docs/capabilities.mdx +387 -0
- package/docs/cli/affected.mdx +51 -0
- package/docs/cli/capsules.mdx +69 -0
- package/docs/cli/cloud.mdx +127 -0
- package/docs/cli/doctor.mdx +76 -0
- package/docs/cli/drive.mdx +55 -0
- package/docs/cli/feedback.mdx +67 -0
- package/docs/cli/gate.mdx +64 -0
- package/docs/cli/help.mdx +92 -0
- package/docs/cli/hunt.mdx +57 -0
- package/docs/cli/identify.mdx +62 -0
- package/docs/cli/init.mdx +94 -0
- package/docs/cli/kill.mdx +91 -0
- package/docs/cli/license.mdx +48 -0
- package/docs/cli/mcp.mdx +61 -0
- package/docs/cli/open.mdx +68 -0
- package/docs/cli/restart.mdx +60 -0
- package/docs/cli/rollback.mdx +35 -0
- package/docs/cli/serve.mdx +62 -0
- package/docs/cli/status.mdx +64 -0
- package/docs/cli/stop.mdx +56 -0
- package/docs/cli/telemetry.mdx +57 -0
- package/docs/cli/update.mdx +52 -0
- package/docs/cli/verify.mdx +57 -0
- package/docs/cli/version.mdx +39 -0
- package/docs/cli/watch.mdx +55 -0
- package/docs/cli.mdx +219 -0
- package/docs/debugging.md +13 -9
- package/docs/deploy-checks.md +15 -11
- package/docs/desktop-apps.md +49 -40
- package/docs/desktop.mdx +188 -0
- package/docs/docs.json +363 -0
- package/docs/enterprise.md +20 -16
- package/docs/faq.mdx +88 -0
- package/docs/fixtures.md +11 -7
- package/docs/flows.md +26 -22
- package/docs/for-agents.md +71 -0
- package/docs/frameworks.mdx +199 -0
- package/docs/gate-plan.md +45 -41
- package/docs/gates.md +22 -18
- package/docs/getting-started.md +76 -65
- package/docs/human-control.md +22 -18
- package/docs/index.mdx +101 -0
- package/docs/install-agentic.mdx +215 -0
- package/docs/install-manual.mdx +194 -0
- package/docs/instrumentation.mdx +232 -0
- package/docs/integration-patterns.md +38 -34
- package/docs/local-registry.md +16 -12
- package/docs/multi-agent-testing.md +16 -12
- package/docs/packages/babel-plugin.mdx +70 -0
- package/docs/packages/browser.mdx +111 -0
- package/docs/packages/core.mdx +106 -0
- package/docs/packages/electron.mdx +87 -0
- package/docs/packages/eslint-plugin.mdx +84 -0
- package/docs/packages/next.mdx +91 -0
- package/docs/packages/react.mdx +91 -0
- package/docs/packages/server.mdx +98 -0
- package/docs/packages/tauri.mdx +63 -0
- package/docs/packages/test.mdx +108 -0
- package/docs/packages/vite-plugin.mdx +96 -0
- package/docs/packages.mdx +217 -0
- package/docs/platform-integration.md +33 -26
- package/docs/predicates.mdx +179 -0
- package/docs/quickstart.mdx +211 -0
- package/docs/recipes.mdx +242 -0
- package/docs/skill-file.mdx +105 -0
- package/docs/state-management.mdx +197 -0
- package/docs/style.css +14 -0
- package/docs/system-map.md +47 -32
- package/docs/telemetry-contract.md +93 -50
- package/docs/telemetry-events.mdx +430 -0
- package/docs/telemetry.md +57 -52
- package/docs/testing.md +10 -6
- package/docs/token-efficiency.md +30 -26
- package/docs/tools-act-and-wait.mdx +123 -0
- package/docs/tools-act-sequence.mdx +120 -0
- package/docs/tools-act.mdx +82 -0
- package/docs/tools-assert.mdx +70 -0
- package/docs/tools-console.mdx +59 -0
- package/docs/tools-inspect.mdx +85 -0
- package/docs/tools-navigate.mdx +63 -0
- package/docs/tools-network.mdx +86 -0
- package/docs/tools-observe.mdx +89 -0
- package/docs/tools-overview.mdx +157 -0
- package/docs/tools-query.mdx +82 -0
- package/docs/tools-session-and-feedback.mdx +91 -0
- package/docs/tools-sessions.mdx +65 -0
- package/docs/tools-snapshot.mdx +75 -0
- package/docs/tools-state.mdx +81 -0
- package/docs/tools-tools-and-run.mdx +125 -0
- package/docs/tools-wait-for.mdx +66 -0
- package/docs/troubleshooting.mdx +193 -0
- package/docs/usage.md +175 -153
- package/docs/vs-chrome-devtools-mcp.mdx +82 -0
- package/docs/vs-playwright-mcp.mdx +85 -0
- package/docs/vs-screenshots.mdx +96 -0
- package/docs/what-is-reticle.mdx +63 -0
- package/docs/why-reticle.mdx +107 -0
- package/package.json +2 -2
- package/docs/matrix/2.5.0/claude-code-machine.json +0 -19
- package/docs/matrix/2.5.0/codex-machine.json +0 -20
- package/docs/matrix/2.5.0/cursor-machine.json +0 -22
- package/docs/matrix/2.5.0/gemini-machine.json +0 -22
- package/docs/matrix/2.5.0/opencode-machine.json +0 -22
- package/docs/matrix/2.5.0/vscode-machine.json +0 -22
- package/docs/matrix/2.5.0/windsurf-machine.json +0 -22
- package/docs/matrix/MATRIX.md +0 -26
- package/docs/matrix/README.md +0 -59
- package/docs/mint.json +0 -61
package/docs/gates.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Gates
|
|
3
|
+
description: 'I changed some files. Which command do I run before I push?'
|
|
4
|
+
icon: shield-check
|
|
5
|
+
---
|
|
2
6
|
|
|
3
7
|
> **One question this file answers:** _I changed some files. Which command do I run?_
|
|
4
8
|
>
|
|
@@ -20,14 +24,14 @@ Every gate below was executed end to end against `main` on **2026-08-12** (macOS
|
|
|
20
24
|
| `pnpm test:integration` | ✅ 12/12 | 17s |
|
|
21
25
|
| `pnpm test:e2e` | ✅ 32/32 specs + soak 60/60 answered | **490s** |
|
|
22
26
|
| `pnpm test:e2e:desktop` | ✅ 2/2 (Electron 20, Tauri 14) | 58s |
|
|
23
|
-
| `node apps/e2e/soak.mjs --self-check` | ✅ |
|
|
24
|
-
| `node apps/e2e/matrix.mjs --self-check` | ✅ |
|
|
27
|
+
| `node apps/e2e/soak.mjs --self-check` | ✅ | `<1s` |
|
|
28
|
+
| `node apps/e2e/matrix.mjs --self-check` | ✅ | `<1s` |
|
|
25
29
|
| `pnpm matrix:compat --only cursor` | ✅ 4/4 | ~10s |
|
|
26
|
-
| `pnpm bench` | ✅ 10/10
|
|
30
|
+
| `pnpm bench` | ✅ 10/10 (**was failing before this sweep**, see below) | 279s |
|
|
27
31
|
|
|
28
|
-
**`pnpm bench` was broken and nobody knew.** `suite-rre.mjs` recorded four flows that asserted no observable consequence and then demanded a `pass` verdict from `reticle_flow_verify
|
|
32
|
+
**`pnpm bench` was broken and nobody knew.** `suite-rre.mjs` recorded four flows that asserted no observable consequence and then demanded a `pass` verdict from `reticle_flow_verify`, which correctly grades an assertion-free suite `unverifiable`. The product got more honest about false greens; the benchmark measuring it did not follow, so the whole run aborted at script 9 of 10 and `replay-determinism` never ran at all. Each flow now carries a success oracle. This is the failure mode `bench/` is most exposed to: **nothing in CI runs it**, so it can only rot silently.
|
|
29
33
|
|
|
30
|
-
`pnpm gate:install` (~15 min) and the Windows / Rust jobs were **not** run in this sweep
|
|
34
|
+
`pnpm gate:install` (~15 min) and the Windows / Rust jobs were **not** run in this sweep; they are CI-only or network-bound. They are green on `main` per the last CI run, which is a weaker claim than every row above, and is stated that way on purpose.
|
|
31
35
|
|
|
32
36
|
---
|
|
33
37
|
|
|
@@ -39,13 +43,13 @@ Find the row that matches what you changed. Run its commands. That is the whole
|
|
|
39
43
|
| --- | --- | --- |
|
|
40
44
|
| **Anything at all** | `pnpm lint && pnpm typecheck && pnpm test:unit` | ~2 min |
|
|
41
45
|
| The tool surface, the wire contract (`packages/core`), or an observer | ↑ **and** `pnpm test:e2e` | +~8 min |
|
|
42
|
-
| `reticle init`, `@reticlehq/vite-plugin`, `@reticlehq/next`, `@reticlehq/babel-plugin
|
|
46
|
+
| `reticle init`, `@reticlehq/vite-plugin`, `@reticlehq/next`, `@reticlehq/babel-plugin`, anything a user runs before their first session | ↑ **and** `pnpm gate:install` | +~15 min |
|
|
43
47
|
| `@reticlehq/electron`, `packages/tauri`, the IPC observer, desktop capture | ↑ **and** `pnpm test:e2e:desktop` | +~3 min |
|
|
44
|
-
| Telemetry, feedback, or anything that emits an event | ↑ **and** read [`telemetry-contract.md`](./telemetry-contract.md) first. `pnpm test:e2e` covers it (`telemetry-events-test`) |
|
|
48
|
+
| Telemetry, feedback, or anything that emits an event | ↑ **and** read [`telemetry-contract.md`](./telemetry-contract.md) first. `pnpm test:e2e` covers it (`telemetry-events-test`) | n/a |
|
|
45
49
|
| `packages/tauri` (Rust) | ↑ **and** `cd packages/tauri && cargo fmt --check && cargo clippy --all-targets -- -D warnings` | +~2 min |
|
|
46
50
|
| Docs, README, comments only | `pnpm format:check` | seconds |
|
|
47
51
|
|
|
48
|
-
**Why routing exists.** The full set is roughly 35 minutes. A gate people resent is a gate people route around, so only the tier that can see your change is worth your time. CI runs everything regardless
|
|
52
|
+
**Why routing exists.** The full set is roughly 35 minutes. A gate people resent is a gate people route around, so only the tier that can see your change is worth your time. CI runs everything regardless, so routing costs you a slower red, never a missed one.
|
|
49
53
|
|
|
50
54
|
---
|
|
51
55
|
|
|
@@ -59,16 +63,16 @@ Each gate exists because the ones above it are blind to something. That blindnes
|
|
|
59
63
|
| **Lint** | `pnpm lint` | style rules, plus the dependency-boundary and lossy-transform guards | anything not expressible as a rule | `verify` |
|
|
60
64
|
| **Typecheck** | `pnpm typecheck` | types agree across package boundaries | runtime behaviour | `verify` |
|
|
61
65
|
| **Unit** | `pnpm test:unit` | 4,315 tests, per-package, no browser | anything crossing a package boundary at runtime | `verify` |
|
|
62
|
-
| **Format** | `pnpm format:check` | Prettier |
|
|
66
|
+
| **Format** | `pnpm format:check` | Prettier | n/a | `verify` |
|
|
63
67
|
| **Integration** | `pnpm test:integration` | real headless Chromium: browser pool, crash isolation, framework adapters, `withReticle` | the MCP surface, the daemon | `e2e` |
|
|
64
|
-
| **Web e2e battery** | `pnpm test:e2e` | 30 specs against 3 booted servers and a real browser
|
|
68
|
+
| **Web e2e battery** | `pnpm test:e2e` | 30 specs against 3 booted servers and a real browser (the tool surface, the daemon lifecycle, transport faults, telemetry, trace shape), plus the soak | desktop runtimes; the install | `e2e` |
|
|
65
69
|
| **Desktop battery** | `pnpm test:e2e:desktop` | a real Electron main process and a **packaged** Tauri binary, driven headless | web-only paths | `desktop-e2e` |
|
|
66
|
-
| **Install gate** | `pnpm gate:install` | scaffolds 3 pristine apps, publishes this checkout to a local Verdaccio, lets `init` install itself, boots each app in a real browser, polls for a session | install _complexity_
|
|
70
|
+
| **Install gate** | `pnpm gate:install` | scaffolds 3 pristine apps, publishes this checkout to a local Verdaccio, lets `init` install itself, boots each app in a real browser, polls for a session | install _complexity_; see [`fixtures.md`](./fixtures.md) | `install-gate` |
|
|
67
71
|
| **Matrix records** | `pnpm matrix:validate` | every submitted client-compat record is well-formed | whether the client actually works | `matrix-records` |
|
|
68
72
|
| **Windows** | (CI only) | that the code runs at all on the majority platform | e2e; Windows is unit-only | `windows` |
|
|
69
73
|
| **Rust** | `cargo fmt/clippy/check` | `packages/tauri` compiles and lints on Linux, macOS, and cross-checks Windows | everything JS | `rust`, `rust-macos` |
|
|
70
74
|
|
|
71
|
-
**The single required status check is `gate`.** It passes when every job above either succeeded or was deliberately skipped by path routing, and fails on anything else. Adding a job to `ci.yml` is half the work; adding it to `gate`'s `needs:` list is the other half
|
|
75
|
+
**The single required status check is `gate`.** It passes when every job above either succeeded or was deliberately skipped by path routing, and fails on anything else. Adding a job to `ci.yml` is half the work; adding it to `gate`'s `needs:` list is the other half. A job missing from that list runs, reports, and is structurally incapable of blocking a merge.
|
|
72
76
|
|
|
73
77
|
### Guards that self-test
|
|
74
78
|
|
|
@@ -99,7 +103,7 @@ These are real and they work; they are not on the PR path, so they only run when
|
|
|
99
103
|
|
|
100
104
|
## 4. `bench/` is not a gate
|
|
101
105
|
|
|
102
|
-
`bench/` is **measurement and research**, not a merge check. Nothing there blocks a PR, nothing runs in CI, and it is allowed to bit-rot in a way a gate is not. Read [`bench/README.md`](../bench/README.md) before touching it
|
|
106
|
+
`bench/` is **measurement and research**, not a merge check. Nothing there blocks a PR, nothing runs in CI, and it is allowed to bit-rot in a way a gate is not. Read [`bench/README.md`](../bench/README.md) before touching it: it says which scripts are live and which are one-off studies kept as evidence for a published claim.
|
|
103
107
|
|
|
104
108
|
The one exception worth knowing: `pnpm bench` + `pnpm bench:gate` is a working regression gate for the replay numbers, and it is run by hand before a release.
|
|
105
109
|
|
|
@@ -109,9 +113,9 @@ The one exception worth knowing: `pnpm bench` + `pnpm bench:gate` is a working r
|
|
|
109
113
|
|
|
110
114
|
Sometimes it is. The specific failures worth recognising:
|
|
111
115
|
|
|
112
|
-
- **`EADDRINUSE` / "died during boot"
|
|
113
|
-
- **Killing port 4400 with `lsof -ti tcp:4400 | xargs kill -9
|
|
114
|
-
- **A timing assertion
|
|
115
|
-
- **An `INCONCLUSIVE` verdict
|
|
116
|
+
- **`EADDRINUSE` / "died during boot".** A previous run left something on `:8787`, `:4310`, or `:3100`. `run-ci.sh` frees these on exit; if it was killed, free them by hand.
|
|
117
|
+
- **Killing port 4400 with `lsof -ti tcp:4400 | xargs kill -9`.** This SIGKILLs the `reticle mcp` proxy too, because the proxy holds a _client_ socket on the bridge port. Always add `-sTCP:LISTEN`. This is the root cause of most "the MCP went down" reports.
|
|
118
|
+
- **A timing assertion.** If a test asserts `Date.now() - t < N`, that is a bug in the test, not a flake to re-run. Assert the bound (output size, a truncation flag), or use a generous per-test timeout. See [`harness-rules.md`](../apps/e2e/harness-rules.md).
|
|
119
|
+
- **An `INCONCLUSIVE` verdict.** The harness is telling you the transport did not stay up, so it is claiming nothing about the product. That is the harness working, not the product failing.
|
|
116
120
|
|
|
117
121
|
The four rules every tier obeys, and the incident behind each, are in [`apps/e2e/harness-rules.md`](../apps/e2e/harness-rules.md).
|
package/docs/getting-started.md
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Getting started
|
|
3
|
+
description: 'Zero to your agent verifying your real app, step by step, with working code for Vite, Next.js, React and plain HTML.'
|
|
4
|
+
icon: rocket
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
> **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.
|
|
8
|
+
>
|
|
9
|
+
> This page is the long-form walkthrough: the same ground, more slowly, plus per-framework wiring, multi-app setups and prerelease notes. Start here if you want the whole picture in one file rather than the fast route.
|
|
10
|
+
|
|
11
|
+
This walks you from zero to your agent verifying your app, step by step, with real code for real frameworks. ~10 minutes.
|
|
4
12
|
|
|
5
13
|
- [What you're setting up](#what-youre-setting-up)
|
|
6
14
|
- [Prerequisites](#prerequisites)
|
|
7
|
-
- [Step 1
|
|
8
|
-
- [Step 2
|
|
15
|
+
- [Step 1: Connect your coding agent (MCP)](#step-1-connect-your-coding-agent-mcp)
|
|
16
|
+
- [Step 2: Embed the SDK in your app](#step-2-embed-the-sdk-in-your-app)
|
|
9
17
|
- [Vite + React](#vite--react)
|
|
10
18
|
- [Next.js](#nextjs)
|
|
11
19
|
- [Plain / other frameworks](#plain--other-frameworks)
|
|
12
|
-
- [Step 3
|
|
13
|
-
- [Step 4
|
|
14
|
-
- [Step 5
|
|
15
|
-
- [Step 6
|
|
20
|
+
- [Step 3: (React) component and source-file mapping](#step-3-react-component-and-source-file-mapping)
|
|
21
|
+
- [Step 4: Run it and verify the connection](#step-4-run-it-and-verify-the-connection)
|
|
22
|
+
- [Step 5: Your first verification](#step-5-your-first-verification)
|
|
23
|
+
- [Step 6: Make your app agent-legible](#step-6-make-your-app-agent-legible-optional-high-leverage)
|
|
16
24
|
- [Common setups at a glance](#common-setups-at-a-glance)
|
|
17
25
|
- [Troubleshooting](#troubleshooting)
|
|
18
26
|
|
|
@@ -22,18 +30,21 @@ This walks you from zero to your agent verifying your app — step by step, with
|
|
|
22
30
|
|
|
23
31
|
Three pieces, each tiny:
|
|
24
32
|
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
```mermaid
|
|
34
|
+
flowchart LR
|
|
35
|
+
A["coding agent<br/>(Claude Code)"]
|
|
36
|
+
S["reticle bridge + server<br/>npx @reticlehq/server"]
|
|
37
|
+
B["your app<br/>+ the Reticle SDK<br/>(dev only)"]
|
|
38
|
+
|
|
39
|
+
A <-- "MCP over stdio" --> S
|
|
40
|
+
S <-- "WebSocket<br/>localhost:4400" --> B
|
|
30
41
|
```
|
|
31
42
|
|
|
32
43
|
Three pieces, each from the package for its audience:
|
|
33
44
|
|
|
34
|
-
1. **The MCP server
|
|
35
|
-
2. **The SDK
|
|
36
|
-
3. **(Optional) React adapter + source-mapping
|
|
45
|
+
1. **The MCP server.** Your agent launches it with `npx @reticlehq/server mcp`; it hosts the tools _and_ the WebSocket bridge your app connects to. You don't run it by hand; the agent does.
|
|
46
|
+
2. **The SDK**: `import { reticle } from '@reticlehq/react'`, a few lines in your app's dev entry point.
|
|
47
|
+
3. **(Optional) React adapter + source-mapping**, so `reticle_inspect` can tell the agent which component/file to edit (also from `@reticlehq/react`).
|
|
37
48
|
|
|
38
49
|
Everything is **dev-only** and **localhost-only**. It's tree-shaken out of production builds.
|
|
39
50
|
|
|
@@ -45,35 +56,35 @@ Everything is **dev-only** and **localhost-only**. It's tree-shaken out of produ
|
|
|
45
56
|
|
|
46
57
|
---
|
|
47
58
|
|
|
48
|
-
## Fastest path
|
|
59
|
+
## Fastest path: `reticle init`
|
|
49
60
|
|
|
50
61
|
From your project root:
|
|
51
62
|
|
|
52
63
|
```bash
|
|
53
|
-
npx @reticlehq/server init
|
|
64
|
+
RETICLE_INSTALL_SOURCE=docs_site npx @reticlehq/server init
|
|
54
65
|
```
|
|
55
66
|
|
|
56
67
|
It detects your framework, package manager, and React version, then:
|
|
57
68
|
|
|
58
|
-
- **registers the Reticle MCP server once, globally, for each agent you have installed**
|
|
59
|
-
- **writes a verification rule into your agent's instruction file**
|
|
69
|
+
- **registers the Reticle MCP server once, globally, for each agent you have installed** (Claude Code via `claude mcp add reticle -s user`, and/or Cursor via `~/.cursor/mcp.json`), so every project on this machine gets it; you never re-add it per project,
|
|
70
|
+
- **writes a verification rule into your agent's instruction file** (`CLAUDE.md`, `.cursor/rules/reticle.mdc`, or `AGENTS.md`), so the agent knows to verify a feature with Reticle _after building it_, not only when you remember to ask (idempotent; appended below anything you already have),
|
|
60
71
|
- installs the SDK kit (`@reticlehq/react`) and the right build plugin (`@reticlehq/vite-plugin` or `@reticlehq/next`) as dev dependencies,
|
|
61
|
-
- **Vite:** adds the `reticle()` plugin to your config
|
|
72
|
+
- **Vite:** adds the `reticle()` plugin to your config, which wires source mapping _and_ `reticle.connect()` for you, so there is nothing else to edit,
|
|
62
73
|
- **Next / other:** creates the dev component and prints the exact `withReticle` / mount / connect snippets to paste (it never half-edits a build config).
|
|
63
74
|
|
|
64
75
|
The bridge + MCP server is a single process that serves all your projects, so it's registered at **user scope**, not in a per-project `.mcp.json`. Only the SDK (the `reticle()` plugin / connect call) is added per project.
|
|
65
76
|
|
|
66
77
|
Re-running is safe (already-registered/already-patched steps are skipped). Preview without writing via `npx @reticlehq/server init --dry-run`. Flags: `--port N`, `--no-mcp`, `--no-install`, `--yes`.
|
|
67
78
|
|
|
68
|
-
Then restart your dev server and skip to [Step 4](#step-4
|
|
79
|
+
Then restart your dev server and 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.
|
|
69
80
|
|
|
70
81
|
---
|
|
71
82
|
|
|
72
|
-
## Step 1
|
|
83
|
+
## Step 1: Connect your coding agent (MCP), once
|
|
73
84
|
|
|
74
|
-
You don't start the server manually
|
|
85
|
+
You don't start the server manually; your agent starts it via MCP. Register Reticle **once, at the user (global) scope** so every project picks it up. There's nothing to add per project.
|
|
75
86
|
|
|
76
|
-
**Claude Code
|
|
87
|
+
**Claude Code**, one command:
|
|
77
88
|
|
|
78
89
|
```bash
|
|
79
90
|
claude mcp add reticle -s user -- npx @reticlehq/server mcp
|
|
@@ -81,7 +92,7 @@ claude mcp add reticle -s user -- npx @reticlehq/server mcp
|
|
|
81
92
|
|
|
82
93
|
(`reticle init` runs exactly this for you. `-s user` is what makes it global; drop it for a project-local registration instead.)
|
|
83
94
|
|
|
84
|
-
**Cursor
|
|
95
|
+
**Cursor**: add to your global `~/.cursor/mcp.json` (not per-project; `reticle init` writes this for you):
|
|
85
96
|
|
|
86
97
|
```jsonc
|
|
87
98
|
{
|
|
@@ -97,7 +108,7 @@ Other MCP clients (Windsurf, Claude Desktop, …) use the same `command`/`args`
|
|
|
97
108
|
|
|
98
109
|
---
|
|
99
110
|
|
|
100
|
-
## Step 2
|
|
111
|
+
## Step 2: Embed the SDK in your app
|
|
101
112
|
|
|
102
113
|
Install the SDK kit plus your framework's build plugin as dev dependencies (the kit re-exports the browser sensor, so one install gives both `reticle` and `install`):
|
|
103
114
|
|
|
@@ -110,7 +121,7 @@ Then call `reticle.connect()` once, in dev only. Where you put it depends on you
|
|
|
110
121
|
|
|
111
122
|
### Vite + React
|
|
112
123
|
|
|
113
|
-
**Recommended
|
|
124
|
+
**Recommended: the Vite plugin (one line, does everything).** Add `reticle()` to your `vite.config.ts`:
|
|
114
125
|
|
|
115
126
|
```ts
|
|
116
127
|
import { defineConfig } from 'vite';
|
|
@@ -122,7 +133,7 @@ export default defineConfig({
|
|
|
122
133
|
});
|
|
123
134
|
```
|
|
124
135
|
|
|
125
|
-
This injects `reticle.connect()` for you _and_ handles React 19 source mapping (Step 3)
|
|
136
|
+
This injects `reticle.connect()` for you _and_ handles React 19 source mapping (Step 3), so there's no entry-file edit and no separate Babel setup. `apply: 'serve'` means it's dropped from `vite build` entirely, so it can never reach production. (This is exactly what `reticle init` adds.)
|
|
126
137
|
|
|
127
138
|
<details>
|
|
128
139
|
<summary>Prefer to wire it by hand instead of the plugin?</summary>
|
|
@@ -153,7 +164,7 @@ On React 19 you then also need the source-mapping Babel plugin from Step 3. The
|
|
|
153
164
|
|
|
154
165
|
### The pairing token (why some setups need one line more)
|
|
155
166
|
|
|
156
|
-
The daemon auto-generates a **pairing token** on first run and stores it at `~/.reticle/pairing-token` (owner-only, `0600`). The bridge requires it, so another app running on `http://localhost:<some-other-port>` can't quietly register or drive your session
|
|
167
|
+
The daemon auto-generates a **pairing token** on first run and stores it at `~/.reticle/pairing-token` (owner-only, `0600`). The bridge requires it, so another app running on `http://localhost:<some-other-port>` can't quietly register or drive your session. Only code that can read that file (your dev server, not a web page) can present it.
|
|
157
168
|
|
|
158
169
|
- **Vite plugin users:** nothing to do. The plugin reads the token server-side and injects it into `connect()` for you.
|
|
159
170
|
- **Next.js / hand-wired `connect()`:** your `connect()` runs in the browser and can't read the file, so pass the token in yourself. The simplest path is a shared secret: set `RETICLE_TOKEN` for the daemon (it uses that instead of auto-generating) and expose the same value to the client as `NEXT_PUBLIC_RETICLE_TOKEN`, then pass it to `connect({ token })` (see below). On a single-user machine you can also just read `~/.reticle/pairing-token` in your dev tooling and forward it the same way.
|
|
@@ -222,12 +233,12 @@ Or, with no build step, a script tag pointed at the bridge:
|
|
|
222
233
|
|
|
223
234
|
### Running multiple apps at once
|
|
224
235
|
|
|
225
|
-
It's common to have several apps open in dev
|
|
236
|
+
It's common to have several apps open in dev: a few Next.js and React projects, or multiple tabs of the same app. Reticle handles this cleanly **as long as each connection has a unique session id**, which is exactly what `SESSION_AUTO` gives you (a fresh id per tab). The examples above all use it, so you get this for free. When more than one app is connected, an Reticle tool call targets the focused / most recently active one automatically, or you can pass an explicit `sessionId` to target a specific app.
|
|
226
237
|
|
|
227
238
|
**Two separate projects, fully isolated.** If you want each repo to have its own independent Reticle bridge (separate sessions, separate `.reticle/` workspace), give each project its own port. Set the same port in both the MCP server config and the app's connection:
|
|
228
239
|
|
|
229
240
|
```jsonc
|
|
230
|
-
// project-b/.mcp.json
|
|
241
|
+
// project-b/.mcp.json: give this project its own bridge port
|
|
231
242
|
{
|
|
232
243
|
"mcpServers": {
|
|
233
244
|
"reticle": {
|
|
@@ -240,33 +251,33 @@ It's common to have several apps open in dev — a few Next.js and React project
|
|
|
240
251
|
```
|
|
241
252
|
|
|
242
253
|
```ts
|
|
243
|
-
// project-b's app
|
|
254
|
+
// project-b's app: dial the same port
|
|
244
255
|
reticle.connect({ session: SESSION_AUTO, url: 'ws://localhost:4401/reticle' });
|
|
245
256
|
```
|
|
246
257
|
|
|
247
|
-
**On the Vite plugin?** You don't have a hand-written `connect()` to edit
|
|
258
|
+
**On the Vite plugin?** You don't have a hand-written `connect()` to edit; the plugin injects it. Set the port on the plugin instead, and it bakes the matching URL in for you:
|
|
248
259
|
|
|
249
260
|
```ts
|
|
250
261
|
// project-b/vite.config.ts
|
|
251
262
|
plugins: [react(), reticle({ port: 4401 })],
|
|
252
263
|
```
|
|
253
264
|
|
|
254
|
-
Either way, the rule is the same: **the app's bridge port must equal the daemon's `RETICLE_PORT
|
|
265
|
+
Either way, the rule is the same: **the app's bridge port must equal the daemon's `RETICLE_PORT`**, and it's the Reticle bridge port, never your dev-server port. Project A stays on the default `4400`, project B on `4401`; they never touch each other. (A port that is already in use now fails fast with a clear error instead of hanging, so a misconfiguration is obvious.)
|
|
255
266
|
|
|
256
267
|
---
|
|
257
268
|
|
|
258
|
-
## Step 3
|
|
269
|
+
## Step 3: (React) component and source-file mapping
|
|
259
270
|
|
|
260
|
-
This is optional but high-value: it lets `reticle_inspect` map a DOM element back to the **React component and the source file:line
|
|
271
|
+
This is optional but high-value: it lets `reticle_inspect` map a DOM element back to the **React component and the source file:line**, so when the agent finds a problem, it knows which file to edit. (The React adapter ships in `@reticlehq/react`; nothing extra to install.)
|
|
261
272
|
|
|
262
273
|
```ts
|
|
263
274
|
import { install as installReticleReact } from '@reticlehq/react';
|
|
264
275
|
if (import.meta.env.DEV) installReticleReact(); // call before reticle.connect()
|
|
265
276
|
```
|
|
266
277
|
|
|
267
|
-
**React ≤ 18:** that's all
|
|
278
|
+
**React ≤ 18:** that's all; it uses React's dev `_debugSource`.
|
|
268
279
|
|
|
269
|
-
**React 19:** React removed `_debugSource`, so the source has to be stamped at build time. **If you added the `reticle()` Vite plugin in Step 2, this is already handled
|
|
280
|
+
**React 19:** React removed `_debugSource`, so the source has to be stamped at build time. **If you added the `reticle()` Vite plugin in Step 2, this is already handled, so skip ahead.** Otherwise add the Babel plugin (`@reticlehq/babel-plugin`) to stamp the source onto elements in dev:
|
|
270
281
|
|
|
271
282
|
```ts
|
|
272
283
|
// vite.config.ts
|
|
@@ -278,7 +289,7 @@ export default defineConfig({
|
|
|
278
289
|
});
|
|
279
290
|
```
|
|
280
291
|
|
|
281
|
-
> **Next.js:** verified on **Next.js 15 / React 19 (app router, SWC)**. For source-file mapping, use `@reticlehq/next` instead of the Babel plugin
|
|
292
|
+
> **Next.js:** verified on **Next.js 15 / React 19 (app router, SWC)**. For source-file mapping, use `@reticlehq/next` instead of the Babel plugin. It adds a **dev-only webpack pre-loader that keeps SWC** and stamps `data-reticle-source` so `reticle_inspect` returns `file:line` (e.g. `app/page.tsx:30`):
|
|
282
293
|
>
|
|
283
294
|
> ```js
|
|
284
295
|
> // next.config.mjs
|
|
@@ -292,7 +303,7 @@ export default defineConfig({
|
|
|
292
303
|
|
|
293
304
|
---
|
|
294
305
|
|
|
295
|
-
## Step 4
|
|
306
|
+
## Step 4: Run it and verify the connection
|
|
296
307
|
|
|
297
308
|
1. Start your app's dev server as usual (`npm run dev`).
|
|
298
309
|
2. Open it in the browser (the SDK connects when the page loads).
|
|
@@ -310,7 +321,7 @@ If the list is empty, see [Troubleshooting](#troubleshooting).
|
|
|
310
321
|
|
|
311
322
|
---
|
|
312
323
|
|
|
313
|
-
## Step 5
|
|
324
|
+
## Step 5: Your first verification
|
|
314
325
|
|
|
315
326
|
Now just talk to your agent in plain language. For example:
|
|
316
327
|
|
|
@@ -326,22 +337,22 @@ reticle_query({ by: "role", value: "button", name: "Refresh" }) // → ref e12
|
|
|
326
337
|
reticle_act({ ref: "e12", action: "click" }) // → { since: 920 }
|
|
327
338
|
|
|
328
339
|
// verifies the reaction
|
|
329
|
-
reticle_assert({ timeout_ms: 2000, predicate: { allOf: [
|
|
340
|
+
reticle_assert({ timeout_ms: 2000, predicate: { kind: "allOf", predicates: [
|
|
330
341
|
{ kind: "net", method: "GET", urlContains: "/api/dashboard", status: 200, since: 920 },
|
|
331
342
|
{ kind: "console", level: "error", absent: true }
|
|
332
343
|
]}})
|
|
333
344
|
// → { pass: true }
|
|
334
345
|
```
|
|
335
346
|
|
|
336
|
-
You get a real, evidence-backed answer
|
|
347
|
+
You get a real, evidence-backed answer. If it fails, the agent sees the reason (e.g. the call 404'd, or a `TypeError` in `Dashboard.tsx:88`) and can fix it and re-check.
|
|
337
348
|
|
|
338
349
|
That's the whole loop. From here, the [Usage Guide](usage.md) covers every tool, the full predicate DSL, and a dozen real situations (login, long lists, eventual consistency, file uploads, LLM calls, regressions, and more).
|
|
339
350
|
|
|
340
351
|
---
|
|
341
352
|
|
|
342
|
-
## Step 6
|
|
353
|
+
## Step 6: Make your app agent-legible (optional, high-leverage)
|
|
343
354
|
|
|
344
|
-
The basics above work with zero app changes. These four additions make the agent dramatically faster and let it verify things the DOM can't express
|
|
355
|
+
The basics above work with zero app changes. These four additions make the agent dramatically faster and let it verify things the DOM can't express; they're what turn Reticle from "usable" into "magic." All are dev-only.
|
|
345
356
|
|
|
346
357
|
**1. Stable `data-testid` on key elements.** Agents target testids more reliably than visible text (which changes with copy/i18n). Reticle matches testids _exactly_.
|
|
347
358
|
|
|
@@ -349,7 +360,7 @@ The basics above work with zero app changes. These four additions make the agent
|
|
|
349
360
|
<button data-testid="refresh">Refresh</button>
|
|
350
361
|
```
|
|
351
362
|
|
|
352
|
-
**2. `reticle.signal` for off-DOM facts.** When something matters but isn't visible
|
|
363
|
+
**2. `reticle.signal` for off-DOM facts.** When something matters but isn't visible (a save committed, a webhook arrived, an edit applied, an LLM caption finished), emit a signal the agent can assert on. This is the single highest-value instrumentation.
|
|
353
364
|
|
|
354
365
|
```ts
|
|
355
366
|
import { reticle } from '@reticlehq/react';
|
|
@@ -357,9 +368,9 @@ onSaved(() => reticle.signal('order:saved', { id, total }));
|
|
|
357
368
|
// agent: reticle_assert({ predicate: { kind: 'signal', name: 'order:saved', dataMatches: { id: '*' } } })
|
|
358
369
|
```
|
|
359
370
|
|
|
360
|
-
> **Recommended:** instead of importing `reticle` into components, inject a `createReticleEmitter()` emitter and pair each commit with `commitAndSignal(...)` so the mutation↔signal can't drift
|
|
371
|
+
> **Recommended:** instead of importing `reticle` into components, inject a `createReticleEmitter()` emitter and pair each commit with `commitAndSignal(...)` so the mutation↔signal can't drift. `reticle.signal` stays the primitive underneath. See [integration-patterns.md](integration-patterns.md).
|
|
361
372
|
|
|
362
|
-
**3. `registerStore` so the agent reads state directly.** No need to broadcast a signal for every fact
|
|
373
|
+
**3. `registerStore` so the agent reads state directly.** No need to broadcast a signal for every fact: expose the store and the agent reads it via `reticle_state`.
|
|
363
374
|
|
|
364
375
|
```ts
|
|
365
376
|
import { registerStore } from '@reticlehq/react';
|
|
@@ -379,11 +390,11 @@ registerCapabilities({
|
|
|
379
390
|
// agent: reticle_capabilities() → the whole testable surface
|
|
380
391
|
```
|
|
381
392
|
|
|
382
|
-
> **Multi-domain apps:** prefer `registerReticleDomain({ testids, signals, stores })` co-located in one `reticle.ts` per domain
|
|
393
|
+
> **Multi-domain apps:** prefer `registerReticleDomain({ testids, signals, stores })` co-located in one `reticle.ts` per domain. Each self-registers and `reticle_capabilities()` assembles the union, so there's no central map to forget. See [integration-patterns.md](integration-patterns.md).
|
|
383
394
|
|
|
384
395
|
> Watch the agent work: pass `present: true` to `reticle.connect()` for a glowing border, a cursor that flies to targets, and a HUD; the agent can call `reticle_session {action:"narrate"}({ text })` to show its intent. See [usage §16](usage.md#16-presenter-mode-narration--fake-clock-watch--control).
|
|
385
396
|
|
|
386
|
-
> **Hover-gated UI (tooltips, hover menus, pointer drag)?** Synthetic events can't trigger native `onMouseEnter`. Enable **real input** by launching your browser with `--remote-debugging-port=9222` and setting `RETICLE_CDP_URL` in the MCP server `env
|
|
397
|
+
> **Hover-gated UI (tooltips, hover menus, pointer drag)?** Synthetic events can't trigger native `onMouseEnter`. Enable **real input** by launching your browser with `--remote-debugging-port=9222` and setting `RETICLE_CDP_URL` in the MCP server `env`. Reticle then drives real pointer input and `reticle_act` reports `inputMode:"real"`. See [usage §18](usage.md#18-real-input-mode-native-hover--drag).
|
|
387
398
|
|
|
388
399
|
---
|
|
389
400
|
|
|
@@ -391,10 +402,10 @@ registerCapabilities({
|
|
|
391
402
|
|
|
392
403
|
Once the loop works, these turn ad-hoc runs into a maintained suite:
|
|
393
404
|
|
|
394
|
-
- **[Flows, recorder & self-healing](flows.md)
|
|
395
|
-
- **[Testing with `@reticlehq/test`](testing.md)**
|
|
396
|
-
- **[Human-in-the-loop control](human-control.md)
|
|
397
|
-
- **[Integration patterns](integration-patterns.md)**
|
|
405
|
+
- **[Flows, recorder & self-healing](flows.md)**: record a golden path once; Reticle saves it to a git-checked `.reticle/` flow anchored on testid+signal, replays it (with legible drift), and `reticle_flow_heal` repairs renamed anchors.
|
|
406
|
+
- **[Testing with `@reticlehq/test`](testing.md)** gives you declarative `reticleTest` specs you run headless / in CI; flows can _become_ the specs.
|
|
407
|
+
- **[Human-in-the-loop control](human-control.md)**: with `present: true`, pause / message / end the agent from the floating panel.
|
|
408
|
+
- **[Integration patterns](integration-patterns.md)** covers the recommended zero-prod-bundle emit adapter, store-layer signals, and incremental adoption.
|
|
398
409
|
|
|
399
410
|
---
|
|
400
411
|
|
|
@@ -404,20 +415,20 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
|
|
|
404
415
|
|
|
405
416
|
| Stack | SDK connect | Source mapping |
|
|
406
417
|
| --- | --- | --- |
|
|
407
|
-
| Vite + React (any) | `reticle()` plugin (auto)
|
|
418
|
+
| Vite + React (any) | `reticle()` plugin (auto), or `connect()` | `reticle()` plugin handles it (incl. React 19) |
|
|
408
419
|
| Next.js (app router) | `ReticleDev` client component in layout (dev) | `@reticlehq/next` (`withReticle`) → component + file:line |
|
|
409
420
|
| SvelteKit | `src/hooks.client.ts` (written by `reticle init`) | `reticle()` plugin stamps `.svelte` → file:line |
|
|
410
|
-
| Vanilla / plain HTML | `reticle.connect()` at boot (dev) | none
|
|
421
|
+
| Vanilla / plain HTML | `reticle.connect()` at boot (dev) | none (refs and testids only) |
|
|
411
422
|
|
|
412
423
|
### What Svelte support is, and what it is not
|
|
413
424
|
|
|
414
425
|
`reticle init` detects SvelteKit and writes both halves: a client hook that calls `connect()` (SvelteKit renders through `app.html`, so the plugin's HTML injection never fires) and `reticle()` in `vite.config`, which is what stamps `data-reticle-source`.
|
|
415
426
|
|
|
416
|
-
**You get** `file:line` on every element in a `.svelte` component, plus everything the framework-agnostic core already gave you
|
|
427
|
+
**You get** `file:line` on every element in a `.svelte` component, plus everything the framework-agnostic core already gave you (DOM, network, console, routing, storage, actions), and `svelteStore` for reading a Svelte store (see [usage](usage.md)).
|
|
417
428
|
|
|
418
429
|
**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.
|
|
419
430
|
|
|
420
|
-
**It is still unverified.** There is no SvelteKit app in `apps/` and no CI gate for one, so nothing would tell us when this breaks
|
|
431
|
+
**It is still unverified.** There is no SvelteKit app in `apps/` and no CI gate for one, so nothing would tell us when this breaks; `reticle init` says so out loud in its plan. React, Next.js, Remix and Astro each have an app and a gate. Treat SvelteKit as wired and plausible, not as supported.
|
|
421
432
|
|
|
422
433
|
**Vue is not supported.** The SDK is framework-agnostic so `connect()` may work, and `piniaStore` will read a Pinia store, but there is no detection, no `.vue` source stamping and no CI gate.
|
|
423
434
|
|
|
@@ -427,13 +438,13 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
|
|
|
427
438
|
|
|
428
439
|
**`reticle_sessions` is empty / "no browser session connected"**
|
|
429
440
|
|
|
430
|
-
- Run **`reticle status
|
|
441
|
+
- Run **`reticle status`**. It shows whether the daemon is up and which tabs are connected (url, health, pending flagged bugs) at a glance. No connected sessions means the SDK isn't reaching the bridge.
|
|
431
442
|
- Is your app actually running and open in a browser tab?
|
|
432
443
|
- Is `reticle.connect()` running? (Check it's inside your dev guard and the guard is true.)
|
|
433
444
|
- Port mismatch? If you set `RETICLE_PORT`, pass the same URL to `reticle.connect({ url: 'ws://localhost:<port>/reticle' })`.
|
|
434
|
-
- Need to restart the daemon? **`reticle stop`** cleans it up
|
|
445
|
+
- Need to restart the daemon? **`reticle stop`** cleans it up, no `pkill` needed.
|
|
435
446
|
|
|
436
|
-
The errors Reticle returns to the agent now carry a `recovery` hint for this exact situation (and for multiple/unknown sessions, a throttled tab, a missing baseline)
|
|
447
|
+
The errors Reticle returns to the agent now carry a `recovery` hint for this exact situation (and for multiple/unknown sessions, a throttled tab, a missing baseline), so the agent knows the next move.
|
|
437
448
|
|
|
438
449
|
**The agent can't find an element**
|
|
439
450
|
|
|
@@ -452,13 +463,13 @@ The errors Reticle returns to the agent now carry a `recovery` hint for this exa
|
|
|
452
463
|
|
|
453
464
|
**Nothing should run in production**
|
|
454
465
|
|
|
455
|
-
- Keep `reticle.connect()` behind a dev guard (`import.meta.env.DEV` / `NODE_ENV`). The package is side-effect free and tree-shakes out when unused. As a backstop, `connect()` also self-disables when the build reports `NODE_ENV=production` (so an SSR healthcheck or a prod bundle opened on localhost won't activate it)
|
|
466
|
+
- Keep `reticle.connect()` behind a dev guard (`import.meta.env.DEV` / `NODE_ENV`). The package is side-effect free and tree-shakes out when unused. As a backstop, `connect()` also self-disables when the build reports `NODE_ENV=production` (so an SSR healthcheck or a prod bundle opened on localhost won't activate it). Pass `allowInProduction: true` only for a deliberate prod diagnostic.
|
|
456
467
|
|
|
457
468
|
## Installing alongside a Next.js or React prerelease
|
|
458
469
|
|
|
459
|
-
`@reticlehq/next` declares `peer next >=13`, and `@reticlehq/react` declares `peer react >=18`. If your app runs a **prerelease
|
|
470
|
+
`@reticlehq/next` declares `peer next >=13`, and `@reticlehq/react` declares `peer react >=18`. If your app runs a **prerelease**, such as a Next.js canary/preview (`16.3.0-preview.9`) or a React RC, npm will refuse the install with `ERESOLVE`.
|
|
460
471
|
|
|
461
|
-
That is npm's semver rule, not a Reticle restriction: a prerelease version satisfies a range only when some comparator shares its exact `major.minor.patch`. No floor-style range accepts it
|
|
472
|
+
That is npm's semver rule, not a Reticle restriction: a prerelease version satisfies a range only when some comparator shares its exact `major.minor.patch`. No floor-style range accepts it (verified, including `*`). Marking the peer optional does not help either, because npm still version-checks a peer that is present.
|
|
462
473
|
|
|
463
474
|
Install with either of these instead. Both are safe; the floor is a real minimum, not a maximum:
|
|
464
475
|
|
package/docs/human-control.md
CHANGED
|
@@ -1,39 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: 'Human-in-the-loop control'
|
|
3
|
+
description: 'Watch the agent’s live transcript, steer it mid-run, or end the session, all from inside the page itself.'
|
|
4
|
+
icon: hand
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
When you run with the presenter on (`reticle.connect({ present: true })`), the floating panel is a **two-way control surface**. You watch the agent's live transcript _and_ can steer it
|
|
7
|
+
When you run with the presenter on (`reticle.connect({ present: true })`), the floating panel is a **two-way control surface**. You watch the agent's live transcript _and_ can steer it (pause it, send it a correction, or end the session) without leaving the page. There's also a clean, unmistakable "session ended" signal for both you and the agent.
|
|
4
8
|
|
|
5
9
|
## Watch the testing score climb (the live verdict tally)
|
|
6
10
|
|
|
7
|
-
The panel header shows a running **✓N ✗M** tally
|
|
11
|
+
The panel header shows a running **✓N ✗M** tally: the verdict score, live. It's hidden until the first verdict lands, then climbs as the agent verifies: every `reticle_assert`, every replayed flow step, every pass/fail outcome bumps it, and the side that just grew **pops** so you _feel_ the green (or red) arrive. The cursor flies, the log streams the journey, and the score ticks up. You see the agent working and the result landing, not just a final checkmark. It persists when the session ends, so the last thing on screen is what this run actually verified. Green that's earned, not asserted.
|
|
8
12
|
|
|
9
13
|
## The constraint that shapes the design
|
|
10
14
|
|
|
11
|
-
MCP is **pull-based**: the agent only receives data when it calls a tool. Reticle can't freeze the model mid-thought
|
|
15
|
+
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:
|
|
12
16
|
|
|
13
|
-
- **Nudge** (a message, no pause)
|
|
14
|
-
- **Pause
|
|
15
|
-
- **End
|
|
17
|
+
- **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.
|
|
18
|
+
- **Pause.** The agent's next `reticle_act` / `reticle_act_and_wait` / `reticle_act_sequence` **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.
|
|
19
|
+
- **End.** The session is over; the panel shows "Session ended" and clears.
|
|
16
20
|
|
|
17
21
|
## From the panel (the human)
|
|
18
22
|
|
|
19
23
|
The floating panel (bottom-center, `present: true`) gives you:
|
|
20
24
|
|
|
21
|
-
- **Pause / Resume
|
|
22
|
-
- **Message box + send
|
|
23
|
-
- **End**
|
|
24
|
-
- **Minimise (▾)**
|
|
25
|
-
- **Flag a bug
|
|
25
|
+
- **Pause / Resume**: one toggle. Paused turns the panel + page border **amber** with a `PAUSED` badge.
|
|
26
|
+
- **Message box + send.** Type a correction ("check the error state first"), hit Send (or Enter). Your message shows as a `🧑 you:` bubble in the transcript **and** is delivered to the agent.
|
|
27
|
+
- **End** ends the session: panel turns **emerald**, shows `Session ended · <summary>`, then fades away.
|
|
28
|
+
- **Minimise (▾)** collapses the panel to a bar that streams only the live line; click the bar to restore.
|
|
29
|
+
- **Flag a bug**, the button in the corner. Toggle it on, click the element that looks wrong, type what's wrong (⌘/Ctrl+Enter to send, Esc to back out). Reticle pins a numbered marker, logs your flag in the panel, and hands the agent a structured mark.
|
|
26
30
|
|
|
27
|
-
## Flag a bug
|
|
31
|
+
## Flag a bug: annotate the mistake where you see it
|
|
28
32
|
|
|
29
|
-
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
|
|
33
|
+
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:
|
|
30
34
|
|
|
31
35
|
1. **You** flag the element and type the problem → Reticle emits a `HUMAN_MARK`.
|
|
32
|
-
2. **The agent** drains it with `reticle_session {action:"review"}
|
|
36
|
+
2. **The agent** drains it with `reticle_session {action:"review"}`, getting note + element label + `source: { file, line }` + a ready-to-act `fix` hint. `reticle_sessions` also reports `pendingMarks` so the agent notices flags.
|
|
33
37
|
3. **The agent** opens the file, fixes it, and calls `reticle_session {action:"review"}({ resolve: "m1" })`.
|
|
34
38
|
4. **You** see **"✓ fixed: \<your note\>"** land in the panel. Flag → fix → confirmation.
|
|
35
39
|
|
|
36
|
-
See [`reticle_session {action:"review"}` in the usage guide](usage
|
|
40
|
+
See [`reticle_session {action:"review"}` in the usage guide](/usage) for the tool shape. Suppress the button with `annotate: false` if you don't want it.
|
|
37
41
|
|
|
38
42
|
## From the agent (the tools)
|
|
39
43
|
|
|
@@ -44,7 +48,7 @@ See [`reticle_session {action:"review"}` in the usage guide](usage.md#reticle_se
|
|
|
44
48
|
| `reticle_session {action:"messages"}` | `{ sessionId? }` | drain + read pending human messages (explicit poll) |
|
|
45
49
|
| `reticle_session {action:"review"}` | `{ resolve?, all?, sessionId? }` | list the bugs the human flagged; resolve one once fixed |
|
|
46
50
|
|
|
47
|
-
When paused, every action tool short-circuits with the human's guidance, so the agent learns of the pause on its very next action. The agent's expected behavior: **read the guidance, adjust the plan, then call `reticle_session {action:"resume"}`** (or wait for the human to click Resume). The agent can also end the run itself with `reticle_session {action:"end"}({ summary })` when it's done
|
|
51
|
+
When paused, every action tool short-circuits with the human's guidance, so the agent learns of the pause on its very next action. The agent's expected behavior: **read the guidance, adjust the plan, then call `reticle_session {action:"resume"}`** (or wait for the human to click Resume). The agent can also end the run itself with `reticle_session {action:"end"}({ summary })` when it's done. Either path shows the same "ended" state.
|
|
48
52
|
|
|
49
53
|
## Piggybacked guidance
|
|
50
54
|
|
|
@@ -54,7 +58,7 @@ Even without a pause, action/observe/assert results carry a `control` block when
|
|
|
54
58
|
{ since, dispatched, settled, result, control: { state: "active", guidance: ["looks good, keep going"] } }
|
|
55
59
|
```
|
|
56
60
|
|
|
57
|
-
`guidance` is drained (delivered once), so a hint never repeats. When the session is clean and the inbox empty, no `control` field is added
|
|
61
|
+
`guidance` is drained (delivered once), so a hint never repeats. When the session is clean and the inbox empty, no `control` field is added: zero overhead on the happy path.
|
|
58
62
|
|
|
59
63
|
## Tell your agent to honor it
|
|
60
64
|
|