@reticlehq/server 2.11.0 → 2.13.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 (513) hide show
  1. package/SKILL.md +93 -111
  2. package/dist/bridge/auth-failure-reason.d.ts +6 -2
  3. package/dist/bridge/auth-failure-reason.js +15 -8
  4. package/dist/bridge/bridge.d.ts +18 -14
  5. package/dist/bridge/bridge.js +248 -165
  6. package/dist/bridge/bridge.test-harness.d.ts +2 -0
  7. package/dist/bridge/bridge.test-harness.js +8 -1
  8. package/dist/bridge/pairing-token.d.ts +5 -0
  9. package/dist/bridge/pairing-token.js +28 -0
  10. package/dist/bridge/protocol-skew.d.ts +37 -0
  11. package/dist/bridge/protocol-skew.js +45 -0
  12. package/dist/capsule/capsule-store.d.ts +8 -7
  13. package/dist/capsule/capsule-store.js +1 -1
  14. package/dist/capsule/causal-summary.d.ts +2 -2
  15. package/dist/capsule/causal-summary.js +7 -2
  16. package/dist/chromium-launch-options.d.ts +28 -0
  17. package/dist/chromium-launch-options.js +29 -0
  18. package/dist/cli/cli-doctor.js +39 -1
  19. package/dist/cli/cli-flow-commands.d.ts +3 -1
  20. package/dist/cli/cli-flow-commands.js +34 -4
  21. package/dist/cli/cli-kill.d.ts +2 -1
  22. package/dist/cli/cli-launch.d.ts +11 -1
  23. package/dist/cli/cli-launch.js +59 -10
  24. package/dist/cli/cli-parse.d.ts +17 -1
  25. package/dist/cli/cli-parse.js +238 -12
  26. package/dist/cli/cli-update-commands.js +20 -1
  27. package/dist/cli/cli-verify.d.ts +2 -1
  28. package/dist/cli/cli-verify.js +5 -4
  29. package/dist/cli/cloud-cli.js +428 -253
  30. package/dist/cli/cloud-keystore.d.ts +37 -0
  31. package/dist/cli/cloud-keystore.js +97 -0
  32. package/dist/cli/cloud-kit.d.ts +37 -0
  33. package/dist/cli/cloud-kit.js +121 -0
  34. package/dist/cli/cloud-login.d.ts +17 -0
  35. package/dist/cli/cloud-login.js +183 -0
  36. package/dist/cli/cloud-session.d.ts +48 -0
  37. package/dist/cli/cloud-session.js +53 -0
  38. package/dist/cli/config-discovery.d.ts +2 -1
  39. package/dist/cli/config-discovery.js +1 -0
  40. package/dist/cli/daemon-start-options.d.ts +32 -0
  41. package/dist/cli/daemon-start-options.js +38 -0
  42. package/dist/cli/device-flow.d.ts +76 -0
  43. package/dist/cli/device-flow.js +56 -0
  44. package/dist/cli/doctor-rows.d.ts +12 -1
  45. package/dist/cli/doctor-rows.js +12 -1
  46. package/dist/cli/drive-attach.d.ts +13 -0
  47. package/dist/cli/drive-attach.js +18 -0
  48. package/dist/cli/drive-command.js +7 -3
  49. package/dist/cli/gate-exit.d.ts +21 -0
  50. package/dist/cli/gate-exit.js +21 -0
  51. package/dist/cli/gate-hook-message.d.ts +33 -0
  52. package/dist/cli/gate-hook-message.js +46 -0
  53. package/dist/cli/mcp-command.d.ts +25 -0
  54. package/dist/cli/mcp-command.js +174 -0
  55. package/dist/cli/project-name.d.ts +30 -0
  56. package/dist/cli/project-name.js +44 -0
  57. package/dist/cli/sibling-ports.d.ts +37 -0
  58. package/dist/cli/sibling-ports.js +56 -0
  59. package/dist/cli/status-next-action.d.ts +14 -0
  60. package/dist/cli/status-next-action.js +4 -4
  61. package/dist/cli.d.ts +1 -0
  62. package/dist/cli.js +167 -186
  63. package/dist/cloud/cloud-config.d.ts +24 -2
  64. package/dist/cloud/cloud-config.js +64 -5
  65. package/dist/cloud/cloud-sync.d.ts +16 -5
  66. package/dist/cloud/cloud-sync.js +64 -1
  67. package/dist/cloud/sync-cycle.d.ts +110 -0
  68. package/dist/cloud/sync-cycle.js +270 -0
  69. package/dist/cloud/sync-daemon.d.ts +69 -0
  70. package/dist/cloud/sync-daemon.js +260 -0
  71. package/dist/cloud/sync-disk.d.ts +9 -0
  72. package/dist/cloud/sync-disk.js +191 -0
  73. package/dist/cloud/sync-hash.d.ts +2 -0
  74. package/dist/cloud/sync-hash.js +35 -0
  75. package/dist/crawl/crawl-tools.js +5 -3
  76. package/dist/crawl/crawl.d.ts +12 -13
  77. package/dist/crawl/crawl.js +25 -3
  78. package/dist/daemon/daemon-resolve.d.ts +122 -0
  79. package/dist/daemon/daemon-resolve.js +187 -0
  80. package/dist/daemon/dev-servers.d.ts +15 -0
  81. package/dist/daemon/dev-servers.js +68 -0
  82. package/dist/daemon/free-port.d.ts +29 -0
  83. package/dist/daemon/free-port.js +55 -0
  84. package/dist/daemon/heartbeat.d.ts +0 -4
  85. package/dist/daemon/heartbeat.js +2 -2
  86. package/dist/daemon/wake-decision.d.ts +32 -0
  87. package/dist/daemon/wake-decision.js +38 -0
  88. package/dist/dev/stale-issue-guard.d.ts +0 -2
  89. package/dist/dev/stale-issue-guard.js +1 -1
  90. package/dist/domain/domain-tools.js +2 -2
  91. package/dist/events/contradictions.d.ts +59 -0
  92. package/dist/events/contradictions.js +253 -27
  93. package/dist/events/declared.d.ts +17 -0
  94. package/dist/events/declared.js +43 -1
  95. package/dist/events/echo-mismatch.js +65 -1
  96. package/dist/events/event-filters.d.ts +8 -0
  97. package/dist/events/event-filters.js +19 -2
  98. package/dist/events/observed-in-window.d.ts +12 -0
  99. package/dist/events/observed-in-window.js +17 -0
  100. package/dist/events/predicate-asks.d.ts +13 -0
  101. package/dist/events/predicate-asks.js +22 -0
  102. package/dist/events/predicate-eval.js +117 -27
  103. package/dist/events/predicate-parse.d.ts +0 -6
  104. package/dist/events/predicate-parse.js +43 -3
  105. package/dist/events/predicate-precheck.d.ts +8 -0
  106. package/dist/events/predicate-precheck.js +41 -0
  107. package/dist/events/predicate-route.d.ts +14 -0
  108. package/dist/events/predicate-route.js +36 -8
  109. package/dist/events/predicate-schema.d.ts +44 -0
  110. package/dist/events/predicate-schema.js +126 -23
  111. package/dist/events/predicate.d.ts +20 -0
  112. package/dist/events/predicate.js +77 -6
  113. package/dist/events/split-text-miss.d.ts +30 -0
  114. package/dist/events/split-text-miss.js +44 -0
  115. package/dist/flows/coverage.d.ts +0 -8
  116. package/dist/flows/coverage.js +1 -1
  117. package/dist/flows/decision.d.ts +5 -1
  118. package/dist/flows/decision.js +33 -7
  119. package/dist/flows/flake.d.ts +6 -8
  120. package/dist/flows/flake.js +1 -1
  121. package/dist/flows/flow-intent.d.ts +52 -0
  122. package/dist/flows/flow-intent.js +123 -0
  123. package/dist/flows/flow-memory-consult.d.ts +55 -0
  124. package/dist/flows/flow-memory-consult.js +38 -0
  125. package/dist/flows/flow-replay-run.d.ts +18 -7
  126. package/dist/flows/flow-replay-run.js +263 -23
  127. package/dist/flows/flow-replay.js +11 -2
  128. package/dist/flows/flow-step-runners.d.ts +8 -0
  129. package/dist/flows/flow-step-runners.js +8 -2
  130. package/dist/flows/flow-store-for-session.d.ts +31 -0
  131. package/dist/flows/flow-store-for-session.js +33 -0
  132. package/dist/flows/flow-success.js +15 -3
  133. package/dist/flows/flow-tools.js +87 -33
  134. package/dist/flows/flows.d.ts +14 -1
  135. package/dist/flows/flows.js +502 -24
  136. package/dist/flows/heal-run.js +5 -2
  137. package/dist/flows/predicate-to-expect.d.ts +17 -12
  138. package/dist/flows/predicate-to-expect.js +36 -15
  139. package/dist/flows/recordings.d.ts +13 -1
  140. package/dist/flows/recordings.js +17 -2
  141. package/dist/flows/replay.d.ts +22 -2
  142. package/dist/flows/replay.js +28 -2
  143. package/dist/flows/server-verify.js +16 -2
  144. package/dist/flows/verify-change-tools.js +9 -3
  145. package/dist/honesty/blind-spots.d.ts +51 -15
  146. package/dist/honesty/blind-spots.js +64 -4
  147. package/dist/honesty/feature-capture.d.ts +150 -0
  148. package/dist/honesty/feature-capture.js +233 -0
  149. package/dist/honesty/honesty.d.ts +24 -0
  150. package/dist/honesty/honesty.js +25 -1
  151. package/dist/honesty/instrumentation-gaps.d.ts +39 -6
  152. package/dist/honesty/instrumentation-gaps.js +88 -5
  153. package/dist/honesty/tool-hit-rate.d.ts +86 -0
  154. package/dist/honesty/tool-hit-rate.js +90 -0
  155. package/dist/honesty/undeclared-change.d.ts +51 -0
  156. package/dist/honesty/undeclared-change.js +56 -0
  157. package/dist/honesty/verified.d.ts +16 -0
  158. package/dist/honesty/verified.js +84 -5
  159. package/dist/http-server.d.ts +0 -1
  160. package/dist/http-server.js +13 -1
  161. package/dist/hunt/hunt-report.d.ts +0 -2
  162. package/dist/hunt/hunt-report.js +1 -1
  163. package/dist/impact/impact-recorder.d.ts +31 -8
  164. package/dist/impact/impact-recorder.js +104 -21
  165. package/dist/impact/impact-store.d.ts +13 -4
  166. package/dist/impact/impact-store.js +44 -3
  167. package/dist/index.d.ts +4 -1
  168. package/dist/index.js +113 -7
  169. package/dist/init/agent-rules.d.ts +30 -6
  170. package/dist/init/agent-rules.js +54 -13
  171. package/dist/init/astro-patch.d.ts +16 -0
  172. package/dist/init/astro-patch.js +28 -0
  173. package/dist/init/capabilities.d.ts +4 -3
  174. package/dist/init/capabilities.js +23 -3
  175. package/dist/init/closing-hint.d.ts +36 -0
  176. package/dist/init/closing-hint.js +120 -0
  177. package/dist/init/confirm.d.ts +27 -28
  178. package/dist/init/confirm.js +51 -11
  179. package/dist/init/cra.d.ts +34 -5
  180. package/dist/init/cra.js +96 -25
  181. package/dist/init/csp-check.js +17 -2
  182. package/dist/init/csp-doctor.d.ts +8 -1
  183. package/dist/init/csp-doctor.js +8 -3
  184. package/dist/init/dev-script.d.ts +50 -0
  185. package/dist/init/dev-script.js +66 -0
  186. package/dist/init/format-generated.d.ts +18 -0
  187. package/dist/init/format-generated.js +39 -0
  188. package/dist/init/install-hint.d.ts +9 -0
  189. package/dist/init/install-hint.js +29 -0
  190. package/dist/init/mcp-clients.d.ts +7 -0
  191. package/dist/init/mcp-clients.js +7 -0
  192. package/dist/init/mcp.d.ts +17 -6
  193. package/dist/init/mcp.js +27 -11
  194. package/dist/init/node-io.js +11 -1
  195. package/dist/init/plan-framework.d.ts +0 -5
  196. package/dist/init/plan-framework.js +147 -37
  197. package/dist/init/plan.d.ts +30 -37
  198. package/dist/init/plan.js +50 -60
  199. package/dist/init/preflight.d.ts +28 -0
  200. package/dist/init/preflight.js +38 -0
  201. package/dist/init/refresh-rules.d.ts +39 -0
  202. package/dist/init/refresh-rules.js +51 -0
  203. package/dist/init/run.d.ts +32 -35
  204. package/dist/init/run.js +151 -202
  205. package/dist/init/slash-command.d.ts +1 -1
  206. package/dist/init/snippets.js +90 -31
  207. package/dist/init/vite-config.d.ts +1 -1
  208. package/dist/init/vite-config.js +39 -25
  209. package/dist/init/workspace-apps.d.ts +17 -10
  210. package/dist/init/workspace-apps.js +110 -0
  211. package/dist/input/network-mock-tools.js +3 -6
  212. package/dist/input/real-input.d.ts +2 -0
  213. package/dist/input/real-input.js +28 -7
  214. package/dist/input/scroll-find.d.ts +7 -4
  215. package/dist/input/scroll-find.js +63 -25
  216. package/dist/input/scroll-tools.js +4 -6
  217. package/dist/input/viewport-tools.js +5 -4
  218. package/dist/intent/inline-intent.d.ts +37 -0
  219. package/dist/intent/inline-intent.js +193 -0
  220. package/dist/intent/intent-shard-store.d.ts +72 -0
  221. package/dist/intent/intent-shard-store.js +203 -0
  222. package/dist/intent/intent-shard.d.ts +411 -0
  223. package/dist/intent/intent-shard.js +176 -0
  224. package/dist/intent/intent-store.d.ts +49 -0
  225. package/dist/intent/intent-store.js +133 -0
  226. package/dist/intent/intent-subject.d.ts +45 -0
  227. package/dist/intent/intent-subject.js +177 -0
  228. package/dist/intent/intent-tools.d.ts +2 -0
  229. package/dist/intent/intent-tools.js +143 -0
  230. package/dist/intent/open-intents.d.ts +42 -0
  231. package/dist/intent/open-intents.js +40 -0
  232. package/dist/journal/envelope.d.ts +26 -26
  233. package/dist/journal/journal-recorder.d.ts +23 -0
  234. package/dist/journal/journal-recorder.js +35 -0
  235. package/dist/license/license-env.d.ts +29 -0
  236. package/dist/license/license-env.js +148 -0
  237. package/dist/loopback-agent.d.ts +31 -0
  238. package/dist/loopback-agent.js +57 -0
  239. package/dist/mcp/attach-memory.d.ts +83 -0
  240. package/dist/mcp/attach-memory.js +185 -0
  241. package/dist/mcp/client-identity.d.ts +31 -0
  242. package/dist/mcp/client-identity.js +51 -0
  243. package/dist/mcp/mcp-post-transport.d.ts +26 -0
  244. package/dist/mcp/mcp-post-transport.js +109 -0
  245. package/dist/mcp/mcp-proxy.d.ts +2 -8
  246. package/dist/mcp/mcp-proxy.js +64 -62
  247. package/dist/mcp/mcp.d.ts +2 -0
  248. package/dist/mcp/mcp.js +52 -24
  249. package/dist/mcp/proxy-backoff.d.ts +11 -0
  250. package/dist/mcp/proxy-backoff.js +13 -0
  251. package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
  252. package/dist/mcp/proxy-daemon-probe.js +7 -1
  253. package/dist/mcp/proxy-handshake.d.ts +23 -0
  254. package/dist/mcp/proxy-handshake.js +31 -0
  255. package/dist/mcp/server-instructions.js +45 -3
  256. package/dist/mcp/tool-catalog-cache.d.ts +16 -0
  257. package/dist/mcp/tool-catalog-cache.js +22 -0
  258. package/dist/memory/memory-tools.d.ts +2 -0
  259. package/dist/memory/memory-tools.js +82 -0
  260. package/dist/memory/project-memory.d.ts +34 -0
  261. package/dist/memory/project-memory.js +96 -0
  262. package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
  263. package/dist/oracles/flow-instrument-gaps.js +4 -5
  264. package/dist/oracles/propose-consequences.js +12 -3
  265. package/dist/oracles/self-instrument.d.ts +9 -9
  266. package/dist/oracles/self-instrument.js +19 -13
  267. package/dist/pool/browser-pool.d.ts +75 -5
  268. package/dist/pool/browser-pool.js +144 -5
  269. package/dist/pool/playwright-launcher.js +12 -1
  270. package/dist/project/fs-port.d.ts +3 -0
  271. package/dist/project/fs-port.js +3 -2
  272. package/dist/project/memory-fs.js +6 -1
  273. package/dist/project/project-tools.js +6 -35
  274. package/dist/project/reticle-dir.d.ts +17 -2
  275. package/dist/project/reticle-dir.js +22 -4
  276. package/dist/runs/context-tools.d.ts +2 -0
  277. package/dist/runs/context-tools.js +89 -0
  278. package/dist/runs/run-context.d.ts +40 -0
  279. package/dist/runs/run-context.js +158 -0
  280. package/dist/runs/run-diff.d.ts +2 -1
  281. package/dist/runs/run-store.d.ts +10 -0
  282. package/dist/runs/run-store.js +10 -0
  283. package/dist/runs/verification-sync.d.ts +0 -11
  284. package/dist/runs/verification-sync.js +24 -3
  285. package/dist/runs/verify-server.d.ts +8 -1
  286. package/dist/runs/verify-server.js +14 -2
  287. package/dist/session/command-timeout.d.ts +5 -2
  288. package/dist/session/command-timeout.js +5 -4
  289. package/dist/session/dev-server-probe.d.ts +0 -8
  290. package/dist/session/dev-server-probe.js +1 -1
  291. package/dist/session/last-act.d.ts +7 -1
  292. package/dist/session/last-act.js +2 -2
  293. package/dist/session/lease-visibility.d.ts +67 -0
  294. package/dist/session/lease-visibility.js +76 -0
  295. package/dist/session/live-control-tools.js +1 -42
  296. package/dist/session/no-session-diagnosis.d.ts +33 -0
  297. package/dist/session/no-session-diagnosis.js +82 -24
  298. package/dist/session/no-session-next-action.d.ts +12 -0
  299. package/dist/session/no-session-next-action.js +77 -6
  300. package/dist/session/no-session-watch.d.ts +18 -6
  301. package/dist/session/no-session-watch.js +125 -75
  302. package/dist/session/output-budget.js +4 -1
  303. package/dist/session/pending-commands.d.ts +11 -1
  304. package/dist/session/pending-commands.js +12 -2
  305. package/dist/session/resolve-within.d.ts +34 -0
  306. package/dist/session/resolve-within.js +53 -0
  307. package/dist/session/session-health.d.ts +9 -0
  308. package/dist/session/session-health.js +23 -0
  309. package/dist/session/session-info.d.ts +18 -0
  310. package/dist/session/session-info.js +11 -0
  311. package/dist/session/session-manager.d.ts +14 -0
  312. package/dist/session/session-manager.js +111 -4
  313. package/dist/session/session-mirror.d.ts +16 -0
  314. package/dist/session/session-mirror.js +22 -0
  315. package/dist/session/session-recommendation.d.ts +19 -5
  316. package/dist/session/session-recommendation.js +25 -9
  317. package/dist/session/session-successor.d.ts +37 -0
  318. package/dist/session/session-successor.js +113 -0
  319. package/dist/session/session-tools.js +2 -2
  320. package/dist/session/session.d.ts +53 -3
  321. package/dist/session/session.js +162 -9
  322. package/dist/session/wall-clock.d.ts +12 -0
  323. package/dist/session/wall-clock.js +14 -0
  324. package/dist/setup/agent-configs.d.ts +96 -0
  325. package/dist/setup/agent-configs.js +333 -0
  326. package/dist/setup/agent-io.d.ts +9 -0
  327. package/dist/setup/agent-io.js +16 -0
  328. package/dist/setup/agent-writer.d.ts +51 -0
  329. package/dist/setup/agent-writer.js +115 -0
  330. package/dist/setup/approval-migration.d.ts +47 -0
  331. package/dist/setup/approval-migration.js +68 -0
  332. package/dist/setup/auto-approve.d.ts +85 -0
  333. package/dist/setup/auto-approve.js +150 -0
  334. package/dist/setup/bridge-port.d.ts +15 -0
  335. package/dist/setup/bridge-port.js +24 -0
  336. package/dist/setup/desktop-shape.d.ts +57 -0
  337. package/dist/setup/desktop-shape.js +63 -0
  338. package/dist/setup/dev-server-wait.d.ts +60 -0
  339. package/dist/setup/dev-server-wait.js +65 -0
  340. package/dist/setup/drive-agent.d.ts +71 -0
  341. package/dist/setup/drive-agent.js +171 -0
  342. package/dist/setup/drive-plan.d.ts +68 -0
  343. package/dist/setup/drive-plan.js +130 -0
  344. package/dist/setup/ensure-daemon.d.ts +29 -0
  345. package/dist/setup/ensure-daemon.js +58 -0
  346. package/dist/setup/init-runtime.d.ts +47 -0
  347. package/dist/setup/init-runtime.js +160 -0
  348. package/dist/setup/interrupt.d.ts +25 -0
  349. package/dist/setup/interrupt.js +40 -0
  350. package/dist/setup/license-key.d.ts +39 -0
  351. package/dist/setup/license-key.js +72 -0
  352. package/dist/setup/listeners.d.ts +37 -0
  353. package/dist/setup/listeners.js +85 -0
  354. package/dist/setup/node-effects.d.ts +60 -0
  355. package/dist/setup/node-effects.js +210 -0
  356. package/dist/setup/page-probe.d.ts +39 -0
  357. package/dist/setup/page-probe.js +49 -0
  358. package/dist/setup/relaunch.d.ts +36 -0
  359. package/dist/setup/relaunch.js +57 -0
  360. package/dist/setup/remaining-steps.d.ts +19 -0
  361. package/dist/setup/remaining-steps.js +31 -0
  362. package/dist/setup/run-setup.d.ts +99 -0
  363. package/dist/setup/run-setup.js +187 -0
  364. package/dist/setup/session-pick.d.ts +47 -0
  365. package/dist/setup/session-pick.js +51 -0
  366. package/dist/setup/setup-command.d.ts +45 -0
  367. package/dist/setup/setup-command.js +199 -0
  368. package/dist/setup/setup-options.d.ts +74 -0
  369. package/dist/setup/setup-options.js +67 -0
  370. package/dist/setup/transcripts.d.ts +16 -0
  371. package/dist/setup/transcripts.js +43 -0
  372. package/dist/status-payload.d.ts +14 -1
  373. package/dist/status-payload.js +23 -1
  374. package/dist/telemetry/automation-hint.d.ts +2 -1
  375. package/dist/telemetry/daemon-telemetry.d.ts +8 -1
  376. package/dist/telemetry/daemon-telemetry.js +52 -5
  377. package/dist/telemetry/error-class.js +1 -1
  378. package/dist/telemetry/feedback-context.d.ts +9 -1
  379. package/dist/telemetry/feedback-context.js +60 -29
  380. package/dist/telemetry/feedback-local.d.ts +15 -0
  381. package/dist/telemetry/feedback-local.js +56 -0
  382. package/dist/telemetry/feedback-outbox.d.ts +2 -1
  383. package/dist/telemetry/feedback.d.ts +11 -0
  384. package/dist/telemetry/feedback.js +19 -6
  385. package/dist/telemetry/init-telemetry.d.ts +2 -0
  386. package/dist/telemetry/init-telemetry.js +2 -0
  387. package/dist/telemetry/install-source.d.ts +14 -0
  388. package/dist/telemetry/install-source.js +30 -0
  389. package/dist/telemetry/license-activation.d.ts +7 -1
  390. package/dist/telemetry/license-activation.js +9 -5
  391. package/dist/telemetry/mcp-connection.js +7 -0
  392. package/dist/telemetry/project-profile.d.ts +12 -9
  393. package/dist/telemetry/project-profile.js +12 -7
  394. package/dist/telemetry/proxy-telemetry.d.ts +3 -0
  395. package/dist/telemetry/proxy-telemetry.js +21 -0
  396. package/dist/telemetry/run-telemetry.d.ts +0 -2
  397. package/dist/telemetry/run-telemetry.js +1 -1
  398. package/dist/telemetry/session-metrics.d.ts +12 -0
  399. package/dist/telemetry/session-metrics.js +35 -1
  400. package/dist/telemetry/telemetry.js +17 -2
  401. package/dist/telemetry/tool-refused.d.ts +3 -1
  402. package/dist/telemetry/tool-refused.js +34 -2
  403. package/dist/telemetry/verification-of.js +2 -2
  404. package/dist/tools/act-capsule.d.ts +10 -0
  405. package/dist/tools/act-capsule.js +8 -2
  406. package/dist/tools/act-danger.d.ts +2 -1
  407. package/dist/tools/act-danger.js +11 -3
  408. package/dist/tools/act-observation.d.ts +41 -0
  409. package/dist/tools/act-observation.js +36 -0
  410. package/dist/tools/act-preflight.d.ts +20 -0
  411. package/dist/tools/act-preflight.js +98 -0
  412. package/dist/tools/act-sequence-retry.d.ts +45 -0
  413. package/dist/tools/act-sequence-retry.js +58 -0
  414. package/dist/tools/act-sequence-tool.d.ts +13 -0
  415. package/dist/tools/act-sequence-tool.js +135 -0
  416. package/dist/tools/act-target.d.ts +14 -0
  417. package/dist/tools/act-target.js +47 -0
  418. package/dist/tools/act-tools.d.ts +21 -1
  419. package/dist/tools/act-tools.js +196 -204
  420. package/dist/tools/assert-source.d.ts +22 -0
  421. package/dist/tools/assert-source.js +53 -0
  422. package/dist/tools/assert-verdict.d.ts +20 -3
  423. package/dist/tools/assert-verdict.js +58 -20
  424. package/dist/tools/browser-tools.js +6 -22
  425. package/dist/tools/complete-snapshot.d.ts +48 -0
  426. package/dist/tools/complete-snapshot.js +75 -0
  427. package/dist/tools/coverage-tools.d.ts +10 -1
  428. package/dist/tools/coverage-tools.js +123 -69
  429. package/dist/tools/dynamic-tools.js +53 -13
  430. package/dist/tools/error-recovery.d.ts +25 -14
  431. package/dist/tools/error-recovery.js +78 -9
  432. package/dist/tools/feedback-tools.d.ts +1 -1
  433. package/dist/tools/feedback-tools.js +2 -2
  434. package/dist/tools/invoke-tool.js +121 -11
  435. package/dist/tools/lease-hint.d.ts +10 -0
  436. package/dist/tools/lease-hint.js +53 -4
  437. package/dist/tools/lease-tools.d.ts +35 -5
  438. package/dist/tools/lease-tools.js +191 -13
  439. package/dist/tools/merged-name-redirect.d.ts +13 -0
  440. package/dist/tools/merged-name-redirect.js +22 -0
  441. package/dist/tools/navigate-arrival.d.ts +0 -2
  442. package/dist/tools/navigate-arrival.js +1 -1
  443. package/dist/tools/numeric-bounds.d.ts +100 -0
  444. package/dist/tools/numeric-bounds.js +125 -0
  445. package/dist/tools/observe-tools.js +109 -65
  446. package/dist/tools/read-tools.js +31 -8
  447. package/dist/tools/real-input-attempt.d.ts +32 -0
  448. package/dist/tools/real-input-attempt.js +212 -2
  449. package/dist/tools/reconcile-tools.js +22 -8
  450. package/dist/tools/resolve-target.d.ts +0 -7
  451. package/dist/tools/sdk-marker-probe.d.ts +29 -0
  452. package/dist/tools/sdk-marker-probe.js +57 -0
  453. package/dist/tools/snapshot-delta.d.ts +1 -1
  454. package/dist/tools/tool-kit.d.ts +24 -0
  455. package/dist/tools/tool-kit.js +21 -0
  456. package/dist/tools/tool-names.d.ts +5 -0
  457. package/dist/tools/tool-names.js +5 -0
  458. package/dist/tools/tool-surface.d.ts +85 -6
  459. package/dist/tools/tool-surface.js +122 -2
  460. package/dist/tools/tools-helpers.d.ts +19 -0
  461. package/dist/tools/tools-helpers.js +46 -0
  462. package/dist/tools/tools.d.ts +2 -0
  463. package/dist/tools/tools.js +78 -12
  464. package/dist/tools/unadvertised-help.js +4 -4
  465. package/dist/tools/verify-next-baton.d.ts +53 -0
  466. package/dist/tools/verify-next-baton.js +52 -0
  467. package/dist/update/reticle-deps.d.ts +9 -0
  468. package/dist/update/reticle-deps.js +27 -0
  469. package/dist/update/sdk-sync.d.ts +1 -2
  470. package/dist/update/sdk-sync.js +1 -21
  471. package/dist/update/update-nudge.d.ts +25 -10
  472. package/dist/update/update-nudge.js +32 -3
  473. package/dist/update/updater.d.ts +1 -1
  474. package/dist/version/peer-announce.js +4 -2
  475. package/dist/version/sdk-fix.d.ts +47 -0
  476. package/dist/version/sdk-fix.js +127 -0
  477. package/dist/version/version-nudge.d.ts +14 -0
  478. package/dist/version/version-nudge.js +21 -0
  479. package/dist/version/version-skew.d.ts +38 -3
  480. package/dist/version/version-skew.js +83 -7
  481. package/dist/visual/visual-store.d.ts +6 -6
  482. package/dist/visual/visual-store.js +18 -16
  483. package/dist/visual/visual-tools.js +57 -12
  484. package/docs/README.md +2 -1
  485. package/docs/cli/cloud.mdx +1 -1
  486. package/docs/cli/doctor.mdx +2 -0
  487. package/docs/cli/serve.mdx +4 -2
  488. package/docs/cli/verify.mdx +4 -5
  489. package/docs/cli.mdx +7 -3
  490. package/docs/desktop.mdx +1 -1
  491. package/docs/docs.json +2 -2
  492. package/docs/gates.md +1 -1
  493. package/docs/getting-started.md +19 -3
  494. package/docs/index.mdx +9 -6
  495. package/docs/install-agentic.mdx +38 -12
  496. package/docs/install-manual.mdx +1 -1
  497. package/docs/packages/browser.mdx +2 -2
  498. package/docs/packages/server.mdx +1 -1
  499. package/docs/packages.mdx +8 -6
  500. package/docs/platform-integration.md +17 -0
  501. package/docs/predicates.mdx +8 -0
  502. package/docs/reticle-manual.md +106 -0
  503. package/docs/skill-file.mdx +3 -3
  504. package/docs/telemetry-contract.md +108 -4
  505. package/docs/tools-act-sequence.mdx +16 -0
  506. package/docs/tools-tools-and-run.mdx +2 -4
  507. package/docs/troubleshooting.mdx +9 -0
  508. package/docs/usage.md +4 -3
  509. package/package.json +4 -3
  510. package/dist/init/cursor.d.ts +0 -24
  511. package/dist/init/cursor.js +0 -80
  512. package/dist/session/session-readiness.d.ts +0 -34
  513. package/dist/session/session-readiness.js +0 -36
