@reticlehq/server 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) hide show
  1. package/SKILL.md +702 -0
  2. package/dist/bridge/auth-failure-reason.d.ts +19 -0
  3. package/dist/bridge/auth-failure-reason.js +29 -0
  4. package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
  5. package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
  6. package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +105 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
  10. package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
  11. package/dist/capsule/capsule-store.d.ts +8 -3
  12. package/dist/capsule/capsule-store.js +7 -2
  13. package/dist/capsule/capsule.js +3 -3
  14. package/dist/capsule/causal-summary.d.ts +8 -0
  15. package/dist/capsule/causal-summary.js +22 -12
  16. package/dist/capsule/divergence.d.ts +9 -4
  17. package/dist/capsule/divergence.js +8 -8
  18. package/dist/capsule/predicate-to-links.js +11 -8
  19. package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
  20. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  21. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  22. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  23. package/dist/cli/cli-launch.js +171 -0
  24. package/dist/cli/cli-parse.d.ts +143 -0
  25. package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
  26. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
  27. package/dist/{cli-port.js → cli/cli-port.js} +29 -8
  28. package/dist/cli/cli-update-commands.d.ts +4 -0
  29. package/dist/cli/cli-update-commands.js +93 -0
  30. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  31. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  32. package/dist/cli.d.ts +2 -2
  33. package/dist/cli.js +182 -93
  34. package/dist/cloud/cloud-config.d.ts +0 -1
  35. package/dist/cloud/cloud-config.js +9 -9
  36. package/dist/cloud/cloud-sync.d.ts +1 -7
  37. package/dist/cloud/cloud-sync.js +12 -12
  38. package/dist/crawl/crawl-empty.d.ts +16 -0
  39. package/dist/crawl/crawl-empty.js +24 -0
  40. package/dist/crawl/crawl-tools.js +4 -1
  41. package/dist/crawl/crawl.js +26 -7
  42. package/dist/daemon/daemon-resilience.d.ts +49 -0
  43. package/dist/daemon/daemon-resilience.js +191 -0
  44. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  45. package/dist/daemon/daemon-usefulness.js +62 -0
  46. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  47. package/dist/{daemon.js → daemon/daemon.js} +80 -48
  48. package/dist/daemon/idle-grace.d.ts +32 -0
  49. package/dist/daemon/idle-grace.js +41 -0
  50. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
  51. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
  52. package/dist/domain/domain-model.js +2 -2
  53. package/dist/events/body-failures.js +8 -8
  54. package/dist/events/contradictions.d.ts +1 -1
  55. package/dist/events/contradictions.js +60 -10
  56. package/dist/events/echo-mismatch.js +10 -10
  57. package/dist/events/event-filters.js +6 -6
  58. package/dist/events/json-salvage.d.ts +2 -1
  59. package/dist/events/json-salvage.js +6 -6
  60. package/dist/events/observed-in-window.d.ts +12 -0
  61. package/dist/events/observed-in-window.js +21 -0
  62. package/dist/events/predicate-eval.d.ts +43 -20
  63. package/dist/events/predicate-eval.js +159 -55
  64. package/dist/events/predicate.d.ts +2 -2
  65. package/dist/events/predicate.js +112 -35
  66. package/dist/events/reconcile.js +5 -5
  67. package/dist/events/ring-buffer.js +1 -1
  68. package/dist/events/stale-response.js +6 -6
  69. package/dist/events/testid-near-miss.d.ts +22 -0
  70. package/dist/events/testid-near-miss.js +33 -0
  71. package/dist/events/unit-mismatch.js +8 -8
  72. package/dist/flows/affected-tools.js +19 -4
  73. package/dist/flows/affected.js +1 -1
  74. package/dist/flows/annotate-target.d.ts +1 -0
  75. package/dist/flows/annotate-target.js +20 -0
  76. package/dist/flows/annotate-tools.js +25 -3
  77. package/dist/flows/annotate.js +17 -4
  78. package/dist/flows/assertion-integrity.d.ts +2 -1
  79. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  80. package/dist/flows/assertion-tiers-store.js +2 -2
  81. package/dist/flows/attributed-failure.d.ts +17 -0
  82. package/dist/flows/attributed-failure.js +20 -0
  83. package/dist/flows/buddy-status.d.ts +2 -1
  84. package/dist/flows/buddy-status.js +2 -2
  85. package/dist/flows/coverage.d.ts +3 -2
  86. package/dist/flows/coverage.js +2 -2
  87. package/dist/flows/decision.d.ts +0 -6
  88. package/dist/flows/decision.js +64 -8
  89. package/dist/flows/empty-flow.d.ts +6 -0
  90. package/dist/flows/empty-flow.js +24 -0
  91. package/dist/flows/flake-store.d.ts +0 -1
  92. package/dist/flows/flake-store.js +1 -1
  93. package/dist/flows/flake.d.ts +2 -1
  94. package/dist/flows/flake.js +2 -2
  95. package/dist/flows/flow-classify.js +12 -0
  96. package/dist/flows/flow-replay-run.js +4 -4
  97. package/dist/flows/flow-replay.d.ts +5 -7
  98. package/dist/flows/flow-replay.js +114 -47
  99. package/dist/flows/flow-report.js +1 -1
  100. package/dist/flows/flow-scope.d.ts +1 -6
  101. package/dist/flows/flow-step-runners.d.ts +26 -6
  102. package/dist/flows/flow-step-runners.js +157 -6
  103. package/dist/flows/flow-success.js +20 -15
  104. package/dist/flows/flow-tools.js +67 -13
  105. package/dist/flows/flows.js +15 -11
  106. package/dist/flows/gate.d.ts +4 -3
  107. package/dist/flows/gate.js +1 -1
  108. package/dist/flows/heal-run.d.ts +0 -8
  109. package/dist/flows/heal-run.js +5 -5
  110. package/dist/flows/heal.js +3 -3
  111. package/dist/flows/predicate-to-expect.d.ts +38 -0
  112. package/dist/flows/predicate-to-expect.js +95 -0
  113. package/dist/flows/replay.d.ts +16 -1
  114. package/dist/flows/replay.js +58 -22
  115. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  116. package/dist/flows/role-anchor-nearest.js +45 -0
  117. package/dist/flows/server-verify.d.ts +1 -1
  118. package/dist/flows/server-verify.js +5 -5
  119. package/dist/flows/verify-change-tools.js +32 -10
  120. package/dist/flows/verify-change-verdict.d.ts +15 -0
  121. package/dist/flows/verify-change-verdict.js +25 -0
  122. package/dist/flows/watch-batcher.d.ts +3 -2
  123. package/dist/honesty/already-true.d.ts +2 -0
  124. package/dist/honesty/already-true.js +31 -0
  125. package/dist/honesty/blind-spots.d.ts +16 -3
  126. package/dist/honesty/blind-spots.js +24 -12
  127. package/dist/honesty/honesty.d.ts +3 -2
  128. package/dist/honesty/honesty.js +4 -4
  129. package/dist/honesty/uncaptured-bodies.js +1 -1
  130. package/dist/honesty/unread-outcome.js +2 -2
  131. package/dist/honesty/verified.d.ts +22 -3
  132. package/dist/honesty/verified.js +48 -8
  133. package/dist/http-server.js +14 -7
  134. package/dist/hunt/hunt-report.d.ts +2 -1
  135. package/dist/hunt/hunt-report.js +2 -2
  136. package/dist/index.d.ts +8 -6
  137. package/dist/index.js +32 -25
  138. package/dist/init/agent-rules.d.ts +2 -1
  139. package/dist/init/agent-rules.js +45 -7
  140. package/dist/init/app-choice.d.ts +20 -0
  141. package/dist/init/app-choice.js +25 -0
  142. package/dist/init/astro-host.d.ts +21 -0
  143. package/dist/init/astro-host.js +27 -0
  144. package/dist/init/astro-patch.d.ts +16 -0
  145. package/dist/init/astro-patch.js +240 -0
  146. package/dist/init/capabilities.d.ts +17 -0
  147. package/dist/init/capabilities.js +54 -0
  148. package/dist/init/cra.d.ts +37 -0
  149. package/dist/init/cra.js +90 -0
  150. package/dist/init/cursor.js +39 -4
  151. package/dist/init/desktop-doctor.d.ts +3 -2
  152. package/dist/init/desktop-doctor.js +4 -4
  153. package/dist/init/detect.d.ts +26 -0
  154. package/dist/init/detect.js +78 -5
  155. package/dist/init/mcp-registered.d.ts +12 -0
  156. package/dist/init/mcp-registered.js +14 -0
  157. package/dist/init/mcp.d.ts +27 -2
  158. package/dist/init/mcp.js +40 -4
  159. package/dist/init/next-patch.d.ts +39 -0
  160. package/dist/init/next-patch.js +196 -0
  161. package/dist/init/node-io.js +48 -6
  162. package/dist/init/patch-kind.d.ts +20 -0
  163. package/dist/init/patch-kind.js +10 -0
  164. package/dist/init/plan-framework.d.ts +50 -0
  165. package/dist/init/plan-framework.js +341 -0
  166. package/dist/init/plan.d.ts +119 -6
  167. package/dist/init/plan.js +230 -131
  168. package/dist/init/project-id.d.ts +5 -9
  169. package/dist/init/project-id.js +10 -22
  170. package/dist/init/run.d.ts +22 -0
  171. package/dist/init/run.js +393 -28
  172. package/dist/init/slash-command.d.ts +27 -0
  173. package/dist/init/slash-command.js +61 -0
  174. package/dist/init/snippets.d.ts +41 -3
  175. package/dist/init/snippets.js +185 -10
  176. package/dist/init/vite-config.d.ts +4 -10
  177. package/dist/init/vite-config.js +15 -7
  178. package/dist/init/workspace-apps.d.ts +23 -0
  179. package/dist/init/workspace-apps.js +68 -0
  180. package/dist/input/driven-redaction.d.ts +15 -0
  181. package/dist/input/driven-redaction.js +76 -0
  182. package/dist/input/network-detail.d.ts +9 -3
  183. package/dist/input/network-detail.js +43 -23
  184. package/dist/input/network-mock-tools.js +2 -2
  185. package/dist/input/network-mock.js +4 -4
  186. package/dist/input/real-input.js +8 -3
  187. package/dist/input/scroll-find.js +2 -2
  188. package/dist/input/scroll-tools.d.ts +0 -5
  189. package/dist/input/scroll-tools.js +11 -4
  190. package/dist/input/viewport-tools.js +2 -2
  191. package/dist/journal/ambient-store.d.ts +0 -2
  192. package/dist/journal/ambient-store.js +1 -1
  193. package/dist/journal/ambient.js +1 -1
  194. package/dist/journal/attach-journal.d.ts +2 -1
  195. package/dist/journal/deviation-report.js +6 -6
  196. package/dist/journal/deviation-service.js +1 -1
  197. package/dist/journal/envelope-store.d.ts +0 -2
  198. package/dist/journal/envelope-store.js +1 -1
  199. package/dist/journal/envelope.d.ts +2 -3
  200. package/dist/journal/envelope.js +3 -3
  201. package/dist/journal/journal-query.js +1 -1
  202. package/dist/journal/journal-recorder.d.ts +2 -1
  203. package/dist/journal/journal-recorder.js +2 -2
  204. package/dist/journal/rollups.js +9 -9
  205. package/dist/journal/session-end.d.ts +2 -1
  206. package/dist/journal/session-journal.js +4 -4
  207. package/dist/license/license.d.ts +0 -14
  208. package/dist/license/license.js +8 -8
  209. package/dist/log.d.ts +10 -0
  210. package/dist/log.js +11 -1
  211. package/dist/mcp/mcp-is-error.d.ts +13 -0
  212. package/dist/mcp/mcp-is-error.js +18 -0
  213. package/dist/mcp/mcp-outage.d.ts +31 -0
  214. package/dist/mcp/mcp-outage.js +48 -0
  215. package/dist/mcp/mcp-proxy.d.ts +124 -0
  216. package/dist/mcp/mcp-proxy.js +697 -0
  217. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  218. package/dist/{mcp.js → mcp/mcp.js} +110 -35
  219. package/dist/mcp/proxy-handshake.d.ts +3 -0
  220. package/dist/mcp/proxy-handshake.js +54 -0
  221. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  222. package/dist/mcp/proxy-lifecycle.js +70 -0
  223. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  224. package/dist/mcp/tool-catalog-cache.js +55 -0
  225. package/dist/oracles/propose-consequences.d.ts +2 -1
  226. package/dist/oracles/propose-consequences.js +16 -11
  227. package/dist/oracles/self-instrument.d.ts +2 -1
  228. package/dist/oracles/self-instrument.js +1 -1
  229. package/dist/phenomena/phenomena.js +3 -3
  230. package/dist/platform.d.ts +16 -0
  231. package/dist/platform.js +16 -0
  232. package/dist/pool/playwright-launcher.d.ts +15 -2
  233. package/dist/pool/playwright-launcher.js +21 -2
  234. package/dist/project/fs-port.js +1 -1
  235. package/dist/project/project-tools.js +2 -2
  236. package/dist/runs/build-verification-run.js +1 -1
  237. package/dist/runs/repair-prompt.d.ts +0 -6
  238. package/dist/runs/repair-prompt.js +3 -3
  239. package/dist/runs/risk-classify.js +1 -1
  240. package/dist/runs/run-diff.d.ts +0 -2
  241. package/dist/runs/run-diff.js +3 -3
  242. package/dist/runs/run-tools.js +2 -2
  243. package/dist/runs/verification-sync.js +2 -2
  244. package/dist/runs/verify-http.js +1 -1
  245. package/dist/runs/verify-server.js +1 -1
  246. package/dist/session/command-timeout.d.ts +11 -3
  247. package/dist/session/command-timeout.js +2 -2
  248. package/dist/session/control-envelope.d.ts +6 -0
  249. package/dist/session/control-envelope.js +7 -1
  250. package/dist/session/dev-server-probe.d.ts +13 -0
  251. package/dist/session/dev-server-probe.js +52 -0
  252. package/dist/session/last-act.d.ts +17 -3
  253. package/dist/session/last-act.js +16 -4
  254. package/dist/session/live-control-tools.js +7 -2
  255. package/dist/session/live-control.js +1 -1
  256. package/dist/session/looks-like-dev-server.d.ts +14 -0
  257. package/dist/session/looks-like-dev-server.js +25 -0
  258. package/dist/session/no-session-diagnosis.d.ts +38 -0
  259. package/dist/session/no-session-diagnosis.js +70 -0
  260. package/dist/session/no-session-watch.d.ts +19 -0
  261. package/dist/session/no-session-watch.js +61 -0
  262. package/dist/session/observed-state.d.ts +4 -0
  263. package/dist/session/observed-state.js +19 -1
  264. package/dist/session/output-budget.js +1 -1
  265. package/dist/session/session-health.d.ts +8 -1
  266. package/dist/session/session-health.js +8 -7
  267. package/dist/session/session-info.d.ts +26 -7
  268. package/dist/session/session-info.js +42 -1
  269. package/dist/session/session-lease.d.ts +0 -11
  270. package/dist/session/session-lease.js +1 -1
  271. package/dist/session/session-manager.d.ts +3 -0
  272. package/dist/session/session-manager.js +58 -9
  273. package/dist/session/session-readiness.d.ts +1 -1
  274. package/dist/session/session-readiness.js +6 -1
  275. package/dist/session/session-reaper.js +1 -1
  276. package/dist/session/session-reconnect.d.ts +32 -0
  277. package/dist/session/session-reconnect.js +42 -0
  278. package/dist/session/session.d.ts +20 -2
  279. package/dist/session/session.js +76 -26
  280. package/dist/session/state-select.d.ts +1 -1
  281. package/dist/session/state-select.js +1 -1
  282. package/dist/status-payload.d.ts +19 -0
  283. package/dist/status-payload.js +11 -0
  284. package/dist/telemetry/argument-shape.js +10 -1
  285. package/dist/telemetry/browser-mode.d.ts +27 -0
  286. package/dist/telemetry/browser-mode.js +33 -0
  287. package/dist/telemetry/bug-found.d.ts +17 -21
  288. package/dist/telemetry/bug-found.js +120 -8
  289. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  290. package/dist/telemetry/cli-telemetry.js +27 -4
  291. package/dist/telemetry/daemon-telemetry.d.ts +19 -0
  292. package/dist/telemetry/daemon-telemetry.js +12 -2
  293. package/dist/telemetry/dev-repo.js +4 -4
  294. package/dist/telemetry/error-fingerprint.js +21 -2
  295. package/dist/telemetry/feedback-cli.d.ts +1 -1
  296. package/dist/telemetry/feedback-cli.js +4 -4
  297. package/dist/telemetry/feedback-context.d.ts +22 -0
  298. package/dist/telemetry/feedback-context.js +41 -1
  299. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  300. package/dist/telemetry/feedback-delivery.js +30 -0
  301. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  302. package/dist/telemetry/feedback-outbox.js +87 -0
  303. package/dist/telemetry/feedback.d.ts +22 -1
  304. package/dist/telemetry/feedback.js +67 -8
  305. package/dist/telemetry/git-facts.js +1 -1
  306. package/dist/telemetry/session-metrics.d.ts +2 -0
  307. package/dist/telemetry/session-metrics.js +68 -6
  308. package/dist/telemetry/telemetry.d.ts +27 -2
  309. package/dist/telemetry/telemetry.js +119 -29
  310. package/dist/telemetry/verification-of.d.ts +22 -0
  311. package/dist/telemetry/verification-of.js +84 -0
  312. package/dist/tools/act-capsule.js +2 -2
  313. package/dist/tools/act-danger.d.ts +12 -0
  314. package/dist/tools/act-danger.js +20 -3
  315. package/dist/tools/act-helpers.js +4 -4
  316. package/dist/tools/act-tools.d.ts +0 -8
  317. package/dist/tools/act-tools.js +84 -115
  318. package/dist/tools/act-view.js +7 -7
  319. package/dist/tools/alias-args.d.ts +17 -0
  320. package/dist/tools/alias-args.js +25 -0
  321. package/dist/tools/assert-grade.js +9 -9
  322. package/dist/tools/assert-verdict.d.ts +3 -1
  323. package/dist/tools/assert-verdict.js +8 -5
  324. package/dist/tools/browser-tools.js +30 -11
  325. package/dist/tools/contract-tools.js +1 -1
  326. package/dist/tools/coverage-identity.d.ts +34 -0
  327. package/dist/tools/coverage-identity.js +46 -0
  328. package/dist/tools/coverage-tools.js +10 -14
  329. package/dist/tools/dynamic-tools.d.ts +2 -1
  330. package/dist/tools/dynamic-tools.js +90 -18
  331. package/dist/tools/error-recovery.d.ts +20 -2
  332. package/dist/tools/error-recovery.js +179 -4
  333. package/dist/tools/feedback-tools.d.ts +12 -1
  334. package/dist/tools/feedback-tools.js +28 -5
  335. package/dist/tools/invoke-tool.d.ts +0 -6
  336. package/dist/tools/invoke-tool.js +57 -32
  337. package/dist/tools/lease-hint.d.ts +17 -0
  338. package/dist/tools/lease-hint.js +24 -0
  339. package/dist/tools/lease-tools.js +6 -5
  340. package/dist/tools/merge-tools.d.ts +10 -1
  341. package/dist/tools/merge-tools.js +9 -3
  342. package/dist/tools/merged-name-redirect.d.ts +13 -0
  343. package/dist/tools/merged-name-redirect.js +55 -0
  344. package/dist/tools/navigate-result.d.ts +17 -0
  345. package/dist/tools/navigate-result.js +31 -0
  346. package/dist/tools/observe-tools.js +54 -20
  347. package/dist/tools/observed-nothing.d.ts +25 -0
  348. package/dist/tools/observed-nothing.js +33 -0
  349. package/dist/tools/query-paginate.js +2 -2
  350. package/dist/tools/query-shape.d.ts +18 -0
  351. package/dist/tools/query-shape.js +39 -0
  352. package/dist/tools/read-tools.js +85 -20
  353. package/dist/tools/real-input-attempt.d.ts +28 -0
  354. package/dist/tools/real-input-attempt.js +88 -0
  355. package/dist/tools/reconcile-tools.js +7 -7
  356. package/dist/tools/reload-result.d.ts +14 -0
  357. package/dist/tools/reload-result.js +29 -0
  358. package/dist/tools/snapshot-delta.d.ts +4 -5
  359. package/dist/tools/snapshot-delta.js +0 -0
  360. package/dist/tools/tool-kit.d.ts +8 -0
  361. package/dist/tools/tool-surface.d.ts +83 -0
  362. package/dist/tools/tool-surface.js +204 -0
  363. package/dist/tools/tools-helpers.js +6 -6
  364. package/dist/tools/tools.d.ts +15 -0
  365. package/dist/tools/tools.js +53 -17
  366. package/dist/tools/unadvertised-help.d.ts +5 -0
  367. package/dist/tools/unadvertised-help.js +39 -0
  368. package/dist/trace.d.ts +69 -0
  369. package/dist/trace.js +150 -0
  370. package/dist/update/nudge-credit.d.ts +4 -0
  371. package/dist/update/nudge-credit.js +53 -0
  372. package/dist/update/sdk-sync.d.ts +21 -0
  373. package/dist/update/sdk-sync.js +42 -0
  374. package/dist/update/update-checker.js +3 -3
  375. package/dist/update/update-nudge.d.ts +63 -6
  376. package/dist/update/update-nudge.js +128 -15
  377. package/dist/update/updater.d.ts +1 -9
  378. package/dist/update/updater.js +13 -8
  379. package/dist/version/peer-announce.d.ts +4 -0
  380. package/dist/version/peer-announce.js +30 -0
  381. package/dist/{server-version.js → version/server-version.js} +5 -1
  382. package/dist/version/version-nudge.d.ts +28 -0
  383. package/dist/version/version-nudge.js +40 -0
  384. package/dist/version/version-skew.d.ts +66 -0
  385. package/dist/version/version-skew.js +71 -0
  386. package/dist/visual/visual-diff.js +2 -2
  387. package/dist/visual/visual-tools.js +4 -4
  388. package/docs/agent-cheatsheet.md +85 -0
  389. package/docs/architecture.md +113 -0
  390. package/docs/benchmarks.md +255 -0
  391. package/docs/debugging.md +102 -0
  392. package/docs/deploy-checks.md +69 -0
  393. package/docs/desktop-apps.md +256 -0
  394. package/docs/enterprise.md +62 -0
  395. package/docs/fixtures.md +28 -0
  396. package/docs/flows.md +166 -0
  397. package/docs/getting-started.md +469 -0
  398. package/docs/human-control.md +63 -0
  399. package/docs/integration-patterns.md +171 -0
  400. package/docs/local-registry.md +126 -0
  401. package/docs/mint.json +61 -0
  402. package/docs/multi-agent-testing.md +55 -0
  403. package/docs/platform-integration.md +122 -0
  404. package/docs/telemetry-contract.md +214 -0
  405. package/docs/telemetry.md +184 -0
  406. package/docs/testing.md +82 -0
  407. package/docs/token-efficiency.md +80 -0
  408. package/docs/usage.md +1046 -0
  409. package/package.json +8 -5
  410. package/dist/cli-launch.js +0 -112
  411. package/dist/cli-parse.d.ts +0 -140
  412. package/dist/daemon-resilience.d.ts +0 -22
  413. package/dist/daemon-resilience.js +0 -85
  414. package/dist/mcp-proxy.d.ts +0 -35
  415. package/dist/mcp-proxy.js +0 -190
  416. package/dist/tools/profiles.d.ts +0 -37
  417. package/dist/tools/profiles.js +0 -139
  418. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  419. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  420. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  421. /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
  422. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  423. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  424. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -6,8 +6,39 @@
