@reticlehq/server 2.11.0 → 2.12.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 (252) hide show
  1. package/SKILL.md +26 -2
  2. package/dist/bridge/bridge.d.ts +0 -13
  3. package/dist/bridge/bridge.js +1 -1
  4. package/dist/bridge/bridge.test-harness.d.ts +2 -0
  5. package/dist/bridge/bridge.test-harness.js +8 -1
  6. package/dist/capsule/capsule-store.d.ts +2 -1
  7. package/dist/capsule/capsule-store.js +1 -1
  8. package/dist/capsule/causal-summary.d.ts +2 -2
  9. package/dist/chromium-launch-options.d.ts +28 -0
  10. package/dist/chromium-launch-options.js +29 -0
  11. package/dist/cli/cli-doctor.js +8 -0
  12. package/dist/cli/cli-flow-commands.d.ts +3 -1
  13. package/dist/cli/cli-flow-commands.js +34 -4
  14. package/dist/cli/cli-kill.d.ts +2 -1
  15. package/dist/cli/cli-parse.d.ts +3 -1
  16. package/dist/cli/cli-parse.js +29 -7
  17. package/dist/cli/cli-update-commands.js +20 -1
  18. package/dist/cli/cli-verify.d.ts +1 -1
  19. package/dist/cli/cli-verify.js +2 -2
  20. package/dist/cli/cloud-cli.js +12 -6
  21. package/dist/cli/config-discovery.d.ts +2 -1
  22. package/dist/cli/doctor-rows.d.ts +3 -1
  23. package/dist/cli/doctor-rows.js +3 -1
  24. package/dist/cli/gate-exit.d.ts +21 -0
  25. package/dist/cli/gate-exit.js +21 -0
  26. package/dist/cli/gate-hook-message.d.ts +33 -0
  27. package/dist/cli/gate-hook-message.js +46 -0
  28. package/dist/cli/mcp-command.d.ts +25 -0
  29. package/dist/cli/mcp-command.js +143 -0
  30. package/dist/cli/sibling-ports.d.ts +37 -0
  31. package/dist/cli/sibling-ports.js +56 -0
  32. package/dist/cli.js +37 -110
  33. package/dist/cloud/cloud-config.d.ts +2 -1
  34. package/dist/cloud/cloud-sync.d.ts +11 -0
  35. package/dist/cloud/cloud-sync.js +32 -0
  36. package/dist/crawl/crawl.d.ts +9 -13
  37. package/dist/crawl/crawl.js +20 -3
  38. package/dist/daemon/daemon-resolve.d.ts +79 -0
  39. package/dist/daemon/daemon-resolve.js +118 -0
  40. package/dist/daemon/free-port.d.ts +29 -0
  41. package/dist/daemon/free-port.js +55 -0
  42. package/dist/daemon/heartbeat.d.ts +0 -4
  43. package/dist/daemon/heartbeat.js +2 -2
  44. package/dist/dev/stale-issue-guard.d.ts +0 -2
  45. package/dist/dev/stale-issue-guard.js +1 -1
  46. package/dist/events/contradictions.d.ts +41 -0
  47. package/dist/events/contradictions.js +125 -12
  48. package/dist/events/declared.d.ts +14 -0
  49. package/dist/events/declared.js +33 -0
  50. package/dist/events/echo-mismatch.js +14 -0
  51. package/dist/events/observed-in-window.d.ts +12 -0
  52. package/dist/events/observed-in-window.js +17 -0
  53. package/dist/events/predicate-asks.d.ts +13 -0
  54. package/dist/events/predicate-asks.js +22 -0
  55. package/dist/events/predicate-eval.js +64 -22
  56. package/dist/events/predicate-parse.d.ts +0 -6
  57. package/dist/events/predicate-parse.js +43 -3
  58. package/dist/events/predicate-schema.d.ts +44 -0
  59. package/dist/events/predicate-schema.js +126 -23
  60. package/dist/events/predicate.d.ts +15 -0
  61. package/dist/events/predicate.js +41 -2
  62. package/dist/flows/coverage.d.ts +0 -8
  63. package/dist/flows/coverage.js +1 -1
  64. package/dist/flows/decision.d.ts +5 -1
  65. package/dist/flows/decision.js +33 -7
  66. package/dist/flows/flake.d.ts +0 -2
  67. package/dist/flows/flake.js +1 -1
  68. package/dist/flows/flow-intent.d.ts +52 -0
  69. package/dist/flows/flow-intent.js +123 -0
  70. package/dist/flows/flow-replay-run.js +27 -4
  71. package/dist/flows/flow-success.js +15 -3
  72. package/dist/flows/flow-tools.js +32 -4
  73. package/dist/flows/flows.d.ts +6 -1
  74. package/dist/flows/flows.js +34 -22
  75. package/dist/flows/predicate-to-expect.js +6 -3
  76. package/dist/flows/server-verify.js +16 -2
  77. package/dist/flows/verify-change-tools.js +4 -1
  78. package/dist/honesty/blind-spots.d.ts +0 -17
  79. package/dist/honesty/blind-spots.js +1 -1
  80. package/dist/honesty/feature-capture.d.ts +150 -0
  81. package/dist/honesty/feature-capture.js +233 -0
  82. package/dist/honesty/instrumentation-gaps.d.ts +30 -6
  83. package/dist/honesty/instrumentation-gaps.js +61 -5
  84. package/dist/honesty/tool-hit-rate.d.ts +86 -0
  85. package/dist/honesty/tool-hit-rate.js +90 -0
  86. package/dist/honesty/undeclared-change.d.ts +51 -0
  87. package/dist/honesty/undeclared-change.js +56 -0
  88. package/dist/honesty/verified.d.ts +15 -0
  89. package/dist/honesty/verified.js +68 -4
  90. package/dist/http-server.d.ts +0 -1
  91. package/dist/http-server.js +1 -1
  92. package/dist/hunt/hunt-report.d.ts +0 -2
  93. package/dist/hunt/hunt-report.js +1 -1
  94. package/dist/impact/impact-store.d.ts +0 -5
  95. package/dist/impact/impact-store.js +1 -1
  96. package/dist/index.d.ts +4 -1
  97. package/dist/init/agent-rules.d.ts +17 -0
  98. package/dist/init/agent-rules.js +31 -1
  99. package/dist/init/capabilities.d.ts +0 -2
  100. package/dist/init/capabilities.js +1 -1
  101. package/dist/init/closing-hint.d.ts +36 -0
  102. package/dist/init/closing-hint.js +120 -0
  103. package/dist/init/confirm.d.ts +0 -25
  104. package/dist/init/confirm.js +3 -3
  105. package/dist/init/cra.d.ts +14 -1
  106. package/dist/init/cra.js +43 -12
  107. package/dist/init/dev-script.d.ts +48 -0
  108. package/dist/init/dev-script.js +63 -0
  109. package/dist/init/mcp.d.ts +16 -5
  110. package/dist/init/mcp.js +26 -10
  111. package/dist/init/plan-framework.d.ts +0 -5
  112. package/dist/init/plan-framework.js +83 -9
  113. package/dist/init/plan.d.ts +10 -30
  114. package/dist/init/plan.js +30 -15
  115. package/dist/init/refresh-rules.d.ts +39 -0
  116. package/dist/init/refresh-rules.js +51 -0
  117. package/dist/init/run.d.ts +0 -26
  118. package/dist/init/run.js +14 -87
  119. package/dist/init/snippets.js +53 -10
  120. package/dist/input/real-input.d.ts +2 -0
  121. package/dist/input/real-input.js +3 -2
  122. package/dist/input/scroll-find.d.ts +7 -4
  123. package/dist/input/scroll-find.js +63 -25
  124. package/dist/intent/inline-intent.d.ts +46 -0
  125. package/dist/intent/inline-intent.js +103 -0
  126. package/dist/intent/intent-store.d.ts +37 -0
  127. package/dist/intent/intent-store.js +112 -0
  128. package/dist/intent/intent-tools.d.ts +2 -0
  129. package/dist/intent/intent-tools.js +80 -0
  130. package/dist/intent/open-intents.d.ts +21 -0
  131. package/dist/intent/open-intents.js +25 -0
  132. package/dist/journal/envelope.d.ts +26 -26
  133. package/dist/journal/journal-recorder.d.ts +23 -0
  134. package/dist/journal/journal-recorder.js +35 -0
  135. package/dist/mcp/attach-memory.d.ts +67 -0
  136. package/dist/mcp/attach-memory.js +142 -0
  137. package/dist/mcp/mcp-proxy.d.ts +0 -7
  138. package/dist/mcp/mcp-proxy.js +14 -2
  139. package/dist/mcp/mcp.d.ts +2 -0
  140. package/dist/mcp/mcp.js +45 -23
  141. package/dist/mcp/server-instructions.js +45 -3
  142. package/dist/mcp/tool-catalog-cache.d.ts +8 -0
  143. package/dist/mcp/tool-catalog-cache.js +10 -0
  144. package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
  145. package/dist/oracles/flow-instrument-gaps.js +4 -5
  146. package/dist/oracles/self-instrument.d.ts +9 -9
  147. package/dist/oracles/self-instrument.js +19 -13
  148. package/dist/pool/browser-pool.d.ts +12 -1
  149. package/dist/pool/browser-pool.js +31 -1
  150. package/dist/pool/playwright-launcher.js +3 -1
  151. package/dist/project/fs-port.d.ts +3 -0
  152. package/dist/project/fs-port.js +3 -2
  153. package/dist/project/memory-fs.js +6 -1
  154. package/dist/project/project-tools.js +3 -32
  155. package/dist/project/reticle-dir.d.ts +2 -0
  156. package/dist/project/reticle-dir.js +1 -0
  157. package/dist/runs/context-tools.d.ts +2 -0
  158. package/dist/runs/context-tools.js +89 -0
  159. package/dist/runs/run-context.d.ts +40 -0
  160. package/dist/runs/run-context.js +158 -0
  161. package/dist/runs/run-diff.d.ts +2 -1
  162. package/dist/runs/verification-sync.js +2 -2
  163. package/dist/session/dev-server-probe.d.ts +0 -8
  164. package/dist/session/dev-server-probe.js +1 -1
  165. package/dist/session/last-act.d.ts +7 -1
  166. package/dist/session/last-act.js +2 -2
  167. package/dist/session/live-control-tools.js +1 -42
  168. package/dist/session/no-session-diagnosis.d.ts +10 -0
  169. package/dist/session/no-session-diagnosis.js +24 -7
  170. package/dist/session/no-session-next-action.d.ts +5 -0
  171. package/dist/session/no-session-next-action.js +35 -3
  172. package/dist/session/no-session-watch.d.ts +8 -0
  173. package/dist/session/no-session-watch.js +82 -64
  174. package/dist/session/output-budget.js +4 -1
  175. package/dist/session/session-health.d.ts +9 -0
  176. package/dist/session/session-health.js +23 -0
  177. package/dist/session/session-manager.js +50 -1
  178. package/dist/session/session-successor.d.ts +37 -0
  179. package/dist/session/session-successor.js +78 -0
  180. package/dist/session/session.d.ts +31 -1
  181. package/dist/session/session.js +75 -2
  182. package/dist/telemetry/automation-hint.d.ts +2 -1
  183. package/dist/telemetry/feedback-outbox.d.ts +2 -1
  184. package/dist/telemetry/project-profile.d.ts +0 -8
  185. package/dist/telemetry/project-profile.js +1 -1
  186. package/dist/telemetry/run-telemetry.d.ts +0 -2
  187. package/dist/telemetry/run-telemetry.js +1 -1
  188. package/dist/telemetry/verification-of.js +2 -2
  189. package/dist/tools/act-tools.js +126 -21
  190. package/dist/tools/assert-source.d.ts +22 -0
  191. package/dist/tools/assert-source.js +53 -0
  192. package/dist/tools/assert-verdict.d.ts +20 -3
  193. package/dist/tools/assert-verdict.js +36 -4
  194. package/dist/tools/browser-tools.js +0 -20
  195. package/dist/tools/complete-snapshot.d.ts +48 -0
  196. package/dist/tools/complete-snapshot.js +75 -0
  197. package/dist/tools/coverage-tools.d.ts +10 -1
  198. package/dist/tools/coverage-tools.js +123 -69
  199. package/dist/tools/dynamic-tools.js +29 -8
  200. package/dist/tools/error-recovery.d.ts +24 -14
  201. package/dist/tools/error-recovery.js +70 -9
  202. package/dist/tools/feedback-tools.d.ts +1 -1
  203. package/dist/tools/feedback-tools.js +2 -2
  204. package/dist/tools/invoke-tool.js +62 -2
  205. package/dist/tools/lease-hint.d.ts +10 -0
  206. package/dist/tools/lease-hint.js +53 -4
  207. package/dist/tools/lease-tools.d.ts +5 -1
  208. package/dist/tools/lease-tools.js +22 -8
  209. package/dist/tools/navigate-arrival.d.ts +0 -2
  210. package/dist/tools/navigate-arrival.js +1 -1
  211. package/dist/tools/observe-tools.js +63 -26
  212. package/dist/tools/read-tools.js +5 -3
  213. package/dist/tools/real-input-attempt.d.ts +22 -0
  214. package/dist/tools/real-input-attempt.js +153 -1
  215. package/dist/tools/reconcile-tools.js +18 -4
  216. package/dist/tools/resolve-target.d.ts +0 -7
  217. package/dist/tools/sdk-marker-probe.d.ts +29 -0
  218. package/dist/tools/sdk-marker-probe.js +57 -0
  219. package/dist/tools/snapshot-delta.d.ts +1 -1
  220. package/dist/tools/tool-kit.d.ts +12 -0
  221. package/dist/tools/tool-kit.js +16 -0
  222. package/dist/tools/tool-names.d.ts +3 -0
  223. package/dist/tools/tool-names.js +3 -0
  224. package/dist/tools/tool-surface.d.ts +85 -6
  225. package/dist/tools/tool-surface.js +122 -2
  226. package/dist/tools/tools.js +22 -4
  227. package/dist/tools/unadvertised-help.js +4 -4
  228. package/dist/tools/verify-next-baton.d.ts +53 -0
  229. package/dist/tools/verify-next-baton.js +52 -0
  230. package/dist/update/update-nudge.d.ts +7 -10
  231. package/dist/update/update-nudge.js +12 -3
  232. package/dist/update/updater.d.ts +1 -1
  233. package/docs/README.md +2 -1
  234. package/docs/cli/cloud.mdx +1 -1
  235. package/docs/cli/doctor.mdx +1 -0
  236. package/docs/cli/verify.mdx +4 -5
  237. package/docs/desktop.mdx +1 -1
  238. package/docs/docs.json +2 -2
  239. package/docs/gates.md +1 -1
  240. package/docs/install-manual.mdx +1 -1
  241. package/docs/packages/browser.mdx +2 -2
  242. package/docs/packages/server.mdx +1 -1
  243. package/docs/packages.mdx +8 -6
  244. package/docs/platform-integration.md +17 -0
  245. package/docs/predicates.mdx +8 -0
  246. package/docs/reticle-manual.md +106 -0
  247. package/docs/tools-tools-and-run.mdx +2 -4
  248. package/docs/troubleshooting.mdx +9 -0
  249. package/docs/usage.md +3 -2
  250. package/package.json +2 -2
  251. package/dist/session/session-readiness.d.ts +0 -34
  252. package/dist/session/session-readiness.js +0 -36