@@ -103,6 +103,37 @@ export function toolsChangedNotification(catalogWasLocal) {
103
103
  return null;
104
104
  return JSON.stringify({ jsonrpc: '2.0', method: TOOLS_CHANGED_NOTIFICATION });
105
105
  }
106
+ /**
107
+ * What a locally-answered handshake must say about itself.
108
+ *
109
+ * The proxy answering `initialize` is right — a hang gives the agent no tools AND no diagnosis — but
110
+ * the response it sent was INDISTINGUISHABLE from a daemon's. Same `serverInfo`, same capabilities,
111
+ * same `instructions`. So the client marked the server installed and enabled, the agent got a tool
112
+ * list that was cached at best and empty at worst, and nothing anywhere connected the two facts.
113
+ *
114
+ * Reported from the field, from two different agent hosts at once: the marketplace showed Reticle
115
+ * installed with the whole tool surface enabled, and the agent asked to verify a flow reported that
116
+ * RETICLE WAS NOT PRESENT ON THE MACHINE — while the daemon was running and a browser session was
117
+ * live. That conclusion is the reasonable reading of an empty surface with no explanation attached,
118
+ * and it is unrecoverable: the proxy's own recovery is "the next client request re-probes the port",
119
+ * and a client holding zero tools never makes another request.
120
+ *
121
+ * `instructions` is the channel because it is the only one that survives an empty catalog. It rides
122
+ * on the initialize result, which SUCCEEDED, and every host we target puts it in front of the model.
123
+ * A tool description cannot reach an agent that was handed no tools.
124
+ *
125
+ * The healthy block is kept underneath rather than replaced: an agent that IS about to get a working
126
+ * surface (the common case — the first call wakes a daemon) still needs to know how to drive it.
127
+ */
128
+ export function degradedInstructions(healthy, port, reason) {
129
+ return `DEGRADED SURFACE — READ THIS BEFORE ANY reticle_* CALL: no Reticle daemon answered on port ${String(port)} (${reason}), so this handshake was completed by the Reticle MCP proxy itself, not by Reticle's daemon. Any tool list you were handed came from a cache or is empty — it is not evidence that anything is behind it.
130
+
131
+ Reticle IS installed: this process is Reticle. Do NOT report it as missing, not present, or not installed — that is the wrong diagnosis and it is the one this state reliably produces. The right one is that the daemon is not reachable yet.
132
+
133
+ Your next reticle_* call starts a daemon and then either works or fails with the specific reason. If you have NO reticle_* tools at all, say exactly that to the user and tell them to run \`reticle doctor\` in a terminal — it reports what is holding port ${String(port)} and whether a daemon can start at all.
134
+
135
+ ${healthy}`;
136
+ }
106
137
  /**
107
138
  * The largest single JSON-RPC line the proxy will accumulate from a client.
108
139
  *
@@ -18,11 +18,31 @@ import { SHARED_PARAM_GUIDANCE } from './shared-params.js';
18
18
  * So the lead is state-dependent. A project that has never had an app connect is told the one thing
19
19
  * it needs; a project that has is not nagged about a step it already took. The verdict discipline
20
20
  * and the feedback ask are constant, because they matter in both states.
21
+ *
22
+ * The stopping rule is here for a measured reason. On a HEALTHY app in the competitor benchmark,
23
+ * this agent spent 22 turns and roughly 3.5x the cheapest competitor's tokens confirming that
24
+ * nothing was wrong — and it was not payload, which was the smallest of its five runs. It was
25
+ * turns: three navigations, four queries, a console read, a network read and an assert, on a page
26
+ * whose first verdict had already come back "yes" over a clean capture. Nothing anywhere told it
27
+ * when it was finished. Every other instruction here pushes toward MORE checking, which is right
28
+ * when something is broken and is the whole bill when nothing is.
29
+ *
30
+ * The diagnose-from-source rule has the same provenance and a bigger number behind it. Measured on
31
+ * a fix-and-verify benchmark, split at the call that writes the fix: this agent spent 14 and 18
32
+ * calls BEFORE its first edit where chrome-devtools-mcp spent 8 and 9 — and the sequences say why.
33
+ * It drove the app to understand it (snapshot, act, wait_for, snapshot, act, act_and_wait) and only
34
+ * then opened a file, while the competitor read six files and then looked once. Per-turn cost was
35
+ * IDENTICAL on the hardest cell (22,575 against 22,466), so the whole gap was turns, and most of
36
+ * those turns were spent asking a browser a question that only source can answer.
21
37
  */