6
6
  export declare function viteManual(port: number | undefined): string;
7
7
  /** Next.js config wrap — always printed (we never auto-rewrite next.config). */
8
8
  export declare function nextConfigManual(configFile: string): string;
9
- /** The dev-only client component that connects Reticle after hydration. */
10
- export declare function nextReticleDevFile(port: number | undefined, projectId?: string): string;
9
+ /**
10
+ * The dev-only client component that connects Reticle after hydration.
11
+ *
12
+ * The token is not optional. The bridge requires a pairing token even on localhost, and unlike Vite
13
+ * (where the plugin injects it) a Next app has to carry it through `withReticle`, which publishes it
14
+ * as `NEXT_PUBLIC_RETICLE_TOKEN`. This file used to connect with only a projectId, so every Next
15
+ * setup ended at `bridge refused the connection: authentication failed` and no session ever appeared.
16
+ */
17
+ export declare function nextReticleDevFile(port: number | undefined, projectId?: string, testids?: readonly string[], stores?: readonly string[]): string;
18
+ export declare function astroManual(port: number | undefined, projectId?: string,
19
+ /**
20
+ * A layout file that actually exists, when one does.
21
+ *
22
+ * Reported from the field: on `examples/framework-react` — which has no layout at all, only
23
+ * `src/pages/index.astro` — init printed thirty lines telling the user to paste into "your
24
+ * layout". Instructions that name a file the project does not have read as a mistake by the
25
+ * reader, and cost them the time it takes to go and confirm it is missing.
26
+ */
27
+ layoutPath?: string): string;
28
+ /**
29
+ * The app-side dev module the Vite plugin imports by convention.
30
+ *
31
+ * `registerCapabilities` tells the agent what it can drive without guessing; `registerStore` is the
32
+ * one that matters most and the one we cannot write for you — detecting that an app depends on
33
+ * zustand is easy, knowing which module exports the store instance is not, and a wrong import here
34
+ * breaks the module everything else hangs off. So the store lines are generated COMMENTED, naming
35
+ * the libraries actually found in package.json, with the exact call to uncomment.
36
+ */
37
+ export declare function viteDevModuleFile(testids: readonly string[], stores: readonly string[]): string;
38
+ /** Where that module goes. Matches @reticlehq/vite-plugin's convention list. */
39
+ export declare const VITE_DEV_MODULE_PATH = "src/reticle-dev.ts";
40
+ /** Default root-layout path, used when no layout was found on disk (reporting only). */
41
+ export declare const NEXT_LAYOUT_PATH = "app/layout.tsx";
11
42
  /** Mount instruction for the root layout. */