@@ -70,6 +70,45 @@ export const TOOL_SURFACE = {
70
70
  * supplies the evidence the surface would otherwise have to go and find.
71
71
  */
72
72
  VERIFY: 'verify',
73
+ /**
74
+ * EXPERIMENTAL, opt-in, and UNDER MEASUREMENT. Not a recommendation, and not on a path to becoming
75
+ * the default until it has a number of its own.
76
+ *
77
+ * It exists because of the finding recorded on VERIFY above, read forwards instead of backwards.
78
+ * That surface cut the bill 37% and TRIPLED false alarms, and the five new false alarms were not
79
+ * scattered: every one of them was a defect whose evidence lives in `reticle_state`,
80
+ * `reticle_network` or `reticle_observe`. So the token saving and the accuracy loss came from two
81
+ * different cuts, and only one of them has to be paid for. `lean` keeps every observation tool and
82
+ * cuts the rest.
83
+ *
84
+ * What stays, and why each one is not a candidate for removal:
85
+ * SNAPSHOT / QUERY look. Without them the agent cannot name an element it did not already know.
86
+ * STATE / NETWORK / CONSOLE / OBSERVE the four evidence tools. Dropping these is the MEASURED
87
+ * cause of the tripled false-alarm rate. They are the point of the profile.
88
+ * ACT_AND_WAIT the only tool that acts AND returns a verdict. Omitting `until` makes it
89
+ * act-then-settle, which is what `reticle_act` does, so `act` is redundant here.
90
+ * ASSERT the only way to get a verdict WITHOUT acting. `act_and_wait` requires an
91
+ * `action`, so this is a capability it genuinely cannot express, not a synonym.
92
+ *
93
+ * What goes, and what it costs (one `reticle_run` hop each — `unadvertisedToolHelp` hands the agent
94
+ * the exact call, so a dropped name never comes back as "not found"):
95
+ * ACT subsumed: `act_and_wait` with `until` omitted is act-then-settle.
96
+ * WAIT_FOR subsumed: `act_and_wait { until }` takes the same PredicateSchema.
97
+ * ACT_SEQUENCE batching is a cost optimisation on top of a surface built to be cheap.
98
+ * NAVIGATE once per run, not once per turn — the wrong thing to pay for every turn.
99
+ * SESSIONS the CLI answers this (`reticle status`) without spending any turn budget.
100
+ * INSPECT fix-side, not verdict-side: it is called once a bug is FOUND, so its hop is
101
+ * paid once per finding rather than once per turn. Nothing else maps a node to
102
+ * a source file, which is why it is a hop and not a deletion.
103
+ * SESSION the lease block and the pause hint name it in prose, which is what made it
104
+ * load-bearing on the default surface. Under a trimmed surface the
105
+ * unadvertised-tool help turns that into a working `reticle_run` call.
106
+ * FEEDBACK the one drop with a KNOWN CEILING: an unadvertised feedback channel collects
107
+ * little to nothing, so a long-lived profile must not ship without it. Acceptable
108
+ * only because this profile is opt-in and short-lived by construction; if it
109
+ * ever stops being an experiment, feedback comes back first.
110
+ */
111
+ LEAN: 'lean',
73
112
  };