22
38
  /** The tools, and the rule that only two of them decide anything. Constant across both states. */
23
- const VERDICT_DISCIPLINE = `Reticle verifies a running web app from the inside: look (reticle_snapshot / reticle_query), act and prove in one hop (reticle_act_and_wait), observe (reticle_state / reticle_network / reticle_console), assert (reticle_assert). Verify a user-facing change against the real app before you call it done, and never weaken a check to make it pass.
39
+ const VERDICT_DISCIPLINE = `Reticle verifies a running web app from the inside: go (reticle_navigate), look (reticle_snapshot / reticle_query), act and prove in one hop (reticle_act_and_wait), observe (reticle_observe / reticle_state / reticle_network / reticle_console), assert (reticle_assert). Verify a user-facing change against the real app before you call it done, and never weaken a check to make it pass.
40
+
41
+ Only reticle_act_and_wait and reticle_assert produce a verdict. reticle_act and everything else move or read the app and prove nothing, so a drive that ends without one of those two has no result however many tools it used. Prefer reticle_act_and_wait({ ref, action, until }) — it names the expected consequence BEFORE the action, which is the difference between a check and a rationalisation. Only verified:"yes" is a pass — "unknown" means Reticle could not tell what happened, "no-fault" means nothing was declared to prove. Report either as not proved.
24
42
 
