@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
@@ -0,0 +1,89 @@
1
+ import { z } from 'zod';
2
+ import {} from '@reticlehq/core';
3
+ import { runContextFor } from './run-context.js';
4
+ import { openSessionIntents } from '../intent/open-intents.js';
5
+ import { ReticleTool } from '../tools/tool-names.js';
6
+ import { sessionIdShape } from '../tools/tool-kit.js';
7
+ import { asString } from '../tools/tools-helpers.js';
8
+ /**
9
+ * What this run has established, asked for by the one party that knows when it is needed.
10
+ *
11
+ * Reticle holds the trajectory: the journal, the verdicts, the intent ledger. The agent holds a copy
12
+ * in its context window, and that copy is the one that disappears — at a compaction, at the end of a
13
+ * turn, at the handover to a sub-agent. Reticle's does not disappear at the same moment. So this is
14
+ * not a competing memory; it is the ground truth compaction destroyed, handed back on request.
15
+ *
16
+ * ## Why it is PULLED
17
+ *
18
+ * The same content was once pushed onto every session-bound tool response. It cost +136% on a
19
+ * verdict and was cut, because most of the time the agent still had the context and we were paying
20
+ * on every call to duplicate what it knew. Nobody but the agent can tell when that stops being true.
21
+ * So the agent asks, once, at the moment it knows its own memory is gone, and pays once.
22
+ *
23
+ * ## Why it is on the EXTENDED surface rather than the default
24
+ *
25
+ * The default surface is a hard COUNT — editors budget tools across every connected MCP server, so a
26
+ * slot taken here is taken from a tool that has already earned it, and nothing on that list has been
27
+ * demoted for this. The claim behind this tool is unmeasured, which is the same test `reticle_intent`
28
+ * and the `reticle_capabilities` demotion were held to, and it fails that test today.
29
+ *
30
+ * The reachability argument is also stronger here than for most of the cold tail. The caller is,
31
+ * by construction, an agent that has just lost its context and is re-reading the tool list it was
32
+ * handed this turn — so `reticle_tools` (advertised on every surface) is exactly the call it is
33
+ * already going to make, and this tool is one `reticle_run` hop behind it. A tool that is only useful
34
+ * after a discovery step, to a caller who is already performing a discovery step, is the cheapest
35
+ * possible thing to leave off the hot list. It moves to the default surface when a measurement says
36
+ * the pull happens often enough to be worth a permanent slot, and not before.
37
+ */
38
+ /** What the run has established, and where each part of it came from. */
39
+ const CONTEXT_OUTPUT_SCHEMA = {
40
+ step: z
41
+ .number()
42
+ .describe('How many actions this run has dispatched. Counted off the journal, not a counter of its own.'),
43
+ established: z
44
+ .array(z.unknown())
45
+ .describe('What Reticle OBSERVED, folded and capped, each `{ key, fact, source?, doc?, epoch? }`. `key` is the subject, so re-observing it supersedes rather than appends. Anything seen under a replaced document or a pre-edit epoch is already gone from this list.'),
46
+ proven: z
47
+ .array(z.unknown())
48
+ .describe('Claims a verdict already settled, each `{ claim, verified, source?, doc?, epoch? }`. Read this before re-driving something: re-proving it is slow, and assuming it is a false green.'),
49
+ remaining: z
50
+ .array(z.string())
51
+ .describe('The intents from .reticle/intent.json that no verdict has discharged. Derived from the ledger, never a guess at what you meant to do next.'),
52
+ };
53
+ /** The ledger and the live page state this run is folded out of. `[]`/undefined when nothing is connected. */
54
+ async function evidenceFor(deps, sessionId) {
55
+ try {
56
+ const session = deps.sessions.resolve(sessionId);
57
+ return {
58
+ actions: await session.readJournalActions(),
59
+ events: await session.queryEvents({}),
60
+ currentDocumentId: session.currentDocumentId,
61
+ currentEditEpoch: session.currentEditEpoch,
62
+ };
63
+ }
64
+ catch {
65
+ // `resolve` throws when nothing is connected, when the id names no session, and when several are
66
+ // connected and none was named. All three mean the same thing here: there is no run to report,
67
+ // and an empty context is the honest answer rather than a reason to fail the call.
68
+ return {
69
+ actions: [],
70
+ events: [],
71
+ currentDocumentId: undefined,
72
+ currentEditEpoch: undefined,
73
+ };
74
+ }
75
+ }
76
+ export const CONTEXT_TOOLS = [
77
+ {
78
+ name: ReticleTool.CONTEXT,
79
+ description: 'What THIS run has already established, so you do not rediscover it. Call it when your own copy is gone: right after a compaction, at the top of a fresh sub-agent, or at the start of a turn you did not begin. Returns `established` (what Reticle OBSERVED, with the source file:line where one was reported), `proven` (claims a verdict already settled, so you neither re-prove them nor assume them) and `remaining` (the intents from .reticle/intent.json that nothing has discharged). It is a FOLD over the journal, never a second store, so it cannot disagree with the ledger. Bounded and superseding rather than accumulating, and anything observed under a replaced document or before your last source edit is already dropped rather than presented as current. Only what Reticle observed goes in: it never reports what you intended or believed.',
80
+ example: {},
81
+ inputSchema: { ...sessionIdShape },
82
+ outputSchema: CONTEXT_OUTPUT_SCHEMA,
83
+ handler: async (deps, args) => {
84
+ const sessionId = asString(args['sessionId']);
85
+ const evidence = await evidenceFor(deps, sessionId);
86
+ return runContextFor({ ...evidence, intents: await openSessionIntents(deps, sessionId) });
87
+ },
88
+ },
89
+ ];
@@ -0,0 +1,40 @@
1
+ import { type EstablishedFact, type Intent, type JournalAction, type ProvenClaim, type ReticleEvent, type RunContext } from '@reticlehq/core';
2
+ /**
3
+ * The SUBJECT a journal action was about: the ref it spent, else the target it resolved from.
4
+ *
5
+ * Exported so the feature-use instrument files a re-fetch under the same key this fold establishes
6
+ * it under. A second copy of this rule there would be a second definition of "the same subject".
7
+ */
8
+ export declare function subjectOf(action: JournalAction): string | undefined;
9
+ /**
10
+ * Fold the journal into candidate facts, newest last.
11
+ *
12
+ * Only the last `RUN_ESTABLISHED_CAP` actions are read: anything older cannot survive the fold, so
13
+ * parsing it would be work done to throw away. An action with no observed settle outcome states
14
+ * nothing and is skipped — "we drove it and cannot say what happened" is not an established fact,
15
+ * and writing it down as one is how a memory starts lying.
16
+ */
17
+ export declare function establishedFromJournal(actions: readonly JournalAction[], events: readonly ReticleEvent[]): EstablishedFact[];
18
+ /**
19
+ * What a verdict already settled, newest last.
20
+ *
21
+ * Both verdict tools write here. `reticle_act_and_wait` closes its attribution window with one;
22
+ * `reticle_assert` drives nothing, so it appends its record WITHOUT opening a window — same effect
23
+ * shape, so this fold reads one kind of verdict rather than two.
24
+ */
25
+ export declare function provenFromJournal(actions: readonly JournalAction[], events: readonly ReticleEvent[]): ProvenClaim[];
26
+ /**
27
+ * The context for one run.
28
+ *
29
+ * `step` is the journal's action count rather than a counter of its own, for the reason at the top
30
+ * of this file: a second counter is a second thing that can disagree with the ledger. The document
31
+ * and epoch are the session's own, so the staleness rule here is the same one every other verdict in
32
+ * this codebase is scoped by.
33
+ */
34
+ export declare function runContextFor(input: {
35
+ actions: readonly JournalAction[];
36
+ events: readonly ReticleEvent[];
37
+ intents: readonly Intent[];
38
+ currentDocumentId: string | undefined;
39
+ currentEditEpoch: number | undefined;
40
+ }): RunContext;
@@ -0,0 +1,158 @@
1
+ import { EventType, JournalVerdictEffectSchema, RUN_ESTABLISHED_CAP, buildRunContext, } from '@reticlehq/core';
2
+ /**
3
+ * The run, derived from the journal that is already there.
4
+ *
5
+ * `.reticle/sessions/<id>/` already holds an append-only causal ledger of every action dispatched
6
+ * and every event observed. The run is a FOLD over that, never a second store — the same argument
7
+ * that deleted `instrumentation_stalled` from this codebase applies without modification: two ways
8
+ * to compute one number is worse than one way, because the day they disagree the disagreement lands
9
+ * inside a verdict and a user finds it before we do. So nothing here writes: it reads the ledger and
10
+ * folds it, and if the fold is wrong the ledger is still the single authority.
11
+ *
12
+ * **One run per session, for now.** Nothing here assumes it: the fold is pure and takes its evidence
13
+ * as arguments, so a later multi-run or multi-agent model is a change of caller, not a rewrite. It
14
+ * is not built today because nobody has hit the case, and a coordination model invented ahead of its
15
+ * first user is a guess.
16
+ *
17
+ * Only what Reticle OBSERVED goes in. A settle outcome is an observation. A route change is an
18
+ * observation. A verdict a verification tool recorded is an observation. What the agent meant to do
19
+ * next is not, and never enters here.
20
+ */
21
+ /**
22
+ * The one key the route is filed under.
23
+ *
24
+ * The subject of "the app is on /checkout" is the route itself, so a later route change SUPERSEDES
25
+ * rather than appends. Filing it per-path would accumulate every page ever visited, which is the
26
+ * token bomb the cap exists to prevent.
27
+ */
28
+ const ROUTE_KEY = 'route';
29
+ /** The conclusions this fold is allowed to state, as prose rather than transcript. */
30
+ const Fact = {
31
+ route: (to) => `route is ${to}`,
32
+ settled: (verb) => `${verb} settled`,
33
+ unsettled: (verb) => `${verb} did not settle`,
34
+ };
35
+ /**
36
+ * The SUBJECT a journal action was about: the ref it spent, else the target it resolved from.
37
+ *
38
+ * Exported so the feature-use instrument files a re-fetch under the same key this fold establishes
39
+ * it under. A second copy of this rule there would be a second definition of "the same subject".
40
+ */
41
+ export function subjectOf(action) {
42
+ const ref = action.args['ref'];
43
+ if ('string' === typeof ref && ref.length > 0)
44
+ return ref;
45
+ const target = action.args['target'];
46
+ if ('string' === typeof target && target.length > 0)
47
+ return target;
48
+ return undefined;
49
+ }
50
+ /** What the action did, in the caller's own vocabulary — 'click', 'fill', else the tool's name. */
51
+ function verbOf(action) {
52
+ const named = action.args['action'];
53
+ return 'string' === typeof named && named.length > 0 ? named : action.tool;
54
+ }
55
+ /** The verdict a verification tool recorded on this action, if it recorded one. */
56
+ function verdictOf(action) {
57
+ const parsed = JournalVerdictEffectSchema.safeParse(action.effect);
58
+ return parsed.success ? parsed.data : undefined;
59
+ }
60
+ /** Which document and edit epoch each action was observed under, read off its attributed events. */
61
+ function scopeByAction(events) {
62
+ const out = new Map();
63
+ for (const event of events) {
64
+ if (event.actionId === undefined)
65
+ continue;
66
+ if (event.documentId === undefined && event.editEpoch === undefined)
67
+ continue;
68
+ const existing = out.get(event.actionId) ?? {};
69
+ out.set(event.actionId, {
70
+ doc: event.documentId ?? existing.doc,
71
+ epoch: event.editEpoch ?? existing.epoch,
72
+ });
73
+ }
74
+ return out;
75
+ }
76
+ /**
77
+ * Fold the journal into candidate facts, newest last.
78
+ *
79
+ * Only the last `RUN_ESTABLISHED_CAP` actions are read: anything older cannot survive the fold, so
80
+ * parsing it would be work done to throw away. An action with no observed settle outcome states
81
+ * nothing and is skipped — "we drove it and cannot say what happened" is not an established fact,
82
+ * and writing it down as one is how a memory starts lying.
83
+ */
84
+ export function establishedFromJournal(actions, events) {
85
+ const scope = scopeByAction(events);
86
+ let route;
87
+ for (const event of events) {
88
+ if (EventType.ROUTE_CHANGE !== event.type)
89
+ continue;
90
+ const pathname = event.data['pathname'];
91
+ const to = 'string' === typeof pathname ? pathname : event.data['to'];
92
+ if ('string' !== typeof to)
93
+ continue;
94
+ route = { key: ROUTE_KEY, fact: Fact.route(to), doc: event.documentId, epoch: event.editEpoch };
95
+ }
96
+ const out = [];
97
+ for (const action of actions.slice(-RUN_ESTABLISHED_CAP)) {
98
+ const settled = action.settled;
99
+ if (undefined === settled)
100
+ continue;
101
+ const key = subjectOf(action);
102
+ if (undefined === key)
103
+ continue;
104
+ const verb = verbOf(action);
105
+ out.push({
106
+ key,
107
+ fact: settled ? Fact.settled(verb) : Fact.unsettled(verb),
108
+ // The source pointer the verdict already reported. Read off the ledger rather than looked up
109
+ // again: a second lookup could disagree with the one the agent was shown.
110
+ source: verdictOf(action)?.source,
111
+ ...(scope.get(action.actionId) ?? {}),
112
+ });
113
+ }
114
+ if (route !== undefined)
115
+ out.push(route);
116
+ return out;
117
+ }
118
+ /**
119
+ * What a verdict already settled, newest last.
120
+ *
121
+ * Both verdict tools write here. `reticle_act_and_wait` closes its attribution window with one;
122
+ * `reticle_assert` drives nothing, so it appends its record WITHOUT opening a window — same effect
123
+ * shape, so this fold reads one kind of verdict rather than two.
124
+ */
125
+ export function provenFromJournal(actions, events) {
126
+ const scope = scopeByAction(events);
127
+ const out = [];
128
+ for (const action of actions.slice(-RUN_ESTABLISHED_CAP)) {
129
+ const verdict = verdictOf(action);
130
+ if (undefined === verdict)
131
+ continue;
132
+ out.push({
133
+ claim: verdict.claim,
134
+ verified: verdict.verified,
135
+ source: verdict.source,
136
+ ...(scope.get(action.actionId) ?? {}),
137
+ });
138
+ }
139
+ return out;
140
+ }
141
+ /**
142
+ * The context for one run.
143
+ *
144
+ * `step` is the journal's action count rather than a counter of its own, for the reason at the top
145
+ * of this file: a second counter is a second thing that can disagree with the ledger. The document
146
+ * and epoch are the session's own, so the staleness rule here is the same one every other verdict in
147
+ * this codebase is scoped by.
148
+ */
149
+ export function runContextFor(input) {
150
+ return buildRunContext({
151
+ step: input.actions.length,
152
+ intents: input.intents,
153
+ established: establishedFromJournal(input.actions, input.events),
154
+ proven: provenFromJournal(input.actions, input.events),
155
+ currentDocumentId: input.currentDocumentId,
156
+ currentEditEpoch: input.currentEditEpoch,
157
+ });
158
+ }
@@ -1,6 +1,6 @@
1
1
  import type { ReticleVerificationRun } from '@reticlehq/core';
