@reticlehq/server 3.1.0 → 3.2.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.
Files changed (228) hide show
  1. package/SKILL.md +17 -18
  2. package/dist/command/cli/auth/device-flow.d.ts +2 -2
  3. package/dist/command/cli/cli-doctor.js +13 -1
  4. package/dist/command/cli/cli-flow-commands.d.ts +2 -1
  5. package/dist/command/cli/cli-kill.js +1 -1
  6. package/dist/command/cli/cli-parse.d.ts +2 -1
  7. package/dist/command/cli/cli-parse.js +5 -2
  8. package/dist/command/cli/cli-verify.js +2 -2
  9. package/dist/command/cli/cloud-cli.js +7 -6
  10. package/dist/command/cli/cloud-kit.js +2 -6
  11. package/dist/command/cli/config/config-discovery.d.ts +12 -0
  12. package/dist/command/cli/config/config-discovery.js +19 -0
  13. package/dist/command/cli/doctor/doctor-rows.d.ts +12 -0
  14. package/dist/command/cli/doctor/doctor-rows.js +12 -0
  15. package/dist/command/cli/drive/drive-command.js +1 -1
  16. package/dist/command/cli/launch/cli-launch.d.ts +10 -2
  17. package/dist/command/cli/launch/cli-launch.js +32 -39
  18. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +33 -0
  19. package/dist/command/cli/lifecycle/daemon-lifecycle.js +198 -0
  20. package/dist/command/cli/mcp-command.js +5 -3
  21. package/dist/command/cli/ports/resolve/cli-port.d.ts +2 -1
  22. package/dist/command/cli/ports/resolve/cli-port.js +5 -1
  23. package/dist/command/cli/status/status-lines.d.ts +28 -0
  24. package/dist/command/cli/status/status-lines.js +117 -0
  25. package/dist/command/cli.d.ts +1 -1
  26. package/dist/command/cli.js +24 -177
  27. package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
  28. package/dist/command/daemon/binding/daemon-status-probe.js +48 -0
  29. package/dist/command/daemon/binding/loopback-reach.d.ts +42 -0
  30. package/dist/command/daemon/binding/loopback-reach.js +63 -0
  31. package/dist/command/daemon/daemon-resilience.d.ts +13 -1
  32. package/dist/command/daemon/daemon-resilience.js +12 -1
  33. package/dist/command/daemon/daemon.d.ts +2 -11
  34. package/dist/command/daemon/daemon.js +8 -11
  35. package/dist/command/daemon/lifetime/heartbeat.d.ts +8 -0
  36. package/dist/command/daemon/lifetime/heartbeat.js +10 -0
  37. package/dist/command/daemon/lifetime/log-cap.d.ts +68 -0
  38. package/dist/command/daemon/lifetime/log-cap.js +115 -0
  39. package/dist/command/setup/agent-notice-memory.d.ts +16 -0
  40. package/dist/command/setup/agent-notice-memory.js +65 -0
  41. package/dist/command/setup/bringup/dev-server-wait.d.ts +1 -0
  42. package/dist/command/setup/bringup/dev-server-wait.js +16 -0
  43. package/dist/command/setup/bringup/ensure-daemon.js +1 -1
  44. package/dist/command/setup/init/init-runtime.js +13 -1
  45. package/dist/command/setup/node-effects.js +1 -1
  46. package/dist/command/setup/probe/page-probe.js +9 -2
  47. package/dist/command/setup/run-setup.d.ts +25 -7
  48. package/dist/command/setup/run-setup.js +51 -9
  49. package/dist/command/setup/setup-command.js +31 -3
  50. package/dist/command/setup/terminal/confirm.js +2 -1
  51. package/dist/command/update/updater.d.ts +27 -0
  52. package/dist/command/update/updater.js +35 -2
  53. package/dist/features/crawl/crawl-tools.js +3 -1
  54. package/dist/features/crawl/crawl.js +2 -1
  55. package/dist/features/harness/drive-report.d.ts +73 -0
  56. package/dist/features/harness/drive-report.js +196 -0
  57. package/dist/features/harness/driver.js +29 -8
  58. package/dist/features/harness/drivers.d.ts +19 -0
  59. package/dist/features/harness/drivers.js +19 -0
  60. package/dist/features/harness/fill-values.d.ts +67 -0
  61. package/dist/features/harness/fill-values.js +153 -0
  62. package/dist/features/harness/jev-driver.d.ts +188 -0
  63. package/dist/features/harness/jev-driver.js +664 -0
  64. package/dist/features/harness/openai-driver.d.ts +46 -0
  65. package/dist/features/harness/openai-driver.js +197 -0
  66. package/dist/features/harness/platform-config.d.ts +52 -0
  67. package/dist/features/harness/platform-config.js +89 -0
  68. package/dist/features/visual/visual-tools.js +26 -2
  69. package/dist/index.js +60 -55
  70. package/dist/judgement/domain/domain-tools.js +4 -2
  71. package/dist/judgement/runs/verification-sync.d.ts +2 -2
  72. package/dist/judgement/runs/verification-sync.js +9 -2
  73. package/dist/language/flows/affected-tools.js +6 -3
  74. package/dist/language/flows/flow-replay-run.d.ts +25 -2
  75. package/dist/language/flows/flow-replay-run.js +73 -27
  76. package/dist/language/flows/flow-replay.d.ts +17 -0
  77. package/dist/language/flows/flow-replay.js +148 -100
  78. package/dist/language/flows/flow-result.d.ts +18 -1
  79. package/dist/language/flows/flow-result.js +18 -2
  80. package/dist/language/flows/flow-store-for-session.d.ts +2 -1
  81. package/dist/language/flows/flow-tools.js +8 -4
  82. package/dist/language/flows/flows.d.ts +8 -7
  83. package/dist/language/flows/flows.js +7 -3
  84. package/dist/language/flows/narrow-write.d.ts +4 -4
  85. package/dist/language/flows/replay.js +1 -1
  86. package/dist/language/flows/suite-selection.d.ts +15 -1
  87. package/dist/language/flows/suite-selection.js +1 -1
  88. package/dist/language/flows/verify-change-tools.js +7 -4
  89. package/dist/memory/cloud/account-state.d.ts +1 -1
  90. package/dist/memory/cloud/account-state.js +5 -4
  91. package/dist/memory/cloud/cloud-config.d.ts +9 -0
  92. package/dist/memory/cloud/cloud-config.js +9 -0
  93. package/dist/memory/cloud/cloud-sync.d.ts +7 -2
  94. package/dist/memory/cloud/cloud-sync.js +10 -5
  95. package/dist/memory/cloud/harness-config.d.ts +10 -0
  96. package/dist/memory/cloud/harness-config.js +58 -0
  97. package/dist/memory/cloud/harness-offer.d.ts +44 -0
  98. package/dist/memory/cloud/harness-offer.js +146 -0
  99. package/dist/memory/cloud/harness-switch.d.ts +11 -0
  100. package/dist/memory/cloud/harness-switch.js +53 -0
  101. package/dist/memory/cloud/sync-daemon.d.ts +22 -2
  102. package/dist/memory/cloud/sync-daemon.js +24 -0
  103. package/dist/memory/impact/impact-recorder.d.ts +8 -0
  104. package/dist/memory/impact/impact-recorder.js +2 -1
  105. package/dist/memory/impact/impact-store.d.ts +5 -0
  106. package/dist/memory/impact/impact-store.js +19 -0
  107. package/dist/memory/intent/intent-shard-store.js +2 -2
  108. package/dist/memory/intent/intent-shard.d.ts +12 -12
  109. package/dist/memory/journal/attach-journal.d.ts +11 -0
  110. package/dist/memory/journal/attach-journal.js +1 -1
  111. package/dist/memory/journal/journal-query.d.ts +35 -1
  112. package/dist/memory/journal/journal-query.js +46 -0
  113. package/dist/memory/journal/journal-recorder.d.ts +50 -2
  114. package/dist/memory/journal/journal-recorder.js +45 -7
  115. package/dist/memory/journal/on-disk/retention.d.ts +87 -2
  116. package/dist/memory/journal/on-disk/retention.js +177 -8
  117. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +9 -0
  118. package/dist/memory/journal/on-disk/workspace-gitignore.js +9 -2
  119. package/dist/memory/journal/self-observation.d.ts +3 -0
  120. package/dist/memory/journal/self-observation.js +67 -0
  121. package/dist/memory/journal/session-end.d.ts +13 -3
  122. package/dist/memory/journal/session-end.js +24 -4
  123. package/dist/memory/journal/session-journal.d.ts +115 -5
  124. package/dist/memory/journal/session-journal.js +479 -32
  125. package/dist/memory/project/artifact-root-resolver.d.ts +3 -0
  126. package/dist/memory/project/artifact-root-resolver.js +89 -0
  127. package/dist/memory/project/artifact-root.d.ts +39 -2
  128. package/dist/memory/project/artifact-root.js +58 -1
  129. package/dist/memory/project/dir/fill-value-store.d.ts +32 -0
  130. package/dist/memory/project/dir/fill-value-store.js +88 -0
  131. package/dist/memory/project/dir/reticle-dir.d.ts +14 -4
  132. package/dist/memory/project/dir/reticle-dir.js +11 -0
  133. package/dist/memory/project/fs/fs-port.d.ts +22 -1
  134. package/dist/memory/project/fs/fs-port.js +33 -5
  135. package/dist/memory/project/learned-routes.d.ts +16 -2
  136. package/dist/memory/project/learned-routes.js +26 -10
  137. package/dist/memory/project/project-for-root.d.ts +30 -0
  138. package/dist/memory/project/project-for-root.js +43 -0
  139. package/dist/memory/project/project-tools.js +10 -5
  140. package/dist/memory/project/session-root.d.ts +35 -6
  141. package/dist/memory/project/session-root.js +70 -8
  142. package/dist/portal/bridge/auth-failure-reason.d.ts +9 -0
  143. package/dist/portal/bridge/auth-failure-reason.js +15 -1
  144. package/dist/portal/bridge/bridge.d.ts +6 -1
  145. package/dist/portal/bridge/bridge.js +36 -2
  146. package/dist/portal/session/facts/session-replaced.d.ts +19 -0
  147. package/dist/portal/session/facts/session-replaced.js +22 -0
  148. package/dist/portal/session/no-session-diagnosis.d.ts +1 -0
  149. package/dist/portal/session/no-session-diagnosis.js +67 -60
  150. package/dist/portal/session/no-session-next-action.js +3 -2
  151. package/dist/portal/session/no-session-watch.js +14 -10
  152. package/dist/portal/session/rebind.d.ts +18 -0
  153. package/dist/portal/session/rebind.js +38 -0
  154. package/dist/portal/session/session-manager.d.ts +4 -0
  155. package/dist/portal/session/session-manager.js +19 -1
  156. package/dist/portal/session/session.d.ts +21 -10
  157. package/dist/portal/session/session.js +29 -42
  158. package/dist/status-payload.d.ts +12 -1
  159. package/dist/status-payload.js +4 -1
  160. package/dist/surface/http-server.js +3 -5
  161. package/dist/surface/mcp/faults/mcp-outage.d.ts +2 -1
  162. package/dist/surface/mcp/faults/mcp-outage.js +20 -4
  163. package/dist/surface/mcp/mcp-proxy.d.ts +10 -1
  164. package/dist/surface/mcp/mcp-proxy.js +20 -9
  165. package/dist/surface/mcp/mcp.d.ts +10 -1
  166. package/dist/surface/mcp/mcp.js +59 -29
  167. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +15 -2
  168. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +22 -5
  169. package/dist/surface/mcp/proxy/proxy-log.d.ts +5 -11
  170. package/dist/surface/mcp/proxy/proxy-log.js +7 -22
  171. package/dist/surface/tools/act/act-preflight.js +6 -1
  172. package/dist/surface/tools/act/sequence-grade.js +13 -0
  173. package/dist/surface/tools/act-capsule.js +27 -1
  174. package/dist/surface/tools/assert/assert-verdict.js +44 -17
  175. package/dist/surface/tools/coverage-tools.js +5 -2
  176. package/dist/surface/tools/dynamic-tools.js +37 -46
  177. package/dist/surface/tools/error-recovery.d.ts +6 -0
  178. package/dist/surface/tools/error-recovery.js +15 -0
  179. package/dist/surface/tools/explore-tools.js +80 -7
  180. package/dist/surface/tools/feedback-tools.js +49 -2
  181. package/dist/surface/tools/first-sentence.d.ts +24 -0
  182. package/dist/surface/tools/first-sentence.js +0 -0
  183. package/dist/surface/tools/harness-explore.d.ts +125 -1
  184. package/dist/surface/tools/harness-explore.js +401 -15
  185. package/dist/surface/tools/harness-plan.d.ts +76 -0
  186. package/dist/surface/tools/harness-plan.js +156 -0
  187. package/dist/surface/tools/harness-toolset.js +12 -1
  188. package/dist/surface/tools/invoke-tool.js +65 -5
  189. package/dist/surface/tools/live-call-text.d.ts +6 -0
  190. package/dist/surface/tools/live-call-text.js +36 -2
  191. package/dist/surface/tools/observe-tools.js +55 -9
  192. package/dist/surface/tools/read/query-windowed.d.ts +1 -1
  193. package/dist/surface/tools/read/query-windowed.js +1 -1
  194. package/dist/surface/tools/read-tools.js +1 -2
  195. package/dist/surface/tools/tool-kit.d.ts +18 -3
  196. package/dist/surface/tools/tools-helpers.d.ts +8 -7
  197. package/dist/surface/tools/tools-helpers.js +11 -13
  198. package/dist/surface/tools/tools.js +6 -1
  199. package/dist/telemetry/bug-found.js +32 -2
  200. package/dist/telemetry/feedback-context.js +3 -3
  201. package/dist/telemetry/harness-drive.d.ts +6 -0
  202. package/dist/telemetry/harness-drive.js +39 -0
  203. package/dist/telemetry/mcp-connection.d.ts +14 -1
  204. package/dist/telemetry/mcp-connection.js +17 -2
  205. package/dist/telemetry/onboarding-firsts.d.ts +30 -1
  206. package/dist/telemetry/onboarding-firsts.js +54 -12
  207. package/dist/telemetry/verification-of.js +31 -3
  208. package/docs/agent-cheatsheet.md +7 -3
  209. package/docs/autodrive.md +42 -2
  210. package/docs/benchmarks.md +1 -1
  211. package/docs/capabilities.mdx +1 -1
  212. package/docs/cli/cloud.mdx +3 -3
  213. package/docs/cli/doctor.mdx +1 -0
  214. package/docs/cli/status.mdx +35 -20
  215. package/docs/cli.mdx +8 -18
  216. package/docs/desktop.mdx +1 -1
  217. package/docs/faq.mdx +1 -1
  218. package/docs/install-manual.mdx +1 -1
  219. package/docs/packages/tauri.mdx +1 -1
  220. package/docs/packages.mdx +1 -1
  221. package/docs/predicates.mdx +16 -0
  222. package/docs/system-map.md +6 -4
  223. package/docs/telemetry.md +2 -1
  224. package/docs/token-efficiency.md +2 -2
  225. package/docs/tools/overview.mdx +1 -1
  226. package/docs/troubleshooting.mdx +8 -2
  227. package/docs/usage.md +1 -1
  228. package/package.json +6 -6