25
- Only reticle_act_and_wait and reticle_assert produce a verdict. reticle_act and everything else move or read the app and prove nothing, so a drive that ends without one of those two has no result however many tools it used. Prefer reticle_act_and_wait({ ref, action, until }) — it names the expected consequence BEFORE the action, which is the difference between a check and a rationalisation. Only verified:"yes" is a pass — "unknown" means Reticle could not tell what happened, "no-fault" means nothing was declared to prove. Report either as not proved.`;
43
+ A "yes" over a clean capture IS the answer — re-reading the page after one finds the same state. When every consequence you set out to check has one, stop.
44
+
45
+ DIAGNOSING a bug? Read the source first — Reticle proves what the app DOES, not why. Drive to CONFIRM a fix, not to find one.`;
26
46
  /**
27
47
  * The first move, for a project no app has ever connected to.
28
48
  *
@@ -35,6 +55,28 @@ Only reticle_act_and_wait and reticle_assert produce a verdict. reticle_act and
35
55
  const FIRST_MOVE = `FIRST: no app has ever connected to Reticle in this project, so nothing here can be verified yet. Having these tools is not the same as being set up — the tools reach a daemon, and the daemon has nothing to look at until the app itself is instrumented.
36
56
 
37
57
  Fix that before anything else: run \`npx @reticlehq/server init\` in the project, then RESTART the dev server (a plugin added to a config the running server already read is not in the bundle), then load the app in a browser. Confirm with reticle_sessions — a session listed there is the proof, and until one appears no other tool can tell you anything about this app.`;
58
+ /**
59
+ * When to reach for the tools that are advertised but never explained, and how to reach the two
60
+ * that are explained but not advertised. Both halves of the same defect.
61
+ *
62
+ * An advertised tool arrives as a name and one line of description. That is enough to USE and not
63
+ * enough to CHOOSE, and `reticle_observe` is the case that proves it costs something: the
64
+ * measurement recorded on TOOL_SURFACE.VERIFY is that dropping the observation tools TRIPLED false
65
+ * alarms, because the model stops observing and reaches for the verdict without the evidence. So
66
+ * observe gets a reason, not a listing.
67
+ *
68
+ * The other direction: `reticle_context` and `reticle_intent` sit on the EXTENDED surface, so they
69
+ * are not in the list an agent is handed. Naming them without their call shape sent an agent at a
70
+ * tool it could not call, which is worse than silence — it teaches distrust of everything else
71
+ * here. They are now written the way the skill writes them, as the `reticle_run` call that works.
72
+ *
73
+ * Only in the connected state, and that is not a budget trick: an agent whose app has never
74
+ * connected has nothing to observe, no run to carry forward and nothing to declare an intent
75
+ * against. Its one instruction is the first move, and adding a second would compete with it.
76
+ */
77
+ const REACH_FOR = `reticle_observe is the evidence channel: everything the page did since a cursor, in one read. Take it BEFORE you call a build broken — measured, stripping the observation tools TRIPLED false alarms, because an agent that stops observing reaches for the verdict without the evidence. reticle_wait_for settles a page that is changing without you; reticle_inspect maps an element to its source file:line, which turns a finding into an edit; reticle_session {action:"yield"} hands the tab back to the human.
78
+
79
+ Everything else is one hop: reticle_tools lists it, reticle_run calls it. Two to know early: reticle_run({ tool: "reticle_context", args: {} }) returns what this run already established, after a compaction or handover, and reticle_run({ tool: "reticle_intent", args: { action: "declare", ... } }) declares what a change was MEANT to do, so the verdict has something to check against other than itself.`;
38
80
  /** The feedback ask. Constant: an agent that cannot get set up is the report we most need. */