74
113
  /**
75
114
  * The switch that turns on the full, output-schema-carrying surface. A boolean, not a menu.
@@ -79,7 +118,7 @@ export const TOOL_SURFACE = {
79
118
  */
80
119
  export const ADVERTISE_ALL_ENV = 'RETICLE_ADVERTISE_ALL_TOOLS';
81
120
  /** Opt into the smallest verdict-capable surface. Read by the DAEMON at startup, like the others. */
82
- export const VERIFY_SURFACE_ENV = 'RETICLE_VERIFY_SURFACE';
121
+ const VERIFY_SURFACE_ENV = 'RETICLE_VERIFY_SURFACE';
83
122
  /**
84
123
  * The retired setting, still read so nobody's shell profile breaks.
85
124
  *
@@ -185,6 +224,17 @@ export const CORE_TOOL_NAMES = new Set([
185
224
  * 48 by itself can push a user's other servers out or be dropped wholesale. The budget is a count,
186
225
  * so no amount of trimming parameter prose buys anything back — only advertising fewer names does.
187
226
  *
227
+ * That is true of the COUNT cap and false of everything else, which is worth separating here before
228
+ * somebody reads it as "prose is free". Measured against two competitor MCP servers on the same
229
+ * agent loop: this server advertises the FEWEST tools of the three and ships the HEAVIEST schema
230
+ * block, and 83% of that weight is parameter descriptions, not tool descriptions. The block is
231
+ * re-sent every turn, so it is multiplied by turn count before one byte of evidence is counted, and
232
+ * it was most of the gap in that run.
233
+ *
234
+ * Which does NOT license trimming by eye. Those descriptions are what make a tool get called
235
+ * correctly, and one malformed call costs a whole extra turn — more than the bytes saved. The
236
+ * outcome to measure is turn count and error rate, not size.
237
+ *
188
238
  * Everything omitted stays in the registry, stays catalogued by `reticle_tools`, and stays callable
189
239
  * by name through `reticle_run { tool, args }`. The cost is one discovery hop on the cold tail, which
190
240
  * is the same trade the default surface has always made, applied one level further out.
@@ -206,6 +256,13 @@ export const EXTENDED_TOOL_NAMES = new Set([
206
256
  ReticleTool.CAPABILITIES,
207
257
  ReticleTool.FLOW_SAVE,
208
258
  ReticleTool.FLOW_REPLAY,
259
+ // New and unproven, so it does not take a default-surface slot under the cap. Same terms the
260
+ // capabilities demotion set: reachable in one reticle_run hop by any agent that wants it.
261
+ ReticleTool.INTENT,
262
+ // Same terms again, plus one argument specific to it: its caller is BY CONSTRUCTION an agent that
263
+ // just lost its context and is re-reading the tool list, so the discovery hop it costs is a call
264
+ // that caller was already going to make. See context-tools.ts for the full argument.
265
+ ReticleTool.CONTEXT,
209
266
  ReticleTool.RECORD,
210
267
  ReticleTool.SCREENSHOT,
211
268
  ReticleTool.VISUAL_DIFF,
@@ -220,7 +277,53 @@ export const EXTENDED_TOOL_NAMES = new Set([
220
277
  * everything else. `act_and_wait` can resolve its own target, so no query tool is needed to name an
221
278
  * element — that round trip was half the token cost of a verification.
222
279
  */