2
2
  /** A per-flow change between two runs. Only flows with a meaningful change are reported. */
3
- export interface FlowDelta {
3
+ interface FlowDelta {
4
4
  name: string;
5
5
  beforeMs: number;
6
6
  afterMs: number;
@@ -29,3 +29,4 @@ export interface RunDiff {
29
29
  * console diffs land once runs persist per-step detail.
30
30
  */
31
31
  export declare function diffRuns(before: ReticleVerificationRun, after: ReticleVerificationRun, noiseFloorPct?: number): RunDiff;
32
+ export {};
@@ -19,6 +19,16 @@ export type ReadRunResult = {
19
19
  interface RunStoreOptions {
20
20
  retention?: number;
21
21
  slack?: number;
22
+ /**
23
+ * Called after a run artifact is safely on disk. Used to wake cloud sync so a finished
24
+ * verification reaches the dashboard promptly instead of waiting for the next timer tick.
25
+ *
26
+ * A callback rather than a dependency on the sync daemon: the store's job is durability, and a
27
+ * store that imported the cloud would make "write a run" fail whenever the cloud did. It is
28
+ * invoked AFTER the rename, so nothing can observe a run that is not yet readable, and it must
29
+ * never throw — see the call site.
30
+ */
31
+ onWrote?: () => void;
22
32
  }
23
33
  export declare class RunStore {
24
34
  #private;
@@ -15,11 +15,13 @@ export class RunStore {
15
15
  #root;
16
16
  #retention;
17
17
  #slack;
18
+ #onWrote;
18
19
  constructor(fs, root, opts = {}) {
19
20
  this.#fs = fs;
20
21
  this.#root = root;
21
22
  this.#retention = opts.retention ?? RUN_RETENTION;
22
23
  this.#slack = opts.slack ?? RUN_RETENTION_SLACK;
24
+ this.#onWrote = opts.onWrote;
23
25
  }
24
26
  /**
25
27
  * Write one run artifact. Creates .reticle/runs/ first; byte-stable (fixed indent + trailing newline).
@@ -37,6 +39,14 @@ export class RunStore {
37
39
  const tmp = `${path}${TMP_EXT}`;
38
40
  await this.#fs.writeFile(tmp, `${JSON.stringify(run, null, JSON_INDENT)}\n`);
39
41
  await this.#fs.rename(tmp, path);
42
+ // AFTER the rename: a listener that reads the run back must find it there. Swallowed, because
43
+ // this is a notification — a sync that cannot be woken must never fail the write it followed.
44
+ try {
45
+ this.#onWrote?.();
46
+ }
47
+ catch {
48
+ // Not this store's problem, and not worth failing a durable write over.
49
+ }
40
50
  await this.#pruneOld();
41
51
  }
42
52
  /**
@@ -1,16 +1,5 @@
1
- /**
2
- * Turn an MCP verification (a `reticle_flow_verify` suite run) into a first-class ReticleVerificationRun:
3
- * assemble the artifact from the already-computed replays, persist it to .reticle/runs/ (so
4
- * `reticle_run_export` and CI can read it), and — when logged in (cloud creds set) — best-effort push it
5
- * to the cloud's `POST /v1/runs` so the team's dashboard Runs tab shows what the agent just verified.
6
- *
7
- * This is the artifact tier that maps to the dashboard's "Runs" (the OEM/CI-consumable verdict); the
8
- * lighter per-flow RunRecord that feeds regression memory is synced separately in flow-replay-run.ts.
9
- * Both are best-effort + opt-in: no creds → the artifact still lands on disk, nothing leaves the machine.
10
- */
11
1
  import { type FlowReplayResult } from '@reticlehq/core';
12
2
  import type { ToolDeps } from '../tools/tools.js';
13
- /** A replay plus the wall-clock time it took — the shape the verify handler already collects. */
14
3
  export interface TimedReplay {
15
4
  replay: FlowReplayResult;
16
5
  durationMs: number;
@@ -8,15 +8,19 @@
8
8
  * lighter per-flow RunRecord that feeds regression memory is synced separately in flow-replay-run.ts.
9
9
  * Both are best-effort + opt-in: no creds → the artifact still lands on disk, nothing leaves the machine.
10
10
  */
11
+ import { mcpClientIdentity } from '../mcp/client-identity.js';
11
12
  import { RunAgentKind, RunFramework, RunProfile, RunTrigger, } from '@reticlehq/core';
12
13
  import { homedir } from 'node:os';
13
14
  import { buildVerificationRun } from './build-verification-run.js';
14
15
  import { mapReplayToFlowResult } from './replay-mapping.js';
15
16
  import { defaultRunId } from './runner-port.js';
16
17
  import { RunStore } from './run-store.js';
17
- import { syncRunToCloud, SyncOutcome } from '../cloud/cloud-sync.js';
18
+ import { cloudFetch, syncRunToCloud, SyncOutcome } from '../cloud/cloud-sync.js';
18
19
  import { resolveProjectCloud } from '../cloud/cloud-config.js';
19
20
  import { log } from '../log.js';
21
+ /** A replay plus the wall-clock time it took — the shape the verify handler already collects. */
22
+ /** The author of record when no MCP peer introduced itself — a CLI run, or a client that skipped the handshake. */
23
+ const RETICLE_MCP_AGENT_ID = 'reticle-mcp';
20
24
  /** Assemble the ReticleVerificationRun artifact for a verify suite (pure — no IO). */
21
25
  function assembleRun(deps, timed, projectId) {
22
26
  const flows = timed.map((t) => mapReplayToFlowResult(t.replay, t.durationMs));
@@ -25,7 +29,20 @@ function assembleRun(deps, timed, projectId) {
25
29
  durationMs: flows.reduce((sum, f) => sum + f.durationMs, 0),
26
30
  profile: RunProfile.DEV,
27
31
  project: { name: projectId ?? 'reticle', framework: RunFramework.OTHER },
28
- agent: { id: 'reticle-mcp', kind: RunAgentKind.CODING_AGENT },
32
+ /*
33
+ * WHICH agent drove this, not merely THAT one did.
34
+ *
35
+ * This was hardcoded to 'reticle-mcp', so every run in every workspace claimed the same author.
36
+ * A dashboard built on it could say "an agent verified this" and never "Claude Code verified
37
+ * this, Cursor broke it" — which is the question a team actually has once more than one agent
38
+ * touches the repo. The name is the client's own claim from the MCP handshake; a plain CLI run
39
+ * with no MCP peer keeps the old id, because inventing a vendor there would be worse than
40
+ * admitting we do not know.
41
+ */
42
+ agent: {
43
+ id: mcpClientIdentity().name ?? RETICLE_MCP_AGENT_ID,
44
+ kind: RunAgentKind.CODING_AGENT,
45
+ },
29
46
  trigger: { kind: RunTrigger.MANUAL },
30
47
  changedFiles: [],
31
48
  flows,
@@ -58,9 +75,13 @@ export async function persistAndSyncVerificationRun(deps, timed, projectId) {
58
75
  const cloud = await resolveProjectCloud(deps.fs, deps.reticleRoot, homedir(), process.env);
59
76
  if (null === cloud.config || !cloud.policy.runs)
60
77
  return run.runId; // not attached / runs disabled → local only
61
- const result = await syncRunToCloud(run, cloud.config, (url, init) => fetch(url, init));
78
+ const result = await syncRunToCloud(run, cloud.config, cloudFetch);
62
79
  if (result.outcome !== SyncOutcome.SYNCED) {
63
80
  log('cloud-run-sync-failed', { runId: run.runId, status: result.status, error: result.error });
64
81
  }
82
+ // The run is up; its CONTEXT is not. Nudged rather than pushed inline, so the counters and flows
83
+ // ride the ordinary cycle (with its cursor and its overlap guard) instead of growing a second,
84
+ // subtly different upload path here.
85
+ deps.onRunPersisted?.();
65
86
  return run.runId;
66
87
  }
@@ -18,7 +18,14 @@ export interface VerifyServerOptions {
18
18
  }
19
19
  /** Build the request listener. Reads + size-caps the body, then hands off to the pure handler. */
20
20
  export declare function createVerifyRequestListener(opts: VerifyServerOptions): http.RequestListener;
21
- /** Start the verify server on localhost. Resolves once listening; returns the bound server + port. */
21
+ /**
22
+ * Start the verify server on localhost. Resolves once listening; returns the bound server + port.
23
+ *
24
+ * A failed bind REJECTS, naming the verify port. It used to emit `'error'` with no listener, so an
25
+ * EADDRINUSE on `--http-port` crashed the daemon with a raw stack, and the parent `serve` reported
26
+ * only that the daemon "did not come up" on the BRIDGE port — the one port that was fine (#687).
27
+ * Rejecting lets the daemon's start-failure path say what actually could not be honoured.
28
+ */
22
29
  export declare function startVerifyServer(opts: VerifyServerOptions, port: number): Promise<{
23
30
  server: http.Server;
24
31
  port: number;
@@ -83,13 +83,25 @@ async function writeResponse(req, res, opts, raw) {
83
83
  res.writeHead(result.status, JSON_HEADERS);
84
84
  res.end(JSON.stringify(result.body));
85
85
  }
86
- /** Start the verify server on localhost. Resolves once listening; returns the bound server + port. */
86
+ /**
87
+ * Start the verify server on localhost. Resolves once listening; returns the bound server + port.
88
+ *
89
+ * A failed bind REJECTS, naming the verify port. It used to emit `'error'` with no listener, so an
90
+ * EADDRINUSE on `--http-port` crashed the daemon with a raw stack, and the parent `serve` reported
91
+ * only that the daemon "did not come up" on the BRIDGE port — the one port that was fine (#687).
92
+ * Rejecting lets the daemon's start-failure path say what actually could not be honoured.
93
+ */
87
94
  export function startVerifyServer(opts, port) {
88
95
  const server = http.createServer(createVerifyRequestListener(opts));
89
96
  server.requestTimeout = REQUEST_TIMEOUT_MS;
90
97
  server.headersTimeout = HEADERS_TIMEOUT_MS;
91
- return new Promise((resolve) => {
98
+ return new Promise((resolve, reject) => {
99
+ const onError = (err) => {
100
+ reject(new Error(`the verify HTTP endpoint could not bind ${LOCALHOST}:${String(port)} — ${err.message}`));
101
+ };
102
+ server.once('error', onError);
92
103
  server.listen(port, LOCALHOST, () => {
104
+ server.removeListener('error', onError);
93
105
  const address = server.address();
94
106
  const boundPort = 'object' === typeof address && address !== null ? address.port : port;
95
107
  resolve({ server, port: boundPort });
@@ -9,8 +9,11 @@ import { AppRuntime } from '@reticlehq/core';
9
9
  * developer goes hunting through their own code for a fault that is not there.
10
10
  *
11
11
  * This advice previously blamed occlusion — "macOS suspends an occluded or off-Space WKWebView" —
12
- * which is false, and was the kind of wrong steer this function exists to prevent. A LOADED Tauri
13
- * webview keeps answering while minimized, app-hidden, occluded, and on another Space.
12
+ * which is false for those states, and was the kind of wrong steer this function exists to prevent.
13
+ * A LOADED Tauri webview keeps answering while minimized, occluded, and on another Space. `hide()`
14
+ * after load is the remaining headless path: a hidden macOS WKWebView has been observed to go quiet
15
+ * after a pause, but that is not something every machine demonstrates, so this message names it as
16
+ * a candidate rather than a fact.
14
17
  *
15
18
  * It then over-corrected: it asserted the hidden-before-load ordering as FACT. But the only evidence
16
19
  * here is `hidden === true` — the ordering is never observed. Measured on a Tauri shell pointed at an
@@ -11,11 +11,12 @@ const ONE_WAY_ADVICE = 'The page is ALIVE — the SDK last reported to the bridg
11
11
  'replaced mid-session, a proxy buffering one way) rather than at your app.';
12
12
  const HIDDEN_ADVICE = 'The page last reported itself hidden, and this is a WKWebView (Tauri) window, so the webview is ' +
13
13
  'most likely not running the page at all. Two known causes, commonest first: (1) the window was ' +
14
- 'hidden BEFORE its first page load — a webview that has never been presented never loads, so hide ' +
14
+ 'hidden BEFORE its first page load — a webview that has never been presented never loads, so park ' +
15
15
  'it from `on_page_load` instead of `setup` (see `reticle_tauri::on_page_load`, which does this for ' +
16
- 'RETICLE_HEADLESS=1); once the page HAS loaded, hiding it is safe. (2) the window never presented ' +
17
- 'for another reason — check that it is declared with a visible size and that the page URL actually ' +
18
- 'loaded. If neither fits, the page is reachable but not executing, which is worth reporting.';
16
+ 'RETICLE_HEADLESS=1). (2) the window was hidden after load and then went quiet — that has been ' +
17
+ 'observed on a hidden macOS WKWebView after a pause, even though capture still works; park it ' +
18
+ 'off-screen rather than calling hide. If neither fits, the page is reachable but not executing, ' +
19
+ 'which is worth reporting.';
19
20
  /** True when this session is a WebKit desktop shell — the only runtime that suffers this. */
20
21
  function isWebKitDesktop(context) {
21
22
  if (AppRuntime.TAURI === context.runtime)
@@ -69,13 +69,5 @@ export declare function probeDevServerStates(ports?: readonly number[], probe?:
69
69
  serving: number[];
70
70
  slow: number[];
71
71
  }>;
72
- /**
73
- * The strongest state either loopback family reports.
74
- *
75
- * Serving a document beats a bare connection beats nothing, so a server bound to only one family is
76
- * still seen at its best — the same "either, not both" rule `anyFamilyServes` follows, extended to
77
- * three states.
78
- */
79
- export declare function anyFamilyState(port: number, probe?: (port: number, host: string) => Promise<PortState>): Promise<PortState>;
80
72
  /** Every candidate port serving a document, in ascending order. Never rejects. */
81
73
  export declare function probeDevServers(ports?: readonly number[], probe?: (port: number) => Promise<boolean>): Promise<number[]>;
@@ -132,7 +132,7 @@ export async function probeDevServerStates(ports = [...DEV_SERVER_PORTS], probe
132
132
  * still seen at its best — the same "either, not both" rule `anyFamilyServes` follows, extended to
133
133
  * three states.
134
134
  */
135
- export async function anyFamilyState(port, probe = probeState) {
135
+ async function anyFamilyState(port, probe = probeState) {
136
136
  const answers = await Promise.all(PROBE_HOSTS.map((host) => probe(port, host).catch(() => PortState.CLOSED)));
137
137
  if (answers.includes(PortState.SERVES_DOCUMENT))
138
138
  return PortState.SERVES_DOCUMENT;
@@ -16,6 +16,12 @@
16
16
  */
17
17
  interface ActEffect {
18
18
  action?: string | undefined;
19
+ /**
20
+ * The ref the action dispatched against. Recorded so the verdict nudge can suggest a call about
21
+ * the element the agent actually touched, rather than a worked example from a document. Written
22
+ * on the SAME success path as `action`, so a refused act leaves neither.
23
+ */
24
+ ref?: string | undefined;
19
25
  /** DOM mutations inside the acted element's own subtree. Undefined means nobody measured. */
20
26
  mutatedWithin?: number | undefined;
21
27
  }
@@ -35,7 +41,7 @@ export declare class LastAct {
35
41
  * Marking only on the success path is what makes that unrepeatable: a failure mode added later
36
42
  * cannot forget to clean up state that was never written.
37
43
  */
38
- markActed(cursor: number, action: string | undefined, mutatedWithin: number | undefined): void;
44
+ markActed(cursor: number, action: string | undefined, mutatedWithin: number | undefined, ref?: string): void;
39
45
  /**
40
46
  * A navigation — by URL or by reload — moves the floor without being an act.
41
47
  *
@@ -18,9 +18,9 @@ export class LastAct {
18
18
  * Marking only on the success path is what makes that unrepeatable: a failure mode added later
19
19
  * cannot forget to clean up state that was never written.
20
20
  */
21
- markActed(cursor, action, mutatedWithin) {
21
+ markActed(cursor, action, mutatedWithin, ref) {
22
22
  this.#cursor = cursor;
23
- this.#effect = { action, mutatedWithin };
23
+ this.#effect = { action, mutatedWithin, ...(ref === undefined ? {} : { ref }) };
24
24
  }
25
25
  /**
26
26
  * A navigation — by URL or by reload — moves the floor without being an act.
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Telling the human that the agent went somewhere they cannot see.
3
+ *
4
+ * A lease is a separate pooled context, and it is the highest-value path in the product for
5
+ * autonomous work. It also has one failure mode that is entirely about people: the HUD lives in the
6
+ * human's OWN tab, so while an agent drives a lease that tab shows nothing at all. An idle agent and
7
+ * a busy one are then indistinguishable from the only seat a human occupies.
8
+ *
9
+ * That is not hypothetical. An agent followed the daemon's own throttle recommendation into a lease,
10
+ * drove fifteen calls, and the developer watching an empty HUD asked why nothing was running. The
11
+ * product was working perfectly and looked broken, which is the worst combination available.
12
+ *
13
+ * The fix rides on machinery that already exists — `pushNarration` posts `ReticleCommand.NARRATE`
14
+ * and every SDK in the field already renders it — so nothing here moves the wire contract. It is a
15
+ * message to tabs that are ALREADY connected, not a new capability.
16
+ */
17
+ /** The minimum a session has to expose to be a candidate watcher. Structural, so tests need no SDK. */
18
+ export interface WatcherCandidate {
19
+ id: string;
20
+ projectId: string | undefined;
21
+ }
22
+ /**
23
+ * Shown on a watching human's HUD when an agent takes a lease.
24
+ *
25
+ * States the fact and its consequence, because the fact alone ("agent leased a context") does not
26
+ * tell somebody why their screen is still. The consequence is the whole reason to say anything.
27
+ */
28
+ export declare const AGENT_DRIVING_ELSEWHERE = "An agent is driving a separate leased context \u2014 its actions will not appear in this tab.";
29
+ /** Shown when the lease is released, so a HUD that went quiet is explained in both directions. */
30
+ export declare const AGENT_DRIVING_HERE_AGAIN = "The agent released its separate context \u2014 this tab is live again.";
31
+ /**
32
+ * Shown to a tab that CONNECTS while a lease is already open.
33
+ *
34
+ * The acquire-time notice only reaches tabs that were already connected, so opening the app — or
35
+ * merely reloading it — during a lease landed somebody on a silent HUD with nothing explaining it.
36
+ * That is the same dark-HUD failure the acquire notice exists to prevent, arrived at from the other
37
+ * direction, and it is the more common one: a person opens the dashboard BECAUSE they want to watch.
38
+ *
39
+ * Worded for arrival rather than for a change of state — nothing just happened from this tab's point
40
+ * of view, so "an agent is driving elsewhere" would describe an event it never saw.
41
+ */
42
+ export declare const AGENT_ALREADY_DRIVING_ELSEWHERE = "An agent is already driving a separate leased context \u2014 its actions will not appear in this tab.";
43
+ /**
44
+ * Whether a session arriving now should be told a lease is already running.
45
+ *
46
+ * Same rules as `watchersToNotify`, expressed for one session: never tell a leased context about
47
+ * itself, and never tell somebody watching app A about a lease against app B.
48
+ */
49
+ export declare function shouldGreetWithLeaseNotice(session: WatcherCandidate, leasedSessionIds: readonly string[], leasedProjectIds: readonly (string | undefined)[]): boolean;
50
+ /**
51
+ * Which connected sessions belong to a human who should be told.
52
+ *
53
+ * Two exclusions, both deliberate:
54
+ *
55
+ * - Leased sessions. They ARE the agent's context; narrating "an agent is driving elsewhere" into
56
+ * the tab doing the driving is noise, and with several leases open they would talk about each
57
+ * other. Membership comes from the pool rather than an id prefix, because a naming convention is
58
+ * not a fact and `pool.leasedSessionIds()` is.
59
+ *
60
+ * - Other projects. One daemon serves many, and somebody watching app A does not need to hear about
61
+ * a lease taken against app B. A session that declares no project is NOT excluded: an older SDK
62
+ * sends no projectId, and silently dropping those would reintroduce the dark HUD for exactly the
63
+ * people least likely to work out why.
64
+ *
65
+ * Pure: no IO, no clock, no session objects. The caller does the posting.
66
+ */
67
+ export declare function watchersToNotify(sessions: readonly WatcherCandidate[], leasedSessionIds: readonly string[], projectId: string | undefined): string[];