39
81
  const FEEDBACK_ASK = `Feedback is first-class here, not a courtesy. Reticle is built FOR agents, so your experience is the only signal that decides what gets built or fixed — and it disappears when your context does. Send it with \`reticle_feedback\` the moment you notice, then carry on with your task:
40
82
  - Reticle broke, lied, or could not see something you needed (\`bug\` / \`gap\` / \`ambiguity\`).
@@ -50,7 +92,7 @@ Nothing is too minor, and a report costs one call. If the tools are unreachable
50
92
  */
51
93
  export function buildServerInstructions(state) {
52
94
  const base = state.previouslyConnected
53
- ? `${VERDICT_DISCIPLINE}\n\n${FEEDBACK_ASK}`
95
+ ? `${VERDICT_DISCIPLINE}\n\n${REACH_FOR}\n\n${FEEDBACK_ASK}`
54
96
  : `${FIRST_MOVE}\n\n${VERDICT_DISCIPLINE}\n\n${FEEDBACK_ASK}`;
55
97
  return `${base}\n\n${SHARED_PARAM_GUIDANCE}`;
56
98
  }
@@ -15,6 +15,22 @@
15
15
  * its own confidently-wrong answer, and a proxy that has never seen one has nothing honest to serve —
16
16
  * so it says nothing rather than inventing a list the agent would then call into.
17
17
  */
18
+ /**
19
+ * Is this client line a request for the tool list?
20
+ *
21
+ * Lives here because this module already owns the method name and the tolerant parse. The caller is
22
+ * the proxy's stdin reader, which records the arrival as the one observable fact behind `reticle
23
+ * status`'s "registered, never enumerated" state — see attach-memory.ts.
24
+ */
25
+ export declare function isToolsListRequest(line: string): boolean;
26
+ /**
27
+ * Is this line a client CALLING a tool?
28
+ *
29
+ * The one fact that separates a working agent link from a merely-registered one. Listing the tools
30
+ * proves a client read the catalogue; it proves nothing about whether a request can cross to the
31
+ * daemon, and the field reports those two states looking identical from every other angle.
32
+ */
33
+ export declare function isToolCallRequest(line: string): boolean;
18
34
  export declare class ToolCatalogCache {
19
35
  #private;
20
36
  /** Watch a line coming BACK from the daemon; remember it if it is a tool catalog. */
@@ -25,6 +25,28 @@ function parse(line) {
25
25
  return null;
26
26
  }
27
27
  }
28
+ /**
29
+ * Is this client line a request for the tool list?
30
+ *
31
+ * Lives here because this module already owns the method name and the tolerant parse. The caller is
32
+ * the proxy's stdin reader, which records the arrival as the one observable fact behind `reticle
33
+ * status`'s "registered, never enumerated" state — see attach-memory.ts.
34
+ */
35
+ export function isToolsListRequest(line) {
36
+ return parse(line)?.method === LIST_METHOD;
37
+ }
38
+ /** The JSON-RPC method a client sends to actually USE a tool, as opposed to listing them. */
39
+ const CALL_METHOD = 'tools/call';
40
+ /**
41
+ * Is this line a client CALLING a tool?
42
+ *
43
+ * The one fact that separates a working agent link from a merely-registered one. Listing the tools
44
+ * proves a client read the catalogue; it proves nothing about whether a request can cross to the
45
+ * daemon, and the field reports those two states looking identical from every other angle.
46
+ */
47
+ export function isToolCallRequest(line) {
48
+ return parse(line)?.method === CALL_METHOD;
49
+ }
28
50
  export class ToolCatalogCache {
29
51
  #tools;
30
52
  /** Watch a line coming BACK from the daemon; remember it if it is a tool catalog. */
@@ -0,0 +1,2 @@
1
+ import type { ToolDef } from '../tools/tools.js';
2
+ export declare const MEMORY_TOOLS: ToolDef[];
@@ -0,0 +1,82 @@
1
+ /**
2
+ * `reticle_memory` — ask the project what it already knows.
3
+ *
4
+ * Shared memory reached three callers before this one: the sync writes it, flow replay consults it
5
+ * automatically, and `reticle memory` prints it at a terminal. The one who could not ask was the
6
+ * agent doing the work — mid-drive, holding a question, with a whole tool surface and nothing on it
7
+ * for "what has this team already established about checkout?".
8
+ *
9
+ * That matters twice over. An agent that can read the corpus stops re-deriving what a colleague
10
+ * proved last week; and because the read is attributed, the coverage map's fetch counts finally
11
+ * measure something — they were zero across an entire real corpus, not because the knowledge was
12
+ * useless but because consulting it was a separate act nobody performed.
13
+ */
14
+ import { z } from 'zod';
15
+ import { ReticleTool } from '../tools/tool-names.js';
16
+ import { countSchema } from '../tools/numeric-bounds.js';
17
+ import { sessionIdShape } from '../tools/tool-kit.js';
18
+ import { asNumber, asString } from '../tools/tools-helpers.js';
19
+ import { sessionRoot } from '../project/session-root.js';
20
+ import { MemoryUnavailable, readProjectMemory } from './project-memory.js';
21
+ import { homedir } from 'node:os';
22
+ /**
23
+ * Enough to orient on, not enough to flood a context.
24
+ *
25
+ * A busy subject on a real project runs to dozens of statements; an agent asking a question wants
26
+ * the ones that settle it. `total` always rides along, so the cap is never silent.
27
+ */
28
+ const DEFAULT_LIMIT = 10;
29
+ /** What to do next, per reason. A refusal that does not say how to fix it is a dead end. */
30
+ const ADVICE = {
31
+ [MemoryUnavailable.NOT_LINKED]: 'this project is not linked to a Reticle workspace, so there is no shared memory to read. Run `npx @reticlehq/server login` in the project to link it.',
32
+ [MemoryUnavailable.DISABLED]: 'this project is linked but memory sync is turned off in .reticle/cloud.json, so nothing is being shared. Enable `sync.memory` to use it.',
33
+ [MemoryUnavailable.UNREACHABLE]: 'the workspace could not be reached, or refused the key. Nothing is wrong with this project — try again, or check the link with `reticle status`.',
34
+ };
35
+ export const MEMORY_TOOLS = [
36
+ {
37
+ name: ReticleTool.MEMORY,
38
+ description: 'Ask what THIS PROJECT already knows — the shared memory its team has built up, written by every engineer who verified something and readable by every agent since. Use it BEFORE re-deriving how a feature is supposed to behave: a colleague may already have proved it, and a statement marked `proved` was established by a real verdict rather than written down. With { subject } it narrows to one area (a flow name, or a route like "checkout"); without, it returns across the project. Each entry carries the flow and the source file it is about, so a claim can be checked. The read is recorded as an agent consultation, which is what the dashboard\'s per-flow fetch counts are built from. Returns { subject, known, total } or { error, reason, advice } when this project is not linked to a workspace.',
39
+ inputSchema: {
40
+ subject: z
41
+ .string()
42
+ .optional()
43
+ .describe('Narrow to one area — a flow name ("checkout-pay") or a route segment ("issues"). Omit to read across the whole project. Subjects come from the coverage map; an unknown one returns nothing rather than erroring.'),
44
+ limit: countSchema
45
+ .optional()
46
+ .describe(`Most relevant N statements. Defaults to ${String(DEFAULT_LIMIT)}. Proved statements rank first, so a small limit still returns the settled ones. \`total\` always reports the true count.`),
47
+ ...sessionIdShape,
48
+ },
49
+ outputSchema: {
50
+ subject: z.string().nullable().optional(),
51
+ known: z.array(z.unknown()).optional(),
52
+ total: z.number().optional(),
53
+ error: z.string().optional(),
54
+ reason: z.string().optional(),
55
+ advice: z.string().optional(),
56
+ },
57
+ handler: async (deps, args) => {
58
+ /*
59
+ * The APP's root, not the daemon's.
60
+ *
61
+ * `deps.reticleRoot` is wherever the daemon was launched, which for a user-scoped MCP
62
+ * registration is almost never the project being driven — so the link file it reads is the
63
+ * wrong one or absent, and every project silently reads as "not attached". The same defect
64
+ * has now been fixed three times in this codebase; routing through sessionRoot is what stops
65
+ * a fourth.
66
+ */
67
+ const root = sessionRoot(deps, asString(args['sessionId']));
68
+ const subject = asString(args['subject']);
69
+ const result = await readProjectMemory(deps.fs, root, homedir(), process.env, {
70
+ subject,
71
+ limit: asNumber(args['limit']) ?? DEFAULT_LIMIT,
72
+ });
73
+ if (!result.ok)
74
+ return {
75
+ error: 'no shared memory available for this project',
76
+ reason: result.reason,
77
+ advice: ADVICE[result.reason],
78
+ };
79
+ return { subject: result.subject, known: result.known, total: result.total };
80
+ },
81
+ },
82
+ ];
@@ -0,0 +1,34 @@
1
+ import type { FileSystemPort } from '../project/fs-port.js';
2
+ /** Named so a read from the tool surface is distinguishable from the CLI's and from replay's. */
3
+ export declare const MCP_AGENT_ID = "reticle-mcp";
4
+ /** Why a lookup returned nothing. Each one is a different thing for the agent to do next. */
5
+ export declare const MemoryUnavailable: {
6
+ /** No `.reticle/cloud.json` — this project has never been linked to a workspace. */
7
+ readonly NOT_LINKED: "not-linked";
8
+ /** Linked, but the link says not to sync memory. A setting, not a failure. */
9
+ readonly DISABLED: "memory-sync-disabled";
10
+ /** The server was reached and said no, or could not be reached at all. */
11
+ readonly UNREACHABLE: "unreachable";
12
+ };
13
+ export type MemoryUnavailable = (typeof MemoryUnavailable)[keyof typeof MemoryUnavailable];
14
+ export interface KnownThing {
15
+ statement: string;
16
+ status: string;
17
+ flowName: string | null;
18
+ sourceFile: string | null;
19
+ subject: string;
20
+ }
21
+ export type ProjectMemoryResult = {
22
+ ok: true;
23
+ subject: string | null;
24
+ known: KnownThing[];
25
+ total: number;
26
+ } | {
27
+ ok: false;
28
+ reason: MemoryUnavailable;
29
+ };
30
+ export declare const rankKnown: (things: readonly KnownThing[]) => KnownThing[];
31
+ export declare function readProjectMemory(fs: FileSystemPort, root: string, home: string, env: NodeJS.ProcessEnv, opts: {
32
+ subject?: string | undefined;
33
+ limit: number;
34
+ }): Promise<ProjectMemoryResult>;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * What the PROJECT knows, read on demand.
3
+ *
4
+ * Shared memory reached three places before this one: the sync writes it, flow replay consults it
5
+ * automatically, and `reticle memory` prints it at a terminal. The one caller who could not ask was
6
+ * the agent actually doing the work — mid-drive, holding a question, with 44 tools and none of them
7
+ * "what does this team already know about checkout?". That is the gap this closes, and it is the
8
+ * difference between a corpus and a wiki nobody opens.
9
+ *
10
+ * The read is ATTRIBUTED. It carries an agent header, so the coverage map counts it as a
11
+ * consultation and a manager can see which flows are actually being pulled and by whom — the
12
+ * question the fetch counters exist to answer and could not while nothing was reading.
13
+ */
14
+ import { cloudFetch } from '../cloud/cloud-sync.js';
15
+ import { resolveProjectCloud } from '../cloud/cloud-config.js';
16
+ /** Named so a read from the tool surface is distinguishable from the CLI's and from replay's. */
17
+ export const MCP_AGENT_ID = 'reticle-mcp';
18
+ /** Why a lookup returned nothing. Each one is a different thing for the agent to do next. */
19
+ export const MemoryUnavailable = {
20
+ /** No `.reticle/cloud.json` — this project has never been linked to a workspace. */
21
+ NOT_LINKED: 'not-linked',
22
+ /** Linked, but the link says not to sync memory. A setting, not a failure. */
23
+ DISABLED: 'memory-sync-disabled',
24
+ /** The server was reached and said no, or could not be reached at all. */
25
+ UNREACHABLE: 'unreachable',
26
+ };
27
+ /** Defensive: the wire is somebody else's server, so every field is checked before it is trusted. */
28
+ const asKnown = (raw) => {
29
+ if (null === raw || typeof raw !== 'object')
30
+ return null;
31
+ const r = raw;
32
+ const statement = r['statement'];
33
+ if (typeof statement !== 'string' || 0 === statement.length)
34
+ return null;
35
+ return {
36
+ statement,
37
+ status: 'string' === typeof r['status'] ? r['status'] : 'unknown',
38
+ flowName: 'string' === typeof r['flowName'] ? r['flowName'] : null,
39
+ sourceFile: 'string' === typeof r['sourceFile'] ? r['sourceFile'] : null,
40
+ subject: 'string' === typeof r['subject'] ? r['subject'] : 'unsorted',
41
+ };
42
+ };
43
+ /**
44
+ * Proved first, then the rest.
45
+ *
46
+ * An agent reading this is about to act on it, and a statement a verdict has actually established
47
+ * is worth more than one somebody merely wrote down. Stable within each group so the same call
48
+ * twice returns the same order — an unstable list reads as the corpus churning when it has not.
49
+ */
50
+ const PROVED = 'proved';
51
+ export const rankKnown = (things) => [
52
+ ...things.filter((t) => PROVED === t.status),
53
+ ...things.filter((t) => PROVED !== t.status),
54
+ ];
55
+ export async function readProjectMemory(fs, root, home, env, opts) {
56
+ const cloud = await resolveProjectCloud(fs, root, home, env);
57
+ if (null === cloud.config)
58
+ return { ok: false, reason: MemoryUnavailable.NOT_LINKED };
59
+ if (!cloud.policy.memory)
60
+ return { ok: false, reason: MemoryUnavailable.DISABLED };
61
+ const query = opts.subject === undefined ? '' : `?subject=${encodeURIComponent(opts.subject)}`;
62
+ try {
63
+ const res = await cloudFetch(`${cloud.config.url}/v1/memory${query}`, {
64
+ method: 'GET',
65
+ headers: {
66
+ authorization: `Bearer ${cloud.config.apiKey}`,
67
+ // What makes this read COUNT as an agent consulting the corpus rather than a human
68
+ // browsing — the distinction the dashboard's "times an agent consulted it" rests on.
69
+ 'x-reticle-agent': MCP_AGENT_ID,
70
+ },
71
+ });
72
+ if (200 !== res.status)
73
+ return { ok: false, reason: MemoryUnavailable.UNREACHABLE };
74
+ // `cloudFetch` returns a real Response, so the body is a METHOD. Reading `res.json` as a
75
+ // property yields the function, and every downstream field is undefined — a silent "the project
76
+ // knows nothing" that is indistinguishable from the honest empty case. It cost a live drive to
77
+ // find once already.
78
+ const body = (await res.json());
79
+ const entries = body?.entries;
80
+ if (!Array.isArray(entries))
81
+ return { ok: false, reason: MemoryUnavailable.UNREACHABLE };
82
+ const known = entries.map(asKnown).filter((k) => k !== null);
83
+ return {
84
+ ok: true,
85
+ subject: opts.subject ?? null,
86
+ // Capped for the agent's context, but `total` always reports the truth so the cap is never
87
+ // silent — a truncated list that looks complete is how an agent concludes a project knows
88
+ // less than it does.
89
+ known: rankKnown(known).slice(0, opts.limit),
90
+ total: known.length,
91
+ };
92
+ }
93
+ catch {
94
+ return { ok: false, reason: MemoryUnavailable.UNREACHABLE };
95
+ }
96
+ }
@@ -1,5 +1,5 @@
1
1
  import { type FlowStep } from '@reticlehq/core';