223
- export const VERIFY_TOOL_NAMES = new Set([ReticleTool.ACT_AND_WAIT]);
280
+ const VERIFY_TOOL_NAMES = new Set([ReticleTool.ACT_AND_WAIT]);
281
+ /**
282
+ * The lean surface: look, observe, act-with-a-verdict, assert. Pinned by lean-surface.test.ts,
283
+ * because an experiment whose independent variable drifts mid-flight measures nothing. Every
284
+ * inclusion and every exclusion is argued at TOOL_SURFACE.LEAN.
285
+ *
286
+ * ## MEASURED: it is cheaper and less correct. Do not promote it to the default.
287
+ *
288
+ * Run as its own arm of the fix-and-verify benchmark — same five bugs, same model, same budget, the
289
+ * full surface as its control:
290
+ *
291
+ * | | full surface | lean |
292
+ * |---|---|---|
293
+ * | bugs fixed | **5/5** | **3/5** |
294
+ * | FALSE GREENS | **0** | **1** |
295
+ *
296
+ * The false green is the finding, and it is the first this benchmark has ever produced. On
297
+ * `broken-form-validation` the lean agent finished in SIX turns and 62k tokens — the cheapest cell
298
+ * of the whole run, a fifth of what the full surface spent — edited the file, took one snapshot,
299
+ * and ended with a hypothetical walkthrough: "Enter spaces + valid name -> spaces are trimmed,
300
+ * valid part is used. VERDICT: FIXED". The submit button was still enabled for a whitespace-only
301
+ * service. It reasoned about what its own code would now do instead of driving it, which is exactly
302
+ * the failure this product exists to catch, produced by our own surface.
303
+ *
304
+ * The other loss has the same root from the other side. On `cross-component-regression` the agent
305
+ * called `reticle_tools` three times and `reticle_run` eight, saying "since Reticle isn't
306
+ * connecting, let me see what reticle_tools offers" — it spent the run hunting for capabilities
307
+ * this surface does not advertise instead of using them, and never fixed the bug.
308
+ *
309
+ * That is the same result the `verify` surface already recorded — dropping the observation tools
310
+ * TRIPLED false alarms — arriving from the other direction: strip the surface and the agent stops
311
+ * verifying, then claims anyway. The known ceiling written here was that `reticle_feedback` goes
312
+ * uncollected. This is a bigger one and it was not predicted.
313
+ *
314
+ * It stays opt-in and it stays measured. The one thing it genuinely bought — the ceiling-bound cell
315
+ * halved, 30 turns to 14 — is not worth a verdict that lies, because the product is the verdict.
316
+ */
317
+ export const LEAN_TOOL_NAMES = new Set([
318
+ ReticleTool.SNAPSHOT,
319
+ ReticleTool.QUERY,
320
+ ReticleTool.ACT_AND_WAIT,
321
+ ReticleTool.ASSERT,
322
+ ReticleTool.OBSERVE,
323
+ ReticleTool.NETWORK,
324
+ ReticleTool.CONSOLE,
325
+ ReticleTool.STATE,
326
+ ]);
224
327
  /** Is the truthy form of a boolean env var set? `1`, `true`, `yes` — anything else is off. */