@@ -20,7 +20,7 @@ These are thin clients over the `/v1` API. The logic is server-side; these verbs
20
20
  | `~/.reticle/credentials.json` | Project-scoped API keys from `reticle link`, keyed by `projectId` | Yes |
21
21
  | `<repo>/.reticle/cloud.json` | `{ projectId, projectName, url, sync, verify }`. The non-secret binding | No, commit it |
22
22
 
23
- Auth for any command is `RETICLE_CLOUD_KEY` from the environment (the agent path) if set, otherwise the login token. `RETICLE_CLOUD_URL` overrides the endpoint; the default is `http://localhost:8890`. When a command falls back to that default without `RETICLE_CLOUD_URL` (or a saved session URL), it prints a one-line notice on stderr; stdout stays pure JSON for agents.
23
+ Auth for any command is `RETICLE_API_KEY` from the environment (the agent path) if set, otherwise the login token. `RETICLE_CLOUD_URL` overrides the endpoint; the default is `http://localhost:8890`. When a command falls back to that default without `RETICLE_CLOUD_URL` (or a saved session URL), it prints a one-line notice on stderr; stdout stays pure JSON for agents.
24
24
 
25
25
  ## The commands
26
26
 
@@ -70,7 +70,7 @@ An unattached repo also gets a next-step nudge on stderr, telling you to run `re
70
70
  npx @reticlehq/server link [--project <name|id>]
71
71
  ```