2
- import { type InstrumentationGap } from './self-instrument.js';
2
+ import type { LocatedGap } from './self-instrument.js';
3
3
  /**
4
4
  * Bridge domain gaps → LOCATED instrumentation gaps. A flow that asserts no consequence should emit one —
5
5
  * and the flow's own step anchors carry the source file:line the babel/next plugin stamped, so we know
@@ -12,7 +12,7 @@ export declare function lastSource(steps: readonly FlowStep[]): {
12
12
  line: number;
13
13
  } | undefined;
14
14
  /**
15
- * Turn unasserted flows into missing-signal gaps at each flow's source location. Flows with no stamped
16
- * source are skipped (no location to propose). The suggested signal name is `<flow>:done`.
15
+ * Turn unasserted flows into no-signal-on-mutation gaps at each flow's source location. Flows with
16
+ * no stamped source are skipped (no location to propose). The suggested signal name is `<flow>:done`.
17
17
  */
18
- export declare function instrumentationGapsForFlows(unassertedFlows: readonly string[], flowStepsByName: ReadonlyMap<string, readonly FlowStep[]>): InstrumentationGap[];
18
+ export declare function instrumentationGapsForFlows(unassertedFlows: readonly string[], flowStepsByName: ReadonlyMap<string, readonly FlowStep[]>): LocatedGap[];
@@ -1,5 +1,4 @@
1
- import { AnchorKind } from '@reticlehq/core';
2
- import { InstrumentationGapKind } from './self-instrument.js';
1
+ import { AnchorKind, InstrumentationGapKind } from '@reticlehq/core';
3
2
  /**
4
3
  * Bridge domain gaps → LOCATED instrumentation gaps. A flow that asserts no consequence should emit one —
5
4
  * and the flow's own step anchors carry the source file:line the babel/next plugin stamped, so we know
@@ -22,8 +21,8 @@ export function lastSource(steps) {
22
21
  return found;
23
22
  }
24
23
  /**
25
- * Turn unasserted flows into missing-signal gaps at each flow's source location. Flows with no stamped
26
- * source are skipped (no location to propose). The suggested signal name is `<flow>:done`.
24
+ * Turn unasserted flows into no-signal-on-mutation gaps at each flow's source location. Flows with
25
+ * no stamped source are skipped (no location to propose). The suggested signal name is `<flow>:done`.
27
26
  */