225
328
  function envFlagOn(raw) {
226
329
  if (raw === undefined)
@@ -235,6 +338,8 @@ function envFlagOn(raw) {
235
338
  * switch decides, and the retired setting is honoured last so an old shell profile still works.
236
339
  */
237
340
  export function resolveToolSurface(explicit) {
341
+ if (explicit === TOOL_SURFACE.LEAN)
342
+ return TOOL_SURFACE.LEAN;
238
343
  if (explicit === TOOL_SURFACE.VERIFY)
239
344
  return TOOL_SURFACE.VERIFY;
240
345
  if (explicit === TOOL_SURFACE.ALL)
@@ -244,6 +349,11 @@ export function resolveToolSurface(explicit) {
244
349
  const retiredExplicit = explicit === undefined ? undefined : RETIRED_PROFILE_VALUES[explicit];
245
350
  if (retiredExplicit !== undefined)
246
351
  return retiredExplicit;
352
+ // The one LIVE value of the otherwise-retired setting. It rides there rather than on a switch of
353
+ // its own because the experiment is an A/B between two surfaces, and a boolean cannot name an arm.
354
+ if (TOOL_SURFACE.LEAN === process.env[TOOL_PROFILE_ENV]?.trim().toLowerCase()) {
355
+ return TOOL_SURFACE.LEAN;
356
+ }
247
357
  if (envFlagOn(process.env[VERIFY_SURFACE_ENV]))
248
358
  return TOOL_SURFACE.VERIFY;
249
359
  if (envFlagOn(process.env[ADVERTISE_ALL_ENV]))
@@ -262,6 +372,14 @@ export function resolveToolSurface(explicit) {
262
372
  */
263
373
  export function describeToolSurface(active, requested) {
264
374
  const retired = requested ?? process.env[TOOL_PROFILE_ENV];
375
+ // `lean` is the one value of this setting that is NOT retired. Reporting it as retired would tell
376
+ // an agent its arm did not take, which on an A/B is worse than saying nothing.
377
+ if (TOOL_SURFACE.LEAN === active) {
378
+ return {
379
+ active,
380
+ source: `${TOOL_PROFILE_ENV}=${TOOL_SURFACE.LEAN} — an EXPERIMENTAL surface under measurement; unadvertised tools stay callable via ${ReticleTool.RUN}`,
381
+ };
382
+ }
265
383
  if (retired !== undefined && retired in RETIRED_PROFILE_VALUES) {
266
384
  return {
267
385
  active,
@@ -293,6 +411,8 @@ export function filterTools(tools, surface) {
293
411
  // interesting thing about the `core` PROFILE, whose only distinction was a second name for this.
294
412
  if (surface === TOOL_SURFACE.VERIFY)
295
413
  return tools.filter((t) => VERIFY_TOOL_NAMES.has(t.name));
414
+ if (surface === TOOL_SURFACE.LEAN)
415
+ return tools.filter((t) => LEAN_TOOL_NAMES.has(t.name));
296
416
  if (surface === TOOL_SURFACE.DEFAULT)
297
417
  return tools.filter((t) => CORE_TOOL_NAMES.has(t.name));
298
418
  // `all` is the extended surface, not the whole registry — the cap is a hard budget shared with
@@ -21,11 +21,13 @@ import { CONTRACT_TOOLS } from './contract-tools.js';
21
21
  import { DOMAIN_TOOLS } from '../domain/domain-tools.js';
22
22
  import { BROWSER_TOOLS } from './browser-tools.js';
23
23
  import { FLOW_TOOLS } from '../flows/flow-tools.js';
24
+ import { INTENT_TOOLS } from '../intent/intent-tools.js';
25
+ import { CONTEXT_TOOLS } from '../runs/context-tools.js';
24
26
  import { PROJECT_TOOLS } from '../project/project-tools.js';
25
27
  import { RUN_TOOLS } from '../runs/run-tools.js';
26
28
  import { VISUAL_TOOLS } from '../visual/visual-tools.js';
27
29
  import { AFFECTED_TOOLS } from '../flows/affected-tools.js';
28
- import { COVERAGE_TOOLS } from './coverage-tools.js';
30
+ import { buildCoverageTools } from './coverage-tools.js';
29
31
  import { VERIFY_CHANGE_TOOLS } from '../flows/verify-change-tools.js';
30
32
  import { CRAWL_TOOLS } from '../crawl/crawl-tools.js';
31
33
  import { SCROLL_TOOLS } from '../input/scroll-tools.js';
@@ -47,7 +49,7 @@ const SNAPSHOT_CACHE = new SnapshotCache();
47
49
  const RAW_TOOLS = [
48
50
  {
49
51
  name: ReticleTool.SESSIONS,
50
- description: 'List connected browser sessions (tab url/title, sessionId, last-seen, health: hidden/focused/throttled, and `realInputAvailable` — true when native CDP/launched real input is driving this tab), plus a `recommendation` pointing to `reticle drive` when a tab is hidden/throttled and may be un-scriptable from here.',
52
+ description: 'List connected browser sessions (tab url/title, sessionId, last-seen, health: hidden/focused/throttled, and `realInputAvailable` — true when native CDP/launched real input is driving this tab), plus a `recommendation` naming the in-protocol escape hatch (`reticle_run { tool: "reticle_lease" }`, with `reticle drive` as the human-side equivalent) when a tab is hidden/throttled and may be un-scriptable from here.',
51
53
  inputSchema: {},
52
54
  outputSchema: {
53
55
  sessions: z
@@ -76,6 +78,18 @@ const RAW_TOOLS = [
76
78
  pendingMarks: z.number().optional(),
77
79
  review_suggestion: z.string().optional(),
78
80
  recommendation: z.string().optional(),
81
+ // Attached by SessionManager.list() (#117): whether this tab ever dropped and came back,
82
+ // how often, and how long the last drop lasted. Undeclared here, a validating client
83
+ // stripped the outage history while the row still looked complete — a verdict over a
84
+ // window with a four-second blind gap read as trustworthy.
85
+ attachment: z
86
+ .object({
87
+ connectedSinceMs: z.number(),
88
+ outages: z.number(),
89
+ lastOutage: z.object({ startedMs: z.number(), durationMs: z.number() }).optional(),
90
+ })
91
+ .optional()
92
+ .describe('Present only when this tab has dropped and reconnected at least once: how long it has been attached in total, how many outages occurred, and when the last one started and how long it lasted. A verdict over this window spans a gap where nothing was observed.'),
79
93
  }))
80
94
  .describe('Connected browser sessions with health state. `projectId` groups sessions by app (stable across port changes); `leased` marks a pool-managed headless context vs a human tab.'),
81
95
  why: z
@@ -218,7 +232,7 @@ const RAW_TOOLS = [
218
232
  {
219
233
  name: ReticleTool.QUERY,
220
234
  example: { by: 'testid', value: 'todo-list' },
221
- description: `Find elements by Testing-Library semantics, INCLUDING open shadow roots — \`count_only:true\` gives just the count (~30x smaller); \`limit\` caps descriptors. Pass \`by\` (${QUERY_BY_LIST}) and \`value\` (the query string). Returns matching refs + descriptors + visibility. Pass \`attrs:["href"]\` to project attributes (link/image URLs) onto each match. Pass \`limit\` to cap descriptors (broad role queries can be large) or \`count_only:true\` for just the match count — both cut tokens. On zero matches, also returns hint:{ route, presentRegions[], knownEmptyState } so you can distinguish an empty state from a missing element WITHOUT taking a snapshot.`,
235
+ description: `Find elements by Testing-Library semantics, INCLUDING open shadow roots — \`count_only:true\` gives just the count (~30x smaller); \`limit\` caps descriptors. Pass \`by\` (${QUERY_BY_LIST}) and \`value\` (the query string). Returns matching refs + descriptors + visibility. Pass \`attrs:["href"]\` to project attributes (link/image URLs) onto each match. Pass \`limit\` to cap descriptors (broad role queries can be large) or \`count_only:true\` for just the match count — both cut tokens. On zero matches, also returns hint:{ route, presentRegions[], knownEmptyState } so you can distinguish an empty state from a missing element WITHOUT taking a snapshot. ASKING SEVERAL QUESTIONS ABOUT THE PAGE? One reticle_snapshot answers them all at once — a run of queries costs a round trip each and returns what the snapshot already held.`,
222
236
  inputSchema: {
223
237
  // Constrained to the enum, NOT z.string(). A free string let `by:'css'` through to the
224
238
  // browser's `default: return []`, so an unsupported strategy answered "0 matches" — which
@@ -446,6 +460,8 @@ const RAW_TOOLS = [
446
460
  // reticle_capabilities (live | fromDisk) + reticle_contract_save. See contract-tools.ts.
447
461
  ...RECONCILE_TOOLS,
448
462
  ...CONTRACT_TOOLS,
463
+ ...INTENT_TOOLS,
464
+ ...CONTEXT_TOOLS,
449
465
  ...DOMAIN_TOOLS,
450
466
  // reticle_flow_save / reticle_flow_list / reticle_flow_load. See flow-tools.ts.
451
467
  ...FLOW_TOOLS,
@@ -469,7 +485,9 @@ const RAW_TOOLS = [
469
485
  // reachable through reticle_run. See affected-tools.ts.
470
486
  ...AFFECTED_TOOLS,
471
487
  // reticle_coverage — which controls were driven vs never touched. Unadvertised; via reticle_run.
472
- ...COVERAGE_TOOLS,
488
+ // The table is passed as a thunk so the coverage tool can report which of it was called without
489
+ // importing it back — see buildCoverageTools. Called from the handler, long after TOOLS exists.
490
+ ...buildCoverageTools(() => TOOLS.map((t) => t.name)),
473
491
  // reticle_verify_change — "did my change break anything" in one call. See verify-change-tools.ts.
474
492
  ...VERIFY_CHANGE_TOOLS,
475
493
  // Live-control: reticle_end_session / reticle_resume / reticle_messages. See live-control-tools.ts.
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * What to say when an agent calls a real Reticle tool that this profile does not advertise.
3
3
  *
4
- * The default surface advertises 18 of 48 tools, and the extended one 30. That is deliberate — every
5
- * advertised tool's schema is re-sent on every turn, and editors budget MCP tools as a COUNT shared
6
- * across every connected server — and everything omitted stays fully callable through `reticle_run`,
7
- * which `surface-reachability.test.ts` guards.
4
+ * The default surface is the verify loop plus the two meta-tools; the extended one is larger but
5
+ * still capped. Counts live in `surface-sizes.test.ts` — restating them here is how they drift.
6
+ * Everything omitted stays fully callable through `reticle_run`, which `surface-reachability.test.ts`
7
+ * guards.
8
8
  *
9
9
  * The gap was in the ERROR. The MCP SDK answers `Tool <name> not found`, which is indistinguishable
10
10
  * from "this tool does not exist", so an agent that trusts it stops trying. Reported from a real
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The verdict nudge, as a call the agent can make rather than a sentence it has to translate.
3
+ *
4
+ * `verify_next` already fires when an agent has driven the page several times and asked for no
5
+ * verdict, which is the commonest shape of a wasted session: almost every verdict-less session in
6
+ * the field never called a verdict-producing tool once. What it carried was prose. Prose has to be
7
+ * turned back into a call, and the translation is exactly where the agent was already going wrong —
8
+ * `until` omitted, arguments flat instead of nested under `args`, a predicate written as a bare
9
+ * array.
10
+ *
11
+ * So it carries the call. `ref` and `action` come from the act that actually dispatched, so the
12
+ * suggestion is about the element the agent really touched rather than a worked example from a
13
+ * document. `until` is deliberately left as a placeholder the agent must replace: naming the
14
+ * consequence is the one part nothing but the agent can know, and filling it in with a guess would
15
+ * be Reticle inventing the assertion — the failure `no-fault` exists to prevent.
16
+ *
17
+ * Emitted only when there is a real call to suggest. A baton with nothing behind it is the router
18
+ * failure in miniature: an agent that follows a fabricated next step follows it confidently.
19
+ */
20
+ /**
21
+ * Set to `1` to withhold the baton from tool results — the control arm of its A/B, and nothing else.
22
+ *
23
+ * A measurement flag, not a product setting: it is read at one call site, documented here, and named
24
+ * loudly enough that nobody sets it by accident. It exists because this feature has shipped, been
25
+ * declared the largest lever on whether a session verifies at all, and never been measured — and a
26
+ * control arm is the only way to tell "agents that see it verify" from "agents that were going to
27
+ * verify anyway also saw it".
28
+ */
29
+ export declare const SUPPRESS_VERIFY_NEXT_ENV = "RETICLE_SUPPRESS_VERIFY_NEXT";
30
+ /** What the agent must supply itself. Phrased as an instruction so it cannot be sent verbatim. */
31
+ export declare const UNTIL_PLACEHOLDER = "<name the consequence this action causes>";
32
+ export interface VerifyNextBaton {
33
+ /** The fact that produced this, in the agent's own terms. */
34
+ why: string;
35
+ /** A call, ready to make once `until` is filled in. */
36
+ call?: {
37
+ tool: string;
38
+ args: Record<string, unknown>;
39
+ };
40
+ /** Where the argument shapes are stated, on disk and version-exact rather than on the web. */
41
+ docs?: string;
42
+ }
43
+ /**
44
+ * Build the baton from what the session actually did.
45
+ *
46
+ * `ref` absent means no act dispatched under a ref this session — a navigation, or a run that only
47
+ * read. There is then no honest call to name, so the prose stands alone rather than pointing at an
48
+ * element nobody touched.
49
+ */
50
+ export declare function verifyNextBaton(prose: string, lastAct: {
51
+ action?: string | undefined;
52
+ ref?: string | undefined;
53
+ }): VerifyNextBaton;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The verdict nudge, as a call the agent can make rather than a sentence it has to translate.
3
+ *
4
+ * `verify_next` already fires when an agent has driven the page several times and asked for no
5
+ * verdict, which is the commonest shape of a wasted session: almost every verdict-less session in
6
+ * the field never called a verdict-producing tool once. What it carried was prose. Prose has to be
7
+ * turned back into a call, and the translation is exactly where the agent was already going wrong —
8
+ * `until` omitted, arguments flat instead of nested under `args`, a predicate written as a bare
9
+ * array.
10
+ *
11
+ * So it carries the call. `ref` and `action` come from the act that actually dispatched, so the
12
+ * suggestion is about the element the agent really touched rather than a worked example from a
13
+ * document. `until` is deliberately left as a placeholder the agent must replace: naming the
14
+ * consequence is the one part nothing but the agent can know, and filling it in with a guess would
15
+ * be Reticle inventing the assertion — the failure `no-fault` exists to prevent.
16
+ *
17
+ * Emitted only when there is a real call to suggest. A baton with nothing behind it is the router
18
+ * failure in miniature: an agent that follows a fabricated next step follows it confidently.
19
+ */
20
+ import { ReticleTool } from './tool-names.js';
21
+ /**
22
+ * Set to `1` to withhold the baton from tool results — the control arm of its A/B, and nothing else.
23
+ *
24
+ * A measurement flag, not a product setting: it is read at one call site, documented here, and named
25
+ * loudly enough that nobody sets it by accident. It exists because this feature has shipped, been
26
+ * declared the largest lever on whether a session verifies at all, and never been measured — and a
27
+ * control arm is the only way to tell "agents that see it verify" from "agents that were going to
28
+ * verify anyway also saw it".
29
+ */
30
+ export const SUPPRESS_VERIFY_NEXT_ENV = 'RETICLE_SUPPRESS_VERIFY_NEXT';
31
+ /** What the agent must supply itself. Phrased as an instruction so it cannot be sent verbatim. */
32
+ export const UNTIL_PLACEHOLDER = '<name the consequence this action causes>';
33
+ /**
34
+ * Build the baton from what the session actually did.
35
+ *
36
+ * `ref` absent means no act dispatched under a ref this session — a navigation, or a run that only
37
+ * read. There is then no honest call to name, so the prose stands alone rather than pointing at an
38
+ * element nobody touched.
39
+ */
40
+ export function verifyNextBaton(prose, lastAct) {
41
+ const { ref, action } = lastAct;
42
+ if (ref === undefined || action === undefined)
43
+ return { why: prose };
44
+ return {
45
+ why: prose,
46
+ call: {
47
+ tool: ReticleTool.ACT_AND_WAIT,
48
+ args: { ref, action, until: { kind: 'text', value: UNTIL_PLACEHOLDER } },
49
+ },
50
+ docs: `reticle_tools { names: ["${ReticleTool.ACT_AND_WAIT}"] }`,
51
+ };
52
+ }
@@ -1,13 +1,3 @@
1
- /**
2
- * Is `candidate` a strictly NEWER release than `current`?
3
- *
4
- * Replaces a plain `!==`, which told anyone on a version newer than the published one — a
5
- * prerelease, a local build, a rollback in progress — to "update" to something older. A nudge that
6
- * prompts a downgrade is one people learn to ignore, and this is the mechanism the entire adoption
7
- * story rests on. Numeric per segment, so 2.10.0 correctly beats 2.9.0 (string order does not), and
8
- * a bare release beats its own prerelease.
9
- */
10
- export declare function isNewerVersion(candidate: string, current: string): boolean;
11
1
  interface UpdateNudge {
12
2
  /** The version available, so the agent can tell the human what they would be moving to. */
13
3
  latestVersion: string;
@@ -30,6 +20,13 @@ interface UpdateNudge {
30
20
  *
31
21
  * Bounded on purpose: this rides on a tool result every turn until it is delivered, so a long
32
22
  * changelog is a per-turn tax. The count of anything elided is stated rather than silently dropped.
23
+ *
24
+ * It also names what `update` does to the RULE FILES, which is the half nobody would guess. A new
25
+ * release changes what the always-loaded instructions should say — that is the whole reason
26
+ * `refreshAgentRules` exists — and an agent that reads "it restarts the daemon" has no reason to
27
+ * think its CLAUDE.md is now a release behind. Saying so costs one clause on a message that is
28
+ * delivered once per daemon, and it is the difference between a fleet that upgrades its rules and
29
+ * one that upgrades its binary and keeps last release's instructions forever.
33
30
  */
34
31
  export declare function buildNudge(latestVersion: string, currentVersion: string, release?: {
35
32
  changelog?: string;
@@ -29,7 +29,7 @@ import { creditNudge } from './nudge-credit.js';
29
29
  * story rests on. Numeric per segment, so 2.10.0 correctly beats 2.9.0 (string order does not), and
30
30
  * a bare release beats its own prerelease.
31
31
  */
32
- export function isNewerVersion(candidate, current) {
32
+ function isNewerVersion(candidate, current) {
33
33
  const parse = (v) => {
34
34
  const [core = '', ...rest] = v.split(/[-+]/);
35
35
  return { parts: core.split('.').map((n) => Number.parseInt(n, 10) || 0), pre: rest.length > 0 };
@@ -72,6 +72,13 @@ const MAX_LISTED_BREAKING = 4;
72
72
  *
73
73
  * Bounded on purpose: this rides on a tool result every turn until it is delivered, so a long
74
74
  * changelog is a per-turn tax. The count of anything elided is stated rather than silently dropped.
75
+ *
76
+ * It also names what `update` does to the RULE FILES, which is the half nobody would guess. A new
77
+ * release changes what the always-loaded instructions should say — that is the whole reason
78
+ * `refreshAgentRules` exists — and an agent that reads "it restarts the daemon" has no reason to
79
+ * think its CLAUDE.md is now a release behind. Saying so costs one clause on a message that is
80
+ * delivered once per daemon, and it is the difference between a fleet that upgrades its rules and
81
+ * one that upgrades its binary and keeps last release's instructions forever.
75
82
  */
76
83
  export function buildNudge(latestVersion, currentVersion, release) {
77
84
  const changelog = (release?.changelog ?? '').trim();
@@ -89,8 +96,10 @@ export function buildNudge(latestVersion, currentVersion, release) {
89
96
  : `This release has BREAKING changes: ${listed.join('; ')}` +
90
97
  (0 < rest ? ` (+${String(rest)} more — see the changelog)` : '') +
91
98
  '. Read them before updating. ') +
92
- `Tell the human, and run \`${UPDATE_COMMAND}\` if they agree — it restarts the daemon, ` +
93
- 'so do it between tasks rather than mid-verification. Continue your current task first.',
99
+ `Tell the human, and run \`${UPDATE_COMMAND}\` if they agree — it upgrades the packages, ` +
100
+ "refreshes the Reticle rules in this project's CLAUDE.md / AGENTS.md, and restarts the " +
101
+ 'daemon, so do it between tasks rather than mid-verification. Continue your current task ' +
102
+ 'first.',
94
103
  };
95
104
  }
96
105
  export function startUpdateCheck(now = () => Date.now()) {
@@ -3,7 +3,7 @@ declare const VersionChangeDirection: {
3
3
  readonly UPDATE: "update";
4
4
  readonly ROLLBACK: "rollback";
5
5
  };
6
- export type VersionChangeDirection = (typeof VersionChangeDirection)[keyof typeof VersionChangeDirection];
6
+ type VersionChangeDirection = (typeof VersionChangeDirection)[keyof typeof VersionChangeDirection];
7
7
  /**
8
8
  * Report a completed version move. Never throws — an install that worked must still be reported done.
9
9
  *
package/docs/README.md CHANGED
@@ -26,7 +26,7 @@ Everything here is published to [docs.reticle.sh](https://docs.reticle.sh) by `d
26
26
  | [faq.mdx](faq.mdx) | production, frameworks, comparisons, and the honest limits |
27
27
  | [tools-overview.mdx](tools-overview.mdx) | the 18 advertised tools, the 30 in the cold tail, and why |
28
28
  | [tools-snapshot.mdx](tools-snapshot.mdx) | `reticle_snapshot`: three modes, from full tree to a 25-token route check |
29
- | [tools-query.mdx](tools-query.mdx) | `reticle_query` finds elements by testing-library semantics |
29
+ | [tools-query.mdx](tools-query.mdx) | `reticle_query` finds elements by their reported roles, names and labels |
30
30
  | [tools-inspect.mdx](tools-inspect.mdx) | `reticle_inspect`: one element, down to source and design tokens |
31
31
  | [tools-navigate.mdx](tools-navigate.mdx) | `reticle_navigate`, and why `ok` does not mean the page arrived |
32
32
  | [tools-act.mdx](tools-act.mdx) | `reticle_act` acts, proves nothing, and says so |
@@ -46,6 +46,7 @@ Everything here is published to [docs.reticle.sh](https://docs.reticle.sh) by `d
46
46
  | [best-practices.mdx](best-practices.mdx) | the habits that make a verdict worth trusting |
47
47
  | [skill-file.mdx](skill-file.mdx) | the paste-one-URL skill that teaches an agent Reticle |
48
48
  | [agent-cheatsheet.md](agent-cheatsheet.md) | the condensed version an agent keeps in context |
49
+ | [reticle-manual.md](reticle-manual.md) | GENERATED: what each tool actually returned when it was called, per argument shape |
49
50
  | [for-agents.md](for-agents.md) | how to fetch these docs as Markdown or `llms.txt` |
50
51
  | [architecture.md](architecture.md) | how it works, and why it is built this way |
51
52
  | [platform-integration.md](platform-integration.md) | Vite, Next, Remix, Astro, plain HTML |
@@ -20,7 +20,7 @@ These are thin clients over the `/v1` API. The moat is the server, not the 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`.
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.
24
24
 
25
25
  ## The commands
26
26
 
@@ -53,6 +53,7 @@ When nothing has connected, the `sessions` line fails and the daemon's own diagn
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
  | `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 |
55
55
  | `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 |
56
+ | `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 |
56
57
  | `daemon log` | Where the structured daemon log lives |
57
58
  | `tracing` | How to turn that log into a per-stage trace |
58
59
  | `desktop` | Only on a desktop project. Every Electron and Tauri misconfiguration here fails silently, so it is diagnosed explicitly |
@@ -50,11 +50,10 @@ Call log:
50
50
 
51
51
  <Warning>
52
52
  `verify` binds the bridge port itself and has **no `--port` flag**: passing one is rejected as
53
- `unknown argument '--port'`. If a daemon is already on the port, `verify` dies with a raw `Error:
54
- listen EADDRINUSE: address already in use 127.0.0.1:4400` stack trace before it drives anything.
55
- Stop the daemon first, or move `verify` with the `RETICLE_PORT` environment variable. This bites
56
- most often on a developer machine, where an agent has usually left a daemon running; in CI the
57
- port is normally free.
53
+ `unknown argument '--port'`. If a daemon is already on the port, `verify` does not start a second
54
+ one: it prints three ways out (ask the running daemon via `reticle_run`, stop it, or move with
55
+ `RETICLE_PORT`) rather than a raw listen error. This bites most often on a developer machine,
56
+ where an agent has usually left a daemon running; in CI the port is normally free.
58
57
  </Warning>
59
58
 
60
59
  ## Exit codes
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 = "0.1"
217
+ reticle-tauri = "2.12"
218
218
  ```
219
219
 
220
220
  ```rust
package/docs/docs.json CHANGED
@@ -32,7 +32,7 @@
32
32
  "navigation": {
33
33
  "versions": [
34
34
  {
35
- "version": "2.11.0",
35
+ "version": "2.12.0",
36
36
  "default": true,
37
37
  "tabs": [
38
38
  {
@@ -153,7 +153,7 @@
153
153
  },
154
154
  {
155
155
  "group": "Long form",
156
- "pages": ["agent-cheatsheet", "usage", "faq"]
156
+ "pages": ["agent-cheatsheet", "reticle-manual", "usage", "faq"]
157
157
  }
158
158
  ]
159
159
  },
package/docs/gates.md CHANGED
@@ -69,7 +69,7 @@ Each gate exists because the ones above it are blind to something. That blindnes
69
69
  | **Integration** | `pnpm test:integration` | real headless Chromium: browser pool, crash isolation, framework adapters, `withReticle` | the MCP surface, the daemon | `e2e` |
70
70
  | **Web e2e battery** | `pnpm test:e2e` | 32 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` |
71
71
  | **Desktop battery** | `pnpm test:e2e:desktop` | a real Electron main process and a **packaged** Tauri binary, driven headless | web-only paths | `desktop-e2e` |
72
- | **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` |
72
+ | **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 that advertised capabilities | install _complexity_; see [`fixtures.md`](./fixtures.md) | `install-gate` |
73
73
  | **Matrix records** | `pnpm matrix:validate` | every submitted client-compat record is well-formed | whether the client actually works | `matrix-records` |
74
74
  | **Windows** | (CI only) | that the code runs at all on the majority platform | e2e; Windows is unit-only | `windows` |
75
75
  | **Rust** | `cargo fmt/clippy/check` | `packages/tauri` compiles and lints on Linux, macOS, and cross-checks Windows | everything JS | `rust`, `rust-macos` |
@@ -159,7 +159,7 @@ Screenshots and headless mode need the Rust crate, which is versioned independen
159
159
 
160
160
  ```toml
161
161
  [dependencies]
162
- reticle-tauri = "0.1"
162
+ reticle-tauri = "2.12"
163
163
  ```
164
164
 
165
165
  ```rust
@@ -6,13 +6,13 @@ icon: window
6
6
 
7
7
  `@reticlehq/browser` is the Reticle SDK that runs inside your page. It installs the observers, builds semantic snapshots, executes actions, and talks to the bridge. Install it directly only if you are **not** on React or want to call `connect()` yourself, because [`@reticlehq/react`](/packages/react) already re-exports all of it.
8
8
 
9
- **Apache 2.0. Depends on `@reticlehq/core` and `@testing-library/dom`.**
9
+ **Apache 2.0. Depends on `@reticlehq/core`.**
10
10
 
11
11
  ## Why it exists
12
12
 
13
13
  This is what makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel. It never imports a Node API; that boundary is enforced by the dependency graph.
14
14
 
15
- `@testing-library/dom` is why [`reticle_query`](/tools-query) speaks role, label and testid instead of CSS selectors.
15
+ [`reticle_query`](/tools-query) speaks role, label and testid instead of CSS selectors through its own local accessibility engine: one engine computes both what elements are called and what they match by, so a reported identity always round-trips.
16
16
 
17
17
  ## When you need it directly
18
18
 
@@ -64,7 +64,7 @@ await server.close();
64
64
  | `reticleRoot` | `string` | `cwd()/.reticle` |
65
65
  | `pairingTokenDir` | `string` | `~/.reticle` |
66
66
  | `now` | `() => number` | `Date.now` |
67
- | `toolProfile` | `string` | `RETICLE_TOOL_PROFILE`, else `full`. `core` serves the lean surface |
67
+ | `toolProfile` | `string` | retired names (`core`/`full`/…) still map. Live switch is `RETICLE_ADVERTISE_ALL_TOOLS=1`. Default is the lean surface |
68
68
  | `httpVerify` | `boolean` | `false` |
69
69
  | `httpVerifyPort` | `number` | `RETICLE_VERIFY_DEFAULT_PORT` (`7331`) |
70
70
  | `httpVerifyToken` | `string` | `RETICLE_VERIFY_TOKEN` |
package/docs/packages.mdx CHANGED
@@ -8,9 +8,11 @@ icon: cubes
8
8
  Reticle is eleven packages, and most people install two. This page exists so you can tell which two, and so that the ones you skip stop looking mysterious.
9
9
 
10
10
  <Note>
11
- The ten npm packages ship together at one version: whatever `npm view @reticlehq/core version`
12
- says, every other `@reticlehq/*` package says too. The Rust crate `reticle-tauri` is versioned
13
- **independently**. That is deliberate, not drift.
11
+ Everything ships together at one version: whatever `npm view @reticlehq/core version` says, every
12
+ other `@reticlehq/*` package says too, and so does the `reticle-tauri` crate on crates.io.
13
+
14
+ It was versioned independently until 2.12.0, and that turned out to mean _stuck_: the crate sat at `0.1.0` from the day it was written, the publish job skipped it every release because that version already existed, and no Rust-side fix reached anybody. Lockstep costs one line in the bump script and cannot fail that way.
15
+
14
16
  </Note>
15
17
 
16
18
  ## Which ones do I need?
@@ -70,7 +72,7 @@ Those rules are why the browser SDK cannot accidentally pull in a filesystem cal
70
72
 
71
73
  **Why it exists:** this is the part that makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel.
72
74
 
73
- **Depends on:** `@reticlehq/core`, `@testing-library/dom`. The latter is why [`reticle_query`](/tools-query) speaks role, label and testid rather than CSS selectors.
75
+ **Depends on:** `@reticlehq/core`. [`reticle_query`](/tools-query) speaks role, label and testid rather than CSS selectors through its own local accessibility engine, so what a snapshot reports is exactly what a query matches.
74
76
 
75
77
  **Install it directly when** you are not on React, or you want `connect()` under your own control:
76
78
 
@@ -199,13 +201,13 @@ A lint rule makes the signal layer self-enforcing, which is the only way a conve
199
201
 
200
202
  ## reticle-tauri
201
203
 
202
- **The Rust crate**, on crates.io. Versioned independently of the npm packages.
204
+ **The Rust crate**, on crates.io. Versioned in lockstep with the npm packages, so `2.11` here and `2.11` there mean the same release.
203
205
 
204
206
  **Why it exists:** Tauri IPC observation needs nothing on the Rust side; an `invoke('load_todos')` already reaches Reticle as `ipc://load_todos`. You only need this crate for screenshots and headless mode.
205
207
 
206
208
  ```toml
207
209
  [dependencies]
208
- reticle-tauri = "0.1"
210
+ reticle-tauri = "2.12"
209
211
  ```
210
212
 
211
213
  <Warning>