12
43
  export declare const NEXT_LAYOUT_MANUAL = "Mount <ReticleDev /> in your root layout (app/layout.tsx), dev-only:\n\n import { ReticleDev } from './reticle-dev';\n // inside <body>:\n {process.env.NODE_ENV === 'development' ? <ReticleDev /> : null}";
13
44
  /**
@@ -23,7 +54,14 @@ export declare const SVELTEKIT_HOOKS_PATH = "src/hooks.client.ts";
23
54
  * Said to the user's face rather than discovered later. React, Next, Remix and Astro each have an
24
55
  * app and a CI gate; SvelteKit has neither, so "it generated some wiring" is not evidence it works.
25
56
  */
26
- export declare const UNVERIFIED_FRAMEWORK_NOTE = "Reticle has no SvelteKit app and no CI gate for one, so this wiring is untested \u2014 it may work, but nothing proves it and nothing will tell us if it breaks. Supported and gated today: Vite + React, Next.js, Remix, Astro, and plain HTML. If the hook does not register a session, please open an issue.";
57
+ /**
58
+ * Said out loud when the app does not render through React. The SDK is framework-agnostic — DOM,
59
+ * network, console and routing all still work — but `@reticlehq/react` is a React adapter, so
60
+ * component names and source mapping do not, and no CI gate covers this stack. Reporting all-green
61
+ * here is the one thing this project exists not to do.
62
+ */
63
+ export declare function unverifiedUiLibraryNote(library: string): string;
64
+ export declare const UNVERIFIED_FRAMEWORK_NOTE = "Reticle has no SvelteKit app and no CI gate for one, so this wiring is untested \u2014 it may work, but nothing proves it and nothing will tell us if it breaks. Supported and gated today: Vite + React, Next.js, Remix and Astro. If the hook does not register a session, please open an issue.";
27
65
  /**
28
66
  * Dev-only client hook that connects Reticle in a SvelteKit app. SvelteKit renders through app.html and
29
67
  * never triggers Vite's index.html injection (verified), so the standard plugin can't auto-connect —
@@ -37,8 +37,21 @@ export function nextConfigManual(configFile) {
37
37
 
38
38
  export default withReticle(nextConfig);`;
39
39
  }
40
- /** The dev-only client component that connects Reticle after hydration. */
41
- export function nextReticleDevFile(port, projectId) {
40
+ /**
41
+ * The dev-only client component that connects Reticle after hydration.
42
+ *
43
+ * The token is not optional. The bridge requires a pairing token even on localhost, and unlike Vite
44
+ * (where the plugin injects it) a Next app has to carry it through `withReticle`, which publishes it
45
+ * as `NEXT_PUBLIC_RETICLE_TOKEN`. This file used to connect with only a projectId, so every Next
46
+ * setup ended at `bridge refused the connection: authentication failed` and no session ever appeared.
47
+ */
48
+ export function nextReticleDevFile(port, projectId, testids = [], stores = []) {
49
+ const base = connectArg(port, projectId);
50
+ const fields = '' === base ? '' : `${base.slice(1, -1).trim()}, `;
51
+ const ids = testids.map((t) => `'${t}'`).join(', ');
52
+ const storeBlock = 0 === stores.length
53
+ ? ' // No state library detected. If you add one, register it here — see docs/usage.md.'
54
+ : stores.map((h) => ` // import your store, then: ${h}`).join('\n');
42
55
  return `'use client';
43
56
  import { useEffect } from 'react';
44
57
 
@@ -46,15 +59,151 @@ import { useEffect } from 'react';
46
59
  export function ReticleDev() {
47
60
  useEffect(() => {
48
61
  if (process.env.NODE_ENV !== 'development') return;
49
- void import('@reticlehq/react').then(({ reticle, install }) => {
62
+ void import('@reticlehq/react').then(({ reticle, install, registerCapabilities }) => {
50
63
  install();
51
- reticle.connect(${connectArg(port, projectId)});
64
+ // Both provided by withReticle() in next.config. The bridge rejects a connect with no token;
65
+ // the root makes source paths repo-relative instead of absolute.
66
+ const token = process.env.NEXT_PUBLIC_RETICLE_TOKEN;
67
+ const root = process.env.NEXT_PUBLIC_RETICLE_ROOT;
68
+ reticle.connect({ ${fields}...(token ? { token } : {}), ...(root ? { root } : {}) });
69
+
70
+ // ── Start with ONE flow. ──────────────────────────────────────────────────────────────────
71
+ // Registering a store is the highest-value line here: it lets the agent check what the app
72
+ // BELIEVES, not just what it rendered. Pass the STORE, not \`() => store.getState()\` — the store
73
+ // form wires \`subscribe\` too, so every mutation emits a diff; the getter form is read-only.
74
+ ${storeBlock}
75
+ registerCapabilities({
76
+ testids: [${ids}],${0 === testids.length ? ' // none found — add data-testid to your key elements' : ''}
77
+ signals: [], // names you pass to reticle.signal()
78
+ stores: [], // the keys you registered above
79
+ });
52
80
  });
53
81
  }, []);
54
82
  return null;
55
83
  }
56
84
  `;
57
85
  }
86
+ /**
87
+ * Astro connect instructions.
88
+ *
89
+ * Astro is Vite-based but SSRs its own HTML, so the plugin's index.html injection never fires, and
90
+ * `vite` is not a direct dependency — so this used to fall through to the generic HTML advice, which
91
+ * tells you to add a connect to an "entry module" Astro does not have, or to bundle the SDK with
92
+ * esbuild. Both are wrong for Astro, and following either gets you nothing.
93
+ *
94
+ * Astro bundles a page `<script>`, so the bare import resolves there. The token has to be inlined by
95
+ * the config because there is no plugin in the page's path to inject it, and `build.target` has to be
96
+ * raised or Astro down-levels the modern SDK bundle and dies on a destructuring transform.
97
+ */
98
+ /**
99
+ * Name the file the connect <script> should go in — the layout when one exists, otherwise the page,
100
+ * because a project with no layout has nowhere else to put it and should not be told otherwise.
101
+ */
102
+ function layoutHost(layoutPath) {
103
+ return layoutPath === undefined
104
+ ? '2. This project has no layout, so put it in the page you want instrumented (e.g.\n src/pages/index.astro) — every page you want a session from needs it — inside <body>:'
105
+ : `2. In ${layoutPath} (or any other page you want instrumented), inside <body>:`;
106
+ }
107
+ export function astroManual(port, projectId,
108
+ /**
109
+ * A layout file that actually exists, when one does.
110
+ *
111
+ * Reported from the field: on `examples/framework-react` — which has no layout at all, only
112
+ * `src/pages/index.astro` — init printed thirty lines telling the user to paste into "your
113
+ * layout". Instructions that name a file the project does not have read as a mistake by the
114
+ * reader, and cost them the time it takes to go and confirm it is missing.
115
+ */
116
+ layoutPath) {
117
+ const extra = port !== undefined && port !== RETICLE_DEFAULT_PORT
118
+ ? `\n url: '${bridgeWsUrl(port)}',`
119
+ : '';
120
+ const id = projectId !== undefined && projectId.length > 0 ? `\n projectId: '${projectId}',` : '';
121
+ return `Astro renders its own HTML, so the connect goes in a page <script> and the pairing token is inlined by the config.
122
+
123
+ 1. In astro.config.mjs — inline the daemon's token and raise the build target:
124
+
125
+ import { readFileSync } from 'node:fs';
126
+ import { homedir } from 'node:os';
127
+ import { join } from 'node:path';
128
+
129
+ function reticleToken() {
130
+ const dir = process.env['RETICLE_PAIRING_TOKEN_DIR'] || join(homedir(), '.reticle');
131
+ try { return readFileSync(join(dir, 'pairing-token'), 'utf8').trim(); } catch { return ''; }
132
+ }
133
+
134
+ export default defineConfig({
135
+ vite: {
136
+ // Astro's default target down-levels the modern SDK bundle and fails on a destructuring transform.
137
+ build: { target: 'es2022' },
138
+ optimizeDeps: { esbuildOptions: { target: 'es2022' } },
139
+ define: {
140
+ __RETICLE_TOKEN__: JSON.stringify(reticleToken()),
141
+ // Without this, source pointers come back as absolute paths from YOUR machine — useless in a
142
+ // report. Every other framework gets it from its build plugin; Astro owns its Vite instance.
143
+ __RETICLE_ROOT__: JSON.stringify(process.cwd()),
144
+ },
145
+ },
146
+ });
147
+
148
+ ${layoutHost(layoutPath)}
149
+
150
+ <script>
151
+ if (import.meta.env.DEV) {
152
+ const token = typeof __RETICLE_TOKEN__ !== 'undefined' ? __RETICLE_TOKEN__ : '';
153
+ const root = typeof __RETICLE_ROOT__ !== 'undefined' ? __RETICLE_ROOT__ : '';
154
+ const { reticle, install } = await import('@reticlehq/react');
155
+ install();
156
+ reticle.connect({${id}${extra}
157
+ ...(token.length > 0 ? { token } : {}),
158
+ ...(root.length > 0 ? { root } : {}),
159
+ });
160
+ }
161
+ </script>
162
+
163
+ Start the daemon BEFORE \`astro dev\`, so the token file exists when the config is read. Until it does the token is empty and the page reloads once the daemon is up.`;
164
+ }
165
+ /**
166
+ * The app-side dev module the Vite plugin imports by convention.
167
+ *
168
+ * `registerCapabilities` tells the agent what it can drive without guessing; `registerStore` is the
169
+ * one that matters most and the one we cannot write for you — detecting that an app depends on
170
+ * zustand is easy, knowing which module exports the store instance is not, and a wrong import here
171
+ * breaks the module everything else hangs off. So the store lines are generated COMMENTED, naming
172
+ * the libraries actually found in package.json, with the exact call to uncomment.
173
+ */
174
+ export function viteDevModuleFile(testids, stores) {
175
+ const ids = testids.map((t) => `'${t}'`).join(', ');
176
+ const storeBlock = 0 === stores.length
177
+ ? ' // No state library detected. If you add one, register it here — see docs/usage.md.'
178
+ : stores.map((h) => ` // import your store, then: ${h}`).join('\n');
179
+ return `// Dev-only. Imported automatically by @reticlehq/vite-plugin — you do not need to import it.
180
+ // Self-guards on import.meta.env.DEV, so it is a no-op in a production build.
181
+ import { registerCapabilities } from '@reticlehq/react';
182
+
183
+ if (import.meta.env.DEV) {
184
+ // ── Start with ONE flow. ─────────────────────────────────────────────────────────────────────
185
+ // You do not need to describe the whole app to get value, and trying to is the slow path. Register
186
+ // the store your most important flow reads, and list the testids that flow touches. Add more later,
187
+ // when a flow you actually replay needs them.
188
+ //
189
+ // Registering a store is the highest-value line in this file: it lets the agent check what the app
190
+ // BELIEVES, not just what it rendered — the class of bug a screenshot cannot see. Pass the STORE,
191
+ // not \`() => store.getState()\`: the store form wires \`subscribe\` too, so every mutation emits a
192
+ // state diff; the getter form is read-only and silently produces empty diffs.
193
+ ${storeBlock}
194
+
195
+ registerCapabilities({
196
+ testids: [${ids}],${0 === testids.length ? ' // none found — add data-testid to your key elements' : ''}
197
+ signals: [], // names you pass to reticle.signal()
198
+ stores: [], // the keys you registered above
199
+ });
200
+ }
201
+ `;
202
+ }
203
+ /** Where that module goes. Matches @reticlehq/vite-plugin's convention list. */
204
+ export const VITE_DEV_MODULE_PATH = 'src/reticle-dev.ts';
205
+ /** Default root-layout path, used when no layout was found on disk (reporting only). */
206
+ export const NEXT_LAYOUT_PATH = 'app/layout.tsx';
58
207
  /** Mount instruction for the root layout. */
59
208
  export const NEXT_LAYOUT_MANUAL = `Mount <ReticleDev /> in your root layout (app/layout.tsx), dev-only:
60
209
 
@@ -74,10 +223,11 @@ export function htmlManual(port, projectId) {
74
223
  • Bundled app (Create React App, webpack, Parcel, Vue/Svelte CLI, etc.) — add to your ENTRY module
75
224
  (e.g. src/index.js or src/main.js), where '@reticlehq/react' resolves through your bundler:
76
225
 
77
- if (location.hostname === 'localhost') {
78
- const { reticle, install } = await import('@reticlehq/react');
79
- install();
80
- reticle.connect(${arg});
226
+ if (window.location.hostname === 'localhost') {
227
+ void import('@reticlehq/react').then(({ reticle, install }) => {
228
+ install();
229
+ reticle.connect(${arg});
230
+ });
81
231
  }
82
232
 
83
233
  • Plain static HTML with no build step — the browser can't resolve the bare '@reticlehq/react' import, so
@@ -90,21 +240,46 @@ export const SVELTEKIT_HOOKS_PATH = 'src/hooks.client.ts';
90
240
  * Said to the user's face rather than discovered later. React, Next, Remix and Astro each have an
91
241
  * app and a CI gate; SvelteKit has neither, so "it generated some wiring" is not evidence it works.
92
242
  */
93
- export const UNVERIFIED_FRAMEWORK_NOTE = 'Reticle has no SvelteKit app and no CI gate for one, so this wiring is untested — it may work, but nothing proves it and nothing will tell us if it breaks. Supported and gated today: Vite + React, Next.js, Remix, Astro, and plain HTML. If the hook does not register a session, please open an issue.';
243
+ /**
244
+ * Said out loud when the app does not render through React. The SDK is framework-agnostic — DOM,
245
+ * network, console and routing all still work — but `@reticlehq/react` is a React adapter, so
246
+ * component names and source mapping do not, and no CI gate covers this stack. Reporting all-green
247
+ * here is the one thing this project exists not to do.
248
+ */
249
+ export function unverifiedUiLibraryNote(library) {
250
+ return `Detected a ${library} app. Reticle's DOM, network, console and state tools work here, and source file:line does too — the build plugin stamps data-reticle-source regardless of UI library (measured on preact and svelte). What @reticlehq/react adds and you will NOT get is React component identity: component names and component stacks. No CI gate covers ${library}. Gated today: Vite + React, Next.js, Remix, Astro. If something doesn't work, please open an issue.`;
251
+ }
252
+ export const UNVERIFIED_FRAMEWORK_NOTE = 'Reticle has no SvelteKit app and no CI gate for one, so this wiring is untested — it may work, but nothing proves it and nothing will tell us if it breaks. Supported and gated today: Vite + React, Next.js, Remix and Astro. If the hook does not register a session, please open an issue.';
94
253
  /**
95
254
  * Dev-only client hook that connects Reticle in a SvelteKit app. SvelteKit renders through app.html and
96
255
  * never triggers Vite's index.html injection (verified), so the standard plugin can't auto-connect —
97
256
  * a client hook is the reliable path. SvelteKit runs src/hooks.client.ts on the client at startup.
98
257
  */
99
258
  export function svelteKitHooksFile(port, projectId) {
259
+ const base = connectArg(port, projectId);
260
+ const fields = '' === base ? '' : `${base.slice(1, -1).trim()}, `;
100
261
  return `// Dev-only: connect Reticle on the client. SvelteKit renders via app.html, so the Vite-plugin
101
262
  // index.html injection doesn't fire — connect from this client hook instead.
102
263
  if (import.meta.env.DEV) {
103
264
  void import('@reticlehq/react').then(({ reticle, install }) => {
104
265
  install();
105
- reticle.connect(${connectArg(port, projectId)});
266
+ // The bridge requires the pairing token even on localhost. Nothing in a browser can read the
267
+ // file it lives in, so @reticlehq/vite-plugin inlines it here at build time. Without it the
268
+ // console reads "bridge refused the connection: authentication failed" and no session appears.
269
+ const token = typeof __RETICLE_TOKEN__ !== 'undefined' ? __RETICLE_TOKEN__ : '';
270
+ const root = typeof __RETICLE_ROOT__ !== 'undefined' ? __RETICLE_ROOT__ : '';
271
+ const sdkVersion = typeof __RETICLE_SDK_VERSION__ !== 'undefined' ? __RETICLE_SDK_VERSION__ : '';
272
+ reticle.connect({
273
+ ${fields}...(token.length > 0 ? { token } : {}),
274
+ ...(root.length > 0 ? { root } : {}),
275
+ ...(sdkVersion.length > 0 ? { sdkVersion } : {}),
276
+ });
106
277
  });
107
278
  }
279
+
280
+ declare const __RETICLE_TOKEN__: string | undefined;
281
+ declare const __RETICLE_ROOT__: string | undefined;
282
+ declare const __RETICLE_SDK_VERSION__: string | undefined;
108
283
  `;
109
284
  }
110
285
  /**
@@ -4,21 +4,15 @@
4
4
  * bails to a `manual` result so we never half-edit a build config (a broken config is worse than a
5
5
  * documented manual step).
6
6
  */
7
+ import { PatchKind, type SourcePatch } from './patch-kind.js';
7
8
  export declare const VITE_IMPORT = "import { reticle } from '@reticlehq/vite-plugin';";
9
+ /** Alias kept so existing call sites read in Vite terms; the vocabulary is shared (see patch-kind). */
8
10
  export declare const VitePatchKind: {
9
11
  readonly APPLY: "apply";
10
12
  readonly ALREADY: "already";
11
13
  readonly MANUAL: "manual";
12
14
  };
13
- export type VitePatchKind = (typeof VitePatchKind)[keyof typeof VitePatchKind];
14
- type VitePatch = {
15
- kind: typeof VitePatchKind.APPLY;
16
- code: string;
17
- } | {
18
- kind: typeof VitePatchKind.ALREADY;
19
- } | {
20
- kind: typeof VitePatchKind.MANUAL;
21
- reason: string;
22
- };
15
+ export type VitePatchKind = PatchKind;
16
+ type VitePatch = SourcePatch;
23
17
  export declare function patchViteConfig(source: string, port?: number): VitePatch;
24
18
  export {};
@@ -4,6 +4,7 @@
4
4
  * bails to a `manual` result so we never half-edit a build config (a broken config is worse than a
5
5
  * documented manual step).
6
6
  */
7
+ import { PatchKind } from './patch-kind.js';
7
8
  export const VITE_IMPORT = "import { reticle } from '@reticlehq/vite-plugin';";
8
9
  const RETICLE_MARKER = '@reticlehq/vite-plugin';
9
10
  /** The `reticle...)` call — carries the bridge port so the injected connect targets it. */
@@ -14,11 +15,8 @@ function reticlePluginCall(port) {
14
15
  const PLUGINS_ARRAY = /plugins\s*:\s*\[/;
15
16
  /** Matches an ES import statement (used to place our import after the last one). */
16
17
  const IMPORT_LINE = /^import\s.+from\s+['"][^'"]+['"];?\s*$/gm;
17
- export const VitePatchKind = {
18
- APPLY: 'apply',
19
- ALREADY: 'already',
20
- MANUAL: 'manual',
21
- };
18
+ /** Alias kept so existing call sites read in Vite terms; the vocabulary is shared (see patch-kind). */
19
+ export const VitePatchKind = PatchKind;
22
20
  const NO_PLUGINS_REASON = "couldn't find a `plugins: [...]` array to extend";
23
21
  function insertImport(source) {
24
22
  const matches = [...source.matchAll(IMPORT_LINE)];
@@ -29,9 +27,19 @@ function insertImport(source) {
29
27
  const end = last.index + last[0].length;
30
28
  return `${source.slice(0, end)}\n${VITE_IMPORT}${source.slice(end)}`;
31
29
  }
30
+ /**
31
+ * Insert right after the opening `[` of the plugins array, spaced the way the surrounding line is.
32
+ *
33
+ * A multi-line array puts a newline next, and `[reticle(), \n` leaves trailing whitespace — exactly
34
+ * what a formatter rewrites, turning a one-line install into a diff against the user's own style. A
35
+ * single-line array needs the space, or the result reads `[reticle(),react()]`.
36
+ */
32
37
  function insertPlugin(source, port) {
33
- // Insert right after the opening `[` of the plugins array.
34
- return source.replace(PLUGINS_ARRAY, (match) => `${match}${reticlePluginCall(port)}, `);
38
+ return source.replace(PLUGINS_ARRAY, (match, _g, offset) => {
39
+ const next = source[offset + match.length] ?? '';
40
+ const separator = '' === next || /\s/.test(next) ? '' : ' ';
41
+ return `${match}${reticlePluginCall(port)},${separator}`;
42
+ });
35
43
  }
36
44
  export function patchViteConfig(source, port) {
37
45
  if (source.includes(RETICLE_MARKER)) {
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Which directories to look in for the app, when `reticle init` runs at a monorepo root.
3
+ *
4
+ * This used to be the literal list `['apps', 'packages']`. Measured on a real repo with three Next
5
+ * apps at `web/`, `admin/` and `space/`, it found none — so the redirect never fired, init ran
6
+ * against the root, and it reported ✓ for writing `app/reticle-dev.tsx` into a directory Next never
7
+ * compiles. A ⚠ tells a human to act; a ✓ tells them it is handled.
8
+ *
9
+ * A workspace DECLARES its packages, so that declaration is used first and directory names are not
10
+ * guessed at all. Where nothing is declared, every top-level directory is a better candidate than two
11
+ * hardcoded ones — filtered by the usual non-source suspects, and still subject to the `looksLikeApp`
12
+ * check that follows, so a wrong guess here costs a `package.json` read.
13
+ */
14
+ interface WorkspaceSources {
15
+ /** Raw contents of pnpm-workspace.yaml, when present. */
16
+ pnpmWorkspace?: string;
17
+ /** The `workspaces` field of package.json — array form or the yarn/npm object form. */
18
+ pkgWorkspaces?: unknown;
19
+ /** Directory names at the repo root, used when nothing is declared. */
20
+ topLevelDirs?: readonly string[];
21
+ }
22
+ export declare function workspaceParents(sources: WorkspaceSources): string[];
23
+ export {};
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Which directories to look in for the app, when `reticle init` runs at a monorepo root.
3
+ *
4
+ * This used to be the literal list `['apps', 'packages']`. Measured on a real repo with three Next
5
+ * apps at `web/`, `admin/` and `space/`, it found none — so the redirect never fired, init ran
6
+ * against the root, and it reported ✓ for writing `app/reticle-dev.tsx` into a directory Next never
7
+ * compiles. A ⚠ tells a human to act; a ✓ tells them it is handled.
8
+ *
9
+ * A workspace DECLARES its packages, so that declaration is used first and directory names are not
10
+ * guessed at all. Where nothing is declared, every top-level directory is a better candidate than two
11
+ * hardcoded ones — filtered by the usual non-source suspects, and still subject to the `looksLikeApp`
12
+ * check that follows, so a wrong guess here costs a `package.json` read.
13
+ */
14
+ /** Directories that are never a workspace package, whatever the layout. */
15
+ const NEVER_A_PACKAGE = new Set(['node_modules', 'dist', 'build', 'out', 'coverage', 'target']);
16
+ /** `packages/*` -> `packages`; `web` -> `web`. The base directory a glob searches. */
17
+ function globBase(pattern) {
18
+ const base = pattern
19
+ .split('/')[0]
20
+ ?.trim()
21
+ .replace(/^['"]|['"]$/g, '');
22
+ return base === undefined || '' === base || '.' === base || base.includes('*') ? undefined : base;
23
+ }
24
+ function fromPnpm(yaml) {
25
+ // A deliberately small reader rather than a YAML dependency: this file is a list of globs, and the
26
+ // only shape that matters is `- 'pattern'` under `packages:`. A malformed file yields nothing,
27
+ // which falls through to the top-level scan rather than failing the install.
28
+ const out = [];
29
+ let inPackages = false;
30
+ for (const line of yaml.split('\n')) {
31
+ if (/^packages\s*:/.test(line)) {
32
+ inPackages = true;
33
+ continue;
34
+ }
35
+ if (inPackages && /^\S/.test(line))
36
+ break;
37
+ const item = /^\s*-\s*(.+)$/.exec(line);
38
+ if (inPackages && item?.[1] !== undefined) {
39
+ const base = globBase(item[1]);
40
+ if (base !== undefined)
41
+ out.push(base);
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+ function fromPkg(workspaces) {
47
+ const list = Array.isArray(workspaces)
48
+ ? workspaces
49
+ : 'object' === typeof workspaces && workspaces !== null
50
+ ? workspaces.packages
51
+ : undefined;
52
+ if (!Array.isArray(list))
53
+ return [];
54
+ return list
55
+ .filter((p) => 'string' === typeof p)
56
+ .map(globBase)
57
+ .filter((p) => p !== undefined);
58
+ }
59
+ export function workspaceParents(sources) {
60
+ const declared = [
61
+ ...(sources.pnpmWorkspace === undefined ? [] : fromPnpm(sources.pnpmWorkspace)),
62
+ ...fromPkg(sources.pkgWorkspaces),
63
+ ];
64
+ const candidates = declared.length > 0
65
+ ? declared
66
+ : (sources.topLevelDirs ?? []).filter((d) => !d.startsWith('.') && !NEVER_A_PACKAGE.has(d));
67
+ return [...new Set(candidates)];
68
+ }
@@ -0,0 +1,15 @@
1
+ import { type RedactionPolicy } from '@reticlehq/core';
2
+ /** Record what a session declared sensitive. Called when its hello lands. */
3
+ export declare function declareDrivenRedactionKeys(sessionId: string, keys: readonly string[]): void;
4
+ /** Drop a session's declarations when it disconnects, so a closed tab stops widening the union. */
5
+ export declare function forgetDrivenRedactionKeys(sessionId: string): void;
6
+ /** Every key declared by any connected session, sorted so the memo signature is stable. */
7
+ export declare function drivenRedactionKeys(): string[];
8
+ /**
9
+ * The rule in force for driven-path captures: the built-in rule plus every declared key. Identical
10
+ * to the built-in rule when no session declared anything, which is the overwhelmingly common case
11
+ * and the one that must stay byte-identical to before this feature existed.
12
+ */
13
+ export declare function drivenRedactionPolicy(): RedactionPolicy;
14
+ /** Test seam: drop every declaration. Not used in production — sessions unregister themselves. */
15
+ export declare function resetDrivenRedaction(): void;
@@ -0,0 +1,76 @@
1
+ import { buildRedactionPolicy } from '@reticlehq/core';
2
+ /**
3
+ * The redaction rule the DRIVEN path uses — the one place in the daemon that redacts a payload the
4
+ * browser SDK never saw.
5
+ *
6
+ * ## Why this exists at all
7
+ *
8
+ * `connect({ redact: { keys } })` is configured in the page, and the SDK redacts everything it
9
+ * observes. But a driven session's request bodies and response headers are captured by the daemon
10
+ * straight from the network stack (`page.on('response')`), so they never pass through the SDK. An app
11
+ * that declared `licenceKey` sensitive would see it redacted everywhere the developer looked, and
12
+ * find it in cleartext in the on-disk journal — a leak on precisely the path they cannot inspect.
13
+ * So a session's declared keys are announced in its hello and land here.
14
+ *
15
+ * ## Why a daemon-wide UNION rather than a per-session rule
16
+ *
17
+ * A NET_DETAIL is redacted at capture time and only routed to a session afterwards, by `pageUrl`.
18
+ * At the moment of redaction there is no session in hand, and there is a real window in which there
19
+ * is no session at all yet. Resolving one first would mean either delaying redaction (holding a raw
20
+ * credential in memory until routing succeeds) or dropping the detail.
21
+ *
22
+ * The union sidesteps that, and is only safe because of an asymmetry that is enforced upstream:
23
+ * declared keys can ONLY add redaction. The worst case is that one local app's declared key
24
+ * over-redacts a same-named field in another local app on the same daemon — an inconvenience, in the
25
+ * safe direction, between two apps belonging to the same developer on the same machine. The
26
+ * dangerous direction is closed by construction: `allow` never crosses the bridge, so nothing a page
27
+ * sends can lower this floor below the built-in rule.
28
+ */
29
+ /** Declared keys per session id, so a disconnect narrows the union back down. */
30
+ const declaredBySession = new Map();
31
+ /** Memoized policy + the key set it was built from, so the hot path does not rebuild per response. */
32
+ let cached;
33
+ function invalidate() {
34
+ cached = undefined;
35
+ }
36
+ /** Record what a session declared sensitive. Called when its hello lands. */
37
+ export function declareDrivenRedactionKeys(sessionId, keys) {
38
+ if (0 === keys.length) {
39
+ if (declaredBySession.delete(sessionId))
40
+ invalidate();
41
+ return;
42
+ }
43
+ declaredBySession.set(sessionId, [...keys]);
44
+ invalidate();
45
+ }
46
+ /** Drop a session's declarations when it disconnects, so a closed tab stops widening the union. */
47
+ export function forgetDrivenRedactionKeys(sessionId) {
48
+ if (declaredBySession.delete(sessionId))
49
+ invalidate();
50
+ }
51
+ /** Every key declared by any connected session, sorted so the memo signature is stable. */
52
+ export function drivenRedactionKeys() {
53
+ const all = new Set();
54
+ for (const keys of declaredBySession.values())
55
+ for (const key of keys)
56
+ all.add(key);
57
+ return [...all].sort();
58
+ }
59
+ /**
60
+ * The rule in force for driven-path captures: the built-in rule plus every declared key. Identical
61
+ * to the built-in rule when no session declared anything, which is the overwhelmingly common case
62
+ * and the one that must stay byte-identical to before this feature existed.
63
+ */
64
+ export function drivenRedactionPolicy() {
65
+ const keys = drivenRedactionKeys();
66
+ const signature = keys.join('\u0000');
67
+ if (cached?.signature !== signature) {
68
+ cached = { signature, policy: buildRedactionPolicy({ keys }) };
69
+ }
70
+ return cached.policy;
71
+ }
72
+ /** Test seam: drop every declaration. Not used in production — sessions unregister themselves. */
73
+ export function resetDrivenRedaction() {
74
+ declaredBySession.clear();
75
+ invalidate();
76
+ }
@@ -1,4 +1,4 @@
1
- import { type ReticleEvent } from '@reticlehq/core';
1
+ import { type RedactionPolicy, type ReticleEvent } from '@reticlehq/core';
2
2
  /**
3
3
  * CDP-authoritative network detail. Wherever the daemon has a CDP connection — whether it launched
4
4
  * the browser or attached to one someone else started — `page.on('response')` sees the FULL response:
@@ -34,7 +34,13 @@ export interface NetworkDetail {
34
34
  */
35
35
  pageUrl?: string;
36
36
  }
37
- /** Shape a raw authoritative response into a NET_DETAIL payload (lower-cased headers). */
37
+ /**
38
+ * Shape a raw authoritative response into a NET_DETAIL payload (lower-cased headers).
39
+ *
40
+ * `policy` defaults to the built-in rule so this stays a PURE builder — the driven-path union is
41
+ * resolved by `attachNetworkDetail`, at the glue layer, where reading process state is not a lie
42
+ * about the function's inputs.
43
+ */
38
44
  export declare function buildNetworkDetail(raw: {
39
45
  url: string;
40
46
  method?: string;
@@ -43,7 +49,7 @@ export declare function buildNetworkDetail(raw: {
43
49
  resourceType?: string;
44
50
  requestBody?: string;
45
51
  pageUrl?: string;
46
- }): NetworkDetail;
52
+ }, policy?: RedactionPolicy): NetworkDetail;
47
53
  /**
48
54
  * Fold each NET_DETAIL onto the matching in-page NET_REQUEST (by method+url), enriching it with the
49
55
  * authoritative headers/resourceType the page-side wrapper couldn't see — WITHOUT clobbering fields the