72
72
 
73
- Binds this repository to a cloud project. With a login token it **mints** a project-scoped key, so there is nothing to paste. With `RETICLE_CLOUD_KEY` already set, it resolves that key's project instead. `--project` accepts a slug id or a display name; omitted, it uses the `default` project. Writes `cloud.json` and the key.
73
+ Binds this repository to a cloud project. With a login token it **mints** a project-scoped key, so there is nothing to paste. With `RETICLE_API_KEY` already set, it resolves that key's project instead. `--project` accepts a slug id or a display name; omitted, it uses the `default` project. Writes `cloud.json` and the key.
74
74
 
75
75
  ### `reticle project`
76
76
 
@@ -137,7 +137,7 @@ reticle: cloud not attached here ... run `reticle link` (or set RETICLE_CLOUD_UR
137
137
  | ---- | ----------------------------------------------------------------------------------- |
138
138
  | `0` | Success |
139
139
  | `1` | The API call failed, or the repository is not attached where attachment is required |
140
- | `2` | Usage error, or not signed in and no `RETICLE_CLOUD_KEY` |
140
+ | `2` | Usage error, or not signed in and no `RETICLE_API_KEY` |
141
141
  | `3` | `regression` only: at least one flow broke |
142
142
 
143
143
  Output is pretty-printed JSON on stdout. Errors and next-step nudges go to stderr, so an agent parsing stdout is unaffected.
@@ -52,6 +52,7 @@ When nothing has connected, the `sessions` line fails and the daemon's own diagn
52
52
  | `daemon` | Whether the port holds a Reticle daemon, a foreign process (named where possible), or nothing. Adds a `version` line when the daemon's version or contract fingerprint disagrees with the CLI's |
53
53
  | `sessions` | Whether any page has actually connected. Zero is a FAILING check rather than a neutral note, because it is the state the setup most often stalls in: wired correctly, daemon up, nothing dialled in. When it is zero the daemon's own no-session diagnosis prints below the checklist |
54
54
  | `agent link` | Whether an MCP client has listed Reticle's tools on this port **and actually called one**. Every other row here checks a component; this one checks the hop between your agent and the daemon. It is possible for the SDK to be injected, a session to be live, and your client to show every tool enabled, while no request has ever crossed from the agent. That state passes every other check and is the one people lose hours to |
55
+ | `loopback` | Which of `localhost`'s two answers reaches the bridge. Printed **only when they disagree**. The daemon binds `127.0.0.1`, and a best-effort alias forwards `[::1]` to it so the name works everywhere. When that alias cannot open (IPv6 disabled, or something already holding `[::1]` on the port) the daemon serves IPv4 perfectly and a page told to dial `localhost` still cannot reach it, because Windows Chrome tries the IPv6 answer first. Every other row here goes green in that state |
55
56
  | `bridge port` | The port your app must dial, spelled out because mixing it up with the dev server port is the most common setup failure |
56
57
  | `port check` | Only when your project's configured port disagrees with the one the daemon is on. Prints the mismatch rather than leaving you to spot it across two lines |
57
58
  | `sibling` | Only when a well-known Reticle port other than this daemon's has a listener. An observation, not a diagnosis: that listener may or may not be related |
@@ -7,7 +7,7 @@ icon: heart-pulse
7
7
  `reticle status` is the command that tells you whether the install actually finished. Not a tick in a checklist: a connected app.
8
8
 
9
9
  ```bash
10
- npx @reticlehq/server status [--port N]
10
+ npx @reticlehq/server status [--port N] [--json]
11
11
  ```
12
12
 
13
13
  ## Flags
@@ -15,10 +15,37 @@ npx @reticlehq/server status [--port N]
15
15
  | Flag | Type | Default | What it does |
16
16
  | --- | --- | --- | --- |
17
17
  | `--port N` | number | `4400` (or `RETICLE_PORT`, or the port in `.reticle.json`) | Which daemon to ask |
18
+ | `--json` | flag | off | Print the machine-readable event instead of the block below |
18
19
 
19
20
  ## What it prints
20
21
 
21
- One JSON line on stdout. Real capture, daemon up and no app connected:
22
+ A short block, for whoever is reading the terminal. Real capture, daemon up with two apps connected:
23
+
24
+ ```text
25
+ reticle status
26
+ daemon ✓ running on :4400 (pid 90790)
27
+ sessions ✓ 2 pages connected
28
+ http://localhost:5173/ (throttled, hidden)
29
+ http://localhost:7699/ (throttled, hidden)
30
+ agent link enumerated
31
+ ```
32
+
33
+ And with no daemon on the port:
34
+
35
+ ```text
36
+ reticle status
37
+ daemon ✗ not running on :4999
38
+ agent link never-attached
39
+ next no daemon is running on this port, which is normal. An agent starts it when it
40
+ first calls a Reticle tool, and it exits again when idle. Ask your agent to verify
41
+ something, then run status again.
42
+ ```
43
+
44
+ A session's parenthesised notes are the ones that explain an answer you would otherwise call a bug: `throttled` and `hidden` mean a background tab answering slowly, `stale` means the page has moved on from what the daemon last saw, and `unresponsive` means it is not answering at all.
45
+
46
+ ### `--json`
47
+
48
+ The same facts as one event line on stderr, for a caller that parses rather than reads:
22
49
 
23
50
  ```json
24
51
  {
@@ -33,30 +60,18 @@ One JSON line on stdout. Real capture, daemon up and no app connected:
33
60
  }
34
61
  ```
35
62
 
36
- With an app connected, `why` is absent and each session carries its own fields:
63
+ It is one line in the terminal, pretty-printed here, with `why` trimmed at the ellipses. With an app connected `why` is absent and each session carries its own fields:
37
64
 
38
65
  ```json
39
66
  {
40
- "t": "2026-08-15T18:45:58.190Z",
41
- "event": "reticle_status",
42
- "port": 4400,
43
- "running": true,
44
- "pid": 72698,
45
- "sessionCount": 1,
46
- "sessions": [
47
- {
48
- "sessionId": "s223e0fad-e012-43f9-a23d-6d585470c90d",
49
- "url": "http://localhost:4310/",
50
- "throttled": false,
51
- "stale": false,
52
- "pendingMarks": 0
53
- }
54
- ]
67
+ "sessionId": "s223e0fad-e012-43f9-a23d-6d585470c90d",
68
+ "url": "http://localhost:4310/",
69
+ "throttled": false,
70
+ "stale": false,
71
+ "pendingMarks": 0
55
72
  }
56
73
  ```
57
74
 
58
- Both are one line of JSON in the terminal, pretty-printed here, with `why` trimmed at the ellipses.
59
-
60
75
  | Field | Meaning |
61
76
  | --- | --- |
62
77
  | `running` | Whether a usable daemon owns the port |
package/docs/cli.mdx CHANGED
@@ -71,26 +71,16 @@ Exits `0`. That parenthetical about the bridge port is doing a lot of work. Mixi
71
71
 
72
72
  Whether the daemon is up and which apps have connected.
73
73
 
74
- ```json
75
- {
76
- "t": "2026-08-15T18:45:58.190Z",
77
- "event": "reticle_status",
78
- "port": 4400,
79
- "running": true,
80
- "pid": 72698,
81
- "sessionCount": 1,
82
- "sessions": [
83
- {
84
- "sessionId": "s223e0fad-e012-43f9-a23d-6d585470c90d",
85
- "url": "http://localhost:4310/",
86
- "throttled": false,
87
- "stale": false,
88
- "pendingMarks": 0
89
- }
90
- ]
91
- }
74
+ ```text
75
+ reticle status
76
+ daemon ✓ running on :4400 (pid 90790)
77
+ sessions ✓ 1 page connected
78
+ http://localhost:4310/
79
+ agent link enumerated
92
80
  ```
93
81
 
82
+ Add `--json` for the same facts as one machine-readable event line. See [`reticle status`](/cli/status) for the fields.
83
+
94
84
  Exits `0`. A session in the list is the install genuinely finished. Not a tick in a checklist, a connected app.
95
85
 
96
86
  ### `reticle telemetry [status|enable|disable]`
package/docs/desktop.mdx CHANGED
@@ -214,7 +214,7 @@ Alternatively, since an Electron renderer _is_ Chromium, `--remote-debugging-por
214
214
 
215
215
  ```toml
216
216
  [dependencies]
217
- reticle-tauri = "3.1"
217
+ reticle-tauri = "3.2"
218
218
  ```
219
219
 
220
220
  ```rust
package/docs/faq.mdx CHANGED
@@ -102,7 +102,7 @@ What is measured is detection: 10 of 10 injected regressions caught, with zero f
102
102
  <Accordion title="Why does my agent only see some of Reticle's tools?">
103
103
  Because MCP re-sends every advertised tool definition on **every turn**, so the tool list is rent, not a one-time cost. Reticle advertises the verify loop directly and keeps the cold tail one call away.
104
104
 
105
- `reticle_tools` lists every tool (pass `names: [...]` for full parameters), but on the default surface listing is all it does: that surface is closed and ships no `reticle_run`, so a name outside the nine cannot be called until the daemon advertises it. If you want the whole surface advertised directly, set `RETICLE_ADVERTISE_ALL_TOOLS=1` in the **daemon's** environment and restart it, which costs roughly seven times the per-turn schema tax and is meant for suites that call by name rather than for running agents.
105
+ `reticle_tools` lists every registered tool (pass `names: [...]` for full parameters), and `reticle_run { tool, args }` calls any of them by name, whether or not it is advertised. That hatch is what makes a trimmed surface honest: a tool it does not show is one hop away rather than out of reach. If you want the whole surface advertised directly, set `RETICLE_ADVERTISE_ALL_TOOLS=1` in the **daemon's** environment and restart it, which costs roughly seven times the per-turn schema tax and is meant for suites that call by name rather than for running agents.
106
106
 
107
107
  <Card title="The surface, explained" icon="wrench" href="/tools/overview">
108
108
  Which tools are advertised directly, and why each earned its slot.
@@ -175,7 +175,7 @@ Screenshots and headless mode need the Rust crate, which is versioned independen
175
175
 
176
176
  ```toml
177
177
  [dependencies]
178
- reticle-tauri = "3.1"
178
+ reticle-tauri = "3.2"
179
179
  ```
180
180
 
181
181
  ```rust
@@ -21,7 +21,7 @@ Tauri IPC observation needs nothing on the Rust side. An `invoke('load_todos')`
21
21
 
22
22
  ```toml
23
23
  [dependencies]
24
- reticle-tauri = "3.1"
24
+ reticle-tauri = "3.2"
25
25
  ```
26
26
 
27
27
  Its own dependencies are `tauri = "2"` plus the per-platform capture backends: `objc2` and friends on macOS, `webview2-com` and `windows` on Windows, `webkit2gtk` and `cairo-rs` on Linux and the BSDs.
package/docs/packages.mdx CHANGED
@@ -239,7 +239,7 @@ A lint rule makes the signal layer self-enforcing, which is the only way a conve
239
239
 
240
240
  ```toml
241
241
  [dependencies]
242
- reticle-tauri = "3.1"
242
+ reticle-tauri = "3.2"
243
243
  ```
244
244
 
245
245
  <Warning>
@@ -76,6 +76,22 @@ The one that catches the expensive bug:
76
76
 
77
77
  **Exactly one.** Double-submit fails at two, and nothing on screen would have told you.
78
78
 
79
+ Two fields read the RESPONSE BODY, so a request that succeeded with the wrong number is still a failure:
80
+
81
+ ```json
82
+ { "kind": "net", "urlContains": "/api/refund", "bodyContains": "\"refunded\":11.87" }
83
+ ```
84
+
85
+ `bodyContains` is a substring test over the response body. It is deliberately not a JSON path: the string above is the whole assertion, it needs no schema, and it behaves the same on JSON, form encoding and plain text.
86
+
87
+ ```json
88
+ { "kind": "net", "urlContains": "/api/job", "bodyMatches": { "status": "completed" } }
89
+ ```
90
+
91
+ `bodyMatches` is a shallow field match, keyed like `signal.dataMatches` and taking the same operators (`*`, `$gte`, `$contains`). Reach for it whenever the value is an enum, because that is the case a substring cannot decide: `bodyContains: "completed"` passes on a body carrying `"completedAt": null`, where the needle matched the KEY and the job was still queued. `completed`/`completedAt`, `success`/`successRate`, `sent`/`unsent` all have that shape.
92
+
93
+ Both require body capture, `reticle({ captureNetworkBodies: true })`, and say so when the body was never recorded rather than reporting an ordinary mismatch.
94
+
79
95
  ### route
80
96
 
81
97
  ```json
@@ -56,11 +56,13 @@ Three different counts, all measured, and the difference between them matters:
56
56
 
57
57
  | Count | What it is |
58
58
  | --- | --- |
59
- | **68** | name constants in `ReticleTool` |
60
- | **48** | tools advertised under `RETICLE_ADVERTISE_ALL_TOOLS=1`, i.e. what `tool-surface-sweep-test` drives (51 calls) |
61
- | **19** | advertised by default; the rest are reached through `reticle_run` |
59
+ | **76** | name constants in `ReticleTool` |
60
+ | **30** | tools advertised under `RETICLE_ADVERTISE_ALL_TOOLS=1`, i.e. what `tool-surface-sweep-test` drives |
61
+ | **10** | advertised by default; the rest are reached through `reticle_run { tool, args }` |
62
62
 
63
- 68 → 48 is **family folding**: `reticle_baseline`, `reticle_session`, `reticle_record`, `reticle_flow` and `reticle_lease` each absorb their members behind an `action` parameter. So "every tool is callable" is asserted over 48 surfaces, not 68 behaviours; a family member reachable only through an `action` value the sweep never passes is not covered by it. Worth closing when the per-tool budget lands (see [`gate-plan.md`](https://github.com/reticlehq/reticle/blob/main/docs/gate-plan.md), Phase 4).
63
+ The middle number is **family folding**: `reticle_baseline`, `reticle_session`, `reticle_record`, `reticle_flow` and `reticle_lease` each absorb their members behind an `action` parameter. So "every tool is callable" is asserted over the advertised surfaces, not over every named behaviour; a family member reachable only through an `action` value the sweep never passes is not covered by it. Worth closing when the per-tool budget lands (see [`gate-plan.md`](https://github.com/reticlehq/reticle/blob/main/docs/gate-plan.md), Phase 4).
64
+
65
+ These three were each wrong by the time somebody checked: the table read 68 / 48 / 19 against a measured 76 / 30 / 10, and the middle one had a derived token cost hanging off it. `surface-sizes.test.ts` is the authority and fails when a surface changes size, so prefer reading it to trusting this row.
64
66
 
65
67
  | Token | Produced by | Consumed by | Invalidated by |
66
68
  | --- | --- | --- | --- |
package/docs/telemetry.md CHANGED
@@ -37,6 +37,7 @@ Eighteen kinds of events, each a single small JSON object. This is the whole lis
37
37
  | `runtime_crashed` | The daemon hits an uncaught error | The error's type, **Reticle's own** stack frames, and the message with variables stripped; see below |
38
38
  | `mcp_client_connected` | An agent attaches to the daemon | Whether it is a reconnect, and how long the daemon had been idle |
39
39
  | `mcp_connection_lost` | The agent's MCP tools go away | Which stage (`first`, or `budget_spent` when it stopped retrying), the cause, and the attempt count. **At most twice per session**, because a reconnect loop can fire many times in a row and an event each would bill for the pathology instead of measuring it |
40
+ | `onboarding_step` | One step of getting set up begins, finishes, fails, is skipped, or is walked away from | Which phase (`install`, `onboard`, `first_run`), which step, what became of it, how long it took, and the framework. The step name comes from a closed list we define -- it can only ever be one of the names in `core/src/onboarding.ts`, so nothing you typed or any path on your disk can arrive in it. `unattended` says whether anybody was asked anything |
40
41
  | `init_completed` | `reticle init` finishes | Whether it worked, a classified reason when it did not, and which published install route you came through, DECLARED by that route via `RETICLE_INSTALL_SOURCE` and never inferred, and `unknown` when nothing declared one |
41
42
  | `bug_found` | Reticle finds a defect in the app under test | The **kind** of defect (`signal-contradicted`, `console-error`, …) and how it was found, never what it was found in |
42
43
  | `tool_refused` | A tool cannot do what an agent asked | Which tool, why (one of six buckets: no session, no match, unsupported, bad arguments, not ready, other), and whether the same call was retried. **Never the message**, because a refusal interpolates whatever you asked for, so only the tool name and the bucket are sent. Capped at 50 per daemon run |
@@ -57,7 +58,7 @@ It also carries a snapshot of the **machine's** state (our own process's memory,
57
58
 
58
59
  It exists because `verified: "unknown"` covered seven different situations belonging to three different owners (your app, your agent, and Reticle's own blind spots), and they arrived as one value. It is a **name from our own vocabulary**, never a description of your app: `contradicted` says two channels disagreed, not which ones, about what, or on which page.
59
60
 
60
- When the clause is `unclean_capture`, one further name says which of our three losses caused it: `buffer_loss` (our server's event buffer), `transport_gap` (our browser-side queue), `blind_spot` (a boundary in the page, such as a cross-origin frame), or `other`. All four are facts about **Reticle's** ability to observe, not about what it observed. This field is how we found that Reticle was refusing to answer over windows that were completely intact.
61
+ When the clause is `unclean_capture`, one further name says which of our losses caused it: `buffer_loss` (our server's event buffer), `transport_gap` (our browser-side queue), `journal_loss` (our on-disk event ledger reached its size cap), `blind_spot` (a boundary in the page, such as a cross-origin frame), or `other`. All five are facts about **Reticle's** ability to observe, not about what it observed. This field is how we found that Reticle was refusing to answer over windows that were completely intact.
61
62
 
62
63
  ### Bugs Reticle finds
63
64
 
@@ -53,11 +53,11 @@ Measured live, all servers in one run, same tokenizer (`bench/harness/schema-tax
53
53
  | **Reticle, the tool surface** | 18 | **~4,930** |
54
54
  | Playwright MCP | 23 | 3,725 |
55
55
  | Chrome DevTools MCP | 29 | 5,116 |
56
- | Reticle, `RETICLE_ADVERTISE_ALL_TOOLS=1` | 48 | ~30,200 |
56
+ | Reticle, `RETICLE_ADVERTISE_ALL_TOOLS=1` | 30 | ~30,200 |
57
57
 
58
58
  _Measured 2026-08-12 (`bench/raw/schema-tax.json`). Reticle's default surface has gained a tool since, so treat the first row as a floor._
59
59
 
60
- The default surface is the verify loop plus `reticle_tools`, and it is CLOSED: those nine names are the whole of what can be called. There is no `reticle_run` dispatch hatch on it, so the cold tail is not merely un-advertised, it is unreachable until the daemon is started with `RETICLE_ADVERTISE_ALL_TOOLS=1`.
60
+ The default surface is the verify loop plus `reticle_tools` and `reticle_run`. The cold tail is un-advertised rather than unreachable: `reticle_run { tool, args }` calls any registered tool by name, and `RETICLE_ADVERTISE_ALL_TOOLS=1` advertises the whole table outright for suites that prefer that. Dropping the hatch was tried, and it left eleven registered tools callable by nothing at all.
61
61
 
62
62
  `RETICLE_ADVERTISE_ALL_TOOLS=1` advertises everything WITH output schemas. It is a verification switch for suites that call by name, not a mode to run agents in. It is roughly 7x the per-turn cost, which is why it is opt-in.
63
63
 
@@ -13,7 +13,7 @@ That is not a limitation, it's the design. MCP re-sends every advertised tool de
13
13
 
14
14
  A wider surface costs tokens on every message. Less obviously, it makes the model wander: give it fifty ways to look at a page and it will try several before doing anything useful.
15
15
 
16
- What the nine do **not** reach are the specialised tools: `reticle_screenshot`, `reticle_visual_diff`, `reticle_storage`, `reticle_network_mock`, `reticle_clock`. There is no dispatch hatch on this surface by design, so those need a daemon started with `RETICLE_ADVERTISE_ALL_TOOLS=1`.
16
+ What the verify loop does **not** advertise are the specialised tools: `reticle_screenshot`, `reticle_visual_diff`, `reticle_storage`, `reticle_network_mock`, `reticle_clock`. Each is one `reticle_run { tool, args }` hop away, and a daemon started with `RETICLE_ADVERTISE_ALL_TOOLS=1` advertises them outright.
17
17
 
18
18
  <Note>
19
19
  Measured off the real wire on 2026-09-16, by reading `tools/list` from a fresh daemon: the default
@@ -39,7 +39,7 @@ By far the most common. Reticle's own diagnostic is unusually good here. It tell
39
39
  ```json
40
40
  {
41
41
  "sessionCount": 0,
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_session { action: "list" } again."
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_session { action: \"list\" } again."
43
43
  }
44
44
  ```
45
45
 
@@ -186,7 +186,7 @@ The usual cause is a throttled tab: throttling suppresses the quiescence detecti
186
186
  If it persists on an unthrottled context, that is worth reporting. Reticle's own response says so:
187
187
 
188
188
  ```json
189
- "feedback_invite": "Reticle could not tell what happened here. If you expected otherwise, reticle_session { action: "feedback" }. An unknown verdict is our defect, not yours"
189
+ "feedback_invite": "Reticle could not tell what happened here. If you expected otherwise, reticle_session { action: \"feedback\" }. An unknown verdict is our defect, not yours"
190
190
  ```
191
191
 
192
192
  ## A negative result you are not sure about
@@ -227,6 +227,12 @@ React 19 dropped `_debugSource`. Wire up `@reticlehq/babel-plugin`. Or just use
227
227
 
228
228
  `@reticlehq/next` declares `peer next >=13` and `@reticlehq/react` declares `peer react >=18`. On a Next canary or React RC, npm refuses. See [the prerelease notes](/getting-started#installing-alongside-a-nextjs-or-react-prerelease).
229
229
 
230
+ ## The tool list is empty
231
+
232
+ Some clients read their MCP tool list once, at startup, and never again. Cursor Cloud Agent is the usual report: the daemon is listening on port 4400, the app is instrumented, and `reticle_*` is absent for the whole session. Restarting the client picks the tools up when a restart is possible. When it is not, the same tools are on the HTTP transport.
233
+
234
+ `GET /mcp/sse` opens the session. The first SSE frame is an `endpoint` event whose data is the path to POST to, including the session id. POST JSON-RPC there. The POST answers `202` with an empty body, and the result comes back on the SSE stream. The handshake and a working client are in [HTTP transport](/http-transport).
235
+
230
236
  ## Restarting things
231
237
 
232
238
  ```bash
package/docs/usage.md CHANGED
@@ -84,7 +84,7 @@ Who benefits most: anyone shipping **dashboards, internal tools, SaaS apps**. Be
84
84
 
85
85
  ### `reticle_session { action: "list" }`
86
86
 
87
- List connected tabs. → `{ sessions: [{ sessionId, url, title, lastSeenMs, hidden, focused, throttled }] }`. `lastSeenMs` is the silence since the tab last reported (not time-since-connect); `throttled` is `true` when the tab is hidden or has gone quiet, and a throttled tab silently no-ops timers/rAF/pointer.
87
+ List connected tabs. → `{ sessions: [{ sessionId, url, title, lastSeenMs, hidden, focused, throttled }] }`. `lastSeenMs` is the silence since the tab last reported (not time-since-connect); `throttled` is `true` when the tab is hidden **or** has merely gone quiet, and the two are not the same finding: a hidden tab clamps timers/rAF/pointer so an action can land on a page that never advances, while a quiet-but-visible tab is usually still driveable. The accompanying `recommendation` says which case you are in; treat `throttled` as a reason to read it, not as a reason to lease.
88
88
 
89
89
  ### `reticle_look { action: "page" }`
90
90
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reticlehq/server",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Reticle bridge + MCP server. Hosts the browser WS endpoint and exposes MCP tools to the coding agent.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,11 +23,11 @@
23
23
  "ws": "^8.21.3",
24
24
  "zod": "^3.24.1",
25
25
  "zod-to-json-schema": "^3.24.0",
26
- "@reticlehq/browser": "3.1.0",
27
- "@reticlehq/core": "3.1.0",
28
- "open-verification": "1.0.0",
29
- "@reticlehq/engine": "3.1.0",
30
- "@reticlehq/init": "3.1.0"
26
+ "@reticlehq/browser": "3.2.0",
27
+ "@reticlehq/engine": "3.2.0",
28
+ "@reticlehq/init": "3.2.0",
29
+ "@reticlehq/core": "3.2.0",
30
+ "open-verification": "1.0.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/pngjs": "^6.0.5",