28
27
  export function instrumentationGapsForFlows(unassertedFlows, flowStepsByName) {
29
28
  const gaps = [];
@@ -33,7 +32,7 @@ export function instrumentationGapsForFlows(unassertedFlows, flowStepsByName) {
33
32
  if (source === undefined)
34
33
  continue;
35
34
  gaps.push({
36
- kind: InstrumentationGapKind.MISSING_SIGNAL,
35
+ kind: InstrumentationGapKind.NO_SIGNAL_ON_MUTATION,
37
36
  file: source.file,
38
37
  line: source.line,
39
38
  name: `${name}:done`,
@@ -1,4 +1,5 @@
1
1
  import { EventType, PredicateKind } from '@reticlehq/core';
2
+ import { routePathOf } from '../events/predicate-route.js';
2
3
  const TIER = { SIGNAL: 0, CONSEQUENCE: 1, PRESENCE: 2 };
3
4
  function pathname(url) {
4
5
  if (typeof url !== 'string')
@@ -66,10 +67,18 @@ export function proposeConsequences(events) {
66
67
  break;
67
68
  }
68
69
  case EventType.ROUTE_CHANGE: {
69
- const to = data['pathname'];
70
- if ('string' === typeof to) {
70
+ const docPath = data['pathname'];
71
+ if ('string' === typeof docPath) {
72
+ // `pathname`, not `to`: the evaluator has never accepted `to`, so this surface was handing
73
+ // the agent a predicate reticle_assert refuses with "unknown field to". A proposal that
74
+ // cannot be evaluated is worse than no proposal — see the schema check in the test.
75
+ //
76
+ // And the ROUTER's path, not the document's: under a hash router the document pathname is
77
+ // `/` on every page, so proposing it proposes an assertion that says nothing about where
78
+ // the app went.
79
+ const to = routePathOf(docPath, 'string' === typeof data['hash'] ? data['hash'] : '');
71
80
  add(`route:${to}`, {
72
- predicate: { kind: PredicateKind.ROUTE, to },
81
+ predicate: { kind: PredicateKind.ROUTE, pathname: to },
73
82
  tier: TIER.CONSEQUENCE,
74
83
  label: `route changes to ${to}`,
75
84
  weak: false,
@@ -4,15 +4,15 @@
4
4
  * apply instrumentation proposals as concrete diffs, so the AGENT closes the gap and the app's
5
5
  * observability compounds run over run — instead of a human being a prerequisite. Deterministic mapping
6
6
  * from a located gap to the code to insert; pure and testable. Result-enrichment wiring is a later step.
7
+ *
8
+ * Kinds come from `@reticlehq/core`. A second enum here is how `reticle_domain` used to say
9
+ * `missing-signal` while honesty said `no-signal-on-mutation` for the same absence.
7
10
  */
8
- export declare const InstrumentationGapKind: {
9
- readonly MISSING_SIGNAL: "missing-signal";
10
- readonly UNREGISTERED_STORE: "unregistered-store";
11
- readonly MISSING_TESTID: "missing-testid";
12
- };
13
- export type InstrumentationGapKind = (typeof InstrumentationGapKind)[keyof typeof InstrumentationGapKind];
14
- export interface InstrumentationGap {
15
- kind: InstrumentationGapKind;
11
+ import { InstrumentationGapKind } from '@reticlehq/core';
12
+ /** The three core kinds a located proposal can close. Other kinds are verdict-side, not insert-side. */
13
+ export type LocatableGapKind = typeof InstrumentationGapKind.NO_SIGNAL_ON_MUTATION | typeof InstrumentationGapKind.NO_STORE_REGISTERED | typeof InstrumentationGapKind.MISSING_TESTID;
14
+ export interface LocatedGap {
15
+ kind: LocatableGapKind;
16
16
  file: string;
17
17
  line: number;
18
18
  /** The name to instrument with — a signal name, store name, or testid. */
@@ -28,5 +28,5 @@ interface InstrumentationProposal {
28
28
  rationale: string;
29
29
  }
30
30
  /** Turn located capability gaps into ready-to-apply instrumentation proposals. Deterministic. */
31
- export declare function proposeInstrumentation(gaps: readonly InstrumentationGap[]): InstrumentationProposal[];
31
+ export declare function proposeInstrumentation(gaps: readonly LocatedGap[]): InstrumentationProposal[];
32
32
  export {};
@@ -4,33 +4,39 @@
4
4
  * apply instrumentation proposals as concrete diffs, so the AGENT closes the gap and the app's
5
5
  * observability compounds run over run — instead of a human being a prerequisite. Deterministic mapping
6
6
  * from a located gap to the code to insert; pure and testable. Result-enrichment wiring is a later step.
7
+ *
8
+ * Kinds come from `@reticlehq/core`. A second enum here is how `reticle_domain` used to say
9
+ * `missing-signal` while honesty said `no-signal-on-mutation` for the same absence.
7
10
  */
8
- export const InstrumentationGapKind = {
9
- MISSING_SIGNAL: 'missing-signal',
10
- UNREGISTERED_STORE: 'unregistered-store',
11
- MISSING_TESTID: 'missing-testid',
12
- };
11
+ import { InstrumentationGapKind } from '@reticlehq/core';
13
12
  /** PascalCase a store name for a `useX.getState` hint (cart → Cart). */
14
13
  function pascal(name) {
15
14
  return 0 === name.length ? name : name[0]?.toUpperCase() + name.slice(1);
16
15
  }
17
16
  function insertFor(gap) {
18
17
  switch (gap.kind) {
19
- case InstrumentationGapKind.MISSING_SIGNAL:
18
+ case InstrumentationGapKind.NO_SIGNAL_ON_MUTATION:
20
19
  return `reticle.signal('${gap.name}');`;
21
- case InstrumentationGapKind.UNREGISTERED_STORE:
20
+ case InstrumentationGapKind.NO_STORE_REGISTERED:
22
21
  return `registerStore('${gap.name}', () => use${pascal(gap.name)}.getState());`;
23
22
  case InstrumentationGapKind.MISSING_TESTID:
24
23
  return `data-testid="${gap.name}"`;
25
24
  }
26
25
  }
27
26
  function rationaleFor(gap) {
28
- const base = gap.kind === InstrumentationGapKind.MISSING_SIGNAL
29
- ? `emit a consequence signal so verification proves the outcome, not just presence`
30
- : gap.kind === InstrumentationGapKind.UNREGISTERED_STORE
31
- ? `register the store so state assertions can read the source of truth`
32
- : `add a stable testid so the anchor survives refactors`;
33
- return gap.context === undefined ? base : `${base} — ${gap.context}`;
27
+ let base;
28
+ switch (gap.kind) {
29
+ case InstrumentationGapKind.NO_SIGNAL_ON_MUTATION:
30
+ base = `emit a consequence signal so verification proves the outcome, not just presence`;
31
+ break;
32
+ case InstrumentationGapKind.NO_STORE_REGISTERED:
33
+ base = `register the store so state assertions can read the source of truth`;
34
+ break;
35
+ case InstrumentationGapKind.MISSING_TESTID:
36
+ base = `add a stable testid so the anchor survives refactors`;
37
+ break;
38
+ }
39
+ return undefined === gap.context ? base : `${base} — ${gap.context}`;
34
40
  }
35
41
  /** Turn located capability gaps into ready-to-apply instrumentation proposals. Deterministic. */
36
42
  export function proposeInstrumentation(gaps) {